Add GUI package update handler w/ checksum update function

Part of the Heads workflow involves handling legitimate changes to /boot
as part of the package manager. This is a challenging workflow to handle
as package managers on many systems work in a completely unattended way
(and some even reboot first, apply updates, and then reboot again).

We need to be able to detect changes that are potentially caused by a
package manager so to do that I've set up a trigger within the OS
(currently just for Debian) that runs both before and after package
updates. It verifies the signatures in /boot and if they fail before
package updates it creates a log file in
/boot/kexec_package_trigger_pre.txt. If they fail after package updates
run /boot/kexec_package_trigger_post.txt is created. These files contain
the following fields:

CHANGED_FILES: A list of files in /boot that failed the sha256sum check
UPDATE_INITRAMFS_PACKAGE: An (optional) list of packages known to
trigger initramfs changes

Following those fields is a list of log output from the last package
manager run which contains its own formatted fields (I'm pulling from
/var/lib/dpkg/info).

When a user selects a boot option, gui-init first verifies the
checksums just to catch errors before calling kexec-select-boot. If
there are any errors it looks for these package logs and if they exist,
it displays appropriate warnings. If the files are absent it displays a
more generic warning. The user is also given an opportunity to re-sign
the /boot hashes.
This commit is contained in:
Kyle Rankin 2018-04-03 15:20:34 -07:00
parent f60f0543d6
commit 887c79065e
No known key found for this signature in database
GPG Key ID: 555577116BFA74B9

View File

@ -14,7 +14,81 @@ mount_boot()
|| recovery "Unable to mount /boot"
fi
}
verify_global_hashes()
{
# Check the hashes of all the files, ignoring signatures for now
check_config /boot force
TMP_HASH_FILE="/tmp/kexec/kexec_hashes.txt"
TMP_PACKAGE_TRIGGER_PRE="/tmp/kexec/kexec_package_trigger_pre.txt"
TMP_PACKAGE_TRIGGER_POST="/tmp/kexec/kexec_package_trigger_post.txt"
if cd /boot && sha256sum -c "$TMP_HASH_FILE" > /tmp/hash_output ; then
return 0
elif [ ! -f $TMP_HASH_FILE ]; then
if (whiptail --clear --title 'ERROR: Missing Hash File!' \
--yesno "The file containing hashes for /boot is missing!\n\nIf you are setting this system up for the first time, select Yes to update your list of checksums.\n\nOtherwise this could indicate a compromise and you should select No to return to the main menu.\n\nWould you like to update your checksums now?" 30 80) then
update_checksums
fi
return 1
else
CHANGED_FILES=$(grep -v 'OK$' /tmp/hash_output | cut -f1 -d ':')
# if files changed before package manager started, show stern warning
if [ -f "$TMP_PACKAGE_TRIGGER_PRE" ]; then
PRE_CHANGED_FILES=$(grep '^CHANGED_FILES' $TMP_PACKAGE_TRIGGER_POST | cut -f 2 -d '=' | tr -d '"')
TEXT="The OS logged that the following files failed the verification process BEFORE package updates ran:\n${PRE_CHANGED_FILES}\n\nCompare against the files Heads has detected have changed:\n${CHANGED_FILES}\n\nThis could indicate a compromise!\n\nWould you like to update your checksums anyway?"
# if files changed after package manager started, probably caused by package manager
elif [ -f "$TMP_PACKAGE_TRIGGER_POST" ]; then
LAST_PACKAGE_LIST=$(grep -E "^(Install|Remove|Upgrade|Reinstall):" $TMP_PACKAGE_TRIGGER_POST)
UPDATE_INITRAMFS_PACKAGE=$(grep '^UPDATE_INITRAMFS_PACKAGE' $TMP_PACKAGE_TRIGGER_POST | cut -f 2 -d '=' | tr -d '"')
if [ "$UPDATE_INITRAMFS_PACKAGE" != "" ]; then
TEXT="The following files failed the verification process AFTER package updates ran:\n${CHANGED_FILES}\n\nThis is likely due to package triggers in$UPDATE_INITRAMFS_PACKAGE.\n\nYou will need to update your checksums for all files in /boot.\n\nWould you like to update your checksums now?"
else
TEXT="The following files failed the verification process AFTER package updates ran:\n${CHANGED_FILES}\n\nThis might be due to the following package updates:\n$LAST_PACKAGE_LIST.\n\nYou will need to update your checksums for all files in /boot.\n\nWould you like to update your checksums now?"
fi
else
TEXT="The following files failed the verification process:\n${CHANGED_FILES}\n\nThis could indicate a compromise!\n\nWould you like to update your checksums now?"
fi
if (whiptail --clear --title 'ERROR: Boot Hash Mismatch' --yesno "$TEXT" 30 80) then
update_checksums
fi
return 1
fi
}
update_checksums()
{
if (whiptail --title 'Update Checksums and sign all files in /boot' \
--yesno "You have chosen to update the checksums and sign all of the files in /boot.\n\nThis means that you trust that the files in /boot have not been tampered with.\n\nYou will need your GPG key to continue and this change will modify your disk.\n\nDo you want to continue?" 16 80) then
mount_boot
mount -o rw,remount /boot
cd /boot
find ./ -type f ! -name '*kexec*' | xargs sha256sum > /boot/kexec_hashes.txt
DEFAULT_FILES=$(cat /boot/kexec_default_hashes.txt | cut -f3 -d ' ')
echo $DEFAULT_FILES | xargs sha256sum > /boot/kexec_default_hashes.txt
# Remove any package trigger log files
# We don't need them after the user decides to sign
rm -f /boot/kexec_package_trigger*
# sign and auto-roll config counter
extparam=
if [ "$CONFIG_TPM" = "y" ]; then
extparam=-u
fi
kexec-sign-config -p /boot $extparam \
|| die "Failed to sign default config"
# switch back to ro mode
mount -o ro,remount /boot
else
echo "Returning to the main menu"
fi
}
last_half=X
while true; do
@ -125,6 +199,13 @@ while true; do
if (whiptail --title 'Reset the TPM' \
--yesno "This will clear the TPM, erase the old TPM password and replace it with a new one!\n\nDo you want to proceed?" 16 80) then
/bin/tpm-reset
# now that the TPM is reset, remove invalid kexec_rollback.txt file
mount_boot
mount -o rw,remount /boot
rm -f /boot/kexec_rollback.txt
mount -o ro,remount /boot
echo "Scan the QR code to add the new TOTP secret"
/bin/seal-totp
echo "Once you have scanned the QR code, hit Enter to reboot"
@ -139,6 +220,10 @@ while true; do
if [ "$totp_confirm" = "m" ]; then
# Try to select a kernel from the menu
mount_boot
verify_global_hashes
if [ $? -ne 0 ]; then
continue
fi
kexec-select-boot -m -b /boot -c "grub.cfg" -g
continue
fi
@ -156,39 +241,18 @@ while true; do
fi
if [ "$totp_confirm" = "s" ]; then
if (whiptail --title 'Update Checksums and sign all files in /boot' \
--yesno "You have chosen to update the checksums and sign all of the files in /boot.\n\nThis means that you trust that the files in /boot have not been tampered with.\n\nYou will need your GPG key to continue and this change will modify your disk.\n\nDo you want to continue?" 16 80) then
mount_boot
mount -o rw,remount /boot
cd /boot
find ./ -type f ! -name '*kexec*' | xargs sha256sum > /boot/kexec_hashes.txt
DEFAULT_FILES=$(cat /boot/kexec_default_hashes.txt | cut -f3 -d ' ')
echo $DEFAULT_FILES | xargs sha256sum > /boot/kexec_default_hashes.txt
# sign and auto-roll config counter
extparam=
if [ "$CONFIG_TPM" = "y" ]; then
extparam=-u
fi
kexec-sign-config -p /boot $extparam
if [ $? -ne 0 ]; then
mount -o ro,remount /boot
whiptail --title 'ERROR: Signing Failed' \
--msgbox "The signing process failed!\n\nReturning to main menu." 16 60
else
# switch back to ro mode
mount -o ro,remount /boot
fi
fi
update_checksums
continue
fi
if [ "$totp_confirm" = "y" -o -n "$totp_confirm" ]; then
# Try to boot the default
mount_boot
DEFAULT_FILE=`find /boot/kexec_default.*.txt 2>/dev/null | head -1`
verify_global_hashes
if [ $? -ne 0 ]; then
continue
fi
DEFAULT_FILE=`find /boot/kexec_default.*.txt 2>/dev/null | head -1`
if [ -r "$DEFAULT_FILE" ]; then
kexec-select-boot -b /boot -c "grub.cfg" -g \
|| recovery "Failed default boot"