zbus/connection/handshake/
mod.rs1mod auth_mechanism;
2mod client;
3mod command;
4mod common;
5#[cfg(feature = "p2p")]
6mod server;
7
8use async_trait::async_trait;
9#[cfg(unix)]
10use rustix::process::geteuid;
11use std::fmt::Debug;
12use zbus_names::OwnedUniqueName;
13
14#[cfg(windows)]
15use crate::win32;
16use crate::{Error, OwnedGuid, Result};
17
18use super::socket::{BoxedSplit, ReadHalf, WriteHalf};
19
20pub use auth_mechanism::AuthMechanism;
21use client::Client;
22use command::Command;
23use common::Common;
24#[cfg(feature = "p2p")]
25use server::Server;
26
27#[derive(Debug)]
34pub struct Authenticated {
35 pub(crate) socket_write: Box<dyn WriteHalf>,
36 pub(crate) server_guid: OwnedGuid,
38 #[cfg(unix)]
40 pub(crate) cap_unix_fd: bool,
41
42 pub(crate) socket_read: Option<Box<dyn ReadHalf>>,
43 pub(crate) already_received_bytes: Vec<u8>,
44 #[cfg(unix)]
45 pub(crate) already_received_fds: Vec<std::os::fd::OwnedFd>,
46 pub(crate) unique_name: Option<OwnedUniqueName>,
47}
48
49impl Authenticated {
50 pub async fn client(
52 socket: BoxedSplit,
53 server_guid: Option<OwnedGuid>,
54 mechanism: Option<AuthMechanism>,
55 bus: bool,
56 user_id: Option<u32>,
57 ) -> Result<Self> {
58 Client::new(socket, mechanism, server_guid, bus, user_id)
59 .perform()
60 .await
61 }
62
63 #[cfg(feature = "p2p")]
67 pub async fn server(
68 socket: BoxedSplit,
69 guid: OwnedGuid,
70 #[cfg(unix)] client_uid: Option<u32>,
71 #[cfg(windows)] client_sid: Option<String>,
72 auth_mechanism: Option<AuthMechanism>,
73 unique_name: Option<OwnedUniqueName>,
74 ) -> Result<Self> {
75 Server::new(
76 socket,
77 guid,
78 #[cfg(unix)]
79 client_uid,
80 #[cfg(windows)]
81 client_sid,
82 auth_mechanism,
83 unique_name,
84 )?
85 .perform()
86 .await
87 }
88}
89
90#[async_trait]
91pub trait Handshake {
92 async fn perform(mut self) -> Result<Authenticated>;
97}
98
99fn sasl_auth_id() -> Result<String> {
100 let id = {
101 #[cfg(unix)]
102 {
103 geteuid().as_raw().to_string()
104 }
105
106 #[cfg(windows)]
107 {
108 win32::ProcessToken::open(None)?.sid()?
109 }
110 };
111
112 Ok(id)
113}
114
115#[cfg(feature = "p2p")]
116#[cfg(unix)]
117#[cfg(test)]
118mod tests {
119 use futures_util::future::join;
120 #[cfg(not(feature = "tokio"))]
121 use futures_util::io::{AsyncWrite, AsyncWriteExt};
122 use ntest::timeout;
123 #[cfg(not(feature = "tokio"))]
124 use std::os::unix::net::UnixStream;
125 use test_log::test;
126 #[cfg(feature = "tokio")]
127 use tokio::{
128 io::{AsyncWrite, AsyncWriteExt},
129 net::UnixStream,
130 };
131
132 use super::*;
133
134 use crate::{Guid, connection::Socket};
135
136 fn create_async_socket_pair() -> (impl AsyncWrite + Socket, impl AsyncWrite + Socket) {
137 let (p0, p1) = crate::utils::block_on(async { UnixStream::pair().unwrap() });
139
140 #[cfg(not(feature = "tokio"))]
142 let (p0, p1) = {
143 p0.set_nonblocking(true).unwrap();
144 p1.set_nonblocking(true).unwrap();
145
146 (
147 async_io::Async::new(p0).unwrap(),
148 async_io::Async::new(p1).unwrap(),
149 )
150 };
151
152 (p0, p1)
153 }
154
155 #[test]
156 #[timeout(15000)]
157 fn handshake() {
158 let (p0, p1) = create_async_socket_pair();
159
160 let guid = OwnedGuid::from(Guid::generate());
161 let client = Client::new(p0.into(), None, Some(guid.clone()), false, None);
162 let server = Server::new(p1.into(), guid, Some(geteuid().as_raw()), None, None).unwrap();
163
164 let (client, server) = crate::utils::block_on(join(
166 async move { client.perform().await.unwrap() },
167 async move { server.perform().await.unwrap() },
168 ));
169
170 assert_eq!(client.server_guid, server.server_guid);
171 assert_eq!(client.cap_unix_fd, server.cap_unix_fd);
172 }
173
174 #[test]
175 #[timeout(15000)]
176 fn pipelined_handshake() {
177 let (mut p0, p1) = create_async_socket_pair();
178 let server = Server::new(
179 p1.into(),
180 Guid::generate().into(),
181 Some(geteuid().as_raw()),
182 None,
183 None,
184 )
185 .unwrap();
186
187 crate::utils::block_on(
188 p0.write_all(
189 format!(
190 "\0AUTH EXTERNAL {}\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n",
191 hex::encode(sasl_auth_id().unwrap())
192 )
193 .as_bytes(),
194 ),
195 )
196 .unwrap();
197 let server = crate::utils::block_on(server.perform()).unwrap();
198
199 assert!(server.cap_unix_fd);
200 }
201
202 #[test]
203 #[timeout(15000)]
204 fn separate_external_data() {
205 let (mut p0, p1) = create_async_socket_pair();
206 let server = Server::new(
207 p1.into(),
208 Guid::generate().into(),
209 Some(geteuid().as_raw()),
210 None,
211 None,
212 )
213 .unwrap();
214
215 crate::utils::block_on(
216 p0.write_all(
217 format!(
218 "\0AUTH EXTERNAL\r\nDATA {}\r\nBEGIN\r\n",
219 hex::encode(sasl_auth_id().unwrap())
220 )
221 .as_bytes(),
222 ),
223 )
224 .unwrap();
225 crate::utils::block_on(server.perform()).unwrap();
226 }
227
228 #[test]
229 #[timeout(15000)]
230 fn missing_external_data() {
231 let (mut p0, p1) = create_async_socket_pair();
232 let server = Server::new(
233 p1.into(),
234 Guid::generate().into(),
235 Some(geteuid().as_raw()),
236 None,
237 None,
238 )
239 .unwrap();
240
241 crate::utils::block_on(p0.write_all(b"\0AUTH EXTERNAL\r\nDATA\r\nBEGIN\r\n")).unwrap();
242 crate::utils::block_on(server.perform()).unwrap();
243 }
244
245 #[test]
246 #[timeout(15000)]
247 fn anonymous_handshake() {
248 let (mut p0, p1) = create_async_socket_pair();
249 let server = Server::new(
250 p1.into(),
251 Guid::generate().into(),
252 Some(geteuid().as_raw()),
253 Some(AuthMechanism::Anonymous),
254 None,
255 )
256 .unwrap();
257
258 crate::utils::block_on(p0.write_all(b"\0AUTH ANONYMOUS abcd\r\nBEGIN\r\n")).unwrap();
259 crate::utils::block_on(server.perform()).unwrap();
260 }
261
262 #[test]
263 #[timeout(15000)]
264 fn separate_anonymous_data() {
265 let (mut p0, p1) = create_async_socket_pair();
266 let server = Server::new(
267 p1.into(),
268 Guid::generate().into(),
269 Some(geteuid().as_raw()),
270 Some(AuthMechanism::Anonymous),
271 None,
272 )
273 .unwrap();
274
275 crate::utils::block_on(p0.write_all(b"\0AUTH ANONYMOUS\r\nDATA abcd\r\nBEGIN\r\n"))
276 .unwrap();
277 crate::utils::block_on(server.perform()).unwrap();
278 }
279}