Re: [PATCH v10 0/2] Introduce EROFS support

2024-05-09 Thread Gao Xiang

Hi Daniel, Glenn, Vladimir,

On 2024/5/2 15:01, Yifan Zhao wrote:

EROFS [1] is a lightweight read-only filesystem designed for performance
which has already been shipped in most Linux distributions as well as widely
used in several scenarios, such as Android system partitions, container
images, and rootfs for embedded devices.

This patch brings EROFS uncompressed support together with related tests.
Now, it's possible to boot directly through GRUB with an EROFS rootfs.

EROFS compressed files will be supported later since it has more work to
polish.

[1] https://erofs.docs.kernel.org

changelog since v9:
- fix overflow and style issues according to Serbinenko's review comments
- add a upper bound for symlink length according to Daniel's advice

Tested-by Link (Commit 1): 
https://lists.gnu.org/archive/html/grub-devel/2024-05/msg1.html
Reviewed-by Link (Commit 2): 
https://lists.gnu.org/archive/html/grub-devel/2024-04/msg00101.html



Does this version look good you? Could you help take some
time look at the latest version if possible?

Thanks,
Gao Xiang


Yifan Zhao (2):
   fs/erofs: Add support for EROFS
   fs/erofs: Add tests for EROFS in grub-fs-tester

  .gitignore   |1 +
  INSTALL  |8 +-
  Makefile.util.def|7 +
  docs/grub.texi   |3 +-
  grub-core/Makefile.core.def  |5 +
  grub-core/fs/erofs.c | 1007 ++
  tests/erofs_test.in  |   20 +
  tests/util/grub-fs-tester.in |   32 +-
  8 files changed, 1071 insertions(+), 12 deletions(-)
  create mode 100644 grub-core/fs/erofs.c
  create mode 100644 tests/erofs_test.in

Interdiff against v9:
diff --git a/grub-core/fs/erofs.c b/grub-core/fs/erofs.c
index 9c2678796..b82212b16 100644
--- a/grub-core/fs/erofs.c
+++ b/grub-core/fs/erofs.c
@@ -101,6 +101,7 @@ struct grub_erofs_inode_chunk_info
  
  #define EROFS_NULL_ADDR			1

  #define EROFS_NAME_LEN255
+#define EROFS_PATH_LEN 4096
  #define EROFS_MIN_LOG2_BLOCK_SIZE 9
  #define EROFS_MAX_LOG2_BLOCK_SIZE 16
  
@@ -160,6 +161,12 @@ struct grub_erofs_inode_extended

grub_uint8_t i_reserved2[16];
  } GRUB_PACKED;
  
+union grub_erofs_inode

+{
+  struct grub_erofs_inode_compact c;
+  struct grub_erofs_inode_extended e;
+} GRUB_PACKED;
+
  #define EROFS_FT_UNKNOWN  0
  #define EROFS_FT_REG_FILE 1
  #define EROFS_FT_DIR  2
@@ -197,7 +204,7 @@ struct grub_erofs_xattr_ibody_header
  struct grub_fshelp_node
  {
struct grub_erofs_data *data;
-  struct grub_erofs_inode_extended inode;
+  union grub_erofs_inode inode;
  
grub_uint64_t ino;

grub_uint8_t inode_type;
@@ -236,26 +243,27 @@ erofs_iloc (grub_fshelp_node_t node)
  {
struct grub_erofs_super *sb = &node->data->sb;
  
-  return (grub_le_to_cpu32 (sb->meta_blkaddr) << sb->log2_blksz) + (node->ino << EROFS_ISLOTBITS);

+  return ((grub_uint64_t) grub_le_to_cpu32 (sb->meta_blkaddr) << 
sb->log2_blksz) +
+(node->ino << EROFS_ISLOTBITS);
  }
  
  static grub_err_t

  erofs_read_inode (struct grub_erofs_data *data, grub_fshelp_node_t node)
  {
-  struct grub_erofs_inode_compact *dic;
+  union grub_erofs_inode *di;
grub_err_t err;
grub_uint16_t i_format;
grub_uint64_t addr = erofs_iloc (node);
  
-  dic = (struct grub_erofs_inode_compact *) &node->inode;

+  di = (union grub_erofs_inode *) &node->inode;
  
err = grub_disk_read (data->disk, addr >> GRUB_DISK_SECTOR_BITS,

addr & (GRUB_DISK_SECTOR_SIZE - 1),
-   sizeof (struct grub_erofs_inode_compact), dic);
+   sizeof (struct grub_erofs_inode_compact), &di->c);
if (err != GRUB_ERR_NONE)
  return err;
  
-  i_format = grub_le_to_cpu16 (dic->i_format);

+  i_format = grub_le_to_cpu16 (di->c.i_format);
node->inode_type = (i_format >> EROFS_I_VERSION_BIT) & 
EROFS_I_VERSION_MASKS;
node->inode_datalayout = (i_format >> EROFS_I_DATALAYOUT_BIT) & 
EROFS_I_DATALAYOUT_MASKS;
  
@@ -267,7 +275,7 @@ erofs_read_inode (struct grub_erofs_data *data, grub_fshelp_node_t node)

  data->disk, addr >> GRUB_DISK_SECTOR_BITS,
  addr & (GRUB_DISK_SECTOR_SIZE - 1),
  sizeof (struct grub_erofs_inode_extended) - sizeof (struct 
grub_erofs_inode_compact),
- (grub_uint8_t *) dic + sizeof (struct grub_erofs_inode_compact));
+ (grub_uint8_t *) di + sizeof (struct grub_erofs_inode_compact));
if (err != GRUB_ERR_NONE)
return err;
break;
@@ -294,17 +302,17 @@ erofs_inode_size (grub_fshelp_node_t node)
  static grub_uint64_t
  erofs_inode_file_size (grub_fshelp_node_t node)
  {
-  struct grub_erofs_inode_compact *dic = (struct grub_erofs_inode_compact *) 
&node->inode;
+  union grub_erofs_inode *di = (union grub_erofs_inode *) &node->inode;
  
return node->inode_type == EROFS_INODE_LAYOUT_COMPACT

-? grub_le_to_cpu32 (dic->i_size)
-: grub_le_to_cpu64

Re: [PATCH v10 0/2] Introduce EROFS support

2024-05-09 Thread Daniel Kiper
Hey,

On Thu, May 02, 2024 at 03:01:37PM +0800, Yifan Zhao wrote:
> EROFS [1] is a lightweight read-only filesystem designed for performance
> which has already been shipped in most Linux distributions as well as widely
> used in several scenarios, such as Android system partitions, container
> images, and rootfs for embedded devices.
>
> This patch brings EROFS uncompressed support together with related tests.
> Now, it's possible to boot directly through GRUB with an EROFS rootfs.
>
> EROFS compressed files will be supported later since it has more work to
> polish.
>
> [1] https://erofs.docs.kernel.org

Sadly Coverity complains about the EROFS code. Please take a look below...

Daniel


*** CID 460612:(BAD_SHIFT)
/grub-core/fs/erofs.c: 418 in erofs_map_blocks_chunkmode()
412   pos &= ~(unit - 1);
413
414   /* no overflow for multiplication as chunkbits >= 9 and sizeof(unit) 
<= 8 */
415   if (grub_add (pos, chunknr * unit, &pos))
416 return grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow is detected");
417
>>> CID 460612:(BAD_SHIFT)
>>> In expression "chunknr << chunkbits", left shifting by more than 63 
>>> bits has undefined behavior.  The shift amount, "chunkbits", is as much as 
>>> 64.
418   map->m_la = chunknr << chunkbits;
419
420   if (grub_sub (erofs_inode_file_size (node), map->m_la, &map->m_plen))
421 return grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow is detected");
422   map->m_plen = grub_min (((grub_uint64_t) 1) << chunkbits,
423   ALIGN_UP (map->m_plen, erofs_blocksz 
(node->data)));
/grub-core/fs/erofs.c: 403 in erofs_map_blocks_chunkmode()
397
398   chunkbits = node->data->sb.log2_blksz + (chunk_format & 
EROFS_CHUNK_FORMAT_BLKBITS_MASK);
399   if (chunkbits > 64)
400 return grub_error (GRUB_ERR_BAD_FS, "invalid chunkbits %u @ inode 
%" PRIuGRUB_UINT64_T,
401chunkbits, node->ino);
402
>>> CID 460612:(BAD_SHIFT)
>>> In expression "map->m_la >> chunkbits", right shifting by more than 63 
>>> bits has undefined behavior.  The shift amount, "chunkbits", is as much as 
>>> 64.
403   chunknr = map->m_la >> chunkbits;
404
405   if (grub_add (erofs_iloc (node), erofs_inode_size (node), &pos) ||
406   grub_add (pos, erofs_inode_xattr_ibody_size (node), &pos))
407 return grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow is detected");
408
/grub-core/fs/erofs.c: 422 in erofs_map_blocks_chunkmode()
416 return grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow is detected");
417
418   map->m_la = chunknr << chunkbits;
419
420   if (grub_sub (erofs_inode_file_size (node), map->m_la, &map->m_plen))
421 return grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow is detected");
>>> CID 460612:(BAD_SHIFT)
>>> In expression "1UL << chunkbits", left shifting by more than 63 bits 
>>> has undefined behavior.  The shift amount, "chunkbits", is as much as 64.
422   map->m_plen = grub_min (((grub_uint64_t) 1) << chunkbits,
423   ALIGN_UP (map->m_plen, erofs_blocksz 
(node->data)));
424
425   if (chunk_format & EROFS_CHUNK_FORMAT_INDEXES)
426 {
427   struct grub_erofs_inode_chunk_index idx;


*** CID 460611:  Error handling issues  (CHECKED_RETURN)
/grub-core/fs/erofs.c: 793 in erofs_dir_iter()
787 {
788   struct grub_erofs_dir_ctx *ctx = data;
789   struct grub_dirhook_info info = {0};
790
791   if (!node->inode_loaded)
792 {
>>> CID 460611:  Error handling issues  (CHECKED_RETURN)
>>> Calling "erofs_read_inode" without checking return value (as is done 
>>> elsewhere 6 out of 7 times).
793   erofs_read_inode (ctx->data, node);
794   grub_errno = GRUB_ERR_NONE;
795 }
796
797   if (node->inode_loaded)
798 {


*** CID 460610:  Resource leaks  (RESOURCE_LEAK)
/grub-core/fs/erofs.c: 957 in grub_erofs_label()
951   *label = NULL;
952   return grub_errno;
953 }
954
955   *label = grub_strndup ((char *) data->sb.volume_name, sizeof 
(data->sb.volume_name));
956   if (!*label)
>>> CID 460610:  Resource leaks  (RESOURCE_LEAK)
>>> Variable "data" going out of scope leaks the storage it points to.
957 return grub_errno;
958
959   grub_free (data);
960
961   return GRUB_ERR_NONE;
962 }


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


Re: [PATCH v10 0/2] Introduce EROFS support

2024-05-09 Thread Gao Xiang

Hi Daniel,

On 2024/5/10 01:57, Daniel Kiper wrote:

Hey,

On Thu, May 02, 2024 at 03:01:37PM +0800, Yifan Zhao wrote:

EROFS [1] is a lightweight read-only filesystem designed for performance
which has already been shipped in most Linux distributions as well as widely
used in several scenarios, such as Android system partitions, container
images, and rootfs for embedded devices.

This patch brings EROFS uncompressed support together with related tests.
Now, it's possible to boot directly through GRUB with an EROFS rootfs.

EROFS compressed files will be supported later since it has more work to
polish.

[1] https://erofs.docs.kernel.org


Sadly Coverity complains about the EROFS code. Please take a look below...


Let me try to fix these Coverity warnings directly..



Daniel


*** CID 460612:(BAD_SHIFT)
/grub-core/fs/erofs.c: 418 in erofs_map_blocks_chunkmode()
412   pos &= ~(unit - 1);
413
414   /* no overflow for multiplication as chunkbits >= 9 and sizeof(unit) 
<= 8 */
415   if (grub_add (pos, chunknr * unit, &pos))
416 return grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow is detected");
417

 CID 460612:(BAD_SHIFT)
 In expression "chunknr << chunkbits", left shifting by more than 63 bits has 
undefined behavior.  The shift amount, "chunkbits", is as much as 64.


These BAD_SHIFT warnings are in the same function:
erofs_map_blocks_chunkmode()

and we already have

 398   chunkbits = node->data->sb.log2_blksz + (chunk_format & 
EROFS_CHUNK_FORMAT_BLKBITS_MASK);
 399   if (chunkbits > 64)
 400 return grub_error (GRUB_ERR_BAD_FS, "invalid chunkbits %u @ inode %" 
PRIuGRUB_UINT64_T,
 401chunkbits, node->ino);

I guess here should be "if (chunkbits > 63)"?


418   map->m_la = chunknr << chunkbits;
419
420   if (grub_sub (erofs_inode_file_size (node), map->m_la, &map->m_plen))
421 return grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow is detected");
422   map->m_plen = grub_min (((grub_uint64_t) 1) << chunkbits,
423   ALIGN_UP (map->m_plen, erofs_blocksz 
(node->data)));
/grub-core/fs/erofs.c: 403 in erofs_map_blocks_chunkmode()
397
398   chunkbits = node->data->sb.log2_blksz + (chunk_format & 
EROFS_CHUNK_FORMAT_BLKBITS_MASK);
399   if (chunkbits > 64)
400 return grub_error (GRUB_ERR_BAD_FS, "invalid chunkbits %u @ inode 
%" PRIuGRUB_UINT64_T,
401chunkbits, node->ino);
402

 CID 460612:(BAD_SHIFT)
 In expression "map->m_la >> chunkbits", right shifting by more than 63 bits has 
undefined behavior.  The shift amount, "chunkbits", is as much as 64.

403   chunknr = map->m_la >> chunkbits;
404
405   if (grub_add (erofs_iloc (node), erofs_inode_size (node), &pos) ||
406   grub_add (pos, erofs_inode_xattr_ibody_size (node), &pos))
407 return grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow is detected");
408
/grub-core/fs/erofs.c: 422 in erofs_map_blocks_chunkmode()
416 return grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow is detected");
417
418   map->m_la = chunknr << chunkbits;
419
420   if (grub_sub (erofs_inode_file_size (node), map->m_la, &map->m_plen))
421 return grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow is detected");

 CID 460612:(BAD_SHIFT)
 In expression "1UL << chunkbits", left shifting by more than 63 bits has undefined 
behavior.  The shift amount, "chunkbits", is as much as 64.

422   map->m_plen = grub_min (((grub_uint64_t) 1) << chunkbits,
423   ALIGN_UP (map->m_plen, erofs_blocksz 
(node->data)));
424
425   if (chunk_format & EROFS_CHUNK_FORMAT_INDEXES)
426 {
427   struct grub_erofs_inode_chunk_index idx;


*** CID 460611:  Error handling issues  (CHECKED_RETURN)
/grub-core/fs/erofs.c: 793 in erofs_dir_iter()
787 {
788   struct grub_erofs_dir_ctx *ctx = data;
789   struct grub_dirhook_info info = {0};
790
791   if (!node->inode_loaded)
792 {

 CID 460611:  Error handling issues  (CHECKED_RETURN)
 Calling "erofs_read_inode" without checking return value (as is done 
elsewhere 6 out of 7 times).


Will fix as below:

@@ -787,11 +787,13 @@ erofs_dir_iter (const char *filename, enum 
grub_fshelp_filetype filetype,
 {
   struct grub_erofs_dir_ctx *ctx = data;
   struct grub_dirhook_info info = {0};
+  grub_err_t err;

   if (!node->inode_loaded)
 {
-  erofs_read_inode (ctx->data, node);
-  grub_errno = GRUB_ERR_NONE;
+  err = erofs_read_inode (ctx->data, node);
+  if (err != GRUB_ERR_NONE)
+return 0;
 }


793   erofs_read_inode (ctx->data, node);
794   grub_errno = GRUB_ERR_NONE;
795 }
796
797   if (node->inode_loaded)
798 {

_

[PATCH v11 2/2] fs/erofs: Add tests for EROFS in grub-fs-tester

2024-05-09 Thread Gao Xiang
From: Yifan Zhao 

In this patch, three tests of EROFS are introduced and they cover
compact, extended and chunk-based inodes, respectively.

Signed-off-by: Yifan Zhao 
Reviewed-by: Glenn Washburn 
Signed-off-by: Gao Xiang 
---
Reviewed-by Link: 
https://lists.gnu.org/archive/html/grub-devel/2024-04/msg00101.html

 .gitignore   |  1 +
 Makefile.util.def|  6 ++
 tests/erofs_test.in  | 20 
 tests/util/grub-fs-tester.in | 32 +---
 4 files changed, 52 insertions(+), 7 deletions(-)
 create mode 100644 tests/erofs_test.in

diff --git a/.gitignore b/.gitignore
index 11fcecf5c..4c1f91db8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -104,6 +104,7 @@ widthspec.bin
 /docs/version-dev.texi
 /docs/version.texi
 /ehci_test
+/erofs_test
 /example_grub_script_test
 /example_scripted_test
 /example_unit_test
diff --git a/Makefile.util.def b/Makefile.util.def
index 8d3bc107f..0f74a1680 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -764,6 +764,12 @@ script = {
   dependencies = 'garbage-gen$(BUILD_EXEEXT)';
 };
 
+script = {
+  testcase = native;
+  name = erofs_test;
+  common = tests/erofs_test.in;
+};
+
 script = {
   testcase = native;
   name = ext234_test;
diff --git a/tests/erofs_test.in b/tests/erofs_test.in
new file mode 100644
index 0..5627a
--- /dev/null
+++ b/tests/erofs_test.in
@@ -0,0 +1,20 @@
+#!@BUILD_SHEBANG@
+
+set -e
+
+if [ "x$EUID" = "x" ] ; then
+  EUID=`id -u`
+fi
+
+if [ "$EUID" != 0 ] ; then
+   exit 99
+fi
+
+if ! which mkfs.erofs >/dev/null 2>&1; then
+   echo "mkfs.erofs not installed; cannot test erofs."
+   exit 99
+fi
+
+"@builddir@/grub-fs-tester" erofs_compact
+"@builddir@/grub-fs-tester" erofs_extended
+"@builddir@/grub-fs-tester" erofs_chunk
diff --git a/tests/util/grub-fs-tester.in b/tests/util/grub-fs-tester.in
index ea8b2d1f6..df5dc7542 100644
--- a/tests/util/grub-fs-tester.in
+++ b/tests/util/grub-fs-tester.in
@@ -227,6 +227,10 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" 
"$MAXLOGSECSIZE" 1); do
xsquash*)
MINBLKSIZE=4096
MAXBLKSIZE=1048576;;
+   x"erofs_"*)
+   MINBLKSIZE=4096
+   MAXBLKSIZE=4096
+   ;;
xxfs|xf2fs)
MINBLKSIZE=$SECSIZE
# OS Limitation: GNU/Linux doesn't accept > 4096
@@ -382,8 +386,8 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 
1); do
FSLABEL="g;/_é䏌䐓䏕䎛䎾䏴кит u"
#FSLABEL="g;/_é莭莽😁кит u"
;;
-   # FS LIMITATION: reiserfs, extN and jfs label is at most 16 
UTF-8 characters
-   x"reiserfs_old" | x"reiserfs" | x"ext"* | x"lvm"* | x"luks"* | 
x"mdraid"* | x"jfs" | x"jfs_caseins")
+   # FS LIMITATION: reiserfs, extN, jfs and erofs label is at most 
16 UTF-8 characters
+   x"reiserfs_old" | x"reiserfs" | x"ext"* | x"lvm"* | x"luks"* | 
x"mdraid"* | x"jfs" | x"jfs_caseins" | x"erofs_"*)
FSLABEL="g;/éт 莭😁";;
# FS LIMITATION: No underscore, space, semicolon, slash or 
international characters in UFS* in label. Limited to 32 UTF-8 characters
x"ufs1" | x"ufs1_sun" | x"ufs2")
@@ -661,7 +665,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 
1); do
x"tarfs" | x"cpio_"*| x"ziso9660" | x"romfs" | x"squash4_"*\
| x"iso9660" | xjoliet | xrockridge | xrockridge_joliet \
| x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 \
-   | xrockridge_joliet_1999)
+   | xrockridge_joliet_1999 | x"erofs_"*)
MNTPOINTRW="$MASTER"
MNTPOINTRO="$MASTER"
mkdir -p "$MASTER";;
@@ -805,7 +809,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 
1); do
sleep 1
"zfs" create "$FSLABEL"/"grub fs"
sleep 1;;
-   x"tarfs" | x"cpio_"* | x"iso9660" | xjoliet | xrockridge | 
xrockridge_joliet | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 | 
xrockridge_joliet_1999 | x"ziso9660" | x"romfs" | x"squash4_"*)
+   x"tarfs" | x"cpio_"* | x"iso9660" | xjoliet | xrockridge | 
xrockridge_joliet | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 | 
xrockridge_joliet_1999 | x"ziso9660" | x"romfs" | x"squash4_"* | x"erofs_"*)
INSTDEVICE=/dev/null;;
x"reiserfs")
"mkfs.reiserfs" --format=3.6 -b $BLKSIZE -l "$FSLABEL" -q 
"${MOUNTDEVICE}" ;;
@@ -990,7 +994,7 @@ for LOGSECSIZE in $(range "$MINLOGSECSIZE" "$MAXLOGSECSIZE" 
1); do
x"zfs"*)
OSDIR="grub fs/"
GRUBDIR="($GRUBDEVICE)/grub fs@";;
-   x"tarfs" | x"cpio_"* | x"iso9660" | xjoliet | xrockridge | 
xrockridge_joliet | x"iso9660_1999" | xjoliet_1999 | xrockridge_1999 | 
xrockridge_joliet_1999 | x"ziso9660" | x"romfs" |

[PATCH v11 0/2] Introduce EROFS support

2024-05-09 Thread Gao Xiang
EROFS [1] is a lightweight read-only filesystem designed for performance
which has already been shipped in most Linux distributions as well as widely
used in several scenarios, such as Android system partitions, container
images, and rootfs for embedded devices.

This patch brings EROFS uncompressed support together with related tests.
Now, it's possible to boot directly through GRUB with an EROFS rootfs.

EROFS compressed files will be supported later since it has more work to
polish.

[1] https://erofs.docs.kernel.org

changelog since v10:
- Fix coverity warnings reported by Daniel:
   
https://lore.kernel.org/grub-devel/20240509175714.fccferxvffylk...@tomti.i.net-space.pl

Tested-by Link (Commit 1): 
https://lists.gnu.org/archive/html/grub-devel/2024-05/msg1.html
Reviewed-by Link (Commit 2): 
https://lists.gnu.org/archive/html/grub-devel/2024-04/msg00101.html

Yifan Zhao (2):
  fs/erofs: Add support for EROFS
  fs/erofs: Add tests for EROFS in grub-fs-tester

 .gitignore   |1 +
 INSTALL  |8 +-
 Makefile.util.def|7 +
 docs/grub.texi   |3 +-
 grub-core/Makefile.core.def  |5 +
 grub-core/fs/erofs.c | 1008 ++
 tests/erofs_test.in  |   20 +
 tests/util/grub-fs-tester.in |   32 +-
 8 files changed, 1072 insertions(+), 12 deletions(-)
 create mode 100644 grub-core/fs/erofs.c
 create mode 100644 tests/erofs_test.in

Interdiff against v10:

diff --git a/grub-core/fs/erofs.c b/grub-core/fs/erofs.c
index b82212b16..14c86f435 100644
--- a/grub-core/fs/erofs.c
+++ b/grub-core/fs/erofs.c
@@ -396,7 +396,7 @@ erofs_map_blocks_chunkmode (grub_fshelp_node_t node,
 unit = EROFS_BLOCK_MAP_ENTRY_SIZE;
 
   chunkbits = node->data->sb.log2_blksz + (chunk_format & 
EROFS_CHUNK_FORMAT_BLKBITS_MASK);
-  if (chunkbits > 64)
+  if (chunkbits > 63)
 return grub_error (GRUB_ERR_BAD_FS, "invalid chunkbits %u @ inode %" 
PRIuGRUB_UINT64_T,
   chunkbits, node->ino);
 
@@ -787,11 +787,13 @@ erofs_dir_iter (const char *filename, enum 
grub_fshelp_filetype filetype,
 {
   struct grub_erofs_dir_ctx *ctx = data;
   struct grub_dirhook_info info = {0};
+  grub_err_t err;
 
   if (!node->inode_loaded)
 {
-  erofs_read_inode (ctx->data, node);
-  grub_errno = GRUB_ERR_NONE;
+  err = erofs_read_inode (ctx->data, node);
+  if (err != GRUB_ERR_NONE)
+return 0;
 }
 
   if (node->inode_loaded)
@@ -953,11 +955,10 @@ grub_erofs_label (grub_device_t device, char **label)
 }
 
   *label = grub_strndup ((char *) data->sb.volume_name, sizeof 
(data->sb.volume_name));
-  if (!*label)
-return grub_errno;
-
   grub_free (data);
 
+  if (!*label)
+return grub_errno;
   return GRUB_ERR_NONE;
 }
 
@@ -1004,4 +1005,4 @@ GRUB_MOD_INIT (erofs)
 GRUB_MOD_FINI (erofs)
 {
   grub_fs_unregister (&grub_erofs_fs);
 }

-- 
2.39.3


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


[PATCH v11 1/2] fs/erofs: Add support for EROFS

2024-05-09 Thread Gao Xiang
From: Yifan Zhao 

EROFS [1] is a lightweight read-only filesystem designed for performance
which has already been shipped in most Linux distributions as well as widely
used in several scenarios, such as Android system partitions, container
images, and rootfs for embedded devices.

This patch brings EROFS uncompressed support. Now, it's possible to boot
directly through GRUB with an EROFS rootfs.

EROFS compressed files will be supported later since it has more work to
polish.

[1] https://erofs.docs.kernel.org

Signed-off-by: Yifan Zhao 
Tested-by: Daniel Axtens  # fuzz testing only
Signed-off-by: Gao Xiang 
---
Tested-by Link: 
https://lists.gnu.org/archive/html/grub-devel/2024-05/msg1.html

 INSTALL |8 +-
 Makefile.util.def   |1 +
 docs/grub.texi  |3 +-
 grub-core/Makefile.core.def |5 +
 grub-core/fs/erofs.c| 1008 +++
 5 files changed, 1020 insertions(+), 5 deletions(-)
 create mode 100644 grub-core/fs/erofs.c

diff --git a/INSTALL b/INSTALL
index 8d9207c84..84030c9f4 100644
--- a/INSTALL
+++ b/INSTALL
@@ -77,15 +77,15 @@ Prerequisites for make-check:
 
 * If running a Linux kernel the following modules must be loaded:
   - fuse, loop
-  - btrfs, ext4, f2fs, fat, hfs, hfsplus, jfs, mac-roman, minix, nilfs2,
+  - btrfs, erofs, ext4, f2fs, fat, hfs, hfsplus, jfs, mac-roman, minix, nilfs2,
 reiserfs, udf, xfs
   - On newer kernels, the exfat kernel modules may be used instead of the
 exfat FUSE filesystem
 * The following are Debian named packages required mostly for the full
   suite of filesystem testing (but some are needed by other tests as well):
-  - btrfs-progs, dosfstools, e2fsprogs, exfat-utils, f2fs-tools, genromfs,
-hfsprogs, jfsutils, nilfs-tools, ntfs-3g, reiserfsprogs, squashfs-tools,
-reiserfsprogs, udftools, xfsprogs, zfs-fuse
+  - btrfs-progs, dosfstools, e2fsprogs, erofs-utils, exfat-utils, f2fs-tools,
+genromfs, hfsprogs, jfsutils, nilfs-tools, ntfs-3g, reiserfsprogs,
+squashfs-tools, reiserfsprogs, udftools, xfsprogs, zfs-fuse
   - exfat-fuse, if not using the exfat kernel module
   - gzip, lzop, xz-utils
   - attr, cpio, g++, gawk, parted, recode, tar, util-linux
diff --git a/Makefile.util.def b/Makefile.util.def
index 9432365a9..8d3bc107f 100644
--- a/Makefile.util.def
+++ b/Makefile.util.def
@@ -98,6 +98,7 @@ library = {
   common = grub-core/fs/cpio_be.c;
   common = grub-core/fs/odc.c;
   common = grub-core/fs/newc.c;
+  common = grub-core/fs/erofs.c;
   common = grub-core/fs/ext2.c;
   common = grub-core/fs/fat.c;
   common = grub-core/fs/exfat.c;
diff --git a/docs/grub.texi b/docs/grub.texi
index d32266f69..b198d963d 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -353,6 +353,7 @@ blocklist notation. The currently supported filesystem 
types are @dfn{Amiga
 Fast FileSystem (AFFS)}, @dfn{AtheOS fs}, @dfn{BeFS},
 @dfn{BtrFS} (including raid0, raid1, raid10, gzip and lzo),
 @dfn{cpio} (little- and big-endian bin, odc and newc variants),
+@dfn{EROFS} (only uncompressed support for now),
 @dfn{Linux ext2/ext3/ext4}, @dfn{DOS FAT12/FAT16/FAT32},
 @dfn{exFAT}, @dfn{F2FS}, @dfn{HFS}, @dfn{HFS+},
 @dfn{ISO9660} (including Joliet, Rock-ridge and multi-chunk files),
@@ -6276,7 +6277,7 @@ assumed to be encoded in UTF-8.
 NTFS, JFS, UDF, HFS+, exFAT, long filenames in FAT, Joliet part of
 ISO9660 are treated as UTF-16 as per specification. AFS and BFS are read
 as UTF-8, again according to specification. BtrFS, cpio, tar, squash4, minix,
-minix2, minix3, ROMFS, ReiserFS, XFS, ext2, ext3, ext4, FAT (short names),
+minix2, minix3, ROMFS, ReiserFS, XFS, EROFS, ext2, ext3, ext4, FAT (short 
names),
 F2FS, RockRidge part of ISO9660, nilfs2, UFS1, UFS2 and ZFS are assumed
 to be UTF-8. This might be false on systems configured with legacy charset
 but as long as the charset used is superset of ASCII you should be able to
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 8e1b1d9f3..7fa9446bd 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1442,6 +1442,11 @@ module = {
   common = fs/odc.c;
 };
 
+module = {
+  name = erofs;
+  common = fs/erofs.c;
+};
+
 module = {
   name = ext2;
   common = fs/ext2.c;
diff --git a/grub-core/fs/erofs.c b/grub-core/fs/erofs.c
new file mode 100644
index 0..14c86f435
--- /dev/null
+++ b/grub-core/fs/erofs.c
@@ -0,0 +1,1008 @@
+/* erofs.c - Enhanced Read-Only File System */
+/*
+ *  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 FI

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

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

2024-05-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 v15 14/20] tpm2: Support authorized policy

2024-05-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 -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 v15 04/20] libtasn1: changes for grub compatibility

2024-05-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 
Reviewed-by: Stefan Berger 
---
 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 v15 00/20] Automatic Disk Unlock with TPM2

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

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

v15:
- Changes in tpm2_test
  - Quoting the variables which contain file paths
  - Correcting the exit code for several commands
  - Writing the verification text directly into the LUKS device
  - Amending the waiting loop for swtpm
  - Replacing exit with return in tpm2_seal_unseal() and
tpm2_seal_unseal_nv()
  - Collecting the parameters for the SRK mode testcases in an array
and invoking tpm2_seal_unseal() with a for loop
  - Moving the tpm2-tools commands for the NV index mode to a separate
function  
  - Using tpm2_evictcontrol to remove the object from the NV index to
match the key sealing commands
  - Printing the test results
  - Printing error messages to stderr

v14:
- https://lists.gnu.org/archive/html/grub-devel/2024-05/msg00011.html
- GIT repo: https://github.com/lcp/grub2/tree/tpm2-unlock-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 lates

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

2024-05-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 v15 09/20] key_protector: Add key protectors framework

2024-05-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 
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 v15 12/20] cryptodisk: Support key protectors

2024-05-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 
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 v15 19/20] tpm2: Enable tpm2 module for grub-emu

2024-05-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 
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 v15 20/20] tests: Add tpm2_test

2024-05-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 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   | 389 +++
 tests/util/grub-shell.in |   6 +-
 3 files changed, 400 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..aecd1a5a3
--- /dev/null
+++ b/tests/tpm2_test.in
@@ -0,0 +1,389 @@
+#! @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 99
+cryptsetup luksFormat -q ${csopt} "${luksfile}" "${lukskeyfile}" || exit 99
+
+# Write vtext into the first block of the LUKS2 image
+luksdev=/dev/mapper/`basename "${tpm2testdir}"`
+cryptsetup open --key-file "${lukskeyfile}" "${luksfile}" `basename 
"${luksdev}"` || exit 99
+echo "${vtext}" > "${luksdev}"
+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 v15 18/20] diskfilter: look up cryptodisk devices first

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

2024-05-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.

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 v15 06/20] libtasn1: compile into asn1 module

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

2024-05-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.

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

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

2024-05-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|  34 ++
 ...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, 524 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..0cd419390 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,39 @@ 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 may be necessary to apply the patches in
+@file{grub-core/lib/libtasn1-patches/} to adjust the code to be compatible with
+grub. 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.
+
 @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 fo

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

2024-05-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
 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 v15 16/20] cryptodisk: Fallback to passphrase

2024-05-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 
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 v15 15/20] tpm2: Implement NV index

2024-05-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 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