Signed-off-by: Marc S. Weidner <msw@coresecret.dev>
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
#!/bin/bash
|
||||
# SPDX-Version: 3.0
|
||||
# SPDX-CreationInfo: 2026-06-04; WEIDNER, Marc S.; <msw@coresecret.dev>
|
||||
# SPDX-ExternalRef: GIT https://git.coresecret.dev/msw/CISS.debian.live.builder.git
|
||||
# SPDX-FileContributor: WEIDNER, Marc S.; Centurion Intelligence Consulting Agency
|
||||
# SPDX-FileCopyrightText: 2024-2026; WEIDNER, Marc S.; <msw@coresecret.dev>
|
||||
# SPDX-FileType: SOURCE
|
||||
# SPDX-License-Identifier: LicenseRef-CNCL-1.1 OR LicenseRef-CCLA-1.1
|
||||
# SPDX-LicenseComment: This file is part of the CISS.debian.installer.secure framework.
|
||||
# SPDX-PackageName: CISS.debian.live.builder
|
||||
# SPDX-Security-Contact: security@coresecret.eu
|
||||
set -Ceuo pipefail
|
||||
|
||||
declare TMP_DIR=""
|
||||
|
||||
cleanup() {
|
||||
declare build_dir="${VAR_HANDLER_BUILD_DIR:-${PWD}}"
|
||||
|
||||
if [[ -n "${TMP_DIR}" && -d "${TMP_DIR}" ]]; then
|
||||
case "${TMP_DIR}" in
|
||||
"${build_dir}/ciss.secureboot/"*)
|
||||
rm -rf -- "${TMP_DIR}"
|
||||
;;
|
||||
*)
|
||||
printf "\e[91m++++ ++++ ++++ ++++ ++++ ++++ ++ [FATAL] Refusing to clean unexpected temporary path: '%s'. \e[0m\n" "${TMP_DIR}" >&2
|
||||
return 42
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
die() {
|
||||
declare message="$1"
|
||||
printf "\e[91m++++ ++++ ++++ ++++ ++++ ++++ ++ [FATAL] %s \e[0m\n" "${message}" >&2
|
||||
exit 42
|
||||
}
|
||||
|
||||
require_command() {
|
||||
declare command_name="$1"
|
||||
|
||||
command -v "${command_name}" >/dev/null 2>&1 || die "Required command not found: '${command_name}'."
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
require_file() {
|
||||
declare file_path="$1"
|
||||
declare description="$2"
|
||||
|
||||
[[ -f "${file_path}" ]] || die "Missing ${description}: '${file_path}'."
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
select_signed_uki() {
|
||||
declare uki_dir="$1"
|
||||
declare output_var="$2"
|
||||
declare -a signed_ukis=()
|
||||
|
||||
[[ -d "${uki_dir}" ]] || die "Missing CISS UKI output directory: '${uki_dir}'."
|
||||
|
||||
mapfile -d '' -t signed_ukis < <(find "${uki_dir}" -maxdepth 1 -type f -name "CISS-LIVE-*.signed.efi" -print0 | LC_ALL=C sort -z)
|
||||
|
||||
if (( ${#signed_ukis[@]} != 1 )); then
|
||||
die "Expected exactly one signed CISS UKI in '${uki_dir}', found '${#signed_ukis[@]}'."
|
||||
fi
|
||||
|
||||
printf -v "${output_var}" "%s" "${signed_ukis[0]}"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
guard_private_key_leaks() {
|
||||
declare -a guard_roots=(binary chroot config/includes.binary config/includes.chroot config/includes.installer)
|
||||
declare guard_root=""
|
||||
declare private_file=""
|
||||
|
||||
for guard_root in "${guard_roots[@]}"; do
|
||||
|
||||
if [[ ! -d "${guard_root}" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
while IFS= read -r -d '' private_file; do
|
||||
die "Refusing private Secure Boot key inside build artifact path: '${private_file}'."
|
||||
done < <(find "${guard_root}" -xdev -type f \( -name "ciss-efi-image.key" -o -name "ciss-module-signing.key" \) -print0)
|
||||
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
install_iso_tree_bootx64() {
|
||||
declare signed_uki="$1"
|
||||
declare output_var="$2"
|
||||
declare iso_tree_bootx64=""
|
||||
|
||||
if [[ -d "binary/EFI/boot" ]]; then
|
||||
iso_tree_bootx64="binary/EFI/boot/bootx64.efi"
|
||||
elif [[ -d "binary/EFI/BOOT" ]]; then
|
||||
iso_tree_bootx64="binary/EFI/BOOT/BOOTX64.EFI"
|
||||
elif [[ -d "binary/EFI" ]]; then
|
||||
install -d -m 0755 "binary/EFI/BOOT"
|
||||
iso_tree_bootx64="binary/EFI/BOOT/BOOTX64.EFI"
|
||||
fi
|
||||
|
||||
if [[ -n "${iso_tree_bootx64}" ]]; then
|
||||
install -m 0644 "${signed_uki}" "${iso_tree_bootx64}"
|
||||
printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] Mirrored signed UKI into ISO EFI tree: '%s'. \e[0m\n" "${iso_tree_bootx64}"
|
||||
else
|
||||
printf "\e[93m++++ ++++ ++++ ++++ ++++ ++++ ++ [WARN] No binary/EFI tree found; only EFI boot image was updated. \e[0m\n"
|
||||
fi
|
||||
|
||||
printf -v "${output_var}" "%s" "${iso_tree_bootx64}"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
main() {
|
||||
declare profile="${VAR_CISS_SECUREBOOT_PROFILE:-debian-shim}"
|
||||
declare build_dir="${VAR_HANDLER_BUILD_DIR:-${PWD}}"
|
||||
declare secureboot_dir="${VAR_CISS_SECUREBOOT_DIR:-${VAR_WORKDIR:-${build_dir}}/ciss.secureboot}"
|
||||
declare secureboot_cert="${VAR_CISS_SECUREBOOT_EFI_CERT:-${secureboot_dir}/public/ciss-efi-image.crt}"
|
||||
declare output_root=""
|
||||
declare uki_dir=""
|
||||
declare manifest_dir=""
|
||||
declare signed_uki=""
|
||||
declare efi_img="binary/boot/grub/efi.img"
|
||||
declare uki_name=""
|
||||
declare kernel_version=""
|
||||
declare manifest=""
|
||||
declare tmp_img=""
|
||||
declare extracted_uki=""
|
||||
declare iso_tree_bootx64=""
|
||||
declare uki_size=""
|
||||
declare -i uki_kib=0
|
||||
declare -i blocks=0
|
||||
declare source_epoch="${SOURCE_DATE_EPOCH:-0}"
|
||||
declare volid=""
|
||||
|
||||
if [[ "${profile}" != "ciss-uki" ]]; then
|
||||
printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] Secure Boot profile '%s'; skipping CISS UKI EFI installation. \e[0m\n" "${profile}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf "\e[95m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] Installing CISS signed UKI into EFI boot image ... \e[0m\n"
|
||||
|
||||
cd "${build_dir}"
|
||||
|
||||
require_command cmp
|
||||
require_command mcopy
|
||||
require_command mdir
|
||||
require_command mkfs.msdos
|
||||
require_command sbverify
|
||||
require_command sha512sum
|
||||
require_command stat
|
||||
require_command ukify
|
||||
require_file "${secureboot_cert}" "CISS EFI image signing certificate"
|
||||
require_file "${efi_img}" "live-build EFI boot image"
|
||||
guard_private_key_leaks
|
||||
|
||||
output_root="${build_dir}/ciss.secureboot"
|
||||
uki_dir="${output_root}/uki"
|
||||
manifest_dir="${output_root}/manifests"
|
||||
select_signed_uki "${uki_dir}" signed_uki
|
||||
|
||||
uki_name="${signed_uki##*/}"
|
||||
kernel_version="${uki_name#CISS-LIVE-}"
|
||||
kernel_version="${kernel_version%.signed.efi}"
|
||||
[[ -n "${kernel_version}" && "${kernel_version}" != "${uki_name}" ]] || die "Signed UKI name does not match CISS-LIVE-<version>.signed.efi: '${signed_uki}'."
|
||||
|
||||
install -d -m 0755 "${manifest_dir}"
|
||||
TMP_DIR="$(mktemp -d -p "${output_root}" "efi-img.XXXXXXXX")"
|
||||
tmp_img="${TMP_DIR}/efi.img"
|
||||
extracted_uki="${TMP_DIR}/BOOTX64.EFI"
|
||||
manifest="${manifest_dir}/CISS-LIVE-${kernel_version}.efi-install.txt"
|
||||
rm -f -- "${manifest}"
|
||||
|
||||
uki_size="$(stat -c %s -- "${signed_uki}")"
|
||||
uki_kib=$(( (uki_size + 1023) / 1024 ))
|
||||
blocks=$(( (uki_kib + 8192 + 31) / 32 * 32 ))
|
||||
if (( blocks < 32768 )); then
|
||||
blocks=32768
|
||||
fi
|
||||
|
||||
if [[ ! "${source_epoch}" =~ ^[0-9]+$ ]]; then
|
||||
source_epoch="0"
|
||||
fi
|
||||
printf -v volid "%08x" "$((source_epoch % 4294967296))"
|
||||
|
||||
printf "\e[95m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] Rebuilding EFI boot image with signed UKI as EFI/BOOT/BOOTX64.EFI. \e[0m\n"
|
||||
mkfs.msdos -C "${tmp_img}" "${blocks}" -i "${volid}" >/dev/null
|
||||
mmd -i "${tmp_img}" "::EFI"
|
||||
mmd -i "${tmp_img}" "::EFI/BOOT"
|
||||
mcopy -m -o -i "${tmp_img}" "${signed_uki}" "::EFI/BOOT/BOOTX64.EFI"
|
||||
mcopy -o -i "${tmp_img}" "::EFI/BOOT/BOOTX64.EFI" "${extracted_uki}"
|
||||
|
||||
cmp -s "${signed_uki}" "${extracted_uki}" || die "Extracted BOOTX64.EFI differs from signed UKI before EFI image installation."
|
||||
sbverify --cert "${secureboot_cert}" "${extracted_uki}" >/dev/null
|
||||
|
||||
install -m 0644 "${tmp_img}" "${efi_img}"
|
||||
|
||||
rm -f -- "${extracted_uki}"
|
||||
mcopy -o -i "${efi_img}" "::EFI/BOOT/BOOTX64.EFI" "${extracted_uki}"
|
||||
cmp -s "${signed_uki}" "${extracted_uki}" || die "Installed EFI/BOOT/BOOTX64.EFI differs from signed UKI."
|
||||
sbverify --cert "${secureboot_cert}" "${extracted_uki}" >/dev/null
|
||||
|
||||
install_iso_tree_bootx64 "${signed_uki}" iso_tree_bootx64
|
||||
if [[ -n "${iso_tree_bootx64}" ]]; then
|
||||
cmp -s "${signed_uki}" "${iso_tree_bootx64}" || die "ISO EFI tree BOOTX64.EFI differs from signed UKI."
|
||||
sbverify --cert "${secureboot_cert}" "${iso_tree_bootx64}" >/dev/null
|
||||
fi
|
||||
|
||||
guard_private_key_leaks
|
||||
|
||||
{
|
||||
printf "CISS Secure Boot EFI image installation manifest\n"
|
||||
printf "EFI image: %s\n" "${efi_img}"
|
||||
printf "Installed path: EFI/BOOT/BOOTX64.EFI\n"
|
||||
printf "ISO EFI tree mirror: %s\n" "${iso_tree_bootx64:-none}"
|
||||
printf "Signed UKI: %s\n" "${signed_uki}"
|
||||
printf "FAT image blocks KiB: %s\n" "${blocks}"
|
||||
printf "FAT volume id: %s\n" "${volid}"
|
||||
printf "\nSHA512:\n"
|
||||
sha512sum "${efi_img}" "${signed_uki}" "${extracted_uki}"
|
||||
if [[ -n "${iso_tree_bootx64}" ]]; then
|
||||
sha512sum "${iso_tree_bootx64}"
|
||||
fi
|
||||
printf "\nEFI directory:\n"
|
||||
mdir -i "${efi_img}" "::EFI/BOOT"
|
||||
printf "\nukify inspect installed BOOTX64.EFI:\n"
|
||||
ukify inspect "${extracted_uki}"
|
||||
printf "\nsbverify installed BOOTX64.EFI:\n"
|
||||
sbverify --cert "${secureboot_cert}" "${extracted_uki}"
|
||||
} >| "${manifest}" 2>&1
|
||||
|
||||
printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] EFI image installation verification written to '%s'. \e[0m\n" "${manifest}"
|
||||
printf "\e[92m++++ ++++ ++++ ++++ ++++ ++++ ++ [INFO] CISS signed UKI installed as EFI/BOOT/BOOTX64.EFI. \e[0m\n"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
main "$@"
|
||||
exit 0
|
||||
# vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh
|
||||
Reference in New Issue
Block a user