I finally managed to get tboot working in a way that makes sense to me. For those unfamiliar, tboot uses Intel's Trusted eXecution Technology to invoke a dynamic root of trust and then measure all the components you boot with (eg kernel and initrd). What took me ages to figure out was how one can upgrade kernels while keeping this chain working.
There are two policies in tboot: Launch Control Policy and Verified Launch Policy. LCP is handled by TXT automagically and is used to make sure the correct tboot and PCRs are set and either continues boot by executing tboot or resets the machine. VLP is where the kernel, initrd and any other components are measured and is handled by tboot after the SINIT has happened.
The trick I finally figured out is that the policy list needs to be signed with your own key. If the policy unsigned (unfortunately what most documentation shows :-() then tboot will verify the kernels and extend the sha1 hash of the policy file into PCR 18. This obviously causes problems when upgrading kernels because everything changes.
The difference with a signed policy is that instead of using the sha1 of the policy file itself, tboot will use the sha1 of the signed public key which is easily kept the same! hurrah! Now finally I can update kernels and PCR 18 is unchanged so there is no mess with migrating sealed secrets to new, probably unknown, PCRs.
I have extended genkernel to include all the tools needed to start tcsd and tpm_unsealdata and can successfully unseal a secret from the prompt within the initrd. Next I need to add the parts to automatically unseal and unlock the LUKS drive. There will be another post when that's all working.
Here is the Makefile that I used to output and setup my policies:
KVERS := $(shell readlink /usr/src/linux | sed 's/^linux-//')
CMDLINE_TBOOT := $(shell source /etc/default/grub-tboot; echo $${GRUB_CMDLINE_TBOOT})
all: list.pol list.data
clean:
rm -f list* mle* *.elt vl.pol pcrs.txt
# Launch Control Policy element for MLE
mle_hash: /boot/tboot.gz /etc/default/grub-tboot
lcp_mlehash -c "$(CMDLINE_TBOOT)" /boot/tboot.gz > mle_hash
mle.elt: mle_hash
lcp_crtpolelt --create --ctrl 0x01 --type mle --minver 17 --out mle.elt mle_hash
# LCP element for pcrs
pcrs.txt: /sys/class/tpm/tpm0/device/pcrs
egrep "^PCR-00" /sys/class/tpm/tpm0/device/pcrs > pcrs.txt
pconf.elt: pcrs.txt
lcp_crtpolelt --create --ctrl 0x01 --type pconf --out pconf.elt pcrs.txt
# Make list out of all the policy elements
list_unsig.lst: mle.elt pconf.elt vl.elt
lcp_crtpollist --create --out list_unsig.lst mle.elt pconf.elt vl.elt
# Sign the LCP so the pubkey is extended and NVRam is invariant
list_sig.lst: list_unsig.lst
cp list_unsig.lst list_sig.lst
lcp_crtpollist --sign --pub pubkey.pem --priv privkey.pem --out list_sig.lst
# Policy type lists need to have both .pol (in NVRAM) and data separately (loaded by grub)
list.data: list_sig.lst
lcp_crtpol2 --create --ctrl 0x00 --type list --pol list.pol --data list.data list_sig.lst
list.pol: list.data
# Verified Launch Policy for tboot to verify kernel+initrd
# ctrl 0x00 means it does not extend module 0 into pcr18
vl.pol: /boot/initramfs-genkernel-x86_64-$(KVERS) /boot/vmlinuz-$(KVERS)
tb_polgen --create --ctrl 0x00 --type continue vl.pol
tb_polgen --add --num 0 --pcr 19 --hash image --cmdline "root=/dev/mapper/root ro console=ttyS0,115200n8 console=tty0 intel_iommu=on noefi" --image /boot/vmlinuz-$(KVERS) vl.pol
tb_polgen --add --num 1 --pcr 20 --hash image --cmdline "" --image /boot/initramfs-genkernel-x86_64-$(KVERS) vl.pol
vl.elt: vl.pol
lcp_crtpolelt --create --ctrl 0x01 --type custom --out vl.elt --uuid tboot vl.pol
# define the indexes in the TPM
definenv:
# define the error locality
tpmnv_defindex -i 0x20000002 -s 8 -pv 0 -rl 0x07 -wl 0x07 -p $(PASSWORD) || true
# define the tboot verified launch policy locality
tpmnv_defindex -i 0x20000001 -s 512 -pv 0x02 -p $(PASSWORD) || true
# define the TXT launch control policy locality
tpmnv_defindex -i owner -s 0x36 -p $(PASSWORD) || true
# write everything to tpm and /boot
writelcp: list.pol
lcp_writepol -i owner -f list.pol -p $(PASSWORD)
writevlp: vl.pol
lcp_writepol -i 0x20000001 -f vl.pol -p $(PASSWORD)
install: writelcp
cp list.data /boot/list.data