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:
commit
e54a8e0fc3
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
@ -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="))) {
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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.*
|
||||
|
||||
|
@ -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.*
|
||||
|
@ -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.*
|
||||
|
||||
|
@ -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.*
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user