1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-26 17:27:41 +03:00

Merge pull request #33873 from DaanDeMeyer/rename-creds

core: Add support for renaming credentials with ImportCredential=
This commit is contained in:
Daan De Meyer 2024-07-31 17:35:58 +02:00 committed by GitHub
commit e54a8e0fc3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 367 additions and 55 deletions

View File

@ -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 {
<!--property ImportCredential is not documented!-->
<!--property ImportCredentialEx is not documented!-->
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
@ -4488,6 +4492,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
<variablelist class="dbus-property" generated="True" extra-ref="ImportCredentialEx"/>
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@ -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 {
<!--property ImportCredential is not documented!-->
<!--property ImportCredentialEx is not documented!-->
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
@ -6603,6 +6613,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
<variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
<variablelist class="dbus-property" generated="True" extra-ref="ImportCredentialEx"/>
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@ -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 {
<!--property ImportCredential is not documented!-->
<!--property ImportCredentialEx is not documented!-->
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
@ -8420,6 +8436,8 @@ node /org/freedesktop/systemd1/unit/home_2emount {
<variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
<variablelist class="dbus-property" generated="True" extra-ref="ImportCredentialEx"/>
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@ -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 {
<!--property ImportCredential is not documented!-->
<!--property ImportCredentialEx is not documented!-->
<!--property SupplementaryGroups is not documented!-->
<!--property PAMName is not documented!-->
@ -10332,6 +10354,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
<variablelist class="dbus-property" generated="True" extra-ref="ImportCredential"/>
<variablelist class="dbus-property" generated="True" extra-ref="ImportCredentialEx"/>
<variablelist class="dbus-property" generated="True" extra-ref="SupplementaryGroups"/>
<variablelist class="dbus-property" generated="True" extra-ref="PAMName"/>
@ -12099,8 +12123,9 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>ExecMainHandoffTimestampMonotonic</varname>, and
<varname>ExecMainHandoffTimestamp</varname> were added in version 256.</para>
<para><varname>StatusBusError</varname>,
<varname>StatusVarlinkError</varname>, and
<varname>PrivateTmpEx</varname> were added in version 257.</para>
<varname>StatusVarlinkError</varname>,
<varname>PrivateTmpEx</varname>, and
<varname>ImportCredentialEx</varname> were added in version 257.</para>
</refsect2>
<refsect2>
<title>Socket Unit Objects</title>
@ -12137,7 +12162,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>EffectiveTasksMax</varname>,
<varname>MemoryZSwapWriteback</varname>, and
<varname>PassFileDescriptorsToExec</varname> were added in version 256.</para>
<para><varname>PrivateTmpEx</varname> was added in version 257.</para>
<para><varname>PrivateTmpEx</varname>, and
<varname>ImportCredentialEx</varname> were added in version 257.</para>
</refsect2>
<refsect2>
<title>Mount Unit Objects</title>
@ -12171,7 +12197,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>EffectiveMemoryMax</varname>,
<varname>EffectiveTasksMax</varname>, and
<varname>MemoryZSwapWriteback</varname> were added in version 256.</para>
<para><varname>PrivateTmpEx</varname> was added in version 257.</para>
<para><varname>PrivateTmpEx</varname>, and
<varname>ImportCredentialEx</varname> were added in version 257.</para>
</refsect2>
<refsect2>
<title>Swap Unit Objects</title>
@ -12205,7 +12232,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
<varname>EffectiveMemoryMax</varname>,
<varname>EffectiveTasksMax</varname>, and
<varname>MemoryZSwapWriteback</varname> were added in version 256.</para>
<para><varname>PrivateTmpEx</varname> was added in version 257.</para>
<para><varname>PrivateTmpEx</varname>, and
<varname>ImportCredentialEx</varname> were added in version 257.</para>
</refsect2>
<refsect2>
<title>Slice Unit Objects</title>

View File

@ -3520,6 +3520,18 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX
<literal>[]</literal> wildcards are not permitted, nor are <literal>*</literal> wildcards anywhere
except at the end of the glob expression.</para>
<para>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 <literal>ImportCredential=my.original.cred:my.renamed.cred</literal> is
specified, the service manager will read the <literal>my.original.cred</literal> credential and make
it available as the <literal>my.renamed.cred</literal> credential to the service. Similarly, if
<literal>ImportCredential=my.original.*:my.renamed.</literal> is specified, the service manager will
read all credentials starting with <literal>my.original.</literal> and make them available as
<literal>my.renamed.xxx</literal> to the service.</para>
<para>If <varname>ImportCredential=</varname> 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.</para>.
<para>When multiple credentials of the same name are found, credentials found by
<varname>LoadCredential=</varname> and <varname>LoadCredentialEncrypted=</varname> take priority over
credentials found by <varname>ImportCredential=</varname>.</para>

View File

@ -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);
}

View File

@ -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;
@ -417,6 +488,27 @@ static int load_credential_glob(
_cleanup_(erase_and_freep) char *data = NULL;
size_t size;
r = path_extract_filename(*p, &fn);
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;
}
if (errno != ENOENT)
return log_debug_errno(errno, "Failed to test if credential %s exists: %m", fn);
/* path is absolute, hence pass AT_FDCWD as nop dir fd here */
r = read_full_file_full(
AT_FDCWD,
@ -429,10 +521,6 @@ static int load_credential_glob(
if (r < 0)
return log_debug_errno(r, "Failed to read credential '%s': %m", *p);
r = path_extract_filename(*p, &fn);
if (r < 0)
return log_debug_errno(r, "Failed to extract filename from '%s': %m", *p);
r = maybe_decrypt_and_write_credential(
write_dfd,
fn,
@ -442,8 +530,6 @@ static int load_credential_glob(
ownership_ok,
data, size,
left);
if (r == -EEXIST)
continue;
if (r < 0)
return r;
}
@ -656,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;
@ -731,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);
@ -781,8 +867,10 @@ static int acquire_credentials(
* EEXIST if the credential already exists. That's because the TPM2-based decryption is kinda
* slow and involved, hence it's nice to be able to skip that if the credential already
* exists anyway. */
if (faccessat(dfd, sc->id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)
if (faccessat(dfd, sc->id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) {
log_debug("Skipping credential with duplicated ID %s", sc->id);
continue;
}
if (errno != ENOENT)
return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sc->id);

View File

@ -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);

View File

@ -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="))) {

View File

@ -640,7 +640,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);

View File

@ -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;
};

View File

@ -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)

View File

@ -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;
}

View File

@ -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)

View File

@ -273,8 +273,11 @@ rm -rf /tmp/ts54-creds
# Check that globs work as expected
mkdir -p /run/credstore
echo -n a >/run/credstore/test.creds.first
echo -n b >/run/credstore/test.creds.second
# Make sure that when multiple credentials of the same name are found, the first one is used (/etc/credstore
# is searched before /run/credstore).
echo -n ignored >/run/credstore/test.creds.second
mkdir -p /etc/credstore
echo -n b >/etc/credstore/test.creds.second
echo -n c >/etc/credstore/test.creds.third
systemd-run -p "ImportCredential=test.creds.*" \
--unit=test-54-ImportCredential.service \
@ -286,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

View File

@ -37,6 +37,8 @@ KillMode=process
{% endif %}
IgnoreSIGPIPE=no
SendSIGHUP=yes
ImportCredential=tty.console.agetty.*:agetty.
ImportCredential=tty.console.login.*:login.
ImportCredential=agetty.*
ImportCredential=login.*

View File

@ -43,5 +43,7 @@ KillMode=process
{% endif %}
IgnoreSIGPIPE=no
SendSIGHUP=yes
ImportCredential=tty.container.%I.agetty.*:agetty.
ImportCredential=tty.container.%I.login.*:login.
ImportCredential=agetty.*
ImportCredential=login.*

View File

@ -53,6 +53,8 @@ KillMode=process
{% endif %}
IgnoreSIGPIPE=no
SendSIGHUP=yes
ImportCredential=tty.virtual.%I.agetty.*:agetty.
ImportCredential=tty.virtual.%I.login.*:login.
ImportCredential=agetty.*
ImportCredential=login.*

View File

@ -47,6 +47,8 @@ KillMode=process
{% endif %}
IgnoreSIGPIPE=no
SendSIGHUP=yes
ImportCredential=tty.serial.%I.agetty.*:agetty.
ImportCredential=tty.serial.%I.login.*:login.
ImportCredential=agetty.*
ImportCredential=login.*