Files
omarchy/test/shell.d/usb-boot-enrollment-test.sh
T

184 lines
8.6 KiB
Bash
Executable File

#!/bin/bash
set -euo pipefail
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
source "$ROOT/bin/omarchy-usb-authorization-boot"
scratch=$(mktemp -d)
trap 'rm -rf "$scratch"' EXIT
usb_authorization_secure_boot_enabled() { return 0; }
usb_authorization_limine_settings() { printf 'x64 %s\n' "${ENROLLMENT:-yes}"; }
# Real PE enrollment always runs. An optional isolated sbctl configuration also
# exercises real signatures; default runs track signed content without host keys.
sbctl() {
if [[ $1 == sign && ${SIGN_FAIL:-0} == 1 ]]; then
return 1
fi
if [[ -n ${USB_TEST_SBCTL_CONFIG:-} ]]; then
/usr/bin/sbctl --config "$USB_TEST_SBCTL_CONFIG" "$@"
elif [[ $1 == sign ]]; then
sha256sum "${!#}" | cut -d ' ' -f1 >>"$scratch/signed"
elif [[ $1 == verify ]]; then
local digest signed=0
digest=$(sha256sum "${!#}")
if grep -qx "${digest%% *}" "$scratch/signed"; then signed=1; fi
jq -n --arg file "${!#}" --argjson signed "$signed" '[{file_name: $file, is_signed: $signed}]'
fi
}
fixture() {
case_dir=$(mktemp -d "$scratch/case.XXXXXXXX")
boot_path="$case_dir/boot"
cache="$case_dir/cache"
limine_conf="$boot_path/limine.conf"
limine_defaults="$case_dir/defaults"
drop_in="$case_dir/dropins/usb-authorization.conf"
image="$boot_path/EFI/limine/limine_x64.efi"
mkdir -p "${image%/*}" "$cache/lss" "${drop_in%/*}"
printf 'initial menu\n' >"$limine_conf"
printf 'initial kernel\n' >"$boot_path/kernel.efi"
printf 'initial snapshot\n' >"$cache/lss/snapshots.json"
printf 'initial defaults\n' >"$limine_defaults"
: >"$scratch/signed"
cp /usr/share/limine/BOOTX64.EFI "$image"
if [[ ${ENROLLMENT:-yes} == yes ]]; then
digest=$(b2sum "$limine_conf")
limine enroll-config "$image" "${digest%% *}" --quiet
fi
sbctl sign "$image" >/dev/null
cp "$image" "$case_dir/original.efi"
usb_authorization_verify_bootloader "$limine_conf" "$boot_path"
}
# Matches the installed wrapper: restore unsigned binary, enroll, attempt sign,
# then mask failure with a successful final sync. No real ESP is ever touched.
limine-enroll-config() {
cp /usr/share/limine/BOOTX64.EFI "$image"
if [[ ${ENROLLMENT:-yes} == yes && ${SKIP_ENROLLMENT:-0} != 1 ]]; then
local digest
digest=$(b2sum "$limine_conf")
limine enroll-config "$image" "${digest%% *}" --quiet || return 1
fi
sbctl sign "$image" || true
sync -f "$image"
}
# Relocate the outer real flock into scratch. The runner stands in for the
# namespace child and deliberately mutates the same files as rebuild hooks.
transaction=$(declare -f usb_authorization_boot_transaction)
transaction=${transaction//\/run\/lock\/boot-partition.lock/$scratch\/boot.lock}
eval "$transaction"
usb_authorization_run_transaction() {
if flock --nonblock "$scratch/boot.lock" true; then
fail "the real boot lock must cover the entire transaction"
fi
printf 'changed menu\n' >"$limine_conf"
printf 'changed kernel\n' >"$boot_path/kernel.efi"
printf 'changed metadata\n' >"$cache/lss/snapshots.json"
printf 'new cached backup\n' >"$cache/lss/snapshots.json.old"
mkdir -p "$boot_path/$(</etc/machine-id)/limine_history"
printf 'new archived backup\n' >"$boot_path/$(</etc/machine-id)/limine_history/snapshots.json.old"
printf 'changed defaults\n' >"$limine_defaults"
printf '%s\n' "$setting" >"$drop_in"
if [[ ${EARLY_FAILURE:-0} == 1 ]]; then
cp /usr/share/limine/BOOTX64.EFI "$image"
return 1
fi
if [[ ${UNCHECKED_HOOK:-0} == 1 ]]; then
limine-enroll-config
else
usb_authorization_enroll_config "$limine_conf" "$boot_path"
fi
}
for failure in SIGN_FAIL SKIP_ENROLLMENT EARLY_FAILURE; do
fixture
if (export "$failure=1"; usb_authorization_boot_transaction enable "$cache") >"$case_dir/failure.log" 2>&1; then
fail "$failure must reject the boot transition"
fi
cmp "$image" "$case_dir/original.efi" || fail "$failure must restore the signed bootloader byte-for-byte without signing"
[[ $(<"$limine_conf") == 'initial menu' ]] || fail "$failure must restore matching menu"
[[ $(<"$boot_path/kernel.efi") == 'initial kernel' ]] || fail "$failure must restore images referenced by that menu"
[[ $(<"$cache/lss/snapshots.json") == 'initial snapshot' ]] || fail "$failure must restore cached snapshot metadata"
[[ ! -e $cache/lss/snapshots.json.old && ! -e $boot_path/$(</etc/machine-id)/limine_history/snapshots.json.old ]] || fail "$failure must remove newly created recovery metadata"
[[ $(<"$limine_defaults") == 'initial defaults' && ! -e $drop_in ]] || fail "$failure must restore settings including an absent drop-in"
usb_authorization_verify_bootloader "$limine_conf" "$boot_path"
done
pass "USB boot transitions restore signed Limine, matching menu, images, metadata and settings on failure"
fixture
printf 'original opt-in\n' >"$drop_in"
if (SIGN_FAIL=1 UNCHECKED_HOOK=1 usb_authorization_boot_transaction disable "$cache") >"$case_dir/failure.log" 2>&1; then
fail "final verification must reject false-success rebuild hooks"
fi
cmp "$image" "$case_dir/original.efi" || fail "false-success hooks must restore original signed Limine"
[[ $(<"$drop_in") == 'original opt-in' ]] || fail "failed disable must preserve an existing drop-in"
pass "USB boot transitions independently verify enrollment after external hooks"
for enrollment in yes no; do
export ENROLLMENT="$enrollment"
fixture
usb_authorization_boot_transaction enable "$cache"
[[ $(<"$limine_conf") == 'changed menu' && -e $drop_in ]] || fail "successful transition must commit"
usb_authorization_verify_bootloader "$limine_conf" "$boot_path"
done
pass "USB boot transitions commit verified signing with enrollment enabled or disabled"
# Disposable Lab only: exercise the actual namespace runner and both lock
# inodes, with the installed enrollment/restore functions and real signatures.
if [[ ${USB_TEST_NAMESPACE:-0} == 1 ]]; then
(( EUID == 0 )) || fail "namespace tests require root in the disposable guest"
[[ -n ${USB_TEST_SBCTL_CONFIG:-} ]] || fail "namespace tests require isolated signing keys"
export ENROLLMENT=yes
for outcome in failure success; do
fixture
cp /usr/share/limine/BOOTX64.EFI "$case_dir/limine_x64.efi"
tar -cf "$case_dir/limine.bak" -C "$case_dir" limine_x64.efi
lock_inode=$(stat -Lc '%d:%i' /run/lock/boot-partition.lock)
# Source the shipped functions in both the parent and namespace child.
sed '/^if \[\[ ${BASH_SOURCE\[0\]} == "\$0" \]\]; then/,$d' "$ROOT/bin/omarchy-usb-authorization-boot" >"$case_dir/helper"
for variable in scratch case_dir boot_path cache limine_conf limine_defaults drop_in image lock_inode; do
printf '%s=%q\n' "$variable" "${!variable}" >>"$case_dir/helper"
done
declare -f sbctl usb_authorization_limine_settings usb_authorization_secure_boot_enabled >>"$case_dir/helper"
cat >>"$case_dir/helper" <<'SCRIPT'
usb_authorization_main() {
[[ $(stat -Lc '%d:%i' /run/lock/boot-partition.lock) != "$lock_inode" ]] || return 1
exec 9>/run/lock/boot-partition.lock
flock --nonblock 9 || return 1
printf 'changed menu\n' >"$limine_conf"
printf 'changed kernel\n' >"$boot_path/kernel.efi"
source /usr/lib/limine/limine-common-functions
LIMINE_DIR_PATH="${image%/*}"
LIMINE_EFI_FILE=limine_x64.efi
BINARY_TARGET_PATH="$image"
BINARY_BACKUP_PATH="$case_dir/limine.bak"
LIMINE_CONFIG_PATH="$limine_conf"
ENABLE_ENROLL_LIMINE_CONFIG=yes
is_sb_installed() { return 0; }
is_uefi() { return 0; }
limine-enroll-config() { enroll_config; }
usb_authorization_enroll_config "$limine_conf" "$boot_path"
}
if [[ ${BASH_SOURCE[0]} == "$0" ]]; then
usb_authorization_boot_transaction "$1" "$cache"
fi
SCRIPT
if [[ $outcome == failure ]]; then
if SIGN_FAIL=1 bash "$case_dir/helper" enable >"$case_dir/namespace.log" 2>&1; then
fail "actual namespace must reject installed wrapper's false success"
fi
grep -Fq 'Failed to sign' "$case_dir/namespace.log" || fail "failure must reach the installed enrollment function"
cmp "$image" "$case_dir/original.efi" || fail "namespace rollback restores original signed bootloader"
[[ $(<"$limine_conf") == 'initial menu' && $(<"$boot_path/kernel.efi") == 'initial kernel' ]] || fail "namespace rollback restores matching boot files"
else
bash "$case_dir/helper" disable >"$case_dir/namespace.log" 2>&1
[[ $(<"$limine_conf") == 'changed menu' ]] || fail "namespace success commits"
fi
usb_authorization_verify_bootloader "$limine_conf" "$boot_path"
flock --nonblock /run/lock/boot-partition.lock true || fail "outer lock must be released"
done
pass "USB real namespace isolates nested locks and recovers installed Limine signing failure"
fi