heads/initrd/bin/kexec-seal-key
Jonathon Hall b500505312
tpm2-tools: Change sense of CONFIG_TPM to mean any TPM, not just TPM1.
Most logic throughout Heads doesn't need to know TPM1 versus TPM2 (and
shouldn't, the differences should be localized).  Some checks were
incorrect and are fixed by this change.  Most checks are now unchanged
relative to master.

There are not that many places outside of tpmr that need to
differentiate TPM1 and TPM2.  Some of those are duplicate code that
should be consolidated (seal-hotpkey, unseal-totp, unseal-hotp), and
some more are probably good candidates for abstracting in tpmr so the
business logic doesn't have to know TPM1 vs. TPM2.

Previously, CONFIG_TPM could be variously 'y', 'n', or empty.  Now it
is always 'y' or 'n', and 'y' means "any TPM".  Board configs are
unchanged, setting CONFIG_TPM2_TOOLS=y implies CONFIG_TPM=y so this
doesn't have to be duplicated and can't be mistakenly mismatched.

There were a few checks for CONFIG_TPM = n that only coincidentally
worked for TPM2 because CONFIG_TPM was empty (not 'n').  This test is
now OK, but the checks were also cleaned up to '!= "y"' for robustness.

Signed-off-by: Jonathon Hall <jonathon.hall@puri.sm>
2023-03-08 12:45:46 -05:00

228 lines
6.9 KiB
Bash
Executable File

#!/bin/bash
# This will generate a disk encryption key and seal / ecncrypt
# with the current PCRs and then store it in the TPM NVRAM.
# It will then need to be bundled into initrd that is booted.
set -e -o pipefail
. /etc/functions
TPM_INDEX=3
TPM_SIZE=312
KEY_FILE="/tmp/secret/secret.key"
TPM_SEALED="/tmp/secret/secret.sealed"
RECOVERY_KEY="/tmp/secret/recovery.key"
. /etc/functions
. /tmp/config
paramsdir=$1
if [ -z "$paramsdir" ]; then
die "Usage $0 /boot"
fi
KEY_DEVICES="$paramsdir/kexec_key_devices.txt"
KEY_LVM="$paramsdir/kexec_key_lvm.txt"
if [ ! -r "$KEY_DEVICES" ]; then
die "No devices defined for disk encryption"
fi
if [ -r "$KEY_LVM" ]; then
# Activate the LVM volume group
VOLUME_GROUP=`cat $KEY_LVM`
if [ -z "$VOLUME_GROUP" ]; then
die "No LVM volume group defined for activation"
fi
lvm vgchange -a y $VOLUME_GROUP \
|| die "$VOLUME_GROUP: unable to activate volume group"
fi
echo "DEBUG: CONFIG_TPM: $CONFIG_TPM"
echo "DEBUG: CONFIG_TPM2_TOOLS: $CONFIG_TPM2_TOOLS"
echo "DEBUG: Show PCRs"
pcrs
# LUKS Key slot 0 is the manual recovery pass phrase
# that they user entered when they installed OS,
# key slot 1 is the one that we've generated.
read -s -p "Enter disk recovery key: " disk_password
echo -n "$disk_password" > "$RECOVERY_KEY"
echo
if [ "$CONFIG_AUTO_UNLOCK" != y ]; then
read -s -p "New disk unlock password for booting: " key_password
echo
read -s -p "Repeat unlock code: " key_password2
echo
if [ "$key_password" != "$key_password2" ]; then
die "Key passwords do not match"
fi
fi
# Generate key file
dd \
if=/dev/urandom \
of="$KEY_FILE" \
bs=1 \
count=128 \
2>/dev/null \
|| die "Unable to generate 128 random bytes"
# Remove all the old keys from slot 1
for dev in `cat "$KEY_DEVICES" | cut -d\ -f1`; do
echo "++++++ $dev: Removing old key slot"
cryptsetup luksKillSlot \
--key-file "$RECOVERY_KEY" \
$dev 1 \
|| warn "$dev: ignoring problem"
echo "++++++ $dev: Adding key"
cryptsetup luksAddKey \
--key-file "$RECOVERY_KEY" \
--key-slot 1 \
$dev "$KEY_FILE" \
|| die "$dev: Unable to add key"
done
# Now that we have setup the new keys, measure the PCRs
# We don't care what ends up in PCR 6; we just want
# to get the /tmp/luksDump.txt file. We use PCR16
# since it should still be zero
cat "$KEY_DEVICES" | cut -d\ -f1 | xargs /bin/qubes-measure-luks \
|| die "Unable to measure the LUKS headers"
if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM2_TOOLS" != "y" ]; then
luks_pcr=`tpm calcfuturepcr -ix 16 -if /tmp/luksDump.txt`
# HOTP USB Secrity Dongle loads USB modules which changes PCR5.
# In the event HOTP USB Security Dongle is enabled, skip verification of PCR5
# If modules should be loaded during normal boot, skip verification of PCR5
#
#TODO: check condition, no libdata.ko
if [ "$CONFIG_USB_KEYBOARD" = "y" -o -r /lib/modules/libata.ko -o -x /bin/hotp_verification ]; then
pcr_5="X"
else
pcr_5="0000000000000000000000000000000000000000"
fi
# Note that PCR 4 needs to be set with the "normal-boot"
# path value, which we do not have right now since we are
# in a "init" mode
# used to be -ix 4 f8fa3b6e32e7c6fe04c366e74636e505b28f3b0d \
# now just all zeros in a normal boot
# PCR 5 must be all zero since no kernel modules should have
# been loaded during a normal boot, but might have been
# loaded in the recovery shell.
# Otherwise use the current values of the PCRs, which will be read
# from the TPM as part of the sealing ("X").
if [ "$CONFIG_AUTO_UNLOCK" != y ]; then
tpm sealfile2 \
-if "$KEY_FILE" \
-of "$TPM_SEALED" \
-pwdd "$key_password" \
-hk 40000000 \
-ix 0 X \
-ix 1 X \
-ix 2 X \
-ix 3 X \
-ix 4 0000000000000000000000000000000000000000 \
-ix 5 $pcr_5 \
-ix 6 $luks_pcr \
-ix 7 X
else
tpm sealfile2 \
-if "$KEY_FILE" \
-of "$TPM_SEALED" \
-hk 40000000 \
-ix 0 X \
-ix 1 X \
-ix 2 X \
-ix 3 X \
-ix 4 0000000000000000000000000000000000000000 \
-ix 5 $pcr_5 \
-ix 6 $luks_pcr \
-ix 7 X
fi
if [ $? -ne 0 ]; then
die "Unable to seal secret"
fi
shred -n 10 -z -u "$KEY_FILE" 2> /dev/null \
|| die "Failed to delete key file"
# try it without the owner password first
if ! tpm nv_writevalue \
-in $TPM_INDEX \
-if "$TPM_SEALED" \
; then
# to create an nvram space we need the TPM owner password
# and the TPM physical presence must be asserted.
#
# The permissions are 0 since there is nothing special
# about the sealed file
tpm physicalpresence -s \
|| warn "Warning: Unable to assert physical presence"
read -s -p "TPM Owner password: " tpm_password
echo
tpm nv_definespace \
-in $TPM_INDEX \
-sz $TPM_SIZE \
-pwdo "$tpm_password" \
-per 0 \
|| warn "Warning: Unable to define NVRAM space; trying anyway"
tpm nv_writevalue \
-in $TPM_INDEX \
-if "$TPM_SEALED" \
|| die "Unable to write sealed secret to NVRAM"
fi
elif [ "$CONFIG_TPM2_TOOLS" = "y" ]; then
pcrf="/tmp/secret/pcrf.bin"
#TODO: verify logic below against tpm2
tpm2 pcrread -o "$pcrf" sha256:0,1,2,3
# pcr 4 is expected to be zero (init mode)
dd if=/dev/zero bs=32 count=1 >> "$pcrf"
if [ "$CONFIG_USB_KEYBOARD" = "y" -o -r /lib/modules/libata.ko -o -x /bin/hotp_verification ]; then
# Here, we take pcr 5 into consideration if modules are expected to be measured+loaded
#
# binary pcr dump can only go via stderr for redirection
# sadly this busybox has buggy support for "2>>" and "2|"
# so we need workaround...
tpm2 pcrread -o /dev/stderr sha256:5 2>&1 >/dev/console | cat >> "$pcrf"
else
#no kernel modules are expected to be measured+loaded
dd if=/dev/zero bs=32 count=1 >> "$pcrf"
fi
# Use pcr 23 to precompute the value for pcr 6
tpmr extend -ix 23 -if /tmp/luksDump.txt
tpm2 pcrread -o /dev/stderr sha256:23 2>&1 >/dev/console | cat >> "$pcrf"
#TODO: delete the following pcrs output on screen
# goal is to validate that what is in pcr 23 is at pcr 6 at unseal
pcrs
tpm2 pcrreset 23
# We take into consideration user files in cbfs
tpm2 pcrread -o /dev/stderr sha256:7 2>&1 >/dev/console | cat >> "$pcrf"
if [ "$CONFIG_AUTO_UNLOCK" = "y" ]; then
#TODO: There is no CONFIG_AUTO_UNLOCK. Should be removed
tpmr seal "$KEY_FILE" "0x8100000$TPM_INDEX" sha256:0,1,2,3,4,5,6,7 "$pcrf"
else
#TODO: wrap TPM disk encryption key passphrase, otherwise prompt to user looks like if we were asking for TPM ownership passphrase
#TODO: everything is supposed to be under $pcrf, why considering them twice?
# TODO: review syntax to not duplicate expending pcr 2 times with pcr0-7: find a way to only use $pcrf? : sha256 "$pcrf" "$key_password"
tpmr seal "$KEY_FILE" "0x8100000$TPM_INDEX" sha256:0,1,2,3,4,5,6,7 "$pcrf" "$key_password"
fi
if [ $? -eq 0 ]; then
# should be okay if this fails
shred -n 10 -z -u "$pcrf".* 2> /dev/null || true
fi
fi
shred -n 10 -z -u "$TPM_SEALED" 2> /dev/null \
|| warn "Failed to delete the sealed secret - continuing"
cp /tmp/luksDump.txt "$paramsdir/kexec_lukshdr_hash.txt" \
|| warn "Failed to have hashes of LUKS header - continuing"