229 lines
7.5 KiB
Bash
Executable File
229 lines
7.5 KiB
Bash
Executable File
#!/bin/bash
|
|
# SPDX-Version: 3.0
|
|
# SPDX-FileCopyrightText: 2026; WEIDNER, Marc S.; <msw@coresecret.dev>
|
|
# SPDX-FileType: SOURCE
|
|
# SPDX-License-Identifier: LicenseRef-CNCL-1.1 OR LicenseRef-CCLA-1.1
|
|
# SPDX-PackageName: CISS.debian.live.builder
|
|
|
|
set -Ceuo pipefail
|
|
|
|
declare ROOT_DIR=""
|
|
declare SHA512SUM=""
|
|
declare SHASUM=""
|
|
declare TMP_BASE=""
|
|
declare TMP_ROOT=""
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
SHA512SUM="$(command -v sha512sum)"
|
|
SHASUM="$(command -v shasum || true)"
|
|
TMP_BASE="${TMPDIR:-/tmp}"
|
|
TMP_BASE="${TMP_BASE%/}"
|
|
TMP_ROOT="$(mktemp -d "${TMP_BASE}/ciss-boot-attestation.XXXXXXXX")"
|
|
|
|
cleanup() {
|
|
case "${TMP_ROOT}" in
|
|
"${TMP_BASE}/ciss-boot-attestation."*)
|
|
rm -rf -- "${TMP_ROOT}"
|
|
;;
|
|
*)
|
|
printf 'Refusing to clean unexpected test path: %s\n' "${TMP_ROOT}" >&2
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
fail() {
|
|
printf 'FAIL: %s\n' "${1}" >&2
|
|
exit 1
|
|
}
|
|
|
|
prepare_checksum_hook() {
|
|
declare source_hook="${1}"
|
|
declare target_hook="${2}"
|
|
declare fault_mode="${3}"
|
|
declare test_bin="${4}"
|
|
declare test_tty="${5}"
|
|
declare test_runtime=""
|
|
|
|
test_runtime="$(dirname "${test_tty}")"
|
|
sed \
|
|
-e "s|/usr/bin/|${test_bin}/|g" \
|
|
-e "s|/run/ciss-|${test_runtime}/ciss-|g" \
|
|
-e "s|_TTY=\"/dev/tty8\"|_TTY=\"${test_tty}\"|" \
|
|
"${source_hook}" > "${target_hook}"
|
|
|
|
if [[ "${fault_mode}" == "unknown-state" ]]; then
|
|
sed \
|
|
-e 's/_RETURN_PGP="na"/_RETURN_PGP="unknown"/' \
|
|
-e 's/_RETURN_SHA="${?}"/_RETURN_SHA="unknown"/g' \
|
|
"${target_hook}" > "${target_hook}.new"
|
|
mv "${target_hook}.new" "${target_hook}"
|
|
fi
|
|
}
|
|
|
|
install_test_sha512sum() {
|
|
declare test_bin="${1}"
|
|
|
|
if printf '' | "${SHA512SUM}" -c >/dev/null 2>&1; then
|
|
ln -s "${SHA512SUM}" "${test_bin}/sha512sum"
|
|
elif [[ -n "${SHASUM}" ]]; then
|
|
printf '#!/bin/sh\nexec "%s" -a 512 "$@"\n' "${SHASUM}" > "${test_bin}/sha512sum"
|
|
chmod 0755 "${test_bin}/sha512sum"
|
|
else
|
|
fail "No SHA-512 tool with checksum verification support is available."
|
|
fi
|
|
}
|
|
|
|
run_checksum_case() {
|
|
declare source_hook="${1}"
|
|
declare case_name="${2}"
|
|
declare expected_status="${3}"
|
|
declare expected_message="${4}"
|
|
declare case_dir=""
|
|
declare hook_copy=""
|
|
declare output_file=""
|
|
declare test_bin=""
|
|
declare test_tty=""
|
|
declare source_id=""
|
|
declare fault_mode=""
|
|
declare panic_returns="false"
|
|
declare status=0
|
|
|
|
source_id="$(printf '%s' "${source_hook#"${ROOT_DIR}"/}" | tr '/' '_')"
|
|
case_dir="${TMP_ROOT}/${source_id}-${case_name}"
|
|
hook_copy="${case_dir}/0030-ciss-verify-checksums"
|
|
output_file="${case_dir}/output.log"
|
|
test_bin="${case_dir}/bin"
|
|
test_tty="${case_dir}/tty.log"
|
|
|
|
mkdir -p "${case_dir}" "${test_bin}"
|
|
: >| "${test_tty}"
|
|
|
|
case "${case_name}" in
|
|
valid)
|
|
install_test_sha512sum "${test_bin}"
|
|
printf 'trusted rootfs payload\n' > "${case_dir}/payload"
|
|
(cd "${case_dir}" && "${test_bin}/sha512sum" payload > sha512sum.txt)
|
|
;;
|
|
missing-manifest)
|
|
install_test_sha512sum "${test_bin}"
|
|
;;
|
|
unsupported-manifest)
|
|
install_test_sha512sum "${test_bin}"
|
|
printf 'unsupported\n' > "${case_dir}/md5sum.txt"
|
|
;;
|
|
failed-checksum)
|
|
install_test_sha512sum "${test_bin}"
|
|
printf 'trusted rootfs payload\n' > "${case_dir}/payload"
|
|
(cd "${case_dir}" && "${test_bin}/sha512sum" payload > sha512sum.txt)
|
|
printf 'tampered rootfs payload\n' >| "${case_dir}/payload"
|
|
;;
|
|
missing-tool)
|
|
install_test_sha512sum "${test_bin}"
|
|
printf 'trusted rootfs payload\n' > "${case_dir}/payload"
|
|
(cd "${case_dir}" && "${test_bin}/sha512sum" payload > sha512sum.txt)
|
|
rm -f "${test_bin}/sha512sum"
|
|
;;
|
|
unknown-state)
|
|
fault_mode="unknown-state"
|
|
panic_returns="true"
|
|
install_test_sha512sum "${test_bin}"
|
|
printf 'trusted rootfs payload\n' > "${case_dir}/payload"
|
|
(cd "${case_dir}" && "${test_bin}/sha512sum" payload > sha512sum.txt)
|
|
;;
|
|
*)
|
|
fail "Unknown checksum test case: ${case_name}"
|
|
;;
|
|
esac
|
|
|
|
prepare_checksum_hook "${source_hook}" "${hook_copy}" "${fault_mode}" "${test_bin}" "${test_tty}"
|
|
|
|
set +e
|
|
# shellcheck disable=SC2034,SC2329
|
|
(
|
|
set +C
|
|
CDLB_SCRIPT_FULL="${hook_copy}"
|
|
LIVE_BOOT_CMDLINE="verify-checksums=sha512"
|
|
LIVE_VERIFY_CHECKSUMS=""
|
|
|
|
log_begin_msg() { :; }
|
|
log_end_msg() { :; }
|
|
log_success_msg() { :; }
|
|
panic() {
|
|
printf 'PANIC: %s\n' "${1}" >&2
|
|
if [[ "${panic_returns}" == "true" ]]; then
|
|
return 0
|
|
fi
|
|
exit 97
|
|
}
|
|
sleep() { :; }
|
|
|
|
# shellcheck source=/dev/null
|
|
. "${hook_copy}"
|
|
Verify_checksums "${case_dir}"
|
|
) > "${output_file}" 2>&1
|
|
status="${?}"
|
|
set -e
|
|
|
|
[[ "${status}" -eq "${expected_status}" ]] || {
|
|
cat "${output_file}" >&2
|
|
fail "${source_hook} ${case_name}: expected status ${expected_status}, got ${status}"
|
|
}
|
|
|
|
grep -Fq "${expected_message}" "${output_file}" || {
|
|
cat "${output_file}" >&2
|
|
fail "${source_hook} ${case_name}: missing expected message '${expected_message}'"
|
|
}
|
|
}
|
|
|
|
test_rootfs_payload_tamper() {
|
|
declare case_dir="${TMP_ROOT}/rootfs-payload-tamper"
|
|
declare test_bin="${case_dir}/bin"
|
|
declare payload="${case_dir}/crypt_liveiso"
|
|
declare manifest="${case_dir}/ciss_rootfs.crypt.decrypted.sha512sum.txt"
|
|
declare build_hook="${ROOT_DIR}/config/hooks/live/zzzz_ciss_crypt_squash.hook.binary"
|
|
declare boot_hook="${ROOT_DIR}/config/includes.chroot/usr/lib/live/boot/0042_ciss_post_decrypt_attest"
|
|
|
|
mkdir -p "${case_dir}" "${test_bin}"
|
|
install_test_sha512sum "${test_bin}"
|
|
|
|
printf 'trusted selected rootfs mapper payload\n' > "${payload}"
|
|
"${test_bin}/sha512sum" "${payload}" > "${manifest}"
|
|
"${test_bin}/sha512sum" -c --strict --quiet "${manifest}"
|
|
|
|
printf 'tampered selected rootfs mapper payload\n' >> "${payload}"
|
|
if "${test_bin}/sha512sum" -c --strict --quiet "${manifest}" >/dev/null 2>&1; then
|
|
fail "Modified selected rootfs payload unexpectedly passed checksum verification."
|
|
fi
|
|
|
|
# shellcheck disable=SC2016
|
|
grep -Fq 'sha512sum "${MAPPER_DEV}" >| "${ROOTFS_ATTESTATION}"' "${build_hook}" || \
|
|
fail "Build hook does not generate the attestation from the decrypted mapper."
|
|
# shellcheck disable=SC2016
|
|
grep -Fq '/usr/bin/sha512sum -c --strict --quiet "${HASH_FILE}"' "${boot_hook}" || \
|
|
fail "Boot hook does not verify the selected rootfs payload with sha512sum -c."
|
|
# shellcheck disable=SC2016
|
|
grep -Fq '[ "${_ATTESTED_PAYLOAD}" != "${CDLB_MAPPER_DEV}" ]' "${boot_hook}" || \
|
|
fail "Boot hook does not require the manifest to target the selected rootfs payload."
|
|
}
|
|
|
|
declare -a CHECKSUM_HOOKS=(
|
|
"${ROOT_DIR}/config/includes.chroot/usr/lib/live/boot/0030-ciss-verify-checksums"
|
|
"${ROOT_DIR}/scripts/usr/lib/live/boot/0030-ciss-verify-checksums"
|
|
)
|
|
declare checksum_hook=""
|
|
|
|
for checksum_hook in "${CHECKSUM_HOOKS[@]}"; do
|
|
run_checksum_case "${checksum_hook}" "valid" 0 "Verification of [sha checksum] file successful"
|
|
run_checksum_case "${checksum_hook}" "missing-manifest" 97 "No supported checksum manifest found."
|
|
run_checksum_case "${checksum_hook}" "unsupported-manifest" 97 "No supported checksum manifest found."
|
|
run_checksum_case "${checksum_hook}" "failed-checksum" 97 "No supported checksum manifest was verified successfully."
|
|
run_checksum_case "${checksum_hook}" "missing-tool" 97 "No supported checksum verification tool was available."
|
|
run_checksum_case "${checksum_hook}" "unknown-state" 1 "Checksum verification ended in an unsupported state."
|
|
done
|
|
|
|
test_rootfs_payload_tamper
|
|
|
|
printf 'PASS: checksum verification fails closed and modified rootfs payloads fail attestation.\n'
|