Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1f6a975
Codebase up to TPM DUK: ident, add DEBUG+TRACE_FUNC, TRACE_FUNC now g…
tlaurion Feb 7, 2025
f9def6b
gui-init: reboot as told to user if he refused to update+sign /boot c…
tlaurion Mar 27, 2025
b5cb7ae
etc/functions: die+warn+INFO: add TRACE_FUNC and TODO to add colors (…
tlaurion Mar 28, 2025
c4b7fef
bin/kexec-select-boot/reboot/recovery: reuse reboot+DEBUG conditional…
tlaurion Mar 28, 2025
0d3b3b6
WiP
tlaurion Mar 31, 2025
68da322
WiP: tpm2 increment still fails when tpm reseal is done, and past DUK…
tlaurion Mar 31, 2025
03c5d39
WiP: so tpm reset+reboot+TPM DUK works. HOTP reseal fails at post DUK…
tlaurion Mar 31, 2025
6d90480
kexec-seal-key: force minimal DUK to 12 chars (2 words DICEWARE passp…
tlaurion Apr 28, 2025
08c8862
Merge+adapt remote-tracking branch 'osresearch/master' into hotp_fixu…
tlaurion Apr 28, 2025
4ef7473
prompts: Only continue on Enter if we ask to press Enter
JonathonHall-Purism Apr 15, 2025
d9730be
kexec-select-boot+functions : Use NOTE instead of warn, and have NOTE…
tlaurion Apr 28, 2025
9c7ef3f
kexec-seal-key: have proper spacing before and after console read pro…
tlaurion Apr 29, 2025
d10a424
kexec-seal-key: make sure TPM DRK passphrase is verified to unlock al…
tlaurion Apr 29, 2025
19d400a
/etc/functions: simplify logic of increment_tpm_counter to ease its u…
tlaurion Apr 29, 2025
67a7fd9
etc/functions: NOTE echoes to console before and after its message an…
tlaurion Apr 29, 2025
5dcad9e
Merge remote-tracking branch 'osresearch/master' into HEAD
tlaurion Jun 23, 2025
1928995
initrd/etc/functions: fix TPM counter newline presence/stripping
tlaurion Jun 23, 2025
faf32fb
initrd/etc/functions: silence output of verify_checksums + escape_zer…
tlaurion Jun 30, 2025
b95ffcc
initrd/etc/functions: check_tpm_counter(), remove unused code, add wa…
tlaurion Jun 30, 2025
c43af60
codebase: unify all 'read' prompts to inject newline prior of asking …
tlaurion Jul 2, 2025
f475296
codebase: unify all 'read' prompts to add -r, so that backslackes in …
tlaurion Jul 2, 2025
21adb0d
codebase: fix console echo needed accordingly to previous read changes
tlaurion Jul 2, 2025
0b4befa
Merge osresearch/master: resolve conflicts in initrd/bin/reboot and i…
tlaurion Nov 20, 2025
561126c
gui-init: remove TODO fixed in this PR
tlaurion Nov 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions initrd/.bash_history
Original file line number Diff line number Diff line change
Expand Up @@ -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 link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bash history now promotes mount-usb --mode rw. nitpick

#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:
Expand Down
4 changes: 2 additions & 2 deletions initrd/bin/generic-init
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
41 changes: 22 additions & 19 deletions initrd/bin/gui-init
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No more 3 attempts on boot to unseal TPMTOTP: if multiple consoles (Eg Talos-2 with display console + BMC, at worst we could intruduce small delay if race condition still happening, while die asks user to press Enter now, guiding to reseal TPMTOTP or reset TPM if unable to access TPM NVRAM.

# 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
Expand Down Expand Up @@ -280,7 +269,10 @@ update_hotp() {
HOTP='N/A'
fi

if [[ "$CONFIG_TPM" = n && "$HOTP" = "Invalid code" ]]; then
if [[ "$HOTP" = "Invalid code" ]]; then
Copy link
Copy Markdown
Collaborator Author

@tlaurion tlaurion Mar 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check only verified if HOTP was invalid if no TPM was in use.

So now, if there is no /boot/kexec_hotp_counter and TPMTOTP can unseal, user is promoted to reseal HOTP alone (OS reinstall use case without firmware upgrade)

This comment was marked as outdated.

#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' \
Expand Down Expand Up @@ -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 </tmp/counter)
DEBUG "TPM_COUNTER: $TPM_COUNTER"
#TODO was counter supposed to be empty and that was ok?!?!?!

DO_WITH_DEBUG increment_tpm_counter $TPM_COUNTER>/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
Expand All @@ -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
}

Expand All @@ -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
}

Expand Down
6 changes: 3 additions & 3 deletions initrd/bin/gui-init-basic
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
}

Expand Down
10 changes: 5 additions & 5 deletions initrd/bin/kexec-save-default
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion initrd/bin/kexec-save-key
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
48 changes: 24 additions & 24 deletions initrd/bin/kexec-select-boot
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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"
}
Expand Down Expand Up @@ -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"
Expand Down
62 changes: 52 additions & 10 deletions initrd/bin/kexec-sign-config
Original file line number Diff line number Diff line change
Expand Up @@ -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."

Expand All @@ -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

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 </tmp/counter)
DEBUG "rollback=y: Created new TPM counter $TPM_COUNTER"
fi
fi

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"

increment_tpm_counter $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

sha256sum /tmp/counter-$counter >$rollback_file ||
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 \
Expand Down
18 changes: 9 additions & 9 deletions initrd/bin/key-init
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading