From 81072e718ac2ba2dcffdd36a4ff377657235f9ce Mon Sep 17 00:00:00 2001 From: Daniel Kiper Date: Fri, 3 Aug 2018 13:25:44 +0200 Subject: [PATCH] efi: Add EFI shim lock verifier This module provides shim lock verification for various kernels if UEFI secure boot is enabled on a machine. It is recommended to put this module into GRUB2 standalone image (avoid putting iorw and memrw modules into it; they are disallowed if UEFI secure boot is enabled). However, it is also possible to use it as a normal module. Though such configurations are more fragile and less secure due to various limitations. If the module is loaded and UEFI secure boot is enabled then: - module itself cannot be unloaded (persistent module), - the iorw and memrw modules cannot be loaded, - if the iorw and memrw modules are loaded then machine boot is disabled, - GRUB2 defers modules and ACPI tables verification to other verifiers. Signed-off-by: Daniel Kiper Reviewed-by: Ross Philipson --- docs/grub.texi | 15 +++ grub-core/Makefile.core.def | 6 ++ grub-core/commands/efi/shim_lock.c | 141 +++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 grub-core/commands/efi/shim_lock.c diff --git a/docs/grub.texi b/docs/grub.texi index 2f828f122..0f4b1bf8a 100644 --- a/docs/grub.texi +++ b/docs/grub.texi @@ -5613,6 +5613,7 @@ environment variables and commands are listed in the same order. @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 @end menu @node Authentication and authorisation @@ -5775,6 +5776,20 @@ 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 UEFI secure boot and shim +@section UEFI secure boot and shim support + +The GRUB, except the @command{chainloader} command, works with the UEFI secure +boot and the shim. This functionality is provided by the shim_lock module. It +is recommend to build in this and other required modules into the @file{core.img}. +All modules not stored in the @file{core.img} and the ACPI tables for the +@command{acpi} command have to be signed, e.g. using PGP. Additionally, the +@command{iorw} and the @command{memrw} commands are prohibited if the UEFI +secure boot is enabled. This is done due to security reasons. All above +mentioned requirements are enforced by the shim_lock module. And itself it +is a persistent module which means that it cannot be unloaded if it was +loaded into the memory. + @node Platform limitations @chapter Platform limitations diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index 449a6a87b..3a709ebf8 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -923,6 +923,12 @@ module = { common = commands/verifiers.c; }; +module = { + name = shim_lock; + common = commands/efi/shim_lock.c; + enable = x86_64_efi; +}; + module = { name = hdparm; common = commands/hdparm.c; diff --git a/grub-core/commands/efi/shim_lock.c b/grub-core/commands/efi/shim_lock.c new file mode 100644 index 000000000..01246b0fc --- /dev/null +++ b/grub-core/commands/efi/shim_lock.c @@ -0,0 +1,141 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2017 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 . + * + * EFI shim lock verifier. + */ + +#include +#include +#include +#include +#include +#include + +GRUB_MOD_LICENSE ("GPLv3+"); + +#define GRUB_EFI_SHIM_LOCK_GUID \ + { 0x605dab50, 0xe046, 0x4300, \ + { 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 } \ + } + +struct grub_efi_shim_lock_protocol +{ + grub_efi_status_t + (*verify) (void *buffer, grub_uint32_t size); +}; +typedef struct grub_efi_shim_lock_protocol grub_efi_shim_lock_protocol_t; + +static grub_efi_guid_t shim_lock_guid = GRUB_EFI_SHIM_LOCK_GUID; +static grub_efi_shim_lock_protocol_t *sl; + +/* List of modules which cannot be loaded if UEFI secure boot mode is enabled. */ +static const char * const disabled_mods[] = {"iorw", "memrw", NULL}; + +static grub_err_t +shim_lock_init (grub_file_t io, enum grub_file_type type, + void **context __attribute__ ((unused)), + enum grub_verify_flags *flags) +{ + const char *b, *e; + int i; + + *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION; + + if (!sl) + return GRUB_ERR_NONE; + + switch (type & GRUB_FILE_TYPE_MASK) + { + case GRUB_FILE_TYPE_GRUB_MODULE: + /* Establish GRUB module name. */ + b = grub_strrchr (io->name, '/'); + e = grub_strrchr (io->name, '.'); + + b = b ? (b + 1) : io->name; + e = e ? e : io->name + grub_strlen (io->name); + e = (e > b) ? e : io->name + grub_strlen (io->name); + + for (i = 0; disabled_mods[i]; i++) + if (!grub_strncmp (b, disabled_mods[i], grub_strlen (b) - grub_strlen (e))) + { + grub_error (GRUB_ERR_ACCESS_DENIED, + N_("module cannot be loaded in UEFI secure boot mode: %s"), + io->name); + return GRUB_ERR_ACCESS_DENIED; + } + + /* Fall through. */ + + case GRUB_FILE_TYPE_ACPI_TABLE: + *flags = GRUB_VERIFY_FLAGS_DEFER_AUTH; + + return GRUB_ERR_NONE; + + case GRUB_FILE_TYPE_LINUX_KERNEL: + case GRUB_FILE_TYPE_MULTIBOOT_KERNEL: + case GRUB_FILE_TYPE_BSD_KERNEL: + case GRUB_FILE_TYPE_XNU_KERNEL: + case GRUB_FILE_TYPE_PLAN9_KERNEL: + for (i = 0; disabled_mods[i]; i++) + if (grub_dl_get (disabled_mods[i])) + { + grub_error (GRUB_ERR_ACCESS_DENIED, + N_("cannot boot due to dangerous module in memory: %s"), + disabled_mods[i]); + return GRUB_ERR_ACCESS_DENIED; + } + + *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK; + + /* Fall through. */ + + default: + return GRUB_ERR_NONE; + } +} + +static grub_err_t +shim_lock_write (void *context __attribute__ ((unused)), void *buf, grub_size_t size) +{ + if (sl->verify (buf, size) != GRUB_EFI_SUCCESS) + return grub_error (GRUB_ERR_BAD_SIGNATURE, N_("bad shim signature")); + + return GRUB_ERR_NONE; +} + +struct grub_file_verifier shim_lock = + { + .name = "shim_lock", + .init = shim_lock_init, + .write = shim_lock_write + }; + +GRUB_MOD_INIT(shim_lock) +{ + sl = grub_efi_locate_protocol (&shim_lock_guid, 0); + grub_verifier_register (&shim_lock); + + if (!sl) + return; + + grub_dl_set_persistent (mod); +} + +GRUB_MOD_FINI(shim_lock) +{ + grub_verifier_unregister (&shim_lock); +}