1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use libc::{gid_t, uid_t};
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct UCred {
pub uid: uid_t,
pub gid: gid_t,
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub(crate) use self::impl_linux::get_peer_cred;
#[cfg(any(
target_os = "dragonfly",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
pub(crate) use self::impl_macos::get_peer_cred;
#[cfg(any(target_os = "linux", target_os = "android"))]
pub(crate) mod impl_linux {
use crate::uds::UnixStream;
use libc::{c_void, getsockopt, socklen_t, SOL_SOCKET, SO_PEERCRED};
use std::os::unix::io::AsRawFd;
use std::{io, mem};
use libc::ucred;
pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
unsafe {
let raw_fd = sock.as_raw_fd();
let mut ucred = ucred {
pid: 0,
uid: 0,
gid: 0,
};
let ucred_size = mem::size_of::<ucred>();
assert!(mem::size_of::<u32>() <= mem::size_of::<usize>());
assert!(ucred_size <= u32::max_value() as usize);
let mut ucred_size = ucred_size as socklen_t;
let ret = getsockopt(
raw_fd,
SOL_SOCKET,
SO_PEERCRED,
&mut ucred as *mut ucred as *mut c_void,
&mut ucred_size,
);
if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
Ok(super::UCred {
uid: ucred.uid,
gid: ucred.gid,
})
} else {
Err(io::Error::last_os_error())
}
}
}
}
#[cfg(any(
target_os = "dragonfly",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
pub(crate) mod impl_macos {
use crate::uds::UnixStream;
use libc::getpeereid;
use std::os::unix::io::AsRawFd;
use std::{io, mem};
pub(crate) fn get_peer_cred(sock: &UnixStream) -> io::Result<super::UCred> {
unsafe {
let raw_fd = sock.as_raw_fd();
let mut cred: super::UCred = mem::uninitialized();
let ret = getpeereid(raw_fd, &mut cred.uid, &mut cred.gid);
if ret == 0 {
Ok(cred)
} else {
Err(io::Error::last_os_error())
}
}
}
}
#[cfg(not(target_os = "dragonfly"))]
#[cfg(test)]
mod test {
use crate::uds::UnixStream;
use libc::getegid;
use libc::geteuid;
#[test]
#[cfg_attr(
target_os = "freebsd",
ignore = "Requires FreeBSD 12.0 or later. https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=176419"
)]
#[cfg_attr(
target_os = "netbsd",
ignore = "NetBSD does not support getpeereid() for sockets created by socketpair()"
)]
fn test_socket_pair() {
let (a, b) = UnixStream::pair().unwrap();
let cred_a = a.peer_cred().unwrap();
let cred_b = b.peer_cred().unwrap();
assert_eq!(cred_a, cred_b);
let uid = unsafe { geteuid() };
let gid = unsafe { getegid() };
assert_eq!(cred_a.uid, uid);
assert_eq!(cred_a.gid, gid);
}
}