zbus/connection/handshake/
mod.rs
1mod auth_mechanism;
2mod client;
3mod command;
4mod common;
5mod cookies;
6#[cfg(feature = "p2p")]
7mod server;
8
9use async_trait::async_trait;
10#[cfg(unix)]
11use nix::unistd::Uid;
12use std::{collections::VecDeque, fmt::Debug};
13use zbus_names::OwnedUniqueName;
14use zvariant::Str;
15
16#[cfg(windows)]
17use crate::win32;
18use crate::{Error, OwnedGuid, Result};
19
20use super::socket::{BoxedSplit, ReadHalf, WriteHalf};
21
22pub use auth_mechanism::AuthMechanism;
23use client::Client;
24use command::Command;
25use common::Common;
26use cookies::Cookie;
27pub(crate) use cookies::CookieContext;
28#[cfg(feature = "p2p")]
29use server::Server;
30
31#[derive(Debug)]
38pub struct Authenticated {
39 pub(crate) socket_write: Box<dyn WriteHalf>,
40 pub(crate) server_guid: OwnedGuid,
42 #[cfg(unix)]
44 pub(crate) cap_unix_fd: bool,
45
46 pub(crate) socket_read: Option<Box<dyn ReadHalf>>,
47 pub(crate) already_received_bytes: Vec<u8>,
48 #[cfg(unix)]
49 pub(crate) already_received_fds: Vec<std::os::fd::OwnedFd>,
50 pub(crate) unique_name: Option<OwnedUniqueName>,
51}
52
53impl Authenticated {
54 pub async fn client(
56 socket: BoxedSplit,
57 server_guid: Option<OwnedGuid>,
58 mechanisms: Option<VecDeque<AuthMechanism>>,
59 bus: bool,
60 ) -> Result<Self> {
61 Client::new(socket, mechanisms, server_guid, bus)
62 .perform()
63 .await
64 }
65
66 #[cfg(feature = "p2p")]
70 pub async fn server(
71 socket: BoxedSplit,
72 guid: OwnedGuid,
73 #[cfg(unix)] client_uid: Option<u32>,
74 #[cfg(windows)] client_sid: Option<String>,
75 auth_mechanisms: Option<VecDeque<AuthMechanism>>,
76 cookie_id: Option<usize>,
77 cookie_context: CookieContext<'_>,
78 unique_name: Option<OwnedUniqueName>,
79 ) -> Result<Self> {
80 Server::new(
81 socket,
82 guid,
83 #[cfg(unix)]
84 client_uid,
85 #[cfg(windows)]
86 client_sid,
87 auth_mechanisms,
88 cookie_id,
89 cookie_context,
90 unique_name,
91 )?
92 .perform()
93 .await
94 }
95}
96
97#[async_trait]
98pub trait Handshake {
99 async fn perform(mut self) -> Result<Authenticated>;
104}
105
106fn random_ascii(len: usize) -> String {
107 use rand::{distributions::Alphanumeric, thread_rng, Rng};
108 use std::iter;
109
110 let mut rng = thread_rng();
111 iter::repeat(())
112 .map(|()| rng.sample(Alphanumeric))
113 .map(char::from)
114 .take(len)
115 .collect()
116}
117
118fn sasl_auth_id() -> Result<String> {
119 let id = {
120 #[cfg(unix)]
121 {
122 Uid::effective().to_string()
123 }
124
125 #[cfg(windows)]
126 {
127 win32::ProcessToken::open(None)?.sid()?
128 }
129 };
130
131 Ok(id)
132}
133
134#[cfg(feature = "p2p")]
135#[cfg(unix)]
136#[cfg(test)]
137mod tests {
138 use futures_util::future::join;
139 #[cfg(not(feature = "tokio"))]
140 use futures_util::io::{AsyncWrite, AsyncWriteExt};
141 use ntest::timeout;
142 #[cfg(not(feature = "tokio"))]
143 use std::os::unix::net::UnixStream;
144 use test_log::test;
145 #[cfg(feature = "tokio")]
146 use tokio::{
147 io::{AsyncWrite, AsyncWriteExt},
148 net::UnixStream,
149 };
150
151 use super::*;
152
153 use crate::{Guid, Socket};
154
155 fn create_async_socket_pair() -> (impl AsyncWrite + Socket, impl AsyncWrite + Socket) {
156 let (p0, p1) = crate::utils::block_on(async { UnixStream::pair().unwrap() });
158
159 #[cfg(not(feature = "tokio"))]
161 let (p0, p1) = {
162 p0.set_nonblocking(true).unwrap();
163 p1.set_nonblocking(true).unwrap();
164
165 (
166 async_io::Async::new(p0).unwrap(),
167 async_io::Async::new(p1).unwrap(),
168 )
169 };
170
171 (p0, p1)
172 }
173
174 #[test]
175 #[timeout(15000)]
176 fn handshake() {
177 let (p0, p1) = create_async_socket_pair();
178
179 let guid = OwnedGuid::from(Guid::generate());
180 let client = Client::new(p0.into(), None, Some(guid.clone()), false);
181 let server = Server::new(
182 p1.into(),
183 guid,
184 Some(Uid::effective().into()),
185 None,
186 None,
187 CookieContext::default(),
188 None,
189 )
190 .unwrap();
191
192 let (client, server) = crate::utils::block_on(join(
194 async move { client.perform().await.unwrap() },
195 async move { server.perform().await.unwrap() },
196 ));
197
198 assert_eq!(client.server_guid, server.server_guid);
199 assert_eq!(client.cap_unix_fd, server.cap_unix_fd);
200 }
201
202 #[test]
203 #[timeout(15000)]
204 fn pipelined_handshake() {
205 let (mut p0, p1) = create_async_socket_pair();
206 let server = Server::new(
207 p1.into(),
208 Guid::generate().into(),
209 Some(Uid::effective().into()),
210 None,
211 None,
212 CookieContext::default(),
213 None,
214 )
215 .unwrap();
216
217 crate::utils::block_on(
218 p0.write_all(
219 format!(
220 "\0AUTH EXTERNAL {}\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n",
221 hex::encode(sasl_auth_id().unwrap())
222 )
223 .as_bytes(),
224 ),
225 )
226 .unwrap();
227 let server = crate::utils::block_on(server.perform()).unwrap();
228
229 assert!(server.cap_unix_fd);
230 }
231
232 #[test]
233 #[timeout(15000)]
234 fn separate_external_data() {
235 let (mut p0, p1) = create_async_socket_pair();
236 let server = Server::new(
237 p1.into(),
238 Guid::generate().into(),
239 Some(Uid::effective().into()),
240 None,
241 None,
242 CookieContext::default(),
243 None,
244 )
245 .unwrap();
246
247 crate::utils::block_on(
248 p0.write_all(
249 format!(
250 "\0AUTH EXTERNAL\r\nDATA {}\r\nBEGIN\r\n",
251 hex::encode(sasl_auth_id().unwrap())
252 )
253 .as_bytes(),
254 ),
255 )
256 .unwrap();
257 crate::utils::block_on(server.perform()).unwrap();
258 }
259
260 #[test]
261 #[timeout(15000)]
262 fn missing_external_data() {
263 let (mut p0, p1) = create_async_socket_pair();
264 let server = Server::new(
265 p1.into(),
266 Guid::generate().into(),
267 Some(Uid::effective().into()),
268 None,
269 None,
270 CookieContext::default(),
271 None,
272 )
273 .unwrap();
274
275 crate::utils::block_on(p0.write_all(b"\0AUTH EXTERNAL\r\nDATA\r\nBEGIN\r\n")).unwrap();
276 crate::utils::block_on(server.perform()).unwrap();
277 }
278
279 #[test]
280 #[timeout(15000)]
281 fn anonymous_handshake() {
282 let (mut p0, p1) = create_async_socket_pair();
283 let server = Server::new(
284 p1.into(),
285 Guid::generate().into(),
286 Some(Uid::effective().into()),
287 Some(vec![AuthMechanism::Anonymous].into()),
288 None,
289 CookieContext::default(),
290 None,
291 )
292 .unwrap();
293
294 crate::utils::block_on(p0.write_all(b"\0AUTH ANONYMOUS abcd\r\nBEGIN\r\n")).unwrap();
295 crate::utils::block_on(server.perform()).unwrap();
296 }
297
298 #[test]
299 #[timeout(15000)]
300 fn separate_anonymous_data() {
301 let (mut p0, p1) = create_async_socket_pair();
302 let server = Server::new(
303 p1.into(),
304 Guid::generate().into(),
305 Some(Uid::effective().into()),
306 Some(vec![AuthMechanism::Anonymous].into()),
307 None,
308 CookieContext::default(),
309 None,
310 )
311 .unwrap();
312
313 crate::utils::block_on(p0.write_all(b"\0AUTH ANONYMOUS\r\nDATA abcd\r\nBEGIN\r\n"))
314 .unwrap();
315 crate::utils::block_on(server.perform()).unwrap();
316 }
317}