• 5.5.3-24 34617b4a06

    amnesia released this 2026-07-15 04:38:10 +02:00 | 4 commits to main since this release

    protocol7-core-5.5.3-24 ~ Changelog

    Note: The three hardening techniques addressed in this release ~ restricting allowed socket address families, preventing ASLR-personality changes, and restricting namespace creation ~ were prompted by reviewing how Whonix/Kicksecure hardens its own systemd services (RestrictAddressFamilies, LockPersonality, RestrictNamespaces), and manually reimplementing those same protections in protocol7-core's daemons via libseccomp, since there's no systemd here to enforce them through a config directive.


    Security Hardening (lainos-dbus-bridge)

    • Added a redundant, defense-in-depth caller-identity check to Reboot/PowerOff D-Bus methods via manual, logic-focused code review (conducted 2026-07-09, a category of review distinct from fuzzing/memory-safety testing). The D-Bus system bus's default <policy context="default"> allows send_destination from any local process with no UID restriction, so this check adds an explicit authorization layer at the application level. In practice, this was never exploitable: the daemon runs as nobody after privilege drop, and openrc-shutdown itself refuses to run without root regardless of caller ~ so the missing check never allowed an actual reboot/poweroff. The fix closes the gap at an earlier point in the call chain rather than relying solely on that downstream protection.
    • Fix: added an explicit caller-UID check before the fork, using dbus_bus_get_unix_user() to resolve the sender's real UID via the trusted bus daemon, rejecting any caller that is neither the active session user nor root with a proper org.freedesktop.DBus.Error.AccessDenied reply.
    • Seccomp interaction: dbus_bus_get_unix_user() requires libdbus's internal blocking-call machinery, which invokes clone(). Added clone to the seccomp whitelist, scoped via SCMP_CMP_MASKED_EQ to the specific CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD flag pattern libdbus's worker-thread creation uses, rather than an unconditional allow.
    • Scope note: this review covered lainos-dbus-bridge only. Reboot/PowerOff are the only two Manager-interface methods with real, irreversible system-level effects; all other action methods either have no real effect (Suspend is a documented no-op, Hibernate/HybridSleep return NotSupported) or are read-only. Session/User-interface methods (Lock, Unlock, Terminate, etc.) are not implemented in handle_method() at all and correctly return UnknownInterface.

    Security Hardening (lainos-notifyd and lainos-dbus-bridge)

    • Restricted socket() to AF_UNIX only, via seccomp argument filtering (SCMP_CMP(0, SCMP_CMP_EQ, AF_UNIX)). Both daemons only ever communicate over Unix domain sockets (/run/systemd/notify for notifyd, the D-Bus system bus for dbus-bridge); this closes off AF_INET/AF_INET6/AF_NETLINK/raw sockets and any other address family the syscall would otherwise permit. Equivalent to systemd's RestrictAddressFamilies=AF_UNIX, as hardened by Whonix.
    • Added the missing #include <sys/socket.h> to lainos-dbus-bridge.c, required for the AF_UNIX constant used by the new restriction (notifyd already had it via an existing include).

    Security Review (no code changes required)

    • Confirmed LockPersonality and RestrictNamespaces equivalents, as hardened by Whonix, are already effectively enforced in both daemons under the existing default-deny (SCMP_ACT_KILL) seccomp policy: personality(), unshare(), and setns() were never present in either daemon's syscall whitelist, meaning they were already fully blocked. The one clone() allowance (added for the authorization fix above) remains narrowly scoped, not general namespace creation.

    Verification

    • lainos-dbus-bridge: full dfuzzer 2.6 regression pass ~ Exit status: 0, all methods/properties PASS, real (non-bypassed) seccomp filter active. Valgrind memcheck ~ 0 errors, 0 leaks (definite/indirect/possible); 19,837 bytes still reachable in 79 blocks, consistent with the pre-fix baseline (no new leak). Manual allow-path and deny-path authorization tests both passed correctly. Functional D-Bus smoke test (CanReboot) confirmed working.
    • lainos-notifyd: Valgrind memcheck under real, exercised traffic (Tor daemon restart, generating genuine sd_notify messages) ~ 0 errors, 0 leaks, daemon survived without crash.
    • Both daemons compile cleanly against the new restrictions (only the pre-existing, previously-triaged unused variable 'path' warning in lainos-dbus-bridge.c, unrelated to this change).

    Security Verification (lainos-init)

    • Previously verified only by manual code review, not automated testing. lainos-init is one-shot (single execution per login, called by greetd), but unlike lainos-ghost-units (a genuinely input-free daemon), it reads real, environment-controlled input (P7_CMD, P7_COMPOSITOR, P7_SESSION_TYPE) and uses it to decide what binary to execlp() ~ a real attack surface that "one-shot" alone does not make safe.

    • Built a libFuzzer harness (fuzz_compositor_is_safe.c) against the actual, unmodified compositor_is_safe() function ~ the gatekeeper deciding whether an environment-supplied compositor value is allowed to reach execlp(). Ran 7,332,678 executions under AddressSanitizer with an RSS-limited, time-boxed configuration ~ zero crashes, zero memory-safety findings.

    • Manually tested a set of semantically adversarial inputs the coverage-guided fuzzer is not guaranteed to prioritize: path traversal (../../../etc/passwd), command-injection-style payloads (sway; rm -rf /), shell substitution (sway$(whoami)), a raw shell path (/bin/sh), and flag-injection-style input (-e). All correctly rejected with no crash.

    • Built a second libFuzzer harness (fuzz_path_construction.c) verifying the exact snprintf-based overflow-detection pattern used for XDG_RUNTIME_DIR/DBUS_SESSION_BUS_ADDRESS path construction, with an explicit invariant check (abort() on any case where the overflow check reports success but the resulting buffer is actually out-of-bounds or non-terminated). Ran 1,769,984 executions ~ zero crashes, invariant held in all cases.

    • preserve_critical_env() reviewed and confirmed to have no fuzzable logic of its own (unconditional getenv/setenv passthrough, no parsing or branching on value content); any risk from preserved variable values lies in downstream consumers outside protocol7-core's scope, not in this function.

    • This confirms compositor_is_safe()'s whitelist-or-verified-absolute-path validation and the path-construction overflow checks both hold under sustained fuzzing and targeted adversarial testing, closing the gap between "reviewed by inspection" and "verified by testing" for this daemon's real external-input paths.

    Known gap, unchanged: lainos-ghost-units confirmed to take no external input at all (fixed array of 8 literal paths, no arguments/environment/file parsing), and is not prioritized for fuzzing on that basis.

    Downloads