Skip to main content

tokio/net/unix/
ucred.rs

1use crate::net::unix;
2
3/// Credentials of a process.
4#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
5pub 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
14impl UCred {
15    /// Gets UID (user ID) of the process.
16    pub fn uid(&self) -> unix::uid_t {
17        self.uid
18    }
19
20    /// Gets GID (group ID) of the process.
21    pub fn gid(&self) -> unix::gid_t {
22        self.gid
23    }
24
25    /// Gets PID (process ID) of the process.
26    ///
27    /// This is implemented under Linux, Android, OpenBSD, FreeBSD (since
28    /// FreeBSD 13), NetBSD, NTO, iOS, macOS, tvOS, watchOS, visionOS,
29    /// Solaris, Illumos, Cygwin, Haiku, and Redox. On other platforms this
30    /// will always return `None`.
31    pub fn pid(&self) -> Option<unix::pid_t> {
32        self.pid
33    }
34}
35
36#[cfg(any(
37    target_os = "linux",
38    target_os = "redox",
39    target_os = "android",
40    target_os = "openbsd",
41    target_os = "haiku",
42    target_os = "cygwin"
43))]
44pub(crate) use self::impl_linux::get_peer_cred;
45
46#[cfg(target_os = "netbsd")]
47pub(crate) use self::impl_netbsd::get_peer_cred;
48
49#[cfg(target_os = "dragonfly")]
50pub(crate) use self::impl_dragonfly::get_peer_cred;
51
52#[cfg(target_os = "freebsd")]
53pub(crate) use self::impl_freebsd::get_peer_cred;
54
55#[cfg(any(
56    target_os = "macos",
57    target_os = "ios",
58    target_os = "tvos",
59    target_os = "watchos",
60    target_os = "visionos"
61))]
62pub(crate) use self::impl_macos::get_peer_cred;
63
64#[cfg(any(target_os = "solaris", target_os = "illumos"))]
65pub(crate) use self::impl_solaris::get_peer_cred;
66
67#[cfg(target_os = "aix")]
68pub(crate) use self::impl_aix::get_peer_cred;
69
70#[cfg(any(
71    target_os = "espidf",
72    target_os = "nuttx",
73    target_os = "vita",
74    target_os = "hurd"
75))]
76pub(crate) use self::impl_noproc::get_peer_cred;
77
78#[cfg(target_os = "nto")]
79pub(crate) use self::impl_nto::get_peer_cred;
80
81#[cfg(any(
82    target_os = "linux",
83    target_os = "redox",
84    target_os = "android",
85    target_os = "openbsd",
86    target_os = "haiku",
87    target_os = "cygwin"
88))]
89pub(crate) mod impl_linux {
90    use crate::net::unix::{self, UnixStream};
91
92    use libc::{c_void, getsockopt, socklen_t, SOL_SOCKET, SO_PEERCRED};
93    use std::{io, mem};
94
95    #[cfg(target_os = "openbsd")]
96    use libc::sockpeercred as ucred;
97    #[cfg(any(
98        target_os = "linux",
99        target_os = "redox",
100        target_os = "android",
101        target_os = "haiku",
102        target_os = "cygwin"
103    ))]
104    use libc::ucred;
105
106    pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
107        use std::os::unix::io::AsRawFd;
108
109        unsafe {
110            let raw_fd = sock.as_raw_fd();
111
112            let mut ucred = ucred {
113                pid: 0,
114                uid: 0,
115                gid: 0,
116            };
117
118            let ucred_size = mem::size_of::<ucred>();
119
120            // These paranoid checks should be optimized-out
121            assert!(mem::size_of::<u32>() <= mem::size_of::<usize>());
122            assert!(ucred_size <= u32::MAX as usize);
123
124            let mut ucred_size = ucred_size as socklen_t;
125
126            let ret = getsockopt(
127                raw_fd,
128                SOL_SOCKET,
129                SO_PEERCRED,
130                &mut ucred as *mut ucred as *mut c_void,
131                &mut ucred_size,
132            );
133            if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
134                Ok(super::UCred {
135                    uid: ucred.uid as unix::uid_t,
136                    gid: ucred.gid as unix::gid_t,
137                    pid: Some(ucred.pid as unix::pid_t),
138                })
139            } else {
140                Err(io::Error::last_os_error())
141            }
142        }
143    }
144}
145
146#[cfg(any(target_os = "netbsd", target_os = "nto"))]
147pub(crate) mod impl_netbsd {
148    use crate::net::unix::{self, UnixStream};
149
150    use libc::{c_void, getsockopt, socklen_t, unpcbid, LOCAL_PEEREID, SOL_SOCKET};
151    use std::io;
152    use std::mem::size_of;
153    use std::os::unix::io::AsRawFd;
154
155    pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
156        unsafe {
157            let raw_fd = sock.as_raw_fd();
158
159            let mut unpcbid = unpcbid {
160                unp_pid: 0,
161                unp_euid: 0,
162                unp_egid: 0,
163            };
164
165            let unpcbid_size = size_of::<unpcbid>();
166            let mut unpcbid_size = unpcbid_size as socklen_t;
167
168            let ret = getsockopt(
169                raw_fd,
170                SOL_SOCKET,
171                LOCAL_PEEREID,
172                &mut unpcbid as *mut unpcbid as *mut c_void,
173                &mut unpcbid_size,
174            );
175            if ret == 0 && unpcbid_size as usize == size_of::<unpcbid>() {
176                Ok(super::UCred {
177                    uid: unpcbid.unp_euid as unix::uid_t,
178                    gid: unpcbid.unp_egid as unix::gid_t,
179                    pid: Some(unpcbid.unp_pid as unix::pid_t),
180                })
181            } else {
182                Err(io::Error::last_os_error())
183            }
184        }
185    }
186}
187
188#[cfg(target_os = "dragonfly")]
189pub(crate) mod impl_dragonfly {
190    use crate::net::unix::{self, UnixStream};
191
192    use libc::getpeereid;
193    use std::io;
194    use std::mem::MaybeUninit;
195    use std::os::unix::io::AsRawFd;
196
197    pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
198        unsafe {
199            let raw_fd = sock.as_raw_fd();
200
201            let mut uid = MaybeUninit::uninit();
202            let mut gid = MaybeUninit::uninit();
203
204            let ret = getpeereid(raw_fd, uid.as_mut_ptr(), gid.as_mut_ptr());
205
206            if ret == 0 {
207                Ok(super::UCred {
208                    uid: uid.assume_init() as unix::uid_t,
209                    gid: gid.assume_init() as unix::gid_t,
210                    pid: None,
211                })
212            } else {
213                Err(io::Error::last_os_error())
214            }
215        }
216    }
217}
218
219#[cfg(target_os = "freebsd")]
220pub(crate) mod impl_freebsd {
221    use crate::net::unix::{self, UnixStream};
222
223    use libc::{c_void, getsockopt, socklen_t, xucred, LOCAL_PEERCRED, XUCRED_VERSION};
224    use std::io;
225    use std::mem::{size_of, MaybeUninit};
226    use std::os::unix::io::AsRawFd;
227
228    pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
229        // `SOL_LOCAL` is not re-exported by `libc` for FreeBSD; it is defined
230        // as 0 in `<sys/un.h>`.
231        const SOL_LOCAL: libc::c_int = 0;
232
233        unsafe {
234            let raw_fd = sock.as_raw_fd();
235
236            let mut xucred = MaybeUninit::<xucred>::zeroed();
237            let mut len = size_of::<xucred>() as socklen_t;
238
239            let ret = getsockopt(
240                raw_fd,
241                SOL_LOCAL,
242                LOCAL_PEERCRED,
243                xucred.as_mut_ptr() as *mut c_void,
244                &mut len,
245            );
246
247            if ret != 0 {
248                return Err(io::Error::last_os_error());
249            }
250            if len as usize != size_of::<xucred>() {
251                return Err(io::Error::new(
252                    io::ErrorKind::InvalidData,
253                    "unexpected xucred size from LOCAL_PEERCRED",
254                ));
255            }
256
257            let xucred = xucred.assume_init();
258
259            // Match `getpeereid(3)` and reject any `xucred` whose version we
260            // don't know how to interpret.
261            if xucred.cr_version != XUCRED_VERSION {
262                return Err(io::Error::new(
263                    io::ErrorKind::InvalidData,
264                    "unexpected xucred version from LOCAL_PEERCRED",
265                ));
266            }
267
268            // `cr_pid` is populated by the kernel since FreeBSD 13. PID 0 is
269            // the kernel scheduler and never a real userland peer, so we
270            // surface it as `None` rather than a misleading `Some(0)`.
271            let pid = match xucred.cr_pid__c_anonymous_union.cr_pid {
272                0 => None,
273                p => Some(p as unix::pid_t),
274            };
275
276            // `xucred` carries the effective uid in `cr_uid` and the effective
277            // gid in `cr_groups[0]`, matching what `getpeereid(2)` returns.
278            Ok(super::UCred {
279                uid: xucred.cr_uid as unix::uid_t,
280                gid: xucred.cr_groups[0] as unix::gid_t,
281                pid,
282            })
283        }
284    }
285}
286
287#[cfg(any(
288    target_os = "macos",
289    target_os = "ios",
290    target_os = "tvos",
291    target_os = "watchos",
292    target_os = "visionos"
293))]
294pub(crate) mod impl_macos {
295    use crate::net::unix::{self, UnixStream};
296
297    use libc::{c_void, getpeereid, getsockopt, pid_t, LOCAL_PEEREPID, SOL_LOCAL};
298    use std::io;
299    use std::mem::size_of;
300    use std::mem::MaybeUninit;
301    use std::os::unix::io::AsRawFd;
302
303    pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
304        unsafe {
305            let raw_fd = sock.as_raw_fd();
306
307            let mut uid = MaybeUninit::uninit();
308            let mut gid = MaybeUninit::uninit();
309            let mut pid: MaybeUninit<pid_t> = MaybeUninit::uninit();
310            let mut pid_size: MaybeUninit<u32> = MaybeUninit::new(size_of::<pid_t>() as u32);
311
312            if getsockopt(
313                raw_fd,
314                SOL_LOCAL,
315                LOCAL_PEEREPID,
316                pid.as_mut_ptr() as *mut c_void,
317                pid_size.as_mut_ptr(),
318            ) != 0
319            {
320                return Err(io::Error::last_os_error());
321            }
322
323            assert!(pid_size.assume_init() == (size_of::<pid_t>() as u32));
324
325            let ret = getpeereid(raw_fd, uid.as_mut_ptr(), gid.as_mut_ptr());
326
327            if ret == 0 {
328                Ok(super::UCred {
329                    uid: uid.assume_init() as unix::uid_t,
330                    gid: gid.assume_init() as unix::gid_t,
331                    pid: Some(pid.assume_init() as unix::pid_t),
332                })
333            } else {
334                Err(io::Error::last_os_error())
335            }
336        }
337    }
338}
339
340#[cfg(any(target_os = "solaris", target_os = "illumos"))]
341pub(crate) mod impl_solaris {
342    use crate::net::unix::{self, UnixStream};
343    use std::io;
344    use std::os::unix::io::AsRawFd;
345    use std::ptr;
346
347    pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
348        unsafe {
349            let raw_fd = sock.as_raw_fd();
350
351            let mut cred = ptr::null_mut();
352            let ret = libc::getpeerucred(raw_fd, &mut cred);
353
354            if ret == 0 {
355                let uid = libc::ucred_geteuid(cred);
356                let gid = libc::ucred_getegid(cred);
357                let pid = libc::ucred_getpid(cred);
358
359                libc::ucred_free(cred);
360
361                Ok(super::UCred {
362                    uid: uid as unix::uid_t,
363                    gid: gid as unix::gid_t,
364                    pid: Some(pid as unix::pid_t),
365                })
366            } else {
367                Err(io::Error::last_os_error())
368            }
369        }
370    }
371}
372
373#[cfg(target_os = "aix")]
374pub(crate) mod impl_aix {
375    use crate::net::unix::UnixStream;
376    use std::io;
377    use std::os::unix::io::AsRawFd;
378
379    pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
380        unsafe {
381            let raw_fd = sock.as_raw_fd();
382
383            let mut uid = std::mem::MaybeUninit::uninit();
384            let mut gid = std::mem::MaybeUninit::uninit();
385
386            let ret = libc::getpeereid(raw_fd, uid.as_mut_ptr(), gid.as_mut_ptr());
387
388            if ret == 0 {
389                Ok(super::UCred {
390                    uid: uid.assume_init(),
391                    gid: gid.assume_init(),
392                    pid: None,
393                })
394            } else {
395                Err(io::Error::last_os_error())
396            }
397        }
398    }
399}
400
401#[cfg(any(
402    target_os = "espidf",
403    target_os = "nuttx",
404    target_os = "vita",
405    target_os = "hurd"
406))]
407pub(crate) mod impl_noproc {
408    use crate::net::unix::UnixStream;
409    use std::io;
410
411    pub(crate) fn get_peer_cred(_sock: &UnixStream) -> io::Result<super::UCred> {
412        Ok(super::UCred {
413            uid: 0,
414            gid: 0,
415            pid: None,
416        })
417    }
418}
419
420#[cfg(target_os = "nto")]
421pub(crate) mod impl_nto {
422    use crate::net::unix::{self, UnixStream};
423
424    use libc::getpeereid;
425    use std::io;
426    use std::mem::MaybeUninit;
427    use std::os::unix::io::AsRawFd;
428
429    pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
430        unsafe {
431            let raw_fd = sock.as_raw_fd();
432
433            let mut uid = MaybeUninit::uninit();
434            let mut gid = MaybeUninit::uninit();
435
436            let ret = getpeereid(raw_fd, uid.as_mut_ptr(), gid.as_mut_ptr());
437
438            if ret == 0 {
439                Ok(super::UCred {
440                    uid: uid.assume_init() as unix::uid_t,
441                    gid: gid.assume_init() as unix::gid_t,
442                    pid: None,
443                })
444            } else {
445                Err(io::Error::last_os_error())
446            }
447        }
448    }
449}