Peer credentials API for Mac and FreeBSD
Currently, the rustix::net::sockopt::socket_peercred is Linux-specific only. The PR that added the API, had pointers to the API used on other platforms but I didn't find any issues regarding this, so opening this issue.
I am not sure what the API would look like though? Ideally we should just make socket_peercred work on those platforms but getpeereid doesn't give you the PID. Perhaps the solution would be to just add a #[cfg(target_os = "linux")] to rustix::net::UCred::pid but then what about rustix::net::SendAncillaryMessage::ScmCredentials? 🤔
cc @ids1024
Looking at this again, as noted in https://github.com/bytecodealliance/rustix/pull/851, LOCAL_PEERCRED on FreeBSD and macOS (as well as DragonflyBSD) will also include the pid. But contains an array of GIDs instead of just one.
It should be possible to unify the return value of that with the return value of SO_PEERCRED by making SO_PEERCRED just use an array with a single GID. But I guess that's a bit semantically different? If FreeBSD would return all the groups the process user is part of, while this would only return the particular GID it is running as (however exactly that works).
Though I guess in a low-level API like this, it's expected that platforms have differences. They just need to be documented.
https://man.freebsd.org/cgi/man.cgi?query=getpeereid&sektion=3 says:
On FreeBSD, getpeereid() is implemented in terms of the LOCAL_PEERCRED socket option.
If getpeereid() just uses LOCAL_PEERCRED, there's presumably no reason to use that on FreeBSD rather than LOCAL_PEERCRED directly. So it could be an option to use LOCAL_PEERCRED where supported, use SO_PEERCRED on Linux. Then on NetBSD and OpenBSD it can use getpeereid() , and have a #[cfg] to make the pid field not present on those platforms.
(The alternative, of course, would be to just expose all these APIs separately in rustix, and let the caller handle the platform differences.)
FreeBSD's getpeereid() is defined in https://github.com/freebsd/freebsd-src/blob/main/lib/libc/gen/getpeereid.c.
https://github.com/freebsd/freebsd-src/blob/main/sys/sys/ucred.h has the definition for struct xucred.
/*
* This is the external representation of struct ucred.
*/
struct xucred {
u_int cr_version; /* structure layout version */
uid_t cr_uid; /* effective user id */
short cr_ngroups; /* number of groups (incl. cr_gid). */
union {
/*
* The effective GID has been the first element of cr_groups[]
* for historical reasons. It should be accessed using the
* 'cr_gid' identifier. Supplementary groups should be accessed
* using cr_sgroups[]. Note that 'cr_ngroups' currently
* includes the effective GID.
*
* XXXOC: On the next API change (requires versioning), please
* replace this union with a true unaliased field 'cr_gid' and
* make sure that cr_groups[]/'cr_ngroups' only account for
* supplementary groups.
*/
struct {
gid_t cr_gid; /* effective group id */
gid_t cr_sgroups[XU_NGROUPS - 1];
};
gid_t cr_groups[XU_NGROUPS]; /* groups */
};
union {
void *_cr_unused1; /* compatibility with old ucred */
pid_t cr_pid;
};
};
So it looks like the first group can be assumed to be the effective group ID (though the definition of this struct will change on a future version of FreeBSD).
Maybe a better way to unify the three APIs then would be to have separate fields for the effective GID and for supplementary groups. The former would be present on all platforms, while the latter would be specific to FreeBSD and family. Similarly to the pid field.
while the latter would be specific to FreeBSD and family
But there is a way to get supplementary groups on non-BSD, e.g Linux. It's not a very direct mechanism though so maybe as I wrote in the comment above, perhaps it's not suited for rustix?
Presumably getgrouplist gets the list of groups associated with the user in /etc/group. Which is different from the GIDs associated with a process. It's not necessarily a problem that changes to /etc/group take effect immediately with this instead of requiring logging in again, but it would prevent recognizing GIDs associated with sandboxed clients, presumably.
getgrouplist is sufficiently un-syscall-like that I expect it's out of scope for rustix. I imagine it should be possible to build a higher-level library that uses rustix's socket_peercred on Linux and wraps getgrouplist or other APIs on other platforms.