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
--- /dev/null
+// 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
--- /dev/null
+// 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
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
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)
{
/*
* 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)
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)
/*
* 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;
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)
{
#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"
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; \