From 4910c1188fc9c5f1db839a8bfd52dcf980e3b25c Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Tue, 22 Aug 2023 14:34:29 -0400 Subject: [PATCH 01/13] 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 From 67c865d151cffe15d4179fd68fdc48c2eac78d54 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Wed, 30 Aug 2023 16:49:39 -0400 Subject: [PATCH 02/13] TPM DISK Unlock Key : add cryptroot/crypttab to fix #1474 Tested working on both TPM1/TPM2 under debian bookwork, standard encrypted TLVM setup --- initrd/bin/gui-init | 1 - initrd/bin/kexec-insert-key | 51 +++++++++++++++++++++---------------- initrd/bin/kexec-seal-key | 7 ++--- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/initrd/bin/gui-init b/initrd/bin/gui-init index 64f7563f..382c3742 100755 --- a/initrd/bin/gui-init +++ b/initrd/bin/gui-init @@ -580,7 +580,6 @@ reset_tpm() 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-insert-key b/initrd/bin/kexec-insert-key index 95f7a097..4ea43ea2 100755 --- a/initrd/bin/kexec-insert-key +++ b/initrd/bin/kexec-insert-key @@ -20,17 +20,17 @@ fi if [ -r "$TMP_KEY_LVM" ]; then # Activate the LVM volume group - VOLUME_GROUP=`cat $TMP_KEY_LVM` + VOLUME_GROUP=$(cat $TMP_KEY_LVM) if [ -z "$TMP_KEY_LVM" ]; 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" fi # Measure the LUKS headers before we unseal the disk key -cat "$TMP_KEY_DEVICES" | cut -d\ -f1 | xargs /bin/qubes-measure-luks \ - || die "LUKS measure failed" +cat "$TMP_KEY_DEVICES" | cut -d\ -f1 | xargs /bin/qubes-measure-luks || + die "LUKS measure failed" # Unpack the initrd and fixup the crypttab # this is a hack to split it into two parts since @@ -43,14 +43,14 @@ mkdir -p "$INITRD_DIR/etc" # Attempt to unseal the disk key from the TPM # should we give this some number of tries? unseal_failed="n" -if ! kexec-unseal-key "$INITRD_DIR/secret.key" ; then +if ! kexec-unseal-key "$INITRD_DIR/secret.key"; then unseal_failed="y" echo "!!! Failed to unseal the TPM LUKS disk key" fi # Override PCR 4 so that user can't read the key -tpmr extend -ix 4 -ic generic \ - || die 'Unable to scramble PCR' +tpmr extend -ix 4 -ic generic || + die 'Unable to scramble PCR' # Check to continue if [ "$unseal_failed" = "y" ]; then @@ -63,21 +63,21 @@ if [ "$unseal_failed" = "y" ]; then if [ "$confirm_boot" != 'y' \ -a "$confirm_boot" != 'Y' \ -a -n "$confirm_boot" ] \ - ; then + ; then die "!!! Aborting boot due to failure to unseal TPM disk key" fi fi -echo +echo echo '+++ Building initrd' # pad the initramfs (dracut doesn't pad the last gz blob) # without this the kernel init/initramfs.c fails to read # the subsequent uncompressed/compressed cpio -dd if="$INITRD" of="$SECRET_CPIO" bs=512 conv=sync \ -|| die "Failed to copy initrd to /tmp" +dd if="$INITRD" of="$SECRET_CPIO" bs=512 conv=sync || + die "Failed to copy initrd to /tmp" if [ "$unseal_failed" = "n" ]; then - # kexec-save-default might have created crypttab overrides to be injected in initramfs through additional cpio + # kexec-save-default might have created crypttab overrides to be injected in initramfs through additional cpio if [ -r "$bootdir/kexec_initrd_crypttab_overrides.txt" ]; then echo "+++ $bootdir/kexec_initrd_crypttab_overrides.txt found..." echo "+++ Preparing initramfs crypttab overrides as defined under $bootdir/kexec_initrd_crypttab_overrides.txt to be injected through cpio at next kexec call..." @@ -87,19 +87,26 @@ if [ "$unseal_failed" = "n" ]; then crypttab_entry=$(echo "$line" | awk -F ':' {'print $NF'}) # Replace each initrd crypttab file with modified entry containing /secret.key path mkdir -p "$INITRD_DIR/$(dirname $crypttab_file)" - echo "$crypttab_entry" | tee -a "$INITRD_DIR/$crypttab_file" > /dev/null + echo "$crypttab_entry" | tee -a "$INITRD_DIR/$crypttab_file" >/dev/null echo "+++ initramfs's $crypttab_file will be overriden with: $crypttab_entry" done else # No crypttab files were found under selected default boot option's initrd file - crypttab_file="etc/crypttab" - mkdir -p "$INITRD_DIR/$(dirname $crypttab_file)" - # overwrite crypttab to mirror behavior of seal-key - echo "+++ The following /etc/crypttab lines will be passed through cpio into kexec call for default boot option:" - for uuid in `cat "$TMP_KEY_DEVICES" | cut -d\ -f2`; do - # NOTE: discard operation (TRIM) is activated by default if no crypptab found in initrd - echo "luks-$uuid UUID=$uuid /secret.key luks,discard" | tee -a "$INITRD_DIR/$crypttab_file" + # TODO: cpio -t is unfit here :( it just extracts early cpio header and not the whole file. Replace with something else + # Meanwhile, force crypttab to be created from scratch on both possible locations: /etc/crypttab and /cryptroot/crypttab + crypttab_files="etc/crypttab cryptroot/crypttab" + for crypttab_file in $crypttab_files; do + mkdir -p "$INITRD_DIR/$(dirname $crypttab_file)" + # overwrite crypttab to mirror behavior of seal-key + echo "+++ The following $crypttab_file overrides will be passed through concatenated secret/initrd.cpio at kexec call:" + for uuid in $(cat "$TMP_KEY_DEVICES" | cut -d\ -f2); do + # NOTE: discard operation (TRIM) is activated by default if no crypptab found in initrd + echo "luks-$uuid UUID=$uuid /secret.key luks,discard" | tee -a "$INITRD_DIR/$crypttab_file" + done done fi - ( cd "$INITRD_DIR" ; find . -type f | cpio -H newc -o ) >> "$SECRET_CPIO" + ( + cd "$INITRD_DIR" + find . -type f | cpio -H newc -o + ) >>"$SECRET_CPIO" fi diff --git a/initrd/bin/kexec-seal-key b/initrd/bin/kexec-seal-key index 517f7ad5..2757087b 100755 --- a/initrd/bin/kexec-seal-key +++ b/initrd/bin/kexec-seal-key @@ -47,13 +47,13 @@ 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/passphrase: " disk_password +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: " key_password2 +read -s -p "Repeat TPM Disk Unlock key passphrase for booting: " key_password2 echo if [ "$key_password" != "$key_password2" ]; then @@ -84,7 +84,8 @@ for dev in $(cat "$KEY_DEVICES" | cut -d\ -f1); do 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" + 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 From 03d8f93c9517230adabaaa1cb9b3fcf53dd50af5 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 31 Aug 2023 11:14:33 -0400 Subject: [PATCH 03/13] modules/zstd: now included by default. Deactivated under legacy-flash boards Rationale: cpio -t alone cannot extract initrd past early cpio (microcode) in most packed initrd. unpack_initramfs.sh already under master comes to the rescue, but its usage up to today was limited to pass firmware blobs to final OS under boards/librem_mini_v2 Debian OSes (and probably others) need to have cryptroot/crypttab overriden directly, otherwise generic generation of crypttab is not enough. Extracting crypttab and overriding directly what is desired by final OS and exposed into /boot/initrd is the way to go otherwise hacking on top of hacks. This brings default packed modules under Heads to 5 modules, which needs to be deactivate in board configs if undesired: user@heads-tests-deb12:~/heads$ grep -Rn "?= y" modules/ | grep -v MUSL modules/zlib:1:CONFIG_ZLIB ?= y modules/zstd:3:CONFIG_ZSTD ?= y modules/exfatprogs:2:CONFIG_EXFATPROGS ?= y modules/busybox:2:CONFIG_BUSYBOX ?= y modules/e2fsprogs:2:CONFIG_E2FSPROGS ?= y --- .../UNTESTED_t430-legacy-flash.config | 1 + boards/librem_mini_v2/librem_mini_v2.config | 2 -- boards/x230-legacy-flash/x230-legacy-flash.config | 2 ++ modules/zstd | 3 +++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/boards/UNTESTED_t430-legacy-flash/UNTESTED_t430-legacy-flash.config b/boards/UNTESTED_t430-legacy-flash/UNTESTED_t430-legacy-flash.config index 94c74887..3d8cdadb 100644 --- a/boards/UNTESTED_t430-legacy-flash/UNTESTED_t430-legacy-flash.config +++ b/boards/UNTESTED_t430-legacy-flash/UNTESTED_t430-legacy-flash.config @@ -13,6 +13,7 @@ CONFIG_LINUX_CONFIG=config/linux-x230-flash.config #Add bare minimal tools for flashing boards CONFIG_BASH=n CONFIG_FLASHROM=y +CONFIG_ZSTD=n #CONFIG_GPG=y #CONFIG_FLASHTOOLS=y CONFIG_PCIUTILS=y diff --git a/boards/librem_mini_v2/librem_mini_v2.config b/boards/librem_mini_v2/librem_mini_v2.config index 40881e12..30224921 100644 --- a/boards/librem_mini_v2/librem_mini_v2.config +++ b/boards/librem_mini_v2/librem_mini_v2.config @@ -20,8 +20,6 @@ CONFIG_PCIUTILS=y CONFIG_POPT=y CONFIG_QRENCODE=y CONFIG_TPMTOTP=y -# zstd-decompress - for blob jail, needed to extract /init from zstd cpio archive -CONFIG_ZSTD=y CONFIG_CAIRO=y CONFIG_FBWHIPTAIL=y diff --git a/boards/x230-legacy-flash/x230-legacy-flash.config b/boards/x230-legacy-flash/x230-legacy-flash.config index 52eb41f9..1ad51a0f 100644 --- a/boards/x230-legacy-flash/x230-legacy-flash.config +++ b/boards/x230-legacy-flash/x230-legacy-flash.config @@ -13,6 +13,7 @@ CONFIG_LINUX_CONFIG=config/linux-x230-flash.config #Add bare minimal tools for flashing boards CONFIG_BASH=n CONFIG_FLASHROM=y +CONFIG_ZSTD=n #CONFIG_GPG=y #CONFIG_FLASHTOOLS=y CONFIG_PCIUTILS=y @@ -21,6 +22,7 @@ CONFIG_PCIUTILS=y #CONFIG_TPMTOTP=y #CONFIG_DROPBEAR=y + #Additional hardware support CONFIG_LINUX_USB=y #CONFIG_LINUX_E1000E=y diff --git a/modules/zstd b/modules/zstd index 3d7bd4b3..cf980812 100644 --- a/modules/zstd +++ b/modules/zstd @@ -1,3 +1,6 @@ +# ZSTD is always included, unless explicitly turned off +# Needed by initrd/bin/unpack_initramfs.sh under TPM Disk Unlock Key setup +CONFIG_ZSTD ?= y modules-$(CONFIG_ZSTD) += zstd zstd_version := 1.5.5 From 64ad01f3338652a10132a9ff2873e4cf2aeaf0c6 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Thu, 31 Aug 2023 12:07:39 -0400 Subject: [PATCH 04/13] WiP: Staging commit to facilitate review, will squash into previous commits once confirmed good --- initrd/bin/gui-init | 2 +- initrd/bin/kexec-save-default | 19 ++- initrd/bin/kexec-seal-key | 2 +- initrd/bin/kexec-select-boot | 6 +- initrd/etc/functions | 293 ++++++++++++++++------------------ initrd/etc/luks-functions | 2 +- 6 files changed, 156 insertions(+), 168 deletions(-) diff --git a/initrd/bin/gui-init b/initrd/bin/gui-init index 382c3742..1ab178f3 100755 --- a/initrd/bin/gui-init +++ b/initrd/bin/gui-init @@ -174,7 +174,7 @@ generate_totp_hotp() printf "\033c" else warn "Unsealing TOTP/HOTP secret from previous sealed measurements failed" - warn "Try "Generate new HOTP/TOTP secret" option if you updated firmware content" + warn 'Try "Generate new HOTP/TOTP secret" option if you updated firmware content' fi } diff --git a/initrd/bin/kexec-save-default b/initrd/bin/kexec-save-default index bc2febcc..a38316e5 100755 --- a/initrd/bin/kexec-save-default +++ b/initrd/bin/kexec-save-default @@ -38,7 +38,6 @@ 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 @@ -50,8 +49,7 @@ 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 + if cryptsetup isLuks "$device"; then echo "$device"; fi done | sort) num_devices=$(echo "$devices_suggest" | wc -l) @@ -79,9 +77,9 @@ fi 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" + DEBUG "Checking if a a TPM Disk Unlock Key was previously set up 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" + DEBUG "No previous TPM Disk Unlock Key was set up 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]: " \ @@ -94,7 +92,7 @@ if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ save_key="y" fi else - DEBUG "Previous TPM Disk Unlock Key was setuped for LUKS devices" + DEBUG "Previous TPM Disk Unlock Key was set up for LUKS devices" read \ -n 1 \ -p "Do you want to reseal a disk key to the TPM [y/N]: " \ @@ -123,7 +121,7 @@ if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ if [ "$save_key" = "y" ]; then 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" + DEBUG "Previous TPM Disk Unlock Key was set up 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):" \ @@ -138,10 +136,10 @@ if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ fi fi else - DEBUG "No previous TPM Disk Unlock Key was setuped for LUKS devices, setting up new ones" + DEBUG "No previous TPM Disk Unlock Key was set up for LUKS devices, setting up new ones" if [ "$num_lvm" -gt 1 ]; then DEBUG "Multiple LVM groups found" - //untested + # TODO: untested codepath. What uses lvm+LUKS on top nowadays?!? selected_lvmdev_not_existing=1 while [ $selected_lvmdev_not_existing -ne 0 ]; do { @@ -172,7 +170,8 @@ if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ key_devices result=$(echo "$devices_suggest" | grep -q "$key_devices") || selected_luksdev_not_existing=1 - if [ $? == 0 ]; then + #if result is not empty, then the device exists + if [ -n "$result" ]; then selected_luksdev_not_existing=0 fi } diff --git a/initrd/bin/kexec-seal-key b/initrd/bin/kexec-seal-key index 2757087b..e9f3248e 100755 --- a/initrd/bin/kexec-seal-key +++ b/initrd/bin/kexec-seal-key @@ -53,7 +53,7 @@ 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 +read -s -p "Repeat TPM Disk Unlock Key passphrase for booting: " key_password2 echo if [ "$key_password" != "$key_password2" ]; then diff --git a/initrd/bin/kexec-select-boot b/initrd/bin/kexec-select-boot index 28c7087d..1562638c 100755 --- a/initrd/bin/kexec-select-boot +++ b/initrd/bin/kexec-select-boot @@ -56,12 +56,12 @@ if [ "$CONFIG_TPM2_TOOLS" = "y" ]; then sha256sum -c "$PRIMHASH_FILE" \ || { echo "FATAL: Hash of TPM2 primary key handle mismatch!"; - echo "If you have not intentionally regenerated TPM2 primary key,"; + warn "If you have not intentionally regenerated TPM2 primary key,"; warn "your system may have been compromised"; } else - echo "WARNING: Hash of TPM2 primary key handle does not exist!" - echo "Please rebuild the boot hash tree." + warn "Hash of TPM2 primary key handle does not exist" + warn "Please rebuild the boot hash tree" default_failed="y" fi fi diff --git a/initrd/etc/functions b/initrd/etc/functions index c8f4a844..4ea7abed 100755 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -41,8 +41,7 @@ pcrs() { fi } -confirm_totp() -{ +confirm_totp() { TRACE "Under /etc/functions:confirm_totp" prompt="$1" last_half=X @@ -51,15 +50,15 @@ confirm_totp() while true; do # update the TOTP code every thirty seconds - date=`date "+%Y-%m-%d %H:%M:%S"` - seconds=`date "+%s"` - half=`expr \( $seconds % 60 \) / 30` + date=$(date "+%Y-%m-%d %H:%M:%S") + seconds=$(date "+%s") + half=$(expr \( $seconds % 60 \) / 30) if [ "$CONFIG_TPM" != "y" ]; then TOTP="NO TPM" elif [ "$half" != "$last_half" ]; then - last_half=$half; - TOTP=`unseal-totp` \ - || recovery "TOTP code generation failed" + last_half=$half + TOTP=$(unseal-totp) || + recovery "TOTP code generation failed" fi echo -n "$date $TOTP: " @@ -70,8 +69,8 @@ confirm_totp() -n 1 \ -s \ -p "$prompt" \ - totp_confirm \ - && break + totp_confirm && + break # nothing typed, redraw the line echo -ne '\r' @@ -81,27 +80,32 @@ confirm_totp() echo } -reseal_tpm_disk_decryption_key() -{ +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" + #For robustness, exit early if TPM Disk Unlock Key is prohibited in board configs + if [ "$CONFIG_TPM_DISK_UNLOCK_KEY" != "y" ]; then + DEBUG "TPM Disk Unlock Key is prohibited in board configs" + return + else + DEBUG "TPM Disk Unlock Key is allowed in board configs. Continuing" + fi + + 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" + warn "A TPM Disk Unlock Key previously sealed is now invalid since firmware measurements cannot be unsealed" + echo "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!" + 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" + warn "LUKS header hash changed under /boot/kexec_luks_hdr_hash.txt" + echo "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" + 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 @@ -114,23 +118,21 @@ reseal_tpm_disk_decryption_key() # 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. -enable_usb_storage() -{ +enable_usb_storage() { if ! lsmod | grep -q usb_storage; then timeout=0 echo "Scanning for USB storage devices..." - insmod /lib/modules/usb-storage.ko >/dev/null 2>&1 \ - || die "usb_storage: module load failed" - while [[ $(list_usb_storage | wc -l) -eq 0 ]]; do + insmod /lib/modules/usb-storage.ko >/dev/null 2>&1 || + die "usb_storage: module load failed" + while [[ $(list_usb_storage | wc -l) -eq 0 ]]; do [[ $timeout -ge 8 ]] && break sleep 1 - timeout=$(($timeout+1)) + timeout=$(($timeout + 1)) done fi } -list_usb_storage() -{ +list_usb_storage() { TRACE "Under /etc/functions:list_usb_storage" # List all USB storage devices, including partitions unless we received argument stating we want drives only # The output is a list of device names, one per line. @@ -187,8 +189,7 @@ list_usb_storage() done } -confirm_gpg_card() -{ +confirm_gpg_card() { TRACE "Under /etc/functions:confirm_gpg_card" read \ -n 1 \ @@ -199,7 +200,7 @@ confirm_gpg_card() if [ "$card_confirm" != "y" \ -a "$card_confirm" != "Y" \ -a -n "$card_confirm" ] \ - ; then + ; then die "gpg card not confirmed" fi @@ -210,23 +211,23 @@ confirm_gpg_card() # ensure we don't exit without retrying errexit=$(set -o | grep errexit | awk '{print $2}') set +e - gpg --card-status > /dev/null + gpg --card-status >/dev/null if [ $? -ne 0 ]; then - # prompt for reinsertion and try a second time - read -n1 -r -p \ - "Can't access GPG key; remove and reinsert, then press Enter to retry. " \ - ignored - # restore prev errexit state - if [ "$errexit" = "on" ]; then - set -e - fi - # retry card status - gpg --card-status > /dev/null \ - || die "gpg card read failed" + # prompt for reinsertion and try a second time + read -n1 -r -p \ + "Can't access GPG key; remove and reinsert, then press Enter to retry. " \ + ignored + # restore prev errexit state + if [ "$errexit" = "on" ]; then + set -e + fi + # retry card status + gpg --card-status >/dev/null || + die "gpg card read failed" fi # restore prev errexit state if [ "$errexit" = "on" ]; then - set -e + set -e fi } @@ -235,7 +236,7 @@ confirm_gpg_card() # line, since some flows need it multiple times and only one prompt is ideal. prompt_tpm_password() { if [ -n "$tpm_password" ]; then - return 0; + return 0 fi read -s -p "TPM Owner password: " tpm_password @@ -263,25 +264,24 @@ prompt_new_owner_password() { done } -check_tpm_counter() -{ - TRACE "Under /etc/functions:check_tpm_counter" - LABEL=${2:-3135106223} - tpm_password="$3" +check_tpm_counter() { + TRACE "Under /etc/functions:check_tpm_counter" + LABEL=${2:-3135106223} + tpm_password="$3" # if the /boot.hashes file already exists, read the TPM counter ID # from it. if [ -r "$1" ]; then - TPM_COUNTER=`grep counter- "$1" | cut -d- -f2` + TPM_COUNTER=$(grep counter- "$1" | cut -d- -f2) else warn "$1 does not exist; creating new TPM counter" prompt_tpm_password tpmr counter_create \ -pwdo "$tpm_password" \ -pwdc '' \ - -la $LABEL \ - | tee /tmp/counter \ - || die "Unable to create TPM counter" - TPM_COUNTER=`cut -d: -f1 < /tmp/counter` + -la $LABEL | + tee /tmp/counter || + die "Unable to create TPM counter" + TPM_COUNTER=$(cut -d: -f1 ${CONFIG_FILE}.tmp - awk "gsub(\"^${CONFIG_OPTION}=.*\",\"${CONFIG_OPTION}=\\\"${NEW_SETTING}\\\"\")" /tmp/config >> ${CONFIG_FILE}.tmp + # first pull out the existing option from the global config and place in a tmp file + awk "gsub(\"^export ${CONFIG_OPTION}=.*\",\"export ${CONFIG_OPTION}=\\\"${NEW_SETTING}\\\"\")" /tmp/config >${CONFIG_FILE}.tmp + awk "gsub(\"^${CONFIG_OPTION}=.*\",\"${CONFIG_OPTION}=\\\"${NEW_SETTING}\\\"\")" /tmp/config >>${CONFIG_FILE}.tmp -# then copy any remaining settings from the existing config file, minus the option you changed - grep -v "^export ${CONFIG_OPTION}=" ${CONFIG_FILE} | grep -v "^${CONFIG_OPTION}=" >> ${CONFIG_FILE}.tmp || true - sort ${CONFIG_FILE}.tmp | uniq > ${CONFIG_FILE} + # then copy any remaining settings from the existing config file, minus the option you changed + grep -v "^export ${CONFIG_OPTION}=" ${CONFIG_FILE} | grep -v "^${CONFIG_OPTION}=" >>${CONFIG_FILE}.tmp || true + sort ${CONFIG_FILE}.tmp | uniq >${CONFIG_FILE} rm -f ${CONFIG_FILE}.tmp } @@ -368,7 +366,7 @@ set_config() { CONFIG_FILE="$1" CONFIG_OPTION="$2" NEW_SETTING="$3" - + if grep -q "$CONFIG_OPTION" "$CONFIG_FILE"; then replace_config "$CONFIG_FILE" "$CONFIG_OPTION" "$NEW_SETTING" else @@ -389,12 +387,11 @@ set_user_config() { # Load a config value to a variable, defaulting to empty. Does not fail if the # config is not set (since it would expand to empty by default). -load_config_value() -{ - local config_name="$1" - if grep -q "$config_name=" /tmp/config; then - grep "$config_name=" /tmp/config | tail -n1 | cut -f2 -d '=' | tr -d '"' - fi +load_config_value() { + local config_name="$1" + if grep -q "$config_name=" /tmp/config; then + grep "$config_name=" /tmp/config | tail -n1 | cut -f2 -d '=' | tr -d '"' + fi } # Generate a secret for TPM-less HOTP by reading the ROM. Output is the @@ -413,13 +410,12 @@ secret_from_rom_hash() { sha256sum "${ROM_IMAGE}" | cut -f1 -d ' ' | fromhex_plain } -update_checksums() -{ +update_checksums() { TRACE "Under /etc/functions:update_checksums" # ensure /boot mounted - if ! grep -q /boot /proc/mounts ; then - mount -o ro /boot \ - || recovery "Unable to mount /boot" + if ! grep -q /boot /proc/mounts; then + mount -o ro /boot || + recovery "Unable to mount /boot" fi # remount RW @@ -427,12 +423,12 @@ update_checksums() # 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 ! kexec-sign-config -p /boot -u $extparam ; then + if ! kexec-sign-config -p /boot -u $extparam; then rv=1 else rv=0 @@ -466,15 +462,15 @@ escape_zero() { echo -e -n "$prefix" xxd -p -c1 | tr -d '\n' | - { - while IFS= read -r -n2 -d '' ; do - if [ -n "$todo" ] ; then - #REPLY == " " is EOF - [[ "$REPLY" == " " ]] && echo '' || echo -e -n "$todo" - todo="" - fi + { + while IFS= read -r -n2 -d ''; do + if [ -n "$todo" ]; then + #REPLY == " " is EOF + [[ "$REPLY" == " " ]] && echo '' || echo -e -n "$todo" + todo="" + fi - case "$REPLY" in + case "$REPLY" in 00) todo="\n$prefix" ;; @@ -497,16 +493,16 @@ escape_zero() { echo -n "$echar$echar" ;; #interpreted characters: - 2[0-9a-f]|3[0-9a-f]|4[0-9a-f]|5[0-9abd-f]|6[0-9a-f]|7[0-9a-e]) + 2[0-9a-f] | 3[0-9a-f] | 4[0-9a-f] | 5[0-9abd-f] | 6[0-9a-f] | 7[0-9a-e]) echo -e -n '\x'"$REPLY" ;; # All others are escaped *) echo -n "${echar}x$REPLY" ;; - esac - done - } + esac + done + } } # Currently heads doesn't support signing file names with certain characters @@ -515,14 +511,14 @@ escape_zero() { assert_signable() { TRACE "Under /etc/functions:assert_signable" # ensure /boot mounted - if ! grep -q /boot /proc/mounts ; then + if ! grep -q /boot /proc/mounts; then mount -o ro /boot || die "Unable to mount /boot" fi - find /boot -print0 > /tmp/signable.ref + find /boot -print0 >/tmp/signable.ref local del='\001-\037\134\177-\377' - LC_ALL=C tr -d "$del" < /tmp/signable.ref > /tmp/signable.del || die "Failed to execute tr." - if ! cmp -s "/tmp/signable.ref" "/tmp/signable.del" &> /dev/null ; then + LC_ALL=C tr -d "$del" /tmp/signable.del || die "Failed to execute tr." + if ! cmp -s "/tmp/signable.ref" "/tmp/signable.del" &>/dev/null; then local user_out="/tmp/hash_output_mismatches" local add="Please investigate!" [ -f "$user_out" ] && add="Please investigate the following relative paths to /boot (where # are sanitized invalid characters):"$'\n'"$(cat "$user_out")" @@ -531,8 +527,7 @@ assert_signable() { rm -f /tmp/signable.* } -verify_checksums() -{ +verify_checksums() { TRACE "Under /etc/functions:verify_checksums" local boot_dir="$1" local gui="${2:-y}" @@ -541,20 +536,20 @@ verify_checksums() set +e -o pipefail local ret=0 cd "$boot_dir" || ret=1 - sha256sum -c "$TMP_HASH_FILE" > /tmp/hash_output || ret=1 + sha256sum -c "$TMP_HASH_FILE" >/tmp/hash_output || ret=1 # also make sure that the file & directory structure didn't change # (sha256sum won't detect added files) - print_tree > /tmp/tree_output || ret=1 - if ! cmp -s "$TMP_TREE_FILE" /tmp/tree_output &> /dev/null ; then + print_tree >/tmp/tree_output || ret=1 + if ! cmp -s "$TMP_TREE_FILE" /tmp/tree_output &>/dev/null; then ret=1 [[ "$gui" != "y" ]] && exit "$ret" # produce a diff that can safely be presented to the user # this is relatively hard as file names may e.g. contain backslashes etc., # which are interpreted by whiptail, less, ... - escape_zero "(new) " < "$TMP_TREE_FILE" > "${TMP_TREE_FILE}.user" - escape_zero "(new) " < /tmp/tree_output > /tmp/tree_output.user - diff "${TMP_TREE_FILE}.user" /tmp/tree_output.user | grep -E '^\+\(new\).*$' | sed -r 's/^\+\(new\)/(new)/g' >> /tmp/hash_output + escape_zero "(new) " <"$TMP_TREE_FILE" >"${TMP_TREE_FILE}.user" + escape_zero "(new) " /tmp/tree_output.user + diff "${TMP_TREE_FILE}.user" /tmp/tree_output.user | grep -E '^\+\(new\).*$' | sed -r 's/^\+\(new\)/(new)/g' >>/tmp/hash_output rm -f "${TMP_TREE_FILE}.user" rm -f /tmp/tree_output.user fi @@ -565,8 +560,7 @@ verify_checksums() # detect and set /boot device # mount /boot if successful -detect_boot_device() -{ +detect_boot_device() { TRACE "Under /etc/functions:detect_boot_device" # unmount /boot to be safe cd / && umount /boot 2>/dev/null @@ -582,22 +576,22 @@ detect_boot_device() fi # generate list of possible boot devices - fdisk -l | grep "Disk /dev/" | cut -f2 -d " " | cut -f1 -d ":" > /tmp/disklist + fdisk -l | grep "Disk /dev/" | cut -f2 -d " " | cut -f1 -d ":" >/tmp/disklist # filter out extraneous options - > /tmp/boot_device_list - for i in `cat /tmp/disklist`; do + >/tmp/boot_device_list + for i in $(cat /tmp/disklist); do # remove block device from list if numeric partitions exist, since not bootable - DEV_NUM_PARTITIONS=$((`ls -1 $i* | wc -l`-1)) + DEV_NUM_PARTITIONS=$(($(ls -1 $i* | wc -l) - 1)) if [ ${DEV_NUM_PARTITIONS} -eq 0 ]; then - echo $i >> /tmp/boot_device_list + echo $i >>/tmp/boot_device_list else - ls $i* | tail -${DEV_NUM_PARTITIONS} >> /tmp/boot_device_list + ls $i* | tail -${DEV_NUM_PARTITIONS} >>/tmp/boot_device_list fi done # iterate thru possible options and check for grub dir - for i in `cat /tmp/boot_device_list`; do + for i in $(cat /tmp/boot_device_list); do umount /boot 2>/dev/null if mount -o ro $i /boot >/dev/null 2>&1; then if ls -d /boot/grub* >/dev/null 2>&1; then @@ -613,30 +607,28 @@ detect_boot_device() return 1 } -scan_boot_options() -{ +scan_boot_options() { local bootdir config option_file bootdir="$1" config="$2" option_file="$3" if [ -r $option_file ]; then rm $option_file; fi - for i in `find $bootdir -name "$config"`; do - DO_WITH_DEBUG kexec-parse-boot "$bootdir" "$i" >> $option_file + for i in $(find $bootdir -name "$config"); do + DO_WITH_DEBUG kexec-parse-boot "$bootdir" "$i" >>$option_file done # FC29/30+ may use BLS format grub config files # https://fedoraproject.org/wiki/Changes/BootLoaderSpecByDefault # only parse these if $option_file is still empty if [ ! -s $option_file ] && [ -d "$bootdir/loader/entries" ]; then - for i in `find $bootdir -name "$config"`; do - kexec-parse-bls "$bootdir" "$i" "$bootdir/loader/entries" >> $option_file + for i in $(find $bootdir -name "$config"); do + kexec-parse-bls "$bootdir" "$i" "$bootdir/loader/entries" >>$option_file done fi } -calc() -{ - awk "BEGIN { print "$*" }"; +calc() { + awk "BEGIN { print "$*" }" } # truncate a file to a size only if it is longer (busybox truncate lacks '<' and @@ -665,24 +657,21 @@ fromhex_plain() { fold -w 60 | xxd -p -r } -print_battery_health() -{ +print_battery_health() { if [ -d /sys/class/power_supply/BAT* ]; then - battery_health=$(calc $(cat /sys/class/power_supply/BAT*/charge_full)/$(cat /sys/class/power_supply/BAT*/charge_full_design)*100 | awk -F "." {'print $1'}) + battery_health=$(calc $(cat /sys/class/power_supply/BAT*/charge_full)/$(cat /sys/class/power_supply/BAT*/charge_full_design)*100 | awk -F "." {'print $1'}) echo "$battery_health" fi } -print_battery_charge() -{ +print_battery_charge() { if [ -d /sys/class/power_supply/BAT* ]; then - battery_charge=$(calc $(cat /sys/class/power_supply/BAT*/charge_now)/$(cat /sys/class/power_supply/BAT*/charge_full)*100 | awk -F "." {'print $1'}) - echo "$battery_charge" - fi + battery_charge=$(calc $(cat /sys/class/power_supply/BAT*/charge_now)/$(cat /sys/class/power_supply/BAT*/charge_full)*100 | awk -F "." {'print $1'}) + echo "$battery_charge" + fi } -generate_random_mac_address() -{ +generate_random_mac_address() { #Borrowed from https://stackoverflow.com/questions/42660218/bash-generate-random-mac-address-unicast hexdump -n 6 -ve '1/1 "%.2x "' /dev/urandom | awk -v a="2,6,a,e" -v r="$RANDOM" 'BEGIN{srand(r);}NR==1{split(a,b,",");r=int(rand()*4+1);printf "%s%s:%s:%s:%s:%s:%s\n",substr($1,0,1),b[r],$2,$3,$4,$5,$6}' } @@ -713,9 +702,9 @@ run_at_exit_handlers() { # Silence trace if there are no handlers, this is common and occurs a lot [ "$cmd_pos" -gt 0 ] && DEBUG "Running at_exit handlers" while [ "$cmd_pos" -gt 0 ]; do - cmd_pos="$((cmd_pos-1))" + cmd_pos="$((cmd_pos - 1))" cmd_len="${AT_EXIT_HANDLERS[$cmd_pos]}" - cmd_pos="$((cmd_pos-cmd_len))" + cmd_pos="$((cmd_pos - cmd_len))" "${AT_EXIT_HANDLERS[@]:$cmd_pos:$cmd_len}" done } diff --git a/initrd/etc/luks-functions b/initrd/etc/luks-functions index c64c00dc..67c937a9 100644 --- a/initrd/etc/luks-functions +++ b/initrd/etc/luks-functions @@ -301,7 +301,7 @@ while : ; do if [ -z "$luks_current_Disk_Recovery_Key_passphrase" ]; then #if no external provisioning provides current Disk Recovery Key passphrase whiptail --title 'Reencrypt LUKS disk encrypted container ?' \ - --msgbox "This will replace the encrypted container content and its Disk Recovery Key.\n\nThe passphrase associated with this key will be asked from the user in the\nfollowing conditions:\n 1-Every boot if no Disk unlock key was added to the TPM\n 2-If the TPM fails (Hardware failure)\n 3-If the firmware has been tampered with/upgraded/modified by the user\n\nThis process requires you to type the current Disk Recovery Key passphrase\nand will delete TPM Disk unlock key slot if setuped by setting a default boot\n LUKS header (slot 1) if present.\n\nAt the next prompt, you may be asked to select which file corresponds to\nthe LUKS device container.\n\nHit Enter to continue." 0 80 + --msgbox "This will replace the encrypted container content and its Disk Recovery Key.\n\nThe passphrase associated with this key will be asked from the user in the\nfollowing conditions:\n 1-Every boot if no Disk unlock key was added to the TPM\n 2-If the TPM fails (Hardware failure)\n 3-If the firmware has been tampered with/upgraded/modified by the user\n\nThis process requires you to type the current Disk Recovery Key passphrase\nand will delete TPM Disk unlock key slot if set up by setting a default boot\n LUKS header (slot 1) if present.\n\nAt the next prompt, you may be asked to select which file corresponds to\nthe LUKS device container.\n\nHit Enter to continue." 0 80 echo -e "\nEnter current Disk Recovery Key passphrase (Provisioned at OS installation or by OEM):" read -r luks_current_Disk_Recovery_Key_passphrase echo -n "$luks_current_Disk_Recovery_Key_passphrase" > /tmp/luks_current_Disk_Recovery_Key_passphrase From 4a7e23b4c62e5f1bd2d5b213db86238fe975cf9d Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Fri, 1 Sep 2023 15:18:36 -0400 Subject: [PATCH 05/13] Address review for: first set up of TPM DUK and renewal after firmware upg --- initrd/bin/kexec-save-default | 196 +++++++++++++++++++++++----------- initrd/bin/kexec-save-key | 42 ++++---- initrd/bin/kexec-seal-key | 2 +- initrd/bin/qubes-measure-luks | 21 ++-- initrd/etc/functions | 2 +- 5 files changed, 169 insertions(+), 94 deletions(-) diff --git a/initrd/bin/kexec-save-default b/initrd/bin/kexec-save-default index a38316e5..ca10e96e 100755 --- a/initrd/bin/kexec-save-default +++ b/initrd/bin/kexec-save-default @@ -45,6 +45,8 @@ if [ "$num_lvm" -eq 1 ] && [ -n "$lvm_suggest" ]; then elif [ -z "$lvm_suggest" ]; then num_lvm=0 fi +# $lvm_suggest is a multiline string, we need to convert it to a space separated string +lvm_suggest=$(echo "$lvm_suggest" | xargs) DEBUG "LVM num_lvm: $num_lvm, lvm_suggest: $lvm_suggest" # get all LUKS container devices @@ -55,9 +57,11 @@ 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) +elif [ -z "$devices_suggest" ]; then + num_devices=0 fi +# $devices_suggest is a multiline string, we need to convert it to a space separated string +devices_suggest=$(echo "$devices_suggest" | xargs) DEBUG "LUKS num_devices: $num_devices, devices_suggest: $devices_suggest" if [ "$num_lvm" -eq 0 ] && [ "$num_devices" -eq 0 ]; then @@ -65,6 +69,113 @@ if [ "$num_lvm" -eq 0 ] && [ "$num_devices" -eq 0 ]; then no_encrypted_partition=1 fi +#Reusable function when user wants to define new TPM DUK for lvms/disks +prompt_for_existing_encrypted_lvms_or_disks() { + TRACE "Under kexec-save-default:prompt_for_existing_encrypted_lvms_or_disks" + DEBUG "num_lvm: $num_lvm, lvm_suggest: $lvm_suggest, num_devices: $num_devices, devices_suggest: $devices_suggest" + + # Create an associative array to store the suggested LVMs and their paths + declare -A lvms_array + # Loop through the suggested LVMs and add them to the array + for lvm in $lvm_suggest; do + lvms_array[$lvm]=$lvm + done + + # Get the number of suggested LVMs + num_lvms=${#lvms_array[@]} + + if [ "$num_lvms" -gt 1 ]; then + DEBUG "Multiple LVMs found: $lvm_suggest" + selected_lvms_not_existing=1 + # Create an array to store the selected LVMs + declare -a key_lvms_array + + while [ $selected_lvms_not_existing -ne 0 ]; do + { + # Read the user input and store it in a variable + read \ + -p "Encrypted LVMs? (choose between/all: $lvm_suggest): " \ + key_lvms + + # Split the user input by spaces and add each element to the array + IFS=' ' read -r -a key_lvms_array <<<"$key_lvms" + + # Loop through the array and check if each element is in the lvms_array + valid=1 + for lvm in "${key_lvms_array[@]}"; do + if [[ ! ${lvms_array[$lvm]+_} ]]; then + # If not found, set the flag to indicate invalid input + valid=0 + break + fi + done + + # If valid, set the flag to indicate valid input + if [[ $valid -eq 1 ]]; then + selected_lvms_not_existing=0 + fi + } + done + elif [ "$num_lvms" -eq 1 ]; then + echo "Single Encrypted LVM found at $lvm_suggest." + key_lvms=$lvm_suggest + else + echo "No encrypted LVMs found." + fi + + # Create an associative array to store the suggested devices and their paths + declare -A devices_array + # Loop through the suggested devices and add them to the array + for device in $devices_suggest; do + devices_array[$device]=$device + done + + # Get the number of suggested devices + num_devices=${#devices_array[@]} + + if [ "$num_devices" -gt 1 ]; then + DEBUG "Multiple LUKS devices found: $devices_suggest" + selected_luksdevs_not_existing=1 + # Create an array to store the selected devices + declare -a key_devices_array + + while [ $selected_luksdevs_not_existing -ne 0 ]; do + { + # Read the user input and store it in a variable + read \ + -p "Encrypted devices? (choose between/all: $devices_suggest): " \ + key_devices + + # Split the user input by spaces and add each element to the array + IFS=' ' read -r -a key_devices_array <<<"$key_devices" + + # Loop through the array and check if each element is in the devices_array + valid=1 + for device in "${key_devices_array[@]}"; do + if [[ ! ${devices_array[$device]+_} ]]; then + # If not found, set the flag to indicate invalid input + valid=0 + break + fi + done + + # If valid, set the flag to indicate valid input + if [[ $valid -eq 1 ]]; then + selected_luksdevs_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 + + DEBUG "Multiple LUKS devices selected: $key_devices" + +} + if [ ! -r "$TMP_MENU_FILE" ]; then die "No menu options available, please run kexec-select-boot" fi @@ -75,24 +186,13 @@ if [ -z "$entry" ]; then fi 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 set up from $KEY_DEVICES" - if [ ! -r "$KEY_DEVICES" ]; then - DEBUG "No previous TPM Disk Unlock Key was set up 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]: " \ - add_key_confirm - echo - - if [ "$add_key_confirm" = "y" \ - -o "$add_key_confirm" = "Y" ]; then - DEBUG "User confirmed to add a disk encryption to the TPM" - save_key="y" - fi - else - DEBUG "Previous TPM Disk Unlock Key was set up for LUKS devices" + #check if $KEY_DEVICES file exists and is not empty + if [ -r "$KEY_DEVICES" ] && [ -s "$KEY_DEVICES" ]; then + DEBUG "TPM Disk Unlock Key was previously set up from $KEY_DEVICES" read \ -n 1 \ -p "Do you want to reseal a disk key to the TPM [y/N]: " \ @@ -117,6 +217,19 @@ if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ devices_suggest="$old_key_devices" save_key="y" fi + else + DEBUG "No previous TPM Disk Unlock Key was set up 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]: " \ + add_key_confirm + echo + + if [ "$add_key_confirm" = "y" \ + -o "$add_key_confirm" = "Y" ]; then + DEBUG "User confirmed to add a disk encryption to the TPM" + save_key="y" + fi fi if [ "$save_key" = "y" ]; then @@ -134,54 +247,13 @@ if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ if [ -z "$lvm_volume_group" ] && [ -n "$old_lvm_volume_group" ]; then lvm_volume_group="$old_lvm_volume_group" fi + #User doesn't want to reuse past devices, so we need to prompt him from devices_suggest and lvm_suggest + else + prompt_for_existing_encrypted_lvms_or_disks fi else DEBUG "No previous TPM Disk Unlock Key was set up for LUKS devices, setting up new ones" - if [ "$num_lvm" -gt 1 ]; then - DEBUG "Multiple LVM groups found" - # TODO: untested codepath. What uses lvm+LUKS on top nowadays?!? - 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 - - 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 - - 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 - - result=$(echo "$devices_suggest" | grep -q "$key_devices") || selected_luksdev_not_existing=1 - #if result is not empty, then the device exists - if [ -n "$result" ]; 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 + prompt_for_existing_encrypted_lvms_or_disks fi save_key_params="-s -p $paramsdev" diff --git a/initrd/bin/kexec-save-key b/initrd/bin/kexec-save-key index e9d22b34..be92ba32 100755 --- a/initrd/bin/kexec-save-key +++ b/initrd/bin/kexec-save-key @@ -11,16 +11,16 @@ lvm_volume_group="" skip_sign="n" while getopts "sp:d:l:" arg; do case $arg in - s) skip_sign="y" ;; - p) paramsdir="$OPTARG" ;; - d) paramsdev="$OPTARG" ;; - l) lvm_volume_group="$OPTARG" ;; + s) skip_sign="y" ;; + p) paramsdir="$OPTARG" ;; + d) paramsdev="$OPTARG" ;; + 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` +shift $(expr $OPTIND - 1) key_devices="$@" DEBUG "kexec-save-key: key_devices: $key_devices" @@ -40,9 +40,9 @@ 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" - + lvm vgchange -a y $lvm_volume_group || + die "Failed to activate the LVM group" + #TODO: why reuse key_devices for lvm devices? for dev in /dev/$lvm_volume_group/*; do key_devices="$key_devices $dev" done @@ -58,22 +58,22 @@ 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 " + 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" + DEBUG "Getting 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 -kexec-seal-key $paramsdir \ -|| die "Failed to save and generate key in TPM" +kexec-seal-key $paramsdir || + die "Failed to save and generate key in TPM" if [ "$skip_sign" != "y" ]; then extparam= @@ -81,9 +81,9 @@ if [ "$skip_sign" != "y" ]; then extparam=-r fi # sign and auto-roll config counter - kexec-sign-config -p $paramsdir $extparam \ - || die "Failed to sign updated config" + kexec-sign-config -p $paramsdir $extparam || + die "Failed to sign updated config" fi # switch back to ro mode -mount -o ro,remount $paramsdev \ No newline at end of file +mount -o ro,remount $paramsdev diff --git a/initrd/bin/kexec-seal-key b/initrd/bin/kexec-seal-key index e9f3248e..51c258db 100755 --- a/initrd/bin/kexec-seal-key +++ b/initrd/bin/kexec-seal-key @@ -27,7 +27,7 @@ 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)" + DEBUG "Devices defined for disk encryption: $(cat "$KEY_DEVICES" | cut -d\ -f1 | tr '\n' ' ')" fi if [ -r "$KEY_LVM" ]; then diff --git a/initrd/bin/qubes-measure-luks b/initrd/bin/qubes-measure-luks index 257ee861..782dc8de 100755 --- a/initrd/bin/qubes-measure-luks +++ b/initrd/bin/qubes-measure-luks @@ -1,21 +1,24 @@ #!/bin/bash -# Measure all of the luks disk encryption headers into +# Measure all of the LUKS Disk Encryption headers into # a PCR so that we can detect disk swap attacks. . /etc/functions TRACE "Under /bin/qubes-measure-luks" +DEBUG "Arguments passed to qubes-measure-luks: $@" -die() { echo >&2 "$@"; exit 1; } - -# Measure the luks headers into PCR 6 +# Measure the LUKS headers into PCR 6 for dev in "$@"; do + DEBUG "Storing LUKS header for $dev into /tmp/lukshdr-$(echo "$dev" | sed 's/\//_/g')" cryptsetup luksHeaderBackup $dev \ - --header-backup-file /tmp/lukshdr-$(echo "$dev" | sed 's/\//_/g') \ - || die "$dev: Unable to read luks header" + --header-backup-file /tmp/lukshdr-$(echo "$dev" | sed 's/\//_/g') || + die "$dev: Unable to read LUKS header" done -sha256sum /tmp/lukshdr-* > /tmp/luksDump.txt || die "Unable to hash luks headers" +DEBUG "Hashing luks headers into /tmp/luksDump.txt" +sha256sum /tmp/lukshdr-* >/tmp/luksDump.txt || die "Unable to hash luks headers" +DEBUG "Removing /tmp/lukshdr-*" rm /tmp/lukshdr-* -tpmr extend -ix 6 -if /tmp/luksDump.txt \ -|| die "Unable to extend PCR" +DEBUG "Extending PCR 6 with /tmp/luksDump.txt" +tpmr extend -ix 6 -if /tmp/luksDump.txt || + die "Unable to extend PCR" diff --git a/initrd/etc/functions b/initrd/etc/functions index 4ea7abed..84a8592a 100755 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -83,7 +83,7 @@ confirm_totp() { reseal_tpm_disk_decryption_key() { TRACE "Under /etc/functions:reseal_tpm_disk_decryption_key" #For robustness, exit early if TPM Disk Unlock Key is prohibited in board configs - if [ "$CONFIG_TPM_DISK_UNLOCK_KEY" != "y" ]; then + if [ "$CONFIG_TPM_DISK_UNLOCK_KEY" == "n" ]; then DEBUG "TPM Disk Unlock Key is prohibited in board configs" return else From a2a30020c093b12801b1744e67db1487e5e26eb3 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Fri, 1 Sep 2023 16:28:53 -0400 Subject: [PATCH 06/13] TPM Disk Unlock Key setup: use unpack_initrd.sh, replace none with /secret.key. Still no joy --- initrd/bin/kexec-save-default | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/initrd/bin/kexec-save-default b/initrd/bin/kexec-save-default index ca10e96e..3f6811b3 100755 --- a/initrd/bin/kexec-save-default +++ b/initrd/bin/kexec-save-default @@ -300,13 +300,13 @@ if [ "$save_key" = "y" ]; then # 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 + initrd_decompressed="/tmp/initrd_extract" + echo "+++ Extracting current selected default boot's $current_default_initrd to find crypttab files..." + unpack_initramfs.sh "$current_default_initrd" "$initrd_decompressed" >/dev/null 2>&1 || true + crypttab_files=$(find "$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 $crypttab_files <$initrd_decompressed 2>/dev/null + DEBUG "Found crypttab files in $current_default_initrd" rm -f $bootdir/kexec_initrd_crypttab_overrides.txt || true #Parsing each crypttab file found From 0ba10e5174ab9b5f71dc8cc30420740ca384746c Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Fri, 1 Sep 2023 18:19:29 -0400 Subject: [PATCH 07/13] path substitution still not working. This is PoC to be tested. Had to go --- initrd/bin/kexec-save-default | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/initrd/bin/kexec-save-default b/initrd/bin/kexec-save-default index 3f6811b3..cec191db 100755 --- a/initrd/bin/kexec-save-default +++ b/initrd/bin/kexec-save-default @@ -313,14 +313,22 @@ if [ "$save_key" = "y" ]; then echo "$crypttab_files" | while read filepath; do # Keep only non-commented lines current_filepath_entries=$(cat "$filepath" | grep -v "^#") + DEBUG "Found crypttab entries in $filepath: $current_filepath_entries" # Modify each retained crypttab line to contain to be injected /secret.key at next default boots modified_filepath_entries=$(echo "$current_filepath_entries" | sed 's/none/\/secret.key/g') + DEBUG "Modified crypttab entries in $filepath: $modified_filepath_entries" + # Mofify paths to remove path of where files were extracted to be those files need to be put in initramfs + modified_filepath_entries=$(echo "$modified_filepath_entries" | awk -v var="$initrd_decompressed" '{gsub(var,"")}1') + DEBUG "Modified crypttab filepath entries in $filepath: $modified_filepath_entries" + # Get the relative path of the filepath + modified_filepath=$(echo "$filepath" | cut -d'/' -f2-) 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 "$modified_filepath:$single_modified_filepath_entry" >>$bootdir/kexec_initrd_crypttab_overrides.txt done done + #insert current default boot's initrd crypttab locations into tracking file to be overwritten into initramfs at kexec-inject-key echo "+++ The following OS crypttab file:entry were modified from default boot's initrd:" cat $bootdir/kexec_initrd_crypttab_overrides.txt From e9dbce2adfda6306b6496b8d83d4c2aa513a38ff Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sat, 2 Sep 2023 01:51:50 -0400 Subject: [PATCH 08/13] bin/unpack_initramfs.sh: Add TRACE and DEBUG traces --- initrd/bin/unpack_initramfs.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/initrd/bin/unpack_initramfs.sh b/initrd/bin/unpack_initramfs.sh index 8c1a5cd4..cbefc10a 100755 --- a/initrd/bin/unpack_initramfs.sh +++ b/initrd/bin/unpack_initramfs.sh @@ -3,6 +3,7 @@ set -e -o pipefail . /etc/functions +TRACE "Under unpack_initramfs.sh" # Unpack a Linux initramfs archive. # # In general, the initramfs archive is one or more cpio archives, optionally @@ -30,6 +31,7 @@ CPIO_ARGS=("$@") # Consume zero bytes, the first nonzero byte read (if any) is repeated on stdout consume_zeros() { + TRACE "Under unpack_initramfs.sh:consume_zeros" next_byte='00' while [ "$next_byte" = "00" ]; do # if we reach EOF, next_byte becomes empty (dd does not fail) @@ -42,11 +44,13 @@ consume_zeros() { } unpack_cpio() { + TRACE "Under unpack_initramfs.sh:unpack_cpio" (cd "$dest_dir"; cpio -i "${CPIO_ARGS[@]}" 2>/dev/null) } # unpack the first segment of an archive, then write the rest to another file unpack_first_segment() { + TRACE "Under unpack_initramfs.sh:unpack_first_segment" unpack_archive="$1" dest_dir="$2" rest_archive="$3" @@ -62,18 +66,21 @@ unpack_first_segment() { # lib/decompress.c (gzip) case "$magic" in 00*) + DEBUG "archive segment $magic: uncompressed cpio" # Skip zero bytes and copy the first nonzero byte consume_zeros # Copy the remaining data cat ;; 303730373031*|303730373032*) # plain cpio + DEBUG "archive segment $magic: plain cpio" # Unpack the plain cpio, this stops reading after the trailer unpack_cpio # Copy the remaining data cat ;; 1f8b*|1f9e*) # gzip + DEBUG "archive segment $magic: gzip" # gunzip won't stop when reaching the end of the gzipped member, # so we can't read another segment after this. We can't # reasonably determine the member length either, this requires @@ -81,6 +88,7 @@ unpack_first_segment() { gunzip | unpack_cpio ;; 28b5*) # zstd + DEBUG "archive segment $magic: zstd" # Like gunzip, this will not stop when reaching the end of the # frame, and determining the frame length requires walking all # of its blocks. From 52947e276713ebea357388d06a91ea222092c5b5 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sat, 2 Sep 2023 01:53:31 -0400 Subject: [PATCH 09/13] WiP TPM DUK cleanup --- initrd/bin/kexec-save-default | 13 +++++-------- initrd/bin/kexec-seal-key | 4 ++-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/initrd/bin/kexec-save-default b/initrd/bin/kexec-save-default index cec191db..94e27c8c 100755 --- a/initrd/bin/kexec-save-default +++ b/initrd/bin/kexec-save-default @@ -317,18 +317,15 @@ if [ "$save_key" = "y" ]; then # Modify each retained crypttab line to contain to be injected /secret.key at next default boots modified_filepath_entries=$(echo "$current_filepath_entries" | sed 's/none/\/secret.key/g') DEBUG "Modified crypttab entries in $filepath: $modified_filepath_entries" - # Mofify paths to remove path of where files were extracted to be those files need to be put in initramfs - modified_filepath_entries=$(echo "$modified_filepath_entries" | awk -v var="$initrd_decompressed" '{gsub(var,"")}1') - DEBUG "Modified crypttab filepath entries in $filepath: $modified_filepath_entries" - # Get the relative path of the filepath - modified_filepath=$(echo "$filepath" | cut -d'/' -f2-) - echo "$modified_filepath_entries" | while read single_modified_filepath_entry; do + # Get the relative path of the filepath which will correspond to local path of the crypttab file in initrd + initrd_filepath_entries=$(echo "$modified_filepath_entries" | cut -d'/' -f3-) + DEBUG "Modified crypttab initrd local path entries in $filepath: $initrd_filepath_entries" + echo "$initrd_filepath_entries" | while read initrd_filepath_entry; do # Append each found filepath:entry into additional kexec_ file that will be part of detached signed digest - echo "$modified_filepath:$single_modified_filepath_entry" >>$bootdir/kexec_initrd_crypttab_overrides.txt + echo "$modified_filepath_entries:$initrd_filepath_entry" >>$bootdir/kexec_initrd_crypttab_overrides.txt done done - #insert current default boot's initrd crypttab locations into tracking file to be overwritten into initramfs at kexec-inject-key echo "+++ The following OS crypttab file:entry were modified from default boot's initrd:" cat $bootdir/kexec_initrd_crypttab_overrides.txt diff --git a/initrd/bin/kexec-seal-key b/initrd/bin/kexec-seal-key index 51c258db..b405abeb 100755 --- a/initrd/bin/kexec-seal-key +++ b/initrd/bin/kexec-seal-key @@ -81,10 +81,10 @@ for dev in $(cat "$KEY_DEVICES" | cut -d\ -f1); do # 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" + die "Slot 1 should not be the only slot existing on $dev. Fix your custom setup" fi else - DEBUG "Slot 1 is not the only one existing on $dev" + DEBUG "Slot 1 is not the only existing slot on $dev" DEBUG "$dev Slot 1 will be used to store LUKS Disk Unlock Key that will be sealed into TPM next" fi done From 51b1ad39c33eefc6c496f80e259d7afe9b65f473 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sat, 2 Sep 2023 04:16:16 -0400 Subject: [PATCH 10/13] sbin/insmod wrapper: Add TRACE and DEBUG traces --- initrd/sbin/insmod | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/initrd/sbin/insmod b/initrd/sbin/insmod index ce1adefd..39d205b0 100755 --- a/initrd/sbin/insmod +++ b/initrd/sbin/insmod @@ -4,10 +4,9 @@ # The default PCR to be extended is 5, but can be # overridden with the MODULE_PCR environment variable -die() { - echo >&2 "$@" - exit 1 -} +. /etc/functions + +TRACE "Under insmod.sh" MODULE="$1"; shift @@ -31,18 +30,22 @@ if [ ! -r /sys/class/tpm/tpm0/pcrs -o ! -x /bin/tpm ]; then fi if [ -z "$tpm_missing" ]; then + DEBUG "Extending PCR $MODULE_PCR with $MODULE" tpmr extend -ix "$MODULE_PCR" -if "$MODULE" \ || die "$MODULE: tpm extend failed" fi if [ ! -z "$*" -a -z "$tpm_missing" ]; then + DEBUG "Extending PCR $MODULE_PCR with $*" TMPFILE=/tmp/insmod.$$ echo "$@" > $TMPFILE + DEBUG "Extending PCR $MODULE_PCR with $TMPFILE" tpmr extend -ix "$MODULE_PCR" -if $TMPFILE \ || die "$MODULE: tpm extend on arguments failed" fi # Since we have replaced the real insmod, we must invoke # the busybox insmod via the original executable +DEBUG "Loading $MODULE with busybox insmod" busybox insmod "$MODULE" "$@" \ || die "$MODULE: insmod failed" From 8b0fc0f1296e26e3c36095adc0657caa62a6edbf Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sat, 2 Sep 2023 04:17:50 -0400 Subject: [PATCH 11/13] kexec-seal/save-key /etc/functions : some more uniformisation of TPM DUK verbiage --- initrd/bin/kexec-save-key | 2 +- initrd/bin/kexec-seal-key | 4 ++-- initrd/etc/functions | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/initrd/bin/kexec-save-key b/initrd/bin/kexec-save-key index be92ba32..3ba8e932 100755 --- a/initrd/bin/kexec-save-key +++ b/initrd/bin/kexec-save-key @@ -73,7 +73,7 @@ for dev in $key_devices; do done kexec-seal-key $paramsdir || - die "Failed to save and generate key in TPM" + die "Failed to save and generate TPM Disk Unlock Key" if [ "$skip_sign" != "y" ]; then extparam= diff --git a/initrd/bin/kexec-seal-key b/initrd/bin/kexec-seal-key index b405abeb..fe36808c 100755 --- a/initrd/bin/kexec-seal-key +++ b/initrd/bin/kexec-seal-key @@ -61,7 +61,7 @@ if [ "$key_password" != "$key_password2" ]; then fi # Generate key file -echo "++++++ Generating new randomized 128 bytes key file that will be unsealed by TPM Disk Unlock Key passphrase" +echo "++++++ Generating new randomized 128 bytes key file that will be sealed/unsealed by TPM Disk Unlock Key passphrase" dd \ if=/dev/urandom \ of="$KEY_FILE" \ @@ -85,7 +85,7 @@ for dev in $(cat "$KEY_DEVICES" | cut -d\ -f1); do fi else DEBUG "Slot 1 is not the only existing slot on $dev" - DEBUG "$dev Slot 1 will be used to store LUKS Disk Unlock Key that will be sealed into TPM next" + DEBUG "$dev Slot 1 will be used to store LUKS Disk Unlock Key that TPM will seal/unseal with TPM Disk Unlock Key passphrase" fi done diff --git a/initrd/etc/functions b/initrd/etc/functions index 84a8592a..2163587b 100755 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -96,7 +96,7 @@ reseal_tpm_disk_decryption_key() { fi if [ -s /boot/kexec_key_devices.txt ] || [ -s /boot/kexec_key_lvm.txt ]; then - warn "A TPM Disk Unlock Key previously sealed is now invalid since firmware measurements cannot be unsealed" + warn "A TPM Disk Unlock Key previously sealed is now invalid since firmware measurements could not unseal TOTP" echo "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!" From e291797e655b524db27f69bba34a6b6d8048fcd3 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sat, 2 Sep 2023 04:21:08 -0400 Subject: [PATCH 12/13] kexec-save-default : Finally fix #1474 under #1482 --- initrd/bin/kexec-save-default | 37 +++++++++++++++-------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/initrd/bin/kexec-save-default b/initrd/bin/kexec-save-default index 94e27c8c..ad292e77 100755 --- a/initrd/bin/kexec-save-default +++ b/initrd/bin/kexec-save-default @@ -263,7 +263,7 @@ if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ save_key_params="$save_key_params $key_devices" fi kexec-save-key $save_key_params || - die "Failed to save the disk key" + die "Failed to save the TPM Disk Unlock Key" fi fi @@ -292,17 +292,13 @@ fi if [ "$save_key" = "y" ]; then # logic to parse OS initrd to extract crypttab, its filepaths and its OS defined options - mkdir -p /tmp/initrd_extract - cd /tmp/initrd_extract + initrd_decompressed="/tmp/initrd_extract" + mkdir -p "$initrd_decompressed" # 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" echo "+++ Extracting current selected default boot's $current_default_initrd to find crypttab files..." - unpack_initramfs.sh "$current_default_initrd" "$initrd_decompressed" >/dev/null 2>&1 || true + unpack_initramfs.sh "$current_default_initrd" "$initrd_decompressed" crypttab_files=$(find "$initrd_decompressed" | grep crypttab 2>/dev/null) || true if [ ! -z "$crypttab_files" ]; then @@ -310,19 +306,18 @@ if [ "$save_key" = "y" ]; then 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 - current_filepath_entries=$(cat "$filepath" | grep -v "^#") - DEBUG "Found crypttab entries in $filepath: $current_filepath_entries" - # Modify each retained crypttab line to contain to be injected /secret.key at next default boots - modified_filepath_entries=$(echo "$current_filepath_entries" | sed 's/none/\/secret.key/g') - DEBUG "Modified crypttab entries in $filepath: $modified_filepath_entries" - # Get the relative path of the filepath which will correspond to local path of the crypttab file in initrd - initrd_filepath_entries=$(echo "$modified_filepath_entries" | cut -d'/' -f3-) - DEBUG "Modified crypttab initrd local path entries in $filepath: $initrd_filepath_entries" - echo "$initrd_filepath_entries" | while read initrd_filepath_entry; do - # Append each found filepath:entry into additional kexec_ file that will be part of detached signed digest - echo "$modified_filepath_entries:$initrd_filepath_entry" >>$bootdir/kexec_initrd_crypttab_overrides.txt + echo "$crypttab_files" | while read crypttab_file; do + # Change crypttab file path to be relative to initrd for string manipulation + final_initrd_filepath=${crypttab_file#/tmp/initrd_extract} + DEBUG "Final initramfs crypttab path:$final_initrd_filepath" + # Keep only non-commented lines for crypttab entries + current_crypttab_entries=$(cat "$crypttab_file" | grep -v "^#") + DEBUG "Found initrd crypttab entries $final_initrd_filepath:$current_crypttab_entries" + # Modify each retained crypttab line for /secret.key under intramfs to be considered as a keyfile + modified_crypttab_entries=$(echo "$current_crypttab_entries" | sed 's/none/\/secret.key/g') + DEBUG "Modified crypttab entries $final_initrd_filepath:$modified_crypttab_entries" + echo "$modified_crypttab_entries" | while read modified_crypttab_entry; do + echo "$final_initrd_filepath:$modified_crypttab_entry" >>$bootdir/kexec_initrd_crypttab_overrides.txt done done From 47eba7d80b49a3cec2c41a048acd30fc5a394056 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Sat, 2 Sep 2023 11:49:57 -0400 Subject: [PATCH 13/13] kexec-save-default: Fix multiple LUKS/LVM+LUKS suggestion + other working uniformization for DUK --- initrd/bin/kexec-save-default | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/initrd/bin/kexec-save-default b/initrd/bin/kexec-save-default index ad292e77..d4f26c85 100755 --- a/initrd/bin/kexec-save-default +++ b/initrd/bin/kexec-save-default @@ -46,7 +46,7 @@ elif [ -z "$lvm_suggest" ]; then num_lvm=0 fi # $lvm_suggest is a multiline string, we need to convert it to a space separated string -lvm_suggest=$(echo "$lvm_suggest" | xargs) +lvm_suggest=$(echo $lvm_suggest | tr '\n' ' ') DEBUG "LVM num_lvm: $num_lvm, lvm_suggest: $lvm_suggest" # get all LUKS container devices @@ -61,7 +61,7 @@ elif [ -z "$devices_suggest" ]; then num_devices=0 fi # $devices_suggest is a multiline string, we need to convert it to a space separated string -devices_suggest=$(echo "$devices_suggest" | xargs) +devices_suggest=$(echo $devices_suggest | tr '\n' ' ') DEBUG "LUKS num_devices: $num_devices, devices_suggest: $devices_suggest" if [ "$num_lvm" -eq 0 ] && [ "$num_devices" -eq 0 ]; then @@ -218,7 +218,7 @@ if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ save_key="y" fi else - DEBUG "No previous TPM Disk Unlock Key was set up for LUKS devices, confirming to add a disk encryption to the TPM" + DEBUG "No previous TPM Disk Unlock Key was set up for LUKS devices, confirming to add a Disk Encryption Key to the TPM" read \ -n 1 \ -p "Do you wish to add a disk encryption to the TPM [y/N]: " \ @@ -227,7 +227,7 @@ if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ if [ "$add_key_confirm" = "y" \ -o "$add_key_confirm" = "Y" ]; then - DEBUG "User confirmed to add a disk encryption to the TPM" + DEBUG "User confirmed desire to add a Disk Encryption Key to the TPM" save_key="y" fi fi @@ -252,7 +252,7 @@ if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ prompt_for_existing_encrypted_lvms_or_disks fi else - DEBUG "No previous TPM Disk Unlock Key was set up for LUKS devices, setting up new ones" + DEBUG "No previous TPM Disk Unlock Key was set up for LUKS devices, setting up new one" prompt_for_existing_encrypted_lvms_or_disks fi