Updated version 12+1503074702.5202f80 from 'upstream/12+1503074702.5202f80'

with Debian dir 08243b332b
This commit is contained in:
Mathieu Trudel-Lapierre 2017-08-29 13:55:31 -04:00
commit c224bb09d2
15 changed files with 953 additions and 150 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
certdb
shim_cert.h
*.a
*.CSV
*.cer
*.crl
*.crt

61
BUILDING Normal file
View File

@ -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

View File

@ -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 \

View File

@ -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 \

221
Makefile
View File

@ -1,7 +1,8 @@
VERSION = 12
RELEASE :=
ifneq ($(RELEASE),"")
RELEASE:="-$(RELEASE)"
ifneq ($(origin RELEASE),undefined)
DASHRELEASE ?= -$(RELEASE)
else
DASHRELEASE ?=
endif
ifeq ($(MAKELEVEL),0)
@ -10,15 +11,28 @@ 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)
@ -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)
@ -62,36 +72,55 @@ 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
-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
-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 </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,19 +221,77 @@ 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)
@ -198,16 +302,31 @@ endif
-j .rela* -j .reloc -j .eh_frame \
-j .vendor_cert \
$(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 .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

View File

@ -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

7
README
View File

@ -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.

22
README.tpm Normal file
View File

@ -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.

37
TODO
View File

@ -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

192
buildid.c Normal file
View File

@ -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

2
commit
View File

@ -1 +1 @@
478f9bb2ea91b361ab52dac6604fdfb47e1e963c
5202f80c32bdcab0469785e953bf9fa8dd4eaaa1

View File

@ -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;
}
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");

120
shim.c
View File

@ -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,9 +450,11 @@ 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,38 +1917,65 @@ 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);
if (efi_status != EFI_SUCCESS)
return efi_status;
if (EFI_ERROR(efi_status))
ret = efi_status;
} else {
ret = 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_status != EFI_SUCCESS)
return efi_status;
efi_status = tpm_log_event((EFI_PHYSICAL_ADDRESS)(UINTN)Data,
DataSize, 14, (CHAR8 *)"MokSBState");
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");
}
FreePool(Data);
if (EFI_ERROR(efi_status) && !EFI_ERROR(ret))
ret = efi_status;
} else if (!EFI_ERROR(ret)) {
ret = efi_status;
}
return efi_status;
}

256
tpm.c
View File

@ -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,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;
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;
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;
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,
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;
}

32
tpm.h
View File

@ -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