From cf7d0a2d2e00846096908082ffc3fc1953e015e6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Dec 2024 11:10:30 +0100 Subject: [PATCH 01/11] pid1: normalize oom error handling a bit --- src/core/exec-credential.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/core/exec-credential.c b/src/core/exec-credential.c index bce0ee7968e..784cea208d6 100644 --- a/src/core/exec-credential.c +++ b/src/core/exec-credential.c @@ -117,10 +117,9 @@ int exec_context_put_load_credential(ExecContext *c, const char *id, const char return -ENOMEM; r = hashmap_ensure_put(&c->load_credentials, &exec_load_credential_hash_ops, lc->id, lc); - if (r < 0) { - assert(r != -EEXIST); + assert(r != -EEXIST); + if (r < 0) return r; - } TAKE_PTR(lc); } @@ -167,10 +166,9 @@ int exec_context_put_set_credential( return -ENOMEM; r = hashmap_ensure_put(&c->set_credentials, &exec_set_credential_hash_ops, sc->id, sc); - if (r < 0) { - assert(r != -EEXIST); + assert(r != -EEXIST); + if (r < 0) return r; - } TAKE_PTR(sc); } @@ -193,19 +191,22 @@ int exec_context_put_import_credential(ExecContext *c, const char *glob, const c *ic = (ExecImportCredential) { .glob = strdup(glob), - .rename = rename ? strdup(rename) : NULL, }; - if (!ic->glob || (rename && !ic->rename)) + if (!ic->glob) return -ENOMEM; + if (rename) { + ic->rename = strdup(rename); + if (!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); + assert(r != -EEXIST); + if (r < 0) return r; - } TAKE_PTR(ic); From 616586b91003adf08c56b5f63e60b6f8a4dbe893 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Dec 2024 13:37:56 +0100 Subject: [PATCH 02/11] sd-path: don't chop off trailing slash in sd_path apis, when user provided them This is a minor compat break, but given the slow adoption of the sd-path.h APIs I think it's one we should take. Basically, the idea is that if the user provides a suffix path with a trailing slash (thus encoding in the path that the last element must be a dir), we should keep it in place, and not suppress it, in order to not willy nilly reduce the amount of information contained in the path. Simplifications that do not alter meaning, and do not suppress information should be fine to apply to a path, but otherwise we really should be conservative on this. --- src/libsystemd/sd-path/sd-path.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c index 9f495f30512..a2f03f52e92 100644 --- a/src/libsystemd/sd-path/sd-path.c +++ b/src/libsystemd/sd-path/sd-path.c @@ -366,12 +366,12 @@ static int get_path_alloc(uint64_t type, const char *suffix, char **ret) { if (r < 0) return r; - if (suffix) { + if (!isempty(suffix)) { char *suffixed = path_join(p, suffix); if (!suffixed) return -ENOMEM; - path_simplify(suffixed); + path_simplify_full(suffixed, PATH_SIMPLIFY_KEEP_TRAILING_SLASH); free_and_replace(buffer, suffixed); } else if (!buffer) { @@ -637,7 +637,7 @@ _public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***ret) if (!path_extend(i, suffix)) return -ENOMEM; - path_simplify(*i); + path_simplify_full(*i, PATH_SIMPLIFY_KEEP_TRAILING_SLASH); } *ret = TAKE_PTR(l); From 81082f2dc2d80efdf6c7e9a84df90dcd13b3d0ae Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Dec 2024 14:01:13 +0100 Subject: [PATCH 03/11] systemd-path: order all listed paths by their ID alphabetically Let's add some system to the madness, given we added user-specific dirs to the end of the list, but they should really be listed together with the other user-specific ones. --- src/path/path.c | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/path/path.c b/src/path/path.c index 3ab09344b4e..604e4c170b6 100644 --- a/src/path/path.c +++ b/src/path/path.c @@ -14,6 +14,7 @@ #include "main-func.h" #include "pager.h" #include "pretty-print.h" +#include "sort-util.h" #include "string-util.h" static const char *arg_suffix = NULL; @@ -103,25 +104,38 @@ static const char* const path_table[_SD_PATH_MAX] = { [SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR] = "systemd-search-user-environment-generator", }; +static int order_cmp(const size_t *a, const size_t *b) { + assert(*a < ELEMENTSOF(path_table)); + assert(*b < ELEMENTSOF(path_table)); + return strcmp(path_table[*a], path_table[*b]); +} + static int list_paths(void) { - int r = 0; + int ret = 0, r; pager_open(arg_pager_flags); - for (size_t i = 0; i < ELEMENTSOF(path_table); i++) { - _cleanup_free_ char *p = NULL; - int q; + size_t order[ELEMENTSOF(path_table)]; - q = sd_path_lookup(i, arg_suffix, &p); - if (q < 0) { - log_full_errno(q == -ENXIO ? LOG_DEBUG : LOG_ERR, - q, "Failed to query %s: %m", path_table[i]); - if (q != -ENXIO) - RET_GATHER(r, q); + for (size_t i = 0; i < ELEMENTSOF(order); i++) + order[i] = i; + + typesafe_qsort(order, ELEMENTSOF(order), order_cmp); + + for (size_t i = 0; i < ELEMENTSOF(order); i++) { + size_t j = order[i]; + const char *t = ASSERT_PTR(path_table[j]); + + _cleanup_free_ char *p = NULL; + r = sd_path_lookup(j, arg_suffix, &p); + if (r < 0) { + log_full_errno(r == -ENXIO ? LOG_DEBUG : LOG_ERR, r, "Failed to query %s, proceeding: %m", t); + if (r != -ENXIO) + RET_GATHER(ret, r); continue; } - printf("%s%s:%s %s\n", ansi_highlight(), path_table[i], ansi_normal(), p); + printf("%s%s:%s %s\n", ansi_highlight(), t, ansi_normal(), p); } return r; From 060e2512cdb1bf985965d991e0bb746ff21362ee Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Dec 2024 14:05:04 +0100 Subject: [PATCH 04/11] systemd-path: guarantee that tool exit status is zero on success Let's not inherit the error code from an earlier function invocation. --- src/path/path.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/path/path.c b/src/path/path.c index 604e4c170b6..8abfc6c7f2b 100644 --- a/src/path/path.c +++ b/src/path/path.c @@ -238,10 +238,11 @@ static int run(int argc, char* argv[]) { if (r <= 0) return r; - if (argc > optind) + if (argc > optind) { + r = 0; for (int i = optind; i < argc; i++) RET_GATHER(r, print_path(argv[i])); - else + } else r = list_paths(); return r; From b226b7fb6d5269973f8a1927735b8bf16c469f6e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Dec 2024 21:38:37 +0100 Subject: [PATCH 05/11] systemd-path: add the usual ANSI sequences to --help text --- src/path/path.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/path/path.c b/src/path/path.c index 8abfc6c7f2b..ad65437c8fe 100644 --- a/src/path/path.c +++ b/src/path/path.c @@ -168,14 +168,16 @@ static int help(void) { if (r < 0) return log_oom(); - printf("%s [OPTIONS...] [NAME...]\n\n" - "Show system and user paths.\n\n" + printf("%s [OPTIONS...] [NAME...]\n" + "\n%sShow system and user paths.%s\n\n" " -h --help Show this help\n" " --version Show package version\n" " --suffix=SUFFIX Suffix to append to paths\n" " --no-pager Do not pipe output into a pager\n" "\nSee the %s for details.\n", program_invocation_short_name, + ansi_highlight(), + ansi_normal(), link); return 0; From d2cd18932422563c12a6f6e7f3019750784705ab Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Dec 2024 14:34:41 +0100 Subject: [PATCH 06/11] sd-path: expose credential store in sd-path --- src/libsystemd/sd-path/sd-path.c | 78 +++++++++++++++++++++++++++++++- src/path/path.c | 10 ++++ src/systemd/sd-path.h | 10 ++++ 3 files changed, 97 insertions(+), 1 deletion(-) diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c index a2f03f52e92..d5363520383 100644 --- a/src/libsystemd/sd-path/sd-path.c +++ b/src/libsystemd/sd-path/sd-path.c @@ -36,7 +36,12 @@ static int from_environment(const char *envname, const char *fallback, const cha return -ENXIO; } -static int from_home_dir(const char *envname, const char *suffix, char **buffer, const char **ret) { +static int from_home_dir( + const char *envname, + const char *suffix, + char **buffer, + const char **ret) { + _cleanup_free_ char *h = NULL; int r; @@ -350,6 +355,30 @@ static int get_path(uint64_t type, char **buffer, const char **ret) { case SD_PATH_SYSTEMD_USER_ENVIRONMENT_GENERATOR: *ret = USER_ENV_GENERATOR_DIR; return 0; + + case SD_PATH_SYSTEM_CREDENTIAL_STORE: + *ret = "/etc/credstore"; + return 0; + + case SD_PATH_SYSTEM_CREDENTIAL_STORE_ENCRYPTED: + *ret = "/etc/credstore.encrypted"; + return 0; + + case SD_PATH_USER_CREDENTIAL_STORE: + r = xdg_user_config_dir("credstore", buffer); + if (r < 0) + return r; + + *ret = *buffer; + return 0; + + case SD_PATH_USER_CREDENTIAL_STORE_ENCRYPTED: + r = xdg_user_config_dir("credstore.encrypted", buffer); + if (r < 0) + return r; + + *ret = *buffer; + return 0; } return -EOPNOTSUPP; @@ -601,8 +630,55 @@ static int get_search(uint64_t type, char ***ret) { case SD_PATH_SYSTEMD_SEARCH_NETWORK: return strv_from_nulstr(ret, NETWORK_DIRS_NULSTR); + case SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE: + case SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE_ENCRYPTED: { + const char *suffix = + type == SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE_ENCRYPTED ? "credstore.encrypted" : "credstore"; + + _cleanup_strv_free_ char **l = NULL; + FOREACH_STRING(d, CONF_PATHS("")) { + char *j = path_join(d, suffix); + if (!j) + return -ENOMEM; + + r = strv_consume(&l, TAKE_PTR(j)); + if (r < 0) + return r; + } + + *ret = TAKE_PTR(l); + return 0; } + case SD_PATH_USER_SEARCH_CREDENTIAL_STORE: + case SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED: { + const char *suffix = + type == SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED ? "credstore.encrypted" : "credstore"; + + static const uint64_t dirs[] = { + SD_PATH_USER_CONFIGURATION, + SD_PATH_USER_RUNTIME, + SD_PATH_USER_LIBRARY_PRIVATE, + }; + + _cleanup_strv_free_ char **l = NULL; + FOREACH_ELEMENT(d, dirs) { + _cleanup_free_ char *p = NULL; + r = sd_path_lookup(*d, suffix, &p); + if (r == -ENXIO) + continue; + if (r < 0) + return r; + + r = strv_consume(&l, TAKE_PTR(p)); + if (r < 0) + return r; + } + + *ret = TAKE_PTR(l); + return 0; + }} + return -EOPNOTSUPP; } diff --git a/src/path/path.c b/src/path/path.c index ad65437c8fe..6ca2bd8c388 100644 --- a/src/path/path.c +++ b/src/path/path.c @@ -102,6 +102,16 @@ static const char* const path_table[_SD_PATH_MAX] = { [SD_PATH_SYSTEMD_USER_ENVIRONMENT_GENERATOR] = "systemd-user-environment-generator", [SD_PATH_SYSTEMD_SEARCH_SYSTEM_ENVIRONMENT_GENERATOR] = "systemd-search-system-environment-generator", [SD_PATH_SYSTEMD_SEARCH_USER_ENVIRONMENT_GENERATOR] = "systemd-search-user-environment-generator", + + [SD_PATH_SYSTEM_CREDENTIAL_STORE] = "system-credential-store", + [SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE] = "system-search-credential-store", + [SD_PATH_SYSTEM_CREDENTIAL_STORE_ENCRYPTED] = "system-credential-store-encrypted", + [SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE_ENCRYPTED] = "system-search-credential-store-encrypted", + [SD_PATH_USER_CREDENTIAL_STORE] = "user-credential-store", + [SD_PATH_USER_SEARCH_CREDENTIAL_STORE] = "user-search-credential-store", + [SD_PATH_USER_CREDENTIAL_STORE_ENCRYPTED] = "user-credential-store-encrypted", + [SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED] = "user-search-credential-store-encrypted", + }; static int order_cmp(const size_t *a, const size_t *b) { diff --git a/src/systemd/sd-path.h b/src/systemd/sd-path.h index 820116a6f8b..bd3a60150cd 100644 --- a/src/systemd/sd-path.h +++ b/src/systemd/sd-path.h @@ -120,6 +120,16 @@ enum { SD_PATH_USER_STATE_PRIVATE, + /* credential store */ + SD_PATH_SYSTEM_CREDENTIAL_STORE, + SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE, + SD_PATH_SYSTEM_CREDENTIAL_STORE_ENCRYPTED, + SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE_ENCRYPTED, + SD_PATH_USER_CREDENTIAL_STORE, + SD_PATH_USER_SEARCH_CREDENTIAL_STORE, + SD_PATH_USER_CREDENTIAL_STORE_ENCRYPTED, + SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED, + _SD_PATH_MAX }; From 8506a9955cb4e6036a38d8634af683d8a4e47220 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Dec 2024 13:35:39 +0100 Subject: [PATCH 07/11] execute: introduce a user-scoped credstore Fixes: #33887 --- src/core/exec-credential.c | 56 ++++++++++++++++++---------- src/libsystemd/sd-path/path-lookup.h | 16 ++++++++ 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/src/core/exec-credential.c b/src/core/exec-credential.c index 784cea208d6..56fc86ef8d3 100644 --- a/src/core/exec-credential.c +++ b/src/core/exec-credential.c @@ -384,30 +384,46 @@ typedef enum CredentialSearchPath { _CREDENTIAL_SEARCH_PATH_INVALID = -EINVAL, } CredentialSearchPath; -static char** credential_search_path(const ExecParameters *params, CredentialSearchPath path) { +static int credential_search_path(const ExecParameters *params, CredentialSearchPath path, char ***ret) { _cleanup_strv_free_ char **l = NULL; + int r; assert(params); assert(path >= 0 && path < _CREDENTIAL_SEARCH_PATH_MAX); + assert(ret); /* Assemble a search path to find credentials in. For non-encrypted credentials, We'll look in * /etc/credstore/ (and similar directories in /usr/lib/ + /run/). If we're looking for encrypted * credentials, we'll look in /etc/credstore.encrypted/ (and similar dirs). */ if (IN_SET(path, CREDENTIAL_SEARCH_PATH_ENCRYPTED, CREDENTIAL_SEARCH_PATH_ALL)) { - if (strv_extend(&l, params->received_encrypted_credentials_directory) < 0) - return NULL; + r = strv_extend(&l, params->received_encrypted_credentials_directory); + if (r < 0) + return r; - if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore.encrypted"), /* filter_duplicates= */ true) < 0) - return NULL; + _cleanup_strv_free_ char **add = NULL; + r = credential_store_path_encrypted(params->runtime_scope, &add); + if (r < 0) + return r; + + r = strv_extend_strv_consume(&l, TAKE_PTR(add), /* filter_duplicates= */ false); + if (r < 0) + return r; } if (IN_SET(path, CREDENTIAL_SEARCH_PATH_TRUSTED, CREDENTIAL_SEARCH_PATH_ALL)) { - if (strv_extend(&l, params->received_credentials_directory) < 0) - return NULL; + r = strv_extend(&l, params->received_credentials_directory); + if (r < 0) + return r; - if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore"), /* filter_duplicates= */ true) < 0) - return NULL; + _cleanup_strv_free_ char **add = NULL; + r = credential_store_path(params->runtime_scope, &add); + if (r < 0) + return r; + + r = strv_extend_strv_consume(&l, TAKE_PTR(add), /* filter_duplicates= */ false); + if (r < 0) + return r; } if (DEBUG_LOGGING) { @@ -415,7 +431,8 @@ static char** credential_search_path(const ExecParameters *params, CredentialSea log_debug("Credential search path is: %s", strempty(t)); } - return TAKE_PTR(l); + *ret = TAKE_PTR(l); + return 0; } struct load_cred_args { @@ -612,9 +629,9 @@ static int load_credential( * 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(args->params, CREDENTIAL_SEARCH_PATH_ALL); - if (!search_path) - return -ENOMEM; + r = credential_search_path(args->params, CREDENTIAL_SEARCH_PATH_ALL, &search_path); + if (r < 0) + return r; missing_ok = true; } else @@ -798,9 +815,9 @@ static int acquire_credentials( ORDERED_SET_FOREACH(ic, context->import_credentials) { _cleanup_free_ char **search_path = NULL; - search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_TRUSTED); - if (!search_path) - return -ENOMEM; + r = credential_search_path(params, CREDENTIAL_SEARCH_PATH_TRUSTED, &search_path); + if (r < 0) + return r; args.encrypted = false; @@ -812,9 +829,10 @@ static int acquire_credentials( return r; search_path = strv_free(search_path); - search_path = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ENCRYPTED); - if (!search_path) - return -ENOMEM; + + r = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ENCRYPTED, &search_path); + if (r < 0) + return r; args.encrypted = true; diff --git a/src/libsystemd/sd-path/path-lookup.h b/src/libsystemd/sd-path/path-lookup.h index 819c4cdb15d..1289e7ac6fc 100644 --- a/src/libsystemd/sd-path/path-lookup.h +++ b/src/libsystemd/sd-path/path-lookup.h @@ -84,3 +84,19 @@ static inline char** generator_binary_paths(RuntimeScope runtime_scope) { static inline char** env_generator_binary_paths(RuntimeScope runtime_scope) { return generator_binary_paths_internal(runtime_scope, true); } + +static inline int credential_store_path(RuntimeScope runtime_scope, char ***ret) { + return sd_path_lookup_strv( + runtime_scope == RUNTIME_SCOPE_SYSTEM ? + SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE : SD_PATH_USER_SEARCH_CREDENTIAL_STORE, + /* suffix= */ NULL, + ret); +} + +static inline int credential_store_path_encrypted(RuntimeScope runtime_scope, char ***ret) { + return sd_path_lookup_strv( + runtime_scope == RUNTIME_SCOPE_SYSTEM ? + SD_PATH_SYSTEM_SEARCH_CREDENTIAL_STORE_ENCRYPTED : SD_PATH_USER_SEARCH_CREDENTIAL_STORE_ENCRYPTED, + /* suffix= */ NULL, + ret); +} From 1af989e8de71a613ae08bd8f095de5308478fd13 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Dec 2024 14:56:18 +0100 Subject: [PATCH 08/11] pid1: add support for decrypting per-user credentials When I added support for unprivileged credentials I apparently never hooked them up to service management correctly. Let's fix that. Fixes: #33796 #33318 --- src/core/exec-credential.c | 41 +++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/src/core/exec-credential.c b/src/core/exec-credential.c index 56fc86ef8d3..58d722ab857 100644 --- a/src/core/exec-credential.c +++ b/src/core/exec-credential.c @@ -463,15 +463,38 @@ static int maybe_decrypt_and_write_credential( assert(data || size == 0); if (args->encrypted) { - r = decrypt_credential_and_warn( - id, - now(CLOCK_REALTIME), - /* tpm2_device= */ NULL, - /* tpm2_signature_path= */ NULL, - getuid(), - &IOVEC_MAKE(data, size), - CREDENTIAL_ANY_SCOPE, - &plaintext); + switch (args->params->runtime_scope) { + + case RUNTIME_SCOPE_SYSTEM: + /* In system mode talk directly to the TPM */ + r = decrypt_credential_and_warn( + id, + now(CLOCK_REALTIME), + /* tpm2_device= */ NULL, + /* tpm2_signature_path= */ NULL, + getuid(), + &IOVEC_MAKE(data, size), + CREDENTIAL_ANY_SCOPE, + &plaintext); + break; + + case RUNTIME_SCOPE_USER: + /* In per user mode we'll not have access to the machine secret, nor to the TPM (most + * likely), hence go via the IPC service instead. Do this if we are run in root's + * per-user invocation too, to minimize differences and because isolating this logic + * into a separate process is generally a good thing anyway. */ + r = ipc_decrypt_credential( + id, + now(CLOCK_REALTIME), + getuid(), + &IOVEC_MAKE(data, size), + /* flags= */ 0, /* only allow user creds in user scope */ + &plaintext); + break; + + default: + assert_not_reached(); + } if (r < 0) return r; From 026dfd60d477237f0e69e30ba0900e95b139436d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Dec 2024 20:50:19 +0100 Subject: [PATCH 09/11] test: add integration test that makes sure unpriv creds work correctly This checks both the per-user credstore directory logic, and that unprivileged, encrypted credentials work. --- src/test/test-execute.c | 4 ++++ test/units/TEST-54-CREDS.sh | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/test/test-execute.c b/src/test/test-execute.c index de575ec1e6d..cd1bca1b31e 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -1398,6 +1398,10 @@ static void run_tests(RuntimeScope scope, char **patterns) { ASSERT_NOT_NULL(unit_paths = strjoin(PRIVATE_UNIT_DIR, ":", user_runtime_unit_dir)); ASSERT_OK(setenv_unit_path(unit_paths)); + /* Write credential for test-execute-load-credential to the fake runtime dir, too */ + _cleanup_free_ char *j = ASSERT_PTR(path_join(runtime_dir, "credstore/test-execute.load-credential")); + ASSERT_OK(write_string_file(j, "foo", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755)); + r = manager_new(scope, MANAGER_TEST_RUN_BASIC, &m); if (manager_errno_skip_test(r)) return (void) log_tests_skipped_errno(r, "manager_new"); diff --git a/test/units/TEST-54-CREDS.sh b/test/units/TEST-54-CREDS.sh index 82dd37aa4d6..dae8d6a2429 100755 --- a/test/units/TEST-54-CREDS.sh +++ b/test/units/TEST-54-CREDS.sh @@ -490,7 +490,7 @@ cmp /tmp/vlcredsdata /tmp/vlcredsdata2 rm /tmp/vlcredsdata /tmp/vlcredsdata2 clean_usertest() { - rm -f /tmp/usertest.data /tmp/usertest.data + rm -f /tmp/usertest.data /tmp/usertest.data /tmp/brummbaer.data } trap clean_usertest EXIT @@ -520,6 +520,12 @@ XDG_RUNTIME_DIR=/run/user/0 systemd-run --pipe --user --unit=waldi.service -p Lo # Test mount unit with credential test_mount_with_credential +# Fully unpriv operation +dd if=/dev/urandom of=/tmp/brummbaer.data bs=4096 count=1 +run0 -u testuser --pipe mkdir -p /home/testuser/.config/credstore.encrypted +run0 -u testuser --pipe systemd-creds encrypt --user --name=brummbaer - /home/testuser/.config/credstore.encrypted/brummbaer < /tmp/brummbaer.data +run0 -u testuser --pipe systemd-run --user --pipe -p ImportCredential=brummbaer systemd-creds cat brummbaer | cmp /tmp/brummbaer.data + systemd-analyze log-level info touch /testok From 4103bf9f2fe8744fe53d9929d86004f5c20750e3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Dec 2024 21:34:06 +0100 Subject: [PATCH 10/11] man: document the new per-use credstore paths (And some other minor tweaks) --- man/systemd.exec.xml | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 467781f06b1..f4917f59c17 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -3468,37 +3468,43 @@ StandardInputData=V2XigLJyZSBubyBzdHJhbmdlcnMgdG8gbG92ZQpZb3Uga25vdyB0aGUgcnVsZX LoadCredentialEncrypted=ID:PATH Pass a credential to the unit. Credentials are limited-size binary or textual objects - that may be passed to unit processes. They are primarily used for passing cryptographic keys (both - public and private) or certificates, user account information or identity information from host to - services. The data is accessible from the unit's processes via the file system, at a read-only - location that (if possible and permitted) is backed by non-swappable memory. The data is only - accessible to the user associated with the unit, via the - User=/DynamicUser= settings (as well as the superuser). When - available, the location of credentials is exported as the $CREDENTIALS_DIRECTORY - environment variable to the unit's processes. + that may be passed to unit processes. They are primarily intended for passing cryptographic keys + (both public and private) or certificates, user account information or identity information from host + to services, but can be freely used to pass any kind of limited-size information to a service. The + data is accessible from the unit's processes via the file system, at a read-only location that (if + possible and permitted) is backed by non-swappable memory. The data is only accessible to the user + associated with the unit, via the User=/DynamicUser= settings + (as well as the superuser). When available, the location of credentials is exported as the + $CREDENTIALS_DIRECTORY environment variable to the unit's processes. The LoadCredential= setting takes a textual ID to use as name for a credential plus a file system path, separated by a colon. The ID must be a short ASCII string suitable as filename in the filesystem, and may be chosen freely by the user. If the specified path is absolute it is opened as regular file and the credential data is read from it. If the absolute path refers to an AF_UNIX stream socket in the file system a connection is made - to it (only once at unit start-up) and the credential data read from the connection, providing an + to it (once at process invocation) and the credential data read from the connection, providing an easy IPC integration point for dynamically transferring credentials from other services. If the specified path is not absolute and itself qualifies as valid credential identifier it is attempted to find a credential that the service manager itself received under the specified name — which may be used to propagate credentials from an invoking environment (e.g. a container manager - that invoked the service manager) into a service. If no matching system credential is found, the - directories /etc/credstore/, /run/credstore/ and - /usr/lib/credstore/ are searched for files under the credential's name — which - hence are recommended locations for credential data on disk. If + that invoked the service manager) into a service. If no matching passed credential is found, the + system service manager will search the directories /etc/credstore/, + /run/credstore/ and /usr/lib/credstore/ for files under the + credential's name — which hence are recommended locations for credential data on disk. If LoadCredentialEncrypted= is used /run/credstore.encrypted/, /etc/credstore.encrypted/, and - /usr/lib/credstore.encrypted/ are searched as well. + /usr/lib/credstore.encrypted/ are searched as well. The per-user service manager + will search $XDG_CONFIG_HOME/credstore/, + $XDG_RUNTIME_DIR/credstore/, $HOME/.local/lib/credstore/ + (and the counterparts ending with …/credstore.encrypted/) instead. The + systemd-path1 tool + may be used to query the precise credential store search path. If the file system path is omitted it is chosen identical to the credential name, i.e. this is - a terse way to declare credentials to inherit from the service manager into a service. This option - may be used multiple times, each time defining an additional credential to pass to the unit. + a terse way to declare credentials to inherit from the service manager or credstore directories into + a service. This option may be used multiple times, each time defining an additional credential to + pass to the unit. Note that if the path is not specified or a valid credential identifier is given, i.e. in the above two cases, a missing credential is not considered fatal. From 8cbcdc78db9f9f39631bd211d4e2cc6578ff31e0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 10 Dec 2024 20:49:31 +0100 Subject: [PATCH 11/11] update TODO --- TODO | 4 ---- 1 file changed, 4 deletions(-) diff --git a/TODO b/TODO index 3fad822bc66..ef8b79024d1 100644 --- a/TODO +++ b/TODO @@ -446,10 +446,6 @@ Features: * credentials: add a flag to the scoped credentials that if set require PK reauthentication when unlocking a secret. -* teach systemd --user to properly load credentials off disk, with - /etc/credstore equivalent and similar. Make sure that $CREDENTIALS_DIRECTORY= - actually works too when run with user privs. - * extend the smbios11 logic for passing credentials so that instead of passing the credential data literally it can also just reference an AF_VSOCK CID/port to read them from. This way the data doesn't remain in the SMBIOS blob during