#!/bin/sh
# softperfect-ramfs-mok-setup — walk the user through enrolling the Machine
# Owner Key that DKMS signs the RAM File System kernel module with. After
# reboot + MokManager enrolment, the kernel module loads under Secure Boot.
#
# The key is the one DKMS ALREADY signs with on this machine (Ubuntu's
# shim-signed MOK, or the key DKMS 3 generates for itself): enrolling it
# trusts every DKMS module at once and changes nothing for the others. Only
# a DKMS with no signing key at all gets one generated here.

set -e

DKMS_NAME=spramfs
DKMS_VERSION=26.10
KEY_DIR=/var/lib/softperfect-ramfs
DKMS_FRAMEWORK=/etc/dkms/framework.conf.d/spramfs-sb-sign.conf
PATH=$PATH:/usr/sbin:/sbin

if [ "$(id -u)" -ne 0 ]; then
	echo "softperfect-ramfs-mok-setup must run as root." >&2
	echo "Try: sudo softperfect-ramfs-mok-setup" >&2
	exit 1
fi

# Secure Boot state straight from the EFI variable (4-byte attribute header,
# then the value): asking mokutil would read "not installed" as "disabled".
sb_enabled() {
	set -- /sys/firmware/efi/efivars/SecureBoot-*
	[ -e "$1" ] || return 1
	[ "$(od -An -j4 -N1 -tu1 "$1" 2>/dev/null | tr -d ' ')" = "1" ]
}

if ! sb_enabled; then
	echo "Secure Boot is not enabled on this system — no setup needed."
	echo "The RAM File System kernel module will load without any extra steps."
	exit 0
fi

if ! command -v mokutil >/dev/null 2>&1; then
	cat >&2 <<EOF
Secure Boot is enabled, and enrolling a key needs 'mokutil', which isn't
installed. Install it and run this wizard again:

  Debian/Ubuntu:  sudo apt install mokutil
  RHEL/Fedora:    sudo dnf install mokutil
  openSUSE:       sudo zypper install mokutil
EOF
	exit 1
fi

# The certificate DKMS signs with: its own configuration first (a distro's or
# an admin's override), then the two defaults.
dkms_cert() {
	_c=$( (
		mok_certificate=
		for f in /etc/dkms/framework.conf /etc/dkms/framework.conf.d/*.conf; do
			[ -f "$f" ] && . "$f"
		done
		echo "$mok_certificate"
	) 2>/dev/null )
	for _c in "$_c" /var/lib/shim-signed/mok/MOK.der /var/lib/dkms/mok.pub; do
		if [ -n "$_c" ] && [ -f "$_c" ]; then
			echo "$_c"
			return
		fi
	done
}

cat <<EOF
SoftPerfect RAM File System — Secure Boot setup wizard

This will stage the key that signs the RAM File System kernel module for
enrolment in your firmware.

The process takes two reboots:

  1. The key is staged for enrolment (now).
  2. Reboot. Your firmware shows a blue "MOK management" screen — pick
     "Enrol MOK" and enter the password you'll be asked for in a moment.
  3. Reboot again. The kernel module loads, spramfsd starts.

EOF

printf "Continue? [y/N] "
read -r REPLY
case "$REPLY" in
[Yy]*) ;;
*) echo "Aborted."; exit 0 ;;
esac

CERT=$(dkms_cert)
if [ -z "$CERT" ]; then
	# A DKMS that signs nothing yet: give it a key. This is the one case
	# that writes DKMS configuration, and there is no other key to displace.
	PRIV=${KEY_DIR}/mok.priv
	CERT=${KEY_DIR}/mok.der
	mkdir -p "$KEY_DIR"
	chmod 700 "$KEY_DIR"
	echo "Generating a 2048-bit RSA signing key (10-year validity)..."
	openssl req -new -x509 -newkey rsa:2048 -nodes -days 3650 \
		-outform DER -keyout "$PRIV" -out "$CERT" \
		-subj "/CN=SoftPerfect RAM File System DKMS module signing key/" \
		>/dev/null
	chmod 600 "$PRIV"
	mkdir -p "$(dirname "$DKMS_FRAMEWORK")"
	cat > "$DKMS_FRAMEWORK" <<EOF
# Auto-generated by softperfect-ramfs-mok-setup.
# DKMS picks up these paths when (re)building modules under Secure Boot.
mok_signing_key="${PRIV}"
mok_certificate="${CERT}"
EOF
	chmod 644 "$DKMS_FRAMEWORK"
fi
echo "Signing key: $CERT"

# A module built before the key existed carries no signature: rebuild it.
if [ -z "$(modinfo -F signer "$DKMS_NAME" 2>/dev/null)" ]; then
	echo
	echo "Re-building the kernel module so it picks up the signature..."
	dkms remove -m "$DKMS_NAME" -v "$DKMS_VERSION" --all >/dev/null 2>&1 || true
	dkms install -m "$DKMS_NAME" -v "$DKMS_VERSION" 2>&1 | sed 's/^/dkms: /'
fi

if mokutil --test-key "$CERT" 2>/dev/null | grep -q "already enrolled"; then
	echo
	echo "The key is already enrolled — skipping mokutil --import."
	echo "If the module still fails to load, reboot or run dkms install again."
else
	echo
	echo "Staging the public key for enrolment."
	echo "Pick any password — you'll type this once on the MokManager screen"
	echo "after reboot. It's used once and then discarded."
	echo
	mokutil --import "$CERT"
fi

cat <<EOF

Setup staged. Next steps:

  1. Reboot.
  2. On the blue "MOK management" screen, choose "Enrol MOK", then
     "Continue", then enter the password you just set.
  3. After enrolment your machine reboots again. The kernel module then
     loads automatically and spramfsd starts.

If you cancel enrolment on the MokManager screen, the module won't
load — re-run this wizard to stage a fresh import.
EOF
