On Mon, Nov 04, 2024 at 03:31:58PM +0800, Gary Lin 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 \ > --tpm2key \ > --tpm2-keyfile=luks-key \ > --tpm2-outfile=/boot/efi/efi/grub/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 \ > --tpm2-keyfile=luks-key \ > --tpm2-outfile=/boot/efi/efi/grub/sealed.key > > Then, in the boot script, for TPM 2.0 Key File: > > tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub/sealed.tpm > cryptomount -u <SDB1_UUID> -P tpm2 > > Or, for the raw sealed key: > > tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub/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 | 1407 +++++++++++++++++++++++++++++++++++++ > 5 files changed, 1469 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..a120529ef > --- /dev/null > +++ b/util/grub-protect.c > @@ -0,0 +1,1407 @@ > +/* > + * 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 protect_opt > +{ > + /* General */ > + PROTECT_OPT_ACTION = 'a', > + PROTECT_OPT_PROTECTOR = 'p', > + /* TPM2 */ > + PROTECT_OPT_TPM2_DEVICE = 0x100, > + PROTECT_OPT_TPM2_PCRS, > + PROTECT_OPT_TPM2_ASYMMETRIC, > + PROTECT_OPT_TPM2_BANK, > + PROTECT_OPT_TPM2_SRK, > + PROTECT_OPT_TPM2_KEYFILE, > + PROTECT_OPT_TPM2_OUTFILE, > + PROTECT_OPT_TPM2_EVICT, > + PROTECT_OPT_TPM2_TPM2KEY > +} protect_opt_t; > + > +/* Option flags to keep track of specified arguments */ > +typedef enum protect_arg > +{ > + /* General */ > + PROTECT_ARG_ACTION = 1 << 0, > + PROTECT_ARG_PROTECTOR = 1 << 1, > + /* TPM2 */ > + PROTECT_ARG_TPM2_DEVICE = 1 << 2, > + PROTECT_ARG_TPM2_PCRS = 1 << 3, > + PROTECT_ARG_TPM2_ASYMMETRIC = 1 << 4, > + PROTECT_ARG_TPM2_BANK = 1 << 5, > + PROTECT_ARG_TPM2_SRK = 1 << 6, > + 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_t; > + > +typedef enum protect_protector > +{ > + PROTECT_TYPE_ERROR, > + PROTECT_TYPE_TPM2 > +} protect_protector_t; > + > +typedef enum protect_action > +{ > + PROTECT_ACTION_ERROR, > + PROTECT_ACTION_ADD, > + PROTECT_ACTION_REMOVE > +} protect_action_t; > + > +typedef struct protect_args > +{ > + protect_arg_t args; > + protect_action_t action; > + 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;
AFAICT the tpm2_evict and tpm2_tpm2key should be defined as bool. > +} protect_args_t; [...] > +static error_t > +protect_argp_parser (int key, char *arg, struct argp_state *state) > +{ > + grub_err_t err; > + protect_args_t *args = state->input; > + > + switch (key) > + { > + case PROTECT_OPT_ACTION: > + if (args->args & PROTECT_ARG_ACTION) > + { > + fprintf (stderr, N_("--action|-a can only be specified once.\n")); > + return EINVAL; > + } > + > + if (grub_strcmp (arg, "add") == 0) > + args->action = PROTECT_ACTION_ADD; > + else if (grub_strcmp (arg, "remove") == 0) > + args->action = PROTECT_ACTION_REMOVE; > + else > + { > + fprintf (stderr, N_("'%s' is not a valid action.\n"), arg); > + return EINVAL; > + } > + > + args->args |= PROTECT_ARG_ACTION; > + break; > + > + case PROTECT_OPT_PROTECTOR: > + if (args->args & PROTECT_ARG_PROTECTOR) > + { > + fprintf (stderr, N_("--protector|-p can only be specified once.\n")); > + return EINVAL; > + } > + > + if (grub_strcmp (arg, "tpm2") == 0) > + args->protector = PROTECT_TYPE_TPM2; > + else > + { > + fprintf (stderr, N_("'%s' is not a valid protector.\n"), arg); > + return EINVAL; > + } > + > + args->args |= PROTECT_ARG_PROTECTOR; > + break; > + > + case PROTECT_OPT_TPM2_DEVICE: > + if (args->args & PROTECT_ARG_TPM2_DEVICE) > + { > + fprintf (stderr, N_("--tpm2-device can only be specified once.\n")); > + return EINVAL; > + } > + > + args->tpm2_device = xstrdup(arg); Missing space before "(". Please fix this here and in other places too. If you fix these minor issues and problems mentioned by Stefan you can add Reviewed-by: Daniel Kiper <daniel.ki...@oracle.com>. Daniel _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel