]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tpm2-setup: introduce a 'priority' concept for NvPCRs
authorLennart Poettering <lennart@amutable.com>
Thu, 21 May 2026 12:19:42 +0000 (14:19 +0200)
committerLennart Poettering <lennart@amutable.com>
Thu, 21 May 2026 14:09:25 +0000 (16:09 +0200)
src/analyze/analyze-nvpcrs.c
src/shared/tpm2-util.c
src/shared/tpm2-util.h
src/tpm2-setup/tpm2-setup.c

index 56b5c9a20494574335f0675e9d92d1d4d1569c6c..c3967ca01a581d4f451d8870d6c5d95e6dbf47c8 100644 (file)
@@ -16,6 +16,7 @@ static int add_nvpcr_to_table(Tpm2Context **c, Table *t, const char *name) {
 
         _cleanup_free_ char *h = NULL;
         uint32_t nv_index = 0;
+        uint64_t priority = 0;
         if (c) {
                 if (!*c) {
                         r = tpm2_context_new_or_warn(/* device= */ NULL, c);
@@ -24,7 +25,7 @@ static int add_nvpcr_to_table(Tpm2Context **c, Table *t, const char *name) {
                 }
 
                 _cleanup_(iovec_done) struct iovec digest = {};
-                r = tpm2_nvpcr_read(*c, /* session= */ NULL, name, &digest, &nv_index);
+                r = tpm2_nvpcr_read(*c, /* session= */ NULL, name, &digest, &nv_index, &priority);
                 if (r < 0)
                         return log_error_errno(r, "Failed to read NvPCR '%s': %m", name);
                 if (r > 0) { /* set? */
@@ -33,7 +34,7 @@ static int add_nvpcr_to_table(Tpm2Context **c, Table *t, const char *name) {
                                 return log_oom();
                 }
         } else {
-                r = tpm2_nvpcr_get_index(name, &nv_index);
+                r = tpm2_nvpcr_get_index(name, &nv_index, &priority);
                 if (r < 0)
                         return log_error_errno(r, "Failed to get NV index of NvPCR '%s': %m", name);
         }
index 533cdd630650e73c3529c82f71fd3b020c4a7f23..26ce8d72464bff41c9bc39e7a8e0bfeac56232fd 100644 (file)
@@ -6991,6 +6991,7 @@ typedef struct NvPCRData {
         char *name;
         uint16_t algorithm;
         uint32_t nv_index;
+        uint64_t priority;
 } NvPCRData;
 
 static void nvpcr_data_done(NvPCRData *d) {
@@ -7030,11 +7031,13 @@ static int nvpcr_data_load(const char *name, NvPCRData *ret) {
                 { "name",      SD_JSON_VARIANT_STRING,        sd_json_dispatch_string,      offsetof(NvPCRData, name),      SD_JSON_MANDATORY },
                 { "algorithm", _SD_JSON_VARIANT_TYPE_INVALID, json_dispatch_tpm2_algorithm, offsetof(NvPCRData, algorithm), 0                 },
                 { "nvIndex",   _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint32,      offsetof(NvPCRData, nv_index),  SD_JSON_MANDATORY },
+                { "priority",  _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64,      offsetof(NvPCRData, priority),  0                 },
                 {},
         };
 
         _cleanup_(nvpcr_data_done) NvPCRData p = {
                 .algorithm = TPM2_ALG_SHA256,
+                .priority = TPM2_NVPCR_PRIORITY_DEFAULT,
         };
         r = sd_json_dispatch(v, dispatch_table, SD_JSON_ALLOW_EXTENSIONS, &p);
         if (r < 0)
@@ -7047,7 +7050,7 @@ static int nvpcr_data_load(const char *name, NvPCRData *ret) {
         return 0;
 }
 
-int tpm2_nvpcr_get_index(const char *name, uint32_t *ret) {
+int tpm2_nvpcr_get_index(const char *name, uint32_t *ret_nv_index, uint64_t *ret_priority) {
         int r;
 
         _cleanup_(nvpcr_data_done) NvPCRData p = {};
@@ -7055,8 +7058,10 @@ int tpm2_nvpcr_get_index(const char *name, uint32_t *ret) {
         if (r < 0)
                 return r;
 
-        if (ret)
-                *ret = p.nv_index;
+        if (ret_nv_index)
+                *ret_nv_index = p.nv_index;
+        if (ret_priority)
+                *ret_priority = p.priority;
 
         return 0;
 }
@@ -7748,7 +7753,8 @@ int tpm2_nvpcr_read(
                 const Tpm2Handle *session,
                 const char *name,
                 struct iovec *ret_value,
-                uint32_t *ret_nv_index) {
+                uint32_t *ret_nv_index,
+                uint64_t *ret_priority) {
 
 #if HAVE_OPENSSL
         int r;
@@ -7775,6 +7781,8 @@ int tpm2_nvpcr_read(
                 *ret_value = (struct iovec) {};
                 if (ret_nv_index)
                         *ret_nv_index = p.nv_index;
+                if (ret_priority)
+                        *ret_priority = p.priority;
 
                 return 0;
         }
@@ -7811,6 +7819,8 @@ int tpm2_nvpcr_read(
 
         if (ret_nv_index)
                 *ret_nv_index = p.nv_index;
+        if (ret_priority)
+                *ret_priority = p.priority;
 
         return r;
 #else /* HAVE_OPENSSL */
index f2f63a94a43fc679824092af1f8d380d1ba5d8c4..bf2321a514aab048bff743450d7c9c77c1c6d093 100644 (file)
@@ -158,11 +158,16 @@ typedef enum Tpm2UserspaceEventType {
 DECLARE_STRING_TABLE_LOOKUP(tpm2_userspace_event_type, Tpm2UserspaceEventType);
 
 int tpm2_pcr_extend_bytes(Tpm2Context *c, char **banks, unsigned pcr_index, const struct iovec *data, const struct iovec *secret, Tpm2UserspaceEventType event_type, const char *description);
-int tpm2_nvpcr_get_index(const char *name, uint32_t *ret);
+
+/* Default allocation priority for NvPCRs that do not specify one explicitly. Lower values are more
+ * important and are allocated first when the TPM's NV index space is constrained. */
+#define TPM2_NVPCR_PRIORITY_DEFAULT UINT64_C(1000)
+
+int tpm2_nvpcr_get_index(const char *name, uint32_t *ret_nv_index, uint64_t *ret_priority);
 int tpm2_nvpcr_extend_bytes(Tpm2Context *c, const Tpm2Handle *session, const char *name, const struct iovec *data, const struct iovec *secret, Tpm2UserspaceEventType event_type, const char *description);
 int tpm2_nvpcr_acquire_anchor_secret(struct iovec *ret, bool sync_secondary);
 int tpm2_nvpcr_initialize(Tpm2Context *c, const Tpm2Handle *session, const char *name, const struct iovec *anchor_secret);
-int tpm2_nvpcr_read(Tpm2Context *c, const Tpm2Handle *session, const char *name, struct iovec *ret, uint32_t *ret_nv_index);
+int tpm2_nvpcr_read(Tpm2Context *c, const Tpm2Handle *session, const char *name, struct iovec *ret, uint32_t *ret_nv_index, uint64_t *ret_priority);
 
 uint32_t tpm2_tpms_pcr_selection_to_mask(const TPMS_PCR_SELECTION *s);
 void tpm2_tpms_pcr_selection_from_mask(uint32_t mask, TPMI_ALG_HASH hash, TPMS_PCR_SELECTION *ret);
index bb08e31a81c87b6a474dafcaaf284656608a8b71..ac294f7fae46a2e146803afa1ce5d69a6f1c8438 100644 (file)
@@ -23,6 +23,7 @@
 #include "parse-util.h"
 #include "pretty-print.h"
 #include "set.h"
+#include "sort-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "tmpfile-util.h"
@@ -434,6 +435,26 @@ static int setup_nvpcr_one(
         return 0;
 }
 
+typedef struct NvPCREntry {
+        const char *name;       /* points into the strv 'l' in setup_nvpcr() */
+        uint64_t priority;
+} NvPCREntry;
+
+static int nvpcr_entry_compare(const NvPCREntry *a, const NvPCREntry *b) {
+        int r;
+
+        assert(a);
+        assert(b);
+
+        /* Lower priority value means more important, hence allocate it first. Break ties by name
+         * (ascending) so the resulting order is fully deterministic. */
+        r = CMP(a->priority, b->priority);
+        if (r != 0)
+                return r;
+
+        return strcmp(a->name, b->name);
+}
+
 static int setup_nvpcr(void) {
         _cleanup_(setup_nvpcr_context_done) SetupNvPCRContext c = {};
         int r;
@@ -448,8 +469,33 @@ static int setup_nvpcr(void) {
         if (r < 0)
                 return log_error_errno(r, "Failed to find .nvpcr files: %m");
 
+        /* Pair each NvPCR name with its allocation priority, then sort, so that we allocate the most
+         * important NvPCRs first. This matters when the TPM's NV index space is too small to fit all of
+         * them: we then degrade gracefully, skipping the least important NvPCRs. */
+        size_t n = strv_length(l);
+        _cleanup_free_ NvPCREntry *entries = new(NvPCREntry, n);
+        if (!entries)
+                return log_oom();
+
+        for (size_t i = 0; i < n; i++) {
+                uint64_t priority = TPM2_NVPCR_PRIORITY_DEFAULT;
+
+                r = tpm2_nvpcr_get_index(l[i], /* ret_nv_index= */ NULL, &priority);
+                if (r < 0)
+                        /* If we can't read the definition, assume the default priority and let the
+                         * per-NvPCR error path in setup_nvpcr_one() report the actual problem. */
+                        log_warning_errno(r, "Failed to read priority of NvPCR '%s', assuming default priority %" PRIu64 ": %m", l[i], priority);
+
+                entries[i] = (NvPCREntry) {
+                        .name = l[i],
+                        .priority = priority,
+                };
+        }
+
+        typesafe_qsort(entries, n, nvpcr_entry_compare);
+
         int ret = 0;
-        STRV_FOREACH(i, l) {
+        FOREACH_ARRAY(e, entries, n) {
                 if (!c.tpm2_context) {
                         /* Inability to contact the TPM shall be fatal for us */
                         r = tpm2_context_new_or_warn(arg_tpm2_device, &c.tpm2_context);
@@ -457,8 +503,10 @@ static int setup_nvpcr(void) {
                                 return r;
                 }
 
+                log_debug("Setting up NvPCR '%s' (priority %" PRIu64 ").", e->name, e->priority);
+
                 /* But if we fail to initialize some NvPCR, we go on */
-                RET_GATHER(ret, setup_nvpcr_one(&c, *i));
+                RET_GATHER(ret, setup_nvpcr_one(&c, e->name));
         }
 
         if (c.n_already > 0 && c.n_anchored == 0 && !arg_early)