From 2ad591a3a3d0e0da43c0e3252cc15bd00af9bfb7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 21 Apr 2022 15:32:21 +0200 Subject: [PATCH] pid1: search for creds in LoadCredential=/LoadCredentialEncrypted= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for searching for credentials more comprehensively. Specifically, unless an absolute source path is specified we'll now search for the credentials in the system credentials first, and then in /etc/credstore/, /run/credstore/, and /usr/lib/credstore, making these dirs hence the recommended place for credentials to leave in the system. For LoadCredentialEncrypted= we'll also look into /etc/credstore.encrypted/, /run/credstore.encrypted/, …. These dirs are hence suitable for credentials whose provenience isn't trusted (e.g. UEFI creds from systemd-stub), and thus require to be authenticated before use. --- src/core/execute.c | 109 ++++++++++++++++++++++++++++++++++++++------- src/core/execute.h | 3 +- src/core/manager.c | 42 +++++++++++++---- src/core/manager.h | 3 +- src/core/unit.c | 3 +- 5 files changed, 133 insertions(+), 27 deletions(-) diff --git a/src/core/execute.c b/src/core/execute.c index b3e64ade1b..2762b10287 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -2595,6 +2595,42 @@ static int write_credential( return 0; } +static char **credential_search_path( + const ExecParameters *params, + bool encrypted) { + + _cleanup_strv_free_ char **l = NULL; + + assert(params); + + /* Assemble a search path to find credentials in. We'll look in /etc/credstore/ (and similar + * directories in /usr/lib/ + /run/) for all types of credentials. If we are looking for encrypted + * credentials, also look in /etc/credstore.encrypted/ (and similar dirs). */ + + if (encrypted) { + if (strv_extend(&l, params->received_encrypted_credentials_directory) < 0) + return NULL; + + if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore.encrypted"), /* filter_duplicates= */ true) < 0) + return NULL; + } + + if (params->received_credentials_directory) + if (strv_extend(&l, params->received_credentials_directory) < 0) + return NULL; + + if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore"), /* filter_duplicates= */ true) < 0) + return NULL; + + if (DEBUG_LOGGING) { + _cleanup_free_ char *t = strv_join(l, ":"); + + log_debug("Credential search path is: %s", t); + } + + return TAKE_PTR(l); +} + static int load_credential( const ExecContext *context, const ExecParameters *params, @@ -2609,11 +2645,12 @@ static int load_credential( uint64_t *left) { ReadFullFileFlags flags = READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER; + _cleanup_strv_free_ char **search_path = NULL; _cleanup_(erase_and_freep) char *data = NULL; - _cleanup_free_ char *j = NULL, *bindname = NULL; + _cleanup_free_ char *bindname = NULL; + const char *source = NULL; bool missing_ok = true; - const char *source; - size_t size, add; + size_t size, add, maxsz; int r; assert(context); @@ -2624,10 +2661,25 @@ static int load_credential( assert(write_dfd >= 0); assert(left); - if (path_is_absolute(path) || read_dfd >= 0) { - /* If this is an absolute path (or a directory fd is specifier relative which to read), read - * the data directly from it, and support AF_UNIX sockets */ + if (read_dfd >= 0) { + /* If a directory fd is specified, then read the file directly from that dir. In this case we + * won't do AF_UNIX stuff (we simply don't want to recursively iterate down a tree of AF_UNIX + * IPC sockets). It's OK if a file vanishes here in the time we enumerate it and intend to + * open it. */ + + if (!filename_is_valid(path)) /* safety check */ + return -EINVAL; + + missing_ok = true; source = path; + + } else if (path_is_absolute(path)) { + /* If this is an absolute path, read the data directly from it, and support AF_UNIX + * sockets */ + + if (!path_is_valid(path)) /* safety check */ + return -EINVAL; + flags |= READ_FULL_FILE_CONNECT_SOCKET; /* Pass some minimal info about the unit and the credential name we are looking to acquire @@ -2636,25 +2688,50 @@ static int load_credential( return -ENOMEM; missing_ok = false; + source = path; - } else if (params->received_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, path); - if (!j) + } else if (credential_name_valid(path)) { + /* If this is a relative path, take it as credential name relative to the credentials + * directory 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. */ + + search_path = credential_search_path(params, encrypted); + if (!search_path) return -ENOMEM; - source = j; + missing_ok = true; } else source = NULL; - if (source) + if (encrypted) + flags |= READ_FULL_FILE_UNBASE64; + + maxsz = encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX; + + if (search_path) { + STRV_FOREACH(d, search_path) { + _cleanup_free_ char *j = NULL; + + j = path_join(*d, path); + if (!j) + return -ENOMEM; + + r = read_full_file_full( + AT_FDCWD, j, /* path is absolute, hence pass AT_FDCWD as nop dir fd here */ + UINT64_MAX, + maxsz, + flags, + NULL, + &data, &size); + if (r != -ENOENT) + break; + } + } else if (source) r = read_full_file_full( read_dfd, source, UINT64_MAX, - encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX, - flags | (encrypted ? READ_FULL_FILE_UNBASE64 : 0), + maxsz, + flags, bindname, &data, &size); else diff --git a/src/core/execute.h b/src/core/execute.h index 43070ce111..904e7943f3 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -406,7 +406,8 @@ struct ExecParameters { const char *cgroup_path; char **prefix; - const char *received_credentials; + const char *received_credentials_directory; + const char *received_encrypted_credentials_directory; const char *confirm_spawn; diff --git a/src/core/manager.c b/src/core/manager.c index a68324affc..18daff66c7 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -777,9 +777,37 @@ static int manager_setup_sigchld_event_source(Manager *m) { return 0; } +static int manager_find_credentials_dirs(Manager *m) { + const char *e; + int r; + + assert(m); + + r = get_credentials_dir(&e); + if (r < 0) { + if (r != -ENXIO) + log_debug_errno(r, "Failed to determine credentials directory, ignoring: %m"); + } else { + m->received_credentials_directory = strdup(e); + if (!m->received_credentials_directory) + return -ENOMEM; + } + + r = get_encrypted_credentials_dir(&e); + if (r < 0) { + if (r != -ENXIO) + log_debug_errno(r, "Failed to determine encrypted credentials directory, ignoring: %m"); + } else { + m->received_encrypted_credentials_directory = strdup(e); + if (!m->received_encrypted_credentials_directory) + return -ENOMEM; + } + + return 0; +} + int manager_new(LookupScope scope, ManagerTestRunFlags test_run_flags, Manager **_m) { _cleanup_(manager_freep) Manager *m = NULL; - const char *e; int r; assert(_m); @@ -883,12 +911,9 @@ int manager_new(LookupScope scope, ManagerTestRunFlags test_run_flags, Manager * if (r < 0) return r; - r = get_credentials_dir(&e); - if (r >= 0) { - m->received_credentials = strdup(e); - if (!m->received_credentials) - return -ENOMEM; - } + r = manager_find_credentials_dirs(m); + if (r < 0) + return r; r = sd_event_default(&m->event); if (r < 0) @@ -1533,7 +1558,8 @@ Manager* manager_free(Manager *m) { for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) m->prefix[dt] = mfree(m->prefix[dt]); - free(m->received_credentials); + free(m->received_credentials_directory); + free(m->received_encrypted_credentials_directory); free(m->watchdog_pretimeout_governor); free(m->watchdog_pretimeout_governor_overridden); diff --git a/src/core/manager.h b/src/core/manager.h index 6f4bbb36ab..fd5da52b7f 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -438,7 +438,8 @@ struct Manager { /* Prefixes of e.g. RuntimeDirectory= */ char *prefix[_EXEC_DIRECTORY_TYPE_MAX]; - char *received_credentials; + char *received_credentials_directory; + char *received_encrypted_credentials_directory; /* Used in the SIGCHLD and sd_notify() message invocation logic to avoid that we dispatch the same event * multiple times on the same unit. */ diff --git a/src/core/unit.c b/src/core/unit.c index 5ab7601ed8..ff1288dcac 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -5032,7 +5032,8 @@ int unit_set_exec_params(Unit *u, ExecParameters *p) { p->cgroup_path = u->cgroup_path; SET_FLAG(p->flags, EXEC_CGROUP_DELEGATE, unit_cgroup_delegate(u)); - p->received_credentials = u->manager->received_credentials; + p->received_credentials_directory = u->manager->received_credentials_directory; + p->received_encrypted_credentials_directory = u->manager->received_encrypted_credentials_directory; return 0; }