diff --git a/Buildroot/board/FOG/FOS/rootfs_overlay/bin/fog.checkin b/Buildroot/board/FOG/FOS/rootfs_overlay/bin/fog.checkin index 713c4ec..6a0de83 100755 --- a/Buildroot/board/FOG/FOS/rootfs_overlay/bin/fog.checkin +++ b/Buildroot/board/FOG/FOS/rootfs_overlay/bin/fog.checkin @@ -58,7 +58,7 @@ checkin() { # must keep waiting however long that takes. What was NOT deliberate is that # a dead server looked identical to a queue position -- $res was empty, so # this printed " * (In line for 5s)" forever with nothing to diagnose. - # serverReason distinguishes them; the waiting behaviour is unchanged. + # serverReason distinguishes them; the waiting behavior is unchanged. while [[ $res != "##@GO" ]]; do callServer "${web}service/$php_post" "$poststring" res="$serverBody" diff --git a/Buildroot/board/FOG/FOS/rootfs_overlay/bin/fog.enrollsb b/Buildroot/board/FOG/FOS/rootfs_overlay/bin/fog.enrollsb index 0281638..634d682 100755 --- a/Buildroot/board/FOG/FOS/rootfs_overlay/bin/fog.enrollsb +++ b/Buildroot/board/FOG/FOS/rootfs_overlay/bin/fog.enrollsb @@ -41,10 +41,10 @@ esac # nothing signed to boot, irreversibly from the client's side. # # Checked before the certificate fetch and before the already-trusted -# short-circuit: no db contents make enrolment useful here, so there is no state +# short-circuit: no db contents make enrollment useful here, so there is no state # in which continuing tells the admin something more useful than this does. if [[ $(sbPlatformBits) == 32 ]]; then - handleError "This machine has 32-bit UEFI firmware, which FOG cannot enrol. ($0)\n No Microsoft-signed 32-bit shim and no signed 32-bit iPXE exist, so it\n cannot boot FOG under Secure Boot enforcement even with this key trusted.\n Nothing was enrolled and nothing was changed. Leave Secure Boot off here." + handleError "This machine has 32-bit UEFI firmware, which FOG cannot enroll. ($0)\n No Microsoft-signed 32-bit shim and no signed 32-bit iPXE exist, so it\n cannot boot FOG under Secure Boot enforcement even with this key trusted.\n Nothing was enrolled and nothing was changed. Leave Secure Boot off here." fi dots "Fetching certificate" @@ -93,12 +93,12 @@ if [[ $state == setup ]]; then echo "Failed" debugPause # Deliberately spells out the state the machine is left in. A partial - # enrolment stops before the PK write, so the platform is still in Setup + # enrollment stops before the PK write, so the platform is still in Setup # Mode and still boots exactly what it booted before -- that is worth - # saying, because "Secure Boot enrolment failed" otherwise reads like + # saying, because "Secure Boot enrollment failed" otherwise reads like # the machine may now be unbootable, and someone will make an # unnecessary trip to the firmware screen to check. - handleError "Automatic Secure Boot enrolment failed. ($0)\n This machine is still in Setup Mode and still boots as it did before;\n nothing has been made unbootable.\n Check that ${web}service/secureboot/ serves PK.auth, KEK.auth and db.auth --\n the FOG server needs efitools installed to produce them." + handleError "Automatic Secure Boot enrollment failed. ($0)\n This machine is still in Setup Mode and still boots as it did before;\n nothing has been made unbootable.\n Check that ${web}service/secureboot/ serves PK.auth, KEK.auth and db.auth --\n the FOG server needs efitools installed to produce them." fi echo "Done" debugPause @@ -126,18 +126,18 @@ if [[ -z $password ]]; then handleError "Could not determine a one-time password for the MOK request. ($0)" fi -dots "Staging MOK enrolment request" +dots "Staging MOK enrollment request" if ! sbStageMok "$cert" "$password"; then echo "Failed" debugPause - handleError "The MOK enrolment request was NOT staged. ($0)\n Do not reboot expecting a MokManager prompt; nothing is pending.\n See the messages above for what ran." + handleError "The MOK enrollment request was NOT staged. ($0)\n Do not reboot expecting a MokManager prompt; nothing is pending.\n See the messages above for what ran." fi echo "Done" debugPause echo echo "==============================================================================" -echo " A Secure Boot enrolment request is now pending on this machine." +echo " A Secure Boot enrollment request is now pending on this machine." echo echo " ONE-TIME PASSWORD: $password" echo diff --git a/Buildroot/board/FOG/FOS/rootfs_overlay/bin/fog.enrollsbforce b/Buildroot/board/FOG/FOS/rootfs_overlay/bin/fog.enrollsbforce index 87139f4..b184616 100755 --- a/Buildroot/board/FOG/FOS/rootfs_overlay/bin/fog.enrollsbforce +++ b/Buildroot/board/FOG/FOS/rootfs_overlay/bin/fog.enrollsbforce @@ -66,7 +66,7 @@ dots "Enrolling Secure Boot keys (forced)" if ! sbEnrollDb; then echo "Failed" debugPause - handleError "[TEST] Forced Secure Boot enrolment failed. ($0)\n Firmware state was: $state\n Expected on a machine whose currently-enrolled PK/KEK do not match\n FOG's -- the write is rejected rather than applied partially.\n Check that ${web}service/secureboot/ serves PK.auth, KEK.auth and db.auth." + handleError "[TEST] Forced Secure Boot enrollment failed. ($0)\n Firmware state was: $state\n Expected on a machine whose currently-enrolled PK/KEK do not match\n FOG's -- the write is rejected rather than applied partially.\n Check that ${web}service/secureboot/ serves PK.auth, KEK.auth and db.auth." fi echo "Done" debugPause diff --git a/Buildroot/board/FOG/FOS/rootfs_overlay/usr/share/fog/lib/funcs.sh b/Buildroot/board/FOG/FOS/rootfs_overlay/usr/share/fog/lib/funcs.sh index 9d7a7bc..609a9e2 100644 --- a/Buildroot/board/FOG/FOS/rootfs_overlay/usr/share/fog/lib/funcs.sh +++ b/Buildroot/board/FOG/FOS/rootfs_overlay/usr/share/fog/lib/funcs.sh @@ -2517,7 +2517,7 @@ wipeDisk() { # Validate the mode before dispatching anywhere: an unknown or empty mode is # a malformed task, and guessing a destructive action on one is its own # hazard. Must stay ahead of the nvme branch below, which would otherwise - # erase on any mode it doesn't recognise. + # erase on any mode it doesn't recognize. case $mode in fast|normal|full) ;; *) diff --git a/Buildroot/board/FOG/FOS/rootfs_overlay/usr/share/fog/lib/procsfdisk.awk b/Buildroot/board/FOG/FOS/rootfs_overlay/usr/share/fog/lib/procsfdisk.awk index f01809d..86b38f0 100755 --- a/Buildroot/board/FOG/FOS/rootfs_overlay/usr/share/fog/lib/procsfdisk.awk +++ b/Buildroot/board/FOG/FOS/rootfs_overlay/usr/share/fog/lib/procsfdisk.awk @@ -464,7 +464,7 @@ function fill_disk(partition_names, partitions, args, n, fixed_partitions, origi # Used for extended volumes (logical disks) extended_margin = 2; # The "find the next partition" scan below infers a partition's original - # size from where its neighbour starts, so the partition_names traversals + # size from where its neighbor starts, so the partition_names traversals # need a defined order -- ascending partition number, the order the dump # was written in. See by_partition_number(). old_sorted_in = PROCINFO["sorted_in"]; diff --git a/Buildroot/board/FOG/FOS/rootfs_overlay/usr/share/fog/lib/secureboot-funcs.sh b/Buildroot/board/FOG/FOS/rootfs_overlay/usr/share/fog/lib/secureboot-funcs.sh index 8ea6909..3723e3a 100644 --- a/Buildroot/board/FOG/FOS/rootfs_overlay/usr/share/fog/lib/secureboot-funcs.sh +++ b/Buildroot/board/FOG/FOS/rootfs_overlay/usr/share/fog/lib/secureboot-funcs.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Secure Boot enrolment helpers. +# Secure Boot enrollment helpers. # # Deliberately a separate library rather than more of funcs.sh: this shares no # state, no vocabulary and no failure modes with the imaging engine, and @@ -9,8 +9,8 @@ # shim's MokList is a BOOT-SERVICES-ONLY variable, so the running OS cannot # write it -- only MokManager, in boot services, can promote MokNew into # MokList, and it demands the one-time password as proof of physical presence. -# So nothing here "enrols a MOK". It STAGES a request that a human then -# confirms at the MokManager screen. Fully automatic enrolment is the db path +# So nothing here "enrolls a MOK". It STAGES a request that a human then +# confirms at the MokManager screen. Fully automatic enrollment is the db path # (Setup Mode), which is Phase 2 and lands beside this. # The EFI global variable namespace. SetupMode, SecureBoot, PK and KEK live @@ -95,7 +95,7 @@ sbState() { # # Why FOS cares at all: no Microsoft-signed 32-bit shim and no signed 32-bit # iPXE exist, so there is no Secure Boot chain an ia32 machine can boot. See the -# refusal in bin/fog.enrollsb for what that means for enrolment. +# refusal in bin/fog.enrollsb for what that means for enrollment. # # The kernel has exposed fw_platform_size on every EFI boot since 4.14 and FOS # runs 6.x, so on UEFI it is always readable. "unknown" therefore means a BIOS @@ -231,7 +231,7 @@ sbCertInDb() { # Two stores, because a machine can be trusting this certificate by either # route: the Setup Mode path puts it in db with no MOK entry at all, and the # staged-MOK path puts it in MokList with nothing in db. Missing either one -# sends a technician to a blue screen to re-enrol something already trusted. +# sends a technician to a blue screen to re-enroll something already trusted. # # THIS FUNCTION GOT IT WRONG ONCE, AND ONLY HARDWARE CAUGHT IT. It used to grep # `mokutil --db` for the certificate's SHA-256. mokutil prints a **SHA1** @@ -258,7 +258,7 @@ sbCertTrusted() { esac return 1 } -# Stage a MOK enrolment request, without prompting. +# Stage a MOK enrollment request, without prompting. # # mokutil normally reads the one-time password from the terminal, which is # useless in a task. --generate-hash= prints the SHA-512 crypt string @@ -269,7 +269,7 @@ sbCertTrusted() { # # The password is NOT a secret. It authenticates nothing at rest; it exists so # that whoever answers MokManager after the reboot is demonstrably the same -# person who asked for the enrolment. It therefore has to be shown to the +# person who asked for the enrollment. It therefore has to be shown to the # technician, which is the caller's job. # # $1 path to the DER certificate @@ -408,7 +408,7 @@ sbWriteEfiAuthVar() { [[ -s $path ]] || return 1 return 0 } -# Enrol this server's certificate into the platform's Secure Boot databases. +# Enroll this server's certificate into the platform's Secure Boot databases. # # Order is db, then KEK, then PK, and it is not interchangeable. Writing PK is # what takes the platform OUT of Setup Mode; from that moment every further @@ -426,7 +426,7 @@ sbWriteEfiAuthVar() { sbEnrollDb() { local var guid authfile # Fetch all three BEFORE writing any. A download that fails halfway would - # otherwise leave the platform mid-enrolment for no better reason than a + # otherwise leave the platform mid-enrollment for no better reason than a # web server hiccup, and the fetch is free to retry while a partial write # is not. for var in db KEK PK; do diff --git a/CLAUDE.md b/CLAUDE.md index 57b1b3d..7623743 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -227,8 +227,8 @@ and add a new ADR for any similarly hard-to-reverse decision: `--ses` flag must never be issued (it doesn't guarantee erasure); prefer `sanitize` when supported, falling back to `format --ses=1` only when no sanitize is in progress or unrecoverably failed. -- **0009 — Secure Boot enrolment.** shim's `MokList` is a boot-services-only - variable, so the running OS *cannot* enrol a MOK — only MokManager can, behind +- **0009 — Secure Boot enrollment.** shim's `MokList` is a boot-services-only + variable, so the running OS *cannot* enroll a MOK — only MokManager can, behind a physical-presence password. `secureboot-funcs.sh`/`fog.enrollsb` therefore **stage** a request and must never report that they enrolled anything. The automatable path is writing `db` while the platform is in **Setup Mode**; note @@ -247,7 +247,7 @@ and add a new ADR for any similarly hard-to-reverse decision: (`CONFIG_LOCK_DOWN_KERNEL_FORCE_NONE=y`) — activating it is downstream-only work gated on the vendor-shim question. `CONFIG_LSM` is set explicitly rather than left to `oldconfig`, because an LSM missing from the ordered list never - initialises, and `CONFIG_LOAD_UEFI_KEYS` is what imports the firmware's `db` + initializes, and `CONFIG_LOAD_UEFI_KEYS` is what imports the firmware's `db` and `MokList` into the platform keyring. The trap this ADR exists for: `make oldconfig` **silently drops** any symbol whose dependencies are unmet, so a config can look right in git and produce a kernel missing lockdown @@ -267,7 +267,7 @@ and add a new ADR for any similarly hard-to-reverse decision: `keymap`, `mdraid`, `chkdsk`, `mc`, `setmacto`) aren't server-known data at all and need their own design pass. See the ADR for the full analysis. - **0012 — Microsoft-signed FOG shim (proposed, unstarted).** The only way to - remove Secure Boot enrolment entirely rather than automate it further — + remove Secure Boot enrollment entirely rather than automate it further — gated on ADR-0011's redesign actually shipping, and on the still-inactive ADR-0010 lockdown patch. `ipxe/shim` cannot be repurposed for this (it trusts exactly one thing, derives its second stage from its own filename, diff --git a/docs/adr/0008-secure-wipe-by-device-class.md b/docs/adr/0008-secure-wipe-by-device-class.md index d6236b0..453b0f1 100644 --- a/docs/adr/0008-secure-wipe-by-device-class.md +++ b/docs/adr/0008-secure-wipe-by-device-class.md @@ -16,7 +16,7 @@ secure erase operation requested"**. That reformats the namespace's LBA metadata and returns in seconds, but the NVMe specification does not require the controller to erase user data. Many drives do deallocate blocks on format, and a deallocated read commonly returns zeros — which is exactly why this looked -correct in testing. That behaviour is implementation-defined per drive, not +correct in testing. That behavior is implementation-defined per drive, not guaranteed, and "reads back as zeros" is not "the data is gone from the NAND". Every NVMe wipe FOG has performed through this path should be assumed non-erasing. @@ -64,7 +64,7 @@ Class comes from `diskClass()`: `*nvme*` by name, otherwise the kernel's say. `unknown` is treated as possibly-flash — it gets the SSD warning — because the failure that matters is assuming flash is a platter, not the reverse. -`fast` is honestly labelled a metadata-only wipe on every non-NVMe class. It +`fast` is honestly labeled a metadata-only wipe on every non-NVMe class. It destroys the partition table so the disk looks blank; it never claims to be a secure erase. On NVMe it is a real erase, because a crypto erase is both instantaneous and complete. @@ -129,7 +129,7 @@ all. The correct primitive is ATA SANITIZE (`hdparm --sanitize-block-erase` / `--sanitize-crypto-scramble`) or ATA Secure Erase. We did not implement it here, and the overwrite remains. Two reasons. First, -removing the existing behaviour without a working replacement would leave SATA +removing the existing behavior without a working replacement would leave SATA SSD users with less than they have today. Second — and this is the substantive one — **the obstacle has already been investigated twice, and it is not merely awkward.** @@ -190,7 +190,7 @@ sanitize-log parse fails 5, and allowing a format fallback after a sanitize has started fails 3. That last control guards a non-obvious ordering constraint found while writing -these tests. `nvmeSecureErase()` treats any mode it does not recognise as +these tests. `nvmeSecureErase()` treats any mode it does not recognize as "neither full nor fast" and issues `format --ses=1`, so with the validation placed after the class dispatch — the natural reading order — an unknown mode refused on `/dev/sda` but *erased* `/dev/nvme0n1`. The mode check must stay ahead diff --git a/docs/adr/0009-secure-boot-enrolment-paths.md b/docs/adr/0009-secure-boot-enrolment-paths.md index 3abd538..3a4b6dc 100644 --- a/docs/adr/0009-secure-boot-enrolment-paths.md +++ b/docs/adr/0009-secure-boot-enrolment-paths.md @@ -1,9 +1,9 @@ -# Secure Boot enrolment: only Setup Mode scales, because FOS cannot bootstrap its own trust +# Secure Boot enrollment: only Setup Mode scales, because FOS cannot bootstrap its own trust FOG generates a Secure Boot signing key by default and signs the FOS kernels with it on every install and upgrade. What it has never had is a way to get that certificate **trusted on more than a handful of machines**. Both existing routes -end at a human at a keyboard: the USB enrolment kit (`fog-enroll-mok.sh` on a +end at a human at a keyboard: the USB enrollment kit (`fog-enroll-mok.sh` on a stock Ubuntu/Debian live image) and PXE menu item 14, which chains MokManager directly and still needs `MOK.der` on local FAT media because MokManager has no network stack. @@ -14,13 +14,13 @@ originally got wrong. ## The governing constraint: trust cannot bootstrap itself -**Whatever performs the enrolment must already be trusted by the firmware.** +**Whatever performs the enrollment must already be trusted by the firmware.** Every viable design is a different answer to "trusted by what?", and every non-viable design is one that forgot to ask. Two independent walls enforce this. -### Wall 1 — MOK enrolment always requires a human +### Wall 1 — MOK enrollment always requires a human `mokutil --import` writes the `MokNew` UEFI variable, which is runtime-accessible. `MokList` — the store shim actually consults when deciding @@ -29,14 +29,14 @@ cannot write it after ExitBootServices. Only MokManager, executing in boot services before the OS starts, can promote `MokNew` into `MokList`, and it demands a one-time password as proof that a human is present. -That is shim's entire security model. If the OS could enrol a key silently, +That is shim's entire security model. If the OS could enroll a key silently, Secure Boot would mean nothing, because the first thing any malware would do is -enrol its own. There is no `--yes` flag and there should not be one. Any future +enroll its own. There is no `--yes` flag and there should not be one. Any future change that appears to have found a way around this has almost certainly found a bug; report it upstream rather than depend on it. **Consequence for this codebase:** `sbStageMok()` is named for what it does. It -stages a request. It does not enrol anything, no message in `fog.enrollsb` may +stages a request. It does not enroll anything, no message in `fog.enrollsb` may claim it did, and the task reports "pending", not "enrolled". #### Rejected: a Microsoft-signed generic UEFI Shell writing `MokList` directly @@ -70,10 +70,10 @@ hashes via `dbx` over time. stop working — or get flagged by security tooling watching for this exact behavior — at a time FOG does not control. -The real, legitimate versions of "close the enrolment gap further" are Path 1 +The real, legitimate versions of "close the enrollment gap further" are Path 1 below (Setup Mode, or a signed update once FOG already owns the platform's `PK`/`KEK`), Path 2 (out-of-band BMC), or — the only way to remove the -enrolment step entirely — FOGProject/fogproject#995, tracked in +enrollment step entirely — FOGProject/fogproject#995, tracked in [ADR-0012](0012-fog-vendor-shim-signed-by-microsoft.md). ### Wall 2 — FOS itself is not loadable until the key is already trusted @@ -103,7 +103,7 @@ loads fine right up to the point where FOG's own artefacts are checked against a MokList that does not yet contain FOG's certificate, and both are refused. So FOS can never be the thing that establishes trust in FOG's key on a machine -that is enforcing Secure Boot. The task that would enrol the key cannot run on +that is enforcing Secure Boot. The task that would enroll the key cannot run on the machine that needs it enrolled. ## The three paths that survive, ranked @@ -162,14 +162,14 @@ silently, so each has a dedicated assertion in the harness: available in this file. All three blobs are downloaded before any is written: a web server hiccup should -cost a retry, not leave a platform mid-enrolment. +cost a retry, not leave a platform mid-enrollment. #### An alternate front-end: user-supplied WinPE -A site that would rather not PXE-boot FOS for the enrolment step — or prefers +A site that would rather not PXE-boot FOS for the enrollment step — or prefers a Windows-native workflow — has another way to drive the exact same mechanism. WinPE boots via Windows Boot Manager, verified directly against Microsoft's -certificates already in `db`. No shim, no `MokList`, nothing to enrol just to +certificates already in `db`. No shim, no `MokList`, nothing to enroll just to get WinPE running. Windows exposes an officially supported equivalent of `sbWriteEfiAuthVar()`/`sbEnrollDb()` for this exact purpose: the `Set-SecureBootUEFI` PowerShell cmdlet, which writes the same signed @@ -245,7 +245,7 @@ run" check. The password is not a secret. It authenticates nothing at rest; it exists so the person answering MokManager is demonstrably the person who requested the -enrolment. `$sbmokpw` sets one password fleet-wide. +enrollment. `$sbmokpw` sets one password fleet-wide. ## Fail loud, per ADR-0003 @@ -265,9 +265,9 @@ into efivarfs that the firmware then declines to apply. `sbEnrollDb()` therefore re-reads `SetupMode` and requires it to have flipped `1 → 0` before reporting success: that is the firmware confirming it accepted the `PK`, and it is the only confirmation available before a reboot (`SecureBoot` stays `0` until the next -POST computes it). An enrolment that failed part-way stops before the `PK` write, +POST computes it). An enrollment that failed part-way stops before the `PK` write, so the machine is still in Setup Mode and still boots whatever it booted before — -`fog.enrollsb` says so explicitly, because "Secure Boot enrolment failed" +`fog.enrollsb` says so explicitly, because "Secure Boot enrollment failed" otherwise reads like the machine may now be unbootable. ## Why the `db` baseline is Microsoft's certificates @@ -281,7 +281,7 @@ recording each source URL and sha256. The decisive reason is **not** Windows compatibility, it is FOG itself. The chain measured above is `shimx64.efi` → signed iPXE → FOG-signed kernel, and that shim is signed by **Microsoft Corporation UEFI CA 2011**. A `db` without that CA -breaks FOG's own Secure Boot PXE boot — we would enrol the key and break the +breaks FOG's own Secure Boot PXE boot — we would enroll the key and break the thing we enrolled it for. **Rejected as the baseline: capturing each machine's factory keyset.** No answer @@ -351,7 +351,7 @@ the firmware afterwards held exactly what it should: - `PK` — this server's PK alone Secure Boot was then switched **on**, and the same machine PXE-booted FOG's -signed chain: `bzImage... ok`, `init.xz... ok`, where before enrolment both were +signed chain: `bzImage... ok`, `init.xz... ok`, where before enrollment both were refused with `Verification failed: Security Policy Violation`. That is the whole feature demonstrated in one line. diff --git a/docs/adr/0010-secure-boot-kernel-hardening.md b/docs/adr/0010-secure-boot-kernel-hardening.md index f98a887..3557d41 100644 --- a/docs/adr/0010-secure-boot-kernel-hardening.md +++ b/docs/adr/0010-secure-boot-kernel-hardening.md @@ -9,7 +9,7 @@ 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 +and enroll 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. @@ -48,7 +48,7 @@ architecture configs, but **do not force lockdown on**: - `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 + ordered list never initializes. 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. @@ -74,7 +74,7 @@ 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. +is on**. That is the behavior FOS wants. ## The part that is not done, and what it actually takes @@ -136,7 +136,7 @@ what it leaves open before implementation can start. ## 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 + so behavior 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 diff --git a/docs/adr/0012-fog-vendor-shim-signed-by-microsoft.md b/docs/adr/0012-fog-vendor-shim-signed-by-microsoft.md index cde6cab..828daf1 100644 --- a/docs/adr/0012-fog-vendor-shim-signed-by-microsoft.md +++ b/docs/adr/0012-fog-vendor-shim-signed-by-microsoft.md @@ -3,7 +3,7 @@ ## Status Proposed. Research recorded, no work started. This is the only route that -removes Secure Boot enrolment entirely rather than automating it further; it +removes Secure Boot enrollment entirely rather than automating it further; it is a multi-month, ongoing-cost undertaking and should be weighed as one before anyone starts. @@ -16,7 +16,7 @@ the right API. All of them exist because FOG's certificate has to become trusted *somewhere*, and only Microsoft's signature is trusted everywhere by default. A shim of FOG's own, signed by Microsoft with FOG's certificate baked in as the vendor certificate, is the only way to make a FOG-signed kernel boot -out of the box on stock hardware with no enrolment step at all. +out of the box on stock hardware with no enrollment step at all. This is tracked upstream as FOGProject/fogproject#995 (successor to the now-closed #962, which did the initial investigation). Nothing here has been @@ -60,7 +60,7 @@ imaging logic in an unsigned initrd. There is no FOS kernel anywhere in that chain, iPXE is dropped entirely, and the project is archived with unfinished docs. It demonstrates that *a* signed chain can be made to boot something, not that FOG's own kernel can be trusted anywhere without either MOK/Setup -Mode enrolment or exactly the shim-signing work this ADR describes. +Mode enrollment or exactly the shim-signing work this ADR describes. ## Remaining checklist, from #995 @@ -91,20 +91,20 @@ the last item does not go away: ## A clarification worth stating explicitly -#962/#995 record that fleet-scale `db` enrolment via firmware tooling is +#962/#995 record that fleet-scale `db` enrollment via firmware tooling is "mostly a dead end" as a *general* answer on stock OEM hardware: `db`/`dbx` updates must be signed by the currently-trusted `PK`/`KEK`, and on unmodified OEM firmware that key belongs to Microsoft or the OEM, not FOG. Dell genuinely exposes programmable custom-mode `PK`/`KEK`/`db` import via Dell Command | Configure and iDRAC; Lenovo's ThinkBIOS Config can clear `PK` into Setup Mode -but unattended cert push is unconfirmed; HP consumer lines have no enrolment +but unattended cert push is unconfirmed; HP consumer lines have no enrollment mode at all, and HP commercial is unconfirmed. This is a **different scenario** from a machine that has already been through ADR-0009 Path 1, where FOG's own `PK`/`KEK` is now the one installed — on such a machine FOG legitimately holds the signing key for future `db` updates, and those updates are a signature check, not a firmware-tooling problem. The two -should not be conflated: "fleet-scale enrolment on stock OEM hardware is a +should not be conflated: "fleet-scale enrollment on stock OEM hardware is a dead end" and "a FOG-owned platform can take further signed updates with no Setup Mode revisit" are both true, about different machines. @@ -126,7 +126,7 @@ Setup Mode revisit" are both true, about different machines. Microsoft — settle the UKI question first" - FOGProject/fogproject#962 (closed) — the parent tracking issue; source of the `ipxe/shim` and `foguefi` findings and the OEM-tooling survey -- [ADR-0009](0009-secure-boot-enrolment-paths.md) — the enrolment paths this +- [ADR-0009](0009-secure-boot-enrolment-paths.md) — the enrollment paths this would eventually make unnecessary for new installs - [ADR-0010](0010-secure-boot-kernel-hardening.md) — the lockdown-activation patch this depends on diff --git a/docs/adr/0013-pcie-aspm-and-extended-config-space.md b/docs/adr/0013-pcie-aspm-and-extended-config-space.md index 9a226e7..f83cae8 100644 --- a/docs/adr/0013-pcie-aspm-and-extended-config-space.md +++ b/docs/adr/0013-pcie-aspm-and-extended-config-space.md @@ -39,7 +39,7 @@ threshold, so ASPM was effectively switched off for the entire duration of an image regardless of what the kernel did or did not support. Commit `bc9ee24` ("Switch x64/arm64 kernels to in-kernel r8169…", ADR-less, -refs FOGProject/fos#108) dropped the vendor drivers in favour of the in-kernel +refs FOGProject/fos#108) dropped the vendor drivers in favor of the in-kernel `r8169`. That was the right call for the MAC-brick bug it fixed, but it removed the cover that had been hiding the missing symbols. @@ -156,7 +156,7 @@ honouring `rtl_aspm_is_safe()` for boards whose vendor has certified ASPM 1.2 as safe (OCP `0xc0b2`). `PERFORMANCE` would disable ASPM on every link on every machine regardless of driver. That is a defensible choice for a short-lived, mains-powered imaging init and is the obvious escalation if other NICs turn out -to have the same problem, but it is a larger behavioural change across the +to have the same problem, but it is a larger behavioral change across the whole fleet to fix a bug that the smaller one fixes, so it is not the default today. @@ -174,7 +174,7 @@ have made this bug appear on legacy boots too. its `0x070f` (ASPM entry latency) and `0x0890` (ZRXDC timeout) writes. - Other drivers that call `pci_disable_link_state()` — several NIC and NVMe drivers do — get a real answer instead of a stub's lie for the first time on - FOS. This is a fleet-wide behavioural change and the main reason to watch the + FOS. This is a fleet-wide behavioral change and the main reason to watch the first release carrying it. - Extended config space becoming visible means AER/L1SS/other extended capabilities are now parsed at enumeration. `CONFIG_PCIEAER` stays off, so diff --git a/docs/adr/0014-mbr-extended-partitions.md b/docs/adr/0014-mbr-extended-partitions.md index f79e7a9..16e7789 100644 --- a/docs/adr/0014-mbr-extended-partitions.md +++ b/docs/adr/0014-mbr-extended-partitions.md @@ -45,7 +45,7 @@ requires and what the rest of the script was written assuming. Two traversals needed care rather than a blanket setting: - `fill_disk()`'s "find the next partition" scan infers a partition's original - size from where its neighbour starts, so it needs the same by-number order. + size from where its neighbor starts, so it needs the same by-number order. - the `ordered_starts` walk that assigns new start positions accumulates `curr_start` as it goes and must follow `asort()`'s ascending *index* order. It was relying on hash order too, so it is now pinned explicitly to diff --git a/docs/adr/0016-sector-units-in-the-resize-path.md b/docs/adr/0016-sector-units-in-the-resize-path.md index d12937e..ad0770a 100644 --- a/docs/adr/0016-sector-units-in-the-resize-path.md +++ b/docs/adr/0016-sector-units-in-the-resize-path.md @@ -31,7 +31,7 @@ The guard is gone. Every action gets the rescale, because every action compares against `diskSize`: `check_overlap()` uses it as the bound for "does this partition fit on the disk", and with a value eight times too large that check cannot fail no matter how wrong the layout is. The rescale is an exact no-op on -512-byte-sector disks (`disk_size * 512 / 512`), so this is not a behaviour +512-byte-sector disks (`disk_size * 512 / 512`), so this is not a behavior change there. ### 2. `SECTOR_SIZE` and `LOGICAL_SECTOR_SIZE` are different numbers @@ -99,13 +99,13 @@ look identical and are not. of `tests/checks/fill-engine.sh`. It pins the passthrough, the divisor, the rounding direction, and that a valid 4Kn shrink reaches the sfdisk write. - `tests/checks/mbr-extended.sh`'s resize case had encoded the old flooring - behaviour (`10000000 / 512`) and now expects the rounded-up value. It is the + behavior (`10000000 / 512`) and now expects the rounded-up value. It is the only in-tree assertion the rounding change moved. ## Alternatives rejected **Revert ADR-0003's fail-loud apply.** It would restore the observed "working" -March behaviour, and that behaviour is a 4Kn capture that quietly does not +March behavior, and that behavior is a 4Kn capture that quietly does not shrink. The refusal was the only reason anyone found out. **Reuse `SECTOR_SIZE` for the divisor by moving the existing rescale out of the diff --git a/docs/adr/0017-arm64-display-vc4.md b/docs/adr/0017-arm64-display-vc4.md index f2e1b07..111dfc2 100644 --- a/docs/adr/0017-arm64-display-vc4.md +++ b/docs/adr/0017-arm64-display-vc4.md @@ -74,7 +74,7 @@ Two habits follow, and neither is optional here: ## Validation Confirmed on real hardware by the reporter, 2026-08-27, against -`EXP_20260827-114033`: HDMI initialised, FOS reached userspace, and Partclone +`EXP_20260827-114033`: HDMI initialized, FOS reached userspace, and Partclone captured `/dev/sda` to the FOG server. That is the first end-to-end FOS imaging run on a Raspberry Pi, and it also became the first real-hardware exercise of ADR-0015's platform support and of the `mount -t nfs` fix, both of which had diff --git a/tests/README.md b/tests/README.md index 8301c39..0973b7f 100644 --- a/tests/README.md +++ b/tests/README.md @@ -28,7 +28,7 @@ committed fixture), make the change, run `check` again — it must still pass. ## checks/ — assertion harnesses -Pass/fail assertions for behaviour that a single golden output stream can't +Pass/fail assertions for behavior that a single golden output stream can't express (e.g. "does this function abort or not?"). Each script runs a battery of cases and exits non-zero if any fail. diff --git a/tests/checks/error-report.sh b/tests/checks/error-report.sh index 8992848..d993d84 100755 --- a/tests/checks/error-report.sh +++ b/tests/checks/error-report.sh @@ -22,7 +22,7 @@ # the external tools with deterministic stubs. curl is the stub that matters -- # it records its argv rather than making a request. # -# One thing here is asserted as behaviour rather than as implementation: no FOS +# One thing here is asserted as behavior rather than as implementation: no FOS # script sets errexit today, so the `|| :` on the curl is defensive and cannot # be isolated by a case. Case 5 pins what actually matters instead -- that # handleError still reaches its reboot notice when the report fails -- which diff --git a/tests/checks/fill-engine.sh b/tests/checks/fill-engine.sh index f4cbe2f..d76879f 100755 --- a/tests/checks/fill-engine.sh +++ b/tests/checks/fill-engine.sh @@ -9,7 +9,7 @@ # The golden harness (tests/golden/) proves one fixed table stays byte-identical; # it can't express the invariants that matter when the target geometry differs # from the captured image. This harness drives the REAL awk through the REAL -# shell entry points to lock three behaviours that have no other in-tree coverage: +# shell entry points to lock three behaviors that have no other in-tree coverage: # # 1. Sector-size awareness (processSfdisk, filldisk only). blockdev --getsz # always reports 512-byte units; on a 4Kn (getss=4096) target the disk size diff --git a/tests/checks/package-mirrors.sh b/tests/checks/package-mirrors.sh index 3b43de0..6f32936 100755 --- a/tests/checks/package-mirrors.sh +++ b/tests/checks/package-mirrors.sh @@ -5,7 +5,7 @@ # tests/checks/package-mirrors.sh # run all cases, exit non-zero on any failure # # What is under test is "which mirror did we fall through to, and did we refuse -# bytes that don't match the hash" -- pass/fail behaviour a golden output stream +# bytes that don't match the hash" -- pass/fail behavior a golden output stream # can't express, so this is a checks/ harness rather than a golden case. # # The regression that motivated it: all five of FOG's own Buildroot packages @@ -91,7 +91,7 @@ expect_vars partclone 0.3.47 partclone-0.3.47.tar.gz \ https://github.com/Thomas-Tsai/partclone/archive/0.3.47/partclone-0.3.47.tar.gz # ============================================================================ -echo "== seedPackage() behaviour ==" +echo "== seedPackage() behavior ==" # ============================================================================ # Against a synthetic package, so these cases stay offline and independent of # what any real upstream is serving today. diff --git a/tests/checks/resize-engine.sh b/tests/checks/resize-engine.sh index cd80ad8..254444d 100644 --- a/tests/checks/resize-engine.sh +++ b/tests/checks/resize-engine.sh @@ -271,7 +271,7 @@ else fail "4Kn shrink applied table" "last-lba in written table: '${appliedlast:-none}' vs disk $DISK4K" fi -# 6. A failing sfdisk write still aborts (ADR-0003) -- the behaviour that turned +# 6. A failing sfdisk write still aborts (ADR-0003) -- the behavior that turned # this silent no-op into a visible failure in the first place. FAKE_SFDISK_RC=1 resize_case "resizeSfdiskPartition aborts when sfdisk refuses the table" \ "$SANDBOX/d.4k" "$GETSZ4K" 4096 /dev/vda4 "$BYTES30G" abort yes diff --git a/tests/checks/secureboot-config.sh b/tests/checks/secureboot-config.sh index 8783ee2..4cfddda 100755 --- a/tests/checks/secureboot-config.sh +++ b/tests/checks/secureboot-config.sh @@ -74,7 +74,7 @@ checkConfig() { fails=$((fails + 1)) fi done - # The lockdown LSM only initialises if it is in the ordered LSM list. + # The lockdown LSM only initializes 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)) diff --git a/tests/checks/secureboot.sh b/tests/checks/secureboot.sh index e1a8d56..4d3dbf2 100755 --- a/tests/checks/secureboot.sh +++ b/tests/checks/secureboot.sh @@ -161,7 +161,7 @@ if [[ -n $FAKE_DD_FAIL && ${out##*/} == "$FAKE_DD_FAIL"-* ]]; then fi /usr/bin/dd "$@" || exit 1 # Writing a PK is what takes a platform out of Setup Mode. Modelling that here -# is the only way to test that sbEnrollDb confirms the enrolment from the +# is the only way to test that sbEnrollDb confirms the enrollment from the # firmware rather than from dd's exit status. if [[ ${out##*/} == PK-* && -z $FAKE_PK_KEEPS_SETUP ]]; then guid="8be4df61-93ca-11d2-aa0d-00e098032b8c" @@ -341,7 +341,7 @@ check "absent SetupMode -> falls through to SecureBoot" "$(lib 'sbState')" "enfo # anything about what a given firmware does. # 9a. The ordinary case. Every other case in this file relies on this default, -# so a regression that made 64-bit read as anything else would refuse enrolment +# so a regression that made 64-bit read as anything else would refuse enrollment # on the hardware the feature is actually for. new_case; make_firmware 1 0 64 check "fw_platform_size 64 -> 64" "$(lib 'sbPlatformBits')" "64" @@ -404,7 +404,7 @@ check "fingerprint is colon-separated uppercase SHA-256 of the DER" \ # --- already-trusted detection --- # 14. A key already in the MOK list must short-circuit, or every run sends a -# technician to a blue screen to re-enrol something already trusted. +# technician to a blue screen to re-enroll something already trusted. new_case; make_firmware 0 1; FAKE_KEY_ENROLLED=1; export FAKE_KEY_ENROLLED check "already-enrolled MOK is detected" \ "$(lib 'sbCertTrusted "$SANDBOX/fp.der" && echo trusted || echo untrusted')" "trusted" @@ -510,7 +510,7 @@ check "22. failing import refuses" \ "$(lib 'sbStageMok "$SANDBOX/fp.der" hunter2 >/dev/null && echo ok || echo refused')" "refused" # 23. THE silent-failure guard. mokutil exits 0 but staged nothing. Trusting the -# exit status here reports a pending enrolment that does not exist, and the +# exit status here reports a pending enrollment that does not exist, and the # technician reboots into a normal boot with no explanation. new_case; make_firmware 0 1; FAKE_IMPORT_NOOP=1; export FAKE_IMPORT_NOOP check "23. import exits 0 but stages nothing -> refuse" \ @@ -549,14 +549,14 @@ fi # --- the Setup Mode (db) path --- -# 27. A signed variable update is recognised by its +# 27. A signed variable update is recognized by its # EFI_VARIABLE_AUTHENTICATION_2 header, not by being non-empty. new_case; make_firmware 1 0 check "27. a well-formed .auth is accepted" \ "$(lib 'sbFetchAuthVar db "$SANDBOX/x.auth" && echo ok || echo refused')" "ok" # 28. An HTML error page is longer than the header it would have to match, so a -# size check alone would pass it. A client that wrote one into db would enrol +# size check alone would pass it. A client that wrote one into db would enroll # nothing and report success. new_case; make_firmware 1 0; FAKE_AUTH_FAIL=db; export FAKE_AUTH_FAIL check "28. an HTML error page is not mistaken for a .auth" \ @@ -650,7 +650,7 @@ else fi # 37. Every blob is downloaded BEFORE any is written. A web server hiccup partway -# through should cost nothing; leaving a platform mid-enrolment over one is a +# through should cost nothing; leaving a platform mid-enrollment over one is a # far worse trade than re-running the fetch. new_case; make_firmware 1 0; FAKE_AUTH_FAIL=PK; export FAKE_AUTH_FAIL GOT="$(lib 'sbEnrollDb && echo ok || echo refused')" @@ -685,7 +685,7 @@ check "39. writes succeed but SetupMode stays 1 -> refuse" \ # 40. Happy path: all three written, firmware left Setup Mode. new_case; make_firmware 1 0 -check "40. enrolment succeeds when the firmware leaves Setup Mode" \ +check "40. enrollment succeeds when the firmware leaves Setup Mode" \ "$(lib 'sbEnrollDb && echo ok || echo refused')" "ok" echo "----" diff --git a/tests/checks/wipe.sh b/tests/checks/wipe.sh index 20c7ae7..c80aa95 100755 --- a/tests/checks/wipe.sh +++ b/tests/checks/wipe.sh @@ -4,7 +4,7 @@ # # tests/checks/wipe.sh # run all cases, exit non-zero on any failure # -# The behaviour under test is "which erase primitive did we actually issue, and +# The behavior under test is "which erase primitive did we actually issue, and # did we refuse when it failed" -- a pass/fail assertion a single golden output # stream can't express, so this is a sibling to the golden harness rather than a # case inside it. See docs/adr/0008-secure-wipe-by-device-class.md @@ -354,7 +354,7 @@ new_case run_case "empty mode -> refuse, touch nothing" /dev/sda "" fail "" "shred" # 21. Mode validation must sit AHEAD of the nvme dispatch. nvmeSecureErase treats -# any mode it doesn't recognise as "not full, not fast" and issues a format +# any mode it doesn't recognize as "not full, not fast" and issues a format # --ses=1, so an unknown mode on an NVMe target would erase the drive even though # the same mode refuses on /dev/sda. Guards that asymmetry. new_case diff --git a/tests/run-all.sh b/tests/run-all.sh index 3805ff5..797b58a 100755 --- a/tests/run-all.sh +++ b/tests/run-all.sh @@ -10,7 +10,7 @@ # release or dispatch, so until now not one of these assertions had ever run # anywhere but on a maintainer's laptop. # -# Modelled on fogproject's tests/run-all.sh, deliberately: the two projects are +# Modeled on fogproject's tests/run-all.sh, deliberately: the two projects are # maintained by the same people and a suite that reports differently in each is # a suite people read less carefully. #