diff --git a/man/systemd-tpm2-setup.service.xml b/man/systemd-tpm2-setup.service.xml
index 52ed6acf92..95d8da4644 100644
--- a/man/systemd-tpm2-setup.service.xml
+++ b/man/systemd-tpm2-setup.service.xml
@@ -20,7 +20,7 @@
systemd-tpm2-setup.service
systemd-tpm2-setup-early.service
systemd-tpm2-setup
- Set up the TPM2 Storage Root Key (SRK) at boot
+ Set up the TPM2 Storage Root Key (SRK) and initialize NvPCRs at boot
@@ -33,7 +33,8 @@
systemd-tpm2-setup.service and
systemd-tpm2-setup-early.service are services that generate the Storage Root Key
- (SRK) if it has not been generated yet, and stores it in the TPM.
+ (SRK) if it has not been generated yet, and stores it in the TPM. If NvPCRs (additional PCR registers in
+ TPM NV Indexes) are defined, these are initialized with the anchoring secret.
The services will store the public key of the SRK key pair in a PEM file in
/run/systemd/tpm2-srk-public-key.pem and
@@ -70,6 +71,28 @@
+
+
+ /usr/lib/nvpcr/*.nvpcr
+
+ Definition files for NvPCRs.
+
+
+
+
+ /run/credentials/@encrypted/nvpcr-anchor.*
+
+ Encrypted NvPCR anchor secret, received into the system via system credentials.
+
+
+
+
+ $BOOT/loader/credentials/nvpcr-anchor.*.cred
+
+ Encrypted NvPCR anchor secret, to be picked up by the kernel loader stub,
+ i.e. systemd-stub7.
+
+
diff --git a/src/tpm2-setup/tpm2-setup.c b/src/tpm2-setup/tpm2-setup.c
index 6338b7a75b..baa485debe 100644
--- a/src/tpm2-setup/tpm2-setup.c
+++ b/src/tpm2-setup/tpm2-setup.c
@@ -7,6 +7,10 @@
#include "alloc-util.h"
#include "build.h"
+#include "conf-files.h"
+#include "constants.h"
+#include "creds-util.h"
+#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
@@ -16,7 +20,12 @@
#include "mkdir.h"
#include "parse-util.h"
#include "pretty-print.h"
+#include "recurse-dir.h"
+#include "set.h"
#include "string-util.h"
+#include "strv.h"
+#include "terminal-util.h"
+#include "time-util.h"
#include "tmpfile-util.h"
#include "tpm2-util.h"
@@ -41,7 +50,7 @@ static int help(int argc, char *argv[], void *userdata) {
return log_oom();
printf("%1$s [OPTIONS...]\n"
- "\n%5$sSet up the TPM2 Storage Root Key (SRK).%6$s\n"
+ "\n%5$sSet up the TPM2 Storage Root Key (SRK), and initialize NvPCRs.%6$s\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Show package version\n"
@@ -377,6 +386,108 @@ static int setup_srk(void) {
return 0;
}
+typedef struct SetupNvPCRContext {
+ Tpm2Context *tpm2_context;
+ struct iovec anchor_secret;
+ size_t n_already, n_anchored;
+ Set *done;
+} SetupNvPCRContext;
+
+static void setup_nvpcr_context_done(SetupNvPCRContext *c) {
+ assert(c);
+
+ iovec_done_erase(&c->anchor_secret);
+ c->tpm2_context = tpm2_context_unref(c->tpm2_context);
+ c->done = set_free(c->done);
+}
+
+static int setup_nvpcr_one(
+ SetupNvPCRContext *c,
+ const char *name) {
+ int r;
+
+ assert(c);
+ assert(name);
+
+ if (set_contains(c->done, name))
+ return 0;
+
+ if (!c->tpm2_context) {
+ r = tpm2_context_new_or_warn(arg_tpm2_device, &c->tpm2_context);
+ if (r < 0)
+ return r;
+ }
+
+ r = tpm2_nvpcr_initialize(c->tpm2_context, /* session= */ NULL, name, &c->anchor_secret);
+ if (r == -EUNATCH) {
+ assert(!iovec_is_set(&c->anchor_secret));
+
+ /* If we get EUNATCH this means we actually need to initialize this NvPCR
+ * now, and haven't provided the anchor secret yet. Hence acquire it now. */
+
+ r = tpm2_nvpcr_acquire_anchor_secret(&c->anchor_secret, /* sync_secondary= */ !arg_early);
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire anchor secret: %m");
+
+ r = tpm2_nvpcr_initialize(c->tpm2_context, /* session= */ NULL, name, &c->anchor_secret);
+ }
+ if (r < 0)
+ return log_error_errno(r, "Failed to extend NvPCR index with anchor secret: %m");
+
+ if (r > 0)
+ c->n_anchored++;
+ else
+ c->n_already++;
+
+ if (set_put_strdup(&c->done, name) < 0)
+ return log_oom();
+
+ return 0;
+}
+
+static int setup_nvpcr(void) {
+ _cleanup_(setup_nvpcr_context_done) SetupNvPCRContext c = {};
+ int r = 0;
+
+ _cleanup_strv_free_ char **l = NULL;
+ r = conf_files_list_nulstr(
+ &l,
+ ".nvpcr",
+ /* root= */ NULL,
+ CONF_FILES_REGULAR|CONF_FILES_BASENAME|CONF_FILES_FILTER_MASKED|CONF_FILES_TRUNCATE_SUFFIX,
+ CONF_PATHS_NULSTR("nvpcr"));
+ if (r < 0)
+ return log_error_errno(r, "Failed to find .nvpcr files: %m");
+
+ STRV_FOREACH(i, l) {
+ r = setup_nvpcr_one(&c, *i);
+ if (r < 0)
+ return r;
+ }
+
+ if (c.n_already > 0 && c.n_anchored == 0 && !arg_early) {
+ /* If we didn't anchor anything right now, but we anchored something earlier, then it might
+ * have happened in the initrd, and thus the anchor ID was not commited to /var/ or the ESP
+ * yet. Hence, let's explicitly do so now, to catch up. */
+
+ r = tpm2_nvpcr_acquire_anchor_secret(/* ret= */ NULL, /* sync_secondary= */ true);
+ if (r < 0)
+ return r;
+ }
+
+ if (c.n_anchored > 0) {
+ if (c.n_already == 0)
+ log_info("%zu NvPCRs initialized.", c.n_anchored);
+ else
+ log_info("%zu NvPCRs initialized. (%zu NvPCRs were already initialized.)", c.n_anchored, c.n_already);
+ } else if (c.n_already > 0)
+ log_info("%zu NvPCRs already initialized.", c.n_already);
+ else
+ log_debug("No NvPCRs defined, nothing initialized.");
+
+ return r;
+}
+
static int run(int argc, char *argv[]) {
int r;
@@ -393,7 +504,10 @@ static int run(int argc, char *argv[]) {
umask(0022);
- return setup_srk();
+ r = setup_srk();
+ RET_GATHER(r, setup_nvpcr());
+
+ return r;
}
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);
diff --git a/units/systemd-tpm2-setup.service.in b/units/systemd-tpm2-setup.service.in
index ac29a76966..34404a24cb 100644
--- a/units/systemd-tpm2-setup.service.in
+++ b/units/systemd-tpm2-setup.service.in
@@ -14,7 +14,7 @@ DefaultDependencies=no
Conflicts=shutdown.target
After=tpm2.target systemd-tpm2-setup-early.service systemd-remount-fs.service
Before=sysinit.target shutdown.target
-RequiresMountsFor=/var/lib/systemd/tpm2-srk-public-key.pem
+RequiresMountsFor=/var/lib/systemd
ConditionSecurity=measured-uki
ConditionPathExists=!/etc/initrd-release