diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 31e6194bec3..b9120cc2228 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -3187,6 +3187,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly as ImportCredential = ['...', ...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ss) ImportCredentialEx = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly as SupplementaryGroups = ['...', ...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s PAMName = '...'; @@ -3800,6 +3802,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + @@ -4488,6 +4492,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + @@ -5312,6 +5318,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly as ImportCredential = ['...', ...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ss) ImportCredentialEx = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly as SupplementaryGroups = ['...', ...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s PAMName = '...'; @@ -5939,6 +5947,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + @@ -6603,6 +6613,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + @@ -7291,6 +7303,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly as ImportCredential = ['...', ...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ss) ImportCredentialEx = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly as SupplementaryGroups = ['...', ...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s PAMName = '...'; @@ -7844,6 +7858,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { + + @@ -8420,6 +8436,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { + + @@ -9231,6 +9249,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly as ImportCredential = ['...', ...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly a(ss) ImportCredentialEx = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly as SupplementaryGroups = ['...', ...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s PAMName = '...'; @@ -9770,6 +9790,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { + + @@ -10332,6 +10354,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { + + @@ -12099,8 +12123,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \ ExecMainHandoffTimestampMonotonic, and ExecMainHandoffTimestamp were added in version 256. StatusBusError, - StatusVarlinkError, and - PrivateTmpEx were added in version 257. + StatusVarlinkError, + PrivateTmpEx, and + ImportCredentialEx were added in version 257. Socket Unit Objects @@ -12137,7 +12162,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \ EffectiveTasksMax, MemoryZSwapWriteback, and PassFileDescriptorsToExec were added in version 256. - PrivateTmpEx was added in version 257. + PrivateTmpEx, and + ImportCredentialEx were added in version 257. Mount Unit Objects @@ -12171,7 +12197,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \ EffectiveMemoryMax, EffectiveTasksMax, and MemoryZSwapWriteback were added in version 256. - PrivateTmpEx was added in version 257. + PrivateTmpEx, and + ImportCredentialEx were added in version 257. Swap Unit Objects @@ -12205,7 +12232,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \ EffectiveMemoryMax, EffectiveTasksMax, and MemoryZSwapWriteback were added in version 256. - PrivateTmpEx was added in version 257. + PrivateTmpEx, and + ImportCredentialEx were added in version 257. Slice Unit Objects diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index c79cf674458..fe19c8a657c 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -3520,6 +3520,18 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX [] wildcards are not permitted, nor are * wildcards anywhere except at the end of the glob expression. + Optionally, the credential name or glob may be followed by a colon followed by a rename pattern. + If specified, all credentials matching the credential name or glob are renamed according to the given + pattern. For example, if ImportCredential=my.original.cred:my.renamed.cred is + specified, the service manager will read the my.original.cred credential and make + it available as the my.renamed.cred credential to the service. Similarly, if + ImportCredential=my.original.*:my.renamed. is specified, the service manager will + read all credentials starting with my.original. and make them available as + my.renamed.xxx to the service. + + If ImportCredential= is specified multiple times and multiple credentials + end up with the same name after renaming, the first one is kept and later ones are dropped.. + When multiple credentials of the same name are found, credentials found by LoadCredential= and LoadCredentialEncrypted= take priority over credentials found by ImportCredential=. diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index c9b118a1a04..feafa15e430 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -688,6 +688,66 @@ static int property_get_load_credential( return sd_bus_message_close_container(reply); } +static int property_get_import_credential( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + ExecContext *c = ASSERT_PTR(userdata); + ExecImportCredential *ic; + int r; + + assert(bus); + assert(property); + assert(reply); + + r = sd_bus_message_open_container(reply, 'a', "s"); + if (r < 0) + return r; + + ORDERED_SET_FOREACH(ic, c->import_credentials) { + r = sd_bus_message_append(reply, "s", ic->glob); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + +static int property_get_import_credential_ex( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + ExecContext *c = ASSERT_PTR(userdata); + ExecImportCredential *ic; + int r; + + assert(bus); + assert(property); + assert(reply); + + r = sd_bus_message_open_container(reply, 'a', "(ss)"); + if (r < 0) + return r; + + ORDERED_SET_FOREACH(ic, c->import_credentials) { + r = sd_bus_message_append(reply, "(ss)", ic->glob, ic->rename); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); +} + static int property_get_root_hash( sd_bus *bus, const char *path, @@ -1068,7 +1128,8 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("SetCredentialEncrypted", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("LoadCredential", "a(ss)", property_get_load_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("LoadCredentialEncrypted", "a(ss)", property_get_load_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("ImportCredential", "as", bus_property_get_string_set, offsetof(ExecContext, import_credentials), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ImportCredential", "as", property_get_import_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ImportCredentialEx", "a(ss)", property_get_import_credential_ex, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SupplementaryGroups", "as", NULL, offsetof(ExecContext, supplementary_groups), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("PAMName", "s", NULL, offsetof(ExecContext, pam_name), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ReadWritePaths", "as", NULL, offsetof(ExecContext, read_write_paths), SD_BUS_VTABLE_PROPERTY_CONST), @@ -2142,33 +2203,41 @@ int bus_exec_context_set_transient_property( return 1; - } else if (streq(name, "ImportCredential")) { - bool isempty = true; + } else if (STR_IN_SET(name, "ImportCredential", "ImportCredentialEx")) { + bool empty = true, ex = streq(name, "ImportCredentialEx"); - r = sd_bus_message_enter_container(message, 'a', "s"); + r = sd_bus_message_enter_container(message, 'a', ex ? "(ss)" : "s"); if (r < 0) return r; for (;;) { - const char *s; + const char *glob, *rename = NULL; - r = sd_bus_message_read(message, "s", &s); + if (ex) + r = sd_bus_message_read(message, "(ss)", &glob, &rename); + else + r = sd_bus_message_read(message, "s", &glob); if (r < 0) return r; if (r == 0) break; - if (!credential_glob_valid(s)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Credential name or glob is invalid: %s", s); + if (!credential_glob_valid(glob)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Credential name or glob is invalid: %s", glob); - isempty = false; + if (rename && !credential_name_valid(rename)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Credential name is invalid: %s", rename); + + empty = false; if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - r = set_put_strdup(&c->import_credentials, s); + r = exec_context_put_import_credential(c, glob, rename); if (r < 0) return r; - (void) unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, s); + (void) unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, + "ImportCredential=%s%s%s", + glob, rename ? ":" : "", strempty(rename)); } } @@ -2176,8 +2245,8 @@ int bus_exec_context_set_transient_property( if (r < 0) return r; - if (!UNIT_WRITE_FLAGS_NOOP(flags) && isempty) { - c->import_credentials = set_free_free(c->import_credentials); + if (!UNIT_WRITE_FLAGS_NOOP(flags) && empty) { + c->import_credentials = ordered_set_free(c->import_credentials); (void) unit_write_settingf(u, flags, name, "%s=", name); } diff --git a/src/core/exec-credential.c b/src/core/exec-credential.c index 75eca830f80..e1b09f67189 100644 --- a/src/core/exec-credential.c +++ b/src/core/exec-credential.c @@ -38,6 +38,37 @@ ExecLoadCredential* exec_load_credential_free(ExecLoadCredential *lc) { return mfree(lc); } +ExecImportCredential* exec_import_credential_free(ExecImportCredential *ic) { + if (!ic) + return NULL; + + free(ic->glob); + free(ic->rename); + return mfree(ic); +} + +static void exec_import_credential_hash_func(const ExecImportCredential *ic, struct siphash *state) { + assert(ic); + assert(state); + + siphash24_compress_string(ic->glob, state); + if (ic->rename) + siphash24_compress_string(ic->rename, state); +} + +static int exec_import_credential_compare_func(const ExecImportCredential *a, const ExecImportCredential *b) { + int r; + + assert(a); + assert(b); + + r = strcmp(a->glob, b->glob); + if (r != 0) + return r; + + return strcmp_ptr(a->rename, b->rename); +} + DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( exec_set_credential_hash_ops, char, string_hash_func, string_compare_func, @@ -48,6 +79,13 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( char, string_hash_func, string_compare_func, ExecLoadCredential, exec_load_credential_free); +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( + exec_import_credential_hash_ops, + ExecImportCredential, + exec_import_credential_hash_func, + exec_import_credential_compare_func, + exec_import_credential_free); + int exec_context_put_load_credential(ExecContext *c, const char *id, const char *path, bool encrypted) { ExecLoadCredential *old; int r; @@ -140,6 +178,39 @@ int exec_context_put_set_credential( return 0; } +int exec_context_put_import_credential(ExecContext *c, const char *glob, const char *rename) { + _cleanup_(exec_import_credential_freep) ExecImportCredential *ic = NULL; + int r; + + assert(c); + + rename = empty_to_null(rename); + + ic = new(ExecImportCredential, 1); + if (!ic) + return -ENOMEM; + + *ic = (ExecImportCredential) { + .glob = strdup(glob), + .rename = rename ? strdup(rename) : NULL, + }; + if (!ic->glob || (rename && !ic->rename)) + return -ENOMEM; + + if (ordered_set_contains(c->import_credentials, ic)) + return 0; + + r = ordered_set_ensure_put(&c->import_credentials, &exec_import_credential_hash_ops, ic); + if (r < 0) { + assert(r != -EEXIST); + return r; + } + + TAKE_PTR(ic); + + return 0; +} + bool exec_params_need_credentials(const ExecParameters *p) { assert(p); @@ -151,7 +222,7 @@ bool exec_context_has_credentials(const ExecContext *c) { return !hashmap_isempty(c->set_credentials) || !hashmap_isempty(c->load_credentials) || - !set_isempty(c->import_credentials); + !ordered_set_isempty(c->import_credentials); } bool exec_context_has_encrypted_credentials(const ExecContext *c) { @@ -381,7 +452,7 @@ static int maybe_decrypt_and_write_credential( } static int load_credential_glob( - const char *path, + const ExecImportCredential *ic, bool encrypted, char * const *search_path, ReadFullFileFlags flags, @@ -393,7 +464,7 @@ static int load_credential_glob( int r; - assert(path); + assert(ic); assert(search_path); assert(write_dfd >= 0); assert(left); @@ -402,7 +473,7 @@ static int load_credential_glob( _cleanup_globfree_ glob_t pglob = {}; _cleanup_free_ char *j = NULL; - j = path_join(*d, path); + j = path_join(*d, ic->glob); if (!j) return -ENOMEM; @@ -421,6 +492,16 @@ static int load_credential_glob( if (r < 0) return log_debug_errno(r, "Failed to extract filename from '%s': %m", *p); + if (ic->rename) { + _cleanup_free_ char *renamed = NULL; + + renamed = strjoin(ic->rename, fn + strlen(ic->glob) - !!endswith(ic->glob, "*")); + if (!renamed) + return log_oom_debug(); + + free_and_replace(fn, renamed); + } + if (faccessat(write_dfd, fn, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) { log_debug("Skipping credential with duplicated ID %s at %s", fn, *p); continue; @@ -661,7 +742,7 @@ static int acquire_credentials( uint64_t left = CREDENTIALS_TOTAL_SIZE_MAX; _cleanup_close_ int dfd = -EBADF; - const char *ic; + ExecImportCredential *ic; ExecLoadCredential *lc; ExecSetCredential *sc; int r; @@ -736,7 +817,7 @@ static int acquire_credentials( /* Next, look for system credentials and credentials in the credentials store. Note that these do not * override any credentials found earlier. */ - SET_FOREACH(ic, context->import_credentials) { + ORDERED_SET_FOREACH(ic, context->import_credentials) { _cleanup_free_ char **search_path = NULL; search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_TRUSTED); diff --git a/src/core/exec-credential.h b/src/core/exec-credential.h index 87b85d77099..88d7f715801 100644 --- a/src/core/exec-credential.h +++ b/src/core/exec-credential.h @@ -25,12 +25,20 @@ typedef struct ExecSetCredential { size_t size; } ExecSetCredential; +typedef struct ExecImportCredential { + char *glob; + char *rename; +} ExecImportCredential; + ExecSetCredential* exec_set_credential_free(ExecSetCredential *sc); DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSetCredential*, exec_set_credential_free); ExecLoadCredential* exec_load_credential_free(ExecLoadCredential *lc); DEFINE_TRIVIAL_CLEANUP_FUNC(ExecLoadCredential*, exec_load_credential_free); +ExecImportCredential* exec_import_credential_free(ExecImportCredential *lc); +DEFINE_TRIVIAL_CLEANUP_FUNC(ExecImportCredential*, exec_import_credential_free); + int exec_context_put_load_credential(ExecContext *c, const char *id, const char *path, bool encrypted); int exec_context_put_set_credential( ExecContext *c, @@ -38,6 +46,7 @@ int exec_context_put_set_credential( void *data_consume, size_t size, bool encrypted); +int exec_context_put_import_credential(ExecContext *c, const char *glob, const char *rename); bool exec_params_need_credentials(const ExecParameters *p); diff --git a/src/core/execute-serialize.c b/src/core/execute-serialize.c index 69f79984a54..23816627690 100644 --- a/src/core/execute-serialize.c +++ b/src/core/execute-serialize.c @@ -2554,13 +2554,14 @@ static int exec_context_serialize(const ExecContext *c, FILE *f) { return r; } - if (!set_isempty(c->import_credentials)) { - char *ic; - SET_FOREACH(ic, c->import_credentials) { - r = serialize_item(f, "exec-context-import-credentials", ic); - if (r < 0) - return r; - } + ExecImportCredential *ic; + ORDERED_SET_FOREACH(ic, c->import_credentials) { + r = serialize_item_format(f, "exec-context-import-credentials", "%s%s%s", + ic->glob, + ic->rename ? " " : "", + strempty(ic->rename)); + if (r < 0) + return r; } r = serialize_image_policy(f, "exec-context-root-image-policy", c->root_image_policy); @@ -3688,11 +3689,15 @@ static int exec_context_deserialize(ExecContext *c, FILE *f) { if (r < 0) return r; } else if ((val = startswith(l, "exec-context-import-credentials="))) { - r = set_ensure_allocated(&c->import_credentials, &string_hash_ops); + _cleanup_free_ char *glob = NULL, *rename = NULL; + + r = extract_many_words(&val, " ", EXTRACT_DONT_COALESCE_SEPARATORS, &glob, &rename); if (r < 0) return r; + if (r == 0) + return -EINVAL; - r = set_put_strdup(&c->import_credentials, val); + r = exec_context_put_import_credential(c, glob, rename); if (r < 0) return r; } else if ((val = startswith(l, "exec-context-root-image-policy="))) { diff --git a/src/core/execute.c b/src/core/execute.c index 26b2c0fb4af..56e6d4b1ca8 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -630,7 +630,7 @@ void exec_context_done(ExecContext *c) { c->load_credentials = hashmap_free(c->load_credentials); c->set_credentials = hashmap_free(c->set_credentials); - c->import_credentials = set_free_free(c->import_credentials); + c->import_credentials = ordered_set_free(c->import_credentials); c->root_image_policy = image_policy_free(c->root_image_policy); c->mount_image_policy = image_policy_free(c->mount_image_policy); diff --git a/src/core/execute.h b/src/core/execute.h index b801bfe8532..22d72c8959f 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -26,6 +26,7 @@ typedef struct Manager Manager; #include "nsflags.h" #include "numa-util.h" #include "open-file.h" +#include "ordered-set.h" #include "path-util.h" #include "runtime-scope.h" #include "set.h" @@ -363,7 +364,7 @@ struct ExecContext { Hashmap *set_credentials; /* output id → ExecSetCredential */ Hashmap *load_credentials; /* output id → ExecLoadCredential */ - Set *import_credentials; + OrderedSet *import_credentials; /* ExecImportCredential */ ImagePolicy *root_image_policy, *mount_image_policy, *extension_image_policy; }; diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in index e04450d869d..9a78e22fb52 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -153,7 +153,7 @@ {{type}}.SetCredentialEncrypted, config_parse_set_credential, 1, offsetof({{type}}, exec_context) {{type}}.LoadCredential, config_parse_load_credential, 0, offsetof({{type}}, exec_context) {{type}}.LoadCredentialEncrypted, config_parse_load_credential, 1, offsetof({{type}}, exec_context) -{{type}}.ImportCredential, config_parse_import_credential, 0, offsetof({{type}}, exec_context.import_credentials) +{{type}}.ImportCredential, config_parse_import_credential, 0, offsetof({{type}}, exec_context) {{type}}.TimeoutCleanSec, config_parse_sec, 0, offsetof({{type}}, exec_context.timeout_clean_usec) {% if HAVE_PAM %} {{type}}.PAMName, config_parse_unit_string_printf, 0, offsetof({{type}}, exec_context.pam_name) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 97b719f52aa..7633589b0d4 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -4895,8 +4895,7 @@ int config_parse_import_credential( void *data, void *userdata) { - _cleanup_free_ char *s = NULL; - Set** import_credentials = ASSERT_PTR(data); + ExecContext *context = ASSERT_PTR(data); Unit *u = userdata; int r; @@ -4906,23 +4905,40 @@ int config_parse_import_credential( if (isempty(rvalue)) { /* Empty assignment resets the list */ - *import_credentials = set_free_free(*import_credentials); + context->import_credentials = ordered_set_free(context->import_credentials); return 0; } - r = unit_cred_printf(u, rvalue, &s); + const char *p = rvalue; + _cleanup_free_ char *word = NULL, *glob = NULL; + + r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r == -ENOMEM) + return log_oom(); + if (r <= 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); + return 0; + } + + r = unit_cred_printf(u, word, &glob); if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", s); - return 0; - } - if (!credential_glob_valid(s)) { - log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name or glob \"%s\" not valid, ignoring.", s); + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in \"%s\", ignoring: %m", word); return 0; } - r = set_put_strdup(import_credentials, s); + if (!credential_glob_valid(glob)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name or glob \"%s\" not valid, ignoring.", glob); + return 0; + } + + if (!isempty(p) && !credential_name_valid(p)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential name \"%s\" not valid, ignoring.", p); + return 0; + } + + r = exec_context_put_import_credential(context, glob, p); if (r < 0) - return log_error_errno(r, "Failed to store credential name '%s': %m", rvalue); + return log_error_errno(r, "Failed to store import credential '%s': %m", rvalue); return 0; } diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 259fbeaf5c4..6ce76ded431 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -1310,6 +1310,49 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con return 1; } + if (streq(field, "ImportCredentialEx")) { + r = sd_bus_message_open_container(m, 'r', "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append_basic(m, 's', field); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'v', "a(ss)"); + if (r < 0) + return bus_log_create_error(r); + + if (isempty(eq)) + r = sd_bus_message_append(m, "a(ss)", 0); + else { + _cleanup_free_ char *word = NULL; + const char *p = eq; + + r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) + return log_error_errno(r, "Failed to parse %s= parameter: %s", field, eq); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing argument to %s=.", field); + + r = sd_bus_message_append(m, "a(ss)", 1, word, p); + } + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + if (streq(field, "LogExtraFields")) { r = sd_bus_message_open_container(m, 'r', "sv"); if (r < 0) diff --git a/test/units/TEST-54-CREDS.sh b/test/units/TEST-54-CREDS.sh index 89d6dcdf034..9f7f4b11606 100755 --- a/test/units/TEST-54-CREDS.sh +++ b/test/units/TEST-54-CREDS.sh @@ -289,6 +289,36 @@ systemd-run -p "ImportCredential=test.creds.*" \ '${CREDENTIALS_DIRECTORY}/test.creds.third' >/tmp/ts54-concat cmp /tmp/ts54-concat <(echo -n abc) +# Check that renaming with globs works as expected. +systemd-run -p "ImportCredentialEx=test.creds.*:renamed.creds." \ + --unit=test-54-ImportCredential.service \ + -p DynamicUser=1 \ + --wait \ + --pipe \ + cat '${CREDENTIALS_DIRECTORY}/renamed.creds.first' \ + '${CREDENTIALS_DIRECTORY}/renamed.creds.second' \ + '${CREDENTIALS_DIRECTORY}/renamed.creds.third' >/tmp/ts54-concat +cmp /tmp/ts54-concat <(echo -n abc) + +# Check that renaming without globs works as expected. +systemd-run -p "ImportCredentialEx=test.creds.first:renamed.creds.first" \ + --unit=test-54-ImportCredential.service \ + -p DynamicUser=1 \ + --wait \ + --pipe \ + cat '${CREDENTIALS_DIRECTORY}/renamed.creds.first' >/tmp/ts54-concat +cmp /tmp/ts54-concat <(echo -n a) + +# Test that multiple renames are processed in the correct order. +systemd-run -p "ImportCredentialEx=test.creds.first:renamed.creds.first" \ + -p "ImportCredentialEx=test.creds.second:renamed.creds.first" \ + --unit=test-54-ImportCredential.service \ + -p DynamicUser=1 \ + --wait \ + --pipe \ + cat '${CREDENTIALS_DIRECTORY}/renamed.creds.first' >/tmp/ts54-concat +cmp /tmp/ts54-concat <(echo -n a) + # Now test encrypted credentials (only supported when built with OpenSSL though) if systemctl --version | grep -q -- +OPENSSL ; then echo -n $RANDOM >/tmp/test-54-plaintext