mirror of
https://github.com/linuxboot/heads.git
synced 2024-12-24 15:16:42 +00:00
157efc6b03
The CONFIG_BASIC test was backwards, as a result it skipped the LUKS disk unlock logic if basic mode was _not_ enabled. This wasn't observed in the PureBoot distribution because we disable the LUKS disk unlock feature. CONFIG_BOOT_REQ_ROLLBACK and CONFIG_BOOT_REQ_HASH logic was also skipped incorrectly, though neither of these are enabled on any board so this had no effect in the PureBoot distribution either. Test basic with each bit of logic to eliminate duplication of the kexec-boot call and fix the LUKS disk unlock feature. Signed-off-by: Jonathon Hall <jonathon.hall@puri.sm>
424 lines
11 KiB
Bash
Executable File
424 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
# Generic configurable boot script via kexec
|
|
set -e -o pipefail
|
|
. /tmp/config
|
|
. /etc/functions
|
|
|
|
TRACE "Under /bin/kexec-select-boot"
|
|
|
|
add=""
|
|
remove=""
|
|
config="*.cfg"
|
|
unique="n"
|
|
valid_hash="n"
|
|
valid_global_hash="n"
|
|
valid_rollback="n"
|
|
force_menu="n"
|
|
gui_menu="n"
|
|
force_boot="n"
|
|
skip_confirm="n"
|
|
while getopts "b:d:p:a:r:c:uimgfs" arg; do
|
|
case $arg in
|
|
b) bootdir="$OPTARG" ;;
|
|
d) paramsdev="$OPTARG" ;;
|
|
p) paramsdir="$OPTARG" ;;
|
|
a) add="$OPTARG" ;;
|
|
r) remove="$OPTARG" ;;
|
|
c) config="$OPTARG" ;;
|
|
u) unique="y" ;;
|
|
m) force_menu="y" ;;
|
|
i) valid_hash="y"; valid_rollback="y" ;;
|
|
g) gui_menu="y" ;;
|
|
f) force_boot="y"; valid_hash="y"; valid_rollback="y" ;;
|
|
s) skip_confirm="y" ;;
|
|
esac
|
|
done
|
|
|
|
if [ -z "$bootdir" ]; then
|
|
die "Usage: $0 -b /boot"
|
|
fi
|
|
|
|
if [ -z "$paramsdev" ]; then
|
|
paramsdev="$bootdir"
|
|
fi
|
|
|
|
if [ -z "$paramsdir" ]; then
|
|
paramsdir="$bootdir"
|
|
fi
|
|
|
|
bootdir="${bootdir%%/}"
|
|
paramsdev="${paramsdev%%/}"
|
|
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!";
|
|
echo "If you have not intentionally regenerated TPM2 primary key,";
|
|
warn "your system may have been compromised!";
|
|
}
|
|
else
|
|
echo "WARNING: Hash of TPM2 primary key handle does not exist!"
|
|
echo "Please rebuild the boot hash tree."
|
|
default_failed="y"
|
|
fi
|
|
fi
|
|
|
|
verify_global_hashes()
|
|
{
|
|
echo "+++ Checking verified boot hash file "
|
|
# Check the hashes of all the files
|
|
if verify_checksums "$bootdir" "$gui_menu" ; then
|
|
echo "+++ Verified boot hashes "
|
|
valid_hash='y'
|
|
valid_global_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: Boot Hash Mismatch' \
|
|
--msgbox "The following files failed the verification process:\n${CHANGED_FILES}\nExiting to a recovery shell" 0 80
|
|
fi
|
|
die "$TMP_HASH_FILE: boot hash mismatch"
|
|
fi
|
|
# If user enables it, check root hashes before boot as well
|
|
if [[ "$CONFIG_ROOT_CHECK_AT_BOOT" = "y" && "$force_menu" == "n" ]]; then
|
|
if root-hashes-gui.sh -c; then
|
|
echo "+++ Verified root hashes, continuing boot "
|
|
# if user re-signs, it wipes out saved options, so scan the boot directory and generate
|
|
if [ ! -r "$TMP_MENU_FILE" ]; then
|
|
scan_options
|
|
fi
|
|
else
|
|
# root-hashes-gui.sh handles the GUI error menu, just die here
|
|
if [ "$gui_menu" = "y" ]; then
|
|
whiptail $BG_COLOR_ERROR --title 'ERROR: Root Hash Mismatch' \
|
|
--msgbox "The root hash check failed!\nExiting to a recovery shell" 0 80
|
|
fi
|
|
die "root hash mismatch, see /tmp/hash_output_mismatches for details"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
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"
|
|
|
|
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`
|
|
if [ $num_options -eq 0 ]; then
|
|
die "No boot options"
|
|
fi
|
|
|
|
if [ $num_options -eq 1 -a $first_menu = "y" ]; then
|
|
option_index=1
|
|
elif [ "$gui_menu" = "y" ]; then
|
|
MENU_OPTIONS=""
|
|
n=0
|
|
while read option
|
|
do
|
|
parse_option
|
|
n=`expr $n + 1`
|
|
name=$(echo $name | tr " " "_")
|
|
MENU_OPTIONS="$MENU_OPTIONS $n ${name} "
|
|
done < $TMP_MENU_FILE
|
|
|
|
whiptail --title "Select your boot option" \
|
|
--menu "Choose the boot option [1-$n, a to abort]:" 0 80 8 \
|
|
-- $MENU_OPTIONS \
|
|
2>/tmp/whiptail || die "Aborting boot attempt"
|
|
|
|
option_index=$(cat /tmp/whiptail)
|
|
else
|
|
echo "+++ Select your boot option:"
|
|
n=0
|
|
while read option
|
|
do
|
|
parse_option
|
|
n=`expr $n + 1`
|
|
echo "$n. $name [$kernel]"
|
|
done < $TMP_MENU_FILE
|
|
|
|
read \
|
|
-p "Choose the boot option [1-$n, a to abort]: " \
|
|
option_index
|
|
|
|
if [ "$option_index" = "a" ]; then
|
|
die "Aborting boot attempt"
|
|
fi
|
|
fi
|
|
first_menu="n"
|
|
|
|
option=`head -n $option_index $TMP_MENU_FILE | tail -1`
|
|
parse_option
|
|
}
|
|
|
|
confirm_menu_option() {
|
|
if [ "$gui_menu" = "y" ]; then
|
|
default_text="Make default"
|
|
[[ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" = "y" ]] && default_text="${default_text} and boot"
|
|
whiptail $BG_COLOR_WARNING --title "Confirm boot details" \
|
|
--menu "Confirm the boot details for $name:\n\n$(echo $kernel| fold -s -w 80) \n\n" 0 80 8 \
|
|
-- 'd' "${default_text}" 'y' "Boot one time" \
|
|
2>/tmp/whiptail || die "Aborting boot attempt"
|
|
|
|
option_confirm=$(cat /tmp/whiptail)
|
|
else
|
|
echo "+++ Please confirm the boot details for $name:"
|
|
echo $option
|
|
|
|
read \
|
|
-n 1 \
|
|
-p "Confirm selection by pressing 'y', make default with 'd': " \
|
|
option_confirm
|
|
echo
|
|
fi
|
|
}
|
|
|
|
parse_option() {
|
|
name=`echo $option | cut -d\| -f1`
|
|
kernel=`echo $option | cut -d\| -f3`
|
|
}
|
|
|
|
scan_options() {
|
|
echo "+++ Scanning for unsigned boot options"
|
|
option_file="/tmp/kexec_options.txt"
|
|
scan_boot_options "$bootdir" "$config" "$option_file"
|
|
if [ ! -s $option_file ]; then
|
|
die "Failed to parse any boot options"
|
|
fi
|
|
if [ "$unique" = 'y' ]; then
|
|
sort -r $option_file | uniq > $TMP_MENU_FILE
|
|
else
|
|
cp $option_file $TMP_MENU_FILE
|
|
fi
|
|
}
|
|
|
|
save_default_option() {
|
|
if [ "$gui_menu" != "y" ]; then
|
|
read \
|
|
-n 1 \
|
|
-p "Saving a default will modify the disk. Proceed? (Y/n): " \
|
|
default_confirm
|
|
echo
|
|
fi
|
|
|
|
[ "$default_confirm" = "" ] && default_confirm="y"
|
|
if [[ "$default_confirm" = "y" || "$default_confirm" = "Y" ]]; then
|
|
if kexec-save-default \
|
|
-b "$bootdir" \
|
|
-d "$paramsdev" \
|
|
-p "$paramsdir" \
|
|
-i "$option_index" \
|
|
; then
|
|
echo "+++ Saved defaults to device"
|
|
sleep 2
|
|
default_failed="n"
|
|
force_menu="n"
|
|
return
|
|
else
|
|
echo "Failed to save defaults"
|
|
fi
|
|
fi
|
|
|
|
option_confirm="n"
|
|
}
|
|
|
|
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`
|
|
|
|
# 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`
|
|
if [ "$option" != "$expectedoption" ]; then
|
|
if [ "$gui_menu" = "y" ]; then
|
|
whiptail $BG_COLOR_ERROR --title 'ERROR: Boot Entry Has Changed' \
|
|
--msgbox "The list of boot entries has changed\n\nPlease set a new default" 0 80
|
|
fi
|
|
warn "!!! Boot entry has changed - please set a new default"
|
|
return
|
|
fi
|
|
parse_option
|
|
|
|
if [ "$CONFIG_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
|
|
fi
|
|
|
|
echo "+++ Executing default boot for $name:"
|
|
do_boot
|
|
warn "Failed to boot default option"
|
|
}
|
|
|
|
user_select() {
|
|
# No default expected boot parameters, ask user
|
|
|
|
option_confirm=""
|
|
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
|
|
do_boot
|
|
else
|
|
confirm_menu_option
|
|
fi
|
|
|
|
if [ "$option_confirm" = 'd' ]; then
|
|
save_default_option
|
|
fi
|
|
done
|
|
|
|
if [ "$option_confirm" = "d" ]; then
|
|
if [ ! -r "$TMP_KEY_DEVICES" ]; then
|
|
# continue below to boot the new default option
|
|
true
|
|
else
|
|
echo "+++ Rebooting to start the new default option"
|
|
sleep 2
|
|
if [ "$CONFIG_DEBUG_OUTPUT" != "y" ]; then
|
|
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
|
|
# for a chance to inspect debug output
|
|
recovery "Entering recovery to permit inspection of /tmp/debug.log output, reboot to continue"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
do_boot
|
|
}
|
|
|
|
do_boot()
|
|
{
|
|
if [ "$CONFIG_BASIC" != y ] && [ "$CONFIG_BOOT_REQ_ROLLBACK" = "y" ] && [ "$valid_rollback" = "n" ]; then
|
|
die "!!! Missing required rollback counter state"
|
|
fi
|
|
|
|
if [ "$CONFIG_BASIC" != y ] && [ "$CONFIG_BOOT_REQ_HASH" = "y" ] && [ "$valid_hash" = "n" ]; then
|
|
die "!!! Missing required boot hashes"
|
|
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"
|
|
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-boot -b "$bootdir" -e "$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"
|
|
fi
|
|
}
|
|
|
|
while true; do
|
|
if [ "$force_boot" = "y" -o "$CONFIG_BASIC" = "y" ]; then
|
|
check_config $paramsdir force
|
|
else
|
|
check_config $paramsdir
|
|
fi
|
|
TMP_DEFAULT_FILE=`find /tmp/kexec/kexec_default.*.txt 2>/dev/null | head -1` || true
|
|
TMP_MENU_FILE="/tmp/kexec/kexec_menu.txt"
|
|
TMP_HASH_FILE="/tmp/kexec/kexec_hashes.txt"
|
|
TMP_TREE_FILE="/tmp/kexec/kexec_tree.txt"
|
|
TMP_DEFAULT_HASH_FILE="/tmp/kexec/kexec_default_hashes.txt"
|
|
TMP_ROLLBACK_FILE="/tmp/kexec/kexec_rollback.txt"
|
|
TMP_KEY_DEVICES="/tmp/kexec/kexec_key_devices.txt"
|
|
TMP_KEY_LVM="/tmp/kexec/kexec_key_lvm.txt"
|
|
|
|
# Allow a way for users to ignore warnings and boot into their systems
|
|
# even if hashes don't match
|
|
if [ "$force_boot" = "y" ]; then
|
|
scan_options
|
|
if [ "$CONFIG_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
|
|
|
|
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"
|
|
fi
|
|
fi
|
|
|
|
# if no saved options, scan the boot directory and generate
|
|
if [ ! -r "$TMP_MENU_FILE" ]; then
|
|
scan_options
|
|
fi
|
|
|
|
if [ "$CONFIG_BASIC" != "y" ]; then
|
|
# Optionally enforce device file hashes
|
|
if [ -r "$TMP_HASH_FILE" ]; then
|
|
valid_global_hash="n"
|
|
|
|
verify_global_hashes
|
|
|
|
if [ "$valid_global_hash" = "n" ]; then
|
|
die "Failed to verify global hashes"
|
|
fi
|
|
fi
|
|
|
|
if [ "$CONFIG_IGNORE_ROLLBACK" != "y" -a -r "$TMP_ROLLBACK_FILE" ]; then
|
|
# in the case of iso boot with a rollback file, do not assume valid
|
|
valid_rollback="n"
|
|
|
|
verify_rollback_counter
|
|
fi
|
|
fi
|
|
|
|
if [ "$default_failed" != "y" \
|
|
-a "$force_menu" = "n" \
|
|
-a -r "$TMP_DEFAULT_FILE" \
|
|
-a -r "$TMP_DEFAULT_HASH_FILE" ] \
|
|
; then
|
|
default_select
|
|
default_failed="y"
|
|
else
|
|
user_select
|
|
fi
|
|
done
|
|
|
|
die "!!! Shouldn't get here"
|