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