diff --git a/.gitignore b/.gitignore index 4d0dfb700e..c603c15d66 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ *.img *.log *.lst -!/grub-core/extra_deps.lst *.marker *.mod *.o @@ -268,6 +267,7 @@ widthspec.bin /stamp-h1 /syslinux_test /tar_test +/test_asn1 /test_sha512sum /test_unset /tests/syslinux/ubuntu10.04_grub.cfg @@ -279,3 +279,153 @@ widthspec.bin /xfs_test /xzcompress_test /zfs_test +======= +# things ./autogen.sh will create +/Makefile.utilgcry.def +/ABOUT-NLS +/aclocal.m4 +/autom4te.cache +/build-aux +/configure +/gnulib +/grub-core/lib/gnulib/ +/Makefile + +# things very common editors create that we never want +*~ +.*.sw? +*.patch + +# stuff you're likely to make while building test trees +grub.cfg +/build*/ + +# built objects across the whole tree +Makefile.in +*.a +*.am +*.efi +*.exec +*.image +*.img +*.info +*.lst +*.marker +/m4 +*.mod +*.module +*.o +*.pf2 +*.yy.[ch] +.deps/ +.deps-core/ +.deps-util/ +.dirstamp + +# next are things you get if you do ./configure in the topdir (for e.g. +# "make dist" invocation. +/config-util.h +/config.h +/include/grub/cpu +/include/grub/machine +/INSTALL +/INSTALL.grub +/po/Makefile.in.in +/po/Makevars +/po/Makevars.template +/po/POTFILES +/po/Rules-quot +/stamp-h +/stamp-h1 +bootstrap.log +config.log +config.status + +# stuff "make dist" creates +ChangeLog +grub-*.tar +grub-*.tar.* + +# stuff "make" creates +/[[:digit:]][[:digit:]]_?* +/ascii.h +/build-grub-gen-asciih +/build-grub-gen-widthspec +/build-grub-mkfont +/config-util.h.in +/garbage-gen +/grub*-bios-setup +/grub*-bios-setup.8 +/grub*-editenv +/grub*-editenv.1 +/grub*-file +/grub*-file.1 +/grub*-fs-tester +/grub*-fstest +/grub*-fstest.1 +/grub*-get-kernel-settings +/grub*-get-kernel-settings.3 +/grub*-glue-efi +/grub*-glue-efi.1 +/grub*-install +/grub*-install.8 +/grub*-kbdcomp +/grub*-kbdcomp.1 +/grub*-macbless +/grub*-macbless.8 +/grub*-menulst2cfg +/grub*-menulst2cfg.1 +/grub*-mount +/grub*-mount.1 +/grub*-mkconfig +/grub*-mkconfig.8 +/grub*-mkconfig_lib +/grub*-mkfont +/grub*-mkfont.1 +/grub*-mkimage +/grub*-mkimage.1 +/grub*-mklayout +/grub*-mklayout.1 +/grub*-mknetdir +/grub*-mknetdir.1 +/grub*-mkpasswd-pbkdf2 +/grub*-mkpasswd-pbkdf2.1 +/grub*-mkrelpath +/grub*-mkrelpath.1 +/grub*-mkrescue +/grub*-mkrescue.1 +/grub*-mkstandalone +/grub*-mkstandalone.1 +/grub*-ofpathname +/grub*-ofpathname.8 +/grub*-probe +/grub*-probe.8 +/grub*-reboot +/grub*-reboot.8 +/grub*-render-label +/grub*-render-label.1 +/grub*-script-check +/grub*-script-check.1 +/grub*-set-bootflag +/grub*-set-bootflag.1 +/grub*-set-default +/grub*-set-default.8 +/grub*-set-password +/grub*-set-password.8 +/grub*-shell +/grub*-shell-tester +/grub*-sparc64-setup +/grub*-sparc64-setup.8 +/grub*-syslinux2cfg +/grub*-syslinux2cfg.1 +/grub*-switch-to-blscfg +/grub*-switch-to-blscfg.8 +/grub_fstest.pp +/grub_fstest_init.c +/grub_fstest_init.lst +/grub_script.tab.[ch] +/libgrub.pp +/libgrub_a_init.c +/libgrub_a_init.lst +/stamp-h.in +/widthspec.h diff --git a/Makefile.util.def b/Makefile.util.def index 9432365a9d..ef344f5aa8 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -51,6 +51,12 @@ library = { common = grub-core/partmap/msdos.c; common = grub-core/fs/proc.c; common = grub-core/fs/archelp.c; + common = grub-core/kern/backtrace.c; + + x86 = grub-core/kern/i386/backtrace.c; + i386_xen = grub-core/kern/i386/backtrace.c; + x86_64_xen = grub-core/kern/i386/backtrace.c; + arm64 = grub-core/kern/arm64/backtrace.c; }; library = { @@ -455,6 +461,30 @@ script = { installdir = grubconf; }; +script = { + name = '08_fallback_counting'; + common = util/grub.d/08_fallback_counting.in; + installdir = grubconf; +}; + +script = { + name = '12_menu_auto_hide'; + common = util/grub.d/12_menu_auto_hide.in; + installdir = grubconf; +}; + +script = { + name = '14_menu_show_once'; + common = util/grub.d/14_menu_show_once.in; + installdir = grubconf; +}; + +script = { + name = '01_users'; + common = util/grub.d/01_users.in; + installdir = grubconf; +}; + script = { name = '10_windows'; common = util/grub.d/10_windows.in; @@ -497,6 +527,12 @@ script = { condition = COND_HOST_LINUX; }; +script = { + name = '10_reset_boot_success'; + common = util/grub.d/10_reset_boot_success.in; + installdir = grubconf; +}; + script = { name = '10_xnu'; common = util/grub.d/10_xnu.in; @@ -517,6 +553,13 @@ script = { installdir = grubconf; }; +script = { + name = '20_ppc_terminfo'; + common = util/grub.d/20_ppc_terminfo.in; + installdir = grubconf; + condition = COND_HOST_LINUX; +}; + script = { name = '30_os-prober'; common = util/grub.d/30_os-prober.in; @@ -541,6 +584,27 @@ script = { installdir = grubconf; }; +script = { + name = 'grub-systemd-integration.service'; + common = util/systemd/grub-systemd-integration.service.in; + installdir = systemdunit; + condition = COND_HOST_LINUX; +}; + +script = { + name = 'systemd-integration.sh'; + common = util/systemd/systemd-integration.sh.in; + installdir = grublibexec; + condition = COND_HOST_LINUX; +}; + +script = { + name = '10-grub-logind-service.conf'; + common = util/systemd/10-grub-logind-service.conf.in; + installdir = systemd_logind_service_d; + condition = COND_HOST_LINUX; +}; + program = { mansection = 1; name = grub-mkrescue; @@ -712,6 +776,13 @@ script = { installdir = sbin; }; +script = { + name = grub-get-kernel-settings; + common = util/grub-get-kernel-settings.in; + mansection = 3; + installdir = sbin; +}; + script = { name = grub-set-default; common = util/grub-set-default.in; @@ -726,6 +797,13 @@ script = { installdir = sbin; }; +script = { + name = grub-set-password; + common = util/grub-set-password.in; + mansection = 8; + installdir = sbin; +}; + script = { name = grub-mkconfig_lib; common = util/grub-mkconfig_lib.in; @@ -1250,6 +1328,12 @@ script = { common = tests/luks2_test.in; }; +script = { + testcase = native; + name = test_asn1; + common = tests/test_asn1.in; +}; + program = { testcase = native; name = example_unit_test; @@ -1360,6 +1444,13 @@ program = { ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; +script = { + name = grub-switch-to-blscfg; + common = util/grub-switch-to-blscfg.in; + mansection = 8; + installdir = sbin; +}; + program = { name = grub-glue-efi; mansection = 1; @@ -1422,3 +1513,10 @@ program = { ldadd = grub-core/lib/gnulib/libgnu.a; ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; + +program = { + name = grub-set-bootflag; + installdir = sbin; + mansection = 1; + common = util/grub-set-bootflag.c; +}; diff --git a/NEWS b/NEWS index 3101309621..e3cfcd569c 100644 --- a/NEWS +++ b/NEWS @@ -118,7 +118,7 @@ New in 2.02: * Prefer pmtimer for TSC calibration. * New/improved platform support: - * New `efifwsetup' and `lsefi' commands on EFI platforms. + * New `efifwsetup', `lsefi' and `connectefi` commands on EFI platforms. * New `cmosdump' and `cmosset' commands on platforms with CMOS support. * New command `pcidump' for PCI platforms. * Improve opcode parsing in ACPI halt implementation. diff --git a/acinclude.m4 b/acinclude.m4 index fa7840f09a..0231e64e33 100644 --- a/acinclude.m4 +++ b/acinclude.m4 @@ -136,6 +136,25 @@ if test "x$grub_cv_prog_ld_build_id_none" = xyes; then fi ]) +dnl Supply --build-id=sha1 to ld if building modules. +dnl This suppresses warnings from ld on some systems +AC_DEFUN([grub_PROG_LD_BUILD_ID_SHA1], +[AC_MSG_CHECKING([whether linker accepts --build-id=sha1]) +AC_CACHE_VAL(grub_cv_prog_ld_build_id_sha1, +[save_LDFLAGS="$LDFLAGS" +LDFLAGS="$LDFLAGS -Wl,--build-id=sha1" +AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_prog_ld_build_id_sha1=yes], + [grub_cv_prog_ld_build_id_sha1=no]) +LDFLAGS="$save_LDFLAGS" +]) +AC_MSG_RESULT([$grub_cv_prog_ld_build_id_sha1]) + +if test "x$grub_cv_prog_ld_build_id_sha1" = xyes; then + TARGET_LDFLAGS="$TARGET_LDFLAGS -Wl,--build-id=sha1" +fi +]) + dnl Check nm AC_DEFUN([grub_PROG_NM_WORKS], [AC_MSG_CHECKING([whether nm works]) diff --git a/bootstrap.conf b/bootstrap.conf index 7a7813d282..799cdb1a35 100644 --- a/bootstrap.conf +++ b/bootstrap.conf @@ -24,6 +24,7 @@ gnulib_modules=" argp base64 error + filevercmp fnmatch getdelim getline @@ -94,7 +95,7 @@ bootstrap_post_import_hook () { patch -d po -p3 \ < "po/gettext-patches/$patchname.patch" done - FROM_BOOTSTRAP=1 ./autogen.sh + PYTHON=python3 FROM_BOOTSTRAP=1 ./autogen.sh set +e # bootstrap expects this } diff --git a/conf/Makefile.common b/conf/Makefile.common index b8f216f6cd..f8c2f6bd88 100644 --- a/conf/Makefile.common +++ b/conf/Makefile.common @@ -41,7 +41,7 @@ CFLAGS_KERNEL = $(CFLAGS_PLATFORM) -ffreestanding LDFLAGS_KERNEL = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) CPPFLAGS_KERNEL = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) -DGRUB_KERNEL=1 CCASFLAGS_KERNEL = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) -STRIPFLAGS_KERNEL = -R .rel.dyn -R .reginfo -R .note -R .comment -R .drectve -R .note.gnu.gold-version -R .MIPS.abiflags -R .ARM.exidx +STRIPFLAGS_KERNEL = -R .eh_frame -R .rel.dyn -R .reginfo -R .note -R .comment -R .drectve -R .note.gnu.gold-version -R .MIPS.abiflags -R .ARM.exidx -R .note.gnu.property -R .gnu.build.attributes if !COND_emu if COND_HAVE_ASM_USCORE LDFLAGS_KERNEL += -Wl,--defsym=_malloc=_grub_malloc -Wl,--defsym=_free=_grub_free @@ -50,32 +50,35 @@ else endif endif -CFLAGS_MODULE = $(CFLAGS_PLATFORM) -ffreestanding -LDFLAGS_MODULE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-r -CPPFLAGS_MODULE = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) -CCASFLAGS_MODULE = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) +CFLAGS_MODULE = $(TARGET_CFLAGS) $(CFLAGS_PLATFORM) -ffreestanding +LDFLAGS_MODULE = $(TARGET_LDFLAGS) $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-r +CPPFLAGS_MODULE = $(TARGET_CPPFLAGS) $(CPPFLAGS_DEFAULT) $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) +CCASFLAGS_MODULE = $(TARGET_CCASFLAGS) $(CCASFLAGS_DEFAULT) $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) CFLAGS_IMAGE = $(CFLAGS_PLATFORM) -fno-builtin LDFLAGS_IMAGE = $(LDFLAGS_PLATFORM) -nostdlib $(TARGET_LDFLAGS_OLDMAGIC) -Wl,-S CPPFLAGS_IMAGE = $(CPPFLAGS_CPU) $(CPPFLAGS_PLATFORM) CCASFLAGS_IMAGE = $(CCASFLAGS_CPU) $(CCASFLAGS_PLATFORM) -CFLAGS_PROGRAM = -LDFLAGS_PROGRAM = -CPPFLAGS_PROGRAM = -CCASFLAGS_PROGRAM = +CFLAGS_PROGRAM = $(CFLAGS_CPU) +LDFLAGS_PROGRAM = $(LDFLAGS_CPU) +CPPFLAGS_PROGRAM = $(CPPFLAGS_CPU) +CCASFLAGS_PROGRAM = $(CCASFLAGS_CPU) -CFLAGS_LIBRARY = -CPPFLAGS_LIBRARY = -CCASFLAGS_LIBRARY = +CFLAGS_LIBRARY = $(CFLAGS_CPU) +CPPFLAGS_LIBRARY = $(CPPFLAGS_CPU) +CCASFLAGS_LIBRARY = $(CCASFLAGS_CPU) # Other variables grubconfdir = $(sysconfdir)/grub.d +grublibexecdir = $(libexecdir)/$(grubdirname) platformdir = $(pkglibdir)/$(target_cpu)-$(platform) starfielddir = $(pkgdatadir)/themes/starfield +systemdunitdir = ${prefix}/lib/systemd/system +systemd_logind_service_ddir = $(systemdunitdir)/systemd-logind.service.d -CFLAGS_GNULIB = -Wno-undef -Wno-sign-compare -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code -Wno-conversion -Wno-error=attributes +CFLAGS_GNULIB = -Wno-undef -Wno-error=sign-compare -Wno-unused -Wno-unused-parameter -Wno-redundant-decls -Wno-unreachable-code -Wno-error=attributes -Werror=trampolines -fno-trampolines CPPFLAGS_GNULIB = -I$(top_builddir)/grub-core/lib/gnulib -I$(top_srcdir)/grub-core/lib/gnulib CFLAGS_POSIX = -fno-builtin @@ -111,6 +114,7 @@ MOD_FILES = MODULE_FILES = MARKER_FILES = KERNEL_HEADER_FILES = +EXTRA_DEPS = bin_SCRIPTS = bin_PROGRAMS = @@ -132,6 +136,9 @@ platform_SCRIPTS = platform_PROGRAMS = sbin_SCRIPTS = sbin_PROGRAMS = +grublibexec_SCRIPTS = +systemdunit_SCRIPTS = +systemd_logind_service_d_SCRIPTS = EXTRA_DIST = CLEANFILES = diff --git a/conf/Makefile.extra-dist b/conf/Makefile.extra-dist index 5e7126f987..26ac8765e3 100644 --- a/conf/Makefile.extra-dist +++ b/conf/Makefile.extra-dist @@ -15,6 +15,9 @@ EXTRA_DIST += docs/man EXTRA_DIST += docs/autoiso.cfg EXTRA_DIST += docs/grub.cfg EXTRA_DIST += docs/osdetect.cfg +EXTRA_DIST += docs/org.gnu.grub.policy +EXTRA_DIST += docs/grub-boot-success.service +EXTRA_DIST += docs/grub-boot-success.timer EXTRA_DIST += conf/i386-cygwin-img-ld.sc diff --git a/config.h.in b/config.h.in index 9b1d399718..54a29a1719 100644 --- a/config.h.in +++ b/config.h.in @@ -16,6 +16,8 @@ /* Define to 1 to enable disk cache statistics. */ #define DISK_CACHE_STATS @DISK_CACHE_STATS@ #define BOOT_TIME_STATS @BOOT_TIME_STATS@ +#define DEBUG_WITH_TIMESTAMPS @DEBUG_WITH_TIMESTAMPS@ +#define DISABLE_PCR8 @DISABLE_PCR8@ /* We don't need those. */ #define MINILZO_CFG_SKIP_LZO_PTR 1 @@ -63,6 +65,7 @@ # define GRUB_TARGET_CPU "@GRUB_TARGET_CPU@" # define GRUB_PLATFORM "@GRUB_PLATFORM@" +# define GRUB_RPM_VERSION "@GRUB_RPM_VERSION@" # define GRUB_STACK_PROTECTOR_INIT @GRUB_STACK_PROTECTOR_INIT@ diff --git a/configure.ac b/configure.ac index cd667a2eb7..b37914440a 100644 --- a/configure.ac +++ b/configure.ac @@ -70,6 +70,7 @@ grub_TRANSFORM([grub-install]) grub_TRANSFORM([grub-mkconfig]) grub_TRANSFORM([grub-mkfont]) grub_TRANSFORM([grub-mkimage]) +grub_TRANSFORM([grub-get-kernel-settings]) grub_TRANSFORM([grub-glue-efi]) grub_TRANSFORM([grub-mklayout]) grub_TRANSFORM([grub-mkpasswd-pbkdf2]) @@ -77,6 +78,7 @@ grub_TRANSFORM([grub-mkrelpath]) grub_TRANSFORM([grub-mkrescue]) grub_TRANSFORM([grub-probe]) grub_TRANSFORM([grub-reboot]) +grub_TRANSFORM([grub-set-password]) grub_TRANSFORM([grub-script-check]) grub_TRANSFORM([grub-set-default]) grub_TRANSFORM([grub-sparc64-setup]) @@ -287,6 +289,19 @@ AC_SUBST(target_cpu) AC_SUBST(platform) # Define default variables +have_with_rpm_version=n +AC_ARG_WITH([rpm_version], + AS_HELP_STRING([--with-rpm-version=VERSION], + [set the rpm package version [[guessed]]]), + [have_with_rpm_version=y], + [have_with_rpm_version=n]) +if test x$have_with_rpm_version = xy; then + rpm_version="$with_rpm_version" +else + rpm_version="" +fi +GRUB_RPM_VERSION="$rpm_version" +AC_SUBST(GRUB_RPM_VERSION) have_with_bootdir=n AC_ARG_WITH([bootdir], @@ -319,6 +334,14 @@ AC_SUBST(grubdirname) AC_DEFINE_UNQUOTED(GRUB_DIR_NAME, "$grubdirname", [Default grub directory name]) +PKG_PROG_PKG_CONFIG +AS_IF([$($PKG_CONFIG --exists bash-completion)], [ + bashcompletiondir=$($PKG_CONFIG --variable=completionsdir bash-completion) +] , [ + bashcompletiondir=${datadir}/bash-completion/completions +]) +AC_SUBST(bashcompletiondir) + # # Checks for build programs. # @@ -534,6 +557,9 @@ HOST_CFLAGS="$HOST_CFLAGS $grub_cv_cc_w_extra_flags" # Check for target programs. # +# This makes sure pkg.m4 is available. +m4_pattern_forbid([^_?PKG_[A-Z_]+$],[*** pkg.m4 missing, please install pkg-config]) + # Find tools for the target. if test "x$target_alias" != x && test "x$host_alias" != "x$target_alias"; then tmp_ac_tool_prefix="$ac_tool_prefix" @@ -901,11 +927,23 @@ if test "x$target_cpu" = xloongarch64; then TARGET_CCASFLAGS="$TARGET_CCASFLAGS -Wa,-mla-global-with-abs" fi +# Should grub utils get the host CFLAGS, or the target CFLAGS? +AC_ARG_WITH([utils], + AS_HELP_STRING([--with-utils=host|target|build], + [choose which flags to build utilities with. (default=target)]), + [have_with_utils=y], + [have_with_utils=n]) +if test x"$have_with_utils" = xy ; then + with_utils="$withval" +else + with_utils=target +fi + # GRUB doesn't use float or doubles at all. Yet some toolchains may decide # that floats are a good fit to run instead of what's written in the code. # Given that floating point unit is disabled (if present to begin with) # when GRUB is running which may result in various hard crashes. -if test x"$platform" != xemu ; then +if test x"$platform" != xemu -a x"$with_utils" == xtarget ; then AC_CACHE_CHECK([for options to get soft-float], grub_cv_target_cc_soft_float, [ grub_cv_target_cc_soft_float=no if test "x$target_cpu" = xarm64; then @@ -1275,7 +1313,7 @@ AC_SUBST(TARGET_LDFLAGS_OLDMAGIC) LDFLAGS="$TARGET_LDFLAGS" -if test "$target_cpu" = x86_64 || test "$target_cpu" = sparc64 || test "$target_cpu" = riscv64 ; then +if test "$target_cpu" = x86_64 || test "$target_cpu" = sparc64 ; then # Use large model to support 4G memory AC_CACHE_CHECK([whether option -mcmodel=large works], grub_cv_cc_mcmodel, [ CFLAGS="$TARGET_CFLAGS -mcmodel=large" @@ -1285,9 +1323,11 @@ if test "$target_cpu" = x86_64 || test "$target_cpu" = sparc64 || test "$target_ ]) if test "x$grub_cv_cc_mcmodel" = xyes; then TARGET_CFLAGS="$TARGET_CFLAGS -mcmodel=large" - elif test "$target_cpu" = sparc64 || test "$target_cpu" = riscv64; then + elif test "$target_cpu" = sparc64; then TARGET_CFLAGS="$TARGET_CFLAGS -mcmodel=medany" fi +elif test "$target_cpu" = riscv64 ; then + TARGET_CFLAGS="$TARGET_CFLAGS -mcmodel=medany" fi if test "$target_cpu"-"$platform" = x86_64-efi; then @@ -1321,6 +1361,26 @@ if test "x$target_cpu" = xarm; then done ]) + AC_CACHE_CHECK([for options to disable movt and movw relocations], + grub_cv_target_cc_mword_relocations, + [grub_cv_target_cc_mword_relocations=no + for cand in "-mword-relocations" ; do + if test x"$grub_cv_target_cc_mword_relocations" != xno ; then + break + fi + CFLAGS="$TARGET_CFLAGS $cand -Werror" + CPPFLAGS="$TARGET_CPPFLAGS" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [grub_cv_target_cc_mword_relocations="$cand"], + []) + done + ]) + if test x"$grub_cv_target_cc_mword_relocations" = xno ; then + AC_MSG_ERROR(["your compiler doesn't support disabling movw/movt relocations"]) + else + TARGET_CFLAGS="$TARGET_CFLAGS $grub_cv_target_cc_mword_relocations" + fi + if test x"$grub_cv_target_cc_mno_movt" != xno ; then # A trick so that clang doesn't see it on link stage TARGET_CPPFLAGS="$TARGET_CPPFLAGS $grub_cv_target_cc_mno_movt" @@ -1527,7 +1587,15 @@ grub_PROG_TARGET_CC if test "x$TARGET_APPLE_LINKER" != x1 ; then grub_PROG_OBJCOPY_ABSOLUTE fi + +AC_ARG_ENABLE([build-id], + [AS_HELP_STRING([--enable-build-id], + [ask the linker to supply build-id notes (default=no)])]) +if test x$enable_build_id = xyes; then +grub_PROG_LD_BUILD_ID_SHA1 +else grub_PROG_LD_BUILD_ID_NONE +fi if test "x$target_cpu" = xi386; then if test "$platform" != emu && test "x$TARGET_APPLE_LINKER" != x1 ; then if test ! -z "$TARGET_IMG_LDSCRIPT"; then @@ -1620,6 +1688,28 @@ else fi AC_SUBST([BOOT_TIME_STATS]) +AC_ARG_WITH([debug-timestamps], + AS_HELP_STRING([--with-debug-timestamps], + [prepend debug traces with absolute and relative timestamps])) + +if test x$with_debug_timestamps = xyes; then + DEBUG_WITH_TIMESTAMPS=1 +else + DEBUG_WITH_TIMESTAMPS=0 +fi +AC_SUBST([DEBUG_WITH_TIMESTAMPS]) + +AC_ARG_WITH([pcr8-disabled], + AS_HELP_STRING([--with-pcr8-disabled], + [disable PCR8 measurements])) + +if test x$with_pcr8_disabled = xyes; then + DISABLE_PCR8=1 +else + DISABLE_PCR8=0 +fi +AC_SUBST([DISABLE_PCR8]) + AC_ARG_ENABLE([grub-emu-sdl2], [AS_HELP_STRING([--enable-grub-emu-sdl2], [build and install the `grub-emu' debugging utility with SDL2 support (default=guessed)])]) @@ -1847,7 +1937,7 @@ if test "x$with_dejavufont" = x; then # search in well-known directories if test x"$starfield_excuse" = x; then for ext in pcf pcf.gz bdf bdf.gz ttf ttf.gz; do - for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/dejavu /usr/share/fonts/truetype /usr/pkg/share/fonts/X11/TTF /usr/local/share/fonts/dejavu /usr/X11R6/lib/X11/fonts/TTF; do + for dir in . /usr/src /usr/share/fonts/X11/misc /usr/share/fonts/truetype/ttf-dejavu /usr/share/fonts/dejavu /usr/share/fonts/truetype /usr/pkg/share/fonts/X11/TTF /usr/local/share/fonts/dejavu /usr/X11R6/lib/X11/fonts/TTF /usr/share/fonts/dejavu-sans-fonts; do if test -f "$dir/DejaVuSans.$ext"; then DJVU_FONT_SOURCE="$dir/DejaVuSans.$ext" break 2 @@ -2081,6 +2171,17 @@ if test x"$enable_werror" != xno ; then fi fi +AC_ARG_ENABLE([wextra], + [AS_HELP_STRING([--disable-wextra], + [do not use -Wextra when building GRUB])]) +if test x"$enable_wextra" != xno ; then + TARGET_CFLAGS="$TARGET_CFLAGS -Wextra" + HOST_CFLAGS="$HOST_CFLAGS -Wextra" +fi + +TARGET_CFLAGS="$TARGET_CFLAGS -Werror=trampolines -fno-trampolines -Wno-incompatible-pointer-types" +HOST_CFLAGS="$HOST_CFLAGS -Werror=trampolines -fno-trampolines -Wno-incompatible-pointer-types" + TARGET_CPP="$TARGET_CC -E" TARGET_CCAS=$TARGET_CC @@ -2090,6 +2191,41 @@ HOST_CPPFLAGS="$HOST_CPPFLAGS -I\$(top_builddir)/include" TARGET_CPPFLAGS="$TARGET_CPPFLAGS -I\$(top_srcdir)/include" TARGET_CPPFLAGS="$TARGET_CPPFLAGS -I\$(top_builddir)/include" +case "$with_utils" in + host) + UTILS_CFLAGS=$HOST_CFLAGS + UTILS_CPPFLAGS=$HOST_CPPFLAGS + UTILS_CCASFLAGS=$HOST_CCASFLAGS + UTILS_LDFLAGS=$HOST_LDFLAGS + ;; + target) + UTILS_CFLAGS=$TARGET_CFLAGS + UTILS_CPPFLAGS=$TARGET_CPPFLAGS + UTILS_CCASFLAGS=$TARGET_CCASFLAGS + UTILS_LDFLAGS=$TARGET_LDFLAGS + ;; + build) + UTILS_CFLAGS=$BUILD_CFLAGS + UTILS_CPPFLAGS=$BUILD_CPPFLAGS + UTILS_CCASFLAGS=$BUILD_CCASFLAGS + UTILS_LDFLAGS=$BUILD_LDFLAGS + ;; + *) + AC_MSG_ERROR([--with-utils must be either host, target, or build]) + ;; +esac +AC_MSG_NOTICE([Using $with_utils flags for utilities.]) + +unset CFLAGS +unset CPPFLAGS +unset CCASFLAGS +unset LDFLAGS + +AC_SUBST(UTILS_CFLAGS) +AC_SUBST(UTILS_CPPFLAGS) +AC_SUBST(UTILS_CCASFLAGS) +AC_SUBST(UTILS_LDFLAGS) + GRUB_TARGET_CPU="${target_cpu}" GRUB_PLATFORM="${platform}" @@ -2184,6 +2320,8 @@ AM_CONDITIONAL([COND_APPLE_LINKER], [test x$TARGET_APPLE_LINKER = x1]) AM_CONDITIONAL([COND_ENABLE_EFIEMU], [test x$enable_efiemu = xyes]) AM_CONDITIONAL([COND_ENABLE_CACHE_STATS], [test x$DISK_CACHE_STATS = x1]) AM_CONDITIONAL([COND_ENABLE_BOOT_TIME_STATS], [test x$BOOT_TIME_STATS = x1]) +AM_CONDITIONAL([COND_DEBUG_WITH_TIMESTAMPS], [test x$DEBUG_WITH_TIMESTAMPS = x1]) +AM_CONDITIONAL([COND_DISABLE_PCR8], [test x$DISABLE_PCR8 = x1]) AM_CONDITIONAL([COND_HAVE_CXX], [test x$HAVE_CXX = xyes]) @@ -2285,6 +2423,12 @@ else echo With boot time statistics: No fi +if [ x"$with_debug_timestamps" = xyes ]; then +echo Debug traces with timestamps: Yes +else +echo Debug traces with timestamps: No +fi + if [ x"$efiemu_excuse" = x ]; then echo efiemu runtime: Yes else diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000000..e1d849ef95 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,5 @@ +/*.in +/Makefile +/stamp-1 +/stamp-vti +/version*.texi diff --git a/docs/grub-boot-indeterminate.service b/docs/grub-boot-indeterminate.service new file mode 100644 index 0000000000..6c8dcb186b --- /dev/null +++ b/docs/grub-boot-indeterminate.service @@ -0,0 +1,11 @@ +[Unit] +Description=Mark boot as indeterminate +DefaultDependencies=false +Requires=sysinit.target +After=sysinit.target +Wants=system-update-pre.target +Before=system-update-pre.target + +[Service] +Type=oneshot +ExecStart=/usr/bin/grub2-editenv - incr boot_indeterminate diff --git a/docs/grub-boot-success.service b/docs/grub-boot-success.service new file mode 100644 index 0000000000..80e79584c9 --- /dev/null +++ b/docs/grub-boot-success.service @@ -0,0 +1,6 @@ +[Unit] +Description=Mark boot as successful + +[Service] +Type=oneshot +ExecStart=/usr/sbin/grub2-set-bootflag boot_success diff --git a/docs/grub-boot-success.timer b/docs/grub-boot-success.timer new file mode 100644 index 0000000000..1d124cccc1 --- /dev/null +++ b/docs/grub-boot-success.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Mark boot as successful after the user session has run 2 minutes +ConditionUser=!@system +ConditionVirtualization=!container + +[Timer] +OnActiveSec=2min + +[Install] +WantedBy=timers.target diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi index 1276c59302..8ad5494f5c 100644 --- a/docs/grub-dev.texi +++ b/docs/grub-dev.texi @@ -1,7 +1,7 @@ \input texinfo @c -*-texinfo-*- @c %**start of header -@setfilename grub-dev.info +@setfilename grub2-dev.info @include version-dev.texi @settitle GNU GRUB Developers Manual @value{VERSION} @c Unify all our little indices for now. @@ -32,7 +32,7 @@ Invariant Sections. @dircategory Kernel @direntry -* grub-dev: (grub-dev). The GRand Unified Bootloader Dev +* grub2-dev: (grub2-dev). The GRand Unified Bootloader Dev @end direntry @setchapternewpage odd @@ -996,9 +996,9 @@ declare startup asm file ($cpu_$platform_startup) as well as any other files (e.g. init.c and callwrap.S) (e.g. $cpu_$platform = kern/$cpu/$platform/init.c). At this stage you will also need to add dummy dl.c and cache.S with functions grub_err_t grub_arch_dl_check_header (void *ehdr), grub_err_t -grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c) and -void grub_arch_sync_caches (void *address, grub_size_t len) (cache.S). They -won't be used for now. +grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c), grub_uint32_t +grub_arch_dl_min_alignment (void), and void grub_arch_sync_caches (void +*address, grub_size_t len) (cache.S). They won't be used for now. You will need to create directory include/$cpu/$platform and a file include/$cpu/types.h. The latter following this template: diff --git a/docs/grub.texi b/docs/grub.texi index a225f9a88d..d669972337 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -1,7 +1,7 @@ \input texinfo @c -*-texinfo-*- @c %**start of header -@setfilename grub.info +@setfilename grub2.info @include version.texi @settitle GNU GRUB Manual @value{VERSION} @c Unify all our little indices for now. @@ -32,15 +32,15 @@ Invariant Sections. @dircategory Kernel @direntry -* GRUB: (grub). The GRand Unified Bootloader -* grub-install: (grub)Invoking grub-install. Install GRUB on your drive -* grub-mkconfig: (grub)Invoking grub-mkconfig. Generate GRUB configuration -* grub-mkpasswd-pbkdf2: (grub)Invoking grub-mkpasswd-pbkdf2. -* grub-mkrelpath: (grub)Invoking grub-mkrelpath. -* grub-mkrescue: (grub)Invoking grub-mkrescue. Make a GRUB rescue image -* grub-mount: (grub)Invoking grub-mount. Mount a file system using GRUB -* grub-probe: (grub)Invoking grub-probe. Probe device information -* grub-script-check: (grub)Invoking grub-script-check. +* GRUB2: (grub2). The GRand Unified Bootloader +* grub2-install: (grub2)Invoking grub2-install. Install GRUB on your drive +* grub2-mkconfig: (grub2)Invoking grub2-mkconfig. Generate GRUB configuration +* grub2-mkpasswd-pbkdf2: (grub2)Invoking grub2-mkpasswd-pbkdf2. +* grub2-mkrelpath: (grub2)Invoking grub2-mkrelpath. +* grub2-mkrescue: (grub2)Invoking grub2-mkrescue. Make a GRUB rescue image +* grub2-mount: (grub2)Invoking grub2-mount. Mount a file system using GRUB +* grub2-probe: (grub2)Invoking grub2-probe. Probe device information +* grub2-script-check: (grub2)Invoking grub2-script-check. @end direntry @setchapternewpage odd @@ -223,7 +223,7 @@ surprising. @item @file{grub.cfg} is typically automatically generated by -@command{grub-mkconfig} (@pxref{Simple configuration}). This makes it +@command{grub2-mkconfig} (@pxref{Simple configuration}). This makes it easier to handle versioned kernel upgrades. @item @@ -237,7 +237,7 @@ scripting language: variables, conditionals, and loops are available. @item A small amount of persistent storage is available across reboots, using the @command{save_env} and @command{load_env} commands in GRUB and the -@command{grub-editenv} utility. This is not available in all configurations +@command{grub2-editenv} utility. This is not available in all configurations (@pxref{Environment block}). @item @@ -542,7 +542,7 @@ On OS which have device nodes similar to Unix-like OS GRUB tools use the OS name. E.g. for GNU/Linux: @example -# @kbd{grub-install /dev/sda} +# @kbd{grub2-install /dev/sda} @end example On AROS we use another syntax. For volumes: @@ -565,7 +565,7 @@ For disks we use syntax: E.g. @example -# @kbd{grub-install //:ata.device/0/0} +# @kbd{grub2-install //:ata.device/0/0} @end example On Windows we use UNC path. For volumes it's typically @@ -592,7 +592,7 @@ For disks it's E.g. @example -# @kbd{grub-install \\?\PhysicalDrive0} +# @kbd{grub2-install \\?\PhysicalDrive0} @end example Beware that you may need to further escape the backslashes depending on your @@ -602,7 +602,7 @@ When compiled with cygwin support then cygwin drive names are automatically when needed. E.g. @example -# @kbd{grub-install /dev/sda} +# @kbd{grub2-install /dev/sda} @end example @node Installation @@ -615,7 +615,7 @@ from the source tarball, or as a package for your OS. After you have done that, you need to install the boot loader on a drive (floppy or hard disk) by using the utility -@command{grub-install} (@pxref{Invoking grub-install}) on a UNIX-like OS. +@command{grub2-install} (@pxref{Invoking grub2-install}) on a UNIX-like OS. GRUB comes with boot images, which are normally put in the directory @file{/usr/lib/grub/-} (for BIOS-based machines @@ -626,22 +626,22 @@ loader needs to find them (usually @file{/boot}) will be called the @dfn{boot directory}. @menu -* Installing GRUB using grub-install:: +* Installing GRUB using grub2-install:: * Making a GRUB bootable CD-ROM:: * Device map:: * BIOS installation:: @end menu -@node Installing GRUB using grub-install -@section Installing GRUB using grub-install +@node Installing GRUB using grub2-install +@section Installing GRUB using grub2-install For information on where GRUB should be installed on PC BIOS platforms, @pxref{BIOS installation}. In order to install GRUB under a UNIX-like OS (such -as @sc{gnu}), invoke the program @command{grub-install} (@pxref{Invoking -grub-install}) as the superuser (@dfn{root}). +as @sc{gnu}), invoke the program @command{grub2-install} (@pxref{Invoking +grub2-install}) as the superuser (@dfn{root}). The usage is basically very simple. You only need to specify one argument to the program, namely, where to install the boot loader. The @@ -650,13 +650,13 @@ For example, under Linux the following will install GRUB into the MBR of the first IDE disk: @example -# @kbd{grub-install /dev/sda} +# @kbd{grub2-install /dev/sda} @end example Likewise, under GNU/Hurd, this has the same effect: @example -# @kbd{grub-install /dev/hd0} +# @kbd{grub2-install /dev/hd0} @end example But all the above examples assume that GRUB should put images under @@ -670,7 +670,7 @@ boot floppy with a filesystem. Here is an example: # @kbd{mke2fs /dev/fd0} # @kbd{mount -t ext2 /dev/fd0 /mnt} # @kbd{mkdir /mnt/boot} -# @kbd{grub-install --boot-directory=/mnt/boot /dev/fd0} +# @kbd{grub2-install --boot-directory=/mnt/boot /dev/fd0} # @kbd{umount /mnt} @end group @end example @@ -682,30 +682,37 @@ floppy instead of exposing the USB drive as a hard disk (they call it @example # @kbd{losetup /dev/loop0 /dev/sdb1} # @kbd{mount /dev/loop0 /mnt/usb} -# @kbd{grub-install --boot-directory=/mnt/usb/bugbios --force --allow-floppy /dev/loop0} +# @kbd{grub2-install --boot-directory=/mnt/usb/bugbios --force --allow-floppy /dev/loop0} @end example This install doesn't conflict with standard install as long as they are in separate directories. +Note that @command{grub2-install} is actually just a shell script and the +real task is done by other tools such as @command{grub2-mkimage}. Therefore, +you may run those commands directly to install GRUB, without using +@command{grub2-install}. Don't do that, however, unless you are very familiar +with the internals of GRUB. Installing a boot loader on a running OS may be +extremely dangerous. + On EFI systems for fixed disk install you have to mount EFI System Partition. If you mount it at @file{/boot/efi} then you don't need any special arguments: @example -# @kbd{grub-install} +# @kbd{grub2-install} @end example Otherwise you need to specify where your EFI System partition is mounted: @example -# @kbd{grub-install --efi-directory=/mnt/efi} +# @kbd{grub2-install --efi-directory=/mnt/efi} @end example For removable installs you have to use @option{--removable} and specify both @option{--boot-directory} and @option{--efi-directory}: @example -# @kbd{grub-install --efi-directory=/mnt/usb --boot-directory=/mnt/usb/boot --removable} +# @kbd{grub2-install --efi-directory=/mnt/usb --boot-directory=/mnt/usb/boot --removable} @end example @node Making a GRUB bootable CD-ROM @@ -725,10 +732,10 @@ usually also need to include a configuration file @file{grub.cfg} and some other GRUB modules. To make a simple generic GRUB rescue CD, you can use the -@command{grub-mkrescue} program (@pxref{Invoking grub-mkrescue}): +@command{grub2-mkrescue} program (@pxref{Invoking grub2-mkrescue}): @example -$ @kbd{grub-mkrescue -o grub.iso} +$ @kbd{grub2-mkrescue -o grub.iso} @end example You will often need to include other files in your image. To do this, first @@ -751,7 +758,7 @@ directory @file{iso/}. Finally, make the image: @example -$ @kbd{grub-mkrescue -o grub.iso iso} +$ @kbd{grub2-mkrescue -o grub.iso iso} @end example This produces a file named @file{grub.iso}, which then can be burned @@ -767,7 +774,7 @@ storage devices. @node Device map @section The map between BIOS drives and OS devices -If the device map file exists, the GRUB utilities (@command{grub-probe}, +If the device map file exists, the GRUB utilities (@command{grub2-probe}, etc.) read it to map BIOS drives to OS devices. This file consists of lines like this: @@ -1269,23 +1276,23 @@ need to write the whole thing by hand. @node Simple configuration @section Simple configuration handling -The program @command{grub-mkconfig} (@pxref{Invoking grub-mkconfig}) +The program @command{grub2-mkconfig} (@pxref{Invoking grub2-mkconfig}) generates @file{grub.cfg} files suitable for most cases. It is suitable for use when upgrading a distribution, and will discover available kernels and attempt to generate menu entries for them. -@command{grub-mkconfig} does have some limitations. While adding extra +@command{grub2-mkconfig} does have some limitations. While adding extra custom menu entries to the end of the list can be done by editing -@file{/etc/grub.d/40_custom} or creating @file{/boot/grub/custom.cfg}, +@file{/etc/grub.d/40_custom} or creating @file{/boot/grub2/custom.cfg}, changing the order of menu entries or changing their titles may require making complex changes to shell scripts stored in @file{/etc/grub.d/}. This may be improved in the future. In the meantime, those who feel that it would be easier to write @file{grub.cfg} directly are encouraged to do so (@pxref{Booting}, and @ref{Shell-like scripting}), and to disable any system -provided by their distribution to automatically run @command{grub-mkconfig}. +provided by their distribution to automatically run @command{grub2-mkconfig}. The file @file{/etc/default/grub} controls the operation of -@command{grub-mkconfig}. It is sourced by a shell script, and so must be +@command{grub2-mkconfig}. It is sourced by a shell script, and so must be valid POSIX shell input; normally, it will just be a sequence of @samp{KEY=value} lines, but if the value contains spaces or other special characters then it must be quoted. For example: @@ -1323,7 +1330,7 @@ works it's not recommended since titles often contain unstable device names and may be translated If you set this to @samp{saved}, then the default menu entry will be that -saved by @samp{GRUB_SAVEDEFAULT} or @command{grub-set-default}. This relies on +saved by @samp{GRUB_SAVEDEFAULT} or @command{grub2-set-default}. This relies on the environment block, which may not be available in all situations (@pxref{Environment block}). @@ -1334,7 +1341,7 @@ If this option is set to @samp{true}, then, when an entry is selected, save it as a new default entry for use by future runs of GRUB. This is only useful if @samp{GRUB_DEFAULT=saved}; it is a separate option because @samp{GRUB_DEFAULT=saved} is useful without this option, in conjunction with -@command{grub-set-default}. Unset by default. +@command{grub2-set-default}. Unset by default. This option relies on the environment block, which may not be available in all situations (@pxref{Environment block}). @@ -1481,15 +1488,15 @@ intel-uc.img intel-ucode.img amd-uc.img amd-ucode.img early_ucode.cpio microcode @end example @item GRUB_DISABLE_LINUX_UUID -Normally, @command{grub-mkconfig} will generate menu entries that use +Normally, @command{grub2-mkconfig} will generate menu entries that use universally-unique identifiers (UUIDs) to identify the root filesystem to the Linux kernel, using a @samp{root=UUID=...} kernel parameter. This is usually more reliable, but in some cases it may not be appropriate. To disable the use of UUIDs, set this option to @samp{true}. @item GRUB_DISABLE_LINUX_PARTUUID -If @command{grub-mkconfig} cannot identify the root filesystem via its -universally-unique indentifier (UUID), @command{grub-mkconfig} can use the UUID +If @command{grub2-mkconfig} cannot identify the root filesystem via its +universally-unique indentifier (UUID), @command{grub2-mkconfig} can use the UUID of the partition containing the filesystem to identify the root filesystem to the Linux kernel via a @samp{root=PARTUUID=...} kernel parameter. This is not as reliable as using the filesystem UUID, but is more reliable than using the @@ -1503,7 +1510,7 @@ If this option is set to @samp{true}, disable the generation of recovery mode menu entries. @item GRUB_DISABLE_UUID -Normally, @command{grub-mkconfig} will generate menu entries that use +Normally, @command{grub2-mkconfig} will generate menu entries that use universally-unique identifiers (UUIDs) to identify various filesystems to search for files. This is usually more reliable, but in some cases it may not be appropriate. To disable this use of UUIDs, set this option to @@ -1514,12 +1521,12 @@ not be appropriate. To disable this use of UUIDs, set this option to @item GRUB_VIDEO_BACKEND If graphical video support is required, either because the @samp{gfxterm} graphical terminal is in use or because @samp{GRUB_GFXPAYLOAD_LINUX} is set, -then @command{grub-mkconfig} will normally load all available GRUB video +then @command{grub2-mkconfig} will normally load all available GRUB video drivers and use the one most appropriate for your hardware. If you need to override this for some reason, then you can set this option. -After @command{grub-install} has been run, the available video drivers are -listed in @file{/boot/grub/video.lst}. +After @command{grub2-install} has been run, the available video drivers are +listed in @file{/boot/grub2/video.lst}. @item GRUB_GFXMODE Set the resolution used on the @samp{gfxterm} graphical terminal. Note that @@ -1552,20 +1559,17 @@ boot sequence. If you have problems, set this option to @samp{text} and GRUB will tell Linux to boot in normal text mode. @item GRUB_DISABLE_OS_PROBER -The @command{grub-mkconfig} has a feature to use the external -@command{os-prober} program to discover other operating systems installed on -the same machine and generate appropriate menu entries for them. It is disabled -by default since automatic and silent execution of @command{os-prober}, and -creating boot entries based on that data, is a potential attack vector. Set -this option to @samp{false} to enable this feature in the -@command{grub-mkconfig} command. +Normally, @command{grub2-mkconfig} will try to use the external +@command{os-prober} program, if installed, to discover other operating +systems installed on the same system and generate appropriate menu entries +for them. Set this option to @samp{true} to disable this. @item GRUB_OS_PROBER_SKIP_LIST List of space-separated FS UUIDs of filesystems to be ignored from os-prober output. For efi chainloaders it's @@ @item GRUB_DISABLE_SUBMENU -Normally, @command{grub-mkconfig} will generate top level menu entry for +Normally, @command{grub2-mkconfig} will generate top level menu entry for the kernel with highest version number and put all other found kernels or alternative menu entries for recovery mode in submenu. For entries returned by @command{os-prober} first entry will be put on top level and all others @@ -1573,11 +1577,11 @@ in submenu. If this option is set to @samp{true}, flat menu with all entries on top level will be generated instead. Changing this option will require changing existing values of @samp{GRUB_DEFAULT}, @samp{fallback} (@pxref{fallback}) and @samp{default} (@pxref{default}) environment variables as well as saved -default entry using @command{grub-set-default} and value used with -@command{grub-reboot}. +default entry using @command{grub2-set-default} and value used with +@command{grub2-reboot}. @item GRUB_ENABLE_CRYPTODISK -If set to @samp{y}, @command{grub-mkconfig} and @command{grub-install} will +If set to @samp{y}, @command{grub2-mkconfig} and @command{grub2-install} will check for encrypted disks and generate additional commands needed to access them during boot. Note that in this case unattended boot is not possible because GRUB will wait for passphrase to unlock encrypted container. @@ -1636,7 +1640,7 @@ confusing @samp{GRUB_TIMEOUT_STYLE=countdown} or @end table -For more detailed customisation of @command{grub-mkconfig}'s output, you may +For more detailed customisation of @command{grub2-mkconfig}'s output, you may edit the scripts in @file{/etc/grub.d} directly. @file{/etc/grub.d/40_custom} is particularly useful for adding entire custom menu entries; simply type the menu entries you want to add at the end of @@ -1644,14 +1648,14 @@ that file, making sure to leave at least the first two lines intact. @node Root Identifcation Heuristics @section Root Identifcation Heuristics -If the target operating system uses the Linux kernel, @command{grub-mkconfig} +If the target operating system uses the Linux kernel, @command{grub2-mkconfig} attempts to identify the root file system via a heuristic algoirthm. This algorithm selects the identification method of the root file system by considering three factors. The first is if an initrd for the target operating system is also present. The second is @samp{GRUB_DISABLE_LINUX_UUID} and if set -to @samp{true}, prevents @command{grub-mkconfig} from identifying the root file +to @samp{true}, prevents @command{grub2-mkconfig} from identifying the root file system by its UUID. The third is @samp{GRUB_DISABLE_LINUX_PARTUUID} and if set -to @samp{true}, prevents @command{grub-mkconfig} from identifying the root file +to @samp{true}, prevents @command{grub2-mkconfig} from identifying the root file system via the UUID of its enclosing partition. If the variables are assigned any other value, that value is considered equivalent to @samp{false}. The variables are also considered to be set to @samp{false} if they are not set. @@ -1661,18 +1665,18 @@ filesystem to the initrd. Most initrd images determine the root file system by checking the Linux kernel's command-line for the @samp{root} key and use its value as the identification method of the root file system. To improve the reliability of booting, most initrd images also allow the root file system to be -identified by its UUID. Because of this behavior, the @command{grub-mkconfig} +identified by its UUID. Because of this behavior, the @command{grub2-mkconfig} command will set @samp{root} to @samp{root=UUID=...} to provide the initrd with the filesystem UUID of the root file system. If no initrd is detected or @samp{GRUB_DISABLE_LINUX_UUID} is set to @samp{true} -then @command{grub-command} will identify the root filesystem by setting the +then @command{grub2-command} will identify the root filesystem by setting the kernel command-line variable @samp{root} to @samp{root=PARTUUID=...} unless @samp{GRUB_DISABLE_LINUX_PARTUUID} is also set to @samp{true}. If @samp{GRUB_DISABLE_LINUX_PARTUUID} is also set to @samp{true}, -@command{grub-command} will identify by its Linux device name. +@command{grub2-command} will identify by its Linux device name. -The following table summarizes the behavior of the @command{grub-mkconfig} +The following table summarizes the behavior of the @command{grub2-mkconfig} command. @multitable {detected} {GRUB_DISABLE_LINUX_PARTUUID} {GRUB_DISABLE_LINUX_UUID} {Linux Root} @@ -1889,9 +1893,10 @@ than zero; otherwise 0. @section Multi-boot manual config Currently autogenerating config files for multi-boot environments depends on -os-prober and has several shortcomings. Due to that it is disabled by default. -It is advised to use the power of GRUB syntax and do it yourself. A possible -configuration is detailed here, feel free to adjust to your needs. +os-prober and has several shortcomings. While fixing it is scheduled for the +next release, meanwhile you can make use of the power of GRUB syntax and do it +yourself. A possible configuration is detailed here, feel free to adjust to your +needs. First create a separate GRUB partition, big enough to hold GRUB. Some of the following entries show how to load OS installer images from this same partition, @@ -1900,7 +1905,7 @@ images as well. Mount this partition on/mnt/boot and disable GRUB in all OSes and manually install self-compiled latest GRUB with: -@code{grub-install --boot-directory=/mnt/boot /dev/sda} +@code{grub2-install --boot-directory=/mnt/boot /dev/sda} In all the OSes install GRUB tools but disable installing GRUB in bootsector, so you'll have menu.lst and grub.cfg available for use. Also disable os-prober @@ -1910,20 +1915,20 @@ use by setting: in /etc/default/grub -Then write a grub.cfg (/mnt/boot/grub/grub.cfg): +Then write a grub.cfg (/mnt/boot/grub2/grub.cfg): @example menuentry "OS using grub2" @{ insmod xfs search --set=root --label OS1 --hint hd0,msdos8 - configfile /boot/grub/grub.cfg + configfile /boot/grub2/grub.cfg @} menuentry "OS using grub2-legacy" @{ insmod ext2 search --set=root --label OS2 --hint hd0,msdos6 - legacy_configfile /boot/grub/menu.lst + legacy_configfile /boot/grub2/menu.lst @} menuentry "Windows XP" @{ @@ -1986,15 +1991,15 @@ GRUB supports embedding a configuration file directly into the core image, so that it is loaded before entering normal mode. This is useful, for example, when it is not straightforward to find the real configuration file, or when you need to debug problems with loading that file. -@command{grub-install} uses this feature when it is not using BIOS disk +@command{grub2-install} uses this feature when it is not using BIOS disk functions or when installing to a different disk from the one containing @file{/boot/grub}, in which case it needs to use the @command{search} command (@pxref{search}) to find @file{/boot/grub}. To embed a configuration file, use the @option{-c} option to -@command{grub-mkimage}. The file is copied into the core image, so it may +@command{grub2-mkimage}. The file is copied into the core image, so it may reside anywhere on the file system, and may be removed after running -@command{grub-mkimage}. +@command{grub2-mkimage}. After the embedded configuration file (if any) is executed, GRUB will load the @samp{normal} module (@pxref{normal}), which will then read the real @@ -2029,13 +2034,13 @@ included in the core image: @example @group search.fs_label grub root -if [ -e /boot/grub/example/test1.cfg ]; then +if [ -e /boot/grub2/example/test1.cfg ]; then set prefix=($root)/boot/grub - configfile /boot/grub/example/test1.cfg + configfile /boot/grub2/example/test1.cfg else - if [ -e /boot/grub/example/test2.cfg ]; then + if [ -e /boot/grub2/example/test2.cfg ]; then set prefix=($root)/boot/grub - configfile /boot/grub/example/test2.cfg + configfile /boot/grub2/example/test2.cfg else echo "Could not find an example configuration file!" fi @@ -2559,7 +2564,7 @@ grub-mknetdir --net-directory=/srv/tftp --subdir=/boot/grub -d /usr/lib/grub/i38 @end group @end example -Then follow instructions printed out by grub-mknetdir on configuring your DHCP +Then follow instructions printed out by grub2-mknetdir on configuring your DHCP server. The grub.cfg file is placed in the same directory as the path output by @@ -2762,7 +2767,7 @@ team are: @end table To take full advantage of this function, install GRUB into the MBR -(@pxref{Installing GRUB using grub-install}). +(@pxref{Installing GRUB using grub2-install}). If you have a laptop which has a similar feature and not in the above list could you figure your address and contribute? @@ -2823,7 +2828,7 @@ bytes. The sole function of @file{boot.img} is to read the first sector of the core image from a local disk and jump to it. Because of the size restriction, @file{boot.img} cannot understand any file system structure, so -@command{grub-install} hardcodes the location of the first sector of the +@command{grub2-install} hardcodes the location of the first sector of the core image into @file{boot.img} when installing GRUB. @item diskboot.img @@ -2853,7 +2858,7 @@ images. @item core.img This is the core image of GRUB. It is built dynamically from the kernel -image and an arbitrary list of modules by the @command{grub-mkimage} +image and an arbitrary list of modules by the @command{grub2-mkimage} program. Usually, it contains enough modules to access @file{/boot/grub}, and loads everything else (including menu handling, the ability to load target operating systems, and so on) from the file system at run-time. The @@ -2905,7 +2910,7 @@ GRUB 2 has no single Stage 2 image. Instead, it loads modules from In GRUB 2, images for booting from CD-ROM drives are now constructed using @file{cdboot.img} and @file{core.img}, making sure that the core image contains the @samp{iso9660} module. It is usually best to use the -@command{grub-mkrescue} program for this. +@command{grub2-mkrescue} program for this. @item nbgrub There is as yet no equivalent for @file{nbgrub} in GRUB 2; it was used by @@ -3073,8 +3078,8 @@ There are two ways to specify files, by @dfn{absolute file name} and by An absolute file name resembles a Unix absolute file name, using @samp{/} for the directory separator (not @samp{\} as in DOS). One -example is @samp{(hd0,1)/boot/grub/grub.cfg}. This means the file -@file{/boot/grub/grub.cfg} in the first partition of the first hard +example is @samp{(hd0,1)/boot/grub2/grub.cfg}. This means the file +@file{/boot/grub2/grub.cfg} in the first partition of the first hard disk. If you omit the device name in an absolute file name, GRUB uses GRUB's @dfn{root device} implicitly. So if you set the root device to, say, @samp{(hd1,1)} by the command @samp{set root=(hd1,1)} (@pxref{set}), @@ -3082,8 +3087,8 @@ then @code{/boot/kernel} is the same as @code{(hd1,1)/boot/kernel}. On ZFS filesystem the first path component must be @var{volume}@samp{@@}[@var{snapshot}]. -So @samp{/rootvol@@snap-129/boot/grub/grub.cfg} refers to file -@samp{/boot/grub/grub.cfg} in snapshot of volume @samp{rootvol} with name +So @samp{/rootvol@@snap-129/boot/grub2/grub.cfg} refers to file +@samp{/boot/grub2/grub.cfg} in snapshot of volume @samp{rootvol} with name @samp{snap-129}. Trailing @samp{@@} after volume name is mandatory even if snapshot name is omitted. @@ -3260,6 +3265,7 @@ GRUB. Others may be used freely in GRUB configuration files. @menu * Special environment variables:: * Environment block:: +* Special environment block variables:: @end menu @@ -3270,6 +3276,8 @@ These variables have special meaning to GRUB. @menu * biosnum:: +* blsuki_save_default:: +* check_appended_signatures:: * check_signatures:: * chosen:: * cmdpath:: @@ -3333,12 +3341,24 @@ this. For an alternative approach which also changes BIOS drive mappings for the chain-loaded system, @pxref{drivemap}. +@node blsuki_save_default +@subsection blsuki_save_default + +If this variable is set, menu entries generated from BLS config files +(@pxref{blscfg}) or UKI files (@pxref{uki}) will be set as the default boot +entry when selected. + +@node check_appended_signatures +@subsection check_appended_signatures + +This variable controls whether GRUB enforces appended signature validation on +certain loaded files. @xref{Using appended signatures}. @node check_signatures @subsection check_signatures -This variable controls whether GRUB enforces digital signature -validation on loaded files. @xref{Using digital signatures}. +This variable controls whether GRUB enforces GPG-style digital signature +validation on loaded files. @xref{Using GPG-style digital signatures}. @node chosen @subsection chosen @@ -3502,7 +3522,7 @@ The more recent release of Minix would then be identified as @samp{other>minix>minix-3.4.0}. This variable is often set by @samp{GRUB_DEFAULT} (@pxref{Simple -configuration}), @command{grub-set-default}, or @command{grub-reboot}. +configuration}), @command{grub2-set-default}, or @command{grub2-reboot}. @node fallback @@ -3592,7 +3612,7 @@ If this variable is set, it names the language code that the example, French would be named as @samp{fr}, and Simplified Chinese as @samp{zh_CN}. -@command{grub-mkconfig} (@pxref{Simple configuration}) will try to set a +@command{grub2-mkconfig} (@pxref{Simple configuration}) will try to set a reasonable default for this variable based on the system locale. @@ -3600,10 +3620,10 @@ reasonable default for this variable based on the system locale. @subsection locale_dir If this variable is set, it names the directory where translation files may -be found (@pxref{gettext}), usually @file{/boot/grub/locale}. Otherwise, +be found (@pxref{gettext}), usually @file{/boot/grub2/locale}. Otherwise, internationalization is disabled. -@command{grub-mkconfig} (@pxref{Simple configuration}) will set a reasonable +@command{grub2-mkconfig} (@pxref{Simple configuration}) will set a reasonable default for this variable if internationalization is needed and any translation files are available. @@ -3740,7 +3760,7 @@ input. The default is not to pause output. The location of the @samp{/boot/grub} directory as an absolute file name (@pxref{File name syntax}). This is normally set by GRUB at startup based -on information provided by @command{grub-install}. GRUB modules are +on information provided by @command{grub2-install}. GRUB modules are dynamically loaded from this directory, so it must be set correctly in order for many parts of GRUB to work. @@ -3852,19 +3872,73 @@ GRUB provides an ``environment block'' which can be used to save a small amount of state. The environment block is a preallocated 1024-byte file, which normally lives -in @file{/boot/grub/grubenv} (although you should not assume this). At boot +in @file{/boot/grub2/grubenv} (although you should not assume this). At boot time, the @command{load_env} command (@pxref{load_env}) loads environment variables from it, and the @command{save_env} (@pxref{save_env}) command saves environment variables to it. From a running system, the -@command{grub-editenv} utility can be used to edit the environment block. +@command{grub2-editenv} utility can be used to edit the environment block. For safety reasons, this storage is only available when installed on a plain disk (no LVM or RAID), using a non-checksumming filesystem (no ZFS), and using BIOS or EFI functions (no ATA, USB or IEEE1275). -@command{grub-mkconfig} uses this facility to implement +On Btrfs filesystems, a reserved area in the filesystem header may be used to +store the environment block. This static block avoids the problems of updating +a normal file on a copy-on-write filesystem, where writing raw block is not +stable and requires metadata update. The reserved area provides a fixed +location that GRUB can update directly, allowing commands such as +@command{grub-reboot} and @samp{GRUB_SAVEDEFAULT} to function correctly on +Btrfs volumes. + +@command{grub2-mkconfig} uses this facility to implement @samp{GRUB_SAVEDEFAULT} (@pxref{Simple configuration}). +@node Special environment block variables +@section Special environment block variables + +These special variables are usually written to the environment block +(@pxref{Environment block}) to customize the behavior of @file{grub.cfg} +generated by @command{grub-mkconfig}. + +@menu +* saved_entry:: +* next_entry:: +* env_block:: +@end menu + +@node saved_entry +@subsection saved_entry + +The @var{saved_entry} variable sets the default boot entry in @file{grub.cfg} +created by @command{grub-mkconfig}. It can be set with +@command{grub-set-default} to choose a default entry, or at runtime with the +@code{savedefault} function in grub.cfg to save the current entry as the new +default. This may require write access by GRUB. + +@node next_entry +@subsection next_entry + +The @var{next_entry} variable sets the boot entry for the next boot only. After +it is used, GRUB clears the value so it is not reused. This requires write +access to the environment block (@pxref{Environment block}) at runtime. The +@command{grub-reboot} command is usually used instead of changing this variable +directly. + +@node env_block +@subsection env_block + +If the filesystem is Btrfs and the disk is not an abstracted device such as +LVM, RAID, or encryption, the reserved space in the Btrfs header can be used as +the environment block (@pxref{Environment block}). This provides a fixed raw +block that GRUB can reliably write to. The @var{env_block} records this +location in GRUB blocklist syntax (@pxref{Block list syntax}) so that +@command{grub-editenv} and @file{grub.cfg} know how to access and use the +external raw block. + +This variable is initialized when @file{grubenv} is first created by +@command{grub-editenv} and is treated as read-only to avoid being overwritten +with an unpredictable value. + @node Modules @chapter Modules @@ -4342,6 +4416,7 @@ you forget a command, you can run the command @command{help} * background_image:: Load background image for active terminal * badram:: Filter out bad regions of RAM * blocklist:: Print a block list +* blscfg:: Load Boot Loader Specification menu entries * boot:: Start up your operating system * cat:: Show the contents of a file * clear:: Clear the screen @@ -4352,11 +4427,13 @@ you forget a command, you can run the command @command{help} * configfile:: Load a configuration file * cpuid:: Check for CPU features * crc:: Compute or check CRC32 checksums +* cryptocheck:: Check if a device is encrypted * cryptomount:: Mount a crypto device * cutmem:: Remove memory regions * date:: Display or set current date and time * devicetree:: Load a device tree blob * distrust:: Remove a pubkey from trusted keys +* distrust_certificate:: Remove a certificate from the list of trusted certificates * drivemap:: Map a drive to another * echo:: Display a line of text * efitextmode:: Set/Get text output mode resolution @@ -4373,6 +4450,7 @@ you forget a command, you can run the command @command{help} * hexdump:: Show raw contents of a file or memory * insmod:: Insert a module * keystatus:: Check key modifier status +* list_certificates:: List trusted certificates * list_env:: List variables in environment block * list_trusted:: List trusted public keys * load_env:: Load variables from environment block @@ -4411,8 +4489,11 @@ you forget a command, you can run the command @command{help} * test:: Check file types and compare values * true:: Do nothing, successfully * trust:: Add public key to list of trusted keys +* trust_certificate:: Add an x509 certificate to the list of trusted certificates +* uki:: Load Unified Kernel Image menu entries * unset:: Unset an environment variable @comment * vbeinfo:: List available video modes +* verify_appended:: Verify appended digital signature * verify_detached:: Verify detached digital signature * videoinfo:: List available video modes * wrmsr:: Write values to model-specific registers @@ -4524,6 +4605,72 @@ Print a block list (@pxref{Block list syntax}) for @var{file}. @end deffn +@node blscfg +@subsection blscfg + +@deffn Command blscfg [@option{-p|--path} dir] [@option{-f|--enable-fallback}] [@option{-d|--show-default}] [@option{-n|--show-non-default}] [@option{-e|--entry} file] +Load Boot Loader Specification (BLS) entries into the GRUB menu. Boot entries +generated from @command{blscfg} won't interfere with entries from @file{grub.cfg} appearing in +the GRUB menu. Also, entries generated from @command{blscfg} exists only in memory and +don't update @file{grub.cfg}. + +By default, the BLS entries are stored in the @file{/loader/entries} directory in the +boot partition. If BLS entries are stored elsewhere, the @option{--path} option can be +used to check a different directory instead of the default location. If no BLS +entries are found while using the @option{--path} option, the @option{--enable-fallback} option +can be used to check for entries in the default location. + +The @option{--show-default} option allows the default boot entry to be added to the +GRUB menu from the BLS entries. + +The @option{--show-non-default} option allows non-default boot entries to be added to +the GRUB menu from the BLS entries. + +The @option{--entry} option allows specific boot entries to be added to the GRUB menu +from the BLS entries. + +The @option{--entry}, @option{--show-default}, and @option{--show-non-default} options +are used to filter which BLS entries are added to the GRUB menu. If none are +used, all entries in the default location or the location specified by @option{--path} +will be added to the GRUB menu. + +A BLS config file example: +@example +# /boot/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf +title Fedora 19 (Rawhide) +sort-key fedora +machine-id 6a9857a393724b7a981ebb5b8495b9ea +version 3.8.0-2.fc19.x86_64 +options root=UUID=6d3376e4-fc93-4509-95ec-a21d68011da2 quiet +architecture x64 +linux /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux +initrd /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/initrd +@end example + +For more information on BLS entry keys as well as other information on BLS, +see: @uref{https://uapi-group.org/specifications/specs/boot_loader_specification/, The Boot Loader Specification}. For the GRUB, there are a few additional +BLS entry keys based on the @command{menuentry} command (@pxref{menuentry}). + +The @code{grub_class} key may be used any number of times to group menu entries into +classes. Menu themes may display different classes using different styles. + +The @code{grub_users} key grants specific users access to specific menu +entries. @xref{Security}. + +The @code{grub_hotkey} key associates a hotkey with a menu entry. +@var{key} may be a single letter, or one of the aliases @samp{backspace}, +@samp{tab}, or @samp{delete}. + +The @code{grub_args} key can be used for any other argument to be passed as positonal +parameters when the list of commands generated from the BLS config file are +executed. + +Variable expansion using the @samp{$} character (@xref{Shell-like scripting}) may be +used with BLS config files for the GRUB but might not be compatible with other +bootloaders. +@end deffn + + @node boot @subsection boot @@ -4657,6 +4804,18 @@ Alias for @code{hashsum --hash crc32 arg @dots{}}. See command @command{hashsum} (@pxref{hashsum}) for full description. @end deffn +@node cryptocheck +@subsection cryptocheck + +@deffn Command cryptocheck [ @option{--quiet} ] device +Check if a given diskfilter device is backed by encrypted devices +(@pxref{cryptomount} for additional information). + +The command examines all backing devices, physical volumes, of a specified +logical volume, like LVM2, and fails when at least one of them is unencrypted. + +The option @option{--quiet} can be given to suppress the output. +@end deffn @node cryptomount @subsection cryptomount @@ -4693,6 +4852,11 @@ namespace in addition to the cryptodisk namespace. Support for plain encryption mode (plain dm-crypt) is provided via separate @command{@pxref{plainmount}} command. + +On the EFI platform, GRUB tries to erase master keys from memory when the cryptodisk +module is unloaded or the command @command{exit} is executed. All secrets remain in +memory when the command @command{chainloader} is issued, because execution can +return to GRUB on the EFI platform. @end deffn @node cutmem @@ -4752,9 +4916,28 @@ These keys are used to validate signatures when environment variable @code{check_signatures} is set to @code{enforce} (@pxref{check_signatures}), and by some invocations of @command{verify_detached} (@pxref{verify_detached}). @xref{Using -digital signatures}, for more information. +GPG-style digital signatures}, for more information. @end deffn + +@node distrust_certificate +@subsection distrust_certificate + +@deffn Command distrust_certificate cert_number +Remove the x509 certificate numbered @var{cert_number} from GRUB's keyring of +trusted x509 certificates for verifying appended signatures. + +@var{cert_number} is the certificate number as listed by +@command{list_certificates} (@pxref{list_certificates}). + +These certificates are used to validate appended signatures when environment +variable @code{check_appended_signatures} is set to @code{enforce} or +@code{forced} (@pxref{check_appended_signatures}), and by +@command{verify_appended} (@pxref{verify_appended}). See +@xref{Using appended signatures} for more information. +@end deffn + + @node drivemap @subsection drivemap @@ -4924,7 +5107,7 @@ Translate @var{string} into the current language. The current language code is stored in the @samp{lang} variable in GRUB's environment (@pxref{lang}). Translation files in MO format are read from -@samp{locale_dir} (@pxref{locale_dir}), usually @file{/boot/grub/locale}. +@samp{locale_dir} (@pxref{locale_dir}), usually @file{/boot/grub2/locale}. @end deffn @@ -5031,6 +5214,21 @@ only if checking key modifier status is supported. @end deffn +@node list_certificates +@subsection list_certificates + +@deffn Command list_certificates +List all x509 certificates trusted by GRUB for validating appended signatures. +The output is a numbered list of certificates, showing the certificate's serial +number and Common Name. + +The certificate number can be used as an argument to +@command{distrust_certificate} (@pxref{distrust_certificate}). + +See @xref{Using appended signatures} for more information. +@end deffn + + @node list_env @subsection list_env @@ -5050,7 +5248,7 @@ The output is in GPG's v4 key fingerprint format (i.e., the output of @code{gpg --fingerprint}). The least significant four bytes (last eight hexadecimal digits) can be used as an argument to @command{distrust} (@pxref{distrust}). -@xref{Using digital signatures}, for more information about uses for +@xref{Using GPG-style digital signatures}, for more information about uses for these keys. @end deffn @@ -5085,8 +5283,13 @@ When used with care, @option{--skip-sig} and the whitelist enable an administrator to configure a system to boot only signed configurations, but to allow the user to select from among multiple configurations, and to enable ``one-shot'' boot attempts and -``savedefault'' behavior. @xref{Using digital signatures}, for more +``savedefault'' behavior. @xref{Using GPG-style digital signatures}, for more information. + +Extra care should be taken when combining this command with appended signatures +(@pxref{Using appended signatures}), as this file is not validated by an +appended signature and could set @code{check_appended_signatures=no} if GRUB is +not in @pxref{Lockdown} mode. @end deffn @@ -5276,7 +5479,7 @@ Define a user named @var{user} with password @var{clear-password}. @deffn Command password_pbkdf2 user hashed-password Define a user named @var{user} with password hash @var{hashed-password}. -Use @command{grub-mkpasswd-pbkdf2} (@pxref{Invoking grub-mkpasswd-pbkdf2}) +Use @command{grub2-mkpasswd-pbkdf2} (@pxref{Invoking grub2-mkpasswd-pbkdf2}) to generate password hashes. @xref{Security}. @end deffn @@ -5457,7 +5660,7 @@ read. It is possible to modify a digitally signed environment block file from within GRUB using this command, such that its signature will no longer be valid on subsequent boots. Care should be taken in such advanced configurations to avoid rendering the system -unbootable. @xref{Using digital signatures}, for more information. +unbootable. @xref{Using GPG-style digital signatures}, for more information. @end deffn @@ -5466,7 +5669,8 @@ unbootable. @xref{Using digital signatures}, for more information. @deffn Command search @ [@option{--file}|@option{--label}|@option{--fs-uuid}] @ - [@option{--set} [var]] [@option{--no-floppy}|@option{--efidisk-only}] name + [@option{--set} [var]] [@option{--no-floppy}|@option{--efidisk-only}|@option{--cryptodisk-only}] @ + name Search devices by file (@option{-f}, @option{--file}), filesystem label (@option{-l}, @option{--label}), or filesystem UUID (@option{-u}, @option{--fs-uuid}). @@ -5481,6 +5685,14 @@ devices, which can be slow. The (@option{--efidisk-only}) option prevents searching any other devices then EFI disks. This is typically used when chainloading to local EFI partition. +The (@option{--cryptodisk-only}) option prevents searching any devices other +than encrypted disks. This is typically used when booting from an encrypted +file system to ensure that no code gets executed from an unencrypted device +having the same filesystem UUID or label. + +This option implicitly invokes the command @command{cryptocheck}, if it is +available (@pxref{cryptocheck} for additional information). + The @samp{search.file}, @samp{search.fs_label}, and @samp{search.fs_uuid} commands are aliases for @samp{search --file}, @samp{search --label}, and @samp{search --fs-uuid} respectively. @@ -5873,11 +6085,32 @@ signatures when environment variable @code{check_signatures} is set to must itself be properly signed. The @option{--skip-sig} option can be used to disable signature-checking when reading @var{pubkey_file} itself. It is expected that @option{--skip-sig} is useful for testing -and manual booting. @xref{Using digital signatures}, for more +and manual booting. @xref{Using GPG-style digital signatures}, for more information. @end deffn +@node trust_certificate +@subsection trust_certificate + +@deffn Command trust_certificate x509_certificate +Read an DER-formatted x509 certificate from the file @var{x509_certificate} +and add it to GRUB's internal list of trusted x509 certificates. These +certificates are used to validate appended signatures when the environment +variable @code{check_appended_signatures} is set to @code{enforce} or +@code{forced}. + +Note that if @code{check_appended_signatures} is set to @code{enforce} or +@code{forced} when @command{trust_certificate} is executed, then +@var{x509_certificate} must itself bear an appended signature. (It is not +sufficient that @var{x509_certificate} be signed by a trusted certificate +according to the x509 rules: grub does not include support for validating +signatures within x509 certificates themselves.) + +See @xref{Using appended signatures} for more information. +@end deffn + + @node unset @subsection unset @@ -5886,6 +6119,38 @@ Unset the environment variable @var{envvar}. @end deffn +@node uki +@subsection uki + +@deffn Command uki [@option{-p|--path} dir] [@option{-f|--enable-fallback}] [@option{-d|--show-default}] [@option{-n|--show-non-default}] [@option{-e|--entry} file] +Load Unified Kernel Image (UKI) files into the GRUB menu. Boot entries +generated from @command{uki} won't interfere with entries from @file{grub.cfg} appearing in the +GRUB menu. Also, entries generated from @command{uki} exists only in memory and don't +update @file{grub.cfg}. + +By default, the UKI files are stored in the @file{/EFI/Linux} directory in the EFI +system partition. If UKI files are stored elsewhere, the @option{--path} option can be +used to check a different directory instead of the default location. If no UKI +files are found while using the @option{--path} option, the @option{--enable-fallback} option can +be used to check for files in the default location. + +The @option{--show-default} option allows the default boot entry to be added to the +GRUB menu from the UKI files. + +The @option{--show-non-default} option allows non-default boot entries to be added to +the GRUB menu from the UKI files. + +The @option{--entry} option allows specific boot entries to be added to the GRUB menu +from the UKI files. + +The @option{--entry}, @option{--show-default}, and @option{--show-non-default} options +are used to filter which UKI files are added to the GRUB menu. If none are +used, all files in the default location or the location specified by @option{--path} +will be added to the GRUB menu. + +For more information on UKI, see: @uref{https://uapi-group.org/specifications/specs/unified_kernel_image/, The Unified Kernel Image Specification} +@end deffn + @ignore @node vbeinfo @subsection vbeinfo @@ -5896,6 +6161,18 @@ only on PC BIOS platforms. @end deffn @end ignore +@node verify_appended +@subsection verify_appended + +@deffn Command verify_appended file +Verifies an appended signature on @var{file} against the trusted certificates +known to GRUB (See @pxref{list_certificates}, @pxref{trust_certificate}, and +@pxref{distrust_certificate}). + +Exit code @code{$?} is set to 0 if the signature validates +successfully. If validation fails, it is set to a non-zero value. +See @xref{Using appended signatures}, for more information. +@end deffn @node verify_detached @subsection verify_detached @@ -5914,7 +6191,7 @@ tried. Exit code @code{$?} is set to 0 if the signature validates successfully. If validation fails, it is set to a non-zero value. -@xref{Using digital signatures}, for more information. +@xref{Using GPG-style digital signatures}, for more information. @end deffn @node videoinfo @@ -5950,6 +6227,7 @@ Note: The command is not allowed when lockdown is enforced (@pxref{Lockdown}). * net_add_dns:: Add a DNS server * net_add_route:: Add routing entry * net_bootp:: Perform a bootp/DHCP autoconfiguration +* net_bootp6:: Perform a DHCPv6 autoconfiguration * net_del_addr:: Remove IP address from interface * net_del_dns:: Remove a DNS server * net_del_route:: Remove a route entry @@ -6075,6 +6353,22 @@ Sets environment variable @samp{net_}@var{}@samp{_boot_file} @end deffn +@node net_bootp6 +@subsection net_bootp6 + +@deffn Command net_bootp6 [@var{card}] +Perform configuration of @var{card} using DHCPv6 protocol. If no card name is +specified, try to configure all existing cards. If configuration was +successful, interface with name @var{card}@samp{:dhcp6} and configured address +is added to @var{card}. + +@table @samp +@item 1 (Domain Name Server) +Adds all servers from option value to the list of servers used during name +resolution. +@end table + +@end deffn @node net_get_dhcp_option @subsection net_get_dhcp_option @@ -6375,12 +6669,14 @@ environment variables and commands are listed in the same order. @chapter Security @menu -* Authentication and authorisation:: Users and access control -* Using digital signatures:: Booting digitally signed code -* UEFI secure boot and shim:: Booting digitally signed PE files -* Secure Boot Advanced Targeting:: Embedded information for generation number based revocation -* Measured Boot:: Measuring boot components -* Lockdown:: Lockdown when booting on a secure setup +* Authentication and authorisation:: Users and access control +* Using GPG-style digital signatures:: Booting digitally signed code +* Using appended signatures:: An alternative approach to booting digitally signed code +* UEFI secure boot and shim:: Booting digitally signed PE files +* Secure Boot Advanced Targeting:: Embedded information for generation number based revocation +* Measured Boot:: Measuring boot components +* Lockdown:: Lockdown when booting on a secure setup +* Signing GRUB itself:: Ensuring the integrity of the GRUB core image @end menu @node Authentication and authorisation @@ -6402,8 +6698,8 @@ The @samp{password} (@pxref{password}) and @samp{password_pbkdf2} which has an associated password. @samp{password} sets the password in plain text, requiring @file{grub.cfg} to be secure; @samp{password_pbkdf2} sets the password hashed using the Password-Based Key Derivation Function -(RFC 2898), requiring the use of @command{grub-mkpasswd-pbkdf2} -(@pxref{Invoking grub-mkpasswd-pbkdf2}) to generate password hashes. +(RFC 2898), requiring the use of @command{grub2-mkpasswd-pbkdf2} +(@pxref{Invoking grub2-mkpasswd-pbkdf2}) to generate password hashes. In order to enable authentication support, the @samp{superusers} environment variable must be set to a list of usernames, separated by any of spaces, @@ -6412,8 +6708,10 @@ the GRUB command line, edit menu entries, and execute any menu entry. If @samp{superusers} is set, then use of the command line and editing of menu entries are automatically restricted to superusers. Setting @samp{superusers} to empty string effectively disables both access to CLI and editing of menu -entries. Note: The environment variable needs to be exported to also affect -the section defined by the @samp{submenu} command (@pxref{submenu}). +entries. Building a grub image with @samp{--disable-cli} option will also +disable access to CLI and editing of menu entries, as well as disabling rescue +mode. Note: The environment variable needs to be exported to also affect the +section defined by the @samp{submenu} command (@pxref{submenu}). Other users may be allowed to execute specific menu entries by giving a list of usernames (as above) using the @option{--users} option to the @@ -6448,18 +6746,18 @@ menuentry "May be run by user1 or a superuser" --users user1 @{ @end group @end example -The @command{grub-mkconfig} program does not yet have built-in support for +The @command{grub2-mkconfig} program does not yet have built-in support for generating configuration files with authentication. You can use @file{/etc/grub.d/40_custom} to add simple superuser authentication, by adding @kbd{set superusers=} and @kbd{password} or @kbd{password_pbkdf2} commands. -@node Using digital signatures -@section Using digital signatures in GRUB +@node Using GPG-style digital signatures +@section Using GPG-style digital signatures in GRUB GRUB's @file{core.img} can optionally provide enforcement that all files subsequently read from disk are covered by a valid digital signature. -This document does @strong{not} cover how to ensure that your +This section does @strong{not} cover how to ensure that your platform's firmware (e.g., Coreboot) validates @file{core.img}. If environment variable @code{check_signatures} @@ -6474,8 +6772,18 @@ validation fails, then file @file{foo} cannot be opened. This failure may halt or otherwise impact the boot process. An initial trusted public key can be embedded within the GRUB @file{core.img} -using the @code{--pubkey} option to @command{grub-install} -(@pxref{Invoking grub-install}). +using the @code{--pubkey} option to @command{grub2-install} +(@pxref{Invoking grub2-install}). + +@comment Unfortunately --pubkey is not yet supported by grub2-install, +@comment but we should not bring up internal detail grub2-mkimage here +@comment in the user guide (as opposed to developer's manual). + +@comment An initial trusted public key can be embedded within the GRUB +@comment @file{core.img} using the @code{--pubkey} option to +@comment @command{grub2-mkimage} (@pxref{Invoking grub2-install}). Presently it +@comment is necessary to write a custom wrapper around @command{grub2-mkimage} +@comment using the @code{--grub-mkimage} flag to @command{grub2-install}. GRUB uses GPG-style detached signatures (meaning that a file @file{foo.sig} will be produced when file @file{foo} is signed), and @@ -6495,8 +6803,8 @@ gpg --detach-sign /path/to/file For successful validation of all of GRUB's subcomponents and the loaded OS kernel, they must all be signed. One way to accomplish this is the following (after having already produced the desired -@file{grub.cfg} file, e.g., by running @command{grub-mkconfig} -(@pxref{Invoking grub-mkconfig}): +@file{grub.cfg} file, e.g., by running @command{grub2-mkconfig} +(@pxref{Invoking grub2-mkconfig}): @example @group @@ -6518,7 +6826,7 @@ See also: @ref{check_signatures}, @ref{verify_detached}, @ref{trust}, Note that internally signature enforcement is controlled by setting the environment variable @code{check_signatures} equal to @code{enforce}. Passing one or more @code{--pubkey} options to -@command{grub-mkimage} implicitly defines @code{check_signatures} +@command{grub2-mkimage} implicitly defines @code{check_signatures} equal to @code{enforce} in @file{core.img} prior to processing any configuration files. @@ -6538,6 +6846,86 @@ or BIOS) configuration to cause the machine to boot from a different (attacker-controlled) device. GRUB is at best only one link in a secure boot chain. +@node Using appended signatures +@section Using appended signatures in GRUB + +GRUB supports verifying Linux-style 'appended signatures' for secure boot. +Appended signatures are PKCS#7 messages containing a signature over the +contents of a file, plus some metadata, appended to the end of a file. A file +with an appended signature ends with the magic string: + +@example +~Module signature appended~\n +@end example + +where @code{\n} represents the line-feed character, @code{0x0a}. + +Certificates can be managed at boot time using the @pxref{trust_certificate}, +@pxref{distrust_certificate} and @pxref{list_certificates} commands. +Certificates can also be built in to the core image using the @code{--x509} +parameter to @command{grub-install} or @command{grub-mkimage}. + +A file can be explictly verified using the @pxref{verify_appended} command. + +Only signatures made with the SHA-256 or SHA-512 hash algorithm are supported, +and only RSA signatures are supported. + +A file can be signed with the @command{sign-file} utility supplied with the +Linux kernel source. For example, if you have @code{signing.key} as the private +key and @code{certificate.der} as the x509 certificate containing the public key: + +@example +sign-file SHA256 signing.key certificate.der vmlinux vmlinux.signed +@end example + +Enforcement of signature verification is controlled by the +@code{check_appended_signatures} variable. + +@itemize +@item @samp{no}: no verification is performed. This is the default when GRUB + is not in @pxref{Lockdown} mode. +@item @samp{enforce}: verification is performed. Verification can be disabled + by setting the variable back to @samp{no}. +@item @samp{forced}: verification is performed and cannot be disabled. This is + set when GRUB is in Lockdown when the appendedsig module is loaded. +@end itemize + +Unlike GPG-style signatures, not all files loaded by GRUB are required to be +signed. Once verification is turned on, the following file types will have +appended signatures verified: + +@itemize +@item Linux kernels +@item GRUB modules, except those built into the core image +@item Any new certificate files to be trusted +@end itemize + +ACPI tables and Device Tree images will not be checked for appended signatures +but must be verified by another mechanism such as GPG-style signatures before +they will be loaded. + +Unless lockdown mode is enabled, signature checking does @strong{not} +stop an attacker with console access from dropping manually to the GRUB +console and executing: + +@example +set check_appended_signatures=no +@end example + +Refer to the section on password-protecting GRUB (@pxref{Authentication +and authorisation}) for more information on preventing this. + +Additionally, unless lockdown mode is enabled: + +@itemize +@item Special care must be taken around the @command{loadenv} command, which + can be used to turn off @code{check_appended_signature}. + +@item If the grub configuration file is loaded from the disk, anyone who can + modify the file on disk can turn off @code{check_appended_signature}. + Consider embedding the configuration into the core grub image. +@end itemize + @node UEFI secure boot and shim @section UEFI secure boot and shim support @@ -6566,7 +6954,7 @@ UTF-8 strings as comma-separated values (CSV). See @uref{https://github.com/rhboot/shim/blob/main/SBAT.md} for more details. To add a data section containing the SBAT information into the binary, the -@option{--sbat} option of @command{grub-mkimage} command should be used. The content +@option{--sbat} option of @command{grub2-mkimage} command should be used. The content of a CSV file, encoded with UTF-8, is copied as is to the .sbat data section into the generated EFI binary. The CSV file can be stored anywhere on the file system. @@ -6616,12 +7004,102 @@ platforms. @section Lockdown when booting on a secure setup The GRUB can be locked down when booted on a secure boot environment, for example -if the UEFI secure boot is enabled. On a locked down configuration, the GRUB will -be restricted and some operations/commands cannot be executed. +if UEFI or Power secure boot is enabled. On a locked down configuration, the +GRUB will be restricted and some operations/commands cannot be executed. The @samp{lockdown} variable is set to @samp{y} when the GRUB is locked down. Otherwise it does not exit. +@node Signing GRUB itself +@section Signing GRUB itself + +To ensure a complete secure-boot chain, there must be a way for the code that +loads GRUB to verify the integrity of the core image. + +This is ultimately platform-specific and individual platforms can define their +own mechanisms. However, there are general-purpose mechanisms that can be used +with GRUB. + +@section Signing GRUB for UEFI secure boot + +On UEFI platforms, @file{core.img} is a PE binary. Therefore, it can be signed +with a tool such as @command{pesign} or @command{sbsign}. Refer to the +suggestions in @pxref{UEFI secure boot and shim} to ensure that the final +image works under UEFI secure boot and can maintain the secure-boot chain. It +will also be necessary to enrol the public key used into a relevant firmware +key database. + +@section Signing GRUB with an appended signature + +The @file{core.elf} itself can be signed with a Linux kernel module-style +appended signature. + +To support IEEE1275 platforms where the boot image is often loaded directly +from a disk partition rather than from a file system, the @file{core.elf} +can specify the size and location of the appended signature with an ELF +note added by @command{grub-install}. + +An image can be signed this way using the @command{sign-file} command from +the Linux kernel: + +@example +@group +# grub.key is your private key and certificate.der is your public key + +# Determine the size of the appended signature. It depends on the signing +# certificate and the hash algorithm +touch empty +sign-file SHA256 grub.key certificate.der empty empty.sig +SIG_SIZE=`stat -c '%s' empty.sig` +rm empty empty.sig + +# Build a grub image with $SIG_SIZE reserved for the signature +grub-install --appended-signature-size $SIG_SIZE --modules="..." ... + +# Replace the reserved size with a signature: +# cut off the last $SIG_SIZE bytes with truncate's minus modifier +truncate -s -$SIG_SIZE /boot/grub/powerpc-ieee1275/core.elf core.elf.unsigned +# sign the trimmed file with an appended signature, restoring the correct size +sign-file SHA256 grub.key certificate.der core.elf.unsigned core.elf.signed + +# Don't forget to install the signed image as required +# (e.g. on powerpc-ieee1275, to the PReP partition) +@end group +@end example + +As with UEFI secure boot, it is necessary to build in the required modules, +or sign them separately. + +@subsection Command line and menuentry editor protection + +The TPM key protector provides full disk encryption support on servers or +virtual machine images, meanwhile keeping the boot process unattended. This +prevents service disruptions by eliminating the need for manual password input +during startup, improving system uptime and continuity. It is achieved by TPM, +which verifies the integrity of boot components by checking cryptographic +hashes against securely stored values, to confirm the disks are unlocked in a +trusted state. + +However, for users to access the system interactively, some form of +authentication is still required, as the disks are not unlocked by an +authorized user. This raised concerns about using an unprotected +@samp{command-line interface} (@pxref{Command-line interface}), as anyone could +execute commands to access decrypted data. To address this issue, the LUKS +password is used to ensure that only authorized users are granted access to the +interface. Additionally, the @samp{menu entry editor} (@pxref{Menu entry +editor}) is also safeguarded by the LUKS password, as modifying a boot entry is +effectively the same as altering the @file{grub.cfg} file read from encrypted +files. + +It is worth mentioning that the built-in password support, as described in +@samp{Authentication and Authorization in GRUB} (@pxref{Authentication and +authorisation}), can also be used to protect the command-line interface from +unauthorized access. However, it is not recommended to rely on this approach as +it is an optional step. Setting it up requires additional manual intervention, +which increases the risk of password leakage during the process. Moreover, the +superuser list must be well maintained, and the password used cannot be +synchronized with LUKS key rotation. + @node Platform limitations @chapter Platform limitations @@ -6672,6 +7150,7 @@ USB support provides benefits similar to ATA (for USB disks) or AT (for USB keyboards). In addition it allows USBserial. Chainloading refers to the ability to load another bootloader through the same protocol +and on some platforms, like EFI, allow that bootloader to return to the GRUB. Hints allow faster disk discovery by already knowing in advance which is the disk in question. On some platforms hints are correct unless you move the disk between boots. @@ -6977,10 +7456,10 @@ Required files are: GRUB's normal start-up procedure involves setting the @samp{prefix} environment variable to a value set in the core image by -@command{grub-install}, setting the @samp{root} variable to match, loading +@command{grub2-install}, setting the @samp{root} variable to match, loading the @samp{normal} module from the prefix, and running the @samp{normal} command (@pxref{normal}). This command is responsible for reading -@file{/boot/grub/grub.cfg}, running the menu, and doing all the useful +@file{/boot/grub2/grub.cfg}, running the menu, and doing all the useful things GRUB is supposed to do. If, instead, you only get a rescue shell, this usually means that GRUB @@ -7006,8 +7485,8 @@ normal However, any problem that leaves you in the rescue shell probably means that GRUB was not correctly installed. It may be more useful to try to reinstall -it properly using @kbd{grub-install @var{device}} (@pxref{Invoking -grub-install}). When doing this, there are a few things to remember: +it properly using @kbd{grub2-install @var{device}} (@pxref{Invoking +grub2-install}). When doing this, there are a few things to remember: @itemize @bullet{} @item @@ -7019,7 +7498,7 @@ is usually better to use UUIDs or file system labels and avoid depending on drive ordering entirely. @item -At least on BIOS systems, if you tell @command{grub-install} to install GRUB +At least on BIOS systems, if you tell @command{grub2-install} to install GRUB to a partition but GRUB has already been installed in the master boot record, then the GRUB installation in the partition will be ignored. @@ -7054,33 +7533,40 @@ bootability on other machines. @chapter User-space utilities @menu -* Invoking grub-install:: How to use the GRUB installer -* Invoking grub-mkconfig:: Generate a GRUB configuration file -* Invoking grub-mkpasswd-pbkdf2:: +* Invoking grub2-install:: How to use the GRUB installer +* Invoking grub2-mkconfig:: Generate a GRUB configuration file +* Invoking grub2-mkpasswd-pbkdf2:: Generate GRUB password hashes -* Invoking grub-mkrelpath:: Make system path relative to its root -* Invoking grub-mkrescue:: Make a GRUB rescue image -* Invoking grub-mount:: Mount a file system using GRUB -* Invoking grub-probe:: Probe device information for GRUB -* Invoking grub-script-check:: Check GRUB script file for syntax errors +* Invoking grub2-mkrelpath:: Make system path relative to its root +* Invoking grub2-mkrescue:: Make a GRUB rescue image +* Invoking grub2-mount:: Mount a file system using GRUB +* Invoking grub2-probe:: Probe device information for GRUB +* Invoking grub2-script-check:: Check GRUB script file for syntax errors @end menu -@node Invoking grub-install -@section Invoking grub-install +@node Invoking grub2-install +@section Invoking grub2-install -The program @command{grub-install} generates a GRUB core image using -@command{grub-mkimage} and installs it on your system. You must specify the +The program @command{grub2-install} generates a GRUB core image using +@command{grub2-mkimage} and installs it on your system. You must specify the device name on which you want to install GRUB, like this: @example -grub-install @var{install_device} +grub2-install @var{install_device} @end example The device name @var{install_device} is an OS device name or a GRUB device name. -@command{grub-install} accepts the following options: +In order to support UEFI Secure Boot, the resulting GRUB EFI binary must +be signed by a recognized private key. For this reason, for EFI +platforms, most distributions also ship prebuilt GRUB EFI binaries +signed by a distribution-specific private key. In this case, however, +@command{grub2-install} should not be used because it would overwrite +the signed EFI binary. + +@command{grub2-install} accepts the following options: @table @option @item --help @@ -7096,13 +7582,13 @@ separate partition or a removable disk. If this option is not specified then it defaults to @file{/boot}, so @example -@kbd{grub-install /dev/sda} +@kbd{grub2-install /dev/sda} @end example is equivalent to @example -@kbd{grub-install --boot-directory=/boot/ /dev/sda} +@kbd{grub2-install --boot-directory=/boot/ /dev/sda} @end example Here is an example in which you have a separate @dfn{boot} partition which is @@ -7110,16 +7596,16 @@ mounted on @file{/mnt/boot}: @example -@kbd{grub-install --boot-directory=/mnt/boot /dev/sdb} +@kbd{grub2-install --boot-directory=/mnt/boot /dev/sdb} @end example @item --recheck -Recheck the device map, even if @file{/boot/grub/device.map} already +Recheck the device map, even if @file{/boot/grub2/device.map} already exists. You should use this option whenever you add/remove a disk into/from your computer. @item --no-rs-codes -By default on x86 BIOS systems, @command{grub-install} will use some +By default on x86 BIOS systems, @command{grub2-install} will use some extra space in the bootloader embedding area for Reed-Solomon error-correcting codes. This enables GRUB to still boot successfully if some blocks are corrupted. The exact amount of protection offered @@ -7132,17 +7618,17 @@ installation}) where GRUB does not reside in any unpartitioned space outside of the MBR. Disable the Reed-Solomon codes with this option. @end table -@node Invoking grub-mkconfig -@section Invoking grub-mkconfig +@node Invoking grub2-mkconfig +@section Invoking grub2-mkconfig -The program @command{grub-mkconfig} generates a configuration file for GRUB +The program @command{grub2-mkconfig} generates a configuration file for GRUB (@pxref{Simple configuration}). @example -grub-mkconfig -o /boot/grub/grub.cfg +grub2-mkconfig -o /boot/grub2/grub.cfg @end example -@command{grub-mkconfig} accepts the following options: +@command{grub2-mkconfig} accepts the following options: @table @option @item --help @@ -7158,17 +7644,17 @@ it to standard output. @end table -@node Invoking grub-mkpasswd-pbkdf2 -@section Invoking grub-mkpasswd-pbkdf2 +@node Invoking grub2-mkpasswd-pbkdf2 +@section Invoking grub2-mkpasswd-pbkdf2 -The program @command{grub-mkpasswd-pbkdf2} generates password hashes for +The program @command{grub2-mkpasswd-pbkdf2} generates password hashes for GRUB (@pxref{Security}). @example -grub-mkpasswd-pbkdf2 +grub2-mkpasswd-pbkdf2 @end example -@command{grub-mkpasswd-pbkdf2} accepts the following options: +@command{grub2-mkpasswd-pbkdf2} accepts the following options: @table @option @item -c @var{number} @@ -7186,23 +7672,23 @@ Length of the salt. Defaults to 64. @end table -@node Invoking grub-mkrelpath -@section Invoking grub-mkrelpath +@node Invoking grub2-mkrelpath +@section Invoking grub2-mkrelpath -The program @command{grub-mkrelpath} makes a file system path relative to +The program @command{grub2-mkrelpath} makes a file system path relative to the root of its containing file system. For instance, if @file{/usr} is a mount point, then: @example -$ @kbd{grub-mkrelpath /usr/share/grub/unicode.pf2} +$ @kbd{grub2-mkrelpath /usr/share/grub/unicode.pf2} @samp{/share/grub/unicode.pf2} @end example This is mainly used internally by other GRUB utilities such as -@command{grub-mkconfig} (@pxref{Invoking grub-mkconfig}), but may +@command{grub2-mkconfig} (@pxref{Invoking grub2-mkconfig}), but may occasionally also be useful for debugging. -@command{grub-mkrelpath} accepts the following options: +@command{grub2-mkrelpath} accepts the following options: @table @option @item --help @@ -7213,17 +7699,17 @@ Print the version number of GRUB and exit. @end table -@node Invoking grub-mkrescue -@section Invoking grub-mkrescue +@node Invoking grub2-mkrescue +@section Invoking grub2-mkrescue -The program @command{grub-mkrescue} generates a bootable GRUB rescue image +The program @command{grub2-mkrescue} generates a bootable GRUB rescue image (@pxref{Making a GRUB bootable CD-ROM}). @example -grub-mkrescue -o grub.iso +grub2-mkrescue -o grub.iso @end example -All arguments not explicitly listed as @command{grub-mkrescue} options are +All arguments not explicitly listed as @command{grub2-mkrescue} options are passed on directly to @command{xorriso} in @command{mkisofs} emulation mode. Options passed to @command{xorriso} will normally be interpreted as @command{mkisofs} options; if the option @samp{--} is used, then anything @@ -7235,10 +7721,10 @@ commonly used to add extra files to the image: @example mkdir -p disk/boot/grub @r{(add extra files to @file{disk/boot/grub})} -grub-mkrescue -o grub.iso disk +grub2-mkrescue -o grub.iso disk @end example -@command{grub-mkrescue} accepts the following options: +@command{grub2-mkrescue} accepts the following options: @table @option @item --help @@ -7266,15 +7752,15 @@ Use @var{file} as the @command{xorriso} program, rather than the built-in default. @item --grub-mkimage=@var{file} -Use @var{file} as the @command{grub-mkimage} program, rather than the +Use @var{file} as the @command{grub2-mkimage} program, rather than the built-in default. @end table -@node Invoking grub-mount -@section Invoking grub-mount +@node Invoking grub2-mount +@section Invoking grub2-mount -The program @command{grub-mount} performs a read-only mount of any file +The program @command{grub2-mount} performs a read-only mount of any file system or file system image that GRUB understands, using GRUB's file system drivers via FUSE. (It is only available if FUSE development files were present when GRUB was built.) This has a number of uses: @@ -7306,13 +7792,13 @@ even if nobody has yet written a FUSE module specifically for that file system type. @end itemize -Using @command{grub-mount} is normally as simple as: +Using @command{grub2-mount} is normally as simple as: @example -grub-mount /dev/sda1 /mnt +grub2-mount /dev/sda1 /mnt @end example -@command{grub-mount} must be given one or more images and a mount point as +@command{grub2-mount} must be given one or more images and a mount point as non-option arguments (if it is given more than one image, it will treat them as a RAID set), and also accepts the following options: @@ -7334,13 +7820,13 @@ Show debugging output for conditions matching @var{string}. @item -K prompt|@var{file} @itemx --zfs-key=prompt|@var{file} Load a ZFS encryption key. If you use @samp{prompt} as the argument, -@command{grub-mount} will read a passphrase from the terminal; otherwise, it +@command{grub2-mount} will read a passphrase from the terminal; otherwise, it will read key material from the specified file. @item -r @var{device} @itemx --root=@var{device} Set the GRUB root device to @var{device}. You do not normally need to set -this; @command{grub-mount} will automatically set the root device to the +this; @command{grub2-mount} will automatically set the root device to the root of the supplied file system. If @var{device} is just a number, then it will be treated as a partition @@ -7349,7 +7835,7 @@ an entire disk in @file{disk.img}, then you can use this command to mount its second partition: @example -grub-mount -r 2 disk.img mount-point +grub2-mount -r 2 disk.img mount-point @end example @item -v @@ -7358,18 +7844,18 @@ Print verbose messages. @end table -@node Invoking grub-probe -@section Invoking grub-probe +@node Invoking grub2-probe +@section Invoking grub2-probe -The program @command{grub-probe} probes device information for a given path +The program @command{grub2-probe} probes device information for a given path or device. @example -grub-probe --target=fs /boot/grub -grub-probe --target=drive --device /dev/sda1 +grub2-probe --target=fs /boot/grub +grub2-probe --target=drive --device /dev/sda1 @end example -@command{grub-probe} must be given a path or device as a non-option +@command{grub2-probe} must be given a path or device as a non-option argument, and also accepts the following options: @table @option @@ -7382,16 +7868,16 @@ Print the version number of GRUB and exit. @item -d @itemx --device If this option is given, then the non-option argument is a system device -name (such as @samp{/dev/sda1}), and @command{grub-probe} will print +name (such as @samp{/dev/sda1}), and @command{grub2-probe} will print information about that device. If it is not given, then the non-option argument is a filesystem path (such as @samp{/boot/grub}), and -@command{grub-probe} will print information about the device containing that +@command{grub2-probe} will print information about the device containing that part of the filesystem. @item -m @var{file} @itemx --device-map=@var{file} Use @var{file} as the device map (@pxref{Device map}) rather than the -default, usually @samp{/boot/grub/device.map}. +default, usually @samp{/boot/grub2/device.map}. @item -t @var{target} @itemx --target=@var{target} @@ -7444,19 +7930,19 @@ Print verbose messages. @end table -@node Invoking grub-script-check -@section Invoking grub-script-check +@node Invoking grub2-script-check +@section Invoking grub2-script-check -The program @command{grub-script-check} takes a GRUB script file +The program @command{grub2-script-check} takes a GRUB script file (@pxref{Shell-like scripting}) and checks it for syntax errors, similar to commands such as @command{sh -n}. It may take a @var{path} as a non-option argument; if none is supplied, it will read from standard input. @example -grub-script-check /boot/grub/grub.cfg +grub2-script-check /boot/grub2/grub.cfg @end example -@command{grub-script-check} accepts the following options: +@command{grub2-script-check} accepts the following options: @table @option @item --help diff --git a/docs/man/grub-get-kernel-settings.h2m b/docs/man/grub-get-kernel-settings.h2m new file mode 100644 index 0000000000..b8051f01f3 --- /dev/null +++ b/docs/man/grub-get-kernel-settings.h2m @@ -0,0 +1,2 @@ +[NAME] +grub-get-kernel-settings \- Evaluate the system's kernel installation settings for use while making a grub configuration file diff --git a/docs/man/grub-set-bootflag.h2m b/docs/man/grub-set-bootflag.h2m new file mode 100644 index 0000000000..94ec0b92ed --- /dev/null +++ b/docs/man/grub-set-bootflag.h2m @@ -0,0 +1,2 @@ +[NAME] +grub-set-bootflag \- set a bootflag in the GRUB environment block diff --git a/docs/man/grub-set-password.h2m b/docs/man/grub-set-password.h2m new file mode 100644 index 0000000000..10ee82f4d5 --- /dev/null +++ b/docs/man/grub-set-password.h2m @@ -0,0 +1,2 @@ +[NAME] +grub-set-password \- generate the user.cfg file containing the hashed grub bootloader password diff --git a/docs/man/grub-switch-to-blscfg.h2m b/docs/man/grub-switch-to-blscfg.h2m new file mode 100644 index 0000000000..fa341426a5 --- /dev/null +++ b/docs/man/grub-switch-to-blscfg.h2m @@ -0,0 +1,2 @@ +[NAME] +grub-switch-to-blscfg \- switch to using BLS config files diff --git a/gentpl.py b/gentpl.py index bfab2113a1..0f8d009ef2 100644 --- a/gentpl.py +++ b/gentpl.py @@ -52,6 +52,7 @@ GROUPS["riscv64"] = [ "riscv64_efi" ] # Groups based on firmware +GROUPS["pc"] = [ "i386_pc" ] GROUPS["efi"] = [ "i386_efi", "x86_64_efi", "ia64_efi", "arm_efi", "arm64_efi", "loongarch64_efi", "riscv32_efi", "riscv64_efi" ] GROUPS["ieee1275"] = [ "i386_ieee1275", "sparc64_ieee1275", "powerpc_ieee1275" ] @@ -594,11 +595,21 @@ def platform_conditional(platform, closure): # }; # def foreach_enabled_platform(defn, closure): + enabled = False + disabled = False if 'enable' in defn: + enabled = True for platform in GRUB_PLATFORMS: if platform_tagged(defn, platform, "enable"): platform_conditional(platform, closure) - else: + + if 'disable' in defn: + disabled = True + for platform in GRUB_PLATFORMS: + if not platform_tagged(defn, platform, "disable"): + platform_conditional(platform, closure) + + if not enabled and not disabled: for platform in GRUB_PLATFORMS: platform_conditional(platform, closure) @@ -631,6 +642,9 @@ def platform_values(defn, platform, suffix): def extra_dist(defn): return foreach_value(defn, "extra_dist", lambda value: value + " ") +def extra_dep(defn): + return foreach_value(defn, "depends", lambda value: value + " ") + def platform_sources(defn, p): return platform_values(defn, p, "") def platform_nodist_sources(defn, p): return platform_values(defn, p, "_nodist") @@ -657,6 +671,8 @@ def first_time(defn, snippet): def is_platform_independent(defn): if 'enable' in defn: return False + if 'disable' in defn: + return False for suffix in [ "", "_nodist" ]: template = platform_values(defn, GRUB_PLATFORMS[0], suffix) for platform in GRUB_PLATFORMS[1:]: @@ -686,10 +702,10 @@ def module(defn, platform): var_set(cname(defn) + "_SOURCES", platform_sources(defn, platform) + " ## platform sources") var_set("nodist_" + cname(defn) + "_SOURCES", platform_nodist_sources(defn, platform) + " ## platform nodist sources") var_set(cname(defn) + "_LDADD", platform_ldadd(defn, platform)) - var_set(cname(defn) + "_CFLAGS", "$(AM_CFLAGS) $(CFLAGS_MODULE) " + platform_cflags(defn, platform)) - var_set(cname(defn) + "_LDFLAGS", "$(AM_LDFLAGS) $(LDFLAGS_MODULE) " + platform_ldflags(defn, platform)) - var_set(cname(defn) + "_CPPFLAGS", "$(AM_CPPFLAGS) $(CPPFLAGS_MODULE) " + platform_cppflags(defn, platform)) - var_set(cname(defn) + "_CCASFLAGS", "$(AM_CCASFLAGS) $(CCASFLAGS_MODULE) " + platform_ccasflags(defn, platform)) + var_set(cname(defn) + "_CFLAGS", "$(CFLAGS_MODULE) " + platform_cflags(defn, platform)) + var_set(cname(defn) + "_LDFLAGS", "$(LDFLAGS_MODULE) " + platform_ldflags(defn, platform)) + var_set(cname(defn) + "_CPPFLAGS", "$(CPPFLAGS_MODULE) " + platform_cppflags(defn, platform)) + var_set(cname(defn) + "_CCASFLAGS", "$(CCASFLAGS_MODULE) " + platform_ccasflags(defn, platform)) var_set(cname(defn) + "_DEPENDENCIES", "$(TARGET_OBJ2ELF) " + platform_dependencies(defn, platform)) gvar_add("dist_noinst_DATA", extra_dist(defn)) @@ -699,6 +715,10 @@ def module(defn, platform): gvar_add("MOD_FILES", name + ".mod") gvar_add("MARKER_FILES", name + ".marker") gvar_add("CLEANFILES", name + ".marker") + + for dep in defn.find_all("depends"): + gvar_add("EXTRA_DEPS", "depends " + name + " " + dep + ":") + output(""" """ + name + """.marker: $(""" + cname(defn) + """_SOURCES) $(nodist_""" + cname(defn) + """_SOURCES) $(TARGET_CPP) -DGRUB_LST_GENERATOR $(CPPFLAGS_MARKER) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(""" + cname(defn) + """_CPPFLAGS) $(CPPFLAGS) $^ > $@.new || (rm -f $@; exit 1) diff --git a/grub-core/.gitignore b/grub-core/.gitignore new file mode 100644 index 0000000000..2acce28115 --- /dev/null +++ b/grub-core/.gitignore @@ -0,0 +1,16 @@ +/*.lst +/Makefile +/Makefile.gcry.def +/unidata.c +/build-grub-module-verifier +/gdb_grub +/genmod.sh +/gensyminfo.sh +/gentrigtables +/gmodule.pl +/grub_script.tab.[ch] +/modinfo.sh +/rs_decoder.h +/symlist.c +/symlist.h +/trigtables.c diff --git a/grub-core/Makefile.am b/grub-core/Makefile.am index f18550c1c9..aa172391fd 100644 --- a/grub-core/Makefile.am +++ b/grub-core/Makefile.am @@ -66,6 +66,7 @@ CLEANFILES += grub_script.yy.c grub_script.yy.h include $(srcdir)/Makefile.core.am +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/backtrace.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/cache.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/command.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/device.h @@ -75,6 +76,7 @@ KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/efi/sb.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/env.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/env_private.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/err.h +KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/fdt.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/file.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/fs.h KERNEL_HEADER_FILES += $(top_srcdir)/include/grub/i18n.h @@ -454,8 +456,15 @@ crypto.lst: $(srcdir)/lib/libgcrypt-grub/cipher/crypto.lst platform_DATA += crypto.lst CLEANFILES += crypto.lst -syminfo.lst: gensyminfo.sh kernel_syms.lst $(top_srcdir)/grub-core/extra_deps.lst $(MODULE_FILES) - cat kernel_syms.lst $(top_srcdir)/grub-core/extra_deps.lst > $@.new +extra_deps.lst: + @echo $(EXTRA_DEPS) | sed "s/\s*:\s*/\n/g" > $@ + +syminfo.lst: gensyminfo.sh kernel_syms.lst extra_deps.lst $(MODULE_FILES) + if ! [ -f extra_deps.lst ] ; then \ + echo Missing extra_deps.lst ; \ + echo $(EXTRA_DEPS) | sed "s/\s*:\s*/\n/g" > extra_deps.lst ; \ + fi + cat kernel_syms.lst extra_deps.lst > $@.new for m in $(MODULE_FILES); do \ sh $< $$m >> $@.new || exit 1; \ done @@ -465,7 +474,7 @@ syminfo.lst: gensyminfo.sh kernel_syms.lst $(top_srcdir)/grub-core/extra_deps.ls moddep.lst: syminfo.lst genmoddep.awk video.lst cat $< | sort | $(AWK) -f $(srcdir)/genmoddep.awk > $@ || (rm -f $@; exit 1) platform_DATA += moddep.lst -CLEANFILES += config.log syminfo.lst moddep.lst +CLEANFILES += config.log syminfo.lst moddep.lst extra_deps.lst $(MOD_FILES): %.mod : genmod.sh moddep.lst %.module$(EXEEXT) build-grub-module-verifier$(BUILD_EXEEXT) TARGET_OBJ2ELF=@TARGET_OBJ2ELF@ sh $^ $@ diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 1571421d7e..a4532d74e3 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -99,7 +99,7 @@ kernel = { i386_xen_pvh_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x100000'; mips_loongson_ldflags = '-Wl,-Ttext,0x80200000'; - powerpc_ieee1275_ldflags = '-Wl,-Ttext,0x200000'; + powerpc_ieee1275_ldflags = '-Wl,-Ttext,0x400000'; sparc64_ieee1275_ldflags = '-Wl,-Ttext,0x4400'; mips_arc_ldflags = '-Wl,-Ttext,$(TARGET_LINK_ADDR)'; mips_qemu_mips_ldflags = '-Wl,-Ttext,0x80200000'; @@ -153,6 +153,12 @@ kernel = { common = kern/rescue_reader.c; common = kern/term.c; common = kern/verifiers.c; + common = kern/backtrace.c; + + x86 = kern/i386/backtrace.c; + i386_xen = kern/i386/backtrace.c; + x86_64_xen = kern/i386/backtrace.c; + arm64 = kern/arm64/backtrace.c; noemu = kern/compiler-rt.c; noemu = kern/mm.c; @@ -182,7 +188,6 @@ kernel = { arm_coreboot = kern/arm/coreboot/init.c; arm_coreboot = kern/arm/coreboot/timer.c; arm_coreboot = kern/arm/coreboot/coreboot.S; - arm_coreboot = lib/fdt.c; arm_coreboot = bus/fdt.c; arm_coreboot = term/ps2.c; arm_coreboot = term/arm/pl050.c; @@ -219,6 +224,7 @@ kernel = { efi = kern/efi/acpi.c; efi = kern/efi/sb.c; efi = kern/lockdown.c; + efi = lib/envblk.c; i386_coreboot = kern/i386/pc/acpi.c; i386_multiboot = kern/i386/pc/acpi.c; i386_coreboot = kern/acpi.c; @@ -331,6 +337,7 @@ kernel = { powerpc_ieee1275 = kern/powerpc/cache.S; powerpc_ieee1275 = kern/powerpc/dl.c; powerpc_ieee1275 = kern/powerpc/compiler-rt.S; + powerpc_ieee1275 = kern/lockdown.c; sparc64_ieee1275 = kern/sparc64/cache.S; sparc64_ieee1275 = kern/sparc64/dl.c; @@ -363,10 +370,15 @@ kernel = { riscv64 = kern/riscv/cache_flush.S; riscv64 = kern/riscv/dl.c; + fdt = lib/fdt.c; + emu = disk/host.c; emu = kern/emu/cache_s.S; emu = kern/emu/hostdisk.c; emu = osdep/unix/hostdisk.c; + emu = osdep/relpath.c; + emu = osdep/getroot.c; + emu = osdep/unix/getroot.c; emu = osdep/exec.c; extra_dist = osdep/unix/exec.c; emu = osdep/devmapper/hostdisk.c; @@ -409,6 +421,11 @@ kernel = { extra_dist = kern/mips/cache_flush.S; }; +module = { + name = increment; + common = commands/increment.c; +}; + program = { name = grub-emu; mansection = 1; @@ -590,6 +607,11 @@ image = { enable = mips_loongson; }; +module = { + name = version; + common = commands/version.c; +}; + module = { name = disk; common = lib/disk.c; @@ -824,6 +846,12 @@ module = { enable = efi; }; +module = { + name = efienv; + common = commands/efi/env.c; + enable = efi; +}; + module = { name = efifwsetup; efi = commands/efi/efifwsetup.c; @@ -836,11 +864,39 @@ module = { enable = efi; }; +module = { + name = connectefi; + common = commands/efi/connectefi.c; + enable = efi; +}; + module = { name = blocklist; common = commands/blocklist.c; }; +module = { + name = blscfg; + common = commands/blscfg.c; + common = commands/loadenv.h; + enable = powerpc_ieee1275; + enable = efi; + enable = i386_pc; + enable = emu; + enable = xen; + enable = i386_xen_pvh; +}; + +module = { + name = blsuki; + common = commands/blsuki.c; + common = lib/gnulib/filevercmp.c; + enable = powerpc_ieee1275; + enable = efi; + cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)'; + cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)'; +}; + module = { name = boot; common = commands/boot.c; @@ -975,6 +1031,18 @@ module = { cppflags = '-I$(srcdir)/lib/posix_wrap'; }; +module = { + name = appendedsig; + common = commands/appendedsig/appendedsig.c; + common = commands/appendedsig/x509.c; + common = commands/appendedsig/pkcs7.c; + common = commands/appendedsig/asn1util.c; + common = commands/appendedsig/gnutls_asn1_tab.c; + common = commands/appendedsig/pkix_asn1_tab.c; + cflags = '$(CFLAGS_POSIX)'; + cppflags = '-I$(srcdir)/lib/posix_wrap'; +}; + module = { name = hdparm; common = commands/hdparm.c; @@ -1008,6 +1076,7 @@ module = { module = { name = loadenv; common = commands/loadenv.c; + common = commands/loadenv.h; common = lib/envblk.c; }; @@ -1777,12 +1846,6 @@ module = { }; -module = { - name = linux16; - common = loader/i386/pc/linux.c; - enable = x86; -}; - module = { name = ntldr; i386_pc = loader/i386/pc/ntldr.c; @@ -1838,10 +1901,9 @@ module = { module = { name = linux; - x86 = loader/i386/linux.c; i386_xen_pvh = loader/i386/linux.c; xen = loader/i386/xen.c; - i386_pc = lib/i386/pc/vesa_modes_table.c; + i386_pc = loader/i386/pc/linux.c; i386_xen_pvh = lib/i386/pc/vesa_modes_table.c; mips = loader/mips/linux.c; powerpc_ieee1275 = loader/powerpc/ieee1275/linux.c; @@ -1854,8 +1916,12 @@ module = { loongarch64 = loader/efi/linux.c; riscv32 = loader/efi/linux.c; riscv64 = loader/efi/linux.c; + i386_efi = loader/i386/efi/linux.c; i386_efi = loader/efi/linux.c; + i386_efi = loader/i386/linux.c; + x86_64_efi = loader/i386/efi/linux.c; x86_64_efi = loader/efi/linux.c; + x86_64_efi = loader/i386/linux.c; emu = loader/emu/linux.c; common = loader/linux.c; common = lib/cmdline.c; @@ -1864,7 +1930,6 @@ module = { module = { name = fdt; efi = loader/efi/fdt.c; - common = lib/fdt.c; enable = fdt; }; @@ -2165,6 +2230,12 @@ module = { common = tests/setjmp_test.c; }; +module = { + name = appended_signature_test; + common = tests/appended_signature_test.c; + common = tests/appended_signatures.h; +}; + module = { name = signature_test; common = tests/signature_test.c; @@ -2338,6 +2409,12 @@ module = { common = hook/datehook.c; }; +module = { + name = efi_netfs; + common = net/efi/efi_netfs.c; + enable = efi; +}; + module = { name = net; common = net/net.c; @@ -2351,6 +2428,12 @@ module = { common = net/ethernet.c; common = net/arp.c; common = net/netbuff.c; + efi = net/efi/net.c; + efi = net/efi/http.c; + efi = net/efi/pxe.c; + efi = net/efi/ip4_config.c; + efi = net/efi/ip6_config.c; + efi = net/efi/dhcp.c; }; module = { @@ -2392,6 +2475,7 @@ module = { common = commands/legacycfg.c; common = lib/legacy_parse.c; emu = lib/i386/pc/vesa_modes_table.c; + i386_pc = lib/i386/pc/vesa_modes_table.c; i386_efi = lib/i386/pc/vesa_modes_table.c; x86_64_efi = lib/i386/pc/vesa_modes_table.c; xen = lib/i386/pc/vesa_modes_table.c; @@ -2440,15 +2524,13 @@ module = { module = { name = backtrace; - x86 = lib/i386/backtrace.c; - i386_xen_pvh = lib/i386/backtrace.c; - i386_xen = lib/i386/backtrace.c; - x86_64_xen = lib/i386/backtrace.c; - common = lib/backtrace.c; + common = commands/backtrace.c; enable = x86; enable = i386_xen_pvh; enable = i386_xen; enable = x86_64_xen; + enable = arm64; + enable = loongarch64; }; module = { @@ -2525,6 +2607,14 @@ module = { cppflags = '$(CPPFLAGS_GCRY)'; }; +module = { + name = pkcs1_v15; + common = lib/pkcs1_v15.c; + + cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare'; + cppflags = '$(CPPFLAGS_GCRY)'; +}; + module = { name = all_video; common = lib/fake_module.c; @@ -2594,4 +2684,33 @@ module = { name = bli; efi = commands/bli.c; enable = efi; + depends = part_gpt; +}; + +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'; +}; + +module = { + name = test_asn1; + common = lib/libtasn1_wrap/tests/CVE-2018-1000654.c; + common = lib/libtasn1_wrap/tests/object-id-decoding.c; + common = lib/libtasn1_wrap/tests/object-id-encoding.c; + common = lib/libtasn1_wrap/tests/octet-string.c; + common = lib/libtasn1_wrap/tests/reproducers.c; + common = lib/libtasn1_wrap/tests/Test_overflow.c; + common = lib/libtasn1_wrap/tests/Test_simple.c; + common = lib/libtasn1_wrap/tests/Test_strings.c; + common = lib/libtasn1_wrap/wrap_tests.c; }; diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c index 9abebc6bda..2db07c7c0c 100644 --- a/grub-core/bus/usb/ehci.c +++ b/grub-core/bus/usb/ehci.c @@ -218,7 +218,7 @@ enum #define GRUB_EHCI_TERMINATE (1<<0) -#define GRUB_EHCI_TOGGLE (1<<31) +#define GRUB_EHCI_TOGGLE ((grub_uint32_t) 1<<31) enum { diff --git a/grub-core/commands/appendedsig/appendedsig.c b/grub-core/commands/appendedsig/appendedsig.c new file mode 100644 index 0000000000..1b8a807806 --- /dev/null +++ b/grub-core/commands/appendedsig/appendedsig.c @@ -0,0 +1,645 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2020-2021 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "appendedsig.h" + +GRUB_MOD_LICENSE ("GPLv3+"); + +const char magic[] = "~Module signature appended~\n"; + +/* + * This structure is extracted from scripts/sign-file.c in the linux kernel + * source. It was licensed as LGPLv2.1+, which is GPLv3+ compatible. + */ +struct module_signature +{ + grub_uint8_t algo; /* Public-key crypto algorithm [0] */ + grub_uint8_t hash; /* Digest algorithm [0] */ + grub_uint8_t id_type; /* Key identifier type [PKEY_ID_PKCS7] */ + grub_uint8_t signer_len; /* Length of signer's name [0] */ + grub_uint8_t key_id_len; /* Length of key identifier [0] */ + grub_uint8_t __pad[3]; + grub_uint32_t sig_len; /* Length of signature data */ +} GRUB_PACKED; + + +/* This represents an entire, parsed, appended signature */ +struct grub_appended_signature +{ + grub_size_t signature_len; /* Length of PKCS#7 data + + * metadata + magic */ + + struct module_signature sig_metadata; /* Module signature metadata */ + struct pkcs7_signedData pkcs7; /* Parsed PKCS#7 data */ +}; + +/* Trusted certificates for verifying appended signatures */ +struct x509_certificate *grub_trusted_key; + +/* + * Force gcry_rsa to be a module dependency. + * + * If we use grub_crypto_pk_rsa, then then the gcry_rsa module won't be built + * in if you add 'appendedsig' to grub-install --modules. You would need to + * add 'gcry_rsa' too. That's confusing and seems suboptimal, especially when + * we only support RSA. + * + * Dynamic loading also causes some concerns. We can't load gcry_rsa from the + * the filesystem after we install the verifier - we won't be able to verify + * it without having it already present. We also shouldn't load it before we + * install the verifier, because that would mean it wouldn't be verified - an + * attacker could insert any code they wanted into the module. + * + * So instead, reference the internal symbol from gcry_rsa. That creates a + * direct dependency on gcry_rsa, so it will be built in when this module + * is built in. Being built in (assuming the core image is itself signed!) + * also resolves our concerns about loading from the filesystem. + */ +extern gcry_pk_spec_t _gcry_pubkey_spec_rsa; + +static int check_sigs = 0; + +static const char * +grub_env_read_sec (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + if (check_sigs == 2) + return "forced"; + else if (check_sigs == 1) + return "enforce"; + else + return "no"; +} + +static char * +grub_env_write_sec (struct grub_env_var *var __attribute__((unused)), + const char *val) +{ + /* Do not allow the value to be changed if set to forced */ + if (check_sigs == 2) + return grub_strdup ("forced"); + + if ((*val == '2') || (*val == 'f')) + check_sigs = 2; + else if ((*val == '1') || (*val == 'e')) + check_sigs = 1; + else if ((*val == '0') || (*val == 'n')) + check_sigs = 0; + + return grub_strdup (grub_env_read_sec (NULL, NULL)); +} + +static grub_err_t +read_cert_from_file (grub_file_t f, struct x509_certificate *certificate) +{ + grub_err_t err; + grub_uint8_t *buf = NULL; + grub_ssize_t read_size; + grub_off_t total_read_size = 0; + grub_off_t file_size = grub_file_size (f); + + + if (file_size == GRUB_FILE_SIZE_UNKNOWN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Cannot parse a certificate file of unknown size")); + + buf = grub_zalloc (file_size); + if (!buf) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("Could not allocate buffer for certificate file contents")); + + while (total_read_size < file_size) + { + read_size = + grub_file_read (f, &buf[total_read_size], + file_size - total_read_size); + if (read_size < 0) + { + err = grub_error (GRUB_ERR_READ_ERROR, + N_("Error reading certificate file")); + goto cleanup_buf; + } + total_read_size += read_size; + } + + err = certificate_import (buf, total_read_size, certificate); + if (err != GRUB_ERR_NONE) + goto cleanup_buf; + + return GRUB_ERR_NONE; + +cleanup_buf: + grub_free (buf); + return err; +} + +static grub_err_t +extract_appended_signature (grub_uint8_t * buf, grub_size_t bufsize, + struct grub_appended_signature *sig) +{ + grub_err_t err; + grub_size_t pkcs7_size; + grub_size_t remaining_len; + grub_uint8_t *appsigdata = buf + bufsize - grub_strlen (magic); + + if (bufsize < grub_strlen (magic)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, + N_("File too short for signature magic")); + + if (grub_memcmp (appsigdata, (grub_uint8_t *) magic, grub_strlen (magic))) + return grub_error (GRUB_ERR_BAD_SIGNATURE, + N_("Missing or invalid signature magic")); + + remaining_len = bufsize - grub_strlen (magic); + + if (remaining_len < sizeof (struct module_signature)) + return grub_error (GRUB_ERR_BAD_SIGNATURE, + N_("File too short for signature metadata")); + + appsigdata -= sizeof (struct module_signature); + + /* extract the metadata */ + grub_memcpy (&(sig->sig_metadata), appsigdata, + sizeof (struct module_signature)); + + remaining_len -= sizeof (struct module_signature); + + if (sig->sig_metadata.id_type != 2) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("Wrong signature type")); + +#ifdef GRUB_TARGET_WORDS_BIGENDIAN + pkcs7_size = sig->sig_metadata.sig_len; +#else + pkcs7_size = __builtin_bswap32 (sig->sig_metadata.sig_len); +#endif + + if (pkcs7_size > remaining_len) + return grub_error (GRUB_ERR_BAD_SIGNATURE, + N_("File too short for PKCS#7 message")); + + grub_dprintf ("appendedsig", "sig len %" PRIuGRUB_SIZE "\n", pkcs7_size); + + sig->signature_len = + grub_strlen (magic) + sizeof (struct module_signature) + pkcs7_size; + + /* rewind pointer and parse pkcs7 data */ + appsigdata -= pkcs7_size; + + err = parse_pkcs7_signedData (appsigdata, pkcs7_size, &sig->pkcs7); + if (err != GRUB_ERR_NONE) + return err; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_verify_appended_signature (grub_uint8_t * buf, grub_size_t bufsize) +{ + grub_err_t err = GRUB_ERR_NONE; + grub_size_t datasize; + void *context; + unsigned char *hash; + gcry_mpi_t hashmpi; + gcry_err_code_t rc; + struct x509_certificate *pk; + struct grub_appended_signature sig; + + if (!grub_trusted_key) + return grub_error (GRUB_ERR_BAD_SIGNATURE, + N_("No trusted keys to verify against")); + + err = extract_appended_signature (buf, bufsize, &sig); + if (err != GRUB_ERR_NONE) + return err; + + datasize = bufsize - sig.signature_len; + + context = grub_zalloc (sig.pkcs7.hash->contextsize); + if (!context) + return grub_errno; + + sig.pkcs7.hash->init (context); + sig.pkcs7.hash->write (context, buf, datasize); + sig.pkcs7.hash->final (context); + hash = sig.pkcs7.hash->read (context); + grub_dprintf ("appendedsig", + "data size %" PRIxGRUB_SIZE ", hash %02x%02x%02x%02x...\n", + datasize, hash[0], hash[1], hash[2], hash[3]); + + err = GRUB_ERR_BAD_SIGNATURE; + for (pk = grub_trusted_key; pk; pk = pk->next) + { + rc = grub_crypto_rsa_pad (&hashmpi, hash, sig.pkcs7.hash, pk->mpis[0]); + if (rc) + { + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + N_("Error padding hash for RSA verification: %d"), + rc); + goto cleanup; + } + + rc = _gcry_pubkey_spec_rsa.verify (0, hashmpi, &sig.pkcs7.sig_mpi, + pk->mpis, NULL, NULL); + gcry_mpi_release (hashmpi); + + if (rc == 0) + { + grub_dprintf ("appendedsig", "verify with key '%s' succeeded\n", + pk->subject); + err = GRUB_ERR_NONE; + break; + } + + grub_dprintf ("appendedsig", "verify with key '%s' failed with %d\n", + pk->subject, rc); + } + + /* If we didn't verify, provide a neat message */ + if (err != GRUB_ERR_NONE) + err = grub_error (GRUB_ERR_BAD_SIGNATURE, + N_("Failed to verify signature against a trusted key")); + +cleanup: + grub_free (context); + pkcs7_signedData_release (&sig.pkcs7); + + return err; +} + +static grub_err_t +grub_cmd_verify_signature (grub_command_t cmd __attribute__((unused)), + int argc, char **args) +{ + grub_file_t f; + grub_err_t err = GRUB_ERR_NONE; + grub_uint8_t *data; + grub_ssize_t read_size; + grub_off_t file_size, total_read_size = 0; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + grub_dprintf ("appendedsig", "verifying %s\n", args[0]); + + f = grub_file_open (args[0], GRUB_FILE_TYPE_VERIFY_SIGNATURE); + if (!f) + { + err = grub_errno; + goto cleanup; + } + + file_size = grub_file_size (f); + if (file_size == GRUB_FILE_SIZE_UNKNOWN) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Cannot verify the signature of a file of unknown size")); + + data = grub_malloc (file_size); + if (!data) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("Could not allocate data buffer size %" + PRIuGRUB_UINT64_T " for verification"), file_size); + + while (total_read_size < file_size) + { + read_size = + grub_file_read (f, &data[total_read_size], + file_size - total_read_size); + if (read_size < 0) + { + err = grub_error (GRUB_ERR_READ_ERROR, + N_("Error reading file to verify")); + goto cleanup_data; + } + total_read_size += read_size; + } + + err = grub_verify_appended_signature (data, file_size); + +cleanup_data: + grub_free (data); +cleanup: + if (f) + grub_file_close (f); + return err; +} + +static grub_err_t +grub_cmd_distrust (grub_command_t cmd __attribute__((unused)), + int argc, char **args) +{ + unsigned long cert_num, i; + struct x509_certificate *cert, *prev; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("One argument expected")); + + grub_errno = GRUB_ERR_NONE; + cert_num = grub_strtoul (args[0], NULL, 10); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + if (cert_num < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("Certificate number too small - numbers start at 1")); + + if (cert_num == 1) + { + cert = grub_trusted_key; + grub_trusted_key = cert->next; + + certificate_release (cert); + grub_free (cert); + return GRUB_ERR_NONE; + } + i = 2; + prev = grub_trusted_key; + cert = grub_trusted_key->next; + while (cert) + { + if (i == cert_num) + { + prev->next = cert->next; + certificate_release (cert); + grub_free (cert); + return GRUB_ERR_NONE; + } + i++; + prev = cert; + cert = cert->next; + } + + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("No certificate number %lu found - only %lu certificates in the store"), + cert_num, i - 1); +} + +static grub_err_t +grub_cmd_trust (grub_command_t cmd __attribute__((unused)), + int argc, char **args) +{ + grub_file_t certf; + struct x509_certificate *cert = NULL; + grub_err_t err; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + certf = grub_file_open (args[0], + GRUB_FILE_TYPE_CERTIFICATE_TRUST + | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (!certf) + return grub_errno; + + + cert = grub_zalloc (sizeof (struct x509_certificate)); + if (!cert) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + N_("Could not allocate memory for certificate")); + + err = read_cert_from_file (certf, cert); + grub_file_close (certf); + if (err != GRUB_ERR_NONE) + { + grub_free (cert); + return err; + } + grub_dprintf ("appendedsig", "Loaded certificate with CN: %s\n", + cert->subject); + + cert->next = grub_trusted_key; + grub_trusted_key = cert; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_list (grub_command_t cmd __attribute__((unused)), + int argc __attribute__((unused)), + char **args __attribute__((unused))) +{ + struct x509_certificate *cert; + int cert_num = 1; + grub_size_t i; + + for (cert = grub_trusted_key; cert; cert = cert->next) + { + grub_printf (N_("Certificate %d:\n"), cert_num); + + grub_printf (N_("\tSerial: ")); + for (i = 0; i < cert->serial_len - 1; i++) + { + grub_printf ("%02x:", cert->serial[i]); + } + grub_printf ("%02x\n", cert->serial[cert->serial_len - 1]); + + grub_printf ("\tCN: %s\n\n", cert->subject); + cert_num++; + + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +appendedsig_init (grub_file_t io __attribute__((unused)), + enum grub_file_type type, + void **context __attribute__((unused)), + enum grub_verify_flags *flags) +{ + if (!check_sigs) + { + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + return GRUB_ERR_NONE; + } + + switch (type & GRUB_FILE_TYPE_MASK) + { + case GRUB_FILE_TYPE_CERTIFICATE_TRUST: + /* + * This is a certificate to add to trusted keychain. + * + * This needs to be verified or blocked. Ideally we'd write an x509 + * verifier, but we lack the hubris required to take this on. Instead, + * require that it have an appended signature. + */ + + /* Fall through */ + + case GRUB_FILE_TYPE_LINUX_KERNEL: + case GRUB_FILE_TYPE_GRUB_MODULE: + /* + * Appended signatures are only defined for ELF binaries. + * Out of an abundance of caution, we only verify Linux kernels and + * GRUB modules at this point. + */ + *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK; + return GRUB_ERR_NONE; + + case GRUB_FILE_TYPE_ACPI_TABLE: + case GRUB_FILE_TYPE_DEVICE_TREE_IMAGE: + /* + * It is possible to use appended signature verification without + * lockdown - like the PGP verifier. When combined with an embedded + * config file in a signed grub binary, this could still be a meaningful + * secure-boot chain - so long as it isn't subverted by something like a + * rouge ACPI table or DT image. Defer them explicitly. + */ + *flags = GRUB_VERIFY_FLAGS_DEFER_AUTH; + return GRUB_ERR_NONE; + + default: + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + return GRUB_ERR_NONE; + } +} + +static grub_err_t +appendedsig_write (void *ctxt __attribute__((unused)), + void *buf, grub_size_t size) +{ + return grub_verify_appended_signature (buf, size); +} + +struct grub_file_verifier grub_appendedsig_verifier = { + .name = "appendedsig", + .init = appendedsig_init, + .write = appendedsig_write, +}; + +static grub_ssize_t +pseudo_read (struct grub_file *file, char *buf, grub_size_t len) +{ + grub_memcpy (buf, (grub_uint8_t *) file->data + file->offset, len); + return len; +} + +/* Filesystem descriptor. */ +static struct grub_fs pseudo_fs = { + .name = "pseudo", + .fs_read = pseudo_read +}; + +static grub_command_t cmd_verify, cmd_list, cmd_distrust, cmd_trust; + +GRUB_MOD_INIT (appendedsig) +{ + int rc; + struct grub_module_header *header; + + /* If in lockdown, immediately enter forced mode */ + if (grub_is_lockdown () == GRUB_LOCKDOWN_ENABLED) + check_sigs = 2; + + grub_trusted_key = NULL; + + grub_register_variable_hook ("check_appended_signatures", + grub_env_read_sec, + grub_env_write_sec); + grub_env_export ("check_appended_signatures"); + + rc = asn1_init (); + if (rc) + grub_fatal ("Error initing ASN.1 data structures: %d: %s\n", rc, + asn1_strerror (rc)); + + FOR_MODULES (header) + { + struct grub_file pseudo_file; + struct x509_certificate *pk = NULL; + grub_err_t err; + + /* Not an ELF module, skip. */ + if (header->type != OBJ_TYPE_X509_PUBKEY) + continue; + + grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); + pseudo_file.fs = &pseudo_fs; + pseudo_file.size = header->size - sizeof (struct grub_module_header); + pseudo_file.data = (char *) header + sizeof (struct grub_module_header); + + grub_dprintf ("appendedsig", + "Found an x509 key, size=%" PRIuGRUB_UINT64_T "\n", + pseudo_file.size); + + pk = grub_zalloc (sizeof (struct x509_certificate)); + if (!pk) + { + grub_fatal ("Out of memory loading initial certificates"); + } + + err = read_cert_from_file (&pseudo_file, pk); + if (err != GRUB_ERR_NONE) + grub_fatal ("Error loading initial key: %s", grub_errmsg); + + grub_dprintf ("appendedsig", "loaded certificate CN='%s'\n", pk->subject); + + pk->next = grub_trusted_key; + grub_trusted_key = pk; + } + + cmd_trust = + grub_register_command ("trust_certificate", grub_cmd_trust, + N_("X509_CERTIFICATE"), + N_("Add X509_CERTIFICATE to trusted certificates.")); + cmd_list = + grub_register_command ("list_certificates", grub_cmd_list, 0, + N_("Show the list of trusted x509 certificates.")); + cmd_verify = + grub_register_command ("verify_appended", grub_cmd_verify_signature, + N_("FILE"), + N_("Verify FILE against the trusted x509 certificates.")); + cmd_distrust = + grub_register_command ("distrust_certificate", grub_cmd_distrust, + N_("CERT_NUMBER"), + N_("Remove CERT_NUMBER (as listed by list_certificates) from trusted certificates.")); + + grub_verifier_register (&grub_appendedsig_verifier); + grub_dl_set_persistent (mod); +} + +GRUB_MOD_FINI (appendedsig) +{ + /* + * grub_dl_set_persistent should prevent this from actually running, but + * it does still run under emu. + */ + + grub_verifier_unregister (&grub_appendedsig_verifier); + grub_unregister_command (cmd_verify); + grub_unregister_command (cmd_list); + grub_unregister_command (cmd_trust); + grub_unregister_command (cmd_distrust); +} diff --git a/grub-core/commands/appendedsig/appendedsig.h b/grub-core/commands/appendedsig/appendedsig.h new file mode 100644 index 0000000000..9792ef3901 --- /dev/null +++ b/grub-core/commands/appendedsig/appendedsig.h @@ -0,0 +1,110 @@ +/* + * 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 +#include + +extern asn1_node _gnutls_gnutls_asn; +extern asn1_node _gnutls_pkix_asn; + +#define MAX_OID_LEN 32 + +/* + * One or more x509 certificates. + * + * We do limited parsing: extracting only the serial, CN and RSA public key. + */ +struct x509_certificate +{ + struct x509_certificate *next; + + grub_uint8_t *serial; + grub_size_t serial_len; + + char *subject; + grub_size_t subject_len; + + /* We only support RSA public keys. This encodes [modulus, publicExponent] */ + gcry_mpi_t mpis[2]; +}; + +/* + * A PKCS#7 signedData message. + * + * We make no attempt to match intelligently, so we don't save any info about + * the signer. We also support only 1 signerInfo, so we only store a single + * MPI for the signature. + */ +struct pkcs7_signedData +{ + const gcry_md_spec_t *hash; + gcry_mpi_t sig_mpi; +}; + + +/* Do libtasn1 init */ +int asn1_init (void); + +/* + * Import a DER-encoded certificate at 'data', of size 'size'. + * + * Place the results into 'results', which must be already allocated. + */ +grub_err_t +certificate_import (void *data, grub_size_t size, + struct x509_certificate *results); + +/* + * Release all the storage associated with the x509 certificate. + * If the caller dynamically allocated the certificate, it must free it. + * The caller is also responsible for maintenance of the linked list. + */ +void certificate_release (struct x509_certificate *cert); + +/* + * Parse a PKCS#7 message, which must be a signedData message. + * + * The message must be in 'sigbuf' and of size 'data_size'. The result is + * placed in 'msg', which must already be allocated. + */ +grub_err_t +parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size, + struct pkcs7_signedData *msg); + +/* + * Release all the storage associated with the PKCS#7 message. + * If the caller dynamically allocated the message, it must free it. + */ +void pkcs7_signedData_release (struct pkcs7_signedData *msg); + +/* + * Read a value from an ASN1 node, allocating memory to store it. + * + * It will work for anything where the size libtasn1 returns is right: + * - Integers + * - Octet strings + * - DER encoding of other structures + * It will _not_ work for things where libtasn1 size requires adjustment: + * - Strings that require an extra NULL byte at the end + * - Bit strings because libtasn1 returns the length in bits, not bytes. + * + * If the function returns a non-NULL value, the caller must free it. + */ +void *grub_asn1_allocate_and_read (asn1_node node, const char *name, + const char *friendly_name, + int *content_size); diff --git a/grub-core/commands/appendedsig/asn1util.c b/grub-core/commands/appendedsig/asn1util.c new file mode 100644 index 0000000000..eff095a9df --- /dev/null +++ b/grub-core/commands/appendedsig/asn1util.c @@ -0,0 +1,102 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include "appendedsig.h" + +asn1_node _gnutls_gnutls_asn = ASN1_TYPE_EMPTY; +asn1_node _gnutls_pkix_asn = ASN1_TYPE_EMPTY; + +extern const ASN1_ARRAY_TYPE gnutls_asn1_tab[]; +extern const ASN1_ARRAY_TYPE pkix_asn1_tab[]; + +/* + * Read a value from an ASN1 node, allocating memory to store it. + * + * It will work for anything where the size libtasn1 returns is right: + * - Integers + * - Octet strings + * - DER encoding of other structures + * It will _not_ work for things where libtasn1 size requires adjustment: + * - Strings that require an extra NULL byte at the end + * - Bit strings because libtasn1 returns the length in bits, not bytes. + * + * If the function returns a non-NULL value, the caller must free it. + */ +void * +grub_asn1_allocate_and_read (asn1_node node, const char *name, + const char *friendly_name, int *content_size) +{ + int result; + grub_uint8_t *tmpstr = NULL; + int tmpstr_size = 0; + + result = asn1_read_value (node, name, NULL, &tmpstr_size); + if (result != ASN1_MEM_ERROR) + { + grub_snprintf (grub_errmsg, sizeof (grub_errmsg), + _ + ("Reading size of %s did not return expected status: %s"), + friendly_name, asn1_strerror (result)); + grub_errno = GRUB_ERR_BAD_FILE_TYPE; + return NULL; + } + + tmpstr = grub_malloc (tmpstr_size); + if (tmpstr == NULL) + { + grub_snprintf (grub_errmsg, sizeof (grub_errmsg), + "Could not allocate memory to store %s", friendly_name); + grub_errno = GRUB_ERR_OUT_OF_MEMORY; + return NULL; + } + + result = asn1_read_value (node, name, tmpstr, &tmpstr_size); + if (result != ASN1_SUCCESS) + { + grub_free (tmpstr); + grub_snprintf (grub_errmsg, sizeof (grub_errmsg), + "Error reading %s: %s", + friendly_name, asn1_strerror (result)); + grub_errno = GRUB_ERR_BAD_FILE_TYPE; + return NULL; + } + + *content_size = tmpstr_size; + + return tmpstr; +} + +int +asn1_init (void) +{ + int res; + res = asn1_array2tree (gnutls_asn1_tab, &_gnutls_gnutls_asn, NULL); + if (res != ASN1_SUCCESS) + { + return res; + } + res = asn1_array2tree (pkix_asn1_tab, &_gnutls_pkix_asn, NULL); + return res; +} diff --git a/grub-core/commands/appendedsig/gnutls_asn1_tab.c b/grub-core/commands/appendedsig/gnutls_asn1_tab.c new file mode 100644 index 0000000000..ddd1314e63 --- /dev/null +++ b/grub-core/commands/appendedsig/gnutls_asn1_tab.c @@ -0,0 +1,121 @@ +#include +#include + +const asn1_static_node gnutls_asn1_tab[] = { + { "GNUTLS", 536872976, NULL }, + { NULL, 1073741836, NULL }, + { "RSAPublicKey", 1610612741, NULL }, + { "modulus", 1073741827, NULL }, + { "publicExponent", 3, NULL }, + { "RSAPrivateKey", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "modulus", 1073741827, NULL }, + { "publicExponent", 1073741827, NULL }, + { "privateExponent", 1073741827, NULL }, + { "prime1", 1073741827, NULL }, + { "prime2", 1073741827, NULL }, + { "exponent1", 1073741827, NULL }, + { "exponent2", 1073741827, NULL }, + { "coefficient", 1073741827, NULL }, + { "otherPrimeInfos", 16386, "OtherPrimeInfos"}, + { "ProvableSeed", 1610612741, NULL }, + { "algorithm", 1073741836, NULL }, + { "seed", 7, NULL }, + { "OtherPrimeInfos", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "OtherPrimeInfo"}, + { "OtherPrimeInfo", 1610612741, NULL }, + { "prime", 1073741827, NULL }, + { "exponent", 1073741827, NULL }, + { "coefficient", 3, NULL }, + { "AlgorithmIdentifier", 1610612741, NULL }, + { "algorithm", 1073741836, NULL }, + { "parameters", 541081613, NULL }, + { "algorithm", 1, NULL }, + { "DigestInfo", 1610612741, NULL }, + { "digestAlgorithm", 1073741826, "DigestAlgorithmIdentifier"}, + { "digest", 7, NULL }, + { "DigestAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, + { "DSAPublicKey", 1073741827, NULL }, + { "DSAParameters", 1610612741, NULL }, + { "p", 1073741827, NULL }, + { "q", 1073741827, NULL }, + { "g", 3, NULL }, + { "DSASignatureValue", 1610612741, NULL }, + { "r", 1073741827, NULL }, + { "s", 3, NULL }, + { "DSAPrivateKey", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "p", 1073741827, NULL }, + { "q", 1073741827, NULL }, + { "g", 1073741827, NULL }, + { "Y", 1073741827, NULL }, + { "priv", 3, NULL }, + { "DHParameter", 1610612741, NULL }, + { "prime", 1073741827, NULL }, + { "base", 1073741827, NULL }, + { "privateValueLength", 16387, NULL }, + { "ECParameters", 1610612754, NULL }, + { "namedCurve", 12, NULL }, + { "ECPrivateKey", 1610612741, NULL }, + { "Version", 1073741827, NULL }, + { "privateKey", 1073741831, NULL }, + { "parameters", 1610637314, "ECParameters"}, + { NULL, 2056, "0"}, + { "publicKey", 536895494, NULL }, + { NULL, 2056, "1"}, + { "PrincipalName", 1610612741, NULL }, + { "name-type", 1610620931, NULL }, + { NULL, 2056, "0"}, + { "name-string", 536879115, NULL }, + { NULL, 1073743880, "1"}, + { NULL, 27, NULL }, + { "KRB5PrincipalName", 1610612741, NULL }, + { "realm", 1610620955, NULL }, + { NULL, 2056, "0"}, + { "principalName", 536879106, "PrincipalName"}, + { NULL, 2056, "1"}, + { "RSAPSSParameters", 1610612741, NULL }, + { "hashAlgorithm", 1610637314, "AlgorithmIdentifier"}, + { NULL, 2056, "0"}, + { "maskGenAlgorithm", 1610637314, "AlgorithmIdentifier"}, + { NULL, 2056, "1"}, + { "saltLength", 1610653699, NULL }, + { NULL, 1073741833, "20"}, + { NULL, 2056, "2"}, + { "trailerField", 536911875, NULL }, + { NULL, 1073741833, "1"}, + { NULL, 2056, "3"}, + { "GOSTParameters", 1610612741, NULL }, + { "publicKeyParamSet", 1073741836, NULL }, + { "digestParamSet", 16396, NULL }, + { "GOSTParametersOld", 1610612741, NULL }, + { "publicKeyParamSet", 1073741836, NULL }, + { "digestParamSet", 1073741836, NULL }, + { "encryptionParamSet", 16396, NULL }, + { "GOSTPrivateKey", 1073741831, NULL }, + { "GOSTPrivateKeyOld", 1073741827, NULL }, + { "IssuerSignTool", 1610612741, NULL }, + { "signTool", 1073741858, NULL }, + { "cATool", 1073741858, NULL }, + { "signToolCert", 1073741858, NULL }, + { "cAToolCert", 34, NULL }, + { "Gost28147-89-EncryptedKey", 1610612741, NULL }, + { "encryptedKey", 1073741831, NULL }, + { "maskKey", 1610637319, NULL }, + { NULL, 4104, "0"}, + { "macKey", 7, NULL }, + { "SubjectPublicKeyInfo", 1610612741, NULL }, + { "algorithm", 1073741826, "AlgorithmIdentifier"}, + { "subjectPublicKey", 6, NULL }, + { "GostR3410-TransportParameters", 1610612741, NULL }, + { "encryptionParamSet", 1073741836, NULL }, + { "ephemeralPublicKey", 1610637314, "SubjectPublicKeyInfo"}, + { NULL, 4104, "0"}, + { "ukm", 7, NULL }, + { "GostR3410-KeyTransport", 536870917, NULL }, + { "sessionEncryptedKey", 1073741826, "Gost28147-89-EncryptedKey"}, + { "transportParameters", 536895490, "GostR3410-TransportParameters"}, + { NULL, 4104, "0"}, + { NULL, 0, NULL } +}; diff --git a/grub-core/commands/appendedsig/pkcs7.c b/grub-core/commands/appendedsig/pkcs7.c new file mode 100644 index 0000000000..dc6afe203f --- /dev/null +++ b/grub-core/commands/appendedsig/pkcs7.c @@ -0,0 +1,305 @@ +/* + * 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 "appendedsig.h" +#include +#include +#include + + +static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + +/* + * RFC 5652 s 5.1 + */ +const char *signedData_oid = "1.2.840.113549.1.7.2"; + +/* + * RFC 4055 s 2.1 + */ +const char *sha256_oid = "2.16.840.1.101.3.4.2.1"; +const char *sha512_oid = "2.16.840.1.101.3.4.2.3"; + +static grub_err_t +process_content (grub_uint8_t * content, int size, + struct pkcs7_signedData *msg) +{ + int res; + asn1_node signed_part; + grub_err_t err = GRUB_ERR_NONE; + char algo_oid[MAX_OID_LEN]; + int algo_oid_size = sizeof (algo_oid); + int algo_count; + char version; + int version_size = sizeof (version); + grub_uint8_t *result_buf; + int result_size = 0; + int crls_size = 0; + gcry_error_t gcry_err; + + res = asn1_create_element (_gnutls_pkix_asn, "PKIX1.pkcs-7-SignedData", + &signed_part); + if (res != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not create ASN.1 structure for PKCS#7 signed part."); + } + + res = asn1_der_decoding2 (&signed_part, content, &size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (res != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Error reading PKCS#7 signed data: %s", asn1_error); + goto cleanup_signed_part; + } + + /* SignedData ::= SEQUENCE { + * version CMSVersion, + * digestAlgorithms DigestAlgorithmIdentifiers, + * encapContentInfo EncapsulatedContentInfo, + * certificates [0] IMPLICIT CertificateSet OPTIONAL, + * crls [1] IMPLICIT RevocationInfoChoices OPTIONAL, + * signerInfos SignerInfos } + */ + + /* version per the algo in 5.1, must be 1 */ + res = asn1_read_value (signed_part, "version", &version, &version_size); + if (res != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Error reading signedData version: %s", + asn1_strerror (res)); + goto cleanup_signed_part; + } + + if (version != 1) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Unexpected signature version v%d, only v1 supported", + version); + goto cleanup_signed_part; + } + + /* + * digestAlgorithms DigestAlgorithmIdentifiers + * + * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier + * DigestAlgorithmIdentifer is an X.509 AlgorithmIdentifier (10.1.1) + * + * RFC 4055 s 2.1: + * sha256Identifier AlgorithmIdentifier ::= { id-sha256, NULL } + * sha512Identifier AlgorithmIdentifier ::= { id-sha512, NULL } + * + * We only support 1 element in the set, and we do not check parameters atm. + */ + res = + asn1_number_of_elements (signed_part, "digestAlgorithms", &algo_count); + if (res != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Error counting number of digest algorithms: %s", + asn1_strerror (res)); + goto cleanup_signed_part; + } + + if (algo_count != 1) + { + err = + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Only 1 digest algorithm is supported"); + goto cleanup_signed_part; + } + + res = + asn1_read_value (signed_part, "digestAlgorithms.?1.algorithm", algo_oid, + &algo_oid_size); + if (res != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Error reading digest algorithm: %s", + asn1_strerror (res)); + goto cleanup_signed_part; + } + + if (grub_strncmp (sha512_oid, algo_oid, algo_oid_size) == 0) + { + msg->hash = grub_crypto_lookup_md_by_name ("sha512"); + } + else if (grub_strncmp (sha256_oid, algo_oid, algo_oid_size) == 0) + { + msg->hash = grub_crypto_lookup_md_by_name ("sha256"); + } + else + { + err = + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Only SHA-256 and SHA-512 hashes are supported, found OID %s", + algo_oid); + goto cleanup_signed_part; + } + + if (!msg->hash) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Hash algorithm for OID %s not loaded", algo_oid); + goto cleanup_signed_part; + } + + /* + * We ignore the certificates, but we don't permit CRLs. + * A CRL entry might be revoking the certificate we're using, and we have + * no way of dealing with that at the moment. + */ + res = asn1_read_value (signed_part, "crls", NULL, &crls_size); + if (res != ASN1_ELEMENT_NOT_FOUND) + { + err = + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "PKCS#7 messages with embedded CRLs are not supported"); + goto cleanup_signed_part; + } + + /* read the signature */ + result_buf = + grub_asn1_allocate_and_read (signed_part, "signerInfos.?1.signature", + "signature data", &result_size); + if (!result_buf) + { + err = grub_errno; + goto cleanup_signed_part; + } + + gcry_err = + gcry_mpi_scan (&(msg->sig_mpi), GCRYMPI_FMT_USG, result_buf, result_size, + NULL); + if (gcry_err != GPG_ERR_NO_ERROR) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Error loading signature into MPI structure: %d", + gcry_err); + goto cleanup_result; + } + +cleanup_result: + grub_free (result_buf); +cleanup_signed_part: + asn1_delete_structure (&signed_part); + + return err; +} + +grub_err_t +parse_pkcs7_signedData (void *sigbuf, grub_size_t data_size, + struct pkcs7_signedData *msg) +{ + int res; + asn1_node content_info; + grub_err_t err = GRUB_ERR_NONE; + char content_oid[MAX_OID_LEN]; + grub_uint8_t *content; + int content_size; + int content_oid_size = sizeof (content_oid); + int size; + + if (data_size > GRUB_INT_MAX) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "Cannot parse a PKCS#7 message where data size > INT_MAX"); + size = (int) data_size; + + res = asn1_create_element (_gnutls_pkix_asn, + "PKIX1.pkcs-7-ContentInfo", &content_info); + if (res != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not create ASN.1 structure for PKCS#7 data: %s", + asn1_strerror (res)); + } + + res = asn1_der_decoding2 (&content_info, sigbuf, &size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (res != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Error decoding PKCS#7 message DER: %s", asn1_error); + goto cleanup; + } + + /* + * ContentInfo ::= SEQUENCE { + * contentType ContentType, + * content [0] EXPLICIT ANY DEFINED BY contentType } + * + * ContentType ::= OBJECT IDENTIFIER + */ + res = + asn1_read_value (content_info, "contentType", content_oid, + &content_oid_size); + if (res != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Error reading PKCS#7 content type: %s", + asn1_strerror (res)); + goto cleanup; + } + + /* OID for SignedData defined in 5.1 */ + if (grub_strncmp (signedData_oid, content_oid, content_oid_size) != 0) + { + err = + grub_error (GRUB_ERR_BAD_SIGNATURE, + "Unexpected content type in PKCS#7 message: OID %s", + content_oid); + goto cleanup; + } + + content = + grub_asn1_allocate_and_read (content_info, "content", + "PKCS#7 message content", &content_size); + if (!content) + { + err = grub_errno; + goto cleanup; + } + + err = process_content (content, content_size, msg); + grub_free (content); + +cleanup: + asn1_delete_structure (&content_info); + return err; +} + +/* + * Release all the storage associated with the PKCS#7 message. + * If the caller dynamically allocated the message, it must free it. + */ +void +pkcs7_signedData_release (struct pkcs7_signedData *msg) +{ + gcry_mpi_release (msg->sig_mpi); +} diff --git a/grub-core/commands/appendedsig/pkix_asn1_tab.c b/grub-core/commands/appendedsig/pkix_asn1_tab.c new file mode 100644 index 0000000000..adef69d95c --- /dev/null +++ b/grub-core/commands/appendedsig/pkix_asn1_tab.c @@ -0,0 +1,484 @@ +#include +#include + +const asn1_static_node pkix_asn1_tab[] = { + { "PKIX1", 536875024, NULL }, + { NULL, 1073741836, NULL }, + { "PrivateKeyUsagePeriod", 1610612741, NULL }, + { "notBefore", 1610637349, NULL }, + { NULL, 4104, "0"}, + { "notAfter", 536895525, NULL }, + { NULL, 4104, "1"}, + { "AuthorityKeyIdentifier", 1610612741, NULL }, + { "keyIdentifier", 1610637319, NULL }, + { NULL, 4104, "0"}, + { "authorityCertIssuer", 1610637314, "GeneralNames"}, + { NULL, 4104, "1"}, + { "authorityCertSerialNumber", 536895490, "CertificateSerialNumber"}, + { NULL, 4104, "2"}, + { "SubjectKeyIdentifier", 1073741831, NULL }, + { "KeyUsage", 1073741830, NULL }, + { "DirectoryString", 1610612754, NULL }, + { "teletexString", 1612709918, NULL }, + { "MAX", 524298, "1"}, + { "printableString", 1612709919, NULL }, + { "MAX", 524298, "1"}, + { "universalString", 1612709920, NULL }, + { "MAX", 524298, "1"}, + { "utf8String", 1612709922, NULL }, + { "MAX", 524298, "1"}, + { "bmpString", 1612709921, NULL }, + { "MAX", 524298, "1"}, + { "ia5String", 538968093, NULL }, + { "MAX", 524298, "1"}, + { "SubjectAltName", 1073741826, "GeneralNames"}, + { "GeneralNames", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "GeneralName"}, + { "GeneralName", 1610612754, NULL }, + { "otherName", 1610620930, "AnotherName"}, + { NULL, 4104, "0"}, + { "rfc822Name", 1610620957, NULL }, + { NULL, 4104, "1"}, + { "dNSName", 1610620957, NULL }, + { NULL, 4104, "2"}, + { "x400Address", 1610620941, NULL }, + { NULL, 4104, "3"}, + { "directoryName", 1610620939, NULL }, + { NULL, 1073743880, "4"}, + { NULL, 2, "RelativeDistinguishedName"}, + { "ediPartyName", 1610620941, NULL }, + { NULL, 4104, "5"}, + { "uniformResourceIdentifier", 1610620957, NULL }, + { NULL, 4104, "6"}, + { "iPAddress", 1610620935, NULL }, + { NULL, 4104, "7"}, + { "registeredID", 536879116, NULL }, + { NULL, 4104, "8"}, + { "AnotherName", 1610612741, NULL }, + { "type-id", 1073741836, NULL }, + { "value", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "type-id", 1, NULL }, + { "IssuerAltName", 1073741826, "GeneralNames"}, + { "BasicConstraints", 1610612741, NULL }, + { "cA", 1610645508, NULL }, + { NULL, 131081, NULL }, + { "pathLenConstraint", 537411587, NULL }, + { "0", 10, "MAX"}, + { "CRLDistributionPoints", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "DistributionPoint"}, + { "DistributionPoint", 1610612741, NULL }, + { "distributionPoint", 1610637314, "DistributionPointName"}, + { NULL, 2056, "0"}, + { "reasons", 1610637314, "ReasonFlags"}, + { NULL, 4104, "1"}, + { "cRLIssuer", 536895490, "GeneralNames"}, + { NULL, 4104, "2"}, + { "DistributionPointName", 1610612754, NULL }, + { "fullName", 1610620930, "GeneralNames"}, + { NULL, 4104, "0"}, + { "nameRelativeToCRLIssuer", 536879106, "RelativeDistinguishedName"}, + { NULL, 4104, "1"}, + { "ReasonFlags", 1073741830, NULL }, + { "ExtKeyUsageSyntax", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 12, NULL }, + { "AuthorityInfoAccessSyntax", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "AccessDescription"}, + { "AccessDescription", 1610612741, NULL }, + { "accessMethod", 1073741836, NULL }, + { "accessLocation", 2, "GeneralName"}, + { "Attribute", 1610612741, NULL }, + { "type", 1073741836, NULL }, + { "values", 536870927, NULL }, + { NULL, 13, NULL }, + { "AttributeTypeAndValue", 1610612741, NULL }, + { "type", 1073741836, NULL }, + { "value", 13, NULL }, + { "Name", 1610612754, NULL }, + { "rdnSequence", 536870923, NULL }, + { NULL, 2, "RelativeDistinguishedName"}, + { "DistinguishedName", 1610612747, NULL }, + { NULL, 2, "RelativeDistinguishedName"}, + { "RelativeDistinguishedName", 1612709903, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "AttributeTypeAndValue"}, + { "Certificate", 1610612741, NULL }, + { "tbsCertificate", 1073741826, "TBSCertificate"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 6, NULL }, + { "TBSCertificate", 1610612741, NULL }, + { "version", 1610653699, NULL }, + { NULL, 1073741833, "0"}, + { NULL, 2056, "0"}, + { "serialNumber", 1073741826, "CertificateSerialNumber"}, + { "signature", 1073741826, "AlgorithmIdentifier"}, + { "issuer", 1073741826, "Name"}, + { "validity", 1073741826, "Validity"}, + { "subject", 1073741826, "Name"}, + { "subjectPublicKeyInfo", 1073741826, "SubjectPublicKeyInfo"}, + { "issuerUniqueID", 1610637314, "UniqueIdentifier"}, + { NULL, 4104, "1"}, + { "subjectUniqueID", 1610637314, "UniqueIdentifier"}, + { NULL, 4104, "2"}, + { "extensions", 536895490, "Extensions"}, + { NULL, 2056, "3"}, + { "CertificateSerialNumber", 1073741827, NULL }, + { "Validity", 1610612741, NULL }, + { "notBefore", 1073741826, "Time"}, + { "notAfter", 2, "Time"}, + { "Time", 1610612754, NULL }, + { "utcTime", 1073741860, NULL }, + { "generalTime", 37, NULL }, + { "UniqueIdentifier", 1073741830, NULL }, + { "SubjectPublicKeyInfo", 1610612741, NULL }, + { "algorithm", 1073741826, "AlgorithmIdentifier"}, + { "subjectPublicKey", 6, NULL }, + { "Extensions", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "Extension"}, + { "Extension", 1610612741, NULL }, + { "extnID", 1073741836, NULL }, + { "critical", 1610645508, NULL }, + { NULL, 131081, NULL }, + { "extnValue", 7, NULL }, + { "CertificateList", 1610612741, NULL }, + { "tbsCertList", 1073741826, "TBSCertList"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 6, NULL }, + { "TBSCertList", 1610612741, NULL }, + { "version", 1073758211, NULL }, + { "signature", 1073741826, "AlgorithmIdentifier"}, + { "issuer", 1073741826, "Name"}, + { "thisUpdate", 1073741826, "Time"}, + { "nextUpdate", 1073758210, "Time"}, + { "revokedCertificates", 1610629131, NULL }, + { NULL, 536870917, NULL }, + { "userCertificate", 1073741826, "CertificateSerialNumber"}, + { "revocationDate", 1073741826, "Time"}, + { "crlEntryExtensions", 16386, "Extensions"}, + { "crlExtensions", 536895490, "Extensions"}, + { NULL, 2056, "0"}, + { "AlgorithmIdentifier", 1610612741, NULL }, + { "algorithm", 1073741836, NULL }, + { "parameters", 541081613, NULL }, + { "algorithm", 1, NULL }, + { "Dss-Sig-Value", 1610612741, NULL }, + { "r", 1073741827, NULL }, + { "s", 3, NULL }, + { "Dss-Parms", 1610612741, NULL }, + { "p", 1073741827, NULL }, + { "q", 1073741827, NULL }, + { "g", 3, NULL }, + { "pkcs-7-ContentInfo", 1610612741, NULL }, + { "contentType", 1073741836, NULL }, + { "content", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "contentType", 1, NULL }, + { "pkcs-7-DigestInfo", 1610612741, NULL }, + { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "digest", 7, NULL }, + { "pkcs-7-SignedData", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "digestAlgorithms", 1073741826, "pkcs-7-DigestAlgorithmIdentifiers"}, + { "encapContentInfo", 1073741826, "pkcs-7-EncapsulatedContentInfo"}, + { "certificates", 1610637314, "pkcs-7-CertificateSet"}, + { NULL, 4104, "0"}, + { "crls", 1610637314, "pkcs-7-CertificateRevocationLists"}, + { NULL, 4104, "1"}, + { "signerInfos", 2, "pkcs-7-SignerInfos"}, + { "pkcs-7-DigestAlgorithmIdentifiers", 1610612751, NULL }, + { NULL, 2, "AlgorithmIdentifier"}, + { "pkcs-7-EncapsulatedContentInfo", 1610612741, NULL }, + { "eContentType", 1073741836, NULL }, + { "eContent", 536895501, NULL }, + { NULL, 2056, "0"}, + { "pkcs-7-CertificateRevocationLists", 1610612751, NULL }, + { NULL, 13, NULL }, + { "pkcs-7-CertificateChoices", 1610612754, NULL }, + { "certificate", 13, NULL }, + { "pkcs-7-CertificateSet", 1610612751, NULL }, + { NULL, 2, "pkcs-7-CertificateChoices"}, + { "IssuerAndSerialNumber", 1610612741, NULL }, + { "issuer", 1073741826, "Name"}, + { "serialNumber", 2, "CertificateSerialNumber"}, + { "pkcs-7-SignerInfo", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "sid", 1073741826, "SignerIdentifier"}, + { "digestAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signedAttrs", 1610637314, "SignedAttributes"}, + { NULL, 4104, "0"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 1073741831, NULL }, + { "unsignedAttrs", 536895490, "SignedAttributes"}, + { NULL, 4104, "1"}, + { "SignedAttributes", 1612709903, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "Attribute"}, + { "SignerIdentifier", 1610612754, NULL }, + { "issuerAndSerialNumber", 1073741826, "IssuerAndSerialNumber"}, + { "subjectKeyIdentifier", 536879111, NULL }, + { NULL, 4104, "0"}, + { "pkcs-7-SignerInfos", 1610612751, NULL }, + { NULL, 2, "pkcs-7-SignerInfo"}, + { "pkcs-10-CertificationRequestInfo", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "subject", 1073741826, "Name"}, + { "subjectPKInfo", 1073741826, "SubjectPublicKeyInfo"}, + { "attributes", 536879106, "Attributes"}, + { NULL, 4104, "0"}, + { "Attributes", 1610612751, NULL }, + { NULL, 2, "Attribute"}, + { "pkcs-10-CertificationRequest", 1610612741, NULL }, + { "certificationRequestInfo", 1073741826, "pkcs-10-CertificationRequestInfo"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 6, NULL }, + { "pkcs-9-at-challengePassword", 1879048204, NULL }, + { "iso", 1073741825, "1"}, + { "member-body", 1073741825, "2"}, + { "us", 1073741825, "840"}, + { "rsadsi", 1073741825, "113549"}, + { "pkcs", 1073741825, "1"}, + { NULL, 1073741825, "9"}, + { NULL, 1, "7"}, + { "pkcs-9-challengePassword", 1610612754, NULL }, + { "printableString", 1073741855, NULL }, + { "utf8String", 34, NULL }, + { "pkcs-9-localKeyId", 1073741831, NULL }, + { "pkcs-8-PrivateKeyInfo", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "privateKeyAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "privateKey", 1073741831, NULL }, + { "attributes", 536895490, "Attributes"}, + { NULL, 4104, "0"}, + { "pkcs-8-EncryptedPrivateKeyInfo", 1610612741, NULL }, + { "encryptionAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "encryptedData", 2, "pkcs-8-EncryptedData"}, + { "pkcs-8-EncryptedData", 1073741831, NULL }, + { "pkcs-5-des-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "8"}, + { "pkcs-5-des-EDE3-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "8"}, + { "pkcs-5-aes128-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "16"}, + { "pkcs-5-aes192-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "16"}, + { "pkcs-5-aes256-CBC-params", 1612709895, NULL }, + { NULL, 1048586, "16"}, + { "Gost28147-89-Parameters", 1610612741, NULL }, + { "iv", 1073741831, NULL }, + { "encryptionParamSet", 12, NULL }, + { "pkcs-5-PBE-params", 1610612741, NULL }, + { "salt", 1073741831, NULL }, + { "iterationCount", 3, NULL }, + { "pkcs-5-PBES2-params", 1610612741, NULL }, + { "keyDerivationFunc", 1073741826, "AlgorithmIdentifier"}, + { "encryptionScheme", 2, "AlgorithmIdentifier"}, + { "pkcs-5-PBKDF2-params", 1610612741, NULL }, + { "salt", 1610612754, NULL }, + { "specified", 1073741831, NULL }, + { "otherSource", 2, "AlgorithmIdentifier"}, + { "iterationCount", 1611137027, NULL }, + { "1", 10, "MAX"}, + { "keyLength", 1611153411, NULL }, + { "1", 10, "MAX"}, + { "prf", 16386, "AlgorithmIdentifier"}, + { "pkcs-12-PFX", 1610612741, NULL }, + { "version", 1610874883, NULL }, + { "v3", 1, "3"}, + { "authSafe", 1073741826, "pkcs-7-ContentInfo"}, + { "macData", 16386, "pkcs-12-MacData"}, + { "pkcs-12-PbeParams", 1610612741, NULL }, + { "salt", 1073741831, NULL }, + { "iterations", 3, NULL }, + { "pkcs-12-MacData", 1610612741, NULL }, + { "mac", 1073741826, "pkcs-7-DigestInfo"}, + { "macSalt", 1073741831, NULL }, + { "iterations", 536903683, NULL }, + { NULL, 9, "1"}, + { "pkcs-12-AuthenticatedSafe", 1610612747, NULL }, + { NULL, 2, "pkcs-7-ContentInfo"}, + { "pkcs-12-SafeContents", 1610612747, NULL }, + { NULL, 2, "pkcs-12-SafeBag"}, + { "pkcs-12-SafeBag", 1610612741, NULL }, + { "bagId", 1073741836, NULL }, + { "bagValue", 1614815245, NULL }, + { NULL, 1073743880, "0"}, + { "badId", 1, NULL }, + { "bagAttributes", 536887311, NULL }, + { NULL, 2, "Attribute"}, + { "pkcs-12-CertBag", 1610612741, NULL }, + { "certId", 1073741836, NULL }, + { "certValue", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "certId", 1, NULL }, + { "pkcs-12-CRLBag", 1610612741, NULL }, + { "crlId", 1073741836, NULL }, + { "crlValue", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "crlId", 1, NULL }, + { "pkcs-12-SecretBag", 1610612741, NULL }, + { "secretTypeId", 1073741836, NULL }, + { "secretValue", 541073421, NULL }, + { NULL, 1073743880, "0"}, + { "secretTypeId", 1, NULL }, + { "pkcs-7-Data", 1073741831, NULL }, + { "pkcs-7-EncryptedData", 1610612741, NULL }, + { "version", 1073741827, NULL }, + { "encryptedContentInfo", 1073741826, "pkcs-7-EncryptedContentInfo"}, + { "unprotectedAttrs", 536895490, "pkcs-7-UnprotectedAttributes"}, + { NULL, 4104, "1"}, + { "pkcs-7-EncryptedContentInfo", 1610612741, NULL }, + { "contentType", 1073741836, NULL }, + { "contentEncryptionAlgorithm", 1073741826, "pkcs-7-ContentEncryptionAlgorithmIdentifier"}, + { "encryptedContent", 536895495, NULL }, + { NULL, 4104, "0"}, + { "pkcs-7-ContentEncryptionAlgorithmIdentifier", 1073741826, "AlgorithmIdentifier"}, + { "pkcs-7-UnprotectedAttributes", 1612709903, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "Attribute"}, + { "ProxyCertInfo", 1610612741, NULL }, + { "pCPathLenConstraint", 1611153411, NULL }, + { "0", 10, "MAX"}, + { "proxyPolicy", 2, "ProxyPolicy"}, + { "ProxyPolicy", 1610612741, NULL }, + { "policyLanguage", 1073741836, NULL }, + { "policy", 16391, NULL }, + { "certificatePolicies", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "PolicyInformation"}, + { "PolicyInformation", 1610612741, NULL }, + { "policyIdentifier", 1073741836, NULL }, + { "policyQualifiers", 538984459, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "PolicyQualifierInfo"}, + { "PolicyQualifierInfo", 1610612741, NULL }, + { "policyQualifierId", 1073741836, NULL }, + { "qualifier", 541065229, NULL }, + { "policyQualifierId", 1, NULL }, + { "CPSuri", 1073741853, NULL }, + { "UserNotice", 1610612741, NULL }, + { "noticeRef", 1073758210, "NoticeReference"}, + { "explicitText", 16386, "DisplayText"}, + { "NoticeReference", 1610612741, NULL }, + { "organization", 1073741826, "DisplayText"}, + { "noticeNumbers", 536870923, NULL }, + { NULL, 3, NULL }, + { "DisplayText", 1610612754, NULL }, + { "ia5String", 1612709917, NULL }, + { "200", 524298, "1"}, + { "visibleString", 1612709923, NULL }, + { "200", 524298, "1"}, + { "bmpString", 1612709921, NULL }, + { "200", 524298, "1"}, + { "utf8String", 538968098, NULL }, + { "200", 524298, "1"}, + { "OCSPRequest", 1610612741, NULL }, + { "tbsRequest", 1073741826, "TBSRequest"}, + { "optionalSignature", 536895490, "Signature"}, + { NULL, 2056, "0"}, + { "TBSRequest", 1610612741, NULL }, + { "version", 1610653699, NULL }, + { NULL, 1073741833, "0"}, + { NULL, 2056, "0"}, + { "requestorName", 1610637314, "GeneralName"}, + { NULL, 2056, "1"}, + { "requestList", 1610612747, NULL }, + { NULL, 2, "Request"}, + { "requestExtensions", 536895490, "Extensions"}, + { NULL, 2056, "2"}, + { "Signature", 1610612741, NULL }, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 1073741830, NULL }, + { "certs", 536895499, NULL }, + { NULL, 1073743880, "0"}, + { NULL, 2, "Certificate"}, + { "Request", 1610612741, NULL }, + { "reqCert", 1073741826, "CertID"}, + { "singleRequestExtensions", 536895490, "Extensions"}, + { NULL, 2056, "0"}, + { "CertID", 1610612741, NULL }, + { "hashAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "issuerNameHash", 1073741831, NULL }, + { "issuerKeyHash", 1073741831, NULL }, + { "serialNumber", 2, "CertificateSerialNumber"}, + { "OCSPResponse", 1610612741, NULL }, + { "responseStatus", 1073741826, "OCSPResponseStatus"}, + { "responseBytes", 536895490, "ResponseBytes"}, + { NULL, 2056, "0"}, + { "OCSPResponseStatus", 1610874901, NULL }, + { "successful", 1073741825, "0"}, + { "malformedRequest", 1073741825, "1"}, + { "internalError", 1073741825, "2"}, + { "tryLater", 1073741825, "3"}, + { "sigRequired", 1073741825, "5"}, + { "unauthorized", 1, "6"}, + { "ResponseBytes", 1610612741, NULL }, + { "responseType", 1073741836, NULL }, + { "response", 7, NULL }, + { "BasicOCSPResponse", 1610612741, NULL }, + { "tbsResponseData", 1073741826, "ResponseData"}, + { "signatureAlgorithm", 1073741826, "AlgorithmIdentifier"}, + { "signature", 1073741830, NULL }, + { "certs", 536895499, NULL }, + { NULL, 1073743880, "0"}, + { NULL, 2, "Certificate"}, + { "ResponseData", 1610612741, NULL }, + { "version", 1610653699, NULL }, + { NULL, 1073741833, "0"}, + { NULL, 2056, "0"}, + { "responderID", 1073741826, "ResponderID"}, + { "producedAt", 1073741861, NULL }, + { "responses", 1610612747, NULL }, + { NULL, 2, "SingleResponse"}, + { "responseExtensions", 536895490, "Extensions"}, + { NULL, 2056, "1"}, + { "ResponderID", 1610612754, NULL }, + { "byName", 1610620939, NULL }, + { NULL, 1073743880, "1"}, + { NULL, 2, "RelativeDistinguishedName"}, + { "byKey", 536879111, NULL }, + { NULL, 2056, "2"}, + { "SingleResponse", 1610612741, NULL }, + { "certID", 1073741826, "CertID"}, + { "certStatus", 1073741826, "CertStatus"}, + { "thisUpdate", 1073741861, NULL }, + { "nextUpdate", 1610637349, NULL }, + { NULL, 2056, "0"}, + { "singleExtensions", 536895490, "Extensions"}, + { NULL, 2056, "1"}, + { "CertStatus", 1610612754, NULL }, + { "good", 1610620948, NULL }, + { NULL, 4104, "0"}, + { "revoked", 1610620930, "RevokedInfo"}, + { NULL, 4104, "1"}, + { "unknown", 536879106, "UnknownInfo"}, + { NULL, 4104, "2"}, + { "RevokedInfo", 1610612741, NULL }, + { "revocationTime", 1073741861, NULL }, + { "revocationReason", 537157653, NULL }, + { NULL, 1073743880, "0"}, + { "unspecified", 1, "0"}, + { "UnknownInfo", 1073741844, NULL }, + { "NameConstraints", 1610612741, NULL }, + { "permittedSubtrees", 1610637314, "GeneralSubtrees"}, + { NULL, 4104, "0"}, + { "excludedSubtrees", 536895490, "GeneralSubtrees"}, + { NULL, 4104, "1"}, + { "GeneralSubtrees", 1612709899, NULL }, + { "MAX", 1074266122, "1"}, + { NULL, 2, "GeneralSubtree"}, + { "GeneralSubtree", 1610612741, NULL }, + { "base", 1073741826, "GeneralName"}, + { "minimum", 1610653699, NULL }, + { NULL, 1073741833, "0"}, + { NULL, 4104, "0"}, + { "maximum", 536895491, NULL }, + { NULL, 4104, "1"}, + { "TlsFeatures", 536870923, NULL }, + { NULL, 3, NULL }, + { NULL, 0, NULL } +}; diff --git a/grub-core/commands/appendedsig/x509.c b/grub-core/commands/appendedsig/x509.c new file mode 100644 index 0000000000..42ec65c54a --- /dev/null +++ b/grub-core/commands/appendedsig/x509.c @@ -0,0 +1,1050 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include "appendedsig.h" + +static char asn1_error[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + +/* + * RFC 3279 2.3.1 RSA Keys + */ +const char *rsaEncryption_oid = "1.2.840.113549.1.1.1"; + +/* + * RFC 5280 Appendix A + */ +const char *commonName_oid = "2.5.4.3"; + +/* + * RFC 5280 4.2.1.3 Key Usage + */ +const char *keyUsage_oid = "2.5.29.15"; + +/* + * RFC 5280 4.2.1.9 Basic Constraints + */ +const char *basicConstraints_oid = "2.5.29.19"; + +/* + * RFC 5280 4.2.1.12 Extended Key Usage + */ +const char *extendedKeyUsage_oid = "2.5.29.37"; +const char *codeSigningUsage_oid = "1.3.6.1.5.5.7.3.3"; + +/* + * RFC 3279 2.3.1 + * + * The RSA public key MUST be encoded using the ASN.1 type RSAPublicKey: + * + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER } -- e + * + * where modulus is the modulus n, and publicExponent is the public + * exponent e. + */ +static grub_err_t +grub_parse_rsa_pubkey (grub_uint8_t * der, int dersize, + struct x509_certificate *certificate) +{ + int result; + asn1_node spk = ASN1_TYPE_EMPTY; + grub_uint8_t *m_data, *e_data; + int m_size, e_size; + grub_err_t err = GRUB_ERR_NONE; + gcry_error_t gcry_err; + + result = + asn1_create_element (_gnutls_gnutls_asn, "GNUTLS.RSAPublicKey", &spk); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Cannot create storage for public key ASN.1 data"); + } + + result = asn1_der_decoding2 (&spk, der, &dersize, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Cannot decode certificate public key DER: %s", + asn1_error); + goto cleanup; + } + + m_data = + grub_asn1_allocate_and_read (spk, "modulus", "RSA modulus", &m_size); + if (!m_data) + { + err = grub_errno; + goto cleanup; + } + + e_data = + grub_asn1_allocate_and_read (spk, "publicExponent", "RSA public exponent", + &e_size); + if (!e_data) + { + err = grub_errno; + goto cleanup_m_data; + } + + /* + * convert m, e to mpi + * + * nscanned is not set for FMT_USG, it's only set for FMT_PGP, + * so we can't verify it + */ + gcry_err = + gcry_mpi_scan (&certificate->mpis[0], GCRYMPI_FMT_USG, m_data, m_size, + NULL); + if (gcry_err != GPG_ERR_NO_ERROR) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error loading RSA modulus into MPI structure: %d", + gcry_err); + goto cleanup_e_data; + } + + gcry_err = + gcry_mpi_scan (&certificate->mpis[1], GCRYMPI_FMT_USG, e_data, e_size, + NULL); + if (gcry_err != GPG_ERR_NO_ERROR) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error loading RSA exponent into MPI structure: %d", + gcry_err); + goto cleanup_m_mpi; + } + + grub_free (e_data); + grub_free (m_data); + asn1_delete_structure (&spk); + return GRUB_ERR_NONE; + +cleanup_m_mpi: + gcry_mpi_release (certificate->mpis[0]); +cleanup_e_data: + grub_free (e_data); +cleanup_m_data: + grub_free (m_data); +cleanup: + asn1_delete_structure (&spk); + return err; +} + + +/* + * RFC 5280: + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + * + * AlgorithmIdentifiers come from RFC 3279, we are not strictly compilant as we + * only support RSA Encryption. + */ + +static grub_err_t +grub_x509_read_subject_public_key (asn1_node asn, + struct x509_certificate *results) +{ + int result; + grub_err_t err; + const char *algo_name = + "tbsCertificate.subjectPublicKeyInfo.algorithm.algorithm"; + const char *params_name = + "tbsCertificate.subjectPublicKeyInfo.algorithm.parameters"; + const char *pk_name = + "tbsCertificate.subjectPublicKeyInfo.subjectPublicKey"; + char algo_oid[MAX_OID_LEN]; + int algo_size = sizeof (algo_oid); + char params_value[2]; + int params_size = sizeof (params_value); + grub_uint8_t *key_data = NULL; + int key_size = 0; + unsigned int key_type; + + /* algorithm: see notes for rsaEncryption_oid */ + result = asn1_read_value (asn, algo_name, algo_oid, &algo_size); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading x509 public key algorithm: %s", + asn1_strerror (result)); + } + + if (grub_strncmp (algo_oid, rsaEncryption_oid, sizeof (rsaEncryption_oid)) + != 0) + { + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Unsupported x509 public key algorithm: %s", + algo_oid); + } + + /* + * RFC 3279 2.3.1 + * The rsaEncryption OID is intended to be used in the algorithm field + * of a value of type AlgorithmIdentifier. The parameters field MUST + * have ASN.1 type NULL for this algorithm identifier. + */ + result = asn1_read_value (asn, params_name, params_value, ¶ms_size); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading x509 public key parameters: %s", + asn1_strerror (result)); + } + + if (params_value[0] != ASN1_TAG_NULL) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Invalid x509 public key parameters: expected NULL"); + } + + /* + * RFC 3279 2.3.1: The DER encoded RSAPublicKey is the value of the BIT + * STRING subjectPublicKey. + */ + result = asn1_read_value_type (asn, pk_name, NULL, &key_size, &key_type); + if (result != ASN1_MEM_ERROR) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading size of x509 public key: %s", + asn1_strerror (result)); + } + if (key_type != ASN1_ETYPE_BIT_STRING) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Unexpected ASN.1 type when reading x509 public key: %x", + key_type); + } + + /* length is in bits */ + key_size = (key_size + 7) / 8; + + key_data = grub_malloc (key_size); + if (!key_data) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Out of memory for x509 public key"); + } + + result = asn1_read_value (asn, pk_name, key_data, &key_size); + if (result != ASN1_SUCCESS) + { + grub_free (key_data); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading public key data"); + } + key_size = (key_size + 7) / 8; + + err = grub_parse_rsa_pubkey (key_data, key_size, results); + grub_free (key_data); + + return err; +} + +/* Decode a string as defined in Appendix A */ +static grub_err_t +decode_string (char *der, int der_size, char **string, + grub_size_t * string_size) +{ + asn1_node strasn; + int result; + char *choice; + int choice_size = 0; + int tmp_size = 0; + grub_err_t err = GRUB_ERR_NONE; + + result = + asn1_create_element (_gnutls_pkix_asn, "PKIX1.DirectoryString", &strasn); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not create ASN.1 structure for certificate: %s", + asn1_strerror (result)); + } + + result = asn1_der_decoding2 (&strasn, der, &der_size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Could not parse DER for DirectoryString: %s", + asn1_error); + goto cleanup; + } + + choice = + grub_asn1_allocate_and_read (strasn, "", "DirectoryString choice", + &choice_size); + if (!choice) + { + err = grub_errno; + goto cleanup; + } + + if (grub_strncmp ("utf8String", choice, choice_size)) + { + err = + grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "Only UTF-8 DirectoryStrings are supported, got %s", + choice); + goto cleanup_choice; + } + + result = asn1_read_value (strasn, "utf8String", NULL, &tmp_size); + if (result != ASN1_MEM_ERROR) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading size of UTF-8 string: %s", + asn1_strerror (result)); + goto cleanup_choice; + } + + /* read size does not include trailing null */ + tmp_size++; + + *string = grub_malloc (tmp_size); + if (!*string) + { + err = + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Cannot allocate memory for DirectoryString contents"); + goto cleanup_choice; + } + + result = asn1_read_value (strasn, "utf8String", *string, &tmp_size); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading out UTF-8 string in DirectoryString: %s", + asn1_strerror (result)); + grub_free (*string); + goto cleanup_choice; + } + *string_size = tmp_size + 1; + (*string)[tmp_size] = '\0'; + +cleanup_choice: + grub_free (choice); +cleanup: + asn1_delete_structure (&strasn); + return err; +} + +/* + * TBSCertificate ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1, + * ... + * + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + */ +static grub_err_t +check_version (asn1_node certificate) +{ + int rc; + const char *name = "tbsCertificate.version"; + grub_uint8_t version; + int len = 1; + + rc = asn1_read_value (certificate, name, &version, &len); + + /* require version 3 */ + if (rc != ASN1_SUCCESS || len != 1) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading certificate version"); + + if (version != 0x02) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Invalid x509 certificate version, expected v3 (0x02), got 0x%02x", + version); + + return GRUB_ERR_NONE; +} + +/* + * This is an X.501 Name, which is complex. + * + * For simplicity, we extract only the CN. + */ +static grub_err_t +read_name (asn1_node asn, const char *name_path, char **name, + grub_size_t * name_size) +{ + int seq_components, set_components; + int result; + int i, j; + char *top_path, *set_path, *type_path, *val_path; + char type[MAX_OID_LEN]; + int type_len = sizeof (type); + int string_size = 0; + char *string_der; + grub_err_t err; + + *name = NULL; + + top_path = grub_xasprintf ("%s.rdnSequence", name_path); + if (!top_path) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not allocate memory for %s name parsing path", + name_path); + + result = asn1_number_of_elements (asn, top_path, &seq_components); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error counting name components: %s", + asn1_strerror (result)); + goto cleanup; + } + + for (i = 1; i <= seq_components; i++) + { + set_path = grub_xasprintf ("%s.?%d", top_path, i); + if (!set_path) + { + err = + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not allocate memory for %s name set parsing path", + name_path); + goto cleanup_set; + } + /* this brings us, hopefully, to a set */ + result = asn1_number_of_elements (asn, set_path, &set_components); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error counting name sub-components components (element %d): %s", + i, asn1_strerror (result)); + goto cleanup_set; + } + for (j = 1; j <= set_components; j++) + { + type_path = grub_xasprintf ("%s.?%d.?%d.type", top_path, i, j); + if (!type_path) + { + err = + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not allocate memory for %s name component type path", + name_path); + goto cleanup_set; + } + type_len = sizeof (type); + result = asn1_read_value (asn, type_path, type, &type_len); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading %s name component type: %s", + name_path, asn1_strerror (result)); + goto cleanup_type; + } + + if (grub_strncmp (type, commonName_oid, type_len) != 0) + { + grub_free (type_path); + continue; + } + + val_path = grub_xasprintf ("%s.?%d.?%d.value", top_path, i, j); + if (!val_path) + { + err = + grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not allocate memory for %s name component value path", + name_path); + goto cleanup_set; + } + + string_der = + grub_asn1_allocate_and_read (asn, val_path, name_path, + &string_size); + if (!string_der) + { + err = grub_errno; + goto cleanup_val_path; + } + + err = decode_string (string_der, string_size, name, name_size); + if (err) + goto cleanup_string; + + grub_free (string_der); + grub_free (type_path); + grub_free (val_path); + break; + } + grub_free (set_path); + + if (*name) + break; + } + + return GRUB_ERR_NONE; + +cleanup_string: + grub_free (string_der); +cleanup_val_path: + grub_free (val_path); +cleanup_type: + grub_free (type_path); +cleanup_set: + grub_free (set_path); +cleanup: + grub_free (top_path); + return err; +} + +/* + * details here + */ +static grub_err_t +verify_key_usage (grub_uint8_t * value, int value_size) +{ + asn1_node usageasn; + int result; + grub_err_t err = GRUB_ERR_NONE; + grub_uint8_t usage = 0xff; + int usage_size = 1; + + result = + asn1_create_element (_gnutls_pkix_asn, "PKIX1.KeyUsage", &usageasn); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not create ASN.1 structure for key usage"); + } + + result = asn1_der_decoding2 (&usageasn, value, &value_size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error parsing DER for Key Usage: %s", asn1_error); + goto cleanup; + } + + result = asn1_read_value (usageasn, "", &usage, &usage_size); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading Key Usage value: %s", + asn1_strerror (result)); + goto cleanup; + } + + /* Only the first bit is permitted to be set */ + if (usage != 0x80) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected Key Usage value: %x", + usage); + goto cleanup; + } + +cleanup: + asn1_delete_structure (&usageasn); + return err; +} + +/* + * BasicConstraints ::= SEQUENCE { + * cA BOOLEAN DEFAULT FALSE, + * pathLenConstraint INTEGER (0..MAX) OPTIONAL } + */ +static grub_err_t +verify_basic_constraints (grub_uint8_t * value, int value_size) +{ + asn1_node basicasn; + int result; + grub_err_t err = GRUB_ERR_NONE; + char cA[6]; /* FALSE or TRUE */ + int cA_size = sizeof (cA); + + result = + asn1_create_element (_gnutls_pkix_asn, "PKIX1.BasicConstraints", + &basicasn); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not create ASN.1 structure for Basic Constraints"); + } + + result = asn1_der_decoding2 (&basicasn, value, &value_size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error parsing DER for Basic Constraints: %s", + asn1_error); + goto cleanup; + } + + result = asn1_read_value (basicasn, "cA", cA, &cA_size); + if (result == ASN1_ELEMENT_NOT_FOUND) + { + /* Not present, default is False, so this is OK */ + err = GRUB_ERR_NONE; + goto cleanup; + } + else if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading Basic Constraints cA value: %s", + asn1_strerror (result)); + goto cleanup; + } + + /* The certificate must not be a CA certificate */ + if (grub_strncmp ("FALSE", cA, cA_size) != 0) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "Unexpected CA value: %s", + cA); + goto cleanup; + } + +cleanup: + asn1_delete_structure (&basicasn); + return err; +} + +/* + * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + * + * KeyPurposeId ::= OBJECT IDENTIFIER + */ +static grub_err_t +verify_extended_key_usage (grub_uint8_t * value, int value_size) +{ + asn1_node extendedasn; + int result, count; + grub_err_t err = GRUB_ERR_NONE; + char usage[MAX_OID_LEN]; + int usage_size = sizeof (usage); + + result = + asn1_create_element (_gnutls_pkix_asn, "PKIX1.ExtKeyUsageSyntax", + &extendedasn); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not create ASN.1 structure for Extended Key Usage"); + } + + result = asn1_der_decoding2 (&extendedasn, value, &value_size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error parsing DER for Extended Key Usage: %s", + asn1_error); + goto cleanup; + } + + /* + * If EKUs are present, there must be exactly 1 and it must be a + * codeSigning usage. + */ + result = asn1_number_of_elements(extendedasn, "", &count); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error counting number of Extended Key Usages: %s", + asn1_strerror (result)); + goto cleanup; + } + + result = asn1_read_value (extendedasn, "?1", usage, &usage_size); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading Extended Key Usage: %s", + asn1_strerror (result)); + goto cleanup; + } + + if (grub_strncmp (codeSigningUsage_oid, usage, usage_size) != 0) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Unexpected Extended Key Usage OID, got: %s", + usage); + goto cleanup; + } + +cleanup: + asn1_delete_structure (&extendedasn); + return err; +} + +/* + * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + * + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING + * -- contains the DER encoding of an ASN.1 value + * -- corresponding to the extension type identified + * -- by extnID + * } + * + * We require that a certificate: + * - contain the Digital Signature usage only + * - not be a CA + * - MUST not contain any other critical extensions (RFC 5280 s 4.2) + */ +static grub_err_t +verify_extensions (asn1_node cert) +{ + int result; + int ext, num_extensions = 0; + int usage_present = 0, constraints_present = 0, extended_usage_present = 0; + char *oid_path, *critical_path, *value_path; + char extnID[MAX_OID_LEN]; + int extnID_size; + grub_err_t err; + char critical[6]; /* we get either "TRUE" or "FALSE" */ + int critical_size; + grub_uint8_t *value; + int value_size; + + result = + asn1_number_of_elements (cert, "tbsCertificate.extensions", + &num_extensions); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error counting number of extensions: %s", + asn1_strerror (result)); + } + + if (num_extensions < 2) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Insufficient number of extensions for certificate, need at least 2, got %d", + num_extensions); + } + + for (ext = 1; ext <= num_extensions; ext++) + { + oid_path = grub_xasprintf ("tbsCertificate.extensions.?%d.extnID", ext); + + extnID_size = sizeof (extnID); + result = asn1_read_value (cert, oid_path, extnID, &extnID_size); + if (result != GRUB_ERR_NONE) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading extension OID: %s", + asn1_strerror (result)); + goto cleanup_oid_path; + } + + critical_path = + grub_xasprintf ("tbsCertificate.extensions.?%d.critical", ext); + critical_size = sizeof (critical); + result = + asn1_read_value (cert, critical_path, critical, &critical_size); + if (result == ASN1_ELEMENT_NOT_FOUND) + { + critical[0] = '\0'; + } + else if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Error reading extension criticality: %s", + asn1_strerror (result)); + goto cleanup_critical_path; + } + + value_path = + grub_xasprintf ("tbsCertificate.extensions.?%d.extnValue", ext); + value = + grub_asn1_allocate_and_read (cert, value_path, + "certificate extension value", + &value_size); + if (!value) + { + err = grub_errno; + goto cleanup_value_path; + } + + /* + * Now we must see if we recognise the OID. + * If we have an unrecognised critical extension we MUST bail. + */ + if (grub_strncmp (keyUsage_oid, extnID, extnID_size) == 0) + { + err = verify_key_usage (value, value_size); + if (err != GRUB_ERR_NONE) + { + goto cleanup_value; + } + usage_present++; + } + else if (grub_strncmp (basicConstraints_oid, extnID, extnID_size) == 0) + { + err = verify_basic_constraints (value, value_size); + if (err != GRUB_ERR_NONE) + { + goto cleanup_value; + } + constraints_present++; + } + else if (grub_strncmp (extendedKeyUsage_oid, extnID, extnID_size) == 0) + { + err = verify_extended_key_usage (value, value_size); + if (err != GRUB_ERR_NONE) + { + goto cleanup_value; + } + extended_usage_present++; + } + else if (grub_strncmp ("TRUE", critical, critical_size) == 0) + { + /* + * per the RFC, we must not process a certificate with + * a critical extension we do not understand. + */ + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Unhandled critical x509 extension with OID %s", + extnID); + goto cleanup_value; + } + + grub_free (value); + grub_free (value_path); + grub_free (critical_path); + grub_free (oid_path); + } + + if (usage_present != 1) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Unexpected number of Key Usage extensions - expected 1, got %d", + usage_present); + } + if (constraints_present != 1) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Unexpected number of basic constraints extensions - expected 1, got %d", + constraints_present); + } + if (extended_usage_present > 1) + { + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Unexpected number of Extended Key Usage extensions - expected 0 or 1, got %d", + extended_usage_present); + } + return GRUB_ERR_NONE; + +cleanup_value: + grub_free (value); +cleanup_value_path: + grub_free (value_path); +cleanup_critical_path: + grub_free (critical_path); +cleanup_oid_path: + grub_free (oid_path); + return err; +} + +/* + * Parse a certificate whose DER-encoded form is in @data, of size @data_size. + * Return the results in @results, which must point to an allocated x509 certificate. + */ +grub_err_t +certificate_import (void *data, grub_size_t data_size, + struct x509_certificate *results) +{ + int result = 0; + asn1_node cert; + grub_err_t err; + int size; + int tmp_size; + + if (data_size > GRUB_INT_MAX) + return grub_error (GRUB_ERR_OUT_OF_RANGE, + "Cannot parse a certificate where data size > INT_MAX"); + size = (int) data_size; + + result = asn1_create_element (_gnutls_pkix_asn, "PKIX1.Certificate", &cert); + if (result != ASN1_SUCCESS) + { + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "Could not create ASN.1 structure for certificate: %s", + asn1_strerror (result)); + } + + result = asn1_der_decoding2 (&cert, data, &size, + ASN1_DECODE_FLAG_STRICT_DER, asn1_error); + if (result != ASN1_SUCCESS) + { + err = + grub_error (GRUB_ERR_BAD_FILE_TYPE, + "Could not parse DER for certificate: %s", asn1_error); + goto cleanup; + } + + /* + * TBSCertificate ::= SEQUENCE { + * version [0] EXPLICIT Version DEFAULT v1 + */ + err = check_version (cert); + if (err != GRUB_ERR_NONE) + { + goto cleanup; + } + + /* + * serialNumber CertificateSerialNumber, + * + * CertificateSerialNumber ::= INTEGER + */ + results->serial = + grub_asn1_allocate_and_read (cert, "tbsCertificate.serialNumber", + "certificate serial number", &tmp_size); + if (!results->serial) + { + err = grub_errno; + goto cleanup; + } + /* + * It's safe to cast the signed int to an unsigned here, we know + * length is non-negative + */ + results->serial_len = tmp_size; + + /* + * signature AlgorithmIdentifier, + * + * We don't load the signature or issuer at the moment, + * as we don't attempt x509 verification. + */ + + /* + * issuer Name, + * + * The RFC only requires the serial number to be unique within + * issuers, so to avoid ambiguity we _technically_ ought to make + * this available. + */ + + /* + * validity Validity, + * + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time } + * + * We can't validate this reasonably, we have no true time source on several + * platforms. For now we do not parse them. + */ + + /* + * subject Name, + * + * This is an X501 name, we parse out just the CN. + */ + err = + read_name (cert, "tbsCertificate.subject", &results->subject, + &results->subject_len); + if (err != GRUB_ERR_NONE) + goto cleanup_serial; + + /* + * TBSCertificate ::= SEQUENCE { + * ... + * subjectPublicKeyInfo SubjectPublicKeyInfo, + * ... + */ + err = grub_x509_read_subject_public_key (cert, results); + if (err != GRUB_ERR_NONE) + goto cleanup_name; + + /* + * TBSCertificate ::= SEQUENCE { + * ... + * extensions [3] EXPLICIT Extensions OPTIONAL + * -- If present, version MUST be v3 + * } + */ + + err = verify_extensions (cert); + if (err != GRUB_ERR_NONE) + goto cleanup_name; + + + /* + * We do not read or check the signature on the certificate: + * as discussed we do not try to validate the certificate but trust + * it implictly. + */ + + asn1_delete_structure (&cert); + return GRUB_ERR_NONE; + + +cleanup_name: + grub_free (results->subject); +cleanup_serial: + grub_free (results->serial); +cleanup: + asn1_delete_structure (&cert); + return err; +} + +/* + * Release all the storage associated with the x509 certificate. + * If the caller dynamically allocated the certificate, it must free it. + * The caller is also responsible for maintenance of the linked list. + */ +void +certificate_release (struct x509_certificate *cert) +{ + grub_free (cert->subject); + grub_free (cert->serial); + gcry_mpi_release (cert->mpis[0]); + gcry_mpi_release (cert->mpis[1]); +} diff --git a/grub-core/lib/backtrace.c b/grub-core/commands/backtrace.c similarity index 97% rename from grub-core/lib/backtrace.c rename to grub-core/commands/backtrace.c index 825a8800e2..8b5ec3913b 100644 --- a/grub-core/lib/backtrace.c +++ b/grub-core/commands/backtrace.c @@ -29,6 +29,7 @@ GRUB_MOD_LICENSE ("GPLv3+"); void grub_backtrace_print_address (void *addr) { +#ifndef GRUB_UTIL grub_dl_t mod; FOR_DL_MODULES (mod) @@ -44,6 +45,7 @@ grub_backtrace_print_address (void *addr) } } +#endif grub_printf ("%p", addr); } @@ -52,7 +54,7 @@ grub_cmd_backtrace (grub_command_t cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) { - grub_backtrace (); + grub_backtrace (1); return 0; } diff --git a/grub-core/commands/bli.c b/grub-core/commands/bli.c index e0d8a54f7b..298c5f70a8 100644 --- a/grub-core/commands/bli.c +++ b/grub-core/commands/bli.c @@ -48,6 +48,22 @@ get_part_uuid (const char *device_name, char **part_uuid) if (device == NULL) return grub_error (grub_errno, N_("cannot open device: %s"), device_name); + if (device->disk == NULL) + { + grub_dprintf ("bli", "%s is not a disk device, partuuid skipped\n", device_name); + *part_uuid = NULL; + grub_device_close (device); + return GRUB_ERR_NONE; + } + + if (device->disk->partition == NULL) + { + grub_dprintf ("bli", "%s has no partition, partuuid skipped\n", device_name); + *part_uuid = NULL; + grub_device_close (device); + return GRUB_ERR_NONE; + } + disk = grub_disk_open (device->disk->name); if (disk == NULL) { @@ -99,7 +115,7 @@ set_loader_device_part_uuid (void) status = get_part_uuid (device_name, &part_uuid); - if (status == GRUB_ERR_NONE) + if (status == GRUB_ERR_NONE && part_uuid) status = grub_efi_set_variable_to_string ("LoaderDevicePartUUID", &bli_vendor_guid, part_uuid, GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | GRUB_EFI_VARIABLE_RUNTIME_ACCESS); @@ -117,4 +133,6 @@ GRUB_MOD_INIT (bli) GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | GRUB_EFI_VARIABLE_RUNTIME_ACCESS); set_loader_device_part_uuid (); + /* No error here is critical, other than being logged */ + grub_print_error (); } diff --git a/grub-core/commands/blscfg.c b/grub-core/commands/blscfg.c new file mode 100644 index 0000000000..6fc621400a --- /dev/null +++ b/grub-core/commands/blscfg.c @@ -0,0 +1,1253 @@ +/*-*- Mode: C; c-basic-offset: 2; indent-tabs-mode: t -*-*/ + +/* bls.c - implementation of the boot loader spec */ + +/* + * GRUB -- GRand Unified Bootloader + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#include "loadenv.h" + +#define GRUB_BLS_CONFIG_PATH "/loader/entries/" + +#ifdef GRUB_MACHINE_EMU +#define GRUB_BOOT_DEVICE "/boot" +#else +#define GRUB_BOOT_DEVICE "($root)" +#endif + +struct keyval +{ + const char *key; + char *val; +}; + +static struct bls_entry *entries = NULL; + +/* Cache probing in frob_boot_device(). Used for linux entry also. + * Always true in non-emu, meaning to prefix things with GRUB_BOOT_DEVICE. */ +static int separate_boot = -1; + +#define FOR_BLS_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries) + +/* BLS appears to make paths relative to the filesystem that snippets are + * on, not /. Attempt to cope. */ +static char *frob_boot_device(char *tmp) +{ +#ifdef GRUB_MACHINE_EMU + grub_file_t f; + char *line = NULL; + + if (separate_boot != -1) + goto probed; + + separate_boot = 0; + + f = grub_file_open ("/proc/mounts", GRUB_FILE_TYPE_CONFIG); + if (f == NULL) + goto probed; + + while ((line = grub_file_getline (f))) + { + if (grub_strstr (line, " " GRUB_BOOT_DEVICE " ")) + { + separate_boot = 1; + grub_free (line); + break; + } + + grub_free(line); + } + + grub_file_close (f); + probed: + if (!separate_boot) + return grub_stpcpy (tmp, " "); +#endif + + return grub_stpcpy (tmp, " " GRUB_BOOT_DEVICE); +} + +static int bls_add_keyval(struct bls_entry *entry, char *key, char *val) +{ + char *k, *v; + struct keyval **kvs, *kv; + int new_n = entry->nkeyvals + 1; + + kvs = grub_realloc (entry->keyvals, new_n * sizeof (struct keyval *)); + if (!kvs) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "couldn't find space for BLS entry"); + entry->keyvals = kvs; + + kv = grub_malloc (sizeof (struct keyval)); + if (!kv) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "couldn't find space for BLS entry"); + + k = grub_strdup (key); + if (!k) + { + grub_free (kv); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "couldn't find space for BLS entry"); + } + + v = grub_strdup (val); + if (!v) + { + grub_free (k); + grub_free (kv); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, + "couldn't find space for BLS entry"); + } + + kv->key = k; + kv->val = v; + + entry->keyvals[entry->nkeyvals] = kv; + grub_dprintf("blscfg", "new keyval at %p:%s:%s\n", entry->keyvals[entry->nkeyvals], k, v); + entry->nkeyvals = new_n; + + return 0; +} + +/* Find they value of the key named by keyname. If there are allowed to be + * more than one, pass a pointer to an int set to -1 the first time, and pass + * the same pointer through each time after, and it'll return them in sorted + * order as defined in the BLS fragment file */ +static char *bls_get_val(struct bls_entry *entry, const char *keyname, int *last) +{ + int idx, start = 0; + struct keyval *kv = NULL; + + if (last) + start = *last + 1; + + for (idx = start; idx < entry->nkeyvals; idx++) { + kv = entry->keyvals[idx]; + + if (!grub_strcmp (keyname, kv->key)) + break; + } + + if (idx == entry->nkeyvals) { + if (last) + *last = -1; + return NULL; + } + + if (last) + *last = idx; + + return kv->val; +} + +#define goto_return(x) ({ ret = (x); goto finish; }) + +/* compare alpha and numeric segments of two versions */ +/* return 1: a is newer than b */ +/* 0: a and b are the same version */ +/* -1: b is newer than a */ +static int vercmp(const char * a, const char * b) +{ + char oldch1, oldch2; + char *abuf, *bbuf; + char *str1, *str2; + char * one, * two; + int rc; + int isnum; + int ret = 0; + + grub_dprintf("blscfg", "%s comparing %s and %s\n", __func__, a, b); + if (!grub_strcmp(a, b)) + return 0; + + abuf = grub_malloc(grub_strlen(a) + 1); + bbuf = grub_malloc(grub_strlen(b) + 1); + str1 = abuf; + str2 = bbuf; + grub_strcpy(str1, a); + grub_strcpy(str2, b); + + one = str1; + two = str2; + + /* loop through each version segment of str1 and str2 and compare them */ + while (*one || *two) { + while (*one && !grub_isalnum(*one) && *one != '~' && *one != '+') one++; + while (*two && !grub_isalnum(*two) && *two != '~' && *two != '+') two++; + + /* handle the tilde separator, it sorts before everything else */ + if (*one == '~' || *two == '~') { + if (*one != '~') goto_return (1); + if (*two != '~') goto_return (-1); + one++; + two++; + continue; + } + + /* + * Handle plus separator. Concept is the same as tilde, + * except that if one of the strings ends (base version), + * the other is considered as higher version. + */ + if (*one == '+' || *two == '+') { + if (!*one) return -1; + if (!*two) return 1; + if (*one != '+') goto_return (1); + if (*two != '+') goto_return (-1); + one++; + two++; + continue; + } + + /* If we ran to the end of either, we are finished with the loop */ + if (!(*one && *two)) break; + + str1 = one; + str2 = two; + + /* grab first completely alpha or completely numeric segment */ + /* leave one and two pointing to the start of the alpha or numeric */ + /* segment and walk str1 and str2 to end of segment */ + if (grub_isdigit(*str1)) { + while (*str1 && grub_isdigit(*str1)) str1++; + while (*str2 && grub_isdigit(*str2)) str2++; + isnum = 1; + } else { + while (*str1 && grub_isalpha(*str1)) str1++; + while (*str2 && grub_isalpha(*str2)) str2++; + isnum = 0; + } + + /* save character at the end of the alpha or numeric segment */ + /* so that they can be restored after the comparison */ + oldch1 = *str1; + *str1 = '\0'; + oldch2 = *str2; + *str2 = '\0'; + + /* this cannot happen, as we previously tested to make sure that */ + /* the first string has a non-null segment */ + if (one == str1) goto_return(-1); /* arbitrary */ + + /* take care of the case where the two version segments are */ + /* different types: one numeric, the other alpha (i.e. empty) */ + /* numeric segments are always newer than alpha segments */ + /* XXX See patch #60884 (and details) from bugzilla #50977. */ + if (two == str2) goto_return (isnum ? 1 : -1); + + if (isnum) { + grub_size_t onelen, twolen; + /* this used to be done by converting the digit segments */ + /* to ints using atoi() - it's changed because long */ + /* digit segments can overflow an int - this should fix that. */ + + /* throw away any leading zeros - it's a number, right? */ + while (*one == '0') one++; + while (*two == '0') two++; + + /* whichever number has more digits wins */ + onelen = grub_strlen(one); + twolen = grub_strlen(two); + if (onelen > twolen) goto_return (1); + if (twolen > onelen) goto_return (-1); + } + + /* grub_strcmp will return which one is greater - even if the two */ + /* segments are alpha or if they are numeric. don't return */ + /* if they are equal because there might be more segments to */ + /* compare */ + rc = grub_strcmp(one, two); + if (rc) goto_return (rc < 1 ? -1 : 1); + + /* restore character that was replaced by null above */ + *str1 = oldch1; + one = str1; + *str2 = oldch2; + two = str2; + } + + /* this catches the case where all numeric and alpha segments have */ + /* compared identically but the segment sepparating characters were */ + /* different */ + if ((!*one) && (!*two)) goto_return (0); + + /* whichever version still has characters left over wins */ + if (!*one) goto_return (-1); else goto_return (1); + +finish: + grub_free (abuf); + grub_free (bbuf); + return ret; +} + +/* returns name/version/release */ +/* NULL string pointer returned if nothing found */ +static void +split_package_string (char *package_string, char **name, + char **version, char **release) +{ + char *package_version, *package_release; + + /* Release */ + package_release = grub_strrchr (package_string, '-'); + + if (package_release != NULL) + *package_release++ = '\0'; + + *release = package_release; + + if (name == NULL) + { + *version = package_string; + } + else + { + /* Version */ + package_version = grub_strrchr(package_string, '-'); + + if (package_version != NULL) + *package_version++ = '\0'; + + *version = package_version; + /* Name */ + *name = package_string; + } + + /* Bubble up non-null values from release to name */ + if (name != NULL && *name == NULL) + { + *name = (*version == NULL ? *release : *version); + *version = *release; + *release = NULL; + } + if (*version == NULL) + { + *version = *release; + *release = NULL; + } +} + +static int +split_cmp(char *nvr0, char *nvr1, int has_name) +{ + int ret = 0; + char *name0, *version0, *release0; + char *name1, *version1, *release1; + + split_package_string(nvr0, has_name ? &name0 : NULL, &version0, &release0); + split_package_string(nvr1, has_name ? &name1 : NULL, &version1, &release1); + + if (has_name) + { + ret = vercmp(name0 == NULL ? "" : name0, + name1 == NULL ? "" : name1); + if (ret != 0) + return ret; + } + + ret = vercmp(version0 == NULL ? "" : version0, + version1 == NULL ? "" : version1); + if (ret != 0) + return ret; + + ret = vercmp(release0 == NULL ? "" : release0, + release1 == NULL ? "" : release1); + return ret; +} + +/* return 1: e0 is newer than e1 */ +/* 0: e0 and e1 are the same version */ +/* -1: e1 is newer than e0 */ +static int bls_cmp(const struct bls_entry *e0, const struct bls_entry *e1) +{ + char *id0, *id1; + int r; + + id0 = grub_strdup(e0->filename); + id1 = grub_strdup(e1->filename); + + r = split_cmp(id0, id1, 1); + + grub_free(id0); + grub_free(id1); + + return r; +} + +static void list_add_tail(struct bls_entry *head, struct bls_entry *item) +{ + item->next = head; + if (head->prev) + head->prev->next = item; + item->prev = head->prev; + head->prev = item; +} + +static int bls_add_entry(struct bls_entry *entry) +{ + struct bls_entry *e, *last = NULL; + int rc; + + if (!entries) { + grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename); + entries = entry; + return 0; + } + + FOR_BLS_ENTRIES(e) { + rc = bls_cmp(entry, e); + + if (!rc) + return GRUB_ERR_BAD_ARGUMENT; + + if (rc == 1) { + grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename); + list_add_tail (e, entry); + if (e == entries) { + entries = entry; + entry->prev = NULL; + } + return 0; + } + last = e; + } + + if (last) { + grub_dprintf ("blscfg", "Add entry with id \"%s\"\n", entry->filename); + last->next = entry; + entry->prev = last; + } + + return 0; +} + +struct read_entry_info { + const char *devid; + const char *dirname; + grub_file_t file; +}; + +static int read_entry ( + const char *filename, + const struct grub_dirhook_info *dirhook_info UNUSED, + void *data) +{ + grub_size_t m = 0, n, clip = 0; + int rc = 0; + char *p = NULL; + grub_file_t f = NULL; + struct bls_entry *entry; + struct read_entry_info *info = (struct read_entry_info *)data; + + grub_dprintf ("blscfg", "filename: \"%s\"\n", filename); + + n = grub_strlen (filename); + + if (info->file) + { + f = info->file; + } + else + { + if (filename[0] == '.') + return 0; + + if (n <= 5) + return 0; + + if (grub_strcmp (filename + n - 5, ".conf") != 0) + return 0; + + p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename); + + f = grub_file_open (p, GRUB_FILE_TYPE_CONFIG); + if (!f) + goto finish; + } + + entry = grub_zalloc (sizeof (*entry)); + if (!entry) + goto finish; + + if (info->file) + { + char *slash; + + if (n > 5 && !grub_strcmp (filename + n - 5, ".conf") == 0) + clip = 5; + + slash = grub_strrchr (filename, '/'); + if (!slash) + slash = grub_strrchr (filename, '\\'); + + while (*slash == '/' || *slash == '\\') + slash++; + + m = slash ? slash - filename : 0; + } + else + { + m = 0; + clip = 5; + } + n -= m; + + entry->filename = grub_strndup(filename + m, n - clip); + if (!entry->filename) + goto finish; + + entry->filename[n - 5] = '\0'; + + for (;;) + { + char *buf; + char *separator; + + buf = grub_file_getline (f); + if (!buf) + break; + + while (buf && buf[0] && (buf[0] == ' ' || buf[0] == '\t')) + buf++; + if (buf[0] == '#') + continue; + + separator = grub_strchr (buf, ' '); + + if (!separator) + separator = grub_strchr (buf, '\t'); + + if (!separator || separator[1] == '\0') + { + grub_free (buf); + break; + } + + separator[0] = '\0'; + + do { + separator++; + } while (*separator == ' ' || *separator == '\t'); + + rc = bls_add_keyval (entry, buf, separator); + grub_free (buf); + if (rc < 0) + break; + } + + if (!rc) + bls_add_entry(entry); + +finish: + if (p) + grub_free (p); + + if (f) + grub_file_close (f); + + return 0; +} + +static grub_envblk_t saved_env = NULL; + +static int UNUSED +save_var (const char *name, const char *value, void *whitelist UNUSED) +{ + const char *val = grub_env_get (name); + grub_dprintf("blscfg", "saving \"%s\"\n", name); + + if (val) + grub_envblk_set (saved_env, name, value); + + return 0; +} + +static int UNUSED +unset_var (const char *name, const char *value UNUSED, void *whitelist) +{ + grub_dprintf("blscfg", "restoring \"%s\"\n", name); + if (! whitelist) + { + grub_env_unset (name); + return 0; + } + + if (test_whitelist_membership (name, + (const grub_env_whitelist_t *) whitelist)) + grub_env_unset (name); + + return 0; +} + +static char **bls_make_list (struct bls_entry *entry, const char *key, int *num) +{ + int last = -1; + char *val; + + int nlist = 0; + char **list = NULL; + + list = grub_malloc (sizeof (char *)); + if (!list) + return NULL; + list[0] = NULL; + + while (1) + { + char **new; + + val = bls_get_val (entry, key, &last); + if (!val) + break; + + new = grub_realloc (list, (nlist + 2) * sizeof (char *)); + if (!new) + break; + + list = new; + list[nlist++] = val; + list[nlist] = NULL; + } + + if (!nlist) + { + grub_free (list); + return NULL; + } + + if (num) + *num = nlist; + + return list; +} + +static char *field_append(bool is_var, char *buffer, const char *start, const char *end) +{ + char *tmp = grub_strndup(start, end - start + 1); + const char *field = tmp; + int term = is_var ? 2 : 1; + + if (is_var) { + field = grub_env_get (tmp); + if (!field) + return buffer; + } + + if (!buffer) + buffer = grub_zalloc (grub_strlen(field) + term); + else + buffer = grub_realloc (buffer, grub_strlen(buffer) + grub_strlen(field) + term); + + if (!buffer) + return NULL; + + tmp = buffer + grub_strlen(buffer); + tmp = grub_stpcpy (tmp, field); + + if (is_var) + tmp = grub_stpcpy (tmp, " "); + + return buffer; +} + +static char *expand_val(const char *value) +{ + char *buffer = NULL; + const char *start = value; + const char *end = value; + bool is_var = false; + + if (!value) + return NULL; + + while (*value) { + /* It's a variable only when *value is '$' and it is not escaped with '\'*/ + if (*value == '$' && *end != '\\') { + if (start != end) { + buffer = field_append(is_var, buffer, start, end); + if (!buffer) + return NULL; + } + + is_var = true; + start = value + 1; + } else if (is_var) { + if (!grub_isalnum(*value) && *value != '_') { + buffer = field_append(is_var, buffer, start, end); + is_var = false; + start = value; + if (*start == ' ') + start++; + } + } + + end = value; + value++; + } + + if (start != end) { + buffer = field_append(is_var, buffer, start, end); + if (!buffer) + return NULL; + } + + return buffer; +} + +static char **early_initrd_list (const char *initrd) +{ + int nlist = 0; + char **list = NULL; + char *separator; + + while ((separator = grub_strchr (initrd, ' '))) + { + list = grub_realloc (list, (nlist + 2) * sizeof (char *)); + if (!list) + return NULL; + + list[nlist++] = grub_strndup(initrd, separator - initrd); + list[nlist] = NULL; + initrd = separator + 1; + } + + list = grub_realloc (list, (nlist + 2) * sizeof (char *)); + if (!list) + return NULL; + + list[nlist++] = grub_strndup(initrd, grub_strlen(initrd)); + list[nlist] = NULL; + + return list; +} + +static void create_entry (struct bls_entry *entry) +{ + int argc = 0; + const char **argv = NULL; + + char *title = NULL; + char *clinux = NULL; + char *cuki = NULL; + char *options = NULL; + char **initrds = NULL; + char *initrd = NULL; + const char *early_initrd = NULL; + char **early_initrds = NULL; + char *initrd_prefix = NULL; + char *devicetree = NULL; + char *dt = NULL; + char *id = entry->filename; + char *dotconf = id; + char *hotkey = NULL; + + char *users = NULL; + char **classes = NULL; + + char **args = NULL; + + char *src = NULL; + int i, index; + bool add_dt_prefix = false; + + grub_dprintf("blscfg", "%s got here\n", __func__); + clinux = bls_get_val (entry, "linux", NULL); + cuki = bls_get_val (entry, "efi", NULL); + if (!clinux && !cuki) + { + if (!clinux) + { + grub_dprintf ("blscfg", "Skipping file %s with no 'linux' key.\n", entry->filename); + goto finish; + } + if (!cuki) + { + grub_dprintf ("blscfg", "Skipping file %s with no 'efi' key.\n", entry->filename); + goto finish; + } + } + + /* + * strip the ".conf" off the end before we make it our "id" field. + */ + do + { + dotconf = grub_strstr(dotconf, ".conf"); + } while (dotconf != NULL && dotconf[5] != '\0'); + if (dotconf) + dotconf[0] = '\0'; + + initrds = bls_make_list (entry, "initrd", NULL); + + devicetree = expand_val (bls_get_val (entry, "devicetree", NULL)); + + if (!devicetree) + { + devicetree = expand_val (grub_env_get("devicetree")); + add_dt_prefix = true; + } + + hotkey = bls_get_val (entry, "grub_hotkey", NULL); + users = expand_val (bls_get_val (entry, "grub_users", NULL)); + classes = bls_make_list (entry, "grub_class", NULL); + args = bls_make_list (entry, "grub_arg", &argc); + + argc += 1; + argv = grub_malloc ((argc + 1) * sizeof (char *)); + argv[0] = title ? title : clinux; + + title = bls_get_val (entry, "title", NULL); + if (clinux) + { + argv[0] = title ? title : clinux; + options = expand_val (bls_get_val (entry, "options", NULL)); + if (!options) + options = expand_val (grub_env_get("default_kernelopts")); + } + if (cuki) + { + argv[0] = title ? title : cuki; + options = bls_get_val (entry, ".cmdline", NULL); + if (!options) + options = expand_val (bls_get_val (entry, "options", NULL)); + } + for (i = 1; i < argc; i++) + argv[i] = args[i-1]; + argv[argc] = NULL; + + early_initrd = grub_env_get("early_initrd"); + + grub_dprintf ("blscfg", "adding menu entry for \"%s\" with id \"%s\"\n", + title, id); + if (early_initrd) + { + early_initrds = early_initrd_list(early_initrd); + if (!early_initrds) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto finish; + } + + if (initrds != NULL && initrds[0] != NULL) + { + initrd_prefix = grub_strrchr (initrds[0], '/'); + initrd_prefix = grub_strndup(initrds[0], initrd_prefix - initrds[0] + 1); + } + else + { + initrd_prefix = grub_strrchr (clinux, '/'); + initrd_prefix = grub_strndup(clinux, initrd_prefix - clinux + 1); + } + + if (!initrd_prefix) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto finish; + } + } + + if (early_initrds || initrds) + { + int initrd_size = sizeof ("initrd"); + char *tmp; + + for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++) + initrd_size += sizeof (" " GRUB_BOOT_DEVICE) \ + + grub_strlen(initrd_prefix) \ + + grub_strlen (early_initrds[i]) + 1; + + for (i = 0; initrds != NULL && initrds[i] != NULL; i++) + initrd_size += sizeof (" " GRUB_BOOT_DEVICE) \ + + grub_strlen (initrds[i]) + 1; + initrd_size += 1; + + initrd = grub_malloc (initrd_size); + if (!initrd) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto finish; + } + + tmp = grub_stpcpy(initrd, "initrd"); + for (i = 0; early_initrds != NULL && early_initrds[i] != NULL; i++) + { + grub_dprintf ("blscfg", "adding early initrd %s\n", early_initrds[i]); + tmp = frob_boot_device (tmp); + tmp = grub_stpcpy (tmp, initrd_prefix); + tmp = grub_stpcpy (tmp, early_initrds[i]); + grub_free(early_initrds[i]); + } + + for (i = 0; initrds != NULL && initrds[i] != NULL; i++) + { + grub_dprintf ("blscfg", "adding initrd %s\n", initrds[i]); + tmp = frob_boot_device (tmp); + tmp = grub_stpcpy (tmp, initrds[i]); + } + tmp = grub_stpcpy (tmp, "\n"); + } + + if (devicetree) + { + char *prefix = NULL; + int dt_size; + + if (add_dt_prefix) + { + prefix = grub_strrchr (clinux, '/'); + prefix = grub_strndup(clinux, prefix - clinux + 1); + if (!prefix) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto finish; + } + } + + dt_size = sizeof("devicetree " GRUB_BOOT_DEVICE) + grub_strlen(devicetree) + 1; + + if (add_dt_prefix) + { + dt_size += grub_strlen(prefix); + } + + dt = grub_malloc (dt_size); + if (!dt) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto finish; + } + char *tmp = dt; + tmp = grub_stpcpy (dt, "devicetree"); + tmp = frob_boot_device (tmp); + if (add_dt_prefix) + tmp = grub_stpcpy (tmp, prefix); + tmp = grub_stpcpy (tmp, devicetree); + tmp = grub_stpcpy (tmp, "\n"); + + grub_free(prefix); + } + + grub_dprintf ("blscfg2", "devicetree %s for id:\"%s\"\n", dt, id); + + const char *sdval = grub_env_get("save_default"); + bool savedefault = ((NULL != sdval) && (grub_strcmp(sdval, "true") == 0)); + if(clinux) + { + src = grub_xasprintf ("%sload_video\n" + "set gfxpayload=keep\n" + "insmod gzio\n" + "linux %s%s%s%s\n" + "%s%s", + savedefault ? "savedefault\n" : "", + separate_boot ? GRUB_BOOT_DEVICE : "", + clinux, options ? " " : "", options ? options : "", + initrd ? initrd : "", dt ? dt : ""); + } + if(cuki) + { + src = grub_xasprintf ("%schainloader %s%s%s%s\n", + savedefault ? "savedefault\n" : "", + separate_boot ? GRUB_BOOT_DEVICE : "", + cuki, + (options != NULL) ? " " : "", + (options != NULL) ? options : ""); + } + grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, &index, entry, NULL); + grub_dprintf ("blscfg", "Added entry %d id:\"%s\"\n", index, id); + +finish: + grub_free (dt); + grub_free (initrd); + grub_free (initrd_prefix); + grub_free (early_initrds); + grub_free (devicetree); + grub_free (initrds); + grub_free (options); + grub_free (classes); + grub_free (args); + grub_free (argv); + grub_free (src); +} + +struct find_entry_info { + const char *dirname; + const char *devid; + grub_device_t dev; + grub_fs_t fs; +}; + +/* + * info: the filesystem object the file is on. + */ +static int find_entry (struct find_entry_info *info) +{ + struct read_entry_info read_entry_info; + grub_fs_t blsdir_fs = NULL; + grub_device_t blsdir_dev = NULL; + const char *blsdir = info->dirname; + int fallback = 0; + int r = 0; + + if (!blsdir) { + blsdir = grub_env_get ("blsdir"); + if (!blsdir) + blsdir = GRUB_BLS_CONFIG_PATH; + } + + read_entry_info.file = NULL; + read_entry_info.dirname = blsdir; + + grub_dprintf ("blscfg", "scanning blsdir: %s\n", blsdir); + + blsdir_dev = info->dev; + blsdir_fs = info->fs; + read_entry_info.devid = info->devid; + +read_fallback: + r = blsdir_fs->fs_dir (blsdir_dev, read_entry_info.dirname, read_entry, + &read_entry_info); + if (r != 0) { + grub_dprintf ("blscfg", "read_entry returned error\n"); + grub_err_t e; + do + { + e = grub_error_pop(); + } while (e); + } + + if (r && !info->dirname && !fallback) { + read_entry_info.dirname = "/boot" GRUB_BLS_CONFIG_PATH; + grub_dprintf ("blscfg", "Entries weren't found in %s, fallback to %s\n", + blsdir, read_entry_info.dirname); + fallback = 1; + goto read_fallback; + } + + return 0; +} + +static grub_err_t +bls_load_entries (const char *path) +{ + grub_size_t len; + grub_fs_t fs; + grub_device_t dev; + static grub_err_t r; + const char *devid = NULL; + char *blsdir = NULL; + struct find_entry_info info = { + .dev = NULL, + .fs = NULL, + .dirname = NULL, + }; + struct read_entry_info rei = { + .devid = NULL, + .dirname = NULL, + }; + + if (path) { + len = grub_strlen (path); + if (grub_strcmp (path + len - 5, ".conf") == 0) { + rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG); + if (!rei.file) + return grub_errno; + /* + * read_entry() closes the file + */ + return read_entry(path, NULL, &rei); + } else if (path[0] == '(') { + devid = path + 1; + + blsdir = grub_strchr (path, ')'); + if (!blsdir) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("Filepath isn't correct")); + + *blsdir = '\0'; + blsdir = blsdir + 1; + } + } + + if (!devid) { +#ifdef GRUB_MACHINE_EMU + devid = "host"; +#else + devid = grub_env_get ("root"); +#endif + if (!devid) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, + N_("variable `%s' isn't set"), "root"); + } + + grub_dprintf ("blscfg", "opening %s\n", devid); + dev = grub_device_open (devid); + if (!dev) + return grub_errno; + + grub_dprintf ("blscfg", "probing fs\n"); + fs = grub_fs_probe (dev); + if (!fs) + { + r = grub_errno; + goto finish; + } + + info.dirname = blsdir; + info.devid = devid; + info.dev = dev; + info.fs = fs; + find_entry(&info); + +finish: + if (dev) + grub_device_close (dev); + + return r; +} + +static bool +is_default_entry(const char *def_entry, struct bls_entry *entry, int idx) +{ + const char *title; + int def_idx; + + if (!def_entry) + return false; + + if (grub_strcmp(def_entry, entry->filename) == 0) + return true; + + title = bls_get_val(entry, "title", NULL); + + if (title && grub_strcmp(def_entry, title) == 0) + return true; + + def_idx = (int)grub_strtol(def_entry, NULL, 0); + if (grub_errno == GRUB_ERR_BAD_NUMBER) { + grub_errno = GRUB_ERR_NONE; + return false; + } + + if (def_idx == idx) + return true; + + return false; +} + +static grub_err_t +bls_create_entries (bool show_default, bool show_non_default, char *entry_id) +{ + const char *def_entry = NULL; + struct bls_entry *entry = NULL; + int idx = 0; + + def_entry = grub_env_get("default"); + + grub_dprintf ("blscfg", "%s Creating entries from bls\n", __func__); + FOR_BLS_ENTRIES(entry) { + if (entry->visible) { + idx++; + continue; + } + + if ((show_default && is_default_entry(def_entry, entry, idx)) || + (show_non_default && !is_default_entry(def_entry, entry, idx)) || + (entry_id && grub_strcmp(entry_id, entry->filename) == 0)) { + create_entry(entry); + entry->visible = 1; + } + idx++; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_blscfg (grub_extcmd_context_t ctxt UNUSED, + int argc, char **args) +{ + grub_err_t r; + char *path = NULL; + char *entry_id = NULL; + bool show_default = true; + bool show_non_default = true; + + if (argc == 1) { + if (grub_strcmp (args[0], "default") == 0) { + show_non_default = false; + } else if (grub_strcmp (args[0], "non-default") == 0) { + show_default = false; + } else if (args[0][0] == '(') { + path = args[0]; + } else { + entry_id = args[0]; + show_default = false; + show_non_default = false; + } + } + + r = bls_load_entries(path); + if (r) + return r; + + return bls_create_entries(show_default, show_non_default, entry_id); +} + +static grub_extcmd_t cmd; +static grub_extcmd_t oldcmd; + +GRUB_MOD_INIT(blscfg) +{ + grub_dprintf("blscfg", "%s got here\n", __func__); + cmd = grub_register_extcmd ("blscfg", + grub_cmd_blscfg, + 0, + NULL, + N_("Import Boot Loader Specification snippets."), + NULL); + oldcmd = grub_register_extcmd ("bls_import", + grub_cmd_blscfg, + 0, + NULL, + N_("Import Boot Loader Specification snippets."), + NULL); +} + +GRUB_MOD_FINI(blscfg) +{ + grub_unregister_extcmd (cmd); + grub_unregister_extcmd (oldcmd); +} diff --git a/grub-core/commands/blsuki.c b/grub-core/commands/blsuki.c new file mode 100644 index 0000000000..bcd0114d85 --- /dev/null +++ b/grub-core/commands/blsuki.c @@ -0,0 +1,1491 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2025 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GRUB_MACHINE_EFI +#include +#include +#include +#endif + +#ifdef GRUB_MACHINE_EMU +#include +#define GRUB_BOOT_DEVICE "/boot" +#else +#define GRUB_BOOT_DEVICE "" +#endif + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define GRUB_BLS_CONFIG_PATH "/loader/entries/" +#define GRUB_UKI_CONFIG_PATH "/EFI/Linux" + +#define BLS_EXT_LEN (sizeof (".conf") - 1) +#define UKI_EXT_LEN (sizeof (".efi") - 1) + +/* + * It is highly unlikely to ever receive a large amount of keyval pairs. A + * limit of 10000 is more than enough. + */ +#define BLSUKI_KEYVALS_MAX 10000 +/* + * The only sections we read are ".cmdline" and ".osrel". The ".cmdline" + * section has a size limit of 4096 and it would be very unlikely for the size + * of the ".osrel" section to be 5 times larger than 4096. + */ +#define UKI_SECTION_SIZE_MAX (5 * 4096) + +enum blsuki_cmd_type + { + BLSUKI_BLS_CMD, + BLSUKI_UKI_CMD, + }; + +static const struct grub_arg_option bls_opt[] = + { + {"path", 'p', 0, "Specify path to find BLS entries.", N_("DIR"), ARG_TYPE_PATHNAME}, + {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find BLS entries.", 0, ARG_TYPE_NONE}, + {"show-default", 'd', 0, "Allow the default BLS entry to be added to the GRUB menu.", 0, ARG_TYPE_NONE}, + {"show-non-default", 'n', 0, "Allow the non-default BLS entries to be added to the GRUB menu.", 0, ARG_TYPE_NONE}, + {"entry", 'e', 0, "Allow specific BLS entries to be added to the GRUB menu.", N_("FILE"), ARG_TYPE_FILE}, + {0, 0, 0, 0, 0, 0} + }; + +#ifdef GRUB_MACHINE_EFI +static const struct grub_arg_option uki_opt[] = + { + {"path", 'p', 0, N_("Specify path to find UKI entries."), N_("DIR"), ARG_TYPE_PATHNAME}, + {"enable-fallback", 'f', 0, "Fallback to the default BLS path if --path fails to find UKI entries.", 0, ARG_TYPE_NONE}, + {"show-default", 'd', 0, N_("Allow the default UKI entry to be added to the GRUB menu."), 0, ARG_TYPE_NONE}, + {"show-non-default", 'n', 0, N_("Allow the non-default UKI entries to be added to the GRUB menu."), 0, ARG_TYPE_NONE}, + {"entry", 'e', 0, N_("Allow specificUKII entries to be added to the GRUB menu."), N_("FILE"), ARG_TYPE_FILE}, + {0, 0, 0, 0, 0, 0} + }; +#endif + +struct keyval +{ + const char *key; + char *val; +}; + +struct read_entry_info +{ + const char *devid; + const char *dirname; + enum blsuki_cmd_type cmd_type; + grub_file_t file; +}; + +struct find_entry_info +{ + const char *dirname; + const char *devid; + grub_device_t dev; + grub_fs_t fs; +}; + +static grub_blsuki_entry_t *entries = NULL; + +#define FOR_BLSUKI_ENTRIES(var) FOR_LIST_ELEMENTS (var, entries) + +/* + * BLS appears to make paths relative to the filesystem that snippets are + * on, not /. Attempt to cope. + */ +static char *blsuki_update_boot_device (char *tmp) +{ +#ifdef GRUB_MACHINE_EMU + static int separate_boot = -1; + char *ret; + + if (separate_boot != -1) + goto probed; + + separate_boot = 0; + + ret = grub_make_system_path_relative_to_its_root (GRUB_BOOT_DEVICE); + + if (ret != NULL && ret[0] == '\0') + separate_boot = 1; + + probed: + if (separate_boot == 0) + return tmp; +#endif + + return grub_stpcpy (tmp, GRUB_BOOT_DEVICE); +} + +/* + * This function will add a new keyval pair to a list of keyvals stored in the + * entry parameter. + */ +static grub_err_t +blsuki_add_keyval (grub_blsuki_entry_t *entry, char *key, char *val) +{ + char *k, *v; + struct keyval **kvs, *kv; + grub_size_t size; + int new_n = entry->nkeyvals + 1; + + if (new_n > BLSUKI_KEYVALS_MAX) + return grub_error (GRUB_ERR_BAD_NUMBER, "too many keyval pairs"); + + if (entry->keyvals_size == 0) + { + size = sizeof (struct keyval *); + kvs = grub_malloc (size); + if (kvs == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't allocate space for BLS key values"); + + entry->keyvals = kvs; + entry->keyvals_size = size; + } + else if (entry->keyvals_size < new_n * sizeof (struct keyval *)) + { + size = entry->keyvals_size * 2; + kvs = grub_realloc (entry->keyvals, size); + if (kvs == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't reallocate space for BLS key values"); + + entry->keyvals = kvs; + entry->keyvals_size = size; + } + + kv = grub_malloc (sizeof (struct keyval)); + if (kv == NULL) + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value"); + + k = grub_strdup (key); + if (k == NULL) + { + grub_free (kv); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value"); + } + + v = grub_strdup (val); + if (v == NULL) + { + grub_free (k); + grub_free (kv); + return grub_error (GRUB_ERR_OUT_OF_MEMORY, "couldn't find space for new BLS key value"); + } + + kv->key = k; + kv->val = v; + entry->keyvals[entry->nkeyvals] = kv; + entry->nkeyvals = new_n; + + return GRUB_ERR_NONE; +} + +/* + * Find the value of the key named by keyname. If there are allowed to be + * more than one, pass a pointer set to -1 to the last parameter the first + * time, and pass the same pointer through each time after, and it'll return + * them in sorted order. + */ +static char * +blsuki_get_val (grub_blsuki_entry_t *entry, const char *keyname, int *last) +{ + int idx, start = (last != NULL) ? (*last + 1) : 0; + struct keyval *kv = NULL; + char *ret = NULL; + + for (idx = start; idx < entry->nkeyvals; idx++) + { + kv = entry->keyvals[idx]; + + if (grub_strcmp (keyname, kv->key) == 0) + { + ret = kv->val; + break; + } + } + + if (last != NULL) + { + if (idx == entry->nkeyvals) + *last = -1; + else + *last = idx; + } + + return ret; +} + +/* + * Add a new grub_blsuki_entry_t struct to the entries list and sort it's + * position on the list. + */ +static grub_err_t +blsuki_add_entry (grub_blsuki_entry_t *entry) +{ + grub_blsuki_entry_t *e, *last = NULL; + int rc; + + if (entries == NULL) + { + grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename); + entries = entry; + return GRUB_ERR_NONE; + } + + FOR_BLSUKI_ENTRIES (e) + { + rc = filevercmp (entry->filename, e->filename); + if (rc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("duplicate file: `%s'"), entry->filename); + + if (rc > 0) + { + grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename); + grub_list_push (GRUB_AS_LIST_P (&e), GRUB_AS_LIST (entry)); + if (entry->next == entries) + { + entries = entry; + entry->prev = NULL; + } + else if (last != NULL) + last->next = entry; + + return GRUB_ERR_NONE; + } + last = e; + } + + if (last != NULL) + { + grub_dprintf ("blsuki", "Add entry with id \"%s\"\n", entry->filename); + last->next = entry; + entry->prev = &last; + } + + return GRUB_ERR_NONE; +} + +/* + * This function parses each line of a BLS config file to obtain the key value + * pairs that will be used to setup the GRUB menu entries. The key value pair + * will be stored in a list in the entry parameter. + */ +static grub_err_t +bls_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry) +{ + grub_err_t err = GRUB_ERR_NONE; + + for (;;) + { + char *line, *key, *val; + + line = grub_file_getline (f); + if (line == NULL) + break; + + key = grub_strtok_r (line, " \t", &val); + if (key == NULL) + { + grub_free (line); + break; + } + if (*key == '#') + { + grub_free (line); + continue; + } + + while (*val == ' ' || *val == '\t') + val++; + + if (*val == '\0') + { + grub_free (line); + break; + } + + err = blsuki_add_keyval (entry, key, val); + grub_free (line); + if (err != GRUB_ERR_NONE) + break; + } + + return err; +} + +#ifdef GRUB_MACHINE_EFI +/* + * This function searches for the .cmdline, .osrel, and .linux sections of a + * UKI. We only need to store the data for the .cmdline and .osrel sections, + * but we also need to verify that the .linux section exists. + */ +static grub_err_t +uki_parse_keyvals (grub_file_t f, grub_blsuki_entry_t *entry) +{ + struct grub_msdos_image_header *dos = NULL; + struct grub_pe_image_header *pe = NULL; + grub_off_t section_offset = 0; + struct grub_pe32_section_table *section = NULL; + struct grub_pe32_coff_header *coff_header = NULL; + char *val = NULL; + char *key = NULL; + const char *target[] = {".cmdline", ".osrel", ".linux", NULL}; + bool has_linux = false; + grub_err_t err = GRUB_ERR_NONE; + + dos = grub_zalloc (sizeof (*dos)); + if (dos == NULL) + return grub_errno; + if (grub_file_read (f, dos, sizeof (*dos)) < (grub_ssize_t) sizeof (*dos)) + { + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read UKI image header"); + goto finish; + } + if (dos->msdos_magic != GRUB_DOS_MAGIC) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "plain image kernel is not supported"); + goto finish; + } + + grub_dprintf ("blsuki", "PE/COFF header @ %08x\n", dos->pe_image_header_offset); + pe = grub_zalloc (sizeof (*pe)); + if (pe == NULL) + { + err = grub_errno; + goto finish; + } + if (grub_file_seek (f, dos->pe_image_header_offset) == (grub_off_t) -1 || + grub_file_read (f, pe, sizeof (*pe)) != sizeof (*pe)) + { + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read COFF image header"); + goto finish; + } + if (pe->optional_header.magic != GRUB_PE32_NATIVE_MAGIC) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "non-native image not supported"); + goto finish; + } + + coff_header = &(pe->coff_header); + section_offset = dos->pe_image_header_offset + sizeof (*pe); + + for (int i = 0; i < coff_header->num_sections; i++) + { + section = grub_zalloc (sizeof (*section)); + if (section == NULL) + { + err = grub_errno; + goto finish; + } + + if (grub_file_seek (f, section_offset) == (grub_off_t) -1 || + grub_file_read (f, section, sizeof (*section)) != sizeof (*section)) + { + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read section header"); + goto finish; + } + + key = grub_strndup (section->name, 8); + if (key == NULL) + { + err = grub_errno; + goto finish; + } + + for (int j = 0; target[j] != NULL; j++) + { + if (grub_strcmp (key, target[j]) == 0) + { + /* + * We don't need to read the contents of the .linux PE section, but we + * should verify that the section exists. + */ + if (grub_strcmp (key, ".linux") == 0) + { + has_linux = true; + break; + } + + if (section->raw_data_size > UKI_SECTION_SIZE_MAX) + { + err = grub_error (GRUB_ERR_BAD_NUMBER, "UKI section size is larger than expected"); + goto finish; + } + + val = grub_zalloc (section->raw_data_size); + if (val == NULL) + { + err = grub_errno; + goto finish; + } + + if (grub_file_seek (f, section->raw_data_offset) == (grub_off_t) -1 || + grub_file_read (f, val, section->raw_data_size) != (grub_ssize_t) section->raw_data_size) + { + err = grub_error (GRUB_ERR_FILE_READ_ERROR, "failed to read section"); + goto finish; + } + + err = blsuki_add_keyval (entry, key, val); + if (err != GRUB_ERR_NONE) + goto finish; + + break; + } + } + + section_offset += sizeof (*section); + grub_free (section); + grub_free (val); + grub_free (key); + section = NULL; + val = NULL; + key = NULL; + } + + if (has_linux == false) + err = grub_error (GRUB_ERR_NO_KERNEL, "UKI is missing the '.linux' section"); + + finish: + grub_free (dos); + grub_free (pe); + grub_free (section); + grub_free (val); + grub_free (key); + return err; +} + +/* + * This function obtains the keyval pairs when the .osrel data is input into + * the osrel_ptr parameter and returns the keyval pair. Since we are using + * grub_strtok_r(), the osrel_ptr will be updated to the following line of + * osrel. This function returns NULL when it reaches the end of osrel. + */ +static char * +uki_read_osrel (char **osrel_ptr, char **val_ret) +{ + char *key, *val; + grub_size_t val_size; + + for (;;) + { + key = grub_strtok_r (NULL, "\n\r", osrel_ptr); + if (key == NULL) + return NULL; + + /* Remove leading white space */ + while (*key == ' ' || *key == '\t') + key++; + + /* Skip commented lines */ + if (*key == '#') + continue; + + /* Split key/value */ + key = grub_strtok_r (key, "=", &val); + if (key == NULL || *val == '\0') + continue; + + /* Remove quotes from value */ + val_size = grub_strlen (val); + if ((*val == '\"' && val[val_size - 1] == '\"') || + (*val == '\'' && val[val_size - 1] == '\'')) + { + val[val_size - 1] = '\0'; + val++; + } + + *val_ret = val; + break; + } + + return key; +} +#endif + +/* + * If a file hasn't already been opened, this function opens a BLS config file + * or UKI and initializes entry data before parsing keyvals and adding the entry + * to the list of BLS or UKI entries. + */ +static int +blsuki_read_entry (const char *filename, + const struct grub_dirhook_info *dirhook_info __attribute__ ((__unused__)), + void *data) +{ + grub_size_t path_len = 0, ext_len = 0, filename_len; + grub_err_t err = GRUB_ERR_NONE; + char *p = NULL; + const char *ext = NULL; + grub_file_t f = NULL; + enum grub_file_type file_type = 0; + grub_blsuki_entry_t *entry; + struct read_entry_info *info = (struct read_entry_info *) data; + + grub_dprintf ("blsuki", "filename: \"%s\"\n", filename); + + filename_len = grub_strlen (filename); + + if (info->cmd_type == BLSUKI_BLS_CMD) + { + ext = ".conf"; + ext_len = BLS_EXT_LEN; + file_type = GRUB_FILE_TYPE_CONFIG; + } +#ifdef GRUB_MACHINE_EFI + else if (info->cmd_type == BLSUKI_UKI_CMD) + { + ext = ".efi"; + ext_len = UKI_EXT_LEN; + file_type = GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE; + } +#endif + + if (info->file != NULL) + f = info->file; + else + { + if (filename_len < ext_len || + grub_strcmp (filename + filename_len - ext_len, ext) != 0) + return 0; + + p = grub_xasprintf ("(%s)%s/%s", info->devid, info->dirname, filename); + f = grub_file_open (p, file_type); + grub_free (p); + if (f == NULL) + goto finish; + } + + entry = grub_zalloc (sizeof (*entry)); + if (entry == NULL) + goto finish; + + /* + * If a file is opened before this function, the filename may have a path. + * Since the filename is used for the ID of the GRUB menu entry, we can + * remove the path. + */ + if (info->file != NULL) + { + char *slash; + + slash = grub_strrchr (filename, '/'); + if (slash != NULL) + path_len = slash - filename + 1; + } + filename_len -= path_len; + + entry->filename = grub_strndup (filename + path_len, filename_len); + if (entry->filename == NULL) + { + grub_free (entry); + goto finish; + } + + entry->dirname = grub_strdup (info->dirname); + if (entry->dirname == NULL) + { + grub_free (entry); + goto finish; + } + + entry->devid = grub_strdup (info->devid); + if (entry->devid == NULL) + { + grub_free (entry); + goto finish; + } + + if (info->cmd_type == BLSUKI_BLS_CMD) + err = bls_parse_keyvals (f, entry); +#ifdef GRUB_MACHINE_EFI + else if (info->cmd_type == BLSUKI_UKI_CMD) + err = uki_parse_keyvals (f, entry); +#endif + + if (err == GRUB_ERR_NONE) + blsuki_add_entry (entry); + else + grub_free (entry); + + finish: + if (f != NULL) + grub_file_close (f); + + return 0; +} + +/* + * This function returns a list of values that had the same key in the BLS + * config file or UKI. The number of entries in this list is returned by the len + * parameter. + */ +static char ** +blsuki_make_list (grub_blsuki_entry_t *entry, const char *key, int *len) +{ + int last = -1; + char *val; + int nlist = 0; + char **list; + + list = grub_zalloc (sizeof (char *)); + if (list == NULL) + return NULL; + + while (1) + { + char **new; + + /* + * Since the same key might appear more than once, the 'last' variable + * starts at -1 and increments to indicate the last index in the list + * we obtained from blsuki_get_val(). + */ + val = blsuki_get_val (entry, key, &last); + if (val == NULL) + break; + + new = grub_realloc (list, (nlist + 2) * sizeof (char *)); + if (new == NULL) + break; + + list = new; + list[nlist++] = val; + list[nlist] = NULL; + } + + if (nlist == 0) + { + grub_free (list); + return NULL; + } + + if (len != NULL) + *len = nlist; + + return list; +} + +/* + * This function appends a field to the end of a buffer. If the field given is + * an enviornmental variable, it gets the value stored for that variable and + * appends that to the buffer instead. + */ +static char * +blsuki_field_append (bool is_env_var, char *buffer, const char *start, const char *end) +{ + char *tmp; + const char *field; + grub_size_t size = 0; + + tmp = grub_strndup (start, end - start + 1); + if (tmp == NULL) + return NULL; + + field = tmp; + + if (is_env_var == true) + { + field = grub_env_get (tmp); + if (field == NULL) + return buffer; + } + + if (grub_add (grub_strlen (field), 1, &size)) + return NULL; + + if (buffer == NULL) + buffer = grub_zalloc (size); + else + { + if (grub_add (size, grub_strlen (buffer), &size)) + return NULL; + + buffer = grub_realloc (buffer, size); + } + + if (buffer == NULL) + return NULL; + + tmp = buffer + grub_strlen (buffer); + tmp = grub_stpcpy (tmp, field); + + if (is_env_var == true) + tmp = grub_stpcpy (tmp, " "); + + return buffer; +} + +/* + * This function takes a value string, checks for environmental variables, and + * returns the value string with all environmental variables replaced with the + * value stored in the variable. + */ +static char * +blsuki_expand_val (const char *value) +{ + char *buffer = NULL; + const char *start = value; + const char *end = value; + bool is_env_var = false; + + if (value == NULL) + return NULL; + + while (*value != '\0') + { + if (*value == '$') + { + if (start != end) + { + buffer = blsuki_field_append (is_env_var, buffer, start, end); + if (buffer == NULL) + return NULL; + } + + is_env_var = true; + start = value + 1; + } + else if (is_env_var == true) + { + if (grub_isalnum (*value) == 0 && *value != '_') + { + buffer = blsuki_field_append (is_env_var, buffer, start, end); + is_env_var = false; + start = value; + if (*start == ' ') + start++; + } + } + + end = value; + value++; + } + + if (start != end) + { + buffer = blsuki_field_append (is_env_var, buffer, start, end); + if (buffer == NULL) + return NULL; + } + + return buffer; +} + +/* + * This function returns a string with the command to load a linux kernel with + * kernel command-line options based on what was specified in the BLS config + * file. + */ +static char * +bls_get_linux (grub_blsuki_entry_t *entry) +{ + char *linux_path; + char *linux_cmd = NULL; + char *options = NULL; + char *tmp; + grub_size_t size; + + linux_path = blsuki_get_val (entry, "linux", NULL); + options = blsuki_expand_val (blsuki_get_val (entry, "options", NULL)); + + if (grub_add (sizeof ("linux " GRUB_BOOT_DEVICE), grub_strlen (linux_path), &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size"); + goto finish; + } + + if (options != NULL) + { + if (grub_add (size, grub_strlen (options), &size) || + grub_add (size, 1, &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while calculating linux buffer size"); + goto finish; + } + } + + linux_cmd = grub_malloc (size); + if (linux_cmd == NULL) + goto finish; + + tmp = linux_cmd; + tmp = grub_stpcpy (tmp, "linux "); + tmp = blsuki_update_boot_device (tmp); + tmp = grub_stpcpy (tmp, linux_path); + if (options != NULL) + { + tmp = grub_stpcpy (tmp, " "); + tmp = grub_stpcpy (tmp, options); + } + + tmp = grub_stpcpy (tmp, "\n"); + + finish: + grub_free (options); + return linux_cmd; +} + +/* + * This function returns a string with the command to load all initrds for a + * linux kernel image based on the list provided by the BLS config file. + */ +static char * +bls_get_initrd (grub_blsuki_entry_t *entry) +{ + char **initrd_list; + char *initrd_cmd = NULL; + char *tmp; + grub_size_t size; + int i; + + initrd_list = blsuki_make_list (entry, "initrd", NULL); + if (initrd_list != NULL) + { + size = sizeof ("initrd"); + + for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++) + { + if (grub_add (size, sizeof (" " GRUB_BOOT_DEVICE) - 1, &size) || + grub_add (size, grub_strlen (initrd_list[i]), &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size"); + goto finish; + } + } + + if (grub_add (size, 1, &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating initrd buffer size"); + goto finish; + } + + initrd_cmd = grub_malloc (size); + if (initrd_cmd == NULL) + goto finish; + + tmp = grub_stpcpy (initrd_cmd, "initrd"); + for (i = 0; initrd_list != NULL && initrd_list[i] != NULL; i++) + { + grub_dprintf ("blsuki", "adding initrd %s\n", initrd_list[i]); + tmp = grub_stpcpy (tmp, " "); + tmp = blsuki_update_boot_device (tmp); + tmp = grub_stpcpy (tmp, initrd_list[i]); + } + tmp = grub_stpcpy (tmp, "\n"); + } + + finish: + grub_free (initrd_list); + return initrd_cmd; +} + +/* + * This function returns a string with the command to load a device tree blob + * from the BLS config file. + */ +static char * +bls_get_devicetree (grub_blsuki_entry_t *entry) +{ + char *dt_path; + char *dt_cmd = NULL; + char *tmp; + grub_size_t size; + + dt_path = blsuki_expand_val (blsuki_get_val (entry, "devicetree", NULL)); + if (dt_path != NULL) + { + if (grub_add (sizeof ("devicetree " GRUB_BOOT_DEVICE), grub_strlen (dt_path), &size) || + grub_add (size, 1, &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected calculating device tree buffer size"); + return NULL; + } + + dt_cmd = grub_malloc (size); + if (dt_cmd == NULL) + return NULL; + + tmp = dt_cmd; + tmp = grub_stpcpy (dt_cmd, "devicetree "); + tmp = blsuki_update_boot_device (tmp); + tmp = grub_stpcpy (tmp, dt_path); + tmp = grub_stpcpy (tmp, "\n"); + } + + return dt_cmd; +} + +/* + * This function puts together all of the commands generated from the contents + * of the BLS config file and creates a new entry in the GRUB boot menu. + */ +static void +bls_create_entry (grub_blsuki_entry_t *entry) +{ + int argc = 0; + const char **argv = NULL; + char *title = NULL; + char *linux_path = NULL; + char *linux_cmd = NULL; + char *initrd_cmd = NULL; + char *dt_cmd = NULL; + char *id = entry->filename; + grub_size_t id_len; + char *hotkey = NULL; + char *users = NULL; + char **classes = NULL; + char **args = NULL; + char *src = NULL; + int i; + grub_size_t size; + bool blsuki_save_default; + + linux_path = blsuki_get_val (entry, "linux", NULL); + if (linux_path == NULL) + { + grub_dprintf ("blsuki", "Skipping file %s with no 'linux' key.\n", entry->filename); + goto finish; + } + + id_len = grub_strlen (id); + if (id_len >= BLS_EXT_LEN && grub_strcmp (id + id_len - BLS_EXT_LEN, ".conf") == 0) + id[id_len - BLS_EXT_LEN] = '\0'; + + title = blsuki_get_val (entry, "title", NULL); + hotkey = blsuki_get_val (entry, "grub_hotkey", NULL); + users = blsuki_expand_val (blsuki_get_val (entry, "grub_users", NULL)); + classes = blsuki_make_list (entry, "grub_class", NULL); + args = blsuki_make_list (entry, "grub_arg", &argc); + + argc++; + if (grub_mul (argc + 1, sizeof (char *), &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected creating argv list")); + goto finish; + } + + argv = grub_malloc (size); + if (argv == NULL) + goto finish; + + argv[0] = (title != NULL) ? title : linux_path; + for (i = 1; i < argc; i++) + argv[i] = args[i - 1]; + argv[argc] = NULL; + + linux_cmd = bls_get_linux (entry); + if (linux_cmd == NULL) + goto finish; + + initrd_cmd = bls_get_initrd (entry); + if (grub_errno != GRUB_ERR_NONE) + goto finish; + + dt_cmd = bls_get_devicetree (entry); + if (grub_errno != GRUB_ERR_NONE) + goto finish; + + blsuki_save_default = grub_env_get_bool ("blsuki_save_default", false); + src = grub_xasprintf ("%s%s%s%s", + blsuki_save_default ? "savedefault\n" : "", + linux_cmd, initrd_cmd ? initrd_cmd : "", + dt_cmd ? dt_cmd : ""); + + grub_normal_add_menu_entry (argc, argv, classes, id, users, hotkey, NULL, src, 0, NULL, NULL, entry); + + finish: + grub_free (linux_cmd); + grub_free (dt_cmd); + grub_free (initrd_cmd); + grub_free (classes); + grub_free (args); + grub_free (argv); + grub_free (src); +} + +#ifdef GRUB_MACHINE_EFI +/* + * This function puts together the section data received from the UKI and + * generates a new entry in the GRUB boot menu. + */ +static void +uki_create_entry (grub_blsuki_entry_t *entry) +{ + const char **argv = NULL; + char *id = entry->filename; + char *title = NULL; + char *options = NULL; + char *osrel, *osrel_line; + char *key = NULL; + char *value = NULL; + char *src = NULL; + bool blsuki_save_default; + + /* + * Although .osrel is listed as optional in the UKI specification, the .osrel + * section is needed to generate the GRUB menu entry title. + */ + osrel = blsuki_get_val (entry, ".osrel", NULL); + if (osrel == NULL) + { + grub_dprintf ("blsuki", "Skipping file %s with no '.osrel' key.\n", entry->filename); + goto finish; + } + + osrel_line = osrel; + while ((key = uki_read_osrel (&osrel_line, &value)) != NULL) + { + if (grub_strcmp ("PRETTY_NAME", key) == 0) + { + title = value; + break; + } + } + + options = blsuki_get_val (entry, ".cmdline", NULL); + + argv = grub_zalloc (2 * sizeof (char *)); + if (argv == NULL) + goto finish; + argv[0] = title; + + blsuki_save_default = grub_env_get_bool ("blsuki_save_default", false); + src = grub_xasprintf ("%schainloader (%s)%s/%s%s%s\n", + blsuki_save_default ? "savedefault\n" : "", + entry->devid, entry->dirname, + entry->filename, + (options != NULL) ? " " : "", + (options != NULL) ? options : ""); + + grub_normal_add_menu_entry (1, argv, NULL, id, NULL, NULL, NULL, src, 0, NULL, NULL, entry); + + finish: + grub_free (argv); + grub_free (src); + grub_free (options); + grub_free (osrel); +} +#endif + +/* + * This function fills a find_entry_info struct passed in by the info parameter. + * If the dirname or devid parameters are set to NULL, the dirname and devid + * fields in the info parameter will be set to default values. If info already + * has a value in the dev fields, we can compare it to the value passed in by + * the devid parameter or the default devid to see if we need to open a new + * device. + */ +static grub_err_t +blsuki_set_find_entry_info (struct find_entry_info *info, const char *dirname, const char *devid, enum blsuki_cmd_type cmd_type) +{ + grub_device_t dev; + grub_fs_t fs; + + if (info == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "info parameter is not set"); + + if (devid == NULL) + { + if (cmd_type == BLSUKI_BLS_CMD) + { +#ifdef GRUB_MACHINE_EMU + devid = "host"; +#else + devid = grub_env_get ("root"); +#endif + } +#ifdef GRUB_MACHINE_EFI + else if (cmd_type == BLSUKI_UKI_CMD) + { + grub_efi_loaded_image_t *image = grub_efi_get_loaded_image (grub_efi_image_handle); + + if (image == NULL) + return grub_error (GRUB_ERR_BAD_DEVICE, N_("unable to find boot device")); + devid = grub_efidisk_get_device_name (image->device_handle); + } +#endif + if (devid == NULL) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable '%s' isn't set"), "root"); + } + + /* Check that we aren't closing and opening the same device. */ + if (info->dev != NULL && grub_strcmp (info->devid, devid) != 0) + { + grub_device_close (info->dev); + info->dev = NULL; + } + /* If we are using the same device, then we can skip this step and only set the directory. */ + if (info->dev == NULL) + { + grub_dprintf ("blsuki", "opening %s\n", devid); + dev = grub_device_open (devid); + if (dev == NULL) + return grub_errno; + + grub_dprintf ("blsuki", "probing fs\n"); + fs = grub_fs_probe (dev); + if (fs == NULL) + { + grub_device_close (dev); + return grub_errno; + } + + info->devid = devid; + info->dev = dev; + info->fs = fs; + } + + info->dirname = dirname; + + return GRUB_ERR_NONE; +} + +/* + * This function searches for BLS config files and UKIs based on the data in the + * info parameter. If the fallback option is enabled, the default location will + * be checked for BLS config files or UKIs if the first attempt fails. + */ +static grub_err_t +blsuki_find_entry (struct find_entry_info *info, bool enable_fallback, enum blsuki_cmd_type cmd_type) +{ + struct read_entry_info read_entry_info; + char *default_dir = NULL; + const char *cmd_dir = NULL; + char *tmp; + grub_size_t default_size; + grub_fs_t dir_fs = NULL; + grub_device_t dir_dev = NULL; + bool fallback = false; + int r; + + do + { + read_entry_info.file = NULL; + read_entry_info.dirname = info->dirname; + + grub_dprintf ("blsuki", "scanning dir: %s\n", info->dirname); + dir_dev = info->dev; + dir_fs = info->fs; + read_entry_info.devid = info->devid; + read_entry_info.cmd_type = cmd_type; + + r = dir_fs->fs_dir (dir_dev, read_entry_info.dirname, blsuki_read_entry, + &read_entry_info); + if (r != 0) + { + grub_dprintf ("blsuki", "blsuki_read_entry returned error\n"); + grub_errno = GRUB_ERR_NONE; + } + + /* + * If we aren't able to find BLS entries in the directory given by info->dirname, + * we can fallback to the default location "/boot/loader/entries/" and see if we + * can find the files there. If we can't find UKI entries, fallback to + * "/EFI/Linux" on the EFI system partition. + */ + if (entries == NULL && fallback == false && enable_fallback == true) + { + if (cmd_type == BLSUKI_BLS_CMD) + cmd_dir = GRUB_BLS_CONFIG_PATH; +#ifdef GRUB_MACHINE_EFI + else if (cmd_type == BLSUKI_UKI_CMD) + cmd_dir = GRUB_UKI_CONFIG_PATH; +#endif + + default_size = sizeof (GRUB_BOOT_DEVICE) + grub_strlen (cmd_dir); + default_dir = grub_malloc (default_size); + if (default_dir == NULL) + return grub_errno; + + tmp = blsuki_update_boot_device (default_dir); + tmp = grub_stpcpy (tmp, cmd_dir); + + blsuki_set_find_entry_info (info, default_dir, NULL, cmd_type); + grub_dprintf ("blsuki", "Entries weren't found in %s, fallback to %s\n", + read_entry_info.dirname, info->dirname); + fallback = true; + } + else + fallback = false; + } + while (fallback == true); + + grub_free (default_dir); + return GRUB_ERR_NONE; +} + +static grub_err_t +blsuki_load_entries (char *path, bool enable_fallback, enum blsuki_cmd_type cmd_type) +{ + grub_size_t len, ext_len = 0; + static grub_err_t r; + const char *devid = NULL; + char *dir = NULL; + char *default_dir = NULL; + char *tmp; + const char *cmd_dir = NULL; + grub_size_t dir_size; + const char *ext = NULL; + struct find_entry_info info = { + .dev = NULL, + .fs = NULL, + .dirname = NULL, + }; + struct read_entry_info rei = { + .devid = NULL, + .dirname = NULL, + .cmd_type = cmd_type, + }; + + if (path != NULL) + { + if (cmd_type == BLSUKI_BLS_CMD) + { + ext = ".conf"; + ext_len = BLS_EXT_LEN; + } +#ifdef GRUB_MACHINE_EFI + else if (cmd_type == BLSUKI_UKI_CMD) + { + ext = ".efi"; + ext_len = UKI_EXT_LEN; + } +#endif + + len = grub_strlen (path); + if (len >= ext_len && grub_strcmp (path + len - ext_len, ext) == 0) + { + rei.file = grub_file_open (path, GRUB_FILE_TYPE_CONFIG); + if (rei.file == NULL) + return grub_errno; + + /* blsuki_read_entry() closes the file. */ + return blsuki_read_entry (path, NULL, &rei); + } + else if (path[0] == '(') + { + devid = path + 1; + + dir = grub_strchr (path, ')'); + if (dir == NULL) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid file name `%s'"), path); + + *dir = '\0'; + + /* Check if there is more than the devid in the path. */ + if (dir + 1 < path + len) + dir = dir + 1; + } + else if (path[0] == '/') + dir = path; + } + + if (dir == NULL) + { + if (cmd_type == BLSUKI_BLS_CMD) + cmd_dir = GRUB_BLS_CONFIG_PATH; +#ifdef GRUB_MACHINE_EFI + else if (cmd_type == BLSUKI_UKI_CMD) + cmd_dir = GRUB_UKI_CONFIG_PATH; +#endif + + dir_size = sizeof (GRUB_BOOT_DEVICE) + grub_strlen (cmd_dir); + default_dir = grub_malloc (dir_size); + if (default_dir == NULL) + return grub_errno; + + tmp = blsuki_update_boot_device (default_dir); + tmp = grub_stpcpy (tmp, cmd_dir); + dir = default_dir; + } + + r = blsuki_set_find_entry_info (&info, dir, devid, cmd_type); + if (r == GRUB_ERR_NONE) + r = blsuki_find_entry (&info, enable_fallback, cmd_type); + + if (info.dev != NULL) + grub_device_close (info.dev); + + grub_free (default_dir); + return r; +} + +static bool +blsuki_is_default_entry (const char *def_entry, grub_blsuki_entry_t *entry, int idx) +{ + const char *title; + const char *def_entry_end; + long def_idx; + + if (def_entry == NULL || *def_entry == '\0') + return false; + + if (grub_strcmp (def_entry, entry->filename) == 0) + return true; + + title = blsuki_get_val (entry, "title", NULL); + + if (title != NULL && grub_strcmp (def_entry, title) == 0) + return true; + + def_idx = grub_strtol (def_entry, &def_entry_end, 0); + if (*def_entry_end != '\0' || def_idx < 0 || def_idx > GRUB_INT_MAX) + return false; + + if ((int) def_idx == idx) + return true; + + return false; +} + +/* + * This function creates a GRUB boot menu entry for each BLS or UKI entry in + * the entries list. + */ +static grub_err_t +blsuki_create_entries (bool show_default, bool show_non_default, char *entry_id, enum blsuki_cmd_type cmd_type) +{ + const char *def_entry = NULL; + grub_blsuki_entry_t *entry = NULL; + int idx = 0; + + def_entry = grub_env_get ("default"); + + FOR_BLSUKI_ENTRIES(entry) + { + if (entry->visible == true) + { + idx++; + continue; + } + if ((show_default == true && blsuki_is_default_entry (def_entry, entry, idx) == true) || + (show_non_default == true && blsuki_is_default_entry (def_entry, entry, idx) == false) || + (entry_id != NULL && grub_strcmp (entry_id, entry->filename) == 0)) + { + if (cmd_type == BLSUKI_BLS_CMD) + bls_create_entry (entry); +#ifdef GRUB_MACHINE_EFI + else if (cmd_type == BLSUKI_UKI_CMD) + uki_create_entry (entry); +#endif + entry->visible = true; + } + + idx++; + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +blsuki_cmd (grub_extcmd_context_t ctxt, enum blsuki_cmd_type cmd_type) +{ + grub_err_t err; + struct grub_arg_list *state = ctxt->state; + char *path = NULL; + char *entry_id = NULL; + bool enable_fallback = false; + bool show_default = false; + bool show_non_default = false; + bool all = true; + entries = NULL; + + if (state[0].set) + path = state[0].arg; + if (state[1].set) + enable_fallback = true; + if (state[2].set) + { + show_default = true; + all = false; + } + if (state[3].set) + { + show_non_default = true; + all = false; + } + if (state[4].set) + { + entry_id = state[4].arg; + all = false; + } + if (all == true) + { + show_default = true; + show_non_default = true; + } + + err = blsuki_load_entries (path, enable_fallback, cmd_type); + if (err != GRUB_ERR_NONE) + return err; + + return blsuki_create_entries (show_default, show_non_default, entry_id, cmd_type); +} + +static grub_err_t +grub_cmd_blscfg (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + return blsuki_cmd (ctxt, BLSUKI_BLS_CMD); +} + +static grub_extcmd_t bls_cmd; + +#ifdef GRUB_MACHINE_EFI +static grub_err_t +grub_cmd_uki (grub_extcmd_context_t ctxt, int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + return blsuki_cmd (ctxt, BLSUKI_UKI_CMD); +} + +static grub_extcmd_t uki_cmd; +#endif + +GRUB_MOD_INIT(blsuki) +{ +#ifdef GRUB_MACHINE_EFI + uki_cmd = grub_register_extcmd ("uki", grub_cmd_uki, 0, + N_("[-p|--path] DIR [-f|--enable-fallback] [-d|--show-default] [-n|--show-non-default] [-e|--entry] FILE"), + N_("Import Unified Kernel Images"), uki_opt); +#endif +} + +GRUB_MOD_FINI(blsuki) +{ + grub_unregister_extcmd (bls_cmd); +#ifdef GRUB_MACHINE_EFI + grub_unregister_extcmd (uki_cmd); +#endif +} diff --git a/grub-core/commands/efi/connectefi.c b/grub-core/commands/efi/connectefi.c new file mode 100644 index 0000000000..9d02f2780f --- /dev/null +++ b/grub-core/commands/efi/connectefi.c @@ -0,0 +1,205 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2022 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 . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +typedef struct handle_list +{ + grub_efi_handle_t handle; + struct handle_list *next; +} handle_list_t; + +static handle_list_t *already_handled = NULL; + +static grub_err_t +add_handle (grub_efi_handle_t handle) +{ + handle_list_t *e; + e = grub_malloc (sizeof (*e)); + if (! e) + return grub_errno; + e->handle = handle; + e->next = already_handled; + already_handled = e; + return GRUB_ERR_NONE; +} + +static int +is_in_list (grub_efi_handle_t handle) +{ + handle_list_t *e; + for (e = already_handled; e != NULL; e = e->next) + if (e->handle == handle) + return 1; + return 0; +} + +static void +free_handle_list (void) +{ + handle_list_t *e; + while ((e = already_handled) != NULL) + { + already_handled = already_handled->next; + grub_free (e); + } +} + +typedef enum searched_item_flag +{ + SEARCHED_ITEM_FLAG_LOOP = 1, + SEARCHED_ITEM_FLAG_RECURSIVE = 2 +} searched_item_flags; + +typedef struct searched_item +{ + grub_guid_t guid; + const char *name; + searched_item_flags flags; +} searched_items; + +static grub_err_t +grub_cmd_connectefi (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + unsigned s; + searched_items pciroot_items[] = + { + { GRUB_EFI_PCI_ROOT_IO_GUID, "PCI root", SEARCHED_ITEM_FLAG_RECURSIVE } + }; + searched_items scsi_items[] = + { + { GRUB_EFI_PCI_ROOT_IO_GUID, "PCI root", 0 }, + { GRUB_EFI_PCI_IO_GUID, "PCI", SEARCHED_ITEM_FLAG_LOOP }, + { GRUB_EFI_SCSI_IO_PROTOCOL_GUID, "SCSI I/O", SEARCHED_ITEM_FLAG_RECURSIVE } + }; + searched_items *items = NULL; + unsigned nitems = 0; + grub_err_t grub_err = GRUB_ERR_NONE; + unsigned total_connected = 0; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + if (grub_strcmp(args[0], N_("pciroot")) == 0) + { + items = pciroot_items; + nitems = ARRAY_SIZE (pciroot_items); + } + else if (grub_strcmp(args[0], N_("scsi")) == 0) + { + items = scsi_items; + nitems = ARRAY_SIZE (scsi_items); + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, + N_("unexpected argument `%s'"), args[0]); + + for (s = 0; s < nitems; s++) + { + grub_efi_handle_t *handles; + grub_efi_uintn_t num_handles; + unsigned i, connected = 0, loop = 0; + +loop: + loop++; + grub_dprintf ("efi", "step '%s' loop %d:\n", items[s].name, loop); + + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, + &items[s].guid, 0, &num_handles); + + if (!handles) + continue; + + for (i = 0; i < num_handles; i++) + { + grub_efi_handle_t handle = handles[i]; + grub_efi_status_t status; + + /* Skip already handled handles */ + if (is_in_list (handle)) + { + grub_dprintf ("efi", " handle %p: already processed\n", + handle); + continue; + } + + status = grub_efi_connect_controller(handle, NULL, NULL, + items[s].flags & SEARCHED_ITEM_FLAG_RECURSIVE ? 1 : 0); + if (status == GRUB_EFI_SUCCESS) + { + connected++; + total_connected++; + grub_dprintf ("efi", " handle %p: connected\n", handle); + } + else + grub_dprintf ("efi", " handle %p: failed to connect (%d)\n", + handle, (grub_efi_int8_t) status); + + if ((grub_err = add_handle (handle)) != GRUB_ERR_NONE) + break; /* fatal */ + } + + grub_free (handles); + if (grub_err != GRUB_ERR_NONE) + break; /* fatal */ + + if (items[s].flags & SEARCHED_ITEM_FLAG_LOOP && connected) + { + connected = 0; + goto loop; + } + + free_handle_list (); + } + + free_handle_list (); + + if (total_connected) + grub_efidisk_reenumerate_disks (); + + return grub_err; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(connectefi) +{ + cmd = grub_register_command ("connectefi", grub_cmd_connectefi, + N_("pciroot|scsi"), + N_("Connect EFI handles." + " If 'pciroot' is specified, connect PCI" + " root EFI handles recursively." + " If 'scsi' is specified, connect SCSI" + " I/O EFI handles recursively.")); +} + +GRUB_MOD_FINI(connectefi) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/commands/efi/env.c b/grub-core/commands/efi/env.c new file mode 100644 index 0000000000..4080f24fdb --- /dev/null +++ b/grub-core/commands/efi/env.c @@ -0,0 +1,170 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2012 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 . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static const grub_guid_t grub_env_guid = GRUB_EFI_GRUB_VARIABLE_GUID; + +static grub_err_t +grub_efi_export_env(grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + const char *value; + char *old_value; + struct grub_envblk envblk_s = { NULL, 0 }; + grub_envblk_t envblk = &envblk_s; + grub_err_t err; + int changed = 1; + grub_efi_status_t status; + + grub_dprintf ("efienv", "argc:%d\n", argc); + for (int i = 0; i < argc; i++) + grub_dprintf ("efienv", "argv[%d]: %s\n", i, argv[i]); + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("variable name expected")); + + grub_efi_get_variable ("GRUB_ENV", &grub_env_guid, &envblk_s.size, + (void **) &envblk_s.buf); + if (!envblk_s.buf || envblk_s.size < 1) + { + char *buf = grub_malloc (1025); + if (!buf) + return grub_errno; + + grub_memcpy (buf, GRUB_ENVBLK_SIGNATURE, sizeof (GRUB_ENVBLK_SIGNATURE) - 1); + grub_memset (buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1, '#', + DEFAULT_ENVBLK_SIZE - sizeof (GRUB_ENVBLK_SIGNATURE) + 1); + buf[1024] = '\0'; + + envblk_s.buf = buf; + envblk_s.size = 1024; + } + else + { + char *buf = grub_realloc (envblk_s.buf, envblk_s.size + 1); + if (!buf) + return grub_errno; + + envblk_s.buf = buf; + envblk_s.buf[envblk_s.size] = '\0'; + } + + err = grub_envblk_get(envblk, argv[0], &old_value); + if (err != GRUB_ERR_NONE) + { + grub_dprintf ("efienv", "grub_envblk_get returned %d\n", err); + return err; + } + + value = grub_env_get(argv[0]); + if ((!value && !old_value) || + (value && old_value && !grub_strcmp(old_value, value))) + changed = 0; + + if (old_value) + grub_free(old_value); + + if (changed == 0) + { + grub_dprintf ("efienv", "No changes necessary\n"); + return 0; + } + + if (value) + { + grub_dprintf ("efienv", "setting \"%s\" to \"%s\"\n", argv[0], value); + grub_envblk_set(envblk, argv[0], value); + } + else + { + grub_dprintf ("efienv", "deleting \"%s\" from envblk\n", argv[0]); + grub_envblk_delete(envblk, argv[0]); + } + + grub_dprintf ("efienv", "envblk is %lu bytes:\n\"%s\"\n", envblk_s.size, envblk_s.buf); + + grub_dprintf ("efienv", "removing GRUB_ENV\n"); + status = grub_efi_set_variable ("GRUB_ENV", &grub_env_guid, NULL, 0); + if (status != GRUB_EFI_SUCCESS) + grub_dprintf ("efienv", "removal returned %ld\n", status); + + grub_dprintf ("efienv", "setting GRUB_ENV\n"); + status = grub_efi_set_variable ("GRUB_ENV", &grub_env_guid, + envblk_s.buf, envblk_s.size); + if (status != GRUB_EFI_SUCCESS) + grub_dprintf ("efienv", "setting GRUB_ENV returned %ld\n", status); + + return 0; +} + +static int +set_var (const char *name, const char *value, + void *whitelist __attribute__((__unused__))) +{ + grub_env_set (name, value); + return 0; +} + +static grub_err_t +grub_efi_load_env(grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[] __attribute__((__unused__))) +{ + struct grub_envblk envblk_s = { NULL, 0 }; + grub_envblk_t envblk = &envblk_s; + + grub_efi_get_variable ("GRUB_ENV", &grub_env_guid, &envblk_s.size, + (void **) &envblk_s.buf); + if (!envblk_s.buf || envblk_s.size < 1) + return 0; + + if (argc > 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected argument")); + + grub_envblk_iterate (envblk, NULL, set_var); + grub_free (envblk_s.buf); + + return GRUB_ERR_NONE; +} + +static grub_command_t export_cmd, loadenv_cmd; + +GRUB_MOD_INIT(lsefi) +{ + export_cmd = grub_register_command ("efi-export-env", grub_efi_export_env, + N_("VARIABLE_NAME"), N_("Export environment variable to UEFI.")); + loadenv_cmd = grub_register_command ("efi-load-env", grub_efi_load_env, + NULL, N_("Load the grub environment from UEFI.")); +} + +GRUB_MOD_FINI(lsefi) +{ + grub_unregister_command (export_cmd); + grub_unregister_command (loadenv_cmd); +} diff --git a/grub-core/commands/efi/lsefi.c b/grub-core/commands/efi/lsefi.c index 7b8316d418..3635cd99b7 100644 --- a/grub-core/commands/efi/lsefi.c +++ b/grub-core/commands/efi/lsefi.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/grub-core/commands/efi/tpm.c b/grub-core/commands/efi/tpm.c index f250c30dbc..cfddeb8719 100644 --- a/grub-core/commands/efi/tpm.c +++ b/grub-core/commands/efi/tpm.c @@ -273,6 +273,11 @@ grub_tpm_measure (unsigned char *buf, grub_size_t size, grub_uint8_t pcr, grub_efi_handle_t tpm_handle; grub_efi_uint8_t protocol_version; + grub_dprintf ("tpm", "PCR %d %s\n", pcr, grub_tpm_pcr_is_enabled (pcr) ? "enabled" : "disabled"); + + if (!grub_tpm_pcr_is_enabled (pcr)) + return GRUB_ERR_NONE; + grub_cc_log_event(buf, size, pcr, description); if (!grub_tpm_handle_find (&tpm_handle, &protocol_version)) diff --git a/grub-core/commands/extcmd.c b/grub-core/commands/extcmd.c index 90a5ca24a6..c236be13af 100644 --- a/grub-core/commands/extcmd.c +++ b/grub-core/commands/extcmd.c @@ -49,6 +49,9 @@ grub_extcmd_dispatcher (struct grub_command *cmd, int argc, char **args, } state = grub_arg_list_alloc (ext, argc, args); + if (state == NULL) + return grub_errno; + if (grub_arg_parse (ext, argc, args, state, &new_args, &new_argc)) { context.state = state; diff --git a/grub-core/commands/hexdump.c b/grub-core/commands/hexdump.c index eaa12465bb..d6f61d98a1 100644 --- a/grub-core/commands/hexdump.c +++ b/grub-core/commands/hexdump.c @@ -24,6 +24,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -51,7 +52,11 @@ grub_cmd_hexdump (grub_extcmd_context_t ctxt, int argc, char **args) length = (state[1].set) ? grub_strtoul (state[1].arg, 0, 0) : 256; if (!grub_strcmp (args[0], "(mem)")) - hexdump (skip, (char *) (grub_addr_t) skip, length); + { + if (grub_is_lockdown() == GRUB_LOCKDOWN_ENABLED) + return grub_error (GRUB_ERR_ACCESS_DENIED, N_("memory reading is disabled in lockdown mode")); + hexdump (skip, (char *) (grub_addr_t) skip, length); + } else if ((args[0][0] == '(') && (args[0][namelen - 1] == ')')) { grub_disk_t disk; diff --git a/grub-core/commands/increment.c b/grub-core/commands/increment.c new file mode 100644 index 0000000000..79cf137656 --- /dev/null +++ b/grub-core/commands/increment.c @@ -0,0 +1,105 @@ +/* increment.c - Commands to increment and decrement variables. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +typedef enum { + INCREMENT, + DECREMENT, +} operation; + +static grub_err_t +incr_decr(operation op, int argc, char **args) +{ + const char *old; + char *new; + long value; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_ ("no variable specified")); + if (argc > 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_ ("too many arguments")); + + old = grub_env_get (*args); + if (!old) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("No such variable \"%s\""), + *args); + + value = grub_strtol (old, NULL, 0); + if (grub_errno != GRUB_ERR_NONE) + return grub_errno; + + switch (op) + { + case INCREMENT: + value += 1; + break; + case DECREMENT: + value -= 1; + break; + } + + new = grub_xasprintf ("%ld", value); + if (!new) + return grub_errno; + + grub_env_set (*args, new); + grub_free (new); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_incr(struct grub_command *cmd UNUSED, + int argc, char **args) +{ + return incr_decr(INCREMENT, argc, args); +} + +static grub_err_t +grub_cmd_decr(struct grub_command *cmd UNUSED, + int argc, char **args) +{ + return incr_decr(DECREMENT, argc, args); +} + +static grub_command_t cmd_incr, cmd_decr; + +GRUB_MOD_INIT(increment) +{ + cmd_incr = grub_register_command ("increment", grub_cmd_incr, N_("VARIABLE"), + N_("increment VARIABLE")); + cmd_decr = grub_register_command ("decrement", grub_cmd_decr, N_("VARIABLE"), + N_("decrement VARIABLE")); +} + +GRUB_MOD_FINI(increment) +{ + grub_unregister_command (cmd_incr); + grub_unregister_command (cmd_decr); +} diff --git a/grub-core/commands/iorw.c b/grub-core/commands/iorw.c index 584baec8f9..7b2999b14b 100644 --- a/grub-core/commands/iorw.c +++ b/grub-core/commands/iorw.c @@ -24,6 +24,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -119,6 +120,9 @@ grub_cmd_write (grub_command_t cmd, int argc, char **argv) GRUB_MOD_INIT(memrw) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + cmd_read_byte = grub_register_extcmd ("inb", grub_cmd_read, 0, N_("PORT"), N_("Read 8-bit value from PORT."), @@ -147,6 +151,9 @@ GRUB_MOD_INIT(memrw) GRUB_MOD_FINI(memrw) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + grub_unregister_extcmd (cmd_read_byte); grub_unregister_extcmd (cmd_read_word); grub_unregister_extcmd (cmd_read_dword); diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c index e9e9d94eff..a5d296769e 100644 --- a/grub-core/commands/legacycfg.c +++ b/grub-core/commands/legacycfg.c @@ -143,7 +143,7 @@ legacy_file (const char *filename) args[0] = oldname; grub_normal_add_menu_entry (1, args, NULL, NULL, "legacy", NULL, NULL, - entrysrc, 0); + entrysrc, 0, NULL, NULL, NULL); grub_free (args); entrysrc[0] = 0; grub_free (oldname); @@ -198,14 +198,14 @@ legacy_file (const char *filename) const char **args = grub_malloc (sizeof (args[0])); if (!args) { - grub_file_close (file); grub_free (suffix); grub_free (entrysrc); return grub_errno; } args[0] = entryname; grub_normal_add_menu_entry (1, args, NULL, NULL, NULL, - NULL, NULL, entrysrc, 0); + NULL, NULL, entrysrc, 0, NULL, + NULL, NULL); grub_free (args); } diff --git a/grub-core/commands/loadenv.c b/grub-core/commands/loadenv.c index 1664458497..dfcb19e0ff 100644 --- a/grub-core/commands/loadenv.c +++ b/grub-core/commands/loadenv.c @@ -28,6 +28,8 @@ #include #include +#include "loadenv.h" + GRUB_MOD_LICENSE ("GPLv3+"); static const struct grub_arg_option options[] = @@ -79,81 +81,6 @@ open_envblk_file (char *filename, return file; } -static grub_envblk_t -read_envblk_file (grub_file_t file) -{ - grub_off_t offset = 0; - char *buf; - grub_size_t size = grub_file_size (file); - grub_envblk_t envblk; - - buf = grub_malloc (size); - if (! buf) - return 0; - - while (size > 0) - { - grub_ssize_t ret; - - ret = grub_file_read (file, buf + offset, size); - if (ret <= 0) - { - grub_free (buf); - return 0; - } - - size -= ret; - offset += ret; - } - - envblk = grub_envblk_open (buf, offset); - if (! envblk) - { - grub_free (buf); - grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block"); - return 0; - } - - return envblk; -} - -struct grub_env_whitelist -{ - grub_size_t len; - char **list; -}; -typedef struct grub_env_whitelist grub_env_whitelist_t; - -static int -test_whitelist_membership (const char* name, - const grub_env_whitelist_t* whitelist) -{ - grub_size_t i; - - for (i = 0; i < whitelist->len; i++) - if (grub_strcmp (name, whitelist->list[i]) == 0) - return 1; /* found it */ - - return 0; /* not found */ -} - -/* Helper for grub_cmd_load_env. */ -static int -set_var (const char *name, const char *value, void *whitelist) -{ - if (! whitelist) - { - grub_env_set (name, value); - return 0; - } - - if (test_whitelist_membership (name, - (const grub_env_whitelist_t *) whitelist)) - grub_env_set (name, value); - - return 0; -} - static grub_err_t grub_cmd_load_env (grub_extcmd_context_t ctxt, int argc, char **args) { diff --git a/grub-core/commands/loadenv.h b/grub-core/commands/loadenv.h new file mode 100644 index 0000000000..952f46121b --- /dev/null +++ b/grub-core/commands/loadenv.h @@ -0,0 +1,93 @@ +/* loadenv.c - command to load/save environment variable. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008,2009,2010 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 . + */ + +static grub_envblk_t UNUSED +read_envblk_file (grub_file_t file) +{ + grub_off_t offset = 0; + char *buf; + grub_size_t size = grub_file_size (file); + grub_envblk_t envblk; + + buf = grub_malloc (size); + if (! buf) + return 0; + + while (size > 0) + { + grub_ssize_t ret; + + ret = grub_file_read (file, buf + offset, size); + if (ret <= 0) + { + grub_free (buf); + return 0; + } + + size -= ret; + offset += ret; + } + + envblk = grub_envblk_open (buf, offset); + if (! envblk) + { + grub_free (buf); + grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid environment block"); + return 0; + } + + return envblk; +} + +struct grub_env_whitelist +{ + grub_size_t len; + char **list; +}; +typedef struct grub_env_whitelist grub_env_whitelist_t; + +static int UNUSED +test_whitelist_membership (const char* name, + const grub_env_whitelist_t* whitelist) +{ + grub_size_t i; + + for (i = 0; i < whitelist->len; i++) + if (grub_strcmp (name, whitelist->list[i]) == 0) + return 1; /* found it */ + + return 0; /* not found */ +} + +/* Helper for grub_cmd_load_env. */ +static int UNUSED +set_var (const char *name, const char *value, void *whitelist) +{ + if (! whitelist) + { + grub_env_set (name, value); + return 0; + } + + if (test_whitelist_membership (name, + (const grub_env_whitelist_t *) whitelist)) + grub_env_set (name, value); + + return 0; +} diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c index 6a1c7f5d36..f660946a24 100644 --- a/grub-core/commands/ls.c +++ b/grub-core/commands/ls.c @@ -241,7 +241,11 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human) grub_file_close (file); - p = grub_strrchr (dirname, '/') + 1; + p = grub_strrchr (dirname, '/'); + if (p == NULL) + goto fail; + ++p; + ctx.dirname = grub_strndup (dirname, p - dirname); if (ctx.dirname == NULL) goto fail; diff --git a/grub-core/commands/memrw.c b/grub-core/commands/memrw.c index d401a6db0e..9d8a54a4b3 100644 --- a/grub-core/commands/memrw.c +++ b/grub-core/commands/memrw.c @@ -23,6 +23,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -121,18 +122,24 @@ grub_cmd_write (grub_command_t cmd, int argc, char **argv) GRUB_MOD_INIT(memrw) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + cmd_read_byte = - grub_register_extcmd ("read_byte", grub_cmd_read, 0, - N_("ADDR"), N_("Read 8-bit value from ADDR."), - options); + grub_register_extcmd_lockdown ("read_byte", grub_cmd_read, 0, + N_("ADDR"), + N_("Read 8-bit value from ADDR."), + options); cmd_read_word = - grub_register_extcmd ("read_word", grub_cmd_read, 0, - N_("ADDR"), N_("Read 16-bit value from ADDR."), - options); + grub_register_extcmd_lockdown ("read_word", grub_cmd_read, 0, + N_("ADDR"), + N_("Read 16-bit value from ADDR."), + options); cmd_read_dword = - grub_register_extcmd ("read_dword", grub_cmd_read, 0, - N_("ADDR"), N_("Read 32-bit value from ADDR."), - options); + grub_register_extcmd_lockdown ("read_dword", grub_cmd_read, 0, + N_("ADDR"), + N_("Read 32-bit value from ADDR."), + options); cmd_write_byte = grub_register_command_lockdown ("write_byte", grub_cmd_write, N_("ADDR VALUE [MASK]"), @@ -149,6 +156,9 @@ GRUB_MOD_INIT(memrw) GRUB_MOD_FINI(memrw) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + grub_unregister_extcmd (cmd_read_byte); grub_unregister_extcmd (cmd_read_word); grub_unregister_extcmd (cmd_read_dword); diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c index 720e6d8ea3..d6cf329203 100644 --- a/grub-core/commands/menuentry.c +++ b/grub-core/commands/menuentry.c @@ -29,7 +29,7 @@ static const struct grub_arg_option options[] = { {"class", 1, GRUB_ARG_OPTION_REPEATABLE, N_("Menu entry type."), N_("STRING"), ARG_TYPE_STRING}, - {"users", 2, 0, + {"users", 2, GRUB_ARG_OPTION_OPTIONAL, N_("List of users allowed to boot this entry."), N_("USERNAME[,USERNAME]"), ARG_TYPE_STRING}, {"hotkey", 3, 0, @@ -78,7 +78,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes, const char *id, const char *users, const char *hotkey, const char *prefix, const char *sourcecode, - int submenu) + int submenu, int *index, struct bls_entry *bls, grub_blsuki_entry_t *blsuki) { int menu_hotkey = 0; char **menu_args = NULL; @@ -149,9 +149,12 @@ grub_normal_add_menu_entry (int argc, const char **args, if (! menu_title) goto fail; + grub_dprintf ("menu", "id:\"%s\"\n", id); + grub_dprintf ("menu", "title:\"%s\"\n", menu_title); menu_id = grub_strdup (id ? : menu_title); if (! menu_id) goto fail; + grub_dprintf ("menu", "menu_id:\"%s\"\n", menu_id); /* Save argc, args to pass as parameters to block arg later. */ menu_args = grub_calloc (argc + 1, sizeof (char *)); @@ -170,8 +173,12 @@ grub_normal_add_menu_entry (int argc, const char **args, } /* Add the menu entry at the end of the list. */ + int ind=0; while (*last) - last = &(*last)->next; + { + ind++; + last = &(*last)->next; + } *last = grub_zalloc (sizeof (**last)); if (! *last) @@ -188,8 +195,12 @@ grub_normal_add_menu_entry (int argc, const char **args, (*last)->args = menu_args; (*last)->sourcecode = menu_sourcecode; (*last)->submenu = submenu; + (*last)->bls = bls; + (*last)->blsuki = blsuki; menu->size++; + if (index) + *index = ind; return GRUB_ERR_NONE; fail: @@ -271,7 +282,7 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) if (! ctxt->state[3].set && ! ctxt->script) return grub_error (GRUB_ERR_BAD_ARGUMENT, "no menuentry definition"); - if (ctxt->state[1].set) + if (ctxt->state[1].set && ctxt->state[1].arg) users = ctxt->state[1].arg; else if (ctxt->state[5].set) users = NULL; @@ -286,7 +297,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) users, ctxt->state[2].arg, 0, ctxt->state[3].arg, - ctxt->extcmd->cmd->name[0] == 's'); + ctxt->extcmd->cmd->name[0] == 's', + NULL, NULL, NULL); src = args[argc - 1]; args[argc - 1] = NULL; @@ -303,7 +315,8 @@ grub_cmd_menuentry (grub_extcmd_context_t ctxt, int argc, char **args) ctxt->state[0].args, ctxt->state[4].arg, users, ctxt->state[2].arg, prefix, src + 1, - ctxt->extcmd->cmd->name[0] == 's'); + ctxt->extcmd->cmd->name[0] == 's', NULL, + NULL, NULL); src[len - 1] = ch; args[argc - 1] = src; diff --git a/grub-core/commands/minicmd.c b/grub-core/commands/minicmd.c index fa498931ed..eb786766a8 100644 --- a/grub-core/commands/minicmd.c +++ b/grub-core/commands/minicmd.c @@ -29,6 +29,10 @@ #include #include +#ifdef GRUB_MACHINE_EFI +#include +#endif + GRUB_MOD_LICENSE ("GPLv3+"); /* cat FILE */ @@ -167,7 +171,7 @@ grub_mini_cmd_lsmod (struct grub_command *cmd __attribute__ ((unused)), { grub_dl_dep_t dep; - grub_printf ("%s\t%d\t\t", mod->name, mod->ref_count); + grub_printf ("%s\t%" PRIuGRUB_UINT64_T "\t\t", mod->name, mod->ref_count); for (dep = mod->dep; dep; dep = dep->next) { if (dep != mod->dep) @@ -182,12 +186,31 @@ grub_mini_cmd_lsmod (struct grub_command *cmd __attribute__ ((unused)), } /* exit */ -static grub_err_t __attribute__ ((noreturn)) +static grub_err_t grub_mini_cmd_exit (struct grub_command *cmd __attribute__ ((unused)), - int argc __attribute__ ((unused)), - char *argv[] __attribute__ ((unused))) + int argc, char *argv[]) { - grub_exit (); + int retval = -1; + unsigned long n; + + if (argc < 0 || argc > 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + + if (argc == 1) + { + n = grub_strtoul (argv[0], 0, 10); + if (n != ~0UL) + retval = n; + } + +#ifdef GRUB_MACHINE_EFI + /* + * The "exit" command is often used to launch the next boot application. + * So, erase the secrets. + */ + grub_cryptodisk_erasesecrets (); +#endif + grub_exit (retval); /* Not reached. */ } @@ -203,8 +226,8 @@ GRUB_MOD_INIT(minicmd) grub_register_command ("help", grub_mini_cmd_help, 0, N_("Show this message.")); cmd_dump = - grub_register_command ("dump", grub_mini_cmd_dump, - N_("ADDR [SIZE]"), N_("Show memory contents.")); + grub_register_command_lockdown ("dump", grub_mini_cmd_dump, + N_("ADDR [SIZE]"), N_("Show memory contents.")); cmd_rmmod = grub_register_command ("rmmod", grub_mini_cmd_rmmod, N_("MODULE"), N_("Remove a module.")); diff --git a/grub-core/commands/pgp.c b/grub-core/commands/pgp.c index c6766f044a..fa3ef5c754 100644 --- a/grub-core/commands/pgp.c +++ b/grub-core/commands/pgp.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -146,10 +147,6 @@ const char *hashes[] = { [0x0b] = "sha224" }; -struct gcry_pk_spec *grub_crypto_pk_dsa; -struct gcry_pk_spec *grub_crypto_pk_ecdsa; -struct gcry_pk_spec *grub_crypto_pk_rsa; - static int dsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, const gcry_md_spec_t *hash, struct grub_public_subkey *sk); @@ -411,32 +408,7 @@ static int rsa_pad (gcry_mpi_t *hmpi, grub_uint8_t *hval, const gcry_md_spec_t *hash, struct grub_public_subkey *sk) { - grub_size_t tlen, emlen, fflen; - grub_uint8_t *em, *emptr; - unsigned nbits = gcry_mpi_get_nbits (sk->mpis[0]); - int ret; - tlen = hash->mdlen + hash->asnlen; - emlen = (nbits + 7) / 8; - if (emlen < tlen + 11) - return 1; - - em = grub_malloc (emlen); - if (!em) - return 1; - - em[0] = 0x00; - em[1] = 0x01; - fflen = emlen - tlen - 3; - for (emptr = em + 2; emptr < em + 2 + fflen; emptr++) - *emptr = 0xff; - *emptr++ = 0x00; - grub_memcpy (emptr, hash->asnoid, hash->asnlen); - emptr += hash->asnlen; - grub_memcpy (emptr, hval, hash->mdlen); - - ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0); - grub_free (em); - return ret; + return grub_crypto_rsa_pad(hmpi, hval, hash, sk->mpis[0]); } struct grub_pubkey_context @@ -972,7 +944,7 @@ GRUB_MOD_INIT(pgp) grub_memset (&pseudo_file, 0, sizeof (pseudo_file)); /* Not an ELF module, skip. */ - if (header->type != OBJ_TYPE_PUBKEY) + if (header->type != OBJ_TYPE_GPG_PUBKEY) continue; pseudo_file.fs = &pseudo_fs; @@ -1010,6 +982,8 @@ GRUB_MOD_INIT(pgp) GRUB_MOD_FINI(pgp) { + grub_register_variable_hook ("check_signatures", NULL, NULL); + grub_env_unset ("check_signatures"); grub_verifier_unregister (&grub_pubkey_verifier); grub_unregister_extcmd (cmd); grub_unregister_extcmd (cmd_trust); diff --git a/grub-core/commands/read.c b/grub-core/commands/read.c index 597c907065..8d72e45c93 100644 --- a/grub-core/commands/read.c +++ b/grub-core/commands/read.c @@ -25,6 +25,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -37,13 +38,14 @@ static const struct grub_arg_option options[] = static char * grub_getline (int silent) { - int i; + grub_size_t i; char *line; char *tmp; int c; + grub_size_t alloc_size; i = 0; - line = grub_malloc (1 + i + sizeof('\0')); + line = grub_malloc (1 + sizeof('\0')); if (! line) return NULL; @@ -59,8 +61,17 @@ grub_getline (int silent) line[i] = (char) c; if (!silent) grub_printf ("%c", c); - i++; - tmp = grub_realloc (line, 1 + i + sizeof('\0')); + if (grub_add (i, 1, &i)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + return NULL; + } + if (grub_add (i, 1 + sizeof('\0'), &alloc_size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + return NULL; + } + tmp = grub_realloc (line, alloc_size); if (! tmp) { grub_free (line); diff --git a/grub-core/commands/reboot.c b/grub-core/commands/reboot.c index 46d364c99a..f5cc228363 100644 --- a/grub-core/commands/reboot.c +++ b/grub-core/commands/reboot.c @@ -32,15 +32,18 @@ grub_cmd_reboot (grub_command_t cmd __attribute__ ((unused)), grub_reboot (); } -static grub_command_t cmd; +static grub_command_t reboot_cmd, reset_cmd; GRUB_MOD_INIT(reboot) { - cmd = grub_register_command ("reboot", grub_cmd_reboot, - 0, N_("Reboot the computer.")); + reboot_cmd = grub_register_command ("reboot", grub_cmd_reboot, + 0, N_("Reboot the computer.")); + reset_cmd = grub_register_command ("reset", grub_cmd_reboot, + 0, N_("Reboot the computer.")); } GRUB_MOD_FINI(reboot) { - grub_unregister_command (cmd); + grub_unregister_command (reboot_cmd); + grub_unregister_command (reset_cmd); } diff --git a/grub-core/commands/search.c b/grub-core/commands/search.c index 263f1501cd..c432c0ac6f 100644 --- a/grub-core/commands/search.c +++ b/grub-core/commands/search.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -54,6 +56,135 @@ struct search_ctx int is_cache; }; +static int +is_device_usb (const char *name) +{ + int ret = 0; + + grub_device_t dev = grub_device_open(name); + + if (dev) + { + struct grub_efidisk_data + { + grub_efi_handle_t handle; + grub_efi_device_path_t *device_path; + grub_efi_device_path_t *last_device_path; + grub_efi_block_io_t *block_io; + struct grub_efidisk_data *next; + }; + + if (dev->disk && dev->disk->data) + { + struct grub_efidisk_data *dp = dev->disk->data; + + if ( GRUB_EFI_DEVICE_PATH_TYPE (dp->last_device_path) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE && + GRUB_EFI_DEVICE_PATH_SUBTYPE (dp->last_device_path) == GRUB_EFI_USB_DEVICE_PATH_SUBTYPE) + { + ret = 1; + } + } + grub_device_close(dev); + } + + return ret; +} + +static int +get_device_uuid(const char *name, char** quid) +{ + int ret = 0; + + grub_device_t dev_part = grub_device_open(name); + + if (dev_part) + { + grub_fs_t fs; + + fs = grub_fs_probe (dev_part); + +#ifdef DO_SEARCH_FS_UUID +#define read_fn fs_uuid +#else +#define read_fn fs_label +#endif + if (fs && fs->read_fn) + { + fs->read_fn (dev_part, quid); + + if (grub_errno == GRUB_ERR_NONE && *quid) + { + ret = 1; + } + + } + grub_device_close (dev_part); + } + + return ret; +} +struct uuid_context { + char* name; + char* uuid; +}; + +static int +check_for_duplicate (const char *name, void *data) +{ + int ret = 0; + struct uuid_context * uuid_ctx = (struct uuid_context *)data; + char *quid = 0; + + get_device_uuid(name, &quid); + + if (quid == NULL) + return 0; + + if (!grub_strcasecmp(quid, uuid_ctx->uuid) && grub_strcasecmp(name, uuid_ctx->name)) + { + ret = 1; + } + + grub_free(quid); + + return ret; +} + +static bool +is_unencrypted_disk (grub_disk_t disk) +{ + grub_command_t cmd; + char *disk_str; + int disk_str_len; + int res; + + if (disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID) + return false; /* This is (crypto) disk. */ + + if (disk->dev->id == GRUB_DISK_DEVICE_DISKFILTER_ID) + { + char opt[] = "--quiet"; + char *args[2]; + + cmd = grub_command_find ("cryptocheck"); + if (cmd == NULL) /* No diskfilter module loaded for some reason. */ + return true; + + disk_str_len = grub_strlen (disk->name) + 2 + 1; + disk_str = grub_malloc (disk_str_len); + if (disk_str == NULL) /* Something is wrong, better report as unencrypted. */ + return true; + + grub_snprintf (disk_str, disk_str_len, "(%s)", disk->name); + args[0] = opt; + args[1] = disk_str; + res = cmd->func (cmd, 2, args); + grub_free (disk_str); + return (res != GRUB_ERR_NONE) ? true : false; /* GRUB_ERR_NONE for encrypted. */ + } + return true; +} + /* Helper for FUNC_NAME. */ static int iterate_device (const char *name, void *data) @@ -86,6 +217,86 @@ iterate_device (const char *name, void *data) grub_device_close (dev); } + /* Limit to encrypted disks when requested. */ + if (ctx->flags & SEARCH_FLAGS_CRYPTODISK_ONLY) + { + grub_device_t dev; + + dev = grub_device_open (name); + if (dev == NULL) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (dev->disk == NULL || is_unencrypted_disk (dev->disk) == true) + { + grub_device_close (dev); + grub_errno = GRUB_ERR_NONE; + return 0; + } + grub_device_close (dev); + } + + /* Skip it if it's not the root device when requested. */ + if (ctx->flags & SEARCH_FLAGS_ROOTDEV_ONLY) + { + const char *root_dev; + root_dev = grub_env_get ("root"); + if (root_dev != NULL && *root_dev != '\0') + { + char *root_disk = grub_malloc (grub_strlen(root_dev) + 1); + char *name_disk = grub_malloc (grub_strlen(name) + 1); + char *rem_1 = grub_malloc(grub_strlen(root_dev) + 1); + char *rem_2 = grub_malloc(grub_strlen(name) + 1); + + if (root_disk != NULL && name_disk != NULL && + rem_1 != NULL && rem_2 != NULL) + { + /* get just the disk name; partitions will be different. */ + grub_str_sep (root_dev, root_disk, ',', rem_1); + grub_str_sep (name, name_disk, ',', rem_2); + if (root_disk != NULL && *root_disk != '\0' && + name_disk != NULL && *name_disk != '\0') + { + grub_device_t dev, dev_part; + + if (is_device_usb(name) && !is_device_usb(root_dev)) + { + char *quid_name = NULL; + int longlist = 0; + struct uuid_context uuid_ctx; + int ret = 0; + + if (get_device_uuid(name, &quid_name)) + { + if (!grub_strcmp(quid_name, ctx->key)) + { + uuid_ctx.name = name; + uuid_ctx.uuid = quid_name; + + ret = grub_device_iterate (check_for_duplicate, &uuid_ctx); + + if (ret) + { + grub_printf("Duplicated media UUID found, rebooting ...\n"); + grub_sleep(10); + grub_reboot(); + } + } + + if (quid_name) grub_free (quid_name); + + } + } + } + } + grub_free (root_disk); + grub_free (name_disk); + grub_free (rem_1); + grub_free (rem_2); + } + } + #ifdef DO_SEARCH_FS_UUID #define compare_fn grub_strcasecmp #else diff --git a/grub-core/commands/search_wrap.c b/grub-core/commands/search_wrap.c index 318581f3b1..ec2fb638f1 100644 --- a/grub-core/commands/search_wrap.c +++ b/grub-core/commands/search_wrap.c @@ -41,6 +41,8 @@ static const struct grub_arg_option options[] = ARG_TYPE_STRING}, {"no-floppy", 'n', 0, N_("Do not probe any floppy drive."), 0, 0}, {"efidisk-only", 0, 0, N_("Only probe EFI disks."), 0, 0}, + {"cryptodisk-only", 0, 0, N_("Only probe encrypted disks."), 0, 0}, + {"root-dev-only", 'r', 0, N_("Only probe root device."), 0, 0}, {"hint", 'h', GRUB_ARG_OPTION_REPEATABLE, N_("First try the device HINT. If HINT ends in comma, " "also try subpartitions"), N_("HINT"), ARG_TYPE_STRING}, @@ -75,6 +77,8 @@ enum options SEARCH_SET, SEARCH_NO_FLOPPY, SEARCH_EFIDISK_ONLY, + SEARCH_CRYPTODISK_ONLY, + SEARCH_ROOTDEV_ONLY, SEARCH_HINT, SEARCH_HINT_IEEE1275, SEARCH_HINT_BIOS, @@ -189,6 +193,12 @@ grub_cmd_search (grub_extcmd_context_t ctxt, int argc, char **args) if (state[SEARCH_EFIDISK_ONLY].set) flags |= SEARCH_FLAGS_EFIDISK_ONLY; + if (state[SEARCH_CRYPTODISK_ONLY].set) + flags |= SEARCH_FLAGS_CRYPTODISK_ONLY; + + if (state[SEARCH_ROOTDEV_ONLY].set) + flags |= SEARCH_FLAGS_ROOTDEV_ONLY; + if (state[SEARCH_LABEL].set) grub_search_label (id, var, flags, hints, nhints); else if (state[SEARCH_FS_UUID].set) @@ -210,7 +220,7 @@ GRUB_MOD_INIT(search) cmd = grub_register_extcmd ("search", grub_cmd_search, GRUB_COMMAND_FLAG_EXTRACTOR | GRUB_COMMAND_ACCEPT_DASH, - N_("[-f|-l|-u|-s|-n] [--hint HINT [--hint HINT] ...]" + N_("[-f|-l|-u|-s|-n] [--cryptodisk-only] [--hint HINT [--hint HINT] ...]" " NAME"), N_("Search devices by file, filesystem label" " or filesystem UUID." diff --git a/grub-core/commands/test.c b/grub-core/commands/test.c index 62d3fb3988..ee47ab2641 100644 --- a/grub-core/commands/test.c +++ b/grub-core/commands/test.c @@ -29,6 +29,9 @@ GRUB_MOD_LICENSE ("GPLv3+"); +/* Set a limit on recursion to avoid stack overflow. */ +#define MAX_TEST_RECURSION_DEPTH 100 + /* A simple implementation for signed numbers. */ static int grub_strtosl (char *arg, const char ** const end, int base) @@ -150,7 +153,7 @@ get_fileinfo (char *path, struct test_parse_ctx *ctx) /* Parse a test expression starting from *argn. */ static int -test_parse (char **args, int *argn, int argc) +test_parse (char **args, int *argn, int argc, int *depth) { struct test_parse_ctx ctx = { .and = 1, @@ -387,13 +390,24 @@ test_parse (char **args, int *argn, int argc) if (grub_strcmp (args[*argn], ")") == 0) { (*argn)++; + if (*depth > 0) + (*depth)--; + return ctx.or || ctx.and; } /* Recursively invoke if parenthesis. */ if (grub_strcmp (args[*argn], "(") == 0) { (*argn)++; - update_val (test_parse (args, argn, argc), &ctx); + + if (++(*depth) > MAX_TEST_RECURSION_DEPTH) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("max recursion depth exceeded")); + (*depth)--; + return ctx.or || ctx.and; + } + + update_val (test_parse (args, argn, argc, depth), &ctx); continue; } @@ -428,11 +442,12 @@ grub_cmd_test (grub_command_t cmd __attribute__ ((unused)), int argc, char **args) { int argn = 0; + int depth = 0; if (argc >= 1 && grub_strcmp (args[argc - 1], "]") == 0) argc--; - return test_parse (args, &argn, argc) ? GRUB_ERR_NONE + return test_parse (args, &argn, argc, &depth) ? GRUB_ERR_NONE : grub_error (GRUB_ERR_TEST_FAILURE, N_("false")); } diff --git a/grub-core/commands/tpm.c b/grub-core/commands/tpm.c index 324423ef86..9fbbada65b 100644 --- a/grub-core/commands/tpm.c +++ b/grub-core/commands/tpm.c @@ -29,6 +29,27 @@ GRUB_MOD_LICENSE ("GPLv3+"); +/* By default, enable all PCR's */ +static grub_uint32_t pcr_mask = 0xffffffff; + +inline bool +grub_tpm_pcr_is_enabled (grub_uint8_t pcr) +{ + return pcr_mask & ( 1 << pcr ); +} + +inline void +grub_tpm_enable_pcr (grub_uint8_t pcr) +{ + pcr_mask |= ( 1 << pcr ); +} + +inline void +grub_tpm_disable_pcr (grub_uint8_t pcr) +{ + pcr_mask &= ~( 1 << pcr ); +} + static grub_err_t grub_tpm_verify_init (grub_file_t io, enum grub_file_type type __attribute__ ((unused)), @@ -36,6 +57,11 @@ grub_tpm_verify_init (grub_file_t io, { *context = io->name; *flags |= GRUB_VERIFY_FLAGS_SINGLE_CHUNK; + +#if DISABLE_PCR8 + grub_tpm_disable_pcr (GRUB_STRING_PCR); +#endif + return GRUB_ERR_NONE; } @@ -56,7 +82,7 @@ grub_tpm_verify_string (char *str, enum grub_verify_string_type type) { const char *prefix = NULL; char *description; - grub_err_t status; + grub_err_t status = GRUB_ERR_NONE; switch (type) { @@ -76,9 +102,13 @@ grub_tpm_verify_string (char *str, enum grub_verify_string_type type) grub_memcpy (description, prefix, grub_strlen (prefix)); grub_memcpy (description + grub_strlen (prefix), str, grub_strlen (str) + 1); +#if DISABLE_PCR8 + grub_dprintf ("tpm", "String measurement disabled for %s\n", description); +#else status = grub_tpm_measure ((unsigned char *) str, grub_strlen (str), GRUB_STRING_PCR, description); +#endif grub_free (description); if (status == GRUB_ERR_NONE) return GRUB_ERR_NONE; diff --git a/grub-core/commands/usbtest.c b/grub-core/commands/usbtest.c index 2c6d93fe66..3184ac9afd 100644 --- a/grub-core/commands/usbtest.c +++ b/grub-core/commands/usbtest.c @@ -90,7 +90,7 @@ grub_usb_get_string (grub_usb_device_t dev, grub_uint8_t index, int langid, 0x06, (3 << 8) | index, langid, descstr.length, (char *) descstrp); - if (descstrp->length == 0) + if (descstrp->length < 2) { grub_free (descstrp); *string = grub_strdup (""); @@ -99,7 +99,7 @@ grub_usb_get_string (grub_usb_device_t dev, grub_uint8_t index, int langid, return GRUB_USB_ERR_NONE; } - *string = grub_malloc (descstr.length * 2 + 1); + *string = grub_malloc (descstrp->length * 2 + 1); if (! *string) { grub_free (descstrp); diff --git a/grub-core/commands/version.c b/grub-core/commands/version.c new file mode 100644 index 0000000000..de0acb07ba --- /dev/null +++ b/grub-core/commands/version.c @@ -0,0 +1,56 @@ +/* version.c - Command to print the grub version and build info. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_err_t +grub_cmd_version (grub_command_t cmd UNUSED, int argc, char **args UNUSED) +{ + if (argc != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("no arguments expected")); + + grub_printf (_("GRUB version %s\n"), PACKAGE_VERSION); + grub_printf (_("Platform %s-%s\n"), GRUB_TARGET_CPU, GRUB_PLATFORM); + if (grub_strlen(GRUB_RPM_VERSION) != 0) + grub_printf (_("RPM package version %s\n"), GRUB_RPM_VERSION); + grub_printf (_("Compiler version %s\n"), __VERSION__); + + return 0; +} + +static grub_command_t cmd; + +GRUB_MOD_INIT(version) +{ + cmd = grub_register_command ("version", grub_cmd_version, NULL, + N_("Print version and build information.")); +} + +GRUB_MOD_FINI(version) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/commands/wildcard.c b/grub-core/commands/wildcard.c index ed65865050..5455242c35 100644 --- a/grub-core/commands/wildcard.c +++ b/grub-core/commands/wildcard.c @@ -488,6 +488,12 @@ check_file (const char *dir, const char *basename) return ctx.found; } +static int +is_hex(char c) +{ + return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + static void unescape (char *out, const char *in, const char *end) { @@ -496,7 +502,15 @@ unescape (char *out, const char *in, const char *end) for (optr = out, iptr = in; iptr < end;) { - if (*iptr == '\\' && iptr + 1 < end) + if (*iptr == '\\' && iptr + 3 < end && iptr[1] == 'x' && is_hex(iptr[2]) && is_hex(iptr[3])) + { + *optr++ = *iptr++; + *optr++ = *iptr++; + *optr++ = *iptr++; + *optr++ = *iptr++; + continue; + } + else if (*iptr == '\\' && iptr + 1 < end) { *optr++ = iptr[1]; iptr += 2; diff --git a/grub-core/disk/ahci.c b/grub-core/disk/ahci.c index e7b5dc5f20..b247161b90 100644 --- a/grub-core/disk/ahci.c +++ b/grub-core/disk/ahci.c @@ -1038,7 +1038,7 @@ grub_ahci_readwrite_real (struct grub_ahci_device *dev, grub_dprintf ("ahci", "AHCI tfd = %x\n", dev->hba->ports[dev->port].task_file_data); - endtime = grub_get_time_ms () + (spinup ? 20000 : 20000); + endtime = grub_get_time_ms () + 20000; while ((dev->hba->ports[dev->port].command_issue & 1)) if (grub_get_time_ms () > endtime || (dev->hba->ports[dev->port].intstatus & GRUB_AHCI_HBA_PORT_IS_FATAL_MASK)) diff --git a/grub-core/disk/ata.c b/grub-core/disk/ata.c index 7b6ac7bfc7..a2433e29e2 100644 --- a/grub-core/disk/ata.c +++ b/grub-core/disk/ata.c @@ -112,10 +112,10 @@ grub_ata_identify (struct grub_ata *dev) return grub_atapi_identify (dev); info64 = grub_malloc (GRUB_DISK_SECTOR_SIZE); + if (info64 == NULL) + return grub_errno; info32 = (grub_uint32_t *) info64; info16 = (grub_uint16_t *) info64; - if (! info16) - return grub_errno; grub_memset (&parms, 0, sizeof (parms)); parms.buffer = info16; diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 2246af51b6..26457095d3 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 @@ -721,7 +722,7 @@ grub_cryptodisk_open (const char *name, grub_disk_t disk) #ifdef GRUB_UTIL if (dev->cheat) { - grub_uint64_t cheat_dev_size; + grub_int64_t cheat_dev_size; unsigned int cheat_log_sector_size; if (!GRUB_UTIL_FD_IS_VALID (dev->cheat_fd)) @@ -1144,7 +1145,9 @@ grub_cryptodisk_scan_device_real (const char *name, ret = grub_cryptodisk_insert (dev, name, source); if (ret != GRUB_ERR_NONE) goto error; - +#ifndef GRUB_UTIL + grub_cli_set_auth_needed (); +#endif goto cleanup; } grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk module can handle this device"); @@ -1161,6 +1164,7 @@ grub_cryptodisk_scan_device_real (const char *name, if (askpass) { + grub_memset (cargs->key_data, 0, cargs->key_len); cargs->key_len = 0; grub_free (cargs->key_data); } @@ -1473,7 +1477,7 @@ static char * luks_script_get (grub_size_t *sz) { grub_cryptodisk_t i; - grub_size_t size = 0; + grub_size_t size = 0, mul; char *ptr, *ret; *sz = 0; @@ -1482,10 +1486,6 @@ luks_script_get (grub_size_t *sz) if (grub_strcmp (i->modname, "luks") == 0 || grub_strcmp (i->modname, "luks2") == 0) { - size += grub_strlen (i->modname); - size += sizeof ("_mount"); - size += grub_strlen (i->uuid); - size += grub_strlen (i->cipher->cipher->name); /* * Add space in the line for (in order) spaces, cipher mode, cipher IV * mode, sector offset, sector size and the trailing newline. This is @@ -1493,14 +1493,35 @@ luks_script_get (grub_size_t *sz) * in an earlier version of this code that are unaccounted for. It is * left in the calculations in case it is needed. At worst, its short- * lived wasted space. + * + * 60 = 5 + 5 + 8 + 20 + 6 + 1 + 15 */ - size += 5 + 5 + 8 + 20 + 6 + 1 + 15; + if (grub_add (size, grub_strlen (i->modname), &size) || + grub_add (size, sizeof ("_mount") + 60, &size) || + grub_add (size, grub_strlen (i->uuid), &size) || + grub_add (size, grub_strlen (i->cipher->cipher->name), &size) || + grub_mul (i->keysize, 2, &mul) || + grub_add (size, mul, &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while obtaining size of luks script"); + return 0; + } if (i->essiv_hash) - size += grub_strlen (i->essiv_hash->name); - size += i->keysize * 2; + { + if (grub_add (size, grub_strlen (i->essiv_hash->name), &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while obtaining size of luks script"); + return 0; + } + } } + if (grub_add (size, 1, &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while obtaining size of luks script"); + return 0; + } - ret = grub_malloc (size + 1); + ret = grub_malloc (size); if (!ret) return 0; @@ -1576,6 +1597,114 @@ luks_script_get (grub_size_t *sz) return ret; } +#ifdef GRUB_MACHINE_EFI +grub_err_t +grub_cryptodisk_challenge_password (void) +{ + grub_cryptodisk_t cr_dev; + + for (cr_dev = cryptodisk_list; cr_dev != NULL; cr_dev = cr_dev->next) + { + grub_cryptodisk_dev_t cr; + grub_disk_t source = NULL; + grub_err_t ret = GRUB_ERR_NONE; + grub_cryptodisk_t dev = NULL; + char *part = NULL; + struct grub_cryptomount_args cargs = {0}; + + cargs.check_boot = 0; + cargs.search_uuid = cr_dev->uuid; + + source = grub_disk_open (cr_dev->source); + + if (source == NULL) + { + ret = grub_errno; + goto error_out; + } + + FOR_CRYPTODISK_DEVS (cr) + { + dev = cr->scan (source, &cargs); + if (grub_errno) + { + ret = grub_errno; + goto error_out; + } + if (dev == NULL) + continue; + break; + } + + if (dev == NULL) + { + ret = grub_error (GRUB_ERR_BAD_MODULE, "no cryptodisk module can handle this device"); + goto error_out; + } + + 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"), cr_dev->uuid); + grub_free (part); + + cargs.key_data = grub_malloc (GRUB_CRYPTODISK_MAX_PASSPHRASE); + if (cargs.key_data == NULL) + { + ret = grub_errno; + goto error_out; + } + + if (!grub_password_get ((char *) cargs.key_data, GRUB_CRYPTODISK_MAX_PASSPHRASE)) + { + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "passphrase not supplied"); + goto error_out; + } + cargs.key_len = grub_strlen ((char *) cargs.key_data); + ret = cr->recover_key (source, dev, &cargs); + + error_out: + grub_disk_close (source); + if (dev != NULL) + cryptodisk_close (dev); + if (cargs.key_data) + { + grub_memset (cargs.key_data, 0, cargs.key_len); + grub_free (cargs.key_data); + } + + return ret; + } + + return GRUB_ERR_NONE; +} + +void +grub_cryptodisk_erasesecrets (void) +{ + grub_cryptodisk_t i; + grub_uint8_t *buf; + + buf = grub_zalloc (GRUB_CRYPTODISK_MAX_KEYLEN); + if (buf == NULL) + grub_fatal ("grub_cryptodisk_erasesecrets: cannot allocate memory"); + + for (i = cryptodisk_list; i != NULL; i = i->next) + if (grub_cryptodisk_setkey (i, buf, i->keysize)) + grub_fatal ("grub_cryptodisk_erasesecrets: cannot erase secrets for %s", i->source); + else + grub_printf ("Erased crypto secrets for %s\n", i->source); + /* + * Unfortunately, there is no way to "force unmount" a given disk, it may + * have mounted "child" disks as well, e.g., an LVM volume. So, this + * function MUST be called when there is no way back, e.g., when exiting. + * Otherwise, subsequent read calls for a cryptodisk will return garbage. + */ + + grub_free (buf); +} +#endif /* GRUB_MACHINE_EFI */ + struct grub_procfs_entry luks_script = { .name = "luks_script", @@ -1597,6 +1726,9 @@ GRUB_MOD_INIT (cryptodisk) GRUB_MOD_FINI (cryptodisk) { +#ifdef GRUB_MACHINE_EFI + grub_cryptodisk_erasesecrets (); +#endif grub_disk_dev_unregister (&grub_cryptodisk_dev); cryptodisk_cleanup (); grub_unregister_extcmd (cmd); diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c index 21e2395111..04b64aebc6 100644 --- a/grub-core/disk/diskfilter.c +++ b/grub-core/disk/diskfilter.c @@ -20,10 +20,12 @@ #include #include #include +#include #include #include #include #include +#include #ifdef GRUB_UTIL #include #include @@ -188,6 +190,8 @@ scan_disk (const char *name, int accept_diskfilter) grub_disk_t disk; static int scan_depth = 0; + grub_dprintf ("diskfilter", "scanning %s\n", name); + if (!accept_diskfilter && is_valid_diskfilter_name (name)) return 0; @@ -1039,7 +1043,7 @@ grub_diskfilter_make_raid (grub_size_t uuidlen, char *uuid, int nmemb, { struct grub_diskfilter_vg *array; int i; - grub_size_t j; + grub_size_t j, sz; grub_uint64_t totsize; struct grub_diskfilter_pv *pv; grub_err_t err; @@ -1140,7 +1144,11 @@ grub_diskfilter_make_raid (grub_size_t uuidlen, char *uuid, int nmemb, } array->lvs->vg = array; - array->lvs->idname = grub_malloc (sizeof ("mduuid/") + 2 * uuidlen); + if (grub_mul (uuidlen, 2, &sz) || + grub_add (sz, sizeof ("mduuid/"), &sz)) + goto fail; + + array->lvs->idname = grub_malloc (sz); if (!array->lvs->idname) goto fail; @@ -1247,6 +1255,7 @@ insert_array (grub_disk_t disk, const struct grub_diskfilter_pv_id *id, the same. */ if (pv->disk && grub_disk_native_sectors (disk) >= pv->part_size) return GRUB_ERR_NONE; + grub_dprintf ("diskfilter", "checking %s\n", disk->name); pv->disk = grub_disk_open (disk->name); if (!pv->disk) return grub_errno; @@ -1357,6 +1366,86 @@ grub_diskfilter_get_pv_from_disk (grub_disk_t disk, } #endif +static int +grub_diskfilter_check_pvs_encrypted (grub_disk_t disk, int *pvs_cnt) +{ + struct grub_diskfilter_lv *lv = disk->data; + struct grub_diskfilter_pv *pv; + + *pvs_cnt = 0; + + if (lv->vg->pvs) + for (pv = lv->vg->pvs; pv; pv = pv->next) + { + (*pvs_cnt)++; + + if (pv->disk == NULL) + { + /* Can be a partially activated VG, bail out. */ + return GRUB_ERR_TEST_FAILURE; + } + + if (pv->disk->dev->id != GRUB_DISK_DEVICE_CRYPTODISK_ID) + { + /* All backing devices must be cryptodisks, stop. */ + return GRUB_ERR_TEST_FAILURE; + } + } + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_cryptocheck (grub_command_t cmd __attribute__ ((unused)), + int argc, char **args) +{ + grub_disk_t disk; + int check_pvs_res; + int namelen; + int pvs_cnt; + int opt_quiet = 0; + + if (argc == 2) + { + if (grub_strcmp (args[0], "--quiet") == 0) + { + opt_quiet = 1; + argc--; + args++; + } + else + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized option: %s"), args[0]); + } + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("disk name expected")); + + namelen = grub_strlen (args[0]); + if (namelen > 2 && (args[0][0] == '(') && (args[0][namelen - 1] == ')')) + args[0][namelen - 1] = 0; + else + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("invalid disk: %s"), + args[0]); + + if (!is_valid_diskfilter_name (&args[0][1])) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("unrecognized disk: %s"), + &args[0][1]); + + disk = grub_disk_open (&args[0][1]); + if (disk == NULL) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("no such disk: %s"), + &args[0][1]); + + check_pvs_res = grub_diskfilter_check_pvs_encrypted (disk, &pvs_cnt); + grub_disk_close (disk); + if (!opt_quiet) + grub_printf ("%s is %sencrypted (%d pv%s examined)\n", &args[0][1], + (check_pvs_res == GRUB_ERR_NONE) ? "" : "un", + pvs_cnt, + (pvs_cnt > 1) ? "s" : ""); + + return check_pvs_res; +} + static struct grub_disk_dev grub_diskfilter_dev = { .name = "diskfilter", @@ -1373,14 +1462,21 @@ static struct grub_disk_dev grub_diskfilter_dev = .next = 0 }; +static grub_command_t cmd; + GRUB_MOD_INIT(diskfilter) { grub_disk_dev_register (&grub_diskfilter_dev); + cmd = grub_register_command ("cryptocheck", grub_cmd_cryptocheck, + N_("[--quiet] DEVICE"), + N_("Check if a logical volume resides on encrypted disks.")); } GRUB_MOD_FINI(diskfilter) { grub_disk_dev_unregister (&grub_diskfilter_dev); + if (cmd != NULL) + grub_unregister_command (cmd); free_array (); } diff --git a/grub-core/disk/efi/efidisk.c b/grub-core/disk/efi/efidisk.c index 3b5ed56913..af69cc3bc8 100644 --- a/grub-core/disk/efi/efidisk.c +++ b/grub-core/disk/efi/efidisk.c @@ -396,6 +396,19 @@ enumerate_disks (void) free_devices (devices); } +void +grub_efidisk_reenumerate_disks (void) +{ + free_devices (fd_devices); + free_devices (hd_devices); + free_devices (cd_devices); + fd_devices = 0; + hd_devices = 0; + cd_devices = 0; + + enumerate_disks (); +} + static int grub_efidisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, grub_disk_pull_t pull) @@ -865,6 +878,7 @@ grub_efidisk_get_device_name (grub_efi_handle_t *handle) return 0; } + grub_dprintf ("efidisk", "getting disk for %s\n", device_name); parent = grub_disk_open (device_name); grub_free (dup_dp); diff --git a/grub-core/disk/ieee1275/obdisk.c b/grub-core/disk/ieee1275/obdisk.c index cd923b90fb..fcc39e0a23 100644 --- a/grub-core/disk/ieee1275/obdisk.c +++ b/grub-core/disk/ieee1275/obdisk.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -128,9 +129,17 @@ count_commas (const char *src) static char * decode_grub_devname (const char *name) { - char *devpath = grub_malloc (grub_strlen (name) + 1); + char *devpath; char *p, c; + grub_size_t sz; + if (grub_add (grub_strlen (name), 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of device name")); + return NULL; + } + + devpath = grub_malloc (sz); if (devpath == NULL) return NULL; @@ -156,12 +165,20 @@ static char * encode_grub_devname (const char *path) { char *encoding, *optr; + grub_size_t sz; if (path == NULL) return NULL; - encoding = grub_malloc (sizeof (IEEE1275_DEV) + count_commas (path) + - grub_strlen (path) + 1); + if (grub_add (sizeof (IEEE1275_DEV) + 1, count_commas (path), &sz) || + grub_add (sz, grub_strlen (path), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining encoding size")); + grub_print_error (); + return NULL; + } + + encoding = grub_malloc (sz); if (encoding == NULL) { @@ -396,8 +413,22 @@ canonicalise_disk (const char *devname) real_unit_str_len = grub_strlen (op->name) + sizeof (IEEE1275_DISK_ALIAS) + grub_strlen (real_unit_address); + if (grub_add (grub_strlen (op->name), sizeof (IEEE1275_DISK_ALIAS), &real_unit_str_len) || + grub_add (real_unit_str_len, grub_strlen (real_unit_address), &real_unit_str_len)) + { + grub_free (parent); + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of canonical name")); + grub_print_error (); + return NULL; + } real_canon = grub_malloc (real_unit_str_len); + if (real_canon == NULL) + { + grub_free (parent); + grub_print_error (); + return NULL; + } grub_snprintf (real_canon, real_unit_str_len, "%s/disk@%s", op->name, real_unit_address); @@ -413,6 +444,7 @@ canonicalise_disk (const char *devname) static struct disk_dev * add_canon_disk (const char *cname) { + grub_size_t sz; struct disk_dev *dev; dev = grub_zalloc (sizeof (struct disk_dev)); @@ -428,13 +460,18 @@ add_canon_disk (const char *cname) * arguments and allows a client program to open * the entire (raw) disk. Any disk label is ignored. */ - dev->raw_name = grub_malloc (grub_strlen (cname) + sizeof (":nolabel")); + if (grub_add (grub_strlen (cname), sizeof (":nolabel"), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while appending :nolabel to end of canonical name"); + goto failed; + } + + dev->raw_name = grub_malloc (sz); if (dev->raw_name == NULL) goto failed; - grub_snprintf (dev->raw_name, grub_strlen (cname) + sizeof (":nolabel"), - "%s:nolabel", cname); + grub_snprintf (dev->raw_name, sz, "%s:nolabel", cname); } /* diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c index c6cba0c8aa..71237b2560 100644 --- a/grub-core/disk/ieee1275/ofdisk.c +++ b/grub-core/disk/ieee1275/ofdisk.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include static char *last_devpath; static grub_ieee1275_ihandle_t last_ihandle; @@ -44,7 +46,7 @@ struct ofdisk_hash_ent }; static grub_err_t -grub_ofdisk_get_block_size (const char *device, grub_uint32_t *block_size, +grub_ofdisk_get_block_size (grub_uint32_t *block_size, struct ofdisk_hash_ent *op); #define OFDISK_HASH_SZ 8 @@ -80,6 +82,7 @@ ofdisk_hash_add_real (char *devpath) struct ofdisk_hash_ent **head = &ofdisk_hash[ofdisk_hash_fn(devpath)]; const char *iptr; char *optr; + grub_size_t sz; p = grub_zalloc (sizeof (*p)); if (!p) @@ -87,8 +90,14 @@ ofdisk_hash_add_real (char *devpath) p->devpath = devpath; - p->grub_devpath = grub_malloc (sizeof ("ieee1275/") - + 2 * grub_strlen (p->devpath)); + if (grub_mul (grub_strlen (p->devpath), 2, &sz) || + grub_add (sz, sizeof ("ieee1275/"), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of device path")); + return NULL; + } + + p->grub_devpath = grub_malloc (sz); if (!p->grub_devpath) { @@ -98,7 +107,13 @@ ofdisk_hash_add_real (char *devpath) if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_NO_PARTITION_0)) { - p->open_path = grub_malloc (grub_strlen (p->devpath) + 3); + if (grub_add (grub_strlen (p->devpath), 3, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of an open path")); + return NULL; + } + + p->open_path = grub_malloc (sz); if (!p->open_path) { grub_free (p->grub_devpath); @@ -224,8 +239,10 @@ dev_iterate (const struct grub_ieee1275_devalias *alias) args; char *buf, *bufptr; unsigned i; + grub_size_t sz; - if (grub_ieee1275_open (alias->path, &ihandle)) + RETRY_IEEE1275_OFDISK_OPEN(alias->path, &ihandle) + if (! ihandle) return; /* This method doesn't need memory allocation for the table. Open @@ -243,9 +260,19 @@ dev_iterate (const struct grub_ieee1275_devalias *alias) return; } - buf = grub_malloc (grub_strlen (alias->path) + 32); + if (grub_add (grub_strlen (alias->path), 32, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while creating buffer for vscsi"); + grub_ieee1275_close (ihandle); + return; + } + + buf = grub_malloc (sz); if (!buf) - return; + { + grub_ieee1275_close (ihandle); + return; + } bufptr = grub_stpcpy (buf, alias->path); for (i = 0; i < args.nentries; i++) @@ -287,9 +314,15 @@ dev_iterate (const struct grub_ieee1275_devalias *alias) grub_uint64_t *table; grub_uint16_t table_size; grub_ieee1275_ihandle_t ihandle; + grub_size_t sz; + + if (grub_add (grub_strlen (alias->path), sizeof ("/disk@7766554433221100"), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while creating buffer for sas_ioa"); + return; + } - buf = grub_malloc (grub_strlen (alias->path) + - sizeof ("/disk@7766554433221100")); + buf = grub_malloc (sz); if (!buf) return; bufptr = grub_stpcpy (buf, alias->path); @@ -305,7 +338,9 @@ dev_iterate (const struct grub_ieee1275_devalias *alias) return; } - if (grub_ieee1275_open (alias->path, &ihandle)) + RETRY_IEEE1275_OFDISK_OPEN(alias->path, &ihandle); + + if (! ihandle) { grub_free (buf); grub_free (table); @@ -427,9 +462,17 @@ grub_ofdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data, static char * compute_dev_path (const char *name) { - char *devpath = grub_malloc (grub_strlen (name) + 3); + char *devpath; char *p, c; + grub_size_t sz; + + if (grub_add (grub_strlen (name), 3, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining size of device path")); + return NULL; + } + devpath = grub_malloc (sz); if (!devpath) return NULL; @@ -461,6 +504,7 @@ grub_ofdisk_open (const char *name, grub_disk_t disk) grub_ssize_t actual; grub_uint32_t block_size = 0; grub_err_t err; + struct ofdisk_hash_ent *op; if (grub_strncmp (name, "ieee1275/", sizeof ("ieee1275/") - 1) != 0) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, @@ -471,6 +515,35 @@ grub_ofdisk_open (const char *name, grub_disk_t disk) grub_dprintf ("disk", "Opening `%s'.\n", devpath); + op = ofdisk_hash_find (devpath); + if (!op) + op = ofdisk_hash_add (devpath, NULL); + if (!op) + { + grub_free (devpath); + return grub_errno; + } + + /* Check if the call to open is the same to the last disk already opened */ + if (last_devpath && !grub_strcmp(op->open_path,last_devpath)) + { + goto finish; + } + + /* If not, we need to close the previous disk and open the new one */ + else { + if (last_ihandle){ + grub_ieee1275_close (last_ihandle); + } + last_ihandle = 0; + last_devpath = NULL; + + RETRY_IEEE1275_OFDISK_OPEN(op->open_path, &last_ihandle); + if (! last_ihandle) + return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); + last_devpath = op->open_path; + } + if (grub_ieee1275_finddevice (devpath, &dev)) { grub_free (devpath); @@ -491,25 +564,18 @@ grub_ofdisk_open (const char *name, grub_disk_t disk) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a block device"); } + + finish: /* XXX: There is no property to read the number of blocks. There should be a property `#blocks', but it is not there. Perhaps it is possible to use seek for this. */ disk->total_sectors = GRUB_DISK_SIZE_UNKNOWN; { - struct ofdisk_hash_ent *op; - op = ofdisk_hash_find (devpath); - if (!op) - op = ofdisk_hash_add (devpath, NULL); - if (!op) - { - grub_free (devpath); - return grub_errno; - } disk->id = (unsigned long) op; disk->data = op->open_path; - err = grub_ofdisk_get_block_size (devpath, &block_size, op); + err = grub_ofdisk_get_block_size (&block_size, op); if (err) { grub_free (devpath); @@ -528,13 +594,6 @@ grub_ofdisk_open (const char *name, grub_disk_t disk) static void grub_ofdisk_close (grub_disk_t disk) { - if (disk->data == last_devpath) - { - if (last_ihandle) - grub_ieee1275_close (last_ihandle); - last_ihandle = 0; - last_devpath = NULL; - } disk->data = 0; } @@ -551,7 +610,7 @@ grub_ofdisk_prepare (grub_disk_t disk, grub_disk_addr_t sector) last_ihandle = 0; last_devpath = NULL; - grub_ieee1275_open (disk->data, &last_ihandle); + RETRY_IEEE1275_OFDISK_OPEN(disk->data, &last_ihandle); if (! last_ihandle) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); last_devpath = disk->data; @@ -578,12 +637,23 @@ grub_ofdisk_read (grub_disk_t disk, grub_disk_addr_t sector, return err; grub_ieee1275_read (last_ihandle, buf, size << disk->log_sector_size, &actual); - if (actual != (grub_ssize_t) (size << disk->log_sector_size)) + int i = 0; + while(actual != (grub_ssize_t) (size << disk->log_sector_size)){ + if (i>MAX_RETRIES){ return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx " "from `%s'"), (unsigned long long) sector, disk->name); + } + last_devpath = NULL; + err = grub_ofdisk_prepare (disk, sector); + if (err) + return err; + grub_ieee1275_read (last_ihandle, buf, size << disk->log_sector_size, + &actual); + i++; + } return 0; } @@ -625,6 +695,7 @@ insert_bootpath (void) char *bootpath; grub_ssize_t bootpath_size; char *type; + grub_size_t sz; if (grub_ieee1275_get_property_length (grub_ieee1275_chosen, "bootpath", &bootpath_size) @@ -635,7 +706,13 @@ insert_bootpath (void) return; } - bootpath = (char *) grub_malloc ((grub_size_t) bootpath_size + 64); + if (grub_add (bootpath_size, 64, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining bootpath size")); + return; + } + + bootpath = (char *) grub_malloc (sz); if (! bootpath) { grub_print_error (); @@ -681,7 +758,7 @@ grub_ofdisk_init (void) } static grub_err_t -grub_ofdisk_get_block_size (const char *device, grub_uint32_t *block_size, +grub_ofdisk_get_block_size (grub_uint32_t *block_size, struct ofdisk_hash_ent *op) { struct size_args_ieee1275 @@ -694,16 +771,6 @@ grub_ofdisk_get_block_size (const char *device, grub_uint32_t *block_size, grub_ieee1275_cell_t size2; } args_ieee1275; - if (last_ihandle) - grub_ieee1275_close (last_ihandle); - - last_ihandle = 0; - last_devpath = NULL; - - grub_ieee1275_open (device, &last_ihandle); - if (! last_ihandle) - return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); - *block_size = 0; if (op->block_size_fails >= 2) diff --git a/grub-core/disk/ldm.c b/grub-core/disk/ldm.c index 34bfe6bd13..048e29cd09 100644 --- a/grub-core/disk/ldm.c +++ b/grub-core/disk/ldm.c @@ -220,6 +220,7 @@ make_vg (grub_disk_t disk, struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_ldm_vblk)]; unsigned i; + grub_size_t sz; err = grub_disk_read (disk, cursec, 0, sizeof(vblk), &vblk); if (err) @@ -251,7 +252,13 @@ make_vg (grub_disk_t disk, grub_free (pv); goto fail2; } - pv->internal_id = grub_malloc (ptr[0] + 2); + if (grub_add (ptr[0], 2, &sz)) + { + grub_free (pv); + goto fail2; + } + + pv->internal_id = grub_malloc (sz); if (!pv->internal_id) { grub_free (pv); @@ -276,7 +283,21 @@ make_vg (grub_disk_t disk, goto fail2; } pv->id.uuidlen = *ptr; - pv->id.uuid = grub_malloc (pv->id.uuidlen + 1); + + if (grub_add (pv->id.uuidlen, 1, &sz)) + { + grub_free (pv->internal_id); + grub_free (pv); + goto fail2; + } + + pv->id.uuid = grub_malloc (sz); + if (pv->id.uuid == NULL) + { + grub_free (pv->internal_id); + grub_free (pv); + goto fail2; + } grub_memcpy (pv->id.uuid, ptr + 1, pv->id.uuidlen); pv->id.uuid[pv->id.uuidlen] = 0; @@ -343,7 +364,13 @@ make_vg (grub_disk_t disk, grub_free (lv); goto fail2; } - lv->internal_id = grub_malloc ((grub_size_t) ptr[0] + 2); + if (grub_add (ptr[0], 2, &sz)) + { + grub_free (lv->segments); + grub_free (lv); + goto fail2; + } + lv->internal_id = grub_malloc (sz); if (!lv->internal_id) { grub_free (lv); @@ -455,6 +482,7 @@ make_vg (grub_disk_t disk, struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_ldm_vblk)]; unsigned i; + grub_size_t sz; err = grub_disk_read (disk, cursec, 0, sizeof(vblk), &vblk); if (err) @@ -490,7 +518,12 @@ make_vg (grub_disk_t disk, grub_free (comp); goto fail2; } - comp->internal_id = grub_malloc ((grub_size_t) ptr[0] + 2); + if (grub_add (ptr[0], 2, &sz)) + { + grub_free (comp); + goto fail2; + } + comp->internal_id = grub_malloc (sz); if (!comp->internal_id) { grub_free (comp); @@ -640,7 +673,6 @@ make_vg (grub_disk_t disk, if (lv->segments->node_alloc == lv->segments->node_count) { void *t; - grub_size_t sz; if (grub_mul (lv->segments->node_alloc, 2, &lv->segments->node_alloc) || grub_mul (lv->segments->node_alloc, sizeof (*lv->segments->nodes), &sz)) diff --git a/grub-core/disk/loopback.c b/grub-core/disk/loopback.c index 4635dcfdee..f392813239 100644 --- a/grub-core/disk/loopback.c +++ b/grub-core/disk/loopback.c @@ -21,20 +21,14 @@ #include #include #include +#include #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); -struct grub_loopback -{ - char *devname; - grub_file_t file; - struct grub_loopback *next; - unsigned long id; -}; - static struct grub_loopback *loopback_list; static unsigned long last_id = 0; @@ -64,6 +58,8 @@ delete_loopback (const char *name) if (! dev) return grub_error (GRUB_ERR_BAD_DEVICE, "device not found"); + if (dev->refcnt > 0) + return grub_error (GRUB_ERR_STILL_REFERENCED, "device still referenced"); /* Remove the device from the list. */ *prev = dev->next; @@ -120,6 +116,7 @@ grub_cmd_loopback (grub_extcmd_context_t ctxt, int argc, char **args) newdev->file = file; newdev->id = last_id++; + newdev->refcnt = 0; /* Add the new entry to the list. */ newdev->next = loopback_list; @@ -161,6 +158,9 @@ grub_loopback_open (const char *name, grub_disk_t disk) if (! dev) return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "can't open device"); + if (grub_add (dev->refcnt, 1, &dev->refcnt)) + grub_fatal ("Reference count overflow"); + /* Use the filesize for the disk size, round up to a complete sector. */ if (dev->file->size != GRUB_FILE_SIZE_UNKNOWN) disk->total_sectors = ((dev->file->size + GRUB_DISK_SECTOR_SIZE - 1) @@ -178,6 +178,15 @@ grub_loopback_open (const char *name, grub_disk_t disk) return 0; } +static void +grub_loopback_close (grub_disk_t disk) +{ + struct grub_loopback *dev = disk->data; + + if (grub_sub (dev->refcnt, 1, &dev->refcnt)) + grub_fatal ("Reference count underflow"); +} + static grub_err_t grub_loopback_read (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t size, char *buf) @@ -220,6 +229,7 @@ static struct grub_disk_dev grub_loopback_dev = .id = GRUB_DISK_DEVICE_LOOPBACK_ID, .disk_iterate = grub_loopback_iterate, .disk_open = grub_loopback_open, + .disk_close = grub_loopback_close, .disk_read = grub_loopback_read, .disk_write = grub_loopback_write, .next = 0 diff --git a/grub-core/disk/luks2.c b/grub-core/disk/luks2.c index d5106402fb..8036d76ff1 100644 --- a/grub-core/disk/luks2.c +++ b/grub-core/disk/luks2.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -569,6 +570,7 @@ luks2_recover_key (grub_disk_t source, gcry_err_code_t gcry_ret; grub_json_t *json = NULL, keyslots; grub_err_t ret; + grub_size_t sz; if (cargs->key_data == NULL || cargs->key_len == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, "no key data"); @@ -577,7 +579,10 @@ luks2_recover_key (grub_disk_t source, if (ret) return ret; - json_header = grub_zalloc (grub_be_to_cpu64 (header.hdr_size) - sizeof (header)); + if (grub_sub (grub_be_to_cpu64 (header.hdr_size), sizeof (header), &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "underflow detected while calculating json header size"); + + json_header = grub_zalloc (sz); if (!json_header) return GRUB_ERR_OUT_OF_MEMORY; diff --git a/grub-core/disk/lvm.c b/grub-core/disk/lvm.c index 794248540c..b2dff76d17 100644 --- a/grub-core/disk/lvm.c +++ b/grub-core/disk/lvm.c @@ -370,6 +370,8 @@ grub_lvm_detect (grub_disk_t disk, break; pv = grub_zalloc (sizeof (*pv)); + if (pv == NULL) + goto fail4; q = p; while (*q != ' ' && q < mda_end) q++; @@ -379,6 +381,8 @@ grub_lvm_detect (grub_disk_t disk, s = q - p; pv->name = grub_malloc (s + 1); + if (pv->name == NULL) + goto pvs_fail_noname; grub_memcpy (pv->name, p, s); pv->name[s] = '\0'; @@ -451,6 +455,8 @@ grub_lvm_detect (grub_disk_t disk, break; lv = grub_zalloc (sizeof (*lv)); + if (lv == NULL) + goto fail4; q = p; while (*q != ' ' && q < mda_end) @@ -545,6 +551,8 @@ grub_lvm_detect (grub_disk_t disk, goto lvs_fail; } lv->segments = grub_calloc (lv->segment_count, sizeof (*seg)); + if (lv->segments == NULL) + goto lvs_fail; seg = lv->segments; for (i = 0; i < lv->segment_count; i++) @@ -612,6 +620,8 @@ grub_lvm_detect (grub_disk_t disk, seg->nodes = grub_calloc (seg->node_count, sizeof (*stripe)); + if (seg->nodes == NULL) + goto lvs_segment_fail; stripe = seg->nodes; p = grub_strstr (p, "stripes = ["); @@ -671,8 +681,9 @@ grub_lvm_detect (grub_disk_t disk, goto lvs_segment_fail; } - seg->nodes = grub_zalloc (sizeof (seg->nodes[0]) - * seg->node_count); + seg->nodes = grub_calloc (seg->node_count, sizeof (seg->nodes[0])); + if (seg->nodes == NULL) + goto lvs_segment_fail; p = grub_strstr (p, "mirrors = ["); if (p == NULL) @@ -760,8 +771,9 @@ grub_lvm_detect (grub_disk_t disk, } } - seg->nodes = grub_zalloc (sizeof (seg->nodes[0]) - * seg->node_count); + seg->nodes = grub_calloc (seg->node_count, sizeof (seg->nodes[0])); + if (seg->nodes == NULL) + goto lvs_segment_fail; p = grub_strstr (p, "raids = ["); if (p == NULL) diff --git a/grub-core/disk/mdraid1x_linux.c b/grub-core/disk/mdraid1x_linux.c index 72e5cb6f48..3b5b6c4234 100644 --- a/grub-core/disk/mdraid1x_linux.c +++ b/grub-core/disk/mdraid1x_linux.c @@ -130,6 +130,12 @@ grub_mdraid_detect (grub_disk_t disk, struct grub_diskfilter_vg *array; char *uuid; +#ifdef __powerpc__ + /* Firmware will yell at us for reading too far. */ + if (minor_version == 0) + continue; +#endif + if (size == GRUB_DISK_SIZE_UNKNOWN && minor_version == 0) continue; diff --git a/grub-core/disk/mdraid_linux.c b/grub-core/disk/mdraid_linux.c index e40216f511..98fcfb1be6 100644 --- a/grub-core/disk/mdraid_linux.c +++ b/grub-core/disk/mdraid_linux.c @@ -189,6 +189,11 @@ grub_mdraid_detect (grub_disk_t disk, grub_uint32_t level; struct grub_diskfilter_vg *ret; +#ifdef __powerpc__ + /* Firmware will yell at us for reading too far. */ + return NULL; +#endif + /* The sector where the mdraid 0.90 superblock is stored, if available. */ size = grub_disk_native_sectors (disk); if (size == GRUB_DISK_SIZE_UNKNOWN) diff --git a/grub-core/disk/memdisk.c b/grub-core/disk/memdisk.c index 613779cf32..2d7afaea3f 100644 --- a/grub-core/disk/memdisk.c +++ b/grub-core/disk/memdisk.c @@ -23,6 +23,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -96,8 +97,14 @@ GRUB_MOD_INIT(memdisk) grub_dprintf ("memdisk", "Found memdisk image at %p\n", memdisk_orig_addr); - memdisk_size = header->size - sizeof (struct grub_module_header); + if (grub_sub (header->size, sizeof (struct grub_module_header), &memdisk_size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "underflow detected while obtaining memdisk size"); + return; + } memdisk_addr = grub_malloc (memdisk_size); + if (memdisk_addr == NULL) + return; grub_dprintf ("memdisk", "Copying memdisk image to dynamic memory\n"); grub_memmove (memdisk_addr, memdisk_orig_addr, memdisk_size); diff --git a/grub-core/disk/plainmount.c b/grub-core/disk/plainmount.c index 47e64805fb..21ec4072c6 100644 --- a/grub-core/disk/plainmount.c +++ b/grub-core/disk/plainmount.c @@ -24,6 +24,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -126,7 +127,7 @@ plainmount_configure_password (grub_cryptodisk_t dev, const char *hash, grub_uint8_t *derived_hash, *dh; char *p; unsigned int round, i, len, size; - grub_size_t alloc_size; + grub_size_t alloc_size, sz; grub_err_t err = GRUB_ERR_NONE; /* Support none (plain) hash */ @@ -145,7 +146,11 @@ plainmount_configure_password (grub_cryptodisk_t dev, const char *hash, * Allocate buffer for the password and for an added prefix character * for each hash round ('alloc_size' may not be a multiple of 'len'). */ - p = grub_zalloc (alloc_size + (alloc_size / len) + 1); + if (grub_add (alloc_size, (alloc_size / len), &sz) || + grub_add (sz, 1, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while allocating size of password buffer")); + + p = grub_zalloc (sz); derived_hash = grub_zalloc (GRUB_CRYPTODISK_MAX_KEYLEN * 2); if (p == NULL || derived_hash == NULL) { diff --git a/grub-core/fs/affs.c b/grub-core/fs/affs.c index ed606b3f15..520a001c75 100644 --- a/grub-core/fs/affs.c +++ b/grub-core/fs/affs.c @@ -26,6 +26,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -703,11 +704,16 @@ static struct grub_fs grub_affs_fs = GRUB_MOD_INIT(affs) { - grub_fs_register (&grub_affs_fs); + if (!grub_is_lockdown ()) + { + grub_affs_fs.mod = mod; + grub_fs_register (&grub_affs_fs); + } my_mod = mod; } GRUB_MOD_FINI(affs) { - grub_fs_unregister (&grub_affs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_affs_fs); } diff --git a/grub-core/fs/archelp.c b/grub-core/fs/archelp.c index c1dcc62858..0816b28dee 100644 --- a/grub-core/fs/archelp.c +++ b/grub-core/fs/archelp.c @@ -21,6 +21,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -68,6 +69,7 @@ handle_symlink (struct grub_archelp_data *data, char *rest; char *linktarget; grub_size_t linktarget_len; + grub_size_t sz; *restart = 0; @@ -98,7 +100,12 @@ handle_symlink (struct grub_archelp_data *data, if (linktarget[0] == '\0') return GRUB_ERR_NONE; linktarget_len = grub_strlen (linktarget); - target = grub_malloc (linktarget_len + grub_strlen (*name) + 2); + + if (grub_add (linktarget_len, grub_strlen (*name), &sz) || + grub_add (sz, 2, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("link target length overflow")); + + target = grub_malloc (sz); if (!target) return grub_errno; diff --git a/grub-core/fs/bfs.c b/grub-core/fs/bfs.c index 07cb3e3acf..c92fd79168 100644 --- a/grub-core/fs/bfs.c +++ b/grub-core/fs/bfs.c @@ -30,6 +30,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -1106,7 +1107,11 @@ GRUB_MOD_INIT (bfs) { COMPILE_TIME_ASSERT (1 << LOG_EXTENT_SIZE == sizeof (struct grub_bfs_extent)); - grub_fs_register (&grub_bfs_fs); + if (!grub_is_lockdown ()) + { + grub_bfs_fs.mod = mod; + grub_fs_register (&grub_bfs_fs); + } } #ifdef MODE_AFS @@ -1115,5 +1120,6 @@ GRUB_MOD_FINI (afs) GRUB_MOD_FINI (bfs) #endif { - grub_fs_unregister (&grub_bfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_bfs_fs); } diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c index ba0c583526..2222c57abd 100644 --- a/grub-core/fs/btrfs.c +++ b/grub-core/fs/btrfs.c @@ -38,6 +38,10 @@ #include #include #include +#include +#include +#include +#include #include #include #include @@ -79,9 +83,11 @@ struct grub_btrfs_superblock grub_uint64_t generation; grub_uint64_t root_tree; grub_uint64_t chunk_tree; - grub_uint8_t dummy2[0x20]; + grub_uint8_t dummy2[0x18]; + grub_uint64_t bytes_used; grub_uint64_t root_dir_objectid; - grub_uint8_t dummy3[0x41]; + grub_uint64_t num_devices; + grub_uint8_t dummy3[0x39]; struct grub_btrfs_device this_device; char label[0x100]; grub_uint8_t dummy4[0x100]; @@ -121,6 +127,7 @@ struct grub_btrfs_data grub_uint64_t exttree; grub_size_t extsize; struct grub_btrfs_extent_data *extent; + grub_uint64_t fs_tree; }; struct grub_btrfs_chunk_item @@ -191,6 +198,14 @@ struct grub_btrfs_leaf_descriptor } *data; }; +struct grub_btrfs_root_ref +{ + grub_uint64_t dirid; + grub_uint64_t sequence; + grub_uint16_t name_len; + const char name[0]; +} __attribute__ ((packed)); + struct grub_btrfs_time { grub_int64_t sec; @@ -203,7 +218,7 @@ struct grub_btrfs_inode grub_uint64_t size; grub_uint8_t dummy2[0x70]; struct grub_btrfs_time mtime; -} GRUB_PACKED; +} GRUB_PACKED __attribute__ ((aligned(8))); struct grub_btrfs_extent_data { @@ -236,6 +251,14 @@ struct grub_btrfs_extent_data #define GRUB_BTRFS_OBJECT_ID_CHUNK 0x100 +#define GRUB_BTRFS_ROOT_TREE_OBJECTID 1ULL +#define GRUB_BTRFS_FS_TREE_OBJECTID 5ULL +#define GRUB_BTRFS_ROOT_REF_KEY 156 +#define GRUB_BTRFS_ROOT_ITEM_KEY 132 + +static grub_uint64_t btrfs_default_subvolid = 0; +static char *btrfs_default_subvol = NULL; + static grub_disk_addr_t superblock_sectors[] = { 64 * 2, 64 * 1024 * 2, 256 * 1048576 * 2, 1048576ULL * 1048576ULL * 2 }; @@ -244,6 +267,12 @@ static grub_err_t grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, void *buf, grub_size_t size, int recursion_depth); +static grub_err_t +get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key, + grub_uint64_t *tree, grub_uint8_t *type); + +grub_uint64_t +find_mtab_subvol_tree (const char *path, char **path_in_subvol); static grub_err_t read_sblock (grub_disk_t disk, struct grub_btrfs_superblock *sb) @@ -1252,11 +1281,115 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr, return GRUB_ERR_NONE; } +static grub_err_t +get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, + grub_uint64_t objectid, grub_uint64_t offset, + grub_uint64_t *fs_root); + +static grub_err_t +lookup_root_by_id(struct grub_btrfs_data *data, grub_uint64_t id) +{ + grub_err_t err; + grub_uint64_t tree; + + err = get_fs_root(data, data->sblock.root_tree, id, -1, &tree); + if (!err) + data->fs_tree = tree; + return err; +} + +static grub_err_t +find_path (struct grub_btrfs_data *data, + const char *path, struct grub_btrfs_key *key, + grub_uint64_t *tree, grub_uint8_t *type); + +static grub_err_t +lookup_root_by_name(struct grub_btrfs_data *data, const char *path) +{ + grub_err_t err; + grub_uint64_t tree = 0; + grub_uint8_t type; + grub_uint64_t saved_tree; + struct grub_btrfs_key key; + + if (path[0] == '\0') + { + data->fs_tree = 0; + return GRUB_ERR_NONE; + } + + err = get_root (data, &key, &tree, &type); + if (err) + return err; + + saved_tree = data->fs_tree; + data->fs_tree = tree; + + err = find_path (data, path, &key, &tree, &type); + + data->fs_tree = saved_tree; + + if (err) + return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path); + + if (key.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0) + return grub_error(GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", path); + + data->fs_tree = tree; + return GRUB_ERR_NONE; +} + +static grub_err_t +lookup_root_by_name_fallback(struct grub_btrfs_data *data, const char *path) +{ + grub_err_t err; + grub_uint64_t tree = 0; + grub_uint8_t type; + struct grub_btrfs_key key; + + err = find_path (data, path, &key, &tree, &type); + if (err) + return grub_error(GRUB_ERR_FILE_NOT_FOUND, "couldn't locate %s\n", path); + + if (key.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0) + return grub_error(GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", path); + + data->fs_tree = tree; + return GRUB_ERR_NONE; +} + +static grub_err_t +btrfs_handle_subvol(struct grub_btrfs_data *data __attribute__ ((unused))) +{ + if (btrfs_default_subvol) + { + grub_err_t err; + err = lookup_root_by_name(data, btrfs_default_subvol); + + /* Fallback to old schemes */ + if (err == GRUB_ERR_FILE_NOT_FOUND) + { + err = GRUB_ERR_NONE; + return lookup_root_by_name_fallback(data, btrfs_default_subvol); + } + return err; + } + + if (btrfs_default_subvolid) + return lookup_root_by_id(data, btrfs_default_subvolid); + + data->fs_tree = 0; + + return GRUB_ERR_NONE; +} + + static struct grub_btrfs_data * grub_btrfs_mount (grub_device_t dev) { struct grub_btrfs_data *data; grub_err_t err; + const char *relpath = grub_env_get ("btrfs_relative_path"); if (!dev->disk) { @@ -1276,8 +1409,8 @@ grub_btrfs_mount (grub_device_t dev) } data->n_devices_allocated = 16; - data->devices_attached = grub_malloc (sizeof (data->devices_attached[0]) - * data->n_devices_allocated); + data->devices_attached = grub_calloc (data->n_devices_allocated, + sizeof (data->devices_attached[0])); if (!data->devices_attached) { grub_free (data); @@ -1287,6 +1420,16 @@ grub_btrfs_mount (grub_device_t dev) data->devices_attached[0].dev = dev; data->devices_attached[0].id = data->sblock.this_device.device_id; + if (relpath && (relpath[0] == '1' || relpath[0] == 'y')) + { + err = btrfs_handle_subvol (data); + if (err) + { + grub_free (data); + return NULL; + } + } + return data; } @@ -1784,6 +1927,91 @@ get_root (struct grub_btrfs_data *data, struct grub_btrfs_key *key, return GRUB_ERR_NONE; } +static grub_err_t +find_pathname(struct grub_btrfs_data *data, grub_uint64_t objectid, + grub_uint64_t fs_root, const char *name, char **pathname) +{ + grub_err_t err; + struct grub_btrfs_key key = { + .object_id = objectid, + .type = GRUB_BTRFS_ITEM_TYPE_INODE_REF, + .offset = 0, + }; + struct grub_btrfs_key key_out; + struct grub_btrfs_leaf_descriptor desc; + char *p = grub_strdup (name); + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + grub_size_t alloc = grub_strlen(name) + 1; + + err = lower_bound(data, &key, &key_out, fs_root, + &elemaddr, &elemsize, &desc, 0); + if (err) + return grub_error(err, "lower_bound caught %d\n", err); + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) + next(data, &desc, &elemaddr, &elemsize, &key_out); + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) + { + return grub_error(GRUB_ERR_FILE_NOT_FOUND, + "Can't find inode ref for {%"PRIuGRUB_UINT64_T + ", %u, %"PRIuGRUB_UINT64_T"} %"PRIuGRUB_UINT64_T + "/%"PRIuGRUB_SIZE"\n", + key_out.object_id, key_out.type, + key_out.offset, elemaddr, elemsize); + } + + + while (key_out.type == GRUB_BTRFS_ITEM_TYPE_INODE_REF && + key_out.object_id != key_out.offset) { + struct grub_btrfs_inode_ref *inode_ref; + char *new; + + inode_ref = grub_malloc(elemsize + 1); + if (!inode_ref) + return grub_error(GRUB_ERR_OUT_OF_MEMORY, + "couldn't allocate memory for inode_ref (%"PRIuGRUB_SIZE")\n", elemsize); + + err = grub_btrfs_read_logical(data, elemaddr, inode_ref, elemsize, 0); + if (err) + return grub_error(err, "read_logical caught %d\n", err); + + alloc += grub_le_to_cpu16 (inode_ref->n) + 2; + new = grub_malloc(alloc); + if (!new) + return grub_error(GRUB_ERR_OUT_OF_MEMORY, + "couldn't allocate memory for name (%"PRIuGRUB_SIZE")\n", alloc); + + grub_memcpy(new, inode_ref->name, grub_le_to_cpu16 (inode_ref->n)); + if (p) + { + new[grub_le_to_cpu16 (inode_ref->n)] = '/'; + grub_strcpy (new + grub_le_to_cpu16 (inode_ref->n) + 1, p); + grub_free(p); + } + else + new[grub_le_to_cpu16 (inode_ref->n)] = 0; + grub_free(inode_ref); + + p = new; + + key.object_id = key_out.offset; + + err = lower_bound(data, &key, &key_out, fs_root, &elemaddr, + &elemsize, &desc, 0); + if (err) + return grub_error(err, "lower_bound caught %d\n", err); + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_REF) + next(data, &desc, &elemaddr, &elemsize, &key_out); + + } + + *pathname = p; + return 0; +} + static grub_err_t find_path (struct grub_btrfs_data *data, const char *path, struct grub_btrfs_key *key, @@ -1796,31 +2024,67 @@ find_path (struct grub_btrfs_data *data, grub_size_t allocated = 0; struct grub_btrfs_dir_item *direl = NULL; struct grub_btrfs_key key_out; + int follow_default; const char *ctoken; grub_size_t ctokenlen; char *path_alloc = NULL; char *origpath = NULL; unsigned symlinks_max = 32; + const char *relpath = grub_env_get ("btrfs_relative_path"); + grub_size_t sz; - err = get_root (data, key, tree, type); - if (err) - return err; - + follow_default = 0; origpath = grub_strdup (path); if (!origpath) return grub_errno; + if (relpath && (relpath[0] == '1' || relpath[0] == 'y')) + { + if (data->fs_tree) + { + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->fs_tree; + /* This is a tree root, so everything starts at objectid 256 */ + key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + } + else + { + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->sblock.root_tree; + key->object_id = data->sblock.root_dir_objectid; + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + follow_default = 1; + } + } + else + { + err = get_root (data, key, tree, type); + if (err) + return err; + } + while (1) { - while (path[0] == '/') - path++; - if (!path[0]) - break; - slash = grub_strchr (path, '/'); - if (!slash) - slash = path + grub_strlen (path); - ctoken = path; - ctokenlen = slash - path; + if (!follow_default) + { + while (path[0] == '/') + path++; + if (!path[0]) + break; + slash = grub_strchr (path, '/'); + if (!slash) + slash = path + grub_strlen (path); + ctoken = path; + ctokenlen = slash - path; + } + else + { + ctoken = "default"; + ctokenlen = sizeof ("default") - 1; + } if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) { @@ -1831,7 +2095,9 @@ find_path (struct grub_btrfs_data *data, if (ctokenlen == 1 && ctoken[0] == '.') { - path = slash; + if (!follow_default) + path = slash; + follow_default = 0; continue; } if (ctokenlen == 2 && ctoken[0] == '.' && ctoken[1] == '.') @@ -1862,8 +2128,9 @@ find_path (struct grub_btrfs_data *data, *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; key->object_id = key_out.offset; - path = slash; - + if (!follow_default) + path = slash; + follow_default = 0; continue; } @@ -1891,9 +2158,15 @@ find_path (struct grub_btrfs_data *data, struct grub_btrfs_dir_item *cdirel; if (elemsize > allocated) { - allocated = 2 * elemsize; + if (grub_mul (2, elemsize, &allocated) || + grub_add (allocated, 1, &sz)) + { + grub_free (path_alloc); + grub_free (origpath); + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("directory item size overflow")); + } grub_free (direl); - direl = grub_malloc (allocated + 1); + direl = grub_malloc (sz); if (!direl) { grub_free (path_alloc); @@ -1932,7 +2205,9 @@ find_path (struct grub_btrfs_data *data, return err; } - path = slash; + if (!follow_default) + path = slash; + follow_default = 0; if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK) { struct grub_btrfs_inode inode; @@ -1955,8 +2230,16 @@ find_path (struct grub_btrfs_data *data, grub_free (origpath); return err; } - tmp = grub_malloc (grub_le_to_cpu64 (inode.size) - + grub_strlen (path) + 1); + + if (grub_add (grub_le_to_cpu64 (inode.size), grub_strlen (path), &sz) || + grub_add (sz, 1, &sz)) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("buffer size overflow")); + } + tmp = grub_malloc (sz); if (!tmp) { grub_free (direl); @@ -1982,13 +2265,37 @@ find_path (struct grub_btrfs_data *data, path = path_alloc = tmp; if (path[0] == '/') { - err = get_root (data, key, tree, type); - if (err) + if (relpath && (relpath[0] == '1' || relpath[0] == 'y')) { - grub_free (direl); - grub_free (path_alloc); - grub_free (origpath); - return err; + if (data->fs_tree) + { + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->fs_tree; + /* This is a tree root, so everything starts at objectid 256 */ + key->object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK); + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + } + else + { + *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY; + *tree = data->sblock.root_tree; + key->object_id = data->sblock.root_dir_objectid; + key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key->offset = 0; + follow_default = 1; + } + } + else + { + err = get_root (data, key, tree, type); + if (err) + { + grub_free (direl); + grub_free (path_alloc); + grub_free (origpath); + return err; + } } } continue; @@ -2078,11 +2385,21 @@ grub_btrfs_dir (grub_device_t device, const char *path, grub_uint64_t tree; grub_uint8_t type; grub_size_t est_size = 0; + char *new_path = NULL; + grub_size_t sz; if (!data) return grub_errno; - err = find_path (data, path, &key_in, &tree, &type); + tree = find_mtab_subvol_tree (path, &new_path); + + if (tree) + data->fs_tree = tree; + + err = find_path (data, new_path ? new_path : path, &key_in, &tree, &type); + if (new_path) + grub_free (new_path); + if (err) { grub_btrfs_unmount (data); @@ -2119,9 +2436,15 @@ grub_btrfs_dir (grub_device_t device, const char *path, } if (elemsize > allocated) { - allocated = 2 * elemsize; + if (grub_mul (2, elemsize, &allocated) || + grub_add (allocated, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("directory element size overflow")); + r = -grub_errno; + break; + } grub_free (direl); - direl = grub_malloc (allocated + 1); + direl = grub_malloc (sz); if (!direl) { r = -grub_errno; @@ -2209,11 +2532,21 @@ grub_btrfs_open (struct grub_file *file, const char *name) struct grub_btrfs_inode inode; grub_uint8_t type; struct grub_btrfs_key key_in; + grub_uint64_t tree; + char *new_path = NULL; if (!data) return grub_errno; - err = find_path (data, name, &key_in, &data->tree, &type); + tree = find_mtab_subvol_tree (name, &new_path); + + if (tree) + data->fs_tree = tree; + + err = find_path (data, new_path ? new_path : name, &key_in, &data->tree, &type); + if (new_path) + grub_free (new_path); + if (err) { grub_btrfs_unmount (data); @@ -2256,6 +2589,20 @@ grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len) data->tree, file->offset, buf, len); } +static char * +btrfs_unparse_uuid(struct grub_btrfs_data *data) +{ + return grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", + grub_be_to_cpu16 (data->sblock.uuid[0]), + grub_be_to_cpu16 (data->sblock.uuid[1]), + grub_be_to_cpu16 (data->sblock.uuid[2]), + grub_be_to_cpu16 (data->sblock.uuid[3]), + grub_be_to_cpu16 (data->sblock.uuid[4]), + grub_be_to_cpu16 (data->sblock.uuid[5]), + grub_be_to_cpu16 (data->sblock.uuid[6]), + grub_be_to_cpu16 (data->sblock.uuid[7])); +} + static grub_err_t grub_btrfs_uuid (grub_device_t device, char **uuid) { @@ -2267,15 +2614,7 @@ grub_btrfs_uuid (grub_device_t device, char **uuid) if (!data) return grub_errno; - *uuid = grub_xasprintf ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x", - grub_be_to_cpu16 (data->sblock.uuid[0]), - grub_be_to_cpu16 (data->sblock.uuid[1]), - grub_be_to_cpu16 (data->sblock.uuid[2]), - grub_be_to_cpu16 (data->sblock.uuid[3]), - grub_be_to_cpu16 (data->sblock.uuid[4]), - grub_be_to_cpu16 (data->sblock.uuid[5]), - grub_be_to_cpu16 (data->sblock.uuid[6]), - grub_be_to_cpu16 (data->sblock.uuid[7])); + *uuid = btrfs_unparse_uuid(data); grub_btrfs_unmount (data); @@ -2308,14 +2647,20 @@ struct embed_region { }; /* - * https://btrfs.wiki.kernel.org/index.php/Manpage/btrfs(5)#BOOTLOADER_SUPPORT + * https://btrfs.readthedocs.io/en/latest/btrfs-man5.html#man-btrfs5-bootloader-support + * or invoke "man 5 btrfs" and visit the "bootloader support" subsection. * The first 1 MiB on each device is unused with the exception of primary * superblock that is on the offset 64 KiB and spans 4 KiB. + * + * Note: If this table is modified, also update + * util/grub-editenv.c::fs_envblk_spec, which describes the file-system + * specific layout of reserved raw blocks used as environment blocks so that + * both stay consistent. */ static const struct { struct embed_region available; - struct embed_region used[6]; + struct embed_region used[9]; } btrfs_head = { .available = {0, GRUB_DISK_KiB_TO_SECTORS (1024)}, /* The first 1 MiB. */ .used = { @@ -2323,6 +2668,9 @@ static const struct { {GRUB_DISK_KiB_TO_SECTORS (64) - 1, 1}, /* Overflow guard. */ {GRUB_DISK_KiB_TO_SECTORS (64), GRUB_DISK_KiB_TO_SECTORS (4)}, /* 4 KiB superblock. */ {GRUB_DISK_KiB_TO_SECTORS (68), 1}, /* Overflow guard. */ + {(GRUB_ENV_BTRFS_OFFSET >> GRUB_DISK_SECTOR_BITS) - 1, 1}, /* Overflow guard. */ + {(GRUB_ENV_BTRFS_OFFSET >> GRUB_DISK_SECTOR_BITS), 1}, /* Environment Block. */ + {(GRUB_ENV_BTRFS_OFFSET >> GRUB_DISK_SECTOR_BITS) + 1, 1}, /* Overflow guard. */ {GRUB_DISK_KiB_TO_SECTORS (1024) - 1, 1}, /* Overflow guard. */ {0, 0} /* Array terminator. */ } @@ -2396,6 +2744,618 @@ grub_btrfs_embed (grub_device_t device __attribute__ ((unused)), } #endif +static grub_err_t +grub_cmd_btrfs_info (grub_command_t cmd __attribute__ ((unused)), int argc, + char **argv) +{ + grub_device_t dev; + char *devname; + struct grub_btrfs_data *data; + char *uuid; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + devname = grub_file_get_device_name(argv[0]); + + if (!devname) + return grub_errno; + + dev = grub_device_open (devname); + grub_free (devname); + if (!dev) + return grub_errno; + + data = grub_btrfs_mount (dev); + if (!data) + { + grub_device_close(dev); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "failed to open fs"); + } + + if (data->sblock.label[0]) + grub_printf("Label: '%s' ", data->sblock.label); + else + grub_printf("Label: none "); + + uuid = btrfs_unparse_uuid(data); + + grub_printf(" uuid: %s\n\tTotal devices %" PRIuGRUB_UINT64_T + " FS bytes used %" PRIuGRUB_UINT64_T "\n", + uuid, grub_cpu_to_le64(data->sblock.num_devices), + grub_cpu_to_le64(data->sblock.bytes_used)); + + grub_btrfs_unmount (data); + + return 0; +} + +struct grub_btrfs_mtab +{ + struct grub_btrfs_mtab *next; + struct grub_btrfs_mtab **prev; + char *path; + char *subvol; + grub_uint64_t tree; +}; + +typedef struct grub_btrfs_mtab* grub_btrfs_mtab_t; + +static struct grub_btrfs_mtab *btrfs_mtab; + +#define FOR_GRUB_MTAB(var) FOR_LIST_ELEMENTS (var, btrfs_mtab) +#define FOR_GRUB_MTAB_SAFE(var, next) FOR_LIST_ELEMENTS_SAFE((var), (next), btrfs_mtab) + +static void +add_mountpoint (const char *path, const char *subvol, grub_uint64_t tree) +{ + grub_btrfs_mtab_t m = grub_malloc (sizeof (*m)); + + m->path = grub_strdup (path); + m->subvol = grub_strdup (subvol); + m->tree = tree; + grub_list_push (GRUB_AS_LIST_P (&btrfs_mtab), GRUB_AS_LIST (m)); +} + +static grub_err_t +grub_cmd_btrfs_mount_subvol (grub_command_t cmd __attribute__ ((unused)), int argc, + char **argv) +{ + char *devname, *dirname, *subvol; + struct grub_btrfs_key key_in; + grub_uint8_t type; + grub_uint64_t tree; + grub_uint64_t saved_tree; + grub_err_t err; + struct grub_btrfs_data *data = NULL; + grub_device_t dev = NULL; + + if (argc < 3) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "required and "); + + devname = grub_file_get_device_name(argv[0]); + dev = grub_device_open (devname); + grub_free (devname); + + if (!dev) + { + err = grub_errno; + goto err_out; + } + + dirname = argv[1]; + subvol = argv[2]; + + data = grub_btrfs_mount (dev); + if (!data) + { + err = grub_errno; + goto err_out; + } + + err = find_path (data, dirname, &key_in, &tree, &type); + if (err) + goto err_out; + + if (type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory")); + goto err_out; + } + + err = get_root (data, &key_in, &tree, &type); + + if (err) + goto err_out; + + saved_tree = data->fs_tree; + data->fs_tree = tree; + err = find_path (data, subvol, &key_in, &tree, &type); + data->fs_tree = saved_tree; + + if (err) + goto err_out; + + if (key_in.object_id != grub_cpu_to_le64_compile_time (GRUB_BTRFS_OBJECT_ID_CHUNK) || tree == 0) + { + err = grub_error (GRUB_ERR_BAD_FILE_TYPE, "%s: not a subvolume\n", subvol); + goto err_out; + } + + grub_btrfs_unmount (data); + grub_device_close (dev); + add_mountpoint (dirname, subvol, tree); + + return GRUB_ERR_NONE; + +err_out: + + if (data) + grub_btrfs_unmount (data); + + if (dev) + grub_device_close (dev); + + return err; +} + +grub_uint64_t +find_mtab_subvol_tree (const char *path, char **path_in_subvol) +{ + grub_btrfs_mtab_t m, cm; + grub_uint64_t tree; + + if (!path || !path_in_subvol) + return 0; + + *path_in_subvol = NULL; + tree = 0; + cm = NULL; + + FOR_GRUB_MTAB (m) + { + if (grub_strncmp (path, m->path, grub_strlen (m->path)) == 0) + { + if (!cm) + cm = m; + else + if (grub_strcmp (m->path, cm->path) > 0) + cm = m; + } + } + + if (cm) + { + const char *s = path + grub_strlen (cm->path); + *path_in_subvol = (s[0] == '\0') ? grub_strdup ("/") : grub_strdup (s); + tree = cm->tree; + } + + return tree; +} + +static grub_err_t +get_fs_root(struct grub_btrfs_data *data, grub_uint64_t tree, + grub_uint64_t objectid, grub_uint64_t offset, + grub_uint64_t *fs_root) +{ + grub_err_t err; + struct grub_btrfs_key key_in = { + .object_id = objectid, + .type = GRUB_BTRFS_ROOT_ITEM_KEY, + .offset = offset, + }, key_out; + struct grub_btrfs_leaf_descriptor desc; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + struct grub_btrfs_root_item ri; + + err = lower_bound(data, &key_in, &key_out, tree, + &elemaddr, &elemsize, &desc, 0); + + if (err) + return err; + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM || elemaddr == 0) + return grub_error(GRUB_ERR_FILE_NOT_FOUND, + N_("can't find fs root for subvol %"PRIuGRUB_UINT64_T"\n"), + key_in.object_id); + + err = grub_btrfs_read_logical (data, elemaddr, &ri, sizeof (ri), 0); + if (err) + return err; + + *fs_root = ri.tree; + + return GRUB_ERR_NONE; +} + +static const struct grub_arg_option options[] = { + {"output", 'o', 0, N_("Output to a variable instead of the console."), + N_("VARNAME"), ARG_TYPE_STRING}, + {"path-only", 'p', 0, N_("Show only the path of the subvolume."), 0, 0}, + {"id-only", 'i', 0, N_("Show only the id of the subvolume."), 0, 0}, + {0, 0, 0, 0, 0, 0} +}; + +static grub_err_t +grub_cmd_btrfs_list_subvols (struct grub_extcmd_context *ctxt, + int argc, char **argv) +{ + struct grub_btrfs_data *data; + grub_device_t dev; + char *devname; + grub_uint64_t tree; + struct grub_btrfs_key key_in = { + .object_id = grub_cpu_to_le64_compile_time (GRUB_BTRFS_FS_TREE_OBJECTID), + .type = GRUB_BTRFS_ROOT_REF_KEY, + .offset = 0, + }, key_out; + struct grub_btrfs_leaf_descriptor desc; + grub_disk_addr_t elemaddr; + grub_uint64_t fs_root = 0; + grub_size_t elemsize; + grub_size_t allocated = 0; + int r = 0; + grub_err_t err; + char *buf = NULL; + int print = 1; + int path_only = ctxt->state[1].set; + int num_only = ctxt->state[2].set; + char *varname = NULL; + char *output = NULL; + + if (ctxt->state[0].set) { + varname = ctxt->state[0].arg; + print = 0; + } + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + devname = grub_file_get_device_name(argv[0]); + if (!devname) + return grub_errno; + + dev = grub_device_open (devname); + grub_free (devname); + if (!dev) + return grub_errno; + + data = grub_btrfs_mount(dev); + if (!data) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "could not open device"); + + tree = data->sblock.root_tree; + err = get_fs_root(data, tree, grub_cpu_to_le64_compile_time (GRUB_BTRFS_FS_TREE_OBJECTID), + 0, &fs_root); + if (err) + goto out; + + err = lower_bound(data, &key_in, &key_out, tree, + &elemaddr, &elemsize, &desc, 0); + + if (err) + { + grub_btrfs_unmount(data); + return err; + } + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF || elemaddr == 0) + { + r = next(data, &desc, &elemaddr, &elemsize, &key_out); + } + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF) { + err = GRUB_ERR_FILE_NOT_FOUND; + grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("can't find root refs")); + goto out; + } + + do + { + struct grub_btrfs_root_ref *ref; + char *p = NULL; + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_REF) + { + r = 0; + break; + } + + if (elemsize > allocated) + { + grub_free(buf); + allocated = 2 * elemsize; + buf = grub_malloc(allocated + 1); + if (!buf) + { + r = -grub_errno; + break; + } + } + ref = (struct grub_btrfs_root_ref *)buf; + + err = grub_btrfs_read_logical(data, elemaddr, buf, elemsize, 0); + if (err) + { + r = -err; + break; + } + buf[elemsize] = 0; + + find_pathname(data, ref->dirid, fs_root, ref->name, &p); + + if (print) + { + if (num_only) + grub_printf("ID %"PRIuGRUB_UINT64_T"\n", key_out.offset); + else if (path_only) + grub_printf("%s\n", p); + else + grub_printf("ID %"PRIuGRUB_UINT64_T" path %s\n", key_out.offset, p); + } else { + char *old = output; + if (num_only) + output = grub_xasprintf("%s%"PRIuGRUB_UINT64_T"\n", + old ?: "", key_out.offset); + else if (path_only) + output = grub_xasprintf("%s%s\n", old ?: "", p); + else + output = grub_xasprintf("%sID %"PRIuGRUB_UINT64_T" path %s\n", + old ?: "", key_out.offset, p); + + if (old) + grub_free(old); + } + + r = next(data, &desc, &elemaddr, &elemsize, &key_out); + } while(r > 0); + + if (output) + grub_env_set(varname, output); + +out: + free_iterator(&desc); + grub_btrfs_unmount(data); + + grub_device_close (dev); + + return 0; +} + +static grub_err_t +grub_btrfs_get_parent_subvol_path (struct grub_btrfs_data *data, + grub_uint64_t child_id, + const char *child_path, + grub_uint64_t *parent_id, + char **path_out) +{ + grub_uint64_t fs_root = 0; + struct grub_btrfs_key key_in = { + .object_id = child_id, + .type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF, + .offset = 0, + }, key_out; + struct grub_btrfs_root_ref *ref; + char *buf; + struct grub_btrfs_leaf_descriptor desc; + grub_size_t elemsize; + grub_disk_addr_t elemaddr; + grub_err_t err; + char *parent_path; + + *parent_id = 0; + *path_out = 0; + + err = lower_bound(data, &key_in, &key_out, data->sblock.root_tree, + &elemaddr, &elemsize, &desc, 0); + if (err) + return err; + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF || elemaddr == 0) + next(data, &desc, &elemaddr, &elemsize, &key_out); + + if (key_out.type != GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF) + { + free_iterator(&desc); + return grub_error(GRUB_ERR_FILE_NOT_FOUND, N_("can't find root backrefs")); + } + + buf = grub_malloc(elemsize + 1); + if (!buf) + { + free_iterator(&desc); + return grub_errno; + } + + err = grub_btrfs_read_logical(data, elemaddr, buf, elemsize, 0); + if (err) + { + grub_free(buf); + free_iterator(&desc); + return err; + } + + buf[elemsize] = 0; + ref = (struct grub_btrfs_root_ref *)buf; + + err = get_fs_root(data, data->sblock.root_tree, grub_le_to_cpu64 (key_out.offset), + 0, &fs_root); + if (err) + { + grub_free(buf); + free_iterator(&desc); + return err; + } + + find_pathname(data, grub_le_to_cpu64 (ref->dirid), fs_root, ref->name, &parent_path); + + if (child_path) + { + *path_out = grub_xasprintf ("%s/%s", parent_path, child_path); + grub_free (parent_path); + } + else + *path_out = parent_path; + + *parent_id = grub_le_to_cpu64 (key_out.offset); + + grub_free(buf); + free_iterator(&desc); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_btrfs_get_default_subvolume_id (struct grub_btrfs_data *data, grub_uint64_t *id) +{ + grub_err_t err; + grub_disk_addr_t elemaddr; + grub_size_t elemsize; + struct grub_btrfs_key key, key_out; + struct grub_btrfs_dir_item *direl = NULL; + const char *ctoken = "default"; + grub_size_t ctokenlen = sizeof ("default") - 1; + + *id = 0; + key.object_id = data->sblock.root_dir_objectid; + key.type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM; + key.offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen)); + err = lower_bound (data, &key, &key_out, data->sblock.root_tree, &elemaddr, &elemsize, + NULL, 0); + if (err) + return err; + + if (key_cmp (&key, &key_out) != 0) + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found")); + + struct grub_btrfs_dir_item *cdirel; + direl = grub_malloc (elemsize + 1); + err = grub_btrfs_read_logical (data, elemaddr, direl, elemsize, 0); + if (err) + { + grub_free (direl); + return err; + } + for (cdirel = direl; + (grub_uint8_t *) cdirel - (grub_uint8_t *) direl + < (grub_ssize_t) elemsize; + cdirel = (void *) ((grub_uint8_t *) (direl + 1) + + grub_le_to_cpu16 (cdirel->n) + + grub_le_to_cpu16 (cdirel->m))) + { + if (ctokenlen == grub_le_to_cpu16 (cdirel->n) + && grub_memcmp (cdirel->name, ctoken, ctokenlen) == 0) + break; + } + if ((grub_uint8_t *) cdirel - (grub_uint8_t *) direl + >= (grub_ssize_t) elemsize) + { + grub_free (direl); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found")); + return err; + } + + if (cdirel->key.type != GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM) + { + grub_free (direl); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file not found")); + return err; + } + + *id = grub_le_to_cpu64 (cdirel->key.object_id); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_btrfs_get_default_subvol (struct grub_extcmd_context *ctxt, + int argc, char **argv) +{ + char *devname; + grub_device_t dev; + struct grub_btrfs_data *data; + grub_err_t err; + grub_uint64_t id; + char *subvol = NULL; + grub_uint64_t subvolid = 0; + char *varname = NULL; + char *output = NULL; + int path_only = ctxt->state[1].set; + int num_only = ctxt->state[2].set; + + if (ctxt->state[0].set) + varname = ctxt->state[0].arg; + + if (argc < 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "device name required"); + + devname = grub_file_get_device_name(argv[0]); + if (!devname) + return grub_errno; + + dev = grub_device_open (devname); + grub_free (devname); + if (!dev) + return grub_errno; + + data = grub_btrfs_mount(dev); + if (!data) + { + grub_device_close (dev); + grub_dprintf ("btrfs", "failed to open fs\n"); + grub_errno = GRUB_ERR_NONE; + return 0; + } + + err = grub_btrfs_get_default_subvolume_id (data, &subvolid); + if (err) + { + grub_btrfs_unmount (data); + grub_device_close (dev); + return err; + } + + id = subvolid; + while (id != GRUB_BTRFS_ROOT_VOL_OBJECTID) + { + grub_uint64_t parent_id; + char *path_out; + + err = grub_btrfs_get_parent_subvol_path (data, grub_cpu_to_le64 (id), subvol, &parent_id, &path_out); + if (err) + { + grub_btrfs_unmount (data); + grub_device_close (dev); + return err; + } + + if (subvol) + grub_free (subvol); + subvol = path_out; + id = parent_id; + } + + if (num_only && path_only) + output = grub_xasprintf ("%"PRIuGRUB_UINT64_T" /%s", subvolid, subvol); + else if (num_only) + output = grub_xasprintf ("%"PRIuGRUB_UINT64_T, subvolid); + else + output = grub_xasprintf ("/%s", subvol); + + if (varname) + grub_env_set(varname, output); + else + grub_printf ("%s\n", output); + + grub_free (output); + grub_free (subvol); + + grub_btrfs_unmount (data); + grub_device_close (dev); + + return GRUB_ERR_NONE; +} + static struct grub_fs grub_btrfs_fs = { .name = "btrfs", .fs_dir = grub_btrfs_dir, @@ -2411,12 +3371,102 @@ static struct grub_fs grub_btrfs_fs = { #endif }; +static grub_command_t cmd_info; +static grub_command_t cmd_mount_subvol; +static grub_extcmd_t cmd_list_subvols; +static grub_extcmd_t cmd_get_default_subvol; + +static char * +subvolid_set_env (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + unsigned long long result = 0; + + grub_errno = GRUB_ERR_NONE; + if (*val) + { + result = grub_strtoull(val, NULL, 10); + if (grub_errno) + return NULL; + } + + grub_free (btrfs_default_subvol); + btrfs_default_subvol = NULL; + btrfs_default_subvolid = result; + return grub_strdup(val); +} + +static const char * +subvolid_get_env (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + if (btrfs_default_subvol) + return grub_xasprintf("subvol:%s", btrfs_default_subvol); + else if (btrfs_default_subvolid) + return grub_xasprintf("%"PRIuGRUB_UINT64_T, btrfs_default_subvolid); + else + return ""; +} + +static char * +subvol_set_env (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + grub_free (btrfs_default_subvol); + btrfs_default_subvol = grub_strdup (val); + btrfs_default_subvolid = 0; + return grub_strdup(val); +} + +static const char * +subvol_get_env (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + if (btrfs_default_subvol) + return btrfs_default_subvol; + else if (btrfs_default_subvolid) + return grub_xasprintf("subvolid:%" PRIuGRUB_UINT64_T, + btrfs_default_subvolid); + else + return ""; +} + GRUB_MOD_INIT (btrfs) { + grub_btrfs_fs.mod = mod; grub_fs_register (&grub_btrfs_fs); + cmd_info = grub_register_command("btrfs-info", grub_cmd_btrfs_info, + "DEVICE", + "Print BtrFS info about DEVICE."); + cmd_mount_subvol = grub_register_command("btrfs-mount-subvol", grub_cmd_btrfs_mount_subvol, + "DEVICE DIRECTORY SUBVOL", + "Set btrfs DEVICE the DIRECTORY a mountpoint of SUBVOL."); + cmd_list_subvols = grub_register_extcmd("btrfs-list-subvols", + grub_cmd_btrfs_list_subvols, 0, + "[-p|-n] [-o var] DEVICE", + "Print list of BtrFS subvolumes on " + "DEVICE.", options); + cmd_get_default_subvol = grub_register_extcmd("btrfs-get-default-subvol", + grub_cmd_btrfs_get_default_subvol, 0, + "[-p|-n] [-o var] DEVICE", + "Print default BtrFS subvolume on " + "DEVICE.", options); + grub_register_variable_hook ("btrfs_subvol", subvol_get_env, + subvol_set_env); + grub_register_variable_hook ("btrfs_subvolid", subvolid_get_env, + subvolid_set_env); + grub_env_export ("btrfs_subvol"); + grub_env_export ("btrfs_subvolid"); + grub_env_export ("btrfs_relative_path"); } GRUB_MOD_FINI (btrfs) { + grub_register_variable_hook ("btrfs_subvol", NULL, NULL); + grub_register_variable_hook ("btrfs_subvolid", NULL, NULL); + grub_unregister_command (cmd_info); + grub_unregister_extcmd (cmd_list_subvols); grub_fs_unregister (&grub_btrfs_fs); } + +// vim: si et sw=2: diff --git a/grub-core/fs/cbfs.c b/grub-core/fs/cbfs.c index 8ab7106afb..b62c8777cf 100644 --- a/grub-core/fs/cbfs.c +++ b/grub-core/fs/cbfs.c @@ -26,6 +26,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -390,12 +391,17 @@ GRUB_MOD_INIT (cbfs) #if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) && !defined (GRUB_MACHINE_XEN) init_cbfsdisk (); #endif - grub_fs_register (&grub_cbfs_fs); + if (!grub_is_lockdown ()) + { + grub_cbfs_fs.mod = mod; + grub_fs_register (&grub_cbfs_fs); + } } GRUB_MOD_FINI (cbfs) { - grub_fs_unregister (&grub_cbfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_cbfs_fs); #if (defined (__i386__) || defined (__x86_64__)) && !defined (GRUB_UTIL) && !defined (GRUB_MACHINE_EMU) && !defined (GRUB_MACHINE_XEN) fini_cbfsdisk (); #endif diff --git a/grub-core/fs/cpio.c b/grub-core/fs/cpio.c index dab5f98988..1799f7ff5a 100644 --- a/grub-core/fs/cpio.c +++ b/grub-core/fs/cpio.c @@ -52,6 +52,7 @@ read_number (const grub_uint16_t *arr, grub_size_t size) GRUB_MOD_INIT (cpio) { + grub_cpio_fs.mod = mod; grub_fs_register (&grub_cpio_fs); } diff --git a/grub-core/fs/cpio_be.c b/grub-core/fs/cpio_be.c index 8465488921..7bed1b848c 100644 --- a/grub-core/fs/cpio_be.c +++ b/grub-core/fs/cpio_be.c @@ -52,6 +52,7 @@ read_number (const grub_uint16_t *arr, grub_size_t size) GRUB_MOD_INIT (cpio_be) { + grub_cpio_fs.mod = mod; grub_fs_register (&grub_cpio_fs); } diff --git a/grub-core/fs/cpio_common.c b/grub-core/fs/cpio_common.c index 5d41b6fdbe..45ac119a8f 100644 --- a/grub-core/fs/cpio_common.c +++ b/grub-core/fs/cpio_common.c @@ -24,6 +24,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -48,6 +49,7 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, struct head hd; grub_size_t namesize; grub_uint32_t modeval; + grub_size_t sz; data->hofs = data->next_hofs; @@ -60,11 +62,21 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, #endif ) return grub_error (GRUB_ERR_BAD_FS, "invalid cpio archive"); - data->size = read_number (hd.filesize, ARRAY_SIZE (hd.filesize)); + + if (grub_cast (read_number (hd.filesize, ARRAY_SIZE (hd.filesize)), &data->size)) + return grub_error (GRUB_ERR_BAD_FS, N_("data size overflow")); + if (mtime) - *mtime = read_number (hd.mtime, ARRAY_SIZE (hd.mtime)); - modeval = read_number (hd.mode, ARRAY_SIZE (hd.mode)); - namesize = read_number (hd.namesize, ARRAY_SIZE (hd.namesize)); + { + if (grub_cast (read_number (hd.mtime, ARRAY_SIZE (hd.mtime)), mtime)) + return grub_error (GRUB_ERR_BAD_FS, N_("mtime overflow")); + } + + if (grub_cast (read_number (hd.mode, ARRAY_SIZE (hd.mode)), &modeval)) + return grub_error (GRUB_ERR_BAD_FS, N_("mode overflow")); + + if (grub_cast (read_number (hd.namesize, ARRAY_SIZE (hd.namesize)), &namesize)) + return grub_error (GRUB_ERR_BAD_FS, N_("namesize overflow")); /* Don't allow negative numbers. */ if (namesize >= 0x80000000) @@ -76,7 +88,10 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, *mode = modeval; - *name = grub_malloc (namesize + 1); + if (grub_add (namesize, 1, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("file name size overflow")); + + *name = grub_malloc (sz); if (*name == NULL) return grub_errno; @@ -110,10 +125,17 @@ grub_cpio_get_link_target (struct grub_archelp_data *data) { char *ret; grub_err_t err; + grub_size_t sz; if (data->size == 0) return grub_strdup (""); - ret = grub_malloc (data->size + 1); + + if (grub_add (data->size, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("target data size overflow")); + return NULL; + } + ret = grub_malloc (sz); if (!ret) return NULL; diff --git a/grub-core/fs/ext2.c b/grub-core/fs/ext2.c index e1cc5e62aa..a5650c34cf 100644 --- a/grub-core/fs/ext2.c +++ b/grub-core/fs/ext2.c @@ -495,6 +495,11 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) struct grub_ext4_extent *ext; int i; grub_disk_addr_t ret; + grub_uint16_t nent; + /* maximum number of extent entries in the inode's inline extent area */ + const grub_uint16_t max_inline_ext = sizeof (inode->blocks) / sizeof (*ext) - 1; /* Minus 1 extent header. */ + /* maximum number of extent entries in the external extent block */ + const grub_uint16_t max_external_ext = EXT2_BLOCK_SIZE(data) / sizeof (*ext) - 1; /* Minus 1 extent header. */ if (grub_ext4_find_leaf (data, (struct grub_ext4_extent_header *) inode->blocks.dir_blocks, fileblock, &leaf) != GRUB_ERR_NONE) @@ -508,7 +513,23 @@ grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) return 0; ext = (struct grub_ext4_extent *) (leaf + 1); - for (i = 0; i < grub_le_to_cpu16 (leaf->entries); i++) + + nent = grub_le_to_cpu16 (leaf->entries); + + /* + * Determine the effective number of extent entries (nent) to process: + * If the extent header (leaf) is stored inline in the inode’s block + * area (i.e. at inode->blocks.dir_blocks), then only max_inline_ext + * entries can fit. + * Otherwise, if the header was read from an external extent block, use + * the larger limit, max_external_ext, based on the full block size. + */ + if (leaf == (struct grub_ext4_extent_header *) inode->blocks.dir_blocks) + nent = grub_min (nent, max_inline_ext); + else + nent = grub_min (nent, max_external_ext); + + for (i = 0; i < nent; i++) { if (fileblock < grub_le_to_cpu32 (ext[i].block)) break; @@ -1123,6 +1144,7 @@ static struct grub_fs grub_ext2_fs = GRUB_MOD_INIT(ext2) { + grub_ext2_fs.mod = mod; grub_fs_register (&grub_ext2_fs); my_mod = mod; } diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c index 855e24618c..72b4aa1e65 100644 --- a/grub-core/fs/f2fs.c +++ b/grub-core/fs/f2fs.c @@ -28,6 +28,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -872,6 +873,9 @@ grub_f2fs_mount (grub_disk_t disk) return data; fail: + if (grub_errno == GRUB_ERR_NONE) + grub_error (GRUB_ERR_BAD_FS, "not a F2FS filesystem"); + grub_free (data); return NULL; @@ -955,6 +959,7 @@ grub_f2fs_read_symlink (grub_fshelp_node_t node) char *symlink; struct grub_fshelp_node *diro = node; grub_uint64_t filesize; + grub_size_t sz; if (!diro->inode_read) { @@ -965,7 +970,12 @@ grub_f2fs_read_symlink (grub_fshelp_node_t node) filesize = grub_f2fs_file_size(&diro->inode.i); - symlink = grub_malloc (filesize + 1); + if (grub_add (filesize, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("symlink size overflow")); + return 0; + } + symlink = grub_malloc (sz); if (!symlink) return 0; @@ -994,6 +1004,7 @@ grub_f2fs_check_dentries (struct grub_f2fs_dir_iter_ctx *ctx) enum FILE_TYPE ftype; int name_len; int ret; + int sz; if (grub_f2fs_test_bit_le (i, ctx->bitmap) == 0) { @@ -1007,7 +1018,12 @@ grub_f2fs_check_dentries (struct grub_f2fs_dir_iter_ctx *ctx) if (name_len >= F2FS_NAME_LEN) return 0; - filename = grub_malloc (name_len + 1); + if (grub_add (name_len, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("directory entry name length overflow")); + return 0; + } + filename = grub_malloc (sz); if (!filename) return 0; @@ -1350,6 +1366,7 @@ static struct grub_fs grub_f2fs_fs = { GRUB_MOD_INIT (f2fs) { + grub_f2fs_fs.mod = mod; grub_fs_register (&grub_f2fs_fs); my_mod = mod; } diff --git a/grub-core/fs/fat.c b/grub-core/fs/fat.c index c5efed7241..6e62b915dd 100644 --- a/grub-core/fs/fat.c +++ b/grub-core/fs/fat.c @@ -1312,6 +1312,7 @@ GRUB_MOD_INIT(fat) #endif { COMPILE_TIME_ASSERT (sizeof (struct grub_fat_dir_entry) == 32); + grub_fat_fs.mod = mod; grub_fs_register (&grub_fat_fs); my_mod = mod; } diff --git a/grub-core/fs/hfs.c b/grub-core/fs/hfs.c index 91dc0e69c3..ce7581dd5f 100644 --- a/grub-core/fs/hfs.c +++ b/grub-core/fs/hfs.c @@ -379,7 +379,7 @@ grub_hfs_mount (grub_disk_t disk) volume name. */ key.parent_dir = grub_cpu_to_be32_compile_time (1); key.strlen = data->sblock.volname[0]; - grub_strcpy ((char *) key.str, (char *) (data->sblock.volname + 1)); + grub_strlcpy ((char *) key.str, (char *) (data->sblock.volname + 1), sizeof (key.str)); if (grub_hfs_find_node (data, (char *) &key, data->cat_root, 0, (char *) &dir, sizeof (dir)) == 0) @@ -1434,6 +1434,7 @@ static struct grub_fs grub_hfs_fs = GRUB_MOD_INIT(hfs) { + grub_hfs_fs.mod = mod; if (!grub_is_lockdown ()) grub_fs_register (&grub_hfs_fs); my_mod = mod; diff --git a/grub-core/fs/hfsplus.c b/grub-core/fs/hfsplus.c index 295822f694..3f203abccd 100644 --- a/grub-core/fs/hfsplus.c +++ b/grub-core/fs/hfsplus.c @@ -405,7 +405,7 @@ grub_hfsplus_mount (grub_disk_t disk) fail: - if (grub_errno == GRUB_ERR_OUT_OF_RANGE) + if (grub_errno == GRUB_ERR_OUT_OF_RANGE || grub_errno == GRUB_ERR_NONE) grub_error (GRUB_ERR_BAD_FS, "not a HFS+ filesystem"); grub_free (data); @@ -1176,6 +1176,7 @@ static struct grub_fs grub_hfsplus_fs = GRUB_MOD_INIT(hfsplus) { + grub_hfsplus_fs.mod = mod; grub_fs_register (&grub_hfsplus_fs); my_mod = mod; } diff --git a/grub-core/fs/hfspluscomp.c b/grub-core/fs/hfspluscomp.c index 48ae438d85..a80954ee61 100644 --- a/grub-core/fs/hfspluscomp.c +++ b/grub-core/fs/hfspluscomp.c @@ -244,14 +244,19 @@ hfsplus_open_compressed_real (struct grub_hfsplus_file *node) return 0; } node->compress_index_size = grub_le_to_cpu32 (index_size); - node->compress_index = grub_malloc (node->compress_index_size - * sizeof (node->compress_index[0])); + node->compress_index = grub_calloc (node->compress_index_size, + sizeof (node->compress_index[0])); if (!node->compress_index) { node->compressed = 0; grub_free (attr_node); return grub_errno; } + + /* + * The node->compress_index_size * sizeof (node->compress_index[0]) is safe here + * due to relevant checks done in grub_calloc() above. + */ if (grub_hfsplus_read_file (node, 0, 0, 0x104 + sizeof (index_size), node->compress_index_size diff --git a/grub-core/fs/iso9660.c b/grub-core/fs/iso9660.c index 8c348b59a5..c73cb9ce05 100644 --- a/grub-core/fs/iso9660.c +++ b/grub-core/fs/iso9660.c @@ -551,6 +551,9 @@ grub_iso9660_mount (grub_disk_t disk) return data; fail: + if (grub_errno == GRUB_ERR_NONE) + grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem"); + grub_free (data); return 0; } @@ -625,9 +628,19 @@ susp_iterate_dir (struct grub_iso9660_susp_entry *entry, filename type is stored. */ /* FIXME: Fix this slightly improper cast. */ if (entry->data[0] & GRUB_ISO9660_RR_DOT) - ctx->filename = (char *) "."; + { + if (ctx->filename_alloc) + grub_free (ctx->filename); + ctx->filename_alloc = 0; + ctx->filename = (char *) "."; + } else if (entry->data[0] & GRUB_ISO9660_RR_DOTDOT) - ctx->filename = (char *) ".."; + { + if (ctx->filename_alloc) + grub_free (ctx->filename); + ctx->filename_alloc = 0; + ctx->filename = (char *) ".."; + } else if (entry->len >= 5) { grub_size_t off = 0, csize = 1; @@ -1247,6 +1260,7 @@ static struct grub_fs grub_iso9660_fs = GRUB_MOD_INIT(iso9660) { + grub_iso9660_fs.mod = mod; grub_fs_register (&grub_iso9660_fs); my_mod = mod; } diff --git a/grub-core/fs/jfs.c b/grub-core/fs/jfs.c index 6f7c439049..ab175c7f15 100644 --- a/grub-core/fs/jfs.c +++ b/grub-core/fs/jfs.c @@ -26,6 +26,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -41,6 +42,13 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define GRUB_JFS_TREE_LEAF 2 +/* + * Define max entries stored in-line in an inode. + * https://jfs.sourceforge.net/project/pub/jfslayout.pdf + */ +#define GRUB_JFS_INODE_INLINE_ENTRIES 8 +#define GRUB_JFS_DIR_MAX_SLOTS 128 + struct grub_jfs_sblock { /* The magic for JFS. It should contain the string "JFS1". */ @@ -203,9 +211,9 @@ struct grub_jfs_inode grub_uint8_t freecnt; grub_uint8_t freelist; grub_uint32_t idotdot; - grub_uint8_t sorted[8]; + grub_uint8_t sorted[GRUB_JFS_INODE_INLINE_ENTRIES]; } header; - struct grub_jfs_leaf_dirent dirents[8]; + struct grub_jfs_leaf_dirent dirents[GRUB_JFS_INODE_INLINE_ENTRIES]; } GRUB_PACKED dir; /* Fast symlink. */ struct @@ -258,7 +266,21 @@ static grub_dl_t my_mod; static grub_err_t grub_jfs_lookup_symlink (struct grub_jfs_data *data, grub_uint32_t ino); -static grub_int64_t +/* + * An extent's offset, physical and logical, is represented as a 40-bit value. + * This 40-bit value is split into two parts: + * - offset1: the most signficant 8 bits of the offset, + * - offset2: the least significant 32 bits of the offset. + * + * This function calculates and returns the 64-bit offset of an extent. + */ +static grub_uint64_t +get_ext_offset (grub_uint8_t offset1, grub_uint32_t offset2) +{ + return (((grub_uint64_t) offset1 << 32) | grub_le_to_cpu32 (offset2)); +} + +static grub_uint64_t getblk (struct grub_jfs_treehead *treehead, struct grub_jfs_tree_extent *extents, int max_extents, @@ -267,28 +289,33 @@ getblk (struct grub_jfs_treehead *treehead, { int found = -1; int i; + grub_uint64_t ext_offset, ext_blk; + + grub_errno = GRUB_ERR_NONE; for (i = 0; i < grub_le_to_cpu16 (treehead->count) - 2 && i < max_extents; i++) { + ext_offset = get_ext_offset (extents[i].offset1, extents[i].offset2); + ext_blk = get_ext_offset (extents[i].extent.blk1, extents[i].extent.blk2); + if (treehead->flags & GRUB_JFS_TREE_LEAF) { /* Read the leafnode. */ - if (grub_le_to_cpu32 (extents[i].offset2) <= blk + if (ext_offset <= blk && ((grub_le_to_cpu16 (extents[i].extent.length)) + (extents[i].extent.length2 << 16) - + grub_le_to_cpu32 (extents[i].offset2)) > blk) - return (blk - grub_le_to_cpu32 (extents[i].offset2) - + grub_le_to_cpu32 (extents[i].extent.blk2)); + + ext_offset) > blk) + return (blk - ext_offset + ext_blk); } else - if (blk >= grub_le_to_cpu32 (extents[i].offset2)) + if (blk >= ext_offset) found = i; } if (found != -1) { - grub_int64_t ret = -1; + grub_uint64_t ret = 0; struct { struct grub_jfs_treehead treehead; @@ -297,13 +324,12 @@ getblk (struct grub_jfs_treehead *treehead, tree = grub_zalloc (sizeof (*tree)); if (!tree) - return -1; + return 0; if (!grub_disk_read (data->disk, - ((grub_disk_addr_t) grub_le_to_cpu32 (extents[found].extent.blk2)) - << (grub_le_to_cpu16 (data->sblock.log2_blksz) - - GRUB_DISK_SECTOR_BITS), 0, - sizeof (*tree), (char *) tree)) + (grub_disk_addr_t) ext_blk + << (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS), + 0, sizeof (*tree), (char *) tree)) { if (grub_memcmp (&tree->treehead, treehead, sizeof (struct grub_jfs_treehead)) || grub_memcmp (&tree->extents, extents, 254 * sizeof (struct grub_jfs_tree_extent))) @@ -311,19 +337,20 @@ getblk (struct grub_jfs_treehead *treehead, else { grub_error (GRUB_ERR_BAD_FS, "jfs: infinite recursion detected"); - ret = -1; + ret = 0; } } grub_free (tree); return ret; } - return -1; + grub_error (GRUB_ERR_READ_ERROR, "jfs: block %" PRIuGRUB_UINT64_T " not found", blk); + return 0; } /* Get the block number for the block BLK in the node INODE in the mounted filesystem DATA. */ -static grub_int64_t +static grub_uint64_t grub_jfs_blkno (struct grub_jfs_data *data, struct grub_jfs_inode *inode, grub_uint64_t blk) { @@ -354,7 +381,7 @@ grub_jfs_read_inode (struct grub_jfs_data *data, grub_uint32_t ino, sizeof (iag_inodes), &iag_inodes)) return grub_errno; - inoblk = grub_le_to_cpu32 (iag_inodes[inoext].blk2); + inoblk = get_ext_offset (iag_inodes[inoext].blk1, iag_inodes[inoext].blk2); inoblk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS); inoblk += inonum; @@ -453,6 +480,13 @@ grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode) /* Check if the entire tree is contained within the inode. */ if (inode->file.tree.flags & GRUB_JFS_TREE_LEAF) { + if (inode->dir.header.count > GRUB_JFS_INODE_INLINE_ENTRIES) + { + grub_free (diro); + grub_error (GRUB_ERR_BAD_FS, N_("invalid JFS inode")); + return 0; + } + diro->leaf = inode->dir.dirents; diro->next_leaf = (struct grub_jfs_leaf_next_dirent *) de; diro->sorted = inode->dir.header.sorted; @@ -468,7 +502,16 @@ grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode) return 0; } - blk = grub_le_to_cpu32 (de[inode->dir.header.sorted[0]].ex.blk2); + if (inode->dir.header.sorted[0] >= GRUB_JFS_DIR_MAX_SLOTS) + { + grub_error (GRUB_ERR_BAD_FS, N_("invalid directory slot index")); + grub_free (diro->dirpage); + grub_free (diro); + return 0; + } + + blk = get_ext_offset (de[inode->dir.header.sorted[0]].ex.blk1, + de[inode->dir.header.sorted[0]].ex.blk2); blk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS); /* Read in the nodes until we are on the leaf node level. */ @@ -486,7 +529,7 @@ grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode) de = (struct grub_jfs_internal_dirent *) diro->dirpage->dirent; index = diro->dirpage->sorted[diro->dirpage->header.sindex * 32]; - blk = (grub_le_to_cpu32 (de[index].ex.blk2) + blk = (get_ext_offset (de[index].ex.blk1, de[index].ex.blk2) << (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS)); } while (!(diro->dirpage->header.flags & GRUB_JFS_TREE_LEAF)); @@ -963,11 +1006,16 @@ static struct grub_fs grub_jfs_fs = GRUB_MOD_INIT(jfs) { - grub_fs_register (&grub_jfs_fs); + if (!grub_is_lockdown ()) + { + grub_jfs_fs.mod = mod; + grub_fs_register (&grub_jfs_fs); + } my_mod = mod; } GRUB_MOD_FINI(jfs) { - grub_fs_unregister (&grub_jfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_jfs_fs); } diff --git a/grub-core/fs/minix.c b/grub-core/fs/minix.c index 5354951d10..4440fcca83 100644 --- a/grub-core/fs/minix.c +++ b/grub-core/fs/minix.c @@ -25,6 +25,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -734,7 +735,11 @@ GRUB_MOD_INIT(minix) #endif #endif { - grub_fs_register (&grub_minix_fs); + if (!grub_is_lockdown ()) + { + grub_minix_fs.mod = mod; + grub_fs_register (&grub_minix_fs); + } my_mod = mod; } @@ -756,5 +761,6 @@ GRUB_MOD_FINI(minix) #endif #endif { - grub_fs_unregister (&grub_minix_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_minix_fs); } diff --git a/grub-core/fs/newc.c b/grub-core/fs/newc.c index 4fb8b2e3d2..43b7f8b642 100644 --- a/grub-core/fs/newc.c +++ b/grub-core/fs/newc.c @@ -64,6 +64,7 @@ read_number (const char *str, grub_size_t size) GRUB_MOD_INIT (newc) { + grub_cpio_fs.mod = mod; grub_fs_register (&grub_cpio_fs); } diff --git a/grub-core/fs/nilfs2.c b/grub-core/fs/nilfs2.c index fc7374ead4..26e6077ff2 100644 --- a/grub-core/fs/nilfs2.c +++ b/grub-core/fs/nilfs2.c @@ -34,6 +34,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -1231,11 +1232,16 @@ GRUB_MOD_INIT (nilfs2) grub_nilfs2_dat_entry)); COMPILE_TIME_ASSERT (1 << LOG_INODE_SIZE == sizeof (struct grub_nilfs2_inode)); - grub_fs_register (&grub_nilfs2_fs); + if (!grub_is_lockdown ()) + { + grub_nilfs2_fs.mod = mod; + grub_fs_register (&grub_nilfs2_fs); + } my_mod = mod; } GRUB_MOD_FINI (nilfs2) { - grub_fs_unregister (&grub_nilfs2_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_nilfs2_fs); } diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c index de435aa14d..960833a345 100644 --- a/grub-core/fs/ntfs.c +++ b/grub-core/fs/ntfs.c @@ -27,6 +27,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -70,6 +71,170 @@ res_attr_data_len (void *res_attr_ptr) return u32at (res_attr_ptr, 0x10); } +/* + * Check if the attribute is valid and doesn't exceed the allocated region. + * This accounts for resident and non-resident data. + * + * This is based off the documentation from the linux-ntfs project: + * https://flatcap.github.io/linux-ntfs/ntfs/concepts/attribute_header.html + */ +static bool +validate_attribute (grub_uint8_t *attr, void *end) +{ + grub_size_t attr_size = 0; + grub_size_t min_size = 0; + grub_size_t spare = (grub_uint8_t *) end - attr; + /* + * Just used as a temporary variable to try and deal with cases where someone + * tries to overlap fields. + */ + grub_size_t curr = 0; + + /* Need verify we can entirely read the attributes header. */ + if (attr + GRUB_NTFS_ATTRIBUTE_HEADER_SIZE >= (grub_uint8_t *) end) + goto fail; + + /* + * So, the rest of this code uses a 16bit int for the attribute length but + * from reading the all the documentation I could find it says this field is + * actually 32bit. But let's be consistent with the rest of the code. + * + * https://elixir.bootlin.com/linux/v6.10.7/source/fs/ntfs3/ntfs.h#L370 + */ + attr_size = u16at (attr, GRUB_NTFS_ATTRIBUTE_LENGTH); + + if (attr_size > spare) + goto fail; + + /* Not an error case, just reached the end of the attributes. */ + if (attr_size == 0) + return false; + + /* + * Extra validation by trying to calculate a minimum possible size for this + * attribute. +8 from the size of the resident data struct which is the + * minimum that can be added. + */ + min_size = GRUB_NTFS_ATTRIBUTE_HEADER_SIZE + 8; + + if (min_size > attr_size) + goto fail; + + /* Is the data is resident (0) or not (1). */ + if (attr[GRUB_NTFS_ATTRIBUTE_RESIDENT] == 0) + { + /* Read the offset and size of the attribute. */ + curr = u16at (attr, GRUB_NTFS_ATTRIBUTE_RES_OFFSET); + curr += u32at (attr, GRUB_NTFS_ATTRIBUTE_RES_LENGTH); + if (curr > min_size) + min_size = curr; + } + else + { + /* + * If the data is non-resident, the minimum size is 64 which is where + * the data runs start. We already have a minimum size of 24. So, just + * adding 40 to get to the real value. + */ + min_size += 40; + if (min_size > attr_size) + goto fail; + /* If the compression unit size is > 0, +8 bytes*/ + if (u16at (attr, GRUB_NTFS_ATTRIBUTE_COMPRESSION_UNIT_SIZE) > 0) + min_size += 8; + + /* + * Need to consider the data runs now. Each member of the run has byte + * that describes the size of the data length and offset. Each being + * 4 bits in the byte. + */ + curr = u16at (attr, GRUB_NTFS_ATTRIBUTE_DATA_RUNS); + + if (curr + 1 > min_size) + min_size = curr + 1; + + if (min_size > attr_size) + goto fail; + + /* + * Each attribute can store multiple data runs which are stored + * continuously in the attribute. They exist as one header byte + * with up to 14 bytes following it depending on the lengths. + * We stop when we hit a header that is just a NUL byte. + * + * https://flatcap.github.io/linux-ntfs/ntfs/concepts/data_runs.html + */ + while (attr[curr] != 0) + { + /* + * We stop when we hit a header that is just a NUL byte. The data + * run header is stored as a single byte where the top 4 bits refer + * to the number of bytes used to store the total length of the + * data run, and the number of bytes used to store the offset. + * These directly follow the header byte, so we use them to update + * the minimum size. + */ + min_size += (attr[curr] & 0x7) + ((attr[curr] >> 4) & 0x7); + curr += min_size; + min_size++; + if (min_size > attr_size) + goto fail; + } + } + + /* Name offset, doing this after data residence checks. */ + if (u16at (attr, GRUB_NTFS_ATTRIBUTE_NAME_OFFSET) != 0) + { + curr = u16at (attr, GRUB_NTFS_ATTRIBUTE_NAME_OFFSET); + /* + * Multiple the name length by 2 as its UTF-16. Can be zero if this in an + * unamed attribute. + */ + curr += attr[GRUB_NTFS_ATTRIBUTE_NAME_LENGTH] * 2; + if (curr > min_size) + min_size = curr; + } + + /* Padded to 8 bytes. */ + if (min_size % 8 != 0) + min_size += 8 - (min_size % 8); + + /* + * At this point min_size should be exactly attr_size but being flexible + * here to avoid any issues. + */ + if (min_size > attr_size) + goto fail; + + return true; + + fail: + grub_dprintf ("ntfs", "spare=%" PRIuGRUB_SIZE " min_size=%" PRIuGRUB_SIZE " attr_size=%" PRIuGRUB_SIZE "\n", + spare, min_size, attr_size); + return false; +} + +/* Return the next attribute if it exists, otherwise return NULL. */ +static grub_uint8_t * +next_attribute (grub_uint8_t *curr_attribute, void *end) +{ + grub_uint8_t *next = curr_attribute; + + /* + * Need to verify we aren't exceeding the end of the buffer by reading the + * header for the current attribute + */ + if (curr_attribute + GRUB_NTFS_ATTRIBUTE_HEADER_SIZE >= (grub_uint8_t *) end) + return NULL; + + next += u16at (curr_attribute, 4); + if (validate_attribute (next, end) == false) + return NULL; + + return next; +} + + grub_ntfscomp_func_t grub_ntfscomp_func; static grub_err_t @@ -119,13 +284,20 @@ static grub_err_t read_data (struct grub_ntfs_attr *at, grub_uint8_t *pa, grub_disk_read_hook_t read_hook, void *read_hook_data); -static void +static grub_err_t init_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft) { at->mft = mft; at->flags = (mft == &mft->data->mmft) ? GRUB_NTFS_AF_MMFT : 0; at->attr_nxt = mft->buf + first_attr_off (mft->buf); + at->end = mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR); + + if (at->attr_nxt > at->end) + return grub_error (GRUB_ERR_BAD_FS, "attributes start outside the MFT"); + at->attr_end = at->emft_buf = at->edat_buf = at->sbuf = NULL; + + return GRUB_ERR_NONE; } static void @@ -139,16 +311,18 @@ free_attr (struct grub_ntfs_attr *at) static grub_uint8_t * find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) { + grub_uint8_t *mft_end; + if (at->flags & GRUB_NTFS_AF_ALST) { retry: - while (at->attr_nxt < at->attr_end) + while (at->attr_nxt) { at->attr_cur = at->attr_nxt; - at->attr_nxt += u16at (at->attr_cur, 4); + at->attr_nxt = next_attribute (at->attr_cur, at->attr_end); if ((*at->attr_cur == attr) || (attr == 0)) { - grub_uint8_t *new_pos; + grub_uint8_t *new_pos, *end; if (at->flags & GRUB_NTFS_AF_MMFT) { @@ -172,15 +346,36 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) return NULL; } + /* + * Only time emft_bufs is defined is in this function, with this + * size. + */ + grub_size_t emft_buf_size = + at->mft->data->mft_size << GRUB_NTFS_BLK_SHR; + + /* + * Needs to be enough space for the successful case to even + * bother. + */ + if (first_attr_off (at->emft_buf) >= (emft_buf_size - 0x18 - 2)) + { + grub_error (GRUB_ERR_BAD_FS, + "can\'t find 0x%X in attribute list", + (unsigned char) *at->attr_cur); + return NULL; + } + new_pos = &at->emft_buf[first_attr_off (at->emft_buf)]; - while (*new_pos != 0xFF) + end = &at->emft_buf[emft_buf_size]; + + while (new_pos && *new_pos != 0xFF) { if ((*new_pos == *at->attr_cur) && (u16at (new_pos, 0xE) == u16at (at->attr_cur, 0x18))) { return new_pos; } - new_pos += u16at (new_pos, 4); + new_pos = next_attribute (new_pos, end); } grub_error (GRUB_ERR_BAD_FS, "can\'t find 0x%X in attribute list", @@ -191,9 +386,10 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) return NULL; } at->attr_cur = at->attr_nxt; - while (*at->attr_cur != 0xFF) + mft_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); + while (at->attr_cur < mft_end && *at->attr_cur != 0xFF) { - at->attr_nxt += u16at (at->attr_cur, 4); + at->attr_nxt = next_attribute (at->attr_cur, at->end); if (*at->attr_cur == GRUB_NTFS_AT_ATTRIBUTE_LIST) at->attr_end = at->attr_cur; if ((*at->attr_cur == attr) || (attr == 0)) @@ -236,13 +432,21 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) pa_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR); } at->flags |= GRUB_NTFS_AF_ALST; - while (at->attr_nxt < at->attr_end) + + /* From this point on pa_end is the end of the buffer */ + at->end = pa_end; + + if (validate_attribute (at->attr_nxt, pa_end) == false) + return NULL; + + while (at->attr_nxt) { if ((*at->attr_nxt == attr) || (attr == 0)) break; - at->attr_nxt += u16at (at->attr_nxt, 4); + at->attr_nxt = next_attribute (at->attr_nxt, pa_end); } - if (at->attr_nxt >= at->attr_end) + + if (at->attr_nxt >= at->attr_end || at->attr_nxt == NULL) return NULL; if ((at->flags & GRUB_NTFS_AF_MMFT) && (attr == GRUB_NTFS_AT_DATA)) @@ -263,7 +467,11 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) grub_cpu_to_le32 (at->mft->data->mft_start + 1)); pa = at->attr_nxt + u16at (pa, 4); - while (pa < at->attr_end) + + if (validate_attribute (pa, pa_end) == true) + pa = NULL; + + while (pa) { if (*pa != attr) break; @@ -279,7 +487,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr) u32at (pa, 0x10) * (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR), at->mft->data->mft_size << GRUB_NTFS_BLK_SHR, 0, 0, 0)) return NULL; - pa += u16at (pa, 4); + pa = next_attribute (pa, pa_end); } at->attr_nxt = at->attr_cur; at->flags &= ~GRUB_NTFS_AF_GPOS; @@ -295,7 +503,9 @@ locate_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft, { grub_uint8_t *pa; - init_attr (at, mft); + if (init_attr (at, mft) != GRUB_ERR_NONE) + return NULL; + pa = find_attr (at, attr); if (pa == NULL) return NULL; @@ -311,7 +521,8 @@ locate_attr (struct grub_ntfs_attr *at, struct grub_ntfs_file *mft, } grub_errno = GRUB_ERR_NONE; free_attr (at); - init_attr (at, mft); + if (init_attr (at, mft) != GRUB_ERR_NONE) + return NULL; pa = find_attr (at, attr); } return pa; @@ -363,7 +574,7 @@ grub_ntfs_read_run_list (struct grub_ntfs_rlst * ctx) goto retry; } } - return grub_error (GRUB_ERR_BAD_FS, "run list overflown"); + return grub_error (GRUB_ERR_BAD_FS, "run list overflow"); } ctx->curr_vcn = ctx->next_vcn; ctx->next_vcn += read_run_data (run, c1, 0); /* length of current VCN */ @@ -513,14 +724,17 @@ read_attr (struct grub_ntfs_attr *at, grub_uint8_t *dest, grub_disk_addr_t ofs, else vcn = ofs >> (at->mft->data->log_spc + GRUB_NTFS_BLK_SHR); pa = at->attr_nxt + u16at (at->attr_nxt, 4); - while (pa < at->attr_end) + if (validate_attribute (pa, at->attr_end) == false) + pa = NULL; + + while (pa) { if (*pa != attr) break; if (u32at (pa, 8) > vcn) break; at->attr_nxt = pa; - pa += u16at (pa, 4); + pa = next_attribute (pa, at->attr_end); } } pp = find_attr (at, attr); @@ -582,7 +796,7 @@ init_file (struct grub_ntfs_file *mft, grub_uint64_t mftno) mft->attr.attr_end = 0; /* Don't jump to attribute list */ } else - init_attr (&mft->attr, mft); + return init_attr (&mft->attr, mft); return 0; } @@ -808,7 +1022,9 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, bmp = NULL; at = &attr; - init_attr (at, mft); + if (init_attr (at, mft) != GRUB_ERR_NONE) + return 0; + while (1) { cur_pos = find_attr (at, GRUB_NTFS_AT_INDEX_ROOT); @@ -839,7 +1055,9 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir, bitmap = NULL; bitmap_len = 0; free_attr (at); + /* No need to check errors here, as it will already be fine */ init_attr (at, mft); + while ((cur_pos = find_attr (at, GRUB_NTFS_AT_BITMAP)) != NULL) { int ofs; @@ -1204,6 +1422,7 @@ grub_ntfs_label (grub_device_t device, char **label) struct grub_ntfs_data *data = 0; struct grub_fshelp_node *mft = 0; grub_uint8_t *pa; + grub_err_t err; grub_dl_ref (my_mod); @@ -1229,7 +1448,10 @@ grub_ntfs_label (grub_device_t device, char **label) goto fail; } - init_attr (&mft->attr, mft); + err = init_attr (&mft->attr, mft); + if (err != GRUB_ERR_NONE) + return err; + pa = find_attr (&mft->attr, GRUB_NTFS_AT_VOLUME_NAME); if (pa >= mft->buf + (mft->data->mft_size << GRUB_NTFS_BLK_SHR)) @@ -1320,11 +1542,16 @@ static struct grub_fs grub_ntfs_fs = GRUB_MOD_INIT (ntfs) { - grub_fs_register (&grub_ntfs_fs); + if (!grub_is_lockdown ()) + { + grub_ntfs_fs.mod = mod; + grub_fs_register (&grub_ntfs_fs); + } my_mod = mod; } GRUB_MOD_FINI (ntfs) { - grub_fs_unregister (&grub_ntfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_ntfs_fs); } diff --git a/grub-core/fs/ntfscomp.c b/grub-core/fs/ntfscomp.c index a009f2c2dd..b68bf5e40a 100644 --- a/grub-core/fs/ntfscomp.c +++ b/grub-core/fs/ntfscomp.c @@ -22,6 +22,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -29,7 +30,7 @@ static grub_err_t decomp_nextvcn (struct grub_ntfs_comp *cc) { if (cc->comp_head >= cc->comp_tail) - return grub_error (GRUB_ERR_BAD_FS, "compression block overflown"); + return grub_error (GRUB_ERR_BAD_FS, "compression block overflow"); if (grub_disk_read (cc->disk, (cc->comp_table[cc->comp_head].next_lcn - @@ -310,6 +311,7 @@ ntfscomp (grub_uint8_t *dest, grub_disk_addr_t ofs, { grub_err_t ret; grub_disk_addr_t vcn; + int log_sz; if (ctx->attr->sbuf) { @@ -349,7 +351,12 @@ ntfscomp (grub_uint8_t *dest, grub_disk_addr_t ofs, } ctx->comp.comp_head = ctx->comp.comp_tail = 0; - ctx->comp.cbuf = grub_malloc (1 << (ctx->comp.log_spc + GRUB_NTFS_BLK_SHR)); + if (grub_add (ctx->comp.log_spc, GRUB_NTFS_BLK_SHR, &log_sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("compression buffer size overflow")); + return 0; + } + ctx->comp.cbuf = grub_malloc (1 << log_sz); if (!ctx->comp.cbuf) return 0; diff --git a/grub-core/fs/odc.c b/grub-core/fs/odc.c index 790000622d..8e4e8aeac8 100644 --- a/grub-core/fs/odc.c +++ b/grub-core/fs/odc.c @@ -52,6 +52,7 @@ read_number (const char *str, grub_size_t size) GRUB_MOD_INIT (odc) { + grub_cpio_fs.mod = mod; grub_fs_register (&grub_cpio_fs); } diff --git a/grub-core/fs/proc.c b/grub-core/fs/proc.c index 5f516502d4..bcde433495 100644 --- a/grub-core/fs/proc.c +++ b/grub-core/fs/proc.c @@ -192,6 +192,7 @@ static struct grub_fs grub_procfs_fs = GRUB_MOD_INIT (procfs) { + grub_procfs_fs.mod = mod; grub_disk_dev_register (&grub_procfs_dev); grub_fs_register (&grub_procfs_fs); } diff --git a/grub-core/fs/reiserfs.c b/grub-core/fs/reiserfs.c index 36b26ac98a..ca47e0a43f 100644 --- a/grub-core/fs/reiserfs.c +++ b/grub-core/fs/reiserfs.c @@ -39,19 +39,10 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); -#define MIN(a, b) \ - ({ typeof (a) _a = (a); \ - typeof (b) _b = (b); \ - _a < _b ? _a : _b; }) - -#define MAX(a, b) \ - ({ typeof (a) _a = (a); \ - typeof (b) _b = (b); \ - _a > _b ? _a : _b; }) - #define REISERFS_SUPER_BLOCK_OFFSET 0x10000 #define REISERFS_MAGIC_LEN 12 #define REISERFS_MAGIC_STRING "ReIsEr" @@ -1076,7 +1067,7 @@ grub_reiserfs_read_real (struct grub_fshelp_node *node, grub_reiserfs_set_key_type (&key, GRUB_REISERFS_ANY, 2); initial_position = off; current_position = 0; - final_position = MIN (len + initial_position, node->size); + final_position = grub_min (len + initial_position, node->size); grub_dprintf ("reiserfs", "Reading from %lld to %lld (%lld instead of requested %ld)\n", (unsigned long long) initial_position, @@ -1115,8 +1106,8 @@ grub_reiserfs_read_real (struct grub_fshelp_node *node, grub_dprintf ("reiserfs_blocktype", "D: %u\n", (unsigned) block); if (initial_position < current_position + item_size) { - offset = MAX ((signed) (initial_position - current_position), 0); - length = (MIN (item_size, final_position - current_position) + offset = grub_max ((signed) (initial_position - current_position), 0); + length = (grub_min (item_size, final_position - current_position) - offset); grub_dprintf ("reiserfs", "Reading direct block %u from %u to %u...\n", @@ -1161,9 +1152,9 @@ grub_reiserfs_read_real (struct grub_fshelp_node *node, grub_dprintf ("reiserfs_blocktype", "I: %u\n", (unsigned) block); if (current_position + block_size >= initial_position) { - offset = MAX ((signed) (initial_position - current_position), - 0); - length = (MIN (block_size, final_position - current_position) + offset = grub_max ((signed) (initial_position - current_position), + 0); + length = (grub_min (block_size, final_position - current_position) - offset); grub_dprintf ("reiserfs", "Reading indirect block %u from %u to %u...\n", @@ -1205,7 +1196,7 @@ grub_reiserfs_read_real (struct grub_fshelp_node *node, switch (found.type) { case GRUB_REISERFS_DIRECT: - read_length = MIN (len, item_size - file->offset); + read_length = grub_min (len, item_size - file->offset); grub_disk_read (found.data->disk, (found.block_number * block_size) / GRUB_DISK_SECTOR_SIZE, grub_le_to_cpu16 (found.header.item_location) + file->offset, @@ -1224,12 +1215,12 @@ grub_reiserfs_read_real (struct grub_fshelp_node *node, item_size, (char *) indirect_block_ptr); if (grub_errno) goto fail; - len = MIN (len, file->size - file->offset); + len = grub_min (len, file->size - file->offset); for (indirect_block = file->offset / block_size; indirect_block < indirect_block_count && read_length < len; indirect_block++) { - read = MIN (block_size, len - read_length); + read = grub_min (block_size, len - read_length); grub_disk_read (found.data->disk, (grub_le_to_cpu32 (indirect_block_ptr[indirect_block]) * block_size) / GRUB_DISK_SECTOR_SIZE, file->offset % block_size, read, @@ -1417,11 +1408,16 @@ static struct grub_fs grub_reiserfs_fs = GRUB_MOD_INIT(reiserfs) { - grub_fs_register (&grub_reiserfs_fs); + if (!grub_is_lockdown ()) + { + grub_reiserfs_fs.mod = mod; + grub_fs_register (&grub_reiserfs_fs); + } my_mod = mod; } GRUB_MOD_FINI(reiserfs) { - grub_fs_unregister (&grub_reiserfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_reiserfs_fs); } diff --git a/grub-core/fs/romfs.c b/grub-core/fs/romfs.c index 1f7dcfca1d..eafab03b25 100644 --- a/grub-core/fs/romfs.c +++ b/grub-core/fs/romfs.c @@ -23,6 +23,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -475,10 +476,15 @@ static struct grub_fs grub_romfs_fs = GRUB_MOD_INIT(romfs) { - grub_fs_register (&grub_romfs_fs); + if (!grub_is_lockdown ()) + { + grub_romfs_fs.mod = mod; + grub_fs_register (&grub_romfs_fs); + } } GRUB_MOD_FINI(romfs) { - grub_fs_unregister (&grub_romfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_romfs_fs); } diff --git a/grub-core/fs/sfs.c b/grub-core/fs/sfs.c index 983e880088..bad4ae8d10 100644 --- a/grub-core/fs/sfs.c +++ b/grub-core/fs/sfs.c @@ -26,6 +26,7 @@ #include #include #include +#include #include GRUB_MOD_LICENSE ("GPLv3+"); @@ -428,6 +429,9 @@ grub_sfs_mount (grub_disk_t disk) - 24 /* offsetof (struct grub_sfs_objc, objects) */ - 25); /* offsetof (struct grub_sfs_obj, filename) */ data->label = grub_zalloc (max_len + 1); + if (data->label == NULL) + goto fail; + grub_strncpy (data->label, (char *) rootobjc->objects[0].filename, max_len); grub_free (rootobjc_data); @@ -779,11 +783,16 @@ static struct grub_fs grub_sfs_fs = GRUB_MOD_INIT(sfs) { - grub_fs_register (&grub_sfs_fs); + if (!grub_is_lockdown ()) + { + grub_sfs_fs.mod = mod; + grub_fs_register (&grub_sfs_fs); + } my_mod = mod; } GRUB_MOD_FINI(sfs) { - grub_fs_unregister (&grub_sfs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_sfs_fs); } diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c index a30e6ebe14..cf2bca8221 100644 --- a/grub-core/fs/squash4.c +++ b/grub-core/fs/squash4.c @@ -460,11 +460,11 @@ grub_squash_read_symlink (grub_fshelp_node_t node) { char *ret; grub_err_t err; - grub_size_t sz; + grub_uint32_t sz; if (grub_add (grub_le_to_cpu32 (node->ino.symlink.namelen), 1, &sz)) { - grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("symlink name length overflow")); return NULL; } @@ -580,6 +580,7 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, struct grub_squash_dirent di; struct grub_squash_inode ino; grub_size_t sz; + grub_uint16_t nlen; err = read_chunk (dir->data, &di, sizeof (di), grub_le_to_cpu64 (dir->data->sb.diroffset) @@ -595,7 +596,12 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir, if (err) return 0; - buf = grub_malloc (grub_le_to_cpu16 (di.namelen) + 2); + if (grub_add (grub_le_to_cpu16 (di.namelen), 2, &nlen)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("name length overflow")); + return 0; + } + buf = grub_malloc (nlen); if (!buf) return 0; err = read_chunk (dir->data, buf, @@ -816,10 +822,10 @@ direct_read (struct grub_squash_data *data, break; } total_blocks = ((total_size + data->blksz - 1) >> data->log2_blksz); - ino->block_sizes = grub_malloc (total_blocks - * sizeof (ino->block_sizes[0])); - ino->cumulated_block_sizes = grub_malloc (total_blocks - * sizeof (ino->cumulated_block_sizes[0])); + ino->block_sizes = grub_calloc (total_blocks, + sizeof (ino->block_sizes[0])); + ino->cumulated_block_sizes = grub_calloc (total_blocks, + sizeof (ino->cumulated_block_sizes[0])); if (!ino->block_sizes || !ino->cumulated_block_sizes) { grub_free (ino->block_sizes); @@ -1044,6 +1050,7 @@ static struct grub_fs grub_squash_fs = GRUB_MOD_INIT(squash4) { + grub_squash_fs.mod = mod; grub_fs_register (&grub_squash_fs); } diff --git a/grub-core/fs/tar.c b/grub-core/fs/tar.c index c551ed6b52..1eaa5349f3 100644 --- a/grub-core/fs/tar.c +++ b/grub-core/fs/tar.c @@ -25,6 +25,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -76,8 +77,10 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, { struct head hd; int reread = 0, have_longname = 0, have_longlink = 0; + grub_size_t sz; data->hofs = data->next_hofs; + *name = NULL; for (reread = 0; reread < 3; reread++) { @@ -96,8 +99,13 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, if (hd.typeflag == 'L') { grub_err_t err; - grub_size_t namesize = read_number (hd.size, sizeof (hd.size)); - *name = grub_malloc (namesize + 1); + grub_size_t namesize; + + if (grub_cast (read_number (hd.size, sizeof (hd.size)), &namesize) || + grub_add (namesize, 1, &sz)) + return grub_error (GRUB_ERR_BAD_FS, N_("name size overflow")); + + *name = grub_malloc (sz); if (*name == NULL) return grub_errno; err = grub_disk_read (data->disk, 0, @@ -116,16 +124,21 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, if (hd.typeflag == 'K') { grub_err_t err; - grub_size_t linksize = read_number (hd.size, sizeof (hd.size)); - if (data->linkname_alloc < linksize + 1) + grub_size_t linksize; + + if (grub_cast (read_number (hd.size, sizeof (hd.size)), &linksize) || + grub_add (linksize, 1, &sz)) + return grub_error (GRUB_ERR_BAD_FS, N_("link size overflow")); + + if (data->linkname_alloc < sz) { char *n; - n = grub_calloc (2, linksize + 1); + n = grub_calloc (2, sz); if (!n) return grub_errno; grub_free (data->linkname); data->linkname = n; - data->linkname_alloc = 2 * (linksize + 1); + data->linkname_alloc = 2 * (sz); } err = grub_disk_read (data->disk, 0, @@ -148,7 +161,10 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, while (extra_size < sizeof (hd.prefix) && hd.prefix[extra_size]) extra_size++; - *name = grub_malloc (sizeof (hd.name) + extra_size + 2); + + if (grub_add (sizeof (hd.name) + 2, extra_size, &sz)) + return grub_error (GRUB_ERR_BAD_FS, N_("long name size overflow")); + *name = grub_malloc (sz); if (*name == NULL) return grub_errno; if (hd.prefix[0]) @@ -160,15 +176,22 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, (*name)[extra_size + sizeof (hd.name)] = 0; } - data->size = read_number (hd.size, sizeof (hd.size)); + if (grub_cast (read_number (hd.size, sizeof (hd.size)), &data->size)) + return grub_error (GRUB_ERR_BAD_FS, N_("data size overflow")); + data->dofs = data->hofs + GRUB_DISK_SECTOR_SIZE; data->next_hofs = data->dofs + ((data->size + GRUB_DISK_SECTOR_SIZE - 1) & ~(GRUB_DISK_SECTOR_SIZE - 1)); if (mtime) - *mtime = read_number (hd.mtime, sizeof (hd.mtime)); + { + if (grub_cast (read_number (hd.mtime, sizeof (hd.mtime)), mtime)) + return grub_error (GRUB_ERR_BAD_FS, N_("mtime overflow")); + } if (mode) { - *mode = read_number (hd.mode, sizeof (hd.mode)); + if (grub_cast (read_number (hd.mode, sizeof (hd.mode)), mode)) + return grub_error (GRUB_ERR_BAD_FS, N_("mode overflow")); + switch (hd.typeflag) { /* Hardlink. */ @@ -202,6 +225,10 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name, } return GRUB_ERR_NONE; } + + if (*name == NULL) + return grub_error (GRUB_ERR_BAD_FS, "invalid tar archive"); + return GRUB_ERR_NONE; } @@ -336,6 +363,7 @@ static struct grub_fs grub_cpio_fs = { GRUB_MOD_INIT (tar) { + grub_cpio_fs.mod = mod; grub_fs_register (&grub_cpio_fs); } diff --git a/grub-core/fs/udf.c b/grub-core/fs/udf.c index b836e61075..3d5ee5af50 100644 --- a/grub-core/fs/udf.c +++ b/grub-core/fs/udf.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -1455,11 +1456,16 @@ static struct grub_fs grub_udf_fs = { GRUB_MOD_INIT (udf) { - grub_fs_register (&grub_udf_fs); + if (!grub_is_lockdown ()) + { + grub_udf_fs.mod = mod; + grub_fs_register (&grub_udf_fs); + } my_mod = mod; } GRUB_MOD_FINI (udf) { - grub_fs_unregister (&grub_udf_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_udf_fs); } diff --git a/grub-core/fs/ufs.c b/grub-core/fs/ufs.c index a354c92d93..8b5adbd48d 100644 --- a/grub-core/fs/ufs.c +++ b/grub-core/fs/ufs.c @@ -25,6 +25,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -463,7 +464,7 @@ grub_ufs_lookup_symlink (struct grub_ufs_data *data, int ino) /* Check against zero is paylindromic, no need to swap. */ if (data->inode.nblocks == 0 && INODE_SIZE (data) <= sizeof (data->inode.symlink)) - grub_strcpy (symlink, (char *) data->inode.symlink); + grub_strlcpy (symlink, (char *) data->inode.symlink, sz); else { if (grub_ufs_read_file (data, 0, 0, 0, sz, symlink) < 0) @@ -899,7 +900,11 @@ GRUB_MOD_INIT(ufs1) #endif #endif { - grub_fs_register (&grub_ufs_fs); + if (!grub_is_lockdown ()) + { + grub_ufs_fs.mod = mod; + grub_fs_register (&grub_ufs_fs); + } my_mod = mod; } @@ -913,6 +918,7 @@ GRUB_MOD_FINI(ufs1) #endif #endif { - grub_fs_unregister (&grub_ufs_fs); + if (!grub_is_lockdown ()) + grub_fs_unregister (&grub_ufs_fs); } diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c index bc2224dbb4..0519105aaf 100644 --- a/grub-core/fs/xfs.c +++ b/grub-core/fs/xfs.c @@ -88,7 +88,10 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define XFS_SB_FEAT_INCOMPAT_META_UUID (1 << 2) /* metadata UUID */ #define XFS_SB_FEAT_INCOMPAT_BIGTIME (1 << 3) /* large timestamps */ #define XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR (1 << 4) /* needs xfs_repair */ -#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */ +#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5) /* large extent counters */ +#define XFS_SB_FEAT_INCOMPAT_EXCHRANGE (1 << 6) /* exchangerange supported */ +#define XFS_SB_FEAT_INCOMPAT_PARENT (1 << 7) /* parent pointers */ +#define XFS_SB_FEAT_INCOMPAT_METADIR (1 << 8) /* metadata dir tree */ /* * Directory entries with ftype are explicitly handled by GRUB code. @@ -98,6 +101,15 @@ GRUB_MOD_LICENSE ("GPLv3+"); * * We do not currently verify metadata UUID, so it is safe to read filesystems * with the XFS_SB_FEAT_INCOMPAT_META_UUID feature. + * + * We do not currently replay the log, so it is safe to read filesystems + * with the XFS_SB_FEAT_INCOMPAT_EXCHRANGE feature. + * + * We do not currently read directory parent pointers, so it is safe to read + * filesystems with the XFS_SB_FEAT_INCOMPAT_PARENT feature. + * + * We do not currently look at realtime or quota metadata, so it is safe to + * read filesystems with the XFS_SB_FEAT_INCOMPAT_METADIR feature. */ #define XFS_SB_FEAT_INCOMPAT_SUPPORTED \ (XFS_SB_FEAT_INCOMPAT_FTYPE | \ @@ -105,7 +117,10 @@ GRUB_MOD_LICENSE ("GPLv3+"); XFS_SB_FEAT_INCOMPAT_META_UUID | \ XFS_SB_FEAT_INCOMPAT_BIGTIME | \ XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR | \ - XFS_SB_FEAT_INCOMPAT_NREXT64) + XFS_SB_FEAT_INCOMPAT_NREXT64 | \ + XFS_SB_FEAT_INCOMPAT_EXCHRANGE | \ + XFS_SB_FEAT_INCOMPAT_PARENT | \ + XFS_SB_FEAT_INCOMPAT_METADIR) struct grub_xfs_sblock { @@ -327,6 +342,8 @@ static int grub_xfs_sb_valid(struct grub_xfs_data *data) } return 1; } + + grub_error (GRUB_ERR_BAD_FS, "unsupported XFS filesystem version"); return 0; } @@ -595,6 +612,17 @@ grub_xfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) do { grub_uint64_t i; + grub_addr_t keys_end, data_end; + + if (grub_mul (sizeof (grub_uint64_t), nrec, &keys_end) || + grub_add ((grub_addr_t) keys, keys_end, &keys_end) || + grub_add ((grub_addr_t) node->data, node->data->data_size, &data_end) || + keys_end > data_end) + { + grub_error (GRUB_ERR_BAD_FS, "invalid number of XFS root keys"); + grub_free (leaf); + return 0; + } for (i = 0; i < nrec; i++) { @@ -705,6 +733,7 @@ static char * grub_xfs_read_symlink (grub_fshelp_node_t node) { grub_ssize_t size = grub_be_to_cpu64 (node->inode.size); + grub_size_t sz; if (size < 0) { @@ -726,7 +755,12 @@ grub_xfs_read_symlink (grub_fshelp_node_t node) if (node->data->hascrc) off = 56; - symlink = grub_malloc (size + 1); + if (grub_add (size, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("symlink size overflow")); + return 0; + } + symlink = grub_malloc (sz); if (!symlink) return 0; @@ -776,8 +810,15 @@ static int iterate_dir_call_hook (grub_uint64_t ino, const char *filename, { struct grub_fshelp_node *fdiro; grub_err_t err; + grub_size_t sz; - fdiro = grub_malloc (grub_xfs_fshelp_size(ctx->diro->data) + 1); + if (grub_add (grub_xfs_fshelp_size(ctx->diro->data), 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("directory data size overflow")); + grub_print_error (); + return 0; + } + fdiro = grub_malloc (sz); if (!fdiro) { grub_print_error (); @@ -902,6 +943,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, grub_xfs_first_de(dir->data, dirblock); int entries = -1; char *end = dirblock + dirblk_size; + grub_uint32_t magic; numread = grub_xfs_read_file (dir, 0, 0, blk << dirblk_log2, @@ -912,11 +954,20 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, return 0; } + /* + * If this data block isn't actually part of the extent list then + * grub_xfs_read_file() returns a block of zeros. So, if the magic + * number field is all zeros then this block should be skipped. + */ + magic = *(grub_uint32_t *)(void *) dirblock; + if (!magic) + continue; + /* * Leaf and tail information are only in the data block if the number * of extents is 1. */ - if (dir->inode.nextents == grub_cpu_to_be32_compile_time (1)) + if (grub_xfs_get_inode_nextents(&dir->inode) == 1) { struct grub_xfs_dirblock_tail *tail = grub_xfs_dir_tail (dir->data, dirblock); @@ -970,7 +1021,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir, * The expected number of directory entries is only tracked for the * single extent case. */ - if (dir->inode.nextents == grub_cpu_to_be32_compile_time (1)) + if (grub_xfs_get_inode_nextents(&dir->inode) == 1) { /* Check if last direntry in this block is reached. */ entries--; @@ -1047,7 +1098,7 @@ grub_xfs_mount (grub_disk_t disk) return data; fail: - if (grub_errno == GRUB_ERR_OUT_OF_RANGE) + if (grub_errno == GRUB_ERR_OUT_OF_RANGE || grub_errno == GRUB_ERR_NONE) grub_error (GRUB_ERR_BAD_FS, "not an XFS filesystem"); grub_free (data); @@ -1281,6 +1332,7 @@ static struct grub_fs grub_xfs_fs = GRUB_MOD_INIT(xfs) { + grub_xfs_fs.mod = mod; grub_fs_register (&grub_xfs_fs); my_mod = mod; } diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c index b5453e0062..5ff647ffbb 100644 --- a/grub-core/fs/zfs/zfs.c +++ b/grub-core/fs/zfs/zfs.c @@ -614,6 +614,8 @@ zfs_fetch_nvlist (struct grub_zfs_device_desc *diskdesc, char **nvlist) return grub_error (GRUB_ERR_BUG, "member drive unknown"); *nvlist = grub_malloc (VDEV_PHYS_SIZE); + if (!*nvlist) + return grub_errno; /* Read in the vdev name-value pair list (112K). */ err = grub_disk_read (diskdesc->dev->disk, diskdesc->vdev_phys_sector, 0, @@ -723,8 +725,13 @@ fill_vdev_info_real (struct grub_zfs_data *data, { fill->n_children = nelm; - fill->children = grub_zalloc (fill->n_children - * sizeof (fill->children[0])); + fill->children = grub_calloc (fill->n_children, + sizeof (fill->children[0])); + if (!fill->children) + { + grub_free (type); + return grub_errno; + } } for (i = 0; i < nelm; i++) @@ -2387,6 +2394,7 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap, zap_dnode->endian) << DNODE_SHIFT); grub_err_t err; grub_zfs_endian_t endian; + grub_size_t sz; if (zap_verify (zap, zap_dnode->endian)) return 0; @@ -2448,8 +2456,19 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap, if (le->le_type != ZAP_CHUNK_ENTRY) continue; - buf = grub_malloc (grub_zfs_to_cpu16 (le->le_name_length, endian) - * name_elem_length + 1); + if (grub_mul (grub_zfs_to_cpu16 (le->le_name_length, endian), name_elem_length, &sz) || + grub_add (sz, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("buffer size overflow")); + grub_free (l); + return grub_errno; + } + buf = grub_malloc (sz); + if (!buf) + { + grub_free (l); + return grub_errno; + } if (zap_leaf_array_get (l, endian, blksft, grub_zfs_to_cpu16 (le->le_name_chunk, endian), @@ -2465,6 +2484,12 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap, val_length = ((int) le->le_value_length * (int) le->le_int_size); val = grub_malloc (grub_zfs_to_cpu16 (val_length, endian)); + if (!val) + { + grub_free (l); + grub_free (buf); + return grub_errno; + } if (zap_leaf_array_get (l, endian, blksft, grub_zfs_to_cpu16 (le->le_value_chunk, endian), @@ -2872,6 +2897,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, && ((grub_zfs_to_cpu64(((znode_phys_t *) DN_BONUS (&dnode_path->dn.dn))->zp_mode, dnode_path->dn.endian) >> 12) & 0xf) == 0xa) { char *sym_value; + grub_size_t sz; grub_size_t sym_sz; int free_symval = 0; char *oldpath = path, *oldpathbuf = path_buf; @@ -2923,7 +2949,18 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, break; free_symval = 1; } - path = path_buf = grub_malloc (sym_sz + grub_strlen (oldpath) + 1); + + if (grub_add (sym_sz, grub_strlen (oldpath), &sz) || + grub_add (sz, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("path buffer size overflow")); + grub_free (oldpathbuf); + if (free_symval) + grub_free (sym_value); + err = grub_errno; + break; + } + path = path_buf = grub_malloc (sz); if (!path_buf) { grub_free (oldpathbuf); @@ -2960,6 +2997,7 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, { void *sahdrp; int hdrsize; + grub_size_t sz; if (dnode_path->dn.dn.dn_bonuslen != 0) { @@ -2993,7 +3031,15 @@ dnode_get_path (struct subvolume *subvol, const char *path_in, dnode_end_t *dn, + SA_SIZE_OFFSET), dnode_path->dn.endian); char *oldpath = path, *oldpathbuf = path_buf; - path = path_buf = grub_malloc (sym_sz + grub_strlen (oldpath) + 1); + if (grub_add (sym_sz, grub_strlen (oldpath), &sz) || + grub_add (sz, 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("path buffer size overflow")); + grub_free (oldpathbuf); + err = grub_errno; + break; + } + path = path_buf = grub_malloc (sz); if (!path_buf) { grub_free (oldpathbuf); @@ -3263,6 +3309,8 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol, filename = 0; snapname = 0; fsname = grub_strdup (fullpath); + if (!fsname) + return grub_errno; } else { @@ -3568,6 +3616,7 @@ grub_zfs_nvlist_lookup_nvlist_array (const char *nvlist, const char *name, unsigned i; grub_size_t nelm; int elemsize = 0; + int sz; found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST_ARRAY, &nvpair, &size, &nelm); @@ -3602,7 +3651,12 @@ grub_zfs_nvlist_lookup_nvlist_array (const char *nvlist, const char *name, return 0; } - ret = grub_zalloc (elemsize + sizeof (grub_uint32_t)); + if (grub_add (elemsize, sizeof (grub_uint32_t), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("elemsize overflow")); + return 0; + } + ret = grub_zalloc (sz); if (!ret) return 0; grub_memcpy (ret, nvlist, sizeof (grub_uint32_t)); @@ -3678,8 +3732,13 @@ zfs_mount (grub_device_t dev) #endif data->n_devices_allocated = 16; - data->devices_attached = grub_malloc (sizeof (data->devices_attached[0]) - * data->n_devices_allocated); + data->devices_attached = grub_calloc (data->n_devices_allocated, + sizeof (data->devices_attached[0])); + if (!data->devices_attached) + { + grub_free (data); + return NULL; + } data->n_devices_attached = 0; err = scan_disk (dev, data, 1, &inserted); if (err) @@ -4193,6 +4252,7 @@ iterate_zap_snap (const char *name, grub_uint64_t val, struct grub_dirhook_info info; char *name2; int ret; + grub_size_t sz; dnode_end_t mdn; @@ -4213,7 +4273,13 @@ iterate_zap_snap (const char *name, grub_uint64_t val, return 0; } - name2 = grub_malloc (grub_strlen (name) + 2); + if (grub_add (grub_strlen (name), 2, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("name length overflow")); + + name2 = grub_malloc (sz); + if (!name2) + return grub_errno; + name2[0] = '@'; grub_memcpy (name2 + 1, name, grub_strlen (name) + 1); ret = ctx->hook (name2, &info, ctx->hook_data); @@ -4424,6 +4490,7 @@ static struct grub_fs grub_zfs_fs = { GRUB_MOD_INIT (zfs) { COMPILE_TIME_ASSERT (sizeof (zap_leaf_chunk_t) == ZAP_LEAF_CHUNKSIZE); + grub_zfs_fs.mod = mod; grub_fs_register (&grub_zfs_fs); #ifndef GRUB_UTIL my_mod = mod; diff --git a/grub-core/gdb/cstub.c b/grub-core/gdb/cstub.c index b64acd70fe..99281472d3 100644 --- a/grub-core/gdb/cstub.c +++ b/grub-core/gdb/cstub.c @@ -215,7 +215,6 @@ grub_gdb_trap (int trap_no) grub_printf ("Unhandled exception 0x%x at ", trap_no); grub_backtrace_print_address ((void *) grub_gdb_regs[PC]); grub_printf ("\n"); - grub_backtrace_pointer ((void *) grub_gdb_regs[EBP]); grub_fatal ("Unhandled exception"); } diff --git a/grub-core/genmod.sh.in b/grub-core/genmod.sh.in index e57c4d9205..337753c574 100644 --- a/grub-core/genmod.sh.in +++ b/grub-core/genmod.sh.in @@ -57,8 +57,11 @@ if test x@TARGET_APPLE_LINKER@ != x1; then @TARGET_STRIP@ --strip-unneeded \ -K grub_mod_init -K grub_mod_fini \ -K _grub_mod_init -K _grub_mod_fini \ - -R .note.gnu.gold-version -R .note.GNU-stack \ + -R .note.GNU-stack \ + -R .note.gnu.gold-version \ + -R .note.gnu.property \ -R .gnu.build.attributes \ + -R '.llvm*' \ -R .rel.gnu.build.attributes \ -R .rela.gnu.build.attributes \ -R .eh_frame -R .rela.eh_frame -R .rel.eh_frame \ diff --git a/grub-core/genmoddep.awk b/grub-core/genmoddep.awk index cc987a53aa..ab457cb2b5 100644 --- a/grub-core/genmoddep.awk +++ b/grub-core/genmoddep.awk @@ -35,7 +35,7 @@ BEGIN { for (i = 3; i <= NF; i++) { modtab[$2] = modtab[$2] " " $i; } - } + } else if ($1 == "") {} #Skip empty lines else { printf "error: %u: unrecognized input format\n", NR >"/dev/stderr"; error++; diff --git a/grub-core/gettext/gettext.c b/grub-core/gettext/gettext.c index 7a1c14e4f3..1ca152ddd9 100644 --- a/grub-core/gettext/gettext.c +++ b/grub-core/gettext/gettext.c @@ -26,6 +26,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -99,6 +100,7 @@ grub_gettext_getstr_from_position (struct grub_gettext_context *ctx, char *translation; struct string_descriptor desc; grub_err_t err; + grub_size_t alloc_sz; internal_position = (off + position * sizeof (desc)); @@ -109,7 +111,10 @@ grub_gettext_getstr_from_position (struct grub_gettext_context *ctx, length = grub_cpu_to_le32 (desc.length); offset = grub_cpu_to_le32 (desc.offset); - translation = grub_malloc (length + 1); + if (grub_add (length, 1, &alloc_sz)) + return NULL; + + translation = grub_malloc (alloc_sz); if (!translation) return NULL; @@ -323,8 +328,8 @@ grub_mofile_open (struct grub_gettext_context *ctx, for (ctx->grub_gettext_max_log = 0; ctx->grub_gettext_max >> ctx->grub_gettext_max_log; ctx->grub_gettext_max_log++); - ctx->grub_gettext_msg_list = grub_zalloc (ctx->grub_gettext_max - * sizeof (ctx->grub_gettext_msg_list[0])); + ctx->grub_gettext_msg_list = grub_calloc (ctx->grub_gettext_max, + sizeof (ctx->grub_gettext_msg_list[0])); if (!ctx->grub_gettext_msg_list) { grub_file_close (fd); @@ -424,6 +429,13 @@ grub_gettext_init_ext (struct grub_gettext_context *ctx, grub_free (lang); } + /* If no translations are available, fall back to untranslated text. */ + if (err == GRUB_ERR_FILE_NOT_FOUND) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (locale[0] == 'e' && locale[1] == 'n' && (locale[2] == '\0' || locale[2] == '_')) grub_errno = err = GRUB_ERR_NONE; @@ -497,6 +509,8 @@ grub_cmd_translate (grub_command_t cmd __attribute__ ((unused)), return 0; } +static grub_command_t cmd; + GRUB_MOD_INIT (gettext) { const char *lang; @@ -516,13 +530,14 @@ GRUB_MOD_INIT (gettext) grub_register_variable_hook ("locale_dir", NULL, read_main); grub_register_variable_hook ("secondary_locale_dir", NULL, read_secondary); - grub_register_command_p1 ("gettext", grub_cmd_translate, - N_("STRING"), - /* TRANSLATORS: It refers to passing the string through gettext. - So it's "translate" in the same meaning as in what you're - doing now. - */ - N_("Translates the string with the current settings.")); + cmd = grub_register_command_p1 ("gettext", grub_cmd_translate, + N_("STRING"), + /* + * TRANSLATORS: It refers to passing the string through gettext. + * So it's "translate" in the same meaning as in what you're + * doing now. + */ + N_("Translates the string with the current settings.")); /* Reload .mo file information if lang changes. */ grub_register_variable_hook ("lang", NULL, grub_gettext_env_write_lang); @@ -535,6 +550,12 @@ GRUB_MOD_INIT (gettext) GRUB_MOD_FINI (gettext) { + grub_register_variable_hook ("locale_dir", NULL, NULL); + grub_register_variable_hook ("secondary_locale_dir", NULL, NULL); + grub_register_variable_hook ("lang", NULL, NULL); + + grub_unregister_command (cmd); + grub_gettext_delete_list (&main_context); grub_gettext_delete_list (&secondary_context); diff --git a/grub-core/io/bufio.c b/grub-core/io/bufio.c index a458c3aca7..1637731535 100644 --- a/grub-core/io/bufio.c +++ b/grub-core/io/bufio.c @@ -139,7 +139,7 @@ grub_bufio_read (grub_file_t file, char *buf, grub_size_t len) return res; /* Need to read some more. */ - next_buf = (file->offset + res + len - 1) & ~((grub_off_t) bufio->block_size - 1); + next_buf = (grub_divmod64 (file->offset + res + len - 1, bufio->block_size, NULL)) * bufio->block_size; /* Now read between file->offset + res and bufio->buffer_at. */ if (file->offset + res < next_buf) { diff --git a/grub-core/kern/acpi.c b/grub-core/kern/acpi.c index 48ded4e2ea..8ff0835d50 100644 --- a/grub-core/kern/acpi.c +++ b/grub-core/kern/acpi.c @@ -75,7 +75,7 @@ grub_acpi_xsdt_find_table (struct grub_acpi_table_header *xsdt, const char *sig) return 0; ptr = (grub_unaligned_uint64_t *) (xsdt + 1); - s = (xsdt->length - sizeof (*xsdt)) / sizeof (grub_uint32_t); + s = (xsdt->length - sizeof (*xsdt)) / sizeof (grub_uint64_t); for (; s; s--, ptr++) { struct grub_acpi_table_header *tbl; diff --git a/grub-core/kern/arm/dl.c b/grub-core/kern/arm/dl.c index eab9d17ff2..9260737936 100644 --- a/grub-core/kern/arm/dl.c +++ b/grub-core/kern/arm/dl.c @@ -278,3 +278,16 @@ grub_arch_dl_check_header (void *ehdr) return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ +#ifdef GRUB_MACHINE_EFI + return 4096; +#else + return 1; +#endif +} diff --git a/grub-core/kern/arm/efi/startup.S b/grub-core/kern/arm/efi/startup.S index 9f8265315a..f3bc41f9d0 100644 --- a/grub-core/kern/arm/efi/startup.S +++ b/grub-core/kern/arm/efi/startup.S @@ -23,6 +23,8 @@ .file "startup.S" .text .arm + .globl start, _start +FUNCTION(start) FUNCTION(_start) /* * EFI_SYSTEM_TABLE and EFI_HANDLE are passed in r1/r0. diff --git a/grub-core/kern/arm/startup.S b/grub-core/kern/arm/startup.S index 3946fe8e18..5679a1d00a 100644 --- a/grub-core/kern/arm/startup.S +++ b/grub-core/kern/arm/startup.S @@ -48,6 +48,8 @@ .text .arm + .globl start, _start +FUNCTION(start) FUNCTION(_start) b codestart diff --git a/grub-core/kern/arm64/backtrace.c b/grub-core/kern/arm64/backtrace.c new file mode 100644 index 0000000000..019c6fdfef --- /dev/null +++ b/grub-core/kern/arm64/backtrace.c @@ -0,0 +1,94 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_STACK_FRAME 102400 + +struct fplr +{ + void *lr; + struct fplr *fp; +}; + +void +grub_backtrace_pointer (void *frame, unsigned int skip) +{ + unsigned int x = 0; + struct fplr *fplr = (struct fplr *)frame; + + while (fplr) + { + const char *name = NULL; + char *addr = NULL; + + grub_dprintf("backtrace", "fp is %p next_fp is %p\n", + fplr, fplr->fp); + + if (x >= skip) + { + name = grub_get_symbol_by_addr (fplr->lr, 1); + if (name) + addr = grub_resolve_symbol (name); + grub_backtrace_print_address (fplr->lr); + + if (addr && addr != fplr->lr) + grub_printf (" %s() %p+%p \n", name ? name : "unknown", addr, + (void *)((grub_uint64_t)fplr->lr - (grub_uint64_t)addr)); + else + grub_printf(" %s() %p \n", name ? name : "unknown", addr); + + } + + x += 1; + + if (fplr->fp < fplr || + (grub_uint64_t)fplr->fp - (grub_uint64_t)fplr > MAX_STACK_FRAME || + fplr->fp == fplr) + { + break; + } + fplr = fplr->fp; + } +} + +asm ("\t.global \"_text\"\n" + "_text:\n" + "\t.quad .text\n" + "\t.global \"_data\"\n" + "_data:\n" + "\t.quad .data\n" + ); + +extern grub_uint64_t _text; +extern grub_uint64_t _data; + +void +grub_backtrace_arch (unsigned int skip) +{ + grub_printf ("Backtrace (.text %p .data %p):\n", + (void *)_text, (void *)_data); + skip += 1; + grub_backtrace_pointer(__builtin_frame_address(0), skip); +} diff --git a/grub-core/kern/arm64/dl.c b/grub-core/kern/arm64/dl.c index a2b5789a9d..95c6d5bf4e 100644 --- a/grub-core/kern/arm64/dl.c +++ b/grub-core/kern/arm64/dl.c @@ -196,3 +196,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ +#ifdef GRUB_MACHINE_EFI + return 4096; +#else + return 1; +#endif +} diff --git a/grub-core/kern/arm64/efi/startup.S b/grub-core/kern/arm64/efi/startup.S index 666a7ee3c9..41676bdb2b 100644 --- a/grub-core/kern/arm64/efi/startup.S +++ b/grub-core/kern/arm64/efi/startup.S @@ -19,7 +19,9 @@ #include .file "startup.S" + .globl start, _start .text +FUNCTION(start) FUNCTION(_start) /* * EFI_SYSTEM_TABLE and EFI_HANDLE are passed in x1/x0. diff --git a/grub-core/kern/backtrace.c b/grub-core/kern/backtrace.c new file mode 100644 index 0000000000..4a82e865cc --- /dev/null +++ b/grub-core/kern/backtrace.c @@ -0,0 +1,97 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static void +grub_backtrace_print_address_default (void *addr) +{ +#ifndef GRUB_UTIL + grub_dl_t mod; + void *start_addr; + + FOR_DL_MODULES (mod) + { + grub_dl_segment_t segment; + for (segment = mod->segment; segment; segment = segment->next) + if (segment->addr <= addr && (grub_uint8_t *) segment->addr + + segment->size > (grub_uint8_t *) addr) + { + grub_printf ("%s.%x+%" PRIxGRUB_SIZE, mod->name, + segment->section, + (grub_size_t) + ((grub_uint8_t *)addr - (grub_uint8_t *)segment->addr)); + return; + } + } + + start_addr = grub_resolve_symbol ("_start"); + if (start_addr && start_addr < addr) + grub_printf ("kernel+%" PRIxGRUB_SIZE, + (grub_size_t) + ((grub_uint8_t *)addr - (grub_uint8_t *)start_addr)); + else +#endif + grub_printf ("%p", addr); +} + +static void +grub_backtrace_pointer_default (void *frame __attribute__((__unused__)), + unsigned int skip __attribute__((__unused__))) +{ + return; +} + +void +grub_backtrace_pointer (void *frame, unsigned int skip) + __attribute__((__weak__, + __alias__(("grub_backtrace_pointer_default")))); + +void +grub_backtrace_print_address (void *addr) + __attribute__((__weak__, + __alias__(("grub_backtrace_print_address_default")))); + +static void +grub_backtrace_arch_default(unsigned int skip) +{ + grub_backtrace_pointer(__builtin_frame_address(0), skip + 1); +} + +void grub_backtrace_arch (unsigned int skip) + __attribute__((__weak__, __alias__(("grub_backtrace_arch_default")))); + +void grub_backtrace (unsigned int skip) +{ + grub_backtrace_arch(skip + 1); +} + +void grub_debug_backtrace (const char * const debug, + unsigned int skip) +{ + if (grub_debug_enabled (debug)) + grub_backtrace (skip + 1); +} diff --git a/grub-core/kern/device.c b/grub-core/kern/device.c index 670e213cf3..d019637470 100644 --- a/grub-core/kern/device.c +++ b/grub-core/kern/device.c @@ -34,6 +34,7 @@ grub_device_open (const char *name) { grub_device_t dev = 0; + grub_dprintf ("device", "opening device %s\n", name); if (! name) { name = grub_env_get ("root"); diff --git a/grub-core/kern/disk.c b/grub-core/kern/disk.c index 1eda58fe9a..5747ad85cc 100644 --- a/grub-core/kern/disk.c +++ b/grub-core/kern/disk.c @@ -28,6 +28,10 @@ #define GRUB_CACHE_TIMEOUT 2 +/* Disk reads may trigger other disk reads. So, limit recursion depth. */ +#define MAX_READ_RECURSION_DEPTH 16 +static unsigned int read_recursion_depth = 0; + /* The last time the disk was used. */ static grub_uint64_t grub_last_time = 0; @@ -285,6 +289,8 @@ grub_disk_open (const char *name) return 0; } + grub_dprintf ("disk", "Opening `%s' succeeded.\n", name); + return disk; } @@ -310,8 +316,10 @@ grub_disk_close (grub_disk_t disk) grub_free (disk->partition); disk->partition = part; } + grub_dprintf ("disk", "Closing `%s' succeeded.\n", disk->name); grub_free ((void *) disk->name); grub_free (disk); + } /* Small read (less than cache size and not pass across cache unit boundaries). @@ -417,6 +425,8 @@ grub_err_t grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, grub_off_t offset, grub_size_t size, void *buf) { + grub_err_t err = GRUB_ERR_NONE; + /* First of all, check if the region is within the disk. */ if (grub_disk_adjust_range (disk, §or, &offset, size) != GRUB_ERR_NONE) { @@ -427,12 +437,17 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, return grub_errno; } + if (++read_recursion_depth >= MAX_READ_RECURSION_DEPTH) + { + grub_error (GRUB_ERR_RECURSION_DEPTH, "grub_disk_read(): Maximum recursion depth exceeded"); + goto error; + } + /* First read until first cache boundary. */ if (offset || (sector & (GRUB_DISK_CACHE_SIZE - 1))) { grub_disk_addr_t start_sector; grub_size_t pos; - grub_err_t err; grub_size_t len; start_sector = sector & ~((grub_disk_addr_t) GRUB_DISK_CACHE_SIZE - 1); @@ -444,7 +459,7 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, err = grub_disk_read_small (disk, start_sector, offset + pos, len, buf); if (err) - return err; + goto error; buf = (char *) buf + len; size -= len; offset += len; @@ -457,7 +472,6 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, { char *data = NULL; grub_disk_addr_t agglomerate; - grub_err_t err; /* agglomerate read until we find a first cached entry. */ for (agglomerate = 0; agglomerate @@ -493,7 +507,7 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, - disk->log_sector_size), buf); if (err) - return err; + goto error; for (i = 0; i < agglomerate; i ++) grub_disk_cache_store (disk->dev->id, disk->id, @@ -527,13 +541,16 @@ grub_disk_read (grub_disk_t disk, grub_disk_addr_t sector, /* And now read the last part. */ if (size) { - grub_err_t err; err = grub_disk_read_small (disk, sector, 0, size, buf); if (err) - return err; + goto error; } - return grub_errno; + err = grub_errno; + + error: + read_recursion_depth--; + return err; } grub_uint64_t diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 0bf40caa61..dc8cb7ec87 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -32,12 +32,22 @@ #include #include #include +#include +#include /* Platforms where modules are in a readonly area of memory. */ #if defined(GRUB_MACHINE_QEMU) #define GRUB_MODULES_MACHINE_READONLY #endif +#ifdef GRUB_MACHINE_EMU +#include +#endif + +#ifdef GRUB_MACHINE_EFI +#include +#endif + #pragma GCC diagnostic ignored "-Wcast-align" @@ -115,6 +125,50 @@ grub_dl_resolve_symbol (const char *name) return 0; } +void * +grub_resolve_symbol (const char *name) +{ + grub_symbol_t sym; + + sym = grub_dl_resolve_symbol (name); + if (sym) + return sym->addr; + return NULL; +} + +const char * +grub_get_symbol_by_addr(const void *addr, int isfunc) +{ + unsigned int i; + grub_symbol_t before = NULL, after = NULL; + for (i = 0; i < GRUB_SYMTAB_SIZE; i++) + { + grub_symbol_t sym; + for (sym = grub_symtab[i]; sym; sym = sym->next) + { + //grub_printf ("addr 0x%08llx symbol %s\n", (unsigned long long)sym->addr, sym->name); + if (sym->addr > addr) + { + if (!after || sym->addr > after->addr) + after = sym; + } + + if (isfunc != sym->isfunc) + continue; + if (sym->addr > addr) + continue; + + if ((!before && sym->addr <= addr) || (before && before->addr <= sym->addr)) + before = sym; + } + } + + if (before && addr < after->addr) + return before->name; + + return NULL; +} + /* Register a symbol with the name NAME and the address ADDR. */ grub_err_t grub_dl_register_symbol (const char *name, void *addr, int isfunc, @@ -224,22 +278,37 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) { unsigned i; const Elf_Shdr *s; - grub_size_t tsize = 0, talign = 1; + grub_size_t tsize = 0, talign = 1, arch_addralign = 1; #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ !defined (__loongarch__) grub_size_t tramp; + grub_size_t tramp_align; grub_size_t got; + grub_size_t got_align; grub_err_t err; #endif char *ptr; + grub_dprintf ("modules", "loading segments for \"%s\"\n", mod->name); + + arch_addralign = grub_arch_dl_min_alignment (); + for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff); i < e->e_shnum; i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize)) { - tsize = ALIGN_UP (tsize, s->sh_addralign) + s->sh_size; - if (talign < s->sh_addralign) - talign = s->sh_addralign; + grub_size_t sh_addralign; + grub_size_t sh_size; + + if (s->sh_size == 0 || !(s->sh_flags & SHF_ALLOC)) + continue; + + sh_addralign = ALIGN_UP(s->sh_addralign, arch_addralign); + sh_size = ALIGN_UP(s->sh_size, sh_addralign); + + tsize = ALIGN_UP (tsize, sh_addralign) + sh_size; + if (talign < sh_addralign) + talign = sh_addralign; } #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ @@ -247,12 +316,18 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) err = grub_arch_dl_get_tramp_got_size (e, &tramp, &got); if (err) return err; - tsize += ALIGN_UP (tramp, GRUB_ARCH_DL_TRAMP_ALIGN); - if (talign < GRUB_ARCH_DL_TRAMP_ALIGN) - talign = GRUB_ARCH_DL_TRAMP_ALIGN; - tsize += ALIGN_UP (got, GRUB_ARCH_DL_GOT_ALIGN); - if (talign < GRUB_ARCH_DL_GOT_ALIGN) - talign = GRUB_ARCH_DL_GOT_ALIGN; + tramp_align = GRUB_ARCH_DL_TRAMP_ALIGN; + if (tramp_align < arch_addralign) + tramp_align = arch_addralign; + tsize += ALIGN_UP (tramp, tramp_align); + if (talign < tramp_align) + talign = tramp_align; + got_align = GRUB_ARCH_DL_GOT_ALIGN; + if (got_align < arch_addralign) + got_align = arch_addralign; + tsize += ALIGN_UP (got, got_align); + if (talign < got_align) + talign = got_align; #endif #ifdef GRUB_MACHINE_EMU @@ -269,6 +344,9 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) i < e->e_shnum; i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize)) { + grub_size_t sh_addralign = ALIGN_UP(s->sh_addralign, arch_addralign); + grub_size_t sh_size = ALIGN_UP(s->sh_size, sh_addralign); + if (s->sh_flags & SHF_ALLOC) { grub_dl_segment_t seg; @@ -281,17 +359,19 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) { void *addr; - ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, s->sh_addralign); + ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, sh_addralign); addr = ptr; - ptr += s->sh_size; + ptr += sh_size; switch (s->sh_type) { case SHT_PROGBITS: grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size); + grub_memset ((char *)addr + s->sh_size, 0, + sh_size - s->sh_size); break; case SHT_NOBITS: - grub_memset (addr, 0, s->sh_size); + grub_memset (addr, 0, sh_size); break; } @@ -300,7 +380,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) else seg->addr = 0; - seg->size = s->sh_size; + seg->size = sh_size; seg->section = i; seg->next = mod->segment; mod->segment = seg; @@ -308,16 +388,17 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e) } #if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ !defined (__loongarch__) - ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_TRAMP_ALIGN); + ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, tramp_align); mod->tramp = ptr; mod->trampptr = ptr; ptr += tramp; - ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, GRUB_ARCH_DL_GOT_ALIGN); + ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, got_align); mod->got = ptr; mod->gotptr = ptr; ptr += got; #endif + grub_dprintf ("modules", "done loading segments for \"%s\"\n", mod->name); return GRUB_ERR_NONE; } @@ -330,6 +411,7 @@ grub_dl_resolve_symbols (grub_dl_t mod, Elf_Ehdr *e) const char *str; Elf_Word size, entsize; + grub_dprintf ("modules", "Resolving symbols for \"%s\"\n", mod->name); for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); i < e->e_shnum; i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) @@ -532,7 +614,7 @@ grub_dl_resolve_dependencies (grub_dl_t mod, Elf_Ehdr *e) return GRUB_ERR_NONE; } -int +grub_uint64_t grub_dl_ref (grub_dl_t mod) { grub_dl_dep_t dep; @@ -543,10 +625,13 @@ grub_dl_ref (grub_dl_t mod) for (dep = mod->dep; dep; dep = dep->next) grub_dl_ref (dep->mod); - return ++mod->ref_count; + if (grub_add (mod->ref_count, 1, &mod->ref_count)) + grub_fatal ("Module reference count overflow"); + + return mod->ref_count; } -int +grub_uint64_t grub_dl_unref (grub_dl_t mod) { grub_dl_dep_t dep; @@ -557,10 +642,13 @@ grub_dl_unref (grub_dl_t mod) for (dep = mod->dep; dep; dep = dep->next) grub_dl_unref (dep->mod); - return --mod->ref_count; + if (grub_sub (mod->ref_count, 1, &mod->ref_count)) + grub_fatal ("Module reference count underflow"); + + return mod->ref_count; } -int +grub_uint64_t grub_dl_ref_count (grub_dl_t mod) { if (mod == NULL) @@ -584,6 +672,7 @@ grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr) Elf_Shdr *s; unsigned i; + grub_dprintf ("modules", "relocating symbols for \"%s\"\n", mod->name); for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); i < e->e_shnum; i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) @@ -592,22 +681,139 @@ grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr) grub_dl_segment_t seg; grub_err_t err; - /* Find the target segment. */ - for (seg = mod->segment; seg; seg = seg->next) - if (seg->section == s->sh_info) - break; + if (!(s->sh_flags & SHF_INFO_LINK)) + continue; - if (seg) - { - if (!mod->symtab) - return grub_error (GRUB_ERR_BAD_MODULE, "relocation without symbol table"); + seg = grub_dl_find_segment(mod, s->sh_info); + if (!seg) + continue; - err = grub_arch_dl_relocate_symbols (mod, ehdr, s, seg); - if (err) - return err; - } + if (!mod->symtab) + return grub_error (GRUB_ERR_BAD_MODULE, "relocation without symbol table"); + + err = grub_arch_dl_relocate_symbols (mod, ehdr, s, seg); + if (err) + return err; } + grub_dprintf ("modules", "done relocating symbols for \"%s\"\n", mod->name); + return GRUB_ERR_NONE; +} + +static void +grub_dl_print_gdb_info (grub_dl_t mod, Elf_Ehdr *e) +{ + void *text, *data = NULL; + long idx; + + idx = grub_dl_find_section_index (e, ".text"); + if (idx < 0) + return; + + text = grub_dl_get_section_addr (mod, idx); + if (!text) + return; + + idx = grub_dl_find_section_index (e, ".data"); + if (idx >= 0) + data = grub_dl_get_section_addr (mod, idx); + + if (data) + grub_qdprintf ("gdb", "add-symbol-file \\\n" + "/usr/lib/debug/usr/lib/grub/%s-%s/%s.debug " + "\\\n %p -s .data %p\n", + GRUB_TARGET_CPU, GRUB_PLATFORM, + mod->name, text, data); + else + grub_qdprintf ("gdb", "add-symbol-file \\\n" + "/usr/lib/debug/usr/lib/grub/%s-%s/%s.debug " + "\\\n%p\n", + GRUB_TARGET_CPU, GRUB_PLATFORM, + mod->name, text); +} + +static grub_err_t +grub_dl_set_mem_attrs (grub_dl_t mod, void *ehdr) +{ + unsigned i; + const Elf_Shdr *s; + const Elf_Ehdr *e = ehdr; +#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ + !defined(__loongarch__) + grub_size_t arch_addralign = grub_arch_dl_min_alignment (); + grub_addr_t tgaddr; + grub_size_t tgsz; +#endif + + grub_dprintf ("modules", "updating memory attributes for \"%s\"\n", + mod->name); + for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff); + i < e->e_shnum; + i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize)) + { + grub_dl_segment_t seg; + grub_uint64_t set_attrs = GRUB_MEM_ATTR_R; + grub_uint64_t clear_attrs = GRUB_MEM_ATTR_W|GRUB_MEM_ATTR_X; + + seg = grub_dl_find_segment(mod, i); + if (!seg) + continue; + + if (seg->size == 0 || !(s->sh_flags & SHF_ALLOC)) + continue; + + if (s->sh_flags & SHF_WRITE) + { + set_attrs |= GRUB_MEM_ATTR_W; + clear_attrs &= ~GRUB_MEM_ATTR_W; + } + + if (s->sh_flags & SHF_EXECINSTR) + { + set_attrs |= GRUB_MEM_ATTR_X; + clear_attrs &= ~GRUB_MEM_ATTR_X; + } + + grub_dprintf ("modules", "setting memory attrs for section \"%s\" to -%s%s%s+%s%s%s\n", + grub_dl_get_section_name(e, s), + (clear_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (clear_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (clear_attrs & GRUB_MEM_ATTR_X) ? "x" : "", + (set_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (set_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (set_attrs & GRUB_MEM_ATTR_X) ? "x" : ""); + grub_update_mem_attrs ((grub_addr_t)(seg->addr), seg->size, set_attrs, clear_attrs); + } + +#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \ + !defined(__loongarch__) + tgaddr = grub_min((grub_addr_t)mod->tramp, (grub_addr_t)mod->got); + tgsz = grub_max((grub_addr_t)mod->trampptr, (grub_addr_t)mod->gotptr) - tgaddr; + + if (tgsz) + { + tgsz = ALIGN_UP(tgsz, arch_addralign); + + grub_dprintf ("modules", + "updating attributes for GOT and trampolines (\"%s\")\n", + mod->name); + if (tgaddr < (grub_addr_t)mod->base || + tgsz > (grub_addr_t)-1 - tgaddr || + tgaddr + tgsz > (grub_addr_t)mod->base + mod->sz) + return grub_error (GRUB_ERR_BUG, + "BUG: trying to protect pages outside of module " + "allocation (\"%s\"): module base %p, size 0x%" + PRIxGRUB_SIZE "; tramp/GOT base 0x%" PRIxGRUB_ADDR + ", size 0x%" PRIxGRUB_SIZE, + mod->name, mod->base, mod->sz, tgaddr, tgsz); + grub_update_mem_attrs (tgaddr, tgsz, GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_X, + GRUB_MEM_ATTR_W); + } +#endif + + grub_dprintf ("modules", "done updating module memory attributes for \"%s\"\n", + mod->name); + return GRUB_ERR_NONE; } @@ -644,6 +850,7 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size) mod->ref_count = 1; grub_dprintf ("modules", "relocating to %p\n", mod); + /* Me, Vladimir Serbinenko, hereby I add this module check as per new GNU module policy. Note that this license check is informative only. Modules have to be licensed under GPLv3 or GPLv3+ (optionally @@ -657,7 +864,8 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size) || grub_dl_resolve_dependencies (mod, e) || grub_dl_load_segments (mod, e) || grub_dl_resolve_symbols (mod, e) - || grub_dl_relocate_symbols (mod, e)) + || grub_dl_relocate_symbols (mod, e) + || grub_dl_set_mem_attrs (mod, e)) { mod->fini = 0; grub_dl_unload (mod); @@ -669,6 +877,8 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size) grub_dprintf ("modules", "module name: %s\n", mod->name); grub_dprintf ("modules", "init function: %p\n", mod->init); + grub_dl_print_gdb_info (mod, e); + if (grub_dl_add (mod)) { grub_dl_unload (mod); @@ -706,6 +916,19 @@ grub_dl_load_file (const char *filename) void *core = 0; grub_dl_t mod = 0; +#ifdef GRUB_MACHINE_EFI + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + { +#if 0 + /* This is an error, but grub2-mkconfig still generates a pile of + * insmod commands, so emitting it would be mostly just obnoxious. */ + grub_error (GRUB_ERR_ACCESS_DENIED, + "Secure Boot forbids loading module from %s", filename); +#endif + return 0; + } +#endif + grub_boot_time ("Loading module %s", filename); file = grub_file_open (filename, GRUB_FILE_TYPE_GRUB_MODULE); diff --git a/grub-core/kern/efi/debug.c b/grub-core/kern/efi/debug.c index 5d2ab1a36f..5ac194fc8f 100644 --- a/grub-core/kern/efi/debug.c +++ b/grub-core/kern/efi/debug.c @@ -26,7 +26,7 @@ grub_cmd_gdbinfo (struct grub_command *cmd __attribute__ ((unused)), int argc __attribute__ ((unused)), char **args __attribute__ ((unused))) { - grub_efi_print_gdb_info (); + grub_efi_print_gdb_info (false); return 0; } diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index b93ae3aba1..b5b8a32962 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -96,6 +96,19 @@ grub_efi_locate_handle (grub_efi_locate_search_type_t search_type, return buffer; } +grub_efi_status_t +grub_efi_connect_controller (grub_efi_handle_t controller_handle, + grub_efi_handle_t *driver_image_handle, + grub_efi_device_path_protocol_t *remaining_device_path, + grub_efi_boolean_t recursive) +{ + grub_efi_boot_services_t *b; + + b = grub_efi_system_table->boot_services; + return b->connect_controller(controller_handle, + driver_image_handle, remaining_device_path, recursive); +} + void * grub_efi_open_protocol (grub_efi_handle_t handle, grub_guid_t *protocol, @@ -175,11 +188,16 @@ grub_reboot (void) } void -grub_exit (void) +grub_exit (int retval) { + grub_efi_status_t rc = GRUB_EFI_LOAD_ERROR; + + if (retval == 0) + rc = GRUB_EFI_SUCCESS; + grub_machine_fini (GRUB_LOADER_FLAG_NORETURN); grub_efi_system_table->boot_services->exit (grub_efi_image_handle, - GRUB_EFI_SUCCESS, 0, 0); + rc, 0, 0); for (;;) ; } @@ -222,6 +240,9 @@ grub_efi_set_variable_with_attributes (const char *var, const grub_guid_t *guid, if (status == GRUB_EFI_SUCCESS) return GRUB_ERR_NONE; + if (status == GRUB_EFI_NOT_FOUND && datasize == 0) + return GRUB_ERR_NONE; + return grub_error (GRUB_ERR_IO, "could not set EFI variable `%s'", var); } @@ -770,7 +791,7 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp) { grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) dp; - grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x)", + grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x", (unsigned) ipv4->local_ip_address[0], (unsigned) ipv4->local_ip_address[1], (unsigned) ipv4->local_ip_address[2], @@ -783,33 +804,60 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp) (unsigned) ipv4->remote_port, (unsigned) ipv4->protocol, (unsigned) ipv4->static_ip_address); + if (len == sizeof (*ipv4)) + { + grub_printf (",%u.%u.%u.%u,%u.%u.%u.%u", + (unsigned) ipv4->gateway_ip_address[0], + (unsigned) ipv4->gateway_ip_address[1], + (unsigned) ipv4->gateway_ip_address[2], + (unsigned) ipv4->gateway_ip_address[3], + (unsigned) ipv4->subnet_mask[0], + (unsigned) ipv4->subnet_mask[1], + (unsigned) ipv4->subnet_mask[2], + (unsigned) ipv4->subnet_mask[3]); + } + grub_printf (")"); } break; case GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE: { grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) dp; - grub_printf ("/IPv6(%x:%x:%x:%x:%x:%x:%x:%x,%x:%x:%x:%x:%x:%x:%x:%x,%u,%u,%x,%x)", - (unsigned) ipv6->local_ip_address[0], - (unsigned) ipv6->local_ip_address[1], - (unsigned) ipv6->local_ip_address[2], - (unsigned) ipv6->local_ip_address[3], - (unsigned) ipv6->local_ip_address[4], - (unsigned) ipv6->local_ip_address[5], - (unsigned) ipv6->local_ip_address[6], - (unsigned) ipv6->local_ip_address[7], - (unsigned) ipv6->remote_ip_address[0], - (unsigned) ipv6->remote_ip_address[1], - (unsigned) ipv6->remote_ip_address[2], - (unsigned) ipv6->remote_ip_address[3], - (unsigned) ipv6->remote_ip_address[4], - (unsigned) ipv6->remote_ip_address[5], - (unsigned) ipv6->remote_ip_address[6], - (unsigned) ipv6->remote_ip_address[7], + grub_printf ("/IPv6(%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,%u,%u,%x,%x", + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[0]), + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[1]), + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[2]), + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[3]), + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[4]), + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[5]), + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[6]), + (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[7]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[0]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[1]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[2]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[3]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[4]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[5]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[6]), + (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[7]), (unsigned) ipv6->local_port, (unsigned) ipv6->remote_port, (unsigned) ipv6->protocol, (unsigned) ipv6->static_ip_address); + if (len == sizeof (*ipv6)) + { + grub_printf (",%u,%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + (unsigned) ipv6->prefix_length, + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[0]), + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[1]), + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[2]), + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[3]), + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[4]), + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[5]), + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[6]), + (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[7])); + } + grub_printf (")"); } break; case GRUB_EFI_INFINIBAND_DEVICE_PATH_SUBTYPE: @@ -856,6 +904,39 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp) dump_vendor_path ("Messaging", (grub_efi_vendor_device_path_t *) dp); break; + case GRUB_EFI_URI_DEVICE_PATH_SUBTYPE: + { + grub_efi_uri_device_path_t *uri + = (grub_efi_uri_device_path_t *) dp; + grub_printf ("/URI(%s)", uri->uri); + } + break; + case GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE: + { + grub_efi_dns_device_path_t *dns + = (grub_efi_dns_device_path_t *) dp; + if (dns->is_ipv6) + { + grub_printf ("/DNS(%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x)", + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[0]) >> 16), + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[0])), + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[1]) >> 16), + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[1])), + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[2]) >> 16), + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[2])), + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[3]) >> 16), + (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[3]))); + } + else + { + grub_printf ("/DNS(%d.%d.%d.%d)", + dns->dns_server_ip[0].v4.addr[0], + dns->dns_server_ip[0].v4.addr[1], + dns->dns_server_ip[0].v4.addr[2], + dns->dns_server_ip[0].v4.addr[3]); + } + } + break; default: grub_printf ("/UnknownMessaging(%x)", (unsigned) subtype); break; @@ -1049,3 +1130,96 @@ grub_efi_find_configuration_table (const grub_guid_t *target_guid) return 0; } + +static const grub_efi_loader_t *override_loader = NULL; + +grub_err_t +grub_efi_register_loader (const grub_efi_loader_t *loader) +{ + if (override_loader != NULL) + return grub_error (GRUB_ERR_BUG, "trying to register different loader"); + override_loader = loader; + return GRUB_ERR_NONE; +} + +grub_err_t +grub_efi_unregister_loader (const grub_efi_loader_t *loader) +{ + if (loader != override_loader) + return grub_error (GRUB_ERR_BUG, "trying to unregister different loader"); + + override_loader = NULL; + return GRUB_ERR_NONE; +} + +grub_efi_status_t +grub_efi_load_image (grub_efi_boolean_t boot_policy, + grub_efi_handle_t parent_image_handle, + grub_efi_device_path_t *file_path, void *source_buffer, + grub_efi_uintn_t source_size, + grub_efi_handle_t *image_handle) +{ + if (override_loader != NULL) + return override_loader->load_image (boot_policy, parent_image_handle, + file_path, source_buffer, source_size, + image_handle); + return grub_efi_system_table->boot_services->load_image ( + boot_policy, parent_image_handle, file_path, source_buffer, source_size, + image_handle); +} + +grub_efi_status_t +grub_efi_start_image (grub_efi_handle_t image_handle, + grub_efi_uintn_t *exit_data_size, + grub_efi_char16_t **exit_data) +{ + if (override_loader != NULL) + return override_loader->start_image (image_handle, exit_data_size, + exit_data); + return grub_efi_system_table->boot_services->start_image ( + image_handle, exit_data_size, exit_data); +} + +grub_efi_status_t +grub_efi_unload_image (grub_efi_handle_t image_handle) +{ + if (override_loader != NULL) + return override_loader->unload_image (image_handle); + return grub_efi_system_table->boot_services->unload_image (image_handle); +} + +grub_err_t +grub_efi_status_to_err (grub_efi_status_t status) +{ + grub_err_t err; + switch (status) + { + case GRUB_EFI_SUCCESS: + err = GRUB_ERR_NONE; + break; + case GRUB_EFI_INVALID_PARAMETER: + default: + err = GRUB_ERR_BAD_ARGUMENT; + break; + case GRUB_EFI_OUT_OF_RESOURCES: + err = GRUB_ERR_OUT_OF_MEMORY; + break; + case GRUB_EFI_DEVICE_ERROR: + err = GRUB_ERR_IO; + break; + case GRUB_EFI_WRITE_PROTECTED: + err = GRUB_ERR_WRITE_ERROR; + break; + case GRUB_EFI_SECURITY_VIOLATION: + err = GRUB_ERR_ACCESS_DENIED; + break; + case GRUB_EFI_NOT_FOUND: + err = GRUB_ERR_FILE_NOT_FOUND; + break; + case GRUB_EFI_ABORTED: + err = GRUB_ERR_WAIT; + break; + } + + return err; +} diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c index 6c54af6e79..ce8a7fe712 100644 --- a/grub-core/kern/efi/init.c +++ b/grub-core/kern/efi/init.c @@ -28,8 +28,11 @@ #include #include #include + #include +#include + #ifdef GRUB_STACK_PROTECTOR static grub_efi_char16_t stack_chk_fail_msg[] = @@ -103,6 +106,31 @@ stack_protector_init (void) grub_addr_t grub_modbase; +/* Helper for grub_efi_env_init */ +static int +set_var (const char *name, const char *value, + void *whitelist __attribute__((__unused__))) +{ + grub_env_set (name, value); + return 0; +} + +static void +grub_efi_env_init (void) +{ + grub_guid_t efi_grub_guid = GRUB_EFI_GRUB_VARIABLE_GUID; + struct grub_envblk envblk_s = { NULL, 0 }; + grub_envblk_t envblk = &envblk_s; + + grub_efi_get_variable ("GRUB_ENV", &efi_grub_guid, &envblk_s.size, + (void **) &envblk_s.buf); + if (!envblk_s.buf || envblk_s.size < 1) + return; + + grub_envblk_iterate (envblk, NULL, set_var); + grub_free (envblk_s.buf); +} + __attribute__ ((__optimize__ ("-fno-stack-protector"))) void grub_efi_init (void) { @@ -128,6 +156,8 @@ grub_efi_init (void) grub_efi_system_table->boot_services->set_watchdog_timer (0, 0, 0, NULL); + grub_efi_env_init (); + grub_efi_print_gdb_info (true); grub_efidisk_init (); grub_efi_register_debug_commands (); diff --git a/grub-core/kern/efi/mm.c b/grub-core/kern/efi/mm.c index 6a6fba8918..464fe1c3c0 100644 --- a/grub-core/kern/efi/mm.c +++ b/grub-core/kern/efi/mm.c @@ -112,6 +112,38 @@ grub_efi_drop_alloc (grub_efi_physical_address_t address, } } +/* Allocate pages below a specified address */ +void * +grub_efi_allocate_pages_max (grub_efi_physical_address_t max, + grub_efi_uintn_t pages) +{ + grub_efi_status_t status; + grub_efi_boot_services_t *b; + grub_efi_physical_address_t address = max; + + if (max > GRUB_EFI_MAX_USABLE_ADDRESS) + return 0; + + b = grub_efi_system_table->boot_services; + status = b->allocate_pages (GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address); + + if (status != GRUB_EFI_SUCCESS) + return 0; + + if (address == 0) + { + /* Uggh, the address 0 was allocated... This is too annoying, + so reallocate another one. */ + address = max; + status = b->allocate_pages (GRUB_EFI_ALLOCATE_MAX_ADDRESS, GRUB_EFI_LOADER_DATA, pages, &address); + grub_efi_free_pages (0, pages); + if (status != GRUB_EFI_SUCCESS) + return 0; + } + + return (void *) ((grub_addr_t) address); +} + /* Allocate pages. Return the pointer to the first of allocated pages. */ void * grub_efi_allocate_pages_real (grub_efi_physical_address_t address, @@ -121,8 +153,9 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address, { grub_efi_status_t status; grub_efi_boot_services_t *b; + grub_efi_physical_address_t ret = address; - /* Limit the memory access to less than 4GB for 32-bit platforms. */ +/* Limit the memory access to less than 4GB for 32-bit platforms. */ if (address > GRUB_EFI_MAX_USABLE_ADDRESS) { char inv_addr[17], max_addr[17]; /* log16(2^64) = 16, plus NUL. */ @@ -137,19 +170,22 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address, } b = grub_efi_system_table->boot_services; - status = b->allocate_pages (alloctype, memtype, pages, &address); + status = b->allocate_pages (alloctype, memtype, pages, &ret); if (status != GRUB_EFI_SUCCESS) { + grub_dprintf ("efi", + "allocate_pages(%d, %d, 0x%0lx, 0x%016lx) = 0x%016lx\n", + alloctype, memtype, pages, address, status); grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); return NULL; } - if (address == 0) + if (ret == 0) { /* Uggh, the address 0 was allocated... This is too annoying, so reallocate another one. */ - address = GRUB_EFI_MAX_USABLE_ADDRESS; - status = b->allocate_pages (alloctype, memtype, pages, &address); + ret = address; + status = b->allocate_pages (alloctype, memtype, pages, &ret); grub_efi_free_pages (0, pages); if (status != GRUB_EFI_SUCCESS) { @@ -158,9 +194,9 @@ grub_efi_allocate_pages_real (grub_efi_physical_address_t address, } } - grub_efi_store_alloc (address, pages); + grub_efi_store_alloc (ret, pages); - return (void *) ((grub_addr_t) address); + return (void *) ((grub_addr_t) ret); } void * @@ -445,7 +481,7 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map, { if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY #if 1 - && desc->physical_start <= GRUB_EFI_MAX_USABLE_ADDRESS + && desc->physical_start <= GRUB_EFI_MAX_ALLOCATION_ADDRESS #endif && desc->physical_start + PAGES_TO_BYTES (desc->num_pages) > 0x100000 && desc->num_pages != 0) @@ -463,9 +499,9 @@ filter_memory_map (grub_efi_memory_descriptor_t *memory_map, #if 1 if (BYTES_TO_PAGES (filtered_desc->physical_start) + filtered_desc->num_pages - > BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS)) + > BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_ALLOCATION_ADDRESS)) filtered_desc->num_pages - = (BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_USABLE_ADDRESS) + = (BYTES_TO_PAGES_DOWN (GRUB_EFI_MAX_ALLOCATION_ADDRESS) - BYTES_TO_PAGES (filtered_desc->physical_start)); #endif @@ -567,6 +603,83 @@ print_memory_map (grub_efi_memory_descriptor_t *memory_map, } #endif +grub_addr_t grub_stack_addr = (grub_addr_t)-1ll; +grub_size_t grub_stack_size = 0; + +static void +grub_nx_init (void) +{ + grub_uint64_t attrs, stack_attrs; + grub_err_t err; + grub_addr_t stack_current, stack_end; + const grub_uint64_t page_size = 4096; + const grub_uint64_t page_mask = ~(page_size - 1); + + /* + * These are to confirm that the flags are working as expected when + * debugging. + */ + attrs = 0; + stack_current = (grub_addr_t)grub_nx_init & page_mask; + err = grub_get_mem_attrs (stack_current, page_size, &attrs); + if (err) + { + grub_dprintf ("nx", + "grub_get_mem_attrs(0x%"PRIxGRUB_UINT64_T", ...) -> 0x%x\n", + stack_current, err); + grub_error_pop (); + } + else + grub_dprintf ("nx", "page attrs for grub_nx_init (%p) are %c%c%c\n", + grub_dl_load_core, + (attrs & GRUB_MEM_ATTR_R) ? 'r' : '-', + (attrs & GRUB_MEM_ATTR_R) ? 'w' : '-', + (attrs & GRUB_MEM_ATTR_R) ? 'x' : '-'); + + stack_current = (grub_addr_t)&stack_current & page_mask; + err = grub_get_mem_attrs (stack_current, page_size, &stack_attrs); + if (err) + { + grub_dprintf ("nx", + "grub_get_mem_attrs(0x%"PRIxGRUB_UINT64_T", ...) -> 0x%x\n", + stack_current, err); + grub_error_pop (); + } + else + { + attrs = stack_attrs; + grub_dprintf ("nx", "page attrs for stack (%p) are %c%c%c\n", + &attrs, + (attrs & GRUB_MEM_ATTR_R) ? 'r' : '-', + (attrs & GRUB_MEM_ATTR_R) ? 'w' : '-', + (attrs & GRUB_MEM_ATTR_R) ? 'x' : '-'); + } + for (stack_end = stack_current + page_size ; + !(attrs & GRUB_MEM_ATTR_R); + stack_end += page_size) + { + err = grub_get_mem_attrs (stack_current, page_size, &attrs); + if (err) + { + grub_dprintf ("nx", + "grub_get_mem_attrs(0x%"PRIxGRUB_UINT64_T", ...) -> 0x%x\n", + stack_current, err); + grub_error_pop (); + break; + } + } + if (stack_end > stack_current) + { + grub_stack_addr = stack_current; + grub_stack_size = stack_end - stack_current; + grub_dprintf ("nx", + "detected stack from 0x%"PRIxGRUB_ADDR" to 0x%"PRIxGRUB_ADDR"\n", + grub_stack_addr, grub_stack_addr + grub_stack_size - 1); + } +} + + + static grub_err_t grub_efi_mm_add_regions (grub_size_t required_bytes, unsigned int flags) { @@ -579,6 +692,8 @@ grub_efi_mm_add_regions (grub_size_t required_bytes, unsigned int flags) grub_err_t err; int mm_status; + grub_nx_init (); + /* Prepare a memory region to store two memory maps. */ memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE)); if (! memory_map) @@ -679,11 +794,155 @@ grub_efi_get_ram_base(grub_addr_t *base_addr) for (desc = memory_map, *base_addr = GRUB_EFI_MAX_USABLE_ADDRESS; (grub_addr_t) desc < ((grub_addr_t) memory_map + memory_map_size); desc = NEXT_MEMORY_DESCRIPTOR (desc, desc_size)) - if (desc->attribute & GRUB_EFI_MEMORY_WB) - *base_addr = grub_min (*base_addr, desc->physical_start); + { + if (desc->type == GRUB_EFI_CONVENTIONAL_MEMORY && + (desc->attribute & GRUB_EFI_MEMORY_WB)) + { + *base_addr = grub_min (*base_addr, desc->physical_start); + grub_dprintf ("efi", "setting base_addr=0x%016lx\n", *base_addr); + } + else + { + grub_dprintf ("efi", "ignoring address 0x%016lx\n", desc->physical_start); + } + } + + if (*base_addr == GRUB_EFI_MAX_USABLE_ADDRESS) + grub_dprintf ("efi", "base_addr 0x%016lx is probably wrong.\n", *base_addr); grub_free(memory_map); return GRUB_ERR_NONE; } #endif + +static inline grub_uint64_t +grub_mem_attrs_to_uefi_mem_attrs (grub_uint64_t attrs) +{ + grub_uint64_t ret = GRUB_EFI_MEMORY_RP | + GRUB_EFI_MEMORY_RO | + GRUB_EFI_MEMORY_XP; + + if (attrs & GRUB_MEM_ATTR_R) + ret &= ~GRUB_EFI_MEMORY_RP; + + if (attrs & GRUB_MEM_ATTR_W) + ret &= ~GRUB_EFI_MEMORY_RO; + + if (attrs & GRUB_MEM_ATTR_X) + ret &= ~GRUB_EFI_MEMORY_XP; + + return ret; +} + +static inline grub_uint64_t +uefi_mem_attrs_to_grub_mem_attrs (grub_uint64_t attrs) +{ + grub_uint64_t ret = GRUB_MEM_ATTR_R | + GRUB_MEM_ATTR_W | + GRUB_MEM_ATTR_X; + + if (attrs & GRUB_EFI_MEMORY_RP) + ret &= ~GRUB_MEM_ATTR_R; + + if (attrs & GRUB_EFI_MEMORY_RO) + ret &= ~GRUB_MEM_ATTR_W; + + if (attrs & GRUB_EFI_MEMORY_XP) + ret &= ~GRUB_MEM_ATTR_X; + + return ret; +} + +grub_err_t +grub_get_mem_attrs (grub_addr_t addr, grub_size_t size, grub_uint64_t *attrs) +{ + grub_efi_memory_attribute_protocol_t *proto; + grub_efi_physical_address_t physaddr = addr; + grub_guid_t protocol_guid = GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; + grub_efi_status_t efi_status; + + proto = grub_efi_locate_protocol (&protocol_guid, 0); + if (!proto) + return GRUB_ERR_NOT_IMPLEMENTED_YET; + + if (physaddr & 0xfff || size & 0xfff || size == 0 || attrs == NULL) + { + grub_dprintf ("nx", "%s called on 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" and attrs %p\n", + __func__, physaddr, physaddr+size-1, attrs); + return 0; + } + + efi_status = proto->get_memory_attributes( + proto, physaddr, size, attrs); + *attrs = uefi_mem_attrs_to_grub_mem_attrs (*attrs); + + return grub_efi_status_to_err (efi_status); +} + +grub_err_t +grub_update_mem_attrs (grub_addr_t addr, grub_size_t size, + grub_uint64_t set_attrs, grub_uint64_t clear_attrs) +{ + grub_efi_memory_attribute_protocol_t *proto; + grub_efi_physical_address_t physaddr = addr; + grub_guid_t protocol_guid = GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; + grub_efi_status_t efi_status = GRUB_EFI_SUCCESS; + grub_uint64_t before = 0, after = 0, uefi_set_attrs, uefi_clear_attrs; + grub_err_t err; + + proto = grub_efi_locate_protocol (&protocol_guid, 0); + if (!proto) + return GRUB_ERR_NONE; + + err = grub_get_mem_attrs (addr, size, &before); + if (err) + grub_dprintf ("nx", "grub_get_mem_attrs(0x%"PRIxGRUB_ADDR", %"PRIuGRUB_SIZE", %p) -> 0x%x\n", + addr, size, &before, err); + + if (physaddr & 0xfff || size & 0xfff || size == 0) + { + grub_dprintf ("nx", "%s called on 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" +%s%s%s -%s%s%s\n", + __func__, physaddr, physaddr + size - 1, + (set_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (set_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (set_attrs & GRUB_MEM_ATTR_X) ? "x" : "", + (clear_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (clear_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (clear_attrs & GRUB_MEM_ATTR_X) ? "x" : ""); + return 0; + } + + uefi_set_attrs = grub_mem_attrs_to_uefi_mem_attrs (set_attrs); + grub_dprintf ("nx", "translating set_attrs from 0x%lx to 0x%lx\n", set_attrs, uefi_set_attrs); + uefi_clear_attrs = grub_mem_attrs_to_uefi_mem_attrs (clear_attrs); + grub_dprintf ("nx", "translating clear_attrs from 0x%lx to 0x%lx\n", clear_attrs, uefi_clear_attrs); + if (uefi_set_attrs) + efi_status = proto->set_memory_attributes( + proto, physaddr, size, uefi_set_attrs); + if (efi_status == GRUB_EFI_SUCCESS && uefi_clear_attrs) + efi_status = proto->clear_memory_attributes( + proto, physaddr, size, uefi_clear_attrs); + + err = grub_get_mem_attrs (addr, size, &after); + if (err) + grub_dprintf ("nx", "grub_get_mem_attrs(0x%"PRIxGRUB_ADDR", %"PRIuGRUB_SIZE", %p) -> 0x%x\n", + addr, size, &after, err); + + grub_dprintf ("nx", "set +%s%s%s -%s%s%s on 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" before:%c%c%c after:%c%c%c\n", + (set_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (set_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (set_attrs & GRUB_MEM_ATTR_X) ? "x" : "", + (clear_attrs & GRUB_MEM_ATTR_R) ? "r" : "", + (clear_attrs & GRUB_MEM_ATTR_W) ? "w" : "", + (clear_attrs & GRUB_MEM_ATTR_X) ? "x" : "", + addr, addr + size - 1, + (before & GRUB_MEM_ATTR_R) ? 'r' : '-', + (before & GRUB_MEM_ATTR_W) ? 'w' : '-', + (before & GRUB_MEM_ATTR_X) ? 'x' : '-', + (after & GRUB_MEM_ATTR_R) ? 'r' : '-', + (after & GRUB_MEM_ATTR_W) ? 'w' : '-', + (after & GRUB_MEM_ATTR_X) ? 'x' : '-'); + + return grub_efi_status_to_err (efi_status); +} diff --git a/grub-core/kern/emu/full.c b/grub-core/kern/emu/full.c index e8d63b1f5f..1de1c28eb0 100644 --- a/grub-core/kern/emu/full.c +++ b/grub-core/kern/emu/full.c @@ -67,3 +67,16 @@ grub_arch_dl_init_linker (void) } #endif + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ +#ifdef GRUB_MACHINE_EFI + return 4096; +#else + return 1; +#endif +} diff --git a/grub-core/kern/emu/main.c b/grub-core/kern/emu/main.c index 855b11c3de..61b3181775 100644 --- a/grub-core/kern/emu/main.c +++ b/grub-core/kern/emu/main.c @@ -67,7 +67,7 @@ grub_reboot (void) } void -grub_exit (void) +grub_exit (int retval __attribute__((unused))) { grub_reboot (); } @@ -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}, + {"switch-root", 'W', 0, 0, N_("use switch-root to only switch root filesystem without restarting the kernel."), 0}, { 0, 0, 0, 0, 0, 0 } }; @@ -168,7 +169,9 @@ argp_parser (int key, char *arg, struct argp_state *state) case 'X': grub_util_set_kexecute (); break; - + case 'W': + grub_util_set_switch_root (); + break; case ARGP_KEY_ARG: { /* Too many arguments. */ diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c index 521220b49d..7b9731b7ef 100644 --- a/grub-core/kern/emu/misc.c +++ b/grub-core/kern/emu/misc.c @@ -40,6 +40,7 @@ int verbosity; int kexecute; +int switchroot = 0; void grub_util_warn (const char *fmt, ...) @@ -83,7 +84,7 @@ grub_util_error (const char *fmt, ...) vfprintf (stderr, fmt, ap); va_end (ap); fprintf (stderr, ".\n"); - grub_exit (); + grub_exit (1); } void * @@ -152,12 +153,13 @@ xasprintf (const char *fmt, ...) #if !defined (GRUB_MACHINE_EMU) || defined (GRUB_UTIL) void -grub_exit (void) +__attribute__ ((noreturn)) +grub_exit (int rc) { -#if defined (GRUB_KERNEL) +#if defined (GRUB_KERNEL) && !defined (GRUB_MACHINE_EFI) grub_reboot (); #endif - exit (1); + exit (rc < 0 ? 1 : rc); } #endif @@ -188,7 +190,7 @@ grub_util_get_image_size (const char *path) sz = ftello (f); if (sz < 0) grub_util_error (_("cannot open `%s': %s"), path, strerror (errno)); - if (sz != (size_t) sz) + if (sz > (off_t)(GRUB_SIZE_MAX >> 1)) grub_util_error (_("file `%s' is too big"), path); ret = (size_t) sz; @@ -230,3 +232,15 @@ grub_util_get_kexecute (void) { return kexecute; } + +void +grub_util_set_switch_root (void) +{ + switchroot = 1; +} + +int +grub_util_get_switch_root (void) +{ + return switchroot; +} diff --git a/grub-core/kern/err.c b/grub-core/kern/err.c index 53c734de70..ba04b57fb7 100644 --- a/grub-core/kern/err.c +++ b/grub-core/kern/err.c @@ -33,15 +33,24 @@ static struct grub_error_saved grub_error_stack_items[GRUB_ERROR_STACK_SIZE]; static int grub_error_stack_pos; static int grub_error_stack_assert; +#ifdef grub_error +#undef grub_error +#endif + grub_err_t -grub_error (grub_err_t n, const char *fmt, ...) +grub_error (grub_err_t n, const char *file, const char *function, const int line, const char *fmt, ...) { va_list ap; + int m; grub_errno = n; + m = grub_snprintf (grub_errmsg, sizeof (grub_errmsg), "%s:%s:%d:", file, function, line); + if (m < 0) + m = 0; + va_start (ap, fmt); - grub_vsnprintf (grub_errmsg, sizeof (grub_errmsg), _(fmt), ap); + grub_vsnprintf (grub_errmsg + m, sizeof (grub_errmsg) - m, _(fmt), ap); va_end (ap); return n; diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c index 750177248f..f051bd48ff 100644 --- a/grub-core/kern/file.c +++ b/grub-core/kern/file.c @@ -25,11 +25,20 @@ #include #include #include +#include void (*EXPORT_VAR (grub_grubnet_fini)) (void); grub_file_filter_t grub_file_filters[GRUB_FILE_FILTER_MAX]; +static const char *filter_names[] = { + [GRUB_FILE_FILTER_VERIFY] = "GRUB_FILE_FILTER_VERIFY", + [GRUB_FILE_FILTER_GZIO] = "GRUB_FILE_FILTER_GZIO", + [GRUB_FILE_FILTER_XZIO] = "GRUB_FILE_FILTER_XZIO", + [GRUB_FILE_FILTER_LZOPIO] = "GRUB_FILE_FILTER_LZOPIO", + [GRUB_FILE_FILTER_MAX] = "GRUB_FILE_FILTER_MAX" +}; + /* Get the device part of the filename NAME. It is enclosed by parentheses. */ char * grub_file_get_device_name (const char *name) @@ -66,6 +75,8 @@ grub_file_open (const char *name, enum grub_file_type type) const char *file_name; grub_file_filter_id_t filter; + grub_dprintf ("file", "Opening `%s' ...\n", name); + /* Reset grub_errno before we start. */ grub_errno = GRUB_ERR_NONE; @@ -114,6 +125,12 @@ grub_file_open (const char *name, enum grub_file_type type) if ((file->fs->fs_open) (file, file_name) != GRUB_ERR_NONE) goto fail; + if (file->data == NULL) + goto fail; + + if (file->fs->mod) + grub_dl_ref (file->fs->mod); + file->name = grub_strdup (name); grub_errno = GRUB_ERR_NONE; @@ -122,6 +139,9 @@ grub_file_open (const char *name, enum grub_file_type type) if (grub_file_filters[filter]) { last_file = file; + if (filter < GRUB_FILE_FILTER_MAX) + grub_dprintf ("file", "Running %s file filter\n", + filter_names[filter]); file = grub_file_filters[filter] (file, type); if (file && file != last_file) { @@ -132,6 +152,8 @@ grub_file_open (const char *name, enum grub_file_type type) if (!file) grub_file_close (last_file); + grub_dprintf ("file", "Opening `%s' succeeded.\n", name); + return file; fail: @@ -143,6 +165,8 @@ grub_file_open (const char *name, enum grub_file_type type) grub_free (file); + grub_dprintf ("file", "Opening `%s' failed.\n", name); + return 0; } @@ -155,25 +179,30 @@ grub_file_read (grub_file_t file, void *buf, grub_size_t len) grub_disk_read_hook_t read_hook; void *read_hook_data; - if (file->offset > file->size) - { - grub_error (GRUB_ERR_OUT_OF_RANGE, - N_("attempt to read past the end of file")); - return -1; - } - if (len == 0) return 0; - if (len > file->size - file->offset) - len = file->size - file->offset; +#ifdef GRUB_MACHINE_EMU + if (file->size >= 0) + { +#endif + if (file->offset > file->size) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + N_("attempt to read past the end of file")); + return -1; + } + + if (len > file->size - file->offset) + len = file->size - file->offset; +#ifdef GRUB_MACHINE_EMU + } +#endif /* Prevent an overflow. */ if ((grub_ssize_t) len < 0) len >>= 1; - if (len == 0) - return 0; read_hook = file->read_hook; read_hook_data = file->read_hook_data; if (!file->read_hook) @@ -194,11 +223,21 @@ grub_file_read (grub_file_t file, void *buf, grub_size_t len) grub_err_t grub_file_close (grub_file_t file) { + grub_dprintf ("file", "Closing `%s' ...\n", file->name); if (file->fs->fs_close) (file->fs->fs_close) (file); + if (file->fs->mod) + grub_dl_unref (file->fs->mod); + if (file->device) grub_device_close (file->device); + + if (grub_errno == GRUB_ERR_NONE) + grub_dprintf ("file", "Closing `%s' succeeded.\n", file->name); + else + grub_dprintf ("file", "Closing `%s' failed with %d.\n", file->name, grub_errno); + grub_free (file->name); grub_free (file); return grub_errno; diff --git a/grub-core/kern/fs.c b/grub-core/kern/fs.c index 7ad0aaf4e2..9f6f4ef9da 100644 --- a/grub-core/kern/fs.c +++ b/grub-core/kern/fs.c @@ -74,6 +74,7 @@ grub_fs_probe (grub_device_t device) if (grub_errno == GRUB_ERR_NONE) return p; + grub_dprintf ("fs", _("error: %s.\n"), grub_errmsg); grub_error_push (); /* The grub_error_push() does not touch grub_errmsg. */ grub_dprintf ("fs", _("error: %s.\n"), grub_errmsg); @@ -215,12 +216,15 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len) grub_disk_addr_t sector; grub_off_t offset; grub_ssize_t ret = 0; + grub_disk_t disk = file->device->disk; if (len > file->size - file->offset) len = file->size - file->offset; sector = (file->offset >> GRUB_DISK_SECTOR_BITS); offset = (file->offset & (GRUB_DISK_SECTOR_SIZE - 1)); + disk->read_hook = file->read_hook; + disk->read_hook_data = file->read_hook_data; for (p = file->data; p->length && len > 0; p++) { if (sector < p->length) @@ -232,9 +236,12 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len) >> GRUB_DISK_SECTOR_BITS) > p->length - sector) size = ((p->length - sector) << GRUB_DISK_SECTOR_BITS) - offset; - if (grub_disk_read (file->device->disk, p->offset + sector, offset, + if (grub_disk_read (disk, p->offset + sector, offset, size, buf) != GRUB_ERR_NONE) - return -1; + { + ret = -1; + break; + } ret += size; len -= size; @@ -244,6 +251,8 @@ grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len) else sector -= p->length; } + disk->read_hook = NULL; + disk->read_hook_data = NULL; return ret; } diff --git a/grub-core/kern/i386/backtrace.c b/grub-core/kern/i386/backtrace.c new file mode 100644 index 0000000000..2413f9a57d --- /dev/null +++ b/grub-core/kern/i386/backtrace.c @@ -0,0 +1,125 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_STACK_FRAME 102400 + +void +grub_backtrace_pointer (void *frame, unsigned int skip) +{ + void **ebp = (void **)frame; + unsigned long x = 0; + + while (ebp) + { + void **next_ebp = (void **)ebp[0]; + const char *name = NULL; + char *addr = NULL; + + grub_dprintf("backtrace", "ebp is %p next_ebp is %p\n", ebp, next_ebp); + + if (x >= skip) + { + name = grub_get_symbol_by_addr (ebp[1], 1); + if (name) + addr = grub_resolve_symbol (name); + grub_backtrace_print_address (ebp[1]); + + if (addr && addr != ebp[1]) + grub_printf (" %s() %p+%p \n", name ? name : "unknown", addr, + (char *)((char *)ebp[1] - addr)); + else + grub_printf(" %s() %p \n", name ? name : "unknown", addr); + +#if 0 + grub_printf ("("); + for (i = 0, arg = ebp[2]; arg != next_ebp && i < 12; arg++, i++) + grub_printf ("%p,", arg); + grub_printf (")\n"); +#endif + } + + x += 1; + + if (next_ebp < ebp || next_ebp - ebp > MAX_STACK_FRAME || next_ebp == ebp) + { + //grub_printf ("Invalid stack frame at %p (%p)\n", ebp, next_ebp); + break; + } + ebp = next_ebp; + } +} + +#if defined (__x86_64__) +asm ("\t.global \"_text\"\n" + "_text:\n" + "\t.quad .text\n" + "\t.global \"_data\"\n" + "_data:\n" + "\t.quad .data\n" + ); +#elif defined(__i386__) +asm ("\t.global \"_text\"\n" + "_text:\n" + "\t.long .text\n" + "\t.global \"_data\"\n" + "_data:\n" + "\t.long .data\n" + ); +#else +#warning I dunno... +#endif + +extern unsigned long _text; +extern unsigned long _data; + +#ifdef GRUB_UTIL +#define EXT_C(x) x +#endif + +void +grub_backtrace_arch (unsigned int skip) +{ + grub_printf ("Backtrace (.text %p .data %p):\n", + (void *)_text, (void *)_data); + skip += 1; +#if defined (__x86_64__) + asm volatile ("movq %%rbp, %%rdi\n" + "movq 0, %%rsi\n" + "movl %0, %%esi\n" + "call " EXT_C("grub_backtrace_pointer") + : + : "r" (skip)); +#elif defined(__i386__) + asm volatile ("addl $8, %%esp\n" + "pushl %0\n" + "pushl %%ebp\n" + "call " EXT_C("grub_backtrace_pointer") + : + : "r" (skip)); +#else + grub_backtrace_pointer(__builtin_frame_address(0), skip); +#endif +} diff --git a/grub-core/kern/i386/coreboot/init.c b/grub-core/kern/i386/coreboot/init.c index 4fae8b571f..feaf9295e3 100644 --- a/grub-core/kern/i386/coreboot/init.c +++ b/grub-core/kern/i386/coreboot/init.c @@ -41,7 +41,7 @@ extern grub_uint8_t _end[]; extern grub_uint8_t _edata[]; void __attribute__ ((noreturn)) -grub_exit (void) +grub_exit (int rc __attribute__((unused))) { /* We can't use grub_fatal() in this function. This would create an infinite loop, since grub_fatal() calls grub_abort() which in turn calls grub_exit(). */ diff --git a/grub-core/kern/i386/dl.c b/grub-core/kern/i386/dl.c index 1346da5cc9..d6b4681fc9 100644 --- a/grub-core/kern/i386/dl.c +++ b/grub-core/kern/i386/dl.c @@ -79,3 +79,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ +#ifdef GRUB_MACHINE_EFI + return 4096; +#else + return 1; +#endif +} diff --git a/grub-core/kern/i386/efi/startup.S b/grub-core/kern/i386/efi/startup.S index fc5ea3dac8..36d1b1a689 100644 --- a/grub-core/kern/i386/efi/startup.S +++ b/grub-core/kern/i386/efi/startup.S @@ -34,3 +34,8 @@ _start: movl %eax, EXT_C(grub_efi_system_table) call EXT_C(grub_main) ret + +/* An executable stack is not required for these functions. */ +#if defined (__linux__) && defined (__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/grub-core/kern/i386/pc/init.c b/grub-core/kern/i386/pc/init.c index 326d491c58..fe7ad14d45 100644 --- a/grub-core/kern/i386/pc/init.c +++ b/grub-core/kern/i386/pc/init.c @@ -153,7 +153,7 @@ compact_mem_regions (void) } grub_addr_t grub_modbase; -extern grub_uint8_t _start[], _edata[]; +extern grub_uint8_t _edata[]; /* Helper for grub_machine_init. */ static int @@ -226,7 +226,7 @@ grub_machine_init (void) /* This has to happen before any BIOS calls. */ grub_via_workaround_init (); - grub_modbase = GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR + (_edata - _start); + grub_modbase = GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR + (_edata - (grub_uint8_t *)_start); /* Initialize the console as early as possible. */ grub_console_init (); diff --git a/grub-core/kern/i386/qemu/init.c b/grub-core/kern/i386/qemu/init.c index 08f81d25e6..604fc94b5a 100644 --- a/grub-core/kern/i386/qemu/init.c +++ b/grub-core/kern/i386/qemu/init.c @@ -42,7 +42,7 @@ extern grub_uint8_t _end[]; extern grub_uint8_t _edata[]; void __attribute__ ((noreturn)) -grub_exit (void) +grub_exit (int rc __attribute__((unused))) { /* We can't use grub_fatal() in this function. This would create an infinite loop, since grub_fatal() calls grub_abort() which in turn calls grub_exit(). */ diff --git a/grub-core/kern/i386/qemu/startup.S b/grub-core/kern/i386/qemu/startup.S index 0d89858d9b..939f182fc7 100644 --- a/grub-core/kern/i386/qemu/startup.S +++ b/grub-core/kern/i386/qemu/startup.S @@ -24,7 +24,8 @@ .text .code32 - .globl _start + .globl start, _start +start: _start: jmp codestart diff --git a/grub-core/kern/ia64/dl.c b/grub-core/kern/ia64/dl.c index db59300fea..92d82c5750 100644 --- a/grub-core/kern/ia64/dl.c +++ b/grub-core/kern/ia64/dl.c @@ -148,3 +148,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, } return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ + return 1; +} diff --git a/grub-core/kern/ia64/efi/startup.S b/grub-core/kern/ia64/efi/startup.S index d75c6d7cc7..8f2a593e52 100644 --- a/grub-core/kern/ia64/efi/startup.S +++ b/grub-core/kern/ia64/efi/startup.S @@ -24,8 +24,9 @@ .psr lsb .lsb - .global _start + .global start, _start .proc _start +start: _start: alloc loc0=ar.pfs,2,4,0,0 mov loc1=rp diff --git a/grub-core/kern/ieee1275/cmain.c b/grub-core/kern/ieee1275/cmain.c index e74de32483..810a089a93 100644 --- a/grub-core/kern/ieee1275/cmain.c +++ b/grub-core/kern/ieee1275/cmain.c @@ -89,7 +89,10 @@ grub_ieee1275_find_options (void) } if (rc >= 0 && grub_strncmp (tmp, "IBM", 3) == 0) - grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_TREE_SCANNING_FOR_DISKS); + { + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_NO_TREE_SCANNING_FOR_DISKS); + grub_ieee1275_set_flag (GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT); + } /* Old Macs have no key repeat, newer ones have fully working one. The ones inbetween when repeated key generates an escaoe sequence diff --git a/grub-core/kern/ieee1275/init.c b/grub-core/kern/ieee1275/init.c index fb7d1a3bac..482cad2090 100644 --- a/grub-core/kern/ieee1275/init.c +++ b/grub-core/kern/ieee1275/init.c @@ -49,8 +49,9 @@ #if defined(__powerpc__) || defined(__i386__) #include #endif +#include -/* The maximum heap size we're going to claim at boot. Not used by sparc. */ +/* The maximum heap size we're going to claim. Not used by sparc. */ #ifdef __i386__ #define HEAP_MAX_SIZE (unsigned long) (64 * 1024 * 1024) #else /* __powerpc__ */ @@ -70,7 +71,6 @@ */ #define RUNTIME_MIN_SPACE (128UL * 1024 * 1024) -extern char _start[]; extern char _end[]; #ifdef __sparc__ @@ -113,8 +113,18 @@ grub_addr_t grub_ieee1275_original_stack; #define DRC_INFO 0x40 #define BYTE22 (DY_MEM_V2 | DRC_INFO) +/* For ibm,arch-vec-5-platform-support. */ +#define XIVE_INDEX 0x17 +#define MMU_INDEX 0x18 +#define RADIX_GTSE_INDEX 0x1a +#define RADIX_ENABLED 0x40 +#define XIVE_ENABLED 0x40 +#define HASH_ENABLED 0x00 +#define MAX_SUPPORTED 0xC0 +#define RADIX_GTSE_ENABLED 0x40 + void -grub_exit (void) +grub_exit (int rc __attribute__((unused))) { grub_ieee1275_exit (); } @@ -170,23 +180,25 @@ grub_machine_get_bootlocation (char **device, char **path) grub_free (canon); } else - *device = grub_ieee1275_encode_devname (bootpath); - grub_free (type); - - filename = grub_ieee1275_get_filename (bootpath); - if (filename) { - char *lastslash = grub_strrchr (filename, '\\'); - - /* Truncate at last directory. */ - if (lastslash) + filename = grub_ieee1275_get_filename (bootpath); + if (filename) { - *lastslash = '\0'; - grub_translate_ieee1275_path (filename); + char *lastslash = grub_strrchr (filename, '\\'); - *path = filename; - } + /* Truncate at last directory. */ + if (lastslash) + { + *lastslash = '\0'; + grub_translate_ieee1275_path (filename); + + *path = filename; + } + } + *device = grub_ieee1275_encode_devname (bootpath); } + + grub_free (type); grub_free (bootpath); } @@ -737,6 +749,10 @@ struct option_vector5 grub_uint32_t platform_facilities; grub_uint8_t sub_processors; grub_uint8_t byte22; + grub_uint8_t xive; + grub_uint8_t mmu; + grub_uint8_t hpt_ext; + grub_uint8_t radix_gtse; } GRUB_PACKED; struct pvr_entry @@ -766,7 +782,7 @@ struct cas_vector /* * Call ibm,client-architecture-support to try to get more RMA. - * We ask for 512MB which should be enough to verify a distro kernel. + * We ask for 768MB which should be enough to verify a distro kernel. * We ignore most errors: if we don't succeed we'll proceed with whatever * memory we have. */ @@ -775,6 +791,13 @@ grub_ieee1275_ibm_cas (void) { int rc; grub_ieee1275_ihandle_t root; + grub_uint8_t ibm_arch_platform_support[8]; + grub_ssize_t actual; + grub_uint8_t xive_support = 0; + grub_uint8_t mmu_support = 0; + grub_uint8_t radix_gtse_support = 0; + int i = 0; + int prop_len = 8; struct cas_args { struct grub_ieee1275_common_hdr common; @@ -783,6 +806,46 @@ grub_ieee1275_ibm_cas (void) grub_ieee1275_cell_t cas_addr; grub_ieee1275_cell_t result; } args; + + grub_ieee1275_get_integer_property (grub_ieee1275_chosen, + "ibm,arch-vec-5-platform-support", + (grub_uint32_t *) ibm_arch_platform_support, + sizeof (ibm_arch_platform_support), + &actual); + + for (i = 0; i < prop_len; i++) + { + switch (ibm_arch_platform_support[i]) + { + case XIVE_INDEX: + if (ibm_arch_platform_support[i + 1] & MAX_SUPPORTED) + xive_support = XIVE_ENABLED; + else + xive_support = 0; + break; + + case MMU_INDEX: + if (ibm_arch_platform_support[i + 1] & MAX_SUPPORTED) + mmu_support = RADIX_ENABLED; + else + mmu_support = HASH_ENABLED; + break; + + case RADIX_GTSE_INDEX: + if (mmu_support == RADIX_ENABLED) + radix_gtse_support = ibm_arch_platform_support[i + 1] & RADIX_GTSE_ENABLED; + else + radix_gtse_support = 0; + break; + + default: + /* Ignoring the other indexes of ibm,arch-vec-5-platform-support. */ + break; + } + /* Skipping the property value. */ + i++; + } + struct cas_vector vector = { .pvr_list = { { 0x00000000, 0xffffffff } }, /* any processor */ @@ -791,7 +854,7 @@ grub_ieee1275_ibm_cas (void) .vec1 = 0x80, /* ignore */ .vec2_size = 1 + sizeof (struct option_vector2) - 2, .vec2 = { - 0, 0, -1, -1, -1, -1, -1, 512, -1, 0, 48 + 0, 0, -1, -1, -1, -1, -1, 768, -1, 0, 48 }, .vec3_size = 2 - 1, .vec3 = 0x00e0, /* ask for FP + VMX + DFP but don't halt if unsatisfied */ @@ -799,7 +862,7 @@ grub_ieee1275_ibm_cas (void) .vec4 = 0x0001, /* set required minimum capacity % to the lowest value */ .vec5_size = 1 + sizeof (struct option_vector5) - 2, .vec5 = { - 0, BYTE2, 0, CMO, ASSOCIATIVITY, BIN_OPTS, 0, 0, MAX_CPU, 0, 0, PLATFORM_FACILITIES, SUB_PROCESSORS, BYTE22 + 0, BYTE2, 0, CMO, ASSOCIATIVITY, BIN_OPTS, 0, 0, MAX_CPU, 0, 0, PLATFORM_FACILITIES, SUB_PROCESSORS, BYTE22, xive_support, mmu_support, 0, radix_gtse_support } }; @@ -828,6 +891,10 @@ grub_claim_heap (void) { grub_err_t err; grub_uint32_t total = HEAP_MAX_SIZE; +#if defined(__powerpc__) + grub_uint32_t ibm_ca_support_reboot = 0; + grub_ssize_t actual; +#endif err = grub_ieee1275_total_mem (&rmo_top); @@ -840,10 +907,60 @@ grub_claim_heap (void) grub_mm_add_region_fn = grub_ieee1275_mm_add_region; #if defined(__powerpc__) + /* Check if it's a CAS reboot with below property. If so, we will skip CAS call */ + if (grub_ieee1275_get_integer_property (grub_ieee1275_chosen, + "ibm,client-architecture-support-reboot", + &ibm_ca_support_reboot, + sizeof (ibm_ca_support_reboot), + &actual) >= 0) + grub_dprintf ("ieee1275", "ibm,client-architecture-support-reboot: %" PRIuGRUB_UINT32_T "\n", + ibm_ca_support_reboot); + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY)) { + /* + * If we have an error, don't call CAS. Just hope for the best. + * Along with the above, if the rmo_top is 512 MB or above. We + * will skip the CAS call. However, if we call CAS, the rmo_top + * will be set to 768 MB via CAS Vector2. But we need to call + * CAS with "rmo_top < 512 MB" to avoid the issue on the older + * Linux kernel, which still uses rmo_top as 512 MB. If we call + * CAS with a condition "rmo_top < 768 MB", it will result in an + * issue due to the IBM CAS reboot feature and we won't be able + * to boot the newer kernel. Whenever a reboot is detected as + * the CAS reboot by GRUB. It will boot the machine with the + * last booted kernel by reading the variable "boot-last-label" + * which has the info related to the last boot and it's specific + * to IBM PowerPC. Due to this, the machine will boot with the + * last booted kernel which has rmo_top as 512 MB. Also, if the + * reboot is detected as a CAS reboot, the GRUB will skip the CAS + * call. As the CAS has already been called earlier, so it is + * not required to call CAS even if the other conditions are met. + * This condition will also prevent a scenario where the machine + * get stuck in the CAS reboot loop while booting. A machine with + * an older kernel, having option_vector2 MIN_RMA as 512 MB in + * Linux prom_init.c and GRUB uses "rmo_top < 768 MB" condition + * for calling CAS. Due to their respective conditions, linux + * CAS and GRUB CAS will keep doing the CAS calls and change + * the MIN_RMA from 768(changed by GRUB) to 512(changed by Linux) + * to 768(changed by GRUB) to 512(changed by Linux) and so on, + * and the machine will stuck in this CAS reboot loop forever. + * IBM PAPR : https://openpower.foundation/specifications/linuxonpower/ + */ + if (!ibm_ca_support_reboot && err == GRUB_ERR_NONE && rmo_top < (512 * 1024 * 1024)) + grub_ieee1275_ibm_cas (); + } +#endif + +#if defined(__powerpc__) + if (grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_CAN_TRY_CAS_FOR_MORE_MEMORY)) + { + grub_uint64_t rma_size; + grub_err_t err; + + err = grub_ieee1275_total_mem (&rma_size); /* if we have an error, don't call CAS, just hope for the best */ - if (err == GRUB_ERR_NONE && rmo_top < (512 * 1024 * 1024)) + if (err == GRUB_ERR_NONE && rma_size < (512 * 1024 * 1024)) grub_ieee1275_ibm_cas (); } #endif @@ -892,6 +1009,30 @@ grub_parse_cmdline (void) } } +static void +grub_get_ieee1275_secure_boot (void) +{ + grub_ieee1275_phandle_t root; + int rc; + grub_uint32_t is_sb; + + grub_ieee1275_finddevice ("/", &root); + + rc = grub_ieee1275_get_integer_property (root, "ibm,secure-boot", &is_sb, + sizeof (is_sb), 0); + + /* ibm,secure-boot: + * 0 - disabled + * 1 - audit + * 2 - enforce + * 3 - enforce + OS-specific behaviour + * + * We only support enforce. + */ + if (rc >= 0 && is_sb >= 2) + grub_lockdown (); +} + grub_addr_t grub_modbase; void @@ -917,6 +1058,8 @@ grub_machine_init (void) #else grub_install_get_time_ms (grub_rtc_get_time_ms); #endif + + grub_get_ieee1275_secure_boot (); } void diff --git a/grub-core/kern/ieee1275/openfw.c b/grub-core/kern/ieee1275/openfw.c index 11b2beb2ff..65616254e0 100644 --- a/grub-core/kern/ieee1275/openfw.c +++ b/grub-core/kern/ieee1275/openfw.c @@ -499,7 +499,7 @@ grub_ieee1275_encode_devname (const char *path) *optr++ ='\\'; *optr++ = *iptr++; } - if (partition && partition[0]) + if (partition && partition[0] >= '0' && partition[0] <= '9') { unsigned int partno = grub_strtoul (partition, 0, 0); @@ -591,3 +591,66 @@ grub_ieee1275_get_boot_dev (void) return bootpath; } + +/* Check if it's a CAS reboot. If so, set the script to be executed. */ +int +grub_ieee1275_cas_reboot (char *script) +{ + grub_uint32_t ibm_ca_support_reboot; + grub_uint32_t ibm_fw_nbr_reboots; + char property_value[10]; + grub_ssize_t actual; + grub_ieee1275_ihandle_t options; + + if (grub_ieee1275_finddevice ("/options", &options) < 0) + return -1; + + /* Check two properties, one is enough to get cas reboot value */ + ibm_ca_support_reboot = 0; + if (grub_ieee1275_get_integer_property (grub_ieee1275_chosen, + "ibm,client-architecture-support-reboot", + &ibm_ca_support_reboot, + sizeof (ibm_ca_support_reboot), + &actual) >= 0) + grub_dprintf("ieee1275", "ibm,client-architecture-support-reboot: %u\n", + ibm_ca_support_reboot); + + ibm_fw_nbr_reboots = 0; + if (grub_ieee1275_get_property (options, "ibm,fw-nbr-reboots", + property_value, sizeof (property_value), + &actual) >= 0) + { + property_value[sizeof (property_value) - 1] = 0; + ibm_fw_nbr_reboots = (grub_uint8_t) grub_strtoul (property_value, 0, 10); + grub_dprintf("ieee1275", "ibm,fw-nbr-reboots: %u\n", ibm_fw_nbr_reboots); + } + + if (ibm_ca_support_reboot || ibm_fw_nbr_reboots) + { + if (! grub_ieee1275_get_property_length (options, "boot-last-label", &actual)) + { + if (actual > 1024) + script = grub_realloc (script, actual + 1); + grub_ieee1275_get_property (options, "boot-last-label", script, actual, + &actual); + return 0; + } + } + + grub_ieee1275_set_boot_last_label (""); + + return -1; +} + +int grub_ieee1275_set_boot_last_label (const char *text) +{ + grub_ieee1275_ihandle_t options; + grub_ssize_t actual; + + grub_dprintf("ieee1275", "set boot_last_label (size: %u)\n", grub_strlen(text)); + if (! grub_ieee1275_finddevice ("/options", &options) && + options != (grub_ieee1275_ihandle_t) -1) + grub_ieee1275_set_property (options, "boot-last-label", text, + grub_strlen (text), &actual); + return 0; +} diff --git a/grub-core/kern/loongarch64/dl.c b/grub-core/kern/loongarch64/dl.c index 7f923b4159..951b131c18 100644 --- a/grub-core/kern/loongarch64/dl.c +++ b/grub-core/kern/loongarch64/dl.c @@ -148,3 +148,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, } return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ +#ifdef GRUB_MACHINE_EFI + return 4096; +#else + return 1; +#endif +} diff --git a/grub-core/kern/loongarch64/efi/startup.S b/grub-core/kern/loongarch64/efi/startup.S index 87cfb23ea5..f5e6acf1bf 100644 --- a/grub-core/kern/loongarch64/efi/startup.S +++ b/grub-core/kern/loongarch64/efi/startup.S @@ -21,6 +21,7 @@ .file "startup.S" .text +FUNCTION(start) FUNCTION(_start) /* * EFI_SYSTEM_TABLE and EFI_HANDLE are passed in $a1/$a0. diff --git a/grub-core/kern/main.c b/grub-core/kern/main.c index 731c07c290..2e6b79ee3d 100644 --- a/grub-core/kern/main.c +++ b/grub-core/kern/main.c @@ -30,11 +30,15 @@ #include #include #include +#include #ifdef GRUB_MACHINE_PCBIOS #include #endif +static bool cli_disabled = false; +static bool cli_need_auth = false; + grub_addr_t grub_modules_get_end (void) { @@ -128,16 +132,24 @@ grub_set_prefix_and_root (void) grub_machine_get_bootlocation (&fwdevice, &fwpath); - if (fwdevice) + if (fwdevice && fwpath) { - char *cmdpath; + char *fw_path; + char separator[3] = ")"; + + grub_dprintf ("fw_path", "\n"); + grub_dprintf ("fw_path", "fwdevice:\"%s\" fwpath:\"%s\"\n", fwdevice, fwpath); - cmdpath = grub_xasprintf ("(%s)%s", fwdevice, fwpath ? : ""); - if (cmdpath) + if (!grub_strncmp(fwdevice, "http", 4) && fwpath[0] != '/') + grub_strcpy(separator, ")/"); + + fw_path = grub_xasprintf ("(%s%s%s", fwdevice, separator, fwpath); + if (fw_path) { - grub_env_set ("cmdpath", cmdpath); - grub_env_export ("cmdpath"); - grub_free (cmdpath); + grub_env_set ("fw_path", fw_path); + grub_env_export ("fw_path"); + grub_dprintf ("fw_path", "fw_path:\"%s\"\n", fw_path); + grub_free (fw_path); } } @@ -209,12 +221,66 @@ grub_set_prefix_and_root (void) { char *prefix_set; - prefix_set = grub_xasprintf ("(%s)%s", device, path ? : ""); - if (prefix_set) +#ifdef __powerpc__ + /* We have to be careful here on powerpc-ieee1275 + signed grub. We + will have signed something with a prefix that doesn't have a device + because we cannot know in advance what partition we're on. + + We will have had !device earlier, so we will have set device=fwdevice + However, we want to make sure we do not end up setting prefix to be + ($fwdevice)/path, because we will then end up trying to boot or search + based on a prefix of (ieee1275/disk)/path, which will not work because + it's missing a partition. + + Also: + - You can end up with a device with an FS directly on it, without + a partition, e.g. ieee1275/cdrom. + + - powerpc-ieee1275 + grub-install sets e.g. prefix=(,gpt2)/path, + which will have now been extended to device=$fwdisk,partition + and path=/path + + - PowerVM will give us device names like + ieee1275//vdevice/v-scsi@3000006c/disk@8100000000000000 + and we don't want to try to encode some sort of truth table about + what sorts of paths represent disks with partition tables and those + without partition tables. + + - Frustratingly, the device name itself can contain an embedded comma: + /pci@800000020000015/pci1014,034A@0/sas/disk@5000c50098a0ee8b + So we cannot even rely upon the presence of a comma to say that a + partition has been specified! + + If we only have a path in $prefix, the code in normal to discover + config files will try all disks, both without partitions and then with + any partitions so we will cover both CDs and HDs. + + However, it doesn't then set the prefix to be something like + (discovered partition)/path, and so it is fragile against runtime + changes to $root. For example some of the stuff done in 10_linux to + reload $root sets root differently and then uses search to find it + again. If the search module is not built in, when we change root, grub + will look in (new root)/path/powerpc-ieee1275, that won't work, and we + will not be able to load the search module and the boot will fail. + + This is particularly likely to hit us in the grub-install + (,msdos2)/grub2 case, so we act unless the supplied prefix starts with + '(', which would likely indicate a partition has already been + specified. + */ + if (prefix && prefix[0] != '(') + grub_env_set ("prefix", path); + else +#endif { - grub_env_set ("prefix", prefix_set); - grub_free (prefix_set); + prefix_set = grub_xasprintf ("(%s)%s", device, path ? : ""); + if (prefix_set) + { + grub_env_set ("prefix", prefix_set); + grub_free (prefix_set); + } } + grub_env_set ("root", device); } @@ -237,6 +303,39 @@ grub_load_normal_mode (void) grub_command_execute ("normal", 0, 0); } +bool +grub_is_cli_disabled (void) +{ + return cli_disabled; +} + +bool +grub_is_cli_need_auth (void) +{ + return cli_need_auth; +} + +void grub_cli_set_auth_needed (void) +{ + cli_need_auth = true; +} + +static void +check_is_cli_disabled (void) +{ + struct grub_module_header *header; + header = 0; + + FOR_MODULES (header) + { + if (header->type == OBJ_TYPE_DISABLE_CLI) + { + cli_disabled = true; + return; + } + } +} + static void reclaim_module_space (void) { @@ -294,6 +393,9 @@ grub_main (void) grub_boot_time ("After loading embedded modules."); + /* Check if the CLI should be disabled */ + check_is_cli_disabled (); + /* It is better to set the root device as soon as possible, for convenience. */ grub_set_prefix_and_root (); diff --git a/grub-core/kern/mips/arc/init.c b/grub-core/kern/mips/arc/init.c index 2ed3ff3191..5c40c34078 100644 --- a/grub-core/kern/mips/arc/init.c +++ b/grub-core/kern/mips/arc/init.c @@ -276,7 +276,7 @@ grub_halt (void) } void -grub_exit (void) +grub_exit (int rc __attribute__((unused))) { GRUB_ARC_FIRMWARE_VECTOR->exit (); diff --git a/grub-core/kern/mips/dl.c b/grub-core/kern/mips/dl.c index 5b02f97fca..db411899db 100644 --- a/grub-core/kern/mips/dl.c +++ b/grub-core/kern/mips/dl.c @@ -272,3 +272,11 @@ grub_arch_dl_init_linker (void) grub_dl_register_symbol ("_gp_disp", &_gp_disp_dummy, 0, 0); } +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ + return 1; +} diff --git a/grub-core/kern/mips/loongson/init.c b/grub-core/kern/mips/loongson/init.c index 5bd721260f..97b09b0eee 100644 --- a/grub-core/kern/mips/loongson/init.c +++ b/grub-core/kern/mips/loongson/init.c @@ -304,7 +304,7 @@ grub_halt (void) } void -grub_exit (void) +grub_exit (int rc __attribute__((unused))) { grub_halt (); } diff --git a/grub-core/kern/mips/qemu_mips/init.c b/grub-core/kern/mips/qemu_mips/init.c index b5477b87ff..69488a34e7 100644 --- a/grub-core/kern/mips/qemu_mips/init.c +++ b/grub-core/kern/mips/qemu_mips/init.c @@ -75,7 +75,7 @@ grub_machine_fini (int flags __attribute__ ((unused))) } void -grub_exit (void) +grub_exit (int rc __attribute__((unused))) { grub_halt (); } diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c index 7cee5d75c3..2568a5489b 100644 --- a/grub-core/kern/misc.c +++ b/grub-core/kern/misc.c @@ -26,6 +26,11 @@ #include #include #include +#include +#if DEBUG_WITH_TIMESTAMPS +#include +#endif +#include union printf_arg { @@ -182,6 +187,19 @@ int grub_err_printf (const char *fmt, ...) __attribute__ ((alias("grub_printf"))); #endif +/* Return 1 if 'debug' is set and not empty */ +int +grub_debug_is_enabled (void) +{ + const char *debug; + + debug = grub_env_get ("debug"); + if (!debug || debug[0] == '\0') + return 0; + + return 1; +} + int grub_debug_enabled (const char * condition) { @@ -231,14 +249,49 @@ grub_debug_enabled (const char * condition) } void -grub_real_dprintf (const char *file, const int line, const char *condition, +grub_real_dprintf (const char *file, const char *function, const int line, const char *condition, const char *fmt, ...) { va_list args; +#if DEBUG_WITH_TIMESTAMPS + static long unsigned int last_time = 0; + static int last_had_cr = 1; +#endif if (grub_debug_enabled (condition)) { - grub_printf ("%s:%d:%s: ", file, line, condition); +#if DEBUG_WITH_TIMESTAMPS + /* Don't print timestamp if last printed message isn't terminated yet */ + if (last_had_cr) { + long unsigned int tmabs = (long unsigned int) grub_get_time_ms(); + long unsigned int tmrel = tmabs - last_time; + last_time = tmabs; + grub_printf ("%3lu.%03lus +%2lu.%03lus ", tmabs / 1000, tmabs % 1000, tmrel / 1000, tmrel % 1000); + } + if (fmt[grub_strlen(fmt)-1] == '\n') + last_had_cr = 1; + else + last_had_cr = 0; +#endif + grub_printf ("%s:%s:%d:%s: ", file, function, line, condition); + va_start (args, fmt); + grub_vprintf (fmt, args); + va_end (args); + grub_refresh (); + } +} + +void +grub_qdprintf (const char *condition, const char *fmt, ...) +{ + va_list args; + const char *debug = grub_env_get ("debug"); + + if (! debug) + return; + + if (grub_strword (debug, "all") || grub_strword (debug, condition)) + { va_start (args, fmt); grub_vprintf (fmt, args); va_end (args); @@ -401,6 +454,68 @@ grub_strword (const char *haystack, const char *needle) return 0; } +char * +grub_strtok_r (char *s, const char *delim, char **save_ptr) +{ + char *token; + const char *c; + bool is_delim; + + if (s == NULL) + s = *save_ptr; + + /* Scan leading delimiters. */ + while (*s != '\0') + { + is_delim = false; + for (c = delim; *c != '\0'; c++) + { + if (*s == *c) + { + is_delim = true; + break; + } + } + if (is_delim == true) + s++; + else + break; + } + + if (*s == '\0') + { + *save_ptr = s; + return NULL; + } + + /* Find the end of the token. */ + token = s; + while (*s != '\0') + { + for (c = delim; *c != '\0'; c++) + { + if (*s == *c) + { + *s = '\0'; + *save_ptr = s + 1; + return token; + } + } + s++; + } + + *save_ptr = s; + return token; +} + +char * +grub_strtok (char *s, const char *delim) +{ + static char *last; + + return grub_strtok_r (s, delim, &last); +} + int grub_isspace (int c) { @@ -611,6 +726,36 @@ grub_reverse (char *str) } } +/* Separate string into two parts, broken up by delimiter delim. */ +void +grub_str_sep (const char *s, char *p, char delim, char *r) +{ + char* t = grub_strndup(s, grub_strlen(s)); + + if (t != NULL && *t != '\0') + { + char* tmp = t; + + while (((*p = *t) != '\0') && ((*p = *t) != delim)) + { + p++; + t++; + } + *p = '\0'; + + if (*t != '\0') + { + t++; + while ((*r++ = *t++) != '\0') + ; + *r = '\0'; + } + grub_free (tmp); + } + else + grub_free (t); +} + /* Divide N by D, return the quotient, and store the remainder in *R. */ grub_uint64_t grub_divmod64 (grub_uint64_t n, grub_uint64_t d, grub_uint64_t *r) @@ -739,6 +884,9 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args, COMPILE_TIME_ASSERT (sizeof (long) <= sizeof (long long)); COMPILE_TIME_ASSERT (sizeof (long long) == sizeof (void *) || sizeof (int) == sizeof (void *)); + COMPILE_TIME_ASSERT (sizeof (size_t) == sizeof (unsigned) + || sizeof (size_t) == sizeof (unsigned long) + || sizeof (size_t) == sizeof (unsigned long long)); fmt = fmt0; while ((c = *fmt++) != 0) @@ -773,11 +921,17 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args, fmt++; c = *fmt++; + if (c == 'z') + { + c = *fmt++; + goto do_count; + } if (c == 'l') c = *fmt++; if (c == 'l') c = *fmt++; + do_count: switch (c) { case 'p': @@ -830,7 +984,7 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args, while ((c = *fmt++) != 0) { int longfmt = 0; - grub_size_t curn; + unsigned long curn; const char *p; if (c != '%') @@ -848,7 +1002,10 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args, if (*fmt == '$') { - curn = grub_strtoull (p, 0, 10) - 1; + curn = grub_strtoul (p, 0, 10); + if (curn == 0) + continue; + curn--; fmt++; } @@ -871,6 +1028,14 @@ parse_printf_arg_fmt (const char *fmt0, struct printf_args *args, continue; } + if (c == 'z') + { + c = *fmt++; + if (sizeof (size_t) == sizeof (unsigned long)) + longfmt = 1; + else if (sizeof (size_t) == sizeof (unsigned long long)) + longfmt = 2; + } if (c == 'l') { c = *fmt++; @@ -1034,6 +1199,8 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, if (*fmt == '$') { + if (format1 == 0) + continue; curn = format1 - 1; fmt++; format1 = 0; @@ -1045,6 +1212,8 @@ grub_vsnprintf_real (char *str, grub_size_t max_len, const char *fmt0, } c = *fmt++; + if (c == 'z') + c = *fmt++; if (c == 'l') c = *fmt++; if (c == 'l') @@ -1301,7 +1470,12 @@ grub_printf_fmt_check (const char *fmt, const char *fmt_expected) void __attribute__ ((noreturn)) grub_abort (void) { - grub_printf ("\nAborted."); +#if !defined(GRUB_MACHINE_EMU) && !defined(GRUB_UTIL) + grub_backtrace (1); +#else + grub_printf ("\n"); +#endif + grub_printf ("Aborted."); #ifndef GRUB_UTIL if (grub_term_inputs) @@ -1311,14 +1485,24 @@ grub_abort (void) grub_getkey (); } - grub_exit (); + grub_exit (1); +} + +#if defined (__clang__) && !defined (GRUB_UTIL) +/* clang emits references to abort(). */ +void __attribute__ ((noreturn)) +abort (void) +{ + grub_abort (); } +#endif void grub_fatal (const char *fmt, ...) { va_list ap; + grub_printf ("\n"); va_start (ap, fmt); grub_vprintf (_(fmt), ap); va_end (ap); @@ -1389,6 +1573,7 @@ grub_real_boot_time (const char *file, va_start (args, fmt); n->msg = grub_xvasprintf (fmt, args); + grub_dprintf ("boot", "%s\n", n->msg); va_end (args); *boot_time_last = n; diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c index 027a25cd1f..819bc5126a 100644 --- a/grub-core/kern/mm.c +++ b/grub-core/kern/mm.c @@ -144,13 +144,13 @@ get_header_from_pointer (void *ptr, grub_mm_header_t *p, grub_mm_region_t *r) break; if (! *r) - grub_fatal ("out of range pointer %p", ptr); + grub_fatal ("out of range pointer %p\n", ptr); *p = (grub_mm_header_t) ptr - 1; if ((*p)->magic == GRUB_MM_FREE_MAGIC) - grub_fatal ("double free at %p", *p); + grub_fatal ("double free at %p\n", *p); if ((*p)->magic != GRUB_MM_ALLOC_MAGIC) - grub_fatal ("alloc magic is broken at %p: %lx", *p, + grub_fatal ("alloc magic is broken at %p: %lx\n", *p, (unsigned long) (*p)->magic); } @@ -505,7 +505,7 @@ grub_memalign (grub_size_t align, grub_size_t size) goto fail; /* Preallocate some extra space if heap growth is small. */ - grow = grub_max (grow, GRUB_MM_HEAP_GROW_EXTRA); + grow = grub_max (grow, (grub_size_t)GRUB_MM_HEAP_GROW_EXTRA); /* Align up heap growth to make it friendly to CPU/MMU. */ if (grow > ~(grub_size_t) (GRUB_MM_HEAP_GROW_ALIGN - 1)) diff --git a/grub-core/kern/partition.c b/grub-core/kern/partition.c index edad9f9e47..c6a578cf4a 100644 --- a/grub-core/kern/partition.c +++ b/grub-core/kern/partition.c @@ -28,6 +28,9 @@ grub_partition_map_t grub_partition_map_list; +#define MAX_RECURSION_DEPTH 32 +static unsigned int recursion_depth = 0; + /* * Checks that disk->partition contains part. This function assumes that the * start of part is relative to the start of disk->partition. Returns 1 if @@ -122,14 +125,22 @@ grub_partition_probe (struct grub_disk *disk, const char *str) for (ptr = str; *ptr;) { grub_partition_map_t partmap; - int num; + unsigned long num; const char *partname, *partname_end; partname = ptr; while (*ptr && grub_isalpha (*ptr)) ptr++; partname_end = ptr; - num = grub_strtoul (ptr, &ptr, 0) - 1; + + num = grub_strtoul (ptr, &ptr, 0); + if (*ptr != '\0' || num == 0 || num > GRUB_INT_MAX) + { + grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid partition number")); + return 0; + } + + num -= 1; curpart = 0; /* Use the first partition map type found. */ @@ -208,7 +219,12 @@ part_iterate (grub_disk_t dsk, const grub_partition_t partition, void *data) FOR_PARTITION_MAPS(partmap) { grub_err_t err; - err = partmap->iterate (dsk, part_iterate, ctx); + recursion_depth++; + if (recursion_depth <= MAX_RECURSION_DEPTH) + err = partmap->iterate (dsk, part_iterate, ctx); + else + err = grub_error (GRUB_ERR_RECURSION_DEPTH, "maximum recursion depth exceeded"); + recursion_depth--; if (err) grub_errno = GRUB_ERR_NONE; if (ctx->ret) diff --git a/grub-core/kern/powerpc/dl.c b/grub-core/kern/powerpc/dl.c index 7b6418eabd..0eb8bc5bd3 100644 --- a/grub-core/kern/powerpc/dl.c +++ b/grub-core/kern/powerpc/dl.c @@ -167,3 +167,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ + return 1; +} diff --git a/grub-core/kern/rescue_reader.c b/grub-core/kern/rescue_reader.c index dcd7d44397..a71ada8fb7 100644 --- a/grub-core/kern/rescue_reader.c +++ b/grub-core/kern/rescue_reader.c @@ -78,6 +78,19 @@ grub_rescue_read_line (char **line, int cont, void __attribute__ ((noreturn)) grub_rescue_run (void) { + /* Stall if the CLI has been disabled */ + if (grub_is_cli_disabled () || grub_is_cli_need_auth ()) + { + grub_printf ("Rescue mode has been disabled...\n"); + + do + { + /* Do not optimize out the loop. */ + asm volatile (""); + } + while (1); + } + grub_printf ("Entering rescue mode...\n"); while (1) diff --git a/grub-core/kern/riscv/dl.c b/grub-core/kern/riscv/dl.c index 896653bb41..1fa085b4ac 100644 --- a/grub-core/kern/riscv/dl.c +++ b/grub-core/kern/riscv/dl.c @@ -344,3 +344,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ +#ifdef GRUB_MACHINE_EFI + return 4096; +#else + return 1; +#endif +} diff --git a/grub-core/kern/riscv/efi/init.c b/grub-core/kern/riscv/efi/init.c index 38795fe674..0d7de4f541 100644 --- a/grub-core/kern/riscv/efi/init.c +++ b/grub-core/kern/riscv/efi/init.c @@ -33,16 +33,15 @@ grub_efi_get_time_ms (void) grub_uint64_t tmr; #if __riscv_xlen == 64 - asm volatile ("rdcycle %0" : "=r" (tmr)); + asm volatile ("rdtime %0" : "=r"(tmr)); #else grub_uint32_t lo, hi, tmp; - asm volatile ( - "1:\n" - "rdcycleh %0\n" - "rdcycle %1\n" - "rdcycleh %2\n" - "bne %0, %2, 1b" - : "=&r" (hi), "=&r" (lo), "=&r" (tmp)); + asm volatile ("1:\n" + "rdtimeh %0\n" + "rdtime %1\n" + "rdtimeh %2\n" + "bne %0, %2, 1b" + : "=&r" (hi), "=&r" (lo), "=&r" (tmp)); tmr = ((grub_uint64_t)hi << 32) | lo; #endif diff --git a/grub-core/kern/riscv/efi/startup.S b/grub-core/kern/riscv/efi/startup.S index f2a7b2b1ed..781773136e 100644 --- a/grub-core/kern/riscv/efi/startup.S +++ b/grub-core/kern/riscv/efi/startup.S @@ -29,6 +29,7 @@ .file "startup.S" .text +FUNCTION(start) FUNCTION(_start) /* * EFI_SYSTEM_TABLE and EFI_HANDLE are passed in a1/a0. diff --git a/grub-core/kern/sparc64/dl.c b/grub-core/kern/sparc64/dl.c index f3d960186b..f054f08241 100644 --- a/grub-core/kern/sparc64/dl.c +++ b/grub-core/kern/sparc64/dl.c @@ -189,3 +189,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ + return 1; +} diff --git a/grub-core/kern/sparc64/ieee1275/crt0.S b/grub-core/kern/sparc64/ieee1275/crt0.S index 03b916f053..701bf63abc 100644 --- a/grub-core/kern/sparc64/ieee1275/crt0.S +++ b/grub-core/kern/sparc64/ieee1275/crt0.S @@ -22,7 +22,8 @@ .text .align 4 - .globl _start + .globl start, _start +start: _start: ba codestart mov %o4, %o0 diff --git a/grub-core/kern/term.c b/grub-core/kern/term.c index 14d5964983..4d61f4e979 100644 --- a/grub-core/kern/term.c +++ b/grub-core/kern/term.c @@ -144,9 +144,10 @@ grub_key_is_interrupt (int key) /* * ESC sometimes is the BIOS setup hotkey and may be hard to discover, also * check F4, which was chosen because is not used as a hotkey to enter the - * BIOS setup by any vendor. + * BIOS setup by any vendor. Also, F8 which was the key to get the Windows + * bootmenu for a long time. */ - if (key == GRUB_TERM_ESC || key == GRUB_TERM_KEY_F4) + if (key == GRUB_TERM_ESC || key == GRUB_TERM_KEY_F4 || key == GRUB_TERM_KEY_F8) return 1; /* diff --git a/grub-core/kern/uboot/init.c b/grub-core/kern/uboot/init.c index 3e338645c5..be2a5be1d0 100644 --- a/grub-core/kern/uboot/init.c +++ b/grub-core/kern/uboot/init.c @@ -39,9 +39,9 @@ extern grub_size_t grub_total_module_size; static unsigned long timer_start; void -grub_exit (void) +grub_exit (int rc) { - grub_uboot_return (0); + grub_uboot_return (rc < 0 ? 1 : rc); } static grub_uint64_t @@ -78,7 +78,7 @@ grub_machine_init (void) if (!ver) { /* Don't even have a console to log errors to... */ - grub_exit (); + grub_exit (-1); } else if (ver > API_SIG_VERSION) { diff --git a/grub-core/kern/verifiers.c b/grub-core/kern/verifiers.c index 75d7994cf6..21fc752f17 100644 --- a/grub-core/kern/verifiers.c +++ b/grub-core/kern/verifiers.c @@ -22,6 +22,10 @@ #include #include +#if defined (GRUB_MACHINE_EFI) && defined (__x86_64__) +#include +#endif + GRUB_MOD_LICENSE ("GPLv3+"); struct grub_file_verifier *grub_file_verifiers; @@ -38,7 +42,13 @@ verified_free (grub_verified_t verified) { if (verified) { +#if defined (GRUB_MACHINE_EFI) && defined (__x86_64__) + if (verified->buf && verified->file->size) + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)verified->buf, + GRUB_EFI_BYTES_TO_PAGES(verified->file->size)); +#else grub_free (verified->buf); +#endif grub_free (verified); } } @@ -145,7 +155,11 @@ grub_verifiers_open (grub_file_t io, enum grub_file_type type) { goto fail; } +#if defined (GRUB_MACHINE_EFI) && defined (__x86_64__) + verified->buf = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (ret->size)); +#else verified->buf = grub_malloc (ret->size); +#endif if (!verified->buf) { goto fail; diff --git a/grub-core/kern/x86_64/dl.c b/grub-core/kern/x86_64/dl.c index e5a8bdcf4f..a105dc50ce 100644 --- a/grub-core/kern/x86_64/dl.c +++ b/grub-core/kern/x86_64/dl.c @@ -119,3 +119,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, return GRUB_ERR_NONE; } + +/* + * Tell the loader what our minimum section alignment is. + */ +grub_size_t +grub_arch_dl_min_alignment (void) +{ +#ifdef GRUB_MACHINE_EFI + return 4096; +#else + return 1; +#endif +} diff --git a/grub-core/kern/x86_64/efi/startup.S b/grub-core/kern/x86_64/efi/startup.S index 9357e5c5dd..f5c6bc3d8e 100644 --- a/grub-core/kern/x86_64/efi/startup.S +++ b/grub-core/kern/x86_64/efi/startup.S @@ -33,3 +33,8 @@ _start: andq $~0xf, %rsp call EXT_C(grub_main) /* Doesn't return. */ + +/* An executable stack is not required for these functions. */ +#if defined (__linux__) && defined (__ELF__) +.section .note.GNU-stack,"",%progbits +#endif diff --git a/grub-core/kern/xen/init.c b/grub-core/kern/xen/init.c index 782ca72952..708b060f32 100644 --- a/grub-core/kern/xen/init.c +++ b/grub-core/kern/xen/init.c @@ -584,7 +584,7 @@ grub_machine_init (void) } void -grub_exit (void) +grub_exit (int rc __attribute__((unused))) { struct sched_shutdown arg; diff --git a/grub-core/lib/.gitignore b/grub-core/lib/.gitignore new file mode 100644 index 0000000000..6815459140 --- /dev/null +++ b/grub-core/lib/.gitignore @@ -0,0 +1 @@ +/libgcrypt-grub/ diff --git a/grub-core/lib/cmdline.c b/grub-core/lib/cmdline.c index ed0b149dca..8e2294d8ff 100644 --- a/grub-core/lib/cmdline.c +++ b/grub-core/lib/cmdline.c @@ -20,6 +20,12 @@ #include #include +static int +is_hex(char c) +{ + return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + static unsigned int check_arg (char *c, int *has_space) { int space = 0; @@ -27,7 +33,13 @@ static unsigned int check_arg (char *c, int *has_space) while (*c) { - if (*c == '\\' || *c == '\'' || *c == '"') + if (*c == '\\' && *(c+1) == 'x' && is_hex(*(c+2)) && is_hex(*(c+3))) + { + size += 4; + c += 4; + continue; + } + else if (*c == '\\' || *c == '\'' || *c == '"') size++; else if (*c == ' ') space = 1; @@ -86,7 +98,16 @@ grub_create_loader_cmdline (int argc, char *argv[], char *buf, while (*c) { - if (*c == '\\' || *c == '\'' || *c == '"') + if (*c == '\\' && *(c+1) == 'x' && + is_hex(*(c+2)) && is_hex(*(c+3))) + { + *buf++ = *c++; + *buf++ = *c++; + *buf++ = *c++; + *buf++ = *c++; + continue; + } + else if (*c == '\\' || *c == '\'' || *c == '"') *buf++ = '\\'; *buf++ = *c; diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c index 396f764108..d53ddbe2c4 100644 --- a/grub-core/lib/crypto.c +++ b/grub-core/lib/crypto.c @@ -121,6 +121,10 @@ grub_md_unregister (gcry_md_spec_t *cipher) } } +struct gcry_pk_spec *grub_crypto_pk_dsa; +struct gcry_pk_spec *grub_crypto_pk_ecdsa; +struct gcry_pk_spec *grub_crypto_pk_rsa; + void grub_crypto_hash (const gcry_md_spec_t *hash, void *out, const void *in, grub_size_t inlen) diff --git a/grub-core/lib/envblk.c b/grub-core/lib/envblk.c index 2e4e78b132..874506da16 100644 --- a/grub-core/lib/envblk.c +++ b/grub-core/lib/envblk.c @@ -223,6 +223,49 @@ grub_envblk_delete (grub_envblk_t envblk, const char *name) } } +struct get_var_state { + const char * const name; + char * value; + int found; +}; + +static int +get_var (const char * const name, const char * const value, void *statep) +{ + struct get_var_state *state = (struct get_var_state *)statep; + + if (!grub_strcmp(state->name, name)) + { + state->found = 1; + state->value = grub_strdup(value); + if (!state->value) + grub_errno = grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + + return 1; + } + + return 0; +} + +grub_err_t +grub_envblk_get (grub_envblk_t envblk, const char * const name, char ** const value) +{ + struct get_var_state state = { + .name = name, + .value = NULL, + .found = 0, + }; + + grub_envblk_iterate(envblk, (void *)&state, get_var); + + *value = state.value; + + if (state.found && !state.value) + return grub_errno; + + return GRUB_ERR_NONE; +} + void grub_envblk_iterate (grub_envblk_t envblk, void *hook_data, diff --git a/grub-core/lib/fdt.c b/grub-core/lib/fdt.c index 73cfa94a2d..fbf749e1d6 100644 --- a/grub-core/lib/fdt.c +++ b/grub-core/lib/fdt.c @@ -21,8 +21,6 @@ #include #include -GRUB_MOD_LICENSE ("GPLv3+"); - #define FDT_SUPPORTED_VERSION 17 #define FDT_BEGIN_NODE 0x00000001 diff --git a/grub-core/lib/i386/backtrace.c b/grub-core/lib/i386/backtrace.c deleted file mode 100644 index c3e03c7275..0000000000 --- a/grub-core/lib/i386/backtrace.c +++ /dev/null @@ -1,66 +0,0 @@ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2009 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 . - */ - -#include -#include -#include -#include -#include -#include -#include - -#define MAX_STACK_FRAME 102400 - -void -grub_backtrace_pointer (void *ebp) -{ - void *ptr, *nptr; - unsigned i; - - ptr = ebp; - while (1) - { - grub_printf ("%p: ", ptr); - grub_backtrace_print_address (((void **) ptr)[1]); - grub_printf (" ("); - for (i = 0; i < 2; i++) - grub_printf ("%p,", ((void **)ptr) [i + 2]); - grub_printf ("%p)\n", ((void **)ptr) [i + 2]); - nptr = *(void **)ptr; - if (nptr < ptr || (void **) nptr - (void **) ptr > MAX_STACK_FRAME - || nptr == ptr) - { - grub_printf ("Invalid stack frame at %p (%p)\n", ptr, nptr); - break; - } - ptr = nptr; - } -} - -void -grub_backtrace (void) -{ -#ifdef __x86_64__ - asm volatile ("movq %%rbp, %%rdi\n" - "callq *%%rax": :"a"(grub_backtrace_pointer)); -#else - asm volatile ("movl %%ebp, %%eax\n" - "calll *%%ecx": :"c"(grub_backtrace_pointer)); -#endif -} - diff --git a/grub-core/lib/libtasn1/LICENSE b/grub-core/lib/libtasn1/LICENSE new file mode 100644 index 0000000000..e8b3628db9 --- /dev/null +++ b/grub-core/lib/libtasn1/LICENSE @@ -0,0 +1,16 @@ +LICENSING +========= + +The libtasn1 library is released under the GNU Lesser General Public +License (LGPL) version 2.1 or later; see [COPYING.LESSER](doc/COPYING.LESSER) +for the license terms. + +The GNU LGPL applies to the main libtasn1 library, while the +included applications library are under the GNU GPL version 3. +The libtasn1 library is located in the lib directory, while the applications +in src/. + +The documentation in doc/ is under the GNU FDL license 1.3. + +For any copyright year range specified as YYYY-ZZZZ in this package +note that the range specifies every single year in that closed interval. diff --git a/grub-core/lib/libtasn1/README.md b/grub-core/lib/libtasn1/README.md new file mode 100644 index 0000000000..50a8642296 --- /dev/null +++ b/grub-core/lib/libtasn1/README.md @@ -0,0 +1,91 @@ +|Branch|CI system|Status| +|:----:|:-------:|-----:| +|Master|Gitlab|[![build status](https://gitlab.com/gnutls/libtasn1/badges/master/pipeline.svg)](https://gitlab.com/gnutls/libtasn1/commits/master)[![coverage report](https://gitlab.com/gnutls/libtasn1/badges/master/coverage.svg)](https://gnutls.gitlab.io/libtasn1/coverage)| + +# libtasn1 + +This is GNU Libtasn1, a small ASN.1 library. + +The C library (libtasn1.*) is licensed under the GNU Lesser General +Public License version 2.1 or later. See the file COPYING.LIB. + +The command line tool, self tests, examples, and other auxilliary +files, are licensed under the GNU General Public License version 3.0 +or later. See the file COPYING. + +## Building the library + +We require several tools to build the software, including: + +* [Make](https://www.gnu.org/software/make/) +* [Automake](https://www.gnu.org/software/automake/) (use 1.11.3 or later) +* [Autoconf](https://www.gnu.org/software/autoconf/) +* [Libtool](https://www.gnu.org/software/libtool/) +* [Texinfo](https://www.gnu.org/software/texinfo/) +* [help2man](http://www.gnu.org/software/help2man/) +* [Tar](https://www.gnu.org/software/tar/) +* [Gzip](https://www.gnu.org/software/gzip/) +* [bison](https://www.gnu.org/software/bison/) +* [Texlive & epsf](https://www.tug.org/texlive/) (for PDF manual) +* [GTK-DOC](https://www.gtk.org/gtk-doc/) (for API manual) +* [Git](https://git-scm.com/) +* [libabigail](https://pagure.io/libabigail/) (for abi comparison in make dist) +* [Valgrind](https://valgrind.org/) (optional) + +The required software is typically distributed with your operating +system, and the instructions for installing them differ. Here are +some hints: + +gNewSense/Debian/Ubuntu: +``` +sudo apt-get install make git-core autoconf automake libtool +sudo apt-get install texinfo texlive texlive-generic-recommended texlive-extra-utils +sudo apt-get install help2man gtk-doc-tools valgrind abigail-tools +``` + +The next step is to run autoreconf, ./configure, etc: + +``` +$ ./bootstrap +``` + +Then build the project normally: + +``` +$ make +$ make check +``` + +Happy hacking! + + +## Manual + +The manual is in the `doc/` directory of the release. You can also browse +the manual online at: + + - https://gnutls.gitlab.io/libtasn1/ + + +## Code coverage report + +The coverage report is at: + + - https://gnutls.gitlab.io/libtasn1/coverage + + +## Issue trackers + + - [Main issue tracker](https://gitlab.com/gnutls/libtasn1/issues) + - [oss-fuzz found issues](https://bugs.chromium.org/p/oss-fuzz/issues/list?q=libtasn1&can=2) + + +## Homepage + +The project homepage at the gnu site is at: + +http://www.gnu.org/software/libtasn1/ + + +For any copyright year range specified as YYYY-ZZZZ in this package +note that the range specifies every single year in that closed interval. diff --git a/grub-core/lib/libtasn1/lib/coding.c b/grub-core/lib/libtasn1/lib/coding.c new file mode 100644 index 0000000000..52def59836 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/coding.c @@ -0,0 +1,1423 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/*****************************************************/ +/* File: coding.c */ +/* Description: Functions to create a DER coding of */ +/* an ASN1 type. */ +/*****************************************************/ + +#include +#include "parser_aux.h" +#include +#include "element.h" +#include + +#define MAX_TAG_LEN 16 + +#if 0 +/******************************************************/ +/* Function : _asn1_error_description_value_not_found */ +/* Description: creates the ErrorDescription string */ +/* for the ASN1_VALUE_NOT_FOUND error. */ +/* Parameters: */ +/* node: node of the tree where the value is NULL. */ +/* ErrorDescription: string returned. */ +/* Return: */ +/******************************************************/ +static void +_asn1_error_description_value_not_found (asn1_node node, + char *ErrorDescription) +{ + + if (ErrorDescription == NULL) + return; + + Estrcpy (ErrorDescription, ":: value of element '"); + _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription), + ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40); + Estrcat (ErrorDescription, "' not found"); + +} +#endif + +/** + * asn1_length_der: + * @len: value to convert. + * @der: buffer to hold the returned encoding (may be %NULL). + * @der_len: number of meaningful bytes of ANS (der[0]..der[der_len-1]). + * + * Creates the DER encoding of the provided length value. + * The @der buffer must have enough room for the output. The maximum + * length this function will encode is %ASN1_MAX_LENGTH_SIZE. + * + * To know the size of the DER encoding use a %NULL value for @der. + **/ +void +asn1_length_der (unsigned long int len, unsigned char *der, int *der_len) +{ + int k; + unsigned char temp[ASN1_MAX_LENGTH_SIZE]; +#if SIZEOF_UNSIGNED_LONG_INT > 8 + len &= 0xFFFFFFFFFFFFFFFF; +#endif + + if (len < 128) + { + /* short form */ + if (der != NULL) + der[0] = (unsigned char) len; + *der_len = 1; + } + else + { + /* Long form */ + k = 0; + while (len) + { + temp[k++] = len & 0xFF; + len = len >> 8; + } + *der_len = k + 1; + if (der != NULL) + { + der[0] = ((unsigned char) k & 0x7F) + 128; + while (k--) + der[*der_len - 1 - k] = temp[k]; + } + } +} + +/******************************************************/ +/* Function : _asn1_tag_der */ +/* Description: creates the DER coding for the CLASS */ +/* and TAG parameters. */ +/* It is limited by the ASN1_MAX_TAG_SIZE variable */ +/* Parameters: */ +/* class: value to convert. */ +/* tag_value: value to convert. */ +/* ans: string returned. */ +/* ans_len: number of meaningful bytes of ANS */ +/* (ans[0]..ans[ans_len-1]). */ +/* Return: */ +/******************************************************/ +static void +_asn1_tag_der (unsigned char class, unsigned int tag_value, + unsigned char ans[ASN1_MAX_TAG_SIZE], int *ans_len) +{ + int k; + unsigned char temp[ASN1_MAX_TAG_SIZE]; + + if (tag_value < 31) + { + /* short form */ + ans[0] = (class & 0xE0) + ((unsigned char) (tag_value & 0x1F)); + *ans_len = 1; + } + else + { + /* Long form */ + ans[0] = (class & 0xE0) + 31; + k = 0; + while (tag_value != 0) + { + temp[k++] = tag_value & 0x7F; + tag_value >>= 7; + + if (k > ASN1_MAX_TAG_SIZE - 1) + break; /* will not encode larger tags */ + } + *ans_len = k + 1; + while (k--) + ans[*ans_len - 1 - k] = temp[k] + 128; + ans[*ans_len - 1] -= 128; + } +} + +/** + * asn1_octet_der: + * @str: the input data. + * @str_len: STR length (str[0]..str[*str_len-1]). + * @der: encoded string returned. + * @der_len: number of meaningful bytes of DER (der[0]..der[der_len-1]). + * + * Creates a length-value DER encoding for the input data. + * The DER encoding of the input data will be placed in the @der variable. + * + * Note that the OCTET STRING tag is not included in the output. + * + * This function does not return any value because it is expected + * that @der_len will contain enough bytes to store the string + * plus the DER encoding. The DER encoding size can be obtained using + * asn1_length_der(). + **/ +void +asn1_octet_der (const unsigned char *str, int str_len, + unsigned char *der, int *der_len) +{ + int len_len; + + if (der == NULL || str_len < 0) + return; + + asn1_length_der (str_len, der, &len_len); + memcpy (der + len_len, str, str_len); + *der_len = str_len + len_len; +} + + +/** + * asn1_encode_simple_der: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @str: the string data. + * @str_len: the string length + * @tl: the encoded tag and length + * @tl_len: the bytes of the @tl field + * + * Creates the DER encoding for various simple ASN.1 types like strings etc. + * It stores the tag and length in @tl, which should have space for at least + * %ASN1_MAX_TL_SIZE bytes. Initially @tl_len should contain the size of @tl. + * + * The complete DER encoding should consist of the value in @tl appended + * with the provided @str. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + **/ +int +asn1_encode_simple_der (unsigned int etype, const unsigned char *str, + unsigned int str_len, unsigned char *tl, + unsigned int *tl_len) +{ + int tag_len, len_len; + unsigned tlen; + unsigned char der_tag[ASN1_MAX_TAG_SIZE]; + unsigned char der_length[ASN1_MAX_LENGTH_SIZE]; + unsigned char *p; + + if (str == NULL) + return ASN1_VALUE_NOT_VALID; + + if (ETYPE_OK (etype) == 0) + return ASN1_VALUE_NOT_VALID; + + /* doesn't handle constructed classes */ + if (ETYPE_CLASS (etype) != ASN1_CLASS_UNIVERSAL) + return ASN1_VALUE_NOT_VALID; + + _asn1_tag_der (ETYPE_CLASS (etype), ETYPE_TAG (etype), der_tag, &tag_len); + + asn1_length_der (str_len, der_length, &len_len); + + if (tag_len <= 0 || len_len <= 0) + return ASN1_VALUE_NOT_VALID; + + tlen = tag_len + len_len; + + if (*tl_len < tlen) + return ASN1_MEM_ERROR; + + p = tl; + memcpy (p, der_tag, tag_len); + p += tag_len; + memcpy (p, der_length, len_len); + + *tl_len = tlen; + + return ASN1_SUCCESS; +} + +#if 0 +/******************************************************/ +/* Function : _asn1_time_der */ +/* Description: creates the DER coding for a TIME */ +/* type (length included). */ +/* Parameters: */ +/* str: TIME null-terminated string. */ +/* der: string returned. */ +/* der_len: number of meaningful bytes of DER */ +/* (der[0]..der[ans_len-1]). Initially it */ +/* if must store the lenght of DER. */ +/* Return: */ +/* ASN1_MEM_ERROR when DER isn't big enough */ +/* ASN1_SUCCESS otherwise */ +/******************************************************/ +static int +_asn1_time_der (unsigned char *str, int str_len, unsigned char *der, + int *der_len) +{ + int len_len; + int max_len; + + if (der == NULL) + return ASN1_VALUE_NOT_VALID; + + max_len = *der_len; + + asn1_length_der (str_len, (max_len > 0) ? der : NULL, &len_len); + + if ((len_len + str_len) <= max_len) + memcpy (der + len_len, str, str_len); + *der_len = len_len + str_len; + + if ((*der_len) > max_len) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} +#endif + +/* +void +_asn1_get_utctime_der(unsigned char *der,int *der_len,unsigned char *str) +{ + int len_len,str_len; + char temp[20]; + + if(str==NULL) return; + str_len=asn1_get_length_der(der,*der_len,&len_len); + if (str_len<0) return; + memcpy(temp,der+len_len,str_len); + *der_len=str_len+len_len; + switch(str_len) + { + case 11: + temp[10]=0; + strcat(temp,"00+0000"); + break; + case 13: + temp[12]=0; + strcat(temp,"+0000"); + break; + case 15: + temp[15]=0; + memmove(temp+12,temp+10,6); + temp[10]=temp[11]='0'; + break; + case 17: + temp[17]=0; + break; + default: + return; + } + strcpy(str,temp); +} +*/ + +static +void encode_val(uint64_t val, unsigned char *der, int max_len, int *der_len) +{ + int first, k; + unsigned char bit7; + + first = 0; + for (k = sizeof(val); k >= 0; k--) + { + bit7 = (val >> (k * 7)) & 0x7F; + if (bit7 || first || !k) + { + if (k) + bit7 |= 0x80; + if (max_len > (*der_len)) + der[*der_len] = bit7; + (*der_len)++; + first = 1; + } + } +} + +/******************************************************/ +/* Function : _asn1_object_id_der */ +/* Description: creates the DER coding for an */ +/* OBJECT IDENTIFIER type (length included). */ +/* Parameters: */ +/* str: OBJECT IDENTIFIER null-terminated string. */ +/* der: string returned. */ +/* der_len: number of meaningful bytes of DER */ +/* (der[0]..der[ans_len-1]). Initially it */ +/* must store the length of DER. */ +/* Return: */ +/* ASN1_MEM_ERROR when DER isn't big enough */ +/* ASN1_SUCCESS if succesful */ +/* or an error value. */ +/******************************************************/ +static int +_asn1_object_id_der (const char *str, unsigned char *der, int *der_len) +{ + int len_len, counter, max_len; + char *temp, *n_end, *n_start; + uint64_t val, val1 = 0; + int str_len = _asn1_strlen (str); + + max_len = *der_len; + *der_len = 0; + + if (der == NULL && max_len > 0) + return ASN1_VALUE_NOT_VALID; + + temp = malloc (str_len + 2); + if (temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + memcpy (temp, str, str_len); + temp[str_len] = '.'; + temp[str_len + 1] = 0; + + counter = 0; + n_start = temp; + while ((n_end = strchr (n_start, '.'))) + { + *n_end = 0; + val = _asn1_strtou64 (n_start, NULL, 10); + counter++; + + if (counter == 1) + { + val1 = val; + } + else if (counter == 2) + { + uint64_t val0; + + if (val1 > 2) + { + free(temp); + return ASN1_VALUE_NOT_VALID; + } + else if ((val1 == 0 || val1 == 1) && val > 39) + { + free(temp); + return ASN1_VALUE_NOT_VALID; + } + + val0 = 40 * val1 + val; + encode_val(val0, der, max_len, der_len); + } + else + { + encode_val(val, der, max_len, der_len); + } + n_start = n_end + 1; + } + + asn1_length_der (*der_len, NULL, &len_len); + if (max_len >= (*der_len + len_len)) + { + memmove (der + len_len, der, *der_len); + asn1_length_der (*der_len, der, &len_len); + } + *der_len += len_len; + + free (temp); + + if (max_len < (*der_len)) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} + +/** + * asn1_object_id_der: + * @str: An object identifier in numeric, dot format. + * @der: buffer to hold the returned encoding (may be %NULL). + * @der_len: initially the size of @der; will hold the final size. + * @flags: must be zero + * + * Creates the DER encoding of the provided object identifier. + * + * Returns: %ASN1_SUCCESS if DER encoding was OK, %ASN1_VALUE_NOT_VALID + * if @str is not a valid OID, %ASN1_MEM_ERROR if the @der + * vector isn't big enough and in this case @der_len will contain the + * length needed. + **/ +int asn1_object_id_der(const char *str, unsigned char *der, int *der_len, unsigned flags) +{ + unsigned char tag_der[MAX_TAG_LEN]; + int tag_len = 0, r; + int max_len = *der_len; + + *der_len = 0; + + _asn1_tag_der (ETYPE_CLASS (ASN1_ETYPE_OBJECT_ID), ETYPE_TAG (ASN1_ETYPE_OBJECT_ID), + tag_der, &tag_len); + + if (max_len > tag_len) + { + memcpy(der, tag_der, tag_len); + } + max_len -= tag_len; + der += tag_len; + + r = _asn1_object_id_der (str, der, &max_len); + if (r == ASN1_MEM_ERROR || r == ASN1_SUCCESS) + { + *der_len = max_len + tag_len; + } + + return r; +} + +static const unsigned char bit_mask[] = + { 0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80 }; + +/** + * asn1_bit_der: + * @str: BIT string. + * @bit_len: number of meaningful bits in STR. + * @der: string returned. + * @der_len: number of meaningful bytes of DER + * (der[0]..der[ans_len-1]). + * + * Creates a length-value DER encoding for the input data + * as it would have been for a BIT STRING. + * The DER encoded data will be copied in @der. + * + * Note that the BIT STRING tag is not included in the output. + * + * This function does not return any value because it is expected + * that @der_len will contain enough bytes to store the string + * plus the DER encoding. The DER encoding size can be obtained using + * asn1_length_der(). + **/ +void +asn1_bit_der (const unsigned char *str, int bit_len, + unsigned char *der, int *der_len) +{ + int len_len, len_byte, len_pad; + + if (der == NULL) + return; + + len_byte = bit_len >> 3; + len_pad = 8 - (bit_len & 7); + if (len_pad == 8) + len_pad = 0; + else + len_byte++; + asn1_length_der (len_byte + 1, der, &len_len); + der[len_len] = len_pad; + + if (str) + memcpy (der + len_len + 1, str, len_byte); + der[len_len + len_byte] &= bit_mask[len_pad]; + *der_len = len_byte + len_len + 1; +} + + +#if 0 +/******************************************************/ +/* Function : _asn1_complete_explicit_tag */ +/* Description: add the length coding to the EXPLICIT */ +/* tags. */ +/* Parameters: */ +/* node: pointer to the tree element. */ +/* der: string with the DER coding of the whole tree*/ +/* counter: number of meaningful bytes of DER */ +/* (der[0]..der[*counter-1]). */ +/* max_len: size of der vector */ +/* Return: */ +/* ASN1_MEM_ERROR if der vector isn't big enough, */ +/* otherwise ASN1_SUCCESS. */ +/******************************************************/ +static int +_asn1_complete_explicit_tag (asn1_node node, unsigned char *der, + int *counter, int *max_len) +{ + asn1_node p; + int is_tag_implicit, len2, len3; + unsigned char temp[SIZEOF_UNSIGNED_INT]; + + if (der == NULL && *max_len > 0) + return ASN1_VALUE_NOT_VALID; + + is_tag_implicit = 0; + + if (node->type & CONST_TAG) + { + p = node->down; + if (p == NULL) + return ASN1_DER_ERROR; + /* When there are nested tags we must complete them reverse to + the order they were created. This is because completing a tag + modifies all data within it, including the incomplete tags + which store buffer positions -- simon@josefsson.org 2002-09-06 + */ + while (p->right) + p = p->right; + while (p && p != node->down->left) + { + if (type_field (p->type) == ASN1_ETYPE_TAG) + { + if (p->type & CONST_EXPLICIT) + { + len2 = strtol (p->name, NULL, 10); + _asn1_set_name (p, NULL); + + asn1_length_der (*counter - len2, temp, &len3); + if (len3 <= (*max_len)) + { + memmove (der + len2 + len3, der + len2, + *counter - len2); + memcpy (der + len2, temp, len3); + } + *max_len -= len3; + *counter += len3; + is_tag_implicit = 0; + } + else + { /* CONST_IMPLICIT */ + if (!is_tag_implicit) + { + is_tag_implicit = 1; + } + } + } + p = p->left; + } + } + + if (*max_len < 0) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} +#endif + +const tag_and_class_st _asn1_tags[] = { + [ASN1_ETYPE_GENERALSTRING] = + {ASN1_TAG_GENERALSTRING, ASN1_CLASS_UNIVERSAL, "type:GENERALSTRING"}, + [ASN1_ETYPE_NUMERIC_STRING] = + {ASN1_TAG_NUMERIC_STRING, ASN1_CLASS_UNIVERSAL, "type:NUMERIC_STR"}, + [ASN1_ETYPE_IA5_STRING] = + {ASN1_TAG_IA5_STRING, ASN1_CLASS_UNIVERSAL, "type:IA5_STR"}, + [ASN1_ETYPE_TELETEX_STRING] = + {ASN1_TAG_TELETEX_STRING, ASN1_CLASS_UNIVERSAL, "type:TELETEX_STR"}, + [ASN1_ETYPE_PRINTABLE_STRING] = + {ASN1_TAG_PRINTABLE_STRING, ASN1_CLASS_UNIVERSAL, "type:PRINTABLE_STR"}, + [ASN1_ETYPE_UNIVERSAL_STRING] = + {ASN1_TAG_UNIVERSAL_STRING, ASN1_CLASS_UNIVERSAL, "type:UNIVERSAL_STR"}, + [ASN1_ETYPE_BMP_STRING] = + {ASN1_TAG_BMP_STRING, ASN1_CLASS_UNIVERSAL, "type:BMP_STR"}, + [ASN1_ETYPE_UTF8_STRING] = + {ASN1_TAG_UTF8_STRING, ASN1_CLASS_UNIVERSAL, "type:UTF8_STR"}, + [ASN1_ETYPE_VISIBLE_STRING] = + {ASN1_TAG_VISIBLE_STRING, ASN1_CLASS_UNIVERSAL, "type:VISIBLE_STR"}, + [ASN1_ETYPE_OCTET_STRING] = + {ASN1_TAG_OCTET_STRING, ASN1_CLASS_UNIVERSAL, "type:OCT_STR"}, + [ASN1_ETYPE_BIT_STRING] = + {ASN1_TAG_BIT_STRING, ASN1_CLASS_UNIVERSAL, "type:BIT_STR"}, + [ASN1_ETYPE_OBJECT_ID] = + {ASN1_TAG_OBJECT_ID, ASN1_CLASS_UNIVERSAL, "type:OBJ_ID"}, + [ASN1_ETYPE_NULL] = {ASN1_TAG_NULL, ASN1_CLASS_UNIVERSAL, "type:NULL"}, + [ASN1_ETYPE_BOOLEAN] = + {ASN1_TAG_BOOLEAN, ASN1_CLASS_UNIVERSAL, "type:BOOLEAN"}, + [ASN1_ETYPE_INTEGER] = + {ASN1_TAG_INTEGER, ASN1_CLASS_UNIVERSAL, "type:INTEGER"}, + [ASN1_ETYPE_ENUMERATED] = + {ASN1_TAG_ENUMERATED, ASN1_CLASS_UNIVERSAL, "type:ENUMERATED"}, + [ASN1_ETYPE_SEQUENCE] = + {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, + "type:SEQUENCE"}, + [ASN1_ETYPE_SEQUENCE_OF] = + {ASN1_TAG_SEQUENCE, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, + "type:SEQ_OF"}, + [ASN1_ETYPE_SET] = + {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, "type:SET"}, + [ASN1_ETYPE_SET_OF] = + {ASN1_TAG_SET, ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED, + "type:SET_OF"}, + [ASN1_ETYPE_GENERALIZED_TIME] = + {ASN1_TAG_GENERALIZEDTime, ASN1_CLASS_UNIVERSAL, "type:GENERALIZED_TIME"}, + [ASN1_ETYPE_UTC_TIME] = + {ASN1_TAG_UTCTime, ASN1_CLASS_UNIVERSAL, "type:UTC_TIME"}, +}; + +unsigned int _asn1_tags_size = sizeof (_asn1_tags) / sizeof (_asn1_tags[0]); + + +#if 0 +/******************************************************/ +/* Function : _asn1_insert_tag_der */ +/* Description: creates the DER coding of tags of one */ +/* NODE. */ +/* Parameters: */ +/* node: pointer to the tree element. */ +/* der: string returned */ +/* counter: number of meaningful bytes of DER */ +/* (counter[0]..der[*counter-1]). */ +/* max_len: size of der vector */ +/* Return: */ +/* ASN1_GENERIC_ERROR if the type is unknown, */ +/* ASN1_MEM_ERROR if der vector isn't big enough, */ +/* otherwise ASN1_SUCCESS. */ +/******************************************************/ +static int +_asn1_insert_tag_der (asn1_node node, unsigned char *der, int *counter, + int *max_len) +{ + asn1_node p; + int tag_len, is_tag_implicit; + unsigned char class, class_implicit = 0, temp[MAX(SIZEOF_UNSIGNED_INT * 3 + 1, LTOSTR_MAX_SIZE)]; + unsigned long tag_implicit = 0; + unsigned char tag_der[MAX_TAG_LEN]; + + is_tag_implicit = 0; + + if (node->type & CONST_TAG) + { + p = node->down; + while (p) + { + if (type_field (p->type) == ASN1_ETYPE_TAG) + { + if (p->type & CONST_APPLICATION) + class = ASN1_CLASS_APPLICATION; + else if (p->type & CONST_UNIVERSAL) + class = ASN1_CLASS_UNIVERSAL; + else if (p->type & CONST_PRIVATE) + class = ASN1_CLASS_PRIVATE; + else + class = ASN1_CLASS_CONTEXT_SPECIFIC; + + if (p->type & CONST_EXPLICIT) + { + if (is_tag_implicit) + _asn1_tag_der (class_implicit, tag_implicit, tag_der, + &tag_len); + else + _asn1_tag_der (class | ASN1_CLASS_STRUCTURED, + _asn1_strtoul (p->value, NULL, 10), + tag_der, &tag_len); + + *max_len -= tag_len; + if (der && *max_len >= 0) + memcpy (der + *counter, tag_der, tag_len); + *counter += tag_len; + + _asn1_ltostr (*counter, (char *) temp); + _asn1_set_name (p, (const char *) temp); + + is_tag_implicit = 0; + } + else + { /* CONST_IMPLICIT */ + if (!is_tag_implicit) + { + if ((type_field (node->type) == ASN1_ETYPE_SEQUENCE) || + (type_field (node->type) == ASN1_ETYPE_SEQUENCE_OF) + || (type_field (node->type) == ASN1_ETYPE_SET) + || (type_field (node->type) == ASN1_ETYPE_SET_OF)) + class |= ASN1_CLASS_STRUCTURED; + class_implicit = class; + tag_implicit = _asn1_strtoul (p->value, NULL, 10); + is_tag_implicit = 1; + } + } + } + p = p->right; + } + } + + if (is_tag_implicit) + { + _asn1_tag_der (class_implicit, tag_implicit, tag_der, &tag_len); + } + else + { + unsigned type = type_field (node->type); + switch (type) + { + CASE_HANDLED_ETYPES: + _asn1_tag_der (_asn1_tags[type].class, _asn1_tags[type].tag, + tag_der, &tag_len); + break; + case ASN1_ETYPE_TAG: + case ASN1_ETYPE_CHOICE: + case ASN1_ETYPE_ANY: + tag_len = 0; + break; + default: + return ASN1_GENERIC_ERROR; + } + } + + *max_len -= tag_len; + if (der && *max_len >= 0) + memcpy (der + *counter, tag_der, tag_len); + *counter += tag_len; + + if (*max_len < 0) + return ASN1_MEM_ERROR; + + return ASN1_SUCCESS; +} + +/******************************************************/ +/* Function : _asn1_ordering_set */ +/* Description: puts the elements of a SET type in */ +/* the correct order according to DER rules. */ +/* Parameters: */ +/* der: string with the DER coding. */ +/* node: pointer to the SET element. */ +/* Return: */ +/* ASN1_SUCCESS if successful */ +/* or an error value. */ +/******************************************************/ +static int +_asn1_ordering_set (unsigned char *der, int der_len, asn1_node node) +{ + struct vet + { + int end; + unsigned long value; + struct vet *next, *prev; + }; + + int counter, len, len2; + struct vet *first, *last, *p_vet, *p2_vet; + asn1_node p; + unsigned char class, *temp; + unsigned long tag, t; + int err; + + counter = 0; + + if (type_field (node->type) != ASN1_ETYPE_SET) + return ASN1_VALUE_NOT_VALID; + + p = node->down; + while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) || + (type_field (p->type) == ASN1_ETYPE_SIZE))) + p = p->right; + + if ((p == NULL) || (p->right == NULL)) + return ASN1_SUCCESS; + + first = last = NULL; + while (p) + { + p_vet = malloc (sizeof (struct vet)); + if (p_vet == NULL) + { + err = ASN1_MEM_ALLOC_ERROR; + goto error; + } + + p_vet->next = NULL; + p_vet->prev = last; + if (first == NULL) + first = p_vet; + else + last->next = p_vet; + last = p_vet; + + /* tag value calculation */ + err = asn1_get_tag_der (der + counter, der_len - counter, &class, &len2, + &tag); + if (err != ASN1_SUCCESS) + goto error; + + t = ((unsigned int)class) << 24; + p_vet->value = t | tag; + counter += len2; + + /* extraction and length */ + len2 = asn1_get_length_der (der + counter, der_len - counter, &len); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + counter += len + len2; + + p_vet->end = counter; + p = p->right; + } + + p_vet = first; + + while (p_vet) + { + p2_vet = p_vet->next; + counter = 0; + while (p2_vet) + { + if (p_vet->value > p2_vet->value) + { + /* change position */ + temp = malloc (p_vet->end - counter); + if (temp == NULL) + { + err = ASN1_MEM_ALLOC_ERROR; + goto error; + } + + memcpy (temp, der + counter, p_vet->end - counter); + memcpy (der + counter, der + p_vet->end, + p2_vet->end - p_vet->end); + memcpy (der + counter + p2_vet->end - p_vet->end, temp, + p_vet->end - counter); + free (temp); + + tag = p_vet->value; + p_vet->value = p2_vet->value; + p2_vet->value = tag; + + p_vet->end = counter + (p2_vet->end - p_vet->end); + } + counter = p_vet->end; + + p2_vet = p2_vet->next; + p_vet = p_vet->next; + } + + if (p_vet != first) + p_vet->prev->next = NULL; + else + first = NULL; + free (p_vet); + p_vet = first; + } + return ASN1_SUCCESS; + +error: + while (first != NULL) + { + p_vet = first; + first = first->next; + free(p_vet); + } + return err; +} + +struct vet +{ + unsigned char *ptr; + int size; +}; + +static int setof_compar(const void *_e1, const void *_e2) +{ + unsigned length; + const struct vet *e1 = _e1, *e2 = _e2; + int rval; + + /* The encodings of the component values of a set-of value shall + * appear in ascending order, the encodings being compared + * as octet strings with the shorter components being + * padded at their trailing end with 0-octets. + * The padding octets are for comparison purposes and + * do not appear in the encodings. + */ + length = MIN(e1->size, e2->size); + + rval = memcmp(e1->ptr, e2->ptr, length); + if (rval == 0 && e1->size != e2->size) + { + if (e1->size > e2->size) + rval = 1; + else if (e2->size > e1->size) + rval = -1; + } + + return rval; +} + +/******************************************************/ +/* Function : _asn1_ordering_set_of */ +/* Description: puts the elements of a SET OF type in */ +/* the correct order according to DER rules. */ +/* Parameters: */ +/* der: string with the DER coding. */ +/* node: pointer to the SET OF element. */ +/* Return: */ +/* ASN1_SUCCESS if successful */ +/* or an error value. */ +/******************************************************/ +static int +_asn1_ordering_set_of (unsigned char *der, int der_len, asn1_node node) +{ + int counter, len, len2; + struct vet *list = NULL, *tlist; + unsigned list_size = 0; + struct vet *p_vet; + asn1_node p; + unsigned char class; + unsigned i; + unsigned char *out = NULL; + int err; + + if (der == NULL) + return ASN1_VALUE_NOT_VALID; + + counter = 0; + + if (type_field (node->type) != ASN1_ETYPE_SET_OF) + return ASN1_VALUE_NOT_VALID; + + p = node->down; + while (p && ((type_field (p->type) == ASN1_ETYPE_TAG) || + (type_field (p->type) == ASN1_ETYPE_SIZE))) + p = p->right; + if (p == NULL) + return ASN1_VALUE_NOT_VALID; + p = p->right; + + if ((p == NULL) || (p->right == NULL)) + return ASN1_SUCCESS; + + while (p) + { + list_size++; + tlist = realloc (list, list_size*sizeof(struct vet)); + if (tlist == NULL) + { + err = ASN1_MEM_ALLOC_ERROR; + goto error; + } + list = tlist; + p_vet = &list[list_size-1]; + + p_vet->ptr = der+counter; + p_vet->size = 0; + + /* extraction of tag and length */ + if (der_len - counter > 0) + { + err = asn1_get_tag_der (der + counter, der_len - counter, &class, + &len, NULL); + if (err != ASN1_SUCCESS) + goto error; + counter += len; + p_vet->size += len; + + len2 = asn1_get_length_der (der + counter, der_len - counter, &len); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + counter += len + len2; + p_vet->size += len + len2; + + } + else + { + err = ASN1_DER_ERROR; + goto error; + } + p = p->right; + } + + if (counter > der_len) + { + err = ASN1_DER_ERROR; + goto error; + } + + qsort(list, list_size, sizeof(struct vet), setof_compar); + + out = malloc(der_len); + if (out == NULL) + { + err = ASN1_MEM_ERROR; + goto error; + } + + /* the sum of p_vet->size == der_len */ + counter = 0; + for (i = 0; i < list_size; i++) + { + p_vet = &list[i]; + memcpy(out+counter, p_vet->ptr, p_vet->size); + counter += p_vet->size; + } + memcpy(der, out, der_len); + free(out); + + err = ASN1_SUCCESS; + +error: + free(list); + return err; +} + +/** + * asn1_der_coding: + * @element: pointer to an ASN1 element + * @name: the name of the structure you want to encode (it must be + * inside *POINTER). + * @ider: vector that will contain the DER encoding. DER must be a + * pointer to memory cells already allocated. + * @len: number of bytes of *@ider: @ider[0]..@ider[len-1], Initialy + * holds the sizeof of der vector. + * @ErrorDescription: return the error description or an empty + * string if success. + * + * Creates the DER encoding for the NAME structure (inside *POINTER + * structure). + * + * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND + * if @name is not a valid element, %ASN1_VALUE_NOT_FOUND if there + * is an element without a value, %ASN1_MEM_ERROR if the @ider + * vector isn't big enough and in this case @len will contain the + * length needed. + **/ +int +asn1_der_coding (asn1_node_const element, const char *name, void *ider, int *len, + char *ErrorDescription) +{ + asn1_node node, p, p2; + unsigned char temp[MAX(LTOSTR_MAX_SIZE, SIZEOF_UNSIGNED_LONG_INT * 3 + 1)]; + int counter, counter_old, len2, len3, move, max_len, max_len_old; + int err; + unsigned char *der = ider; + + if (ErrorDescription) + ErrorDescription[0] = 0; + + node = asn1_find_node (element, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + /* Node is now a locally allocated variable. + * That is because in some point we modify the + * structure, and I don't know why! --nmav + */ + node = _asn1_copy_structure3 (node); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + max_len = *len; + + if (der == NULL && max_len > 0) + return ASN1_VALUE_NOT_VALID; + + counter = 0; + move = DOWN; + p = node; + + while (1) + { + + counter_old = counter; + max_len_old = max_len; + if (move != UP) + { + p->start = counter; + err = _asn1_insert_tag_der (p, der, &counter, &max_len); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + } + switch (type_field (p->type)) + { + case ASN1_ETYPE_NULL: + max_len--; + if (der != NULL && max_len >= 0) + der[counter] = 0; + counter++; + move = RIGHT; + break; + case ASN1_ETYPE_BOOLEAN: + if ((p->type & CONST_DEFAULT) && (p->value == NULL)) + { + counter = counter_old; + max_len = max_len_old; + } + else + { + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, + ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + max_len -= 2; + if (der != NULL && max_len >= 0) + { + der[counter++] = 1; + if (p->value[0] == 'F') + der[counter++] = 0; + else + der[counter++] = 0xFF; + } + else + counter += 2; + } + move = RIGHT; + break; + case ASN1_ETYPE_INTEGER: + case ASN1_ETYPE_ENUMERATED: + if ((p->type & CONST_DEFAULT) && (p->value == NULL)) + { + counter = counter_old; + max_len = max_len_old; + } + else + { + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, + ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + max_len -= len2 + len3; + if (der != NULL && max_len >= 0) + memcpy (der + counter, p->value, len3 + len2); + counter += len3 + len2; + } + move = RIGHT; + break; + case ASN1_ETYPE_OBJECT_ID: + if ((p->type & CONST_DEFAULT) && (p->value == NULL)) + { + counter = counter_old; + max_len = max_len_old; + } + else + { + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, + ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = max_len; + err = _asn1_object_id_der ((char*)p->value, der + counter, &len2); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + + max_len -= len2; + counter += len2; + } + move = RIGHT; + break; + case ASN1_ETYPE_GENERALIZED_TIME: + case ASN1_ETYPE_UTC_TIME: + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = max_len; + err = _asn1_time_der (p->value, p->value_len, der + counter, &len2); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + + max_len -= len2; + counter += len2; + move = RIGHT; + break; + case ASN1_ETYPE_OCTET_STRING: + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + case ASN1_ETYPE_BIT_STRING: + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + max_len -= len2 + len3; + if (der != NULL && max_len >= 0) + memcpy (der + counter, p->value, len3 + len2); + counter += len3 + len2; + move = RIGHT; + break; + case ASN1_ETYPE_SEQUENCE: + case ASN1_ETYPE_SET: + if (move != UP) + { + p->tmp_ival = counter; + if (p->down == NULL) + { + move = UP; + continue; + } + else + { + p2 = p->down; + while (p2 && (type_field (p2->type) == ASN1_ETYPE_TAG)) + p2 = p2->right; + if (p2) + { + p = p2; + move = RIGHT; + continue; + } + move = UP; + continue; + } + } + else + { /* move==UP */ + len2 = p->tmp_ival; + p->tmp_ival = 0; + if ((type_field (p->type) == ASN1_ETYPE_SET) && (max_len >= 0)) + { + err = _asn1_ordering_set (der + len2, counter - len2, p); + if (err != ASN1_SUCCESS) + goto error; + } + asn1_length_der (counter - len2, temp, &len3); + max_len -= len3; + if (der != NULL && max_len >= 0) + { + memmove (der + len2 + len3, der + len2, counter - len2); + memcpy (der + len2, temp, len3); + } + counter += len3; + move = RIGHT; + } + break; + case ASN1_ETYPE_SEQUENCE_OF: + case ASN1_ETYPE_SET_OF: + if (move != UP) + { + p->tmp_ival = counter; + p = p->down; + while ((type_field (p->type) == ASN1_ETYPE_TAG) + || (type_field (p->type) == ASN1_ETYPE_SIZE)) + p = p->right; + if (p->right) + { + p = p->right; + move = RIGHT; + continue; + } + else + p = _asn1_find_up (p); + move = UP; + } + if (move == UP) + { + len2 = p->tmp_ival; + p->tmp_ival = 0; + if ((type_field (p->type) == ASN1_ETYPE_SET_OF) + && (counter - len2 > 0) && (max_len >= 0)) + { + err = _asn1_ordering_set_of (der + len2, counter - len2, p); + if (err != ASN1_SUCCESS) + goto error; + } + asn1_length_der (counter - len2, temp, &len3); + max_len -= len3; + if (der != NULL && max_len >= 0) + { + memmove (der + len2 + len3, der + len2, counter - len2); + memcpy (der + len2, temp, len3); + } + counter += len3; + move = RIGHT; + } + break; + case ASN1_ETYPE_ANY: + if (p->value == NULL) + { + _asn1_error_description_value_not_found (p, ErrorDescription); + err = ASN1_VALUE_NOT_FOUND; + goto error; + } + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + if (len2 < 0) + { + err = ASN1_DER_ERROR; + goto error; + } + max_len -= len2; + if (der != NULL && max_len >= 0) + memcpy (der + counter, p->value + len3, len2); + counter += len2; + move = RIGHT; + break; + default: + move = (move == UP) ? RIGHT : DOWN; + break; + } + + if ((move != DOWN) && (counter != counter_old)) + { + p->end = counter - 1; + err = _asn1_complete_explicit_tag (p, der, &counter, &max_len); + if (err != ASN1_SUCCESS && err != ASN1_MEM_ERROR) + goto error; + } + + if (p == node && move != DOWN) + break; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + if (move == RIGHT) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + *len = counter; + + if (max_len < 0) + { + err = ASN1_MEM_ERROR; + goto error; + } + + err = ASN1_SUCCESS; + +error: + asn1_delete_structure (&node); + return err; +} + +#endif \ No newline at end of file diff --git a/grub-core/lib/libtasn1/lib/decoding.c b/grub-core/lib/libtasn1/lib/decoding.c new file mode 100644 index 0000000000..7856858b27 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/decoding.c @@ -0,0 +1,2481 @@ +/* + * Copyright (C) 2002-2016 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/*****************************************************/ +/* File: decoding.c */ +/* Description: Functions to manage DER decoding */ +/*****************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#define c_isdigit grub_isdigit + +#ifdef DEBUG +# define warn() fprintf(stderr, "%s: %d\n", __func__, __LINE__) +#else +# define warn() +#endif + +#define IS_ERR(len, flags) (len < -1 || ((flags & ASN1_DECODE_FLAG_STRICT_DER) && len < 0)) + +#define HAVE_TWO(x) (x>=2?1:0) + +/* Decoding flags (dflags) used in several decoding functions. + * DECODE_FLAG_HAVE_TAG: The provided buffer includes a tag + * DECODE_FLAG_CONSTRUCTED: The provided buffer is of indefinite encoding (useful + * when no tags are present). + * DECODE_FLAG_LEVEL1: Internal flag to indicate a level of recursion for BER strings. + * DECODE_FLAG_LEVEL2: Internal flag to indicate two levels of recursion for BER strings. + * DECODE_FLAG_LEVEL3: Internal flag to indicate three levels of recursion for BER strings. + * This is the maximum levels of recursion possible to prevent stack + * exhaustion. + */ + +#define DECODE_FLAG_HAVE_TAG 1 +#define DECODE_FLAG_CONSTRUCTED (1<<1) +#define DECODE_FLAG_LEVEL1 (1<<2) +#define DECODE_FLAG_LEVEL2 (1<<3) +#define DECODE_FLAG_LEVEL3 (1<<4) + +#define DECR_LEN(l, s) do { \ + l -= s; \ + if (l < 0) { \ + warn(); \ + result = ASN1_DER_ERROR; \ + goto cleanup; \ + } \ + } while (0) + +static int +_asn1_get_indefinite_length_string (const unsigned char *der, int der_len, int *len); + +static int +_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, + unsigned int _der_len, unsigned char **str, + unsigned int *str_len, unsigned int *ber_len, + unsigned dflags); + +static int +_asn1_decode_simple_der (unsigned int etype, const unsigned char *der, + unsigned int _der_len, const unsigned char **str, + unsigned int *str_len, unsigned dflags); + +static void +_asn1_error_description_tag_error (asn1_node node, char *ErrorDescription) +{ + + Estrcpy (ErrorDescription, ":: tag error near element '"); + _asn1_hierarchical_name (node, ErrorDescription + strlen (ErrorDescription), + ASN1_MAX_ERROR_DESCRIPTION_SIZE - 40); + Estrcat (ErrorDescription, "'"); + +} + +/** + * asn1_get_length_der: + * @der: DER data to decode. + * @der_len: Length of DER data to decode. + * @len: Output variable containing the length of the DER length field. + * + * Extract a length field from DER data. + * + * Returns: Return the decoded length value, or -1 on indefinite + * length, or -2 when the value was too big to fit in a int, or -4 + * when the decoded length value plus @len would exceed @der_len. + **/ +long +asn1_get_length_der (const unsigned char *der, int der_len, int *len) +{ + unsigned int ans; + int k, punt, sum; + + *len = 0; + if (der_len <= 0) + return 0; + + if (!(der[0] & 128)) + { + /* short form */ + *len = 1; + ans = der[0]; + } + else + { + /* Long form */ + k = der[0] & 0x7F; + punt = 1; + if (k) + { /* definite length method */ + ans = 0; + while (punt <= k && punt < der_len) + { + if (INT_MULTIPLY_OVERFLOW (ans, 256)) + return -2; + ans *= 256; + + if (INT_ADD_OVERFLOW (ans, ((unsigned) der[punt]))) + return -2; + ans += der[punt]; + punt++; + } + } + else + { /* indefinite length method */ + *len = punt; + return -1; + } + + *len = punt; + } + + sum = ans; + if (ans >= INT_MAX || INT_ADD_OVERFLOW (sum, (*len))) + return -2; + sum += *len; + + if (sum > der_len) + return -4; + + return ans; +} + +/** + * asn1_get_tag_der: + * @der: DER data to decode. + * @der_len: Length of DER data to decode. + * @cls: Output variable containing decoded class. + * @len: Output variable containing the length of the DER TAG data. + * @tag: Output variable containing the decoded tag (may be %NULL). + * + * Decode the class and TAG from DER code. + * + * Returns: Returns %ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_tag_der (const unsigned char *der, int der_len, + unsigned char *cls, int *len, unsigned long *tag) +{ + unsigned int ris; + int punt; + + if (der == NULL || der_len < 2 || len == NULL) + return ASN1_DER_ERROR; + + *cls = der[0] & 0xE0; + if ((der[0] & 0x1F) != 0x1F) + { + /* short form */ + *len = 1; + ris = der[0] & 0x1F; + } + else + { + /* Long form */ + punt = 1; + ris = 0; + while (punt < der_len && der[punt] & 128) + { + + if (INT_MULTIPLY_OVERFLOW (ris, 128)) + return ASN1_DER_ERROR; + ris *= 128; + + if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F)))) + return ASN1_DER_ERROR; + ris += (der[punt] & 0x7F); + punt++; + } + + if (punt >= der_len) + return ASN1_DER_ERROR; + + if (INT_MULTIPLY_OVERFLOW (ris, 128)) + return ASN1_DER_ERROR; + ris *= 128; + + if (INT_ADD_OVERFLOW (ris, ((unsigned) (der[punt] & 0x7F)))) + return ASN1_DER_ERROR; + ris += (der[punt] & 0x7F); + punt++; + + *len = punt; + } + + if (tag) + *tag = ris; + return ASN1_SUCCESS; +} + +/** + * asn1_get_length_ber: + * @ber: BER data to decode. + * @ber_len: Length of BER data to decode. + * @len: Output variable containing the length of the BER length field. + * + * Extract a length field from BER data. The difference to + * asn1_get_length_der() is that this function will return a length + * even if the value has indefinite encoding. + * + * Returns: Return the decoded length value, or negative value when + * the value was too big. + * + * Since: 2.0 + **/ +long +asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len) +{ + int ret; + long err; + + ret = asn1_get_length_der (ber, ber_len, len); + + if (ret == -1 && ber_len > 1) + { /* indefinite length method */ + err = _asn1_get_indefinite_length_string (ber + 1, ber_len-1, &ret); + if (err != ASN1_SUCCESS) + return -3; + } + + return ret; +} + +/** + * asn1_get_octet_der: + * @der: DER data to decode containing the OCTET SEQUENCE. + * @der_len: The length of the @der data to decode. + * @ret_len: Output variable containing the encoded length of the DER data. + * @str: Pre-allocated output buffer to put decoded OCTET SEQUENCE in. + * @str_size: Length of pre-allocated output buffer. + * @str_len: Output variable containing the length of the contents of the OCTET SEQUENCE. + * + * Extract an OCTET SEQUENCE from DER data. Note that this function + * expects the DER data past the tag field, i.e., the length and + * content octets. + * + * Returns: Returns %ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_octet_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, int str_size, + int *str_len) +{ + int len_len = 0; + + if (der_len <= 0) + return ASN1_GENERIC_ERROR; + + *str_len = asn1_get_length_der (der, der_len, &len_len); + + if (*str_len < 0) + return ASN1_DER_ERROR; + + *ret_len = *str_len + len_len; + if (str_size >= *str_len) + { + if (*str_len > 0 && str != NULL) + memcpy (str, der + len_len, *str_len); + } + else + { + return ASN1_MEM_ERROR; + } + + return ASN1_SUCCESS; +} + + +/*- + * _asn1_get_time_der: + * @type: %ASN1_ETYPE_GENERALIZED_TIME or %ASN1_ETYPE_UTC_TIME + * @der: DER data to decode containing the time + * @der_len: Length of DER data to decode. + * @ret_len: Output variable containing the length of the DER data. + * @str: Pre-allocated output buffer to put the textual time in. + * @str_size: Length of pre-allocated output buffer. + * @flags: Zero or %ASN1_DECODE_FLAG_STRICT_DER + * + * Performs basic checks in the DER encoded time object and returns its textual form. + * The textual form will be in the YYYYMMDD000000Z format for GeneralizedTime + * and YYMMDD000000Z for UTCTime. + * + * Returns: %ASN1_SUCCESS on success, or an error. + -*/ +static int +_asn1_get_time_der (unsigned type, const unsigned char *der, int der_len, int *ret_len, + char *str, int str_size, unsigned flags) +{ + int len_len, str_len; + unsigned i; + unsigned sign_count = 0; + unsigned dot_count = 0; + const unsigned char *p; + + if (der_len <= 0 || str == NULL) + return ASN1_DER_ERROR; + + str_len = asn1_get_length_der (der, der_len, &len_len); + if (str_len <= 0 || str_size < str_len) + return ASN1_DER_ERROR; + + /* perform some sanity checks on the data */ + if (str_len < 8) + { + warn(); + return ASN1_TIME_ENCODING_ERROR; + } + + if ((flags & ASN1_DECODE_FLAG_STRICT_DER) && !(flags & ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME)) + { + p = &der[len_len]; + for (i=0;i<(unsigned)(str_len-1);i++) + { + if (c_isdigit(p[i]) == 0) + { + if (type == ASN1_ETYPE_GENERALIZED_TIME) + { + /* tolerate lax encodings */ + if (p[i] == '.' && dot_count == 0) + { + dot_count++; + continue; + } + + /* This is not really valid DER, but there are + * structures using that */ + if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) && + (p[i] == '+' || p[i] == '-') && sign_count == 0) + { + sign_count++; + continue; + } + } + + warn(); + return ASN1_TIME_ENCODING_ERROR; + } + } + + if (sign_count == 0 && p[str_len-1] != 'Z') + { + warn(); + return ASN1_TIME_ENCODING_ERROR; + } + } + memcpy (str, der + len_len, str_len); + str[str_len] = 0; + *ret_len = str_len + len_len; + + return ASN1_SUCCESS; +} + +/** + * asn1_get_object_id_der: + * @der: DER data to decode containing the OBJECT IDENTIFIER + * @der_len: Length of DER data to decode. + * @ret_len: Output variable containing the length of the DER data. + * @str: Pre-allocated output buffer to put the textual object id in. + * @str_size: Length of pre-allocated output buffer. + * + * Converts a DER encoded object identifier to its textual form. This + * function expects the DER object identifier without the tag. + * + * Returns: %ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_object_id_der (const unsigned char *der, int der_len, int *ret_len, + char *str, int str_size) +{ + int len_len, len, k; + int leading, parsed; + char temp[LTOSTR_MAX_SIZE]; + uint64_t val, val1, val0; + + *ret_len = 0; + if (str && str_size > 0) + str[0] = 0; /* no oid */ + + if (str == NULL || der_len <= 0) + return ASN1_GENERIC_ERROR; + + len = asn1_get_length_der (der, der_len, &len_len); + + if (len <= 0 || len + len_len > der_len) + return ASN1_DER_ERROR; + + /* leading octet can never be 0x80 */ + if (der[len_len] == 0x80) + return ASN1_DER_ERROR; + + val0 = 0; + + for (k = 0; k < len; k++) + { + if (INT_LEFT_SHIFT_OVERFLOW (val0, 7)) + return ASN1_DER_ERROR; + + val0 <<= 7; + val0 |= der[len_len + k] & 0x7F; + if (!(der[len_len + k] & 0x80)) + break; + } + parsed = ++k; + + /* val0 = (X*40) + Y, X={0,1,2}, Y<=39 when X={0,1} */ + /* X = val, Y = val1 */ + + /* check if X == 0 */ + val = 0; + val1 = val0; + if (val1 > 39) + { + val = 1; + val1 = val0 - 40; + if (val1 > 39) + { + val = 2; + val1 = val0 - 80; + } + } + + _asn1_str_cpy (str, str_size, _asn1_ltostr (val, temp)); + _asn1_str_cat (str, str_size, "."); + _asn1_str_cat (str, str_size, _asn1_ltostr (val1, temp)); + + val = 0; + leading = 1; + for (k = parsed; k < len; k++) + { + /* X.690 mandates that the leading byte must never be 0x80 + */ + if (leading != 0 && der[len_len + k] == 0x80) + return ASN1_DER_ERROR; + leading = 0; + + /* check for wrap around */ + if (INT_LEFT_SHIFT_OVERFLOW (val, 7)) + return ASN1_DER_ERROR; + + val = val << 7; + val |= der[len_len + k] & 0x7F; + + if (!(der[len_len + k] & 0x80)) + { + _asn1_str_cat (str, str_size, "."); + _asn1_str_cat (str, str_size, _asn1_ltostr (val, temp)); + val = 0; + leading = 1; + } + } + + if (INT_ADD_OVERFLOW (len, len_len)) + return ASN1_DER_ERROR; + + *ret_len = len + len_len; + + return ASN1_SUCCESS; +} + +/** + * asn1_get_bit_der: + * @der: DER data to decode containing the BIT SEQUENCE. + * @der_len: Length of DER data to decode. + * @ret_len: Output variable containing the length of the DER data. + * @str: Pre-allocated output buffer to put decoded BIT SEQUENCE in. + * @str_size: Length of pre-allocated output buffer. + * @bit_len: Output variable containing the size of the BIT SEQUENCE. + * + * Extract a BIT SEQUENCE from DER data. + * + * Returns: %ASN1_SUCCESS on success, or an error. + **/ +int +asn1_get_bit_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, int str_size, + int *bit_len) +{ + int len_len = 0, len_byte; + + if (der_len <= 0) + return ASN1_GENERIC_ERROR; + + len_byte = asn1_get_length_der (der, der_len, &len_len) - 1; + if (len_byte < 0) + return ASN1_DER_ERROR; + + *ret_len = len_byte + len_len + 1; + *bit_len = len_byte * 8 - der[len_len]; + + if (*bit_len < 0) + return ASN1_DER_ERROR; + + if (str_size >= len_byte) + { + if (len_byte > 0 && str) + memcpy (str, der + len_len + 1, len_byte); + } + else + { + return ASN1_MEM_ERROR; + } + + return ASN1_SUCCESS; +} + +/* tag_len: the total tag length (explicit+inner) + * inner_tag_len: the inner_tag length + */ +static int +_asn1_extract_tag_der (asn1_node node, const unsigned char *der, int der_len, + int *tag_len, int *inner_tag_len, unsigned flags) +{ + asn1_node p; + int counter, len2, len3, is_tag_implicit; + int result; + unsigned long tag, tag_implicit = 0; + unsigned char class, class2, class_implicit = 0; + + if (der_len <= 0) + return ASN1_GENERIC_ERROR; + + counter = is_tag_implicit = 0; + + if (node->type & CONST_TAG) + { + p = node->down; + while (p) + { + if (type_field (p->type) == ASN1_ETYPE_TAG) + { + if (p->type & CONST_APPLICATION) + class2 = ASN1_CLASS_APPLICATION; + else if (p->type & CONST_UNIVERSAL) + class2 = ASN1_CLASS_UNIVERSAL; + else if (p->type & CONST_PRIVATE) + class2 = ASN1_CLASS_PRIVATE; + else + class2 = ASN1_CLASS_CONTEXT_SPECIFIC; + + if (p->type & CONST_EXPLICIT) + { + if (asn1_get_tag_der + (der + counter, der_len, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + + DECR_LEN(der_len, len2); + counter += len2; + + if (flags & ASN1_DECODE_FLAG_STRICT_DER) + len3 = + asn1_get_length_der (der + counter, der_len, + &len2); + else + len3 = + asn1_get_length_ber (der + counter, der_len, + &len2); + if (len3 < 0) + return ASN1_DER_ERROR; + + DECR_LEN(der_len, len2); + counter += len2; + + if (!is_tag_implicit) + { + if ((class != (class2 | ASN1_CLASS_STRUCTURED)) || + (tag != strtoul ((char *) p->value, NULL, 10))) + return ASN1_TAG_ERROR; + } + else + { /* ASN1_TAG_IMPLICIT */ + if ((class != class_implicit) || (tag != tag_implicit)) + return ASN1_TAG_ERROR; + } + is_tag_implicit = 0; + } + else + { /* ASN1_TAG_IMPLICIT */ + if (!is_tag_implicit) + { + if ((type_field (node->type) == ASN1_ETYPE_SEQUENCE) || + (type_field (node->type) == ASN1_ETYPE_SEQUENCE_OF) + || (type_field (node->type) == ASN1_ETYPE_SET) + || (type_field (node->type) == ASN1_ETYPE_SET_OF)) + class2 |= ASN1_CLASS_STRUCTURED; + class_implicit = class2; + tag_implicit = strtoul ((char *) p->value, NULL, 10); + is_tag_implicit = 1; + } + } + } + p = p->right; + } + } + + if (is_tag_implicit) + { + if (asn1_get_tag_der + (der + counter, der_len, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + + DECR_LEN(der_len, len2); + + if ((class != class_implicit) || (tag != tag_implicit)) + { + if (type_field (node->type) == ASN1_ETYPE_OCTET_STRING) + { + class_implicit |= ASN1_CLASS_STRUCTURED; + if ((class != class_implicit) || (tag != tag_implicit)) + return ASN1_TAG_ERROR; + } + else + return ASN1_TAG_ERROR; + } + } + else + { + unsigned type = type_field (node->type); + if (type == ASN1_ETYPE_TAG) + { + *tag_len = 0; + if (inner_tag_len) + *inner_tag_len = 0; + return ASN1_SUCCESS; + } + + if (asn1_get_tag_der + (der + counter, der_len, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + + DECR_LEN(der_len, len2); + + switch (type) + { + case ASN1_ETYPE_NULL: + case ASN1_ETYPE_BOOLEAN: + case ASN1_ETYPE_INTEGER: + case ASN1_ETYPE_ENUMERATED: + case ASN1_ETYPE_OBJECT_ID: + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + case ASN1_ETYPE_BIT_STRING: + case ASN1_ETYPE_SEQUENCE: + case ASN1_ETYPE_SEQUENCE_OF: + case ASN1_ETYPE_SET: + case ASN1_ETYPE_SET_OF: + case ASN1_ETYPE_GENERALIZED_TIME: + case ASN1_ETYPE_UTC_TIME: + if ((class != _asn1_tags[type].class) + || (tag != _asn1_tags[type].tag)) + return ASN1_DER_ERROR; + break; + + case ASN1_ETYPE_OCTET_STRING: + /* OCTET STRING is handled differently to allow + * BER encodings (structured class). */ + if (((class != ASN1_CLASS_UNIVERSAL) + && (class != (ASN1_CLASS_UNIVERSAL | ASN1_CLASS_STRUCTURED))) + || (tag != ASN1_TAG_OCTET_STRING)) + return ASN1_DER_ERROR; + break; + case ASN1_ETYPE_ANY: + counter -= len2; + break; + case ASN1_ETYPE_CHOICE: + counter -= len2; + break; + default: + return ASN1_DER_ERROR; + break; + } + } + + counter += len2; + *tag_len = counter; + if (inner_tag_len) + *inner_tag_len = len2; + return ASN1_SUCCESS; + +cleanup: + return result; +} + +static int +extract_tag_der_recursive(asn1_node node, const unsigned char *der, int der_len, + int *ret_len, int *inner_len, unsigned flags) +{ +asn1_node p; +int ris = ASN1_DER_ERROR; + + if (type_field (node->type) == ASN1_ETYPE_CHOICE) + { + p = node->down; + while (p) + { + ris = _asn1_extract_tag_der (p, der, der_len, ret_len, inner_len, flags); + if (ris == ASN1_SUCCESS) + break; + p = p->right; + } + + *ret_len = 0; + return ris; + } + else + return _asn1_extract_tag_der (node, der, der_len, ret_len, inner_len, flags); +} + +static int +_asn1_delete_not_used (asn1_node node) +{ + asn1_node p, p2; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if (p->type & CONST_NOT_USED) + { + p2 = NULL; + if (p != node) + { + p2 = _asn1_find_left (p); + if (!p2) + p2 = _asn1_find_up (p); + } + asn1_delete_structure (&p); + p = p2; + } + + if (!p) + break; /* reach node */ + + if (p->down) + { + p = p->down; + } + else + { + if (p == node) + p = NULL; + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p->right) + { + p = p->right; + break; + } + } + } + } + } + return ASN1_SUCCESS; +} + +static int +_asn1_get_indefinite_length_string (const unsigned char *der, + int der_len, int *len) +{ + int len2, len3, counter, indefinite; + int result; + unsigned long tag; + unsigned char class; + + counter = indefinite = 0; + + while (1) + { + if (HAVE_TWO(der_len) && (der[counter] == 0) && (der[counter + 1] == 0)) + { + counter += 2; + DECR_LEN(der_len, 2); + + indefinite--; + if (indefinite <= 0) + break; + else + continue; + } + + if (asn1_get_tag_der + (der + counter, der_len, &class, &len2, + &tag) != ASN1_SUCCESS) + return ASN1_DER_ERROR; + + DECR_LEN(der_len, len2); + counter += len2; + + len2 = asn1_get_length_der (der + counter, der_len, &len3); + if (len2 < -1) + return ASN1_DER_ERROR; + + if (len2 == -1) + { + indefinite++; + counter += 1; + DECR_LEN(der_len, 1); + } + else + { + counter += len2 + len3; + DECR_LEN(der_len, len2+len3); + } + } + + *len = counter; + return ASN1_SUCCESS; + +cleanup: + return result; +} + +static void delete_unneeded_choice_fields(asn1_node p) +{ + asn1_node p2; + + while (p->right) + { + p2 = p->right; + asn1_delete_structure (&p2); + } +} + + +/** + * asn1_der_decoding2 + * @element: pointer to an ASN1 structure. + * @ider: vector that contains the DER encoding. + * @max_ider_len: pointer to an integer giving the information about the + * maximal number of bytes occupied by *@ider. The real size of the DER + * encoding is returned through this pointer. + * @flags: flags controlling the behaviour of the function. + * @errorDescription: null-terminated string contains details when an + * error occurred. + * + * Fill the structure *@element with values of a DER encoding string. The + * structure must just be created with function asn1_create_element(). + * + * If %ASN1_DECODE_FLAG_ALLOW_PADDING flag is set then the function will ignore + * padding after the decoded DER data. Upon a successful return the value of + * *@max_ider_len will be set to the number of bytes decoded. + * + * If %ASN1_DECODE_FLAG_STRICT_DER flag is set then the function will + * not decode any BER-encoded elements. + * + * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND + * if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or + * %ASN1_DER_ERROR if the der encoding doesn't match the structure + * name (*@ELEMENT deleted). + **/ +int +asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, + unsigned int flags, char *errorDescription) +{ + asn1_node node, p, p2, p3; + char temp[128]; + int counter, len2, len3, len4, move, ris, tlen; + struct node_tail_cache_st tcache = {NULL, NULL}; + unsigned char class; + unsigned long tag; + int tag_len; + int indefinite, result, total_len = *max_ider_len, ider_len = *max_ider_len; + int inner_tag_len; + unsigned char *ptmp; + const unsigned char *ptag; + const unsigned char *der = ider; + + node = *element; + + if (errorDescription != NULL) + errorDescription[0] = 0; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + if (node->type & CONST_OPTION) + { + result = ASN1_GENERIC_ERROR; + warn(); + goto cleanup; + } + + counter = 0; + move = DOWN; + p = node; + while (1) + { + tag_len = 0; + inner_tag_len = 0; + ris = ASN1_SUCCESS; + if (move != UP) + { + if (p->type & CONST_SET) + { + p2 = _asn1_find_up (p); + len2 = p2->tmp_ival; + if (len2 == -1) + { + if (HAVE_TWO(ider_len) && !der[counter] && !der[counter + 1]) + { + p = p2; + move = UP; + counter += 2; + DECR_LEN(ider_len, 2); + continue; + } + } + else if (counter == len2) + { + p = p2; + move = UP; + continue; + } + else if (counter > len2) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + p2 = p2->down; + while (p2) + { + if ((p2->type & CONST_SET) && (p2->type & CONST_NOT_USED)) + { + ris = + extract_tag_der_recursive (p2, der + counter, + ider_len, &len2, NULL, flags); + if (ris == ASN1_SUCCESS) + { + p2->type &= ~CONST_NOT_USED; + p = p2; + break; + } + } + p2 = p2->right; + } + if (p2 == NULL) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + } + + /* the position in the DER structure this starts */ + p->start = counter; + p->end = total_len - 1; + + if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) + { + p2 = _asn1_find_up (p); + len2 = p2->tmp_ival; + if (counter == len2) + { + if (p->right) + { + p2 = p->right; + move = RIGHT; + } + else + move = UP; + + if (p->type & CONST_OPTION) + asn1_delete_structure (&p); + + p = p2; + continue; + } + } + + if (type_field (p->type) == ASN1_ETYPE_CHOICE) + { + while (p->down) + { + ris = + extract_tag_der_recursive (p->down, der + counter, + ider_len, &len2, NULL, flags); + + if (ris == ASN1_SUCCESS) + { + delete_unneeded_choice_fields(p->down); + break; + } + else if (ris == ASN1_ERROR_TYPE_ANY) + { + result = ASN1_ERROR_TYPE_ANY; + warn(); + goto cleanup; + } + else + { + p2 = p->down; + asn1_delete_structure (&p2); + } + } + + if (p->down == NULL) + { + if (!(p->type & CONST_OPTION)) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + } + else if (type_field (p->type) != ASN1_ETYPE_CHOICE) + p = p->down; + + p->start = counter; + } + + if ((p->type & CONST_OPTION) || (p->type & CONST_DEFAULT)) + { + p2 = _asn1_find_up (p); + len2 = p2->tmp_ival; + + if ((len2 != -1) && (counter > len2)) + ris = ASN1_TAG_ERROR; + } + + if (ris == ASN1_SUCCESS) + ris = + extract_tag_der_recursive (p, der + counter, ider_len, + &tag_len, &inner_tag_len, flags); + + if (ris != ASN1_SUCCESS) + { + if (p->type & CONST_OPTION) + { + p->type |= CONST_NOT_USED; + move = RIGHT; + } + else if (p->type & CONST_DEFAULT) + { + _asn1_set_value (p, NULL, 0); + move = RIGHT; + } + else + { + if (errorDescription != NULL) + _asn1_error_description_tag_error (p, errorDescription); + + result = ASN1_TAG_ERROR; + warn(); + goto cleanup; + } + } + else + { + DECR_LEN(ider_len, tag_len); + counter += tag_len; + } + } + + if (ris == ASN1_SUCCESS) + { + switch (type_field (p->type)) + { + case ASN1_ETYPE_NULL: + DECR_LEN(ider_len, 1); + if (der[counter]) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + counter++; + move = RIGHT; + break; + case ASN1_ETYPE_BOOLEAN: + DECR_LEN(ider_len, 2); + + if (der[counter++] != 1) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + if (der[counter++] == 0) + _asn1_set_value (p, "F", 1); + else + _asn1_set_value (p, "T", 1); + move = RIGHT; + break; + case ASN1_ETYPE_INTEGER: + case ASN1_ETYPE_ENUMERATED: + len2 = + asn1_get_length_der (der + counter, ider_len, &len3); + if (len2 < 0) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len3+len2); + + _asn1_set_value (p, der + counter, len3 + len2); + counter += len3 + len2; + move = RIGHT; + break; + case ASN1_ETYPE_OBJECT_ID: + result = + asn1_get_object_id_der (der + counter, ider_len, &len2, + temp, sizeof (temp)); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len2); + + tlen = strlen (temp); + if (tlen > 0) + _asn1_set_value (p, temp, tlen + 1); + + counter += len2; + move = RIGHT; + break; + case ASN1_ETYPE_GENERALIZED_TIME: + case ASN1_ETYPE_UTC_TIME: + result = + _asn1_get_time_der (type_field (p->type), der + counter, ider_len, &len2, temp, + sizeof (temp) - 1, flags); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len2); + + tlen = strlen (temp); + if (tlen > 0) + _asn1_set_value (p, temp, tlen); + + counter += len2; + move = RIGHT; + break; + case ASN1_ETYPE_OCTET_STRING: + if (counter < inner_tag_len) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + ptag = der + counter - inner_tag_len; + if ((flags & ASN1_DECODE_FLAG_STRICT_DER) || !(ptag[0] & ASN1_CLASS_STRUCTURED)) + { + if (ptag[0] & ASN1_CLASS_STRUCTURED) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + len2 = + asn1_get_length_der (der + counter, ider_len, &len3); + if (len2 < 0) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len3+len2); + + _asn1_set_value (p, der + counter, len3 + len2); + counter += len3 + len2; + } + else + { + unsigned dflags = 0, vlen, ber_len; + + if (ptag[0] & ASN1_CLASS_STRUCTURED) + dflags |= DECODE_FLAG_CONSTRUCTED; + + result = _asn1_decode_simple_ber(type_field (p->type), der+counter, ider_len, &ptmp, &vlen, &ber_len, dflags); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, ber_len); + + _asn1_set_value_lv (p, ptmp, vlen); + + counter += ber_len; + free(ptmp); + } + move = RIGHT; + break; + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + case ASN1_ETYPE_BIT_STRING: + len2 = + asn1_get_length_der (der + counter, ider_len, &len3); + if (len2 < 0) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len3+len2); + + _asn1_set_value (p, der + counter, len3 + len2); + counter += len3 + len2; + move = RIGHT; + break; + case ASN1_ETYPE_SEQUENCE: + case ASN1_ETYPE_SET: + if (move == UP) + { + len2 = p->tmp_ival; + p->tmp_ival = 0; + if (len2 == -1) + { /* indefinite length method */ + DECR_LEN(ider_len, 2); + if ((der[counter]) || der[counter + 1]) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + counter += 2; + } + else + { /* definite length method */ + if (len2 != counter) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + } + move = RIGHT; + } + else + { /* move==DOWN || move==RIGHT */ + len3 = + asn1_get_length_der (der + counter, ider_len, &len2); + if (IS_ERR(len3, flags)) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len2); + counter += len2; + + if (len3 > 0) + { + p->tmp_ival = counter + len3; + move = DOWN; + } + else if (len3 == 0) + { + p2 = p->down; + while (p2) + { + if (type_field (p2->type) != ASN1_ETYPE_TAG) + { + p3 = p2->right; + asn1_delete_structure (&p2); + p2 = p3; + } + else + p2 = p2->right; + } + move = RIGHT; + } + else + { /* indefinite length method */ + p->tmp_ival = -1; + move = DOWN; + } + } + break; + case ASN1_ETYPE_SEQUENCE_OF: + case ASN1_ETYPE_SET_OF: + if (move == UP) + { + len2 = p->tmp_ival; + if (len2 == -1) + { /* indefinite length method */ + if (!HAVE_TWO(ider_len) || ((der[counter]) || der[counter + 1])) + { + result = _asn1_append_sequence_set (p, &tcache); + if (result != 0) + { + warn(); + goto cleanup; + } + p = tcache.tail; + move = RIGHT; + continue; + } + + p->tmp_ival = 0; + tcache.tail = NULL; /* finished decoding this structure */ + tcache.head = NULL; + DECR_LEN(ider_len, 2); + counter += 2; + } + else + { /* definite length method */ + if (len2 > counter) + { + result = _asn1_append_sequence_set (p, &tcache); + if (result != 0) + { + warn(); + goto cleanup; + } + p = tcache.tail; + move = RIGHT; + continue; + } + + p->tmp_ival = 0; + tcache.tail = NULL; /* finished decoding this structure */ + tcache.head = NULL; + + if (len2 != counter) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + } + } + else + { /* move==DOWN || move==RIGHT */ + len3 = + asn1_get_length_der (der + counter, ider_len, &len2); + if (IS_ERR(len3, flags)) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len2); + counter += len2; + if (len3) + { + if (len3 > 0) + { /* definite length method */ + p->tmp_ival = counter + len3; + } + else + { /* indefinite length method */ + p->tmp_ival = -1; + } + + p2 = p->down; + if (p2 == NULL) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + while ((type_field (p2->type) == ASN1_ETYPE_TAG) + || (type_field (p2->type) == ASN1_ETYPE_SIZE)) + p2 = p2->right; + if (p2->right == NULL) + { + result = _asn1_append_sequence_set (p, &tcache); + if (result != 0) + { + warn(); + goto cleanup; + } + } + p = p2; + } + } + move = RIGHT; + break; + case ASN1_ETYPE_ANY: + /* Check indefinite lenth method in an EXPLICIT TAG */ + + if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) && (p->type & CONST_TAG) && + tag_len == 2 && (der[counter - 1] == 0x80)) + indefinite = 1; + else + indefinite = 0; + + if (asn1_get_tag_der + (der + counter, ider_len, &class, &len2, + &tag) != ASN1_SUCCESS) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len2); + + len4 = + asn1_get_length_der (der + counter + len2, + ider_len, &len3); + if (IS_ERR(len4, flags)) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + if (len4 != -1) /* definite */ + { + len2 += len4; + + DECR_LEN(ider_len, len4+len3); + _asn1_set_value_lv (p, der + counter, len2 + len3); + counter += len2 + len3; + } + else /* == -1 */ + { /* indefinite length */ + ider_len += len2; /* undo DECR_LEN */ + + if (counter == 0) + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + + result = + _asn1_get_indefinite_length_string (der + counter, ider_len, &len2); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + DECR_LEN(ider_len, len2); + _asn1_set_value_lv (p, der + counter, len2); + counter += len2; + + } + + /* Check if a couple of 0x00 are present due to an EXPLICIT TAG with + an indefinite length method. */ + if (indefinite) + { + DECR_LEN(ider_len, 2); + if (!der[counter] && !der[counter + 1]) + { + counter += 2; + } + else + { + result = ASN1_DER_ERROR; + warn(); + goto cleanup; + } + } + + move = RIGHT; + break; + default: + move = (move == UP) ? RIGHT : DOWN; + break; + } + } + + if (p) + { + p->end = counter - 1; + } + + if (p == node && move != DOWN) + break; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + if ((move == RIGHT) && !(p->type & CONST_SET)) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + _asn1_delete_not_used (*element); + + if ((ider_len < 0) || + (!(flags & ASN1_DECODE_FLAG_ALLOW_PADDING) && (ider_len != 0))) + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + + *max_ider_len = total_len - ider_len; + + return ASN1_SUCCESS; + +cleanup: + asn1_delete_structure (element); + return result; +} + + +/** + * asn1_der_decoding: + * @element: pointer to an ASN1 structure. + * @ider: vector that contains the DER encoding. + * @ider_len: number of bytes of *@ider: @ider[0]..@ider[len-1]. + * @errorDescription: null-terminated string contains details when an + * error occurred. + * + * Fill the structure *@element with values of a DER encoding + * string. The structure must just be created with function + * asn1_create_element(). + * + * Note that the *@element variable is provided as a pointer for + * historical reasons. + * + * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND + * if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or + * %ASN1_DER_ERROR if the der encoding doesn't match the structure + * name (*@ELEMENT deleted). + **/ +int +asn1_der_decoding (asn1_node * element, const void *ider, int ider_len, + char *errorDescription) +{ + return asn1_der_decoding2 (element, ider, &ider_len, 0, errorDescription); +} + +#if 0 +/** + * asn1_der_decoding_element: + * @structure: pointer to an ASN1 structure + * @elementName: name of the element to fill + * @ider: vector that contains the DER encoding of the whole structure. + * @len: number of bytes of *der: der[0]..der[len-1] + * @errorDescription: null-terminated string contains details when an + * error occurred. + * + * Fill the element named @ELEMENTNAME with values of a DER encoding + * string. The structure must just be created with function + * asn1_create_element(). The DER vector must contain the encoding + * string of the whole @STRUCTURE. If an error occurs during the + * decoding procedure, the *@STRUCTURE is deleted and set equal to + * %NULL. + * + * This function is deprecated and may just be an alias to asn1_der_decoding + * in future versions. Use asn1_der_decoding() instead. + * + * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND + * if ELEMENT is %NULL or @elementName == NULL, and + * %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding doesn't + * match the structure @structure (*ELEMENT deleted). + **/ +int +asn1_der_decoding_element (asn1_node * structure, const char *elementName, + const void *ider, int len, char *errorDescription) +{ + return asn1_der_decoding(structure, ider, len, errorDescription); +} +#endif + +/** + * asn1_der_decoding_startEnd: + * @element: pointer to an ASN1 element + * @ider: vector that contains the DER encoding. + * @ider_len: number of bytes of *@ider: @ider[0]..@ider[len-1] + * @name_element: an element of NAME structure. + * @start: the position of the first byte of NAME_ELEMENT decoding + * (@ider[*start]) + * @end: the position of the last byte of NAME_ELEMENT decoding + * (@ider[*end]) + * + * Find the start and end point of an element in a DER encoding + * string. I mean that if you have a der encoding and you have already + * used the function asn1_der_decoding() to fill a structure, it may + * happen that you want to find the piece of string concerning an + * element of the structure. + * + * One example is the sequence "tbsCertificate" inside an X509 + * certificate. + * + * Note that since libtasn1 3.7 the @ider and @ider_len parameters + * can be omitted, if the element is already decoded using asn1_der_decoding(). + * + * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND + * if ELEMENT is %asn1_node EMPTY or @name_element is not a valid + * element, %ASN1_TAG_ERROR or %ASN1_DER_ERROR if the der encoding + * doesn't match the structure ELEMENT. + **/ +int +asn1_der_decoding_startEnd (asn1_node element, const void *ider, int ider_len, + const char *name_element, int *start, int *end) +{ + asn1_node node, node_to_find; + int result = ASN1_DER_ERROR; + + node = element; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + node_to_find = asn1_find_node (node, name_element); + + if (node_to_find == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + *start = node_to_find->start; + *end = node_to_find->end; + + if (*start == 0 && *end == 0) + { + if (ider == NULL || ider_len == 0) + return ASN1_GENERIC_ERROR; + + /* it seems asn1_der_decoding() wasn't called before. Do it now */ + result = asn1_der_decoding (&node, ider, ider_len, NULL); + if (result != ASN1_SUCCESS) + { + warn(); + return result; + } + + node_to_find = asn1_find_node (node, name_element); + if (node_to_find == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + *start = node_to_find->start; + *end = node_to_find->end; + } + + if (*end < *start) + return ASN1_GENERIC_ERROR; + + return ASN1_SUCCESS; +} + +/** + * asn1_expand_any_defined_by: + * @definitions: ASN1 definitions + * @element: pointer to an ASN1 structure + * + * Expands every "ANY DEFINED BY" element of a structure created from + * a DER decoding process (asn1_der_decoding function). The element + * ANY must be defined by an OBJECT IDENTIFIER. The type used to + * expand the element ANY is the first one following the definition of + * the actual value of the OBJECT IDENTIFIER. + * + * Returns: %ASN1_SUCCESS if Substitution OK, %ASN1_ERROR_TYPE_ANY if + * some "ANY DEFINED BY" element couldn't be expanded due to a + * problem in OBJECT_ID -> TYPE association, or other error codes + * depending on DER decoding. + **/ +int +asn1_expand_any_defined_by (asn1_node_const definitions, asn1_node * element) +{ + char name[2 * ASN1_MAX_NAME_SIZE + 2], + value[ASN1_MAX_NAME_SIZE]; + int retCode = ASN1_SUCCESS, result; + int len, len2, len3; + asn1_node_const p2; + asn1_node p, p3, aux = NULL; + char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + const char *definitionsName; + + if ((definitions == NULL) || (*element == NULL)) + return ASN1_ELEMENT_NOT_FOUND; + + definitionsName = definitions->name; + + p = *element; + while (p) + { + + switch (type_field (p->type)) + { + case ASN1_ETYPE_ANY: + if ((p->type & CONST_DEFINED_BY) && (p->value)) + { + /* search the "DEF_BY" element */ + p2 = p->down; + while ((p2) && (type_field (p2->type) != ASN1_ETYPE_CONSTANT)) + p2 = p2->right; + + if (!p2) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + p3 = _asn1_find_up (p); + + if (!p3) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + p3 = p3->down; + while (p3) + { + if (!(strcmp (p3->name, p2->name))) + break; + p3 = p3->right; + } + + if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) || + (p3->value == NULL)) + { + + p3 = _asn1_find_up (p); + p3 = _asn1_find_up (p3); + + if (!p3) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + p3 = p3->down; + + while (p3) + { + if (!(strcmp (p3->name, p2->name))) + break; + p3 = p3->right; + } + + if ((!p3) || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) + || (p3->value == NULL)) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + } + + /* search the OBJECT_ID into definitions */ + p2 = definitions->down; + while (p2) + { + if ((type_field (p2->type) == ASN1_ETYPE_OBJECT_ID) && + (p2->type & CONST_ASSIGN)) + { + snprintf(name, sizeof(name), "%s.%s", definitionsName, p2->name); + + len = ASN1_MAX_NAME_SIZE; + result = + asn1_read_value (definitions, name, value, &len); + + if ((result == ASN1_SUCCESS) + && (!_asn1_strcmp (p3->value, value))) + { + p2 = p2->right; /* pointer to the structure to + use for expansion */ + while ((p2) && (p2->type & CONST_ASSIGN)) + p2 = p2->right; + + if (p2) + { + snprintf(name, sizeof(name), "%s.%s", definitionsName, p2->name); + + result = + asn1_create_element (definitions, name, &aux); + if (result == ASN1_SUCCESS) + { + _asn1_cpy_name (aux, p); + len2 = + asn1_get_length_der (p->value, + p->value_len, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + + result = + asn1_der_decoding (&aux, p->value + len3, + len2, + errorDescription); + if (result == ASN1_SUCCESS) + { + + _asn1_set_right (aux, p->right); + _asn1_set_right (p, aux); + + result = asn1_delete_structure (&p); + if (result == ASN1_SUCCESS) + { + p = aux; + aux = NULL; + break; + } + else + { /* error with asn1_delete_structure */ + asn1_delete_structure (&aux); + retCode = result; + break; + } + } + else + { /* error with asn1_der_decoding */ + retCode = result; + break; + } + } + else + { /* error with asn1_create_element */ + retCode = result; + break; + } + } + else + { /* error with the pointer to the structure to exapand */ + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + } + } + p2 = p2->right; + } /* end while */ + + if (!p2) + { + retCode = ASN1_ERROR_TYPE_ANY; + break; + } + + } + break; + default: + break; + } + + + if (p->down) + { + p = p->down; + } + else if (p == *element) + { + p = NULL; + break; + } + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == *element) + { + p = NULL; + break; + } + if (p->right) + { + p = p->right; + break; + } + } + } + } + + return retCode; +} + +/** + * asn1_expand_octet_string: + * @definitions: ASN1 definitions + * @element: pointer to an ASN1 structure + * @octetName: name of the OCTECT STRING field to expand. + * @objectName: name of the OBJECT IDENTIFIER field to use to define + * the type for expansion. + * + * Expands an "OCTET STRING" element of a structure created from a DER + * decoding process (the asn1_der_decoding() function). The type used + * for expansion is the first one following the definition of the + * actual value of the OBJECT IDENTIFIER indicated by OBJECTNAME. + * + * Returns: %ASN1_SUCCESS if substitution OK, %ASN1_ELEMENT_NOT_FOUND + * if @objectName or @octetName are not correct, + * %ASN1_VALUE_NOT_VALID if it wasn't possible to find the type to + * use for expansion, or other errors depending on DER decoding. + **/ +int +asn1_expand_octet_string (asn1_node_const definitions, asn1_node * element, + const char *octetName, const char *objectName) +{ + char name[2 * ASN1_MAX_NAME_SIZE + 1], value[ASN1_MAX_NAME_SIZE]; + int retCode = ASN1_SUCCESS, result; + int len, len2, len3; + asn1_node_const p2; + asn1_node aux = NULL; + asn1_node octetNode = NULL, objectNode = NULL; + char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + + if ((definitions == NULL) || (*element == NULL)) + return ASN1_ELEMENT_NOT_FOUND; + + octetNode = asn1_find_node (*element, octetName); + if (octetNode == NULL) + return ASN1_ELEMENT_NOT_FOUND; + if (type_field (octetNode->type) != ASN1_ETYPE_OCTET_STRING) + return ASN1_ELEMENT_NOT_FOUND; + if (octetNode->value == NULL) + return ASN1_VALUE_NOT_FOUND; + + objectNode = asn1_find_node (*element, objectName); + if (objectNode == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + if (type_field (objectNode->type) != ASN1_ETYPE_OBJECT_ID) + return ASN1_ELEMENT_NOT_FOUND; + + if (objectNode->value == NULL) + return ASN1_VALUE_NOT_FOUND; + + + /* search the OBJECT_ID into definitions */ + p2 = definitions->down; + while (p2) + { + if ((type_field (p2->type) == ASN1_ETYPE_OBJECT_ID) && + (p2->type & CONST_ASSIGN)) + { + strcpy (name, definitions->name); + memcpy (name + strlen(name), ".", sizeof(" . ")); + memcpy (name + strlen(name), p2->name, strlen(p2->name) + 1); + + len = sizeof (value); + result = asn1_read_value (definitions, name, value, &len); + + if ((result == ASN1_SUCCESS) + && (!_asn1_strcmp (objectNode->value, value))) + { + + p2 = p2->right; /* pointer to the structure to + use for expansion */ + while ((p2) && (p2->type & CONST_ASSIGN)) + p2 = p2->right; + + if (p2) + { + strcpy (name, definitions->name); + memcpy (name + strlen(name), ".", sizeof(" . ")); + memcpy (name + strlen(name), p2->name, strlen(p2->name) + 1); + + result = asn1_create_element (definitions, name, &aux); + if (result == ASN1_SUCCESS) + { + _asn1_cpy_name (aux, octetNode); + len2 = + asn1_get_length_der (octetNode->value, + octetNode->value_len, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + + result = + asn1_der_decoding (&aux, octetNode->value + len3, + len2, errorDescription); + if (result == ASN1_SUCCESS) + { + + _asn1_set_right (aux, octetNode->right); + _asn1_set_right (octetNode, aux); + + result = asn1_delete_structure (&octetNode); + if (result == ASN1_SUCCESS) + { + aux = NULL; + break; + } + else + { /* error with asn1_delete_structure */ + asn1_delete_structure (&aux); + retCode = result; + break; + } + } + else + { /* error with asn1_der_decoding */ + retCode = result; + break; + } + } + else + { /* error with asn1_create_element */ + retCode = result; + break; + } + } + else + { /* error with the pointer to the structure to exapand */ + retCode = ASN1_VALUE_NOT_VALID; + break; + } + } + } + + p2 = p2->right; + + } + + if (!p2) + retCode = ASN1_VALUE_NOT_VALID; + + return retCode; +} + +/*- + * _asn1_decode_simple_der: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @der: the encoded string + * @_der_len: the bytes of the encoded string + * @str: a pointer to the data + * @str_len: the length of the data + * @dflags: DECODE_FLAG_* + * + * Decodes a simple DER encoded type (e.g. a string, which is not constructed). + * The output is a pointer inside the @der. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + -*/ +static int +_asn1_decode_simple_der (unsigned int etype, const unsigned char *der, + unsigned int _der_len, const unsigned char **str, + unsigned int *str_len, unsigned dflags) +{ + int tag_len, len_len; + const unsigned char *p; + int der_len = _der_len; + unsigned char class; + unsigned long tag; + long ret; + + if (der == NULL || der_len == 0) + return ASN1_VALUE_NOT_VALID; + + if (ETYPE_OK (etype) == 0 || ETYPE_IS_STRING(etype) == 0) + return ASN1_VALUE_NOT_VALID; + + /* doesn't handle constructed classes */ + class = ETYPE_CLASS(etype); + if (class != ASN1_CLASS_UNIVERSAL) + return ASN1_VALUE_NOT_VALID; + + p = der; + + if (dflags & DECODE_FLAG_HAVE_TAG) + { + ret = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag); + if (ret != ASN1_SUCCESS) + return ret; + + if (class != ETYPE_CLASS (etype) || tag != ETYPE_TAG (etype)) + { + warn(); + return ASN1_DER_ERROR; + } + + p += tag_len; + der_len -= tag_len; + if (der_len <= 0) + return ASN1_DER_ERROR; + } + + ret = asn1_get_length_der (p, der_len, &len_len); + if (ret < 0) + return ASN1_DER_ERROR; + + p += len_len; + der_len -= len_len; + if (der_len <= 0) + return ASN1_DER_ERROR; + + *str_len = ret; + *str = p; + + return ASN1_SUCCESS; +} + +/** + * asn1_decode_simple_der: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @der: the encoded string + * @_der_len: the bytes of the encoded string + * @str: a pointer to the data + * @str_len: the length of the data + * + * Decodes a simple DER encoded type (e.g. a string, which is not constructed). + * The output is a pointer inside the @der. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + **/ +int +asn1_decode_simple_der (unsigned int etype, const unsigned char *der, + unsigned int _der_len, const unsigned char **str, + unsigned int *str_len) +{ + return _asn1_decode_simple_der(etype, der, _der_len, str, str_len, DECODE_FLAG_HAVE_TAG); +} + +static int append(uint8_t **dst, unsigned *dst_size, const unsigned char *src, unsigned src_size) +{ + if (src_size == 0) + return ASN1_SUCCESS; + + *dst = _asn1_realloc(*dst, *dst_size+src_size); + if (*dst == NULL) + return ASN1_MEM_ALLOC_ERROR; + memcpy(*dst + *dst_size, src, src_size); + *dst_size += src_size; + return ASN1_SUCCESS; +} + +/*- + * _asn1_decode_simple_ber: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @der: the encoded string + * @_der_len: the bytes of the encoded string + * @str: a pointer to the data + * @str_len: the length of the data + * @ber_len: the total length occupied by BER (may be %NULL) + * @have_tag: whether a DER tag is included + * + * Decodes a BER encoded type. The output is an allocated value + * of the data. This decodes BER STRINGS only. Other types are + * decoded as DER. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + -*/ +static int +_asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, + unsigned int _der_len, unsigned char **str, + unsigned int *str_len, unsigned int *ber_len, + unsigned dflags) +{ + int tag_len, len_len; + const unsigned char *p; + int der_len = _der_len; + uint8_t *total = NULL; + unsigned total_size = 0; + unsigned char class; + unsigned long tag; + unsigned char *out = NULL; + const unsigned char *cout = NULL; + unsigned out_len; + long result; + + if (ber_len) *ber_len = 0; + + if (der == NULL || der_len == 0) + { + warn(); + return ASN1_VALUE_NOT_VALID; + } + + if (ETYPE_OK (etype) == 0) + { + warn(); + return ASN1_VALUE_NOT_VALID; + } + + /* doesn't handle constructed + definite classes */ + class = ETYPE_CLASS (etype); + if (class != ASN1_CLASS_UNIVERSAL) + { + warn(); + return ASN1_VALUE_NOT_VALID; + } + + p = der; + + if (dflags & DECODE_FLAG_HAVE_TAG) + { + result = asn1_get_tag_der (p, der_len, &class, &tag_len, &tag); + if (result != ASN1_SUCCESS) + { + warn(); + return result; + } + + if (tag != ETYPE_TAG (etype)) + { + warn(); + return ASN1_DER_ERROR; + } + + p += tag_len; + + DECR_LEN(der_len, tag_len); + + if (ber_len) *ber_len += tag_len; + } + + /* indefinite constructed */ + if ((((dflags & DECODE_FLAG_CONSTRUCTED) || class == ASN1_CLASS_STRUCTURED) && ETYPE_IS_STRING(etype)) && + !(dflags & DECODE_FLAG_LEVEL3)) + { + if (der_len == 0) + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + + if (der_len > 0 && p[0] == 0x80) /* indefinite */ + { + len_len = 1; + DECR_LEN(der_len, len_len); + p += len_len; + + if (ber_len) *ber_len += len_len; + + /* decode the available octet strings */ + do + { + unsigned tmp_len; + unsigned flags = DECODE_FLAG_HAVE_TAG; + + if (dflags & DECODE_FLAG_LEVEL1) + flags |= DECODE_FLAG_LEVEL2; + else if (dflags & DECODE_FLAG_LEVEL2) + flags |= DECODE_FLAG_LEVEL3; + else + flags |= DECODE_FLAG_LEVEL1; + + result = _asn1_decode_simple_ber(etype, p, der_len, &out, &out_len, &tmp_len, + flags); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + p += tmp_len; + DECR_LEN(der_len, tmp_len); + + if (ber_len) *ber_len += tmp_len; + + DECR_LEN(der_len, 2); /* we need the EOC */ + + result = append(&total, &total_size, out, out_len); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + free(out); + out = NULL; + + if (p[0] == 0 && p[1] == 0) /* EOC */ + { + if (ber_len) *ber_len += 2; + break; + } + + /* no EOC */ + der_len += 2; + + if (der_len == 2) + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + } + while(1); + } + else /* constructed */ + { + long const_len; + + result = asn1_get_length_ber(p, der_len, &len_len); + if (result < 0) + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + + DECR_LEN(der_len, len_len); + p += len_len; + + const_len = result; + + if (ber_len) *ber_len += len_len; + + /* decode the available octet strings */ + while(const_len > 0) + { + unsigned tmp_len; + unsigned flags = DECODE_FLAG_HAVE_TAG; + + if (dflags & DECODE_FLAG_LEVEL1) + flags |= DECODE_FLAG_LEVEL2; + else if (dflags & DECODE_FLAG_LEVEL2) + flags |= DECODE_FLAG_LEVEL3; + else + flags |= DECODE_FLAG_LEVEL1; + + result = _asn1_decode_simple_ber(etype, p, der_len, &out, &out_len, &tmp_len, + flags); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + p += tmp_len; + DECR_LEN(der_len, tmp_len); + DECR_LEN(const_len, tmp_len); + + if (ber_len) *ber_len += tmp_len; + + result = append(&total, &total_size, out, out_len); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + free(out); + out = NULL; + } + } + } + else if (class == ETYPE_CLASS(etype)) + { + if (ber_len) + { + result = asn1_get_length_der (p, der_len, &len_len); + if (result < 0) + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + *ber_len += result + len_len; + } + + /* non-string values are decoded as DER */ + result = _asn1_decode_simple_der(etype, der, _der_len, &cout, &out_len, dflags); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + + result = append(&total, &total_size, cout, out_len); + if (result != ASN1_SUCCESS) + { + warn(); + goto cleanup; + } + } + else + { + warn(); + result = ASN1_DER_ERROR; + goto cleanup; + } + + *str = total; + *str_len = total_size; + + return ASN1_SUCCESS; +cleanup: + free(out); + free(total); + return result; +} + +/** + * asn1_decode_simple_ber: + * @etype: The type of the string to be encoded (ASN1_ETYPE_) + * @der: the encoded string + * @_der_len: the bytes of the encoded string + * @str: a pointer to the data + * @str_len: the length of the data + * @ber_len: the total length occupied by BER (may be %NULL) + * + * Decodes a BER encoded type. The output is an allocated value + * of the data. This decodes BER STRINGS only. Other types are + * decoded as DER. + * + * Returns: %ASN1_SUCCESS if successful or an error value. + **/ +int +asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, + unsigned int _der_len, unsigned char **str, + unsigned int *str_len, unsigned int *ber_len) +{ + return _asn1_decode_simple_ber(etype, der, _der_len, str, str_len, ber_len, DECODE_FLAG_HAVE_TAG); +} diff --git a/grub-core/lib/libtasn1/lib/element.c b/grub-core/lib/libtasn1/lib/element.c new file mode 100644 index 0000000000..8cc73455ae --- /dev/null +++ b/grub-core/lib/libtasn1/lib/element.c @@ -0,0 +1,1112 @@ +/* + * Copyright (C) 2000-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/*****************************************************/ +/* File: element.c */ +/* Description: Functions with the read and write */ +/* functions. */ +/*****************************************************/ + + +#include +#include "parser_aux.h" +#include +#include "structure.h" +#include "element.h" + +#define c_isdigit grub_isdigit + +void +_asn1_hierarchical_name (asn1_node_const node, char *name, int name_size) +{ + asn1_node_const p; + char tmp_name[64]; + + p = node; + + name[0] = 0; + + while (p != NULL) + { + if (p->name[0] != 0) + { + _asn1_str_cpy (tmp_name, sizeof (tmp_name), name), + _asn1_str_cpy (name, name_size, p->name); + _asn1_str_cat (name, name_size, "."); + _asn1_str_cat (name, name_size, tmp_name); + } + p = _asn1_find_up (p); + } + + if (name[0] == 0) + _asn1_str_cpy (name, name_size, "ROOT"); +} + + +/******************************************************************/ +/* Function : _asn1_convert_integer */ +/* Description: converts an integer from a null terminated string */ +/* to der decoding. The convertion from a null */ +/* terminated string to an integer is made with */ +/* the 'strtol' function. */ +/* Parameters: */ +/* value: null terminated string to convert. */ +/* value_out: convertion result (memory must be already */ +/* allocated). */ +/* value_out_size: number of bytes of value_out. */ +/* len: number of significant byte of value_out. */ +/* Return: ASN1_MEM_ERROR or ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_convert_integer (const unsigned char *value, unsigned char *value_out, + int value_out_size, int *len) +{ + char negative; + unsigned char val[SIZEOF_UNSIGNED_LONG_INT]; + long valtmp; + int k, k2; + + valtmp = _asn1_strtol (value, NULL, 10); + + for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT; k++) + { + val[SIZEOF_UNSIGNED_LONG_INT - k - 1] = (valtmp >> (8 * k)) & 0xFF; + } + + if (val[0] & 0x80) + negative = 1; + else + negative = 0; + + for (k = 0; k < SIZEOF_UNSIGNED_LONG_INT - 1; k++) + { + if (negative && (val[k] != 0xFF)) + break; + else if (!negative && val[k]) + break; + } + + if ((negative && !(val[k] & 0x80)) || (!negative && (val[k] & 0x80))) + k--; + + *len = SIZEOF_UNSIGNED_LONG_INT - k; + + if (SIZEOF_UNSIGNED_LONG_INT - k > value_out_size) + /* VALUE_OUT is too short to contain the value conversion */ + return ASN1_MEM_ERROR; + + if (value_out != NULL) + { + for (k2 = k; k2 < SIZEOF_UNSIGNED_LONG_INT; k2++) + value_out[k2 - k] = val[k2]; + } + +#if 0 + 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]); + printf ("\n"); +#endif + + return ASN1_SUCCESS; +} + +/* Appends a new element into the sequence (or set) defined by this + * node. The new element will have a name of '?number', where number + * is a monotonically increased serial number. + * + * The last element in the list may be provided in @pcache, to avoid + * traversing the list, an expensive operation in long lists. + * + * On success it returns in @pcache the added element (which is the + * tail in the list of added elements). + */ +int +_asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcache) +{ + asn1_node p, p2; + char temp[LTOSTR_MAX_SIZE + 1]; + long n; + + if (!node || !(node->down)) + return ASN1_GENERIC_ERROR; + + p = node->down; + while ((type_field (p->type) == ASN1_ETYPE_TAG) + || (type_field (p->type) == ASN1_ETYPE_SIZE)) + p = p->right; + + p2 = _asn1_copy_structure3 (p); + if (p2 == NULL) + return ASN1_GENERIC_ERROR; + + if (pcache == NULL || pcache->tail == NULL || pcache->head != node) + { + while (p->right) + { + p = p->right; + } + } + else + { + p = pcache->tail; + } + + _asn1_set_right (p, p2); + if (pcache) + { + pcache->head = node; + pcache->tail = p2; + } + + if (p->name[0] == 0) + _asn1_str_cpy (temp, sizeof (temp), "?1"); + else + { + n = strtol (p->name + 1, NULL, 0); + n++; + temp[0] = '?'; + _asn1_ltostr (n, temp + 1); + } + _asn1_set_name (p2, temp); + /* p2->type |= CONST_OPTION; */ + + return ASN1_SUCCESS; +} + +#if 0 +/** + * asn1_write_value: + * @node_root: pointer to a structure + * @name: the name of the element inside the structure that you want to set. + * @ivalue: vector used to specify the value to set. If len is >0, + * VALUE must be a two's complement form integer. if len=0 *VALUE + * must be a null terminated string with an integer value. + * @len: number of bytes of *value to use to set the value: + * value[0]..value[len-1] or 0 if value is a null terminated string + * + * Set the value of one element inside a structure. + * + * If an element is OPTIONAL and you want to delete it, you must use + * the value=NULL and len=0. Using "pkix.asn": + * + * result=asn1_write_value(cert, "tbsCertificate.issuerUniqueID", + * NULL, 0); + * + * Description for each type: + * + * INTEGER: VALUE must contain a two's complement form integer. + * + * value[0]=0xFF , len=1 -> integer=-1. + * value[0]=0xFF value[1]=0xFF , len=2 -> integer=-1. + * value[0]=0x01 , len=1 -> integer= 1. + * value[0]=0x00 value[1]=0x01 , len=2 -> integer= 1. + * value="123" , len=0 -> integer= 123. + * + * ENUMERATED: As INTEGER (but only with not negative numbers). + * + * BOOLEAN: VALUE must be the null terminated string "TRUE" or + * "FALSE" and LEN != 0. + * + * value="TRUE" , len=1 -> boolean=TRUE. + * value="FALSE" , len=1 -> boolean=FALSE. + * + * OBJECT IDENTIFIER: VALUE must be a null terminated string with + * each number separated by a dot (e.g. "1.2.3.543.1"). LEN != 0. + * + * value="1 2 840 10040 4 3" , len=1 -> OID=dsa-with-sha. + * + * UTCTime: VALUE must be a null terminated string in one of these + * formats: "YYMMDDhhmmssZ", "YYMMDDhhmmssZ", + * "YYMMDDhhmmss+hh'mm'", "YYMMDDhhmmss-hh'mm'", + * "YYMMDDhhmm+hh'mm'", or "YYMMDDhhmm-hh'mm'". LEN != 0. + * + * value="9801011200Z" , len=1 -> time=Jannuary 1st, 1998 + * at 12h 00m Greenwich Mean Time + * + * GeneralizedTime: VALUE must be in one of this format: + * "YYYYMMDDhhmmss.sZ", "YYYYMMDDhhmmss.sZ", + * "YYYYMMDDhhmmss.s+hh'mm'", "YYYYMMDDhhmmss.s-hh'mm'", + * "YYYYMMDDhhmm+hh'mm'", or "YYYYMMDDhhmm-hh'mm'" where ss.s + * indicates the seconds with any precision like "10.1" or "01.02". + * LEN != 0 + * + * value="2001010112001.12-0700" , len=1 -> time=Jannuary + * 1st, 2001 at 12h 00m 01.12s Pacific Daylight Time + * + * OCTET STRING: VALUE contains the octet string and LEN is the + * number of octets. + * + * value="$\backslash$x01$\backslash$x02$\backslash$x03" , + * len=3 -> three bytes octet string + * + * GeneralString: VALUE contains the generalstring and LEN is the + * number of octets. + * + * value="$\backslash$x01$\backslash$x02$\backslash$x03" , + * len=3 -> three bytes generalstring + * + * BIT STRING: VALUE contains the bit string organized by bytes and + * LEN is the number of bits. + * + * value="$\backslash$xCF" , len=6 -> bit string="110011" (six + * bits) + * + * CHOICE: if NAME indicates a choice type, VALUE must specify one of + * the alternatives with a null terminated string. LEN != 0. Using + * "pkix.asn"\: + * + * result=asn1_write_value(cert, + * "certificate1.tbsCertificate.subject", "rdnSequence", + * 1); + * + * ANY: VALUE indicates the der encoding of a structure. LEN != 0. + * + * SEQUENCE OF: VALUE must be the null terminated string "NEW" and + * LEN != 0. With this instruction another element is appended in + * the sequence. The name of this element will be "?1" if it's the + * first one, "?2" for the second and so on. + * + * Using "pkix.asn"\: + * + * result=asn1_write_value(cert, + * "certificate1.tbsCertificate.subject.rdnSequence", "NEW", 1); + * + * SET OF: the same as SEQUENCE OF. Using "pkix.asn": + * + * result=asn1_write_value(cert, + * "tbsCertificate.subject.rdnSequence.?LAST", "NEW", 1); + * + * Returns: %ASN1_SUCCESS if the value was set, + * %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element, and + * %ASN1_VALUE_NOT_VALID if @ivalue has a wrong format. + **/ +int +asn1_write_value (asn1_node node_root, const char *name, + const void *ivalue, int len) +{ + asn1_node node, p, p2; + unsigned char *temp, *value_temp = NULL, *default_temp = NULL; + int len2, k, k2, negative; + size_t i; + const unsigned char *value = ivalue; + unsigned int type; + + node = asn1_find_node (node_root, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + if ((node->type & CONST_OPTION) && (value == NULL) && (len == 0)) + { + asn1_delete_structure (&node); + return ASN1_SUCCESS; + } + + type = type_field (node->type); + + if ((type == ASN1_ETYPE_SEQUENCE_OF || type == ASN1_ETYPE_SET_OF) && (value == NULL) && (len == 0)) + { + p = node->down; + while ((type_field (p->type) == ASN1_ETYPE_TAG) + || (type_field (p->type) == ASN1_ETYPE_SIZE)) + p = p->right; + + while (p->right) + asn1_delete_structure (&p->right); + + return ASN1_SUCCESS; + } + + /* Don't allow element deletion for other types */ + if (value == NULL) + { + return ASN1_VALUE_NOT_VALID; + } + + switch (type) + { + case ASN1_ETYPE_BOOLEAN: + if (!_asn1_strcmp (value, "TRUE")) + { + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if (p->type & CONST_TRUE) + _asn1_set_value (node, NULL, 0); + else + _asn1_set_value (node, "T", 1); + } + else + _asn1_set_value (node, "T", 1); + } + else if (!_asn1_strcmp (value, "FALSE")) + { + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if (p->type & CONST_FALSE) + _asn1_set_value (node, NULL, 0); + else + _asn1_set_value (node, "F", 1); + } + else + _asn1_set_value (node, "F", 1); + } + else + return ASN1_VALUE_NOT_VALID; + break; + case ASN1_ETYPE_INTEGER: + case ASN1_ETYPE_ENUMERATED: + if (len == 0) + { + if ((c_isdigit (value[0])) || (value[0] == '-')) + { + value_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); + if (value_temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + _asn1_convert_integer (value, value_temp, + SIZEOF_UNSIGNED_LONG_INT, &len); + } + else + { /* is an identifier like v1 */ + if (!(node->type & CONST_LIST)) + return ASN1_VALUE_NOT_VALID; + p = node->down; + while (p) + { + if (type_field (p->type) == ASN1_ETYPE_CONSTANT) + { + if (!_asn1_strcmp (p->name, value)) + { + value_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); + if (value_temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + _asn1_convert_integer (p->value, + value_temp, + SIZEOF_UNSIGNED_LONG_INT, + &len); + break; + } + } + p = p->right; + } + if (p == NULL) + return ASN1_VALUE_NOT_VALID; + } + } + else + { /* len != 0 */ + value_temp = malloc (len); + if (value_temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + memcpy (value_temp, value, len); + } + + if (value_temp[0] & 0x80) + negative = 1; + else + negative = 0; + + if (negative && (type_field (node->type) == ASN1_ETYPE_ENUMERATED)) + { + free (value_temp); + return ASN1_VALUE_NOT_VALID; + } + + for (k = 0; k < len - 1; k++) + if (negative && (value_temp[k] != 0xFF)) + break; + else if (!negative && value_temp[k]) + break; + + if ((negative && !(value_temp[k] & 0x80)) || + (!negative && (value_temp[k] & 0x80))) + k--; + + _asn1_set_value_lv (node, value_temp + k, len - k); + + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if ((c_isdigit (p->value[0])) || (p->value[0] == '-')) + { + default_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); + if (default_temp == NULL) + { + free (value_temp); + return ASN1_MEM_ALLOC_ERROR; + } + + _asn1_convert_integer (p->value, default_temp, + SIZEOF_UNSIGNED_LONG_INT, &len2); + } + else + { /* is an identifier like v1 */ + if (!(node->type & CONST_LIST)) + { + free (value_temp); + return ASN1_VALUE_NOT_VALID; + } + p2 = node->down; + while (p2) + { + if (type_field (p2->type) == ASN1_ETYPE_CONSTANT) + { + if (!_asn1_strcmp (p2->name, p->value)) + { + default_temp = malloc (SIZEOF_UNSIGNED_LONG_INT); + if (default_temp == NULL) + { + free (value_temp); + return ASN1_MEM_ALLOC_ERROR; + } + + _asn1_convert_integer (p2->value, + default_temp, + SIZEOF_UNSIGNED_LONG_INT, + &len2); + break; + } + } + p2 = p2->right; + } + if (p2 == NULL) + { + free (value_temp); + return ASN1_VALUE_NOT_VALID; + } + } + + + if ((len - k) == len2) + { + for (k2 = 0; k2 < len2; k2++) + if (value_temp[k + k2] != default_temp[k2]) + { + break; + } + if (k2 == len2) + _asn1_set_value (node, NULL, 0); + } + free (default_temp); + } + free (value_temp); + break; + case ASN1_ETYPE_OBJECT_ID: + for (i = 0; i < _asn1_strlen (value); i++) + if ((!c_isdigit (value[i])) && (value[i] != '.') && (value[i] != '+')) + return ASN1_VALUE_NOT_VALID; + if (node->type & CONST_DEFAULT) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if (!_asn1_strcmp (value, p->value)) + { + _asn1_set_value (node, NULL, 0); + break; + } + } + _asn1_set_value (node, value, _asn1_strlen (value) + 1); + break; + case ASN1_ETYPE_UTC_TIME: + { + len = _asn1_strlen (value); + if (len < 11) + return ASN1_VALUE_NOT_VALID; + for (k = 0; k < 10; k++) + if (!c_isdigit (value[k])) + return ASN1_VALUE_NOT_VALID; + switch (len) + { + case 11: + if (value[10] != 'Z') + return ASN1_VALUE_NOT_VALID; + break; + case 13: + if ((!c_isdigit (value[10])) || (!c_isdigit (value[11])) || + (value[12] != 'Z')) + return ASN1_VALUE_NOT_VALID; + break; + case 15: + if ((value[10] != '+') && (value[10] != '-')) + return ASN1_VALUE_NOT_VALID; + for (k = 11; k < 15; k++) + if (!c_isdigit (value[k])) + return ASN1_VALUE_NOT_VALID; + break; + case 17: + if ((!c_isdigit (value[10])) || (!c_isdigit (value[11]))) + return ASN1_VALUE_NOT_VALID; + if ((value[12] != '+') && (value[12] != '-')) + return ASN1_VALUE_NOT_VALID; + for (k = 13; k < 17; k++) + if (!c_isdigit (value[k])) + return ASN1_VALUE_NOT_VALID; + break; + default: + return ASN1_VALUE_NOT_FOUND; + } + _asn1_set_value (node, value, len); + } + break; + case ASN1_ETYPE_GENERALIZED_TIME: + len = _asn1_strlen (value); + _asn1_set_value (node, value, len); + break; + case ASN1_ETYPE_OCTET_STRING: + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + if (len == 0) + len = _asn1_strlen (value); + _asn1_set_value_lv (node, value, len); + break; + case ASN1_ETYPE_BIT_STRING: + if (len == 0) + len = _asn1_strlen (value); + asn1_length_der ((len >> 3) + 2, NULL, &len2); + temp = malloc ((len >> 3) + 2 + len2); + if (temp == NULL) + return ASN1_MEM_ALLOC_ERROR; + + asn1_bit_der (value, len, temp, &len2); + _asn1_set_value_m (node, temp, len2); + temp = NULL; + break; + case ASN1_ETYPE_CHOICE: + p = node->down; + while (p) + { + if (!_asn1_strcmp (p->name, value)) + { + p2 = node->down; + while (p2) + { + if (p2 != p) + { + asn1_delete_structure (&p2); + p2 = node->down; + } + else + p2 = p2->right; + } + break; + } + p = p->right; + } + if (!p) + return ASN1_ELEMENT_NOT_FOUND; + break; + case ASN1_ETYPE_ANY: + _asn1_set_value_lv (node, value, len); + break; + case ASN1_ETYPE_SEQUENCE_OF: + case ASN1_ETYPE_SET_OF: + if (_asn1_strcmp (value, "NEW")) + return ASN1_VALUE_NOT_VALID; + _asn1_append_sequence_set (node, NULL); + break; + default: + return ASN1_ELEMENT_NOT_FOUND; + break; + } + + return ASN1_SUCCESS; +} +#endif + +#define PUT_VALUE( ptr, ptr_size, data, data_size) \ + *len = data_size; \ + if (ptr_size < data_size) { \ + return ASN1_MEM_ERROR; \ + } else { \ + if (ptr && data_size > 0) \ + memcpy (ptr, data, data_size); \ + } + +#define PUT_STR_VALUE( ptr, ptr_size, data) \ + *len = _asn1_strlen (data) + 1; \ + if (ptr_size < *len) { \ + return ASN1_MEM_ERROR; \ + } else { \ + /* this strcpy is checked */ \ + if (ptr) { \ + _asn1_strcpy (ptr, data); \ + } \ + } + +#define PUT_AS_STR_VALUE( ptr, ptr_size, data, data_size) \ + *len = data_size + 1; \ + if (ptr_size < *len) { \ + return ASN1_MEM_ERROR; \ + } else { \ + /* this strcpy is checked */ \ + if (ptr) { \ + if (data_size > 0) \ + memcpy (ptr, data, data_size); \ + ptr[data_size] = 0; \ + } \ + } + +#define ADD_STR_VALUE( ptr, ptr_size, data) \ + *len += _asn1_strlen(data); \ + if (ptr_size < (int) *len) { \ + (*len)++; \ + return ASN1_MEM_ERROR; \ + } else { \ + /* this strcat is checked */ \ + if (ptr) _asn1_strcat (ptr, data); \ + } + +/** + * asn1_read_value: + * @root: pointer to a structure. + * @name: the name of the element inside a structure that you want to read. + * @ivalue: vector that will contain the element's content, must be a + * pointer to memory cells already allocated (may be %NULL). + * @len: number of bytes of *value: value[0]..value[len-1]. Initialy + * holds the sizeof value. + * + * Returns the value of one element inside a structure. + * If an element is OPTIONAL and this returns + * %ASN1_ELEMENT_NOT_FOUND, it means that this element wasn't present + * in the der encoding that created the structure. The first element + * of a SEQUENCE_OF or SET_OF is named "?1". The second one "?2" and + * so on. If the @root provided is a node to specific sequence element, + * then the keyword "?CURRENT" is also acceptable and indicates the + * current sequence element of this node. + * + * Note that there can be valid values with length zero. In these case + * this function will succeed and @len will be zero. + * + * INTEGER: VALUE will contain a two's complement form integer. + * + * integer=-1 -> value[0]=0xFF , len=1. + * integer=1 -> value[0]=0x01 , len=1. + * + * ENUMERATED: As INTEGER (but only with not negative numbers). + * + * BOOLEAN: VALUE will be the null terminated string "TRUE" or + * "FALSE" and LEN=5 or LEN=6. + * + * OBJECT IDENTIFIER: VALUE will be a null terminated string with + * each number separated by a dot (i.e. "1.2.3.543.1"). + * + * LEN = strlen(VALUE)+1 + * + * UTCTime: VALUE will be a null terminated string in one of these + * formats: "YYMMDDhhmmss+hh'mm'" or "YYMMDDhhmmss-hh'mm'". + * LEN=strlen(VALUE)+1. + * + * GeneralizedTime: VALUE will be a null terminated string in the + * same format used to set the value. + * + * OCTET STRING: VALUE will contain the octet string and LEN will be + * the number of octets. + * + * GeneralString: VALUE will contain the generalstring and LEN will + * be the number of octets. + * + * BIT STRING: VALUE will contain the bit string organized by bytes + * and LEN will be the number of bits. + * + * CHOICE: If NAME indicates a choice type, VALUE will specify the + * alternative selected. + * + * ANY: If NAME indicates an any type, VALUE will indicate the DER + * encoding of the structure actually used. + * + * Returns: %ASN1_SUCCESS if value is returned, + * %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element, + * %ASN1_VALUE_NOT_FOUND if there isn't any value for the element + * selected, and %ASN1_MEM_ERROR if The value vector isn't big enough + * to store the result, and in this case @len will contain the number of + * bytes needed. On the occasion that the stored data are of zero-length + * this function may return %ASN1_SUCCESS even if the provided @len is zero. + **/ +int +asn1_read_value (asn1_node_const root, const char *name, void *ivalue, int *len) +{ + return asn1_read_value_type (root, name, ivalue, len, NULL); +} + +/** + * asn1_read_value_type: + * @root: pointer to a structure. + * @name: the name of the element inside a structure that you want to read. + * @ivalue: vector that will contain the element's content, must be a + * pointer to memory cells already allocated (may be %NULL). + * @len: number of bytes of *value: value[0]..value[len-1]. Initialy + * holds the sizeof value. + * @etype: The type of the value read (ASN1_ETYPE) + * + * Returns the type and value of one element inside a structure. + * If an element is OPTIONAL and this returns + * %ASN1_ELEMENT_NOT_FOUND, it means that this element wasn't present + * in the der encoding that created the structure. The first element + * of a SEQUENCE_OF or SET_OF is named "?1". The second one "?2" and + * so on. If the @root provided is a node to specific sequence element, + * then the keyword "?CURRENT" is also acceptable and indicates the + * current sequence element of this node. + * + * Note that there can be valid values with length zero. In these case + * this function will succeed and @len will be zero. + * + * + * INTEGER: VALUE will contain a two's complement form integer. + * + * integer=-1 -> value[0]=0xFF , len=1. + * integer=1 -> value[0]=0x01 , len=1. + * + * ENUMERATED: As INTEGER (but only with not negative numbers). + * + * BOOLEAN: VALUE will be the null terminated string "TRUE" or + * "FALSE" and LEN=5 or LEN=6. + * + * OBJECT IDENTIFIER: VALUE will be a null terminated string with + * each number separated by a dot (i.e. "1.2.3.543.1"). + * + * LEN = strlen(VALUE)+1 + * + * UTCTime: VALUE will be a null terminated string in one of these + * formats: "YYMMDDhhmmss+hh'mm'" or "YYMMDDhhmmss-hh'mm'". + * LEN=strlen(VALUE)+1. + * + * GeneralizedTime: VALUE will be a null terminated string in the + * same format used to set the value. + * + * OCTET STRING: VALUE will contain the octet string and LEN will be + * the number of octets. + * + * GeneralString: VALUE will contain the generalstring and LEN will + * be the number of octets. + * + * BIT STRING: VALUE will contain the bit string organized by bytes + * and LEN will be the number of bits. + * + * CHOICE: If NAME indicates a choice type, VALUE will specify the + * alternative selected. + * + * ANY: If NAME indicates an any type, VALUE will indicate the DER + * encoding of the structure actually used. + * + * Returns: %ASN1_SUCCESS if value is returned, + * %ASN1_ELEMENT_NOT_FOUND if @name is not a valid element, + * %ASN1_VALUE_NOT_FOUND if there isn't any value for the element + * selected, and %ASN1_MEM_ERROR if The value vector isn't big enough + * to store the result, and in this case @len will contain the number of + * bytes needed. On the occasion that the stored data are of zero-length + * this function may return %ASN1_SUCCESS even if the provided @len is zero. + **/ +int +asn1_read_value_type (asn1_node_const root, const char *name, void *ivalue, + int *len, unsigned int *etype) +{ + asn1_node_const node, p, p2; + int len2, len3, result; + int value_size = *len; + unsigned char *value = ivalue; + unsigned type; + + node = asn1_find_node (root, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + type = type_field (node->type); + + if ((type != ASN1_ETYPE_NULL) && + (type != ASN1_ETYPE_CHOICE) && + !(node->type & CONST_DEFAULT) && !(node->type & CONST_ASSIGN) && + (node->value == NULL)) + return ASN1_VALUE_NOT_FOUND; + + if (etype) + *etype = type; + switch (type) + { + case ASN1_ETYPE_NULL: + PUT_STR_VALUE (value, value_size, "NULL"); + break; + case ASN1_ETYPE_BOOLEAN: + if ((node->type & CONST_DEFAULT) && (node->value == NULL)) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if (p->type & CONST_TRUE) + { + PUT_STR_VALUE (value, value_size, "TRUE"); + } + else + { + PUT_STR_VALUE (value, value_size, "FALSE"); + } + } + else if (node->value[0] == 'T') + { + PUT_STR_VALUE (value, value_size, "TRUE"); + } + else + { + PUT_STR_VALUE (value, value_size, "FALSE"); + } + break; + case ASN1_ETYPE_INTEGER: + case ASN1_ETYPE_ENUMERATED: + if ((node->type & CONST_DEFAULT) && (node->value == NULL)) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + if ((c_isdigit (p->value[0])) || (p->value[0] == '-') + || (p->value[0] == '+')) + { + result = _asn1_convert_integer + (p->value, value, value_size, len); + if (result != ASN1_SUCCESS) + return result; + } + else + { /* is an identifier like v1 */ + p2 = node->down; + while (p2) + { + if (type_field (p2->type) == ASN1_ETYPE_CONSTANT) + { + if (!_asn1_strcmp (p2->name, p->value)) + { + result = _asn1_convert_integer + (p2->value, value, value_size, + len); + if (result != ASN1_SUCCESS) + return result; + break; + } + } + p2 = p2->right; + } + } + } + else + { + len2 = -1; + result = asn1_get_octet_der + (node->value, node->value_len, &len2, value, value_size, + len); + if (result != ASN1_SUCCESS) + return result; + } + break; + case ASN1_ETYPE_OBJECT_ID: + if (node->type & CONST_ASSIGN) + { + *len = 0; + if (value) + value[0] = 0; + p = node->down; + while (p) + { + if (type_field (p->type) == ASN1_ETYPE_CONSTANT) + { + ADD_STR_VALUE (value, value_size, p->value); + if (p->right) + { + ADD_STR_VALUE (value, value_size, "."); + } + } + p = p->right; + } + (*len)++; + } + else if ((node->type & CONST_DEFAULT) && (node->value == NULL)) + { + p = node->down; + while (type_field (p->type) != ASN1_ETYPE_DEFAULT) + p = p->right; + PUT_STR_VALUE (value, value_size, p->value); + } + else + { + PUT_STR_VALUE (value, value_size, node->value); + } + break; + case ASN1_ETYPE_GENERALIZED_TIME: + case ASN1_ETYPE_UTC_TIME: + PUT_AS_STR_VALUE (value, value_size, node->value, node->value_len); + break; + case ASN1_ETYPE_OCTET_STRING: + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + len2 = -1; + result = asn1_get_octet_der + (node->value, node->value_len, &len2, value, value_size, + len); + if (result != ASN1_SUCCESS) + return result; + break; + case ASN1_ETYPE_BIT_STRING: + len2 = -1; + result = asn1_get_bit_der + (node->value, node->value_len, &len2, value, value_size, + len); + if (result != ASN1_SUCCESS) + return result; + break; + case ASN1_ETYPE_CHOICE: + PUT_STR_VALUE (value, value_size, node->down->name); + break; + case ASN1_ETYPE_ANY: + len3 = -1; + len2 = asn1_get_length_der (node->value, node->value_len, &len3); + if (len2 < 0) + return ASN1_DER_ERROR; + PUT_VALUE (value, value_size, node->value + len3, len2); + break; + default: + return ASN1_ELEMENT_NOT_FOUND; + break; + } + return ASN1_SUCCESS; +} + + +/** + * asn1_read_tag: + * @root: pointer to a structure + * @name: the name of the element inside a structure. + * @tagValue: variable that will contain the TAG value. + * @classValue: variable that will specify the TAG type. + * + * Returns the TAG and the CLASS of one element inside a structure. + * CLASS can have one of these constants: %ASN1_CLASS_APPLICATION, + * %ASN1_CLASS_UNIVERSAL, %ASN1_CLASS_PRIVATE or + * %ASN1_CLASS_CONTEXT_SPECIFIC. + * + * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if + * @name is not a valid element. + **/ +int +asn1_read_tag (asn1_node_const root, const char *name, int *tagValue, + int *classValue) +{ + asn1_node node, p, pTag; + + node = asn1_find_node (root, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node->down; + + /* pTag will points to the IMPLICIT TAG */ + pTag = NULL; + if (node->type & CONST_TAG) + { + while (p) + { + if (type_field (p->type) == ASN1_ETYPE_TAG) + { + if ((p->type & CONST_IMPLICIT) && (pTag == NULL)) + pTag = p; + else if (p->type & CONST_EXPLICIT) + pTag = NULL; + } + p = p->right; + } + } + + if (pTag) + { + *tagValue = _asn1_strtoul (pTag->value, NULL, 10); + + if (pTag->type & CONST_APPLICATION) + *classValue = ASN1_CLASS_APPLICATION; + else if (pTag->type & CONST_UNIVERSAL) + *classValue = ASN1_CLASS_UNIVERSAL; + else if (pTag->type & CONST_PRIVATE) + *classValue = ASN1_CLASS_PRIVATE; + else + *classValue = ASN1_CLASS_CONTEXT_SPECIFIC; + } + else + { + unsigned type = type_field (node->type); + *classValue = ASN1_CLASS_UNIVERSAL; + + switch (type) + { + CASE_HANDLED_ETYPES: + *tagValue = _asn1_tags[type].tag; + break; + case ASN1_ETYPE_TAG: + case ASN1_ETYPE_CHOICE: + case ASN1_ETYPE_ANY: + *tagValue = -1; + break; + default: + break; + } + } + + return ASN1_SUCCESS; +} + +/** + * asn1_read_node_value: + * @node: pointer to a node. + * @data: a point to a asn1_data_node_st + * + * Returns the value a data node inside a asn1_node structure. + * The data returned should be handled as constant values. + * + * Returns: %ASN1_SUCCESS if the node exists. + **/ +int +asn1_read_node_value (asn1_node_const node, asn1_data_node_st * data) +{ + data->name = node->name; + data->value = node->value; + data->value_len = node->value_len; + data->type = type_field (node->type); + + return ASN1_SUCCESS; +} diff --git a/grub-core/lib/libtasn1/lib/element.h b/grub-core/lib/libtasn1/lib/element.h new file mode 100644 index 0000000000..440a33f4bb --- /dev/null +++ b/grub-core/lib/libtasn1/lib/element.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2000-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _ELEMENT_H +#define _ELEMENT_H + + +struct node_tail_cache_st +{ + asn1_node head; /* the first element of the sequence */ + asn1_node tail; +}; + +int _asn1_append_sequence_set (asn1_node node, struct node_tail_cache_st *pcached); + +int _asn1_convert_integer (const unsigned char *value, + unsigned char *value_out, + int value_out_size, int *len); + +void _asn1_hierarchical_name (asn1_node_const node, char *name, int name_size); + +#endif diff --git a/grub-core/lib/libtasn1/lib/errors.c b/grub-core/lib/libtasn1/lib/errors.c new file mode 100644 index 0000000000..42785e8622 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/errors.c @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include +#ifdef STDC_HEADERS +#include +#endif + +#define LIBTASN1_ERROR_ENTRY(name) { #name, name } + +struct libtasn1_error_entry +{ + const char *name; + int number; +}; +typedef struct libtasn1_error_entry libtasn1_error_entry; + +static const libtasn1_error_entry error_algorithms[] = { + LIBTASN1_ERROR_ENTRY (ASN1_SUCCESS), + LIBTASN1_ERROR_ENTRY (ASN1_FILE_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_ELEMENT_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_IDENTIFIER_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_DER_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_VALUE_NOT_FOUND), + LIBTASN1_ERROR_ENTRY (ASN1_GENERIC_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_VALUE_NOT_VALID), + LIBTASN1_ERROR_ENTRY (ASN1_TAG_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_TAG_IMPLICIT), + LIBTASN1_ERROR_ENTRY (ASN1_ERROR_TYPE_ANY), + LIBTASN1_ERROR_ENTRY (ASN1_SYNTAX_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_MEM_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_MEM_ALLOC_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_DER_OVERFLOW), + LIBTASN1_ERROR_ENTRY (ASN1_NAME_TOO_LONG), + LIBTASN1_ERROR_ENTRY (ASN1_ARRAY_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_ELEMENT_NOT_EMPTY), + LIBTASN1_ERROR_ENTRY (ASN1_TIME_ENCODING_ERROR), + LIBTASN1_ERROR_ENTRY (ASN1_RECURSION), + {0, 0} +}; + + +#if 0 +/** + * asn1_perror: + * @error: is an error returned by a libtasn1 function. + * + * Prints a string to stderr with a description of an error. This + * function is like perror(). The only difference is that it accepts + * an error returned by a libtasn1 function. + * + * Since: 1.6 + **/ +void +asn1_perror (int error) +{ + const char *str = asn1_strerror (error); + fprintf (stderr, "LIBTASN1 ERROR: %s\n", str ? str : "(null)"); +} +#endif + +/** + * asn1_strerror: + * @error: is an error returned by a libtasn1 function. + * + * Returns a string with a description of an error. This function is + * similar to strerror. The only difference is that it accepts an + * error (number) returned by a libtasn1 function. + * + * Returns: Pointer to static zero-terminated string describing error + * code. + * + * Since: 1.6 + **/ +const char * +asn1_strerror (int error) +{ + const libtasn1_error_entry *p; + + for (p = error_algorithms; p->name != NULL; p++) + if (p->number == error) + return p->name + sizeof ("ASN1_") - 1; + + return NULL; +} diff --git a/grub-core/lib/libtasn1/lib/gstr.c b/grub-core/lib/libtasn1/lib/gstr.c new file mode 100644 index 0000000000..e33875c2c7 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/gstr.c @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include +#include "gstr.h" + +/* These function are like strcat, strcpy. They only + * do bounds checking (they shouldn't cause buffer overruns), + * and they always produce null terminated strings. + * + * They should be used only with null terminated strings. + */ +void +_asn1_str_cat (char *dest, size_t dest_tot_size, const char *src) +{ + size_t str_size = strlen (src); + size_t dest_size = strlen (dest); + + if (dest_tot_size - dest_size > str_size) + { + memcpy (dest + dest_size, src, str_size + 1); + } + else + { + if (dest_tot_size - dest_size > 0) + { + memcpy (dest + dest_size, src, (dest_tot_size - dest_size) - 1); + dest[dest_tot_size - 1] = 0; + } + } +} + +/* Returns the bytes copied (not including the null terminator) */ +unsigned int +_asn1_str_cpy (char *dest, size_t dest_tot_size, const char *src) +{ + size_t str_size = strlen (src); + + if (dest_tot_size > str_size) + { + strcpy (dest, src); + return str_size; + } + else + { + if (dest_tot_size > 0) + { + str_size = dest_tot_size - 1; + memcpy (dest, src, str_size); + dest[str_size] = 0; + return str_size; + } + else + return 0; + } +} diff --git a/grub-core/lib/libtasn1/lib/gstr.h b/grub-core/lib/libtasn1/lib/gstr.h new file mode 100644 index 0000000000..48229844ff --- /dev/null +++ b/grub-core/lib/libtasn1/lib/gstr.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef GSTR_H +# define GSTR_H + +unsigned int _asn1_str_cpy (char *dest, size_t dest_tot_size, + const char *src); +void _asn1_str_cat (char *dest, size_t dest_tot_size, const char *src); + +#define Estrcpy(x,y) _asn1_str_cpy(x,ASN1_MAX_ERROR_DESCRIPTION_SIZE,y) +#define Estrcat(x,y) _asn1_str_cat(x,ASN1_MAX_ERROR_DESCRIPTION_SIZE,y) + +inline static +void safe_memset(void *data, int c, size_t size) +{ + volatile unsigned volatile_zero = 0; + volatile char *vdata = (volatile char*)data; + + /* This is based on a nice trick for safe memset, + * sent by David Jacobson in the openssl-dev mailing list. + */ + + if (size > 0) do { + memset(data, c, size); + } while(vdata[volatile_zero] != c); +} + +#endif /* GSTR_H */ diff --git a/grub-core/lib/libtasn1/lib/int.h b/grub-core/lib/libtasn1/lib/int.h new file mode 100644 index 0000000000..4a568efee9 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/int.h @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef INT_H +#define INT_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include "grub/libtasn1.h" + +#define ASN1_SMALL_VALUE_SIZE 16 + +/* This structure is also in libtasn1.h, but then contains less + fields. You cannot make any modifications to these first fields + without breaking ABI. */ +struct asn1_node_st +{ + /* public fields: */ + char name[ASN1_MAX_NAME_SIZE + 1]; /* Node name */ + unsigned int name_hash; + unsigned int type; /* Node type */ + unsigned char *value; /* Node value */ + int value_len; + asn1_node down; /* Pointer to the son node */ + asn1_node right; /* Pointer to the brother node */ + asn1_node left; /* Pointer to the next list element */ + /* private fields: */ + unsigned char small_value[ASN1_SMALL_VALUE_SIZE]; /* For small values */ + + /* values used during decoding/coding */ + int tmp_ival; + unsigned start; /* the start of the DER sequence - if decoded */ + unsigned end; /* the end of the DER sequence - if decoded */ +}; + +typedef struct tag_and_class_st +{ + unsigned tag; + unsigned class; + const char *desc; +} tag_and_class_st; + +/* the types that are handled in _asn1_tags */ +#define CASE_HANDLED_ETYPES \ + case ASN1_ETYPE_NULL: \ + case ASN1_ETYPE_BOOLEAN: \ + case ASN1_ETYPE_INTEGER: \ + case ASN1_ETYPE_ENUMERATED: \ + case ASN1_ETYPE_OBJECT_ID: \ + case ASN1_ETYPE_OCTET_STRING: \ + case ASN1_ETYPE_GENERALSTRING: \ + case ASN1_ETYPE_NUMERIC_STRING: \ + case ASN1_ETYPE_IA5_STRING: \ + case ASN1_ETYPE_TELETEX_STRING: \ + case ASN1_ETYPE_PRINTABLE_STRING: \ + case ASN1_ETYPE_UNIVERSAL_STRING: \ + case ASN1_ETYPE_BMP_STRING: \ + case ASN1_ETYPE_UTF8_STRING: \ + case ASN1_ETYPE_VISIBLE_STRING: \ + case ASN1_ETYPE_BIT_STRING: \ + case ASN1_ETYPE_SEQUENCE: \ + case ASN1_ETYPE_SEQUENCE_OF: \ + case ASN1_ETYPE_SET: \ + case ASN1_ETYPE_UTC_TIME: \ + case ASN1_ETYPE_GENERALIZED_TIME: \ + case ASN1_ETYPE_SET_OF + +#define ETYPE_TAG(etype) (_asn1_tags[etype].tag) +#define ETYPE_CLASS(etype) (_asn1_tags[etype].class) +#define ETYPE_OK(etype) (((etype) != ASN1_ETYPE_INVALID && \ + (etype) <= _asn1_tags_size && \ + _asn1_tags[(etype)].desc != NULL)?1:0) + +#define ETYPE_IS_STRING(etype) ((etype == ASN1_ETYPE_GENERALSTRING || \ + etype == ASN1_ETYPE_NUMERIC_STRING || etype == ASN1_ETYPE_IA5_STRING || \ + etype == ASN1_ETYPE_TELETEX_STRING || etype == ASN1_ETYPE_PRINTABLE_STRING || \ + etype == ASN1_ETYPE_UNIVERSAL_STRING || etype == ASN1_ETYPE_BMP_STRING || \ + etype == ASN1_ETYPE_UTF8_STRING || etype == ASN1_ETYPE_VISIBLE_STRING || \ + etype == ASN1_ETYPE_OCTET_STRING)?1:0) + +extern unsigned int _asn1_tags_size; +extern const tag_and_class_st _asn1_tags[]; + +#define _asn1_strlen(s) strlen((const char *) s) +#define _asn1_strtol(n,e,b) strtol((const char *) n, e, b) +#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) memcpy((char *)a + strlen((const char *)a), (const char *)b, strlen((const char *)b) + 1) + +#if SIZEOF_UNSIGNED_LONG_INT == 8 +# define _asn1_strtou64(n,e,b) strtoul((const char *) n, e, b) +#else +# define _asn1_strtou64(n,e,b) strtoull((const char *) n, e, b) +#endif + +#define MAX_LOG_SIZE 1024 /* maximum number of characters of a log message */ + +/* Define used for visiting trees. */ +#define UP 1 +#define RIGHT 2 +#define DOWN 3 + +/***********************************************************************/ +/* List of constants to better specify the type of typedef asn1_node_st. */ +/***********************************************************************/ +/* Used with TYPE_TAG */ +#define CONST_UNIVERSAL (1U<<8) +#define CONST_PRIVATE (1U<<9) +#define CONST_APPLICATION (1U<<10) +#define CONST_EXPLICIT (1U<<11) +#define CONST_IMPLICIT (1U<<12) + +#define CONST_TAG (1U<<13) /* Used in ASN.1 assignement */ +#define CONST_OPTION (1U<<14) +#define CONST_DEFAULT (1U<<15) +#define CONST_TRUE (1U<<16) +#define CONST_FALSE (1U<<17) + +#define CONST_LIST (1U<<18) /* Used with TYPE_INTEGER and TYPE_BIT_STRING */ +#define CONST_MIN_MAX (1U<<19) + +#define CONST_1_PARAM (1U<<20) + +#define CONST_SIZE (1U<<21) + +#define CONST_DEFINED_BY (1U<<22) + +/* Those two are deprecated and used for backwards compatibility */ +#define CONST_GENERALIZED (1U<<23) +#define CONST_UTC (1U<<24) + +/* #define CONST_IMPORTS (1U<<25) */ + +#define CONST_NOT_USED (1U<<26) +#define CONST_SET (1U<<27) +#define CONST_ASSIGN (1U<<28) + +#define CONST_DOWN (1U<<29) +#define CONST_RIGHT (1U<<30) + + +#define ASN1_ETYPE_TIME 17 +/****************************************/ +/* Returns the first 8 bits. */ +/* Used with the field type of asn1_node_st */ +/****************************************/ +inline static unsigned int +type_field (unsigned int ntype) +{ + return (ntype & 0xff); +} + +/* To convert old types from a static structure */ +inline static unsigned int +convert_old_type (unsigned int ntype) +{ + unsigned int type = ntype & 0xff; + if (type == ASN1_ETYPE_TIME) + { + if (ntype & CONST_UTC) + type = ASN1_ETYPE_UTC_TIME; + else + type = ASN1_ETYPE_GENERALIZED_TIME; + + ntype &= ~(CONST_UTC | CONST_GENERALIZED); + ntype &= 0xffffff00; + ntype |= type; + + return ntype; + } + else + return ntype; +} + +static inline +void *_asn1_realloc(void *ptr, size_t size) +{ + void *ret; + + if (size == 0) + return ptr; + + ret = realloc(ptr, size); + if (ret == NULL) + { + free(ptr); + } + return ret; +} + +#endif /* INT_H */ diff --git a/grub-core/lib/libtasn1/lib/parser_aux.c b/grub-core/lib/libtasn1/lib/parser_aux.c new file mode 100644 index 0000000000..89c9be69dc --- /dev/null +++ b/grub-core/lib/libtasn1/lib/parser_aux.c @@ -0,0 +1,1174 @@ +/* + * Copyright (C) 2000-2016 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#include // WORD_BIT + +#include "int.h" +#include "parser_aux.h" +#include "gstr.h" +#include "structure.h" +#include "element.h" + +#define c_isdigit grub_isdigit + +char _asn1_identifierMissing[ASN1_MAX_NAME_SIZE + 1]; /* identifier name not found */ + +/* Return a hash of the N bytes of X using the method described by + Bruno Haible in https://www.haible.de/bruno/hashfunc.html. + Note that while many hash functions reduce their result via modulo + to a 0..table_size-1 range, this function does not do that. + + This implementation has been changed from size_t -> unsigned int. */ + +#ifdef __clang__ +__attribute__((no_sanitize("integer"))) +#endif +__attribute__((__pure__)) +static unsigned int +_asn1_hash_name (const char *x) +{ + const unsigned char *s = (unsigned char *) x; + unsigned h = 0; + + while (*s) + h = (*s++) + ((h << 9) | (h >> (WORD_BIT - 9))); + + return h; +} + +/******************************************************/ +/* Function : _asn1_add_static_node */ +/* Description: creates a new NODE_ASN element and */ +/* puts it in the list pointed by e_list. */ +/* Parameters: */ +/* e_list: of type list_type; must be NULL initially */ +/* type: type of the new element (see ASN1_ETYPE_ */ +/* and CONST_ constants). */ +/* Return: pointer to the new element. */ +/******************************************************/ +asn1_node +_asn1_add_static_node (list_type **e_list, unsigned int type) +{ + list_type *p; + asn1_node punt; + + punt = calloc (1, sizeof (struct asn1_node_st)); + if (punt == NULL) + return NULL; + + p = malloc (sizeof (list_type)); + if (p == NULL) + { + free (punt); + return NULL; + } + + p->node = punt; + p->next = *e_list; + *e_list = p; + + punt->type = type; + + return punt; +} + +static +int _asn1_add_static_node2 (list_type **e_list, asn1_node node) +{ + list_type *p; + + p = malloc (sizeof (list_type)); + if (p == NULL) + { + return -1; + } + + p->node = node; + p->next = *e_list; + *e_list = p; + + return 0; +} + +/** + * asn1_find_node: + * @pointer: NODE_ASN element pointer. + * @name: null terminated string with the element's name to find. + * + * Searches for an element called @name starting from @pointer. The + * name is composed by different identifiers separated by dots. When + * *@pointer has a name, the first identifier must be the name of + * *@pointer, otherwise it must be the name of one child of *@pointer. + * + * Returns: the search result, or %NULL if not found. + **/ +asn1_node +asn1_find_node (asn1_node_const pointer, const char *name) +{ + asn1_node_const p; + char *n_end, n[ASN1_MAX_NAME_SIZE + 1]; + const char *n_start; + unsigned int nsize; + unsigned int nhash; + + if (pointer == NULL) + return NULL; + + if (name == NULL) + return NULL; + + p = pointer; + n_start = name; + + if (name[0] == '?' && name[1] == 'C' && p->name[0] == '?') + { /* ?CURRENT */ + n_start = strchr(n_start, '.'); + if (n_start) + n_start++; + } + else if (p->name[0] != 0) + { /* has *pointer got a name ? */ + n_end = strchr (n_start, '.'); /* search the first dot */ + if (n_end) + { + nsize = n_end - n_start; + if (nsize >= sizeof(n)) + return NULL; + + memcpy (n, n_start, nsize); + n[nsize] = 0; + n_start = n_end; + n_start++; + + nhash = _asn1_hash_name (n); + } + else + { + _asn1_str_cpy (n, sizeof (n), n_start); + nhash = _asn1_hash_name (n); + + n_start = NULL; + } + + while (p) + { + if (nhash == p->name_hash && (!strcmp (p->name, n))) + break; + else + p = p->right; + } /* while */ + + if (p == NULL) + return NULL; + } + else + { /* *pointer doesn't have a name */ + if (n_start[0] == 0) + return (asn1_node) p; + } + + while (n_start) + { /* Has the end of NAME been reached? */ + n_end = strchr (n_start, '.'); /* search the next dot */ + if (n_end) + { + nsize = n_end - n_start; + if (nsize >= sizeof(n)) + return NULL; + + memcpy (n, n_start, nsize); + n[nsize] = 0; + n_start = n_end; + n_start++; + + nhash = _asn1_hash_name (n); + } + else + { + _asn1_str_cpy (n, sizeof (n), n_start); + nhash = _asn1_hash_name (n); + n_start = NULL; + } + + if (p->down == NULL) + return NULL; + + p = p->down; + if (p == NULL) + return NULL; + + /* The identifier "?LAST" indicates the last element + in the right chain. */ + if (n[0] == '?' && n[1] == 'L') /* ?LAST */ + { + while (p->right) + p = p->right; + } + else + { /* no "?LAST" */ + while (p) + { + if (p->name_hash == nhash && !strcmp (p->name, n)) + break; + else + p = p->right; + } + } + if (p == NULL) + return NULL; + } /* while */ + + return (asn1_node) p; +} + + +/******************************************************************/ +/* Function : _asn1_set_value */ +/* Description: sets the field VALUE in a NODE_ASN element. The */ +/* previous value (if exist) will be lost */ +/* Parameters: */ +/* node: element pointer. */ +/* value: pointer to the value that you want to set. */ +/* len: character number of value. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +asn1_node +_asn1_set_value (asn1_node node, const void *value, unsigned int len) +{ + if (node == NULL) + return node; + if (node->value) + { + if (node->value != node->small_value) + free (node->value); + node->value = NULL; + node->value_len = 0; + } + + if (!len) + return node; + + if (len < sizeof (node->small_value)) + { + node->value = node->small_value; + } + else + { + node->value = malloc (len); + if (node->value == NULL) + return NULL; + } + node->value_len = len; + + memcpy (node->value, value, len); + return node; +} + +/******************************************************************/ +/* Function : _asn1_set_value_lv */ +/* Description: sets the field VALUE in a NODE_ASN element. The */ +/* previous value (if exist) will be lost. The value */ +/* given is stored as an length-value format (LV */ +/* Parameters: */ +/* node: element pointer. */ +/* value: pointer to the value that you want to set. */ +/* len: character number of value. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +asn1_node +_asn1_set_value_lv (asn1_node node, const void *value, unsigned int len) +{ + int len2; + void *temp; + + if (node == NULL) + return node; + + asn1_length_der (len, NULL, &len2); + temp = malloc (len + len2); + if (temp == NULL) + return NULL; + + asn1_octet_der (value, len, temp, &len2); + return _asn1_set_value_m (node, temp, len2); +} + +/* the same as _asn1_set_value except that it sets an already malloc'ed + * value. + */ +asn1_node +_asn1_set_value_m (asn1_node node, void *value, unsigned int len) +{ + if (node == NULL) + return node; + + if (node->value) + { + if (node->value != node->small_value) + free (node->value); + node->value = NULL; + node->value_len = 0; + } + + if (!len) + return node; + + node->value = value; + node->value_len = len; + + return node; +} + +/******************************************************************/ +/* Function : _asn1_append_value */ +/* Description: appends to the field VALUE in a NODE_ASN element. */ +/* */ +/* Parameters: */ +/* node: element pointer. */ +/* value: pointer to the value that you want to be appended. */ +/* len: character number of value. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +asn1_node +_asn1_append_value (asn1_node node, const void *value, unsigned int len) +{ + if (node == NULL) + return node; + + if (node->value == NULL) + return _asn1_set_value (node, value, len); + + if (len == 0) + return node; + + if (node->value == node->small_value) + { + /* value is in node */ + int prev_len = node->value_len; + node->value_len += len; + node->value = malloc (node->value_len); + if (node->value == NULL) + { + node->value_len = 0; + return NULL; + } + + if (prev_len > 0) + memcpy (node->value, node->small_value, prev_len); + + memcpy (&node->value[prev_len], value, len); + + return node; + } + else /* if (node->value != NULL && node->value != node->small_value) */ + { + /* value is allocated */ + int prev_len = node->value_len; + node->value_len += len; + + node->value = _asn1_realloc (node->value, node->value_len); + if (node->value == NULL) + { + node->value_len = 0; + return NULL; + } + + memcpy (&node->value[prev_len], value, len); + + return node; + } +} + +/******************************************************************/ +/* Function : _asn1_set_name */ +/* Description: sets the field NAME in a NODE_ASN element. The */ +/* previous value (if exist) will be lost */ +/* Parameters: */ +/* node: element pointer. */ +/* name: a null terminated string with the name that you want */ +/* to set. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +asn1_node +_asn1_set_name (asn1_node node, const char *name) +{ + if (node == NULL) + return node; + + _asn1_str_cpy (node->name, sizeof (node->name), name ? name : ""); + node->name_hash = _asn1_hash_name (node->name); + + return node; +} + +/******************************************************************/ +/* Function : _asn1_cpy_name */ +/* Description: copies the field NAME in a NODE_ASN element. */ +/* Parameters: */ +/* dst: a dest element pointer. */ +/* src: a source element pointer. */ +/* Return: pointer to the NODE_ASN element. */ +/******************************************************************/ +asn1_node +_asn1_cpy_name (asn1_node dst, asn1_node_const src) +{ + if (dst == NULL) + return dst; + + if (src == NULL) + { + dst->name[0] = 0; + dst->name_hash = _asn1_hash_name (dst->name); + return dst; + } + + _asn1_str_cpy (dst->name, sizeof (dst->name), src->name); + dst->name_hash = src->name_hash; + + return dst; +} + +/******************************************************************/ +/* Function : _asn1_set_right */ +/* Description: sets the field RIGHT in a NODE_ASN element. */ +/* Parameters: */ +/* node: element pointer. */ +/* right: pointer to a NODE_ASN element that you want be pointed*/ +/* by NODE. */ +/* Return: pointer to *NODE. */ +/******************************************************************/ +asn1_node +_asn1_set_right (asn1_node node, asn1_node right) +{ + if (node == NULL) + return node; + node->right = right; + if (right) + right->left = node; + return node; +} + + +/******************************************************************/ +/* Function : _asn1_get_last_right */ +/* Description: return the last element along the right chain. */ +/* Parameters: */ +/* node: starting element pointer. */ +/* Return: pointer to the last element along the right chain. */ +/******************************************************************/ +asn1_node +_asn1_get_last_right (asn1_node_const node) +{ + asn1_node_const p; + + if (node == NULL) + return NULL; + p = node; + while (p->right) + p = p->right; + return (asn1_node) p; +} + +/******************************************************************/ +/* Function : _asn1_remove_node */ +/* Description: gets free the memory allocated for an NODE_ASN */ +/* element (not the elements pointed by it). */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* flags: ASN1_DELETE_FLAG_* */ +/******************************************************************/ +void +_asn1_remove_node (asn1_node node, unsigned int flags) +{ + if (node == NULL) + return; + + if (node->value != NULL) + { + if (flags & ASN1_DELETE_FLAG_ZEROIZE) + { + safe_memset(node->value, 0, node->value_len); + } + + if (node->value != node->small_value) + free (node->value); + } + free (node); +} + +/******************************************************************/ +/* Function : _asn1_find_up */ +/* Description: return the father of the NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: Null if not found. */ +/******************************************************************/ +asn1_node +_asn1_find_up (asn1_node_const node) +{ + asn1_node_const p; + + if (node == NULL) + return NULL; + + p = node; + + while ((p->left != NULL) && (p->left->right == p)) + p = p->left; + + return p->left; +} + +static +unsigned _asn1_is_up (asn1_node_const up_cand, asn1_node_const down) +{ + asn1_node_const d, u; + + if (up_cand == NULL || down == NULL) + return 0; + + d = down; + + while ((u = _asn1_find_up(d)) != NULL && u != d) + { + if (u == up_cand) + return 1; + d = u; + } + + return 0; +} + +/******************************************************************/ +/* Function : _asn1_delete_node_from_list */ +/* Description: deletes the list element given */ +/******************************************************************/ +void +_asn1_delete_node_from_list (list_type *list, asn1_node node) +{ + list_type *p = list; + + while (p) + { + if (p->node == node) + p->node = NULL; + p = p->next; + } +} + +/******************************************************************/ +/* Function : _asn1_delete_list */ +/* Description: deletes the list elements (not the elements */ +/* pointed by them). */ +/******************************************************************/ +void +_asn1_delete_list (list_type *e_list) +{ + list_type *p; + + while (e_list) + { + p = e_list; + e_list = e_list->next; + free (p); + } +} + +/******************************************************************/ +/* Function : _asn1_delete_list_and nodes */ +/* Description: deletes the list elements and the elements */ +/* pointed by them. */ +/******************************************************************/ +void +_asn1_delete_list_and_nodes (list_type *e_list) +{ + list_type *p; + + while (e_list) + { + p = e_list; + e_list = e_list->next; + _asn1_remove_node (p->node, 0); + free (p); + } +} + + +char * +_asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE]) +{ + uint64_t d, r; + char temp[LTOSTR_MAX_SIZE]; + int count, k, start; + uint64_t val; + + if (v < 0) + { + str[0] = '-'; + start = 1; + val = -((uint64_t)v); + } + else + { + val = v; + start = 0; + } + + count = 0; + do + { + d = grub_divmod64(val, 10, NULL); + r = val - d * 10; + temp[start + count] = '0' + (char) r; + count++; + val = d; + } + while (val && ((start+count) < LTOSTR_MAX_SIZE-1)); + + for (k = 0; k < count; k++) + str[k + start] = temp[start + count - k - 1]; + str[count + start] = 0; + return str; +} + + +/******************************************************************/ +/* Function : _asn1_change_integer_value */ +/* Description: converts into DER coding the value assign to an */ +/* INTEGER constant. */ +/* Parameters: */ +/* node: root of an ASN1element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_change_integer_value (asn1_node node) +{ + asn1_node p; + unsigned char val[SIZEOF_UNSIGNED_LONG_INT]; + unsigned char val2[SIZEOF_UNSIGNED_LONG_INT + 1]; + int len; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if ((type_field (p->type) == ASN1_ETYPE_INTEGER) + && (p->type & CONST_ASSIGN)) + { + if (p->value) + { + _asn1_convert_integer (p->value, val, sizeof (val), &len); + asn1_octet_der (val, len, val2, &len); + _asn1_set_value (p, val2, len); + } + } + + if (p->down) + { + p = p->down; + } + else + { + if (p == node) + p = NULL; + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p && p->right) + { + p = p->right; + break; + } + } + } + } + } + + return ASN1_SUCCESS; +} + +#define MAX_CONSTANTS 1024 +/******************************************************************/ +/* Function : _asn1_expand_object_id */ +/* Description: expand the IDs of an OBJECT IDENTIFIER constant. */ +/* Parameters: */ +/* list: root of an object list */ +/* node: root of an ASN1 element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_expand_object_id (list_type **list, asn1_node node) +{ + asn1_node p, p2, p3, p4, p5; + char name_root[ASN1_MAX_NAME_SIZE], name2[2 * ASN1_MAX_NAME_SIZE + 1]; + int move, tlen, tries; + unsigned max_constants; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + _asn1_str_cpy (name_root, sizeof (name_root), node->name); + + p = node; + move = DOWN; + tries = 0; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) + && (p->type & CONST_ASSIGN)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == ASN1_ETYPE_CONSTANT)) + { + if (p2->value && !c_isdigit (p2->value[0])) + { + _asn1_str_cpy (name2, sizeof (name2), name_root); + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), (char *) p2->value); + p3 = asn1_find_node (node, name2); + if (!p3 || _asn1_is_up(p2, p3) || + (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) || + !(p3->type & CONST_ASSIGN)) + return ASN1_ELEMENT_NOT_FOUND; + + _asn1_set_down (p, p2->right); + if (p2->down) + _asn1_delete_structure (*list, &p2->down, 0); + _asn1_delete_node_from_list(*list, p2); + _asn1_remove_node (p2, 0); + p2 = p; + p4 = p3->down; + max_constants = 0; + while (p4) + { + if (type_field (p4->type) == ASN1_ETYPE_CONSTANT) + { + max_constants++; + if (max_constants == MAX_CONSTANTS) + return ASN1_RECURSION; + + p5 = + _asn1_add_single_node (ASN1_ETYPE_CONSTANT); + _asn1_set_name (p5, p4->name); + if (p4->value) + { + tlen = _asn1_strlen (p4->value); + if (tlen > 0) + _asn1_set_value (p5, p4->value, tlen + 1); + } + _asn1_add_static_node2(list, p5); + + if (p2 == p) + { + _asn1_set_right (p5, p->down); + _asn1_set_down (p, p5); + } + else + { + _asn1_set_right (p5, p2->right); + _asn1_set_right (p2, p5); + } + p2 = p5; + } + p4 = p4->right; + } + move = DOWN; + + tries++; + if (tries >= EXPAND_OBJECT_ID_MAX_RECURSION) + return ASN1_RECURSION; + + continue; + } + } + } + move = DOWN; + } + else + move = RIGHT; + + tries = 0; + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p && p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + /*******************************/ + /* expand DEFAULT */ + /*******************************/ + p = node; + move = DOWN; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && + (p->type & CONST_DEFAULT)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == ASN1_ETYPE_DEFAULT)) + { + _asn1_str_cpy (name2, sizeof (name2), name_root); + _asn1_str_cat (name2, sizeof (name2), "."); + if (p2->value) + _asn1_str_cat (name2, sizeof (name2), (char *) p2->value); + p3 = asn1_find_node (node, name2); + if (!p3 || (type_field (p3->type) != ASN1_ETYPE_OBJECT_ID) + || !(p3->type & CONST_ASSIGN)) + return ASN1_ELEMENT_NOT_FOUND; + p4 = p3->down; + name2[0] = 0; + while (p4) + { + if (type_field (p4->type) == ASN1_ETYPE_CONSTANT) + { + if (p4->value == NULL) + return ASN1_VALUE_NOT_FOUND; + + if (name2[0]) + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), + (char *) p4->value); + } + p4 = p4->right; + } + tlen = strlen (name2); + if (tlen > 0) + _asn1_set_value (p2, name2, tlen + 1); + } + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p && p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +/******************************************************************/ +/* Function : _asn1_type_set_config */ +/* Description: sets the CONST_SET and CONST_NOT_USED properties */ +/* in the fields of the SET elements. */ +/* Parameters: */ +/* node: root of an ASN1 element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_type_set_config (asn1_node node) +{ + asn1_node p, p2; + int move; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + move = DOWN; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if (type_field (p->type) == ASN1_ETYPE_SET) + { + p2 = p->down; + while (p2) + { + if (type_field (p2->type) != ASN1_ETYPE_TAG) + p2->type |= CONST_SET | CONST_NOT_USED; + p2 = p2->right; + } + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p && p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +/******************************************************************/ +/* Function : _asn1_check_identifier */ +/* Description: checks the definitions of all the identifiers */ +/* and the first element of an OBJECT_ID (e.g. {pkix 0 4}). */ +/* The _asn1_identifierMissing global variable is filled if */ +/* necessary. */ +/* Parameters: */ +/* node: root of an ASN1 element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL, */ +/* ASN1_IDENTIFIER_NOT_FOUND if an identifier is not defined, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_check_identifier (asn1_node_const node) +{ + asn1_node_const p, p2; + char name2[ASN1_MAX_NAME_SIZE * 2 + 2]; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if (p->value && type_field (p->type) == ASN1_ETYPE_IDENTIFIER) + { + _asn1_str_cpy (name2, sizeof (name2), node->name); + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), (char *) p->value); + p2 = asn1_find_node (node, name2); + if (p2 == NULL) + { + if (p->value) + _asn1_str_cpy (_asn1_identifierMissing, sizeof(_asn1_identifierMissing), (char*)p->value); + else + _asn1_strcpy (_asn1_identifierMissing, "(null)"); + return ASN1_IDENTIFIER_NOT_FOUND; + } + } + else if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && + (p->type & CONST_DEFAULT)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == ASN1_ETYPE_DEFAULT)) + { + _asn1_str_cpy (name2, sizeof (name2), node->name); + if (p2->value) + { + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), (char *) p2->value); + _asn1_str_cpy (_asn1_identifierMissing, sizeof(_asn1_identifierMissing), (char*)p2->value); + } + else + _asn1_strcpy (_asn1_identifierMissing, "(null)"); + + p2 = asn1_find_node (node, name2); + if (!p2 || (type_field (p2->type) != ASN1_ETYPE_OBJECT_ID) || + !(p2->type & CONST_ASSIGN)) + return ASN1_IDENTIFIER_NOT_FOUND; + else + _asn1_identifierMissing[0] = 0; + } + } + else if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && + (p->type & CONST_ASSIGN)) + { + p2 = p->down; + if (p2 && (type_field (p2->type) == ASN1_ETYPE_CONSTANT)) + { + if (p2->value && !c_isdigit (p2->value[0])) + { + _asn1_str_cpy (name2, sizeof (name2), node->name); + _asn1_str_cat (name2, sizeof (name2), "."); + _asn1_str_cat (name2, sizeof (name2), (char *) p2->value); + _asn1_str_cpy (_asn1_identifierMissing, sizeof(_asn1_identifierMissing), (char*)p2->value); + + p2 = asn1_find_node (node, name2); + if (!p2 || (type_field (p2->type) != ASN1_ETYPE_OBJECT_ID) + || !(p2->type & CONST_ASSIGN)) + return ASN1_IDENTIFIER_NOT_FOUND; + else + _asn1_identifierMissing[0] = 0; + } + } + } + + if (p->down) + { + p = p->down; + } + else if (p->right) + p = p->right; + else + { + while (p) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p && p->right) + { + p = p->right; + break; + } + } + } + } + + return ASN1_SUCCESS; +} + + +/******************************************************************/ +/* Function : _asn1_set_default_tag */ +/* Description: sets the default IMPLICIT or EXPLICIT property in */ +/* the tagged elements that don't have this declaration. */ +/* Parameters: */ +/* node: pointer to a DEFINITIONS element. */ +/* Return: */ +/* ASN1_ELEMENT_NOT_FOUND if NODE is NULL or not a pointer to */ +/* a DEFINITIONS element, */ +/* otherwise ASN1_SUCCESS */ +/******************************************************************/ +int +_asn1_set_default_tag (asn1_node node) +{ + asn1_node p; + + if ((node == NULL) || (type_field (node->type) != ASN1_ETYPE_DEFINITIONS)) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + while (p) + { + if ((type_field (p->type) == ASN1_ETYPE_TAG) && + !(p->type & CONST_EXPLICIT) && !(p->type & CONST_IMPLICIT)) + { + if (node->type & CONST_EXPLICIT) + p->type |= CONST_EXPLICIT; + else + p->type |= CONST_IMPLICIT; + } + + if (p->down) + { + p = p->down; + } + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == node) + { + p = NULL; + break; + } + if (p && p->right) + { + p = p->right; + break; + } + } + } + } + + return ASN1_SUCCESS; +} diff --git a/grub-core/lib/libtasn1/lib/parser_aux.h b/grub-core/lib/libtasn1/lib/parser_aux.h new file mode 100644 index 0000000000..598e684b35 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/parser_aux.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2000-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +#ifndef _PARSER_AUX_H +#define _PARSER_AUX_H + +/***********************************************/ +/* Type: list_type */ +/* Description: type used in the list during */ +/* the structure creation. */ +/***********************************************/ +typedef struct list_struct +{ + asn1_node node; + struct list_struct *next; +} list_type; + +/***************************************/ +/* Functions used by ASN.1 parser */ +/***************************************/ +asn1_node _asn1_add_static_node (list_type **e_list, unsigned int type); + +void _asn1_delete_list (list_type *e_list); + +void _asn1_delete_list_and_nodes (list_type *e_list); + +void _asn1_delete_node_from_list (list_type *list, asn1_node node); + +asn1_node +_asn1_set_value (asn1_node node, const void *value, unsigned int len); + +asn1_node _asn1_set_value_m (asn1_node node, void *value, unsigned int len); + +asn1_node +_asn1_set_value_lv (asn1_node node, const void *value, unsigned int len); + +asn1_node +_asn1_append_value (asn1_node node, const void *value, unsigned int len); + +asn1_node _asn1_set_name (asn1_node node, const char *name); + +asn1_node _asn1_cpy_name (asn1_node dst, asn1_node_const src); + +asn1_node _asn1_set_right (asn1_node node, asn1_node right); + +asn1_node _asn1_get_last_right (asn1_node_const node); + +void _asn1_remove_node (asn1_node node, unsigned int flags); + +/* Max 64-bit integer length is 20 chars + 1 for sign + 1 for null termination */ +#define LTOSTR_MAX_SIZE 22 +char *_asn1_ltostr (int64_t v, char str[LTOSTR_MAX_SIZE]); + +asn1_node _asn1_find_up (asn1_node_const node); + +int _asn1_change_integer_value (asn1_node node); + +#define EXPAND_OBJECT_ID_MAX_RECURSION 16 +int _asn1_expand_object_id (list_type **list, asn1_node node); + +int _asn1_type_set_config (asn1_node node); + +int _asn1_check_identifier (asn1_node_const node); + +int _asn1_set_default_tag (asn1_node node); + +/******************************************************************/ +/* Function : _asn1_get_right */ +/* Description: returns the element pointed by the RIGHT field of */ +/* a NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: field RIGHT of NODE. */ +/******************************************************************/ +inline static asn1_node +_asn1_get_right (asn1_node_const node) +{ + if (node == NULL) + return NULL; + return node->right; +} + +/******************************************************************/ +/* Function : _asn1_set_down */ +/* Description: sets the field DOWN in a NODE_ASN element. */ +/* Parameters: */ +/* node: element pointer. */ +/* down: pointer to a NODE_ASN element that you want be pointed */ +/* by NODE. */ +/* Return: pointer to *NODE. */ +/******************************************************************/ +inline static asn1_node +_asn1_set_down (asn1_node node, asn1_node down) +{ + if (node == NULL) + return node; + node->down = down; + if (down) + down->left = node; + return node; +} + +/******************************************************************/ +/* Function : _asn1_get_down */ +/* Description: returns the element pointed by the DOWN field of */ +/* a NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: field DOWN of NODE. */ +/******************************************************************/ +inline static asn1_node +_asn1_get_down (asn1_node_const node) +{ + if (node == NULL) + return NULL; + return node->down; +} + +/******************************************************************/ +/* Function : _asn1_get_name */ +/* Description: returns the name of a NODE_ASN element. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: a null terminated string. */ +/******************************************************************/ +inline static char * +_asn1_get_name (asn1_node_const node) +{ + if (node == NULL) + return NULL; + return (char *) node->name; +} + +/******************************************************************/ +/* Function : _asn1_mod_type */ +/* Description: change the field TYPE of an NODE_ASN element. */ +/* The new value is the old one | (bitwise or) the */ +/* paramener VALUE. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* value: the integer value that must be or-ed with the current */ +/* value of field TYPE. */ +/* Return: NODE pointer. */ +/******************************************************************/ +inline static asn1_node +_asn1_mod_type (asn1_node node, unsigned int value) +{ + if (node == NULL) + return node; + node->type |= value; + return node; +} + +#endif diff --git a/grub-core/lib/libtasn1/lib/structure.c b/grub-core/lib/libtasn1/lib/structure.c new file mode 100644 index 0000000000..fcfde01a39 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/structure.c @@ -0,0 +1,1222 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + + +/*****************************************************/ +/* File: structure.c */ +/* Description: Functions to create and delete an */ +/* ASN1 tree. */ +/*****************************************************/ + + +#include +#include +#include "parser_aux.h" +#include + + +extern char _asn1_identifierMissing[]; + + +/******************************************************/ +/* Function : _asn1_add_single_node */ +/* Description: creates a new NODE_ASN element. */ +/* Parameters: */ +/* type: type of the new element (see ASN1_ETYPE_ */ +/* and CONST_ constants). */ +/* Return: pointer to the new element. */ +/******************************************************/ +asn1_node +_asn1_add_single_node (unsigned int type) +{ + asn1_node punt; + + punt = calloc (1, sizeof (struct asn1_node_st)); + if (punt == NULL) + return NULL; + + punt->type = type; + + return punt; +} + + +/******************************************************************/ +/* Function : _asn1_find_left */ +/* Description: returns the NODE_ASN element with RIGHT field that*/ +/* points the element NODE. */ +/* Parameters: */ +/* node: NODE_ASN element pointer. */ +/* Return: NULL if not found. */ +/******************************************************************/ +asn1_node +_asn1_find_left (asn1_node_const node) +{ + if ((node == NULL) || (node->left == NULL) || (node->left->down == node)) + return NULL; + + return node->left; +} + +#if 0 +int +_asn1_create_static_structure (asn1_node_const pointer, char *output_file_name, + char *vector_name) +{ + FILE *file; + asn1_node_const p; + unsigned long t; + + file = fopen (output_file_name, "w"); + + if (file == NULL) + return ASN1_FILE_NOT_FOUND; + + fprintf (file, "#if HAVE_CONFIG_H\n"); + fprintf (file, "# include \"config.h\"\n"); + fprintf (file, "#endif\n\n"); + + fprintf (file, "#include \n\n"); + + fprintf (file, "const asn1_static_node %s[] = {\n", vector_name); + + p = pointer; + + while (p) + { + fprintf (file, " { "); + + if (p->name[0] != 0) + fprintf (file, "\"%s\", ", p->name); + else + fprintf (file, "NULL, "); + + t = p->type; + if (p->down) + t |= CONST_DOWN; + if (p->right) + t |= CONST_RIGHT; + + fprintf (file, "%lu, ", t); + + if (p->value) + fprintf (file, "\"%s\"},\n", p->value); + else + fprintf (file, "NULL },\n"); + + if (p->down) + { + p = p->down; + } + else if (p->right) + { + p = p->right; + } + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == pointer) + { + p = NULL; + break; + } + if (p->right) + { + p = p->right; + break; + } + } + } + } + + fprintf (file, " { NULL, 0, NULL }\n};\n"); + + fclose (file); + + return ASN1_SUCCESS; +} +#endif + +/** + * asn1_array2tree: + * @array: specify the array that contains ASN.1 declarations + * @definitions: return the pointer to the structure created by + * *ARRAY ASN.1 declarations + * @errorDescription: return the error description. + * + * Creates the structures needed to manage the ASN.1 definitions. + * @array is a vector created by asn1_parser2array(). + * + * Returns: %ASN1_SUCCESS if structure was created correctly, + * %ASN1_ELEMENT_NOT_EMPTY if *@definitions not NULL, + * %ASN1_IDENTIFIER_NOT_FOUND if in the file there is an identifier + * that is not defined (see @errorDescription for more information), + * %ASN1_ARRAY_ERROR if the array pointed by @array is wrong. + **/ +int +asn1_array2tree (const asn1_static_node * array, asn1_node * definitions, + char *errorDescription) +{ + asn1_node p, p_last = NULL; + unsigned long k; + int move; + int result; + unsigned int type; + list_type *e_list = NULL; + + if (errorDescription) + errorDescription[0] = 0; + + if (*definitions != NULL) + return ASN1_ELEMENT_NOT_EMPTY; + + move = UP; + + for (k = 0; array[k].value || array[k].type || array[k].name; k++) + { + type = convert_old_type (array[k].type); + + p = _asn1_add_static_node (&e_list, type & (~CONST_DOWN)); + if (array[k].name) + _asn1_set_name (p, array[k].name); + if (array[k].value) + _asn1_set_value (p, array[k].value, strlen (array[k].value) + 1); + + if (*definitions == NULL) + *definitions = p; + + if (move == DOWN) + { + if (p_last && p_last->down) + _asn1_delete_structure (e_list, &p_last->down, 0); + _asn1_set_down (p_last, p); + } + else if (move == RIGHT) + { + if (p_last && p_last->right) + _asn1_delete_structure (e_list, &p_last->right, 0); + _asn1_set_right (p_last, p); + } + + p_last = p; + + if (type & CONST_DOWN) + move = DOWN; + else if (type & CONST_RIGHT) + move = RIGHT; + else + { + while (p_last != *definitions) + { + p_last = _asn1_find_up (p_last); + + if (p_last == NULL) + break; + + if (p_last->type & CONST_RIGHT) + { + p_last->type &= ~CONST_RIGHT; + move = RIGHT; + break; + } + } /* while */ + } + } /* while */ + + if (p_last == *definitions) + { + result = _asn1_check_identifier (*definitions); + if (result == ASN1_SUCCESS) + { + _asn1_change_integer_value (*definitions); + result = _asn1_expand_object_id (&e_list, *definitions); + } + } + else + { + result = ASN1_ARRAY_ERROR; + } + + if (errorDescription != NULL) + { + if (result == ASN1_IDENTIFIER_NOT_FOUND) + { + Estrcpy (errorDescription, ":: identifier '"); + Estrcat (errorDescription, _asn1_identifierMissing); + Estrcat (errorDescription, "' not found"); + } + else + errorDescription[0] = 0; + } + + if (result != ASN1_SUCCESS) + { + _asn1_delete_list_and_nodes (e_list); + *definitions = NULL; + } + else + _asn1_delete_list (e_list); + + return result; +} + +/** + * asn1_delete_structure: + * @structure: pointer to the structure that you want to delete. + * + * Deletes the structure *@structure. At the end, *@structure is set + * to NULL. + * + * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if + * *@structure was NULL. + **/ +int +asn1_delete_structure (asn1_node * structure) +{ + return _asn1_delete_structure (NULL, structure, 0); +} + +/** + * asn1_delete_structure2: + * @structure: pointer to the structure that you want to delete. + * @flags: additional flags (see %ASN1_DELETE_FLAG) + * + * Deletes the structure *@structure. At the end, *@structure is set + * to NULL. + * + * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if + * *@structure was NULL. + **/ +int +asn1_delete_structure2 (asn1_node * structure, unsigned int flags) +{ + return _asn1_delete_structure (NULL, structure, flags); +} + +int +_asn1_delete_structure (list_type *e_list, asn1_node * structure, unsigned int flags) +{ + asn1_node p, p2, p3; + + if (*structure == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = *structure; + while (p) + { + if (p->down) + { + p = p->down; + } + else + { /* no down */ + p2 = p->right; + if (p != *structure) + { + p3 = _asn1_find_up (p); + _asn1_set_down (p3, p2); + if (e_list) + _asn1_delete_node_from_list (e_list, p); + _asn1_remove_node (p, flags); + p = p3; + } + else + { /* p==root */ + p3 = _asn1_find_left (p); + if (!p3) + { + p3 = _asn1_find_up (p); + if (p3) + _asn1_set_down (p3, p2); + else + { + if (p->right) + p->right->left = NULL; + } + } + else + _asn1_set_right (p3, p2); + if (e_list) + _asn1_delete_node_from_list (e_list, p); + _asn1_remove_node (p, flags); + p = NULL; + } + } + } + + *structure = NULL; + return ASN1_SUCCESS; +} + + +/** + * asn1_delete_element: + * @structure: pointer to the structure that contains the element you + * want to delete. + * @element_name: element's name you want to delete. + * + * Deletes the element named *@element_name inside *@structure. + * + * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if + * the @element_name was not found. + **/ +int +asn1_delete_element (asn1_node structure, const char *element_name) +{ + asn1_node p2, p3, source_node; + + source_node = asn1_find_node (structure, element_name); + + if (source_node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p2 = source_node->right; + p3 = _asn1_find_left (source_node); + if (!p3) + { + p3 = _asn1_find_up (source_node); + if (p3) + _asn1_set_down (p3, p2); + else if (source_node->right) + source_node->right->left = NULL; + } + else + _asn1_set_right (p3, p2); + + return asn1_delete_structure (&source_node); +} + +#ifndef __clang_analyzer__ +asn1_node +_asn1_copy_structure3 (asn1_node_const source_node) +{ + asn1_node_const p_s; + asn1_node dest_node, p_d, p_d_prev; + int move; + + if (source_node == NULL) + return NULL; + + dest_node = _asn1_add_single_node (source_node->type); + + p_s = source_node; + p_d = dest_node; + + move = DOWN; + + do + { + if (move != UP) + { + if (p_s->name[0] != 0) + _asn1_cpy_name (p_d, p_s); + if (p_s->value) + _asn1_set_value (p_d, p_s->value, p_s->value_len); + if (p_s->down) + { + p_s = p_s->down; + p_d_prev = p_d; + p_d = _asn1_add_single_node (p_s->type); + _asn1_set_down (p_d_prev, p_d); + continue; + } + p_d->start = p_s->start; + p_d->end = p_s->end; + } + + if (p_s == source_node) + break; + + if (p_s->right) + { + move = RIGHT; + p_s = p_s->right; + p_d_prev = p_d; + p_d = _asn1_add_single_node (p_s->type); + _asn1_set_right (p_d_prev, p_d); + } + else + { + move = UP; + p_s = _asn1_find_up (p_s); + p_d = _asn1_find_up (p_d); + } + } + while (p_s != source_node); + return dest_node; +} +#else + +/* Non-production code */ +asn1_node +_asn1_copy_structure3 (asn1_node_const source_node) +{ + return NULL; +} +#endif /* __clang_analyzer__ */ + + +static asn1_node +_asn1_copy_structure2 (asn1_node_const root, const char *source_name) +{ + asn1_node source_node; + + source_node = asn1_find_node (root, source_name); + + return _asn1_copy_structure3 (source_node); + +} + + +static int +_asn1_type_choice_config (asn1_node node) +{ + asn1_node p, p2, p3, p4; + int move, tlen; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node; + move = DOWN; + + while (!((p == node) && (move == UP))) + { + if (move != UP) + { + if ((type_field (p->type) == ASN1_ETYPE_CHOICE) + && (p->type & CONST_TAG)) + { + p2 = p->down; + while (p2) + { + if (type_field (p2->type) != ASN1_ETYPE_TAG) + { + p2->type |= CONST_TAG; + p3 = _asn1_find_left (p2); + while (p3) + { + if (type_field (p3->type) == ASN1_ETYPE_TAG) + { + p4 = _asn1_add_single_node (p3->type); + tlen = _asn1_strlen (p3->value); + if (tlen > 0) + _asn1_set_value (p4, p3->value, tlen + 1); + _asn1_set_right (p4, p2->down); + _asn1_set_down (p2, p4); + } + p3 = _asn1_find_left (p3); + } + } + p2 = p2->right; + } + p->type &= ~(CONST_TAG); + p2 = p->down; + while (p2) + { + p3 = p2->right; + if (type_field (p2->type) == ASN1_ETYPE_TAG) + asn1_delete_structure (&p2); + p2 = p3; + } + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +static int +_asn1_expand_identifier (asn1_node * node, asn1_node_const root) +{ + asn1_node p, p2, p3; + char name2[ASN1_MAX_NAME_SIZE + 2]; + int move; + + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = *node; + move = DOWN; + + while (!((p == *node) && (move == UP))) + { + if (move != UP) + { + if (type_field (p->type) == ASN1_ETYPE_IDENTIFIER) + { + snprintf (name2, sizeof (name2), "%s.%s", root->name, p->value); + p2 = _asn1_copy_structure2 (root, name2); + if (p2 == NULL) + { + return ASN1_IDENTIFIER_NOT_FOUND; + } + _asn1_cpy_name (p2, p); + p2->right = p->right; + p2->left = p->left; + if (p->right) + p->right->left = p2; + p3 = p->down; + if (p3) + { + while (p3->right) + p3 = p3->right; + _asn1_set_right (p3, p2->down); + _asn1_set_down (p2, p->down); + } + + p3 = _asn1_find_left (p); + if (p3) + _asn1_set_right (p3, p2); + else + { + p3 = _asn1_find_up (p); + if (p3) + _asn1_set_down (p3, p2); + else + { + p2->left = NULL; + } + } + + if (p->type & CONST_SIZE) + p2->type |= CONST_SIZE; + if (p->type & CONST_TAG) + p2->type |= CONST_TAG; + if (p->type & CONST_OPTION) + p2->type |= CONST_OPTION; + if (p->type & CONST_DEFAULT) + p2->type |= CONST_DEFAULT; + if (p->type & CONST_SET) + p2->type |= CONST_SET; + if (p->type & CONST_NOT_USED) + p2->type |= CONST_NOT_USED; + + if (p == *node) + *node = p2; + _asn1_remove_node (p, 0); + p = p2; + move = DOWN; + continue; + } + move = DOWN; + } + else + move = RIGHT; + + if (move == DOWN) + { + if (p->down) + p = p->down; + else + move = RIGHT; + } + + if (p == *node) + { + move = UP; + continue; + } + + if (move == RIGHT) + { + if (p->right) + p = p->right; + else + move = UP; + } + if (move == UP) + p = _asn1_find_up (p); + } + + return ASN1_SUCCESS; +} + + +/** + * asn1_create_element: + * @definitions: pointer to the structure returned by "parser_asn1" function + * @source_name: the name of the type of the new structure (must be + * inside p_structure). + * @element: pointer to the structure created. + * + * Creates a structure of type @source_name. Example using + * "pkix.asn": + * + * rc = asn1_create_element(cert_def, "PKIX1.Certificate", certptr); + * + * Returns: %ASN1_SUCCESS if creation OK, %ASN1_ELEMENT_NOT_FOUND if + * @source_name is not known. + **/ +int +asn1_create_element (asn1_node_const definitions, const char *source_name, + asn1_node * element) +{ + asn1_node dest_node; + int res; + + dest_node = _asn1_copy_structure2 (definitions, source_name); + + if (dest_node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + _asn1_set_name (dest_node, ""); + + res = _asn1_expand_identifier (&dest_node, definitions); + _asn1_type_choice_config (dest_node); + + *element = dest_node; + + return res; +} + +#if 0 +/** + * asn1_print_structure: + * @out: pointer to the output file (e.g. stdout). + * @structure: pointer to the structure that you want to visit. + * @name: an element of the structure + * @mode: specify how much of the structure to print, can be + * %ASN1_PRINT_NAME, %ASN1_PRINT_NAME_TYPE, + * %ASN1_PRINT_NAME_TYPE_VALUE, or %ASN1_PRINT_ALL. + * + * Prints on the @out file descriptor the structure's tree starting + * from the @name element inside the structure @structure. + **/ +void +asn1_print_structure (FILE * out, asn1_node_const structure, const char *name, + int mode) +{ + asn1_node_const p, root; + int k, indent = 0, len, len2, len3; + + if (out == NULL) + return; + + root = asn1_find_node (structure, name); + + if (root == NULL) + return; + + p = root; + while (p) + { + if (mode == ASN1_PRINT_ALL) + { + for (k = 0; k < indent; k++) + fprintf (out, " "); + fprintf (out, "name:"); + if (p->name[0] != 0) + fprintf (out, "%s ", p->name); + else + fprintf (out, "NULL "); + } + else + { + switch (type_field (p->type)) + { + case ASN1_ETYPE_CONSTANT: + case ASN1_ETYPE_TAG: + case ASN1_ETYPE_SIZE: + break; + default: + for (k = 0; k < indent; k++) + fprintf (out, " "); + fprintf (out, "name:"); + if (p->name[0] != 0) + fprintf (out, "%s ", p->name); + else + fprintf (out, "NULL "); + } + } + + if (mode != ASN1_PRINT_NAME) + { + unsigned type = type_field (p->type); + switch (type) + { + case ASN1_ETYPE_CONSTANT: + if (mode == ASN1_PRINT_ALL) + fprintf (out, "type:CONST"); + break; + case ASN1_ETYPE_TAG: + if (mode == ASN1_PRINT_ALL) + fprintf (out, "type:TAG"); + break; + case ASN1_ETYPE_SIZE: + if (mode == ASN1_PRINT_ALL) + fprintf (out, "type:SIZE"); + break; + case ASN1_ETYPE_DEFAULT: + fprintf (out, "type:DEFAULT"); + break; + case ASN1_ETYPE_IDENTIFIER: + fprintf (out, "type:IDENTIFIER"); + break; + case ASN1_ETYPE_ANY: + fprintf (out, "type:ANY"); + break; + case ASN1_ETYPE_CHOICE: + fprintf (out, "type:CHOICE"); + break; + case ASN1_ETYPE_DEFINITIONS: + fprintf (out, "type:DEFINITIONS"); + break; + CASE_HANDLED_ETYPES: + fprintf (out, "%s", _asn1_tags[type].desc); + break; + default: + break; + } + } + + if ((mode == ASN1_PRINT_NAME_TYPE_VALUE) || (mode == ASN1_PRINT_ALL)) + { + switch (type_field (p->type)) + { + case ASN1_ETYPE_CONSTANT: + if (mode == ASN1_PRINT_ALL) + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case ASN1_ETYPE_TAG: + if (mode == ASN1_PRINT_ALL) + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case ASN1_ETYPE_SIZE: + if (mode == ASN1_PRINT_ALL) + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case ASN1_ETYPE_DEFAULT: + if (p->value) + fprintf (out, " value:%s", p->value); + else if (p->type & CONST_TRUE) + fprintf (out, " value:TRUE"); + else if (p->type & CONST_FALSE) + fprintf (out, " value:FALSE"); + break; + case ASN1_ETYPE_IDENTIFIER: + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case ASN1_ETYPE_INTEGER: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:0x"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%02x", (unsigned) (p->value)[k + len2]); + } + break; + case ASN1_ETYPE_ENUMERATED: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:0x"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%02x", (unsigned) (p->value)[k + len2]); + } + break; + case ASN1_ETYPE_BOOLEAN: + if (p->value) + { + if (p->value[0] == 'T') + fprintf (out, " value:TRUE"); + else if (p->value[0] == 'F') + fprintf (out, " value:FALSE"); + } + break; + case ASN1_ETYPE_BIT_STRING: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + if (len > 0) + { + fprintf (out, " value(%i):", + (len - 1) * 8 - (p->value[len2])); + for (k = 1; k < len; k++) + fprintf (out, "%02x", (unsigned) (p->value)[k + len2]); + } + } + break; + case ASN1_ETYPE_GENERALIZED_TIME: + case ASN1_ETYPE_UTC_TIME: + if (p->value) + { + fprintf (out, " value:"); + for (k = 0; k < p->value_len; k++) + fprintf (out, "%c", (p->value)[k]); + } + break; + case ASN1_ETYPE_GENERALSTRING: + case ASN1_ETYPE_NUMERIC_STRING: + case ASN1_ETYPE_IA5_STRING: + case ASN1_ETYPE_TELETEX_STRING: + case ASN1_ETYPE_PRINTABLE_STRING: + case ASN1_ETYPE_UNIVERSAL_STRING: + case ASN1_ETYPE_UTF8_STRING: + case ASN1_ETYPE_VISIBLE_STRING: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%c", (p->value)[k + len2]); + } + break; + case ASN1_ETYPE_BMP_STRING: + case ASN1_ETYPE_OCTET_STRING: + if (p->value) + { + len2 = -1; + len = asn1_get_length_der (p->value, p->value_len, &len2); + fprintf (out, " value:"); + if (len > 0) + for (k = 0; k < len; k++) + fprintf (out, "%02x", (unsigned) (p->value)[k + len2]); + } + break; + case ASN1_ETYPE_OBJECT_ID: + if (p->value) + fprintf (out, " value:%s", p->value); + break; + case ASN1_ETYPE_ANY: + if (p->value) + { + len3 = -1; + len2 = asn1_get_length_der (p->value, p->value_len, &len3); + fprintf (out, " value:"); + if (len2 > 0) + for (k = 0; k < len2; k++) + fprintf (out, "%02x", (unsigned) (p->value)[k + len3]); + } + break; + case ASN1_ETYPE_SET: + case ASN1_ETYPE_SET_OF: + case ASN1_ETYPE_CHOICE: + case ASN1_ETYPE_DEFINITIONS: + case ASN1_ETYPE_SEQUENCE_OF: + case ASN1_ETYPE_SEQUENCE: + case ASN1_ETYPE_NULL: + break; + default: + break; + } + } + + if (mode == ASN1_PRINT_ALL) + { + if (p->type & 0x1FFFFF00) + { + fprintf (out, " attr:"); + if (p->type & CONST_UNIVERSAL) + fprintf (out, "UNIVERSAL,"); + if (p->type & CONST_PRIVATE) + fprintf (out, "PRIVATE,"); + if (p->type & CONST_APPLICATION) + fprintf (out, "APPLICATION,"); + if (p->type & CONST_EXPLICIT) + fprintf (out, "EXPLICIT,"); + if (p->type & CONST_IMPLICIT) + fprintf (out, "IMPLICIT,"); + if (p->type & CONST_TAG) + fprintf (out, "TAG,"); + if (p->type & CONST_DEFAULT) + fprintf (out, "DEFAULT,"); + if (p->type & CONST_TRUE) + fprintf (out, "TRUE,"); + if (p->type & CONST_FALSE) + fprintf (out, "FALSE,"); + if (p->type & CONST_LIST) + fprintf (out, "LIST,"); + if (p->type & CONST_MIN_MAX) + fprintf (out, "MIN_MAX,"); + if (p->type & CONST_OPTION) + fprintf (out, "OPTION,"); + if (p->type & CONST_1_PARAM) + fprintf (out, "1_PARAM,"); + if (p->type & CONST_SIZE) + fprintf (out, "SIZE,"); + if (p->type & CONST_DEFINED_BY) + fprintf (out, "DEF_BY,"); + if (p->type & CONST_GENERALIZED) + fprintf (out, "GENERALIZED,"); + if (p->type & CONST_UTC) + fprintf (out, "UTC,"); + if (p->type & CONST_SET) + fprintf (out, "SET,"); + if (p->type & CONST_NOT_USED) + fprintf (out, "NOT_USED,"); + if (p->type & CONST_ASSIGN) + fprintf (out, "ASSIGNMENT,"); + } + } + + if (mode == ASN1_PRINT_ALL) + { + fprintf (out, "\n"); + } + else + { + switch (type_field (p->type)) + { + case ASN1_ETYPE_CONSTANT: + case ASN1_ETYPE_TAG: + case ASN1_ETYPE_SIZE: + break; + default: + fprintf (out, "\n"); + } + } + + if (p->down) + { + p = p->down; + indent += 2; + } + else if (p == root) + { + p = NULL; + break; + } + else if (p->right) + p = p->right; + else + { + while (1) + { + p = _asn1_find_up (p); + if (p == root) + { + p = NULL; + break; + } + indent -= 2; + if (p->right) + { + p = p->right; + break; + } + } + } + } +} +#endif + + +/** + * asn1_number_of_elements: + * @element: pointer to the root of an ASN1 structure. + * @name: the name of a sub-structure of ROOT. + * @num: pointer to an integer where the result will be stored + * + * Counts the number of elements of a sub-structure called NAME with + * names equal to "?1","?2", ... + * + * Returns: %ASN1_SUCCESS if successful, %ASN1_ELEMENT_NOT_FOUND if + * @name is not known, %ASN1_GENERIC_ERROR if pointer @num is %NULL. + **/ +int +asn1_number_of_elements (asn1_node_const element, const char *name, int *num) +{ + asn1_node_const node, p; + + if (num == NULL) + return ASN1_GENERIC_ERROR; + + *num = 0; + + node = asn1_find_node (element, name); + if (node == NULL) + return ASN1_ELEMENT_NOT_FOUND; + + p = node->down; + + while (p) + { + if (p->name[0] == '?') + (*num)++; + p = p->right; + } + + return ASN1_SUCCESS; +} + + +/** + * asn1_find_structure_from_oid: + * @definitions: ASN1 definitions + * @oidValue: value of the OID to search (e.g. "1.2.3.4"). + * + * Search the structure that is defined just after an OID definition. + * + * Returns: %NULL when @oidValue not found, otherwise the pointer to a + * constant string that contains the element name defined just after + * the OID. + **/ +const char * +asn1_find_structure_from_oid (asn1_node_const definitions, const char *oidValue) +{ + char name[2 * ASN1_MAX_NAME_SIZE + 2]; + char value[ASN1_MAX_NAME_SIZE]; + asn1_node p; + int len; + int result; + const char *definitionsName; + + if ((definitions == NULL) || (oidValue == NULL)) + return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ + + definitionsName = definitions->name; + + /* search the OBJECT_ID into definitions */ + p = definitions->down; + while (p) + { + if ((type_field (p->type) == ASN1_ETYPE_OBJECT_ID) && + (p->type & CONST_ASSIGN)) + { + snprintf(name, sizeof(name), "%s.%s", definitionsName, p->name); + + len = ASN1_MAX_NAME_SIZE; + result = asn1_read_value (definitions, name, value, &len); + + if ((result == ASN1_SUCCESS) && (!strcmp (oidValue, value))) + { + p = p->right; + if (p == NULL) /* reach the end of ASN1 definitions */ + return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ + + return p->name; + } + } + p = p->right; + } + + return NULL; /* ASN1_ELEMENT_NOT_FOUND; */ +} + +#if 0 +/** + * asn1_copy_node: + * @dst: Destination asn1 node. + * @dst_name: Field name in destination node. + * @src: Source asn1 node. + * @src_name: Field name in source node. + * + * Create a deep copy of a asn1_node variable. That + * function requires @dst to be expanded using asn1_create_element(). + * + * Returns: Return %ASN1_SUCCESS on success. + **/ +int +asn1_copy_node (asn1_node dst, const char *dst_name, + asn1_node_const src, const char *src_name) +{ + int result; + asn1_node dst_node; + void *data = NULL; + int size = 0; + + result = asn1_der_coding (src, src_name, NULL, &size, NULL); + if (result != ASN1_MEM_ERROR) + return result; + + data = malloc (size); + if (data == NULL) + return ASN1_MEM_ERROR; + + result = asn1_der_coding (src, src_name, data, &size, NULL); + if (result != ASN1_SUCCESS) + { + free (data); + return result; + } + + dst_node = asn1_find_node (dst, dst_name); + if (dst_node == NULL) + { + free (data); + return ASN1_ELEMENT_NOT_FOUND; + } + + result = asn1_der_decoding (&dst_node, data, size, NULL); + + free (data); + + return result; +} +#endif + +/** + * asn1_dup_node: + * @src: Source asn1 node. + * @src_name: Field name in source node. + * + * Create a deep copy of a asn1_node variable. This function + * will return an exact copy of the provided structure. + * + * Returns: Return %NULL on failure. + **/ +asn1_node +asn1_dup_node (asn1_node_const src, const char *src_name) +{ + return _asn1_copy_structure2(src, src_name); +} diff --git a/grub-core/lib/libtasn1/lib/structure.h b/grub-core/lib/libtasn1/lib/structure.h new file mode 100644 index 0000000000..99e685da07 --- /dev/null +++ b/grub-core/lib/libtasn1/lib/structure.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * The LIBTASN1 library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + */ + +/*************************************************/ +/* File: structure.h */ +/* Description: list of exported object by */ +/* "structure.c" */ +/*************************************************/ + +#ifndef _STRUCTURE_H +#define _STRUCTURE_H + +#include "parser_aux.h" // list_type + +int _asn1_create_static_structure (asn1_node_const pointer, + char *output_file_name, char *vector_name); + +asn1_node _asn1_copy_structure3 (asn1_node_const source_node); + +asn1_node _asn1_add_single_node (unsigned int type); + +asn1_node _asn1_find_left (asn1_node_const node); + +int +_asn1_delete_structure (list_type *e_list, asn1_node *structure, unsigned int flags); + +#endif diff --git a/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h new file mode 100644 index 0000000000..1e7d3d64f5 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-1_asn1_tab.h @@ -0,0 +1,32 @@ +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +const asn1_static_node CVE_2018_1000654_1_asn1_tab[] = { + { "TEST_TREE", 536875024, NULL }, + { NULL, 1610612748, NULL }, + { "iso", 1073741825, "1"}, + { "identified-organization", 1073741825, "3"}, + { "dod", 1073741825, "6"}, + { "internet", 1073741825, "1"}, + { "security", 1073741825, "5"}, + { "mechanisms", 1073741825, "5"}, + { "pkix", 1073741825, "7"}, + { "id-mod", 1073741825, "0"}, + { "id-pkix1-implicit-88", 1, "2"}, + { "id-xnyTest", 1879048204, NULL }, + { NULL, 1073741825, "id-ix"}, + { NULL, 1073741825, "29"}, + { NULL, 1, "1"}, + { "id-ix", 1880096780, "OBJECR"}, + { NULL, 1073741825, "id-ix"}, + { NULL, 1073741825, "29"}, + { NULL, 1, "2"}, + { "id-xnyTest", 805306380, NULL }, + { NULL, 1073741825, "id-ix"}, + { NULL, 1073741825, "29"}, + { NULL, 1, "1"}, + { NULL, 0, NULL } +}; diff --git a/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h new file mode 100644 index 0000000000..e2561e5ec6 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654-2_asn1_tab.h @@ -0,0 +1,36 @@ +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +const asn1_static_node CVE_2018_1000654_2_asn1_tab[] = { + { "TEST_TREE", 536875024, NULL }, + { NULL, 1610612748, NULL }, + { "iso", 1073741825, "1"}, + { "identified-organization", 1073741825, "3"}, + { "dod", 1073741825, "6"}, + { "internet", 1073741825, "1"}, + { "security", 1073741825, "5"}, + { "mechanisms", 1073741825, "5"}, + { "pkix", 1073741825, "7"}, + { "id-mod", 1073741825, "0"}, + { "id-pkix1-implicit-88", 1, "2"}, + { "id-oneTest", 1879048204, NULL }, + { NULL, 1073741825, "id-two"}, + { NULL, 1073741825, "9"}, + { NULL, 1, "1"}, + { "id-two", 1879048204, NULL }, + { NULL, 1073741825, "id-three"}, + { NULL, 1073741825, "2"}, + { NULL, 1, "2"}, + { "id-three", 1879048204, NULL }, + { NULL, 1073741825, "id-four"}, + { NULL, 1073741825, "3"}, + { NULL, 1, "3"}, + { "id-four", 805306380, NULL }, + { NULL, 1073741825, "id-two"}, + { NULL, 1073741825, "3"}, + { NULL, 1, "3"}, + { NULL, 0, NULL } +}; diff --git a/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c new file mode 100644 index 0000000000..534e304521 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/CVE-2018-1000654.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2002-2018 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * This program 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. + * + * This program 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 this program. If not, see . + * + */ + +/****************************************************************/ +/* Description: reproducer for CVE-2018-1000654 */ +/****************************************************************/ + +#include +#include +#include +#include +#include +#include "../wrap_tests.h" + +#include "CVE-2018-1000654-1_asn1_tab.h" +#include "CVE-2018-1000654-2_asn1_tab.h" + +void +test_CVE_2018_1000654 (void) +{ + int result; + asn1_node definitions = NULL; + char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + + result = asn1_array2tree (CVE_2018_1000654_1_asn1_tab, &definitions, errorDescription); + if (result != ASN1_RECURSION) + { + grub_fatal ("Error: %s\nErrorDescription = %s\n\n", + asn1_strerror (result), errorDescription); + return; + } + + asn1_delete_structure (&definitions); + + result = asn1_array2tree (CVE_2018_1000654_2_asn1_tab, &definitions, errorDescription); + if (result != ASN1_RECURSION) + { + grub_fatal ("Error: %s\nErrorDescription = %s\n\n", + asn1_strerror (result), errorDescription); + return; + } + + asn1_delete_structure (&definitions); +} diff --git a/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c b/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c new file mode 100644 index 0000000000..f48aea0ef8 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/Test_overflow.c @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2012-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * This program 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. + * + * This program 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 this program. If not, see . + * + */ + +/* Written by Simon Josefsson */ + +#include +#include +#include +#include +#include +#include "../wrap_tests.h" + +void +test_overflow(void) +{ + /* Test that values larger than long are rejected. This has worked + fine with all versions of libtasn1. */ + + { + unsigned char der[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"; + long l; + int len; + + l = asn1_get_length_der (der, sizeof der, &len); + + if (l != -2L) + { + grub_fatal ("ERROR: asn1_get_length_der bignum (l %ld len %d)\n", l, len); + return; + } + } + + /* Test that values larger than int but smaller than long are + rejected. This limitation was introduced with libtasn1 2.12. */ +#if (GRUB_LONG_MAX > GRUB_INT_MAX) + { + unsigned long num = ((long) GRUB_UINT_MAX) << 2; + unsigned char der[20]; + int der_len; + long l; + int len; + + asn1_length_der (num, der, &der_len); + + l = asn1_get_length_der (der, der_len, &len); + + if (l != -2L) + { + grub_fatal ("ERROR: asn1_get_length_der intnum (l %ld len %d)\n", l, + len); + return; + } + } +#endif + + /* Test that values larger than would fit in the input string are + rejected. This problem was fixed in libtasn1 2.12. */ + { + unsigned long num = 64; + unsigned char der[20]; + int der_len; + long l; + int len; + + asn1_length_der (num, der, &der_len); + + der_len = sizeof (der); + l = asn1_get_length_der (der, der_len, &len); + + if (l != -4L) + { + grub_fatal ("ERROR: asn1_get_length_der overflow-small (l %ld len %d)\n", + l, len); + return; + } + } + + /* Test that values larger than would fit in the input string are + rejected. This problem was fixed in libtasn1 2.12. */ + { + unsigned long num = 1073741824; + unsigned char der[20]; + int der_len; + long l; + int len; + + asn1_length_der (num, der, &der_len); + + der_len = sizeof (der); + l = asn1_get_length_der (der, der_len, &len); + + if (l != -4L) + { + grub_fatal ("ERROR: asn1_get_length_der overflow-large1 (l %ld len %d)\n", + l, len); + return; + } + } + + /* Test that values larger than would fit in the input string are + rejected. This problem was fixed in libtasn1 2.12. */ + { + unsigned long num = 2147483649; + unsigned char der[20]; + int der_len; + long l; + int len; + + asn1_length_der (num, der, &der_len); + + der_len = sizeof (der); + l = asn1_get_length_der (der, der_len, &len); + + if (l != -2L) + { + grub_fatal ("ERROR: asn1_get_length_der overflow-large2 (l %ld len %d)\n", + l, len); + return; + } + } +} diff --git a/grub-core/lib/libtasn1_wrap/tests/Test_simple.c b/grub-core/lib/libtasn1_wrap/tests/Test_simple.c new file mode 100644 index 0000000000..9f01006ddf --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/Test_simple.c @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2011-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * Written by Simon Josefsson + * + */ + +#include +#include +#include +#include +#include "../wrap_tests.h" + +struct tv +{ + int bitlen; + const char *bitstr; + int derlen; + const char *der; +}; + +static const struct tv tv[] = { + {0, "", 2, "\x01\x00"}, + {1, "\x00", 3, "\x02\x07\x00"}, + {2, "\x00", 3, "\x02\x06\x00"}, + {3, "\x00", 3, "\x02\x05\x00"}, + {4, "\x00", 3, "\x02\x04\x00"}, + {5, "\x00", 3, "\x02\x03\x00"}, + {6, "\x00", 3, "\x02\x02\x00"}, + {7, "\x00", 3, "\x02\x01\x00"}, + {8, "\x00\x00", 3, "\x02\x00\x00"}, + {9, "\x00\x00", 4, "\x03\x07\x00\x00"}, + {10, "\x00\x00", 4, "\x03\x06\x00\x00"}, + {11, "\x00\x00", 4, "\x03\x05\x00\x00"}, + {12, "\x00\x00", 4, "\x03\x04\x00\x00"}, + {13, "\x00\x00", 4, "\x03\x03\x00\x00"}, + {14, "\x00\x00", 4, "\x03\x02\x00\x00"}, + {15, "\x00\x00", 4, "\x03\x01\x00\x00"}, + {16, "\x00\x00", 4, "\x03\x00\x00\x00"}, + {17, "\x00\x00\x00", 5, "\x04\x07\x00\x00\x00"}, + {18, "\x00\x00\x00", 5, "\x04\x06\x00\x00\x00"}, + {19, "\x00\x00\x00", 5, "\x04\x05\x00\x00\x00"}, + {1, "\xFF", 3, "\x02\x07\x80"}, + {2, "\xFF", 3, "\x02\x06\xc0"}, + {3, "\xFF", 3, "\x02\x05\xe0"}, + {4, "\xFF", 3, "\x02\x04\xf0"}, + {5, "\xFF", 3, "\x02\x03\xf8"}, + {6, "\xFF", 3, "\x02\x02\xfc"}, + {7, "\xFF", 3, "\x02\x01\xfe"}, + {8, "\xFF\xFF", 3, "\x02\x00\xff"}, + {9, "\xFF\xFF", 4, "\x03\x07\xff\x80"}, + {10, "\xFF\xFF", 4, "\x03\x06\xff\xc0"}, + {11, "\xFF\xFF", 4, "\x03\x05\xff\xe0"}, + {12, "\xFF\xFF", 4, "\x03\x04\xff\xf0"}, + {13, "\xFF\xFF", 4, "\x03\x03\xff\xf8"}, + {14, "\xFF\xFF", 4, "\x03\x02\xff\xfc"}, + {15, "\xFF\xFF", 4, "\x03\x01\xff\xfe"}, + {16, "\xFF\xFF", 4, "\x03\x00\xff\xff"}, + {17, "\xFF\xFF\xFF", 5, "\x04\x07\xff\xff\x80"}, + {18, "\xFF\xFF\xFF", 5, "\x04\x06\xff\xff\xc0"}, + {19, "\xFF\xFF\xFF", 5, "\x04\x05\xff\xff\xe0"}, +}; + +void +test_simple (void) +{ + int result; + unsigned char der[100]; + unsigned char str[100]; + int der_len = sizeof (der); + int str_size = sizeof (str); + int ret_len, bit_len; + grub_size_t i; + + /* Dummy test */ + + asn1_bit_der (NULL, 0, der, &der_len); + result = asn1_get_bit_der (der, 0, &ret_len, str, str_size, &bit_len); + if (result != ASN1_GENERIC_ERROR) + { + grub_fatal ("asn1_get_bit_der zero\n"); + return; + } + + /* Encode short strings with increasing bit lengths */ + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { + /* Encode */ + + asn1_bit_der ((const unsigned char *) tv[i].bitstr, tv[i].bitlen, + der, &der_len); + +#if 0 + { + size_t j; + for (j = 0; j < der_len; j++) + printf ("\\x%02x", der[j]); + printf ("\n"); + } +#endif + + if (der_len != tv[i].derlen || grub_memcmp (der, tv[i].der, der_len) != 0) + { + grub_fatal ("asn1_bit_der iter %lu\n", (unsigned long) i); + return; + } + + /* Decode it */ + + result = asn1_get_bit_der (der, der_len, &ret_len, str, + str_size, &bit_len); + if (result != ASN1_SUCCESS || ret_len != tv[i].derlen + || bit_len != tv[i].bitlen) + { + grub_fatal ("asn1_get_bit_der iter %lu, err: %d\n", (unsigned long) i, result); + return; + } + } + + + /* Decode sample from "A Layman's Guide to a Subset of ASN.1, BER, + and DER" section 5.4 "BIT STRING": "The BER encoding of the BIT + STRING value "011011100101110111" can be any of the following, + among others, depending on the choice of padding bits, the form + of length octets [...]". + */ + + /* 03 04 06 6e 5d c0 DER encoding */ + + grub_memcpy (der, "\x04\x06\x6e\x5d\xc0", 5); + der_len = 5; + + result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len); + if (result != ASN1_SUCCESS || ret_len != 5 + || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xc0", 3) != 0) + { + grub_fatal ("asn1_get_bit_der example\n"); + return; + } + + der_len = sizeof (der); + asn1_bit_der (str, bit_len, der, &der_len); + if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) + { + grub_fatal ("asn1_bit_der example roundtrip\n"); + return; + } + + /* 03 04 06 6e 5d e0 padded with "100000" */ + + grub_memcpy (der, "\x04\x06\x6e\x5d\xe0", 5); + der_len = 5; + + result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len); + if (result != ASN1_SUCCESS || ret_len != 5 + || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xe0", 3) != 0) + { + grub_fatal ("asn1_get_bit_der example padded\n"); + return; + } + + der_len = sizeof (der); + asn1_bit_der (str, bit_len, der, &der_len); + if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) + { + grub_fatal ("asn1_bit_der example roundtrip\n"); + return; + } + + /* 03 81 04 06 6e 5d c0 long form of length octets */ + + grub_memcpy (der, "\x81\x04\x06\x6e\x5d\xc0", 6); + der_len = 6; + + result = asn1_get_bit_der (der, der_len, &ret_len, str, str_size, &bit_len); + + if (result != ASN1_SUCCESS || ret_len != 6 + || bit_len != 18 || grub_memcmp (str, "\x6e\x5d\xc0", 3) != 0) + { + grub_fatal ("asn1_get_bit_der example long form\n"); + return; + } + + der_len = sizeof (der); + asn1_bit_der (str, bit_len, der, &der_len); + if (der_len != 5 || grub_memcmp (der, "\x04\x06\x6e\x5d\xc0", 5) != 0) + { + grub_fatal ("asn1_bit_der example roundtrip\n"); + return; + } +} diff --git a/grub-core/lib/libtasn1_wrap/tests/Test_strings.c b/grub-core/lib/libtasn1_wrap/tests/Test_strings.c new file mode 100644 index 0000000000..dbe1474b20 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/Test_strings.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2012-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * Written by Simon Josefsson + * + */ + +#include +#include +#include +#include +#include "../wrap_tests.h" + +struct tv +{ + unsigned int etype; + unsigned int str_len; + const void *str; + unsigned int der_len; + const void *der; +}; + +static const struct tv tv[] = { + {ASN1_ETYPE_IA5_STRING, 20, + "\x63\x73\x63\x61\x40\x70\x61\x73\x73\x70\x6f\x72\x74\x2e\x67\x6f\x76\x2e\x67\x72", + 22, + "\x16\x14\x63\x73\x63\x61\x40\x70\x61\x73\x73\x70\x6f\x72\x74\x2e\x67\x6f\x76\x2e\x67\x72"}, + {ASN1_ETYPE_PRINTABLE_STRING, 5, "\x4e\x69\x6b\x6f\x73", + 7, "\x13\x05\x4e\x69\x6b\x6f\x73"}, + {ASN1_ETYPE_UTF8_STRING, 12, "Αττική", + 14, "\x0c\x0c\xce\x91\xcf\x84\xcf\x84\xce\xb9\xce\xba\xce\xae"}, + {ASN1_ETYPE_TELETEX_STRING, 15, + "\x53\x69\x6d\x6f\x6e\x20\x4a\x6f\x73\x65\x66\x73\x73\x6f\x6e", + 17, + "\x14\x0f\x53\x69\x6d\x6f\x6e\x20\x4a\x6f\x73\x65\x66\x73\x73\x6f\x6e"}, + {ASN1_ETYPE_OCTET_STRING, 36, + "\x30\x22\x80\x0F\x32\x30\x31\x31\x30\x38\x32\x31\x30\x38\x30\x30\x30\x36\x5A\x81\x0F\x32\x30\x31\x31\x30\x38\x32\x33\x32\x30\x35\x39\x35\x39\x5A", + 38, + "\x04\x24\x30\x22\x80\x0F\x32\x30\x31\x31\x30\x38\x32\x31\x30\x38\x30\x30\x30\x36\x5A\x81\x0F\x32\x30\x31\x31\x30\x38\x32\x33\x32\x30\x35\x39\x35\x39\x5A"} +}; + +#define SSTR(x) sizeof(x)-1,x +static const struct tv ber[] = { + {ASN1_ETYPE_OCTET_STRING, + SSTR("\xa0\xa0"), + SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x00\x00")}, + {ASN1_ETYPE_OCTET_STRING, + SSTR("\xa0\xa0\xb0\xb0\xb0"), + SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x00\x00")}, + {ASN1_ETYPE_OCTET_STRING, + SSTR("\xa0\xa0\xb0\xb0\xb0\xa1\xa1"), + SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x24\x80\x04\x82\x00\x02\xa1\xa1\x00\x00\x00\x00")}, + {ASN1_ETYPE_OCTET_STRING, + SSTR("\xa0\xa0\xb0\xb0\xb0\xa1\xa1\xc1"), + SSTR("\x24\x80\x04\x82\x00\x02\xa0\xa0\x04\x82\x00\x03\xb0\xb0\xb0\x24\x80\x04\x82\x00\x02\xa1\xa1\x04\x82\x00\x01\xc1\x00\x00\x00\x00")}, +}; + +void +test_strings () +{ + int ret; + unsigned char tl[ASN1_MAX_TL_SIZE]; + unsigned int tl_len, der_len, str_len; + const unsigned char *str; + unsigned char *b; + unsigned int i; + + /* Dummy test */ + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { + /* Encode */ + tl_len = sizeof (tl); + ret = asn1_encode_simple_der (tv[i].etype, tv[i].str, tv[i].str_len, + tl, &tl_len); + if (ret != ASN1_SUCCESS) + { + grub_fatal ("Encoding error in %u: %s\n", i, + asn1_strerror (ret)); + return; + } + der_len = tl_len + tv[i].str_len; + + if (der_len != tv[i].der_len || grub_memcmp (tl, tv[i].der, tl_len) != 0) + { + grub_fatal ( + "DER encoding differs in %u! (size: %u, expected: %u)\n", + i, der_len, tv[i].der_len); + return; + } + + /* decoding */ + ret = + asn1_decode_simple_der (tv[i].etype, tv[i].der, tv[i].der_len, &str, + &str_len); + if (ret != ASN1_SUCCESS) + { + grub_fatal ("Decoding error in %u: %s\n", i, + asn1_strerror (ret)); + return; + } + + if (str_len != tv[i].str_len || grub_memcmp (str, tv[i].str, str_len) != 0) + { + grub_fatal ( + "DER decoded data differ in %u! (size: %u, expected: %u)\n", + i, der_len, tv[i].str_len); + return; + } + } + + /* BER decoding */ + for (i = 0; i < sizeof (ber) / sizeof (ber[0]); i++) + { + /* decoding */ + ret = + asn1_decode_simple_ber (ber[i].etype, ber[i].der, ber[i].der_len, &b, + &str_len, NULL); + if (ret != ASN1_SUCCESS) + { + grub_fatal ("BER decoding error in %u: %s\n", i, + asn1_strerror (ret)); + return; + } + + if (str_len != ber[i].str_len || grub_memcmp (b, ber[i].str, str_len) != 0) + { + grub_fatal ( + "BER decoded data differ in %u! (size: %u, expected: %u)\n", + i, str_len, ber[i].str_len); + return; + } + grub_free(b); + } +} diff --git a/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c b/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c new file mode 100644 index 0000000000..d367bbfb5a --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/object-id-decoding.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2016 Red Hat, Inc. + * + * This file is part of LIBTASN1. + * + * This program 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. + * + * This program 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 this program. If not, see . + * + */ + +#include +#include +#include +#include +#include "../wrap_tests.h" + +struct tv +{ + int der_len; + const unsigned char *der; + const char *oid; + int expected_error; +}; + +static const struct tv tv[] = { + {.der_len = 5, + .der = (void *) "\x06\x03\x80\x37\x03", + .oid = "2.999.3", + .expected_error = ASN1_DER_ERROR /* leading 0x80 */ + }, + {.der_len = 12, + .der = (void *) "\x06\x0a\x2b\x06\x01\x80\x01\x92\x08\x09\x05\x01", + .oid = "1.3.6.1.4.1.2312.9.5.1", + .expected_error = ASN1_DER_ERROR /* leading 0x80 */ + }, + {.der_len = 6, + .der = (void *) "\x06\x04\x01\x02\x03\x04", + .oid = "0.1.2.3.4", + .expected_error = ASN1_SUCCESS}, + {.der_len = 5, + .der = (void *) "\x06\x03\x51\x02\x03", + .oid = "2.1.2.3", + .expected_error = ASN1_SUCCESS}, + {.der_len = 5, + .der = (void *) "\x06\x03\x88\x37\x03", + .oid = "2.999.3", + .expected_error = ASN1_SUCCESS}, + {.der_len = 12, + .der = (void *) "\x06\x0a\x2b\x06\x01\x04\x01\x92\x08\x09\x05\x01", + .oid = "1.3.6.1.4.1.2312.9.5.1", + .expected_error = ASN1_SUCCESS}, + {.der_len = 19, + .der = (void *) "\x06\x11\xfa\x80\x00\x00\x00\x0e\x01\x0e\xfa\x80\x00\x00\x00\x0e\x63\x6f\x6d", + .oid = "2.1998768.0.0.14.1.14.1998848.0.0.14.99.111.109", + .expected_error = ASN1_SUCCESS}, + {.der_len = 19, + .der = + (void *) + "\x06\x11\x2b\x06\x01\x04\x01\x92\x08\x09\x02\xaa\xda\xbe\xbe\xfa\x72\x01\x07", + .oid = "1.3.6.1.4.1.2312.9.2.1467399257458.1.7", + .expected_error = ASN1_SUCCESS}, +}; + +void +test_object_id_decoding (void) +{ + char str[128]; + int ret, ret_len; + grub_size_t i; + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { + /* decode */ + ret = + asn1_get_object_id_der (tv[i].der+1, + tv[i].der_len-1, &ret_len, str, + sizeof (str)); + if (ret != tv[i].expected_error) + { + grub_fatal ( + "%d: asn1_get_object_id_der iter %lu: got '%s' expected %d\n", + __LINE__, (unsigned long) i, asn1_strerror(ret), tv[i].expected_error); + return; + } + + if (tv[i].expected_error != ASN1_SUCCESS) + continue; + + if (ret_len != tv[i].der_len-1) + { + grub_fatal ( + "%d: iter %lu: error in DER, length returned is %d, had %d\n", + __LINE__, (unsigned long)i, ret_len, tv[i].der_len-1); + return; + } + + if (grub_strcmp (tv[i].oid, str) != 0) + { + grub_fatal ( + "%d: strcmp iter %lu: got invalid OID: %s, expected: %s\n", + __LINE__, (unsigned long) i, str, tv[i].oid); + return; + } + + } +} diff --git a/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c b/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c new file mode 100644 index 0000000000..3a83b58c59 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/object-id-encoding.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2019 Nikos Mavrogiannopoulos + * + * This file is part of LIBTASN1. + * + * This program 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. + * + * This program 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 this program. If not, see . + * + */ + +#include +#include +#include +#include +#include "../wrap_tests.h" + +struct tv +{ + int der_len; + const unsigned char *der; + const char *oid; + int expected_error; +}; + +static const struct tv tv[] = { + {.der_len = 0, + .der = (void *) "", + .oid = "5.999.3", + .expected_error = ASN1_VALUE_NOT_VALID /* cannot start with 5 */ + }, + {.der_len = 0, + .der = (void *) "", + .oid = "0.48.9", + .expected_error = ASN1_VALUE_NOT_VALID /* second field cannot be 48 */ + }, + {.der_len = 0, + .der = (void *) "", + .oid = "1.40.9", + .expected_error = ASN1_VALUE_NOT_VALID /* second field cannot be 40 */ + }, + {.der_len = 4, + .der = (void *) "\x06\x02\x4f\x63", + .oid = "1.39.99", + .expected_error = ASN1_SUCCESS, + }, + {.der_len = 6, + .der = (void *) "\x06\x04\x01\x02\x03\x04", + .oid = "0.1.2.3.4", + .expected_error = ASN1_SUCCESS}, + {.der_len = 5, + .der = (void *) "\x06\x03\x51\x02\x03", + .oid = "2.1.2.3", + .expected_error = ASN1_SUCCESS}, + {.der_len = 5, + .der = (void *) "\x06\x03\x88\x37\x03", + .oid = "2.999.3", + .expected_error = ASN1_SUCCESS}, + {.der_len = 12, + .der = (void *) "\x06\x0a\x2b\x06\x01\x04\x01\x92\x08\x09\x05\x01", + .oid = "1.3.6.1.4.1.2312.9.5.1", + .expected_error = ASN1_SUCCESS}, + {.der_len = 19, + .der = (void *) "\x06\x11\xfa\x80\x00\x00\x00\x0e\x01\x0e\xfa\x80\x00\x00\x00\x0e\x63\x6f\x6d", + .oid = "2.1998768.0.0.14.1.14.1998848.0.0.14.99.111.109", + .expected_error = ASN1_SUCCESS}, + {.der_len = 19, + .der = + (void *) + "\x06\x11\x2b\x06\x01\x04\x01\x92\x08\x09\x02\xaa\xda\xbe\xbe\xfa\x72\x01\x07", + .oid = "1.3.6.1.4.1.2312.9.2.1467399257458.1.7", + .expected_error = ASN1_SUCCESS}, +}; + +void +test_object_id_encoding(void) +{ + unsigned char der[128]; + int ret, der_len, i; + + for (i = 0; i < (int)(sizeof (tv) / sizeof (tv[0])); i++) + { + der_len = sizeof(der); + ret = asn1_object_id_der(tv[i].oid, der, &der_len, 0); + if (ret != ASN1_SUCCESS) + { + if (ret == tv[i].expected_error) + continue; + grub_fatal ( + "%d: iter %lu, encoding of OID failed: %s\n", + __LINE__, (unsigned long) i, asn1_strerror(ret)); + return; + } + else if (ret != tv[i].expected_error) + { + grub_fatal ( + "%d: iter %lu, encoding of OID %s succeeded when expecting failure\n", + __LINE__, (unsigned long) i, tv[i].oid); + return; + } + + if (der_len != tv[i].der_len || grub_memcmp(der, tv[i].der, der_len) != 0) + { + grub_fatal ( + "%d: iter %lu, re-encoding of OID %s resulted to different string (%d vs %d bytes)\n", + __LINE__, (unsigned long) i, tv[i].oid, der_len, tv[i].der_len); + + return; + } + } +} diff --git a/grub-core/lib/libtasn1_wrap/tests/octet-string.c b/grub-core/lib/libtasn1_wrap/tests/octet-string.c new file mode 100644 index 0000000000..d8a049e8df --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/octet-string.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2011-2020 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * Written by Simon Josefsson and Nikos Mavrogiannopoulos + * + */ + +#include +#include +#include +#include +#include "../wrap_tests.h" + + +struct tv +{ + const char *name; + int der_len; + const unsigned char *der_str; + int len; + const unsigned char *string; + int expected_error; + int ber; +}; + +static const struct tv tv[] = { + {.name = "primitive octet strings", + .der_len = 10, + .der_str = + (void*)"\x04\x08\x01\x23\x45\x67\x89\xab\xcd\xef", + .len = 8, + .string = + (void*)"\x01\x23\x45\x67\x89\xab\xcd\xef", + .ber = 0}, + {.der_len = 22, + .der_str = + (void*)"\x04\x14\x13\x00\xd9\xa8\x47\xf7\xf2\x1c\xf4\xb0\xec\x5f\xc1\x73\xe5\x1b\x25\xc2\x62\x27", + .len = 20, + .string = + (void*)"\x13\x00\xD9\xA8\x47\xF7\xF2\x1C\xF4\xB0\xEC\x5F\xC1\x73\xE5\x1B\x25\xC2\x62\x27"}, + + {.name = "long type of length", + .der_len = 23, + .der_str = + (void*)"\x04\x81\x14\x13\x00\xd9\xa8\x47\xf7\xf2\x1c\xf4\xb0\xec\x5f\xc1\x73\xe5\x1b\x25\xc2\x62\x27", + .len = 20, + .string = + (void*)"\x13\x00\xD9\xA8\x47\xF7\xF2\x1C\xF4\xB0\xEC\x5F\xC1\x73\xE5\x1B\x25\xC2\x62\x27", + .ber = 1}, + {.der_len = 11, + .der_str = + (void*)"\x04\x81\x08\x01\x23\x45\x67\x89\xab\xcd\xef", + .len = 8, + .string = + (void*)"\x01\x23\x45\x67\x89\xab\xcd\xef", + .ber = 1}, + + {.name = "constructed - indefinite", + .der_len = 11, + .der_str = (void*)"\x24\x80\x04\x05\x01\x02\x03\x04\x05\x00\x00", + .len = 5, + .string = (void*)"\x01\x02\x03\x04\x05", + .ber = 1, + }, + + {.name = "constructed - definite - concat", + .der_len = 12, + .der_str = (void*)"\x24\x0a\x04\x04\x0a\x0b\x0c\x0d\x04\x02\x0e\x0f", + .len = 6, + .string = (void*)"\x0a\x0b\x0c\x0d\x0e\x0f", + .ber = 1, + }, + {.name = "constructed - definite - recursive", + .der_len = 15, + .der_str = (void*)"\x24\x0d\x04\x04\x0a\x0b\x0c\x0d\x24\x05\x04\x00\x04\x01\x0f", + .len = 5, + .string = (void*)"\x0a\x0b\x0c\x0d\x0f", + .ber = 1, + }, + {.name = "constructed - definite - single", + .der_len = 7, + .der_str = (void*)"\x24\x05\x04\x03\x01\x02\x03", + .len = 3, + .string = (void*)"\x01\x02\x03", + .ber = 1, + }, + + /* a large amount of recursive indefinite encoding */ + {.name = "a large amount of recursive indefinite encoding", + .der_len = 29325, + .der_str = (void*)"\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80\x24\x80", + .len = 0, + .ber = 1, + .expected_error = ASN1_DER_ERROR + } +}; + +void +test_octet_string (void) +{ + unsigned char str[100]; + unsigned char der[100]; + int der_len = sizeof (der); + int str_size = sizeof (str); + unsigned char *tmp = NULL; + int ret, ret_len; + grub_size_t i; + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { + /* Decode */ + + if (tv[i].ber == 0) + { + str_size = sizeof (str); + ret = + asn1_get_octet_der (tv[i].der_str + 1, + tv[i].der_len - 1, &ret_len, str, + sizeof (str), &str_size); + if (ret != tv[i].expected_error) + { + grub_fatal ( + "%d: asn1_get_octet_der: %s: got %d expected %d\n", + __LINE__, tv[i].name, ret, + tv[i].expected_error); + return; + } + if (tv[i].expected_error) + continue; + + if (ret_len != tv[i].der_len - 1) + { + grub_fatal ( + "%d: error in DER, length returned is %d, had %d\n", + __LINE__, ret_len, tv[i].der_len - 1); + return; + } + + if (str_size != tv[i].len + || grub_memcmp (tv[i].string, str, tv[i].len) != 0) + { + grub_fatal ( + "%d: memcmp: %s: got invalid decoding\n", + __LINE__, tv[i].name); + + return; + } + + /* Encode */ + der_len = sizeof (der); + asn1_octet_der (str, str_size, der, &der_len); + + if (der_len != tv[i].der_len - 1 + || grub_memcmp (tv[i].der_str + 1, der, tv[i].der_len - 1) != 0) + { + grub_fatal ( + "encoding: %s: got invalid encoding\n", + tv[i].name); + return; + } + } + + ret = + asn1_decode_simple_ber (ASN1_ETYPE_OCTET_STRING, + tv[i].der_str, tv[i].der_len, + &tmp, (unsigned int*)&str_size, (unsigned int*)&der_len); + if (ret != tv[i].expected_error) + { + grub_fatal ( + "%d: asn1_decode_simple_ber: %s: got %s expected %s\n", + __LINE__, tv[i].name, asn1_strerror(ret), asn1_strerror(tv[i].expected_error)); + return; + } + if (tv[i].expected_error) + continue; + + if (der_len != tv[i].der_len) + { + grub_fatal ( + "%d: error: %s: DER, length returned is %d, had %d\n", + __LINE__, tv[i].name, der_len, tv[i].der_len); + return; + } + + if (str_size != tv[i].len || grub_memcmp (tv[i].string, tmp, tv[i].len) != 0) + { + grub_fatal ( + "%d: memcmp: %s: got invalid decoding\n", + __LINE__, tv[i].name); + return; + } + grub_free (tmp); + tmp = NULL; + + } +} diff --git a/grub-core/lib/libtasn1_wrap/tests/reproducers.c b/grub-core/lib/libtasn1_wrap/tests/reproducers.c new file mode 100644 index 0000000000..dc7268d4c6 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/tests/reproducers.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2019 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * This program 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. + * + * This program 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 this program. If not, see . + * + */ + +/****************************************************************/ +/* Description: run reproducers for several fixed issues */ +/****************************************************************/ + +#include +#include +#include +#include "../wrap_tests.h" + +#define CONST_DOWN (1U<<29) + +/* produces endless loop (fixed by d4b624b2): + * The following translates into a single node with all pointers + * (right,left,down) set to NULL. */ +const asn1_static_node endless_asn1_tab[] = { + { "TEST_TREE", 536875024, NULL }, + { NULL, 0, NULL } +}; + +/* produces memory leak (fixed by f16d1ff9): + * 152 bytes in 1 blocks are definitely lost in loss record 1 of 1 + * at 0x4837B65: calloc (vg_replace_malloc.c:762) + * by 0x4851C0D: _asn1_add_static_node (parser_aux.c:71) + * by 0x4853AAC: asn1_array2tree (structure.c:200) + * by 0x10923B: main (single_node.c:67) + */ +const asn1_static_node tab[] = { +{ "a", CONST_DOWN, "" }, +{ "b", 0, "" }, +{ "c", 0, "" }, +{ NULL, 0, NULL } +}; + +void +test_reproducers (void) +{ + int result; + asn1_node definitions = NULL; + char errorDescription[ASN1_MAX_ERROR_DESCRIPTION_SIZE]; + + result = asn1_array2tree (endless_asn1_tab, &definitions, errorDescription); + if (result != ASN1_SUCCESS) + { + grub_fatal ("Error: %s\nErrorDescription = %s\n\n", + asn1_strerror (result), errorDescription); + return; + } + + asn1_delete_structure (&definitions); + + definitions = NULL; + result = asn1_array2tree (tab, &definitions, errorDescription); + if (result != ASN1_SUCCESS) + { + grub_fatal ("Error: %s\nErrorDescription = %s\n\n", + asn1_strerror (result), errorDescription); + return; + } + + asn1_delete_structure (&definitions); +} diff --git a/grub-core/lib/libtasn1_wrap/wrap.c b/grub-core/lib/libtasn1_wrap/wrap.c new file mode 100644 index 0000000000..622ba942e3 --- /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+"); diff --git a/grub-core/lib/libtasn1_wrap/wrap_tests.c b/grub-core/lib/libtasn1_wrap/wrap_tests.c new file mode 100644 index 0000000000..75fcd21f0d --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/wrap_tests.c @@ -0,0 +1,75 @@ +/* + * 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 +#include +#include +#include "wrap_tests.h" + +/* + * libtasn1 tests - from which this is derived - are provided under GPL3+. + */ +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_command_t cmd; + +static grub_err_t +grub_cmd_asn1test (grub_command_t cmdd __attribute__((unused)), + int argc __attribute__((unused)), + char **args __attribute__((unused))) +{ + grub_printf ("test_CVE_2018_1000654\n"); + test_CVE_2018_1000654 (); + + grub_printf ("test_object_id_decoding\n"); + test_object_id_decoding (); + + grub_printf ("test_object_id_encoding\n"); + test_object_id_encoding (); + + grub_printf ("test_octet_string\n"); + test_octet_string (); + + grub_printf ("test_overflow\n"); + test_overflow (); + + grub_printf ("test_reproducers\n"); + test_overflow (); + + grub_printf ("test_simple\n"); + test_simple (); + + grub_printf ("test_strings\n"); + test_strings (); + + grub_printf ("ASN.1 self-tests passed\n"); + + return GRUB_ERR_NONE; +} + + +GRUB_MOD_INIT(test_asn1) +{ + cmd = grub_register_command ("test_asn1", grub_cmd_asn1test, NULL, + "Run self-tests for the ASN.1 parser."); +} + +GRUB_MOD_FINI(test_asn1) +{ + grub_unregister_command (cmd); +} diff --git a/grub-core/lib/libtasn1_wrap/wrap_tests.h b/grub-core/lib/libtasn1_wrap/wrap_tests.h new file mode 100644 index 0000000000..555e56dd20 --- /dev/null +++ b/grub-core/lib/libtasn1_wrap/wrap_tests.h @@ -0,0 +1,38 @@ +/* + * 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 . + */ + +#ifndef LIBTASN1_WRAP_TESTS_H +#define LIBTASN1_WRAP_TESTS_H + +void test_CVE_2018_1000654 (void); + +void test_object_id_encoding (void); + +void test_object_id_decoding (void); + +void test_octet_string (void); + +void test_overflow (void); + +void test_reproducers (void); + +void test_simple (void); + +void test_strings (void); + +#endif diff --git a/grub-core/lib/pkcs1_v15.c b/grub-core/lib/pkcs1_v15.c new file mode 100644 index 0000000000..dbacd563d0 --- /dev/null +++ b/grub-core/lib/pkcs1_v15.c @@ -0,0 +1,59 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +/* + * Given a hash value 'hval', of hash specification 'hash', perform + * the EMSA-PKCS1-v1_5 padding suitable for a key with modulus 'mod' + * (see RFC 8017 s 9.2) and place the result in 'hmpi'. + */ +gcry_err_code_t +grub_crypto_rsa_pad (gcry_mpi_t * hmpi, grub_uint8_t * hval, + const gcry_md_spec_t * hash, gcry_mpi_t mod) +{ + grub_size_t tlen, emlen, fflen; + grub_uint8_t *em, *emptr; + unsigned nbits = gcry_mpi_get_nbits (mod); + int ret; + tlen = hash->mdlen + hash->asnlen; + emlen = (nbits + 7) / 8; + if (emlen < tlen + 11) + return GPG_ERR_TOO_SHORT; + + em = grub_malloc (emlen); + if (!em) + return 1; + + em[0] = 0x00; + em[1] = 0x01; + fflen = emlen - tlen - 3; + for (emptr = em + 2; emptr < em + 2 + fflen; emptr++) + *emptr = 0xff; + *emptr++ = 0x00; + grub_memcpy (emptr, hash->asnoid, hash->asnlen); + emptr += hash->asnlen; + grub_memcpy (emptr, hval, hash->mdlen); + + ret = gcry_mpi_scan (hmpi, GCRYMPI_FMT_USG, em, emlen, 0); + grub_free (em); + return ret; +} 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 0000000000..5f8fc8ce3f --- /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 26918c8a01..4be7b40806 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 f5279756ab..14e4efdd0d 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 1adb450b5a..d3e400d500 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 eeda543c47..2f3e865495 100644 --- a/grub-core/lib/posix_wrap/sys/types.h +++ b/grub-core/lib/posix_wrap/sys/types.h @@ -50,6 +50,7 @@ typedef grub_uint8_t byte; typedef grub_addr_t uintptr_t; #define SIZEOF_UNSIGNED_LONG GRUB_CPU_SIZEOF_LONG +#define SIZEOF_UNSIGNED_LONG_INT GRUB_CPU_SIZEOF_LONG #define SIZEOF_UNSIGNED_INT 4 #define SIZEOF_UNSIGNED_LONG_LONG 8 #define SIZEOF_UNSIGNED_SHORT 2 diff --git a/grub-core/lib/progress.c b/grub-core/lib/progress.c index 4f4389dd58..eb1b7d13cf 100644 --- a/grub-core/lib/progress.c +++ b/grub-core/lib/progress.c @@ -72,7 +72,7 @@ grub_file_progress_hook_real (grub_disk_addr_t sector __attribute__ ((unused)), * 100ULL * 1000ULL, now - file->last_progress_time, 0); - if (file->size == 0) + if (file->size <= 0) percent = 100; else percent = grub_divmod64 (100 * file->progress_offset, diff --git a/grub-core/lib/reed_solomon.c b/grub-core/lib/reed_solomon.c index 562bd2e3e3..5fee7f2a1a 100644 --- a/grub-core/lib/reed_solomon.c +++ b/grub-core/lib/reed_solomon.c @@ -162,7 +162,7 @@ static void rs_encode (gf_single_t *data, grub_size_t s, grub_size_t rs) { gf_single_t *rs_polynomial; - int i, j; + unsigned int i, j; gf_single_t *m; m = xcalloc (s + rs, sizeof (gf_single_t)); grub_memcpy (m, data, s * sizeof (gf_single_t)); @@ -333,7 +333,7 @@ static void encode_block (gf_single_t *ptr, grub_size_t s, gf_single_t *rptr, grub_size_t rs) { - int i, j; + unsigned int i, j; for (i = 0; i < SECTOR_SIZE; i++) { grub_size_t ds = (s + SECTOR_SIZE - 1 - i) / SECTOR_SIZE; diff --git a/grub-core/loader/arm64/xen_boot.c b/grub-core/loader/arm64/xen_boot.c index 26e1472c9d..881011ec42 100644 --- a/grub-core/loader/arm64/xen_boot.c +++ b/grub-core/loader/arm64/xen_boot.c @@ -252,8 +252,8 @@ xen_boot (void) return err; return grub_arch_efi_linux_boot_image (xen_hypervisor->start, - xen_hypervisor->size, - xen_hypervisor->cmdline); + xen_hypervisor->size, + xen_hypervisor->cmdline); } static void diff --git a/grub-core/loader/efi/appleloader.c b/grub-core/loader/efi/appleloader.c index a0b61a240b..35d53c5f85 100644 --- a/grub-core/loader/efi/appleloader.c +++ b/grub-core/loader/efi/appleloader.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -227,6 +228,9 @@ static grub_command_t cmd; GRUB_MOD_INIT(appleloader) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + cmd = grub_register_command ("appleloader", grub_cmd_appleloader, N_("[OPTS]"), /* TRANSLATORS: This command is used on EFI to @@ -238,5 +242,8 @@ GRUB_MOD_INIT(appleloader) GRUB_MOD_FINI(appleloader) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + grub_unregister_command (cmd); } diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c index 1de98f7831..d96a0f82c2 100644 --- a/grub-core/loader/efi/chainloader.c +++ b/grub-core/loader/efi/chainloader.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -45,35 +49,27 @@ GRUB_MOD_LICENSE ("GPLv3+"); static grub_dl_t my_mod; -static grub_err_t -grub_chainloader_unload (void *context) -{ - grub_efi_handle_t image_handle = (grub_efi_handle_t) context; - grub_efi_loaded_image_t *loaded_image; - grub_efi_boot_services_t *b; - - loaded_image = grub_efi_get_loaded_image (image_handle); - if (loaded_image != NULL) - grub_free (loaded_image->load_options); - - b = grub_efi_system_table->boot_services; - b->unload_image (image_handle); - - grub_dl_unref (my_mod); - return GRUB_ERR_NONE; -} +struct grub_secureboot_chainloader_context { + grub_efi_physical_address_t address; + grub_efi_uintn_t pages; + grub_ssize_t fsize; + grub_efi_device_path_t *file_path; + grub_efi_char16_t *cmdline; + grub_ssize_t cmdline_len; + grub_efi_handle_t dev_handle; +}; static grub_err_t -grub_chainloader_boot (void *context) +grub_start_image (grub_efi_handle_t handle) { - grub_efi_handle_t image_handle = (grub_efi_handle_t) context; grub_efi_boot_services_t *b; grub_efi_status_t status; grub_efi_uintn_t exit_data_size; grub_efi_char16_t *exit_data = NULL; b = grub_efi_system_table->boot_services; - status = b->start_image (image_handle, &exit_data_size, &exit_data); + + status = grub_efi_start_image (handle, &exit_data_size, &exit_data); if (status != GRUB_EFI_SUCCESS) { if (exit_data) @@ -95,13 +91,42 @@ grub_chainloader_boot (void *context) } if (exit_data) - b->free_pool (exit_data); - - grub_loader_unset (); + grub_efi_free_pool (exit_data); return grub_errno; } +static grub_err_t +grub_chainloader_unload (void *context) +{ + grub_efi_handle_t image_handle; + grub_efi_loaded_image_t *loaded_image; + + image_handle = (grub_efi_handle_t) context; + + loaded_image = grub_efi_get_loaded_image (image_handle); + if (loaded_image != NULL) + grub_free (loaded_image->load_options); + + grub_efi_unload_image (image_handle); + + grub_dl_unref (my_mod); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_chainloader_boot (void *context) +{ + grub_efi_handle_t image_handle; + grub_err_t err; + + image_handle = (grub_efi_handle_t) context; + err = grub_start_image (image_handle); + + grub_loader_unset (); + return err; +} + static grub_err_t copy_file_path (grub_efi_file_path_device_path_t *fp, const char *str, grub_efi_uint16_t len) @@ -185,7 +210,6 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename) /* Fill the file path for the directory. */ d = (grub_efi_device_path_t *) ((char *) file_path + ((char *) d - (char *) dp)); - grub_efi_print_device_path (d); if (copy_file_path ((grub_efi_file_path_device_path_t *) d, dir_start, dir_end - dir_start) != GRUB_ERR_NONE) { @@ -209,24 +233,734 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename) return file_path; } +#define SHIM_LOCK_GUID \ + { 0x605dab50, 0xe046, 0x4300, { 0xab,0xb6,0x3d,0xd8,0x10,0xdd,0x8b,0x23 } } + +typedef union +{ + struct grub_pe32_header_32 pe32; + struct grub_pe32_header_64 pe32plus; +} grub_pe_header_t; + +struct pe_coff_loader_image_context +{ + grub_efi_uint64_t image_address; + grub_efi_uint64_t image_size; + grub_efi_uint64_t entry_point; + grub_efi_uintn_t size_of_headers; + grub_efi_uint16_t image_type; + grub_efi_uint16_t number_of_sections; + grub_efi_uint32_t section_alignment; + struct grub_pe32_section_table *first_section; + struct grub_pe32_data_directory *reloc_dir; + struct grub_pe32_data_directory *sec_dir; + grub_efi_uint64_t number_of_rva_and_sizes; + grub_pe_header_t *pe_hdr; +}; + +typedef struct pe_coff_loader_image_context pe_coff_loader_image_context_t; + +struct grub_efi_shim_lock +{ + grub_efi_status_t (*verify)(void *buffer, + grub_efi_uint32_t size); + grub_efi_status_t (*hash)(void *data, + grub_efi_int32_t datasize, + pe_coff_loader_image_context_t *context, + grub_efi_uint8_t *sha256hash, + grub_efi_uint8_t *sha1hash); + grub_efi_status_t (*context)(void *data, + grub_efi_uint32_t size, + pe_coff_loader_image_context_t *context); +}; + +typedef struct grub_efi_shim_lock grub_efi_shim_lock_t; + +static grub_efi_boolean_t +read_header (void *data, grub_efi_uint32_t size, + pe_coff_loader_image_context_t *context) +{ + grub_guid_t guid = SHIM_LOCK_GUID; + grub_efi_shim_lock_t *shim_lock; + grub_efi_status_t status; + + shim_lock = grub_efi_locate_protocol (&guid, NULL); + if (!shim_lock) + { + grub_dprintf ("chain", "no shim lock protocol"); + return 0; + } + + status = shim_lock->context (data, size, context); + + if (status == GRUB_EFI_SUCCESS) + { + grub_dprintf ("chain", "context success\n"); + return 1; + } + + switch (status) + { + case GRUB_EFI_UNSUPPORTED: + grub_error (GRUB_ERR_BAD_ARGUMENT, "context error unsupported"); + break; + case GRUB_EFI_INVALID_PARAMETER: + grub_error (GRUB_ERR_BAD_ARGUMENT, "context error invalid parameter"); + break; + default: + grub_error (GRUB_ERR_BAD_ARGUMENT, "context error code"); + break; + } + + return -1; +} + +static void* +image_address (void *image, grub_efi_uint64_t sz, grub_efi_uint64_t adr) +{ + if (adr > sz) + return NULL; + + return ((grub_uint8_t*)image + adr); +} + +static int +image_is_64_bit (grub_pe_header_t *pe_hdr) +{ + /* .Magic is the same offset in all cases */ + if (pe_hdr->pe32plus.optional_header.magic == GRUB_PE32_PE64_MAGIC) + return 1; + return 0; +} + +static const grub_uint16_t machine_type __attribute__((__unused__)) = +#if defined(__x86_64__) + GRUB_PE32_MACHINE_X86_64; +#elif defined(__aarch64__) + GRUB_PE32_MACHINE_ARM64; +#elif defined(__arm__) + GRUB_PE32_MACHINE_ARMTHUMB_MIXED; +#elif defined(__i386__) || defined(__i486__) || defined(__i686__) + GRUB_PE32_MACHINE_I386; +#elif defined(__ia64__) + GRUB_PE32_MACHINE_IA64; +#elif defined(__riscv) && (__riscv_xlen == 32) + GRUB_PE32_MACHINE_RISCV32; +#elif defined(__riscv) && (__riscv_xlen == 64) + GRUB_PE32_MACHINE_RISCV64; +#elif defined(__loongarch__) && (__loongarch_grlen == 64) + GRUB_PE32_MACHINE_LOONGARCH64; +#else +#error this architecture is not supported by grub2 +#endif + +static grub_efi_status_t +relocate_coff (pe_coff_loader_image_context_t *context, + struct grub_pe32_section_table *section, + void *orig, void *data) +{ + struct grub_pe32_data_directory *reloc_base, *reloc_base_end; + grub_efi_uint64_t adjust; + struct grub_pe32_fixup_block *reloc, *reloc_end; + char *fixup, *fixup_base, *fixup_data = NULL; + grub_efi_uint16_t *fixup_16; + grub_efi_uint32_t *fixup_32; + grub_efi_uint64_t *fixup_64; + grub_efi_uint64_t size = context->image_size; + void *image_end = (char *)orig + size; + int n = 0; + + if (image_is_64_bit (context->pe_hdr)) + context->pe_hdr->pe32plus.optional_header.image_base = + (grub_uint64_t)(unsigned long)data; + else + context->pe_hdr->pe32.optional_header.image_base = + (grub_uint32_t)(unsigned long)data; + + /* Alright, so here's how this works: + * + * context->reloc_dir gives us two things: + * - the VA the table of base relocation blocks are (maybe) to be + * mapped at (reloc_dir->rva) + * - the virtual size (reloc_dir->size) + * + * The .reloc section (section here) gives us some other things: + * - the name! kind of. (section->name) + * - the virtual size (section->virtual_size), which should be the same + * as RelocDir->Size + * - the virtual address (section->virtual_address) + * - the file section size (section->raw_data_size), which is + * a multiple of optional_header->file_alignment. Only useful for image + * validation, not really useful for iteration bounds. + * - the file address (section->raw_data_offset) + * - a bunch of stuff we don't use that's 0 in our binaries usually + * - Flags (section->characteristics) + * + * and then the thing that's actually at the file address is an array + * of struct grub_pe32_fixup_block structs with some values packed behind + * them. The block_size field of this structure includes the + * structure itself, and adding it to that structure's address will + * yield the next entry in the array. + */ + + reloc_base = image_address (orig, size, section->raw_data_offset); + reloc_base_end = image_address (orig, size, section->raw_data_offset + + section->virtual_size); + + grub_dprintf ("chain", "relocate_coff(): reloc_base %p reloc_base_end %p\n", + reloc_base, reloc_base_end); + + if (!reloc_base && !reloc_base_end) + return GRUB_EFI_SUCCESS; + + if (!reloc_base || !reloc_base_end) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc table overflows binary"); + return GRUB_EFI_UNSUPPORTED; + } + + adjust = (grub_uint64_t)(grub_efi_uintn_t)data - context->image_address; + if (adjust == 0) + return GRUB_EFI_SUCCESS; + + while (reloc_base < reloc_base_end) + { + grub_uint16_t *entry; + reloc = (struct grub_pe32_fixup_block *)((char*)reloc_base); + + if ((reloc_base->size == 0) || + (reloc_base->size > context->reloc_dir->size)) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, + "Reloc %d block size %d is invalid\n", n, + reloc_base->size); + return GRUB_EFI_UNSUPPORTED; + } + + entry = &reloc->entries[0]; + reloc_end = (struct grub_pe32_fixup_block *) + ((char *)reloc_base + reloc_base->size); + + if ((void *)reloc_end < orig || (void *)reloc_end > image_end) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc entry %d overflows binary", + n); + return GRUB_EFI_UNSUPPORTED; + } + + fixup_base = image_address(data, size, reloc_base->rva); + + if (!fixup_base) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc %d Invalid fixupbase", n); + return GRUB_EFI_UNSUPPORTED; + } + + while ((void *)entry < (void *)reloc_end) + { + fixup = fixup_base + (*entry & 0xFFF); + switch ((*entry) >> 12) + { + case GRUB_PE32_REL_BASED_ABSOLUTE: + break; + case GRUB_PE32_REL_BASED_HIGH: + fixup_16 = (grub_uint16_t *)fixup; + *fixup_16 = (grub_uint16_t) + (*fixup_16 + ((grub_uint16_t)((grub_uint32_t)adjust >> 16))); + if (fixup_data != NULL) + { + *(grub_uint16_t *) fixup_data = *fixup_16; + fixup_data = fixup_data + sizeof (grub_uint16_t); + } + break; + case GRUB_PE32_REL_BASED_LOW: + fixup_16 = (grub_uint16_t *)fixup; + *fixup_16 = (grub_uint16_t) (*fixup_16 + (grub_uint16_t)adjust); + if (fixup_data != NULL) + { + *(grub_uint16_t *) fixup_data = *fixup_16; + fixup_data = fixup_data + sizeof (grub_uint16_t); + } + break; + case GRUB_PE32_REL_BASED_HIGHLOW: + fixup_32 = (grub_uint32_t *)fixup; + *fixup_32 = *fixup_32 + (grub_uint32_t)adjust; + if (fixup_data != NULL) + { + fixup_data = (char *)ALIGN_UP ((grub_addr_t)fixup_data, sizeof (grub_uint32_t)); + *(grub_uint32_t *) fixup_data = *fixup_32; + fixup_data += sizeof (grub_uint32_t); + } + break; + case GRUB_PE32_REL_BASED_DIR64: + fixup_64 = (grub_uint64_t *)fixup; + *fixup_64 = *fixup_64 + (grub_uint64_t)adjust; + if (fixup_data != NULL) + { + fixup_data = (char *)ALIGN_UP ((grub_addr_t)fixup_data, sizeof (grub_uint64_t)); + *(grub_uint64_t *) fixup_data = *fixup_64; + fixup_data += sizeof (grub_uint64_t); + } + break; + default: + grub_error (GRUB_ERR_BAD_ARGUMENT, + "Reloc %d unknown relocation type %d", + n, (*entry) >> 12); + return GRUB_EFI_UNSUPPORTED; + } + entry += 1; + } + reloc_base = (struct grub_pe32_data_directory *)reloc_end; + n++; + } + + return GRUB_EFI_SUCCESS; +} + +static grub_efi_device_path_t * +grub_efi_get_media_file_path (grub_efi_device_path_t *dp) +{ + while (1) + { + grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); + grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); + + if (type == GRUB_EFI_END_DEVICE_PATH_TYPE) + break; + else if (type == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE + && subtype == GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE) + return dp; + + dp = GRUB_EFI_NEXT_DEVICE_PATH (dp); + } + + return NULL; +} + +static grub_efi_boolean_t +handle_image (struct grub_secureboot_chainloader_context *load_context) +{ + grub_efi_boot_services_t *b; + grub_efi_loaded_image_t *li, li_bak; + grub_efi_status_t efi_status; + void *data = (void *)(unsigned long)load_context->address; + grub_efi_uint32_t datasize = load_context->fsize; + void *buffer = NULL; + char *buffer_aligned = NULL; + grub_efi_uint32_t i; + struct grub_pe32_section_table *section; + char *base, *end; + pe_coff_loader_image_context_t context; + grub_uint32_t section_alignment; + grub_uint32_t buffer_size; + int found_entry_point = 0; + int rc; + grub_efi_status_t (__grub_efi_api *entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table); + + b = grub_efi_system_table->boot_services; + + rc = read_header (data, datasize, &context); + if (rc < 0) + { + grub_dprintf ("chain", "Failed to read header\n"); + goto error_exit; + } + else if (rc == 0) + { + grub_dprintf ("chain", "Secure Boot is not enabled\n"); + return 0; + } + else + { + grub_dprintf ("chain", "Header read without error\n"); + } + + /* + * The spec says, uselessly, of SectionAlignment: + * ===== + * The alignment (in bytes) of sections when they are loaded into + * memory. It must be greater than or equal to FileAlignment. The + * default is the page size for the architecture. + * ===== + * Which doesn't tell you whose responsibility it is to enforce the + * "default", or when. It implies that the value in the field must + * be > FileAlignment (also poorly defined), but it appears visual + * studio will happily write 512 for FileAlignment (its default) and + * 0 for SectionAlignment, intending to imply PAGE_SIZE. + * + * We only support one page size, so if it's zero, nerf it to 4096. + */ + section_alignment = context.section_alignment; + if (section_alignment == 0) + section_alignment = 4096; + + buffer_size = context.image_size + section_alignment; + grub_dprintf ("chain", "image size is %08"PRIxGRUB_UINT64_T", datasize is %08x\n", + context.image_size, datasize); + + efi_status = b->allocate_pool (GRUB_EFI_LOADER_DATA, + buffer_size, (void**)&buffer); + + if (efi_status != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto error_exit; + } + + buffer_aligned = (char *)ALIGN_UP ((grub_addr_t)buffer, section_alignment); + if (!buffer_aligned) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory")); + goto error_exit; + } + + grub_memcpy (buffer_aligned, data, context.size_of_headers); + + entry_point = image_address (buffer_aligned, context.image_size, + context.entry_point); + + grub_dprintf ("chain", "entry_point: %p\n", entry_point); + if (!entry_point) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid entry point"); + goto error_exit; + } + + char *reloc_base, *reloc_base_end; + grub_dprintf ("chain", "reloc_dir: %p reloc_size: 0x%08x\n", + (void *)(unsigned long)context.reloc_dir->rva, + context.reloc_dir->size); + reloc_base = image_address (buffer_aligned, context.image_size, + context.reloc_dir->rva); + /* RelocBaseEnd here is the address of the last byte of the table */ + reloc_base_end = image_address (buffer_aligned, context.image_size, + context.reloc_dir->rva + + context.reloc_dir->size - 1); + grub_dprintf ("chain", "reloc_base: %p reloc_base_end: %p\n", + reloc_base, reloc_base_end); + + struct grub_pe32_section_table *reloc_section = NULL, fake_reloc_section; + + section = context.first_section; + for (i = 0; i < context.number_of_sections; i++, section++) + { + char name[9]; + + base = image_address (buffer_aligned, context.image_size, + section->virtual_address); + end = image_address (buffer_aligned, context.image_size, + section->virtual_address + section->virtual_size -1); + + grub_strncpy(name, section->name, 9); + name[8] = '\0'; + grub_dprintf ("chain", "Section %d \"%s\" at %p..%p\n", i, + name, base, end); + + if (end < base) + { + grub_dprintf ("chain", " base is %p but end is %p... bad.\n", + base, end); + grub_error (GRUB_ERR_BAD_ARGUMENT, + "Image has invalid negative size"); + goto error_exit; + } + + if (section->virtual_address <= context.entry_point && + (section->virtual_address + section->raw_data_size - 1) + > context.entry_point) + { + found_entry_point++; + grub_dprintf ("chain", " section contains entry point\n"); + } + + /* We do want to process .reloc, but it's often marked + * discardable, so we don't want to memcpy it. */ + if (grub_memcmp (section->name, ".reloc\0\0", 8) == 0) + { + if (reloc_section) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, + "Image has multiple relocation sections"); + goto error_exit; + } + + /* If it has nonzero sizes, and our bounds check + * made sense, and the VA and size match RelocDir's + * versions, then we believe in this section table. */ + if (section->raw_data_size && section->virtual_size && + base && end && reloc_base == base) + { + if (reloc_base_end == end) + { + grub_dprintf ("chain", " section is relocation section\n"); + reloc_section = section; + } + else if (reloc_base_end && reloc_base_end < end) + { + /* Bogus virtual size in the reloc section -- RelocDir + * reported a smaller Base Relocation Directory. Decrease + * the section's virtual size so that it equal RelocDir's + * idea, but only for the purposes of relocate_coff(). */ + grub_dprintf ("chain", + " section is (overlong) relocation section\n"); + grub_memcpy (&fake_reloc_section, section, sizeof *section); + fake_reloc_section.virtual_size -= (end - reloc_base_end); + reloc_section = &fake_reloc_section; + } + } + + if (!reloc_section) + { + grub_dprintf ("chain", " section is not reloc section?\n"); + grub_dprintf ("chain", " rds: 0x%08x, vs: %08x\n", + section->raw_data_size, section->virtual_size); + grub_dprintf ("chain", " base: %p end: %p\n", base, end); + grub_dprintf ("chain", " reloc_base: %p reloc_base_end: %p\n", + reloc_base, reloc_base_end); + } + } + + grub_dprintf ("chain", " Section characteristics are %08x\n", + section->characteristics); + grub_dprintf ("chain", " Section virtual size: %08x\n", + section->virtual_size); + grub_dprintf ("chain", " Section raw_data size: %08x\n", + section->raw_data_size); + if (section->characteristics & GRUB_PE32_SCN_MEM_DISCARDABLE) + { + grub_dprintf ("chain", " Discarding section\n"); + continue; + } + + if (!base || !end) + { + grub_dprintf ("chain", " section is invalid\n"); + grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid section size"); + goto error_exit; + } + + if (section->characteristics & GRUB_PE32_SCN_CNT_UNINITIALIZED_DATA) + { + if (section->raw_data_size != 0) + grub_dprintf ("chain", " UNINITIALIZED_DATA section has data?\n"); + } + else if (section->virtual_address < context.size_of_headers || + section->raw_data_offset < context.size_of_headers) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, + "Section %d is inside image headers", i); + goto error_exit; + } + + if (section->raw_data_size > 0) + { + grub_dprintf ("chain", " copying 0x%08x bytes to %p\n", + section->raw_data_size, base); + grub_memcpy (base, + (grub_efi_uint8_t*)data + section->raw_data_offset, + section->raw_data_size); + } + + if (section->raw_data_size < section->virtual_size) + { + grub_dprintf ("chain", " padding with 0x%08x bytes at %p\n", + section->virtual_size - section->raw_data_size, + base + section->raw_data_size); + grub_memset (base + section->raw_data_size, 0, + section->virtual_size - section->raw_data_size); + } + + grub_dprintf ("chain", " finished section %s\n", name); + } + + /* 5 == EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC */ + if (context.number_of_rva_and_sizes <= 5) + { + grub_dprintf ("chain", "image has no relocation entry\n"); + goto error_exit; + } + + if (context.reloc_dir->size && reloc_section) + { + /* run the relocation fixups */ + efi_status = relocate_coff (&context, reloc_section, data, + buffer_aligned); + + if (efi_status != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "relocation failed"); + goto error_exit; + } + } + + if (!found_entry_point) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "entry point is not within sections"); + goto error_exit; + } + if (found_entry_point > 1) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "%d sections contain entry point", + found_entry_point); + goto error_exit; + } + + li = grub_efi_get_loaded_image (grub_efi_image_handle); + if (!li) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, "no loaded image available"); + goto error_exit; + } + + grub_memcpy (&li_bak, li, sizeof (grub_efi_loaded_image_t)); + li->image_base = buffer_aligned; + li->image_size = context.image_size; + li->load_options = load_context->cmdline; + li->load_options_size = load_context->cmdline_len; + li->file_path = grub_efi_get_media_file_path (load_context->file_path); + li->device_handle = load_context->dev_handle; + if (!li->file_path) + { + grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching file path found"); + goto error_exit; + } + + grub_dprintf ("chain", "booting via entry point\n"); + efi_status = entry_point (grub_efi_image_handle, + grub_efi_system_table); + + grub_dprintf ("chain", "entry_point returned %ld\n", efi_status); + grub_memcpy (li, &li_bak, sizeof (grub_efi_loaded_image_t)); + efi_status = b->free_pool (buffer); + + return 1; + +error_exit: + grub_dprintf ("chain", "error_exit: grub_errno: %d\n", grub_errno); + if (buffer) + b->free_pool (buffer); + + return 0; +} + +static grub_err_t +grub_secureboot_chainloader_unload (void *context) +{ + struct grub_secureboot_chainloader_context *sb_context; + + sb_context = (struct grub_secureboot_chainloader_context *) context; + + grub_efi_free_pages (sb_context->address, sb_context->pages); + grub_free (sb_context->file_path); + grub_free (sb_context->cmdline); + grub_free (sb_context); + + grub_dl_unref (my_mod); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_load_image(grub_efi_device_path_t *file_path, void *boot_image, + grub_efi_uintn_t image_size, grub_efi_handle_t dev_handle, + grub_efi_char16_t *cmdline, grub_ssize_t cmdline_len, + grub_efi_handle_t *image_handle_out) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + grub_efi_loaded_image_t *loaded_image; + + b = grub_efi_system_table->boot_services; + + status = grub_efi_load_image (0, grub_efi_image_handle, file_path, + boot_image, image_size, image_handle_out); + if (status != GRUB_EFI_SUCCESS) + { + if (status == GRUB_EFI_OUT_OF_RESOURCES) + grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources"); + else + grub_error (GRUB_ERR_BAD_OS, "cannot load image"); + return -1; + } + + /* LoadImage does not set a device handler when the image is + loaded from memory, so it is necessary to set it explicitly here. + This is a mess. */ + loaded_image = grub_efi_get_loaded_image (*image_handle_out); + if (! loaded_image) + { + grub_error (GRUB_ERR_BAD_OS, "no loaded image available"); + return -1; + } + loaded_image->device_handle = dev_handle; + + if (cmdline) + { + loaded_image->load_options = cmdline; + loaded_image->load_options_size = cmdline_len; + } + + return 0; +} + +static grub_err_t +grub_secureboot_chainloader_boot (void *context) +{ + struct grub_secureboot_chainloader_context *sb_context; + grub_efi_boot_services_t *b; + int rc; + grub_efi_handle_t handle = 0; + + sb_context = (struct grub_secureboot_chainloader_context *) context; + + rc = handle_image (sb_context); + if (rc == 0) + { + /* We weren't able to attempt to execute the image, so fall back + * to LoadImage / StartImage. + */ + rc = grub_load_image(sb_context->file_path, + (void *)(unsigned long)sb_context->address, + sb_context->fsize, sb_context->dev_handle, + sb_context->cmdline, sb_context->cmdline_len, + &handle); + if (rc == 0) + grub_start_image (handle); + } + + b = grub_efi_system_table->boot_services; + b->unload_image (handle); + + grub_loader_unset (); + return grub_errno; +} + static grub_err_t grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), int argc, char *argv[]) { grub_file_t file = 0; - grub_ssize_t size; grub_efi_status_t status; grub_efi_boot_services_t *b; grub_device_t dev = 0; - grub_efi_device_path_t *dp = NULL, *file_path = NULL; - grub_efi_loaded_image_t *loaded_image; + grub_device_t orig_dev = 0; + grub_efi_device_path_t *dp = 0, *file_path = 0; char *filename; void *boot_image = 0; - grub_efi_handle_t dev_handle = 0; grub_efi_physical_address_t address = 0; + grub_ssize_t fsize; grub_efi_uintn_t pages = 0; - grub_efi_char16_t *cmdline = NULL; - grub_efi_handle_t image_handle = NULL; + grub_efi_char16_t *cmdline = 0; + grub_ssize_t cmdline_len = 0; + grub_efi_handle_t dev_handle = 0; + grub_efi_handle_t image_handle = 0; + struct grub_secureboot_chainloader_context *sb_context = 0; + + file_path = NULL; + address = 0; + pages = 0; + cmdline = NULL; + image_handle = NULL; + dev_handle = 0; if (argc == 0) return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); @@ -236,12 +970,52 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), b = grub_efi_system_table->boot_services; + if (argc > 1) + { + int i; + grub_efi_char16_t *p16; + + for (i = 1, cmdline_len = 0; i < argc; i++) + cmdline_len += grub_strlen (argv[i]) + 1; + + cmdline_len *= sizeof (grub_efi_char16_t); + cmdline = p16 = grub_malloc (cmdline_len); + if (! cmdline) + goto fail; + + for (i = 1; i < argc; i++) + { + char *p8; + + p8 = argv[i]; + while (*p8) + *(p16++) = *(p8++); + + *(p16++) = ' '; + } + *(--p16) = 0; + } + + file = grub_file_open (filename, GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE); if (! file) goto fail; - /* Get the root device's device path. */ - dev = grub_device_open (0); + /* Get the device path from filename. */ + char *devname = grub_file_get_device_name (filename); + dev = grub_device_open (devname); + if (devname) + grub_free (devname); + + /* if device is loopback, use underlying dev */ + if (dev && dev->disk && dev->disk->dev->id == GRUB_DISK_DEVICE_LOOPBACK_ID) + { + struct grub_loopback *d; + orig_dev = dev; + d = dev->disk->data; + dev = d->file->device; + } + if (dev == NULL) ; else if (dev->disk) @@ -272,19 +1046,16 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), file_path = make_file_path (dp, filename); if (file_path == NULL) goto fail; - - grub_printf ("file path: "); - grub_efi_print_device_path (file_path); } - size = grub_file_size (file); - if (!size) + fsize = grub_file_size (file); + if (!fsize) { grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), filename); goto fail; } - pages = (grub_efi_uintn_t) GRUB_EFI_BYTES_TO_PAGES (size); + pages = (grub_efi_uintn_t) GRUB_EFI_BYTES_TO_PAGES (fsize); status = b->allocate_pages (GRUB_EFI_ALLOCATE_ANY_PAGES, GRUB_EFI_LOADER_CODE, @@ -298,7 +1069,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), } boot_image = (void *) ((grub_addr_t) address); - if (grub_file_read (file, boot_image, size) != size) + if (grub_file_read (file, boot_image, fsize) != fsize) { if (grub_errno == GRUB_ERR_NONE) grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), @@ -307,8 +1078,19 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), goto fail; } + /* + * The OS kernel is going to set its own permissions when it takes over + * paging a few million instructions from now, and load_image() will set up + * anything that's needed based on the section headers, so there's no point + * in doing anything but clearing the protection bits here. + */ + grub_dprintf("nx", "setting attributes for %p (%lu bytes) to %llx\n", + (void *)(grub_addr_t)address, fsize, 0llu); + grub_update_mem_attrs (address, fsize, + GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W|GRUB_MEM_ATTR_X, 0); + #if defined (__i386__) || defined (__x86_64__) - if (size >= (grub_ssize_t) sizeof (struct grub_macho_fat_header)) + if (fsize >= (grub_ssize_t) sizeof (struct grub_macho_fat_header)) { struct grub_macho_fat_header *head = boot_image; if (head->magic @@ -317,6 +1099,14 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_uint32_t i; struct grub_macho_fat_arch *archs = (struct grub_macho_fat_arch *) (head + 1); + + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + { + grub_error (GRUB_ERR_BAD_OS, + "MACHO binaries are forbidden with Secure Boot"); + goto fail; + } + for (i = 0; i < grub_cpu_to_le32 (head->nfat_arch); i++) { if (GRUB_MACHO_CPUTYPE_IS_HOST_CURRENT (archs[i].cputype)) @@ -331,83 +1121,67 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), > ~grub_cpu_to_le32 (archs[i].size) || grub_cpu_to_le32 (archs[i].offset) + grub_cpu_to_le32 (archs[i].size) - > (grub_size_t) size) + > (grub_size_t) fsize) { grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), filename); goto fail; } boot_image = (char *) boot_image + grub_cpu_to_le32 (archs[i].offset); - size = grub_cpu_to_le32 (archs[i].size); + fsize = grub_cpu_to_le32 (archs[i].size); } } #endif - status = b->load_image (0, grub_efi_image_handle, file_path, - boot_image, size, - &image_handle); - if (status != GRUB_EFI_SUCCESS) + if (orig_dev) { - if (status == GRUB_EFI_OUT_OF_RESOURCES) - grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources"); - else - grub_error (GRUB_ERR_BAD_OS, "cannot load image"); - - goto fail; + dev = orig_dev; + orig_dev = 0; } - /* LoadImage does not set a device handler when the image is - loaded from memory, so it is necessary to set it explicitly here. - This is a mess. */ - loaded_image = grub_efi_get_loaded_image (image_handle); - if (! loaded_image) + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) { - grub_error (GRUB_ERR_BAD_OS, "no loaded image available"); - goto fail; + sb_context = grub_malloc (sizeof (*sb_context)); + if (sb_context == NULL) + goto fail; + sb_context->address = address; + sb_context->fsize = fsize; + sb_context->pages = pages; + sb_context->file_path = file_path; + sb_context->cmdline = cmdline; + sb_context->cmdline_len = cmdline_len; + sb_context->dev_handle = dev_handle; + + grub_file_close (file); + grub_device_close (dev); + + grub_loader_set_ex (grub_secureboot_chainloader_boot, + grub_secureboot_chainloader_unload, sb_context, 0); + return 0; } - loaded_image->device_handle = dev_handle; - - /* Build load options with arguments from chainloader command line. */ - if (argc > 1) + else { - int i, len; - grub_efi_char16_t *p16; - - for (i = 1, len = 0; i < argc; i++) - len += grub_strlen (argv[i]) + 1; - - len *= sizeof (grub_efi_char16_t); - cmdline = p16 = grub_malloc (len); - if (! cmdline) - goto fail; + grub_load_image(file_path, boot_image, fsize, dev_handle, cmdline, + cmdline_len, &image_handle); + grub_file_close (file); + grub_device_close (dev); - for (i = 1; i < argc; i++) - { - char *p8; + /* We're finished with the source image buffer and file path now */ + b->free_pages (address, pages); + grub_free (file_path); - p8 = argv[i]; - while (*p8) - *(p16++) = *(p8++); + grub_loader_set_ex (grub_chainloader_boot, grub_chainloader_unload, image_handle, 0); - *(p16++) = ' '; - } - *(--p16) = 0; - - loaded_image->load_options = cmdline; - loaded_image->load_options_size = len; + return 0; } - grub_file_close (file); - grub_device_close (dev); - - /* We're finished with the source image buffer and file path now. */ - b->free_pages (address, pages); - grub_free (file_path); - - grub_loader_set_ex (grub_chainloader_boot, grub_chainloader_unload, image_handle, 0); - return 0; +fail: - fail: + if (orig_dev) + { + dev = orig_dev; + orig_dev = 0; + } if (dev) grub_device_close (dev); @@ -419,10 +1193,10 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)), grub_free (file_path); if (address) - b->free_pages (address, pages); + grub_efi_free_pages (address, pages); if (image_handle != NULL) - b->unload_image (image_handle); + grub_efi_unload_image (image_handle); grub_dl_unref (my_mod); diff --git a/grub-core/loader/efi/fdt.c b/grub-core/loader/efi/fdt.c index 439964b9ca..e08d8738af 100644 --- a/grub-core/loader/efi/fdt.c +++ b/grub-core/loader/efi/fdt.c @@ -27,6 +27,8 @@ #include #include +GRUB_MOD_LICENSE ("GPLv3+"); + static void *loaded_fdt; static void *fdt; diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c index bfbd95aeef..16f92dfc94 100644 --- a/grub-core/loader/efi/linux.c +++ b/grub-core/loader/efi/linux.c @@ -29,13 +29,16 @@ #include #include #include +#include #include #include #include +#include #include GRUB_MOD_LICENSE ("GPLv3+"); +#if !defined(__i386__) && !defined(__x86_64__) static grub_dl_t my_mod; static int loaded; @@ -70,12 +73,6 @@ static initrd_media_device_path_t initrd_lf2_device_path = { } }; -extern grub_err_t -grub_cmd_linux_x86_legacy (grub_command_t cmd, int argc, char *argv[]); - -extern grub_err_t -grub_cmd_initrd_x86_legacy (grub_command_t cmd, int argc, char *argv[]); - static grub_efi_status_t __grub_efi_api grub_efi_initrd_load_file2 (grub_efi_load_file2_t *this, grub_efi_device_path_t *device_path, @@ -86,6 +83,280 @@ grub_efi_initrd_load_file2 (grub_efi_load_file2_t *this, static grub_efi_load_file2_t initrd_lf2 = { grub_efi_initrd_load_file2 }; +#endif + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" +#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" + +grub_err_t +grub_efi_check_nx_image_support (grub_addr_t k_add, + grub_size_t k_size, + int *nx_supported) +{ + struct grub_dos_header *doshdr; + grub_size_t sz = sizeof (*doshdr); + + struct grub_pe32_header_32 *pe32; + struct grub_pe32_header_64 *pe64; + + int image_is_compatible = 0; + int is_64_bit; + + if (k_size < sz) + return grub_error (GRUB_ERR_BAD_OS, N_("kernel is too small")); + + doshdr = (void *)k_add; + + if ((doshdr->magic & 0xffff) != GRUB_DOS_MAGIC) + return grub_error (GRUB_ERR_BAD_OS, N_("kernel DOS magic is invalid")); + + sz = doshdr->lfanew + sizeof (*pe32); + if (k_size < sz) + return grub_error (GRUB_ERR_BAD_OS, N_("kernel is too small")); + + pe32 = (struct grub_pe32_header_32 *)(k_add + doshdr->lfanew); + pe64 = (struct grub_pe32_header_64 *)pe32; + + if (grub_memcmp (pe32->signature, GRUB_PE32_SIGNATURE, + GRUB_PE32_SIGNATURE_SIZE) != 0) + return grub_error (GRUB_ERR_BAD_OS, N_("kernel PE magic is invalid")); + + switch (pe32->coff_header.machine) + { + case GRUB_PE32_MACHINE_ARMTHUMB_MIXED: + case GRUB_PE32_MACHINE_I386: + case GRUB_PE32_MACHINE_RISCV32: + is_64_bit = 0; + break; + case GRUB_PE32_MACHINE_ARM64: + case GRUB_PE32_MACHINE_IA64: + case GRUB_PE32_MACHINE_LOONGARCH64: + case GRUB_PE32_MACHINE_RISCV64: + case GRUB_PE32_MACHINE_X86_64: + is_64_bit = 1; + break; + default: + return grub_error (GRUB_ERR_BAD_OS, N_("PE machine type 0x%04hx unknown"), + pe32->coff_header.machine); + } + + if (is_64_bit) + { + sz = doshdr->lfanew + sizeof (*pe64); + if (k_size < sz) + return grub_error (GRUB_ERR_BAD_OS, N_("kernel is too small")); + + if (pe64->optional_header.dll_characteristics & GRUB_PE32_NX_COMPAT) + image_is_compatible = 1; + } + else + { + if (pe32->optional_header.dll_characteristics & GRUB_PE32_NX_COMPAT) + image_is_compatible = 1; + } + + *nx_supported = image_is_compatible; + return GRUB_ERR_NONE; +} + +grub_err_t +grub_efi_check_nx_required (int *nx_required) +{ + grub_efi_status_t status; + grub_guid_t guid = GRUB_EFI_SHIM_LOCK_GUID; + grub_size_t mok_policy_sz = 0; + char *mok_policy = NULL; + grub_uint32_t mok_policy_attrs = 0; + + status = grub_efi_get_variable_with_attributes ("MokPolicy", &guid, + &mok_policy_sz, + (void **)&mok_policy, + &mok_policy_attrs); + if (status == GRUB_EFI_NOT_FOUND || + mok_policy_sz == 0 || + mok_policy == NULL) + { + *nx_required = 0; + return GRUB_ERR_NONE; + } + + *nx_required = 0; + if (mok_policy_sz < 1 || + mok_policy_attrs != (GRUB_EFI_VARIABLE_BOOTSERVICE_ACCESS | + GRUB_EFI_VARIABLE_RUNTIME_ACCESS) || + (mok_policy[mok_policy_sz-1] & GRUB_MOK_POLICY_NX_REQUIRED)) + *nx_required = 1; + + return GRUB_ERR_NONE; +} + +typedef void (*handover_func) (void *, grub_efi_system_table_t *, void *); + +grub_err_t +grub_efi_mem_set_att(grub_addr_t k_address, grub_size_t k_size, + grub_size_t k_start, int nx_supported) +{ + grub_addr_t k_start_address = k_address + k_start; + + grub_uint64_t default_set_attrs = GRUB_MEM_ATTR_R | GRUB_MEM_ATTR_W | GRUB_MEM_ATTR_X; + grub_uint64_t default_clear_attrs = 0; + grub_uint64_t stack_set_attrs = default_set_attrs; + grub_uint64_t stack_clear_attrs = default_clear_attrs; + grub_uint64_t kernel_set_attrs = default_set_attrs; + grub_uint64_t kernel_clear_attrs = default_clear_attrs; + grub_uint64_t attrs; + + struct grub_msdos_image_header *header; + struct grub_pe_image_header *pe_image_header; + struct grub_pe32_coff_header *coff_header; + struct grub_pe32_section_table *section, *sections; + grub_uint16_t i; + grub_size_t sz; + + header = (struct grub_msdos_image_header *)k_address; + + if (grub_add ((grub_addr_t) header, header->pe_image_header_offset, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("Error on PE image header address calculation")); + + pe_image_header = (struct grub_pe_image_header *) (sz); + + if (pe_image_header > (k_address + k_size)) + return grub_error (GRUB_ERR_BAD_OS, N_("PE image header address is invalid")); + + if (grub_memcmp (pe_image_header->signature, GRUB_PE32_SIGNATURE, + GRUB_PE32_SIGNATURE_SIZE) != 0) + return grub_error (GRUB_ERR_BAD_OS, N_("kernel PE magic is invalid")); + + coff_header = &(pe_image_header->coff_header); + grub_dprintf ("nx", "coff_header 0x%"PRIxGRUB_ADDR" machine %08x\n", (grub_addr_t)coff_header, coff_header->machine); + + if (grub_add ((grub_addr_t) coff_header, sizeof (*coff_header), &sz) || + grub_add (sz, coff_header->optional_header_size, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("Error on PE sections calculation")); + + sections = (struct grub_pe32_section_table *) (sz); + + if (sections > (k_address + k_size)) + return grub_error (GRUB_ERR_BAD_OS, N_("Section address is invalid")); + + /* Parse the PE, remove W for code section, remove X for data sections, RO for the rest */ + for (i = 0, section = sections; i < coff_header->num_sections; i++, section++) + { + kernel_set_attrs = default_set_attrs; + kernel_clear_attrs = default_clear_attrs; + + if (nx_supported) + { + if (section->characteristics & GRUB_PE32_SCN_MEM_EXECUTE) + { + /* RX section */ + kernel_set_attrs &= ~GRUB_MEM_ATTR_W; + kernel_clear_attrs |= GRUB_MEM_ATTR_W; + } + else if (section->characteristics & GRUB_PE32_SCN_MEM_WRITE) + { + /* RW section */ + kernel_set_attrs &= ~GRUB_MEM_ATTR_X; + kernel_clear_attrs |= GRUB_MEM_ATTR_X; + } + else + { + /* RO section */ + kernel_set_attrs &= ~GRUB_MEM_ATTR_W & ~GRUB_MEM_ATTR_X; + kernel_clear_attrs |= GRUB_MEM_ATTR_X | GRUB_MEM_ATTR_W ; + } + } + + /* Make sure we are inside range */ + if (grub_add ((grub_addr_t) k_address, section->raw_data_offset, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("Error on PE Executable section calculation")); + + grub_update_mem_attrs (sz, section->raw_data_size, kernel_set_attrs, kernel_clear_attrs); + + grub_get_mem_attrs (sz, 4096, &attrs); + grub_dprintf ("nx", "permissions for section %s 0x%"PRIxGRUB_ADDR" are %s%s%s\n", + section->name, + (grub_addr_t)sz, + (attrs & GRUB_MEM_ATTR_R) ? "r" : "-", + (attrs & GRUB_MEM_ATTR_W) ? "w" : "-", + (attrs & GRUB_MEM_ATTR_X) ? "x" : "-"); + } + + if (grub_stack_addr != (grub_addr_t)-1ll) + { + if (nx_supported) + { + stack_set_attrs &= ~GRUB_MEM_ATTR_X; + stack_clear_attrs |= GRUB_MEM_ATTR_X; + } + + grub_dprintf ("nx", "Setting attributes for stack at 0x%"PRIxGRUB_ADDR"-0x%"PRIxGRUB_ADDR" to rw%c\n", + grub_stack_addr, grub_stack_addr + grub_stack_size - 1, + (stack_set_attrs & GRUB_MEM_ATTR_X) ? 'x' : '-'); + + grub_update_mem_attrs (grub_stack_addr, grub_stack_size, + stack_set_attrs, stack_clear_attrs); + + grub_get_mem_attrs (grub_stack_addr, 4096, &attrs); + grub_dprintf ("nx", "permissions for 0x%"PRIxGRUB_ADDR" are %s%s%s\n", + grub_stack_addr, + (attrs & GRUB_MEM_ATTR_R) ? "r" : "-", + (attrs & GRUB_MEM_ATTR_W) ? "w" : "-", + (attrs & GRUB_MEM_ATTR_X) ? "x" : "-"); + } + + return GRUB_ERR_NONE; +} + + +grub_err_t +grub_efi_linux_boot (grub_addr_t k_address, grub_size_t k_size, grub_size_t k_start, + grub_off_t h_offset, void *k_params, + int nx_supported) +{ + grub_addr_t k_start_address = k_address + k_start; + grub_efi_loaded_image_t *loaded_image = NULL; + handover_func hf; + int offset = 0; + int nx_required = 0; + +#ifdef __x86_64__ + offset = 512; +#endif + + /* + * Since the EFI loader is not calling the LoadImage() and StartImage() + * services for loading the kernel and booting respectively, it has to + * set the Loaded Image base address. + */ + loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle); + if (loaded_image) + loaded_image->image_base = (void *)k_address; + else + grub_dprintf ("linux", "Loaded Image base address could not be set\n"); + + grub_dprintf ("linux", "kernel_address: %p handover_offset: %p params: %p\n", + (void *)k_address, (void *)h_offset, k_params); + + grub_efi_check_nx_required(&nx_required); + + if (nx_required && !nx_supported) + return grub_error (GRUB_ERR_BAD_OS, N_("kernel does not support NX loading required by policy")); + + grub_efi_mem_set_att (k_address, k_size, k_start, nx_supported); + +#if defined(__i386__) || defined(__x86_64__) + asm volatile ("cli"); +#endif + + hf = (handover_func)((char *)k_start_address + h_offset + offset); + hf (grub_efi_image_handle, grub_efi_system_table, k_params); + + return GRUB_ERR_BUG; +} + +#pragma GCC diagnostic pop grub_err_t grub_arch_efi_linux_load_image_header (grub_file_t file, @@ -95,7 +366,7 @@ grub_arch_efi_linux_load_image_header (grub_file_t file, if (grub_file_read (file, lh, sizeof (*lh)) < (grub_ssize_t) sizeof (*lh)) return grub_error(GRUB_ERR_FILE_READ_ERROR, "failed to read Linux image header"); - if ((lh->code0 & 0xffff) != GRUB_PE32_MAGIC) + if ((lh->code0 & 0xffff) != GRUB_DOS_MAGIC) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, N_("plain image kernel not supported - rebuild with CONFIG_(U)EFI_STUB enabled")); @@ -120,6 +391,7 @@ grub_arch_efi_linux_load_image_header (grub_file_t file, if (lh->pe_image_header.optional_header.magic != GRUB_PE32_NATIVE_MAGIC) return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "non-native image not supported"); +#if !defined(__i386__) && !defined(__x86_64__) /* * Linux kernels built for any architecture are guaranteed to support the * LoadFile2 based initrd loading protocol if the image version is >= 1. @@ -131,6 +403,7 @@ grub_arch_efi_linux_load_image_header (grub_file_t file, grub_dprintf ("linux", "LoadFile2 initrd loading %sabled\n", initrd_use_loadfile2 ? "en" : "dis"); +#endif return GRUB_ERR_NONE; } @@ -139,8 +412,8 @@ grub_arch_efi_linux_load_image_header (grub_file_t file, static grub_err_t finalize_params_linux (void) { + grub_err_t err = GRUB_ERR_NONE; int node, retval; - void *fdt; /* Set initrd info */ @@ -149,14 +422,21 @@ finalize_params_linux (void) fdt = grub_fdt_load (GRUB_EFI_LINUX_FDT_EXTRA_SPACE); if (!fdt) - goto failure; + { + err = grub_error(GRUB_ERR_BAD_OS, "failed to load FDT"); + goto failure; + } + node = grub_fdt_find_subnode (fdt, 0, "chosen"); if (node < 0) node = grub_fdt_add_subnode (fdt, 0, "chosen"); if (node < 1) - goto failure; + { + err = grub_error(grub_errno, "failed to load chosen fdt node."); + goto failure; + } grub_dprintf ("linux", "Initrd @ %p-%p\n", (void *) initrd_start, (void *) initrd_end); @@ -164,21 +444,32 @@ finalize_params_linux (void) retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-start", initrd_start); if (retval) - goto failure; + { + err = grub_error(retval, "Failed to set linux,initrd-start property"); + goto failure; + } + retval = grub_fdt_set_prop64 (fdt, node, "linux,initrd-end", initrd_end); if (retval) - goto failure; + { + err = grub_error(retval, "Failed to set linux,initrd-end property"); + goto failure; + } } - if (grub_fdt_install() != GRUB_ERR_NONE) - goto failure; + retval = grub_fdt_install(); + if (retval != GRUB_ERR_NONE) + { + err = grub_error(retval, "Failed to install fdt"); + goto failure; + } return GRUB_ERR_NONE; failure: grub_fdt_unload(); - return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT"); + return err; } #endif @@ -211,6 +502,7 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args) status = b->load_image (0, grub_efi_image_handle, (grub_efi_device_path_t *) mempath, (void *) addr, size, &image_handle); + grub_free (mempath); if (status != GRUB_EFI_SUCCESS) return grub_error (GRUB_ERR_BAD_OS, "cannot load image"); @@ -228,7 +520,7 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args) loaded_image->load_options = grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size)); if (!loaded_image->load_options) - return grub_errno; + goto unload; loaded_image->load_options_size = 2 * grub_utf8_to_utf16 (loaded_image->load_options, len, @@ -247,13 +539,12 @@ grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args) return grub_errno; } +#if !defined(__i386__) && !defined(__x86_64__) static grub_err_t grub_linux_boot (void) { -#if !defined(__i386__) && !defined(__x86_64__) if (finalize_params_linux () != GRUB_ERR_NONE) return grub_errno; -#endif return grub_arch_efi_linux_boot_image ((grub_addr_t) kernel_addr, kernel_size, linux_args); @@ -274,9 +565,7 @@ grub_linux_unload (void) if (kernel_addr) grub_efi_free_pages ((grub_addr_t) kernel_addr, GRUB_EFI_BYTES_TO_PAGES (kernel_size)); -#if !defined(__i386__) && !defined(__x86_64__) grub_fdt_unload (); -#endif if (initrd_lf2_handle != NULL) { @@ -292,7 +581,6 @@ grub_linux_unload (void) return GRUB_ERR_NONE; } -#if !defined(__i386__) && !defined(__x86_64__) /* * As per linux/Documentation/arm/Booting * ARM initrd needs to be covered by kernel linear mapping, @@ -317,18 +605,29 @@ grub_linux_unload (void) static void * allocate_initrd_mem (int initrd_pages) { - grub_addr_t max_addr; + grub_addr_t max_addr = 0; + grub_err_t err; + void *ret; - if (grub_efi_get_ram_base (&max_addr) != GRUB_ERR_NONE) - return NULL; + err = grub_efi_get_ram_base (&max_addr); + if (err != GRUB_ERR_NONE) + { + grub_error (err, "grub_efi_get_ram_base() failed"); + return NULL; + } + + grub_dprintf ("linux", "max_addr: 0x%016lx, INITRD_MAX_ADDRESS_OFFSET: 0x%016llx\n", + max_addr, INITRD_MAX_ADDRESS_OFFSET); max_addr += INITRD_MAX_ADDRESS_OFFSET - 1; + grub_dprintf ("linux", "calling grub_efi_allocate_pages_real (0x%016lx, 0x%08x, EFI_ALLOCATE_MAX_ADDRESS, EFI_LOADER_DATA)", max_addr, initrd_pages); - return grub_efi_allocate_pages_real (max_addr, initrd_pages, - GRUB_EFI_ALLOCATE_MAX_ADDRESS, - GRUB_EFI_LOADER_DATA); + ret = grub_efi_allocate_pages_real (max_addr, initrd_pages, + GRUB_EFI_ALLOCATE_MAX_ADDRESS, + GRUB_EFI_LOADER_DATA); + grub_dprintf ("linux", "got 0x%016llx\n", (unsigned long long)ret); + return ret; } -#endif static grub_efi_status_t __grub_efi_api grub_efi_initrd_load_file2 (grub_efi_load_file2_t *this, @@ -381,11 +680,6 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), goto fail; } -#if defined(__i386__) || defined(__x86_64__) - if (!initrd_use_loadfile2) - return grub_cmd_initrd_x86_legacy (cmd, argc, argv); -#endif - if (!loaded) { grub_error (GRUB_ERR_BAD_ARGUMENT, @@ -421,7 +715,6 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), return GRUB_ERR_NONE; } -#if !defined(__i386__) && !defined(__x86_64__) initrd_size = grub_get_initrd_size (&initrd_ctx); grub_dprintf ("linux", "Loading initrd\n"); @@ -444,7 +737,6 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), initrd_end = initrd_start + initrd_size; grub_dprintf ("linux", "[addr=%p, size=0x%x]\n", (void *) initrd_start, initrd_size); -#endif fail: grub_initrd_close (&initrd_ctx); @@ -463,20 +755,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_dl_ref (my_mod); if (grub_is_shim_lock_enabled () == true) - { -#if defined(__i386__) || defined(__x86_64__) - grub_dprintf ("linux", "shim_lock enabled, falling back to legacy Linux kernel loader\n"); - - err = grub_cmd_linux_x86_legacy (cmd, argc, argv); - - if (err == GRUB_ERR_NONE) - return GRUB_ERR_NONE; - else - goto fail; -#else - grub_dprintf ("linux", "shim_lock enabled, trying Linux kernel EFI stub loader\n"); -#endif - } + grub_dprintf ("linux", "shim_lock enabled, trying Linux kernel EFI stub loader\n"); if (argc == 0) { @@ -491,24 +770,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), kernel_size = grub_file_size (file); if (grub_arch_efi_linux_load_image_header (file, &lh) != GRUB_ERR_NONE) -#if !defined(__i386__) && !defined(__x86_64__) goto fail; -#else - goto fallback; - - if (!initrd_use_loadfile2) - { - /* - * This is a EFI stub image but it is too old to implement the LoadFile2 - * based initrd loading scheme, and Linux/x86 does not support the DT - * based method either. So fall back to the x86-specific loader that - * enters Linux in EFI mode but without going through its EFI stub. - */ -fallback: - grub_file_close (file); - return grub_cmd_linux_x86_legacy (cmd, argc, argv); - } -#endif grub_loader_unset(); @@ -591,3 +853,4 @@ GRUB_MOD_FINI (linux) grub_unregister_command (cmd_linux); grub_unregister_command (cmd_initrd); } +#endif diff --git a/grub-core/loader/emu/linux.c b/grub-core/loader/emu/linux.c index 7de3f7f861..6feb0412c5 100644 --- a/grub-core/loader/emu/linux.c +++ b/grub-core/loader/emu/linux.c @@ -15,7 +15,6 @@ * You should have received a copy of the GNU General Public License * along with GRUB. If not, see . */ - #include #include #include @@ -33,6 +32,196 @@ static char *kernel_path; static char *initrd_path; static char *boot_cmdline; +static grub_err_t +grub_switch_root (void) +{ + char *tmp = NULL; + char *options_cmd = NULL; + char *options = NULL; + char *subvol = NULL; + char *root_uuid = NULL; + char *kernel_release = NULL; + grub_err_t rc = GRUB_ERR_NONE; + const char *subvol_param = "subvol="; + const char *kernel_release_prefix = "/boot/vmlinuz-"; + const char *root_prefix = "root="; + const char *systemctl[] = {"systemctl", "--force", "switch-root", "/sysroot", NULL}; + const char *mountrootfs[] = {"mount", root_uuid, "/sysroot", options_cmd, options, NULL}; + const char *unamer[] = {"uname", "-r", NULL}; + char *uname_buf = NULL; + int i = 0; + + /* Extract the kernel release tag from kernel_path */ + if (!kernel_path) + { + rc = GRUB_ERR_BAD_ARGUMENT; + grub_dprintf ("linux", "switch_root: No kernel_path found\n"); + goto out; + } + + if ((kernel_release = grub_xasprintf ("%s", (kernel_path + grub_strlen (kernel_release_prefix)))) == NULL) + { + grub_dprintf ("linux", "switch_root: Failed to allocate memory\n"); + rc = GRUB_ERR_BAD_ARGUMENT; + goto out; + } + + + /* Check for kernel mismatch */ + /* Retrieve the current kernel relase tag */ + grub_util_exec_redirect (unamer, NULL, "/tmp/version"); + + grub_file_t f = grub_file_open ("/tmp/version", GRUB_FILE_TYPE_FS_SEARCH); + + if (f == NULL) + { + grub_dprintf ("linux", "failed opening file.\n"); + rc = GRUB_ERR_FILE_NOT_FOUND; + goto out; + } + + if ((uname_buf = grub_malloc (f->size)) == NULL) + { + grub_dprintf ("linux", "switch_root: Failed to allocate memory\n"); + rc = GRUB_ERR_OUT_OF_MEMORY; + goto out; + } + + if (grub_file_read (f, uname_buf, f->size) < 0) + { + grub_dprintf ("linux", "switch_root: failed to read from file\n"); + rc = GRUB_ERR_FILE_READ_ERROR; + goto out; + } + + grub_file_close (f); + + if (grub_strstr (uname_buf, kernel_release) == NULL) + { + grub_dprintf ("linux", "switch_root: kernel mismatch, not performing switch-root ...\n"); + rc = GRUB_ERR_NO_KERNEL; + goto out; + } + + /* Extract the root partition from boot_cmdline */ + if (!boot_cmdline) + { + rc = GRUB_ERR_BAD_ARGUMENT; + goto out; + } + + tmp = grub_strdup (boot_cmdline); + + if (tmp == NULL) + { + rc = GRUB_ERR_OUT_OF_MEMORY; + goto out; + } + + if ((root_uuid = grub_strstr (tmp, root_prefix)) == NULL) + { + rc = GRUB_ERR_BAD_ARGUMENT; + grub_dprintf ("linux", "switch_root: Can't find rootfs\n"); + goto out; + } + + root_uuid += grub_strlen (root_prefix); + + while (root_uuid[i] != ' ' && root_uuid[i] != '\0') + i++; + + root_uuid[i] = '\0'; + + /* Allocate a new buffer holding root_uuid */ + root_uuid = grub_xasprintf ("%s", root_uuid); + + if (root_uuid == NULL) + { + grub_dprintf ("linux", "switch_root: Failed to allocated memory\n"); + rc = GRUB_ERR_OUT_OF_MEMORY; + goto out; + } + + /* Check for subvol parameter */ + grub_strcpy (tmp, boot_cmdline); + + if ((subvol = grub_strstr(tmp, subvol_param)) != NULL) + { + i = 0; + + while (subvol[i] != ' ' && subvol[i] != '\0') + i++; + + subvol[i] = '\0'; + + /* Allocate a new buffer holding subvol */ + subvol = grub_xasprintf("%s", subvol); + + if (subvol == NULL) + { + grub_dprintf ("linux", "switch_root: Failed to allocated memory\n"); + rc = GRUB_ERR_OUT_OF_MEMORY; + goto out; + } + + options_cmd = grub_xasprintf("%s", "-o"); + options = grub_xasprintf("%s", subvol); + } + + if (options == NULL) + { + mountrootfs[3] = NULL; + } + else + { + mountrootfs[3] = options_cmd; + mountrootfs[4] = options; + } + + mountrootfs[1] = root_uuid; + + grub_dprintf ("linux", "Executing:\n"); + grub_dprintf ("linux", "%s %s %s %s %s\n", mountrootfs[0], mountrootfs[1], + mountrootfs[2], mountrootfs[3], mountrootfs[4]); + + /* Mount the rootfs */ + rc = grub_util_exec (mountrootfs); + + if (rc != GRUB_ERR_NONE) + { + grub_dprintf ("linux", "switch_root: Failed.\n"); + rc = GRUB_ERR_INVALID_COMMAND; + goto out; + } + + grub_dprintf ("linux", "Done.\n"); + + grub_dprintf ("linux", "%s %s %s %s\n", systemctl[0], systemctl[1], + systemctl[2], systemctl[3]); + + /* Switch root */ + rc = grub_util_exec (systemctl); + + if (rc != GRUB_ERR_NONE) + { + grub_dprintf ("linux", "switch_root: Failed.\n"); + rc = GRUB_ERR_INVALID_COMMAND; + goto out; + } + + grub_dprintf ("linux", "Done.\n"); + +out: + grub_free (tmp); + grub_free (options_cmd); + grub_free (options); + grub_free (subvol); + grub_free (root_uuid); + grub_free (uname_buf); + grub_free (kernel_release); + return rc; +} + static grub_err_t grub_linux_boot (void) { @@ -51,12 +240,20 @@ grub_linux_boot (void) else initrd_param = grub_xasprintf ("%s", ""); - grub_dprintf ("linux", "%serforming 'kexec -la %s %s %s'\n", - (kexecute) ? "P" : "Not p", - kernel_path, initrd_param, boot_cmdline); + if (grub_util_get_switch_root() == 1) + { + rc = grub_switch_root(); + if (rc != GRUB_ERR_NONE) + grub_fatal (N_("Failed to execute switch_root\n")); + } + else if (kexecute) + { + grub_dprintf ("linux", "%serforming 'kexec -la %s %s %s'\n", + (kexecute) ? "P" : "Not p", + kernel_path, initrd_param, boot_cmdline); - if (kexecute) - rc = grub_util_exec (kexec); + rc = grub_util_exec (kexec); + } grub_free (initrd_param); diff --git a/grub-core/loader/i386/bsd.c b/grub-core/loader/i386/bsd.c index 1f9128f6f2..50f96ea7a0 100644 --- a/grub-core/loader/i386/bsd.c +++ b/grub-core/loader/i386/bsd.c @@ -40,6 +40,7 @@ #ifdef GRUB_MACHINE_PCBIOS #include #endif +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -1340,6 +1341,7 @@ static grub_err_t grub_bsd_load_elf (grub_elf_t elf, const char *filename) { grub_err_t err; + grub_size_t sz; kern_end = 0; kern_start = ~0; @@ -1370,8 +1372,11 @@ grub_bsd_load_elf (grub_elf_t elf, const char *filename) if (grub_errno) return grub_errno; - err = grub_relocator_alloc_chunk_addr (relocator, &ch, - kern_start, kern_end - kern_start); + + if (grub_sub (kern_end, kern_start, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "underflow detected while determining size of kernel for relocator"); + + err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_start, sz); if (err) return err; @@ -1431,8 +1436,10 @@ grub_bsd_load_elf (grub_elf_t elf, const char *filename) { grub_relocator_chunk_t ch; - err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_start, - kern_end - kern_start); + if (grub_sub (kern_end, kern_start, &sz)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, "underflow detected while determining size of kernel for relocator"); + + err = grub_relocator_alloc_chunk_addr (relocator, &ch, kern_start, sz); if (err) return err; kern_chunk_src = get_virtual_current_address (ch); @@ -2137,6 +2144,9 @@ static grub_command_t cmd_netbsd_module_elf, cmd_openbsd_ramdisk; GRUB_MOD_INIT (bsd) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + /* Net and OpenBSD kernels are often compressed. */ grub_dl_load ("gzio"); @@ -2176,6 +2186,9 @@ GRUB_MOD_INIT (bsd) GRUB_MOD_FINI (bsd) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + grub_unregister_extcmd (cmd_freebsd); grub_unregister_extcmd (cmd_openbsd); grub_unregister_extcmd (cmd_netbsd); diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c new file mode 100644 index 0000000000..6c310d9879 --- /dev/null +++ b/grub-core/loader/i386/efi/linux.c @@ -0,0 +1,607 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2012 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_dl_t my_mod; + +static grub_command_t cmd_linux, cmd_initrd; +static grub_command_t cmd_linuxefi, cmd_initrdefi; + +struct grub_linuxefi_context { + void *kernel_mem; + grub_uint64_t kernel_size; + grub_uint64_t kernel_start; + grub_uint32_t handover_offset; + struct linux_kernel_params *params; + char *cmdline; + int nx_supported; + void *initrd_mem; +}; + +#define MIN(a, b) \ + ({ typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + _a < _b ? _a : _b; }) + +#define BYTES_TO_PAGES(bytes) (((bytes) + 0xfff) >> 12) + +typedef enum { + NO_MEM, + KERNEL_MEM, + INITRD_MEM, +} kernel_alloc_purpose_t; + +struct allocation_choice { + kernel_alloc_purpose_t purpose; + grub_efi_physical_address_t addr; + grub_efi_allocate_type_t alloc_type; +}; + +enum { + KERNEL_PREF_ADDRESS, + KERNEL_4G_LIMIT, + KERNEL_NO_LIMIT, + INITRD_MAX_ADDRESS, +}; + +static struct allocation_choice max_addresses[] = + { + /* the kernel overrides this one with pref_address and + * GRUB_EFI_ALLOCATE_ADDRESS */ + [KERNEL_PREF_ADDRESS] = + { KERNEL_MEM, GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + /* If the flag in params is set, this one gets changed to be above 4GB. */ + [KERNEL_4G_LIMIT] = + { KERNEL_MEM, GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + /* this one is always below 4GB, which we still *prefer* even if the flag + * is set. */ + [KERNEL_NO_LIMIT] = + { KERNEL_MEM, GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + /* this is for the initrd */ + [INITRD_MAX_ADDRESS] = + { INITRD_MEM, GRUB_EFI_MAX_ALLOCATION_ADDRESS, GRUB_EFI_ALLOCATE_MAX_ADDRESS }, + { NO_MEM, 0, 0 } + }; +static struct allocation_choice saved_addresses[sizeof(max_addresses) / sizeof(max_addresses[0])]; + +#define save_addresses() grub_memcpy(saved_addresses, max_addresses, sizeof(max_addresses)) +#define restore_addresses() grub_memcpy(max_addresses, saved_addresses, sizeof(max_addresses)) + +static inline void +kernel_free(void *addr, grub_efi_uintn_t size) +{ + if (addr && size) + grub_efi_free_pages ((grub_efi_physical_address_t)(grub_addr_t)addr, + BYTES_TO_PAGES(size)); +} + +static void * +kernel_alloc(kernel_alloc_purpose_t purpose, + grub_efi_uintn_t size, + grub_efi_memory_type_t memtype, + const char * const errmsg) +{ + void *addr = 0; + unsigned int i; + grub_efi_physical_address_t prev_max = 0; + + for (i = 0; max_addresses[i].addr != 0 && addr == 0; i++) + { + grub_uint64_t max = max_addresses[i].addr; + grub_efi_uintn_t pages; + + if (purpose != max_addresses[i].purpose) + continue; + + /* + * When we're *not* loading the kernel, or >4GB allocations aren't + * supported, these entries are basically all the same, so don't re-try + * the same parameters. + */ + if (max == prev_max) + continue; + + pages = BYTES_TO_PAGES(size); + grub_dprintf ("linux", "Trying to allocate %lu pages from %p\n", + (unsigned long)pages, (void *)(unsigned long)max); + size = pages * GRUB_EFI_PAGE_SIZE; + + prev_max = max; + addr = grub_efi_allocate_pages_real (max, pages, + max_addresses[i].alloc_type, + memtype); + if (addr) + { + grub_dprintf ("linux", "Allocated at %p\n", addr); + grub_update_mem_attrs ((grub_addr_t)addr, size, + GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W, + GRUB_MEM_ATTR_X); + } + } + + while (grub_error_pop ()) + { + ; + } + + if (addr == NULL) + grub_error (GRUB_ERR_OUT_OF_MEMORY, "%s", errmsg); + + return addr; +} + +static grub_err_t +grub_linuxefi_boot (void *data) +{ + struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) data; + + asm volatile ("cli"); + + return grub_efi_linux_boot ((grub_addr_t)context->kernel_mem, + context->kernel_size, + context->kernel_start, + context->handover_offset, + context->params, + context->nx_supported); +} + +static grub_err_t +grub_linuxefi_unload (void *data) +{ + struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) data; + struct linux_kernel_params *params = context->params; + + grub_dl_unref (my_mod); + + kernel_free (context->initrd_mem, params->ramdisk_size); + kernel_free (context->cmdline, params->cmdline_size + 1); + kernel_free (context->kernel_mem, context->kernel_size); + kernel_free (params, sizeof(*params)); + cmd_initrd->data = 0; + cmd_initrdefi->data = 0; + grub_free (context); + + max_addresses[INITRD_MAX_ADDRESS].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; + + return GRUB_ERR_NONE; +} + +#define BOUNCE_BUFFER_MAX 0x1000000ull + +static grub_ssize_t +read(grub_file_t file, grub_uint8_t *bufp, grub_size_t len) +{ + grub_ssize_t bufpos = 0; + static grub_size_t bbufsz = 0; + static char *bbuf = NULL; + + if (bbufsz == 0) + bbufsz = MIN(BOUNCE_BUFFER_MAX, len); + + while (!bbuf && bbufsz) + { + bbuf = grub_malloc(bbufsz); + if (!bbuf) + bbufsz >>= 1; + } + if (!bbuf) + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate bounce buffer")); + + while (bufpos < (long long)len) + { + grub_ssize_t sz; + + sz = grub_file_read (file, bbuf, MIN(bbufsz, len - bufpos)); + if (sz < 0) + return sz; + if (sz == 0) + break; + + grub_memcpy(bufp + bufpos, bbuf, sz); + bufpos += sz; + } + + return bufpos; +} + +#define LOW_U32(val) ((grub_uint32_t)(((grub_addr_t)(val)) & 0xffffffffull)) +#define HIGH_U32(val) ((grub_uint32_t)(((grub_addr_t)(val) >> 32) & 0xffffffffull)) + +static grub_err_t +grub_cmd_initrd (grub_command_t cmd, int argc, char *argv[]) +{ + grub_file_t *files = 0; + int i, nfiles = 0; + grub_size_t size = 0; + grub_uint8_t *ptr; + struct grub_linuxefi_context *context = (struct grub_linuxefi_context *) cmd->data; + struct linux_kernel_params *params; + void *initrd_mem = 0; + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + goto fail; + } + + if (!context) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("you need to load the kernel first")); + goto fail; + } + + params = context->params; + + files = grub_calloc (argc, sizeof (files[0])); + if (!files) + goto fail; + + for (i = 0; i < argc; i++) + { + files[i] = grub_file_open (argv[i], GRUB_FILE_TYPE_LINUX_INITRD | GRUB_FILE_TYPE_NO_DECOMPRESS); + if (! files[i]) + goto fail; + nfiles++; + if (grub_add (size, ALIGN_UP (grub_file_size (files[i]), 4), &size)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected")); + goto fail; + } + } + + grub_dprintf ("linux", "Trying to allocate initrd mem\n"); + initrd_mem = kernel_alloc(INITRD_MEM, size, GRUB_EFI_LOADER_DATA, + N_("can't allocate initrd")); + if (initrd_mem == NULL) + goto fail; + grub_dprintf ("linux", "initrd_mem = %p\n", initrd_mem); + + params->ramdisk_size = LOW_U32(size); + params->ramdisk_image = LOW_U32(initrd_mem); +#if defined(__x86_64__) + params->ext_ramdisk_size = HIGH_U32(size); + params->ext_ramdisk_image = HIGH_U32(initrd_mem); +#endif + + ptr = initrd_mem; + + for (i = 0; i < nfiles; i++) + { + grub_ssize_t cursize = grub_file_size (files[i]); + if (read (files[i], ptr, cursize) != cursize) + { + if (!grub_errno) + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), + argv[i]); + goto fail; + } + ptr += cursize; + grub_memset (ptr, 0, ALIGN_UP_OVERHEAD (cursize, 4)); + ptr += ALIGN_UP_OVERHEAD (cursize, 4); + } + + kernel_free(context->initrd_mem, params->ramdisk_size); + + context->initrd_mem = initrd_mem; + params->ramdisk_size = size; + + fail: + for (i = 0; i < nfiles; i++) + grub_file_close (files[i]); + grub_free (files); + + if (initrd_mem && grub_errno) + kernel_free (initrd_mem, size); + + return grub_errno; +} + +static grub_err_t +grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + grub_file_t file = 0; + struct linux_i386_kernel_header *lh = NULL; + grub_ssize_t start, filelen; + void *kernel = NULL; + int setup_header_end_offset; + void *kernel_mem = 0; + grub_uint64_t kernel_size = 0; + grub_uint32_t handover_offset; + struct linux_kernel_params *params = 0; + char *cmdline = 0; + int nx_supported = 1; + struct grub_linuxefi_context *context = 0; + grub_err_t err; + + grub_dl_ref (my_mod); + + if (argc == 0) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected")); + goto fail; + } + + file = grub_file_open (argv[0], GRUB_FILE_TYPE_LINUX_KERNEL); + if (! file) + goto fail; + + filelen = grub_file_size (file); + + kernel = grub_malloc(filelen); + if (!kernel) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); + goto fail; + } + + if (grub_file_read (file, kernel, filelen) != filelen) + { + grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), + argv[0]); + goto fail; + } + + err = grub_efi_check_nx_image_support ((grub_addr_t)kernel, filelen, + &nx_supported); + if (err != GRUB_ERR_NONE) + return err; + grub_dprintf ("linux", "nx is%s supported by this kernel\n", + nx_supported ? "" : " not"); + + lh = (struct linux_i386_kernel_header *)kernel; + grub_dprintf ("linux", "original lh is at %p\n", kernel); + + grub_dprintf ("linux", "checking lh->boot_flag\n"); + if (lh->boot_flag != grub_cpu_to_le16 (0xaa55)) + { + grub_error (GRUB_ERR_BAD_OS, N_("invalid magic number")); + goto fail; + } + + grub_dprintf ("linux", "checking lh->setup_sects\n"); + if (lh->setup_sects > GRUB_LINUX_MAX_SETUP_SECTS) + { + grub_error (GRUB_ERR_BAD_OS, N_("too many setup sectors")); + goto fail; + } + + grub_dprintf ("linux", "checking lh->version\n"); + if (lh->version < grub_cpu_to_le16 (0x020b)) + { + grub_error (GRUB_ERR_BAD_OS, N_("kernel too old")); + goto fail; + } + + grub_dprintf ("linux", "checking lh->handover_offset\n"); + if (!lh->handover_offset) + { + grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support EFI handover")); + goto fail; + } + +#if defined(__x86_64__) + grub_dprintf ("linux", "checking lh->xloadflags\n"); + if (!(lh->xloadflags & LINUX_XLF_KERNEL_64)) + { + grub_error (GRUB_ERR_BAD_OS, N_("kernel doesn't support 64-bit CPUs")); + goto fail; + } +#endif + +#if defined(__i386__) + if ((lh->xloadflags & LINUX_XLF_KERNEL_64) && + !(lh->xloadflags & LINUX_XLF_EFI_HANDOVER_32)) + { + grub_error (GRUB_ERR_BAD_OS, + N_("kernel doesn't support 32-bit handover")); + goto fail; + } +#endif + + max_addresses[INITRD_MAX_ADDRESS].addr = lh->initrd_addr_max; +#if defined(__x86_64__) + if (lh->xloadflags & LINUX_XLF_CAN_BE_LOADED_ABOVE_4G) + { + grub_dprintf ("linux", "Loading kernel above 4GB is supported; enabling.\n"); + max_addresses[KERNEL_NO_LIMIT].addr = GRUB_EFI_MAX_USABLE_ADDRESS; + max_addresses[INITRD_MAX_ADDRESS].addr = GRUB_EFI_MAX_USABLE_ADDRESS; + } + else + { + grub_dprintf ("linux", "Loading kernel above 4GB is not supported\n"); + } +#endif + + params = kernel_alloc (KERNEL_MEM, sizeof(*params), + GRUB_EFI_LOADER_DATA, + "cannot allocate kernel parameters"); + if (!params) + goto fail; + grub_dprintf ("linux", "params = %p\n", params); + + grub_memset (params, 0, sizeof(*params)); + + setup_header_end_offset = *((grub_uint8_t *)kernel + 0x201); + grub_dprintf ("linux", "copying %lu bytes from %p to %p\n", + (unsigned long) + MIN((grub_size_t)0x202+setup_header_end_offset, + sizeof (*params)) - 0x1f1, + (grub_uint8_t *)kernel + 0x1f1, + (grub_uint8_t *)params + 0x1f1); + grub_memcpy ((grub_uint8_t *)params + 0x1f1, + (grub_uint8_t *)kernel + 0x1f1, + MIN((grub_size_t)0x202+setup_header_end_offset,sizeof (*params)) - 0x1f1); + + lh = (struct linux_i386_kernel_header *)params; + grub_dprintf ("linux", "new lh is at %p\n", lh); + + grub_dprintf ("linux", "setting up cmdline\n"); + cmdline = kernel_alloc (KERNEL_MEM, lh->cmdline_size + 1, + GRUB_EFI_LOADER_DATA, + N_("can't allocate cmdline")); + if (!cmdline) + goto fail; + grub_dprintf ("linux", "cmdline = %p\n", cmdline); + + grub_memcpy (cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE)); + grub_create_loader_cmdline (argc, argv, + cmdline + sizeof (LINUX_IMAGE) - 1, + lh->cmdline_size - (sizeof (LINUX_IMAGE) - 1), + GRUB_VERIFY_KERNEL_CMDLINE); + + grub_dprintf ("linux", "cmdline:%s\n", cmdline); + grub_dprintf ("linux", "setting lh->cmd_line_ptr to 0x%08x\n", + LOW_U32(cmdline)); + lh->cmd_line_ptr = LOW_U32(cmdline); +#if defined(__x86_64__) + if ((grub_efi_uintn_t)cmdline > 0xffffffffull) + { + grub_dprintf ("linux", "setting params->ext_cmd_line_ptr to 0x%08x\n", + HIGH_U32(cmdline)); + params->ext_cmd_line_ptr = HIGH_U32(cmdline); + } +#endif + + handover_offset = lh->handover_offset; + grub_dprintf("linux", "handover_offset: 0x%08x\n", handover_offset); + + start = (lh->setup_sects + 1) * 512; + + /* + * AFAICS >4GB for kernel *cannot* work because of params->code32_start being + * 32-bit and getting called unconditionally in head_64.S from either entry + * point. + * + * so nerf that out here... + */ + save_addresses(); + grub_dprintf ("linux", "lh->pref_address: %p\n", (void *)(grub_addr_t)lh->pref_address); + if (lh->pref_address < (grub_uint64_t)GRUB_EFI_MAX_ALLOCATION_ADDRESS) + { + max_addresses[KERNEL_PREF_ADDRESS].addr = lh->pref_address; + max_addresses[KERNEL_PREF_ADDRESS].alloc_type = GRUB_EFI_ALLOCATE_ADDRESS; + } + max_addresses[KERNEL_4G_LIMIT].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; + max_addresses[KERNEL_NO_LIMIT].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; + kernel_size = lh->init_size; + grub_dprintf ("linux", "Trying to allocate kernel mem\n"); + kernel_mem = kernel_alloc (KERNEL_MEM, kernel_size, + GRUB_EFI_LOADER_CODE, + N_("can't allocate kernel")); + restore_addresses(); + if (!kernel_mem) + goto fail; + grub_dprintf("linux", "kernel_mem = %p\n", kernel_mem); + + grub_dprintf ("linux", "setting lh->code32_start to 0x%08x\n", + LOW_U32(kernel_mem)); + lh->code32_start = LOW_U32(kernel_mem); + + grub_memcpy (kernel_mem, (char *)kernel, filelen); + + lh->type_of_loader = 0x6; + grub_dprintf ("linux", "setting lh->type_of_loader = 0x%02x\n", + lh->type_of_loader); + + params->ext_loader_type = 0; + params->ext_loader_ver = 2; + grub_dprintf ("linux", + "setting lh->ext_loader_{type,ver} = {0x%02x,0x%02x}\n", + params->ext_loader_type, params->ext_loader_ver); + + context = grub_zalloc (sizeof (*context)); + if (!context) + goto fail; + context->kernel_mem = kernel_mem; + context->kernel_size = kernel_size; + context->kernel_start = start; + context->handover_offset = handover_offset; + context->params = params; + context->cmdline = cmdline; + context->nx_supported = nx_supported; + + grub_loader_set_ex (grub_linuxefi_boot, grub_linuxefi_unload, context, 0); + + cmd_initrd->data = context; + cmd_initrdefi->data = context; + + grub_file_close (file); + grub_free (kernel); + return 0; + +fail: + if (file) + grub_file_close (file); + + grub_dl_unref (my_mod); + + max_addresses[INITRD_MAX_ADDRESS].addr = GRUB_EFI_MAX_ALLOCATION_ADDRESS; + + if (lh) + kernel_free (cmdline, lh->cmdline_size + 1); + + kernel_free (kernel_mem, kernel_size); + kernel_free (params, sizeof(*params)); + + grub_free (context); + grub_free (kernel); + + return grub_errno; +} + +GRUB_MOD_INIT(linux) +{ + cmd_linux = + grub_register_command ("linux", grub_cmd_linux, + 0, N_("Load Linux.")); + cmd_linuxefi = + grub_register_command ("linuxefi", grub_cmd_linux, + 0, N_("Load Linux.")); + cmd_initrd = + grub_register_command ("initrd", grub_cmd_initrd, + 0, N_("Load initrd.")); + cmd_initrdefi = + grub_register_command ("initrdefi", grub_cmd_initrd, + 0, N_("Load initrd.")); + my_mod = mod; +} + +GRUB_MOD_FINI(linux) +{ + grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_linuxefi); + grub_unregister_command (cmd_initrd); + grub_unregister_command (cmd_initrdefi); +} diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c index 977757f2cc..33a8521972 100644 --- a/grub-core/loader/i386/linux.c +++ b/grub-core/loader/i386/linux.c @@ -37,6 +37,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -661,13 +662,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), { grub_file_t file = 0; struct linux_i386_kernel_header lh; + grub_uint8_t *linux_params_ptr; grub_uint8_t setup_sects; - grub_size_t real_size, prot_size, prot_file_size; + grub_size_t real_size, prot_size, prot_file_size, kernel_offset; grub_ssize_t len; int i; grub_size_t align, min_align; int relocatable; grub_uint64_t preferred_address = GRUB_LINUX_BZIMAGE_ADDR; + grub_uint8_t *kernel = NULL; grub_dl_ref (my_mod); @@ -681,7 +684,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (! file) goto fail; - if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) + len = grub_file_size (file); + kernel = grub_malloc (len); + if (!kernel) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); + goto fail; + } + + if (grub_file_read (file, kernel, len) != len) { if (!grub_errno) grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), @@ -689,6 +700,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_memcpy (&lh, kernel, sizeof (lh)); + kernel_offset = sizeof (lh); + if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55)) { grub_error (GRUB_ERR_BAD_OS, "invalid magic number"); @@ -796,17 +810,20 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), /* We've already read lh so there is no need to read it second time. */ len -= sizeof(lh); - if ((len > 0) && - (grub_file_read (file, (char *) &linux_params + sizeof (lh), len) != len)) + linux_params_ptr = (void *)&linux_params; + if (len > 0) { - if (!grub_errno) - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), - argv[0]); - goto fail; + grub_memcpy (linux_params_ptr + sizeof (lh), kernel + kernel_offset, len); + kernel_offset += len; } + grub_dprintf("efi", "setting attributes for %p (%zu bytes) to +rw-x\n", + &linux_params, sizeof (lh) + len); + grub_update_mem_attrs ((grub_addr_t)&linux_params, sizeof (lh) + len, + GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_W, GRUB_MEM_ATTR_X); + linux_params.code32_start = prot_mode_target + lh.code32_start - GRUB_LINUX_BZIMAGE_ADDR; - linux_params.kernel_alignment = (1 << align); + linux_params.kernel_alignment = ((grub_uint32_t)1 << align); linux_params.ps_mouse = linux_params.padding11 = 0; linux_params.type_of_loader = GRUB_LINUX_BOOT_LOADER_TYPE; @@ -865,7 +882,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), /* The other parameters are filled when booting. */ - grub_file_seek (file, real_size + GRUB_DISK_SECTOR_SIZE); + kernel_offset = real_size + GRUB_DISK_SECTOR_SIZE; grub_dprintf ("linux", "bzImage, setup=0x%x, size=0x%x\n", (unsigned) real_size, (unsigned) prot_size); @@ -1019,9 +1036,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } len = prot_file_size; - if (grub_file_read (file, prot_mode_mem, len) != len && !grub_errno) - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), - argv[0]); + grub_memcpy (prot_mode_mem, kernel + kernel_offset, len); if (grub_errno == GRUB_ERR_NONE) { @@ -1032,6 +1047,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), fail: + grub_free (kernel); + if (file) grub_file_close (file); @@ -1153,6 +1170,9 @@ static grub_command_t cmd_linux, cmd_initrd; GRUB_MOD_INIT(linux) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + cmd_linux = grub_register_command ("linux", grub_cmd_linux, 0, N_("Load Linux.")); cmd_initrd = grub_register_command ("initrd", grub_cmd_initrd, @@ -1162,6 +1182,9 @@ GRUB_MOD_INIT(linux) GRUB_MOD_FINI(linux) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + grub_unregister_command (cmd_linux); grub_unregister_command (cmd_initrd); } diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c index 4adeee9ae0..54a76504a7 100644 --- a/grub-core/loader/i386/pc/linux.c +++ b/grub-core/loader/i386/pc/linux.c @@ -36,6 +36,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -124,13 +125,14 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_file_t file = 0; struct linux_i386_kernel_header lh; grub_uint8_t setup_sects; - grub_size_t real_size; + grub_size_t real_size, kernel_offset = 0; grub_ssize_t len; int i; char *grub_linux_prot_chunk; int grub_linux_is_bzimage; grub_addr_t grub_linux_prot_target; grub_err_t err; + grub_uint8_t *kernel = NULL; grub_dl_ref (my_mod); @@ -144,7 +146,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), if (! file) goto fail; - if (grub_file_read (file, &lh, sizeof (lh)) != sizeof (lh)) + len = grub_file_size (file); + kernel = grub_malloc (len); + if (!kernel) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("cannot allocate kernel buffer")); + goto fail; + } + + if (grub_file_read (file, kernel, len) != len) { if (!grub_errno) grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), @@ -152,6 +162,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), goto fail; } + grub_memcpy (&lh, kernel, sizeof (lh)); + kernel_offset = sizeof (lh); + if (lh.boot_flag != grub_cpu_to_le16_compile_time (0xaa55)) { grub_error (GRUB_ERR_BAD_OS, "invalid magic number"); @@ -320,13 +333,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), grub_memmove (grub_linux_real_chunk, &lh, sizeof (lh)); len = real_size + GRUB_DISK_SECTOR_SIZE - sizeof (lh); - if (grub_file_read (file, grub_linux_real_chunk + sizeof (lh), len) != len) - { - if (!grub_errno) - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), - argv[0]); - goto fail; - } + grub_memcpy (grub_linux_real_chunk + sizeof (lh), kernel + kernel_offset, + len); + kernel_offset += len; if (lh.header != grub_cpu_to_le32_compile_time (GRUB_LINUX_I386_MAGIC_SIGNATURE) || grub_le_to_cpu16 (lh.version) < 0x0200) @@ -364,9 +373,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), } len = grub_linux16_prot_size; - if (grub_file_read (file, grub_linux_prot_chunk, len) != len && !grub_errno) - grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"), - argv[0]); + grub_memcpy (grub_linux_prot_chunk, kernel + kernel_offset, len); + kernel_offset += len; if (grub_errno == GRUB_ERR_NONE) { @@ -376,6 +384,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)), fail: + grub_free (kernel); + if (file) grub_file_close (file); @@ -474,14 +484,23 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)), return grub_errno; } -static grub_command_t cmd_linux, cmd_initrd; +static grub_command_t cmd_linux, cmd_linux16, cmd_initrd, cmd_initrd16; GRUB_MOD_INIT(linux16) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + cmd_linux = + grub_register_command ("linux", grub_cmd_linux, + 0, N_("Load Linux.")); + cmd_linux16 = grub_register_command ("linux16", grub_cmd_linux, 0, N_("Load Linux.")); cmd_initrd = + grub_register_command ("initrd", grub_cmd_initrd, + 0, N_("Load initrd.")); + cmd_initrd16 = grub_register_command ("initrd16", grub_cmd_initrd, 0, N_("Load initrd.")); my_mod = mod; @@ -489,6 +508,11 @@ GRUB_MOD_INIT(linux16) GRUB_MOD_FINI(linux16) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + grub_unregister_command (cmd_linux); + grub_unregister_command (cmd_linux16); grub_unregister_command (cmd_initrd); + grub_unregister_command (cmd_initrd16); } diff --git a/grub-core/loader/i386/pc/truecrypt.c b/grub-core/loader/i386/pc/truecrypt.c index bae1ad031b..7bf801603f 100644 --- a/grub-core/loader/i386/pc/truecrypt.c +++ b/grub-core/loader/i386/pc/truecrypt.c @@ -176,7 +176,7 @@ grub_cmd_truecrypt (grub_command_t cmd __attribute__ ((unused)), edx = (dh << 8) | grub_get_root_biosnumber (); - destaddr = ALIGN_DOWN (grub_min (0x90000, grub_mmap_get_lower ()) + destaddr = ALIGN_DOWN (grub_min (0x90000ull, grub_mmap_get_lower ()) - truecryptmemsize, 64 * 1024); { diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c index 94be512c4d..94c112a29d 100644 --- a/grub-core/loader/multiboot.c +++ b/grub-core/loader/multiboot.c @@ -50,6 +50,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -444,6 +445,9 @@ static grub_command_t cmd_multiboot, cmd_module; GRUB_MOD_INIT(multiboot) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + cmd_multiboot = #ifdef GRUB_USE_MULTIBOOT2 grub_register_command ("multiboot2", grub_cmd_multiboot, @@ -464,6 +468,9 @@ GRUB_MOD_INIT(multiboot) GRUB_MOD_FINI(multiboot) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + grub_unregister_command (cmd_multiboot); grub_unregister_command (cmd_module); } diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c index 1edad05948..24480df505 100644 --- a/grub-core/loader/multiboot_elfxx.c +++ b/grub-core/loader/multiboot_elfxx.c @@ -45,9 +45,7 @@ #endif #include - -#define CONCAT(a,b) CONCAT_(a, b) -#define CONCAT_(a,b) a ## b +#include #pragma GCC diagnostic ignored "-Wcast-align" @@ -101,7 +99,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld) return err; /* FIXME: Should we support program headers at strange locations? */ - phlimit = grub_min (MULTIBOOT_SEARCH, grub_file_size (mld->file)); + phlimit = grub_min ((grub_off_t)MULTIBOOT_SEARCH, grub_file_size (mld->file)); if ((grub_off_t) ehdr->e_phoff + phnum * ehdr->e_phentsize > phlimit) return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset"); diff --git a/grub-core/loader/xnu.c b/grub-core/loader/xnu.c index 4cca55df92..f76f511ac2 100644 --- a/grub-core/loader/xnu.c +++ b/grub-core/loader/xnu.c @@ -35,6 +35,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -1497,6 +1498,9 @@ static grub_extcmd_t cmd_splash; GRUB_MOD_INIT(xnu) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + cmd_kernel = grub_register_command ("xnu_kernel", grub_cmd_xnu_kernel, 0, N_("Load XNU image.")); cmd_kernel64 = grub_register_command ("xnu_kernel64", grub_cmd_xnu_kernel64, @@ -1540,6 +1544,9 @@ GRUB_MOD_INIT(xnu) GRUB_MOD_FINI(xnu) { + if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED) + return; + #ifndef GRUB_MACHINE_EMU grub_unregister_command (cmd_resume); #endif diff --git a/grub-core/net/arp.c b/grub-core/net/arp.c index 1d367436cf..7adeb7447b 100644 --- a/grub-core/net/arp.c +++ b/grub-core/net/arp.c @@ -31,22 +31,12 @@ enum ARP_REPLY = 2 }; -enum - { - /* IANA ARP constant to define hardware type as ethernet. */ - GRUB_NET_ARPHRD_ETHERNET = 1 - }; - -struct arppkt { +struct arphdr { grub_uint16_t hrd; grub_uint16_t pro; grub_uint8_t hln; grub_uint8_t pln; grub_uint16_t op; - grub_uint8_t sender_mac[6]; - grub_uint32_t sender_ip; - grub_uint8_t recv_mac[6]; - grub_uint32_t recv_ip; } GRUB_PACKED; static int have_pending; @@ -57,12 +47,16 @@ grub_net_arp_send_request (struct grub_net_network_level_interface *inf, const grub_net_network_level_address_t *proto_addr) { struct grub_net_buff nb; - struct arppkt *arp_packet; + struct arphdr *arp_header; grub_net_link_level_address_t target_mac_addr; grub_err_t err; int i; grub_uint8_t *nbd; grub_uint8_t arp_data[128]; + grub_uint8_t hln; + grub_uint8_t pln; + grub_uint8_t arp_packet_len; + grub_uint8_t *tmp_ptr; if (proto_addr->type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4) return grub_error (GRUB_ERR_BUG, "unsupported address family"); @@ -73,23 +67,39 @@ grub_net_arp_send_request (struct grub_net_network_level_interface *inf, grub_netbuff_clear (&nb); grub_netbuff_reserve (&nb, 128); - err = grub_netbuff_push (&nb, sizeof (*arp_packet)); + hln = inf->card->default_address.len; + pln = sizeof (proto_addr->ipv4); + arp_packet_len = sizeof (*arp_header) + 2 * (hln + pln); + + err = grub_netbuff_push (&nb, arp_packet_len); if (err) return err; - arp_packet = (struct arppkt *) nb.data; - arp_packet->hrd = grub_cpu_to_be16_compile_time (GRUB_NET_ARPHRD_ETHERNET); - arp_packet->hln = 6; - arp_packet->pro = grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP); - arp_packet->pln = 4; - arp_packet->op = grub_cpu_to_be16_compile_time (ARP_REQUEST); - /* Sender hardware address. */ - grub_memcpy (arp_packet->sender_mac, &inf->hwaddress.mac, 6); - arp_packet->sender_ip = inf->address.ipv4; - grub_memset (arp_packet->recv_mac, 0, 6); - arp_packet->recv_ip = proto_addr->ipv4; - /* Target protocol address */ - grub_memset (&target_mac_addr.mac, 0xff, 6); + arp_header = (struct arphdr *) nb.data; + arp_header->hrd = grub_cpu_to_be16 (inf->card->default_address.type); + arp_header->hln = hln; + arp_header->pro = grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP); + arp_header->pln = pln; + arp_header->op = grub_cpu_to_be16_compile_time (ARP_REQUEST); + tmp_ptr = nb.data + sizeof (*arp_header); + + /* The source hardware address. */ + grub_memcpy (tmp_ptr, inf->hwaddress.mac, hln); + tmp_ptr += hln; + + /* The source protocol address. */ + grub_memcpy (tmp_ptr, &inf->address.ipv4, pln); + tmp_ptr += pln; + + /* The target hardware address. */ + grub_memset (tmp_ptr, 0, hln); + tmp_ptr += hln; + + /* The target protocol address */ + grub_memcpy (tmp_ptr, &proto_addr->ipv4, pln); + tmp_ptr += pln; + + grub_memset (&target_mac_addr.mac, 0xff, hln); nbd = nb.data; send_ethernet_packet (inf, &nb, target_mac_addr, GRUB_NET_ETHERTYPE_ARP); @@ -114,30 +124,56 @@ grub_err_t grub_net_arp_receive (struct grub_net_buff *nb, struct grub_net_card *card, grub_uint16_t *vlantag) { - struct arppkt *arp_packet = (struct arppkt *) nb->data; + struct arphdr *arp_header = (struct arphdr *) nb->data; grub_net_network_level_address_t sender_addr, target_addr; grub_net_link_level_address_t sender_mac_addr; struct grub_net_network_level_interface *inf; - - if (arp_packet->pro != grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP) - || arp_packet->pln != 4 || arp_packet->hln != 6 - || nb->tail - nb->data < (int) sizeof (*arp_packet)) + grub_uint16_t hw_type; + grub_uint8_t hln; + grub_uint8_t pln; + grub_uint8_t arp_packet_len; + grub_uint8_t *tmp_ptr; + + hw_type = card->default_address.type; + hln = card->default_address.len; + pln = sizeof(sender_addr.ipv4); + arp_packet_len = sizeof (*arp_header) + 2 * (pln + hln); + + if (arp_header->pro != grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP) + || arp_header->hrd != grub_cpu_to_be16 (hw_type) + || arp_header->hln != hln || arp_header->pln != pln + || nb->tail - nb->data < (int) arp_packet_len) { return GRUB_ERR_NONE; + } + tmp_ptr = nb->data + sizeof (*arp_header); + + /* The source hardware address. */ + sender_mac_addr.type = hw_type; + sender_mac_addr.len = hln; + grub_memcpy (sender_mac_addr.mac, tmp_ptr, hln); + tmp_ptr += hln; + + /* The source protocol address. */ sender_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - target_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; - sender_addr.ipv4 = arp_packet->sender_ip; - target_addr.ipv4 = arp_packet->recv_ip; sender_addr.option = 0; target_addr.option = 0; - if (arp_packet->sender_ip == pending_req) - have_pending = 1; - sender_mac_addr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (sender_mac_addr.mac, arp_packet->sender_mac, - sizeof (sender_mac_addr.mac)); + grub_memcpy(&sender_addr.ipv4, tmp_ptr, pln); + tmp_ptr += pln; + grub_net_link_layer_add_address (card, &sender_addr, &sender_mac_addr, 1); + /* The target hardware address. */ + tmp_ptr += hln; + + /* The target protocol address. */ + target_addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4; + grub_memcpy(&target_addr.ipv4, tmp_ptr, pln); + + if (sender_addr.ipv4 == pending_req) + have_pending = 1; + FOR_NET_NETWORK_LEVEL_INTERFACES (inf) { /* Verify vlantag id */ @@ -150,11 +186,11 @@ grub_net_arp_receive (struct grub_net_buff *nb, struct grub_net_card *card, /* Am I the protocol address target? */ if (grub_net_addr_cmp (&inf->address, &target_addr) == 0 - && arp_packet->op == grub_cpu_to_be16_compile_time (ARP_REQUEST)) + && arp_header->op == grub_cpu_to_be16_compile_time (ARP_REQUEST)) { grub_net_link_level_address_t target; struct grub_net_buff nb_reply; - struct arppkt *arp_reply; + struct arphdr *arp_reply; grub_uint8_t arp_data[128]; grub_err_t err; @@ -163,25 +199,39 @@ grub_net_arp_receive (struct grub_net_buff *nb, struct grub_net_card *card, grub_netbuff_clear (&nb_reply); grub_netbuff_reserve (&nb_reply, 128); - err = grub_netbuff_push (&nb_reply, sizeof (*arp_packet)); + err = grub_netbuff_push (&nb_reply, arp_packet_len); if (err) return err; - arp_reply = (struct arppkt *) nb_reply.data; + arp_reply = (struct arphdr *) nb_reply.data; - arp_reply->hrd = grub_cpu_to_be16_compile_time (GRUB_NET_ARPHRD_ETHERNET); + arp_reply->hrd = grub_cpu_to_be16 (hw_type); arp_reply->pro = grub_cpu_to_be16_compile_time (GRUB_NET_ETHERTYPE_IP); - arp_reply->pln = 4; - arp_reply->hln = 6; + arp_reply->pln = pln; + arp_reply->hln = hln; arp_reply->op = grub_cpu_to_be16_compile_time (ARP_REPLY); - arp_reply->sender_ip = arp_packet->recv_ip; - arp_reply->recv_ip = arp_packet->sender_ip; - arp_reply->hln = 6; - - target.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (target.mac, arp_packet->sender_mac, 6); - grub_memcpy (arp_reply->sender_mac, inf->hwaddress.mac, 6); - grub_memcpy (arp_reply->recv_mac, arp_packet->sender_mac, 6); + + tmp_ptr = nb_reply.data + sizeof (*arp_reply); + + /* The source hardware address. */ + grub_memcpy (tmp_ptr, inf->hwaddress.mac, hln); + tmp_ptr += hln; + + /* The source protocol address. */ + grub_memcpy (tmp_ptr, &target_addr.ipv4, pln); + tmp_ptr += pln; + + /* The target hardware address. */ + grub_memcpy (tmp_ptr, sender_mac_addr.mac, hln); + tmp_ptr += hln; + + /* The target protocol address */ + grub_memcpy (tmp_ptr, &sender_addr.ipv4, pln); + tmp_ptr += pln; + + target.type = hw_type; + target.len = hln; + grub_memcpy (target.mac, sender_mac_addr.mac, hln); /* Change operation to REPLY and send packet */ send_ethernet_packet (inf, &nb_reply, target, GRUB_NET_ETHERTYPE_ARP); diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c index abe45ef7bf..bed70c0b26 100644 --- a/grub-core/net/bootp.c +++ b/grub-core/net/bootp.c @@ -20,10 +20,104 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include + +static int +dissect_url (const char *url, char **proto, char **host, char **path) +{ + const char *p, *ps; + grub_size_t l; + + *proto = *host = *path = NULL; + ps = p = url; + + while ((p = grub_strchr (p, ':'))) + { + if (grub_strlen (p) < sizeof ("://") - 1) + break; + if (grub_memcmp (p, "://", sizeof ("://") - 1) == 0) + { + l = p - ps; + *proto = grub_malloc (l + 1); + if (!*proto) + { + grub_print_error (); + return 0; + } + + grub_memcpy (*proto, ps, l); + (*proto)[l] = '\0'; + p += sizeof ("://") - 1; + break; + } + ++p; + } + + if (!*proto) + { + grub_dprintf ("bootp", "url: %s is not valid, protocol not found\n", url); + return 0; + } + + ps = p; + p = grub_strchr (p, '/'); + + if (!p) + { + grub_dprintf ("bootp", "url: %s is not valid, host/path not found\n", url); + grub_free (*proto); + *proto = NULL; + return 0; + } + + l = p - ps; + + if (l > 2 && ps[0] == '[' && ps[l - 1] == ']') + { + *host = grub_malloc (l - 1); + if (!*host) + { + grub_print_error (); + grub_free (*proto); + *proto = NULL; + return 0; + } + grub_memcpy (*host, ps + 1, l - 2); + (*host)[l - 2] = 0; + } + else + { + *host = grub_malloc (l + 1); + if (!*host) + { + grub_print_error (); + grub_free (*proto); + *proto = NULL; + return 0; + } + grub_memcpy (*host, ps, l); + (*host)[l] = 0; + } + + *path = grub_strdup (p); + if (!*path) + { + grub_print_error (); + grub_free (*host); + grub_free (*proto); + *host = NULL; + *proto = NULL; + return 0; + } + return 1; +} struct grub_dhcp_discover_options { @@ -233,11 +327,10 @@ grub_net_configure_by_dhcp_ack (const char *name, int is_def, char **device, char **path) { grub_net_network_level_address_t addr; - grub_net_link_level_address_t hwaddr; struct grub_net_network_level_interface *inter; int mask = -1; char server_ip[sizeof ("xxx.xxx.xxx.xxx")]; - const grub_uint8_t *opt; + const char *opt; grub_uint8_t opt_len, overload = 0; const char *boot_file = 0, *server_name = 0; grub_size_t boot_file_len, server_name_len; @@ -251,12 +344,8 @@ grub_net_configure_by_dhcp_ack (const char *name, if (path) *path = 0; - grub_memcpy (hwaddr.mac, bp->mac_addr, - bp->hw_len < sizeof (hwaddr.mac) ? bp->hw_len - : sizeof (hwaddr.mac)); - hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - - inter = grub_net_add_addr (name, card, &addr, &hwaddr, flags); + grub_dprintf("dhcp", "configuring dhcp for %s\n", name); + inter = grub_net_add_addr (name, card, &addr, &card->default_address, flags); if (!inter) return 0; @@ -414,6 +503,60 @@ grub_net_configure_by_dhcp_ack (const char *name, if (opt && opt_len) grub_env_set_net_property (name, "rootpath", (const char *) opt, opt_len); + opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER, &opt_len); + if (opt && opt_len) + { + grub_env_set_net_property (name, "vendor_class_identifier", (const char *) opt, opt_len); + if (opt && grub_strcmp ((char *)opt, "HTTPClient") == 0) + { + char *proto, *ip, *pa; + + if (!dissect_url (bp->boot_file, &proto, &ip, &pa)) + return inter; + + grub_env_set_net_property (name, "boot_file", pa, grub_strlen (pa)); + if (is_def) + { + grub_net_default_server = grub_strdup (ip); + grub_env_set ("net_default_interface", name); + grub_env_export ("net_default_interface"); + } + if (device && !*device) + { + *device = grub_xasprintf ("%s,%s", proto, ip); + grub_print_error (); + } + if (path) + { + *path = grub_strdup (pa); + grub_print_error (); + if (*path) + { + char *slash; + slash = grub_strrchr (*path, '/'); + if (slash) + *slash = 0; + else + **path = 0; + } + } + grub_net_add_ipv4_local (inter, mask); + inter->dhcp_ack = grub_malloc (size); + if (inter->dhcp_ack) + { + grub_memcpy (inter->dhcp_ack, bp, size); + inter->dhcp_acklen = size; + } + else + grub_errno = GRUB_ERR_NONE; + + grub_free (proto); + grub_free (ip); + grub_free (pa); + return inter; + } + } + opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_EXTENSIONS_PATH, &opt_len); if (opt && opt_len) grub_env_set_net_property (name, "extensionspath", (const char *) opt, opt_len); @@ -568,7 +711,9 @@ send_dhcp_packet (struct grub_net_network_level_interface *iface) grub_memset (pack, 0, sizeof (*pack)); pack->opcode = 1; pack->hw_type = 1; - pack->hw_len = 6; + pack->hw_len = iface->hwaddress.len > 16 ? 0 + : iface->hwaddress.len; + err = grub_get_datetime (&date); if (err || !grub_datetime2unixtime (&date, &t)) { @@ -581,7 +726,7 @@ send_dhcp_packet (struct grub_net_network_level_interface *iface) else pack->ident = iface->xid; - grub_memcpy (&pack->mac_addr, &iface->hwaddress.mac, 6); + grub_memcpy (&pack->mac_addr, &iface->hwaddress.mac, pack->hw_len); err = grub_netbuff_push (nb, sizeof (*udph)); if (err) @@ -610,6 +755,584 @@ send_dhcp_packet (struct grub_net_network_level_interface *iface) return err; } +/* The default netbuff size for sending DHCPv6 packets which should be + large enough to hold the information */ +#define GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE 512 + +struct grub_dhcp6_options +{ + grub_uint8_t *client_duid; + grub_uint16_t client_duid_len; + grub_uint8_t *server_duid; + grub_uint16_t server_duid_len; + grub_uint32_t iaid; + grub_uint32_t t1; + grub_uint32_t t2; + grub_net_network_level_address_t *ia_addr; + grub_uint32_t preferred_lifetime; + grub_uint32_t valid_lifetime; + grub_net_network_level_address_t *dns_server_addrs; + grub_uint16_t num_dns_server; + char *boot_file_proto; + char *boot_file_server_ip; + char *boot_file_path; +}; + +typedef struct grub_dhcp6_options *grub_dhcp6_options_t; + +struct grub_dhcp6_session +{ + struct grub_dhcp6_session *next; + struct grub_dhcp6_session **prev; + grub_uint32_t iaid; + grub_uint32_t transaction_id:24; + grub_uint64_t start_time; + struct grub_net_dhcp6_option_duid_ll duid; + struct grub_net_network_level_interface *iface; + + /* The associated dhcpv6 options */ + grub_dhcp6_options_t adv; + grub_dhcp6_options_t reply; +}; + +typedef struct grub_dhcp6_session *grub_dhcp6_session_t; + +typedef void (*dhcp6_option_hook_fn) (const struct grub_net_dhcp6_option *opt, void *data); + +static void +foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size, + dhcp6_option_hook_fn hook, void *hook_data); + +static void +parse_dhcp6_iaaddr (const struct grub_net_dhcp6_option *opt, void *data) +{ + grub_dhcp6_options_t dhcp6 = (grub_dhcp6_options_t )data; + + grub_uint16_t code = grub_be_to_cpu16 (opt->code); + grub_uint16_t len = grub_be_to_cpu16 (opt->len); + + if (code == GRUB_NET_DHCP6_OPTION_IAADDR) + { + const struct grub_net_dhcp6_option_iaaddr *iaaddr; + iaaddr = (const struct grub_net_dhcp6_option_iaaddr *)opt->data; + + if (len < sizeof (*iaaddr)) + { + grub_dprintf ("bootp", "DHCPv6: code %u with insufficient length %u\n", code, len); + return; + } + if (!dhcp6->ia_addr) + { + dhcp6->ia_addr = grub_malloc (sizeof(*dhcp6->ia_addr)); + dhcp6->ia_addr->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + dhcp6->ia_addr->ipv6[0] = grub_get_unaligned64 (iaaddr->addr); + dhcp6->ia_addr->ipv6[1] = grub_get_unaligned64 (iaaddr->addr + 8); + dhcp6->preferred_lifetime = grub_be_to_cpu32 (iaaddr->preferred_lifetime); + dhcp6->valid_lifetime = grub_be_to_cpu32 (iaaddr->valid_lifetime); + } + } +} + +static void +parse_dhcp6_option (const struct grub_net_dhcp6_option *opt, void *data) +{ + grub_dhcp6_options_t dhcp6 = (grub_dhcp6_options_t)data; + grub_uint16_t code = grub_be_to_cpu16 (opt->code); + grub_uint16_t len = grub_be_to_cpu16 (opt->len); + + switch (code) + { + case GRUB_NET_DHCP6_OPTION_CLIENTID: + + if (dhcp6->client_duid || !len) + { + grub_dprintf ("bootp", "Skipped DHCPv6 CLIENTID with length %u\n", len); + break; + } + dhcp6->client_duid = grub_malloc (len); + grub_memcpy (dhcp6->client_duid, opt->data, len); + dhcp6->client_duid_len = len; + break; + + case GRUB_NET_DHCP6_OPTION_SERVERID: + + if (dhcp6->server_duid || !len) + { + grub_dprintf ("bootp", "Skipped DHCPv6 SERVERID with length %u\n", len); + break; + } + dhcp6->server_duid = grub_malloc (len); + grub_memcpy (dhcp6->server_duid, opt->data, len); + dhcp6->server_duid_len = len; + break; + + case GRUB_NET_DHCP6_OPTION_IA_NA: + { + const struct grub_net_dhcp6_option_iana *ia_na; + grub_uint16_t data_len; + + if (dhcp6->iaid || len < sizeof (*ia_na)) + { + grub_dprintf ("bootp", "Skipped DHCPv6 IA_NA with length %u\n", len); + break; + } + ia_na = (const struct grub_net_dhcp6_option_iana *)opt->data; + dhcp6->iaid = grub_be_to_cpu32 (ia_na->iaid); + dhcp6->t1 = grub_be_to_cpu32 (ia_na->t1); + dhcp6->t2 = grub_be_to_cpu32 (ia_na->t2); + + data_len = len - sizeof (*ia_na); + if (data_len) + foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)ia_na->data, data_len, parse_dhcp6_iaaddr, dhcp6); + } + break; + + case GRUB_NET_DHCP6_OPTION_DNS_SERVERS: + { + const grub_uint8_t *po; + grub_uint16_t ln; + grub_net_network_level_address_t *la; + + if (!len || len & 0xf) + { + grub_dprintf ("bootp", "Skip invalid length DHCPv6 DNS_SERVERS \n"); + break; + } + dhcp6->num_dns_server = ln = len >> 4; + dhcp6->dns_server_addrs = la = grub_zalloc (ln * sizeof (*la)); + + for (po = opt->data; ln > 0; po += 0x10, la++, ln--) + { + la->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + la->ipv6[0] = grub_get_unaligned64 (po); + la->ipv6[1] = grub_get_unaligned64 (po + 8); + la->option = DNS_OPTION_PREFER_IPV6; + } + } + break; + + case GRUB_NET_DHCP6_OPTION_BOOTFILE_URL: + dissect_url ((const char *)opt->data, + &dhcp6->boot_file_proto, + &dhcp6->boot_file_server_ip, + &dhcp6->boot_file_path); + break; + + default: + break; + } +} + +static void +foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size, dhcp6_option_hook_fn hook, void *hook_data) +{ + while (size) + { + grub_uint16_t code, len; + + if (size < sizeof (*opt)) + { + grub_dprintf ("bootp", "DHCPv6: Options stopped with remaining size %" PRIxGRUB_SIZE "\n", size); + break; + } + size -= sizeof (*opt); + len = grub_be_to_cpu16 (opt->len); + code = grub_be_to_cpu16 (opt->code); + if (size < len) + { + grub_dprintf ("bootp", "DHCPv6: Options stopped at out of bound length %u for option %u\n", len, code); + break; + } + if (!len) + { + grub_dprintf ("bootp", "DHCPv6: Options stopped at zero length option %u\n", code); + break; + } + else + { + if (hook) + hook (opt, hook_data); + size -= len; + opt = (const struct grub_net_dhcp6_option *)((grub_uint8_t *)opt + len + sizeof (*opt)); + } + } +} + +static grub_dhcp6_options_t +grub_dhcp6_options_get (const struct grub_net_dhcp6_packet *v6h, + grub_size_t size) +{ + grub_dhcp6_options_t options; + + if (size < sizeof (*v6h)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("DHCPv6 packet size too small")); + return NULL; + } + + options = grub_zalloc (sizeof(*options)); + if (!options) + return NULL; + + foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)v6h->dhcp_options, + size - sizeof (*v6h), parse_dhcp6_option, options); + + return options; +} + +static void +grub_dhcp6_options_free (grub_dhcp6_options_t options) +{ + if (options->client_duid) + grub_free (options->client_duid); + if (options->server_duid) + grub_free (options->server_duid); + if (options->ia_addr) + grub_free (options->ia_addr); + if (options->dns_server_addrs) + grub_free (options->dns_server_addrs); + if (options->boot_file_proto) + grub_free (options->boot_file_proto); + if (options->boot_file_server_ip) + grub_free (options->boot_file_server_ip); + if (options->boot_file_path) + grub_free (options->boot_file_path); + + grub_free (options); +} + +static grub_dhcp6_session_t grub_dhcp6_sessions; +#define FOR_DHCP6_SESSIONS_SAFE(var, next) FOR_LIST_ELEMENTS_SAFE (var, next, grub_dhcp6_sessions) +#define FOR_DHCP6_SESSIONS(var) FOR_LIST_ELEMENTS (var, grub_dhcp6_sessions) + +static void +grub_net_configure_by_dhcp6_info (const char *name, + struct grub_net_card *card, + grub_dhcp6_options_t dhcp6, + int is_def, + int flags, + struct grub_net_network_level_interface **ret_inf) +{ + grub_net_network_level_netaddress_t netaddr; + struct grub_net_network_level_interface *inf; + + if (dhcp6->ia_addr) + { + inf = grub_net_add_addr (name, card, dhcp6->ia_addr, &card->default_address, flags); + + netaddr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + netaddr.ipv6.base[0] = dhcp6->ia_addr->ipv6[0]; + netaddr.ipv6.base[1] = 0; + netaddr.ipv6.masksize = 64; + grub_net_add_route (name, netaddr, inf); + + if (ret_inf) + *ret_inf = inf; + } + + if (dhcp6->dns_server_addrs) + { + grub_uint16_t i; + + for (i = 0; i < dhcp6->num_dns_server; ++i) + grub_net_add_dns_server (dhcp6->dns_server_addrs + i); + } + + if (dhcp6->boot_file_path) + grub_env_set_net_property (name, "boot_file", dhcp6->boot_file_path, + grub_strlen (dhcp6->boot_file_path)); + + if (is_def && dhcp6->boot_file_server_ip) + { + grub_net_default_server = grub_strdup (dhcp6->boot_file_server_ip); + grub_env_set ("net_default_interface", name); + grub_env_export ("net_default_interface"); + } +} + +static void +grub_dhcp6_session_add (struct grub_net_network_level_interface *iface, + grub_uint32_t iaid) +{ + grub_dhcp6_session_t se; + struct grub_datetime date; + grub_err_t err; + grub_int64_t t = 0; + + se = grub_malloc (sizeof (*se)); + + err = grub_get_datetime (&date); + if (err || !grub_datetime2unixtime (&date, &t)) + { + grub_errno = GRUB_ERR_NONE; + t = 0; + } + + se->iface = iface; + se->iaid = iaid; + se->transaction_id = t; + se->start_time = grub_get_time_ms (); + se->duid.type = grub_cpu_to_be16_compile_time (3) ; + se->duid.hw_type = grub_cpu_to_be16_compile_time (1); + grub_memcpy (&se->duid.hwaddr, &iface->hwaddress.mac, sizeof (se->duid.hwaddr)); + se->adv = NULL; + se->reply = NULL; + grub_list_push (GRUB_AS_LIST_P (&grub_dhcp6_sessions), GRUB_AS_LIST (se)); +} + +static void +grub_dhcp6_session_remove (grub_dhcp6_session_t se) +{ + grub_list_remove (GRUB_AS_LIST (se)); + if (se->adv) + grub_dhcp6_options_free (se->adv); + if (se->reply) + grub_dhcp6_options_free (se->reply); + grub_free (se); +} + +static void +grub_dhcp6_session_remove_all (void) +{ + grub_dhcp6_session_t se, next; + + FOR_DHCP6_SESSIONS_SAFE (se, next) + { + grub_dhcp6_session_remove (se); + } + grub_dhcp6_sessions = NULL; +} + +static grub_err_t +grub_dhcp6_session_configure_network (grub_dhcp6_session_t se) +{ + char *name; + + name = grub_xasprintf ("%s:dhcp6", se->iface->card->name); + if (!name) + return grub_errno; + + grub_net_configure_by_dhcp6_info (name, se->iface->card, se->reply, 1, 0, 0); + grub_free (name); + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_dhcp6_session_send_request (grub_dhcp6_session_t se) +{ + struct grub_net_buff *nb; + struct grub_net_dhcp6_option *opt; + struct grub_net_dhcp6_packet *v6h; + struct grub_net_dhcp6_option_iana *ia_na; + struct grub_net_dhcp6_option_iaaddr *iaaddr; + struct udphdr *udph; + grub_net_network_level_address_t multicast; + grub_net_link_level_address_t ll_multicast; + grub_uint64_t elapsed; + struct grub_net_network_level_interface *inf = se->iface; + grub_dhcp6_options_t dhcp6 = se->adv; + grub_err_t err = GRUB_ERR_NONE; + + multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48); + multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL); + + err = grub_net_link_layer_resolve (inf, &multicast, &ll_multicast); + if (err) + return err; + + nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); + + if (!nb) + return grub_errno; + + err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + err = grub_netbuff_push (nb, dhcp6->client_duid_len + sizeof (*opt)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_CLIENTID); + opt->len = grub_cpu_to_be16 (dhcp6->client_duid_len); + grub_memcpy (opt->data, dhcp6->client_duid , dhcp6->client_duid_len); + + err = grub_netbuff_push (nb, dhcp6->server_duid_len + sizeof (*opt)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_SERVERID); + opt->len = grub_cpu_to_be16 (dhcp6->server_duid_len); + grub_memcpy (opt->data, dhcp6->server_duid , dhcp6->server_duid_len); + + err = grub_netbuff_push (nb, sizeof (*ia_na) + sizeof (*opt)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + if (dhcp6->ia_addr) + { + err = grub_netbuff_push (nb, sizeof(*iaaddr) + sizeof (*opt)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + } + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA); + opt->len = grub_cpu_to_be16 (sizeof (*ia_na)); + if (dhcp6->ia_addr) + opt->len += grub_cpu_to_be16 (sizeof(*iaaddr) + sizeof (*opt)); + + ia_na = (struct grub_net_dhcp6_option_iana *)opt->data; + ia_na->iaid = grub_cpu_to_be32 (dhcp6->iaid); + + ia_na->t1 = grub_cpu_to_be32 (dhcp6->t1); + ia_na->t2 = grub_cpu_to_be32 (dhcp6->t2); + + if (dhcp6->ia_addr) + { + opt = (struct grub_net_dhcp6_option *)ia_na->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR); + opt->len = grub_cpu_to_be16 (sizeof (*iaaddr)); + iaaddr = (struct grub_net_dhcp6_option_iaaddr *)opt->data; + grub_set_unaligned64 (iaaddr->addr, dhcp6->ia_addr->ipv6[0]); + grub_set_unaligned64 (iaaddr->addr + 8, dhcp6->ia_addr->ipv6[1]); + + iaaddr->preferred_lifetime = grub_cpu_to_be32 (dhcp6->preferred_lifetime); + iaaddr->valid_lifetime = grub_cpu_to_be32 (dhcp6->valid_lifetime); + } + + err = grub_netbuff_push (nb, sizeof (*opt) + 2 * sizeof (grub_uint16_t)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + opt = (struct grub_net_dhcp6_option*) nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ORO); + opt->len = grub_cpu_to_be16_compile_time (2 * sizeof (grub_uint16_t)); + grub_set_unaligned16 (opt->data, grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_BOOTFILE_URL)); + grub_set_unaligned16 (opt->data + 2, grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_DNS_SERVERS)); + + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + opt = (struct grub_net_dhcp6_option*) nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ELAPSED_TIME); + opt->len = grub_cpu_to_be16_compile_time (sizeof (grub_uint16_t)); + + /* the time is expressed in hundredths of a second */ + elapsed = grub_divmod64 (grub_get_time_ms () - se->start_time, 10, 0); + + if (elapsed > 0xffff) + elapsed = 0xffff; + + grub_set_unaligned16 (opt->data, grub_cpu_to_be16 ((grub_uint16_t)elapsed)); + + err = grub_netbuff_push (nb, sizeof (*v6h)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + v6h = (struct grub_net_dhcp6_packet *) nb->data; + v6h->message_type = GRUB_NET_DHCP6_REQUEST; + v6h->transaction_id = se->transaction_id; + + err = grub_netbuff_push (nb, sizeof (*udph)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + udph = (struct udphdr *) nb->data; + udph->src = grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT); + udph->dst = grub_cpu_to_be16_compile_time (DHCP6_SERVER_PORT); + udph->chksum = 0; + udph->len = grub_cpu_to_be16 (nb->tail - nb->data); + + udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, + &inf->address, + &multicast); + err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb, + GRUB_NET_IP_UDP); + + grub_netbuff_free (nb); + + return err; +} + +struct grub_net_network_level_interface * +grub_net_configure_by_dhcpv6_reply (const char *name, + struct grub_net_card *card, + grub_net_interface_flags_t flags, + const struct grub_net_dhcp6_packet *v6h, + grub_size_t size, + int is_def, + char **device, char **path) +{ + struct grub_net_network_level_interface *inf; + grub_dhcp6_options_t dhcp6; + int mask = -1; + + dhcp6 = grub_dhcp6_options_get (v6h, size); + if (!dhcp6) + { + grub_print_error (); + return NULL; + } + + grub_net_configure_by_dhcp6_info (name, card, dhcp6, is_def, flags, &inf); + + if (device && dhcp6->boot_file_proto && dhcp6->boot_file_server_ip) + { + *device = grub_xasprintf ("%s,%s", dhcp6->boot_file_proto, dhcp6->boot_file_server_ip); + grub_print_error (); + } + if (path && dhcp6->boot_file_path) + { + *path = grub_strdup (dhcp6->boot_file_path); + grub_print_error (); + if (*path) + { + char *slash; + slash = grub_strrchr (*path, '/'); + if (slash) + *slash = 0; + else + **path = 0; + } + } + + grub_dhcp6_options_free (dhcp6); + + if (inf) + grub_net_add_ipv6_local (inf, mask); + + return inf; +} + /* * This is called directly from net/ip.c:handle_dgram(), because those * BOOTP/DHCP packets are a bit special due to their improper @@ -678,6 +1401,77 @@ grub_net_process_dhcp (struct grub_net_buff *nb, } } +grub_err_t +grub_net_process_dhcp6 (struct grub_net_buff *nb, + struct grub_net_card *card __attribute__ ((unused))) +{ + const struct grub_net_dhcp6_packet *v6h; + grub_dhcp6_session_t se; + grub_size_t size; + grub_dhcp6_options_t options; + + v6h = (const struct grub_net_dhcp6_packet *) nb->data; + size = nb->tail - nb->data; + + options = grub_dhcp6_options_get (v6h, size); + if (!options) + return grub_errno; + + if (!options->client_duid || !options->server_duid || !options->ia_addr) + { + grub_dhcp6_options_free (options); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Bad DHCPv6 Packet"); + } + + FOR_DHCP6_SESSIONS (se) + { + if (se->transaction_id == v6h->transaction_id && + grub_memcmp (options->client_duid, &se->duid, sizeof (se->duid)) == 0 && + se->iaid == options->iaid) + break; + } + + if (!se) + { + grub_dprintf ("bootp", "DHCPv6 session not found\n"); + grub_dhcp6_options_free (options); + return GRUB_ERR_NONE; + } + + if (v6h->message_type == GRUB_NET_DHCP6_ADVERTISE) + { + if (se->adv) + { + grub_dprintf ("bootp", "Skipped DHCPv6 Advertised .. \n"); + grub_dhcp6_options_free (options); + return GRUB_ERR_NONE; + } + + se->adv = options; + return grub_dhcp6_session_send_request (se); + } + else if (v6h->message_type == GRUB_NET_DHCP6_REPLY) + { + if (!se->adv) + { + grub_dprintf ("bootp", "Skipped DHCPv6 Reply .. \n"); + grub_dhcp6_options_free (options); + return GRUB_ERR_NONE; + } + + se->reply = options; + grub_dhcp6_session_configure_network (se); + grub_dhcp6_session_remove (se); + return GRUB_ERR_NONE; + } + else + { + grub_dhcp6_options_free (options); + } + + return GRUB_ERR_NONE; +} + static grub_err_t grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)), int argc, char **args) @@ -686,6 +1480,7 @@ grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)), unsigned num; const grub_uint8_t *ptr; grub_uint8_t taglength; + grub_uint8_t len; if (argc < 4) return grub_error (GRUB_ERR_BAD_ARGUMENT, @@ -727,7 +1522,12 @@ grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)), if (grub_strcmp (args[3], "string") == 0) { grub_err_t err = GRUB_ERR_NONE; - char *val = grub_malloc (taglength + 1); + char *val; + + if (grub_add (taglength, 1, &len)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("tag length overflow")); + + val = grub_malloc (len); if (!val) return grub_errno; grub_memcpy (val, ptr, taglength); @@ -760,7 +1560,12 @@ grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)), if (grub_strcmp (args[3], "hex") == 0) { grub_err_t err = GRUB_ERR_NONE; - char *val = grub_malloc (2 * taglength + 1); + char *val; + + if (grub_mul (taglength, 2, &len) || grub_add (len, 1, &len)) + return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("tag length overflow")); + + val = grub_malloc (len); int i; if (!val) return grub_errno; @@ -903,7 +1708,174 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)), return err; } -static grub_command_t cmd_getdhcp, cmd_bootp, cmd_dhcp; +static grub_err_t +grub_cmd_bootp6 (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_net_card *card; + grub_uint32_t iaid = 0; + int interval; + grub_err_t err; + grub_dhcp6_session_t se; + + err = GRUB_ERR_NONE; + + FOR_NET_CARDS (card) + { + struct grub_net_network_level_interface *iface; + + if (argc > 0 && grub_strcmp (card->name, args[0]) != 0) + continue; + + iface = grub_net_ipv6_get_link_local (card, &card->default_address); + if (!iface) + { + grub_dhcp6_session_remove_all (); + return grub_errno; + } + + grub_dhcp6_session_add (iface, iaid++); + } + + for (interval = 200; interval < 10000; interval *= 2) + { + int done = 1; + + FOR_DHCP6_SESSIONS (se) + { + struct grub_net_buff *nb; + struct grub_net_dhcp6_option *opt; + struct grub_net_dhcp6_packet *v6h; + struct grub_net_dhcp6_option_duid_ll *duid; + struct grub_net_dhcp6_option_iana *ia_na; + grub_net_network_level_address_t multicast; + grub_net_link_level_address_t ll_multicast; + struct udphdr *udph; + + multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48); + multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL); + + err = grub_net_link_layer_resolve (se->iface, + &multicast, &ll_multicast); + if (err) + { + grub_dhcp6_session_remove_all (); + return err; + } + + nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); + + if (!nb) + { + grub_dhcp6_session_remove_all (); + return grub_errno; + } + + err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE); + if (err) + { + grub_dhcp6_session_remove_all (); + grub_netbuff_free (nb); + return err; + } + + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t)); + if (err) + { + grub_dhcp6_session_remove_all (); + grub_netbuff_free (nb); + return err; + } + + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ELAPSED_TIME); + opt->len = grub_cpu_to_be16_compile_time (sizeof (grub_uint16_t)); + grub_set_unaligned16 (opt->data, 0); + + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*duid)); + if (err) + { + grub_dhcp6_session_remove_all (); + grub_netbuff_free (nb); + return err; + } + + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_CLIENTID); + opt->len = grub_cpu_to_be16 (sizeof (*duid)); + + duid = (struct grub_net_dhcp6_option_duid_ll *) opt->data; + grub_memcpy (duid, &se->duid, sizeof (*duid)); + + err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*ia_na)); + if (err) + { + grub_dhcp6_session_remove_all (); + grub_netbuff_free (nb); + return err; + } + + opt = (struct grub_net_dhcp6_option *)nb->data; + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA); + opt->len = grub_cpu_to_be16 (sizeof (*ia_na)); + ia_na = (struct grub_net_dhcp6_option_iana *)opt->data; + ia_na->iaid = grub_cpu_to_be32 (se->iaid); + ia_na->t1 = 0; + ia_na->t2 = 0; + + err = grub_netbuff_push (nb, sizeof (*v6h)); + if (err) + { + grub_dhcp6_session_remove_all (); + grub_netbuff_free (nb); + return err; + } + + v6h = (struct grub_net_dhcp6_packet *)nb->data; + v6h->message_type = GRUB_NET_DHCP6_SOLICIT; + v6h->transaction_id = se->transaction_id; + + grub_netbuff_push (nb, sizeof (*udph)); + + udph = (struct udphdr *) nb->data; + udph->src = grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT); + udph->dst = grub_cpu_to_be16_compile_time (DHCP6_SERVER_PORT); + udph->chksum = 0; + udph->len = grub_cpu_to_be16 (nb->tail - nb->data); + + udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP, + &se->iface->address, &multicast); + + err = grub_net_send_ip_packet (se->iface, &multicast, + &ll_multicast, nb, GRUB_NET_IP_UDP); + done = 0; + grub_netbuff_free (nb); + + if (err) + { + grub_dhcp6_session_remove_all (); + return err; + } + } + if (!done) + grub_net_poll_cards (interval, 0); + } + + FOR_DHCP6_SESSIONS (se) + { + grub_error_push (); + err = grub_error (GRUB_ERR_FILE_NOT_FOUND, + N_("couldn't autoconfigure %s"), + se->iface->card->name); + } + + grub_dhcp6_session_remove_all (); + + return err; +} + +static grub_command_t cmd_getdhcp, cmd_bootp, cmd_dhcp, cmd_bootp6; void grub_bootp_init (void) @@ -917,11 +1889,15 @@ grub_bootp_init (void) cmd_getdhcp = grub_register_command ("net_get_dhcp_option", grub_cmd_dhcpopt, N_("VAR INTERFACE NUMBER DESCRIPTION"), N_("retrieve DHCP option and save it into VAR. If VAR is - then print the value.")); + cmd_bootp6 = grub_register_command ("net_bootp6", grub_cmd_bootp6, + N_("[CARD]"), + N_("perform a DHCPv6 autoconfiguration")); } void grub_bootp_fini (void) { + grub_unregister_command (cmd_bootp6); grub_unregister_command (cmd_getdhcp); grub_unregister_command (cmd_bootp); grub_unregister_command (cmd_dhcp); diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c index fcc09aa65d..f20cd6f835 100644 --- a/grub-core/net/dns.c +++ b/grub-core/net/dns.c @@ -224,10 +224,17 @@ get_name (const grub_uint8_t *name_at, const grub_uint8_t *head, { int length; char *ret; + int len; if (!check_name_real (name_at, head, tail, NULL, &length, NULL)) return NULL; - ret = grub_malloc (length + 1); + + if (grub_add (length, 1, &len)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("name length overflow")); + return NULL; + } + ret = grub_malloc (len); if (!ret) return NULL; if (!check_name_real (name_at, head, tail, NULL, NULL, ret)) @@ -463,8 +470,8 @@ grub_net_dns_lookup (const char *name, && grub_get_time_ms () < dns_cache[h].limit_time) { grub_dprintf ("dns", "retrieved from cache\n"); - *addresses = grub_malloc (dns_cache[h].naddresses - * sizeof ((*addresses)[0])); + *addresses = grub_calloc (dns_cache[h].naddresses, + sizeof ((*addresses)[0])); if (!*addresses) return grub_errno; *naddresses = dns_cache[h].naddresses; diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c index 3ea25cf989..4591d0757e 100644 --- a/grub-core/net/drivers/efi/efinet.c +++ b/grub-core/net/drivers/efi/efinet.c @@ -18,17 +18,24 @@ #include #include +#include #include #include #include #include #include +#include +#include +#include +#include GRUB_MOD_LICENSE ("GPLv3+"); /* GUID. */ static grub_guid_t net_io_guid = GRUB_EFI_SIMPLE_NETWORK_GUID; static grub_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID; +static grub_guid_t ip4_config_guid = GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID; +static grub_guid_t ip6_config_guid = GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID; static grub_err_t send_card_buffer (struct grub_net_card *dev, @@ -285,6 +292,9 @@ grub_efinet_findcards (void) /* This should not happen... Why? */ continue; + if (net->mode->hwaddr_size > GRUB_NET_MAX_LINK_ADDRESS_SIZE) + continue; + if (net->mode->state == GRUB_EFI_NETWORK_STOPPED && net->start (net) != GRUB_EFI_SUCCESS) continue; @@ -329,10 +339,11 @@ grub_efinet_findcards (void) * such as LoadFile2 for initrd loading. */ card->flags = GRUB_NET_CARD_NO_CLOSE_ON_FINI_HW; - card->default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + card->default_address.type = net->mode->if_type; + card->default_address.len = net->mode->hwaddr_size; grub_memcpy (card->default_address.mac, net->mode->current_address, - sizeof (card->default_address.mac)); + net->mode->hwaddr_size); card->efi_net = net; card->efi_handle = *handle; @@ -341,12 +352,405 @@ grub_efinet_findcards (void) grub_free (handles); } +static grub_efi_handle_t +grub_efi_locate_device_path (grub_guid_t *protocol, grub_efi_device_path_t *device_path, + grub_efi_device_path_t **r_device_path) +{ + grub_efi_handle_t handle; + grub_efi_status_t status; + + status = grub_efi_system_table->boot_services->locate_device_path( + protocol, &device_path, &handle); + + if (status != GRUB_EFI_SUCCESS) + return 0; + + if (r_device_path) + *r_device_path = device_path; + + return handle; +} + +static grub_efi_ipv4_address_t * +grub_dns_server_ip4_address (grub_efi_device_path_t *dp, grub_efi_uintn_t *num_dns) +{ + grub_efi_handle_t hnd; + grub_efi_status_t status; + grub_efi_ip4_config2_protocol_t *conf; + grub_efi_ipv4_address_t *addrs; + grub_efi_uintn_t data_size = 1 * sizeof (grub_efi_ipv4_address_t); + + hnd = grub_efi_locate_device_path (&ip4_config_guid, dp, NULL); + + if (!hnd) + return 0; + + conf = grub_efi_open_protocol (hnd, &ip4_config_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!conf) + return 0; + + addrs = grub_malloc (data_size); + if (!addrs) + return 0; + + status = conf->get_data(conf, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, + &data_size, addrs); + + if (status == GRUB_EFI_BUFFER_TOO_SMALL) + { + grub_free (addrs); + addrs = grub_malloc (data_size); + if (!addrs) + return 0; + + status = conf->get_data(conf, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, + &data_size, addrs); + } + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (addrs); + return 0; + } + + *num_dns = data_size / sizeof (grub_efi_ipv4_address_t); + return addrs; +} + +static grub_efi_ipv6_address_t * +grub_dns_server_ip6_address (grub_efi_device_path_t *dp, grub_efi_uintn_t *num_dns) +{ + grub_efi_handle_t hnd; + grub_efi_status_t status; + grub_efi_ip6_config_protocol_t *conf; + grub_efi_ipv6_address_t *addrs; + grub_efi_uintn_t data_size = 1 * sizeof (grub_efi_ipv6_address_t); + + hnd = grub_efi_locate_device_path (&ip6_config_guid, dp, NULL); + + if (!hnd) + return 0; + + conf = grub_efi_open_protocol (hnd, &ip6_config_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!conf) + return 0; + + addrs = grub_malloc (data_size); + if (!addrs) + return 0; + + status = conf->get_data(conf, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER, + &data_size, addrs); + + if (status == GRUB_EFI_BUFFER_TOO_SMALL) + { + grub_free (addrs); + addrs = grub_malloc (data_size); + if (!addrs) + return 0; + + status = conf->get_data(conf, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER, + &data_size, addrs); + } + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (addrs); + return 0; + } + + *num_dns = data_size / sizeof (grub_efi_ipv6_address_t); + return addrs; +} + +static struct grub_net_buff * +grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *use_ipv6) +{ + grub_efi_uint16_t uri_len; + grub_efi_device_path_t *ldp, *ddp; + grub_efi_uri_device_path_t *uri_dp; + struct grub_net_buff *nb; + grub_err_t err; + + ddp = grub_efi_duplicate_device_path (dp); + if (!ddp) + return NULL; + + ldp = grub_efi_find_last_device_path (ddp); + + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + || GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE) + { + grub_free (ddp); + return NULL; + } + + uri_len = GRUB_EFI_DEVICE_PATH_LENGTH (ldp) > 4 ? GRUB_EFI_DEVICE_PATH_LENGTH (ldp) - 4 : 0; + + if (!uri_len) + { + grub_free (ddp); + return NULL; + } + + uri_dp = (grub_efi_uri_device_path_t *) ldp; + + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + + ldp = grub_efi_find_last_device_path (ddp); + + /* Skip the DNS Device */ + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE) + { + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + + ldp = grub_efi_find_last_device_path (ddp); + } + + if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)) + { + grub_free (ddp); + return NULL; + } + + nb = grub_netbuff_alloc (512); + if (!nb) + { + grub_free (ddp); + return NULL; + } + + if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE) + { + grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) ldp; + struct grub_net_bootp_packet *bp; + grub_uint8_t *ptr; + grub_efi_ipv4_address_t *dns; + grub_efi_uintn_t num_dns; + + bp = (struct grub_net_bootp_packet *) nb->tail; + err = grub_netbuff_put (nb, sizeof (*bp) + 4); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + + if (sizeof(bp->boot_file) < uri_len) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + grub_memcpy (bp->boot_file, uri_dp->uri, uri_len); + grub_memcpy (&bp->your_ip, ipv4->local_ip_address, sizeof (bp->your_ip)); + grub_memcpy (&bp->server_ip, ipv4->remote_ip_address, sizeof (bp->server_ip)); + + bp->vendor[0] = GRUB_NET_BOOTP_RFC1048_MAGIC_0; + bp->vendor[1] = GRUB_NET_BOOTP_RFC1048_MAGIC_1; + bp->vendor[2] = GRUB_NET_BOOTP_RFC1048_MAGIC_2; + bp->vendor[3] = GRUB_NET_BOOTP_RFC1048_MAGIC_3; + + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof (ipv4->subnet_mask) + 2); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr++ = GRUB_NET_BOOTP_NETMASK; + *ptr++ = sizeof (ipv4->subnet_mask); + grub_memcpy (ptr, ipv4->subnet_mask, sizeof (ipv4->subnet_mask)); + + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof (ipv4->gateway_ip_address) + 2); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr++ = GRUB_NET_BOOTP_ROUTER; + *ptr++ = sizeof (ipv4->gateway_ip_address); + grub_memcpy (ptr, ipv4->gateway_ip_address, sizeof (ipv4->gateway_ip_address)); + + ptr = nb->tail; + err = grub_netbuff_put (nb, sizeof ("HTTPClient") + 1); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr++ = GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER; + *ptr++ = sizeof ("HTTPClient") - 1; + grub_memcpy (ptr, "HTTPClient", sizeof ("HTTPClient") - 1); + + dns = grub_dns_server_ip4_address (dp, &num_dns); + if (dns) + { + grub_efi_uintn_t size_dns = sizeof (*dns) * num_dns; + + ptr = nb->tail; + err = grub_netbuff_put (nb, size_dns + 2); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr++ = GRUB_NET_BOOTP_DNS; + *ptr++ = size_dns; + grub_memcpy (ptr, dns, size_dns); + grub_free (dns); + } + + ptr = nb->tail; + err = grub_netbuff_put (nb, 1); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + *ptr = GRUB_NET_BOOTP_END; + *use_ipv6 = 0; + + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + ldp = grub_efi_find_last_device_path (ddp); + + if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE) + { + grub_efi_mac_address_device_path_t *mac = (grub_efi_mac_address_device_path_t *) ldp; + bp->hw_type = mac->if_type; + bp->hw_len = sizeof (bp->mac_addr); + grub_memcpy (bp->mac_addr, mac->mac_address, bp->hw_len); + } + } + else + { + grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) ldp; + + struct grub_net_dhcp6_packet *d6p; + struct grub_net_dhcp6_option *opt; + struct grub_net_dhcp6_option_iana *iana; + struct grub_net_dhcp6_option_iaaddr *iaaddr; + grub_efi_ipv6_address_t *dns; + grub_efi_uintn_t num_dns; + + d6p = (struct grub_net_dhcp6_packet *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*d6p)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + d6p->message_type = GRUB_NET_DHCP6_REPLY; + + opt = (struct grub_net_dhcp6_option *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*opt)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA); + opt->len = grub_cpu_to_be16_compile_time (sizeof(*iana) + sizeof(*opt) + sizeof(*iaaddr)); + + err = grub_netbuff_put (nb, sizeof(*iana)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + + opt = (struct grub_net_dhcp6_option *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*opt)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR); + opt->len = grub_cpu_to_be16_compile_time (sizeof (*iaaddr)); + + iaaddr = (struct grub_net_dhcp6_option_iaaddr *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*iaaddr)); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + grub_memcpy (iaaddr->addr, ipv6->local_ip_address, sizeof(ipv6->local_ip_address)); + + opt = (struct grub_net_dhcp6_option *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*opt) + uri_len); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_BOOTFILE_URL); + opt->len = grub_cpu_to_be16 (uri_len); + grub_memcpy (opt->data, uri_dp->uri, uri_len); + + dns = grub_dns_server_ip6_address (dp, &num_dns); + if (dns) + { + grub_efi_uintn_t size_dns = sizeof (*dns) * num_dns; + + opt = (struct grub_net_dhcp6_option *)nb->tail; + err = grub_netbuff_put (nb, sizeof(*opt) + size_dns); + if (err) + { + grub_free (ddp); + grub_netbuff_free (nb); + return NULL; + } + opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_DNS_SERVERS); + opt->len = grub_cpu_to_be16 (size_dns); + grub_memcpy (opt->data, dns, size_dns); + grub_free (dns); + } + + *use_ipv6 = 1; + } + + grub_free (ddp); + return nb; +} + static void grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, char **path) { struct grub_net_card *card; - grub_efi_device_path_t *dp; + grub_efi_device_path_t *dp, *ldp = NULL; struct grub_net_network_level_interface *inter; grub_efi_device_path_t *vlan_dp; grub_efi_uint16_t vlan_dp_len; @@ -360,15 +764,24 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, { grub_efi_device_path_t *cdp; struct grub_efi_pxe *pxe; - struct grub_efi_pxe_mode *pxe_mode; + struct grub_efi_pxe_mode *pxe_mode = NULL; + grub_uint8_t *packet_buf; + grub_size_t packet_bufsz ; + int ipv6; + struct grub_net_buff *nb = NULL; + if (card->driver != &efidriver) continue; + cdp = grub_efi_get_device_path (card->efi_handle); if (! cdp) continue; + + ldp = grub_efi_find_last_device_path (dp); + if (grub_efi_compare_device_paths (dp, cdp) != 0) { - grub_efi_device_path_t *ldp, *dup_dp, *dup_ldp; + grub_efi_device_path_t *dup_dp, *dup_ldp; int match; /* EDK2 UEFI PXE driver creates pseudo devices with type IPv4/IPv6 @@ -377,14 +790,33 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, devices. We skip them when enumerating cards, so here we need to find matching MAC device. */ - ldp = grub_efi_find_last_device_path (dp); if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE - && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)) + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE + && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)) continue; dup_dp = grub_efi_duplicate_device_path (dp); if (!dup_dp) continue; + + if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE) + { + dup_ldp = grub_efi_find_last_device_path (dup_dp); + dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + dup_ldp->length = sizeof (*dup_ldp); + } + + dup_ldp = grub_efi_find_last_device_path (dup_dp); + if (GRUB_EFI_DEVICE_PATH_SUBTYPE (dup_ldp) == GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE) + { + dup_ldp = grub_efi_find_last_device_path (dup_dp); + dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + dup_ldp->length = sizeof (*dup_ldp); + } + dup_ldp = grub_efi_find_last_device_path (dup_dp); dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; @@ -394,46 +826,114 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, if (!match) continue; } + pxe = grub_efi_open_protocol (hnd, &pxe_io_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); - if (! pxe) - continue; - pxe_mode = pxe->mode; - - inter = grub_net_configure_by_dhcp_ack (card->name, card, 0, - (struct grub_net_bootp_packet *) - &pxe_mode->dhcp_ack, - sizeof (pxe_mode->dhcp_ack), - 1, device, path); - - if (inter != NULL) + if (!pxe) { - /* - * Search the device path for any VLAN subtype and use it - * to configure the interface. - */ - vlan_dp = dp; + nb = grub_efinet_create_dhcp_ack_from_device_path (dp, &ipv6); + if (!nb) + { + grub_print_error (); + continue; + } + packet_buf = nb->head; + packet_bufsz = nb->tail - nb->head; + } + else + { + pxe_mode = pxe->mode; + packet_buf = (grub_uint8_t *) &pxe_mode->dhcp_ack; + packet_bufsz = sizeof (pxe_mode->dhcp_ack); + ipv6 = pxe_mode->using_ipv6; + } - while (!GRUB_EFI_END_ENTIRE_DEVICE_PATH (vlan_dp)) + if (ipv6) + { + grub_dprintf ("efinet", "using ipv6 and dhcpv6\n"); + if (pxe_mode) + grub_dprintf ("efinet", "dhcp_ack_received: %s%s\n", + pxe_mode->dhcp_ack_received ? "yes" : "no", + pxe_mode->dhcp_ack_received ? "" : " cannot continue"); + + inter = grub_net_configure_by_dhcpv6_reply (card->name, card, 0, + (struct grub_net_dhcp6_packet *) + packet_buf, + packet_bufsz, + 1, device, path); + if (grub_errno) + grub_print_error (); + if (inter && device && path) + grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path); + if (grub_errno) + grub_print_error (); + } + else + { + grub_dprintf ("efinet", "using ipv4 and dhcp\n"); + + struct grub_net_bootp_packet *dhcp_ack = (struct grub_net_bootp_packet *) &pxe_mode->dhcp_ack; + + if (pxe_mode->proxy_offer_received) + { + grub_dprintf ("efinet", "proxy offer receive"); + struct grub_net_bootp_packet *proxy_offer = (struct grub_net_bootp_packet *) &pxe_mode->proxy_offer; + + if (proxy_offer && dhcp_ack->boot_file[0] == '\0') + { + grub_dprintf ("efinet", "setting values from proxy offer"); + /* Here we got a proxy offer and the dhcp_ack has a nil boot_file + * Copy the proxy DHCP offer details into the bootp_packet we are + * sending forward as they are the deatils we need. + */ + *dhcp_ack->server_name = *proxy_offer->server_name; + *dhcp_ack->boot_file = *proxy_offer->boot_file; + dhcp_ack->server_ip = proxy_offer->server_ip; + } + } + + inter = grub_net_configure_by_dhcp_ack (card->name, card, 0, + (struct grub_net_bootp_packet *) + packet_buf, + packet_bufsz, + 1, device, path); + grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path); + if (inter != NULL) { - if (GRUB_EFI_DEVICE_PATH_TYPE (vlan_dp) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE && - GRUB_EFI_DEVICE_PATH_SUBTYPE (vlan_dp) == GRUB_EFI_VLAN_DEVICE_PATH_SUBTYPE) + /* + * Search the device path for any VLAN subtype and use it + * to configure the interface. + */ + vlan_dp = dp; + + while (!GRUB_EFI_END_ENTIRE_DEVICE_PATH (vlan_dp)) { - vlan = (grub_efi_vlan_device_path_t *) vlan_dp; - inter->vlantag = vlan->vlan_id; - break; + if (GRUB_EFI_DEVICE_PATH_TYPE (vlan_dp) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE && + GRUB_EFI_DEVICE_PATH_SUBTYPE (vlan_dp) == GRUB_EFI_VLAN_DEVICE_PATH_SUBTYPE) + { + vlan = (grub_efi_vlan_device_path_t *) vlan_dp; + inter->vlantag = vlan->vlan_id; + break; + } + + vlan_dp_len = GRUB_EFI_DEVICE_PATH_LENGTH (vlan_dp); + vlan_dp = (grub_efi_device_path_t *) ((grub_efi_uint8_t *) vlan_dp + vlan_dp_len); } - - vlan_dp_len = GRUB_EFI_DEVICE_PATH_LENGTH (vlan_dp); - vlan_dp = (grub_efi_device_path_t *) ((grub_efi_uint8_t *) vlan_dp + vlan_dp_len); } } + + if (nb) + grub_netbuff_free (nb); + return; } } GRUB_MOD_INIT(efinet) { + if (grub_efi_net_config) + return; + grub_efinet_findcards (); grub_efi_net_config = grub_efi_net_config_real; } @@ -445,5 +945,7 @@ GRUB_MOD_FINI(efinet) FOR_NET_CARDS_SAFE (card, next) if (card->driver == &efidriver) grub_net_card_unregister (card); + + grub_efi_net_config = NULL; } diff --git a/grub-core/net/drivers/emu/emunet.c b/grub-core/net/drivers/emu/emunet.c index 5f311d40f4..7461d13bbc 100644 --- a/grub-core/net/drivers/emu/emunet.c +++ b/grub-core/net/drivers/emu/emunet.c @@ -46,6 +46,7 @@ static struct grub_net_card emucard = .mtu = 1500, .default_address = { .type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET, + . len = 6, {.mac = {0, 1, 2, 3, 4, 5}} }, .flags = 0 diff --git a/grub-core/net/drivers/i386/pc/pxe.c b/grub-core/net/drivers/i386/pc/pxe.c index db17186ee7..816d1d0eed 100644 --- a/grub-core/net/drivers/i386/pc/pxe.c +++ b/grub-core/net/drivers/i386/pc/pxe.c @@ -386,20 +386,21 @@ GRUB_MOD_INIT(pxe) grub_memset (ui, 0, sizeof (*ui)); grub_pxe_call (GRUB_PXENV_UNDI_GET_INFORMATION, ui, pxe_rm_entry); + grub_pxe_card.default_address.len = 6; grub_memcpy (grub_pxe_card.default_address.mac, ui->current_addr, - sizeof (grub_pxe_card.default_address.mac)); - for (i = 0; i < sizeof (grub_pxe_card.default_address.mac); i++) + grub_pxe_card.default_address.len); + for (i = 0; i < grub_pxe_card.default_address.len; i++) if (grub_pxe_card.default_address.mac[i] != 0) break; - if (i != sizeof (grub_pxe_card.default_address.mac)) + if (i != grub_pxe_card.default_address.len) { - for (i = 0; i < sizeof (grub_pxe_card.default_address.mac); i++) + for (i = 0; i < grub_pxe_card.default_address.len; i++) if (grub_pxe_card.default_address.mac[i] != 0xff) break; } - if (i == sizeof (grub_pxe_card.default_address.mac)) + if (i == grub_pxe_card.default_address.len) grub_memcpy (grub_pxe_card.default_address.mac, ui->permanent_addr, - sizeof (grub_pxe_card.default_address.mac)); + grub_pxe_card.default_address.len); grub_pxe_card.mtu = ui->mtu; grub_pxe_card.default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; diff --git a/grub-core/net/drivers/ieee1275/ofnet.c b/grub-core/net/drivers/ieee1275/ofnet.c index 78f03df8e2..9b045ce70a 100644 --- a/grub-core/net/drivers/ieee1275/ofnet.c +++ b/grub-core/net/drivers/ieee1275/ofnet.c @@ -22,6 +22,7 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); @@ -154,12 +155,13 @@ grub_ieee1275_parse_bootpath (const char *devpath, char *bootpath, char *equal_char = 0; grub_size_t field_counter = 0; grub_net_network_level_address_t client_addr = {0, {0}, 0}, gateway_addr = {0, {0}, 0}, subnet_mask = {0, {0}, 0}; - grub_net_link_level_address_t hw_addr = {0, {{0, 0, 0, 0, 0, 0}}}; + grub_net_link_level_address_t hw_addr = {}; grub_net_interface_flags_t flags = 0; struct grub_net_network_level_interface *inter = NULL; grub_uint16_t vlantag = 0; hw_addr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + hw_addr.len = 6; args = bootpath + grub_strlen (devpath) + 1; do @@ -391,6 +393,7 @@ search_net_devices (struct grub_ieee1275_devalias *alias) grub_uint8_t *pprop; char *shortname; char need_suffix = 1; + grub_size_t sz; if (grub_strcmp (alias->type, "network") != 0) return 0; @@ -448,9 +451,26 @@ search_net_devices (struct grub_ieee1275_devalias *alias) } if (need_suffix) - ofdata->path = grub_malloc (grub_strlen (alias->path) + sizeof (SUFFIX)); + { + if (grub_add (grub_strlen (alias->path), sizeof (SUFFIX), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obatining size of ofdata path")); + grub_print_error (); + return 0; + } + } else - ofdata->path = grub_malloc (grub_strlen (alias->path) + 1); + { + if (grub_add (grub_strlen (alias->path), 1, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obatining size of ofdata path")); + grub_print_error (); + return 0; + } + } + + ofdata->path = grub_malloc(sz); + if (!ofdata->path) { grub_print_error (); @@ -491,6 +511,7 @@ search_net_devices (struct grub_ieee1275_devalias *alias) grub_memcpy (&lla.mac, pprop, 6); lla.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + lla.len = 6; card->default_address = lla; card->txbufsize = ALIGN_UP (card->mtu, 64) + 256; diff --git a/grub-core/net/drivers/uboot/ubootnet.c b/grub-core/net/drivers/uboot/ubootnet.c index b9d5a0cd49..9f3354a1b7 100644 --- a/grub-core/net/drivers/uboot/ubootnet.c +++ b/grub-core/net/drivers/uboot/ubootnet.c @@ -131,6 +131,7 @@ GRUB_MOD_INIT (ubootnet) grub_memcpy (&(card->default_address.mac), &devinfo->di_net.hwaddr, 6); card->default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + card->default_address.len = 6; card->txbufsize = ALIGN_UP (card->mtu, 64) + 256; card->txbuf = grub_zalloc (card->txbufsize); diff --git a/grub-core/net/efi/dhcp.c b/grub-core/net/efi/dhcp.c new file mode 100644 index 0000000000..6cc0dad85a --- /dev/null +++ b/grub-core/net/efi/dhcp.c @@ -0,0 +1,400 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifdef GRUB_EFI_NET_DEBUG +static void +dhcp4_mode_print (grub_efi_dhcp4_mode_data_t *mode) +{ + switch (mode->state) + { + case GRUB_EFI_DHCP4_STOPPED: + grub_printf ("STATE: STOPPED\n"); + break; + case GRUB_EFI_DHCP4_INIT: + grub_printf ("STATE: INIT\n"); + break; + case GRUB_EFI_DHCP4_SELECTING: + grub_printf ("STATE: SELECTING\n"); + break; + case GRUB_EFI_DHCP4_REQUESTING: + grub_printf ("STATE: REQUESTING\n"); + break; + case GRUB_EFI_DHCP4_BOUND: + grub_printf ("STATE: BOUND\n"); + break; + case GRUB_EFI_DHCP4_RENEWING: + grub_printf ("STATE: RENEWING\n"); + break; + case GRUB_EFI_DHCP4_REBINDING: + grub_printf ("STATE: REBINDING\n"); + break; + case GRUB_EFI_DHCP4_INIT_REBOOT: + grub_printf ("STATE: INIT_REBOOT\n"); + break; + case GRUB_EFI_DHCP4_REBOOTING: + grub_printf ("STATE: REBOOTING\n"); + break; + default: + grub_printf ("STATE: UNKNOWN\n"); + break; + } + + grub_printf ("CLIENT_ADDRESS: %u.%u.%u.%u\n", + mode->client_address[0], + mode->client_address[1], + mode->client_address[2], + mode->client_address[3]); + grub_printf ("SERVER_ADDRESS: %u.%u.%u.%u\n", + mode->server_address[0], + mode->server_address[1], + mode->server_address[2], + mode->server_address[3]); + grub_printf ("SUBNET_MASK: %u.%u.%u.%u\n", + mode->subnet_mask[0], + mode->subnet_mask[1], + mode->subnet_mask[2], + mode->subnet_mask[3]); + grub_printf ("ROUTER_ADDRESS: %u.%u.%u.%u\n", + mode->router_address[0], + mode->router_address[1], + mode->router_address[2], + mode->router_address[3]); +} +#endif + +static grub_efi_ipv4_address_t * +grub_efi_dhcp4_parse_dns (grub_efi_dhcp4_protocol_t *dhcp4, grub_efi_dhcp4_packet_t *reply_packet) +{ + grub_efi_dhcp4_packet_option_t **option_list; + grub_efi_status_t status; + grub_efi_uint32_t option_count = 0; + grub_efi_uint32_t i; + + status = dhcp4->parse(dhcp4, reply_packet, &option_count, NULL); + + if (status != GRUB_EFI_BUFFER_TOO_SMALL) + return NULL; + + option_list = grub_calloc (option_count, sizeof(*option_list)); + if (!option_list) + return NULL; + + status = dhcp4->parse(dhcp4, reply_packet, &option_count, option_list); + if (status != GRUB_EFI_SUCCESS) + { + grub_free (option_list); + return NULL; + } + + for (i = 0; i < option_count; ++i) + { + if (option_list[i]->op_code == 6) + { + grub_efi_ipv4_address_t *dns_address; + + if (((option_list[i]->length & 0x3) != 0) || (option_list[i]->length == 0)) + continue; + + /* We only contact primary dns */ + dns_address = grub_malloc (sizeof (*dns_address)); + if (!dns_address) + { + grub_free (option_list); + return NULL; + } + grub_memcpy (dns_address, option_list[i]->data, sizeof (dns_address)); + grub_free (option_list); + return dns_address; + } + } + + grub_free (option_list); + return NULL; +} + +#if 0 +/* Somehow this doesn't work ... */ +static grub_err_t +grub_cmd_efi_bootp (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct grub_efi_net_device *dev; + for (dev = net_devices; dev; dev = dev->next) + { + grub_efi_pxe_t *pxe = dev->ip4_pxe; + grub_efi_pxe_mode_t *mode = pxe->mode; + grub_efi_status_t status; + + if (!mode->started) + { + status = pxe->start(pxe, 0); + + if (status != GRUB_EFI_SUCCESS) + grub_printf ("Couldn't start PXE\n"); + } + + status = pxe->dhcp(pxe, 0); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("dhcp4 configure failed, %d\n", (int)status); + continue; + } + + dev->prefer_ip6 = 0; + } + + return GRUB_ERR_NONE; +} +#endif + +static grub_err_t +grub_cmd_efi_bootp (struct grub_command *cmd __attribute__ ((unused)), + int argc, + char **args) +{ + struct grub_efi_net_device *netdev; + + for (netdev = net_devices; netdev; netdev = netdev->next) + { + grub_efi_status_t status; + grub_efi_dhcp4_mode_data_t mode; + grub_efi_dhcp4_config_data_t config; + grub_efi_dhcp4_packet_option_t *options; + grub_efi_ipv4_address_t *dns_address; + grub_efi_net_ip_manual_address_t net_ip; + grub_efi_net_ip_address_t ip_addr; + grub_efi_net_interface_t *inf = NULL; + + if (argc > 0 && grub_strcmp (netdev->card_name, args[0]) != 0) + continue; + + grub_memset (&config, 0, sizeof(config)); + + config.option_count = 1; + options = grub_malloc (sizeof(*options) + 2); + /* Parameter request list */ + options->op_code = 55; + options->length = 3; + /* subnet mask */ + options->data[0] = 1; + /* router */ + options->data[1] = 3; + /* DNS */ + options->data[2] = 6; + config.option_list = &options; + + /* FIXME: What if the dhcp has bounded */ + status = netdev->dhcp4->configure(netdev->dhcp4, &config); + grub_free (options); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("dhcp4 configure failed, %d\n", (int)status); + continue; + } + + status = netdev->dhcp4->start(netdev->dhcp4, NULL); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("dhcp4 start failed, %d\n", (int)status); + continue; + } + + status = netdev->dhcp4->get_mode_data(netdev->dhcp4, &mode); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("dhcp4 get mode failed, %d\n", (int)status); + continue; + } + +#ifdef GRUB_EFI_NET_DEBUG + dhcp4_mode_print (&mode); +#endif + + for (inf = netdev->net_interfaces; inf; inf = inf->next) + if (inf->prefer_ip6 == 0) + break; + + grub_memcpy (net_ip.ip4.address, mode.client_address, sizeof (net_ip.ip4.address)); + grub_memcpy (net_ip.ip4.subnet_mask, mode.subnet_mask, sizeof (net_ip.ip4.subnet_mask)); + + if (!inf) + { + char *name = grub_xasprintf ("%s:dhcp", netdev->card_name); + + net_ip.is_ip6 = 0; + inf = grub_efi_net_create_interface (netdev, + name, + &net_ip, + 1); + grub_free (name); + } + else + { + efi_net_interface_set_address (inf, &net_ip, 1); + } + + grub_memcpy (ip_addr.ip4, mode.router_address, sizeof (ip_addr.ip4)); + efi_net_interface_set_gateway (inf, &ip_addr); + + dns_address = grub_efi_dhcp4_parse_dns (netdev->dhcp4, mode.reply_packet); + if (dns_address) + efi_net_interface_set_dns (inf, (grub_efi_net_ip_address_t *)&dns_address); + + } + + return GRUB_ERR_NONE; +} + + +static grub_err_t +grub_cmd_efi_bootp6 (struct grub_command *cmd __attribute__ ((unused)), + int argc, + char **args) +{ + struct grub_efi_net_device *dev; + grub_efi_uint32_t ia_id; + + for (dev = net_devices, ia_id = 0; dev; dev = dev->next, ia_id++) + { + grub_efi_dhcp6_config_data_t config; + grub_efi_dhcp6_packet_option_t *option_list[1]; + grub_efi_dhcp6_packet_option_t *opt; + grub_efi_status_t status; + grub_efi_dhcp6_mode_data_t mode; + grub_efi_dhcp6_retransmission_t retrans; + grub_efi_net_ip_manual_address_t net_ip; + grub_efi_boot_services_t *b = grub_efi_system_table->boot_services; + grub_efi_net_interface_t *inf = NULL; + + if (argc > 0 && grub_strcmp (dev->card_name, args[0]) != 0) + continue; + + opt = grub_malloc (sizeof(*opt) + 2 * sizeof (grub_efi_uint16_t)); + +#define GRUB_EFI_DHCP6_OPT_ORO 6 + + opt->op_code = grub_cpu_to_be16_compile_time (GRUB_EFI_DHCP6_OPT_ORO); + opt->op_len = grub_cpu_to_be16_compile_time (2 * sizeof (grub_efi_uint16_t)); + +#define GRUB_EFI_DHCP6_OPT_BOOT_FILE_URL 59 +#define GRUB_EFI_DHCP6_OPT_DNS_SERVERS 23 + + grub_set_unaligned16 (opt->data, grub_cpu_to_be16_compile_time(GRUB_EFI_DHCP6_OPT_BOOT_FILE_URL)); + grub_set_unaligned16 (opt->data + 1 * sizeof (grub_efi_uint16_t), + grub_cpu_to_be16_compile_time(GRUB_EFI_DHCP6_OPT_DNS_SERVERS)); + + option_list[0] = opt; + retrans.irt = 4; + retrans.mrc = 4; + retrans.mrt = 32; + retrans.mrd = 60; + + config.dhcp6_callback = NULL; + config.callback_context = NULL; + config.option_count = 1; + config.option_list = option_list; + config.ia_descriptor.ia_id = ia_id; + config.ia_descriptor.type = GRUB_EFI_DHCP6_IA_TYPE_NA; + config.ia_info_event = NULL; + config.reconfigure_accept = 0; + config.rapid_commit = 0; + config.solicit_retransmission = &retrans; + + status = dev->dhcp6->configure(dev->dhcp6, &config); + grub_free (opt); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("dhcp6 configure failed, %d\n", (int)status); + continue; + } + status = dev->dhcp6->start(dev->dhcp6); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("dhcp6 start failed, %d\n", (int)status); + continue; + } + + status = dev->dhcp6->get_mode_data(dev->dhcp6, &mode, NULL); + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("dhcp4 get mode failed, %d\n", (int)status); + continue; + } + + for (inf = dev->net_interfaces; inf; inf = inf->next) + if (inf->prefer_ip6 == 1) + break; + + grub_memcpy (net_ip.ip6.address, mode.ia->ia_address[0].ip_address, sizeof (net_ip.ip6.address)); + net_ip.ip6.prefix_length = 64; + net_ip.ip6.is_anycast = 0; + net_ip.is_ip6 = 1; + + if (!inf) + { + char *name = grub_xasprintf ("%s:dhcp", dev->card_name); + + inf = grub_efi_net_create_interface (dev, + name, + &net_ip, + 1); + grub_free (name); + } + else + { + efi_net_interface_set_address (inf, &net_ip, 1); + } + + { + grub_efi_uint32_t count = 0; + grub_efi_dhcp6_packet_option_t **options = NULL; + grub_efi_uint32_t i; + + status = dev->dhcp6->parse(dev->dhcp6, mode.ia->reply_packet, &count, NULL); + + if (status == GRUB_EFI_BUFFER_TOO_SMALL && count) + { + options = grub_calloc (count, sizeof(*options)); + if (options) + status = dev->dhcp6->parse(dev->dhcp6, mode.ia->reply_packet, &count, options); + else + status = GRUB_EFI_OUT_OF_RESOURCES; + } + + if (status != GRUB_EFI_SUCCESS) + { + if (options) + grub_free (options); + continue; + } + + for (i = 0; i < count; ++i) + { + if (options[i]->op_code == grub_cpu_to_be16_compile_time(GRUB_EFI_DHCP6_OPT_DNS_SERVERS)) + { + grub_efi_net_ip_address_t dns; + grub_memcpy (dns.ip6, options[i]->data, sizeof(net_ip.ip6)); + efi_net_interface_set_dns (inf, &dns); + break; + } + } + + if (options) + grub_free (options); + } + + b->free_pool(mode.client_id); + b->free_pool(mode.ia); + } + + return GRUB_ERR_NONE; +} + +grub_command_func_t grub_efi_net_bootp = grub_cmd_efi_bootp; +grub_command_func_t grub_efi_net_bootp6 = grub_cmd_efi_bootp6; diff --git a/grub-core/net/efi/efi_netfs.c b/grub-core/net/efi/efi_netfs.c new file mode 100644 index 0000000000..ef371d885e --- /dev/null +++ b/grub-core/net/efi/efi_netfs.c @@ -0,0 +1,57 @@ +#include +#include +#define EFI_NET_CMD_PREFIX "net_efi" +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +static grub_command_t cmd_efi_lsroutes; +static grub_command_t cmd_efi_lscards; +static grub_command_t cmd_efi_lsaddrs; +static grub_command_t cmd_efi_addaddr; +static grub_command_t cmd_efi_bootp; +static grub_command_t cmd_efi_bootp6; + +static int initialized; + +GRUB_MOD_INIT(efi_netfs) +{ + if (grub_net_open) + return; + + if (grub_efi_net_fs_init ()) + { + cmd_efi_lsroutes = grub_register_command ("net_efi_ls_routes", grub_efi_net_list_routes, + "", N_("list network routes")); + cmd_efi_lscards = grub_register_command ("net_efi_ls_cards", grub_efi_net_list_cards, + "", N_("list network cards")); + cmd_efi_lsaddrs = grub_register_command ("net_efi_ls_addr", grub_efi_net_list_addrs, + "", N_("list network addresses")); + cmd_efi_addaddr = grub_register_command ("net_efi_add_addr", grub_efi_net_add_addr, + N_("SHORTNAME CARD ADDRESS [HWADDRESS]"), + N_("Add a network address.")); + cmd_efi_bootp = grub_register_command ("net_efi_bootp", grub_efi_net_bootp, + N_("[CARD]"), + N_("perform a bootp autoconfiguration")); + cmd_efi_bootp6 = grub_register_command ("net_efi_bootp6", grub_efi_net_bootp6, + N_("[CARD]"), + N_("perform a bootp autoconfiguration")); + initialized = 1; + } +} + +GRUB_MOD_FINI(efi_netfs) +{ + if (initialized) + { + grub_unregister_command (cmd_efi_lsroutes); + grub_unregister_command (cmd_efi_lscards); + grub_unregister_command (cmd_efi_lsaddrs); + grub_unregister_command (cmd_efi_addaddr); + grub_unregister_command (cmd_efi_bootp); + grub_unregister_command (cmd_efi_bootp6); + grub_efi_net_fs_fini (); + initialized = 0; + return; + } +} diff --git a/grub-core/net/efi/http.c b/grub-core/net/efi/http.c new file mode 100644 index 0000000000..5bdb47548f --- /dev/null +++ b/grub-core/net/efi/http.c @@ -0,0 +1,494 @@ + +#include +#include +#include +#include +#include +#include + +static void +http_configure (struct grub_efi_net_device *dev, int prefer_ip6) +{ + grub_efi_ipv6_address_t address; + grub_efi_http_config_data_t http_config; + grub_efi_httpv4_access_point_t httpv4_node; + grub_efi_httpv6_access_point_t httpv6_node; + grub_efi_status_t status; + int https; + char *http_url; + const char *rest, *http_server, *http_path = NULL; + + http_server = grub_env_get ("root"); + https = (grub_strncmp (http_server, "https", 5) == 0) ? 1 : 0; + + /* extract http server + port */ + if (http_server) + { + http_server = grub_strchr (http_server, ','); + if (http_server) + http_server++; + } + + /* fw_path is like (http,192.168.1.1:8000)/httpboot, extract path part */ + http_path = grub_env_get ("fw_path"); + if (http_path) + { + http_path = grub_strchr (http_path, ')'); + if (http_path) + { + http_path++; + grub_env_unset ("http_path"); + grub_env_set ("http_path", http_path); + grub_env_export ("http_path"); + } + } + + if (http_server && http_path) + { + if (grub_efi_string_to_ip6_address (http_server, &address, &rest) && *rest == 0) + http_url = grub_xasprintf ("%s://[%s]%s", https ? "https" : "http", http_server, http_path); + else + http_url = grub_xasprintf ("%s://%s%s", https ? "https" : "http", http_server, http_path); + if (http_url) + { + grub_env_unset ("http_url"); + grub_env_set ("http_url", http_url); + grub_free (http_url); + } + } + + grub_efi_http_t *http = dev->http; + + grub_memset (&http_config, 0, sizeof(http_config)); + http_config.http_version = GRUB_EFI_HTTPVERSION11; + http_config.timeout_millisec = 5000; + + if (prefer_ip6) + { + grub_efi_uintn_t sz; + grub_efi_ip6_config_manual_address_t manual_address; + + http_config.local_address_is_ipv6 = 1; + sz = sizeof (manual_address); + status = dev->ip6_config->get_data(dev->ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, + &sz, &manual_address); + + if (status == GRUB_EFI_NOT_FOUND) + { + grub_printf ("The MANUAL ADDRESS is not found\n"); + } + + /* FIXME: The manual interface would return BUFFER TOO SMALL !!! */ + if (status != GRUB_EFI_SUCCESS) + { + grub_printf ("??? %d\n",(int) status); + return; + } + + grub_memcpy (httpv6_node.local_address, manual_address.address, sizeof (httpv6_node.local_address)); + httpv6_node.local_port = 0; + http_config.access_point.ipv6_node = &httpv6_node; + } + else + { + http_config.local_address_is_ipv6 = 0; + grub_memset (&httpv4_node, 0, sizeof(httpv4_node)); + httpv4_node.use_default_address = 1; + + /* Use random port here */ + /* See TcpBind() in edk2/NetworkPkg/TcpDxe/TcpDispatcher.c */ + httpv4_node.local_port = 0; + http_config.access_point.ipv4_node = &httpv4_node; + } + + status = http->configure(http, &http_config); + + if (status == GRUB_EFI_ALREADY_STARTED) + { + /* XXX: This hangs HTTPS boot */ +#if 0 + if (http->configure(http, NULL) != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_IO, N_("couldn't reset http instance")); + grub_print_error (); + return; + } + status = http->configure(http, &http_config); +#endif + return; + } + + if (status != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_IO, N_("couldn't configure http protocol, reason: %d"), (int)status); + grub_print_error (); + return ; + } +} + +static grub_efi_boolean_t request_callback_done; +static grub_efi_boolean_t response_callback_done; + +static void +__grub_efi_api grub_efi_http_request_callback (grub_efi_event_t event __attribute__ ((unused)), + void *context __attribute__ ((unused))) +{ + request_callback_done = 1; +} + +static void +__grub_efi_api grub_efi_http_response_callback (grub_efi_event_t event __attribute__ ((unused)), + void *context __attribute__ ((unused))) +{ + response_callback_done = 1; +} + +static grub_err_t +efihttp_request (grub_efi_http_t *http, char *server, char *name, int use_https, int headeronly, grub_off_t *file_size) +{ + grub_efi_http_request_data_t request_data; + grub_efi_http_message_t request_message; + grub_efi_http_token_t request_token; + grub_efi_http_response_data_t response_data; + grub_efi_http_message_t response_message; + grub_efi_http_token_t response_token; + grub_efi_http_header_t request_headers[3]; + + grub_efi_status_t status; + grub_efi_boot_services_t *b = grub_efi_system_table->boot_services; + char *url = NULL; + char *hostname = NULL; + + { + grub_efi_ipv6_address_t address; + const char *rest; + grub_efi_char16_t *ucs2_url; + grub_size_t url_len, ucs2_url_len; + const char *protocol = (use_https == 1) ? "https" : "http"; + + if (grub_efi_string_to_ip6_address (server, &address, &rest) && *rest == 0) + { + hostname = grub_xasprintf ("[%s]", server); + if (!hostname) + return GRUB_ERR_OUT_OF_MEMORY; + + server = hostname; + + url = grub_xasprintf ("%s://%s%s", protocol, server, name); + if (!url) + { + grub_free (hostname); + return GRUB_ERR_OUT_OF_MEMORY; + } + } + else + { + url = grub_xasprintf ("%s://%s%s", protocol, server, name); + } + + if (!url) + { + return grub_errno; + } + + url_len = grub_strlen (url); + ucs2_url_len = url_len * GRUB_MAX_UTF16_PER_UTF8; + ucs2_url = grub_malloc ((ucs2_url_len + 1) * sizeof (ucs2_url[0])); + + if (!ucs2_url) + { + grub_free (url); + return grub_errno; + } + + ucs2_url_len = grub_utf8_to_utf16 (ucs2_url, ucs2_url_len, (grub_uint8_t *)url, url_len, NULL); /* convert string format from ascii to usc2 */ + ucs2_url[ucs2_url_len] = 0; + grub_free (url); + request_data.url = ucs2_url; + } + + request_headers[0].field_name = (grub_efi_char8_t *)"Host"; + request_headers[0].field_value = (grub_efi_char8_t *)server; + request_headers[1].field_name = (grub_efi_char8_t *)"Accept"; + request_headers[1].field_value = (grub_efi_char8_t *)"*/*"; + request_headers[2].field_name = (grub_efi_char8_t *)"User-Agent"; + request_headers[2].field_value = (grub_efi_char8_t *)"UefiHttpBoot/1.0"; + + request_data.method = (headeronly > 0) ? GRUB_EFI_HTTPMETHODHEAD : GRUB_EFI_HTTPMETHODGET; + + request_message.data.request = &request_data; + request_message.header_count = 3; + request_message.headers = request_headers; + request_message.body_length = 0; + request_message.body = NULL; + + /* request token */ + request_token.event = NULL; + request_token.status = GRUB_EFI_NOT_READY; + request_token.message = &request_message; + + request_callback_done = 0; + status = b->create_event(GRUB_EFI_EVT_NOTIFY_SIGNAL, GRUB_EFI_TPL_CALLBACK, + grub_efi_http_request_callback, NULL, + &request_token.event); + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (request_data.url); + return grub_error (GRUB_ERR_IO, "Fail to create an event! status=0x%x\n", (unsigned) status); + } + + status = http->request(http, &request_token); + + if (hostname) + grub_free (hostname); + + if (status != GRUB_EFI_SUCCESS) + { + b->close_event(request_token.event); + grub_free (request_data.url); + return grub_error (GRUB_ERR_IO, "Fail to send a request! status=0x%x\n", (unsigned) status); + } + /* TODO: Add Timeout */ + while (!request_callback_done) + http->poll(http); + + response_data.status_code = GRUB_EFI_HTTP_STATUS_UNSUPPORTED_STATUS; + response_message.data.response = &response_data; + /* herader_count will be updated by the HTTP driver on response */ + response_message.header_count = 0; + /* headers will be populated by the driver on response */ + response_message.headers = NULL; + /* use zero BodyLength to only receive the response headers */ + response_message.body_length = 0; + response_message.body = NULL; + response_token.event = NULL; + + status = b->create_event(GRUB_EFI_EVT_NOTIFY_SIGNAL, GRUB_EFI_TPL_CALLBACK, + grub_efi_http_response_callback, NULL, + &response_token.event); + + if (status != GRUB_EFI_SUCCESS) + { + b->close_event(request_token.event); + grub_free (request_data.url); + return grub_error (GRUB_ERR_IO, "Fail to create an event! status=0x%x\n", (unsigned) status); + } + + response_token.status = GRUB_EFI_SUCCESS; + response_token.message = &response_message; + + /* wait for HTTP response */ + response_callback_done = 0; + status = http->response(http, &response_token); + + if (status != GRUB_EFI_SUCCESS) + { + b->close_event(response_token.event); + b->close_event(request_token.event); + grub_free (request_data.url); + return grub_error (GRUB_ERR_IO, "Fail to receive a response! status=%d\n", (int)status); + } + + /* TODO: Add Timeout */ + while (!response_callback_done) + http->poll(http); + + if (response_message.data.response->status_code != GRUB_EFI_HTTP_STATUS_200_OK) + { + grub_efi_http_status_code_t status_code = response_message.data.response->status_code; + + if (response_message.headers) + b->free_pool(response_message.headers); + b->close_event(response_token.event); + b->close_event(request_token.event); + grub_free (request_data.url); + if (status_code == GRUB_EFI_HTTP_STATUS_404_NOT_FOUND) + { + return grub_error (GRUB_ERR_FILE_NOT_FOUND, _("file `%s' not found"), name); + } + else + { + return grub_error (GRUB_ERR_NET_UNKNOWN_ERROR, + _("unsupported uefi http status code 0x%x"), status_code); + } + } + + if (file_size) + { + int i; + /* parse the length of the file from the ContentLength header */ + for (*file_size = 0, i = 0; i < (int)response_message.header_count; ++i) + { + if (!grub_strcmp((const char*)response_message.headers[i].field_name, "Content-Length")) + { + *file_size = grub_strtoul((const char*)response_message.headers[i].field_value, 0, 10); + break; + } + } + } + + if (response_message.headers) + b->free_pool(response_message.headers); + b->close_event(response_token.event); + b->close_event(request_token.event); + grub_free (request_data.url); + + return GRUB_ERR_NONE; +} + +static grub_ssize_t +efihttp_read (struct grub_efi_net_device *dev, + char *buf, + grub_size_t len) +{ + grub_efi_http_message_t response_message; + grub_efi_http_token_t response_token; + + grub_efi_status_t status; + grub_size_t sum = 0; + grub_efi_boot_services_t *b = grub_efi_system_table->boot_services; + grub_efi_http_t *http = dev->http; + + if (!len) + { + grub_error (GRUB_ERR_BUG, "Invalid arguments to EFI HTTP Read"); + return -1; + } + + b->create_event(GRUB_EFI_EVT_NOTIFY_SIGNAL, GRUB_EFI_TPL_CALLBACK, + grub_efi_http_response_callback, NULL, + &response_token.event); + + while (len) + { + response_message.data.response = NULL; + response_message.header_count = 0; + response_message.headers = NULL; + response_message.body_length = len; + response_message.body = buf; + + response_token.message = &response_message; + response_token.status = GRUB_EFI_NOT_READY; + + response_callback_done = 0; + + status = http->response(http, &response_token); + if (status != GRUB_EFI_SUCCESS) + { + b->close_event(response_token.event); + grub_error (GRUB_ERR_IO, "Error! status=%d\n", (int)status); + return -1; + } + + while (!response_callback_done) + http->poll(http); + + sum += response_message.body_length; + buf += response_message.body_length; + len -= response_message.body_length; + } + + b->close_event(response_token.event); + + return sum; +} + +static grub_err_t +grub_efihttp_open (struct grub_efi_net_device *dev, + int prefer_ip6 __attribute__ ((unused)), + grub_file_t file, + const char *filename __attribute__ ((unused)), + int type) +{ + grub_err_t err; + grub_off_t size = 0; + char *buf = NULL; + char *file_name = NULL; + const char *http_path; + + /* If path is relative, prepend http_path */ + http_path = grub_env_get ("http_path"); + if (http_path && file->device->net->name[0] != '/') { + file_name = grub_xasprintf ("%s/%s", http_path, file->device->net->name); + if (!file_name) + return grub_errno; + } + + err = efihttp_request (dev->http, file->device->net->server, + file_name ? file_name : file->device->net->name, type, 1, 0); + if (err != GRUB_ERR_NONE) + { + grub_free (file_name); + return err; + } + + err = efihttp_request (dev->http, file->device->net->server, + file_name ? file_name : file->device->net->name, type, 0, &size); + grub_free (file_name); + if (err != GRUB_ERR_NONE) + { + return err; + } + + if (size) + { + buf = grub_malloc (size); + efihttp_read (dev, buf, size); + } + + file->size = size; + file->data = buf; + file->not_easily_seekable = 0; + file->device->net->offset = 0; + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_efihttp_close (struct grub_efi_net_device *dev __attribute__ ((unused)), + int prefer_ip6 __attribute__ ((unused)), + grub_file_t file) +{ + if (file->data) + grub_free (file->data); + + file->data = 0; + file->offset = 0; + file->size = 0; + file->device->net->offset = 0; + return GRUB_ERR_NONE; +} + +static grub_ssize_t +grub_efihttp_read (struct grub_efi_net_device *dev __attribute__((unused)), + int prefer_ip6 __attribute__((unused)), + grub_file_t file, + char *buf, + grub_size_t len) +{ + grub_size_t r = len; + + if (!file->data || !buf || !len) + return 0; + + if ((file->device->net->offset + len) > file->size) + r = file->size - file->device->net->offset; + + if (r) + { + grub_memcpy (buf, (char *)file->data + file->device->net->offset, r); + file->device->net->offset += r; + } + + return r; +} + +struct grub_efi_net_io io_http = + { + .configure = http_configure, + .open = grub_efihttp_open, + .read = grub_efihttp_read, + .close = grub_efihttp_close + }; diff --git a/grub-core/net/efi/ip4_config.c b/grub-core/net/efi/ip4_config.c new file mode 100644 index 0000000000..85a090ec42 --- /dev/null +++ b/grub-core/net/efi/ip4_config.c @@ -0,0 +1,421 @@ + +#include +#include +#include +#include +#include +#include + +char * +grub_efi_hw_address_to_string (grub_efi_uint32_t hw_address_size, grub_efi_mac_address_t hw_address) +{ + char *hw_addr, *p; + grub_size_t sz, s, i; + + if (grub_mul (hw_address_size, sizeof ("XX:") - 1, &sz) || + grub_add (sz, 1, &sz)) + { + grub_errno = GRUB_ERR_OUT_OF_RANGE; + return NULL; + } + + hw_addr = grub_malloc (sz); + if (!hw_addr) + return NULL; + + p = hw_addr; + s = sz; + for (i = 0; i < hw_address_size; i++) + { + grub_snprintf (p, sz, "%02x:", hw_address[i]); + p += sizeof ("XX:") - 1; + s -= sizeof ("XX:") - 1; + } + + hw_addr[sz - 2] = '\0'; + return hw_addr; +} + +char * +grub_efi_ip4_address_to_string (grub_efi_ipv4_address_t *address) +{ + char *addr; + + addr = grub_malloc (sizeof ("XXX.XXX.XXX.XXX")); + if (!addr) + return NULL; + + /* FIXME: Use grub_xasprintf ? */ + grub_snprintf (addr, + sizeof ("XXX.XXX.XXX.XXX"), + "%u.%u.%u.%u", + (*address)[0], + (*address)[1], + (*address)[2], + (*address)[3]); + + return addr; +} + +int +grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *address, const char **rest) +{ + grub_uint32_t newip = 0; + grub_size_t i; + int ncolon = 0; + const char *ptr = val; + + /* Check that is not an IPv6 address */ + for (i = 0; i < grub_strlen(ptr); i++) + { + if (ptr[i] == '[' && i == 0) + return 0; + + if (ptr[i] == ':') + if (i == 0 || ++ncolon == 2) + return 0; + } + + for (i = 0; i < 4; i++) + { + unsigned long t; + t = grub_strtoul (ptr, &ptr, 0); + if (grub_errno) + { + grub_errno = GRUB_ERR_NONE; + return 0; + } + if (*ptr != '.' && i == 0) + { + /* XXX: t is in host byte order */ + newip = t; + break; + } + if (t & ~0xff) + return 0; + newip <<= 8; + newip |= t; + if (i != 3 && *ptr != '.') + return 0; + ptr++; + } + + newip = grub_cpu_to_be32 (newip); + + grub_memcpy (address, &newip, sizeof(*address)); + + if (rest) + *rest = (ptr - 1); + return 1; +} + +static grub_efi_ip4_config2_interface_info_t * +efi_ip4_config_interface_info (grub_efi_ip4_config2_protocol_t *ip4_config) +{ + grub_efi_uintn_t sz; + grub_efi_status_t status; + grub_efi_ip4_config2_interface_info_t *interface_info; + + sz = sizeof (*interface_info) + sizeof (*interface_info->route_table); + interface_info = grub_malloc (sz); + if (!interface_info) + return NULL; + + status = ip4_config->get_data(ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO, + &sz, interface_info); + + if (status == GRUB_EFI_BUFFER_TOO_SMALL) + { + grub_free (interface_info); + interface_info = grub_malloc (sz); + status = ip4_config->get_data(ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO, + &sz, interface_info); + } + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (interface_info); + return NULL; + } + + return interface_info; +} + +static grub_efi_ip4_config2_manual_address_t * +efi_ip4_config_manual_address (grub_efi_ip4_config2_protocol_t *ip4_config) +{ + grub_efi_uintn_t sz; + grub_efi_status_t status; + grub_efi_ip4_config2_manual_address_t *manual_address; + + sz = sizeof (*manual_address); + manual_address = grub_malloc (sz); + if (!manual_address) + return NULL; + + status = ip4_config->get_data(ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, + &sz, manual_address); + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (manual_address); + return NULL; + } + + return manual_address; +} + +char * +grub_efi_ip4_interface_name (struct grub_efi_net_device *dev) +{ + grub_efi_ip4_config2_interface_info_t *interface_info; + char *name; + + interface_info = efi_ip4_config_interface_info (dev->ip4_config); + + if (!interface_info) + return NULL; + + name = grub_malloc (GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE + * GRUB_MAX_UTF8_PER_UTF16 + 1); + *grub_utf16_to_utf8 ((grub_uint8_t *)name, interface_info->name, + GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE) = 0; + grub_free (interface_info); + return name; +} + +static char * +grub_efi_ip4_interface_hw_address (struct grub_efi_net_device *dev) +{ + grub_efi_ip4_config2_interface_info_t *interface_info; + char *hw_addr; + + interface_info = efi_ip4_config_interface_info (dev->ip4_config); + + if (!interface_info) + return NULL; + + hw_addr = grub_efi_hw_address_to_string (interface_info->hw_address_size, interface_info->hw_address); + grub_free (interface_info); + + return hw_addr; +} + +static char * +grub_efi_ip4_interface_address (struct grub_efi_net_device *dev) +{ + grub_efi_ip4_config2_manual_address_t *manual_address; + char *addr; + + manual_address = efi_ip4_config_manual_address (dev->ip4_config); + + if (!manual_address) + return NULL; + + addr = grub_efi_ip4_address_to_string (&manual_address->address); + grub_free (manual_address); + return addr; +} + + +static int +address_mask_size (grub_efi_ipv4_address_t *address) +{ + grub_uint8_t i; + grub_uint32_t u32_addr = grub_be_to_cpu32 (grub_get_unaligned32 (address)); + + if (u32_addr == 0) + return 0; + + for (i = 0; i < 32 ; ++i) + { + if (u32_addr == ((0xffffffff >> i) << i)) + return (32 - i); + } + + return -1; +} + +static char ** +grub_efi_ip4_interface_route_table (struct grub_efi_net_device *dev) +{ + grub_efi_ip4_config2_interface_info_t *interface_info; + char **ret; + int id; + grub_size_t i, nmemb; + + interface_info = efi_ip4_config_interface_info (dev->ip4_config); + if (!interface_info) + return NULL; + + if (grub_add (interface_info->route_table_size, 1, &nmemb)) + { + grub_errno = GRUB_ERR_OUT_OF_RANGE; + return NULL; + } + + ret = grub_calloc (nmemb, sizeof (*ret)); + if (!ret) + { + grub_free (interface_info); + return NULL; + } + + id = 0; + for (i = 0; i < interface_info->route_table_size; i++) + { + char *subnet, *gateway, *mask; + grub_uint32_t u32_subnet, u32_gateway; + int mask_size; + grub_efi_ip4_route_table_t *route_table = interface_info->route_table + i; + grub_efi_net_interface_t *inf; + char *interface_name = NULL; + + for (inf = dev->net_interfaces; inf; inf = inf->next) + if (!inf->prefer_ip6) + interface_name = inf->name; + + u32_gateway = grub_get_unaligned32 (&route_table->gateway_address); + gateway = grub_efi_ip4_address_to_string (&route_table->gateway_address); + u32_subnet = grub_get_unaligned32 (&route_table->subnet_address); + subnet = grub_efi_ip4_address_to_string (&route_table->subnet_address); + mask_size = address_mask_size (&route_table->subnet_mask); + mask = grub_efi_ip4_address_to_string (&route_table->subnet_mask); + if (u32_subnet && !u32_gateway && interface_name) + ret[id++] = grub_xasprintf ("%s:local %s/%d %s", dev->card_name, subnet, mask_size, interface_name); + else if (u32_subnet && u32_gateway) + ret[id++] = grub_xasprintf ("%s:gw %s/%d gw %s", dev->card_name, subnet, mask_size, gateway); + else if (!u32_subnet && u32_gateway) + ret[id++] = grub_xasprintf ("%s:default %s/%d gw %s", dev->card_name, subnet, mask_size, gateway); + grub_free (subnet); + grub_free (gateway); + grub_free (mask); + } + + ret[id] = NULL; + grub_free (interface_info); + return ret; +} + +static grub_efi_net_interface_t * +grub_efi_ip4_interface_match (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *ip_address) +{ + grub_efi_ip4_config2_interface_info_t *interface_info; + grub_efi_net_interface_t *inf; + int i; + grub_efi_ipv4_address_t *address = &ip_address->ip4; + + interface_info = efi_ip4_config_interface_info (dev->ip4_config); + if (!interface_info) + return NULL; + + for (i = 0; i < (int)interface_info->route_table_size; i++) + { + grub_efi_ip4_route_table_t *route_table = interface_info->route_table + i; + grub_uint32_t u32_address, u32_mask, u32_subnet; + + u32_address = grub_get_unaligned32 (address); + u32_subnet = grub_get_unaligned32 (route_table->subnet_address); + u32_mask = grub_get_unaligned32 (route_table->subnet_mask); + + /* SKIP Default GATEWAY */ + if (!u32_subnet && !u32_mask) + continue; + + if ((u32_address & u32_mask) == u32_subnet) + { + for (inf = dev->net_interfaces; inf; inf = inf->next) + if (!inf->prefer_ip6) + { + grub_free (interface_info); + return inf; + } + } + } + + grub_free (interface_info); + return NULL; +} + +static int +grub_efi_ip4_interface_set_manual_address (struct grub_efi_net_device *dev, + grub_efi_net_ip_manual_address_t *net_ip, + int with_subnet) +{ + grub_efi_status_t status; + grub_efi_ip4_config2_manual_address_t *address = &net_ip->ip4; + + if (!with_subnet) + { + grub_efi_ip4_config2_manual_address_t *manual_address = + efi_ip4_config_manual_address (dev->ip4_config); + + if (manual_address) + { + grub_memcpy (address->subnet_mask, manual_address->subnet_mask, sizeof(address->subnet_mask)); + grub_free (manual_address); + } + else + { + /* XXX: */ + address->subnet_mask[0] = 0xff; + address->subnet_mask[1] = 0xff; + address->subnet_mask[2] = 0xff; + address->subnet_mask[3] = 0; + } + } + + status = dev->ip4_config->set_data(dev->ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, + sizeof(*address), address); + + if (status != GRUB_EFI_SUCCESS) + return 0; + + return 1; +} + +static int +grub_efi_ip4_interface_set_gateway (struct grub_efi_net_device *dev, + grub_efi_net_ip_address_t *address) +{ + grub_efi_status_t status; + + status = dev->ip4_config->set_data(dev->ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_GATEWAY, + sizeof (address->ip4), &address->ip4); + + if (status != GRUB_EFI_SUCCESS) + return 0; + return 1; +} + +/* FIXME: Multiple DNS */ +static int +grub_efi_ip4_interface_set_dns (struct grub_efi_net_device *dev, + grub_efi_net_ip_address_t *address) +{ + grub_efi_status_t status; + + status = dev->ip4_config->set_data(dev->ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, + sizeof (address->ip4), &address->ip4); + + if (status != GRUB_EFI_SUCCESS) + return 0; + return 1; +} + +grub_efi_net_ip_config_t *efi_net_ip4_config = &(grub_efi_net_ip_config_t) + { + .get_hw_address = grub_efi_ip4_interface_hw_address, + .get_address = grub_efi_ip4_interface_address, + .get_route_table = grub_efi_ip4_interface_route_table, + .best_interface = grub_efi_ip4_interface_match, + .set_address = grub_efi_ip4_interface_set_manual_address, + .set_gateway = grub_efi_ip4_interface_set_gateway, + .set_dns = grub_efi_ip4_interface_set_dns + }; diff --git a/grub-core/net/efi/ip6_config.c b/grub-core/net/efi/ip6_config.c new file mode 100644 index 0000000000..952bda9fa7 --- /dev/null +++ b/grub-core/net/efi/ip6_config.c @@ -0,0 +1,429 @@ +#include +#include +#include +#include +#include +#include + +char * +grub_efi_ip6_address_to_string (grub_efi_pxe_ipv6_address_t *address) +{ + char *str = grub_malloc (sizeof ("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX")); + char *p; + int i; + int squash; + + if (!str) + return NULL; + + p = str; + squash = 0; + for (i = 0; i < 8; ++i) + { + grub_uint16_t addr; + + if (i == 7) + squash = 2; + + addr = grub_get_unaligned16 (address->addr + i * 2); + + if (grub_be_to_cpu16 (addr)) + { + char buf[sizeof ("XXXX")]; + if (i > 0) + *p++ = ':'; + grub_snprintf (buf, sizeof (buf), "%x", grub_be_to_cpu16 (addr)); + grub_strcpy (p, buf); + p += grub_strlen (buf); + + if (squash == 1) + squash = 2; + } + else + { + if (squash == 0) + { + *p++ = ':'; + squash = 1; + } + else if (squash == 2) + { + *p++ = ':'; + *p++ = '0'; + } + } + } + *p = '\0'; + return str; +} + +int +grub_efi_string_to_ip6_address (const char *val, grub_efi_ipv6_address_t *address, const char **rest) +{ + grub_uint16_t newip[8]; + const char *ptr = val; + int word, quaddot = -1; + int bracketed = 0; + + if (ptr[0] == '[') { + bracketed = 1; + ptr++; + } + + if (ptr[0] == ':' && ptr[1] != ':') + return 0; + if (ptr[0] == ':') + ptr++; + + for (word = 0; word < 8; word++) + { + unsigned long t; + if (*ptr == ':') + { + quaddot = word; + word--; + ptr++; + continue; + } + t = grub_strtoul (ptr, &ptr, 16); + if (grub_errno) + { + grub_errno = GRUB_ERR_NONE; + break; + } + if (t & ~0xffff) + return 0; + newip[word] = grub_cpu_to_be16 (t); + if (*ptr != ':') + break; + ptr++; + } + if (quaddot == -1 && word < 7) + return 0; + if (quaddot != -1) + { + grub_memmove (&newip[quaddot + 7 - word], &newip[quaddot], + (word - quaddot + 1) * sizeof (newip[0])); + grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0])); + } + grub_memcpy (address, newip, 16); + if (bracketed && *ptr == ']') { + ptr++; + } + if (rest) + *rest = ptr; + return 1; +} + +static grub_efi_ip6_config_interface_info_t * +efi_ip6_config_interface_info (grub_efi_ip6_config_protocol_t *ip6_config) +{ + grub_efi_uintn_t sz; + grub_efi_status_t status; + grub_efi_ip6_config_interface_info_t *interface_info; + + sz = sizeof (*interface_info) + sizeof (*interface_info->route_table); + interface_info = grub_malloc (sz); + + status = ip6_config->get_data(ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO, + &sz, interface_info); + + if (status == GRUB_EFI_BUFFER_TOO_SMALL) + { + grub_free (interface_info); + interface_info = grub_malloc (sz); + status = ip6_config->get_data(ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO, + &sz, interface_info); + } + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (interface_info); + return NULL; + } + + return interface_info; +} + +static grub_efi_ip6_config_manual_address_t * +efi_ip6_config_manual_address (grub_efi_ip6_config_protocol_t *ip6_config) +{ + grub_efi_uintn_t sz; + grub_efi_status_t status; + grub_efi_ip6_config_manual_address_t *manual_address; + + sz = sizeof (*manual_address); + manual_address = grub_malloc (sz); + if (!manual_address) + return NULL; + + status = ip6_config->get_data(ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, + &sz, manual_address); + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (manual_address); + return NULL; + } + + return manual_address; +} + +char * +grub_efi_ip6_interface_name (struct grub_efi_net_device *dev) +{ + grub_efi_ip6_config_interface_info_t *interface_info; + char *name; + + interface_info = efi_ip6_config_interface_info (dev->ip6_config); + + if (!interface_info) + return NULL; + + name = grub_malloc (GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE + * GRUB_MAX_UTF8_PER_UTF16 + 1); + *grub_utf16_to_utf8 ((grub_uint8_t *)name, interface_info->name, + GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE) = 0; + grub_free (interface_info); + return name; +} + +static char * +grub_efi_ip6_interface_hw_address (struct grub_efi_net_device *dev) +{ + grub_efi_ip6_config_interface_info_t *interface_info; + char *hw_addr; + + interface_info = efi_ip6_config_interface_info (dev->ip6_config); + + if (!interface_info) + return NULL; + + hw_addr = grub_efi_hw_address_to_string (interface_info->hw_address_size, interface_info->hw_address); + grub_free (interface_info); + + return hw_addr; +} + +static char * +grub_efi_ip6_interface_address (struct grub_efi_net_device *dev) +{ + grub_efi_ip6_config_manual_address_t *manual_address; + char *addr; + + manual_address = efi_ip6_config_manual_address (dev->ip6_config); + + if (!manual_address) + return NULL; + + addr = grub_efi_ip6_address_to_string ((grub_efi_pxe_ipv6_address_t *)&manual_address->address); + grub_free (manual_address); + return addr; +} + +static char ** +grub_efi_ip6_interface_route_table (struct grub_efi_net_device *dev) +{ + grub_efi_ip6_config_interface_info_t *interface_info; + char **ret; + int id; + grub_size_t i, nmemb; + + interface_info = efi_ip6_config_interface_info (dev->ip6_config); + if (!interface_info) + return NULL; + + if (grub_add (interface_info->route_count, 1, &nmemb)) + { + grub_errno = GRUB_ERR_OUT_OF_RANGE; + return NULL; + } + + ret = grub_calloc (nmemb, sizeof (*ret)); + if (!ret) + { + grub_free (interface_info); + return NULL; + } + + id = 0; + for (i = 0; i < interface_info->route_count ; i++) + { + char *gateway, *destination; + grub_uint64_t u64_gateway[2]; + grub_uint64_t u64_destination[2]; + grub_efi_ip6_route_table_t *route_table = interface_info->route_table + i; + grub_efi_net_interface_t *inf; + char *interface_name = NULL; + + gateway = grub_efi_ip6_address_to_string (&route_table->gateway); + destination = grub_efi_ip6_address_to_string (&route_table->destination); + + u64_gateway[0] = grub_get_unaligned64 (route_table->gateway.addr); + u64_gateway[1] = grub_get_unaligned64 (route_table->gateway.addr + 8); + u64_destination[0] = grub_get_unaligned64 (route_table->destination.addr); + u64_destination[1] = grub_get_unaligned64 (route_table->destination.addr + 8); + + for (inf = dev->net_interfaces; inf; inf = inf->next) + if (inf->prefer_ip6) + interface_name = inf->name; + + if ((!u64_gateway[0] && !u64_gateway[1]) + && (u64_destination[0] || u64_destination[1])) + { + if (interface_name) + { + if ((grub_be_to_cpu64 (u64_destination[0]) == 0xfe80000000000000ULL) + && (!u64_destination[1]) + && (route_table->prefix_length == 64)) + ret[id++] = grub_xasprintf ("%s:link %s/%d %s", dev->card_name, destination, route_table->prefix_length, interface_name); + else + ret[id++] = grub_xasprintf ("%s:local %s/%d %s", dev->card_name, destination, route_table->prefix_length, interface_name); + } + } + else if ((u64_gateway[0] || u64_gateway[1]) + && (u64_destination[0] || u64_destination[1])) + ret[id++] = grub_xasprintf ("%s:gw %s/%d gw %s", dev->card_name, destination, route_table->prefix_length, gateway); + else if ((u64_gateway[0] || u64_gateway[1]) + && (!u64_destination[0] && !u64_destination[1])) + ret[id++] = grub_xasprintf ("%s:default %s/%d gw %s", dev->card_name, destination, route_table->prefix_length, gateway); + + grub_free (gateway); + grub_free (destination); + } + + ret[id] = NULL; + grub_free (interface_info); + return ret; +} + +static grub_efi_net_interface_t * +grub_efi_ip6_interface_match (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *ip_address) +{ + grub_efi_ip6_config_interface_info_t *interface_info; + grub_efi_net_interface_t *inf; + int i; + grub_efi_ipv6_address_t *address = &ip_address->ip6; + + interface_info = efi_ip6_config_interface_info (dev->ip6_config); + if (!interface_info) + return NULL; + + for (i = 0; i < (int)interface_info->route_count ; i++) + { + grub_uint64_t u64_addr[2]; + grub_uint64_t u64_subnet[2]; + grub_uint64_t u64_mask[2]; + + grub_efi_ip6_route_table_t *route_table = interface_info->route_table + i; + + /* SKIP Default GATEWAY */ + if (route_table->prefix_length == 0) + continue; + + u64_addr[0] = grub_get_unaligned64 (address); + u64_addr[1] = grub_get_unaligned64 (address + 4); + u64_subnet[0] = grub_get_unaligned64 (route_table->destination.addr); + u64_subnet[1] = grub_get_unaligned64 (route_table->destination.addr + 8); + u64_mask[0] = (route_table->prefix_length <= 64) ? + 0xffffffffffffffffULL << (64 - route_table->prefix_length) : + 0xffffffffffffffffULL; + u64_mask[1] = (route_table->prefix_length <= 64) ? + 0 : + 0xffffffffffffffffULL << (128 - route_table->prefix_length); + + if (((u64_addr[0] & u64_mask[0]) == u64_subnet[0]) + && ((u64_addr[1] & u64_mask[1]) == u64_subnet[1])) + { + for (inf = dev->net_interfaces; inf; inf = inf->next) + if (inf->prefer_ip6) + { + grub_free (interface_info); + return inf; + } + } + } + + grub_free (interface_info); + return NULL; +} + +static int +grub_efi_ip6_interface_set_manual_address (struct grub_efi_net_device *dev, + grub_efi_net_ip_manual_address_t *net_ip, + int with_subnet) +{ + grub_efi_status_t status; + grub_efi_ip6_config_manual_address_t *address = &net_ip->ip6; + + if (!with_subnet) + { + grub_efi_ip6_config_manual_address_t *manual_address = + efi_ip6_config_manual_address (dev->ip6_config); + + if (manual_address) + { + address->prefix_length = manual_address->prefix_length; + grub_free (manual_address); + } + else + { + /* XXX: */ + address->prefix_length = 64; + } + } + + status = dev->ip6_config->set_data(dev->ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, + sizeof(*address), address); + + if (status != GRUB_EFI_SUCCESS) + return 0; + + return 1; +} + +static int +grub_efi_ip6_interface_set_gateway (struct grub_efi_net_device *dev, + grub_efi_net_ip_address_t *address) +{ + grub_efi_status_t status; + + status = dev->ip6_config->set_data(dev->ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_GATEWAY, + sizeof (address->ip6), &address->ip6); + + if (status != GRUB_EFI_SUCCESS) + return 0; + return 1; +} + +static int +grub_efi_ip6_interface_set_dns (struct grub_efi_net_device *dev, + grub_efi_net_ip_address_t *address) +{ + + grub_efi_status_t status; + + status = dev->ip6_config->set_data(dev->ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER, + sizeof (address->ip6), &address->ip6); + + if (status != GRUB_EFI_SUCCESS) + return 0; + return 1; +} + +grub_efi_net_ip_config_t *efi_net_ip6_config = &(grub_efi_net_ip_config_t) + { + .get_hw_address = grub_efi_ip6_interface_hw_address, + .get_address = grub_efi_ip6_interface_address, + .get_route_table = grub_efi_ip6_interface_route_table, + .best_interface = grub_efi_ip6_interface_match, + .set_address = grub_efi_ip6_interface_set_manual_address, + .set_gateway = grub_efi_ip6_interface_set_gateway, + .set_dns = grub_efi_ip6_interface_set_dns + }; diff --git a/grub-core/net/efi/net.c b/grub-core/net/efi/net.c new file mode 100644 index 0000000000..1b13603a63 --- /dev/null +++ b/grub-core/net/efi/net.c @@ -0,0 +1,1436 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define GRUB_EFI_IP6_PREFIX_LENGTH 64 + +static grub_guid_t ip4_config_guid = GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID; +static grub_guid_t ip6_config_guid = GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID; +static grub_guid_t http_service_binding_guid = GRUB_EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID; +static grub_guid_t http_guid = GRUB_EFI_HTTP_PROTOCOL_GUID; +static grub_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID; +static grub_guid_t dhcp4_service_binding_guid = GRUB_EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID; +static grub_guid_t dhcp4_guid = GRUB_EFI_DHCP4_PROTOCOL_GUID; +static grub_guid_t dhcp6_service_binding_guid = GRUB_EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID; +static grub_guid_t dhcp6_guid = GRUB_EFI_DHCP6_PROTOCOL_GUID; + +struct grub_efi_net_device *net_devices; + +static char *default_server; +static grub_efi_net_interface_t *net_interface; +static grub_efi_net_interface_t *net_default_interface; + +#define efi_net_interface_configure(inf) inf->io->configure (inf->dev, inf->prefer_ip6) +#define efi_net_interface_open(inf, file, name) inf->io->open (inf->dev, inf->prefer_ip6, file, name, inf->io_type) +#define efi_net_interface_read(inf, file, buf, sz) inf->io->read (inf->dev, inf->prefer_ip6, file, buf, sz) +#define efi_net_interface_close(inf, file) inf->io->close (inf->dev, inf->prefer_ip6, file) +#define efi_net_interface(m,...) efi_net_interface_ ## m (net_interface, ## __VA_ARGS__) + +static grub_efi_handle_t +grub_efi_locate_device_path (grub_guid_t *protocol, grub_efi_device_path_t *device_path, + grub_efi_device_path_t **r_device_path) +{ + grub_efi_handle_t handle; + grub_efi_status_t status; + + status = grub_efi_system_table->boot_services->locate_device_path( + protocol, &device_path, &handle); + + if (status != GRUB_EFI_SUCCESS) + return 0; + + if (r_device_path) + *r_device_path = device_path; + + return handle; +} + +static int +url_parse_fields (const char *url, char **proto, char **host, char **path) +{ + const char *p, *ps; + grub_size_t l; + + *proto = *host = *path = NULL; + ps = p = url; + + while ((p = grub_strchr (p, ':'))) + { + if (grub_strlen (p) < sizeof ("://") - 1) + break; + if (grub_memcmp (p, "://", sizeof ("://") - 1) == 0) + { + l = p - ps; + *proto = grub_malloc (l + 1); + if (!*proto) + { + grub_print_error (); + return 0; + } + + grub_memcpy (*proto, ps, l); + (*proto)[l] = '\0'; + p += sizeof ("://") - 1; + break; + } + ++p; + } + + if (!*proto) + { + grub_dprintf ("bootp", "url: %s is not valid, protocol not found\n", url); + return 0; + } + + ps = p; + p = grub_strchr (p, '/'); + + if (!p) + { + grub_dprintf ("bootp", "url: %s is not valid, host/path not found\n", url); + grub_free (*proto); + *proto = NULL; + return 0; + } + + l = p - ps; + + if (l > 2 && ps[0] == '[' && ps[l - 1] == ']') + { + *host = grub_malloc (l - 1); + if (!*host) + { + grub_print_error (); + grub_free (*proto); + *proto = NULL; + return 0; + } + grub_memcpy (*host, ps + 1, l - 2); + (*host)[l - 2] = 0; + } + else + { + *host = grub_malloc (l + 1); + if (!*host) + { + grub_print_error (); + grub_free (*proto); + *proto = NULL; + return 0; + } + grub_memcpy (*host, ps, l); + (*host)[l] = 0; + } + + *path = grub_strdup (p); + if (!*path) + { + grub_print_error (); + grub_free (*host); + grub_free (*proto); + *host = NULL; + *proto = NULL; + return 0; + } + return 1; +} + +static void +url_get_boot_location (const char *url, char **device, char **path, int is_default) +{ + char *protocol, *server, *file; + char *slash; + + if (!url_parse_fields (url, &protocol, &server, &file)) + return; + + if ((slash = grub_strrchr (file, '/'))) + *slash = 0; + else + *file = 0; + + *device = grub_xasprintf ("%s,%s", protocol, server); + *path = grub_strdup(file); + + if (is_default) + default_server = server; + else + grub_free (server); + + grub_free (protocol); + grub_free (file); +} + +static void +pxe_get_boot_location (const struct grub_net_bootp_packet *bp, + char **device, + char **path, + int is_default) +{ + char *server = grub_xasprintf ("%d.%d.%d.%d", + ((grub_uint8_t *) &bp->server_ip)[0], + ((grub_uint8_t *) &bp->server_ip)[1], + ((grub_uint8_t *) &bp->server_ip)[2], + ((grub_uint8_t *) &bp->server_ip)[3]); + + *device = grub_xasprintf ("tftp,%s", server); + + *path = grub_strndup (bp->boot_file, sizeof (bp->boot_file)); + + if (*path) + { + char *slash; + slash = grub_strrchr (*path, '/'); + if (slash) + *slash = 0; + else + **path = 0; + } + + if (is_default) + default_server = server; + else + grub_free (server); +} + +static void +pxe_get_boot_location_v6 (const struct grub_net_dhcp6_packet *dp, + grub_size_t dhcp_size, + char **device, + char **path) +{ + + struct grub_net_dhcp6_option *dhcp_opt; + grub_size_t dhcp_remain_size; + *device = *path = 0; + + if (dhcp_size < sizeof (*dp)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("DHCPv6 packet size too small")); + return; + } + + dhcp_remain_size = dhcp_size - sizeof (*dp); + dhcp_opt = (struct grub_net_dhcp6_option *)dp->dhcp_options; + + while (dhcp_remain_size) + { + grub_uint16_t code = grub_be_to_cpu16 (dhcp_opt->code); + grub_uint16_t len = grub_be_to_cpu16 (dhcp_opt->len); + grub_uint16_t option_size = sizeof (*dhcp_opt) + len; + + if (dhcp_remain_size < option_size || code == 0) + break; + + if (code == GRUB_NET_DHCP6_OPTION_BOOTFILE_URL) + { + char *url = grub_malloc (len + 1); + + grub_memcpy (url, dhcp_opt->data, len); + url[len] = 0; + + url_get_boot_location ((const char *)url, device, path, 1); + grub_free (url); + break; + } + + dhcp_remain_size -= option_size; + dhcp_opt = (struct grub_net_dhcp6_option *)((grub_uint8_t *)dhcp_opt + option_size); + } +} + +static grub_efi_net_interface_t * +grub_efi_net_config_from_device_path (grub_efi_device_path_t *dp, + struct grub_efi_net_device *netdev, + char **device, + char **path) +{ + grub_efi_net_interface_t *inf = NULL; + + while (1) + { + grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); + grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); + grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp); + + if (type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE) + { + if (subtype == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE) + { + grub_efi_uri_device_path_t *uri_dp; + uri_dp = (grub_efi_uri_device_path_t *) dp; + /* Beware that uri_dp->uri may not be null terminated */ + url_get_boot_location ((const char *)uri_dp->uri, device, path, 1); + } + else if (subtype == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE) + { + grub_efi_net_ip_manual_address_t net_ip; + grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) dp; + + if (inf) + continue; + grub_memcpy (net_ip.ip4.address, ipv4->local_ip_address, sizeof (net_ip.ip4.address)); + grub_memcpy (net_ip.ip4.subnet_mask, ipv4->subnet_mask, sizeof (net_ip.ip4.subnet_mask)); + net_ip.is_ip6 = 0; + inf = grub_efi_net_create_interface (netdev, + netdev->card_name, + &net_ip, + 1); + } + else if (subtype == GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE) + { + grub_efi_net_ip_manual_address_t net_ip; + grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) dp; + + if (inf) + continue; + grub_memcpy (net_ip.ip6.address, ipv6->local_ip_address, sizeof (net_ip.ip6.address)); + net_ip.ip6.prefix_length = GRUB_EFI_IP6_PREFIX_LENGTH; + net_ip.ip6.is_anycast = 0; + net_ip.is_ip6 = 1; + inf = grub_efi_net_create_interface (netdev, + netdev->card_name, + &net_ip, + 1); + } + } + + if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) + break; + dp = (grub_efi_device_path_t *) ((char *) dp + len); + } + + return inf; +} + +static grub_efi_net_interface_t * +grub_efi_net_config_from_handle (grub_efi_handle_t *hnd, + struct grub_efi_net_device *netdev, + char **device, + char **path) +{ + grub_efi_pxe_t *pxe = NULL; + + if (hnd == netdev->ip4_pxe_handle) + pxe = netdev->ip4_pxe; + else if (hnd == netdev->ip6_pxe_handle) + pxe = netdev->ip6_pxe; + + if (!pxe) + return (grub_efi_net_config_from_device_path ( + grub_efi_get_device_path (hnd), + netdev, + device, + path)); + + if (pxe->mode->using_ipv6) + { + grub_efi_net_ip_manual_address_t net_ip; + + pxe_get_boot_location_v6 ( + (const struct grub_net_dhcp6_packet *) &pxe->mode->dhcp_ack, + sizeof (pxe->mode->dhcp_ack), + device, + path); + + grub_memcpy (net_ip.ip6.address, pxe->mode->station_ip.v6, sizeof(net_ip.ip6.address)); + net_ip.ip6.prefix_length = GRUB_EFI_IP6_PREFIX_LENGTH; + net_ip.ip6.is_anycast = 0; + net_ip.is_ip6 = 1; + return (grub_efi_net_create_interface (netdev, + netdev->card_name, + &net_ip, + 1)); + } + else + { + grub_efi_net_ip_manual_address_t net_ip; + + pxe_get_boot_location ( + (const struct grub_net_bootp_packet *) &pxe->mode->dhcp_ack, + device, + path, + 1); + + grub_memcpy (net_ip.ip4.address, pxe->mode->station_ip.v4, sizeof (net_ip.ip4.address)); + grub_memcpy (net_ip.ip4.subnet_mask, pxe->mode->subnet_mask.v4, sizeof (net_ip.ip4.subnet_mask)); + net_ip.is_ip6 = 0; + return (grub_efi_net_create_interface (netdev, + netdev->card_name, + &net_ip, + 1)); + } +} + +static const char * +grub_efi_net_var_get_address (struct grub_env_var *var, + const char *val __attribute__ ((unused))) +{ + struct grub_efi_net_device *dev; + + for (dev = net_devices; dev; dev = dev->next) + { + grub_efi_net_interface_t *inf; + + for (inf = dev->net_interfaces; inf; inf = inf->next) + { + char *var_name; + + var_name = grub_xasprintf ("net_%s_ip", inf->name); + if (grub_strcmp (var_name, var->name) == 0) + return efi_net_interface_get_address (inf); + grub_free (var_name); + var_name = grub_xasprintf ("net_%s_mac", inf->name); + if (grub_strcmp (var_name, var->name) == 0) + return efi_net_interface_get_hw_address (inf); + grub_free (var_name); + } + } + + return NULL; +} + +static char * +grub_efi_net_var_set_interface (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + struct grub_efi_net_device *dev; + grub_efi_net_interface_t *inf; + + for (dev = net_devices; dev; dev = dev->next) + for (inf = dev->net_interfaces; inf; inf = inf->next) + if (grub_strcmp (inf->name, val) == 0) + { + net_default_interface = inf; + return grub_strdup (val); + } + + return NULL; +} + +static char * +grub_efi_net_var_set_server (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + grub_free (default_server); + default_server = grub_strdup (val); + return grub_strdup (val); +} + +static const char * +grub_efi_net_var_get_server (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + return default_server ? : ""; +} + +static const char * +grub_efi_net_var_get_ip (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + const char *intf = grub_env_get ("net_default_interface"); + const char *ret = NULL; + if (intf) + { + char *buf = grub_xasprintf ("net_%s_ip", intf); + if (buf) + ret = grub_env_get (buf); + grub_free (buf); + } + return ret; +} + +static const char * +grub_efi_net_var_get_mac (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + const char *intf = grub_env_get ("net_default_interface"); + const char *ret = NULL; + if (intf) + { + char *buf = grub_xasprintf ("net_%s_mac", intf); + if (buf) + ret = grub_env_get (buf); + grub_free (buf); + } + return ret; +} + +static void +grub_efi_net_export_interface_vars (void) +{ + struct grub_efi_net_device *dev; + + for (dev = net_devices; dev; dev = dev->next) + { + grub_efi_net_interface_t *inf; + + for (inf = dev->net_interfaces; inf; inf = inf->next) + { + char *var; + + var = grub_xasprintf ("net_%s_ip", inf->name); + grub_register_variable_hook (var, grub_efi_net_var_get_address, 0); + grub_env_export (var); + grub_free (var); + var = grub_xasprintf ("net_%s_mac", inf->name); + grub_register_variable_hook (var, grub_efi_net_var_get_address, 0); + grub_env_export (var); + grub_free (var); + } + } +} + +static void +grub_efi_net_unset_interface_vars (void) +{ + struct grub_efi_net_device *dev; + + for (dev = net_devices; dev; dev = dev->next) + { + grub_efi_net_interface_t *inf; + + for (inf = dev->net_interfaces; inf; inf = inf->next) + { + char *var; + + var = grub_xasprintf ("net_%s_ip", inf->name); + grub_register_variable_hook (var, 0, 0); + grub_env_unset (var); + grub_free (var); + var = grub_xasprintf ("net_%s_mac", inf->name); + grub_register_variable_hook (var, 0, 0); + grub_env_unset (var); + grub_free (var); + } + } +} + +grub_efi_net_interface_t * +grub_efi_net_create_interface (struct grub_efi_net_device *dev, + const char *interface_name, + grub_efi_net_ip_manual_address_t *net_ip, + int has_subnet) +{ + grub_efi_net_interface_t *inf; + + for (inf = dev->net_interfaces; inf; inf = inf->next) + { + if (inf->prefer_ip6 == net_ip->is_ip6) + break; + } + + if (!inf) + { + inf = grub_malloc (sizeof(*inf)); + inf->name = grub_strdup (interface_name); + inf->prefer_ip6 = net_ip->is_ip6; + inf->dev = dev; + inf->next = dev->net_interfaces; + inf->ip_config = (net_ip->is_ip6) ? efi_net_ip6_config : efi_net_ip4_config ; + dev->net_interfaces = inf; + } + else + { + grub_free (inf->name); + inf->name = grub_strdup (interface_name); + } + + if (!efi_net_interface_set_address (inf, net_ip, has_subnet)) + { + grub_error (GRUB_ERR_BUG, N_("Set Address Failed")); + return NULL; + } + + return inf; +} + +static void +grub_efi_net_config_real (grub_efi_handle_t hnd, char **device, + char **path) +{ + grub_efi_handle_t config_hnd; + + struct grub_efi_net_device *netdev; + grub_efi_net_interface_t *inf; + + config_hnd = grub_efi_locate_device_path (&ip4_config_guid, grub_efi_get_device_path (hnd), NULL); + + if (!config_hnd) + return; + + for (netdev = net_devices; netdev; netdev = netdev->next) + if (netdev->handle == config_hnd) + break; + + if (!netdev) + return; + + if (!(inf = grub_efi_net_config_from_handle (hnd, netdev, device, path))) + return; + + grub_env_set ("net_default_interface", inf->name); + grub_efi_net_export_interface_vars (); +} + +static grub_err_t +grub_efi_netfs_dir (grub_device_t device, const char *path __attribute__ ((unused)), + grub_fs_dir_hook_t hook __attribute__ ((unused)), + void *hook_data __attribute__ ((unused))) +{ + if (!device->net) + return grub_error (GRUB_ERR_BUG, "invalid net device"); + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_efi_netfs_open (struct grub_file *file_out __attribute__ ((unused)), + const char *name __attribute__ ((unused))) +{ + struct grub_file *file, *bufio; + + file = grub_malloc (sizeof (*file)); + if (!file) + return grub_errno; + + grub_memcpy (file, file_out, sizeof (struct grub_file)); + file->device->net->name = grub_strdup (name); + + if (!file->device->net->name) + { + grub_free (file); + return grub_errno; + } + + efi_net_interface(open, file, name); + grub_print_error (); + + bufio = grub_bufio_open (file, 32768); + if (!bufio) + { + grub_free (file->device->net->name); + grub_free (file); + return grub_errno; + } + grub_memcpy (file_out, bufio, sizeof (struct grub_file)); + grub_free (bufio); + + return GRUB_ERR_NONE; +} + +static grub_ssize_t +grub_efihttp_chunk_read (grub_file_t file, char *buf, + grub_size_t len, grub_size_t chunk_size) +{ + char *chunk = grub_malloc (chunk_size); + grub_size_t sum = 0; + + while (len) + { + grub_ssize_t rd; + grub_size_t sz = (len > chunk_size) ? chunk_size : len; + + rd = efi_net_interface (read, file, chunk, sz); + + if (rd <= 0) { + grub_free (chunk); + return rd; + } + + if (buf) + { + grub_memcpy (buf, chunk, rd); + buf += rd; + } + sum += rd; + len -= rd; + } + + grub_free (chunk); + return sum; +} + +static grub_ssize_t +grub_efi_netfs_read (grub_file_t file __attribute__ ((unused)), + char *buf __attribute__ ((unused)), grub_size_t len __attribute__ ((unused))) +{ + if (file->offset > file->device->net->offset) + { + grub_efihttp_chunk_read (file, NULL, file->offset - file->device->net->offset, 10240); + } + else if (file->offset < file->device->net->offset) + { + efi_net_interface (close, file); + efi_net_interface (open, file, file->device->net->name); + if (file->offset) + grub_efihttp_chunk_read (file, NULL, file->offset, 10240); + } + + return efi_net_interface (read, file, buf, len); +} + +static grub_err_t +grub_efi_netfs_close (grub_file_t file) +{ + efi_net_interface (close, file); + return GRUB_ERR_NONE; +} + +static grub_efi_handle_t +grub_efi_service_binding (grub_efi_handle_t dev, grub_guid_t *service_binding_guid) +{ + grub_efi_service_binding_t *service; + grub_efi_status_t status; + grub_efi_handle_t child_dev = NULL; + + service = grub_efi_open_protocol (dev, service_binding_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (!service) + { + grub_error (GRUB_ERR_IO, N_("couldn't open efi service binding protocol")); + return NULL; + } + + status = service->create_child(service, &child_dev); + if (status != GRUB_EFI_SUCCESS) + { + grub_error (GRUB_ERR_IO, N_("Failed to create child device of http service %x"), (unsigned) status); + return NULL; + } + + return child_dev; +} + +static grub_err_t +grub_efi_net_parse_address (const char *address, + grub_efi_ip4_config2_manual_address_t *ip4, + grub_efi_ip6_config_manual_address_t *ip6, + int *is_ip6, + int *has_cidr) +{ + const char *rest; + + if (grub_efi_string_to_ip4_address (address, &ip4->address, &rest)) + { + *is_ip6 = 0; + if (*rest == '/') + { + grub_uint32_t subnet_mask_size; + + subnet_mask_size = grub_strtoul (rest + 1, &rest, 0); + + if (!grub_errno && subnet_mask_size <= 32 && *rest == 0) + { + grub_uint32_t subnet_mask; + + subnet_mask = grub_cpu_to_be32 ((0xffffffffU << (32 - subnet_mask_size))); + grub_memcpy (ip4->subnet_mask, &subnet_mask, sizeof (ip4->subnet_mask)); + if (has_cidr) + *has_cidr = 1; + return GRUB_ERR_NONE; + } + } + else if (*rest == 0 || *rest == ':') + { + grub_uint32_t subnet_mask = 0xffffffffU; + grub_memcpy (ip4->subnet_mask, &subnet_mask, sizeof (ip4->subnet_mask)); + if (has_cidr) + *has_cidr = 0; + return GRUB_ERR_NONE; + } + } + else if (grub_efi_string_to_ip6_address (address, &ip6->address, &rest)) + { + *is_ip6 = 1; + if (*rest == '/') + { + grub_efi_uint8_t prefix_length; + + prefix_length = grub_strtoul (rest + 1, &rest, 0); + if (!grub_errno && prefix_length <= 128 && *rest == 0) + { + ip6->prefix_length = prefix_length; + ip6->is_anycast = 0; + if (has_cidr) + *has_cidr = 1; + return GRUB_ERR_NONE; + } + } + else if (*rest == 0 || *rest == ':') + { + ip6->prefix_length = 128; + ip6->is_anycast = 0; + if (has_cidr) + *has_cidr = 0; + return GRUB_ERR_NONE; + } + } + + grub_dprintf ("efinet", "unrecognised network address '%s'\n", address); + + return GRUB_ERR_NET_BAD_ADDRESS; +} + +static grub_efi_net_interface_t * +match_route (const char *server) +{ + grub_err_t err; + grub_efi_ip4_config2_manual_address_t ip4; + grub_efi_ip6_config_manual_address_t ip6; + grub_efi_net_interface_t *inf; + int is_ip6 = 0; + + err = grub_efi_net_parse_address (server, &ip4, &ip6, &is_ip6, 0); + + if (err) + return NULL; + + if (is_ip6) + { + struct grub_efi_net_device *dev; + grub_efi_net_ip_address_t addr; + + grub_memcpy (addr.ip6, ip6.address, sizeof(ip6.address)); + + for (dev = net_devices; dev; dev = dev->next) + if ((inf = efi_net_ip6_config->best_interface (dev, &addr))) + return inf; + } + else + { + struct grub_efi_net_device *dev; + grub_efi_net_ip_address_t addr; + + grub_memcpy (addr.ip4, ip4.address, sizeof(ip4.address)); + + for (dev = net_devices; dev; dev = dev->next) + if ((inf = efi_net_ip4_config->best_interface (dev, &addr))) + return inf; + } + + return 0; +} + +static void +grub_efi_net_add_pxebc_to_cards (void) +{ + grub_efi_uintn_t num_handles; + grub_efi_handle_t *handles; + grub_efi_handle_t *handle; + + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &pxe_io_guid, + 0, &num_handles); + if (!handles) + return; + + for (handle = handles; num_handles--; handle++) + { + grub_efi_device_path_t *dp, *ddp, *ldp; + grub_efi_pxe_t *pxe; + struct grub_efi_net_device *d; + int is_ip6 = 0; + + dp = grub_efi_get_device_path (*handle); + if (!dp) + continue; + + ddp = grub_efi_duplicate_device_path (dp); + ldp = grub_efi_find_last_device_path (ddp); + + if (ldp->type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + && ldp->subtype == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE) + { + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + } + else if (ldp->type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE + && ldp->subtype == GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE) + { + is_ip6 = 1; + ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE; + ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE; + ldp->length = sizeof (*ldp); + } + + for (d = net_devices; d; d = d->next) + if (grub_efi_compare_device_paths (ddp, grub_efi_get_device_path (d->handle)) == 0) + break; + + if (!d) + { + grub_free (ddp); + continue; + } + + pxe = grub_efi_open_protocol (*handle, &pxe_io_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + if (!pxe) + { + grub_free (ddp); + continue; + } + + if (is_ip6) + { + d->ip6_pxe_handle = *handle; + d->ip6_pxe = pxe; + } + else + { + d->ip4_pxe_handle = *handle; + d->ip4_pxe = pxe; + } + + grub_free (ddp); + } + + grub_free (handles); +} + +static void +set_ip_policy_to_static (void) +{ + struct grub_efi_net_device *dev; + + for (dev = net_devices; dev; dev = dev->next) + { + grub_efi_ip4_config2_policy_t ip4_policy = GRUB_EFI_IP4_CONFIG2_POLICY_STATIC; + + if (dev->ip4_config->set_data(dev->ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_POLICY, + sizeof (ip4_policy), &ip4_policy) != GRUB_EFI_SUCCESS) + grub_dprintf ("efinetfs", "could not set GRUB_EFI_IP4_CONFIG2_POLICY_STATIC on dev `%s'", dev->card_name); + + if (dev->ip6_config) + { + grub_efi_ip6_config_policy_t ip6_policy = GRUB_EFI_IP6_CONFIG_POLICY_MANUAL; + + if (dev->ip6_config->set_data(dev->ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_POLICY, + sizeof (ip6_policy), &ip6_policy) != GRUB_EFI_SUCCESS) + grub_dprintf ("efinetfs", "could not set GRUB_EFI_IP6_CONFIG_POLICY_MANUAL on dev `%s'", dev->card_name); + } + } +} + +/* FIXME: Do not fail if the card did not support any of the protocol (Eg http) */ +static void +grub_efi_net_find_cards (void) +{ + grub_efi_uintn_t num_handles; + grub_efi_handle_t *handles; + grub_efi_handle_t *handle; + int id; + + handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &ip4_config_guid, + 0, &num_handles); + if (!handles) + return; + + for (id = 0, handle = handles; num_handles--; handle++, id++) + { + grub_efi_device_path_t *dp; + grub_efi_ip4_config2_protocol_t *ip4_config; + grub_efi_ip6_config_protocol_t *ip6_config; + grub_efi_handle_t http_handle; + grub_efi_http_t *http; + grub_efi_handle_t dhcp4_handle; + grub_efi_dhcp4_protocol_t *dhcp4; + grub_efi_handle_t dhcp6_handle; + grub_efi_dhcp6_protocol_t *dhcp6; + + struct grub_efi_net_device *d; + + dp = grub_efi_get_device_path (*handle); + if (!dp) + continue; + + ip4_config = grub_efi_open_protocol (*handle, &ip4_config_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + if (!ip4_config) + continue; + + ip6_config = grub_efi_open_protocol (*handle, &ip6_config_guid, + GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL); + + http_handle = grub_efi_service_binding (*handle, &http_service_binding_guid); + grub_errno = GRUB_ERR_NONE; + http = (http_handle) + ? grub_efi_open_protocol (http_handle, &http_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL) + : NULL; + + dhcp4_handle = grub_efi_service_binding (*handle, &dhcp4_service_binding_guid); + grub_errno = GRUB_ERR_NONE; + dhcp4 = (dhcp4_handle) + ? grub_efi_open_protocol (dhcp4_handle, &dhcp4_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL) + : NULL; + + + dhcp6_handle = grub_efi_service_binding (*handle, &dhcp6_service_binding_guid); + grub_errno = GRUB_ERR_NONE; + dhcp6 = (dhcp6_handle) + ? grub_efi_open_protocol (dhcp6_handle, &dhcp6_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL) + : NULL; + + d = grub_malloc (sizeof (*d)); + if (!d) + { + grub_free (handles); + while (net_devices) + { + d = net_devices->next; + grub_free (net_devices); + net_devices = d; + } + return; + } + d->handle = *handle; + d->ip4_config = ip4_config; + d->ip6_config = ip6_config; + d->http_handle = http_handle; + d->http = http; + d->dhcp4_handle = dhcp4_handle; + d->dhcp4 = dhcp4; + d->dhcp6_handle = dhcp6_handle; + d->dhcp6 = dhcp6; + d->next = net_devices; + d->card_name = grub_xasprintf ("efinet%d", id); + d->net_interfaces = NULL; + net_devices = d; + } + + grub_efi_net_add_pxebc_to_cards (); + grub_free (handles); + set_ip_policy_to_static (); +} + +static void +listroutes_ip4 (struct grub_efi_net_device *netdev) +{ + char **routes; + + routes = NULL; + + if ((routes = efi_net_ip4_config->get_route_table (netdev))) + { + char **r; + + for (r = routes; *r; ++r) + grub_printf ("%s\n", *r); + } + + if (routes) + { + char **r; + + for (r = routes; *r; ++r) + grub_free (*r); + grub_free (routes); + } +} + +static void +listroutes_ip6 (struct grub_efi_net_device *netdev) +{ + char **routes; + + routes = NULL; + + if ((routes = efi_net_ip6_config->get_route_table (netdev))) + { + char **r; + + for (r = routes; *r; ++r) + grub_printf ("%s\n", *r); + } + + if (routes) + { + char **r; + + for (r = routes; *r; ++r) + grub_free (*r); + grub_free (routes); + } +} + +static grub_err_t +grub_cmd_efi_listroutes (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct grub_efi_net_device *netdev; + + for (netdev = net_devices; netdev; netdev = netdev->next) + { + listroutes_ip4 (netdev); + listroutes_ip6 (netdev); + } + + return GRUB_ERR_NONE; +} +static grub_err_t +grub_cmd_efi_listcards (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct grub_efi_net_device *dev; + + for (dev = net_devices; dev; dev = dev->next) + { + char *hw_addr; + + hw_addr = efi_net_ip4_config->get_hw_address (dev); + + if (hw_addr) + { + grub_printf ("%s %s\n", dev->card_name, hw_addr); + grub_free (hw_addr); + } + } + + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_efi_listaddrs (struct grub_command *cmd __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct grub_efi_net_device *dev; + grub_efi_net_interface_t *inf; + + for (dev = net_devices; dev; dev = dev->next) + for (inf = dev->net_interfaces; inf; inf = inf->next) + { + char *hw_addr = NULL; + char *addr = NULL; + + if ((hw_addr = efi_net_interface_get_hw_address (inf)) + && (addr = efi_net_interface_get_address (inf))) + grub_printf ("%s %s %s\n", inf->name, hw_addr, addr); + + if (hw_addr) + grub_free (hw_addr); + if (addr) + grub_free (addr); + } + + return GRUB_ERR_NONE; +} + +/* FIXME: support MAC specifying. */ +static grub_err_t +grub_cmd_efi_addaddr (struct grub_command *cmd __attribute__ ((unused)), + int argc, char **args) +{ + struct grub_efi_net_device *dev; + grub_err_t err; + grub_efi_ip4_config2_manual_address_t ip4; + grub_efi_ip6_config_manual_address_t ip6; + grub_efi_net_ip_manual_address_t net_ip; + int is_ip6 = 0; + int cidr = 0; + + if (argc != 3) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("three arguments expected")); + + for (dev = net_devices; dev; dev = dev->next) + { + if (grub_strcmp (dev->card_name, args[1]) == 0) + break; + } + + if (!dev) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("card not found")); + + err = grub_efi_net_parse_address (args[2], &ip4, &ip6, &is_ip6, &cidr); + + if (err) + return err; + + net_ip.is_ip6 = is_ip6; + if (is_ip6) + grub_memcpy (&net_ip.ip6, &ip6, sizeof(net_ip.ip6)); + else + grub_memcpy (&net_ip.ip4, &ip4, sizeof(net_ip.ip4)); + + if (!grub_efi_net_create_interface (dev, + args[0], + &net_ip, + cidr)) + return grub_errno; + + return GRUB_ERR_NONE; +} + +static struct grub_fs grub_efi_netfs; + +static grub_net_t +grub_net_open_real (const char *name __attribute__ ((unused))) +{ + grub_size_t protnamelen; + const char *protname, *server; + grub_net_t ret; + + net_interface = NULL; + + if (grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) == 0) + { + protname = "tftp"; + protnamelen = sizeof ("tftp") - 1; + server = name + sizeof ("pxe:") - 1; + } + else if (grub_strcmp (name, "pxe") == 0) + { + protname = "tftp"; + protnamelen = sizeof ("tftp") - 1; + server = default_server; + } + else + { + const char *comma; + + comma = grub_strchr (name, ','); + if (comma) + { + protnamelen = comma - name; + server = comma + 1; + protname = name; + } + else + { + protnamelen = grub_strlen (name); + server = default_server; + protname = name; + } + } + + if (!server) + { + grub_error (GRUB_ERR_NET_BAD_ADDRESS, + N_("no server is specified")); + return NULL; + } + + /*FIXME: Use DNS translate name to address */ + net_interface = match_route (server); + + if (!net_interface && net_default_interface) + { + net_interface = net_default_interface; + grub_dprintf ("efinet", "interface lookup failed, using default '%s'\n", + net_interface->name); + } + + /*XXX: should we check device with default gateway ? */ + if (!net_interface) + { + grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' no route found"), + name); + return NULL; + } + + if ((protnamelen == (sizeof ("https") - 1) + && grub_memcmp ("https", protname, protnamelen) == 0)) + { + net_interface->io = &io_http; + net_interface->io_type = 1; + } + else if ((protnamelen == (sizeof ("http") - 1) + && grub_memcmp ("http", protname, protnamelen) == 0)) + { + net_interface->io = &io_http; + net_interface->io_type = 0; + } + else if (protnamelen == (sizeof ("tftp") - 1) + && grub_memcmp ("tftp", protname, protnamelen) == 0) + { + net_interface->io = &io_pxe; + net_interface->io_type = 0; + } + else + { + grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' not found"), + name); + return NULL; + } + + /*XXX: Should we try to avoid doing excess "reconfigure" here ??? */ + efi_net_interface (configure); + + ret = grub_zalloc (sizeof (*ret)); + if (!ret) + return NULL; + + ret->server = grub_strdup (server); + if (!ret->server) + { + grub_free (ret); + return NULL; + } + + ret->fs = &grub_efi_netfs; + return ret; +} +#if 0 +static grub_command_t cmd_efi_lsaddr; +static grub_command_t cmd_efi_lscards; +static grub_command_t cmd_efi_lsroutes; +static grub_command_t cmd_efi_addaddr; +#endif + +static struct grub_fs grub_efi_netfs = + { + .name = "efi netfs", + .fs_dir = grub_efi_netfs_dir, + .fs_open = grub_efi_netfs_open, + .fs_read = grub_efi_netfs_read, + .fs_close = grub_efi_netfs_close, + .fs_label = NULL, + .fs_uuid = NULL, + .fs_mtime = NULL, + }; + +int +grub_efi_net_boot_from_https (void) +{ + grub_efi_loaded_image_t *image = NULL; + grub_efi_device_path_t *dp; + + image = grub_efi_get_loaded_image (grub_efi_image_handle); + if (!image) + return 0; + + dp = grub_efi_get_device_path (image->device_handle); + + while (1) + { + grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); + grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); + grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp); + + if ((type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE) + && (subtype == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)) + { + grub_efi_uri_device_path_t *uri_dp = (grub_efi_uri_device_path_t *) dp; + grub_dprintf ("efinet", "url:%s\n", (const char *)uri_dp->uri); + return (grub_strncmp ((const char *)uri_dp->uri, "https://", sizeof ("https://") - 1) == 0 || + grub_strncmp ((const char *)uri_dp->uri, "http://", sizeof ("http://") - 1) == 0); + } + + if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) + break; + dp = (grub_efi_device_path_t *) ((char *) dp + len); + } + + return 0; +} + +int +grub_efi_net_boot_from_opa (void) +{ + grub_efi_loaded_image_t *image = NULL; + grub_efi_device_path_t *dp; + + image = grub_efi_get_loaded_image (grub_efi_image_handle); + if (!image) + return 0; + + dp = grub_efi_get_device_path (image->device_handle); + + while (1) + { + grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp); + grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp); + grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp); + + if ((type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE) + && (subtype == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE)) + { + grub_efi_mac_address_device_path_t *mac_dp = (grub_efi_mac_address_device_path_t *)dp; + return (mac_dp->if_type == 0xC7) ? 1 : 0; + } + + if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp)) + break; + dp = (grub_efi_device_path_t *) ((char *) dp + len); + } + + return 0; +} + +static char * +grub_env_write_readonly (struct grub_env_var *var __attribute__ ((unused)), + const char *val __attribute__ ((unused))) +{ + return NULL; +} + +grub_command_func_t grub_efi_net_list_routes = grub_cmd_efi_listroutes; +grub_command_func_t grub_efi_net_list_cards = grub_cmd_efi_listcards; +grub_command_func_t grub_efi_net_list_addrs = grub_cmd_efi_listaddrs; +grub_command_func_t grub_efi_net_add_addr = grub_cmd_efi_addaddr; + +int +grub_efi_net_fs_init () +{ + grub_efi_net_find_cards (); + grub_efi_net_config = grub_efi_net_config_real; + grub_net_open = grub_net_open_real; + grub_register_variable_hook ("net_default_server", grub_efi_net_var_get_server, + grub_efi_net_var_set_server); + grub_env_export ("net_default_server"); + grub_register_variable_hook ("pxe_default_server", grub_efi_net_var_get_server, + grub_efi_net_var_set_server); + grub_env_export ("pxe_default_server"); + grub_register_variable_hook ("net_default_interface", 0, + grub_efi_net_var_set_interface); + grub_env_export ("net_default_interface"); + grub_register_variable_hook ("net_default_ip", grub_efi_net_var_get_ip, + 0); + grub_env_export ("net_default_ip"); + grub_register_variable_hook ("net_default_mac", grub_efi_net_var_get_mac, + 0); + grub_env_export ("net_default_mac"); + + grub_env_set ("grub_netfs_type", "efi"); + grub_register_variable_hook ("grub_netfs_type", 0, grub_env_write_readonly); + grub_env_export ("grub_netfs_type"); + + return 1; +} + +void +grub_efi_net_fs_fini (void) +{ + grub_env_unset ("grub_netfs_type"); + grub_efi_net_unset_interface_vars (); + grub_register_variable_hook ("net_default_server", 0, 0); + grub_env_unset ("net_default_server"); + grub_register_variable_hook ("net_default_interface", 0, 0); + grub_env_unset ("net_default_interface"); + grub_register_variable_hook ("pxe_default_server", 0, 0); + grub_env_unset ("pxe_default_server"); + grub_register_variable_hook ("net_default_ip", 0, 0); + grub_env_unset ("net_default_ip"); + grub_register_variable_hook ("net_default_mac", 0, 0); + grub_env_unset ("net_default_mac"); + grub_efi_net_config = NULL; + grub_net_open = NULL; + grub_fs_unregister (&grub_efi_netfs); +} diff --git a/grub-core/net/efi/pxe.c b/grub-core/net/efi/pxe.c new file mode 100644 index 0000000000..e828fb23e9 --- /dev/null +++ b/grub-core/net/efi/pxe.c @@ -0,0 +1,424 @@ + +#include +#include +#include +#include +#include + +static grub_efi_ip6_config_manual_address_t * +efi_ip6_config_manual_address (grub_efi_ip6_config_protocol_t *ip6_config) +{ + grub_efi_uintn_t sz; + grub_efi_status_t status; + grub_efi_ip6_config_manual_address_t *manual_address; + + sz = sizeof (*manual_address); + manual_address = grub_malloc (sz); + if (!manual_address) + return NULL; + + status = ip6_config->get_data(ip6_config, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, + &sz, manual_address); + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (manual_address); + return NULL; + } + + return manual_address; +} + +static grub_efi_ip4_config2_manual_address_t * +efi_ip4_config_manual_address (grub_efi_ip4_config2_protocol_t *ip4_config) +{ + grub_efi_uintn_t sz; + grub_efi_status_t status; + grub_efi_ip4_config2_manual_address_t *manual_address; + + sz = sizeof (*manual_address); + manual_address = grub_malloc (sz); + if (!manual_address) + return NULL; + + status = ip4_config->get_data(ip4_config, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, + &sz, manual_address); + + if (status != GRUB_EFI_SUCCESS) + { + grub_free (manual_address); + return NULL; + } + + return manual_address; +} + +static void +pxe_configure (struct grub_efi_net_device *dev, int prefer_ip6) +{ + grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe; + + grub_efi_pxe_mode_t *mode = pxe->mode; + + if (!mode->started) + { + grub_efi_status_t status; + status = pxe->start(pxe, prefer_ip6); + + if (status != GRUB_EFI_SUCCESS) + grub_printf ("Couldn't start PXE\n"); + } + +#if 0 + grub_printf ("PXE STARTED: %u\n", mode->started); + grub_printf ("PXE USING IPV6: %u\n", mode->using_ipv6); +#endif + + if (mode->using_ipv6) + { + grub_efi_ip6_config_manual_address_t *manual_address; + manual_address = efi_ip6_config_manual_address (dev->ip6_config); + + if (manual_address && + grub_memcmp (manual_address->address, mode->station_ip.v6, sizeof (manual_address->address)) != 0) + { + grub_efi_status_t status; + grub_efi_pxe_ip_address_t station_ip; + + grub_memcpy (station_ip.v6.addr, manual_address->address, sizeof (station_ip.v6.addr)); + status = pxe->set_station_ip(pxe, &station_ip, NULL); + + if (status != GRUB_EFI_SUCCESS) + grub_printf ("Couldn't set station ip\n"); + + grub_free (manual_address); + } + } + else + { + grub_efi_ip4_config2_manual_address_t *manual_address; + manual_address = efi_ip4_config_manual_address (dev->ip4_config); + + if (manual_address && + grub_memcmp (manual_address->address, mode->station_ip.v4, sizeof (manual_address->address)) != 0) + { + grub_efi_status_t status; + grub_efi_pxe_ip_address_t station_ip; + grub_efi_pxe_ip_address_t subnet_mask; + + grub_memcpy (station_ip.v4.addr, manual_address->address, sizeof (station_ip.v4.addr)); + grub_memcpy (subnet_mask.v4.addr, manual_address->subnet_mask, sizeof (subnet_mask.v4.addr)); + + status = pxe->set_station_ip(pxe, &station_ip, &subnet_mask); + + if (status != GRUB_EFI_SUCCESS) + grub_printf ("Couldn't set station ip\n"); + + grub_free (manual_address); + } + } + +#if 0 + if (mode->using_ipv6) + { + grub_printf ("PXE STATION IP: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n", + mode->station_ip.v6.addr[0], + mode->station_ip.v6.addr[1], + mode->station_ip.v6.addr[2], + mode->station_ip.v6.addr[3], + mode->station_ip.v6.addr[4], + mode->station_ip.v6.addr[5], + mode->station_ip.v6.addr[6], + mode->station_ip.v6.addr[7], + mode->station_ip.v6.addr[8], + mode->station_ip.v6.addr[9], + mode->station_ip.v6.addr[10], + mode->station_ip.v6.addr[11], + mode->station_ip.v6.addr[12], + mode->station_ip.v6.addr[13], + mode->station_ip.v6.addr[14], + mode->station_ip.v6.addr[15]); + } + else + { + grub_printf ("PXE STATION IP: %d.%d.%d.%d\n", + mode->station_ip.v4.addr[0], + mode->station_ip.v4.addr[1], + mode->station_ip.v4.addr[2], + mode->station_ip.v4.addr[3]); + grub_printf ("PXE SUBNET MASK: %d.%d.%d.%d\n", + mode->subnet_mask.v4.addr[0], + mode->subnet_mask.v4.addr[1], + mode->subnet_mask.v4.addr[2], + mode->subnet_mask.v4.addr[3]); + } +#endif + + /* TODO: Set The Station IP to the IP2 Config */ +} + +static int +parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest) +{ + grub_uint16_t newip[8]; + const char *ptr = val; + int word, quaddot = -1; + int bracketed = 0; + + if (ptr[0] == '[') { + bracketed = 1; + ptr++; + } + + if (ptr[0] == ':' && ptr[1] != ':') + return 0; + if (ptr[0] == ':') + ptr++; + + for (word = 0; word < 8; word++) + { + unsigned long t; + if (*ptr == ':') + { + quaddot = word; + word--; + ptr++; + continue; + } + t = grub_strtoul (ptr, &ptr, 16); + if (grub_errno) + { + grub_errno = GRUB_ERR_NONE; + break; + } + if (t & ~0xffff) + return 0; + newip[word] = grub_cpu_to_be16 (t); + if (*ptr != ':') + break; + ptr++; + } + if (quaddot == -1 && word < 7) + return 0; + if (quaddot != -1) + { + grub_memmove (&newip[quaddot + 7 - word], &newip[quaddot], + (word - quaddot + 1) * sizeof (newip[0])); + grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0])); + } + grub_memcpy (ip, newip, 16); + if (bracketed && *ptr == ']') { + ptr++; + } + if (rest) + *rest = ptr; + return 1; +} + +static grub_err_t +pxe_open (struct grub_efi_net_device *dev, + int prefer_ip6, + grub_file_t file, + const char *filename, + int type __attribute__((unused))) +{ + int i; + const char *p; + grub_efi_status_t status; + grub_efi_pxe_ip_address_t server_ip; + grub_efi_uint64_t file_size = 0; + grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe; + + if (pxe->mode->using_ipv6) + { + const char *rest; + grub_uint64_t ip6[2]; + if (parse_ip6 (file->device->net->server, ip6, &rest) && *rest == 0) + grub_memcpy (server_ip.v6.addr, ip6, sizeof (server_ip.v6.addr)); + /* TODO: ERROR Handling Here */ +#if 0 + grub_printf ("PXE SERVER IP: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n", + server_ip.v6.addr[0], + server_ip.v6.addr[1], + server_ip.v6.addr[2], + server_ip.v6.addr[3], + server_ip.v6.addr[4], + server_ip.v6.addr[5], + server_ip.v6.addr[6], + server_ip.v6.addr[7], + server_ip.v6.addr[8], + server_ip.v6.addr[9], + server_ip.v6.addr[10], + server_ip.v6.addr[11], + server_ip.v6.addr[12], + server_ip.v6.addr[13], + server_ip.v6.addr[14], + server_ip.v6.addr[15]); +#endif + } + else + { + for (i = 0, p = file->device->net->server; i < 4; ++i, ++p) + server_ip.v4.addr[i] = grub_strtoul (p, &p, 10); + } + + status = pxe->mtftp( + pxe, + GRUB_EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + NULL, + 0, + &file_size, + NULL, + &server_ip, + (grub_efi_char8_t *)filename, + NULL, + 0); + + if (status != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_IO, "Couldn't get file size"); + + file->size = (grub_off_t)file_size; + file->not_easily_seekable = 0; + file->data = 0; + file->device->net->offset = 0; + + return GRUB_ERR_NONE; +} + +static grub_err_t +pxe_close (struct grub_efi_net_device *dev __attribute__((unused)), + int prefer_ip6 __attribute__((unused)), + grub_file_t file __attribute__((unused))) +{ + file->offset = 0; + file->size = 0; + file->device->net->offset = 0; + + if (file->data) + { + grub_free (file->data); + file->data = NULL; + } + + return GRUB_ERR_NONE; +} + +static grub_ssize_t +pxe_read (struct grub_efi_net_device *dev, + int prefer_ip6, + grub_file_t file, + char *buf, + grub_size_t len) +{ + int i; + const char *p; + grub_efi_status_t status; + grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe; + grub_efi_uint64_t bufsz = len; + grub_efi_pxe_ip_address_t server_ip; + char *buf2 = NULL; + + if (file->data) + { + /* TODO: RANGE Check for offset and file size */ + grub_memcpy (buf, (char*)file->data + file->device->net->offset, len); + file->device->net->offset += len; + return len; + } + + if (file->device->net->offset) + { + grub_error (GRUB_ERR_BUG, "No Offet Read Possible"); + grub_print_error (); + return 0; + } + + if (pxe->mode->using_ipv6) + { + const char *rest; + grub_uint64_t ip6[2]; + if (parse_ip6 (file->device->net->server, ip6, &rest) && *rest == 0) + grub_memcpy (server_ip.v6.addr, ip6, sizeof (server_ip.v6.addr)); + /* TODO: ERROR Handling Here */ + } + else + { + for (i = 0, p = file->device->net->server; i < 4; ++i, ++p) + server_ip.v4.addr[i] = grub_strtoul (p, &p, 10); + } + + status = pxe->mtftp( + pxe, + GRUB_EFI_PXE_BASE_CODE_TFTP_READ_FILE, + buf, + 0, + &bufsz, + NULL, + &server_ip, + (grub_efi_char8_t *)file->device->net->name, + NULL, + 0); + + if (bufsz != file->size) + { + grub_error (GRUB_ERR_BUG, "Short read should not happen here"); + grub_print_error (); + return 0; + } + + if (status == GRUB_EFI_BUFFER_TOO_SMALL) + { + + buf2 = grub_malloc (bufsz); + + if (!buf2) + { + grub_error (GRUB_ERR_OUT_OF_MEMORY, "ERROR OUT OF MEMORY"); + grub_print_error (); + return 0; + } + + status = pxe->mtftp( + pxe, + GRUB_EFI_PXE_BASE_CODE_TFTP_READ_FILE, + buf2, + 0, + &bufsz, + NULL, + &server_ip, + (grub_efi_char8_t *)file->device->net->name, + NULL, + 0); + } + + if (status != GRUB_EFI_SUCCESS) + { + if (buf2) + grub_free (buf2); + + grub_error (GRUB_ERR_IO, "Failed to Read File"); + grub_print_error (); + return 0; + } + + if (buf2) + grub_memcpy (buf, buf2, len); + + file->device->net->offset = len; + + if (buf2) + file->data = buf2; + + return len; +} + +struct grub_efi_net_io io_pxe = + { + .configure = pxe_configure, + .open = pxe_open, + .read = pxe_read, + .close = pxe_close + }; + diff --git a/grub-core/net/ethernet.c b/grub-core/net/ethernet.c index 707bbb12cb..1441a1bacf 100644 --- a/grub-core/net/ethernet.c +++ b/grub-core/net/ethernet.c @@ -29,13 +29,6 @@ #define LLCADDRMASK 0x7f -struct etherhdr -{ - grub_uint8_t dst[6]; - grub_uint8_t src[6]; - grub_uint16_t type; -} GRUB_PACKED; - struct llchdr { grub_uint8_t dsap; @@ -55,13 +48,15 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf, grub_net_link_level_address_t target_addr, grub_net_ethertype_t ethertype) { - struct etherhdr *eth; + grub_uint8_t *eth; grub_err_t err; - grub_uint8_t etherhdr_size; - grub_uint16_t vlantag_id = grub_cpu_to_be16_compile_time (VLANTAG_IDENTIFIER); + grub_uint32_t vlantag = 0; + grub_uint8_t hw_addr_len = inf->card->default_address.len; + grub_uint8_t etherhdr_size = 2 * hw_addr_len + 2; - etherhdr_size = sizeof (*eth); - COMPILE_TIME_ASSERT (sizeof (*eth) + 4 < GRUB_NET_MAX_LINK_HEADER_SIZE); + /* Source and destination link addresses + ethertype + vlan tag */ + COMPILE_TIME_ASSERT ((GRUB_NET_MAX_LINK_ADDRESS_SIZE * 2 + 2 + 4) < + GRUB_NET_MAX_LINK_HEADER_SIZE); /* Increase ethernet header in case of vlantag */ if (inf->vlantag != 0) @@ -70,11 +65,22 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf, err = grub_netbuff_push (nb, etherhdr_size); if (err) return err; - eth = (struct etherhdr *) nb->data; - grub_memcpy (eth->dst, target_addr.mac, 6); - grub_memcpy (eth->src, inf->hwaddress.mac, 6); + eth = nb->data; + grub_memcpy (eth, target_addr.mac, hw_addr_len); + eth += hw_addr_len; + grub_memcpy (eth, inf->hwaddress.mac, hw_addr_len); + eth += hw_addr_len; + + /* Check if a vlan-tag is present. */ + if (vlantag != 0) + { + *((grub_uint32_t *)eth) = grub_cpu_to_be32 (vlantag); + eth += sizeof (vlantag); + } + + /* Write ethertype */ + *((grub_uint16_t*) eth) = grub_cpu_to_be16 (ethertype); - eth->type = grub_cpu_to_be16 (ethertype); if (!inf->card->opened) { err = GRUB_ERR_NONE; @@ -85,19 +91,6 @@ send_ethernet_packet (struct grub_net_network_level_interface *inf, inf->card->opened = 1; } - /* Check and add a vlan-tag if needed. */ - if (inf->vlantag != 0) - { - /* Move eth type to the right */ - grub_memcpy ((char *) nb->data + etherhdr_size - 2, - (char *) nb->data + etherhdr_size - 6, 2); - - /* Add the tag in the middle */ - grub_uint16_t vlan = grub_cpu_to_be16 (inf->vlantag); - grub_memcpy ((char *) nb->data + etherhdr_size - 6, &vlantag_id, 2); - grub_memcpy ((char *) nb->data + etherhdr_size - 4, &vlan, 2); - } - return inf->card->driver->send (inf->card, nb); } @@ -105,31 +98,40 @@ grub_err_t grub_net_recv_ethernet_packet (struct grub_net_buff *nb, struct grub_net_card *card) { - struct etherhdr *eth; + grub_uint8_t *eth; struct llchdr *llch; struct snaphdr *snaph; grub_net_ethertype_t type; grub_net_link_level_address_t hwaddress; grub_net_link_level_address_t src_hwaddress; grub_err_t err; - grub_uint8_t etherhdr_size = sizeof (*eth); + grub_uint8_t hw_addr_len = card->default_address.len; + grub_uint8_t etherhdr_size = 2 * hw_addr_len + 2; grub_uint16_t vlantag = 0; + eth = nb->data; - /* Check if a vlan-tag is present. If so, the ethernet header is 4 bytes */ - /* longer than the original one. The vlantag id is extracted and the header */ - /* is reseted to the original size. */ - if (grub_get_unaligned16 (nb->data + etherhdr_size - 2) == grub_cpu_to_be16_compile_time (VLANTAG_IDENTIFIER)) + hwaddress.type = card->default_address.type; + hwaddress.len = hw_addr_len; + grub_memcpy (hwaddress.mac, eth, hw_addr_len); + eth += hw_addr_len; + + src_hwaddress.type = card->default_address.type; + src_hwaddress.len = hw_addr_len; + grub_memcpy (src_hwaddress.mac, eth, hw_addr_len); + eth += hw_addr_len; + + type = grub_be_to_cpu16 (*(grub_uint16_t*)(eth)); + if (type == VLANTAG_IDENTIFIER) { - vlantag = grub_be_to_cpu16 (grub_get_unaligned16 (nb->data + etherhdr_size)); + /* Skip vlan tag */ + eth += 2; + vlantag = grub_be_to_cpu16 (*(grub_uint16_t*)(eth)); etherhdr_size += 4; - /* Move eth type to the original position */ - grub_memcpy((char *) nb->data + etherhdr_size - 6, - (char *) nb->data + etherhdr_size - 2, 2); + eth += 2; + type = grub_be_to_cpu16 (*(grub_uint16_t*)(eth)); } - eth = (struct etherhdr *) nb->data; - type = grub_be_to_cpu16 (eth->type); err = grub_netbuff_pull (nb, etherhdr_size); if (err) return err; @@ -149,11 +151,6 @@ grub_net_recv_ethernet_packet (struct grub_net_buff *nb, } } - hwaddress.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (hwaddress.mac, eth->dst, sizeof (hwaddress.mac)); - src_hwaddress.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (src_hwaddress.mac, eth->src, sizeof (src_hwaddress.mac)); - switch (type) { /* ARP packet. */ diff --git a/grub-core/net/http.c b/grub-core/net/http.c index f389bf03d9..40df19426c 100644 --- a/grub-core/net/http.c +++ b/grub-core/net/http.c @@ -26,10 +26,15 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); -#define HTTP_PORT ((grub_uint16_t) 80) +enum + { + HTTP_PORT = 80, + HTTP_MAX_CHUNK_SIZE = 0x80000000 + }; typedef struct http_data { @@ -82,6 +87,8 @@ parse_line (grub_file_t file, http_data_t data, char *ptr, grub_size_t len) if (data->in_chunk_len == 2) { data->chunk_rem = grub_strtoul (ptr, 0, 16); + if (data->chunk_rem > HTTP_MAX_CHUNK_SIZE) + return GRUB_ERR_NET_PACKET_TOO_BIG; grub_errno = GRUB_ERR_NONE; if (data->chunk_rem == 0) { @@ -496,13 +503,20 @@ http_open (struct grub_file *file, const char *filename) { grub_err_t err; struct http_data *data; + const char *http_path; data = grub_zalloc (sizeof (*data)); if (!data) return grub_errno; file->size = GRUB_FILE_SIZE_UNKNOWN; - data->filename = grub_strdup (filename); + /* If path is relative, prepend http_path */ + http_path = grub_env_get ("http_path"); + if (http_path && filename[0] != '/') + data->filename = grub_xasprintf ("%s/%s", http_path, filename); + else + data->filename = grub_strdup (filename); + if (!data->filename) { grub_free (data); diff --git a/grub-core/net/icmp6.c b/grub-core/net/icmp6.c index 9a8c451128..5924fd53ac 100644 --- a/grub-core/net/icmp6.c +++ b/grub-core/net/icmp6.c @@ -231,8 +231,9 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, && ohdr->len == 1) { grub_net_link_level_address_t ll_address; - ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac)); + ll_address.type = card->default_address.type; + ll_address.len = card->default_address.len; + grub_memcpy (ll_address.mac, ohdr + 1, ll_address.len); grub_net_link_layer_add_address (card, source, &ll_address, 0); } } @@ -335,8 +336,9 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, && ohdr->len == 1) { grub_net_link_level_address_t ll_address; - ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac)); + ll_address.type = card->default_address.type; + ll_address.len = card->default_address.len; + grub_memcpy (ll_address.mac, ohdr + 1, ll_address.len); grub_net_link_layer_add_address (card, source, &ll_address, 0); } } @@ -384,8 +386,9 @@ grub_net_recv_icmp6_packet (struct grub_net_buff *nb, && ohdr->len == 1) { grub_net_link_level_address_t ll_address; - ll_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memcpy (ll_address.mac, ohdr + 1, sizeof (ll_address.mac)); + ll_address.type = card->default_address.type; + ll_address.len = card->default_address.len; + grub_memcpy (ll_address.mac, ohdr + 1, ll_address.len); grub_net_link_layer_add_address (card, source, &ll_address, 0); } if (ohdr->type == OPTION_PREFIX && ohdr->len == 4) diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c index 3c3d0be0e5..3c475762cc 100644 --- a/grub-core/net/ip.c +++ b/grub-core/net/ip.c @@ -240,6 +240,45 @@ handle_dgram (struct grub_net_buff *nb, { struct udphdr *udph; udph = (struct udphdr *) nb->data; + + if (proto == GRUB_NET_IP_UDP && udph->dst == grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT)) + { + if (udph->chksum) + { + grub_uint16_t chk, expected; + chk = udph->chksum; + udph->chksum = 0; + expected = grub_net_ip_transport_checksum (nb, + GRUB_NET_IP_UDP, + source, + dest); + if (expected != chk) + { + grub_dprintf ("net", "Invalid UDP checksum. " + "Expected %x, got %x\n", + grub_be_to_cpu16 (expected), + grub_be_to_cpu16 (chk)); + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + udph->chksum = chk; + } + + err = grub_netbuff_pull (nb, sizeof (*udph)); + if (err) + { + grub_netbuff_free (nb); + return err; + } + + err = grub_net_process_dhcp6 (nb, card); + if (err) + grub_print_error (); + + grub_netbuff_free (nb); + return GRUB_ERR_NONE; + } + if (proto == GRUB_NET_IP_UDP && grub_be_to_cpu16 (udph->dst) == 68) { const struct grub_net_bootp_packet *bootp; @@ -277,8 +316,8 @@ handle_dgram (struct grub_net_buff *nb, if (inf->card == card && inf->address.type == GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV && inf->hwaddress.type == GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET - && grub_memcmp (inf->hwaddress.mac, &bootp->mac_addr, - sizeof (inf->hwaddress.mac)) == 0) + && (grub_memcmp (inf->hwaddress.mac, &bootp->mac_addr, + bootp->hw_len) == 0 || bootp->hw_len == 0)) { grub_net_process_dhcp (nb, inf); grub_netbuff_free (nb); diff --git a/grub-core/net/net.c b/grub-core/net/net.c index 8cad4fb6d4..f9a5edcef7 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -32,6 +32,10 @@ #include #include #include +#include +#ifdef GRUB_MACHINE_EFI +#include +#endif GRUB_MOD_LICENSE ("GPLv3+"); @@ -87,8 +91,8 @@ grub_net_link_layer_add_address (struct grub_net_card *card, /* Add sender to cache table. */ if (card->link_layer_table == NULL) { - card->link_layer_table = grub_zalloc (LINK_LAYER_CACHE_SIZE - * sizeof (card->link_layer_table[0])); + card->link_layer_table = grub_calloc (LINK_LAYER_CACHE_SIZE, + sizeof (card->link_layer_table[0])); if (card->link_layer_table == NULL) return; } @@ -133,8 +137,9 @@ grub_net_link_layer_resolve (struct grub_net_network_level_interface *inf, << 48) && proto_addr->ipv6[1] == (grub_be_to_cpu64_compile_time (1)))) { - hw_addr->type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; - grub_memset (hw_addr->mac, -1, 6); + hw_addr->type = inf->card->default_address.type; + hw_addr->len = inf->card->default_address.len; + grub_memset (hw_addr->mac, -1, hw_addr->len); return GRUB_ERR_NONE; } @@ -142,6 +147,7 @@ grub_net_link_layer_resolve (struct grub_net_network_level_interface *inf, && ((grub_be_to_cpu64 (proto_addr->ipv6[0]) >> 56) == 0xff)) { hw_addr->type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET; + hw_addr->len = inf->card->default_address.len; hw_addr->mac[0] = 0x33; hw_addr->mac[1] = 0x33; hw_addr->mac[2] = ((grub_be_to_cpu64 (proto_addr->ipv6[1]) >> 24) & 0xff); @@ -206,6 +212,7 @@ grub_net_ipv6_get_slaac (struct grub_net_card *card, { struct grub_net_slaac_mac_list *slaac; char *ptr; + grub_size_t sz; for (slaac = card->slaac_list; slaac; slaac = slaac->next) if (grub_net_hwaddr_cmp (&slaac->address, hwaddr) == 0) @@ -215,9 +222,21 @@ grub_net_ipv6_get_slaac (struct grub_net_card *card, if (!slaac) return NULL; - slaac->name = grub_malloc (grub_strlen (card->name) - + GRUB_NET_MAX_STR_HWADDR_LEN - + sizeof (":slaac")); + if (grub_add (grub_strlen (card->name), + (GRUB_NET_MAX_STR_HWADDR_LEN + sizeof (":slaac")), &sz)) + { + grub_free (slaac); + grub_error (GRUB_ERR_OUT_OF_RANGE, + "overflow detected while obtaining size of slaac name"); + return NULL; + } + + slaac->name = grub_malloc (sz); + if (slaac->name == NULL) + { + grub_free (slaac); + return NULL; + } ptr = grub_stpcpy (slaac->name, card->name); if (grub_net_hwaddr_cmp (&card->default_address, hwaddr) != 0) { @@ -288,6 +307,7 @@ grub_net_ipv6_get_link_local (struct grub_net_card *card, char *name; char *ptr; grub_net_network_level_address_t addr; + grub_size_t sz; addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; addr.ipv6[0] = grub_cpu_to_be64_compile_time (0xfe80ULL << 48); @@ -302,9 +322,14 @@ grub_net_ipv6_get_link_local (struct grub_net_card *card, return inf; } - name = grub_malloc (grub_strlen (card->name) - + GRUB_NET_MAX_STR_HWADDR_LEN - + sizeof (":link")); + if (grub_add (grub_strlen (card->name), + (GRUB_NET_MAX_STR_HWADDR_LEN + sizeof (":link")), &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, + "overflow detected while obtaining size of link name"); + return NULL; + } + name = grub_malloc (sz); if (!name) return NULL; @@ -772,23 +797,23 @@ grub_net_addr_to_str (const grub_net_network_level_address_t *target, char *buf) void grub_net_hwaddr_to_str (const grub_net_link_level_address_t *addr, char *str) { - str[0] = 0; - switch (addr->type) + char *ptr; + unsigned i; + int maxstr; + + if (addr->len > GRUB_NET_MAX_LINK_ADDRESS_SIZE) { - case GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET: - { - char *ptr; - unsigned i; - for (ptr = str, i = 0; i < ARRAY_SIZE (addr->mac); i++) - { - grub_snprintf (ptr, GRUB_NET_MAX_STR_HWADDR_LEN - (ptr - str), - "%02x:", addr->mac[i] & 0xff); - ptr += (sizeof ("XX:") - 1); - } - return; - } + str[0] = 0; + grub_printf (_("Unsupported hw address type %d len %d\n"), + addr->type, addr->len); + return; + } + maxstr = addr->len * grub_strlen ("XX:"); + for (ptr = str, i = 0; i < addr->len; i++) + { + ptr += grub_snprintf (ptr, maxstr - (ptr - str), + "%02x:", addr->mac[i] & 0xff); } - grub_printf (_("Unsupported hw address type %d\n"), addr->type); } void @@ -813,13 +838,17 @@ grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a, return -1; if (a->type > b->type) return +1; - switch (a->type) + if (a->len < b->len) + return -1; + if (a->len > b->len) + return +1; + if (a->len > GRUB_NET_MAX_LINK_ADDRESS_SIZE) { - case GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET: - return grub_memcmp (a->mac, b->mac, sizeof (a->mac)); + grub_printf (_("Unsupported hw address type %d len %d\n"), + a->type, a->len); + return + 1; } - grub_printf (_("Unsupported hw address type %d\n"), a->type); - return 1; + return grub_memcmp (a->mac, b->mac, a->len); } int @@ -984,6 +1013,111 @@ grub_net_network_level_interface_register (struct grub_net_network_level_interfa grub_net_network_level_interfaces = inter; } +int +grub_ipv6_get_masksize (grub_uint16_t *be_mask) +{ + grub_uint8_t *mask; + grub_uint16_t mask16[8]; + int x, y; + int ret = 128; + + grub_memcpy (mask16, be_mask, sizeof (mask16)); + for (x = 0; x < 8; x++) + mask16[x] = grub_be_to_cpu16 (mask16[x]); + + mask = (grub_uint8_t *)mask16; + + for (x = 15; x >= 0; x--) + { + grub_uint8_t octet = mask[x]; + if (!octet) + { + ret -= 8; + continue; + } + for (y = 0; y < 8; y++) + { + if (octet & (1 << y)) + break; + else + ret--; + } + break; + } + + return ret; +} + +grub_err_t +grub_net_add_ipv6_local (struct grub_net_network_level_interface *inter, + int mask) +{ + struct grub_net_route *route; + + if (inter->address.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6) + return 0; + + if (mask == -1) + mask = grub_ipv6_get_masksize ((grub_uint16_t *)inter->address.ipv6); + + if (mask == -1) + return 0; + + route = grub_zalloc (sizeof (*route)); + if (!route) + return grub_errno; + + route->name = grub_xasprintf ("%s:local", inter->name); + if (!route->name) + { + grub_free (route); + return grub_errno; + } + + route->target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6; + grub_memcpy (route->target.ipv6.base, inter->address.ipv6, + sizeof (inter->address.ipv6)); + route->target.ipv6.masksize = mask; + route->is_gateway = 0; + route->interface = inter; + + grub_net_route_register (route); + + return 0; +} + +void +grub_net_network_level_interface_unregister (struct grub_net_network_level_interface *inter) +{ + char *name; + + { + char buf[GRUB_NET_MAX_STR_HWADDR_LEN]; + + grub_net_hwaddr_to_str (&inter->hwaddress, buf); + name = grub_xasprintf ("net_%s_mac", inter->name); + if (name != NULL) + grub_register_variable_hook (name, NULL, NULL); + grub_free (name); + } + + { + char buf[GRUB_NET_MAX_STR_ADDR_LEN]; + + grub_net_addr_to_str (&inter->address, buf); + name = grub_xasprintf ("net_%s_ip", inter->name); + if (name != NULL) + grub_register_variable_hook (name, NULL, NULL); + grub_free (name); + } + + inter->card->num_ifaces--; + *inter->prev = inter->next; + if (inter->next) + inter->next->prev = inter->prev; + inter->next = 0; + inter->prev = 0; +} grub_err_t grub_net_add_ipv4_local (struct grub_net_network_level_interface *inter, @@ -1402,9 +1536,15 @@ grub_net_open_real (const char *name) if (grub_strchr (port_start + 1, ':')) { int iplen = grub_strlen (server); + grub_size_t sz; /* Bracket bare IPv6 addr. */ - host = grub_malloc (iplen + 3); + if (grub_add (iplen, 3, &sz)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow detected while obtaining length of host")); + return NULL; + } + host = grub_malloc (sz); if (!host) return NULL; @@ -1659,6 +1799,7 @@ grub_env_set_net_property (const char *intername, const char *suffix, { char *varname, *varvalue; char *ptr; + grub_size_t sz; varname = grub_xasprintf ("net_%s_%s", intername, suffix); if (!varname) @@ -1666,7 +1807,12 @@ grub_env_set_net_property (const char *intername, const char *suffix, for (ptr = varname; *ptr; ptr++) if (*ptr == ':') *ptr = '_'; - varvalue = grub_malloc (len + 1); + if (grub_add (len, 1, &sz)) + { + grub_free (varname); + return grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow detected while obtaining the size of an env variable"); + } + varvalue = grub_malloc (sz); if (!varvalue) { grub_free (varname); @@ -1909,14 +2055,15 @@ grub_config_search_through (char *config, char *suffix, } grub_err_t -grub_net_search_config_file (char *config) +grub_net_search_config_file (char *config, grub_size_t config_buf_len) { - grub_size_t config_len; + grub_size_t config_len, suffix_len; char *suffix; config_len = grub_strlen (config); config[config_len] = '-'; suffix = config + config_len + 1; + suffix_len = config_buf_len - (config_len + 1); struct grub_net_network_level_interface *inf; FOR_NET_NETWORK_LEVEL_INTERFACES (inf) @@ -1942,7 +2089,7 @@ grub_net_search_config_file (char *config) if (client_uuid) { - grub_strcpy (suffix, client_uuid); + grub_strlcpy (suffix, client_uuid, suffix_len); if (grub_config_search_through (config, suffix, 1, 0) == 0) return GRUB_ERR_NONE; } @@ -2005,7 +2152,7 @@ grub_net_search_config_file (char *config) /* Remove the remaining minus sign at the end. */ config[config_len] = '\0'; - return GRUB_ERR_NONE; + return GRUB_ERR_FILE_NOT_FOUND; } static struct grub_preboot *fini_hnd; @@ -2014,8 +2161,49 @@ static grub_command_t cmd_addaddr, cmd_deladdr, cmd_addroute, cmd_delroute; static grub_command_t cmd_setvlan, cmd_lsroutes, cmd_lscards; static grub_command_t cmd_lsaddr, cmd_slaac; +#ifdef GRUB_MACHINE_EFI + +static enum { + INIT_MODE_NONE, + INIT_MODE_GRUB, + INIT_MODE_EFI +} init_mode; + +static grub_command_t cmd_bootp, cmd_bootp6; + +#endif + GRUB_MOD_INIT(net) { +#ifdef GRUB_MACHINE_EFI + if (grub_net_open) + return; + + if ((grub_efi_net_boot_from_https () || grub_efi_net_boot_from_opa ()) + && grub_efi_net_fs_init ()) + { + cmd_lsroutes = grub_register_command ("net_ls_routes", grub_efi_net_list_routes, + "", N_("list network routes")); + cmd_lscards = grub_register_command ("net_ls_cards", grub_efi_net_list_cards, + "", N_("list network cards")); + cmd_lsaddr = grub_register_command ("net_ls_addr", grub_efi_net_list_addrs, + "", N_("list network addresses")); + cmd_addaddr = grub_register_command ("net_add_addr", grub_efi_net_add_addr, + /* TRANSLATORS: HWADDRESS stands for + "hardware address". */ + N_("SHORTNAME CARD ADDRESS [HWADDRESS]"), + N_("Add a network address.")); + cmd_bootp = grub_register_command ("net_bootp", grub_efi_net_bootp, + N_("[CARD]"), + N_("perform a bootp autoconfiguration")); + cmd_bootp6 = grub_register_command ("net_bootp6", grub_efi_net_bootp6, + N_("[CARD]"), + N_("perform a bootp autoconfiguration")); + init_mode = INIT_MODE_EFI; + return; + } +#endif + grub_register_variable_hook ("net_default_server", defserver_get_env, defserver_set_env); grub_env_export ("net_default_server"); @@ -2066,12 +2254,41 @@ GRUB_MOD_INIT(net) grub_net_restore_hw, GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK); grub_net_poll_cards_idle = grub_net_poll_cards_idle_real; + +#ifdef GRUB_MACHINE_EFI + grub_env_set ("grub_netfs_type", "grub"); + grub_register_variable_hook ("grub_netfs_type", 0, grub_env_write_readonly); + grub_env_export ("grub_netfs_type"); + init_mode = INIT_MODE_GRUB; +#endif + } GRUB_MOD_FINI(net) { + +#ifdef GRUB_MACHINE_EFI + if (init_mode == INIT_MODE_NONE) + return; + + if (init_mode == INIT_MODE_EFI) + { + grub_unregister_command (cmd_lsroutes); + grub_unregister_command (cmd_lscards); + grub_unregister_command (cmd_lsaddr); + grub_unregister_command (cmd_addaddr); + grub_unregister_command (cmd_bootp); + grub_unregister_command (cmd_bootp6); + grub_efi_net_fs_fini (); + init_mode = INIT_MODE_NONE; + return; + } +#endif + grub_register_variable_hook ("net_default_server", 0, 0); grub_register_variable_hook ("pxe_default_server", 0, 0); + grub_register_variable_hook ("net_default_ip", 0, 0); + grub_register_variable_hook ("net_default_mac", 0, 0); grub_bootp_fini (); grub_dns_fini (); @@ -2079,6 +2296,7 @@ GRUB_MOD_FINI(net) grub_unregister_command (cmd_deladdr); grub_unregister_command (cmd_addroute); grub_unregister_command (cmd_delroute); + grub_unregister_command (cmd_setvlan); grub_unregister_command (cmd_lsroutes); grub_unregister_command (cmd_lscards); grub_unregister_command (cmd_lsaddr); @@ -2088,4 +2306,7 @@ GRUB_MOD_FINI(net) grub_net_fini_hw (0); grub_loader_unregister_preboot_hook (fini_hnd); grub_net_poll_cards_idle = NULL; +#ifdef GRUB_MACHINE_EFI + init_mode = INIT_MODE_NONE; +#endif } diff --git a/grub-core/net/tcp.c b/grub-core/net/tcp.c index 93dee0caa1..902ab7e8ae 100644 --- a/grub-core/net/tcp.c +++ b/grub-core/net/tcp.c @@ -106,6 +106,18 @@ struct tcphdr grub_uint16_t urgent; } GRUB_PACKED; +struct tcp_scale_opt { + grub_uint8_t kind; + grub_uint8_t length; + grub_uint8_t scale; +} GRUB_PACKED; + +struct tcp_synhdr { + struct tcphdr tcphdr; + struct tcp_scale_opt scale_opt; + grub_uint8_t padding; +}; + struct tcp_pseudohdr { grub_uint32_t src; @@ -571,7 +583,7 @@ grub_net_tcp_open (char *server, grub_net_tcp_socket_t socket; static grub_uint16_t in_port = 21550; struct grub_net_buff *nb; - struct tcphdr *tcph; + struct tcp_synhdr *tcph; int i; grub_uint8_t *nbd; grub_net_link_level_address_t ll_target_addr; @@ -640,20 +652,24 @@ grub_net_tcp_open (char *server, } tcph = (void *) nb->data; + grub_memset(tcph, 0, sizeof (*tcph)); socket->my_start_seq = grub_get_time_ms (); socket->my_cur_seq = socket->my_start_seq + 1; - socket->my_window = 8192; - tcph->seqnr = grub_cpu_to_be32 (socket->my_start_seq); - tcph->ack = grub_cpu_to_be32_compile_time (0); - tcph->flags = grub_cpu_to_be16_compile_time ((5 << 12) | TCP_SYN); - tcph->window = grub_cpu_to_be16 (socket->my_window); - tcph->urgent = 0; - tcph->src = grub_cpu_to_be16 (socket->in_port); - tcph->dst = grub_cpu_to_be16 (socket->out_port); - tcph->checksum = 0; - tcph->checksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP, - &socket->inf->address, - &socket->out_nla); + socket->my_window = 32768; + tcph->tcphdr.seqnr = grub_cpu_to_be32 (socket->my_start_seq); + tcph->tcphdr.ack = grub_cpu_to_be32_compile_time (0); + tcph->tcphdr.flags = grub_cpu_to_be16_compile_time ((6 << 12) | TCP_SYN); + tcph->tcphdr.window = grub_cpu_to_be16 (socket->my_window); + tcph->tcphdr.urgent = 0; + tcph->tcphdr.src = grub_cpu_to_be16 (socket->in_port); + tcph->tcphdr.dst = grub_cpu_to_be16 (socket->out_port); + tcph->tcphdr.checksum = 0; + tcph->scale_opt.kind = 3; + tcph->scale_opt.length = 3; + tcph->scale_opt.scale = 5; + tcph->tcphdr.checksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_TCP, + &socket->inf->address, + &socket->out_nla); tcp_socket_register (socket); diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c index 409b1d09bc..3d96e50f4b 100644 --- a/grub-core/net/tftp.c +++ b/grub-core/net/tftp.c @@ -266,17 +266,19 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)), * forward slashes to a single forward slash. */ static void -grub_normalize_filename (char *normalized, const char *filename) +grub_normalize_filename (char *normalized, const char *filename, int c) { char *dest = normalized; const char *src = filename; - while (*src != '\0') + while (*src != '\0' && c > 0) { if (src[0] == '/' && src[1] == '/') src++; - else + else { + c--; *dest++ = *src++; + } } *dest = '\0'; } @@ -287,7 +289,7 @@ tftp_open (struct grub_file *file, const char *filename) struct tftphdr *tftph; char *rrq; int i; - int rrqlen; + int rrqlen, rrqsize; int hdrlen; grub_uint8_t open_data[1500]; struct grub_net_buff nb; @@ -315,37 +317,45 @@ tftp_open (struct grub_file *file, const char *filename) tftph = (struct tftphdr *) nb.data; - rrq = (char *) tftph->u.rrq; - rrqlen = 0; - tftph->opcode = grub_cpu_to_be16_compile_time (TFTP_RRQ); + rrq = (char *) tftph->u.rrq; + rrqsize = sizeof (tftph->u.rrq); + /* * Copy and normalize the filename to work-around issues on some TFTP * servers when file names are being matched for remapping. */ - grub_normalize_filename (rrq, filename); - rrqlen += grub_strlen (rrq) + 1; + grub_normalize_filename (rrq, filename, rrqsize); + + rrqlen = grub_strlen (rrq) + 1; rrq += grub_strlen (rrq) + 1; - grub_strcpy (rrq, "octet"); + /* Verify there is enough space for the remaining components. */ rrqlen += grub_strlen ("octet") + 1; + rrqlen += grub_strlen ("blksize") + 1; + rrqlen += grub_strlen ("1024") + 1; + rrqlen += grub_strlen ("tsize") + 1; + rrqlen += grub_strlen ("0") + 1; + + if (rrqlen >= rrqsize) { + grub_free (data); + return grub_error (GRUB_ERR_BAD_FILENAME, N_("filename too long")); + } + + grub_strcpy (rrq, "octet"); rrq += grub_strlen ("octet") + 1; grub_strcpy (rrq, "blksize"); - rrqlen += grub_strlen ("blksize") + 1; rrq += grub_strlen ("blksize") + 1; grub_strcpy (rrq, "1024"); - rrqlen += grub_strlen ("1024") + 1; rrq += grub_strlen ("1024") + 1; grub_strcpy (rrq, "tsize"); - rrqlen += grub_strlen ("tsize") + 1; rrq += grub_strlen ("tsize") + 1; grub_strcpy (rrq, "0"); - rrqlen += grub_strlen ("0") + 1; rrq += grub_strlen ("0") + 1; hdrlen = sizeof (tftph->opcode) + rrqlen; @@ -359,6 +369,7 @@ tftp_open (struct grub_file *file, const char *filename) file->not_easily_seekable = 1; file->data = data; + grub_dprintf("tftp", "resolving address for %s\n", file->device->net->server); err = grub_net_resolve_address (file->device->net->server, &addr); if (err) { @@ -369,11 +380,13 @@ tftp_open (struct grub_file *file, const char *filename) return err; } + grub_dprintf("tftp", "opening connection\n"); data->sock = grub_net_udp_open (addr, port ? port : TFTP_SERVER_PORT, tftp_receive, file); if (!data->sock) { + grub_dprintf("tftp", "connection failed\n"); grub_free (data); return grub_errno; } diff --git a/grub-core/normal/auth.c b/grub-core/normal/auth.c index 517fc623f0..71b361bc04 100644 --- a/grub-core/normal/auth.c +++ b/grub-core/normal/auth.c @@ -25,6 +25,10 @@ #include #include +#ifdef GRUB_MACHINE_EFI +#include +#endif + struct grub_auth_user { struct grub_auth_user *next; @@ -200,6 +204,32 @@ grub_username_get (char buf[], unsigned buf_size) return (key != GRUB_TERM_ESC); } +grub_err_t +grub_auth_check_cli_access (void) +{ + if (grub_is_cli_need_auth () == true) + { +#ifdef GRUB_MACHINE_EFI + static bool authenticated = false; + + if (authenticated == false) + { + grub_err_t ret; + + ret = grub_cryptodisk_challenge_password (); + if (ret == GRUB_ERR_NONE) + authenticated = true; + return ret; + } + return GRUB_ERR_NONE; +#else + return GRUB_ACCESS_DENIED; +#endif + } + + return GRUB_ERR_NONE; +} + grub_err_t grub_auth_check_authentication (const char *userlist) { @@ -209,6 +239,9 @@ grub_auth_check_authentication (const char *userlist) char entered[GRUB_AUTH_MAX_PASSLEN]; struct grub_auth_user *user; + if (grub_is_cli_disabled ()) + return GRUB_ACCESS_DENIED; + grub_memset (login, 0, sizeof (login)); if (is_authenticated (userlist)) diff --git a/grub-core/normal/context.c b/grub-core/normal/context.c index ba185e9159..b4baa4392c 100644 --- a/grub-core/normal/context.c +++ b/grub-core/normal/context.c @@ -99,7 +99,7 @@ grub_env_new_context (int export_all) grub_err_t grub_env_context_open (void) { - return grub_env_new_context (0); + return grub_env_new_context (1); } int grub_extractor_level = 0; diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index bd44310005..189ff9600c 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,9 @@ #include #include #include +#ifdef GRUB_MACHINE_IEEE1275 +#include +#endif GRUB_MOD_LICENSE ("GPLv3+"); @@ -67,6 +71,15 @@ grub_normal_free_menu (grub_menu_t menu) grub_free (entry->args); } + if (entry->bls) + { + entry->bls->visible = 0; + } + if (entry->blsuki) + { + entry->blsuki->visible = 0; + } + grub_free ((void *) entry->id); grub_free ((void *) entry->users); grub_free ((void *) entry->title); @@ -207,9 +220,10 @@ grub_normal_init_page (struct grub_term_output *term, grub_uint32_t *unicode_msg; grub_uint32_t *last_position; - grub_term_cls (term); + if (! grub_debug_is_enabled ()) + grub_term_cls (term); - msg_formatted = grub_xasprintf (_("GNU GRUB version %s"), PACKAGE_VERSION); + msg_formatted = grub_xasprintf (_("GRUB version %s"), PACKAGE_VERSION); if (!msg_formatted) return; @@ -276,6 +290,22 @@ grub_normal_execute (const char *config, int nested, int batch) { menu = read_config_file (config); +#ifdef GRUB_MACHINE_IEEE1275 + int boot; + boot = 0; + char *script; + script = grub_malloc (1024); + if (! grub_ieee1275_cas_reboot (script)) + { + char *dummy[1] = { NULL }; + if (! grub_script_execute_sourcecode (script)) + boot = 1; + } + grub_free (script); + if (boot) + grub_command_execute ("boot", 0, 0); +#endif + /* Ignore any error. */ grub_errno = GRUB_ERR_NONE; } @@ -310,52 +340,127 @@ grub_enter_normal_mode (const char *config) grub_boot_time ("Exiting normal mode"); } -/* Enter normal mode from rescue mode. */ static grub_err_t -grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)), - int argc, char *argv[]) +grub_try_normal_prefix (const char *prefix) { - if (argc == 0) - { - /* Guess the config filename. It is necessary to make CONFIG static, - so that it won't get broken by longjmp. */ - char *config; - const char *prefix; + char *config; + grub_err_t err = GRUB_ERR_FILE_NOT_FOUND; + const char *net_search_cfg; + int disable_net_search = 0; + + net_search_cfg = grub_env_get ("feature_net_search_cfg"); + if (net_search_cfg && net_search_cfg[0] == 'n') + disable_net_search = 1; + + if (grub_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0 && + !disable_net_search) + { + grub_size_t config_len; + config_len = grub_strlen (prefix) + + sizeof ("/grub.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); + config = grub_malloc (config_len); + + if (! config) + return err; + + grub_snprintf (config, config_len, "%s/grub.cfg", prefix); + err = grub_net_search_config_file (config, config_len); + } + + if (err != GRUB_ERR_NONE) + { + config = grub_xasprintf ("%s/grub.cfg", prefix); + if (config) + { + grub_file_t file; + file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG); + if (file) + { + grub_file_close (file); + err = GRUB_ERR_NONE; + } + } + } + + if (err == GRUB_ERR_NONE) + grub_enter_normal_mode (config); + + grub_errno = 0; + grub_free (config); + return err; +} - prefix = grub_env_get ("prefix"); - if (prefix) - { - grub_size_t config_len; - int disable_net_search = 0; - const char *net_search_cfg; +static int +grub_try_normal_dev (const char *name, void *data) +{ + grub_err_t err; + const char *prefix = grub_xasprintf ("(%s)%s", name, (char *)data); - config_len = grub_strlen (prefix) + - sizeof ("/grub.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); - config = grub_malloc (config_len); + if (!prefix) + return 0; - if (!config) - goto quit; + err = grub_try_normal_prefix (prefix); + if (err == GRUB_ERR_NONE) + return 1; - grub_snprintf (config, config_len, "%s/grub.cfg", prefix); + return 0; +} - net_search_cfg = grub_env_get ("feature_net_search_cfg"); - if (net_search_cfg && net_search_cfg[0] == 'n') - disable_net_search = 1; +static grub_err_t +grub_try_normal_discover (void) +{ + const char *prefix = grub_env_get ("prefix"); + grub_err_t err = GRUB_ERR_FILE_NOT_FOUND; - if (grub_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0 && - !disable_net_search) - grub_net_search_config_file (config); + if (!prefix) + return err; - grub_enter_normal_mode (config); - grub_free (config); - } - else - grub_enter_normal_mode (0); - } - else + if (grub_device_iterate (grub_try_normal_dev, (void *)prefix)) + return GRUB_ERR_NONE; + + return err; +} + +static grub_err_t +grub_try_normal (const char *variable) +{ + grub_err_t err = GRUB_ERR_FILE_NOT_FOUND; + const char *prefix; + + if (!variable) + return err; + + prefix = grub_env_get (variable); + if (!prefix) + return err; + + return grub_try_normal_prefix (prefix); +} + +/* Enter normal mode from rescue mode. */ +static grub_err_t +grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)), + int argc, char *argv[]) +{ + if (argc) grub_enter_normal_mode (argv[0]); + else + { + /* Guess the config filename. */ + grub_err_t err; + err = grub_try_normal ("fw_path"); + if (err == GRUB_ERR_FILE_NOT_FOUND) + err = grub_try_normal ("prefix"); +#ifdef __powerpc__ + if (err == GRUB_ERR_FILE_NOT_FOUND) + err = grub_try_normal_prefix ("/boot/grub"); +#endif + if (err == GRUB_ERR_FILE_NOT_FOUND) + err = grub_try_normal_discover (); + if (err == GRUB_ERR_FILE_NOT_FOUND) + grub_enter_normal_mode (0); + } -quit: return 0; } @@ -378,6 +483,9 @@ grub_normal_reader_init (int nested) const char *msg_esc = _("ESC at any time exits."); char *msg_formatted; + if (grub_env_get ("debug") != NULL) + return 0; + msg_formatted = grub_xasprintf (_("Minimal BASH-like line editing is supported. For " "the first word, TAB lists possible command completions. Anywhere " "else TAB lists possible device or file completions. To enable " @@ -453,9 +561,13 @@ grub_cmdline_run (int nested, int force_auth) } while (err && force_auth); + if (err == GRUB_ERR_NONE) + err = grub_auth_check_cli_access (); + if (err) { grub_print_error (); + grub_wait_after_message (); grub_errno = GRUB_ERR_NONE; return; } @@ -500,14 +612,15 @@ grub_mini_cmd_clear (struct grub_command *cmd __attribute__ ((unused)), return 0; } -static grub_command_t cmd_clear; +static grub_command_t cmd_clear, cmd_normal, cmd_normal_exit; static void (*grub_xputs_saved) (const char *str); static const char *features[] = { "feature_chainloader_bpb", "feature_ntldr", "feature_platform_search_hint", "feature_default_font_path", "feature_all_video_module", "feature_menuentry_id", "feature_menuentry_options", "feature_200_final", - "feature_nativedisk_cmd", "feature_timeout_style" + "feature_nativedisk_cmd", "feature_timeout_style", + "feature_search_cryptodisk_only" }; GRUB_MOD_INIT(normal) @@ -542,10 +655,10 @@ GRUB_MOD_INIT(normal) grub_env_export ("pager"); /* Register a command "normal" for the rescue mode. */ - grub_register_command ("normal", grub_cmd_normal, - 0, N_("Enter normal mode.")); - grub_register_command ("normal_exit", grub_cmd_normal_exit, - 0, N_("Exit from normal mode.")); + cmd_normal = grub_register_command ("normal", grub_cmd_normal, + 0, N_("Enter normal mode.")); + cmd_normal_exit = grub_register_command ("normal_exit", grub_cmd_normal_exit, + 0, N_("Exit from normal mode.")); /* Reload terminal colors when these variables are written to. */ grub_register_variable_hook ("color_normal", NULL, grub_env_write_color_normal); @@ -582,7 +695,11 @@ GRUB_MOD_FINI(normal) grub_xputs = grub_xputs_saved; grub_set_history (0); - grub_register_variable_hook ("pager", 0, 0); + grub_register_variable_hook ("pager", NULL, NULL); + grub_register_variable_hook ("color_normal", NULL, NULL); + grub_register_variable_hook ("color_highlight", NULL, NULL); grub_fs_autoload_hook = 0; grub_unregister_command (cmd_clear); + grub_unregister_command (cmd_normal); + grub_unregister_command (cmd_normal_exit); } diff --git a/grub-core/normal/menu.c b/grub-core/normal/menu.c index 6a90e091f2..a2703dabb1 100644 --- a/grub-core/normal/menu.c +++ b/grub-core/normal/menu.c @@ -32,6 +32,7 @@ #include #include #include +#include /* Time to delay after displaying an error message about a default/fallback entry failing to boot. */ @@ -163,12 +164,92 @@ grub_menu_set_timeout (int timeout) } } +static int +menuentry_eq (const char *id, const char *spec, int limit) +{ + const char *ptr1, *ptr2; + ptr1 = id; + ptr2 = spec; + while (limit == -1 || ptr1 - id <= limit) + { + if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0) + return ptr2 - spec; + if (*ptr2 == '>' && ptr2[1] != '>') + return 0; + if (*ptr2 == '>') + ptr2++; + if (*ptr1 != *ptr2) + { + if (limit > -1 && ptr1 - id == limit && !*ptr1 && grub_isspace(*ptr2)) + return ptr1 -id -1; + return 0; + } + if (*ptr1 == 0) + return ptr1 - id; + ptr1++; + ptr2++; + } + return 0; +} + +static int +get_entry_number_helper(grub_menu_t menu, + const char * const val, const char ** const tail) +{ + /* See if the variable matches the title of a menu entry. */ + int entry = -1; + grub_menu_entry_t e; + int i; + + for (i = 0, e = menu->entry_list; e; i++) + { + int l = 0; + while (val[l] && !grub_isspace(val[l])) + l++; + + if (menuentry_eq (e->id, val, l)) + { + if (tail) + *tail = val + l; + return i; + } + e = e->next; + } + + for (i = 0, e = menu->entry_list; e; i++) + { + + if (menuentry_eq (e->title, val, -1)) + { + if (tail) + *tail = NULL; + return i; + } + e = e->next; + } + + if (tail) + *tail = NULL; + + entry = (int) grub_strtoul (val, tail, 0); + if (grub_errno == GRUB_ERR_BAD_NUMBER || + (*tail && **tail && !grub_isspace(**tail))) + { + entry = -1; + if (tail) + *tail = NULL; + grub_errno = GRUB_ERR_NONE; + } + + return entry; +} + /* Get the first entry number from the value of the environment variable NAME, which is a space-separated list of non-negative integers. The entry number which is returned is stripped from the value of NAME. If no entry number can be found, -1 is returned. */ static int -get_and_remove_first_entry_number (const char *name) +get_and_remove_first_entry_number (grub_menu_t menu, const char *name) { const char *val, *tail; int entry; @@ -179,20 +260,24 @@ get_and_remove_first_entry_number (const char *name) grub_error_push (); - entry = (int) grub_strtoul (val, &tail, 0); + entry = get_entry_number_helper(menu, val, &tail); + if (!(*tail == 0 || grub_isspace(*tail))) + entry = -1; - if (grub_errno == GRUB_ERR_NONE) + if (entry >= 0) { - /* Skip whitespace to find the next digit. */ + /* Skip whitespace to find the next entry. */ while (*tail && grub_isspace (*tail)) tail++; - grub_env_set (name, tail); + if (*tail) + grub_env_set (name, tail); + else + grub_env_unset (name); } else { grub_env_unset (name); grub_errno = GRUB_ERR_NONE; - entry = -1; } grub_error_pop (); @@ -201,7 +286,7 @@ get_and_remove_first_entry_number (const char *name) } /* Run a menu entry. */ -static void +static grub_err_t grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot) { grub_err_t err = GRUB_ERR_NONE; @@ -218,7 +303,7 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot) { grub_print_error (); grub_errno = GRUB_ERR_NONE; - return; + return grub_errno; } errs_before = grub_err_printed_errors; @@ -231,7 +316,7 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot) grub_env_context_open (); menu = grub_zalloc (sizeof (*menu)); if (! menu) - return; + return grub_errno; grub_env_set_menu (menu); if (auto_boot) grub_env_set ("timeout", "0"); @@ -291,19 +376,17 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot) if (ptr && ptr[0] && ptr[1]) grub_env_set ("default", ptr + 1); - else - grub_env_unset ("default"); - grub_script_execute_new_scope (entry->sourcecode, entry->argc, entry->args); + err = grub_script_execute_new_scope (entry->sourcecode, entry->argc, entry->args); if (errs_before != grub_err_printed_errors) grub_wait_after_message (); errs_before = grub_err_printed_errors; - if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ()) + if (err == GRUB_ERR_NONE && grub_loader_is_loaded ()) /* Implicit execution of boot, only if something is loaded. */ - grub_command_execute ("boot", 0, 0); + err = grub_command_execute ("boot", 0, 0); if (errs_before != grub_err_printed_errors) grub_wait_after_message (); @@ -326,6 +409,8 @@ grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot) else grub_env_unset ("default"); grub_env_unset ("timeout"); + + return err; } /* Execute ENTRY from the menu MENU, falling back to entries specified @@ -340,13 +425,16 @@ grub_menu_execute_with_fallback (grub_menu_t menu, void *callback_data) { int fallback_entry; + grub_err_t err; callback->notify_booting (entry, callback_data); - grub_menu_execute_entry (entry, 1); + err = grub_menu_execute_entry (entry, 1); + if (err == GRUB_ERR_NONE) + return; /* Deal with fallback entries. */ - while ((fallback_entry = get_and_remove_first_entry_number ("fallback")) + while ((fallback_entry = get_and_remove_first_entry_number (menu, "fallback")) >= 0) { grub_print_error (); @@ -354,11 +442,9 @@ grub_menu_execute_with_fallback (grub_menu_t menu, entry = grub_menu_get_entry (menu, fallback_entry); callback->notify_fallback (entry, callback_data); - grub_menu_execute_entry (entry, 1); - /* If the function call to execute the entry returns at all, then this is - taken to indicate a boot failure. For menu entries that do something - other than actually boot an operating system, this could assume - incorrectly that something failed. */ + err = grub_menu_execute_entry (entry, 1); + if (err == GRUB_ERR_NONE) + return; } if (!autobooted) @@ -464,35 +550,12 @@ grub_menu_register_viewer (struct grub_menu_viewer *viewer) viewers = viewer; } -static int -menuentry_eq (const char *id, const char *spec) -{ - const char *ptr1, *ptr2; - ptr1 = id; - ptr2 = spec; - while (1) - { - if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0) - return 1; - if (*ptr2 == '>' && ptr2[1] != '>') - return 0; - if (*ptr2 == '>') - ptr2++; - if (*ptr1 != *ptr2) - return 0; - if (*ptr1 == 0) - return 1; - ptr1++; - ptr2++; - } -} - - /* Get the entry number from the variable NAME. */ static int get_entry_number (grub_menu_t menu, const char *name) { const char *val; + const char *tail; int entry; val = grub_env_get (name); @@ -500,38 +563,9 @@ get_entry_number (grub_menu_t menu, const char *name) return -1; grub_error_push (); - - entry = (int) grub_strtoul (val, 0, 0); - - if (grub_errno == GRUB_ERR_BAD_NUMBER) - { - /* See if the variable matches the title of a menu entry. */ - grub_menu_entry_t e = menu->entry_list; - int i; - - grub_errno = GRUB_ERR_NONE; - - for (i = 0; e; i++) - { - if (menuentry_eq (e->title, val) - || menuentry_eq (e->id, val)) - { - entry = i; - break; - } - e = e->next; - } - - if (! e) - entry = -1; - } - - if (grub_errno != GRUB_ERR_NONE) - { - grub_errno = GRUB_ERR_NONE; - entry = -1; - } - + entry = get_entry_number_helper(menu, val, &tail); + if (tail && *tail != '\0') + entry = -1; grub_error_pop (); return entry; @@ -751,9 +785,7 @@ run_menu (grub_menu_t menu, int nested, int *auto_boot, int *notify_boot) case GRUB_TERM_CTRL | 'c': case GRUB_TERM_KEY_NPAGE: - if (current_entry + GRUB_MENU_PAGE_SIZE < menu->size) - current_entry += GRUB_MENU_PAGE_SIZE; - else + if (grub_add (current_entry, GRUB_MENU_PAGE_SIZE, ¤t_entry) || current_entry >= menu->size) current_entry = menu->size - 1; menu_set_chosen_entry (current_entry); break; diff --git a/grub-core/normal/menu_entry.c b/grub-core/normal/menu_entry.c index ade56be2be..8b0d17e3f2 100644 --- a/grub-core/normal/menu_entry.c +++ b/grub-core/normal/menu_entry.c @@ -1255,9 +1255,13 @@ grub_menu_entry_run (grub_menu_entry_t entry) err = grub_auth_check_authentication (NULL); + if (err == GRUB_ERR_NONE) + err = grub_auth_check_cli_access (); + if (err) { grub_print_error (); + grub_wait_after_message (); grub_errno = GRUB_ERR_NONE; return; } diff --git a/grub-core/normal/menu_text.c b/grub-core/normal/menu_text.c index b1321eb268..9c383e64ae 100644 --- a/grub-core/normal/menu_text.c +++ b/grub-core/normal/menu_text.c @@ -178,21 +178,24 @@ command-line or ESC to discard edits and return to the GRUB menu."), grub_free (msg_translated); - if (nested) + if (!grub_is_cli_disabled ()) { - ret += grub_print_message_indented_real - (_("Press enter to boot the selected OS, " - "`e' to edit the commands before booting " - "or `c' for a command-line. ESC to return previous menu."), - STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run); - } - else - { - ret += grub_print_message_indented_real - (_("Press enter to boot the selected OS, " - "`e' to edit the commands before booting " - "or `c' for a command-line."), - STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run); + if (nested) + { + ret += grub_print_message_indented_real + (_("Press enter to boot the selected OS, " + "`e' to edit the commands before booting " + "or `c' for a command-line. ESC to return previous menu."), + STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run); + } + else + { + ret += grub_print_message_indented_real + (_("Press enter to boot the selected OS, " + "`e' to edit the commands before booting " + "or `c' for a command-line."), + STANDARD_MARGIN, STANDARD_MARGIN, term, dry_run); + } } } return ret; diff --git a/grub-core/osdep/linux/blocklist.c b/grub-core/osdep/linux/blocklist.c index 2efee2c2ae..902cfee53a 100644 --- a/grub-core/osdep/linux/blocklist.c +++ b/grub-core/osdep/linux/blocklist.c @@ -109,7 +109,7 @@ grub_install_get_blocklist (grub_device_t root_dev, else { struct fiemap *fie2; - int i; + unsigned int i; fie2 = xmalloc (sizeof (*fie2) + fie1.fm_mapped_extents * sizeof (fie1.fm_extents[1])); diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c index 7dd775d2ad..8bf281c1d8 100644 --- a/grub-core/osdep/linux/getroot.c +++ b/grub-core/osdep/linux/getroot.c @@ -103,6 +103,14 @@ struct btrfs_ioctl_search_key grub_uint32_t unused[9]; }; +struct btrfs_ioctl_search_header { + grub_uint64_t transid; + grub_uint64_t objectid; + grub_uint64_t offset; + grub_uint32_t type; + grub_uint32_t len; +}; + struct btrfs_ioctl_search_args { struct btrfs_ioctl_search_key key; grub_uint64_t buf[(4096 - sizeof(struct btrfs_ioctl_search_key)) @@ -119,7 +127,7 @@ struct btrfs_ioctl_search_args { struct btrfs_ioctl_fs_info_args) static int -grub_util_is_imsm (const char *os_dev); +grub_util_is_imsm_or_ddf (const char *os_dev); #define ESCAPED_PATH_MAX (4 * PATH_MAX) @@ -131,6 +139,7 @@ struct mountinfo_entry char fstype[ESCAPED_PATH_MAX + 1], device[ESCAPED_PATH_MAX + 1]; }; +#ifdef GRUB_UTIL static char ** grub_util_raid_getmembers (const char *name, int bootable) { @@ -191,6 +200,7 @@ grub_util_raid_getmembers (const char *name, int bootable) return devicelist; } +#endif /* Statting something on a btrfs filesystem always returns a virtual device major/minor pair rather than the real underlying device, because btrfs @@ -225,7 +235,7 @@ grub_find_root_devices_from_btrfs (const char *dir) { int fd; struct btrfs_ioctl_fs_info_args fsi; - int i, j = 0; + unsigned int i, j = 0; char **ret; fd = open (dir, 0); @@ -373,6 +383,110 @@ get_btrfs_fs_prefix (const char *mount_path) return NULL; } +int use_relative_path_on_btrfs = 0; + +static char * +get_btrfs_subvol (const char *path) +{ + struct btrfs_ioctl_ino_lookup_args args; + grub_uint64_t tree_id; + int fd = -1; + char *ret = NULL; + + fd = open (path, O_RDONLY); + + if (fd < 0) + return NULL; + + memset (&args, 0, sizeof(args)); + args.objectid = GRUB_BTRFS_TREE_ROOT_OBJECTID; + + if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0) + goto error; + + tree_id = args.treeid; + + while (tree_id != GRUB_BTRFS_ROOT_VOL_OBJECTID) + { + struct btrfs_ioctl_search_args sargs; + struct grub_btrfs_root_backref *br; + struct btrfs_ioctl_search_header *search_header; + char *old; + grub_uint16_t len; + grub_uint64_t inode_id; + + memset (&sargs, 0, sizeof(sargs)); + + sargs.key.tree_id = 1; + sargs.key.min_objectid = tree_id; + sargs.key.max_objectid = tree_id; + + sargs.key.min_offset = 0; + sargs.key.max_offset = ~0ULL; + sargs.key.min_transid = 0; + sargs.key.max_transid = ~0ULL; + sargs.key.min_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF; + sargs.key.max_type = GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF; + + sargs.key.nr_items = 1; + + if (ioctl (fd, BTRFS_IOC_TREE_SEARCH, &sargs) < 0) + goto error; + + if (sargs.key.nr_items == 0) + goto error; + + search_header = (struct btrfs_ioctl_search_header *)sargs.buf; + br = (struct grub_btrfs_root_backref *) (search_header + 1); + + len = grub_le_to_cpu16 (br->n); + inode_id = grub_le_to_cpu64 (br->inode_id); + tree_id = search_header->offset; + + old = ret; + ret = malloc (len + 1); + memcpy (ret, br->name, len); + ret[len] = '\0'; + + if (inode_id != GRUB_BTRFS_TREE_ROOT_OBJECTID) + { + char *s; + + memset(&args, 0, sizeof(args)); + args.treeid = search_header->offset; + args.objectid = inode_id; + + if (ioctl (fd, BTRFS_IOC_INO_LOOKUP, &args) < 0) + goto error; + + s = xasprintf ("%s%s", args.name, ret); + free (ret); + ret = s; + } + + if (old) + { + char *s = xasprintf ("%s/%s", ret, old); + free (ret); + free (old); + ret = s; + } + } + + close (fd); + return ret; + +error: + + if (fd >= 0) + close (fd); + if (ret) + free (ret); + + return NULL; +} + +void (*grub_find_root_btrfs_mount_path_hook)(const char *mount_path); char ** grub_find_root_devices_from_mountinfo (const char *dir, char **relroot) @@ -484,6 +598,9 @@ grub_find_root_devices_from_mountinfo (const char *dir, char **relroot) } } + if (!entry_len) + goto out; + /* Now scan visible mounts for the ones we're interested in. */ for (i = entry_len - 1; i >= 0; i--) { @@ -515,7 +632,16 @@ grub_find_root_devices_from_mountinfo (const char *dir, char **relroot) else if (grub_strcmp (entries[i].fstype, "btrfs") == 0) { ret = grub_find_root_devices_from_btrfs (dir); - fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path); + if (use_relative_path_on_btrfs) + { + fs_prefix = xstrdup ("/"); + if (grub_find_root_btrfs_mount_path_hook) + grub_find_root_btrfs_mount_path_hook (entries[i].enc_path); + } + else + { + fs_prefix = get_btrfs_fs_prefix (entries[i].enc_path); + } } else if (!retry && grub_strcmp (entries[i].fstype, "autofs") == 0) { @@ -576,6 +702,7 @@ grub_find_root_devices_from_mountinfo (const char *dir, char **relroot) return ret; } +#ifdef GRUB_UTIL static char * get_mdadm_uuid (const char *os_dev) { @@ -633,12 +760,13 @@ get_mdadm_uuid (const char *os_dev) return name; } +#endif static int -grub_util_is_imsm (const char *os_dev) +grub_util_is_imsm_or_ddf (const char *os_dev) { int retry; - int is_imsm = 0; + int is_imsm_or_ddf = 0; int container_seen = 0; const char *dev = os_dev; @@ -699,10 +827,17 @@ grub_util_is_imsm (const char *os_dev) if (strncmp (buf, "MD_METADATA=imsm", sizeof ("MD_METADATA=imsm") - 1) == 0) { - is_imsm = 1; + is_imsm_or_ddf = 1; grub_util_info ("%s is imsm", dev); break; } + if (strncmp (buf, "MD_METADATA=ddf", + sizeof ("MD_METADATA=ddf") - 1) == 0) + { + is_imsm_or_ddf = 1; + grub_util_info ("%s is ddf", dev); + break; + } } free (buf); @@ -713,7 +848,7 @@ grub_util_is_imsm (const char *os_dev) if (dev != os_dev) free ((void *) dev); - return is_imsm; + return is_imsm_or_ddf; } char * @@ -965,6 +1100,7 @@ grub_util_part_to_disk (const char *os_dev, struct stat *st, return path; } +#ifdef GRUB_UTIL static char * grub_util_get_raid_grub_dev (const char *os_dev) { @@ -1067,6 +1203,7 @@ grub_util_get_raid_grub_dev (const char *os_dev) } return grub_dev; } +#endif enum grub_dev_abstraction_types grub_util_get_dev_abstraction_os (const char *os_dev) @@ -1078,11 +1215,12 @@ grub_util_get_dev_abstraction_os (const char *os_dev) /* Check for RAID. */ if (!strncmp (os_dev, "/dev/md", 7) && ! grub_util_device_is_mapped (os_dev) - && !grub_util_is_imsm (os_dev)) + && !grub_util_is_imsm_or_ddf (os_dev)) return GRUB_DEV_ABSTRACTION_RAID; return GRUB_DEV_ABSTRACTION_NONE; } +#ifdef GRUB_UTIL int grub_util_pull_device_os (const char *os_dev, enum grub_dev_abstraction_types ab) @@ -1139,6 +1277,33 @@ grub_util_get_grub_dev_os (const char *os_dev) return grub_dev; } +#endif + +static void *mp = NULL; +static void +btrfs_mount_path_hook(const char *m) +{ + mp = strdup (m); +} + +char * +grub_util_get_btrfs_subvol (const char *path, char **mount_path) +{ + if (mount_path) + *mount_path = NULL; + + grub_find_root_btrfs_mount_path_hook = btrfs_mount_path_hook; + grub_free (grub_find_root_devices_from_mountinfo (path, NULL)); + grub_find_root_btrfs_mount_path_hook = NULL; + + if (!mp) + return NULL; + + if (mount_path) + *mount_path = mp; + + return get_btrfs_subvol (mp); +} char * grub_make_system_path_relative_to_its_root_os (const char *path) diff --git a/grub-core/osdep/linux/ofpath.c b/grub-core/osdep/linux/ofpath.c index a6153d3595..72772c87a6 100644 --- a/grub-core/osdep/linux/ofpath.c +++ b/grub-core/osdep/linux/ofpath.c @@ -37,6 +37,7 @@ #include #include #include +#include #ifdef __sparc__ typedef enum @@ -350,6 +351,38 @@ of_path_of_ide(const char *sys_devname __attribute__((unused)), const char *devi return ret; } + +static void +of_fc_port_name(const char *path, const char *subpath, char *port_name) +{ + char *bname, *basepath, *p; + int fd; + + bname = xmalloc(sizeof(char)*150); + basepath = xmalloc(strlen(path)); + + /* Generate the path to get port name information from the drive */ + strncpy(basepath,path,subpath-path); + basepath[subpath-path-1] = '\0'; + p = get_basename(basepath); + snprintf(bname,sizeof(char)*150,"%s/fc_transport/%s/port_name",basepath,p); + + /* Read the information from the port name */ + fd = open (bname, O_RDONLY); + if (fd < 0) + grub_util_error (_("cannot open `%s': %s"), bname, strerror (errno)); + + if (read(fd,port_name,sizeof(char)*19) < 0) + grub_util_error (_("cannot read `%s': %s"), bname, strerror (errno)); + + sscanf(port_name,"0x%s",port_name); + + close(fd); + + free(bname); + free(basepath); +} + #ifdef __sparc__ static char * of_path_of_nvme(const char *sys_devname __attribute__((unused)), @@ -577,6 +610,16 @@ of_path_of_scsi(const char *sys_devname __attribute__((unused)), const char *dev digit_string = trailing_digits (device); if (strncmp (of_path, "/vdevice/", sizeof ("/vdevice/") - 1) == 0) { + if(strstr(of_path,"vfc-client")) + { + char * port_name = xmalloc(sizeof(char)*17); + of_fc_port_name(sysfs_path, p, port_name); + + snprintf(disk,sizeof(disk),"/%s@%s", disk_name, port_name); + free(port_name); + } + else + { unsigned long id = 0x8000 | (tgt << 8) | (bus << 5) | lun; if (*digit_string == '\0') { @@ -590,6 +633,13 @@ of_path_of_scsi(const char *sys_devname __attribute__((unused)), const char *dev snprintf(disk, sizeof (disk), "/%s@%04lx000000000000:%c", disk_name, id, 'a' + (part - 1)); } + } + } else if (strstr(of_path,"fibre-channel")||(strstr(of_path,"vfc-client"))){ + char * port_name = xmalloc(sizeof(char)*17); + of_fc_port_name(sysfs_path, p, port_name); + + snprintf(disk,sizeof(disk),"/%s@%s", disk_name, port_name); + free(port_name); } else { @@ -706,13 +756,74 @@ strip_trailing_digits (const char *p) return new; } +static char * +get_slave_from_dm(const char * device){ + char *curr_device, *tmp; + char *directory; + char *ret = NULL; + + directory = grub_strdup (device); + tmp = get_basename(directory); + curr_device = grub_strdup (tmp); + *tmp = '\0'; + + /* Recursively check for slaves devices so we can find the root device */ + while ((curr_device[0] == 'd') && (curr_device[1] == 'm') && (curr_device[2] == '-')){ + DIR *dp; + struct dirent *ep; + char* device_path; + + device_path = grub_xasprintf ("/sys/block/%s/slaves", curr_device); + dp = opendir(device_path); + if (dp == NULL) + grub_util_warn (_("cannot open directory `%s'"), device_path); + free(device_path); + + if (dp != NULL) + { + ep = readdir (dp); + while (ep != NULL){ + + /* avoid some system directories */ + if (!strcmp(ep->d_name,".")) + goto next_dir; + if (!strcmp(ep->d_name,"..")) + goto next_dir; + + free (curr_device); + free (ret); + curr_device = grub_strdup (ep->d_name); + ret = grub_xasprintf ("%s%s", directory, curr_device); + break; + + next_dir: + ep = readdir (dp); + continue; + } + closedir (dp); + } + } + + free (directory); + free (curr_device); + + return ret; +} + char * grub_util_devname_to_ofpath (const char *sys_devname) { - char *name_buf, *device, *devnode, *devicenode, *ofpath; + char *name_buf, *device, *devnode, *devicenode, *ofpath, *realname; name_buf = xrealpath (sys_devname); + realname = get_slave_from_dm (name_buf); + if (realname) + { + free (name_buf); + name_buf = realname; + } + device = get_basename (name_buf); devnode = strip_trailing_digits (name_buf); devicenode = strip_trailing_digits (device); diff --git a/grub-core/osdep/unix/config.c b/grub-core/osdep/unix/config.c index 0b1f7618d2..0ce0e309ac 100644 --- a/grub-core/osdep/unix/config.c +++ b/grub-core/osdep/unix/config.c @@ -82,6 +82,19 @@ grub_util_load_config (struct grub_util_config *cfg) if (v) cfg->grub_distributor = xstrdup (v); + v = getenv ("SUSE_BTRFS_SNAPSHOT_BOOTING"); + if (v) + { + if (grub_strncmp(v, "true", sizeof ("true") - 1) == 0) + { + cfg->is_suse_btrfs_snapshot_enabled = 1; + } + else + { + cfg->is_suse_btrfs_snapshot_enabled = 0; + } + } + cfgfile = grub_util_get_config_filename (); if (!grub_util_is_regular (cfgfile)) return; @@ -105,8 +118,8 @@ grub_util_load_config (struct grub_util_config *cfg) *ptr++ = *iptr; } - strcpy (ptr, "'; printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_DISTRIBUTOR=%s\\n\" " - "\"$GRUB_ENABLE_CRYPTODISK\" \"$GRUB_DISTRIBUTOR\""); + strcpy (ptr, "'; printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_DISTRIBUTOR=%s\\nSUSE_BTRFS_SNAPSHOT_BOOTING=%s\\n\" " + "\"$GRUB_ENABLE_CRYPTODISK\" \"$GRUB_DISTRIBUTOR\" \"$SUSE_BTRFS_SNAPSHOT_BOOTING\""); argv[2] = script; argv[3] = '\0'; diff --git a/grub-core/osdep/unix/getroot.c b/grub-core/osdep/unix/getroot.c index ee11b02fb6..62581ba601 100644 --- a/grub-core/osdep/unix/getroot.c +++ b/grub-core/osdep/unix/getroot.c @@ -16,8 +16,8 @@ * along with GRUB. If not, see . */ -#include #include +#include #include #include @@ -567,6 +567,7 @@ grub_guess_root_devices (const char *dir_in) #endif +#ifdef GRUB_UTIL void grub_util_pull_lvm_by_command (const char *os_dev) { @@ -663,6 +664,7 @@ grub_util_pull_lvm_by_command (const char *os_dev) free (buf); free (vgid); } +#endif /* ZFS has similar problems to those of btrfs (see above). */ void diff --git a/grub-core/osdep/unix/hostdisk.c b/grub-core/osdep/unix/hostdisk.c index 3a00d7451a..e5f4b4d5f9 100644 --- a/grub-core/osdep/unix/hostdisk.c +++ b/grub-core/osdep/unix/hostdisk.c @@ -71,6 +71,12 @@ grub_util_get_fd_size (grub_util_fd_t fd, const char *name, unsigned *log_secsiz if (log_secsize) *log_secsize = 9; +#ifdef GRUB_MACHINE_EMU + /* /proc doesn't behave itself and gives 0 for file sizes to stat. */ + if (st.st_size == 0 && !grub_strncmp ("/proc", name, 5)) + return -1; +#endif + return st.st_size; } diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c index 14ff09094f..3d26a3fe41 100644 --- a/grub-core/script/execute.c +++ b/grub-core/script/execute.c @@ -28,15 +28,26 @@ #include #include #include +#ifdef GRUB_MACHINE_IEEE1275 +#include +#endif /* Max digits for a char is 3 (0xFF is 255), similarly for an int it is sizeof (int) * 3, and one extra for a possible -ve sign. */ #define ERRNO_DIGITS_MAX (sizeof (int) * 3 + 1) +/* + * A limit on recursion, to avoid colliding with the heap. UEFI defines a baseline + * stack size of 128 KiB. So, assuming at most 1-2 KiB per iteration this should + * keep us safe. + */ +#define MAX_RECURSION_DEPTH 64 + static unsigned long is_continue; static unsigned long active_loops; static unsigned long active_breaks; static unsigned long function_return; +static unsigned long recursion_depth; #define GRUB_SCRIPT_SCOPE_MALLOCED 1 #define GRUB_SCRIPT_SCOPE_ARGS_MALLOCED 2 @@ -53,6 +64,12 @@ static struct grub_script_scope *scope = 0; /* Wildcard translator for GRUB script. */ struct grub_script_wildcard_translator *grub_wildcard_translator; +static int +is_hex(char c) +{ + return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + static char* wildcard_escape (const char *s) { @@ -69,7 +86,15 @@ wildcard_escape (const char *s) i = 0; while ((ch = *s++)) { - if (ch == '*' || ch == '\\' || ch == '?') + if (ch == '\\' && s[0] == 'x' && is_hex(s[1]) && is_hex(s[2])) + { + p[i++] = ch; + p[i++] = *s++; + p[i++] = *s++; + p[i++] = *s++; + continue; + } + else if (ch == '*' || ch == '\\' || ch == '?') p[i++] = '\\'; p[i++] = ch; } @@ -93,7 +118,14 @@ wildcard_unescape (const char *s) i = 0; while ((ch = *s++)) { - if (ch == '\\') + if (ch == '\\' && s[0] == 'x' && is_hex(s[1]) && is_hex(s[2])) + { + p[i++] = '\\'; + p[i++] = *s++; + p[i++] = *s++; + p[i++] = *s++; + } + else if (ch == '\\') p[i++] = *s++; else p[i++] = ch; @@ -395,10 +427,20 @@ parse_string (const char *str, switch (*ptr) { case '\\': - escaped = !escaped; - if (!escaped && put) - *(put++) = '\\'; - ptr++; + if (!escaped && put && *(ptr+1) == 'x' && is_hex(*(ptr+2)) && is_hex(*(ptr+3))) + { + *(put++) = *ptr++; + *(put++) = *ptr++; + *(put++) = *ptr++; + *(put++) = *ptr++; + } + else + { + escaped = !escaped; + if (!escaped && put) + *(put++) = '\\'; + ptr++; + } break; case '$': if (escaped) @@ -752,6 +794,9 @@ grub_script_arglist_to_argv (struct grub_script_arglist *arglist, } } + if (result.args == NULL || result.argc == 0) + goto fail; + if (! result.args[result.argc - 1]) result.argc--; @@ -816,7 +861,13 @@ grub_script_execute_cmd (struct grub_script_cmd *cmd) if (cmd == 0) return 0; + recursion_depth++; + + if (recursion_depth >= MAX_RECURSION_DEPTH) + return grub_error (GRUB_ERR_RECURSION_DEPTH, N_("maximum recursion depth exceeded")); + ret = cmd->exec (cmd); + recursion_depth--; grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret); grub_env_set ("?", errnobuf); @@ -883,6 +934,10 @@ grub_script_execute_sourcecode (const char *source) grub_err_t ret = 0; struct grub_script *parsed_script; +#ifdef GRUB_MACHINE_IEEE1275 + grub_ieee1275_set_boot_last_label (source); +#endif + while (source) { char *line; @@ -897,7 +952,10 @@ grub_script_execute_sourcecode (const char *source) break; } - ret = grub_script_execute (parsed_script); + /* Don't let trailing blank lines determine the return code. */ + if (parsed_script->cmd) + ret = grub_script_execute (parsed_script); + grub_script_free (parsed_script); grub_free (line); } diff --git a/grub-core/script/script.c b/grub-core/script/script.c index ec4d4337c6..844e8343ca 100644 --- a/grub-core/script/script.c +++ b/grub-core/script/script.c @@ -22,6 +22,11 @@ #include #include +#ifdef grub_dprintf +#undef grub_dprintf +#endif +#define grub_dprintf(no, fmt, ...) + /* It is not possible to deallocate the memory when a syntax error was found. Because of that it is required to keep track of all memory allocations. The memory is freed in case of an error, or assigned diff --git a/grub-core/term/at_keyboard.c b/grub-core/term/at_keyboard.c index f8a129eb7f..d2b716b5b4 100644 --- a/grub-core/term/at_keyboard.c +++ b/grub-core/term/at_keyboard.c @@ -25,12 +25,14 @@ #include #include #include +#include GRUB_MOD_LICENSE ("GPLv3+"); static grub_uint8_t grub_keyboard_controller_orig; static grub_uint8_t grub_keyboard_orig_set; struct grub_ps2_state ps2_state; +static int fallback_set; static int ping_sent; @@ -86,10 +88,12 @@ at_command (grub_uint8_t data) break; return 0; } + if (i == GRUB_AT_TRIES) + grub_dprintf ("atkeyb", "at_command() timed out! (stopped after %d tries)\n", i); return (i != GRUB_AT_TRIES); } -static void +static void __attribute__((__unused__)) grub_keyboard_controller_write (grub_uint8_t c) { at_command (KEYBOARD_COMMAND_WRITE); @@ -115,6 +119,21 @@ grub_keyboard_controller_read (void) #endif +static int +resend_last_result (void) +{ + grub_uint8_t ret; + keyboard_controller_wait_until_ready (); + grub_dprintf ("atkeyb", "resend_last_result: sending 0xfe\n"); + grub_outb (0xfe, KEYBOARD_REG_DATA); + ret = wait_ack (); + grub_dprintf ("atkeyb", "resend_last_result: wait_ack() returned 0x%x\n", ret); + keyboard_controller_wait_until_ready (); + ret = grub_inb (KEYBOARD_REG_DATA); + grub_dprintf ("atkeyb", "resend_last_result: read 0x%x from controller\n", ret); + return ret; +} + static int write_mode (int mode) { @@ -123,11 +142,14 @@ write_mode (int mode) { grub_uint8_t ack; keyboard_controller_wait_until_ready (); + grub_dprintf ("atkeyb", "write_mode: sending 0xf0\n"); grub_outb (0xf0, KEYBOARD_REG_DATA); keyboard_controller_wait_until_ready (); + grub_dprintf ("atkeyb", "write_mode: sending mode %d\n", mode); grub_outb (mode, KEYBOARD_REG_DATA); keyboard_controller_wait_until_ready (); ack = wait_ack (); + grub_dprintf ("atkeyb", "write_mode: wait_ack() returned 0x%x\n", ack); if (ack == GRUB_AT_NACK) continue; if (ack == GRUB_AT_ACK) @@ -135,6 +157,9 @@ write_mode (int mode) return 0; } + if (i == GRUB_AT_TRIES) + grub_dprintf ("atkeyb", "write_mode() timed out! (stopped after %d tries)\n", i); + return (i != GRUB_AT_TRIES); } @@ -142,23 +167,66 @@ static int query_mode (void) { grub_uint8_t ret; + grub_uint64_t endtime; + unsigned i; int e; + const char *envvar; - e = write_mode (0); - if (!e) - return 0; + for (i = 0; i < GRUB_AT_TRIES; i++) { + grub_dprintf ("atkeyb", "query_mode: sending command to controller\n"); + e = write_mode (0); + if (!e) { + grub_dprintf ("atkeyb", "query_mode: write_mode(0) failed\n"); + return 0; + } - do { - keyboard_controller_wait_until_ready (); - ret = grub_inb (KEYBOARD_REG_DATA); - } while (ret == GRUB_AT_ACK); - /* QEMU translates the set even in no-translate mode. */ - if (ret == 0x43 || ret == 1) - return 1; - if (ret == 0x41 || ret == 2) - return 2; - if (ret == 0x3f || ret == 3) - return 3; + endtime = grub_get_time_ms () + 20; + do { + keyboard_controller_wait_until_ready (); + ret = grub_inb (KEYBOARD_REG_DATA); + grub_dprintf ("atkeyb", "query_mode/loop: read 0x%x from controller\n", ret); + } while ((ret == GRUB_AT_ACK || ret == GRUB_AT_NACK) && grub_get_time_ms () < endtime); + if (ret == 0xfe) { + grub_dprintf ("atkeyb", "query_mode: asking controller to resend last result\n"); + ret = resend_last_result(); + grub_dprintf ("atkeyb", "query_mode: read 0x%x from controller\n", ret); + } + /* QEMU translates the set even in no-translate mode. */ + if (ret == 0x43 || ret == 1) { + grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 1\n", ret); + return 1; + } + if (ret == 0x41 || ret == 2) { + grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 2\n", ret); + return 2; + } + if (ret == 0x3f || ret == 3) { + grub_dprintf ("atkeyb", "query_mode: controller returned 0x%x, returning 3\n", ret); + return 3; + } + grub_dprintf ("atkeyb", "query_mode: controller returned unexpected value 0x%x, retrying\n", ret); + } + + /* + * Falling here means we tried querying and the controller returned something + * we don't understand, try to use 'at_keyboard_fallback_set' if it exists, + * otherwise return 0. + */ + envvar = grub_env_get ("at_keyboard_fallback_set"); + if (envvar) { + fallback_set = grub_strtoul (envvar, 0, 10); + if ((grub_errno) || (fallback_set < 1) || (fallback_set > 3)) { + grub_dprintf ("atkeyb", "WARNING: ignoring unexpected value '%s' for '%s' variable\n", + envvar, "at_keyboard_fallback_set"); + fallback_set = 0; + } else { + grub_dprintf ("atkeyb", "query_mode: '%s' specified in environment, returning %d\n", + "at_keyboard_fallback_set", fallback_set); + } + return fallback_set; + } + grub_dprintf ("atkeyb", "WARNING: no '%s' specified in environment, returning 0\n", + "at_keyboard_fallback_set"); return 0; } @@ -167,15 +235,32 @@ set_scancodes (void) { /* You must have visited computer museum. Keyboard without scancode set knowledge. Assume XT. */ - if (!grub_keyboard_orig_set) - { - grub_dprintf ("atkeyb", "No sets support assumed\n"); - ps2_state.current_set = 1; + if (!grub_keyboard_orig_set) { + if (fallback_set) { + grub_dprintf ("atkeyb", "No sets support assumed but set forced to %d\n", fallback_set); + ps2_state.current_set = fallback_set; return; } + grub_dprintf ("atkeyb", "No sets support assumed, forcing to set 1\n"); + ps2_state.current_set = 1; + return; + } #if !USE_SCANCODE_SET - ps2_state.current_set = 1; + if (fallback_set) { + grub_dprintf ("atkeyb", "queried set is %d but set forced to %d\n", + grub_keyboard_orig_set, fallback_set); + ps2_state.current_set = fallback_set; + return; + } + + if ((grub_keyboard_controller_orig & KEYBOARD_AT_TRANSLATE) == KEYBOARD_AT_TRANSLATE) { + grub_dprintf ("atkeyb", "queried set is %d but keyboard in Translate mode, so actually in set 1\n", grub_keyboard_orig_set); + ps2_state.current_set = 1; + } else { + grub_dprintf ("atkeyb", "using queried set %d\n", grub_keyboard_orig_set); + ps2_state.current_set = grub_keyboard_orig_set; + } return; #else @@ -257,6 +342,7 @@ grub_at_keyboard_getkey (struct grub_term_input *term __attribute__ ((unused))) static void grub_keyboard_controller_init (void) { + grub_dprintf ("atkeyb", "initializing the controller\n"); ps2_state.at_keyboard_status = 0; /* Drain input buffer. */ while (1) @@ -276,7 +362,9 @@ grub_keyboard_controller_init (void) grub_keyboard_orig_set = 2; #else grub_keyboard_controller_orig = grub_keyboard_controller_read (); + grub_dprintf ("atkeyb", "grub_keyboard_controller_orig = 0x%x\n", grub_keyboard_controller_orig); grub_keyboard_orig_set = query_mode (); + grub_dprintf ("atkeyb", "grub_keyboard_orig_set = %d\n", grub_keyboard_orig_set); #endif set_scancodes (); keyboard_controller_led (ps2_state.led_status); @@ -285,11 +373,15 @@ grub_keyboard_controller_init (void) static grub_err_t grub_keyboard_controller_fini (struct grub_term_input *term __attribute__ ((unused))) { +/* In !USE_SCANCODE_SET mode, we didn't change anything, so nothing to restore */ +#if USE_SCANCODE_SET if (ps2_state.current_set == 0) return GRUB_ERR_NONE; + grub_dprintf ("atkeyb", "restoring set %d, controller 0x%x\n", grub_keyboard_orig_set, grub_keyboard_controller_orig); if (grub_keyboard_orig_set) write_mode (grub_keyboard_orig_set); grub_keyboard_controller_write (grub_keyboard_controller_orig); +#endif return GRUB_ERR_NONE; } @@ -320,7 +412,6 @@ grub_at_restore_hw (void) return GRUB_ERR_NONE; } - static struct grub_term_input grub_at_keyboard_term = { .name = "at_keyboard", diff --git a/grub-core/term/ns8250-spcr.c b/grub-core/term/ns8250-spcr.c index d52b52c265..86f1aa078c 100644 --- a/grub-core/term/ns8250-spcr.c +++ b/grub-core/term/ns8250-spcr.c @@ -75,6 +75,11 @@ grub_ns8250_spcr_init (void) config.speed = 115200; break; }; + + /* if base address is 0, it means redirection is disable, so return it */ + if (spcr->base_addr.addr == 0) + return NULL; + switch (spcr->base_addr.space_id) { case GRUB_ACPI_GENADDR_MEM_SPACE: diff --git a/grub-core/term/serial.c b/grub-core/term/serial.c index 8260dcb7a8..fe85010532 100644 --- a/grub-core/term/serial.c +++ b/grub-core/term/serial.c @@ -213,10 +213,15 @@ grub_serial_find (const char *name) #if (defined(__i386__) || defined(__x86_64__)) && !defined(GRUB_MACHINE_IEEE1275) && !defined(GRUB_MACHINE_QEMU) if (grub_strcmp (name, "auto") == 0) { - /* Look for an SPCR if any. If not, default to com0. */ + /* Look for an SPCR if any. If not, default to efi0 or com0, in that order. */ port = grub_ns8250_spcr_init (); if (port != NULL) return port; + + FOR_SERIAL_PORTS (port) + if (grub_strcmp (port->name, "efi0") == 0) + return port; + FOR_SERIAL_PORTS (port) if (grub_strcmp (port->name, "com0") == 0) return port; @@ -350,7 +355,11 @@ grub_cmd_serial (grub_extcmd_context_t ctxt, int argc, char **args) #if !defined (GRUB_MACHINE_EMU) && !defined(GRUB_MACHINE_ARC) && (defined(__mips__) || defined (__i386__) || defined (__x86_64__)) /* Compatibility kludge. */ +#ifdef GRUB_MACHINE_EFI + if (port->driver == &grub_efiserial_driver) +#else if (port->driver == &grub_ns8250_driver) +#endif { if (!registered) { diff --git a/grub-core/term/terminfo.c b/grub-core/term/terminfo.c index 4e534c683e..3dbe88e893 100644 --- a/grub-core/term/terminfo.c +++ b/grub-core/term/terminfo.c @@ -151,7 +151,7 @@ grub_terminfo_set_current (struct grub_term_output *term, /* Clear the screen. Using serial console, screen(1) only recognizes the * ANSI escape sequence. Using video console, Apple Open Firmware * (version 3.1.1) only recognizes the literal ^L. So use both. */ - data->cls = grub_strdup (" \e[2J"); + data->cls = grub_strdup ("\x0c\e[2J\e[m"); data->reverse_video_on = grub_strdup ("\e[7m"); data->reverse_video_off = grub_strdup ("\e[m"); if (grub_strcmp ("ieee1275", str) == 0) diff --git a/grub-core/tests/appended_signature_test.c b/grub-core/tests/appended_signature_test.c new file mode 100644 index 0000000000..dbba061662 --- /dev/null +++ b/grub-core/tests/appended_signature_test.c @@ -0,0 +1,306 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "appended_signatures.h" + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define DEFINE_TEST_CASE(case_name) \ +static char * \ +get_ ## case_name (grub_size_t *sz) \ +{ \ + char *ret; \ + *sz = case_name ## _len; \ + ret = grub_malloc (*sz); \ + if (ret) \ + grub_memcpy (ret, case_name, *sz); \ + return ret; \ +} \ +\ +static struct grub_procfs_entry case_name ## _entry = \ +{ \ + .name = #case_name, \ + .get_contents = get_ ## case_name \ +} + +#define DO_TEST(case_name, is_valid) \ +{ \ + grub_procfs_register (#case_name, &case_name ## _entry); \ + do_verify ("(proc)/" #case_name, is_valid); \ + grub_procfs_unregister (&case_name ## _entry); \ +} + + +DEFINE_TEST_CASE (hi_signed); +DEFINE_TEST_CASE (hi_signed_sha256); +DEFINE_TEST_CASE (hj_signed); +DEFINE_TEST_CASE (short_msg); +DEFINE_TEST_CASE (unsigned_msg); +DEFINE_TEST_CASE (hi_signed_2nd); + +static char * +get_certificate_der (grub_size_t * sz) +{ + char *ret; + *sz = certificate_der_len; + ret = grub_malloc (*sz); + if (ret) + grub_memcpy (ret, certificate_der, *sz); + return ret; +} + +static struct grub_procfs_entry certificate_der_entry = { + .name = "certificate.der", + .get_contents = get_certificate_der +}; + +static char * +get_certificate2_der (grub_size_t * sz) +{ + char *ret; + *sz = certificate2_der_len; + ret = grub_malloc (*sz); + if (ret) + grub_memcpy (ret, certificate2_der, *sz); + return ret; +} + +static struct grub_procfs_entry certificate2_der_entry = { + .name = "certificate2.der", + .get_contents = get_certificate2_der +}; + +static char * +get_certificate_printable_der (grub_size_t * sz) +{ + char *ret; + *sz = certificate_printable_der_len; + ret = grub_malloc (*sz); + if (ret) + grub_memcpy (ret, certificate_printable_der, *sz); + return ret; +} + +static struct grub_procfs_entry certificate_printable_der_entry = { + .name = "certificate_printable.der", + .get_contents = get_certificate_printable_der +}; + +static char * +get_certificate_eku_der (grub_size_t * sz) +{ + char *ret; + *sz = certificate_eku_der_len; + ret = grub_malloc (*sz); + if (ret) + grub_memcpy (ret, certificate_eku_der, *sz); + return ret; +} + +static struct grub_procfs_entry certificate_eku_der_entry = { + .name = "certificate_eku.der", + .get_contents = get_certificate_eku_der +}; + + +static void +do_verify (const char *f, int is_valid) +{ + grub_command_t cmd; + char *args[] = { (char *) f, NULL }; + grub_err_t err; + + cmd = grub_command_find ("verify_appended"); + if (!cmd) + { + grub_test_assert (0, "can't find command `%s'", "verify_appended"); + return; + } + err = (cmd->func) (cmd, 1, args); + if (is_valid) + { + grub_test_assert (err == GRUB_ERR_NONE, + "verification of %s failed: %d: %s", f, grub_errno, + grub_errmsg); + } + else + { + grub_test_assert (err == GRUB_ERR_BAD_SIGNATURE, + "verification of %s unexpectedly succeeded", f); + } + grub_errno = GRUB_ERR_NONE; + +} + +static void +appended_signature_test (void) +{ + grub_command_t cmd_trust, cmd_distrust; + char *trust_args[] = { (char *) "(proc)/certificate.der", NULL }; + char *trust_args2[] = { (char *) "(proc)/certificate2.der", NULL }; + char *trust_args_printable[] = { (char *) "(proc)/certificate_printable.der", + NULL }; + char *trust_args_eku[] = { (char *) "(proc)/certificate_eku.der", NULL }; + char *distrust_args[] = { (char *) "1", NULL }; + char *distrust2_args[] = { (char *) "2", NULL }; + grub_err_t err; + + grub_procfs_register ("certificate.der", &certificate_der_entry); + grub_procfs_register ("certificate2.der", &certificate2_der_entry); + grub_procfs_register ("certificate_printable.der", + &certificate_printable_der_entry); + grub_procfs_register ("certificate_eku.der", &certificate_eku_der_entry); + + cmd_trust = grub_command_find ("trust_certificate"); + if (!cmd_trust) + { + grub_test_assert (0, "can't find command `%s'", "trust_certificate"); + return; + } + err = (cmd_trust->func) (cmd_trust, 1, trust_args); + + grub_test_assert (err == GRUB_ERR_NONE, + "loading certificate failed: %d: %s", grub_errno, + grub_errmsg); + + /* If we have no certificate the remainder of the tests are meaningless */ + if (err != GRUB_ERR_NONE) + return; + + /* + * Reload the command: this works around some 'interesting' behaviour in the + * dynamic command dispatcher. The first time you call cmd->func you get a + * dispatcher that loads the module, finds the real cmd, calls it, and then + * releases some internal storage. This means it's not safe to call a second + * time and we need to reload it. + */ + cmd_trust = grub_command_find ("trust_certificate"); + + DO_TEST (hi_signed, 1); + DO_TEST (hi_signed_sha256, 1); + DO_TEST (hj_signed, 0); + DO_TEST (short_msg, 0); + DO_TEST (unsigned_msg, 0); + + /* + * in enforcing mode, we shouldn't be able to load a certificate that isn't + * signed by an existing trusted key. + * + * However, procfs files automatically skip the verification test, so we can't + * easily test this. + */ + + /* + * verify that testing with 2 trusted certs works + */ + DO_TEST (hi_signed_2nd, 0); + + err = (cmd_trust->func) (cmd_trust, 1, trust_args2); + + grub_test_assert (err == GRUB_ERR_NONE, + "loading certificate 2 failed: %d: %s", grub_errno, + grub_errmsg); + + if (err != GRUB_ERR_NONE) + return; + + DO_TEST (hi_signed_2nd, 1); + DO_TEST (hi_signed, 1); + + /* + * Check certificate removal. They're added to the _top_ of the list and + * removed by position in the list. Current the list looks like [#2, #1]. + * + * First test removing the second certificate in the list, which is + * certificate #1, giving us just [#2]. + */ + cmd_distrust = grub_command_find ("distrust_certificate"); + if (!cmd_distrust) + { + grub_test_assert (0, "can't find command `%s'", "distrust_certificate"); + return; + } + + err = (cmd_distrust->func) (cmd_distrust, 1, distrust2_args); + grub_test_assert (err == GRUB_ERR_NONE, + "distrusting certificate 1 failed: %d: %s", grub_errno, + grub_errmsg); + DO_TEST (hi_signed_2nd, 1); + DO_TEST (hi_signed, 0); + + /* + * Now reload certificate #1. This will make the list look like [#1, #2] + */ + err = (cmd_trust->func) (cmd_trust, 1, trust_args); + + grub_test_assert (err == GRUB_ERR_NONE, + "reloading certificate 1 failed: %d: %s", grub_errno, + grub_errmsg); + DO_TEST (hi_signed, 1); + + /* Remove the first certificate in the list, giving us just [#2] */ + err = (cmd_distrust->func) (cmd_distrust, 1, distrust_args); + grub_test_assert (err == GRUB_ERR_NONE, + "distrusting certificate 1 (first time) failed: %d: %s", + grub_errno, grub_errmsg); + DO_TEST (hi_signed_2nd, 1); + DO_TEST (hi_signed, 0); + + /* + * Remove the first certificate again, giving an empty list. + * + * verify_appended should fail if there are no certificates to verify against. + */ + err = (cmd_distrust->func) (cmd_distrust, 1, distrust_args); + grub_test_assert (err == GRUB_ERR_NONE, + "distrusting certificate 1 (second time) failed: %d: %s", + grub_errno, grub_errmsg); + DO_TEST (hi_signed_2nd, 0); + + /* + * Lastly, check a certificate that uses printableString rather than + * utf8String loads properly, and that a certificate with an appropriate + * extended key usage loads. + */ + err = (cmd_trust->func) (cmd_trust, 1, trust_args_printable); + grub_test_assert (err == GRUB_ERR_NONE, + "trusting printable certificate failed: %d: %s", + grub_errno, grub_errmsg); + + err = (cmd_trust->func) (cmd_trust, 1, trust_args_eku); + grub_test_assert (err == GRUB_ERR_NONE, + "trusting certificate with extended key usage failed: %d: %s", + grub_errno, grub_errmsg); + + grub_procfs_unregister (&certificate_der_entry); + grub_procfs_unregister (&certificate2_der_entry); + grub_procfs_unregister (&certificate_printable_der_entry); + grub_procfs_unregister (&certificate_eku_der_entry); +} + +GRUB_FUNCTIONAL_TEST (appended_signature_test, appended_signature_test); diff --git a/grub-core/tests/appended_signatures.h b/grub-core/tests/appended_signatures.h new file mode 100644 index 0000000000..2e5ebd7d8b --- /dev/null +++ b/grub-core/tests/appended_signatures.h @@ -0,0 +1,638 @@ +unsigned char certificate_der[] = { + 0x30, 0x82, 0x03, 0x88, 0x30, 0x82, 0x02, 0x70, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x14, 0x25, 0x2e, 0xb8, 0xfd, 0x12, 0x62, 0x2e, 0xcd, 0x5d, + 0xa7, 0x53, 0xd2, 0x0b, 0xc2, 0x61, 0x7c, 0x14, 0xe0, 0x0f, 0x5c, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x30, 0x49, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x1f, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, + 0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1d, + 0x30, 0x1b, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, + 0x01, 0x16, 0x0e, 0x64, 0x6a, 0x61, 0x40, 0x61, 0x78, 0x74, 0x65, 0x6e, + 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x30, 0x30, + 0x37, 0x30, 0x39, 0x30, 0x36, 0x32, 0x32, 0x30, 0x37, 0x5a, 0x18, 0x0f, + 0x32, 0x31, 0x32, 0x30, 0x30, 0x36, 0x31, 0x35, 0x30, 0x36, 0x32, 0x32, + 0x30, 0x37, 0x5a, 0x30, 0x52, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x28, 0x47, 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, + 0x65, 0x6e, 0x64, 0x65, 0x64, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x53, 0x69, 0x67, + 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x31, 0x1d, 0x30, 0x1b, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, + 0x0e, 0x64, 0x6a, 0x61, 0x40, 0x61, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x2e, + 0x6e, 0x65, 0x74, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xcd, 0xe8, 0x1c, 0x08, 0x68, 0x2e, 0xcb, 0xfe, 0x8c, 0x4b, 0x3b, 0x61, + 0xe7, 0x8e, 0x80, 0x58, 0x85, 0x85, 0xea, 0xc8, 0x3b, 0x42, 0xba, 0x72, + 0x84, 0x65, 0x20, 0xbc, 0x48, 0xa2, 0x25, 0x49, 0x6e, 0x1c, 0xb9, 0x7d, + 0xeb, 0xc1, 0x0c, 0xa8, 0xb7, 0xcc, 0x13, 0x78, 0xba, 0x11, 0xa4, 0x98, + 0xd7, 0xd0, 0x7c, 0xdd, 0xf5, 0x5a, 0xb7, 0xcd, 0x31, 0x0e, 0xcd, 0x9e, + 0xa7, 0x19, 0xf0, 0xbd, 0x0f, 0xa6, 0xfe, 0x8a, 0x11, 0x97, 0xed, 0x8b, + 0xe5, 0x16, 0xa6, 0x21, 0x13, 0x36, 0xad, 0x05, 0x49, 0xec, 0x29, 0x12, + 0x38, 0xa7, 0x4b, 0x0f, 0xa1, 0xfb, 0x72, 0xc0, 0xc0, 0x09, 0x67, 0x78, + 0xa8, 0xb6, 0xd6, 0x1a, 0x39, 0xc0, 0xa8, 0xbf, 0x5f, 0x14, 0x89, 0x5c, + 0xbc, 0x41, 0x0c, 0x0c, 0x5d, 0x42, 0x2e, 0x1c, 0xdf, 0x1f, 0x1d, 0xc9, + 0x43, 0x94, 0x5b, 0x6e, 0x8f, 0x15, 0x8c, 0x8f, 0x94, 0x73, 0x4f, 0x97, + 0x54, 0xf1, 0x86, 0x8a, 0xbc, 0xe4, 0xe4, 0x93, 0xc1, 0x5e, 0xc2, 0x3e, + 0x31, 0x5e, 0xd4, 0x85, 0x57, 0x14, 0xd0, 0x11, 0x07, 0x65, 0xf4, 0x7c, + 0x8f, 0x07, 0x57, 0xe1, 0x22, 0xd4, 0x78, 0x47, 0x65, 0x4e, 0xa9, 0xb3, + 0xaa, 0xce, 0xc7, 0x36, 0xfe, 0xda, 0x66, 0x02, 0xb6, 0x8d, 0x18, 0x2f, + 0x3b, 0x41, 0x8d, 0x02, 0x08, 0x72, 0x4b, 0x69, 0xbd, 0x1e, 0x58, 0xfc, + 0x1b, 0x64, 0x04, 0x52, 0x35, 0x35, 0xe2, 0x3d, 0x3e, 0xde, 0xd6, 0x64, + 0xf4, 0xec, 0x57, 0x7e, 0x65, 0x59, 0x00, 0xa6, 0xd3, 0x4b, 0x09, 0x93, + 0x2a, 0x95, 0x0f, 0x30, 0xb6, 0xa1, 0x8c, 0xe7, 0x8b, 0x49, 0xa4, 0x1d, + 0x25, 0x2d, 0x65, 0x48, 0x8a, 0x0f, 0xcf, 0x2a, 0xa2, 0xe1, 0xef, 0x72, + 0x92, 0xc3, 0xf5, 0x21, 0x37, 0x83, 0x9b, 0x6d, 0x0b, 0x1b, 0xb3, 0xa2, + 0x32, 0x38, 0x11, 0xb1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30, + 0x5b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, + 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, + 0x03, 0x02, 0x07, 0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, + 0x16, 0x04, 0x14, 0xe5, 0x2a, 0x4f, 0xf2, 0x84, 0x91, 0x57, 0x91, 0xaf, + 0x12, 0xd2, 0xf1, 0xa1, 0x87, 0x73, 0x0f, 0x90, 0x25, 0xa0, 0x7a, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0x56, 0xd1, 0xfd, 0xe2, 0x1e, 0x7e, 0x1c, 0x63, 0x4f, 0x47, 0xdb, 0xe4, + 0xc4, 0x51, 0x04, 0x03, 0x9a, 0x48, 0x35, 0x6e, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x01, 0x00, 0x65, 0x82, 0xd5, 0x88, 0x30, 0xe2, 0x2c, 0x47, + 0xf3, 0x31, 0x39, 0xa1, 0x75, 0x9a, 0xb0, 0x8a, 0x6c, 0x4b, 0xac, 0xdf, + 0x09, 0x7b, 0x90, 0xb6, 0x9e, 0x76, 0x62, 0x94, 0xc1, 0x3a, 0x99, 0x49, + 0x68, 0x29, 0x47, 0x42, 0xc3, 0x06, 0xcb, 0x88, 0x75, 0xe6, 0x79, 0x13, + 0x8c, 0x4b, 0x49, 0x6a, 0xb5, 0x56, 0x95, 0xc0, 0x42, 0x21, 0x9b, 0xd4, + 0x61, 0xd0, 0x02, 0x41, 0xdd, 0x20, 0x61, 0xe5, 0x91, 0xdf, 0x75, 0x00, + 0x25, 0x0e, 0x99, 0x65, 0x5c, 0x54, 0x49, 0x32, 0xa3, 0xe2, 0xcd, 0xa1, + 0x5f, 0x40, 0xf3, 0xc5, 0x81, 0xd9, 0x3c, 0xa3, 0x63, 0x5a, 0x38, 0x79, + 0xab, 0x77, 0x98, 0xde, 0x8f, 0x4e, 0x9e, 0x26, 0xbc, 0x4e, 0x80, 0x9e, + 0x8f, 0xbe, 0xf1, 0x00, 0xb3, 0x78, 0xb9, 0x4b, 0x1d, 0xc7, 0xa4, 0x83, + 0x59, 0x56, 0x11, 0xd1, 0x11, 0x1e, 0x50, 0x39, 0xd5, 0x78, 0x14, 0xf3, + 0xb9, 0x1d, 0xda, 0xe4, 0xc4, 0x63, 0x74, 0x26, 0xab, 0xa3, 0xfd, 0x9d, + 0x58, 0xa2, 0xee, 0x7b, 0x28, 0x34, 0xa3, 0xbe, 0x85, 0x7e, 0xaa, 0x97, + 0xb7, 0x5b, 0x9d, 0xa9, 0x4d, 0x96, 0xdb, 0x6b, 0x21, 0xe1, 0x96, 0x5d, + 0xc7, 0xad, 0x23, 0x03, 0x9a, 0x16, 0xdb, 0xa4, 0x1f, 0x63, 0xef, 0xaf, + 0x1e, 0x4f, 0xf8, 0x27, 0xdc, 0x4b, 0xfc, 0x2b, 0x68, 0x2e, 0xa0, 0xd3, + 0xae, 0xf2, 0xce, 0xf5, 0xfc, 0x97, 0x92, 0xd2, 0x29, 0x0f, 0x4f, 0x4b, + 0x29, 0xeb, 0x06, 0xcb, 0xf8, 0x21, 0x6e, 0xbc, 0x8b, 0x5c, 0xc5, 0xc9, + 0xf7, 0xe2, 0x7c, 0x47, 0xcd, 0x43, 0x98, 0xc4, 0xa3, 0x9a, 0xd7, 0x3e, + 0xdc, 0x01, 0x13, 0x28, 0x96, 0xc4, 0x60, 0x83, 0xe2, 0x79, 0xa1, 0x46, + 0xef, 0xf5, 0xa4, 0x7b, 0x00, 0xe3, 0x3d, 0x7d, 0xbc, 0xa8, 0x98, 0x49, + 0xa8, 0xcf, 0x3b, 0x41, 0xb6, 0x09, 0x97, 0x07 +}; +unsigned int certificate_der_len = 908; + +unsigned char hi_signed[] = { + 0x68, 0x69, 0x0a, 0x30, 0x82, 0x01, 0xc0, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x01, 0xb1, 0x30, 0x82, + 0x01, 0xad, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x01, + 0x8a, 0x30, 0x82, 0x01, 0x86, 0x02, 0x01, 0x01, 0x30, 0x61, 0x30, 0x49, + 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1f, 0x47, + 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, + 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, + 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0e, 0x64, + 0x6a, 0x61, 0x40, 0x61, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x2e, 0x6e, 0x65, + 0x74, 0x02, 0x14, 0x25, 0x2e, 0xb8, 0xfd, 0x12, 0x62, 0x2e, 0xcd, 0x5d, + 0xa7, 0x53, 0xd2, 0x0b, 0xc2, 0x61, 0x7c, 0x14, 0xe0, 0x0f, 0x5c, 0x30, + 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0xc7, 0x69, 0x35, 0x21, 0x66, + 0x4d, 0x50, 0xd4, 0x73, 0xde, 0xbd, 0x3a, 0xf6, 0x45, 0xe3, 0xe4, 0xd0, + 0xb6, 0xa1, 0xe7, 0xc0, 0xa2, 0xc9, 0xf4, 0xf0, 0x05, 0x8c, 0xa4, 0x16, + 0x9e, 0x81, 0x0d, 0x21, 0x68, 0xf3, 0xfe, 0x03, 0x96, 0x77, 0x31, 0x69, + 0x01, 0xd8, 0x26, 0xd9, 0x48, 0x95, 0xcf, 0xd1, 0x17, 0xb1, 0x0b, 0x6b, + 0x2c, 0xf1, 0xb0, 0xab, 0x65, 0x65, 0x56, 0xf8, 0x0c, 0xa7, 0xf7, 0xbb, + 0xf6, 0x5a, 0x55, 0x98, 0x14, 0x07, 0x8d, 0x2a, 0xbc, 0x16, 0x48, 0x94, + 0xab, 0x2f, 0x85, 0x97, 0x90, 0x51, 0x78, 0xa0, 0xda, 0x60, 0xb5, 0x41, + 0x4b, 0xe8, 0x78, 0xc5, 0xa6, 0x04, 0x9d, 0x54, 0x2a, 0x85, 0xfd, 0x86, + 0x0b, 0x6d, 0xc2, 0xd2, 0xad, 0x07, 0xff, 0x16, 0x42, 0x82, 0xe3, 0x5c, + 0xaa, 0x22, 0x59, 0x78, 0x92, 0xea, 0x94, 0xc3, 0x41, 0xb7, 0xa1, 0x86, + 0x44, 0xea, 0xd1, 0xdb, 0xe5, 0xac, 0x30, 0x32, 0xfb, 0x7d, 0x3f, 0xf7, + 0x8b, 0x11, 0x7f, 0x80, 0x3b, 0xe5, 0xc7, 0x82, 0x0f, 0x92, 0x07, 0x14, + 0x66, 0x01, 0x6e, 0x85, 0xab, 0x3a, 0x14, 0xcf, 0x76, 0xd1, 0x7e, 0x14, + 0x85, 0xca, 0x01, 0x73, 0x72, 0x38, 0xdc, 0xde, 0x30, 0x5c, 0xfb, 0xc0, + 0x3d, 0x93, 0xef, 0x9c, 0xbc, 0xf8, 0xcc, 0xd2, 0xbf, 0x47, 0xec, 0xf8, + 0x88, 0x9b, 0xe1, 0x43, 0xbe, 0xa7, 0x47, 0x96, 0xb6, 0x5d, 0x46, 0x0e, + 0x7a, 0x78, 0x38, 0x19, 0xbc, 0xb5, 0xbc, 0x9b, 0x3c, 0x39, 0x92, 0x70, + 0x0d, 0x9d, 0x8a, 0x35, 0xaf, 0xb4, 0x9e, 0xf4, 0xef, 0xc1, 0xb8, 0x25, + 0xd0, 0x14, 0x91, 0xd6, 0xc2, 0xb6, 0xc7, 0x3c, 0x72, 0x91, 0x0f, 0xad, + 0xde, 0xb2, 0x36, 0xf8, 0x4e, 0x59, 0xd4, 0xa4, 0x21, 0x9f, 0x03, 0x95, + 0x48, 0x01, 0xb4, 0x05, 0xc3, 0x39, 0x60, 0x51, 0x08, 0xd0, 0xbe, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x7e, + 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, + 0x64, 0x7e, 0x0a +}; +unsigned int hi_signed_len = 495; + +unsigned char hj_signed[] = { + 0x68, 0x6a, 0x0a, 0x30, 0x82, 0x01, 0xc0, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x01, 0xb1, 0x30, 0x82, + 0x01, 0xad, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x01, + 0x8a, 0x30, 0x82, 0x01, 0x86, 0x02, 0x01, 0x01, 0x30, 0x61, 0x30, 0x49, + 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1f, 0x47, + 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, + 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, + 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0e, 0x64, + 0x6a, 0x61, 0x40, 0x61, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x2e, 0x6e, 0x65, + 0x74, 0x02, 0x14, 0x25, 0x2e, 0xb8, 0xfd, 0x12, 0x62, 0x2e, 0xcd, 0x5d, + 0xa7, 0x53, 0xd2, 0x0b, 0xc2, 0x61, 0x7c, 0x14, 0xe0, 0x0f, 0x5c, 0x30, + 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0xc7, 0x69, 0x35, 0x21, 0x66, + 0x4d, 0x50, 0xd4, 0x73, 0xde, 0xbd, 0x3a, 0xf6, 0x45, 0xe3, 0xe4, 0xd0, + 0xb6, 0xa1, 0xe7, 0xc0, 0xa2, 0xc9, 0xf4, 0xf0, 0x05, 0x8c, 0xa4, 0x16, + 0x9e, 0x81, 0x0d, 0x21, 0x68, 0xf3, 0xfe, 0x03, 0x96, 0x77, 0x31, 0x69, + 0x01, 0xd8, 0x26, 0xd9, 0x48, 0x95, 0xcf, 0xd1, 0x17, 0xb1, 0x0b, 0x6b, + 0x2c, 0xf1, 0xb0, 0xab, 0x65, 0x65, 0x56, 0xf8, 0x0c, 0xa7, 0xf7, 0xbb, + 0xf6, 0x5a, 0x55, 0x98, 0x14, 0x07, 0x8d, 0x2a, 0xbc, 0x16, 0x48, 0x94, + 0xab, 0x2f, 0x85, 0x97, 0x90, 0x51, 0x78, 0xa0, 0xda, 0x60, 0xb5, 0x41, + 0x4b, 0xe8, 0x78, 0xc5, 0xa6, 0x04, 0x9d, 0x54, 0x2a, 0x85, 0xfd, 0x86, + 0x0b, 0x6d, 0xc2, 0xd2, 0xad, 0x07, 0xff, 0x16, 0x42, 0x82, 0xe3, 0x5c, + 0xaa, 0x22, 0x59, 0x78, 0x92, 0xea, 0x94, 0xc3, 0x41, 0xb7, 0xa1, 0x86, + 0x44, 0xea, 0xd1, 0xdb, 0xe5, 0xac, 0x30, 0x32, 0xfb, 0x7d, 0x3f, 0xf7, + 0x8b, 0x11, 0x7f, 0x80, 0x3b, 0xe5, 0xc7, 0x82, 0x0f, 0x92, 0x07, 0x14, + 0x66, 0x01, 0x6e, 0x85, 0xab, 0x3a, 0x14, 0xcf, 0x76, 0xd1, 0x7e, 0x14, + 0x85, 0xca, 0x01, 0x73, 0x72, 0x38, 0xdc, 0xde, 0x30, 0x5c, 0xfb, 0xc0, + 0x3d, 0x93, 0xef, 0x9c, 0xbc, 0xf8, 0xcc, 0xd2, 0xbf, 0x47, 0xec, 0xf8, + 0x88, 0x9b, 0xe1, 0x43, 0xbe, 0xa7, 0x47, 0x96, 0xb6, 0x5d, 0x46, 0x0e, + 0x7a, 0x78, 0x38, 0x19, 0xbc, 0xb5, 0xbc, 0x9b, 0x3c, 0x39, 0x92, 0x70, + 0x0d, 0x9d, 0x8a, 0x35, 0xaf, 0xb4, 0x9e, 0xf4, 0xef, 0xc1, 0xb8, 0x25, + 0xd0, 0x14, 0x91, 0xd6, 0xc2, 0xb6, 0xc7, 0x3c, 0x72, 0x91, 0x0f, 0xad, + 0xde, 0xb2, 0x36, 0xf8, 0x4e, 0x59, 0xd4, 0xa4, 0x21, 0x9f, 0x03, 0x95, + 0x48, 0x01, 0xb4, 0x05, 0xc3, 0x39, 0x60, 0x51, 0x08, 0xd0, 0xbe, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x7e, + 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, + 0x64, 0x7e, 0x0a +}; +unsigned int hj_signed_len = 495; + +unsigned char hi_signed_sha256[] = { + 0x68, 0x69, 0x0a, 0x30, 0x82, 0x01, 0xc0, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x01, 0xb1, 0x30, 0x82, + 0x01, 0xad, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x01, + 0x8a, 0x30, 0x82, 0x01, 0x86, 0x02, 0x01, 0x01, 0x30, 0x61, 0x30, 0x49, + 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1f, 0x47, + 0x72, 0x75, 0x62, 0x20, 0x41, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x64, + 0x20, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, + 0x65, 0x73, 0x74, 0x20, 0x43, 0x41, 0x31, 0x1d, 0x30, 0x1b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0e, 0x64, + 0x6a, 0x61, 0x40, 0x61, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x2e, 0x6e, 0x65, + 0x74, 0x02, 0x14, 0x25, 0x2e, 0xb8, 0xfd, 0x12, 0x62, 0x2e, 0xcd, 0x5d, + 0xa7, 0x53, 0xd2, 0x0b, 0xc2, 0x61, 0x7c, 0x14, 0xe0, 0x0f, 0x5c, 0x30, + 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0x7b, 0x5e, 0x82, 0x1d, 0x21, + 0xb6, 0x40, 0xd3, 0x33, 0x79, 0xa7, 0x52, 0x2b, 0xfc, 0x46, 0x51, 0x26, + 0xfe, 0x0f, 0x81, 0x90, 0x81, 0xab, 0x57, 0x5e, 0xf6, 0x45, 0x41, 0xa3, + 0x7b, 0x48, 0xdd, 0xd6, 0x59, 0x60, 0x51, 0x31, 0x14, 0x14, 0x7b, 0xb4, + 0x55, 0x7b, 0x4d, 0xfe, 0x09, 0x7a, 0x5d, 0xae, 0xc4, 0x58, 0x50, 0x80, + 0x75, 0xf2, 0x23, 0x20, 0x62, 0xe3, 0x7c, 0x26, 0x1d, 0x2a, 0x4d, 0x9f, + 0x89, 0xf0, 0x4f, 0x95, 0x8a, 0x80, 0x6e, 0x1a, 0xea, 0x87, 0xdb, 0x1f, + 0xf3, 0xda, 0x04, 0x91, 0x37, 0xea, 0x0a, 0xfb, 0x6c, 0xc9, 0x3d, 0x73, + 0xf9, 0x58, 0x7c, 0x15, 0x6b, 0xa2, 0x52, 0x5a, 0x97, 0xff, 0xd6, 0xb0, + 0xf1, 0xbf, 0xa5, 0x04, 0x6d, 0x91, 0xc1, 0x54, 0x05, 0xdc, 0x7f, 0x5d, + 0x19, 0xaf, 0x55, 0xec, 0x51, 0xfb, 0x66, 0x0a, 0xa4, 0x4e, 0x96, 0x47, + 0x43, 0x54, 0x7c, 0x64, 0xa8, 0xaa, 0xb4, 0x90, 0x02, 0xf3, 0xa7, 0x0b, + 0xb7, 0xbf, 0x06, 0xdb, 0x5e, 0x9c, 0x32, 0x6d, 0x45, 0x14, 0x1c, 0xaf, + 0x46, 0x30, 0x08, 0x55, 0x49, 0x78, 0xfa, 0x57, 0xda, 0x3d, 0xf5, 0xa0, + 0xef, 0x11, 0x0a, 0x81, 0x0d, 0x82, 0xcd, 0xaf, 0xdb, 0xda, 0x0e, 0x1a, + 0x44, 0xd1, 0xee, 0xc4, 0xb8, 0xde, 0x97, 0xb4, 0xda, 0xb4, 0x8b, 0x4f, + 0x58, 0x24, 0x59, 0xc0, 0xe0, 0x08, 0x97, 0x14, 0x68, 0xbe, 0x31, 0x09, + 0x5e, 0x67, 0x45, 0xf0, 0xcb, 0x81, 0x4f, 0x17, 0x44, 0x61, 0xe0, 0xe2, + 0xf0, 0xfc, 0x1e, 0xb9, 0x73, 0xaf, 0x42, 0xff, 0x33, 0xde, 0x61, 0x6b, + 0x7f, 0xc2, 0x69, 0x0d, 0x66, 0x54, 0xae, 0xf6, 0xde, 0x20, 0x47, 0x44, + 0x9b, 0x73, 0xd1, 0x07, 0x6e, 0x77, 0x37, 0x0a, 0xbb, 0x7f, 0xa0, 0x93, + 0x2d, 0x8d, 0x44, 0xba, 0xe2, 0xdd, 0x34, 0x32, 0xd7, 0x56, 0x71, 0x00, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc4, 0x7e, + 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, 0x65, + 0x64, 0x7e, 0x0a +}; +unsigned int hi_signed_sha256_len = 495; + +unsigned char short_msg[] = { + 0x68, 0x69, 0x0a +}; +unsigned int short_msg_len = 3; + +unsigned char unsigned_msg[] = { + 0x53, 0x65, 0x64, 0x20, 0x75, 0x74, 0x20, 0x70, 0x65, 0x72, 0x73, 0x70, + 0x69, 0x63, 0x69, 0x61, 0x74, 0x69, 0x73, 0x20, 0x75, 0x6e, 0x64, 0x65, + 0x20, 0x6f, 0x6d, 0x6e, 0x69, 0x73, 0x20, 0x69, 0x73, 0x74, 0x65, 0x20, + 0x6e, 0x61, 0x74, 0x75, 0x73, 0x20, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x20, + 0x73, 0x69, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74, + 0x65, 0x6d, 0x20, 0x61, 0x63, 0x63, 0x75, 0x73, 0x61, 0x6e, 0x74, 0x69, + 0x75, 0x6d, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x71, 0x75, + 0x65, 0x20, 0x6c, 0x61, 0x75, 0x64, 0x61, 0x6e, 0x74, 0x69, 0x75, 0x6d, + 0x2c, 0x20, 0x74, 0x6f, 0x74, 0x61, 0x6d, 0x20, 0x72, 0x65, 0x6d, 0x20, + 0x61, 0x70, 0x65, 0x72, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x65, 0x61, 0x71, + 0x75, 0x65, 0x20, 0x69, 0x70, 0x73, 0x61, 0x20, 0x71, 0x75, 0x61, 0x65, + 0x20, 0x61, 0x62, 0x20, 0x69, 0x6c, 0x6c, 0x6f, 0x20, 0x69, 0x6e, 0x76, + 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x65, 0x20, 0x76, 0x65, 0x72, 0x69, 0x74, + 0x61, 0x74, 0x69, 0x73, 0x20, 0x65, 0x74, 0x20, 0x71, 0x75, 0x61, 0x73, + 0x69, 0x20, 0x61, 0x72, 0x63, 0x68, 0x69, 0x74, 0x65, 0x63, 0x74, 0x6f, + 0x20, 0x62, 0x65, 0x61, 0x74, 0x61, 0x65, 0x20, 0x76, 0x69, 0x74, 0x61, + 0x65, 0x20, 0x64, 0x69, 0x63, 0x74, 0x61, 0x20, 0x73, 0x75, 0x6e, 0x74, + 0x20, 0x65, 0x78, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x62, 0x6f, 0x2e, 0x20, + 0x4e, 0x65, 0x6d, 0x6f, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20, 0x69, 0x70, + 0x73, 0x61, 0x6d, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74, + 0x65, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75, + 0x70, 0x74, 0x61, 0x73, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x73, 0x70, + 0x65, 0x72, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x20, 0x61, 0x75, 0x74, 0x20, + 0x6f, 0x64, 0x69, 0x74, 0x20, 0x61, 0x75, 0x74, 0x20, 0x66, 0x75, 0x67, + 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64, 0x20, 0x71, 0x75, 0x69, 0x61, + 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x75, 0x6e, 0x74, 0x75, + 0x72, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, + 0x72, 0x65, 0x73, 0x20, 0x65, 0x6f, 0x73, 0x20, 0x71, 0x75, 0x69, 0x20, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x20, 0x76, 0x6f, 0x6c, 0x75, + 0x70, 0x74, 0x61, 0x74, 0x65, 0x6d, 0x20, 0x73, 0x65, 0x71, 0x75, 0x69, + 0x20, 0x6e, 0x65, 0x73, 0x63, 0x69, 0x75, 0x6e, 0x74, 0x2e, 0x20, 0x4e, + 0x65, 0x71, 0x75, 0x65, 0x20, 0x70, 0x6f, 0x72, 0x72, 0x6f, 0x20, 0x71, + 0x75, 0x69, 0x73, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x73, 0x74, 0x2c, + 0x20, 0x71, 0x75, 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, + 0x20, 0x69, 0x70, 0x73, 0x75, 0x6d, 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, + 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x20, 0x73, 0x69, 0x74, 0x20, 0x61, 0x6d, + 0x65, 0x74, 0x2c, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x63, 0x74, 0x65, + 0x74, 0x75, 0x72, 0x2c, 0x20, 0x61, 0x64, 0x69, 0x70, 0x69, 0x73, 0x63, + 0x69, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x2c, 0x20, 0x73, 0x65, 0x64, + 0x20, 0x71, 0x75, 0x69, 0x61, 0x20, 0x6e, 0x6f, 0x6e, 0x20, 0x6e, 0x75, + 0x6d, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x65, 0x69, 0x75, 0x73, 0x20, 0x6d, + 0x6f, 0x64, 0x69, 0x20, 0x74, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x20, + 0x69, 0x6e, 0x63, 0x69, 0x64, 0x75, 0x6e, 0x74, 0x20, 0x75, 0x74, 0x20, + 0x6c, 0x61, 0x62, 0x6f, 0x72, 0x65, 0x20, 0x65, 0x74, 0x20, 0x64, 0x6f, + 0x6c, 0x6f, 0x72, 0x65, 0x20, 0x6d, 0x61, 0x67, 0x6e, 0x61, 0x6d, 0x20, + 0x61, 0x6c, 0x69, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x71, 0x75, 0x61, 0x65, + 0x72, 0x61, 0x74, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x74, + 0x65, 0x6d, 0x2e, 0x20, 0x55, 0x74, 0x20, 0x65, 0x6e, 0x69, 0x6d, 0x20, + 0x61, 0x64, 0x20, 0x6d, 0x69, 0x6e, 0x69, 0x6d, 0x61, 0x20, 0x76, 0x65, + 0x6e, 0x69, 0x61, 0x6d, 0x2c, 0x20, 0x71, 0x75, 0x69, 0x73, 0x20, 0x6e, + 0x6f, 0x73, 0x74, 0x72, 0x75, 0x6d, 0x20, 0x65, 0x78, 0x65, 0x72, 0x63, + 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x65, 0x6d, 0x20, 0x75, 0x6c, + 0x6c, 0x61, 0x6d, 0x20, 0x63, 0x6f, 0x72, 0x70, 0x6f, 0x72, 0x69, 0x73, + 0x20, 0x73, 0x75, 0x73, 0x63, 0x69, 0x70, 0x69, 0x74, 0x20, 0x6c, 0x61, + 0x62, 0x6f, 0x72, 0x69, 0x6f, 0x73, 0x61, 0x6d, 0x2c, 0x20, 0x6e, 0x69, + 0x73, 0x69, 0x20, 0x75, 0x74, 0x20, 0x61, 0x6c, 0x69, 0x71, 0x75, 0x69, + 0x64, 0x20, 0x65, 0x78, 0x20, 0x65, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x6d, + 0x6f, 0x64, 0x69, 0x20, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61, + 0x74, 0x75, 0x72, 0x3f, 0x20, 0x51, 0x75, 0x69, 0x73, 0x20, 0x61, 0x75, + 0x74, 0x65, 0x6d, 0x20, 0x76, 0x65, 0x6c, 0x20, 0x65, 0x75, 0x6d, 0x20, + 0x69, 0x75, 0x72, 0x65, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x68, 0x65, + 0x6e, 0x64, 0x65, 0x72, 0x69, 0x74, 0x20, 0x71, 0x75, 0x69, 0x20, 0x69, + 0x6e, 0x20, 0x65, 0x61, 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, + 0x74, 0x65, 0x20, 0x76, 0x65, 0x6c, 0x69, 0x74, 0x20, 0x65, 0x73, 0x73, + 0x65, 0x20, 0x71, 0x75, 0x61, 0x6d, 0x20, 0x6e, 0x69, 0x68, 0x69, 0x6c, + 0x20, 0x6d, 0x6f, 0x6c, 0x65, 0x73, 0x74, 0x69, 0x61, 0x65, 0x20, 0x63, + 0x6f, 0x6e, 0x73, 0x65, 0x71, 0x75, 0x61, 0x74, 0x75, 0x72, 0x2c, 0x20, + 0x76, 0x65, 0x6c, 0x20, 0x69, 0x6c, 0x6c, 0x75, 0x6d, 0x20, 0x71, 0x75, + 0x69, 0x20, 0x64, 0x6f, 0x6c, 0x6f, 0x72, 0x65, 0x6d, 0x20, 0x65, 0x75, + 0x6d, 0x20, 0x66, 0x75, 0x67, 0x69, 0x61, 0x74, 0x20, 0x71, 0x75, 0x6f, + 0x20, 0x76, 0x6f, 0x6c, 0x75, 0x70, 0x74, 0x61, 0x73, 0x20, 0x6e, 0x75, + 0x6c, 0x6c, 0x61, 0x20, 0x70, 0x61, 0x72, 0x69, 0x61, 0x74, 0x75, 0x72, + 0x3f, 0x0a +}; +unsigned int unsigned_msg_len = 866; + +unsigned char certificate2_der[] = { + 0x30, 0x82, 0x05, 0x52, 0x30, 0x82, 0x03, 0x3a, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x14, 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, + 0x3a, 0x91, 0x07, 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x30, 0x3a, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x2f, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, + 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, + 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, + 0x74, 0x79, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x37, 0x32, 0x38, + 0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x32, + 0x30, 0x30, 0x37, 0x30, 0x34, 0x31, 0x33, 0x32, 0x34, 0x32, 0x39, 0x5a, + 0x30, 0x2b, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x20, 0x47, 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, + 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x4b, 0x65, 0x79, 0x30, 0x82, 0x02, + 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00, 0x30, 0x82, 0x02, + 0x0a, 0x02, 0x82, 0x02, 0x01, 0x00, 0xb0, 0x2f, 0x50, 0x01, 0x9c, 0x0e, + 0xd6, 0x8c, 0x07, 0xca, 0xc1, 0xcf, 0xbc, 0x03, 0xdd, 0xd3, 0xfa, 0xe3, + 0x4f, 0x71, 0xc1, 0x30, 0xaa, 0x09, 0x96, 0xe4, 0xd0, 0x6c, 0x42, 0x93, + 0xdb, 0x35, 0xf6, 0x7e, 0x1b, 0x67, 0xc0, 0xc2, 0x2d, 0x5b, 0xec, 0xca, + 0x35, 0x06, 0x32, 0x6c, 0x7b, 0x2c, 0xd3, 0x71, 0x2b, 0xe9, 0x7a, 0x19, + 0xd1, 0xf2, 0xa0, 0x7f, 0xd7, 0x4d, 0x6e, 0x28, 0xbb, 0xae, 0x49, 0x4a, + 0xbc, 0xea, 0x47, 0x67, 0xb8, 0x36, 0xa6, 0xf5, 0x0d, 0x0e, 0x20, 0x14, + 0x0c, 0x66, 0x67, 0x28, 0xb5, 0x97, 0x8b, 0x1f, 0x5e, 0x32, 0x06, 0x29, + 0x9c, 0x99, 0x92, 0x0f, 0x73, 0xac, 0xfd, 0xd2, 0x1d, 0xf2, 0xa8, 0x55, + 0x9d, 0x1b, 0xd8, 0x3d, 0xb0, 0x76, 0x9a, 0xb6, 0x6c, 0x9f, 0x62, 0x37, + 0x2f, 0xc0, 0xef, 0x44, 0xb3, 0x0d, 0x4a, 0x3e, 0x4f, 0x7d, 0xbd, 0xdb, + 0xd8, 0x75, 0x5f, 0x68, 0xe3, 0xf0, 0xec, 0x82, 0x66, 0x7c, 0x31, 0x70, + 0xa9, 0xa1, 0x6f, 0x38, 0x9f, 0xdf, 0xf5, 0xf0, 0x7d, 0x23, 0x9d, 0x34, + 0xa5, 0x85, 0xd3, 0xdf, 0x68, 0x41, 0xfc, 0x4f, 0x89, 0x45, 0x3c, 0x24, + 0x81, 0xa6, 0xf2, 0x3c, 0x02, 0x26, 0x09, 0x48, 0xdd, 0xfe, 0x4b, 0xb6, + 0x66, 0xbf, 0x8f, 0xe5, 0x5f, 0xf0, 0x5d, 0x8a, 0x61, 0x2e, 0x5f, 0x9f, + 0x80, 0xd9, 0xd5, 0xe6, 0x41, 0xd8, 0x10, 0x5e, 0x7a, 0xc6, 0xdb, 0x89, + 0xc7, 0xca, 0x6c, 0x5b, 0xb1, 0x4e, 0x7d, 0x0c, 0x03, 0xfd, 0x50, 0xca, + 0xbf, 0xbb, 0xe2, 0x69, 0x4b, 0x4e, 0xc2, 0x3d, 0x75, 0xfa, 0xd1, 0xcc, + 0xd6, 0xf9, 0x39, 0xb9, 0xdc, 0x53, 0xad, 0x62, 0xfb, 0x1b, 0x94, 0x26, + 0x7f, 0x21, 0x54, 0x5c, 0xb7, 0xdc, 0xe7, 0x96, 0x8c, 0xce, 0x75, 0xe0, + 0x17, 0x01, 0x3a, 0x3c, 0x77, 0x6e, 0xa4, 0x8b, 0x7a, 0x83, 0x28, 0x7a, + 0xf7, 0xb0, 0x5f, 0xfc, 0x7f, 0x2d, 0x2e, 0xec, 0xf5, 0xeb, 0x9c, 0x63, + 0x74, 0xd0, 0xe5, 0xdc, 0x19, 0xe4, 0x71, 0xc5, 0x4a, 0x8a, 0x54, 0xa4, + 0xe0, 0x7d, 0x4e, 0xbf, 0x53, 0x30, 0xaf, 0xd0, 0xeb, 0x96, 0xc3, 0xbb, + 0x65, 0xf7, 0x67, 0xf5, 0xae, 0xd3, 0x96, 0xf2, 0x63, 0xc8, 0x69, 0xf7, + 0x47, 0xcb, 0x27, 0x79, 0xe1, 0xff, 0x2f, 0x68, 0xdf, 0x1e, 0xb3, 0xb8, + 0x0c, 0xc5, 0x58, 0x73, 0xcc, 0xfe, 0x8c, 0xda, 0x4e, 0x3b, 0x01, 0x04, + 0xcd, 0xcb, 0xb8, 0x3e, 0x06, 0xfd, 0x4c, 0x0a, 0x9f, 0x5e, 0x76, 0x8c, + 0x0c, 0x83, 0x75, 0x09, 0x08, 0xb2, 0xdb, 0xf4, 0x49, 0x4e, 0xa0, 0xf2, + 0x0c, 0x7b, 0x87, 0x38, 0x9e, 0x22, 0x67, 0xbd, 0xd1, 0x97, 0x57, 0x24, + 0xf1, 0x46, 0x07, 0xf9, 0xd2, 0x1b, 0xec, 0x25, 0x5e, 0x67, 0xd9, 0x66, + 0x23, 0x1b, 0xd3, 0xe4, 0xaa, 0xec, 0x88, 0xf0, 0x7e, 0x15, 0x83, 0x51, + 0x31, 0x67, 0x51, 0x76, 0x5f, 0x55, 0xd7, 0x36, 0xdf, 0x4a, 0x84, 0x0b, + 0x6f, 0x5c, 0xbb, 0x5b, 0x8f, 0x37, 0x23, 0x7f, 0xf8, 0x17, 0x84, 0xa2, + 0x70, 0x20, 0x07, 0x0c, 0x90, 0x3a, 0x04, 0xfd, 0xf0, 0x08, 0x4a, 0xb1, + 0x16, 0x0f, 0xe6, 0xf6, 0x40, 0x51, 0x83, 0xd2, 0x87, 0x40, 0x9c, 0x1c, + 0x9f, 0x13, 0x38, 0x17, 0xd3, 0x34, 0x58, 0xad, 0x05, 0x71, 0xa0, 0x73, + 0xca, 0x40, 0xa6, 0xa4, 0x81, 0x02, 0xee, 0xa8, 0x72, 0x41, 0xa1, 0x41, + 0x18, 0x64, 0x8a, 0x86, 0x8a, 0x5d, 0xe6, 0x4f, 0x0a, 0xc5, 0x95, 0x98, + 0xf9, 0x78, 0xfe, 0x19, 0x0d, 0xc9, 0xb3, 0x89, 0xc1, 0x2b, 0x09, 0xbe, + 0xf1, 0xd2, 0x04, 0x5d, 0xcc, 0x28, 0xf5, 0x4b, 0xd2, 0x20, 0x4f, 0xc5, + 0x41, 0x9d, 0x8c, 0x85, 0xd8, 0xb0, 0x68, 0x5e, 0xc1, 0x0c, 0xb7, 0x24, + 0x4d, 0x67, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, + 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, + 0x07, 0x80, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, + 0x14, 0xac, 0xf5, 0x47, 0x17, 0xd9, 0x7d, 0xc1, 0xb1, 0xc4, 0x41, 0xe1, + 0x41, 0x60, 0xcb, 0x37, 0x11, 0x60, 0x28, 0x78, 0x5f, 0x30, 0x1f, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x21, 0x94, + 0xfb, 0xf9, 0xb2, 0x43, 0xe9, 0x33, 0xd7, 0x50, 0x7d, 0xc7, 0x37, 0xdb, + 0xd5, 0x82, 0x5a, 0x4e, 0xbe, 0x1b, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, + 0x01, 0x00, 0x96, 0x70, 0x65, 0x26, 0x42, 0xf8, 0xdc, 0x69, 0xde, 0xcf, + 0x41, 0x3a, 0x2e, 0x7f, 0x5b, 0xf1, 0xf9, 0x3b, 0x9b, 0xd2, 0x4e, 0x64, + 0x48, 0x81, 0xe4, 0x5d, 0x1e, 0x22, 0xce, 0x68, 0x63, 0x62, 0xe5, 0x1b, + 0x9b, 0xf2, 0xc7, 0x12, 0xda, 0x1e, 0x9b, 0x90, 0x84, 0x79, 0x48, 0x12, + 0xe6, 0x21, 0x6f, 0x2f, 0x7e, 0x18, 0x77, 0xdb, 0x8c, 0xc4, 0xd1, 0x0d, + 0x91, 0xbf, 0x39, 0x22, 0x0f, 0x64, 0xcf, 0x25, 0x2e, 0x8c, 0x1f, 0x91, + 0x81, 0xb5, 0xe9, 0x6c, 0x02, 0x3a, 0xf8, 0x07, 0xa2, 0x6f, 0x46, 0x5d, + 0x7b, 0xfd, 0x43, 0xff, 0x41, 0x0f, 0xe2, 0x57, 0x1c, 0xbd, 0x48, 0x60, + 0x53, 0x11, 0x48, 0x87, 0x88, 0x9d, 0x13, 0x82, 0x40, 0x68, 0x44, 0x2c, + 0xc6, 0xc8, 0x95, 0x27, 0x4f, 0xb6, 0xb9, 0x4a, 0x22, 0x0a, 0xfd, 0xe4, + 0x46, 0x8f, 0x35, 0x12, 0x98, 0x5a, 0x34, 0x6f, 0x2b, 0x57, 0x62, 0xa1, + 0x4d, 0x8d, 0x79, 0x37, 0xe4, 0x6b, 0x8a, 0x32, 0x5b, 0xcb, 0xef, 0x79, + 0x11, 0xed, 0xa7, 0xf8, 0x7a, 0x1c, 0xbd, 0x86, 0xdc, 0x0e, 0x2e, 0xfd, + 0xd3, 0x51, 0xbb, 0x73, 0xad, 0x00, 0xa0, 0x1b, 0xf9, 0x1d, 0xd1, 0x4a, + 0xe4, 0xd4, 0x02, 0x63, 0x2b, 0x39, 0x5f, 0x18, 0x08, 0x2f, 0x42, 0xb7, + 0x23, 0x4b, 0x48, 0x46, 0x1f, 0x63, 0x87, 0xae, 0x6d, 0xd5, 0xdb, 0x60, + 0xf8, 0x5f, 0xd3, 0x13, 0xec, 0xca, 0xdd, 0x60, 0x60, 0x79, 0x52, 0x70, + 0x47, 0xae, 0x1d, 0x38, 0x78, 0x71, 0xcf, 0xb3, 0x04, 0x03, 0xbe, 0xba, + 0x81, 0xba, 0x74, 0xb1, 0x30, 0x35, 0xdc, 0xea, 0x21, 0x4a, 0x9b, 0x70, + 0xfb, 0xd6, 0x60, 0x59, 0x78, 0x0c, 0x4d, 0x39, 0x19, 0x1d, 0xe5, 0x75, + 0xba, 0x07, 0xf4, 0x22, 0x37, 0x64, 0xb7, 0xf2, 0x9a, 0xc9, 0x11, 0x2d, + 0x8e, 0x58, 0xa6, 0xcf, 0x83, 0xf1, 0xcb, 0x6c, 0x7f, 0x02, 0xbd, 0xda, + 0x03, 0x92, 0xa9, 0x45, 0x24, 0x56, 0xc5, 0xbd, 0x41, 0xd1, 0x20, 0x86, + 0xc0, 0xb6, 0xb7, 0xe8, 0xa7, 0xb2, 0x46, 0xf7, 0x8e, 0xa9, 0x38, 0x0e, + 0x23, 0x77, 0x3c, 0x0d, 0x66, 0x83, 0x6a, 0x1a, 0x6b, 0x7f, 0x54, 0x11, + 0x58, 0x0d, 0x4a, 0xb5, 0x74, 0x60, 0xca, 0xed, 0xff, 0x91, 0x47, 0xd9, + 0x29, 0xe0, 0xaa, 0x8c, 0xa8, 0x8f, 0x10, 0x4c, 0x15, 0x7d, 0xce, 0x95, + 0xf9, 0x87, 0x1e, 0x18, 0x38, 0x18, 0xfc, 0xcc, 0xaf, 0x91, 0x17, 0x3f, + 0xfa, 0xf0, 0x8a, 0x09, 0x6f, 0xba, 0x4e, 0x53, 0xf7, 0xfa, 0x4f, 0x20, + 0xa3, 0xf4, 0x4a, 0x5a, 0xde, 0x17, 0x1c, 0x29, 0x6a, 0x6f, 0x03, 0x48, + 0xdf, 0xad, 0x4f, 0xe4, 0xbc, 0x71, 0xc4, 0x72, 0x32, 0x11, 0x84, 0xac, + 0x09, 0xd2, 0x18, 0x44, 0x35, 0xf1, 0xcd, 0xaf, 0xa8, 0x98, 0xe0, 0x8b, + 0xec, 0xa0, 0x83, 0x37, 0xc3, 0x35, 0x85, 0xd6, 0xd8, 0x1b, 0xe0, 0x75, + 0xdc, 0xfd, 0xde, 0xc9, 0xeb, 0xd5, 0x18, 0x0f, 0xd3, 0x4c, 0x2f, 0x71, + 0xdc, 0x48, 0xe3, 0x14, 0xeb, 0xda, 0x00, 0x24, 0x24, 0x9e, 0xa3, 0x8e, + 0x3e, 0x08, 0x6f, 0x22, 0x24, 0xd6, 0xc4, 0x85, 0x8f, 0x68, 0x00, 0x4a, + 0x82, 0x4c, 0x33, 0x6e, 0xa5, 0x35, 0x7b, 0xeb, 0x4b, 0xdc, 0xa0, 0xa6, + 0x65, 0x6f, 0x5a, 0x7a, 0xdf, 0x8a, 0x01, 0x52, 0xa1, 0x6c, 0xff, 0x59, + 0x22, 0x7f, 0xe1, 0x96, 0x1b, 0x19, 0xb8, 0xf9, 0x5d, 0x44, 0x9f, 0x91, + 0x03, 0x3c, 0x3d, 0xa1, 0x2a, 0xb6, 0x5a, 0x51, 0xa0, 0xce, 0x4a, 0x88, + 0x22, 0x72, 0x9c, 0xdc, 0xc0, 0x47, 0x76, 0x35, 0x84, 0x75, 0x9b, 0x87, + 0x5c, 0xd3, 0xcf, 0xe7, 0xdd, 0xa3, 0x57, 0x14, 0xdf, 0x00, 0xfd, 0x19, + 0x2a, 0x7d, 0x89, 0x27, 0x1c, 0x78, 0x97, 0x04, 0x58, 0x48 +}; +unsigned int certificate2_der_len = 1366; + +unsigned char hi_signed_2nd[] = { + 0x68, 0x69, 0x0a, 0x30, 0x82, 0x02, 0xb1, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, 0xa0, 0x82, 0x02, 0xa2, 0x30, 0x82, + 0x02, 0x9e, 0x02, 0x01, 0x01, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0b, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x31, 0x82, 0x02, + 0x7b, 0x30, 0x82, 0x02, 0x77, 0x02, 0x01, 0x01, 0x30, 0x52, 0x30, 0x3a, + 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x2f, 0x47, + 0x72, 0x75, 0x62, 0x20, 0x32, 0x6e, 0x64, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, + 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x02, 0x14, + 0x5b, 0x5e, 0x59, 0xf2, 0x5f, 0x75, 0x4c, 0x8e, 0xc5, 0x3a, 0x91, 0x07, + 0xe9, 0xe7, 0x6d, 0x3c, 0xd0, 0x7f, 0x91, 0xff, 0x30, 0x0b, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x04, 0x82, 0x02, 0x00, 0x0e, 0xc2, 0x30, 0x38, 0x81, 0x23, 0x68, 0x90, + 0xae, 0x5f, 0xce, 0xf7, 0x27, 0xb1, 0x8c, 0x2e, 0x12, 0x10, 0xc6, 0x99, + 0xdc, 0x4d, 0x4b, 0x79, 0xda, 0xe4, 0x32, 0x10, 0x46, 0x1c, 0x16, 0x07, + 0x87, 0x66, 0x55, 0xff, 0x64, 0x1c, 0x61, 0x25, 0xd5, 0xb9, 0xe1, 0xfe, + 0xea, 0x5a, 0xcd, 0x56, 0xa5, 0xc3, 0xbe, 0xb1, 0x61, 0xc7, 0x6f, 0x5f, + 0x69, 0x20, 0x64, 0x50, 0x6f, 0x12, 0x78, 0xb6, 0x0c, 0x72, 0x44, 0x4f, + 0x60, 0x0f, 0x9f, 0xa2, 0x83, 0x3b, 0xc2, 0x83, 0xd5, 0x14, 0x1f, 0x6f, + 0x3e, 0xb2, 0x47, 0xb5, 0x58, 0xc5, 0xa7, 0xb4, 0x82, 0x53, 0x2e, 0x53, + 0x95, 0x4e, 0x3d, 0xe4, 0x62, 0xe8, 0xa1, 0xaf, 0xae, 0xbf, 0xa9, 0xd2, + 0x22, 0x07, 0xbe, 0x71, 0x37, 0x2c, 0x5a, 0xa7, 0x6c, 0xaf, 0x14, 0xc0, + 0x6c, 0x2f, 0xbf, 0x4f, 0x15, 0xc2, 0x0f, 0x8b, 0xdc, 0x68, 0x45, 0xdf, + 0xf3, 0xa5, 0x7f, 0x11, 0x6a, 0x54, 0xcd, 0x67, 0xb9, 0x2e, 0x7d, 0x05, + 0xe3, 0x1c, 0x1d, 0xcc, 0x77, 0x8e, 0x97, 0xb1, 0xa0, 0x11, 0x09, 0x3d, + 0x90, 0x54, 0xfc, 0x7e, 0xbb, 0xbb, 0x21, 0x23, 0x03, 0x44, 0xbf, 0x7d, + 0x2c, 0xc9, 0x15, 0x42, 0xe5, 0xa0, 0x3b, 0xa2, 0xd1, 0x5b, 0x73, 0x81, + 0xff, 0xfa, 0x90, 0xfc, 0x27, 0x7b, 0x2f, 0x86, 0x9c, 0x1d, 0x14, 0x36, + 0x94, 0xa2, 0x6e, 0xe8, 0x9d, 0xa0, 0x5f, 0xfc, 0x5a, 0x0d, 0xa4, 0xd5, + 0x2f, 0x8d, 0xd6, 0x00, 0xfa, 0x93, 0x5b, 0x09, 0x7f, 0x42, 0x78, 0xcc, + 0x8c, 0x49, 0xda, 0xd9, 0xf6, 0x43, 0xe7, 0xe1, 0x3c, 0xa2, 0xe2, 0x70, + 0xe2, 0x6a, 0x99, 0xc5, 0xd6, 0xa2, 0xe3, 0x0b, 0xd4, 0x09, 0xac, 0x94, + 0xaf, 0xb7, 0xf0, 0xb3, 0x0c, 0x1e, 0xf5, 0x16, 0x4f, 0x53, 0x9a, 0xe3, + 0xcc, 0xe2, 0x0c, 0x4a, 0xb9, 0xe6, 0x06, 0xbb, 0xf7, 0x41, 0x43, 0x20, + 0x04, 0xee, 0x99, 0x2f, 0xd8, 0x9f, 0xda, 0x3f, 0xfd, 0x49, 0xb8, 0xc2, + 0xbd, 0xd9, 0xc5, 0x72, 0xfd, 0xe3, 0xce, 0x1c, 0xbc, 0xe4, 0x39, 0xac, + 0x2a, 0x99, 0xe9, 0xb4, 0x3e, 0x74, 0x10, 0xeb, 0xd5, 0x14, 0xcc, 0xdb, + 0xf1, 0x04, 0x63, 0x36, 0xfb, 0x1f, 0x2b, 0xe2, 0x73, 0xd4, 0xd8, 0x49, + 0x31, 0xa8, 0x55, 0xcc, 0xa7, 0x76, 0x36, 0x6e, 0x18, 0xdc, 0xb9, 0xb0, + 0x29, 0x99, 0xcf, 0x49, 0xbf, 0xf9, 0xdb, 0x7f, 0x24, 0x42, 0x02, 0xcb, + 0xc1, 0xaa, 0xcb, 0xba, 0x18, 0x85, 0x86, 0xc7, 0xf4, 0x1c, 0x62, 0x76, + 0xbc, 0x73, 0xfb, 0xe4, 0x15, 0xb8, 0xdd, 0x5d, 0xa6, 0x68, 0x39, 0xa5, + 0x3d, 0x33, 0xaf, 0xd5, 0x92, 0x4d, 0x48, 0xdb, 0x22, 0xc0, 0xdc, 0x49, + 0x5f, 0x7b, 0xa8, 0xd2, 0x62, 0x2d, 0xa7, 0x39, 0x93, 0x48, 0xe7, 0x6b, + 0x23, 0xba, 0xd4, 0xe0, 0xc1, 0x29, 0x55, 0xc4, 0x34, 0xe3, 0xac, 0x25, + 0xa7, 0x15, 0xad, 0xab, 0xb3, 0xb7, 0x25, 0xca, 0x37, 0x88, 0x40, 0x2e, + 0x47, 0x6e, 0x92, 0x20, 0x09, 0x2e, 0x5a, 0xec, 0xf2, 0xfb, 0xb3, 0xa0, + 0x16, 0xb6, 0x93, 0xf2, 0xf5, 0x8b, 0xfe, 0xaf, 0x25, 0xee, 0x2e, 0x98, + 0x6c, 0x0a, 0xfe, 0xae, 0x0b, 0x57, 0xf5, 0x9f, 0x3c, 0x80, 0xe9, 0x8b, + 0xaf, 0x92, 0x8a, 0xad, 0xe7, 0xa0, 0xe4, 0xe6, 0x0a, 0xa0, 0xc7, 0x83, + 0xb5, 0x48, 0x58, 0x5f, 0x55, 0x9e, 0x9b, 0x27, 0xcd, 0x31, 0x1f, 0x3e, + 0x50, 0x5a, 0x91, 0xad, 0x21, 0x1b, 0x97, 0x5b, 0xe8, 0xfa, 0x29, 0x8a, + 0xa4, 0x17, 0xe8, 0xab, 0x87, 0x02, 0xd6, 0x18, 0x8c, 0x9f, 0x65, 0xb7, + 0x2a, 0xfa, 0xde, 0x5f, 0x77, 0x30, 0x6c, 0x04, 0x22, 0xe6, 0x58, 0x26, + 0x14, 0x0d, 0x9c, 0x41, 0x0a, 0x82, 0x77, 0xdb, 0x40, 0xa1, 0x58, 0xac, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xb5, + 0x7e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x20, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x61, 0x70, 0x70, 0x65, 0x6e, 0x64, + 0x65, 0x64, 0x7e, 0x0a +}; +unsigned int hi_signed_2nd_len = 736; + +unsigned char certificate_printable_der[] = { + 0x30, 0x82, 0x03, 0x39, 0x30, 0x82, 0x02, 0x21, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0xde, 0xf6, 0x22, 0xc4, 0xf2, 0xf1, 0x86, 0x02, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x30, 0x2a, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, + 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x20, 0x32, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30, + 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x31, 0x30, 0x33, 0x31, 0x31, 0x34, 0x31, + 0x39, 0x32, 0x33, 0x5a, 0x17, 0x0d, 0x33, 0x37, 0x31, 0x30, 0x32, 0x35, + 0x31, 0x34, 0x31, 0x39, 0x32, 0x33, 0x5a, 0x30, 0x2f, 0x31, 0x2d, 0x30, + 0x2b, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x24, 0x52, 0x65, 0x64, 0x20, + 0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, + 0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x20, + 0x33, 0x20, 0x28, 0x62, 0x65, 0x74, 0x61, 0x29, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xbd, 0xda, 0xa1, 0xed, 0x8d, 0x8e, 0x15, + 0x5c, 0xf8, 0x01, 0x77, 0x48, 0x4a, 0x60, 0x96, 0xf9, 0x27, 0xfa, 0xe2, + 0xb1, 0x69, 0x0f, 0x51, 0x19, 0x52, 0x7e, 0xc4, 0x34, 0x8e, 0xe1, 0x9b, + 0x9c, 0xa4, 0xb1, 0x5c, 0xd6, 0x81, 0x98, 0x78, 0xfe, 0xa9, 0xe5, 0x0b, + 0x00, 0xba, 0x9c, 0x64, 0x7e, 0xc7, 0xcc, 0x72, 0xb1, 0x73, 0x4b, 0x11, + 0x07, 0x52, 0xf0, 0x20, 0x96, 0x8b, 0x99, 0x39, 0xde, 0xdb, 0xfa, 0x3d, + 0x45, 0xe2, 0x98, 0x7b, 0x0c, 0x41, 0xe4, 0x0c, 0xb5, 0x5d, 0x92, 0x74, + 0x39, 0x96, 0xe1, 0x97, 0x97, 0xa1, 0xad, 0x2e, 0xcc, 0xd0, 0x1b, 0x4d, + 0x9d, 0xbd, 0x3e, 0xa9, 0x36, 0x8e, 0xcc, 0xc7, 0x5f, 0x6a, 0x7d, 0x39, + 0x5e, 0x0b, 0x8d, 0xca, 0xe4, 0x83, 0xe9, 0x3b, 0x5c, 0x86, 0x47, 0xd4, + 0xba, 0x7d, 0x98, 0x26, 0xa1, 0xf4, 0xe8, 0x90, 0x6b, 0x0f, 0xf1, 0x6b, + 0x8c, 0xe3, 0xa2, 0x80, 0x3c, 0x96, 0xf1, 0x0a, 0xb6, 0x66, 0xc0, 0x4b, + 0x61, 0xf7, 0x74, 0xcd, 0xd3, 0x7b, 0x8e, 0x5e, 0x39, 0xda, 0x99, 0x20, + 0x33, 0x93, 0xd3, 0xf0, 0x7f, 0xad, 0x35, 0xe9, 0x88, 0x8d, 0x9c, 0xbf, + 0x65, 0xf1, 0x47, 0x02, 0xf9, 0x7c, 0xed, 0x27, 0x5f, 0x4a, 0x65, 0x3c, + 0xcf, 0x5f, 0x0e, 0x88, 0x95, 0x74, 0xde, 0xfb, 0x9e, 0x2e, 0x91, 0x9b, + 0x45, 0x37, 0xc8, 0x85, 0xff, 0xe3, 0x41, 0x70, 0xfe, 0xd5, 0xef, 0x0e, + 0x82, 0x22, 0x08, 0xb7, 0x3b, 0x44, 0x3e, 0xdc, 0x5b, 0x7f, 0xba, 0xbf, + 0xe6, 0x58, 0x9d, 0x02, 0x6e, 0x75, 0xbf, 0x50, 0xec, 0xcf, 0x3f, 0xa5, + 0x91, 0x0a, 0xe2, 0x59, 0x2c, 0xc3, 0xe7, 0x05, 0x03, 0xe8, 0xf2, 0x6f, + 0x2a, 0x04, 0x68, 0x9a, 0x31, 0x32, 0x8f, 0x04, 0x35, 0xcd, 0x1f, 0x34, + 0xcc, 0x4f, 0x79, 0x5a, 0x99, 0x8d, 0x9d, 0x5c, 0xf5, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x5d, 0x30, 0x5b, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x1d, 0x06, + 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x65, 0xc5, 0xbe, 0xca, + 0xe6, 0x59, 0x6a, 0xfd, 0x6c, 0x71, 0xc4, 0xa7, 0x98, 0xc6, 0x25, 0x8d, + 0x7b, 0x67, 0x05, 0xd0, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, + 0x18, 0x30, 0x16, 0x80, 0x14, 0x81, 0xf8, 0xee, 0x47, 0x5c, 0x3e, 0xed, + 0xfb, 0xce, 0xa5, 0x84, 0xbe, 0xd7, 0xae, 0xdb, 0xd3, 0x7d, 0x64, 0xb3, + 0x2a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x66, 0x1e, 0x3d, + 0x1d, 0x53, 0x33, 0xde, 0x4e, 0xc7, 0xc4, 0xf4, 0xdf, 0xda, 0x18, 0x19, + 0x8a, 0xa9, 0xff, 0xe2, 0x63, 0x2b, 0xbe, 0xf2, 0x61, 0x63, 0xe2, 0xf6, + 0xed, 0x47, 0x1a, 0x71, 0x02, 0xec, 0x2a, 0xef, 0x89, 0x77, 0xe3, 0xfd, + 0x86, 0x69, 0xf1, 0x3f, 0x0d, 0xf9, 0x6e, 0xf9, 0x3b, 0xad, 0x26, 0x47, + 0xb7, 0xf2, 0x0d, 0xad, 0x23, 0xa3, 0x67, 0x3b, 0xcb, 0x6d, 0x9e, 0x03, + 0x0f, 0xbc, 0x69, 0x73, 0x9f, 0xd4, 0xa5, 0x0f, 0x6f, 0xf8, 0xab, 0x4d, + 0x36, 0xd1, 0xe0, 0xe0, 0x5d, 0x20, 0x43, 0x90, 0xc4, 0x65, 0x61, 0x93, + 0xe2, 0x0f, 0x51, 0x59, 0x0a, 0xf7, 0x88, 0x70, 0x57, 0xb9, 0x04, 0xa9, + 0x32, 0x57, 0x9c, 0xb3, 0x57, 0x38, 0x8b, 0x8e, 0x46, 0xc8, 0x32, 0x6c, + 0xb4, 0xf3, 0x96, 0x7f, 0x4b, 0xf0, 0x88, 0xf9, 0x7f, 0xe2, 0x71, 0xe1, + 0x8b, 0xe2, 0x14, 0xf1, 0x4b, 0x25, 0x00, 0x48, 0x1c, 0x7e, 0xe5, 0x8d, + 0x65, 0x2d, 0xeb, 0x72, 0x4f, 0x92, 0x44, 0xf3, 0xe6, 0xe0, 0xd0, 0xdf, + 0x85, 0xa8, 0x13, 0x4a, 0xfb, 0x99, 0xca, 0x14, 0x2c, 0x97, 0x80, 0x93, + 0x27, 0xd3, 0x20, 0xf8, 0x6d, 0x29, 0x28, 0x2c, 0xb9, 0x77, 0xea, 0xb1, + 0x63, 0xbd, 0x7d, 0x53, 0xfd, 0x4a, 0x62, 0x64, 0x0b, 0x98, 0xa8, 0xae, + 0x11, 0xfc, 0x6e, 0x8d, 0x63, 0xd4, 0x15, 0x55, 0xc6, 0x4c, 0x74, 0xf5, + 0x5f, 0xa0, 0xb9, 0x2c, 0x2d, 0x9a, 0x7a, 0x87, 0x6e, 0xf0, 0x5e, 0x25, + 0xed, 0xfc, 0xd8, 0xc4, 0x34, 0x33, 0x32, 0xad, 0x01, 0xd4, 0x4b, 0x49, + 0x51, 0xc2, 0x07, 0x7f, 0x90, 0x6d, 0xea, 0xf5, 0x4c, 0x41, 0x71, 0x64, + 0xeb, 0x1f, 0x29, 0xa3, 0x1f, 0x64, 0xa2, 0x1e, 0x0e, 0x6f, 0xa1, 0x67, + 0x99, 0x8d, 0x98, 0x1c, 0xb8, 0x53, 0x9d, 0x30, 0x1d, 0xae, 0x32, 0x56, + 0xd2 +}; +unsigned int certificate_printable_der_len = 829; + +unsigned char certificate_eku_der[] = { + 0x30, 0x82, 0x03, 0x90, 0x30, 0x82, 0x02, 0x78, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0xd3, 0x9c, 0x41, 0x33, 0xdd, 0x6b, 0x5f, 0x45, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x30, 0x47, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x18, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, + 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, + 0x43, 0x41, 0x20, 0x36, 0x31, 0x22, 0x30, 0x20, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x13, 0x73, 0x65, 0x63, + 0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65, 0x64, 0x68, 0x61, 0x74, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x32, + 0x31, 0x35, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a, 0x17, 0x0d, 0x33, + 0x38, 0x30, 0x31, 0x31, 0x37, 0x31, 0x34, 0x30, 0x30, 0x34, 0x34, 0x5a, + 0x30, 0x4e, 0x31, 0x28, 0x30, 0x26, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, + 0x1f, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, 0x53, 0x65, 0x63, + 0x75, 0x72, 0x65, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x20, 0x53, 0x69, 0x67, + 0x6e, 0x69, 0x6e, 0x67, 0x20, 0x36, 0x30, 0x32, 0x31, 0x22, 0x30, 0x20, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, + 0x13, 0x73, 0x65, 0x63, 0x61, 0x6c, 0x65, 0x72, 0x74, 0x40, 0x72, 0x65, + 0x64, 0x68, 0x61, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xaa, 0x6f, 0xbb, 0x92, 0x77, 0xd7, 0x15, + 0xef, 0x88, 0x80, 0x88, 0xc0, 0xe7, 0x89, 0xeb, 0x35, 0x76, 0xf4, 0x85, + 0x05, 0x0f, 0x19, 0xe4, 0x5f, 0x25, 0xdd, 0xc1, 0xa2, 0xe5, 0x5c, 0x06, + 0xfb, 0xf1, 0x06, 0xb5, 0x65, 0x45, 0xcb, 0xbd, 0x19, 0x33, 0x54, 0xb5, + 0x1a, 0xcd, 0xe4, 0xa8, 0x35, 0x2a, 0xfe, 0x9c, 0x53, 0xf4, 0xc6, 0x76, + 0xdb, 0x1f, 0x8a, 0xd4, 0x7b, 0x18, 0x11, 0xaf, 0xa3, 0x90, 0xd4, 0xdd, + 0x4d, 0xd5, 0x42, 0xcc, 0x14, 0x9a, 0x64, 0x6b, 0xc0, 0x7f, 0xaa, 0x1c, + 0x94, 0x47, 0x4d, 0x79, 0xbd, 0x57, 0x9a, 0xbf, 0x99, 0x4e, 0x96, 0xa9, + 0x31, 0x2c, 0xa9, 0xe7, 0x14, 0x65, 0x86, 0xc8, 0xac, 0x79, 0x5e, 0x78, + 0xa4, 0x3c, 0x00, 0x24, 0xd3, 0xf7, 0xe1, 0xf5, 0x12, 0xad, 0xa0, 0x29, + 0xe5, 0xfe, 0x80, 0xae, 0xf8, 0xaa, 0x60, 0x36, 0xe7, 0xe8, 0x94, 0xcb, + 0xe9, 0xd1, 0xcc, 0x0b, 0x4d, 0xf7, 0xde, 0xeb, 0x52, 0xd2, 0x73, 0x09, + 0x28, 0xdf, 0x48, 0x99, 0x53, 0x9f, 0xc5, 0x9a, 0xd4, 0x36, 0xa3, 0xc6, + 0x5e, 0x8d, 0xbe, 0xd5, 0xdc, 0x76, 0xb4, 0x74, 0xb8, 0x26, 0x18, 0x27, + 0xfb, 0xf2, 0xfb, 0xd0, 0x9b, 0x3d, 0x7f, 0x10, 0xe2, 0xab, 0x44, 0xc7, + 0x88, 0x7f, 0xb4, 0x3d, 0x3e, 0xa3, 0xff, 0x6d, 0x06, 0x4b, 0x3e, 0x55, + 0xb2, 0x84, 0xf4, 0xad, 0x54, 0x88, 0x81, 0xc3, 0x9c, 0xf8, 0xb6, 0x68, + 0x96, 0x38, 0x8b, 0xcd, 0x90, 0x6d, 0x25, 0x4b, 0xbf, 0x0c, 0x44, 0x90, + 0xa5, 0x5b, 0x98, 0xd0, 0x40, 0x2f, 0xbb, 0x0d, 0xa8, 0x4b, 0x8a, 0x62, + 0x82, 0x46, 0x46, 0x18, 0x38, 0xae, 0x82, 0x07, 0xd0, 0xb4, 0x2f, 0x16, + 0x79, 0x55, 0x9f, 0x1b, 0xc5, 0x08, 0x6d, 0x85, 0xdf, 0x3f, 0xa9, 0x9b, + 0x4b, 0xc6, 0x28, 0xd3, 0x58, 0x72, 0x3d, 0x37, 0x11, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x78, 0x30, 0x76, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, + 0x30, 0x16, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x01, 0x01, 0xff, 0x04, 0x0c, + 0x30, 0x0a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03, + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x6c, + 0xe4, 0x6c, 0x27, 0xaa, 0xcd, 0x0d, 0x4b, 0x74, 0x21, 0xa4, 0xf6, 0x5f, + 0x87, 0xb5, 0x31, 0xfe, 0x10, 0xbb, 0xa7, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xe8, 0x6a, 0x1c, 0xab, + 0x2c, 0x48, 0xf9, 0x60, 0x36, 0xa2, 0xf0, 0x7b, 0x8e, 0xd2, 0x9d, 0xb4, + 0x2a, 0x28, 0x98, 0xc8, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, + 0x55, 0x34, 0xe2, 0xfa, 0xf6, 0x89, 0x86, 0xad, 0x92, 0x21, 0xec, 0xb9, + 0x54, 0x0e, 0x18, 0x47, 0x0d, 0x1b, 0xa7, 0x58, 0xad, 0x69, 0xe4, 0xef, + 0x3b, 0xe6, 0x8d, 0xdd, 0xda, 0x0c, 0x45, 0xf6, 0xe8, 0x96, 0xa4, 0x29, + 0x0f, 0xbb, 0xcf, 0x16, 0xae, 0x93, 0xd0, 0xcb, 0x2a, 0x26, 0x1a, 0x7b, + 0xfc, 0x51, 0x22, 0x76, 0x98, 0x31, 0xa7, 0x0f, 0x29, 0x35, 0x79, 0xbf, + 0xe2, 0x4f, 0x0f, 0x14, 0xf5, 0x1f, 0xcb, 0xbf, 0x87, 0x65, 0x13, 0x32, + 0xa3, 0x19, 0x4a, 0xd1, 0x3f, 0x45, 0xd4, 0x4b, 0xe2, 0x00, 0x26, 0xa9, + 0x3e, 0xd7, 0xa5, 0x37, 0x9f, 0xf5, 0xad, 0x61, 0xe2, 0x40, 0xa9, 0x74, + 0x24, 0x53, 0xf2, 0x78, 0xeb, 0x10, 0x9b, 0x2c, 0x27, 0x88, 0x46, 0xcb, + 0xe4, 0x60, 0xca, 0xf5, 0x06, 0x24, 0x40, 0x2a, 0x97, 0x3a, 0xcc, 0xd0, + 0x81, 0xb1, 0x15, 0xa3, 0x4f, 0xd0, 0x2b, 0x4f, 0xca, 0x6e, 0xaa, 0x24, + 0x31, 0xb3, 0xac, 0xa6, 0x75, 0x05, 0xfe, 0x8a, 0xf4, 0x41, 0xc4, 0x06, + 0x8a, 0xc7, 0x0a, 0x83, 0x4e, 0x49, 0xd4, 0x3f, 0x83, 0x50, 0xec, 0x57, + 0x04, 0x97, 0x14, 0x49, 0xf5, 0xe1, 0xb1, 0x7a, 0x9c, 0x09, 0x4f, 0x61, + 0x87, 0xc3, 0x97, 0x22, 0x17, 0xc2, 0xeb, 0xcc, 0x32, 0x81, 0x31, 0x21, + 0x3f, 0x10, 0x57, 0x5b, 0x43, 0xbe, 0xcd, 0x68, 0x82, 0xbe, 0xe5, 0xc1, + 0x65, 0x94, 0x7e, 0xc2, 0x34, 0x76, 0x2b, 0xcf, 0x89, 0x3c, 0x2b, 0x81, + 0x23, 0x72, 0x95, 0xcf, 0xc9, 0x67, 0x19, 0x2a, 0xd5, 0x5c, 0xca, 0xa3, + 0x46, 0xbd, 0x48, 0x06, 0x0b, 0xa6, 0xa3, 0x96, 0x50, 0x28, 0xc7, 0x7e, + 0xcf, 0x62, 0xf2, 0xfa, 0xc4, 0xf2, 0x53, 0xe3, 0xc9, 0xe8, 0x2e, 0xdd, + 0x29, 0x37, 0x07, 0x47, 0xff, 0xff, 0x8a, 0x32, 0xbd, 0xa2, 0xb7, 0x21, + 0x89, 0xa0, 0x55, 0xf7 +}; +unsigned int certificate_eku_der_len = 916; diff --git a/grub-core/tests/lib/functional_test.c b/grub-core/tests/lib/functional_test.c index 96781fb39b..31b6b5dab3 100644 --- a/grub-core/tests/lib/functional_test.c +++ b/grub-core/tests/lib/functional_test.c @@ -73,6 +73,7 @@ grub_functional_all_tests (grub_extcmd_context_t ctxt __attribute__ ((unused)), grub_dl_load ("xnu_uuid_test"); grub_dl_load ("pbkdf2_test"); grub_dl_load ("signature_test"); + grub_dl_load ("appended_signature_test"); grub_dl_load ("sleep_test"); grub_dl_load ("bswap_test"); grub_dl_load ("ctz_test"); @@ -89,17 +90,18 @@ grub_functional_all_tests (grub_extcmd_context_t ctxt __attribute__ ((unused)), return GRUB_ERR_NONE; } -static grub_extcmd_t cmd; +static grub_extcmd_t cmd, cmd_all; GRUB_MOD_INIT (functional_test) { cmd = grub_register_extcmd ("functional_test", grub_functional_test, 0, 0, "Run all loaded functional tests.", 0); - cmd = grub_register_extcmd ("all_functional_test", grub_functional_all_tests, 0, 0, - "Run all functional tests.", 0); + cmd_all = grub_register_extcmd ("all_functional_test", grub_functional_all_tests, 0, 0, + "Run all functional tests.", 0); } GRUB_MOD_FINI (functional_test) { grub_unregister_extcmd (cmd); + grub_unregister_extcmd (cmd_all); } diff --git a/grub-core/video/ieee1275.c b/grub-core/video/ieee1275.c index ca3d3c3b2c..5592e4bb7c 100644 --- a/grub-core/video/ieee1275.c +++ b/grub-core/video/ieee1275.c @@ -351,9 +351,12 @@ static struct grub_video_adapter grub_video_ieee1275_adapter = GRUB_MOD_INIT(ieee1275_fb) { - find_display (); - if (display) - grub_video_register (&grub_video_ieee1275_adapter); + if (! grub_ieee1275_test_flag (GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT)) + { + find_display (); + if (display) + grub_video_register (&grub_video_ieee1275_adapter); + } } GRUB_MOD_FINI(ieee1275_fb) diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c index ae634fd419..631a893564 100644 --- a/grub-core/video/readers/jpeg.c +++ b/grub-core/video/readers/jpeg.c @@ -339,6 +339,10 @@ grub_jpeg_decode_sof (struct grub_jpeg_data *data) if (grub_errno != GRUB_ERR_NONE) return grub_errno; + if (data->image_height != 0 || data->image_width != 0) + return grub_error (GRUB_ERR_BAD_FILE_TYPE, + "jpeg: cannot have duplicate SOF0 markers"); + if (grub_jpeg_get_byte (data) != 8) return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: only 8-bit precision is supported"); diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c index 3163e97bfe..aa7524b7dc 100644 --- a/grub-core/video/readers/png.c +++ b/grub-core/video/readers/png.c @@ -626,7 +626,7 @@ static grub_err_t grub_png_output_byte (struct grub_png_data *data, grub_uint8_t n) { if (--data->raw_bytes < 0) - return grub_error (GRUB_ERR_BAD_FILE_TYPE, "image size overflown"); + return grub_error (GRUB_ERR_BAD_FILE_TYPE, "image size overflow"); if (data->cur_column == 0) { diff --git a/include/grub/arm/efi/console.h b/include/grub/arm/efi/console.h new file mode 100644 index 0000000000..1592f6f76b --- /dev/null +++ b/include/grub/arm/efi/console.h @@ -0,0 +1,24 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2005,2006,2007 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_ARM_EFI_CONSOLE_H +#define GRUB_ARM_EFI_CONSOLE_H + +#include + +#endif /* ! GRUB_ARM_EFI_CONSOLE_H */ diff --git a/include/grub/arm/efi/memory.h b/include/grub/arm/efi/memory.h index 2c64918e3f..a4c2ec8350 100644 --- a/include/grub/arm/efi/memory.h +++ b/include/grub/arm/efi/memory.h @@ -2,5 +2,6 @@ #include #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff +#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS #endif /* ! GRUB_MEMORY_CPU_HEADER */ diff --git a/include/grub/arm64/efi/console.h b/include/grub/arm64/efi/console.h new file mode 100644 index 0000000000..9568933938 --- /dev/null +++ b/include/grub/arm64/efi/console.h @@ -0,0 +1,24 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2005,2006,2007 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_ARM64_EFI_CONSOLE_H +#define GRUB_ARM64_EFI_CONSOLE_H + +#include + +#endif /* ! GRUB_ARM64_EFI_CONSOLE_H */ diff --git a/include/grub/arm64/efi/memory.h b/include/grub/arm64/efi/memory.h index c6cb324171..acb61dca44 100644 --- a/include/grub/arm64/efi/memory.h +++ b/include/grub/arm64/efi/memory.h @@ -2,5 +2,6 @@ #include #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffffffffULL +#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS #endif /* ! GRUB_MEMORY_CPU_HEADER */ diff --git a/include/grub/at_keyboard.h b/include/grub/at_keyboard.h index bcb4d9ba78..9414dc1b99 100644 --- a/include/grub/at_keyboard.h +++ b/include/grub/at_keyboard.h @@ -19,6 +19,10 @@ #ifndef GRUB_AT_KEYBOARD_HEADER #define GRUB_AT_KEYBOARD_HEADER 1 +/* + * Refer to https://wiki.osdev.org/%228042%22_PS/2_Controller for details. + */ + /* Used for sending commands to the controller. */ #define KEYBOARD_COMMAND_ISREADY(x) !((x) & 0x02) #define KEYBOARD_COMMAND_READ 0x20 diff --git a/include/grub/auth.h b/include/grub/auth.h index 7473344517..21d5190f04 100644 --- a/include/grub/auth.h +++ b/include/grub/auth.h @@ -33,5 +33,6 @@ grub_err_t grub_auth_unregister_authentication (const char *user); grub_err_t grub_auth_authenticate (const char *user); grub_err_t grub_auth_deauthenticate (const char *user); grub_err_t grub_auth_check_authentication (const char *userlist); +grub_err_t grub_auth_check_cli_access (void); #endif /* ! GRUB_AUTH_HEADER */ diff --git a/include/grub/backtrace.h b/include/grub/backtrace.h index 395519762f..275cf85e2d 100644 --- a/include/grub/backtrace.h +++ b/include/grub/backtrace.h @@ -19,8 +19,14 @@ #ifndef GRUB_BACKTRACE_HEADER #define GRUB_BACKTRACE_HEADER 1 -void grub_backtrace (void); -void grub_backtrace_pointer (void *ptr); +#include +#include + +void EXPORT_FUNC(grub_debug_backtrace) (const char * const debug, + unsigned int skip); +void EXPORT_FUNC(grub_backtrace) (unsigned int skip); +void grub_backtrace_arch (unsigned int skip); +void grub_backtrace_pointer (void *ptr, unsigned int skip); void grub_backtrace_print_address (void *addr); #endif diff --git a/include/grub/btrfs.h b/include/grub/btrfs.h index 9d93fb6c18..234ad97677 100644 --- a/include/grub/btrfs.h +++ b/include/grub/btrfs.h @@ -29,6 +29,7 @@ enum GRUB_BTRFS_ITEM_TYPE_ROOT_ITEM = 0x84, GRUB_BTRFS_ITEM_TYPE_ROOT_BACKREF = 0x90, GRUB_BTRFS_ITEM_TYPE_DEVICE = 0xd8, + GRUB_BTRFS_ITEM_TYPE_ROOT_REF = 0x9c, GRUB_BTRFS_ITEM_TYPE_CHUNK = 0xe4 }; diff --git a/include/grub/compiler.h b/include/grub/compiler.h index 0c5519387b..441a9eca07 100644 --- a/include/grub/compiler.h +++ b/include/grub/compiler.h @@ -56,4 +56,6 @@ # define CLANG_PREREQ(maj,min) 0 #endif +#define UNUSED __attribute__((__unused__)) + #endif /* ! GRUB_COMPILER_HEADER */ diff --git a/include/grub/cryptodisk.h b/include/grub/cryptodisk.h index d94df68b65..ef307dfd5d 100644 --- a/include/grub/cryptodisk.h +++ b/include/grub/cryptodisk.h @@ -187,4 +187,8 @@ grub_util_get_geli_uuid (const char *dev); grub_cryptodisk_t grub_cryptodisk_get_by_uuid (const char *uuid); grub_cryptodisk_t grub_cryptodisk_get_by_source_disk (grub_disk_t disk); +#ifdef GRUB_MACHINE_EFI +grub_err_t grub_cryptodisk_challenge_password (void); +void grub_cryptodisk_erasesecrets (void); +#endif #endif diff --git a/include/grub/dl.h b/include/grub/dl.h index cd1f46c8ba..055bb564e4 100644 --- a/include/grub/dl.h +++ b/include/grub/dl.h @@ -27,6 +27,7 @@ #include #include #include +#include #endif /* @@ -54,6 +55,7 @@ grub_mod_fini (void) #define GRUB_MOD_INIT(name) \ static void grub_mod_init (grub_dl_t mod __attribute__ ((unused))) __attribute__ ((used)); \ +extern void grub_##name##_init (void); \ void \ grub_##name##_init (void) { grub_mod_init (0); } \ static void \ @@ -61,6 +63,7 @@ grub_mod_init (grub_dl_t mod __attribute__ ((unused))) #define GRUB_MOD_FINI(name) \ static void grub_mod_fini (void) __attribute__ ((used)); \ +extern void grub_##name##_fini (void); \ void \ grub_##name##_fini (void) { grub_mod_fini (); } \ static void \ @@ -119,7 +122,7 @@ grub_mod_fini (void) #define ATTRIBUTE_USED __unused__ #endif #define GRUB_MOD_LICENSE(license) \ - static char grub_module_license[] __attribute__ ((section (GRUB_MOD_SECTION (module_license)), ATTRIBUTE_USED)) = "LICENSE=" license; + static const char grub_module_license[] __attribute__ ((section (GRUB_MOD_SECTION (module_license)), ATTRIBUTE_USED)) = "LICENSE=" license; #define GRUB_MOD_DEP(name) \ static const char grub_module_depend_##name[] \ __attribute__((section(GRUB_MOD_SECTION(moddeps)), ATTRIBUTE_USED)) = #name @@ -174,7 +177,7 @@ typedef struct grub_dl_dep *grub_dl_dep_t; struct grub_dl { char *name; - int ref_count; + grub_uint64_t ref_count; int persistent; grub_dl_dep_t dep; grub_dl_segment_t segment; @@ -203,9 +206,9 @@ grub_dl_t EXPORT_FUNC(grub_dl_load) (const char *name); grub_dl_t grub_dl_load_core (void *addr, grub_size_t size); grub_dl_t EXPORT_FUNC(grub_dl_load_core_noinit) (void *addr, grub_size_t size); int EXPORT_FUNC(grub_dl_unload) (grub_dl_t mod); -extern int EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod); -extern int EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod); -extern int EXPORT_FUNC(grub_dl_ref_count) (grub_dl_t mod); +extern grub_uint64_t EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod); +extern grub_uint64_t EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod); +extern grub_uint64_t EXPORT_FUNC(grub_dl_ref_count) (grub_dl_t mod); extern grub_dl_t EXPORT_VAR(grub_dl_head); @@ -242,11 +245,22 @@ grub_dl_get (const char *name) return 0; } +#ifdef GRUB_MACHINE_EMU +/* + * Under grub-emu, modules are faked and NULL is passed to GRUB_MOD_INIT. + * So we fake this out to avoid a NULL deref. + */ +static inline void +grub_dl_set_persistent (grub_dl_t mod __attribute__((unused))) +{ +} +#else static inline void grub_dl_set_persistent (grub_dl_t mod) { mod->persistent = 1; } +#endif static inline int grub_dl_is_persistent (grub_dl_t mod) @@ -254,8 +268,53 @@ grub_dl_is_persistent (grub_dl_t mod) return mod->persistent; } +static inline const char * +grub_dl_get_section_name (const Elf_Ehdr *e, const Elf_Shdr *s) +{ + Elf_Shdr *str_s; + const char *str; + + str_s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize); + str = (char *) e + str_s->sh_offset; + + return str + s->sh_name; +} + +static inline long +grub_dl_find_section_index (Elf_Ehdr *e, const char *name) +{ + Elf_Shdr *s; + const char *str; + unsigned i; + + s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize); + str = (char *) e + s->sh_offset; + + for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); + i < e->e_shnum; + i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) + if (grub_strcmp (str + s->sh_name, name) == 0) + return (long)i; + return -1; +} + +/* Return the segment for a section of index N */ +static inline grub_dl_segment_t +grub_dl_find_segment (grub_dl_t mod, unsigned n) +{ + grub_dl_segment_t seg; + + for (seg = mod->segment; seg; seg = seg->next) + if (seg->section == n) + return seg; + + return NULL; +} + #endif +void * EXPORT_FUNC(grub_resolve_symbol) (const char *name); +const char * EXPORT_FUNC(grub_get_symbol_by_addr) (const void *addr, int isfunc); grub_err_t grub_dl_register_symbol (const char *name, void *addr, int isfunc, grub_dl_t mod); @@ -264,6 +323,8 @@ grub_err_t grub_arch_dl_check_header (void *ehdr); grub_err_t grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, Elf_Shdr *s, grub_dl_segment_t seg); +grub_size_t +grub_arch_dl_min_alignment (void); #endif #if defined (_mips) diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h index d44d00ad7d..24243eb031 100644 --- a/include/grub/efi/api.h +++ b/include/grub/efi/api.h @@ -369,6 +369,16 @@ { 0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44 } \ } +#define GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID \ + { 0x5b446ed1, 0xe30b, 0x4faa, \ + { 0x87, 0x1a, 0x36, 0x54, 0xec, 0xa3, 0x60, 0x80 } \ + } + +#define GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID \ + { 0x937fe521, 0x95ae, 0x4d1a, \ + { 0x89, 0x29, 0x48, 0xbc, 0xd9, 0x0a, 0xd3, 0x1a } \ + } + #define LINUX_EFI_INITRD_MEDIA_GUID \ { 0x5568e427, 0x68fc, 0x4f3d, \ { 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68 } \ @@ -379,6 +389,11 @@ {0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } \ } +#define GRUB_EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID \ + { 0xf4560cf6, 0x40ec, 0x4b4a, \ + { 0xa1, 0x92, 0xbf, 0x1d, 0x57, 0xd0, 0xb1, 0x89 } \ + } + struct grub_efi_sal_system_table { grub_uint32_t signature; @@ -577,7 +592,16 @@ typedef grub_uint64_t grub_efi_uint64_t; typedef grub_uint8_t grub_efi_char8_t; typedef grub_uint16_t grub_efi_char16_t; + typedef grub_efi_uintn_t grub_efi_status_t; +/* Make grub_efi_status_t reasonably printable. */ +#if GRUB_CPU_SIZEOF_VOID_P == 8 +#define PRIxGRUB_EFI_STATUS "lx" +#define PRIdGRUB_EFI_STATUS "ld" +#else +#define PRIxGRUB_EFI_STATUS "llx" +#define PRIdGRUB_EFI_STATUS "lld" +#endif /* * On x86, the EFI calling convention may deviate from the local one, so @@ -637,12 +661,35 @@ typedef void *grub_efi_handle_t; typedef void *grub_efi_event_t; typedef grub_efi_uint64_t grub_efi_lba_t; typedef grub_efi_uintn_t grub_efi_tpl_t; -typedef grub_uint8_t grub_efi_mac_address_t[32]; -typedef grub_uint8_t grub_efi_ipv4_address_t[4]; -typedef grub_uint16_t grub_efi_ipv6_address_t[8]; -typedef grub_uint8_t grub_efi_ip_address_t[8] __attribute__ ((aligned(4))); +typedef grub_efi_uint8_t grub_efi_mac_address_t[32]; +typedef grub_efi_uint8_t grub_efi_ipv4_address_t[4]; +typedef grub_efi_uint8_t grub_efi_ipv6_address_t[16]; +typedef union +{ + grub_efi_uint32_t addr[4]; + grub_efi_ipv4_address_t v4; + grub_efi_ipv6_address_t v6; +} grub_efi_ip_address_t __attribute__ ((aligned(4))); + typedef grub_efi_uint64_t grub_efi_physical_address_t; typedef grub_efi_uint64_t grub_efi_virtual_address_t; +typedef struct { + grub_uint8_t addr[4]; +} grub_efi_pxe_ipv4_address_t; + +typedef struct { + grub_uint8_t addr[16]; +} grub_efi_pxe_ipv6_address_t; + +typedef struct { + grub_uint8_t addr[32]; +} grub_efi_pxe_mac_address_t; + +typedef union { + grub_uint32_t addr[4]; + grub_efi_pxe_ipv4_address_t v4; + grub_efi_pxe_ipv6_address_t v6; +} grub_efi_pxe_ip_address_t; /* XXX although the spec does not specify the padding, this actually must have the padding! */ @@ -876,6 +923,8 @@ struct grub_efi_ipv4_device_path grub_efi_uint16_t remote_port; grub_efi_uint16_t protocol; grub_efi_uint8_t static_ip_address; + grub_efi_ipv4_address_t gateway_ip_address; + grub_efi_ipv4_address_t subnet_mask; } GRUB_PACKED; typedef struct grub_efi_ipv4_device_path grub_efi_ipv4_device_path_t; @@ -890,6 +939,8 @@ struct grub_efi_ipv6_device_path grub_efi_uint16_t remote_port; grub_efi_uint16_t protocol; grub_efi_uint8_t static_ip_address; + grub_efi_uint8_t prefix_length; + grub_efi_ipv6_address_t gateway_ip_address; } GRUB_PACKED; typedef struct grub_efi_ipv6_device_path grub_efi_ipv6_device_path_t; @@ -939,6 +990,24 @@ struct grub_efi_vlan_device_path } GRUB_PACKED; typedef struct grub_efi_vlan_device_path grub_efi_vlan_device_path_t; +#define GRUB_EFI_URI_DEVICE_PATH_SUBTYPE 24 + +struct grub_efi_uri_device_path +{ + grub_efi_device_path_t header; + grub_efi_uint8_t uri[0]; +} GRUB_PACKED; +typedef struct grub_efi_uri_device_path grub_efi_uri_device_path_t; + +#define GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE 31 +struct grub_efi_dns_device_path +{ + grub_efi_device_path_t header; + grub_efi_uint8_t is_ipv6; + grub_efi_pxe_ip_address_t dns_server_ip[0]; +} GRUB_PACKED; +typedef struct grub_efi_dns_device_path grub_efi_dns_device_path_t; + #define GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE 10 /* Media Device Path. */ @@ -1021,6 +1090,23 @@ struct grub_efi_bios_device_path } GRUB_PACKED; typedef struct grub_efi_bios_device_path grub_efi_bios_device_path_t; +/* Service Binding definitions */ +struct grub_efi_service_binding; + +typedef grub_efi_status_t +(__grub_efi_api *grub_efi_service_binding_create_child) (struct grub_efi_service_binding *this, + grub_efi_handle_t *child_handle); + +typedef grub_efi_status_t +(__grub_efi_api *grub_efi_service_binding_destroy_child) (struct grub_efi_service_binding *this, + grub_efi_handle_t *child_handle); + +typedef struct grub_efi_service_binding +{ + grub_efi_service_binding_create_child create_child; + grub_efi_service_binding_destroy_child destroy_child; +} grub_efi_service_binding_t; + struct grub_efi_open_protocol_information_entry { grub_efi_handle_t agent_handle; @@ -1521,33 +1607,178 @@ struct grub_efi_simple_text_output_interface }; typedef struct grub_efi_simple_text_output_interface grub_efi_simple_text_output_interface_t; -typedef grub_uint8_t grub_efi_pxe_packet_t[1472]; +typedef struct grub_efi_pxe_dhcpv4_packet +{ + grub_efi_uint8_t bootp_opcode; + grub_efi_uint8_t bootp_hwtype; + grub_efi_uint8_t bootp_hwaddr_len; + grub_efi_uint8_t bootp_gate_hops; + grub_efi_uint32_t bootp_ident; + grub_efi_uint16_t bootp_seconds; + grub_efi_uint16_t bootp_flags; + grub_efi_uint8_t bootp_ci_addr[4]; + grub_efi_uint8_t bootp_yi_addr[4]; + grub_efi_uint8_t bootp_si_addr[4]; + grub_efi_uint8_t bootp_gi_addr[4]; + grub_efi_uint8_t bootp_hw_addr[16]; + grub_efi_uint8_t bootp_srv_name[64]; + grub_efi_uint8_t bootp_boot_file[128]; + grub_efi_uint32_t dhcp_magik; + grub_efi_uint8_t dhcp_options[56]; +} grub_efi_pxe_dhcpv4_packet_t; + +struct grub_efi_pxe_dhcpv6_packet +{ + grub_efi_uint32_t message_type:8; + grub_efi_uint32_t transaction_id:24; + grub_efi_uint8_t dhcp_options[1024]; +} GRUB_PACKED; +typedef struct grub_efi_pxe_dhcpv6_packet grub_efi_pxe_dhcpv6_packet_t; + +typedef union +{ + grub_efi_uint8_t raw[1472]; + grub_efi_pxe_dhcpv4_packet_t dhcpv4; + grub_efi_pxe_dhcpv6_packet_t dhcpv6; +} grub_efi_pxe_packet_t; + +typedef struct grub_efi_pxe_icmp_error +{ + grub_efi_uint8_t type; + grub_efi_uint8_t code; + grub_efi_uint16_t checksum; + union + { + grub_efi_uint32_t reserved; + grub_efi_uint32_t mtu; + grub_efi_uint32_t pointer; + struct + { + grub_efi_uint16_t identifier; + grub_efi_uint16_t sequence; + } echo; + } u; + grub_efi_uint8_t data[494]; +} grub_efi_pxe_icmp_error_t; + +typedef struct grub_efi_pxe_tftp_error +{ + grub_efi_uint8_t error_code; + grub_efi_char8_t error_string[127]; +} grub_efi_pxe_tftp_error_t; + +typedef grub_efi_uint16_t grub_efi_pxe_base_code_udp_port_t; + +typedef enum { + GRUB_EFI_PXE_BASE_CODE_TFTP_FIRST, + GRUB_EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE, + GRUB_EFI_PXE_BASE_CODE_TFTP_READ_FILE, + GRUB_EFI_PXE_BASE_CODE_TFTP_WRITE_FILE, + GRUB_EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY, + GRUB_EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE, + GRUB_EFI_PXE_BASE_CODE_MTFTP_READ_FILE, + GRUB_EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY, + GRUB_EFI_PXE_BASE_CODE_MTFTP_LAST +} grub_efi_pxe_base_code_tftp_opcode_t; + +typedef struct { + grub_efi_ip_address_t mcast_ip; + grub_efi_pxe_base_code_udp_port_t c_port; + grub_efi_pxe_base_code_udp_port_t s_port; + grub_efi_uint16_t listen_timeout; + grub_efi_uint16_t transmit_timeout; +} grub_efi_pxe_base_code_mtftp_info_t; + +#define GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT 8 +typedef struct grub_efi_pxe_ip_filter +{ + grub_efi_uint8_t filters; + grub_efi_uint8_t ip_count; + grub_efi_uint16_t reserved; + grub_efi_ip_address_t ip_list[GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT]; +} grub_efi_pxe_ip_filter_t; + +typedef struct { + grub_efi_pxe_ip_address_t ip_addr; + grub_efi_pxe_mac_address_t mac_addr; +} grub_efi_pxe_arp_entry_t; + +typedef struct { + grub_efi_pxe_ip_address_t ip_addr; + grub_efi_pxe_ip_address_t subnet_mask; + grub_efi_pxe_ip_address_t gw_addr; +} grub_efi_pxe_route_entry_t; + + +#define GRUB_EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES 8 +#define GRUB_EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES 8 typedef struct grub_efi_pxe_mode { - grub_uint8_t unused[52]; + grub_efi_boolean_t started; + grub_efi_boolean_t ipv6_available; + grub_efi_boolean_t ipv6_supported; + grub_efi_boolean_t using_ipv6; + grub_efi_boolean_t bis_supported; + grub_efi_boolean_t bis_detected; + grub_efi_boolean_t auto_arp; + grub_efi_boolean_t send_guid; + grub_efi_boolean_t dhcp_discover_valid; + grub_efi_boolean_t dhcp_ack_received; + grub_efi_boolean_t proxy_offer_received; + grub_efi_boolean_t pxe_discover_valid; + grub_efi_boolean_t pxe_reply_received; + grub_efi_boolean_t pxe_bis_reply_received; + grub_efi_boolean_t icmp_error_received; + grub_efi_boolean_t tftp_error_received; + grub_efi_boolean_t make_callbacks; + grub_efi_uint8_t ttl; + grub_efi_uint8_t tos; + grub_efi_ip_address_t station_ip; + grub_efi_ip_address_t subnet_mask; grub_efi_pxe_packet_t dhcp_discover; grub_efi_pxe_packet_t dhcp_ack; grub_efi_pxe_packet_t proxy_offer; grub_efi_pxe_packet_t pxe_discover; grub_efi_pxe_packet_t pxe_reply; + grub_efi_pxe_packet_t pxe_bis_reply; + grub_efi_pxe_ip_filter_t ip_filter; + grub_efi_uint32_t arp_cache_entries; + grub_efi_pxe_arp_entry_t arp_cache[GRUB_EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES]; + grub_efi_uint32_t route_table_entries; + grub_efi_pxe_route_entry_t route_table[GRUB_EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES]; + grub_efi_pxe_icmp_error_t icmp_error; + grub_efi_pxe_tftp_error_t tftp_error; } grub_efi_pxe_mode_t; typedef struct grub_efi_pxe { grub_uint64_t rev; - void *start; - void *stop; - void *dhcp; - void *discover; - void *mftp; - void *udpwrite; - void *udpread; - void *setipfilter; - void *arp; - void *setparams; - void *setstationip; - void *setpackets; + grub_efi_status_t (__grub_efi_api *start) (struct grub_efi_pxe *this, grub_efi_boolean_t use_ipv6); + void (*stop) (void); + grub_efi_status_t (__grub_efi_api *dhcp) (struct grub_efi_pxe *this, + grub_efi_boolean_t sort_offers); + void (*discover) (void); + grub_efi_status_t (__grub_efi_api *mtftp) (struct grub_efi_pxe *this, + grub_efi_pxe_base_code_tftp_opcode_t operation, + void *buffer_ptr, + grub_efi_boolean_t overwrite, + grub_efi_uint64_t *buffer_size, + grub_efi_uintn_t *block_size, + grub_efi_pxe_ip_address_t *server_ip, + //grub_efi_ip_address_t *server_ip, + grub_efi_char8_t *filename, + grub_efi_pxe_base_code_mtftp_info_t *info, + grub_efi_boolean_t dont_use_buffer); + void (*udpwrite) (void); + void (*udpread) (void); + void (*setipfilter) (void); + void (*arp) (void); + void (*setparams) (void); + grub_efi_status_t (__grub_efi_api *set_station_ip) (struct grub_efi_pxe *this, + grub_efi_pxe_ip_address_t *new_station_ip, + grub_efi_pxe_ip_address_t *new_subnet_mask); + void (*setpackets) (void); struct grub_efi_pxe_mode *mode; } grub_efi_pxe_t; @@ -1809,6 +2040,173 @@ struct grub_efi_load_file2 }; typedef struct grub_efi_load_file2 grub_efi_load_file2_t; +enum grub_efi_ip4_config2_data_type { + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_POLICY, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_GATEWAY, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER, + GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MAXIMUM +}; +typedef enum grub_efi_ip4_config2_data_type grub_efi_ip4_config2_data_type_t; + +struct grub_efi_ip4_config2_protocol +{ + grub_efi_status_t (__grub_efi_api *set_data) (struct grub_efi_ip4_config2_protocol *this, + grub_efi_ip4_config2_data_type_t data_type, + grub_efi_uintn_t data_size, + void *data); + + grub_efi_status_t (__grub_efi_api *get_data) (struct grub_efi_ip4_config2_protocol *this, + grub_efi_ip4_config2_data_type_t data_type, + grub_efi_uintn_t *data_size, + void *data); + + grub_efi_status_t (__grub_efi_api *register_data_notify) (struct grub_efi_ip4_config2_protocol *this, + grub_efi_ip4_config2_data_type_t data_type, + grub_efi_event_t event); + + grub_efi_status_t (__grub_efi_api *unregister_datanotify) (struct grub_efi_ip4_config2_protocol *this, + grub_efi_ip4_config2_data_type_t data_type, + grub_efi_event_t event); +}; +typedef struct grub_efi_ip4_config2_protocol grub_efi_ip4_config2_protocol_t; + +struct grub_efi_ip4_route_table { + grub_efi_ipv4_address_t subnet_address; + grub_efi_ipv4_address_t subnet_mask; + grub_efi_ipv4_address_t gateway_address; +}; + +typedef struct grub_efi_ip4_route_table grub_efi_ip4_route_table_t; + +#define GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE 32 + +struct grub_efi_ip4_config2_interface_info { + grub_efi_char16_t name[GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE]; + grub_efi_uint8_t if_type; + grub_efi_uint32_t hw_address_size; + grub_efi_mac_address_t hw_address; + grub_efi_ipv4_address_t station_address; + grub_efi_ipv4_address_t subnet_mask; + grub_efi_uint32_t route_table_size; + grub_efi_ip4_route_table_t *route_table; +}; + +typedef struct grub_efi_ip4_config2_interface_info grub_efi_ip4_config2_interface_info_t; + +enum grub_efi_ip4_config2_policy { + GRUB_EFI_IP4_CONFIG2_POLICY_STATIC, + GRUB_EFI_IP4_CONFIG2_POLICY_DHCP, + GRUB_EFI_IP4_CONFIG2_POLICY_MAX +}; + +typedef enum grub_efi_ip4_config2_policy grub_efi_ip4_config2_policy_t; + +struct grub_efi_ip4_config2_manual_address { + grub_efi_ipv4_address_t address; + grub_efi_ipv4_address_t subnet_mask; +}; + +typedef struct grub_efi_ip4_config2_manual_address grub_efi_ip4_config2_manual_address_t; + +enum grub_efi_ip6_config_data_type { + GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_ALT_INTERFACEID, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_POLICY, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_DUP_ADDR_DETECT_TRANSMITS, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_GATEWAY, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER, + GRUB_EFI_IP6_CONFIG_DATA_TYPE_MAXIMUM +}; +typedef enum grub_efi_ip6_config_data_type grub_efi_ip6_config_data_type_t; + +struct grub_efi_ip6_config_protocol +{ + grub_efi_status_t (__grub_efi_api *set_data) (struct grub_efi_ip6_config_protocol *this, + grub_efi_ip6_config_data_type_t data_type, + grub_efi_uintn_t data_size, + void *data); + + grub_efi_status_t (__grub_efi_api *get_data) (struct grub_efi_ip6_config_protocol *this, + grub_efi_ip6_config_data_type_t data_type, + grub_efi_uintn_t *data_size, + void *data); + + grub_efi_status_t (__grub_efi_api *register_data_notify) (struct grub_efi_ip6_config_protocol *this, + grub_efi_ip6_config_data_type_t data_type, + grub_efi_event_t event); + + grub_efi_status_t (__grub_efi_api *unregister_datanotify) (struct grub_efi_ip6_config_protocol *this, + grub_efi_ip6_config_data_type_t data_type, + grub_efi_event_t event); +}; +typedef struct grub_efi_ip6_config_protocol grub_efi_ip6_config_protocol_t; + +enum grub_efi_ip6_config_policy { + GRUB_EFI_IP6_CONFIG_POLICY_MANUAL, + GRUB_EFI_IP6_CONFIG_POLICY_AUTOMATIC +}; +typedef enum grub_efi_ip6_config_policy grub_efi_ip6_config_policy_t; + +struct grub_efi_ip6_address_info { + grub_efi_ipv6_address_t address; + grub_efi_uint8_t prefix_length; +}; +typedef struct grub_efi_ip6_address_info grub_efi_ip6_address_info_t; + +struct grub_efi_ip6_route_table { + grub_efi_pxe_ipv6_address_t gateway; + grub_efi_pxe_ipv6_address_t destination; + grub_efi_uint8_t prefix_length; +}; +typedef struct grub_efi_ip6_route_table grub_efi_ip6_route_table_t; + +struct grub_efi_ip6_config_interface_info { + grub_efi_char16_t name[32]; + grub_efi_uint8_t if_type; + grub_efi_uint32_t hw_address_size; + grub_efi_mac_address_t hw_address; + grub_efi_uint32_t address_info_count; + grub_efi_ip6_address_info_t *address_info; + grub_efi_uint32_t route_count; + grub_efi_ip6_route_table_t *route_table; +}; +typedef struct grub_efi_ip6_config_interface_info grub_efi_ip6_config_interface_info_t; + +struct grub_efi_ip6_config_dup_addr_detect_transmits { + grub_efi_uint32_t dup_addr_detect_transmits; +}; +typedef struct grub_efi_ip6_config_dup_addr_detect_transmits grub_efi_ip6_config_dup_addr_detect_transmits_t; + +struct grub_efi_ip6_config_manual_address { + grub_efi_ipv6_address_t address; + grub_efi_boolean_t is_anycast; + grub_efi_uint8_t prefix_length; +}; +typedef struct grub_efi_ip6_config_manual_address grub_efi_ip6_config_manual_address_t; + +struct grub_efi_memory_attribute_protocol +{ + grub_efi_status_t (__grub_efi_api *get_memory_attributes) ( + struct grub_efi_memory_attribute_protocol *this, + grub_efi_physical_address_t base_address, + grub_efi_uint64_t length, + grub_efi_uint64_t *attributes); + grub_efi_status_t (__grub_efi_api *set_memory_attributes) ( + struct grub_efi_memory_attribute_protocol *this, + grub_efi_physical_address_t base_address, + grub_efi_uint64_t length, + grub_efi_uint64_t attributes); + grub_efi_status_t (__grub_efi_api *clear_memory_attributes) ( + struct grub_efi_memory_attribute_protocol *this, + grub_efi_physical_address_t base_address, + grub_efi_uint64_t length, + grub_efi_uint64_t attributes); +}; +typedef struct grub_efi_memory_attribute_protocol grub_efi_memory_attribute_protocol_t; + struct initrd_media_device_path { grub_efi_vendor_media_device_path_t vendor; grub_efi_device_path_t end; diff --git a/include/grub/efi/debug.h b/include/grub/efi/debug.h index c2d2a03b06..961e591afa 100644 --- a/include/grub/efi/debug.h +++ b/include/grub/efi/debug.h @@ -27,7 +27,7 @@ void grub_efi_register_debug_commands (void); static inline void -grub_efi_print_gdb_info (void) +grub_efi_print_gdb_info (bool debug) { grub_addr_t text; @@ -35,7 +35,10 @@ grub_efi_print_gdb_info (void) if (!text) return; - grub_printf ("dynamic_load_symbols %p\n", (void *)text); + if (debug) + grub_qdprintf ("gdb", "dynamic_load_symbols %p\n", (void *)text); + else + grub_printf ("dynamic_load_symbols %p\n", (void *)text); } #endif /* ! GRUB_EFI_DEBUG_HEADER */ diff --git a/include/grub/efi/dhcp.h b/include/grub/efi/dhcp.h new file mode 100644 index 0000000000..d996716e40 --- /dev/null +++ b/include/grub/efi/dhcp.h @@ -0,0 +1,343 @@ +#ifndef GRUB_EFI_DHCP_HEADER +#define GRUB_EFI_DHCP_HEADER 1 + +#define GRUB_EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID \ + { 0x9d9a39d8, 0xbd42, 0x4a73, \ + { 0xa4, 0xd5, 0x8e, 0xe9, 0x4b, 0xe1, 0x13, 0x80 } \ + } + +#define GRUB_EFI_DHCP4_PROTOCOL_GUID \ + { 0x8a219718, 0x4ef5, 0x4761, \ + { 0x91, 0xc8, 0xc0, 0xf0, 0x4b, 0xda, 0x9e, 0x56 } \ + } + +#define GRUB_EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID \ + { 0x9fb9a8a1, 0x2f4a, 0x43a6, \ + { 0x88, 0x9c, 0xd0, 0xf7, 0xb6, 0xc4 ,0x7a, 0xd5 } \ + } + +#define GRUB_EFI_DHCP6_PROTOCOL_GUID \ + { 0x87c8bad7, 0x595, 0x4053, \ + { 0x82, 0x97, 0xde, 0xde, 0x39, 0x5f, 0x5d, 0x5b } \ + } + +typedef struct grub_efi_dhcp4_protocol grub_efi_dhcp4_protocol_t; + +enum grub_efi_dhcp4_state { + GRUB_EFI_DHCP4_STOPPED, + GRUB_EFI_DHCP4_INIT, + GRUB_EFI_DHCP4_SELECTING, + GRUB_EFI_DHCP4_REQUESTING, + GRUB_EFI_DHCP4_BOUND, + GRUB_EFI_DHCP4_RENEWING, + GRUB_EFI_DHCP4_REBINDING, + GRUB_EFI_DHCP4_INIT_REBOOT, + GRUB_EFI_DHCP4_REBOOTING +}; + +typedef enum grub_efi_dhcp4_state grub_efi_dhcp4_state_t; + +struct grub_efi_dhcp4_header { + grub_efi_uint8_t op_code; + grub_efi_uint8_t hw_type; + grub_efi_uint8_t hw_addr_len; + grub_efi_uint8_t hops; + grub_efi_uint32_t xid; + grub_efi_uint16_t seconds; + grub_efi_uint16_t reserved; + grub_efi_ipv4_address_t client_addr; + grub_efi_ipv4_address_t your_addr; + grub_efi_ipv4_address_t server_addr; + grub_efi_ipv4_address_t gateway_addr; + grub_efi_uint8_t client_hw_addr[16]; + grub_efi_char8_t server_name[64]; + grub_efi_char8_t boot_file_name[128]; +} GRUB_PACKED; + +typedef struct grub_efi_dhcp4_header grub_efi_dhcp4_header_t; + +struct grub_efi_dhcp4_packet { + grub_efi_uint32_t size; + grub_efi_uint32_t length; + struct { + grub_efi_dhcp4_header_t header; + grub_efi_uint32_t magik; + grub_efi_uint8_t option[1]; + } dhcp4; +} GRUB_PACKED; + +typedef struct grub_efi_dhcp4_packet grub_efi_dhcp4_packet_t; + +struct grub_efi_dhcp4_listen_point { + grub_efi_ipv4_address_t listen_address; + grub_efi_ipv4_address_t subnet_mask; + grub_efi_uint16_t listen_port; +}; + +typedef struct grub_efi_dhcp4_listen_point grub_efi_dhcp4_listen_point_t; + +struct grub_efi_dhcp4_transmit_receive_token { + grub_efi_status_t status; + grub_efi_event_t completion_event; + grub_efi_ipv4_address_t remote_address; + grub_efi_uint16_t remote_port; + grub_efi_ipv4_address_t gateway_address; + grub_efi_uint32_t listen_point_count; + grub_efi_dhcp4_listen_point_t *listen_points; + grub_efi_uint32_t timeout_value; + grub_efi_dhcp4_packet_t *packet; + grub_efi_uint32_t response_count; + grub_efi_dhcp4_packet_t *response_list; +}; + +typedef struct grub_efi_dhcp4_transmit_receive_token grub_efi_dhcp4_transmit_receive_token_t; + +enum grub_efi_dhcp4_event { + GRUB_EFI_DHCP4_SEND_DISCOVER = 0X01, + GRUB_EFI_DHCP4_RCVD_OFFER, + GRUB_EFI_DHCP4_SELECT_OFFER, + GRUB_EFI_DHCP4_SEND_REQUEST, + GRUB_EFI_DHCP4_RCVD_ACK, + GRUB_EFI_DHCP4_RCVD_NAK, + GRUB_EFI_DHCP4_SEND_DECLINE, + GRUB_EFI_DHCP4_BOUND_COMPLETED, + GRUB_EFI_DHCP4_ENTER_RENEWING, + GRUB_EFI_DHCP4_ENTER_REBINDING, + GRUB_EFI_DHCP4_ADDRESS_LOST, + GRUB_EFI_DHCP4_FAIL +}; + +typedef enum grub_efi_dhcp4_event grub_efi_dhcp4_event_t; + +struct grub_efi_dhcp4_packet_option { + grub_efi_uint8_t op_code; + grub_efi_uint8_t length; + grub_efi_uint8_t data[1]; +} GRUB_PACKED; + +typedef struct grub_efi_dhcp4_packet_option grub_efi_dhcp4_packet_option_t; + +struct grub_efi_dhcp4_config_data { + grub_efi_uint32_t discover_try_count; + grub_efi_uint32_t *discover_timeout; + grub_efi_uint32_t request_try_count; + grub_efi_uint32_t *request_timeout; + grub_efi_ipv4_address_t client_address; + grub_efi_status_t (__grub_efi_api *dhcp4_callback) ( + grub_efi_dhcp4_protocol_t *this, + void *context, + grub_efi_dhcp4_state_t current_state, + grub_efi_dhcp4_event_t dhcp4_event, + grub_efi_dhcp4_packet_t *packet, + grub_efi_dhcp4_packet_t **new_packet + ); + void *callback_context; + grub_efi_uint32_t option_count; + grub_efi_dhcp4_packet_option_t **option_list; +}; + +typedef struct grub_efi_dhcp4_config_data grub_efi_dhcp4_config_data_t; + +struct grub_efi_dhcp4_mode_data { + grub_efi_dhcp4_state_t state; + grub_efi_dhcp4_config_data_t config_data; + grub_efi_ipv4_address_t client_address; + grub_efi_mac_address_t client_mac_address; + grub_efi_ipv4_address_t server_address; + grub_efi_ipv4_address_t router_address; + grub_efi_ipv4_address_t subnet_mask; + grub_efi_uint32_t lease_time; + grub_efi_dhcp4_packet_t *reply_packet; +}; + +typedef struct grub_efi_dhcp4_mode_data grub_efi_dhcp4_mode_data_t; + +struct grub_efi_dhcp4_protocol { + grub_efi_status_t (__grub_efi_api *get_mode_data) (grub_efi_dhcp4_protocol_t *this, + grub_efi_dhcp4_mode_data_t *dhcp4_mode_data); + grub_efi_status_t (__grub_efi_api *configure) (grub_efi_dhcp4_protocol_t *this, + grub_efi_dhcp4_config_data_t *dhcp4_cfg_data); + grub_efi_status_t (__grub_efi_api *start) (grub_efi_dhcp4_protocol_t *this, + grub_efi_event_t completion_event); + grub_efi_status_t (__grub_efi_api *renew_rebind) (grub_efi_dhcp4_protocol_t *this, + grub_efi_boolean_t rebind_request, + grub_efi_event_t completion_event); + grub_efi_status_t (__grub_efi_api *release) (grub_efi_dhcp4_protocol_t *this); + grub_efi_status_t (__grub_efi_api *stop) (grub_efi_dhcp4_protocol_t *this); + grub_efi_status_t (__grub_efi_api *build) (grub_efi_dhcp4_protocol_t *this, + grub_efi_dhcp4_packet_t *seed_packet, + grub_efi_uint32_t delete_count, + grub_efi_uint8_t *delete_list, + grub_efi_uint32_t append_count, + grub_efi_dhcp4_packet_option_t *append_list[], + grub_efi_dhcp4_packet_t **new_packet); + grub_efi_status_t (__grub_efi_api *transmit_receive) (grub_efi_dhcp4_protocol_t *this, + grub_efi_dhcp4_transmit_receive_token_t *token); + grub_efi_status_t (__grub_efi_api *parse) (grub_efi_dhcp4_protocol_t *this, + grub_efi_dhcp4_packet_t *packet, + grub_efi_uint32_t *option_count, + grub_efi_dhcp4_packet_option_t *packet_option_list[]); +}; + +typedef struct grub_efi_dhcp6_protocol grub_efi_dhcp6_protocol_t; + +struct grub_efi_dhcp6_retransmission { + grub_efi_uint32_t irt; + grub_efi_uint32_t mrc; + grub_efi_uint32_t mrt; + grub_efi_uint32_t mrd; +}; + +typedef struct grub_efi_dhcp6_retransmission grub_efi_dhcp6_retransmission_t; + +enum grub_efi_dhcp6_event { + GRUB_EFI_DHCP6_SEND_SOLICIT, + GRUB_EFI_DHCP6_RCVD_ADVERTISE, + GRUB_EFI_DHCP6_SELECT_ADVERTISE, + GRUB_EFI_DHCP6_SEND_REQUEST, + GRUB_EFI_DHCP6_RCVD_REPLY, + GRUB_EFI_DHCP6_RCVD_RECONFIGURE, + GRUB_EFI_DHCP6_SEND_DECLINE, + GRUB_EFI_DHCP6_SEND_CONFIRM, + GRUB_EFI_DHCP6_SEND_RELEASE, + GRUB_EFI_DHCP6_SEND_RENEW, + GRUB_EFI_DHCP6_SEND_REBIND +}; + +typedef enum grub_efi_dhcp6_event grub_efi_dhcp6_event_t; + +struct grub_efi_dhcp6_packet_option { + grub_efi_uint16_t op_code; + grub_efi_uint16_t op_len; + grub_efi_uint8_t data[1]; +} GRUB_PACKED; + +typedef struct grub_efi_dhcp6_packet_option grub_efi_dhcp6_packet_option_t; + +struct grub_efi_dhcp6_header { + grub_efi_uint32_t transaction_id:24; + grub_efi_uint32_t message_type:8; +} GRUB_PACKED; + +typedef struct grub_efi_dhcp6_header grub_efi_dhcp6_header_t; + +struct grub_efi_dhcp6_packet { + grub_efi_uint32_t size; + grub_efi_uint32_t length; + struct { + grub_efi_dhcp6_header_t header; + grub_efi_uint8_t option[1]; + } dhcp6; +} GRUB_PACKED; + +typedef struct grub_efi_dhcp6_packet grub_efi_dhcp6_packet_t; + +struct grub_efi_dhcp6_ia_address { + grub_efi_ipv6_address_t ip_address; + grub_efi_uint32_t preferred_lifetime; + grub_efi_uint32_t valid_lifetime; +}; + +typedef struct grub_efi_dhcp6_ia_address grub_efi_dhcp6_ia_address_t; + +enum grub_efi_dhcp6_state { + GRUB_EFI_DHCP6_INIT, + GRUB_EFI_DHCP6_SELECTING, + GRUB_EFI_DHCP6_REQUESTING, + GRUB_EFI_DHCP6_DECLINING, + GRUB_EFI_DHCP6_CONFIRMING, + GRUB_EFI_DHCP6_RELEASING, + GRUB_EFI_DHCP6_BOUND, + GRUB_EFI_DHCP6_RENEWING, + GRUB_EFI_DHCP6_REBINDING +}; + +typedef enum grub_efi_dhcp6_state grub_efi_dhcp6_state_t; + +#define GRUB_EFI_DHCP6_IA_TYPE_NA 3 +#define GRUB_EFI_DHCP6_IA_TYPE_TA 4 + +struct grub_efi_dhcp6_ia_descriptor { + grub_efi_uint16_t type; + grub_efi_uint32_t ia_id; +}; + +typedef struct grub_efi_dhcp6_ia_descriptor grub_efi_dhcp6_ia_descriptor_t; + +struct grub_efi_dhcp6_ia { + grub_efi_dhcp6_ia_descriptor_t descriptor; + grub_efi_dhcp6_state_t state; + grub_efi_dhcp6_packet_t *reply_packet; + grub_efi_uint32_t ia_address_count; + grub_efi_dhcp6_ia_address_t ia_address[1]; +}; + +typedef struct grub_efi_dhcp6_ia grub_efi_dhcp6_ia_t; + +struct grub_efi_dhcp6_duid { + grub_efi_uint16_t length; + grub_efi_uint8_t duid[1]; +}; + +typedef struct grub_efi_dhcp6_duid grub_efi_dhcp6_duid_t; + +struct grub_efi_dhcp6_mode_data { + grub_efi_dhcp6_duid_t *client_id; + grub_efi_dhcp6_ia_t *ia; +}; + +typedef struct grub_efi_dhcp6_mode_data grub_efi_dhcp6_mode_data_t; + +struct grub_efi_dhcp6_config_data { + grub_efi_status_t (__grub_efi_api *dhcp6_callback) (grub_efi_dhcp6_protocol_t this, + void *context, + grub_efi_dhcp6_state_t current_state, + grub_efi_dhcp6_event_t dhcp6_event, + grub_efi_dhcp6_packet_t *packet, + grub_efi_dhcp6_packet_t **new_packet); + void *callback_context; + grub_efi_uint32_t option_count; + grub_efi_dhcp6_packet_option_t **option_list; + grub_efi_dhcp6_ia_descriptor_t ia_descriptor; + grub_efi_event_t ia_info_event; + grub_efi_boolean_t reconfigure_accept; + grub_efi_boolean_t rapid_commit; + grub_efi_dhcp6_retransmission_t *solicit_retransmission; +}; + +typedef struct grub_efi_dhcp6_config_data grub_efi_dhcp6_config_data_t; + +struct grub_efi_dhcp6_protocol { + grub_efi_status_t (__grub_efi_api *get_mode_data) (grub_efi_dhcp6_protocol_t *this, + grub_efi_dhcp6_mode_data_t *dhcp6_mode_data, + grub_efi_dhcp6_config_data_t *dhcp6_config_data); + grub_efi_status_t (__grub_efi_api *configure) (grub_efi_dhcp6_protocol_t *this, + grub_efi_dhcp6_config_data_t *dhcp6_cfg_data); + grub_efi_status_t (__grub_efi_api *start) (grub_efi_dhcp6_protocol_t *this); + grub_efi_status_t (__grub_efi_api *info_request) (grub_efi_dhcp6_protocol_t *this, + grub_efi_boolean_t send_client_id, + grub_efi_dhcp6_packet_option_t *option_request, + grub_efi_uint32_t option_count, + grub_efi_dhcp6_packet_option_t *option_list[], + grub_efi_dhcp6_retransmission_t *retransmission, + grub_efi_event_t timeout_event, + grub_efi_status_t (__grub_efi_api *reply_callback) (grub_efi_dhcp6_protocol_t *this, + void *context, + grub_efi_dhcp6_packet_t *packet), + void *callback_context); + grub_efi_status_t (__grub_efi_api *renew_rebind) (grub_efi_dhcp6_protocol_t *this, + grub_efi_boolean_t rebind_request); + grub_efi_status_t (__grub_efi_api *decline) (grub_efi_dhcp6_protocol_t *this, + grub_efi_uint32_t address_count, + grub_efi_ipv6_address_t *addresses); + grub_efi_status_t (__grub_efi_api *release) (grub_efi_dhcp6_protocol_t *this, + grub_efi_uint32_t address_count, + grub_efi_ipv6_address_t *addresses); + grub_efi_status_t (__grub_efi_api *stop) (grub_efi_dhcp6_protocol_t *this); + grub_efi_status_t (__grub_efi_api *parse) (grub_efi_dhcp6_protocol_t *this, + grub_efi_dhcp6_packet_t *packet, + grub_efi_uint32_t *option_count, + grub_efi_dhcp6_packet_option_t *packet_option_list[]); +}; + +#endif /* ! GRUB_EFI_DHCP_HEADER */ diff --git a/include/grub/efi/disk.h b/include/grub/efi/disk.h index 254475c842..6845c2f1fd 100644 --- a/include/grub/efi/disk.h +++ b/include/grub/efi/disk.h @@ -27,6 +27,8 @@ grub_efi_handle_t EXPORT_FUNC(grub_efidisk_get_device_handle) (grub_disk_t disk); char *EXPORT_FUNC(grub_efidisk_get_device_name) (grub_efi_handle_t *handle); +void EXPORT_FUNC(grub_efidisk_reenumerate_disks) (void); + void grub_efidisk_init (void); void grub_efidisk_fini (void); diff --git a/include/grub/efi/edid.h b/include/grub/efi/edid.h index 43c0c4372f..ac1b8e2fda 100644 --- a/include/grub/efi/edid.h +++ b/include/grub/efi/edid.h @@ -33,7 +33,7 @@ struct grub_efi_edid_override; typedef grub_efi_status_t -(*grub_efi_edid_override_get_edid) (struct grub_efi_edid_override *this, +(__grub_efi_api *grub_efi_edid_override_get_edid) (struct grub_efi_edid_override *this, grub_efi_handle_t *childhandle, grub_efi_uint32_t *attributes, grub_efi_uintn_t *edidsize, diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index a5cd99e5af..48a757789a 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -20,6 +20,7 @@ #ifndef GRUB_EFI_EFI_HEADER #define GRUB_EFI_EFI_HEADER 1 +#include #include #include #include @@ -36,6 +37,37 @@ struct linux_arch_kernel_header { struct grub_pe_image_header pe_image_header; }; +struct grub_arm_linux_pe_header +{ + grub_uint32_t magic; + struct grub_pe32_coff_header coff; + struct grub_pe32_optional_header opt; +}; + +struct grub_arm64_linux_pe_header +{ + grub_uint32_t magic; + struct grub_pe32_coff_header coff; + struct grub_pe64_optional_header opt; +}; + +#if defined(__arm__) +# define grub_armxx_linux_pe_header grub_arm_linux_pe_header +#endif + +#if defined(__aarch64__) +# define grub_armxx_linux_pe_header grub_arm64_linux_pe_header +#endif + +#define GRUB_EFI_GRUB_VARIABLE_GUID \ + { 0x91376aff, 0xcba6, 0x42be, \ + { 0x94, 0x9d, 0x06, 0xfd, 0xe8, 0x11, 0x28, 0xe8 } \ + } + +/* Variables. */ +extern grub_efi_system_table_t *EXPORT_VAR(grub_efi_system_table); +extern grub_efi_handle_t EXPORT_VAR(grub_efi_image_handle); + /* Functions. */ void *EXPORT_FUNC(grub_efi_locate_protocol) (grub_guid_t *protocol, void *registration); @@ -44,6 +76,11 @@ EXPORT_FUNC(grub_efi_locate_handle) (grub_efi_locate_search_type_t search_type, grub_guid_t *protocol, void *search_key, grub_efi_uintn_t *num_handles); +grub_efi_status_t +EXPORT_FUNC(grub_efi_connect_controller) (grub_efi_handle_t controller_handle, + grub_efi_handle_t *driver_image_handle, + grub_efi_device_path_protocol_t *remaining_device_path, + grub_efi_boolean_t recursive); void *EXPORT_FUNC(grub_efi_open_protocol) (grub_efi_handle_t handle, grub_guid_t *protocol, grub_efi_uint32_t attributes); @@ -61,6 +98,9 @@ EXPORT_FUNC(grub_efi_allocate_fixed) (grub_efi_physical_address_t address, grub_efi_uintn_t pages); void * EXPORT_FUNC(grub_efi_allocate_any_pages) (grub_efi_uintn_t pages); +void * +EXPORT_FUNC(grub_efi_allocate_pages_max) (grub_efi_physical_address_t max, + grub_efi_uintn_t pages); void EXPORT_FUNC(grub_efi_free_pages) (grub_efi_physical_address_t address, grub_efi_uintn_t pages); grub_efi_uintn_t EXPORT_FUNC(grub_efi_find_mmap_size) (void); @@ -71,6 +111,33 @@ EXPORT_FUNC(grub_efi_get_memory_map) (grub_efi_uintn_t *memory_map_size, grub_efi_uintn_t *descriptor_size, grub_efi_uint32_t *descriptor_version); void grub_efi_memory_fini (void); + +static inline grub_efi_status_t +__attribute__((__unused__)) +grub_efi_allocate_pool (grub_efi_memory_type_t pool_type, + grub_efi_uintn_t buffer_size, + void **buffer) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + + b = grub_efi_system_table->boot_services; + status = b->allocate_pool(pool_type, buffer_size, buffer); + return status; +} + +static inline grub_efi_status_t +__attribute__((__unused__)) +grub_efi_free_pool (void *buffer) +{ + grub_efi_boot_services_t *b; + grub_efi_status_t status; + + b = grub_efi_system_table->boot_services; + status = b->free_pool(buffer); + return status; +} + grub_efi_loaded_image_t *EXPORT_FUNC(grub_efi_get_loaded_image) (grub_efi_handle_t image_handle); void EXPORT_FUNC(grub_efi_print_device_path) (grub_efi_device_path_t *dp); char *EXPORT_FUNC(grub_efi_get_filename) (grub_efi_device_path_t *dp); @@ -122,6 +189,9 @@ extern void (*EXPORT_VAR(grub_efi_net_config)) (grub_efi_handle_t hnd, void * EXPORT_FUNC (grub_efi_find_configuration_table) (const grub_guid_t *target_guid); +extern grub_addr_t EXPORT_VAR(grub_stack_addr); +extern grub_size_t EXPORT_VAR(grub_stack_size); + #if defined(__arm__) || defined(__aarch64__) || defined(__riscv) || defined(__loongarch__) void *EXPORT_FUNC(grub_efi_get_firmware_fdt)(void); grub_err_t EXPORT_FUNC(grub_efi_get_ram_base)(grub_addr_t *); @@ -130,7 +200,44 @@ grub_err_t EXPORT_FUNC(grub_efi_get_ram_base)(grub_addr_t *); grub_err_t grub_arch_efi_linux_load_image_header(grub_file_t file, struct linux_arch_kernel_header *lh); grub_err_t grub_arch_efi_linux_boot_image(grub_addr_t addr, grub_size_t size, - char *args); + char *args); +grub_efi_status_t +EXPORT_FUNC (grub_efi_load_image) (grub_efi_boolean_t boot_policy, + grub_efi_handle_t parent_image_handle, + grub_efi_device_path_t *file_path, + void *source_buffer, grub_efi_uintn_t source_size, + grub_efi_handle_t *image_handle); + +grub_efi_status_t +EXPORT_FUNC (grub_efi_start_image) (grub_efi_handle_t image_handle, + grub_efi_uintn_t *exit_data_size, + grub_efi_char16_t **exit_data); + +grub_efi_status_t +EXPORT_FUNC (grub_efi_unload_image) (grub_efi_handle_t image_handle); + +typedef struct grub_efi_loader +{ + grub_efi_status_t (__grub_efi_api *load_image) (grub_efi_boolean_t boot_policy, + grub_efi_handle_t parent_image_handle, + grub_efi_device_path_t *file_path, + void *source_buffer, + grub_efi_uintn_t source_size, + grub_efi_handle_t *image_handle); + + grub_efi_status_t (__grub_efi_api *start_image) (grub_efi_handle_t image_handle, + grub_efi_uintn_t *exit_data_size, + grub_efi_char16_t **exit_data); + + grub_efi_status_t (__grub_efi_api *unload_image) (grub_efi_handle_t image_handle); +} grub_efi_loader_t; + +grub_err_t +EXPORT_FUNC (grub_efi_register_loader) (const grub_efi_loader_t *loader); + +grub_err_t +EXPORT_FUNC (grub_efi_unregister_loader) (const grub_efi_loader_t *loader); + grub_addr_t grub_efi_section_addr (const char *section); @@ -140,10 +247,7 @@ void grub_efi_init (void); void grub_efi_fini (void); void grub_efi_set_prefix (void); -/* Variables. */ -extern grub_efi_system_table_t *EXPORT_VAR(grub_efi_system_table); -extern grub_efi_handle_t EXPORT_VAR(grub_efi_image_handle); - +/* More variables. */ extern int EXPORT_VAR(grub_efi_is_finished); struct grub_net_card; @@ -151,4 +255,6 @@ struct grub_net_card; grub_efi_handle_t grub_efinet_get_device_handle (struct grub_net_card *card); +grub_err_t EXPORT_FUNC(grub_efi_status_to_err) (grub_efi_status_t status); + #endif /* ! GRUB_EFI_EFI_HEADER */ diff --git a/include/grub/efi/http.h b/include/grub/efi/http.h new file mode 100644 index 0000000000..242d001b62 --- /dev/null +++ b/include/grub/efi/http.h @@ -0,0 +1,215 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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_EFI_HTTP_HEADER +#define GRUB_EFI_HTTP_HEADER 1 + +#include +#include +#include + +#define GRUB_EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID \ + { 0xbdc8e6af, 0xd9bc, 0x4379, \ + { 0xa7, 0x2a, 0xe0, 0xc4, 0xe7, 0x5d, 0xae, 0x1c } \ + } + +#define GRUB_EFI_HTTP_PROTOCOL_GUID \ + { 0x7A59B29B, 0x910B, 0x4171, \ + { 0x82, 0x42, 0xA8, 0x5A, 0x0D, 0xF2, 0x5B, 0x5B } \ + } + +#define EFIHTTP_WAIT_TIME 10000 // 10000ms = 10s +#define EFIHTTP_RX_BUF_LEN 10240 + +//****************************************** +// Protocol Interface Structure +//****************************************** +struct grub_efi_http; + +//****************************************** +// EFI_HTTP_VERSION +//****************************************** +typedef enum { + GRUB_EFI_HTTPVERSION10, + GRUB_EFI_HTTPVERSION11, + GRUB_EFI_HTTPVERSIONUNSUPPORTED +} grub_efi_http_version_t; + +//****************************************** +// EFI_HTTPv4_ACCESS_POINT +//****************************************** +typedef struct { + grub_efi_boolean_t use_default_address; + grub_efi_ipv4_address_t local_address; + grub_efi_ipv4_address_t local_subnet; + grub_efi_uint16_t local_port; +} grub_efi_httpv4_access_point_t; + +//****************************************** +// EFI_HTTPv6_ACCESS_POINT +//****************************************** +typedef struct { + grub_efi_ipv6_address_t local_address; + grub_efi_uint16_t local_port; +} grub_efi_httpv6_access_point_t; + +//****************************************** +// EFI_HTTP_CONFIG_DATA +//****************************************** +typedef struct { + grub_efi_http_version_t http_version; + grub_efi_uint32_t timeout_millisec; + grub_efi_boolean_t local_address_is_ipv6; + union { + grub_efi_httpv4_access_point_t *ipv4_node; + grub_efi_httpv6_access_point_t *ipv6_node; + } access_point; +} grub_efi_http_config_data_t; + +//****************************************** +// EFI_HTTP_METHOD +//****************************************** +typedef enum { + GRUB_EFI_HTTPMETHODGET, + GRUB_EFI_HTTPMETHODPOST, + GRUB_EFI_HTTPMETHODPATCH, + GRUB_EFI_HTTPMETHODOPTIONS, + GRUB_EFI_HTTPMETHODCONNECT, + GRUB_EFI_HTTPMETHODHEAD, + GRUB_EFI_HTTPMETHODPUT, + GRUB_EFI_HTTPMETHODDELETE, + GRUB_EFI_HTTPMETHODTRACE, +} grub_efi_http_method_t; + +//****************************************** +// EFI_HTTP_REQUEST_DATA +//****************************************** +typedef struct { + grub_efi_http_method_t method; + grub_efi_char16_t *url; +} grub_efi_http_request_data_t; + +typedef enum { + GRUB_EFI_HTTP_STATUS_UNSUPPORTED_STATUS = 0, + GRUB_EFI_HTTP_STATUS_100_CONTINUE, + GRUB_EFI_HTTP_STATUS_101_SWITCHING_PROTOCOLS, + GRUB_EFI_HTTP_STATUS_200_OK, + GRUB_EFI_HTTP_STATUS_201_CREATED, + GRUB_EFI_HTTP_STATUS_202_ACCEPTED, + GRUB_EFI_HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION, + GRUB_EFI_HTTP_STATUS_204_NO_CONTENT, + GRUB_EFI_HTTP_STATUS_205_RESET_CONTENT, + GRUB_EFI_HTTP_STATUS_206_PARTIAL_CONTENT, + GRUB_EFI_HTTP_STATUS_300_MULTIPLE_CHIOCES, + GRUB_EFI_HTTP_STATUS_301_MOVED_PERMANENTLY, + GRUB_EFI_HTTP_STATUS_302_FOUND, + GRUB_EFI_HTTP_STATUS_303_SEE_OTHER, + GRUB_EFI_HTTP_STATUS_304_NOT_MODIFIED, + GRUB_EFI_HTTP_STATUS_305_USE_PROXY, + GRUB_EFI_HTTP_STATUS_307_TEMPORARY_REDIRECT, + GRUB_EFI_HTTP_STATUS_400_BAD_REQUEST, + GRUB_EFI_HTTP_STATUS_401_UNAUTHORIZED, + GRUB_EFI_HTTP_STATUS_402_PAYMENT_REQUIRED, + GRUB_EFI_HTTP_STATUS_403_FORBIDDEN, + GRUB_EFI_HTTP_STATUS_404_NOT_FOUND, + GRUB_EFI_HTTP_STATUS_405_METHOD_NOT_ALLOWED, + GRUB_EFI_HTTP_STATUS_406_NOT_ACCEPTABLE, + GRUB_EFI_HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED, + GRUB_EFI_HTTP_STATUS_408_REQUEST_TIME_OUT, + GRUB_EFI_HTTP_STATUS_409_CONFLICT, + GRUB_EFI_HTTP_STATUS_410_GONE, + GRUB_EFI_HTTP_STATUS_411_LENGTH_REQUIRED, + GRUB_EFI_HTTP_STATUS_412_PRECONDITION_FAILED, + GRUB_EFI_HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE, + GRUB_EFI_HTTP_STATUS_414_REQUEST_URI_TOO_LARGE, + GRUB_EFI_HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE, + GRUB_EFI_HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED, + GRUB_EFI_HTTP_STATUS_417_EXPECTATION_FAILED, + GRUB_EFI_HTTP_STATUS_500_INTERNAL_SERVER_ERROR, + GRUB_EFI_HTTP_STATUS_501_NOT_IMPLEMENTED, + GRUB_EFI_HTTP_STATUS_502_BAD_GATEWAY, + GRUB_EFI_HTTP_STATUS_503_SERVICE_UNAVAILABLE, + GRUB_EFI_HTTP_STATUS_504_GATEWAY_TIME_OUT, + GRUB_EFI_HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED +} grub_efi_http_status_code_t; + +//****************************************** +// EFI_HTTP_RESPONSE_DATA +//****************************************** +typedef struct { + grub_efi_http_status_code_t status_code; +} grub_efi_http_response_data_t; + +//****************************************** +// EFI_HTTP_HEADER +//****************************************** +typedef struct { + grub_efi_char8_t *field_name; + grub_efi_char8_t *field_value; +} grub_efi_http_header_t; + +//****************************************** +// EFI_HTTP_MESSAGE +//****************************************** +typedef struct { + union { + grub_efi_http_request_data_t *request; + grub_efi_http_response_data_t *response; + } data; + grub_efi_uintn_t header_count; + grub_efi_http_header_t *headers; + grub_efi_uintn_t body_length; + void *body; +} grub_efi_http_message_t; + +//****************************************** +// EFI_HTTP_TOKEN +//****************************************** +typedef struct { + grub_efi_event_t event; + grub_efi_status_t status; + grub_efi_http_message_t *message; +} grub_efi_http_token_t; + +struct grub_efi_http { + grub_efi_status_t + (__grub_efi_api *get_mode_data) (struct grub_efi_http *this, + grub_efi_http_config_data_t *http_config_data); + + grub_efi_status_t + (__grub_efi_api *configure) (struct grub_efi_http *this, + grub_efi_http_config_data_t *http_config_data); + + grub_efi_status_t + (__grub_efi_api *request) (struct grub_efi_http *this, + grub_efi_http_token_t *token); + + grub_efi_status_t + (__grub_efi_api *cancel) (struct grub_efi_http *this, + grub_efi_http_token_t *token); + + grub_efi_status_t + (__grub_efi_api *response) (struct grub_efi_http *this, + grub_efi_http_token_t *token); + + grub_efi_status_t + (__grub_efi_api *poll) (struct grub_efi_http *this); +}; +typedef struct grub_efi_http grub_efi_http_t; + +#endif /* !GRUB_EFI_HTTP_HEADER */ diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h new file mode 100644 index 0000000000..cd17be506a --- /dev/null +++ b/include/grub/efi/linux.h @@ -0,0 +1,47 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2014 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_EFI_LINUX_HEADER +#define GRUB_EFI_LINUX_HEADER 1 + +#include +#include +#include + +#define GRUB_MOK_POLICY_NX_REQUIRED 0x1 + +grub_err_t +EXPORT_FUNC(grub_efi_linux_boot) (grub_addr_t kernel_address, + grub_size_t kernel_size, + grub_size_t kernel_start, + grub_off_t handover_offset, + void *kernel_param, int nx_enabled); + +grub_err_t +EXPORT_FUNC(grub_efi_check_nx_image_support) (grub_addr_t kernel_addr, + grub_size_t kernel_size, + int *nx_supported); + +grub_err_t +EXPORT_FUNC(grub_efi_check_nx_required) (int *nx_required); + +grub_err_t +EXPORT_FUNC(grub_efi_mem_set_att) (grub_addr_t k_address, + grub_size_t k_size, + grub_size_t k_start, int nx_supported); + +#endif /* ! GRUB_EFI_LINUX_HEADER */ diff --git a/include/grub/efi/pci.h b/include/grub/efi/pci.h index b17245549f..a90f610422 100644 --- a/include/grub/efi/pci.h +++ b/include/grub/efi/pci.h @@ -48,7 +48,7 @@ typedef enum struct grub_efi_pci_io; typedef grub_efi_status_t -(*grub_efi_pci_io_mem_t) (struct grub_efi_pci_io *this, +(__grub_efi_api *grub_efi_pci_io_mem_t) (struct grub_efi_pci_io *this, grub_efi_pci_io_width_t width, grub_efi_uint8_t bar_index, grub_efi_uint64_t offset, @@ -56,7 +56,7 @@ typedef grub_efi_status_t void *buffer); typedef grub_efi_status_t -(*grub_efi_pci_io_config_t) (struct grub_efi_pci_io *this, +(__grub_efi_api *grub_efi_pci_io_config_t) (struct grub_efi_pci_io *this, grub_efi_pci_io_width_t width, grub_efi_uint32_t offset, grub_efi_uintn_t count, @@ -113,7 +113,7 @@ typedef enum grub_efi_pci_io_attribute_operation_t; typedef grub_efi_status_t -(*grub_efi_pci_io_poll_io_mem_t) (struct grub_efi_pci_io *this, +(__grub_efi_api *grub_efi_pci_io_poll_io_mem_t) (struct grub_efi_pci_io *this, grub_efi_pci_io_width_t width, grub_efi_uint8_t bar_ndex, grub_efi_uint64_t offset, @@ -123,7 +123,7 @@ typedef grub_efi_status_t grub_efi_uint64_t *result); typedef grub_efi_status_t -(*grub_efi_pci_io_copy_mem_t) (struct grub_efi_pci_io *this, +(__grub_efi_api *grub_efi_pci_io_copy_mem_t) (struct grub_efi_pci_io *this, grub_efi_pci_io_width_t width, grub_efi_uint8_t dest_bar_index, grub_efi_uint64_t dest_offset, @@ -132,7 +132,7 @@ typedef grub_efi_status_t grub_efi_uintn_t count); typedef grub_efi_status_t -(*grub_efi_pci_io_map_t) (struct grub_efi_pci_io *this, +(__grub_efi_api *grub_efi_pci_io_map_t) (struct grub_efi_pci_io *this, grub_efi_pci_io_operation_t operation, void *host_address, grub_efi_uintn_t *number_of_bytes, @@ -140,11 +140,11 @@ typedef grub_efi_status_t void **mapping); typedef grub_efi_status_t -(*grub_efi_pci_io_unmap_t) (struct grub_efi_pci_io *this, +(__grub_efi_api *grub_efi_pci_io_unmap_t) (struct grub_efi_pci_io *this, void *mapping); typedef grub_efi_status_t -(*grub_efi_pci_io_allocate_buffer_t) (struct grub_efi_pci_io *this, +(__grub_efi_api *grub_efi_pci_io_allocate_buffer_t) (struct grub_efi_pci_io *this, grub_efi_allocate_type_t type, grub_efi_memory_type_t memory_type, grub_efi_uintn_t pages, @@ -152,7 +152,7 @@ typedef grub_efi_status_t grub_efi_uint64_t attributes); typedef grub_efi_status_t -(*grub_efi_pci_io_free_buffer_t) (struct grub_efi_pci_io *this, +(__grub_efi_api *grub_efi_pci_io_free_buffer_t) (struct grub_efi_pci_io *this, grub_efi_allocate_type_t type, grub_efi_memory_type_t memory_type, grub_efi_uintn_t pages, @@ -160,29 +160,29 @@ typedef grub_efi_status_t grub_efi_uint64_t attributes); typedef grub_efi_status_t -(*grub_efi_pci_io_flush_t) (struct grub_efi_pci_io *this); +(__grub_efi_api *grub_efi_pci_io_flush_t) (struct grub_efi_pci_io *this); typedef grub_efi_status_t -(*grub_efi_pci_io_get_location_t) (struct grub_efi_pci_io *this, +(__grub_efi_api *grub_efi_pci_io_get_location_t) (struct grub_efi_pci_io *this, grub_efi_uintn_t *segment_number, grub_efi_uintn_t *bus_number, grub_efi_uintn_t *device_number, grub_efi_uintn_t *function_number); typedef grub_efi_status_t -(*grub_efi_pci_io_attributes_t) (struct grub_efi_pci_io *this, +(__grub_efi_api *grub_efi_pci_io_attributes_t) (struct grub_efi_pci_io *this, grub_efi_pci_io_attribute_operation_t operation, grub_efi_uint64_t attributes, grub_efi_uint64_t *result); typedef grub_efi_status_t -(*grub_efi_pci_io_get_bar_attributes_t) (struct grub_efi_pci_io *this, +(__grub_efi_api *grub_efi_pci_io_get_bar_attributes_t) (struct grub_efi_pci_io *this, grub_efi_uint8_t bar_index, grub_efi_uint64_t *supports, void **resources); typedef grub_efi_status_t -(*grub_efi_pci_io_set_bar_attributes_t) (struct grub_efi_pci_io *this, +(__grub_efi_api *grub_efi_pci_io_set_bar_attributes_t) (struct grub_efi_pci_io *this, grub_efi_uint64_t attributes, grub_efi_uint8_t bar_index, grub_efi_uint64_t *offset, @@ -212,12 +212,12 @@ struct grub_efi_pci_root_io; typedef struct { - grub_efi_status_t(*read) (struct grub_efi_pci_root_io *this, + grub_efi_status_t(__grub_efi_api *read) (struct grub_efi_pci_root_io *this, grub_efi_pci_io_width_t width, grub_efi_uint64_t address, grub_efi_uintn_t count, void *buffer); - grub_efi_status_t(*write) (struct grub_efi_pci_root_io *this, + grub_efi_status_t(__grub_efi_api *write) (struct grub_efi_pci_root_io *this, grub_efi_pci_io_width_t width, grub_efi_uint64_t address, grub_efi_uintn_t count, @@ -237,7 +237,7 @@ typedef enum grub_efi_pci_root_io_operation_t; typedef grub_efi_status_t -(*grub_efi_pci_root_io_poll_io_mem_t) (struct grub_efi_pci_root_io *this, +(__grub_efi_api *grub_efi_pci_root_io_poll_io_mem_t) (struct grub_efi_pci_root_io *this, grub_efi_pci_io_width_t width, grub_efi_uint64_t address, grub_efi_uint64_t mask, @@ -246,7 +246,7 @@ typedef grub_efi_status_t grub_efi_uint64_t *result); typedef grub_efi_status_t -(*grub_efi_pci_root_io_copy_mem_t) (struct grub_efi_pci_root_io *this, +(__grub_efi_api *grub_efi_pci_root_io_copy_mem_t) (struct grub_efi_pci_root_io *this, grub_efi_pci_io_width_t width, grub_efi_uint64_t dest_offset, grub_efi_uint64_t src_offset, @@ -254,7 +254,7 @@ typedef grub_efi_status_t typedef grub_efi_status_t -(*grub_efi_pci_root_io_map_t) (struct grub_efi_pci_root_io *this, +(__grub_efi_api *grub_efi_pci_root_io_map_t) (struct grub_efi_pci_root_io *this, grub_efi_pci_root_io_operation_t operation, void *host_address, grub_efi_uintn_t *number_of_bytes, @@ -262,11 +262,11 @@ typedef grub_efi_status_t void **mapping); typedef grub_efi_status_t -(*grub_efi_pci_root_io_unmap_t) (struct grub_efi_pci_root_io *this, +(__grub_efi_api *grub_efi_pci_root_io_unmap_t) (struct grub_efi_pci_root_io *this, void *mapping); typedef grub_efi_status_t -(*grub_efi_pci_root_io_allocate_buffer_t) (struct grub_efi_pci_root_io *this, +(__grub_efi_api *grub_efi_pci_root_io_allocate_buffer_t) (struct grub_efi_pci_root_io *this, grub_efi_allocate_type_t type, grub_efi_memory_type_t memory_type, grub_efi_uintn_t pages, @@ -274,26 +274,26 @@ typedef grub_efi_status_t grub_efi_uint64_t attributes); typedef grub_efi_status_t -(*grub_efi_pci_root_io_free_buffer_t) (struct grub_efi_pci_root_io *this, +(__grub_efi_api *grub_efi_pci_root_io_free_buffer_t) (struct grub_efi_pci_root_io *this, grub_efi_uintn_t pages, void **host_address); typedef grub_efi_status_t -(*grub_efi_pci_root_io_flush_t) (struct grub_efi_pci_root_io *this); +(__grub_efi_api *grub_efi_pci_root_io_flush_t) (struct grub_efi_pci_root_io *this); typedef grub_efi_status_t -(*grub_efi_pci_root_io_get_attributes_t) (struct grub_efi_pci_root_io *this, +(__grub_efi_api *grub_efi_pci_root_io_get_attributes_t) (struct grub_efi_pci_root_io *this, grub_efi_uint64_t *supports, void **resources); typedef grub_efi_status_t -(*grub_efi_pci_root_io_set_attributes_t) (struct grub_efi_pci_root_io *this, +(__grub_efi_api *grub_efi_pci_root_io_set_attributes_t) (struct grub_efi_pci_root_io *this, grub_efi_uint64_t attributes, grub_efi_uint64_t *offset, grub_efi_uint64_t *length); typedef grub_efi_status_t -(*grub_efi_pci_root_io_configuration_t) (struct grub_efi_pci_root_io *this, +(__grub_efi_api *grub_efi_pci_root_io_configuration_t) (struct grub_efi_pci_root_io *this, void **resources); struct grub_efi_pci_root_io { diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h index 4e6e9d254b..a4d62373cc 100644 --- a/include/grub/efi/pe32.h +++ b/include/grub/efi/pe32.h @@ -46,7 +46,30 @@ #define GRUB_PE32_MSDOS_STUB_SIZE 0x80 -#define GRUB_PE32_MAGIC 0x5a4d +#define GRUB_DOS_MAGIC 0x5a4d + +struct grub_dos_header +{ + grub_uint16_t magic; + grub_uint16_t cblp; + grub_uint16_t cp; + grub_uint16_t crlc; + grub_uint16_t cparhdr; + grub_uint16_t minalloc; + grub_uint16_t maxalloc; + grub_uint16_t ss; + grub_uint16_t sp; + grub_uint16_t csum; + grub_uint16_t ip; + grub_uint16_t cs; + grub_uint16_t lfarlc; + grub_uint16_t ovno; + grub_uint16_t res0[4]; + grub_uint16_t oemid; + grub_uint16_t oeminfo; + grub_uint16_t res1[10]; + grub_uint32_t lfanew; +}; struct grub_msdos_image_header { @@ -171,6 +194,8 @@ struct grub_pe32_optional_header struct grub_pe32_data_directory reserved_entry; }; +#define GRUB_PE32_NX_COMPAT 0x0100 + struct grub_pe64_optional_header { grub_uint16_t magic; @@ -231,12 +256,18 @@ struct grub_pe64_optional_header #define GRUB_PE32_SUBSYSTEM_EFI_APPLICATION 10 +#define GRUB_PE32_NX_COMPAT 0x0100 + #define GRUB_PE32_NUM_DATA_DIRECTORIES 16 struct grub_pe32_section_table { char name[8]; - grub_uint32_t virtual_size; + union + { + grub_uint32_t physical_address; + grub_uint32_t virtual_size; + }; grub_uint32_t virtual_address; grub_uint32_t raw_data_size; grub_uint32_t raw_data_offset; @@ -247,12 +278,18 @@ struct grub_pe32_section_table grub_uint32_t characteristics; }; +#define GRUB_PE32_SCN_TYPE_NO_PAD 0x00000008 #define GRUB_PE32_SCN_CNT_CODE 0x00000020 #define GRUB_PE32_SCN_CNT_INITIALIZED_DATA 0x00000040 -#define GRUB_PE32_SCN_MEM_DISCARDABLE 0x02000000 -#define GRUB_PE32_SCN_MEM_EXECUTE 0x20000000 -#define GRUB_PE32_SCN_MEM_READ 0x40000000 -#define GRUB_PE32_SCN_MEM_WRITE 0x80000000 +#define GRUB_PE32_SCN_CNT_UNINITIALIZED_DATA 0x00000080 +#define GRUB_PE32_SCN_LNK_OTHER 0x00000100 +#define GRUB_PE32_SCN_LNK_INFO 0x00000200 +#define GRUB_PE32_SCN_LNK_REMOVE 0x00000800 +#define GRUB_PE32_SCN_LNK_COMDAT 0x00001000 +#define GRUB_PE32_SCN_GPREL 0x00008000 +#define GRUB_PE32_SCN_MEM_16BIT 0x00020000 +#define GRUB_PE32_SCN_MEM_LOCKED 0x00040000 +#define GRUB_PE32_SCN_MEM_PRELOAD 0x00080000 #define GRUB_PE32_SCN_ALIGN_1BYTES 0x00100000 #define GRUB_PE32_SCN_ALIGN_2BYTES 0x00200000 @@ -261,11 +298,28 @@ struct grub_pe32_section_table #define GRUB_PE32_SCN_ALIGN_16BYTES 0x00500000 #define GRUB_PE32_SCN_ALIGN_32BYTES 0x00600000 #define GRUB_PE32_SCN_ALIGN_64BYTES 0x00700000 +#define GRUB_PE32_SCN_ALIGN_128BYTES 0x00800000 +#define GRUB_PE32_SCN_ALIGN_256BYTES 0x00900000 +#define GRUB_PE32_SCN_ALIGN_512BYTES 0x00A00000 +#define GRUB_PE32_SCN_ALIGN_1024BYTES 0x00B00000 +#define GRUB_PE32_SCN_ALIGN_2048BYTES 0x00C00000 +#define GRUB_PE32_SCN_ALIGN_4096BYTES 0x00D00000 +#define GRUB_PE32_SCN_ALIGN_8192BYTES 0x00E00000 #define GRUB_PE32_SCN_ALIGN_SHIFT 20 #define GRUB_PE32_SCN_ALIGN_MASK 7 -#define GRUB_PE32_SIGNATURE_SIZE 4 +#define GRUB_PE32_SCN_LNK_NRELOC_OVFL 0x01000000 +#define GRUB_PE32_SCN_MEM_DISCARDABLE 0x02000000 +#define GRUB_PE32_SCN_MEM_NOT_CACHED 0x04000000 +#define GRUB_PE32_SCN_MEM_NOT_PAGED 0x08000000 +#define GRUB_PE32_SCN_MEM_SHARED 0x10000000 +#define GRUB_PE32_SCN_MEM_EXECUTE 0x20000000 +#define GRUB_PE32_SCN_MEM_READ 0x40000000 +#define GRUB_PE32_SCN_MEM_WRITE 0x80000000 + +#define GRUB_PE32_SIGNATURE_SIZE 4 +#define GRUB_PE32_SIGNATURE "PE\0\0" #if GRUB_TARGET_SIZEOF_VOID_P == 8 #define GRUB_PE32_NATIVE_MAGIC GRUB_PE32_PE64_MAGIC @@ -290,6 +344,21 @@ struct grub_pe_image_header #endif }; +struct grub_pe32_header_32 +{ + char signature[GRUB_PE32_SIGNATURE_SIZE]; + struct grub_pe32_coff_header coff_header; + struct grub_pe32_optional_header optional_header; +}; + +struct grub_pe32_header_64 +{ + char signature[GRUB_PE32_SIGNATURE_SIZE]; + struct grub_pe32_coff_header coff_header; + struct grub_pe64_optional_header optional_header; +}; + + struct grub_pe32_fixup_block { grub_uint32_t page_rva; diff --git a/include/grub/efiemu/runtime.h b/include/grub/efiemu/runtime.h index 2ff4298454..6363fd522e 100644 --- a/include/grub/efiemu/runtime.h +++ b/include/grub/efiemu/runtime.h @@ -33,5 +33,5 @@ struct efi_variable grub_uint32_t namelen; grub_uint32_t size; grub_efi_uint32_t attributes; -} GRUB_PACKED; +} GRUB_PACKED GRUB_ALIGNED(8); #endif /* ! GRUB_EFI_EMU_RUNTIME_HEADER */ diff --git a/include/grub/emu/config.h b/include/grub/emu/config.h index 875d5896ce..c9a7e5f4ad 100644 --- a/include/grub/emu/config.h +++ b/include/grub/emu/config.h @@ -37,6 +37,7 @@ struct grub_util_config { int is_cryptodisk_enabled; char *grub_distributor; + int is_suse_btrfs_snapshot_enabled; }; void diff --git a/include/grub/emu/exec.h b/include/grub/emu/exec.h index 1b61b4a2e5..e82f13215e 100644 --- a/include/grub/emu/exec.h +++ b/include/grub/emu/exec.h @@ -36,7 +36,7 @@ grub_util_exec_redirect_all (const char *const *argv, const char *stdin_file, int EXPORT_FUNC(grub_util_exec) (const char *const *argv); int -grub_util_exec_redirect (const char *const *argv, const char *stdin_file, +EXPORT_FUNC(grub_util_exec_redirect) (const char *const *argv, const char *stdin_file, const char *stdout_file); int grub_util_exec_redirect_null (const char *const *argv); diff --git a/include/grub/emu/getroot.h b/include/grub/emu/getroot.h index 73fa2d34ab..9c642ae3fe 100644 --- a/include/grub/emu/getroot.h +++ b/include/grub/emu/getroot.h @@ -53,6 +53,11 @@ char ** grub_find_root_devices_from_mountinfo (const char *dir, char **relroot); #endif +#ifdef __linux__ +char * +grub_util_get_btrfs_subvol (const char *path, char **mount_path); +#endif + /* Devmapper functions provided by getroot_devmapper.c. */ void grub_util_pull_devmapper (const char *os_dev); diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h index 01056954b9..59b8038c60 100644 --- a/include/grub/emu/misc.h +++ b/include/grub/emu/misc.h @@ -39,7 +39,7 @@ void grub_fini_all (void); void grub_find_zpool_from_dir (const char *dir, char **poolname, char **poolfs); -char *grub_make_system_path_relative_to_its_root (const char *path) +char *EXPORT_FUNC (grub_make_system_path_relative_to_its_root) (const char *path) WARN_UNUSED_RESULT; int grub_util_device_is_mapped (const char *dev); @@ -59,6 +59,8 @@ void EXPORT_FUNC(grub_util_error) (const char *fmt, ...) __attribute__ ((format void EXPORT_FUNC(grub_util_set_kexecute) (void); int EXPORT_FUNC(grub_util_get_kexecute) (void) WARN_UNUSED_RESULT; +void EXPORT_FUNC(grub_util_set_switch_root) (void); +int EXPORT_FUNC(grub_util_get_switch_root) (void); grub_uint64_t EXPORT_FUNC (grub_util_get_cpu_time_ms) (void); diff --git a/include/grub/err.h b/include/grub/err.h index 1c07034cd6..6379a6baf8 100644 --- a/include/grub/err.h +++ b/include/grub/err.h @@ -73,7 +73,9 @@ typedef enum GRUB_ERR_NET_NO_DOMAIN, GRUB_ERR_EOF, GRUB_ERR_BAD_SIGNATURE, - GRUB_ERR_BAD_FIRMWARE + GRUB_ERR_BAD_FIRMWARE, + GRUB_ERR_STILL_REFERENCED, + GRUB_ERR_RECURSION_DEPTH } grub_err_t; @@ -86,8 +88,12 @@ struct grub_error_saved extern grub_err_t EXPORT_VAR(grub_errno); extern char EXPORT_VAR(grub_errmsg)[GRUB_MAX_ERRMSG]; -grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *fmt, ...) - __attribute__ ((format (GNU_PRINTF, 2, 3))); +grub_err_t EXPORT_FUNC(grub_error) (grub_err_t n, const char *file, const char *function, const int line, const char *fmt, ...) + __attribute__ ((format (GNU_PRINTF, 5, 6))); + +#define grub_error(n, fmt, ...) grub_error (n, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) + + void EXPORT_FUNC(grub_fatal) (const char *fmt, ...) __attribute__ ((noreturn)); void EXPORT_FUNC(grub_error_push) (void); int EXPORT_FUNC(grub_error_pop) (void); diff --git a/include/grub/fdt.h b/include/grub/fdt.h index e609c7e411..ba2f9a95c0 100644 --- a/include/grub/fdt.h +++ b/include/grub/fdt.h @@ -19,6 +19,9 @@ #ifndef GRUB_FDT_HEADER #define GRUB_FDT_HEADER 1 +#if !defined(GRUB_MACHINE_EMU) && \ + (defined(__arm__) || defined(__aarch64__) || defined(__riscv) || defined(__loongarch__)) + #include #include @@ -144,4 +147,7 @@ int EXPORT_FUNC(grub_fdt_set_prop) (void *fdt, unsigned int nodeoffset, const ch grub_fdt_set_prop ((fdt), (nodeoffset), "reg", reg_64, 16); \ }) +#endif /* !defined(GRUB_MACHINE_EMU) && \ + (defined(__arm__) || defined(__aarch64__) || defined(__riscv) || defined(__loongarch__)) */ + #endif /* ! GRUB_FDT_HEADER */ diff --git a/include/grub/file.h b/include/grub/file.h index a5bf3a792d..d678de0632 100644 --- a/include/grub/file.h +++ b/include/grub/file.h @@ -80,6 +80,8 @@ enum grub_file_type GRUB_FILE_TYPE_PUBLIC_KEY, /* File holding public key to add to trused keys. */ GRUB_FILE_TYPE_PUBLIC_KEY_TRUST, + /* File holding x509 certificiate to add to trusted keys. */ + GRUB_FILE_TYPE_CERTIFICATE_TRUST, /* File of which we intend to print a blocklist to the user. */ GRUB_FILE_TYPE_PRINT_BLOCKLIST, /* File we intend to use for test loading or testing speed. */ diff --git a/include/grub/fs.h b/include/grub/fs.h index 026bc3bb86..89e4d2b9bc 100644 --- a/include/grub/fs.h +++ b/include/grub/fs.h @@ -23,6 +23,7 @@ #include #include #include +#include #include /* For embedding types. */ @@ -57,6 +58,9 @@ struct grub_fs /* My name. */ const char *name; + /* My module */ + grub_dl_t mod; + /* Call HOOK with each file under DIR. */ grub_err_t (*fs_dir) (grub_device_t device, const char *path, grub_fs_dir_hook_t hook, void *hook_data); @@ -128,4 +132,6 @@ grub_fs_unregister (grub_fs_t fs) grub_fs_t EXPORT_FUNC(grub_fs_probe) (grub_device_t device); +#define GRUB_ENV_BTRFS_OFFSET (256 * 1024) + #endif /* ! GRUB_FS_HEADER */ diff --git a/include/grub/gcrypt/.gitignore b/include/grub/gcrypt/.gitignore new file mode 100644 index 0000000000..8fbf564624 --- /dev/null +++ b/include/grub/gcrypt/.gitignore @@ -0,0 +1,2 @@ +g10lib.h +gcrypt.h diff --git a/include/grub/i386/efi/console.h b/include/grub/i386/efi/console.h new file mode 100644 index 0000000000..9231375cb0 --- /dev/null +++ b/include/grub/i386/efi/console.h @@ -0,0 +1,24 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2005,2006,2007 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_I386_EFI_CONSOLE_H +#define GRUB_I386_EFI_CONSOLE_H + +#include + +#endif /* ! GRUB_I386_EFI_CONSOLE_H */ diff --git a/include/grub/i386/efi/memory.h b/include/grub/i386/efi/memory.h index 2c64918e3f..a4c2ec8350 100644 --- a/include/grub/i386/efi/memory.h +++ b/include/grub/i386/efi/memory.h @@ -2,5 +2,6 @@ #include #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff +#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS #endif /* ! GRUB_MEMORY_CPU_HEADER */ diff --git a/include/grub/i386/linux.h b/include/grub/i386/linux.h index d4b5508695..461097781f 100644 --- a/include/grub/i386/linux.h +++ b/include/grub/i386/linux.h @@ -148,6 +148,11 @@ struct linux_i386_kernel_header grub_uint32_t kernel_alignment; grub_uint8_t relocatable; grub_uint8_t min_alignment; +#define LINUX_XLF_KERNEL_64 (1<<0) +#define LINUX_XLF_CAN_BE_LOADED_ABOVE_4G (1<<1) +#define LINUX_XLF_EFI_HANDOVER_32 (1<<2) +#define LINUX_XLF_EFI_HANDOVER_64 (1<<3) +#define LINUX_XLF_EFI_KEXEC (1<<4) grub_uint16_t xloadflags; grub_uint32_t cmdline_size; grub_uint32_t hardware_subarch; @@ -241,7 +246,11 @@ struct linux_kernel_params grub_uint32_t ofw_cif_handler; /* b8 */ grub_uint32_t ofw_idt; /* bc */ - grub_uint8_t padding7[0x1b8 - 0xc0]; + grub_uint32_t ext_ramdisk_image; /* 0xc0 */ + grub_uint32_t ext_ramdisk_size; /* 0xc4 */ + grub_uint32_t ext_cmd_line_ptr; /* 0xc8 */ + + grub_uint8_t padding7[0x1b8 - 0xcc]; union { diff --git a/include/grub/ia64/efi/memory.h b/include/grub/ia64/efi/memory.h index 2c64918e3f..a4c2ec8350 100644 --- a/include/grub/ia64/efi/memory.h +++ b/include/grub/ia64/efi/memory.h @@ -2,5 +2,6 @@ #include #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff +#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS #endif /* ! GRUB_MEMORY_CPU_HEADER */ diff --git a/include/grub/ia64/linux.h b/include/grub/ia64/linux.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/include/grub/ieee1275/ieee1275.h b/include/grub/ieee1275/ieee1275.h index dddb385149..db0ec5f4c6 100644 --- a/include/grub/ieee1275/ieee1275.h +++ b/include/grub/ieee1275/ieee1275.h @@ -145,6 +145,8 @@ enum grub_ieee1275_flag GRUB_IEEE1275_FLAG_POWER_VM, GRUB_IEEE1275_FLAG_POWER_KVM, + + GRUB_IEEE1275_FLAG_DISABLE_VIDEO_SUPPORT }; extern int EXPORT_FUNC(grub_ieee1275_test_flag) (enum grub_ieee1275_flag flag); @@ -251,6 +253,8 @@ int EXPORT_FUNC(grub_ieee1275_devalias_next) (struct grub_ieee1275_devalias *ali void EXPORT_FUNC(grub_ieee1275_children_peer) (struct grub_ieee1275_devalias *alias); void EXPORT_FUNC(grub_ieee1275_children_first) (const char *devpath, struct grub_ieee1275_devalias *alias); +int EXPORT_FUNC(grub_ieee1275_cas_reboot) (char *script); +int EXPORT_FUNC(grub_ieee1275_set_boot_last_label) (const char *text); char *EXPORT_FUNC(grub_ieee1275_get_boot_dev) (void); diff --git a/include/grub/ieee1275/ofdisk.h b/include/grub/ieee1275/ofdisk.h index 2f69e3f191..0074d55eee 100644 --- a/include/grub/ieee1275/ofdisk.h +++ b/include/grub/ieee1275/ofdisk.h @@ -22,4 +22,17 @@ extern void grub_ofdisk_init (void); extern void grub_ofdisk_fini (void); +#define MAX_RETRIES 20 + + +#define RETRY_IEEE1275_OFDISK_OPEN(device, last_ihandle) \ + unsigned max_retries = MAX_RETRIES; \ + if(grub_env_get("ofdisk_retries") != NULL) \ + max_retries = grub_strtoul(grub_env_get("ofdisk_retries"), 0, 10)+1; \ + grub_dprintf("ofdisk","MAX_RETRIES set to %u\n",max_retries); \ + unsigned retry_i=0;for(retry_i=0; retry_i < max_retries; retry_i++){ \ + if(!grub_ieee1275_open(device, last_ihandle)) \ + break; \ + grub_dprintf("ofdisk","Opening disk %s failed. Retrying...\n",device); } + #endif /* ! GRUB_INIT_HEADER */ diff --git a/include/grub/kernel.h b/include/grub/kernel.h index abbca5ea33..f98a780da7 100644 --- a/include/grub/kernel.h +++ b/include/grub/kernel.h @@ -30,7 +30,10 @@ enum OBJ_TYPE_PREFIX, OBJ_TYPE_PUBKEY, OBJ_TYPE_DTB, - OBJ_TYPE_DISABLE_SHIM_LOCK + OBJ_TYPE_DISABLE_SHIM_LOCK, + OBJ_TYPE_GPG_PUBKEY, + OBJ_TYPE_X509_PUBKEY, + OBJ_TYPE_DISABLE_CLI }; /* The module header. */ @@ -111,6 +114,11 @@ grub_addr_t grub_modules_get_end (void); #endif +#if !defined(GRUB_MACHINE_EMU) +void EXPORT_FUNC(start) (void); +void EXPORT_FUNC(_start) (void); +#endif + /* The start point of the C code. */ void grub_main (void) __attribute__ ((noreturn)); diff --git a/include/grub/lib/envblk.h b/include/grub/lib/envblk.h index c3e6559217..ab969af246 100644 --- a/include/grub/lib/envblk.h +++ b/include/grub/lib/envblk.h @@ -22,6 +22,8 @@ #define GRUB_ENVBLK_SIGNATURE "# GRUB Environment Block\n" #define GRUB_ENVBLK_DEFCFG "grubenv" +#define DEFAULT_ENVBLK_SIZE 1024 + #ifndef ASM_FILE struct grub_envblk @@ -33,6 +35,7 @@ typedef struct grub_envblk *grub_envblk_t; grub_envblk_t grub_envblk_open (char *buf, grub_size_t size); int grub_envblk_set (grub_envblk_t envblk, const char *name, const char *value); +grub_err_t grub_envblk_get (grub_envblk_t envblk, const char * const name, char ** const value); void grub_envblk_delete (grub_envblk_t envblk, const char *name); void grub_envblk_iterate (grub_envblk_t envblk, void *hook_data, diff --git a/include/grub/libtasn1.h b/include/grub/libtasn1.h new file mode 100644 index 0000000000..28dbf16c4e --- /dev/null +++ b/include/grub/libtasn1.h @@ -0,0 +1,589 @@ +/* + * Copyright (C) 2002-2014 Free Software Foundation, Inc. + * + * This file is part of LIBTASN1. + * + * LIBTASN1 is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * LIBTASN1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with LIBTASN1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * + */ + +/** + * libtasn1:Short_Description: + * + * GNU ASN.1 library + */ +/** + * libtasn1:Long_Description: + * + * The Libtasn1 library provides 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. + */ + + +#ifndef LIBTASN1_H +#define LIBTASN1_H + +/* grub: ASN1_API is not used */ +#define ASN1_API + +/* grub: all our supported compilers support these attributes */ +#define __LIBTASN1_CONST__ __attribute__((const)) +#define __LIBTASN1_PURE__ __attribute__((pure)) + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * ASN1_VERSION: + * + * Version of the library as a string. + */ +#define ASN1_VERSION "4.16.0" + +/** + * ASN1_VERSION_MAJOR: + * + * Major version number of the library. + */ +#define ASN1_VERSION_MAJOR 4 + +/** + * ASN1_VERSION_MINOR: + * + * Minor version number of the library. + */ +#define ASN1_VERSION_MINOR 16 + +/** + * ASN1_VERSION_PATCH: + * + * Patch version number of the library. + */ +#define ASN1_VERSION_PATCH 0 + +/** + * ASN1_VERSION_NUMBER: + * + * Version number of the library as a number. + */ +#define ASN1_VERSION_NUMBER 0x041000 + + +#if defined __GNUC__ && !defined ASN1_INTERNAL_BUILD +# define _ASN1_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +# if _ASN1_GCC_VERSION >= 30100 +# define _ASN1_GCC_ATTR_DEPRECATED __attribute__ ((__deprecated__)) +# endif +#endif + +#ifndef _ASN1_GCC_ATTR_DEPRECATED +#define _ASN1_GCC_ATTR_DEPRECATED +#endif + +/*****************************************/ +/* Errors returned by libtasn1 functions */ +/*****************************************/ +#define ASN1_SUCCESS 0 +#define ASN1_FILE_NOT_FOUND 1 +#define ASN1_ELEMENT_NOT_FOUND 2 +#define ASN1_IDENTIFIER_NOT_FOUND 3 +#define ASN1_DER_ERROR 4 +#define ASN1_VALUE_NOT_FOUND 5 +#define ASN1_GENERIC_ERROR 6 +#define ASN1_VALUE_NOT_VALID 7 +#define ASN1_TAG_ERROR 8 +#define ASN1_TAG_IMPLICIT 9 +#define ASN1_ERROR_TYPE_ANY 10 +#define ASN1_SYNTAX_ERROR 11 +#define ASN1_MEM_ERROR 12 +#define ASN1_MEM_ALLOC_ERROR 13 +#define ASN1_DER_OVERFLOW 14 +#define ASN1_NAME_TOO_LONG 15 +#define ASN1_ARRAY_ERROR 16 +#define ASN1_ELEMENT_NOT_EMPTY 17 +#define ASN1_TIME_ENCODING_ERROR 18 +#define ASN1_RECURSION 19 + +/*************************************/ +/* Constants used in asn1_visit_tree */ +/*************************************/ +#define ASN1_PRINT_NAME 1 +#define ASN1_PRINT_NAME_TYPE 2 +#define ASN1_PRINT_NAME_TYPE_VALUE 3 +#define ASN1_PRINT_ALL 4 + +/*****************************************/ +/* Constants returned by asn1_read_tag */ +/*****************************************/ +#define ASN1_CLASS_UNIVERSAL 0x00 /* old: 1 */ +#define ASN1_CLASS_APPLICATION 0x40 /* old: 2 */ +#define ASN1_CLASS_CONTEXT_SPECIFIC 0x80 /* old: 3 */ +#define ASN1_CLASS_PRIVATE 0xC0 /* old: 4 */ +#define ASN1_CLASS_STRUCTURED 0x20 + +/*****************************************/ +/* Constants returned by asn1_read_tag */ +/*****************************************/ +#define ASN1_TAG_BOOLEAN 0x01 +#define ASN1_TAG_INTEGER 0x02 +#define ASN1_TAG_SEQUENCE 0x10 +#define ASN1_TAG_SET 0x11 +#define ASN1_TAG_OCTET_STRING 0x04 +#define ASN1_TAG_BIT_STRING 0x03 +#define ASN1_TAG_UTCTime 0x17 +#define ASN1_TAG_GENERALIZEDTime 0x18 +#define ASN1_TAG_OBJECT_ID 0x06 +#define ASN1_TAG_ENUMERATED 0x0A +#define ASN1_TAG_NULL 0x05 +#define ASN1_TAG_GENERALSTRING 0x1B +#define ASN1_TAG_NUMERIC_STRING 0x12 +#define ASN1_TAG_IA5_STRING 0x16 +#define ASN1_TAG_TELETEX_STRING 0x14 +#define ASN1_TAG_PRINTABLE_STRING 0x13 +#define ASN1_TAG_UNIVERSAL_STRING 0x1C +#define ASN1_TAG_BMP_STRING 0x1E +#define ASN1_TAG_UTF8_STRING 0x0C +#define ASN1_TAG_VISIBLE_STRING 0x1A + +/** + * asn1_node: + * + * Structure definition used for the node of the tree + * that represents an ASN.1 DEFINITION. + */ +typedef struct asn1_node_st asn1_node_st; + +typedef asn1_node_st *asn1_node; +typedef const asn1_node_st *asn1_node_const; + +/** + * ASN1_MAX_NAME_SIZE: + * + * Maximum number of characters of a name + * inside a file with ASN1 definitions. + */ +#define ASN1_MAX_NAME_SIZE 64 + + +/** + * asn1_static_node: + * @name: Node name + * @type: Node typ + * @value: Node value + * + * For the on-disk format of ASN.1 trees, created by asn1_parser2array(). + */ +struct asn1_static_node_st +{ + const char *name; /* Node name */ + unsigned int type; /* Node type */ + const void *value; /* Node value */ +}; +typedef struct asn1_static_node_st asn1_static_node; + +/* List of constants for field type of node_asn */ +#define ASN1_ETYPE_INVALID 0 +#define ASN1_ETYPE_CONSTANT 1 +#define ASN1_ETYPE_IDENTIFIER 2 +#define ASN1_ETYPE_INTEGER 3 +#define ASN1_ETYPE_BOOLEAN 4 +#define ASN1_ETYPE_SEQUENCE 5 +#define ASN1_ETYPE_BIT_STRING 6 +#define ASN1_ETYPE_OCTET_STRING 7 +#define ASN1_ETYPE_TAG 8 +#define ASN1_ETYPE_DEFAULT 9 +#define ASN1_ETYPE_SIZE 10 +#define ASN1_ETYPE_SEQUENCE_OF 11 +#define ASN1_ETYPE_OBJECT_ID 12 +#define ASN1_ETYPE_ANY 13 +#define ASN1_ETYPE_SET 14 +#define ASN1_ETYPE_SET_OF 15 +#define ASN1_ETYPE_DEFINITIONS 16 +#define ASN1_ETYPE_CHOICE 18 +#define ASN1_ETYPE_IMPORTS 19 +#define ASN1_ETYPE_NULL 20 +#define ASN1_ETYPE_ENUMERATED 21 +#define ASN1_ETYPE_GENERALSTRING 27 +#define ASN1_ETYPE_NUMERIC_STRING 28 +#define ASN1_ETYPE_IA5_STRING 29 +#define ASN1_ETYPE_TELETEX_STRING 30 +#define ASN1_ETYPE_PRINTABLE_STRING 31 +#define ASN1_ETYPE_UNIVERSAL_STRING 32 +#define ASN1_ETYPE_BMP_STRING 33 +#define ASN1_ETYPE_UTF8_STRING 34 +#define ASN1_ETYPE_VISIBLE_STRING 35 +#define ASN1_ETYPE_UTC_TIME 36 +#define ASN1_ETYPE_GENERALIZED_TIME 37 + +/** + * ASN1_DELETE_FLAG_ZEROIZE: + * + * Used by: asn1_delete_structure2() + * + * Zeroize values prior to deinitialization. + */ +#define ASN1_DELETE_FLAG_ZEROIZE 1 + +/** + * ASN1_DECODE_FLAG_ALLOW_PADDING: + * + * Used by: asn1_der_decoding2() + * + * This flag would allow arbitrary data past the DER data. + */ +#define ASN1_DECODE_FLAG_ALLOW_PADDING 1 +/** + * ASN1_DECODE_FLAG_STRICT_DER: + * + * Used by: asn1_der_decoding2() + * + * This flag would ensure that no BER decoding takes place. + */ +#define ASN1_DECODE_FLAG_STRICT_DER (1<<1) +/** + * ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME: + * + * Used by: asn1_der_decoding2() + * + * This flag will tolerate Time encoding errors when in strict DER. + */ +#define ASN1_DECODE_FLAG_ALLOW_INCORRECT_TIME (1<<2) + + +/** + * asn1_data_node_st: + * @name: Node name + * @value: Node value + * @value_len: Node value size + * @type: Node value type (ASN1_ETYPE_*) + * + * Data node inside a #asn1_node structure. + */ +struct asn1_data_node_st +{ + const char *name; /* Node name */ + const void *value; /* Node value */ + unsigned int value_len; /* Node value size */ + unsigned int type; /* Node value type (ASN1_ETYPE_*) */ +}; +typedef struct asn1_data_node_st asn1_data_node_st; + +/***********************************/ +/* Fixed constants */ +/***********************************/ + +/** + * ASN1_MAX_ERROR_DESCRIPTION_SIZE: + * + * Maximum number of characters + * of a description message + * (null character included). + */ +#define ASN1_MAX_ERROR_DESCRIPTION_SIZE 128 + +/***********************************/ +/* Functions definitions */ +/***********************************/ + +/* These functions are not used in grub and should not be referenced. */ +#if 0 +extern ASN1_API int + asn1_parser2tree (const char *file, + asn1_node * definitions, char *error_desc); + +extern ASN1_API int + asn1_parser2array (const char *inputFileName, + const char *outputFileName, + const char *vectorName, char *error_desc); +#endif + +extern ASN1_API int + asn1_array2tree (const asn1_static_node * array, + asn1_node * definitions, char *errorDescription); + +#if 0 +extern ASN1_API void + asn1_print_structure (FILE * out, asn1_node_const structure, + const char *name, int mode); +#endif + +extern ASN1_API int + asn1_create_element (asn1_node_const definitions, + const char *source_name, asn1_node * element); + +extern ASN1_API int asn1_delete_structure (asn1_node * structure); + +extern ASN1_API int asn1_delete_structure2 (asn1_node * structure, unsigned int flags); + +extern ASN1_API int + asn1_delete_element (asn1_node structure, const char *element_name); + +#if 0 +extern ASN1_API int + asn1_write_value (asn1_node node_root, const char *name, + const void *ivalue, int len); +#endif + +extern ASN1_API int + asn1_read_value (asn1_node_const root, const char *name, + void *ivalue, int *len); + +extern ASN1_API int + asn1_read_value_type (asn1_node_const root, const char *name, + void *ivalue, int *len, unsigned int *etype); + +extern ASN1_API int + asn1_read_node_value (asn1_node_const node, asn1_data_node_st * data); + +extern ASN1_API int + asn1_number_of_elements (asn1_node_const element, const char *name, int *num); + +#if 0 +extern ASN1_API int + asn1_der_coding (asn1_node_const element, const char *name, + void *ider, int *len, char *ErrorDescription); +#endif + +extern ASN1_API int + asn1_der_decoding2 (asn1_node *element, const void *ider, + int *max_ider_len, unsigned int flags, + char *errorDescription); + +extern ASN1_API int + asn1_der_decoding (asn1_node * element, const void *ider, + int ider_len, char *errorDescription); + +#if 0 +/* Do not use. Use asn1_der_decoding() instead. */ +extern ASN1_API int + asn1_der_decoding_element (asn1_node * structure, + const char *elementName, + const void *ider, int len, + char *errorDescription) _ASN1_GCC_ATTR_DEPRECATED; +#endif + +extern ASN1_API int + asn1_der_decoding_startEnd (asn1_node element, + const void *ider, int ider_len, + const char *name_element, + int *start, int *end); + +extern ASN1_API int + asn1_expand_any_defined_by (asn1_node_const definitions, asn1_node * element); + +extern ASN1_API int + asn1_expand_octet_string (asn1_node_const definitions, + asn1_node * element, + const char *octetName, const char *objectName); + +extern ASN1_API int + asn1_read_tag (asn1_node_const root, const char *name, + int *tagValue, int *classValue); + +extern ASN1_API const char *asn1_find_structure_from_oid (asn1_node_const + definitions, + const char + *oidValue); + +#if 0 +__LIBTASN1_PURE__ +extern ASN1_API const char *asn1_check_version (const char *req_version); +#endif + +__LIBTASN1_PURE__ +extern ASN1_API const char *asn1_strerror (int error); + +#if 0 +extern ASN1_API void asn1_perror (int error); +#endif + +#define ASN1_MAX_TAG_SIZE 4 +#define ASN1_MAX_LENGTH_SIZE 9 +#define ASN1_MAX_TL_SIZE (ASN1_MAX_TAG_SIZE+ASN1_MAX_LENGTH_SIZE) +extern ASN1_API long + asn1_get_length_der (const unsigned char *der, int der_len, int *len); + +extern ASN1_API long + asn1_get_length_ber (const unsigned char *ber, int ber_len, int *len); + +extern ASN1_API void + asn1_length_der (unsigned long int len, unsigned char *der, int *der_len); + +/* Other utility functions. */ + +extern ASN1_API + int asn1_decode_simple_der (unsigned int etype, const unsigned char *der, + unsigned int _der_len, + const unsigned char **str, + unsigned int *str_len); + +extern ASN1_API + int asn1_decode_simple_ber (unsigned int etype, const unsigned char *der, + unsigned int _der_len, + unsigned char **str, + unsigned int *str_len, + unsigned int *ber_len); + +extern ASN1_API int + asn1_encode_simple_der (unsigned int etype, const unsigned char *str, + unsigned int str_len, unsigned char *tl, + unsigned int *tl_len); + +extern ASN1_API asn1_node + asn1_find_node (asn1_node_const pointer, const char *name); + +extern ASN1_API int + asn1_copy_node (asn1_node dst, const char *dst_name, + asn1_node_const src, const char *src_name); +extern ASN1_API asn1_node + asn1_dup_node (asn1_node_const src, const char *src_name); + +/* Internal and low-level DER utility functions. */ + +extern ASN1_API int + asn1_get_tag_der (const unsigned char *der, int der_len, + unsigned char *cls, int *len, unsigned long *tag); + +extern ASN1_API void + asn1_octet_der (const unsigned char *str, int str_len, + unsigned char *der, int *der_len); + +extern ASN1_API int + asn1_get_octet_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, + int str_size, int *str_len); + +extern ASN1_API void asn1_bit_der (const unsigned char *str, int bit_len, + unsigned char *der, int *der_len); + +extern ASN1_API int + asn1_get_bit_der (const unsigned char *der, int der_len, + int *ret_len, unsigned char *str, + int str_size, int *bit_len); + +extern ASN1_API int + asn1_get_object_id_der (const unsigned char *der, + int der_len, int *ret_len, + char *str, int str_size); + +extern ASN1_API int + asn1_object_id_der (const char *str, unsigned char *der, int *der_len, + unsigned flags); + +/* Compatibility types */ + +/** + * asn1_retCode: + * + * Type formerly returned by libtasn1 functions. + * + * Deprecated: 3.0: Use int instead. + */ +typedef int asn1_retCode; + +/** + * node_asn_struct: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_node instead. + */ +#define node_asn_struct asn1_node_st + +/** + * node_asn: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_node instead. + */ +#define node_asn asn1_node_st + +/** + * ASN1_TYPE: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_node instead. + */ +#define ASN1_TYPE asn1_node + +/** + * ASN1_TYPE_EMPTY: + * + * Compat #define. + * + * Deprecated: 3.0: Use NULL instead. + */ +#define ASN1_TYPE_EMPTY NULL + +/** + * static_struct_asn: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_static_node instead. + */ +#define static_struct_asn asn1_static_node_st + +/** + * ASN1_ARRAY_TYPE: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_static_node instead. + */ +#define ASN1_ARRAY_TYPE asn1_static_node + +/** + * asn1_static_node_t: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_static_node instead. + */ +#define asn1_static_node_t asn1_static_node + +/** + * node_data_struct: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_data_node_st instead. + */ +#define node_data_struct asn1_data_node_st + +/** + * ASN1_DATA_NODE: + * + * Compat #define. + * + * Deprecated: 3.0: Use #asn1_data_node_st instead. + */ +#define ASN1_DATA_NODE asn1_data_node_st + +#ifdef __cplusplus +} +#endif + +#endif /* LIBTASN1_H */ diff --git a/include/grub/lockdown.h b/include/grub/lockdown.h index 40531fa823..ebfee4bf06 100644 --- a/include/grub/lockdown.h +++ b/include/grub/lockdown.h @@ -24,7 +24,8 @@ #define GRUB_LOCKDOWN_DISABLED 0 #define GRUB_LOCKDOWN_ENABLED 1 -#ifdef GRUB_MACHINE_EFI +#if defined(GRUB_MACHINE_EFI) || \ + (defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275)) extern void EXPORT_FUNC (grub_lockdown) (void); extern int diff --git a/include/grub/loongarch64/efi/memory.h b/include/grub/loongarch64/efi/memory.h index d460267be5..792258b2af 100644 --- a/include/grub/loongarch64/efi/memory.h +++ b/include/grub/loongarch64/efi/memory.h @@ -20,5 +20,6 @@ #include #define GRUB_EFI_MAX_USABLE_ADDRESS 0xfffffffffffULL +#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS #endif /* ! GRUB_MEMORY_CPU_HEADER */ diff --git a/include/grub/loopback.h b/include/grub/loopback.h new file mode 100644 index 0000000000..915ef65fce --- /dev/null +++ b/include/grub/loopback.h @@ -0,0 +1,31 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2019 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_LOOPBACK_HEADER +#define GRUB_LOOPBACK_HEADER 1 + +struct grub_loopback +{ + char *devname; + grub_file_t file; + struct grub_loopback *next; + unsigned long id; + grub_uint64_t refcnt; +}; + +#endif /* ! GRUB_LOOPBACK_HEADER */ diff --git a/include/grub/menu.h b/include/grub/menu.h index ee2b5e9104..0e4978dc3b 100644 --- a/include/grub/menu.h +++ b/include/grub/menu.h @@ -20,6 +20,31 @@ #ifndef GRUB_MENU_HEADER #define GRUB_MENU_HEADER 1 + +struct bls_entry +{ + struct bls_entry *next; + struct bls_entry *prev; + struct keyval **keyvals; + int nkeyvals; + char *filename; + int visible; +}; + +struct grub_blsuki_entry +{ + struct grub_blsuki_entry *next; + struct grub_blsuki_entry **prev; + struct keyval **keyvals; + grub_size_t keyvals_size; + int nkeyvals; + char *filename; + char *dirname; + char *devid; + bool visible; +}; +typedef struct grub_blsuki_entry grub_blsuki_entry_t; + struct grub_menu_entry_class { char *name; @@ -60,6 +85,10 @@ struct grub_menu_entry /* The next element. */ struct grub_menu_entry *next; + + /* BLS used to populate the entry */ + struct bls_entry *bls; + grub_blsuki_entry_t *blsuki; }; typedef struct grub_menu_entry *grub_menu_entry_t; diff --git a/include/grub/mips/linux.h b/include/grub/mips/linux.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/include/grub/misc.h b/include/grub/misc.h index 1b35a167fd..4ec42e228a 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -35,7 +35,15 @@ #define ARRAY_SIZE(array) (sizeof (array) / sizeof (array[0])) #define COMPILE_TIME_ASSERT(cond) switch (0) { case 1: case !(cond): ; } -#define grub_dprintf(condition, ...) grub_real_dprintf(GRUB_FILE, __LINE__, condition, __VA_ARGS__) +#ifndef CONCAT_ +#define CONCAT_(a, b) a ## b +#endif + +#ifndef CONCAT +#define CONCAT(a, b) CONCAT_(a, b) +#endif + +#define grub_dprintf(condition, ...) grub_real_dprintf(GRUB_FILE, __FUNCTION__, __LINE__, condition, __VA_ARGS__) void *EXPORT_FUNC(grub_memmove) (void *dest, const void *src, grub_size_t n); char *EXPORT_FUNC(grub_strcpy) (char *dest, const char *src); @@ -64,6 +72,45 @@ grub_stpcpy (char *dest, const char *src) return d - 1; } +static inline grub_size_t +grub_strlcpy (char *dest, const char *src, grub_size_t size) +{ + char *d = dest; + grub_size_t res = 0; + /* + * We do not subtract one from size here to avoid dealing with underflowing + * the value, which is why to_copy is always checked to be greater than one + * throughout this function. + */ + grub_size_t to_copy = size; + + /* Copy size - 1 bytes to dest. */ + if (to_copy > 1) + while ((*d++ = *src++) != '\0' && ++res && --to_copy > 1) + ; + + /* + * NUL terminate if size != 0. The previous step may have copied a NUL byte + * if it reached the end of the string, but we know dest[size - 1] must always + * be a NUL byte. + */ + if (size != 0) + dest[size - 1] = '\0'; + + /* If there is still space in dest, but are here, we reached the end of src. */ + if (to_copy > 1) + return res; + + /* + * If we haven't reached the end of the string, iterate through to determine + * the strings total length. + */ + while (*src++ != '\0' && ++res) + ; + + return res; +} + /* XXX: If grub_memmove is too slow, we must implement grub_memcpy. */ static inline void * grub_memcpy (void *dest, const void *src, grub_size_t n) @@ -87,6 +134,9 @@ char *EXPORT_FUNC(grub_strchr) (const char *s, int c); char *EXPORT_FUNC(grub_strrchr) (const char *s, int c); int EXPORT_FUNC(grub_strword) (const char *s, const char *w); +char *EXPORT_FUNC(grub_strtok_r) (char *s, const char *delim, char **save_ptr); +char *EXPORT_FUNC(grub_strtok) (char *s, const char *delim); + /* Copied from gnulib. Written by Bruno Haible , 2005. */ static inline char * @@ -335,6 +385,7 @@ char *EXPORT_FUNC(grub_strdup) (const char *s) WARN_UNUSED_RESULT; char *EXPORT_FUNC(grub_strndup) (const char *s, grub_size_t n) WARN_UNUSED_RESULT; void *EXPORT_FUNC(grub_memset) (void *s, int c, grub_size_t n); grub_size_t EXPORT_FUNC(grub_strlen) (const char *s) WARN_UNUSED_RESULT; +void EXPORT_FUNC(grub_str_sep) (const char *s, char *p, char delim, char *r); /* Replace all `ch' characters of `input' with `with' and copy the result into `output'; return EOS address of `output'. */ @@ -369,13 +420,17 @@ grub_puts (const char *s) } int EXPORT_FUNC(grub_puts_) (const char *s); +int EXPORT_FUNC(grub_debug_is_enabled) (void); int EXPORT_FUNC(grub_debug_enabled) (const char *condition); void EXPORT_FUNC(grub_real_dprintf) (const char *file, + const char *function, const int line, const char *condition, - const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 4, 5))); + const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 5, 6))); int EXPORT_FUNC(grub_printf) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2))); int EXPORT_FUNC(grub_printf_) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2))); +void EXPORT_FUNC(grub_qdprintf) (const char *condition, + const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 2, 3))); int EXPORT_FUNC(grub_vprintf) (const char *fmt, va_list args); int EXPORT_FUNC(grub_snprintf) (char *str, grub_size_t n, const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 3, 4))); @@ -385,12 +440,16 @@ char *EXPORT_FUNC(grub_xasprintf) (const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 1, 2))) WARN_UNUSED_RESULT; char *EXPORT_FUNC(grub_xvasprintf) (const char *fmt, va_list args) WARN_UNUSED_RESULT; -void EXPORT_FUNC(grub_exit) (void) __attribute__ ((noreturn)); +void EXPORT_FUNC(grub_exit) (int rc) __attribute__ ((noreturn)); void EXPORT_FUNC(grub_abort) (void) __attribute__ ((noreturn)); grub_uint64_t EXPORT_FUNC(grub_divmod64) (grub_uint64_t n, grub_uint64_t d, grub_uint64_t *r); +extern bool EXPORT_FUNC(grub_is_cli_disabled) (void); +extern bool EXPORT_FUNC(grub_is_cli_need_auth) (void); +extern void EXPORT_FUNC(grub_cli_set_auth_needed) (void); + /* Must match softdiv group in gentpl.py. */ #if !defined(GRUB_MACHINE_EMU) && (defined(__arm__) || defined(__ia64__) || \ (defined(__riscv) && (__riscv_xlen == 32))) @@ -521,11 +580,24 @@ void EXPORT_FUNC(grub_real_boot_time) (const char *file, const char *fmt, ...) __attribute__ ((format (GNU_PRINTF, 3, 4))); #define grub_boot_time(...) grub_real_boot_time(GRUB_FILE, __LINE__, __VA_ARGS__) #else -#define grub_boot_time(...) +#define grub_boot_time(fmt, ...) grub_dprintf("boot", fmt "\n", ##__VA_ARGS__) #endif -#define grub_max(a, b) (((a) > (b)) ? (a) : (b)) -#define grub_min(a, b) (((a) < (b)) ? (a) : (b)) +#define _grub_min(a, b, _a, _b) \ + ({ typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + _a < _b ? _a : _b; }) +#define grub_min(a, b) _grub_min(a, b, \ + CONCAT(_a_,__COUNTER__), \ + CONCAT(_b_,__COUNTER__)) + +#define _grub_max(a, b, _a, _b) \ + ({ typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + _a > _b ? _a : _b; }) +#define grub_max(a, b) _grub_max(a, b, \ + CONCAT(_a_,__COUNTER__), \ + CONCAT(_b_,__COUNTER__)) #define grub_log2ull(n) (GRUB_TYPE_BITS (grub_uint64_t) - __builtin_clzll (n) - 1) diff --git a/include/grub/mm.h b/include/grub/mm.h index f3bf87fa0f..8ee1fc7171 100644 --- a/include/grub/mm.h +++ b/include/grub/mm.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #ifndef NULL @@ -56,6 +57,37 @@ void *EXPORT_FUNC(grub_realloc) (void *ptr, grub_size_t size); void *EXPORT_FUNC(grub_memalign) (grub_size_t align, grub_size_t size); #endif +#define GRUB_MEM_ATTR_R 0x0000000000000004LLU +#define GRUB_MEM_ATTR_W 0x0000000000000002LLU +#define GRUB_MEM_ATTR_X 0x0000000000000001LLU + +#ifdef GRUB_MACHINE_EFI +grub_err_t EXPORT_FUNC(grub_get_mem_attrs) (grub_addr_t addr, + grub_size_t size, + grub_uint64_t *attrs); +grub_err_t EXPORT_FUNC(grub_update_mem_attrs) (grub_addr_t addr, + grub_size_t size, + grub_uint64_t set_attrs, + grub_uint64_t clear_attrs); +#else /* !GRUB_MACHINE_EFI */ +static inline grub_err_t +grub_get_mem_attrs (grub_addr_t addr __attribute__((__unused__)), + grub_size_t size __attribute__((__unused__)), + grub_uint64_t *attrs __attribute__((__unused__))) +{ + return GRUB_ERR_NONE; +} + +static inline grub_err_t +grub_update_mem_attrs (grub_addr_t addr __attribute__((__unused__)), + grub_size_t size __attribute__((__unused__)), + grub_uint64_t set_attrs __attribute__((__unused__)), + grub_uint64_t clear_attrs __attribute__((__unused__))) +{ + return GRUB_ERR_NONE; +} +#endif /* GRUB_MACHINE_EFI */ + void grub_mm_check_real (const char *file, int line); #define grub_mm_check() grub_mm_check_real (GRUB_FILE, __LINE__); diff --git a/include/grub/mm_private.h b/include/grub/mm_private.h index 96c2d816be..2481ee1fa2 100644 --- a/include/grub/mm_private.h +++ b/include/grub/mm_private.h @@ -20,7 +20,6 @@ #define GRUB_MM_PRIVATE_H 1 #include -#include /* For context, see kern/mm.c */ @@ -99,17 +98,4 @@ typedef struct grub_mm_region extern grub_mm_region_t EXPORT_VAR (grub_mm_base); #endif -static inline void -grub_mm_size_sanity_check (void) { - /* Ensure we preserve alignment when doing h = (grub_mm_header_t) (r + 1). */ - COMPILE_TIME_ASSERT ((sizeof (struct grub_mm_region) % - sizeof (struct grub_mm_header)) == 0); - - /* - * GRUB_MM_ALIGN is supposed to represent cell size, and a mm_header is - * supposed to be 1 cell. - */ - COMPILE_TIME_ASSERT (sizeof (struct grub_mm_header) == GRUB_MM_ALIGN); -} - #endif diff --git a/include/grub/net.h b/include/grub/net.h index 844e501c12..d280acd728 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -29,7 +29,8 @@ enum { - GRUB_NET_MAX_LINK_HEADER_SIZE = 64, + GRUB_NET_MAX_LINK_HEADER_SIZE = 96, + GRUB_NET_MAX_LINK_ADDRESS_SIZE = 32, GRUB_NET_UDP_HEADER_SIZE = 8, GRUB_NET_TCP_HEADER_SIZE = 20, GRUB_NET_OUR_IPV4_HEADER_SIZE = 20, @@ -42,15 +43,17 @@ enum typedef enum grub_link_level_protocol_id { - GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET + /* IANA ARP constant to define hardware type. */ + GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET = 1, } grub_link_level_protocol_id_t; typedef struct grub_net_link_level_address { grub_link_level_protocol_id_t type; + grub_uint8_t len; union { - grub_uint8_t mac[6]; + grub_uint8_t mac[GRUB_NET_MAX_LINK_ADDRESS_SIZE]; }; } grub_net_link_level_address_t; @@ -450,6 +453,66 @@ struct grub_net_bootp_packet grub_uint8_t vendor[0]; } GRUB_PACKED; +struct grub_net_dhcp6_packet +{ + grub_uint32_t message_type:8; + grub_uint32_t transaction_id:24; + grub_uint8_t dhcp_options[0]; +} GRUB_PACKED; + +struct grub_net_dhcp6_option { + grub_uint16_t code; + grub_uint16_t len; + grub_uint8_t data[0]; +} GRUB_PACKED; + +struct grub_net_dhcp6_option_iana { + grub_uint32_t iaid; + grub_uint32_t t1; + grub_uint32_t t2; + grub_uint8_t data[0]; +} GRUB_PACKED; + +struct grub_net_dhcp6_option_iaaddr { + grub_uint8_t addr[16]; + grub_uint32_t preferred_lifetime; + grub_uint32_t valid_lifetime; + grub_uint8_t data[0]; +} GRUB_PACKED; + +struct grub_net_dhcp6_option_duid_ll +{ + grub_uint16_t type; + grub_uint16_t hw_type; + grub_uint8_t hwaddr[6]; +} GRUB_PACKED; + +enum + { + GRUB_NET_DHCP6_SOLICIT = 1, + GRUB_NET_DHCP6_ADVERTISE = 2, + GRUB_NET_DHCP6_REQUEST = 3, + GRUB_NET_DHCP6_REPLY = 7 + }; + +enum + { + DHCP6_CLIENT_PORT = 546, + DHCP6_SERVER_PORT = 547 + }; + +enum + { + GRUB_NET_DHCP6_OPTION_CLIENTID = 1, + GRUB_NET_DHCP6_OPTION_SERVERID = 2, + GRUB_NET_DHCP6_OPTION_IA_NA = 3, + GRUB_NET_DHCP6_OPTION_IAADDR = 5, + GRUB_NET_DHCP6_OPTION_ORO = 6, + GRUB_NET_DHCP6_OPTION_ELAPSED_TIME = 8, + GRUB_NET_DHCP6_OPTION_DNS_SERVERS = 23, + GRUB_NET_DHCP6_OPTION_BOOTFILE_URL = 59 + }; + #define GRUB_NET_BOOTP_RFC1048_MAGIC_0 0x63 #define GRUB_NET_BOOTP_RFC1048_MAGIC_1 0x82 #define GRUB_NET_BOOTP_RFC1048_MAGIC_2 0x53 @@ -470,6 +533,7 @@ enum GRUB_NET_DHCP_MESSAGE_TYPE = 53, GRUB_NET_DHCP_SERVER_IDENTIFIER = 54, GRUB_NET_DHCP_PARAMETER_REQUEST_LIST = 55, + GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER = 60, GRUB_NET_BOOTP_CLIENT_ID = 61, GRUB_NET_DHCP_TFTP_SERVER_NAME = 66, GRUB_NET_DHCP_BOOTFILE_NAME = 67, @@ -485,6 +549,21 @@ grub_net_configure_by_dhcp_ack (const char *name, grub_size_t size, int is_def, char **device, char **path); +struct grub_net_network_level_interface * +grub_net_configure_by_dhcpv6_reply (const char *name, + struct grub_net_card *card, + grub_net_interface_flags_t flags, + const struct grub_net_dhcp6_packet *v6, + grub_size_t size, + int is_def, char **device, char **path); + +int +grub_ipv6_get_masksize(grub_uint16_t *mask); + +grub_err_t +grub_net_add_ipv6_local (struct grub_net_network_level_interface *inf, + int mask); + grub_err_t grub_net_add_ipv4_local (struct grub_net_network_level_interface *inf, int mask); @@ -493,6 +572,10 @@ void grub_net_process_dhcp (struct grub_net_buff *nb, struct grub_net_network_level_interface *iface); +grub_err_t +grub_net_process_dhcp6 (struct grub_net_buff *nb, + struct grub_net_card *card); + int grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a, const grub_net_link_level_address_t *b); @@ -509,11 +592,13 @@ grub_net_addr_cmp (const grub_net_network_level_address_t *a, #define GRUB_NET_MAX_STR_ADDR_LEN sizeof ("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX") /* - Currently suppoerted adresses: - ethernet: XX:XX:XX:XX:XX:XX + Up to 32 byte hardware address supported, see GRUB_NET_MAX_LINK_ADDRESS_SIZE */ - -#define GRUB_NET_MAX_STR_HWADDR_LEN (sizeof ("XX:XX:XX:XX:XX:XX")) +#define GRUB_NET_MAX_STR_HWADDR_LEN (sizeof (\ + "XX:XX:XX:XX:XX:XX:XX:XX:"\ + "XX:XX:XX:XX:XX:XX:XX:XX:"\ + "XX:XX:XX:XX:XX:XX:XX:XX:"\ + "XX:XX:XX:XX:XX:XX:XX:XX")) /* Max VLAN id = 4094 */ #define GRUB_NET_MAX_STR_VLAN_LEN (sizeof ("vlanXXXX")) @@ -540,16 +625,7 @@ void grub_bootp_fini (void); void grub_dns_init (void); void grub_dns_fini (void); -static inline void -grub_net_network_level_interface_unregister (struct grub_net_network_level_interface *inter) -{ - inter->card->num_ifaces--; - *inter->prev = inter->next; - if (inter->next) - inter->next->prev = inter->prev; - inter->next = 0; - inter->prev = 0; -} +void grub_net_network_level_interface_unregister (struct grub_net_network_level_interface *inter); void grub_net_tcp_retransmit (void); @@ -579,7 +655,7 @@ void grub_net_remove_dns_server (const struct grub_net_network_level_address *s); grub_err_t -grub_net_search_config_file (char *config); +grub_net_search_config_file (char *config, grub_size_t config_buf_len); extern char *grub_net_default_server; diff --git a/include/grub/net/efi.h b/include/grub/net/efi.h new file mode 100644 index 0000000000..de90d223e8 --- /dev/null +++ b/include/grub/net/efi.h @@ -0,0 +1,144 @@ +#ifndef GRUB_NET_EFI_HEADER +#define GRUB_NET_EFI_HEADER 1 + +#include +#include +#include +#include + +typedef struct grub_efi_net_interface grub_efi_net_interface_t; +typedef struct grub_efi_net_ip_config grub_efi_net_ip_config_t; +typedef union grub_efi_net_ip_address grub_efi_net_ip_address_t; +typedef struct grub_efi_net_ip_manual_address grub_efi_net_ip_manual_address_t; + +struct grub_efi_net_interface +{ + char *name; + int prefer_ip6; + struct grub_efi_net_device *dev; + struct grub_efi_net_io *io; + grub_efi_net_ip_config_t *ip_config; + int io_type; + struct grub_efi_net_interface *next; +}; + +#define efi_net_interface_get_hw_address(inf) inf->ip_config->get_hw_address (inf->dev) +#define efi_net_interface_get_address(inf) inf->ip_config->get_address (inf->dev) +#define efi_net_interface_get_route_table(inf) inf->ip_config->get_route_table (inf->dev) +#define efi_net_interface_set_address(inf, addr, with_subnet) inf->ip_config->set_address (inf->dev, addr, with_subnet) +#define efi_net_interface_set_gateway(inf, addr) inf->ip_config->set_gateway (inf->dev, addr) +#define efi_net_interface_set_dns(inf, addr) inf->ip_config->set_dns (inf->dev, addr) + +struct grub_efi_net_ip_config +{ + char * (*get_hw_address) (struct grub_efi_net_device *dev); + char * (*get_address) (struct grub_efi_net_device *dev); + char ** (*get_route_table) (struct grub_efi_net_device *dev); + grub_efi_net_interface_t * (*best_interface) (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *address); + int (*set_address) (struct grub_efi_net_device *dev, grub_efi_net_ip_manual_address_t *net_ip, int with_subnet); + int (*set_gateway) (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *address); + int (*set_dns) (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *dns); +}; + +union grub_efi_net_ip_address +{ + grub_efi_ipv4_address_t ip4; + grub_efi_ipv6_address_t ip6; +}; + +struct grub_efi_net_ip_manual_address +{ + int is_ip6; + union + { + grub_efi_ip4_config2_manual_address_t ip4; + grub_efi_ip6_config_manual_address_t ip6; + }; +}; + +struct grub_efi_net_device +{ + grub_efi_handle_t handle; + grub_efi_ip4_config2_protocol_t *ip4_config; + grub_efi_ip6_config_protocol_t *ip6_config; + grub_efi_handle_t http_handle; + grub_efi_http_t *http; + grub_efi_handle_t ip4_pxe_handle; + grub_efi_pxe_t *ip4_pxe; + grub_efi_handle_t ip6_pxe_handle; + grub_efi_pxe_t *ip6_pxe; + grub_efi_handle_t dhcp4_handle; + grub_efi_dhcp4_protocol_t *dhcp4; + grub_efi_handle_t dhcp6_handle; + grub_efi_dhcp6_protocol_t *dhcp6; + char *card_name; + grub_efi_net_interface_t *net_interfaces; + struct grub_efi_net_device *next; +}; + +struct grub_efi_net_io +{ + void (*configure) (struct grub_efi_net_device *dev, int prefer_ip6); + grub_err_t (*open) (struct grub_efi_net_device *dev, + int prefer_ip6, + grub_file_t file, + const char *filename, + int type); + grub_ssize_t (*read) (struct grub_efi_net_device *dev, + int prefer_ip6, + grub_file_t file, + char *buf, + grub_size_t len); + grub_err_t (*close) (struct grub_efi_net_device *dev, + int prefer_ip6, + grub_file_t file); +}; + +extern struct grub_efi_net_device *net_devices; + +extern struct grub_efi_net_io io_http; +extern struct grub_efi_net_io io_pxe; + +extern grub_efi_net_ip_config_t *efi_net_ip4_config; +extern grub_efi_net_ip_config_t *efi_net_ip6_config; + +char * +grub_efi_ip4_address_to_string (grub_efi_ipv4_address_t *address); + +char * +grub_efi_ip6_address_to_string (grub_efi_pxe_ipv6_address_t *address); + +char * +grub_efi_hw_address_to_string (grub_efi_uint32_t hw_address_size, grub_efi_mac_address_t hw_address); + +int +grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *address, const char **rest); + +int +grub_efi_string_to_ip6_address (const char *val, grub_efi_ipv6_address_t *address, const char **rest); + +char * +grub_efi_ip6_interface_name (struct grub_efi_net_device *dev); + +char * +grub_efi_ip4_interface_name (struct grub_efi_net_device *dev); + +grub_efi_net_interface_t * +grub_efi_net_create_interface (struct grub_efi_net_device *dev, + const char *interface_name, + grub_efi_net_ip_manual_address_t *net_ip, + int has_subnet); + +int grub_efi_net_fs_init (void); +void grub_efi_net_fs_fini (void); +int grub_efi_net_boot_from_https (void); +int grub_efi_net_boot_from_opa (void); + +extern grub_command_func_t grub_efi_net_list_routes; +extern grub_command_func_t grub_efi_net_list_cards; +extern grub_command_func_t grub_efi_net_list_addrs; +extern grub_command_func_t grub_efi_net_add_addr; +extern grub_command_func_t grub_efi_net_bootp; +extern grub_command_func_t grub_efi_net_bootp6; + +#endif /* ! GRUB_NET_EFI_HEADER */ diff --git a/include/grub/normal.h b/include/grub/normal.h index 218cbabcca..6d2b59db88 100644 --- a/include/grub/normal.h +++ b/include/grub/normal.h @@ -145,7 +145,7 @@ grub_normal_add_menu_entry (int argc, const char **args, char **classes, const char *id, const char *users, const char *hotkey, const char *prefix, const char *sourcecode, - int submenu); + int submenu, int *index, struct bls_entry *bls, grub_blsuki_entry_t *blsuki); grub_err_t grub_normal_set_password (const char *user, const char *password); diff --git a/include/grub/ntfs.h b/include/grub/ntfs.h index d1a6af6967..77b182acff 100644 --- a/include/grub/ntfs.h +++ b/include/grub/ntfs.h @@ -89,6 +89,30 @@ enum #define GRUB_NTFS_COM_SEC (GRUB_NTFS_COM_LEN >> GRUB_NTFS_BLK_SHR) #define GRUB_NTFS_LOG_COM_SEC (GRUB_NTFS_COM_LOG_LEN - GRUB_NTFS_BLK_SHR) +#define GRUB_NTFS_ATTRIBUTE_HEADER_SIZE 16 + +/* + * To make attribute validation clearer the offsets for each value in the + * attribute headers are defined as macros. + * + * These offsets are all from: + * https://flatcap.github.io/linux-ntfs/ntfs/concepts/attribute_header.html + */ + +/* These offsets are part of the attribute header. */ +#define GRUB_NTFS_ATTRIBUTE_LENGTH 4 +#define GRUB_NTFS_ATTRIBUTE_RESIDENT 8 +#define GRUB_NTFS_ATTRIBUTE_NAME_LENGTH 9 +#define GRUB_NTFS_ATTRIBUTE_NAME_OFFSET 10 + +/* Offsets for values needed for resident data. */ +#define GRUB_NTFS_ATTRIBUTE_RES_LENGTH 16 +#define GRUB_NTFS_ATTRIBUTE_RES_OFFSET 20 + +/* Offsets for values needed for non-resident data. */ +#define GRUB_NTFS_ATTRIBUTE_DATA_RUNS 32 +#define GRUB_NTFS_ATTRIBUTE_COMPRESSION_UNIT_SIZE 34 + enum { GRUB_NTFS_AF_ALST = 1, @@ -134,6 +158,7 @@ struct grub_ntfs_attr grub_uint8_t *attr_cur, *attr_nxt, *attr_end; grub_uint32_t save_pos; grub_uint8_t *sbuf; + grub_uint8_t *end; struct grub_ntfs_file *mft; }; diff --git a/include/grub/offsets.h b/include/grub/offsets.h index 871e1cd4c3..69211aa798 100644 --- a/include/grub/offsets.h +++ b/include/grub/offsets.h @@ -63,7 +63,7 @@ #define GRUB_KERNEL_SPARC64_IEEE1275_LINK_ADDR 0x4400 #define GRUB_KERNEL_POWERPC_IEEE1275_LINK_ALIGN 4 -#define GRUB_KERNEL_POWERPC_IEEE1275_LINK_ADDR 0x200000 +#define GRUB_KERNEL_POWERPC_IEEE1275_LINK_ADDR 0x400000 #define GRUB_KERNEL_MIPS_LOONGSON_LINK_ADDR 0x80200000 diff --git a/include/grub/pkcs1_v15.h b/include/grub/pkcs1_v15.h new file mode 100644 index 0000000000..5c338c84a1 --- /dev/null +++ b/include/grub/pkcs1_v15.h @@ -0,0 +1,27 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2013 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 . + */ + +/* + * Given a hash value 'hval', of hash specification 'hash', perform + * the EMSA-PKCS1-v1_5 padding suitable for a key with modulus 'mod' + * (See RFC 8017 s 9.2) + */ +gcry_err_code_t +grub_crypto_rsa_pad (gcry_mpi_t * hmpi, grub_uint8_t * hval, + const gcry_md_spec_t * hash, gcry_mpi_t mod); + diff --git a/include/grub/powerpc/linux.h b/include/grub/powerpc/linux.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/include/grub/riscv64/efi/memory.h b/include/grub/riscv64/efi/memory.h index c6cb324171..acb61dca44 100644 --- a/include/grub/riscv64/efi/memory.h +++ b/include/grub/riscv64/efi/memory.h @@ -2,5 +2,6 @@ #include #define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffffffffULL +#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS #endif /* ! GRUB_MEMORY_CPU_HEADER */ diff --git a/include/grub/search.h b/include/grub/search.h index ffd2411ca1..b6b5c8be1f 100644 --- a/include/grub/search.h +++ b/include/grub/search.h @@ -21,9 +21,11 @@ enum search_flags { - SEARCH_FLAGS_NONE = 0, - SEARCH_FLAGS_NO_FLOPPY = 1, - SEARCH_FLAGS_EFIDISK_ONLY = 2 + SEARCH_FLAGS_NONE = 0, + SEARCH_FLAGS_NO_FLOPPY = 1, + SEARCH_FLAGS_EFIDISK_ONLY = 2, + SEARCH_FLAGS_ROOTDEV_ONLY = 4, + SEARCH_FLAGS_CRYPTODISK_ONLY = 8 }; void grub_search_fs_file (const char *key, const char *var, diff --git a/include/grub/serial.h b/include/grub/serial.h index d7e0635788..19cecd3161 100644 --- a/include/grub/serial.h +++ b/include/grub/serial.h @@ -216,6 +216,7 @@ extern void grub_pciserial_init (void); struct grub_serial_port *grub_serial_find (const char *name); extern struct grub_serial_driver grub_ns8250_driver; +extern struct grub_serial_driver grub_efiserial_driver; void EXPORT_FUNC(grub_serial_unregister_driver) (struct grub_serial_driver *driver); #ifndef GRUB_MACHINE_EMU diff --git a/include/grub/sparc64/linux.h b/include/grub/sparc64/linux.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/include/grub/tpm.h b/include/grub/tpm.h index d09783dacc..d02f0f3c22 100644 --- a/include/grub/tpm.h +++ b/include/grub/tpm.h @@ -46,4 +46,8 @@ grub_is_tpm_fail_fatal (void) return grub_env_get_bool ("tpm_fail_fatal", false); } +bool EXPORT_FUNC(grub_tpm_pcr_is_enabled) (grub_uint8_t pcr); +void EXPORT_FUNC(grub_tpm_enable_pcr) (grub_uint8_t pcr); +void EXPORT_FUNC(grub_tpm_disable_pcr) (grub_uint8_t pcr); + #endif diff --git a/include/grub/types.h b/include/grub/types.h index 064066e2e1..59e030268c 100644 --- a/include/grub/types.h +++ b/include/grub/types.h @@ -30,6 +30,7 @@ #else #define GRUB_PACKED __attribute__ ((packed)) #endif +#define GRUB_ALIGNED(x) __attribute__((aligned (x))) #ifdef GRUB_BUILD # define GRUB_CPU_SIZEOF_VOID_P BUILD_SIZEOF_VOID_P diff --git a/include/grub/util/install.h b/include/grub/util/install.h index 35cf17a8d9..dbf3c216d4 100644 --- a/include/grub/util/install.h +++ b/include/grub/util/install.h @@ -67,6 +67,13 @@ N_("SBAT metadata"), 0 }, \ { "disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, \ N_("disable shim_lock verifier"), 0 }, \ + { "x509key", 'x', N_("FILE"), 0, \ + N_("embed FILE as an x509 certificate for signature checking"), 0}, \ + { "appended-signature-size", GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE,\ + "SIZE", 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), \ + 1}, \ + { "disable-cli", GRUB_INSTALL_OPTIONS_DISABLE_CLI, 0, 0, \ + N_("disabled command line interface access"), 0 }, \ { "verbose", 'v', 0, 0, \ N_("print verbose messages."), 1 } @@ -129,7 +136,9 @@ enum grub_install_options { GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS, GRUB_INSTALL_OPTIONS_DTB, GRUB_INSTALL_OPTIONS_SBAT, - GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK + GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, + GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE, + GRUB_INSTALL_OPTIONS_DISABLE_CLI }; extern char *grub_install_source_directory; @@ -185,13 +194,15 @@ void grub_install_generate_image (const char *dir, const char *prefix, FILE *out, const char *outname, char *mods[], - char *memdisk_path, char **pubkey_paths, - size_t npubkeys, + char *memdisk_path, + char **pubkey_paths, size_t npubkeys, + char **x509key_paths, size_t nx509keys, char *config_path, const struct grub_install_image_target_desc *image_target, - int note, + int note, size_t appsig_size, grub_compression_t comp, const char *dtb_file, - const char *sbat_path, const int disable_shim_lock); + const char *sbat_path, const int disable_shim_lock, + const int disable_cli); const struct grub_install_image_target_desc * grub_install_get_image_target (const char *arg); diff --git a/include/grub/util/misc.h b/include/grub/util/misc.h index e9e0a6724a..bfce065583 100644 --- a/include/grub/util/misc.h +++ b/include/grub/util/misc.h @@ -36,7 +36,7 @@ char *grub_util_read_image (const char *path); void grub_util_load_image (const char *path, char *buf); void grub_util_write_image (const char *img, size_t size, FILE *out, const char *name); -void grub_util_write_image_at (const void *img, size_t size, off_t offset, +void grub_util_write_image_at (const void *img, size_t size, grub_off_t offset, FILE *out, const char *name); char *make_system_path_relative_to_its_root (const char *path); diff --git a/include/grub/util/mkimage.h b/include/grub/util/mkimage.h index 3819a67441..881e3031f4 100644 --- a/include/grub/util/mkimage.h +++ b/include/grub/util/mkimage.h @@ -51,12 +51,12 @@ grub_mkimage_load_image64 (const char *kernel_path, const struct grub_install_image_target_desc *image_target); void grub_mkimage_generate_elf32 (const struct grub_install_image_target_desc *image_target, - int note, char **core_img, size_t *core_size, + int note, size_t appsig_size, char *sbat, char **core_img, size_t *core_size, Elf32_Addr target_addr, struct grub_mkimage_layout *layout); void grub_mkimage_generate_elf64 (const struct grub_install_image_target_desc *image_target, - int note, char **core_img, size_t *core_size, + int note, size_t appsig_size, char *sbat, char **core_img, size_t *core_size, Elf64_Addr target_addr, struct grub_mkimage_layout *layout); diff --git a/include/grub/x86_64/efi/console.h b/include/grub/x86_64/efi/console.h new file mode 100644 index 0000000000..dba9d8678d --- /dev/null +++ b/include/grub/x86_64/efi/console.h @@ -0,0 +1,24 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2005,2006,2007 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_X86_64_EFI_CONSOLE_H +#define GRUB_X86_64_EFI_CONSOLE_H + +#include + +#endif /* ! GRUB_X86_64_EFI_CONSOLE_H */ diff --git a/include/grub/x86_64/efi/memory.h b/include/grub/x86_64/efi/memory.h index 46e9145a30..e81cfb3221 100644 --- a/include/grub/x86_64/efi/memory.h +++ b/include/grub/x86_64/efi/memory.h @@ -2,9 +2,11 @@ #include #if defined (__code_model_large__) -#define GRUB_EFI_MAX_USABLE_ADDRESS 0xffffffff +#define GRUB_EFI_MAX_USABLE_ADDRESS __UINTPTR_MAX__ +#define GRUB_EFI_MAX_ALLOCATION_ADDRESS 0x7fffffff #else #define GRUB_EFI_MAX_USABLE_ADDRESS 0x7fffffff +#define GRUB_EFI_MAX_ALLOCATION_ADDRESS GRUB_EFI_MAX_USABLE_ADDRESS #endif #endif /* ! GRUB_MEMORY_CPU_HEADER */ diff --git a/po/.gitignore b/po/.gitignore new file mode 100644 index 0000000000..f507e7741e --- /dev/null +++ b/po/.gitignore @@ -0,0 +1,5 @@ +/Makefile +/POTFILES*.in +/grub.pot +/remove-potcdate.sed +/stamp-po diff --git a/tests/grub_script_eval.in b/tests/grub_script_eval.in index c97b78d77f..9c62110424 100644 --- a/tests/grub_script_eval.in +++ b/tests/grub_script_eval.in @@ -3,4 +3,12 @@ eval echo "Hello world" valname=tst eval $valname=hi -echo $tst \ No newline at end of file +echo $tst + +if eval " +false +"; then + echo should have failed +else + echo failed as expected +fi diff --git a/tests/printf_unit_test.c b/tests/printf_unit_test.c index 098c29fd9c..f2de187b03 100644 --- a/tests/printf_unit_test.c +++ b/tests/printf_unit_test.c @@ -20,6 +20,7 @@ #include #include #include +#include #define MSG "printf test failed: %s, %s", real, expected @@ -73,6 +74,15 @@ printf_test (void) grub_snprintf (real, sizeof (real), "%%0%dd ", 1); snprintf (expected, sizeof (expected), "%%0%dd ", 1); grub_test_assert (strcmp (real, expected) == 0, MSG); + grub_snprintf (real, sizeof (real), "%zd %zd %zd", (ssize_t) -1, (ssize_t) (SIZE_MAX >> 1), (ssize_t) 42); + snprintf (expected, sizeof (expected), "%zd %zd %zd", (ssize_t) -1, (ssize_t) (SIZE_MAX >> 1), (ssize_t) 42); + grub_test_assert (strcmp (real, expected) == 0, MSG); + grub_snprintf (real, sizeof (real), "%zu %zu %zu", (size_t) 0, (size_t) SIZE_MAX, (size_t) 42); + snprintf (expected, sizeof (expected), "%zu %zu %zu", (size_t) 0, (size_t) SIZE_MAX, (size_t) 42); + grub_test_assert (strcmp (real, expected) == 0, MSG); + grub_snprintf (real, sizeof (real), "%zx %zx %zx", (ssize_t) (SIZE_MAX >> 1), (size_t) SIZE_MAX, (size_t) 0xdeadbeefU); + snprintf (expected, sizeof (expected), "%zx %zx %zx", (ssize_t) (SIZE_MAX >> 1), (size_t) SIZE_MAX, (size_t) 0xdeadbeefU); + grub_test_assert (strcmp (real, expected) == 0, MSG); } GRUB_UNIT_TEST ("printf_unit_test", printf_test); diff --git a/tests/test_asn1.in b/tests/test_asn1.in new file mode 100644 index 0000000000..8173c5c270 --- /dev/null +++ b/tests/test_asn1.in @@ -0,0 +1,12 @@ +#! @BUILD_SHEBANG@ +set -e + +. "@builddir@/grub-core/modinfo.sh" + +out=`echo test_asn1 | @builddir@/grub-shell` + +if [ "$(echo "$out" | tail -n 1)" != "ASN.1 self-tests passed" ]; then + echo "ASN.1 test failure: $out" + exit 1 +fi + diff --git a/tests/util/grub-shell-tester.in b/tests/util/grub-shell-tester.in index 8a87109b15..9a4319d4f4 100644 --- a/tests/util/grub-shell-tester.in +++ b/tests/util/grub-shell-tester.in @@ -56,7 +56,7 @@ for option in "$@"; do usage exit 0 ;; -v | --version) - echo "$0 (GNU GRUB ${PACKAGE_VERSION})" + echo "$0 (GRUB ${PACKAGE_VERSION})" exit 0 ;; --modules=*) ms=`echo "$option" | sed -e 's/--modules=//'` diff --git a/tests/util/grub-shell.in b/tests/util/grub-shell.in index 496e1bab33..e0570c88e1 100644 --- a/tests/util/grub-shell.in +++ b/tests/util/grub-shell.in @@ -243,7 +243,7 @@ for option in "$@"; do usage exit 0 ;; -v | --version) - echo "$0 (GNU GRUB ${PACKAGE_VERSION})" + echo "$0 (GRUB ${PACKAGE_VERSION})" exit 0 ;; --trim) trim=1 ;; diff --git a/util/bash-completion.d/.gitignore b/util/bash-completion.d/.gitignore new file mode 100644 index 0000000000..6813a527ad --- /dev/null +++ b/util/bash-completion.d/.gitignore @@ -0,0 +1,2 @@ +Makefile +grub diff --git a/util/bash-completion.d/Makefile.am b/util/bash-completion.d/Makefile.am index 136287cf1b..61108f0542 100644 --- a/util/bash-completion.d/Makefile.am +++ b/util/bash-completion.d/Makefile.am @@ -6,7 +6,6 @@ EXTRA_DIST = $(bash_completion_source) CLEANFILES = $(bash_completion_script) config.log -bashcompletiondir = $(sysconfdir)/bash_completion.d bashcompletion_DATA = $(bash_completion_script) $(bash_completion_script): $(bash_completion_source) $(top_builddir)/config.status diff --git a/util/bash-completion.d/grub-completion.bash.in b/util/bash-completion.d/grub-completion.bash.in index 213ce1e57c..da7fca5efa 100644 --- a/util/bash-completion.d/grub-completion.bash.in +++ b/util/bash-completion.d/grub-completion.bash.in @@ -275,6 +275,28 @@ have ${__grub_sparc64_setup_program} && \ unset __grub_sparc64_setup_program +# +# grub-get-kernel-settings +# +_grub_get_kernel_settings () { + local cur + + COMPREPLY=() + cur=`_get_cword` + + if [[ "$cur" == -* ]]; then + __grubcomp "$(__grub_get_options_from_help)" + else + # Default complete with a filename + _filedir + fi +} +__grub_get_kernel_settings_program="@grub_get_kernel_settings@" +have ${__grub_get_kernel_settings_program} && \ + complete -F _grub_get_kernel_settings -o filenames ${__grub_get_kernel_settings_program} +unset __grub_get_kernel_settings_program + + # # grub-install # diff --git a/util/config.c b/util/config.c index ebcdd8f5e2..f044a880a7 100644 --- a/util/config.c +++ b/util/config.c @@ -42,6 +42,16 @@ grub_util_parse_config (FILE *f, struct grub_util_config *cfg, int simple) cfg->is_cryptodisk_enabled = 1; continue; } + if (grub_strncmp (ptr, "SUSE_BTRFS_SNAPSHOT_BOOTING=", + sizeof ("SUSE_BTRFS_SNAPSHOT_BOOTING=") - 1) == 0) + { + ptr += sizeof ("SUSE_BTRFS_SNAPSHOT_BOOTING=") - 1; + if (*ptr == '"' || *ptr == '\'') + ptr++; + if (grub_strncmp(ptr, "true", sizeof ("true") - 1) == 0) + cfg->is_suse_btrfs_snapshot_enabled = 1; + continue; + } if (grub_strncmp (ptr, "GRUB_DISTRIBUTOR=", sizeof ("GRUB_DISTRIBUTOR=") - 1) == 0) { diff --git a/util/grub-editenv.c b/util/grub-editenv.c index db6f187cc6..3aa2381ca5 100644 --- a/util/grub-editenv.c +++ b/util/grub-editenv.c @@ -23,8 +23,11 @@ #include #include #include -#include +#include #include +#include +#include +#include #include #include @@ -53,6 +56,9 @@ static struct argp_option options[] = { /* TRANSLATORS: "unset" is a keyword. It's a summary of "unset" subcommand. */ {N_("unset [NAME ...]"), 0, 0, OPTION_DOC|OPTION_NO_USAGE, N_("Delete variables."), 0}, + /* TRANSLATORS: "incr" is a keyword. It's a summary of "incr" subcommand. */ + {N_("incr [NAME ...]"), 0, 0, OPTION_DOC|OPTION_NO_USAGE, + N_("Increase value of integer variables."), 0}, {0, 0, 0, OPTION_DOC, N_("Options:"), -1}, {"verbose", 'v', 0, 0, N_("print verbose messages."), 0}, @@ -120,6 +126,186 @@ block, use `rm %s'."), NULL, help_filter, NULL }; +struct fs_envblk_spec { + const char *fs_name; + off_t offset; + size_t size; +}; +typedef struct fs_envblk_spec fs_envblk_spec_t; + +static grub_envblk_t fs_envblk_open (grub_envblk_t envblk); +static void fs_envblk_write (grub_envblk_t envblk); + +struct fs_envblk_ops { + grub_envblk_t (*open) (grub_envblk_t); + void (*write) (grub_envblk_t); +}; +typedef struct fs_envblk_ops fs_envblk_ops_t; + +struct fs_envblk { + fs_envblk_spec_t *spec; + fs_envblk_ops_t *ops; + const char *dev; +}; +typedef struct fs_envblk *fs_envblk_t; + +static fs_envblk_ops_t fs_envblk_ops = { + .open = fs_envblk_open, + .write = fs_envblk_write +}; + +/* + * fs_envblk_spec describes the file-system specific layout of reserved raw + * blocks used as environment blocks. At present only Btrfs is supported. Other + * file-systems may be added if they provide a similar facility and avoid the + * limitation of writing to COW. + * + * Note: If this table is modified, also update + * grub-core/fs/btrfs.c::btrfs_head, which defines the layout in the Btrfs + * header and exports GRUB_ENV_BTRFS_OFFSET, so that both stay consistent. + */ +static fs_envblk_spec_t fs_envblk_spec[] = { + { "btrfs", GRUB_ENV_BTRFS_OFFSET, GRUB_DISK_SECTOR_SIZE }, + { NULL, 0, 0 } +}; + +static fs_envblk_t fs_envblk = NULL; + +static void +fs_envblk_init (const char *fs_name, const char *dev) +{ + fs_envblk_spec_t *p; + + if (fs_name == NULL || dev == NULL) + return; + + for (p = fs_envblk_spec; p->fs_name != NULL; p++) + { + if (strcmp (fs_name, p->fs_name) == 0) + { + if (fs_envblk == NULL) + fs_envblk = xmalloc (sizeof (*fs_envblk)); + fs_envblk->spec = p; + fs_envblk->dev = xstrdup (dev); + fs_envblk->ops = &fs_envblk_ops; + break; + } + } +} + +static int +read_env_block_var (const char *varname, const char *value, void *hook_data) +{ + grub_envblk_t *p_envblk = (grub_envblk_t *) hook_data; + off_t off; + size_t sz; + char *p, *buf; + FILE *fp; + + if (p_envblk == NULL || fs_envblk == NULL) + return 1; + + if (strcmp (varname, "env_block") != 0) + return 0; + + off = strtol (value, &p, 10); + if (*p == '+') + sz = strtol (p + 1, &p, 10); + else + return 0; + + if (*p != '\0' || sz == 0) + return 0; + + off <<= GRUB_DISK_SECTOR_BITS; + sz <<= GRUB_DISK_SECTOR_BITS; + + fp = grub_util_fopen (fs_envblk->dev, "rb"); + if (fp == NULL) + grub_util_error (_("cannot open `%s': %s"), fs_envblk->dev, strerror (errno)); + + if (fseek (fp, off, SEEK_SET) < 0) + grub_util_error (_("cannot seek `%s': %s"), fs_envblk->dev, strerror (errno)); + + buf = xmalloc (sz); + if ((fread (buf, 1, sz, fp)) != sz) + grub_util_error (_("cannot read `%s': %s"), fs_envblk->dev, strerror (errno)); + + fclose (fp); + + *p_envblk = grub_envblk_open (buf, sz); + + return 1; +} + +static void +create_env_on_block (void) +{ + FILE *fp; + char *buf; + const char *device; + off_t offset; + size_t size; + + if (fs_envblk == NULL) + return; + + device = fs_envblk->dev; + offset = fs_envblk->spec->offset; + size = fs_envblk->spec->size; + + fp = grub_util_fopen (device, "r+b"); + if (fp == NULL) + grub_util_error (_("cannot open `%s': %s"), device, strerror (errno)); + + buf = xmalloc (size); + memcpy (buf, GRUB_ENVBLK_SIGNATURE, sizeof (GRUB_ENVBLK_SIGNATURE) - 1); + memset (buf + sizeof (GRUB_ENVBLK_SIGNATURE) - 1, '#', size - sizeof (GRUB_ENVBLK_SIGNATURE) + 1); + + if (fseek (fp, offset, SEEK_SET) < 0) + grub_util_error (_("cannot seek `%s': %s"), device, strerror (errno)); + + if (fwrite (buf, 1, size, fp) != size) + grub_util_error (_("cannot write to `%s': %s"), device, strerror (errno)); + + grub_util_file_sync (fp); + free (buf); + fclose (fp); +} + +static grub_envblk_t +fs_envblk_open (grub_envblk_t envblk) +{ + grub_envblk_t envblk_on_block = NULL; + char *val; + off_t offset; + size_t size; + + if (envblk == NULL) + return NULL; + + offset = fs_envblk->spec->offset; + size = fs_envblk->spec->size; + + grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var); + + if (envblk_on_block != NULL && grub_envblk_size (envblk_on_block) == size) + return envblk_on_block; + + create_env_on_block (); + + offset = offset >> GRUB_DISK_SECTOR_BITS; + size = (size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS; + + val = xasprintf ("%lld+%zu", (long long) offset, size); + if (grub_envblk_set (envblk, "env_block", val) == 0) + grub_util_error ("%s", _("environment block too small")); + grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var); + free (val); + + return envblk_on_block; +} + static grub_envblk_t open_envblk_file (const char *name) { @@ -182,10 +368,17 @@ static void list_variables (const char *name) { grub_envblk_t envblk; + grub_envblk_t envblk_on_block = NULL; envblk = open_envblk_file (name); + grub_envblk_iterate (envblk, &envblk_on_block, read_env_block_var); grub_envblk_iterate (envblk, NULL, print_var); grub_envblk_close (envblk); + if (envblk_on_block != NULL) + { + grub_envblk_iterate (envblk_on_block, NULL, print_var); + grub_envblk_close (envblk_on_block); + } } static void @@ -208,12 +401,70 @@ write_envblk (const char *name, grub_envblk_t envblk) fclose (fp); } +static void +fs_envblk_write (grub_envblk_t envblk) +{ + FILE *fp; + const char *device; + off_t offset; + size_t size; + + if (envblk == NULL) + return; + + device = fs_envblk->dev; + offset = fs_envblk->spec->offset; + size = fs_envblk->spec->size; + + if (grub_envblk_size (envblk) > size) + grub_util_error ("%s", _("environment block too small")); + + fp = grub_util_fopen (device, "r+b"); + + if (fp == NULL) + grub_util_error (_("cannot open `%s': %s"), device, strerror (errno)); + + if (fseek (fp, offset, SEEK_SET) < 0) + grub_util_error (_("cannot seek `%s': %s"), device, strerror (errno)); + + if (fwrite (grub_envblk_buffer (envblk), 1, grub_envblk_size (envblk), fp) != grub_envblk_size (envblk)) + grub_util_error (_("cannot write to `%s': %s"), device, strerror (errno)); + + grub_util_file_sync (fp); + fclose (fp); +} + +struct var_lookup_ctx { + const char *varname; + bool found; +}; +typedef struct var_lookup_ctx var_lookup_ctx_t; + +static int +var_lookup_iter (const char *varname, const char *value __attribute__ ((unused)), void *hook_data) +{ + var_lookup_ctx_t *ctx = (var_lookup_ctx_t *) hook_data; + + if (grub_strcmp (ctx->varname, varname) == 0) + { + ctx->found = true; + return 1; + } + return 0; +} + static void set_variables (const char *name, int argc, char *argv[]) { grub_envblk_t envblk; + grub_envblk_t envblk_on_block = NULL; envblk = open_envblk_file (name); + if (fs_envblk != NULL) + envblk_on_block = fs_envblk->ops->open (envblk); + else + grub_envblk_delete (envblk, "env_block"); + while (argc) { char *p; @@ -224,33 +475,236 @@ set_variables (const char *name, int argc, char *argv[]) *(p++) = 0; - if (! grub_envblk_set (envblk, argv[0], p)) - grub_util_error ("%s", _("environment block too small")); - + if (((strcmp (argv[0], "next_entry") == 0) || + (strcmp (argv[0], "boot_counter") == 0) || + (strcmp (argv[0], "boot_indeterminate") == 0) || + (strcmp (argv[0], "boot_success") == 0) || + (strcmp (argv[0], "menu_show_once") == 0) || + (strcmp (argv[0], "menu_show_once_timeout") == 0)) + && envblk_on_block != NULL) + { + if (grub_envblk_set (envblk_on_block, argv[0], p) == 0) + grub_util_error ("%s", _("environment block too small")); + goto next; + } + + if (strcmp (argv[0], "env_block") == 0) + { + grub_util_warn (_("can't set env_block as it's read-only")); + goto next; + } + + if (grub_envblk_set (envblk, argv[0], p) == 0) + grub_util_error ("%s", _("environment block too small")); + + if (envblk_on_block != NULL) + { + var_lookup_ctx_t ctx = { + .varname = argv[0], + .found = false + }; + + grub_envblk_iterate (envblk_on_block, &ctx, var_lookup_iter); + if (ctx.found == true) + grub_envblk_delete (envblk_on_block, argv[0]); + } + next: argc--; argv++; } write_envblk (name, envblk); grub_envblk_close (envblk); + + if (envblk_on_block != NULL) + { + fs_envblk->ops->write (envblk_on_block); + grub_envblk_close (envblk_on_block); + } } static void unset_variables (const char *name, int argc, char *argv[]) { grub_envblk_t envblk; + grub_envblk_t envblk_on_block = NULL; envblk = open_envblk_file (name); + + if (fs_envblk != NULL) + envblk_on_block = fs_envblk->ops->open (envblk); + else + grub_envblk_delete (envblk, "env_block"); + while (argc) { grub_envblk_delete (envblk, argv[0]); + if (envblk_on_block != NULL) + grub_envblk_delete (envblk_on_block, argv[0]); + argc--; argv++; } write_envblk (name, envblk); grub_envblk_close (envblk); + + if (envblk_on_block != NULL) + { + fs_envblk->ops->write (envblk_on_block); + grub_envblk_close (envblk_on_block); + } +} + +struct get_int_value_params { + char *varname; + int value; +}; + +static int +get_int_value (const char *varname, const char *value, void *hook_data) +{ + struct get_int_value_params *params = hook_data; + + if (strcmp (varname, params->varname) == 0) { + params->value = strtol (value, NULL, 10); + return 1; + } + return 0; +} + +static void +incr_variables (const char *name, int argc, char *argv[]) +{ + grub_envblk_t envblk; + char buf[16]; + + envblk = open_envblk_file (name); + while (argc) + { + struct get_int_value_params params = { + .varname = argv[0], + .value = 0, /* Consider unset variables 0 */ + }; + + grub_envblk_iterate (envblk, ¶ms, get_int_value); + snprintf(buf, sizeof(buf), "%d", params.value + 1); + + if (! grub_envblk_set (envblk, argv[0], buf)) + grub_util_error ("%s", _("environment block too small")); + + argc--; + argv++; + } + + write_envblk (name, envblk); + grub_envblk_close (envblk); +} + +static bool +is_abstraction (grub_device_t dev) +{ + if (dev == NULL || dev->disk == NULL) + return false; + + if (dev->disk->dev->id == GRUB_DISK_DEVICE_DISKFILTER_ID || + dev->disk->dev->id == GRUB_DISK_DEVICE_CRYPTODISK_ID) + return true; + + return false; +} + +static void +probe_fs_envblk (fs_envblk_spec_t *spec) +{ + char **grub_devices = NULL; + char **curdev, **curdrive; + size_t ndev = 0; + char **grub_drives = NULL; + grub_device_t grub_dev = NULL; + grub_fs_t grub_fs = NULL; + bool have_abstraction = false; + + grub_util_biosdisk_init (DEFAULT_DEVICE_MAP); + grub_init_all (); + grub_gcry_init_all (); + + grub_lvm_fini (); + grub_mdraid09_fini (); + grub_mdraid1x_fini (); + grub_diskfilter_fini (); + grub_diskfilter_init (); + grub_mdraid09_init (); + grub_mdraid1x_init (); + grub_lvm_init (); + + grub_devices = grub_guess_root_devices (DEFAULT_DIRECTORY); + + if (grub_devices == NULL || grub_devices[0] == NULL) + { + grub_util_warn (_("cannot find a device for %s (is /dev mounted?)"), DEFAULT_DIRECTORY); + goto cleanup; + } + + for (curdev = grub_devices; *curdev != NULL; curdev++, ndev++) + grub_util_pull_device (*curdev); + + grub_drives = xcalloc ((ndev + 1), sizeof (grub_drives[0])); + + for (curdev = grub_devices, curdrive = grub_drives; *curdev != NULL; curdev++, + curdrive++) + { + *curdrive = grub_util_get_grub_dev (*curdev); + if (*curdrive == NULL) + { + grub_util_warn (_("cannot find a GRUB drive for %s. Check your device.map"), + *curdev); + goto cleanup; + } + } + *curdrive = NULL; + + grub_dev = grub_device_open (grub_drives[0]); + if (grub_dev == NULL) + { + grub_util_warn (_("cannot open device %s: %s"), grub_drives[0], grub_errmsg); + grub_errno = GRUB_ERR_NONE; + goto cleanup; + } + + grub_fs = grub_fs_probe (grub_dev); + if (grub_fs == NULL) + { + grub_util_warn (_("cannot probe fs for %s: %s"), grub_drives[0], grub_errmsg); + grub_errno = GRUB_ERR_NONE; + goto cleanup; + } + + have_abstraction = is_abstraction (grub_dev); + for (curdrive = grub_drives + 1; *curdrive != NULL && have_abstraction == false; curdrive++) + { + grub_device_t dev = grub_device_open (*curdrive); + + if (dev == NULL) + continue; + have_abstraction = is_abstraction (dev); + grub_device_close (dev); + } + + if (have_abstraction == false) + fs_envblk_init (grub_fs->name, grub_devices[0]); + + cleanup: + if (grub_devices != NULL) + for (curdev = grub_devices; *curdev != NULL; curdev++) + free (*curdev); + free (grub_devices); + free (grub_drives); + grub_device_close (grub_dev); + grub_gcry_fini_all (); + grub_fini_all (); + grub_util_biosdisk_fini (); } int @@ -284,6 +738,9 @@ main (int argc, char *argv[]) command = argv[curindex++]; } + if (strcmp (filename, DEFAULT_ENVBLK_PATH) == 0) + probe_fs_envblk (fs_envblk_spec); + if (strcmp (command, "create") == 0) grub_util_create_envblk_file (filename); else if (strcmp (command, "list") == 0) @@ -292,6 +749,8 @@ main (int argc, char *argv[]) set_variables (filename, argc - curindex, argv + curindex); else if (strcmp (command, "unset") == 0) unset_variables (filename, argc - curindex, argv + curindex); + else if (strcmp (command, "incr") == 0) + incr_variables (filename, argc - curindex, argv + curindex); else { char *program = xstrdup(program_name); diff --git a/util/grub-fstest.c b/util/grub-fstest.c index 25ae52ab6f..982ce9ed7d 100644 --- a/util/grub-fstest.c +++ b/util/grub-fstest.c @@ -329,7 +329,7 @@ cmd_cmp (char *src, char *dest) read_file (src, cmp_hook, ff); { - grub_uint64_t pre; + long long pre; pre = ftell (ff); fseek (ff, 0, SEEK_END); if (pre != ftell (ff)) diff --git a/util/grub-get-kernel-settings.in b/util/grub-get-kernel-settings.in new file mode 100644 index 0000000000..f71bc64360 --- /dev/null +++ b/util/grub-get-kernel-settings.in @@ -0,0 +1,96 @@ +#!/bin/sh +set -e + +# Evaluate new-kernel-pkg's configuration file. +# Copyright (C) 2016 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 . + +PACKAGE_NAME=@PACKAGE_NAME@ +PACKAGE_VERSION=@PACKAGE_VERSION@ +datadir="@datadir@" +if [ "x$pkgdatadir" = x ]; then + pkgdatadir="${datadir}/@PACKAGE@" +fi + +self=`basename $0` + +export TEXTDOMAIN=@PACKAGE@ +export TEXTDOMAINDIR="@localedir@" + +. "${pkgdatadir}/grub-mkconfig_lib" + +# Usage: usage +# Print the usage. +usage () { + gettext_printf "Usage: %s [OPTION]\n" "$self" + gettext "Evaluate new-kernel-pkg configuration"; echo + echo + print_option_help "-h, --help" "$(gettext "print this message and exit")" + print_option_help "-v, --version" "$(gettext "print the version information and exit")" + echo +} + +# Check the arguments. +while test $# -gt 0 +do + option=$1 + shift + + case "$option" in + -h | --help) + usage + exit 0 ;; + -v | --version) + echo "$self (${PACKAGE_NAME}) ${PACKAGE_VERSION}" + exit 0 ;; + -*) + gettext_printf "Unrecognized option \`%s'\n" "$option" 1>&2 + usage + exit 1 + ;; + # Explicitly ignore non-option arguments, for compatibility. + esac +done + +if test -f /etc/sysconfig/kernel ; then + . /etc/sysconfig/kernel +fi + +GRUB_DEFAULT_KERNEL_TYPE=${DEFAULTKERNEL/-core/} +if [ "$GRUB_DEFAULT_KERNEL_TYPE" != "kernel" ]; then + echo GRUB_NON_STANDARD_KERNEL=true + echo export GRUB_NON_STANDARD_KERNEL + GRUB_DEFAULT_KERNEL_TYPE=${GRUB_DEFAULT_KERNEL_TYPE/kernel-/} +fi +echo GRUB_DEFAULT_KERNEL_TYPE=$GRUB_DEFAULT_KERNEL_TYPE +echo export GRUB_DEFAULT_KERNEL_TYPE +if [ "$MAKEDEBUG" = "yes" ]; then + echo GRUB_LINUX_MAKE_DEBUG=true + echo export GRUB_LINUX_MAKE_DEBUG + echo GRUB_CMDLINE_LINUX_DEBUG=\"systemd.log_level=debug systemd.log_target=kmsg\" + echo export GRUB_CMDLINE_LINUX_DEBUG + echo GRUB_LINUX_DEBUG_TITLE_POSTFIX=\" with debugging\" + echo export GRUB_LINUX_DEBUG_TITLE_POSTFIX +fi +if [ "$DEFAULTDEBUG" = "yes" ]; then + echo GRUB_DEFAULT_TO_DEBUG=true +else + echo GRUB_DEFAULT_TO_DEBUG=false +fi +echo export GRUB_DEFAULT_TO_DEBUG +if [ "$UPDATEDEFAULT" = "yes" ]; then + echo GRUB_UPDATE_DEFAULT_KERNEL=true + echo export GRUB_UPDATE_DEFAULT_KERNEL +fi diff --git a/util/grub-install-common.c b/util/grub-install-common.c index ce854d86f8..42aec141e4 100644 --- a/util/grub-install-common.c +++ b/util/grub-install-common.c @@ -368,7 +368,7 @@ char *grub_install_themes_directory = NULL; int grub_install_is_short_mbrgap_supported (void) { - int i, j; + unsigned int i, j; static const char *whitelist[] = { "part_msdos", "biosdisk", "affs", "afs", "bfs", "archelp", @@ -465,11 +465,16 @@ static char **pubkeys; static size_t npubkeys; static char *sbat; static int disable_shim_lock; +static char **x509keys; +static size_t nx509keys; static grub_compression_t compression; +static size_t appsig_size; +static int disable_cli; int grub_install_parse (int key, char *arg) { + const char *end; switch (key) { case GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS: @@ -504,6 +509,15 @@ grub_install_parse (int key, char *arg) case GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK: disable_shim_lock = 1; return 1; + case 'x': + x509keys = xrealloc (x509keys, + sizeof (x509keys[0]) + * (nx509keys + 1)); + x509keys[nx509keys++] = xstrdup (arg); + return 1; + case GRUB_INSTALL_OPTIONS_DISABLE_CLI: + disable_cli = 1; + return 1; case GRUB_INSTALL_OPTIONS_VERBOSITY: verbosity++; @@ -567,6 +581,12 @@ grub_install_parse (int key, char *arg) grub_util_error (_("Unrecognized compression `%s'"), arg); case GRUB_INSTALL_OPTIONS_GRUB_MKIMAGE: return 1; + case GRUB_INSTALL_OPTIONS_APPENDED_SIGNATURE_SIZE: + grub_errno = 0; + appsig_size = grub_strtol(arg, &end, 10); + if (grub_errno) + return 0; + return 1; default: return 0; } @@ -628,6 +648,9 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, for (pk = pubkeys; pk < pubkeys + npubkeys; pk++) slen += sizeof (" --pubkey ''") + grub_strlen (*pk); + for (pk = x509keys; pk < x509keys + nx509keys; pk++) + slen += 10 + grub_strlen (*pk); + for (md = modules.entries; *md; md++) slen += sizeof (" ''") + grub_strlen (*md); @@ -668,6 +691,14 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, *p++ = '\''; } + for (pk = x509keys; pk < x509keys + nx509keys; pk++) + { + p = grub_stpcpy (p, "--x509 '"); + p = grub_stpcpy (p, *pk); + *p++ = '\''; + *p++ = ' '; + } + for (md = modules.entries; *md; md++) { *p++ = ' '; @@ -679,11 +710,15 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, *p = '\0'; grub_util_info ("grub-mkimage --directory '%s' --prefix '%s' --output '%s'" - " --format '%s' --compression '%s'%s%s%s\n", + " --format '%s' --compression '%s'" + " --appended-signture-size %zu %s%s%s\n", + " --format '%s' --compression '%s'%s%s%s%s\n", dir, prefix, outname, mkimage_target, compnames[compression], + appsig_size, note ? " --note" : "", - disable_shim_lock ? " --disable-shim-lock" : "", s); + disable_shim_lock ? " --disable-shim-lock" : "", + disable_cli ? " --disable-cli" : "", s); free (s); tgt = grub_install_get_image_target (mkimage_target); @@ -692,9 +727,11 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix, grub_install_generate_image (dir, prefix, fp, outname, modules.entries, memdisk_path, - pubkeys, npubkeys, config_path, tgt, - note, compression, dtb, sbat, - disable_shim_lock); + pubkeys, npubkeys, + x509keys, nx509keys, + config_path, tgt, + note, appsig_size, compression, dtb, sbat, + disable_shim_lock, disable_cli); while (dc--) grub_install_pop_module (); } diff --git a/util/grub-install.c b/util/grub-install.c index 7dc5657bb6..314b6143b8 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -843,6 +843,8 @@ try_open (const char *path) } #endif +extern int use_relative_path_on_btrfs; + int main (int argc, char *argv[]) { @@ -876,6 +878,9 @@ main (int argc, char *argv[]) grub_util_load_config (&config); + if (config.is_suse_btrfs_snapshot_enabled) + use_relative_path_on_btrfs = 1; + if (!bootloader_id && config.grub_distributor) { char *ptr; @@ -1026,27 +1031,22 @@ main (int argc, char *argv[]) switch (platform) { - case GRUB_INSTALL_PLATFORM_I386_EFI: - case GRUB_INSTALL_PLATFORM_X86_64_EFI: case GRUB_INSTALL_PLATFORM_ARM_EFI: case GRUB_INSTALL_PLATFORM_ARM64_EFI: + case GRUB_INSTALL_PLATFORM_I386_EFI: + case GRUB_INSTALL_PLATFORM_IA64_EFI: case GRUB_INSTALL_PLATFORM_LOONGARCH64_EFI: case GRUB_INSTALL_PLATFORM_RISCV32_EFI: case GRUB_INSTALL_PLATFORM_RISCV64_EFI: - case GRUB_INSTALL_PLATFORM_IA64_EFI: + case GRUB_INSTALL_PLATFORM_X86_64_EFI: is_efi = 1; + if (!force) + grub_util_error (_("This utility should not be used for EFI platforms" + " because it does not support UEFI Secure Boot." + " If you really wish to proceed, invoke the --force" + " option.\nMake sure Secure Boot is disabled before" + " proceeding")); break; - default: - is_efi = 0; - break; - - /* pacify warning. */ - case GRUB_INSTALL_PLATFORM_MAX: - break; - } - - switch (platform) - { case GRUB_INSTALL_PLATFORM_I386_IEEE1275: case GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275: #ifdef __linux__ @@ -1055,12 +1055,14 @@ main (int argc, char *argv[]) try_open ("/dev/nvram"); #endif break; + /* pacify warning. */ + case GRUB_INSTALL_PLATFORM_MAX: + break; default: break; } /* Find the EFI System Partition. */ - if (is_efi) { grub_fs_t fs; @@ -1229,18 +1231,8 @@ main (int argc, char *argv[]) char *d; is_guess = 1; - d = grub_util_path_concat (2, bootdir, "macppc"); - if (!grub_util_is_directory (d)) - { - free (d); - d = grub_util_path_concat (2, bootdir, "efi"); - } /* Find the Mac HFS(+) System Partition. */ - if (!grub_util_is_directory (d)) - { - free (d); - d = grub_util_path_concat (2, bootdir, "EFI"); - } + d = grub_util_path_concat (2, bootdir, "macppc"); if (!grub_util_is_directory (d)) { free (d); @@ -1366,6 +1358,15 @@ main (int argc, char *argv[]) relative_grubdir = xstrdup ("/"); } + if (config.is_suse_btrfs_snapshot_enabled + && grub_strncmp(grub_fs->name, "btrfs", sizeof ("btrfs") - 1) == 0) + { + if (!load_cfg_f) + load_cfg_f = grub_util_fopen (load_cfg, "wb"); + have_load_cfg = 1; + fprintf (load_cfg_f, "set btrfs_relative_path='y'\n"); + } + char *prefix_drive = NULL; char *install_drive = NULL; @@ -1601,6 +1602,55 @@ main (int argc, char *argv[]) prefix_drive = xasprintf ("(%s)", grub_drives[0]); } +#ifdef __linux__ + + if (config.is_suse_btrfs_snapshot_enabled + && grub_strncmp(grub_fs->name, "btrfs", sizeof ("btrfs") - 1) == 0) + { + char *subvol = NULL; + char *mount_path = NULL; + char **rootdir_devices = NULL; + char *rootdir_path = grub_util_path_concat (2, "/", rootdir); + + if (grub_util_is_directory (rootdir_path)) + rootdir_devices = grub_guess_root_devices (rootdir_path); + + free (rootdir_path); + + if (rootdir_devices && rootdir_devices[0]) + if (grub_strcmp (rootdir_devices[0], grub_devices[0]) == 0) + subvol = grub_util_get_btrfs_subvol (platdir, &mount_path); + + if (subvol && mount_path) + { + char *def_subvol; + + def_subvol = grub_util_get_btrfs_subvol ("/", NULL); + + if (def_subvol) + { + if (!load_cfg_f) + load_cfg_f = grub_util_fopen (load_cfg, "wb"); + have_load_cfg = 1; + + if (grub_strcmp (subvol, def_subvol) != 0) + fprintf (load_cfg_f, "btrfs-mount-subvol ($root) %s %s\n", mount_path, subvol); + free (def_subvol); + } + } + + for (curdev = rootdir_devices; *curdev; curdev++) + free (*curdev); + if (rootdir_devices) + free (rootdir_devices); + if (subvol) + free (subvol); + if (mount_path) + free (mount_path); + } + +#endif + char mkimage_target[200]; const char *core_name = NULL; diff --git a/util/grub-menulst2cfg.c b/util/grub-menulst2cfg.c index b80e15cc36..1ea2a86e12 100644 --- a/util/grub-menulst2cfg.c +++ b/util/grub-menulst2cfg.c @@ -34,7 +34,7 @@ main (int argc, char **argv) char *buf = NULL; size_t bufsize = 0; char *suffix = xstrdup (""); - int suffixlen = 0; + size_t suffixlen = 0; const char *out_fname = 0; grub_util_host_init (&argc, &argv); diff --git a/util/grub-mkconfig.in b/util/grub-mkconfig.in index 32c480daeb..1cfb5873ff 100644 --- a/util/grub-mkconfig.in +++ b/util/grub-mkconfig.in @@ -45,10 +45,13 @@ grub_probe="${sbindir}/@grub_probe@" grub_file="${bindir}/@grub_file@" grub_editenv="${bindir}/@grub_editenv@" grub_script_check="${bindir}/@grub_script_check@" +grub_get_kernel_settings="${sbindir}/@grub_get_kernel_settings@" export TEXTDOMAIN=@PACKAGE@ export TEXTDOMAINDIR="@localedir@" +export GRUB_GRUBENV_UPDATE="yes" + . "${pkgdatadir}/grub-mkconfig_lib" # Usage: usage @@ -58,6 +61,7 @@ usage () { gettext "Generate a grub config file"; echo echo print_option_help "-o, --output=$(gettext FILE)" "$(gettext "output generated config to FILE [default=stdout]")" + print_option_help "--no-grubenv-update" "$(gettext "do not update variables in the grubenv file")" print_option_help "-h, --help" "$(gettext "print this message and exit")" print_option_help "-V, --version" "$(gettext "print the version information and exit")" echo @@ -93,6 +97,9 @@ do --output=*) grub_cfg=`echo "$option" | sed 's/--output=//'` ;; + --no-grubenv-update) + GRUB_GRUBENV_UPDATE="no" + ;; -*) gettext_printf "Unrecognized option \`%s'\n" "$option" 1>&2 usage @@ -102,6 +109,14 @@ do esac done +os_name=$(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/' -e 's/\"//g') +if test -s "${grub_cfg}" && test "x${grub_cfg}" = "x/boot/efi/EFI/$os_name/grub.cfg"; then + gettext_printf "Running \`grub2-mkconfig -o %s' will overwrite the GRUB wrapper.\n" "$grub_cfg" 1>&2 + gettext_printf "Please run \`grub2-mkconfig -o /boot/grub2/grub.cfg' instead to update grub.cfg.\n" 1>&2 + gettext_printf "GRUB configuration file was not updated.\n" 1>&2 + exit 1 +fi + if [ "x$EUID" = "x" ] ; then EUID=`id -u` fi @@ -140,9 +155,6 @@ GRUB_DEVICE_PARTUUID="`${grub_probe} --device ${GRUB_DEVICE} --target=partuuid 2 GRUB_DEVICE_BOOT="`${grub_probe} --target=device /boot`" GRUB_DEVICE_BOOT_UUID="`${grub_probe} --device ${GRUB_DEVICE_BOOT} --target=fs_uuid 2> /dev/null`" || true -# Disable os-prober by default due to security reasons. -GRUB_DISABLE_OS_PROBER="true" - # Filesystem for the device containing our userland. Used for stuff like # choosing Hurd filesystem module. GRUB_FS="`${grub_probe} --device ${GRUB_DEVICE} --target=fs 2> /dev/null || echo unknown`" @@ -161,6 +173,8 @@ if test -f ${sysconfdir}/default/grub ; then . ${sysconfdir}/default/grub fi +eval "$("${grub_get_kernel_settings}")" || true + if [ "x${GRUB_DISABLE_UUID}" = "xtrue" ]; then if [ -z "${GRUB_DISABLE_LINUX_UUID}" ]; then GRUB_DISABLE_LINUX_UUID="true" @@ -204,7 +218,6 @@ export GRUB_DEVICE \ GRUB_DEVICE_PARTUUID \ GRUB_DEVICE_BOOT \ GRUB_DEVICE_BOOT_UUID \ - GRUB_DISABLE_OS_PROBER \ GRUB_FS \ GRUB_FONT \ GRUB_PRELOAD_MODULES \ @@ -250,12 +263,16 @@ export GRUB_DEFAULT \ GRUB_BACKGROUND \ GRUB_THEME \ GRUB_GFXPAYLOAD_LINUX \ + GRUB_DISABLE_OS_PROBER \ GRUB_INIT_TUNE \ GRUB_SAVEDEFAULT \ GRUB_ENABLE_CRYPTODISK \ GRUB_BADRAM \ GRUB_OS_PROBER_SKIP_LIST \ - GRUB_DISABLE_SUBMENU + GRUB_DISABLE_SUBMENU \ + GRUB_DEFAULT_DTB \ + SUSE_BTRFS_SNAPSHOT_BOOTING \ + GRUB_ENABLE_BLSCFG if test "x${grub_cfg}" != "x"; then rm -f "${grub_cfg}.new" @@ -282,6 +299,8 @@ for i in "${grub_mkconfig_dir}"/* ; do *~) ;; # emacsen autosave files. FIXME: support other editors */\#*\#) ;; + # rpm config files of yore. + *.rpmsave|*.rpmnew|*.rpmorig) ;; *) if grub_file_is_not_garbage "$i" && test -x "$i" ; then echo @@ -305,7 +324,7 @@ and /etc/grub.d/* files or please file a bug report with else # none of the children aborted with error, install the new grub.cfg oldumask=$(umask) - umask 077 + umask 177 cat ${grub_cfg}.new > ${grub_cfg} umask $oldumask rm -f ${grub_cfg}.new diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index 33e1750aeb..4eadcba4b8 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -30,6 +30,9 @@ fi if test "x$grub_file" = x; then grub_file="${bindir}/@grub_file@" fi +if test "x$grub_editenv" = x; then + grub_editenv="${bindir}/@grub_editenv@" +fi if test "x$grub_mkrelpath" = x; then grub_mkrelpath="${bindir}/@grub_mkrelpath@" fi @@ -49,7 +52,11 @@ grub_warn () make_system_path_relative_to_its_root () { + if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] ; then + "${grub_mkrelpath}" -r "$1" + else "${grub_mkrelpath}" "$1" + fi } is_path_readable_by_grub () @@ -118,8 +125,19 @@ EOF fi } +prepare_grub_to_access_device_with_variable () +{ + device_variable="$1" + shift + prepare_grub_to_access_device "$@" + unset "device_variable" +} + prepare_grub_to_access_device () { + if [ -z "$device_variable" ]; then + device_variable="root" + fi old_ifs="$IFS" IFS=' ' @@ -154,18 +172,18 @@ prepare_grub_to_access_device () # otherwise set root as per value in device.map. fs_hint="`"${grub_probe}" --device $@ --target=compatibility_hint`" if [ "x$fs_hint" != x ]; then - echo "set root='$fs_hint'" + echo "set ${device_variable}='$fs_hint'" fi if [ "x${GRUB_DISABLE_UUID}" != "xtrue" ] && fs_uuid="`"${grub_probe}" --device $@ --target=fs_uuid 2> /dev/null`" ; then hints="`"${grub_probe}" --device $@ --target=hints_string 2> /dev/null`" || hints= if [ "x$hints" != x ]; then echo "if [ x\$feature_platform_search_hint = xy ]; then" - echo " search --no-floppy --fs-uuid --set=root ${hints} ${fs_uuid}" + echo " search --no-floppy --fs-uuid --set=${device_variable} ${hints} ${fs_uuid}" echo "else" - echo " search --no-floppy --fs-uuid --set=root ${fs_uuid}" + echo " search --no-floppy --fs-uuid --set=${device_variable} ${fs_uuid}" echo "fi" else - echo "search --no-floppy --fs-uuid --set=root ${fs_uuid}" + echo "search --no-floppy --fs-uuid --set=${device_variable} ${fs_uuid}" fi fi IFS="$old_ifs" @@ -281,6 +299,14 @@ version_test_gt () *.old:*.old) ;; *.old:*) version_test_gt_a="`echo "$version_test_gt_a" | sed -e 's/\.old$//'`" ; version_test_gt_cmp=gt ;; *:*.old) version_test_gt_b="`echo "$version_test_gt_b" | sed -e 's/\.old$//'`" ; version_test_gt_cmp=ge ;; + *-rescue*:*-rescue*) ;; + *?debug:*?debug) ;; + *-rescue*:*?debug) return 1 ;; + *?debug:*-rescue*) return 0 ;; + *-rescue*:*) return 1 ;; + *:*-rescue*) return 0 ;; + *?debug:*) return 1 ;; + *:*?debug) return 0 ;; esac version_test_numeric "$version_test_gt_a" "$version_test_gt_cmp" "$version_test_gt_b" return "$?" diff --git a/util/grub-mkfont.c b/util/grub-mkfont.c index 7624d7808b..e8a914021b 100644 --- a/util/grub-mkfont.c +++ b/util/grub-mkfont.c @@ -143,7 +143,8 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, int width, height; int cuttop, cutbottom, cutleft, cutright; grub_uint8_t *data; - int mask, i, j, bitmap_size; + int mask, i, bitmap_size; + unsigned int j; FT_GlyphSlot glyph; int flag = FT_LOAD_RENDER | FT_LOAD_MONOCHROME; FT_Error err; @@ -188,7 +189,7 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, cuttop = cutbottom = cutleft = cutright = 0; else { - for (cuttop = 0; cuttop < glyph->bitmap.rows; cuttop++) + for (cuttop = 0; cuttop < (long)glyph->bitmap.rows; cuttop++) { for (j = 0; j < glyph->bitmap.width; j++) if (glyph->bitmap.buffer[j / 8 + cuttop * glyph->bitmap.pitch] @@ -208,10 +209,10 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, break; } cutbottom = glyph->bitmap.rows - 1 - cutbottom; - if (cutbottom + cuttop >= glyph->bitmap.rows) + if (cutbottom + cuttop >= (long)glyph->bitmap.rows) cutbottom = 0; - for (cutleft = 0; cutleft < glyph->bitmap.width; cutleft++) + for (cutleft = 0; cutleft < (long)glyph->bitmap.width; cutleft++) { for (j = 0; j < glyph->bitmap.rows; j++) if (glyph->bitmap.buffer[cutleft / 8 + j * glyph->bitmap.pitch] @@ -230,7 +231,7 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, break; } cutright = glyph->bitmap.width - 1 - cutright; - if (cutright + cutleft >= glyph->bitmap.width) + if (cutright + cutleft >= (long)glyph->bitmap.width) cutright = 0; } @@ -267,7 +268,7 @@ add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, mask = 0; data = &glyph_info->bitmap[0] - 1; - for (j = cuttop; j < height + cuttop; j++) + for (j = cuttop; j < (long)height + cuttop; j++) for (i = cutleft; i < width + cutleft; i++) add_pixel (&data, &mask, glyph->bitmap.buffer[i / 8 + j * glyph->bitmap.pitch] & diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c index c0d5599370..13bdc6cf03 100644 --- a/util/grub-mkimage.c +++ b/util/grub-mkimage.c @@ -75,7 +75,8 @@ static struct argp_option options[] = { /* TRANSLATORS: "embed" is a verb (command description). "*/ {"config", 'c', N_("FILE"), 0, N_("embed FILE as an early config"), 0}, /* TRANSLATORS: "embed" is a verb (command description). "*/ - {"pubkey", 'k', N_("FILE"), 0, N_("embed FILE as public key for signature checking"), 0}, + {"pubkey", 'k', N_("FILE"), 0, N_("embed FILE as public key for PGP signature checking"), 0}, + {"x509", 'x', N_("FILE"), 0, N_("embed FILE as an x509 certificate for appended signature checking"), 0}, /* TRANSLATORS: NOTE is a name of segment. */ {"note", 'n', 0, 0, N_("add NOTE segment for CHRP IEEE1275"), 0}, {"output", 'o', N_("FILE"), 0, N_("output a generated image to FILE [default=stdout]"), 0}, @@ -83,7 +84,9 @@ static struct argp_option options[] = { {"compression", 'C', "(xz|none|auto)", 0, N_("choose the compression to use for core image"), 0}, {"sbat", 's', N_("FILE"), 0, N_("SBAT metadata"), 0}, {"disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, N_("disable shim_lock verifier"), 0}, + {"disable-cli", GRUB_INSTALL_OPTIONS_DISABLE_CLI, 0, 0, N_("disable command line interface access"), 0}, {"verbose", 'v', 0, 0, N_("print verbose messages."), 0}, + {"appended-signature-size", 'S', N_("SIZE"), 0, N_("Add a note segment reserving SIZE bytes for an appended signature"), 0}, { 0, 0, 0, 0, 0, 0 } }; @@ -123,11 +126,15 @@ struct arguments char *dtb; char **pubkeys; size_t npubkeys; + char **x509keys; + size_t nx509keys; char *font; char *config; char *sbat; int note; int disable_shim_lock; + size_t appsig_size; + int disable_cli; const struct grub_install_image_target_desc *image_target; grub_compression_t comp; }; @@ -138,6 +145,7 @@ argp_parser (int key, char *arg, struct argp_state *state) /* Get the input argument from argp_parse, which we know is a pointer to our arguments structure. */ struct arguments *arguments = state->input; + const char* end; switch (key) { @@ -170,6 +178,13 @@ argp_parser (int key, char *arg, struct argp_state *state) arguments->note = 1; break; + case 'S': + grub_errno = 0; + arguments->appsig_size = grub_strtol(arg, &end, 10); + if (grub_errno) + return 0; + break; + case 'm': if (arguments->memdisk) free (arguments->memdisk); @@ -196,6 +211,13 @@ argp_parser (int key, char *arg, struct argp_state *state) arguments->pubkeys[arguments->npubkeys++] = xstrdup (arg); break; + case 'x': + arguments->x509keys = xrealloc (arguments->x509keys, + sizeof (arguments->x509keys[0]) + * (arguments->nx509keys + 1)); + arguments->x509keys[arguments->nx509keys++] = xstrdup (arg); + break; + case 'c': if (arguments->config) free (arguments->config); @@ -239,6 +261,10 @@ argp_parser (int key, char *arg, struct argp_state *state) arguments->disable_shim_lock = 1; break; + case GRUB_INSTALL_OPTIONS_DISABLE_CLI: + arguments->disable_cli = 1; + break; + case 'v': verbosity++; break; @@ -322,10 +348,13 @@ main (int argc, char *argv[]) grub_install_generate_image (arguments.dir, arguments.prefix, fp, arguments.output, arguments.modules, arguments.memdisk, arguments.pubkeys, - arguments.npubkeys, arguments.config, + arguments.npubkeys, arguments.x509keys, + arguments.nx509keys, arguments.config, arguments.image_target, arguments.note, - arguments.comp, arguments.dtb, - arguments.sbat, arguments.disable_shim_lock); + arguments.appsig_size, arguments.comp, + arguments.dtb, arguments.sbat, + arguments.disable_shim_lock, + arguments.disable_cli); if (grub_util_file_sync (fp) < 0) grub_util_error (_("cannot sync `%s': %s"), arguments.output ? : "stdout", diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c index e50b29533c..b993054488 100644 --- a/util/grub-mkimagexx.c +++ b/util/grub-mkimagexx.c @@ -85,6 +85,15 @@ struct grub_ieee1275_note struct grub_ieee1275_note_desc descriptor; }; +#define GRUB_APPENDED_SIGNATURE_NOTE_NAME "Appended-Signature" +#define GRUB_APPENDED_SIGNATURE_NOTE_TYPE 0x41536967 /* "ASig" */ + +struct grub_appended_signature_note +{ + Elf32_Nhdr header; + char name[ALIGN_UP(sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME), 4)]; +}; + #define GRUB_XEN_NOTE_NAME "Xen" struct fixup_block_list @@ -107,6 +116,14 @@ struct section_metadata const char *strtab; }; +#define GRUB_SBAT_NOTE_NAME ".sbat" +#define GRUB_SBAT_NOTE_TYPE 0x53424154 /* "SBAT" */ + +struct grub_sbat_note { + Elf32_Nhdr header; + char name[ALIGN_UP(sizeof(GRUB_SBAT_NOTE_NAME), 4)]; +}; + static int is_relocatable (const struct grub_install_image_target_desc *image_target) { @@ -208,7 +225,7 @@ grub_arm_reloc_jump24 (grub_uint32_t *target, Elf32_Addr sym_addr) void SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc *image_target, - int note, char **core_img, size_t *core_size, + int note, size_t appsig_size, char *sbat, char **core_img, size_t *core_size, Elf_Addr target_addr, struct grub_mkimage_layout *layout) { @@ -217,10 +234,23 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc Elf_Ehdr *ehdr; Elf_Phdr *phdr; Elf_Shdr *shdr; - int header_size, footer_size = 0; + int header_size, footer_size = 0, footer_offset = 0; int phnum = 1; int shnum = 4; int string_size = sizeof (".text") + sizeof ("mods") + 1; + char *footer; + + if (sbat) + { + phnum++; + footer_size += ALIGN_UP (sizeof (struct grub_sbat_note) + layout->sbat_size, 4); + } + + if (appsig_size) + { + phnum++; + footer_size += ALIGN_UP(sizeof (struct grub_appended_signature_note) + appsig_size, 4); + } if (image_target->id != IMAGE_LOONGSON_ELF) phnum += 2; @@ -248,6 +278,7 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc ehdr = (void *) elf_img; phdr = (void *) (elf_img + sizeof (*ehdr)); shdr = (void *) (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr)); + footer = elf_img + program_size + header_size; memcpy (ehdr->e_ident, ELFMAG, SELFMAG); ehdr->e_ident[EI_CLASS] = ELFCLASSXX; if (!image_target->bigendian) @@ -420,6 +451,8 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc phdr->p_filesz = grub_host_to_target32 (XEN_NOTE_SIZE); phdr->p_memsz = 0; phdr->p_offset = grub_host_to_target32 (header_size + program_size); + footer = ptr; + footer_offset = XEN_NOTE_SIZE; } if (image_target->id == IMAGE_XEN_PVH) @@ -453,6 +486,8 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc phdr->p_filesz = grub_host_to_target32 (XEN_PVH_NOTE_SIZE); phdr->p_memsz = 0; phdr->p_offset = grub_host_to_target32 (header_size + program_size); + footer = ptr; + footer_offset = XEN_PVH_NOTE_SIZE; } if (note) @@ -483,8 +518,54 @@ SUFFIX (grub_mkimage_generate_elf) (const struct grub_install_image_target_desc phdr->p_filesz = grub_host_to_target32 (note_size); phdr->p_memsz = 0; phdr->p_offset = grub_host_to_target32 (header_size + program_size); + footer = (elf_img + program_size + header_size + note_size); + footer_offset += note_size; + } + + if (sbat) + { + int note_size = ALIGN_UP (sizeof (struct grub_sbat_note) + layout->sbat_size, 4); + struct grub_sbat_note *note_ptr = (struct grub_sbat_note *) footer; + + note_ptr->header.n_namesz = grub_host_to_target32 (sizeof (GRUB_SBAT_NOTE_NAME)); + note_ptr->header.n_descsz = grub_host_to_target32 (ALIGN_UP(layout->sbat_size, 4)); + note_ptr->header.n_type = grub_host_to_target32 (GRUB_SBAT_NOTE_TYPE); + memcpy (note_ptr->name, GRUB_SBAT_NOTE_NAME, sizeof (GRUB_SBAT_NOTE_NAME)); + memcpy ((char *)(note_ptr + 1), sbat, layout->sbat_size); + + phdr++; + phdr->p_type = grub_host_to_target32 (PT_NOTE); + phdr->p_flags = grub_host_to_target32 (PF_R); + phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof); + phdr->p_vaddr = 0; + phdr->p_paddr = 0; + phdr->p_filesz = grub_host_to_target32 (note_size); + phdr->p_memsz = 0; + phdr->p_offset = grub_host_to_target32 (header_size + program_size + footer_offset); } + if (appsig_size) { + int note_size = ALIGN_UP(sizeof (struct grub_appended_signature_note) + appsig_size, 4); + struct grub_appended_signature_note *note_ptr = (struct grub_appended_signature_note *) + (elf_img + program_size + header_size + (note ? sizeof (struct grub_ieee1275_note) : 0)); + + note_ptr->header.n_namesz = grub_host_to_target32 (sizeof (GRUB_APPENDED_SIGNATURE_NOTE_NAME)); + /* needs to sit at the end, so we round this up and sign some zero padding */ + note_ptr->header.n_descsz = grub_host_to_target32 (ALIGN_UP(appsig_size, 4)); + note_ptr->header.n_type = grub_host_to_target32 (GRUB_APPENDED_SIGNATURE_NOTE_TYPE); + strcpy (note_ptr->name, GRUB_APPENDED_SIGNATURE_NOTE_NAME); + + phdr++; + phdr->p_type = grub_host_to_target32 (PT_NOTE); + phdr->p_flags = grub_host_to_target32 (PF_R); + phdr->p_align = grub_host_to_target32 (image_target->voidp_sizeof); + phdr->p_vaddr = 0; + phdr->p_paddr = 0; + phdr->p_filesz = grub_host_to_target32 (note_size); + phdr->p_memsz = 0; + phdr->p_offset = grub_host_to_target32 (header_size + program_size + (note ? sizeof (struct grub_ieee1275_note) : 0)); + } + { char *str_start = (elf_img + sizeof (*ehdr) + phnum * sizeof (*phdr) + shnum * sizeof (*shdr)); diff --git a/util/grub-mknetdir.c b/util/grub-mknetdir.c index 46f304c2b4..11c9db1177 100644 --- a/util/grub-mknetdir.c +++ b/util/grub-mknetdir.c @@ -32,13 +32,15 @@ static char *rootdir = NULL, *subdir = NULL; static char *debug_image = NULL; +static char efi_netfs = 0; enum { OPTION_NET_DIRECTORY = 0x301, OPTION_SUBDIR, OPTION_DEBUG, - OPTION_DEBUG_IMAGE + OPTION_DEBUG_IMAGE, + OPTION_DEBUG_EFI_NETFS }; static struct argp_option options[] = { @@ -49,6 +51,7 @@ static struct argp_option options[] = { 0, N_("relative subdirectory on network server"), 2}, {"debug", OPTION_DEBUG, 0, OPTION_HIDDEN, 0, 2}, {"debug-image", OPTION_DEBUG_IMAGE, N_("STRING"), OPTION_HIDDEN, 0, 2}, + {"debug-efi-netfs", OPTION_DEBUG_EFI_NETFS, 0, OPTION_HIDDEN, 0, 2}, {0, 0, 0, 0, 0, 0} }; @@ -67,6 +70,9 @@ argp_parser (int key, char *arg, struct argp_state *state) free (subdir); subdir = xstrdup (arg); return 0; + case OPTION_DEBUG_EFI_NETFS: + efi_netfs = 1; + return 0; /* This is an undocumented feature... */ case OPTION_DEBUG: verbosity++; @@ -82,7 +88,6 @@ argp_parser (int key, char *arg, struct argp_state *state) } } - struct argp argp = { options, argp_parser, NULL, "\v"N_("Prepares GRUB network boot images at net_directory/subdir " @@ -92,7 +97,7 @@ struct argp argp = { static char *base; -static const struct +static struct { const char *mkimage_target; const char *netmodule; @@ -157,6 +162,7 @@ process_input_dir (const char *input_dir, enum grub_install_plat platform) grub_install_push_module (targets[platform].netmodule); output = grub_util_path_concat_ext (2, grubdir, "core", targets[platform].ext); + grub_install_make_image_wrap (input_dir, prefix, output, 0, load_cfg, targets[platform].mkimage_target, 0); @@ -196,7 +202,16 @@ main (int argc, char *argv[]) grub_install_mkdir_p (base); - grub_install_push_module ("tftp"); + if (!efi_netfs) + { + grub_install_push_module ("tftp"); + grub_install_push_module ("http"); + } + else + { + targets[GRUB_INSTALL_PLATFORM_I386_EFI].netmodule = "efi_netfs"; + targets[GRUB_INSTALL_PLATFORM_X86_64_EFI].netmodule = "efi_netfs"; + } if (!grub_install_source_directory) { diff --git a/util/grub-mkrelpath.c b/util/grub-mkrelpath.c index 47a241a391..5db7a9a7d9 100644 --- a/util/grub-mkrelpath.c +++ b/util/grub-mkrelpath.c @@ -40,9 +40,12 @@ struct arguments }; static struct argp_option options[] = { + {"relative", 'r', 0, 0, "use relative path on btrfs", 0}, { 0, 0, 0, 0, 0, 0 } }; +extern int use_relative_path_on_btrfs; + static error_t argp_parser (int key, char *arg, struct argp_state *state) { @@ -52,6 +55,9 @@ argp_parser (int key, char *arg, struct argp_state *state) switch (key) { + case 'r': + use_relative_path_on_btrfs = 1; + break; case ARGP_KEY_ARG: if (state->arg_num == 0) arguments->pathname = xstrdup (arg); diff --git a/util/grub-mount.c b/util/grub-mount.c index bf4c8b891b..d369e21666 100644 --- a/util/grub-mount.c +++ b/util/grub-mount.c @@ -269,11 +269,17 @@ fuse_read (const char *path, char *buf, size_t sz, off_t off, { grub_file_t file = files[fi->fh]; grub_ssize_t size; + grub_off_t offset; - if (off > file->size) + if (off < 0) return -EINVAL; - file->offset = off; + if ((grub_off_t)off > file->size) + return -EINVAL; + + offset = (grub_off_t)off; + + file->offset = offset; size = grub_file_read (file, buf, sz); if (size < 0) diff --git a/util/grub-probe.c b/util/grub-probe.c index 65c1ca3f8b..08ae915e8d 100644 --- a/util/grub-probe.c +++ b/util/grub-probe.c @@ -799,7 +799,7 @@ argp_parser (int key, char *arg, struct argp_state *state) case 't': { - int i; + unsigned int i; for (i = PRINT_FS; i < ARRAY_SIZE (targets); i++) if (strcmp (arg, targets[i]) == 0) diff --git a/util/grub-set-bootflag.c b/util/grub-set-bootflag.c new file mode 100644 index 0000000000..31a868aeca --- /dev/null +++ b/util/grub-set-bootflag.c @@ -0,0 +1,319 @@ +/* grub-set-bootflag.c - tool to set boot-flags in the grubenv. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2018 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 . + */ + +/* + * NOTE this gets run by users as root (its suid root), so this does not + * use any grub library / util functions to allow for easy auditing. + * The grub headers are only included to get certain defines. + */ + +#include /* For *_DIR_NAME defines */ +#include +#include +#include /* For GRUB_ENVBLK_DEFCFG define */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "progname.h" + +#define GRUBENV "/" GRUB_BOOT_DIR_NAME "/" GRUB_DIR_NAME "/" GRUB_ENVBLK_DEFCFG +#define GRUBENV_SIZE 1024 + +const char *bootflags[] = { + "boot_success", + "menu_show_once", + NULL +}; + +static void usage(FILE *out) +{ + int i; + + fprintf (out, "Usage: 'grub-set-bootflag ', where is one of:\n"); + for (i = 0; bootflags[i]; i++) + fprintf (out, " %s\n", bootflags[i]); +} + +int main(int argc, char *argv[]) +{ + /* NOTE buf must be at least the longest bootflag length + 4 bytes */ + char env[GRUBENV_SIZE + 1 + 2], buf[64], *s; + /* +1 for 0 termination, +11 for ".%u" in tmp filename */ + char env_filename[PATH_MAX + 1], tmp_filename[PATH_MAX + 11 + 1]; + const char *bootflag; + int i, fd, len, ret; + FILE *f; + + umask(077); + + if (argc != 2) + { + usage (stderr); + return 1; + } + else if (!strcmp (argv[1], "--help")) + { + usage (stdout); + return 0; + } + else if (!strcmp (argv[1], "--version")) + { + printf ("grub-set-bootflag (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION); + return 0; + } + + for (i = 0; bootflags[i]; i++) + if (!strcmp (argv[1], bootflags[i])) + break; + if (!bootflags[i]) + { + fprintf (stderr, "Invalid bootflag: '%s'\n", argv[1]); + usage (stderr); + return 1; + } + + bootflag = bootflags[i]; + len = strlen (bootflag); + + /* + * Exit calmly when not installed SUID root and invoked by non-root. This + * allows installing user/grub-boot-success.service unconditionally while + * supporting non-SUID installation of the program for some limited usage. + */ + if (geteuid()) + { + printf ("grub-set-bootflag not running as root, no action taken\n"); + return 0; + } + + /* + * setegid avoids the new grubenv's gid being that of the user. + */ + if (setegid(0)) + { + perror ("setegid(0) failed"); + return 1; + } + + /* Canonicalize GRUBENV filename, resolving symlinks, etc. */ + if (!realpath(GRUBENV, env_filename)) + { + perror ("Error canonicalizing " GRUBENV " filename"); + return 1; + } + + f = fopen (env_filename, "r"); + if (!f) + { + perror ("Error opening " GRUBENV " for reading"); + return 1; + } + + ret = fread (env, 1, GRUBENV_SIZE, f); + fclose (f); + if (ret != GRUBENV_SIZE) + { + errno = EINVAL; + perror ("Error reading from " GRUBENV); + return 1; + } + + /* 0 terminate env */ + env[GRUBENV_SIZE] = 0; + /* not a valid flag value */ + env[GRUBENV_SIZE + 1] = 0; + env[GRUBENV_SIZE + 2] = 0; + + if (strncmp (env, GRUB_ENVBLK_SIGNATURE, strlen (GRUB_ENVBLK_SIGNATURE))) + { + fprintf (stderr, "Error invalid environment block\n"); + return 1; + } + + /* Find a pre-existing definition of the bootflag */ + s = strstr (env, bootflag); + while (s && s[len] != '=') + s = strstr (s + len, bootflag); + + if (s && ((s[len + 1] != '0' && s[len + 1] != '1') || s[len + 2] != '\n')) + { + fprintf (stderr, "Pre-existing bootflag '%s' has unexpected value\n", bootflag); + return 1; + } + + /* No pre-existing bootflag? -> find free space */ + if (!s) + { + for (i = 0; i < (len + 3); i++) + buf[i] = '#'; + buf[i] = 0; + s = strstr (env, buf); + } + + if (!s) + { + fprintf (stderr, "No space in grubenv to store bootflag '%s'\n", bootflag); + return 1; + } + + /* The grubenv is not 0 terminated, so memcpy the name + '=' , '1', '\n' */ + snprintf(buf, sizeof(buf), "%s=1\n", bootflag); + if (!memcmp(s, buf, len + 3)) + return 0; /* nothing to do */ + memcpy(s, buf, len + 3); + + struct rlimit rlim; + if (getrlimit(RLIMIT_FSIZE, &rlim) || rlim.rlim_cur < GRUBENV_SIZE || rlim.rlim_max < GRUBENV_SIZE) + { + fprintf (stderr, "Resource limits undetermined or too low\n"); + return 1; + } + + /* + * Here we work under the premise that we shouldn't write into the target + * file directly because we might not be able to have all of our changes + * written completely and atomically. That was CVE-2019-14865, known to + * have been triggerable via RLIMIT_FSIZE. While we've dealt with that + * specific attack via the check above, there may be other possibilities. + */ + + /* + * Create a tempfile for writing the new env. Use the canonicalized filename + * for the template so that the tmpfile is in the same dir / on same fs. + * + * We now use per-user fixed temporary filenames, so that a user cannot cause + * multiple files to accumulate. + * + * We don't use O_EXCL so that a stale temporary file doesn't prevent further + * usage of the program by the user. + */ + snprintf(tmp_filename, sizeof(tmp_filename), "%s.%u", env_filename, getuid()); + fd = open(tmp_filename, O_CREAT | O_WRONLY, 0600); + if (fd == -1) + { + perror ("Creating tmpfile failed"); + return 1; + } + + /* + * The lock prevents the same user from reaching further steps ending in + * rename() concurrently, in which case the temporary file only partially + * written by one invocation could be renamed to the target file by another. + * + * The lock also guards the slow fsync() from concurrent calls. After the + * first time that and the rename() complete, further invocations for the + * same flag become no-ops. + * + * We lock the temporary file rather than the target file because locking the + * latter would allow any user having SIGSTOP'ed their process to make all + * other users' invocations fail (or lock up if we'd use blocking mode). + * + * We use non-blocking mode (LOCK_NB) because the lock having been taken by + * another process implies that the other process would normally have already + * renamed the file to target by the time it releases the lock (and we could + * acquire it), so we'd be working directly on the target if we proceeded, + * which is undesirable, and we'd kind of fail on the already-done rename. + */ + if (flock(fd, LOCK_EX | LOCK_NB)) + { + perror ("Locking tmpfile failed"); + return 1; + } + + /* + * Deal with the potential that another invocation proceeded all the way to + * rename() and process exit while we were between open() and flock(). + */ + { + struct stat st1, st2; + if (fstat(fd, &st1) || stat(tmp_filename, &st2)) + { + perror ("stat of tmpfile failed"); + return 1; + } + if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) + { + fprintf (stderr, "Another invocation won race\n"); + return 1; + } + } + + f = fdopen (fd, "w"); + if (!f) + { + perror ("Error fdopen of tmpfile failed"); + unlink(tmp_filename); + return 1; + } + + ret = fwrite (env, 1, GRUBENV_SIZE, f); + if (ret != GRUBENV_SIZE) + { + perror ("Error writing tmpfile"); + unlink(tmp_filename); + return 1; + } + + ret = fflush (f); + if (ret) + { + perror ("Error flushing tmpfile"); + unlink(tmp_filename); + return 1; + } + + ret = ftruncate (fileno (f), GRUBENV_SIZE); + if (ret) + { + perror ("Error truncating tmpfile"); + unlink(tmp_filename); + return 1; + } + + ret = fsync (fileno (f)); + if (ret) + { + perror ("Error syncing tmpfile"); + unlink(tmp_filename); + return 1; + } + + /* + * We must not close the file before rename() as that would remove the lock. + * + * And finally rename the tmpfile with the new env over the old env, the + * linux kernel guarantees that this is atomic (from a syscall pov). + */ + ret = rename(tmp_filename, env_filename); + if (ret) + { + perror ("Error renaming tmpfile to " GRUBENV " failed"); + unlink(tmp_filename); + return 1; + } + + return 0; +} diff --git a/util/grub-set-password.in b/util/grub-set-password.in new file mode 100644 index 0000000000..d8005e5a14 --- /dev/null +++ b/util/grub-set-password.in @@ -0,0 +1,121 @@ +#!/bin/sh -e + +grubdir=`echo "/@bootdirname@/@grubdirname@" | sed 's,//*,/,g'` + +PACKAGE_VERSION="@PACKAGE_VERSION@" +PACKAGE_NAME="@PACKAGE_NAME@" +self=`basename $0` +bindir="@bindir@" +grub_mkpasswd="${bindir}/@grub_mkpasswd_pbkdf2@" + +# Usage: usage +# Print the usage. +usage () { + cat < put user.cfg in a user-selected directory + +Report bugs at https://bugzilla.redhat.com. +EOF +} + +argument () { + opt=$1 + shift + + if test $# -eq 0; then + gettext_printf "%s: option requires an argument -- \`%s'\n" "$self" "$opt" 1>&2 + exit 1 + fi + echo $1 +} + +# Ensure that it's the root user running this script +if [ "${EUID}" -ne 0 ]; then + echo "The grub bootloader password may only be set by root." + usage + exit 2 +fi + +# Check the arguments. +while test $# -gt 0 +do + option=$1 + shift + + case "$option" in + -h | --help) + usage + exit 0 ;; + -v | --version) + echo "$self (${PACKAGE_NAME}) ${PACKAGE_VERSION}" + exit 0 ;; + -o | --output) + OUTPUT_PATH=`argument $option "$@"`; shift ;; + --output=*) + OUTPUT_PATH=`echo "$option" | sed 's/--output=//'` ;; + -o=*) + OUTPUT_PATH=`echo "$option" | sed 's/-o=//'` ;; + esac +done + +# set user input or default path for user.cfg file +if [ -z "${OUTPUT_PATH}" ]; then + OUTPUT_PATH="${grubdir}" +fi + +if [ ! -d "${OUTPUT_PATH}" ]; then + echo "${OUTPUT_PATH} does not exist." + usage + exit 2; +fi + +ttyopt=$(stty -g) +fixtty() { + stty ${ttyopt} +} + +trap fixtty EXIT +stty -echo + +# prompt & confirm new grub2 root user password +echo -n "Enter password: " +read PASSWORD +echo +echo -n "Confirm password: " +read PASSWORD_CONFIRM +echo +stty ${ttyopt} + +getpass() { + local P0 + local P1 + P0="$1" && shift + P1="$1" && shift + + ( echo ${P0} ; echo ${P1} ) | \ + LC_ALL=C ${grub_mkpasswd} | \ + grep -v '[eE]nter password:' | \ + sed -e "s/PBKDF2 hash of your password is //" +} + +MYPASS="$(getpass "${PASSWORD}" "${PASSWORD_CONFIRM}")" +if [ -z "${MYPASS}" ]; then + echo "${self}: error: empty password" 1>&2 + exit 1 +fi + +install -m 0600 /dev/null "${OUTPUT_PATH}/user.cfg" 2>/dev/null || : +chmod 0600 "${OUTPUT_PATH}/user.cfg" 2>/dev/null || : +echo "GRUB2_PASSWORD=${MYPASS}" > "${OUTPUT_PATH}/user.cfg" + +if ! grep -q "^### BEGIN /etc/grub.d/01_users ###$" "${OUTPUT_PATH}/grub.cfg"; then + echo "WARNING: The current configuration lacks password support!" + echo "Update your configuration with @grub_mkconfig@ to support this feature." +fi diff --git a/util/grub-switch-to-blscfg.in b/util/grub-switch-to-blscfg.in new file mode 100644 index 0000000000..1bdf7e331b --- /dev/null +++ b/util/grub-switch-to-blscfg.in @@ -0,0 +1,317 @@ +#! /bin/sh +# +# Set a default boot entry for GRUB. +# Copyright (C) 2004,2009 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 . + +#set -eu + +# Initialize some variables. +prefix=@prefix@ +exec_prefix=@exec_prefix@ +sbindir=@sbindir@ +bindir=@bindir@ +sysconfdir="@sysconfdir@" +PACKAGE_NAME=@PACKAGE_NAME@ +PACKAGE_VERSION=@PACKAGE_VERSION@ +datarootdir="@datarootdir@" +datadir="@datadir@" +if [ ! -v pkgdatadir ]; then + pkgdatadir="${datadir}/@PACKAGE@" +fi + +self=`basename $0` + +grub_get_kernel_settings="${sbindir}/@grub_get_kernel_settings@" +grub_editenv=${bindir}/@grub_editenv@ +etcdefaultgrub=/etc/default/grub + +eval "$("${grub_get_kernel_settings}")" || true + +EFIDIR=$(grep ^ID= /etc/os-release | sed -e 's/^ID=//' -e 's/rhel/redhat/' -e 's/\"//g') +if [ -d /sys/firmware/efi/efivars/ ]; then + startlink=/etc/grub2-efi.cfg + grubdir=`echo "/@bootdirname@/efi/EFI/${EFIDIR}/" | sed 's,//*,/,g'` +else + startlink=/etc/grub2.cfg + grubdir=`echo "/@bootdirname@/@grubdirname@" | sed 's,//*,/,g'` +fi + +blsdir=`echo "/@bootdirname@/loader/entries" | sed 's,//*,/,g'` + +backupsuffix=.bak + +arch="$(uname -m)" + +export TEXTDOMAIN=@PACKAGE@ +export TEXTDOMAINDIR="@localedir@" + +. "${pkgdatadir}/grub-mkconfig_lib" + +# Usage: usage +# Print the usage. +usage () { + gettext_printf "Usage: %s\n" "$self" + gettext "Switch to BLS config files."; echo + echo + print_option_help "-h, --help" "$(gettext "print this message and exit")" + print_option_help "-V, --version" "$(gettext "print the version information and exit")" + echo + print_option_help "--backup-suffix=$(gettext "SUFFIX")" "$backupsuffix" + print_option_help "--bls-directory=$(gettext "DIR")" "$blsdir" + print_option_help "--config-file=$(gettext "FILE")" "$startlink" + print_option_help "--grub-defaults=$(gettext "FILE")" "$etcdefaultgrub" + print_option_help "--grub-directory=$(gettext "DIR")" "$grubdir" + # echo + # gettext "Report bugs to ."; echo +} + +argument () { + opt=$1 + shift + + if test $# -eq 0; then + gettext_printf "%s: option requires an argument -- \`%s'\n" "$self" "$opt" 1>&2 + exit 1 + fi + echo $1 +} + +# Check the arguments. +while test $# -gt 0 +do + option=$1 + shift + + case "$option" in + -h | --help) + usage + exit 0 ;; + -V | --version) + echo "$self (${PACKAGE_NAME}) ${PACKAGE_VERSION}" + exit 0 ;; + + --backup-suffix) + backupsuffix=`argument $option "$@"` + shift + ;; + --backup-suffix=*) + backupsuffix=`echo "$option" | sed 's/--backup-suffix=//'` + ;; + + --bls-directory) + blsdir=`argument $option "$@"` + shift + ;; + --bls-directory=*) + blsdir=`echo "$option" | sed 's/--bls-directory=//'` + ;; + + --config-file) + startlink=`argument $option "$@"` + shift + ;; + --config-file=*) + startlink=`echo "$option" | sed 's/--config-file=//'` + ;; + + --grub-defaults) + etcdefaultgrub=`argument $option "$@"` + shift + ;; + --grub-defaults=*) + etcdefaultgrub=`echo "$option" | sed 's/--grub-defaults=//'` + ;; + + --grub-directory) + grubdir=`argument $option "$@"` + shift + ;; + --grub-directory=*) + grubdir=`echo "$option" | sed 's/--grub-directory=//'` + ;; + + *) + gettext_printf "Unrecognized option \`%s'\n" "$option" 1>&2 + usage + exit 1 + ;; + esac +done + +find_grub_cfg() { + local candidate="" + while [ -e "${candidate}" -o $# -gt 0 ] + do + if [ ! -e "${candidate}" ] ; then + candidate="$1" + shift + fi + + if [ -L "${candidate}" ]; then + candidate="$(realpath "${candidate}")" + fi + + if [ -f "${candidate}" ]; then + export GRUB_CONFIG_FILE="${candidate}" + return 0 + fi + done + return 1 +} + +if ! find_grub_cfg ${startlink} ${grubdir}/grub.cfg ; then + gettext_printf "Couldn't find config file\n" 1>&2 + exit 1 +fi + +if [ ! -d "${blsdir}" ]; then + install -m 700 -d "${blsdir}" +fi + +if [ -f /etc/machine-id ]; then + MACHINE_ID=$(cat /etc/machine-id) +else + MACHINE_ID=$(dmesg | sha256sum) +fi + +mkbls() { + local kernelver=$1 && shift + local datetime=$1 && shift + local kernelopts=$1 && shift + + local debugname="" + local debugid="" + local flavor="" + + if [ "$kernelver" == *\+* ] ; then + local flavor=-"${kernelver##*+}" + if [ "${flavor}" == "-debug" ]; then + local debugname=" with debugging" + local debugid="-debug" + fi + fi + ( + source /etc/os-release + + cat <"${bls_target}" + + if [ "x$GRUB_LINUX_MAKE_DEBUG" = "xtrue" ]; then + bls_debug="$(echo ${bls_target} | sed -e "s/${kernelver}/${kernelver}~debug/")" + cp -aT "${bls_target}" "${bls_debug}" + title="$(grep '^title[ \t]' "${bls_debug}" | sed -e 's/^title[ \t]*//')" + options="$(echo "${cmdline} ${GRUB_CMDLINE_LINUX_DEBUG}" | sed -e 's/\//\\\//g')" + sed -i -e "s/^title.*/title ${title}${GRUB_LINUX_DEBUG_TITLE_POSTFIX}/" "${bls_debug}" + sed -i -e "s/^options.*/options ${options}/" "${bls_debug}" + fi + done + + if [ -f "/boot/vmlinuz-0-rescue-${MACHINE_ID}" ]; then + mkbls "0-rescue-${MACHINE_ID}" "0" "${bootprefix}" >"${blsdir}/${MACHINE_ID}-0-rescue.conf" + fi +} + +# The grub2 EFI binary is not copied to the ESP as a part of an ostree +# transaction. Make sure a grub2 version with BLS support is installed +# but only do this if the blsdir is not set, to make sure that the BLS +# parsing module will search for the BLS snippets in the default path. +if test -f /run/ostree-booted && test -d /sys/firmware/efi/efivars && \ + ! ${grub_editenv} - list | grep -q blsdir && \ + mountpoint -q /boot; then + grub_binary="$(find /usr/lib/ostree-boot/efi/EFI/${EFIDIR}/ -name grub*.efi)" + install -m 700 ${grub_binary} ${grubdir} || exit 1 + # Create a hidden file to indicate that grub2 now has BLS support. + touch /boot/grub2/.grub2-blscfg-supported +fi + +GENERATE=0 +if grep '^GRUB_ENABLE_BLSCFG=.*' "${etcdefaultgrub}" \ + | grep -vq '^GRUB_ENABLE_BLSCFG="*true"*\s*$' ; then + if ! sed -i"${backupsuffix}" \ + -e 's,^GRUB_ENABLE_BLSCFG=.*,GRUB_ENABLE_BLSCFG=true,' \ + "${etcdefaultgrub}" ; then + gettext_printf "Updating %s failed\n" "${etcdefaultgrub}" + exit 1 + fi + GENERATE=1 +elif ! grep -q '^GRUB_ENABLE_BLSCFG=.*' "${etcdefaultgrub}" ; then + if ! echo 'GRUB_ENABLE_BLSCFG=true' >> "${etcdefaultgrub}" ; then + gettext_printf "Updating %s failed\n" "${etcdefaultgrub}" + exit 1 + fi + GENERATE=1 +fi + +if [ "${GENERATE}" -eq 1 ] ; then + copy_bls + + if [ $arch = "x86_64" ] && [ ! -d /sys/firmware/efi ]; then + mod_dir="i386-pc" + elif [ $arch = "ppc64" -o $arch = "ppc64le" ] && [ ! -d /sys/firmware/opal ]; then + mod_dir="powerpc-ieee1275" + fi + + if [ -n "${mod_dir}" ]; then + for mod in blscfg increment; do + install -m 700 ${prefix}/lib/grub/${mod_dir}/${mod}.mod ${grubdir}/$mod_dir/ || exit 1 + done + fi + + cp -af "${GRUB_CONFIG_FILE}" "${GRUB_CONFIG_FILE}${backupsuffix}" + if ! grub2-mkconfig -o "${GRUB_CONFIG_FILE}" ; then + install -m 700 "${GRUB_CONFIG_FILE}${backupsuffix}" "${GRUB_CONFIG_FILE}" + sed -i"${backupsuffix}" \ + -e 's,^GRUB_ENABLE_BLSCFG=.*,GRUB_ENABLE_BLSCFG=false,' \ + "${etcdefaultgrub}" + gettext_printf "Updating %s failed\n" "${GRUB_CONFIG_FILE}" + exit 1 + fi +fi + +# Bye. +exit 0 diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in index 6a316a5ba7..01f1f4144e 100644 --- a/util/grub.d/00_header.in +++ b/util/grub.d/00_header.in @@ -27,6 +27,14 @@ export TEXTDOMAINDIR="@localedir@" . "$pkgdatadir/grub-mkconfig_lib" +if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ] && + [ "x${GRUB_FS}" = "xbtrfs" ] ; then + cat </dev/null || true` @@ -79,6 +84,258 @@ case x"$GRUB_FS" in ;; esac +populate_header_warn() +{ +if [ "x${BLS_POPULATE_MENU}" = "xtrue" ]; then + bls_parser="10_linux script" +else + bls_parser="blscfg command" +fi +cat </dev/null)) || : + + echo "${files[@]}" +} + +update_bls_cmdline() +{ + local cmdline="root=${LINUX_ROOT_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" + local -a files=($(get_sorted_bls)) + + if [ -w /etc/kernel ] && + [[ ! -f /etc/kernel/cmdline || + /etc/kernel/cmdline -ot /etc/default/grub ]]; then + # anaconda has the correct information to create this during install; + # afterward, grubby will take care of syncing on updates. If the user + # has modified /etc/default/grub, try to cope. + echo "$cmdline" > /etc/kernel/cmdline + fi + + for bls in "${files[@]}"; do + local options="${cmdline}" + if [ -z "${bls##*debug*}" ]; then + options="${options} ${GRUB_CMDLINE_LINUX_DEBUG}" + fi + options="$(echo "${options}" | sed -e 's/\//\\\//g')" + options="$(echo "${options}" | sed -e 's/\;/\\\;/g')" + options="$(echo "${options}" | sed -e 's/\\&/\\\\&/g')" + options="$(echo "${options}" | sed -e 's/\$/\\\$/g')" + sed -i -e "s/^options.*/options ${options}/" "${blsdir}/${bls}.conf" + done +} + +populate_menu() +{ + local -a files=($(get_sorted_bls)) + + gettext_printf "Generating boot entries from BLS files...\n" >&2 + + for bls in "${files[@]}"; do + read_config "${blsdir}/${bls}.conf" + + menu="${menu}menuentry '${title}' ${grub_arg} --id=${bls} {\n" + menu="${menu}\t linux ${linux} ${options}\n" + if [ -n "${initrd}" ] ; then + menu="${menu}\t initrd ${boot_prefix}${initrd}\n" + fi + menu="${menu}}\n\n" + done + # The printf command seems to be more reliable across shells for special character (\n, \t) evaluation + printf "$menu" +} + +# Make BLS the default if GRUB_ENABLE_BLSCFG was not set and grubby is not installed. +if [ -z "${GRUB_ENABLE_BLSCFG}" ] && ! command -v new-kernel-pkg >/dev/null; then + GRUB_ENABLE_BLSCFG="true" +fi + +if [ "x${GRUB_ENABLE_BLSCFG}" = "xtrue" ]; then + if [ x$dirname = x/ ]; then + if [ -z "${prepare_root_cache}" ]; then + prepare_grub_to_access_device ${GRUB_DEVICE} + fi + else + if [ -z "${prepare_boot_cache}" ]; then + prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} + fi + fi + + if [ -d /sys/firmware/efi ]; then + bootefi_device="`${grub_probe} --target=device /boot/efi/`" + prepare_grub_to_access_device_with_variable boot ${bootefi_device} + else + boot_device="`${grub_probe} --target=device /boot/`" + prepare_grub_to_access_device_with_variable boot ${boot_device} + fi + + arch="$(uname -m)" + if [ "x${arch}" = "xppc64le" ] && [ -d /sys/firmware/opal ]; then + + BLS_POPULATE_MENU="true" + petitboot_path="/sys/firmware/devicetree/base/ibm,firmware-versions/petitboot" + + if test -e ${petitboot_path}; then + read -r -d '' petitboot_version < ${petitboot_path} + petitboot_version="$(echo ${petitboot_version//v})" + + if test -n ${petitboot_version}; then + major_version="$(echo ${petitboot_version} | cut -d . -f1)" + minor_version="$(echo ${petitboot_version} | cut -d . -f2)" + + re='^[0-9]+$' + if [[ $major_version =~ $re ]] && [[ $minor_version =~ $re ]] && + ([[ ${major_version} -gt 1 ]] || + [[ ${major_version} -eq 1 && + ${minor_version} -ge 8 ]]); then + BLS_POPULATE_MENU="false" + fi + fi + fi + fi + + populate_header_warn + + cat << EOF +# The kernelopts variable should be defined in the grubenv file. But to ensure that menu +# entries populated from BootLoaderSpec files that use this variable work correctly even +# without a grubenv file, define a fallback kernelopts variable if this has not been set. +# +# The kernelopts variable in the grubenv file can be modified using the grubby tool or by +# executing the grub2-mkconfig tool. For the latter, the values of the GRUB_CMDLINE_LINUX +# and GRUB_CMDLINE_LINUX_DEFAULT options from /etc/default/grub file are used to set both +# the kernelopts variable in the grubenv file and the fallback kernelopts variable. +if [ -z "\${kernelopts}" ]; then + set kernelopts="root=${LINUX_ROOT_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" +fi +EOF + + if [ "x${GRUB_GRUBENV_UPDATE}" = "xyes" ]; then + update_bls_cmdline + fi + + if [ "x${BLS_POPULATE_MENU}" = "xtrue" ]; then + populate_menu + else + cat << EOF + +insmod blscfg +blscfg +EOF + fi + + if [ "x${GRUB_GRUBENV_UPDATE}" = "xyes" ]; then + blsdir="/boot/loader/entries" + [ -d "${blsdir}" ] && GRUB_BLS_FS="$(${grub_probe} --target=fs ${blsdir})" + if [ "x${GRUB_BLS_FS}" = "xbtrfs" ] || [ "x${GRUB_BLS_FS}" = "xzfs" ]; then + blsdir=$(make_system_path_relative_to_its_root "${blsdir}") + if [ "x${blsdir}" != "x/loader/entries" ] && [ "x${blsdir}" != "x/boot/loader/entries" ]; then + ${grub_editenv} - set blsdir="${blsdir}" + fi + fi + + if [ -n "${GRUB_EARLY_INITRD_LINUX_CUSTOM}" ]; then + ${grub_editenv} - set early_initrd="${GRUB_EARLY_INITRD_LINUX_CUSTOM}" + fi + + if [ -n "${GRUB_DEFAULT_DTB}" ]; then + ${grub_editenv} - set devicetree="${GRUB_DEFAULT_DTB}" + fi + + if [ -n "${GRUB_SAVEDEFAULT}" ]; then + ${grub_editenv} - set save_default="${GRUB_SAVEDEFAULT}" + fi + fi + + exit 0 +fi + +mktitle () +{ + local title_type + local version + local OS_NAME + local OS_VERS + + title_type=$1 && shift + version=$1 && shift + + OS_NAME="$(eval $(grep ^NAME= /etc/os-release) ; echo ${NAME})" + OS_VERS="$(eval $(grep ^VERSION= /etc/os-release) ; echo ${VERSION})" + + case $title_type in + recovery) + title=$(printf '%s (%s) %s (recovery mode)' \ + "${OS_NAME}" "${version}" "${OS_VERS}") + ;; + *) + title=$(printf '%s (%s) %s' \ + "${OS_NAME}" "${version}" "${OS_VERS}") + ;; + esac + echo -n ${title} +} + title_correction_code= linux_entry () @@ -86,23 +343,22 @@ linux_entry () os="$1" version="$2" type="$3" - args="$4" + isdebug="$4" + args="$5" if [ -z "$boot_device_id" ]; then boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")" fi + if [ x$type != xsimple ] ; then - case $type in - recovery) - title="$(gettext_printf "%s, with Linux %s (recovery mode)" "${os}" "${version}")" ;; - *) - title="$(gettext_printf "%s, with Linux %s" "${os}" "${version}")" ;; - esac + title=$(mktitle "$type" "$version") if [ x"$title" = x"$GRUB_ACTUAL_DEFAULT" ] || [ x"Previous Linux versions>$title" = x"$GRUB_ACTUAL_DEFAULT" ]; then replacement_title="$(echo "Advanced options for ${OS}" | sed 's,>,>>,g')>$(echo "$title" | sed 's,>,>>,g')" quoted="$(echo "$GRUB_ACTUAL_DEFAULT" | grub_quote)" title_correction_code="${title_correction_code}if [ \"x\$default\" = '$quoted' ]; then default='$(echo "$replacement_title" | grub_quote)'; fi;" - grub_warn "$(gettext_printf "Please don't use old title \`%s' for GRUB_DEFAULT, use \`%s' (for versions before 2.00) or \`%s' (for 2.00 or later)" "$GRUB_ACTUAL_DEFAULT" "$replacement_title" "gnulinux-advanced-$boot_device_id>gnulinux-$version-$type-$boot_device_id")" + fi + if [ x$isdebug = xdebug ]; then + title="$title${GRUB_LINUX_DEBUG_TITLE_POSTFIX}" fi echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option 'gnulinux-$version-$type-$boot_device_id' {" | sed "s/^/$submenu_indentation/" else @@ -155,6 +411,13 @@ EOF sed "s/^/$submenu_indentation/" << EOF echo '$(echo "$message" | grub_quote)' initrd $(echo $initrd_path) +EOF + fi + if test -n "${fdt}" ; then + message="$(gettext_printf "Loading fdt ...")" + sed "s/^/$submenu_indentation/" << EOF + echo '$(echo "$message" | grub_quote)' + devicetree ${rel_dirname}/${fdt} EOF fi sed "s/^/$submenu_indentation/" << EOF @@ -207,6 +470,7 @@ fi is_top_level=true for linux in ${reverse_sorted_list}; do gettext_printf "Found linux image: %s\n" "$linux" >&2 + basename=`basename $linux` dirname=`dirname $linux` rel_dirname=`make_system_path_relative_to_its_root $dirname` @@ -247,9 +511,19 @@ for linux in ${reverse_sorted_list}; do for i in ${initrd}; do initrd_display="${initrd_display} ${dirname}/${i}" done - gettext_printf "Found initrd image: %s\n" "$(echo $initrd_display)" >&2 + if [ "x${GRUB_ENABLE_BLSCFG}" != "xtrue" ]; then + gettext_printf "Found initrd image: %s\n" "$(echo $initrd_display)" >&2 + fi fi + fdt= + for i in "dtb-${version}" "dtb-${alt_version}"; do + if test -f "${dirname}/${i}/${GRUB_DEFAULT_DTB}" ; then + fdt="${i}/${GRUB_DEFAULT_DTB}" + break + fi + done + config= for i in "${dirname}/config-${version}" "${dirname}/config-${alt_version}" "/etc/kernels/kernel-config-${version}" ; do if test -e "${i}" ; then @@ -284,11 +558,15 @@ for linux in ${reverse_sorted_list}; do fi if [ "x$is_top_level" = xtrue ] && [ "x${GRUB_DISABLE_SUBMENU}" != xtrue ]; then - linux_entry "${OS}" "${version}" simple \ + linux_entry "${OS}" "${version}" simple standard \ "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" + if [ "x$GRUB_LINUX_MAKE_DEBUG" = "xtrue" ]; then + linux_entry "${OS}" "${version}" simple debug \ + "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT} ${GRUB_CMDLINE_LINUX_DEBUG}" + fi submenu_indentation="$grub_tab" - + if [ -z "$boot_device_id" ]; then boot_device_id="$(grub_get_device_id "${GRUB_DEVICE}")" fi @@ -297,10 +575,15 @@ for linux in ${reverse_sorted_list}; do is_top_level=false fi - linux_entry "${OS}" "${version}" advanced \ + linux_entry "${OS}" "${version}" advanced standard \ "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}" + if [ "x$GRUB_LINUX_MAKE_DEBUG" = "xtrue" ]; then + linux_entry "${OS}" "${version}" advanced debug \ + "${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT} ${GRUB_CMDLINE_LINUX_DEBUG}" + fi + if [ "x${GRUB_DISABLE_RECOVERY}" != "xtrue" ]; then - linux_entry "${OS}" "${version}" recovery \ + linux_entry "${OS}" "${version}" recovery standard \ "${GRUB_CMDLINE_LINUX_RECOVERY} ${GRUB_CMDLINE_LINUX}" fi done diff --git a/util/grub.d/10_reset_boot_success.in b/util/grub.d/10_reset_boot_success.in new file mode 100644 index 0000000000..ca04f9b699 --- /dev/null +++ b/util/grub.d/10_reset_boot_success.in @@ -0,0 +1,29 @@ +#! /bin/sh -e +# Reset Boot Success +# +# The 08_fallback_counting and 12_menu_auto_hide snippets rely on this one +# and need to be kept in sync. +# +# The boot_success var needs to be set to 1 from userspace to mark a boot successful. +cat << EOF +# Hiding the menu is ok if last boot was ok or if this is a first boot attempt to boot the entry +if [ "\${boot_success}" = "1" -o "\${boot_indeterminate}" = "1" ]; then + set menu_hide_ok=1 +else + set menu_hide_ok=0 +fi +# Reset boot_indeterminate after a successful boot +if [ "\${boot_success}" = "1" ] ; then + set boot_indeterminate=0 +# Avoid boot_indeterminate causing the menu to be hidden more than once +elif [ "\${boot_indeterminate}" = "1" ]; then + set boot_indeterminate=2 +fi +# Reset boot_success for current boot +set boot_success=0 +if [ "\${env_block}" ]; then + save_env -f "\${env_block}" boot_success boot_indeterminate +else + save_env boot_success boot_indeterminate +fi +EOF diff --git a/util/grub.d/12_menu_auto_hide.in b/util/grub.d/12_menu_auto_hide.in new file mode 100644 index 0000000000..c2c207232e --- /dev/null +++ b/util/grub.d/12_menu_auto_hide.in @@ -0,0 +1,39 @@ +#! /bin/sh +# Menu Auto Hide +# +# This snippet depends on 10_reset_boot_success and needs to be kept in sync. +# +# Disable / skip generating menu-auto-hide config parts on serial terminals +for x in ${GRUB_TERMINAL_INPUT} ${GRUB_TERMINAL_OUTPUT}; do + case "$x" in + serial*) + exit 0 + ;; + esac +done + +cat << EOF +if [ x\$feature_timeout_style = xy ] ; then + if [ "\${menu_show_once}" ]; then + unset menu_show_once + if [ "\${env_block}" ]; then + save_env -f "\${env_block}" menu_show_once + else + save_env menu_show_once + fi + set timeout_style=menu + set timeout=60 + elif [ "\${menu_auto_hide}" -a "\${menu_hide_ok}" = "1" ]; then + set orig_timeout_style=\${timeout_style} + set orig_timeout=\${timeout} + if [ "\${fastboot}" = "1" ]; then + # timeout_style=menu + timeout=0 avoids the countdown code keypress check + set timeout_style=menu + set timeout=0 + else + set timeout_style=hidden + set timeout=1 + fi + fi +fi +EOF diff --git a/util/grub.d/14_menu_show_once.in b/util/grub.d/14_menu_show_once.in new file mode 100755 index 0000000000..41b37b5ec7 --- /dev/null +++ b/util/grub.d/14_menu_show_once.in @@ -0,0 +1,17 @@ +#! /bin/sh +# Force the menu to be shown once, with a timeout of ${menu_show_once_timeout} +# if requested by ${menu_show_once_timeout} being set in the env. +cat << EOF +if [ x\$feature_timeout_style = xy ]; then + if [ "\${menu_show_once_timeout}" ]; then + set timeout_style=menu + set timeout="\${menu_show_once_timeout}" + unset menu_show_once_timeout + if [ "\${env_block}" ]; then + save_env -f "\${env_block}" menu_show_once_timeout + else + save_env menu_show_once_timeout + fi + fi +fi +EOF diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in index 94dd8be134..4ecf5deea1 100644 --- a/util/grub.d/20_linux_xen.in +++ b/util/grub.d/20_linux_xen.in @@ -29,9 +29,9 @@ export TEXTDOMAINDIR="@localedir@" CLASS="--class gnu-linux --class gnu --class os --class xen" if [ "x${GRUB_DISTRIBUTOR}" = "x" ] ; then - OS=GNU/Linux + OS="$(sed 's, release .*$,,g' /etc/system-release)" else - OS="${GRUB_DISTRIBUTOR} GNU/Linux" + OS="${GRUB_DISTRIBUTOR}" CLASS="--class $(echo ${GRUB_DISTRIBUTOR} | tr 'A-Z' 'a-z' | cut -d' ' -f1|LC_ALL=C sed 's,[^[:alnum:]_],_,g') ${CLASS}" fi @@ -75,10 +75,14 @@ fi case x"$GRUB_FS" in xbtrfs) + if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" = "xtrue" ]; then + GRUB_CMDLINE_LINUX="${GRUB_CMDLINE_LINUX} \${extra_cmdline}" + else rootsubvol="`make_system_path_relative_to_its_root /`" rootsubvol="${rootsubvol#/}" if [ "x${rootsubvol}" != x ]; then GRUB_CMDLINE_LINUX="rootflags=subvol=${rootsubvol} ${GRUB_CMDLINE_LINUX}" + fi fi;; xzfs) rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true` @@ -152,6 +156,7 @@ linux_entry_xsm () else xen_rm_opts="no-real-mode edd=off" fi + insmod ${xen_module} ${xen_loader} ${rel_xen_dirname}/${xen_basename} placeholder ${xen_args} \${xen_rm_opts} echo '$(echo "$lmessage" | grub_quote)' ${module_loader} ${rel_dirname}/${basename} placeholder root=${linux_root_device_thisversion} ro ${args} @@ -164,6 +169,7 @@ EOF initrd_path="${rel_dirname}/${i}" sed "s/^/$submenu_indentation/" << EOF echo '$(echo "$message" | grub_quote)' + insmod ${xen_module} ${module_loader} --nounzip $(echo $initrd_path) EOF done @@ -265,13 +271,16 @@ for current_xen in ${reverse_sorted_xen_list}; do echo " submenu '$(gettext_printf "Xen hypervisor, version %s" "${xen_version}" | grub_quote)' \$menuentry_id_option 'xen-hypervisor-$xen_version-$boot_device_id' {" fi if ($grub_file --is-arm64-efi $current_xen); then + xen_module="xen_boot" xen_loader="xen_hypervisor" module_loader="xen_module" else if ($grub_file --is-x86-multiboot2 $current_xen); then + xen_module="multiboot2" xen_loader="multiboot2" module_loader="module2" else + xen_module="multiboot" xen_loader="multiboot" module_loader="module" fi diff --git a/util/grub.d/20_ppc_terminfo.in b/util/grub.d/20_ppc_terminfo.in new file mode 100644 index 0000000000..10d6658682 --- /dev/null +++ b/util/grub.d/20_ppc_terminfo.in @@ -0,0 +1,114 @@ +#! /bin/sh +set -e + +# grub-mkconfig helper script. +# Copyright (C) 2006,2007,2008,2009,2010 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 . + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +bindir=@bindir@ +libdir=@libdir@ +. "@datadir@/@PACKAGE@/grub-mkconfig_lib" + +export TEXTDOMAIN=@PACKAGE@ +export TEXTDOMAINDIR=@localedir@ + +X=80 +Y=24 +TERMINAL=ofconsole + +argument () { + opt=$1 + shift + + if test $# -eq 0; then + echo "$0: option requires an argument -- '$opt'" 1>&2 + exit 1 + fi + echo $1 +} + +check_terminfo () { + + while test $# -gt 0 + do + option=$1 + shift + + case "$option" in + terminfo | TERMINFO) + ;; + + -g) + NEWXY=`argument $option "$@"` + NEWX=`echo $NEWXY | cut -d x -f 1` + NEWY=`echo $NEWXY | cut -d x -f 2` + + if [ ${NEWX} -ge 80 ] ; then + X=${NEWX} + else + echo "Warning: ${NEWX} is less than the minimum size of 80" + fi + + if [ ${NEWY} -ge 24 ] ; then + Y=${NEWY} + else + echo "Warning: ${NEWY} is less than the minimum size of 24" + fi + + shift + ;; + + *) +# # accept console or ofconsole +# if [ "$option" != "console" -a "$option" != "ofconsole" ] ; then +# echo "Error: GRUB_TERMINFO unknown console: $option" +# exit 1 +# fi +# # perfer console +# TERMINAL=console + # accept ofconsole + if [ "$option" != "ofconsole" ] ; then + echo "Error: GRUB_TERMINFO unknown console: $option" + exit 1 + fi + # perfer console + TERMINAL=ofconsole + ;; + esac + + done + +} + +if ! uname -m | grep -q ppc ; then + exit 0 +fi + +if [ "x${GRUB_TERMINFO}" != "x" ] ; then + F1=`echo ${GRUB_TERMINFO} | cut -d " " -f 1` + + if [ "${F1}" != "terminfo" ] ; then + echo "Error: GRUB_TERMINFO is set to \"${GRUB_TERMINFO}\" The first word should be terminfo." + exit 1 + fi + + check_terminfo ${GRUB_TERMINFO} +fi + +cat << EOF + terminfo -g ${X}x${Y} ${TERMINAL} +EOF diff --git a/util/grub.d/25_bli.in b/util/grub.d/25_bli.in index 26e27a0197..00a8d7159c 100644 --- a/util/grub.d/25_bli.in +++ b/util/grub.d/25_bli.in @@ -19,6 +19,8 @@ set -e cat << EOF if [ "\$grub_platform" = "efi" ]; then - insmod bli + if [ ! insmod bli ]; then + echo "bli module not available" + fi fi EOF diff --git a/util/grub.d/30_os-prober.in b/util/grub.d/30_os-prober.in index 656301eaf2..18fe02cefd 100644 --- a/util/grub.d/30_os-prober.in +++ b/util/grub.d/30_os-prober.in @@ -27,7 +27,6 @@ export TEXTDOMAINDIR="@localedir@" . "$pkgdatadir/grub-mkconfig_lib" if [ "x${GRUB_DISABLE_OS_PROBER}" = "xtrue" ]; then - grub_warn "$(gettext_printf "os-prober will not be executed to detect other bootable partitions.\nSystems on them will not be added to the GRUB boot configuration.\nCheck GRUB_DISABLE_OS_PROBER documentation entry.")" exit 0 fi @@ -36,8 +35,6 @@ if ! command -v os-prober > /dev/null || ! command -v linux-boot-prober > /dev/n exit 0 fi -grub_warn "$(gettext_printf "os-prober will be executed to detect other bootable partitions.\nIts output will be used to detect bootable binaries on them and create new boot entries.")" - OSPROBED="`os-prober | tr ' ' '^' | paste -s -d ' '`" if [ -z "${OSPROBED}" ] ; then # empty os-prober output, nothing doing @@ -45,68 +42,26 @@ if [ -z "${OSPROBED}" ] ; then fi osx_entry() { - if [ x$2 = x32 ]; then - # TRANSLATORS: it refers to kernel architecture (32-bit) - bitstr="$(gettext "(32-bit)")" - else - # TRANSLATORS: it refers to kernel architecture (64-bit) - bitstr="$(gettext "(64-bit)")" - fi + found_other_os=1 # TRANSLATORS: it refers on the OS residing on device %s onstr="$(gettext_printf "(on %s)" "${DEVICE}")" - cat << EOF -menuentry '$(echo "${LONGNAME} $bitstr $onstr" | grub_quote)' --class osx --class darwin --class os \$menuentry_id_option 'osprober-xnu-$2-$(grub_get_device_id "${DEVICE}")' { + hints="" + for hint in `"${grub_probe}" --device ${device} --target=efi_hints 2> /dev/null` ; do + hints="${hints} --hint=${hint}" + done + cat << EOF +menuentry '$(echo "${LONGNAME} $onstr" | grub_quote)' --class osx --class darwin --class os \$menuentry_id_option 'osprober-xnu-$2-$(grub_get_device_id "${DEVICE}")' { EOF save_default_entry | grub_add_tab prepare_grub_to_access_device ${DEVICE} | grub_add_tab cat << EOF + set gfxpayload=keep load_video - set do_resume=0 - if [ /var/vm/sleepimage -nt10 / ]; then - if xnu_resume /var/vm/sleepimage; then - set do_resume=1 - fi - fi - if [ \$do_resume = 0 ]; then - xnu_uuid ${OSXUUID} uuid - if [ -f /Extra/DSDT.aml ]; then - acpi -e /Extra/DSDT.aml - fi - if [ /kernelcache -nt /System/Library/Extensions ]; then - $1 /kernelcache boot-uuid=\${uuid} rd=*uuid - elif [ -f /System/Library/Kernels/kernel ]; then - $1 /System/Library/Kernels/kernel boot-uuid=\${uuid} rd=*uuid - xnu_kextdir /System/Library/Extensions - else - $1 /mach_kernel boot-uuid=\${uuid} rd=*uuid - if [ /System/Library/Extensions.mkext -nt /System/Library/Extensions ]; then - xnu_mkext /System/Library/Extensions.mkext - else - xnu_kextdir /System/Library/Extensions - fi - fi - if [ -f /Extra/Extensions.mkext ]; then - xnu_mkext /Extra/Extensions.mkext - fi - if [ -d /Extra/Extensions ]; then - xnu_kextdir /Extra/Extensions - fi - if [ -f /Extra/devprop.bin ]; then - xnu_devprop_load /Extra/devprop.bin - fi - if [ -f /Extra/splash.jpg ]; then - insmod jpeg - xnu_splash /Extra/splash.jpg - fi - if [ -f /Extra/splash.png ]; then - insmod png - xnu_splash /Extra/splash.png - fi - if [ -f /Extra/splash.tga ]; then - insmod tga - xnu_splash /Extra/splash.tga - fi - fi + insmod part_gpt + insmod hfsplus + search --no-floppy --fs-uuid --set=root ${hints} $(grub_get_device_id "${DEVICE}") + chainloader (\$root)/System/Library/CoreServices/boot.efi + boot } EOF } @@ -152,6 +107,7 @@ for OS in ${OSPROBED} ; do case ${BOOT} in chain) + found_other_os=1 onstr="$(gettext_printf "(on %s)" "${DEVICE}")" cat << EOF @@ -182,6 +138,7 @@ EOF EOF ;; efi) + found_other_os=1 EFIPATH=${DEVICE#*@} DEVICE=${DEVICE%@*} @@ -226,6 +183,7 @@ EOF LINITRD="${LINITRD#/boot}" fi + found_other_os=1 onstr="$(gettext_printf "(on %s)" "${DEVICE}")" recovery_params="$(echo "${LPARAMS}" | grep single)" || true counter=1 @@ -299,13 +257,15 @@ EOF echo "$title_correction_code" ;; macosx) - if [ "${UUID}" ]; then - OSXUUID="${UUID}" - osx_entry xnu_kernel 32 - osx_entry xnu_kernel64 64 - fi + for subdevice in ${DEVICE%[[:digit:]]*}* ; do + parttype="`"${grub_probe}" --device ${device} --target=gpt_parttype "${subdevice}" 2> /dev/null`" + if [[ "$parttype" = "426f6f74-0000-11aa-aa11-00306543ecac" ]]; then + DEVICE="${subdevice}" osx_entry + fi + done ;; hurd) + found_other_os=1 onstr="$(gettext_printf "(on %s)" "${DEVICE}")" cat << EOF menuentry '$(echo "${LONGNAME} $onstr" | grub_quote)' --class hurd --class gnu --class os \$menuentry_id_option 'osprober-gnuhurd-/boot/gnumach.gz-false-$(grub_get_device_id "${DEVICE}")' { @@ -332,6 +292,7 @@ EOF EOF ;; minix) + found_other_os=1 cat << EOF menuentry "${LONGNAME} (on ${DEVICE}, Multiboot)" { EOF @@ -348,3 +309,15 @@ EOF ;; esac done + +# We override the results of the menu_auto_hide code here, this is a bit ugly, +# but grub-mkconfig writes out the file linearly, so this is the only way +if [ "${found_other_os}" = "1" ]; then + cat << EOF +# Other OS found, undo autohiding of menu unless menu_auto_hide=2 +if [ "\${orig_timeout_style}" -a "\${menu_auto_hide}" != "2" ]; then + set timeout_style=\${orig_timeout_style} + set timeout=\${orig_timeout} +fi +EOF +fi diff --git a/util/grub.d/30_uefi-firmware.in b/util/grub.d/30_uefi-firmware.in index 1c2365ddbd..1ef31e08f6 100644 --- a/util/grub.d/30_uefi-firmware.in +++ b/util/grub.d/30_uefi-firmware.in @@ -32,8 +32,7 @@ gettext_printf "Adding boot menu entry for UEFI Firmware Settings ...\n" >&2 cat << EOF if [ "\$grub_platform" = "efi" ]; then - fwsetup --is-supported - if [ "\$?" = 0 ]; then + if [ fwsetup --is-supported ]; then menuentry '$LABEL' \$menuentry_id_option 'uefi-firmware' { fwsetup } diff --git a/util/misc.c b/util/misc.c index d545212d90..6e16a68d9a 100644 --- a/util/misc.c +++ b/util/misc.c @@ -101,7 +101,7 @@ grub_util_read_image (const char *path) } void -grub_util_write_image_at (const void *img, size_t size, off_t offset, FILE *out, +grub_util_write_image_at (const void *img, size_t size, grub_off_t offset, FILE *out, const char *name) { grub_util_info ("writing 0x%" GRUB_HOST_PRIxLONG_LONG " bytes at offset 0x%" @@ -190,14 +190,14 @@ grub_xputs_real (const char *str) void (*grub_xputs) (const char *str) = grub_xputs_real; -int +grub_uint64_t grub_dl_ref (grub_dl_t mod) { (void) mod; return 0; } -int +grub_uint64_t grub_dl_unref (grub_dl_t mod) { (void) mod; diff --git a/util/mkimage.c b/util/mkimage.c index 4237383aca..f92949d1df 100644 --- a/util/mkimage.c +++ b/util/mkimage.c @@ -883,10 +883,12 @@ void grub_install_generate_image (const char *dir, const char *prefix, FILE *out, const char *outname, char *mods[], char *memdisk_path, char **pubkey_paths, - size_t npubkeys, char *config_path, + size_t npubkeys, char **x509key_paths, + size_t nx509keys, char *config_path, const struct grub_install_image_target_desc *image_target, - int note, grub_compression_t comp, const char *dtb_path, - const char *sbat_path, int disable_shim_lock) + int note, size_t appsig_size, grub_compression_t comp, + const char *dtb_path, const char *sbat_path, + int disable_shim_lock, int disable_cli) { char *kernel_img, *core_img; size_t total_module_size, core_size; @@ -928,6 +930,19 @@ grub_install_generate_image (const char *dir, const char *prefix, } } + { + size_t i; + for (i = 0; i < nx509keys; i++) + { + size_t curs; + curs = ALIGN_ADDR (grub_util_get_image_size (x509key_paths[i])); + grub_util_info ("the size of x509 public key %u is 0x%" + GRUB_HOST_PRIxLONG_LONG, + (unsigned) i, (unsigned long long) curs); + total_module_size += curs + sizeof (struct grub_module_header); + } + } + if (memdisk_path) { memdisk_size = ALIGN_UP(grub_util_get_image_size (memdisk_path), 512); @@ -942,12 +957,15 @@ grub_install_generate_image (const char *dir, const char *prefix, total_module_size += dtb_size + sizeof (struct grub_module_header); } - if (sbat_path != NULL && image_target->id != IMAGE_EFI) - grub_util_error (_(".sbat section can be embedded into EFI images only")); + if (sbat_path != NULL && (image_target->id != IMAGE_EFI && image_target->id != IMAGE_PPC)) + grub_util_error (_("SBAT data can be added only to EFI or powerpc-ieee1275 images")); if (disable_shim_lock) total_module_size += sizeof (struct grub_module_header); + if (disable_cli) + total_module_size += sizeof (struct grub_module_header); + if (config_path) { config_size = ALIGN_ADDR (grub_util_get_image_size (config_path) + 1); @@ -1049,7 +1067,7 @@ grub_install_generate_image (const char *dir, const char *prefix, curs = grub_util_get_image_size (pubkey_paths[i]); header = (struct grub_module_header *) (kernel_img + offset); - header->type = grub_host_to_target32 (OBJ_TYPE_PUBKEY); + header->type = grub_host_to_target32 (OBJ_TYPE_GPG_PUBKEY); header->size = grub_host_to_target32 (curs + sizeof (*header)); offset += sizeof (*header); @@ -1058,6 +1076,26 @@ grub_install_generate_image (const char *dir, const char *prefix, } } + { + size_t i; + for (i = 0; i < nx509keys; i++) + { + size_t curs; + struct grub_module_header *header; + + curs = grub_util_get_image_size (x509key_paths[i]); + + header = (struct grub_module_header *) (kernel_img + offset); + header->type = grub_host_to_target32 (OBJ_TYPE_X509_PUBKEY); + header->size = grub_host_to_target32 (curs + sizeof (*header)); + offset += sizeof (*header); + + grub_util_load_image (x509key_paths[i], kernel_img + offset); + offset += ALIGN_ADDR (curs); + } + } + + if (memdisk_path) { struct grub_module_header *header; @@ -1094,6 +1132,16 @@ grub_install_generate_image (const char *dir, const char *prefix, offset += sizeof (*header); } + if (disable_cli) + { + struct grub_module_header *header; + + header = (struct grub_module_header *) (kernel_img + offset); + header->type = grub_host_to_target32 (OBJ_TYPE_DISABLE_CLI); + header->size = grub_host_to_target32 (sizeof (*header)); + offset += sizeof (*header); + } + if (config_path) { struct grub_module_header *header; @@ -1403,6 +1451,7 @@ grub_install_generate_image (const char *dir, const char *prefix, #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdangling-pointer" #endif + PE_OHDR (o32, o64, dll_characteristics) = grub_host_to_target16 (GRUB_PE32_NX_COMPAT); PE_OHDR (o32, o64, header_size) = grub_host_to_target32 (header_size); PE_OHDR (o32, o64, entry_addr) = grub_host_to_target32 (layout.start_address); PE_OHDR (o32, o64, image_base) = 0; @@ -1799,6 +1848,14 @@ grub_install_generate_image (const char *dir, const char *prefix, case IMAGE_I386_IEEE1275: { grub_uint64_t target_addr; + char *sbat = NULL; + if (sbat_path != NULL) + { + sbat_size = grub_util_get_image_size (sbat_path); + sbat = xmalloc (sbat_size); + grub_util_load_image (sbat_path, sbat); + layout.sbat_size = sbat_size; + } if (image_target->id == IMAGE_LOONGSON_ELF) { if (comp == GRUB_COMPRESSION_NONE) @@ -1810,11 +1867,11 @@ grub_install_generate_image (const char *dir, const char *prefix, else target_addr = image_target->link_addr; if (image_target->voidp_sizeof == 4) - grub_mkimage_generate_elf32 (image_target, note, &core_img, &core_size, - target_addr, &layout); + grub_mkimage_generate_elf32 (image_target, note, appsig_size, sbat, &core_img, + &core_size, target_addr, &layout); else - grub_mkimage_generate_elf64 (image_target, note, &core_img, &core_size, - target_addr, &layout); + grub_mkimage_generate_elf64 (image_target, note, appsig_size, sbat, &core_img, + &core_size, target_addr, &layout); } break; } diff --git a/util/setup.c b/util/setup.c index 87a889ff7e..7aab468b0e 100644 --- a/util/setup.c +++ b/util/setup.c @@ -406,7 +406,7 @@ SETUP (const char *dir, int is_ldm; grub_err_t err; grub_disk_addr_t *sectors; - int i; + unsigned int i; grub_fs_t fs; unsigned int nsec, maxsec; diff --git a/util/systemd/10-grub-logind-service.conf.in b/util/systemd/10-grub-logind-service.conf.in new file mode 100644 index 0000000000..f2d4ac0073 --- /dev/null +++ b/util/systemd/10-grub-logind-service.conf.in @@ -0,0 +1,2 @@ +[Service] +Environment=SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU=true diff --git a/util/systemd/grub-systemd-integration.service.in b/util/systemd/grub-systemd-integration.service.in new file mode 100644 index 0000000000..c81fb594ce --- /dev/null +++ b/util/systemd/grub-systemd-integration.service.in @@ -0,0 +1,8 @@ +[Unit] +Description=Grub2 systemctl reboot --boot-loader-menu=... support +Before=umount.target systemd-reboot.service +DefaultDependencies=no +ConditionPathExists=/run/systemd/reboot-to-boot-loader-menu + +[Service] +ExecStart=@libexecdir@/@grubdirname@/systemd-integration.sh diff --git a/util/systemd/systemd-integration.sh.in b/util/systemd/systemd-integration.sh.in new file mode 100644 index 0000000000..a4c071c5b0 --- /dev/null +++ b/util/systemd/systemd-integration.sh.in @@ -0,0 +1,11 @@ +#!/bin/sh + +TIMEOUT_USEC=$(cat /run/systemd/reboot-to-boot-loader-menu) +TIMEOUT=$(((TIMEOUT_USEC + 500000) / 1000000)) + +@grub_editenv@ - set menu_show_once_timeout=$TIMEOUT + +# Downstream RH / Fedora patch for compatibility with old, not (yet) +# regenerated grub.cfg files which miss the menu_show_once_timeout check +# this older grubenv variable leads to a fixed timeout of 60 seconds +@grub_editenv@ - set menu_show_once=1