tokio_uds/
ucred.rs

1use libc::{gid_t, uid_t};
2
3/// Credentials of a process
4#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
5pub struct UCred {
6    /// UID (user ID) of the process
7    pub uid: uid_t,
8    /// GID (group ID) of the process
9    pub gid: gid_t,
10}
11
12#[cfg(any(target_os = "linux", target_os = "android"))]
13pub use self::impl_linux::get_peer_cred;
14
15#[cfg(any(
16    target_os = "dragonfly",
17    target_os = "macos",
18    target_os = "ios",
19    target_os = "freebsd",
20    target_os = "netbsd",
21    target_os = "openbsd"
22))]
23pub use self::impl_macos::get_peer_cred;
24
25#[cfg(any(target_os = "solaris", target_os = "illumos"))]
26pub use self::impl_solaris::get_peer_cred;
27
28#[cfg(any(target_os = "linux", target_os = "android"))]
29pub mod impl_linux {
30    use libc::{c_void, getsockopt, socklen_t, SOL_SOCKET, SO_PEERCRED};
31    use std::os::unix::io::AsRawFd;
32    use std::{io, mem};
33    use UnixStream;
34
35    use libc::ucred;
36
37    pub fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
38        unsafe {
39            let raw_fd = sock.as_raw_fd();
40
41            let mut ucred = ucred {
42                pid: 0,
43                uid: 0,
44                gid: 0,
45            };
46
47            let ucred_size = mem::size_of::<ucred>();
48
49            // These paranoid checks should be optimized-out
50            assert!(mem::size_of::<u32>() <= mem::size_of::<usize>());
51            assert!(ucred_size <= u32::max_value() as usize);
52
53            let mut ucred_size = ucred_size as socklen_t;
54
55            let ret = getsockopt(
56                raw_fd,
57                SOL_SOCKET,
58                SO_PEERCRED,
59                &mut ucred as *mut ucred as *mut c_void,
60                &mut ucred_size,
61            );
62            if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
63                Ok(super::UCred {
64                    uid: ucred.uid,
65                    gid: ucred.gid,
66                })
67            } else {
68                Err(io::Error::last_os_error())
69            }
70        }
71    }
72}
73
74#[cfg(any(
75    target_os = "dragonfly",
76    target_os = "macos",
77    target_os = "ios",
78    target_os = "freebsd",
79    target_os = "netbsd",
80    target_os = "openbsd"
81))]
82pub mod impl_macos {
83    use libc::getpeereid;
84    use std::os::unix::io::AsRawFd;
85    use std::{io, mem};
86    use UnixStream;
87
88    pub fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
89        unsafe {
90            let raw_fd = sock.as_raw_fd();
91
92            let mut cred: super::UCred = mem::uninitialized();
93
94            let ret = getpeereid(raw_fd, &mut cred.uid, &mut cred.gid);
95
96            if ret == 0 {
97                Ok(cred)
98            } else {
99                Err(io::Error::last_os_error())
100            }
101        }
102    }
103}
104
105#[cfg(any(target_os = "solaris", target_os = "illumos"))]
106pub mod impl_solaris {
107    use std::io;
108    use std::os::unix::io::AsRawFd;
109    use std::ptr;
110    use UnixStream;
111
112    #[allow(non_camel_case_types)]
113    enum ucred_t {}
114
115    extern "C" {
116        fn ucred_free(cred: *mut ucred_t);
117        fn ucred_geteuid(cred: *const ucred_t) -> super::uid_t;
118        fn ucred_getegid(cred: *const ucred_t) -> super::gid_t;
119
120        fn getpeerucred(
121            fd: ::std::os::raw::c_int,
122            cred: *mut *mut ucred_t,
123        ) -> ::std::os::raw::c_int;
124    }
125
126    pub fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
127        unsafe {
128            let raw_fd = sock.as_raw_fd();
129
130            let mut cred = ptr::null_mut::<*mut ucred_t>() as *mut ucred_t;
131
132            let ret = getpeerucred(raw_fd, &mut cred);
133
134            if ret == 0 {
135                let uid = ucred_geteuid(cred);
136                let gid = ucred_getegid(cred);
137
138                ucred_free(cred);
139
140                Ok(super::UCred { uid, gid })
141            } else {
142                Err(io::Error::last_os_error())
143            }
144        }
145    }
146}
147
148// Note that LOCAL_PEERCRED is not supported on DragonFly (yet). So do not run tests.
149#[cfg(not(target_os = "dragonfly"))]
150#[cfg(test)]
151mod test {
152    use libc::getegid;
153    use libc::geteuid;
154    use UnixStream;
155
156    #[test]
157    #[cfg_attr(
158        target_os = "freebsd",
159        ignore = "Requires FreeBSD 12.0 or later. https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=176419"
160    )]
161    #[cfg_attr(
162        target_os = "netbsd",
163        ignore = "NetBSD does not support getpeereid() for sockets created by socketpair()"
164    )]
165    fn test_socket_pair() {
166        let (a, b) = UnixStream::pair().unwrap();
167        let cred_a = a.peer_cred().unwrap();
168        let cred_b = b.peer_cred().unwrap();
169        assert_eq!(cred_a, cred_b);
170
171        let uid = unsafe { geteuid() };
172        let gid = unsafe { getegid() };
173
174        assert_eq!(cred_a.uid, uid);
175        assert_eq!(cred_a.gid, gid);
176    }
177}