Re: [PATCH v11 16/20] cryptodisk: Fallback to passphrase

2024-04-12 Thread Patrick Colp via Grub-devel

On 2024-04-12 14:13, Stefan Berger wrote:



On 4/12/24 04:39, Gary Lin via Grub-devel wrote:

From: Patrick Colp 

If a protector is specified, but it fails to unlock the disk, fall back
to asking for the passphrase. However, an error was set indicating that
the protector(s) failed. Later code (e.g., LUKS code) fails as
`grub_errno` is now set. Print the existing errors out first, before


Though you are resetting grub_errno now so that LUKS code then does 
not fail unnecessarily? It's not quite clear what the above wants to 
convey.




Yeah, that's it. If we do nothing with `grub_errno` here, then code will 
eventually execute that checks if `grub_errno` is set. It'll see that it 
is and fail, even though there was no actual failure, it was just set 
before from when the protector failed. I could change it to read 
something like:


If `grub_errno` was previously set, print out the error and clear 
`grub_errno`.



proceeding with the passphrase.





Signed-off-by: Patrick Colp 
Signed-off-by: Gary Lin 
---
  grub-core/disk/cryptodisk.c | 7 ++-
  1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 0ca1a5c4d..0dbf601c4 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -1191,11 +1191,16 @@ grub_cryptodisk_scan_device_real (const char 
*name,

    source->name, source->partition != NULL ? "," : "",
    part != NULL ? part : N_("UNKNOWN"), dev->uuid);
    grub_free (part);
-  goto error;
  }
      if (!cargs->key_len)
  {
+  if (grub_errno)
+    {
+  grub_print_error ();
+  grub_errno = GRUB_ERR_NONE;
+    }
+
    /* Get the passphrase from the user, if no key data. */
    askpass = 1;
    part = grub_partition_get_name (source->partition);



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


Re: [PATCH v11 16/20] cryptodisk: Fallback to passphrase

2024-04-12 Thread Stefan Berger



On 4/12/24 04:39, Gary Lin via Grub-devel wrote:

From: Patrick Colp 

If a protector is specified, but it fails to unlock the disk, fall back
to asking for the passphrase. However, an error was set indicating that
the protector(s) failed. Later code (e.g., LUKS code) fails as
`grub_errno` is now set. Print the existing errors out first, before


Though you are resetting grub_errno now so that LUKS code then does not 
fail unnecessarily? It's not quite clear what the above wants to convey.



proceeding with the passphrase.





Signed-off-by: Patrick Colp 
Signed-off-by: Gary Lin 
---
  grub-core/disk/cryptodisk.c | 7 ++-
  1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 0ca1a5c4d..0dbf601c4 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -1191,11 +1191,16 @@ grub_cryptodisk_scan_device_real (const char *name,
  source->name, source->partition != NULL ? "," : "",
  part != NULL ? part : N_("UNKNOWN"), dev->uuid);
grub_free (part);
-  goto error;
  }
  
if (!cargs->key_len)

  {
+  if (grub_errno)
+   {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+   }
+
/* Get the passphrase from the user, if no key data. */
askpass = 1;
part = grub_partition_get_name (source->partition);


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


Re: [PATCH v11 17/20] cryptodisk: wipe out the cached keys from protectors

2024-04-12 Thread Stefan Berger



On 4/12/24 04:39, Gary Lin via Grub-devel wrote:

An attacker may insert a malicious disk with the same crypto UUID and
trick grub2 to mount the fake root. Even though the key from the key
protector fails to unlock the fake root, it's not wiped out cleanly so
the attacker could dump the memory to retrieve the secret key. To defend
such attack, wipe out the cached key when we don't need it.

Signed-off-by: Gary Lin 
Cc: Fabian Vogt 


Reviewed-by: Stefan Berger 



---
  grub-core/disk/cryptodisk.c | 6 +-
  1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 0dbf601c4..94507ec65 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -1349,7 +1349,11 @@ grub_cryptodisk_clear_key_cache (struct 
grub_cryptomount_args *cargs)
  return;
  
for (i = 0; cargs->protectors[i]; i++)

-grub_free (cargs->key_cache[i].key);
+{
+  if (cargs->key_cache[i].key)
+   grub_memset (cargs->key_cache[i].key, 0, cargs->key_cache[i].key_len);
+  grub_free (cargs->key_cache[i].key);
+}
  
grub_free (cargs->key_cache);

  }


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


Re: [PATCH v11 19/20] tpm2: Enable tpm2 module for grub-emu

2024-04-12 Thread Stefan Berger



On 4/12/24 04:39, Gary Lin via Grub-devel wrote:

As a preparation to test TPM 2.0 TSS stack with grub-emu, the new
option, --tpm-device, is introduced to specify the TPM device for
grub-emu so that grub-emu can share the emulated TPM device with
the host.

Since grub-emu can directly access the device node on host, it's easy to
implement the essential TCG2 command submission function with the
read/write functions and enable tpm2 module for grub-emu, so that we can
further test TPM key unsealing with grub-emu.

Signed-off-by: Gary Lin 

Reviewed-by: Stefan Berger 


---
  grub-core/Makefile.core.def |  2 ++
  grub-core/kern/emu/main.c   | 11 +++-
  grub-core/kern/emu/misc.c   | 51 
  grub-core/tpm2/tcg2-emu.c   | 52 +
  include/grub/emu/misc.h |  5 
  5 files changed, 120 insertions(+), 1 deletion(-)
  create mode 100644 grub-core/tpm2/tcg2-emu.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index e937f6538..962090d2c 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2567,7 +2567,9 @@ module = {
common = tpm2/tpm2key.c;
common = tpm2/tpm2key_asn1_tab.c;
efi = tpm2/tcg2.c;
+  emu = tpm2/tcg2-emu.c;
enable = efi;
+  enable = emu;
  };
  
  module = {

diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c
index 855b11c3d..c10838613 100644
--- a/grub-core/kern/emu/main.c
+++ b/grub-core/kern/emu/main.c
@@ -55,7 +55,7 @@
  static jmp_buf main_env;
  
  /* Store the prefix specified by an argument.  */

-static char *root_dev = NULL, *dir = NULL;
+static char *root_dev = NULL, *dir = NULL, *tpm_dev = NULL;
  
  grub_addr_t grub_modbase = 0;
  
@@ -108,6 +108,7 @@ static struct argp_option options[] = {

{"verbose", 'v', 0,  0, N_("print verbose messages."), 0},
{"hold", 'H', N_("SECS"),  OPTION_ARG_OPTIONAL, N_("wait until a 
debugger will attach"), 0},
{"kexec",   'X', 0,  0, N_("use kexec to boot Linux kernels via 
systemctl (pass twice to enable dangerous fallback to non-systemctl)."), 0},
+  {"tpm-device",  't', N_("DEV"), 0, N_("Set TPM device."), 0},
{ 0, 0, 0, 0, 0, 0 }
  };
  
@@ -168,6 +169,10 @@ argp_parser (int key, char *arg, struct argp_state *state)

  case 'X':
grub_util_set_kexecute ();
break;
+case 't':
+  free (tpm_dev);
+  tpm_dev = xstrdup (arg);
+  break;
  
  case ARGP_KEY_ARG:

{
@@ -276,6 +281,9 @@ main (int argc, char *argv[])
  
dir = xstrdup (dir);
  
+  if (tpm_dev)

+grub_util_tpm_open (tpm_dev);
+
/* Start GRUB!  */
if (setjmp (main_env) == 0)
  grub_main ();
@@ -283,6 +291,7 @@ main (int argc, char *argv[])
grub_fini_all ();
grub_hostfs_fini ();
grub_host_fini ();
+  grub_util_tpm_close ();
  
grub_machine_fini (GRUB_LOADER_FLAG_NORETURN);
  
diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c

index 521220b49..1db24fde7 100644
--- a/grub-core/kern/emu/misc.c
+++ b/grub-core/kern/emu/misc.c
@@ -28,6 +28,8 @@
  #include 
  #include 
  #include 
+#include 
+#include 
  
  #include 

  #include 
@@ -41,6 +43,8 @@
  int verbosity;
  int kexecute;
  
+static int grub_util_tpm_fd = -1;

+
  void
  grub_util_warn (const char *fmt, ...)
  {
@@ -230,3 +234,50 @@ grub_util_get_kexecute (void)
  {
return kexecute;
  }
+
+grub_err_t
+grub_util_tpm_open (const char *tpm_dev)
+{
+  if (grub_util_tpm_fd != -1)
+return GRUB_ERR_NONE;
+
+  grub_util_tpm_fd = open (tpm_dev, O_RDWR);
+  if (grub_util_tpm_fd == -1)
+grub_util_error (_("cannot open TPM device '%s': %s"), tpm_dev, strerror 
(errno));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_util_tpm_close (void)
+{
+  int err;
+
+  if (grub_util_tpm_fd == -1)
+return GRUB_ERR_NONE;
+
+  err = close (grub_util_tpm_fd);
+  if (err != GRUB_ERR_NONE)
+grub_util_error (_("cannot close TPM device: %s"), strerror (errno));
+
+  grub_util_tpm_fd = -1;
+  return GRUB_ERR_NONE;
+}
+
+grub_size_t
+grub_util_tpm_read (void *output, grub_size_t size)
+{
+  if (grub_util_tpm_fd == -1)
+return -1;
+
+  return read (grub_util_tpm_fd, output, size);
+}
+
+grub_size_t
+grub_util_tpm_write (const void *input, grub_size_t size)
+{
+  if (grub_util_tpm_fd == -1)
+return -1;
+
+  return write (grub_util_tpm_fd, input, size);
+}
diff --git a/grub-core/tpm2/tcg2-emu.c b/grub-core/tpm2/tcg2-emu.c
new file mode 100644
index 0..0d7b8b16e
--- /dev/null
+++ b/grub-core/tpm2/tcg2-emu.c
@@ -0,0 +1,52 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2024 SUSE LLC
+ *
+ *  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 

Re: [PATCH v11 15/20] tpm2: Implement NV index

2024-04-12 Thread Stefan Berger



On 4/12/24 04:39, Gary Lin via Grub-devel wrote:

From: Patrick Colp 

Currently with the TPM2 protector, only SRK mode is supported and
NV index support is just a stub. Implement the NV index option.

Note: This only extends support on the unseal path. grub2_protect
has not been updated. tpm2-tools can be used to insert a key into
the NV index.

An example of inserting a key using tpm2-tools:

   # Get random key.
   tpm2_getrandom 32 > key.dat

   # Create primary object.
   tpm2_createprimary -C o -g sha256 -G ecc -c primary.ctx

   # Create policy object. `pcrs.dat` contains the PCR values to seal against.
   tpm2_startauthsession -S session.dat
   tpm2_policypcr -S session.dat -l sha256:7,11 -f pcrs.dat -L policy.dat
   tpm2_flushcontext session.dat

   # Seal key into TPM.
   cat key.dat | tpm2_create -C primary.ctx -u key.pub -r key.priv -L 
policy.dat -i-
   tpm2_load -C primary.ctx -u key.pub -r key.priv -n sealing.name -c 
sealing.ctx
   tpm2_evictcontrol -C o -c sealing.ctx 0x8100

Then to unseal the key in grub, add this to grub.cfg:

   tpm2_key_protector_init --mode=nv --nvindex=0x8100 --pcrs=7,11
   cryptomount -u  --protector tpm2

Signed-off-by: Patrick Colp 
Signed-off-by: Gary Lin 


Reviewed-by: Stefan Berger 


---
  grub-core/tpm2/module.c | 25 -
  1 file changed, 20 insertions(+), 5 deletions(-)

diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
index 0ed8f2682..b4d588b0c 100644
--- a/grub-core/tpm2/module.c
+++ b/grub-core/tpm2/module.c
@@ -1022,12 +1022,27 @@ static grub_err_t
  grub_tpm2_protector_nv_recover (const struct grub_tpm2_protector_context *ctx,
grub_uint8_t **key, grub_size_t *key_size)
  {
-  (void)ctx;
-  (void)key;
-  (void)key_size;
+  TPM_HANDLE sealed_handle = ctx->nv;
+  tpm2key_policy_t policy_seq = NULL;
+  grub_err_t err;
+
+  /* Create a basic policy sequence based on the given PCR selection */
+  err = grub_tpm2_protector_simple_policy_seq (ctx, _seq);
+  if (err != GRUB_ERR_NONE)
+goto exit;
+
+  err = grub_tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size);
+
+  /* Pop error messages on success */
+  if (err == GRUB_ERR_NONE)
+while (grub_error_pop ());
+
+exit:
+  TPM2_FlushContext (sealed_handle);
  
-  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,

-N_("NV Index mode is not implemented yet"));
+  grub_tpm2key_free_policy_seq (policy_seq);
+
+  return err;
  }
  
  static grub_err_t


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


Re: [PATCH v11 13/20] util/grub-protect: Add new tool

2024-04-12 Thread Stefan Berger



On 4/12/24 04:39, Gary Lin via Grub-devel wrote:

From: Hernan Gatta 

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/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 \
 --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  -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  -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.

Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
  .gitignore  |2 +
  Makefile.util.def   |   22 +
  configure.ac|   30 +
  util/grub-protect.c | 1396 +++
  4 files changed, 1450 insertions(+)
  create mode 100644 util/grub-protect.c

diff --git a/.gitignore b/.gitignore
index 4d0dfb700..d7b7c22d6 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 19ad5a96f..a0a3e2cd5 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -207,6 +207,28 @@ program = {
ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
  };
  
+program = {

+  name = grub-protect;
+
+  common = grub-core/osdep/init.c;
+  common = grub-core/tpm2/args.c;
+  common = grub-core/tpm2/buffer.c;
+  common = grub-core/tpm2/mu.c;
+  common = grub-core/tpm2/tpm2.c;
+  common = grub-core/tpm2/tpm2key_asn1_tab.c;
+  common = util/grub-protect.c;
+  common = util/probe.c;
+
+  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 84a202c6e..3a07ab570 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])
@@ -2057,6 +2058,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=""
  
  

Re: [PATCH v11 12/20] cryptodisk: Support key protectors

2024-04-12 Thread Stefan Berger



On 4/12/24 04:39, Gary Lin via Grub-devel wrote:

From: Hernan Gatta 

Add a new parameter to cryptomount to support the key protectors framework: -P.
The parameter is used to automatically retrieve a key from specified key
protectors. The parameter may be repeated to specify any number of key
protectors. These are tried in order until one provides a usable key for any
given disk.

Signed-off-by: Hernan Gatta 
Signed-off-by: Michael Chang 
Signed-off-by: Gary Lin 
Reviewed-by: Glenn Washburn 
---
  Makefile.util.def   |   1 +
  grub-core/disk/cryptodisk.c | 172 +---
  include/grub/cryptodisk.h   |  16 
  3 files changed, 158 insertions(+), 31 deletions(-)

diff --git a/Makefile.util.def b/Makefile.util.def
index b53afb1d3..19ad5a96f 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -40,6 +40,7 @@ library = {
common = grub-core/disk/luks.c;
common = grub-core/disk/luks2.c;
common = grub-core/disk/geli.c;
+  common = grub-core/disk/key_protector.c;
common = grub-core/disk/cryptodisk.c;
common = grub-core/disk/AFSplitter.c;
common = grub-core/lib/pbkdf2.c;
diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 2246af51b..0ca1a5c4d 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -26,6 +26,7 @@
  #include 
  #include 
  #include 
+#include 
  
  #ifdef GRUB_UTIL

  #include 
@@ -44,7 +45,8 @@ enum
  OPTION_KEYFILE,
  OPTION_KEYFILE_OFFSET,
  OPTION_KEYFILE_SIZE,
-OPTION_HEADER
+OPTION_HEADER,
+OPTION_PROTECTOR
};
  
  static const struct grub_arg_option options[] =

@@ -58,6 +60,8 @@ static const struct grub_arg_option options[] =
  {"keyfile-offset", 'O', 0, N_("Key file offset (bytes)"), 0, 
ARG_TYPE_INT},
  {"keyfile-size", 'S', 0, N_("Key file data size (bytes)"), 0, 
ARG_TYPE_INT},
  {"header", 'H', 0, N_("Read header from file"), 0, ARG_TYPE_STRING},
+{"protector", 'P', GRUB_ARG_OPTION_REPEATABLE,
+ N_("Unlock volume(s) using key protector(s)."), 0, ARG_TYPE_STRING},
  {0, 0, 0, 0, 0, 0}
};
  
@@ -1061,6 +1065,7 @@ grub_cryptodisk_scan_device_real (const char *name,

grub_err_t ret = GRUB_ERR_NONE;
grub_cryptodisk_t dev;
grub_cryptodisk_dev_t cr;
+  int i;
struct cryptodisk_read_hook_ctx read_hook_data = {0};
int askpass = 0;
char *part = NULL;
@@ -1113,41 +1118,112 @@ grub_cryptodisk_scan_device_real (const char *name,
goto error_no_close;
  if (!dev)
continue;
+break;
+  }
  
-if (!cargs->key_len)

-  {
-   /* Get the passphrase from the user, if no key data. */
-   askpass = 1;
-   part = grub_partition_get_name (source->partition);
-   grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name,
-source->partition != NULL ? "," : "",
-part != NULL ? part : N_("UNKNOWN"),
-dev->uuid);
-   grub_free (part);
-
-   cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE);
-   if (cargs->key_data == NULL)
- goto error_no_close;
-
-   if (!grub_password_get ((char *) cargs->key_data, 
GRUB_CRYPTODISK_MAX_PASSPHRASE))
- {
-   grub_error (GRUB_ERR_BAD_ARGUMENT, "passphrase not supplied");
-   goto error;
- }
-   cargs->key_len = grub_strlen ((char *) cargs->key_data);
-  }
+  if (dev == NULL)
+{
+  grub_error (GRUB_ERR_BAD_MODULE,
+ "no cryptodisk module can handle this device");
+  goto error_no_close;
+}
  
-ret = cr->recover_key (source, dev, cargs);

-if (ret != GRUB_ERR_NONE)
-  goto error;
+  if (cargs->protectors)
+{
+  for (i = 0; cargs->protectors[i]; i++)
+   {
+ if (cargs->key_cache[i].invalid)
+   continue;
+
+ if (cargs->key_cache[i].key == NULL)
+   {
+ ret = grub_key_protector_recover_key (cargs->protectors[i],
+   >key_cache[i].key,
+   
>key_cache[i].key_len);
+ if (ret != GRUB_ERR_NONE)
+   {
+ if (grub_errno)
+   {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+   }
+
+ grub_dprintf ("cryptodisk",
+   "failed to recover a key from key protector "
+   "%s, will not try it again for any other "
+   "disks, if any, during this invocation of "
+   "cryptomount\n",
+   cargs->protectors[i]);
+
+ cargs->key_cache[i].invalid = 1;
+ continue;
+   }
+   }
+
+ cargs->key_data = cargs->key_cache[i].key;
+ cargs->key_len = cargs->key_cache[i].key_len;
  
-ret = 

Re: [PATCH v11 11/20] key_protector: Add TPM2 Key Protector

2024-04-12 Thread Stefan Berger



On 4/12/24 04:39, Gary Lin via Grub-devel wrote:

From: Hernan Gatta 

The TPM2 key protector is a module that enables the automatic retrieval
of a fully-encrypted disk's unlocking key from a TPM 2.0.


A few minor finds/comments below.



The theory of operation is such that the module accepts various
arguments, most of which are optional and therefore possess reasonable
defaults. One of these arguments is the keyfile/tpm2key parameter, which
is mandatory. There are two supported key formats:

1. Raw Sealed Key (--keyfile)
When sealing a key with TPM2_Create, the public portion of the sealed
key is stored in TPM2B_PUBLIC, and the private portion is in
TPM2B_PRIVATE. The raw sealed key glues the fully marshalled
TPM2B_PUBLIC and TPM2B_PRIVATE into one file.

2. TPM 2.0 Key (--tpm2key)
The following is the ASN.1 definition of TPM 2.0 Key File:

TPMPolicy ::= SEQUENCE {
  CommandCode   [0] EXPLICIT INTEGER
  CommandPolicy [1] EXPLICIT OCTET STRING
}

TPMAuthPolicy ::= SEQUENCE {
  Name[0] EXPLICIT UTF8STRING OPTIONAL
  Policy  [1] EXPLICIT SEQUENCE OF TPMPolicy
}

TPMKey ::= SEQUENCE {
  typeOBJECT IDENTIFIER
  emptyAuth   [0] EXPLICIT BOOLEAN OPTIONAL
  policy  [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL
  secret  [2] EXPLICIT OCTET STRING OPTIONAL
  authPolicy  [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL
  parent  INTEGER
  pubkey  OCTET STRING
  privkey OCTET STRING
}

   The TPM2 key protector only expects a "sealed" key in DER encoding,
   so 'type' is always 2.23.133.10.1.5, 'emptyAuth' is 'TRUE', and
   'secret' is empty. 'policy' and 'authPolicy' are the possible policy
   command sequences to construst the policy digest to unseal the key.
   Similar to the raw sealed key, the public portion (TPM2B_PUBLIC) of
   the sealed key is stored in 'pubkey', and the private portion
   (TPM2B_PRIVATE) is in 'privkey'.

   For more details: 
https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html




+
+/* Get the SRK with the template */
+static grub_err_t
+grub_tpm2_protector_srk_get (const grub_srk_type_t srk_type,
+const TPM_HANDLE parent,
+TPM_HANDLE *srk_handle)
+{
+  TPM_RC rc;
+  TPMT_PUBLIC_PARMS parms = { 0 };
+  TPMS_AUTH_COMMAND authCommand = { 0 };
+  TPM2B_SENSITIVE_CREATE inSensitive = { 0 };
+  TPM2B_PUBLIC inPublic = { 0 };
+  TPM2B_DATA outsideInfo = { 0 };
+  TPML_PCR_SELECTION creationPcr = { 0 };
+  TPM2B_PUBLIC outPublic = { 0 };
+  TPM2B_CREATION_DATA creationData = { 0 };
+  TPM2B_DIGEST creationHash = { 0 };
+  TPMT_TK_CREATION creationTicket = { 0 };
+  TPM2B_NAME srkName = { 0 };
+  TPM_HANDLE tmp_handle = 0;
+
+  inPublic.publicArea.type = srk_type.type;
+  inPublic.publicArea.nameAlg = TPM_ALG_SHA256;
+  inPublic.publicArea.objectAttributes.restricted = 1;
+  inPublic.publicArea.objectAttributes.userWithAuth = 1;
+  inPublic.publicArea.objectAttributes.decrypt = 1;
+  inPublic.publicArea.objectAttributes.fixedTPM = 1;
+  inPublic.publicArea.objectAttributes.fixedParent = 1;
+  inPublic.publicArea.objectAttributes.sensitiveDataOrigin = 1;
+  inPublic.publicArea.objectAttributes.noDA = 1;
+
+  if (srk_type.type == TPM_ALG_RSA)
+{
+  inPublic.publicArea.parameters.rsaDetail.symmetric.algorithm = 
TPM_ALG_AES;
+  inPublic.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128;
+  inPublic.publicArea.parameters.rsaDetail.symmetric.mode.aes = 
TPM_ALG_CFB;
+  inPublic.publicArea.parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL;
+  inPublic.publicArea.parameters.rsaDetail.keyBits = 
srk_type.detail.rsa_bits;


In the officail TCG EK templates they pair RSA3072 with AES 256 and 
SHA384. I wonder whether this would not also make sense here? Similar 
for ECC but I think you only use NIST P256 here iiuc.



+  inPublic.publicArea.parameters.rsaDetail.exponent = 0;
+}
+  else if (srk_type.type == TPM_ALG_ECC)
+{
+  inPublic.publicArea.parameters.eccDetail.symmetric.algorithm = 
TPM_ALG_AES;
+  inPublic.publicArea.parameters.eccDetail.symmetric.keyBits.aes = 128;
+  inPublic.publicArea.parameters.eccDetail.symmetric.mode.aes = 
TPM_ALG_CFB;
+  inPublic.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_NULL;
+  inPublic.publicArea.parameters.eccDetail.curveID = 
srk_type.detail.ecc_curve;
+  inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
+}
+  else
+return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Unknown SRK algorithm"));
+
+  /* Test the parameters before SRK generation */
+  parms.type = srk_type.type;
+  grub_memcpy (, ,
+  sizeof (TPMU_PUBLIC_PARMS));
+
+  rc = TPM2_TestParms (, NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_ARGUMENT,
+  N_("Unsupported SRK template (TPM2_TestParms: 0x%x)"),
+  rc);
+
+  /* Create SRK */
+  

Re: [PATCH v11 09/20] key_protector: Add key protectors framework

2024-04-12 Thread Stefan Berger



On 4/12/24 04:39, Gary Lin via Grub-devel wrote:

From: Hernan Gatta 

A key protector encapsulates functionality to retrieve an unlocking key
for a fully-encrypted disk from a specific source. A key protector
module registers itself with the key protectors framework when it is
loaded and unregisters when unloaded. Additionally, a key protector may
accept parameters that describe how it should operate.

The key protectors framework, besides offering registration and
unregistration functions, also offers a one-stop routine for finding and
invoking a key protector by name. If a key protector with the specified
name exists and if an unlocking key is successfully retrieved by it, the
function returns to the caller the retrieved key and its length.

Cc: Vladimir Serbinenko 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
  grub-core/Makefile.am  |  1 +
  grub-core/Makefile.core.def|  5 +++
  grub-core/disk/key_protector.c | 78 ++
  include/grub/key_protector.h   | 46 
  4 files changed, 130 insertions(+)
  create mode 100644 grub-core/disk/key_protector.c
  create mode 100644 include/grub/key_protector.h

diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
index f18550c1c..9d3d5f519 100644
--- a/grub-core/Makefile.am
+++ b/grub-core/Makefile.am
@@ -90,6 +90,7 @@ endif
  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm.h
  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/parser.h
  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/partition.h
+KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/key_protector.h
  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/stack_protector.h
  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/term.h
  KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/time.h
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index fe2a2da90..2d6871e7c 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1278,6 +1278,11 @@ module = {
common = disk/raid6_recover.c;
  };
  
+module = {

+  name = key_protector;
+  common = disk/key_protector.c;
+};
+
  module = {
name = scsi;
common = disk/scsi.c;
diff --git a/grub-core/disk/key_protector.c b/grub-core/disk/key_protector.c
new file mode 100644
index 0..31de798d6
--- /dev/null
+++ b/grub-core/disk/key_protector.c
@@ -0,0 +1,78 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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 .
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+struct grub_key_protector *grub_key_protectors = NULL;
+
+grub_err_t
+grub_key_protector_register (struct grub_key_protector *protector)
+{
+  if (protector == NULL || protector->name == NULL || 
grub_strlen(protector->name) == 0)


forgot space: grub_strlen (...)


+return GRUB_ERR_BAD_ARGUMENT;
+
+  if (grub_key_protectors &&
+  grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors),
+   protector->name))
+return GRUB_ERR_BAD_ARGUMENT;
+
+  grub_list_push (GRUB_AS_LIST_P (_key_protectors),
+ GRUB_AS_LIST (protector));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_key_protector_unregister (struct grub_key_protector *protector)
+{
+  if (protector == NULL)
+return GRUB_ERR_BAD_ARGUMENT;
+
+  grub_list_remove (GRUB_AS_LIST (protector));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_key_protector_recover_key (const char *protector, grub_uint8_t **key,
+   grub_size_t *key_size)
+{
+  struct grub_key_protector *kp = NULL;
+
+  if (grub_key_protectors == NULL)
+return GRUB_ERR_OUT_OF_RANGE;
+
+  if (protector == NULL || grub_strlen (protector) == 0)
+return GRUB_ERR_BAD_ARGUMENT;
+
+  kp = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors),
+protector);
+  if (kp == NULL)
+return grub_error (GRUB_ERR_OUT_OF_RANGE,
+  N_("A key protector with name '%s' could not be found. "
+ "Is the name spelled correctly and is the "
+ "corresponding module loaded?"), protector);
+
+  return kp->recover_key (key, key_size);
+}
diff --git a/include/grub/key_protector.h b/include/grub/key_protector.h
new file mode 100644
index 0..6e6a6fb24
--- 

Re: [PATCH v11 00/20] Automatic Disk Unlock with TPM2

2024-04-12 Thread Stefan Berger



On 4/12/24 04:39, Gary Lin via Grub-devel wrote:

GIT repo for v11: https://github.com/lcp/grub2/tree/tpm2-unlock-v11

This patch series is based on "Automatic TPM Disk Unlock"(*1) posted by
Hernan Gatta to introduce the key protector framework and TPM2 stack
to GRUB2, and this could be a useful feature for the systems to
implement full disk encryption.


You also need to extend the documentation with the command line steps 
and a IMO there has to be a warning for VM users that sealing to PCRs 
inside a VM is dangerous since the next packages update may bring an 
update to TianoCore UEFI/SeaBIOS/SLOF/... showing different PCR values 
and unsealing will not work then.



   Stefan


To support TPM 2.0 Key File format(*2), patch 1~5,7 are grabbed from
Daniel Axtens's "appended signature secure boot support" (*3) to import
libtasn1 into grub2. Besides, the libtasn1 version is upgraded to
4.19.0 instead of 4.16.0 in the original patch.

Patch 6 fixes a potential buffer overrun in libtasn1.
(https://gitlab.com/gnutls/libtasn1/-/issues/49)

Patch 8 adds the document for libtasn1 and the steps to upgrade the
library.

Patch 9~13 are Hernan Gatta's patches with the follow-up fixes and
improvements:
- Converting 8 spaces into 1 tab
- Merging the minor build fix from Michael Chang
   - Replacing "lu" with "PRIuGRUB_SIZE" for grub_dprintf
   - Adding "enable = efi" to the tpm2 module in grub-core/Makefile.core.def
- Rebasing "cryptodisk: Support key protectors" to the git master
- Removing the measurement on the sealed key
   - Based on the patch from Olaf Kirch 
- Adjusting the input parameters of TPM2_EvictControl to match the order
   in "TCG TPM2 Part3 Commands"
- Declaring the input arguments of TPM2 functions as const
- Resending TPM2 commands on TPM_RC_RETRY
- Adding checks for the parameters of TPM2 commands
- Packing the missing authorization command for TPM2_PCR_Read
- Tweaking the TPM2 command functions to allow some parameters to be
   NULL so that we don't have to declare empty variables
- Only enabling grub-protect for "efi" since the TPM2 stack currently
   relies on the EFI TCG2 protocol to send TPM2 commands
- Using grub_cpu_to_be*() in the TPM2 stack instead of grub_swap_bytes*()
   which may cause problems in big-indian machines
- Changing the short name of "--protector" of "cryptomount" from "-k" to
   "-P" to avoid the conflict with "--key-file"
- Supporting TPM 2.0 Key File Format besides the raw sealed key
- Adding the external libtasn1 dependency to grub-protect to write the
   TPM 2.0 Key files
- Extending the TPM2 TSS stack to support authorized policy

Patch 14 implements the authorized policy support.

Patch 15 implements the missing NV index mode. (Thanks to Patrick Colp)

Patch 16 improves the 'cryptomount' command to fall back to the
passphrase mode when the key protector fails to unlock the encrypted
partition. (Another patch from Patrick Colp)

Patch 17 and 18 fix the potential security issues spotted by Fabian Vogt.

Patch 19 and 20 implement the TPM key unsealing testcases.

To utilize the TPM2 key protector to unlock the encrypted partition
(sdb1), here are the sample steps:

1. Add an extra random key for LUKS (luks-key)
$ dd if=/dev/urandom of=luks-key bs=1 count=32
$ sudo cryptsetup luksAddKey /dev/sdb1 luks-key --pbkdf=pbkdf2

2. Seal the key
$ sudo grub-protect --action=add \
--protector=tpm2 \
--tpm2key \
--tpm2-keyfile=luks-key \
--tpm2-outfile=/boot/efi/boot/grub2/sealed.tpm

3. Unseal the key with the proper commands in grub.cfg:
tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
cryptomount -u  -P tpm2

(*1) https://lists.gnu.org/archive/html/grub-devel/2022-02/msg6.html
(*2) https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
(*3) https://lists.gnu.org/archive/html/grub-devel/2021-06/msg00044.html

v11:
- Adding the missing default: handlers in grub-core/tpm2/mu.c
- Updating the help messages and commit messages to reflect the change
   of the default SRK algorithm (RSA2048 -> ECC_NIST_P256)
- Adding the testcase for the NV index mode

v10:
- https://lists.gnu.org/archive/html/grub-devel/2024-04/msg00019.html
- GIT repo: https://github.com/lcp/grub2/tree/tpm2-unlock-v10
- Fixing the coverity issues: CID 435775, CID 435771, CID 435770, CID
   435769, CID 435767, CID 435761
   https://lists.gnu.org/archive/html/grub-devel/2024-02/txtKIuUb5lf3O.txt
   - Fixing the potential memory leak (CID 435775)
   - Removing the unnecessary grub_protect_get_grub_drive_for_file() from
 util/grub-protect.c (CID 435771)
   - Using the grub_tpm2_mu_TPM2B_*_Unmarshal functions to unmarshal the
 TPM2B structs instead of a generic grub_tpm2_mu_TPM2B_Unmarshal
 (CID 435770)
   - Fixing Null pointer dereference (CID 435769)
   - Adding bound checks to grub_tpm2_mu_TPML_DIGEST_Unmarshal()
 (CID 435767)
   - Improving the check for 

Re: [PATCH v11 14/20] tpm2: Support authorized policy

2024-04-12 Thread Stefan Berger



On 4/12/24 04:39, Gary Lin via Grub-devel wrote:

This commit handles the TPM2_PolicyAuthorize command from the key file
in TPM 2.0 Key File format.

TPM2_PolicyAuthorize is the essential command to support authorized
policy which allows the users to sign TPM policies with their own keys.
Per TPM 2.0 Key File(*1), CommandPolicy for TPM2_PolicyAuthorize
comprises 'TPM2B_PUBLIC pubkey', 'TPM2B_DIGEST policy_ref', and
'TPMT_SIGNATURE signature'. To verify the signature, the current policy
digest is hashed with the hash algorithm written in 'signature', and then
'signature' is verified with the hashed policy digest and 'pubkey'. Once
TPM accepts 'signature', TPM2_PolicyAuthorize is invoked to authorize the
signed policy.

To create the key file with authorized policy, here are the pcr-oracle(*2)
commands:

   # Generate the RSA key and create the authorized policy file
   $ pcr-oracle \
--rsa-generate-key \
--private-key policy-key.pem \
--auth authorized.policy \
create-authorized-policy 0,2,4,7,9

   # Seal the secret with the authorized policy
   $ pcr-oracle \
--key-format tpm2.0 \
--auth authorized.policy \
--input disk-secret.txt \
--output sealed.key \
seal-secret

   # Sign the predicted PCR policy
   $ pcr-oracle \
--key-format tpm2.0 \
--private-key policy-key.pem \
--from eventlog \
--stop-event "grub-file=grub.cfg" \
--after \
--input sealed.key \
--output sealed.tpm \
sign 0,2,4,7.9


typo: ...,4,7,9



Then specify the key file and the key protector to grub.cfg in the EFI
system partition:

tpm2_key_protector_init -a RSA --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
cryptomount -u  -P tpm2

For any change in the boot components, just run the 'sign' command again
to update the signature in sealed.tpm, and TPM can unseal the key file
with the updated PCR policy.

(*1) https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
(*2) https://github.com/okirch/pcr-oracle

Signed-off-by: Gary Lin 


Reviewed-by: Stefan Berger 

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


Re: [PATCH v10 20/20] tests: Add tpm2_test

2024-04-12 Thread Stefan Berger



On 4/9/24 04:30, Gary Lin via Grub-devel wrote:

For the tpm2 module, the TCG2 command submission function is the only
difference between the a QEMU instance and grub-emu. To test TPM key
unsealing with a QEMU instance, it requires an extra OS image to invoke
grub-protect to seal the LUKS key, rather than a simple grub-shell rescue
CD image. On the other hand, grub-emu can share the emulated TPM device
with the host, so that we can seal the LUKS key on host and test key
unsealing with grub-emu.

This test script firstly creates a simple LUKS image to be loaded as a
loopback device in grub-emu. Then an emulated TPM device is created by
swtpm_cuse and PCR 0 and 1 are extended.


My concern here would be that distros for example don't build swtpm with 
the CUSE interface but build it with the socket and chardev interfaces. 
If you don't want users to have to build their own version of swtpm then 
I would suggest to use either 'swtpm chardev' with the vtpm_proxy module 
(it is quite commonly available on recent distros) or 'swtpm socket'.


The chardev usage is for example described here and should be least 
involved to convert to:


https://github.com/stefanberger/swtpm/wiki/Using-the-Intel-TSS-with-swtpm#character-device-using-tpm_vtpm_proxy

You could start it like this with UnixIO control port (needs adjustment 
to swtpm_ioctl in your code).


swtpm chardev --vtpm-proxy --tpmstate dir=/tmp/myvtpm --tpm2 --ctrl 
type=unixio,path=/tmp/myvtpm/ctrl --flags startup-clear --daemon > logfile


No need to run 'swtpm_ioctl -i' and tpm2_startup with the startup-clear 
passed.


One of my test cases determines the created device (/dev/tpmXYZ) like this:

for ((i = 0; i < 200; i ++)); do
if [ -z "${TPM_DEVICE}" ]; then
TPM_DEVICE=$(sed -n 's,.*\(/dev/tpm[0-9]\+\).*,\1,p' 
"logfile")

if [ -n "${TPM_DEVICE}" ]; then
echo "Using ${TPM_DEVICE}."
fi
fi
if [ -n "${TPM_DEVICE}" ]; then
[ -c "${TPM_DEVICE}" ] && break
fi
sleep 0.1
done

shutdown: sudo swtpm_ioctl -s --unix /tmp/myvtpm/ctrl


Otherwise you could use the socket version described here:

https://github.com/stefanberger/swtpm/wiki/Using-the-Intel-TSS-with-swtpm#socket-interface

It needs the swtpm tcti as a dependency, though.

   Stefan



There are several test cases in the script to test various settings. Each
test case uses grub-protect to seal the LUKS password against PCR 0 and
PCR 1. Then grub-emu is launched to load the LUKS image, try to mount
the image with tpm2_key_protector_init and cryptomount, and verify the
result.

Based on the idea from Michael Chang.

Cc: Michael Chang 
Signed-off-by: Gary Lin 
---
  Makefile.util.def|   6 +
  tests/tpm2_test.in   | 284 +++
  tests/util/grub-shell.in |   6 +-
  3 files changed, 295 insertions(+), 1 deletion(-)
  create mode 100644 tests/tpm2_test.in

diff --git a/Makefile.util.def b/Makefile.util.def
index a0a3e2cd5..77bbdd453 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -1279,6 +1279,12 @@ script = {
common = tests/asn1_test.in;
  };
  
+script = {

+  testcase = native;
+  name = tpm2_test;
+  common = tests/tpm2_test.in;
+};
+
  program = {
testcase = native;
name = example_unit_test;
diff --git a/tests/tpm2_test.in b/tests/tpm2_test.in
new file mode 100644
index 0..8d4e96f47
--- /dev/null
+++ b/tests/tpm2_test.in
@@ -0,0 +1,284 @@
+#! @BUILD_SHEBANG@ -e
+
+# Test GRUBs ability to unseal a LUKS key with TPM 2.0
+# 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 .
+
+grubshell=@builddir@/grub-shell
+
+. "@builddir@/grub-core/modinfo.sh"
+
+if [ x$grub_modinfo_platform != xemu ]; then
+  exit 77
+fi
+
+builddir="@builddir@"
+
+# Force build directory components
+PATH="${builddir}:$PATH"
+export PATH
+
+if [ "x$EUID" = "x" ] ; then
+  EUID=`id -u`
+fi
+
+if [ "$EUID" != 0 ] ; then
+   echo "not root; cannot test tpm2."
+   exit 99
+fi
+
+if ! which cryptsetup >/dev/null 2>&1; then
+   echo "cryptsetup not installed; cannot test tpm2."
+   exit 99
+fi
+
+if ! which swtpm >/dev/null 2>&1; then
+   echo "swtpm not installed; cannot test tpm2."
+   exit 99
+fi
+
+if ! which tpm2_startup >/dev/null 2>&1; then
+   echo "tpm2-tools not installed; cannot test tpm2."
+   exit 99
+fi
+

Re: [PATCH v11 10/20] tpm2: Add TPM Software Stack (TSS)

2024-04-12 Thread Stefan Berger



On 4/12/24 04:39, Gary Lin wrote:

From: Hernan Gatta 

A Trusted Platform Module (TPM) Software Stack (TSS) provides logic to
compose, submit, and parse TPM commands and responses.


compose and submit TPM commands and parse reponses.


A limited number of TPM commands may be accessed via the EFI TCG2
protocol. This protocol exposes functionality that is primarily geared
toward TPM usage within the context of Secure Boot. For all other TPM
commands, however, such as sealing and unsealing, this protocol does not
provide any help, with the exception of passthrough command submission.


Other platforms don't have EFI at all...



The SubmitCommand method allows a caller to send raw commands to the
system's TPM and to receive the corresponding response. These
command/response pairs are formatted using the TPM wire protocol. To
construct commands in this way, and to parse the TPM's response, it is
necessary to, first, possess knowledge of the various TPM structures, and,
second, of the TPM wire protocol itself.

As such, this patch includes a set of header files that define the
necessary TPM structures and TSS functions, implementations of various
TPM2_* functions (inventoried below), and logic to write and read command
and response buffers, respectively, using the TPM wire protocol.

Functions: TPM2_Create, TPM2_CreatePrimary, TPM2_EvictControl,
TPM2_FlushContext, TPM2_Load, TPM2_PCR_Read, TPM2_PolicyGetDigest,
TPM2_PolicyPCR, TPM2_ReadPublic, TPM2_StartAuthSession, TPM2_Unseal,
TPM2_LoadExternal, TPM2_HashSequenceStart, TPM2_SequenceUpdate,
TPM2_SequenceComplete, TPM2_Hash, TPM2_VerifySignature,


HashSequenceStart, SequenceUpdate and SequenceComplete don't have 
callers and could be removed.


   Stefan

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


[PATCH v11 13/20] util/grub-protect: Add new tool

2024-04-12 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

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/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 \
--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  -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  -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.

Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 .gitignore  |2 +
 Makefile.util.def   |   22 +
 configure.ac|   30 +
 util/grub-protect.c | 1396 +++
 4 files changed, 1450 insertions(+)
 create mode 100644 util/grub-protect.c

diff --git a/.gitignore b/.gitignore
index 4d0dfb700..d7b7c22d6 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 19ad5a96f..a0a3e2cd5 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -207,6 +207,28 @@ program = {
   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
 };
 
+program = {
+  name = grub-protect;
+
+  common = grub-core/osdep/init.c;
+  common = grub-core/tpm2/args.c;
+  common = grub-core/tpm2/buffer.c;
+  common = grub-core/tpm2/mu.c;
+  common = grub-core/tpm2/tpm2.c;
+  common = grub-core/tpm2/tpm2key_asn1_tab.c;
+  common = util/grub-protect.c;
+  common = util/probe.c;
+
+  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 84a202c6e..3a07ab570 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])
@@ -2057,6 +2058,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])
@@ -2173,6 +2197,7 @@ AM_CONDITIONAL([COND_GRUB_EMU_SDL], [test 

[PATCH v11 18/20] diskfilter: look up cryptodisk devices first

2024-04-12 Thread Gary Lin via Grub-devel
When using disk auto-unlocking with TPM 2.0, the typical grub.cfg may
look like this:

  tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
  cryptomount -u  -P tpm2
  search --fs-uuid --set=root 

Since the disk search order is based on the order of module loading, the
attacker could insert a malicious disk with the same FS-UUID root to
trick grub2 to boot into th malicious root and further dump memory to
steal the unsealed key.

To defend such attack, we can specify the hint provided by 'grub-probe'
to search the encrypted partition first:

search --fs-uuid --set=root --hint='cryptouuid/' 

However, for LVM on a encrypted partition, the search hint provided by
'grub-probe' is:

  --hint='lvmid//'

It doesn't guarantee to look up the logical volume from the encrypted
partition, so the attacker may have the chance to fool grub2 to boot
into the malicious disk.

To mininize the attack surface, this commit tweaks the disk device search
in diskfilter to look up cryptodisk devices first and then others, so
that the auto-unlocked disk will be found first, not the attacker's disk.

Signed-off-by: Gary Lin 
Cc: Fabian Vogt 
---
 grub-core/disk/diskfilter.c | 35 ++-
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c
index 21e239511..df1992305 100644
--- a/grub-core/disk/diskfilter.c
+++ b/grub-core/disk/diskfilter.c
@@ -226,15 +226,32 @@ scan_devices (const char *arname)
   int need_rescan;
 
   for (pull = 0; pull < GRUB_DISK_PULL_MAX; pull++)
-for (p = grub_disk_dev_list; p; p = p->next)
-  if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID
- && p->disk_iterate)
-   {
- if ((p->disk_iterate) (scan_disk_hook, NULL, pull))
-   return;
- if (arname && is_lv_readable (find_lv (arname), 1))
-   return;
-   }
+{
+  /* look up the crytodisk devices first */
+  for (p = grub_disk_dev_list; p; p = p->next)
+   if (p->id == GRUB_DISK_DEVICE_CRYPTODISK_ID
+   && p->disk_iterate)
+ {
+   if ((p->disk_iterate) (scan_disk_hook, NULL, pull))
+ return;
+   if (arname && is_lv_readable (find_lv (arname), 1))
+ return;
+   break;
+ }
+
+  /* check the devices other than crytodisk */
+  for (p = grub_disk_dev_list; p; p = p->next)
+   if (p->id == GRUB_DISK_DEVICE_CRYPTODISK_ID)
+ continue;
+   else if (p->id != GRUB_DISK_DEVICE_DISKFILTER_ID
+   && p->disk_iterate)
+ {
+   if ((p->disk_iterate) (scan_disk_hook, NULL, pull))
+ return;
+   if (arname && is_lv_readable (find_lv (arname), 1))
+ return;
+ }
+}
 
   scan_depth = 0;
   need_rescan = 1;
-- 
2.35.3


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


[PATCH v11 11/20] key_protector: Add TPM2 Key Protector

2024-04-12 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

The TPM2 key protector is a module that enables the automatic retrieval
of a fully-encrypted disk's unlocking key from a TPM 2.0.

The theory of operation is such that the module accepts various
arguments, most of which are optional and therefore possess reasonable
defaults. One of these arguments is the keyfile/tpm2key parameter, which
is mandatory. There are two supported key formats:

1. Raw Sealed Key (--keyfile)
   When sealing a key with TPM2_Create, the public portion of the sealed
   key is stored in TPM2B_PUBLIC, and the private portion is in
   TPM2B_PRIVATE. The raw sealed key glues the fully marshalled
   TPM2B_PUBLIC and TPM2B_PRIVATE into one file.

2. TPM 2.0 Key (--tpm2key)
   The following is the ASN.1 definition of TPM 2.0 Key File:

   TPMPolicy ::= SEQUENCE {
 CommandCode   [0] EXPLICIT INTEGER
 CommandPolicy [1] EXPLICIT OCTET STRING
   }

   TPMAuthPolicy ::= SEQUENCE {
 Name[0] EXPLICIT UTF8STRING OPTIONAL
 Policy  [1] EXPLICIT SEQUENCE OF TPMPolicy
   }

   TPMKey ::= SEQUENCE {
 typeOBJECT IDENTIFIER
 emptyAuth   [0] EXPLICIT BOOLEAN OPTIONAL
 policy  [1] EXPLICIT SEQUENCE OF TPMPolicy OPTIONAL
 secret  [2] EXPLICIT OCTET STRING OPTIONAL
 authPolicy  [3] EXPLICIT SEQUENCE OF TPMAuthPolicy OPTIONAL
 parent  INTEGER
 pubkey  OCTET STRING
 privkey OCTET STRING
   }

  The TPM2 key protector only expects a "sealed" key in DER encoding,
  so 'type' is always 2.23.133.10.1.5, 'emptyAuth' is 'TRUE', and
  'secret' is empty. 'policy' and 'authPolicy' are the possible policy
  command sequences to construst the policy digest to unseal the key.
  Similar to the raw sealed key, the public portion (TPM2B_PUBLIC) of
  the sealed key is stored in 'pubkey', and the private portion
  (TPM2B_PRIVATE) is in 'privkey'.

  For more details: 
https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html

This sealed key file is created via the grub-protect tool. The tool
utilizes the TPM's sealing functionality to seal (i.e., encrypt) an
unlocking key using a Storage Root Key (SRK) to the values of various
Platform Configuration Registers (PCRs). These PCRs reflect the state
of the system as it boots. If the values are as expected, the system
may be considered trustworthy, at which point the TPM allows for a
caller to utilize the private component of the SRK to unseal (i.e.,
decrypt) the sealed key file. The caller, in this case, is this key
protector.

The TPM2 key protector registers two commands:

- tpm2_key_protector_init: Initializes the state of the TPM2 key
   protector for later usage, clearing any
   previous state, too, if any.

- tpm2_key_protector_clear: Clears any state set by tpm2_key_protector_init.

The way this is expected to be used requires the user to, either
interactively or, normally, via a boot script, initialize/configure
the key protector and then specify that it be used by the 'cryptomount'
command (modifications to this command are in a different patch).

For instance, to unseal the raw sealed key file:

tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub2/sealed-1.key
cryptomount -u  -P tpm2

tpm2_key_protector_init --keyfile=(hd0,gpt1)/efi/grub2/sealed-2.key --pcrs=7,11
cryptomount -u  -P tpm2

Or, to unseal the TPM 2.0 Key file:

tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub2/sealed-1.tpm
cryptomount -u  -P tpm2

tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub2/sealed-2.tpm --pcrs=7,11
cryptomount -u  -P tpm2

If a user does not initialize the key protector and attempts to use it
anyway, the protector returns an error.

Before unsealing the key, the TPM2 key protector follows the "TPMPolicy"
sequences to enforce the TPM policy commands to construct a valid policy
digest to unseal the key.

For the TPM 2.0 Key files, 'authPolicy' may contain multiple "TPMPolicy"
sequences, the TPM2 key protector iterates 'authPolicy' to find a valid
sequence to unseal key. If 'authPolicy' is empty or all sequences in
'authPolicy' fail, the protector tries the one from 'policy'. In case
'policy' is also empty, the protector creates a "TPMPolicy" sequence
based on the given PCR selection.

For the raw sealed key, the TPM2 key protector treats the key file as a
TPM 2.0 Key file without 'authPolicy' and 'policy', so the "TPMPolicy"
sequence is always based on the PCR selection from the command
parameters.

This commit only supports one policy command: TPM2_PolicyPCR. The
command set will be extended to support advanced features, such as
authorized policy, in the later commits.

Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 grub-core/Makefile.core.def   |   13 +
 grub-core/tpm2/args.c |  175 +
 grub-core/tpm2/module.c   | 1214 +
 grub-core/tpm2/tpm2key.asn|   31 +
 grub-core/tpm2/tpm2key.c  |  447 +++
 grub-core/tpm2/tpm2key_asn1_tab.c |  

[PATCH v11 20/20] tests: Add tpm2_test

2024-04-12 Thread Gary Lin via Grub-devel
For the tpm2 module, the TCG2 command submission function is the only
difference between the a QEMU instance and grub-emu. To test TPM key
unsealing with a QEMU instance, it requires an extra OS image to invoke
grub-protect to seal the LUKS key, rather than a simple grub-shell rescue
CD image. On the other hand, grub-emu can share the emulated TPM device
with the host, so that we can seal the LUKS key on host and test key
unsealing with grub-emu.

This test script firstly creates a simple LUKS image to be loaded as a
loopback device in grub-emu. Then an emulated TPM device is created by
swtpm_cuse and PCR 0 and 1 are extended.

There are several test cases in the script to test various settings. Each
test case uses grub-protect or tpm2-tools to seal the LUKS password
against PCR 0 and PCR 1. Then grub-emu is launched to load the LUKS image,
try to mount the image with tpm2_key_protector_init and cryptomount, and
verify the result.

Based on the idea from Michael Chang.

Cc: Michael Chang 
Signed-off-by: Gary Lin 
---
 Makefile.util.def|   6 +
 tests/tpm2_test.in   | 351 +++
 tests/util/grub-shell.in |   6 +-
 3 files changed, 362 insertions(+), 1 deletion(-)
 create mode 100644 tests/tpm2_test.in

diff --git a/Makefile.util.def b/Makefile.util.def
index a0a3e2cd5..77bbdd453 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -1279,6 +1279,12 @@ script = {
   common = tests/asn1_test.in;
 };
 
+script = {
+  testcase = native;
+  name = tpm2_test;
+  common = tests/tpm2_test.in;
+};
+
 program = {
   testcase = native;
   name = example_unit_test;
diff --git a/tests/tpm2_test.in b/tests/tpm2_test.in
new file mode 100644
index 0..bea11e3a0
--- /dev/null
+++ b/tests/tpm2_test.in
@@ -0,0 +1,351 @@
+#! @BUILD_SHEBANG@ -e
+
+# Test GRUBs ability to unseal a LUKS key with TPM 2.0
+# 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 .
+
+grubshell=@builddir@/grub-shell
+
+. "@builddir@/grub-core/modinfo.sh"
+
+if [ x$grub_modinfo_platform != xemu ]; then
+  exit 77
+fi
+
+builddir="@builddir@"
+
+# Force build directory components
+PATH="${builddir}:$PATH"
+export PATH
+
+if [ "x$EUID" = "x" ] ; then
+  EUID=`id -u`
+fi
+
+if [ "$EUID" != 0 ] ; then
+   echo "not root; cannot test tpm2."
+   exit 99
+fi
+
+if ! which cryptsetup >/dev/null 2>&1; then
+   echo "cryptsetup not installed; cannot test tpm2."
+   exit 99
+fi
+
+if ! which swtpm >/dev/null 2>&1; then
+   echo "swtpm not installed; cannot test tpm2."
+   exit 99
+fi
+
+if ! which tpm2_startup >/dev/null 2>&1; then
+   echo "tpm2-tools not installed; cannot test tpm2."
+   exit 99
+fi
+
+tpm2testdir="`mktemp -d "${TMPDIR:-/tmp}/$(basename "$0").XX"`" || 
exit 20
+
+disksize=20M
+
+luksfile=$tpm2testdir/luks.disk
+lukskeyfile=${tpm2testdir}/password.txt
+
+# Choose a low iteration number to reduce the time to decrypt the disk
+csopt="--type luks2 --pbkdf pbkdf2 --iter-time 1000"
+
+tpm2devname="vtpm-test0"
+tpm2statedir=${tpm2testdir}/tpm
+tpm2dev="/dev/${tpm2devname}"
+
+sealedkey=${tpm2testdir}/sealed.tpm
+
+timeout=20
+
+testoutput=$tpm2testdir/testoutput
+
+vtext="TEST VERIFIED"
+
+# Create the password file
+echo -n "top secret" > ${lukskeyfile}
+
+# Setup LUKS2 image
+truncate -s ${disksize} ${luksfile} || exit 21
+cryptsetup luksFormat -q ${csopt} ${luksfile} ${lukskeyfile} || exit 22
+
+# Shutdown the swtpm instance on exit
+cleanup() {
+if [ -e "$tpm2dev" ]; then
+swtpm_ioctl -s ${tpm2dev}
+fi
+if [ "${RET:-1}" -eq 0 ]; then
+rm -rf "$tpm2testdir" || :
+fi
+}
+trap cleanup EXIT INT TERM KILL QUIT
+
+# Shutdown the previous swtpm instance if exists
+if [ -c "${tpm2dev}" ]; then
+swtpm_ioctl -s ${tpm2dev}
+fi
+
+# Create the swtpm cuse instannce
+swtpm_cuse -n ${tpm2devname} --tpm2 --tpmstate dir=${tpm2statedir}
+ret=$?
+if [ "$ret" -ne 0 ]; then
+exit $ret
+fi
+
+# Initialize swtpm device
+swtpm_ioctl -i ${tpm2dev}
+ret=$?
+if [ "$ret" -ne 0 ]; then
+exit $ret
+fi
+
+# Export the TCTI variable for tpm2-tools
+export TPM2TOOLS_TCTI="device:${tpm2dev}"
+
+# Send the startup command
+tpm2_startup -c
+ret=$?
+if [ "$ret" -ne 0 ]; then
+exit $ret
+fi
+
+# Extend PCR 0
+tpm2_pcrextend 0:sha256=$(sha256sum <<< "test0" | cut -d ' ' -f 1)
+ret=$?
+if [ "$ret" -ne 0 ]; then
+exit $ret
+fi
+
+# Extend PCR 1
+tpm2_pcrextend 

[PATCH v11 19/20] tpm2: Enable tpm2 module for grub-emu

2024-04-12 Thread Gary Lin via Grub-devel
As a preparation to test TPM 2.0 TSS stack with grub-emu, the new
option, --tpm-device, is introduced to specify the TPM device for
grub-emu so that grub-emu can share the emulated TPM device with
the host.

Since grub-emu can directly access the device node on host, it's easy to
implement the essential TCG2 command submission function with the
read/write functions and enable tpm2 module for grub-emu, so that we can
further test TPM key unsealing with grub-emu.

Signed-off-by: Gary Lin 
---
 grub-core/Makefile.core.def |  2 ++
 grub-core/kern/emu/main.c   | 11 +++-
 grub-core/kern/emu/misc.c   | 51 
 grub-core/tpm2/tcg2-emu.c   | 52 +
 include/grub/emu/misc.h |  5 
 5 files changed, 120 insertions(+), 1 deletion(-)
 create mode 100644 grub-core/tpm2/tcg2-emu.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index e937f6538..962090d2c 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2567,7 +2567,9 @@ module = {
   common = tpm2/tpm2key.c;
   common = tpm2/tpm2key_asn1_tab.c;
   efi = tpm2/tcg2.c;
+  emu = tpm2/tcg2-emu.c;
   enable = efi;
+  enable = emu;
 };
 
 module = {
diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c
index 855b11c3d..c10838613 100644
--- a/grub-core/kern/emu/main.c
+++ b/grub-core/kern/emu/main.c
@@ -55,7 +55,7 @@
 static jmp_buf main_env;
 
 /* Store the prefix specified by an argument.  */
-static char *root_dev = NULL, *dir = NULL;
+static char *root_dev = NULL, *dir = NULL, *tpm_dev = NULL;
 
 grub_addr_t grub_modbase = 0;
 
@@ -108,6 +108,7 @@ static struct argp_option options[] = {
   {"verbose", 'v', 0,  0, N_("print verbose messages."), 0},
   {"hold", 'H', N_("SECS"),  OPTION_ARG_OPTIONAL, N_("wait until a 
debugger will attach"), 0},
   {"kexec",   'X', 0,  0, N_("use kexec to boot Linux kernels via 
systemctl (pass twice to enable dangerous fallback to non-systemctl)."), 0},
+  {"tpm-device",  't', N_("DEV"), 0, N_("Set TPM device."), 0},
   { 0, 0, 0, 0, 0, 0 }
 };
 
@@ -168,6 +169,10 @@ argp_parser (int key, char *arg, struct argp_state *state)
 case 'X':
   grub_util_set_kexecute ();
   break;
+case 't':
+  free (tpm_dev);
+  tpm_dev = xstrdup (arg);
+  break;
 
 case ARGP_KEY_ARG:
   {
@@ -276,6 +281,9 @@ main (int argc, char *argv[])
 
   dir = xstrdup (dir);
 
+  if (tpm_dev)
+grub_util_tpm_open (tpm_dev);
+
   /* Start GRUB!  */
   if (setjmp (main_env) == 0)
 grub_main ();
@@ -283,6 +291,7 @@ main (int argc, char *argv[])
   grub_fini_all ();
   grub_hostfs_fini ();
   grub_host_fini ();
+  grub_util_tpm_close ();
 
   grub_machine_fini (GRUB_LOADER_FLAG_NORETURN);
 
diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c
index 521220b49..1db24fde7 100644
--- a/grub-core/kern/emu/misc.c
+++ b/grub-core/kern/emu/misc.c
@@ -28,6 +28,8 @@
 #include 
 #include 
 #include 
+#include 
+#include 
 
 #include 
 #include 
@@ -41,6 +43,8 @@
 int verbosity;
 int kexecute;
 
+static int grub_util_tpm_fd = -1;
+
 void
 grub_util_warn (const char *fmt, ...)
 {
@@ -230,3 +234,50 @@ grub_util_get_kexecute (void)
 {
   return kexecute;
 }
+
+grub_err_t
+grub_util_tpm_open (const char *tpm_dev)
+{
+  if (grub_util_tpm_fd != -1)
+return GRUB_ERR_NONE;
+
+  grub_util_tpm_fd = open (tpm_dev, O_RDWR);
+  if (grub_util_tpm_fd == -1)
+grub_util_error (_("cannot open TPM device '%s': %s"), tpm_dev, strerror 
(errno));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_util_tpm_close (void)
+{
+  int err;
+
+  if (grub_util_tpm_fd == -1)
+return GRUB_ERR_NONE;
+
+  err = close (grub_util_tpm_fd);
+  if (err != GRUB_ERR_NONE)
+grub_util_error (_("cannot close TPM device: %s"), strerror (errno));
+
+  grub_util_tpm_fd = -1;
+  return GRUB_ERR_NONE;
+}
+
+grub_size_t
+grub_util_tpm_read (void *output, grub_size_t size)
+{
+  if (grub_util_tpm_fd == -1)
+return -1;
+
+  return read (grub_util_tpm_fd, output, size);
+}
+
+grub_size_t
+grub_util_tpm_write (const void *input, grub_size_t size)
+{
+  if (grub_util_tpm_fd == -1)
+return -1;
+
+  return write (grub_util_tpm_fd, input, size);
+}
diff --git a/grub-core/tpm2/tcg2-emu.c b/grub-core/tpm2/tcg2-emu.c
new file mode 100644
index 0..0d7b8b16e
--- /dev/null
+++ b/grub-core/tpm2/tcg2-emu.c
@@ -0,0 +1,52 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2024 SUSE LLC
+ *
+ *  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.
+ *
+ *  

[PATCH v11 15/20] tpm2: Implement NV index

2024-04-12 Thread Gary Lin via Grub-devel
From: Patrick Colp 

Currently with the TPM2 protector, only SRK mode is supported and
NV index support is just a stub. Implement the NV index option.

Note: This only extends support on the unseal path. grub2_protect
has not been updated. tpm2-tools can be used to insert a key into
the NV index.

An example of inserting a key using tpm2-tools:

  # Get random key.
  tpm2_getrandom 32 > key.dat

  # Create primary object.
  tpm2_createprimary -C o -g sha256 -G ecc -c primary.ctx

  # Create policy object. `pcrs.dat` contains the PCR values to seal against.
  tpm2_startauthsession -S session.dat
  tpm2_policypcr -S session.dat -l sha256:7,11 -f pcrs.dat -L policy.dat
  tpm2_flushcontext session.dat

  # Seal key into TPM.
  cat key.dat | tpm2_create -C primary.ctx -u key.pub -r key.priv -L policy.dat 
-i-
  tpm2_load -C primary.ctx -u key.pub -r key.priv -n sealing.name -c sealing.ctx
  tpm2_evictcontrol -C o -c sealing.ctx 0x8100

Then to unseal the key in grub, add this to grub.cfg:

  tpm2_key_protector_init --mode=nv --nvindex=0x8100 --pcrs=7,11
  cryptomount -u  --protector tpm2

Signed-off-by: Patrick Colp 
Signed-off-by: Gary Lin 
---
 grub-core/tpm2/module.c | 25 -
 1 file changed, 20 insertions(+), 5 deletions(-)

diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
index 0ed8f2682..b4d588b0c 100644
--- a/grub-core/tpm2/module.c
+++ b/grub-core/tpm2/module.c
@@ -1022,12 +1022,27 @@ static grub_err_t
 grub_tpm2_protector_nv_recover (const struct grub_tpm2_protector_context *ctx,
grub_uint8_t **key, grub_size_t *key_size)
 {
-  (void)ctx;
-  (void)key;
-  (void)key_size;
+  TPM_HANDLE sealed_handle = ctx->nv;
+  tpm2key_policy_t policy_seq = NULL;
+  grub_err_t err;
+
+  /* Create a basic policy sequence based on the given PCR selection */
+  err = grub_tpm2_protector_simple_policy_seq (ctx, _seq);
+  if (err != GRUB_ERR_NONE)
+goto exit;
+
+  err = grub_tpm2_protector_unseal (policy_seq, sealed_handle, key, key_size);
+
+  /* Pop error messages on success */
+  if (err == GRUB_ERR_NONE)
+while (grub_error_pop ());
+
+exit:
+  TPM2_FlushContext (sealed_handle);
 
-  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-N_("NV Index mode is not implemented yet"));
+  grub_tpm2key_free_policy_seq (policy_seq);
+
+  return err;
 }
 
 static grub_err_t
-- 
2.35.3


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


[PATCH v11 12/20] cryptodisk: Support key protectors

2024-04-12 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

Add a new parameter to cryptomount to support the key protectors framework: -P.
The parameter is used to automatically retrieve a key from specified key
protectors. The parameter may be repeated to specify any number of key
protectors. These are tried in order until one provides a usable key for any
given disk.

Signed-off-by: Hernan Gatta 
Signed-off-by: Michael Chang 
Signed-off-by: Gary Lin 
Reviewed-by: Glenn Washburn 
---
 Makefile.util.def   |   1 +
 grub-core/disk/cryptodisk.c | 172 +---
 include/grub/cryptodisk.h   |  16 
 3 files changed, 158 insertions(+), 31 deletions(-)

diff --git a/Makefile.util.def b/Makefile.util.def
index b53afb1d3..19ad5a96f 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -40,6 +40,7 @@ library = {
   common = grub-core/disk/luks.c;
   common = grub-core/disk/luks2.c;
   common = grub-core/disk/geli.c;
+  common = grub-core/disk/key_protector.c;
   common = grub-core/disk/cryptodisk.c;
   common = grub-core/disk/AFSplitter.c;
   common = grub-core/lib/pbkdf2.c;
diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 2246af51b..0ca1a5c4d 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -26,6 +26,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #ifdef GRUB_UTIL
 #include 
@@ -44,7 +45,8 @@ enum
 OPTION_KEYFILE,
 OPTION_KEYFILE_OFFSET,
 OPTION_KEYFILE_SIZE,
-OPTION_HEADER
+OPTION_HEADER,
+OPTION_PROTECTOR
   };
 
 static const struct grub_arg_option options[] =
@@ -58,6 +60,8 @@ static const struct grub_arg_option options[] =
 {"keyfile-offset", 'O', 0, N_("Key file offset (bytes)"), 0, ARG_TYPE_INT},
 {"keyfile-size", 'S', 0, N_("Key file data size (bytes)"), 0, 
ARG_TYPE_INT},
 {"header", 'H', 0, N_("Read header from file"), 0, ARG_TYPE_STRING},
+{"protector", 'P', GRUB_ARG_OPTION_REPEATABLE,
+ N_("Unlock volume(s) using key protector(s)."), 0, ARG_TYPE_STRING},
 {0, 0, 0, 0, 0, 0}
   };
 
@@ -1061,6 +1065,7 @@ grub_cryptodisk_scan_device_real (const char *name,
   grub_err_t ret = GRUB_ERR_NONE;
   grub_cryptodisk_t dev;
   grub_cryptodisk_dev_t cr;
+  int i;
   struct cryptodisk_read_hook_ctx read_hook_data = {0};
   int askpass = 0;
   char *part = NULL;
@@ -1113,41 +1118,112 @@ grub_cryptodisk_scan_device_real (const char *name,
   goto error_no_close;
 if (!dev)
   continue;
+break;
+  }
 
-if (!cargs->key_len)
-  {
-   /* Get the passphrase from the user, if no key data. */
-   askpass = 1;
-   part = grub_partition_get_name (source->partition);
-   grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name,
-source->partition != NULL ? "," : "",
-part != NULL ? part : N_("UNKNOWN"),
-dev->uuid);
-   grub_free (part);
-
-   cargs->key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE);
-   if (cargs->key_data == NULL)
- goto error_no_close;
-
-   if (!grub_password_get ((char *) cargs->key_data, 
GRUB_CRYPTODISK_MAX_PASSPHRASE))
- {
-   grub_error (GRUB_ERR_BAD_ARGUMENT, "passphrase not supplied");
-   goto error;
- }
-   cargs->key_len = grub_strlen ((char *) cargs->key_data);
-  }
+  if (dev == NULL)
+{
+  grub_error (GRUB_ERR_BAD_MODULE,
+ "no cryptodisk module can handle this device");
+  goto error_no_close;
+}
 
-ret = cr->recover_key (source, dev, cargs);
-if (ret != GRUB_ERR_NONE)
-  goto error;
+  if (cargs->protectors)
+{
+  for (i = 0; cargs->protectors[i]; i++)
+   {
+ if (cargs->key_cache[i].invalid)
+   continue;
+
+ if (cargs->key_cache[i].key == NULL)
+   {
+ ret = grub_key_protector_recover_key (cargs->protectors[i],
+   >key_cache[i].key,
+   
>key_cache[i].key_len);
+ if (ret != GRUB_ERR_NONE)
+   {
+ if (grub_errno)
+   {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+   }
+
+ grub_dprintf ("cryptodisk",
+   "failed to recover a key from key protector "
+   "%s, will not try it again for any other "
+   "disks, if any, during this invocation of "
+   "cryptomount\n",
+   cargs->protectors[i]);
+
+ cargs->key_cache[i].invalid = 1;
+ continue;
+   }
+   }
+
+ cargs->key_data = cargs->key_cache[i].key;
+ cargs->key_len = cargs->key_cache[i].key_len;
 
-ret = grub_cryptodisk_insert (dev, name, source);
-if (ret != GRUB_ERR_NONE)
+ ret = cr->recover_key (source, 

[PATCH v11 14/20] tpm2: Support authorized policy

2024-04-12 Thread Gary Lin via Grub-devel
This commit handles the TPM2_PolicyAuthorize command from the key file
in TPM 2.0 Key File format.

TPM2_PolicyAuthorize is the essential command to support authorized
policy which allows the users to sign TPM policies with their own keys.
Per TPM 2.0 Key File(*1), CommandPolicy for TPM2_PolicyAuthorize
comprises 'TPM2B_PUBLIC pubkey', 'TPM2B_DIGEST policy_ref', and
'TPMT_SIGNATURE signature'. To verify the signature, the current policy
digest is hashed with the hash algorithm written in 'signature', and then
'signature' is verified with the hashed policy digest and 'pubkey'. Once
TPM accepts 'signature', TPM2_PolicyAuthorize is invoked to authorize the
signed policy.

To create the key file with authorized policy, here are the pcr-oracle(*2)
commands:

  # Generate the RSA key and create the authorized policy file
  $ pcr-oracle \
--rsa-generate-key \
--private-key policy-key.pem \
--auth authorized.policy \
create-authorized-policy 0,2,4,7,9

  # Seal the secret with the authorized policy
  $ pcr-oracle \
--key-format tpm2.0 \
--auth authorized.policy \
--input disk-secret.txt \
--output sealed.key \
seal-secret

  # Sign the predicted PCR policy
  $ pcr-oracle \
--key-format tpm2.0 \
--private-key policy-key.pem \
--from eventlog \
--stop-event "grub-file=grub.cfg" \
--after \
--input sealed.key \
--output sealed.tpm \
sign 0,2,4,7.9

Then specify the key file and the key protector to grub.cfg in the EFI
system partition:

tpm2_key_protector_init -a RSA --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
cryptomount -u  -P tpm2

For any change in the boot components, just run the 'sign' command again
to update the signature in sealed.tpm, and TPM can unseal the key file
with the updated PCR policy.

(*1) https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
(*2) https://github.com/okirch/pcr-oracle

Signed-off-by: Gary Lin 
---
 grub-core/tpm2/module.c | 84 +
 1 file changed, 84 insertions(+)

diff --git a/grub-core/tpm2/module.c b/grub-core/tpm2/module.c
index f6859d3d2..0ed8f2682 100644
--- a/grub-core/tpm2/module.c
+++ b/grub-core/tpm2/module.c
@@ -649,6 +649,87 @@ grub_tpm2_protector_policypcr (TPMI_SH_AUTH_SESSION 
session,
   return GRUB_ERR_NONE;
 }
 
+static grub_err_t
+grub_tpm2_protector_policyauthorize (TPMI_SH_AUTH_SESSION session,
+struct grub_tpm2_buffer *cmd_buf)
+{
+  TPM2B_PUBLIC pubkey;
+  TPM2B_DIGEST policy_ref;
+  TPMT_SIGNATURE signature;
+  TPM2B_DIGEST pcr_policy;
+  TPM2B_DIGEST pcr_policy_hash;
+  TPMI_ALG_HASH sig_hash;
+  TPMT_TK_VERIFIED verification_ticket;
+  TPM_HANDLE pubkey_handle = 0;
+  TPM2B_NAME pubname;
+  TPM_RC rc;
+  grub_err_t err;
+
+  grub_tpm2_mu_TPM2B_PUBLIC_Unmarshal (cmd_buf, );
+  grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (cmd_buf, _ref);
+  grub_tpm2_mu_TPMT_SIGNATURE_Unmarshal (cmd_buf, );
+  if (cmd_buf->error != 0)
+return grub_error (GRUB_ERR_BAD_ARGUMENT,
+  N_("Failed to unmarshal the buffer for 
TPM2_PolicyAuthorize"));
+
+  /* Retrieve Policy Digest */
+  rc = TPM2_PolicyGetDigest (session, NULL, _policy, NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_DEVICE,
+  N_("Failed to get policy digest (TPM2_PolicyGetDigest: 
0x%x)."),
+  rc);
+
+  /* Calculate the digest of the polcy for VerifySignature */
+  sig_hash = TPMT_SIGNATURE_get_hash_alg ();
+  if (sig_hash == TPM_ALG_NULL)
+return grub_error (GRUB_ERR_BAD_ARGUMENT,
+  N_("Failed to get the hash algorithm of the signature"));
+
+  rc = TPM2_Hash (NULL, (TPM2B_MAX_BUFFER *)_policy, sig_hash,
+ TPM_RH_NULL, _policy_hash, NULL, NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_DEVICE,
+  N_("Failed to create PCR policy hash (TPM2_Hash: 0x%x)"),
+  rc);
+
+  /* Load the public key */
+  rc = TPM2_LoadExternal (NULL, NULL, , TPM_RH_OWNER,
+ _handle, , NULL);
+  if (rc != TPM_RC_SUCCESS)
+return grub_error (GRUB_ERR_BAD_DEVICE,
+  N_("Failed to load public key (TPM2_LoadExternal: 
0x%x)"),
+  rc);
+
+  /* Verify the signature against the public key and the policy digest */
+  rc = TPM2_VerifySignature (pubkey_handle, NULL, _policy_hash, ,
+_ticket, NULL);
+  if (rc != TPM_RC_SUCCESS)
+{
+  err = grub_error (GRUB_ERR_BAD_DEVICE,
+   N_("Failed to verify signature (TPM2_VerifySignature: 
0x%x)"),
+   rc);
+  goto error;
+}
+
+  /* Authorize the signed policy with the public key and the verification 
ticket */
+  rc = TPM2_PolicyAuthorize (session, NULL, _policy, _ref, ,
+_ticket, NULL);
+  if (rc != TPM_RC_SUCCESS)
+{
+  

[PATCH v11 09/20] key_protector: Add key protectors framework

2024-04-12 Thread Gary Lin via Grub-devel
From: Hernan Gatta 

A key protector encapsulates functionality to retrieve an unlocking key
for a fully-encrypted disk from a specific source. A key protector
module registers itself with the key protectors framework when it is
loaded and unregisters when unloaded. Additionally, a key protector may
accept parameters that describe how it should operate.

The key protectors framework, besides offering registration and
unregistration functions, also offers a one-stop routine for finding and
invoking a key protector by name. If a key protector with the specified
name exists and if an unlocking key is successfully retrieved by it, the
function returns to the caller the retrieved key and its length.

Cc: Vladimir Serbinenko 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 grub-core/Makefile.am  |  1 +
 grub-core/Makefile.core.def|  5 +++
 grub-core/disk/key_protector.c | 78 ++
 include/grub/key_protector.h   | 46 
 4 files changed, 130 insertions(+)
 create mode 100644 grub-core/disk/key_protector.c
 create mode 100644 include/grub/key_protector.h

diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
index f18550c1c..9d3d5f519 100644
--- a/grub-core/Makefile.am
+++ b/grub-core/Makefile.am
@@ -90,6 +90,7 @@ endif
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/mm.h
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/parser.h
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/partition.h
+KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/key_protector.h
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/stack_protector.h
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/term.h
 KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/time.h
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index fe2a2da90..2d6871e7c 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1278,6 +1278,11 @@ module = {
   common = disk/raid6_recover.c;
 };
 
+module = {
+  name = key_protector;
+  common = disk/key_protector.c;
+};
+
 module = {
   name = scsi;
   common = disk/scsi.c;
diff --git a/grub-core/disk/key_protector.c b/grub-core/disk/key_protector.c
new file mode 100644
index 0..31de798d6
--- /dev/null
+++ b/grub-core/disk/key_protector.c
@@ -0,0 +1,78 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022 Microsoft Corporation
+ *
+ *  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 .
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+struct grub_key_protector *grub_key_protectors = NULL;
+
+grub_err_t
+grub_key_protector_register (struct grub_key_protector *protector)
+{
+  if (protector == NULL || protector->name == NULL || 
grub_strlen(protector->name) == 0)
+return GRUB_ERR_BAD_ARGUMENT;
+
+  if (grub_key_protectors &&
+  grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors),
+   protector->name))
+return GRUB_ERR_BAD_ARGUMENT;
+
+  grub_list_push (GRUB_AS_LIST_P (_key_protectors),
+ GRUB_AS_LIST (protector));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_key_protector_unregister (struct grub_key_protector *protector)
+{
+  if (protector == NULL)
+return GRUB_ERR_BAD_ARGUMENT;
+
+  grub_list_remove (GRUB_AS_LIST (protector));
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_key_protector_recover_key (const char *protector, grub_uint8_t **key,
+   grub_size_t *key_size)
+{
+  struct grub_key_protector *kp = NULL;
+
+  if (grub_key_protectors == NULL)
+return GRUB_ERR_OUT_OF_RANGE;
+
+  if (protector == NULL || grub_strlen (protector) == 0)
+return GRUB_ERR_BAD_ARGUMENT;
+
+  kp = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_key_protectors),
+protector);
+  if (kp == NULL)
+return grub_error (GRUB_ERR_OUT_OF_RANGE,
+  N_("A key protector with name '%s' could not be found. "
+ "Is the name spelled correctly and is the "
+ "corresponding module loaded?"), protector);
+
+  return kp->recover_key (key, key_size);
+}
diff --git a/include/grub/key_protector.h b/include/grub/key_protector.h
new file mode 100644
index 0..6e6a6fb24
--- /dev/null
+++ b/include/grub/key_protector.h
@@ -0,0 +1,46 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  

[PATCH v11 16/20] cryptodisk: Fallback to passphrase

2024-04-12 Thread Gary Lin via Grub-devel
From: Patrick Colp 

If a protector is specified, but it fails to unlock the disk, fall back
to asking for the passphrase. However, an error was set indicating that
the protector(s) failed. Later code (e.g., LUKS code) fails as
`grub_errno` is now set. Print the existing errors out first, before
proceeding with the passphrase.

Signed-off-by: Patrick Colp 
Signed-off-by: Gary Lin 
---
 grub-core/disk/cryptodisk.c | 7 ++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 0ca1a5c4d..0dbf601c4 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -1191,11 +1191,16 @@ grub_cryptodisk_scan_device_real (const char *name,
  source->name, source->partition != NULL ? "," : "",
  part != NULL ? part : N_("UNKNOWN"), dev->uuid);
   grub_free (part);
-  goto error;
 }
 
   if (!cargs->key_len)
 {
+  if (grub_errno)
+   {
+ grub_print_error ();
+ grub_errno = GRUB_ERR_NONE;
+   }
+
   /* Get the passphrase from the user, if no key data. */
   askpass = 1;
   part = grub_partition_get_name (source->partition);
-- 
2.35.3


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


[PATCH v11 06/20] libtasn1: compile into asn1 module

2024-04-12 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

Create a wrapper file that specifies the module license.
Set up the makefile so it is built.

Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
Reviewed-by: Vladimir Serbinenko 
---
 grub-core/Makefile.core.def| 15 +++
 grub-core/lib/libtasn1_wrap/wrap.c | 26 ++
 2 files changed, 41 insertions(+)
 create mode 100644 grub-core/lib/libtasn1_wrap/wrap.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 1571421d7..b1294e0f7 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2595,3 +2595,18 @@ module = {
   efi = commands/bli.c;
   enable = efi;
 };
+
+module = {
+  name = asn1;
+  common = lib/libtasn1/lib/decoding.c;
+  common = lib/libtasn1/lib/coding.c;
+  common = lib/libtasn1/lib/element.c;
+  common = lib/libtasn1/lib/structure.c;
+  common = lib/libtasn1/lib/parser_aux.c;
+  common = lib/libtasn1/lib/gstr.c;
+  common = lib/libtasn1/lib/errors.c;
+  common = lib/libtasn1_wrap/wrap.c;
+  cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
+  // -Wno-type-limits comes from libtasn1's configure.ac
+  cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB) 
-I$(srcdir)/lib/libtasn1/lib -Wno-type-limits';
+};
diff --git a/grub-core/lib/libtasn1_wrap/wrap.c 
b/grub-core/lib/libtasn1_wrap/wrap.c
new file mode 100644
index 0..622ba942e
--- /dev/null
+++ b/grub-core/lib/libtasn1_wrap/wrap.c
@@ -0,0 +1,26 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2020 IBM Corporation
+ *
+ *  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 .
+ */
+
+#include 
+
+/*
+ * libtasn1 is provided under LGPL2.1+, which is compatible
+ * with GPL3+. As Grub as a whole is under GPL3+, this module
+ * is therefore under GPL3+ also.
+ */
+GRUB_MOD_LICENSE ("GPLv3+");
-- 
2.35.3


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


[PATCH v11 17/20] cryptodisk: wipe out the cached keys from protectors

2024-04-12 Thread Gary Lin via Grub-devel
An attacker may insert a malicious disk with the same crypto UUID and
trick grub2 to mount the fake root. Even though the key from the key
protector fails to unlock the fake root, it's not wiped out cleanly so
the attacker could dump the memory to retrieve the secret key. To defend
such attack, wipe out the cached key when we don't need it.

Signed-off-by: Gary Lin 
Cc: Fabian Vogt 
---
 grub-core/disk/cryptodisk.c | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c
index 0dbf601c4..94507ec65 100644
--- a/grub-core/disk/cryptodisk.c
+++ b/grub-core/disk/cryptodisk.c
@@ -1349,7 +1349,11 @@ grub_cryptodisk_clear_key_cache (struct 
grub_cryptomount_args *cargs)
 return;
 
   for (i = 0; cargs->protectors[i]; i++)
-grub_free (cargs->key_cache[i].key);
+{
+  if (cargs->key_cache[i].key)
+   grub_memset (cargs->key_cache[i].key, 0, cargs->key_cache[i].key_len);
+  grub_free (cargs->key_cache[i].key);
+}
 
   grub_free (cargs->key_cache);
 }
-- 
2.35.3


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


[PATCH v11 08/20] libtasn1: Add the documentation

2024-04-12 Thread Gary Lin via Grub-devel
Document libtasn1 in docs/grub-dev.texi and add the upgrade steps.
Also add the patches to make libtasn1 compatible with grub code.

Signed-off-by: Gary Lin 
Reviewed-by: Vladimir Serbinenko 
---
 docs/grub-dev.texi|  28 ++
 ...asn1-disable-code-not-needed-in-grub.patch | 320 ++
 ...tasn1-changes-for-grub-compatibility.patch | 135 
 ...sn1-fix-the-potential-buffer-overrun.patch |  35 ++
 4 files changed, 518 insertions(+)
 create mode 100644 
grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
 create mode 100644 
grub-core/lib/libtasn1-patches/0002-libtasn1-changes-for-grub-compatibility.patch
 create mode 100644 
grub-core/lib/libtasn1-patches/0003-libtasn1-fix-the-potential-buffer-overrun.patch

diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
index 1276c5930..36bf77883 100644
--- a/docs/grub-dev.texi
+++ b/docs/grub-dev.texi
@@ -506,6 +506,7 @@ to update it.
 * Gnulib::
 * jsmn::
 * minilzo::
+* libtasn1::
 @end menu
 
 @node Gnulib
@@ -596,6 +597,33 @@ cp minilzo-2.10/*.[hc] grub-core/lib/minilzo
 rm -r minilzo-2.10*
 @end example
 
+@node libtasn1
+@section libtasn1
+
+libtasn1 is a library providing Abstract Syntax Notation One (ASN.1, as
+specified by the X.680 ITU-T recommendation) parsing and structures management,
+and Distinguished Encoding Rules (DER, as per X.690) encoding and decoding
+functions.
+
+To upgrade to a new version of the libtasn1 library, download the release
+tarball and copy the files into the target directory:
+
+@example
+curl -L -O https://ftp.gnu.org/gnu/libtasn1/libtasn1-4.19.0.tar.gz
+tar -zxf libtasn1-4.19.0.tar.gz
+rm -r grub-core/lib/libtasn1/
+mkdir libtasn1/lib
+mkdir -p grub-core/lib/libtasn1/lib/
+cp libtasn1-4.19.0/@lbracechar{}README.md,COPYING@rbracechar{} 
grub-core/lib/libtasn1/
+cp 
libtasn1-4.19.0/lib/@lbracechar{}coding.c,decoding.c,element.c,element.h,errors.c,gstr.c,gstr.h,int.h,parser_aux.c,parser_aux.h,structure.c,structure.h@rbracechar{}
 grub-core/lib/libtasn1/lib/
+cp libtasn1-4.19.0/lib/includes/libtasn1.h include/grub/
+rm -rf libtasn1-4.19.0
+@end example
+
+After upgrading the library, it is necessary to apply the patches in
+@file{grub-core/lib/libtasn1-patches/} to adjust the code to be compatible with
+grub.
+
 @node Debugging
 @chapter Debugging
 
diff --git 
a/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
 
b/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
new file mode 100644
index 0..e3264409f
--- /dev/null
+++ 
b/grub-core/lib/libtasn1-patches/0001-libtasn1-disable-code-not-needed-in-grub.patch
@@ -0,0 +1,320 @@
+From 715f65934a120730316751536194ec5ed86aed9c Mon Sep 17 00:00:00 2001
+From: Daniel Axtens 
+Date: Fri, 1 May 2020 17:12:23 +1000
+Subject: [PATCH 1/3] libtasn1: disable code not needed in grub
+
+We don't expect to be able to write ASN.1, only read it,
+so we can disable some code.
+
+Do that with #if 0/#endif, rather than deletion. This means
+that the difference between upstream and grub is smaller,
+which should make updating libtasn1 easier in the future.
+
+With these exclusions we also avoid the need for minmax.h,
+which is convenient because it means we don't have to
+import it from gnulib.
+
+Cc: Vladimir Serbinenko 
+Signed-off-by: Daniel Axtens 
+Signed-off-by: Gary Lin 
+---
+ grub-core/lib/libtasn1/lib/coding.c| 12 ++--
+ grub-core/lib/libtasn1/lib/decoding.c  |  2 ++
+ grub-core/lib/libtasn1/lib/element.c   |  6 +++---
+ grub-core/lib/libtasn1/lib/errors.c|  3 +++
+ grub-core/lib/libtasn1/lib/structure.c | 10 ++
+ include/grub/libtasn1.h| 15 +++
+ 6 files changed, 39 insertions(+), 9 deletions(-)
+
+diff --git a/grub-core/lib/libtasn1/lib/coding.c 
b/grub-core/lib/libtasn1/lib/coding.c
+index ea5bc370e..5d03bca9d 100644
+--- a/grub-core/lib/libtasn1/lib/coding.c
 b/grub-core/lib/libtasn1/lib/coding.c
+@@ -30,11 +30,11 @@
+ #include "parser_aux.h"
+ #include 
+ #include "element.h"
+-#include "minmax.h"
+ #include 
+ 
+ #define MAX_TAG_LEN 16
+ 
++#if 0 /* GRUB SKIPPED IMPORTING */
+ /**/
+ /* Function : _asn1_error_description_value_not_found */
+ /* Description: creates the ErrorDescription string   */
+@@ -58,6 +58,7 @@ _asn1_error_description_value_not_found (asn1_node node,
+   Estrcat (ErrorDescription, "' not found");
+ 
+ }
++#endif
+ 
+ /**
+  * asn1_length_der:
+@@ -244,6 +245,7 @@ asn1_encode_simple_der (unsigned int etype, const unsigned 
char *str,
+   return ASN1_SUCCESS;
+ }
+ 
++#if 0 /* GRUB SKIPPED IMPORTING */
+ /**/
+ /* Function : _asn1_time_der  */
+ /* Description: creates the DER coding for a TIME */
+@@ -278,7 +280,7 @@ _asn1_time_der (unsigned char *str, int str_len, unsigned 
char *der,
+ 
+   return ASN1_SUCCESS;
+ }
+-
++#endif
+ 
+ /*
+ 

[PATCH v11 00/20] Automatic Disk Unlock with TPM2

2024-04-12 Thread Gary Lin via Grub-devel
GIT repo for v11: https://github.com/lcp/grub2/tree/tpm2-unlock-v11

This patch series is based on "Automatic TPM Disk Unlock"(*1) posted by
Hernan Gatta to introduce the key protector framework and TPM2 stack
to GRUB2, and this could be a useful feature for the systems to
implement full disk encryption.

To support TPM 2.0 Key File format(*2), patch 1~5,7 are grabbed from
Daniel Axtens's "appended signature secure boot support" (*3) to import
libtasn1 into grub2. Besides, the libtasn1 version is upgraded to
4.19.0 instead of 4.16.0 in the original patch.

Patch 6 fixes a potential buffer overrun in libtasn1.
(https://gitlab.com/gnutls/libtasn1/-/issues/49)

Patch 8 adds the document for libtasn1 and the steps to upgrade the
library.

Patch 9~13 are Hernan Gatta's patches with the follow-up fixes and
improvements:
- Converting 8 spaces into 1 tab
- Merging the minor build fix from Michael Chang
  - Replacing "lu" with "PRIuGRUB_SIZE" for grub_dprintf
  - Adding "enable = efi" to the tpm2 module in grub-core/Makefile.core.def
- Rebasing "cryptodisk: Support key protectors" to the git master
- Removing the measurement on the sealed key
  - Based on the patch from Olaf Kirch 
- Adjusting the input parameters of TPM2_EvictControl to match the order
  in "TCG TPM2 Part3 Commands"
- Declaring the input arguments of TPM2 functions as const
- Resending TPM2 commands on TPM_RC_RETRY
- Adding checks for the parameters of TPM2 commands
- Packing the missing authorization command for TPM2_PCR_Read
- Tweaking the TPM2 command functions to allow some parameters to be
  NULL so that we don't have to declare empty variables
- Only enabling grub-protect for "efi" since the TPM2 stack currently
  relies on the EFI TCG2 protocol to send TPM2 commands
- Using grub_cpu_to_be*() in the TPM2 stack instead of grub_swap_bytes*()
  which may cause problems in big-indian machines
- Changing the short name of "--protector" of "cryptomount" from "-k" to
  "-P" to avoid the conflict with "--key-file"
- Supporting TPM 2.0 Key File Format besides the raw sealed key
- Adding the external libtasn1 dependency to grub-protect to write the
  TPM 2.0 Key files
- Extending the TPM2 TSS stack to support authorized policy

Patch 14 implements the authorized policy support.

Patch 15 implements the missing NV index mode. (Thanks to Patrick Colp)

Patch 16 improves the 'cryptomount' command to fall back to the
passphrase mode when the key protector fails to unlock the encrypted
partition. (Another patch from Patrick Colp)

Patch 17 and 18 fix the potential security issues spotted by Fabian Vogt.

Patch 19 and 20 implement the TPM key unsealing testcases.

To utilize the TPM2 key protector to unlock the encrypted partition
(sdb1), here are the sample steps:

1. Add an extra random key for LUKS (luks-key)
   $ dd if=/dev/urandom of=luks-key bs=1 count=32
   $ sudo cryptsetup luksAddKey /dev/sdb1 luks-key --pbkdf=pbkdf2

2. Seal the key
   $ sudo grub-protect --action=add \
   --protector=tpm2 \
   --tpm2key \
   --tpm2-keyfile=luks-key \
   --tpm2-outfile=/boot/efi/boot/grub2/sealed.tpm

3. Unseal the key with the proper commands in grub.cfg:
   tpm2_key_protector_init --tpm2key=(hd0,gpt1)/boot/grub2/sealed.tpm
   cryptomount -u  -P tpm2

(*1) https://lists.gnu.org/archive/html/grub-devel/2022-02/msg6.html
(*2) https://www.hansenpartnership.com/draft-bottomley-tpm2-keys.html
(*3) https://lists.gnu.org/archive/html/grub-devel/2021-06/msg00044.html

v11:
- Adding the missing default: handlers in grub-core/tpm2/mu.c
- Updating the help messages and commit messages to reflect the change
  of the default SRK algorithm (RSA2048 -> ECC_NIST_P256)
- Adding the testcase for the NV index mode

v10:
- https://lists.gnu.org/archive/html/grub-devel/2024-04/msg00019.html
- GIT repo: https://github.com/lcp/grub2/tree/tpm2-unlock-v10
- Fixing the coverity issues: CID 435775, CID 435771, CID 435770, CID
  435769, CID 435767, CID 435761
  https://lists.gnu.org/archive/html/grub-devel/2024-02/txtKIuUb5lf3O.txt
  - Fixing the potential memory leak (CID 435775)
  - Removing the unnecessary grub_protect_get_grub_drive_for_file() from
util/grub-protect.c (CID 435771)
  - Using the grub_tpm2_mu_TPM2B_*_Unmarshal functions to unmarshal the
TPM2B structs instead of a generic grub_tpm2_mu_TPM2B_Unmarshal
(CID 435770)
  - Fixing Null pointer dereference (CID 435769)
  - Adding bound checks to grub_tpm2_mu_TPML_DIGEST_Unmarshal()
(CID 435767)
  - Improving the check for the return value of ftell() (CID 435761)
- Adding a quick fix for CID 435762
- Removing the empty ending line in tests/asn1_test.in
- Fixing docs/grub-dev.texi and updating the libtasn1 patches in
  grub-core/lib/libtasn1-patches/
- Merging all the TPM2 TSS stack patches into one to reduce the total
  patch number
- Switching the default asymmetric algorithm from RSA2048 to
  TPM_ECC_NIST_P256 for the faster key 

[PATCH v11 05/20] libtasn1: fix the potential buffer overrun

2024-04-12 Thread Gary Lin via Grub-devel
In _asn1_tag_der(), the first while loop for the long form may end up
with a 'k' value with 'ASN1_MAX_TAG_SIZE' and cause the buffer overrun
in the second while loop. This commit tweaks the conditional check to
avoid producing a too large 'k'.

This is a quick fix and may differ from the official upstream fix.

libtasn1 issue: https://gitlab.com/gnutls/libtasn1/-/issues/49

Signed-off-by: Gary Lin 
---
 grub-core/lib/libtasn1/lib/coding.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/grub-core/lib/libtasn1/lib/coding.c 
b/grub-core/lib/libtasn1/lib/coding.c
index 5d03bca9d..0458829a5 100644
--- a/grub-core/lib/libtasn1/lib/coding.c
+++ b/grub-core/lib/libtasn1/lib/coding.c
@@ -143,7 +143,7 @@ _asn1_tag_der (unsigned char class, unsigned int tag_value,
  temp[k++] = tag_value & 0x7F;
  tag_value >>= 7;
 
- if (k > ASN1_MAX_TAG_SIZE - 1)
+ if (k >= ASN1_MAX_TAG_SIZE - 1)
break;  /* will not encode larger tags */
}
   *ans_len = k + 1;
-- 
2.35.3


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


[PATCH v11 03/20] libtasn1: disable code not needed in grub

2024-04-12 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

We don't expect to be able to write ASN.1, only read it,
so we can disable some code.

Do that with #if 0/#endif, rather than deletion. This means
that the difference between upstream and grub is smaller,
which should make updating libtasn1 easier in the future.

With these exclusions we also avoid the need for minmax.h,
which is convenient because it means we don't have to
import it from gnulib.

Cc: Vladimir Serbinenko 
Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
---
 grub-core/lib/libtasn1/lib/coding.c| 12 ++--
 grub-core/lib/libtasn1/lib/decoding.c  |  2 ++
 grub-core/lib/libtasn1/lib/element.c   |  6 +++---
 grub-core/lib/libtasn1/lib/errors.c|  3 +++
 grub-core/lib/libtasn1/lib/structure.c | 10 ++
 include/grub/libtasn1.h| 15 +++
 6 files changed, 39 insertions(+), 9 deletions(-)

diff --git a/grub-core/lib/libtasn1/lib/coding.c 
b/grub-core/lib/libtasn1/lib/coding.c
index ea5bc370e..5d03bca9d 100644
--- a/grub-core/lib/libtasn1/lib/coding.c
+++ b/grub-core/lib/libtasn1/lib/coding.c
@@ -30,11 +30,11 @@
 #include "parser_aux.h"
 #include 
 #include "element.h"
-#include "minmax.h"
 #include 
 
 #define MAX_TAG_LEN 16
 
+#if 0 /* GRUB SKIPPED IMPORTING */
 /**/
 /* Function : _asn1_error_description_value_not_found */
 /* Description: creates the ErrorDescription string   */
@@ -58,6 +58,7 @@ _asn1_error_description_value_not_found (asn1_node node,
   Estrcat (ErrorDescription, "' not found");
 
 }
+#endif
 
 /**
  * asn1_length_der:
@@ -244,6 +245,7 @@ asn1_encode_simple_der (unsigned int etype, const unsigned 
char *str,
   return ASN1_SUCCESS;
 }
 
+#if 0 /* GRUB SKIPPED IMPORTING */
 /**/
 /* Function : _asn1_time_der  */
 /* Description: creates the DER coding for a TIME */
@@ -278,7 +280,7 @@ _asn1_time_der (unsigned char *str, int str_len, unsigned 
char *der,
 
   return ASN1_SUCCESS;
 }
-
+#endif
 
 /*
 void
@@ -519,6 +521,7 @@ asn1_bit_der (const unsigned char *str, int bit_len,
 }
 
 
+#if 0 /* GRUB SKIPPED IMPORTING */
 /**/
 /* Function : _asn1_complete_explicit_tag */
 /* Description: add the length coding to the EXPLICIT */
@@ -595,6 +598,7 @@ _asn1_complete_explicit_tag (asn1_node node, unsigned char 
*der,
 
   return ASN1_SUCCESS;
 }
+#endif
 
 const tag_and_class_st _asn1_tags[] = {
   [ASN1_ETYPE_GENERALSTRING] =
@@ -647,6 +651,8 @@ const tag_and_class_st _asn1_tags[] = {
 
 unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]);
 
+
+#if 0 /* GRUB SKIPPED IMPORTING */
 /**/
 /* Function : _asn1_insert_tag_der*/
 /* Description: creates the DER coding of tags of one */
@@ -1423,3 +1429,5 @@ error:
   asn1_delete_structure ();
   return err;
 }
+
+#endif
diff --git a/grub-core/lib/libtasn1/lib/decoding.c 
b/grub-core/lib/libtasn1/lib/decoding.c
index b9245c486..bf9cb13ac 100644
--- a/grub-core/lib/libtasn1/lib/decoding.c
+++ b/grub-core/lib/libtasn1/lib/decoding.c
@@ -1620,6 +1620,7 @@ asn1_der_decoding (asn1_node * element, const void *ider, 
int ider_len,
   return asn1_der_decoding2 (element, ider, _len, 0, errorDescription);
 }
 
+#if 0 /* GRUB SKIPPED IMPORTING */
 /**
  * asn1_der_decoding_element:
  * @structure: pointer to an ASN1 structure
@@ -1650,6 +1651,7 @@ asn1_der_decoding_element (asn1_node * structure, const 
char *elementName,
 {
   return asn1_der_decoding (structure, ider, len, errorDescription);
 }
+#endif
 
 /**
  * asn1_der_decoding_startEnd:
diff --git a/grub-core/lib/libtasn1/lib/element.c 
b/grub-core/lib/libtasn1/lib/element.c
index d4c558e10..bc4c3c8d7 100644
--- a/grub-core/lib/libtasn1/lib/element.c
+++ b/grub-core/lib/libtasn1/lib/element.c
@@ -118,7 +118,7 @@ _asn1_convert_integer (const unsigned char *value, unsigned 
char *value_out,
value_out[k2 - k] = val[k2];
 }
 
-#if 0
+#if 0 /* GRUB SKIPPED IMPORTING */
   printf ("_asn1_convert_integer: valueIn=%s, lenOut=%d", value, *len);
   for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++)
 printf (", vOut[%d]=%d", k, value_out[k]);
@@ -191,7 +191,7 @@ _asn1_append_sequence_set (asn1_node node, struct 
node_tail_cache_st *pcache)
   return ASN1_SUCCESS;
 }
 
-
+#if 0
 /**
  * asn1_write_value:
  * @node_root: pointer to a structure
@@ -646,7 +646,7 @@ asn1_write_value (asn1_node node_root, const char *name,
 
   return ASN1_SUCCESS;
 }
-
+#endif
 
 #define PUT_VALUE( ptr, ptr_size, data, data_size) \
*len = data_size; \
diff --git a/grub-core/lib/libtasn1/lib/errors.c 
b/grub-core/lib/libtasn1/lib/errors.c
index aef5dfe6f..2b2322152 100644
--- a/grub-core/lib/libtasn1/lib/errors.c
+++ b/grub-core/lib/libtasn1/lib/errors.c
@@ -57,6 +57,8 @@ static const libtasn1_error_entry error_algorithms[] = {
   {0, 0}
 };
 
+
+#if 

[PATCH v11 04/20] libtasn1: changes for grub compatibility

2024-04-12 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

Do a few things to make libtasn1 compile as part of grub:

 - remove _asn1_strcat and replace strcat with the bound-checked
   _asn1_str_cat except the one inside _asn1_str_cat. That strcat is
   replaced with strcpy.

 - adjust header paths in libtasn1.h

 - adjust header paths to "grub/libtasn1.h".

 - replace a 64 bit division with a call to grub_divmod64, preventing
   creation of __udivdi3 calls on 32 bit platforms.

Cc: Vladimir Serbinenko 
Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
---
 grub-core/lib/libtasn1/lib/decoding.c   | 8 
 grub-core/lib/libtasn1/lib/element.c| 2 +-
 grub-core/lib/libtasn1/lib/gstr.c   | 2 +-
 grub-core/lib/libtasn1/lib/int.h| 3 +--
 grub-core/lib/libtasn1/lib/parser_aux.c | 2 +-
 include/grub/libtasn1.h | 5 ++---
 6 files changed, 10 insertions(+), 12 deletions(-)

diff --git a/grub-core/lib/libtasn1/lib/decoding.c 
b/grub-core/lib/libtasn1/lib/decoding.c
index bf9cb13ac..51859fe36 100644
--- a/grub-core/lib/libtasn1/lib/decoding.c
+++ b/grub-core/lib/libtasn1/lib/decoding.c
@@ -2016,8 +2016,8 @@ asn1_expand_octet_string (asn1_node_const definitions, 
asn1_node * element,
  (p2->type & CONST_ASSIGN))
{
  strcpy (name, definitions->name);
- strcat (name, ".");
- strcat (name, p2->name);
+ _asn1_str_cat (name, sizeof (name), ".");
+ _asn1_str_cat (name, sizeof (name), p2->name);
 
  len = sizeof (value);
  result = asn1_read_value (definitions, name, value, );
@@ -2034,8 +2034,8 @@ asn1_expand_octet_string (asn1_node_const definitions, 
asn1_node * element,
  if (p2)
{
  strcpy (name, definitions->name);
- strcat (name, ".");
- strcat (name, p2->name);
+ _asn1_str_cat (name, sizeof (name), ".");
+ _asn1_str_cat (name, sizeof (name), p2->name);
 
  result = asn1_create_element (definitions, name, );
  if (result == ASN1_SUCCESS)
diff --git a/grub-core/lib/libtasn1/lib/element.c 
b/grub-core/lib/libtasn1/lib/element.c
index bc4c3c8d7..8694fecb9 100644
--- a/grub-core/lib/libtasn1/lib/element.c
+++ b/grub-core/lib/libtasn1/lib/element.c
@@ -688,7 +688,7 @@ asn1_write_value (asn1_node node_root, const char *name,
 return ASN1_MEM_ERROR; \
 } else { \
 /* this strcat is checked */ \
-if (ptr) _asn1_strcat (ptr, data); \
+if (ptr) _asn1_str_cat ((char *)ptr, ptr_size, (const char 
*)data); \
 }
 
 /**
diff --git a/grub-core/lib/libtasn1/lib/gstr.c 
b/grub-core/lib/libtasn1/lib/gstr.c
index eef419554..a9c16f5d3 100644
--- a/grub-core/lib/libtasn1/lib/gstr.c
+++ b/grub-core/lib/libtasn1/lib/gstr.c
@@ -36,7 +36,7 @@ _asn1_str_cat (char *dest, size_t dest_tot_size, const char 
*src)
 
   if (dest_tot_size - dest_size > str_size)
 {
-  strcat (dest, src);
+  strcpy (dest + dest_size, src);
 }
   else
 {
diff --git a/grub-core/lib/libtasn1/lib/int.h b/grub-core/lib/libtasn1/lib/int.h
index d94d51c8c..7409c7655 100644
--- a/grub-core/lib/libtasn1/lib/int.h
+++ b/grub-core/lib/libtasn1/lib/int.h
@@ -35,7 +35,7 @@
 #  include 
 # endif
 
-# include 
+# include "grub/libtasn1.h"
 
 # define ASN1_SMALL_VALUE_SIZE 16
 
@@ -115,7 +115,6 @@ extern const tag_and_class_st _asn1_tags[];
 # define _asn1_strtoul(n,e,b) strtoul((const char *) n, e, b)
 # define _asn1_strcmp(a,b) strcmp((const char *)a, (const char *)b)
 # define _asn1_strcpy(a,b) strcpy((char *)a, (const char *)b)
-# define _asn1_strcat(a,b) strcat((char *)a, (const char *)b)
 
 # if SIZEOF_UNSIGNED_LONG_INT == 8
 #  define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b)
diff --git a/grub-core/lib/libtasn1/lib/parser_aux.c 
b/grub-core/lib/libtasn1/lib/parser_aux.c
index c05bd2339..e4e4c0556 100644
--- a/grub-core/lib/libtasn1/lib/parser_aux.c
+++ b/grub-core/lib/libtasn1/lib/parser_aux.c
@@ -632,7 +632,7 @@ _asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE])
   count = 0;
   do
 {
-  d = val / 10;
+  d = grub_divmod64(val, 10, NULL);
   r = val - d * 10;
   temp[start + count] = '0' + (char) r;
   count++;
diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h
index 058ab27b0..7d64b6ab7 100644
--- a/include/grub/libtasn1.h
+++ b/include/grub/libtasn1.h
@@ -54,9 +54,8 @@
 #  define __LIBTASN1_PURE__
 # endif
 
-# include 
-# include 
-# include /* for FILE* */
+# include 
+# include 
 
 # ifdef __cplusplus
 extern "C"
-- 
2.35.3


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


[PATCH v11 01/20] posix_wrap: tweaks in preparation for libtasn1

2024-04-12 Thread Gary Lin via Grub-devel
From: Daniel Axtens 

 - Define SIZEOF_UNSIGNED_LONG_INT, it's the same as
   SIZEOF_UNSIGNED_LONG.

 - Define WORD_BIT, the size in bits of an int. This is a defined
   in the Single Unix Specification and in gnulib's limits.h. gnulib
   assumes it's 32 bits on all our platforms, including 64 bit
   platforms, so we also use that value.

 - Provide strto[u]l[l] preprocessor macros that resolve to
   grub_strto[u]l[l]. To avoid gcrypt redefining strtoul, we
   also define HAVE_STRTOUL here.

 - Implement c-ctype.h and the functions defined in the header.

 - Implement strncat in string.h.

Cc: Vladimir Serbinenko 
Signed-off-by: Daniel Axtens 
Signed-off-by: Gary Lin 
---
 grub-core/lib/posix_wrap/c-ctype.h   | 114 +++
 grub-core/lib/posix_wrap/limits.h|   1 +
 grub-core/lib/posix_wrap/stdlib.h|   8 ++
 grub-core/lib/posix_wrap/string.h|  21 +
 grub-core/lib/posix_wrap/sys/types.h |   1 +
 5 files changed, 145 insertions(+)
 create mode 100644 grub-core/lib/posix_wrap/c-ctype.h

diff --git a/grub-core/lib/posix_wrap/c-ctype.h 
b/grub-core/lib/posix_wrap/c-ctype.h
new file mode 100644
index 0..5f8fc8ce3
--- /dev/null
+++ b/grub-core/lib/posix_wrap/c-ctype.h
@@ -0,0 +1,114 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  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 .
+ */
+
+#ifndef GRUB_POSIX_C_CTYPE_H
+#define GRUB_POSIX_C_CTYPE_H   1
+
+#include 
+
+static inline bool
+c_isspace (int c)
+{
+  return !!grub_isspace (c);
+}
+
+static inline bool
+c_isdigit (int c)
+{
+  return !!grub_isdigit (c);
+}
+
+static inline bool
+c_islower (int c)
+{
+  return !!grub_islower (c);
+}
+
+static inline bool
+c_isascii (int c)
+{
+  return !(c & ~0x7f);
+}
+
+static inline bool
+c_isupper (int c)
+{
+  return !!grub_isupper (c);
+}
+
+static inline bool
+c_isxdigit (int c)
+{
+  return !!grub_isxdigit (c);
+}
+
+static inline bool
+c_isprint (int c)
+{
+  return !!grub_isprint (c);
+}
+
+static inline bool
+c_iscntrl (int c)
+{
+  return !grub_isprint (c);
+}
+
+static inline bool
+c_isgraph (int c)
+{
+  return grub_isprint (c) && !grub_isspace (c);
+}
+
+static inline bool
+c_isalnum (int c)
+{
+  return grub_isalpha (c) || grub_isdigit (c);
+}
+
+static inline bool
+c_ispunct (int c)
+{
+  return grub_isprint (c) && !grub_isspace (c) && !c_isalnum (c);
+}
+
+static inline bool
+c_isalpha (int c)
+{
+  return !!grub_isalpha (c);
+}
+
+static inline bool
+c_isblank (int c)
+{
+  return c == ' ' || c == '\t';
+}
+
+static inline int
+c_tolower (int c)
+{
+  return grub_tolower (c);
+}
+
+static inline int
+c_toupper (int c)
+{
+  return grub_toupper (c);
+}
+
+#endif
diff --git a/grub-core/lib/posix_wrap/limits.h 
b/grub-core/lib/posix_wrap/limits.h
index 26918c8a0..4be7b4080 100644
--- a/grub-core/lib/posix_wrap/limits.h
+++ b/grub-core/lib/posix_wrap/limits.h
@@ -41,5 +41,6 @@
 #define LONG_MAX GRUB_LONG_MAX
 
 #define CHAR_BIT 8
+#define WORD_BIT 32
 
 #endif
diff --git a/grub-core/lib/posix_wrap/stdlib.h 
b/grub-core/lib/posix_wrap/stdlib.h
index f5279756a..14e4efdd0 100644
--- a/grub-core/lib/posix_wrap/stdlib.h
+++ b/grub-core/lib/posix_wrap/stdlib.h
@@ -64,4 +64,12 @@ abort (void)
   grub_abort ();
 }
 
+#define strtol grub_strtol
+
+/* for libgcrypt */
+#define HAVE_STRTOUL
+#define strtoul grub_strtoul
+
+#define strtoull grub_strtoull
+
 #endif
diff --git a/grub-core/lib/posix_wrap/string.h 
b/grub-core/lib/posix_wrap/string.h
index 1adb450b5..b0c5928d2 100644
--- a/grub-core/lib/posix_wrap/string.h
+++ b/grub-core/lib/posix_wrap/string.h
@@ -84,6 +84,27 @@ memchr (const void *s, int c, grub_size_t n)
   return grub_memchr (s, c, n);
 }
 
+static inline char *
+strncat(char *dest, const char *src, grub_size_t n)
+{
+  const char *end;
+  char *str = dest;
+  grub_size_t src_len;
+
+  dest += grub_strlen (dest);
+
+  end = grub_memchr (src, '\0', n);
+  if (end != NULL)
+src_len = (grub_size_t) (end - src);
+  else
+src_len = n;
+
+  dest[src_len] = '\0';
+  grub_memcpy (dest, src, src_len);
+
+  return str;
+}
+
 #define memcmp grub_memcmp
 #define memcpy grub_memcpy
 #define memmove grub_memmove
diff --git a/grub-core/lib/posix_wrap/sys/types.h 
b/grub-core/lib/posix_wrap/sys/types.h
index eeda543c4..2f3e86549 100644
--- a/grub-core/lib/posix_wrap/sys/types.h
+++ b/grub-core/lib/posix_wrap/sys/types.h
@@ -50,6 

Re: [PATCH] util/grub.d/30_os-prober.in: Fix GRUB_OS_PROBER_SKIP_LIST for non-EFI

2024-04-12 Thread Pascal Hambourg

Friendly ping. Any comments about this ?

On 19/01/2024 at 11:28, Pascal Hambourg wrote:

GRUB documentation states:

'GRUB_OS_PROBER_SKIP_LIST'
   List of space-separated FS UUIDs of filesystems to be ignored from
   os-prober output. For efi chainloaders it's @

But the actual behaviour does not match this description. Setting

   GRUB_OS_PROBER_SKIP_LIST=""

does not ignore non-EFI boot loaders detected on filesystem .
In order to skip non-EFI boot loaders, you must set

   GRUB_OS_PROBER_SKIP_LIST="@"

which is both absurd (UUID and device are redundant) and wrong
(device names such as /dev/sd* may not be persistent across boots).

This patch fixes the detection of "@" in the device string
reported by os-prober.

Fixes: 55e706c9 (Add GRUB_OS_PROBER_SKIP_LIST to selectively skipping systems)

Note: the UUID matching regex uses word boundaries '\b' but '@' is
not a word character. As a consequence, setting

   GRUB_OS_PROBER_SKIP_LIST="@"

will ignore non-EFI boot loaders detected on filesystem  even
though the goal is to ignore a given EFI boot loader only.
However I think this is desirable to preserve the behaviour of
existing setups which use GRUB_OS_PROBER_SKIP_LIST="@".

Signed-off-by: Pascal Hambourg 
---
  util/grub.d/30_os-prober.in | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in
index e9e217208..a24c01334 100644
--- a/util/grub.d/30_os-prober.in
+++ b/util/grub.d/30_os-prober.in
@@ -125,7 +125,7 @@ for OS in ${OSPROBED} ; do
if UUID="`${grub_probe} --target=fs_uuid --device ${DEVICE%@*}`"; then
  EXPUUID="$UUID"
  
-if [ x"${DEVICE#*@}" != x ] ; then

+if [ x"${DEVICE%@*}" != x"${DEVICE}" ] ; then
EXPUUID="${EXPUUID}@${DEVICE#*@}"
  fi
  


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


Re: [PATCH v10 00/20] Automatic Disk Unlock with TPM2

2024-04-12 Thread Gary Lin via Grub-devel
On Tue, Apr 09, 2024 at 04:30:32PM +0800, Gary Lin wrote:
> GIT repo for v10: https://github.com/lcp/grub2/tree/tpm2-unlock-v10
> 
> This patch series is based on "Automatic TPM Disk Unlock"(*1) posted by
> Hernan Gatta to introduce the key protector framework and TPM2 stack
> to GRUB2, and this could be a useful feature for the systems to
> implement full disk encryption.
> 
-->8--
> 
> v10:
> - Fixing the coverity issues: CID 435775, CID 435771, CID 435770, CID
>   435769, CID 435767, CID 435761
>   https://lists.gnu.org/archive/html/grub-devel/2024-02/txtKIuUb5lf3O.txt
>   - Fixing the potential memory leak (CID 435775)
>   - Removing the unnecessary grub_protect_get_grub_drive_for_file() from
> util/grub-protect.c (CID 435771)
>   - Using the grub_tpm2_mu_TPM2B_*_Unmarshal functions to unmarshal the
> TPM2B structs instead of a generic grub_tpm2_mu_TPM2B_Unmarshal
> (CID 435770)
>   - Fixing Null pointer dereference (CID 435769)
>   - Adding bound checks to grub_tpm2_mu_TPML_DIGEST_Unmarshal()
> (CID 435767)
>   - Improving the check for the return value of ftell() (CID 435761)
> - Adding a quick fix for CID 435762
> - Removing the empty ending line in tests/asn1_test.in
> - Fixing docs/grub-dev.texi and updating the libtasn1 patches in
>   grub-core/lib/libtasn1-patches/
> - Merging all the TPM2 TSS stack patches into one to reduce the total
>   patch number
> - Switching the default asymmetric algorithm from RSA2048 to
>   TPM_ECC_NIST_P256 for the faster key generation
I forgot to update the help messages to reflect the change.
Will fix the help in v11...

> - Adding the fallback SRK templates to try a few more SRK types in case
>   grub2 failed to associate the sealed key with the SRK in the persistent
>   handle or the default SRK
> - Improving the test script to add tests for the persistent handle and
>   the fallback SRKs

Gary Lin

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