From 9e55dd6b8f7a2ddbbefbbdc5028a6c709ad97e4c Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Tue, 27 Jan 2026 01:37:50 +0100 Subject: [PATCH] nix/daemon: receive supplementary groups on Linux On Linux, SO_PEERGROUPS is an option introduced in 2017, that is, in Linux 4.13. Thankfully, Lix targets 5.10+ kernels minimum. It was chosen to allocate 128 gids by default and ramp up (2x) as needed rather than allocate a full 65k of integers as it seems wasteful. I bet the time to the 16 allocations should incur at most an additional millisecond on a modern system, don't quote me on that though. This is preparation to enable ability for the daemon to vet based on supplementary groups. Related to #968. Suggested-by: alois31 Suggested-by: eldritch horrors Change-Id: I26d698327db5d174bf70ca25b0afede132bd9169 Signed-off-by: Raito Bezarius --- lix/nix/daemon.cc | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/lix/nix/daemon.cc b/lix/nix/daemon.cc index 67c5491b6..332f83e60 100644 --- a/lix/nix/daemon.cc +++ b/lix/nix/daemon.cc @@ -173,6 +173,7 @@ struct PeerInfo std::optional pid; uid_t uid; gid_t gid; + std::vector supplementaryGids; }; @@ -181,13 +182,50 @@ struct PeerInfo */ static PeerInfo getPeerInfo(int remote) { + std::vector supplementaryGids; #if defined(SO_PEERCRED) ucred cred; socklen_t credLen = sizeof(cred); if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == -1) throw SysError("getting peer credentials"); - PeerInfo peer = {cred.pid, cred.uid, cred.gid}; + +#if defined(SO_PEERGROUPS) + // NOTE(Raito): + // Linux can go up to NGROUPS_MAX supplementary groups (65K). + // It is safe to assume that most users will have a couple of supplementary + // groups by default (here, my Linux user has ~7). + // We allocate 128 because integers are tiny. + supplementaryGids.resize(128); + + // Initially attempt to retrieve 128 groups. + socklen_t nrSupplementaryGids = supplementaryGids.size(); + + while (true) { + + if (getsockopt(remote, SOL_SOCKET, SO_PEERGROUPS, supplementaryGids.data(), &nrSupplementaryGids) + == -1 + && errno != ERANGE) + { + throw SysError("getting peer groups"); + } + + // If the number of groups returned is less than the requested size, we are done. + if (nrSupplementaryGids <= supplementaryGids.size()) { + // We ensure the vector matches exactly the number of groups to avoid + // letting the rest of the vector imply that the vector is full of `root` groups. + supplementaryGids.resize(nrSupplementaryGids); + break; + } + + // Otherwise, the vector is too small. Resize and try again. + nrSupplementaryGids *= 2; + + // We ensure the vector is big enough in response to our latest known allocation requirement. + supplementaryGids.resize(nrSupplementaryGids); + } +#endif + PeerInfo peer = {cred.pid, cred.uid, cred.gid, supplementaryGids}; #elif defined(LOCAL_PEERCRED)