1use libc::{gid_t, uid_t};
2
3#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
5pub struct UCred {
6 pub uid: uid_t,
8 pub gid: gid_t,
10}
11
12#[cfg(any(target_os = "linux", target_os = "android"))]
13pub use self::impl_linux::get_peer_cred;
14
15#[cfg(any(
16 target_os = "dragonfly",
17 target_os = "macos",
18 target_os = "ios",
19 target_os = "freebsd",
20 target_os = "netbsd",
21 target_os = "openbsd"
22))]
23pub use self::impl_macos::get_peer_cred;
24
25#[cfg(any(target_os = "solaris", target_os = "illumos"))]
26pub use self::impl_solaris::get_peer_cred;
27
28#[cfg(any(target_os = "linux", target_os = "android"))]
29pub mod impl_linux {
30 use libc::{c_void, getsockopt, socklen_t, SOL_SOCKET, SO_PEERCRED};
31 use std::os::unix::io::AsRawFd;
32 use std::{io, mem};
33 use UnixStream;
34
35 use libc::ucred;
36
37 pub fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
38 unsafe {
39 let raw_fd = sock.as_raw_fd();
40
41 let mut ucred = ucred {
42 pid: 0,
43 uid: 0,
44 gid: 0,
45 };
46
47 let ucred_size = mem::size_of::<ucred>();
48
49 assert!(mem::size_of::<u32>() <= mem::size_of::<usize>());
51 assert!(ucred_size <= u32::max_value() as usize);
52
53 let mut ucred_size = ucred_size as socklen_t;
54
55 let ret = getsockopt(
56 raw_fd,
57 SOL_SOCKET,
58 SO_PEERCRED,
59 &mut ucred as *mut ucred as *mut c_void,
60 &mut ucred_size,
61 );
62 if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
63 Ok(super::UCred {
64 uid: ucred.uid,
65 gid: ucred.gid,
66 })
67 } else {
68 Err(io::Error::last_os_error())
69 }
70 }
71 }
72}
73
74#[cfg(any(
75 target_os = "dragonfly",
76 target_os = "macos",
77 target_os = "ios",
78 target_os = "freebsd",
79 target_os = "netbsd",
80 target_os = "openbsd"
81))]
82pub mod impl_macos {
83 use libc::getpeereid;
84 use std::os::unix::io::AsRawFd;
85 use std::{io, mem};
86 use UnixStream;
87
88 pub fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
89 unsafe {
90 let raw_fd = sock.as_raw_fd();
91
92 let mut cred: super::UCred = mem::uninitialized();
93
94 let ret = getpeereid(raw_fd, &mut cred.uid, &mut cred.gid);
95
96 if ret == 0 {
97 Ok(cred)
98 } else {
99 Err(io::Error::last_os_error())
100 }
101 }
102 }
103}
104
105#[cfg(any(target_os = "solaris", target_os = "illumos"))]
106pub mod impl_solaris {
107 use std::io;
108 use std::os::unix::io::AsRawFd;
109 use std::ptr;
110 use UnixStream;
111
112 #[allow(non_camel_case_types)]
113 enum ucred_t {}
114
115 extern "C" {
116 fn ucred_free(cred: *mut ucred_t);
117 fn ucred_geteuid(cred: *const ucred_t) -> super::uid_t;
118 fn ucred_getegid(cred: *const ucred_t) -> super::gid_t;
119
120 fn getpeerucred(
121 fd: ::std::os::raw::c_int,
122 cred: *mut *mut ucred_t,
123 ) -> ::std::os::raw::c_int;
124 }
125
126 pub fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
127 unsafe {
128 let raw_fd = sock.as_raw_fd();
129
130 let mut cred = ptr::null_mut::<*mut ucred_t>() as *mut ucred_t;
131
132 let ret = getpeerucred(raw_fd, &mut cred);
133
134 if ret == 0 {
135 let uid = ucred_geteuid(cred);
136 let gid = ucred_getegid(cred);
137
138 ucred_free(cred);
139
140 Ok(super::UCred { uid, gid })
141 } else {
142 Err(io::Error::last_os_error())
143 }
144 }
145 }
146}
147
148#[cfg(not(target_os = "dragonfly"))]
150#[cfg(test)]
151mod test {
152 use libc::getegid;
153 use libc::geteuid;
154 use UnixStream;
155
156 #[test]
157 #[cfg_attr(
158 target_os = "freebsd",
159 ignore = "Requires FreeBSD 12.0 or later. https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=176419"
160 )]
161 #[cfg_attr(
162 target_os = "netbsd",
163 ignore = "NetBSD does not support getpeereid() for sockets created by socketpair()"
164 )]
165 fn test_socket_pair() {
166 let (a, b) = UnixStream::pair().unwrap();
167 let cred_a = a.peer_cred().unwrap();
168 let cred_b = b.peer_cred().unwrap();
169 assert_eq!(cred_a, cred_b);
170
171 let uid = unsafe { geteuid() };
172 let gid = unsafe { getegid() };
173
174 assert_eq!(cred_a.uid, uid);
175 assert_eq!(cred_a.gid, gid);
176 }
177}