1#![allow(
2 clippy::match_ref_pats, clippy::needless_borrowed_reference,
4)]
5
6#[cfg(any(target_os="linux", target_os="android"))]
7use std::os::fd::AsFd;
8use std::{io, fmt};
9use std::num::NonZeroU32;
10use std::io::ErrorKind::*;
11#[cfg(any(
12 target_os="linux", target_os="android",
13 target_os="freebsd", target_os="dragonfly", target_vendor="apple",
14 target_os="openbsd", target_os="netbsd"
15))]
16use std::mem;
17#[cfg(any(target_os="illumos", target_os="solaris"))]
18use std::ptr;
19
20#[cfg(any(
21 target_os="linux", target_os="android",
22 target_os="freebsd", target_os="dragonfly", target_vendor="apple",
23 target_os="openbsd", target_os="netbsd"
24))]
25use libc::{getsockopt, c_void, socklen_t};
26#[cfg(any(target_os="linux", target_os="android"))]
27use libc::{pid_t, uid_t, gid_t, getpid, getuid, geteuid, getgid, getegid};
28#[cfg(any(target_os="linux", target_os="android"))]
29use libc::{ucred, SOL_SOCKET, SO_PEERCRED, SO_PEERSEC};
30#[cfg(any(target_os="freebsd", target_os="dragonfly", target_vendor="apple"))]
31use libc::{xucred, XUCRED_VERSION, LOCAL_PEERCRED};
32#[cfg(target_vendor="apple")]
33use libc::SOL_LOCAL; #[cfg(target_os="openbsd")]
35use libc::{sockpeercred, SOL_SOCKET, SO_PEERCRED};
36#[cfg(target_os="netbsd")]
37use libc::{unpcbid, LOCAL_PEEREID};
38#[cfg(any(target_os="illumos", target_os="solaris"))]
39use libc::{getpeerucred, ucred_free, ucred_t};
40#[cfg(any(target_os="illumos", target_os="solaris"))]
41use libc::{ucred_geteuid, ucred_getegid, ucred_getpid, ucred_getgroups, uid_t, gid_t, pid_t};
42
43#[derive(Clone,Copy, PartialEq,Eq, Debug)]
48#[allow(unused)] pub enum SendCredentials
50{
51 Effective,
52 Real,
53 Custom{ pid: u32, uid: u32, gid: u32 }
54}
55
56#[cfg(any(target_os="linux", target_os="android"))]
57impl SendCredentials
58{
59 pub fn into_raw(self) -> ucred
60 {
61 let mut ucred: ucred = unsafe { mem::zeroed() };
62 let (pid, uid, gid) = match self {
63 SendCredentials::Effective => unsafe { (getpid(), geteuid(), getegid()) },
64 SendCredentials::Real => unsafe { (getpid(), getuid(), getgid()) },
65 SendCredentials::Custom{pid, uid, gid} => (pid as pid_t, uid as uid_t, gid as gid_t),
66 };
67
68 ucred.pid = pid;
69 ucred.uid = uid;
70 ucred.gid = gid;
71
72 return ucred;
73 }
74}
75
76
77
78#[cfg(any(target_os="linux", target_os="android"))]
79pub
80fn selinux_context<FD: AsFd>(fd: FD, buffer: &mut[u8]) -> Result<usize, io::Error>
81{
82 let ptr = buffer.as_mut_ptr() as *mut c_void;
83 let mut capacity = buffer.len().min(socklen_t::max_value() as usize) as socklen_t;
84
85 let res =
86 unsafe
87 {
88 use std::os::fd::AsRawFd;
89
90 getsockopt(fd.as_fd().as_raw_fd(), SOL_SOCKET, SO_PEERSEC, ptr, &mut capacity)
91 };
92
93 return
94 match res
95 {
96 -1 => Err(io::Error::last_os_error()),
97 _ => Ok(capacity as usize),
98 };
99}
100
101#[cfg(not(any(target_os="linux", target_os="android")))]
102pub fn selinux_context(_fd: RawFd, _buffer: &mut[u8]) -> Result<usize, io::Error> {
103 Err(io::Error::new(Other, "not available"))
104}
105
106
107
108#[derive(Clone,Copy, PartialEq,Eq)]
132pub enum ConnCredentials {
133 LinuxLike{ pid: NonZeroU32, euid: u32, egid: u32 },
134 MacOsLike{ euid: u32, number_of_groups: u8, groups: [u32; 16] },
135}
136impl ConnCredentials {
137 pub fn pid(&self) -> Option<NonZeroU32> {
143 match self {
144 &ConnCredentials::LinuxLike{ pid, .. } => Some(pid),
145 &ConnCredentials::MacOsLike{ .. } => None,
146 }
147 }
148 pub fn euid(&self) -> u32 {
152 match self {
153 &ConnCredentials::LinuxLike{ euid, .. } => euid,
154 &ConnCredentials::MacOsLike{ euid, .. } => euid,
155 }
156 }
157 pub fn egid(&self) -> Option<u32> {
174 match self {
175 &ConnCredentials::LinuxLike{ egid, .. } => Some(egid),
176 &ConnCredentials::MacOsLike{ number_of_groups: 1..=255, groups, .. } => Some(groups[0]),
177 &ConnCredentials::MacOsLike{ number_of_groups: 0, .. } => None,
178 }
179 }
180 pub fn groups(&self) -> &[u32] {
185 match self {
186 &ConnCredentials::LinuxLike{ .. } => &[],
187 &ConnCredentials::MacOsLike{ number_of_groups: n @ 0..=15, ref groups, .. } => {
188 &groups[..(n as usize)]
189 },
190 &ConnCredentials::MacOsLike{ number_of_groups: 16..=255, ref groups, .. } => groups,
191 }
192 }
193}
194impl fmt::Debug for ConnCredentials {
195 fn fmt(&self, fmtr: &mut fmt::Formatter) -> Result<(), fmt::Error> {
196 let mut repr = fmtr.debug_struct("ConnCredentials");
197 match self {
198 &ConnCredentials::LinuxLike{ ref pid, ref euid, ref egid } => {
199 repr.field("pid", pid);
200 repr.field("euid", euid);
201 repr.field("egid", egid);
202 }
203 &ConnCredentials::MacOsLike{ ref euid, number_of_groups, ref groups } => {
204 repr.field("euid", euid);
205 let number_of_groups = (number_of_groups as usize).min(groups.len());
206 repr.field("groups", &&groups[..number_of_groups]);
207 }
208 }
209 repr.finish()
210 }
211}
212
213
214#[cfg(any(target_os="linux", target_os="android"))]
215pub
216fn peer_credentials<FD: AsFd>(conn: FD) -> Result<ConnCredentials, io::Error>
217{
218 use std::os::fd::AsRawFd;
219
220 let mut ucred: ucred = unsafe { mem::zeroed() };
221
222 let ptr = &mut ucred as *mut ucred as *mut c_void;
223 let mut size = mem::size_of::<ucred>() as socklen_t;
224
225 let res =
226 unsafe
227 {
228 getsockopt(conn.as_fd().as_raw_fd(), SOL_SOCKET, SO_PEERCRED, ptr, &mut size)
229 };
230
231 return
232 if res == -1
233 {
234 Err(io::Error::last_os_error())
235 }
236 else if let Some(pid) = NonZeroU32::new(ucred.pid as u32)
237 {
238 Ok(ConnCredentials::LinuxLike{ pid, euid: ucred.uid as u32, egid: ucred.gid as u32 })
239 }
240 else
241 {
242 Err(io::Error::new(NotConnected, "socket is not a connection"))
243 };
244
245}
246
247#[cfg(any(target_os="freebsd", target_os="dragonfly", target_vendor="apple"))]
248pub fn peer_credentials(conn: RawFd) -> Result<ConnCredentials, io::Error> {
249 let mut xucred: xucred = unsafe { mem::zeroed() };
250 xucred.cr_version = XUCRED_VERSION;
251 xucred.cr_ngroups = xucred.cr_groups.len() as _;
252 xucred.cr_uid = !0;
254 for group_slot in &mut xucred.cr_groups {
255 *group_slot = !0;
256 }
257 #[cfg(any(target_os="freebsd", target_os="dragonfly"))]
258 const PEERCRED_SOCKET_LEVEL: i32 = 0; #[cfg(target_vendor="apple")]
260 use SOL_LOCAL as PEERCRED_SOCKET_LEVEL;
261 unsafe {
262 let ptr = &mut xucred as *mut xucred as *mut c_void;
263 let mut size = mem::size_of::<xucred>() as socklen_t;
264 match getsockopt(conn, PEERCRED_SOCKET_LEVEL, LOCAL_PEERCRED, ptr, &mut size) {
265 -1 => Err(io::Error::last_os_error()),
266 _ if xucred.cr_version != XUCRED_VERSION => {
267 Err(io::Error::new(InvalidData, "unknown version of peer credentials"))
268 },
269 _ => {
270 let mut groups = [u32::max_value(); 16]; let filled_groups = xucred.cr_groups.iter().take(xucred.cr_ngroups as usize);
272 for (&src, dst) in filled_groups.zip(&mut groups) {
273 *dst = src.into();
274 }
275 Ok(ConnCredentials::MacOsLike {
276 euid: xucred.cr_uid.into(),
277 number_of_groups: xucred.cr_ngroups as u8,
278 groups: groups,
279 })
280 }
281 }
282 }
283}
284
285#[cfg(target_os="openbsd")]
286pub fn peer_credentials(conn: RawFd) -> Result<ConnCredentials, io::Error> {
287 let mut sockpeercred: sockpeercred = unsafe { mem::zeroed() };
288 unsafe {
289 let ptr = &mut sockpeercred as *mut sockpeercred as *mut c_void;
290 let mut size = mem::size_of::<sockpeercred>() as socklen_t;
291 if getsockopt(conn, SOL_SOCKET, SO_PEERCRED, ptr, &mut size) == -1 {
292 Err(io::Error::last_os_error())
293 } else if let Some(pid) = NonZeroU32::new(sockpeercred.pid as u32) {
294 Ok(ConnCredentials::LinuxLike {
295 pid,
296 euid: sockpeercred.uid as u32,
297 egid: sockpeercred.gid as u32,
298 })
299 } else {
300 Err(io::Error::new(InvalidData, "the returned pid is zero"))
301 }
302 }
303}
304
305#[cfg(target_os="netbsd")]
306pub fn peer_credentials(conn: RawFd) -> Result<ConnCredentials, io::Error> {
307 let mut unpcbid: unpcbid = unsafe { mem::zeroed() };
308 unsafe {
309 let ptr = &mut unpcbid as *mut unpcbid as *mut c_void;
310 let mut size = mem::size_of::<unpcbid>() as socklen_t;
311 if getsockopt(conn, 0, LOCAL_PEEREID, ptr, &mut size) == -1 {
313 Err(io::Error::last_os_error())
314 } else if let Some(pid) = NonZeroU32::new(unpcbid.unp_pid as u32) {
315 Ok(ConnCredentials::LinuxLike {
316 pid,
317 euid: unpcbid.unp_euid as u32,
318 egid: unpcbid.unp_egid as u32,
319 })
320 } else {
321 Err(io::Error::new(InvalidData, "the returned pid is zero"))
322 }
323 }
324}
325
326#[cfg(any(target_os="illumos", target_os="solaris"))]
327pub fn peer_credentials(conn: RawFd) -> Result<ConnCredentials, io::Error> {
328 struct UcredAlloc(*mut ucred_t);
329 impl Drop for UcredAlloc {
330 fn drop(&mut self) {
331 unsafe {
332 if self.0 != ptr::null_mut() {
333 ucred_free(self.0);
334 }
335 }
336 }
337 }
338 unsafe {
339 let mut ucred = UcredAlloc(ptr::null_mut());
340 if getpeerucred(conn, &mut ucred.0) == -1 {
341 Err(io::Error::last_os_error())
342 } else if ucred.0 == ptr::null_mut() {
343 Err(io::Error::new(NotConnected, "socket is not a connection"))
344 } else {
345 let euid = ucred_geteuid(ucred.0 as *const _);
346 let egid = ucred_getegid(ucred.0 as *const _);
347 let pid = ucred_getpid(ucred.0 as *const _);
348 let mut groups_ptr: *const gid_t = ptr::null_mut();
349 let ngroups = ucred_getgroups(ucred.0 as *const _, &mut groups_ptr);
350 if euid != -1i32 as uid_t && egid != -1i32 as gid_t
353 && pid != -1i32 as pid_t && pid != 0 {
354 Ok(ConnCredentials::LinuxLike {
355 pid: NonZeroU32::new(pid as u32).unwrap(), euid: euid as u32,
357 egid: egid as u32,
358 })
359 } else if euid != -1i32 as uid_t && ngroups > 0 && groups_ptr != ptr::null() {
360 let mut groups = [u32::max_value(); 16];
361 let number_of_groups = ngroups.min(16) as u8;
362 for i in 0..number_of_groups {
363 groups[i as usize] = *groups_ptr.offset(i as isize);
364 }
365 Ok(ConnCredentials::MacOsLike {
366 euid: euid as u32,
367 number_of_groups,
368 groups,
369 })
370 } else if euid != -1i32 as uid_t && egid != -1i32 as gid_t {
371 let mut groups = [u32::max_value(); 16];
372 groups[0] = egid as u32;
373 Ok(ConnCredentials::MacOsLike {
374 euid: euid as u32,
375 number_of_groups: 1,
376 groups,
377 })
378 } else {
379 Err(io::Error::new(Other, "Not enough information was available"))
380 }
381 }
382 }
383}
384
385#[cfg(not(any(
386 target_os="linux", target_os="android",
387 target_os="freebsd", target_os="dragonfly", target_vendor="apple",
388 target_os="openbsd", target_os="netbsd",
389 target_os="illumos", target_os="solaris",
390)))]
391pub fn peer_credentials(_: RawFd) -> Result<ConnCredentials, io::Error> {
392 Err(io::Error::new(Other, "Not available"))
393}
394
395
396
397#[cfg(any(target_os="linux", target_os="android"))]
398pub type RawReceivedCredentials = libc::ucred;
399
400
401#[derive(Clone,Copy, PartialEq,Eq,Hash, Debug)]
414pub struct ReceivedCredentials
415{
416 #[cfg(any(target_os="linux", target_os="android", target_os="dragonfly"))]
417 pid: u32,
418 #[cfg(any(target_os="linux", target_os="android"))]
419 uid: u32,
420 #[cfg(any(target_os="linux", target_os="android"))]
421 gid: u32,
422
423 #[cfg(any(
424 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
425 target_os="illumos", target_os="solaris", target_os="macos",
426 ))]
427 real_uid: u32,
428 #[cfg(any(
429 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
430 target_os="illumos", target_os="solaris", target_os="macos",
431 ))]
432 effective_uid: u32,
433 #[cfg(any(
434 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
435 target_os="illumos", target_os="solaris", target_os="macos",
436 ))]
437 real_gid: u32,
438 #[cfg(any(
439 target_os="freebsd", target_os="netbsd",
440 target_os="illumos", target_os="solaris", target_os="macos",
441 ))]
442 effective_gid: u32,
443 #[cfg(any(
444 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
445 target_os="illumos", target_os="solaris", target_os="macos",
446 ))]
447 groups: [u32; 5],
448}
449
450#[allow(unused)] impl ReceivedCredentials
452{
453 #[cfg(any(target_os="linux", target_os="android"))]
454 pub(crate) fn from_raw(creds: libc::ucred) -> Self
455 {
456 ReceivedCredentials
457 {
458 pid: creds.pid as u32,
459 uid: creds.uid as u32,
460 gid: creds.gid as u32,
461 }
462 }
463
464 pub
468 fn pid(&self) -> Option<u32>
469 {
470 #[cfg(any(target_os="linux", target_os="android", target_os="dragonfly"))] {
471 Some(self.pid)
472 }
473 #[cfg(not(any(target_os="linux", target_os="android", target_os="dragonfly")))] {
474 None
475 }
476 }
477
478 pub
479 fn effective_or_sent_uid(&self) -> u32
480 {
481 #[cfg(any(target_os="linux", target_os="android"))] {
482 self.uid
483 }
484 #[cfg(any(
485 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
486 target_os="illumos", target_os="solaris", target_os="macos",
487 ))] {
488 self.effective_uid
489 }
490 #[cfg(not(any(
491 target_os="linux", target_os="android",
492 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
493 target_os="illumos", target_os="solaris", target_os="macos",
494 )))] {
495 unreachable!("struct cannot be created on unsupported OSes")
496 }
497 }
498
499 pub
500 fn real_or_sent_uid(&self) -> u32
501 {
502 #[cfg(any(target_os="linux", target_os="android"))]
503 {
504 self.uid
505 }
506 #[cfg(any(
507 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
508 target_os="illumos", target_os="solaris", target_os="macos",
509 ))]
510 {
511 self.real_uid
512 }
513 #[cfg(not(any(
514 target_os="linux", target_os="android",
515 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
516 target_os="illumos", target_os="solaris", target_os="macos",
517 )))]
518 {
519 unreachable!("struct cannot be created on unsupported OSes")
520 }
521 }
522 pub fn effective_or_sent_gid(&self) -> Option<u32> {
523 #[cfg(any(target_os="linux", target_os="android"))] {
524 Some(self.gid)
525 }
526 #[cfg(any(
527 target_os="freebsd", target_os="netbsd",
528 target_os="illumos", target_os="solaris", target_os="macos",
529 ))] {
530 Some(self.effective_gid)
531 }
532 #[cfg(not(any(
533 target_os="linux", target_os="android",
534 target_os="freebsd", target_os="netbsd",
535 target_os="illumos", target_os="solaris", target_os="macos",
536 )))] {
537 None
538 }
539 }
540
541 pub fn real_or_sent_gid(&self) -> u32
542 {
543 #[cfg(any(target_os="linux", target_os="android"))]
544 {
545 self.gid
546 }
547 #[cfg(any(
548 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
549 target_os="illumos", target_os="solaris", target_os="macos",
550 ))]
551 {
552 self.real_gid
553 }
554 #[cfg(not(any(
555 target_os="linux", target_os="android",
556 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
557 target_os="illumos", target_os="solaris", target_os="macos",
558 )))]
559 {
560 unreachable!("struct cannot be created on unsupported OSes")
561 }
562 }
563 pub
568 fn groups(&self) -> &[u32]
569 {
570 #[cfg(any(
571 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
572 target_os="illumos", target_os="solaris", target_os="macos",
573 ))]
574 {
575 &self.groups[..]
576 }
577 #[cfg(not(any(
578 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
579 target_os="illumos", target_os="solaris", target_os="macos",
580 )))]
581 {
582 &[]
583 }
584 }
585}