[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH v2 08/10] util/grub-protect: Support NV index mode
From: |
Gary Lin |
Subject: |
Re: [PATCH v2 08/10] util/grub-protect: Support NV index mode |
Date: |
Fri, 10 Jan 2025 15:28:05 +0800 |
On Thu, Jan 09, 2025 at 02:03:47PM -0500, Stefan Berger wrote:
>
>
> On 1/8/25 10:58 PM, Gary Lin wrote:
> > This commit implements the missing NV index mode support in
> > 'grub-protect'. NV index mode stores the sealed key in the TPM
> > non-volatile memory (NVRAM) instead of a file. There are two supported
> > types of TPM handles.
> >
> > 1. Persistent handle (0x81000000~0x81FFFFFF)
> > TPM 2.0 Key File format (--tpm2key) is not supported due to the
> > limitation of persistent handles. This 'grub-protect' command
> > seals the key into the persistent handle 0x81000000.
> >
> > # grub-protect \
> > --protector=tpm2 \
> > --action=add \
> > --tpm2-bank=sha256 \
> > --tpm2-pcrs=7,11 \
> > --tpm2-keyfile=luks-key \
> > --tpm2-nvindex=0x81000000
> >
> > 2. NV index handle (0x1000000~0x1FFFFFF)
> > Both TPM 2.0 Key File format and the raw format are supported by NV
> > index handles. Here is the 'grub-protect' command to seal the key in
> > TPM 2.0 Key File format into the NV index handle 0x1000000.
> >
> > # grub-protect \
> > --protector=tpm2 \
> > --action=add \
> > --tpm2key \
> > --tpm2-bank=sha256 \
> > --tpm2-pcrs=7,11 \
> > --tpm2-keyfile=luks-key \
> > --tpm2-nvindex=0x1000000
> >
> > Besides the 'add' action, the corresponding 'remove' action is also
> > introduced. To remove the data from a persistent or NV index handle,
> > just use '--tpm2-nvindex=HANDLE' combining with '--tpm2-evict'. This
> > sample command removes the data from the NV index handle 0x1000000.
> >
> > # grub-protect \
> > --protector=tpm2 \
> > --action=remove \
> > --tpm2-evict \
> > --tpm2-nvindex=0x1000000
> >
> > Signed-off-by: Gary Lin <glin@suse.com>
> > ---
> > util/grub-protect.c | 343 ++++++++++++++++++++++++++++++++++++--------
> > 1 file changed, 287 insertions(+), 56 deletions(-)
> >
> > diff --git a/util/grub-protect.c b/util/grub-protect.c
> > index 5b7e952f4..6219d24a2 100644
> > --- a/util/grub-protect.c
> > +++ b/util/grub-protect.c
> > @@ -61,7 +61,8 @@ typedef enum protect_opt
> > PROTECT_OPT_TPM2_KEYFILE,
> > PROTECT_OPT_TPM2_OUTFILE,
> > PROTECT_OPT_TPM2_EVICT,
> > - PROTECT_OPT_TPM2_TPM2KEY
> > + PROTECT_OPT_TPM2_TPM2KEY,
> > + PROTECT_OPT_TPM2_NVINDEX,
> > } protect_opt_t;
> > /* Option flags to keep track of specified arguments */
> > @@ -79,7 +80,8 @@ typedef enum protect_arg
> > PROTECT_ARG_TPM2_KEYFILE = 1 << 7,
> > PROTECT_ARG_TPM2_OUTFILE = 1 << 8,
> > PROTECT_ARG_TPM2_EVICT = 1 << 9,
> > - PROTECT_ARG_TPM2_TPM2KEY = 1 << 10
> > + PROTECT_ARG_TPM2_TPM2KEY = 1 << 10,
> > + PROTECT_ARG_TPM2_NVINDEX = 1 << 11
> > } protect_arg_t;
> > typedef enum protect_protector
> > @@ -111,6 +113,7 @@ typedef struct protect_args
> > const char *tpm2_outfile;
> > bool tpm2_evict;
> > bool tpm2_tpm2key;
> > + TPM_HANDLE_t tpm2_nvindex;
> > } protect_args_t;
> > static struct argp_option protect_options[] =
> > @@ -224,6 +227,15 @@ static struct argp_option protect_options[] =
> > N_("Use TPM 2.0 Key File format."),
> > .group = 0
> > },
> > + {
> > + .name = "tpm2-nvindex",
> > + .key = PROTECT_OPT_TPM2_NVINDEX,
> > + .arg = "NUM",
> > + .flags = 0,
> > + .doc =
> > + N_("Store the sealed key in a persistent or NV index handle."),
> > + .group = 0
> > + },
> > /* End of list */
> > { 0, 0, 0, 0, 0, 0 }
> > };
> > @@ -668,8 +680,8 @@ extern asn1_static_node tpm2key_asn1_tab[];
> > #define TPM2KEY_SEALED_KEY_OID "2.23.133.10.1.5"
> > static grub_err_t
> > -protect_tpm2_export_tpm2key (const protect_args_t *args,
> > - tpm2_sealed_key_t *sealed_key)
> > +protect_tpm2_export_tpm2key (const protect_args_t *args, tpm2_sealed_key_t
> > *sealed_key,
> > + void **der_buf, int *der_buf_size)
> > {
> > const char *sealed_key_oid = TPM2KEY_SEALED_KEY_OID;
> > asn1_node asn1_def = NULL;
> > @@ -689,12 +701,13 @@ protect_tpm2_export_tpm2key (const protect_args_t
> > *args,
> > };
> > struct grub_tpm2_buffer pub_buf;
> > struct grub_tpm2_buffer priv_buf;
> > - void *der_buf = NULL;
> > - int der_buf_size = 0;
> > int i;
> > int ret;
> > grub_err_t err;
> > + if (der_buf == NULL)
> > + return GRUB_ERR_BAD_ARGUMENT;
> > +
> > for (i = 0; i < args->tpm2_pcr_count; i++)
> > TPMS_PCR_SELECTION_SelectPCR (&pcr_sel.pcrSelections[0],
> > args->tpm2_pcrs[i]);
> > @@ -844,8 +857,8 @@ protect_tpm2_export_tpm2key (const protect_args_t *args,
> > }
> > /* Create the DER binary */
> > - der_buf_size = 0;
> > - ret = asn1_der_coding (tpm2key, "", NULL, &der_buf_size, NULL);
> > + *der_buf_size = 0;
> > + ret = asn1_der_coding (tpm2key, "", NULL, der_buf_size, NULL);
> > if (ret != ASN1_MEM_ERROR)
> > {
> > fprintf (stderr, "Failed to get DER size: 0x%x\n", ret);
> > @@ -853,15 +866,15 @@ protect_tpm2_export_tpm2key (const protect_args_t
> > *args,
> > goto error;
> > }
> > - der_buf = grub_malloc (der_buf_size);
> > - if (der_buf == NULL)
> > + *der_buf = grub_malloc (*der_buf_size);
> > + if (*der_buf == NULL)
> > {
> > fprintf (stderr, "Failed to allocate memory for DER encoding\n");
> > err = GRUB_ERR_OUT_OF_MEMORY;
> > goto error;
> > }
> > - ret = asn1_der_coding (tpm2key, "", der_buf, &der_buf_size, NULL);
> > + ret = asn1_der_coding (tpm2key, "", *der_buf, der_buf_size, NULL);
> > if (ret != ASN1_SUCCESS)
> > {
> > fprintf (stderr, "DER coding error: 0x%x\n", ret);
> > @@ -869,13 +882,7 @@ protect_tpm2_export_tpm2key (const protect_args_t
> > *args,
> > goto error;
> > }
> > - err = protect_write_file (args->tpm2_outfile, der_buf, der_buf_size);
> > - if (err != GRUB_ERR_NONE)
> > - fprintf (stderr, N_("Could not write tpm2key file (%s).\n"), strerror
> > (errno));
> > -
> > error:
> > - grub_free (der_buf);
> > -
> > if (tpm2key)
> > asn1_delete_structure (&tpm2key);
> > @@ -883,10 +890,8 @@ protect_tpm2_export_tpm2key (const protect_args_t
> > *args,
> > }
> > static grub_err_t
> > -protect_tpm2_export_sealed_key (const char *filepath,
> > - tpm2_sealed_key_t *sealed_key)
> > +protect_tpm2_export_raw (tpm2_sealed_key_t *sealed_key, void **out_buf,
> > int *out_buf_size)
> > {
> > - grub_err_t err;
> > struct grub_tpm2_buffer buf;
> > grub_tpm2_buffer_init (&buf);
> > @@ -896,13 +901,98 @@ protect_tpm2_export_sealed_key (const char *filepath,
> > if (buf.error != 0)
> > return GRUB_ERR_BAD_ARGUMENT;
> > - err = protect_write_file (filepath, buf.data, buf.size);
> > - if (err != GRUB_ERR_NONE)
> > - fprintf (stderr, N_("Could not write sealed key file (%s).\n"),
> > strerror (errno));
> > + *out_buf_size = buf.size;
> > + *out_buf = grub_malloc (buf.size);
> > +
> > + if (*out_buf == NULL)
> > + {
> > + fprintf (stderr, N_("Could not allocate memory for the raw format
> > key.\n"));
> > + return GRUB_ERR_OUT_OF_MEMORY;
> > + }
> > +
> > + grub_memcpy (*out_buf, buf.data, buf.size);
> > +
> > + return GRUB_ERR_NONE;
> > +}
> > +
> > +static grub_err_t
> > +protect_tpm2_export_persistent (protect_args_t *args,
> > + TPM_HANDLE_t srk_handle,
> > + tpm2_sealed_key_t *sealed_key)
> > +{
> > + TPMS_AUTH_COMMAND_t authCmd = {0};
> > + TPM2B_NAME_t name = {0};
> > + TPM_HANDLE_t sealed_handle;
> > + TPM_RC_t rc;
> > + grub_err_t err = GRUB_ERR_NONE;
> > +
> > + /* Load the sealed key and associate it with the SRK */
> > + authCmd.sessionHandle = TPM_RS_PW;
> > + rc = grub_tpm2_load (srk_handle, &authCmd, &sealed_key->private,
> > &sealed_key->public,
> > + &sealed_handle, &name, NULL);
> > + if (rc != TPM_RC_SUCCESS)
> > + {
> > + fprintf (stderr, "Failed to load sealed key (TPM2_Load: %x).\n", rc);
> > + return GRUB_ERR_BAD_DEVICE;
> > + }
> > +
> > + /* Make the sealed key object persistent */
> > + authCmd.sessionHandle = TPM_RS_PW;
> > + rc = grub_tpm2_evictcontrol (TPM_RH_OWNER, sealed_handle, &authCmd,
> > args->tpm2_nvindex, NULL);
> > + if (rc != TPM_RC_SUCCESS)
> > + {
> > + fprintf (stderr, "Failed to make sealed key persistent with handle
> > 0x%x (TPM2_EvictControl: 0x%x).\n", args->tpm2_nvindex, rc);
> > + err = GRUB_ERR_BAD_DEVICE;
> > + goto exit;
> > + }
> > +
> > + exit:
> > + grub_tpm2_flushcontext (sealed_handle);
> > return err;
> > }
> > +static grub_err_t
> > +protect_tpm2_export_nvindex (protect_args_t *args, void *data, int
> > data_size)
> > +{
> > + TPMS_AUTH_COMMAND_t authCmd = {0};
> > + TPM2B_NV_PUBLIC_t pub_info = {0};
> > + TPM2B_MAX_NV_BUFFER_t nv_data = {0};
> > + TPM_RC_t rc;
> > +
> > + if (data_size > TPM_MAX_NV_BUFFER_SIZE || data_size < 0)
> > + {
> > + fprintf (stderr, N_("Invalid tpm2key size for TPM NV buffer\n"));
> > + return GRUB_ERR_OUT_OF_RANGE;
> > + }
> > +
> > + pub_info.nvPublic.nvIndex = args->tpm2_nvindex;
> > + pub_info.nvPublic.nameAlg = TPM_ALG_SHA256;
> > + pub_info.nvPublic.attributes = TPMA_NV_OWNERWRITE | TPMA_NV_OWNERREAD;
>
> from TPM 2 specs:
> "At least one of TPMA_NV_PPREAD, TPMA_NV_OWNERREAD, TPMA_NV_AUTHREAD, or
> TPMA_NV_POLICYREAD shall be SET or the TPM shall return TPM_RC_ATTRIBUTES.
> At least one of TPMA_NV_PPWRITE, TPMA_NV_OWNERWRITE, TPMA_NV_AUTHWRITE, or
> TPMA_NV_POLICYWRITE shall be SET or the TPM shall return TPM_RC_ATTRIBUTES."
>
> tricky ... the TPM 2 requires that flags be provided but one could get the
> impression that a hierarchy owner password could be set while it cannot. You
> would have to support password passing in the grub2 commands for reading
> from the nv index and loading keys into the owner hierachy then.
>
My current goal is to boot the system without any human intervention, so
the owner password support has low priority in my TODO list.
>
> > + pub_info.nvPublic.dataSize = (grub_uint16_t) data_size;
> > +
> > + authCmd.sessionHandle = TPM_RS_PW;
> > + rc = grub_tpm2_nv_definespace (TPM_RH_OWNER, &authCmd, NULL, &pub_info);
> > + if (rc != TPM_RC_SUCCESS)
> > + {
> > + fprintf (stderr, "Failed to define NV space for 0x%x
> > (TPM2_NV_DefineSpace: 0x%x)\n", args->tpm2_nvindex, rc);
> > + return GRUB_ERR_BAD_DEVICE;
> > + }
> > +
> > + authCmd.sessionHandle = TPM_RS_PW;
> > + grub_memcpy (nv_data.buffer, data, data_size);
> > + nv_data.size = (grub_uint16_t) data_size;
> > +
> > + rc = grub_tpm2_nv_write (TPM_RH_OWNER, args->tpm2_nvindex, &authCmd,
> > &nv_data, 0);
> > + if (rc != TPM_RC_SUCCESS)
> > + {
> > + fprintf (stderr, "Failed to write data into 0x%x (TPM2_NV_Write:
> > 0x%x)\n", args->tpm2_nvindex, rc);
> > + return GRUB_ERR_BAD_DEVICE;
> > + }
> > +
> > + return GRUB_ERR_NONE;
> > +}
> > +
> > static grub_err_t
> > protect_tpm2_add (protect_args_t *args)
> > {
> > @@ -911,6 +1001,8 @@ protect_tpm2_add (protect_args_t *args)
> > grub_size_t key_size;
> > TPM_HANDLE_t srk;
> > TPM2B_DIGEST_t policy_digest;
> > + void *out_buf = NULL;
> > + int out_buf_size;
> > tpm2_sealed_key_t sealed_key;
> > err = protect_tpm2_open_device (args->tpm2_device);
> > @@ -940,15 +1032,51 @@ protect_tpm2_add (protect_args_t *args)
> > if (err != GRUB_ERR_NONE)
> > goto exit3;
> > - if (args->tpm2_tpm2key != 0)
> > - err = protect_tpm2_export_tpm2key (args, &sealed_key);
> > + if (args->tpm2_tpm2key == true)
> > + {
> > + err = protect_tpm2_export_tpm2key (args, &sealed_key, &out_buf,
> > &out_buf_size);
> > + if (err != GRUB_ERR_NONE)
> > + {
> > + fprintf (stderr, N_("Could not export to TPM 2.0 Key File format\n"));
> > + goto exit3;
> > + }
> > + }
> > else
> > - err = protect_tpm2_export_sealed_key (args->tpm2_outfile, &sealed_key);
> > - if (err != GRUB_ERR_NONE)
> > - goto exit3;
> > + {
> > + err = protect_tpm2_export_raw (&sealed_key, &out_buf, &out_buf_size);
> > + if (err != GRUB_ERR_NONE)
> > + {
> > + fprintf (stderr, N_("Could not export to the raw format\n"));
> > + goto exit3;
> > + }
> > + }
> > +
> > + if (args->tpm2_outfile != NULL)
> > + {
> > + err = protect_write_file (args->tpm2_outfile, out_buf, out_buf_size);
> > + if (err != GRUB_ERR_NONE)
> > + {
> > + fprintf (stderr, N_("Could not write key file (%s).\n"), strerror
> > (errno));
> > + goto exit3;
> > + }
> > + }
> > +
> > + if (TPM_HT_IS_NVINDEX (args->tpm2_nvindex) == true)
> > + {
> > + err = protect_tpm2_export_nvindex (args, out_buf, out_buf_size);
> > + if (err != GRUB_ERR_NONE)
> > + goto exit3;
> > + }
> > + else if (TPM_HT_IS_PERSISTENT (args->tpm2_nvindex) == true)
> > + {
> > + err = protect_tpm2_export_persistent (args, srk, &sealed_key);
> > + if (err != GRUB_ERR_NONE)
> > + goto exit3;
> > + }
> > exit3:
> > grub_tpm2_flushcontext (srk);
> > + grub_free (out_buf);
> > exit2:
> > grub_free (key);
> > @@ -960,14 +1088,66 @@ protect_tpm2_add (protect_args_t *args)
> > }
> > static grub_err_t
> > -protect_tpm2_remove (protect_args_t *args)
> > +protect_tpm2_evict (TPM_HANDLE_t handle)
> > {
> > TPM_RC_t rc;
> > TPM2B_PUBLIC_t public;
> > - TPMS_AUTH_COMMAND_t authCommand = {0};
> > + TPMS_AUTH_COMMAND_t authCmd = {0};
> > +
> > + /* Find the persistent handle */
> > + rc = grub_tpm2_readpublic (handle, NULL, &public);
> > + if (rc != TPM_RC_SUCCESS)
> > + {
> > + fprintf (stderr, "Handle 0x%x not found.\n", handle);
> > + return GRUB_ERR_BAD_ARGUMENT;
> > + }
> > +
> > + /* Evict the persistent handle */
> > + authCmd.sessionHandle = TPM_RS_PW;
> > + rc = grub_tpm2_evictcontrol (TPM_RH_OWNER, handle, &authCmd, handle,
> > NULL);
> > + if (rc != TPM_RC_SUCCESS)
> > + {
> > + fprintf (stderr, "Failed to evict handle 0x%x (TPM2_EvictControl:
> > 0x%x).\n", handle, rc);
> > + return GRUB_ERR_BAD_DEVICE;
> > + }
> > +
> > + return GRUB_ERR_NONE;
> > +}
> > +
> > +static grub_err_t
> > +protect_tpm2_nv_undefine (TPM_HANDLE_t handle)
> > +{
> > + TPM_RC_t rc;
> > + TPM2B_NV_PUBLIC_t nv_public;
> > + TPMS_AUTH_COMMAND_t authCmd = {0};
> > + TPM2B_NAME_t nv_name;
> > +
> > + /* Find the nvindex handle */
> > + rc = grub_tpm2_nv_readpublic (handle, NULL, &nv_public, &nv_name);
> > + if (rc != TPM_RC_SUCCESS)
> > + {
> > + fprintf (stderr, "Handle 0x%x not found.\n", handle);
> > + return GRUB_ERR_BAD_ARGUMENT;;
>
> s/;;/;
>
> With this nit fixed:
>
> Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
>
Thanks!
Gary Lin
- [PATCH v2 02/10] tpm2_key_protector: Add 'tpm2_dump_pcr' command, (continued)
- [PATCH v2 02/10] tpm2_key_protector: Add 'tpm2_dump_pcr' command, Gary Lin, 2025/01/08
- [PATCH v2 03/10] docs: Document tpm2_dump_pcr, Gary Lin, 2025/01/08
- [PATCH v2 04/10] tss2: Fix the missing authCommand, Gary Lin, 2025/01/08
- [PATCH v2 05/10] tss2: Add TPM 2.0 NV index commands, Gary Lin, 2025/01/08
- [PATCH v2 06/10] tpm2_key_protector: Unseal key from a buffer, Gary Lin, 2025/01/08
- [PATCH v2 07/10] tpm2_key_protector: Support NV index handles, Gary Lin, 2025/01/08
- [PATCH v2 08/10] util/grub-protect: Support NV index mode, Gary Lin, 2025/01/08
- [PATCH v2 09/10] tests/tpm2_key_protector_test: Amend the NV index mode test, Gary Lin, 2025/01/08
- [PATCH v2 10/10] docs: Update NV index mode of TPM2 key protector, Gary Lin, 2025/01/08