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);
}