1//! Unix peer credentials.
2
3// NOTE: Code in this file is heavily based on work done in PR 13 from the tokio-uds repository on
4// GitHub.
5//
6// For reference, the link is here: https://github.com/tokio-rs/tokio-uds/pull/13
7// Credit to Martin Habovštiak (GitHub username Kixunil) and contributors for this work.
8
9use libc::{gid_t, pid_t, uid_t};
10
11/// Credentials for a UNIX process for credentials passing.
12#[unstable(feature = "peer_credentials_unix_socket", issue = "42839", reason = "unstable")]
13#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
14pub struct UCred {
15 /// The UID part of the peer credential. This is the effective UID of the process at the domain
16 /// socket's endpoint.
17 pub uid: uid_t,
18 /// The GID part of the peer credential. This is the effective GID of the process at the domain
19 /// socket's endpoint.
20 pub gid: gid_t,
21 /// The PID part of the peer credential. This field is optional because the PID part of the
22 /// peer credentials is not supported on every platform. On platforms where the mechanism to
23 /// discover the PID exists, this field will be populated to the PID of the process at the
24 /// domain socket's endpoint. Otherwise, it will be set to None.
25 pub pid: Option<pid_t>,
26}
27
28#[cfg(any(target_os = "android", target_os = "linux"))]
29pub use self::impl_linux::peer_cred;
30
31#[cfg(any(
32 target_os = "dragonfly",
33 target_os = "freebsd",
34 target_os = "openbsd",
35 target_os = "netbsd"
36))]
37pub use self::impl_bsd::peer_cred;
38
39#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
40pub use self::impl_mac::peer_cred;
41
42#[cfg(any(target_os = "linux", target_os = "android"))]
43pub mod impl_linux {
44 use super::UCred;
45 use crate::os::unix::io::AsRawFd;
46 use crate::os::unix::net::UnixStream;
47 use crate::{io, mem};
48 use libc::{c_void, getsockopt, socklen_t, ucred, SOL_SOCKET, SO_PEERCRED};
49
50 pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
51 let ucred_size = mem::size_of::<ucred>();
52
53 // Trivial sanity checks.
54 assert!(mem::size_of::<u32>() <= mem::size_of::<usize>());
55 assert!(ucred_size <= u32::MAX as usize);
56
57 let mut ucred_size = ucred_size as socklen_t;
58 let mut ucred: ucred = ucred { pid: 1, uid: 1, gid: 1 };
59
60 unsafe {
61 let ret = getsockopt(
62 socket.as_raw_fd(),
63 SOL_SOCKET,
64 SO_PEERCRED,
65 &mut ucred as *mut ucred as *mut c_void,
66 &mut ucred_size,
67 );
68
69 if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
70 Ok(UCred { uid: ucred.uid, gid: ucred.gid, pid: Some(ucred.pid) })
71 } else {
72 Err(io::Error::last_os_error())
73 }
74 }
75 }
76}
77
78#[cfg(any(
79 target_os = "dragonfly",
80 target_os = "freebsd",
81 target_os = "openbsd",
82 target_os = "netbsd",
83 target_os = "nto",
84))]
85pub mod impl_bsd {
86 use super::UCred;
87 use crate::io;
88 use crate::os::unix::io::AsRawFd;
89 use crate::os::unix::net::UnixStream;
90
91 pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
92 let mut cred = UCred { uid: 1, gid: 1, pid: None };
93 unsafe {
94 let ret = libc::getpeereid(socket.as_raw_fd(), &mut cred.uid, &mut cred.gid);
95
96 if ret == 0 { Ok(cred) } else { Err(io::Error::last_os_error()) }
97 }
98 }
99}
100
101#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
102pub mod impl_mac {
103 use super::UCred;
104 use crate::os::unix::io::AsRawFd;
105 use crate::os::unix::net::UnixStream;
106 use crate::{io, mem};
107 use libc::{c_void, getpeereid, getsockopt, pid_t, socklen_t, LOCAL_PEERPID, SOL_LOCAL};
108
109 pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> {
110 let mut cred = UCred { uid: 1, gid: 1, pid: None };
111 unsafe {
112 let ret = getpeereid(socket.as_raw_fd(), &mut cred.uid, &mut cred.gid);
113
114 if ret != 0 {
115 return Err(io::Error::last_os_error());
116 }
117
118 let mut pid: pid_t = 1;
119 let mut pid_size = mem::size_of::<pid_t>() as socklen_t;
120
121 let ret = getsockopt(
122 socket.as_raw_fd(),
123 SOL_LOCAL,
124 LOCAL_PEERPID,
125 &mut pid as *mut pid_t as *mut c_void,
126 &mut pid_size,
127 );
128
129 if ret == 0 && pid_size as usize == mem::size_of::<pid_t>() {
130 cred.pid = Some(pid);
131 Ok(cred)
132 } else {
133 Err(io::Error::last_os_error())
134 }
135 }
136 }
137}
138