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