On Tue, Dec 17, 2024 at 09:35:34AM +0800, Gary Lin wrote:
> On Mon, Dec 16, 2024 at 05:28:34PM +0100, Daniel Kiper wrote:
> > On Thu, Dec 12, 2024 at 02:11:24PM +0800, Gary Lin wrote:
> > > 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
> > > 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 you do this why do not add also a GRUB command to dump all PCRs,
> > including DRTM ones.
> > 
> Sure, a new command would be helpful to inspect PCRs with GRUB shell.
> I'll add the command in v3.
> 
There is one problem with the command. Since GRUB always measures the
commands into PCR 8, so the PCR dump command may also affect PCR 8 and
the user may never get a stable PCR 8.

Gary Lin

> > > 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      | 101 +++++++++++++++++-
> > >  1 file changed, 97 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..827f946ba 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,80 @@ 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 void
> > > +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;
> > > +    }
> > > +
> > > +  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;
> > > +    }
> > > +
> > > +  /* 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;
> > > +    }
> > > +
> > > +  /* 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]);
> > > +}
> > > +
> > > +static void
> > > +tpm2_protector_dump_pcr (const tpm2_protector_context_t *ctx)
> > > +{
> > > +  const char *algo_name;
> > > +  char pcr_str[TPM_PCR_STR_SIZE];
> > > +  grub_uint8_t i;
> > > +
> > > +  if (ctx->bank == TPM_ALG_SHA1)
> > > +    algo_name = "sha1";
> > > +  else if (ctx->bank == TPM_ALG_SHA256)
> > > +    algo_name = "sha256";
> > > +  else if (ctx->bank == TPM_ALG_SHA384)
> > > +    algo_name = "sha384";
> > > +  else if (ctx->bank == TPM_ALG_SHA512)
> > > +    algo_name = "sha512";
> > > +  else
> > > +    algo_name = "other";
> > > +
> > > +  grub_printf ("PCR Mismatching! Check firmware and bootloader before 
> > > typing passphrase!\nTPM PCR [%s]:\n", algo_name);
> > 
> > This grub_printf() does not belong to this function. It should
> > go to the tpm2_protector_unseal().
> > 
> There may be several policies in the policy sequence, so I try to
> suppress the message until all policies fail.
> 
> > s/PCR Mismatching/PCR Mismatch/
> > 
> Will fix it in v3.
> 
> > > +  /* Print PCR 0~15 to cover Static Root of Trust Measurement (SRTM) */
> > > +  for (i = 0; i <= 15; i++)
> > 
> > I would dump all PCRs including DRTM ones.
> > 
> Okay, will make it dump all PCRs.
> 
> > > +    {
> > > +      tpm2_protector_get_pcr_str (ctx->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 +942,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 +1008,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 +1036,17 @@ 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)
> > 
> > Even if it works I think we should use constants if they are defined. So,
> >   if (dump_pcr == true)
> >
> Will fix it in v3.
>  
> Gary Lin
> 
> > > +    tpm2_protector_dump_pcr (ctx);
> > > +
> > >   exit2:
> > >    grub_tpm2_flushcontext (sealed_handle);
> > >
> > > @@ -978,6 +1066,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 +1074,11 @@ 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)
> > 
> > Ditto.
> > 
> > > +    tpm2_protector_dump_pcr (ctx);
> > >
> > >   exit:
> > >    grub_tpm2_flushcontext (sealed_handle);
> > 
> > Daniel

_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to