From: Lennart Poettering Date: Wed, 12 Nov 2025 22:34:52 +0000 (+0100) Subject: tpm2-util: use LoaderTpm2ActivePcrBanks efi var when figuring out best+good banks... X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=7643e4a89ca12049e65ad139160c7b762e060ee2;p=thirdparty%2Fsystemd.git tpm2-util: use LoaderTpm2ActivePcrBanks efi var when figuring out best+good banks to use We nowadays have clear reporting which PCR banks the firmware is using via LoaderTpm2ActivePcrBanks, hence rely on that. --- diff --git a/src/shared/efi-api.c b/src/shared/efi-api.c index bbc410ca50f..608001b6e2d 100644 --- a/src/shared/efi-api.c +++ b/src/shared/efi-api.c @@ -15,8 +15,14 @@ #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 diff --git a/src/shared/efi-api.h b/src/shared/efi-api.h index 6fb3efb88a9..98c55f8a38f 100644 --- a/src/shared/efi-api.h +++ b/src/shared/efi-api.h @@ -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); diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 1603b5f3169..de7e3d9728c 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -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;