#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
#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
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... */
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;