-
protocol7-core-5.5.3-26 Stable
released this
2026-07-17 13:22:37 +02:00 | 2 commits to main since this releaseprotocol7-core-5.5.3-26 ~ Changelog
Security Hardening ~ Complete Filesystem Isolation (
lainos-dbus-bridgeandlainos-notifyd)Both daemons now implement the full set of systemd filesystem-sandboxing equivalents:
ProtectSystem=strictequivalent: private mount namespace (unshare(CLONE_NEWNS)) with the root filesystem remounted read-only (MS_BIND | MS_RDONLY), preceded by anMS_PRIVATEremount to prevent propagation back to the real, shared namespace.PrivateTmp=trueequivalent: a fresh, size-limited (16MB),nosuid/nodevtmpfs mounted over/tmp, invisible to and from the real system/tmp.ProtectHome=trueequivalent:/homeand/rootreplaced with empty, read-only, zero-size tmpfs mounts, hiding all user data from either daemon regardless of discretionary file permissions.PrivateDevices=trueequivalent: the realdevtmpfs(thousands of device nodes) replaced with a minimal tmpfs containing only/dev/null,/dev/zero,/dev/urandom,/dev/random.
All isolation steps run while still root, before privilege drop, since
unshare()/mount()/mknod()requireCAP_SYS_ADMIN(lost immediately after) and are intentionally absent from the seccomp whitelist. All failures on the read-only-root and/tmp//devsteps are fatal;/home//rootmount failures are logged and treated as non-fatal (a configuration difference, not an isolation failure, e.g. on a system without a separate/root).Verification (both daemons, on real
rc-service-managed production processes):- All four mounts confirmed active via direct
/proc/<pid>/mountsinspection, correctly layered over the real, live mounts (visible in the same output for comparison) rather than replacing them system-wide. - Full dfuzzer 2.6 regression pass on
lainos-dbus-bridgeafter each incremental addition ~ Exit status: 0, all methods/properties PASS throughout. - Valgrind memcheck on both daemons, at each checkpoint ~ 0 errors, 0 leaks throughout; reachable-memory figures traced via backtrace inspection at each step, confirmed to be pure libdbus-internal bookkeeping (not application code) whenever the byte count shifted between runs.
lainos-notifydexercised with real traffic (a live Tor service restart, generating genuinesd_notifymessages) while under Valgrind's supervision at the final checkpoint.- Functional D-Bus smoke test (
CanReboot) confirmed correct throughout. - Binary hashes (
md5sum) verified matching between the tested builds and the actual production binaries for both daemons, at the final checkpoint.
This completes the filesystem-isolation work for both daemons. Combined with the seccomp hardening (5.5.3-24) and capability bounding-set clearing (5.5.3-25), protocol7-core's two most exposed daemons now have privilege drop, syscall restriction, capability restriction, and filesystem isolation all implemented and verified, matching the full breadth of systemd's per-service sandboxing model as hardened by Whonix without depending on systemd itself.
Downloads
-
protocol7-core-5.5.3-25 Stable
released this
2026-07-17 12:25:51 +02:00 | 3 commits to main since this releaseprotocol7-core-5.5.3-25 ~ Changelog
Security Hardening
lainos-notifydandlainos-dbus-bridge: explicitly cleared the Linux capability bounding set (CapBnd) during privilege drop, via aprctl(PR_CAPBSET_DROP, ...)loop over all capabilities, called beforesetuid()whileCAP_SETPCAPis still held.
Background: prompted by an external review of the security analysis document, which asked a precise, fair question this document had not previously answered: does the privilege drop clear Linux capabilities, or only UID/GID? Rather than answer from kernel-semantics reasoning alone, this was checked directly via
/proc/<pid>/statuson the live, running daemons.Finding:
setuid()'s standard kernel behavior already cleared the effective and permitted capability sets (CapEff/CapPrmboth confirmed zero, directly, before any change was made). The capability bounding set (CapBnd) is a separate mechanism, not cleared bysetuid()alone, and still showed the full root bounding set ~ a ceiling on capabilities the process could regain, for example via a laterexecveof a file with capabilities set. Not a fix for a live vulnerability (effective/permitted capabilities were already correctly zero), but a real, previously-unverified residual, closed as defense-in-depth.Fix: added a
prctl(PR_CAPBSET_DROP, cap, 0, 0, 0)loop (0 throughCAP_LAST_CAP) to both daemons'drop_privileges(), positioned before the existingsetgid()/setuid()calls. Requires<sys/prctl.h>and<linux/capability.h>, added to both files.Verification:
- Re-checked via
/proc/<pid>/statuspost-fix on both daemons ~ all five capability fields (CapInh,CapPrm,CapEff,CapBnd,CapAmb) now read zero, confirmed on the actual running production processes, not just a test build. - Full dfuzzer 2.6 regression pass on
lainos-dbus-bridge~ Exit status: 0, all methods/properties PASS; functional D-Bus smoke test (CanReboot) confirmed correct. - Valgrind memcheck on both daemons (seccomp-bypass debug-build methodology, matching established practice) ~ 0 errors, 0 leaks; reachable-memory baselines match prior releases exactly (19,837 bytes/79 blocks for dbus-bridge; consistent figures for notifyd under real exercised traffic from a live Tor service restart) ~ confirming the capability change introduced no functional or memory-safety regression.
- Binary hashes (
md5sum) re-confirmed matching between the tested build and the actual production binaries running on the system.
Note on process: this is the second real finding in this project surfaced by someone other than the primary author reviewing the security documentation itself, rather than the code directly ~ the security analysis document's own precision and willingness to state open questions plainly is what made this question askable in the first place.
Downloads
-
protocol7-core-5.5.3-24 Stable
released this
2026-07-15 04:38:10 +02:00 | 4 commits to main since this releaseprotocol7-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/PowerOffD-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">allowssend_destinationfrom 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 asnobodyafter privilege drop, andopenrc-shutdownitself 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 properorg.freedesktop.DBus.Error.AccessDeniedreply. - Seccomp interaction:
dbus_bus_get_unix_user()requires libdbus's internal blocking-call machinery, which invokesclone(). Addedcloneto the seccomp whitelist, scoped viaSCMP_CMP_MASKED_EQto the specificCLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREADflag pattern libdbus's worker-thread creation uses, rather than an unconditional allow. - Scope note: this review covered
lainos-dbus-bridgeonly.Reboot/PowerOffare the only two Manager-interface methods with real, irreversible system-level effects; all other action methods either have no real effect (Suspendis a documented no-op,Hibernate/HybridSleepreturnNotSupported) or are read-only. Session/User-interface methods (Lock,Unlock,Terminate, etc.) are not implemented inhandle_method()at all and correctly returnUnknownInterface.
Security Hardening (
lainos-notifydandlainos-dbus-bridge)- Restricted
socket()toAF_UNIXonly, via seccomp argument filtering (SCMP_CMP(0, SCMP_CMP_EQ, AF_UNIX)). Both daemons only ever communicate over Unix domain sockets (/run/systemd/notifyfor notifyd, the D-Bus system bus for dbus-bridge); this closes offAF_INET/AF_INET6/AF_NETLINK/raw sockets and any other address family the syscall would otherwise permit. Equivalent to systemd'sRestrictAddressFamilies=AF_UNIX, as hardened by Whonix. - Added the missing
#include <sys/socket.h>tolainos-dbus-bridge.c, required for theAF_UNIXconstant used by the new restriction (notifyd already had it via an existing include).
Security Review (no code changes required)
- Confirmed
LockPersonalityandRestrictNamespacesequivalents, as hardened by Whonix, are already effectively enforced in both daemons under the existing default-deny (SCMP_ACT_KILL) seccomp policy:personality(),unshare(), andsetns()were never present in either daemon's syscall whitelist, meaning they were already fully blocked. The oneclone()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 genuinesd_notifymessages) ~ 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 inlainos-dbus-bridge.c, unrelated to this change).
Security Verification (
lainos-init)-
Previously verified only by manual code review, not automated testing.
lainos-initis one-shot (single execution per login, called by greetd), but unlikelainos-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 toexeclp()~ a real attack surface that "one-shot" alone does not make safe. -
Built a libFuzzer harness (
fuzz_compositor_is_safe.c) against the actual, unmodifiedcompositor_is_safe()function ~ the gatekeeper deciding whether an environment-supplied compositor value is allowed to reachexeclp(). 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 exactsnprintf-based overflow-detection pattern used forXDG_RUNTIME_DIR/DBUS_SESSION_BUS_ADDRESSpath 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 (unconditionalgetenv/setenvpassthrough, 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-unitsconfirmed 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
- Added a redundant, defense-in-depth caller-identity check to
-
v5.5.3-22 Stable
released this
2026-07-09 22:25:34 +02:00 | 5 commits to main since this releaseprotocol7-core-5.5.3-22
Security Hardening
lainos-notifyd: Replacedstrncpywithsnprintffor theaddr.sun_pathassignment. Not an exploitable issue as written (fixed-length string literal into a pre-zeroed buffer, well within bounds) ~ flagged by semgrep'sinsecure-use-string-copy-fnrule as a false positive, hardened anyway for defense-in-depth and reviewer clarity.
Security Verification
- Valgrind memcheck run against
lainos-notifydandlainos-dbus-bridge(via dedicated, non-shipped debug builds with seccomp temporarily disabled through an env-var guard) ~ 0 errors, 0 leaks (definite/indirect/possible) on both, independently cross-validating existing ASAN/dfuzzer/libFuzzer results. - cppcheck + semgrep static analysis run against all four daemons (
lainos-dbus-bridge.c,lainos-notifyd.c,lainos-init.c,lainos-ghost-units.c) ~ zero errors, zero real security findings. One semgrep finding investigated and confirmed a false positive (see above). A handful of cosmetic style notes (const-correctness, one dead variable inlainos-init.c, one dead variable inlainos-dbus-bridge.calready known from compiler warnings) ~ no functional or security impact.
Documentation
- Security analysis document (
protocol7-core-security-analysis.md) updated to include Valgrind and static analysis results.
Downloads
-
v5.5.3-21 Stable
released this
2026-07-05 18:49:46 +02:00 | 11 commits to main since this releaseprotocol7-core 5.5.3-21 (2026-07-05)
lainos-ghost-units ~ syslog-ng ordering fix
Root cause: lainos-ghost-units creates
/run/systemd/systemas part of its
AUR-compatibility ghost directories. syslog-ng'ssystem()source macro calls
sd_booted()which checks for/run/systemd/systemat parse time. If ghost-units
ran before syslog-ng started, syslog-ng assumed it was on a real systemd host and
expandedsystem()tosystemd-journal()instead ofunix-dgram("/dev/log").
No journald exists on Protocol 7, so/dev/logwas never created and all syslog
calls silently went nowhere.Fix: Added
after syslog-ngtolainos-ghost-units.initddepend() block.
Since both services are in thebootrunlevel, OpenRC has no implicit ordering
between them -- this constraint was missing.after(notneed) enforces ordering
only when syslog-ng is going to start anyway, without forcing it as a hard dependency.Verification:
/dev/logconfirmed created by syslog-ng before ghost-units runslogger -p daemon.infoconfirmed reaching/var/log/daemon.log- lainos-notifyd messages confirmed appearing in daemon.log with sender uid/pid
lainos-notifyd v4.6 and v4.7
v4.6 ~ security/audit pass:
- FIX: removed SA_RESTART from SIGTERM/SIGINT handlers ~ recv() was transparently
restarted by the kernel after a signal, so shutdown_flag was never checked until
another datagram arrived. Daemon would hang on SIGTERM indefinitely if idle. - ADDED: switched recv() to recvmsg() with SO_PASSCRED so each message is logged
with the sender's real uid/pid (SCM_CREDENTIALS) instead of trusting
unauthenticated content blindly - ADDED: MSG_TRUNC detection ~ oversized datagrams logged as truncated instead of
silently clipped - ADDED: control-character sanitization on logged message bodies to prevent
syslog log-line injection via embedded newlines/escapes
v4.7 ~ shutdown unlink fix:
- FIX: removed shutdown-time unlink() of the socket path. After drop_privileges(),
the process runs as nobody but /run/systemd is root-owned 0755 ~ unlink()
requires write permission on the containing directory, not the file. This call
always failed silently (return value was never checked). Startup-time unlink()
(run as root, before bind()) already handles stale-socket cleanup. Removed
unlink/unlinkat from seccomp whitelist accordingly, tightening the filter.
Fuzz Testing (lainos-notifyd)
libFuzzer harness (prior session):
- Standalone harness targeting
parse_and_log()andsanitize_for_log()in isolation - Compiled with
clang -fsanitize=fuzzer,address,undefined - Seeded with every message-type prefix, boundary lengths, and injection attempts
- 2,000,000 iterations ~ zero crashes
Manual fuzz script (2026-07-05):
- Inputs: empty string, READY=1, 65536-byte payload, null bytes, 4096-byte STATUS,
MAINPID=0, MAINPID=99999999, WATCHDOG=1, 1024 bytes random binary, embedded newline injection - notifyd status: started (survived all inputs)
- MSG_TRUNC correctly fired on oversized payloads (over 8191 bytes, dropping)
- Log injection sanitized ~ embedded newlines replaced with dots in daemon.log
- All message types logged correctly with sender uid/pid attribution via SCM_CREDENTIALS
- READY=1 confirmed:
[notify uid=1000 pid=9454] READY=1 (service ready)
Structural changes
- lainos-net-init removed ~ dead code, no callers
- lainos-audio-init removed ~ now a separate optional package (lainos-audio-init)
in protocol_7_repo; lainos-init calls it via double-fork if present, fails
gracefully if not installed - lainos-init reverted to v4.6 ~ single responsibility (session init only,
no audio orchestration)
ISO changes
- syslog-ng daemon facility enabled in overlay (was commented out)
- notify group added to usermod in shellprocess-final ~ required for notifyd
socket access without doas - lainos-notifyd runlevel corrected to default (depend() requires dbus which
is not available in sysinit)
Downloads
-
v5.5.3-19 Stable
released this
2026-07-05 01:23:04 +02:00 | 12 commits to main since this releaseprotocol7-core 5.5.3-19 (2026-07-04)
Current state (as of 5.5.3-19)
- lainos-notifyd: v4.7, fuzzed clean and verified end-to-end. Runlevel placement
(sysinit-only) confirmed durable across a clean rebuild/reinstall/reboot cycle. No
known issues. - seatd: confirmed default-only, matching documented architecture.
- lainos-ghost-units: audited, no code changes needed. Boot-ordering fix shipped
and confirmed working across reboot. - lainos-audio-init / lainos-init: patched and compile-checked. NOT yet verified
end-to-end on a real login cycle ~ pending test. - lainos-net-init: removed from the package. Still outstanding: doc corrections in
project-status.md, iteration-log.md, and new-master-reference.md still describe it
as active/present. - net.ipv4.tcp_timestamps: recommended change to 0 for consistency with existing
anti-fingerprinting posture ~ NOT yet applied to
/etc/sysctl.d/99-lainos-hardening.conf. - lainos-dbus-bridge: unchanged this session ~ already fuzz tested clean in a
prior session (dfuzzer 2.6 + ASan against the full org.freedesktop.login1
interface, see 5.5.3-12 entry).
Fixes
protocol7-core.install ~ fixed incomplete lainos-notifyd runlevel correction from -18
Root cause: The -18 fix added lainos-notifyd to sysinit correctly, but rc-update add
is purely additive ~ it never removed the stale default entry left over from earlier
manual/script-added state. The duplicate persisted through -18 despite the fix looking
correct in isolation.
Fix: Added an explicitrc-update del "$svc" defaultbefore therc-update add "$svc" sysinitcall in the lainos-notifyd case branch, so the script self-corrects regardless
of prior state on every future upgrade, not just this one.Verification
- Clean reboot after rebuild/reinstall:
rc-update showconfirms lainos-notifyd is
sysinit-only, seatd is default-only - Both services started correctly with no manual rc-update intervention required
- /dev/log confirmed present and receiving log lines with no manual fix needed
- lainos-notifyd confirmed running v4.7 (strings check against installed binary)
protocol7-core 5.5.3-18 (2026-07-04)
protocol7-core.install ~ lainos-notifyd runlevel misassignment
Root cause: post_install()'s runlevel-registration loop unconditionally routed
lainos-notifyd through the genericcasefallthrough, which adds every non-boot
service to default. This directly contradicts notifyd's own depend() (before seatd)
once seatd is correctly single-homed in default ~ placing notifyd in default too gives
no ordering guarantee against seatd within the same runlevel.
Fix: Added an explicit lainos-notifyd case routing it to sysinit instead of the
generic fallthrough. (Incomplete as shipped ~ see -19: didn't clean up the pre-existing
stale default entry, so the duplicate persisted until -19.)seatd ~ reverted incorrect manual runlevel change
Earlier in the session, seatd had been removed from default and left boot-only, based
on reading its depend() block alone without checking the project's documented runlevel
architecture (session handoff doc), which places seatd in default. Restored to
default-only.
protocol7-core 5.5.3-17 (2026-07-04)
lainos-ghost-units.initd ~ shipped in a real pkgrel
Rebuild to ship the
after syslog-ngdepend() fix written into -16's source tree but
not yet built/deployed as its own pkgrel. No other changes.
Root cause (originally found and fixed in source at -16): ghost-units creating
/run/systemd/system before syslog-ng starts causes syslog-ng's sd_booted() check to
falsely detect a systemd host and skip binding /dev/log entirely ~ breaking all system
logging, not just notifyd's.
protocol7-core 5.5.3-16 (2026-07-04)
lainos-net-init ~ removed
Dropped from source=, build(), and package() in the PKGBUILD. Confirmed dead code:
never invoked by any init script, udev rule, or session hook across every prior
version in the repo history. Sysctl hardening it attempted is fully covered, and more
completely, by /etc/sysctl.d/99-lainos-hardening.conf via OpenRC's native sysctl
service.lainos-audio-init ~ stale-socket race in PipeWire readiness check
Root cause: wait_for_socket() only checked that a filesystem node existed at the
expected socket path, not that it was live. A socket file left behind by a crashed
prior instance would report "ready" instantly, before the newly-spawned pipewire had
bound anything, causing wireplumber to launch against a dead socket.
Fix: unlink any stale socket file before spawning pipewire, so existence
afterward genuinely means the new instance created it. Bumped to v4.5.lainos-init ~ wired up lainos-audio-init for the first time
Root cause: lainos-audio-init has been built and shipped since v4.4 but nothing
on the system ever invoked it. lainos-init only sets environment and execs the
compositor; no fork ever happened before that exec. Sessions were running on bare
ALSA with no PipeWire/WirePlumber/Pulse-compat layer at all.
Fix: Added a double-fork launcher (launch_audio_init()) called before both
compositor execlp() calls (X11 and Wayland branches), so the launcher doesn't zombie
under whatever process ends up in lainos-init's PID after exec. Bumped to v4.7.
protocol7-core 5.5.3-15 (2026-07-04)
Package integrity
Bumped provides= to
systemd=1:999(epoch 1) for permanent systemd>=X dependency
satisfaction regardless of upstream version.lainos-ghost-units.initd ~ added to package
Confirmed tracked in source= / package() as of this rebuild.
protocol7-core 5.5.3-14 (2026-07-04)
lainos-notifyd v4.7 ~ shutdown-time unlink() fixed at the actual root cause
Root cause: After drop_privileges(), the process runs as nobody, but /run/systemd
is root-owned 0755 ~ unlink() there always failed on permissions. The v4.6 seccomp fix
(below) only stopped the process being killed by SIGSYS; it didn't fix the underlying
EACCES, which was silently ignored since the return value was never checked.
Fix: Removed the shutdown-time unlink() entirely. Startup-time unlink() (root,
before bind()) already handles stale-socket cleanup on next start, making the
shutdown-time call both broken and redundant. Removed unlink/unlinkat from seccomp
accordingly, tightening the filter back down.
protocol7-core 5.5.3-13 (2026-07-04)
lainos-notifyd v4.6 ~ signal handling, seccomp, and message-handling hardening
Root cause 1 (shutdown hang): SA_RESTART on the SIGTERM/SIGINT handlers meant
recv() was transparently restarted by the kernel after a signal, so shutdown_flag was
never checked until another datagram arrived. The daemon could hang indefinitely on
stop if no more messages came in.
Fix: Removed SA_RESTART from the signal handlers.
Root cause 2 (seccomp kill on shutdown): The shutdown path called unlink() on the
socket after the seccomp filter (default SCMP_ACT_KILL) was loaded, killing the
process with SIGSYS instead of a clean exit.
Fix: Added unlink/unlinkat to the seccomp whitelist. (Superseded at -14 once the
actual permissions root cause was found ~ see above.)Added
- Switched recv() to recvmsg() with SO_PASSCRED ~ every logged message now carries
the sender's real uid/pid (SCM_CREDENTIALS) instead of trusting unauthenticated
message content - MSG_TRUNC detection ~ oversized datagrams are logged as truncated and dropped
instead of silently clipped - Control-character sanitization on logged message bodies, closing a syslog
log-line injection path via embedded newlines
Verification
- parse_and_log()/sanitize_for_log() (the message-parsing path handling untrusted
external bytes) fuzzed with a libFuzzer harness (clang-18, ASan+UBSan), seeded with
every message-type prefix, boundary lengths, empty input, and a log-injection
attempt - 2,000,000 executions, coverage plateaued (fully explored), zero crashes, zero leaks
- This code path is unchanged in v4.7 (-14), so the result still applies ~ the -14
fixes were control-flow bugs (signal handling, permissions), not parsing bugs,
which is why fuzzing wouldn't have caught them and doesn't need re-running - test-notifyd.sh: all checks passing on live hardware (clean shutdown timing,
socket lifecycle, credential attribution, truncation handling, log-injection
sanitization)
Downloads
- lainos-notifyd: v4.7, fuzzed clean and verified end-to-end. Runlevel placement
-
v5.5.3-12 Stable
released this
2026-07-04 04:21:19 +02:00 | 18 commits to main since this releaseprotocol7-core 5.5.3-12 (2026-07-03)
lainos-dbus-bridge ~ dfuzzer audit, AddressSanitizer, and hardening
Root cause: Systematic pointer type bug throughout the file ~ char[] and char[N]
variables were being passed as &var to libdbus functions (dbus_message_append_args,
dbus_message_iter_append_basic) which expect const char **. This caused libdbus to
abort internally when called with certain inputs, crashing the bridge.Crashes found and fixed via dfuzzer:
- GetSession ~ crashed on malformed string input; fixed by returning hardcoded
/org/freedesktop/login1/session/1 (correct for single-user system) - GetSeat ~ same crash, same fix; returns hardcoded /org/freedesktop/login1/seat/seat0
- GetUser ~ crashed when called with UID 0; fixed by ignoring caller-supplied UID
and returning path based on runtime_uid (dynamically detected at startup) - ListSessions ~ crashed due to char[] pointer type bug on sid, uname, seat variables
- ListSeats ~ same pointer type bug on seat variable
- ListUsers ~ same pointer type bug on uname variable
- RuntimePath property ~ crashed due to &runtime_path passed directly to libdbus;
fixed by assigning to const char *rp = runtime_path before passing &rp
Additional fixes from 5.5.3-5:
- Removed setuid/setgid/setresuid/setresgid/setgroups from seccomp whitelist
- Added execve and wait4 to seccomp whitelist (fixes Reboot/PowerOff D-Bus path)
- Replaced atoi with strtoul in /etc/passwd UID parsing
- Removed debug fprintf calls from main()
Risk Context
The vulnerability was real but the practical danger was low for several compounding reasons:
Access requirements: To trigger the crash, an attacker needs either a malicious package installed with user consent, or an existing shell running as the logged-in user. Neither is a remote attack vector. An attacker who already has user-level code execution has far more direct ways to cause damage than crashing a session daemon.
No session interruption: When lainos-dbus-bridge crashes, the desktop session continues running normally. Sway, greetd, and all running applications are unaffected. Only new applications launched after the crash that query logind for the first time would see an issue, and most fail gracefully. The user's work is not interrupted.
Instant recovery:
doas rc-service lainos-dbus-bridge restartfully restores the bridge. No reboot required.No escalation path: The bridge runs as nobody with a tight seccomp filter. Even if the crash were somehow turned into code execution rather than a simple abort, the attacker lands in a nobody context with a restricted syscall surface ~ nothing they didn't already have as the logged-in user. There is no privilege escalation path through the bridge.
The fix still matters: Correct software handles bad input gracefully. The systematic pointer type bug could theoretically have had more serious consequences in untested code paths. And the testing process that found it demonstrates that Protocol 7 takes correctness seriously ~ the code that ships has been put through a real testing process, not just written and deployed.
Verification
- dfuzzer 2.6 run against org.freedesktop.login1 ~ Exit status: 0
- AddressSanitizer + dfuzzer run ~ Exit status: 0, no memory errors detected
- All methods and properties PASS across Manager, Session, and User interfaces
- Reboot and PowerOff skipped as destructive (confirmed working ~ Reboot via D-Bus
caused actual system reboot during testing, confirming the execve fix works) - Bridge confirmed running as nobody after reboot
- seccomp filter confirmed active (/proc//status unreadable even with doas)
- System boots and desktop session starts correctly after package install
Downloads
- GetSession ~ crashed on malformed string input; fixed by returning hardcoded
-
v5.5.3-5 Stable
released this
2026-07-04 02:53:19 +02:00 | 19 commits to main since this releaseprotocol7-core 5.5.3-5 (2026-07-03)
lainos-dbus-bridge security fixes
- Removed setuid, setgid, setreuid, setregid, setresuid, setresgid, setgroups
from seccomp whitelist ~ privilege drop completes before seccomp is applied
so these syscalls serve no purpose post-drop and were unnecessary attack surface - Added execve and wait4 to seccomp whitelist ~ fixes silently broken
Reboot/PowerOff D-Bus methods (fork/exec path was blocked by seccomp) - Replaced atoi with strtoul + error checking in /etc/passwd UID parsing ~
prevents silent fallback to UID 0 on malformed entries - Removed all debug fprintf calls from main() ~ 11 lines of stderr output
on every startup that were never cleaned up from development
Verification
- Daemon confirmed running as nobody after reboot:
ps -o user,pid,comm -p $(pgrep -f lainos-dbus-bridge)returned USER=nobody - seccomp filter confirmed active:
/proc/<pid>/statusunreadable even with doas ~ seccomp blocking the read - D-Bus query methods confirmed working:
CanReboot, CanPowerOff, CanSuspend all return "yes" via dbus-send - System boots and desktop session starts correctly after package install
- No regressions observed in wlogout, polkit, seatd, or greetd
Downloads
- Removed setuid, setgid, setreuid, setregid, setresuid, setresgid, setgroups
-
protocol7-core-v5.5.3 Stable
released this
2026-06-26 01:36:00 +02:00 | 26 commits to main since this releaseProtocol 7 Core ~ Changelog
v5.5.3 (2026-06-25)
protocol7-core 5.5.3-1 ~ lainos-dbus-bridge init script fix
Fixed incorrect PID file path causing false "crashed" status in
rc-status.Problem:
OpenRC tracked the service PID at/run/lainos-dbus-bridge.pid, but the daemon wrote its PID file to/run/openrc/lainos-dbus-bridge.pid. This mismatch causedrc-statusto report[ crashed ]even though the daemon was running correctly. Steam and other D-Bus clients would fail to launch until a manualrc-service lainos-dbus-bridge restart.Fix:
- Changed
pidfilefrom/run/lainos-dbus-bridge.pidto/run/openrc/lainos-dbus-bridge.pid - Increased
start_stop_daemon_args--waitfrom100to500ms, giving the daemon adequate time to fork and write its PID before OpenRC checks status
Result:
lainos-dbus-bridgenow reports[ started ]correctly after boot. No manual restart required. Steam and other logind-dependent applications launch normally.Downloads
- Changed
-
protocol7-core-v5.5 Stable
released this
2026-06-25 00:50:51 +02:00 | 30 commits to main since this releaseProtocol 7 Core ~ Changelog
v5.5 (2026-06-24)
v5.5 is a hardening and reliability release. Here's what changed and why it matters:
Security
Binary hardening flags — every compiled binary now builds with
-pie -fstack-protector-strong -D_FORTIFY_SOURCE=2. In v5.4 the binaries had no stack protection or position-independent hardening, making them easier targets for memory corruption exploits.Root check re-hardened in
lainos-init— v5.4 had the root check removed with a comment saying it "prevents login loop." v5.5 hard-fails on root unlessP7_ALLOW_ROOT=1is explicitly set, closing a silent path to root sessions if greetd/PAM misconfigures.P7_CMDcompositor whitelist — v5.4 exec'd whateverP7_CMDcontained without validation. v5.5 validates against a known-safe compositor list before exec, blocking arbitrary command injection through the environment.drop_privileges()fallback — v5.4 silently returned without dropping privileges if thenobodyuser didn't exist. v5.5 falls back to UID/GID 65534 unconditionally.Ghost directory permissions —
0755→0750. These are placeholder dirs; world-execute was unnecessarily permissive.Reliability
D-Bus socket readiness — v5.4 used a fixed 500ms blind wait. v5.5 polls
/run/dbus/system_bus_socketevery 100ms up to 3 seconds, connecting the moment the socket is ready. Eliminates the timing race on any hardware.Audio zombie fix — v5.4's
spawn_daemon()never calledwaitpid()on the intermediate child, creating a zombie process on every audio daemon spawn. v5.5 reaps it immediately.Signal handling in
lainos-notifyd— v5.4 had no signal handler, so SIGTERM killed the process mid-recv()without cleanup, leaving the socket file behind. v5.5 handles SIGTERM/SIGINT cleanly and unlinks the socket on exit.Dynamic wireless interface detection — v5.4 hardcoded
wlan0. v5.5 enumerates/sys/class/net/and checksueventforDEVTYPE=wlan, working correctly on any hardware regardless of interface naming.Cleanliness
Debug artifacts removed — v5.4 wrote
/tmp/lainos-init.debugon every single login and hadfprintf(stderr)debug calls throughoutlainos-dbus-bridge. Both removed in v5.5.ldconfigremoved from install hook — leftover from when the mock shared libraries existed. No-op in v5.4, removed in v5.5.rc-update delinpre_remove— v5.4 only stopped services on removal, leaving stale runlevel symlinks behind. v5.5 deregisters them properly.makedependscorrected — v5.4 listeddbusandlibseccompin bothdependsandmakedepends. v5.5 keeps them only independswhere they belong as runtime dependencies.Downloads
-
Source code (ZIP)
1 download
-
Source code (TAR.GZ)
1 download
-
Source code (ZIP)