]> git.proxmox.com Git - efi-boot-shim.git/commitdiff
Move a bunch of PE-related stuff out of shim.c
authorPeter Jones <pjones@redhat.com>
Wed, 2 Dec 2020 18:59:38 +0000 (13:59 -0500)
committerPeter Jones <pjones@redhat.com>
Sat, 13 Feb 2021 16:02:59 +0000 (11:02 -0500)
Signed-off-by: Peter Jones <pjones@redhat.com>
Makefile
include/pe.h [new file with mode: 0644]
pe.c [new file with mode: 0644]
shim.c
shim.h

index 45d57fcc27454a06f62570e69c577174c434732a..111f251c4359f2d1bc33cf2c7369fec48e5b6b56 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -33,9 +33,9 @@ CFLAGS += -DENABLE_SHIM_CERT
 else
 TARGETS += $(MMNAME) $(FBNAME)
 endif
-OBJS   = shim.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o
+OBJS   = shim.o mok.o netboot.o cert.o replacements.o tpm.o version.o errlog.o sbat.o pe.o
 KEYS   = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer
-ORIG_SOURCES   = shim.c mok.c netboot.c replacements.c tpm.c errlog.c shim.h version.h $(wildcard include/*.h)
+ORIG_SOURCES   = shim.c mok.c netboot.c replacements.c tpm.c errlog.c pe.c shim.h version.h $(wildcard include/*.h)
 MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o errlog.o sbat.o
 ORIG_MOK_SOURCES = MokManager.c PasswordCrypt.c crypt_blowfish.c shim.h $(wildcard include/*.h)
 FALLBACK_OBJS = fallback.o tpm.o errlog.o sbat.o
diff --git a/include/pe.h b/include/pe.h
new file mode 100644 (file)
index 0000000..7f2236e
--- /dev/null
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+/*
+ * pe.h - helper functions for pe binaries.
+ * Copyright Peter Jones <pjones@redhat.com>
+ */
+
+#ifndef PE_H_
+#define PE_H_
+
+void *
+ImageAddress (void *image, uint64_t size, uint64_t address);
+
+EFI_STATUS
+read_header(void *data, unsigned int datasize,
+           PE_COFF_LOADER_IMAGE_CONTEXT *context);
+
+EFI_STATUS
+handle_image (void *data, unsigned int datasize,
+             EFI_LOADED_IMAGE *li,
+             EFI_IMAGE_ENTRY_POINT *entry_point,
+             EFI_PHYSICAL_ADDRESS *alloc_address,
+             UINTN *alloc_pages);
+
+EFI_STATUS
+generate_hash (char *data, unsigned int datasize_in,
+              PE_COFF_LOADER_IMAGE_CONTEXT *context,
+              UINT8 *sha256hash, UINT8 *sha1hash);
+
+EFI_STATUS
+relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
+              EFI_IMAGE_SECTION_HEADER *Section,
+              void *orig, void *data);
+
+#endif /* !PE_H_ */
+// vim:fenc=utf-8:tw=75:noet
diff --git a/pe.c b/pe.c
new file mode 100644 (file)
index 0000000..43ed4f8
--- /dev/null
+++ b/pe.c
@@ -0,0 +1,940 @@
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+/*
+ * pe.c - helper functions for pe binaries.
+ * Copyright Peter Jones <pjones@redhat.com>
+ */
+
+#include "shim.h"
+#include "hexdump.h"
+
+#include <openssl/err.h>
+#include <openssl/bn.h>
+#include <openssl/dh.h>
+#include <openssl/ocsp.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rand.h>
+#include <openssl/crypto.h>
+#include <openssl/ssl.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/rsa.h>
+#include <openssl/dso.h>
+
+#include <Library/BaseCryptLib.h>
+
+/*
+ * Perform basic bounds checking of the intra-image pointers
+ */
+void *
+ImageAddress (void *image, uint64_t size, uint64_t address)
+{
+       /* ensure our local pointer isn't bigger than our size */
+       if (address > size)
+               return NULL;
+
+       /* Insure our math won't overflow */
+       if (UINT64_MAX - address < (uint64_t)(intptr_t)image)
+               return NULL;
+
+       /* return the absolute pointer */
+       return image + address;
+}
+
+/*
+ * Perform the actual relocation
+ */
+EFI_STATUS
+relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
+              EFI_IMAGE_SECTION_HEADER *Section,
+              void *orig, void *data)
+{
+       EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd;
+       UINT64 Adjust;
+       UINT16 *Reloc, *RelocEnd;
+       char *Fixup, *FixupBase;
+       UINT16 *Fixup16;
+       UINT32 *Fixup32;
+       UINT64 *Fixup64;
+       int size = context->ImageSize;
+       void *ImageEnd = (char *)orig + size;
+       int n = 0;
+
+       /* Alright, so here's how this works:
+        *
+        * context->RelocDir gives us two things:
+        * - the VA the table of base relocation blocks are (maybe) to be
+        *   mapped at (RelocDir->VirtualAddress)
+        * - the virtual size (RelocDir->Size)
+        *
+        * The .reloc section (Section here) gives us some other things:
+        * - the name! kind of. (Section->Name)
+        * - the virtual size (Section->VirtualSize), which should be the same
+        *   as RelocDir->Size
+        * - the virtual address (Section->VirtualAddress)
+        * - the file section size (Section->SizeOfRawData), which is
+        *   a multiple of OptHdr->FileAlignment.  Only useful for image
+        *   validation, not really useful for iteration bounds.
+        * - the file address (Section->PointerToRawData)
+        * - 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 EFI_IMAGE_BASE_RELOCATION structs with some values packed behind
+        * them.  The SizeOfBlock field of this structure includes the
+        * structure itself, and adding it to that structure's address will
+        * yield the next entry in the array.
+        */
+       RelocBase = ImageAddress(orig, size, Section->PointerToRawData);
+       /* RelocBaseEnd here is the address of the first entry /past/ the
+        * table.  */
+       RelocBaseEnd = ImageAddress(orig, size, Section->PointerToRawData +
+                                               Section->Misc.VirtualSize);
+
+       if (!RelocBase && !RelocBaseEnd)
+               return EFI_SUCCESS;
+
+       if (!RelocBase || !RelocBaseEnd) {
+               perror(L"Reloc table overflows binary\n");
+               return EFI_UNSUPPORTED;
+       }
+
+       Adjust = (UINTN)data - context->ImageAddress;
+
+       if (Adjust == 0)
+               return EFI_SUCCESS;
+
+       while (RelocBase < RelocBaseEnd) {
+               Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
+
+               if (RelocBase->SizeOfBlock == 0) {
+                       perror(L"Reloc %d block size 0 is invalid\n", n);
+                       return EFI_UNSUPPORTED;
+               } else if (RelocBase->SizeOfBlock > context->RelocDir->Size) {
+                       perror(L"Reloc %d block size %d greater than reloc dir"
+                                       "size %d, which is invalid\n", n,
+                                       RelocBase->SizeOfBlock,
+                                       context->RelocDir->Size);
+                       return EFI_UNSUPPORTED;
+               }
+
+               RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock);
+               if ((void *)RelocEnd < orig || (void *)RelocEnd > ImageEnd) {
+                       perror(L"Reloc %d entry overflows binary\n", n);
+                       return EFI_UNSUPPORTED;
+               }
+
+               FixupBase = ImageAddress(data, size, RelocBase->VirtualAddress);
+               if (!FixupBase) {
+                       perror(L"Reloc %d Invalid fixupbase\n", n);
+                       return EFI_UNSUPPORTED;
+               }
+
+               while (Reloc < RelocEnd) {
+                       Fixup = FixupBase + (*Reloc & 0xFFF);
+                       switch ((*Reloc) >> 12) {
+                       case EFI_IMAGE_REL_BASED_ABSOLUTE:
+                               break;
+
+                       case EFI_IMAGE_REL_BASED_HIGH:
+                               Fixup16   = (UINT16 *) Fixup;
+                               *Fixup16 = (UINT16) (*Fixup16 + ((UINT16) ((UINT32) Adjust >> 16)));
+                               break;
+
+                       case EFI_IMAGE_REL_BASED_LOW:
+                               Fixup16   = (UINT16 *) Fixup;
+                               *Fixup16  = (UINT16) (*Fixup16 + (UINT16) Adjust);
+                               break;
+
+                       case EFI_IMAGE_REL_BASED_HIGHLOW:
+                               Fixup32   = (UINT32 *) Fixup;
+                               *Fixup32  = *Fixup32 + (UINT32) Adjust;
+                               break;
+
+                       case EFI_IMAGE_REL_BASED_DIR64:
+                               Fixup64 = (UINT64 *) Fixup;
+                               *Fixup64 = *Fixup64 + (UINT64) Adjust;
+                               break;
+
+                       default:
+                               perror(L"Reloc %d Unknown relocation\n", n);
+                               return EFI_UNSUPPORTED;
+                       }
+                       Reloc += 1;
+               }
+               RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd;
+               n++;
+       }
+
+       return EFI_SUCCESS;
+}
+
+#define check_size_line(data, datasize_in, hashbase, hashsize, l) ({   \
+       if ((unsigned long)hashbase >                                   \
+                       (unsigned long)data + datasize_in) {            \
+               efi_status = EFI_INVALID_PARAMETER;                     \
+               perror(L"shim.c:%d Invalid hash base 0x%016x\n", l,     \
+                       hashbase);                                      \
+               goto done;                                              \
+       }                                                               \
+       if ((unsigned long)hashbase + hashsize >                        \
+                       (unsigned long)data + datasize_in) {            \
+               efi_status = EFI_INVALID_PARAMETER;                     \
+               perror(L"shim.c:%d Invalid hash size 0x%016x\n", l,     \
+                       hashsize);                                      \
+               goto done;                                              \
+       }                                                               \
+})
+#define check_size(d, ds, h, hs) check_size_line(d, ds, h, hs, __LINE__)
+
+/*
+ * Calculate the SHA1 and SHA256 hashes of a binary
+ */
+
+EFI_STATUS
+generate_hash(char *data, unsigned int datasize_in,
+             PE_COFF_LOADER_IMAGE_CONTEXT *context, UINT8 *sha256hash,
+             UINT8 *sha1hash)
+{
+       unsigned int sha256ctxsize, sha1ctxsize;
+       unsigned int size = datasize_in;
+       void *sha256ctx = NULL, *sha1ctx = NULL;
+       char *hashbase;
+       unsigned int hashsize;
+       unsigned int SumOfBytesHashed, SumOfSectionBytes;
+       unsigned int index, pos;
+       unsigned int datasize;
+       EFI_IMAGE_SECTION_HEADER  *Section;
+       EFI_IMAGE_SECTION_HEADER  *SectionHeader = NULL;
+       EFI_STATUS efi_status = EFI_SUCCESS;
+       EFI_IMAGE_DOS_HEADER *DosHdr = (void *)data;
+       unsigned int PEHdr_offset = 0;
+
+       size = datasize = datasize_in;
+
+       if (datasize <= sizeof (*DosHdr) ||
+           DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) {
+               perror(L"Invalid signature\n");
+               return EFI_INVALID_PARAMETER;
+       }
+       PEHdr_offset = DosHdr->e_lfanew;
+
+       sha256ctxsize = Sha256GetContextSize();
+       sha256ctx = AllocatePool(sha256ctxsize);
+
+       sha1ctxsize = Sha1GetContextSize();
+       sha1ctx = AllocatePool(sha1ctxsize);
+
+       if (!sha256ctx || !sha1ctx) {
+               perror(L"Unable to allocate memory for hash context\n");
+               return EFI_OUT_OF_RESOURCES;
+       }
+
+       if (!Sha256Init(sha256ctx) || !Sha1Init(sha1ctx)) {
+               perror(L"Unable to initialise hash\n");
+               efi_status = EFI_OUT_OF_RESOURCES;
+               goto done;
+       }
+
+       /* Hash start to checksum */
+       hashbase = data;
+       hashsize = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum -
+               hashbase;
+       check_size(data, datasize_in, hashbase, hashsize);
+
+       if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+           !(Sha1Update(sha1ctx, hashbase, hashsize))) {
+               perror(L"Unable to generate hash\n");
+               efi_status = EFI_OUT_OF_RESOURCES;
+               goto done;
+       }
+
+       /* Hash post-checksum to start of certificate table */
+       hashbase = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum +
+               sizeof (int);
+       hashsize = (char *)context->SecDir - hashbase;
+       check_size(data, datasize_in, hashbase, hashsize);
+
+       if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+           !(Sha1Update(sha1ctx, hashbase, hashsize))) {
+               perror(L"Unable to generate hash\n");
+               efi_status = EFI_OUT_OF_RESOURCES;
+               goto done;
+       }
+
+       /* Hash end of certificate table to end of image header */
+       EFI_IMAGE_DATA_DIRECTORY *dd = context->SecDir + 1;
+       hashbase = (char *)dd;
+       hashsize = context->SizeOfHeaders - (unsigned long)((char *)dd - data);
+       if (hashsize > datasize_in) {
+               perror(L"Data Directory size %d is invalid\n", hashsize);
+               efi_status = EFI_INVALID_PARAMETER;
+               goto done;
+       }
+       check_size(data, datasize_in, hashbase, hashsize);
+
+       if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+           !(Sha1Update(sha1ctx, hashbase, hashsize))) {
+               perror(L"Unable to generate hash\n");
+               efi_status = EFI_OUT_OF_RESOURCES;
+               goto done;
+       }
+
+       /* Sort sections */
+       SumOfBytesHashed = context->SizeOfHeaders;
+
+       /* Validate section locations and sizes */
+       for (index = 0, SumOfSectionBytes = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) {
+               EFI_IMAGE_SECTION_HEADER  *SectionPtr;
+
+               /* Validate SectionPtr is within image */
+               SectionPtr = ImageAddress(data, datasize,
+                       PEHdr_offset +
+                       sizeof (UINT32) +
+                       sizeof (EFI_IMAGE_FILE_HEADER) +
+                       context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader +
+                       (index * sizeof(*SectionPtr)));
+               if (!SectionPtr) {
+                       perror(L"Malformed section %d\n", index);
+                       efi_status = EFI_INVALID_PARAMETER;
+                       goto done;
+               }
+               /* Validate section size is within image. */
+               if (SectionPtr->SizeOfRawData >
+                   datasize - SumOfBytesHashed - SumOfSectionBytes) {
+                       perror(L"Malformed section %d size\n", index);
+                       efi_status = EFI_INVALID_PARAMETER;
+                       goto done;
+               }
+               SumOfSectionBytes += SectionPtr->SizeOfRawData;
+       }
+
+       SectionHeader = (EFI_IMAGE_SECTION_HEADER *) AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * context->PEHdr->Pe32.FileHeader.NumberOfSections);
+       if (SectionHeader == NULL) {
+               perror(L"Unable to allocate section header\n");
+               efi_status = EFI_OUT_OF_RESOURCES;
+               goto done;
+       }
+
+       /* Already validated above */
+       Section = ImageAddress(data, datasize,
+               PEHdr_offset +
+               sizeof (UINT32) +
+               sizeof (EFI_IMAGE_FILE_HEADER) +
+               context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader);
+       /* But check it again just for better error messaging, and so
+        * clang-analyzer doesn't get confused. */
+       if (Section == NULL) {
+               uint64_t addr;
+
+               addr = PEHdr_offset + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER)
+                       + context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader;
+               perror(L"Malformed file header.\n");
+               perror(L"Image address for Section 0 is 0x%016llx\n", addr);
+               perror(L"File size is 0x%016llx\n", datasize);
+               efi_status = EFI_INVALID_PARAMETER;
+               goto done;
+       }
+
+       /* Sort the section headers */
+       for (index = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) {
+               pos = index;
+               while ((pos > 0) && (Section->PointerToRawData < SectionHeader[pos - 1].PointerToRawData)) {
+                       CopyMem (&SectionHeader[pos], &SectionHeader[pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER));
+                       pos--;
+               }
+               CopyMem (&SectionHeader[pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER));
+               Section += 1;
+       }
+
+       /* Hash the sections */
+       for (index = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) {
+               Section = &SectionHeader[index];
+               if (Section->SizeOfRawData == 0) {
+                       continue;
+               }
+               hashbase  = ImageAddress(data, size, Section->PointerToRawData);
+
+               if (!hashbase) {
+                       perror(L"Malformed section header\n");
+                       efi_status = EFI_INVALID_PARAMETER;
+                       goto done;
+               }
+
+               /* Verify hashsize within image. */
+               if (Section->SizeOfRawData >
+                   datasize - Section->PointerToRawData) {
+                       perror(L"Malformed section raw size %d\n", index);
+                       efi_status = EFI_INVALID_PARAMETER;
+                       goto done;
+               }
+               hashsize  = (unsigned int) Section->SizeOfRawData;
+               check_size(data, datasize_in, hashbase, hashsize);
+
+               if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+                   !(Sha1Update(sha1ctx, hashbase, hashsize))) {
+                       perror(L"Unable to generate hash\n");
+                       efi_status = EFI_OUT_OF_RESOURCES;
+                       goto done;
+               }
+               SumOfBytesHashed += Section->SizeOfRawData;
+       }
+
+       /* Hash all remaining data up to SecDir if SecDir->Size is not 0 */
+       if (datasize > SumOfBytesHashed && context->SecDir->Size) {
+               hashbase = data + SumOfBytesHashed;
+               hashsize = datasize - context->SecDir->Size - SumOfBytesHashed;
+
+               if ((datasize - SumOfBytesHashed < context->SecDir->Size) ||
+                   (SumOfBytesHashed + hashsize != context->SecDir->VirtualAddress)) {
+                       perror(L"Malformed binary after Attribute Certificate Table\n");
+                       console_print(L"datasize: %u SumOfBytesHashed: %u SecDir->Size: %lu\n",
+                                     datasize, SumOfBytesHashed, context->SecDir->Size);
+                       console_print(L"hashsize: %u SecDir->VirtualAddress: 0x%08lx\n",
+                                     hashsize, context->SecDir->VirtualAddress);
+                       efi_status = EFI_INVALID_PARAMETER;
+                       goto done;
+               }
+               check_size(data, datasize_in, hashbase, hashsize);
+
+               if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+                   !(Sha1Update(sha1ctx, hashbase, hashsize))) {
+                       perror(L"Unable to generate hash\n");
+                       efi_status = EFI_OUT_OF_RESOURCES;
+                       goto done;
+               }
+
+#if 1
+       }
+#else // we have to migrate to doing this later :/
+               SumOfBytesHashed += hashsize;
+       }
+
+       /* Hash all remaining data */
+       if (datasize > SumOfBytesHashed) {
+               hashbase = data + SumOfBytesHashed;
+               hashsize = datasize - SumOfBytesHashed;
+
+               check_size(data, datasize_in, hashbase, hashsize);
+
+               if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+                   !(Sha1Update(sha1ctx, hashbase, hashsize))) {
+                       perror(L"Unable to generate hash\n");
+                       efi_status = EFI_OUT_OF_RESOURCES;
+                       goto done;
+               }
+
+               SumOfBytesHashed += hashsize;
+       }
+#endif
+
+       if (!(Sha256Final(sha256ctx, sha256hash)) ||
+           !(Sha1Final(sha1ctx, sha1hash))) {
+               perror(L"Unable to finalise hash\n");
+               efi_status = EFI_OUT_OF_RESOURCES;
+               goto done;
+       }
+
+       dprint(L"sha1 authenticode hash:\n");
+       dhexdumpat(sha1hash, SHA1_DIGEST_SIZE, 0);
+       dprint(L"sha256 authenticode hash:\n");
+       dhexdumpat(sha256hash, SHA256_DIGEST_SIZE, 0);
+
+done:
+       if (SectionHeader)
+               FreePool(SectionHeader);
+       if (sha1ctx)
+               FreePool(sha1ctx);
+       if (sha256ctx)
+               FreePool(sha256ctx);
+
+       return efi_status;
+}
+
+/* here's a chart:
+ *             i686    x86_64  aarch64
+ *  64-on-64:  nyet    yes     yes
+ *  64-on-32:  nyet    yes     nyet
+ *  32-on-32:  yes     yes     no
+ */
+static int
+allow_64_bit(void)
+{
+#if defined(__x86_64__) || defined(__aarch64__)
+       return 1;
+#elif defined(__i386__) || defined(__i686__)
+       /* Right now blindly assuming the kernel will correctly detect this
+        * and /halt the system/ if you're not really on a 64-bit cpu */
+       if (in_protocol)
+               return 1;
+       return 0;
+#else /* assuming everything else is 32-bit... */
+       return 0;
+#endif
+}
+
+static int
+allow_32_bit(void)
+{
+#if defined(__x86_64__)
+#if defined(ALLOW_32BIT_KERNEL_ON_X64)
+       if (in_protocol)
+               return 1;
+       return 0;
+#else
+       return 0;
+#endif
+#elif defined(__i386__) || defined(__i686__)
+       return 1;
+#elif defined(__aarch64__)
+       return 0;
+#else /* assuming everything else is 32-bit... */
+       return 1;
+#endif
+}
+
+static int
+image_is_64_bit(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr)
+{
+       /* .Magic is the same offset in all cases */
+       if (PEHdr->Pe32Plus.OptionalHeader.Magic
+                       == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC)
+               return 1;
+       return 0;
+}
+
+static const UINT16 machine_type =
+#if defined(__x86_64__)
+       IMAGE_FILE_MACHINE_X64;
+#elif defined(__aarch64__)
+       IMAGE_FILE_MACHINE_ARM64;
+#elif defined(__arm__)
+       IMAGE_FILE_MACHINE_ARMTHUMB_MIXED;
+#elif defined(__i386__) || defined(__i486__) || defined(__i686__)
+       IMAGE_FILE_MACHINE_I386;
+#elif defined(__ia64__)
+       IMAGE_FILE_MACHINE_IA64;
+#else
+#error this architecture is not supported by shim
+#endif
+
+static int
+image_is_loadable(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr)
+{
+       /* If the machine type doesn't match the binary, bail, unless
+        * we're in an allowed 64-on-32 scenario */
+       if (PEHdr->Pe32.FileHeader.Machine != machine_type) {
+               if (!(machine_type == IMAGE_FILE_MACHINE_I386 &&
+                     PEHdr->Pe32.FileHeader.Machine == IMAGE_FILE_MACHINE_X64 &&
+                     allow_64_bit())) {
+                       return 0;
+               }
+       }
+
+       /* If it's not a header type we recognize at all, bail */
+       switch (PEHdr->Pe32Plus.OptionalHeader.Magic) {
+       case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC:
+       case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC:
+               break;
+       default:
+               return 0;
+       }
+
+       /* and now just check for general 64-vs-32 compatibility */
+       if (image_is_64_bit(PEHdr)) {
+               if (allow_64_bit())
+                       return 1;
+       } else {
+               if (allow_32_bit())
+                       return 1;
+       }
+       return 0;
+}
+
+/*
+ * Read the binary header and grab appropriate information from it
+ */
+EFI_STATUS
+read_header(void *data, unsigned int datasize,
+           PE_COFF_LOADER_IMAGE_CONTEXT *context)
+{
+       EFI_IMAGE_DOS_HEADER *DosHdr = data;
+       EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data;
+       unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize;
+       unsigned long FileAlignment = 0;
+
+       if (datasize < sizeof (PEHdr->Pe32)) {
+               perror(L"Invalid image\n");
+               return EFI_UNSUPPORTED;
+       }
+
+       if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE)
+               PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + DosHdr->e_lfanew);
+
+       if (!image_is_loadable(PEHdr)) {
+               perror(L"Platform does not support this image\n");
+               return EFI_UNSUPPORTED;
+       }
+
+       if (image_is_64_bit(PEHdr)) {
+               context->NumberOfRvaAndSizes = PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes;
+               context->SizeOfHeaders = PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders;
+               context->ImageSize = PEHdr->Pe32Plus.OptionalHeader.SizeOfImage;
+               context->SectionAlignment = PEHdr->Pe32Plus.OptionalHeader.SectionAlignment;
+               FileAlignment = PEHdr->Pe32Plus.OptionalHeader.FileAlignment;
+               OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER64);
+       } else {
+               context->NumberOfRvaAndSizes = PEHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes;
+               context->SizeOfHeaders = PEHdr->Pe32.OptionalHeader.SizeOfHeaders;
+               context->ImageSize = (UINT64)PEHdr->Pe32.OptionalHeader.SizeOfImage;
+               context->SectionAlignment = PEHdr->Pe32.OptionalHeader.SectionAlignment;
+               FileAlignment = PEHdr->Pe32.OptionalHeader.FileAlignment;
+               OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32);
+       }
+
+       if (FileAlignment % 2 != 0) {
+               perror(L"File Alignment is invalid (%d)\n", FileAlignment);
+               return EFI_UNSUPPORTED;
+       }
+       if (FileAlignment == 0)
+               FileAlignment = 0x200;
+       if (context->SectionAlignment == 0)
+               context->SectionAlignment = PAGE_SIZE;
+       if (context->SectionAlignment < FileAlignment)
+               context->SectionAlignment = FileAlignment;
+
+       context->NumberOfSections = PEHdr->Pe32.FileHeader.NumberOfSections;
+
+       if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < context->NumberOfRvaAndSizes) {
+               perror(L"Image header too small\n");
+               return EFI_UNSUPPORTED;
+       }
+
+       HeaderWithoutDataDir = OptHeaderSize
+                       - sizeof (EFI_IMAGE_DATA_DIRECTORY) * EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES;
+       if (((UINT32)PEHdr->Pe32.FileHeader.SizeOfOptionalHeader - HeaderWithoutDataDir) !=
+                       context->NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY)) {
+               perror(L"Image header overflows data directory\n");
+               return EFI_UNSUPPORTED;
+       }
+
+       SectionHeaderOffset = DosHdr->e_lfanew
+                               + sizeof (UINT32)
+                               + sizeof (EFI_IMAGE_FILE_HEADER)
+                               + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader;
+       if (((UINT32)context->ImageSize - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER
+                       <= context->NumberOfSections) {
+               perror(L"Image sections overflow image size\n");
+               return EFI_UNSUPPORTED;
+       }
+
+       if ((context->SizeOfHeaders - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER
+                       < (UINT32)context->NumberOfSections) {
+               perror(L"Image sections overflow section headers\n");
+               return EFI_UNSUPPORTED;
+       }
+
+       if ((((UINT8 *)PEHdr - (UINT8 *)data) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION)) > datasize) {
+               perror(L"Invalid image\n");
+               return EFI_UNSUPPORTED;
+       }
+
+       if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) {
+               perror(L"Unsupported image type\n");
+               return EFI_UNSUPPORTED;
+       }
+
+       if (PEHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) {
+               perror(L"Unsupported image - Relocations have been stripped\n");
+               return EFI_UNSUPPORTED;
+       }
+
+       context->PEHdr = PEHdr;
+
+       if (image_is_64_bit(PEHdr)) {
+               context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase;
+               context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint;
+               context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
+               context->SecDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
+       } else {
+               context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase;
+               context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint;
+               context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
+               context->SecDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
+       }
+
+       context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER));
+
+       if (context->ImageSize < context->SizeOfHeaders) {
+               perror(L"Invalid image\n");
+               return EFI_UNSUPPORTED;
+       }
+
+       if ((unsigned long)((UINT8 *)context->SecDir - (UINT8 *)data) >
+           (datasize - sizeof(EFI_IMAGE_DATA_DIRECTORY))) {
+               perror(L"Invalid image\n");
+               return EFI_UNSUPPORTED;
+       }
+
+       if (context->SecDir->VirtualAddress > datasize ||
+           (context->SecDir->VirtualAddress == datasize &&
+            context->SecDir->Size > 0)) {
+               perror(L"Malformed security header\n");
+               return EFI_INVALID_PARAMETER;
+       }
+       return EFI_SUCCESS;
+}
+
+/*
+ * Once the image has been loaded it needs to be validated and relocated
+ */
+EFI_STATUS
+handle_image (void *data, unsigned int datasize,
+             EFI_LOADED_IMAGE *li,
+             EFI_IMAGE_ENTRY_POINT *entry_point,
+             EFI_PHYSICAL_ADDRESS *alloc_address,
+             UINTN *alloc_pages)
+{
+       EFI_STATUS efi_status;
+       char *buffer;
+       int i;
+       EFI_IMAGE_SECTION_HEADER *Section;
+       char *base, *end;
+       PE_COFF_LOADER_IMAGE_CONTEXT context;
+       unsigned int alignment, alloc_size;
+       int found_entry_point = 0;
+       UINT8 sha1hash[SHA1_DIGEST_SIZE];
+       UINT8 sha256hash[SHA256_DIGEST_SIZE];
+
+       /*
+        * The binary header contains relevant context and section pointers
+        */
+       efi_status = read_header(data, datasize, &context);
+       if (EFI_ERROR(efi_status)) {
+               perror(L"Failed to read header: %r\n", efi_status);
+               return efi_status;
+       }
+
+       /*
+        * We only need to verify the binary if we're in secure mode
+        */
+       efi_status = generate_hash(data, datasize, &context, sha256hash,
+                                  sha1hash);
+       if (EFI_ERROR(efi_status))
+               return efi_status;
+
+       /* Measure the binary into the TPM */
+#ifdef REQUIRE_TPM
+       efi_status =
+#endif
+       tpm_log_pe((EFI_PHYSICAL_ADDRESS)(UINTN)data, datasize,
+                  (EFI_PHYSICAL_ADDRESS)(UINTN)context.ImageAddress,
+                  li->FilePath, sha1hash, 4);
+#ifdef REQUIRE_TPM
+       if (efi_status != EFI_SUCCESS) {
+               return efi_status;
+       }
+#endif
+
+       if (secure_mode ()) {
+               efi_status = verify_buffer(data, datasize, &context,
+                                          sha256hash, sha1hash);
+
+               if (EFI_ERROR(efi_status)) {
+                       if (verbose)
+                               console_print(L"Verification failed: %r\n", efi_status);
+                       else
+                               console_error(L"Verification failed", efi_status);
+                       return efi_status;
+               } else {
+                       if (verbose)
+                               console_print(L"Verification succeeded\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.
+        */
+       alignment = context.SectionAlignment;
+       if (!alignment)
+               alignment = 4096;
+
+       alloc_size = ALIGN_VALUE(context.ImageSize + context.SectionAlignment,
+                                PAGE_SIZE);
+       *alloc_pages = alloc_size / PAGE_SIZE;
+
+       efi_status = gBS->AllocatePages(AllocateAnyPages, EfiLoaderCode,
+                                       *alloc_pages, alloc_address);
+       if (EFI_ERROR(efi_status)) {
+               perror(L"Failed to allocate image buffer\n");
+               return EFI_OUT_OF_RESOURCES;
+       }
+
+       buffer = (void *)ALIGN_VALUE((unsigned long)*alloc_address, alignment);
+
+       CopyMem(buffer, data, context.SizeOfHeaders);
+
+       *entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint);
+       if (!*entry_point) {
+               perror(L"Entry point is invalid\n");
+               gBS->FreePages(*alloc_address, *alloc_pages);
+               return EFI_UNSUPPORTED;
+       }
+
+       char *RelocBase, *RelocBaseEnd;
+       /*
+        * These are relative virtual addresses, so we have to check them
+        * against the image size, not the data size.
+        */
+       RelocBase = ImageAddress(buffer, context.ImageSize,
+                                context.RelocDir->VirtualAddress);
+       /*
+        * RelocBaseEnd here is the address of the last byte of the table
+        */
+       RelocBaseEnd = ImageAddress(buffer, context.ImageSize,
+                                   context.RelocDir->VirtualAddress +
+                                   context.RelocDir->Size - 1);
+
+       EFI_IMAGE_SECTION_HEADER *RelocSection = NULL;
+
+       /*
+        * Copy the executable's sections to their desired offsets
+        */
+       Section = context.FirstSection;
+       for (i = 0; i < context.NumberOfSections; i++, Section++) {
+               /* Don't try to copy discardable sections with zero size */
+               if ((Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) &&
+                   !Section->Misc.VirtualSize)
+                       continue;
+
+               base = ImageAddress (buffer, context.ImageSize,
+                                    Section->VirtualAddress);
+               end = ImageAddress (buffer, context.ImageSize,
+                                   Section->VirtualAddress
+                                    + Section->Misc.VirtualSize - 1);
+
+               if (end < base) {
+                       perror(L"Section %d has negative size\n", i);
+                       gBS->FreePages(*alloc_address, *alloc_pages);
+                       return EFI_UNSUPPORTED;
+               }
+
+               if (Section->VirtualAddress <= context.EntryPoint &&
+                   (Section->VirtualAddress + Section->SizeOfRawData - 1)
+                   > context.EntryPoint)
+                       found_entry_point++;
+
+               /* We do want to process .reloc, but it's often marked
+                * discardable, so we don't want to memcpy it. */
+               if (CompareMem(Section->Name, ".reloc\0\0", 8) == 0) {
+                       if (RelocSection) {
+                               perror(L"Image has multiple relocation sections\n");
+                               return EFI_UNSUPPORTED;
+                       }
+                       /* 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->SizeOfRawData &&
+                                       Section->Misc.VirtualSize &&
+                                       base && end &&
+                                       RelocBase == base &&
+                                       RelocBaseEnd == end) {
+                               RelocSection = Section;
+                       }
+               }
+
+               if (Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) {
+                       continue;
+               }
+
+               if (!base) {
+                       perror(L"Section %d has invalid base address\n", i);
+                       return EFI_UNSUPPORTED;
+               }
+               if (!end) {
+                       perror(L"Section %d has zero size\n", i);
+                       return EFI_UNSUPPORTED;
+               }
+
+               if (!(Section->Characteristics & EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA) &&
+                   (Section->VirtualAddress < context.SizeOfHeaders ||
+                    Section->PointerToRawData < context.SizeOfHeaders)) {
+                       perror(L"Section %d is inside image headers\n", i);
+                       return EFI_UNSUPPORTED;
+               }
+
+               if (Section->Characteristics & EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
+                       ZeroMem(base, Section->Misc.VirtualSize);
+               } else {
+                       if (Section->PointerToRawData < context.SizeOfHeaders) {
+                               perror(L"Section %d is inside image headers\n", i);
+                               return EFI_UNSUPPORTED;
+                       }
+
+                       if (Section->SizeOfRawData > 0)
+                               CopyMem(base, data + Section->PointerToRawData,
+                                       Section->SizeOfRawData);
+
+                       if (Section->SizeOfRawData < Section->Misc.VirtualSize)
+                               ZeroMem(base + Section->SizeOfRawData,
+                                       Section->Misc.VirtualSize - Section->SizeOfRawData);
+               }
+       }
+
+       if (context.NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
+               perror(L"Image has no relocation entry\n");
+               FreePool(buffer);
+               return EFI_UNSUPPORTED;
+       }
+
+       if (context.RelocDir->Size && RelocSection) {
+               /*
+                * Run the relocation fixups
+                */
+               efi_status = relocate_coff(&context, RelocSection, data,
+                                          buffer);
+
+               if (EFI_ERROR(efi_status)) {
+                       perror(L"Relocation failed: %r\n", efi_status);
+                       FreePool(buffer);
+                       return efi_status;
+               }
+       }
+
+       /*
+        * grub needs to know its location and size in memory, so fix up
+        * the loaded image protocol values
+        */
+       li->ImageBase = buffer;
+       li->ImageSize = context.ImageSize;
+
+       /* Pass the load options to the second stage loader */
+       if ( load_options ) {
+               li->LoadOptions = load_options;
+               li->LoadOptionsSize = load_options_size;
+       }
+
+       if (!found_entry_point) {
+               perror(L"Entry point is not within sections\n");
+               return EFI_UNSUPPORTED;
+       }
+       if (found_entry_point > 1) {
+               perror(L"%d sections contain entry point\n");
+               return EFI_UNSUPPORTED;
+       }
+
+       return EFI_SUCCESS;
+}
+
+
+// vim:fenc=utf-8:tw=75:noet
diff --git a/shim.c b/shim.c
index 0d00ada936b9930f65ec841506020b41e23e4e63..eb8192d4a184a81872ab936f4bb232820dde93b0 100644 (file)
--- a/shim.c
+++ b/shim.c
@@ -62,8 +62,8 @@ static EFI_SYSTEM_TABLE *systab;
 static EFI_HANDLE global_image_handle;
 
 static CHAR16 *second_stage;
-static void *load_options;
-static UINT32 load_options_size;
+void *load_options;
+UINT32 load_options_size;
 
 /*
  * The vendor certificate used for validating the second stage loader
@@ -108,250 +108,6 @@ typedef struct {
        UINT8 *Mok;
 } MokListNode;
 
-/*
- * Perform basic bounds checking of the intra-image pointers
- */
-static void *ImageAddress (void *image, uint64_t size, uint64_t address)
-{
-       /* ensure our local pointer isn't bigger than our size */
-       if (address > size)
-               return NULL;
-
-       /* Insure our math won't overflow */
-       if (UINT64_MAX - address < (uint64_t)(intptr_t)image)
-               return NULL;
-
-       /* return the absolute pointer */
-       return image + address;
-}
-
-/* here's a chart:
- *             i686    x86_64  aarch64
- *  64-on-64:  nyet    yes     yes
- *  64-on-32:  nyet    yes     nyet
- *  32-on-32:  yes     yes     no
- */
-static int
-allow_64_bit(void)
-{
-#if defined(__x86_64__) || defined(__aarch64__)
-       return 1;
-#elif defined(__i386__) || defined(__i686__)
-       /* Right now blindly assuming the kernel will correctly detect this
-        * and /halt the system/ if you're not really on a 64-bit cpu */
-       if (in_protocol)
-               return 1;
-       return 0;
-#else /* assuming everything else is 32-bit... */
-       return 0;
-#endif
-}
-
-static int
-allow_32_bit(void)
-{
-#if defined(__x86_64__)
-#if defined(ALLOW_32BIT_KERNEL_ON_X64)
-       if (in_protocol)
-               return 1;
-       return 0;
-#else
-       return 0;
-#endif
-#elif defined(__i386__) || defined(__i686__)
-       return 1;
-#elif defined(__aarch64__)
-       return 0;
-#else /* assuming everything else is 32-bit... */
-       return 1;
-#endif
-}
-
-static int
-image_is_64_bit(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr)
-{
-       /* .Magic is the same offset in all cases */
-       if (PEHdr->Pe32Plus.OptionalHeader.Magic
-                       == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC)
-               return 1;
-       return 0;
-}
-
-static const UINT16 machine_type =
-#if defined(__x86_64__)
-       IMAGE_FILE_MACHINE_X64;
-#elif defined(__aarch64__)
-       IMAGE_FILE_MACHINE_ARM64;
-#elif defined(__arm__)
-       IMAGE_FILE_MACHINE_ARMTHUMB_MIXED;
-#elif defined(__i386__) || defined(__i486__) || defined(__i686__)
-       IMAGE_FILE_MACHINE_I386;
-#elif defined(__ia64__)
-       IMAGE_FILE_MACHINE_IA64;
-#else
-#error this architecture is not supported by shim
-#endif
-
-static int
-image_is_loadable(EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr)
-{
-       /* If the machine type doesn't match the binary, bail, unless
-        * we're in an allowed 64-on-32 scenario */
-       if (PEHdr->Pe32.FileHeader.Machine != machine_type) {
-               if (!(machine_type == IMAGE_FILE_MACHINE_I386 &&
-                     PEHdr->Pe32.FileHeader.Machine == IMAGE_FILE_MACHINE_X64 &&
-                     allow_64_bit())) {
-                       return 0;
-               }
-       }
-
-       /* If it's not a header type we recognize at all, bail */
-       switch (PEHdr->Pe32Plus.OptionalHeader.Magic) {
-       case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC:
-       case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC:
-               break;
-       default:
-               return 0;
-       }
-
-       /* and now just check for general 64-vs-32 compatibility */
-       if (image_is_64_bit(PEHdr)) {
-               if (allow_64_bit())
-                       return 1;
-       } else {
-               if (allow_32_bit())
-                       return 1;
-       }
-       return 0;
-}
-
-/*
- * Perform the actual relocation
- */
-static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
-                                EFI_IMAGE_SECTION_HEADER *Section,
-                                void *orig, void *data)
-{
-       EFI_IMAGE_BASE_RELOCATION *RelocBase, *RelocBaseEnd;
-       UINT64 Adjust;
-       UINT16 *Reloc, *RelocEnd;
-       char *Fixup, *FixupBase;
-       UINT16 *Fixup16;
-       UINT32 *Fixup32;
-       UINT64 *Fixup64;
-       int size = context->ImageSize;
-       void *ImageEnd = (char *)orig + size;
-       int n = 0;
-
-       /* Alright, so here's how this works:
-        *
-        * context->RelocDir gives us two things:
-        * - the VA the table of base relocation blocks are (maybe) to be
-        *   mapped at (RelocDir->VirtualAddress)
-        * - the virtual size (RelocDir->Size)
-        *
-        * The .reloc section (Section here) gives us some other things:
-        * - the name! kind of. (Section->Name)
-        * - the virtual size (Section->VirtualSize), which should be the same
-        *   as RelocDir->Size
-        * - the virtual address (Section->VirtualAddress)
-        * - the file section size (Section->SizeOfRawData), which is
-        *   a multiple of OptHdr->FileAlignment.  Only useful for image
-        *   validation, not really useful for iteration bounds.
-        * - the file address (Section->PointerToRawData)
-        * - 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 EFI_IMAGE_BASE_RELOCATION structs with some values packed behind
-        * them.  The SizeOfBlock field of this structure includes the
-        * structure itself, and adding it to that structure's address will
-        * yield the next entry in the array.
-        */
-       RelocBase = ImageAddress(orig, size, Section->PointerToRawData);
-       /* RelocBaseEnd here is the address of the first entry /past/ the
-        * table.  */
-       RelocBaseEnd = ImageAddress(orig, size, Section->PointerToRawData +
-                                               Section->Misc.VirtualSize);
-
-       if (!RelocBase && !RelocBaseEnd)
-               return EFI_SUCCESS;
-
-       if (!RelocBase || !RelocBaseEnd) {
-               perror(L"Reloc table overflows binary\n");
-               return EFI_UNSUPPORTED;
-       }
-
-       Adjust = (UINTN)data - context->ImageAddress;
-
-       if (Adjust == 0)
-               return EFI_SUCCESS;
-
-       while (RelocBase < RelocBaseEnd) {
-               Reloc = (UINT16 *) ((char *) RelocBase + sizeof (EFI_IMAGE_BASE_RELOCATION));
-
-               if (RelocBase->SizeOfBlock == 0) {
-                       perror(L"Reloc %d block size 0 is invalid\n", n);
-                       return EFI_UNSUPPORTED;
-               } else if (RelocBase->SizeOfBlock > context->RelocDir->Size) {
-                       perror(L"Reloc %d block size %d greater than reloc dir"
-                                       "size %d, which is invalid\n", n,
-                                       RelocBase->SizeOfBlock,
-                                       context->RelocDir->Size);
-                       return EFI_UNSUPPORTED;
-               }
-
-               RelocEnd = (UINT16 *) ((char *) RelocBase + RelocBase->SizeOfBlock);
-               if ((void *)RelocEnd < orig || (void *)RelocEnd > ImageEnd) {
-                       perror(L"Reloc %d entry overflows binary\n", n);
-                       return EFI_UNSUPPORTED;
-               }
-
-               FixupBase = ImageAddress(data, size, RelocBase->VirtualAddress);
-               if (!FixupBase) {
-                       perror(L"Reloc %d Invalid fixupbase\n", n);
-                       return EFI_UNSUPPORTED;
-               }
-
-               while (Reloc < RelocEnd) {
-                       Fixup = FixupBase + (*Reloc & 0xFFF);
-                       switch ((*Reloc) >> 12) {
-                       case EFI_IMAGE_REL_BASED_ABSOLUTE:
-                               break;
-
-                       case EFI_IMAGE_REL_BASED_HIGH:
-                               Fixup16   = (UINT16 *) Fixup;
-                               *Fixup16 = (UINT16) (*Fixup16 + ((UINT16) ((UINT32) Adjust >> 16)));
-                               break;
-
-                       case EFI_IMAGE_REL_BASED_LOW:
-                               Fixup16   = (UINT16 *) Fixup;
-                               *Fixup16  = (UINT16) (*Fixup16 + (UINT16) Adjust);
-                               break;
-
-                       case EFI_IMAGE_REL_BASED_HIGHLOW:
-                               Fixup32   = (UINT32 *) Fixup;
-                               *Fixup32  = *Fixup32 + (UINT32) Adjust;
-                               break;
-
-                       case EFI_IMAGE_REL_BASED_DIR64:
-                               Fixup64 = (UINT64 *) Fixup;
-                               *Fixup64 = *Fixup64 + (UINT64) Adjust;
-                               break;
-
-                       default:
-                               perror(L"Reloc %d Unknown relocation\n", n);
-                               return EFI_UNSUPPORTED;
-                       }
-                       Reloc += 1;
-               }
-               RelocBase = (EFI_IMAGE_BASE_RELOCATION *) RelocEnd;
-               n++;
-       }
-
-       return EFI_SUCCESS;
-}
-
 static void
 drain_openssl_errors(void)
 {
@@ -709,8 +465,7 @@ static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert,
 /*
  * Check whether we're in Secure Boot and user mode
  */
-
-static BOOLEAN secure_mode (void)
+BOOLEAN secure_mode (void)
 {
        static int first = 1;
        if (user_insecure_mode)
@@ -740,288 +495,6 @@ static BOOLEAN secure_mode (void)
        return TRUE;
 }
 
-#define check_size_line(data, datasize_in, hashbase, hashsize, l) ({   \
-       if ((unsigned long)hashbase >                                   \
-                       (unsigned long)data + datasize_in) {            \
-               efi_status = EFI_INVALID_PARAMETER;                     \
-               perror(L"shim.c:%d Invalid hash base 0x%016x\n", l,     \
-                       hashbase);                                      \
-               goto done;                                              \
-       }                                                               \
-       if ((unsigned long)hashbase + hashsize >                        \
-                       (unsigned long)data + datasize_in) {            \
-               efi_status = EFI_INVALID_PARAMETER;                     \
-               perror(L"shim.c:%d Invalid hash size 0x%016x\n", l,     \
-                       hashsize);                                      \
-               goto done;                                              \
-       }                                                               \
-})
-#define check_size(d, ds, h, hs) check_size_line(d, ds, h, hs, __LINE__)
-
-/*
- * Calculate the SHA1 and SHA256 hashes of a binary
- */
-
-static EFI_STATUS generate_hash (char *data, unsigned int datasize_in,
-                                PE_COFF_LOADER_IMAGE_CONTEXT *context,
-                                UINT8 *sha256hash, UINT8 *sha1hash)
-
-{
-       unsigned int sha256ctxsize, sha1ctxsize;
-       unsigned int size = datasize_in;
-       void *sha256ctx = NULL, *sha1ctx = NULL;
-       char *hashbase;
-       unsigned int hashsize;
-       unsigned int SumOfBytesHashed, SumOfSectionBytes;
-       unsigned int index, pos;
-       unsigned int datasize;
-       EFI_IMAGE_SECTION_HEADER  *Section;
-       EFI_IMAGE_SECTION_HEADER  *SectionHeader = NULL;
-       EFI_STATUS efi_status = EFI_SUCCESS;
-       EFI_IMAGE_DOS_HEADER *DosHdr = (void *)data;
-       unsigned int PEHdr_offset = 0;
-
-       size = datasize = datasize_in;
-
-       if (datasize <= sizeof (*DosHdr) ||
-           DosHdr->e_magic != EFI_IMAGE_DOS_SIGNATURE) {
-               perror(L"Invalid signature\n");
-               return EFI_INVALID_PARAMETER;
-       }
-       PEHdr_offset = DosHdr->e_lfanew;
-
-       sha256ctxsize = Sha256GetContextSize();
-       sha256ctx = AllocatePool(sha256ctxsize);
-
-       sha1ctxsize = Sha1GetContextSize();
-       sha1ctx = AllocatePool(sha1ctxsize);
-
-       if (!sha256ctx || !sha1ctx) {
-               perror(L"Unable to allocate memory for hash context\n");
-               return EFI_OUT_OF_RESOURCES;
-       }
-
-       if (!Sha256Init(sha256ctx) || !Sha1Init(sha1ctx)) {
-               perror(L"Unable to initialise hash\n");
-               efi_status = EFI_OUT_OF_RESOURCES;
-               goto done;
-       }
-
-       /* Hash start to checksum */
-       hashbase = data;
-       hashsize = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum -
-               hashbase;
-       check_size(data, datasize_in, hashbase, hashsize);
-
-       if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
-           !(Sha1Update(sha1ctx, hashbase, hashsize))) {
-               perror(L"Unable to generate hash\n");
-               efi_status = EFI_OUT_OF_RESOURCES;
-               goto done;
-       }
-
-       /* Hash post-checksum to start of certificate table */
-       hashbase = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum +
-               sizeof (int);
-       hashsize = (char *)context->SecDir - hashbase;
-       check_size(data, datasize_in, hashbase, hashsize);
-
-       if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
-           !(Sha1Update(sha1ctx, hashbase, hashsize))) {
-               perror(L"Unable to generate hash\n");
-               efi_status = EFI_OUT_OF_RESOURCES;
-               goto done;
-       }
-
-       /* Hash end of certificate table to end of image header */
-       EFI_IMAGE_DATA_DIRECTORY *dd = context->SecDir + 1;
-       hashbase = (char *)dd;
-       hashsize = context->SizeOfHeaders - (unsigned long)((char *)dd - data);
-       if (hashsize > datasize_in) {
-               perror(L"Data Directory size %d is invalid\n", hashsize);
-               efi_status = EFI_INVALID_PARAMETER;
-               goto done;
-       }
-       check_size(data, datasize_in, hashbase, hashsize);
-
-       if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
-           !(Sha1Update(sha1ctx, hashbase, hashsize))) {
-               perror(L"Unable to generate hash\n");
-               efi_status = EFI_OUT_OF_RESOURCES;
-               goto done;
-       }
-
-       /* Sort sections */
-       SumOfBytesHashed = context->SizeOfHeaders;
-
-       /* Validate section locations and sizes */
-       for (index = 0, SumOfSectionBytes = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) {
-               EFI_IMAGE_SECTION_HEADER  *SectionPtr;
-
-               /* Validate SectionPtr is within image */
-               SectionPtr = ImageAddress(data, datasize,
-                       PEHdr_offset +
-                       sizeof (UINT32) +
-                       sizeof (EFI_IMAGE_FILE_HEADER) +
-                       context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader +
-                       (index * sizeof(*SectionPtr)));
-               if (!SectionPtr) {
-                       perror(L"Malformed section %d\n", index);
-                       efi_status = EFI_INVALID_PARAMETER;
-                       goto done;
-               }
-               /* Validate section size is within image. */
-               if (SectionPtr->SizeOfRawData >
-                   datasize - SumOfBytesHashed - SumOfSectionBytes) {
-                       perror(L"Malformed section %d size\n", index);
-                       efi_status = EFI_INVALID_PARAMETER;
-                       goto done;
-               }
-               SumOfSectionBytes += SectionPtr->SizeOfRawData;
-       }
-
-       SectionHeader = (EFI_IMAGE_SECTION_HEADER *) AllocateZeroPool (sizeof (EFI_IMAGE_SECTION_HEADER) * context->PEHdr->Pe32.FileHeader.NumberOfSections);
-       if (SectionHeader == NULL) {
-               perror(L"Unable to allocate section header\n");
-               efi_status = EFI_OUT_OF_RESOURCES;
-               goto done;
-       }
-
-       /* Already validated above */
-       Section = ImageAddress(data, datasize,
-               PEHdr_offset +
-               sizeof (UINT32) +
-               sizeof (EFI_IMAGE_FILE_HEADER) +
-               context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader);
-       /* But check it again just for better error messaging, and so
-        * clang-analyzer doesn't get confused. */
-       if (Section == NULL) {
-               uint64_t addr;
-
-               addr = PEHdr_offset + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER)
-                       + context->PEHdr->Pe32.FileHeader.SizeOfOptionalHeader;
-               perror(L"Malformed file header.\n");
-               perror(L"Image address for Section 0 is 0x%016llx\n", addr);
-               perror(L"File size is 0x%016llx\n", datasize);
-               efi_status = EFI_INVALID_PARAMETER;
-               goto done;
-       }
-
-       /* Sort the section headers */
-       for (index = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) {
-               pos = index;
-               while ((pos > 0) && (Section->PointerToRawData < SectionHeader[pos - 1].PointerToRawData)) {
-                       CopyMem (&SectionHeader[pos], &SectionHeader[pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER));
-                       pos--;
-               }
-               CopyMem (&SectionHeader[pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER));
-               Section += 1;
-       }
-
-       /* Hash the sections */
-       for (index = 0; index < context->PEHdr->Pe32.FileHeader.NumberOfSections; index++) {
-               Section = &SectionHeader[index];
-               if (Section->SizeOfRawData == 0) {
-                       continue;
-               }
-               hashbase  = ImageAddress(data, size, Section->PointerToRawData);
-
-               if (!hashbase) {
-                       perror(L"Malformed section header\n");
-                       efi_status = EFI_INVALID_PARAMETER;
-                       goto done;
-               }
-
-               /* Verify hashsize within image. */
-               if (Section->SizeOfRawData >
-                   datasize - Section->PointerToRawData) {
-                       perror(L"Malformed section raw size %d\n", index);
-                       efi_status = EFI_INVALID_PARAMETER;
-                       goto done;
-               }
-               hashsize  = (unsigned int) Section->SizeOfRawData;
-               check_size(data, datasize_in, hashbase, hashsize);
-
-               if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
-                   !(Sha1Update(sha1ctx, hashbase, hashsize))) {
-                       perror(L"Unable to generate hash\n");
-                       efi_status = EFI_OUT_OF_RESOURCES;
-                       goto done;
-               }
-               SumOfBytesHashed += Section->SizeOfRawData;
-       }
-
-       /* Hash all remaining data up to SecDir if SecDir->Size is not 0 */
-       if (datasize > SumOfBytesHashed && context->SecDir->Size) {
-               hashbase = data + SumOfBytesHashed;
-               hashsize = datasize - context->SecDir->Size - SumOfBytesHashed;
-
-               if ((datasize - SumOfBytesHashed < context->SecDir->Size) ||
-                   (SumOfBytesHashed + hashsize != context->SecDir->VirtualAddress)) {
-                       perror(L"Malformed binary after Attribute Certificate Table\n");
-                       console_print(L"datasize: %u SumOfBytesHashed: %u SecDir->Size: %lu\n",
-                                     datasize, SumOfBytesHashed, context->SecDir->Size);
-                       console_print(L"hashsize: %u SecDir->VirtualAddress: 0x%08lx\n",
-                                     hashsize, context->SecDir->VirtualAddress);
-                       efi_status = EFI_INVALID_PARAMETER;
-                       goto done;
-               }
-               check_size(data, datasize_in, hashbase, hashsize);
-
-               if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
-                   !(Sha1Update(sha1ctx, hashbase, hashsize))) {
-                       perror(L"Unable to generate hash\n");
-                       efi_status = EFI_OUT_OF_RESOURCES;
-                       goto done;
-               }
-
-#if 1
-       }
-#else // we have to migrate to doing this later :/
-               SumOfBytesHashed += hashsize;
-       }
-
-       /* Hash all remaining data */
-       if (datasize > SumOfBytesHashed) {
-               hashbase = data + SumOfBytesHashed;
-               hashsize = datasize - SumOfBytesHashed;
-
-               check_size(data, datasize_in, hashbase, hashsize);
-
-               if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
-                   !(Sha1Update(sha1ctx, hashbase, hashsize))) {
-                       perror(L"Unable to generate hash\n");
-                       efi_status = EFI_OUT_OF_RESOURCES;
-                       goto done;
-               }
-
-               SumOfBytesHashed += hashsize;
-       }
-#endif
-
-       if (!(Sha256Final(sha256ctx, sha256hash)) ||
-           !(Sha1Final(sha1ctx, sha1hash))) {
-               perror(L"Unable to finalise hash\n");
-               efi_status = EFI_OUT_OF_RESOURCES;
-               goto done;
-       }
-
-       dprint(L"sha1 authenticode hash:\n");
-       dhexdumpat(sha1hash, SHA1_DIGEST_SIZE, 0);
-       dprint(L"sha256 authenticode hash:\n");
-       dhexdumpat(sha256hash, SHA256_DIGEST_SIZE, 0);
-
-done:
-       if (SectionHeader)
-               FreePool(SectionHeader);
-       if (sha1ctx)
-               FreePool(sha1ctx);
-       if (sha256ctx)
-               FreePool(sha256ctx);
-
-       return efi_status;
-}
-
 static EFI_STATUS
 verify_one_signature(WIN_CERTIFICATE_EFI_PKCS *sig,
                     UINT8 *sha256hash, UINT8 *sha1hash)
@@ -1122,9 +595,10 @@ verify_one_signature(WIN_CERTIFICATE_EFI_PKCS *sig,
 /*
  * Check that the signature is valid and matches the binary
  */
-static EFI_STATUS verify_buffer (char *data, int datasize,
-                                PE_COFF_LOADER_IMAGE_CONTEXT *context,
-                                UINT8 *sha256hash, UINT8 *sha1hash)
+EFI_STATUS
+verify_buffer (char *data, int datasize,
+              PE_COFF_LOADER_IMAGE_CONTEXT *context,
+              UINT8 *sha256hash, UINT8 *sha1hash)
 {
        EFI_STATUS ret_efi_status;
        size_t size = datasize;
@@ -1257,391 +731,6 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
        return ret_efi_status;
 }
 
-/*
- * Read the binary header and grab appropriate information from it
- */
-static EFI_STATUS read_header(void *data, unsigned int datasize,
-                             PE_COFF_LOADER_IMAGE_CONTEXT *context)
-{
-       EFI_IMAGE_DOS_HEADER *DosHdr = data;
-       EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data;
-       unsigned long HeaderWithoutDataDir, SectionHeaderOffset, OptHeaderSize;
-       unsigned long FileAlignment = 0;
-
-       if (datasize < sizeof (PEHdr->Pe32)) {
-               perror(L"Invalid image\n");
-               return EFI_UNSUPPORTED;
-       }
-
-       if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE)
-               PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + DosHdr->e_lfanew);
-
-       if (!image_is_loadable(PEHdr)) {
-               perror(L"Platform does not support this image\n");
-               return EFI_UNSUPPORTED;
-       }
-
-       if (image_is_64_bit(PEHdr)) {
-               context->NumberOfRvaAndSizes = PEHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes;
-               context->SizeOfHeaders = PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders;
-               context->ImageSize = PEHdr->Pe32Plus.OptionalHeader.SizeOfImage;
-               context->SectionAlignment = PEHdr->Pe32Plus.OptionalHeader.SectionAlignment;
-               FileAlignment = PEHdr->Pe32Plus.OptionalHeader.FileAlignment;
-               OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER64);
-       } else {
-               context->NumberOfRvaAndSizes = PEHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes;
-               context->SizeOfHeaders = PEHdr->Pe32.OptionalHeader.SizeOfHeaders;
-               context->ImageSize = (UINT64)PEHdr->Pe32.OptionalHeader.SizeOfImage;
-               context->SectionAlignment = PEHdr->Pe32.OptionalHeader.SectionAlignment;
-               FileAlignment = PEHdr->Pe32.OptionalHeader.FileAlignment;
-               OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32);
-       }
-
-       if (FileAlignment % 2 != 0) {
-               perror(L"File Alignment is invalid (%d)\n", FileAlignment);
-               return EFI_UNSUPPORTED;
-       }
-       if (FileAlignment == 0)
-               FileAlignment = 0x200;
-       if (context->SectionAlignment == 0)
-               context->SectionAlignment = PAGE_SIZE;
-       if (context->SectionAlignment < FileAlignment)
-               context->SectionAlignment = FileAlignment;
-
-       context->NumberOfSections = PEHdr->Pe32.FileHeader.NumberOfSections;
-
-       if (EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES < context->NumberOfRvaAndSizes) {
-               perror(L"Image header too small\n");
-               return EFI_UNSUPPORTED;
-       }
-
-       HeaderWithoutDataDir = OptHeaderSize
-                       - sizeof (EFI_IMAGE_DATA_DIRECTORY) * EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES;
-       if (((UINT32)PEHdr->Pe32.FileHeader.SizeOfOptionalHeader - HeaderWithoutDataDir) !=
-                       context->NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY)) {
-               perror(L"Image header overflows data directory\n");
-               return EFI_UNSUPPORTED;
-       }
-
-       SectionHeaderOffset = DosHdr->e_lfanew
-                               + sizeof (UINT32)
-                               + sizeof (EFI_IMAGE_FILE_HEADER)
-                               + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader;
-       if (((UINT32)context->ImageSize - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER
-                       <= context->NumberOfSections) {
-               perror(L"Image sections overflow image size\n");
-               return EFI_UNSUPPORTED;
-       }
-
-       if ((context->SizeOfHeaders - SectionHeaderOffset) / EFI_IMAGE_SIZEOF_SECTION_HEADER
-                       < (UINT32)context->NumberOfSections) {
-               perror(L"Image sections overflow section headers\n");
-               return EFI_UNSUPPORTED;
-       }
-
-       if ((((UINT8 *)PEHdr - (UINT8 *)data) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION)) > datasize) {
-               perror(L"Invalid image\n");
-               return EFI_UNSUPPORTED;
-       }
-
-       if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) {
-               perror(L"Unsupported image type\n");
-               return EFI_UNSUPPORTED;
-       }
-
-       if (PEHdr->Pe32.FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) {
-               perror(L"Unsupported image - Relocations have been stripped\n");
-               return EFI_UNSUPPORTED;
-       }
-
-       context->PEHdr = PEHdr;
-
-       if (image_is_64_bit(PEHdr)) {
-               context->ImageAddress = PEHdr->Pe32Plus.OptionalHeader.ImageBase;
-               context->EntryPoint = PEHdr->Pe32Plus.OptionalHeader.AddressOfEntryPoint;
-               context->RelocDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
-               context->SecDir = &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
-       } else {
-               context->ImageAddress = PEHdr->Pe32.OptionalHeader.ImageBase;
-               context->EntryPoint = PEHdr->Pe32.OptionalHeader.AddressOfEntryPoint;
-               context->RelocDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC];
-               context->SecDir = &PEHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
-       }
-
-       context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER));
-
-       if (context->ImageSize < context->SizeOfHeaders) {
-               perror(L"Invalid image\n");
-               return EFI_UNSUPPORTED;
-       }
-
-       if ((unsigned long)((UINT8 *)context->SecDir - (UINT8 *)data) >
-           (datasize - sizeof(EFI_IMAGE_DATA_DIRECTORY))) {
-               perror(L"Invalid image\n");
-               return EFI_UNSUPPORTED;
-       }
-
-       if (context->SecDir->VirtualAddress > datasize ||
-           (context->SecDir->VirtualAddress == datasize &&
-            context->SecDir->Size > 0)) {
-               perror(L"Malformed security header\n");
-               return EFI_INVALID_PARAMETER;
-       }
-       return EFI_SUCCESS;
-}
-
-/*
- * Once the image has been loaded it needs to be validated and relocated
- */
-static EFI_STATUS handle_image (void *data, unsigned int datasize,
-                               EFI_LOADED_IMAGE *li,
-                               EFI_IMAGE_ENTRY_POINT *entry_point,
-                               EFI_PHYSICAL_ADDRESS *alloc_address,
-                               UINTN *alloc_pages)
-{
-       EFI_STATUS efi_status;
-       char *buffer;
-       int i;
-       EFI_IMAGE_SECTION_HEADER *Section;
-       char *base, *end;
-       PE_COFF_LOADER_IMAGE_CONTEXT context;
-       unsigned int alignment, alloc_size;
-       int found_entry_point = 0;
-       UINT8 sha1hash[SHA1_DIGEST_SIZE];
-       UINT8 sha256hash[SHA256_DIGEST_SIZE];
-
-       /*
-        * The binary header contains relevant context and section pointers
-        */
-       efi_status = read_header(data, datasize, &context);
-       if (EFI_ERROR(efi_status)) {
-               perror(L"Failed to read header: %r\n", efi_status);
-               return efi_status;
-       }
-
-       /*
-        * We only need to verify the binary if we're in secure mode
-        */
-       efi_status = generate_hash(data, datasize, &context, sha256hash,
-                                  sha1hash);
-       if (EFI_ERROR(efi_status))
-               return efi_status;
-
-       /* Measure the binary into the TPM */
-#ifdef REQUIRE_TPM
-       efi_status =
-#endif
-       tpm_log_pe((EFI_PHYSICAL_ADDRESS)(UINTN)data, datasize,
-                  (EFI_PHYSICAL_ADDRESS)(UINTN)context.ImageAddress,
-                  li->FilePath, sha1hash, 4);
-#ifdef REQUIRE_TPM
-       if (efi_status != EFI_SUCCESS) {
-               return efi_status;
-       }
-#endif
-
-       if (secure_mode ()) {
-               efi_status = verify_buffer(data, datasize, &context,
-                                          sha256hash, sha1hash);
-
-               if (EFI_ERROR(efi_status)) {
-                       if (verbose)
-                               console_print(L"Verification failed: %r\n", efi_status);
-                       else
-                               console_error(L"Verification failed", efi_status);
-                       return efi_status;
-               } else {
-                       if (verbose)
-                               console_print(L"Verification succeeded\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.
-        */
-       alignment = context.SectionAlignment;
-       if (!alignment)
-               alignment = 4096;
-
-       alloc_size = ALIGN_VALUE(context.ImageSize + context.SectionAlignment,
-                                PAGE_SIZE);
-       *alloc_pages = alloc_size / PAGE_SIZE;
-
-       efi_status = gBS->AllocatePages(AllocateAnyPages, EfiLoaderCode,
-                                       *alloc_pages, alloc_address);
-       if (EFI_ERROR(efi_status)) {
-               perror(L"Failed to allocate image buffer\n");
-               return EFI_OUT_OF_RESOURCES;
-       }
-
-       buffer = (void *)ALIGN_VALUE((unsigned long)*alloc_address, alignment);
-
-       CopyMem(buffer, data, context.SizeOfHeaders);
-
-       *entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint);
-       if (!*entry_point) {
-               perror(L"Entry point is invalid\n");
-               gBS->FreePages(*alloc_address, *alloc_pages);
-               return EFI_UNSUPPORTED;
-       }
-
-
-       char *RelocBase, *RelocBaseEnd;
-       /*
-        * These are relative virtual addresses, so we have to check them
-        * against the image size, not the data size.
-        */
-       RelocBase = ImageAddress(buffer, context.ImageSize,
-                                context.RelocDir->VirtualAddress);
-       /*
-        * RelocBaseEnd here is the address of the last byte of the table
-        */
-       RelocBaseEnd = ImageAddress(buffer, context.ImageSize,
-                                   context.RelocDir->VirtualAddress +
-                                   context.RelocDir->Size - 1);
-
-       EFI_IMAGE_SECTION_HEADER *RelocSection = NULL;
-
-       /*
-        * Copy the executable's sections to their desired offsets
-        */
-       Section = context.FirstSection;
-       for (i = 0; i < context.NumberOfSections; i++, Section++) {
-               /* Don't try to copy discardable sections with zero size */
-               if ((Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) &&
-                   !Section->Misc.VirtualSize)
-                       continue;
-
-               base = ImageAddress (buffer, context.ImageSize,
-                                    Section->VirtualAddress);
-               end = ImageAddress (buffer, context.ImageSize,
-                                   Section->VirtualAddress
-                                    + Section->Misc.VirtualSize - 1);
-
-               if (end < base) {
-                       perror(L"Section %d has negative size\n", i);
-                       gBS->FreePages(*alloc_address, *alloc_pages);
-                       return EFI_UNSUPPORTED;
-               }
-
-               if (Section->VirtualAddress <= context.EntryPoint &&
-                   (Section->VirtualAddress + Section->SizeOfRawData - 1)
-                   > context.EntryPoint)
-                       found_entry_point++;
-
-               /* We do want to process .reloc, but it's often marked
-                * discardable, so we don't want to memcpy it. */
-               if (CompareMem(Section->Name, ".reloc\0\0", 8) == 0) {
-                       if (RelocSection) {
-                               perror(L"Image has multiple relocation sections\n");
-                               return EFI_UNSUPPORTED;
-                       }
-                       /* 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->SizeOfRawData &&
-                                       Section->Misc.VirtualSize &&
-                                       base && end &&
-                                       RelocBase == base &&
-                                       RelocBaseEnd == end) {
-                               RelocSection = Section;
-                       }
-               }
-
-               if (Section->Characteristics & EFI_IMAGE_SCN_MEM_DISCARDABLE) {
-                       continue;
-               }
-
-               if (!base) {
-                       perror(L"Section %d has invalid base address\n", i);
-                       return EFI_UNSUPPORTED;
-               }
-               if (!end) {
-                       perror(L"Section %d has zero size\n", i);
-                       return EFI_UNSUPPORTED;
-               }
-
-               if (!(Section->Characteristics & EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA) &&
-                   (Section->VirtualAddress < context.SizeOfHeaders ||
-                    Section->PointerToRawData < context.SizeOfHeaders)) {
-                       perror(L"Section %d is inside image headers\n", i);
-                       return EFI_UNSUPPORTED;
-               }
-
-               if (Section->Characteristics & EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA) {
-                       ZeroMem(base, Section->Misc.VirtualSize);
-               } else {
-                       if (Section->PointerToRawData < context.SizeOfHeaders) {
-                               perror(L"Section %d is inside image headers\n", i);
-                               return EFI_UNSUPPORTED;
-                       }
-
-                       if (Section->SizeOfRawData > 0)
-                               CopyMem(base, data + Section->PointerToRawData,
-                                       Section->SizeOfRawData);
-
-                       if (Section->SizeOfRawData < Section->Misc.VirtualSize)
-                               ZeroMem(base + Section->SizeOfRawData,
-                                       Section->Misc.VirtualSize - Section->SizeOfRawData);
-               }
-       }
-
-       if (context.NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC) {
-               perror(L"Image has no relocation entry\n");
-               FreePool(buffer);
-               return EFI_UNSUPPORTED;
-       }
-
-       if (context.RelocDir->Size && RelocSection) {
-               /*
-                * Run the relocation fixups
-                */
-               efi_status = relocate_coff(&context, RelocSection, data,
-                                          buffer);
-
-               if (EFI_ERROR(efi_status)) {
-                       perror(L"Relocation failed: %r\n", efi_status);
-                       FreePool(buffer);
-                       return efi_status;
-               }
-       }
-
-       /*
-        * grub needs to know its location and size in memory, so fix up
-        * the loaded image protocol values
-        */
-       li->ImageBase = buffer;
-       li->ImageSize = context.ImageSize;
-
-       /* Pass the load options to the second stage loader */
-       if ( load_options ) {
-               li->LoadOptions = load_options;
-               li->LoadOptionsSize = load_options_size;
-       }
-
-       if (!found_entry_point) {
-               perror(L"Entry point is not within sections\n");
-               return EFI_UNSUPPORTED;
-       }
-       if (found_entry_point > 1) {
-               perror(L"%d sections contain entry point\n");
-               return EFI_UNSUPPORTED;
-       }
-
-       return EFI_SUCCESS;
-}
-
 static int
 should_use_fallback(EFI_HANDLE image_handle)
 {
diff --git a/shim.h b/shim.h
index 433a34d0e106356a6d4aed13f850bc5e54c8bdf5..016222999b4c00d15aa0cd3581c2b680767528a6 100644 (file)
--- a/shim.h
+++ b/shim.h
@@ -29,6 +29,7 @@
 #undef uefi_call_wrapper
 
 #include <stddef.h>
+#include <stdint.h>
 
 #define nonnull(...) __attribute__((__nonnull__(__VA_ARGS__)))
 
 #include "include/netboot.h"
 #include "include/passwordcrypt.h"
 #include "include/peimage.h"
+#include "include/pe.h"
 #include "include/replacements.h"
 #if defined(OVERRIDE_SECURITY_POLICY)
 #include "include/security_policy.h"
@@ -204,6 +206,15 @@ extern UINT8 *build_cert;
 extern UINT8 user_insecure_mode;
 extern UINT8 ignore_db;
 extern UINT8 in_protocol;
+extern void *load_options;
+extern UINT32 load_options_size;
+
+BOOLEAN secure_mode (void);
+
+EFI_STATUS
+verify_buffer (char *data, int datasize,
+              PE_COFF_LOADER_IMAGE_CONTEXT *context,
+              UINT8 *sha256hash, UINT8 *sha1hash);
 
 #define perror_(file, line, func, fmt, ...) ({                                 \
                UINTN __perror_ret = 0;                                         \