Skip to content

Commit

Permalink
qubes init script and improved TPM disk encryption with LUKS headers …
Browse files Browse the repository at this point in the history
…(issue #123 and #6)
  • Loading branch information
osresearch committed Apr 2, 2017
1 parent d06ba0a commit f99944a
Show file tree
Hide file tree
Showing 7 changed files with 163 additions and 20 deletions.
7 changes: 7 additions & 0 deletions config/x230-qubes.config
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Configuration for a x230 running Qubes OS
BOARD=x230

CONFIG_CRYPTSETUP=y
CONFIG_FLASHROM=y
CONFIG_GPG=y
Expand All @@ -15,3 +16,9 @@ CONFIG_XEN=y

CONFIG_LINUX_USB=y
CONFIG_LINUX_E1000E=y

CONFIG_BOOTSCRIPT=/bin/qubes-init

# Disks encrypted by the TPM LUKS key
CONFIG_QUBES_BOOT_DEV="/dev/sda1"
CONFIG_QUBES_DEVS="/dev/sda2 /dev/sda3 /dev/sda5"
18 changes: 18 additions & 0 deletions initrd/bin/mount-usb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/sh
# Mount a USB device
die() { echo >&2 "!!!!! $@"; exit 1; }

if ! lsmod | grep -q ehci_hcd; then
insmod /lib/modules/ehci-hcd.ko \
|| die "ehci_hcd: module load failed"
fi
if ! lsmod | grep -q ehci_pci; then
insmod /lib/modules/ehci-pci.ko \
|| die "ehci_pci: module load failed"
fi

if [ ! -d /media ]; then
mkdir /media
fi

mount -o ro $1 /media
65 changes: 65 additions & 0 deletions initrd/bin/qubes-init
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/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.

recovery() {
echo >&2 "!!!!! $@"
rm -f /tmp/secret.key
tpm extend -ix 4 -if recovery

echo >&2 "!!!!! Starting recovery shell"
exec /bin/ash
}

. /config

# TODO: Allow /boot to be encrypted?
# This would require a different TPM key or a user
# passphrase to decrypt it.
mount -o ro "$CONFIG_QUBES_BOOT_DEV" /boot \
|| recovery '$CONFIG_BOOT_DEV: Unable to mount /boot'

# TODO: Allow these to be specified on the /boot device
XEN=/boot/xen-4.6.3.heads
INITRD=/boot/initramfs-4.4.31-11.pvops.qubes.x86_64.img
KERNEL=/boot/vmlinuz-4.4.31-11.pvops.qubes.x86_64

echo "+++ Checking $XEN"
gpgv "${XEN}.asc" "${XEN}" \
|| recovery 'Xen signature failed'

echo "+++ Checking $INITRD"
gpgv "${INITRD}.asc" "${INITRD}" \
|| recovery 'Initrd signature failed'

echo "+++ Checking $KERNEL"
gpgv "${KERNEL}.asc" "${KERNEL}" \
|| recovery 'Kernel signature failed'

# Measure the LUKS headers before we unseal the disk key
/bin/qubes-measure-luks $CONFIG_QUBES_DEVS \
|| recovery "LUKS measure failed"

# Attempt to unseal the disk key from the TPM
# should we give this some number of tries?
unseal-key \
|| recovery 'Unseal disk key failed. Starting recovery shell'

# command line arguments are in the hash, so they are "correct".
kexec \
-l \
--module "${KERNEL} root=LABEL=root rhgb" \
--module "${INITRD}" \
--command-line "no-real-mode reboot=no console=vga dom0_mem=min:1024M dom0_mem=max:4096M" \
"${XEN}" \
|| recovery "kexec load failed"

# Last step is to override PCR 6 so that user can't read the key
tpm extend -ix 4 -ic qubes \
|| recovery 'Unable to scramble PCR'

echo "+++ Starting Qubes..."
exec kexec -e
14 changes: 14 additions & 0 deletions initrd/bin/qubes-measure-luks
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh
# Measure all of the luks disk encryption headers into
# a PCR so that we can detect disk swap attacks.

die() { echo >&2 "$@"; exit 1; }

# Measure the luks headers into PCR 6
for dev in "$@"; do
cryptsetup luksDump $dev \
|| die "$dev: Unable to measure"
done > /tmp/luksDump.txt

tpm extend -ix 6 -if /tmp/luksDump.txt \
|| die "Unable to extend PCR"
67 changes: 53 additions & 14 deletions initrd/bin/seal-key
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,36 @@ TPM_INDEX=3
TPM_SIZE=312
KEY_FILE=/tmp/secret.key

die() { echo >&2 "$@"; exit 1; }
. /config

die() {
echo >&2 "$@";
rm -f /tmp/secret.key /tmp/recovery.key /tmp/sealed
exit 1;
}
warn() { echo >&2 "$@"; }

read -s -p "New key password: " key_password
# Key slot 0 is the manual recovery pass phrase
# that they user entered when they installed Qubes,
# key slot 1 is the one that we've generated.
read -s -p "Enter disk recovery key: " disk_password
echo -n "$disk_password" > /tmp/recovery.key
echo

for dev in $CONFIG_QUBES_DEVS; do
echo "++++++ $dev: Removing old key slot"
cryptsetup luksKillSlot \
--key-file /tmp/recovery.key \
$dev 1 \
|| warn "$dev: ignoring problem"
done

read -s -p "New disk decryption password for booting: " key_password
echo
read -s -p "Repeat password: " key_password2
echo

if [ "$key_password" -ne "$key_password2" ]; then
if [ "$key_password" != "$key_password2" ]; then
die "Key passwords do not match"
fi

Expand All @@ -27,11 +48,28 @@ dd \
2>/dev/null \
|| die "Unable to generate 128 random bytes"


# Use the current values of the PCRs, which will be read
for dev in $CONFIG_QUBES_DEVS; do
echo "+++++ $dev: Adding key"
cryptsetup luksAddKey \
--key-file /tmp/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
/bin/qubes-measure-luks $CONFIG_QUBES_DEVS \
|| die "Unable to measure the LUKS headers"

# 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 recovery shell.
# 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").
# should this read the storage root key?
sealfile2 \
tpm sealfile2 \
-if "$KEY_FILE" \
-of /tmp/sealed \
-pwdd "$key_password" \
Expand All @@ -40,35 +78,36 @@ sealfile2 \
-ix 1 X \
-ix 2 X \
-ix 3 X \
-ix 4 X \
-ix 4 f8fa3b6e32e7c6fe04c366e74636e505b28f3b0d \
-ix 5 0000000000000000000000000000000000000000 \
-ix 6 X \
|| die "Unable to seal secret"

rm "$KEY_FILE"
rm -f "$KEY_FILE"


# 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
physicalpresence -s \
tpm physicalpresence -s \
|| warn "Warning: Unable to assert physical presence"

read -s -p "TPM Owner password: " tpm_password
echo

nv_definespace \
tpm nv_definespace \
-in $TPM_INDEX \
-sz $TPM_SIZE \
-pwdo "$tpm_password" \
-per 0 \
|| die "Warning: Unable to define NVRAM space; trying anyway"
|| warn "Warning: Unable to define NVRAM space; trying anyway"


nv_writevalue \
tpm nv_writevalue \
-in $TPM_INDEX \
-if /tmp/sealed \
|| die "Unable to write sealed secret to NVRAM"

rm /tmp/sealed

10 changes: 5 additions & 5 deletions initrd/bin/unseal-key
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,22 @@ if [ -z "$key_file" ]; then
key_file=/tmp/secret.key
fi

read -s -p "Encryption password: " tpm_password
read -s -p "Disk encryption password: " tpm_password
echo

nv_readvalue \
tpm nv_readvalue \
-in "$TPM_INDEX" \
-sz "$TPM_SIZE" \
-of /tmp/sealed \
|| die "Unable to read key from TPM NVRAM"

unsealfile \
tpm unsealfile \
-if /tmp/sealed \
-of "$key_file" \
-pwdd "$tpm_password" \
-hk 40000000 \
|| die "Unable to unseal disk encryption key"

rm /tmp/sealed

rm -f /tmp/sealed

exit 0
2 changes: 1 addition & 1 deletion initrd/init
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/ash
# First thing it is vital to mount the /dev and other system directories
mkdir /proc /sys /dev /tmp /boot 2>&- 1>&-
mkdir /proc /sys /dev /tmp /boot /media 2>&- 1>&-
mount -t devtmpfs none /dev
mount -t proc none /proc
mount -t sysfs none /sys
Expand Down

0 comments on commit f99944a

Please sign in to comment.