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...@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); } -- 2.30.2 _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel