]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
pid1: search for creds in LoadCredential=/LoadCredentialEncrypted=
authorLennart Poettering <lennart@poettering.net>
Thu, 21 Apr 2022 13:32:21 +0000 (15:32 +0200)
committerLennart Poettering <lennart@poettering.net>
Thu, 28 Apr 2022 16:12:00 +0000 (18:12 +0200)
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
src/core/execute.h
src/core/manager.c
src/core/manager.h
src/core/unit.c

index b3e64ade1bd68fa6900c96da3a862c34b47a5f15..2762b10287969ec0f92e3931ae59603e57e59e07 100644 (file)
@@ -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 (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. */
 
-        } 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)
+                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
index 43070ce1117486b740343cd6fdcf8b32409479dd..904e7943f32e29c20ca821997c83717333d0de4a 100644 (file)
@@ -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;
 
index a68324affc5bcb2698978f55592d2a2e9ac87e2a..18daff66c78064d81572d191681b5a73be28103f 100644 (file)
@@ -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);
index 6f4bbb36ab1e5f32f26fa3f709160c15fcdfdb05..fd5da52b7f9ee2ec1a851f6b8c9658a44133b7a0 100644 (file)
@@ -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. */
index 5ab7601ed86dab0cfc3a931c230a700816e1f0e7..ff1288dcac0198b414e457e1e98fad0252f0e0e7 100644 (file)
@@ -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;
 }