On Fri, Sep 06, 2024 at 05:11:16PM +0800, Gary Lin via Grub-devel wrote:
> From: Hernan Gatta <hega...@linux.microsoft.com>
>
> To utilize the key protectors framework, there must be a way to protect
> full-disk encryption keys in the first place. The grub-protect tool
> includes support for the TPM2 key protector but other protectors that
> require setup ahead of time can be supported in the future.
>
> For the TPM2 key protector, the intended flow is for a user to have a
> LUKS 1 or LUKS 2-protected fully-encrypted disk. The user then creates a
> new LUKS key file, say by reading /dev/urandom into a file, and creates
> a new LUKS key slot for this key. Then, the user invokes the grub-protect
> tool to seal this key file to a set of PCRs using the system's TPM 2.0.
> The resulting sealed key file is stored in an unencrypted partition such
> as the EFI System Partition (ESP) so that GRUB may read it. The user also
> has to ensure the cryptomount command is included in GRUB's boot script
> and that it carries the requisite key protector (-P) parameter.
>
> Sample usage:
>
> $ dd if=/dev/urandom of=luks-key bs=1 count=32
> $ sudo cryptsetup luksAddKey /dev/sdb1 luks-key --pbkdf=pbkdf2 --hash=sha512
>
> To seal the key with TPM 2.0 Key File (recommended):
>
> $ sudo grub-protect --action=add \
>                     --protector=tpm2 \
>                   --tpm2-pcrs=0,2,4,7,9 \

Please replace tabs with spaces here...

>                     --tpm2key \
>                     --tpm2-keyfile=luks-key \
>                     --tpm2-outfile=/boot/efi/boot/grub2/sealed.tpm
>
> Or, to seal the key with the raw sealed key:
>
> $ sudo grub-protect --action=add \
>                     --protector=tpm2 \
>                   --tpm2-pcrs=0,2,4,7,9 \

Ditto...

>                     --tpm2-keyfile=luks-key \
>                     --tpm2-outfile=/boot/efi/boot/grub2/sealed.key
>
> Then, in the boot script, for TPM 2.0 Key File:
>
> tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
> cryptomount -u <SDB1_UUID> -P tpm2
>
> Or, for the raw sealed key:
>
> tpm2_key_protector_init --keyfile=(hd0,gpt1)/boot/grub2/sealed.key 
> --pcrs=0,2,4,7,9
> cryptomount -u <SDB1_UUID> -P tpm2
>
> The benefit of using TPM 2.0 Key File is that the PCR set is already
> written in the key file, so there is no need to specify PCRs when
> invoking tpm2_key_protector_init.
>
> Cc: Stefan Berger <stef...@linux.ibm.com>
> Signed-off-by: Hernan Gatta <hega...@linux.microsoft.com>
> Signed-off-by: Gary Lin <g...@suse.com>
> ---
>  .gitignore                |    2 +
>  Makefile.util.def         |   26 +
>  configure.ac              |   30 +
>  docs/man/grub-protect.h2m |    4 +
>  util/grub-protect.c       | 1394 +++++++++++++++++++++++++++++++++++++
>  5 files changed, 1456 insertions(+)
>  create mode 100644 docs/man/grub-protect.h2m
>  create mode 100644 util/grub-protect.c
>
> diff --git a/.gitignore b/.gitignore
> index 4c1f91db8..2105d87c8 100644
> --- a/.gitignore
> +++ b/.gitignore
> @@ -169,6 +169,8 @@ widthspec.bin
>  /grub-ofpathname.exe
>  /grub-probe
>  /grub-probe.exe
> +/grub-protect
> +/grub-protect.exe
>  /grub-reboot
>  /grub-render-label
>  /grub-render-label.exe
> diff --git a/Makefile.util.def b/Makefile.util.def
> index fb82f59a0..074c0aff7 100644
> --- a/Makefile.util.def
> +++ b/Makefile.util.def
> @@ -208,6 +208,32 @@ program = {
>    ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
>  };
>
> +program = {
> +  name = grub-protect;
> +  mansection = 1;
> +
> +  common = grub-core/kern/emu/argp_common.c;
> +  common = grub-core/osdep/init.c;
> +  common = grub-core/lib/tss2/buffer.c;
> +  common = grub-core/lib/tss2/tss2_mu.c;
> +  common = grub-core/lib/tss2/tpm2_cmd.c;
> +  common = grub-core/commands/tpm2_key_protector/args.c;
> +  common = grub-core/commands/tpm2_key_protector/tpm2key_asn1_tab.c;
> +  common = util/grub-protect.c;
> +  common = util/probe.c;
> +
> +  cflags = '-I$(srcdir)/grub-core/lib/tss2 
> -I$(srcdir)/grub-core/commands/tpm2_key_protector';
> +
> +  ldadd = libgrubmods.a;
> +  ldadd = libgrubgcry.a;
> +  ldadd = libgrubkern.a;
> +  ldadd = grub-core/lib/gnulib/libgnu.a;
> +  ldadd = '$(LIBTASN1)';
> +  ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBUTIL) $(LIBZFS) $(LIBNVPAIR) 
> $(LIBGEOM)';
> +
> +  condition = COND_GRUB_PROTECT;
> +};
> +
>  program = {
>    name = grub-mkrelpath;
>    mansection = 1;
> diff --git a/configure.ac b/configure.ac
> index 458b8382b..ad1e7bea5 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -76,6 +76,7 @@ grub_TRANSFORM([grub-mkpasswd-pbkdf2])
>  grub_TRANSFORM([grub-mkrelpath])
>  grub_TRANSFORM([grub-mkrescue])
>  grub_TRANSFORM([grub-probe])
> +grub_TRANSFORM([grub-protect])
>  grub_TRANSFORM([grub-reboot])
>  grub_TRANSFORM([grub-script-check])
>  grub_TRANSFORM([grub-set-default])
> @@ -2068,6 +2069,29 @@ fi
>  AC_SUBST([LIBZFS])
>  AC_SUBST([LIBNVPAIR])
>
> +AC_ARG_ENABLE([grub-protect],
> +           [AS_HELP_STRING([--enable-grub-protect],
> +                             [build and install the `grub-protect' utility 
> (default=guessed)])])
> +if test x"$enable_grub_protect" = xno ; then
> +  grub_protect_excuse="explicitly disabled"
> +fi
> +
> +LIBTASN1=
> +if test x"$grub_protect_excuse" = x ; then
> +  AC_CHECK_LIB([tasn1], [asn1_write_value], [LIBTASN1="-ltasn1"], 
> [grub_protect_excuse="need libtasn1 library"])
> +fi
> +AC_SUBST([LIBTASN1])
> +
> +if test x"$enable_grub_protect" = xyes && test x"$grub_protect_excuse" != x 
> ; then
> +  AC_MSG_ERROR([grub-protect was explicitly requested but can't be compiled 
> ($grub_protect_excuse)])
> +fi
> +if test x"$grub_protect_excuse" = x ; then
> +enable_grub_protect=yes
> +else
> +enable_grub_protect=no
> +fi
> +AC_SUBST([enable_grub_protect])
> +
>  LIBS=""
>
>  AC_SUBST([FONT_SOURCE])
> @@ -2184,6 +2208,7 @@ AM_CONDITIONAL([COND_GRUB_EMU_SDL], [test 
> x$enable_grub_emu_sdl = xyes])
>  AM_CONDITIONAL([COND_GRUB_EMU_PCI], [test x$enable_grub_emu_pci = xyes])
>  AM_CONDITIONAL([COND_GRUB_MKFONT], [test x$enable_grub_mkfont = xyes])
>  AM_CONDITIONAL([COND_GRUB_MOUNT], [test x$enable_grub_mount = xyes])
> +AM_CONDITIONAL([COND_GRUB_PROTECT], [test x$enable_grub_protect = xyes])
>  AM_CONDITIONAL([COND_HAVE_FONT_SOURCE], [test x$FONT_SOURCE != x])
>  if test x$FONT_SOURCE != x ; then
>     HAVE_FONT_SOURCE=1
> @@ -2311,6 +2336,11 @@ echo grub-mount: Yes
>  else
>  echo grub-mount: No "($grub_mount_excuse)"
>  fi
> +if [ x"$grub_protect_excuse" = x ]; then
> +echo grub-protect: Yes
> +else
> +echo grub-protect: No "($grub_protect_excuse)"
> +fi
>  if [ x"$starfield_excuse" = x ]; then
>  echo starfield theme: Yes
>  echo With DejaVuSans font from $DJVU_FONT_SOURCE
> diff --git a/docs/man/grub-protect.h2m b/docs/man/grub-protect.h2m
> new file mode 100644
> index 000000000..ecf1c9eab
> --- /dev/null
> +++ b/docs/man/grub-protect.h2m
> @@ -0,0 +1,4 @@
> +[NAME]
> +grub-protect \- protect a disk key with a key protector
> +[DESCRIPTION]
> +grub-protect helps to protect a disk encryption key with a specified key 
> protector.
> diff --git a/util/grub-protect.c b/util/grub-protect.c
> new file mode 100644
> index 000000000..fb4ea4079
> --- /dev/null
> +++ b/util/grub-protect.c
> @@ -0,0 +1,1394 @@
> +/*
> + *  GRUB  --  GRand Unified Bootloader
> + *  Copyright (C) 2022 Microsoft Corporation
> + *  Copyright (C) 2023 SUSE LLC
> + *  Copyright (C) 2024 Free Software Foundation, Inc.
> + *
> + *  GRUB is free software: you can redistribute it and/or modify
> + *  it under the terms of the GNU General Public License as published by
> + *  the Free Software Foundation, either version 3 of the License, or
> + *  (at your option) any later version.
> + *
> + *  GRUB is distributed in the hope that it will be useful,
> + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
> + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + *  GNU General Public License for more details.
> + *
> + *  You should have received a copy of the GNU General Public License
> + *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <config.h>
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <libtasn1.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <unistd.h>
> +
> +#include <grub/emu/hostdisk.h>
> +#include <grub/emu/misc.h>
> +
> +#include <grub/util/misc.h>
> +
> +#include <tss2_buffer.h>
> +#include <tss2_mu.h>
> +#include <tcg2.h>
> +#include <tpm2_args.h>
> +#include <tpm2.h>
> +
> +#pragma GCC diagnostic ignored "-Wmissing-prototypes"
> +#pragma GCC diagnostic ignored "-Wmissing-declarations"
> +#include <argp.h>
> +#pragma GCC diagnostic error "-Wmissing-prototypes"
> +#pragma GCC diagnostic error "-Wmissing-declarations"
> +
> +#include "progname.h"
> +
> +/* Unprintable option keys for argp */
> +typedef enum grub_protect_opt
> +{
> +  /* General */
> +  GRUB_PROTECT_OPT_ACTION      = 'a',
> +  GRUB_PROTECT_OPT_PROTECTOR   = 'p',
> +  /* TPM2 */
> +  GRUB_PROTECT_OPT_TPM2_DEVICE = 0x100,
> +  GRUB_PROTECT_OPT_TPM2_PCRS,
> +  GRUB_PROTECT_OPT_TPM2_ASYMMETRIC,
> +  GRUB_PROTECT_OPT_TPM2_BANK,
> +  GRUB_PROTECT_OPT_TPM2_SRK,
> +  GRUB_PROTECT_OPT_TPM2_KEYFILE,
> +  GRUB_PROTECT_OPT_TPM2_OUTFILE,
> +  GRUB_PROTECT_OPT_TPM2_EVICT,
> +  GRUB_PROTECT_OPT_TPM2_TPM2KEY
> +} grub_protect_opt;

Again, you can drop "grub_" prefixes for internal stuff...

> +/* Option flags to keep track of specified arguments */
> +typedef enum grub_protect_arg
> +{
> +  /* General */
> +  GRUB_PROTECT_ARG_ACTION          = 1 << 0,
> +  GRUB_PROTECT_ARG_PROTECTOR       = 1 << 1,
> +  /* TPM2 */
> +  GRUB_PROTECT_ARG_TPM2_DEVICE     = 1 << 2,
> +  GRUB_PROTECT_ARG_TPM2_PCRS       = 1 << 3,
> +  GRUB_PROTECT_ARG_TPM2_ASYMMETRIC = 1 << 4,
> +  GRUB_PROTECT_ARG_TPM2_BANK       = 1 << 5,
> +  GRUB_PROTECT_ARG_TPM2_SRK        = 1 << 6,
> +  GRUB_PROTECT_ARG_TPM2_KEYFILE    = 1 << 7,
> +  GRUB_PROTECT_ARG_TPM2_OUTFILE    = 1 << 8,
> +  GRUB_PROTECT_ARG_TPM2_EVICT      = 1 << 9,
> +  GRUB_PROTECT_ARG_TPM2_TPM2KEY    = 1 << 10
> +} grub_protect_arg_t;

Ditto and in other patches too...

> +typedef enum grub_protect_protector
> +{
> +  GRUB_PROTECT_TYPE_ERROR,
> +  GRUB_PROTECT_TYPE_TPM2
> +} grub_protect_protector_t;
> +
> +typedef enum grub_protect_action
> +{
> +  GRUB_PROTECT_ACTION_ERROR,
> +  GRUB_PROTECT_ACTION_ADD,
> +  GRUB_PROTECT_ACTION_REMOVE
> +} grub_protect_action_t;
> +
> +struct grub_protect_args
> +{
> +  grub_protect_arg_t args;
> +  grub_protect_action_t action;
> +  grub_protect_protector_t protector;
> +
> +  const char *tpm2_device;
> +  grub_uint8_t tpm2_pcrs[TPM_MAX_PCRS];
> +  grub_uint8_t tpm2_pcr_count;
> +  grub_srk_type_t srk_type;
> +  TPM_ALG_ID_t tpm2_bank;
> +  TPM_HANDLE_t tpm2_srk;
> +  const char *tpm2_keyfile;
> +  const char *tpm2_outfile;
> +  int tpm2_evict;
> +  int tpm2_tpm2key;
> +};
> +
> +static struct argp_option grub_protect_options[] =
> +  {
> +    /* Top-level options */
> +   {
> +      .name  = "action",
> +      .key   = 'a',
> +      .arg   = "add|remove",
> +      .flags = 0,
> +      .doc   =
> +     N_("Add or remove a key protector to or from a key."),
> +      .group = 0
> +    },
> +    {
> +      .name  = "protector",
> +      .key   = 'p',
> +      .arg   = "tpm2",
> +      .flags = 0,
> +      .doc   =
> +     N_("Set key protector to use (only tpm2 is currently supported)."),
> +      .group = 0
> +    },
> +    /* TPM2 key protector options */
> +    {
> +      .name = "tpm2-device",
> +      .key   = GRUB_PROTECT_OPT_TPM2_DEVICE,
> +      .arg   = "FILE",
> +      .flags = 0,
> +      .doc   =
> +     N_("Set the path to the TPM2 device. (default: /dev/tpm0)"),
> +      .group = 0
> +    },
> +    {
> +      .name = "tpm2-pcrs",
> +      .key   = GRUB_PROTECT_OPT_TPM2_PCRS,
> +      .arg   = "0[,1]...",
> +      .flags = 0,
> +      .doc   =
> +     N_("Set a comma-separated list of PCRs used to authorize key release "
> +        "e.g., '7,11'. Please be aware that PCR 0~7 are used by the "
> +        "firmware and the measurement result may change after a "
> +        "firmware update (for baremetal systems) or a package "
> +        "(OVMF/SeaBIOS/SLOF) update in the VM host. This may lead to "
> +        "the failure of key unsealing. (default: 7)"),
> +      .group = 0
> +    },
> +    {
> +      .name = "tpm2-bank",
> +      .key  = GRUB_PROTECT_OPT_TPM2_BANK,
> +      .arg   = "ALG",
> +      .flags = 0,
> +      .doc   =
> +     N_("Set the bank of PCRs used to authorize key release: "
> +        "SHA1, SHA256, SHA384, or SHA512. (default: SHA256)"),
> +      .group = 0
> +    },
> +    {
> +      .name = "tpm2-keyfile",
> +      .key   = GRUB_PROTECT_OPT_TPM2_KEYFILE,
> +      .arg   = "FILE",
> +      .flags = 0,
> +      .doc   =
> +     N_("Set the path to a file that contains the cleartext key to 
> protect."),
> +      .group = 0
> +    },
> +    {
> +      .name = "tpm2-outfile",
> +      .key   = GRUB_PROTECT_OPT_TPM2_OUTFILE,
> +      .arg   = "FILE",
> +      .flags = 0,
> +      .doc   =
> +     N_("Set the path to the file that will contain the key after sealing "
> +        "(must be accessible to GRUB during boot)."),
> +      .group = 0
> +    },
> +    {
> +      .name = "tpm2-srk",
> +      .key   = GRUB_PROTECT_OPT_TPM2_SRK,
> +      .arg   = "NUM",
> +      .flags = 0,
> +      .doc   =
> +     N_("Set the SRK handle if the SRK is to be made persistent."),
> +      .group = 0
> +    },
> +    {
> +      .name = "tpm2-asymmetric",
> +      .key   = GRUB_PROTECT_OPT_TPM2_ASYMMETRIC,
> +      .arg   = "TYPE",
> +      .flags = 0,
> +      .doc   =
> +     N_("Set the type of SRK: RSA (RSA2048) and ECC (ECC_NIST_P256)."
> +        "(default: ECC)"),
> +      .group = 0
> +    },
> +    {
> +      .name = "tpm2-evict",
> +      .key   = GRUB_PROTECT_OPT_TPM2_EVICT,
> +      .arg   = NULL,
> +      .flags = 0,
> +      .doc   =
> +     N_("Evict a previously persisted SRK from the TPM, if any."),
> +      .group = 0
> +    },
> +    {
> +      .name = "tpm2key",
> +      .key   = GRUB_PROTECT_OPT_TPM2_TPM2KEY,
> +      .arg   = NULL,
> +      .flags = 0,
> +      .doc   =
> +     N_("Use TPM 2.0 Key File format instead of the raw format."),
> +      .group = 0
> +    },
> +    /* End of list */
> +    { 0, 0, 0, 0, 0, 0 }
> +  };
> +
> +static int protector_tpm2_fd = -1;
> +
> +static grub_err_t
> +protect_read_file (const char *filepath, void **buffer, size_t *buffer_size)
> +{
> +  grub_err_t err;
> +  FILE *f;
> +  long len;
> +  void *buf;
> +
> +  f = fopen (filepath, "rb");
> +  if (f == NULL)
> +    return GRUB_ERR_FILE_NOT_FOUND;
> +
> +  if (fseek (f, 0, SEEK_END))
> +    {
> +       err = GRUB_ERR_FILE_READ_ERROR;
> +       goto exit1;
> +    }
> +
> +  len = ftell (f);
> +  if (len <= 0)
> +    {
> +       err = GRUB_ERR_FILE_READ_ERROR;
> +       goto exit1;
> +    }
> +
> +  rewind (f);
> +
> +  buf = grub_malloc (len);
> +  if (buf == NULL)
> +    {
> +       err = GRUB_ERR_OUT_OF_MEMORY;
> +       goto exit1;
> +    }
> +
> +  if (fread (buf, len, 1, f) != 1)
> +    {
> +       err = GRUB_ERR_FILE_READ_ERROR;
> +       goto exit2;
> +    }
> +
> +  *buffer = buf;
> +  *buffer_size = len;
> +
> +  buf = NULL;
> +  err = GRUB_ERR_NONE;
> +
> + exit2:
> +  grub_free (buf);
> +
> + exit1:
> +  fclose (f);
> +
> +  return err;
> +}
> +
> +static grub_err_t
> +protect_write_file (const char *filepath, void *buffer, size_t buffer_size)
> +{
> +  grub_err_t err;
> +  FILE *f;
> +
> +  f = fopen (filepath, "wb");
> +  if (f == NULL)
> +    return GRUB_ERR_FILE_NOT_FOUND;
> +
> +  if (fwrite (buffer, buffer_size, 1, f) != 1)
> +  {
> +    err = GRUB_ERR_WRITE_ERROR;
> +    goto exit1;

s/exit1/exit/

> +  }
> +
> +  err = GRUB_ERR_NONE;
> +
> + exit1:
> +  fclose (f);
> +
> +  return err;
> +}
> +
> +grub_err_t
> +grub_tcg2_get_max_output_size (grub_size_t *size)

This function seems unused.

> +{
> +  if (size == NULL)
> +    return GRUB_ERR_BAD_ARGUMENT;
> +
> +  *size = GRUB_TPM2_BUFFER_CAPACITY;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +grub_err_t
> +grub_tcg2_submit_command (grub_size_t input_size, grub_uint8_t *input,
> +                       grub_size_t output_size, grub_uint8_t *output)

Ditto...

> +{
> +  static const grub_size_t header_size = sizeof (grub_uint16_t) +
> +                                      (2 * sizeof(grub_uint32_t));
> +
> +  if (write (protector_tpm2_fd, input, input_size) != input_size)
> +    return GRUB_ERR_BAD_DEVICE;
> +
> +  if (read (protector_tpm2_fd, output, output_size) < header_size)
> +    return GRUB_ERR_BAD_DEVICE;
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +protect_tpm2_open_device (const char *dev_node)
> +{
> +  if (protector_tpm2_fd != -1)
> +    return GRUB_ERR_NONE;
> +
> +  protector_tpm2_fd = open (dev_node, O_RDWR);
> +  if (protector_tpm2_fd == -1)
> +    {
> +      fprintf (stderr, "Could not open TPM device (%s).\n", strerror 
> (errno));
> +      return GRUB_ERR_FILE_NOT_FOUND;
> +    }
> +
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +protect_tpm2_close_device (void)
> +{
> +  int err;
> +
> +  if (protector_tpm2_fd == -1)
> +    return GRUB_ERR_NONE;
> +
> +  err = close (protector_tpm2_fd);
> +  if (err != GRUB_ERR_NONE)
> +  {
> +    fprintf (stderr, "Could not close TPM device (Error: %u).\n", errno);
> +    return GRUB_ERR_IO;
> +  }
> +
> +  protector_tpm2_fd = -1;
> +  return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +protect_tpm2_get_policy_digest (struct grub_protect_args *args, 
> TPM2B_DIGEST_t *digest)
> +{
> +  TPM_RC_t rc;
> +  TPML_PCR_SELECTION_t pcr_sel = {
> +    .count = 1,
> +    .pcrSelections = {
> +      {
> +     .hash = args->tpm2_bank,
> +     .sizeOfSelect = 3,
> +     .pcrSelect = { 0 }
> +      },
> +    }
> +  };
> +  TPML_PCR_SELECTION_t pcr_sel_out = { 0 };

Redundant spaces around 0.

> +  TPML_DIGEST_t pcr_values = { 0 };

Ditto and below...

> +  TPM2B_DIGEST_t pcr_digest = { 0 };
> +  grub_size_t pcr_digest_len;
> +  TPM2B_MAX_BUFFER_t pcr_concat = { 0 };
> +  grub_size_t pcr_concat_len;
> +  grub_uint8_t *pcr_cursor;
> +  TPM2B_NONCE_t nonce = { 0 };
> +  TPM2B_ENCRYPTED_SECRET_t salt = { 0 };
> +  TPMT_SYM_DEF_t symmetric = { 0 };
> +  TPMI_SH_AUTH_SESSION_t session = 0;
> +  TPM2B_DIGEST_t policy_digest = { 0 };
> +  grub_uint8_t i;
> +  grub_err_t err;

Daniel

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

Reply via email to