PCR mismatching is one common cause of TPM key unsealing fail. Since the system may be compromised, it is not safe to boot into OS to get the PCR values and TPM eventlog for the further investigation.
To provide some hints, GRUB now dumps PCRs on policy fail, so the user can check the current PCR values. PCR 0~15 are chosen to cover the firmware, bootloader, and OS. The sample output: PCR Mismatching! Check firmware and bootloader before typing passphrase! TPM PCR [sha256]: 00: 115c89bfa0e59e050cda5d2664031d225305f3582cf0c2afcb7c1f1ac2a7cf8d 01: 079b3eadca25e10248daea4b1d508e5cfb703db28386be809a0b375c0a0a80a5 02: 2cd8ec3de6a07e1fd39676100db57ba62372e820c19812fee55899f65746e192 03: 9423b585d4eac05c97a0c06bca8898ad0ca519a6b810dcb91129bcdc10f4b112 04: fa36bf5c9110d3891f040e2146d157484cd41123fa8faf4bc6b91db3d12b70ca 05: 13e9ea9e38e5258e6ee2b6ae94a3cece0137490ef95c65caaac10cdf5e1bc40d 06: 3ac10d749054a818806788f4e4eaa2fb4dd7d13ce0e99dc175145b63c34bb71c 07: a6657a60f77928cad614a7ad153ab9ae0bed48e33b70348ae11a26762002b3bc 08: 42e04f5bac1965535cb6bdb30c62bb199b1ba21d1ec6b22d0da159dfc925b8bb 09: 5c83e8be79d4a432e6d409610de389ee6f1ac0c193f38d84a9ff94f360bd458b 10: 0000000000000000000000000000000000000000000000000000000000000000 11: 0000000000000000000000000000000000000000000000000000000000000000 12: 0000000000000000000000000000000000000000000000000000000000000000 13: 0000000000000000000000000000000000000000000000000000000000000000 14: 894dd8e4ca1bb62e055f674f9390a39c4643ebdd1014702feef000c47e36a003 15: 0000000000000000000000000000000000000000000000000000000000000000 16: 0000000000000000000000000000000000000000000000000000000000000000 17: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 18: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 19: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 20: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 21: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 22: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 23: 0000000000000000000000000000000000000000000000000000000000000000 error: failed to unseal sealed key (TPM2_Unseal: 0x99d). error: no key protector provided a usable key for luks (af16e48f-746b-4a12-aae1-c14dcee429e0). If the user happens to have the PCR values for key sealing, the PCR dump can be used to identify the changed PCRs and narrow down the scope for closer inspection. Please note that the PCR dump is trustworthy only if the GRUB binary is authentic, so the user has to check the GRUB binary thoroughly before using the PCR dump. Signed-off-by: Gary Lin <g...@suse.com> Reviewed-by: Stefan Berger <stef...@linux.ibm.com> --- .../commands/tpm2_key_protector/module.c | 118 +++++++++++++++++- 1 file changed, 114 insertions(+), 4 deletions(-) diff --git a/grub-core/commands/tpm2_key_protector/module.c b/grub-core/commands/tpm2_key_protector/module.c index 74e79a545..d5e530f77 100644 --- a/grub-core/commands/tpm2_key_protector/module.c +++ b/grub-core/commands/tpm2_key_protector/module.c @@ -790,7 +790,7 @@ tpm2_protector_simple_policy_seq (const tpm2_protector_context_t *ctx, static grub_err_t tpm2_protector_unseal (tpm2key_policy_t policy_seq, TPM_HANDLE_t sealed_handle, - grub_uint8_t **key, grub_size_t *key_size) + grub_uint8_t **key, grub_size_t *key_size, bool *dump_pcr) { TPMS_AUTH_COMMAND_t authCmd = {0}; TPM2B_SENSITIVE_DATA_t data; @@ -801,6 +801,8 @@ tpm2_protector_unseal (tpm2key_policy_t policy_seq, TPM_HANDLE_t sealed_handle, TPM_RC_t rc; grub_err_t err; + *dump_pcr = false; + /* Start Auth Session */ nonceCaller.size = TPM_SHA256_DIGEST_SIZE; symmetric.algorithm = TPM_ALG_NULL; @@ -820,6 +822,13 @@ tpm2_protector_unseal (tpm2key_policy_t policy_seq, TPM_HANDLE_t sealed_handle, rc = grub_tpm2_unseal (sealed_handle, &authCmd, &data, NULL); if (rc != TPM_RC_SUCCESS) { + /* + * Trigger PCR dump on policy fail + * TPM_RC_S (0x800) | TPM_RC_1 (0x100) | RC_FMT (0x80) | TPM_RC_POLICY_FAIL (0x1D) + */ + if (rc == 0x99D) + *dump_pcr = true; + err = grub_error (GRUB_ERR_BAD_DEVICE, "failed to unseal sealed key (TPM2_Unseal: 0x%x)", rc); goto error; } @@ -845,6 +854,91 @@ tpm2_protector_unseal (tpm2key_policy_t policy_seq, TPM_HANDLE_t sealed_handle, return err; } +#define TPM_PCR_STR_SIZE (sizeof (TPMU_HA_t) * 2 + 1) + +static grub_err_t +tpm2_protector_get_pcr_str (const TPM_ALG_ID_t algo, grub_uint32_t index, char *pcr_str, grub_uint16_t buf_size) +{ + TPML_PCR_SELECTION_t pcr_sel = { + .count = 1, + .pcrSelections = { + { + .hash = algo, + .sizeOfSelect = 3, + .pcrSelect = {0} + }, + } + }; + TPML_DIGEST_t digest = {0}; + grub_uint16_t i; + TPM_RC_t rc; + + if (buf_size < TPM_PCR_STR_SIZE) + { + grub_snprintf (pcr_str, buf_size, "insufficient buffer"); + return GRUB_ERR_OUT_OF_MEMORY; + } + + TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0], index); + + rc = grub_tpm2_pcr_read (NULL, &pcr_sel, NULL, NULL, &digest, NULL); + if (rc != TPM_RC_SUCCESS) + { + grub_snprintf (pcr_str, buf_size, "TPM2_PCR_Read: 0x%x", rc); + return GRUB_ERR_BAD_DEVICE; + } + + /* Check the returned digest number and size */ + if (digest.count != 1 || digest.digests[0].size > sizeof (TPMU_HA_t)) + { + grub_snprintf (pcr_str, buf_size, "invalid digest"); + return GRUB_ERR_BAD_DEVICE; + } + + /* Print the digest to the buffer */ + for (i = 0; i < digest.digests[0].size; i++) + grub_snprintf (pcr_str + 2 * i, buf_size - 2 * i, "%02x", digest.digests[0].buffer[i]); + + return GRUB_ERR_NONE; +} + +static void +tpm2_protector_dump_pcr (const TPM_ALG_ID_t bank) +{ + const char *algo_name; + char pcr_str[TPM_PCR_STR_SIZE]; + grub_uint8_t i; + grub_err_t err; + + if (bank == TPM_ALG_SHA1) + algo_name = "sha1"; + else if (bank == TPM_ALG_SHA256) + algo_name = "sha256"; + else if (bank == TPM_ALG_SHA384) + algo_name = "sha384"; + else if (bank == TPM_ALG_SHA512) + algo_name = "sha512"; + else + algo_name = "other"; + + /* Try to fetch PCR 0 */ + err = tpm2_protector_get_pcr_str (bank, 0, pcr_str, sizeof (pcr_str)); + if (err != GRUB_ERR_NONE) + { + grub_printf ("Unsupported PCR bank [%s]: %s\n", algo_name, pcr_str); + return; + } + + grub_printf ("TPM PCR [%s]:\n", algo_name); + + grub_printf (" %02d: %s\n", 0, pcr_str); + for (i = 1; i < TPM_MAX_PCRS; i++) + { + tpm2_protector_get_pcr_str (bank, i, pcr_str, sizeof (pcr_str)); + grub_printf (" %02d: %s\n", i, pcr_str); + } +} + static grub_err_t tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, grub_uint8_t **key, grub_size_t *key_size) @@ -859,6 +953,7 @@ tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, tpm2key_policy_t policy_seq = NULL; tpm2key_authpolicy_t authpol = NULL; tpm2key_authpolicy_t authpol_seq = NULL; + bool dump_pcr = false; grub_err_t err; /* @@ -924,7 +1019,7 @@ tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, /* Iterate the authpolicy sequence to find one that unseals the key */ FOR_LIST_ELEMENTS (authpol, authpol_seq) { - err = tpm2_protector_unseal (authpol->policy_seq, sealed_handle, key, key_size); + err = tpm2_protector_unseal (authpol->policy_seq, sealed_handle, key, key_size, &dump_pcr); if (err == GRUB_ERR_NONE) break; @@ -952,13 +1047,20 @@ tpm2_protector_srk_recover (const tpm2_protector_context_t *ctx, goto exit2; } - err = tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size); + err = tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size, &dump_pcr); } /* Pop error messages on success */ if (err == GRUB_ERR_NONE) while (grub_error_pop ()); + /* Dump PCRs if necessary */ + if (dump_pcr == true) + { + grub_printf ("PCR Mismatch! Check firmware and bootloader before typing passphrase!\n"); + tpm2_protector_dump_pcr (ctx->bank); + } + exit2: grub_tpm2_flushcontext (sealed_handle); @@ -978,6 +1080,7 @@ tpm2_protector_nv_recover (const tpm2_protector_context_t *ctx, { TPM_HANDLE_t sealed_handle = ctx->nv; tpm2key_policy_t policy_seq = NULL; + bool dump_pcr = false; grub_err_t err; /* Create a basic policy sequence based on the given PCR selection */ @@ -985,7 +1088,14 @@ tpm2_protector_nv_recover (const tpm2_protector_context_t *ctx, if (err != GRUB_ERR_NONE) goto exit; - err = tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size); + err = tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size, &dump_pcr); + + /* Dump PCRs if necessary */ + if (dump_pcr == true) + { + grub_printf ("PCR Mismatch! Check firmware and bootloader before typing passphrase!\n"); + tpm2_protector_dump_pcr (ctx->bank); + } exit: grub_tpm2_flushcontext (sealed_handle); -- 2.43.0 _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel