zbus/connection/handshake/
mod.rs

1mod 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/// The result of a finalized handshake
28///
29/// The result of a finalized [`ClientHandshake`] or [`ServerHandshake`].
30///
31/// [`ClientHandshake`]: struct.ClientHandshake.html
32/// [`ServerHandshake`]: struct.ServerHandshake.html
33#[derive(Debug)]
34pub struct Authenticated {
35    pub(crate) socket_write: Box<dyn WriteHalf>,
36    /// The server Guid
37    pub(crate) server_guid: OwnedGuid,
38    /// Whether file descriptor passing has been accepted by both sides
39    #[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    /// Create a client-side `Authenticated` for the given `socket`.
51    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    /// Create a server-side `Authenticated` for the given `socket`.
63    ///
64    /// The function takes `client_uid` on Unix only. On Windows, it takes `client_sid` instead.
65    #[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    /// Perform the handshake.
92    ///
93    /// On a successful handshake, you get an `Authenticated`. If you need to send a Bus Hello,
94    /// this remains to be done.
95    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        // Tokio needs us to call the sync function from async context. :shrug:
137        let (p0, p1) = crate::utils::block_on(async { UnixStream::pair().unwrap() });
138
139        // initialize both handshakes
140        #[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        // proceed to the handshakes
165        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}