]> git.proxmox.com Git - efi-boot-shim.git/commitdiff
* debian/patches/unused-variable: remove unused variable size.
authorMathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
Tue, 26 Jul 2016 18:37:29 +0000 (14:37 -0400)
committerMathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
Tue, 26 Jul 2016 18:37:29 +0000 (14:37 -0400)
* debian/patches/binutils-version-matching: revert d9a4c912 to correctly
  match objcopy's version on Ubuntu.

.pc/applied-patches
.pc/binutils-version-matching/.timestamp [new file with mode: 0644]
.pc/binutils-version-matching/Makefile [new file with mode: 0644]
.pc/unused-variable/.timestamp [new file with mode: 0644]
.pc/unused-variable/shim.c [new file with mode: 0644]
Makefile
debian/changelog
debian/patches/binutils-version-matching [new file with mode: 0644]
debian/patches/series
debian/patches/unused-variable [new file with mode: 0644]
shim.c

index a5f3392d0248b2586ae66c864f83fad3abad8453..8613be11f8a21559ab9ce2488a423f0e6803414a 100644 (file)
@@ -1,2 +1,4 @@
 second-stage-path
 sbsigntool-not-pesign
+unused-variable
+binutils-version-matching
diff --git a/.pc/binutils-version-matching/.timestamp b/.pc/binutils-version-matching/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/binutils-version-matching/Makefile b/.pc/binutils-version-matching/Makefile
new file mode 100644 (file)
index 0000000..740ad53
--- /dev/null
@@ -0,0 +1,197 @@
+VERSION                = 0.9
+RELEASE                :=
+ifneq ($(RELEASE),"")
+       RELEASE:="-$(RELEASE)"
+endif
+
+CC             = $(CROSS_COMPILE)gcc
+LD             = $(CROSS_COMPILE)ld
+OBJCOPY                = $(CROSS_COMPILE)objcopy
+
+ARCH           = $(shell $(CC) -dumpmachine | cut -f1 -d- | sed s,i[3456789]86,ia32,)
+OBJCOPY_GTE224  = $(shell expr `$(OBJCOPY) --version |grep ^"GNU objcopy" | sed 's/^.version //g' | cut -f1-2 -d.` \>= 2.24)
+
+SUBDIRS                = Cryptlib lib
+
+LIB_PATH       = /usr/lib64
+
+EFI_INCLUDE    := /usr/include/efi
+EFI_INCLUDES   = -nostdinc -ICryptlib -ICryptlib/Include -I$(EFI_INCLUDE) -I$(EFI_INCLUDE)/$(ARCH) -I$(EFI_INCLUDE)/protocol -I$(shell pwd)/include
+EFI_PATH       := /usr/lib64/gnuefi
+
+LIB_GCC                = $(shell $(CC) -print-libgcc-file-name)
+EFI_LIBS       = -lefi -lgnuefi --start-group Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a --end-group $(LIB_GCC) 
+
+EFI_CRT_OBJS   = $(EFI_PATH)/crt0-efi-$(ARCH).o
+EFI_LDS                = elf_$(ARCH)_efi.lds
+
+DEFAULT_LOADER := \\\\grubx64.efi
+CFLAGS         = -ggdb -O0 -fno-stack-protector -fno-strict-aliasing -fpic \
+                 -fshort-wchar -Wall -Wsign-compare -Werror -fno-builtin \
+                 -Werror=sign-compare -ffreestanding -std=gnu89 \
+                 -I$(shell $(CC) -print-file-name=include) \
+                 "-DDEFAULT_LOADER=L\"$(DEFAULT_LOADER)\"" \
+                 "-DDEFAULT_LOADER_CHAR=\"$(DEFAULT_LOADER)\"" \
+                 $(EFI_INCLUDES)
+
+ifneq ($(origin OVERRIDE_SECURITY_POLICY), undefined)
+       CFLAGS  += -DOVERRIDE_SECURITY_POLICY
+endif
+
+ifeq ($(ARCH),x86_64)
+       CFLAGS  += -mno-mmx -mno-sse -mno-red-zone -nostdinc \
+               -maccumulate-outgoing-args \
+               -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI \
+               -DNO_BUILTIN_VA_FUNCS \
+               "-DEFI_ARCH=L\"x64\"" \
+               "-DDEBUGDIR=L\"/usr/lib/debug/usr/share/shim/x64-$(VERSION)$(RELEASE)/\""
+endif
+ifeq ($(ARCH),ia32)
+       CFLAGS  += -mno-mmx -mno-sse -mno-red-zone -nostdinc \
+               -maccumulate-outgoing-args -m32 \
+               "-DEFI_ARCH=L\"ia32\"" \
+               "-DDEBUGDIR=L\"/usr/lib/debug/usr/share/shim/ia32-$(VERSION)$(RELEASE)/\""
+endif
+ifeq ($(ARCH),aarch64)
+       CFLAGS += "-DEFI_ARCH=L\"aa64\"" \
+               "-DDEBUGDIR=L\"/usr/lib/debug/usr/share/shim/aa64-$(VERSION)$(RELEASE)/\""
+endif
+
+ifneq ($(origin VENDOR_CERT_FILE), undefined)
+       CFLAGS += -DVENDOR_CERT_FILE=\"$(VENDOR_CERT_FILE)\"
+endif
+ifneq ($(origin VENDOR_DBX_FILE), undefined)
+       CFLAGS += -DVENDOR_DBX_FILE=\"$(VENDOR_DBX_FILE)\"
+endif
+
+LDFLAGS                = --hash-style=sysv -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L$(EFI_PATH) -L$(LIB_PATH) -LCryptlib -LCryptlib/OpenSSL $(EFI_CRT_OBJS) --build-id=sha1
+
+TARGET = shim.efi MokManager.efi.signed fallback.efi.signed
+OBJS   = shim.o netboot.o cert.o replacements.o tpm.o version.o
+KEYS   = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer
+SOURCES        = shim.c shim.h netboot.c include/PeImage.h include/wincert.h include/console.h replacements.c replacements.h tpm.c tpm.h version.c version.h
+MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o
+MOK_SOURCES = MokManager.c shim.h include/console.h PasswordCrypt.c PasswordCrypt.h crypt_blowfish.c crypt_blowfish.h
+FALLBACK_OBJS = fallback.o
+FALLBACK_SRCS = fallback.c
+
+all: $(TARGET)
+
+shim.crt:
+       ./make-certs shim shim@xn--u4h.net all codesign 1.3.6.1.4.1.311.10.3.1 </dev/null
+
+shim.cer: shim.crt
+       openssl x509 -outform der -in $< -out $@
+
+shim_cert.h: shim.cer
+       echo "static UINT8 shim_cert[] = {" > $@
+       hexdump -v -e '1/1 "0x%02x, "' $< >> $@
+       echo "};" >> $@
+
+version.c : version.c.in
+       sed     -e "s,@@VERSION@@,$(VERSION)," \
+               -e "s,@@UNAME@@,$(shell uname -a)," \
+               -e "s,@@COMMIT@@,$(shell if [ -d .git ] ; then git log -1 --pretty=format:%H ; elif [ -f commit ]; then cat commit ; else echo commit id not available; fi)," \
+               < version.c.in > version.c
+
+certdb/secmod.db: shim.crt
+       -mkdir certdb
+       pk12util -d certdb/ -i shim.p12 -W "" -K ""
+       certutil -d certdb/ -A -i shim.crt -n shim -t u
+
+shim.o: $(SOURCES) shim_cert.h
+shim.o: $(wildcard *.h)
+
+cert.o : cert.S
+       $(CC) $(CFLAGS) -c -o $@ $<
+
+shim.so: $(OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a
+       $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS)
+
+fallback.o: $(FALLBACK_SRCS)
+
+fallback.so: $(FALLBACK_OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a
+       $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS)
+
+MokManager.o: $(MOK_SOURCES)
+
+MokManager.so: $(MOK_OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a
+       $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) lib/lib.a
+
+Cryptlib/libcryptlib.a:
+       $(MAKE) -C Cryptlib
+
+Cryptlib/OpenSSL/libopenssl.a:
+       $(MAKE) -C Cryptlib/OpenSSL
+
+lib/lib.a:
+       $(MAKE) CFLAGS="$(CFLAGS)" -C lib
+
+ifeq ($(ARCH),aarch64)
+FORMAT         := -O binary
+SUBSYSTEM      := 0xa
+LDFLAGS                += --defsym=EFI_SUBSYSTEM=$(SUBSYSTEM)
+endif
+
+ifeq ($(ARCH),arm)
+FORMAT         := -O binary
+SUBSYSTEM      := 0xa
+LDFLAGS                += --defsym=EFI_SUBSYSTEM=$(SUBSYSTEM)
+endif
+
+FORMAT         ?= --target efi-app-$(ARCH)
+
+%.efi: %.so
+ifneq ($(OBJCOPY_GTE224),1)
+       $(error objcopy >= 2.24 is required)
+endif
+       $(OBJCOPY) -j .text -j .sdata -j .data \
+               -j .dynamic -j .dynsym  -j .rel* \
+               -j .rela* -j .reloc -j .eh_frame \
+               -j .vendor_cert \
+               $(FORMAT)  $^ $@
+       $(OBJCOPY) -j .text -j .sdata -j .data \
+               -j .dynamic -j .dynsym  -j .rel* \
+               -j .rela* -j .reloc -j .eh_frame \
+               -j .debug_info -j .debug_abbrev -j .debug_aranges \
+               -j .debug_line -j .debug_str -j .debug_ranges \
+               -j .note.gnu.build-id \
+               $(FORMAT) $^ $@.debug
+
+%.efi.signed: %.efi shim.crt
+       sbsign --key shim.key --cert shim.crt $<
+
+clean:
+       $(MAKE) -C Cryptlib clean
+       $(MAKE) -C Cryptlib/OpenSSL clean
+       $(MAKE) -C lib clean
+       rm -rf $(TARGET) $(OBJS) $(MOK_OBJS) $(FALLBACK_OBJS) $(KEYS) certdb
+       rm -f *.debug *.so *.efi *.tar.* version.c
+
+GITTAG = $(VERSION)
+
+test-archive:
+       @rm -rf /tmp/shim-$(VERSION) /tmp/shim-$(VERSION)-tmp
+       @mkdir -p /tmp/shim-$(VERSION)-tmp
+       @git archive --format=tar $(shell git branch | awk '/^*/ { print $$2 }') | ( cd /tmp/shim-$(VERSION)-tmp/ ; tar x )
+       @git diff | ( cd /tmp/shim-$(VERSION)-tmp/ ; patch -s -p1 -b -z .gitdiff )
+       @mv /tmp/shim-$(VERSION)-tmp/ /tmp/shim-$(VERSION)/
+       @git log -1 --pretty=format:%H > /tmp/shim-$(VERSION)/commit
+       @dir=$$PWD; cd /tmp; tar -c --bzip2 -f $$dir/shim-$(VERSION).tar.bz2 shim-$(VERSION)
+       @rm -rf /tmp/shim-$(VERSION)
+       @echo "The archive is in shim-$(VERSION).tar.bz2"
+
+tag:
+       git tag --sign $(GITTAG) refs/heads/master
+
+archive: tag
+       @rm -rf /tmp/shim-$(VERSION) /tmp/shim-$(VERSION)-tmp
+       @mkdir -p /tmp/shim-$(VERSION)-tmp
+       @git archive --format=tar $(GITTAG) | ( cd /tmp/shim-$(VERSION)-tmp/ ; tar x )
+       @mv /tmp/shim-$(VERSION)-tmp/ /tmp/shim-$(VERSION)/
+       @git log -1 --pretty=format:%H > /tmp/shim-$(VERSION)/commit
+       @dir=$$PWD; cd /tmp; tar -c --bzip2 -f $$dir/shim-$(VERSION).tar.bz2 shim-$(VERSION)
+       @rm -rf /tmp/shim-$(VERSION)
+       @echo "The archive is in shim-$(VERSION).tar.bz2"
+
+export ARCH CC LD OBJCOPY EFI_INCLUDE
diff --git a/.pc/unused-variable/.timestamp b/.pc/unused-variable/.timestamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/.pc/unused-variable/shim.c b/.pc/unused-variable/shim.c
new file mode 100644 (file)
index 0000000..ed01899
--- /dev/null
@@ -0,0 +1,2658 @@
+/*
+ * shim - trivial UEFI first-stage bootloader
+ *
+ * Copyright 2012 Red Hat, Inc <mjg@redhat.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Significant portions of this code are derived from Tianocore
+ * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel
+ * Corporation.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+#include <Library/BaseCryptLib.h>
+#include "PeImage.h"
+#include "shim.h"
+#include "netboot.h"
+#include "shim_cert.h"
+#include "replacements.h"
+#include "tpm.h"
+#include "ucs2.h"
+
+#include "guid.h"
+#include "variables.h"
+#include "efiauthenticated.h"
+#include "security_policy.h"
+#include "console.h"
+#include "version.h"
+
+#define FALLBACK L"\\fallback.efi"
+#define MOK_MANAGER L"\\MokManager.efi"
+
+static EFI_SYSTEM_TABLE *systab;
+static EFI_HANDLE image_handle;
+static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table);
+
+static CHAR16 *second_stage;
+static void *load_options;
+static UINT32 load_options_size;
+static UINT8 in_protocol;
+
+#define perror(fmt, ...) ({                                            \
+               UINTN __perror_ret = 0;                                 \
+               if (!in_protocol)                                       \
+                       __perror_ret = Print((fmt), ##__VA_ARGS__);     \
+               __perror_ret;                                           \
+       })
+
+EFI_GUID SHIM_LOCK_GUID = { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} };
+
+/*
+ * The vendor certificate used for validating the second stage loader
+ */
+extern struct {
+       UINT32 vendor_cert_size;
+       UINT32 vendor_dbx_size;
+       UINT32 vendor_cert_offset;
+       UINT32 vendor_dbx_offset;
+} cert_table;
+
+UINT32 vendor_cert_size;
+UINT32 vendor_dbx_size;
+UINT8 *vendor_cert;
+UINT8 *vendor_dbx;
+
+/*
+ * indicator of how an image has been verified
+ */
+verification_method_t verification_method;
+int loader_is_participating;
+
+#define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }}
+
+UINT8 user_insecure_mode;
+UINT8 ignore_db;
+
+typedef enum {
+       DATA_FOUND,
+       DATA_NOT_FOUND,
+       VAR_NOT_FOUND
+} CHECK_STATUS;
+
+typedef struct {
+       UINT32 MokSize;
+       UINT8 *Mok;
+} MokListNode;
+
+/*
+ * Perform basic bounds checking of the intra-image pointers
+ */
+static void *ImageAddress (void *image, unsigned int size, unsigned int address)
+{
+       if (address > size)
+               return NULL;
+
+       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(__arch64__)
+       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, *FixupData = NULL;
+       UINT16 *Fixup16;
+       UINT32 *Fixup32;
+       UINT64 *Fixup64;
+       int size = context->ImageSize;
+       void *ImageEnd = (char *)orig + size;
+       int n = 0;
+
+       if (image_is_64_bit(context->PEHdr))
+               context->PEHdr->Pe32Plus.OptionalHeader.ImageBase = (UINT64)(unsigned long)data;
+       else
+               context->PEHdr->Pe32.OptionalHeader.ImageBase = (UINT32)(unsigned long)data;
+
+       /* 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) || (RelocBase->SizeOfBlock > context->RelocDir->Size)) {
+                       perror(L"Reloc %d block size %d is invalid\n", n, RelocBase->SizeOfBlock);
+                       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)));
+                               if (FixupData != NULL) {
+                                       *(UINT16 *) FixupData = *Fixup16;
+                                       FixupData             = FixupData + sizeof (UINT16);
+                               }
+                               break;
+
+                       case EFI_IMAGE_REL_BASED_LOW:
+                               Fixup16   = (UINT16 *) Fixup;
+                               *Fixup16  = (UINT16) (*Fixup16 + (UINT16) Adjust);
+                               if (FixupData != NULL) {
+                                       *(UINT16 *) FixupData = *Fixup16;
+                                       FixupData             = FixupData + sizeof (UINT16);
+                               }
+                               break;
+
+                       case EFI_IMAGE_REL_BASED_HIGHLOW:
+                               Fixup32   = (UINT32 *) Fixup;
+                               *Fixup32  = *Fixup32 + (UINT32) Adjust;
+                               if (FixupData != NULL) {
+                                       FixupData             = ALIGN_POINTER (FixupData, sizeof (UINT32));
+                                       *(UINT32 *)FixupData  = *Fixup32;
+                                       FixupData             = FixupData + sizeof (UINT32);
+                               }
+                               break;
+
+                       case EFI_IMAGE_REL_BASED_DIR64:
+                               Fixup64 = (UINT64 *) Fixup;
+                               *Fixup64 = *Fixup64 + (UINT64) Adjust;
+                               if (FixupData != NULL) {
+                                       FixupData = ALIGN_POINTER (FixupData, sizeof(UINT64));
+                                       *(UINT64 *)(FixupData) = *Fixup64;
+                                       FixupData = FixupData + sizeof(UINT64);
+                               }
+                               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 BOOLEAN verify_x509(UINT8 *Cert, UINTN CertSize)
+{
+       UINTN length;
+
+       if (!Cert || CertSize < 4)
+               return FALSE;
+
+       /*
+        * A DER encoding x509 certificate starts with SEQUENCE(0x30),
+        * the number of length bytes, and the number of value bytes.
+        * The size of a x509 certificate is usually between 127 bytes
+        * and 64KB. For convenience, assume the number of value bytes
+        * is 2, i.e. the second byte is 0x82.
+        */
+       if (Cert[0] != 0x30 || Cert[1] != 0x82)
+               return FALSE;
+
+       length = Cert[2]<<8 | Cert[3];
+       if (length != (CertSize - 4))
+               return FALSE;
+
+       return TRUE;
+}
+
+static CHECK_STATUS check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList,
+                                        UINTN dbsize,
+                                        WIN_CERTIFICATE_EFI_PKCS *data,
+                                        UINT8 *hash)
+{
+       EFI_SIGNATURE_DATA *Cert;
+       UINTN CertSize;
+       BOOLEAN IsFound = FALSE;
+       EFI_GUID CertType = X509_GUID;
+
+       while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
+               if (CompareGuid (&CertList->SignatureType, &CertType) == 0) {
+                       Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+                       CertSize = CertList->SignatureSize - sizeof(EFI_GUID);
+                       if (verify_x509(Cert->SignatureData, CertSize)) {
+                               IsFound = AuthenticodeVerify (data->CertData,
+                                                             data->Hdr.dwLength - sizeof(data->Hdr),
+                                                             Cert->SignatureData,
+                                                             CertSize,
+                                                             hash, SHA256_DIGEST_SIZE);
+                               if (IsFound)
+                                       return DATA_FOUND;
+                       } else if (verbose) {
+                               console_notify(L"Not a DER encoding x.509 Certificate");
+                       }
+               }
+
+               dbsize -= CertList->SignatureListSize;
+               CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
+       }
+
+       return DATA_NOT_FOUND;
+}
+
+static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid,
+                                 WIN_CERTIFICATE_EFI_PKCS *data, UINT8 *hash)
+{
+       CHECK_STATUS rc;
+       EFI_STATUS efi_status;
+       EFI_SIGNATURE_LIST *CertList;
+       UINTN dbsize = 0;
+       UINT8 *db;
+
+       efi_status = get_variable(dbname, &db, &dbsize, guid);
+
+       if (efi_status != EFI_SUCCESS)
+               return VAR_NOT_FOUND;
+
+       CertList = (EFI_SIGNATURE_LIST *)db;
+
+       rc = check_db_cert_in_ram(CertList, dbsize, data, hash);
+
+       FreePool(db);
+
+       return rc;
+}
+
+/*
+ * Check a hash against an EFI_SIGNATURE_LIST in a buffer
+ */
+static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList,
+                                        UINTN dbsize, UINT8 *data,
+                                        int SignatureSize, EFI_GUID CertType)
+{
+       EFI_SIGNATURE_DATA *Cert;
+       UINTN CertCount, Index;
+       BOOLEAN IsFound = FALSE;
+
+       while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
+               CertCount = (CertList->SignatureListSize -sizeof (EFI_SIGNATURE_LIST) - CertList->SignatureHeaderSize) / CertList->SignatureSize;
+               Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+               if (CompareGuid(&CertList->SignatureType, &CertType) == 0) {
+                       for (Index = 0; Index < CertCount; Index++) {
+                               if (CompareMem (Cert->SignatureData, data, SignatureSize) == 0) {
+                                       //
+                                       // Find the signature in database.
+                                       //
+                                       IsFound = TRUE;
+                                       break;
+                               }
+
+                               Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
+                       }
+                       if (IsFound) {
+                               break;
+                       }
+               }
+
+               dbsize -= CertList->SignatureListSize;
+               CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
+       }
+
+       if (IsFound)
+               return DATA_FOUND;
+
+       return DATA_NOT_FOUND;
+}
+
+/*
+ * Check a hash against an EFI_SIGNATURE_LIST in a UEFI variable
+ */
+static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data,
+                                 int SignatureSize, EFI_GUID CertType)
+{
+       EFI_STATUS efi_status;
+       EFI_SIGNATURE_LIST *CertList;
+       UINTN dbsize = 0;
+       UINT8 *db;
+
+       efi_status = get_variable(dbname, &db, &dbsize, guid);
+
+       if (efi_status != EFI_SUCCESS) {
+               return VAR_NOT_FOUND;
+       }
+
+       CertList = (EFI_SIGNATURE_LIST *)db;
+
+       CHECK_STATUS rc = check_db_hash_in_ram(CertList, dbsize, data,
+                                               SignatureSize, CertType);
+       FreePool(db);
+       return rc;
+
+}
+
+/*
+ * Check whether the binary signature or hash are present in dbx or the
+ * built-in blacklist
+ */
+static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert,
+                                  UINT8 *sha256hash, UINT8 *sha1hash)
+{
+       EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
+       EFI_GUID shim_var = SHIM_LOCK_GUID;
+       EFI_SIGNATURE_LIST *dbx = (EFI_SIGNATURE_LIST *)vendor_dbx;
+
+       if (check_db_hash_in_ram(dbx, vendor_dbx_size, sha256hash,
+                                SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID) ==
+                               DATA_FOUND)
+               return EFI_ACCESS_DENIED;
+       if (check_db_hash_in_ram(dbx, vendor_dbx_size, sha1hash,
+                                SHA1_DIGEST_SIZE, EFI_CERT_SHA1_GUID) ==
+                               DATA_FOUND)
+               return EFI_ACCESS_DENIED;
+       if (cert && check_db_cert_in_ram(dbx, vendor_dbx_size, cert,
+                                        sha256hash) == DATA_FOUND)
+               return EFI_ACCESS_DENIED;
+
+       if (check_db_hash(L"dbx", secure_var, sha256hash, SHA256_DIGEST_SIZE,
+                         EFI_CERT_SHA256_GUID) == DATA_FOUND)
+               return EFI_ACCESS_DENIED;
+       if (check_db_hash(L"dbx", secure_var, sha1hash, SHA1_DIGEST_SIZE,
+                         EFI_CERT_SHA1_GUID) == DATA_FOUND)
+               return EFI_ACCESS_DENIED;
+       if (cert && check_db_cert(L"dbx", secure_var, cert, sha256hash) ==
+                               DATA_FOUND)
+               return EFI_ACCESS_DENIED;
+       if (check_db_hash(L"MokListX", shim_var, sha256hash, SHA256_DIGEST_SIZE,
+                         EFI_CERT_SHA256_GUID) == DATA_FOUND) {
+               return EFI_ACCESS_DENIED;
+       }
+       if (cert && check_db_cert(L"MokListX", shim_var, cert, sha256hash) ==
+                               DATA_FOUND) {
+               return EFI_ACCESS_DENIED;
+       }
+
+       return EFI_SUCCESS;
+}
+
+static void update_verification_method(verification_method_t method)
+{
+       if (verification_method == VERIFIED_BY_NOTHING)
+               verification_method = method;
+}
+
+/*
+ * Check whether the binary signature or hash are present in db or MokList
+ */
+static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert,
+                                  UINT8 *sha256hash, UINT8 *sha1hash)
+{
+       EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
+       EFI_GUID shim_var = SHIM_LOCK_GUID;
+
+       if (!ignore_db) {
+               if (check_db_hash(L"db", secure_var, sha256hash, SHA256_DIGEST_SIZE,
+                                       EFI_CERT_SHA256_GUID) == DATA_FOUND) {
+                       update_verification_method(VERIFIED_BY_HASH);
+                       return EFI_SUCCESS;
+               }
+               if (check_db_hash(L"db", secure_var, sha1hash, SHA1_DIGEST_SIZE,
+                                       EFI_CERT_SHA1_GUID) == DATA_FOUND) {
+                       verification_method = VERIFIED_BY_HASH;
+                       update_verification_method(VERIFIED_BY_HASH);
+                       return EFI_SUCCESS;
+               }
+               if (cert && check_db_cert(L"db", secure_var, cert, sha256hash)
+                                       == DATA_FOUND) {
+                       verification_method = VERIFIED_BY_CERT;
+                       update_verification_method(VERIFIED_BY_CERT);
+                       return EFI_SUCCESS;
+               }
+       }
+
+       if (check_db_hash(L"MokList", shim_var, sha256hash, SHA256_DIGEST_SIZE,
+                         EFI_CERT_SHA256_GUID) == DATA_FOUND) {
+               verification_method = VERIFIED_BY_HASH;
+               update_verification_method(VERIFIED_BY_HASH);
+               return EFI_SUCCESS;
+       }
+       if (cert && check_db_cert(L"MokList", shim_var, cert, sha256hash) ==
+                               DATA_FOUND) {
+               verification_method = VERIFIED_BY_CERT;
+               update_verification_method(VERIFIED_BY_CERT);
+               return EFI_SUCCESS;
+       }
+
+       update_verification_method(VERIFIED_BY_NOTHING);
+       return EFI_ACCESS_DENIED;
+}
+
+/*
+ * Check whether we're in Secure Boot and user mode
+ */
+
+static BOOLEAN secure_mode (void)
+{
+       static int first = 1;
+       if (user_insecure_mode)
+               return FALSE;
+
+       if (variable_is_secureboot() != 1) {
+               if (verbose && !in_protocol && first)
+                       console_notify(L"Secure boot not enabled");
+               first = 0;
+               return FALSE;
+       }
+
+       /* If we /do/ have "SecureBoot", but /don't/ have "SetupMode",
+        * then the implementation is bad, but we assume that secure boot is
+        * enabled according to the status of "SecureBoot".  If we have both
+        * of them, then "SetupMode" may tell us additional data, and we need
+        * to consider it.
+        */
+       if (variable_is_setupmode(0) == 1) {
+               if (verbose && !in_protocol && first)
+                       console_notify(L"Platform is in setup mode");
+               first = 0;
+               return FALSE;
+       }
+
+       first = 0;
+       return TRUE;
+}
+
+#define check_size_line(data, datasize_in, hashbase, hashsize, l) ({   \
+       if ((unsigned long)hashbase >                                   \
+                       (unsigned long)data + datasize_in) {            \
+               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) {            \
+               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 status = EFI_SUCCESS;
+       EFI_IMAGE_DOS_HEADER *DosHdr = (void *)data;
+       unsigned int PEHdr_offset = 0;
+
+       if (datasize_in < 0) {
+               perror(L"Invalid data size\n");
+               return EFI_INVALID_PARAMETER;
+       }
+       size = datasize = (unsigned int)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");
+               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");
+               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");
+               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);
+               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");
+               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);
+                       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);
+                       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");
+               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);
+
+       /* 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");
+                       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);
+                       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");
+                       status = EFI_OUT_OF_RESOURCES;
+                       goto done;
+               }
+               SumOfBytesHashed += Section->SizeOfRawData;
+       }
+
+       /* Hash all remaining data */
+       if (datasize > SumOfBytesHashed) {
+               hashbase = data + SumOfBytesHashed;
+               hashsize = datasize - context->SecDir->Size - SumOfBytesHashed;
+               check_size(data, datasize_in, hashbase, hashsize);
+
+               if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+                   !(Sha1Update(sha1ctx, hashbase, hashsize))) {
+                       perror(L"Unable to generate hash\n");
+                       status = EFI_OUT_OF_RESOURCES;
+                       goto done;
+               }
+       }
+
+       if (!(Sha256Final(sha256ctx, sha256hash)) ||
+           !(Sha1Final(sha1ctx, sha1hash))) {
+               perror(L"Unable to finalise hash\n");
+               status = EFI_OUT_OF_RESOURCES;
+               goto done;
+       }
+
+done:
+       if (SectionHeader)
+               FreePool(SectionHeader);
+       if (sha1ctx)
+               FreePool(sha1ctx);
+       if (sha256ctx)
+               FreePool(sha256ctx);
+
+       return status;
+}
+
+/*
+ * Ensure that the MOK database hasn't been set or modified from an OS
+ */
+static EFI_STATUS verify_mok (void) {
+       EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+       EFI_STATUS status = EFI_SUCCESS;
+       UINT8 *MokListData = NULL;
+       UINTN MokListDataSize = 0;
+       UINT32 attributes;
+
+       status = get_variable_attr(L"MokList", &MokListData, &MokListDataSize,
+                                  shim_lock_guid, &attributes);
+
+       if (!EFI_ERROR(status) && attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
+               perror(L"MokList is compromised!\nErase all keys in MokList!\n");
+               if (LibDeleteVariable(L"MokList", &shim_lock_guid) != EFI_SUCCESS) {
+                       perror(L"Failed to erase MokList\n");
+                        return EFI_ACCESS_DENIED;
+               }
+       }
+
+       if (MokListData)
+               FreePool(MokListData);
+
+       return EFI_SUCCESS;
+}
+
+/*
+ * 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[SHA256_DIGEST_SIZE];
+       UINT8 sha1hash[SHA1_DIGEST_SIZE];
+       EFI_STATUS status = EFI_ACCESS_DENIED;
+       WIN_CERTIFICATE_EFI_PKCS *cert = NULL;
+       unsigned int size = datasize;
+
+       if (context->SecDir->Size != 0) {
+               cert = ImageAddress (data, size,
+                                    context->SecDir->VirtualAddress);
+
+               if (!cert) {
+                       perror(L"Certificate located outside the image\n");
+                       return EFI_INVALID_PARAMETER;
+               }
+
+               if (cert->Hdr.wCertificateType !=
+                   WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
+                       perror(L"Unsupported certificate type %x\n",
+                               cert->Hdr.wCertificateType);
+                       return EFI_UNSUPPORTED;
+               }
+       }
+
+       status = generate_hash(data, datasize, context, sha256hash, sha1hash);
+
+       if (status != EFI_SUCCESS)
+               return status;
+
+       /*
+        * Check that the MOK database hasn't been modified
+        */
+       status = verify_mok();
+       if (status != EFI_SUCCESS)
+               return status;
+
+       /*
+        * Ensure that the binary isn't blacklisted
+        */
+       status = check_blacklist(cert, sha256hash, sha1hash);
+
+       if (status != EFI_SUCCESS) {
+               perror(L"Binary is blacklisted\n");
+               return status;
+       }
+
+       /*
+        * Check whether the binary is whitelisted in any of the firmware
+        * databases
+        */
+       status = check_whitelist(cert, sha256hash, sha1hash);
+       if (status == EFI_SUCCESS)
+               return status;
+
+       if (cert) {
+               /*
+                * Check against the shim build key
+                */
+               if (sizeof(shim_cert) &&
+                   AuthenticodeVerify(cert->CertData,
+                              context->SecDir->Size - sizeof(cert->Hdr),
+                              shim_cert, sizeof(shim_cert), sha256hash,
+                              SHA256_DIGEST_SIZE)) {
+                       status = EFI_SUCCESS;
+                       return status;
+               }
+
+               /*
+                * And finally, check against shim's built-in key
+                */
+               if (vendor_cert_size && AuthenticodeVerify(cert->CertData,
+                                                       context->SecDir->Size - sizeof(cert->Hdr),
+                                                       vendor_cert, vendor_cert_size, sha256hash,
+                                                       SHA256_DIGEST_SIZE)) {
+                       status = EFI_SUCCESS;
+                       return status;
+               }
+       }
+
+       status = EFI_ACCESS_DENIED;
+
+       return 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;
+
+       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;
+               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;
+               OptHeaderSize = sizeof(EFI_IMAGE_OPTIONAL_HEADER32);
+       }
+
+       if (context->SectionAlignment < 0x1000)
+               context->SectionAlignment = 0x1000;
+       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) {
+               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_STATUS efi_status;
+       char *buffer;
+       int i;
+       unsigned int size;
+       EFI_IMAGE_SECTION_HEADER *Section;
+       char *base, *end;
+       PE_COFF_LOADER_IMAGE_CONTEXT context;
+       unsigned int alignment;
+       int found_entry_point = 0;
+
+       /*
+        * The binary header contains relevant context and section pointers
+        */
+       efi_status = read_header(data, datasize, &context);
+       if (efi_status != EFI_SUCCESS) {
+               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
+        */
+       if (secure_mode ()) {
+               efi_status = verify_buffer(data, datasize, &context);
+
+               if (EFI_ERROR(efi_status)) {
+                       console_error(L"Verification failed", efi_status);
+                       return efi_status;
+               } else {
+                       if (verbose)
+                               console_notify(L"Verification succeeded");
+               }
+       }
+
+       /* 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;
+
+       buffer = AllocatePool(context.ImageSize + context.SectionAlignment);
+       buffer = ALIGN_POINTER(buffer, alignment);
+
+       if (!buffer) {
+               perror(L"Failed to allocate image buffer\n");
+               return EFI_OUT_OF_RESOURCES;
+       }
+
+       CopyMem(buffer, data, context.SizeOfHeaders);
+
+       entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint);
+       if (!entry_point) {
+               perror(L"Entry point is invalid\n");
+               FreePool(buffer);
+               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++) {
+               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);
+                       FreePool(buffer);
+                       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->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_status != EFI_SUCCESS) {
+                       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 */
+       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)
+{
+       EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
+       EFI_LOADED_IMAGE *li;
+       unsigned int pathlen = 0;
+       CHAR16 *bootpath = NULL;
+       EFI_FILE_IO_INTERFACE *fio = NULL;
+       EFI_FILE *vh = NULL;
+       EFI_FILE *fh = NULL;
+       EFI_STATUS rc;
+       int ret = 0;
+
+       rc = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
+                                      &loaded_image_protocol, (void **)&li);
+       if (EFI_ERROR(rc)) {
+               perror(L"Could not get image for bootx64.efi: %r\n", rc);
+               return 0;
+       }
+
+       bootpath = DevicePathToStr(li->FilePath);
+
+       /* Check the beginning of the string and the end, to avoid
+        * caring about which arch this is. */
+       /* I really don't know why, but sometimes bootpath gives us
+        * L"\\EFI\\BOOT\\/BOOTX64.EFI".  So just handle that here...
+        */
+       if (StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\BOOT", 14) &&
+                       StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\/BOOT", 15) &&
+                       StrnCaseCmp(bootpath, L"EFI\\BOOT\\BOOT", 13) &&
+                       StrnCaseCmp(bootpath, L"EFI\\BOOT\\/BOOT", 14))
+               goto error;
+
+       pathlen = StrLen(bootpath);
+       if (pathlen < 5 || StrCaseCmp(bootpath + pathlen - 4, L".EFI"))
+               goto error;
+
+       rc = uefi_call_wrapper(BS->HandleProtocol, 3, li->DeviceHandle,
+                              &FileSystemProtocol, (void **)&fio);
+       if (EFI_ERROR(rc)) {
+               perror(L"Could not get fio for li->DeviceHandle: %r\n", rc);
+               goto error;
+       }
+
+       rc = uefi_call_wrapper(fio->OpenVolume, 2, fio, &vh);
+       if (EFI_ERROR(rc)) {
+               perror(L"Could not open fio volume: %r\n", rc);
+               goto error;
+       }
+
+       rc = uefi_call_wrapper(vh->Open, 5, vh, &fh, L"\\EFI\\BOOT" FALLBACK,
+                              EFI_FILE_MODE_READ, 0);
+       if (EFI_ERROR(rc)) {
+               /* Do not print the error here - this is an acceptable case
+                * for removable media, where we genuinely don't want
+                * fallback.efi to exist.
+                * Print(L"Could not open \"\\EFI\\BOOT%s\": %d\n", FALLBACK,
+                *       rc);
+                */
+               uefi_call_wrapper(vh->Close, 1, vh);
+               goto error;
+       }
+
+       ret = 1;
+error:
+       if (fh)
+               uefi_call_wrapper(fh->Close, 1, fh);
+       if (vh)
+           uefi_call_wrapper(vh->Close, 1, vh);
+       if (bootpath)
+               FreePool(bootpath);
+
+       return ret;
+}
+
+/*
+ * Generate the path of an executable given shim's path and the name
+ * of the executable
+ */
+static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath,
+                               CHAR16 **PathName)
+{
+       EFI_DEVICE_PATH *devpath;
+       unsigned int i;
+       int j, last = -1;
+       unsigned int pathlen = 0;
+       EFI_STATUS efi_status = EFI_SUCCESS;
+       CHAR16 *bootpath;
+
+       /*
+        * Suuuuper lazy technique here, but check and see if this is a full
+        * path to something on the ESP.  Backwards compatibility demands
+        * that we don't just use \\, becuase we (not particularly brightly)
+        * used to require that the relative file path started with that.
+        *
+        * If it is a full path, don't try to merge it with the directory
+        * from our Loaded Image handle.
+        */
+       if (StrSize(ImagePath) > 5 && StrnCmp(ImagePath, L"\\EFI\\", 5) == 0) {
+               *PathName = StrDuplicate(ImagePath);
+               if (!*PathName) {
+                       perror(L"Failed to allocate path buffer\n");
+                       return EFI_OUT_OF_RESOURCES;
+               }
+               return EFI_SUCCESS;
+       }
+
+       devpath = li->FilePath;
+
+       bootpath = DevicePathToStr(devpath);
+
+       pathlen = StrLen(bootpath);
+
+       /*
+        * DevicePathToStr() concatenates two nodes with '/'.
+        * Convert '/' to '\\'.
+        */
+       for (i = 0; i < pathlen; i++) {
+               if (bootpath[i] == '/')
+                       bootpath[i] = '\\';
+       }
+
+       for (i=pathlen; i>0; i--) {
+               if (bootpath[i] == '\\' && bootpath[i-1] == '\\')
+                       bootpath[i] = '/';
+               else if (last == -1 && bootpath[i] == '\\')
+                       last = i;
+       }
+
+       if (last == -1 && bootpath[0] == '\\')
+               last = 0;
+       bootpath[last+1] = '\0';
+
+       if (last > 0) {
+               for (i = 0, j = 0; bootpath[i] != '\0'; i++) {
+                       if (bootpath[i] != '/') {
+                               bootpath[j] = bootpath[i];
+                               j++;
+                       }
+               }
+               bootpath[j] = '\0';
+       }
+
+       while (*ImagePath == '\\')
+               ImagePath++;
+
+       *PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath));
+
+       if (!*PathName) {
+               perror(L"Failed to allocate path buffer\n");
+               efi_status = EFI_OUT_OF_RESOURCES;
+               goto error;
+       }
+
+       *PathName[0] = '\0';
+       if (StrnCaseCmp(bootpath, ImagePath, StrLen(bootpath)))
+               StrCat(*PathName, bootpath);
+       StrCat(*PathName, ImagePath);
+
+error:
+       FreePool(bootpath);
+
+       return efi_status;
+}
+
+/*
+ * Open the second stage bootloader and read it into a buffer
+ */
+static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
+                             int *datasize, CHAR16 *PathName)
+{
+       EFI_GUID simple_file_system_protocol = SIMPLE_FILE_SYSTEM_PROTOCOL;
+       EFI_GUID file_info_id = EFI_FILE_INFO_ID;
+       EFI_STATUS efi_status;
+       EFI_HANDLE device;
+       EFI_FILE_INFO *fileinfo = NULL;
+       EFI_FILE_IO_INTERFACE *drive;
+       EFI_FILE *root, *grub;
+       UINTN buffersize = sizeof(EFI_FILE_INFO);
+
+       device = li->DeviceHandle;
+
+       /*
+        * Open the device
+        */
+       efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device,
+                                      &simple_file_system_protocol,
+                                      (void **)&drive);
+
+       if (efi_status != EFI_SUCCESS) {
+               perror(L"Failed to find fs: %r\n", efi_status);
+               goto error;
+       }
+
+       efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root);
+
+       if (efi_status != EFI_SUCCESS) {
+               perror(L"Failed to open fs: %r\n", efi_status);
+               goto error;
+       }
+
+       /*
+        * And then open the file
+        */
+       efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, PathName,
+                                      EFI_FILE_MODE_READ, 0);
+
+       if (efi_status != EFI_SUCCESS) {
+               perror(L"Failed to open %s - %r\n", PathName, efi_status);
+               goto error;
+       }
+
+       fileinfo = AllocatePool(buffersize);
+
+       if (!fileinfo) {
+               perror(L"Unable to allocate file info buffer\n");
+               efi_status = EFI_OUT_OF_RESOURCES;
+               goto error;
+       }
+
+       /*
+        * Find out how big the file is in order to allocate the storage
+        * buffer
+        */
+       efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id,
+                                      &buffersize, fileinfo);
+
+       if (efi_status == EFI_BUFFER_TOO_SMALL) {
+               FreePool(fileinfo);
+               fileinfo = AllocatePool(buffersize);
+               if (!fileinfo) {
+                       perror(L"Unable to allocate file info buffer\n");
+                       efi_status = EFI_OUT_OF_RESOURCES;
+                       goto error;
+               }
+               efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub,
+                                              &file_info_id, &buffersize,
+                                              fileinfo);
+       }
+
+       if (efi_status != EFI_SUCCESS) {
+               perror(L"Unable to get file info: %r\n", efi_status);
+               goto error;
+       }
+
+       buffersize = fileinfo->FileSize;
+
+       *data = AllocatePool(buffersize);
+
+       if (!*data) {
+               perror(L"Unable to allocate file buffer\n");
+               efi_status = EFI_OUT_OF_RESOURCES;
+               goto error;
+       }
+
+       /*
+        * Perform the actual read
+        */
+       efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize,
+                                      *data);
+
+       if (efi_status == EFI_BUFFER_TOO_SMALL) {
+               FreePool(*data);
+               *data = AllocatePool(buffersize);
+               efi_status = uefi_call_wrapper(grub->Read, 3, grub,
+                                              &buffersize, *data);
+       }
+
+       if (efi_status != EFI_SUCCESS) {
+               perror(L"Unexpected return from initial read: %r, buffersize %x\n", efi_status, buffersize);
+               goto error;
+       }
+
+       *datasize = buffersize;
+
+       FreePool(fileinfo);
+
+       return EFI_SUCCESS;
+error:
+       if (*data) {
+               FreePool(*data);
+               *data = NULL;
+       }
+
+       if (fileinfo)
+               FreePool(fileinfo);
+       return efi_status;
+}
+
+/*
+ * Protocol entry point. If secure boot is enabled, verify that the provided
+ * buffer is signed with a trusted key.
+ */
+EFI_STATUS shim_verify (void *buffer, UINT32 size)
+{
+       EFI_STATUS status = EFI_SUCCESS;
+       PE_COFF_LOADER_IMAGE_CONTEXT context;
+
+       loader_is_participating = 1;
+       in_protocol = 1;
+
+       if (!secure_mode())
+               goto done;
+
+       status = read_header(buffer, size, &context);
+       if (status != EFI_SUCCESS)
+               goto done;
+
+       status = verify_buffer(buffer, size, &context);
+done:
+       in_protocol = 0;
+       return status;
+}
+
+static EFI_STATUS shim_hash (char *data, int datasize,
+                            PE_COFF_LOADER_IMAGE_CONTEXT *context,
+                            UINT8 *sha256hash, UINT8 *sha1hash)
+{
+       EFI_STATUS status;
+
+       in_protocol = 1;
+       status = generate_hash(data, datasize, context, sha256hash, sha1hash);
+       in_protocol = 0;
+
+       return status;
+}
+
+static EFI_STATUS shim_read_header(void *data, unsigned int datasize,
+                                  PE_COFF_LOADER_IMAGE_CONTEXT *context)
+{
+       EFI_STATUS status;
+
+       in_protocol = 1;
+       status = read_header(data, datasize, context);
+       in_protocol = 0;
+
+       return status;
+}
+
+/*
+ * Load and run an EFI executable
+ */
+EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
+{
+       EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
+       EFI_STATUS efi_status;
+       EFI_LOADED_IMAGE *li, li_bak;
+       CHAR16 *PathName = NULL;
+       void *sourcebuffer = NULL;
+       UINT64 sourcesize = 0;
+       void *data = NULL;
+       int datasize;
+
+       /*
+        * We need to refer to the loaded image protocol on the running
+        * binary in order to find our path
+        */
+       efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
+                                      &loaded_image_protocol, (void **)&li);
+
+       if (efi_status != EFI_SUCCESS) {
+               perror(L"Unable to init protocol\n");
+               return efi_status;
+       }
+
+       /*
+        * Build a new path from the existing one plus the executable name
+        */
+       efi_status = generate_path(li, ImagePath, &PathName);
+
+       if (efi_status != EFI_SUCCESS) {
+               perror(L"Unable to generate path %s: %r\n", ImagePath, efi_status);
+               goto done;
+       }
+
+       if (findNetboot(li->DeviceHandle)) {
+               efi_status = parseNetbootinfo(image_handle);
+               if (efi_status != EFI_SUCCESS) {
+                       perror(L"Netboot parsing failed: %r\n", efi_status);
+                       return EFI_PROTOCOL_ERROR;
+               }
+               efi_status = FetchNetbootimage(image_handle, &sourcebuffer,
+                                              &sourcesize);
+               if (efi_status != EFI_SUCCESS) {
+                       perror(L"Unable to fetch TFTP image: %r\n", efi_status);
+                       return efi_status;
+               }
+               data = sourcebuffer;
+               datasize = sourcesize;
+       } else {
+               /*
+                * Read the new executable off disk
+                */
+               efi_status = load_image(li, &data, &datasize, PathName);
+
+               if (efi_status != EFI_SUCCESS) {
+                       perror(L"Failed to load image %s: %r\n", PathName, efi_status);
+                       goto done;
+               }
+       }
+
+       /* Measure the binary into the TPM */
+       tpm_log_event((EFI_PHYSICAL_ADDRESS)data, datasize, 9,
+                     (CHAR8 *)"Second stage bootloader");
+
+       /*
+        * We need to modify the loaded image protocol entry before running
+        * the new binary, so back it up
+        */
+       CopyMem(&li_bak, li, sizeof(li_bak));
+
+       /*
+        * Verify and, if appropriate, relocate and execute the executable
+        */
+       efi_status = handle_image(data, datasize, li);
+
+       if (efi_status != EFI_SUCCESS) {
+               perror(L"Failed to load image: %r\n", efi_status);
+               CopyMem(li, &li_bak, sizeof(li_bak));
+               goto done;
+       }
+
+       loader_is_participating = 0;
+
+       /*
+        * The binary is trusted and relocated. Run it
+        */
+       efi_status = uefi_call_wrapper(entry_point, 2, image_handle, systab);
+
+       /*
+        * Restore our original loaded image values
+        */
+       CopyMem(li, &li_bak, sizeof(li_bak));
+done:
+       if (PathName)
+               FreePool(PathName);
+
+       if (data)
+               FreePool(data);
+
+       return efi_status;
+}
+
+/*
+ * Load and run grub. If that fails because grub isn't trusted, load and
+ * run MokManager.
+ */
+EFI_STATUS init_grub(EFI_HANDLE image_handle)
+{
+       EFI_STATUS efi_status;
+       int use_fb = should_use_fallback(image_handle);
+
+       efi_status = start_image(image_handle, use_fb ? FALLBACK :second_stage);
+
+       if (efi_status == EFI_SECURITY_VIOLATION) {
+               efi_status = start_image(image_handle, MOK_MANAGER);
+               if (efi_status != EFI_SUCCESS) {
+                       Print(L"start_image() returned %r\n", efi_status);
+                       uefi_call_wrapper(BS->Stall, 1, 2000000);
+                       return efi_status;
+               }
+
+               efi_status = start_image(image_handle,
+                                        use_fb ? FALLBACK : second_stage);
+       }
+
+       if (efi_status != EFI_SUCCESS) {
+               Print(L"start_image() returned %r\n", efi_status);
+               uefi_call_wrapper(BS->Stall, 1, 2000000);
+       }
+
+       return efi_status;
+}
+
+/*
+ * Measure some of the MOK variables into the TPM
+ */
+EFI_STATUS measure_mok()
+{
+       EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+       EFI_STATUS efi_status;
+       UINT8 *Data = NULL;
+       UINTN DataSize = 0;
+
+       efi_status = get_variable(L"MokList", &Data, &DataSize, shim_lock_guid);
+       if (efi_status != EFI_SUCCESS)
+               return efi_status;
+
+       efi_status = tpm_log_event((EFI_PHYSICAL_ADDRESS)Data, DataSize, 14,
+                                  (CHAR8 *)"MokList");
+
+       FreePool(Data);
+
+       if (efi_status != EFI_SUCCESS)
+               return efi_status;
+
+       efi_status = get_variable(L"MokSBState", &Data, &DataSize,
+                                 shim_lock_guid);
+
+       if (efi_status != EFI_SUCCESS)
+               return efi_status;
+
+       efi_status = tpm_log_event((EFI_PHYSICAL_ADDRESS)Data, DataSize, 14,
+                                  (CHAR8 *)"MokSBState");
+
+       FreePool(Data);
+
+       return efi_status;
+}
+
+/*
+ * Copy the boot-services only MokList variable to the runtime-accessible
+ * MokListRT variable. It's not marked NV, so the OS can't modify it.
+ */
+EFI_STATUS mirror_mok_list()
+{
+       EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+       EFI_STATUS efi_status;
+       UINT8 *Data = NULL;
+       UINTN DataSize = 0;
+       void *FullData = NULL;
+       UINTN FullDataSize = 0;
+       EFI_SIGNATURE_LIST *CertList = NULL;
+       EFI_SIGNATURE_DATA *CertData = NULL;
+       uint8_t *p = NULL;
+
+       efi_status = get_variable(L"MokList", &Data, &DataSize, shim_lock_guid);
+       if (efi_status != EFI_SUCCESS)
+               DataSize = 0;
+
+       if (vendor_cert_size) {
+               FullDataSize = DataSize
+                            + sizeof (*CertList)
+                            + sizeof (EFI_GUID)
+                            + vendor_cert_size
+                            ;
+               FullData = AllocatePool(FullDataSize);
+               if (!FullData) {
+                       perror(L"Failed to allocate space for MokListRT\n");
+                       return EFI_OUT_OF_RESOURCES;
+               }
+               p = FullData;
+
+               if (efi_status == EFI_SUCCESS && DataSize > 0) {
+                       CopyMem(p, Data, DataSize);
+                       p += DataSize;
+               }
+               CertList = (EFI_SIGNATURE_LIST *)p;
+               p += sizeof (*CertList);
+               CertData = (EFI_SIGNATURE_DATA *)p;
+               p += sizeof (EFI_GUID);
+
+               CertList->SignatureType = EFI_CERT_X509_GUID;
+               CertList->SignatureListSize = vendor_cert_size
+                                             + sizeof (*CertList)
+                                             + sizeof (*CertData)
+                                             -1;
+               CertList->SignatureHeaderSize = 0;
+               CertList->SignatureSize = vendor_cert_size + sizeof (EFI_GUID);
+
+               CertData->SignatureOwner = SHIM_LOCK_GUID;
+               CopyMem(p, vendor_cert, vendor_cert_size);
+       } else {
+               FullDataSize = DataSize;
+               FullData = Data;
+       }
+
+       efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokListRT",
+                                      &shim_lock_guid,
+                                      EFI_VARIABLE_BOOTSERVICE_ACCESS
+                                      | EFI_VARIABLE_RUNTIME_ACCESS,
+                                      FullDataSize, FullData);
+       if (efi_status != EFI_SUCCESS) {
+               perror(L"Failed to set MokListRT: %r\n", efi_status);
+       }
+
+       return efi_status;
+}
+
+/*
+ * Copy the boot-services only MokListX variable to the runtime-accessible
+ * MokListXRT variable. It's not marked NV, so the OS can't modify it.
+ */
+EFI_STATUS mirror_mok_list_x()
+{
+       EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+       EFI_STATUS efi_status;
+       UINT8 *Data = NULL;
+       UINTN DataSize = 0;
+
+       efi_status = get_variable(L"MokListX", &Data, &DataSize, shim_lock_guid);
+       if (efi_status != EFI_SUCCESS)
+               return efi_status;
+
+       efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokListXRT",
+                                      &shim_lock_guid,
+                                      EFI_VARIABLE_BOOTSERVICE_ACCESS
+                                      | EFI_VARIABLE_RUNTIME_ACCESS,
+                                      DataSize, Data);
+       if (efi_status != EFI_SUCCESS) {
+               console_error(L"Failed to set MokListRT", efi_status);
+       }
+
+       return efi_status;
+}
+
+/*
+ * Copy the boot-services only MokSBState variable to the runtime-accessible
+ * MokSBStateRT variable. It's not marked NV, so the OS can't modify it.
+ */
+EFI_STATUS mirror_mok_sb_state()
+{
+       EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+       EFI_STATUS efi_status;
+       UINT8 *Data = NULL;
+       UINTN DataSize = 0;
+
+       efi_status = get_variable(L"MokSBState", &Data, &DataSize, shim_lock_guid);
+       if (efi_status != EFI_SUCCESS)
+               return efi_status;
+
+       efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokSBStateRT",
+                                      &shim_lock_guid,
+                                      EFI_VARIABLE_BOOTSERVICE_ACCESS
+                                      | EFI_VARIABLE_RUNTIME_ACCESS,
+                                      DataSize, Data);
+       if (efi_status != EFI_SUCCESS) {
+               console_error(L"Failed to set MokSBStateRT", efi_status);
+       }
+
+       return efi_status;
+}
+
+/*
+ * Check if a variable exists
+ */
+static BOOLEAN check_var(CHAR16 *varname)
+{
+       EFI_STATUS efi_status;
+       EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+       UINTN size = sizeof(UINT32);
+       UINT32 MokVar;
+       UINT32 attributes;
+
+       efi_status = uefi_call_wrapper(RT->GetVariable, 5, varname,
+                                      &shim_lock_guid, &attributes,
+                                      &size, (void *)&MokVar);
+
+       if (efi_status == EFI_SUCCESS || efi_status == EFI_BUFFER_TOO_SMALL)
+               return TRUE;
+
+       return FALSE;
+}
+
+/*
+ * If the OS has set any of these variables we need to drop into MOK and
+ * handle them appropriately
+ */
+EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
+{
+       EFI_STATUS efi_status;
+
+       if (check_var(L"MokNew") || check_var(L"MokSB") ||
+           check_var(L"MokPW") || check_var(L"MokAuth") ||
+           check_var(L"MokDel") || check_var(L"MokDB") ||
+           check_var(L"MokXNew") || check_var(L"MokXDel") ||
+           check_var(L"MokXAuth")) {
+               efi_status = start_image(image_handle, MOK_MANAGER);
+
+               if (efi_status != EFI_SUCCESS) {
+                       perror(L"Failed to start MokManager: %r\n", efi_status);
+                       return efi_status;
+               }
+       }
+
+       return EFI_SUCCESS;
+}
+
+/*
+ * Verify that MokSBState is valid, and if appropriate set insecure mode
+ */
+static EFI_STATUS check_mok_sb (void)
+{
+       EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+       EFI_STATUS status = EFI_SUCCESS;
+       UINT8 MokSBState;
+       UINTN MokSBStateSize = sizeof(MokSBState);
+       UINT32 attributes;
+
+       user_insecure_mode = 0;
+       ignore_db = 0;
+
+       status = uefi_call_wrapper(RT->GetVariable, 5, L"MokSBState", &shim_lock_guid,
+                                  &attributes, &MokSBStateSize, &MokSBState);
+       if (status != EFI_SUCCESS)
+               return EFI_ACCESS_DENIED;
+
+       /*
+        * Delete and ignore the variable if it's been set from or could be
+        * modified by the OS
+        */
+       if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
+               perror(L"MokSBState is compromised! Clearing it\n");
+               if (LibDeleteVariable(L"MokSBState", &shim_lock_guid) != EFI_SUCCESS) {
+                       perror(L"Failed to erase MokSBState\n");
+               }
+               status = EFI_ACCESS_DENIED;
+       } else {
+               if (MokSBState == 1) {
+                       user_insecure_mode = 1;
+               }
+       }
+
+       return status;
+}
+
+/*
+ * Verify that MokDBState is valid, and if appropriate set ignore db mode
+ */
+
+static EFI_STATUS check_mok_db (void)
+{
+       EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+       EFI_STATUS status = EFI_SUCCESS;
+       UINT8 MokDBState;
+       UINTN MokDBStateSize = sizeof(MokDBStateSize);
+       UINT32 attributes;
+
+       status = uefi_call_wrapper(RT->GetVariable, 5, L"MokDBState", &shim_lock_guid,
+                                  &attributes, &MokDBStateSize, &MokDBState);
+       if (status != EFI_SUCCESS)
+               return EFI_ACCESS_DENIED;
+
+       ignore_db = 0;
+
+       /*
+        * Delete and ignore the variable if it's been set from or could be
+        * modified by the OS
+        */
+       if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
+               perror(L"MokDBState is compromised! Clearing it\n");
+               if (LibDeleteVariable(L"MokDBState", &shim_lock_guid) != EFI_SUCCESS) {
+                       perror(L"Failed to erase MokDBState\n");
+               }
+               status = EFI_ACCESS_DENIED;
+       } else {
+               if (MokDBState == 1) {
+                       ignore_db = 1;
+               }
+       }
+
+       return status;
+}
+
+static EFI_STATUS mok_ignore_db()
+{
+       EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+       EFI_STATUS efi_status = EFI_SUCCESS;
+       UINT8 Data = 1;
+       UINTN DataSize = sizeof(UINT8);
+
+       check_mok_db();
+
+       if (ignore_db) {
+               efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokIgnoreDB",
+                               &shim_lock_guid,
+                               EFI_VARIABLE_BOOTSERVICE_ACCESS
+                               | EFI_VARIABLE_RUNTIME_ACCESS,
+                               DataSize, (void *)&Data);
+               if (efi_status != EFI_SUCCESS) {
+                       perror(L"Failed to set MokIgnoreDB: %r\n", efi_status);
+               }
+       }
+
+       return efi_status;
+
+}
+
+EFI_GUID bds_guid = { 0x8108ac4e, 0x9f11, 0x4d59, { 0x85, 0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2 } };
+
+static inline EFI_STATUS
+get_load_option_optional_data(UINT8 *data, UINTN data_size,
+                             UINT8 **od, UINTN *ods)
+{
+       /*
+        * If it's not at least Attributes + FilePathListLength +
+        * Description=L"" + 0x7fff0400 (EndEntrireDevicePath), it can't
+        * be valid.
+        */
+       if (data_size < (sizeof(UINT32) + sizeof(UINT16) + 2 + 4))
+               return EFI_INVALID_PARAMETER;
+
+       UINT8 *cur = data + sizeof(UINT32);
+       UINT16 fplistlen = *(UINT16 *)cur;
+       /*
+        * If there's not enough space for the file path list and the
+        * smallest possible description (L""), it's not valid.
+        */
+       if (fplistlen > data_size - (sizeof(UINT32) + 2 + 4))
+               return EFI_INVALID_PARAMETER;
+
+       cur += sizeof(UINT16);
+       UINTN limit = data_size - (cur - data) - fplistlen;
+       UINTN i;
+       for (i = 0; i < limit ; i++) {
+               /* If the description isn't valid UCS2-LE, it's not valid. */
+               if (i % 2 != 0) {
+                       if (cur[i] != 0)
+                               return EFI_INVALID_PARAMETER;
+               } else if (cur[i] == 0) {
+                       /* we've found the end */
+                       i++;
+                       if (i >= limit || cur[i] != 0)
+                               return EFI_INVALID_PARAMETER;
+                       break;
+               }
+       }
+       i++;
+       if (i > limit)
+               return EFI_INVALID_PARAMETER;
+
+       /*
+        * If i is limit, we know the rest of this is the FilePathList and
+        * there's no optional data.  So just bail now.
+        */
+       if (i == limit) {
+               *od = NULL;
+               *ods = 0;
+               return EFI_SUCCESS;
+       }
+
+       cur += i;
+       limit -= i;
+       limit += fplistlen;
+       i = 0;
+       while (limit - i >= 4) {
+               struct {
+                       UINT8 type;
+                       UINT8 subtype;
+                       UINT16 len;
+               } dp = {
+                       .type = cur[i],
+                       .subtype = cur[i+1],
+                       /*
+                        * it's a little endian UINT16, but we're not
+                        * guaranteed alignment is sane, so we can't just
+                        * typecast it directly.
+                        */
+                       .len = (cur[i+3] << 8) | cur[i+2],
+               };
+
+               /*
+                * We haven't found an EndEntire, so this has to be a valid
+                * EFI_DEVICE_PATH in order for the data to be valid.  That
+                * means it has to fit, and it can't be smaller than 4 bytes.
+                */
+               if (dp.len < 4 || dp.len > limit)
+                       return EFI_INVALID_PARAMETER;
+
+               /*
+                * see if this is an EndEntire node...
+                */
+               if (dp.type == 0x7f && dp.subtype == 0xff) {
+                       /*
+                        * if we've found the EndEntire node, it must be 4
+                        * bytes
+                        */
+                       if (dp.len != 4)
+                               return EFI_INVALID_PARAMETER;
+
+                       i += dp.len;
+                       break;
+               }
+
+               /*
+                * It's just some random DP node; skip it.
+                */
+               i += dp.len;
+       }
+       if (i != fplistlen)
+               return EFI_INVALID_PARAMETER;
+
+       /*
+        * if there's any space left, it's "optional data"
+        */
+       *od = cur + i;
+       *ods = limit - i;
+       return EFI_SUCCESS;
+}
+
+/*
+ * Check the load options to specify the second stage loader
+ */
+EFI_STATUS set_second_stage (EFI_HANDLE image_handle)
+{
+       EFI_STATUS status;
+       EFI_LOADED_IMAGE *li;
+       CHAR16 *start = NULL;
+       int remaining_size = 0;
+       CHAR16 *loader_str = NULL;
+       UINTN loader_len = 0;
+       unsigned int i;
+
+       second_stage = DEFAULT_LOADER;
+       load_options = NULL;
+       load_options_size = 0;
+
+       status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
+                                  &LoadedImageProtocol, (void **) &li);
+       if (status != EFI_SUCCESS) {
+               perror (L"Failed to get load options: %r\n", status);
+               return status;
+       }
+
+       /* So, load options are a giant pain in the ass.  If we're invoked
+        * from the EFI shell, we get something like this:
+
+00000000  5c 00 45 00 36 00 49 00  5c 00 66 00 65 00 64 00  |\.E.F.I.\.f.e.d.|
+00000010  6f 00 72 00 61 00 5c 00  73 00 68 00 69 00 6d 00  |o.r.a.\.s.h.i.m.|
+00000020  78 00 36 00 34 00 2e 00  64 00 66 00 69 00 20 00  |x.6.4...e.f.i. .|
+00000030  5c 00 45 00 46 00 49 00  5c 00 66 00 65 00 64 00  |\.E.F.I.\.f.e.d.|
+00000040  6f 00 72 00 61 00 5c 00  66 00 77 00 75 00 70 00  |o.r.a.\.f.w.u.p.|
+00000050  64 00 61 00 74 00 65 00  2e 00 65 00 66 00 20 00  |d.a.t.e.e.f.i. .|
+00000060  00 00 66 00 73 00 30 00  3a 00 5c 00 00 00        |..f.s.0.:.\...|
+
+       *
+       * which is just some paths rammed together separated by a UCS-2 NUL.
+       * But if we're invoked from BDS, we get something more like:
+       *
+
+00000000  01 00 00 00 62 00 4c 00  69 00 6e 00 75 00 78 00  |....b.L.i.n.u.x.|
+00000010  20 00 46 00 69 00 72 00  6d 00 77 00 61 00 72 00  | .F.i.r.m.w.a.r.|
+00000020  65 00 20 00 55 00 70 00  64 00 61 00 74 00 65 00  |e. .U.p.d.a.t.e.|
+00000030  72 00 00 00 40 01 2a 00  01 00 00 00 00 08 00 00  |r.....*.........|
+00000040  00 00 00 00 00 40 06 00  00 00 00 00 1a 9e 55 bf  |.....@........U.|
+00000050  04 57 f2 4f b4 4a ed 26  4a 40 6a 94 02 02 04 04  |.W.O.:.&J@j.....|
+00000060  34 00 5c 00 45 00 46 00  49 00 5c 00 66 00 65 00  |4.\.E.F.I.f.e.d.|
+00000070  64 00 6f 00 72 00 61 00  5c 00 73 00 68 00 69 00  |o.r.a.\.s.h.i.m.|
+00000080  6d 00 78 00 36 00 34 00  2e 00 65 00 66 00 69 00  |x.6.4...e.f.i...|
+00000090  00 00 7f ff 40 00 20 00  5c 00 66 00 77 00 75 00  |...... .\.f.w.u.|
+000000a0  70 00 78 00 36 00 34 00  2e 00 65 00 66 00 69 00  |p.x.6.4...e.f.i.|
+000000b0  00 00                                             |..|
+
+       *
+       * which is clearly an EFI_LOAD_OPTION filled in halfway reasonably.
+       * In short, the UEFI shell is still a useless piece of junk.
+       *
+       * But then on some versions of BDS, we get:
+
+00000000  5c 00 66 00 77 00 75 00  70 00 78 00 36 00 34 00  |\.f.w.u.p.x.6.4.|
+00000010  2e 00 65 00 66 00 69 00  00 00                    |..e.f.i...|
+0000001a
+
+       * which as you can see is one perfectly normal UCS2-EL string
+       * containing the load option from the Boot#### variable.
+       *
+       * We also sometimes find a guid or partial guid at the end, because
+       * BDS will add that, but we ignore that here.
+       */
+
+       /*
+        * In either case, we've got to have at least a UCS2 NUL...
+        */
+       if (li->LoadOptionsSize < 2)
+               return EFI_BAD_BUFFER_SIZE;
+
+       /*
+        * Some awesome versions of BDS will add entries for Linux.  On top
+        * of that, some versions of BDS will "tag" any Boot#### entries they
+        * create by putting a GUID at the very end of the optional data in
+        * the EFI_LOAD_OPTIONS, thus screwing things up for everybody who
+        * tries to actually *use* the optional data for anything.  Why they
+        * did this instead of adding a flag to the spec to /say/ it's
+        * created by BDS, I do not know.  For shame.
+        *
+        * Anyway, just nerf that out from the start.  It's always just
+        * garbage at the end.
+        */
+       if (li->LoadOptionsSize > 16) {
+               if (CompareGuid((EFI_GUID *)(li->LoadOptions
+                                            + (li->LoadOptionsSize - 16)),
+                               &bds_guid) == 0)
+                       li->LoadOptionsSize -= 16;
+       }
+
+       /*
+        * Check and see if this is just a list of strings.  If it's an
+        * EFI_LOAD_OPTION, it'll be 0, since we know EndEntire device path
+        * won't pass muster as UCS2-LE.
+        *
+        * If there are 3 strings, we're launched from the shell most likely,
+        * But we actually only care about the second one.
+        */
+       UINTN strings = count_ucs2_strings(li->LoadOptions,
+                                          li->LoadOptionsSize);
+       /*
+        * If it's not string data, try it as an EFI_LOAD_OPTION.
+        */
+       if (strings == 0) {
+               /*
+                * We at least didn't find /enough/ strings.  See if it works
+                * as an EFI_LOAD_OPTION.
+                */
+               status = get_load_option_optional_data(li->LoadOptions,
+                                                      li->LoadOptionsSize,
+                                                      (UINT8 **)&start,
+                                                      &loader_len);
+               if (status != EFI_SUCCESS)
+                       return EFI_SUCCESS;
+
+               remaining_size = 0;
+       } else if (strings >= 2) {
+               /*
+                * UEFI shell copies the whole line of the command into
+                * LoadOptions.  We ignore the string before the first L' ',
+                * i.e. the name of this program.
+                * Counting by two bytes is safe, because we know the size is
+                * compatible with a UCS2-LE string.
+                */
+               UINT8 *cur = li->LoadOptions;
+               for (i = 0; i < li->LoadOptionsSize - 2; i += 2) {
+                       CHAR16 c = (cur[i+1] << 8) | cur[i];
+                       if (c == L' ') {
+                               start = (CHAR16 *)&cur[i+2];
+                               remaining_size = li->LoadOptionsSize - i - 2;
+                               break;
+                       }
+               }
+
+               if (!start || remaining_size <= 0 || start[0] == L'\0')
+                       return EFI_SUCCESS;
+
+               for (i = 0; start[i] != '\0'; i++) {
+                       if (start[i] == L' ')
+                               start[i] = L'\0';
+                       if (start[i] == L'\0') {
+                               loader_len = 2 * i + 2;
+                               break;
+                       }
+               }
+               if (loader_len)
+                       remaining_size -= loader_len;
+       } else {
+               /* only find one string */
+               start = li->LoadOptions;
+               loader_len = li->LoadOptionsSize;
+       }
+
+       /*
+        * Just to be sure all that math is right...
+        */
+       if (loader_len % 2 != 0)
+               return EFI_INVALID_PARAMETER;
+
+       strings = count_ucs2_strings((UINT8 *)start, loader_len);
+       if (strings < 1)
+               return EFI_SUCCESS;
+
+       /*
+        * Set up the name of the alternative loader and the LoadOptions for
+        * the loader
+        */
+       if (loader_len > 0) {
+               loader_str = AllocatePool(loader_len);
+               if (!loader_str) {
+                       perror(L"Failed to allocate loader string\n");
+                       return EFI_OUT_OF_RESOURCES;
+               }
+
+               for (i = 0; i < loader_len / 2; i++)
+                       loader_str[i] = start[i];
+               loader_str[loader_len/2-1] = L'\0';
+
+               second_stage = loader_str;
+               load_options = remaining_size ? start + loader_len : NULL;
+               load_options_size = remaining_size;
+       }
+
+       return EFI_SUCCESS;
+}
+
+static SHIM_LOCK shim_lock_interface;
+static EFI_HANDLE shim_lock_handle;
+
+EFI_STATUS
+install_shim_protocols(void)
+{
+       EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+       EFI_STATUS efi_status;
+
+       if (!secure_mode())
+               return EFI_SUCCESS;
+
+       /*
+        * Install the protocol
+        */
+       efi_status = uefi_call_wrapper(BS->InstallProtocolInterface, 4,
+                         &shim_lock_handle, &shim_lock_guid,
+                         EFI_NATIVE_INTERFACE, &shim_lock_interface);
+       if (EFI_ERROR(efi_status)) {
+               console_error(L"Could not install security protocol",
+                             efi_status);
+               return efi_status;
+       }
+
+#if defined(OVERRIDE_SECURITY_POLICY)
+       /*
+        * Install the security protocol hook
+        */
+       security_policy_install(shim_verify);
+#endif
+
+       return EFI_SUCCESS;
+}
+
+void
+uninstall_shim_protocols(void)
+{
+       EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+
+       if (!secure_mode())
+               return;
+
+#if defined(OVERRIDE_SECURITY_POLICY)
+       /*
+        * Clean up the security protocol hook
+        */
+       security_policy_uninstall();
+#endif
+
+       /*
+        * If we're back here then clean everything up before exiting
+        */
+       uefi_call_wrapper(BS->UninstallProtocolInterface, 3, shim_lock_handle,
+                         &shim_lock_guid, &shim_lock_interface);
+}
+
+EFI_STATUS
+shim_init(void)
+{
+       EFI_STATUS status = EFI_SUCCESS;
+       setup_console(1);
+       setup_verbosity();
+       dprinta(shim_version);
+
+       /* Set the second stage loader */
+       set_second_stage (image_handle);
+
+       if (secure_mode()) {
+               if (vendor_cert_size || vendor_dbx_size) {
+                       /*
+                        * If shim includes its own certificates then ensure
+                        * that anything it boots has performed some
+                        * validation of the next image.
+                        */
+                       hook_system_services(systab);
+                       loader_is_participating = 0;
+               }
+
+               hook_exit(systab);
+
+               status = install_shim_protocols();
+       }
+       return status;
+}
+
+void
+shim_fini(void)
+{
+       if (secure_mode()) {
+               /*
+                * Remove our protocols
+                */
+               uninstall_shim_protocols();
+
+               /*
+                * Remove our hooks from system services.
+                */
+               unhook_system_services();
+               unhook_exit();
+       }
+
+       /*
+        * Free the space allocated for the alternative 2nd stage loader
+        */
+       if (load_options_size > 0 && second_stage)
+               FreePool(second_stage);
+
+       setup_console(0);
+}
+
+extern EFI_STATUS
+efi_main(EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab);
+
+static void
+__attribute__((__optimize__("0")))
+debug_hook(void)
+{
+       EFI_GUID guid = SHIM_LOCK_GUID;
+       UINT8 *data = NULL;
+       UINTN dataSize = 0;
+       EFI_STATUS efi_status;
+       volatile register UINTN x = 0;
+       extern char _text, _data;
+
+       if (x)
+               return;
+
+       efi_status = get_variable(L"SHIM_DEBUG", &data, &dataSize, guid);
+       if (EFI_ERROR(efi_status)) {
+               return;
+       }
+
+       Print(L"add-symbol-file "DEBUGDIR
+             L"shim.debug 0x%08x -s .data 0x%08x\n", &_text,
+             &_data);
+
+       Print(L"Pausing for debugger attachment.\n");
+       Print(L"To disable this, remove the EFI variable SHIM_DEBUG-%g .\n",
+             &guid);
+       x = 1;
+       while (x++) {
+               /* Make this so it can't /totally/ DoS us. */
+#if defined(__x86_64__) || defined(__i386__) || defined(__i686__)
+               if (x > 4294967294ULL)
+                       break;
+               __asm__ __volatile__("pause");
+#elif defined(__aarch64__)
+               if (x > 1000)
+                       break;
+               __asm__ __volatile__("wfi");
+#else
+               if (x > 12000)
+                       break;
+               uefi_call_wrapper(BS->Stall, 1, 5000);
+#endif
+       }
+       x = 1;
+}
+
+EFI_STATUS
+efi_main (EFI_HANDLE passed_image_handle, EFI_SYSTEM_TABLE *passed_systab)
+{
+       EFI_STATUS efi_status;
+
+       verification_method = VERIFIED_BY_NOTHING;
+
+       vendor_cert_size = cert_table.vendor_cert_size;
+       vendor_dbx_size = cert_table.vendor_dbx_size;
+       vendor_cert = (UINT8 *)&cert_table + cert_table.vendor_cert_offset;
+       vendor_dbx = (UINT8 *)&cert_table + cert_table.vendor_dbx_offset;
+
+       /*
+        * Set up the shim lock protocol so that grub and MokManager can
+        * call back in and use shim functions
+        */
+       shim_lock_interface.Verify = shim_verify;
+       shim_lock_interface.Hash = shim_hash;
+       shim_lock_interface.Context = shim_read_header;
+
+       systab = passed_systab;
+       image_handle = passed_image_handle;
+
+       /*
+        * Ensure that gnu-efi functions are available
+        */
+       InitializeLib(image_handle, systab);
+
+       /*
+        * if SHIM_DEBUG is set, wait for a debugger to attach.
+        */
+       debug_hook();
+
+       /*
+        * Measure the MOK variables
+        */
+       efi_status = measure_mok();
+       if (efi_status != EFI_SUCCESS && efi_status != EFI_NOT_FOUND) {
+               Print(L"Something has gone seriously wrong: %r\n", efi_status);
+               Print(L"Shim was unable to measure state into the TPM\n");
+               systab->BootServices->Stall(5000000);
+               systab->RuntimeServices->ResetSystem(EfiResetShutdown,
+                                                    EFI_SECURITY_VIOLATION,
+                                                    0, NULL);
+       }
+
+       /*
+        * Check whether the user has configured the system to run in
+        * insecure mode
+        */
+       check_mok_sb();
+
+       efi_status = shim_init();
+       if (EFI_ERROR(efi_status)) {
+               Print(L"Something has gone seriously wrong: %r\n", efi_status);
+               Print(L"shim cannot continue, sorry.\n");
+               uefi_call_wrapper(BS->Stall, 1, 5000000);
+               uefi_call_wrapper(systab->RuntimeServices->ResetSystem, 4,
+                                 EfiResetShutdown, EFI_SECURITY_VIOLATION,
+                                 0, NULL);
+       }
+
+       /*
+        * Tell the user that we're in insecure mode if necessary
+        */
+       if (user_insecure_mode) {
+               Print(L"Booting in insecure mode\n");
+               uefi_call_wrapper(BS->Stall, 1, 2000000);
+       }
+
+       /*
+        * Enter MokManager if necessary
+        */
+       efi_status = check_mok_request(image_handle);
+
+       /*
+        * Copy the MOK list to a runtime variable so the kernel can
+        * make use of it
+        */
+       efi_status = mirror_mok_list();
+
+       efi_status = mirror_mok_list_x();
+
+       /*
+        * Copy the MOK SB State to a runtime variable so the kernel can
+        * make use of it
+        */
+       efi_status = mirror_mok_sb_state();
+
+       /*
+        * Create the runtime MokIgnoreDB variable so the kernel can
+        * make use of it
+        */
+       efi_status = mok_ignore_db();
+
+       /*
+        * Hand over control to the second stage bootloader
+        */
+       efi_status = init_grub(image_handle);
+
+       shim_fini();
+       return efi_status;
+}
index 740ad53eed838224241187a53c49f1052fdba686..bcf0c0bf565c661477cc33aa0183650dbbc7ceeb 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,7 @@ LD              = $(CROSS_COMPILE)ld
 OBJCOPY                = $(CROSS_COMPILE)objcopy
 
 ARCH           = $(shell $(CC) -dumpmachine | cut -f1 -d- | sed s,i[3456789]86,ia32,)
-OBJCOPY_GTE224  = $(shell expr `$(OBJCOPY) --version |grep ^"GNU objcopy" | sed 's/^.version //g' | cut -f1-2 -d.` \>= 2.24)
+OBJCOPY_GTE224  = $(shell expr `$(OBJCOPY) --version |grep ^"GNU objcopy" | sed 's/^.* //g' | cut -f1-2 -d.` \>= 2.24)
 
 SUBDIRS                = Cryptlib lib
 
index e90f3a336a31bbb18ea117da9943991a21334b38..554d602c66b1aa1bd840427975415c544e35d3c3 100644 (file)
@@ -10,6 +10,9 @@ shim (0.9+1465500757.14a5905-0ubuntu1) UNRELEASED; urgency=medium
     - Remaining patches:
       + second-stage-path
       + sbsigntool-not-pesign 
+  * debian/patches/unused-variable: remove unused variable size.
+  * debian/patches/binutils-version-matching: revert d9a4c912 to correctly
+    match objcopy's version on Ubuntu.
 
  -- Mathieu Trudel-Lapierre <cyphermox@ubuntu.com>  Tue, 26 Jul 2016 12:02:21 -0400
 
diff --git a/debian/patches/binutils-version-matching b/debian/patches/binutils-version-matching
new file mode 100644 (file)
index 0000000..0e3bba8
--- /dev/null
@@ -0,0 +1,26 @@
+From: Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
+Subject: Revert d9a4c912 to fix matching binutils version on Ubuntu.
+
+That commit breaks on Ubuntu as we don't just have "version xyz", but rather
+just the version number at the end of the version string, which looks like
+this:
+
+  "GNU objcopy (GNU Binutils for Ubuntu) 2.26"
+
+---
+ Makefile |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+Index: b/Makefile
+===================================================================
+--- a/Makefile
++++ b/Makefile
+@@ -9,7 +9,7 @@ LD             = $(CROSS_COMPILE)ld
+ OBJCOPY               = $(CROSS_COMPILE)objcopy
+ ARCH          = $(shell $(CC) -dumpmachine | cut -f1 -d- | sed s,i[3456789]86,ia32,)
+-OBJCOPY_GTE224  = $(shell expr `$(OBJCOPY) --version |grep ^"GNU objcopy" | sed 's/^.version //g' | cut -f1-2 -d.` \>= 2.24)
++OBJCOPY_GTE224  = $(shell expr `$(OBJCOPY) --version |grep ^"GNU objcopy" | sed 's/^.* //g' | cut -f1-2 -d.` \>= 2.24)
+ SUBDIRS               = Cryptlib lib
index a5f3392d0248b2586ae66c864f83fad3abad8453..8613be11f8a21559ab9ce2488a423f0e6803414a 100644 (file)
@@ -1,2 +1,4 @@
 second-stage-path
 sbsigntool-not-pesign
+unused-variable
+binutils-version-matching
diff --git a/debian/patches/unused-variable b/debian/patches/unused-variable
new file mode 100644 (file)
index 0000000..b6435ce
--- /dev/null
@@ -0,0 +1,19 @@
+From: Mathieu Trudel-Lapierre <mathieu.trudel-lapierre@canonical.com>
+Subject: Remove unused variable; pointed out by Werror=unused-variable
+
+---
+ shim.c |    1 -
+ 1 file changed, 1 deletion(-)
+
+Index: b/shim.c
+===================================================================
+--- a/shim.c
++++ b/shim.c
+@@ -1118,7 +1118,6 @@ static EFI_STATUS handle_image (void *da
+       EFI_STATUS efi_status;
+       char *buffer;
+       int i;
+-      unsigned int size;
+       EFI_IMAGE_SECTION_HEADER *Section;
+       char *base, *end;
+       PE_COFF_LOADER_IMAGE_CONTEXT context;
diff --git a/shim.c b/shim.c
index ed01899c0ed0d3696735322b53eed29f802a9845..f5bed2dcc1a9d320a0ddd6ce9b9c641dd64cff03 100644 (file)
--- a/shim.c
+++ b/shim.c
@@ -1118,7 +1118,6 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
        EFI_STATUS efi_status;
        char *buffer;
        int i;
-       unsigned int size;
        EFI_IMAGE_SECTION_HEADER *Section;
        char *base, *end;
        PE_COFF_LOADER_IMAGE_CONTEXT context;