systemd-257-32

Resolves: RHEL-158349, RHEL-169656, RHEL-115813
This commit is contained in:
Jan Macku 2026-07-31 14:16:56 +02:00
parent 85341d5909
commit 1e44c171ce
21 changed files with 1761 additions and 1 deletions

View File

@ -0,0 +1,53 @@
From f114aaba224846122a0329bc6f12ea163e9e56df Mon Sep 17 00:00:00 2001
From: Milan Kyselica <mil.kyselica@gmail.com>
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)

View File

@ -0,0 +1,70 @@
From 63e6c6b5cb74cd8bb407ccb3f153a7dd92ad3274 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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);

View File

@ -0,0 +1,52 @@
From 0846e7a060e34230e7be7d9224681de3bdc929ac Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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);

View File

@ -0,0 +1,79 @@
From b4b11980b3a96d7b380985e60b102c035f4cf80f Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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;

View File

@ -0,0 +1,33 @@
From d5b7a6386a5a64b6e67e1d42dacf1f5bb5ddb5dd Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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;

View File

@ -0,0 +1,35 @@
From 061d64e4ab4912c3f704c678c86e8e4ec223ca4a Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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;

View File

@ -0,0 +1,161 @@
From 8a9de694d22825ffd201632afc2d139c028db9ab Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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
};

View File

@ -0,0 +1,148 @@
From 6ec0340db60ffa738c62806be215cc8e74e944cc Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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);
+}

View File

@ -0,0 +1,68 @@
From 53b5f30038dbe20197fe77cfd76ed096951c7d81 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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;

View File

@ -0,0 +1,59 @@
From 0928982a40942ccd17f5527886d1ceaf0fe16709 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,78 @@
From 97f26174ca86237dfae9f23cc2f66639fa6d5f7c Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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
<term><varname>LoadCredentialEncrypted=</varname><replaceable>ID</replaceable><optional>:<replaceable>PATH</replaceable></optional></term>
<listitem><para>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
- <varname>User=</varname>/<varname>DynamicUser=</varname> settings (as well as the superuser). When
- available, the location of credentials is exported as the <varname>$CREDENTIALS_DIRECTORY</varname>
- environment variable to the unit's processes.</para>
+ 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 <varname>User=</varname>/<varname>DynamicUser=</varname> settings
+ (as well as the superuser). When available, the location of credentials is exported as the
+ <varname>$CREDENTIALS_DIRECTORY</varname> environment variable to the unit's processes.</para>
<para>The <varname>LoadCredential=</varname> 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 <constant>AF_UNIX</constant> 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.</para>
<para>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 <filename>/etc/credstore/</filename>, <filename>/run/credstore/</filename> and
- <filename>/usr/lib/credstore/</filename> 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 <filename>/etc/credstore/</filename>,
+ <filename>/run/credstore/</filename> and <filename>/usr/lib/credstore/</filename> for files under the
+ credential's name — which hence are recommended locations for credential data on disk. If
<varname>LoadCredentialEncrypted=</varname> is used <filename>/run/credstore.encrypted/</filename>,
<filename>/etc/credstore.encrypted/</filename>, and
- <filename>/usr/lib/credstore.encrypted/</filename> are searched as well.</para>
+ <filename>/usr/lib/credstore.encrypted/</filename> are searched as well. The per-user service manager
+ will search <filename>$XDG_CONFIG_HOME/credstore/</filename>,
+ <filename>$XDG_RUNTIME_DIR/credstore/</filename>, <filename>$HOME/.local/lib/credstore/</filename>
+ (and the counterparts ending with <filename>…/credstore.encrypted/</filename>) instead. The
+ <citerefentry><refentrytitle>systemd-path</refentrytitle><manvolnum>1</manvolnum></citerefentry> tool
+ may be used to query the precise credential store search path.</para>
<para>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.</para>
+ 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.</para>
<para>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.</para>

27
0711-update-TODO.patch Normal file
View File

@ -0,0 +1,27 @@
From 446eee5eca6fb42aa9ed2d3b932073edc25de002 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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

View File

@ -0,0 +1,248 @@
From e3d696ad24a3fbab6b6548635dbb9519061abbb5 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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 @@
<term><option>--tpm2-pcrs=<replaceable>PCR<optional>+PCR...</optional></replaceable></option></term>
<listitem><para>Configures the TPM2 PCRs (Platform Configuration Registers) to bind the encryption
- key to. Takes a <literal>+</literal> 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 <literal>+</literal> 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
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
<xi:include href="version-info.xml" xpointer="v250"/></listitem>
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
<literal>:</literal> and a hash algorithm name (specifying the PCR bank), optionally followed by
<literal>=</literal> and a hash digest value. Multiple PCR entries are separated by
- <literal>+</literal>. 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
+ <literal>+</literal>. 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.</para>
<para>Example: <option>--tpm2-pcrs=boot-loader-code+platform-config+boot-loader-config</option>
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

View File

@ -0,0 +1,49 @@
From 00a47b5ca3631bf40bee4b78ff37a256a7224705 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
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) {

View File

@ -0,0 +1,25 @@
From 730def2a4ac16d93d5e0e5050965b1a920e462b2 Mon Sep 17 00:00:00 2001
From: Jelle van der Waa <jvanderwaa@redhat.com>
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);

View File

@ -0,0 +1,85 @@
From 480f7679259923ffc708273142770bfe39123e26 Mon Sep 17 00:00:00 2001
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
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;
}

View File

@ -0,0 +1,191 @@
From bb445efac7fcd630fa2e59bf1b31355b49ae4012 Mon Sep 17 00:00:00 2001
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
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 @@
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>TPM2PCRs=</varname></term>
+
+ <listitem><para>Configures the list of PCRs to use for LUKS2 volumes configured with
+ the <varname>Encrypt=tpm2</varname> setting in partition files.
+ This option take the same parameters as the similary named options to
+ <citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ and have the same effect on partitions where TPM2 enrollment is requested.
+ This option will be overridden by the global <varname>--tpm2-pcrs=</varname> option.</para>
+
+ <xi:include href="version-info.xml" xpointer="v259"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>Compression=</varname></term>
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 */

View File

@ -0,0 +1,203 @@
From 447bf1e534dcd077b5fca9678642a818b9fd5960 Mon Sep 17 00:00:00 2001
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
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 @@
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>KeyFile=</varname></term>
+
+ <listitem><para>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
+ <varname>Encrypt=key-file</varname> setting in partition files. Please refer to the documentation of
+ <varname>--key-file=</varname> for more details. This option will be overridden by the global
+ <varname>--key-file=</varname> option.</para>
+
+ <xi:include href="version-info.xml" xpointer="v259"/></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>Compression=</varname></term>
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 @@
<listitem><para>Takes a file system path. Configures the encryption key to use when setting up LUKS2
volumes configured with the <varname>Encrypt=key-file</varname> setting in partition files. Should
refer to a regular file containing the key, or an <constant>AF_UNIX</constant> 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.</para>
+ 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 <varname>KeyFile=</varname> 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.</para>
<xi:include href="version-info.xml" xpointer="v247"/></listitem>
</varlistentry>
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;
}

View File

@ -0,0 +1,27 @@
From 18ed8dc523fcc68143869dfee2b6714a2b8b694e Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
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 @@
<listitem><para>Configures the list of PCRs to use for LUKS2 volumes configured with
the <varname>Encrypt=tpm2</varname> 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
<citerefentry><refentrytitle>systemd-cryptenroll</refentrytitle><manvolnum>1</manvolnum></citerefentry>
and have the same effect on partitions where TPM2 enrollment is requested.
This option will be overridden by the global <varname>--tpm2-pcrs=</varname> option.</para>

View File

@ -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 \
<programlisting># systemd-cryptenroll --tpm2-device=auto \
--tpm2-public-key=tpm2-pcr-public-key.pem \
--tpm2-signature=tpm2-pcr-signature.json \
- --tpm2-pcrs="" \
/dev/sda5</programlisting>
<para>And then unlock the device with the signature:</para>

View File

@ -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}
Release: 32%{?dist}
%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 (90009999)
%endif
@ -1760,6 +1780,28 @@ rm -f .file-list-*
rm -f %{name}.lang
%changelog
* Fri Jul 31 2026 systemd maintenance team <systemd-maint@redhat.com> - 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 <systemd-maint@redhat.com> - 257-31
- core: make manager event loop rate limit configurable (RHEL-161564)