diff --git a/initrd/bin/basic-autoboot.sh b/initrd/bin/basic-autoboot.sh new file mode 100755 index 00000000..d924affd --- /dev/null +++ b/initrd/bin/basic-autoboot.sh @@ -0,0 +1,11 @@ +#!/bin/bash +set -o pipefail + +. /etc/functions + +BOOT_MENU_OPTIONS=/tmp/basic-autoboot-options + +scan_boot_options /boot "grub.cfg" "$BOOT_MENU_OPTIONS" +if [ -s "$BOOT_MENU_OPTIONS" ]; then + kexec-boot -b /boot -e "$(head -1 "$BOOT_MENU_OPTIONS")" +fi diff --git a/initrd/bin/config-gui.sh b/initrd/bin/config-gui.sh index 014c8da7..03841551 100755 --- a/initrd/bin/config-gui.sh +++ b/initrd/bin/config-gui.sh @@ -27,14 +27,32 @@ while true; do menu_choice=${param::1} unset param else + # check current PureBoot Mode + BASIC_MODE="$(load_config_value CONFIG_PUREBOOT_BASIC)" + BASIC_NO_AUTOMATIC_DEFAULT="$(load_config_value CONFIG_BASIC_NO_AUTOMATIC_DEFAULT)" + + dynamic_config_options=() + + if [ "$BASIC_MODE" = "y" ]; then + dynamic_config_options+=( \ + 'P' " $(get_config_display_action "$BASIC_MODE") PureBoot Basic Mode" \ + 'A' " $(get_inverted_config_display_action "$BASIC_NO_AUTOMATIC_DEFAULT") automatic default boot" \ + ) + else + dynamic_config_options+=( \ + 'r' ' Clear GPG key(s) and reset all user settings' \ + 'R' ' Change the root device for hashing' \ + 'D' ' Change the root directories to hash' \ + 'B' ' Check root hashes at boot' \ + 'P' " $(get_config_display_action "$BASIC_MODE") PureBoot Basic Mode" \ + ) + fi + unset menu_choice whiptail $BG_COLOR_MAIN_MENU --title "Config Management Menu" \ --menu "This menu lets you change settings for the current BIOS session.\n\nAll changes will revert after a reboot,\n\nunless you also save them to the running BIOS." 0 80 10 \ 'b' ' Change the /boot device' \ - 'r' ' Clear GPG key(s) and reset all user settings' \ - 'R' ' Change the root device for hashing' \ - 'D' ' Change the root directories to hash' \ - 'B' ' Check root hashes at boot' \ + "${dynamic_config_options[@]}" \ 's' ' Save the current configuration to the running BIOS' \ 'x' ' Return to Main Menu' \ 2>/tmp/whiptail || recovery "GUI menu failed" @@ -228,6 +246,61 @@ while true; do fi fi ;; + "P" ) + if [ "$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. + \n\nDo you want to proceed?" 0 80) then + + set_config /etc/config.user "CONFIG_PUREBOOT_BASIC" "y" + combine_configs + + whiptail --title 'Config change successful' \ + --msgbox "PureBoot Basic mode enabled;\nsave the config change and reboot for it to go into effect." 0 80 + + fi + else + if (whiptail --title 'Disable PureBoot Basic Mode?' \ + --yesno "This will enable all signature checking on the firmware + \nand boot files, and enable use of the Librem Key. + \n\nDo you want to proceed?" 0 80) then + + set_config /etc/config.user "CONFIG_PUREBOOT_BASIC" "n" + combine_configs + + whiptail --title 'Config change successful' \ + --msgbox "PureBoot Basic mode has been disabled;\nsave the config change and reboot for it to go into effect." 0 80 + fi + fi + ;; + "A" ) + if [ "$BASIC_NO_AUTOMATIC_DEFAULT" = "n" ]; then + if (whiptail --title 'Disable automatic default boot?' \ + --yesno "You will need to select a default boot option. + \nIf the boot options are changed, such as for an OS update, + \nyou will be prompted to select a new default. + \n\nDo you want to proceed?" 0 80) then + + set_config /etc/config.user "CONFIG_BASIC_NO_AUTOMATIC_DEFAULT" "y" + combine_configs + + whiptail --title 'Config change successful' \ + --msgbox "Automatic default boot disabled;\nsave the config change and reboot for it to go into effect." 0 80 + fi + else + if (whiptail --title 'Enable automatic default boot?' \ + --yesno "The first boot option will be used automatically. + \n\nDo you want to proceed?" 0 80) then + + set_config /etc/config.user "CONFIG_BASIC_NO_AUTOMATIC_DEFAULT" "n" + combine_configs + + whiptail --title 'Config change successful' \ + --msgbox "Automatic default boot enabled;\nsave the config change and reboot for it to go into effect." 0 80 + fi + fi + ;; esac done diff --git a/initrd/bin/gui-init-basic b/initrd/bin/gui-init-basic new file mode 100755 index 00000000..9e76d18c --- /dev/null +++ b/initrd/bin/gui-init-basic @@ -0,0 +1,213 @@ +#!/bin/bash +# Boot from a local disk installation + +BOARD_NAME=${CONFIG_BOARD_NAME:-${CONFIG_BOARD}} +MAIN_MENU_TITLE="${BOARD_NAME} | PureBoot Basic Boot Menu" +export BG_COLOR_MAIN_MENU="" + +. /etc/functions +. /etc/gui_functions +. /tmp/config + +# skip_to_menu is set if the user selects "continue to the main menu" from any +# error, so we will indeed go to the main menu even if other errors occur. It's +# reset when we reach the main menu so the user can retry from the main menu and +# # see errors again. +skip_to_menu="false" + +mount_boot() +{ + TRACE "Under /bin/gui-init:mount_boot" + # Mount local disk if it is not already mounted + while ! grep -q /boot /proc/mounts ; do + # try to mount if CONFIG_BOOT_DEV exists + if [ -e "$CONFIG_BOOT_DEV" ]; then + mount -o ro $CONFIG_BOOT_DEV /boot + [[ $? -eq 0 ]] && continue + fi + + # CONFIG_BOOT_DEV doesn't exist or couldn't be mounted, so give user options + BG_COLOR_MAIN_MENU=$BG_COLOR_ERROR + whiptail $BG_COLOR_ERROR --title "ERROR: No Bootable OS Found!" \ + --menu " No bootable OS was found on the default boot device $CONFIG_BOOT_DEV. + How would you like to proceed?" 0 80 4 \ + 'b' ' Select a new boot device' \ + 'u' ' Boot from USB' \ + 'm' ' Continue to the main menu' \ + 'x' ' Exit to recovery shell' \ + 2>/tmp/whiptail || recovery "GUI menu failed" + + option=$(cat /tmp/whiptail) + case "$option" in + b ) + config-gui.sh boot_device_select + if [ $? -eq 0 ]; then + # update CONFIG_BOOT_DEV + . /tmp/config + BG_COLOR_MAIN_MENU="" + fi + ;; + u ) + exec /bin/usb-init + ;; + m ) + skip_to_menu="true" + break + ;; + * ) + recovery "User requested recovery shell" + ;; + esac + done +} + +prompt_auto_default_boot() +{ + TRACE "Under /bin/gui-init:prompt_auto_default_boot" + + # save IFS before changing, restore after read + IFS_DEF=$IFS + IFS='' + first_pass=false + + echo -e "\n\n" + read -t $CONFIG_AUTO_BOOT_TIMEOUT -s -n 1 -p "Automatic boot in $CONFIG_AUTO_BOOT_TIMEOUT seconds unless interrupted by keypress... " + if [[ $? -ne 0 ]]; then + IFS=$IFS_DEF + echo -e "\n\nAttempting default boot...\n\n" + attempt_default_boot + fi + IFS=$IFS_DEF +} + +show_main_menu() +{ + TRACE "Under /bin/gui-init:show_main_menu" + date=`date "+%Y-%m-%d %H:%M:%S %Z"` + whiptail $BG_COLOR_MAIN_MENU --title "$MAIN_MENU_TITLE" \ + --menu "$date" 0 80 10 \ + 'd' ' Default boot' \ + 'o' ' Options -->' \ + 's' ' System Info' \ + 'p' ' Power Off' \ + 2>/tmp/whiptail || recovery "GUI menu failed" + + option=$(cat /tmp/whiptail) + case "$option" in + d ) + attempt_default_boot + ;; + o ) + show_options_menu + ;; + s ) + show_system_info + ;; + p ) + poweroff + ;; + esac +} + +show_options_menu() +{ + TRACE "Under /bin/gui-init:show_options_menu" + whiptail $BG_COLOR_MAIN_MENU --title "PureBoot Basic Options" \ + --menu "" 0 80 10 \ + 'b' ' Boot Options -->' \ + 'c' ' Change configuration settings -->' \ + 'f' ' Flash/Update the BIOS -->' \ + 'x' ' Exit to recovery shell' \ + 'r' ' <-- Return to main menu' \ + 2>/tmp/whiptail || recovery "GUI menu failed" + + option=$(cat /tmp/whiptail) + case "$option" in + b ) + show_boot_options_menu + ;; + c ) + config-gui.sh + ;; + f ) + flash-gui.sh + ;; + x ) + recovery "User requested recovery shell" + ;; + r ) + ;; + esac +} + +show_boot_options_menu() +{ + TRACE "Under /bin/gui-init:show_boot_options_menu" + whiptail $BG_COLOR_MAIN_MENU --title "Boot Options" \ + --menu "Select A Boot Option" 0 80 10 \ + 'm' ' Show OS boot menu' \ + 'u' ' USB boot' \ + 'r' ' <-- Return to main menu' \ + 2>/tmp/whiptail || recovery "GUI menu failed" + + option=$(cat /tmp/whiptail) + case "$option" in + m ) + # select a kernel from the menu + select_os_boot_option + ;; + u ) + exec /bin/usb-init + ;; + r ) + ;; + esac +} + +select_os_boot_option() +{ + TRACE "Under /bin/gui-init:select_os_boot_option" + mount_boot + kexec-select-boot -m -b /boot -c "grub.cfg" -g -i +} + +attempt_default_boot() +{ + TRACE "Under /bin/gui-init:attempt_default_boot" + mount_boot + + DEFAULT_FILE=`find /boot/kexec_default.*.txt 2>/dev/null | head -1` + # Basic by default boots automatically to the first menu option. This allows + # kernel updates to work in Basic by default without prompting to select a + # new default boot option. + if [ "$CONFIG_BASIC_NO_AUTOMATIC_DEFAULT" != "y" ]; then + basic-autoboot.sh + elif [ -r "$DEFAULT_FILE" ]; then + kexec-select-boot -b /boot -c "grub.cfg" -g -i -s \ + || recovery "Failed default boot" + elif (whiptail $BG_COLOR_WARNING --title 'No Default Boot Option Configured' \ + --yesno "There is no default boot option configured yet.\nWould you like to load a menu of boot options?\nOtherwise you will return to the main menu." 0 80) then + kexec-select-boot -m -b /boot -c "grub.cfg" -g -i + fi +} + +# gui-init-basic start +TRACE "Under /bin/gui-init, start" + +if ! detect_boot_device ; then + # can't determine /boot device or no OS installed, + # so fall back to interactive selection + mount_boot +fi + +if [ "$skip_to_menu" != "true" -a -n "$CONFIG_AUTO_BOOT_TIMEOUT" ]; then + prompt_auto_default_boot +fi + +while true; do + TRACE "Under gui-init:while true loop" + skip_to_menu="false" + show_main_menu +done + +recovery "Something failed during boot" diff --git a/initrd/bin/kexec-save-default b/initrd/bin/kexec-save-default index bb76dbe3..a099a52a 100755 --- a/initrd/bin/kexec-save-default +++ b/initrd/bin/kexec-save-default @@ -194,8 +194,9 @@ if [ "$CONFIG_TPM" = "y" ];then extparam=-r fi fi -kexec-sign-config -p $paramsdir $extparam \ -|| die "Failed to sign default config" - +if [ "$CONFIG_PUREBOOT_BASIC" != "y" ]; then + kexec-sign-config -p $paramsdir $extparam \ + || die "Failed to sign default config" +fi # switch back to ro mode mount -o ro,remount $paramsdev diff --git a/initrd/bin/kexec-select-boot b/initrd/bin/kexec-select-boot index 6eda50cf..b181ff50 100755 --- a/initrd/bin/kexec-select-boot +++ b/initrd/bin/kexec-select-boot @@ -257,19 +257,20 @@ default_select() { fi parse_option - # Enforce that default option hashes are valid - echo "+++ Checking verified default boot hash file " - # Check the hashes of all the files - if ( cd $bootdir && sha256sum -c "$TMP_DEFAULT_HASH_FILE" > /tmp/hash_output ); then - echo "+++ Verified default boot hashes " - valid_hash='y' - else - if [ "$gui_menu" = "y" ]; then - CHANGED_FILES=$(grep -v 'OK$' /tmp/hash_output | cut -f1 -d ':') - whiptail $BG_COLOR_ERROR --title 'ERROR: Default Boot Hash Mismatch' \ - --msgbox "The following files failed the verification process:\n${CHANGED_FILES}\nExiting to a recovery shell" 0 80 + if [ "$CONFIG_PUREBOOT_BASIC" != "y" ]; then + # Enforce that default option hashes are valid + echo "+++ Checking verified default boot hash file " + # Check the hashes of all the files + if ( cd $bootdir && sha256sum -c "$TMP_DEFAULT_HASH_FILE" > /tmp/hash_output ); then + echo "+++ Verified default boot hashes " + valid_hash='y' + else + if [ "$gui_menu" = "y" ]; then + CHANGED_FILES=$(grep -v 'OK$' /tmp/hash_output | cut -f1 -d ':') + whiptail $BG_COLOR_ERROR --title 'ERROR: Default Boot Hash Mismatch' \ + --msgbox "The following files failed the verification process:\n${CHANGED_FILES}\nExiting to a recovery shell" 0 80 + fi fi - die "!!! $TMP_DEFAULT_HASH_FILE: default boot hash mismatch" fi echo "+++ Executing default boot for $name:" @@ -320,6 +321,11 @@ user_select() { do_boot() { + if [ "$CONFIG_PUREBOOT_BASIC" != "y" ]; then + kexec-boot -b "$bootdir" -e "$option" -a "$add" -r "$remove" \ + || die "!!! Failed to boot w/ options: $option" + fi + if [ "$CONFIG_BOOT_REQ_ROLLBACK" = "y" -a "$valid_rollback" = "n" ]; then die "!!! Missing required rollback counter state" fi @@ -348,7 +354,7 @@ do_boot() } while true; do - if [ "$force_boot" = "y" ]; then + if [ "$force_boot" = "y" -o "$CONFIG_PUREBOOT_BASIC" = "y" ]; then check_config $paramsdir force else check_config $paramsdir @@ -366,9 +372,11 @@ while true; do # even if hashes don't match if [ "$force_boot" = "y" ]; then scan_options - # Remove boot splash and make background red in the event of a forced boot - add="$add vt.default_red=0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff" - remove="$remove splash quiet" + if [ "$CONFIG_PUREBOOT_BASIC" != "y" ]; then + # Remove boot splash and make background red in the event of a forced boot + add="$add vt.default_red=0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff" + remove="$remove splash quiet" + fi user_select fi @@ -385,7 +393,7 @@ while true; do scan_options fi - if [ "$CONFIG_TPM" = "y" ]; then + if [ "$CONFIG_TPM" = "y" -a "$CONFIG_PUREBOOT_BASIC" != "y" ]; then # Optionally enforce device file hashes if [ -r "$TMP_HASH_FILE" ]; then valid_global_hash="n" diff --git a/initrd/etc/functions b/initrd/etc/functions index f3cf513d..cf81061c 100755 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -266,7 +266,7 @@ check_config() { || die 'Failed to empty kexec tmp dir' fi - if [ ! -r $1/kexec.sig ]; then + if [ ! -r $1/kexec.sig -a "$CONFIG_PUREBOOT_BASIC" != "y" ]; then return fi diff --git a/initrd/etc/gui_functions b/initrd/etc/gui_functions index 7e050fb1..40a50679 100755 --- a/initrd/etc/gui_functions +++ b/initrd/etc/gui_functions @@ -119,3 +119,16 @@ get_config_display_action() { [ "$1" = "y" ] && echo "Disable" || echo "Enable" } + +# Invert a config value +invert_config() +{ + [ "$1" = "y" ] && echo "n" || echo "y" +} + +# Get "Enable" or "Disable" for a config that internally is inverted (because it +# disables a behavior that is on by default). +get_inverted_config_display_action() +{ + get_config_display_action "$(invert_config "$1")" +} diff --git a/initrd/init b/initrd/init index c6d43630..18d0bd55 100755 --- a/initrd/init +++ b/initrd/init @@ -131,6 +131,10 @@ echo "export CONFIG_TPM2_TOOLS=\"$CONFIG_TPM2_TOOLS\"" >> /etc/config.user combine_configs . /tmp/config +if [ "$CONFIG_PUREBOOT_BASIC" = "y" ]; then + echo -e "***** BASIC mode: tamper detection disabled\n" > /dev/tty0 +fi + # export firmware version export FW_VER=$(dmesg | grep 'DMI' | grep -o 'BIOS.*' | cut -f2- -d ' ') # chop off date, since will always be epoch w/timeless builds @@ -142,6 +146,11 @@ if [ ! -z "$CONFIG_BOOT_DEV" ]; then echo >> /etc/fstab "$CONFIG_BOOT_DEV /boot auto defaults,ro 0 0" fi +if [ "$CONFIG_PUREBOOT_BASIC" = "y" ]; then + CONFIG_BOOTSCRIPT=/bin/gui-init-basic + export CONFIG_HOTPKEY=n +fi + if [ ! -x "$CONFIG_BOOTSCRIPT" -a ! -x "$CONFIG_BOOTSCRIPT_NETWORK" ]; then recovery 'Boot script missing? Entering recovery shell' else