Added a generic boot config and persistent params

Refactored boot parsing code and applied that in local-init to
scan /boot for grub options and allow the user to unsafely boot
anything.  This goes a long way to addressing #196.

Optionally the user can customize those boot parameters or enforce
arbitrary hashes on the boot device by creating and signing config
files in /boot/ or /media/ or /media/kexec_iso/ISO_FILENAME/.
This commit is contained in:
Francis Lam 2017-07-02 23:01:04 -04:00
parent 76a20288a3
commit 3614044fff
No known key found for this signature in database
GPG Key ID: 0A59C698920806EB
11 changed files with 259 additions and 91 deletions

View File

@ -0,0 +1,24 @@
# Configuration for a x230 running non-Qubes
BOARD=x230
CONFIG_CRYPTSETUP=y
CONFIG_FLASHROM=y
CONFIG_GPG=y
CONFIG_KEXEC=y
CONFIG_UTIL_LINUX=y
CONFIG_LVM2=y
CONFIG_MBEDTLS=y
CONFIG_PCIUTILS=y
CONFIG_POPT=y
CONFIG_QRENCODE=y
CONFIG_TPMTOTP=y
CONFIG_XEN=y
CONFIG_DROPBEAR=y
CONFIG_LINUX_USB=y
CONFIG_LINUX_E1000E=y
CONFIG_BOOTSCRIPT=/bin/local-init
CONFIG_BOOT_DEV="/dev/sda1"
CONFIG_USB_BOOT_DEV="/dev/sdb1"

View File

@ -14,6 +14,10 @@ kexectype=`echo $entry | cut -d\| -f2`
kexecparams=`echo $entry | cut -d\| -f3- | tr '|' '\n'`
kexeccmd="kexec"
# TODO: make this configurable
cmdadd="intel_iommu=on $cmdadd"
cmdremove="quiet $cmdremove"
fix_file_path() {
filepath=`find $bootdir -path "*$firstval" | tail -1`
if ! [ -r $filepath ]; then

22
initrd/bin/kexec-check-config Executable file
View File

@ -0,0 +1,22 @@
#!/bin/sh
# Check for valid kexec params and copy to tmp
. /etc/functions
MEDIA="$1"
if [ ! -d /tmp/kexec ]; then
mkdir /tmp/kexec \
|| recovery 'failed to make kexec tmp dir'
fi
if [ ! -r $MEDIA/kexec.sig ]; then
exit 0
fi
if [ -z `find $MEDIA/kexec*.txt` ]; then
exit 0
fi
if sha256sum `find $MEDIA/kexec*.txt` | gpg --verify $MEDIA/kexec.sig - ; then
echo "+++ Found verified kexec boot params"
cp $MEDIA/kexec*.txt /tmp/kexec
fi

42
initrd/bin/kexec-iso-init Executable file
View File

@ -0,0 +1,42 @@
#!/bin/sh
# Boot from signed ISO
. /etc/functions
. /etc/config
MOUNTED_ISO_PATH="$1"
ISO_PATH="$2"
DEV="$3"
echo '+++ Verifying ISO'
# Verify the signature on the hashes
ISOSIG="$MOUNTED_ISO_PATH.sig"
if ! [ -r "$ISOSIG" ]; then
ISOSIG="$MOUNTED_ISO_PATH.asc"
fi
gpgv "$ISOSIG" "$MOUNTED_ISO_PATH" \
|| recovery 'ISO signature failed'
echo '+++ Mounting ISO and booting'
mount -t iso9660 -o loop $MOUNTED_ISO_PATH /boot \
|| recovery '$MOUNTED_ISO_PATH: Unable to mount /boot'
DEV_UUID=`blkid $DEV | tail -1 | tr " " "\n" | grep UUID | cut -d\" -f2`
ADD="fromiso=/dev/disk/by-uuid/$DEV_UUID/$ISO_PATH"
REMOVE=""
ADD_FILE=/tmp/kexec/kexec_iso_add.txt
if [ -r $ADD_FILE ]; then
NEW_ADD=`cat $ADD_FILE`
ADD=$(eval "echo \"$NEW_ADD\"")
echo "+++ Overriding standard ISO kernel add arguments: $ADD"
fi
REMOVE_FILE=/tmp/kexec/kexec_iso_remove.txt
if [ -r $REMOVE_FILE ]; then
NEW_REMOVE=`cat $REMOVE_FILE`
REMOVE=$(eval "echo \"$NEW_REMOVE\"")
echo "+++ Overriding standard ISO kernel remove arguments: $REMOVE"x
fi
kexec-select-boot /boot "$ADD" "$REMOVE"

82
initrd/bin/kexec-select-boot Executable file
View File

@ -0,0 +1,82 @@
#!/bin/sh
. /etc/functions
bootdir=$1
add=$2
remove=$3
MENU_NAME="kexec_menu.txt"
HASH_NAME="kexec_hashes.txt"
TMP_MENU_FILE=/tmp/kexec/$MENU_NAME
TMP_HASH_FILE=/tmp/kexec/$HASH_NAME
get_menu_option() {
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
recovery "Aborting boot attempt"
fi
option=`head -n $option_index $TMP_MENU_FILE | tail -1`
parse_option
}
confirm_menu_option() {
echo "+++ Please confirm the boot details for $name:"
echo $option
read \
-n 1 \
-p "Confirm selection by pressing 'y': " \
option_confirm
echo
}
parse_option() {
name=`echo $option | cut -d\| -f1`
kernel=`echo $option | cut -d\| -f3`
}
# optionally enforce file hashes
if [ -r $TMP_HASH_FILE ]; then
echo "+++ Checking verified boot hash file "
# Check the hashes of all the files
if cd $bootdir && sha256sum -c "$TMP_HASH_FILE" ; then
echo "+++ Verified boot hashes "
else
recovery "$TMP_HASH_FILE: boot hash mismatch"
fi
fi
# otherwise scan the boot directory and generate options
if [ ! -r $TMP_MENU_FILE ]; then
echo "+++ Scanning for unsigned boot options"
option_file="/tmp/kexec_options.txt"
for i in `find $bootdir -name "*.cfg"`; do
kexec-parse-boot $i >> $option_file
done
if [ ! -r $option_file ]; then
recovery "Failed to parse any boot options"
fi
sort $option_file | uniq > $TMP_MENU_FILE
fi
option_confirm=""
while [ "$option_confirm" != "y" ]
do
get_menu_option
confirm_menu_option
done
kexec-boot -b $bootdir -e "$option" -a "$add" -r "$remove"

45
initrd/bin/kexec-sign-config Executable file
View File

@ -0,0 +1,45 @@
#!/bin/sh
# Sign a valid directory of kexec params
. /etc/functions
MEDIA="$1"
if [ -z "$MEDIA" ]; then
die "Usage: $0 /boot "
fi
# setup the USB so we can reach the GPG card
if ! lsmod | grep -q ehci_hcd; then
insmod /lib/modules/ehci-hcd.ko \
|| die "ehci_hcd: module load failed"
fi
if ! lsmod | grep -q ehci_pci; then
insmod /lib/modules/ehci-pci.ko \
|| die "ehci_pci: module load failed"
fi
if ! lsmod | grep -q xhci_hcd; then
insmod /lib/modules/xhci-hcd.ko \
|| die "ehci_hcd: module load failed"
fi
if ! lsmod | grep -q xhci_pci; then
insmod /lib/modules/xhci-pci.ko \
|| die "ehci_pci: module load failed"
sleep 2
fi
gpg --card-status \
|| die "gpg card read failed"
for tries in 1 2 3; do
if sha256sum `find $MEDIA/kexec*.txt` | gpg \
--digest-algo SHA256 \
--detach-sign \
-a \
> $MEDIA/kexec.sig \
; then
exit 0
fi
done
warn "$MEDIA: Unable to sign boot hashes"
exit 1

29
initrd/bin/local-init Executable file
View File

@ -0,0 +1,29 @@
#!/bin/sh
# Boot from a local disk installation
. /etc/functions
. /etc/config
# Confirm we have a good TOTP unseal
if ! confirm_totp ; then
recovery 'Failed to unseal TOTP'
fi
# Extend PCR4 as soon as possible
tpm extend -ix 4 -ic local
if [ ! "$totp_confirm" = "y" ]; then
recovery "Failed to confirm validity of TOTP"
fi
# Mount local disk
if ! grep -q /boot /proc/mounts ; then
mount -o ro "$CONFIG_BOOT_DEV" /boot \
|| recovery '$CONFIG_BOOT_DEV: Unable to mount /boot'
fi
# Attempt to pull verified config from device
kexec-check-config /boot
exec kexec-select-boot /boot
recovery "Something failed..."

View File

@ -1,5 +1,5 @@
#!/bin/sh
# Boot a Tails installation
# Boot a USB installation
. /etc/functions
. /etc/config
@ -16,8 +16,9 @@ if [ ! "$totp_confirm" = "y" ]; then
recovery "Failed to confirm validity of TOTP"
fi
# TODO: Do a scan of USB devices to detect the Tails USB
mount-usb "$CONFIG_USB_BOOT_DEV"
# Mount the USB boot device
mount-usb "$CONFIG_USB_BOOT_DEV" \
|| recovery '$CONFIG_USB_BOOT_DEV: Unable to mount /media'
# Check for ISO first
get_menu_option() {
@ -55,11 +56,16 @@ if [ `wc -l /tmp/iso_menu.txt | cut -d\ -f1` -gt 0 ]; then
done
if [ -n "$option" ]; then
exec usb-iso-init $option
MOUNTED_ISO=$option
ISO=${option:7} # remove /media/ to get device relative path
kexec-check-config /media/kexec_iso/$ISO
exec kexec-iso-init $MOUNTED_ISO $ISO $CONFIG_USB_BOOT_DEV
fi
fi
echo "!!! Could not find any ISO, trying bootable USB"
exec usb-select-boot /media
# Attempt to pull verified config from device
kexec-check-config /media
exec kexec-select-boot /media
recovery "Something failed..."

View File

@ -1,27 +0,0 @@
#!/bin/sh
# Boot from signed ISO
. /etc/functions
. /etc/config
ISO="$1"
echo '+++ Verifying ISO'
# Verify the signature on the hashes
ISOSIG="$ISO.sig"
if ! [ -r "$ISOSIG" ]; then
ISOSIG="$ISO.asc"
fi
gpgv "$ISOSIG" "$ISO" \
|| recovery 'ISO signature failed'
echo '+++ Mounting ISO and booting'
mount -t iso9660 -o loop $ISO /boot
ISO_FILE=${ISO:7}
DEV=$CONFIG_USB_BOOT_DEV
DEV_UUID=`blkid $DEV | tail -1 | tr " " "\n" | grep UUID | cut -d\" -f2`
ADD="fromiso=/dev/disk/by-uuid/$DEV_UUID/$ISO_FILE"
/bin/usb-select-boot /boot "$ADD"

View File

@ -1,59 +0,0 @@
#!/bin/sh
. /etc/functions
bootdir=$1
add=$2
get_menu_option() {
echo "+++ Select your boot option:"
n=0
while read option
do
parse_option
n=`expr $n + 1`
echo "$n. $name [$kernel]"
done < /tmp/usb_menu.txt
read \
-p "Choose the boot option [1-$n, a to abort]: " \
option_index
if [ "$option_index" = "a" ]; then
recovery "Aborting boot attempt"
fi
option=`head -n $option_index /tmp/usb_menu.txt | tail -1`
parse_option
}
confirm_menu_option() {
echo "+++ Please confirm the boot details for $name:"
echo $option
read \
-n 1 \
-p "Confirm selection by pressing 'y': " \
option_confirm
echo
}
parse_option() {
name=`echo $option | cut -d\| -f1`
kernel=`echo $option | cut -d\| -f3`
}
echo "+++ Scanning USB media boot options"
for i in `find $bootdir -name "*.cfg"`; do usb-parse-boot $i >> /tmp/usb_options.txt; done
if [ ! -r /tmp/usb_options.txt ]; then
recovery "Failed to parse any boot options"
fi
sort /tmp/usb_options.txt | uniq > /tmp/usb_menu.txt
option_confirm=""
while [ "$option_confirm" != "y" ]
do
get_menu_option
confirm_menu_option
done
usb-boot -b $bootdir -e "$option" -a "intel_iommu=on $add" -r "quiet"