From 4910c1188fc9c5f1db839a8bfd52dcf980e3b25c Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Tue, 22 Aug 2023 14:34:29 -0400 Subject: [PATCH] TPM Disk Unlock Key sealing/renewal cleanup (Triggered automatically when resealing TOTP) Changes: - As per master: when TOTP cannot unseal TOTP, user is prompted to either reset or regenerate TOTP - Now, when either is done and a previous TPM Disk Unlock Key was setuped, the user is guided into: - Regenerating checksums and signing them - Regenerating TPM disk Unlock Key and resealing TPM disk Unlock Key with passphrase into TPM - LUKS header being modified, user is asked to resign kexec.sig one last time prior of being able to default boot - When no previous Disk Unlock Key was setuped, the user is guided into: - The above, plus - Detection of LUKS containers,suggesting only relevant partitions - Addition of TRACE and DEBUG statements to troubleshoot actual vs expected behavior while coding - Were missing under TPM Disk Unlock Key setup codepaths - Fixes for #645 : We now check if only one slots exists and we do not use it if its slot1. - Also shows in DEBUG traces now Unrelated staged changes - ash_functions: warn and die now contains proper spacing and eye attaction - all warn and die calls modified if containing warnings and too much punctuation - unify usage of term TPM Disk Unlock Key and Disk Recovery Key --- FAQ.md | 2 +- initrd/bin/flashrom-kgpe-d16-openbmc.sh | 2 +- initrd/bin/gui-init | 10 +- initrd/bin/kexec-boot | 4 +- initrd/bin/kexec-save-default | 177 +++++++++++++++++------- initrd/bin/kexec-save-key | 17 ++- initrd/bin/kexec-seal-key | 86 ++++++++---- initrd/bin/kexec-select-boot | 4 +- initrd/bin/kexec-unseal-key | 2 +- initrd/bin/seal-hotpkey | 2 +- initrd/bin/tpmr | 8 +- initrd/etc/ash_functions | 4 +- initrd/etc/functions | 30 ++++ initrd/etc/luks-functions | 1 - 14 files changed, 255 insertions(+), 94 deletions(-) diff --git a/FAQ.md b/FAQ.md index 7e76a8dc..4a175e3b 100644 --- a/FAQ.md +++ b/FAQ.md @@ -110,7 +110,7 @@ to deceive you and steal your login password? Maybe! It wouldn't get your disk password, which is perhaps an improvement. -Disk key in TPM or user passphrase? +Disk key in TPM (TPM Disk Unlock Key) or user passphrase? --- Depends on your threat model. With the disk key in the TPM an attacker would need to have the entire machine (or a backdoor in the TPM) diff --git a/initrd/bin/flashrom-kgpe-d16-openbmc.sh b/initrd/bin/flashrom-kgpe-d16-openbmc.sh index 4c860d80..85108d9f 100755 --- a/initrd/bin/flashrom-kgpe-d16-openbmc.sh +++ b/initrd/bin/flashrom-kgpe-d16-openbmc.sh @@ -14,5 +14,5 @@ sha256sum /tmp/kgpe-d16-openbmc.rom flashrom --programmer="ast1100:spibus=2,cpu=reset" -c "S25FL128P......0" -w /tmp/kgpe-d16-openbmc.rom \ || die "$ROM: Flash failed" -warn "Reboot and hopefully it works..." +warn "Reboot and hopefully it works" exit 0 diff --git a/initrd/bin/gui-init b/initrd/bin/gui-init index 58ff4a7b..64f7563f 100755 --- a/initrd/bin/gui-init +++ b/initrd/bin/gui-init @@ -173,7 +173,8 @@ generate_totp_hotp() # clear screen printf "\033c" else - warn "Unsealing TOTP/HOTP secret from previous sealed measurements failed. Try "Generate new HOTP/TOTP secret" option if you updated firmware content." + warn "Unsealing TOTP/HOTP secret from previous sealed measurements failed" + warn "Try "Generate new HOTP/TOTP secret" option if you updated firmware content" fi } @@ -229,6 +230,7 @@ update_totp() if (whiptail $BG_COLOR_WARNING --title 'Generate new TOTP/HOTP secret' \ --yesno "This will erase your old secret and replace it with a new one!\n\nDo you want to proceed?" 0 80) then generate_totp_hotp && update_totp && BG_COLOR_MAIN_MENU="" + reseal_tpm_disk_decryption_key fi ;; i ) @@ -237,6 +239,7 @@ update_totp() ;; p ) reset_tpm && update_totp && BG_COLOR_MAIN_MENU="" + reseal_tpm_disk_decryption_key ;; x ) recovery "User requested recovery shell" @@ -298,6 +301,7 @@ update_hotp() if (whiptail $BG_COLOR_WARNING --title 'Generate new TOTP/HOTP secret' \ --yesno "This will erase your old secret and replace it with a new one!\n\nDo you want to proceed?" 0 80) then generate_totp_hotp && BG_COLOR_MAIN_MENU="" + reseal_tpm_disk_decryption_key fi ;; i ) @@ -523,9 +527,11 @@ show_tpm_totp_hotp_options_menu() case "$option" in g ) generate_totp_hotp + reseal_tpm_disk_decryption_key ;; r ) reset_tpm + reseal_tpm_disk_decryption_key ;; t ) prompt_totp_mismatch @@ -571,8 +577,10 @@ reset_tpm() # now that the TPM is reset, remove invalid TPM counter files mount_boot mount -o rw,remount /boot + warn "Removing rollback and primary handle hash under /boot" rm -f /boot/kexec_rollback.txt rm -f /boot/kexec_primhdl_hash.txt + #TODO: When primhdl_hash is gone but not recreated and signed: fail at TPM Disk Unlock Key for TPM2.... # create Heads TPM counter before any others check_tpm_counter /boot/kexec_rollback.txt "" "$key_password" \ diff --git a/initrd/bin/kexec-boot b/initrd/bin/kexec-boot index 9cfc6553..d112c791 100755 --- a/initrd/bin/kexec-boot +++ b/initrd/bin/kexec-boot @@ -158,7 +158,9 @@ if [ "$CONFIG_DEBUG_OUTPUT" = "y" ];then #Repeat kexec command that will be executed since in debug DEBUG "kexeccmd= $kexeccmd" - read -n 1 -p "[DEBUG] Continue booting? [Y/n]: " debug_boot_confirm + #Ask user if they want to continue booting without echoing back the input (-s) + read -s -n 1 -p "[DEBUG] Continue booting? [Y/n]: " debug_boot_confirm + echo if [ "${debug_boot_confirm^^}" = N ]; then # abort die "Boot aborted" diff --git a/initrd/bin/kexec-save-default b/initrd/bin/kexec-save-default index 559813b0..bc2febcc 100755 --- a/initrd/bin/kexec-save-default +++ b/initrd/bin/kexec-save-default @@ -8,10 +8,10 @@ TRACE "Under /bin/kexec-save-default" while getopts "b:d:p:i:" arg; do case $arg in - b) bootdir="$OPTARG" ;; - d) paramsdev="$OPTARG" ;; - p) paramsdir="$OPTARG" ;; - i) index="$OPTARG" ;; + b) bootdir="$OPTARG" ;; + d) paramsdev="$OPTARG" ;; + p) paramsdir="$OPTARG" ;; + i) index="$OPTARG" ;; esac done @@ -35,21 +35,53 @@ TMP_MENU_FILE="/tmp/kexec/kexec_menu.txt" ENTRY_FILE="$paramsdir/kexec_default.$index.txt" HASH_FILE="$paramsdir/kexec_default_hashes.txt" PRIMHASH_FILE="$paramsdir/kexec_primhdl_hash.txt" +KEY_DEVICES="$paramsdir/kexec_key_devices.txt" +KEY_LVM="$paramsdir/kexec_key_lvm.txt" + + +lvm_suggest=$(lvm vgscan | awk -F '"' {'print $1'} | tail -n +2) +num_lvm=$(echo "$lvm_suggest" | wc -l) +if [ "$num_lvm" -eq 1 ] && [ -n "$lvm_suggest" ]; then + lvm_volume_group="$lvm_suggest" +elif [ -z "$lvm_suggest" ]; then + num_lvm=0 +fi +DEBUG "LVM num_lvm: $num_lvm, lvm_suggest: $lvm_suggest" + +# get all LUKS container devices +devices_suggest=$(blkid | cut -d ':' -f 1 | while read device; do + cryptsetup isLuks "$device" + if [ $(echo $?) == 0 ]; then echo "$device"; fi +done | sort) +num_devices=$(echo "$devices_suggest" | wc -l) + +if [ "$num_devices" -eq 1 ] && [ -s "$devices_suggest" ]; then + key_devices=$devices_suggest +else + devices_suggest=$(echo $devices_suggest) +fi +DEBUG "LUKS num_devices: $num_devices, devices_suggest: $devices_suggest" + +if [ "$num_lvm" -eq 0 ] && [ "$num_devices" -eq 0 ]; then + #No encrypted partition found. + no_encrypted_partition=1 +fi if [ ! -r "$TMP_MENU_FILE" ]; then die "No menu options available, please run kexec-select-boot" fi -entry=`head -n $index $TMP_MENU_FILE | tail -1` +entry=$(head -n $index $TMP_MENU_FILE | tail -1) if [ -z "$entry" ]; then die "Invalid menu index $index" fi -KEY_DEVICES="$paramsdir/kexec_key_devices.txt" -KEY_LVM="$paramsdir/kexec_key_lvm.txt" save_key="n" if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ "$CONFIG_BASIC" != y ]; then + DEBUG "TPM is enabled and TPM_NO_LUKS_DISK_UNLOCK is not set" + DEBUG "Checking if a a TPM Disk Unlock Key was previously setuped from $KEY_DEVICES" if [ ! -r "$KEY_DEVICES" ]; then + DEBUG "No previous TPM Disk Unlock Key was setuped for LUKS devices, confirming to add a disk encryption to the TPM" read \ -n 1 \ -p "Do you wish to add a disk encryption to the TPM [y/N]: " \ @@ -58,11 +90,11 @@ if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ if [ "$add_key_confirm" = "y" \ -o "$add_key_confirm" = "Y" ]; then - lvm_suggest="e.g. qubes_dom0 or blank" - devices_suggest="e.g. /dev/sda2 or blank" + DEBUG "User confirmed to add a disk encryption to the TPM" save_key="y" fi else + DEBUG "Previous TPM Disk Unlock Key was setuped for LUKS devices" read \ -n 1 \ -p "Do you want to reseal a disk key to the TPM [y/N]: " \ @@ -73,36 +105,85 @@ if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ -o "$change_key_confirm" = "Y" ]; then old_lvm_volume_group="" if [ -r "$KEY_LVM" ]; then - old_lvm_volume_group=`cat $KEY_LVM` || true - old_key_devices=`cat $KEY_DEVICES \ - | cut -d\ -f1 \ - | grep -v "$old_lvm_volume_group" \ - | xargs` || true + old_lvm_volume_group=$(cat $KEY_LVM) || true + old_key_devices=$(cat $KEY_DEVICES | + cut -d\ -f1 | + grep -v "$old_lvm_volume_group" | + xargs) || true else - old_key_devices=`cat $KEY_DEVICES \ - | cut -d\ -f1 | xargs` || true + old_key_devices=$(cat $KEY_DEVICES | + cut -d\ -f1 | xargs) || true fi - lvm_suggest="was '$old_lvm_volume_group'" - devices_suggest="was '$old_key_devices'" + lvm_suggest="$old_lvm_volume_group" + devices_suggest="$old_key_devices" save_key="y" fi fi if [ "$save_key" = "y" ]; then - echo "+++ LVM volume groups (lvm vgscan): " - lvm vgscan || true + if [ -n "$old_key_devices" ] || [ -n "$old_lvm_volume_group" ]; then + DEBUG "Previous TPM Disk Unlock Key was setuped for LUKS devices $old_key_devices $old_lvm_volume_group" + read \ + -n 1 \ + -p "Do you want to reuse configured Encrypted LVM groups/Block devices? (Y/n):" \ + reuse_past_devices + echo + if [ "$reuse_past_devices" = "y" ] || [ "$reuse_past_devices" = "Y" ] || [ -z "$reuse_past_devices" ]; then + if [ -z "$key_devices" ] && [ -n "$old_key_devices" ]; then + key_devices="$old_key_devices" + fi + if [ -z "$lvm_volume_group" ] && [ -n "$old_lvm_volume_group" ]; then + lvm_volume_group="$old_lvm_volume_group" + fi + fi + else + DEBUG "No previous TPM Disk Unlock Key was setuped for LUKS devices, setting up new ones" + if [ "$num_lvm" -gt 1 ]; then + DEBUG "Multiple LVM groups found" + //untested + selected_lvmdev_not_existing=1 + while [ $selected_lvmdev_not_existing -ne 0 ]; do + { + read \ + -p "Encrypted LVM group? choose between: '$lvm_suggest': " \ + lvm_volume_group - read \ - -p "LVM group containing Encrypted LVs (retype to keep)? ($lvm_suggest): " \ - lvm_volume_group + result=$(echo "$lvm_suggest" | grep -q "$lvm_volume_group") || selected_lvmdev_not_existing=1 + if [ $? == 0 ]; then + selected_lvmdev_not_existing=0 + fi + } + done + elif [ "$num_lvm" -eq 1 ] && [ -s $lvm_suggest ]; then + echo "Single Encrypted LVM group found at $lvm_suggest." + lvm_volume_group=$lvm_suggest + else + echo "No encrypted LVM Group found." + fi - echo "+++ Block devices (blkid): " - blkid || true + if [ "$num_devices" -gt 1 ]; then + DEBUG "Multiple LUKS devices found" + selected_luksdev_not_existing=1 + while [ $selected_luksdev_not_existing -ne 0 ]; do + { + read \ + -p "Encrypted devices? (choose between: '$devices_suggest'): " \ + key_devices - read \ - -p "Encrypted devices (retype to keep)? ($devices_suggest): " \ - key_devices + result=$(echo "$devices_suggest" | grep -q "$key_devices") || selected_luksdev_not_existing=1 + if [ $? == 0 ]; then + selected_luksdev_not_existing=0 + fi + } + done + elif [ "$num_devices" -eq 1 ]; then + echo "Single Encrypted Disk found at $devices_suggest." + key_devices=$devices_suggest + else + echo "No encrypted devices found." + fi + fi save_key_params="-s -p $paramsdev" if [ -n "$lvm_volume_group" ]; then @@ -110,9 +191,8 @@ if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ else save_key_params="$save_key_params $key_devices" fi - echo "Running kexec-save-key with params: $save_key_params" - kexec-save-key $save_key_params \ - || die "Failed to save the disk key" + kexec-save-key $save_key_params || + die "Failed to save the disk key" fi fi @@ -120,19 +200,20 @@ fi mount -o rw,remount $paramsdev if [ ! -d $paramsdir ]; then - mkdir -p $paramsdir \ - || die "Failed to create params directory" + mkdir -p $paramsdir || + die "Failed to create params directory" fi if [ "$CONFIG_TPM2_TOOLS" = "y" ]; then - sha256sum /tmp/primary.handle > "$PRIMHASH_FILE" \ - || die "ERROR: Failed to Hash TPM2 primary key handle!" + sha256sum /tmp/primary.handle >"$PRIMHASH_FILE" || + die "ERROR: Failed to Hash TPM2 primary key handle!" fi rm $paramsdir/kexec_default.*.txt 2>/dev/null || true -echo "$entry" > $ENTRY_FILE -( cd $bootdir && kexec-boot -b "$bootdir" -e "$entry" -f| \ - xargs sha256sum > $HASH_FILE \ +echo "$entry" >$ENTRY_FILE +( + cd $bootdir && kexec-boot -b "$bootdir" -e "$entry" -f | + xargs sha256sum >$HASH_FILE ) || die "Failed to create hashes of boot files" if [ ! -r $ENTRY_FILE -o ! -r $HASH_FILE ]; then die "Failed to write default config" @@ -144,19 +225,19 @@ if [ "$save_key" = "y" ]; then cd /tmp/initrd_extract # Get initrd filename selected to be default initrd that OS could be using to configure LUKS on boot by deploying crypttab files current_default_initrd=$(cat /boot/kexec_default_hashes.txt | grep initr | awk -F " " {'print $NF'} | sed 's/\.\//\/boot\//g') - + # Get crypttab files paths from initrd echo "+++ Checking current selected default boot's $current_default_initrd for existing crypttab files..." # First either decompress or use the original if it's not compressed initrd_decompressed="/tmp/initrd_extract/initrd_decompressed.cpio" - zcat < "$current_default_initrd" > "$initrd_decompressed" 2> /dev/null || initrd_decompressed="$current_default_initrd" - crypttab_files=$(cpio --list --quiet < "$initrd_decompressed" | grep crypttab 2> /dev/null) || true - + zcat <"$current_default_initrd" >"$initrd_decompressed" 2>/dev/null || initrd_decompressed="$current_default_initrd" + crypttab_files=$(cpio --list --quiet <"$initrd_decompressed" | grep crypttab 2>/dev/null) || true + if [ ! -z "$crypttab_files" ]; then echo "+++ Extracting current selected default boot's $current_default_initrd for found crypttab files analysis..." - cpio -id --quiet < $initrd_decompressed $crypttab_files 2> /dev/null + cpio -id --quiet $crypttab_files <$initrd_decompressed 2>/dev/null rm -f $bootdir/kexec_initrd_crypttab_overrides.txt || true - + #Parsing each crypttab file found echo "$crypttab_files" | while read filepath; do # Keep only non-commented lines @@ -165,7 +246,7 @@ if [ "$save_key" = "y" ]; then modified_filepath_entries=$(echo "$current_filepath_entries" | sed 's/none/\/secret.key/g') echo "$modified_filepath_entries" | while read single_modified_filepath_entry; do # Append each found filepath:entry into additional kexec_ file that will be part of detached signed digest - echo "$filepath:$single_modified_filepath_entry" >> $bootdir/kexec_initrd_crypttab_overrides.txt + echo "$filepath:$single_modified_filepath_entry" >>$bootdir/kexec_initrd_crypttab_overrides.txt done done @@ -189,14 +270,14 @@ fi # sign and auto-roll config counter extparam= -if [ "$CONFIG_TPM" = "y" ];then +if [ "$CONFIG_TPM" = "y" ]; then if [ "$CONFIG_IGNORE_ROLLBACK" != "y" ]; then extparam=-r fi fi if [ "$CONFIG_BASIC" != "y" ]; then - kexec-sign-config -p $paramsdir $extparam \ - || die "Failed to sign default config" + kexec-sign-config -p $paramsdir $extparam || + die "Failed to sign default config" fi # switch back to ro mode mount -o ro,remount $paramsdev diff --git a/initrd/bin/kexec-save-key b/initrd/bin/kexec-save-key index d6785d67..e9d22b34 100755 --- a/initrd/bin/kexec-save-key +++ b/initrd/bin/kexec-save-key @@ -1,5 +1,9 @@ #!/bin/bash # Generate a TPM key used to unlock LUKS disks + +. /etc/functions + +TRACE "kexec-save-key: start" set -e -o pipefail . /etc/functions @@ -13,20 +17,28 @@ while getopts "sp:d:l:" arg; do l) lvm_volume_group="$OPTARG" ;; esac done + +DEBUG "kexec-save-key prior of parsing: paramsdir: $paramsdir, paramsdev: $paramsdev, lvm_volume_group: $lvm_volume_group" + shift `expr $OPTIND - 1` key_devices="$@" +DEBUG "kexec-save-key: key_devices: $key_devices" + if [ -z "$paramsdir" ]; then die "Usage: $0 [-s] -p /boot [-l qubes_dom0] [/dev/sda2 /dev/sda5 ...] " fi if [ -z "$paramsdev" ]; then paramsdev="$paramsdir" + DEBUG "kexec-save-key: paramsdev modified to : $paramsdev" fi paramsdev="${paramsdev%%/}" paramsdir="${paramsdir%%/}" +DEBUG "kexec-save-key prior of last override: paramsdir: $paramsdir, paramsdev: $paramsdev, lvm_volume_group: $lvm_volume_group" + if [ -n "$lvm_volume_group" ]; then lvm vgchange -a y $lvm_volume_group \ || die "Failed to activate the LVM group" @@ -45,14 +57,17 @@ mount -o rw,remount $paramsdev rm -f $paramsdir/kexec_key_lvm.txt || true if [ -n "$lvm_volume_group" ]; then + DEBUG "kexec-save-key saving under $paramsdir/kexec_key_lvm.txt : lvm_volume_group: $lvm_volume_group" echo "$lvm_volume_group" > $paramsdir/kexec_key_lvm.txt \ || die "Failed to write lvm group to key config " fi rm -f $paramsdir/kexec_key_devices.txt || true for dev in $key_devices; do + DEBUG "gettinmg uuid for $dev" uuid=`cryptsetup luksUUID "$dev" 2>/dev/null` \ || die "Failed to get UUID for device $dev" + DEBUG "saving under $paramsdir/kexec_key_devices.txt : dev: $dev, uuid: $uuid" echo "$dev $uuid" >> $paramsdir/kexec_key_devices.txt \ || die "Failed to add $dev:$uuid to key devices config" done @@ -71,4 +86,4 @@ if [ "$skip_sign" != "y" ]; then fi # switch back to ro mode -mount -o ro,remount $paramsdev +mount -o ro,remount $paramsdev \ No newline at end of file diff --git a/initrd/bin/kexec-seal-key b/initrd/bin/kexec-seal-key index 4a45c0c5..517f7ad5 100755 --- a/initrd/bin/kexec-seal-key +++ b/initrd/bin/kexec-seal-key @@ -26,16 +26,20 @@ 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` + 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" + 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)" @@ -43,50 +47,69 @@ DEBUG "$(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" +read -s -p "Enter disk recovery key/passphrase: " disk_password +echo -n "$disk_password" >"$RECOVERY_KEY" echo -read -s -p "New disk unlock password for booting: " key_password +read -s -p "New TPM Disk Unlock Key passphrase for booting: " key_password echo -read -s -p "Repeat unlock code: " key_password2 +read -s -p "Repeat TPM Disk Unlock key passphrase: " key_password2 echo if [ "$key_password" != "$key_password2" ]; then - die "Key passwords do not match" + 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" + 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. It is safe to use it to store TPM sealed LUKS Disk Unlock Key" + 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" +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: ignoring problem" + $dev 1 || + warn "$dev: removal of key in slot 1 failed: might not exist. Continuing" - echo "++++++ $dev: Adding key" + 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" + $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" +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" @@ -94,19 +117,19 @@ 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" +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)..." + 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)..." + 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" + 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" +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" @@ -115,9 +138,12 @@ DO_WITH_DEBUG --mask-position 7 \ "$TPM_SIZE" "$key_password" # should be okay if this fails -shred -n 10 -z -u "$pcrf".* 2> /dev/null || true -shred -n 10 -z -u "$KEY_FILE" 2> /dev/null \ -|| warn "Failed to delete key file - continuing" +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" -cp /tmp/luksDump.txt "$paramsdir/kexec_lukshdr_hash.txt" \ -|| warn "Failed to have hashes of LUKS header - 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" diff --git a/initrd/bin/kexec-select-boot b/initrd/bin/kexec-select-boot index beacc69c..28c7087d 100755 --- a/initrd/bin/kexec-select-boot +++ b/initrd/bin/kexec-select-boot @@ -57,7 +57,7 @@ if [ "$CONFIG_TPM2_TOOLS" = "y" ]; then || { echo "FATAL: Hash of TPM2 primary key handle mismatch!"; echo "If you have not intentionally regenerated TPM2 primary key,"; - warn "your system may have been compromised!"; + warn "your system may have been compromised"; } else echo "WARNING: Hash of TPM2 primary key handle does not exist!" @@ -252,7 +252,7 @@ default_select() { whiptail $BG_COLOR_ERROR --title 'ERROR: Boot Entry Has Changed' \ --msgbox "The list of boot entries has changed\n\nPlease set a new default" 0 80 fi - warn "!!! Boot entry has changed - please set a new default" + warn "Boot entry has changed - please set a new default" return fi parse_option diff --git a/initrd/bin/kexec-unseal-key b/initrd/bin/kexec-unseal-key index 3c9ab21e..b8c94b38 100755 --- a/initrd/bin/kexec-unseal-key +++ b/initrd/bin/kexec-unseal-key @@ -26,7 +26,7 @@ DEBUG "Show PCRs" DEBUG "$(pcrs)" for tries in 1 2 3; do - read -s -p "Enter unlock password (blank to abort): " tpm_password + read -s -p "Enter LUKS Disk Unlock Key passphrase (blank to abort): " tpm_password echo if [ -z "$tpm_password" ]; then die "Aborting unseal disk encryption key" diff --git a/initrd/bin/seal-hotpkey b/initrd/bin/seal-hotpkey index d206ada1..80aab66b 100755 --- a/initrd/bin/seal-hotpkey +++ b/initrd/bin/seal-hotpkey @@ -9,7 +9,7 @@ HOTP_KEY="/boot/kexec_hotp_key" mount_boot() { - TRACE "Under /bin/seal-htopkey:mount_boot" + TRACE "Under /bin/seal-hotpkey:mount_boot" # Mount local disk if it is not already mounted if ! grep -q /boot /proc/mounts ; then mount -o ro /boot \ diff --git a/initrd/bin/tpmr b/initrd/bin/tpmr index 1e5a19ad..e7d0af66 100755 --- a/initrd/bin/tpmr +++ b/initrd/bin/tpmr @@ -495,13 +495,13 @@ tpm1_seal() { # The permissions are 0 since there is nothing special # about the sealed file tpm physicalpresence -s \ - || warn "Warning: Unable to assert physical presence" + || warn "Unable to assert physical presence" prompt_tpm_password tpm nv_definespace -in "$index" -sz "$sealed_size" \ -pwdo "$tpm_password" -per 0 \ - || warn "Warning: Unable to define NVRAM space; trying anyway" + || warn "Unable to define NVRAM space; trying anyway" tpm nv_writevalue -in "$index" -if "$sealed_file" \ @@ -595,7 +595,7 @@ tpm2_reset() { TRACE "Under /bin/tpmr:tpm2_reset" key_password="$1" mkdir -p "$SECRET_DIR" - tpm2 clear -c platform || warn "Unable to clear TPM on platform hierarchy!" + tpm2 clear -c platform || warn "Unable to clear TPM on platform hierarchy" tpm2 changeauth -c owner "$(tpm2_password_hex "$key_password")" tpm2 changeauth -c endorsement "$(tpm2_password_hex "$key_password")" tpm2 createprimary -C owner -g sha256 -G "${CONFIG_PRIMARY_KEY_TYPE:-rsa}" \ @@ -673,7 +673,7 @@ tpm2_kexec_finalize() { echo "Locking TPM2 platform hierarchy..." randpass=$(dd if=/dev/urandom bs=4 count=1 status=none | xxd -p) tpm2 changeauth -c platform "$randpass" \ - || warn "Failed to lock platform hierarchy of TPM2!" + || warn "Failed to lock platform hierarchy of TPM2" } tpm2_shutdown() { diff --git a/initrd/etc/ash_functions b/initrd/etc/ash_functions index 1c5aaf0b..c62c62c9 100644 --- a/initrd/etc/ash_functions +++ b/initrd/etc/ash_functions @@ -4,13 +4,13 @@ # busybox ash on legacy-flash boards, and with bash on all other boards. die() { - echo >&2 "$*"; + echo >&2 " !!! ERROR: $* !!!"; sleep 2; exit 1; } warn() { - echo >&2 "$*"; + echo >&2 " *** WARNING: $* ***"; sleep 1; } diff --git a/initrd/etc/functions b/initrd/etc/functions index f90b25ee..c8f4a844 100755 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -81,6 +81,36 @@ confirm_totp() echo } +reseal_tpm_disk_decryption_key() +{ + TRACE "Under /etc/functions:reseal_tpm_disk_decryption_key" + #Can be called only on resealing, not on tpm reset: + #otherise primary handle and rollback would change. + if ! grep -q /boot /proc/mounts ; then + mount -o ro /boot \ + || recovery "Unable to mount /boot" + fi + + if [ -s /boot/kexec_key_devices.txt ] || [ -s /boot/kexec_key_lvm.txt ]; then + warn "A TPM disk decryption key previoulsy sealed is now invalid since firmware measurements cannot be unsealed" + warn "Renewing LUKS Disk Unlock Key to be unsealed by TPM Disk Unlock Key passphrase" + while ! kexec-seal-key /boot; do + warn "Recovery disk encryption key passphrase invalid. Try again!" + done + warn "LUKS header hash changed under of /boot/kexec_luks_hdr_hash.txt" + warn "Updating checksums and signing all files under /boot/kexec.sig" + while ! update_checksums; do + warn "Checksums were not signed. Bad GPG PIN provided?" + warn "Please update checksums and provide a valid GPG PIN" + done + warn "Rebooting in 3 seconds to enable booting default boot option" + sleep 3 + reboot + else + DEBUG "No TPM disk decryption key to reseal" + fi +} + # Enable USB storage (if not already enabled), and wait for storage devices to # be detected. If USB storage was already enabled, no wait occurs, this would # have happened already when USB storage was enabled. diff --git a/initrd/etc/luks-functions b/initrd/etc/luks-functions index 31b9abb2..c64c00dc 100644 --- a/initrd/etc/luks-functions +++ b/initrd/etc/luks-functions @@ -246,7 +246,6 @@ select_luks_container() mount -o remount,ro /boot fi else - warn "No encrypted device found." return 1 fi