[PATCH] Allow background to be set when theme is set

2024-06-13 Thread Lin via Grub-devel


  
  
Hello, first time contributor here.
  I'm not experienced with mailing lists and mailing patches so just
  let me know if I do anything wrong :)
This patch simply allows that if GRUB_BACKGROUND and GRUB_THEME
  is set at the same time, both pieces of code gets generated into
  grub.cfg by grub-mkconfig.
If a theme is loaded, the background image will be visible inside
  the console.
Lxtharia




  
diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in
index 4e8f25702..f86b69bad 100644
--- a/util/grub.d/00_header.in
+++ b/util/grub.d/00_header.in
@@ -263,9 +263,7 @@ EOF
 set theme=(\$root)`make_system_path_relative_to_its_root $GRUB_THEME`
 export theme
 EOF
-fi
-
-if [ "x$GRUB_BACKGROUND" != x ] && [ -f "$GRUB_BACKGROUND" ] \
+elif [ "x$GRUB_BACKGROUND" != x ] && [ -f "$GRUB_BACKGROUND" ] \
 	&& is_path_readable_by_grub "$GRUB_BACKGROUND"; then
 	gettext_printf "Found background: %s\n" "$GRUB_BACKGROUND" >&2
 	case "$GRUB_BACKGROUND" in 
___
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel


Re: [PATCH] Allow background to be set when theme is set

2024-10-22 Thread Lin via Grub-devel

On 13.06.24 17:28, Lin wrote:


Hello, first time contributor here.
I'm not experienced with mailing lists and mailing patches so just let
me know if I do anything wrong :)

This patch simply allows that if GRUB_BACKGROUND and GRUB_THEME is set
at the same time, both pieces of code gets generated into grub.cfg by
grub-mkconfig.

If a theme is loaded, the background image will be visible inside the
console.


Already made my first mistake and got my patch reversed lol. Corrected
patch is below.

Again, when setting a GRUB_THEME, the elif disallows the GRUB_BACKGROUND
to be used, yet with my patch applied a set background image will not
interfere with the theme, but only be visible as the background for the
console when opened with 'c'.

Thanks,
Lin


diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in
index f86b69bad..4e8f25702 100644
--- a/util/grub.d/00_header.in
+++ b/util/grub.d/00_header.in
@@ -263,7 +263,9 @@ EOF
 set theme=(\$root)`make_system_path_relative_to_its_root $GRUB_THEME`
 export theme
 EOF
-    elif [ "x$GRUB_BACKGROUND" != x ] && [ -f "$GRUB_BACKGROUND" ] \
+    fi
+
+    if [ "x$GRUB_BACKGROUND" != x ] && [ -f "$GRUB_BACKGROUND" ] \
 && is_path_readable_by_grub "$GRUB_BACKGROUND"; then
 gettext_printf "Found background: %s\n" "$GRUB_BACKGROUND" >&2
 case "$GRUB_BACKGROUND" in



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


Re: [PATCH v9 00/22] Automatic Disk Unlock with TPM2

2024-02-21 Thread Gary Lin via Grub-devel
On Thu, Feb 08, 2024 at 08:58:43PM +0100, Daniel Kiper wrote:
> Hey,
> 
> Adding Patrick...
> 
> On Mon, Feb 05, 2024 at 03:39:33PM +0800, Gary Lin wrote:
> > GIT repo for v9: https://github.com/lcp/grub2/tree/tpm2-unlock-v9
> >
> > 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.
> 
> Sadly this patch set have many issues...
> 
> The git complains in the following way...
> 
>   Applying: asn1_test: test module for libtasn1
>   .git/rebase-apply/patch:1374: new blank line at EOF.
>   warning: 1 line adds whitespace errors.
Will be fixed in v10.

>   Applying: libtasn1: Add the documentation
>   .git/rebase-apply/patch:90: trailing whitespace.
>   .git/rebase-apply/patch:92: trailing whitespace.
>   .git/rebase-apply/patch:99: trailing whitespace.
>   .git/rebase-apply/patch:102: trailing whitespace.
>   .git/rebase-apply/patch:108: trailing whitespace.
>   warning: squelched 80 whitespace errors
>   warning: 85 lines add whitespace errors.
> 
Those whitespaces naturally exist in the two 'patch' files added in this
commit, so it's hard to avoid the warning.

> The developers manual does not build due to following errors...
> 
>   grub-dev.texi:616: misplaced {
>   grub-dev.texi:616: misplaced }
>   grub-dev.texi:617: misplaced {
>   grub-dev.texi:617: misplaced }
>   grub-dev.texi:580: warning: node `libtasn1' is next for `minilzo' in 
> sectioning but not in menu
>   grub-dev.texi:599: warning: unreferenced node `libtasn1'
>   grub-dev.texi:599: warning: node `minilzo' is prev for `libtasn1' in 
> sectioning but not in menu
>   grub-dev.texi:599: warning: node `Updating External Code' is up for 
> `libtasn1' in sectioning but not in menu
>   grub-dev.texi:499: node `Updating External Code' lacks menu item for 
> `libtasn1' despite being its Up target
> 
Will be fixed in v10.

> And I have attached the Coverity report. All issues reported there have
> to be fixed. If you cannot fix an issue you have to explain why you
> cannot do that and what is potential impact on the code stability,
> security, etc.
> 
I'm working on that and will fix them in v10.

> Please do not forget to check code which you add adhere to GRUB coding
> style [1]. Good example is in grub-core/kern/efi/sb.c too. Of course
> this requirement does not apply to the libs which you import.
> 
> Additionally, please CC patrick.c...@oracle.com next time. He is
> interested in this patch set development.
> 
Added Patrick to my patch sending script.

Thanks,

Gary Lin

> Daniel
> 
> [1] 
> https://www.gnu.org/software/grub/manual/grub-dev/grub-dev.html#Coding-style


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


Re: [PATCH v9 00/22] Automatic Disk Unlock with TPM2

2024-03-07 Thread Gary Lin via Grub-devel
On Thu, Feb 08, 2024 at 08:58:43PM +0100, Daniel Kiper wrote:
> Hey,
> 
--8<--
> 
> And I have attached the Coverity report. All issues reported there have
> to be fixed. If you cannot fix an issue you have to explain why you
> cannot do that and what is potential impact on the code stability,
> security, etc.
> 
I have went through all the coverity issues. There are 6 issues in the
TPM2 stack and the utility:

CID 435775, CID 435771, CID 435770, CID 435769, CID 435767, CID 435761 

Those will be fixed in the next version.

The 9 issues are from libtasn1 and gnulib.

There are two memory corruption issue: CID 435762 and CID 435766, and
I've filed the issues in libtasn1 upstream.

- CID 435762
  https://gitlab.com/gnutls/libtasn1/-/issues/49
  This is a valid case. However, the only exploitable function,
  _asn1_insert_tag_der(), is disabled in grub2 patch, so the severity is
  low. I have a quick fix but upstream would like to fix it in another
  way.
- CID 435766
  https://gitlab.com/gnutls/libtasn1/-/issues/50
  IMO, this is false positive, but I need libtasn1 upstream to confirm
  that.

Then, the remaining 7 Integer handling issues are involved with the macros
from gnulib, and I believe those are false positive.

The following are my analyses:


*** CID 435774:  Integer handling issues  (CONSTANT_EXPRESSION_RESULT)
/grub-core/lib/libtasn1/lib/decoding.c: 481 in asn1_get_object_id_der()
475*/
476   if (leading != 0 && der[len_len + k] == 0x80)
477 return ASN1_DER_ERROR;
478   leading = 0;
479 
480   /* check for wrap around */
>>> CID 435774:  Integer handling issues  (CONSTANT_EXPRESSION_RESULT)
>>> "val < 1 ? 0 : val) - 1 < 0) ? ~1 ? 0 : val) + 1 << 62UL /* 
>>> sizeof (+val) * 8 - 2 */) - 1) * 2 + 1) : ((1 ? 0 : val) + 0)) >> 7)" is 
>>> always false regardless of the values of its operands. This occurs as the 
>>> second operand of "?:".
481   if (INT_LEFT_SHIFT_OVERFLOW (val, 7))
482 return ASN1_DER_ERROR;
483 
484   val = val << 7;
485   val |= der[len_len + k] & 0x7F;
486 

Here are the related macros:

#define EXPR_SIGNED(e) (_GL_INT_NEGATE_CONVERT (e, 1) < 0)

#define _GL_INT_CONVERT(e, v) ((1 ? 0 : (e)) + (v))

#define _GL_SIGNED_INT_MAXIMUM(e)   \
  (((_GL_INT_CONVERT (e, 1) << (TYPE_WIDTH (+ (e)) - 2)) - 1) * 2 + 1)

#define _GL_INT_MINIMUM(e)  \
  (EXPR_SIGNED (e)  \
   ? ~ _GL_SIGNED_INT_MAXIMUM (e)   \
   : _GL_INT_CONVERT (e, 0))

#define INT_LEFT_SHIFT_OVERFLOW(a, b) \
  INT_LEFT_SHIFT_RANGE_OVERFLOW (a, b, \
 _GL_INT_MINIMUM (a), _GL_INT_MAXIMUM (a))

#define INT_LEFT_SHIFT_RANGE_OVERFLOW(a, b, min, max)   \
  ((a) < 0  \
   ? (a) < (min) >> (b) \
   : (max) >> (b) < (a))

The statement in question is expanded "(a) < (min) >> (b)" of
INT_LEFT_SHIFT_RANGE_OVERFLOW. Since 'val' is 'uint64_t', '(a) < 0' is always
false, so the result of that statement doen't matter.


*** CID 435773:  Integer handling issues  (NO_EFFECT)
/grub-core/lib/libtasn1/lib/decoding.c: 439 in asn1_get_object_id_der()
433 return ASN1_DER_ERROR;
434 
435   val0 = 0;
436 
437   for (k = 0; k < len; k++)
438 {
>>> CID 435773:  Integer handling issues  (NO_EFFECT)
>>> This less-than-zero comparison of an unsigned value is never true. "(1 
>>> ? 0UL : val0) - 1UL < 0UL".
439   if (INT_LEFT_SHIFT_OVERFLOW (val0, 7))
440 return ASN1_DER_ERROR;
441 
442   val0 <<= 7;
443   val0 |= der[len_len + k] & 0x7F;
444   if (!(der[len_len + k] & 0x80))

Here are the related macros:

#define _GL_INT_NEGATE_CONVERT(e, v) ((1 ? 0 : (e)) - (v))
#define EXPR_SIGNED(e) (_GL_INT_NEGATE_CONVERT (e, 1) < 0)

The statement in question is the expanded 'EXPR_SIGNED'. Since that macro is
to test whether the variable is signed or not. For 'uint64_t val0', it's
expected to be always false.


*** CID 435772:  Integer handling issues  (NO_EFFECT)
/grub-core/lib/libtasn1/lib/decoding.c: 204 in asn1_get_tag_der()
198   /* Long form */
199   punt = 1;
200   ris = 0;
201   while (punt < der_len && der[punt] & 128)
202 {
203 
>>> CID 435772:  Integer handling issues  (NO_EFFECT)
>>> This less-than-zero comparison of an unsigned value is never true. "(1 
>>> ? 0U : ((1 ? 0U : ris) + 128U)) - 1U < 0U".
204   if (INT_

Re: [PATCH RFC 0/2] use confidential computing provisioned secrets for disk decryption

2024-03-12 Thread Gary Lin via Grub-devel
On Tue, Mar 12, 2024 at 11:03:17AM +0800, Hyman Huang wrote:
Hi Hyman,

> This patchset aims to supplement James's previous work, please refer to
> the following link for details:
> https://lists.gnu.org/archive/html/grub-devel/2020-12/msg00257.html
> 
> The alterations listed below were made in light of earlier research:
> 1. As Glenn advised, remove the first commit ([PATCH v3 1/3] cryptodisk:
>make the password getter and additional argument to recover_key) while
>maintaining the original recover key function declaration.
> 
> 2. To decrypt the disk, use the password that was retrieved from the EFI
>secret area and store it in the key_data field of the
>grub_cryptomount_args_t. Then, pass the password to the
>grub_cryptodisk_scan_device function.
> 
> 3. Modify the put method's function definition in struct
>grub_secret_entry, and use grub_errno to log method errors.
> 
> We uploaded this series with the intention of receiving feedback, as
> the title suggests. Any suggestions and feedback regarding this patchset
> are welcom. 
> 
I'm working on disk unlocking with TPM2 and it's a bit similar to your
work. Here is my v9 patchset:
https://lists.gnu.org/archive/html/grub-devel/2024-02/msg7.html

The original patch author, Herman, introduced the key protectors
framework, and it may be useful in your case:

- key protectors framework
  https://lists.gnu.org/archive/html/grub-devel/2024-02/msg00014.html
- cryptodisk change
  https://lists.gnu.org/archive/html/grub-devel/2024-02/msg00016.html
- TPM2 key protector
  https://lists.gnu.org/archive/html/grub-devel/2024-02/msg00017.html

In short, it unlocks the disk with the commands like this:

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

Maybe we can work together to support various disk unlocking mechanisms.

Cheers,

Gary Lin

> Thanks,
> 
> Yong
> 
> Hyman Huang (2):
>   cryptodisk: add OS provided secret support
>   efi: Add API for retrieving the EFI secret for cryptodisk
> 
>  grub-core/Makefile.core.def|   8 +++
>  grub-core/disk/cryptodisk.c|  49 -
>  grub-core/disk/efi/efisecret.c | 123 +
>  include/grub/cryptodisk.h  |  14 
>  include/grub/efi/api.h |  15 
>  5 files changed, 206 insertions(+), 3 deletions(-)
>  create mode 100644 grub-core/disk/efi/efisecret.c
> 
> -- 
> 2.39.3
> 
> 
> ___
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel

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


Re: [PATCH RFC 0/2] use confidential computing provisioned secrets for disk decryption

2024-03-12 Thread Gary Lin via Grub-devel
On Tue, Mar 12, 2024 at 04:00:02PM +0800, Yong Huang wrote:
> On Tue, Mar 12, 2024 at 3:28 PM Gary Lin  wrote:
> 
> > On Tue, Mar 12, 2024 at 11:03:17AM +0800, Hyman Huang wrote:
> > Hi Hyman,
> >
> > > This patchset aims to supplement James's previous work, please refer to
> > > the following link for details:
> > > https://lists.gnu.org/archive/html/grub-devel/2020-12/msg00257.html
> > >
> > > The alterations listed below were made in light of earlier research:
> > > 1. As Glenn advised, remove the first commit ([PATCH v3 1/3] cryptodisk:
> > >make the password getter and additional argument to recover_key) while
> > >maintaining the original recover key function declaration.
> > >
> > > 2. To decrypt the disk, use the password that was retrieved from the EFI
> > >secret area and store it in the key_data field of the
> > >grub_cryptomount_args_t. Then, pass the password to the
> > >grub_cryptodisk_scan_device function.
> > >
> > > 3. Modify the put method's function definition in struct
> > >grub_secret_entry, and use grub_errno to log method errors.
> > >
> > > We uploaded this series with the intention of receiving feedback, as
> > > the title suggests. Any suggestions and feedback regarding this patchset
> > > are welcom.
> > >
> > I'm working on disk unlocking with TPM2 and it's a bit similar to your
> > work. Here is my v9 patchset:
> > https://lists.gnu.org/archive/html/grub-devel/2024-02/msg7.html
> >
> > The original patch author, Herman, introduced the key protectors
> > framework, and it may be useful in your case:
> >
> I take it that the AMD SEV secret inject can be implemented using
> the key protector framework feature once it's ready?
> 
That's my first impression. The key protector framework goes through the
registered list and provides the password from the specified protector,
and it looks similar to your patch.

> >
> > - key protectors framework
> >   https://lists.gnu.org/archive/html/grub-devel/2024-02/msg00014.html
> > - cryptodisk change
> >   https://lists.gnu.org/archive/html/grub-devel/2024-02/msg00016.html
> 
> 
> So, Should I post version 2 and rebase on the patchset then?
> 
If you find it's reasonable to use the key protector framework, then yes :)

Thanks,

Gary Lin

> 
> >
> > - TPM2 key protector
> >   https://lists.gnu.org/archive/html/grub-devel/2024-02/msg00017.html
> >
> > In short, it unlocks the disk with the commands like this:
> >
> >   tpm2_key_protector_init --tpm2key=(hd0,gpt1)/efi/grub2/sealed-1.tpm
> >   cryptomount -u  -P tpm2
> >
> > Maybe we can work together to support various disk unlocking mechanisms.
> >
> 
> Sure, of course, yes.
> 
> 
> > Cheers,
> >
> > Gary Lin
> >
> > > Thanks,
> > >
> > > Yong
> > >
> > > Hyman Huang (2):
> > >   cryptodisk: add OS provided secret support
> > >   efi: Add API for retrieving the EFI secret for cryptodisk
> > >
> > >  grub-core/Makefile.core.def|   8 +++
> > >  grub-core/disk/cryptodisk.c|  49 -
> > >  grub-core/disk/efi/efisecret.c | 123 +
> > >  include/grub/cryptodisk.h  |  14 
> > >  include/grub/efi/api.h |  15 
> > >  5 files changed, 206 insertions(+), 3 deletions(-)
> > >  create mode 100644 grub-core/disk/efi/efisecret.c
> > >
> > > --
> > > 2.39.3
> > >
> > >
> > > ___
> > > Grub-devel mailing list
> > > Grub-devel@gnu.org
> > > https://lists.gnu.org/mailman/listinfo/grub-devel
> >
> 
> Thanks for the comments, :)
> 
> Yong
> 
> -- 
> Best regards

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


[PATCH] util/bash-completion: Fix for bash-completion 2.12

2024-03-24 Thread Gary Lin via Grub-devel
_split_longopt() was the bash-completion private API and removed since
bash-completion 2.12. This commit initializes the bash-completion
general variables with _init_completion() to avoid the potential
'command not found' error.

Although bash-completion 2.12 introduces _comp_initialize() to deprecate
_init_completion(), _init_completion() is still chosen for the better
backward compatibility.

Signed-off-by: Gary Lin 
---
 .../bash-completion.d/grub-completion.bash.in | 61 +++
 1 file changed, 22 insertions(+), 39 deletions(-)

diff --git a/util/bash-completion.d/grub-completion.bash.in 
b/util/bash-completion.d/grub-completion.bash.in
index 4c88ee901..749a5d3cf 100644
--- a/util/bash-completion.d/grub-completion.bash.in
+++ b/util/bash-completion.d/grub-completion.bash.in
@@ -151,13 +151,10 @@ __grub_list_modules () {
 # grub-set-default & grub-reboot
 #
 __grub_set_entry () {
-local cur prev split=false
+local cur prev words cword split
+_init_completion -s || return
 
 COMPREPLY=()
-cur=`_get_cword`
-prev=${COMP_WORDS[COMP_CWORD-1]}
-
-_split_longopt && split=true
 
 case "$prev" in
 --boot-directory)
@@ -180,11 +177,10 @@ __grub_set_entry () {
 # grub-editenv
 #
 __grub_editenv () {
-local cur prev
+local cur prev words cword
+_init_completion || return
 
 COMPREPLY=()
-cur=`_get_cword`
-prev=${COMP_WORDS[COMP_CWORD-1]}
 
 case "$prev" in
 create|list|set|unset)
@@ -201,10 +197,10 @@ __grub_editenv () {
 # grub-mkconfig
 #
 __grub_mkconfig () {
-local cur prev
+local cur prev words cword
+_init_completion || return
 
 COMPREPLY=()
-cur=`_get_cword`
 
 if [[ "$cur" == -* ]]; then
 __grubcomp "$(__grub_get_options_from_help)"
@@ -217,13 +213,10 @@ __grub_mkconfig () {
 # grub-setup
 #
 __grub_setup () {
-local cur prev split=false
+local cur prev words cword split
+_init_completion -s || return
 
 COMPREPLY=()
-cur=`_get_cword`
-prev=${COMP_WORDS[COMP_CWORD-1]}
-
-_split_longopt && split=true
 
 case "$prev" in
 -d|--directory)
@@ -246,15 +239,12 @@ __grub_setup () {
 # grub-install
 #
 __grub_install () {
-local cur prev last split=false
+local cur prev words cword split last
+_init_completion -s || return
 
 COMPREPLY=()
-cur=`_get_cword`
-prev=${COMP_WORDS[COMP_CWORD-1]}
 last=$(__grub_get_last_option)
 
-_split_longopt && split=true
-
 case "$prev" in
 --boot-directory)
 _filedir -d
@@ -287,10 +277,10 @@ __grub_install () {
 # grub-mkfont
 #
 __grub_mkfont () {
-local cur
+local cur prev words cword
+_init_completion || return
 
 COMPREPLY=()
-cur=`_get_cword`
 
 if [[ "$cur" == -* ]]; then
 __grubcomp "$(__grub_get_options_from_help)"
@@ -304,11 +294,10 @@ __grub_mkfont () {
 # grub-mkrescue
 #
 __grub_mkrescue () {
-local cur prev last
+local cur prev words cword last
+_init_completion || return
 
 COMPREPLY=()
-cur=`_get_cword`
-prev=${COMP_WORDS[COMP_CWORD-1]}
 last=$(__grub_get_last_option)
 
 if [[ "$cur" == -* ]]; then
@@ -330,13 +319,10 @@ __grub_mkrescue () {
 # grub-mkimage
 #
 __grub_mkimage () {
-local cur prev split=false
+local cur prev words cword split
+_init_completion -s || return
 
 COMPREPLY=()
-cur=`_get_cword`
-prev=${COMP_WORDS[COMP_CWORD-1]}
-
-_split_longopt && split=true
 
 case "$prev" in
 -d|--directory|-p|--prefix)
@@ -367,10 +353,10 @@ __grub_mkimage () {
 # grub-mkpasswd-pbkdf2
 #
 __grub_mkpasswd_pbkdf2 () {
-local cur
+local cur prev words cword
+_init_completion || return
 
 COMPREPLY=()
-cur=`_get_cword`
 
 if [[ "$cur" == -* ]]; then
 __grubcomp "$(__grub_get_options_from_help)"
@@ -384,13 +370,10 @@ __grub_mkpasswd_pbkdf2 () {
 # grub-probe
 #
 __grub_probe () {
-local cur prev split=false
+local cur prev words cword split
+_init_completion -s || return
 
 COMPREPLY=()
-cur=`_get_cword`
-prev=${COMP_WORDS[COMP_CWORD-1]}
-
-_split_longopt && split=true
 
 case "$prev" in
 -t|--target)
@@ -417,10 +400,10 @@ __grub_probe () {
 # grub-script-check
 #
 __grub_script_check () {
-local cur
+local cur prev words cword
+_init_completion || return
 
 COMPREPLY=()
-cur=`_get_cword`
 
 if [[ "$cur" == -* ]]; then
 __grubcomp "$(__grub_get_options_from_help)"
-- 
2.35.3


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


Re: [PATCH v9 00/22] Automatic Disk Unlock with TPM2

2024-04-07 Thread Gary Lin via Grub-devel
On Thu, Apr 04, 2024 at 11:03:47PM +0200, Daniel Kiper wrote:
> On Thu, Mar 07, 2024 at 04:59:05PM +0800, Gary Lin via Grub-devel wrote:
> > On Thu, Feb 08, 2024 at 08:58:43PM +0100, Daniel Kiper wrote:
> > > Hey,
> > >
> > --8<--
> > >
> > > And I have attached the Coverity report. All issues reported there have
> > > to be fixed. If you cannot fix an issue you have to explain why you
> > > cannot do that and what is potential impact on the code stability,
> > > security, etc.
> > >
> > I have went through all the coverity issues. There are 6 issues in the
> > TPM2 stack and the utility:
> 
> [...]
> 
> Any progress on this? You are blocking another patch set which depends
> on some code which you introduce. If there is no progress here I will
> ask an author of the other patch set to resume the work and queue your
> patch set as a second one to merge.
> 
I was waiting for upstream fix for CID 435762(*). It can be fixed by
tweaking an if statement slightly but upstream prefers code refactoring
to remove the loops. Anyway, the only potentially vulnerable call path
is disabled in my patch set, so we can choose to leave the issue for the
later update or just apply a quick fix.

Cheers,

Gary Lin

(*) https://gitlab.com/gnutls/libtasn1/-/issues/49

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


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

2024-04-09 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 (&node);
   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, &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}
 };

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

2024-04-09 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, &len);
@@ -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, &aux);
  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 v10 01/20] posix_wrap: tweaks in preparation for libtasn1

2024-04-09 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 

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

2024-04-09 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 --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 d52056c24..36943e578 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, &pubkey);
+  grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (cmd_buf, &policy_ref);
+  grub_tpm2_mu_TPMT_SIGNATURE_Unmarshal (cmd_buf, &signature);
+  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, &pcr_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 (&signature);
+  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 *)&pcr_policy, sig_hash,
+ TPM_RH_NULL, &pcr_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, &pubkey, TPM_RH_OWNER,
+ &pubkey_handle, &pubname, 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, &pcr_policy_hash, &signature,
+&verification_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, &pcr_policy, &polic

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

2024-04-09 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 (&grub_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 v10 17/20] cryptodisk: wipe out the cached keys from protectors

2024-04-09 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 v10 05/20] libtasn1: fix the potential buffer overrun

2024-04-09 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 v10 18/20] diskfilter: look up cryptodisk devices first

2024-04-09 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 v10 12/20] cryptodisk: Support key protectors

2024-04-09 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],
+   &cargs->key_cache[i].key,
+   
&cargs->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_

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

2024-04-09 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 v10 13/20] util/grub-protect: Add new tool

2024-04-09 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 
x$enabl

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

2024-04-09 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 v10 06/20] libtasn1: compile into asn1 module

2024-04-09 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 v10 20/20] tests: Add tpm2_test

2024-04-09 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 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
+
+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 1:sha256=$(sha256su

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

2024-04-09 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 v10 15/20] tpm2: Implement NV index

2024-04-09 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 rsa -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 36943e578..a64c34fb9 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, &policy_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 v10 11/20] key_protector: Add TPM2 Key Protector

2024-04-09 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 |  

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

2024-04-09 Thread Gary Lin via Grub-devel
On Tue, Apr 09, 2024 at 11:32:44AM -0400, Stefan Berger wrote:
> 
> 
> On 4/9/24 04:30, Gary Lin via Grub-devel wrote:
> > From: Hernan Gatta 
> > 
> > A Trusted Platform Module (TPM) Software Stack (TSS) provides logic to
> > compose, submit, and parse TPM commands and responses.
> > 
> > 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.
> > 
> > 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,
> > TPM2_PolicyAuthorize, TPM2_TestParms
> > 
> > Signed-off-by: Hernan Gatta 
> > Signed-off-by: Gary Lin 
> > ---
> >   grub-core/tpm2/buffer.c|  145 +++
> >   grub-core/tpm2/mu.c| 1150 ++
> >   grub-core/tpm2/tcg2.c  |  143 +++
> >   grub-core/tpm2/tpm2.c  | 1221 
> 
> Apart from that this is 'a lot' (and maybe more complete than it needs to
> be)
There are some commands added in the early development stage but ended up not
used. I kept those commands since they are already there and may be
useful in some day.

> the only thing that cought my attention was that most times you have a
> default: handler in a case statements, occasionally it's missing. Maybe you
> should add some missing default: handlers.
> 
Oops, thanks for spotting that. I'll add those missing default:
handlers.

> Also, Synopsis should extend Coverity to provide Reviewed-by's :-).
> 
That could be a nice feature :)

Thanks,

Gary Lin

___
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-11 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


[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 

[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, &len);
@@ -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, &aux);
  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 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 (&node);
   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, &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}
 };

[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 ge

[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 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 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 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 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 (&grub_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 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, &pubkey);
+  grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (cmd_buf, &policy_ref);
+  grub_tpm2_mu_TPMT_SIGNATURE_Unmarshal (cmd_buf, &signature);
+  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, &pcr_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 (&signature);
+  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 *)&pcr_policy, sig_hash,
+ TPM_RH_NULL, &pcr_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, &pubkey, TPM_RH_OWNER,
+ &pubkey_handle, &pubname, 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, &pcr_policy_hash, &signature,
+&verification_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, &pcr_policy,

[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],
+   &cargs->key_cache[i].key,
+   
&cargs->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_

[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, &policy_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 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 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 1:sha

[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 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 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 
x$enabl

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

2024-04-14 Thread Gary Lin via Grub-devel
On Fri, Apr 12, 2024 at 12:09:36PM -0400, Stefan Berger wrote:
> 
> 
> 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.
> 
Thanks for the suggestion. Using 'swtpm socket' requires additional changes
to grub-emu and grub-protect. To minimize the impact, I'd convert the test
script to use 'swtpm chardev'.

Gary Lin

>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 

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


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

2024-04-14 Thread Gary Lin via Grub-devel
On Fri, Apr 12, 2024 at 11:26:48AM -0400, Stefan Berger wrote:
> 
> 
> 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 current target is the systems with EFI support. Since the EFI
protocol is only used to submit the commands, other platforms with a
proper grub_tcg2_submit_command() still can use the stack. For example,
tcg2-emu.c implements that function for grub-emu, so that the test
script can test key unsealing.

> > 
> > 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.
> 
Ok, we won't use them in the forseeable future, so I'm fine to remove
them. I'll also remove the related structs and marshal/unmarshal
functions.

Thanks,

Gary Lin

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


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

2024-04-15 Thread Gary Lin via Grub-devel
On Fri, Apr 12, 2024 at 02:57:42PM -0400, Stefan Berger wrote:
> 
> 
> 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 

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

2024-04-15 Thread Gary Lin via Grub-devel
On Fri, Apr 12, 2024 at 04:52:02PM -0400, Stefan Berger wrote:
> 
> 
> 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
> >

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

2024-04-15 Thread Gary Lin via Grub-devel
On Fri, Apr 12, 2024 at 12:24:36PM -0400, Stefan Berger wrote:
> 
> 
> 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.
> 
For baremetal users, it still could happen after upgrading the firmware.
We surely need a place to notice users this situation when using PCR
0~7.

Thanks,

Gary Lin

___
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-15 Thread Gary Lin via Grub-devel
On Mon, Apr 15, 2024 at 05:40:53PM +0800, Gary Lin wrote:
> On Fri, Apr 12, 2024 at 04:52:02PM -0400, Stefan Berger wrote:
> > 
> > 
> > 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
> > > 
[...]
> > > +  /* Create SRK */
> > > +  authCommand.sessionHandle = TPM_RS_PW;
> > > +  inPublic.publicArea.type = args->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;
> > > +
> > > +  switch (args->srk_type.type)
> > > +{
> > > +case 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 = 
> > > args->srk_type.detail.rsa_bits;
> > 
> > Same comment here about pairing RSA3072 with AES-256 and SHA-512 maybe
> > (since SHA 384 isn't supported here).
> > 
> It's sad that we don't have native SHA384 support due to the outdated
> libgcrypt :(
> 
I revised the code and found We actually don't need libgcrypt here. When
writing the patches to support authorized policy, TPM2_Hash() was introduced,
and we can replace grub_crypto_hash() with TPM2_Hash() to enable SHA-384
digest calculation.

Gary Lin

> > Rest LGTM.
> > 
> Thanks for reviewing the patch!
> 
> Gary Lin
> 

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


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

2024-04-15 Thread Gary Lin via Grub-devel
On Mon, Apr 15, 2024 at 10:26:32AM -0400, Stefan Berger wrote:
> 
> 
> On 4/15/24 05:45, Gary Lin wrote:
> > On Fri, Apr 12, 2024 at 12:24:36PM -0400, Stefan Berger wrote:
> > > 
> > > 
> > > 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.
> > > 
> > For baremetal users, it still could happen after upgrading the firmware.
> 
> Right but this is much rarer.
> 
> > We surely need a place to notice users this situation when using PCR
> > 0~7.
> 
> PCRs 8-9 probably have to be all zeros at the time of sealing (running the
> user space application for seting this up) so they have the values at the
> time before grub measures kernel and initramfs, right?
> 
For grub-protect, yes. On the other hand, pcr-oracle can predict PCR 9
based on the current grub.cfg and the eventlog. PCR 8 is tricky because
grub measures the command with the expanded variables, and pcr-oracle has
to be improved to parse all grub config files to make the prediction.

Gary Lin

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


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

2024-04-19 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, &len);
@@ -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, &aux);
  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 v12 03/20] libtasn1: disable code not needed in grub

2024-04-19 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 (&node);
   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, &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}
 };

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

2024-04-19 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 007ff628e..93274bbaf 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2599,3 +2599,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 v12 12/20] cryptodisk: Support key protectors

2024-04-19 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 
Reviewed-by: Stefan Berger 
---
 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..b7648ffb7 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],
+   &cargs->key_cache[i].key,
+   
&cargs->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 = cr->recover_key (source, dev, cargs);
+ if (ret != GRUB_ERR_NO

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

2024-04-19 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 

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

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

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

v12:
- Fixing typos and indentation
- Removing the unused TPM commands: TPM2_HashSequenceStart,
  TPM2_SequenceUpdate, and TPM2_SequenceComplete,
- Following the TCG EK templates to set the parameters of SRK
- Removing ECC_BN_P256 and ECC_BN_P638 from the SRK algorithm list since
  those two algorithms are not mentioned in the TCG EK templates
- Updating the help messages of the tpm2 module and grub-protect
- Removing the unnecessary NULL checks
- Adding the manpage for grub-protect
- Replacing grub_crypto_hash() with TPM2_Hash() in grub-protect to
  support SHA384 PCR banks
- Using 'swtpm chardev' to start swtpm instead of 'swtpm_cuse' since
  some distros may not build swtpm with cuse
- Adding a new testcase without specifying the SRK type to test the
  default SRK settings
- Amending tpm2_test to remove the duplicate error checking code
- Silencing the tpm2-tools commands in tpm2_test
- Fixing the exit trap of tpm2_test to removing the working directory
  on success

v11:
- https://lists.gnu.org/archive/html/grub-devel/2024-04/msg00052.html
- GIT repo: https://github.com/lcp/grub2/tree/tpm2-unlock-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: htt

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

2024-04-19 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.

Cc: Stefan Berger 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 grub-core/Makefile.core.def   |   13 +
 grub-core/tpm2/args.c |  179 +
 grub-core/tpm2/module.c   | 1219 +
 grub-core/tpm2/tpm2key.asn|   31 +
 grub-core/tpm2/tpm2key.c  |  443 +++
 grub-core/tpm2/tpm

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

2024-04-19 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 v12 13/20] util/grub-protect: Add new tool

2024-04-19 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.

Cc: Stefan Berger 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 .gitignore|2 +
 Makefile.util.def |   24 +
 configure.ac  |   30 +
 docs/man/grub-protect.h2m |4 +
 util/grub-protect.c   | 1378 +
 5 files changed, 1438 insertions(+)
 create mode 100644 docs/man/grub-protect.h2m
 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..40bfe713d 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -207,6 +207,30 @@ program = {
   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
 };
 
+program = {
+  name = grub-protect;
+  mansection = 1;
+
+  common = grub-core/kern/emu/argp_common.c;
+  common = grub-core/osdep/init.c;
+  common = grub-core/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_prote

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

2024-04-19 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 v12 08/20] libtasn1: Add the documentation

2024-04-19 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 v12 17/20] cryptodisk: wipe out the cached keys from protectors

2024-04-19 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.

Cc: Fabian Vogt 
Signed-off-by: Gary Lin 
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 8f00f22a8..3b19f2d2d 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 v12 14/20] tpm2: Support authorized policy

2024-04-19 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 
Reviewed-by: Stefan Berger 
---
 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 485845f36..4fc2b5356 100644
--- a/grub-core/tpm2/module.c
+++ b/grub-core/tpm2/module.c
@@ -652,6 +652,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, &pubkey);
+  grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (cmd_buf, &policy_ref);
+  grub_tpm2_mu_TPMT_SIGNATURE_Unmarshal (cmd_buf, &signature);
+  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, &pcr_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 (&signature);
+  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 *)&pcr_policy, sig_hash,
+ TPM_RH_NULL, &pcr_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, &pubkey, TPM_RH_OWNER,
+ &pubkey_handle, &pubname, 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, &pcr_policy_hash, &signature,
+&verification_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 

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

2024-04-19 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.

Cc: Stefan Berger 
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 b7648ffb7..8f00f22a8 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 v12 20/20] tests: Add tpm2_test

2024-04-19 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 
Cc: Stefan Berger 
Signed-off-by: Gary Lin 
---
 Makefile.util.def|   6 +
 tests/tpm2_test.in   | 311 +++
 tests/util/grub-shell.in |   6 +-
 3 files changed, 322 insertions(+), 1 deletion(-)
 create mode 100644 tests/tpm2_test.in

diff --git a/Makefile.util.def b/Makefile.util.def
index 40bfe713d..8d4c53a03 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -1281,6 +1281,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..697319c75
--- /dev/null
+++ b/tests/tpm2_test.in
@@ -0,0 +1,311 @@
+#! @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 ! grep -q tpm_vtpm_proxy /proc/modules && ! modprobe tpm_vtpm_proxy; then
+   echo "no tpm_vtpm_proxy support; 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"
+
+tpm2statedir=${tpm2testdir}/tpm
+tpm2ctrl=${tpm2statedir}/ctrl
+tpm2log=${tpm2statedir}/logfile
+
+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() {
+RET=$?
+if [ -e "$tpm2ctrl" ]; then
+swtpm_ioctl -s --unix ${tpm2ctrl}
+fi
+if [ "${RET}" -eq 0 ]; then
+rm -rf "$tpm2testdir" || :
+fi
+}
+trap cleanup EXIT INT TERM KILL QUIT
+
+mkdir -p ${tpm2statedir}
+
+# Create the swtpm chardev instannce
+swtpm chardev --vtpm-proxy --tpmstate dir=${tpm2statedir} \
+   --tpm2 --ctrl type=unixio,path=${tpm2ctrl} \
+   --flags startup-clear --daemon > ${tpm2log}
+ret=$?
+if [ "$ret" -ne 0 ]; then
+exit $ret
+fi
+
+tpm2dev=$(grep "New TPM device" ${tpm2log} | cut -d' ' -f 4)
+if [ -z "${tpm2dev}" ]; then
+exit QUIT
+fi
+
+# Wait for tpm2 chardev
+wait=3
+while [ "${wait}" -gt 0 ]; do
+if [ -c "${tpm2dev}" ]; then
+break;
+fi
+sleep 1
+((wait--))
+don

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

2024-04-19 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 
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 85aaadf68..b2456a07e 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2571,7 +2571,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

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

2024-04-19 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 
Reviewed-by: Stefan Berger 
---
 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 8cecb9183..fe58ec740 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1282,6 +1282,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..b84afe1c7
--- /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 (&grub_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  --  

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

2024-04-19 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 
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 4fc2b5356..342822fbf 100644
--- a/grub-core/tpm2/module.c
+++ b/grub-core/tpm2/module.c
@@ -1025,12 +1025,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, &policy_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


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

2024-04-22 Thread Gary Lin via Grub-devel
On Fri, Apr 19, 2024 at 08:23:44AM -0400, James Bottomley wrote:
> On Fri, 2024-04-19 at 16:30 +0800, Gary Lin wrote:
> >    TPMKey ::= SEQUENCE {
> >  type    OBJECT 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
> 
> Now that you've got rsaParent [5] EXPLICIT BOOLEAN OPTIONAL, could you
> use it?
> 
Sure. I will add rsaParent in v13.

> Since none of this ever went upstream do the arguments really need the
> old keyfile format?  No-one should have created one, so using the
> tpm2key format going forwards and eliminating the additional policy
> arguments should simplify the user facing piece.  Even if SUSE has
> released something with the old format, since the new key file has a
> more expressive policy it should be easy to convert to it to the
> tpm2key format.
> 
I'd like to keep the old key format so that we can migrate the
installation script gradually.

> The other thing is this:
> 
> > +  .longarg  = "asymmetric",
> > +  .shortarg = 'a',
> > +  .flags= 0,
> > +  .arg  = NULL,
> > +  .type = ARG_TYPE_STRING,
> > +  .doc  =
> > +   N_("In SRK mode, the type of SRK: RSA (RSA2048), RSA3072, "
> > +  "RSA4096, ECC (ECC_NIST_P256), ECC_NIST_P384, "
> > +  "ECC_NIST_P521, and ECC_SM2_P256. (default: ECC)"),
> 
> The TCG has only defined two types of SRK templates for
> interoperability: P-256 and RSA2048 (both with 128 bit AES symmetric
> keys):
> 
> https://trustedcomputinggroup.org/resource/http-trustedcomputinggroup-org-wp-content-uploads-tcg-ek-credential-profile-v-2-5-r2_published-pdf/
> 
> The others are all non-standard and shouldn't be included (they'll just
> cause interoperability issues for people who insist on trying out every
> option and then complain about the problems this causes).
> 
Ok, I will remove the non-standard SRK in v13.

Gary Lin

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


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

2024-04-22 Thread Gary Lin via Grub-devel
On Fri, Apr 19, 2024 at 09:12:00AM -0400, Stefan Berger wrote:
> 
> 
> On 4/19/24 04:31, 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.
> > 
> > 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 
> > Cc: Stefan Berger 
> > Signed-off-by: Gary Lin 
> > ---
> >   Makefile.util.def|   6 +
> >   tests/tpm2_test.in   | 311 +++
> >   tests/util/grub-shell.in |   6 +-
> >   3 files changed, 322 insertions(+), 1 deletion(-)
> >   create mode 100644 tests/tpm2_test.in
> > 
> > diff --git a/Makefile.util.def b/Makefile.util.def
> > index 40bfe713d..8d4c53a03 100644
> > --- a/Makefile.util.def
> > +++ b/Makefile.util.def
> > @@ -1281,6 +1281,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..697319c75
> > --- /dev/null
> > +++ b/tests/tpm2_test.in
> > @@ -0,0 +1,311 @@
> > +#! @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 <http://www.gnu.org/licenses/>.
> > +
> > +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 ! grep -q tpm_vtpm_proxy /proc/modules && ! modprobe tpm_vtpm_proxy; 
> > then
> > +   echo "no tpm_vtpm_proxy support; 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"`" || 
>

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

2024-04-25 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 

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

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

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

v13:
- Fixing typos and a few multi-line comments
- Improving the conditional checks for the arguments of
  tpm2_key_protector_init 
- Updating to the latest TPM 2.0 Key File format
  - Adding the new optional fields: description and rsaParent
  - Handling "rsaParent == TRUE" when unsealing the tpm2key
  - Setting "rsaParent" to "TRUE" when sealing the key with RSA SRK
- Removing non-standard SRKs: RSA3072, RSA4096, ECC_NIST_P384,
  ECC_NIST_P521, and ECC_SM2_P256
- Adding more error messages to grub-protect
- Improving the error checking for the swtpm chardev instance
- Exiting the tpm2_test script if grub-protect failed to seal the key   

v12:
- https://lists.gnu.org/archive/html/grub-devel/2024-04/msg00108.html
- GIT repo: https://github.com/lcp/grub2/tree/tpm2-unlock-v12
- Fixing typos and indentation
- Removing the unused TPM commands: TPM2_HashSequenceStart,
  TPM2_SequenceUpdate, and TPM2_SequenceComplete,
- Following the TCG EK templates to set the parameters of SRK
- Removing ECC_BN_P256 and ECC_BN_P638 from the SRK algorithm list since
  those two algorithms are not mentioned in the TCG EK templates
- Updating the help messages of the tpm2 module and grub-protect
- Removing the unnecessary NULL checks
- Adding the manpage for grub-protect
- Replacing grub_crypto_hash() with TPM2_Hash() in grub-protect to
  support SHA384 PCR banks
- Using 'swtpm chardev' to start swtpm instead of 'swtpm_cuse' since
  some distros 

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

2024-04-25 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 007ff628e..93274bbaf 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2599,3 +2599,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 v13 04/20] libtasn1: changes for grub compatibility

2024-04-25 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, &len);
@@ -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, &aux);
  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 v13 16/20] cryptodisk: Fallback to passphrase

2024-04-25 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 
Reviewed-by: Stefan Berger 
---
 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 b7648ffb7..8f00f22a8 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 v13 12/20] cryptodisk: Support key protectors

2024-04-25 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 
Reviewed-by: Stefan Berger 
---
 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..b7648ffb7 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],
+   &cargs->key_cache[i].key,
+   
&cargs->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 = cr->recover_key (source, dev, cargs);
+ if (ret != GRUB_ERR_NO

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

2024-04-25 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 v13 15/20] tpm2: Implement NV index

2024-04-25 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 
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 e83b02865..b754b38df 100644
--- a/grub-core/tpm2/module.c
+++ b/grub-core/tpm2/module.c
@@ -1035,12 +1035,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, &policy_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 v13 05/20] libtasn1: fix the potential buffer overrun

2024-04-25 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 v13 09/20] key_protector: Add key protectors framework

2024-04-25 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 
Reviewed-by: Stefan Berger 
---
 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 8cecb9183..fe58ec740 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1282,6 +1282,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..b84afe1c7
--- /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 (&grub_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  --  

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

2024-04-25 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 
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 85aaadf68..b2456a07e 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2571,7 +2571,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

[PATCH v13 20/20] tests: Add tpm2_test

2024-04-25 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 
Cc: Stefan Berger 
Signed-off-by: Gary Lin 
---
 Makefile.util.def|   6 +
 tests/tpm2_test.in   | 308 +++
 tests/util/grub-shell.in |   6 +-
 3 files changed, 319 insertions(+), 1 deletion(-)
 create mode 100644 tests/tpm2_test.in

diff --git a/Makefile.util.def b/Makefile.util.def
index 40bfe713d..8d4c53a03 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -1281,6 +1281,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..19cb21849
--- /dev/null
+++ b/tests/tpm2_test.in
@@ -0,0 +1,308 @@
+#! @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 ! grep -q tpm_vtpm_proxy /proc/modules && ! modprobe tpm_vtpm_proxy; then
+   echo "no tpm_vtpm_proxy support; 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"
+
+tpm2statedir=${tpm2testdir}/tpm
+tpm2ctrl=${tpm2statedir}/ctrl
+tpm2log=${tpm2statedir}/logfile
+
+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() {
+RET=$?
+if [ -e "$tpm2ctrl" ]; then
+swtpm_ioctl -s --unix ${tpm2ctrl}
+fi
+if [ "${RET}" -eq 0 ]; then
+rm -rf "$tpm2testdir" || :
+fi
+}
+trap cleanup EXIT INT TERM KILL QUIT
+
+mkdir -p ${tpm2statedir}
+
+# Create the swtpm chardev instance
+swtpm chardev --vtpm-proxy --tpmstate dir=${tpm2statedir} \
+   --tpm2 --ctrl type=unixio,path=${tpm2ctrl} \
+   --flags startup-clear --daemon > ${tpm2log}
+ret=$?
+if [ "$ret" -ne 0 ]; then
+exit $ret
+fi
+
+# Wait for tpm2 chardev
+wait=3
+while [ "${wait}" -gt 0 ]; do
+tpm2dev=$(grep "New TPM device" ${tpm2log} | cut -d' ' -f 4)
+if [ -n "${tpm2dev}" ] && [ -c "${tpm2dev}" ]; then
+break
+fi
+sleep 1
+((wait--))
+done
+if [ "$wait" -le 0 ]; t

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

2024-04-25 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
 description [4] EXPLICIT UTF8String OPTIONAL,
 rsaParent   [5] EXPLICIT BOOLEAN 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.

Cc: Stefan Berger 
Cc: James Bottomley 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 grub-core/Makefile.core.def   |   13 +
 grub-core/tpm2/args.c |  140 
 grub-core/tpm2/module.c   | 1226 

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

2024-04-25 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 the malicious root and further dump memory to
steal the unsealed key.

Do defend against such an 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 an 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 minimize 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.

Cc: Fabian Vogt 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 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 v13 03/20] libtasn1: disable code not needed in grub

2024-04-25 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 (&node);
   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, &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}
 };

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

2024-04-25 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 
Reviewed-by: Stefan Berger 
---
 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 3db25ceca..e83b02865 100644
--- a/grub-core/tpm2/module.c
+++ b/grub-core/tpm2/module.c
@@ -650,6 +650,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, &pubkey);
+  grub_tpm2_mu_TPM2B_DIGEST_Unmarshal (cmd_buf, &policy_ref);
+  grub_tpm2_mu_TPMT_SIGNATURE_Unmarshal (cmd_buf, &signature);
+  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, &pcr_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 (&signature);
+  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 *)&pcr_policy, sig_hash,
+ TPM_RH_NULL, &pcr_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, &pubkey, TPM_RH_OWNER,
+ &pubkey_handle, &pubname, 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, &pcr_policy_hash, &signature,
+&verification_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 

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

2024-04-25 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.

Cc: Fabian Vogt 
Signed-off-by: Gary Lin 
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 8f00f22a8..3b19f2d2d 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 v13 13/20] util/grub-protect: Add new tool

2024-04-25 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.

Cc: Stefan Berger 
Signed-off-by: Hernan Gatta 
Signed-off-by: Gary Lin 
---
 .gitignore|2 +
 Makefile.util.def |   24 +
 configure.ac  |   30 +
 docs/man/grub-protect.h2m |4 +
 util/grub-protect.c   | 1420 +
 5 files changed, 1480 insertions(+)
 create mode 100644 docs/man/grub-protect.h2m
 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..40bfe713d 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -207,6 +207,30 @@ program = {
   ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
 };
 
+program = {
+  name = grub-protect;
+  mansection = 1;
+
+  common = grub-core/kern/emu/argp_common.c;
+  common = grub-core/osdep/init.c;
+  common = grub-core/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_prote

Re: [PATCH v13 08/20] libtasn1: Add the documentation

2024-04-28 Thread Gary Lin via Grub-devel
On Sat, Apr 27, 2024 at 03:27:57AM -0500, Glenn Washburn wrote:
> On Thu, 25 Apr 2024 16:01:54 +0800
> Gary Lin  wrote:
> 
> > 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
> 
> s/is/may be/
> 
> > +@file{grub-core/lib/libtasn1-patches/} to adjust the code to be compatible 
> > with
> > +grub.
> 
> Add after this sentence, I think its worth mentioning the following:
> 
>   These patches were needed to use the current version of libtasn1. The
>   existing patches may not apply cleanly, apply at all, or even be
>   needed for a newer version of the library, and other patches maybe
>   needed due to changes in the newer version. If existing patches need
>   to be refreshed to apply cleanly, please include updated patches as
>   part of the a patch set sent to the list. If new patches are needed
>   or existing patches are not needed, also please send additions or
>   removals as part of any patch set upgrading libtasn1.
> 
Thanks for the suggestion. This is a nice reminder for anyone who wants
to update libtasn1 later. Will add them to v14.

Gary Lin

> Glenn
> 
> > +
> >  @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 ++--

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

2024-04-29 Thread Gary Lin via Grub-devel
On Fri, Apr 26, 2024 at 05:18:04PM -0500, Glenn Washburn wrote:
> On Thu, 25 Apr 2024 16:02:06 +0800
> Gary Lin  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.
> 
> I'm glad we're getting a test with this feature. Its also unfortunate
> that the test only works on the emu platform, which I suspect is tested
> less.
> 
Doing a full test with QEMU requires a more powerful testsuite such as
openQA to manipulate the OS to seal the disk secret to the TPM of the
VM. With grub-emu, at least we can test the TPM2 stack and verify the
result of tpm2_key_protector_init.

> > 
> > 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 
> > Cc: Stefan Berger 
> > Signed-off-by: Gary Lin 
> > ---
> >  Makefile.util.def|   6 +
> >  tests/tpm2_test.in   | 308 +++
> >  tests/util/grub-shell.in |   6 +-
> >  3 files changed, 319 insertions(+), 1 deletion(-)
> >  create mode 100644 tests/tpm2_test.in
> > 
> > diff --git a/Makefile.util.def b/Makefile.util.def
> > index 40bfe713d..8d4c53a03 100644
> > --- a/Makefile.util.def
> > +++ b/Makefile.util.def
> > @@ -1281,6 +1281,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..19cb21849
> > --- /dev/null
> > +++ b/tests/tpm2_test.in
> > @@ -0,0 +1,308 @@
> > +#! @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
> 
> Should be using "command" here (and in the other tests) as the external
> command "which" may not be available. So perhaps this line instead:
> 
>   if ! command -v cryptsetup >/dev/null 2>&1; then
> 
> Same for subsequent usages of which.
> 
Got it. Will fix it in v14.

> > +   echo "cryptsetup not installed; cannot test tpm2."
> > +   exit 99
> > +fi
> > +
> > +if ! grep -q tpm_vtpm_proxy /proc/modules && ! modprobe tpm_vtpm_proxy; 
> > then
> > +   echo "no tpm_vtpm_proxy support; 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
> 
> Why exit 20 here instead of 99 like other tests? It looks like you want
> to differentiate different error conditions, but this will make the
> test show as error, not hard error, which is what

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

2024-04-30 Thread Gary Lin via Grub-devel
On Mon, Apr 29, 2024 at 04:10:05PM +0800, Gary Lin wrote:
> On Fri, Apr 26, 2024 at 05:18:04PM -0500, Glenn Washburn wrote:
> > On Thu, 25 Apr 2024 16:02:06 +0800
> > Gary Lin  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.
> > 
> > I'm glad we're getting a test with this feature. Its also unfortunate
> > that the test only works on the emu platform, which I suspect is tested
> > less.
> > 
> Doing a full test with QEMU requires a more powerful testsuite such as
> openQA to manipulate the OS to seal the disk secret to the TPM of the
> VM. With grub-emu, at least we can test the TPM2 stack and verify the
> result of tpm2_key_protector_init.
> 
> > > 
> > > 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 
> > > Cc: Stefan Berger 
> > > Signed-off-by: Gary Lin 
> > > ---
[...]
> > > diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in
> > > index 496e1bab3..f8642543d 100644
> > > --- a/tests/util/grub-shell.in
> > > +++ b/tests/util/grub-shell.in
> > > @@ -75,6 +75,7 @@ work_directory=${WORKDIR:-`mktemp -d 
> > > "${TMPDIR:-/tmp}/grub-shell.XX"`} |
> > >  
> > >  . "${builddir}/grub-core/modinfo.sh"
> > >  qemuopts=
> > > +grubemuopts=
> > >  serial_port=com0
> > >  serial_null=
> > >  halt_cmd=halt
> > > @@ -281,6 +282,9 @@ for option in "$@"; do
> > >  --qemu-opts=*)
> > >   qs=`echo "$option" | sed -e 's/--qemu-opts=//'`
> > >   qemuopts="$qemuopts $qs" ;;
> > > +--grub-emu-opts=*)
> > > + qs=`echo "$option" | sed -e 's/--grub-emu-opts=//'`
> > > + grubemuopts="$grubemuopts $qs" ;;
> > 
> > I'm on the fence on this. '--qemu-opts' could just be reused, although
> > it might be a little confusing, as it would be misnamed. It would be
> > nice to think of a way to combine --qemu-opts and --grub-emu-opts into
> > one appropriately named argument (perhaps just --emu-opts?). They are
> > mutually exclusive in the sense that they will both never be in effect
> > for a given target. Also, prefixing with 'grub' goes against the
> > existing naming scheme, so regardless should not be done.
> > 
> Then I'd just append ${qemuopts} to the grub-emu command since renaming
> the '--qemu-opts' will affect other test scripts. This has to be done in
> a separate patch set.
> 
It's actually not feasible to append ${qemuopts} to grub-emu since the
following line will be executed when $debug is not specified:

qemuopts="${qemuopts} -nographic -monitor file:/dev/null"

Thus, a separate variable for grub-emu is necessary. I'll change the
option to '--emu-opts' and the variable to '$emuopts'.

Gary Lin

> Gary Lin
> 
> > Glenn
> > 
> > >  --disk=*)
> > >   dsk=`echo "$option" | sed -e 's/--disk=//'`
> > >   if [ ${grub_modinfo_platform} = emu ]; then
> > > @@ -576,7 +580,7 @@ elif [ x$boot = xemu ]; then
> > >  cat >"$work_directory/run.sh" < > >  #! @BUILD_SHEBANG@
> > >  SDIR=\$(realpath -e \${0%/*})
> > > -exec "$(realpath -e "${builddir}")/grub-core/grub-emu" -m 
> > > "\$SDIR/${device_map##*/}" --memdisk "\$SDIR/${roottar##*/}" -r memdisk 
> > > -d "/boot/grub"
> > > +exec "$(realpath -e "${builddir}")/grub-core/grub-emu" -m 
> > > "\$SDIR/${device_map##*/}" --memdisk "\$SDIR/${roottar##*/}" -r memdisk 
> > > -d "/boot/grub" ${grubemuopts}
> > >  EOF
> > >  else
> > >  cat >"$work_directory/run.sh" 

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

2024-05-02 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 
Reviewed-by: Stefan Berger 
---
 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..b7648ffb7 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],
+   &cargs->key_cache[i].key,
+   
&cargs->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 = cr->recover_key (source, dev, cargs);
+ if (ret != GRUB_ERR_NO

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

2024-05-02 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 (&node);
   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, &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}
 };

[PATCH v14 20/20] tests: Add tpm2_test

2024-05-02 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 
Cc: Stefan Berger 
Cc: Glenn Washburn 
Signed-off-by: Gary Lin 
---
 Makefile.util.def|   6 +
 tests/tpm2_test.in   | 306 +++
 tests/util/grub-shell.in |   6 +-
 3 files changed, 317 insertions(+), 1 deletion(-)
 create mode 100644 tests/tpm2_test.in

diff --git a/Makefile.util.def b/Makefile.util.def
index 40bfe713d..8d4c53a03 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -1281,6 +1281,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..75c042274
--- /dev/null
+++ b/tests/tpm2_test.in
@@ -0,0 +1,306 @@
+#! @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 ! command -v cryptsetup >/dev/null 2>&1; then
+   echo "cryptsetup not installed; cannot test tpm2."
+   exit 99
+fi
+
+if ! grep -q tpm_vtpm_proxy /proc/modules && ! modprobe tpm_vtpm_proxy; then
+   echo "no tpm_vtpm_proxy support; cannot test tpm2."
+   exit 99
+fi
+
+if ! command -v swtpm >/dev/null 2>&1; then
+   echo "swtpm not installed; cannot test tpm2."
+   exit 99
+fi
+
+if ! command -v 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 99
+
+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"
+
+tpm2statedir=${tpm2testdir}/tpm
+tpm2ctrl=${tpm2statedir}/ctrl
+tpm2log=${tpm2statedir}/logfile
+
+sealedkey=${tpm2testdir}/sealed.tpm
+
+timeout=20
+
+testoutput=${tpm2testdir}/testoutput
+
+vtext="TEST VERIFIED"
+
+ret=0
+
+# 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
+
+# Write vtext into the first block of the LUKS2 image
+luksdev=/dev/mapper/`basename ${tpm2testdir}`
+cryptsetup open --key-file ${lukskeyfile} ${luksfile} `basename ${luksdev}` || 
exit 23
+echo "${vtext}" | dd of=${luksdev} conv=notrunc status=none
+cryptsetup close ${luksdev}
+
+# Shutdown the swtpm instance on exit
+cleanup() {
+RET=$?
+if [ -e "${tpm2ctrl}" ]; then
+   swtpm_ioctl -s --unix ${tpm2ctrl}
+fi
+if [ "${RET}" -eq 0 ]; then
+   rm -rf "$tpm2testdir" || :
+fi
+}
+trap cleanup EXIT INT TERM KILL QUIT
+
+mkdir -p ${tpm2statedir}
+
+# Create the swtpm chardev instance
+swtpm chardev --vtpm-proxy --tpmstate dir=${tpm2statedir} \
+   --tpm2 --ctrl type=unixio,path=${tpm2ctrl} \
+   --flags startup-clear --daemon

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

2024-05-02 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 the malicious root and further dump memory to
steal the unsealed key.

Do defend against such an 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 an 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 minimize 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.

Cc: Fabian Vogt 
Signed-off-by: Gary Lin 
Reviewed-by: Stefan Berger 
---
 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 v14 00/20] Automatic Disk Unlock with TPM2

2024-05-02 Thread Gary Lin via Grub-devel
GIT repo for v14: https://github.com/lcp/grub2/tree/tpm2-unlock-v14

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

v14:
- Addressing the libtasn1 patches more in the document
- Various improvements in tpm2_test
  - Verifying the test inside the LUKS device
  - Improving the return status checks and the waiting loop for swtpm
  - Fixing the portability issues
  - Making all variables braced
- Renaming grub-emu-opts to --emu-opts (grub-shell)

v13:
- https://lists.gnu.org/archive/html/grub-devel/2024-04/msg00155.html
- GIT repo: https://github.com/lcp/grub2/tree/tpm2-unlock-v13
- Fixing typos and a few multi-line comments
- Improving the conditional checks for the arguments of
  tpm2_key_protector_init 
- Updating to the latest TPM 2.0 Key File format
  - Adding the new optional fields: description and rsaParent
  - Handling "rsaParent == TRUE" when unsealing the tpm2key
  - Setting "rsaParent" to "TRUE" when sealing the key with RSA SRK
- Removing non-standard SRKs: RSA3072, RSA4096, ECC_NIST_P384,
  ECC_NIST_P521, and ECC_SM2_P256
- Adding more error messages to grub-protect
- Improving the error checking for the swtpm chardev instance
- Exiting the tpm2_test script if grub-protect failed to seal the key   

v12:
- https://lists.gnu.org/archive/html/grub-devel/2024-04/msg00108.html
- GIT repo: https://github.com/lcp/grub2/tree/tpm2-unlock-v12
- Fixing typos and indentation
- Removing the unused TPM commands: TPM2_HashSequenceStart,
  TPM2_SequenceUpdate, and TPM2_SequenceComplete,
- Following the TCG EK templates to set the parameters of

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

2024-05-02 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 
Reviewed-by: Stefan Berger 
---
 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 8cecb9183..fe58ec740 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1282,6 +1282,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..b84afe1c7
--- /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 (&grub_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  --  

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

2024-05-02 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 007ff628e..93274bbaf 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2599,3 +2599,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


  1   2   3   4   5   6   7   >