On Wed, Oct 16, 2024 at 06:04:43PM +0200, Daniel Kiper wrote: > 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... > Will fix it in the next version.
> > --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... > I was thinking about matching the name 'grub-protect' with the types and functions here. Using 'protect_' or 'PROTECT_' is enough anyway. > > +/* 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/ > Will fix it in the next version. > > + } > > + > > + 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. This is the tcg2 function defined in grub-core/lib/tss2/tcg2.h. > > > +{ > > + 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... > This is another tcg2 function. The tcg2 functions are architecture dependent. Since grub-protect is for the user space, grub_tcg2_submit_command() here is designed to send the TPM2 commands through /dev/tpm0 or other path specified by '--tpm2-device'. > > +{ > > + 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... > Will fix them in the next version. Gary Lin > > + 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