Merge pull request #1515 from tlaurion/inmemory_keygen-gpg_backup_usable_for_RSA_only-copy_to_card_working_for_RSA_only-gpg_auth_for_recovery_and_sub_boot

GPG User Authentication: In-memory gpg keygen + keytocard and GPG key material backup enabling  (plus a lot of code cleanup and UX improvements)
This commit is contained in:
tlaurion 2023-11-13 16:05:26 -05:00 committed by GitHub
commit 133da0e48e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 4885 additions and 1046 deletions

View File

@ -30,7 +30,6 @@ CONFIG_LINUX_USB=y
export CONFIG_TPM=y export CONFIG_TPM=y
export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y
export CONFIG_TOTP_SKIP_QRCODE=y export CONFIG_TOTP_SKIP_QRCODE=y
export CONFIG_OEMRESET_OFFER_DEFAULTS=y
export CONFIG_BOOTSCRIPT=/bin/gui-init export CONFIG_BOOTSCRIPT=/bin/gui-init
export CONFIG_BOOT_REQ_HASH=n export CONFIG_BOOT_REQ_HASH=n
export CONFIG_BOOT_REQ_ROLLBACK=n export CONFIG_BOOT_REQ_ROLLBACK=n

View File

@ -30,7 +30,6 @@ CONFIG_LINUX_USB=y
export CONFIG_TPM=y export CONFIG_TPM=y
export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y
export CONFIG_TOTP_SKIP_QRCODE=y export CONFIG_TOTP_SKIP_QRCODE=y
export CONFIG_OEMRESET_OFFER_DEFAULTS=y
export CONFIG_BOOTSCRIPT=/bin/gui-init export CONFIG_BOOTSCRIPT=/bin/gui-init
export CONFIG_BOOT_REQ_HASH=n export CONFIG_BOOT_REQ_HASH=n
export CONFIG_BOOT_REQ_ROLLBACK=n export CONFIG_BOOT_REQ_ROLLBACK=n

View File

@ -28,7 +28,6 @@ CONFIG_LINUX_USB=y
export CONFIG_TPM=y export CONFIG_TPM=y
export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y
export CONFIG_TOTP_SKIP_QRCODE=y export CONFIG_TOTP_SKIP_QRCODE=y
export CONFIG_OEMRESET_OFFER_DEFAULTS=y
export CONFIG_BOOTSCRIPT=/bin/gui-init export CONFIG_BOOTSCRIPT=/bin/gui-init
export CONFIG_BOOT_REQ_HASH=n export CONFIG_BOOT_REQ_HASH=n

View File

@ -30,7 +30,6 @@ CONFIG_LINUX_USB=y
export CONFIG_TPM=y export CONFIG_TPM=y
export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y
export CONFIG_TOTP_SKIP_QRCODE=y export CONFIG_TOTP_SKIP_QRCODE=y
export CONFIG_OEMRESET_OFFER_DEFAULTS=y
export CONFIG_BOOTSCRIPT=/bin/gui-init export CONFIG_BOOTSCRIPT=/bin/gui-init
export CONFIG_BOOT_REQ_HASH=n export CONFIG_BOOT_REQ_HASH=n
export CONFIG_BOOT_REQ_ROLLBACK=n export CONFIG_BOOT_REQ_ROLLBACK=n

View File

@ -31,7 +31,6 @@ CONFIG_LINUX_USB=y
export CONFIG_TPM=y export CONFIG_TPM=y
export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y
export CONFIG_TOTP_SKIP_QRCODE=y export CONFIG_TOTP_SKIP_QRCODE=y
export CONFIG_OEMRESET_OFFER_DEFAULTS=y
export CONFIG_BOOTSCRIPT=/bin/gui-init export CONFIG_BOOTSCRIPT=/bin/gui-init
export CONFIG_BOOT_REQ_HASH=n export CONFIG_BOOT_REQ_HASH=n
export CONFIG_BOOT_REQ_ROLLBACK=n export CONFIG_BOOT_REQ_ROLLBACK=n

View File

@ -29,7 +29,6 @@ CONFIG_LINUX_USB=y
export CONFIG_TPM=y export CONFIG_TPM=y
export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y
export CONFIG_TOTP_SKIP_QRCODE=y export CONFIG_TOTP_SKIP_QRCODE=y
export CONFIG_OEMRESET_OFFER_DEFAULTS=y
export CONFIG_BOOTSCRIPT=/bin/gui-init export CONFIG_BOOTSCRIPT=/bin/gui-init
export CONFIG_BOOT_REQ_HASH=n export CONFIG_BOOT_REQ_HASH=n

View File

@ -32,7 +32,6 @@ CONFIG_OPENSSL=y
CONFIG_PRIMARY_KEY_TYPE=ecc CONFIG_PRIMARY_KEY_TYPE=ecc
export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y
export CONFIG_TOTP_SKIP_QRCODE=y export CONFIG_TOTP_SKIP_QRCODE=y
export CONFIG_OEMRESET_OFFER_DEFAULTS=y
export CONFIG_BOOTSCRIPT=/bin/gui-init export CONFIG_BOOTSCRIPT=/bin/gui-init
export CONFIG_BOOT_REQ_HASH=n export CONFIG_BOOT_REQ_HASH=n

View File

@ -30,7 +30,6 @@ CONFIG_LINUX_USB=y
export CONFIG_TPM=n export CONFIG_TPM=n
export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y
export CONFIG_TOTP_SKIP_QRCODE=y export CONFIG_TOTP_SKIP_QRCODE=y
export CONFIG_OEMRESET_OFFER_DEFAULTS=y
export CONFIG_BOOTSCRIPT=/bin/gui-init export CONFIG_BOOTSCRIPT=/bin/gui-init
export CONFIG_BOOT_REQ_HASH=n export CONFIG_BOOT_REQ_HASH=n

View File

@ -30,7 +30,6 @@ CONFIG_LINUX_USB=y
export CONFIG_TPM=n export CONFIG_TPM=n
export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y
export CONFIG_TOTP_SKIP_QRCODE=y export CONFIG_TOTP_SKIP_QRCODE=y
export CONFIG_OEMRESET_OFFER_DEFAULTS=y
export CONFIG_BOOTSCRIPT=/bin/gui-init export CONFIG_BOOTSCRIPT=/bin/gui-init
export CONFIG_BOOT_REQ_HASH=n export CONFIG_BOOT_REQ_HASH=n

View File

@ -41,9 +41,11 @@ Bootstrapping a working system
* Then Heads will indicate that there is no TOTP code yet, at this point shut down (Continue to main menu -> Power off) * Then Heads will indicate that there is no TOTP code yet, at this point shut down (Continue to main menu -> Power off)
5. Get the public key that was saved to the virtual USB flash drive 5. Get the public key that was saved to the virtual USB flash drive
* `sudo mkdir /media/fd_heads_gpg` * `sudo mkdir /media/fd_heads_gpg`
* `sudo mount ./build/x86/qemu-coreboot-fbwhiptail-tpm1-hotp/usb_fd.raw /media/fd_heads_gpg` * `sudo losetup --find --partscan ./build/x86/qemu-coreboot-fbwhiptail-tpm1-hotp/usb_fd.raw`
* `sudo mount /dev/loop0p2 /media/fd_heads_gpg` to mount the second partition (public) or if only one partition, /dev/loop0p1
* Look in `/media/fd_heads_gpg` and copy the most recent public key * Look in `/media/fd_heads_gpg` and copy the most recent public key
* `sudo umount /media/fd_heads_gpg` * `sudo umount /media/fd_heads_gpg`
* `sudo losetup --detach /dev/loop0`
6. Inject the GPG key into the Heads image and run again 6. Inject the GPG key into the Heads image and run again
* `make BOARD=qemu-coreboot-fbwhiptail-tpm1-hotp PUBKEY_ASC=<path_to_key.asc> inject_gpg` * `make BOARD=qemu-coreboot-fbwhiptail-tpm1-hotp PUBKEY_ASC=<path_to_key.asc> inject_gpg`
* `make BOARD=qemu-coreboot-fbwhiptail-tpm1-hotp USB_TOKEN=LibremKey PUBKEY_ASC=<path_to_key.asc> run` * `make BOARD=qemu-coreboot-fbwhiptail-tpm1-hotp USB_TOKEN=LibremKey PUBKEY_ASC=<path_to_key.asc> run`

View File

@ -10,6 +10,9 @@ export CONFIG_LINUX_VERSION=5.10.5
#export CONFIG_RESTRICTED_BOOT=y #export CONFIG_RESTRICTED_BOOT=y
#export CONFIG_BASIC=y #export CONFIG_BASIC=y
#Enable HAVE_GPG_KEY_BACKUP to test GPG key backup drive (we cannot inject config under QEMU (no internal flashing))
#export CONFIG_HAVE_GPG_KEY_BACKUP=y
#Enable DEBUG output #Enable DEBUG output
export CONFIG_DEBUG_OUTPUT=y export CONFIG_DEBUG_OUTPUT=y
export CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=y export CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=y

View File

@ -10,6 +10,9 @@ export CONFIG_LINUX_VERSION=5.10.5
#export CONFIG_RESTRICTED_BOOT=y #export CONFIG_RESTRICTED_BOOT=y
#export CONFIG_BASIC=y #export CONFIG_BASIC=y
#Enable HAVE_GPG_KEY_BACKUP to test GPG key backup drive (we cannot inject config under QEMU (no internal flashing))
#export CONFIG_HAVE_GPG_KEY_BACKUP=y
#Enable DEBUG output #Enable DEBUG output
export CONFIG_DEBUG_OUTPUT=y export CONFIG_DEBUG_OUTPUT=y
export CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=y export CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=y

View File

@ -6,10 +6,13 @@ export CONFIG_COREBOOT=y
export CONFIG_COREBOOT_VERSION=4.19 export CONFIG_COREBOOT_VERSION=4.19
export CONFIG_LINUX_VERSION=5.10.5 export CONFIG_LINUX_VERSION=5.10.5
#Enable only one RESTRICTED/BASIC boot modes below to test them manually (we cannot inject config under QEMU (no internal flashing) #Enable only one RESTRICTED/BASIC boot modes below to test them manually (we cannot inject config under QEMU (no internal flashing))
#export CONFIG_RESTRICTED_BOOT=y #export CONFIG_RESTRICTED_BOOT=y
#export CONFIG_BASIC=y #export CONFIG_BASIC=y
#Enable HAVE_GPG_KEY_BACKUP to test GPG key backup drive (we cannot inject config under QEMU (no internal flashing))
#export CONFIG_HAVE_GPG_KEY_BACKUP=y
#Enable DEBUG output #Enable DEBUG output
export CONFIG_DEBUG_OUTPUT=y export CONFIG_DEBUG_OUTPUT=y
export CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=y export CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=y

View File

@ -10,6 +10,9 @@ export CONFIG_LINUX_VERSION=5.10.5
#export CONFIG_RESTRICTED_BOOT=y #export CONFIG_RESTRICTED_BOOT=y
#export CONFIG_BASIC=y #export CONFIG_BASIC=y
#Enable HAVE_GPG_KEY_BACKUP to test GPG key backup drive (we cannot inject config under QEMU (no internal flashing))
#export CONFIG_HAVE_GPG_KEY_BACKUP=y
#Enable DEBUG output #Enable DEBUG output
export CONFIG_DEBUG_OUTPUT=y export CONFIG_DEBUG_OUTPUT=y
export CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=y export CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=y

File diff suppressed because it is too large Load Diff

View File

@ -1548,7 +1548,7 @@ CONFIG_DEVPORT=y
# CONFIG_HPET is not set # CONFIG_HPET is not set
# CONFIG_HANGCHECK_TIMER is not set # CONFIG_HANGCHECK_TIMER is not set
CONFIG_TCG_TPM=y CONFIG_TCG_TPM=y
CONFIG_HW_RANDOM_TPM=n # CONFIG_HW_RANDOM_TPM is not set
CONFIG_TCG_TIS_CORE=y CONFIG_TCG_TIS_CORE=y
CONFIG_TCG_TIS=y CONFIG_TCG_TIS=y
# CONFIG_TCG_TIS_I2C is not set # CONFIG_TCG_TIS_I2C is not set
@ -2768,7 +2768,8 @@ CONFIG_VFAT_FS=y
CONFIG_FAT_DEFAULT_CODEPAGE=437 CONFIG_FAT_DEFAULT_CODEPAGE=437
CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
# CONFIG_FAT_DEFAULT_UTF8 is not set # CONFIG_FAT_DEFAULT_UTF8 is not set
# CONFIG_EXFAT_FS is not set CONFIG_EXFAT_FS=y
CONFIG_EXFAT_DEFAULT_IOCHARSET="utf8"
# CONFIG_NTFS_FS is not set # CONFIG_NTFS_FS is not set
# CONFIG_NTFS3_FS is not set # CONFIG_NTFS3_FS is not set
# end of DOS/FAT/EXFAT/NT Filesystems # end of DOS/FAT/EXFAT/NT Filesystems

View File

@ -1,3 +1,7 @@
#mount /boot in read-only by default
mount /boot
#verify detached signature of /boot content
find /boot/kexec*.txt | gpg --verify /boot/kexec.sig -
#remove invalid kexec_* signed files #remove invalid kexec_* signed files
mount /dev/sda1 /boot && mount -o remount,rw /boot && rm /boot/kexec* && mount -o remount,ro /boot mount /dev/sda1 /boot && mount -o remount,rw /boot && rm /boot/kexec* && mount -o remount,ro /boot
#Generate keys from GPG smartcard: #Generate keys from GPG smartcard:

View File

@ -24,6 +24,7 @@ for cbfsname in `echo $cbfsfiles`; do
TMPFILE=/tmp/cbfs.$$ TMPFILE=/tmp/cbfs.$$
echo "$filename" > $TMPFILE echo "$filename" > $TMPFILE
cat $filename >> $TMPFILE cat $filename >> $TMPFILE
DEBUG "Extending TPM PCR $CONFIG_PCR with $filename"
tpmr extend -ix "$CONFIG_PCR" -if $TMPFILE \ tpmr extend -ix "$CONFIG_PCR" -if $TMPFILE \
|| die "$filename: tpm extend failed" || die "$filename: tpm extend failed"
fi fi

View File

@ -151,12 +151,12 @@ prompt_update_checksums()
generate_totp_hotp() generate_totp_hotp()
{ {
tpm_password="$1" # May be empty, will prompt if needed and empty
TRACE "Under /bin/gui-init:generate_totp_hotp" TRACE "Under /bin/gui-init:generate_totp_hotp"
tpm_owner_password="$1" # May be empty, will prompt if needed and empty
if [ "$CONFIG_TPM" != "y" ] && [ -x /bin/hotp_verification ]; then if [ "$CONFIG_TPM" != "y" ] && [ -x /bin/hotp_verification ]; then
echo "Generating new HOTP secret" echo "Generating new HOTP secret"
/bin/seal-hotpkey /bin/seal-hotpkey
elif echo -e "Generating new TOTP secret...\n\n" && /bin/seal-totp "$BOARD_NAME" "$tpm_password"; then elif echo -e "Generating new TOTP secret...\n\n" && /bin/seal-totp "$BOARD_NAME" "$tpm_owner_password"; then
echo echo
if [ -x /bin/hotp_verification ]; then if [ -x /bin/hotp_verification ]; then
if [ "$CONFIG_TOTP_SKIP_QRCODE" != y ]; then if [ "$CONFIG_TOTP_SKIP_QRCODE" != y ]; then
@ -229,8 +229,7 @@ update_totp()
g ) g )
if (whiptail $BG_COLOR_WARNING --title 'Generate new TOTP/HOTP secret' \ 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 --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="" generate_totp_hotp && update_totp && BG_COLOR_MAIN_MENU="" && reseal_tpm_disk_decryption_key
reseal_tpm_disk_decryption_key
fi fi
;; ;;
i ) i )
@ -238,8 +237,7 @@ update_totp()
return 1 return 1
;; ;;
p ) p )
reset_tpm && update_totp && BG_COLOR_MAIN_MENU="" reset_tpm && update_totp && BG_COLOR_MAIN_MENU="" && reseal_tpm_disk_decryption_key
reseal_tpm_disk_decryption_key
;; ;;
x ) x )
recovery "User requested recovery shell" recovery "User requested recovery shell"
@ -300,8 +298,7 @@ update_hotp()
g ) g )
if (whiptail $BG_COLOR_WARNING --title 'Generate new TOTP/HOTP secret' \ 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 --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="" generate_totp_hotp && BG_COLOR_MAIN_MENU="" && reseal_tpm_disk_decryption_key
reseal_tpm_disk_decryption_key
fi fi
;; ;;
i ) i )
@ -316,7 +313,7 @@ update_hotp()
clean_boot_check() clean_boot_check()
{ {
TRACE "Under /bin/gui-init:mount_boot" TRACE "Under /bin/gui-init:clean_boot_check"
# assume /boot mounted # assume /boot mounted
if ! grep -q /boot /proc/mounts ; then if ! grep -q /boot /proc/mounts ; then
return return
@ -526,12 +523,10 @@ show_tpm_totp_hotp_options_menu()
option=$(cat /tmp/whiptail) option=$(cat /tmp/whiptail)
case "$option" in case "$option" in
g ) g )
generate_totp_hotp generate_totp_hotp && reseal_tpm_disk_decryption_key
reseal_tpm_disk_decryption_key
;; ;;
r ) r )
reset_tpm reset_tpm && reseal_tpm_disk_decryption_key
reseal_tpm_disk_decryption_key
;; ;;
t ) t )
prompt_totp_mismatch prompt_totp_mismatch
@ -572,7 +567,7 @@ reset_tpm()
return 1 return 1
fi fi
tpmr reset "$key_password" tpmr reset "$tpm_owner_password"
# now that the TPM is reset, remove invalid TPM counter files # now that the TPM is reset, remove invalid TPM counter files
mount_boot mount_boot
@ -582,7 +577,7 @@ reset_tpm()
rm -f /boot/kexec_primhdl_hash.txt rm -f /boot/kexec_primhdl_hash.txt
# create Heads TPM counter before any others # create Heads TPM counter before any others
check_tpm_counter /boot/kexec_rollback.txt "" "$key_password" \ check_tpm_counter /boot/kexec_rollback.txt "" "$tpm_owner_password" \
|| die "Unable to find/create tpm counter" || die "Unable to find/create tpm counter"
counter="$TPM_COUNTER" counter="$TPM_COUNTER"
@ -593,7 +588,7 @@ reset_tpm()
|| die "Unable to create rollback file" || die "Unable to create rollback file"
mount -o ro,remount /boot mount -o ro,remount /boot
generate_totp_hotp "$key_password" generate_totp_hotp "$tpm_owner_password"
else else
echo "Returning to the main menu" echo "Returning to the main menu"
fi fi

View File

@ -49,6 +49,7 @@ if ! kexec-unseal-key "$INITRD_DIR/secret.key"; then
fi fi
# Override PCR 4 so that user can't read the key # Override PCR 4 so that user can't read the key
DEBUG "Extending TPM PCR 4 to prevent further secret unsealing"
tpmr extend -ix 4 -ic generic || tpmr extend -ix 4 -ic generic ||
die 'Unable to scramble PCR' die 'Unable to scramble PCR'
@ -92,7 +93,6 @@ if [ "$unseal_failed" = "n" ]; then
done done
else else
# No crypttab files were found under selected default boot option's initrd file # No crypttab files were found under selected default boot option's initrd 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 # Meanwhile, force crypttab to be created from scratch on both possible locations: /etc/crypttab and /cryptroot/crypttab
crypttab_files="etc/crypttab cryptroot/crypttab" crypttab_files="etc/crypttab cryptroot/crypttab"
for crypttab_file in $crypttab_files; do for crypttab_file in $crypttab_files; do

View File

@ -276,8 +276,9 @@ if [ ! -d $paramsdir ]; then
fi fi
if [ "$CONFIG_TPM2_TOOLS" = "y" ]; then if [ "$CONFIG_TPM2_TOOLS" = "y" ]; then
sha256sum /tmp/primary.handle >"$PRIMHASH_FILE" || sha256sum /tmp/secret/primary.handle >"$PRIMHASH_FILE" ||
die "ERROR: Failed to Hash TPM2 primary key handle!" die "ERROR: Failed to Hash TPM2 primary key handle!"
DEBUG "TPM2 primary key handle hash saved to $PRIMHASH_FILE"
fi fi
rm $paramsdir/kexec_default.*.txt 2>/dev/null || true rm $paramsdir/kexec_default.*.txt 2>/dev/null || true

View File

@ -42,7 +42,6 @@ DEBUG "kexec-save-key prior of last override: paramsdir: $paramsdir, paramsdev:
if [ -n "$lvm_volume_group" ]; then if [ -n "$lvm_volume_group" ]; then
lvm vgchange -a y $lvm_volume_group || lvm vgchange -a y $lvm_volume_group ||
die "Failed to activate the LVM group" die "Failed to activate the LVM group"
#TODO: why reuse key_devices for lvm devices?
for dev in /dev/$lvm_volume_group/*; do for dev in /dev/$lvm_volume_group/*; do
key_devices="$key_devices $dev" key_devices="$key_devices $dev"
done done

View File

@ -72,20 +72,21 @@ dd \
# Count the number of slots used on each device # Count the number of slots used on each device
for dev in $(cat "$KEY_DEVICES" | cut -d\ -f1); do for dev in $(cat "$KEY_DEVICES" | cut -d\ -f1); do
DEBUG "Checking number of slots used on $dev" DEBUG "Checking number of slots used on $dev LUKS header"
#check if the device is a LUKS device with luks[1,2] #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") 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" 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 slot1 is the only one used, warn and die with proper messages
if [ $slots_used -eq 1 ]; then if [ $slots_used -eq 1 ]; then
# Check if slot 1 is the only one existing # Check if slot 1 is the only one existing
if cryptsetup luksDump $dev | grep -q "Slot 1: ENABLED"; then 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" warn "Slot 1 is the only one existing on $dev LUKS header. Heads cannot use it to store TPM sealed LUKS Disk Unlock Key"
die "Slot 1 should not be the only slot existing on $dev. Fix your custom setup" 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 fi
else else
DEBUG "Slot 1 is not the only existing slot on $dev" DEBUG "Slot 1 is not the only existing slot on $dev LUKS header."
DEBUG "$dev Slot 1 will be used to store LUKS Disk Unlock Key that TPM will seal/unseal with TPM Disk Unlock Key passphrase" 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 fi
done done
@ -136,7 +137,7 @@ tpmr pcrread -a 7 "$pcrf"
DO_WITH_DEBUG --mask-position 7 \ DO_WITH_DEBUG --mask-position 7 \
tpmr seal "$KEY_FILE" "$TPM_INDEX" 0,1,2,3,4,5,6,7 "$pcrf" \ tpmr seal "$KEY_FILE" "$TPM_INDEX" 0,1,2,3,4,5,6,7 "$pcrf" \
"$TPM_SIZE" "$key_password" "$TPM_SIZE" "$key_password" || die "Unable to write TPM Disk Unlock Key to NVRAM"
# should be okay if this fails # should be okay if this fails
shred -n 10 -z -u "$pcrf" 2>/dev/null || shred -n 10 -z -u "$pcrf" 2>/dev/null ||
@ -144,7 +145,7 @@ shred -n 10 -z -u "$pcrf" 2>/dev/null ||
shred -n 10 -z -u "$KEY_FILE" 2>/dev/null || shred -n 10 -z -u "$KEY_FILE" 2>/dev/null ||
warn "Failed to delete key file - continuing" warn "Failed to delete key file - continuing"
mount -o rw,remount $paramsdir || die "Failed to remount $paramsdir in RW - continuing" mount -o rw,remount $paramsdir || warn "Failed to remount $paramsdir in RW - continuing"
cp -f /tmp/luksDump.txt "$paramsdir/kexec_lukshdr_hash.txt" || cp -f /tmp/luksDump.txt "$paramsdir/kexec_lukshdr_hash.txt" ||
die "Failed to copy LUKS header hashes to /boot - continuing" warn "Failed to copy LUKS header hashes to /boot - continuing"
mount -o ro,remount $paramsdir || die "Failed to remount $paramsdir in RO - continuing" mount -o ro,remount $paramsdir || warn "Failed to remount $paramsdir in RO - continuing"

View File

@ -27,9 +27,16 @@ while getopts "b:d:p:a:r:c:uimgfs" arg; do
c) config="$OPTARG" ;; c) config="$OPTARG" ;;
u) unique="y" ;; u) unique="y" ;;
m) force_menu="y" ;; m) force_menu="y" ;;
i) valid_hash="y"; valid_rollback="y" ;; i)
valid_hash="y"
valid_rollback="y"
;;
g) gui_menu="y" ;; g) gui_menu="y" ;;
f) force_boot="y"; valid_hash="y"; valid_rollback="y" ;; f)
force_boot="y"
valid_hash="y"
valid_rollback="y"
;;
s) skip_confirm="y" ;; s) skip_confirm="y" ;;
esac esac
done done
@ -53,21 +60,22 @@ paramsdir="${paramsdir%%/}"
PRIMHASH_FILE="$paramsdir/kexec_primhdl_hash.txt" PRIMHASH_FILE="$paramsdir/kexec_primhdl_hash.txt"
if [ "$CONFIG_TPM2_TOOLS" = "y" ]; then if [ "$CONFIG_TPM2_TOOLS" = "y" ]; then
if [ -r "$PRIMHASH_FILE" ]; then if [ -r "$PRIMHASH_FILE" ]; then
sha256sum -c "$PRIMHASH_FILE" \ sha256sum -c "$PRIMHASH_FILE" ||
|| { {
echo "FATAL: Hash of TPM2 primary key handle mismatch!"; echo "FATAL: Hash of TPM2 primary key handle mismatch!"
warn "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"; warn "your system may have been compromised"
DEBUG "Hash of TPM2 primary key handle mismatched for $PRIMHASH_FILE"
} }
else else
warn "Hash of TPM2 primary key handle does not exist" warn "Hash of TPM2 primary key handle does not exist"
warn "Please rebuild the boot hash tree" warn "Please rebuild the boot hash tree"
default_failed="y" default_failed="y"
DEBUG "Hash of TPM2 primary key handle does not exist under $PRIMHASH_FILE"
fi fi
fi fi
verify_global_hashes() verify_global_hashes() {
{
echo "+++ Checking verified boot hash file " echo "+++ Checking verified boot hash file "
# Check the hashes of all the files # Check the hashes of all the files
if verify_checksums "$bootdir" "$gui_menu"; then if verify_checksums "$bootdir" "$gui_menu"; then
@ -101,25 +109,24 @@ verify_global_hashes()
fi fi
} }
verify_rollback_counter() verify_rollback_counter() {
{ TPM_COUNTER=$(grep counter $TMP_ROLLBACK_FILE | cut -d- -f2)
TPM_COUNTER=`grep counter $TMP_ROLLBACK_FILE | cut -d- -f2`
if [ -z "$TPM_COUNTER" ]; then if [ -z "$TPM_COUNTER" ]; then
die "$TMP_ROLLBACK_FILE: TPM counter not found?" die "$TMP_ROLLBACK_FILE: TPM counter not found?"
fi fi
read_tpm_counter $TPM_COUNTER \ read_tpm_counter $TPM_COUNTER ||
|| die "Failed to read TPM counter" die "Failed to read TPM counter"
sha256sum -c $TMP_ROLLBACK_FILE \ sha256sum -c $TMP_ROLLBACK_FILE ||
|| die "Invalid TPM counter state" die "Invalid TPM counter state"
valid_rollback="y" valid_rollback="y"
} }
first_menu="y" first_menu="y"
get_menu_option() { get_menu_option() {
num_options=`cat $TMP_MENU_FILE | wc -l` num_options=$(cat $TMP_MENU_FILE | wc -l)
if [ $num_options -eq 0 ]; then if [ $num_options -eq 0 ]; then
die "No boot options" die "No boot options"
fi fi
@ -129,10 +136,9 @@ get_menu_option() {
elif [ "$gui_menu" = "y" ]; then elif [ "$gui_menu" = "y" ]; then
MENU_OPTIONS="" MENU_OPTIONS=""
n=0 n=0
while read option while read option; do
do
parse_option parse_option
n=`expr $n + 1` n=$(expr $n + 1)
name=$(echo $name | tr " " "_") name=$(echo $name | tr " " "_")
MENU_OPTIONS="$MENU_OPTIONS $n ${name} " MENU_OPTIONS="$MENU_OPTIONS $n ${name} "
done <$TMP_MENU_FILE done <$TMP_MENU_FILE
@ -146,10 +152,9 @@ get_menu_option() {
else else
echo "+++ Select your boot option:" echo "+++ Select your boot option:"
n=0 n=0
while read option while read option; do
do
parse_option parse_option
n=`expr $n + 1` n=$(expr $n + 1)
echo "$n. $name [$kernel]" echo "$n. $name [$kernel]"
done <$TMP_MENU_FILE done <$TMP_MENU_FILE
@ -163,7 +168,7 @@ get_menu_option() {
fi fi
first_menu="n" first_menu="n"
option=`head -n $option_index $TMP_MENU_FILE | tail -1` option=$(head -n $option_index $TMP_MENU_FILE | tail -1)
parse_option parse_option
} }
@ -190,8 +195,8 @@ confirm_menu_option() {
} }
parse_option() { parse_option() {
name=`echo $option | cut -d\| -f1` name=$(echo $option | cut -d\| -f1)
kernel=`echo $option | cut -d\| -f3` kernel=$(echo $option | cut -d\| -f3)
} }
scan_options() { scan_options() {
@ -242,11 +247,11 @@ default_select() {
# Attempt boot with expected parameters # Attempt boot with expected parameters
# Check that entry matches that which is expected from menu # Check that entry matches that which is expected from menu
default_index=`basename "$TMP_DEFAULT_FILE" | cut -d. -f 2` default_index=$(basename "$TMP_DEFAULT_FILE" | cut -d. -f 2)
# Check to see if entries have changed - useful for detecting grub update # Check to see if entries have changed - useful for detecting grub update
expectedoption=`cat $TMP_DEFAULT_FILE` expectedoption=$(cat $TMP_DEFAULT_FILE)
option=`head -n $default_index $TMP_MENU_FILE | tail -1` option=$(head -n $default_index $TMP_MENU_FILE | tail -1)
if [ "$option" != "$expectedoption" ]; then if [ "$option" != "$expectedoption" ]; then
if [ "$gui_menu" = "y" ]; then if [ "$gui_menu" = "y" ]; then
whiptail $BG_COLOR_ERROR --title 'ERROR: Boot Entry Has Changed' \ whiptail $BG_COLOR_ERROR --title 'ERROR: Boot Entry Has Changed' \
@ -282,8 +287,7 @@ user_select() {
# No default expected boot parameters, ask user # No default expected boot parameters, ask user
option_confirm="" option_confirm=""
while [ "$option_confirm" != "y" -a "$option_confirm" != "d" ] while [ "$option_confirm" != "y" -a "$option_confirm" != "d" ]; do
do
get_menu_option get_menu_option
# In force boot mode, no need offer the option to set a default, just boot # In force boot mode, no need offer the option to set a default, just boot
if [[ "$force_boot" = "y" || "$skip_confirm" = "y" ]]; then if [[ "$force_boot" = "y" || "$skip_confirm" = "y" ]]; then
@ -305,8 +309,8 @@ user_select() {
echo "+++ Rebooting to start the new default option" echo "+++ Rebooting to start the new default option"
sleep 2 sleep 2
if [ "$CONFIG_DEBUG_OUTPUT" != "y" ]; then if [ "$CONFIG_DEBUG_OUTPUT" != "y" ]; then
reboot \ reboot ||
|| die "!!! Failed to reboot system" die "!!! Failed to reboot system"
else else
DEBUG "Rebooting is required prior of booting default boot entry" DEBUG "Rebooting is required prior of booting default boot entry"
# Instead of rebooting, drop to a recovery shell # Instead of rebooting, drop to a recovery shell
@ -319,8 +323,7 @@ user_select() {
do_boot do_boot
} }
do_boot() do_boot() {
{
if [ "$CONFIG_BASIC" != y ] && [ "$CONFIG_BOOT_REQ_ROLLBACK" = "y" ] && [ "$valid_rollback" = "n" ]; then if [ "$CONFIG_BASIC" != y ] && [ "$CONFIG_BOOT_REQ_ROLLBACK" = "y" ] && [ "$valid_rollback" = "n" ]; then
die "!!! Missing required rollback counter state" die "!!! Missing required rollback counter state"
fi fi
@ -330,21 +333,21 @@ do_boot()
fi fi
if [ "$CONFIG_BASIC" != y ] && [ "$CONFIG_TPM" = "y" ] && [ -r "$TMP_KEY_DEVICES" ]; then if [ "$CONFIG_BASIC" != y ] && [ "$CONFIG_TPM" = "y" ] && [ -r "$TMP_KEY_DEVICES" ]; then
INITRD=`kexec-boot -b "$bootdir" -e "$option" -i` \ INITRD=$(kexec-boot -b "$bootdir" -e "$option" -i) ||
|| die "!!! Failed to extract the initrd from boot option" die "!!! Failed to extract the initrd from boot option"
if [ -z "$INITRD" ]; then if [ -z "$INITRD" ]; then
die "!!! No initrd file found in boot option" die "!!! No initrd file found in boot option"
fi fi
kexec-insert-key $INITRD \ kexec-insert-key $INITRD ||
|| die "!!! Failed to insert disk key into a new initrd" die "!!! Failed to insert disk key into a new initrd"
kexec-boot -b "$bootdir" -e "$option" \ kexec-boot -b "$bootdir" -e "$option" \
-a "$add" -r "$remove" -o "/tmp/secret/initrd.cpio" \ -a "$add" -r "$remove" -o "/tmp/secret/initrd.cpio" ||
|| die "!!! Failed to boot w/ options: $option" die "!!! Failed to boot w/ options: $option"
else else
kexec-boot -b "$bootdir" -e "$option" -a "$add" -r "$remove" \ kexec-boot -b "$bootdir" -e "$option" -a "$add" -r "$remove" ||
|| die "!!! Failed to boot w/ options: $option" die "!!! Failed to boot w/ options: $option"
fi fi
} }
@ -354,7 +357,7 @@ while true; do
else else
check_config $paramsdir check_config $paramsdir
fi fi
TMP_DEFAULT_FILE=`find /tmp/kexec/kexec_default.*.txt 2>/dev/null | head -1` || true TMP_DEFAULT_FILE=$(find /tmp/kexec/kexec_default.*.txt 2>/dev/null | head -1) || true
TMP_MENU_FILE="/tmp/kexec/kexec_menu.txt" TMP_MENU_FILE="/tmp/kexec/kexec_menu.txt"
TMP_HASH_FILE="/tmp/kexec/kexec_hashes.txt" TMP_HASH_FILE="/tmp/kexec/kexec_hashes.txt"
TMP_TREE_FILE="/tmp/kexec/kexec_tree.txt" TMP_TREE_FILE="/tmp/kexec/kexec_tree.txt"
@ -378,8 +381,9 @@ while true; do
if [ "$CONFIG_TPM" = "y" ]; then if [ "$CONFIG_TPM" = "y" ]; then
if [ ! -r "$TMP_KEY_DEVICES" ]; then if [ ! -r "$TMP_KEY_DEVICES" ]; then
# Extend PCR4 as soon as possible # Extend PCR4 as soon as possible
tpmr extend -ix 4 -ic generic \ DEBUG "Extending TPM PCR 4 to prevent further secret unsealing"
|| die "Failed to extend PCR 4" tpmr extend -ix 4 -ic generic ||
die "Failed to extend PCR 4"
fi fi
fi fi

View File

@ -7,6 +7,9 @@ set -e -o pipefail
TRACE "Under /bin/media-scan" TRACE "Under /bin/media-scan"
#Booting from external media should be authenticated if supported
gpg_auth || die "GPG authentication failed"
# Unmount any previous boot device # Unmount any previous boot device
if grep -q /boot /proc/mounts ; then if grep -q /boot /proc/mounts ; then
umount /boot \ umount /boot \

View File

@ -184,7 +184,7 @@ if cryptsetup isLuks "$USB_MOUNT_DEVICE"; then
|| die "ERROR: Failed to open ${USB_MOUNT_DEVICE} LUKS device" || die "ERROR: Failed to open ${USB_MOUNT_DEVICE} LUKS device"
fi fi
warn "Note that you cannot boot from a mounted encrypted device." warn "Note that you cannot boot from a mounted encrypted device"
DEBUG "Setting USB_MOUNT_DEVICE=/dev/mapper/"usb_mount_$(basename "$USB_MOUNT_DEVICE")"" DEBUG "Setting USB_MOUNT_DEVICE=/dev/mapper/"usb_mount_$(basename "$USB_MOUNT_DEVICE")""
USB_MOUNT_DEVICE="/dev/mapper/"usb_mount_$(basename "$USB_MOUNT_DEVICE")"" USB_MOUNT_DEVICE="/dev/mapper/"usb_mount_$(basename "$USB_MOUNT_DEVICE")""
else else

File diff suppressed because it is too large Load Diff

View File

@ -14,11 +14,11 @@ for dev in "$@"; do
die "$dev: Unable to read LUKS header" die "$dev: Unable to read LUKS header"
done done
DEBUG "Hashing luks headers into /tmp/luksDump.txt" DEBUG "Hashing LUKS headers into /tmp/luksDump.txt"
sha256sum /tmp/lukshdr-* >/tmp/luksDump.txt || die "Unable to hash luks headers" sha256sum /tmp/lukshdr-* >/tmp/luksDump.txt || die "Unable to hash LUKS headers"
DEBUG "Removing /tmp/lukshdr-*" DEBUG "Removing /tmp/lukshdr-*"
rm /tmp/lukshdr-* rm /tmp/lukshdr-*
DEBUG "Extending PCR 6 with /tmp/luksDump.txt" DEBUG "Extending TPM PCR 6 with hash of LUKS headers from /tmp/luksDump.txt"
tpmr extend -ix 6 -if /tmp/luksDump.txt || tpmr extend -ix 6 -if /tmp/luksDump.txt ||
die "Unable to extend PCR" die "Unable to extend PCR"

View File

@ -3,13 +3,22 @@
TRACE "Under /bin/reboot" TRACE "Under /bin/reboot"
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
echo
if [ "$REPLY" = "r" ] || [ "$REPLY" = "R" ]; then
recovery "Reboot call bypassed to go into recovery shell to debug"
fi
fi
# Shut down TPM # Shut down TPM
if [ "$CONFIG_TPM" = "y" ]; then if [ "$CONFIG_TPM" = "y" ]; then
tpmr shutdown tpmr shutdown
fi fi
# Run special EC-based poweroff for Nitropad-Nxx # Run special EC-based poweroff for Nitropad-Nxx
if [ "${CONFIG_BOARD%_*}" = nitropad-nv41 || "${CONFIG_BOARD%_*}" = nitropad-ns51 ]; then if [ "${CONFIG_BOARD%_*}" = nitropad-nv41 ] || [ "${CONFIG_BOARD%_*}" = nitropad-ns51 ]; then
/bin/nitropad-shutdown.sh /bin/nitropad-shutdown.sh
fi fi

View File

@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# Retrieve the sealed TOTP secret and initialize a USB Security dongle with it # Retrieve the sealed TOTP secret and initialize a USB Security Dongle with it
. /etc/functions . /etc/functions

View File

@ -25,10 +25,10 @@ dd \
of="$TOTP_SECRET" \ of="$TOTP_SECRET" \
count=1 \ count=1 \
bs=20 \ bs=20 \
2>/dev/null \ 2>/dev/null ||
|| die "Unable to generate 20 random bytes" die "Unable to generate 20 random bytes"
secret="`base32 < $TOTP_SECRET`" secret="$(base32 <$TOTP_SECRET)"
pcrf="/tmp/secret/pcrf.bin" pcrf="/tmp/secret/pcrf.bin"
DEBUG "Sealing TOTP with actual state of PCR0-3" DEBUG "Sealing TOTP with actual state of PCR0-3"
tpmr pcrread 0 "$pcrf" tpmr pcrread 0 "$pcrf"
@ -43,12 +43,14 @@ DEBUG "Sealing TOTP with boot state of PCR4 (Going to recovery shell extends PCR
tpmr calcfuturepcr 4 >>"$pcrf" tpmr calcfuturepcr 4 >>"$pcrf"
# pcr 5 (kernel modules loaded) is not measured at sealing/unsealing of totp # pcr 5 (kernel modules loaded) is not measured at sealing/unsealing of totp
DEBUG "Sealing TOTP neglecting PCR5 involvement (Dynamically loaded kernel modules are not firmware integrity attestation related)" DEBUG "Sealing TOTP neglecting PCR5 involvement (Dynamically loaded kernel modules are not firmware integrity attestation related)"
# pcr 6 (drive luks header) is not measured at sealing/unsealing of totp # pcr 6 (drive LUKS header) is not measured at sealing/unsealing of totp
DEBUG "Sealing TOTP without PCR6 involvement (LUKS header consistency is not firmware integrity attestation related)" DEBUG "Sealing TOTP without PCR6 involvement (LUKS header consistency is not firmware integrity attestation related)"
# pcr 7 is containing measurements of user injected stuff in cbfs # pcr 7 is containing measurements of user injected stuff in cbfs
tpmr pcrread -a 7 "$pcrf" tpmr pcrread -a 7 "$pcrf"
tpmr seal "$TOTP_SECRET" "$TPM_NVRAM_SPACE" 0,1,2,3,4,7 "$pcrf" 312 "" "$TPM_PASSWORD" \ #Make sure we clear the TPM Owner Password from memory in case it failed to be used to seal TOTP
|| die "Unable to write sealed secret to NVRAM" tpmr seal "$TOTP_SECRET" "$TPM_NVRAM_SPACE" 0,1,2,3,4,7 "$pcrf" 312 "" "$TPM_PASSWORD" ||
die "Unable to write sealed secret to NVRAM from seal-totp"
#Make sure we clear TPM TOTP sealed if we succeed to seal TOTP
shred -n 10 -z -u "$TOTP_SEALED" 2>/dev/null shred -n 10 -z -u "$TOTP_SEALED" 2>/dev/null
url="otpauth://totp/$HOST?secret=$secret" url="otpauth://totp/$HOST?secret=$secret"

View File

@ -7,4 +7,4 @@ echo '*****'
prompt_new_owner_password prompt_new_owner_password
tpmr reset "$key_password" tpmr reset "$tpm_owner_password"

View File

@ -5,9 +5,9 @@
SECRET_DIR="/tmp/secret" SECRET_DIR="/tmp/secret"
PRIMARY_HANDLE="0x81000000" PRIMARY_HANDLE="0x81000000"
ENC_SESSION_FILE="enc.ctx" ENC_SESSION_FILE="$SECRET_DIR/enc.ctx"
DEC_SESSION_FILE="dec.ctx" DEC_SESSION_FILE="$SECRET_DIR/dec.ctx"
PRIMARY_HANDLE_FILE="primary.handle" PRIMARY_HANDLE_FILE="$SECRET_DIR/primary.handle"
# PCR size in bytes. Set when we determine what TPM version is in use. # PCR size in bytes. Set when we determine what TPM version is in use.
# TPM1 PCRs are always 20 bytes. TPM2 is allowed to provide multiple PCR banks # TPM1 PCRs are always 20 bytes. TPM2 is allowed to provide multiple PCR banks
@ -195,14 +195,14 @@ $0 ~ pcr {
replay_pcr() { replay_pcr() {
TRACE "Under /bin/tpmr:replay_pcr" TRACE "Under /bin/tpmr:replay_pcr"
if [ -z "$2" ]; then if [ -z "$2" ]; then
>&2 echo "No PCR number passed" echo >&2 "No PCR number passed"
return return
fi fi
if [ "$2" -ge 8 ]; then if [ "$2" -ge 8 ]; then
>&2 echo "Illegal PCR number ($2)" echo >&2 "Illegal PCR number ($2)"
return return
fi fi
local log=`cbmem -L` local log=$(cbmem -L)
local alg="$1" local alg="$1"
local pcr="$2" local pcr="$2"
local alg_digits=0 local alg_digits=0
@ -225,7 +225,7 @@ replay_pcr() {
# PCR-5, depending on which modules are loaded for given board: # PCR-5, depending on which modules are loaded for given board:
# tpmr calcfuturepcr 5 module0.ko module1.ko module2.ko | xxd -p # tpmr calcfuturepcr 5 module0.ko module1.ko module2.ko | xxd -p
# PCR-6 and PCR-7: similar to 5, but with different files passed # PCR-6 and PCR-7: similar to 5, but with different files passed
# (6: luks header, 7: user related cbfs files loaded from cbfs-init) # (6: LUKS header, 7: user related cbfs files loaded from cbfs-init)
} }
tpm2_extend() { tpm2_extend() {
@ -234,15 +234,19 @@ tpm2_extend() {
case "$1" in case "$1" in
-ix) -ix)
index="$2" index="$2"
shift 2;; shift 2
;;
-ic) -ic)
hash="$(echo -n "$2" | sha256sum | cut -d' ' -f1)" hash="$(echo -n "$2" | sha256sum | cut -d' ' -f1)"
shift 2;; shift 2
;;
-if) -if)
hash="$(sha256sum "$2" | cut -d' ' -f1)" hash="$(sha256sum "$2" | cut -d' ' -f1)"
shift 2;; shift 2
;;
*) *)
break;; break
;;
esac esac
done done
tpm2 pcrextend "$index:sha256=$hash" tpm2 pcrextend "$index:sha256=$hash"
@ -255,12 +259,14 @@ tpm2_counter_read() {
case "$1" in case "$1" in
-ix) -ix)
index="$2" index="$2"
shift 2;; shift 2
;;
*) *)
break;; break
;;
esac esac
done done
echo "$index: `tpm2 nvread 0x$index | xxd -pc8`" echo "$index: $(tpm2 nvread 0x$index | xxd -pc8)"
} }
tpm2_counter_inc() { tpm2_counter_inc() {
@ -269,41 +275,60 @@ tpm2_counter_inc() {
case "$1" in case "$1" in
-ix) -ix)
index="$2" index="$2"
shift 2;; shift 2
;;
-pwdc) -pwdc)
pwd="$2" pwd="$2"
shift 2;; shift 2
;;
*) *)
break;; break
;;
esac esac
done done
tpm2 nvincrement "0x$index" >/dev/console tpm2 nvincrement "0x$index" >/dev/console
echo "$index: `tpm2 nvread 0x$index | xxd -pc8`" echo "$index: $(tpm2 nvread 0x$index | xxd -pc8)"
} }
tpm2_counter_cre() { tpm1_counter_create() {
TRACE "Under /bin/tpmr:tpm2_counter_cre" TRACE "Under /bin/tpmr:tpm1_counter_create"
# tpmr handles the TPM owner password (from cache or prompt), but all
# other parameters for TPM1 are passed directly, and TPM2 mimics the
# TPM1 interface.
prompt_tpm_owner_password
if ! tpm counter_create -pwdo "$(cat "/tmp/secret/tpm_owner_password")" "$@"; then
DEBUG "Failed to create counter from tpm1_counter_create. Wiping /tmp/secret/tpm_owner_password"
shred -n 10 -z -u /tmp/secret/tpm_owner_password
die "Unable to create counter from tpm1_counter_create"
fi
}
tpm2_counter_create() {
TRACE "Under /bin/tpmr:tpm2_counter_create"
while true; do while true; do
case "$1" in case "$1" in
-pwdo)
pwdo="$2"
shift 2;;
-pwdof)
pwdo="file:$2"
shift 2;;
-pwdc) -pwdc)
pwd="$2" pwd="$2"
shift 2;; shift 2
;;
-la) -la)
label="$2" label="$2"
shift 2;; shift 2
;;
*) *)
break;; break
;;
esac esac
done done
rand_index="1`dd if=/dev/urandom bs=1 count=3 | xxd -pc3`" prompt_tpm_owner_password
rand_index="1$(dd if=/dev/urandom bs=1 count=3 | xxd -pc3)"
tpm2 nvdefine -C o -s 8 -a "ownerread|authread|authwrite|nt=1" \ tpm2 nvdefine -C o -s 8 -a "ownerread|authread|authwrite|nt=1" \
-P "$(tpm2_password_hex "$pwdo")" "0x$rand_index" > /dev/console -P "$(tpm2_password_hex "$(cat "/tmp/secret/tpm_owner_password")")" "0x$rand_index" >/dev/console ||
{
DEBUG "Failed to create counter from tpm2_counter_create. Wiping /tmp/secret/tpm_owner_password"
shred -n 10 -z -u /tmp/secret/tpm_owner_password
die "Unable to create counter from tpm2_counter_create"
}
echo "$rand_index: (valid after an increment)" echo "$rand_index: (valid after an increment)"
} }
@ -311,20 +336,20 @@ tpm2_startsession() {
TRACE "Under /bin/tpmr:tpm2_startsession" TRACE "Under /bin/tpmr:tpm2_startsession"
mkdir -p "$SECRET_DIR" mkdir -p "$SECRET_DIR"
tpm2 flushcontext -Q \ tpm2 flushcontext -Q \
--transient-object \ --transient-object ||
|| die "tpm2_flushcontext: unable to flush transient handles" die "tpm2_flushcontext: unable to flush transient handles"
tpm2 flushcontext -Q \ tpm2 flushcontext -Q \
--loaded-session \ --loaded-session ||
|| die "tpm2_flushcontext: unable to flush sessions" die "tpm2_flushcontext: unable to flush sessions"
tpm2 flushcontext -Q \ tpm2 flushcontext -Q \
--saved-session \ --saved-session ||
|| die "tpm2_flushcontext: unable to flush saved session" die "tpm2_flushcontext: unable to flush saved session"
tpm2 readpublic -Q -c "$PRIMARY_HANDLE" -t "/tmp/$PRIMARY_HANDLE_FILE" tpm2 readpublic -Q -c "$PRIMARY_HANDLE" -t "$PRIMARY_HANDLE_FILE"
tpm2 startauthsession -Q -c "/tmp/$PRIMARY_HANDLE_FILE" --hmac-session -S "/tmp/$ENC_SESSION_FILE" tpm2 startauthsession -Q -c "$PRIMARY_HANDLE_FILE" --hmac-session -S "$ENC_SESSION_FILE"
tpm2 startauthsession -Q -c "/tmp/$PRIMARY_HANDLE_FILE" --hmac-session -S "/tmp/$DEC_SESSION_FILE" tpm2 startauthsession -Q -c "$PRIMARY_HANDLE_FILE" --hmac-session -S "$DEC_SESSION_FILE"
tpm2 sessionconfig -Q --disable-encrypt "/tmp/$DEC_SESSION_FILE" tpm2 sessionconfig -Q --disable-encrypt "$DEC_SESSION_FILE"
} }
# Use cleanup_session() with at_exit to release a TPM2 session and delete the # Use cleanup_session() with at_exit to release a TPM2 session and delete the
@ -361,8 +386,8 @@ tpm2_destroy() {
handle="$(printf "0x81%6s" "$index" | tr ' ' 0)" handle="$(printf "0x81%6s" "$index" | tr ' ' 0)"
# remove possible data occupying this handle # remove possible data occupying this handle
tpm2 evictcontrol -Q -C p -c "$handle" 2>/dev/null \ tpm2 evictcontrol -Q -C p -c "$handle" 2>/dev/null ||
|| die "Unable to evict secret" die "Unable to evict secret from TPM NVRAM"
} }
# tpm1_destroy: Destroy a sealed file in the TPM. The mechanism differs by # tpm1_destroy: Destroy a sealed file in the TPM. The mechanism differs by
@ -373,8 +398,8 @@ tpm1_destroy() {
size="$2" # Size of zeroes to overwrite for TPM1 size="$2" # Size of zeroes to overwrite for TPM1
dd if=/dev/zero bs="$size" count=1 of=/tmp/wipe-totp-zero dd if=/dev/zero bs="$size" count=1 of=/tmp/wipe-totp-zero
tpm nv_writevalue -in "$index" -if /tmp/wipe-totp-zero \ tpm nv_writevalue -in "$index" -if /tmp/wipe-totp-zero ||
|| die "Unable to wipe sealed secret" die "Unable to wipe sealed secret from TPM NVRAM"
} }
# tpm2_seal: Seal a file against PCR values and, optionally, a password. # tpm2_seal: Seal a file against PCR values and, optionally, a password.
@ -391,10 +416,10 @@ tpm2_seal() {
sealed_size="$5" # Not used for TPM2 sealed_size="$5" # Not used for TPM2
pass="$6" # May be empty to seal with no password pass="$6" # May be empty to seal with no password
tpm_password="$7" # Owner password - will prompt if needed and not empty tpm_password="$7" # Owner password - will prompt if needed and not empty
# Owner password is always needed for TPM2. # TPM Owner Password is always needed for TPM2.
mkdir -p "$SECRET_DIR" mkdir -p "$SECRET_DIR"
bname="`basename $file`" bname="$(basename $file)"
# Pad with up to 6 zeros, i.e. '0x81000001', '0x81001234', etc. # Pad with up to 6 zeros, i.e. '0x81000001', '0x81001234', etc.
handle="$(printf "0x81%6s" "$index" | tr ' ' 0)" handle="$(printf "0x81%6s" "$index" | tr ' ' 0)"
@ -403,8 +428,8 @@ tpm2_seal() {
# Create a policy requiring both PCRs and the object's authentication # Create a policy requiring both PCRs and the object's authentication
# value using a trial session. # value using a trial session.
TRIAL_SESSION=/tmp/sealfile_trial.session TRIAL_SESSION="$SECRET_DIR/sealfile_trial.session"
AUTH_POLICY=/tmp/sealfile_auth.policy AUTH_POLICY="$SECRET_DIR/sealfile_auth.policy"
rm -f "$TRIAL_SESSION" "$AUTH_POLICY" rm -f "$TRIAL_SESSION" "$AUTH_POLICY"
tpm2 startauthsession -g sha256 -S "$TRIAL_SESSION" tpm2 startauthsession -g sha256 -S "$TRIAL_SESSION"
# We have to clean up the session # We have to clean up the session
@ -430,25 +455,30 @@ tpm2_seal() {
# (The default is to allow either policy auth _or_ password auth. In # (The default is to allow either policy auth _or_ password auth. In
# this case the policy includes the password, and we don't want to allow # this case the policy includes the password, and we don't want to allow
# the password on its own.) # the password on its own.)
tpm2 create -Q -C "/tmp/$PRIMARY_HANDLE_FILE" \ tpm2 create -Q -C "$PRIMARY_HANDLE_FILE" \
-i "$file" \ -i "$file" \
-u "$SECRET_DIR/$bname.priv" \ -u "$SECRET_DIR/$bname.priv" \
-r "$SECRET_DIR/$bname.pub" \ -r "$SECRET_DIR/$bname.pub" \
-L "$AUTH_POLICY" \ -L "$AUTH_POLICY" \
-S "/tmp/$DEC_SESSION_FILE" \ -S "$DEC_SESSION_FILE" \
-a "fixedtpm|fixedparent|adminwithpolicy" \ -a "fixedtpm|fixedparent|adminwithpolicy" \
"${CREATE_PASS_ARGS[@]}" "${CREATE_PASS_ARGS[@]}"
tpm2 load -Q -C "/tmp/$PRIMARY_HANDLE_FILE" \ tpm2 load -Q -C "$PRIMARY_HANDLE_FILE" \
-u "$SECRET_DIR/$bname.priv" -r "$SECRET_DIR/$bname.pub" \ -u "$SECRET_DIR/$bname.priv" -r "$SECRET_DIR/$bname.pub" \
-c "$SECRET_DIR/$bname.seal.ctx" -c "$SECRET_DIR/$bname.seal.ctx"
prompt_tpm_password prompt_tpm_owner_password
# remove possible data occupying this handle # remove possible data occupying this handle
tpm2 evictcontrol -Q -C o -P "$(tpm2_password_hex "$tpm_password")" \ tpm2 evictcontrol -Q -C o -P "$(tpm2_password_hex "$tpm_owner_password")" \
-c "$handle" 2>/dev/null || true -c "$handle" 2>/dev/null || true
DO_WITH_DEBUG --mask-position 6 \ DO_WITH_DEBUG --mask-position 6 \
tpm2 evictcontrol -Q -C o -P "$(tpm2_password_hex "$tpm_password")" \ tpm2 evictcontrol -Q -C o -P "$(tpm2_password_hex "$tpm_owner_password")" \
-c "$SECRET_DIR/$bname.seal.ctx" "$handle" -c "$SECRET_DIR/$bname.seal.ctx" "$handle" ||
{
DEBUG "Failed to write sealed secret to NVRAM from tpm2_seal. Wiping /tmp/secret/tpm_owner_password"
shred -n 10 -z -u /tmp/secret/tpm_owner_password
die "Unable to write sealed secret to TPM NVRAM"
}
} }
tpm1_seal() { tpm1_seal() {
TRACE "Under /bin/tpmr:tpm1_seal" TRACE "Under /bin/tpmr:tpm1_seal"
@ -487,25 +517,28 @@ tpm1_seal() {
-hk 40000000 \ -hk 40000000 \
"${POLICY_ARGS[@]}" "${POLICY_ARGS[@]}"
# try it without the owner password first # try it without the TPM Owner Password first
if ! tpm nv_writevalue -in "$index" -if "$sealed_file"; then if ! tpm nv_writevalue -in "$index" -if "$sealed_file"; then
# to create an nvram space we need the TPM owner password # to create an nvram space we need the TPM Owner Password
# and the TPM physical presence must be asserted. # and the TPM physical presence must be asserted.
# #
# The permissions are 0 since there is nothing special # The permissions are 0 since there is nothing special
# about the sealed file # about the sealed file
tpm physicalpresence -s \ tpm physicalpresence -s ||
|| warn "Unable to assert physical presence" warn "Unable to assert physical presence"
prompt_tpm_password prompt_tpm_owner_password
tpm nv_definespace -in "$index" -sz "$sealed_size" \ tpm nv_definespace -in "$index" -sz "$sealed_size" \
-pwdo "$tpm_password" -per 0 \ -pwdo "$tpm_password" -per 0 ||
|| warn "Unable to define NVRAM space; trying anyway" warn "Unable to define TPM NVRAM space; trying anyway"
tpm nv_writevalue -in "$index" -if "$sealed_file" ||
tpm nv_writevalue -in "$index" -if "$sealed_file" \ {
|| die "Unable to write sealed secret to NVRAM" DEBUG "Failed to write sealed secret to NVRAM from tpm1_seal. Wiping /tmp/secret/tpm_owner_password"
shred -n 10 -z -u /tmp/secret/tpm_owner_password
die "Unable to write sealed secret to TPM NVRAM"
}
fi fi
} }
@ -531,12 +564,13 @@ tpm2_unseal() {
# If we don't have the primary handle (TPM hasn't been reset), tpm2 will # If we don't have the primary handle (TPM hasn't been reset), tpm2 will
# print nonsense error messages about an unexpected handle value. We # print nonsense error messages about an unexpected handle value. We
# can't do anything without a primary handle. # can't do anything without a primary handle.
if [ ! -f "/tmp/$PRIMARY_HANDLE_FILE" ]; then if [ ! -f "$PRIMARY_HANDLE_FILE" ]; then
DEBUG "tpm2_unseal: No primary handle, cannot attempt to unseal" 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"
exit 1 exit 1
fi fi
POLICY_SESSION=/tmp/unsealfile_policy.session POLICY_SESSION="$SECRET_DIR/unsealfile_policy.session"
rm -f "$POLICY_SESSION" rm -f "$POLICY_SESSION"
tpm2 startauthsession -Q -g sha256 -S "$POLICY_SESSION" --policy-session tpm2 startauthsession -Q -g sha256 -S "$POLICY_SESSION" --policy-session
at_exit cleanup_session "$POLICY_SESSION" at_exit cleanup_session "$POLICY_SESSION"
@ -554,7 +588,7 @@ tpm2_unseal() {
fi fi
tpm2 unseal -Q -c "$handle" -p "session:$POLICY_SESSION$UNSEAL_PASS_SUFFIX" \ tpm2 unseal -Q -c "$handle" -p "session:$POLICY_SESSION$UNSEAL_PASS_SUFFIX" \
-S "/tmp/$ENC_SESSION_FILE" > "$file" -S "$ENC_SESSION_FILE" >"$file"
} }
tpm1_unseal() { tpm1_unseal() {
TRACE "Under /bin/tpmr:tpm1_unseal" TRACE "Under /bin/tpmr:tpm1_unseal"
@ -576,8 +610,8 @@ tpm1_unseal() {
tpm nv_readvalue \ tpm nv_readvalue \
-in "$index" \ -in "$index" \
-sz "$sealed_size" \ -sz "$sealed_size" \
-of "$sealed_file" \ -of "$sealed_file" ||
|| die "Unable to read sealed file from TPM NVRAM" die "Unable to read sealed file from TPM NVRAM"
PASS_ARGS=() PASS_ARGS=()
if [ "$pass" ]; then if [ "$pass" ]; then
@ -593,15 +627,18 @@ tpm1_unseal() {
tpm2_reset() { tpm2_reset() {
TRACE "Under /bin/tpmr:tpm2_reset" TRACE "Under /bin/tpmr:tpm2_reset"
key_password="$1" tpm_owner_password="$1"
mkdir -p "$SECRET_DIR" mkdir -p "$SECRET_DIR"
# output TPM Owner Password to a file to be reused in this boot session until recovery shell/reboot
DEBUG "Caching TPM Owner Password to $SECRET_DIR/tpm_owner_password"
echo -n "$tpm_owner_password" >"$SECRET_DIR/tpm_owner_password"
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 owner "$(tpm2_password_hex "$tpm_owner_password")"
tpm2 changeauth -c endorsement "$(tpm2_password_hex "$key_password")" tpm2 changeauth -c endorsement "$(tpm2_password_hex "$tpm_owner_password")"
tpm2 createprimary -C owner -g sha256 -G "${CONFIG_PRIMARY_KEY_TYPE:-rsa}" \ tpm2 createprimary -C owner -g sha256 -G "${CONFIG_PRIMARY_KEY_TYPE:-rsa}" \
-c "$SECRET_DIR/primary.ctx" -P "$(tpm2_password_hex "$key_password")" -c "$SECRET_DIR/primary.ctx" -P "$(tpm2_password_hex "$tpm_owner_password")"
tpm2 evictcontrol -C owner -c "$SECRET_DIR/primary.ctx" "$PRIMARY_HANDLE" \ tpm2 evictcontrol -C owner -c "$SECRET_DIR/primary.ctx" "$PRIMARY_HANDLE" \
-P "$(tpm2_password_hex "$key_password")" -P "$(tpm2_password_hex "$tpm_owner_password")"
shred -u "$SECRET_DIR/primary.ctx" shred -u "$SECRET_DIR/primary.ctx"
tpm2_startsession tpm2_startsession
@ -610,7 +647,7 @@ tpm2_reset() {
# * --max-tries=10: Allow 10 failures before lockout. This allows the # * --max-tries=10: Allow 10 failures before lockout. This allows the
# user to quickly "burst" 10 failures without significantly impacting # user to quickly "burst" 10 failures without significantly impacting
# the rate allowed for a dictionary attacker. # the rate allowed for a dictionary attacker.
# Most TPM2 flows ask for the owner password 2-4 times, so this allows # Most TPM2 flows ask for the TPM Owner Password 2-4 times, so this allows
# a handful of mistypes and some headroom for an expected unseal # a handful of mistypes and some headroom for an expected unseal
# failure if firmware is updated. # failure if firmware is updated.
# Remember that an auth failure is also counted any time an unclean # Remember that an auth failure is also counted any time an unclean
@ -626,7 +663,7 @@ tpm2_reset() {
--max-tries=10 \ --max-tries=10 \
--recovery-time=3600 \ --recovery-time=3600 \
--lockout-recovery-time=0 \ --lockout-recovery-time=0 \
--auth="session:/tmp/$ENC_SESSION_FILE" --auth="session:$ENC_SESSION_FILE"
# Set a random DA lockout password, so the DA lockout can't be cleared # Set a random DA lockout password, so the DA lockout can't be cleared
# with a password. Heads doesn't offer dictionary attach reset, instead # with a password. Heads doesn't offer dictionary attach reset, instead
@ -639,15 +676,18 @@ tpm2_reset() {
} }
tpm1_reset() { tpm1_reset() {
TRACE "Under /bin/tpmr:tpm1_reset" TRACE "Under /bin/tpmr:tpm1_reset"
key_password="$1" tpm_owner_password="$1"
mkdir -p "$SECRET_DIR"
# output tpm_owner_password to a file to be reused in this boot session until recovery shell/reboot
DEBUG "Caching TPM Owner Password to $SECRET_DIR/tpm_owner_password"
echo -n "$tpm_owner_password" >"$SECRET_DIR/tpm_owner_password"
# Make sure the TPM is ready to be reset # Make sure the TPM is ready to be reset
tpm physicalpresence -s tpm physicalpresence -s
tpm physicalenable tpm physicalenable
tpm physicalsetdeactivated -c tpm physicalsetdeactivated -c
tpm forceclear tpm forceclear
tpm physicalenable tpm physicalenable
tpm takeown -pwdo "$key_password" tpm takeown -pwdo "$tpm_owner_password"
# And now turn it all back on # And now turn it all back on
tpm physicalpresence -s tpm physicalpresence -s
@ -660,20 +700,20 @@ tpm2_kexec_finalize() {
TRACE "Under /bin/tpmr:tpm2_kexec_finalize" TRACE "Under /bin/tpmr:tpm2_kexec_finalize"
# Flush sessions and transient objects # Flush sessions and transient objects
tpm2 flushcontext -Q --transient-object \ tpm2 flushcontext -Q --transient-object ||
|| warn "tpm2_flushcontext: unable to flush transient handles" warn "tpm2_flushcontext: unable to flush transient handles"
tpm2 flushcontext -Q --loaded-session \ tpm2 flushcontext -Q --loaded-session ||
|| warn "tpm2_flushcontext: unable to flush sessions" warn "tpm2_flushcontext: unable to flush sessions"
tpm2 flushcontext -Q --saved-session \ tpm2 flushcontext -Q --saved-session ||
|| warn "tpm2_flushcontext: unable to flush saved session" warn "tpm2_flushcontext: unable to flush saved session"
# Add a random passphrase to platform hierarchy to prevent TPM2 from # Add a random passphrase to platform hierarchy to prevent TPM2 from
# being cleared in the OS. # being cleared in the OS.
# This passphrase is only effective before the next boot. # This passphrase is only effective before the next boot.
echo "Locking TPM2 platform hierarchy..." echo "Locking TPM2 platform hierarchy..."
randpass=$(dd if=/dev/urandom bs=4 count=1 status=none | xxd -p) randpass=$(dd if=/dev/urandom bs=4 count=1 status=none | xxd -p)
tpm2 changeauth -c platform "$randpass" \ tpm2 changeauth -c platform "$randpass" ||
|| warn "Failed to lock platform hierarchy of TPM2" warn "Failed to lock platform hierarchy of TPM2"
} }
tpm2_shutdown() { tpm2_shutdown() {
@ -698,25 +738,39 @@ if [ "$CONFIG_TPM2_TOOLS" != "y" ]; then
# Don't shift yet, for most commands we will just forward to tpm. # Don't shift yet, for most commands we will just forward to tpm.
case "$subcmd" in case "$subcmd" in
pcrread) pcrread)
shift; tpm1_pcrread "$@";; shift
tpm1_pcrread "$@"
;;
pcrsize) pcrsize)
echo "$PCR_SIZE";; echo "$PCR_SIZE"
;;
calcfuturepcr) calcfuturepcr)
shift; replay_pcr "sha1" "$@";; shift
replay_pcr "sha1" "$@"
;;
counter_create)
shift
tpm1_counter_create "$@"
;;
destroy) destroy)
shift; tpm1_destroy "$@";; shift
tpm1_destroy "$@"
;;
seal) seal)
shift; tpm1_seal "$@";; shift
startsession) tpm1_seal "$@"
;; # Nothing on TPM1. ;;
startsession) ;; # Nothing on TPM1.
unseal) unseal)
shift; tpm1_unseal "$@";; shift
tpm1_unseal "$@"
;;
reset) reset)
shift; tpm1_reset "$@";; shift
kexec_finalize) tpm1_reset "$@"
;; # Nothing on TPM1. ;;
shutdown) kexec_finalize) ;; # Nothing on TPM1.
;; # Nothing on TPM1. shutdown) ;; # Nothing on TPM1.
*) *)
DEBUG "Direct translation from tpmr to tpm1 call" DEBUG "Direct translation from tpmr to tpm1 call"
DO_WITH_DEBUG exec tpm "$@" DO_WITH_DEBUG exec tpm "$@"
@ -731,34 +785,49 @@ subcmd="$1"
shift 1 shift 1
case "$subcmd" in case "$subcmd" in
pcrread) pcrread)
tpm2_pcrread "$@";; tpm2_pcrread "$@"
;;
pcrsize) pcrsize)
echo "$PCR_SIZE";; echo "$PCR_SIZE"
;;
calcfuturepcr) calcfuturepcr)
replay_pcr "sha256" "$@";; replay_pcr "sha256" "$@"
;;
extend) extend)
tpm2_extend "$@";; tpm2_extend "$@"
;;
counter_read) counter_read)
tpm2_counter_read "$@";; tpm2_counter_read "$@"
;;
counter_increment) counter_increment)
tpm2_counter_inc "$@";; tpm2_counter_inc "$@"
;;
counter_create) counter_create)
tpm2_counter_cre "$@";; tpm2_counter_create "$@"
;;
destroy) destroy)
tpm2_destroy "$@";; tpm2_destroy "$@"
;;
seal) seal)
tpm2_seal "$@";; tpm2_seal "$@"
;;
startsession) startsession)
tpm2_startsession "$@";; tpm2_startsession "$@"
;;
unseal) unseal)
tpm2_unseal "$@";; tpm2_unseal "$@"
;;
reset) reset)
tpm2_reset "$@";; tpm2_reset "$@"
;;
kexec_finalize) kexec_finalize)
tpm2_kexec_finalize "$@";; tpm2_kexec_finalize "$@"
;;
shutdown) shutdown)
tpm2_shutdown "$@";; tpm2_shutdown "$@"
;;
*) *)
echo "Command $subcmd not wrapped!" echo "Command $subcmd not wrapped!"
exit 1 exit 1
;;
esac esac

View File

@ -8,8 +8,8 @@ TOTP_SECRET="/tmp/secret/totp.key"
TRACE "Under /bin/unseal-totp" TRACE "Under /bin/unseal-totp"
if [ "$CONFIG_TPM" = "y" ]; then if [ "$CONFIG_TPM" = "y" ]; then
tpmr unseal 4d47 0,1,2,3,4,7 312 "$TOTP_SECRET" \ tpmr unseal 4d47 0,1,2,3,4,7 312 "$TOTP_SECRET" ||
|| die "Unable to unseal totp secret" die "Unable to unseal TOTP secret"
fi fi
if ! totp -q <"$TOTP_SECRET"; then if ! totp -q <"$TOTP_SECRET"; then

View File

@ -53,6 +53,163 @@ preserve_rom() {
done done
} }
confirm_gpg_card() {
TRACE "Under /etc/ash_functions:confirm_gpg_card"
#Skip prompts if we are currently using a known GPG key material Thumb drive backup and keys are unlocked pinentry
#TODO: probably export CONFIG_GPG_KEY_BACKUP_IN_USE but not under /etc/user.config?
#Toggle to come in next PR, but currently we don't have a way to toggle it back to n if config.user flashed back in rom
if [[ "$CONFIG_HAVE_GPG_KEY_BACKUP" == "y" && "$CONFIG_GPG_KEY_BACKUP_IN_USE" == "y" ]]; then
DEBUG "Using known GPG key material Thumb drive backup and keys are unlocked and useable through pinentry"
return
fi
if [ "$CONFIG_HAVE_GPG_KEY_BACKUP" == "y" ]; then
message="Please confirm that your GPG card is inserted(Y/n) or your GPG key material (b)backup thumbdrive is inserted [Y/n/b]: "
else
# Generic message if no known key material backup
message="Please confirm that your GPG card is inserted [Y/n]: "
fi
read \
-n 1 \
-p "$message" \
card_confirm
echo
if [ "$card_confirm" != "y" \
-a "$card_confirm" != "Y" \
-a "$card_confirm" != "b" \
-a -n "$card_confirm" ] \
; then
die "gpg card not confirmed"
fi
# If user has known GPG key material Thumb drive backup and asked to use it
if [[ "$CONFIG_HAVE_GPG_KEY_BACKUP" == "y" && "$card_confirm" == "b" ]]; then
#Only mount and import GPG key material thumb drive backup once
if [ ! "$CONFIG_GPG_KEY_BACKUP_IN_USE" == "y" ]; then
CR_NONCE="/tmp/secret/cr_nonce"
CR_SIG="$CR_NONCE.sig"
#Wipe any previous CR_NONCE and CR_SIG
shred -n 10 -z -u "$CR_NONCE" "$CR_SIG" >/dev/null 2>&1 || true
#Prompt user for configured GPG Admin PIN that will be passed along to mount-usb and to import gpg subkeys
echo
gpg_admin_pin=""
while [ -z "$gpg_admin_pin" ]; do
#TODO: change all passphrase prompts in codebase to include -r to prevent backslash escapes
read -r -s -p "Please enter GPG Admin PIN needed to use the GPG backup thumb drive: " gpg_admin_pin
echo
done
#prompt user to select the proper encrypted partition, which should the first one on next prompt
warn "Please select encrypted LUKS on GPG key material backup thumb drive (not public labeled one)"
mount-usb --pass "$gpg_admin_pin" || die "Unable to mount USB with provided GPG Admin PIN"
echo "++++ Testing detach-sign operation and verifiying against fused public key in ROM"
gpg --pinentry-mode=loopback --passphrase-file <(echo -n "${gpg_admin_pin}") --import /media/subkeys.sec >/dev/null 2>&1 ||
die "Unable to import GPG private subkeys"
#Do a detach signature to ensure gpg material is usable and cache passphrase to sign /boot from caller functions
dd if=/dev/urandom of="$CR_NONCE" bs=20 count=1 >/dev/null 2>&1 ||
die "Unable to create $CR_NONCE to be detach-signed with GPG private signing subkey"
gpg --pinentry-mode=loopback --passphrase-file <(echo -n "${gpg_admin_pin}") --detach-sign "$CR_NONCE" >/dev/null 2>&1 ||
die "Unable to detach-sign $CR_NONCE with GPG private signing subkey using GPG Admin PIN"
#verify detached signature against public key in rom
gpg --verify "$CR_SIG" "$CR_NONCE" > /dev/null 2>&1 && \
echo "++++ Local GPG keyring can be used to sign/encrypt/authenticate in this boot session ++++" || \
die "Unable to verify $CR_SIG detached signature against public key in ROM"
#Wipe any previous CR_NONCE and CR_SIG
shred -n 10 -z -u "$CR_NONCE" "$CR_SIG" >/dev/null 2>&1 || true
#TODO: maybe just an export instead of setting /etc/user.config otherwise could be flashed in weird corner case situation
set_user_config "CONFIG_GPG_KEY_BACKUP_IN_USE" "y"
umount /media || die "Unable to unmount USB"
return
fi
fi
# setup the USB so we can reach the USB Security Dongle's smartcard
enable_usb
echo -e "\nVerifying presence of GPG card...\n"
# ensure we don't exit without retrying
errexit=$(set -o | grep errexit | awk '{print $2}')
set +e
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"
fi
# restore prev errexit state
if [ "$errexit" = "on" ]; then
set -e
fi
}
gpg_auth() {
if [[ "$CONFIG_HAVE_GPG_KEY_BACKUP" == "y" ]]; then
TRACE "Under /etc/ash_functions:gpg_auth"
# If we have a GPG key backup, we can use it to authenticate even if the card is lost
echo >&2 "!!!!! Please authenticate with OpenPGP smartcard/backup media to prove you are the owner of this machine !!!!!"
# Wipe any existing nonce and signature
shred -n 10 -z -u "$CR_NONCE" "$CR_SIG" 2>/dev/null || true
# In case of gpg_auth, we require confirmation of the card, so loop with confirm_gpg_card until we get it
false
while [ $? -ne 0 ]; do
# Call confirm_gpg_card in subshell to ensure GPG key material presence
( confirm_gpg_card )
done
# Perform a signing-based challenge-response,
# to authencate that the card plugged in holding
# the key to sign the list of boot files.
CR_NONCE="/tmp/secret/cr_nonce"
CR_SIG="$CR_NONCE.sig"
# Generate a random nonce
dd \
if=/dev/urandom \
of="$CR_NONCE" \
count=1 \
bs=20 \
2>/dev/null \
|| die "Unable to generate 20 random bytes"
# Sign the nonce
for tries in 1 2 3; do
if gpg --digest-algo SHA256 \
--detach-sign \
-o "$CR_SIG" \
"$CR_NONCE" > /dev/null 2>&1 \
&& gpg --verify "$CR_SIG" "$CR_NONCE" > /dev/null 2>&1 \
; then
shred -n 10 -z -u "$CR_NONCE" "$CR_SIG" 2>/dev/null || true
DEBUG "Under /etc/ash_functions:gpg_auth: success"
return 0
else
shred -n 10 -z -u "$CR_SIG" 2>/dev/null || true
if [ "$tries" -lt 3 ]; then
echo >&2 "!!!!! GPG authentication failed, please try again !!!!!"
continue
else
die "GPG authentication failed, please reboot and try again"
fi
fi
done
return 1
fi
}
recovery() { recovery() {
TRACE "Under /etc/ash_functions:recovery" TRACE "Under /etc/ash_functions:recovery"
echo >&2 "!!!!! $*" echo >&2 "!!!!! $*"
@ -70,6 +227,7 @@ recovery() {
. /tmp/config . /tmp/config
if [ "$CONFIG_TPM" = "y" ]; then if [ "$CONFIG_TPM" = "y" ]; then
DEBUG "Extending TPM PCR 4 for recovery shell access"
tpmr extend -ix 4 -ic recovery tpmr extend -ix 4 -ic recovery
fi fi
@ -80,6 +238,9 @@ recovery() {
fi fi
while [ true ] while [ true ]
do do
#Going to recovery shell should be authenticated if supported
gpg_auth
echo >&2 "!!!!! Starting recovery shell" echo >&2 "!!!!! Starting recovery shell"
sleep 1 sleep 1
@ -102,6 +263,57 @@ combine_configs() {
cat /etc/config* > /tmp/config cat /etc/config* > /tmp/config
} }
replace_config() {
TRACE "Under /etc/functions:replace_config"
CONFIG_FILE=$1
CONFIG_OPTION=$2
NEW_SETTING=$3
touch $CONFIG_FILE
# 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}
rm -f ${CONFIG_FILE}.tmp
}
# Set a config variable in a specific file to a given value - replace it if it
# exists, or add it. If added, the variable will be exported.
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
echo "export $CONFIG_OPTION=\"$NEW_SETTING\"" >>"$CONFIG_FILE"
fi
}
# Set a value in config.user, re-combine configs, and update configs in the
# environment.
set_user_config() {
CONFIG_OPTION="$1"
NEW_SETTING="$2"
set_config /etc/config.user "$CONFIG_OPTION" "$NEW_SETTING"
combine_configs
. /tmp/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
}
enable_usb() enable_usb()
{ {
TRACE "Under /etc/ash_functions:enable_usb" TRACE "Under /etc/ash_functions:enable_usb"

View File

@ -96,16 +96,15 @@ reseal_tpm_disk_decryption_key() {
fi fi
if [ -s /boot/kexec_key_devices.txt ] || [ -s /boot/kexec_key_lvm.txt ]; then 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 could not unseal TOTP" warn "TPM sealed Disk Unlock Key secret needs to be resealed alongside TOTP/HOTP secret"
echo "Renewing LUKS Disk Unlock Key to be unsealed by TPM Disk Unlock Key passphrase" echo "Resealing TPM LUKS Disk Unlock Key to be unsealed by TPM Disk Unlock Key passphrase"
while ! kexec-seal-key /boot; do while ! kexec-seal-key /boot; do
warn "Recovery Disk Encryption key passphrase invalid. Try again!" warn "Recovery Disk Encryption key passphrase/TPM Owner Password may be invalid. Please try again"
done done
warn "LUKS header hash changed under /boot/kexec_luks_hdr_hash.txt" warn "LUKS header hash changed under /boot/kexec_luks_hdr_hash.txt"
echo "Updating checksums and signing all files under /boot/kexec.sig" echo "Updating checksums and signing all files under /boot/kexec.sig"
while ! update_checksums; do while ! update_checksums; do
warn "Checksums were not signed. Bad GPG PIN provided?" warn "Checksums were not signed. Preceding errors should explain possible causes"
warn "Please update checksums and provide a valid GPG PIN"
done done
warn "Rebooting in 3 seconds to enable booting default boot option" warn "Rebooting in 3 seconds to enable booting default boot option"
sleep 3 sleep 3
@ -119,6 +118,7 @@ reseal_tpm_disk_decryption_key() {
# be detected. If USB storage was already enabled, no wait occurs, this would # be detected. If USB storage was already enabled, no wait occurs, this would
# have happened already when USB storage was enabled. # have happened already when USB storage was enabled.
enable_usb_storage() { enable_usb_storage() {
TRACE "Under /etc/functions:enable_usb_storage"
if ! lsmod | grep -q usb_storage; then if ! lsmod | grep -q usb_storage; then
timeout=0 timeout=0
echo "Scanning for USB storage devices..." echo "Scanning for USB storage devices..."
@ -189,83 +189,59 @@ list_usb_storage() {
done done
} }
confirm_gpg_card() { # Prompt for a TPM Owner Password if it is not already cached in /tmp/secret/tpm_owner_password.
TRACE "Under /etc/functions:confirm_gpg_card" # Sets tpm_owner_password variable reused in flow, and cache file used until recovery shell is accessed.
read \ # Tools should optionally accept a TPM password on the command line, since some flows need
-n 1 \ # it multiple times and only one prompt is ideal.
-p "Please confirm that your GPG card is inserted [Y/n]: " \ prompt_tpm_owner_password() {
card_confirm TRACE "Under /etc/functions:prompt_tpm_owner_password"
echo
if [ "$card_confirm" != "y" \ if [ -s /tmp/secret/tpm_owner_password ]; then
-a "$card_confirm" != "Y" \ DEBUG "/tmp/secret/tpm_owner_password already cached in file. Reusing"
-a -n "$card_confirm" ] \ tpm_owner_password=$(cat /tmp/secret/tpm_owner_password)
; then
die "gpg card not confirmed"
fi
# setup the USB so we can reach the GPG card
enable_usb
echo -e "\nVerifying presence of GPG card...\n"
# ensure we don't exit without retrying
errexit=$(set -o | grep errexit | awk '{print $2}')
set +e
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"
fi
# restore prev errexit state
if [ "$errexit" = "on" ]; then
set -e
fi
}
# Prompt for an owner password if it is not already set in tpm_password. Sets
# tpm_password. Tools should optionally accept a TPM password on the command
# 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 fi
read -s -p "TPM Owner password: " tpm_password read -s -p "TPM Owner Password: " tpm_owner_password
echo # new line after password prompt echo # new line after password prompt
# 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 owner_password under /tmp/secret/tpm_owner_password"
} }
# Prompt for a new owner password when resetting the TPM. Returned in # Prompt for a new TPM Owner Password when resetting the TPM.
# key_password. The password must be 1-32 characters and must be entered twice, # Returned in tpm_owner_passpword and cached under /tpm/secret/tpm_owner_password
# The password must be 1-32 characters and must be entered twice,
# the script will loop until this is met. # the script will loop until this is met.
prompt_new_owner_password() { prompt_new_owner_password() {
local key_password2 TRACE "Under /etc/functions:prompt_new_owner_password"
key_password=1 local tpm_owner_password2
key_password2=2 tpm_owner_password=1
while [ "$key_password" != "$key_password2" ] || [ "${#key_password}" -gt 32 ] || [ -z "$key_password" ]; do tpm_owner_password2=2
read -s -p "New TPM owner passphrase (2 words suggested, 1-32 characters max): " key_password while [ "$tpm_owner_password" != "$tpm_owner_password2" ] || [ "${#tpm_owner_password}" -gt 32 ] || [ -z "$tpm_owner_password" ]; do
read -s -p "New TPM Owner Password (2 words suggested, 1-32 characters max): " tpm_owner_password
echo echo
read -s -p "Repeat chosen TPM owner passphrase: " key_password2 read -s -p "Repeat chosen TPM Owner Password: " tpm_owner_password2
echo echo
if [ "$key_password" != "$key_password2" ]; then if [ "$tpm_owner_password" != "$tpm_owner_password2" ]; then
echo "Passphrases entered do not match. Try again!" echo "Passphrases entered do not match. Try again!"
echo echo
fi fi
done done
# 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"
} }
check_tpm_counter() { check_tpm_counter() {
TRACE "Under /etc/functions:check_tpm_counter" TRACE "Under /etc/functions:check_tpm_counter"
LABEL=${2:-3135106223} LABEL=${2:-3135106223}
tpm_password="$3" tpm_password="$3"
# if the /boot.hashes file already exists, read the TPM counter ID # if the /boot.hashes file already exists, read the TPM counter ID
@ -274,9 +250,7 @@ check_tpm_counter() {
TPM_COUNTER=$(grep counter- "$1" | cut -d- -f2) TPM_COUNTER=$(grep counter- "$1" | cut -d- -f2)
else else
warn "$1 does not exist; creating new TPM counter" warn "$1 does not exist; creating new TPM counter"
prompt_tpm_password
tpmr counter_create \ tpmr counter_create \
-pwdo "$tpm_password" \
-pwdc '' \ -pwdc '' \
-la $LABEL | -la $LABEL |
tee /tmp/counter || tee /tmp/counter ||
@ -299,7 +273,7 @@ increment_tpm_counter() {
TRACE "Under /etc/functions:increment_tpm_counter" TRACE "Under /etc/functions:increment_tpm_counter"
tpmr counter_increment -ix "$1" -pwdc '' | tpmr counter_increment -ix "$1" -pwdc '' |
tee /tmp/counter-$1 || tee /tmp/counter-$1 ||
die "Counter increment failed" die "TPM counter increment failed for rollback prevention. Please reset the TPM"
} }
check_config() { check_config() {
@ -360,40 +334,6 @@ replace_config() {
rm -f ${CONFIG_FILE}.tmp rm -f ${CONFIG_FILE}.tmp
} }
# Set a config variable in a specific file to a given value - replace it if it
# exists, or add it. If added, the variable will be exported.
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
echo "export $CONFIG_OPTION=\"$NEW_SETTING\"" >>"$CONFIG_FILE"
fi
}
# Set a value in config.user, re-combine configs, and update configs in the
# environment.
set_user_config() {
CONFIG_OPTION="$1"
NEW_SETTING="$2"
set_config /etc/config.user "$CONFIG_OPTION" "$NEW_SETTING"
combine_configs
. /tmp/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
}
# Generate a secret for TPM-less HOTP by reading the ROM. Output is the # Generate a secret for TPM-less HOTP by reading the ROM. Output is the
# sha256sum of the ROM (binary, not printable), which can be truncated to the # sha256sum of the ROM (binary, not printable), which can be truncated to the
# supported secret length. # supported secret length.
@ -608,6 +548,7 @@ detect_boot_device() {
} }
scan_boot_options() { scan_boot_options() {
TRACE "Under /etc/functions:scan_boot_options"
local bootdir config option_file local bootdir config option_file
bootdir="$1" bootdir="$1"
config="$2" config="$2"

View File

@ -34,54 +34,100 @@ mount_usb()
fi fi
} }
# Create display text for a size in bytes in either MB or GB, unit selected
# automatically, rounded to nearest
display_size() {
local size_bytes unit_divisor unit_symbol
size_bytes="$1"
# If it's less than 1 GB, display MB
if [ "$((size_bytes))" -lt "$((1024*1024*1024))" ]; then
unit_divisor=$((1024*1024))
unit_symbol="MB"
else
unit_divisor=$((1024*1024*1024))
unit_symbol="GB"
fi
# Divide by the unit divisor and round to nearest
echo "$(( (size_bytes + unit_divisor/2) / unit_divisor )) $unit_symbol"
}
# Create display text for the size of a block device using MB or GB, rounded to
# nearest
display_block_device_size() {
local block_dev disk_size_bytes
block_dev="$1"
# Obtain size of thumb drive to be wiped with fdisk
if ! disk_size_bytes="$(blockdev --getsize64 "$block_dev")"; then
exit 1
fi
display_size "$disk_size_bytes"
}
# Display a menu to select a file from a list. Pass the name of a file
# containing the list.
# --show-size: Append sizes of files listed. Currently only supports block
# devices.
# $1: Name of file listing files that can be chosen (one per line)
# $2: Optional prompt message
# $3: Optional prompt title
#
# Success: Sets FILE with the selected file
# User aborted: Exits successfully with FILE empty
# No entries in list: Displays error and exits unsuccessfully
file_selector() file_selector()
{ {
TRACE "under gui_functions:file_selector" TRACE "under gui_functions:file_selector"
local FILE_LIST MENU_MSG MENU_TITLE CHOICE_ARGS SHOW_SIZE OPTION_SIZE option_index
FILE="" FILE=""
if [ "$1" = "--show-size" ]; then
SHOW_SIZE=y
shift
fi
FILE_LIST=$1 FILE_LIST=$1
MENU_MSG=${2:-"Choose the file"} MENU_MSG=${2:-"Choose the file"}
MENU_TITLE=${3:-"Select your File"} MENU_TITLE=${3:-"Select your File"}
# create file menu options CHOICE_ARGS=()
if [ `cat "$FILE_LIST" | wc -l` -gt 0 ]; then
option=""
while [ -z "$option" ]
do
MENU_OPTIONS=""
n=0 n=0
while read option while read option; do
do n="$((++n))"
n=`expr $n + 1`
option=$(echo $option | tr " " "_")
MENU_OPTIONS="$MENU_OPTIONS $n ${option}"
done < $FILE_LIST
MENU_OPTIONS="$MENU_OPTIONS a Abort" if [ "$SHOW_SIZE" = "y" ] && OPTION_SIZE="$(display_block_device_size "$option")"; then
whiptail --title "${MENU_TITLE}" \ option="$option - $OPTION_SIZE"
--menu "${MENU_MSG} [1-$n, a to abort]:" 20 120 8 \
-- $MENU_OPTIONS \
2>/tmp/whiptail || die "Aborting"
option_index=$(cat /tmp/whiptail)
if [ "$option_index" = "a" ]; then
option="a"
return
fi fi
CHOICE_ARGS+=("$n" "$option")
done < "$FILE_LIST"
option=`head -n $option_index $FILE_LIST | tail -1` if [ "${#CHOICE_ARGS[@]}" -eq 0 ]; then
if [ "$option" == "a" ]; then
return
fi
done
if [ -n "$option" ]; then
FILE=$option
fi
else
whiptail $BG_COLOR_ERROR --title 'ERROR: No Files Found' \ whiptail $BG_COLOR_ERROR --title 'ERROR: No Files Found' \
--msgbox "No Files found matching the pattern. Aborting." 0 80 --msgbox "No Files found matching the pattern. Aborting." 0 80
exit 1 exit 1
fi fi
CHOICE_ARGS+=(a Abort)
# create file menu options
option_index=""
while [ -z "$option_index" ]; do
whiptail --title "${MENU_TITLE}" \
--menu "${MENU_MSG} [1-$n, a to abort]:" 20 120 8 \
-- "${CHOICE_ARGS[@]}" \
2>/tmp/whiptail || die "Aborting"
option_index=$(cat /tmp/whiptail)
if [ "$option_index" != "a" ]; then
FILE="$(head -n "$option_index" "$FILE_LIST" | tail -1)"
fi
done
} }
show_system_info() show_system_info()

View File

@ -6,25 +6,19 @@
. /tmp/config . /tmp/config
#List all LUKS devices on the system #List all LUKS devices on the system
list_luks_devices() list_luks_devices() {
{
#generate a list of devices to choose from that contain a LUKS header #generate a list of devices to choose from that contain a LUKS header
lvm vgscan || true lvm vgscan || true
blkid | cut -d ':' -f 1 | while read device blkid | cut -d ':' -f 1 | while read device; do
do cryptsetup isLuks $device if cryptsetup isLuks $device; then echo $device; fi
if [ $? -eq 0 ]; then
echo "$device"
fi
done | sort done | sort
} }
#Whiptail prompt asking user to select ratio of device to use for LUKS container between: 25, 50, 75
#Whiptail prompt asking user to select ratio of device to use for LUKS container between: 10, 25, 50, 75 select_luks_container_size_percent() {
select_luks_container_size_percent()
{
TRACE "Under /etc/luks-functions:select_luks_container_size_percent()" TRACE "Under /etc/luks-functions:select_luks_container_size_percent()"
if [ -x /bin/whiptail ]; then if [ -x /bin/whiptail ]; then
#whiptail prompt asking user to select ratio of device to use for LUKS container between: 10, 25, 50, 75 #whiptail prompt asking user to select ratio of device to use for LUKS container between: 25, 50, 75
#whiptail returns the percentage of the device to use for LUKS container #whiptail returns the percentage of the device to use for LUKS container
whiptail --title "Select LUKS container size percentage of device" --menu \ whiptail --title "Select LUKS container size percentage of device" --menu \
"Select LUKS container size percentage of device:" 0 80 10 \ "Select LUKS container size percentage of device:" 0 80 10 \
@ -57,11 +51,12 @@ select_luks_container_size_percent()
fi fi
} }
#Partition a device with two partitions: a first one being a LUKS container containing private ext4 partition and second public exfat partition # Partition a device interactively with two partitions: a LUKS container
# containing private ext4 partition and second public exFAT partition
# Size provisioning is done by percentage of the device # Size provisioning is done by percentage of the device
prepare_thumb_drive() interactive_prepare_thumb_drive()
{ {
TRACE "Under /etc/luks-functions:prepare_thumb_drive()" TRACE "Under /etc/luks-functions:interactive_prepare_thumb_drive()"
#Refactoring: only one parameter needed to be prompted for: the passphrase for LUKS container if not coming from oem-provisioning #Refactoring: only one parameter needed to be prompted for: the passphrase for LUKS container if not coming from oem-provisioning
#If no passphrase was provided, ask user to select passphrase for LUKS container #If no passphrase was provided, ask user to select passphrase for LUKS container
# if no device provided as parameter, we will ask user to select device to partition # if no device provided as parameter, we will ask user to select device to partition
@ -84,12 +79,13 @@ prepare_thumb_drive()
PERCENTAGE=$2 PERCENTAGE=$2
shift 2 shift 2
;; ;;
--passphrase) --pass)
PASSPHRASE=$2 PASSPHRASE=$2
shift 2 shift 2
;; ;;
*) *)
echo "usage: prepare_thumb_drive [--device device] [--percentage percentage] [--passphrase passphrase]" echo "usage: prepare_thumb_drive [--device device] [--percentage percentage] [--pass passphrase]"
return 1
;; ;;
esac esac
done done
@ -177,32 +173,77 @@ prepare_thumb_drive()
PERCENTAGE=$(cat /tmp/luks_container_size_percent) PERCENTAGE=$(cat /tmp/luks_container_size_percent)
fi fi
confirm_thumb_drive_format "$DEVICE" "$PERCENTAGE" ||
die "User cancelled wiping and repartitioning of $DEVICE"
#Get disk size in bytes from fdisk prepare_thumb_drive "$DEVICE" "$PERCENTAGE" "$PASSPHRASE"
}
# Show a prompt to confirm formatting a flash drive with a percentage allocated
# to LUKS. interactive_prepare_thumb_drive() uses this; during OEM reset it is
# used separately before performing any reset actions
#
# parameters:
# $1 - block device of flash drive
# $2 - percent of device allocated to LUKS [1-99]
confirm_thumb_drive_format()
{
TRACE "Under /etc/luks-functions:confirm_thumb_drive_format()"
local DEVICE LUKS_PERCENTAGE DISK_SIZE_BYTES DISK_SIZE_DISPLAY LUKS_PERCENTAGE LUKS_SIZE_MB MSG
DEVICE="$1"
LUKS_PERCENTAGE="$2"
LUKS_SIZE_MB=
#Get disk size in bytes
DISK_SIZE_BYTES="$(blockdev --getsize64 "$DEVICE")" DISK_SIZE_BYTES="$(blockdev --getsize64 "$DEVICE")"
DISK_SIZE_DISPLAY="$(display_size "$DISK_SIZE_BYTES")"
#Convert disk size to MB #Convert disk size to MB
DISK_SIZE_MB=$((DISK_SIZE_BYTES/1024/1024)) DISK_SIZE_MB=$((DISK_SIZE_BYTES/1024/1024))
#Get size in bytes from percentage and apply percentage to DISK_SIZE_MB #Calculate percentage of device in MB
PERCENTAGE_MB="$((DISK_SIZE_MB*PERCENTAGE/100))" LUKS_SIZE_MB="$((DISK_SIZE_BYTES*LUKS_PERCENTAGE/100/1024/1024))"
#Console and whiptail $BG_COLOR_WARNING prompt (Y/n) validate one last time wiping and repartitioning of $device of total size $DISK_SIZE_MB with $PERCENTAGE_MB assigned to LUKS encrypted private partition MSG="WARNING: Wiping and repartitioning $DEVICE ($DISK_SIZE_DISPLAY) with $LUKS_SIZE_MB MB\n assigned to private LUKS ext4 partition,\n rest assigned to exFAT public partition.\n\nAre you sure you want to continue?"
if [ -x /bin/whiptail ]; then if [ -x /bin/whiptail ]; then
whiptail $BG_COLOR_WARNING --title "WARNING: Wiping and repartitioning $DEVICE of $DISK_SIZE_MB MB" --yesno \ whiptail $BG_COLOR_WARNING --title "WARNING: Wiping and repartitioning $DEVICE ($DISK_SIZE_DISPLAY)" --yesno \
"WARNING: Wiping and repartitioning $DEVICE with $PERCENTAGE_MB MB assigned to private LUKS contained private ext4 partition, rest assigned to extfat public partition.\n\nAre you sure you want to continue?" 0 80 \ "$MSG" 0 80
|| die "User cancelled wiping and repartitioning of $DEVICE"
else else
echo -e -n "Warning: Wiping and repartitioning $DEVICE with $PERCENTAGE_MB MB assigned to private LUKS contained private ext4 partition, rest assigned to extfat public partition.\n\nAre you sure you want to continue?" echo -e -n "$MSG"
read -r -p " [Y/n] " response read -r -p " [Y/n] " response
#transform response to uppercase with bash parameter expansion #transform response to uppercase with bash parameter expansion
response=${response^^} response=${response^^}
#continue if response different then uppercase N #continue if response is Y, y, or empty, abort for anything else
if [[ $response =~ ^(N)$ ]]; then if [ -n "$response" ] && [ "${response^^}" != Y ]; then
die "User cancelled wiping and repartitioning of $DEVICE" return 1
fi fi
fi fi
}
echo -e "Preparing $DEVICE with $PERCENTAGE_MB MB for private LUKS container and rest of disk with exfat\ # Prepare a flash drive with a private LUKS-encrypted ext4 partition and a
\n for public partition (This may take a while)..." | fold -s # public exFAT partition. This is not interactive - during OEM reset, any
# selections/confirmations must occur before OEM reset starts resetting the
# system.
#
# $1 - block device of flash drive
# $2 - percentage of flash drive to allocate to LUKS [1-99]
# $3 - passphrase for LUKS container
prepare_thumb_drive()
{
TRACE "Under /etc/luks-functions:prepare_thumb_drive()"
local DEVICE PERCENTAGE PASSPHRASE DISK_SIZE_BYTES PERCENTAGE_MB
DEVICE="$1"
PERCENTAGE="$2"
PASSPHRASE="$3"
#Get disk size in bytes
DISK_SIZE_BYTES="$(blockdev --getsize64 "$DEVICE")"
#Calculate percentage of device in MB
PERCENTAGE_MB="$((DISK_SIZE_BYTES*PERCENTAGE/100/1024/1024))"
echo -e "Preparing $DEVICE with $PERCENTAGE_MB MB for private LUKS container while rest of device will be assigned to exFAT public partition...\n"
echo "Please wait..."
DEBUG "Creating empty DOS partition table on device through fdisk to start clean" DEBUG "Creating empty DOS partition table on device through fdisk to start clean"
echo -e "o\nw\n" | fdisk $DEVICE >/dev/null 2>&1 || die "Error creating partition table" echo -e "o\nw\n" | fdisk $DEVICE >/dev/null 2>&1 || die "Error creating partition table"
DEBUG "partition device with two partitions: first one being the percent applied and rest for second partition through fdisk" DEBUG "partition device with two partitions: first one being the percent applied and rest for second partition through fdisk"
@ -246,7 +287,7 @@ select_luks_container()
mount -o remount,ro /boot mount -o remount,ro /boot
fi fi
else else
warn "No encrypted device found." warn "No encrypted device found"
return 1 return 1
fi fi
fi fi
@ -259,7 +300,7 @@ test_luks_current_disk_recovery_key_passphrase()
select_luks_container || return 1 select_luks_container || return 1
if [ -z "$luks_current_Disk_Recovery_Key_passphrase" ]; then if [ -z "$luks_current_Disk_Recovery_Key_passphrase" ]; then
#if no external provisioning provides current Disk Recovery Key passphrase #if no external provisioning provides current Disk Recovery Key passphrase
echo -e "\nEnter current Disk Recovery Key passphrase (Provisioned at OS installation or by OEM):" echo -e "\nEnter current Disk Recovery Key passphrase (Configured at OS installation or by OEM):"
read -r luks_current_Disk_Recovery_Key_passphrase read -r luks_current_Disk_Recovery_Key_passphrase
echo -n "$luks_current_Disk_Recovery_Key_passphrase" >/tmp/luks_current_Disk_Recovery_Key_passphrase echo -n "$luks_current_Disk_Recovery_Key_passphrase" >/tmp/luks_current_Disk_Recovery_Key_passphrase
warn "Test opening "$LUKS" LUKS encrypted drive content with current Recovery Disk Key passphrase..." warn "Test opening "$LUKS" LUKS encrypted drive content with current Recovery Disk Key passphrase..."
@ -276,7 +317,7 @@ test_luks_current_disk_recovery_key_passphrase()
shred -n 10 -z -u /tmp/luks_current_Disk_Recovery_Key_passphrase 2>/dev/null shred -n 10 -z -u /tmp/luks_current_Disk_Recovery_Key_passphrase 2>/dev/null
#unsetting luks_current_Disk_Recovery_Key_passphrase so we prompt for it again Disk Recovery Key passphrase prompt on next round #unsetting luks_current_Disk_Recovery_Key_passphrase so we prompt for it again Disk Recovery Key passphrase prompt on next round
unset luks_current_Disk_Recovery_Key_passphrase unset luks_current_Disk_Recovery_Key_passphrase
#remove "known good" selected luks container so that next pass asks again user to select luks container. #remove "known good" selected LUKS container so that next pass asks again user to select LUKS container.
#maybe the container was not the right one #maybe the container was not the right one
detect_boot_device detect_boot_device
mount -o remount,rw /boot mount -o remount,rw /boot
@ -302,7 +343,7 @@ while : ; do
#if no external provisioning provides current Disk Recovery Key passphrase #if no external provisioning provides current Disk Recovery Key passphrase
whiptail --title 'Reencrypt LUKS disk encrypted container ?' \ 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 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 --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):" echo -e "\nEnter current Disk Recovery Key passphrase (Configured at OS installation or by OEM):"
read -r luks_current_Disk_Recovery_Key_passphrase read -r luks_current_Disk_Recovery_Key_passphrase
echo -n "$luks_current_Disk_Recovery_Key_passphrase" >/tmp/luks_current_Disk_Recovery_Key_passphrase echo -n "$luks_current_Disk_Recovery_Key_passphrase" >/tmp/luks_current_Disk_Recovery_Key_passphrase
warn "Reencrypting "$LUKS" LUKS encrypted drive content with current Recovery Disk Key passphrase..." warn "Reencrypting "$LUKS" LUKS encrypted drive content with current Recovery Disk Key passphrase..."
@ -319,7 +360,7 @@ while : ; do
shred -n 10 -z -u /tmp/luks_current_Disk_Recovery_Key_passphrase 2>/dev/null shred -n 10 -z -u /tmp/luks_current_Disk_Recovery_Key_passphrase 2>/dev/null
#unsetting luks_current_Disk_Recovery_Key_passphrase so we prompt for it again Disk Recovery Key passphrase prompt on next round #unsetting luks_current_Disk_Recovery_Key_passphrase so we prompt for it again Disk Recovery Key passphrase prompt on next round
unset luks_current_Disk_Recovery_Key_passphrase unset luks_current_Disk_Recovery_Key_passphrase
#remove "known good" selected luks container so that next pass asks again user to select luks container. #remove "known good" selected LUKS container so that next pass asks again user to select LUKS container.
#maybe the container was not the right one #maybe the container was not the right one
detect_boot_device detect_boot_device
mount -o remount,rw /boot mount -o remount,rw /boot
@ -351,7 +392,7 @@ while : ; do
};done };done
fi fi
if [ -z "$luks_current_Disk_Recovery_Key_passphrase" ]; then if [ -z "$luks_current_Disk_Recovery_Key_passphrase" ]; then
echo -e "\nEnter current Disk Recovery Key passphrase (Provisioned at OS installation or by OEM):" echo -e "\nEnter current Disk Recovery Key passphrase (Configured at OS installation or by OEM):"
read -r luks_current_Disk_Recovery_Key_passphrase read -r luks_current_Disk_Recovery_Key_passphrase
fi fi
export luks_current_Disk_Recovery_Key_passphrase export luks_current_Disk_Recovery_Key_passphrase
@ -375,7 +416,7 @@ while : ; do
"The LUKS Disk Recovery Key passphrase was provided to you by the OEM over\n secure communication channel.\n\nIf you previously changed it and do not remember it,\n you will have to reinstall OS from a USB drive.\nTo do so, put OS ISO file and it's signature file on root of USB drive,\n And select Boot from USB\n\nHit Enter to continue." 30 60 "The LUKS Disk Recovery Key passphrase was provided to you by the OEM over\n secure communication channel.\n\nIf you previously changed it and do not remember it,\n you will have to reinstall OS from a USB drive.\nTo do so, put OS ISO file and it's signature file on root of USB drive,\n And select Boot from USB\n\nHit Enter to continue." 30 60
unset luks_current_Disk_Recovery_Key_passphrase unset luks_current_Disk_Recovery_Key_passphrase
unset luks_new_Disk_Recovery_Key_passphrase unset luks_new_Disk_Recovery_Key_passphrase
#remove "known good" selected luks container so that next pass asks again user to select LUKS container. #remove "known good" selected LUKS container so that next pass asks again user to select LUKS container.
#maybe the container was not the right one #maybe the container was not the right one
detect_boot_device detect_boot_device
mount -o remount,rw /boot mount -o remount,rw /boot

View File

@ -60,6 +60,14 @@ if [ "$CONFIG_DEBUG_OUTPUT" = "y" ]; then
#DEBUG and TRACE calls will output to /dev/kmsg, outputting both on dmesg and on console #DEBUG and TRACE calls will output to /dev/kmsg, outputting both on dmesg and on console
dmesg -n 8 || true dmesg -n 8 || true
DEBUG "Debug output enabled from board CONFIG_DEBUG_OUTPUT=y option (/etc/config)" DEBUG "Debug output enabled from board CONFIG_DEBUG_OUTPUT=y option (/etc/config)"
else
# Board config did't have CONFIG_DEBUG_OUTPUT=y defined
# config.user extracted and combined from CBFS had CONFIG_DEBUG_OUTPUT=y
# Output only print messages with a priority of 4 (warnings) or lower (errors and critical) kernel messages to console
# This way, "debug" kernel command line option will have all kernel messages output on console prior of this point
# This is useful to debug boot issues but permits qemu board to boot without flooding console with kernel messages by disabling CONFIG_DEBUG_OUTPUT=y in qemu board config
dmesg -n 4 || true
DEBUG "Debug output enabled from /etc/config.user's CONFIG_DEBUG_OUTPUT=y after combine_configs (Config menu enabled Debug)"
fi fi
TRACE "Under init" TRACE "Under init"
@ -160,6 +168,11 @@ if [ "$boot_option" = "r" ]; then
recovery 'User requested recovery shell' recovery 'User requested recovery shell'
# just in case... # just in case...
exit exit
elif [ "$boot_option" = "o" ]; then
# Launch OEM Factory Reset/Re-Ownership
oem-factory-reset
# just in case...
exit
fi fi
if [ "$CONFIG_BASIC" = "y" ]; then if [ "$CONFIG_BASIC" = "y" ]; then

View File

@ -30,16 +30,16 @@ if [ ! -r /sys/class/tpm/tpm0/pcrs -o ! -x /bin/tpm ]; then
fi fi
if [ -z "$tpm_missing" ]; then if [ -z "$tpm_missing" ]; then
DEBUG "Extending PCR $MODULE_PCR with $MODULE" DEBUG "Extending TPM PCR $MODULE_PCR with $MODULE prior of usage"
tpmr extend -ix "$MODULE_PCR" -if "$MODULE" \ tpmr extend -ix "$MODULE_PCR" -if "$MODULE" \
|| die "$MODULE: tpm extend failed" || die "$MODULE: tpm extend failed"
fi fi
if [ ! -z "$*" -a -z "$tpm_missing" ]; then if [ ! -z "$*" -a -z "$tpm_missing" ]; then
DEBUG "Extending PCR $MODULE_PCR with $*" DEBUG "Extending TPM PCR $MODULE_PCR with $*"
TMPFILE=/tmp/insmod.$$ TMPFILE=/tmp/insmod.$$
echo "$@" > $TMPFILE echo "$@" > $TMPFILE
DEBUG "Extending PCR $MODULE_PCR with $TMPFILE" DEBUG "Extending TPM PCR $MODULE_PCR with $MODULE prior of usage"
tpmr extend -ix "$MODULE_PCR" -if $TMPFILE \ tpmr extend -ix "$MODULE_PCR" -if $TMPFILE \
|| die "$MODULE: tpm extend on arguments failed" || die "$MODULE: tpm extend on arguments failed"
fi fi