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_NO_LUKS_DISK_UNLOCK=y
export CONFIG_TOTP_SKIP_QRCODE=y
export CONFIG_OEMRESET_OFFER_DEFAULTS=y
export CONFIG_BOOTSCRIPT=/bin/gui-init
export CONFIG_BOOT_REQ_HASH=n
export CONFIG_BOOT_REQ_ROLLBACK=n

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,7 +30,6 @@ CONFIG_LINUX_USB=y
export CONFIG_TPM=n
export CONFIG_TPM_NO_LUKS_DISK_UNLOCK=y
export CONFIG_TOTP_SKIP_QRCODE=y
export CONFIG_OEMRESET_OFFER_DEFAULTS=y
export CONFIG_BOOTSCRIPT=/bin/gui-init
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)
5. Get the public key that was saved to the virtual USB flash drive
* `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
* `sudo umount /media/fd_heads_gpg`
* `sudo losetup --detach /dev/loop0`
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 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_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
export CONFIG_DEBUG_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_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
export CONFIG_DEBUG_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_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_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
export CONFIG_DEBUG_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_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
export CONFIG_DEBUG_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_HANGCHECK_TIMER is not set
CONFIG_TCG_TPM=y
CONFIG_HW_RANDOM_TPM=n
# CONFIG_HW_RANDOM_TPM is not set
CONFIG_TCG_TIS_CORE=y
CONFIG_TCG_TIS=y
# CONFIG_TCG_TIS_I2C is not set
@ -2768,7 +2768,8 @@ CONFIG_VFAT_FS=y
CONFIG_FAT_DEFAULT_CODEPAGE=437
CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
# 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_NTFS3_FS is not set
# 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
mount /dev/sda1 /boot && mount -o remount,rw /boot && rm /boot/kexec* && mount -o remount,ro /boot
#Generate keys from GPG smartcard:

View File

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

View File

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

View File

@ -49,6 +49,7 @@ if ! kexec-unseal-key "$INITRD_DIR/secret.key"; then
fi
# 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 ||
die 'Unable to scramble PCR'
@ -92,7 +93,6 @@ if [ "$unseal_failed" = "n" ]; then
done
else
# 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
crypttab_files="etc/crypttab cryptroot/crypttab"
for crypttab_file in $crypttab_files; do

View File

@ -276,8 +276,9 @@ if [ ! -d $paramsdir ]; then
fi
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!"
DEBUG "TPM2 primary key handle hash saved to $PRIMHASH_FILE"
fi
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
lvm vgchange -a y $lvm_volume_group ||
die "Failed to activate the LVM group"
#TODO: why reuse key_devices for lvm devices?
for dev in /dev/$lvm_volume_group/*; do
key_devices="$key_devices $dev"
done

View File

@ -72,20 +72,21 @@ dd \
# Count the number of slots used on each device
for dev in $(cat "$KEY_DEVICES" | cut -d\ -f1); do
DEBUG "Checking number of slots used on $dev"
DEBUG "Checking number of slots used on $dev LUKS header"
#check if the device is a LUKS device with luks[1,2]
slots_used=$(cryptsetup luksDump $dev | grep -c 'luks[0-9]*' || die "Unable to get number of slots used on $dev")
DEBUG "Number of slots used on $dev: $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 [ $slots_used -eq 1 ]; then
# Check if slot 1 is the only one existing
if cryptsetup luksDump $dev | grep -q "Slot 1: ENABLED"; then
warn "Slot 1 is the only one existing on $dev. Heads cannot use it to store TPM sealed LUKS Disk Unlock Key"
die "Slot 1 should not be the only slot existing on $dev. Fix your custom setup"
warn "Slot 1 is the only one existing on $dev LUKS header. Heads cannot use it to store TPM sealed LUKS Disk Unlock Key"
warn "Slot 1 should not be the only slot existing on $dev LUKS header. Slot 0 should be used to store Disk Recovery Key/passphrase"
die "You can safely fix this before continuing through Heads recovery shell: cryptsetup luksAddKey $dev"
fi
else
DEBUG "Slot 1 is not the only existing slot on $dev"
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 "Slot 1 is not the only existing slot on $dev LUKS header."
DEBUG "$dev LUKS header's slot 1 will store LUKS Disk Unlock Key that TPM will seal/unseal with TPM Disk Unlock Key passphrase"
fi
done
@ -136,7 +137,7 @@ tpmr pcrread -a 7 "$pcrf"
DO_WITH_DEBUG --mask-position 7 \
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
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 ||
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" ||
die "Failed to copy LUKS header hashes to /boot - continuing"
mount -o ro,remount $paramsdir || die "Failed to remount $paramsdir in RO - continuing"
warn "Failed to copy LUKS header hashes to /boot - 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" ;;
u) unique="y" ;;
m) force_menu="y" ;;
i) valid_hash="y"; valid_rollback="y" ;;
i)
valid_hash="y"
valid_rollback="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" ;;
esac
done
@ -53,21 +60,22 @@ paramsdir="${paramsdir%%/}"
PRIMHASH_FILE="$paramsdir/kexec_primhdl_hash.txt"
if [ "$CONFIG_TPM2_TOOLS" = "y" ]; then
if [ -r "$PRIMHASH_FILE" ]; then
sha256sum -c "$PRIMHASH_FILE" \
|| {
echo "FATAL: Hash of TPM2 primary key handle mismatch!";
warn "If you have not intentionally regenerated TPM2 primary key,";
warn "your system may have been compromised";
sha256sum -c "$PRIMHASH_FILE" ||
{
echo "FATAL: Hash of TPM2 primary key handle mismatch!"
warn "If you have not intentionally regenerated TPM2 primary key,"
warn "your system may have been compromised"
DEBUG "Hash of TPM2 primary key handle mismatched for $PRIMHASH_FILE"
}
else
warn "Hash of TPM2 primary key handle does not exist"
warn "Please rebuild the boot hash tree"
default_failed="y"
DEBUG "Hash of TPM2 primary key handle does not exist under $PRIMHASH_FILE"
fi
fi
verify_global_hashes()
{
verify_global_hashes() {
echo "+++ Checking verified boot hash file "
# Check the hashes of all the files
if verify_checksums "$bootdir" "$gui_menu"; then
@ -101,25 +109,24 @@ verify_global_hashes()
fi
}
verify_rollback_counter()
{
TPM_COUNTER=`grep counter $TMP_ROLLBACK_FILE | cut -d- -f2`
verify_rollback_counter() {
TPM_COUNTER=$(grep counter $TMP_ROLLBACK_FILE | cut -d- -f2)
if [ -z "$TPM_COUNTER" ]; then
die "$TMP_ROLLBACK_FILE: TPM counter not found?"
fi
read_tpm_counter $TPM_COUNTER \
|| die "Failed to read TPM counter"
read_tpm_counter $TPM_COUNTER ||
die "Failed to read TPM counter"
sha256sum -c $TMP_ROLLBACK_FILE \
|| die "Invalid TPM counter state"
sha256sum -c $TMP_ROLLBACK_FILE ||
die "Invalid TPM counter state"
valid_rollback="y"
}
first_menu="y"
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
die "No boot options"
fi
@ -129,10 +136,9 @@ get_menu_option() {
elif [ "$gui_menu" = "y" ]; then
MENU_OPTIONS=""
n=0
while read option
do
while read option; do
parse_option
n=`expr $n + 1`
n=$(expr $n + 1)
name=$(echo $name | tr " " "_")
MENU_OPTIONS="$MENU_OPTIONS $n ${name} "
done <$TMP_MENU_FILE
@ -146,10 +152,9 @@ get_menu_option() {
else
echo "+++ Select your boot option:"
n=0
while read option
do
while read option; do
parse_option
n=`expr $n + 1`
n=$(expr $n + 1)
echo "$n. $name [$kernel]"
done <$TMP_MENU_FILE
@ -163,7 +168,7 @@ get_menu_option() {
fi
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
}
@ -190,8 +195,8 @@ confirm_menu_option() {
}
parse_option() {
name=`echo $option | cut -d\| -f1`
kernel=`echo $option | cut -d\| -f3`
name=$(echo $option | cut -d\| -f1)
kernel=$(echo $option | cut -d\| -f3)
}
scan_options() {
@ -242,11 +247,11 @@ default_select() {
# Attempt boot with expected parameters
# 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
expectedoption=`cat $TMP_DEFAULT_FILE`
option=`head -n $default_index $TMP_MENU_FILE | tail -1`
expectedoption=$(cat $TMP_DEFAULT_FILE)
option=$(head -n $default_index $TMP_MENU_FILE | tail -1)
if [ "$option" != "$expectedoption" ]; then
if [ "$gui_menu" = "y" ]; then
whiptail $BG_COLOR_ERROR --title 'ERROR: Boot Entry Has Changed' \
@ -282,8 +287,7 @@ user_select() {
# No default expected boot parameters, ask user
option_confirm=""
while [ "$option_confirm" != "y" -a "$option_confirm" != "d" ]
do
while [ "$option_confirm" != "y" -a "$option_confirm" != "d" ]; do
get_menu_option
# In force boot mode, no need offer the option to set a default, just boot
if [[ "$force_boot" = "y" || "$skip_confirm" = "y" ]]; then
@ -305,8 +309,8 @@ user_select() {
echo "+++ Rebooting to start the new default option"
sleep 2
if [ "$CONFIG_DEBUG_OUTPUT" != "y" ]; then
reboot \
|| die "!!! Failed to reboot system"
reboot ||
die "!!! Failed to reboot system"
else
DEBUG "Rebooting is required prior of booting default boot entry"
# Instead of rebooting, drop to a recovery shell
@ -319,8 +323,7 @@ user_select() {
do_boot
}
do_boot()
{
do_boot() {
if [ "$CONFIG_BASIC" != y ] && [ "$CONFIG_BOOT_REQ_ROLLBACK" = "y" ] && [ "$valid_rollback" = "n" ]; then
die "!!! Missing required rollback counter state"
fi
@ -330,21 +333,21 @@ do_boot()
fi
if [ "$CONFIG_BASIC" != y ] && [ "$CONFIG_TPM" = "y" ] && [ -r "$TMP_KEY_DEVICES" ]; then
INITRD=`kexec-boot -b "$bootdir" -e "$option" -i` \
|| die "!!! Failed to extract the initrd from boot option"
INITRD=$(kexec-boot -b "$bootdir" -e "$option" -i) ||
die "!!! Failed to extract the initrd from boot option"
if [ -z "$INITRD" ]; then
die "!!! No initrd file found in boot option"
fi
kexec-insert-key $INITRD \
|| die "!!! Failed to insert disk key into a new initrd"
kexec-insert-key $INITRD ||
die "!!! Failed to insert disk key into a new initrd"
kexec-boot -b "$bootdir" -e "$option" \
-a "$add" -r "$remove" -o "/tmp/secret/initrd.cpio" \
|| die "!!! Failed to boot w/ options: $option"
-a "$add" -r "$remove" -o "/tmp/secret/initrd.cpio" ||
die "!!! Failed to boot w/ options: $option"
else
kexec-boot -b "$bootdir" -e "$option" -a "$add" -r "$remove" \
|| die "!!! Failed to boot w/ options: $option"
kexec-boot -b "$bootdir" -e "$option" -a "$add" -r "$remove" ||
die "!!! Failed to boot w/ options: $option"
fi
}
@ -354,7 +357,7 @@ while true; do
else
check_config $paramsdir
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_HASH_FILE="/tmp/kexec/kexec_hashes.txt"
TMP_TREE_FILE="/tmp/kexec/kexec_tree.txt"
@ -378,8 +381,9 @@ while true; do
if [ "$CONFIG_TPM" = "y" ]; then
if [ ! -r "$TMP_KEY_DEVICES" ]; then
# Extend PCR4 as soon as possible
tpmr extend -ix 4 -ic generic \
|| die "Failed to extend PCR 4"
DEBUG "Extending TPM PCR 4 to prevent further secret unsealing"
tpmr extend -ix 4 -ic generic ||
die "Failed to extend PCR 4"
fi
fi

View File

@ -7,6 +7,9 @@ set -e -o pipefail
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
if grep -q /boot /proc/mounts ; then
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"
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")""
USB_MOUNT_DEVICE="/dev/mapper/"usb_mount_$(basename "$USB_MOUNT_DEVICE")""
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"
done
DEBUG "Hashing luks headers into /tmp/luksDump.txt"
sha256sum /tmp/lukshdr-* >/tmp/luksDump.txt || die "Unable to hash luks headers"
DEBUG "Hashing LUKS headers into /tmp/luksDump.txt"
sha256sum /tmp/lukshdr-* >/tmp/luksDump.txt || die "Unable to hash LUKS headers"
DEBUG "Removing /tmp/lukshdr-*"
rm /tmp/lukshdr-*
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 ||
die "Unable to extend PCR"

View File

@ -3,13 +3,22 @@
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
if [ "$CONFIG_TPM" = "y" ]; then
tpmr shutdown
fi
# 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
fi

View File

@ -1,5 +1,5 @@
#!/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

View File

@ -25,10 +25,10 @@ dd \
of="$TOTP_SECRET" \
count=1 \
bs=20 \
2>/dev/null \
|| die "Unable to generate 20 random bytes"
2>/dev/null ||
die "Unable to generate 20 random bytes"
secret="`base32 < $TOTP_SECRET`"
secret="$(base32 <$TOTP_SECRET)"
pcrf="/tmp/secret/pcrf.bin"
DEBUG "Sealing TOTP with actual state of PCR0-3"
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"
# 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)"
# 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)"
# pcr 7 is containing measurements of user injected stuff in cbfs
tpmr pcrread -a 7 "$pcrf"
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"
#Make sure we clear the TPM Owner Password from memory in case it failed to be used to seal TOTP
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
url="otpauth://totp/$HOST?secret=$secret"

View File

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

View File

@ -5,9 +5,9 @@
SECRET_DIR="/tmp/secret"
PRIMARY_HANDLE="0x81000000"
ENC_SESSION_FILE="enc.ctx"
DEC_SESSION_FILE="dec.ctx"
PRIMARY_HANDLE_FILE="primary.handle"
ENC_SESSION_FILE="$SECRET_DIR/enc.ctx"
DEC_SESSION_FILE="$SECRET_DIR/dec.ctx"
PRIMARY_HANDLE_FILE="$SECRET_DIR/primary.handle"
# 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
@ -195,14 +195,14 @@ $0 ~ pcr {
replay_pcr() {
TRACE "Under /bin/tpmr:replay_pcr"
if [ -z "$2" ]; then
>&2 echo "No PCR number passed"
echo >&2 "No PCR number passed"
return
fi
if [ "$2" -ge 8 ]; then
>&2 echo "Illegal PCR number ($2)"
echo >&2 "Illegal PCR number ($2)"
return
fi
local log=`cbmem -L`
local log=$(cbmem -L)
local alg="$1"
local pcr="$2"
local alg_digits=0
@ -225,7 +225,7 @@ replay_pcr() {
# PCR-5, depending on which modules are loaded for given board:
# tpmr calcfuturepcr 5 module0.ko module1.ko module2.ko | xxd -p
# 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() {
@ -234,15 +234,19 @@ tpm2_extend() {
case "$1" in
-ix)
index="$2"
shift 2;;
shift 2
;;
-ic)
hash="$(echo -n "$2" | sha256sum | cut -d' ' -f1)"
shift 2;;
shift 2
;;
-if)
hash="$(sha256sum "$2" | cut -d' ' -f1)"
shift 2;;
shift 2
;;
*)
break;;
break
;;
esac
done
tpm2 pcrextend "$index:sha256=$hash"
@ -255,12 +259,14 @@ tpm2_counter_read() {
case "$1" in
-ix)
index="$2"
shift 2;;
shift 2
;;
*)
break;;
break
;;
esac
done
echo "$index: `tpm2 nvread 0x$index | xxd -pc8`"
echo "$index: $(tpm2 nvread 0x$index | xxd -pc8)"
}
tpm2_counter_inc() {
@ -269,41 +275,60 @@ tpm2_counter_inc() {
case "$1" in
-ix)
index="$2"
shift 2;;
shift 2
;;
-pwdc)
pwd="$2"
shift 2;;
shift 2
;;
*)
break;;
break
;;
esac
done
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() {
TRACE "Under /bin/tpmr:tpm2_counter_cre"
tpm1_counter_create() {
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
case "$1" in
-pwdo)
pwdo="$2"
shift 2;;
-pwdof)
pwdo="file:$2"
shift 2;;
-pwdc)
pwd="$2"
shift 2;;
shift 2
;;
-la)
label="$2"
shift 2;;
shift 2
;;
*)
break;;
break
;;
esac
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" \
-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)"
}
@ -311,20 +336,20 @@ tpm2_startsession() {
TRACE "Under /bin/tpmr:tpm2_startsession"
mkdir -p "$SECRET_DIR"
tpm2 flushcontext -Q \
--transient-object \
|| die "tpm2_flushcontext: unable to flush transient handles"
--transient-object ||
die "tpm2_flushcontext: unable to flush transient handles"
tpm2 flushcontext -Q \
--loaded-session \
|| die "tpm2_flushcontext: unable to flush sessions"
--loaded-session ||
die "tpm2_flushcontext: unable to flush sessions"
tpm2 flushcontext -Q \
--saved-session \
|| die "tpm2_flushcontext: unable to flush saved session"
tpm2 readpublic -Q -c "$PRIMARY_HANDLE" -t "/tmp/$PRIMARY_HANDLE_FILE"
tpm2 startauthsession -Q -c "/tmp/$PRIMARY_HANDLE_FILE" --hmac-session -S "/tmp/$ENC_SESSION_FILE"
tpm2 startauthsession -Q -c "/tmp/$PRIMARY_HANDLE_FILE" --hmac-session -S "/tmp/$DEC_SESSION_FILE"
tpm2 sessionconfig -Q --disable-encrypt "/tmp/$DEC_SESSION_FILE"
--saved-session ||
die "tpm2_flushcontext: unable to flush saved session"
tpm2 readpublic -Q -c "$PRIMARY_HANDLE" -t "$PRIMARY_HANDLE_FILE"
tpm2 startauthsession -Q -c "$PRIMARY_HANDLE_FILE" --hmac-session -S "$ENC_SESSION_FILE"
tpm2 startauthsession -Q -c "$PRIMARY_HANDLE_FILE" --hmac-session -S "$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
@ -361,8 +386,8 @@ tpm2_destroy() {
handle="$(printf "0x81%6s" "$index" | tr ' ' 0)"
# remove possible data occupying this handle
tpm2 evictcontrol -Q -C p -c "$handle" 2>/dev/null \
|| die "Unable to evict secret"
tpm2 evictcontrol -Q -C p -c "$handle" 2>/dev/null ||
die "Unable to evict secret from TPM NVRAM"
}
# 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
dd if=/dev/zero bs="$size" count=1 of=/tmp/wipe-totp-zero
tpm nv_writevalue -in "$index" -if /tmp/wipe-totp-zero \
|| die "Unable to wipe sealed secret"
tpm nv_writevalue -in "$index" -if /tmp/wipe-totp-zero ||
die "Unable to wipe sealed secret from TPM NVRAM"
}
# 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
pass="$6" # May be empty to seal with no password
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"
bname="`basename $file`"
bname="$(basename $file)"
# Pad with up to 6 zeros, i.e. '0x81000001', '0x81001234', etc.
handle="$(printf "0x81%6s" "$index" | tr ' ' 0)"
@ -403,8 +428,8 @@ tpm2_seal() {
# Create a policy requiring both PCRs and the object's authentication
# value using a trial session.
TRIAL_SESSION=/tmp/sealfile_trial.session
AUTH_POLICY=/tmp/sealfile_auth.policy
TRIAL_SESSION="$SECRET_DIR/sealfile_trial.session"
AUTH_POLICY="$SECRET_DIR/sealfile_auth.policy"
rm -f "$TRIAL_SESSION" "$AUTH_POLICY"
tpm2 startauthsession -g sha256 -S "$TRIAL_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
# this case the policy includes the password, and we don't want to allow
# the password on its own.)
tpm2 create -Q -C "/tmp/$PRIMARY_HANDLE_FILE" \
tpm2 create -Q -C "$PRIMARY_HANDLE_FILE" \
-i "$file" \
-u "$SECRET_DIR/$bname.priv" \
-r "$SECRET_DIR/$bname.pub" \
-L "$AUTH_POLICY" \
-S "/tmp/$DEC_SESSION_FILE" \
-S "$DEC_SESSION_FILE" \
-a "fixedtpm|fixedparent|adminwithpolicy" \
"${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" \
-c "$SECRET_DIR/$bname.seal.ctx"
prompt_tpm_password
prompt_tpm_owner_password
# 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
DO_WITH_DEBUG --mask-position 6 \
tpm2 evictcontrol -Q -C o -P "$(tpm2_password_hex "$tpm_password")" \
-c "$SECRET_DIR/$bname.seal.ctx" "$handle"
tpm2 evictcontrol -Q -C o -P "$(tpm2_password_hex "$tpm_owner_password")" \
-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() {
TRACE "Under /bin/tpmr:tpm1_seal"
@ -487,25 +517,28 @@ tpm1_seal() {
-hk 40000000 \
"${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
# 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.
#
# The permissions are 0 since there is nothing special
# about the sealed file
tpm physicalpresence -s \
|| warn "Unable to assert physical presence"
tpm physicalpresence -s ||
warn "Unable to assert physical presence"
prompt_tpm_password
prompt_tpm_owner_password
tpm nv_definespace -in "$index" -sz "$sealed_size" \
-pwdo "$tpm_password" -per 0 \
|| warn "Unable to define NVRAM space; trying anyway"
-pwdo "$tpm_password" -per 0 ||
warn "Unable to define TPM NVRAM space; trying anyway"
tpm nv_writevalue -in "$index" -if "$sealed_file" \
|| die "Unable to write sealed secret to NVRAM"
tpm nv_writevalue -in "$index" -if "$sealed_file" ||
{
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
}
@ -531,12 +564,13 @@ tpm2_unseal() {
# 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
# 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"
warn "No TPM primary handle. You must reset TPM to seal secret to TPM NVRAM"
exit 1
fi
POLICY_SESSION=/tmp/unsealfile_policy.session
POLICY_SESSION="$SECRET_DIR/unsealfile_policy.session"
rm -f "$POLICY_SESSION"
tpm2 startauthsession -Q -g sha256 -S "$POLICY_SESSION" --policy-session
at_exit cleanup_session "$POLICY_SESSION"
@ -554,7 +588,7 @@ tpm2_unseal() {
fi
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() {
TRACE "Under /bin/tpmr:tpm1_unseal"
@ -576,8 +610,8 @@ tpm1_unseal() {
tpm nv_readvalue \
-in "$index" \
-sz "$sealed_size" \
-of "$sealed_file" \
|| die "Unable to read sealed file from TPM NVRAM"
-of "$sealed_file" ||
die "Unable to read sealed file from TPM NVRAM"
PASS_ARGS=()
if [ "$pass" ]; then
@ -593,15 +627,18 @@ tpm1_unseal() {
tpm2_reset() {
TRACE "Under /bin/tpmr:tpm2_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"
tpm2 clear -c platform || warn "Unable to clear TPM on platform hierarchy"
tpm2 changeauth -c owner "$(tpm2_password_hex "$key_password")"
tpm2 changeauth -c endorsement "$(tpm2_password_hex "$key_password")"
tpm2 changeauth -c owner "$(tpm2_password_hex "$tpm_owner_password")"
tpm2 changeauth -c endorsement "$(tpm2_password_hex "$tpm_owner_password")"
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" \
-P "$(tpm2_password_hex "$key_password")"
-P "$(tpm2_password_hex "$tpm_owner_password")"
shred -u "$SECRET_DIR/primary.ctx"
tpm2_startsession
@ -610,7 +647,7 @@ tpm2_reset() {
# * --max-tries=10: Allow 10 failures before lockout. This allows the
# user to quickly "burst" 10 failures without significantly impacting
# 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
# failure if firmware is updated.
# Remember that an auth failure is also counted any time an unclean
@ -626,7 +663,7 @@ tpm2_reset() {
--max-tries=10 \
--recovery-time=3600 \
--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
# with a password. Heads doesn't offer dictionary attach reset, instead
@ -639,15 +676,18 @@ tpm2_reset() {
}
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
tpm physicalpresence -s
tpm physicalenable
tpm physicalsetdeactivated -c
tpm forceclear
tpm physicalenable
tpm takeown -pwdo "$key_password"
tpm takeown -pwdo "$tpm_owner_password"
# And now turn it all back on
tpm physicalpresence -s
@ -660,20 +700,20 @@ tpm2_kexec_finalize() {
TRACE "Under /bin/tpmr:tpm2_kexec_finalize"
# Flush sessions and transient objects
tpm2 flushcontext -Q --transient-object \
|| warn "tpm2_flushcontext: unable to flush transient handles"
tpm2 flushcontext -Q --loaded-session \
|| warn "tpm2_flushcontext: unable to flush sessions"
tpm2 flushcontext -Q --saved-session \
|| warn "tpm2_flushcontext: unable to flush saved session"
tpm2 flushcontext -Q --transient-object ||
warn "tpm2_flushcontext: unable to flush transient handles"
tpm2 flushcontext -Q --loaded-session ||
warn "tpm2_flushcontext: unable to flush sessions"
tpm2 flushcontext -Q --saved-session ||
warn "tpm2_flushcontext: unable to flush saved session"
# Add a random passphrase to platform hierarchy to prevent TPM2 from
# being cleared in the OS.
# This passphrase is only effective before the next boot.
echo "Locking TPM2 platform hierarchy..."
randpass=$(dd if=/dev/urandom bs=4 count=1 status=none | xxd -p)
tpm2 changeauth -c platform "$randpass" \
|| warn "Failed to lock platform hierarchy of TPM2"
tpm2 changeauth -c platform "$randpass" ||
warn "Failed to lock platform hierarchy of TPM2"
}
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.
case "$subcmd" in
pcrread)
shift; tpm1_pcrread "$@";;
shift
tpm1_pcrread "$@"
;;
pcrsize)
echo "$PCR_SIZE";;
echo "$PCR_SIZE"
;;
calcfuturepcr)
shift; replay_pcr "sha1" "$@";;
shift
replay_pcr "sha1" "$@"
;;
counter_create)
shift
tpm1_counter_create "$@"
;;
destroy)
shift; tpm1_destroy "$@";;
shift
tpm1_destroy "$@"
;;
seal)
shift; tpm1_seal "$@";;
startsession)
;; # Nothing on TPM1.
shift
tpm1_seal "$@"
;;
startsession) ;; # Nothing on TPM1.
unseal)
shift; tpm1_unseal "$@";;
shift
tpm1_unseal "$@"
;;
reset)
shift; tpm1_reset "$@";;
kexec_finalize)
;; # Nothing on TPM1.
shutdown)
;; # Nothing on TPM1.
shift
tpm1_reset "$@"
;;
kexec_finalize) ;; # Nothing on TPM1.
shutdown) ;; # Nothing on TPM1.
*)
DEBUG "Direct translation from tpmr to tpm1 call"
DO_WITH_DEBUG exec tpm "$@"
@ -731,34 +785,49 @@ subcmd="$1"
shift 1
case "$subcmd" in
pcrread)
tpm2_pcrread "$@";;
tpm2_pcrread "$@"
;;
pcrsize)
echo "$PCR_SIZE";;
echo "$PCR_SIZE"
;;
calcfuturepcr)
replay_pcr "sha256" "$@";;
replay_pcr "sha256" "$@"
;;
extend)
tpm2_extend "$@";;
tpm2_extend "$@"
;;
counter_read)
tpm2_counter_read "$@";;
tpm2_counter_read "$@"
;;
counter_increment)
tpm2_counter_inc "$@";;
tpm2_counter_inc "$@"
;;
counter_create)
tpm2_counter_cre "$@";;
tpm2_counter_create "$@"
;;
destroy)
tpm2_destroy "$@";;
tpm2_destroy "$@"
;;
seal)
tpm2_seal "$@";;
tpm2_seal "$@"
;;
startsession)
tpm2_startsession "$@";;
tpm2_startsession "$@"
;;
unseal)
tpm2_unseal "$@";;
tpm2_unseal "$@"
;;
reset)
tpm2_reset "$@";;
tpm2_reset "$@"
;;
kexec_finalize)
tpm2_kexec_finalize "$@";;
tpm2_kexec_finalize "$@"
;;
shutdown)
tpm2_shutdown "$@";;
tpm2_shutdown "$@"
;;
*)
echo "Command $subcmd not wrapped!"
exit 1
;;
esac

View File

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

View File

@ -53,6 +53,163 @@ preserve_rom() {
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() {
TRACE "Under /etc/ash_functions:recovery"
echo >&2 "!!!!! $*"
@ -70,6 +227,7 @@ recovery() {
. /tmp/config
if [ "$CONFIG_TPM" = "y" ]; then
DEBUG "Extending TPM PCR 4 for recovery shell access"
tpmr extend -ix 4 -ic recovery
fi
@ -80,6 +238,9 @@ recovery() {
fi
while [ true ]
do
#Going to recovery shell should be authenticated if supported
gpg_auth
echo >&2 "!!!!! Starting recovery shell"
sleep 1
@ -102,6 +263,57 @@ combine_configs() {
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()
{
TRACE "Under /etc/ash_functions:enable_usb"

View File

@ -96,16 +96,15 @@ reseal_tpm_disk_decryption_key() {
fi
if [ -s /boot/kexec_key_devices.txt ] || [ -s /boot/kexec_key_lvm.txt ]; then
warn "A TPM Disk Unlock Key previously sealed is now invalid since firmware measurements could not unseal TOTP"
echo "Renewing LUKS Disk Unlock Key to be unsealed by TPM Disk Unlock Key passphrase"
warn "TPM sealed Disk Unlock Key secret needs to be resealed alongside TOTP/HOTP secret"
echo "Resealing TPM LUKS Disk Unlock Key to be unsealed by TPM Disk Unlock Key passphrase"
while ! kexec-seal-key /boot; do
warn "Recovery Disk Encryption key passphrase invalid. Try again!"
warn "Recovery Disk Encryption key passphrase/TPM Owner Password may be invalid. Please try again"
done
warn "LUKS header hash changed under /boot/kexec_luks_hdr_hash.txt"
echo "Updating checksums and signing all files under /boot/kexec.sig"
while ! update_checksums; do
warn "Checksums were not signed. Bad GPG PIN provided?"
warn "Please update checksums and provide a valid GPG PIN"
warn "Checksums were not signed. Preceding errors should explain possible causes"
done
warn "Rebooting in 3 seconds to enable booting default boot option"
sleep 3
@ -119,6 +118,7 @@ reseal_tpm_disk_decryption_key() {
# be detected. If USB storage was already enabled, no wait occurs, this would
# have happened already when USB storage was enabled.
enable_usb_storage() {
TRACE "Under /etc/functions:enable_usb_storage"
if ! lsmod | grep -q usb_storage; then
timeout=0
echo "Scanning for USB storage devices..."
@ -189,83 +189,59 @@ list_usb_storage() {
done
}
confirm_gpg_card() {
TRACE "Under /etc/functions:confirm_gpg_card"
read \
-n 1 \
-p "Please confirm that your GPG card is inserted [Y/n]: " \
card_confirm
echo
# Prompt for a TPM Owner Password if it is not already cached in /tmp/secret/tpm_owner_password.
# Sets tpm_owner_password variable reused in flow, and cache file used until recovery shell is accessed.
# 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_owner_password() {
TRACE "Under /etc/functions:prompt_tpm_owner_password"
if [ "$card_confirm" != "y" \
-a "$card_confirm" != "Y" \
-a -n "$card_confirm" ] \
; 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
if [ -s /tmp/secret/tpm_owner_password ]; then
DEBUG "/tmp/secret/tpm_owner_password already cached in file. Reusing"
tpm_owner_password=$(cat /tmp/secret/tpm_owner_password)
return 0
fi
read -s -p "TPM Owner password: " tpm_password
read -s -p "TPM Owner Password: " tpm_owner_password
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
# key_password. The password must be 1-32 characters and must be entered twice,
# Prompt for a new TPM Owner Password when resetting the TPM.
# 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.
prompt_new_owner_password() {
local key_password2
key_password=1
key_password2=2
while [ "$key_password" != "$key_password2" ] || [ "${#key_password}" -gt 32 ] || [ -z "$key_password" ]; do
read -s -p "New TPM owner passphrase (2 words suggested, 1-32 characters max): " key_password
TRACE "Under /etc/functions:prompt_new_owner_password"
local tpm_owner_password2
tpm_owner_password=1
tpm_owner_password2=2
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
read -s -p "Repeat chosen TPM owner passphrase: " key_password2
read -s -p "Repeat chosen TPM Owner Password: " tpm_owner_password2
echo
if [ "$key_password" != "$key_password2" ]; then
if [ "$tpm_owner_password" != "$tpm_owner_password2" ]; then
echo "Passphrases entered do not match. Try again!"
echo
fi
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() {
TRACE "Under /etc/functions:check_tpm_counter"
LABEL=${2:-3135106223}
tpm_password="$3"
# if the /boot.hashes file already exists, read the TPM counter ID
@ -274,9 +250,7 @@ check_tpm_counter() {
TPM_COUNTER=$(grep counter- "$1" | cut -d- -f2)
else
warn "$1 does not exist; creating new TPM counter"
prompt_tpm_password
tpmr counter_create \
-pwdo "$tpm_password" \
-pwdc '' \
-la $LABEL |
tee /tmp/counter ||
@ -299,7 +273,7 @@ increment_tpm_counter() {
TRACE "Under /etc/functions:increment_tpm_counter"
tpmr counter_increment -ix "$1" -pwdc '' |
tee /tmp/counter-$1 ||
die "Counter increment failed"
die "TPM counter increment failed for rollback prevention. Please reset the TPM"
}
check_config() {
@ -360,40 +334,6 @@ replace_config() {
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
# sha256sum of the ROM (binary, not printable), which can be truncated to the
# supported secret length.
@ -608,6 +548,7 @@ detect_boot_device() {
}
scan_boot_options() {
TRACE "Under /etc/functions:scan_boot_options"
local bootdir config option_file
bootdir="$1"
config="$2"

View File

@ -34,54 +34,100 @@ mount_usb()
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()
{
TRACE "under gui_functions:file_selector"
local FILE_LIST MENU_MSG MENU_TITLE CHOICE_ARGS SHOW_SIZE OPTION_SIZE option_index
FILE=""
if [ "$1" = "--show-size" ]; then
SHOW_SIZE=y
shift
fi
FILE_LIST=$1
MENU_MSG=${2:-"Choose the file"}
MENU_TITLE=${3:-"Select your File"}
# create file menu options
if [ `cat "$FILE_LIST" | wc -l` -gt 0 ]; then
option=""
while [ -z "$option" ]
do
MENU_OPTIONS=""
CHOICE_ARGS=()
n=0
while read option
do
n=`expr $n + 1`
option=$(echo $option | tr " " "_")
MENU_OPTIONS="$MENU_OPTIONS $n ${option}"
done < $FILE_LIST
while read option; do
n="$((++n))"
MENU_OPTIONS="$MENU_OPTIONS a Abort"
whiptail --title "${MENU_TITLE}" \
--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
if [ "$SHOW_SIZE" = "y" ] && OPTION_SIZE="$(display_block_device_size "$option")"; then
option="$option - $OPTION_SIZE"
fi
CHOICE_ARGS+=("$n" "$option")
done < "$FILE_LIST"
option=`head -n $option_index $FILE_LIST | tail -1`
if [ "$option" == "a" ]; then
return
fi
done
if [ -n "$option" ]; then
FILE=$option
fi
else
if [ "${#CHOICE_ARGS[@]}" -eq 0 ]; then
whiptail $BG_COLOR_ERROR --title 'ERROR: No Files Found' \
--msgbox "No Files found matching the pattern. Aborting." 0 80
exit 1
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()

View File

@ -6,25 +6,19 @@
. /tmp/config
#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
lvm vgscan || true
blkid | cut -d ':' -f 1 | while read device
do cryptsetup isLuks $device
if [ $? -eq 0 ]; then
echo "$device"
fi
blkid | cut -d ':' -f 1 | while read device; do
if cryptsetup isLuks $device; then echo $device; fi
done | sort
}
#Whiptail prompt asking user to select ratio of device to use for LUKS container between: 10, 25, 50, 75
select_luks_container_size_percent()
{
#Whiptail prompt asking user to select ratio of device to use for LUKS container between: 25, 50, 75
select_luks_container_size_percent() {
TRACE "Under /etc/luks-functions:select_luks_container_size_percent()"
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 --title "Select LUKS container size percentage of device" --menu \
"Select LUKS container size percentage of device:" 0 80 10 \
@ -57,11 +51,12 @@ select_luks_container_size_percent()
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
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
#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
@ -84,12 +79,13 @@ prepare_thumb_drive()
PERCENTAGE=$2
shift 2
;;
--passphrase)
--pass)
PASSPHRASE=$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
done
@ -177,32 +173,77 @@ prepare_thumb_drive()
PERCENTAGE=$(cat /tmp/luks_container_size_percent)
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_DISPLAY="$(display_size "$DISK_SIZE_BYTES")"
#Convert disk size to MB
DISK_SIZE_MB=$((DISK_SIZE_BYTES/1024/1024))
#Get size in bytes from percentage and apply percentage to DISK_SIZE_MB
PERCENTAGE_MB="$((DISK_SIZE_MB*PERCENTAGE/100))"
#Calculate percentage of device in MB
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
whiptail $BG_COLOR_WARNING --title "WARNING: Wiping and repartitioning $DEVICE of $DISK_SIZE_MB MB" --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 \
|| die "User cancelled wiping and repartitioning of $DEVICE"
whiptail $BG_COLOR_WARNING --title "WARNING: Wiping and repartitioning $DEVICE ($DISK_SIZE_DISPLAY)" --yesno \
"$MSG" 0 80
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
#transform response to uppercase with bash parameter expansion
response=${response^^}
#continue if response different then uppercase N
if [[ $response =~ ^(N)$ ]]; then
die "User cancelled wiping and repartitioning of $DEVICE"
#continue if response is Y, y, or empty, abort for anything else
if [ -n "$response" ] && [ "${response^^}" != Y ]; then
return 1
fi
fi
}
echo -e "Preparing $DEVICE with $PERCENTAGE_MB MB for private LUKS container and rest of disk with exfat\
\n for public partition (This may take a while)..." | fold -s
# Prepare a flash drive with a private LUKS-encrypted ext4 partition and a
# 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"
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"
@ -246,7 +287,7 @@ select_luks_container()
mount -o remount,ro /boot
fi
else
warn "No encrypted device found."
warn "No encrypted device found"
return 1
fi
fi
@ -259,7 +300,7 @@ test_luks_current_disk_recovery_key_passphrase()
select_luks_container || return 1
if [ -z "$luks_current_Disk_Recovery_Key_passphrase" ]; then
#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
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..."
@ -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
#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
#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
detect_boot_device
mount -o remount,rw /boot
@ -302,7 +343,7 @@ while : ; do
#if no external provisioning provides current Disk Recovery Key passphrase
whiptail --title 'Reencrypt LUKS disk encrypted container ?' \
--msgbox "This will replace the encrypted container content and its Disk Recovery Key.\n\nThe passphrase associated with this key will be asked from the user in the\nfollowing conditions:\n 1-Every boot if no Disk unlock key was added to the TPM\n 2-If the TPM fails (Hardware failure)\n 3-If the firmware has been tampered with/upgraded/modified by the user\n\nThis process requires you to type the current Disk Recovery Key passphrase\nand will delete TPM Disk unlock key slot if 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
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..."
@ -319,7 +360,7 @@ while : ; do
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
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
detect_boot_device
mount -o remount,rw /boot
@ -351,7 +392,7 @@ while : ; do
};done
fi
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
fi
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
unset luks_current_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
detect_boot_device
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
dmesg -n 8 || true
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
TRACE "Under init"
@ -160,6 +168,11 @@ if [ "$boot_option" = "r" ]; then
recovery 'User requested recovery shell'
# just in case...
exit
elif [ "$boot_option" = "o" ]; then
# Launch OEM Factory Reset/Re-Ownership
oem-factory-reset
# just in case...
exit
fi
if [ "$CONFIG_BASIC" = "y" ]; then

View File

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