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 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/// 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        user_id: Option<u32>,
57    ) -> Result<Self> {
58        Client::new(socket, mechanism, server_guid, bus, user_id)
59            .perform()
60            .await
61    }
62
63    /// Create a server-side `Authenticated` for the given `socket`.
64    ///
65    /// The function takes `client_uid` on Unix only. On Windows, it takes `client_sid` instead.
66    #[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    /// Perform the handshake.
93    ///
94    /// On a successful handshake, you get an `Authenticated`. If you need to send a Bus Hello,
95    /// this remains to be done.
96    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        // Tokio needs us to call the sync function from async context. :shrug:
138        let (p0, p1) = crate::utils::block_on(async { UnixStream::pair().unwrap() });
139
140        // initialize both handshakes
141        #[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        // 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(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}