diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index dafd143..252f122 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Install system deps - run: sudo apt-get update -qq && sudo apt-get install -y -qq libasound2-dev libchafa-dev + run: sudo apt-get update -qq && sudo apt-get install -y -qq libasound2-dev libchafa-dev libwayland-dev # Pinned fork on our Gitea (rgb patch committed there) — upstream HEAD # must never decide whether our build passes. - name: Clone tuie (pinned fork) @@ -44,8 +44,19 @@ jobs: components: clippy - name: cargo test run: cargo test + # `souveraine-sessiond` is `required-features = ["sessiond"]`, which is + # not in `default` — so the line above never compiled the device state + # authority, let alone ran its tests. Every sessiond change since the + # daemon existed has been type-checked only by the aarch64 cross-build, + # which builds and does not test. A green rust-test meant nothing for the + # one binary that decides whether the phone is locked. Same trap as the + # old primary/public split: a signal that reads like coverage and isn't. + - name: cargo test (sessiond) + run: cargo test --features sessiond --bin souveraine-sessiond - name: cargo clippy run: cargo clippy -- -D warnings + - name: cargo clippy (sessiond) + run: cargo clippy --features sessiond --bin souveraine-sessiond -- -D warnings no-ai-attribution: runs-on: ubuntu-latest diff --git a/src/sessiond/server.rs b/src/sessiond/server.rs index f565824..a35a3e8 100644 --- a/src/sessiond/server.rs +++ b/src/sessiond/server.rs @@ -8,6 +8,7 @@ //! between those states are the only logic here. use std::io::{BufRead, BufReader, Read, Write}; +use std::os::fd::AsRawFd; use std::os::unix::fs::PermissionsExt; use std::os::unix::net::{UnixListener, UnixStream}; use std::path::{Path, PathBuf}; @@ -624,8 +625,30 @@ fn handle_connection(stream: UnixStream, shared: Arc) { /// SO_PEERCRED pid of the process on the other end, or None if the kernel /// would not say. None is never treated as a match: the lease is exclusive and /// "I could not prove who you are" has to fail closed. +/// +/// Deliberately the same getsockopt as machined's `peer_cred`, not +/// `UnixStream::peer_cred` — that one is still unstable +/// (`peer_credentials_unix_socket`, rust#42839) and only fails at the aarch64 +/// build, after the host test job has already gone green. fn peer_pid(stream: &UnixStream) -> Option { - stream.peer_cred().ok().and_then(|c| c.pid) + let mut cred = libc::ucred { pid: 0, uid: 0, gid: 0 }; + let mut len = std::mem::size_of::() as libc::socklen_t; + // SAFETY: SO_PEERCRED fills a ucred struct of the size we pass; the fd is + // live for the duration of the call because we hold &UnixStream. + let rc = unsafe { + libc::getsockopt( + stream.as_raw_fd(), + libc::SOL_SOCKET, + libc::SO_PEERCRED, + &mut cred as *mut libc::ucred as *mut libc::c_void, + &mut len, + ) + }; + if rc == 0 { + Some(cred.pid) + } else { + None + } } fn handle_request(