From 110c669fd654b4a87f758a82b8bf468596b3ad19 Mon Sep 17 00:00:00 2001 From: Mathieu Trudel-Lapierre Date: Tue, 26 Jul 2016 12:20:13 -0400 Subject: [PATCH 1/6] * Refreshed patches. - Remaining patches: + second-stage-path + sbsigntool-not-pesign --- debian/changelog | 16 ++++++++-------- debian/patches/sbsigntool-not-pesign | 4 ++-- debian/patches/second-stage-path | 4 ++-- debian/patches/series | 3 --- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/debian/changelog b/debian/changelog index 45d0411..ace6789 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,14 +1,14 @@ shim (0.9+1465500757.14a5905-0ubuntu1) UNRELEASED; urgency=medium * New upstream release. - - [ Matthias Klose ] - * Fix build with GCC 5, forcing -std=gnu89 to not rely on stdint.h - required by efibind.h, and not found with -nostdinc. (LP: #1429978) - - [ Mathieu Trudel-Lapierre ] - * More GCC 5 fixes: stdarg.h and other include tweaks, cherry-pick from - d51739a4. + - Better handle LoadOptions. + - Measure state and second stage in TPM. + - Mirror MokSBState in runtime as MokSBStateRT. + - Various bug fixes and other improvements. + * Refreshed patches. + - Remaining patches: + + second-stage-path + + sbsigntool-not-pesign -- Mathieu Trudel-Lapierre Tue, 26 Jul 2016 12:02:21 -0400 diff --git a/debian/patches/sbsigntool-not-pesign b/debian/patches/sbsigntool-not-pesign index c91a647..9629cb1 100644 --- a/debian/patches/sbsigntool-not-pesign +++ b/debian/patches/sbsigntool-not-pesign @@ -13,8 +13,8 @@ Index: b/Makefile =================================================================== --- a/Makefile +++ b/Makefile -@@ -144,8 +144,8 @@ FORMAT ?= --target efi-app-$(ARCH) - -j .debug_line -j .debug_str -j .debug_ranges \ +@@ -158,8 +158,8 @@ endif + -j .note.gnu.build-id \ $(FORMAT) $^ $@.debug -%.efi.signed: %.efi certdb/secmod.db diff --git a/debian/patches/second-stage-path b/debian/patches/second-stage-path index fef139c..da53af8 100644 --- a/debian/patches/second-stage-path +++ b/debian/patches/second-stage-path @@ -13,7 +13,7 @@ Index: b/Makefile =================================================================== --- a/Makefile +++ b/Makefile -@@ -18,7 +18,7 @@ EFI_LIBS = -lefi -lgnuefi --start-group +@@ -25,7 +25,7 @@ EFI_LIBS = -lefi -lgnuefi --start-group EFI_CRT_OBJS = $(EFI_PATH)/crt0-efi-$(ARCH).o EFI_LDS = elf_$(ARCH)_efi.lds @@ -21,4 +21,4 @@ Index: b/Makefile +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 \ + -Werror=sign-compare -ffreestanding -std=gnu89 \ diff --git a/debian/patches/series b/debian/patches/series index 93f1acf..a5f3392 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,5 +1,2 @@ -prototypes second-stage-path sbsigntool-not-pesign -gcc-5.diff -gcc5-includes-stdarg.patch From beb46239384359181d3c73a4bb9c0386a1fb4b46 Mon Sep 17 00:00:00 2001 From: Mathieu Trudel-Lapierre Date: Tue, 26 Jul 2016 13:31:49 -0400 Subject: [PATCH 2/6] Apply patches again --- .pc/applied-patches | 2 + .pc/sbsigntool-not-pesign/.timestamp | 0 .pc/sbsigntool-not-pesign/Makefile | 197 +++++++++++++++++++++++++++ .pc/second-stage-path/.timestamp | 0 .pc/second-stage-path/Makefile | 197 +++++++++++++++++++++++++++ Makefile | 6 +- 6 files changed, 399 insertions(+), 3 deletions(-) create mode 100644 .pc/applied-patches create mode 100644 .pc/sbsigntool-not-pesign/.timestamp create mode 100644 .pc/sbsigntool-not-pesign/Makefile create mode 100644 .pc/second-stage-path/.timestamp create mode 100644 .pc/second-stage-path/Makefile diff --git a/.pc/applied-patches b/.pc/applied-patches new file mode 100644 index 0000000..a5f3392 --- /dev/null +++ b/.pc/applied-patches @@ -0,0 +1,2 @@ +second-stage-path +sbsigntool-not-pesign diff --git a/.pc/sbsigntool-not-pesign/.timestamp b/.pc/sbsigntool-not-pesign/.timestamp new file mode 100644 index 0000000..e69de29 diff --git a/.pc/sbsigntool-not-pesign/Makefile b/.pc/sbsigntool-not-pesign/Makefile new file mode 100644 index 0000000..2c760ef --- /dev/null +++ b/.pc/sbsigntool-not-pesign/Makefile @@ -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 $@ + 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 certdb/secmod.db + pesign -n certdb -i $< -c "shim" -s -o $@ -f + +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/second-stage-path/.timestamp b/.pc/second-stage-path/.timestamp new file mode 100644 index 0000000..e69de29 diff --git a/.pc/second-stage-path/Makefile b/.pc/second-stage-path/Makefile new file mode 100644 index 0000000..640ecf2 --- /dev/null +++ b/.pc/second-stage-path/Makefile @@ -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 := \\\\grub.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 $@ + 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 certdb/secmod.db + pesign -n certdb -i $< -c "shim" -s -o $@ -f + +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/Makefile b/Makefile index 640ecf2..740ad53 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ EFI_LIBS = -lefi -lgnuefi --start-group Cryptlib/libcryptlib.a Cryptlib/OpenSSL/ EFI_CRT_OBJS = $(EFI_PATH)/crt0-efi-$(ARCH).o EFI_LDS = elf_$(ARCH)_efi.lds -DEFAULT_LOADER := \\\\grub.efi +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 \ @@ -158,8 +158,8 @@ endif -j .note.gnu.build-id \ $(FORMAT) $^ $@.debug -%.efi.signed: %.efi certdb/secmod.db - pesign -n certdb -i $< -c "shim" -s -o $@ -f +%.efi.signed: %.efi shim.crt + sbsign --key shim.key --cert shim.crt $< clean: $(MAKE) -C Cryptlib clean From d191cf2c9e3da08479d2c7be84d8305991aa1b16 Mon Sep 17 00:00:00 2001 From: Mathieu Trudel-Lapierre Date: Tue, 26 Jul 2016 13:48:37 -0400 Subject: [PATCH 3/6] Update bug tags for closed bugs upstream. --- debian/changelog | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index ace6789..e90f3a3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,9 +1,10 @@ shim (0.9+1465500757.14a5905-0ubuntu1) UNRELEASED; urgency=medium * New upstream release. - - Better handle LoadOptions. + - Better handle LoadOptions. (LP: #1581299) - Measure state and second stage in TPM. - Mirror MokSBState in runtime as MokSBStateRT. + - Fix failure to build with GCC 5. (LP: #1429978) - Various bug fixes and other improvements. * Refreshed patches. - Remaining patches: From 7fbc200d477efc39342f465c9308e0bd9194262b Mon Sep 17 00:00:00 2001 From: Mathieu Trudel-Lapierre Date: Tue, 26 Jul 2016 14:37:29 -0400 Subject: [PATCH 4/6] * debian/patches/unused-variable: remove unused variable size. * debian/patches/binutils-version-matching: revert d9a4c912 to correctly match objcopy's version on Ubuntu. --- .pc/applied-patches | 2 + .pc/binutils-version-matching/.timestamp | 0 .pc/binutils-version-matching/Makefile | 197 ++ .pc/unused-variable/.timestamp | 0 .pc/unused-variable/shim.c | 2658 ++++++++++++++++++++++ Makefile | 2 +- debian/changelog | 3 + debian/patches/binutils-version-matching | 26 + debian/patches/series | 2 + debian/patches/unused-variable | 19 + shim.c | 1 - 11 files changed, 2908 insertions(+), 2 deletions(-) create mode 100644 .pc/binutils-version-matching/.timestamp create mode 100644 .pc/binutils-version-matching/Makefile create mode 100644 .pc/unused-variable/.timestamp create mode 100644 .pc/unused-variable/shim.c create mode 100644 debian/patches/binutils-version-matching create mode 100644 debian/patches/unused-variable diff --git a/.pc/applied-patches b/.pc/applied-patches index a5f3392..8613be1 100644 --- a/.pc/applied-patches +++ b/.pc/applied-patches @@ -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 index 0000000..e69de29 diff --git a/.pc/binutils-version-matching/Makefile b/.pc/binutils-version-matching/Makefile new file mode 100644 index 0000000..740ad53 --- /dev/null +++ b/.pc/binutils-version-matching/Makefile @@ -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 $@ + 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 index 0000000..e69de29 diff --git a/.pc/unused-variable/shim.c b/.pc/unused-variable/shim.c new file mode 100644 index 0000000..ed01899 --- /dev/null +++ b/.pc/unused-variable/shim.c @@ -0,0 +1,2658 @@ +/* + * shim - trivial UEFI first-stage bootloader + * + * Copyright 2012 Red Hat, Inc + * + * 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 +#include +#include +#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; +} diff --git a/Makefile b/Makefile index 740ad53..bcf0c0b 100644 --- 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 diff --git a/debian/changelog b/debian/changelog index e90f3a3..554d602 100644 --- a/debian/changelog +++ b/debian/changelog @@ -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 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 index 0000000..0e3bba8 --- /dev/null +++ b/debian/patches/binutils-version-matching @@ -0,0 +1,26 @@ +From: Mathieu Trudel-Lapierre +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 + diff --git a/debian/patches/series b/debian/patches/series index a5f3392..8613be1 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -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 index 0000000..b6435ce --- /dev/null +++ b/debian/patches/unused-variable @@ -0,0 +1,19 @@ +From: Mathieu Trudel-Lapierre +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 ed01899..f5bed2d 100644 --- 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; From 9fa1d28f0dd750bf1b47479a25b6afb4de7a256c Mon Sep 17 00:00:00 2001 From: Mathieu Trudel-Lapierre Date: Tue, 26 Jul 2016 16:48:29 -0400 Subject: [PATCH 5/6] debian/copyright: update copyright for patches. --- debian/changelog | 1 + debian/copyright | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/debian/changelog b/debian/changelog index 554d602..0548697 100644 --- a/debian/changelog +++ b/debian/changelog @@ -13,6 +13,7 @@ shim (0.9+1465500757.14a5905-0ubuntu1) UNRELEASED; urgency=medium * debian/patches/unused-variable: remove unused variable size. * debian/patches/binutils-version-matching: revert d9a4c912 to correctly match objcopy's version on Ubuntu. + * debian/copyright: update copyright for patches. -- Mathieu Trudel-Lapierre Tue, 26 Jul 2016 12:02:21 -0400 diff --git a/debian/copyright b/debian/copyright index d9f1275..b25a56d 100644 --- a/debian/copyright +++ b/debian/copyright @@ -3,6 +3,24 @@ Upstream-Name: shim Upstream-Contact: Matthew Garrett Source: https://github.com/mjg59/shim.git +Files: debian/patches/* +Copyright: 2016 Canonical Ltd. +License: GPL-2 + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU General + Public License can be found in `/usr/share/common-licenses/GPL-2'. + Files: * Copyright: 2012 Red Hat, Inc 2009-2012 Intel Corporation From cffaa5078ddcc387c0b6996e3195bf7dae8fadaa Mon Sep 17 00:00:00 2001 From: Mathieu Trudel-Lapierre Date: Tue, 26 Jul 2016 16:49:50 -0400 Subject: [PATCH 6/6] releasing package shim version 0.9+1465500757.14a5905-0ubuntu1 --- debian/changelog | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 0548697..6a2a198 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -shim (0.9+1465500757.14a5905-0ubuntu1) UNRELEASED; urgency=medium +shim (0.9+1465500757.14a5905-0ubuntu1) yakkety; urgency=medium * New upstream release. - Better handle LoadOptions. (LP: #1581299) @@ -15,7 +15,7 @@ shim (0.9+1465500757.14a5905-0ubuntu1) UNRELEASED; urgency=medium match objcopy's version on Ubuntu. * debian/copyright: update copyright for patches. - -- Mathieu Trudel-Lapierre Tue, 26 Jul 2016 12:02:21 -0400 + -- Mathieu Trudel-Lapierre Tue, 26 Jul 2016 16:48:32 -0400 shim (0.8-0ubuntu2) wily; urgency=medium