pwnkit: migrate detect() to consult ctx->host->polkit_version with
the same graceful-fallback pattern as the sudo modules. The version
is populated once at startup by core/host.c (via pkexec --version);
detect() skips the per-scan popen when the host fingerprint has the
version. Falls back to the inline popen path when ctx->host is
missing the version (degenerate test contexts).
sudoedit_editor: already migrated; this commit adds direct test
coverage.
tests/test_detect.c expansion (35 → 39):
- pwnkit: polkit_version='0.105' -> VULNERABLE (pre-0.121 fix)
- pwnkit: polkit_version='0.121' -> OK (fix release)
- sudoedit_editor: vuln sudo + no sudoers grant -> PRECOND_FAIL
(documented behaviour: vulnerable version, but the dispatcher
has no usable sudoedit grant on the host)
- sudoedit_editor: fixed sudo (1.9.13p1) -> OK
The sudoedit_editor 'vuln + no grant' case is the first test to
exercise the second-level precondition gate AFTER the version
check passes — proves the version-pinned detect logic AND the
sudo -ln target-discovery short-circuit both work as intended.
The h_vuln_sudo / h_fixed_sudo synthetic fingerprints gained the
.polkit_version field alongside .sudo_version so a single fingerprint
exercises both pwnkit and the sudo modules.
Verification: 39/39 pass on Linux (docker gcc:latest + libglib2.0-dev
+ sudo, non-root user skeletonkeyci). macOS dev box still reports
'skipped — Linux-only' as designed.
Completes the host-fingerprint refactor that started in c00c3b4. Every
module now consults the shared ctx->host (populated once at startup
by core/host.c) instead of re-doing uname / geteuid / /etc/os-release
parsing / fork+unshare(CLONE_NEWUSER) probes per detect().
Migrations applied per module (mechanical, no exploit logic touched):
1. #include "../../core/host.h" inside each module's #ifdef __linux__.
2. kernel_version_current(&v) -> ctx->host->kernel (with the
v -> v-> arrow-vs-dot fix for all later usage). Drops ~20 redundant
uname() calls across the corpus.
3. geteuid() == 0 (the 'already root, nothing to escalate' gate) ->
bool is_root = ctx->host ? ctx->host->is_root : (geteuid() == 0);
This is the key change that lets the unit test suite construct
non-root fingerprints regardless of the test process's actual euid.
4. Per-detect fork+unshare(CLONE_NEWUSER) probe helpers (named
can_unshare_userns / can_unshare_userns_mount across the corpus)
are removed wholesale; their call sites now consult
ctx->host->unprivileged_userns_allowed, which was probed once at
startup. Removes ~10 per-scan fork()s.
Modules touched by this commit (22):
Batch A (7): dirty_pipe, dirty_cow, ptrace_traceme, pwnkit,
cgroup_release_agent, overlayfs_setuid, and entrybleed
(no migration target — KPTI gate stays as direct sysfs
read; documented as 'no applicable pattern').
Batch B (7): nf_tables, cls_route4, netfilter_xtcompat, af_packet,
af_packet2, af_unix_gc, fuse_legacy.
Batch C (8): stackrot, nft_set_uaf, nft_fwd_dup, nft_payload,
sudo_samedit, sequoia, sudoedit_editor, vmwgfx.
Combined with the 4 modules already migrated (dirtydecrypt, fragnesia,
pack2theroot, overlayfs) and the 5-module copy_fail_family bridge,
the entire registered corpus now goes through ctx->host. The 4
'fork+unshare per detect()' helpers that existed across nf_tables,
cls_route4, netfilter_xtcompat, af_packet, af_packet2, fuse_legacy,
nft_set_uaf, nft_fwd_dup, nft_payload, sequoia,
cgroup_release_agent, and overlayfs_setuid are now gone — replaced by
the single startup probe in core/host.c.
Verification:
- Linux (docker gcc:latest + libglib2.0-dev): full clean build links
31 modules; tests/test_detect.c: 8/8 pass.
- macOS (local): full clean build links 31 modules (Mach-O, 172KB);
test suite reports skipped as designed on non-Linux.
Subsequent commits can add more EXPECT_DETECT cases in
tests/test_detect.c — the host-fingerprint paths in every module are
now uniformly testable via synthetic struct skeletonkey_host instances.