mirror of
https://github.com/systemd/systemd.git
synced 2024-12-22 17:35:35 +03:00
core: Add support for renaming credentials with ImportCredential=
This allows for "per-instance" credentials for units. The use case is best explained with an example. Currently all our getty units have the following stanzas in their unit file: """ ImportCredential=agetty.* ImportCredential=login.* """ This means that setting agetty.autologin=root as a system credential will make every instance of our all our getty units autologin as the root user. This prevents us from doing autologin on /dev/hvc0 while still requiring manual login on all other ttys. To solve the issue, we introduce support for renaming credentials with ImportCredential=. This will allow us to add the following to e.g. serial-getty@.service: """ ImportCredential=tty.serial.%I.agetty.*:agetty. ImportCredential=tty.serial.%I.login.*:login. """ which for serial-getty@hvc0.service will make the service manager read all credentials of the form "tty.serial.hvc0.agetty.xxx" and pass them to the service in the form "agetty.xxx" (same goes for login). We can apply the same to each of the getty units to allow setting agetty and login credentials for individual ttys instead of globally.
This commit is contained in:
parent
3de13e6148
commit
831f208783
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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="))) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user