1#![allow(
2 clippy::match_ref_pats, clippy::needless_borrowed_reference,
4)]
5
6use std::os::fd::AsFd;
7use std::{io, fmt};
8use std::num::NonZeroU32;
9use std::io::ErrorKind::*;
10#[cfg(any(
11 target_os="linux", target_os="android",
12 target_os="freebsd", target_os="dragonfly", target_vendor="apple",
13 target_os="openbsd", target_os="netbsd"
14))]
15use std::mem;
16#[cfg(any(target_os="illumos", target_os="solaris"))]
17use std::ptr;
18
19#[cfg(any(
20 target_os="linux", target_os="android",
21 target_os="freebsd", target_os="dragonfly", target_vendor="apple",
22 target_os="openbsd", target_os="netbsd"
23))]
24use libc::{getsockopt, c_void, socklen_t};
25#[cfg(any(target_os="linux", target_os="android"))]
26use libc::{pid_t, uid_t, gid_t, getpid, getuid, geteuid, getgid, getegid};
27#[cfg(any(target_os="linux", target_os="android"))]
28use libc::{ucred, SOL_SOCKET, SO_PEERCRED, SO_PEERSEC};
29#[cfg(any(target_os="freebsd", target_os="dragonfly", target_vendor="apple"))]
30use libc::{xucred, XUCRED_VERSION, LOCAL_PEERCRED};
31#[cfg(target_vendor="apple")]
32use libc::SOL_LOCAL; #[cfg(target_os="openbsd")]
34use libc::{sockpeercred, SOL_SOCKET, SO_PEERCRED};
35#[cfg(target_os="netbsd")]
36use libc::{unpcbid, LOCAL_PEEREID};
37#[cfg(any(target_os="illumos", target_os="solaris"))]
38use libc::{getpeerucred, ucred_free, ucred_t};
39#[cfg(any(target_os="illumos", target_os="solaris"))]
40use libc::{ucred_geteuid, ucred_getegid, ucred_getpid, ucred_getgroups, uid_t, gid_t, pid_t};
41
42#[derive(Clone,Copy, PartialEq,Eq, Debug)]
47#[allow(unused)] pub enum SendCredentials
49{
50 Effective,
51 Real,
52 Custom{ pid: u32, uid: u32, gid: u32 }
53}
54
55#[cfg(any(target_os="linux", target_os="android"))]
56impl SendCredentials
57{
58 pub
59 fn into_raw(self) -> ucred
60 {
61 let mut ucred: ucred = unsafe { mem::zeroed() };
62
63 let (pid, uid, gid) =
64 match self
65 {
66 SendCredentials::Effective =>
67 unsafe { (getpid(), geteuid(), getegid()) },
68 SendCredentials::Real =>
69 unsafe { (getpid(), getuid(), getgid()) },
70 SendCredentials::Custom{pid, uid, gid} =>
71 (pid as pid_t, uid as uid_t, gid as gid_t),
72 };
73
74 ucred.pid = pid;
75 ucred.uid = uid;
76 ucred.gid = gid;
77
78 return ucred;
79 }
80}
81
82
83
84#[cfg(any(target_os="linux", target_os="android"))]
85pub
86fn selinux_context<FD: AsFd>(fd: FD, buffer: &mut[u8]) -> Result<usize, io::Error>
87{
88 let ptr = buffer.as_mut_ptr() as *mut c_void;
89 let mut capacity = buffer.len().min(socklen_t::max_value() as usize) as socklen_t;
90
91 let res =
92 unsafe
93 {
94 use std::os::fd::AsRawFd;
95
96 getsockopt(fd.as_fd().as_raw_fd(), SOL_SOCKET, SO_PEERSEC, ptr, &mut capacity)
97 };
98
99 return
100 match res
101 {
102 -1 => Err(io::Error::last_os_error()),
103 _ => Ok(capacity as usize),
104 };
105}
106
107#[cfg(not(any(target_os="linux", target_os="android")))]
108pub
109fn selinux_context<FD: AsFd>(_fd: FD, _buffer: &mut[u8]) -> Result<usize, io::Error>
110{
111 Err(io::Error::new(Other, "not available"))
112}
113
114
115
116#[derive(Clone,Copy, PartialEq,Eq)]
140pub enum ConnCredentials
141{
142 LinuxLike{ pid: NonZeroU32, euid: u32, egid: u32 },
143 MacOsLike{ euid: u32, number_of_groups: u8, groups: [u32; 16] },
144}
145
146impl ConnCredentials
147{
148 pub
154 fn pid(&self) -> Option<NonZeroU32>
155 {
156 match self
157 {
158 &ConnCredentials::LinuxLike{ pid, .. } => Some(pid),
159 &ConnCredentials::MacOsLike{ .. } => None,
160 }
161 }
162 pub
166 fn euid(&self) -> u32
167 {
168 match self
169 {
170 &ConnCredentials::LinuxLike{ euid, .. } => euid,
171 &ConnCredentials::MacOsLike{ euid, .. } => euid,
172 }
173 }
174 pub
191 fn egid(&self) -> Option<u32>
192 {
193 return
194 match self
195 {
196 &ConnCredentials::LinuxLike{ egid, .. } =>
197 Some(egid),
198 &ConnCredentials::MacOsLike{ number_of_groups: 1..=255, groups, .. } =>
199 Some(groups[0]),
200 &ConnCredentials::MacOsLike{ number_of_groups: 0, .. } =>
201 None,
202 };
203 }
204
205 pub
210 fn groups(&self) -> &[u32]
211 {
212 return
213 match self
214 {
215 &ConnCredentials::LinuxLike{ .. } =>
216 &[],
217 &ConnCredentials::MacOsLike{ number_of_groups: n @ 0..=15, ref groups, .. } =>
218 &groups[..(n as usize)],
219 &ConnCredentials::MacOsLike{ number_of_groups: 16..=255, ref groups, .. } =>
220 groups,
221 };
222 }
223}
224impl fmt::Debug for ConnCredentials
225{
226 fn fmt(&self, fmtr: &mut fmt::Formatter) -> Result<(), fmt::Error>
227 {
228 let mut repr = fmtr.debug_struct("ConnCredentials");
229
230 match self
231 {
232 &ConnCredentials::LinuxLike{ ref pid, ref euid, ref egid } =>
233 {
234 repr.field("pid", pid);
235 repr.field("euid", euid);
236 repr.field("egid", egid);
237 }
238 &ConnCredentials::MacOsLike{ ref euid, number_of_groups, ref groups } =>
239 {
240 repr.field("euid", euid);
241 let number_of_groups = (number_of_groups as usize).min(groups.len());
242 repr.field("groups", &&groups[..number_of_groups]);
243 }
244 }
245
246 return repr.finish();
247 }
248}
249
250
251#[cfg(any(target_os="linux", target_os="android"))]
252pub
253fn peer_credentials<FD: AsFd>(conn: FD) -> Result<ConnCredentials, io::Error>
254{
255 use std::os::fd::AsRawFd;
256
257 let mut ucred: ucred = unsafe { mem::zeroed() };
258
259 let ptr = &mut ucred as *mut ucred as *mut c_void;
260 let mut size = mem::size_of::<ucred>() as socklen_t;
261
262 let res =
263 unsafe
264 {
265 getsockopt(conn.as_fd().as_raw_fd(), SOL_SOCKET, SO_PEERCRED, ptr, &mut size)
266 };
267
268 return
269 if res == -1
270 {
271 Err(io::Error::last_os_error())
272 }
273 else if let Some(pid) = NonZeroU32::new(ucred.pid as u32)
274 {
275 Ok(ConnCredentials::LinuxLike{ pid, euid: ucred.uid as u32, egid: ucred.gid as u32 })
276 }
277 else
278 {
279 Err(io::Error::new(NotConnected, "socket is not a connection"))
280 };
281
282}
283
284#[cfg(any(target_os="freebsd", target_os="dragonfly", target_vendor="apple"))]
285pub
286fn peer_credentials<FD: AsFd>(conn: FD) -> Result<ConnCredentials, io::Error>
287{
288 let mut xucred: xucred = unsafe { mem::zeroed() };
289 xucred.cr_version = XUCRED_VERSION;
290 xucred.cr_ngroups = xucred.cr_groups.len() as _;
291
292 xucred.cr_uid = !0;
294 for group_slot in &mut xucred.cr_groups
295 {
296 *group_slot = !0;
297 }
298
299 #[cfg(any(target_os="freebsd", target_os="dragonfly"))]
300 const PEERCRED_SOCKET_LEVEL: i32 = 0; #[cfg(target_vendor="apple")]
303 use SOL_LOCAL as PEERCRED_SOCKET_LEVEL;
304
305 unsafe
306 {
307 use std::os::fd::AsRawFd;
308
309 let ptr = &mut xucred as *mut xucred as *mut c_void;
310 let mut size = mem::size_of::<xucred>() as socklen_t;
311
312 match getsockopt(conn.as_fd().as_raw_fd(), PEERCRED_SOCKET_LEVEL, LOCAL_PEERCRED, ptr, &mut size)
313 {
314 -1 =>
315 Err(io::Error::last_os_error()),
316 _ if xucred.cr_version != XUCRED_VERSION =>
317 {
318 Err(io::Error::new(InvalidData, "unknown version of peer credentials"))
319 },
320 _ =>
321 {
322 let mut groups = [u32::max_value(); 16]; let filled_groups = xucred.cr_groups.iter().take(xucred.cr_ngroups as usize);
324 for (&src, dst) in filled_groups.zip(&mut groups)
325 {
326 *dst = src.into();
327 }
328
329 return Ok(
330 ConnCredentials::MacOsLike
331 {
332 euid: xucred.cr_uid.into(),
333 number_of_groups: xucred.cr_ngroups as u8,
334 groups: groups,
335 }
336 )
337 }
338 }
339 }
340}
341
342#[cfg(target_os="openbsd")]
343pub fn peer_credentials<FD: AsFd>(conn: FD) -> Result<ConnCredentials, io::Error>
344{
345 let mut sockpeercred: sockpeercred = unsafe { mem::zeroed() };
346 unsafe
347 {
348 let ptr = &mut sockpeercred as *mut sockpeercred as *mut c_void;
349 let mut size = mem::size_of::<sockpeercred>() as socklen_t;
350
351 if getsockopt(conn.as_fd().as_raw_fd(), SOL_SOCKET, SO_PEERCRED, ptr, &mut size) == -1
352 {
353 return Err(io::Error::last_os_error());
354 }
355 else if let Some(pid) = NonZeroU32::new(sockpeercred.pid as u32)
356 {
357 return
358 Ok(
359 ConnCredentials::LinuxLike
360 {
361 pid,
362 euid: sockpeercred.uid as u32,
363 egid: sockpeercred.gid as u32,
364 }
365 );
366 }
367 else
368 {
369 return Err(io::Error::new(InvalidData, "the returned pid is zero"));
370 }
371 }
372}
373
374#[cfg(target_os="netbsd")]
375pub fn peer_credentials<FD: AsFd>(conn: FD) -> Result<ConnCredentials, io::Error>
376{
377 let mut unpcbid: unpcbid = unsafe { mem::zeroed() };
378 unsafe
379 {
380 let ptr = &mut unpcbid as *mut unpcbid as *mut c_void;
381 let mut size = mem::size_of::<unpcbid>() as socklen_t;
382
383 if getsockopt(conn.as_fd().as_raw_fd(), 0, LOCAL_PEEREID, ptr, &mut size) == -1
385 {
386 return Err(io::Error::last_os_error());
387 }
388 else if let Some(pid) = NonZeroU32::new(unpcbid.unp_pid as u32)
389 {
390 return Ok(
391 ConnCredentials::LinuxLike
392 {
393 pid,
394 euid: unpcbid.unp_euid as u32,
395 egid: unpcbid.unp_egid as u32,
396 }
397 );
398 }
399 else
400 {
401 return Err(io::Error::new(InvalidData, "the returned pid is zero"));
402 }
403 }
404}
405
406#[cfg(any(target_os="illumos", target_os="solaris"))]
407pub
408fn peer_credentials<FD: AsFd>(conn: FD) -> Result<ConnCredentials, io::Error>
409{
410 struct UcredAlloc(*mut ucred_t);
411
412 impl Drop for UcredAlloc
413 {
414 fn drop(&mut self)
415 {
416 unsafe
417 {
418 if self.0 != ptr::null_mut()
419 {
420 ucred_free(self.0);
421 }
422 }
423 }
424 }
425 unsafe
426 {
427 let mut ucred = UcredAlloc(ptr::null_mut());
428 if getpeerucred(conn, &mut ucred.0) == -1
429 {
430 return Err(io::Error::last_os_error());
431 }
432 else if ucred.0 == ptr::null_mut()
433 {
434 return Err(io::Error::new(NotConnected, "socket is not a connection"));
435 }
436 else
437 {
438 let euid = ucred_geteuid(ucred.0 as *const _);
439 let egid = ucred_getegid(ucred.0 as *const _);
440 let pid = ucred_getpid(ucred.0 as *const _);
441 let mut groups_ptr: *const gid_t = ptr::null_mut();
442 let ngroups = ucred_getgroups(ucred.0 as *const _, &mut groups_ptr);
443
444 if euid != -1i32 as uid_t && egid != -1i32 as gid_t && pid != -1i32 as pid_t && pid != 0
447 {
448 return Ok(
449 ConnCredentials::LinuxLike
450 {
451 pid: NonZeroU32::new(pid as u32).unwrap(), euid: euid as u32,
453 egid: egid as u32,
454 }
455 );
456 }
457 else if euid != -1i32 as uid_t && ngroups > 0 && groups_ptr != ptr::null()
458 {
459 let mut groups = [u32::max_value(); 16];
460 let number_of_groups = ngroups.min(16) as u8;
461
462
463 for (i, group) in groups[0..number_of_groups].iter_mut().enumerate()
464 {
465 *group = *groups_ptr.offset(i as isize);
466 }
468
469 return Ok(
470 ConnCredentials::MacOsLike
471 {
472 euid: euid as u32,
473 number_of_groups,
474 groups,
475 }
476 );
477 }
478 else if euid != -1i32 as uid_t && egid != -1i32 as gid_t
479 {
480 let mut groups = [u32::max_value(); 16];
481 groups[0] = egid as u32;
482
483 return Ok(
484 ConnCredentials::MacOsLike
485 {
486 euid: euid as u32,
487 number_of_groups: 1,
488 groups,
489 }
490 );
491 }
492 else
493 {
494 return Err(io::Error::new(Other, "Not enough information was available"));
495 }
496 }
497 }
498}
499
500#[cfg(not(any(
501 target_os="linux", target_os="android",
502 target_os="freebsd", target_os="dragonfly", target_vendor="apple",
503 target_os="openbsd", target_os="netbsd",
504 target_os="illumos", target_os="solaris",
505)))]
506pub
507fn peer_credentials<FD: AsFd>(_conn: FD) -> Result<ConnCredentials, io::Error>
508{
509 Err(io::Error::new(Other, "Not available"))
510}
511
512
513
514#[cfg(any(target_os="linux", target_os="android"))]
515pub type RawReceivedCredentials = libc::ucred;
516
517
518#[derive(Clone,Copy, PartialEq,Eq,Hash, Debug)]
531pub struct ReceivedCredentials
532{
533 #[cfg(any(target_os="linux", target_os="android", target_os="dragonfly"))]
534 pid: u32,
535 #[cfg(any(target_os="linux", target_os="android"))]
536 uid: u32,
537 #[cfg(any(target_os="linux", target_os="android"))]
538 gid: u32,
539
540 #[cfg(any(
541 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
542 target_os="illumos", target_os="solaris", target_os="macos",
543 ))]
544 real_uid: u32,
545 #[cfg(any(
546 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
547 target_os="illumos", target_os="solaris", target_os="macos",
548 ))]
549 effective_uid: u32,
550 #[cfg(any(
551 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
552 target_os="illumos", target_os="solaris", target_os="macos",
553 ))]
554 real_gid: u32,
555 #[cfg(any(
556 target_os="freebsd", target_os="netbsd",
557 target_os="illumos", target_os="solaris", target_os="macos",
558 ))]
559 effective_gid: u32,
560 #[cfg(any(
561 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
562 target_os="illumos", target_os="solaris", target_os="macos",
563 ))]
564 groups: [u32; 5],
565}
566
567#[allow(unused)] impl ReceivedCredentials
569{
570 #[cfg(any(target_os="linux", target_os="android"))]
571 pub(crate)
572 fn from_raw(creds: libc::ucred) -> Self
573 {
574 ReceivedCredentials
575 {
576 pid: creds.pid as u32,
577 uid: creds.uid as u32,
578 gid: creds.gid as u32,
579 }
580 }
581
582 pub
586 fn pid(&self) -> Option<u32>
587 {
588 #[cfg(any(target_os="linux", target_os="android", target_os="dragonfly"))]
589 {
590 Some(self.pid)
591 }
592 #[cfg(not(any(target_os="linux", target_os="android", target_os="dragonfly")))]
593 {
594 None
595 }
596 }
597
598 pub
599 fn effective_or_sent_uid(&self) -> u32
600 {
601 #[cfg(any(target_os="linux", target_os="android"))]
602 {
603 self.uid
604 }
605
606 #[cfg(any(
607 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
608 target_os="illumos", target_os="solaris", target_os="macos",
609 ))]
610 {
611 self.effective_uid
612 }
613
614 #[cfg(not(any(
615 target_os="linux", target_os="android",
616 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
617 target_os="illumos", target_os="solaris", target_os="macos",
618 )))]
619 {
620 unreachable!("struct cannot be created on unsupported OSes")
621 }
622 }
623
624 pub
625 fn real_or_sent_uid(&self) -> u32
626 {
627 #[cfg(any(target_os="linux", target_os="android"))]
628 {
629 self.uid
630 }
631 #[cfg(any(
632 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
633 target_os="illumos", target_os="solaris", target_os="macos",
634 ))]
635 {
636 self.real_uid
637 }
638 #[cfg(not(any(
639 target_os="linux", target_os="android",
640 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
641 target_os="illumos", target_os="solaris", target_os="macos",
642 )))]
643 {
644 unreachable!("struct cannot be created on unsupported OSes")
645 }
646 }
647 pub fn effective_or_sent_gid(&self) -> Option<u32> {
648 #[cfg(any(target_os="linux", target_os="android"))] {
649 Some(self.gid)
650 }
651 #[cfg(any(
652 target_os="freebsd", target_os="netbsd",
653 target_os="illumos", target_os="solaris", target_os="macos",
654 ))] {
655 Some(self.effective_gid)
656 }
657 #[cfg(not(any(
658 target_os="linux", target_os="android",
659 target_os="freebsd", target_os="netbsd",
660 target_os="illumos", target_os="solaris", target_os="macos",
661 )))] {
662 None
663 }
664 }
665
666 pub fn real_or_sent_gid(&self) -> u32
667 {
668 #[cfg(any(target_os="linux", target_os="android"))]
669 {
670 self.gid
671 }
672 #[cfg(any(
673 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
674 target_os="illumos", target_os="solaris", target_os="macos",
675 ))]
676 {
677 self.real_gid
678 }
679 #[cfg(not(any(
680 target_os="linux", target_os="android",
681 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
682 target_os="illumos", target_os="solaris", target_os="macos",
683 )))]
684 {
685 unreachable!("struct cannot be created on unsupported OSes")
686 }
687 }
688 pub
693 fn groups(&self) -> &[u32]
694 {
695 #[cfg(any(
696 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
697 target_os="illumos", target_os="solaris", target_os="macos",
698 ))]
699 {
700 &self.groups[..]
701 }
702 #[cfg(not(any(
703 target_os="freebsd", target_os="netbsd", target_os="dragonfly",
704 target_os="illumos", target_os="solaris", target_os="macos",
705 )))]
706 {
707 &[]
708 }
709 }
710}