[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[RFC PATCH v3 1/1] kern/dl: Add module version check
From: |
Zhang Boyang |
Subject: |
[RFC PATCH v3 1/1] kern/dl: Add module version check |
Date: |
Fri, 23 Dec 2022 00:52:26 +0800 |
This patch add version information to GRUB modules. Specifically,
PACKAGE_VERSION is embedded as null-terminated string in .modver
section. This string is checked at module loading time.
If GRUB is locked down, modules with mismatched version will be
rejected. This is necessary for implementing external signed modules
securely (in future).
If GRUB is not locked down, the behavior is configurable at build time
by "--enable-modver-check={audit|enforce}". If the option is set to
"enforce", the behavior is same as lockdown mode. If the option is set
to "audit" (this is the default), modules can still be loaded even if
they have mismatched version, and a warning message will be emited. If
the option is disabled by "--disable-modver-check", modules can always
be loaded and no warning message will be generated. Please note this
option doesn't have any effect if GRUB is locked down.
It should be pointed out that the version string is only consisted of
PACKAGE_VERSION. Any difference not reflected in PACKAGE_VERSION is not
noticed by version check (e.g. configure options).
Link: https://lists.gnu.org/archive/html/grub-devel/2022-12/msg00280.html
Signed-off-by: Zhang Boyang <zhangboyang.id@gmail.com>
---
config.h.in | 5 +++++
configure.ac | 12 ++++++++++
grub-core/Makefile.am | 2 +-
grub-core/genmod.sh.in | 28 ++++++++++++++---------
grub-core/kern/dl.c | 42 +++++++++++++++++++++++++++++++++++
util/grub-module-verifierXX.c | 10 +++++++++
6 files changed, 87 insertions(+), 12 deletions(-)
diff --git a/config.h.in b/config.h.in
index 4d1e50eba..bac67acc5 100644
--- a/config.h.in
+++ b/config.h.in
@@ -13,6 +13,11 @@
#define MM_DEBUG @MM_DEBUG@
#endif
+#define MODVER_CHECK @MODVER_CHECK@
+#define MODVER_CHECK_IGNORE 0
+#define MODVER_CHECK_AUDIT 1
+#define MODVER_CHECK_ENFORCE 2
+
/* 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..e9fe6c25a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1521,6 +1521,18 @@ else
fi
AC_SUBST([MM_DEBUG])
+AC_ARG_ENABLE([modver-check],
+ AS_HELP_STRING([--enable-modver-check={audit|enforce}],
+ [enable module version check for non-lockdown
mode (default=audit)]))
+if test "x$enable_modver_check" = xno; then
+ MODVER_CHECK=0
+elif test "x$enable_modver_check" = xenforce; then
+ MODVER_CHECK=2
+else
+ MODVER_CHECK=1
+fi
+AC_SUBST([MODVER_CHECK])
+
AC_ARG_ENABLE([cache-stats],
AS_HELP_STRING([--enable-cache-stats],
[enable disk cache statistics collection]))
diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am
index 80e7a83ed..d76aa80f4 100644
--- a/grub-core/Makefile.am
+++ b/grub-core/Makefile.am
@@ -40,7 +40,7 @@ gentrigtables$(BUILD_EXEEXT): gentrigtables.c
CLEANFILES += gentrigtables$(BUILD_EXEEXT)
build-grub-module-verifier$(BUILD_EXEEXT):
$(top_srcdir)/util/grub-module-verifier.c
$(top_srcdir)/util/grub-module-verifier32.c
$(top_srcdir)/util/grub-module-verifier64.c
$(top_srcdir)/grub-core/kern/emu/misc.c $(top_srcdir)/util/misc.c
- $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS)
$(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_BUILD=1 -DGRUB_UTIL=1
-DGRUB_BUILD_PROGRAM_NAME=\"build-grub-module-verifier\" $^
+ $(BUILD_CC) -o $@ -I$(top_srcdir)/include $(BUILD_CFLAGS)
$(BUILD_CPPFLAGS) $(BUILD_LDFLAGS) -DGRUB_BUILD=1 -DGRUB_UTIL=1
-DGRUB_BUILD_PROGRAM_NAME=\"build-grub-module-verifier\"
-DPACKAGE_VERSION=\"$(PACKAGE_VERSION)\" $^
CLEANFILES += build-grub-module-verifier$(BUILD_EXEEXT)
# trigtables.c
diff --git a/grub-core/genmod.sh.in b/grub-core/genmod.sh.in
index e57c4d920..58a4cdcbe 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 "@PACKAGE_VERSION@\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\"))) =
\"@PACKAGE_VERSION@\";" >$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..8c08f8cfb 100644
--- a/grub-core/kern/dl.c
+++ b/grub-core/kern/dl.c
@@ -32,6 +32,7 @@
#include <grub/env.h>
#include <grub/cache.h>
#include <grub/i18n.h>
+#include <grub/lockdown.h>
/* Platforms where modules are in a readonly area of memory. */
#if defined(GRUB_MACHINE_QEMU)
@@ -475,6 +476,46 @@ grub_dl_check_license (grub_dl_t mod, Elf_Ehdr *e)
(char *) e + s->sh_offset);
}
+static grub_err_t
+grub_dl_check_version (grub_dl_t mod, Elf_Ehdr *e)
+{
+ Elf_Shdr *s = grub_dl_find_section (e, ".modver");
+ const char *modver;
+ int policy;
+
+ modver = _("(unknown)");
+ if (s == NULL)
+ goto fail;
+
+ modver = (char *) e + s->sh_offset;
+ if (grub_strcmp (modver, PACKAGE_VERSION) != 0)
+ goto fail;
+
+ return GRUB_ERR_NONE;
+
+fail:
+ policy = MODVER_CHECK;
+ if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED)
+ policy = MODVER_CHECK_ENFORCE;
+
+ switch (policy)
+ {
+ default:
+ case MODVER_CHECK_ENFORCE:
+ return grub_error (GRUB_ERR_BAD_MODULE,
+ N_("refuse to load module %.63s of incorrect version:
%.63s != %s"),
+ mod->name, modver, PACKAGE_VERSION);
+
+ case MODVER_CHECK_AUDIT:
+ grub_printf_ (N_("warning: module %.63s has incorrect version: %.63s !=
%s\n"),
+ mod->name, modver, PACKAGE_VERSION);
+ return GRUB_ERR_NONE;
+
+ case MODVER_CHECK_IGNORE:
+ return GRUB_ERR_NONE;
+ }
+}
+
static grub_err_t
grub_dl_resolve_name (grub_dl_t mod, Elf_Ehdr *e)
{
@@ -650,6 +691,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_version (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..fde111016 100644
--- a/util/grub-module-verifierXX.c
+++ b/util/grub-module-verifierXX.c
@@ -491,8 +491,18 @@ SUFFIX(grub_module_verify) (const char * const filename,
check_license (filename, arch, e);
Elf_Shdr *s;
+ const char *modver;
const char *modname;
+ s = find_section (arch, e, ".modver");
+ if (!s)
+ grub_util_error ("%s: no module version found", filename);
+
+ modver = (const char *) e + grub_target_to_host (s->sh_offset);
+
+ if (strcmp (modver, PACKAGE_VERSION) != 0)
+ grub_util_error ("%s: bad module version", filename);
+
s = find_section (arch, e, ".modname");
if (!s)
grub_util_error ("%s: no module name found", filename);
--
2.30.2