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 nix::unistd::Uid;
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 ) -> Result<Self> {
57 Client::new(socket, mechanism, server_guid, bus)
58 .perform()
59 .await
60 }
61
62 #[cfg(feature = "p2p")]
66 pub async fn server(
67 socket: BoxedSplit,
68 guid: OwnedGuid,
69 #[cfg(unix)] client_uid: Option<u32>,
70 #[cfg(windows)] client_sid: Option<String>,
71 auth_mechanism: Option<AuthMechanism>,
72 unique_name: Option<OwnedUniqueName>,
73 ) -> Result<Self> {
74 Server::new(
75 socket,
76 guid,
77 #[cfg(unix)]
78 client_uid,
79 #[cfg(windows)]
80 client_sid,
81 auth_mechanism,
82 unique_name,
83 )?
84 .perform()
85 .await
86 }
87}
88
89#[async_trait]
90pub trait Handshake {
91 async fn perform(mut self) -> Result<Authenticated>;
96}
97
98fn sasl_auth_id() -> Result<String> {
99 let id = {
100 #[cfg(unix)]
101 {
102 Uid::effective().to_string()
103 }
104
105 #[cfg(windows)]
106 {
107 win32::ProcessToken::open(None)?.sid()?
108 }
109 };
110
111 Ok(id)
112}
113
114#[cfg(feature = "p2p")]
115#[cfg(unix)]
116#[cfg(test)]
117mod tests {
118 use futures_util::future::join;
119 #[cfg(not(feature = "tokio"))]
120 use futures_util::io::{AsyncWrite, AsyncWriteExt};
121 use ntest::timeout;
122 #[cfg(not(feature = "tokio"))]
123 use std::os::unix::net::UnixStream;
124 use test_log::test;
125 #[cfg(feature = "tokio")]
126 use tokio::{
127 io::{AsyncWrite, AsyncWriteExt},
128 net::UnixStream,
129 };
130
131 use super::*;
132
133 use crate::{connection::Socket, Guid};
134
135 fn create_async_socket_pair() -> (impl AsyncWrite + Socket, impl AsyncWrite + Socket) {
136 let (p0, p1) = crate::utils::block_on(async { UnixStream::pair().unwrap() });
138
139 #[cfg(not(feature = "tokio"))]
141 let (p0, p1) = {
142 p0.set_nonblocking(true).unwrap();
143 p1.set_nonblocking(true).unwrap();
144
145 (
146 async_io::Async::new(p0).unwrap(),
147 async_io::Async::new(p1).unwrap(),
148 )
149 };
150
151 (p0, p1)
152 }
153
154 #[test]
155 #[timeout(15000)]
156 fn handshake() {
157 let (p0, p1) = create_async_socket_pair();
158
159 let guid = OwnedGuid::from(Guid::generate());
160 let client = Client::new(p0.into(), None, Some(guid.clone()), false);
161 let server =
162 Server::new(p1.into(), guid, Some(Uid::effective().into()), 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(Uid::effective().into()),
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(Uid::effective().into()),
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(Uid::effective().into()),
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(Uid::effective().into()),
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(Uid::effective().into()),
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}