Skip to main content

lonca_client/
sys.rs

1//! The calls into libc a client needs: who it is, and who is on the other
2//! end of a socket. Unsafe code is refused in the rest of the workspace
3//! (`[workspace.lints]`); each block here says why it is sound.
4
5#![allow(unsafe_code)]
6
7use std::os::fd::AsRawFd;
8use std::os::unix::net::UnixStream;
9
10/// The real uid of this process.
11pub fn current_uid() -> u32 {
12    // SAFETY: getuid has no preconditions and can not fail.
13    unsafe { libc::getuid() }
14}
15
16/// The credentials of the process on the other end of `s` (Linux).
17#[cfg(target_os = "linux")]
18pub fn peer_cred(s: &UnixStream) -> Option<libc::ucred> {
19    let mut cred = libc::ucred {
20        pid: 0,
21        uid: 0,
22        gid: 0,
23    };
24    let mut len = std::mem::size_of::<libc::ucred>() as libc::socklen_t;
25    // SAFETY: the descriptor is a socket that lives as long as `s`; `cred`
26    // and `len` are live, and `len` holds the size of `cred`.
27    let rc = unsafe {
28        libc::getsockopt(
29            s.as_raw_fd(),
30            libc::SOL_SOCKET,
31            libc::SO_PEERCRED,
32            (&raw mut cred).cast::<libc::c_void>(),
33            &mut len,
34        )
35    };
36    (rc == 0).then_some(cred)
37}
38
39/// The uid of the process on the other end of a Unix socket.
40pub fn peer_uid(s: &UnixStream) -> Option<u32> {
41    #[cfg(target_os = "linux")]
42    {
43        peer_cred(s).map(|c| c.uid)
44    }
45    #[cfg(not(target_os = "linux"))]
46    {
47        let mut uid: libc::uid_t = 0;
48        let mut gid: libc::gid_t = 0;
49        // SAFETY: the descriptor is a socket that lives as long as `s`; the
50        // out pointers point to live stack variables.
51        let rc = unsafe { libc::getpeereid(s.as_raw_fd(), &mut uid, &mut gid) };
52        (rc == 0).then_some(uid)
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn the_peer_of_a_pair_is_this_process() {
62        let (a, _b) = UnixStream::pair().unwrap();
63        assert_eq!(peer_uid(&a), Some(current_uid()));
64    }
65}