diff --git a/config/x230-generic.config b/config/x230-generic.config index 272c97e2..28ec1f19 100644 --- a/config/x230-generic.config +++ b/config/x230-generic.config @@ -20,5 +20,6 @@ CONFIG_LINUX_E1000E=y CONFIG_BOOTSCRIPT=/bin/local-init +CONFIG_BOOT_REQ_HASH=n CONFIG_BOOT_DEV="/dev/sda1" CONFIG_USB_BOOT_DEV="/dev/sdb1" diff --git a/initrd/bin/kexec-boot b/initrd/bin/kexec-boot index eb9d095d..b9b67def 100755 --- a/initrd/bin/kexec-boot +++ b/initrd/bin/kexec-boot @@ -1,12 +1,14 @@ #!/bin/sh . /etc/functions -while getopts "b:e:r:a:" arg; do +dryrun="n" +while getopts "b:e:r:a:d" arg; do case $arg in b) bootdir="$OPTARG" ;; e) entry="$OPTARG" ;; r) cmdremove="$OPTARG" ;; a) cmdadd="$OPTARG" ;; + d) dryrun="y" ;; esac done @@ -19,6 +21,8 @@ cmdadd="intel_iommu=on $cmdadd" cmdremove="quiet $cmdremove" fix_file_path() { + if [ "$dryrun" = "y" ]; then echo ".$firstval"; fi + filepath=`find $bootdir -path "*$firstval" | tail -1` if ! [ -r $filepath ]; then die "failed to find file $firstval" @@ -44,20 +48,23 @@ do firstval=`echo $line | cut -d\ -f2` restval=`echo $line | cut -d\ -f3-` if [ "$key" = "kernel" ]; then - fix_file_path - if [ "$kexectype" = "multiboot" ]; then + if [ "$kexectype" = "xen" ]; then # always overload xen and with custom arguments - # TODO: control this replacement via flag kexeccmd="$kexeccmd -l /bin/xen.gz" kexeccmd="$kexeccmd --command-line \"no-real-mode reboot=no\"" + elif [ "$kexectype" = "multiboot" ]; then + fix_file_path + kexeccmd="$kexeccmd -l $filepath" + kexeccmd="$kexeccmd --command-line \"$restval\"" else + fix_file_path kexeccmd="$kexeccmd -l $filepath" fi fi if [ "$key" = "module" ]; then fix_file_path cmdline="$restval" - if [ -n $first_module ]; then + if [ -n "$first_module" ]; then adjust_cmd_line unset first_module fi @@ -76,6 +83,8 @@ done << EOF $kexecparams EOF +if [ "$dryrun" = "y" ]; then exit 0; fi + echo "Loading the new kernel:" echo "$kexeccmd" eval "$kexeccmd" \ diff --git a/initrd/bin/kexec-iso-init b/initrd/bin/kexec-iso-init index ab8ffa17..1736699b 100755 --- a/initrd/bin/kexec-iso-init +++ b/initrd/bin/kexec-iso-init @@ -36,7 +36,8 @@ 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 + echo "+++ Overriding standard ISO kernel remove arguments: $REMOVE" fi -kexec-select-boot /boot "$ADD" "$REMOVE" +# Call kexec and indicate that hashes have been verified +kexec-select-boot -b /boot/ -d /media/ -p "/media/kexec_iso/$ISO_PATH/" -a "$ADD" -r "$REMOVE" -c "*.cfg" -u -h diff --git a/initrd/bin/kexec-parse-boot b/initrd/bin/kexec-parse-boot index bb52ab29..fe003ef9 100755 --- a/initrd/bin/kexec-parse-boot +++ b/initrd/bin/kexec-parse-boot @@ -1,6 +1,5 @@ #!/bin/sh -file=$1 -basefile=$(basename $file) +file="$1" reset_entry() { name="" @@ -21,7 +20,7 @@ echo_entry() { echo $(eval "echo \"$entry\"") fi - if [ "$kexectype" = "multiboot" ]; then + if [ "$kexectype" = "multiboot" -o "$kexectype" = "xen" ]; then if [ -z "$kernel" ]; then return; fi echo $(eval "echo \"$name|$kexectype|kernel $kernel$modules\"") @@ -39,6 +38,7 @@ search_entry() { label* | LABEL* ) state="syslinux" reset_entry + name=`echo $line | cut -c6- ` esac } @@ -55,7 +55,8 @@ grub_entry() { val=`echo $trimcmd | cut -d\ -f2-` case $cmd in multiboot*) - kexectype="multiboot" + # TODO: differentiate between Xen and other multiboot kernels + kexectype="xen" kernel="$val" ;; module*) @@ -130,12 +131,13 @@ syslinux_entry() { menu* | MENU* ) cmd2=`echo $trimcmd | cut -d \ -f2` if [ "$cmd2" = "label" -o "$cmd2" = "LABEL" ]; then - name=`echo ${trimcmd:11} | tr -d '^'` + name=`echo $trimcmd | cut -c11- | tr -d '^'` fi ;; linux* | LINUX* | kernel* | KERNEL* ) case $val in - *mboot.c32) kexectype="multiboot" ;; + # TODO: differentiate between Xen and other multiboot kernels + *mboot.c32) kexectype="xen" ;; *.c32) # skip this entry state="search" @@ -148,7 +150,7 @@ syslinux_entry() { initrd="$val" ;; append* | APPEND* ) - if [ "$kexectype" = "multiboot" ]; then + if [ "$kexectype" = "multiboot" -o "$kexectype" = "xen" ]; then syslinux_multiboot_append else append="$val" diff --git a/initrd/bin/kexec-save-default b/initrd/bin/kexec-save-default new file mode 100755 index 00000000..0b0586c3 --- /dev/null +++ b/initrd/bin/kexec-save-default @@ -0,0 +1,48 @@ +#!/bin/sh +# Save these options to be the persistent default +. /etc/functions + +while getopts "b:d:p:e:i:" arg; do + case $arg in + b) bootdir="$OPTARG" ;; + d) paramsdev="$OPTARG" ;; + p) paramsdir="$OPTARG" ;; + e) entry="$OPTARG" ;; + i) index="$OPTARG" ;; + esac +done + +if [ -z "$bootdir" -o -z "$entry" -o -z "$index" ]; then + die "Usage: $0 -b /boot/ -e \"boot params|...\" -i 1 " +fi + +if [ -z "$paramsdev" ]; then + paramsdev="$bootdir" +fi + +if [ -z "$paramsdir" ]; then + paramsdir="$bootdir" +fi + +ENTRY_FILE="$paramsdir/kexec_default.$index.txt" +HASH_FILE="$paramsdir/kexec_default_hashes.txt" + +# try to switch to rw mode +mount -o rw,remount $paramsdev + +if [ ! -d $paramsdir ]; then + mkdir -p $paramsdir +fi +rm "$paramsdir/kexec_default.*.txt" 2>/dev/null +echo "$entry" > $ENTRY_FILE +cd $bootdir && kexec-boot -e "$entry" -d | xargs sha256sum > $HASH_FILE +if [ ! -r $ENTRY_FILE -o ! -r $HASH_FILE ]; then + die "Failed to write default config" +fi + +if ! kexec-sign-config $paramsdir; then + 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 391c5fed..6dfa2da8 100755 --- a/initrd/bin/kexec-select-boot +++ b/initrd/bin/kexec-select-boot @@ -1,14 +1,36 @@ #!/bin/sh +. /etc/config . /etc/functions -bootdir=$1 -add=$2 -remove=$3 +add="" +remove="" +config="*.cfg" +unique="n" +hashed="n" +while getopts "b:d:p:a:r:c:uh" 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" ;; + h) hashed="y" ;; + esac +done -MENU_NAME="kexec_menu.txt" -HASH_NAME="kexec_hashes.txt" -TMP_MENU_FILE=/tmp/kexec/$MENU_NAME -TMP_HASH_FILE=/tmp/kexec/$HASH_NAME +if [ -z "$bootdir" ]; then + die "Usage: $0 -b /boot/" +fi + +if [ -z "$paramsdev" ]; then + paramsdev="$bootdir" +fi + +if [ -z "$paramsdir" ]; then + paramsdir="$bootdir" +fi first_menu="y" get_menu_option() { @@ -49,7 +71,7 @@ confirm_menu_option() { read \ -n 1 \ - -p "Confirm selection by pressing 'y': " \ + -p "Confirm selection by pressing 'y', make default with 'd': " \ option_confirm echo } @@ -59,35 +81,112 @@ parse_option() { 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 - -# if no saved options, scan the boot directory and generate -if [ ! -r $TMP_MENU_FILE ]; then +scan_options() { echo "+++ Scanning for unsigned boot options" option_file="/tmp/kexec_options.txt" - for i in `find $bootdir -name "*.cfg"`; do + if [ -r $option_file ]; then rm $option_file; fi + for i in `find $bootdir -name "$config"`; 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 + if [ "$unique" = 'y' ]; then + sort $option_file | uniq > $TMP_MENU_FILE + else + cp $option_file $TMP_MENU_FILE + fi +} -option_confirm="" -while [ "$option_confirm" != "y" ] -do - get_menu_option - confirm_menu_option +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 + recovery "Boot entry has changed: expected $expectedoption, found $option" + 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" ; then + echo "+++ Verified default boot hashes " + else + recovery "$TMP_DEFAULT_HASH_FILE: default boot hash mismatch" + fi + + echo "+++ Executing default boot for $name:" + kexec-boot -b "$bootdir" -e "$option" -a "$add" -r "$remove" + recovery "Something failed" +} + +user_select() { + # No default expected boot parameters, ask user + + # Optionally enforce device 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 " + hashed='y' + else + recovery "$TMP_HASH_FILE: boot hash mismatch" + fi + fi + + option_confirm="" + while [ "$option_confirm" != "y" -a "$option_confirm" != "d" ] + do + get_menu_option + confirm_menu_option + + if [ "$option_confirm" = 'd' ]; then + if ! kexec-save-default -b "$bootdir" -d "$paramsdev" -p "$paramsdir" -e "$option" -i "$option_index"; then + echo "!!!!!! Failed to save defaults" + else + echo "+++ Saved defaults to device" + sleep 2 + fi + fi + done + + if [ "$option_confirm" = "d" ]; then + # reload settings to reflect new default + continue + fi + + if [ "$CONFIG_BOOT_REQ_HASH" = "y" -a "$hashed" = "n" ]; then + recovery "!!!!!! Missing required boot hashes" + fi + + kexec-boot -b "$bootdir" -e "$option" -a "$add" -r "$remove" + recovery "Something failed" +} + +while true; do + kexec-check-config $paramsdir + TMP_MENU_FILE="/tmp/kexec/kexec_menu.txt" + TMP_HASH_FILE="/tmp/kexec/kexec_hashes.txt" + TMP_DEFAULT_FILE=`find /tmp/kexec/kexec_default.*.txt 2>/dev/null | head -1` + TMP_DEFAULT_HASH_FILE="/tmp/kexec/kexec_default_hashes.txt" + + # if no saved options, scan the boot directory and generate + if [ ! -r $TMP_MENU_FILE ]; then + scan_options + fi + + if [ -r "$TMP_DEFAULT_FILE" -a -r "$TMP_DEFAULT_HASH_FILE" ]; then + default_select + else + user_select + fi + + recovery "Something failed again" done - -kexec-boot -b $bootdir -e "$option" -a "$add" -r "$remove" diff --git a/initrd/bin/kexec-sign-config b/initrd/bin/kexec-sign-config index 3a7b6e61..d890a78a 100755 --- a/initrd/bin/kexec-sign-config +++ b/initrd/bin/kexec-sign-config @@ -8,27 +8,7 @@ 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" +confirm_gpg_card for tries in 1 2 3; do if sha256sum `find $MEDIA/kexec*.txt` | gpg \ diff --git a/initrd/bin/local-init b/initrd/bin/local-init index c410ecec..381e8ce6 100755 --- a/initrd/bin/local-init +++ b/initrd/bin/local-init @@ -23,7 +23,6 @@ if ! grep -q /boot /proc/mounts ; then fi # Attempt to pull verified config from device -kexec-check-config /boot/ -kexec-select-boot /boot/ +kexec-select-boot -b /boot/ -c "grub.cfg" recovery "Something failed..." diff --git a/initrd/bin/qubes-update b/initrd/bin/qubes-update index 0b259a0c..a16c37c7 100755 --- a/initrd/bin/qubes-update +++ b/initrd/bin/qubes-update @@ -12,27 +12,7 @@ if [ -z "$XEN" -o -z "$KERNEL" -o -z "$INITRD" ]; then die "Usage: $0 /boot/xen... /boot/vmlinuz... /boot/initramfs..." 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" +confirm_gpg_card # if the /boot.hashes file already exists, read the TPM counter ID # from it. diff --git a/initrd/bin/usb-scan b/initrd/bin/usb-scan index 5d3a2a03..2432d5dc 100755 --- a/initrd/bin/usb-scan +++ b/initrd/bin/usb-scan @@ -4,9 +4,17 @@ . /etc/functions . /etc/config +# Unmount any previous boot device +if grep -q /boot /proc/mounts ; then + umount /boot \ + || recovery '$CONFIG_USB_BOOT_DEV: Unable to unmount /boot' +fi + # Mount the USB boot device -mount-usb "$CONFIG_USB_BOOT_DEV" \ - || recovery '$CONFIG_USB_BOOT_DEV: Unable to mount /media' +if ! grep -q /media /proc/mounts ; then + mount-usb "$CONFIG_USB_BOOT_DEV" \ + || recovery '$CONFIG_USB_BOOT_DEV: Unable to mount /media' +fi # Check for ISO first get_menu_option() { @@ -46,7 +54,6 @@ if [ `cat /tmp/iso_menu.txt | wc -l` -gt 0 ]; then if [ -n "$option" ]; then MOUNTED_ISO=$option ISO=${option:7} # remove /media/ to get device relative path - kexec-check-config /media/kexec_iso/$ISO/ kexec-iso-init $MOUNTED_ISO $ISO $CONFIG_USB_BOOT_DEV recovery "Something failed..." @@ -55,7 +62,6 @@ fi echo "!!! Could not find any ISO, trying bootable USB" # Attempt to pull verified config from device -kexec-check-config /media/ -kexec-select-boot /media/ +kexec-select-boot -b /media/ -c "*.cfg" -u recovery "Something failed..." diff --git a/initrd/etc/functions b/initrd/etc/functions index 73715ba5..72a7b3ad 100755 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -63,3 +63,28 @@ confirm_totp() # clean up with a newline echo } + +confirm_gpg_card() +{ + # 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" +}