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 Mismatch! Check firmware and bootloader before typing passphrase!
TPM PCR [sha256]:
  00: 17401f37710984c1d8a03a81fff3ab567ae9291bac61e21715b890ee28879738
  01: 7a114329ba388445a96e8db2a072785937c1b7a8803ed7cc682b87f3ff3dd7a8
  02: 11c2776849e8e24b7d80c926cbc4257871bffa744dadfefd3ed049ce25143e05
  03: 6c33b362073e28e30b47302bbdd3e6f9cee4debca3a304e646f8c68245724350
  04: 62d38838483ecfd2484ee3a2e5450d8ca3b35fc72cda6a8c620f9f43521c37d1
  05: d8a85cb37221ab7d1f2cc5f554dbe0463acb6784b5b8dc3164ccaa66d8fff0e1
  06: 9262e37cbe71ed4daf815b4a4881fb7251c9d371092dde827557d5368121e10e
  07: 219d542233be492d62b079ffe46cf13396a8c27e520e88b08eaf2e6d3b7e70f5
  08: de1f61c973b673e505adebe0d7e8fb65fde6c24dd4ab4fbaff9e28b18df6ecd3
  09: c1de7274fa3e879a16d7e6e7629e3463d95f68adcfd17c477183846dccc41c89
  10: 0000000000000000000000000000000000000000000000000000000000000000
  11: 0000000000000000000000000000000000000000000000000000000000000000
  12: 0000000000000000000000000000000000000000000000000000000000000000
  13: 0000000000000000000000000000000000000000000000000000000000000000
  14: 9ab9ebe4879a7f4dd00c04f37e79cfd69d0dd7a8bcc6b01135525b67676a3e40
  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>
Reviewed-by: Daniel Kiper <daniel.ki...@oracle.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

Reply via email to