From 25850cfd50abd226c0a08ab2ced16c9de2042767 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Tue, 23 Oct 2012 10:40:49 -0400 Subject: [PATCH 1/3] Don't allow insmod when secure boot is enabled. Hi, Fedora's patch to forbid insmod in UEFI Secure Boot environments is fine as far as it goes. However, the insmod command is not the only way that modules can be loaded. In particular, the 'normal' command, which implements the usual GRUB menu and the fully-featured command prompt, will implicitly load commands not currently loaded into memory. This permits trivial Secure Boot violations by writing commands implementing whatever you want to do and pointing $prefix at the malicious code. I'm currently test-building this patch (replacing your current grub-2.00-no-insmod-on-sb.patch), but this should be more correct. It moves the check into grub_dl_load_file. --- grub-core/kern/dl.c | 17 +++++++++++++++++ grub-core/kern/efi/efi.c | 28 ++++++++++++++++++++++++++++ include/grub/efi/efi.h | 1 + 3 files changed, 46 insertions(+) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 6850e0497..80da8706f 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -38,6 +38,14 @@ #define GRUB_MODULES_MACHINE_READONLY #endif +#ifdef GRUB_MACHINE_EMU +#include +#endif + +#ifdef GRUB_MACHINE_EFI +#include +#endif + #pragma GCC diagnostic ignored "-Wcast-align" @@ -680,6 +688,15 @@ grub_dl_load_file (const char *filename) void *core = 0; grub_dl_t mod = 0; +#ifdef GRUB_MACHINE_EFI + if (grub_efi_secure_boot ()) + { + grub_error (GRUB_ERR_ACCESS_DENIED, + "Secure Boot forbids loading module from %s", filename); + return 0; + } +#endif + grub_boot_time ("Loading module %s", filename); file = grub_file_open (filename); diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c index b9eb1ab1e..cd839cc98 100644 --- a/grub-core/kern/efi/efi.c +++ b/grub-core/kern/efi/efi.c @@ -259,6 +259,34 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid, return NULL; } +grub_efi_boolean_t +grub_efi_secure_boot (void) +{ + grub_efi_guid_t efi_var_guid = GRUB_EFI_GLOBAL_VARIABLE_GUID; + grub_size_t datasize; + char *secure_boot = NULL; + char *setup_mode = NULL; + grub_efi_boolean_t ret = 0; + + secure_boot = grub_efi_get_variable("SecureBoot", &efi_var_guid, &datasize); + + if (datasize != 1 || !secure_boot) + goto out; + + setup_mode = grub_efi_get_variable("SetupMode", &efi_var_guid, &datasize); + + if (datasize != 1 || !setup_mode) + goto out; + + if (*secure_boot && !*setup_mode) + ret = 1; + + out: + grub_free (secure_boot); + grub_free (setup_mode); + return ret; +} + #pragma GCC diagnostic ignored "-Wcast-align" /* Search the mods section from the PE32/PE32+ image. This code uses diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h index 9370fd530..a000c383e 100644 --- a/include/grub/efi/efi.h +++ b/include/grub/efi/efi.h @@ -72,6 +72,7 @@ EXPORT_FUNC (grub_efi_set_variable) (const char *var, const grub_efi_guid_t *guid, void *data, grub_size_t datasize); +grub_efi_boolean_t EXPORT_FUNC (grub_efi_secure_boot) (void); int EXPORT_FUNC (grub_efi_compare_device_paths) (const grub_efi_device_path_t *dp1, const grub_efi_device_path_t *dp2); From 9b669efb38a4bd1269fd83dc69ef58cfbd45f7c8 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 16 Apr 2015 16:30:53 -0700 Subject: [PATCH 2/3] Fail validation if we can't find shim and Secure Boot is enabled If grub is signed with a key that's in the trusted EFI keyring, an attacker can point a boot entry at grub rather than at shim and grub will fail to locate the shim verification protocol. This would then allow booting an arbitrary kernel image. Fail validation if Secure Boot is enabled and we can't find the shim protocol in order to prevent this. --- grub-core/loader/i386/efi/linux.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c index b79e6320b..57ccd4ec4 100644 --- a/grub-core/loader/i386/efi/linux.c +++ b/grub-core/loader/i386/efi/linux.c @@ -57,8 +57,12 @@ grub_linuxefi_secure_validate (void *data, grub_uint32_t size) shim_lock = grub_efi_locate_protocol(&guid, NULL); - if (!shim_lock) - return 1; + if (!shim_lock) { + if (grub_efi_secure_boot()) + return 0; + else + return 1; + } if (shim_lock->verify(data, size) == GRUB_EFI_SUCCESS) return 1; From 2755ecd1579871c025560c8b60d6d909c5dea9ec Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 17 Apr 2015 13:34:01 -0700 Subject: [PATCH 3/3] Add efi getenv command Add a command to obtain the contents of EFI firmware variables. --- grub-core/Makefile.core.def | 7 ++ grub-core/commands/efi/getenv.c | 153 ++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) create mode 100644 grub-core/commands/efi/getenv.c diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 5e12f1ace..a084b4110 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -821,6 +821,13 @@ module = { enable = x86_64_efi; }; +module = { + name = getenv; + common = commands/efi/getenv.c; + enable = i386_efi; + enable = x86_64_efi; +}; + module = { name = gptsync; common = commands/gptsync.c; diff --git a/grub-core/commands/efi/getenv.c b/grub-core/commands/efi/getenv.c new file mode 100644 index 000000000..5a829f5e4 --- /dev/null +++ b/grub-core/commands/efi/getenv.c @@ -0,0 +1,153 @@ +/* getenv.c - retrieve EFI variables. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 Free Software Foundation, Inc. + * Copyright (C) 2014 CoreOS, 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+"); + +static const struct grub_arg_option options_getenv[] = { + {"var-name", 'e', 0, + N_("Environment variable to query"), + N_("VARNAME"), ARG_TYPE_STRING}, + {"var-guid", 'g', 0, + N_("GUID of environment variable to query"), + N_("GUID"), ARG_TYPE_STRING}, + {"binary", 'b', 0, + N_("Read binary data and represent it as hex"), + 0, ARG_TYPE_NONE}, + {0, 0, 0, 0, 0, 0} +}; + +enum options_getenv +{ + GETENV_VAR_NAME, + GETENV_VAR_GUID, + GETENV_BINARY, +}; + +static grub_err_t +grub_cmd_getenv (grub_extcmd_context_t ctxt, int argc, char **args) +{ + struct grub_arg_list *state = ctxt->state; + char *envvar = NULL, *guid = NULL, *bindata = NULL, *data = NULL; + grub_size_t datasize; + grub_efi_guid_t efi_var_guid; + grub_efi_boolean_t binary = state[GETENV_BINARY].set; + unsigned int i; + + if (!state[GETENV_VAR_NAME].set || !state[GETENV_VAR_GUID].set) + { + grub_error (GRUB_ERR_INVALID_COMMAND, N_("-e and -g are required")); + goto done; + } + + if (argc != 1) + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unexpected arguments")); + goto done; + } + + envvar = state[GETENV_VAR_NAME].arg; + guid = state[GETENV_VAR_GUID].arg; + + if (grub_strlen(guid) != 36 || + guid[8] != '-' || + guid[13] != '-' || + guid[18] != '-' || + guid[23] != '-') + { + grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid GUID")); + goto done; + } + + /* Forgive me father for I have sinned */ + guid[8] = 0; + efi_var_guid.data1 = grub_strtoul(guid, NULL, 16); + guid[13] = 0; + efi_var_guid.data2 = grub_strtoul(guid + 9, NULL, 16); + guid[18] = 0; + efi_var_guid.data3 = grub_strtoul(guid + 14, NULL, 16); + efi_var_guid.data4[7] = grub_strtoul(guid + 34, NULL, 16); + guid[34] = 0; + efi_var_guid.data4[6] = grub_strtoul(guid + 32, NULL, 16); + guid[32] = 0; + efi_var_guid.data4[5] = grub_strtoul(guid + 30, NULL, 16); + guid[30] = 0; + efi_var_guid.data4[4] = grub_strtoul(guid + 28, NULL, 16); + guid[28] = 0; + efi_var_guid.data4[3] = grub_strtoul(guid + 26, NULL, 16); + guid[26] = 0; + efi_var_guid.data4[2] = grub_strtoul(guid + 24, NULL, 16); + guid[23] = 0; + efi_var_guid.data4[1] = grub_strtoul(guid + 21, NULL, 16); + guid[21] = 0; + efi_var_guid.data4[0] = grub_strtoul(guid + 19, NULL, 16); + + data = grub_efi_get_variable(envvar, &efi_var_guid, &datasize); + + if (!data || !datasize) + { + grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("No such variable")); + goto done; + } + + if (binary) + { + bindata = grub_zalloc(datasize * 2 + 1); + for (i=0; i