From: Lennart Poettering Date: Wed, 12 Nov 2025 21:35:30 +0000 (+0100) Subject: tpm2-setup: measure information about NvPCR initialization to PCR 9 X-Git-Tag: v259-rc1~48^2~7 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d70296bb563338f59d3be998a3049ad74ebd7a32;p=thirdparty%2Fsystemd.git tpm2-setup: measure information about NvPCR initialization to PCR 9 This locks down NvPCR initilization a bit more: we'll measure each initialization of an NvPCR into PCR 9, thus chaining the NvPCRs to the PCR set. After all NvPCRs are initialized we measure a barrier into PCR 9 as well. This ensures that later additions of NvPCRs are clearly recognizable and distuingishable from those done at boot. --- diff --git a/docs/TPM2_PCR_MEASUREMENTS.md b/docs/TPM2_PCR_MEASUREMENTS.md index abd280c0048..7b29069a7ea 100644 --- a/docs/TPM2_PCR_MEASUREMENTS.md +++ b/docs/TPM2_PCR_MEASUREMENTS.md @@ -199,6 +199,22 @@ initrd" in UTF-16. → **Measured hash** covers the per-UKI sysext cpio archive (which is generated on-the-fly by `systemd-stub`). +## PCR Measurements Made by `systemd-tpm2-setup` (Userspace) + +### PCR 9, NvPCR Initializations + +The `systemd-tpm2-setup.service` service initializes any NvPCRs defined via +`*.nvpcr` files. For each initialized NvPCR it will measure an event into PCR +9. + +→ **Measured hash** covers the string `nvpcr-init:`, suffixed by the NvPCR +name, suffixed by `:0x`, suffixed by the NV Index handle (formatted in +hexadecimal), suffixed by a colon, suffixed by the hash function used, in +lowercase (i.e. `sha256` or so), suffixed by a colon, and finally suffixed by +the state of the NvPCR after its initialization with the anchor measurement, in +hexadecimal. Example: +`nvpcr-init:hardware:0x1d10200:sha256:de3857f637c61e82f02e3722e1b207585fe9711045d863238904be8db10683f2` + ## PCR/NvPCR Measurements Made by `systemd-pcrextend` (Userspace) ### PCR 11, boot phases diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 356c3e4909e..ad61fd1d8bd 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -6426,6 +6426,7 @@ static const char* tpm2_userspace_event_type_table[_TPM2_USERSPACE_EVENT_TYPE_MA [TPM2_EVENT_MACHINE_ID] = "machine-id", [TPM2_EVENT_PRODUCT_ID] = "product-id", [TPM2_EVENT_KEYSLOT] = "keyslot", + [TPM2_EVENT_NVPCR_INIT] = "nvpcr-init", }; DEFINE_STRING_TABLE_LOOKUP(tpm2_userspace_event_type, Tpm2UserspaceEventType); @@ -7368,6 +7369,37 @@ int tpm2_nvpcr_initialize( return log_debug_errno(r, "Failed to write anchor file: %m"); tpm2_userspace_log_clean(log_fd); + log_fd = safe_close(log_fd); + + /* Now also measure the initialization into PCR 9, so that there's a trace of it in regular PCRs. You + * might wonder why PCR 9? Well, we have very few PCRs available, and PCR 9 appears to be the least + * bad for this. It typically contains stuff that in our world is hard to predict anyway + * (i.e. possibly some overly verbose Grub stuff, as well as all initrds – those generated on-the-fly + * and those prepared beforehand – mangled into one), quite differently from all other PCRs we could + * use. Moreover PCR 11 already contains most stuff from PCR 9, as it contains the same data + * (i.e. initrds) in a more sensible fashion, clearly separated from on-the-fly generated ones. Note + * that we only do all this measurement stuff if we are booted as UKI, and hence when PCR 11 is + * available, but PCR 9 is not predictable. */ + _cleanup_strv_free_ char **banks = NULL; + r = tpm2_get_good_pcr_banks_strv(c, UINT32_C(1) << TPM2_PCR_KERNEL_INITRD, &banks); + if (r < 0) + return log_error_errno(r, "Could not verify PCR banks: %m"); + + _cleanup_free_ char *word = NULL; + if (asprintf(&word, "nvpcr-init:%s:0x%x:%s:%s", name, p.nv_index, tpm2_hash_alg_to_string(p.algorithm), h) < 0) + return log_oom(); + + r = tpm2_pcr_extend_bytes( + c, + banks, + TPM2_PCR_KERNEL_INITRD, + &IOVEC_MAKE_STRING(word), + /* secret= */ NULL, + TPM2_EVENT_NVPCR_INIT, + word); + if (r < 0) + return log_error_errno(r, "Could not extend PCR %i: %m", TPM2_PCR_KERNEL_INITRD); + return 1; #else /* HAVE_OPENSSL */ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "OpenSSL support is disabled."); diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index abf8adfdaf2..8dfe87af070 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -144,6 +144,7 @@ typedef enum Tpm2UserspaceEventType { TPM2_EVENT_MACHINE_ID, TPM2_EVENT_PRODUCT_ID, TPM2_EVENT_KEYSLOT, + TPM2_EVENT_NVPCR_INIT, _TPM2_USERSPACE_EVENT_TYPE_MAX, _TPM2_USERSPACE_EVENT_TYPE_INVALID = -EINVAL, } Tpm2UserspaceEventType;