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 | |
7 | use 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)] |
12 | pub 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 (target_vendor = "apple" )] |
27 | pub(super) use self::impl_apple::peer_cred; |
28 | #[cfg (any( |
29 | target_os = "dragonfly" , |
30 | target_os = "freebsd" , |
31 | target_os = "openbsd" , |
32 | target_os = "netbsd" , |
33 | target_os = "nto" |
34 | ))] |
35 | pub(super) use self::impl_bsd::peer_cred; |
36 | #[cfg (any(target_os = "android" , target_os = "linux" , target_os = "cygwin" ))] |
37 | pub(super) use self::impl_linux::peer_cred; |
38 | |
39 | #[cfg (any(target_os = "linux" , target_os = "android" , target_os = "cygwin" ))] |
40 | mod impl_linux { |
41 | use libc::{SO_PEERCRED, SOL_SOCKET, c_void, getsockopt, socklen_t, ucred}; |
42 | |
43 | use super::UCred; |
44 | use crate::io; |
45 | use crate::os::unix::io::AsRawFd; |
46 | use crate::os::unix::net::UnixStream; |
47 | |
48 | pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> { |
49 | let ucred_size = size_of::<ucred>(); |
50 | |
51 | // Trivial sanity checks. |
52 | assert!(size_of::<u32>() <= size_of::<usize>()); |
53 | assert!(ucred_size <= u32::MAX as usize); |
54 | |
55 | let mut ucred_size = ucred_size as socklen_t; |
56 | let mut ucred: ucred = ucred { pid: 1, uid: 1, gid: 1 }; |
57 | |
58 | unsafe { |
59 | let ret = getsockopt( |
60 | socket.as_raw_fd(), |
61 | SOL_SOCKET, |
62 | SO_PEERCRED, |
63 | (&raw mut ucred) as *mut c_void, |
64 | &mut ucred_size, |
65 | ); |
66 | |
67 | if ret == 0 && ucred_size as usize == size_of::<ucred>() { |
68 | Ok(UCred { uid: ucred.uid, gid: ucred.gid, pid: Some(ucred.pid) }) |
69 | } else { |
70 | Err(io::Error::last_os_error()) |
71 | } |
72 | } |
73 | } |
74 | } |
75 | |
76 | #[cfg (any( |
77 | target_os = "dragonfly" , |
78 | target_os = "freebsd" , |
79 | target_os = "openbsd" , |
80 | target_os = "netbsd" , |
81 | target_os = "nto" , |
82 | ))] |
83 | mod impl_bsd { |
84 | use super::UCred; |
85 | use crate::io; |
86 | use crate::os::unix::io::AsRawFd; |
87 | use crate::os::unix::net::UnixStream; |
88 | |
89 | pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> { |
90 | let mut cred = UCred { uid: 1, gid: 1, pid: None }; |
91 | unsafe { |
92 | let ret = libc::getpeereid(socket.as_raw_fd(), &mut cred.uid, &mut cred.gid); |
93 | |
94 | if ret == 0 { Ok(cred) } else { Err(io::Error::last_os_error()) } |
95 | } |
96 | } |
97 | } |
98 | |
99 | #[cfg (target_vendor = "apple" )] |
100 | mod impl_apple { |
101 | use libc::{LOCAL_PEERPID, SOL_LOCAL, c_void, getpeereid, getsockopt, pid_t, socklen_t}; |
102 | |
103 | use super::UCred; |
104 | use crate::io; |
105 | use crate::os::unix::io::AsRawFd; |
106 | use crate::os::unix::net::UnixStream; |
107 | |
108 | pub fn peer_cred(socket: &UnixStream) -> io::Result<UCred> { |
109 | let mut cred = UCred { uid: 1, gid: 1, pid: None }; |
110 | unsafe { |
111 | let ret = getpeereid(socket.as_raw_fd(), &mut cred.uid, &mut cred.gid); |
112 | |
113 | if ret != 0 { |
114 | return Err(io::Error::last_os_error()); |
115 | } |
116 | |
117 | let mut pid: pid_t = 1; |
118 | let mut pid_size = size_of::<pid_t>() as socklen_t; |
119 | |
120 | let ret = getsockopt( |
121 | socket.as_raw_fd(), |
122 | SOL_LOCAL, |
123 | LOCAL_PEERPID, |
124 | (&raw mut pid) as *mut c_void, |
125 | &mut pid_size, |
126 | ); |
127 | |
128 | if ret == 0 && pid_size as usize == size_of::<pid_t>() { |
129 | cred.pid = Some(pid); |
130 | Ok(cred) |
131 | } else { |
132 | Err(io::Error::last_os_error()) |
133 | } |
134 | } |
135 | } |
136 | } |
137 | |