diff options
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/dbus-execute.c | 71 | ||||
-rw-r--r-- | src/core/execute.c | 124 | ||||
-rw-r--r-- | src/core/execute.h | 13 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.in | 2 | ||||
-rw-r--r-- | src/core/load-fragment.c | 89 |
5 files changed, 224 insertions, 75 deletions
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 50daef6702..f6783e924a 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -820,6 +820,9 @@ static int property_get_set_credential( HASHMAP_FOREACH(sc, c->set_credentials) { + if (sc->encrypted != streq(property, "SetCredentialEncrypted")) + continue; + r = sd_bus_message_open_container(reply, 'r', "say"); if (r < 0) return r; @@ -850,7 +853,7 @@ static int property_get_load_credential( sd_bus_error *error) { ExecContext *c = userdata; - char **i, **j; + ExecLoadCredential *lc; int r; assert(bus); @@ -862,8 +865,12 @@ static int property_get_load_credential( if (r < 0) return r; - STRV_FOREACH_PAIR(i, j, c->load_credentials) { - r = sd_bus_message_append(reply, "(ss)", *i, *j); + HASHMAP_FOREACH(lc, c->load_credentials) { + + if (lc->encrypted != streq(property, "LoadCredentialEncrypted")) + continue; + + r = sd_bus_message_append(reply, "(ss)", lc->id, lc->path); if (r < 0) return r; } @@ -1144,7 +1151,9 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("DynamicUser", "b", bus_property_get_bool, offsetof(ExecContext, dynamic_user), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RemoveIPC", "b", bus_property_get_bool, offsetof(ExecContext, remove_ipc), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("SetCredential", "a(say)", property_get_set_credential, 0, SD_BUS_VTABLE_PROPERTY_CONST), + 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("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), @@ -1916,7 +1925,7 @@ int bus_exec_context_set_transient_property( return 1; - } else if (streq(name, "SetCredential")) { + } else if (STR_IN_SET(name, "SetCredential", "SetCredentialEncrypted")) { bool isempty = true; r = sd_bus_message_enter_container(message, 'a', "(say)"); @@ -1964,20 +1973,24 @@ int bus_exec_context_set_transient_property( if (old) { free_and_replace(old->data, copy); old->size = sz; + old->encrypted = streq(name, "SetCredentialEncrypted"); } else { _cleanup_(exec_set_credential_freep) ExecSetCredential *sc = NULL; - sc = new0(ExecSetCredential, 1); + sc = new(ExecSetCredential, 1); if (!sc) return -ENOMEM; - sc->id = strdup(id); + *sc = (ExecSetCredential) { + .id = strdup(id), + .data = TAKE_PTR(copy), + .size = sz, + .encrypted = streq(name, "SetCredentialEncrypted"), + }; + if (!sc->id) return -ENOMEM; - sc->data = TAKE_PTR(copy); - sc->size = sz; - r = hashmap_ensure_put(&c->set_credentials, &exec_set_credential_hash_ops, sc->id, sc); if (r < 0) return r; @@ -2008,7 +2021,7 @@ int bus_exec_context_set_transient_property( return 1; - } else if (streq(name, "LoadCredential")) { + } else if (STR_IN_SET(name, "LoadCredential", "LoadCredentialEncrypted")) { bool isempty = true; r = sd_bus_message_enter_container(message, 'a', "(ss)"); @@ -2033,9 +2046,39 @@ int bus_exec_context_set_transient_property( isempty = false; if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - r = strv_extend_strv(&c->load_credentials, STRV_MAKE(id, source), /* filter_duplicates = */ false); - if (r < 0) - return r; + _cleanup_free_ char *copy = NULL; + ExecLoadCredential *old; + + copy = strdup(source); + if (!copy) + return -ENOMEM; + + old = hashmap_get(c->load_credentials, id); + if (old) { + free_and_replace(old->path, copy); + old->encrypted = streq(name, "LoadCredentialEncrypted"); + } else { + _cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL; + + lc = new(ExecLoadCredential, 1); + if (!lc) + return -ENOMEM; + + *lc = (ExecLoadCredential) { + .id = strdup(id), + .path = TAKE_PTR(copy), + .encrypted = streq(name, "LoadCredentialEncrypted"), + }; + + if (!lc->id) + return -ENOMEM; + + r = hashmap_ensure_put(&c->load_credentials, &exec_load_credential_hash_ops, lc->id, lc); + if (r < 0) + return r; + + TAKE_PTR(lc); + } (void) unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s:%s", name, id, source); } @@ -2046,7 +2089,7 @@ int bus_exec_context_set_transient_property( return r; if (!UNIT_WRITE_FLAGS_NOOP(flags) && isempty) { - c->load_credentials = strv_free(c->load_credentials); + c->load_credentials = hashmap_free(c->load_credentials); (void) unit_write_settingf(u, flags, name, "%s=", name); } diff --git a/src/core/execute.c b/src/core/execute.c index 2a337b55a2..f760d6ae07 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -46,6 +46,7 @@ #include "cgroup-setup.h" #include "chown-recursive.h" #include "cpu-set-util.h" +#include "creds-util.h" #include "data-fd-util.h" #include "def.h" #include "env-file.h" @@ -1454,7 +1455,7 @@ static bool exec_context_has_credentials(const ExecContext *context) { assert(context); return !hashmap_isempty(context->set_credentials) || - context->load_credentials; + !hashmap_isempty(context->load_credentials); } #if HAVE_SECCOMP @@ -2486,7 +2487,7 @@ static int write_credential( return -errno; } - r = loop_write(fd, data, size, /* do_pool = */ false); + r = loop_write(fd, data, size, /* do_poll = */ false); if (r < 0) return r; @@ -2519,8 +2520,6 @@ static int write_credential( return 0; } -#define CREDENTIALS_BYTES_MAX (1024LU * 1024LU) /* Refuse to pass more than 1M, after all this is unswappable memory */ - static int acquire_credentials( const ExecContext *context, const ExecParameters *params, @@ -2529,10 +2528,10 @@ static int acquire_credentials( uid_t uid, bool ownership_ok) { - uint64_t left = CREDENTIALS_BYTES_MAX; + uint64_t left = CREDENTIALS_TOTAL_SIZE_MAX; _cleanup_close_ int dfd = -1; + ExecLoadCredential *lc; ExecSetCredential *sc; - char **id, **fn; int r; assert(context); @@ -2542,39 +2541,23 @@ static int acquire_credentials( if (dfd < 0) return -errno; - /* First we use the literally specified credentials. Note that they might be overridden again below, - * and thus act as a "default" if the same credential is specified multiple times */ - HASHMAP_FOREACH(sc, context->set_credentials) { - size_t add; - - add = strlen(sc->id) + sc->size; - if (add > left) - return -E2BIG; - - r = write_credential(dfd, sc->id, sc->data, sc->size, uid, ownership_ok); - if (r < 0) - return r; - - left -= add; - } - - /* Then, load credential off disk (or acquire via AF_UNIX socket) */ - STRV_FOREACH_PAIR(id, fn, context->load_credentials) { - ReadFullFileFlags flags = READ_FULL_FILE_SECURE; + /* First, load credentials off disk (or acquire via AF_UNIX socket) */ + HASHMAP_FOREACH(lc, context->load_credentials) { + ReadFullFileFlags flags = READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER; _cleanup_(erase_and_freep) char *data = NULL; _cleanup_free_ char *j = NULL, *bindname = NULL; bool missing_ok = true; const char *source; size_t size, add; - if (path_is_absolute(*fn)) { + if (path_is_absolute(lc->path)) { /* If this is an absolute path, read the data directly from it, and support AF_UNIX sockets */ - source = *fn; + source = lc->path; flags |= READ_FULL_FILE_CONNECT_SOCKET; /* Pass some minimal info about the unit and the credential name we are looking to acquire * via the source socket address in case we read off an AF_UNIX socket. */ - if (asprintf(&bindname, "@%" PRIx64"/unit/%s/%s", random_u64(), unit, *id) < 0) + if (asprintf(&bindname, "@%" PRIx64"/unit/%s/%s", random_u64(), unit, lc->id) < 0) return -ENOMEM; missing_ok = false; @@ -2583,7 +2566,7 @@ static int acquire_credentials( /* If this is a relative path, take it relative to the credentials we received * ourselves. We don't support the AF_UNIX stuff in this mode, since we are operating * on a credential store, i.e. this is guaranteed to be regular files. */ - j = path_join(params->received_credentials, *fn); + j = path_join(params->received_credentials, lc->path); if (!j) return -ENOMEM; @@ -2592,34 +2575,87 @@ static int acquire_credentials( source = NULL; if (source) - r = read_full_file_full(AT_FDCWD, source, UINT64_MAX, SIZE_MAX, flags, bindname, &data, &size); + r = read_full_file_full( + AT_FDCWD, source, + UINT64_MAX, + lc->encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX, + flags | (lc->encrypted ? READ_FULL_FILE_UNBASE64 : 0), + bindname, + &data, &size); else r = -ENOENT; - if (r == -ENOENT && (missing_ok || faccessat(dfd, *id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0)) { + if (r == -ENOENT && (missing_ok || hashmap_contains(context->set_credentials, lc->id))) { /* Make a missing inherited credential non-fatal, let's just continue. After all apps * will get clear errors if we don't pass such a missing credential on as they * themselves will get ENOENT when trying to read them, which should not be much * worse than when we handle the error here and make it fatal. * - * Also, if the source file doesn't exist, but we already acquired the key otherwise, - * then don't fail either. */ - log_debug_errno(r, "Couldn't read inherited credential '%s', skipping: %m", *fn); + * Also, if the source file doesn't exist, but a fallback is set via SetCredentials= + * we are fine, too. */ + log_debug_errno(r, "Couldn't read inherited credential '%s', skipping: %m", lc->path); continue; } if (r < 0) - return log_debug_errno(r, "Failed to read credential '%s': %m", *fn); + return log_debug_errno(r, "Failed to read credential '%s': %m", lc->path); + + if (lc->encrypted) { + _cleanup_free_ void *plaintext = NULL; + size_t plaintext_size = 0; + + r = decrypt_credential_and_warn(lc->id, now(CLOCK_REALTIME), NULL, data, size, &plaintext, &plaintext_size); + if (r < 0) + return r; - add = strlen(*id) + size; + free_and_replace(data, plaintext); + size = plaintext_size; + } + + add = strlen(lc->id) + size; if (add > left) return -E2BIG; - r = write_credential(dfd, *id, data, size, uid, ownership_ok); + r = write_credential(dfd, lc->id, data, size, uid, ownership_ok); if (r < 0) return r; left -= add; } + /* First we use the literally specified credentials. Note that they might be overridden again below, + * and thus act as a "default" if the same credential is specified multiple times */ + HASHMAP_FOREACH(sc, context->set_credentials) { + _cleanup_(erase_and_freep) void *plaintext = NULL; + const char *data; + size_t size, add; + + if (faccessat(dfd, sc->id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) + continue; + if (errno != ENOENT) + return log_debug_errno(errno, "Failed to test if credential %s exists: %m", sc->id); + + if (sc->encrypted) { + r = decrypt_credential_and_warn(sc->id, now(CLOCK_REALTIME), NULL, sc->data, sc->size, &plaintext, &size); + if (r < 0) + return r; + + data = plaintext; + } else { + data = sc->data; + size = sc->size; + } + + add = strlen(sc->id) + size; + if (add > left) + return -E2BIG; + + r = write_credential(dfd, sc->id, data, size, uid, ownership_ok); + if (r < 0) + return r; + + + left -= add; + } + if (fchmod(dfd, 0500) < 0) /* Now take away the "w" bit */ return -errno; @@ -2715,7 +2751,7 @@ static int setup_credentials_internal( } else if (try == 1) { _cleanup_free_ char *opts = NULL; - if (asprintf(&opts, "mode=0700,nr_inodes=1024,size=%lu", CREDENTIALS_BYTES_MAX) < 0) + if (asprintf(&opts, "mode=0700,nr_inodes=1024,size=%zu", (size_t) CREDENTIALS_TOTAL_SIZE_MAX) < 0) return -ENOMEM; /* Fall back to "tmpfs" otherwise */ @@ -4928,7 +4964,7 @@ void exec_context_done(ExecContext *c) { c->log_namespace = mfree(c->log_namespace); - c->load_credentials = strv_free(c->load_credentials); + c->load_credentials = hashmap_free(c->load_credentials); c->set_credentials = hashmap_free(c->set_credentials); } @@ -6597,7 +6633,17 @@ ExecSetCredential *exec_set_credential_free(ExecSetCredential *sc) { return mfree(sc); } +ExecLoadCredential *exec_load_credential_free(ExecLoadCredential *lc) { + if (!lc) + return NULL; + + free(lc->id); + free(lc->path); + return mfree(lc); +} + DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(exec_set_credential_hash_ops, char, string_hash_func, string_compare_func, ExecSetCredential, exec_set_credential_free); +DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(exec_load_credential_hash_ops, char, string_hash_func, string_compare_func, ExecLoadCredential, exec_load_credential_free); static const char* const exec_input_table[_EXEC_INPUT_MAX] = { [EXEC_INPUT_NULL] = "null", diff --git a/src/core/execute.h b/src/core/execute.h index 4c7a5b874f..fceb8bb6f7 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -150,9 +150,16 @@ typedef enum ExecCleanMask { _EXEC_CLEAN_MASK_INVALID = -EINVAL, } ExecCleanMask; +/* A credential configured with LoadCredential= */ +typedef struct ExecLoadCredential { + char *id, *path; + bool encrypted; +} ExecLoadCredential; + /* A credential configured with SetCredential= */ typedef struct ExecSetCredential { char *id; + bool encrypted; void *data; size_t size; } ExecSetCredential; @@ -325,7 +332,7 @@ struct ExecContext { usec_t timeout_clean_usec; Hashmap *set_credentials; /* output id → ExecSetCredential */ - char **load_credentials; /* pairs of output id, path/input id */ + Hashmap *load_credentials; /* output id → ExecLoadCredential */ }; static inline bool exec_context_restrict_namespaces_set(const ExecContext *c) { @@ -458,7 +465,11 @@ bool exec_context_get_cpu_affinity_from_numa(const ExecContext *c); 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); + extern const struct hash_ops exec_set_credential_hash_ops; +extern const struct hash_ops exec_load_credential_hash_ops; const char* exec_output_to_string(ExecOutput i) _const_; ExecOutput exec_output_from_string(const char *s) _pure_; diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in index 42441eab6e..96507907f6 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -139,7 +139,9 @@ {{type}}.ConfigurationDirectoryMode, config_parse_mode, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION].mode) {{type}}.ConfigurationDirectory, config_parse_exec_directories, 0, offsetof({{type}}, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION].paths) {{type}}.SetCredential, config_parse_set_credential, 0, offsetof({{type}}, exec_context) +{{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}}.TimeoutCleanSec, config_parse_sec, 0, offsetof({{type}}, exec_context.timeout_clean_usec) {% if HAVE_PAM %} {{type}}.PAMName, config_parse_unit_string_printf, 0, offsetof({{type}}, exec_context.pam_name) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 8fb3c378ee..c72f5c22da 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -4469,12 +4469,15 @@ int config_parse_set_credential( void *data, void *userdata) { - _cleanup_free_ char *word = NULL, *k = NULL, *unescaped = NULL; + _cleanup_free_ char *word = NULL, *k = NULL; + _cleanup_free_ void *d = NULL; ExecContext *context = data; ExecSetCredential *old; Unit *u = userdata; - const char *p; - int r, l; + bool encrypted = ltype; + const char *p = rvalue; + size_t size; + int r; assert(filename); assert(lvalue); @@ -4487,7 +4490,6 @@ int config_parse_set_credential( return 0; } - p = rvalue; r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS); if (r == -ENOMEM) return log_oom(); @@ -4506,33 +4508,51 @@ int config_parse_set_credential( return 0; } - /* We support escape codes here, so that users can insert trailing \n if they like */ - l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped); - if (l < 0) { - log_syntax(unit, LOG_WARNING, filename, line, l, "Can't unescape \"%s\", ignoring: %m", p); - return 0; + if (encrypted) { + r = unbase64mem_full(p, SIZE_MAX, true, &d, &size); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Encrypted credential data not valid Base64 data, ignoring."); + return 0; + } + } else { + char *unescaped = NULL; + int l; + + /* We support escape codes here, so that users can insert trailing \n if they like */ + l = cunescape(p, UNESCAPE_ACCEPT_NUL, &unescaped); + if (l < 0) { + log_syntax(unit, LOG_WARNING, filename, line, l, "Can't unescape \"%s\", ignoring: %m", p); + return 0; + } + + d = unescaped; + size = l; } old = hashmap_get(context->set_credentials, k); if (old) { - free_and_replace(old->data, unescaped); - old->size = l; + free_and_replace(old->data, d); + old->size = size; + old->encrypted = encrypted; } else { _cleanup_(exec_set_credential_freep) ExecSetCredential *sc = NULL; - sc = new0(ExecSetCredential, 1); + sc = new(ExecSetCredential, 1); if (!sc) return log_oom(); - sc->id = TAKE_PTR(k); - sc->data = TAKE_PTR(unescaped); - sc->size = l; + *sc = (ExecSetCredential) { + .id = TAKE_PTR(k), + .data = TAKE_PTR(d), + .size = size, + .encrypted = encrypted, + }; r = hashmap_ensure_put(&context->set_credentials, &exec_set_credential_hash_ops, sc->id, sc); if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, l, + log_syntax(unit, LOG_WARNING, filename, line, r, "Duplicated credential value '%s', ignoring assignment: %s", sc->id, rvalue); return 0; } @@ -4557,6 +4577,8 @@ int config_parse_load_credential( _cleanup_free_ char *word = NULL, *k = NULL, *q = NULL; ExecContext *context = data; + ExecLoadCredential *old; + bool encrypted = ltype; Unit *u = userdata; const char *p; int r; @@ -4568,7 +4590,7 @@ int config_parse_load_credential( if (isempty(rvalue)) { /* Empty assignment resets the list */ - context->load_credentials = strv_free(context->load_credentials); + context->load_credentials = hashmap_free(context->load_credentials); return 0; } @@ -4604,14 +4626,39 @@ int config_parse_load_credential( return 0; } if (path_is_absolute(q) ? !path_is_normalized(q) : !credential_name_valid(q)) { - log_syntax(unit, LOG_WARNING, filename, line, r, "Credential source \"%s\" not valid, ignoring.", q); + log_syntax(unit, LOG_WARNING, filename, line, 0, "Credential source \"%s\" not valid, ignoring.", q); return 0; } } - r = strv_consume_pair(&context->load_credentials, TAKE_PTR(k), TAKE_PTR(q)); - if (r < 0) - return log_oom(); + old = hashmap_get(context->load_credentials, k); + if (old) { + free_and_replace(old->path, q); + old->encrypted = encrypted; + } else { + _cleanup_(exec_load_credential_freep) ExecLoadCredential *lc = NULL; + + lc = new(ExecLoadCredential, 1); + if (!lc) + return log_oom(); + + *lc = (ExecLoadCredential) { + .id = TAKE_PTR(k), + .path = TAKE_PTR(q), + .encrypted = encrypted, + }; + + r = hashmap_ensure_put(&context->load_credentials, &exec_load_credential_hash_ops, lc->id, lc); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Duplicated credential value '%s', ignoring assignment: %s", lc->id, rvalue); + return 0; + } + + TAKE_PTR(lc); + } return 0; } |