summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--BUILDING61
-rw-r--r--Cryptlib/Makefile2
-rw-r--r--Cryptlib/OpenSSL/Makefile2
-rw-r--r--Makefile235
-rw-r--r--MokVars.txt5
-rw-r--r--README7
-rw-r--r--README.tpm22
-rw-r--r--TODO37
-rw-r--r--buildid.c192
-rw-r--r--commit2
-rw-r--r--fallback.c125
-rw-r--r--shim.c122
-rw-r--r--tpm.c250
-rw-r--r--tpm.h32
15 files changed, 949 insertions, 146 deletions
diff --git a/.gitignore b/.gitignore
index 586bc246..d0d91293 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,7 @@
certdb
shim_cert.h
*.a
+*.CSV
*.cer
*.crl
*.crt
diff --git a/BUILDING b/BUILDING
new file mode 100644
index 00000000..461b85c4
--- /dev/null
+++ b/BUILDING
@@ -0,0 +1,61 @@
+It's pretty straightforward:
+
+cp $MY_DER_ENCODED_CERT pub.cer
+make VENDOR_CERT_FILE=pub.cer
+make EFIDIR=my_esp_dir_name install
+
+There are a couple of ways to customize the build:
+
+Install targets:
+- install
+ installs shim as if to a hard drive, including installing MokManager and
+ fallback appropriately.
+- install-as-data
+ installs shim files to /usr/share/shim/$(EFI_ARCH)-$(VERSION)/
+
+Variables you should set to customize the build:
+- EFIDIR
+ This is the name of the ESP directory. The install targets won't work
+ without it.
+- DESTDIR
+ This will be prepended to any install targets, so you don't have to
+ install to a live root directory.
+- DEFAULT_LOADER
+ defaults to \\\\grub$(EFI_ARCH).efi , but you could set it to whatever.
+ Be careful with the leading backslashes, they can be hard to get
+ correct.
+
+Variables you could set to customize the build:
+- ENABLE_SHIM_CERT
+ if this variable is defined one the make command line, shim will
+ generate keys during the build and sign MokManager and fallback with
+ them, and the signed version will be what gets installed with the
+ install targets
+- ENABLE_HTTPBOOT
+ build support for http booting
+- ARCH
+ This allows you to do a build for a different arch that we support. For
+ instance, on x86_64 you could do "setarch linux32 make ARCH=ia32" to get
+ the ia32 build instead. (DEFAULT_LOADER will be automatically adjusted
+ in that case.)
+- TOPDIR
+ You can use this along with make -f to build in a subdir. For instance,
+ on an x86_64 machine you could do:
+
+ mkdir build-ia32 build-x64 inst
+ cd build-ia32
+ setarch linux32 make TOPDIR=.. ARCH=ia32 -f ../Makefile
+ setarch linux32 make TOPDIR=.. ARCH=ia32 \
+ DESTDIR=../inst EFIDIR=debian \
+ -f ../Makefile install
+ cd ../build-x64
+ make TOPDIR=.. -f ../Makefile
+ make TOPDIR=.. DESTDIR=../inst EFIDIR=debian \
+ -f ../Makefile install
+
+ That would get you x86_64 and ia32 builds in the "inst" subdir.
+- OSLABEL
+ This is the label that will be put in BOOT$(EFI_ARCH).CSV for your OS.
+ By default this is the same value as EFIDIR .
+
+# vim:filetype=mail:tw=74
diff --git a/Cryptlib/Makefile b/Cryptlib/Makefile
index a5e02d4d..a025ac53 100644
--- a/Cryptlib/Makefile
+++ b/Cryptlib/Makefile
@@ -8,7 +8,7 @@ CFLAGS = -ggdb -O0 -I$(TOPDIR) -iquote $(TOPDIR) -fno-stack-protector -fno-stri
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 \
- -DMDE_CPU_IA64
+ -DMDE_CPU_X64
endif
ifeq ($(ARCH),ia32)
CFLAGS += -mno-mmx -mno-sse -mno-red-zone -nostdinc -maccumulate-outgoing-args -m32 \
diff --git a/Cryptlib/OpenSSL/Makefile b/Cryptlib/OpenSSL/Makefile
index 4319afde..4c683bf5 100644
--- a/Cryptlib/OpenSSL/Makefile
+++ b/Cryptlib/OpenSSL/Makefile
@@ -11,7 +11,7 @@ CFLAGS = -ggdb -O0 -I$(TOPDIR) -I$(TOPDIR)/.. -I$(TOPDIR)/../Include/ -I$(TOPDI
ifeq ($(ARCH),x86_64)
CFLAGS += -mno-mmx -mno-sse -mno-red-zone -maccumulate-outgoing-args \
-DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI \
- -UNO_BUILTIN_VA_FUNCS -DMDE_CPU_IA64
+ -UNO_BUILTIN_VA_FUNCS -DMDE_CPU_X64
endif
ifeq ($(ARCH),ia32)
CFLAGS += -mno-mmx -mno-sse -mno-red-zone -maccumulate-outgoing-args \
diff --git a/Makefile b/Makefile
index fb5ab276..afd65045 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,8 @@
VERSION = 12
-RELEASE :=
-ifneq ($(RELEASE),"")
- RELEASE:="-$(RELEASE)"
+ifneq ($(origin RELEASE),undefined)
+DASHRELEASE ?= -$(RELEASE)
+else
+DASHRELEASE ?=
endif
ifeq ($(MAKELEVEL),0)
@@ -10,18 +11,31 @@ endif
override TOPDIR := $(abspath $(TOPDIR))
VPATH = $(TOPDIR)
-
CC = $(CROSS_COMPILE)gcc
LD = $(CROSS_COMPILE)ld
OBJCOPY = $(CROSS_COMPILE)objcopy
OPENSSL ?= openssl
HEXDUMP ?= hexdump
+INSTALL ?= install
PK12UTIL ?= pk12util
CERTUTIL ?= certutil
PESIGN ?= pesign
+SBSIGN ?= sbsign
+prefix ?= /usr
+prefix := $(abspath $(prefix))
+datadir ?= $(prefix)/share/
+PKGNAME ?= shim
+ESPROOTDIR ?= boot/efi/
+EFIBOOTDIR ?= $(ESPROOTDIR)EFI/BOOT/
+TARGETDIR ?= $(ESPROOTDIR)EFI/$(EFIDIR)/
+DATATARGETDIR ?= $(datadir)/$(PKGNAME)/$(VERSION)$(DASHRELEASE)/$(ARCH_SUFFIX)/
+DEBUGINFO ?= $(prefix)/lib/debug/
+DEBUGSOURCE ?= $(prefix)/src/debug/
+OSLABEL ?= $(EFIDIR)
+DEFAULT_LOADER ?= \\\\grub$(ARCH_SUFFIX).efi
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/^.*\((.*)\|version\) //g' | cut -f1-2 -d.` \>= 2.24)
SUBDIRS = $(TOPDIR)/Cryptlib $(TOPDIR)/lib
@@ -36,7 +50,6 @@ EFI_LIBS = -lefi -lgnuefi --start-group Cryptlib/libcryptlib.a Cryptlib/OpenSSL/
EFI_CRT_OBJS = $(EFI_PATH)/crt0-efi-$(ARCH).o
EFI_LDS = $(TOPDIR)/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 \
@@ -44,9 +57,6 @@ CFLAGS = -ggdb -O0 -fno-stack-protector -fno-strict-aliasing -fpic \
"-DDEFAULT_LOADER=L\"$(DEFAULT_LOADER)\"" \
"-DDEFAULT_LOADER_CHAR=\"$(DEFAULT_LOADER)\"" \
$(EFI_INCLUDES)
-SHIMNAME = shim
-MMNAME = MokManager
-FBNAME = fallback
COMMITID ?= $(shell if [ -d .git ] ; then git log -1 --pretty=format:%H ; elif [ -f commit ]; then cat commit ; else echo commit id not available; fi)
@@ -60,39 +70,58 @@ 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 \
- -DMDE_CPU_X64 "-DEFI_ARCH=L\"x64\"" -DPAGE_SIZE=4096 \
- "-DDEBUGDIR=L\"/usr/lib/debug/usr/share/shim/x64-$(VERSION)$(RELEASE)/\""
- MMNAME = mmx64
- FBNAME = fbx64
- SHIMNAME= shimx64
- EFI_PATH:=/usr/lib64/gnuefi
- LIB_PATH:=/usr/lib64
-
+ -maccumulate-outgoing-args \
+ -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI \
+ -DNO_BUILTIN_VA_FUNCS -DMDE_CPU_X64 -DPAGE_SIZE=4096
+ LIBDIR ?= $(prefix)/lib64
+ ARCH_SUFFIX ?= x64
+ ARCH_SUFFIX_UPPER ?= X64
endif
ifeq ($(ARCH),ia32)
CFLAGS += -mno-mmx -mno-sse -mno-red-zone -nostdinc \
- -maccumulate-outgoing-args -m32 \
- -DMDE_CPU_IA32 "-DEFI_ARCH=L\"ia32\"" -DPAGE_SIZE=4096 \
- "-DDEBUGDIR=L\"/usr/lib/debug/usr/share/shim/ia32-$(VERSION)$(RELEASE)/\""
- MMNAME = mmia32
- FBNAME = fbia32
- SHIMNAME= shimia32
- EFI_PATH:=/usr/lib/gnuefi
- LIB_PATH:=/usr/lib
+ -maccumulate-outgoing-args -m32 \
+ -DMDE_CPU_IA32 -DPAGE_SIZE=4096
+ LIBDIR ?= $(prefix)/lib
+ ARCH_SUFFIX ?= ia32
+ ARCH_SUFFIX_UPPER ?= IA32
endif
ifeq ($(ARCH),aarch64)
- CFLAGS += -DMDE_CPU_AARCH64 "-DEFI_ARCH=L\"aa64\"" -DPAGE_SIZE=4096 \
- "-DDEBUGDIR=L\"/usr/lib/debug/usr/share/shim/aa64-$(VERSION)$(RELEASE)/\""
- MMNAME = mmaa64
- FBNAME = fbaa64
- SHIMNAME= shimaa64
- EFI_PATH:=/usr/lib64/gnuefi
- LIB_PATH:=/usr/lib64
+ CFLAGS += -DMDE_CPU_AARCH64 -DPAGE_SIZE=4096 -mstrict-align
+ LIBDIR ?= $(prefix)/lib64
+ ARCH_SUFFIX ?= aa64
+ ARCH_SUFFIX_UPPER ?= AA64
+ FORMAT := -O binary
+ SUBSYSTEM := 0xa
+ LDFLAGS += --defsym=EFI_SUBSYSTEM=$(SUBSYSTEM)
+endif
+ifeq ($(ARCH),arm)
+ CFLAGS += -DMDE_CPU_ARM -DPAGE_SIZE=4096 -mstrict-align
+ LIBDIR ?= $(prefix)/lib
+ ARCH_SUFFIX ?= arm
+ ARCH_SUFFIX_UPPER ?= ARM
+ FORMAT := -O binary
+ SUBSYSTEM := 0xa
+ LDFLAGS += --defsym=EFI_SUBSYSTEM=$(SUBSYSTEM)
endif
+FORMAT ?= --target efi-app-$(ARCH)
+EFI_PATH ?= $(LIBDIR)/gnuefi
+
+MMSTEM ?= mm$(ARCH_SUFFIX)
+MMNAME = $(MMSTEM).efi
+MMSONAME = $(MMSTEM).so
+FBSTEM ?= fb$(ARCH_SUFFIX)
+FBNAME = $(FBSTEM).efi
+FBSONAME = $(FBSTEM).so
+SHIMSTEM ?= shim$(ARCH_SUFFIX)
+SHIMNAME = $(SHIMSTEM).efi
+SHIMSONAME = $(SHIMSTEM).so
+SHIMHASHNAME = $(SHIMSTEM).hash
+BOOTEFINAME ?= BOOT$(ARCH_SUFFIX_UPPER).EFI
+BOOTCSVNAME ?= BOOT$(ARCH_SUFFIX_UPPER).CSV
+
+CFLAGS += "-DEFI_ARCH=L\"$(ARCH_SUFFIX)\"" "-DDEBUGDIR=L\"/usr/lib/debug/usr/share/shim/$(ARCH_SUFFIX)-$(VERSION)$(DASHRELEASE)/\""
+
ifneq ($(origin VENDOR_CERT_FILE), undefined)
CFLAGS += -DVENDOR_CERT_FILE=\"$(VENDOR_CERT_FILE)\"
endif
@@ -100,15 +129,25 @@ 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
+LDFLAGS = --hash-style=sysv -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L$(EFI_PATH) -L$(LIBDIR) -LCryptlib -LCryptlib/OpenSSL $(EFI_CRT_OBJS) --build-id=sha1
-TARGET = $(SHIMNAME).efi $(MMNAME).efi.signed $(FBNAME).efi.signed
+TARGETS = $(SHIMNAME)
+TARGETS += $(SHIMNAME).debug $(MMNAME).debug $(FBNAME).debug
+ifneq ($(origin ENABLE_SHIM_HASH),undefined)
+TARGETS += $(SHIMHASHNAME)
+endif
+ifneq ($(origin ENABLE_SHIM_CERT),undefined)
+TARGETS += $(MMNAME).signed $(FBNAME).signed
+CFLAGS += -DENABLE_SHIM_CERT
+else
+TARGETS += $(MMNAME) $(FBNAME)
+endif
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
ORIG_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.h
MOK_OBJS = MokManager.o PasswordCrypt.o crypt_blowfish.o
ORIG_MOK_SOURCES = MokManager.c shim.h include/console.h PasswordCrypt.c PasswordCrypt.h crypt_blowfish.c crypt_blowfish.h
-FALLBACK_OBJS = fallback.o
+FALLBACK_OBJS = fallback.o tpm.o
ORIG_FALLBACK_SRCS = fallback.c
ifneq ($(origin ENABLE_HTTPBOOT), undefined)
@@ -120,7 +159,7 @@ SOURCES = $(foreach source,$(ORIG_SOURCES),$(TOPDIR)/$(source)) version.c
MOK_SOURCES = $(foreach source,$(ORIG_MOK_SOURCES),$(TOPDIR)/$(source))
FALLBACK_SRCS = $(foreach source,$(ORIG_FALLBACK_SRCS),$(TOPDIR)/$(source))
-all: $(TARGET)
+all: $(TARGETS)
shim.crt:
$(TOPDIR)/make-certs shim shim@xn--u4h.net all codesign 1.3.6.1.4.1.311.10.3.1 </dev/null
@@ -144,23 +183,30 @@ certdb/secmod.db: shim.crt
$(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 $(TOPDIR)/*.h *.h)
+shim.o: $(SOURCES)
+ifneq ($(origin ENABLE_SHIM_CERT),undefined)
+shim.o: shim_cert.h
+endif
+shim.o: $(wildcard $(TOPDIR)/*.h)
cert.o : $(TOPDIR)/cert.S
$(CC) $(CFLAGS) -c -o $@ $<
-$(SHIMNAME).so: $(OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a
+$(SHIMNAME) : $(SHIMSONAME)
+$(MMNAME) : $(MMSONAME)
+$(FBNAME) : $(FBSONAME)
+
+$(SHIMSONAME): $(OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a
$(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS)
fallback.o: $(FALLBACK_SRCS)
-$(FBNAME).so: $(FALLBACK_OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a
+$(FBSONAME): $(FALLBACK_OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a
$(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS)
MokManager.o: $(MOK_SOURCES)
-$(MMNAME).so: $(MOK_OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a
+$(MMSONAME): $(MOK_OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a
$(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) lib/lib.a
Cryptlib/libcryptlib.a:
@@ -175,39 +221,112 @@ lib/lib.a:
if [ ! -d lib ]; then mkdir lib ; fi
$(MAKE) VPATH=$(TOPDIR)/lib TOPDIR=$(TOPDIR) CFLAGS="$(CFLAGS)" -C lib -f $(TOPDIR)/lib/Makefile
-ifeq ($(ARCH),aarch64)
-FORMAT := -O binary
-SUBSYSTEM := 0xa
-LDFLAGS += --defsym=EFI_SUBSYSTEM=$(SUBSYSTEM)
+buildid : $(TOPDIR)/buildid.c
+ $(CC) -Og -g3 -Wall -Werror -Wextra -o $@ $< -lelf
+
+$(BOOTCSVNAME) :
+ @echo Making $@
+ @( printf "\xff\xfe" ; echo "$(SHIMNAME),$(OSLABEL),,This is the boot entry for $(OSLABEL)" | sed -z 's/./&\x00/g' ) > $@
+
+install-check :
+ifeq ($(origin LIBDIR),undefined)
+ $(error Architecture $(ARCH) is not a supported build target.)
+endif
+ifeq ($(origin EFIDIR),undefined)
+ $(error EFIDIR must be set to your reserved EFI System Partition subdirectory name)
endif
-ifeq ($(ARCH),arm)
-FORMAT := -O binary
-SUBSYSTEM := 0xa
-LDFLAGS += --defsym=EFI_SUBSYSTEM=$(SUBSYSTEM)
+install-deps : $(TARGETS)
+install-deps : $(SHIMNAME).debug $(MMNAME).debug $(FBNAME).debug buildid
+install-deps : $(BOOTCSVNAME)
+
+install-debugsource : install-deps
+ $(INSTALL) -d -m 0755 $(DESTDIR)/$(DEBUGSOURCE)/$(PKGNAME)-$(VERSION)$(DASHRELEASE)
+ find $(TOPDIR) -type f -a '(' -iname '*.c' -o -iname '*.h' -o -iname '*.S' ')' | while read file ; do \
+ outfile=$$(echo $${file} | sed -e "s,^$(TOPDIR),,") ; \
+ $(INSTALL) -d -m 0755 $(DESTDIR)/$(DEBUGSOURCE)/$(PKGNAME)-$(VERSION)$(DASHRELEASE)/$$(dirname $${outfile}) ; \
+ $(INSTALL) -m 0644 $${file} $(DESTDIR)/$(DEBUGSOURCE)/$(PKGNAME)-$(VERSION)$(DASHRELEASE)/$${outfile} ; \
+ done
+
+install-debuginfo : install-deps
+ $(INSTALL) -d -m 0755 $(DESTDIR)/
+ $(INSTALL) -d -m 0755 $(DESTDIR)/$(DEBUGINFO)$(TARGETDIR)/
+ @./buildid $(wildcard *.efi.debug) | while read file buildid ; do \
+ first=$$(echo $${buildid} | cut -b -2) ; \
+ rest=$$(echo $${buildid} | cut -b 3-) ; \
+ $(INSTALL) -d -m 0755 $(DESTDIR)/$(DEBUGINFO).build-id/$${first}/ ;\
+ $(INSTALL) -m 0644 $${file} $(DESTDIR)/$(DEBUGINFO)$(TARGETDIR) ; \
+ ln -s ../../../../..$(DEBUGINFO)$(TARGETDIR)$${file} $(DESTDIR)/$(DEBUGINFO).build-id/$${first}/$${rest}.debug ;\
+ ln -s ../../../.build-id/$${first}/$${rest} $(DESTDIR)/$(DEBUGINFO).build-id/$${first}/$${rest} ;\
+ done
+
+install : | install-check
+install : install-deps install-debuginfo install-debugsource
+ $(INSTALL) -d -m 0755 $(DESTDIR)/
+ $(INSTALL) -d -m 0700 $(DESTDIR)/$(ESPROOTDIR)
+ $(INSTALL) -d -m 0755 $(DESTDIR)/$(EFIBOOTDIR)
+ $(INSTALL) -d -m 0755 $(DESTDIR)/$(TARGETDIR)
+ $(INSTALL) -m 0644 $(SHIMNAME) $(DESTDIR)/$(EFIBOOTDIR)/$(BOOTEFINAME)
+ $(INSTALL) -m 0644 $(SHIMNAME) $(DESTDIR)/$(TARGETDIR)/
+ $(INSTALL) -m 0644 $(BOOTCSVNAME) $(DESTDIR)/$(TARGETDIR)/
+ifneq ($(origin ENABLE_SHIM_CERT),undefined)
+ $(INSTALL) -m 0644 $(FBNAME).signed $(DESTDIR)/$(EFIBOOTDIR)/$(FBNAME)
+ $(INSTALL) -m 0644 $(MMNAME).signed $(DESTDIR)/$(EFIBOOTDIR)/$(MMNAME)
+ $(INSTALL) -m 0644 $(MMNAME).signed $(DESTDIR)/$(TARGETDIR)/$(MMNAME)
+else
+ $(INSTALL) -m 0644 $(FBNAME) $(DESTDIR)/$(EFIBOOTDIR)/
+ $(INSTALL) -m 0644 $(MMNAME) $(DESTDIR)/$(EFIBOOTDIR)/
+ $(INSTALL) -m 0644 $(MMNAME) $(DESTDIR)/$(TARGETDIR)/
endif
-FORMAT ?= --target efi-app-$(ARCH)
+install-as-data : install-deps
+ $(INSTALL) -d -m 0755 $(DESTDIR)/$(DATATARGETDIR)
+ $(INSTALL) -m 0644 $(SHIMNAME) $(DESTDIR)/$(DATATARGETDIR)/
+ifneq ($(origin ENABLE_SHIM_HASH),undefined)
+ $(INSTALL) -m 0644 $(SHIMHASHNAME) $(DESTDIR)/$(DATATARGETDIR)/
+endif
+ifneq ($(origin ENABLE_SHIM_CERT),undefined)
+ $(INSTALL) -m 0644 $(MMNAME).signed $(DESTDIR)/$(DATATARGETDIR)/$(MMNAME)
+ $(INSTALL) -m 0644 $(FBNAME).signed $(DESTDIR)/$(DATATARGETDIR)/$(FBNAME)
+else
+ $(INSTALL) -m 0644 $(MMNAME) $(DESTDIR)/$(DATATARGETDIR)/$(MMNAME)
+ $(INSTALL) -m 0644 $(FBNAME) $(DESTDIR)/$(DATATARGETDIR)/$(FBNAME)
+endif
%.efi: %.so
ifneq ($(OBJCOPY_GTE224),1)
$(error objcopy >= 2.24 is required)
endif
$(OBJCOPY) -j .text -j .sdata -j .data -j .data.ident \
- -j .dynamic -j .dynsym -j .rel* \
+ -j .dynamic -j .dynsym -j .rel* \
-j .rela* -j .reloc -j .eh_frame \
-j .vendor_cert \
- $(FORMAT) $^ $@
+ $(FORMAT) $^ $@
+
+ifneq ($(origin ENABLE_SHIM_HASH),undefined)
+%.hash : %.efi
+ $(PESIGN) -i $< -P -h > $@
+endif
+
+%.efi.debug : %.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 .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
+ $^ $@
+ifneq ($(origin ENABLE_SBSIGN),undefined)
+%.efi.signed: %.efi shim.key shim.crt
+ $(SBSIGN) --key shim.key --cert shim.crt --output $@ $<
+else
%.efi.signed: %.efi certdb/secmod.db
$(PESIGN) -n certdb -i $< -c "shim" -s -o $@ -f
+endif
clean:
$(MAKE) -C Cryptlib -f $(TOPDIR)/Cryptlib/Makefile clean
@@ -243,4 +362,6 @@ archive: tag
@rm -rf /tmp/shim-$(VERSION)
@echo "The archive is in shim-$(VERSION).tar.bz2"
+.PHONY : install-deps
+
export ARCH CC LD OBJCOPY EFI_INCLUDE
diff --git a/MokVars.txt b/MokVars.txt
index cac5349f..d57fd87d 100644
--- a/MokVars.txt
+++ b/MokVars.txt
@@ -60,6 +60,11 @@ as described in the UEFI specification. BS,NV
MokListRT: A copy of MokList made available to the kernel at runtime. RT
+MokListX: A list of blacklisted keys and hashes. An EFI_SIGNATURE_LIST
+as described in the UEFI specification. BS,NV
+
+MokListXRT: A copy of MokListX made available to the kernel at runtime. RT
+
MokSBState: An 8-bit unsigned integer. If 1, shim will switch to
insecure mode. BS,NV
diff --git a/README b/README
index 24a39df1..ec9403a1 100644
--- a/README
+++ b/README
@@ -12,5 +12,12 @@ in the shim.h header file and provides a single entry point. On 64-bit systems
this entry point expects to be called with SysV ABI rather than MSABI, and
so calls to it should not be wrapped.
+On systems with a TPM chip enabled and supported by the system firmware,
+shim will extend various PCRs with the digests of the targets it is
+loading. A full list is in the file README.tpm .
+
To use shim, simply place a DER-encoded public certificate in a file such as
pub.cer and build with "make VENDOR_CERT_FILE=pub.cer".
+
+There are a couple of build options, and a couple of ways to customize the
+build, described in BUILDING.
diff --git a/README.tpm b/README.tpm
new file mode 100644
index 00000000..261bcd05
--- /dev/null
+++ b/README.tpm
@@ -0,0 +1,22 @@
+The following PCRs are extended by shim:
+
+PCR4:
+- the Authenticode hash of the binary being loaded will be extended into
+ PCR4 before SB verification.
+
+PCR7:
+- Any certificate in one of our certificate databases that matches a binary
+ we try to load will be extended into PCR7. That includes:
+ - DBX - the system blacklist, logged as "dbx"
+ - MokListX - the Mok blacklist, logged as "MokListX"
+ - vendor_dbx - shim's built-in vendor blacklist, logged as "dbx"
+ - DB - the system whitelist, logged as "db"
+ - MokList the Mok whitelist, logged as "MokList"
+ - vendor_cert - shim's built-in vendor whitelist, logged as "Shim"
+ - shim_cert - shim's build-time generated whitelist, logged as "Shim"
+- MokSBState will be extended into PCR7 if it is set, logged as
+ "MokSBState".
+
+PCR14:
+- MokList, MokListX, and MokSBState will be extended into PCR14 if they are
+ set.
diff --git a/TODO b/TODO
index 029b0bf2..c86c94d1 100644
--- a/TODO
+++ b/TODO
@@ -1,23 +1,14 @@
-Versioned protocol:
-- Make shim and the bootloaders using it express how enlightened they
- are to one another, so we can stop earlier without tricks like
- the one above
-MokListRT signing:
-- For kexec and hybernate to work right, MokListRT probably needs to
- be an authenticated variable. It's probable this needs to be done
- in the kernel boot stub instead, just because it'll need an
- ephemeral key to be generated, and that means we need some entropy
- to build up.
-New security protocol:
-- TBD
-kexec MoK Management:
-Modsign enforcement mgmt MoK:
-- This is part of the plan for SecureBoot patches. Basically these
- features need to be disableable/enableable in MokManager.
-Variable for debug:
-- basically we need to be able to set a UEFI variable and get debug
- output. Right now some code uses SHIM_VERBOSE but that needs a fair
- amount of work to actually be useful.
-Hashing of option roms:
-- hash option roms and add them to MokListRT
-- probably belongs in MokManager
+- Versioned protocol:
+ - Make shim and the bootloaders using it express how enlightened they
+ are to one another, so we can stop earlier without tricks like the one
+ above
+ - Make a LoadImage/CheckImage/StartImage based protocol
+- Hashing of option roms:
+ - hash option roms and add them to MokListRT
+ - probably belongs in MokManager
+- Ability to specify second stage as a device path
+ - including vendor path that means "parent of this image's path"
+ - including vendor path that means "this image"
+ - including path that's like Fv() to embed images.
+
+# vim:filetype=mail:tw=74
diff --git a/buildid.c b/buildid.c
new file mode 100644
index 00000000..b27aa1fe
--- /dev/null
+++ b/buildid.c
@@ -0,0 +1,192 @@
+/*
+ * Walk a list of input files, printing the name and buildid of any file
+ * that has one.
+ *
+ * This program is licensed under the GNU Public License version 2.
+ */
+
+#include <err.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <libelf.h>
+#include <gelf.h>
+
+static Elf_Scn *get_scn_named(Elf * elf, char *goal, GElf_Shdr * shdrp_out)
+{
+ int rc;
+ size_t shstrndx = -1;
+ int scn_no = 0;
+ Elf_Scn *scn = NULL;
+ GElf_Shdr shdr_data, *shdrp;
+
+ shdrp = shdrp_out ? shdrp_out : &shdr_data;
+
+ rc = elf_getshdrstrndx(elf, &shstrndx);
+ if (rc < 0)
+ return NULL;
+
+ do {
+ GElf_Shdr *shdr;
+ char *name;
+
+ scn = elf_getscn(elf, ++scn_no);
+ if (!scn)
+ break;
+
+ shdr = gelf_getshdr(scn, shdrp);
+ if (!shdr)
+ /*
+ * the binary is malformed, but hey, maybe the next
+ * one is fine, why not...
+ */
+ continue;
+
+ name = elf_strptr(elf, shstrndx, shdr->sh_name);
+ if (name && !strcmp(name, goal))
+ return scn;
+ } while (scn != NULL);
+ return NULL;
+}
+
+static void *get_buildid(Elf * elf, size_t * sz)
+{
+ Elf_Scn *scn;
+ size_t notesz;
+ size_t offset = 0;
+ Elf_Data *data;
+ GElf_Shdr shdr;
+
+ scn = get_scn_named(elf, ".note.gnu.build-id", &shdr);
+ if (!scn)
+ return NULL;
+
+ data = elf_getdata(scn, NULL);
+ if (!data)
+ return NULL;
+
+ do {
+ size_t nameoff;
+ size_t descoff;
+ GElf_Nhdr nhdr;
+ char *name;
+
+ notesz = gelf_getnote(data, offset, &nhdr, &nameoff, &descoff);
+ if (!notesz)
+ break;
+ offset += notesz;
+
+ if (nhdr.n_type != NT_GNU_BUILD_ID)
+ continue;
+
+ name = data->d_buf + nameoff;
+ if (!name || strcmp(name, ELF_NOTE_GNU))
+ continue;
+
+ *sz = nhdr.n_descsz;
+ return data->d_buf + descoff;
+ } while (notesz);
+ return NULL;
+}
+
+static void data2hex(uint8_t * data, size_t ds, char *str)
+{
+ const char hex[] = "0123456789abcdef";
+ int s;
+ unsigned int d;
+ for (d = 0, s = 0; d < ds; d += 1, s += 2) {
+ str[s + 0] = hex[(data[d] >> 4) & 0x0f];
+ str[s + 1] = hex[(data[d] >> 0) & 0x0f];
+ }
+ str[s] = '\0';
+}
+
+static void handle_one(char *f)
+{
+ int fd;
+ Elf *elf;
+ char *b = NULL;
+ size_t sz;
+ uint8_t *data;
+
+ if (!strcmp(f, "-")) {
+ fd = STDIN_FILENO;
+
+ if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
+ errx(1, "Couldn't read ELF data from \"%s\"", f);
+ } else {
+ if ((fd = open(f, O_RDONLY)) < 0)
+ err(1, "Couldn't open \"%s\"", f);
+
+ if ((elf = elf_begin(fd, ELF_C_READ_MMAP, NULL)) == NULL)
+ errx(1, "Couldn't read ELF data from \"%s\"", f);
+ }
+
+ data = get_buildid(elf, &sz);
+ if (data) {
+ b = alloca(sz * 2 + 1);
+ data2hex(data, sz, b);
+ if (b) {
+ write(1, f, strlen(f));
+ write(1, " ", 1);
+ write(1, b, strlen(b));
+ write(1, "\n", 1);
+ }
+ }
+ elf_end(elf);
+ close(fd);
+}
+
+static void
+ __attribute__ ((__noreturn__))
+ usage(int status)
+{
+ FILE *out = status ? stderr : stdout;
+
+ fprintf(out, "Usage: buildid [ flags | file0 [file1 [.. fileN]]]\n");
+ fprintf(out, "Flags:\n");
+ fprintf(out, " -h Print this help text and exit\n");
+
+ exit(status);
+}
+
+int main(int argc, char **argv)
+{
+ int i;
+ struct option options[] = {
+ {.name = "help",
+ .val = '?',
+ },
+ {.name = "usage",
+ .val = '?',
+ },
+ {.name = ""}
+ };
+ int longindex = -1;
+
+ while ((i = getopt_long(argc, argv, "h", options, &longindex)) != -1) {
+ switch (i) {
+ case 'h':
+ case '?':
+ usage(longindex == -1 ? 1 : 0);
+ break;
+ }
+ }
+
+ elf_version(EV_CURRENT);
+
+ if (optind == argc)
+ usage(1);
+
+ for (i = optind; i < argc; i++)
+ handle_one(argv[i]);
+
+ return 0;
+}
+
+// vim:fenc=utf-8:tw=75
diff --git a/commit b/commit
index afa07ac4..9922e3f4 100644
--- a/commit
+++ b/commit
@@ -1 +1 @@
-478f9bb2ea91b361ab52dac6604fdfb47e1e963c \ No newline at end of file
+5202f80c32bdcab0469785e953bf9fa8dd4eaaa1 \ No newline at end of file
diff --git a/fallback.c b/fallback.c
index 09749bb6..46894aff 100644
--- a/fallback.c
+++ b/fallback.c
@@ -12,6 +12,7 @@
#include "ucs2.h"
#include "variables.h"
+#include "tpm.h"
EFI_LOADED_IMAGE *this_image = NULL;
@@ -286,6 +287,105 @@ add_boot_option(EFI_DEVICE_PATH *hddp, EFI_DEVICE_PATH *fulldp,
return EFI_OUT_OF_RESOURCES;
}
+/*
+ * AMI BIOS (e.g, Intel NUC5i3MYHE) may automatically hide and patch BootXXXX
+ * variables with ami_masked_device_path_guid. We can get the valid device path
+ * if just skipping it and its next end path.
+ */
+
+static EFI_GUID ami_masked_device_path_guid = {
+ 0x99e275e7, 0x75a0, 0x4b37,
+ { 0xa2, 0xe6, 0xc5, 0x38, 0x5e, 0x6c, 0x0, 0xcb }
+};
+
+static unsigned int
+calc_masked_boot_option_size(unsigned int size)
+{
+ return size + sizeof(EFI_DEVICE_PATH) +
+ sizeof(ami_masked_device_path_guid) + sizeof(EFI_DEVICE_PATH);
+}
+
+static int
+check_masked_boot_option(CHAR8 *candidate, unsigned int candidate_size,
+ CHAR8 *data, unsigned int data_size)
+{
+ /*
+ * The patched BootXXXX variables contain a hardware device path and
+ * an end path, preceding the real device path.
+ */
+ if (calc_masked_boot_option_size(data_size) != candidate_size)
+ return 1;
+
+ CHAR8 *cursor = candidate;
+
+ /* Check whether the BootXXXX is patched */
+ cursor += sizeof(UINT32) + sizeof(UINT16);
+ cursor += StrSize((CHAR16 *)cursor);
+
+ unsigned int min_valid_size = cursor - candidate + sizeof(EFI_DEVICE_PATH);
+
+ if (candidate_size <= min_valid_size)
+ return 1;
+
+ EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *)cursor;
+ unsigned int node_size = DevicePathNodeLength(dp) - sizeof(EFI_DEVICE_PATH);
+
+ min_valid_size += node_size;
+ if (candidate_size <= min_valid_size ||
+ DevicePathType(dp) != HARDWARE_DEVICE_PATH ||
+ DevicePathSubType(dp) != HW_VENDOR_DP ||
+ node_size != sizeof(ami_masked_device_path_guid) ||
+ CompareGuid((EFI_GUID *)(cursor + sizeof(EFI_DEVICE_PATH)),
+ &ami_masked_device_path_guid))
+ return 1;
+
+ /* Check whether the patched guid is followed by an end path */
+ min_valid_size += sizeof(EFI_DEVICE_PATH);
+ if (candidate_size <= min_valid_size)
+ return 1;
+
+ dp = NextDevicePathNode(dp);
+ if (!IsDevicePathEnd(dp))
+ return 1;
+
+ /*
+ * OK. We may really get a masked BootXXXX variable. The next
+ * step is to test whether it is hidden.
+ */
+ UINT32 attrs = *(UINT32 *)candidate;
+#ifndef LOAD_OPTION_HIDDEN
+# define LOAD_OPTION_HIDDEN 0x00000008
+#endif
+ if (!(attrs & LOAD_OPTION_HIDDEN))
+ return 1;
+
+ attrs &= ~LOAD_OPTION_HIDDEN;
+
+ /* Compare the field Attributes */
+ if (attrs != *(UINT32 *)data)
+ return 1;
+
+ /* Compare the field FilePathListLength */
+ data += sizeof(UINT32);
+ candidate += sizeof(UINT32);
+ if (calc_masked_boot_option_size(*(UINT16 *)data) !=
+ *(UINT16 *)candidate)
+ return 1;
+
+ /* Compare the field Description */
+ data += sizeof(UINT16);
+ candidate += sizeof(UINT16);
+ if (CompareMem(candidate, data, cursor - candidate))
+ return 1;
+
+ /* Compare the filed FilePathList */
+ cursor = (CHAR8 *)NextDevicePathNode(dp);
+ data += sizeof(UINT16);
+ data += StrSize((CHAR16 *)data);
+
+ return CompareMem(cursor, data, candidate_size - min_valid_size);
+}
+
EFI_STATUS
find_boot_option(EFI_DEVICE_PATH *dp, EFI_DEVICE_PATH *fulldp,
CHAR16 *filename, CHAR16 *label, CHAR16 *arguments,
@@ -315,7 +415,8 @@ find_boot_option(EFI_DEVICE_PATH *dp, EFI_DEVICE_PATH *fulldp,
EFI_GUID global = EFI_GLOBAL_VARIABLE;
EFI_STATUS rc;
- CHAR8 *candidate = AllocateZeroPool(size);
+ UINTN max_candidate_size = calc_masked_boot_option_size(size);
+ CHAR8 *candidate = AllocateZeroPool(max_candidate_size);
if (!candidate) {
FreePool(data);
return EFI_OUT_OF_RESOURCES;
@@ -327,17 +428,21 @@ find_boot_option(EFI_DEVICE_PATH *dp, EFI_DEVICE_PATH *fulldp,
varname[6] = hexmap[(bootorder[i] & 0x00f0) >> 4];
varname[7] = hexmap[(bootorder[i] & 0x000f) >> 0];
- UINTN candidate_size = size;
+ UINTN candidate_size = max_candidate_size;
rc = uefi_call_wrapper(RT->GetVariable, 5, varname, &global,
NULL, &candidate_size, candidate);
if (EFI_ERROR(rc))
continue;
- if (candidate_size != size)
+ if (candidate_size != size) {
+ if (check_masked_boot_option(candidate, candidate_size,
+ data, size))
+ continue;
+ } else if (CompareMem(candidate, data, size))
continue;
- if (CompareMem(candidate, data, size))
- continue;
+ VerbosePrint(L"Found boot entry \"%s\" with label \"%s\" "
+ L"for file \"%s\"\n", varname, label, filename);
/* at this point, we have duplicate data. */
if (!first_new_option) {
@@ -481,7 +586,7 @@ add_to_boot_list(CHAR16 *dirname, CHAR16 *filename, CHAR16 *label, CHAR16 *argum
err:
if (full_device_path)
FreePool(full_device_path);
- if (dp)
+ if (dp && dp != full_device_path)
FreePool(dp);
if (fullpath)
FreePool(fullpath);
@@ -904,7 +1009,13 @@ efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab)
return rc;
}
- try_start_first_option(image);
+ rc = fallback_should_prefer_reset();
+ if (EFI_ERROR(rc)) {
+ VerbosePrint(L"tpm not present, starting the first image\n");
+ try_start_first_option(image);
+ } else {
+ VerbosePrint(L"tpm present, resetting system\n");
+ }
Print(L"Reset System\n");
diff --git a/shim.c b/shim.c
index 6e040c4f..bd314b31 100644
--- a/shim.c
+++ b/shim.c
@@ -40,7 +40,6 @@
#include "shim.h"
#include "netboot.h"
#include "httpboot.h"
-#include "shim_cert.h"
#include "replacements.h"
#include "tpm.h"
#include "ucs2.h"
@@ -52,6 +51,10 @@
#include "console.h"
#include "version.h"
+#ifdef ENABLE_SHIM_CERT
+#include "shim_cert.h"
+#endif
+
#include <stdarg.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
@@ -428,7 +431,8 @@ static BOOLEAN verify_eku(UINT8 *Cert, UINTN CertSize)
static CHECK_STATUS check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList,
UINTN dbsize,
WIN_CERTIFICATE_EFI_PKCS *data,
- UINT8 *hash)
+ UINT8 *hash, CHAR16 *dbname,
+ EFI_GUID guid)
{
EFI_SIGNATURE_DATA *Cert;
UINTN CertSize;
@@ -446,8 +450,10 @@ static CHECK_STATUS check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList,
Cert->SignatureData,
CertSize,
hash, SHA256_DIGEST_SIZE);
- if (IsFound)
+ if (IsFound) {
+ tpm_measure_variable(dbname, guid, CertSize, Cert->SignatureData);
return DATA_FOUND;
+ }
}
} else if (verbose) {
console_notify(L"Not a DER encoding x.509 Certificate");
@@ -477,7 +483,7 @@ static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid,
CertList = (EFI_SIGNATURE_LIST *)db;
- rc = check_db_cert_in_ram(CertList, dbsize, data, hash);
+ rc = check_db_cert_in_ram(CertList, dbsize, data, hash, dbname, guid);
FreePool(db);
@@ -489,7 +495,8 @@ static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid,
*/
static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList,
UINTN dbsize, UINT8 *data,
- int SignatureSize, EFI_GUID CertType)
+ int SignatureSize, EFI_GUID CertType,
+ CHAR16 *dbname, EFI_GUID guid)
{
EFI_SIGNATURE_DATA *Cert;
UINTN CertCount, Index;
@@ -505,6 +512,7 @@ static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList,
// Find the signature in database.
//
IsFound = TRUE;
+ tpm_measure_variable(dbname, guid, SignatureSize, data);
break;
}
@@ -545,7 +553,8 @@ static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data,
CertList = (EFI_SIGNATURE_LIST *)db;
CHECK_STATUS rc = check_db_hash_in_ram(CertList, dbsize, data,
- SignatureSize, CertType);
+ SignatureSize, CertType,
+ dbname, guid);
FreePool(db);
return rc;
@@ -563,15 +572,18 @@ static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert,
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) ==
+ SHA256_DIGEST_SIZE, EFI_CERT_SHA256_GUID,
+ L"dbx", secure_var) ==
DATA_FOUND)
return EFI_SECURITY_VIOLATION;
if (check_db_hash_in_ram(dbx, vendor_dbx_size, sha1hash,
- SHA1_DIGEST_SIZE, EFI_CERT_SHA1_GUID) ==
+ SHA1_DIGEST_SIZE, EFI_CERT_SHA1_GUID,
+ L"dbx", secure_var) ==
DATA_FOUND)
return EFI_SECURITY_VIOLATION;
if (cert && check_db_cert_in_ram(dbx, vendor_dbx_size, cert,
- sha256hash) == DATA_FOUND)
+ sha256hash, L"dbx",
+ secure_var) == DATA_FOUND)
return EFI_SECURITY_VIOLATION;
if (check_db_hash(L"dbx", secure_var, sha256hash, SHA256_DIGEST_SIZE,
@@ -953,13 +965,13 @@ static EFI_STATUS verify_mok (void) {
* 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)
+ PE_COFF_LOADER_IMAGE_CONTEXT *context,
+ UINT8 *sha256hash, UINT8 *sha1hash)
{
- UINT8 sha256hash[SHA256_DIGEST_SIZE];
- UINT8 sha1hash[SHA1_DIGEST_SIZE];
EFI_STATUS status = EFI_SECURITY_VIOLATION;
WIN_CERTIFICATE_EFI_PKCS *cert = NULL;
unsigned int size = datasize;
+ EFI_GUID shim_var = SHIM_LOCK_GUID;
if (context->SecDir->Size != 0) {
if (context->SecDir->Size >= size) {
@@ -1017,6 +1029,7 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
return status;
if (cert) {
+#if defined(ENABLE_SHIM_CERT)
/*
* Check against the shim build key
*/
@@ -1026,9 +1039,11 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
shim_cert, sizeof(shim_cert), sha256hash,
SHA256_DIGEST_SIZE)) {
update_verification_method(VERIFIED_BY_CERT);
+ tpm_measure_variable(L"Shim", shim_var, sizeof(shim_cert), shim_cert);
status = EFI_SUCCESS;
return status;
}
+#endif /* defined(ENABLE_SHIM_CERT) */
/*
* And finally, check against shim's built-in key
@@ -1039,6 +1054,7 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
vendor_cert, vendor_cert_size,
sha256hash, SHA256_DIGEST_SIZE)) {
update_verification_method(VERIFIED_BY_CERT);
+ tpm_measure_variable(L"Shim", shim_var, vendor_cert_size, vendor_cert);
status = EFI_SUCCESS;
return status;
}
@@ -1194,6 +1210,8 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
unsigned int alignment, alloc_size;
EFI_PHYSICAL_ADDRESS alloc_address;
int found_entry_point = 0;
+ UINT8 sha1hash[SHA1_DIGEST_SIZE];
+ UINT8 sha256hash[SHA256_DIGEST_SIZE];
/*
* The binary header contains relevant context and section pointers
@@ -1207,8 +1225,17 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
/*
* We only need to verify the binary if we're in secure mode
*/
+ efi_status = generate_hash(data, datasize, &context, sha256hash,
+ sha1hash);
+ if (efi_status != EFI_SUCCESS)
+ return efi_status;
+
+ /* Measure the binary into the TPM */
+ tpm_log_pe((EFI_PHYSICAL_ADDRESS)(UINTN)data, datasize, sha1hash, 4);
+
if (secure_mode ()) {
- efi_status = verify_buffer(data, datasize, &context);
+ efi_status = verify_buffer(data, datasize, &context,
+ sha256hash, sha1hash);
if (EFI_ERROR(efi_status)) {
console_error(L"Verification failed", efi_status);
@@ -1699,6 +1726,8 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size)
{
EFI_STATUS status = EFI_SUCCESS;
PE_COFF_LOADER_IMAGE_CONTEXT context;
+ UINT8 sha1hash[SHA1_DIGEST_SIZE];
+ UINT8 sha256hash[SHA256_DIGEST_SIZE];
loader_is_participating = 1;
in_protocol = 1;
@@ -1710,7 +1739,11 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size)
if (status != EFI_SUCCESS)
goto done;
- status = verify_buffer(buffer, size, &context);
+ status = generate_hash(buffer, size, &context, sha256hash, sha1hash);
+ if (status != EFI_SUCCESS)
+ goto done;
+
+ status = verify_buffer(buffer, size, &context, sha256hash, sha1hash);
done:
in_protocol = 0;
return status;
@@ -1814,10 +1847,6 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
}
}
- /* Measure the binary into the TPM */
- tpm_log_event((EFI_PHYSICAL_ADDRESS)(UINTN)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
@@ -1888,37 +1917,64 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle)
}
/*
- * Measure some of the MOK variables into the TPM
+ * Measure some of the MOK variables into the TPM. We measure the entirety
+ * of MokList into PCR 14, and also measure the raw MokSBState there. PCR 7
+ * will be extended with MokSBState in the Microsoft format, and we'll
+ * measure any matching hashes or certificates later on in order to behave
+ * consistently with the PCR 7 spec.
*/
EFI_STATUS measure_mok()
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
- EFI_STATUS efi_status;
+ EFI_STATUS efi_status, ret = EFI_SUCCESS;
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;
+ if (!EFI_ERROR(efi_status)) {
+ efi_status = tpm_log_event((EFI_PHYSICAL_ADDRESS)(UINTN)Data,
+ DataSize, 14, (CHAR8 *)"MokList");
+ FreePool(Data);
- efi_status = tpm_log_event((EFI_PHYSICAL_ADDRESS)(UINTN)Data,
- DataSize, 14, (CHAR8 *)"MokList");
+ if (EFI_ERROR(efi_status))
+ ret = efi_status;
- FreePool(Data);
+ } else {
+ ret = efi_status;
+ }
- if (efi_status != EFI_SUCCESS)
- return efi_status;
+ efi_status = get_variable(L"MokListX", &Data, &DataSize, shim_lock_guid);
+ if (!EFI_ERROR(efi_status)) {
+ efi_status = tpm_log_event((EFI_PHYSICAL_ADDRESS)(UINTN)Data,
+ DataSize, 14, (CHAR8 *)"MokListX");
+ FreePool(Data);
+
+ if (EFI_ERROR(efi_status) && !EFI_ERROR(ret))
+ ret = efi_status;
+
+ } else if (!EFI_ERROR(ret)) {
+ ret = efi_status;
+ }
efi_status = get_variable(L"MokSBState", &Data, &DataSize,
shim_lock_guid);
+ if (!EFI_ERROR(efi_status)) {
+ efi_status = tpm_measure_variable(L"MokSBState",
+ shim_lock_guid,
+ DataSize, Data);
+ if (!EFI_ERROR(efi_status)) {
+ efi_status = tpm_log_event((EFI_PHYSICAL_ADDRESS)
+ (UINTN)Data, DataSize, 14,
+ (CHAR8 *)"MokSBState");
+ }
- if (efi_status != EFI_SUCCESS)
- return efi_status;
-
- efi_status = tpm_log_event((EFI_PHYSICAL_ADDRESS)(UINTN)Data,
- DataSize, 14, (CHAR8 *)"MokSBState");
+ FreePool(Data);
- FreePool(Data);
+ if (EFI_ERROR(efi_status) && !EFI_ERROR(ret))
+ ret = efi_status;
+ } else if (!EFI_ERROR(ret)) {
+ ret = efi_status;
+ }
return efi_status;
}
diff --git a/tpm.c b/tpm.c
index c37cc484..05b3c6fb 100644
--- a/tpm.c
+++ b/tpm.c
@@ -1,6 +1,7 @@
#include <efi.h>
#include <efilib.h>
#include <string.h>
+#include <stdint.h>
#include "tpm.h"
@@ -14,6 +15,16 @@ extern UINT8 in_protocol;
})
+typedef struct {
+ CHAR16 *VariableName;
+ EFI_GUID *VendorGuid;
+ VOID *Data;
+ UINTN Size;
+} VARIABLE_RECORD;
+
+UINTN measuredcount = 0;
+VARIABLE_RECORD *measureddata = NULL;
+
EFI_GUID tpm_guid = EFI_TPM_GUID;
EFI_GUID tpm2_guid = EFI_TPM2_GUID;
@@ -108,27 +119,60 @@ static EFI_STATUS trigger_tcg2_final_events_table(efi_tpm2_protocol_t *tpm2,
&start, &end, &truncated);
}
-EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr,
- const CHAR8 *description)
+static EFI_STATUS tpm_locate_protocol(efi_tpm_protocol_t **tpm,
+ efi_tpm2_protocol_t **tpm2,
+ BOOLEAN *old_caps_p,
+ EFI_TCG2_BOOT_SERVICE_CAPABILITY *capsp)
{
EFI_STATUS status;
- efi_tpm_protocol_t *tpm;
- efi_tpm2_protocol_t *tpm2;
- status = LibLocateProtocol(&tpm2_guid, (VOID **)&tpm2);
+ *tpm = NULL;
+ *tpm2 = NULL;
+ status = LibLocateProtocol(&tpm2_guid, (VOID **)tpm2);
/* TPM 2.0 */
if (status == EFI_SUCCESS) {
BOOLEAN old_caps;
- EFI_TCG2_EVENT *event;
EFI_TCG2_BOOT_SERVICE_CAPABILITY caps;
- EFI_TCG2_EVENT_LOG_BITMAP supported_logs;
- status = tpm2_get_caps(tpm2, &caps, &old_caps);
- if (status != EFI_SUCCESS)
+ status = tpm2_get_caps(*tpm2, &caps, &old_caps);
+ if (EFI_ERROR(status))
+ return status;
+
+ if (tpm2_present(&caps, old_caps)) {
+ if (old_caps_p)
+ *old_caps_p = old_caps;
+ if (capsp)
+ memcpy(capsp, &caps, sizeof(caps));
return EFI_SUCCESS;
+ }
+ } else {
+ status = LibLocateProtocol(&tpm_guid, (VOID **)tpm);
+ if (EFI_ERROR(status))
+ return status;
- if (!tpm2_present(&caps, old_caps))
+ if (tpm_present(*tpm))
return EFI_SUCCESS;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+static EFI_STATUS tpm_log_event_raw(EFI_PHYSICAL_ADDRESS buf, UINTN size,
+ UINT8 pcr, const CHAR8 *log, UINTN logsize,
+ UINT32 type, CHAR8 *hash)
+{
+ EFI_STATUS status;
+ efi_tpm_protocol_t *tpm;
+ efi_tpm2_protocol_t *tpm2;
+ BOOLEAN old_caps;
+ EFI_TCG2_BOOT_SERVICE_CAPABILITY caps;
+
+ status = tpm_locate_protocol(&tpm, &tpm2, &old_caps, &caps);
+ if (EFI_ERROR(status)) {
+ return status;
+ } else if (tpm2) {
+ EFI_TCG2_EVENT *event;
+ EFI_TCG2_EVENT_LOG_BITMAP supported_logs;
supported_logs = tpm2_get_supported_logs(tpm2, &caps, old_caps);
@@ -138,7 +182,7 @@ EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr,
return status;
}
- event = AllocatePool(sizeof(*event) + strlen(description) + 1);
+ event = AllocatePool(sizeof(*event) + logsize);
if (!event) {
perror(L"Unable to allocate event structure\n");
return EFI_OUT_OF_RESOURCES;
@@ -147,14 +191,24 @@ EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr,
event->Header.HeaderSize = sizeof(EFI_TCG2_EVENT_HEADER);
event->Header.HeaderVersion = 1;
event->Header.PCRIndex = pcr;
- event->Header.EventType = EV_IPL;
- event->Size = sizeof(*event) - sizeof(event->Event) + strlen(description) + 1;
- memcpy(event->Event, description, strlen(description) + 1);
- status = uefi_call_wrapper(tpm2->hash_log_extend_event, 5, tpm2,
- 0, buf, (UINT64) size, event);
+ event->Header.EventType = type;
+ event->Size = sizeof(*event) - sizeof(event->Event) + logsize + 1;
+ CopyMem(event->Event, (VOID *)log, logsize);
+ if (hash) {
+ /* TPM 2 systems will generate the appropriate hash
+ themselves if we pass PE_COFF_IMAGE
+ */
+ status = uefi_call_wrapper(tpm2->hash_log_extend_event,
+ 5, tpm2, PE_COFF_IMAGE, buf,
+ (UINT64) size, event);
+ } else {
+ status = uefi_call_wrapper(tpm2->hash_log_extend_event,
+ 5, tpm2, 0, buf,
+ (UINT64) size, event);
+ }
FreePool(event);
return status;
- } else {
+ } else if (tpm) {
TCG_PCR_EVENT *event;
UINT32 eventnum = 0;
EFI_PHYSICAL_ADDRESS lastevent;
@@ -167,7 +221,7 @@ EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr,
if (!tpm_present(tpm))
return EFI_SUCCESS;
- event = AllocatePool(sizeof(*event) + strlen(description) + 1);
+ event = AllocatePool(sizeof(*event) + logsize);
if (!event) {
perror(L"Unable to allocate event structure\n");
@@ -175,14 +229,164 @@ EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr,
}
event->PCRIndex = pcr;
- event->EventType = EV_IPL;
- event->EventSize = strlen(description) + 1;
- status = uefi_call_wrapper(tpm->log_extend_event, 7, tpm, buf,
- (UINT64)size, TPM_ALG_SHA, event,
- &eventnum, &lastevent);
+ event->EventType = type;
+ event->EventSize = logsize;
+ CopyMem(event->Event, (VOID *)log, logsize);
+ if (hash) {
+ /* TPM 1.2 devices require us to pass the Authenticode
+ hash rather than allowing the firmware to attempt
+ to calculate it */
+ CopyMem(event->digest, hash, sizeof(event->digest));
+ status = uefi_call_wrapper(tpm->log_extend_event, 7,
+ tpm, 0, 0, TPM_ALG_SHA,
+ event, &eventnum,
+ &lastevent);
+ } else {
+ status = uefi_call_wrapper(tpm->log_extend_event, 7,
+ tpm, buf, (UINT64)size,
+ TPM_ALG_SHA, event,
+ &eventnum, &lastevent);
+ }
FreePool(event);
return status;
}
return EFI_SUCCESS;
}
+
+EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr,
+ const CHAR8 *description)
+{
+ return tpm_log_event_raw(buf, size, pcr, description,
+ strlen(description) + 1, 0xd, NULL);
+}
+
+EFI_STATUS tpm_log_pe(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 *sha1hash,
+ UINT8 pcr)
+{
+ EFI_IMAGE_LOAD_EVENT ImageLoad;
+
+ // All of this is informational and forces us to do more parsing before
+ // we can generate it, so let's just leave it out for now
+ ImageLoad.ImageLocationInMemory = 0;
+ ImageLoad.ImageLengthInMemory = 0;
+ ImageLoad.ImageLinkTimeAddress = 0;
+ ImageLoad.LengthOfDevicePath = 0;
+
+ return tpm_log_event_raw(buf, size, pcr, (CHAR8 *)&ImageLoad,
+ sizeof(ImageLoad),
+ EV_EFI_BOOT_SERVICES_APPLICATION, sha1hash);
+}
+
+typedef struct {
+ EFI_GUID VariableName;
+ UINT64 UnicodeNameLength;
+ UINT64 VariableDataLength;
+ CHAR16 UnicodeName[1];
+ INT8 VariableData[1];
+} EFI_VARIABLE_DATA_TREE;
+
+static BOOLEAN tpm_data_measured(CHAR16 *VarName, EFI_GUID VendorGuid, UINTN VarSize, VOID *VarData)
+{
+ UINTN i;
+
+ for (i=0; i<measuredcount; i++) {
+ if ((StrCmp (VarName, measureddata[i].VariableName) == 0) &&
+ (CompareGuid (&VendorGuid, measureddata[i].VendorGuid)) &&
+ (VarSize == measureddata[i].Size) &&
+ (CompareMem (VarData, measureddata[i].Data, VarSize) == 0)) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static EFI_STATUS tpm_record_data_measurement(CHAR16 *VarName, EFI_GUID VendorGuid, UINTN VarSize, VOID *VarData)
+{
+ if (measureddata == NULL) {
+ measureddata = AllocatePool(sizeof(*measureddata));
+ } else {
+ measureddata = ReallocatePool(measureddata, measuredcount * sizeof(*measureddata),
+ (measuredcount + 1) * sizeof(*measureddata));
+ }
+
+ if (measureddata == NULL)
+ return EFI_OUT_OF_RESOURCES;
+
+ measureddata[measuredcount].VariableName = AllocatePool(StrSize(VarName));
+ measureddata[measuredcount].VendorGuid = AllocatePool(sizeof(EFI_GUID));
+ measureddata[measuredcount].Data = AllocatePool(VarSize);
+
+ if (measureddata[measuredcount].VariableName == NULL ||
+ measureddata[measuredcount].VendorGuid == NULL ||
+ measureddata[measuredcount].Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ StrCpy(measureddata[measuredcount].VariableName, VarName);
+ CopyMem(measureddata[measuredcount].VendorGuid, &VendorGuid, sizeof(EFI_GUID));
+ CopyMem(measureddata[measuredcount].Data, VarData, VarSize);
+ measureddata[measuredcount].Size = VarSize;
+ measuredcount++;
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS tpm_measure_variable(CHAR16 *VarName, EFI_GUID VendorGuid, UINTN VarSize, VOID *VarData)
+{
+ EFI_STATUS Status;
+ UINTN VarNameLength;
+ EFI_VARIABLE_DATA_TREE *VarLog;
+ UINT32 VarLogSize;
+
+ /* Don't measure something that we've already measured */
+ if (tpm_data_measured(VarName, VendorGuid, VarSize, VarData))
+ return EFI_SUCCESS;
+
+ VarNameLength = StrLen (VarName);
+ VarLogSize = (UINT32)(sizeof (*VarLog) +
+ VarNameLength * sizeof (*VarName) +
+ VarSize -
+ sizeof (VarLog->UnicodeName) -
+ sizeof (VarLog->VariableData));
+
+ VarLog = (EFI_VARIABLE_DATA_TREE *) AllocateZeroPool (VarLogSize);
+ if (VarLog == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (&VarLog->VariableName, &VendorGuid,
+ sizeof(VarLog->VariableName));
+ VarLog->UnicodeNameLength = VarNameLength;
+ VarLog->VariableDataLength = VarSize;
+ CopyMem (VarLog->UnicodeName, VarName,
+ VarNameLength * sizeof (*VarName));
+ CopyMem ((CHAR16 *)VarLog->UnicodeName + VarNameLength, VarData,
+ VarSize);
+
+ Status = tpm_log_event_raw((EFI_PHYSICAL_ADDRESS)(intptr_t)VarLog,
+ VarLogSize, 7, (CHAR8 *)VarLog, VarLogSize,
+ EV_EFI_VARIABLE_AUTHORITY, NULL);
+
+ FreePool(VarLog);
+
+ if (Status != EFI_SUCCESS)
+ return Status;
+
+ return tpm_record_data_measurement(VarName, VendorGuid, VarSize,
+ VarData);
+}
+
+EFI_STATUS
+fallback_should_prefer_reset(void)
+{
+ EFI_STATUS status;
+ efi_tpm_protocol_t *tpm;
+ efi_tpm2_protocol_t *tpm2;
+
+ status = tpm_locate_protocol(&tpm, &tpm2, NULL, NULL);
+ if (EFI_ERROR(status))
+ return EFI_NOT_FOUND;
+ return EFI_SUCCESS;
+}
diff --git a/tpm.h b/tpm.h
index cc1bbedb..d11b545b 100644
--- a/tpm.h
+++ b/tpm.h
@@ -1,3 +1,5 @@
+#include <efilib.h>
+
#define EFI_TPM_GUID {0xf541796d, 0xa62e, 0x4954, {0xa7, 0x75, 0x95, 0x84, 0xf6, 0x1b, 0x9c, 0xdd }};
#define EFI_TPM2_GUID {0x607f766c, 0x7455, 0x42be, {0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f }};
@@ -6,6 +8,12 @@
EFI_STATUS tpm_log_event(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 pcr,
const CHAR8 *description);
+EFI_STATUS fallback_should_prefer_reset(void);
+
+EFI_STATUS tpm_log_pe(EFI_PHYSICAL_ADDRESS buf, UINTN size, UINT8 *sha1hash,
+ UINT8 pcr);
+
+EFI_STATUS tpm_measure_variable(CHAR16 *dbname, EFI_GUID guid, UINTN size, void *data);
typedef struct {
uint8_t Major;
@@ -31,6 +39,14 @@ typedef struct _TCG_PCR_EVENT {
uint8_t Event[1];
} TCG_PCR_EVENT;
+typedef struct _EFI_IMAGE_LOAD_EVENT {
+ EFI_PHYSICAL_ADDRESS ImageLocationInMemory;
+ UINTN ImageLengthInMemory;
+ UINTN ImageLinkTimeAddress;
+ UINTN LengthOfDevicePath;
+ EFI_DEVICE_PATH DevicePath[1];
+} EFI_IMAGE_LOAD_EVENT;
+
struct efi_tpm_protocol
{
EFI_STATUS (EFIAPI *status_check) (struct efi_tpm_protocol *this,
@@ -154,3 +170,19 @@ struct efi_tpm2_protocol
};
typedef struct efi_tpm2_protocol efi_tpm2_protocol_t;
+
+typedef UINT32 TCG_EVENTTYPE;
+
+#define EV_EFI_EVENT_BASE ((TCG_EVENTTYPE) 0x80000000)
+#define EV_EFI_VARIABLE_DRIVER_CONFIG (EV_EFI_EVENT_BASE + 1)
+#define EV_EFI_VARIABLE_BOOT (EV_EFI_EVENT_BASE + 2)
+#define EV_EFI_BOOT_SERVICES_APPLICATION (EV_EFI_EVENT_BASE + 3)
+#define EV_EFI_BOOT_SERVICES_DRIVER (EV_EFI_EVENT_BASE + 4)
+#define EV_EFI_RUNTIME_SERVICES_DRIVER (EV_EFI_EVENT_BASE + 5)
+#define EV_EFI_GPT_EVENT (EV_EFI_EVENT_BASE + 6)
+#define EV_EFI_ACTION (EV_EFI_EVENT_BASE + 7)
+#define EV_EFI_PLATFORM_FIRMWARE_BLOB (EV_EFI_EVENT_BASE + 8)
+#define EV_EFI_HANDOFF_TABLES (EV_EFI_EVENT_BASE + 9)
+#define EV_EFI_VARIABLE_AUTHORITY (EV_EFI_EVENT_BASE + 0xE0)
+
+#define PE_COFF_IMAGE 0x0000000000000010