ksigner/src/ksigner.in
2025-09-26 17:21:01 -05:00

313 lines
7.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# ksigner - Secure Boot kernel signing utility
# Version: @VERSION@
# Source configuration
CONFIG_FILE="/etc/ksigner/ksigner.conf"
if [[ -f "$CONFIG_FILE" ]]; then
source "$CONFIG_FILE"
fi
# Key lifetime in days (default: 100 years)
KEY_LIFETIME_DAYS=$((365 * 100))
# Directory paths for keys
KEY_PUB_DIR="/etc/pki/sbsign/certs/"
KEY_PRIV_DIR="/etc/pki/sbsign/private/"
# Key filenames
KEY_PUB="MOK.pem"
KEY_PRIV="MOK.priv"
KEY_DER="MOK.der"
KEY_SUBJ="/O=ksigner/OU=KSigner Secure Boot/CN=ksigner"
REQUIRED_BINARIES=(
"openssl"
"mokutil"
"sbsign"
"sbverify"
"sha512hmac"
)
panic() {
echo "ERROR: $1" >&2
exit 1
}
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >&2
}
usage() {
echo "Usage: $0 {setup|sign|sign-all|version|status} [vmlinuz_kernel_filepath]"
echo " setup - Create and install signing keys"
echo " sign - Sign a kernel file (optional: vmlinuz_kernel_filepath)"
echo " sign-all - Sign all available kernels"
echo " version - Show version information"
echo " status - Show signing key status"
exit 1
}
version() {
echo "ksigner version @VERSION@"
echo "Copyright (C) @YEAR@"
echo "This is free software; see the source for copying conditions."
}
status() {
echo "Kernel Signer Status:"
echo "====================="
echo "Public key file: $KEY_PUB_DIR$KEY_PUB"
if [[ -f "$KEY_PUB_DIR$KEY_PUB" ]]; then
echo " Status: Found"
echo " Details:"
openssl x509 -in "$KEY_PUB_DIR$KEY_PUB" -noout -subject -dates 2>/dev/null || echo " Error reading certificate"
else
echo " Status: Not found"
fi
echo "Private key file: $KEY_PRIV_DIR$KEY_PRIV"
[[ -f "$KEY_PRIV_DIR$KEY_PRIV" ]] && echo " Status: Found" || echo " Status: Not found"
echo "DER key file: $KEY_PUB_DIR$KEY_DER"
[[ -f "$KEY_PUB_DIR$KEY_DER" ]] && echo " Status: Found" || echo " Status: Not found"
echo "MOK keys enrolled:"
mokutil --list-enrolled 2>/dev/null | grep -A 3 -B 1 "Kernel Signing" || echo " No custom MOK keys found"
echo "Configuration file: $CONFIG_FILE"
[[ -f "$CONFIG_FILE" ]] && echo " Status: Found" || echo " Status: Using defaults"
}
req_check() {
local missing_binaries=()
for binary in "${REQUIRED_BINARIES[@]}"; do
if ! command -v "$binary" >/dev/null 2>&1; then
missing_binaries+=("$binary")
fi
done
if [[ ${#missing_binaries[@]} -gt 0 ]]; then
echo "Missing required binaries: ${missing_binaries[*]}" >&2
echo "Please install the following packages:" >&2
for binary in "${missing_binaries[@]}"; do
case "$binary" in
"openssl") echo " - openssl" >&2 ;;
"mokutil") echo " - mokutil" >&2 ;;
"sbsign") echo " - sbsigntools" >&2 ;;
"sha512hmac") echo " - hmaccalc" >&2 ;;
esac
done
exit 1
fi
log "All required binaries are present"
}
version_greater() {
local ver1="$1"
local ver2="$2"
[ "$ver1" = "$(printf '%s\n%s' "$ver1" "$ver2" | sort -V | tail -n1)" ]
}
check_kernel_signature() {
local kernel_file="$1"
if [[ -z "$kernel_file" ]]; then
echo "Error: File '$kernel_file' not provided"
return 1
fi
if [[ ! -f "$kernel_file" ]]; then
echo "Error: File '$kernel_file' not found"
return 1
fi
local output
output=$(sbverify --list "$kernel_file" 2>&1)
if [[ -n "$output" ]] && ([[ "$output" == *"signature"* ]] || [[ "$output" == *"issuer"* ]] || [[ "$output" == *"Certificate"* ]]); then
return 0
fi
return 1
}
find_all_kernels() {
local all_files=()
for file in /boot/vmlinuz-*; do
[ -f "$file" ] || continue
if [[ "$file" == *"rescue"* ]]; then
continue
fi
all_files+=("$file")
done
echo "${all_files[@]}"
}
find_latest_kernel() {
local latest_file=""
local latest_version=""
for file in /boot/vmlinuz-*; do
[ -f "$file" ] || continue
if [[ "$file" == *"rescue"* ]]; then
continue
fi
local kver="${file#*vmlinuz-}"
kver="${kver%%-*}"
if [ -z "$latest_version" ] || version_greater "$kver" "$latest_version"; then
latest_version="$kver"
latest_file="$file"
fi
done
echo "$latest_file"
}
setup_signing_keys() {
# Step 1: Create the signing keys
log "[Step 1] Creating signing keys..."
openssl req -new -x509 -newkey rsa:4096 \
-keyout "$KEY_PRIV" \
-outform DER -out "$KEY_DER" \
-nodes -days "$KEY_LIFETIME_DAYS" \
-subj "$KEY_SUBJ" ||
panic "[Step 1] Failed to create signing keys"
# Step 2: Convert the keys to PEM format
log "[Step 2] Converting keys to PEM format..."
openssl x509 -inform der -in $KEY_DER -out $KEY_PUB ||
panic "[Step 2] Failed to convert keys to PEM format"
# Step 3: Create the directory for the keys
log "[Step 3] Creating the directory for the keys..."
mkdir -p $KEY_PUB_DIR $KEY_PRIV_DIR ||
panic "[Step 3] Failed to create the directory for the keys"
# Step 4: Copy the keys to the directory
log "[Step 4] Copying the keys to the directory..."
mv -f $KEY_DER $KEY_PUB $KEY_PUB_DIR ||
panic "[Step 4a] Failed to copy the keys to the directory"
mv -f $KEY_PRIV $KEY_PRIV_DIR ||
panic "[Step 4b] Failed to copy the keys to the directory"
# Step 5: Set the permissions for the keys
log "[Step 5] Setting the permissions for the keys..."
chmod -R 600 $KEY_PRIV_DIR ||
panic "[Step 5] Failed to set the permissions for the keys"
# Step 6: Import the keys to the MOK
log "[Step 6] Importing the keys to the MOK..."
mokutil --import $KEY_PUB_DIR$KEY_DER ||
panic "[Step 6] Failed to import the keys to the MOK"
echo
log "Signing keys have been created and installed"
echo
echo "Please reboot your computer and enroll the keys with MOK"
echo
echo "Enroll MOK -> Continue -> Yes -> Enter password -> OK"
echo
}
sign_kernel() {
local kern_version="$1"
local kern_file="$2"
# Step 1: Check if the kernel is already signed
log "[Step 1] Checking if '$kern_version' is already signed..."
if check_kernel_signature "$kern_file"; then
log "'$kern_version' is already signed, skipping"
return
fi
# Step 2: Sign the kernel
log "[Step 2] Signing '$kern_version'..."
/usr/bin/sbsign \
--key "$KEY_PRIV_DIR$KEY_PRIV" \
--cert "$KEY_PUB_DIR$KEY_PUB" \
"$kern_file" \
--output "$kern_file.signed" ||
panic "[Step 1] Failed to sign '$kern_version'"
# Step 3: Verify the kernel was signed
log "[Step 3] Verifying '$kern_file' was signed"
[ -f "$kern_file.signed" ] ||
panic "'$kern_file.signed' was not found"
# Step 4: Move the signed kernel
log "[Step 4] Moving '$kern_file.signed' to '$kern_file'"
mv -f "$kern_file.signed" "$kern_file" ||
panic "Failed to move '$kern_file.signed'"
# Step 5: Make the kernel executable
log "[Step 5] Setting permissions for '$kern_file'"
chmod +x "$kern_file" ||
panic "Failed to make '$kern_file' executable"
# Step 6: Create the HMAC
log "[Step 6] Creating HMAC for '$kern_file'"
sha512hmac "$kern_file" >"${kern_file/vmlinuz/.vmlinuz}.hmac" ||
panic "Failed to create HMAC for '$kern_file'"
echo
log "Signed '$kern_version' successfully"
echo
}
file_checks() {
local kfile="$1"
[ -e "$kfile" ] ||
panic "Kernel file '$kfile' was not found"
[ -e "$KEY_PUB_DIR$KEY_PUB" ] ||
panic "'$KEY_PUB_DIR$KEY_PUB' was not found, please run 'setup' first"
[ -e "$KEY_PRIV_DIR$KEY_PRIV" ] ||
panic "'$KEY_PRIV_DIR$KEY_PRIV' was not found, please run 'setup' first"
}
main() {
case "$1" in
"setup")
req_check
setup_signing_keys
;;
"sign")
req_check
shift
[ -n "$1" ] &&
kfile="$1" ||
kfile="$(find_latest_kernel)"
kver="${kfile#*vmlinuz-}"
kver="${kver%%-*}"
file_checks "$kfile"
sign_kernel "$kver" "$kfile"
;;
"sign-all")
req_check
all_kernels_str=$(find_all_kernels)
read -ra all_kernels <<<"$all_kernels_str"
for kfile in "${all_kernels[@]}"; do
[ -n "$kfile" ] || continue
kver="${kfile#*vmlinuz-}"
kver="${kver%%-*}"
file_checks "$kfile"
sign_kernel "$kver" "$kfile"
done
;;
"version")
version
;;
"status")
status
;;
*)
usage
;;
esac
}
# Ensure running as root
[[ $EUID -eq 0 ]] || exec sudo "$0" "$@"
main "$@"