Implement Restricted Boot Mode

Restricted Boot mode only allows booting from signed files, whether that
is signed kernels in /boot or signed ISOs on mounted USB disks. This
disables booting from abitrary USB disks as well as the forced "unsafe"
boot mode. This also disables the recovery console so you can't bypass
this mode simply by running kexec manually.

Signed-off-by: Jonathon Hall <jonathon.hall@puri.sm>
This commit is contained in:
Kyle Rankin 2022-10-25 15:09:15 -07:00 committed by Jonathon Hall
parent 4bc6159ab6
commit 79da79a5e4
No known key found for this signature in database
GPG Key ID: 1E9C3CA91AE25114
7 changed files with 125 additions and 1 deletions

View File

@ -29,6 +29,8 @@ while true; do
else
# check current PureBoot Mode
BASIC_MODE="$(load_config_value CONFIG_PUREBOOT_BASIC)"
# check current Restricted Boot Mode
RESTRICTED_BOOT="$(load_config_value CONFIG_RESTRICTED_BOOT)"
BASIC_NO_AUTOMATIC_DEFAULT="$(load_config_value CONFIG_BASIC_NO_AUTOMATIC_DEFAULT)"
dynamic_config_options=()
@ -45,6 +47,7 @@ while true; do
'D' ' Change the root directories to hash' \
'B' ' Check root hashes at boot' \
'P' " $(get_config_display_action "$BASIC_MODE") PureBoot Basic Mode" \
'L' " $(get_config_display_action "$RESTRICTED_BOOT") Restricted Boot" \
)
fi
@ -247,7 +250,10 @@ while true; do
fi
;;
"P" )
if [ "$BASIC_MODE" = "n" ]; then
if ! [ "$RESTRICTED_BOOT" = n ]; then
whiptail $BG_COLOR_ERROR --title 'Restricted Boot Active' \
--msgbox "Disable Restricted Boot to enable Basic Mode." 0 80
elif [ "$BASIC_MODE" = "n" ]; then
if (whiptail --title 'Enable PureBoot Basic Mode?' \
--yesno "This will remove all signature checking on the firmware
\nand boot files, and disable use of the Librem Key.
@ -274,6 +280,61 @@ while true; do
fi
fi
;;
"L" )
if [ "$RESTRICTED_BOOT" = "n" ]; then
if (whiptail --title 'Enable Restricted Boot Mode?' \
--yesno "This will disable booting from any unsigned files,
\nincluding kernels that have not yet been signed,
\n.isos without signatures, raw USB disks,
\nand will disable failsafe boot mode.
\n\nThis will also disable the recovery console.
\n\nDo you want to proceed?" 0 80) then
set_config /etc/config.user "CONFIG_RESTRICTED_BOOT" "y"
combine_configs
whiptail --title 'Config change successful' \
--msgbox "Restricted Boot mode enabled;\nsave the config change and reboot for it to go into effect." 0 80
fi
else
if (whiptail --title 'Disable Restricted Boot Mode?' \
--yesno "This will allow booting from unsigned devices,
\nand will re-enable failsafe boot mode.
\n\nThis will also RESET the TPM and re-enable the recovery console.
\n\nProceeding will automatically update the boot firmware, reset TPM and reboot!
\n\nDo you want to proceed?" 0 80) then
# Wipe the TPM TOTP/HOTP secret before flashing. Otherwise, enabling
# Restricted Boot again might restore the firmware to an identical
# state, and there would be no evidence that it had been temporarily
# disabled.
if ! wipe-totp >/dev/null 2>/tmp/error; then
ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail $BG_COLOR_ERROR --title 'ERROR: erasing TOTP secret' \
--msgbox "Erasing TOTP Secret Failed\n\n${ERROR}" 0 80
exit 1
fi
# We can't allow Restricted Boot to be disabled without flashing the
# firmware - this would allow the use of unrestricted mode without
# leaving evidence in the firmware. Disable it by flashing the new
# config directly.
FLASH_USER_CONFIG=/tmp/config-gui-config-user
cp /etc/config.user "$FLASH_USER_CONFIG"
set_config "$FLASH_USER_CONFIG" "CONFIG_RESTRICTED_BOOT" "n"
read_rom /tmp/config-gui.rom
replace_rom_file /tmp/config-gui.rom "heads/initrd/etc/config.user" "$FLASH_USER_CONFIG"
/bin/flash.sh /tmp/config-gui.rom
whiptail --title 'BIOS Updated Successfully' \
--msgbox "BIOS updated successfully.\n\nIf your keys have changed, be sure to re-sign all files in /boot\nafter you reboot.\n\nPress Enter to reboot" 0 80
/bin/reboot
fi
fi
;;
"A" )
if [ "$BASIC_NO_AUTOMATIC_DEFAULT" = "n" ]; then
if (whiptail --title 'Disable automatic default boot?' \

View File

@ -7,6 +7,12 @@ set -e -o pipefail
TRACE "Under /bin/flash-gui.sh"
if [ "$CONFIG_RESTRICTED_BOOT" = y ]; then
whiptail $BG_COLOR_ERROR --title 'Restricted Boot Active' \
--msgbox "Disable Restricted Boot to flash new firmware." 16 60
exit 1
fi
while true; do
unset menu_choice
whiptail $BG_COLOR_MAIN_MENU --title "Firmware Management Menu" \

View File

@ -603,6 +603,10 @@ attempt_default_boot()
force_unsafe_boot()
{
TRACE "Under /bin/gui-init:force_unsafe_boot"
if [ "$CONFIG_RESTRICTED_BOOT" = y ]; then
whiptail $BG_COLOR_ERROR --title 'ERROR: Restricted Boot Enabled' --msgbox "Restricted Boot is Enabled, forced boot not allowed.\n\nPress OK to return to the Main Menu" 0 80
return
fi
# Run the menu selection in "force" mode, bypassing hash checks
if (whiptail $BG_COLOR_WARNING --title 'Unsafe Forced Boot Selected!' \
--yesno "WARNING: You have chosen to skip all tamper checks and boot anyway.\n\nThis is an unsafe option!\n\nDo you want to proceed?" 0 80) then

View File

@ -92,6 +92,10 @@ if [ `cat /tmp/iso_menu.txt | wc -l` -gt 0 ]; then
fi
fi
if [ "$CONFIG_RESTRICTED_BOOT" = y ]; then
die "ISO boot failed in Restricted Boot mode."
fi
echo "!!! Could not find any ISO, trying bootable USB"
# Attempt to pull verified config from device
if [ -x /bin/whiptail ]; then

View File

@ -250,6 +250,32 @@ cleanup_shred() {
shred -n 10 -z -u "$1" 2>/dev/null || true
}
# tpm2_destroy: Destroy a sealed file in the TPM. The mechanism differs by
# TPM version - TPM2 evicts the file object, so it no longer exists.
tpm2_destroy() {
index="$1" # Index of the sealed file
size="$2" # Size of zeroes to overwrite for TPM1 (unused in TPM2)
# Pad with up to 6 zeros, i.e. '0x81000001', '0x81001234', etc.
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"
}
# tpm1_destroy: Destroy a sealed file in the TPM. The mechanism differs by
# TPM version - TPM1 overwrites the file with zeroes, since this can be done
# without authorization. (Deletion requires authorization.)
tpm1_destroy() {
index="$1" # Index of the sealed file
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"
}
# tpm2_seal: Seal a file against PCR values and, optionally, a password.
# If a password is given, both the PCRs and password are required to unseal the
# file. PCRs are provided as a PCR list and data file. PCR data must be
@ -576,6 +602,8 @@ if [ "$CONFIG_TPM2_TOOLS" != "y" ]; then
echo "$PCR_SIZE";;
calcfuturepcr)
shift; tpm1_calcfuturepcr "$@";;
destroy)
shift; tpm1_destroy "$@";;
seal)
shift; tpm1_seal "$@";;
startsession)
@ -615,6 +643,8 @@ case "$subcmd" in
tpm2_counter_inc "$@";;
counter_create)
tpm2_counter_cre "$@";;
destroy)
tpm2_destroy "$@";;
seal)
tpm2_seal "$@";;
startsession)

14
initrd/bin/wipe-totp Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash
# Wipe the sealed TOTP/HOTP secret. The secret is overwritten with all-0,
# rather than deleted, because deletion requires authorization. Wiping the
# secret will cause the next boot to prompt to regenerate the secret.
. /etc/functions
TPM_NVRAM_SPACE=4d47
TPM_SIZE=312
if [ "$CONFIG_TPM" = "y" ]; then
tpmr destroy "$TPM_NVRAM_SPACE" "$TPM_SIZE" \
|| die "Unable to wipe sealed secret"
fi

View File

@ -62,6 +62,11 @@ recovery() {
tpmr extend -ix 4 -ic recovery
fi
if [ "$CONFIG_RESTRICTED_BOOT" = y ]; then
echo >&2 "Restricted Boot enabled, recovery console disabled, rebooting in 5 seconds"
sleep 5
/bin/reboot
fi
while [ true ]
do
echo >&2 "!!!!! Starting recovery shell"