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