diff --git a/.gitignore b/.gitignore index 586bc24..d0d9129 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 0000000..461b85c --- /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 a5e02d4..a025ac5 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 4319afd..4c683bf 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 fb5ab27..afd6504 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,38 +70,57 @@ 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)\" @@ -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 $@ + +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 cac5349..d57fd87 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 24a39df..ec9403a 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 0000000..261bcd0 --- /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 029b0bf..c86c94d 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 0000000..b27aa1f --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 afa07ac..9922e3f 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 09749bb..46894af 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 6e040c4..bd314b3 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 #include #include @@ -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; + FreePool(Data); - efi_status = tpm_log_event((EFI_PHYSICAL_ADDRESS)(UINTN)Data, - DataSize, 14, (CHAR8 *)"MokSBState"); - - 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 c37cc48..05b3c6f 100644 --- a/tpm.c +++ b/tpm.c @@ -1,6 +1,7 @@ #include #include #include +#include #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,28 +119,61 @@ 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; + + *tpm = NULL; + *tpm2 = NULL; + status = LibLocateProtocol(&tpm2_guid, (VOID **)tpm2); + /* TPM 2.0 */ + if (status == EFI_SUCCESS) { + BOOLEAN old_caps; + EFI_TCG2_BOOT_SERVICE_CAPABILITY caps; + + 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 (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 = LibLocateProtocol(&tpm2_guid, (VOID **)&tpm2); - /* TPM 2.0 */ - if (status == EFI_SUCCESS) { - BOOLEAN old_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_BOOT_SERVICE_CAPABILITY caps; EFI_TCG2_EVENT_LOG_BITMAP supported_logs; - status = tpm2_get_caps(tpm2, &caps, &old_caps); - if (status != EFI_SUCCESS) - return EFI_SUCCESS; - - if (!tpm2_present(&caps, old_caps)) - return EFI_SUCCESS; - supported_logs = tpm2_get_supported_logs(tpm2, &caps, old_caps); status = trigger_tcg2_final_events_table(tpm2, supported_logs); @@ -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; iUnicodeName) - + 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 cc1bbed..d11b545 100644 --- a/tpm.h +++ b/tpm.h @@ -1,3 +1,5 @@ +#include + #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