1 use libc::{gid_t, pid_t, uid_t};
2 
3 /// Credentials of a process.
4 #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
5 pub struct UCred {
6     /// PID (process ID) of the process.
7     pid: Option<pid_t>,
8     /// UID (user ID) of the process.
9     uid: uid_t,
10     /// GID (group ID) of the process.
11     gid: gid_t,
12 }
13 
14 impl UCred {
15     /// Gets UID (user ID) of the process.
uid(&self) -> uid_t16     pub fn uid(&self) -> uid_t {
17         self.uid
18     }
19 
20     /// Gets GID (group ID) of the process.
gid(&self) -> gid_t21     pub fn gid(&self) -> gid_t {
22         self.gid
23     }
24 
25     /// Gets PID (process ID) of the process.
26     ///
27     /// This is only implemented under Linux, Android, iOS, macOS, Solaris and
28     /// Illumos. On other platforms this will always return `None`.
pid(&self) -> Option<pid_t>29     pub fn pid(&self) -> Option<pid_t> {
30         self.pid
31     }
32 }
33 
34 #[cfg(any(target_os = "linux", target_os = "android", target_os = "openbsd"))]
35 pub(crate) use self::impl_linux::get_peer_cred;
36 
37 #[cfg(any(target_os = "netbsd"))]
38 pub(crate) use self::impl_netbsd::get_peer_cred;
39 
40 #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
41 pub(crate) use self::impl_bsd::get_peer_cred;
42 
43 #[cfg(any(target_os = "macos", target_os = "ios"))]
44 pub(crate) use self::impl_macos::get_peer_cred;
45 
46 #[cfg(any(target_os = "solaris", target_os = "illumos"))]
47 pub(crate) use self::impl_solaris::get_peer_cred;
48 
49 #[cfg(any(target_os = "linux", target_os = "android", target_os = "openbsd"))]
50 pub(crate) mod impl_linux {
51     use crate::net::unix::UnixStream;
52 
53     use libc::{c_void, getsockopt, socklen_t, SOL_SOCKET, SO_PEERCRED};
54     use std::{io, mem};
55 
56     #[cfg(target_os = "openbsd")]
57     use libc::sockpeercred as ucred;
58     #[cfg(any(target_os = "linux", target_os = "android"))]
59     use libc::ucred;
60 
get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred>61     pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
62         use std::os::unix::io::AsRawFd;
63 
64         unsafe {
65             let raw_fd = sock.as_raw_fd();
66 
67             let mut ucred = ucred {
68                 pid: 0,
69                 uid: 0,
70                 gid: 0,
71             };
72 
73             let ucred_size = mem::size_of::<ucred>();
74 
75             // These paranoid checks should be optimized-out
76             assert!(mem::size_of::<u32>() <= mem::size_of::<usize>());
77             assert!(ucred_size <= u32::MAX as usize);
78 
79             let mut ucred_size = ucred_size as socklen_t;
80 
81             let ret = getsockopt(
82                 raw_fd,
83                 SOL_SOCKET,
84                 SO_PEERCRED,
85                 &mut ucred as *mut ucred as *mut c_void,
86                 &mut ucred_size,
87             );
88             if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
89                 Ok(super::UCred {
90                     uid: ucred.uid,
91                     gid: ucred.gid,
92                     pid: Some(ucred.pid),
93                 })
94             } else {
95                 Err(io::Error::last_os_error())
96             }
97         }
98     }
99 }
100 
101 #[cfg(any(target_os = "netbsd"))]
102 pub(crate) mod impl_netbsd {
103     use crate::net::unix::UnixStream;
104 
105     use libc::{c_void, getsockopt, socklen_t, unpcbid, LOCAL_PEEREID, SOL_SOCKET};
106     use std::io;
107     use std::mem::size_of;
108     use std::os::unix::io::AsRawFd;
109 
get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred>110     pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
111         unsafe {
112             let raw_fd = sock.as_raw_fd();
113 
114             let mut unpcbid = unpcbid {
115                 unp_pid: 0,
116                 unp_euid: 0,
117                 unp_egid: 0,
118             };
119 
120             let unpcbid_size = size_of::<unpcbid>();
121             let mut unpcbid_size = unpcbid_size as socklen_t;
122 
123             let ret = getsockopt(
124                 raw_fd,
125                 SOL_SOCKET,
126                 LOCAL_PEEREID,
127                 &mut unpcbid as *mut unpcbid as *mut c_void,
128                 &mut unpcbid_size,
129             );
130             if ret == 0 && unpcbid_size as usize == size_of::<unpcbid>() {
131                 Ok(super::UCred {
132                     uid: unpcbid.unp_euid,
133                     gid: unpcbid.unp_egid,
134                     pid: Some(unpcbid.unp_pid),
135                 })
136             } else {
137                 Err(io::Error::last_os_error())
138             }
139         }
140     }
141 }
142 
143 #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
144 pub(crate) mod impl_bsd {
145     use crate::net::unix::UnixStream;
146 
147     use libc::getpeereid;
148     use std::io;
149     use std::mem::MaybeUninit;
150     use std::os::unix::io::AsRawFd;
151 
get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred>152     pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
153         unsafe {
154             let raw_fd = sock.as_raw_fd();
155 
156             let mut uid = MaybeUninit::uninit();
157             let mut gid = MaybeUninit::uninit();
158 
159             let ret = getpeereid(raw_fd, uid.as_mut_ptr(), gid.as_mut_ptr());
160 
161             if ret == 0 {
162                 Ok(super::UCred {
163                     uid: uid.assume_init(),
164                     gid: gid.assume_init(),
165                     pid: None,
166                 })
167             } else {
168                 Err(io::Error::last_os_error())
169             }
170         }
171     }
172 }
173 
174 #[cfg(any(target_os = "macos", target_os = "ios"))]
175 pub(crate) mod impl_macos {
176     use crate::net::unix::UnixStream;
177 
178     use libc::{c_void, getpeereid, getsockopt, pid_t, LOCAL_PEEREPID, SOL_LOCAL};
179     use std::io;
180     use std::mem::size_of;
181     use std::mem::MaybeUninit;
182     use std::os::unix::io::AsRawFd;
183 
get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred>184     pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
185         unsafe {
186             let raw_fd = sock.as_raw_fd();
187 
188             let mut uid = MaybeUninit::uninit();
189             let mut gid = MaybeUninit::uninit();
190             let mut pid: MaybeUninit<pid_t> = MaybeUninit::uninit();
191             let mut pid_size: MaybeUninit<u32> = MaybeUninit::new(size_of::<pid_t>() as u32);
192 
193             if getsockopt(
194                 raw_fd,
195                 SOL_LOCAL,
196                 LOCAL_PEEREPID,
197                 pid.as_mut_ptr() as *mut c_void,
198                 pid_size.as_mut_ptr(),
199             ) != 0
200             {
201                 return Err(io::Error::last_os_error());
202             }
203 
204             assert!(pid_size.assume_init() == (size_of::<pid_t>() as u32));
205 
206             let ret = getpeereid(raw_fd, uid.as_mut_ptr(), gid.as_mut_ptr());
207 
208             if ret == 0 {
209                 Ok(super::UCred {
210                     uid: uid.assume_init(),
211                     gid: gid.assume_init(),
212                     pid: Some(pid.assume_init()),
213                 })
214             } else {
215                 Err(io::Error::last_os_error())
216             }
217         }
218     }
219 }
220 
221 #[cfg(any(target_os = "solaris", target_os = "illumos"))]
222 pub(crate) mod impl_solaris {
223     use crate::net::unix::UnixStream;
224     use std::io;
225     use std::os::unix::io::AsRawFd;
226     use std::ptr;
227 
get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred>228     pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
229         unsafe {
230             let raw_fd = sock.as_raw_fd();
231 
232             let mut cred = ptr::null_mut();
233             let ret = libc::getpeerucred(raw_fd, &mut cred);
234 
235             if ret == 0 {
236                 let uid = libc::ucred_geteuid(cred);
237                 let gid = libc::ucred_getegid(cred);
238                 let pid = libc::ucred_getpid(cred);
239 
240                 libc::ucred_free(cred);
241 
242                 Ok(super::UCred {
243                     uid,
244                     gid,
245                     pid: Some(pid),
246                 })
247             } else {
248                 Err(io::Error::last_os_error())
249             }
250         }
251     }
252 }
253