From 25dddb2c8beec13cf8d464cd2685b5bcedcfd5ba Mon Sep 17 00:00:00 2001 From: Radon Date: Tue, 23 Sep 2025 19:03:16 -0500 Subject: [PATCH] initial commit --- .gitignore | 24 ++++ LICENSE | 21 +++ Makefile | 59 +++++++++ README.md | 171 +++++++++++++++++++++++++ RELEASE | 1 + VERSION | 1 + docs/ksigner.8.in | 147 +++++++++++++++++++++ ksigner.spec.in | 70 ++++++++++ src/ksigner-update-hook | 67 ++++++++++ src/ksigner.conf | 25 ++++ src/ksigner.in | 276 ++++++++++++++++++++++++++++++++++++++++ 11 files changed, 862 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README.md create mode 100644 RELEASE create mode 100644 VERSION create mode 100644 docs/ksigner.8.in create mode 100644 ksigner.spec.in create mode 100644 src/ksigner-update-hook create mode 100644 src/ksigner.conf create mode 100644 src/ksigner.in diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1036007 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Build artifacts +build/ +dist/ +*.rpm +*.src.rpm +*.tar.gz +ksigner.spec +src/ksigner +docs/ksigner.8 + +# Temporary files +*~ +*.swp +*.tmp + +# RPM build artifacts +noarch/ +x86_64/ +SOURCES/ +SPECS/ +BUILD/ +BUILDROOT/ +RPMS/ +SRPMS/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e9ed18f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 KSigner Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..72a75e1 --- /dev/null +++ b/Makefile @@ -0,0 +1,59 @@ +NAME = ksigner +VERSION = $(shell cat VERSION) +RELEASE = $(shell cat RELEASE) +SOURCEDIR = . +BUILDDIR = build +SOURCES = src/ksigner src/ksigner.conf src/ksigner-update-hook docs/ksigner.8 README.md LICENSE +CLEANFILES = $(BUILDDIR) $(NAME).spec src/$(NAME) docs/$(NAME).8 noarch *.tar.gz *.rpm *.src.rpm + +.PHONY: all clean dist rpm srpm install + +all: dist + +clean: + rm -rf $(CLEANFILES) + +$(BUILDDIR): + mkdir -p $(BUILDDIR) + +%.spec: %.spec.in VERSION RELEASE + sed -e 's/@VERSION@/$(VERSION)/g' \ + -e 's/@RELEASE@/$(RELEASE)/g' \ + $< > $@ + +src/%: src/%.in VERSION RELEASE + sed -e 's/@VERSION@/$(VERSION)/g' \ + $< > $@ + +docs/%: docs/%.in VERSION RELEASE + sed -e 's/@VERSION@/$(VERSION)/g' \ + $< > $@ + +dist: $(BUILDDIR) $(NAME).spec src/$(NAME) docs/$(NAME).8 + mkdir -p $(BUILDDIR)/$(NAME)-$(VERSION) + cp -r $(SOURCES) $(NAME).spec $(BUILDDIR)/$(NAME)-$(VERSION)/ + cd $(BUILDDIR) && tar -czf $(NAME)-$(VERSION).tar.gz $(NAME)-$(VERSION)/ + cp $(BUILDDIR)/$(NAME)-$(VERSION).tar.gz $(SOURCEDIR) + +srpm: dist + rpmbuild --define "_topdir $(PWD)/$(BUILDDIR)" \ + --define "_sourcedir $(PWD)" \ + --define "_srcrpmdir $(PWD)" \ + -bs $(NAME).spec + +rpm: dist + rpmbuild --define "_topdir $(PWD)/$(BUILDDIR)" \ + --define "_sourcedir $(PWD)" \ + --define "_rpmdir $(PWD)" \ + --define "_buildrootdir $(PWD)/$(BUILDDIR)" \ + -ba $(NAME).spec + +install: + install -d $(DESTDIR)/usr/bin + install -d $(DESTDIR)/etc/ksigner + install -d $(DESTDIR)/etc/kernel/postinst.d + install -d $(DESTDIR)/usr/share/man/man8 + install -m 755 src/ksigner $(DESTDIR)/usr/bin/ + install -m 644 src/ksigner.conf $(DESTDIR)/etc/ksigner/ + install -m 755 src/ksigner-update-hook $(DESTDIR)/etc/kernel/postinst.d/zz-ksigner + install -m 644 docs/ksigner.8 $(DESTDIR)/usr/share/man/man8/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..cf7cc3d --- /dev/null +++ b/README.md @@ -0,0 +1,171 @@ +```bash +# Enable/disable automatic signing on kernel updates# Kernel Signer + +A secure boot kernel signing utility for Red Hat based systems (RHEL, CentOS, Fedora, Rocky Linux, AlmaLinux, etc.). + +## Overview + +This package provides a comprehensive solution for signing Linux kernels with custom keys for Secure Boot environments. It includes: + +- Automatic key generation and MOK enrollment +- Support for signing individual or all kernels +- Configurable through `/etc/ksigner/ksigner.conf` +- Comprehensive logging and status reporting + +## Installation + +### Building the RPM + +1. Install build dependencies: +```bash +# RHEL/CentOS/Rocky/Alma +sudo dnf install rpm-build rpmdevtools + +# Create build environment +rpmdev-setuptree +``` + +2. Build the package: +```bash +# Create source tarball +make dist + +# Build RPM +make rpm + +# Or build source RPM +make srpm +``` + +3. Install the package: +```bash +sudo rpm -ivh ksigner*.rpm +``` + +### Dependencies + +The following packages will be automatically installed as dependencies: +- `openssl` - Key generation and certificate operations +- `mokutil` - Machine Owner Key management +- `sbsigntools` - Kernel signing utilities +- `hmaccalc` - HMAC generation for signed kernels +- `sudo` - Privilege escalation +- `bash` (>= 4.0) - Shell scripting features + +## Quick Start + +1. **Install the package** (as shown above) + +2. **Set up signing keys**: +```bash +sudo ksigner setup +``` + +3. **Reboot and enroll MOK keys**: + - Reboot your system + - In the MOK management interface: Enroll MOK → Continue → Yes → Enter password → OK + +4. **Sign kernels**: +```bash +# Sign latest kernel +sudo ksigner sign + +# Sign all kernels +sudo ksigner sign-all + +# Check status +sudo ksigner status +``` + +## Configuration + +Edit `/etc/ksigner/ksigner.conf` to customize behavior: + +```bash +# Enable/disable automatic signing on kernel updates +SIGN_ON_UPDATE=true + +# Type of automatic signing (sign, sign-lts, sign-all, sign-all-lts) +AUTO_SIGN_TYPE="sign-lts" + +# Define which kernel versions are considered LTS +LTS_VERSIONS=( + "6.12" + "6.6" + "6.1" + "5.15" + "5.10" +) +``` + +## Commands + +- `ksigner setup` - Create and install signing keys +- `ksigner sign [kernel_file]` - Sign a kernel (latest if no file specified) +- `ksigner sign-lts [kernel_file]` - Sign an LTS kernel +- `ksigner sign-all` - Sign all available kernels +- `ksigner sign-all-lts` - Sign all LTS kernels +- `ksigner status` - Show signing key status +- `ksigner version` - Show version information + +## Automatic Kernel Signing + +When `SIGN_ON_UPDATE=true` in the configuration, kernels are automatically signed when installed via package manager. The hook script `/etc/kernel/postinst.d/zz-ksigner` handles this process. + +Logs are written to `/var/log/ksigner.log`. + +## File Locations + +- **Configuration**: `/etc/ksigner/ksigner.conf` +- **Public Key**: `/etc/pki/sbsign/certs/MOK.pem` +- **Private Key**: `/etc/pki/sbsign/private/MOK.priv` +- **DER Key**: `/etc/pki/sbsign/certs/MOK.der` +- **Log File**: `/var/log/ksigner.log` +- **Update Hook**: `/etc/kernel/postinst.d/zz-ksigner` + +## Security Notes + +- Private keys are stored with restrictive permissions (600) +- MOK enrollment requires manual confirmation to prevent unauthorized access +- All operations require root privileges +- HMAC files are generated for integrity verification + +## Troubleshooting + +### Check Status +```bash +sudo ksigner status +``` + +### View Logs +```bash +sudo tail -f /var/log/ksigner.log +``` + +### Verify MOK Enrollment +```bash +sudo mokutil --list-enrolled +``` + +### Re-enroll Keys +If keys become corrupted or lost: +```bash +sudo ksigner setup +# Then reboot and re-enroll MOK +``` + +## License + +This software is released under the MIT License. See LICENSE file for details. + +## Contributing + +Contributions are welcome! Please submit pull requests or issues through the project repository. + +## Support + +For support, please: +1. Check the man page: `man ksigner` +2. Review logs in `/var/log/ksigner.log` +3. Use the status command: `sudo ksigner status` +4. File issues in the project repository diff --git a/RELEASE b/RELEASE new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/RELEASE @@ -0,0 +1 @@ +1 diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..3eefcb9 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.0.0 diff --git a/docs/ksigner.8.in b/docs/ksigner.8.in new file mode 100644 index 0000000..3f68201 --- /dev/null +++ b/docs/ksigner.8.in @@ -0,0 +1,147 @@ +.TH KSIGNER 8 "September 2025" "ksigner @VERSION@" "System Administration" + +.SH NAME +ksigner \- Secure Boot kernel signing utility + +.SH SYNOPSIS +.B ksigner +.RI { setup | sign | sign-all | version | status } +.RI [ vmlinuz_kernel_filepath ] + +.SH DESCRIPTION +.B ksigner +is a utility for signing Linux kernels with custom keys for Secure Boot environments. +It supports signing individual kernels or all available kernels. + +.SH COMMANDS +.TP +.B setup +Create and install signing keys. This must be run before any signing operations. +The command generates RSA-4096 keys, converts them to appropriate formats, +installs them in the system directories, and imports them to the Machine Owner Key (MOK) database. +After running this command, you must reboot and enroll the keys through the MOK management interface. + +.TP +.B sign +Sign a single kernel file. If no kernel filepath is provided, signs the latest available kernel. + +.TP +.B sign-all +Sign all available kernels in /boot that match the pattern vmlinuz-*. +Excludes rescue kernels. + +.TP +.B version +Display version information. + +.TP +.B status +Show the current status of signing keys and MOK enrollment. + +.SH FILES +.TP +.I /etc/ksigner/ksigner.conf +Main configuration file. Contains key paths, automatic signing settings, and LTS version definitions. + +.TP +.I /etc/pki/sbsign/certs/MOK.pem +Public signing key in PEM format. + +.TP +.I /etc/pki/sbsign/private/MOK.priv +Private signing key. + +.TP +.I /etc/pki/sbsign/certs/MOK.der +Public signing key in DER format for MOK import. + +.TP +.I /etc/kernel/postinst.d/zz-ksigner +Kernel update hook script for automatic signing. + +.TP +.I /var/log/ksigner.log +Log file for automatic signing operations. + +.SH CONFIGURATION +The behavior of ksigner can be customized through the configuration file +.IR /etc/ksigner/ksigner.conf . +Key configuration options include: + +.TP +.B KEY_LIFETIME_DAYS +Number of days the signing keys should remain valid (default: 36500, approximately 100 years). + +.TP +.B SIGN_ON_UPDATE +Enable or disable automatic signing when kernels are updated (default: true). + +.TP +.B AUTO_SIGN_TYPE +Type of automatic signing to perform on kernel updates. +Valid options are: sign, sign-all (default: sign). + +.SH AUTOMATIC KERNEL SIGNING +When SIGN_ON_UPDATE is enabled in the configuration, new kernels are automatically signed +when they are installed through the package manager. The kernel update hook script +.I /etc/kernel/postinst.d/zz-ksigner +is executed during kernel package installation and performs the configured signing operation. + +.SH EXAMPLES +.TP +Set up signing keys for the first time: +.B ksigner setup + +.TP +Sign the latest kernel: +.B ksigner sign + +.TP +Sign a specific kernel: +.B ksigner sign /boot/vmlinuz-6.1.0-13-amd64 + +.TP +Sign all kernels: +.B ksigner sign-all + +.TP +Check the status of signing keys: +.B ksigner status + +.SH REQUIREMENTS +The following packages must be installed for ksigner to function: +.IP \(bu 4 +openssl - for key generation and certificate operations +.IP \(bu 4 +mokutil - for Machine Owner Key management +.IP \(bu 4 +sbsigntools - for signing kernels (provides sbsign command) +.IP \(bu 4 +hmaccalc - for generating kernel HMAC files +.IP \(bu 4 +sudo - for privilege escalation +.IP \(bu 4 +bash (version 4.0 or later) - for shell scripting features + +.SH SECURITY CONSIDERATIONS +.IP \(bu 4 +Private keys are stored with restrictive permissions (600) in /etc/pki/sbsign/private/ +.IP \(bu 4 +The setup process requires manual MOK enrollment to prevent unauthorized key installation +.IP \(bu 4 +All operations require root privileges +.IP \(bu 4 +HMAC files are generated for signed kernels to maintain integrity + +.SH EXIT STATUS +.B ksigner +exits with status 0 on success, and non-zero on error. + +.SH BUGS +Report bugs to your distribution's bug tracking system or the project repository. + +.SH SEE ALSO +.BR mokutil (8), +.BR sbsign (1), +.BR openssl (1), +.BR systemctl (1) diff --git a/ksigner.spec.in b/ksigner.spec.in new file mode 100644 index 0000000..61fcb70 --- /dev/null +++ b/ksigner.spec.in @@ -0,0 +1,70 @@ +Name: ksigner +Version: @VERSION@ +Release: @RELEASE@%{?dist} +Summary: Secure Boot kernel signing utility + +License: MIT +URL: https://git.radon.win/radon/ksigner +Source0: %{name}-%{version}.tar.gz + +BuildArch: noarch + +Requires: openssl +Requires: mokutil +Requires: sbsigntools +Requires: hmaccalc +Requires: sudo +Requires: bash >= 4.0 + +%description +A utility for signing Linux kernels with custom keys for Secure Boot. +Supports signing individual kernels or all kernels. + +%prep +%setup -q + +%build +# Nothing to build - shell script + +%install +# Create directories +install -d %{buildroot}%{_bindir} +install -d %{buildroot}%{_sysconfdir}/ksigner +install -d %{buildroot}%{_unitdir} +install -d %{buildroot}%{_sysconfdir}/kernel/postinst.d +install -d %{buildroot}%{_mandir}/man8 + +# Install main script +install -m 755 ksigner %{buildroot}%{_bindir}/ksigner + +# Install configuration file +install -m 644 ksigner.conf %{buildroot}%{_sysconfdir}/ksigner/ksigner.conf + +# Install kernel update hook +install -m 755 ksigner-update-hook %{buildroot}%{_sysconfdir}/kernel/postinst.d/zz-ksigner + +# Install man page +install -m 644 ksigner.8 %{buildroot}%{_mandir}/man8/ksigner.8 + +%post +echo "===========================================" +echo "Kernel Signer has been installed." +echo "To set up kernel signing:" +echo " 1. Run: sudo ksigner setup" +echo " 2. Reboot your system" +echo " 3. In MOK Manager: Enroll MOK → Continue → Yes → Enter password" +echo "===========================================" + +%files +%license LICENSE +%doc README.md +%{_bindir}/ksigner +%config(noreplace) %{_sysconfdir}/ksigner/ksigner.conf +%{_sysconfdir}/kernel/postinst.d/zz-ksigner +%{_mandir}/man8/ksigner.8* + +%changelog +* Tue Sep 23 2025 RadioactivePb - @VERSION@-@RELEASE@ +- Initial RPM release +- Added automatic kernel signing on updates +- Added configuration file support diff --git a/src/ksigner-update-hook b/src/ksigner-update-hook new file mode 100644 index 0000000..769a882 --- /dev/null +++ b/src/ksigner-update-hook @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +# Kernel update hook for automatic signing +# This script is called when new kernels are installed + +CONFIG_FILE="/etc/ksigner/ksigner.conf" +KERNEL_SIGNER="/usr/bin/ksigner" +LOG_FILE="/var/log/ksigner.log" + +# Source configuration +if [[ -f "$CONFIG_FILE" ]]; then + source "$CONFIG_FILE" +fi + +# Default values +SIGN_ON_UPDATE=${SIGN_ON_UPDATE:-true} +AUTO_SIGN_TYPE=${AUTO_SIGN_TYPE:-sign} +LOG_FILE=${LOG_FILE:-/var/log/ksigner.log} + +log_message() { + echo "$(date '+%Y-%m-%d %H:%M:%S') - ksigner-update-hook: $1" >>"$LOG_FILE" +} + +# Exit if automatic signing is disabled +if [[ "$SIGN_ON_UPDATE" != "true" ]]; then + log_message "Automatic signing disabled, skipping" + exit 0 +fi + +# Check if ksigner exists and keys are set up +if [[ ! -x "$KERNEL_SIGNER" ]]; then + log_message "ksigner not found at $KERNEL_SIGNER" + exit 1 +fi + +if [[ ! -f "/etc/pki/sbsign/certs/MOK.pem" ]]; then + log_message "Signing keys not found, run 'ksigner setup' first" + exit 1 +fi + +# Get the kernel version from the environment or find the latest +if [[ -n "$KERNEL_VERSION" ]]; then + KERNEL_FILE="/boot/vmlinuz-$KERNEL_VERSION" + if [[ -f "$KERNEL_FILE" ]]; then + log_message "Signing newly installed kernel: $KERNEL_VERSION" + if "$KERNEL_SIGNER" sign "$KERNEL_FILE" >>"$LOG_FILE" 2>&1; then + log_message "Successfully signed kernel $KERNEL_VERSION" + else + log_message "Failed to sign kernel $KERNEL_VERSION" + exit 1 + fi + else + log_message "Kernel file not found: $KERNEL_FILE" + exit 1 + fi +else + # Fallback to configured auto-sign type + log_message "Running automatic signing: $AUTO_SIGN_TYPE" + if "$KERNEL_SIGNER" "$AUTO_SIGN_TYPE" >>"$LOG_FILE" 2>&1; then + log_message "Successfully completed $AUTO_SIGN_TYPE" + else + log_message "Failed to complete $AUTO_SIGN_TYPE" + exit 1 + fi +fi + +exit 0 diff --git a/src/ksigner.conf b/src/ksigner.conf new file mode 100644 index 0000000..2acb1a2 --- /dev/null +++ b/src/ksigner.conf @@ -0,0 +1,25 @@ +# Configuration file for ksigner +# This file is sourced by the ksigner script + +# 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" + +# Automatic signing on kernel updates +# Set to true to enable automatic signing when kernels are updated +SIGN_ON_UPDATE=true + +# Type of automatic signing to perform +# Options: sign, sign-all +AUTO_SIGN_TYPE="sign" + +# Log file for automatic signing operations +LOG_FILE="/var/log/ksigner.log" diff --git a/src/ksigner.in b/src/ksigner.in new file mode 100644 index 0000000..80c65ee --- /dev/null +++ b/src/ksigner.in @@ -0,0 +1,276 @@ +#!/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 + +# Default configuration values (can be overridden in config file) +KEY_LIFETIME_DAYS=${KEY_LIFETIME_DAYS:-$((365 * 100))} +KEY_PUB_DIR=${KEY_PUB_DIR:-/etc/pki/sbsign/certs/} +KEY_PUB=${KEY_PUB:-MOK.pem} +KEY_PRIV_DIR=${KEY_PRIV_DIR:-/etc/pki/sbsign/private/} +KEY_PRIV=${KEY_PRIV:-MOK.priv} +KEY_DER=${KEY_DER:-MOK.der} +SIGN_ON_UPDATE=${SIGN_ON_UPDATE:-true} +AUTO_SIGN_TYPE=${AUTO_SIGN_TYPE:-sign} + +REQUIRED_BINARIES=( + "openssl" + "mokutil" + "sbsign" + "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) 2025" + 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)" ] +} + +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 "/CN=Kernel Signing/" || + 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: Sign the kernel + log "[Step 1] 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 2: Verify the kernel was signed + log "[Step 2] Verifying '$kern_file' was signed" + [ -f "$kern_file.signed" ] || + panic "'$kern_file.signed' was not found" + + # Step 3: Move the signed kernel + log "[Step 3] Moving '$kern_file.signed' to '$kern_file'" + mv -f "$kern_file.signed" "$kern_file" || + panic "Failed to move '$kern_file.signed'" + + # Step 4: Make the kernel executable + log "[Step 4] Setting permissions for '$kern_file'" + chmod +x "$kern_file" || + panic "Failed to make '$kern_file' executable" + + # Step 5: Create the HMAC + log "[Step 5] 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 "$@"