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.
This commit is contained in:
Lennart Poettering
2025-11-12 23:34:52 +01:00
parent 867e64737a
commit 7643e4a89c
3 changed files with 98 additions and 12 deletions

View File

@@ -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 (r < 0)
return log_debug_errno(r, "Failed to parse LoaderTpm2ActivePcrBanks variable: %m");
if (ret)
*ret = cache;
return active_pcr_banks_value != 0;
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 r;
return active_pcr_banks != 0;
}
#endif

View 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);

View 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;