From 80eb6a5c329a2b17c1e2fc7347ca3a743f8da126 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 2 Aug 2026 04:39:07 +0000 Subject: [PATCH] Build the lockdown LSM into the FOS kernel, without activating it A signed kernel that does not enforce lockdown is a Secure Boot bypass: anyone can boot it on a machine that trusts the signer and then reach the kernel through /dev/mem, iopl() or kexec_load(). shim-review asks about this directly, and it is the largest gap between FOS today and a kernel anyone would sign for general use. CONFIG_SECURITY was not even set, so the lockdown LSM could not be selected at all. This enables it and the platform keyring machinery across all three architectures -- LOAD_UEFI_KEYS being the one that imports the firmware's db and MokList, without which the kernel cannot see the key the shim just validated against. CONFIG_LSM is set explicitly because an LSM absent from the ordered list never initialises, and oldconfig's default string names LSMs this kernel does not build. CONFIG_KEXEC goes away: lockdown blocks kexec_load() unconditionally and FOS never used it. Lockdown is built in but left inactive (FORCE_NONE), so nothing changes for existing users. FORCE_INTEGRITY would have activated it in one line and is the wrong default -- it locks down every boot, including the overwhelming majority that never enable Secure Boot, and lockdown blocks the /dev/mem fallback that hardware inventory can still depend on. Distributions all carry a patch that activates lockdown only when the firmware reports Secure Boot on. That patch is NOT included here, and 0009 records why in detail: both halves of it turn out to be downstream only in 6.18 -- security_lock_kernel_down() is absent from mainline, as is efi_enabled(EFI_SECURE_BOOT) -- so it is a multi-file arch-specific patch. build.sh applies patch/kernel/linux.patch with `patch -p1` and exits on failure, so shipping one that has never been compiled would break every build and take the Intel VMD patch down with it. Because oldconfig silently drops symbols whose dependencies are unmet, a config can look right in git and still produce a kernel with no lockdown in it. tests/checks/secureboot-config.sh asserts the symbols are present, and with -b re-checks the .config Kconfig actually produced after a build. Not built or booted -- no kernel was compiled for this change. Build all three arches and confirm the Realtek NICs and Intel VMD NVMe still work before merging; those are the whole reason FOS carries a custom kernel. --- configs/kernelarm64.config | 43 ++++- configs/kernelx64.config | 45 ++++- configs/kernelx86.config | 45 ++++- docs/adr/0009-secure-boot-kernel-hardening.md | 155 ++++++++++++++++++ tests/checks/secureboot-config.sh | 111 +++++++++++++ 5 files changed, 385 insertions(+), 14 deletions(-) create mode 100644 docs/adr/0009-secure-boot-kernel-hardening.md create mode 100755 tests/checks/secureboot-config.sh diff --git a/configs/kernelarm64.config b/configs/kernelarm64.config index cc80de9..9624fb8 100644 --- a/configs/kernelarm64.config +++ b/configs/kernelarm64.config @@ -3938,11 +3938,35 @@ CONFIG_PROC_MEM_ALWAYS_FORCE=y # CONFIG_PROC_MEM_FORCE_PTRACE is not set # CONFIG_PROC_MEM_NO_FORCE is not set # CONFIG_MSEAL_SYSTEM_MAPPINGS is not set -# CONFIG_SECURITY is not set -# CONFIG_SECURITYFS is not set +CONFIG_SECURITY=y +CONFIG_SECURITYFS=y +# CONFIG_SECURITY_NETWORK is not set +# CONFIG_SECURITY_PATH is not set +CONFIG_LSM_MMAP_MIN_ADDR=65536 +# CONFIG_SECURITY_SELINUX is not set +# CONFIG_SECURITY_SMACK is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_SECURITY_LOADPIN is not set +# CONFIG_SECURITY_YAMA is not set +# CONFIG_SECURITY_SAFESETID is not set +CONFIG_SECURITY_LOCKDOWN_LSM=y +CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y +CONFIG_LOCK_DOWN_KERNEL_FORCE_NONE=y +# CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY is not set +# CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY is not set +# CONFIG_SECURITY_LANDLOCK is not set +CONFIG_INTEGRITY=y +CONFIG_INTEGRITY_SIGNATURE=y +CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y +CONFIG_INTEGRITY_PLATFORM_KEYRING=y +CONFIG_LOAD_UEFI_KEYS=y +# CONFIG_IMA is not set +# CONFIG_EVM is not set # CONFIG_STATIC_USERMODEHELPER is not set # CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_LSM="lockdown,integrity" # # Kernel hardening options @@ -4162,12 +4186,23 @@ CONFIG_CRYPTO_JITTERENTROPY_OSR=1 # end of Accelerated Cryptographic Algorithms for CPU (arm64) # CONFIG_CRYPTO_HW is not set -# CONFIG_ASYMMETRIC_KEY_TYPE is not set +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_X509_CERTIFICATE_PARSER=y +CONFIG_PKCS7_MESSAGE_PARSER=y +# CONFIG_PKCS8_PRIVATE_KEY_PARSER is not set +# CONFIG_SIGNED_PE_FILE_VERIFICATION is not set # # Certificates for signature checking # -# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set +CONFIG_SYSTEM_TRUSTED_KEYRING=y +CONFIG_SYSTEM_TRUSTED_KEYS="" +# CONFIG_SYSTEM_EXTRA_CERTIFICATE is not set +CONFIG_SECONDARY_TRUSTED_KEYRING=y +CONFIG_SYSTEM_BLACKLIST_KEYRING=y +CONFIG_SYSTEM_BLACKLIST_HASH_LIST="" +# CONFIG_SYSTEM_REVOCATION_LIST is not set # end of Certificates for signature checking # CONFIG_CRYPTO_KRB5 is not set diff --git a/configs/kernelx64.config b/configs/kernelx64.config index e3d118a..d456e97 100644 --- a/configs/kernelx64.config +++ b/configs/kernelx64.config @@ -246,7 +246,7 @@ CONFIG_PERF_EVENTS=y # Kexec and crash features # CONFIG_KEXEC_CORE=y -CONFIG_KEXEC=y +# CONFIG_KEXEC is not set # CONFIG_KEXEC_FILE is not set # CONFIG_KEXEC_HANDOVER is not set # CONFIG_CRASH_DUMP is not set @@ -3936,12 +3936,36 @@ CONFIG_PROC_MEM_ALWAYS_FORCE=y # CONFIG_PROC_MEM_FORCE_PTRACE is not set # CONFIG_PROC_MEM_NO_FORCE is not set # CONFIG_MSEAL_SYSTEM_MAPPINGS is not set -# CONFIG_SECURITY is not set -# CONFIG_SECURITYFS is not set +CONFIG_SECURITY=y +CONFIG_SECURITYFS=y +# CONFIG_SECURITY_NETWORK is not set +# CONFIG_SECURITY_PATH is not set +CONFIG_LSM_MMAP_MIN_ADDR=65536 +# CONFIG_SECURITY_SELINUX is not set +# CONFIG_SECURITY_SMACK is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_SECURITY_LOADPIN is not set +# CONFIG_SECURITY_YAMA is not set +# CONFIG_SECURITY_SAFESETID is not set +CONFIG_SECURITY_LOCKDOWN_LSM=y +CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y +CONFIG_LOCK_DOWN_KERNEL_FORCE_NONE=y +# CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY is not set +# CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY is not set +# CONFIG_SECURITY_LANDLOCK is not set +CONFIG_INTEGRITY=y +CONFIG_INTEGRITY_SIGNATURE=y +CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y +CONFIG_INTEGRITY_PLATFORM_KEYRING=y +CONFIG_LOAD_UEFI_KEYS=y +# CONFIG_IMA is not set +# CONFIG_EVM is not set CONFIG_INTEL_TXT=y # CONFIG_STATIC_USERMODEHELPER is not set # CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_LSM="lockdown,integrity" # # Kernel hardening options @@ -4168,12 +4192,23 @@ CONFIG_CRYPTO_AES_NI_INTEL=y # end of Accelerated Cryptographic Algorithms for CPU (x86) # CONFIG_CRYPTO_HW is not set -# CONFIG_ASYMMETRIC_KEY_TYPE is not set +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_X509_CERTIFICATE_PARSER=y +CONFIG_PKCS7_MESSAGE_PARSER=y +# CONFIG_PKCS8_PRIVATE_KEY_PARSER is not set +# CONFIG_SIGNED_PE_FILE_VERIFICATION is not set # # Certificates for signature checking # -# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set +CONFIG_SYSTEM_TRUSTED_KEYRING=y +CONFIG_SYSTEM_TRUSTED_KEYS="" +# CONFIG_SYSTEM_EXTRA_CERTIFICATE is not set +CONFIG_SECONDARY_TRUSTED_KEYRING=y +CONFIG_SYSTEM_BLACKLIST_KEYRING=y +CONFIG_SYSTEM_BLACKLIST_HASH_LIST="" +# CONFIG_SYSTEM_REVOCATION_LIST is not set # end of Certificates for signature checking # CONFIG_CRYPTO_KRB5 is not set diff --git a/configs/kernelx86.config b/configs/kernelx86.config index 6767015..ad35a78 100755 --- a/configs/kernelx86.config +++ b/configs/kernelx86.config @@ -240,7 +240,7 @@ CONFIG_PERF_EVENTS=y # Kexec and crash features # CONFIG_KEXEC_CORE=y -CONFIG_KEXEC=y +# CONFIG_KEXEC is not set # CONFIG_CRASH_DUMP is not set # end of Kexec and crash features # end of General setup @@ -3844,11 +3844,35 @@ CONFIG_KEYS=y CONFIG_PROC_MEM_ALWAYS_FORCE=y # CONFIG_PROC_MEM_FORCE_PTRACE is not set # CONFIG_PROC_MEM_NO_FORCE is not set -# CONFIG_SECURITY is not set -# CONFIG_SECURITYFS is not set +CONFIG_SECURITY=y +CONFIG_SECURITYFS=y +# CONFIG_SECURITY_NETWORK is not set +# CONFIG_SECURITY_PATH is not set +CONFIG_LSM_MMAP_MIN_ADDR=65536 +# CONFIG_SECURITY_SELINUX is not set +# CONFIG_SECURITY_SMACK is not set +# CONFIG_SECURITY_TOMOYO is not set +# CONFIG_SECURITY_APPARMOR is not set +# CONFIG_SECURITY_LOADPIN is not set +# CONFIG_SECURITY_YAMA is not set +# CONFIG_SECURITY_SAFESETID is not set +CONFIG_SECURITY_LOCKDOWN_LSM=y +CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y +CONFIG_LOCK_DOWN_KERNEL_FORCE_NONE=y +# CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY is not set +# CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY is not set +# CONFIG_SECURITY_LANDLOCK is not set +CONFIG_INTEGRITY=y +CONFIG_INTEGRITY_SIGNATURE=y +CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y +CONFIG_INTEGRITY_PLATFORM_KEYRING=y +CONFIG_LOAD_UEFI_KEYS=y +# CONFIG_IMA is not set +# CONFIG_EVM is not set # CONFIG_STATIC_USERMODEHELPER is not set # CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_LSM="lockdown,integrity" # # Kernel hardening options @@ -4053,12 +4077,23 @@ CONFIG_CRYPTO_AES_NI_INTEL=y # end of Accelerated Cryptographic Algorithms for CPU (x86) # CONFIG_CRYPTO_HW is not set -# CONFIG_ASYMMETRIC_KEY_TYPE is not set +CONFIG_ASYMMETRIC_KEY_TYPE=y +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y +CONFIG_X509_CERTIFICATE_PARSER=y +CONFIG_PKCS7_MESSAGE_PARSER=y +# CONFIG_PKCS8_PRIVATE_KEY_PARSER is not set +# CONFIG_SIGNED_PE_FILE_VERIFICATION is not set # # Certificates for signature checking # -# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set +CONFIG_SYSTEM_TRUSTED_KEYRING=y +CONFIG_SYSTEM_TRUSTED_KEYS="" +# CONFIG_SYSTEM_EXTRA_CERTIFICATE is not set +CONFIG_SECONDARY_TRUSTED_KEYRING=y +CONFIG_SYSTEM_BLACKLIST_KEYRING=y +CONFIG_SYSTEM_BLACKLIST_HASH_LIST="" +# CONFIG_SYSTEM_REVOCATION_LIST is not set # end of Certificates for signature checking # CONFIG_CRYPTO_KRB5 is not set diff --git a/docs/adr/0009-secure-boot-kernel-hardening.md b/docs/adr/0009-secure-boot-kernel-hardening.md new file mode 100644 index 0000000..f1a1850 --- /dev/null +++ b/docs/adr/0009-secure-boot-kernel-hardening.md @@ -0,0 +1,155 @@ +# 0009 — Secure Boot kernel hardening, and why lockdown is not yet activated + +## Status + +Partially implemented. The kernel configuration is in place; the code that +*activates* lockdown when Secure Boot is on is deliberately not, and the reason +is recorded below so the next person does not have to rediscover it. + +## Context + +A site that mandates UEFI Secure Boot must sign the FOS kernel with its own key +and enrol that key per machine. FOG automates the signing +(`build.sh --sign-key`, plus the installer side in FOGProject/fogproject), but +signing only gets the kernel *loaded*. It says nothing about what the kernel +then permits. + +The reason that matters is not academic. If FOG ever wants its own +Microsoft-signed shim — the only route to Secure Boot working out of the box, +rather than after a per-machine visit — `rhboot/shim-review` asks directly: + +> How does your signed kernel enforce lockdown when your system runs with +> Secure Boot enabled? + +A signed kernel that does not enforce lockdown is a Secure Boot bypass: anyone +can boot it on any machine that trusts the signer and then use `/dev/mem`, +`iopl()` or `kexec_load()` to do whatever they like to a kernel the firmware +just vouched for. That is why reviewers ask, and it is the single largest gap +between FOS today and a kernel anyone would sign for general use. + +Two things about FOS make this easier than it usually is: + +- **There are no modules.** `# CONFIG_MODULES is not set`; the config is 1829 + `=y` and zero `=m`. The out-of-tree Realtek drivers are not external modules + either — `addKernelPackages()` copies them into the source tree and appends + to `Kconfig`/`Makefile`, so they are built in and covered by the kernel's own + signature. This answers shim-review's other recurring question, about + ephemeral per-build module-signing keys, with "we build no modules". +- **`CONFIG_EFI_STUB=y` already.** Under Secure Boot the kernel is started by + the firmware's loader rather than iPXE's, which requires the stub. + +## Decision + +Enable the lockdown LSM and the platform keyring infrastructure in all three +architecture configs, but **do not force lockdown on**: + +- `CONFIG_SECURITY=y`, `CONFIG_SECURITYFS=y` — neither was set, so the lockdown + LSM could not even be selected. +- `CONFIG_SECURITY_LOCKDOWN_LSM=y` and `_EARLY=y`. The early variant matters + because some boot parameters are parsed before LSM init would otherwise run. +- `CONFIG_LSM="lockdown,integrity"`. An LSM that is built but absent from the + ordered list never initialises. Set explicitly rather than left for + `oldconfig` to default, because the upstream default string names LSMs this + kernel does not build. +- `CONFIG_LOCK_DOWN_KERNEL_FORCE_NONE=y` — see below. +- `CONFIG_ASYMMETRIC_KEY_TYPE=y` and the keyrings + (`SYSTEM_TRUSTED_KEYRING`, `SECONDARY_TRUSTED_KEYRING`, + `SYSTEM_BLACKLIST_KEYRING`, `INTEGRITY_PLATFORM_KEYRING`, + `LOAD_UEFI_KEYS`). `LOAD_UEFI_KEYS` is the one that imports the firmware's + `db` and `MokList` into the platform keyring; without it the kernel cannot + see the key the shim just validated against. +- `# CONFIG_KEXEC is not set`. The old `kexec_load()` syscall is + unconditionally blocked by lockdown and FOS never uses it, so removing it + beats leaving it to be refused at runtime. + +### Why not `LOCK_DOWN_KERNEL_FORCE_INTEGRITY` + +It would be one line and it would activate lockdown today. It is the wrong +default anyway, because it locks the kernel down on **every** boot — including +the overwhelming majority of FOG deployments that never turn Secure Boot on. +Lockdown blocks `/dev/mem`, `/dev/port`, `iopl`/`ioperm`, raw PCI BAR access +and direct MSR access. FOS's hardware inventory is the obvious thing at risk: +modern `dmidecode` prefers `/sys/firmware/dmi/tables/`, but anything that falls +back to `/dev/mem` would start failing for users who gained nothing in return. + +Distributions all resolve this the same way: build the LSM in, leave it +inactive, and activate it at boot **only when the firmware reports Secure Boot +is on**. That is the behaviour FOS wants. + +## The part that is not done, and what it actually takes + +Activating lockdown from the Secure Boot state is **not upstream**. It is a +downstream patch every distribution carries, and the investigation below is +recorded because the shape of it is not obvious from the outside: + +- `security_lock_kernel_down()` — the function Fedora's and Ubuntu's patches + call — **does not exist in mainline 6.18**. `include/linux/security.h` + exports only `security_locked_down()`, the query. The distro patches add the + setter to the LSM infrastructure, touching `include/linux/security.h`, + `security/security.c` and `include/linux/lsm_hook_defs.h`. +- `efi_enabled(EFI_SECURE_BOOT)` **also does not exist in mainline 6.18**; + `EFI_SECURE_BOOT` is a downstream flag. Upstream x86 keeps the state in + `boot_params.secure_boot` and, in `setup_arch()` + (`arch/x86/kernel/setup.c`), only *prints* it: + + ```c + if (efi_enabled(EFI_BOOT)) { + switch (boot_params.secure_boot) { + case efi_secureboot_mode_enabled: + pr_info("Secure boot enabled\n"); + ``` + + That `case` is exactly where the distro patches insert the lockdown call. +- A smaller alternative exists: `lockdown_lsm_init()` in + `security/lockdown/lockdown.c` already calls a **file-local** + `lock_kernel_down()` for the FORCE_* options, so a single-file patch there + could activate lockdown without touching LSM infrastructure at all. It still + needs an architecture-neutral way to read the Secure Boot state at LSM init + time, which is the unsolved part. + +This was left unwritten on purpose. `build.sh` applies `patch/kernel/linux.patch` +with `patch -p1` and **exits non-zero if it fails**, so a patch that does not +apply cleanly breaks every build for everyone, and takes the Intel VMD patch +down with it. A multi-file, architecture-specific kernel patch that has never +been compiled or booted does not belong in that file. Whoever picks this up +should build and boot it first. + +### Settle the initrd question before going further + +FOS boots as `bzImage` plus a network-supplied kernel command line (`mode=`, +`type=`) plus an unsigned ext2 `init.xz`. shim-review does not literally +mandate a Unified Kernel Image — lockdown in integrity mode is the accepted +mitigation, and UKIs are mentioned only in passing — but for a payload shaped +like this one a reviewer will push hard, because a signed kernel that accepts +an arbitrary command line and an unverified initrd is most of the way back to +the bypass lockdown is supposed to prevent. + +That question is worth answering **before** anyone writes the lockdown patch. +If FOG cannot move to a UKI, an application for its own shim probably is not +winnable, and the patch buys nothing on its own. + +## Consequences + +- The configs are inert for now: lockdown is compiled in but never activated, + so behaviour is unchanged for every existing user. That is the point — this + lands the reviewable, testable half without a flag day. +- `CONFIG_SECURITY=y` pulls a lot of new Kconfig into the build. The three + configs are hand-edited and `make oldconfig` **silently drops symbols whose + dependencies are not met**, so a config can look right in git and produce a + kernel that is missing lockdown entirely. `tests/checks/secureboot-config.sh` + asserts the symbols are present, and with `-b` re-checks the `.config` + Kconfig actually produced after a build. Run it with `-b` after building. +- **This has not been built or booted.** No kernel was compiled while making + this change. Before merging: build all three architectures, boot with Secure + Boot off and image a machine end to end, and confirm the Realtek NICs and + Intel VMD NVMe still work — those two are the entire reason FOS carries a + custom kernel, and `CONFIG_SECURITY`/keyring churn is exactly the kind of + change that could disturb them. + +## References + +- `rhboot/shim-review` — +- Lockdown LSM — , + `man 7 kernel_lockdown` +- ADR 0003, for the "fail loud rather than continue" principle this follows in + refusing to ship an untested patch. diff --git a/tests/checks/secureboot-config.sh b/tests/checks/secureboot-config.sh new file mode 100755 index 0000000..a6af705 --- /dev/null +++ b/tests/checks/secureboot-config.sh @@ -0,0 +1,111 @@ +#!/bin/bash +# +# Assertion harness for the Secure Boot hardening symbols in configs/kernel*.config. +# +# tests/checks/secureboot-config.sh # check the committed configs +# tests/checks/secureboot-config.sh -b # also check post-oldconfig .config +# # in any kernelsource/ present +# +# Why this exists. build.sh copies configs/kernel.config to .config and +# runs `make oldconfig`, which SILENTLY DROPS any symbol whose dependencies are +# not met. A hand-edited config can therefore look completely correct in git and +# still produce a kernel with lockdown missing, and nothing anywhere says so. +# That failure would only surface as a Secure Boot client behaving differently +# from the one you tested. See docs/adr/0009-secure-boot-kernel-hardening.md +# +# The -b mode is the one that actually proves anything, because it inspects the +# config Kconfig produced rather than the one we wrote. Run it after a build. + +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO="$HERE/../.." + +checkBuilt=0 +[[ ${1:-} == -b ]] && checkBuilt=1 + +# Symbols that must be present and enabled for a Secure Boot capable kernel. +# LOAD_UEFI_KEYS is what imports the firmware's db/MokList into the platform +# keyring; without it the kernel cannot see the key the shim validated against. +REQUIRED=( + CONFIG_SECURITY=y + CONFIG_SECURITYFS=y + CONFIG_SECURITY_LOCKDOWN_LSM=y + CONFIG_SECURITY_LOCKDOWN_LSM_EARLY=y + CONFIG_INTEGRITY=y + CONFIG_INTEGRITY_SIGNATURE=y + CONFIG_INTEGRITY_ASYMMETRIC_KEYS=y + CONFIG_INTEGRITY_PLATFORM_KEYRING=y + CONFIG_LOAD_UEFI_KEYS=y + CONFIG_ASYMMETRIC_KEY_TYPE=y + CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y + CONFIG_X509_CERTIFICATE_PARSER=y + CONFIG_PKCS7_MESSAGE_PARSER=y + CONFIG_SYSTEM_TRUSTED_KEYRING=y + CONFIG_SECONDARY_TRUSTED_KEYRING=y + CONFIG_SYSTEM_BLACKLIST_KEYRING=y + CONFIG_EFI_STUB=y +) + +# Symbols that must NOT be enabled. kexec_load() is unconditionally blocked by +# lockdown and FOS never uses it. FORCE_INTEGRITY would lock the kernel down on +# every boot, including the overwhelming majority that never turn Secure Boot +# on -- see the ADR for why that is the wrong default here. +FORBIDDEN=( + CONFIG_KEXEC=y + CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY=y + CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY=y +) + +fails=0 +checked=0 + +checkConfig() { + local label="$1" file="$2" sym + [[ -f $file ]] || return 0 + checked=$((checked + 1)) + for sym in "${REQUIRED[@]}"; do + if ! grep -qxF "$sym" "$file"; then + echo "FAIL [$label] missing: $sym" + fails=$((fails + 1)) + fi + done + for sym in "${FORBIDDEN[@]}"; do + if grep -qxF "$sym" "$file"; then + echo "FAIL [$label] must not be set: $sym" + fails=$((fails + 1)) + fi + done + # The lockdown LSM only initialises if it is in the ordered LSM list. + if ! grep -q '^CONFIG_LSM=.*lockdown' "$file"; then + echo "FAIL [$label] CONFIG_LSM does not include lockdown" + fails=$((fails + 1)) + fi + echo " checked $label" +} + +for arch in x64 x86 arm64; do + checkConfig "configs/kernel${arch}.config" "$REPO/configs/kernel${arch}.config" +done + +if [[ $checkBuilt -eq 1 ]]; then + built=0 + for arch in x64 x86 arm64; do + f="$REPO/kernelsource${arch}/.config" + if [[ -f $f ]]; then + built=1 + checkConfig "kernelsource${arch}/.config (post-oldconfig)" "$f" + fi + done + if [[ $built -eq 0 ]]; then + echo + echo "NOTE: -b given but no kernelsource/.config found -- nothing" + echo " post-oldconfig was checked. Build first, then re-run." + fi +fi + +echo +if [[ $fails -eq 0 ]]; then + echo "PASS: $checked config(s) carry the Secure Boot hardening symbols" + exit 0 +fi +echo "FAILED: $fails problem(s) across $checked config(s)" +exit 1