ab7ed2db6e
Signed-off-by: Leo Sandoval <lsandova@redhat.com>
1391 lines
44 KiB
Diff
1391 lines
44 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Raymund Will <rw@suse.com>
|
|
Date: Tue, 2 Apr 2024 16:39:53 -0600
|
|
Subject: [PATCH] Add secureboot support on efi chainloader
|
|
|
|
Expand the chainloader to be able to verify the image by means of shim
|
|
lock protocol. The PE/COFF image is loaded and relocated by the
|
|
chainloader instead of calling LoadImage and StartImage UEFI boot
|
|
Service as they require positive verification result from keys enrolled
|
|
in KEK or DB. The shim will use MOK in addition to firmware enrolled
|
|
keys to verify the image.
|
|
|
|
The chainloader module could be used to load other UEFI bootloaders,
|
|
such as xen.efi, and could be signed by any of MOK, KEK or DB.
|
|
|
|
Based on https://build.opensuse.org/package/view_file/openSUSE:Factory/grub2/grub2-secureboot-chainloader.patch
|
|
|
|
Signed-off-by: Peter Jones <pjones@redhat.com>
|
|
|
|
Also:
|
|
|
|
commit cd7a8984d4fda905877b5bfe466339100156b3bc
|
|
Author: Raymund Will <rw@suse.com>
|
|
Date: Fri Apr 10 01:45:02 2015 -0400
|
|
|
|
Use device part of chainloader target, if present.
|
|
|
|
Otherwise chainloading is restricted to '$root', which might not even
|
|
be readable by EFI!
|
|
|
|
v1. use grub_file_get_device_name() to get device name
|
|
|
|
Signed-off-by: Michael Chang <mchang@suse.com>
|
|
Signed-off-by: Peter Jones <pjones@redhat.com>
|
|
|
|
Also:
|
|
|
|
commit 0872a2310a0eeac4ecfe9e1b49dd2d72ab373039
|
|
Author: Peter Jones <pjones@redhat.com>
|
|
Date: Fri Jun 10 14:06:15 2016 -0400
|
|
|
|
Rework even more of efi chainload so non-sb cases work right.
|
|
|
|
This ensures that if shim protocol is not loaded, or is loaded but shim
|
|
is disabled, we will fall back to a correct load method for the efi
|
|
chain loader.
|
|
|
|
Here's what I tested with this version:
|
|
|
|
results expected actual
|
|
------------------------------------------------------------
|
|
sb + enabled + shim + fedora success success
|
|
sb + enabled + shim + win success success
|
|
sb + enabled + grub + fedora fail fail
|
|
sb + enabled + grub + win fail fail
|
|
|
|
sb + mokdisabled + shim + fedora success success
|
|
sb + mokdisabled + shim + win success success
|
|
sb + mokdisabled + grub + fedora fail fail
|
|
sb + mokdisabled + grub + win fail fail
|
|
|
|
sb disabled + shim + fedora success success*
|
|
sb disabled + shim + win success success*
|
|
sb disabled + grub + fedora success success
|
|
sb disabled + grub + win success success
|
|
|
|
nosb + shim + fedora success success*
|
|
nosb + shim + win success success*
|
|
nosb + grub + fedora success success
|
|
nosb + grub + win success success
|
|
|
|
* for some reason shim protocol is being installed in these cases, and I
|
|
can't see why, but I think it may be this firmware build returning an
|
|
erroneous value. But this effectively falls back to the mokdisabled
|
|
behavior, which works correctly, and the presence of the "grub" (i.e.
|
|
no shim) tests effectively tests the desired behavior here.
|
|
|
|
Resolves: rhbz#1344512
|
|
|
|
Signed-off-by: Peter Jones <pjones@redhat.com>
|
|
|
|
Also:
|
|
|
|
commit ff7b1cb7f69487870211aeb69ff4f54470fbcb58
|
|
Author: Laszlo Ersek <lersek@redhat.com>
|
|
Date: Mon Nov 21 15:34:00 2016 +0100
|
|
|
|
efi/chainloader: fix wrong sanity check in relocate_coff()
|
|
|
|
In relocate_coff(), the relocation entries are parsed from the original
|
|
image (not the section-wise copied image). The original image is
|
|
pointed-to by the "orig" pointer. The current check
|
|
|
|
(void *)reloc_end < data
|
|
|
|
compares the addresses of independent memory allocations. "data" is a typo
|
|
here, it should be "orig".
|
|
|
|
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1347291
|
|
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
|
|
Tested-by: Bogdan Costescu <bcostescu@gmail.com>
|
|
Tested-by: Juan Orti <j.orti.alcaine@gmail.com>
|
|
|
|
Also:
|
|
|
|
commit ab4ba9997ad4832449e54d930fa2aac6a160d0e9
|
|
Author: Laszlo Ersek <lersek@redhat.com>
|
|
Date: Wed Nov 23 06:27:09 2016 +0100
|
|
|
|
efi/chainloader: truncate overlong relocation section
|
|
|
|
The UEFI Windows 7 boot loader ("EFI/Microsoft/Boot/bootmgfw.efi", SHA1
|
|
31b410e029bba87d2068c65a80b88882f9f8ea25) has inconsistent headers.
|
|
|
|
Compare:
|
|
|
|
> The Data Directory
|
|
> ...
|
|
> Entry 5 00000000000d9000 00000574 Base Relocation Directory [.reloc]
|
|
|
|
Versus:
|
|
|
|
> Sections:
|
|
> Idx Name Size VMA LMA File off ...
|
|
> ...
|
|
> 10 .reloc 00000e22 00000000100d9000 00000000100d9000 000a1800 ...
|
|
|
|
That is, the size reported by the RelocDir entry (0x574) is smaller than
|
|
the virtual size of the .reloc section (0xe22).
|
|
|
|
Quoting the grub2 debug log for the same:
|
|
|
|
> chainloader.c:595: reloc_dir: 0xd9000 reloc_size: 0x00000574
|
|
> chainloader.c:603: reloc_base: 0x7d208000 reloc_base_end: 0x7d208573
|
|
> ...
|
|
> chainloader.c:620: Section 10 ".reloc" at 0x7d208000..0x7d208e21
|
|
> chainloader.c:661: section is not reloc section?
|
|
> chainloader.c:663: rds: 0x00001000, vs: 00000e22
|
|
> chainloader.c:664: base: 0x7d208000 end: 0x7d208e21
|
|
> chainloader.c:666: reloc_base: 0x7d208000 reloc_base_end: 0x7d208573
|
|
> chainloader.c:671: Section characteristics are 42000040
|
|
> chainloader.c:673: Section virtual size: 00000e22
|
|
> chainloader.c:675: Section raw_data size: 00001000
|
|
> chainloader.c:678: Discarding section
|
|
|
|
After hexdumping "bootmgfw.efi" and manually walking its relocation blocks
|
|
(yes, really), I determined that the (smaller) RelocDir value is correct.
|
|
The remaining area that extends up to the .reloc section size (== 0xe22 -
|
|
0x574 == 0x8ae bytes) exists as zero padding in the file.
|
|
|
|
This zero padding shouldn't be passed to relocate_coff() for parsing. In
|
|
order to cope with it, split the handling of .reloc sections into the
|
|
following branches:
|
|
|
|
- original case (equal size): original behavior (--> relocation
|
|
attempted),
|
|
|
|
- overlong .reloc section (longer than reported by RelocDir): truncate the
|
|
section to the RelocDir size for the purposes of relocate_coff(), and
|
|
attempt relocation,
|
|
|
|
- .reloc section is too short, or other checks fail: original behavior
|
|
(--> relocation not attempted).
|
|
|
|
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1347291
|
|
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
|
|
|
|
Also:
|
|
|
|
commit cc06f149fbd2d8c1da1e83173d21629ba97e0d92
|
|
Author: Raymund Will <rw@suse.com>
|
|
|
|
chainloader: Define machine types for RISC-V
|
|
|
|
The commit "Add secureboot support on efi chainloader" didn't add machine
|
|
types for RISC-V, so this patch adds them.
|
|
|
|
Note, that grub-core/loader/riscv/linux.c is skipped because Linux is not
|
|
supported yet. This patch might need a new revision once that's the case.
|
|
|
|
Signed-off-by: David Abdurachmanov <david.abdurachmanov@sifive.com>
|
|
---
|
|
grub-core/loader/efi/chainloader.c | 849 +++++++++++++++++++++++++++++++++----
|
|
grub-core/loader/efi/linux.c | 25 +-
|
|
grub-core/loader/i386/efi/linux.c | 17 +-
|
|
include/grub/efi/linux.h | 2 +-
|
|
include/grub/efi/pe32.h | 51 ++-
|
|
5 files changed, 848 insertions(+), 96 deletions(-)
|
|
|
|
diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
|
|
index 2036924504b..e4ac39c87b4 100644
|
|
--- a/grub-core/loader/efi/chainloader.c
|
|
+++ b/grub-core/loader/efi/chainloader.c
|
|
@@ -32,6 +32,9 @@
|
|
#include <grub/efi/api.h>
|
|
#include <grub/efi/efi.h>
|
|
#include <grub/efi/disk.h>
|
|
+#include <grub/efi/pe32.h>
|
|
+#include <grub/efi/sb.h>
|
|
+#include <grub/efi/linux.h>
|
|
#include <grub/efi/memory.h>
|
|
#include <grub/command.h>
|
|
#include <grub/i18n.h>
|
|
@@ -45,13 +48,24 @@ GRUB_MOD_LICENSE ("GPLv3+");
|
|
|
|
static grub_dl_t my_mod;
|
|
|
|
+static grub_efi_physical_address_t address;
|
|
+static grub_efi_uintn_t pages;
|
|
+static grub_ssize_t fsize;
|
|
+static grub_efi_device_path_t *file_path;
|
|
+static grub_efi_handle_t image_handle;
|
|
+static grub_efi_char16_t *cmdline;
|
|
+static grub_ssize_t cmdline_len;
|
|
+static grub_efi_handle_t dev_handle;
|
|
+
|
|
+static grub_efi_status_t (__grub_efi_api *entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table);
|
|
+
|
|
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;
|
|
|
|
+ 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);
|
|
@@ -66,12 +80,12 @@ grub_chainloader_unload (void *context)
|
|
static grub_err_t
|
|
grub_chainloader_boot (void *context)
|
|
{
|
|
- 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;
|
|
|
|
+ image_handle = (grub_efi_handle_t) context;
|
|
b = grub_efi_system_table->boot_services;
|
|
status = b->start_image (image_handle, &exit_data_size, &exit_data);
|
|
if (status != GRUB_EFI_SUCCESS)
|
|
@@ -136,7 +150,7 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename)
|
|
char *dir_start;
|
|
char *dir_end;
|
|
grub_size_t size;
|
|
- grub_efi_device_path_t *d, *file_path;
|
|
+ grub_efi_device_path_t *d;
|
|
|
|
dir_start = grub_strchr (filename, ')');
|
|
if (! dir_start)
|
|
@@ -209,24 +223,701 @@ 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;
|
|
+#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 (void *data, grub_efi_uint32_t datasize)
|
|
+{
|
|
+ grub_efi_boot_services_t *b;
|
|
+ grub_efi_loaded_image_t *li, li_bak;
|
|
+ grub_efi_status_t efi_status;
|
|
+ char *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;
|
|
+
|
|
+ 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 = cmdline;
|
|
+ li->load_options_size = cmdline_len;
|
|
+ li->file_path = grub_efi_get_media_file_path (file_path);
|
|
+ li->device_handle = 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)
|
|
+{
|
|
+ grub_efi_boot_services_t *b;
|
|
+
|
|
+ b = grub_efi_system_table->boot_services;
|
|
+ b->free_pages (address, pages);
|
|
+ grub_free (file_path);
|
|
+ grub_free (cmdline);
|
|
+ cmdline = 0;
|
|
+ file_path = 0;
|
|
+ dev_handle = 0;
|
|
+
|
|
+ grub_dl_unref (my_mod);
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_load_and_start_image(void *boot_image)
|
|
+{
|
|
+ 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 = b->load_image (0, grub_efi_image_handle, file_path,
|
|
+ boot_image, fsize, &image_handle);
|
|
+ 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);
|
|
+ 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)
|
|
+{
|
|
+ int rc;
|
|
+ rc = handle_image ((void *)(unsigned long)address, fsize);
|
|
+ if (rc == 0)
|
|
+ {
|
|
+ grub_load_and_start_image((void *)(unsigned long)address);
|
|
+ }
|
|
+
|
|
+ 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_efi_device_path_t *dp = NULL;
|
|
char *filename;
|
|
void *boot_image = 0;
|
|
- grub_efi_handle_t dev_handle = 0;
|
|
- grub_efi_physical_address_t address = 0;
|
|
- grub_efi_uintn_t pages = 0;
|
|
- grub_efi_char16_t *cmdline = NULL;
|
|
- grub_efi_handle_t image_handle = NULL;
|
|
+ int rc;
|
|
+
|
|
+ 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 +927,42 @@ 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 (dev == NULL)
|
|
;
|
|
else if (dev->disk)
|
|
@@ -272,19 +993,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 +1016,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"),
|
|
@@ -308,7 +1026,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
|
|
}
|
|
|
|
#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 +1035,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 +1057,39 @@ 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)
|
|
+ rc = grub_linuxefi_secure_validate((void *)(unsigned long)address, fsize);
|
|
+ grub_dprintf ("chain", "linuxefi_secure_validate: %d\n", rc);
|
|
+ if (rc > 0)
|
|
{
|
|
- 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;
|
|
+ grub_file_close (file);
|
|
+ grub_device_close (dev);
|
|
+ grub_loader_set (grub_secureboot_chainloader_boot,
|
|
+ grub_secureboot_chainloader_unload, 0);
|
|
+ return 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)
|
|
+ else if (rc == 0)
|
|
{
|
|
- grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
|
|
- goto fail;
|
|
- }
|
|
- loaded_image->device_handle = dev_handle;
|
|
-
|
|
- /* Build load options with arguments from chainloader command line. */
|
|
- if (argc > 1)
|
|
- {
|
|
- 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_and_start_image(boot_image);
|
|
+ grub_file_close (file);
|
|
+ grub_device_close (dev);
|
|
+ grub_loader_set_ex (grub_chainloader_boot, grub_chainloader_unload, image_handle, 0);
|
|
|
|
- for (i = 1; i < argc; i++)
|
|
- {
|
|
- char *p8;
|
|
-
|
|
- p8 = argv[i];
|
|
- while (*p8)
|
|
- *(p16++) = *(p8++);
|
|
-
|
|
- *(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 (dev)
|
|
grub_device_close (dev);
|
|
@@ -421,6 +1103,9 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
|
|
if (address)
|
|
grub_efi_free_pages (address, pages);
|
|
|
|
+ if (cmdline)
|
|
+ grub_free (cmdline);
|
|
+
|
|
if (image_handle != NULL)
|
|
b->unload_image (image_handle);
|
|
|
|
diff --git a/grub-core/loader/efi/linux.c b/grub-core/loader/efi/linux.c
|
|
index 925394d1767..0a7f8208022 100644
|
|
--- a/grub-core/loader/efi/linux.c
|
|
+++ b/grub-core/loader/efi/linux.c
|
|
@@ -98,21 +98,36 @@ struct grub_efi_shim_lock
|
|
};
|
|
typedef struct grub_efi_shim_lock grub_efi_shim_lock_t;
|
|
|
|
-grub_efi_boolean_t
|
|
+int
|
|
grub_linuxefi_secure_validate (void *data, grub_uint32_t size)
|
|
{
|
|
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);
|
|
|
|
+ grub_dprintf ("secureboot", "shim_lock: %p\n", shim_lock);
|
|
if (!shim_lock)
|
|
- return 1;
|
|
+ {
|
|
+ grub_dprintf ("secureboot", "shim not available\n");
|
|
+ return 0;
|
|
+ }
|
|
|
|
- if (shim_lock->verify(data, size) == GRUB_EFI_SUCCESS)
|
|
- return 1;
|
|
+ grub_dprintf ("secureboot", "Asking shim to verify kernel signature\n");
|
|
+ status = shim_lock->verify (data, size);
|
|
+ grub_dprintf ("secureboot", "shim_lock->verify(): %ld\n", (long int)status);
|
|
+ if (status == GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ grub_dprintf ("secureboot", "Kernel signature verification passed\n");
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+ grub_dprintf ("secureboot", "Kernel signature verification failed (0x%lx)\n",
|
|
+ (unsigned long) status);
|
|
+
|
|
+ return -1;
|
|
|
|
- return 0;
|
|
}
|
|
|
|
#pragma GCC diagnostic push
|
|
diff --git a/grub-core/loader/i386/efi/linux.c b/grub-core/loader/i386/efi/linux.c
|
|
index 9bfb4ab9e3e..b59d125286d 100644
|
|
--- a/grub-core/loader/i386/efi/linux.c
|
|
+++ b/grub-core/loader/i386/efi/linux.c
|
|
@@ -117,6 +117,8 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
|
|
goto fail;
|
|
}
|
|
|
|
+ grub_dprintf ("linux", "initrd_mem = %lx\n", (unsigned long) initrd_mem);
|
|
+
|
|
params->ramdisk_size = size;
|
|
params->ramdisk_image = (grub_uint32_t)(grub_addr_t) initrd_mem;
|
|
|
|
@@ -159,6 +161,7 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
|
|
struct linux_i386_kernel_header lh;
|
|
grub_ssize_t len, start, filelen;
|
|
void *kernel = NULL;
|
|
+ int rc;
|
|
|
|
grub_dl_ref (my_mod);
|
|
|
|
@@ -184,11 +187,13 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
|
|
|
|
if (grub_file_read (file, kernel, filelen) != filelen)
|
|
{
|
|
- grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"), argv[0]);
|
|
+ grub_error (GRUB_ERR_FILE_READ_ERROR, N_("Can't read kernel %s"),
|
|
+ argv[0]);
|
|
goto fail;
|
|
}
|
|
|
|
- if (! grub_linuxefi_secure_validate (kernel, filelen))
|
|
+ rc = grub_linuxefi_secure_validate (kernel, filelen);
|
|
+ if (rc < 0)
|
|
{
|
|
grub_error (GRUB_ERR_INVALID_COMMAND, N_("%s has invalid signature"),
|
|
argv[0]);
|
|
@@ -203,6 +208,8 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
|
|
goto fail;
|
|
}
|
|
|
|
+ grub_dprintf ("linux", "params = %lx\n", (unsigned long) params);
|
|
+
|
|
grub_memset (params, 0, 16384);
|
|
|
|
grub_memcpy (&lh, kernel, sizeof (lh));
|
|
@@ -241,6 +248,9 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
|
|
goto fail;
|
|
}
|
|
|
|
+ grub_dprintf ("linux", "linux_cmdline = %lx\n",
|
|
+ (unsigned long)linux_cmdline);
|
|
+
|
|
grub_memcpy (linux_cmdline, LINUX_IMAGE, sizeof (LINUX_IMAGE));
|
|
grub_create_loader_cmdline (argc, argv,
|
|
linux_cmdline + sizeof (LINUX_IMAGE) - 1,
|
|
@@ -275,9 +285,10 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
|
|
grub_memcpy (params, &lh, 2 * 512);
|
|
|
|
params->type_of_loader = 0x21;
|
|
+ grub_dprintf("linux", "kernel_mem: %p handover_offset: %08x\n",
|
|
+ kernel_mem, handover_offset);
|
|
|
|
fail:
|
|
-
|
|
if (file)
|
|
grub_file_close (file);
|
|
|
|
diff --git a/include/grub/efi/linux.h b/include/grub/efi/linux.h
|
|
index d224daafa4f..b2cfd8c1f44 100644
|
|
--- a/include/grub/efi/linux.h
|
|
+++ b/include/grub/efi/linux.h
|
|
@@ -22,7 +22,7 @@
|
|
#include <grub/err.h>
|
|
#include <grub/symbol.h>
|
|
|
|
-grub_efi_boolean_t
|
|
+int
|
|
EXPORT_FUNC(grub_linuxefi_secure_validate) (void *data, grub_uint32_t size);
|
|
grub_err_t
|
|
EXPORT_FUNC(grub_efi_linux_boot) (void *kernel_address, grub_off_t offset,
|
|
diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h
|
|
index 9887e14b278..a39743396dc 100644
|
|
--- a/include/grub/efi/pe32.h
|
|
+++ b/include/grub/efi/pe32.h
|
|
@@ -238,7 +238,11 @@ struct grub_pe64_optional_header
|
|
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;
|
|
@@ -249,12 +253,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
|
|
@@ -263,10 +273,26 @@ 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_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
|
|
|
|
#if GRUB_TARGET_SIZEOF_VOID_P == 8
|
|
@@ -292,6 +318,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;
|