grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [RFC PATCH v4 1/1] kern/dl: Add module vermagic check


From: Pete Batard
Subject: Re: [RFC PATCH v4 1/1] kern/dl: Add module vermagic check
Date: Thu, 29 Dec 2022 18:21:21 +0000
User-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Thunderbird/102.6.1

On 2022.12.25 07:57, Zhang Boyang wrote:
This commit add vermagic string to GRUB modules. Specifically, a unique
but shared vermagic string is embedded in .modver section of each
module, and it is checked at module loading time. The vermagic string is
uniquely generated by configure script in each run (formatted like
"{version} {target}-{platform} {random}", may be changed in future), so
it is different for each individual build.

If GRUB is locked down, modules with mismatched vermagic will be
rejected. This is a prerequisite for implementing external signed
modules securely.

If GRUB isn't locked down, modules can still be loaded even if they have
mismatched vermagic. A warning message will be generated if mismatched
vermagic is detected.

For reproducible builds, the vermagic string can be overridden by
"--with-vermagic=foobar". The value should be chosen carefully to
achieve uniqueness for each individual build. It's recommended to
include vendor, product, version of product, version of GRUB package in
product repository, target of GRUB (e.g. i386, x86_64), platform of GRUB
(e.g. pc, efi, emu) in that string.

For those who want to avoid warnings about mismatched modules, it can be
disabled by "--without-vermagic". However, this option has no effect if
GRUB is locked down (mismatched modules will be always rejected with an
error message in this case).

Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
---
  config.h.in                   |  3 +++
  configure.ac                  | 24 +++++++++++++++++++++
  grub-core/genmod.sh.in        | 28 +++++++++++++++----------
  grub-core/kern/dl.c           | 39 +++++++++++++++++++++++++++++++++++
  util/grub-module-verifierXX.c | 10 +++++++++
  5 files changed, 93 insertions(+), 11 deletions(-)

diff --git a/config.h.in b/config.h.in
index 4d1e50eba..dc3b6dc89 100644
--- a/config.h.in
+++ b/config.h.in
@@ -13,6 +13,9 @@
  #define MM_DEBUG @MM_DEBUG@
  #endif
+#define GRUB_VERMAGIC_WARNING @vermagic_warning@
+#define GRUB_VERMAGIC_STRING "@vermagic_string@"
+
  /* Define to 1 to enable disk cache statistics.  */
  #define DISK_CACHE_STATS @DISK_CACHE_STATS@
  #define BOOT_TIME_STATS @BOOT_TIME_STATS@
diff --git a/configure.ac b/configure.ac
index 93626b798..87cc654ad 100644
--- a/configure.ac
+++ b/configure.ac
@@ -316,6 +316,25 @@ AC_SUBST(grubdirname)
  AC_DEFINE_UNQUOTED(GRUB_DIR_NAME, "$grubdirname",
      [Default grub directory name])
+AC_ARG_WITH([vermagic],
+            AS_HELP_STRING([--with-vermagic=STRING],
+                           [set vermagic string [[auto-generated]]]),
+                          [vermagic_option="$with_vermagic"],
+                          [vermagic_option="yes"])
+if test x"$vermagic_option" = xno; then
+  vermagic_warning=0
+  vermagic_string="${PACKAGE_VERSION} ${target_cpu}-${platform}"
+elif test x"$vermagic_option" = xyes; then
+  vermagic_warning=1
+  vermagic_string="${PACKAGE_VERSION} ${target_cpu}-${platform} $(LC_ALL=C tr -d -c 
0-9A-Za-z < /dev/urandom | head -c 22)"
+else
+  vermagic_warning=1
+  vermagic_string="$vermagic_option"
+fi
+
+AC_SUBST(vermagic_warning)
+AC_SUBST(vermagic_string)
+
  #
  # Checks for build programs.
  #
@@ -2107,6 +2126,11 @@ AC_OUTPUT
  echo "*******************************************************"
  echo GRUB2 will be compiled with following components:
  echo Platform: "$target_cpu"-"$platform"
+if [ x"$vermagic_warning" = x1 ]; then
+echo Vermagic: "'$vermagic_string'"
+else
+echo Vermagic: "'$vermagic_string'" "(warning disabled)"
+fi
  if [ x"$platform" = xemu ]; then
  if [ x"$grub_emu_sdl_excuse" = x ]; then
  echo SDL support for grub-emu: Yes
diff --git a/grub-core/genmod.sh.in b/grub-core/genmod.sh.in
index e57c4d920..32679bbf0 100644
--- a/grub-core/genmod.sh.in
+++ b/grub-core/genmod.sh.in
@@ -36,22 +36,25 @@ deps=`grep ^$modname: $moddep | sed s@^.*:@@`
  rm -f $tmpfile $outfile
if test x@TARGET_APPLE_LINKER@ != x1; then
-    # stripout .modname and .moddeps sections from input module
-    @TARGET_OBJCOPY@ -R .modname -R .moddeps $infile $tmpfile
+    # stripout .modname and .moddeps and .modver sections from input module
+    @TARGET_OBJCOPY@ -R .modname -R .moddeps -R .modver $infile $tmpfile
- # Attach .modname and .moddeps sections
+    # Attach .modname and .moddeps and .modver sections
      t1=`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` || exit 1
      printf "$modname\0" >$t1
t2=`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` || exit 1
      for dep in $deps; do printf "$dep\0" >> $t2; done
+ t3=`mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX"` || exit 1
+    printf "@vermagic_string@\0" >$t3
+
      if test -n "$deps"; then
-       @TARGET_OBJCOPY@ --add-section .modname=$t1 --add-section .moddeps=$t2 
$tmpfile
+       @TARGET_OBJCOPY@ --add-section .modname=$t1 --add-section .moddeps=$t2 
--add-section .modver=$t3 $tmpfile
      else
-       @TARGET_OBJCOPY@ --add-section .modname=$t1 $tmpfile
+       @TARGET_OBJCOPY@ --add-section .modname=$t1 --add-section .modver=$t3 
$tmpfile
      fi
-    rm -f $t1 $t2
+    rm -f $t1 $t2 $t3
if test x@platform@ != xemu; then
            @TARGET_STRIP@ --strip-unneeded \
@@ -71,23 +74,26 @@ else
      tmpfile2=${outfile}.tmp2
      t1=${outfile}.t1.c
      t2=${outfile}.t2.c
+    t3=${outfile}.t3.c
# remove old files if any
-    rm -f $t1 $t2
+    rm -f $t1 $t2 $t3
cp $infile $tmpfile - # Attach .modname and .moddeps sections
+    # Attach .modname and .moddeps and .modver sections
      echo "char modname[]  __attribute__ ((section(\"_modname, _modname\"))) = 
\"$modname\";" >$t1
for dep in $deps; do echo "char moddep_$dep[] __attribute__ ((section(\"_moddeps, _moddeps\"))) = \"$dep\";" >>$t2; done + echo "char modver[] __attribute__ ((section(\"_modver, _modver\"))) = \"@vermagic_string@\";" >$t3
+
      if test -n "$deps"; then
-       @TARGET_CC@ @TARGET_LDFLAGS@ -ffreestanding -nostdlib -o $tmpfile2 $t1 
$t2 $tmpfile -Wl,-r
+       @TARGET_CC@ @TARGET_LDFLAGS@ -ffreestanding -nostdlib -o $tmpfile2 $t1 
$t2 $t3 $tmpfile -Wl,-r
      else
-       @TARGET_CC@ @TARGET_LDFLAGS@ -ffreestanding -nostdlib -o $tmpfile2 $t1 
$tmpfile -Wl,-r
+       @TARGET_CC@ @TARGET_LDFLAGS@ -ffreestanding -nostdlib -o $tmpfile2 $t1 
$t3 $tmpfile -Wl,-r
      fi
-    rm -f $t1 $t2 $tmpfile
+    rm -f $t1 $t2 $t3 $tmpfile
      mv $tmpfile2 $tmpfile
cp $tmpfile $tmpfile.bin
diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
index e447fd0fa..b4c5cc600 100644
--- a/grub-core/kern/dl.c
+++ b/grub-core/kern/dl.c
@@ -32,6 +32,9 @@
  #include <grub/env.h>
  #include <grub/cache.h>
  #include <grub/i18n.h>
+#include <grub/lockdown.h>
+#include <grub/term.h>
+#include <grub/time.h>
/* Platforms where modules are in a readonly area of memory. */
  #if defined(GRUB_MACHINE_QEMU)
@@ -475,6 +478,41 @@ grub_dl_check_license (grub_dl_t mod, Elf_Ehdr *e)
                     (char *) e + s->sh_offset);
  }
+static grub_err_t
+grub_dl_check_vermagic (grub_dl_t mod, Elf_Ehdr *e)
+{
+  Elf_Shdr *s = grub_dl_find_section (e, ".modver");
+  const char *modver;
+
+  modver = _("(no vermagic)");
+  if (s == NULL)
+    goto fail;
+
+  modver = (char *) e + s->sh_offset;
+  if (grub_strcmp (modver, GRUB_VERMAGIC_STRING) != 0)
+    goto fail;
+
+  return GRUB_ERR_NONE;
+
+fail:
+  if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED)
+    {
+      return grub_error (GRUB_ERR_BAD_MODULE,
+                        N_("refuse to load GRUB module '%.63s' of incorrect 
vermagic: '%.63s' != '%s'"),
+                        mod->name, modver, GRUB_VERMAGIC_STRING);
+    }
+  else
+    {
+#if GRUB_VERMAGIC_WARNING
+      grub_printf_ (N_("warning: GRUB module '%.63s' has incorrect vermagic: 
'%.63s' != '%s'\n"),
+                   mod->name, modver, GRUB_VERMAGIC_STRING);
+      grub_refresh ();
+      grub_millisleep (100);
+#endif
+      return GRUB_ERR_NONE;
+    }
+}
+
  static grub_err_t
  grub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e)
  {
@@ -650,6 +688,7 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size)
       Be sure to understand your license obligations.
    */
    if (grub_dl_resolve_name (mod, e)
+      || grub_dl_check_vermagic (mod, e)
        || grub_dl_check_license (mod, e)
        || grub_dl_resolve_dependencies (mod, e)
        || grub_dl_load_segments (mod, e)
diff --git a/util/grub-module-verifierXX.c b/util/grub-module-verifierXX.c
index d5907f268..ed3c0928d 100644
--- a/util/grub-module-verifierXX.c
+++ b/util/grub-module-verifierXX.c
@@ -492,6 +492,7 @@ SUFFIX(grub_module_verify) (const char * const filename,
Elf_Shdr *s;
    const char *modname;
+  const char *modver;
s = find_section (arch, e, ".modname");
    if (!s)
@@ -499,6 +500,15 @@ SUFFIX(grub_module_verify) (const char * const filename,
modname = (const char *) e + grub_target_to_host (s->sh_offset); + s = find_section (arch, e, ".modver");
+  if (!s)
+    grub_util_error ("%s: no module vermagic found", filename);
+
+  modver = (const char *) e + grub_target_to_host (s->sh_offset);
+
+  if (strcmp (modver, GRUB_VERMAGIC_STRING) != 0)
+    grub_util_error ("%s: bad module vermagic", filename);
+
    check_symbols(arch, e, modname, whitelist_empty);
    check_relocations(modname, arch, e);
  }

Reviewed-by: Pete Batard <pete@akeo.ie>

For what is worth, I also validated that `tr -d -c 0-9A-Za-z < /dev/urandom | head -c 22` does work in a Windows/MinGW environment with msys2.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]