1 use crate::net::unix;
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<unix::pid_t>,
8     /// UID (user ID) of the process.
9     uid: unix::uid_t,
10     /// GID (group ID) of the process.
11     gid: unix::gid_t,
12 }
13 
14 impl UCred {
15     /// Gets UID (user ID) of the process.
uid(&self) -> unix::uid_t16     pub fn uid(&self) -> unix::uid_t {
17         self.uid
18     }
19 
20     /// Gets GID (group ID) of the process.
gid(&self) -> unix::gid_t21     pub fn gid(&self) -> unix::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<unix::pid_t>29     pub fn pid(&self) -> Option<unix::pid_t> {
30         self.pid
31     }
32 }
33 
34 #[cfg(any(
35     target_os = "linux",
36     target_os = "redox",
37     target_os = "android",
38     target_os = "openbsd"
39 ))]
40 pub(crate) use self::impl_linux::get_peer_cred;
41 
42 #[cfg(any(target_os = "netbsd", target_os = "nto"))]
43 pub(crate) use self::impl_netbsd::get_peer_cred;
44 
45 #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
46 pub(crate) use self::impl_bsd::get_peer_cred;
47 
48 #[cfg(any(
49     target_os = "macos",
50     target_os = "ios",
51     target_os = "tvos",
52     target_os = "watchos",
53     target_os = "visionos"
54 ))]
55 pub(crate) use self::impl_macos::get_peer_cred;
56 
57 #[cfg(any(target_os = "solaris", target_os = "illumos"))]
58 pub(crate) use self::impl_solaris::get_peer_cred;
59 
60 #[cfg(target_os = "aix")]
61 pub(crate) use self::impl_aix::get_peer_cred;
62 
63 #[cfg(any(target_os = "espidf", target_os = "vita"))]
64 pub(crate) use self::impl_noproc::get_peer_cred;
65 
66 #[cfg(any(
67     target_os = "linux",
68     target_os = "redox",
69     target_os = "android",
70     target_os = "openbsd"
71 ))]
72 pub(crate) mod impl_linux {
73     use crate::net::unix::{self, UnixStream};
74 
75     use libc::{c_void, getsockopt, socklen_t, SOL_SOCKET, SO_PEERCRED};
76     use std::{io, mem};
77 
78     #[cfg(target_os = "openbsd")]
79     use libc::sockpeercred as ucred;
80     #[cfg(any(target_os = "linux", target_os = "redox", target_os = "android"))]
81     use libc::ucred;
82 
get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred>83     pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
84         use std::os::unix::io::AsRawFd;
85 
86         unsafe {
87             let raw_fd = sock.as_raw_fd();
88 
89             let mut ucred = ucred {
90                 pid: 0,
91                 uid: 0,
92                 gid: 0,
93             };
94 
95             let ucred_size = mem::size_of::<ucred>();
96 
97             // These paranoid checks should be optimized-out
98             assert!(mem::size_of::<u32>() <= mem::size_of::<usize>());
99             assert!(ucred_size <= u32::MAX as usize);
100 
101             let mut ucred_size = ucred_size as socklen_t;
102 
103             let ret = getsockopt(
104                 raw_fd,
105                 SOL_SOCKET,
106                 SO_PEERCRED,
107                 &mut ucred as *mut ucred as *mut c_void,
108                 &mut ucred_size,
109             );
110             if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
111                 Ok(super::UCred {
112                     uid: ucred.uid as unix::uid_t,
113                     gid: ucred.gid as unix::gid_t,
114                     pid: Some(ucred.pid as unix::pid_t),
115                 })
116             } else {
117                 Err(io::Error::last_os_error())
118             }
119         }
120     }
121 }
122 
123 #[cfg(any(target_os = "netbsd", target_os = "nto"))]
124 pub(crate) mod impl_netbsd {
125     use crate::net::unix::{self, UnixStream};
126 
127     use libc::{c_void, getsockopt, socklen_t, unpcbid, LOCAL_PEEREID, SOL_SOCKET};
128     use std::io;
129     use std::mem::size_of;
130     use std::os::unix::io::AsRawFd;
131 
get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred>132     pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
133         unsafe {
134             let raw_fd = sock.as_raw_fd();
135 
136             let mut unpcbid = unpcbid {
137                 unp_pid: 0,
138                 unp_euid: 0,
139                 unp_egid: 0,
140             };
141 
142             let unpcbid_size = size_of::<unpcbid>();
143             let mut unpcbid_size = unpcbid_size as socklen_t;
144 
145             let ret = getsockopt(
146                 raw_fd,
147                 SOL_SOCKET,
148                 LOCAL_PEEREID,
149                 &mut unpcbid as *mut unpcbid as *mut c_void,
150                 &mut unpcbid_size,
151             );
152             if ret == 0 && unpcbid_size as usize == size_of::<unpcbid>() {
153                 Ok(super::UCred {
154                     uid: unpcbid.unp_euid as unix::uid_t,
155                     gid: unpcbid.unp_egid as unix::gid_t,
156                     pid: Some(unpcbid.unp_pid as unix::pid_t),
157                 })
158             } else {
159                 Err(io::Error::last_os_error())
160             }
161         }
162     }
163 }
164 
165 #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
166 pub(crate) mod impl_bsd {
167     use crate::net::unix::{self, UnixStream};
168 
169     use libc::getpeereid;
170     use std::io;
171     use std::mem::MaybeUninit;
172     use std::os::unix::io::AsRawFd;
173 
get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred>174     pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
175         unsafe {
176             let raw_fd = sock.as_raw_fd();
177 
178             let mut uid = MaybeUninit::uninit();
179             let mut gid = MaybeUninit::uninit();
180 
181             let ret = getpeereid(raw_fd, uid.as_mut_ptr(), gid.as_mut_ptr());
182 
183             if ret == 0 {
184                 Ok(super::UCred {
185                     uid: uid.assume_init() as unix::uid_t,
186                     gid: gid.assume_init() as unix::gid_t,
187                     pid: None,
188                 })
189             } else {
190                 Err(io::Error::last_os_error())
191             }
192         }
193     }
194 }
195 
196 #[cfg(any(
197     target_os = "macos",
198     target_os = "ios",
199     target_os = "tvos",
200     target_os = "watchos",
201     target_os = "visionos"
202 ))]
203 pub(crate) mod impl_macos {
204     use crate::net::unix::{self, UnixStream};
205 
206     use libc::{c_void, getpeereid, getsockopt, pid_t, LOCAL_PEEREPID, SOL_LOCAL};
207     use std::io;
208     use std::mem::size_of;
209     use std::mem::MaybeUninit;
210     use std::os::unix::io::AsRawFd;
211 
get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred>212     pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
213         unsafe {
214             let raw_fd = sock.as_raw_fd();
215 
216             let mut uid = MaybeUninit::uninit();
217             let mut gid = MaybeUninit::uninit();
218             let mut pid: MaybeUninit<pid_t> = MaybeUninit::uninit();
219             let mut pid_size: MaybeUninit<u32> = MaybeUninit::new(size_of::<pid_t>() as u32);
220 
221             if getsockopt(
222                 raw_fd,
223                 SOL_LOCAL,
224                 LOCAL_PEEREPID,
225                 pid.as_mut_ptr() as *mut c_void,
226                 pid_size.as_mut_ptr(),
227             ) != 0
228             {
229                 return Err(io::Error::last_os_error());
230             }
231 
232             assert!(pid_size.assume_init() == (size_of::<pid_t>() as u32));
233 
234             let ret = getpeereid(raw_fd, uid.as_mut_ptr(), gid.as_mut_ptr());
235 
236             if ret == 0 {
237                 Ok(super::UCred {
238                     uid: uid.assume_init() as unix::uid_t,
239                     gid: gid.assume_init() as unix::gid_t,
240                     pid: Some(pid.assume_init() as unix::pid_t),
241                 })
242             } else {
243                 Err(io::Error::last_os_error())
244             }
245         }
246     }
247 }
248 
249 #[cfg(any(target_os = "solaris", target_os = "illumos"))]
250 pub(crate) mod impl_solaris {
251     use crate::net::unix::{self, UnixStream};
252     use std::io;
253     use std::os::unix::io::AsRawFd;
254     use std::ptr;
255 
get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred>256     pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
257         unsafe {
258             let raw_fd = sock.as_raw_fd();
259 
260             let mut cred = ptr::null_mut();
261             let ret = libc::getpeerucred(raw_fd, &mut cred);
262 
263             if ret == 0 {
264                 let uid = libc::ucred_geteuid(cred);
265                 let gid = libc::ucred_getegid(cred);
266                 let pid = libc::ucred_getpid(cred);
267 
268                 libc::ucred_free(cred);
269 
270                 Ok(super::UCred {
271                     uid: uid as unix::uid_t,
272                     gid: gid as unix::gid_t,
273                     pid: Some(pid as unix::pid_t),
274                 })
275             } else {
276                 Err(io::Error::last_os_error())
277             }
278         }
279     }
280 }
281 
282 #[cfg(target_os = "aix")]
283 pub(crate) mod impl_aix {
284     use crate::net::unix::UnixStream;
285     use std::io;
286     use std::os::unix::io::AsRawFd;
287 
get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred>288     pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
289         unsafe {
290             let raw_fd = sock.as_raw_fd();
291 
292             let mut uid = std::mem::MaybeUninit::uninit();
293             let mut gid = std::mem::MaybeUninit::uninit();
294 
295             let ret = libc::getpeereid(raw_fd, uid.as_mut_ptr(), gid.as_mut_ptr());
296 
297             if ret == 0 {
298                 Ok(super::UCred {
299                     uid: uid.assume_init(),
300                     gid: gid.assume_init(),
301                     pid: None,
302                 })
303             } else {
304                 Err(io::Error::last_os_error())
305             }
306         }
307     }
308 }
309 
310 #[cfg(any(target_os = "espidf", target_os = "vita"))]
311 pub(crate) mod impl_noproc {
312     use crate::net::unix::UnixStream;
313     use std::io;
314 
get_peer_cred(_sock: &UnixStream) -> io::Result<super::UCred>315     pub(crate) fn get_peer_cred(_sock: &UnixStream) -> io::Result<super::UCred> {
316         Ok(super::UCred {
317             uid: 0,
318             gid: 0,
319             pid: None,
320         })
321     }
322 }
323