mirror of
https://github.com/linuxboot/heads.git
synced 2024-12-18 20:47:55 +00:00
Merge pull request #1586 from JonathonHall-Purism/root-file-hash-qubes
Root file hashing: support Qubes default partition layout (+ tracing helpers)
This commit is contained in:
commit
5a75e6bffa
@ -39,7 +39,7 @@ while true; do
|
||||
'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'
|
||||
'B' " $(get_config_display_action "$CONFIG_ROOT_CHECK_AT_BOOT") root check at boot"
|
||||
'L' " $(get_config_display_action "$CONFIG_RESTRICTED_BOOT") Restricted Boot"
|
||||
)
|
||||
|
||||
@ -265,10 +265,9 @@ while true; do
|
||||
--msgbox "The root directories to hash was successfully changed to:\n$NEW_CONFIG_ROOT_DIRLIST" 0 80
|
||||
;;
|
||||
"B" )
|
||||
CURRENT_OPTION="$(load_config_value CONFIG_ROOT_CHECK_AT_BOOT)"
|
||||
if [ "$CURRENT_OPTION" != "y" ]; then
|
||||
if [ "$CONFIG_ROOT_CHECK_AT_BOOT" != "y" ]; then
|
||||
# Root device and directories must be set to enable this
|
||||
if [ -z "$(load_config_value CONFIG_ROOT_DEV)" ] || [ -z "$(load_config_value CONFIG_ROOT_DIRLIST)" ]; then
|
||||
if [ -z "$CONFIG_ROOT_DEV" ] || [ -z "$CONFIG_ROOT_DIRLIST" ]; then
|
||||
whiptail $BG_COLOR_ERROR --title 'Root Check Not Configured' \
|
||||
--msgbox "Set the root device and directories to hash before enabling this feature." 0 80
|
||||
elif (whiptail --title 'Enable Root Hash Check at Boot?' \
|
||||
@ -277,8 +276,7 @@ while true; do
|
||||
\na minute or more to the boot time.
|
||||
\n\nDo you want to proceed?" 0 80) then
|
||||
|
||||
set_config /etc/config.user "CONFIG_ROOT_CHECK_AT_BOOT" "y"
|
||||
combine_configs
|
||||
set_user_config "CONFIG_ROOT_CHECK_AT_BOOT" "y"
|
||||
|
||||
# check that root hash file exists
|
||||
if [ ! -f ${ROOT_HASH_FILE} ]; then
|
||||
@ -298,8 +296,7 @@ while true; do
|
||||
--yesno "This will disable checking root hashes each time you boot.
|
||||
\n\nDo you want to proceed?" 0 80) then
|
||||
|
||||
set_config /etc/config.user "CONFIG_ROOT_CHECK_AT_BOOT" "n"
|
||||
combine_configs
|
||||
set_user_config "CONFIG_ROOT_CHECK_AT_BOOT" "n"
|
||||
|
||||
whiptail --title 'Config change successful' \
|
||||
--msgbox "The root device will not be checked at each boot." 0 80
|
||||
|
@ -32,7 +32,9 @@ update_root_checksums() {
|
||||
fi
|
||||
|
||||
echo "+++ Calculating hashes for all files in $CONFIG_ROOT_DIRLIST_PRETTY "
|
||||
cd $ROOT_MOUNT && find ${CONFIG_ROOT_DIRLIST} -type f ! -name '*kexec*' -print0 | xargs -0 sha256sum | tee ${HASH_FILE}
|
||||
# Intentional wordsplit
|
||||
# shellcheck disable=SC2086
|
||||
(cd "$ROOT_MOUNT" && find ${CONFIG_ROOT_DIRLIST} -type f ! -name '*kexec*' -print0 | xargs -0 sha256sum) >"${HASH_FILE}"
|
||||
|
||||
# switch back to ro mode
|
||||
mount -o ro,remount /boot
|
||||
@ -86,7 +88,7 @@ check_root_checksums() {
|
||||
fi
|
||||
|
||||
echo "+++ Checking for new files in $CONFIG_ROOT_DIRLIST_PRETTY "
|
||||
find ${CONFIG_ROOT_DIRLIST} -type f ! -name '*kexec*' | sort > /tmp/new_file_list
|
||||
(cd "$ROOT_MOUNT" && find ${CONFIG_ROOT_DIRLIST} -type f ! -name '*kexec*') | sort > /tmp/new_file_list
|
||||
cut -d' ' -f3- ${HASH_FILE} | sort | diff -U0 - /tmp/new_file_list > /tmp/new_file_diff || new_files_found=y
|
||||
if [ "$new_files_found" == "y" ]; then
|
||||
grep -E -v '^[+-]{3}|[@]{2} ' /tmp/new_file_diff > /tmp/new_file_diff2 # strip any output that's not a file
|
||||
@ -102,7 +104,7 @@ check_root_checksums() {
|
||||
fi
|
||||
|
||||
echo "+++ Checking hashes for all files in $CONFIG_ROOT_DIRLIST_PRETTY (this might take a while) "
|
||||
if cd $ROOT_MOUNT && sha256sum -c ${HASH_FILE} > /tmp/hash_output 2>/dev/null; then
|
||||
if (cd $ROOT_MOUNT && sha256sum -c ${HASH_FILE} > /tmp/hash_output 2>/dev/null); then
|
||||
echo "+++ Verified root hashes "
|
||||
valid_hash='y'
|
||||
unmount_root_device
|
||||
@ -156,37 +158,242 @@ check_root_checksums() {
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if a device is an LVM2 PV, and if so print the VG name
|
||||
find_lvm_vg_name() {
|
||||
TRACE_FUNC
|
||||
local DEVICE VG
|
||||
DEVICE="$1"
|
||||
|
||||
mkdir -p /tmp/root-hashes-gui
|
||||
if ! lvm pvs "$DEVICE" >/tmp/root-hashes-gui/lvm_vg 2>/dev/null; then
|
||||
# It's not an LVM PV
|
||||
return 1
|
||||
fi
|
||||
|
||||
VG="$(tail -n +2 /tmp/root-hashes-gui/lvm_vg | awk '{print $2}')"
|
||||
if [ -z "$VG" ]; then
|
||||
DEBUG "Could not find LVM2 VG from lvm pvs output:"
|
||||
DEBUG "$(cat /tmp/root-hashes-gui/lvm_vg)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "$VG"
|
||||
}
|
||||
|
||||
# Open an LVM volume group, then continue looking for more layers in the 'root'
|
||||
# logical volume.
|
||||
open_block_device_lvm() {
|
||||
TRACE_FUNC
|
||||
local VG="$1"
|
||||
|
||||
if ! lvm vgchange -ay "$VG"; then
|
||||
DEBUG "Can't open LVM VG: $VG"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Use the LV 'root'. This is the default name used by Qubes. There's no
|
||||
# way to configure this at the moment.
|
||||
if ! [ -e "/dev/mapper/$VG-root" ]; then
|
||||
DEBUG "LVM volume group does not have 'root' logical volume"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Use the root LV now
|
||||
open_block_device_layers "/dev/mapper/$VG-root"
|
||||
}
|
||||
|
||||
# Open a LUKS device, then continue looking for more layers.
|
||||
open_block_device_luks() {
|
||||
TRACE_FUNC
|
||||
local DEVICE="$1"
|
||||
local LUKSDEV
|
||||
LUKSDEV="$(basename "$DEVICE")_crypt"
|
||||
|
||||
# Open the LUKS device. This may prompt interactively for the passphrase, so
|
||||
# hook it up to the console even if stdout/stdin have been redirected.
|
||||
if ! cryptsetup open "$DEVICE" "$LUKSDEV"; then
|
||||
DEBUG "Can't open LUKS volume: $DEVICE"
|
||||
return 1
|
||||
fi
|
||||
|
||||
open_block_device_layers "/dev/mapper/$LUKSDEV"
|
||||
}
|
||||
|
||||
# Open block device layers to access /root recursively. If another layer (LUKS
|
||||
# or LVM) can be identified, open it and recurse into the new device. When all
|
||||
# recognized layers are opened, print the final block device and exit
|
||||
# successfully (open_root_device will try to mount it).
|
||||
#
|
||||
# This only fails if we can recognize another LUKS or LVM layer, but cannot open
|
||||
# it. It succeeds otherwise, even if no layers are recognized, because we
|
||||
# should try to mount the block device directly in that case.
|
||||
open_block_device_layers() {
|
||||
TRACE_FUNC
|
||||
local DEVICE="$1"
|
||||
local VG
|
||||
|
||||
if ! [ -e "$DEVICE" ]; then
|
||||
DEBUG "Block device doesn't exit: $DEVICE"
|
||||
# This shouldn't really happen, we thought we opened the last layer
|
||||
# successfully. The call stack reveals what LUKS/LVM2 layers have been
|
||||
# opened so far.
|
||||
DEBUG_STACK
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Try to open a LUKS layer
|
||||
if cryptsetup isLuks "$DEVICE" &>/dev/null; then
|
||||
open_block_device_luks "$DEVICE" || return 1
|
||||
# Try to open an LVM layer
|
||||
elif VG="$(find_lvm_vg_name "$DEVICE")"; then
|
||||
open_block_device_lvm "$VG" || return 1
|
||||
else
|
||||
# The given block device exists but is not any layer we understand. Stop
|
||||
# opening layers and try to mount it.
|
||||
echo "$DEVICE"
|
||||
fi
|
||||
}
|
||||
|
||||
# Try to open a block device as /root. open_block_device_layers() is used to
|
||||
# open LUKS and LVM layers before mounting the filesystem.
|
||||
#
|
||||
# This function does not clean up anything if it is unsuccessful. Use
|
||||
# try_open_root_device() to also clean up when unsuccessful.
|
||||
open_root_device_no_clean_up() {
|
||||
TRACE_FUNC
|
||||
local DEVICE="$1"
|
||||
local FS_DEVICE
|
||||
|
||||
# Open LUKS/LVM and get the name of the block device that should contain the
|
||||
# filesystem. If there are no LUKS/LVM layers, FS_DEVICE is just DEVICE.
|
||||
FS_DEVICE="$(open_block_device_layers "$DEVICE")" || return 1
|
||||
|
||||
# Mount the device
|
||||
if ! mount -o ro "$FS_DEVICE" "$ROOT_MOUNT" &>/dev/null; then
|
||||
DEBUG "Can't mount filesystem on $FS_DEVICE from $DEVICE"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# The filesystem must have all of the directories configured. (Intentional
|
||||
# word-split)
|
||||
# shellcheck disable=SC2086
|
||||
if ! (cd "$ROOT_MOUNT" && ls -d $CONFIG_ROOT_DIRLIST &>/dev/null); then
|
||||
DEBUG "Root filesystem on $DEVICE lacks one of the configured directories: $CONFIG_ROOT_DIRLIST"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Root is mounted now and the directories are present
|
||||
return 0
|
||||
}
|
||||
|
||||
# If an LVM VG is open, close any layers within it, then close the LVM VG.
|
||||
close_block_device_lvm() {
|
||||
TRACE_FUNC
|
||||
local VG="$1"
|
||||
|
||||
# We always use the LV 'root' currently
|
||||
local LV="/dev/mapper/$VG-root"
|
||||
if [ -e "$LV" ]; then
|
||||
close_block_device_layers "$LV"
|
||||
fi
|
||||
|
||||
# The LVM VG might be open even if no 'root' LV exists, still try to close it.
|
||||
lvm vgchange -an "$VG" || \
|
||||
DEBUG "Can't close LVM VG: $VG"
|
||||
}
|
||||
|
||||
# If a LUKS device is open, close any layers within the LUKS device, then close
|
||||
# the LUKS device.
|
||||
close_block_device_luks() {
|
||||
TRACE_FUNC
|
||||
local DEVICE="$1"
|
||||
local LUKSDEV
|
||||
LUKSDEV="$(basename "$DEVICE")_crypt"
|
||||
|
||||
if [ -e "/dev/mapper/$LUKSDEV" ]; then
|
||||
# Close inner layers before trying to close LUKS
|
||||
close_block_device_layers "/dev/mapper/$LUKSDEV"
|
||||
cryptsetup close "$LUKSDEV" || \
|
||||
DEBUG "Can't close LUKS volume: $LUKSDEV"
|
||||
fi
|
||||
}
|
||||
|
||||
# Close the root device, including unmounting the filesystem and closing all
|
||||
# layers. This can close a partially-opened device if an error occurs.
|
||||
close_block_device_layers() {
|
||||
TRACE_FUNC
|
||||
local DEVICE="$1"
|
||||
local VG
|
||||
|
||||
if ! [ -e "$DEVICE" ]; then
|
||||
DEBUG "Block device doesn't exit: $DEVICE"
|
||||
# Like in open_root_device(), this shouldn't really happen, show the layers
|
||||
# up to this point via the call stack.
|
||||
DEBUG_STACK
|
||||
return 1
|
||||
fi
|
||||
|
||||
if cryptsetup isLuks "$DEVICE"; then
|
||||
close_block_device_luks "$DEVICE"
|
||||
elif VG="$(find_lvm_vg_name "$DEVICE")"; then
|
||||
close_block_device_lvm "$VG"
|
||||
fi
|
||||
# Otherwise, we've handled all the layers we understood, there's nothing left
|
||||
# to do.
|
||||
}
|
||||
|
||||
# Try to open the root device, and clean up if unsuccessful.
|
||||
open_root_device() {
|
||||
TRACE_FUNC
|
||||
if ! open_root_device_no_clean_up "$1"; then
|
||||
unmount_root_device
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Close the root device, including unmounting the filesystem and closing all
|
||||
# layers. This can close a partially-opened device if an error occurs. This
|
||||
# never fails, if an error occurs it still tries to close anything it can.
|
||||
close_root_device() {
|
||||
TRACE_FUNC
|
||||
local DEVICE="$1"
|
||||
|
||||
# Unmount the filesystem if it is mounted. If it is not mounted, ignore the
|
||||
# failure. If it is mounted but can't be unmounted, this will fail and we
|
||||
# will fail to close any LUKS/LVM layers too.
|
||||
umount "$ROOT_MOUNT" &>/dev/null || true
|
||||
|
||||
close_block_device_layers "$DEVICE" || true
|
||||
}
|
||||
|
||||
# detect and set /root device
|
||||
# mount /root if successful
|
||||
detect_root_device()
|
||||
{
|
||||
TRACE_FUNC
|
||||
|
||||
echo "+++ Detecting root device "
|
||||
|
||||
if [ ! -e $ROOT_MOUNT ]; then
|
||||
mkdir -p $ROOT_MOUNT
|
||||
fi
|
||||
# unmount $ROOT_MOUNT to be safe
|
||||
cd / && umount $ROOT_MOUNT 2>/dev/null
|
||||
# Ensure nothing is opened/mounted
|
||||
unmount_root_device
|
||||
|
||||
# check $CONFIG_ROOT_DEV if set/valid
|
||||
if [ -e "$CONFIG_ROOT_DEV" ]; then
|
||||
if cryptsetup isLuks $CONFIG_ROOT_DEV >/dev/null 2>&1; then
|
||||
if cryptsetup open $CONFIG_ROOT_DEV rootdisk; then
|
||||
if mount -o ro /dev/mapper/rootdisk $ROOT_MOUNT >/dev/null 2>&1; then
|
||||
if cd $ROOT_MOUNT && ls -d $CONFIG_ROOT_DIRLIST >/dev/null 2>&1; then # CONFIG_ROOT_DEV is valid device and contains an installed OS
|
||||
if [ -e "$CONFIG_ROOT_DEV" ] && open_root_device "$CONFIG_ROOT_DEV"; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# generate list of possible boot devices
|
||||
fdisk -l | grep "Disk /dev/" | cut -f2 -d " " | cut -f1 -d ":" > /tmp/disklist
|
||||
|
||||
# filter out extraneous options
|
||||
> /tmp_root_device_list
|
||||
for i in `cat /tmp/disklist`; do
|
||||
while IFS= read -r -u 10 i; do
|
||||
# remove block device from list if numeric partitions exist
|
||||
DEV_NUM_PARTITIONS=$((`ls -1 $i* | wc -l`-1))
|
||||
if [ ${DEV_NUM_PARTITIONS} -eq 0 ]; then
|
||||
@ -194,33 +401,25 @@ detect_root_device()
|
||||
else
|
||||
ls $i* | tail -${DEV_NUM_PARTITIONS} >> /tmp_root_device_list
|
||||
fi
|
||||
done
|
||||
done 10</tmp/disklist
|
||||
|
||||
# iterate thru possible options and check for LUKS
|
||||
for i in `cat /tmp_root_device_list`; do
|
||||
if cryptsetup isLuks $i >/dev/null 2>&1; then
|
||||
if cryptsetup open $i rootdisk; then
|
||||
if mount -o ro /dev/mapper/rootdisk $ROOT_MOUNT >/dev/null 2>&1; then
|
||||
if cd $ROOT_MOUNT && ls -d $CONFIG_ROOT_DIRLIST >/dev/null 2>&1; then
|
||||
# iterate through possible options
|
||||
while IFS= read -r -u 10 i; do
|
||||
if open_root_device "$i"; then
|
||||
# CONFIG_ROOT_DEV is valid device and contains an installed OS
|
||||
CONFIG_ROOT_DEV="$i"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done 10</tmp_root_device_list
|
||||
|
||||
# no valid root device found
|
||||
echo "Unable to locate $ROOT_MOUNT files on any mounted disk"
|
||||
unmount_root_device
|
||||
return 1
|
||||
}
|
||||
|
||||
unmount_root_device()
|
||||
{
|
||||
cd /
|
||||
umount $ROOT_MOUNT 2>/dev/null
|
||||
cryptsetup close rootdisk
|
||||
[ -e "$CONFIG_ROOT_DEV" ] && close_root_device "$CONFIG_ROOT_DEV"
|
||||
}
|
||||
|
||||
checkonly="n"
|
||||
|
@ -33,6 +33,28 @@ DO_WITH_DEBUG() {
|
||||
"$@"
|
||||
}
|
||||
|
||||
# Trace the current script and function.
|
||||
TRACE_FUNC() {
|
||||
# Index [1] for BASH_SOURCE and FUNCNAME give us the caller location.
|
||||
# FUNCNAME is 'main' if called from a script outside any function.
|
||||
# BASH_LINENO is offset by 1, it provides the line that the
|
||||
# corresponding FUNCNAME was _called from_, so BASH_LINENO[0] is the
|
||||
# location of the caller.
|
||||
TRACE "${BASH_SOURCE[1]}(${BASH_LINENO[0]}): ${FUNCNAME[1]}"
|
||||
}
|
||||
|
||||
# Show the entire current call stack in debug output - useful if a catastrophic
|
||||
# error or something very unexpected occurs, like totally invalid parameters.
|
||||
DEBUG_STACK() {
|
||||
local FRAMES
|
||||
FRAMES="${#FUNCNAME[@]}"
|
||||
DEBUG "call stack: ($((FRAMES-1)) frames)"
|
||||
# Don't print DEBUG_STACK itself, start from 1
|
||||
for i in $(seq 1 "$((FRAMES-1))"); do
|
||||
DEBUG "- $((i-1)) - ${BASH_SOURCE[$i]}(${BASH_LINENO[$((i-1))]}): ${FUNCNAME[$i]}"
|
||||
done
|
||||
}
|
||||
|
||||
pcrs() {
|
||||
if [ "$CONFIG_TPM2_TOOLS" = "y" ]; then
|
||||
tpm2 pcrread sha256
|
||||
|
@ -7,6 +7,9 @@ bash_tar := bash-$(bash_version).tar.gz
|
||||
bash_url := https://ftpmirror.gnu.org/bash/$(bash_tar)
|
||||
bash_hash := 5bac17218d3911834520dad13cd1f85ab944e1c09ae1aba55906be1f8192f558
|
||||
|
||||
# --enable-debugger: Enables BASH_SOURCE tracing through functions as well as
|
||||
# BASH_ARGV/BASH_ARGC. (Otherwise BASH_SOURCE[0] is empty when calling a
|
||||
# function, it's only set in top level script code.)
|
||||
bash_configure := CFLAGS="-g0 -Os" LDFLAGS="-s" ./configure \
|
||||
$(CROSS_TOOLS) \
|
||||
--host $(target) \
|
||||
@ -16,11 +19,11 @@ bash_configure := CFLAGS="-g0 -Os" LDFLAGS="-s" ./configure \
|
||||
--mandir=/usr/share/man \
|
||||
--without-bash-malloc \
|
||||
--disable-coprocesses \
|
||||
--disable-debugger \
|
||||
--enable-debugger \
|
||||
--disable-net-redirections \
|
||||
--enable-single-help-strings \
|
||||
--disable-nls \
|
||||
--disable-readline \
|
||||
--enable-readline \
|
||||
|
||||
bash_target := $(MAKE_JOBS) \
|
||||
&& $(MAKE) -C $(build)/$(bash_dir) \
|
||||
|
@ -33,6 +33,7 @@ lvm2_configure := \
|
||||
--disable-cache_check_needs_check \
|
||||
--disable-thin_check_needs_check \
|
||||
--with-cluster=none \
|
||||
--with-thin-check= \
|
||||
|
||||
# not sure why LIB_SUFFIX is not defined in the cross build
|
||||
lvm2_target := \
|
||||
|
Loading…
Reference in New Issue
Block a user