Skip to content

Commit

Permalink
Add support for Linux EFI stub loading.
Browse files Browse the repository at this point in the history
Also:

commit 71c843745f22f81e16d259e2e19c99bf3c1855c1
Author: Colin Watson <cjwatson@ubuntu.com>
Date:   Tue Oct 23 10:40:49 2012 -0400

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.
  • Loading branch information
Matthew Garrett authored and frozencemetery committed Mar 25, 2022
1 parent 1d6bfd6 commit 8e07346
Show file tree
Hide file tree
Showing 13 changed files with 615 additions and 66 deletions.
16 changes: 8 additions & 8 deletions grub-core/Makefile.core.def
Original file line number Diff line number Diff line change
Expand Up @@ -1734,13 +1734,6 @@ module = {
enable = i386_pc;
};


module = {
name = linux16;
common = loader/i386/pc/linux.c;
enable = x86;
};

module = {
name = ntldr;
i386_pc = loader/i386/pc/ntldr.c;
Expand Down Expand Up @@ -1796,7 +1789,9 @@ module = {

module = {
name = linux;
x86 = loader/i386/linux.c;
i386_pc = loader/i386/pc/linux.c;
x86_64_efi = loader/i386/efi/linux.c;
i386_efi = loader/i386/efi/linux.c;
i386_xen_pvh = loader/i386/linux.c;
xen = loader/i386/xen.c;
i386_pc = lib/i386/pc/vesa_modes_table.c;
Expand All @@ -1811,9 +1806,14 @@ module = {
arm64 = loader/arm64/linux.c;
riscv32 = loader/riscv/linux.c;
riscv64 = loader/riscv/linux.c;
emu = loader/emu/linux.c;
fdt = lib/fdt.c;

common = loader/linux.c;
common = lib/cmdline.c;
enable = noemu;

efi = loader/efi/linux.c;
};

module = {
Expand Down
21 changes: 21 additions & 0 deletions grub-core/kern/dl.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@
#define GRUB_MODULES_MACHINE_READONLY
#endif

#ifdef GRUB_MACHINE_EMU
#include <sys/mman.h>
#endif

#ifdef GRUB_MACHINE_EFI
#include <grub/efi/efi.h>
#endif



#pragma GCC diagnostic ignored "-Wcast-align"
Expand Down Expand Up @@ -695,6 +703,19 @@ grub_dl_load_file (const char *filename)
void *core = 0;
grub_dl_t mod = 0;

#ifdef GRUB_MACHINE_EFI
if (grub_efi_secure_boot ())
{
#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);
Expand Down
28 changes: 28 additions & 0 deletions grub-core/kern/efi/efi.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,34 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid,
return grub_efi_get_variable_with_attributes (var, guid, datasize_out, data_out, 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
Expand Down
32 changes: 32 additions & 0 deletions grub-core/kern/efi/mm.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,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 > 0xffffffff)
return 0;

b = grub_efi_system_table->boot_services;
status = efi_call_4 (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 = efi_call_4 (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,
Expand Down
112 changes: 58 additions & 54 deletions grub-core/loader/arm64/linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <grub/efi/efi.h>
#include <grub/efi/fdtload.h>
#include <grub/efi/memory.h>
#include <grub/efi/linux.h>
#include <grub/efi/pe32.h>
#include <grub/i18n.h>
#include <grub/lib/cmdline.h>
Expand All @@ -41,6 +42,7 @@ static int loaded;

static void *kernel_addr;
static grub_uint64_t kernel_size;
static grub_uint32_t handover_offset;

static char *linux_args;
static grub_uint32_t cmdline_size;
Expand All @@ -67,7 +69,8 @@ grub_arch_efi_linux_check_image (struct linux_arch_kernel_header * lh)
static grub_err_t
finalize_params_linux (void)
{
int node, retval;
grub_efi_loaded_image_t *loaded_image = NULL;
int node, retval, len;

void *fdt;

Expand Down Expand Up @@ -102,79 +105,70 @@ finalize_params_linux (void)
if (grub_fdt_install() != GRUB_ERR_NONE)
goto failure;

grub_dprintf ("linux", "Installed/updated FDT configuration table @ %p\n",
fdt);

/* Convert command line to UCS-2 */
loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle);
if (!loaded_image)
goto failure;

loaded_image->load_options_size = len =
(grub_strlen (linux_args) + 1) * sizeof (grub_efi_char16_t);
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_error(GRUB_ERR_BAD_OS, "failed to create kernel parameters");

loaded_image->load_options_size =
2 * grub_utf8_to_utf16 (loaded_image->load_options, len,
(grub_uint8_t *) linux_args, len, NULL);

return GRUB_ERR_NONE;

failure:
grub_fdt_unload();
return grub_error(GRUB_ERR_BAD_OS, "failed to install/update FDT");
}

grub_err_t
grub_arch_efi_linux_boot_image (grub_addr_t addr, grub_size_t size, char *args)
static void
free_params (void)
{
grub_efi_memory_mapped_device_path_t *mempath;
grub_efi_handle_t image_handle;
grub_efi_boot_services_t *b;
grub_efi_status_t status;
grub_efi_loaded_image_t *loaded_image;
int len;

mempath = grub_malloc (2 * sizeof (grub_efi_memory_mapped_device_path_t));
if (!mempath)
return grub_errno;

mempath[0].header.type = GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE;
mempath[0].header.subtype = GRUB_EFI_MEMORY_MAPPED_DEVICE_PATH_SUBTYPE;
mempath[0].header.length = grub_cpu_to_le16_compile_time (sizeof (*mempath));
mempath[0].memory_type = GRUB_EFI_LOADER_DATA;
mempath[0].start_address = addr;
mempath[0].end_address = addr + size;
grub_efi_loaded_image_t *loaded_image = NULL;

mempath[1].header.type = GRUB_EFI_END_DEVICE_PATH_TYPE;
mempath[1].header.subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
mempath[1].header.length = sizeof (grub_efi_device_path_t);

b = grub_efi_system_table->boot_services;
status = b->load_image (0, grub_efi_image_handle,
(grub_efi_device_path_t *) mempath,
(void *) addr, size, &image_handle);
if (status != GRUB_EFI_SUCCESS)
return grub_error (GRUB_ERR_BAD_OS, "cannot load image");
loaded_image = grub_efi_get_loaded_image (grub_efi_image_handle);
if (loaded_image)
{
if (loaded_image->load_options)
grub_efi_free_pages ((grub_efi_physical_address_t)(grub_efi_uintn_t)loaded_image->load_options,
GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
loaded_image->load_options = NULL;
loaded_image->load_options_size = 0;
}
}

grub_dprintf ("linux", "linux command line: '%s'\n", args);
grub_err_t
grub_arch_efi_linux_boot_image (grub_addr_t addr, char *args)
{
grub_err_t retval;

/* Convert command line to UCS-2 */
loaded_image = grub_efi_get_loaded_image (image_handle);
loaded_image->load_options_size = len =
(grub_strlen (args) + 1) * sizeof (grub_efi_char16_t);
loaded_image->load_options =
grub_efi_allocate_any_pages (GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
if (!loaded_image->load_options)
retval = finalize_params_linux ();
if (retval != GRUB_ERR_NONE)
return grub_errno;

loaded_image->load_options_size =
2 * grub_utf8_to_utf16 (loaded_image->load_options, len,
(grub_uint8_t *) args, len, NULL);

grub_dprintf ("linux", "starting image %p\n", image_handle);
status = b->start_image (image_handle, 0, NULL);
grub_dprintf ("linux", "linux command line: '%s'\n", args);

/* When successful, not reached */
b->unload_image (image_handle);
grub_efi_free_pages ((grub_addr_t) loaded_image->load_options,
GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
retval = grub_efi_linux_boot ((char *)addr, handover_offset, (void *)addr);

return grub_errno;
/* Never reached... */
free_params();
return retval;
}

static grub_err_t
grub_linux_boot (void)
{
if (finalize_params_linux () != GRUB_ERR_NONE)
return grub_errno;

return (grub_arch_efi_linux_boot_image((grub_addr_t)kernel_addr,
kernel_size, linux_args));
return (grub_arch_efi_linux_boot_image((grub_addr_t)kernel_addr, linux_args));
}

static grub_err_t
Expand Down Expand Up @@ -288,6 +282,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
{
grub_file_t file = 0;
struct linux_arch_kernel_header lh;
struct grub_armxx_linux_pe_header *pe;
grub_err_t err;

grub_dl_ref (my_mod);
Expand Down Expand Up @@ -333,6 +328,15 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),

grub_dprintf ("linux", "kernel @ %p\n", kernel_addr);

if (!grub_linuxefi_secure_validate (kernel_addr, kernel_size))
{
grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"), argv[0]);
goto fail;
}

pe = (void *)((unsigned long)kernel_addr + lh.hdr_offset);
handover_offset = pe->opt.entry_addr;

cmdline_size = grub_loader_cmdline_size (argc, argv) + sizeof (LINUX_IMAGE);
linux_args = grub_malloc (cmdline_size);
if (!linux_args)
Expand Down
1 change: 0 additions & 1 deletion grub-core/loader/arm64/xen_boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,6 @@ xen_boot (void)
return err;

return grub_arch_efi_linux_boot_image (xen_hypervisor->start,
xen_hypervisor->size,
xen_hypervisor->cmdline);
}

Expand Down
Loading

0 comments on commit 8e07346

Please sign in to comment.