heads/initrd/bin/qubes-init
Trammell Hudson 353a0efe6f
Rework /init and qubes setup scripts (issue #27, #155, #32, #29, #110)
This adds support for seamless booting of Qubes with a TPM disk key,
as well as signing of qubes files in /boot with a Yubikey.

The signed hashes also includes a TPM counter, which is incremented
when new hashes are signed.  This prevents rollback attacks against
the /boot filesystem.

The TPMTOTP value is presented to the user at the time of entering
the disk encryption keys.  Hitting enter will generate a new code.

The LUKS headers are included in the TPM sealing of the disk
encryption keys.
2017-04-12 06:57:58 -04:00

84 lines
2.6 KiB
Bash
Executable File

#!/bin/sh
# Boot a Qubes installation that has already been setup.
# This depends on the PCR 4 being "normal-boot":
# f8fa3b6e32e7c6fe04c366e74636e505b28f3b0d
# which is only set if the top level /init script has started
# without user intervention or dropping into a recovery shell.
. /etc/functions
. /etc/config
if [ "$1" = "recovery" ]; then
warn "Recovery mode boot; ignoring key failures"
RECOVERY=1
fi
# TODO: Allow /boot to be encrypted?
# This would require a different TPM key, a user passphrase to decrypt it,
# or loading the USB modules to talk to a Yubikey to get the thing.
if ! grep -q /boot /proc/mounts ; then
mount -o ro "$CONFIG_QUBES_BOOT_DEV" /boot \
|| recovery '$CONFIG_BOOT_DEV: Unable to mount /boot'
fi
BOOT_HASHES=/boot/boot.hashes
if [ ! -r "$BOOT_HASHES" ]; then
recovery "$BOOT_HASHES does not exist; re-run qubes-update"
fi
# Verify the signature on the hashes
gpgv "$BOOT_HASHES.asc" "$BOOT_HASHES" \
|| recovery 'boot hashes signature failed'
# Retrieve the TPM counter ID and generate its current value
TPM_COUNTER=`grep counter $BOOT_HASHES | cut -d- -f2`
if [ -z "$TPM_COUNTER" ]; then
recovery "$BOOT_HASHES: TPM counter not found?"
fi
tpm counter_read -ix "$TPM_COUNTER" | tee "/tmp/counter-$TPM_COUNTER"
# Check the hashes of all the files
sha256sum -c "$BOOT_HASHES" \
|| recovery "$BOOT_HASHES: hash mismatch"
XEN=`grep /boot/xen $BOOT_HASHES | cut -d\ -f3 | tail -1`
KERNEL=`grep /boot/vmlin $BOOT_HASHES | cut -d\ -f3 | tail -1`
INITRD=`grep /boot/initram $BOOT_HASHES | cut -d\ -f3 | tail -1`
# Activate the dom0 group
lvm vgchange -a y "$CONFIG_QUBES_VG" \
|| recovery "$CONFIG_QUBES_VG: LVM volume group activate failed"
# Measure the LUKS headers before we unseal the disk key
qubes-measure-luks /dev/$CONFIG_QUBES_VG/* \
|| recovery "LUKS measure failed"
# Unpack the initrd and fixup the /etc/crypttab
# this is a hack to split it into two parts since
# we know that the first 0x3400 bytes are the microcode
INITRD_DIR=/tmp/secret/initrd
SECRET_CPIO=/tmp/secret/initrd.cpio
mkdir -p "$INITRD_DIR/etc"
# Attempt to unseal the disk key from the TPM
# should we give this some number of tries?
if ! unseal-key "$INITRD_DIR/secret.key" ; then
warn 'Unseal disk key failed'
if [ -z "$RECOVERY" ]; then
recovery 'Starting recovery shell'
fi
fi
# Override PCR 4 so that user can't read the key
tpm extend -ix 4 -ic qubes \
|| recovery 'Unable to scramble PCR'
echo '+++ Building initrd'
( cd "$INITRD_DIR" ; find . | cpio -H newc -o ) > "$SECRET_CPIO"
cat "$INITRD" >> "$SECRET_CPIO"
/bin/qubes-boot "$XEN" "$KERNEL" "$SECRET_CPIO"
recovery "Something failed..."