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