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