#!/usr/bin/env bash
# fleet-sb-enroll — enrol the fleet's custom Secure Boot keys (PK/KEK/db) from the
# baked PUBLIC .auth files into firmware, via efivarfs.
#
# EL9 ships no efitools (no efi-updatevar), so this is the OS-side equivalent: it
# prepends the EFI authenticated-variable attribute mask to each signed .auth
# payload and writes it to /sys/firmware/efi/efivars in a single write(), in the
# order db -> KEK -> PK (PK last leaves Setup Mode and turns Secure Boot on).
#
# Run as root over SSH on a workstation that is in Secure Boot SETUP MODE. Only
# PUBLIC material is used (the .auth files carry certs + signatures, never the
# PK/KEK/db private keys, which stay offline/HSM — see docs/secure-boot-keys.md).
#
# Usage:
#   fleet-sb-enroll --check     # print SB state + which keys are baked; do nothing
#   fleet-sb-enroll             # enrol db, KEK, PK (PK last) — requires Setup Mode
#   fleet-sb-enroll --force     # attempt even if not in Setup Mode (User-Mode
#                               # writes only succeed if each .auth is signed by
#                               # the in-place parent key)
#
# NOTE: this is a deliberate trust-bootstrap action. In Setup Mode the firmware
# accepts these writes unauthenticated, so only run it on a host you control, on a
# trusted/isolated network. See docs/TODO.md #10 for the auto-enrol risk model.
set -euo pipefail

KEYDIR=/usr/share/fleet-secureboot
EFIVARS=/sys/firmware/efi/efivars
GUID_GLOBAL=8be4df61-93ca-11d2-aa0d-00e098032b8c   # PK, KEK, SetupMode
GUID_DB=d719b2cb-3d3a-4596-a3bc-dad00e67656f       # db, dbx
# EFI var attributes: NON_VOLATILE|BOOTSERVICE_ACCESS|RUNTIME_ACCESS|
# TIME_BASED_AUTHENTICATED_WRITE_ACCESS = 0x27, written as a 4-byte little-endian
# u32 prefixed to the .auth payload. These 4 bytes contain NULs, so they are
# emitted with printf at WRITE TIME, never stored in a variable: a bash string
# can't hold a NUL (it truncates at the first one), which would leave only the
# 0x27 byte, corrupt the 4-byte attribute field efivarfs reads, and make the
# kernel reject the write with EINVAL ("dd: Invalid argument").

die() { echo "fleet-sb-enroll: $*" >&2; exit 1; }

[ "$(id -u)" = 0 ] || die "must run as root (use sudo)"
[ -d "$EFIVARS" ] || die "efivarfs not present ($EFIVARS) — not a UEFI system / not booted via UEFI?"

setup_mode() {  # echoes yes|no|unknown
  local f="$EFIVARS/SetupMode-$GUID_GLOBAL"
  [ -e "$f" ] || { echo unknown; return; }
  # 4 attribute bytes then 1 data byte: 1 = Setup Mode, 0 = User Mode.
  if [ "$(od -An -tu1 -j4 -N1 "$f" | tr -d ' ')" = 1 ]; then echo yes; else echo no; fi
}

show_state() {
  echo "Secure Boot:  $(mokutil --sb-state 2>/dev/null | tr '\n' ' ' || echo '?')"
  echo "Setup Mode:   $(setup_mode)"
  echo "Baked keys ($KEYDIR):"
  local k
  for k in db KEK PK; do
    if [ -f "$KEYDIR/$k.auth" ]; then echo "  $k.auth  present"; else echo "  $k.auth  MISSING"; fi
  done
}

enrol_one() {  # $1=var name  $2=guid  $3=.auth path
  local f="$EFIVARS/$1-$2" tmp
  [ -f "$3" ] || die "missing $3"
  tmp=$(mktemp)
  printf '\x27\x00\x00\x00' > "$tmp"   # 4-byte LE attribute prefix (see note above)
  cat "$3" >> "$tmp"
  # Existing EFI vars carry the immutable flag; clear it before overwriting.
  [ -e "$f" ] && chattr -i "$f" 2>/dev/null || true
  # efivarfs requires the whole variable in ONE write() — dd, single block.
  dd if="$tmp" of="$f" bs="$(stat -c%s "$tmp")" count=1 status=none \
    || { rm -f "$tmp"; die "write to '$1' failed (in Setup Mode? .auth signed by the right parent key?)"; }
  rm -f "$tmp"
  echo "  enrolled $1"
}

FORCE=0
case "${1:-}" in
  --check) show_state; exit 0 ;;
  --force) FORCE=1 ;;
  "")      FORCE=0 ;;
  *)       die "usage: fleet-sb-enroll [--check|--force]" ;;
esac

# Require all three baked keys.
for k in db KEK PK; do
  [ -f "$KEYDIR/$k.auth" ] || die "no baked keys at $KEYDIR/$k.auth — supply public PK/KEK/db .auth via the build (files/secureboot/keys/)"
done

if [ "$(setup_mode)" != yes ] && [ "$FORCE" != 1 ]; then
  show_state
  die "not in Setup Mode — clear/reset Secure Boot keys in firmware first, or pass --force"
fi

echo "Enrolling fleet Secure Boot keys (order: db -> KEK -> PK)..."
enrol_one db  "$GUID_DB"     "$KEYDIR/db.auth"
enrol_one KEK "$GUID_GLOBAL" "$KEYDIR/KEK.auth"
enrol_one PK  "$GUID_GLOBAL" "$KEYDIR/PK.auth"   # PK last -> exits Setup Mode
echo "Done."
echo "Verify with:  mokutil --db   and   mokutil --sb-state"
