From 1f6a97594057e106118d6e2cb706968be88e7800 Mon Sep 17 00:00:00 2001 From: Thierry Laurion Date: Fri, 7 Feb 2025 13:18:17 -0500 Subject: [PATCH] Codebase up to TPM DUK: ident, add DEBUG+TRACE_FUNC, TRACE_FUNC now gives call hierarchy, fix HOTP resealing only on OS reinstall, clarify TPM increment workflow Signed-off-by: Thierry Laurion --- initrd/.bash_history | 4 +-- initrd/bin/generic-init | 4 +-- initrd/bin/gui-init | 41 ++++++++++++----------- initrd/bin/gui-init-basic | 6 ++-- initrd/bin/kexec-save-default | 10 +++--- initrd/bin/kexec-save-key | 3 +- initrd/bin/kexec-select-boot | 48 +++++++++++++-------------- initrd/bin/kexec-sign-config | 62 +++++++++++++++++++++++++++++------ initrd/bin/key-init | 18 +++++----- initrd/bin/oem-factory-reset | 20 +++++++---- initrd/bin/reboot | 2 +- initrd/bin/tpmr | 6 ++-- initrd/bin/unseal-hotp | 36 +++++++++++--------- initrd/bin/unseal-totp | 7 ++-- initrd/etc/functions | 62 ++++++++++++++++++++++++++--------- initrd/sbin/insmod | 6 ++-- 16 files changed, 212 insertions(+), 123 deletions(-) diff --git a/initrd/.bash_history b/initrd/.bash_history index 7f03d3ee..44fd6052 100644 --- a/initrd/.bash_history +++ b/initrd/.bash_history @@ -5,9 +5,9 @@ find /boot/kexec*.txt | gpg --verify /boot/kexec.sig - #remove invalid kexec_* signed files mount /dev/sda1 /boot && mount -o remount,rw /boot && rm /boot/kexec* && mount -o remount,ro /boot #Generate keys on OpenPGP smartcard: -mount-usb && gpg --home=/.gnupg/ --card-edit +mount-usb --mode rw && gpg --home=/.gnupg/ --card-edit #Copy generated public key, private_subkey, trustdb and artifacts to external media for backup: -mount -o remount,rw /media && mkdir -p /media/gpg_keys; gpg --export-secret-keys --armor email@address.com > /media/gpg_keys/private.key && gpg --export --armor email@address.com > /media/gpg_keys/public.key && gpg --export-ownertrust > /media/gpg_keys/otrust.txt && cp -r ./.gnupg/* /media/gpg_keys/ 2> /dev/null +mkdir -p /media/gpg_keys; gpg --export-secret-keys --armor email@address.com > /media/gpg_keys/private.key && gpg --export --armor email@address.com > /media/gpg_keys/public.key && gpg --export-ownertrust > /media/gpg_keys/otrust.txt && cp -r ./.gnupg/* /media/gpg_keys/ 2> /dev/null #Insert public key and trustdb export into reproducible rom: cbfs -o /media/coreboot.rom -a "heads/initrd/.gnupg/keys/public.key" -f /media/gpg_keys/public.key && cbfs -o /media/coreboot.rom -a "heads/initrd/.gnupg/keys/otrust.txt" -f /media/gpg_keys/otrust.txt #Flush changes to external media: diff --git a/initrd/bin/generic-init b/initrd/bin/generic-init index 0a4a17a6..a3b9f34e 100755 --- a/initrd/bin/generic-init +++ b/initrd/bin/generic-init @@ -48,14 +48,14 @@ while true; do if [ "$totp_confirm" = "m" ]; then # Try to select a kernel from the menu mount_boot - kexec-select-boot -m -b /boot -c "grub.cfg" + DO_WITH_DEBUG kexec-select-boot -m -b /boot -c "grub.cfg" continue fi if [ "$totp_confirm" = "y" -o -n "$totp_confirm" ]; then # Try to boot the default mount_boot - kexec-select-boot -b /boot -c "grub.cfg" \ + DO_WITH_DEBUG kexec-select-boot -b /boot -c "grub.cfg" \ || recovery "Failed default boot" fi diff --git a/initrd/bin/gui-init b/initrd/bin/gui-init index 7d4bf2ea..76c42c9a 100755 --- a/initrd/bin/gui-init +++ b/initrd/bin/gui-init @@ -183,17 +183,6 @@ update_totp() { TOTP="NO TPM" else TOTP=$(unseal-totp) - # On platforms using CONFIG_BOOT_EXTRA_TTYS multiple processes may try to - # access TPM at the same time, failing with EBUSY. The order of execution - # is unpredictable, so the error may appear on main console, secondary one, - # or neither of them if the calls are sufficiently staggered. Try up to - # three times (including previous one) with small delays in case of error, - # instead of immediately scaring users with "you've been pwned" message. - while [ $? -ne 0 ] && [ $tries -lt 2 ]; do - sleep 0.5 - ((tries++)) - TOTP=$(unseal-totp) - done if [ $? -ne 0 ]; then BG_COLOR_MAIN_MENU="error" if [ "$skip_to_menu" = "true" ]; then @@ -280,7 +269,10 @@ update_hotp() { HOTP='N/A' fi - if [[ "$CONFIG_TPM" = n && "$HOTP" = "Invalid code" ]]; then + if [[ "$HOTP" = "Invalid code" ]]; then + #Do not propose to generate a new secret if there is no /boot/kexec_hotp_counter + # tpm unseal succeeded: so the sealed secret is correct: we should propose to reset TPM if not already + # Here: the OS was most probably reinstalled since TPM can still unseal the secret whiptail_error --title "ERROR: HOTP Validation Failed!" \ --menu "ERROR: $CONFIG_BRAND_NAME couldn't validate the HOTP code.\n\nIf you just reflashed your BIOS, you should generate a new TOTP/HOTP secret.\n\nIf you have not just reflashed your BIOS, THIS COULD INDICATE TAMPERING!\n\nHow would you like to proceed?" 0 80 4 \ 'g' ' Generate new TOTP/HOTP secret' \ @@ -553,21 +545,30 @@ reset_tpm() { mount -o rw,remount /boot #TODO: this is really problematic, we should really remove the primary handle hash - INFO "Removing rollback and primary handle hash under /boot" + INFO "Removing rollback and primary handle hashes under /boot" + + DEBUG "Removing /boot/kexec_rollback.txt and /boot/kexec_primhdl_hash.txt" rm -f /boot/kexec_rollback.txt rm -f /boot/kexec_primhdl_hash.txt # create Heads TPM counter before any others check_tpm_counter /boot/kexec_rollback.txt "" "$tpm_owner_password" || die "Unable to find/create tpm counter" - counter="$TPM_COUNTER" - increment_tpm_counter $counter >/dev/null 2>&1 || + TRACE_FUNC + + TPM_COUNTER=$(cut -d: -f1 /dev/null 2>&1 || die "Unable to increment tpm counter" - sha256sum /tmp/counter-$counter >/boot/kexec_rollback.txt || + #TODO: should this be here? + DO_WITH_DEBUG sha256sum /tmp/counter-$TPM_COUNTER >/boot/kexec_rollback.txt || die "Unable to create rollback file" + TRACE_FUNC # As a countermeasure for existing primary handle hash, we will now force sign /boot without it if (whiptail --title 'TPM Reset Successfully' \ --yesno "Would you like to update the checksums and sign all of the files in /boot?\n\nYou will need your GPG key to continue and this will modify your disk.\n\nOtherwise the system will reboot immediately." 0 80); then @@ -593,7 +594,7 @@ select_os_boot_option() { TRACE_FUNC mount_boot if verify_global_hashes; then - kexec-select-boot -m -b /boot -c "grub.cfg" -g + DO_WITH_DEBUG kexec-select-boot -m -b /boot -c "grub.cfg" -g fi } @@ -606,11 +607,13 @@ attempt_default_boot() { fi DEFAULT_FILE=$(find /boot/kexec_default.*.txt 2>/dev/null | head -1) if [ -r "$DEFAULT_FILE" ]; then - kexec-select-boot -b /boot -c "grub.cfg" -g || + TRACE_FUNC + DO_WITH_DEBUG kexec-select-boot -b /boot -c "grub.cfg" -g || recovery "Failed default boot" elif (whiptail_warning --title 'No Default Boot Option Configured' \ --yesno "There is no default boot option configured yet.\nWould you like to load a menu of boot options?\nOtherwise you will return to the main menu." 0 80); then - kexec-select-boot -m -b /boot -c "grub.cfg" -g + TRACE_FUNC + DO_WITH_DEBUG kexec-select-boot -m -b /boot -c "grub.cfg" -g fi } diff --git a/initrd/bin/gui-init-basic b/initrd/bin/gui-init-basic index 033e561a..af9da581 100755 --- a/initrd/bin/gui-init-basic +++ b/initrd/bin/gui-init-basic @@ -159,7 +159,7 @@ select_os_boot_option() { TRACE_FUNC mount_boot - kexec-select-boot -m -b /boot -c "grub.cfg" -g -i + DO_WITH_DEBUG kexec-select-boot -m -b /boot -c "grub.cfg" -g -i } attempt_default_boot() @@ -174,11 +174,11 @@ attempt_default_boot() if [ "$CONFIG_BASIC_NO_AUTOMATIC_DEFAULT" != "y" ]; then basic-autoboot.sh elif [ -r "$DEFAULT_FILE" ]; then - kexec-select-boot -b /boot -c "grub.cfg" -g -i -s \ + DO_WITH_DEBUG kexec-select-boot -b /boot -c "grub.cfg" -g -i -s \ || recovery "Failed default boot" elif (whiptail_warning --title 'No Default Boot Option Configured' \ --yesno "There is no default boot option configured yet.\nWould you like to load a menu of boot options?\nOtherwise you will return to the main menu." 0 80) then - kexec-select-boot -m -b /boot -c "grub.cfg" -g -i + DO_WITH_DEBUG kexec-select-boot -m -b /boot -c "grub.cfg" -g -i fi } diff --git a/initrd/bin/kexec-save-default b/initrd/bin/kexec-save-default index 22ed28cf..79d58291 100755 --- a/initrd/bin/kexec-save-default +++ b/initrd/bin/kexec-save-default @@ -8,10 +8,10 @@ TRACE_FUNC 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 @@ -354,7 +354,7 @@ if [ "$CONFIG_TPM" = "y" ]; then fi fi if [ "$CONFIG_BASIC" != "y" ]; then - kexec-sign-config -p $paramsdir $extparam || + DO_WITH_DEBUG kexec-sign-config -p $paramsdir $extparam || die "Failed to sign default config" fi # switch back to ro mode diff --git a/initrd/bin/kexec-save-key b/initrd/bin/kexec-save-key index a6ceb71b..0fe2373d 100755 --- a/initrd/bin/kexec-save-key +++ b/initrd/bin/kexec-save-key @@ -77,10 +77,11 @@ kexec-seal-key $paramsdir || if [ "$skip_sign" != "y" ]; then extparam= if [ "$CONFIG_IGNORE_ROLLBACK" != "y" ]; then + DEBUG "kexec-save-key: CONFIG_IGNORE_ROLLBACK is not set, will sign with -r" extparam=-r fi # sign and auto-roll config counter - kexec-sign-config -p $paramsdir $extparam || + DO_WITH_DEBUG kexec-sign-config -p $paramsdir $extparam || die "Failed to sign updated config" fi diff --git a/initrd/bin/kexec-select-boot b/initrd/bin/kexec-select-boot index 6a0edc6a..d6b0d77e 100755 --- a/initrd/bin/kexec-select-boot +++ b/initrd/bin/kexec-select-boot @@ -20,25 +20,25 @@ force_boot="n" skip_confirm="n" while getopts "b:d:p:a:r:c:uimgfs" arg; do case $arg in - b) bootdir="$OPTARG" ;; - d) paramsdev="$OPTARG" ;; - p) paramsdir="$OPTARG" ;; - a) add="$OPTARG" ;; - r) remove="$OPTARG" ;; - c) config="$OPTARG" ;; - u) unique="y" ;; - m) force_menu="y" ;; - i) - valid_hash="y" - valid_rollback="y" - ;; - g) gui_menu="y" ;; - f) - force_boot="y" - valid_hash="y" - valid_rollback="y" - ;; - s) skip_confirm="y" ;; + b) bootdir="$OPTARG" ;; + d) paramsdev="$OPTARG" ;; + p) paramsdir="$OPTARG" ;; + a) add="$OPTARG" ;; + r) remove="$OPTARG" ;; + c) config="$OPTARG" ;; + u) unique="y" ;; + m) force_menu="y" ;; + i) + valid_hash="y" + valid_rollback="y" + ;; + g) gui_menu="y" ;; + f) + force_boot="y" + valid_hash="y" + valid_rollback="y" + ;; + s) skip_confirm="y" ;; esac done @@ -120,14 +120,14 @@ verify_rollback_counter() { TPM_COUNTER=$(grep counter $TMP_ROLLBACK_FILE | cut -d- -f2) if [ -z "$TPM_COUNTER" ]; then - die "$TMP_ROLLBACK_FILE: TPM counter not found?" + die "$TMP_ROLLBACK_FILE: TPM counter not found. Please reset TPM through the Heads menu: Options -> TPM/TOTP/HOTP Options -> Reset the TPM" fi read_tpm_counter $TPM_COUNTER >/dev/null 2>&1 || - die "Failed to read TPM counter" + die "Failed to read TPM counter. Please reset TPM through the Heads menu: Options -> TPM/TOTP/HOTP Options -> Reset the TPM" sha256sum -c $TMP_ROLLBACK_FILE >/dev/null 2>&1 || - die "Invalid TPM counter state. TPM Reset required" + die "Invalid TPM counter state. Please reset TPM through the Heads menu: Options -> TPM/TOTP/HOTP Options -> Reset the TPM" valid_rollback="y" } @@ -361,9 +361,9 @@ do_boot() { while true; do if [ "$force_boot" = "y" -o "$CONFIG_BASIC" = "y" ]; then - check_config $paramsdir force + DO_WITH_DEBUG check_config $paramsdir force else - check_config $paramsdir + DO_WITH_DEBUG check_config $paramsdir fi TMP_DEFAULT_FILE=$(find /tmp/kexec/kexec_default.*.txt 2>/dev/null | head -1) || true TMP_MENU_FILE="/tmp/kexec/kexec_menu.txt" diff --git a/initrd/bin/kexec-sign-config b/initrd/bin/kexec-sign-config index 52e6add6..8e53702e 100755 --- a/initrd/bin/kexec-sign-config +++ b/initrd/bin/kexec-sign-config @@ -27,24 +27,32 @@ fi paramsdir="${paramsdir%%/}" assert_signable +TRACE_FUNC confirm_gpg_card +TRACE_FUNC # remount /boot as rw mount -o remount,rw /boot +DEBUG "Signing kexec parameters in $paramsdir, rollback=$rollback, update=$update, counter=$counter" + # update hashes in /boot before signing if [ "$update" = "y" ]; then ( + TRACE_FUNC + DEBUG "update=y: Updating kexec hashes in /boot" cd /boot find ./ -type f ! -path './kexec*' -print0 | xargs -0 sha256sum >/boot/kexec_hashes.txt if [ -e /boot/kexec_default_hashes.txt ]; then + DEBUG "/boot/kexec_default_hashes.txt exists, updating /boot/kexec_default_hashes.txt" DEFAULT_FILES=$(cat /boot/kexec_default_hashes.txt | cut -f3 -d ' ') echo $DEFAULT_FILES | xargs sha256sum >/boot/kexec_default_hashes.txt fi #also save the file & directory structure to detect added files print_tree >/boot/kexec_tree.txt + TRACE_FUNC ) [ $? -eq 0 ] || die "$paramsdir: Failed to update hashes." @@ -56,31 +64,65 @@ fi if [ "$rollback" = "y" ]; then rollback_file="$paramsdir/kexec_rollback.txt" + DEBUG "rollback=y, counter=$counter, paramsdir=$paramsdir, rollback_file=$rollback_file" + TRACE_FUNC + if [ -n "$counter" ]; then - # use existing counter - read_tpm_counter $counter >/dev/null 2>&1 || + DEBUG "rollback=y: provided counter=$counter, will read tpm counter next" + TRACE_FUNC + + # use existing tpm counter + DO_WITH_DEBUG read_tpm_counter "$counter" >/dev/null 2>&1 || die "$paramsdir: Unable to read tpm counter '$counter'" else - # increment counter - check_tpm_counter $rollback_file >/dev/null 2>&1 || - die "$paramsdir: Unable to find/create tpm counter" - counter="$TPM_COUNTER" + DEBUG "rollback=y: counter was not provided: checking for existing TPM counter from TPM rollback_file=$rollback_file" + TRACE_FUNC - increment_tpm_counter $counter >/dev/null 2>&1 || - die "$paramsdir: Unable to increment tpm counter" + if [ -e "$rollback_file" ]; then + # Extract TPM_COUNTER from rollback file + TPM_COUNTER=$(grep -o 'counter-[0-9a-f]*' "$rollback_file" | cut -d- -f2) + DEBUG "rollback=y: Found TPM counter $TPM_COUNTER in rollback file $rollback_file" + else + DEBUG "Rollback file $rollback_file does not exist. Creating new TPM counter." + DO_WITH_DEBUG check_tpm_counter $rollback_file || + die "$paramsdir: Unable to find/create tpm counter" + + TRACE_FUNC + TPM_COUNTER=$(cut -d: -f1 $rollback_file || + TRACE_FUNC + + # Increment the TPM counter + DEBUG "rollback=y: Incrementing counter $TPM_COUNTER." + DO_WITH_DEBUG increment_tpm_counter $TPM_COUNTER >/dev/null 2>&1 || + die "$paramsdir: Unable to increment tpm counter" + + # Ensure the incremented counter file exists + incremented_counter_file="/tmp/counter-$TPM_COUNTER" + if [ ! -e "$incremented_counter_file" ]; then + DEBUG "TPM counter file '$incremented_counter_file' not found. Attempting to read it again." + DO_WITH_DEBUG read_tpm_counter "$TPM_COUNTER" >/dev/null 2>&1 || + die "$paramsdir: TPM counter file '$incremented_counter_file' not found after incrementing." + fi + + DEBUG "TPM counter file '$incremented_counter_file' found." + + # Create the rollback file + sha256sum "$incremented_counter_file" >$rollback_file || die "$paramsdir: Unable to create rollback file" fi +TRACE_FUNC param_files=$(find $paramsdir/kexec*.txt) if [ -z "$param_files" ]; then die "$paramsdir: No kexec parameter files to sign" fi for tries in 1 2 3; do - if sha256sum $param_files | gpg \ + if DO_WITH_DEBUG sha256sum $param_files | gpg \ --detach-sign \ -a \ >$paramsdir/kexec.sig \ diff --git a/initrd/bin/key-init b/initrd/bin/key-init index dcfaf529..3213a9d4 100755 --- a/initrd/bin/key-init +++ b/initrd/bin/key-init @@ -10,19 +10,19 @@ TRACE_FUNC # Good system clock is required for GPG to work properly. # if system year is less then 2024, prompt user to set correct time if [ "$(date +%Y)" -lt 2024 ]; then - if whiptail_warning --title "System Time Incorrect" \ - --yesno "The system time is incorrect. Please set the correct time." \ - 0 80 --yes-button Continue --no-button Skip --clear; then - change-time.sh - fi + if whiptail_warning --title "System Time Incorrect" \ + --yesno "The system time is incorrect. Please set the correct time." \ + 0 80 --yes-button Continue --no-button Skip --clear; then + change-time.sh + fi fi # Import user's keys if they exist if [ -d /.gnupg/keys ]; then - # This is legacy location for user's keys. cbfs-init takes for granted that keyring and trustdb are in /.gnupg - # oem-factory-reset generates keyring and trustdb which cbfs-init dumps to /.gnupg - # TODO: Remove individual key imports. This is still valid for distro keys only below. - gpg --import /.gnupg/keys/*.key /.gnupg/keys/*.asc 2>/dev/null || warn "Importing user's keys failed" + # This is legacy location for user's keys. cbfs-init takes for granted that keyring and trustdb are in /.gnupg + # oem-factory-reset generates keyring and trustdb which cbfs-init dumps to /.gnupg + # TODO: Remove individual key imports. This is still valid for distro keys only below. + gpg --import /.gnupg/keys/*.key /.gnupg/keys/*.asc 2>/dev/null || warn "Importing user's keys failed" fi # Import trusted distro keys allowed for ISO signing diff --git a/initrd/bin/oem-factory-reset b/initrd/bin/oem-factory-reset index 22f1bba5..917a9acb 100755 --- a/initrd/bin/oem-factory-reset +++ b/initrd/bin/oem-factory-reset @@ -731,14 +731,20 @@ generate_checksums() { whiptail_error_die "Unable to create TPM counter" TPM_COUNTER=$(cut -d: -f1 /dev/null 2>&1 || - whiptail_error_die "Unable to increment tpm counter" + if [ -s /tmp/counter-$TPM_COUNTER ]; then - # create rollback file - sha256sum /tmp/counter-$TPM_COUNTER >/boot/kexec_rollback.txt 2>/dev/null || - whiptail_error_die "Unable to create rollback file" - else + # increment TPM counter + increment_tpm_counter $TPM_COUNTER >/dev/null 2>&1 || + whiptail_error_die "Unable to increment tpm counter" + + # create rollback file + sha256sum /tmp/counter-$TPM_COUNTER >/boot/kexec_rollback.txt 2>/dev/null || + whiptail_error_die "Unable to create rollback file" + fi + fi + + # If HOTP is enabled from board config, create HOTP counter + if [ -x /bin/hotp_verification]; then ## needs to exist for initial call to unseal-hotp echo "0" >/boot/kexec_hotp_counter fi diff --git a/initrd/bin/reboot b/initrd/bin/reboot index ce7e6947..90a75786 100755 --- a/initrd/bin/reboot +++ b/initrd/bin/reboot @@ -5,7 +5,7 @@ TRACE_FUNC if [ "$CONFIG_DEBUG_OUTPUT" = "y" ]; then #Generalize user prompt to continue reboot or go to recovery shell - read -r -n 1 -s -p "Press any key to continue reboot or 'r' to go to recovery shell: " REPLY + read -r -n 1 -s -p "Press Enter to continue reboot or 'r' to go to recovery shell: " REPLY echo if [ "$REPLY" = "r" ] || [ "$REPLY" = "R" ]; then recovery "Reboot call bypassed to go into recovery shell to debug" diff --git a/initrd/bin/tpmr b/initrd/bin/tpmr index 756050e7..466184d9 100755 --- a/initrd/bin/tpmr +++ b/initrd/bin/tpmr @@ -589,7 +589,7 @@ tpm2_unseal() { # can't do anything without a primary handle. if [ ! -f "$PRIMARY_HANDLE_FILE" ]; then DEBUG "tpm2_unseal: No primary handle, cannot attempt to unseal" - warn "No TPM primary handle. You must reset TPM to seal secret to TPM NVRAM" + warn "No TPM primary handle. You must reset the TPM to seal secret to TPM NVRAM" exit 1 fi @@ -639,7 +639,7 @@ tpm1_unseal() { rm -f "$sealed_file" - tpm nv_readvalue \ + DO_WITH_DEBUG tpm nv_readvalue \ -in "$index" \ -sz "$sealed_size" \ -of "$sealed_file" || @@ -719,7 +719,7 @@ tpm1_reset() { DO_WITH_DEBUG tpm physicalsetdeactivated -c &>/dev/null DO_WITH_DEBUG tpm forceclear &>/dev/null DO_WITH_DEBUG tpm physicalenable &>/dev/null - DO_WITH_DEBUG tpm takeown -pwdo "$tpm_owner_password" &>/dev/null + DO_WITH_DEBUG --mask-position 3 tpm takeown -pwdo "$tpm_owner_password" &>/dev/null # And now turn it all back on DO_WITH_DEBUG tpm physicalpresence -s &>/dev/null diff --git a/initrd/bin/unseal-hotp b/initrd/bin/unseal-hotp index 8565ac61..5fae80da 100755 --- a/initrd/bin/unseal-hotp +++ b/initrd/bin/unseal-hotp @@ -7,12 +7,12 @@ HOTP_SECRET="/tmp/secret/hotp.key" HOTP_COUNTER="/boot/kexec_hotp_counter" mount_boot_or_die() { - TRACE_FUNC - # Mount local disk if it is not already mounted - if ! grep -q /boot /proc/mounts; then - mount -o ro /boot || - die "Unable to mount /boot" - fi + TRACE_FUNC + # Mount local disk if it is not already mounted + if ! grep -q /boot /proc/mounts; then + mount -o ro /boot || + die "Unable to mount /boot" + fi } TRACE_FUNC @@ -29,27 +29,33 @@ mount_boot_or_die #counter_value=$(read_tpm_counter $counter | cut -f2 -d ' ' | awk 'gsub("^000e","")') # -counter_value=$(cat $HOTP_COUNTER) +#if HOTP_COUNTER is not present, bail out +if [ ! -f $HOTP_COUNTER ]; then + die "HOTP counter file not found. If you just reinstalled an OS, you need to reseal the HOTP secret" +fi + +# Read the counter from the file +counter_value=$(cat $HOTP_COUNTER 2>/dev/null) if [ "$counter_value" == "" ]; then - die "Unable to read HOTP counter" + die "Unable to read HOTP counter" fi #counter_value=$(printf "%d" 0x${counter_value}) if [ "$CONFIG_TPM" = "y" ]; then - DEBUG "Unsealing HOTP secret reuses TOTP sealed secret..." - tpmr unseal 4d47 0,1,2,3,4,7 312 "$HOTP_SECRET" || die "Unable to unseal HOTP secret" + DEBUG "Unsealing HOTP secret reuses TOTP sealed secret..." + tpmr unseal 4d47 0,1,2,3,4,7 312 "$HOTP_SECRET" || die "Unable to unseal HOTP secret" else - # without a TPM, generate a secret based on the SHA-256 of the ROM - secret_from_rom_hash >"$HOTP_SECRET" || die "Reading ROM failed" + # without a TPM, generate a secret based on the SHA-256 of the ROM + secret_from_rom_hash >"$HOTP_SECRET" || die "Reading ROM failed" fi # Truncate the secret if it is longer than the maximum HOTP secret truncate_max_bytes 20 "$HOTP_SECRET" if ! hotp $counter_value <"$HOTP_SECRET"; then - shred -n 10 -z -u "$HOTP_SECRET" 2>/dev/null - die 'Unable to compute HOTP hash?' + shred -n 10 -z -u "$HOTP_SECRET" 2>/dev/null + die 'Unable to compute HOTP hash?' fi shred -n 10 -z -u "$HOTP_SECRET" 2>/dev/null @@ -65,7 +71,7 @@ mount -o remount,rw /boot DEBUG "Incrementing HOTP counter under $HOTP_COUNTER" counter_value=$(expr $counter_value + 1) echo $counter_value >$HOTP_COUNTER || - die "Unable to create hotp counter file" + die "Unable to create hotp counter file" mount -o remount,ro /boot exit 0 diff --git a/initrd/bin/unseal-totp b/initrd/bin/unseal-totp index 3ca7cf28..da61deee 100755 --- a/initrd/bin/unseal-totp +++ b/initrd/bin/unseal-totp @@ -8,11 +8,12 @@ TOTP_SECRET="/tmp/secret/totp.key" TRACE_FUNC if [ "$CONFIG_TPM" = "y" ]; then - tpmr unseal 4d47 0,1,2,3,4,7 312 "$TOTP_SECRET" || - die "Unable to unseal TOTP secret from TPM" + DO_WITH_DEBUG --mask-position 5 \ + tpmr unseal 4d47 0,1,2,3,4,7 312 "$TOTP_SECRET" || + die "Unable to unseal TOTP secret from TPM" fi -if ! totp -q <"$TOTP_SECRET"; then +if ! DO_WITH_DEBUG totp -q <"$TOTP_SECRET"; then shred -n 10 -z -u "$TOTP_SECRET" 2>/dev/null die 'Unable to compute TOTP hash?' fi diff --git a/initrd/etc/functions b/initrd/etc/functions index 61fb34fe..73b94f71 100644 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -8,7 +8,10 @@ die() { else echo -e "!!! ERROR: $* !!!" >&2 fi - sleep 2 + + # ask user to press any key prior to exit + read -n 1 -s -r -p $'Press Enter to continue...\n\n' + exit 1 } @@ -194,7 +197,7 @@ confirm_gpg_card() { echo "GPG User PIN retry attempts left before becoming locked: $user_pin_retries" echo "GPG Admin PIN retry attempts left before becoming locked: $admin_pin_retries" echo "" - warn "Your GPG User PIN, followed by Enter key will be required for input at: 'Please unlock the card' next prompt" + warn "Your GPG User PIN, followed by Enter will be required for input at: 'Please unlock the card' next prompt" echo "" } @@ -512,14 +515,33 @@ DO_WITH_DEBUG() { return "$exit_status" } -# Trace the current script and function. +# TRACE_FUNC outputs the function call stack in a readable format. +# It helps debug the execution path leading to the current function. +# +# The format of the output is: +# main(/path/to/script:line) -> function1(/path/to/file:line) -> function2(/path/to/file:line) +# +# Usage: +# Call TRACE_FUNC within any function to print the call hierarchy. TRACE_FUNC() { # Index [1] for BASH_SOURCE and FUNCNAME give us the caller location. # FUNCNAME is 'main' if called from a script outside any function. # BASH_LINENO is offset by 1, it provides the line that the # corresponding FUNCNAME was _called from_, so BASH_LINENO[0] is the # location of the caller. - TRACE "${BASH_SOURCE[1]}(${BASH_LINENO[0]}): ${FUNCNAME[1]}" + + local i stack_trace="" + + # Traverse the call stack from the earliest caller to the direct caller of TRACE_FUNC + for ((i = ${#FUNCNAME[@]} - 1; i > 1; i--)); do + stack_trace+="${FUNCNAME[i]}(${BASH_SOURCE[i]}:${BASH_LINENO[i - 1]}) -> " + done + + # Append the direct caller (without extra " -> " at the end) + stack_trace+="${FUNCNAME[1]}(${BASH_SOURCE[1]}:${BASH_LINENO[0]})" + + # Print the final trace output + TRACE "${stack_trace}" } # Show the entire current call stack in debug output - useful if a catastrophic @@ -750,7 +772,7 @@ prompt_new_owner_password() { # Cache the password externally to be reused by who needs it DEBUG "Caching TPM Owner Password to /tmp/secret/tpm_owner_password" mkdir -p /tmp/secret || die "Unable to create /tmp/secret" - echo -n "$tpm_owner_password" >/tmp/secret/tpm_owner_password || die "Unable to cache TPM password under /tmp/secret" + echo -n "$tpm_owner_password" >/tmp/secret/tpm_owner_password || die "Unable to cache TPM password under /tmp/secret/tpm_owner_password" } check_tpm_counter() { @@ -780,16 +802,21 @@ check_tpm_counter() { # Read the TPM counter value from the TPM. read_tpm_counter() { TRACE_FUNC - tpmr counter_read -ix "$1" | tee "/tmp/counter-$1" >/dev/null 2>&1 || - die "Counter read failed" + if [ ! -e /tmp/counter-"$1" ]; then + DEBUG "Counter file /tmp/counter-$1 not found. Attempting to read from TPM." + DO_WITH_DEBUG tpmr counter_read -ix "$1" | tee /tmp/counter-"$1" >/dev/null 2>&1 || + die "Counter read failed for index $1" + fi + DEBUG "Counter file /tmp/counter-$1 read successfully." } # Increment the TPM counter value in the TPM. increment_tpm_counter() { TRACE_FUNC - tpmr counter_increment -ix "$1" -pwdc '' | - tee /tmp/counter-$1 >/dev/null 2>&1 || - die "TPM counter increment failed for rollback prevention. Please reset the TPM" + DO_WITH_DEBUG tpmr counter_increment -ix "$1" -pwdc '' | + tee /tmp/counter-"$1" >/dev/null 2>&1 || + die "TPM counter increment failed for rollback prevention. Please reset the TPM." + DEBUG "TPM counter incremented successfully for index $1" } # Check detached signature on kexec boot params @@ -804,14 +831,17 @@ check_config() { fi if [ ! -r $1/kexec.sig -a "$CONFIG_BASIC" != "y" ]; then + DEBUG "No $1/kexec.sig found" return fi if [ $(find $1/kexec*.txt | wc -l) -eq 0 ]; then + DEBUG "No $1/kexec*.txt found" return fi if [ "$2" != "force" ]; then + DEBUG "second param: $2 != force" # Note that kexec.sig detached signature is solely verifying kexec*.txt files here! if ! sha256sum $(find $1/kexec*.txt) | gpgv $1/kexec.sig -; then die 'Invalid signature on kexec boot params' @@ -885,10 +915,11 @@ update_checksums() { extparam= if [ "$CONFIG_TPM" = "y" ]; then if [ "$CONFIG_IGNORE_ROLLBACK" != "y" ]; then + DEBUG "add -r to kexec-sign-config since CONFIG_IGNORE_ROLLBACK is not set" extparam=-r fi fi - if ! kexec-sign-config -p /boot -u $extparam; then + if ! DO_WITH_DEBUG kexec-sign-config -p /boot -u $extparam; then rv=1 else rv=0 @@ -996,12 +1027,12 @@ 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 2>/dev/null || 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 + if ! cmp -s "$TMP_TREE_FILE" /tmp/tree_output 2>/dev/null; then ret=1 [[ "$gui" != "y" ]] && exit "$ret" # produce a diff that can safely be presented to the user @@ -1190,7 +1221,6 @@ scan_boot_options() { fi } - # truncate a file to a size only if it is longer (busybox truncate lacks '<' and # always sets the file size) truncate_max_bytes() { @@ -1220,13 +1250,13 @@ fromhex_plain() { print_battery_charge() { local battery battery="$1" - echo "$((100*$(cat "${battery}/charge_now")/$(cat "${battery}/charge_full")))" + echo "$((100 * $(cat "${battery}/charge_now") / $(cat "${battery}/charge_full")))" } print_battery_health() { local battery battery="$1" - echo "$((100*$(cat "${battery}/charge_full")/$(cat "${battery}/charge_full_design")))" + echo "$((100 * $(cat "${battery}/charge_full") / $(cat "${battery}/charge_full_design")))" } print_battery_name() { diff --git a/initrd/sbin/insmod b/initrd/sbin/insmod index b079fcc0..7ca6a28f 100755 --- a/initrd/sbin/insmod +++ b/initrd/sbin/insmod @@ -33,9 +33,9 @@ if lsmod | sed 's/_/-/g' | grep -q "^$module_name\\b"; then fi if [ ! -r /sys/class/tpm/tpm0/pcrs -o ! -x /bin/tpm ]; then - if [ ! -c /dev/tpmrm0 -o ! -x /bin/tpm2 ]; then - tpm_missing=1 - fi + if [ ! -c /dev/tpmrm0 -o ! -x /bin/tpm2 ]; then + tpm_missing=1 + fi fi if [ -z "$tpm_missing" ]; then