diff --git a/initrd/bin/kexec-insert-key b/initrd/bin/kexec-insert-key index 1b8632c02..4d0fc930d 100755 --- a/initrd/bin/kexec-insert-key +++ b/initrd/bin/kexec-insert-key @@ -45,6 +45,7 @@ mkdir -p "$INITRD_DIR/etc" unseal_failed="n" if ! kexec-unseal-key "$INITRD_DIR/secret.key"; then unseal_failed="y" + echo echo "!!! Failed to unseal the TPM LUKS disk key" fi diff --git a/initrd/bin/kexec-save-default b/initrd/bin/kexec-save-default index e21dd13ea..2527dcc86 100755 --- a/initrd/bin/kexec-save-default +++ b/initrd/bin/kexec-save-default @@ -1,7 +1,9 @@ #!/bin/bash # Save these options to be the persistent default set -e -o pipefail +# shellcheck disable=SC1091 . /tmp/config +# shellcheck disable=SC1091 . /etc/functions TRACE "Under /bin/kexec-save-default" @@ -12,6 +14,7 @@ while getopts "b:d:p:i:" arg; do d) paramsdev="$OPTARG" ;; p) paramsdir="$OPTARG" ;; i) index="$OPTARG" ;; + *) echo "Invalid flag: -$OPTARG" >&2 ;; esac done @@ -38,54 +41,23 @@ 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 -# $lvm_suggest is a multiline string, we need to convert it to a space separated string -lvm_suggest=$(echo $lvm_suggest | tr '\n' ' ') -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 - if cryptsetup isLuks "$device"; 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 -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 | tr '\n' ' ') -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 - #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" + 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 + 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" + DEBUG "Multiple LVMs found: ${lvm_suggest[*]}" selected_lvms_not_existing=1 # Create an array to store the selected LVMs declare -a key_lvms_array @@ -94,7 +66,7 @@ prompt_for_existing_encrypted_lvms_or_disks() { { # Read the user input and store it in a variable read \ - -p "Encrypted LVMs? (choose between/all: $lvm_suggest): " \ + -p "Encrypted LVMs? (choose between/all: ${lvm_suggest[*]}): " \ key_lvms # Split the user input by spaces and add each element to the array @@ -117,8 +89,8 @@ prompt_for_existing_encrypted_lvms_or_disks() { } done elif [ "$num_lvms" -eq 1 ]; then - echo "Single Encrypted LVM found at $lvm_suggest." - key_lvms=$lvm_suggest + echo "Single Encrypted LVM found at ${lvm_suggest[*]}." + key_lvms=("${lvm_suggest[@]}") else echo "No encrypted LVMs found." fi @@ -126,15 +98,16 @@ prompt_for_existing_encrypted_lvms_or_disks() { # 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 + for device in ${devices_suggest[@]}; do + devices_array["$device"]=$device done - # Get the number of suggested devices + DEBUG "LUKS num_devices before array: $num_devices, devices_array: ${devices_array[*]}" num_devices=${#devices_array[@]} + DEBUG "LUKS num_devices after array: $num_devices, devices_array: ${devices_array[*]}" if [ "$num_devices" -gt 1 ]; then - DEBUG "Multiple LUKS devices found: $devices_suggest" + 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 @@ -143,7 +116,7 @@ prompt_for_existing_encrypted_lvms_or_disks() { { # Read the user input and store it in a variable read \ - -p "Encrypted devices? (choose between/all: $devices_suggest): " \ + -p "Encrypted devices? (choose between/all: ${devices_suggest[*]}): " \ key_devices # Split the user input by spaces and add each element to the array @@ -165,16 +138,53 @@ prompt_for_existing_encrypted_lvms_or_disks() { fi } done + DEBUG "devices_suggest: ${devices_suggest[*]}, key_devices: $key_devices, key_devices_array: ${key_devices_array[*]}" + DEBUG "Multiple LUKS devices selected: $key_devices" elif [ "$num_devices" -eq 1 ]; then - echo "Single Encrypted Disk found at $devices_suggest." - key_devices=$devices_suggest + echo "Single Encrypted Disk found at ${devices_suggest[*]}." + key_devices=("${devices_suggest[@]}") else echo "No encrypted devices found." fi +} + +# get all LVM volume groups +lvm_suggest=($(lvm vgscan | awk -F '"' '{print $1}' | tail -n +2)) +num_lvm=${#lvm_suggest[@]} +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[*]}" - DEBUG "Multiple LUKS devices selected: $key_devices" +# get all LUKS container devices +devices_suggest=($(blkid | cut -d ':' -f 1 | while read device; do + if cryptsetup isLuks "$device"; then echo "$device"; fi +done)) +num_devices=${#devices_suggest[@]} -} +if [ "$num_devices" -eq 1 ] && [ -s "${devices_suggest[*]}" ]; then + key_devices=("${devices_suggest[@]}") +elif [ -z "${devices_suggest[*]}" ]; then + num_devices=0 +fi + +# Create an 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 + +# $devices_suggest is a multiline string, we need to convert it to a space separated string +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 + #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" @@ -192,10 +202,17 @@ if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ DEBUG "Checking if a a TPM Disk Unlock Key was previously set up from $KEY_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" + DEBUG "A TPM Disk Unlock Key was previously set up from $KEY_DEVICES" + #TODO: nobody complains about encrypted lvms missing, we might want to deprecate encrypted lvm logic: not aware of anyone using that anymore + + #Extract key devices from $KEY_DEVICES file + key_devices=$(cat "$KEY_DEVICES" | + cut -d\ -f1 | xargs) || true + + warn "Reseal a TPM Disk Unlock Key only if a new key/passphrase is desired for $key_devices" read \ -n 1 \ - -p "Do you want to reseal a disk key to the TPM [y/N]: " \ + -p "Do you want to reseal a Disk Unlock Key for $key_devices to the TPM [y/N]: " \ change_key_confirm echo @@ -203,18 +220,18 @@ 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 | + 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 | + old_key_devices=$(cat "$KEY_DEVICES" | cut -d\ -f1 | xargs) || true fi - lvm_suggest="$old_lvm_volume_group" - devices_suggest="$old_key_devices" + lvm_suggest=("$old_lvm_volume_group") + devices_suggest=("$old_key_devices") save_key="y" fi else @@ -234,18 +251,18 @@ 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 set up 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):" \ 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" + 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" + 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 @@ -256,22 +273,23 @@ if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ] && [ prompt_for_existing_encrypted_lvms_or_disks fi - save_key_params="-s -p $paramsdev" - if [ -n "$lvm_volume_group" ]; then - save_key_params="$save_key_params -l $lvm_volume_group $key_devices" + save_key_params=("-s" "-p" "$paramsdev") + if [ -n "${lvm_volume_group[*]}" ]; then + save_key_params+=("-l" "${lvm_volume_group[*]}" "${key_devices[*]}") else - save_key_params="$save_key_params $key_devices" + save_key_params+=("${key_devices[*]}") fi - kexec-save-key $save_key_params || + + kexec-save-key "${save_key_params[@]}" || die "Failed to save the TPM Disk Unlock Key" fi fi # try to switch to rw mode -mount -o rw,remount $paramsdev +mount -o rw,remount "$paramsdev" -if [ ! -d $paramsdir ]; then - mkdir -p $paramsdir || +if [ ! -d "$paramsdir" ]; then + mkdir -p "$paramsdir" || die "Failed to create params directory" fi @@ -281,13 +299,13 @@ if [ "$CONFIG_TPM2_TOOLS" = "y" ]; then DEBUG "TPM2 primary key handle hash saved to $PRIMHASH_FILE" fi -rm $paramsdir/kexec_default.*.txt 2>/dev/null || true -echo "$entry" >$ENTRY_FILE +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 + 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 +if [ ! -r "$ENTRY_FILE" -o ! -r "$HASH_FILE" ]; then die "Failed to write default config" fi @@ -304,10 +322,10 @@ if [ "$save_key" = "y" ]; then if [ ! -z "$crypttab_files" ]; then DEBUG "Found crypttab files in $current_default_initrd" - rm -f $bootdir/kexec_initrd_crypttab_overrides.txt || true + rm -f "$bootdir/kexec_initrd_crypttab_overrides.txt" || true #Parsing each crypttab file found - echo "$crypttab_files" | while read crypttab_file; do + echo "$crypttab_files" | while read -r 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" @@ -317,14 +335,14 @@ if [ "$save_key" = "y" ]; then # 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 + echo "$modified_crypttab_entries" | while read -r modified_crypttab_entry; do + echo "$final_initrd_filepath:$modified_crypttab_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 + cat "$bootdir/kexec_initrd_crypttab_overrides.txt" echo "+++ Heads added /secret.key in those entries and saved them under $bootdir/kexec_initrd_crypttab_overrides.txt" echo "+++ Those overrides will be part of detached signed digests and used to prepare cpio injected at kexec of selected default boot entry." else @@ -348,8 +366,8 @@ if [ "$CONFIG_TPM" = "y" ]; then fi fi if [ "$CONFIG_BASIC" != "y" ]; then - kexec-sign-config -p $paramsdir $extparam || + kexec-sign-config -p "$paramsdir" $extparam || die "Failed to sign default config" fi # switch back to ro mode -mount -o ro,remount $paramsdev +mount -o ro,remount "$paramsdev" diff --git a/initrd/bin/kexec-seal-key b/initrd/bin/kexec-seal-key index 03b64ab86..3c6a94d89 100755 --- a/initrd/bin/kexec-seal-key +++ b/initrd/bin/kexec-seal-key @@ -72,38 +72,57 @@ dd \ # 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 LUKS header" - #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 LUKS header: $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 LUKS header. Heads cannot use it to store TPM sealed LUKS Disk Unlock Key" - warn "Slot 1 should not be the only slot existing on $dev LUKS header. Slot 0 should be used to store Disk Recovery Key/passphrase" - die "You can safely fix this before continuing through Heads recovery shell: cryptsetup luksAddKey $dev" + DEBUG "Checking number of slots used on $dev LUKS header" + #check if the device is a LUKS device with luks[1,2] + # Get the number of key slots used on the LUKS header. + # LUKS1 Format is : + # Slot 0: ENABLED + # Slot 1: ENABLED + # Slot 2: DISABLED + # Slot 3: DISABLED + #... + # Slot 7: DISABLED + # Luks2 only reports on enabled slots. + # luks2 Format is : + # 0: luks2 + # 1: luks2 + # Meaning that the number of slots used is the number of lines returned by a grep on the LUKS2 above format. + # We need to count the number of ENABLED slots for both LUKS1 and LUKS2 + # create glob pattern for both LUKS1 and LUKS2 + glob="Slot [0-9]*: ENABLED" + glob+="\|" + glob+="[0-9]*: luks2" + slot_used=$(cryptsetup luksDump "$dev" | grep -c "$glob" || die "Unable to get number of slots used on $dev") + + DEBUG "Number of slots used on $dev LUKS header: $slot_used" + # If slot1 is the only one used, warn and die with proper messages + if [ "$slot_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 LUKS header. Heads cannot use it to store TPM sealed LUKS Disk Unlock Key" + warn "Slot 1 should not be the only slot existing on $dev LUKS header. Slot 0 should be used to store Disk Recovery Key/passphrase" + die "You can safely fix this before continuing through Heads recovery shell: cryptsetup luksAddKey $dev" + fi + else + DEBUG "Slot 1 is not the only existing slot on $dev LUKS header." + DEBUG "$dev LUKS header's slot 1 will store LUKS Disk Unlock Key that TPM will seal/unseal with TPM Disk Unlock Key passphrase" fi - else - DEBUG "Slot 1 is not the only existing slot on $dev LUKS header." - DEBUG "$dev LUKS header's slot 1 will store LUKS Disk Unlock Key that TPM will seal/unseal with TPM Disk Unlock Key passphrase" - fi -done + done # Remove all the old keys from slot 1 for dev in $(cat "$KEY_DEVICES" | cut -d\ -f1); do - echo "++++++ $dev: Removing old key slot 1" + echo "++++++ $dev: Removing old TPM Disk Unlock Key in LUKS slot 1" cryptsetup luksKillSlot \ --key-file "$RECOVERY_KEY" \ $dev 1 || - warn "$dev: removal of key in slot 1 failed: might not exist. Continuing" + warn "$dev: removal of TPM Disk Unlock Key in LUKS slot 1 failed: might not exist. Continuing" - echo "++++++ $dev: Adding key to slot 1" + echo "++++++ $dev: Adding TPM Disk Unlock Key to LUKS slot 1" cryptsetup luksAddKey \ --key-file "$RECOVERY_KEY" \ --key-slot 1 \ $dev "$KEY_FILE" || - die "$dev: Unable to add key to slot 1" + die "$dev: Unable to add TPM Disk Unlock Key to LUKS slot 1" done # Now that we have setup the new keys, measure the PCRs diff --git a/initrd/bin/kexec-unseal-key b/initrd/bin/kexec-unseal-key index b8c94b387..1a8ad129d 100755 --- a/initrd/bin/kexec-unseal-key +++ b/initrd/bin/kexec-unseal-key @@ -25,8 +25,9 @@ DEBUG "CONFIG_TPM2_TOOLS: $CONFIG_TPM2_TOOLS" DEBUG "Show PCRs" DEBUG "$(pcrs)" +failed=0 for tries in 1 2 3; do - read -s -p "Enter LUKS Disk Unlock Key passphrase (blank to abort): " tpm_password + read -s -p "Enter LUKS TPM Disk Unlock Key passphrase (blank to abort): " tpm_password echo if [ -z "$tpm_password" ]; then die "Aborting unseal disk encryption key" @@ -34,14 +35,15 @@ for tries in 1 2 3; do DO_WITH_DEBUG --mask-position 6 \ tpmr unseal "$TPM_INDEX" "0,1,2,3,4,5,6,7" "$TPM_SIZE" \ - "$key_file" "$tpm_password" + "$key_file" "$tpm_password" || failed=1 - if [ "$?" -eq 0 ]; then + if [ "$failed" -eq 0 ]; then exit 0 fi - pcrs - warn "Unable to unseal disk encryption key" + DEBUG pcrs # Show PCRs final state only in debug mode. TCPA/TPM Event log should only be visible in debug mode/from authenticated Recovery console + warn "Unable to unseal TPM Disk Unlock Key with provided passphrase. retry count: $tries/3." + warn "Caps Lock mode on? (All letters typed might have been uppercased)" done -die "Retry count exceeded..." +die "Retry count exceeded: TPM might be in a locked state until machine is cold rebooted (Power off, wait 30 seconds, power on)"