]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tpm2-util: use LoaderTpm2ActivePcrBanks efi var when figuring out best+good banks...
authorLennart Poettering <lennart@poettering.net>
Wed, 12 Nov 2025 22:34:52 +0000 (23:34 +0100)
committerLennart Poettering <lennart@poettering.net>
Fri, 14 Nov 2025 21:22:39 +0000 (22:22 +0100)
We nowadays have clear reporting which PCR banks the firmware is using
via LoaderTpm2ActivePcrBanks, hence rely on that.

src/shared/efi-api.c
src/shared/efi-api.h
src/shared/tpm2-util.c

index bbc410ca50fbd59a22ac9bd95a03ebc5d7790ea8..608001b6e2d158e415829f15d6f9453c3433de0c 100644 (file)
 #include "stat-util.h"
 #include "stdio-util.h"
 #include "string-util.h"
+#include "tpm2-util.h"
 #include "utf8.h"
 
+#define EFI_TCG2_BOOT_HASH_ALG_SHA1   0x01
+#define EFI_TCG2_BOOT_HASH_ALG_SHA256 0x02
+#define EFI_TCG2_BOOT_HASH_ALG_SHA384 0x04
+#define EFI_TCG2_BOOT_HASH_ALG_SHA512 0x08
+
 #define LOAD_OPTION_ACTIVE            0x00000001
 #define MEDIA_DEVICE_PATH                   0x04
 #define MEDIA_HARDDRIVE_DP                  0x01
@@ -517,24 +523,59 @@ int efi_get_boot_options(uint16_t **ret_options) {
 #endif
 }
 
+int efi_get_active_pcr_banks(uint32_t *ret) {
 #if ENABLE_EFI
-static int loader_has_tpm2(void) {
-        _cleanup_free_ char *active_pcr_banks = NULL;
-        uint32_t active_pcr_banks_value;
+        static uint32_t cache = UINT32_MAX;
         int r;
 
-        r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderTpm2ActivePcrBanks"), &active_pcr_banks);
-        if (r < 0) {
-                if (r != -ENOENT)
-                        log_debug_errno(r, "Failed to read LoaderTpm2ActivePcrBanks variable: %m");
-                return r;
+        if (cache == UINT32_MAX) {
+                _cleanup_free_ char *active_pcr_banks = NULL;
+                r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderTpm2ActivePcrBanks"), &active_pcr_banks);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to read LoaderTpm2ActivePcrBanks variable: %m");
+
+                uint32_t efi_bits;
+                r = safe_atou32_full(active_pcr_banks, 16, &efi_bits);
+                if (r < 0)
+                        return log_debug_errno(r, "Failed to parse LoaderTpm2ActivePcrBanks variable: %m");
+
+                /* EFI TPM protocol uses different bit values for the hash algorithms, let's convert */
+                static const struct {
+                        uint32_t efi;
+                        uint32_t tcg;
+                } table[] = {
+                        { EFI_TCG2_BOOT_HASH_ALG_SHA1,   1U << TPM2_ALG_SHA1   },
+                        { EFI_TCG2_BOOT_HASH_ALG_SHA256, 1U << TPM2_ALG_SHA256 },
+                        { EFI_TCG2_BOOT_HASH_ALG_SHA384, 1U << TPM2_ALG_SHA384 },
+                        { EFI_TCG2_BOOT_HASH_ALG_SHA512, 1U << TPM2_ALG_SHA512 },
+                };
+
+                uint32_t tcg_bits = 0;
+                FOREACH_ELEMENT(t, table)
+                        SET_FLAG(tcg_bits, t->tcg, efi_bits & t->efi);
+
+                cache = tcg_bits;
         }
 
-        r = safe_atou32_full(active_pcr_banks, 16, &active_pcr_banks_value);
+        if (ret)
+                *ret = cache;
+
+        return 0;
+#else
+        return -EOPNOTSUPP;
+#endif
+}
+
+#if ENABLE_EFI
+static int loader_has_tpm2(void) {
+        uint32_t active_pcr_banks;
+        int r;
+
+        r = efi_get_active_pcr_banks(&active_pcr_banks);
         if (r < 0)
-                return log_debug_errno(r, "Failed to parse LoaderTpm2ActivePcrBanks variable: %m");
+                return r;
 
-        return active_pcr_banks_value != 0;
+        return active_pcr_banks != 0;
 }
 #endif
 
index 6fb3efb88a9c36923641afbd64d10de43ce57789..98c55f8a38fb51b455e857c2aac36fabd5f11506 100644 (file)
@@ -18,6 +18,7 @@ int efi_get_boot_order(uint16_t **ret_order);
 int efi_set_boot_order(const uint16_t *order, size_t n);
 int efi_get_boot_options(uint16_t **ret_options);
 
+int efi_get_active_pcr_banks(uint32_t *ret);
 bool efi_has_tpm2(void);
 
 sd_id128_t efi_guid_to_id128(const void *guid);
index 1603b5f3169e3495b47b8c25b6f69a174a4cf955..de7e3d9728c01557d559bb5cff6ac9aa3d51eb6d 100644 (file)
@@ -2697,6 +2697,26 @@ int tpm2_get_best_pcr_bank(
         assert(c);
         assert(ret);
 
+        uint32_t efi_banks;
+        r = efi_get_active_pcr_banks(&efi_banks);
+        if (r < 0) {
+                if (r != -ENOENT)
+                        return r;
+
+                /* If variable is not set use guesswork below */
+                log_debug("Boot loader didn't set the LoaderTpm2ActivePcrBanks EFI variable, we have to guess the used PCR banks.");
+        } else {
+                if (BIT_SET(efi_banks, TPM2_ALG_SHA256))
+                        *ret = TPM2_ALG_SHA256;
+                else if (BIT_SET(efi_banks, TPM2_ALG_SHA1))
+                        *ret = TPM2_ALG_SHA1;
+                else
+                        return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Firmware reports neither SHA1 nor SHA256 PCR banks, cannot operate.");
+
+                log_debug("Picked best PCR bank %s based on firmware reported banks.", tpm2_hash_alg_to_string(*ret));
+                return 0;
+        }
+
         if (pcr_mask == 0) {
                 log_debug("Asked to pick best PCR bank but no PCRs selected we could derive this from. Defaulting to SHA256.");
                 *ret = TPM2_ALG_SHA256; /* if no PCRs are selected this doesn't matter anyway... */
@@ -2784,6 +2804,30 @@ int tpm2_get_good_pcr_banks(
         assert(c);
         assert(ret);
 
+        uint32_t efi_banks;
+        r = efi_get_active_pcr_banks(&efi_banks);
+        if (r < 0) {
+                if (r != -ENOENT)
+                        return r;
+
+                /* If the variable is not set we have to guess via the code below */
+                log_debug("Boot loader didn't set the LoaderTpm2ActivePcrBanks EFI variable, we have to guess the used PCR banks.");
+        } else {
+                FOREACH_ARRAY(hash, tpm2_hash_algorithms, TPM2_N_HASH_ALGORITHMS) {
+                        if (!BIT_SET(efi_banks, *hash))
+                                continue;
+
+                        if (!GREEDY_REALLOC(good_banks, n_good_banks+1))
+                                return log_oom_debug();
+
+                        good_banks[n_good_banks++] = *hash;
+                }
+
+                log_debug("Found %zu initialized TPM2 banks reported by firmware.", n_good_banks);
+                *ret = TAKE_PTR(good_banks);
+                return (int) n_good_banks;
+        }
+
         FOREACH_TPMS_PCR_SELECTION_IN_TPML_PCR_SELECTION(selection, &c->capability_pcrs) {
                 TPMI_ALG_HASH hash = selection->hash;