heads/initrd/bin/kexec-seal-key

151 lines
5.2 KiB
Plaintext
Raw Normal View History

#!/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
TRACE "Under kexec-seal-key"
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"
else
DEBUG "Devices defined for disk encryption: $(cat "$KEY_DEVICES" | cut -d\ -f1)"
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"
else
DEBUG "No LVM volume group defined for activation"
fi
DEBUG "$(pcrs)"
Addition of qemu-(fb)whiptail-tpm2(-hotp) boards -coreboot support of TPM v2.0 (shared config for TPM2 support across all 4 previous variations) -swtpm set to be launched under TPM v2.0 mode under board config -Documentation file under each board.md softlinks to qemu-coreboot-fbwhiptail-tpm1.md (which has been generalized) This is skeleton for TPM v2 integration under Heads ------------- WiP TODO: - libcurl cannot be built as a tpm2-tools dependency as of now not sure why. curl currently needs to be added in board config to be built - Note: tpm-reset (master and here) needs some review, no handle of no tpm use case. Caller is responsible to not call it otherwise does nothing - init tries to bind fd and fails currently - Note: Check if whiptail is different of fbwhiptail in clearing screen. As of now every clear seems to be removed, still whiptail clears previous console output - When no OS' /boot can be mounted, do not try to TPM reset (will fail) - seal-hotpkey is not working properly - setting disk unlock key asks for TPM ownership passphrase (sealing in NV requires ownership, but text is misleading user as if reowning TPM) - We should cache input, feed tpm behind the scene and wipe passphrase and state clearly that this is TPM disk unlock kye passphrase. - primary key from TPM2 is invalid most of the time from kexec-select-boot and verifying global hashes but is setuped correctly at disk unlock key setup - would be nice to take advantage of bash function tracing to understand where we are for debugging purposes, code takes ash in consideration only - tpmr says it implements nv calls but actually doesn't. Removing those falsely wrapped functions would help. - Implementing them would be better - REVIEW TODOS IN CODE - READD CIRCLECI CONFIG Current state: - TPM unseal works without disk unlock key and generates TOTP properly (was missing die condition at unseal to not produce always good TOTP even if invalid) - TPM disk encryption key fails. Hypothesis is that sealing with USB drivers loaded and measures in inconsistent with sealed with/without. - TPM disk unsealing happens without USB modules being loaded in non-HOTP setup. This fails. - Current tests are with fbwhiptail (no clear called so having traces on command line of what happens) - Testing with HOTP implementation for sealing/unsealing since that forces USB module loads on each boot to remove this from failing possibilities
2022-08-25 18:43:31 +00:00
# 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/passphrase: " disk_password
echo -n "$disk_password" >"$RECOVERY_KEY"
echo
read -s -p "New TPM Disk Unlock Key passphrase for booting: " key_password
echo
read -s -p "Repeat TPM Disk Unlock Key passphrase for booting: " key_password2
echo
if [ "$key_password" != "$key_password2" ]; then
die "Key passphrases do not match"
fi
# Generate key file
echo "++++++ Generating new randomized 128 bytes key file that will be unsealed by TPM Disk Unlock Key passphrase"
dd \
if=/dev/urandom \
of="$KEY_FILE" \
bs=1 \
count=128 \
2>/dev/null ||
die "Unable to generate 128 random bytes"
# Count the number of slots used on each device
for dev in $(cat "$KEY_DEVICES" | cut -d\ -f1); do
DEBUG "Checking number of slots used on $dev"
#check if the device is a LUKS device with luks[1,2]
slots_used=$(cryptsetup luksDump $dev | grep -c 'luks[0-9]*' || die "Unable to get number of slots used on $dev")
DEBUG "Number of slots used on $dev: $slots_used"
# If slot1 is the only one used, warn and die with proper messages
if [ $slots_used -eq 1 ]; then
# Check if slot 1 is the only one existing
if cryptsetup luksDump $dev | grep -q "Slot 1: ENABLED"; then
warn "Slot 1 is the only one existing on $dev. Heads cannot use it to store TPM sealed LUKS Disk Unlock Key"
die "Slot 1 should not be the only one existing on $dev. Fix your custom setup"
fi
else
DEBUG "Slot 1 is not the only one existing on $dev"
DEBUG "$dev Slot 1 will be used to store LUKS Disk Unlock Key that will be sealed into TPM next"
fi
done
# Remove all the old keys from slot 1
for dev in $(cat "$KEY_DEVICES" | cut -d\ -f1); do
echo "++++++ $dev: Removing old key slot 1"
cryptsetup luksKillSlot \
--key-file "$RECOVERY_KEY" \
$dev 1 ||
warn "$dev: removal of key in slot 1 failed: might not exist. Continuing"
echo "++++++ $dev: Adding key to slot 1"
cryptsetup luksAddKey \
--key-file "$RECOVERY_KEY" \
--key-slot 1 \
$dev "$KEY_FILE" ||
die "$dev: Unable to add key to slot 1"
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"
pcrf="/tmp/secret/pcrf.bin"
tpmr pcrread 0 "$pcrf"
tpmr pcrread -a 1 "$pcrf"
tpmr pcrread -a 2 "$pcrf"
tpmr pcrread -a 3 "$pcrf"
# Note that PCR 4 needs to be set with the "normal-boot" path value, read it from event log.
tpmr calcfuturepcr 4 >>"$pcrf"
if [ "$CONFIG_USB_KEYBOARD" = "y" -o -r /lib/modules/libata.ko -o -x /bin/hotp_verification ]; then
DEBUG "Sealing TPM Disk Unlock key with PCR5 involvement (additional kernel modules are loaded per board config)..."
# Here, we take pcr 5 into consideration if modules are expected to be measured+loaded
tpmr pcrread -a 5 "$pcrf"
else
DEBUG "Sealing TPM Disk Unlock Key with PCR5=0 (NO additional kernel modules are loaded per board config)..."
#no kernel modules are expected to be measured+loaded
tpmr calcfuturepcr 5 >>"$pcrf"
fi
# Precompute the value for pcr 6
DEBUG "Precomputing TPM future value for PCR6 sealing/unsealing of TPM Disk Unlock Key..."
tpmr calcfuturepcr 6 "/tmp/luksDump.txt" >>"$pcrf"
# We take into consideration user files in cbfs
tpmr pcrread -a 7 "$pcrf"
DO_WITH_DEBUG --mask-position 7 \
tpmr seal "$KEY_FILE" "$TPM_INDEX" 0,1,2,3,4,5,6,7 "$pcrf" \
"$TPM_SIZE" "$key_password"
# should be okay if this fails
shred -n 10 -z -u "$pcrf" 2>/dev/null ||
warn "Failed to delete pcrf file - continuing"
shred -n 10 -z -u "$KEY_FILE" 2>/dev/null ||
warn "Failed to delete key file - continuing"
mount -o rw,remount $paramsdir || die "Failed to remount $paramsdir in RW - continuing"
cp -f /tmp/luksDump.txt "$paramsdir/kexec_lukshdr_hash.txt" ||
die "Failed to copy LUKS header hashes to /boot - continuing"
mount -o ro,remount $paramsdir || die "Failed to remount $paramsdir in RO - continuing"