]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tpm2-setup: measure "anchor" extension early at boot into nvpcrs
authorLennart Poettering <lennart@poettering.net>
Tue, 4 Jun 2024 16:16:03 +0000 (18:16 +0200)
committerLennart Poettering <lennart@poettering.net>
Sun, 2 Nov 2025 20:14:35 +0000 (21:14 +0100)
man/systemd-tpm2-setup.service.xml
src/tpm2-setup/tpm2-setup.c
units/systemd-tpm2-setup.service.in

index 52ed6acf92aa8cbe76db1ae66bc994779c371e36..95d8da464400179cc9b61f4b63fc8054a5a4a070 100644 (file)
@@ -20,7 +20,7 @@
     <refname>systemd-tpm2-setup.service</refname>
     <refname>systemd-tpm2-setup-early.service</refname>
     <refname>systemd-tpm2-setup</refname>
-    <refpurpose>Set up the TPM2 Storage Root Key (SRK) at boot</refpurpose>
+    <refpurpose>Set up the TPM2 Storage Root Key (SRK) and initialize NvPCRs at boot</refpurpose>
   </refnamediv>
 
   <refsynopsisdiv>
@@ -33,7 +33,8 @@
 
     <para><filename>systemd-tpm2-setup.service</filename> and
     <filename>systemd-tpm2-setup-early.service</filename> are services that generate the Storage Root Key
-    (SRK) if it has not been generated yet, and stores it in the TPM.</para>
+    (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.</para>
 
     <para>The services will store the public key of the SRK key pair in a PEM file in
     <filename>/run/systemd/tpm2-srk-public-key.pem</filename> and
 
         <xi:include href="version-info.xml" xpointer="v255"/></listitem>
       </varlistentry>
+
+      <varlistentry>
+        <term><filename>/usr/lib/nvpcr/*.nvpcr</filename></term>
+
+        <listitem><para>Definition files for NvPCRs.</para>
+        <xi:include href="version-info.xml" xpointer="v259"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><filename>/run/credentials/@encrypted/nvpcr-anchor.*</filename></term>
+
+        <listitem><para>Encrypted NvPCR anchor secret, received into the system via system credentials.</para>
+        <xi:include href="version-info.xml" xpointer="v259"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><filename>$BOOT/loader/credentials/nvpcr-anchor.*.cred</filename></term>
+
+        <listitem><para>Encrypted NvPCR anchor secret, to be picked up by the kernel loader stub,
+        i.e. <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>.</para>
+        <xi:include href="version-info.xml" xpointer="v259"/></listitem>
+      </varlistentry>
     </variablelist>
   </refsect1>
 
index 6338b7a75b6dbbe18f4c41f72e8180d6ed75d25c..baa485debee2b13540a0935780391fcdbf4ed8a4 100644 (file)
@@ -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"
 #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);
index ac29a769660d39aade5f7272034a4c729bd0f7e1..34404a24cb5e140cb5e867f71f2baaa9e9782374 100644 (file)
@@ -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