All of lore.kernel.org
 help / color / mirror / Atom feed
From: Gary Lin via Grub-devel <grub-devel@gnu.org>
To: The development of GNU GRUB <grub-devel@gnu.org>
Cc: Gary Lin <glin@suse.com>, Daniel Kiper <daniel.kiper@oracle.com>,
	mchang@suse.com, patrick.colp@oracle.com,
	Stefan Berger <stefanb@linux.ibm.com>,
	jejb@linux.ibm.com, Glenn Washburn <development@efficientek.com>
Subject: [PATCH v4 01/12] tpm2_key_protector: dump PCRs on policy fail
Date: Fri, 21 Mar 2025 15:58:57 +0800	[thread overview]
Message-ID: <20250321075908.10523-2-glin@suse.com> (raw)
In-Reply-To: <20250321075908.10523-1-glin@suse.com>

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 <glin@suse.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Daniel Kiper <daniel.kiper@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	other threads:[~2025-03-21  7:59 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-03-21  7:58 [PATCH v4 00/12] TPM2 key protector follow-up patches Gary Lin via Grub-devel
2025-03-21  7:58 ` Gary Lin via Grub-devel [this message]
2025-03-21  7:58 ` [PATCH v4 02/12] tpm2_key_protector: Add 'tpm2_dump_pcr' command Gary Lin via Grub-devel
2025-03-21  7:58 ` [PATCH v4 03/12] tss2: Fix the missing authCommand Gary Lin via Grub-devel
2025-03-21  7:59 ` [PATCH v4 04/12] tss2: Add TPM 2.0 NV index commands Gary Lin via Grub-devel
2025-03-21  7:59 ` [PATCH v4 05/12] tpm2_key_protector: Unseal key from a buffer Gary Lin via Grub-devel
2025-03-25 16:01   ` Daniel Kiper via Grub-devel
2025-03-26  7:54     ` Gary Lin via Grub-devel
2025-03-21  7:59 ` [PATCH v4 06/12] tpm2_key_protector: Support NV index handles Gary Lin via Grub-devel
2025-03-21  7:59 ` [PATCH v4 07/12] util/grub-protect: Support NV index mode Gary Lin via Grub-devel
2025-03-26 16:14   ` Daniel Kiper via Grub-devel
2025-03-21  7:59 ` [PATCH v4 08/12] tests/tpm2_key_protector_test: Simplify the NV index mode test Gary Lin via Grub-devel
2025-03-24 14:21   ` Stefan Berger
2025-03-26 16:16   ` Daniel Kiper via Grub-devel
2025-03-21  7:59 ` [PATCH v4 09/12] tests/tpm2_key_protector_test: Reset 'ret' on fail Gary Lin via Grub-devel
2025-03-24 13:48   ` Stefan Berger
2025-03-24 14:29   ` Vladimir 'phcoder' Serbinenko
2025-03-24 14:35     ` Stefan Berger
2025-03-25  7:18       ` Gary Lin via Grub-devel
2025-03-21  7:59 ` [PATCH v4 10/12] tests/tpm2_key_protector_test: Add more NV index mode tests Gary Lin via Grub-devel
2025-03-24 14:19   ` Stefan Berger
2025-03-21  7:59 ` [PATCH v4 11/12] docs: Update NV index mode of TPM2 key protector Gary Lin via Grub-devel
2025-03-21  7:59 ` [PATCH v4 12/12] INSTALL: Document the packages needed for TPM2 key protector tests Gary Lin via Grub-devel
2025-03-26 16:19   ` Daniel Kiper via Grub-devel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250321075908.10523-2-glin@suse.com \
    --to=grub-devel@gnu.org \
    --cc=daniel.kiper@oracle.com \
    --cc=development@efficientek.com \
    --cc=glin@suse.com \
    --cc=jejb@linux.ibm.com \
    --cc=mchang@suse.com \
    --cc=patrick.colp@oracle.com \
    --cc=stefanb@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.