3c4dd19
From bbf98517303e438cea398bb74306b6553c880ac3 Mon Sep 17 00:00:00 2001
b9efc54
From: Peter Jones <pjones@redhat.com>
b9efc54
Date: Tue, 6 Oct 2015 13:04:37 -0400
3c4dd19
Subject: [PATCH 151/229] Add secureboot support on efi chainloader
b9efc54
b9efc54
Expand the chainloader to be able to verify the image by means of shim
b9efc54
lock protocol. The PE/COFF image is loaded and relocated by the
b9efc54
chainloader instead of calling LoadImage and StartImage UEFI boot
b9efc54
Service as they require positive verification result from keys enrolled
b9efc54
in KEK or DB. The shim will use MOK in addition to firmware enrolled
b9efc54
keys to verify the image.
b9efc54
b9efc54
The chainloader module could be used to load other UEFI bootloaders,
b9efc54
such as xen.efi, and could be signed by any of MOK, KEK or DB.
b9efc54
b9efc54
Based on https://build.opensuse.org/package/view_file/openSUSE:Factory/grub2/grub2-secureboot-chainloader.patch
b9efc54
b9efc54
Signed-off-by: Peter Jones <pjones@redhat.com>
b9efc54
---
6f1e3d5
 grub-core/loader/efi/chainloader.c | 612 ++++++++++++++++++++++++++++++++++---
b9efc54
 include/grub/efi/pe32.h            |  20 +-
6f1e3d5
 2 files changed, 595 insertions(+), 37 deletions(-)
b9efc54
b9efc54
diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
81987f4
index 14ce6ddd7ad..87a91e16f17 100644
b9efc54
--- a/grub-core/loader/efi/chainloader.c
b9efc54
+++ b/grub-core/loader/efi/chainloader.c
b9efc54
@@ -32,6 +32,8 @@
b9efc54
 #include <grub/efi/api.h>
b9efc54
 #include <grub/efi/efi.h>
b9efc54
 #include <grub/efi/disk.h>
b9efc54
+#include <grub/efi/pe32.h>
b9efc54
+#include <grub/efi/linux.h>
b9efc54
 #include <grub/command.h>
b9efc54
 #include <grub/i18n.h>
b9efc54
 #include <grub/net.h>
b9efc54
@@ -46,9 +48,14 @@ static grub_dl_t my_mod;
b9efc54
 
b9efc54
 static grub_efi_physical_address_t address;
b9efc54
 static grub_efi_uintn_t pages;
b9efc54
+static grub_ssize_t fsize;
b9efc54
 static grub_efi_device_path_t *file_path;
b9efc54
 static grub_efi_handle_t image_handle;
b9efc54
 static grub_efi_char16_t *cmdline;
b9efc54
+static grub_ssize_t cmdline_len;
b9efc54
+static grub_efi_handle_t dev_handle;
b9efc54
+
b9efc54
+static grub_efi_status_t (*entry_point) (grub_efi_handle_t image_handle, grub_efi_system_table_t *system_table);
b9efc54
 
b9efc54
 static grub_err_t
b9efc54
 grub_chainloader_unload (void)
b9efc54
@@ -63,6 +70,7 @@ grub_chainloader_unload (void)
b9efc54
   grub_free (cmdline);
b9efc54
   cmdline = 0;
b9efc54
   file_path = 0;
b9efc54
+  dev_handle = 0;
b9efc54
 
b9efc54
   grub_dl_unref (my_mod);
b9efc54
   return GRUB_ERR_NONE;
6f1e3d5
@@ -191,12 +199,523 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename)
b9efc54
   return file_path;
b9efc54
 }
b9efc54
 
b9efc54
+#define SHIM_LOCK_GUID \
b9efc54
+  { 0x605dab50, 0xe046, 0x4300, { 0xab,0xb6,0x3d,0xd8,0x10,0xdd,0x8b,0x23 } }
b9efc54
+
b9efc54
+typedef union
b9efc54
+{
b9efc54
+  struct grub_pe32_header_32 pe32;
b9efc54
+  struct grub_pe32_header_64 pe32plus;
b9efc54
+} grub_pe_header_t;
b9efc54
+
b9efc54
+struct pe_coff_loader_image_context
b9efc54
+{
b9efc54
+  grub_efi_uint64_t image_address;
b9efc54
+  grub_efi_uint64_t image_size;
b9efc54
+  grub_efi_uint64_t entry_point;
b9efc54
+  grub_efi_uintn_t size_of_headers;
b9efc54
+  grub_efi_uint16_t image_type;
b9efc54
+  grub_efi_uint16_t number_of_sections;
b9efc54
+  grub_efi_uint32_t section_alignment;
b9efc54
+  struct grub_pe32_section_table *first_section;
b9efc54
+  struct grub_pe32_data_directory *reloc_dir;
b9efc54
+  struct grub_pe32_data_directory *sec_dir;
b9efc54
+  grub_efi_uint64_t number_of_rva_and_sizes;
b9efc54
+  grub_pe_header_t *pe_hdr;
b9efc54
+};
b9efc54
+
b9efc54
+typedef struct pe_coff_loader_image_context pe_coff_loader_image_context_t;
b9efc54
+
b9efc54
+struct grub_efi_shim_lock
b9efc54
+{
b9efc54
+  grub_efi_status_t (*verify)(void *buffer,
b9efc54
+                              grub_efi_uint32_t size);
b9efc54
+  grub_efi_status_t (*hash)(void *data,
b9efc54
+                            grub_efi_int32_t datasize,
b9efc54
+                            pe_coff_loader_image_context_t *context,
b9efc54
+                            grub_efi_uint8_t *sha256hash,
b9efc54
+                            grub_efi_uint8_t *sha1hash);
b9efc54
+  grub_efi_status_t (*context)(void *data,
b9efc54
+                               grub_efi_uint32_t size,
b9efc54
+                               pe_coff_loader_image_context_t *context);
b9efc54
+};
b9efc54
+
b9efc54
+typedef struct grub_efi_shim_lock grub_efi_shim_lock_t;
b9efc54
+
b9efc54
+static grub_efi_boolean_t
b9efc54
+read_header (void *data, grub_efi_uint32_t size,
b9efc54
+	     pe_coff_loader_image_context_t *context)
b9efc54
+{
b9efc54
+  grub_efi_guid_t guid = SHIM_LOCK_GUID;
b9efc54
+  grub_efi_shim_lock_t *shim_lock;
b9efc54
+  grub_efi_status_t status;
b9efc54
+
b9efc54
+  shim_lock = grub_efi_locate_protocol (&guid, NULL);
b9efc54
+
b9efc54
+  if (!shim_lock)
b9efc54
+    {
b9efc54
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "no shim lock protocol");
b9efc54
+      return 0;
b9efc54
+    }
b9efc54
+
b9efc54
+  status = shim_lock->context (data, size, context);
b9efc54
+
b9efc54
+  if (status == GRUB_EFI_SUCCESS)
b9efc54
+    {
b9efc54
+      grub_dprintf ("chain", "context success\n");
b9efc54
+      return 1;
b9efc54
+    }
b9efc54
+
b9efc54
+  switch (status)
b9efc54
+    {
b9efc54
+      case GRUB_EFI_UNSUPPORTED:
b9efc54
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "context error unsupported");
b9efc54
+      break;
b9efc54
+      case GRUB_EFI_INVALID_PARAMETER:
b9efc54
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "context error invalid parameter");
b9efc54
+      break;
b9efc54
+      default:
b9efc54
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "context error code");
b9efc54
+      break;
b9efc54
+    }
b9efc54
+
b9efc54
+  return 0;
b9efc54
+}
b9efc54
+
b9efc54
+static void*
b9efc54
+image_address (void *image, grub_efi_uint64_t sz, grub_efi_uint64_t adr)
b9efc54
+{
b9efc54
+  if (adr > sz)
b9efc54
+    return NULL;
b9efc54
+
b9efc54
+  return ((grub_uint8_t*)image + adr);
b9efc54
+}
b9efc54
+
b9efc54
+static int
b9efc54
+image_is_64_bit (grub_pe_header_t *pe_hdr)
b9efc54
+{
b9efc54
+  /* .Magic is the same offset in all cases */
b9efc54
+  if (pe_hdr->pe32plus.optional_header.magic == GRUB_PE32_PE64_MAGIC)
b9efc54
+    return 1;
b9efc54
+  return 0;
b9efc54
+}
b9efc54
+
b9efc54
+static const grub_uint16_t machine_type =
b9efc54
+#if defined(__x86_64__)
b9efc54
+  GRUB_PE32_MACHINE_X86_64;
b9efc54
+#elif defined(__aarch64__)
b9efc54
+  GRUB_PE32_MACHINE_ARM64;
b9efc54
+#elif defined(__arm__)
b9efc54
+  GRUB_PE32_MACHINE_ARMTHUMB_MIXED;
b9efc54
+#elif defined(__i386__) || defined(__i486__) || defined(__i686__)
b9efc54
+  GRUB_PE32_MACHINE_I386;
b9efc54
+#elif defined(__ia64__)
b9efc54
+  GRUB_PE32_MACHINE_IA64;
b9efc54
+#else
b9efc54
+#error this architecture is not supported by grub2
b9efc54
+#endif
b9efc54
+
b9efc54
+static grub_efi_status_t
b9efc54
+relocate_coff (pe_coff_loader_image_context_t *context,
b9efc54
+	       struct grub_pe32_section_table *section,
b9efc54
+	       void *orig, void *data)
b9efc54
+{
b9efc54
+  struct grub_pe32_data_directory *reloc_base, *reloc_base_end;
b9efc54
+  grub_efi_uint64_t adjust;
b9efc54
+  struct grub_pe32_fixup_block *reloc, *reloc_end;
b9efc54
+  char *fixup, *fixup_base, *fixup_data = NULL;
b9efc54
+  grub_efi_uint16_t *fixup_16;
b9efc54
+  grub_efi_uint32_t *fixup_32;
b9efc54
+  grub_efi_uint64_t *fixup_64;
b9efc54
+  grub_efi_uint64_t size = context->image_size;
b9efc54
+  void *image_end = (char *)orig + size;
b9efc54
+  int n = 0;
b9efc54
+
b9efc54
+  if (image_is_64_bit (context->pe_hdr))
b9efc54
+    context->pe_hdr->pe32plus.optional_header.image_base =
b9efc54
+      (grub_uint64_t)(unsigned long)data;
b9efc54
+  else
b9efc54
+    context->pe_hdr->pe32.optional_header.image_base =
b9efc54
+      (grub_uint32_t)(unsigned long)data;
b9efc54
+
b9efc54
+  /* Alright, so here's how this works:
b9efc54
+   *
b9efc54
+   * context->reloc_dir gives us two things:
b9efc54
+   * - the VA the table of base relocation blocks are (maybe) to be
b9efc54
+   *   mapped at (reloc_dir->rva)
b9efc54
+   * - the virtual size (reloc_dir->size)
b9efc54
+   *
b9efc54
+   * The .reloc section (section here) gives us some other things:
b9efc54
+   * - the name! kind of. (section->name)
b9efc54
+   * - the virtual size (section->virtual_size), which should be the same
b9efc54
+   *   as RelocDir->Size
b9efc54
+   * - the virtual address (section->virtual_address)
b9efc54
+   * - the file section size (section->raw_data_size), which is
b9efc54
+   *   a multiple of optional_header->file_alignment.  Only useful for image
b9efc54
+   *   validation, not really useful for iteration bounds.
b9efc54
+   * - the file address (section->raw_data_offset)
b9efc54
+   * - a bunch of stuff we don't use that's 0 in our binaries usually
b9efc54
+   * - Flags (section->characteristics)
b9efc54
+   *
b9efc54
+   * and then the thing that's actually at the file address is an array
b9efc54
+   * of struct grub_pe32_fixup_block structs with some values packed behind
b9efc54
+   * them.  The block_size field of this structure includes the
b9efc54
+   * structure itself, and adding it to that structure's address will
b9efc54
+   * yield the next entry in the array.
b9efc54
+   */
b9efc54
+
b9efc54
+  reloc_base = image_address (orig, size, section->raw_data_offset);
b9efc54
+  reloc_base_end = image_address (orig, size, section->raw_data_offset
b9efc54
+				  + section->virtual_size - 1);
b9efc54
+
b9efc54
+  grub_dprintf ("chain", "reloc_base %p reloc_base_end %p\n", reloc_base,
b9efc54
+		reloc_base_end);
b9efc54
+
b9efc54
+  if (!reloc_base && !reloc_base_end)
b9efc54
+    return GRUB_EFI_SUCCESS;
b9efc54
+
b9efc54
+  if (!reloc_base || !reloc_base_end)
b9efc54
+    {
b9efc54
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc table overflows binary");
b9efc54
+      return GRUB_EFI_UNSUPPORTED;
b9efc54
+    }
b9efc54
+
b9efc54
+  adjust = (grub_uint64_t)data - context->image_address;
b9efc54
+  if (adjust == 0)
b9efc54
+    return GRUB_EFI_SUCCESS;
b9efc54
+
b9efc54
+  while (reloc_base < reloc_base_end)
b9efc54
+    {
b9efc54
+      grub_uint16_t *entry;
b9efc54
+      reloc = (struct grub_pe32_fixup_block *)((char*)reloc_base);
b9efc54
+
b9efc54
+      if ((reloc_base->size == 0) ||
b9efc54
+	  (reloc_base->size > context->reloc_dir->size))
b9efc54
+	{
b9efc54
+	  grub_error (GRUB_ERR_BAD_ARGUMENT,
b9efc54
+		      "Reloc %d block size %d is invalid\n", n,
b9efc54
+		      reloc_base->size);
b9efc54
+	  return GRUB_EFI_UNSUPPORTED;
b9efc54
+	}
b9efc54
+
b9efc54
+      entry = &reloc->entries[0];
b9efc54
+      reloc_end = (struct grub_pe32_fixup_block *)
b9efc54
+	((char *)reloc_base + reloc_base->size);
b9efc54
+
b9efc54
+      if ((void *)reloc_end < data || (void *)reloc_end > image_end)
b9efc54
+        {
b9efc54
+          grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc entry %d overflows binary",
b9efc54
+		      n);
b9efc54
+          return GRUB_EFI_UNSUPPORTED;
b9efc54
+        }
b9efc54
+
b9efc54
+      fixup_base = image_address(data, size, reloc_base->rva);
b9efc54
+
b9efc54
+      if (!fixup_base)
b9efc54
+        {
b9efc54
+          grub_error (GRUB_ERR_BAD_ARGUMENT, "Reloc %d Invalid fixupbase", n);
b9efc54
+          return GRUB_EFI_UNSUPPORTED;
b9efc54
+        }
b9efc54
+
b9efc54
+      while ((void *)entry < (void *)reloc_end)
b9efc54
+        {
b9efc54
+          fixup = fixup_base + (*entry & 0xFFF);
b9efc54
+          switch ((*entry) >> 12)
b9efc54
+            {
b9efc54
+              case GRUB_PE32_REL_BASED_ABSOLUTE:
b9efc54
+                break;
b9efc54
+              case GRUB_PE32_REL_BASED_HIGH:
b9efc54
+                fixup_16 = (grub_uint16_t *)fixup;
b9efc54
+                *fixup_16 = (grub_uint16_t)
b9efc54
+		  (*fixup_16 + ((grub_uint16_t)((grub_uint32_t)adjust >> 16)));
b9efc54
+                if (fixup_data != NULL)
b9efc54
+                  {
b9efc54
+                    *(grub_uint16_t *) fixup_data = *fixup_16;
b9efc54
+                    fixup_data = fixup_data + sizeof (grub_uint16_t);
b9efc54
+                  }
b9efc54
+                break;
b9efc54
+              case GRUB_PE32_REL_BASED_LOW:
b9efc54
+                fixup_16 = (grub_uint16_t *)fixup;
b9efc54
+                *fixup_16 = (grub_uint16_t) (*fixup_16 + (grub_uint16_t)adjust);
b9efc54
+                if (fixup_data != NULL)
b9efc54
+                  {
b9efc54
+                    *(grub_uint16_t *) fixup_data = *fixup_16;
b9efc54
+                    fixup_data = fixup_data + sizeof (grub_uint16_t);
b9efc54
+                  }
b9efc54
+                break;
b9efc54
+              case GRUB_PE32_REL_BASED_HIGHLOW:
b9efc54
+                fixup_32 = (grub_uint32_t *)fixup;
b9efc54
+                *fixup_32 = *fixup_32 + (grub_uint32_t)adjust;
b9efc54
+                if (fixup_data != NULL)
b9efc54
+                  {
b9efc54
+                    fixup_data = (char *)ALIGN_UP ((grub_addr_t)fixup_data, sizeof (grub_uint32_t));
b9efc54
+                    *(grub_uint32_t *) fixup_data = *fixup_32;
b9efc54
+                    fixup_data += sizeof (grub_uint32_t);
b9efc54
+                  }
b9efc54
+                break;
b9efc54
+              case GRUB_PE32_REL_BASED_DIR64:
b9efc54
+                fixup_64 = (grub_uint64_t *)fixup;
b9efc54
+                *fixup_64 = *fixup_64 + (grub_uint64_t)adjust;
b9efc54
+                if (fixup_data != NULL)
b9efc54
+                  {
b9efc54
+                    fixup_data = (char *)ALIGN_UP ((grub_addr_t)fixup_data, sizeof (grub_uint64_t));
b9efc54
+                    *(grub_uint64_t *) fixup_data = *fixup_64;
b9efc54
+                    fixup_data += sizeof (grub_uint64_t);
b9efc54
+                  }
b9efc54
+                break;
b9efc54
+              default:
b9efc54
+                grub_error (GRUB_ERR_BAD_ARGUMENT,
b9efc54
+			    "Reloc %d unknown relocation type %d",
b9efc54
+			    n, (*entry) >> 12);
b9efc54
+                return GRUB_EFI_UNSUPPORTED;
b9efc54
+            }
b9efc54
+          entry += 1;
b9efc54
+        }
b9efc54
+      reloc_base = (struct grub_pe32_data_directory *)reloc_end;
b9efc54
+      n++;
b9efc54
+    }
b9efc54
+
b9efc54
+  return GRUB_EFI_SUCCESS;
b9efc54
+}
b9efc54
+
b9efc54
+static grub_efi_device_path_t *
b9efc54
+grub_efi_get_media_file_path (grub_efi_device_path_t *dp)
b9efc54
+{
b9efc54
+  while (1)
b9efc54
+    {
b9efc54
+      grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
b9efc54
+      grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
b9efc54
+
b9efc54
+      if (type == GRUB_EFI_END_DEVICE_PATH_TYPE)
b9efc54
+        break;
b9efc54
+      else if (type == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
b9efc54
+            && subtype == GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE)
b9efc54
+      return dp;
b9efc54
+
b9efc54
+      dp = GRUB_EFI_NEXT_DEVICE_PATH (dp);
b9efc54
+    }
b9efc54
+
b9efc54
+    return NULL;
b9efc54
+}
b9efc54
+
b9efc54
+static grub_efi_boolean_t
b9efc54
+handle_image (void *data, grub_efi_uint32_t datasize)
b9efc54
+{
6f1e3d5
+  grub_efi_boot_services_t *b;
b9efc54
+  grub_efi_loaded_image_t *li, li_bak;
b9efc54
+  grub_efi_status_t efi_status;
6f1e3d5
+  char *buffer = NULL;
b9efc54
+  char *buffer_aligned = NULL;
b9efc54
+  grub_efi_uint32_t i, size;
b9efc54
+  struct grub_pe32_section_table *section;
b9efc54
+  char *base, *end;
b9efc54
+  pe_coff_loader_image_context_t context;
b9efc54
+  grub_uint32_t section_alignment;
b9efc54
+  grub_uint32_t buffer_size;
b9efc54
+
6f1e3d5
+  b = grub_efi_system_table->boot_services;
6f1e3d5
+
b9efc54
+  if (read_header (data, datasize, &context))
b9efc54
+    {
b9efc54
+      grub_dprintf ("chain", "Succeed to read header\n");
b9efc54
+    }
b9efc54
+  else
b9efc54
+    {
b9efc54
+      grub_dprintf ("chain", "Failed to read header\n");
b9efc54
+      goto error_exit;
b9efc54
+    }
b9efc54
+
b9efc54
+  section_alignment = context.section_alignment;
b9efc54
+  buffer_size = context.image_size + section_alignment;
b9efc54
+
6f1e3d5
+  efi_status = efi_call_3 (b->allocate_pool, GRUB_EFI_LOADER_DATA,
6f1e3d5
+			   buffer_size, &buffer);
6f1e3d5
+
b9efc54
+  if (efi_status != GRUB_EFI_SUCCESS)
b9efc54
+    {
b9efc54
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
b9efc54
+      goto error_exit;
b9efc54
+    }
b9efc54
+
b9efc54
+  buffer_aligned = (char *)ALIGN_UP ((grub_addr_t)buffer, section_alignment);
b9efc54
+
b9efc54
+  if (!buffer_aligned)
b9efc54
+    {
b9efc54
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
b9efc54
+      goto error_exit;
b9efc54
+    }
b9efc54
+
b9efc54
+  grub_memcpy (buffer_aligned, data, context.size_of_headers);
b9efc54
+
b9efc54
+  char *reloc_base, *reloc_base_end;
b9efc54
+  reloc_base = image_address (buffer_aligned, datasize,
b9efc54
+			      context.reloc_dir->rva);
b9efc54
+  /* RelocBaseEnd here is the address of the last byte of the table */
b9efc54
+  reloc_base_end = image_address (buffer_aligned, datasize,
b9efc54
+				  context.reloc_dir->rva
b9efc54
+				  + context.reloc_dir->size - 1);
b9efc54
+  struct grub_pe32_section_table *reloc_section = NULL;
b9efc54
+
b9efc54
+  section = context.first_section;
b9efc54
+  for (i = 0; i < context.number_of_sections; i++, section++)
b9efc54
+    {
b9efc54
+      size = section->virtual_size;
b9efc54
+      if (size > section->raw_data_size)
b9efc54
+        size = section->raw_data_size;
b9efc54
+
b9efc54
+      base = image_address (buffer_aligned, context.image_size,
b9efc54
+			    section->virtual_address);
b9efc54
+      end = image_address (buffer_aligned, context.image_size,
b9efc54
+			   section->virtual_address + size - 1);
b9efc54
+
b9efc54
+
b9efc54
+      /* We do want to process .reloc, but it's often marked
b9efc54
+       * discardable, so we don't want to memcpy it. */
b9efc54
+      if (grub_memcmp (section->name, ".reloc\0\0", 8) == 0)
b9efc54
+	{
b9efc54
+	  if (reloc_section)
b9efc54
+	    {
b9efc54
+	      grub_error (GRUB_ERR_BAD_ARGUMENT,
b9efc54
+			  "Image has multiple relocation sections");
b9efc54
+	      goto error_exit;
b9efc54
+	    }
b9efc54
+
b9efc54
+	  /* If it has nonzero sizes, and our bounds check
b9efc54
+	   * made sense, and the VA and size match RelocDir's
b9efc54
+	   * versions, then we believe in this section table. */
b9efc54
+	  if (section->raw_data_size && section->virtual_size &&
b9efc54
+	      base && end && reloc_base == base && reloc_base_end == end)
b9efc54
+	    {
b9efc54
+	      reloc_section = section;
b9efc54
+	    }
b9efc54
+	}
b9efc54
+
b9efc54
+      if (section->characteristics && GRUB_PE32_SCN_MEM_DISCARDABLE)
b9efc54
+	continue;
b9efc54
+
b9efc54
+      if (!base || !end)
b9efc54
+        {
b9efc54
+          grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid section size");
b9efc54
+          goto error_exit;
b9efc54
+        }
b9efc54
+
b9efc54
+      if (section->virtual_address < context.size_of_headers ||
b9efc54
+	  section->raw_data_offset < context.size_of_headers)
b9efc54
+	{
b9efc54
+	  grub_error (GRUB_ERR_BAD_ARGUMENT,
b9efc54
+		      "Section %d is inside image headers", i);
b9efc54
+	  goto error_exit;
b9efc54
+	}
b9efc54
+
b9efc54
+      if (section->raw_data_size > 0)
b9efc54
+        grub_memcpy (base, (grub_efi_uint8_t*)data + section->raw_data_offset,
b9efc54
+		     size);
b9efc54
+
b9efc54
+      if (size < section->virtual_size)
b9efc54
+        grub_memset (base + size, 0, section->virtual_size - size);
b9efc54
+
b9efc54
+      grub_dprintf ("chain", "copied section %s\n", section->name);
b9efc54
+    }
b9efc54
+
b9efc54
+  /* 5 == EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC */
b9efc54
+  if (context.number_of_rva_and_sizes <= 5)
b9efc54
+    {
b9efc54
+      grub_dprintf ("chain", "image has no relocation entry\n");
b9efc54
+      goto error_exit;
b9efc54
+    }
b9efc54
+
b9efc54
+  if (context.reloc_dir->size && reloc_section)
b9efc54
+    {
b9efc54
+      /* run the relocation fixups */
b9efc54
+      efi_status = relocate_coff (&context, reloc_section, data,
b9efc54
+				  buffer_aligned);
b9efc54
+
b9efc54
+      if (efi_status != GRUB_EFI_SUCCESS)
b9efc54
+	{
b9efc54
+	  grub_error (GRUB_ERR_BAD_ARGUMENT, "relocation failed");
b9efc54
+	  goto error_exit;
b9efc54
+	}
b9efc54
+    }
b9efc54
+
b9efc54
+  entry_point = image_address (buffer_aligned, context.image_size,
b9efc54
+			       context.entry_point);
b9efc54
+
b9efc54
+  if (!entry_point)
b9efc54
+    {
b9efc54
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid entry point");
b9efc54
+      goto error_exit;
b9efc54
+    }
b9efc54
+
b9efc54
+  li = grub_efi_get_loaded_image (grub_efi_image_handle);
b9efc54
+  if (!li)
b9efc54
+    {
b9efc54
+      grub_error (GRUB_ERR_BAD_ARGUMENT, "no loaded image available");
b9efc54
+      goto error_exit;
b9efc54
+    }
b9efc54
+
b9efc54
+  grub_memcpy (&li_bak, li, sizeof (grub_efi_loaded_image_t));
b9efc54
+  li->image_base = buffer_aligned;
b9efc54
+  li->image_size = context.image_size;
b9efc54
+  li->load_options = cmdline;
b9efc54
+  li->load_options_size = cmdline_len;
b9efc54
+  li->file_path = grub_efi_get_media_file_path (file_path);
b9efc54
+  li->device_handle = dev_handle;
b9efc54
+  if (li->file_path)
b9efc54
+    {
b9efc54
+      grub_printf ("file path: ");
b9efc54
+      grub_efi_print_device_path (li->file_path);
b9efc54
+    }
b9efc54
+  else
b9efc54
+    {
b9efc54
+      grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching file path found");
b9efc54
+      goto error_exit;
b9efc54
+    }
b9efc54
+
b9efc54
+  efi_status = efi_call_2 (entry_point, grub_efi_image_handle,
b9efc54
+			   grub_efi_system_table);
b9efc54
+
b9efc54
+  grub_memcpy (li, &li_bak, sizeof (grub_efi_loaded_image_t));
6f1e3d5
+  efi_status = efi_call_1 (b->free_pool, buffer);
b9efc54
+
b9efc54
+  return 1;
b9efc54
+
b9efc54
+error_exit:
b9efc54
+  if (buffer)
6f1e3d5
+      efi_call_1 (b->free_pool, buffer);
b9efc54
+
b9efc54
+  return 0;
b9efc54
+}
b9efc54
+
b9efc54
+static grub_err_t
b9efc54
+grub_secureboot_chainloader_unload (void)
b9efc54
+{
6f1e3d5
+  grub_efi_boot_services_t *b;
6f1e3d5
+
6f1e3d5
+  b = grub_efi_system_table->boot_services;
6f1e3d5
+  efi_call_2 (b->free_pages, address, pages);
b9efc54
+  grub_free (file_path);
b9efc54
+  grub_free (cmdline);
b9efc54
+  cmdline = 0;
b9efc54
+  file_path = 0;
b9efc54
+  dev_handle = 0;
b9efc54
+
b9efc54
+  grub_dl_unref (my_mod);
b9efc54
+  return GRUB_ERR_NONE;
b9efc54
+}
b9efc54
+
b9efc54
+static grub_err_t
b9efc54
+grub_secureboot_chainloader_boot (void)
b9efc54
+{
b9efc54
+  handle_image ((void *)address, fsize);
b9efc54
+  grub_loader_unset ();
b9efc54
+  return grub_errno;
b9efc54
+}
b9efc54
+
b9efc54
 static grub_err_t
b9efc54
 grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
b9efc54
 		      int argc, char *argv[])
b9efc54
 {
b9efc54
   grub_file_t file = 0;
b9efc54
-  grub_ssize_t size;
b9efc54
   grub_efi_status_t status;
b9efc54
   grub_efi_boot_services_t *b;
b9efc54
   grub_device_t dev = 0;
6f1e3d5
@@ -204,7 +723,6 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
b9efc54
   grub_efi_loaded_image_t *loaded_image;
b9efc54
   char *filename;
b9efc54
   void *boot_image = 0;
b9efc54
-  grub_efi_handle_t dev_handle = 0;
b9efc54
 
b9efc54
   if (argc == 0)
b9efc54
     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
6f1e3d5
@@ -216,9 +734,36 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
b9efc54
   address = 0;
b9efc54
   image_handle = 0;
b9efc54
   file_path = 0;
b9efc54
+  dev_handle = 0;
b9efc54
 
b9efc54
   b = grub_efi_system_table->boot_services;
b9efc54
 
b9efc54
+  if (argc > 1)
b9efc54
+    {
b9efc54
+      int i;
b9efc54
+      grub_efi_char16_t *p16;
b9efc54
+
b9efc54
+      for (i = 1, cmdline_len = 0; i < argc; i++)
b9efc54
+        cmdline_len += grub_strlen (argv[i]) + 1;
b9efc54
+
b9efc54
+      cmdline_len *= sizeof (grub_efi_char16_t);
b9efc54
+      cmdline = p16 = grub_malloc (cmdline_len);
b9efc54
+      if (! cmdline)
b9efc54
+        goto fail;
b9efc54
+
b9efc54
+      for (i = 1; i < argc; i++)
b9efc54
+        {
b9efc54
+          char *p8;
b9efc54
+
b9efc54
+          p8 = argv[i];
b9efc54
+          while (*p8)
b9efc54
+            *(p16++) = *(p8++);
b9efc54
+
b9efc54
+          *(p16++) = ' ';
b9efc54
+        }
b9efc54
+      *(--p16) = 0;
b9efc54
+    }
b9efc54
+
b9efc54
   file = grub_file_open (filename);
b9efc54
   if (! file)
b9efc54
     goto fail;
6f1e3d5
@@ -267,14 +812,14 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
b9efc54
   grub_printf ("file path: ");
b9efc54
   grub_efi_print_device_path (file_path);
b9efc54
 
b9efc54
-  size = grub_file_size (file);
b9efc54
-  if (!size)
b9efc54
+  fsize = grub_file_size (file);
b9efc54
+  if (!fsize)
b9efc54
     {
b9efc54
       grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
b9efc54
 		  filename);
b9efc54
       goto fail;
b9efc54
     }
b9efc54
-  pages = (((grub_efi_uintn_t) size + ((1 << 12) - 1)) >> 12);
b9efc54
+  pages = (((grub_efi_uintn_t) fsize + ((1 << 12) - 1)) >> 12);
b9efc54
 
6f1e3d5
   status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ANY_PAGES,
6f1e3d5
 			      GRUB_EFI_LOADER_CODE,
6f1e3d5
@@ -288,7 +833,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
b9efc54
     }
b9efc54
 
b9efc54
   boot_image = (void *) ((grub_addr_t) address);
b9efc54
-  if (grub_file_read (file, boot_image, size) != size)
b9efc54
+  if (grub_file_read (file, boot_image, fsize) != fsize)
b9efc54
     {
b9efc54
       if (grub_errno == GRUB_ERR_NONE)
b9efc54
 	grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
6f1e3d5
@@ -298,7 +843,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
b9efc54
     }
b9efc54
 
b9efc54
 #if defined (__i386__) || defined (__x86_64__)
b9efc54
-  if (size >= (grub_ssize_t) sizeof (struct grub_macho_fat_header))
b9efc54
+  if (fsize >= (grub_ssize_t) sizeof (struct grub_macho_fat_header))
b9efc54
     {
b9efc54
       struct grub_macho_fat_header *head = boot_image;
b9efc54
       if (head->magic
6f1e3d5
@@ -307,6 +852,14 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
b9efc54
 	  grub_uint32_t i;
b9efc54
 	  struct grub_macho_fat_arch *archs
b9efc54
 	    = (struct grub_macho_fat_arch *) (head + 1);
b9efc54
+
b9efc54
+	  if (grub_efi_secure_boot())
b9efc54
+	    {
b9efc54
+	      grub_error (GRUB_ERR_BAD_OS,
b9efc54
+			  "MACHO binaries are forbidden with Secure Boot");
b9efc54
+	      goto fail;
b9efc54
+	    }
b9efc54
+
b9efc54
 	  for (i = 0; i < grub_cpu_to_le32 (head->nfat_arch); i++)
b9efc54
 	    {
b9efc54
 	      if (GRUB_MACHO_CPUTYPE_IS_HOST_CURRENT (archs[i].cputype))
6f1e3d5
@@ -321,21 +874,28 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
b9efc54
 	      > ~grub_cpu_to_le32 (archs[i].size)
b9efc54
 	      || grub_cpu_to_le32 (archs[i].offset)
b9efc54
 	      + grub_cpu_to_le32 (archs[i].size)
b9efc54
-	      > (grub_size_t) size)
b9efc54
+	      > (grub_size_t) fsize)
b9efc54
 	    {
b9efc54
 	      grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
b9efc54
 			  filename);
b9efc54
 	      goto fail;
b9efc54
 	    }
b9efc54
 	  boot_image = (char *) boot_image + grub_cpu_to_le32 (archs[i].offset);
b9efc54
-	  size = grub_cpu_to_le32 (archs[i].size);
b9efc54
+	  fsize = grub_cpu_to_le32 (archs[i].size);
b9efc54
 	}
b9efc54
     }
b9efc54
 #endif
b9efc54
 
b9efc54
+  if (grub_linuxefi_secure_validate((void *)address, fsize))
b9efc54
+    {
b9efc54
+      grub_file_close (file);
b9efc54
+      grub_loader_set (grub_secureboot_chainloader_boot,
b9efc54
+		       grub_secureboot_chainloader_unload, 0);
b9efc54
+      return 0;
b9efc54
+    }
b9efc54
+
b9efc54
   status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path,
b9efc54
-		       boot_image, size,
b9efc54
-		       &image_handle);
b9efc54
+		       boot_image, fsize, &image_handle);
b9efc54
   if (status != GRUB_EFI_SUCCESS)
b9efc54
     {
b9efc54
       if (status == GRUB_EFI_OUT_OF_RESOURCES)
6f1e3d5
@@ -357,33 +917,10 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
b9efc54
     }
b9efc54
   loaded_image->device_handle = dev_handle;
b9efc54
 
b9efc54
-  if (argc > 1)
b9efc54
+  if (cmdline)
b9efc54
     {
b9efc54
-      int i, len;
b9efc54
-      grub_efi_char16_t *p16;
b9efc54
-
b9efc54
-      for (i = 1, len = 0; i < argc; i++)
b9efc54
-        len += grub_strlen (argv[i]) + 1;
b9efc54
-
b9efc54
-      len *= sizeof (grub_efi_char16_t);
b9efc54
-      cmdline = p16 = grub_malloc (len);
b9efc54
-      if (! cmdline)
b9efc54
-        goto fail;
b9efc54
-
b9efc54
-      for (i = 1; i < argc; i++)
b9efc54
-        {
b9efc54
-          char *p8;
b9efc54
-
b9efc54
-          p8 = argv[i];
b9efc54
-          while (*p8)
b9efc54
-            *(p16++) = *(p8++);
b9efc54
-
b9efc54
-          *(p16++) = ' ';
b9efc54
-        }
b9efc54
-      *(--p16) = 0;
b9efc54
-
b9efc54
       loaded_image->load_options = cmdline;
b9efc54
-      loaded_image->load_options_size = len;
b9efc54
+      loaded_image->load_options_size = cmdline_len;
b9efc54
     }
b9efc54
 
b9efc54
   grub_file_close (file);
6f1e3d5
@@ -405,6 +942,9 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
b9efc54
   if (address)
6f1e3d5
     efi_call_2 (b->free_pages, address, pages);
b9efc54
 
b9efc54
+  if (cmdline)
b9efc54
+    grub_free (cmdline);
b9efc54
+
b9efc54
   grub_dl_unref (my_mod);
b9efc54
 
b9efc54
   return grub_errno;
b9efc54
diff --git a/include/grub/efi/pe32.h b/include/grub/efi/pe32.h
81987f4
index 7d44732d2c3..6e24dae2cb6 100644
b9efc54
--- a/include/grub/efi/pe32.h
b9efc54
+++ b/include/grub/efi/pe32.h
bbc6a89
@@ -214,7 +214,11 @@ struct grub_pe64_optional_header
b9efc54
 struct grub_pe32_section_table
b9efc54
 {
b9efc54
   char name[8];
b9efc54
-  grub_uint32_t virtual_size;
b9efc54
+  union
b9efc54
+    {
b9efc54
+      grub_uint32_t physical_address;
b9efc54
+      grub_uint32_t virtual_size;
b9efc54
+    };
b9efc54
   grub_uint32_t virtual_address;
b9efc54
   grub_uint32_t raw_data_size;
b9efc54
   grub_uint32_t raw_data_offset;
bbc6a89
@@ -265,6 +269,20 @@ struct grub_pe32_header
b9efc54
 #endif
b9efc54
 };
b9efc54
 
b9efc54
+struct grub_pe32_header_32
b9efc54
+{
b9efc54
+  char signature[GRUB_PE32_SIGNATURE_SIZE];
b9efc54
+  struct grub_pe32_coff_header coff_header;
b9efc54
+  struct grub_pe32_optional_header optional_header;
b9efc54
+};
b9efc54
+
b9efc54
+struct grub_pe32_header_64
b9efc54
+{
b9efc54
+  char signature[GRUB_PE32_SIGNATURE_SIZE];
b9efc54
+  struct grub_pe32_coff_header coff_header;
b9efc54
+  struct grub_pe64_optional_header optional_header;
b9efc54
+};
b9efc54
+
b9efc54
 struct grub_pe32_fixup_block
b9efc54
 {
b9efc54
   grub_uint32_t page_rva;
b9efc54
-- 
81987f4
2.15.0
b9efc54