Files
Dragan Spiridonov 0df134cf23 fix(provision): rework after adversarial review of the first cut
An adversarial review of my own patch found it did not do what the description
claimed. Fixes:

* `--ota-psk` alone was rejected by has_config_value() — `ota_psk` was missing
  from CONFIG_VALUE_CHECKS, so the headline feature could not provision a fresh
  device standalone. Added; it now reaches the normal WiFi-trio gate, whose
  documented escape (--force-partial) works.

* The MAC was read only when prior state already existed, so a *first*
  provisioning recorded no identity — precisely the swap case #1755 describes.
  Identity is now read on any run that talks to the device.

* The error text offered "pass every value explicitly" as an alternative, but
  the check runs before the merge so only --reset works. Removed the false
  advice.

* --dry-run opened the serial port and could stall ~60s. It and --state now
  never touch hardware.

* The temp file was opened O_TRUNC without O_EXCL/O_NOFOLLOW. O_CREAT ignores
  the mode for an existing path, so a planted symlink captured the cleartext
  password — in a patch whose purpose is not leaking it. Now unlinks any stale
  temp and opens O_EXCL|O_NOFOLLOW.

* --ota-psk accepted a single hex character for a firmware-upload bearer
  credential. Now requires exactly 64 hex chars, as ota_update.c documents.

* Legacy 0644/0755 state was only repaired on the write path, so --state left
  it exposed. Both read paths now repair it.

* The PSK is persisted (provisioning rewrites the whole NVS partition, so
  dropping it would wipe the device's key) but is redacted in --state output
  rather than printed verbatim.

* New tests sat below unittest.main(); direct execution silently ran 11 of 23.
  Moved the runner to the end — direct execution now runs all 24.

One earlier test did not bind: it asserted the final file mode, which the
trailing chmod fixes whether or not O_EXCL is used. Replaced with a planted-
symlink test that fails without the fix (verified: the secret was written
through the symlink).

Controls re-run per fix — CONFIG_VALUE_CHECKS 1 failure, O_EXCL 1, legacy
hardening 1, all restored 24 pass.

Not addressed, and called out on the PR: the MAC gate is fail-open by design
(unknown identity still merges) and has a swap-after-read TOCTOU. It is a
guard against the common accident, not a security boundary. Windows cannot
retrofit owner-only ACLs via chmod, so the migration is POSIX-only.
2026-08-31 17:43:14 +02:00
..