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 (any(target_os = "android" , target_os = "linux" ))] |
27 | pub(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 | ))] |
36 | pub(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 | ))] |
45 | pub(super) use self::impl_mac::peer_cred; |
46 | |
47 | #[cfg (any(target_os = "linux" , target_os = "android" ))] |
48 | mod 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 | ))] |
90 | mod 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 | ))] |
113 | mod 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 | |