diff --git a/0700-resolved-replace-assert-with-error-return-in-DNSSEC-.patch b/0700-resolved-replace-assert-with-error-return-in-DNSSEC-.patch new file mode 100644 index 0000000..a0d7c8d --- /dev/null +++ b/0700-resolved-replace-assert-with-error-return-in-DNSSEC-.patch @@ -0,0 +1,53 @@ +From f114aaba224846122a0329bc6f12ea163e9e56df Mon Sep 17 00:00:00 2001 +From: Milan Kyselica +Date: Thu, 9 Apr 2026 19:43:14 +0200 +Subject: [PATCH] resolved: replace assert() with error return in DNSSEC verify + functions + +dnssec_rsa_verify_raw() asserts that RSA_size(key) matches the RRSIG +signature size, and dnssec_ecdsa_verify_raw() asserts that +EC_KEY_check_key() succeeds. Both conditions depend on parsed DNS +record content. Replace with proper error returns. + +The actual crypto verify calls (EVP_PKEY_verify / ECDSA_do_verify) +handle mismatches fine on their own, so the asserts were also redundant. + +While at it, fix the misleading "EC_POINT_bn2point failed" log message +that actually refers to an EC_KEY_set_public_key() failure. + +Fixes: https://github.com/systemd/systemd/issues/41569 +(cherry picked from commit dd80e5a348bdb8185e040f66ede00fd4ffdee777) + +Resolves: RHEL-158349 +--- + src/resolve/resolved-dns-dnssec.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c +index 6d32b2d798..41f3f1abda 100644 +--- a/src/resolve/resolved-dns-dnssec.c ++++ b/src/resolve/resolved-dns-dnssec.c +@@ -126,7 +126,8 @@ static int dnssec_rsa_verify_raw( + return -EIO; + e = m = NULL; + +- assert((size_t) RSA_size(rpubkey) == signature_size); ++ if ((size_t) RSA_size(rpubkey) != signature_size) ++ return -EINVAL; + + epubkey = EVP_PKEY_new(); + if (!epubkey) +@@ -338,9 +339,11 @@ static int dnssec_ecdsa_verify_raw( + + if (EC_KEY_set_public_key(eckey, p) <= 0) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), +- "EC_POINT_bn2point failed: 0x%lx", ERR_get_error()); ++ "EC_KEY_set_public_key failed: 0x%lx", ERR_get_error()); + +- assert(EC_KEY_check_key(eckey) == 1); ++ if (EC_KEY_check_key(eckey) != 1) ++ return log_debug_errno(SYNTHETIC_ERRNO(EIO), ++ "EC_KEY_check_key failed: 0x%lx", ERR_get_error()); + + r = BN_bin2bn(signature_r, signature_r_size, NULL); + if (!r) diff --git a/0701-pid1-normalize-oom-error-handling-a-bit.patch b/0701-pid1-normalize-oom-error-handling-a-bit.patch new file mode 100644 index 0000000..a7df4d3 --- /dev/null +++ b/0701-pid1-normalize-oom-error-handling-a-bit.patch @@ -0,0 +1,70 @@ +From 63e6c6b5cb74cd8bb407ccb3f153a7dd92ad3274 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 10 Dec 2024 11:10:30 +0100 +Subject: [PATCH] pid1: normalize oom error handling a bit + +(cherry picked from commit cf7d0a2d2e00846096908082ffc3fc1953e015e6) + +Related: RHEL-169656 +--- + 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 bce0ee7968..784cea208d 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); + diff --git a/0702-sd-path-don-t-chop-off-trailing-slash-in-sd_path-api.patch b/0702-sd-path-don-t-chop-off-trailing-slash-in-sd_path-api.patch new file mode 100644 index 0000000..c324541 --- /dev/null +++ b/0702-sd-path-don-t-chop-off-trailing-slash-in-sd_path-api.patch @@ -0,0 +1,52 @@ +From 0846e7a060e34230e7be7d9224681de3bdc929ac Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 10 Dec 2024 13:37:56 +0100 +Subject: [PATCH] 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. + +(cherry picked from commit 616586b91003adf08c56b5f63e60b6f8a4dbe893) + +Related: RHEL-169656 +--- + 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 9f495f3051..a2f03f52e9 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); diff --git a/0703-systemd-path-order-all-listed-paths-by-their-ID-alph.patch b/0703-systemd-path-order-all-listed-paths-by-their-ID-alph.patch new file mode 100644 index 0000000..46dd1a3 --- /dev/null +++ b/0703-systemd-path-order-all-listed-paths-by-their-ID-alph.patch @@ -0,0 +1,79 @@ +From b4b11980b3a96d7b380985e60b102c035f4cf80f Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 10 Dec 2024 14:01:13 +0100 +Subject: [PATCH] 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. + +(cherry picked from commit 81082f2dc2d80efdf6c7e9a84df90dcd13b3d0ae) + +Related: RHEL-169656 +--- + 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 3ab09344b4..604e4c170b 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++) { ++ size_t order[ELEMENTSOF(path_table)]; ++ ++ 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; +- int q; +- +- 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); ++ 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; diff --git a/0704-systemd-path-guarantee-that-tool-exit-status-is-zero.patch b/0704-systemd-path-guarantee-that-tool-exit-status-is-zero.patch new file mode 100644 index 0000000..1f3f65f --- /dev/null +++ b/0704-systemd-path-guarantee-that-tool-exit-status-is-zero.patch @@ -0,0 +1,33 @@ +From d5b7a6386a5a64b6e67e1d42dacf1f5bb5ddb5dd Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 10 Dec 2024 14:05:04 +0100 +Subject: [PATCH] systemd-path: guarantee that tool exit status is zero on + success + +Let's not inherit the error code from an earlier function invocation. + +(cherry picked from commit 060e2512cdb1bf985965d991e0bb746ff21362ee) + +Related: RHEL-169656 +--- + 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 604e4c170b..8abfc6c7f2 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; diff --git a/0705-systemd-path-add-the-usual-ANSI-sequences-to-help-te.patch b/0705-systemd-path-add-the-usual-ANSI-sequences-to-help-te.patch new file mode 100644 index 0000000..fe90d5e --- /dev/null +++ b/0705-systemd-path-add-the-usual-ANSI-sequences-to-help-te.patch @@ -0,0 +1,35 @@ +From 061d64e4ab4912c3f704c678c86e8e4ec223ca4a Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 10 Dec 2024 21:38:37 +0100 +Subject: [PATCH] systemd-path: add the usual ANSI sequences to --help text + +(cherry picked from commit b226b7fb6d5269973f8a1927735b8bf16c469f6e) + +Related: RHEL-169656 +--- + 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 8abfc6c7f2..ad65437c8f 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; diff --git a/0706-sd-path-expose-credential-store-in-sd-path.patch b/0706-sd-path-expose-credential-store-in-sd-path.patch new file mode 100644 index 0000000..f532580 --- /dev/null +++ b/0706-sd-path-expose-credential-store-in-sd-path.patch @@ -0,0 +1,161 @@ +From 8a9de694d22825ffd201632afc2d139c028db9ab Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 10 Dec 2024 14:34:41 +0100 +Subject: [PATCH] sd-path: expose credential store in sd-path + +(cherry picked from commit d2cd18932422563c12a6f6e7f3019750784705ab) + +Related: RHEL-169656 +--- + 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 a2f03f52e9..d536352038 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 ad65437c8f..6ca2bd8c38 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 820116a6f8..bd3a60150c 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 + }; + diff --git a/0707-execute-introduce-a-user-scoped-credstore.patch b/0707-execute-introduce-a-user-scoped-credstore.patch new file mode 100644 index 0000000..ae68aae --- /dev/null +++ b/0707-execute-introduce-a-user-scoped-credstore.patch @@ -0,0 +1,148 @@ +From 6ec0340db60ffa738c62806be215cc8e74e944cc Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 10 Dec 2024 13:35:39 +0100 +Subject: [PATCH] execute: introduce a user-scoped credstore + +Fixes: #33887 +(cherry picked from commit 8506a9955cb4e6036a38d8634af683d8a4e47220) + +Related: RHEL-169656 +--- + 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 784cea208d..56fc86ef8d 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; ++ ++ _cleanup_strv_free_ char **add = NULL; ++ r = credential_store_path_encrypted(params->runtime_scope, &add); ++ if (r < 0) ++ return r; + +- if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore.encrypted"), /* filter_duplicates= */ true) < 0) +- return NULL; ++ 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; ++ ++ _cleanup_strv_free_ char **add = NULL; ++ r = credential_store_path(params->runtime_scope, &add); ++ if (r < 0) ++ return r; + +- if (strv_extend_strv(&l, CONF_PATHS_STRV("credstore"), /* filter_duplicates= */ true) < 0) +- return NULL; ++ 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 819c4cdb15..1289e7ac6f 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); ++} diff --git a/0708-pid1-add-support-for-decrypting-per-user-credentials.patch b/0708-pid1-add-support-for-decrypting-per-user-credentials.patch new file mode 100644 index 0000000..63f7f30 --- /dev/null +++ b/0708-pid1-add-support-for-decrypting-per-user-credentials.patch @@ -0,0 +1,68 @@ +From 53b5f30038dbe20197fe77cfd76ed096951c7d81 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 10 Dec 2024 14:56:18 +0100 +Subject: [PATCH] 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 +(cherry picked from commit 1af989e8de71a613ae08bd8f095de5308478fd13) + +Resolves: RHEL-169656 +--- + 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 56fc86ef8d..58d722ab85 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; + diff --git a/0709-test-add-integration-test-that-makes-sure-unpriv-cre.patch b/0709-test-add-integration-test-that-makes-sure-unpriv-cre.patch new file mode 100644 index 0000000..e5665d4 --- /dev/null +++ b/0709-test-add-integration-test-that-makes-sure-unpriv-cre.patch @@ -0,0 +1,59 @@ +From 0928982a40942ccd17f5527886d1ceaf0fe16709 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 10 Dec 2024 20:50:19 +0100 +Subject: [PATCH] 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. + +(cherry picked from commit 026dfd60d477237f0e69e30ba0900e95b139436d) + +Related: RHEL-169656 +--- + src/test/test-execute.c | 4 ++++ + test/units/TEST-54-CREDS.sh | 9 ++++++++- + 2 files changed, 12 insertions(+), 1 deletion(-) + +diff --git a/src/test/test-execute.c b/src/test/test-execute.c +index de575ec1e6..cd1bca1b31 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 3a4fa654e9..bca68432d1 100755 +--- a/test/units/TEST-54-CREDS.sh ++++ b/test/units/TEST-54-CREDS.sh +@@ -447,7 +447,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 +@@ -474,6 +474,13 @@ systemd-creds encrypt --user /tmp/usertest.data /tmp/usertest.creds --name=mytes + systemctl start user@0.service + XDG_RUNTIME_DIR=/run/user/0 systemd-run --pipe --user --unit=waldi.service -p LoadCredentialEncrypted=mytest:/tmp/usertest.creds cat /run/user/0/credentials/waldi.service/mytest | cmp /tmp/usertest.data + ++ ++# 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 diff --git a/0710-man-document-the-new-per-use-credstore-paths.patch b/0710-man-document-the-new-per-use-credstore-paths.patch new file mode 100644 index 0000000..304bfec --- /dev/null +++ b/0710-man-document-the-new-per-use-credstore-paths.patch @@ -0,0 +1,78 @@ +From 97f26174ca86237dfae9f23cc2f66639fa6d5f7c Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 10 Dec 2024 21:34:06 +0100 +Subject: [PATCH] man: document the new per-use credstore paths + +(And some other minor tweaks) + +(cherry picked from commit 4103bf9f2fe8744fe53d9929d86004f5c20750e3) + +Related: RHEL-169656 +--- + 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 14075cb4e7..6f4b9d1013 100644 +--- a/man/systemd.exec.xml ++++ b/man/systemd.exec.xml +@@ -3449,37 +3449,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. diff --git a/0711-update-TODO.patch b/0711-update-TODO.patch new file mode 100644 index 0000000..3b1afa0 --- /dev/null +++ b/0711-update-TODO.patch @@ -0,0 +1,27 @@ +From 446eee5eca6fb42aa9ed2d3b932073edc25de002 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 10 Dec 2024 20:49:31 +0100 +Subject: [PATCH] update TODO + +(cherry picked from commit 8cbcdc78db9f9f39631bd211d4e2cc6578ff31e0) + +Related: RHEL-169656 +--- + TODO | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/TODO b/TODO +index 4ed9f5a834..12b70cf4cd 100644 +--- a/TODO ++++ b/TODO +@@ -439,10 +439,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 diff --git a/0712-cryptenroll-repart-creds-no-longer-default-to-bindin.patch b/0712-cryptenroll-repart-creds-no-longer-default-to-bindin.patch new file mode 100644 index 0000000..f0efe6c --- /dev/null +++ b/0712-cryptenroll-repart-creds-no-longer-default-to-bindin.patch @@ -0,0 +1,248 @@ +From e3d696ad24a3fbab6b6548635dbb9519061abbb5 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 28 Jan 2025 09:48:48 +0100 +Subject: [PATCH] cryptenroll/repart/creds: no longer default to binding + against literal PCR 7 + +PCR 7 covers the SecureBoot policy, in particular "dbx", i.e. the +denylist of bad actors. That list is pretty much as frequently updated +as firmware these days (as fwupd took over automatic updating). This +means literal PCR 7 policies are problematic: they likely break soon, +and are as brittle as any other literal PCR policies. + +hence, pick safer defaults, i.e. exclude PCR 7 from the default mask. +This means the mask is now empty. + +Generally, people should really switch to signed PCR policies covering +PCR 11, in combination with systemd-pcrlock for the other PCRs. + +(cherry picked from commit 4b840414be3b2d6520599d86d2b718a37574aabf) + +Related: RHEL-115813 +--- + TODO | 13 ------------- + man/systemd-creds.xml | 7 ++++--- + man/systemd-cryptenroll.xml | 4 ++-- + src/creds/creds.c | 2 +- + src/cryptenroll/cryptenroll.c | 14 +------------- + src/cryptsetup/cryptsetup.c | 2 +- + src/repart/repart.c | 14 +------------- + src/shared/tpm2-util.h | 9 ++++++--- + test/units/TEST-70-TPM2.cryptsetup.sh | 8 ++++---- + 9 files changed, 20 insertions(+), 53 deletions(-) + +diff --git a/TODO b/TODO +index 12b70cf4cd..e2c00a3a53 100644 +--- a/TODO ++++ b/TODO +@@ -300,8 +300,6 @@ Features: + * creds: add a new cred format that reused the JSON structures we use in the + LUKS header, so that we get the various newer policies for free. + +-* drop PCR 7 from default PCR mask in credentials and LUKS2 enrollments +- + * systemd-analyze: port "pcrs" verb to talk directly to TPM device, instead of + using sysfs interface (well, or maybe not, as that would require privileges?) + +@@ -939,17 +937,6 @@ Features: + - If run on every boot, should it use the sysupdate config from the host on + subsequent boots? + +-* revisit default PCR bindings in cryptenroll and systemd-creds. Currently they +- use PCR 7 which should contain secureboot state db/dbx. Which sounded like a +- safe bet, given that it should change only on policy changes, and not +- software updates. But that's wrong. Recent fwupd (rightfully) contains code +- for updating the dbx denylist. This means even without any active policy +- change PCR 7 might change. Hence, better idea might be in systemd-creds to +- default to PCR 15 at least if sd-stub is used (i.e. bind to system identity), +- and in cryptsetup simply the empty list? Also, PCR 14 almost certainly should +- be included as much as PCR 7 (as it contains shim's policy, which is +- certainly as relevant as PCR 7 on many systems) +- + * To mimic the new tpm2-measure-pcr= crypttab option add the same to veritytab + (measuring the root hash) and integritytab (measuring the HMAC key if one is + used) +diff --git a/man/systemd-creds.xml b/man/systemd-creds.xml +index 8f972eeffb..dbc63e1bb4 100644 +--- a/man/systemd-creds.xml ++++ b/man/systemd-creds.xml +@@ -371,9 +371,10 @@ + + + Configures the TPM2 PCRs (Platform Configuration Registers) to bind the encryption +- key to. Takes a + separated list of numeric PCR indexes in the range 0…23. If not +- used, defaults to PCR 7 only. If an empty string is specified, binds the encryption key to no PCRs at +- all. For details about the PCRs available, see the documentation of the switch of the same name for ++ key to. Takes a + separated list of numeric PCR indexes in the range 0…23. If an ++ empty string is specified, binds the encryption key to no PCRs at all (this is also the default if ++ this option is not used). For details about the PCRs available, see the documentation of the switch ++ of the same name for + systemd-cryptenroll1. + + +diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml +index fb36f455ba..20b12d79ce 100644 +--- a/man/systemd-cryptenroll.xml ++++ b/man/systemd-cryptenroll.xml +@@ -578,8 +578,8 @@ + entry starts with a name or numeric index in the range 0…23, optionally followed by + : and a hash algorithm name (specifying the PCR bank), optionally followed by + = and a hash digest value. Multiple PCR entries are separated by +- +. If not specified, the default is to use PCR 7 only. If an empty string is +- specified, binds the enrollment to no PCRs at all. See the table above for a list of available ++ +. If an empty string is specified, binds the enrollment to no PCRs at all (this ++ is also the default, if this option is not used). See the table above for a list of available + PCRs. + + Example: +diff --git a/src/creds/creds.c b/src/creds/creds.c +index 7635aee37c..a1257d8c5a 100644 +--- a/src/creds/creds.c ++++ b/src/creds/creds.c +@@ -1030,7 +1030,7 @@ static int parse_argv(int argc, char *argv[]) { + } + + if (arg_tpm2_pcr_mask == UINT32_MAX) +- arg_tpm2_pcr_mask = TPM2_PCR_MASK_DEFAULT; ++ arg_tpm2_pcr_mask = 0; + if (arg_tpm2_public_key_pcr_mask == UINT32_MAX) + arg_tpm2_public_key_pcr_mask = UINT32_C(1) << TPM2_PCR_KERNEL_BOOT; + +diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c +index 3fb58c2874..c40c0d402f 100644 +--- a/src/cryptenroll/cryptenroll.c ++++ b/src/cryptenroll/cryptenroll.c +@@ -308,7 +308,7 @@ static int parse_argv(int argc, char *argv[]) { + {} + }; + +- bool auto_hash_pcr_values = true, auto_public_key_pcr_mask = true, auto_pcrlock = true; ++ bool auto_public_key_pcr_mask = true, auto_pcrlock = true; + int c, r; + + assert(argc >= 0); +@@ -530,7 +530,6 @@ static int parse_argv(int argc, char *argv[]) { + break; + + case ARG_TPM2_PCRS: +- auto_hash_pcr_values = false; + r = tpm2_parse_pcr_argument_append(optarg, &arg_tpm2_hash_pcr_values, &arg_tpm2_n_hash_pcr_values); + if (r < 0) + return r; +@@ -698,17 +697,6 @@ static int parse_argv(int argc, char *argv[]) { + assert(arg_tpm2_public_key_pcr_mask == 0); + arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM2_PCR_KERNEL_BOOT); + } +- +- if (auto_hash_pcr_values && !arg_tpm2_pcrlock) { /* Only lock to PCR 7 by default if no pcrlock policy is around (which is a better replacement) */ +- assert(arg_tpm2_n_hash_pcr_values == 0); +- +- if (!GREEDY_REALLOC_APPEND( +- arg_tpm2_hash_pcr_values, +- arg_tpm2_n_hash_pcr_values, +- &TPM2_PCR_VALUE_MAKE(TPM2_PCR_INDEX_DEFAULT, /* hash= */ 0, /* value= */ {}), +- 1)) +- return log_oom(); +- } + } + + return 1; +diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c +index adeffae003..ba394c1650 100644 +--- a/src/cryptsetup/cryptsetup.c ++++ b/src/cryptsetup/cryptsetup.c +@@ -1855,7 +1855,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( + r = acquire_tpm2_key( + name, + arg_tpm2_device, +- arg_tpm2_pcr_mask == UINT32_MAX ? TPM2_PCR_MASK_DEFAULT : arg_tpm2_pcr_mask, ++ arg_tpm2_pcr_mask == UINT32_MAX ? TPM2_PCR_MASK_DEFAULT_LEGACY : arg_tpm2_pcr_mask, + UINT16_MAX, + /* pubkey= */ NULL, + /* pubkey_pcr_mask= */ 0, +diff --git a/src/repart/repart.c b/src/repart/repart.c +index b2a2bda2a2..4994886fa4 100644 +--- a/src/repart/repart.c ++++ b/src/repart/repart.c +@@ -7959,7 +7959,7 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY * + _cleanup_(X509_freep) X509 *certificate = NULL; + _cleanup_(openssl_ask_password_ui_freep) OpenSSLAskPasswordUI *ui = NULL; + _cleanup_(EVP_PKEY_freep) EVP_PKEY *private_key = NULL; +- bool auto_hash_pcr_values = true, auto_public_key_pcr_mask = true, auto_pcrlock = true; ++ bool auto_public_key_pcr_mask = true, auto_pcrlock = true; + int c, r; + + assert(argc >= 0); +@@ -8188,7 +8188,6 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY * + break; + + case ARG_TPM2_PCRS: +- auto_hash_pcr_values = false; + r = tpm2_parse_pcr_argument_append(optarg, &arg_tpm2_hash_pcr_values, &arg_tpm2_n_hash_pcr_values); + if (r < 0) + return r; +@@ -8464,17 +8463,6 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY * + arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM2_PCR_KERNEL_BOOT); + } + +- if (auto_hash_pcr_values && !arg_tpm2_pcrlock) { /* Only lock to PCR 7 if no pcr policy is specified. */ +- assert(arg_tpm2_n_hash_pcr_values == 0); +- +- if (!GREEDY_REALLOC_APPEND( +- arg_tpm2_hash_pcr_values, +- arg_tpm2_n_hash_pcr_values, +- &TPM2_PCR_VALUE_MAKE(TPM2_PCR_INDEX_DEFAULT, /* hash= */ 0, /* value= */ {}), +- 1)) +- return log_oom(); +- } +- + if (arg_pretty < 0 && isatty_safe(STDOUT_FILENO)) + arg_pretty = true; + +diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h +index 77cd7dbcaf..a0008d9b59 100644 +--- a/src/shared/tpm2-util.h ++++ b/src/shared/tpm2-util.h +@@ -394,9 +394,12 @@ int tpm2_parse_pcr_json_array(sd_json_variant *v, uint32_t *ret); + int tpm2_make_luks2_json(int keyslot, uint32_t hash_pcr_mask, uint16_t pcr_bank, const struct iovec *pubkey, uint32_t pubkey_pcr_mask, uint16_t primary_alg, const struct iovec blobs[], size_t n_blobs, const struct iovec policy_hash[], size_t n_policy_hash, const struct iovec *salt, const struct iovec *srk, const struct iovec *pcrlock_nv, TPM2Flags flags, sd_json_variant **ret); + int tpm2_parse_luks2_json(sd_json_variant *v, int *ret_keyslot, uint32_t *ret_hash_pcr_mask, uint16_t *ret_pcr_bank, struct iovec *ret_pubkey, uint32_t *ret_pubkey_pcr_mask, uint16_t *ret_primary_alg, struct iovec **ret_blobs, size_t *ret_n_blobs, struct iovec **ret_policy_hash, size_t *ret_n_policy_hash, struct iovec *ret_salt, struct iovec *ret_srk, struct iovec *ret_pcrlock_nv, TPM2Flags *ret_flags); + +-/* Default to PCR 7 only */ +-#define TPM2_PCR_INDEX_DEFAULT UINT32_C(7) +-#define TPM2_PCR_MASK_DEFAULT INDEX_TO_MASK(uint32_t, TPM2_PCR_INDEX_DEFAULT) ++/* Before v258 we used to bind to PCR 7 by default at various places if no explicit PCR mask was set. With ++ * v258 we stopped doing that (since the SecureBoot DB is as much subject to regular updates by tools such as ++ * fwupd as the firmware itself), but when unlocking to maintain compatibility when no mask is specified we ++ * still need to default to PCR 7. */ ++#define TPM2_PCR_INDEX_DEFAULT_LEGACY TPM2_PCR_SECURE_BOOT_POLICY ++#define TPM2_PCR_MASK_DEFAULT_LEGACY INDEX_TO_MASK(uint32_t, TPM2_PCR_INDEX_DEFAULT_LEGACY) + + /* We want the helpers below to work also if TPM2 libs are not available, hence define these four defines if + * they are missing. */ +diff --git a/test/units/TEST-70-TPM2.cryptsetup.sh b/test/units/TEST-70-TPM2.cryptsetup.sh +index b5dd4dfe15..8c1a362fbc 100755 +--- a/test/units/TEST-70-TPM2.cryptsetup.sh ++++ b/test/units/TEST-70-TPM2.cryptsetup.sh +@@ -49,10 +49,10 @@ chmod 0600 /tmp/passphrase + cryptsetup luksFormat -q --pbkdf pbkdf2 --pbkdf-force-iterations 1000 --use-urandom "$IMAGE" /tmp/passphrase + + # Unlocking via keyfile +-systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto "$IMAGE" ++systemd-cryptenroll --unlock-key-file=/tmp/passphrase --tpm2-device=auto --tpm2-pcrs=7 "$IMAGE" + +-# Enroll unlock with default PCR policy +-PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto "$IMAGE" ++# Enroll unlock with SecureBoot (PCR 7) PCR policy ++PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=7 "$IMAGE" + systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 + systemd-cryptsetup detach test-volume + +@@ -62,7 +62,7 @@ tpm2_pcrextend 7:sha256=00000000000000000000000000000000000000000000000000000000 + + # Enroll unlock with PCR+PIN policy + systemd-cryptenroll --wipe-slot=tpm2 "$IMAGE" +-PASSWORD=passphrase NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true "$IMAGE" ++PASSWORD=passphrase NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true --tpm2-pcrs=7 "$IMAGE" + PIN=123456 systemd-cryptsetup attach test-volume "$IMAGE" - tpm2-device=auto,headless=1 + systemd-cryptsetup detach test-volume + diff --git a/0713-cryptenroll-repart-print-a-log-message-if-no-access-.patch b/0713-cryptenroll-repart-print-a-log-message-if-no-access-.patch new file mode 100644 index 0000000..262c600 --- /dev/null +++ b/0713-cryptenroll-repart-print-a-log-message-if-no-access-.patch @@ -0,0 +1,49 @@ +From 00a47b5ca3631bf40bee4b78ff37a256a7224705 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Wed, 29 Jan 2025 15:13:35 +0100 +Subject: [PATCH] cryptenroll,repart: print a log message if no access + restrictions are applied to TPM-based encryption + +(cherry picked from commit c205840fe0c3d0fe0ed47eddd98408842e7c423a) + +Related: RHEL-115813 +--- + src/cryptenroll/cryptenroll.c | 7 +++++++ + src/repart/repart.c | 6 ++++++ + 2 files changed, 13 insertions(+) + +diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c +index c40c0d402f..3b86519a80 100644 +--- a/src/cryptenroll/cryptenroll.c ++++ b/src/cryptenroll/cryptenroll.c +@@ -697,6 +697,13 @@ static int parse_argv(int argc, char *argv[]) { + assert(arg_tpm2_public_key_pcr_mask == 0); + arg_tpm2_public_key_pcr_mask = INDEX_TO_MASK(uint32_t, TPM2_PCR_KERNEL_BOOT); + } ++ ++ if (arg_tpm2_n_hash_pcr_values == 0 && ++ !arg_tpm2_pin && ++ arg_tpm2_public_key_pcr_mask == 0 && ++ !arg_tpm2_pcrlock) ++ log_notice("Notice: enrolling TPM2 with an empty policy, i.e. without any state or access restrictions.\n" ++ "Use --tpm2-public-key=, --tpm2-pcrlock=, --tpm2-with-pin= or --tpm2-pcrs= to enable one or more restrictions."); + } + + return 1; +diff --git a/src/repart/repart.c b/src/repart/repart.c +index 4994886fa4..009f563c99 100644 +--- a/src/repart/repart.c ++++ b/src/repart/repart.c +@@ -4592,6 +4592,12 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta + int keyslot; + TPM2Flags flags = 0; + ++ if (arg_tpm2_n_hash_pcr_values == 0 && ++ arg_tpm2_public_key_pcr_mask == 0 && ++ !arg_tpm2_pcrlock) ++ log_notice("Notice: encrypting future partition %" PRIu64 ", locking against TPM2 with an empty policy, i.e. without any state or access restrictions.\n" ++ "Use --tpm2-public-key=, --tpm2-pcrlock=, or --tpm2-pcrs= to enable one or more restrictions.", p->partno); ++ + if (arg_tpm2_public_key_pcr_mask != 0) { + r = tpm2_load_pcr_public_key(arg_tpm2_public_key, &pubkey.iov_base, &pubkey.iov_len); + if (r < 0) { diff --git a/0714-repart-correct-argument-comment.patch b/0714-repart-correct-argument-comment.patch new file mode 100644 index 0000000..705875d --- /dev/null +++ b/0714-repart-correct-argument-comment.patch @@ -0,0 +1,25 @@ +From 730def2a4ac16d93d5e0e5050965b1a920e462b2 Mon Sep 17 00:00:00 2001 +From: Jelle van der Waa +Date: Tue, 3 Jun 2025 15:32:49 +0200 +Subject: [PATCH] repart: correct argument comment + +(cherry picked from commit 2b58550bd4aa154d43372a280312ca249b82619e) + +Related: RHEL-115813 +--- + src/repart/repart.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/repart/repart.c b/src/repart/repart.c +index 009f563c99..d78dc4f8de 100644 +--- a/src/repart/repart.c ++++ b/src/repart/repart.c +@@ -4685,7 +4685,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta + r = tpm2_calculate_sealing_policy( + arg_tpm2_hash_pcr_values, + arg_tpm2_n_hash_pcr_values, +- /* pubkey= */ NULL, /* Turn this one off for the 2nd shard */ ++ /* public= */ NULL, /* Turn this one off for the 2nd shard */ + /* use_pin= */ false, + &pcrlock_policy, /* But turn this one on */ + policy_hash + 1); diff --git a/0715-repart-use-iovec-structure-for-key-file.patch b/0715-repart-use-iovec-structure-for-key-file.patch new file mode 100644 index 0000000..f1389b9 --- /dev/null +++ b/0715-repart-use-iovec-structure-for-key-file.patch @@ -0,0 +1,85 @@ +From 480f7679259923ffc708273142770bfe39123e26 Mon Sep 17 00:00:00 2001 +From: Emanuele Giuseppe Esposito +Date: Mon, 14 Jul 2025 07:56:49 -0400 +Subject: [PATCH] repart: use iovec structure for --key-file + +Use the iovec structure for --key-file, instead of a char pointer and a size. + +(cherry picked from commit d4397999324c5093380fc1e6c8ce430a58e57145) + +Related: RHEL-115813 +--- + src/repart/repart.c | 30 +++++++++++++++--------------- + 1 file changed, 15 insertions(+), 15 deletions(-) + +diff --git a/src/repart/repart.c b/src/repart/repart.c +index d78dc4f8de..a2825cddae 100644 +--- a/src/repart/repart.c ++++ b/src/repart/repart.c +@@ -148,8 +148,7 @@ static bool arg_size_auto = false; + static sd_json_format_flags_t arg_json_format_flags = SD_JSON_FORMAT_OFF; + static PagerFlags arg_pager_flags = 0; + static bool arg_legend = true; +-static void *arg_key = NULL; +-static size_t arg_key_size = 0; ++static struct iovec arg_key = {}; + static char *arg_private_key = NULL; + static KeySourceType arg_private_key_source_type = OPENSSL_KEY_SOURCE_FILE; + static char *arg_private_key_source = NULL; +@@ -184,7 +183,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_node, freep); + STATIC_DESTRUCTOR_REGISTER(arg_root, freep); + STATIC_DESTRUCTOR_REGISTER(arg_image, freep); + STATIC_DESTRUCTOR_REGISTER(arg_definitions, strv_freep); +-STATIC_DESTRUCTOR_REGISTER(arg_key, erase_and_freep); ++STATIC_DESTRUCTOR_REGISTER(arg_key, iovec_done_erase); + STATIC_DESTRUCTOR_REGISTER(arg_private_key, freep); + STATIC_DESTRUCTOR_REGISTER(arg_private_key_source, freep); + STATIC_DESTRUCTOR_REGISTER(arg_certificate, freep); +@@ -4574,13 +4573,13 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta + CRYPT_ANY_SLOT, + NULL, + VOLUME_KEY_SIZE, +- strempty(arg_key), +- arg_key_size); ++ strempty(arg_key.iov_base), ++ arg_key.iov_len); + if (r < 0) + return log_error_errno(r, "Failed to add LUKS2 key: %m"); + +- passphrase = strempty(arg_key); +- passphrase_size = arg_key_size; ++ passphrase = strempty(arg_key.iov_base); ++ passphrase_size = arg_key.iov_len; + } + + if (IN_SET(p->encrypt, ENCRYPT_TPM2, ENCRYPT_KEY_FILE_TPM2)) { +@@ -8114,20 +8113,21 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY * + break; + + case ARG_KEY_FILE: { +- _cleanup_(erase_and_freep) char *k = NULL; +- size_t n = 0; ++ struct iovec key = {}; + + r = read_full_file_full( +- AT_FDCWD, optarg, UINT64_MAX, SIZE_MAX, ++ AT_FDCWD, optarg, ++ /* offset= */ UINT64_MAX, ++ /* size= */ SIZE_MAX, + READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, +- NULL, +- &k, &n); ++ /* bind_name= */ NULL, ++ (char **) &key.iov_base, ++ &key.iov_len); + if (r < 0) + return log_error_errno(r, "Failed to read key file '%s': %m", optarg); + +- erase_and_free(arg_key); +- arg_key = TAKE_PTR(k); +- arg_key_size = n; ++ iovec_done_erase(&arg_key); ++ arg_key = key; + break; + } + diff --git a/0716-repart-make-tpm2-pcrs-also-configurable-in-repart.d.patch b/0716-repart-make-tpm2-pcrs-also-configurable-in-repart.d.patch new file mode 100644 index 0000000..015b40a --- /dev/null +++ b/0716-repart-make-tpm2-pcrs-also-configurable-in-repart.d.patch @@ -0,0 +1,191 @@ +From bb445efac7fcd630fa2e59bf1b31355b49ae4012 Mon Sep 17 00:00:00 2001 +From: Emanuele Giuseppe Esposito +Date: Thu, 3 Jul 2025 08:08:53 -0400 +Subject: [PATCH] repart: make --tpm2-pcrs also configurable in repart.d/* + +Add repart.d TPM2PCRs= option with the same syntax as --tpm2-pcrs. +This allows a per-partition pcr binding, and not rely on a global config +applicable to all partitions. + +The global --tpm2-pcrs overrides TPM2PCRs config. If none of them +is defined, rely on default. + +(cherry picked from commit 49dcc89ddc15651ebca8da7a13e5c5b08ec247cb) + +Resolves: RHEL-115813 +--- + man/repart.d.xml | 13 ++++++++++ + src/repart/repart.c | 58 +++++++++++++++++++++++++++++++++++---------- + 2 files changed, 59 insertions(+), 12 deletions(-) + +diff --git a/man/repart.d.xml b/man/repart.d.xml +index 204fc16208..3e787555b4 100644 +--- a/man/repart.d.xml ++++ b/man/repart.d.xml +@@ -837,6 +837,19 @@ + + + ++ ++ TPM2PCRs= ++ ++ Configures the list of PCRs to use for LUKS2 volumes configured with ++ the Encrypt=tpm2 setting in partition files. ++ This option take the same parameters as the similary named options to ++ systemd-cryptenroll1 ++ and have the same effect on partitions where TPM2 enrollment is requested. ++ This option will be overridden by the global --tpm2-pcrs= option. ++ ++ ++ ++ + + Compression= + +diff --git a/src/repart/repart.c b/src/repart/repart.c +index a2825cddae..28677e02e2 100644 +--- a/src/repart/repart.c ++++ b/src/repart/repart.c +@@ -371,6 +371,8 @@ typedef struct Partition { + OrderedHashmap *subvolumes; + char *default_subvolume; + EncryptMode encrypt; ++ Tpm2PCRValue *tpm2_hash_pcr_values; ++ size_t tpm2_n_hash_pcr_values; + VerityMode verity; + char *verity_match_key; + MinimizeMode minimize; +@@ -616,6 +618,7 @@ static Partition* partition_free(Partition *p) { + strv_free(p->make_symlinks); + ordered_hashmap_free(p->subvolumes); + free(p->default_subvolume); ++ free(p->tpm2_hash_pcr_values); + free(p->verity_match_key); + free(p->compression); + free(p->compression_level); +@@ -657,6 +660,7 @@ static void partition_foreignize(Partition *p) { + p->make_symlinks = strv_free(p->make_symlinks); + p->subvolumes = ordered_hashmap_free(p->subvolumes); + p->default_subvolume = mfree(p->default_subvolume); ++ p->tpm2_hash_pcr_values = mfree(p->tpm2_hash_pcr_values); + p->verity_match_key = mfree(p->verity_match_key); + p->compression = mfree(p->compression); + p->compression_level = mfree(p->compression_level); +@@ -2289,6 +2293,33 @@ static int config_parse_encrypted_volume( + return 0; + } + ++static int config_parse_tpm2_pcrs( ++ const char *unit, ++ const char *filename, ++ unsigned line, ++ const char *section, ++ unsigned section_line, ++ const char *lvalue, ++ int ltype, ++ const char *rvalue, ++ void *data, ++ void *userdata) { ++ ++ Partition *partition = ASSERT_PTR(data); ++ ++ assert(rvalue); ++ ++ if (isempty(rvalue)) { ++ /* Clear existing PCR values if empty */ ++ partition->tpm2_hash_pcr_values = mfree(partition->tpm2_hash_pcr_values); ++ partition->tpm2_n_hash_pcr_values = 0; ++ return 0; ++ } ++ ++ return tpm2_parse_pcr_argument_append(rvalue, &partition->tpm2_hash_pcr_values, ++ &partition->tpm2_n_hash_pcr_values); ++} ++ + static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF); + static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mode, MinimizeMode, MINIMIZE_OFF); + +@@ -2363,6 +2394,7 @@ static int partition_read_definition(Partition *p, const char *path, const char + { "Partition", "VerityHashBlockSizeBytes", config_parse_block_size, 0, &p->verity_hash_block_size }, + { "Partition", "MountPoint", config_parse_mountpoint, 0, p }, + { "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p }, ++ { "Partition", "TPM2PCRs", config_parse_tpm2_pcrs, 0, p }, + { "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression }, + { "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level }, + { "Partition", "SupplementFor", config_parse_string, 0, &p->supplement_for_name }, +@@ -4590,8 +4622,10 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta + ssize_t base64_encoded_size; + int keyslot; + TPM2Flags flags = 0; ++ Tpm2PCRValue *pcr_values = arg_tpm2_n_hash_pcr_values > 0 ? arg_tpm2_hash_pcr_values : p->tpm2_hash_pcr_values; ++ size_t n_pcr_values = arg_tpm2_n_hash_pcr_values > 0 ? arg_tpm2_n_hash_pcr_values : p->tpm2_n_hash_pcr_values; + +- if (arg_tpm2_n_hash_pcr_values == 0 && ++ if (n_pcr_values == 0 && + arg_tpm2_public_key_pcr_mask == 0 && + !arg_tpm2_pcrlock) + log_notice("Notice: encrypting future partition %" PRIu64 ", locking against TPM2 with an empty policy, i.e. without any state or access restrictions.\n" +@@ -4631,7 +4665,7 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta + if (r < 0) + return r; + +- if (!tpm2_pcr_values_has_all_values(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values)) ++ if (!tpm2_pcr_values_has_all_values(pcr_values, n_pcr_values)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Must provide all PCR values when using TPM2 device key."); + } else { +@@ -4639,8 +4673,8 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta + if (r < 0) + return r; + +- if (!tpm2_pcr_values_has_all_values(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values)) { +- r = tpm2_pcr_read_missing_values(tpm2_context, arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values); ++ if (!tpm2_pcr_values_has_all_values(pcr_values, n_pcr_values)) { ++ r = tpm2_pcr_read_missing_values(tpm2_context, pcr_values, n_pcr_values); + if (r < 0) + return log_error_errno(r, "Could not read pcr values: %m"); + } +@@ -4648,17 +4682,17 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta + + uint16_t hash_pcr_bank = 0; + uint32_t hash_pcr_mask = 0; +- if (arg_tpm2_n_hash_pcr_values > 0) { ++ if (n_pcr_values > 0) { + size_t hash_count; +- r = tpm2_pcr_values_hash_count(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, &hash_count); ++ r = tpm2_pcr_values_hash_count(pcr_values, n_pcr_values, &hash_count); + if (r < 0) + return log_error_errno(r, "Could not get hash count: %m"); + + if (hash_count > 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Multiple PCR banks selected."); + +- hash_pcr_bank = arg_tpm2_hash_pcr_values[0].hash; +- r = tpm2_pcr_values_to_mask(arg_tpm2_hash_pcr_values, arg_tpm2_n_hash_pcr_values, hash_pcr_bank, &hash_pcr_mask); ++ hash_pcr_bank = pcr_values[0].hash; ++ r = tpm2_pcr_values_to_mask(pcr_values, n_pcr_values, hash_pcr_bank, &hash_pcr_mask); + if (r < 0) + return log_error_errno(r, "Could not get hash mask: %m"); + } +@@ -4671,8 +4705,8 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta + + /* If both PCR public key unlock and pcrlock unlock is selected, then shard the encryption key. */ + r = tpm2_calculate_sealing_policy( +- arg_tpm2_hash_pcr_values, +- arg_tpm2_n_hash_pcr_values, ++ pcr_values, ++ n_pcr_values, + iovec_is_set(&pubkey) ? &public : NULL, + /* use_pin= */ false, + arg_tpm2_pcrlock && !iovec_is_set(&pubkey) ? &pcrlock_policy : NULL, +@@ -4682,8 +4716,8 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta + + if (arg_tpm2_pcrlock && iovec_is_set(&pubkey)) { + r = tpm2_calculate_sealing_policy( +- arg_tpm2_hash_pcr_values, +- arg_tpm2_n_hash_pcr_values, ++ pcr_values, ++ n_pcr_values, + /* public= */ NULL, /* Turn this one off for the 2nd shard */ + /* use_pin= */ false, + &pcrlock_policy, /* But turn this one on */ diff --git a/0717-repart-make-key-file-also-configurable-in-repart.d.patch b/0717-repart-make-key-file-also-configurable-in-repart.d.patch new file mode 100644 index 0000000..0bae659 --- /dev/null +++ b/0717-repart-make-key-file-also-configurable-in-repart.d.patch @@ -0,0 +1,203 @@ +From 447bf1e534dcd077b5fca9678642a818b9fd5960 Mon Sep 17 00:00:00 2001 +From: Emanuele Giuseppe Esposito +Date: Mon, 14 Jul 2025 05:51:49 -0400 +Subject: [PATCH] repart: make --key-file also configurable in repart.d/* + +Add repart.d KeyFile= option with the same syntax as --key-file. +This allows a per-partition key file encryption, and not rely on a global key +applicable to all partitions. + +The global --key-file overrides KeyFile config. If none of them is +defined, rely on default. + +(cherry picked from commit eb44fa4d198d1da11c998f77bc88f95aaf67e186) + +Resolves: RHEL-115813 +--- + man/repart.d.xml | 12 +++++++ + man/systemd-repart.xml | 8 ++--- + src/repart/repart.c | 78 ++++++++++++++++++++++++++++++++---------- + 3 files changed, 76 insertions(+), 22 deletions(-) + +diff --git a/man/repart.d.xml b/man/repart.d.xml +index 3e787555b4..61deae7b72 100644 +--- a/man/repart.d.xml ++++ b/man/repart.d.xml +@@ -850,6 +850,18 @@ + + + ++ ++ KeyFile= ++ ++ Takes a file system path. This path must be absolute, otherwise the option is ignored. ++ Configures the encryption key to use when setting up LUKS2 volumes configured with the ++ Encrypt=key-file setting in partition files. Please refer to the documentation of ++ --key-file= for more details. This option will be overridden by the global ++ --key-file= option. ++ ++ ++ ++ + + Compression= + +diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml +index d1740af5a2..23951c9d04 100644 +--- a/man/systemd-repart.xml ++++ b/man/systemd-repart.xml +@@ -337,10 +337,10 @@ + Takes a file system path. Configures the encryption key to use when setting up LUKS2 + volumes configured with the Encrypt=key-file setting in partition files. Should + refer to a regular file containing the key, or an AF_UNIX stream socket in the +- file system. In the latter case a connection is made to it and the key read from it. If this switch +- is not specified the empty key (i.e. zero length key) is used. This behaviour is useful for setting +- up encrypted partitions during early first boot that receive their user-supplied password only in a +- later setup step. ++ file system. In the latter case, a connection is made to it and the key read from it. If this switch ++ is not specified, and no KeyFile= is specified in the partition file, the empty ++ key (i.e. zero length key) is used. This behaviour is useful for setting up encrypted partitions during ++ early first boot that receive their user-supplied password only in a later setup step. + + + +diff --git a/src/repart/repart.c b/src/repart/repart.c +index 28677e02e2..a9877f57c1 100644 +--- a/src/repart/repart.c ++++ b/src/repart/repart.c +@@ -371,6 +371,7 @@ typedef struct Partition { + OrderedHashmap *subvolumes; + char *default_subvolume; + EncryptMode encrypt; ++ struct iovec key; + Tpm2PCRValue *tpm2_hash_pcr_values; + size_t tpm2_n_hash_pcr_values; + VerityMode verity; +@@ -623,6 +624,8 @@ static Partition* partition_free(Partition *p) { + free(p->compression); + free(p->compression_level); + ++ iovec_done_erase(&p->key); ++ + iovec_done(&p->roothash); + + free(p->split_name_format); +@@ -665,6 +668,8 @@ static void partition_foreignize(Partition *p) { + p->compression = mfree(p->compression); + p->compression_level = mfree(p->compression_level); + ++ iovec_done_erase(&p->key); ++ + p->priority = 0; + p->weight = 1000; + p->padding_weight = 0; +@@ -2320,6 +2325,51 @@ static int config_parse_tpm2_pcrs( + &partition->tpm2_n_hash_pcr_values); + } + ++static int parse_key_file(const char *filename, struct iovec *key) { ++ _cleanup_(erase_and_freep) char *k = NULL; ++ size_t n = 0; ++ int r; ++ ++ r = read_full_file_full( ++ AT_FDCWD, filename, ++ /* offset= */ UINT64_MAX, ++ /* size= */ SIZE_MAX, ++ READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, ++ /* bind_name= */ NULL, ++ &k, &n); ++ if (r < 0) ++ return log_error_errno(r, "Failed to read key file '%s': %m", filename); ++ ++ iovec_done_erase(key); ++ *key = IOVEC_MAKE(TAKE_PTR(k), n); ++ ++ return 0; ++} ++ ++static int config_parse_key_file( ++ const char *unit, ++ const char *filename, ++ unsigned line, ++ const char *section, ++ unsigned section_line, ++ const char *lvalue, ++ int ltype, ++ const char *rvalue, ++ void *data, ++ void *userdata) { ++ ++ Partition *partition = ASSERT_PTR(userdata); ++ ++ assert(rvalue); ++ ++ if (isempty(rvalue)) { ++ iovec_done_erase(&partition->key); ++ return 0; ++ } ++ ++ return parse_key_file(rvalue, &partition->key); ++} ++ + static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_verity, verity_mode, VerityMode, VERITY_OFF); + static DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_minimize, minimize_mode, MinimizeMode, MINIMIZE_OFF); + +@@ -2395,6 +2445,7 @@ static int partition_read_definition(Partition *p, const char *path, const char + { "Partition", "MountPoint", config_parse_mountpoint, 0, p }, + { "Partition", "EncryptedVolume", config_parse_encrypted_volume, 0, p }, + { "Partition", "TPM2PCRs", config_parse_tpm2_pcrs, 0, p }, ++ { "Partition", "KeyFile", config_parse_key_file, 0, p }, + { "Partition", "Compression", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression }, + { "Partition", "CompressionLevel", config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, &p->compression_level }, + { "Partition", "SupplementFor", config_parse_string, 0, &p->supplement_for_name }, +@@ -4600,18 +4651,21 @@ static int partition_encrypt(Context *context, Partition *p, PartitionTarget *ta + return log_error_errno(r, "Failed to LUKS2 format future partition: %m"); + + if (IN_SET(p->encrypt, ENCRYPT_KEY_FILE, ENCRYPT_KEY_FILE_TPM2)) { ++ /* Use partition-specific key if available, otherwise fall back to global key */ ++ struct iovec *iovec_key = arg_key.iov_base ? &arg_key : &p->key; ++ + r = sym_crypt_keyslot_add_by_volume_key( + cd, + CRYPT_ANY_SLOT, + NULL, + VOLUME_KEY_SIZE, +- strempty(arg_key.iov_base), +- arg_key.iov_len); ++ strempty(iovec_key->iov_base), ++ iovec_key->iov_len); + if (r < 0) + return log_error_errno(r, "Failed to add LUKS2 key: %m"); + +- passphrase = strempty(arg_key.iov_base); +- passphrase_size = arg_key.iov_len; ++ passphrase = strempty(iovec_key->iov_base); ++ passphrase_size = iovec_key->iov_len; + } + + if (IN_SET(p->encrypt, ENCRYPT_TPM2, ENCRYPT_KEY_FILE_TPM2)) { +@@ -8147,21 +8201,9 @@ static int parse_argv(int argc, char *argv[], X509 **ret_certificate, EVP_PKEY * + break; + + case ARG_KEY_FILE: { +- struct iovec key = {}; +- +- r = read_full_file_full( +- AT_FDCWD, optarg, +- /* offset= */ UINT64_MAX, +- /* size= */ SIZE_MAX, +- READ_FULL_FILE_SECURE|READ_FULL_FILE_WARN_WORLD_READABLE|READ_FULL_FILE_CONNECT_SOCKET, +- /* bind_name= */ NULL, +- (char **) &key.iov_base, +- &key.iov_len); ++ r = parse_key_file(optarg, &arg_key); + if (r < 0) +- return log_error_errno(r, "Failed to read key file '%s': %m", optarg); +- +- iovec_done_erase(&arg_key); +- arg_key = key; ++ return r; + break; + } + diff --git a/0718-man-repart-fix-typo.patch b/0718-man-repart-fix-typo.patch new file mode 100644 index 0000000..d8c0de0 --- /dev/null +++ b/0718-man-repart-fix-typo.patch @@ -0,0 +1,27 @@ +From 18ed8dc523fcc68143869dfee2b6714a2b8b694e Mon Sep 17 00:00:00 2001 +From: Yu Watanabe +Date: Fri, 19 Sep 2025 20:24:06 +0900 +Subject: [PATCH] man/repart: fix typo + +Follow-up for 49dcc89ddc15651ebca8da7a13e5c5b08ec247cb. + +(cherry picked from commit cbdbf68a7266c515db12c87542483b907fe7becf) + +Related: RHEL-115813 +--- + man/repart.d.xml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/man/repart.d.xml b/man/repart.d.xml +index 61deae7b72..8e63a48d93 100644 +--- a/man/repart.d.xml ++++ b/man/repart.d.xml +@@ -842,7 +842,7 @@ + + Configures the list of PCRs to use for LUKS2 volumes configured with + the Encrypt=tpm2 setting in partition files. +- This option take the same parameters as the similary named options to ++ This option take the same parameters as the similarly named options to + systemd-cryptenroll1 + and have the same effect on partitions where TPM2 enrollment is requested. + This option will be overridden by the global --tpm2-pcrs= option. diff --git a/0719-man-systemd-measure.-Remove-tpm2-pcrs-from-cryptenro.patch b/0719-man-systemd-measure.-Remove-tpm2-pcrs-from-cryptenro.patch new file mode 100644 index 0000000..01710d9 --- /dev/null +++ b/0719-man-systemd-measure.-Remove-tpm2-pcrs-from-cryptenro.patch @@ -0,0 +1,27 @@ +From 2e50c0aef45f848823fa065c888903272478e802 Mon Sep 17 00:00:00 2001 +From: cvlc12 <97767846+cvlc12@users.noreply.github.com> +Date: Thu, 6 Nov 2025 21:54:06 +0100 +Subject: [PATCH] man: systemd-measure. Remove 'tpm2-pcrs=' from cryptenroll + command (#39590) + +This is now default since 4b840414be3b2d6520599d86d2b718a37574aabf. + +(cherry picked from commit c3e80f8f2bbd2b79350684f52638cedec00eb8ad) + +Related: RHEL-115813 +--- + man/systemd-measure.xml | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/man/systemd-measure.xml b/man/systemd-measure.xml +index 368c94f700..5d697dd62f 100644 +--- a/man/systemd-measure.xml ++++ b/man/systemd-measure.xml +@@ -328,7 +328,6 @@ $ ukify build \ + # systemd-cryptenroll --tpm2-device=auto \ + --tpm2-public-key=tpm2-pcr-public-key.pem \ + --tpm2-signature=tpm2-pcr-signature.json \ +- --tpm2-pcrs="" \ + /dev/sda5 + + And then unlock the device with the signature: diff --git a/systemd.spec b/systemd.spec index 80d0ee0..a8f62e9 100644 --- a/systemd.spec +++ b/systemd.spec @@ -48,7 +48,7 @@ Url: https://systemd.io # Allow users to specify the version and release when building the rpm by # setting the %%version_override and %%release_override macros. Version: %{?version_override}%{!?version_override:257} -Release: 31%{?dist}.alma.1 +Release: 32%{?dist}.alma.1 %global stable %(c="%version"; [ "$c" = "${c#*.*}" ]; echo $?) @@ -809,6 +809,26 @@ Patch0696: 0696-TEST-87-AUX-UTILS-VM-rotate-journal-at-one-more-plac.patch Patch0697: 0697-udev-rules-add-missing-device-name-prefix-in-log-mes.patch Patch0698: 0698-nss-systemd-avoid-ELF-TLS-for-recursion-guard.patch Patch0699: 0699-core-make-manager-event-loop-rate-limit-configurable.patch +Patch0700: 0700-resolved-replace-assert-with-error-return-in-DNSSEC-.patch +Patch0701: 0701-pid1-normalize-oom-error-handling-a-bit.patch +Patch0702: 0702-sd-path-don-t-chop-off-trailing-slash-in-sd_path-api.patch +Patch0703: 0703-systemd-path-order-all-listed-paths-by-their-ID-alph.patch +Patch0704: 0704-systemd-path-guarantee-that-tool-exit-status-is-zero.patch +Patch0705: 0705-systemd-path-add-the-usual-ANSI-sequences-to-help-te.patch +Patch0706: 0706-sd-path-expose-credential-store-in-sd-path.patch +Patch0707: 0707-execute-introduce-a-user-scoped-credstore.patch +Patch0708: 0708-pid1-add-support-for-decrypting-per-user-credentials.patch +Patch0709: 0709-test-add-integration-test-that-makes-sure-unpriv-cre.patch +Patch0710: 0710-man-document-the-new-per-use-credstore-paths.patch +Patch0711: 0711-update-TODO.patch +Patch0712: 0712-cryptenroll-repart-creds-no-longer-default-to-bindin.patch +Patch0713: 0713-cryptenroll-repart-print-a-log-message-if-no-access-.patch +Patch0714: 0714-repart-correct-argument-comment.patch +Patch0715: 0715-repart-use-iovec-structure-for-key-file.patch +Patch0716: 0716-repart-make-tpm2-pcrs-also-configurable-in-repart.d.patch +Patch0717: 0717-repart-make-key-file-also-configurable-in-repart.d.patch +Patch0718: 0718-man-repart-fix-typo.patch +Patch0719: 0719-man-systemd-measure.-Remove-tpm2-pcrs-from-cryptenro.patch # Downstream-only patches (9000–9999) %endif @@ -1760,9 +1780,31 @@ rm -f .file-list-* rm -f %{name}.lang %changelog -* Thu Jul 30 2026 Andrew Lukoshko - 257-31.alma.1 +* Wed Aug 05 2026 Andrew Lukoshko - 257-32.alma.1 - Debrand for AlmaLinux +* Fri Jul 31 2026 systemd maintenance team - 257-32 +- resolved: replace assert() with error return in DNSSEC verify functions (RHEL-158349) +- pid1: normalize oom error handling a bit (RHEL-169656) +- sd-path: don't chop off trailing slash in sd_path apis, when user provided them (RHEL-169656) +- systemd-path: order all listed paths by their ID alphabetically (RHEL-169656) +- systemd-path: guarantee that tool exit status is zero on success (RHEL-169656) +- systemd-path: add the usual ANSI sequences to --help text (RHEL-169656) +- sd-path: expose credential store in sd-path (RHEL-169656) +- execute: introduce a user-scoped credstore (RHEL-169656) +- pid1: add support for decrypting per-user credentials (RHEL-169656) +- test: add integration test that makes sure unpriv creds work correctly (RHEL-169656) +- man: document the new per-use credstore paths (RHEL-169656) +- update TODO (RHEL-169656) +- cryptenroll/repart/creds: no longer default to binding against literal PCR 7 (RHEL-115813) +- cryptenroll,repart: print a log message if no access restrictions are applied to TPM-based encryption (RHEL-115813) +- repart: correct argument comment (RHEL-115813) +- repart: use iovec structure for --key-file (RHEL-115813) +- repart: make --tpm2-pcrs also configurable in repart.d/* (RHEL-115813) +- repart: make --key-file also configurable in repart.d/* (RHEL-115813) +- man/repart: fix typo (RHEL-115813) +- man: systemd-measure. Remove 'tpm2-pcrs=' from cryptenroll command (#39590) (RHEL-115813) + * Mon Jul 27 2026 systemd maintenance team - 257-31 - core: make manager event loop rate limit configurable (RHEL-161564)