#!/bin/sh # bashsupport disable=BP5007 # shellcheck disable=SC2249 # shellcheck shell=sh # SPDX-Version: 3.0 # SPDX-CreationInfo: 2025-11-12; WEIDNER, Marc S.; # 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-2025; WEIDNER, Marc S.; # SPDX-FileType: SOURCE # SPDX-License-Identifier: GPL-3.0-or-later # 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 # Module summary: # This live-boot component implements verify-checksums mode for the mounted live medium. # It reads the live-boot command line to decide whether checksum verification is enabled, which digests to accept, and # whether checksum signature verification is required. When signature verification is enabled, it requires to be pinned CISS GPG # key material from the live medium, verifies this script's signed SHA-512 hash, and verifies the selected checksum manifest # signature before accepting checksum results. It checks the first supported checksum manifest with an available matching digest # tool and writes detailed checksum command output to the verification TTY when checksum execution runs. It fails closed by # panicking on missing manifests, missing digest tools, empty manifests, failed signatures, failed checksums, or unknown # verification states. ### Modified Version of the original file: ### https://salsa.debian.org/live-team/live-boot 'components/0030-ciss-verify-checksums' ### If the offered checksum is successfully verified, proceed with booting. Otherwise, panic. ####################################### # Modified checksum-integrity and authenticity-verification-script depending on pinned GPG FPR for boot process verification. # Globals: # LIVE_BOOT_CMDLINE # _TTY # Arguments: # 1: _MOUNTPOINT # Returns: # 0 : Successful verification ####################################### Verify_checksums() { printf "\e[95m[INFO] Starting : [/usr/lib/live/boot/0030-ciss-verify-checksums] \n\e[0m" ### Declare variables -------------------------------------------------------------------------------------------------------- ### Will be replaced at build time: export CDLB_EXP_FPR="@EXP_FPR@" export CDLB_EXP_CA_FPR="@EXP_CA_FPR@" ### Declare functions -------------------------------------------------------------------------------------------------------- ####################################### # Helper for colored text output on stdout. # Globals: # None # Arguments: # *: String to print ####################################### log_in() { printf '\e[95m[INFO] %s \n\e[0m' "$*"; } ####################################### # Helper for colored text output on stdout. # Globals: # None # Arguments: # *: String to print ####################################### log_ok() { printf '\e[92m[INFO] %s \n\e[0m' "$*"; } ####################################### # Helper for colored text output on stdout. # Globals: # None # Arguments: # *: String to print ####################################### log_er() { printf '\e[91m[FATAL] %s \n\e[0m' "$*"; } _MOUNTPOINT="${1}" _PARAMETER="" _TTY="/dev/tty8" LIVE_VERIFY_CHECKSUMS_DIGESTS="${LIVE_VERIFY_CHECKSUMS_DIGESTS:-sha512 sha384 sha256}" LIVE_VERIFY_CHECKSUMS_SIGNATURES="false" _CHECKSUM_LOG="" _CHECKSUM_LOG_DIR="${LIVE_VERIFY_CHECKSUMS_LOG_DIR:-/run}" _KEYFILE="" _MANIFEST_FOUND="false" _MP="" _RETURN_PGP="" _RETURN_SHA="" _TOOL_FOUND="false" _VERIFICATION_EXECUTED="false" _VERIFICATION_SUCCEEDED="false" ### Parse commandline arguments ---------------------------------------------------------------------------------------------- # shellcheck disable=SC2154 for _PARAMETER in ${LIVE_BOOT_CMDLINE}; do case "${_PARAMETER}" in live-boot.verify-checksums=* | verify-checksums=*) LIVE_VERIFY_CHECKSUMS="true" LIVE_VERIFY_CHECKSUMS_DIGESTS="${_PARAMETER#*verify-checksums=}" ;; live-boot.verify-checksums | verify-checksums) LIVE_VERIFY_CHECKSUMS="true" ;; live-boot.verify-checksums-signatures | verify-checksums-signatures) LIVE_VERIFY_CHECKSUMS_SIGNATURES="true" ;; esac done ### Check if the function should be skipped ---------------------------------------------------------------------------------- case "${LIVE_VERIFY_CHECKSUMS}" in true) : ;; *) return 0 ;; esac ### Check GPG pubkey file correct path --------------------------------------------------------------------------------------- for _MP in /lib/live/mount/medium /run/live/medium /cdrom /; do if [ -e "${_MP}/${CDLB_EXP_FPR}.gpg" ]; then _KEYFILE="${_MP}/${CDLB_EXP_FPR}.gpg" if [ -e "${_MP}/${CDLB_EXP_CA_FPR}.gpg" ]; then _CA_KEYFILE="${_MP}/${CDLB_EXP_CA_FPR}.gpg" fi break fi done if [ "${LIVE_VERIFY_CHECKSUMS_SIGNATURES}" = "true" ] && [ -z "${_KEYFILE}" ]; then log_er "No pinned GPG key file found while checksum signature verification is enabled." sleep 8 panic "[FATAL] No pinned GPG key file found while checksum signature verification is enabled." fi # shellcheck disable=SC2164 cd "${_MOUNTPOINT}" ### CDLB verification of script integrity itself ----------------------------------------------------------------------------- if [ "${LIVE_VERIFY_CHECKSUMS_SIGNATURES}" = "true" ]; then log_begin_msg "Verifying integrity of: [0030-ciss-verify-checksums]" printf "\n" _CAND="" CDLB_SCRIPT_SELF="" CDLB_CMD="" CDLB_COMPUTED="" CDLB_EXPECTED="" CDLB_HASHFILE="" CDLB_SIG_FILE="" CDLB_CMD="/usr/bin/sha512sum" CDLB_SHA="sha512" for _CAND in /scripts/live-bottom/0030-ciss-verify-checksums /usr/lib/live/boot/0030-ciss-verify-checksums ; do [ -e "${_CAND}" ] && { CDLB_SCRIPT_SELF="${_CAND}"; break; } done CDLB_SCRIPT_FILE="${CDLB_SCRIPT_SELF##*/}" CDLB_SCRIPT_PATH="${CDLB_SCRIPT_SELF%/*}" CDLB_SCRIPT_FULL="${CDLB_SCRIPT_PATH%/}/${CDLB_SCRIPT_FILE}" CDLB_HASHFILE="/etc/ciss/hashes/${CDLB_SCRIPT_FILE}.${CDLB_SHA}sum.txt" CDLB_SIG_FILE="/etc/ciss/signatures/${CDLB_SCRIPT_FILE}.${CDLB_SHA}sum.txt.sig" _STATUS="$(/usr/bin/gpgv --keyring "${_KEYFILE}" --status-fd 1 "${CDLB_SIG_FILE}" "${CDLB_HASHFILE}")" _CDLB_SIG_FILE_FPR="$(printf '%s\n' "${_STATUS}" | awk '/^\[GNUPG:\] VALIDSIG /{print $3; exit}')" ### Compare against pinned and expected fingerprint. if [ "${_CDLB_SIG_FILE_FPR}" = "${CDLB_EXP_FPR}" ]; then log_ok "Signature FPR valid: got: [${_CDLB_SIG_FILE_FPR}] expected: [${CDLB_EXP_FPR}]" else log_er "Signature FPR mismatch: got: [${_CDLB_SIG_FILE_FPR}] expected: [${CDLB_EXP_FPR}]" sleep 8 panic "[FATAL] Signature FPR mismatch: got: [${_CDLB_SIG_FILE_FPR}] expected: [${CDLB_EXP_FPR}]." fi ### Script self-integrity and authenticity checks -------------------------------------------------------------------------- ### Assumption: initramfs itself is not altered. log_in "Verifying signature of: [${CDLB_SIG_FILE}] ..." if ! /usr/bin/gpgv --keyring "${_KEYFILE}" --status-fd 1 "${CDLB_SIG_FILE}" "${CDLB_HASHFILE}"; then log_er "Verifying signature of: [${CDLB_SIG_FILE}] failed." sleep 8 panic "[FATAL] Verifying signature of: [${CDLB_SIG_FILE}] failed." else log_ok "Verifying signature of: [${CDLB_SIG_FILE}] successful." fi log_in "Recomputing hash for: [${CDLB_SHA}] ..." CDLB_COMPUTED=$("${CDLB_CMD}" "${CDLB_SCRIPT_FULL}" | { read -r first _ || exit 1; printf '%s\n' "${first}"; }) IFS=' ' read -r CDLB_EXPECTED _ < "${CDLB_HASHFILE}" if [ "${CDLB_COMPUTED}" != "${CDLB_EXPECTED}" ]; then log_er "Recomputing hash for: [${CDLB_SHA}] failed." sleep 8 panic "[FATAL] Recomputing hash for: [${CDLB_SHA}] failed." fi log_ok "Recomputing hash for: [${CDLB_SHA}] successful." log_ok "Verification of authenticity and integrity of [${CDLB_SCRIPT_FULL}] successfully completed." log_end_msg printf "\n" fi ### Checksum and checksum signature verification ----------------------------------------------------------------------------- log_begin_msg "Verifying checksums" printf "\n" log_in "Verifying checksums ..." # shellcheck disable=SC2001 for _DIGEST in $(echo "${LIVE_VERIFY_CHECKSUMS_DIGESTS}" | sed -e 's|,| |g'); do # shellcheck disable=SC2060 _CHECKSUMS="$(echo "${_DIGEST}" | tr [a-z] [A-Z])SUMS ${_DIGEST}sum.txt" for _CHECKSUM in ${_CHECKSUMS}; do if [ -e "${_CHECKSUM}" ]; then _MANIFEST_FOUND="true" log_in "Found: [${_CHECKSUM}] ..." if [ -e "/usr/bin/${_DIGEST}sum" ]; then _TOOL_FOUND="true" log_in "Found: [/usr/bin/${_DIGEST}sum] ..." if [ "${LIVE_VERIFY_CHECKSUMS_SIGNATURES}" = "true" ]; then log_in "Checking signature of: [${_CHECKSUM}] ..." _CHECKSUM_SIGNATURE="${_CHECKSUM}.sig" if /usr/bin/gpgv --keyring "${_KEYFILE}" --status-fd 1 "${_CHECKSUM_SIGNATURE}" "${_CHECKSUM}"; then _RETURN_PGP="${?}" log_ok "Checking signature of: [${_CHECKSUM}] successful." else _RETURN_PGP="${?}" log_er "Checking signature of: [${_CHECKSUM}] failed." fi else _RETURN_PGP="na" fi # shellcheck disable=SC2312 _CHECKSUM_LOG="${_CHECKSUM_LOG_DIR}/ciss-${_DIGEST}sum-check.log" _VERIFICATION_EXECUTED="true" if ! grep -v '^#' "${_CHECKSUM}" | grep -q '[^[:space:]]'; then _RETURN_SHA="254" : > "${_CHECKSUM_LOG}" log_er "Checksum manifest has no checksum entries: [${_CHECKSUM}]" elif grep -v '^#' "${_CHECKSUM}" | LC_ALL=C /usr/bin/"${_DIGEST}"sum -c > "${_CHECKSUM_LOG}" 2>&1; then _RETURN_SHA="${?}" cat "${_CHECKSUM_LOG}" > "${_TTY}" log_ok "Found: [/usr/bin/${_DIGEST}sum] successful verified: [${_CHECKSUM}]" else _RETURN_SHA="${?}" cat "${_CHECKSUM_LOG}" > "${_TTY}" log_er "Found: [/usr/bin/${_DIGEST}sum] unsuccessful verified: [${_CHECKSUM}]" log_er "Checksum verification failed. Failed entries:" if ! grep -E ': FAILED$|FAILED open or read|No such file or directory|WARNING:' "${_CHECKSUM_LOG}" >&2; then cat "${_CHECKSUM_LOG}" >&2 fi fi if { [ "${_RETURN_PGP}" = "0" ] || [ "${_RETURN_PGP}" = "na" ]; } && [ "${_RETURN_SHA}" = "0" ]; then _VERIFICATION_SUCCEEDED="true" fi # Stop after the first verification. break 2 else _RETURN_SHA="255" log_er "NOT Found [/usr/bin/${_DIGEST}sum]." fi fi done done log_end_msg printf "\n" if [ "${_MANIFEST_FOUND}" != "true" ]; then log_er "No supported checksum manifest found. Checksum verification is fail-closed." sleep 8 panic "[FATAL] No supported checksum manifest found. Checksum verification is fail-closed." fi if [ "${_TOOL_FOUND}" != "true" ]; then log_er "Checksum manifest found, but no supported checksum tool is available. Checksum verification is fail-closed." sleep 8 panic "[FATAL] Checksum manifest found, but no supported checksum tool is available. Checksum verification is fail-closed." fi if [ "${_VERIFICATION_EXECUTED}" != "true" ]; then log_er "Checksum verification was not executed. Checksum verification is fail-closed." sleep 8 panic "[FATAL] Checksum verification was not executed. Checksum verification is fail-closed." fi if [ "${_VERIFICATION_SUCCEEDED}" != "true" ]; then log_er "[FATAL] Checksum verification did not complete successfully. Evaluating fail-closed failure state." fi case "${_RETURN_PGP},${_RETURN_SHA}" in "0,0") log_ok "Verification of [GPG signature] and [sha checksum] file successful; continuing booting." log_success_msg "Verification of [GPG signature] and [sha checksum] file successful; continuing booting." return 0 ;; "na,0") log_ok "Verification of [sha checksum] file successful; continuing booting." log_success_msg "Verification of [sha checksum] file successful; continuing booting." return 0 ;; "0,"*) log_er "Verification of [GPG signature] file successful, while verification of [sha checksum] file failed." sleep 8 panic "Verification of [GPG signature] file successful, while verification of [sha checksum] file failed." ;; *",0") log_er "Verification of [GPG signature] file failed, while verification of [sha checksum] file successful." sleep 8 panic "Verification of [GPG signature] file failed, while verification of [sha checksum] file successful." ;; "na,"*) log_er "Verification of [sha checksum] file failed." sleep 8 panic "Verification of [sha checksum] file failed." ;; *) log_er "Unknown checksum verification state: [${_RETURN_PGP:-unset},${_RETURN_SHA:-unset}]." sleep 8 panic "[FATAL] Unknown checksum verification state. Checksum verification is fail-closed." ;; esac } # vim: number et ts=2 sw=2 sts=2 ai tw=128 ft=sh