1use libc::{gid_t, pid_t, uid_t};
2use std::os::unix::io::AsRawFd;
3
4#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
6pub struct UCred {
7 uid: uid_t,
9 gid: gid_t,
11 pid: pid_t,
13}
14
15impl UCred {
16 pub fn from_socket_peer<T: AsRawFd>(socket: &T) -> std::io::Result<Self> {
18 get_peer_cred(socket)
19 }
20
21 pub fn uid(&self) -> uid_t {
23 self.uid
24 }
25
26 pub fn gid(&self) -> gid_t {
28 self.gid
29 }
30
31 pub fn pid(&self) -> Option<pid_t> {
36 if self.pid == 0 {
37 None
38 } else {
39 Some(self.pid)
40 }
41 }
42
43 #[cfg(any(target_os = "linux", target_os = "android"))]
44 pub(crate) fn from_scm_creds(raw: crate::ancillary::RawScmCreds) -> UCred {
45 UCred {
46 uid: raw.uid,
47 gid: raw.gid,
48 pid: raw.pid,
49 }
50 }
51
52 #[cfg(target_os = "netbsd")]
53 pub(crate) fn from_scm_creds(raw: crate::ancillary::RawScmCreds) -> UCred {
54 UCred {
55 uid: raw.sc_uid,
56 gid: raw.sc_gid,
57 pid: raw.sc_pid,
58 }
59 }
60
61 #[cfg(any(target_os = "linux", target_os = "android"))]
62 pub(crate) fn to_scm_creds(self) -> crate::ancillary::RawScmCreds {
63 crate::ancillary::RawScmCreds {
64 uid: self.uid,
65 gid: self.gid,
66 pid: self.pid,
67 }
68 }
69
70 #[cfg(target_os = "netbsd")]
71 pub(crate) fn to_scm_creds(self) -> crate::ancillary::RawScmCreds {
72 crate::ancillary::RawScmCreds {
73 sc_uid: self.uid,
74 sc_gid: self.gid,
75 sc_pid: self.pid,
76 sc_euid: self.uid,
77 sc_egid: self.gid,
78 sc_ngroups: 0,
79 sc_groups: [],
80 }
81 }
82}
83
84#[cfg(any(target_os = "linux", target_os = "android"))]
85fn get_peer_cred<T: AsRawFd>(sock: &T) -> std::io::Result<UCred> {
86 use libc::{c_void, getsockopt, socklen_t, ucred, SOL_SOCKET, SO_PEERCRED};
87
88 let raw_fd = sock.as_raw_fd();
89
90 let mut ucred = ucred { pid: 0, uid: 0, gid: 0 };
91
92 let mut ucred_size = std::mem::size_of::<ucred>() as socklen_t;
93
94 let ret = unsafe {
95 getsockopt(
96 raw_fd,
97 SOL_SOCKET,
98 SO_PEERCRED,
99 &mut ucred as *mut ucred as *mut c_void,
100 &mut ucred_size,
101 )
102 };
103
104 if ret == 0 {
105 Ok(UCred {
106 uid: ucred.uid,
107 gid: ucred.gid,
108 pid: ucred.pid,
109 })
110 } else {
111 Err(std::io::Error::last_os_error())
112 }
113}
114
115#[cfg(any(
116 target_os = "dragonfly",
117 target_os = "freebsd",
118 target_os = "netbsd",
119 target_os = "openbsd"
120))]
121fn get_peer_cred<T: AsRawFd>(sock: &T) -> std::io::Result<UCred> {
122 let raw_fd = sock.as_raw_fd();
123
124 let mut uid = 0;
125 let mut gid = 0;
126
127 let ret = unsafe { libc::getpeereid(raw_fd, &mut uid, &mut gid) };
128
129 if ret == 0 {
130 Ok(UCred { uid, gid, pid: 0 })
131 } else {
132 Err(std::io::Error::last_os_error())
133 }
134}
135
136#[cfg(any(target_os = "macos", target_os = "ios"))]
137fn get_peer_cred<T: AsRawFd>(sock: &T) -> std::io::Result<UCred> {
138 let raw_fd = sock.as_raw_fd();
139
140 let mut uid = 0 as uid_t;
141 let mut gid = 0 as gid_t;
142 let mut pid = 0 as pid_t;
143 let mut pid_size = size_of::<pid_t>() as u32;
144
145 let ret = unsafe {
146 getsockopt(
147 raw_fd,
148 SOL_LOCAL,
149 LOCAL_PEEREPID,
150 pid.as_mut_ptr() as *mut c_void,
151 pid_size.as_mut_ptr(),
152 )
153 };
154 if ret != 0 {
155 return Err(std::io::Error::last_os_error());
156 }
157
158 let ret = unsafe { getpeereid(raw_fd, uid.as_mut_ptr(), gid.as_mut_ptr()) };
159
160 if ret == 0 {
161 Ok(UCred {
162 uid,
163 gid,
164 pid,
165 })
166 } else {
167 Err(std::io::Error::last_os_error())
168 }
169}
170
171#[cfg(any(target_os = "solaris", target_os = "illumos"))]
172fn get_peer_cred<T: AsRawFd>(sock: &T) -> std::io::Result<UCred> {
173 let raw_fd = sock.as_raw_fd();
174 let mut cred = std::ptr::null_mut();
175 unsafe {
176 let ret = libc::getpeerucred(raw_fd, &mut cred);
177
178 if ret == 0 {
179 let uid = libc::ucred_geteuid(cred);
180 let gid = libc::ucred_getegid(cred);
181 let pid = libc::ucred_getpid(cred);
182
183 libc::ucred_free(cred);
184
185 Ok(UCred {
186 uid,
187 gid,
188 pid,
189 })
190 } else {
191 Err(std::io::Error::last_os_error())
192 }
193 }
194}