ironrdp_acceptor/
lib.rs

1#![cfg_attr(doc, doc = include_str!("../README.md"))]
2#![doc(html_logo_url = "https://cdnweb.devolutions.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg")]
3
4use ironrdp_async::{single_sequence_step, Framed, FramedRead, FramedWrite, NetworkClient, StreamWrapper};
5use ironrdp_connector::sspi::credssp::EarlyUserAuthResult;
6use ironrdp_connector::sspi::{AuthIdentity, KerberosServerConfig, Username};
7use ironrdp_connector::{custom_err, general_err, ConnectorResult, ServerName};
8use ironrdp_core::WriteBuf;
9use tracing::{debug, instrument, trace};
10
11mod channel_connection;
12mod connection;
13pub mod credssp;
14mod finalization;
15mod util;
16
17pub use ironrdp_connector::DesktopSize;
18use ironrdp_pdu::nego;
19
20pub use self::channel_connection::{ChannelConnectionSequence, ChannelConnectionState};
21pub use self::connection::{Acceptor, AcceptorResult, AcceptorState};
22pub use self::finalization::{FinalizationSequence, FinalizationState};
23use crate::credssp::resolve_generator;
24
25pub enum BeginResult<S>
26where
27    S: StreamWrapper,
28{
29    ShouldUpgrade(S::InnerStream),
30    Continue(Framed<S>),
31}
32
33pub async fn accept_begin<S>(mut framed: Framed<S>, acceptor: &mut Acceptor) -> ConnectorResult<BeginResult<S>>
34where
35    S: FramedRead + FramedWrite + StreamWrapper,
36{
37    let mut buf = WriteBuf::new();
38
39    loop {
40        if let Some(security) = acceptor.reached_security_upgrade() {
41            let result = if security.is_empty() {
42                BeginResult::Continue(framed)
43            } else {
44                BeginResult::ShouldUpgrade(framed.into_inner_no_leftover())
45            };
46
47            return Ok(result);
48        }
49
50        single_sequence_step(&mut framed, acceptor, &mut buf).await?;
51    }
52}
53
54pub async fn accept_credssp<S, N>(
55    framed: &mut Framed<S>,
56    acceptor: &mut Acceptor,
57    network_client: &mut N,
58    client_computer_name: ServerName,
59    public_key: Vec<u8>,
60    kerberos_config: Option<KerberosServerConfig>,
61) -> ConnectorResult<()>
62where
63    S: FramedRead + FramedWrite,
64    N: NetworkClient,
65{
66    let mut buf = WriteBuf::new();
67
68    if acceptor.should_perform_credssp() {
69        perform_credssp_step(
70            framed,
71            acceptor,
72            network_client,
73            &mut buf,
74            client_computer_name,
75            public_key,
76            kerberos_config,
77        )
78        .await
79    } else {
80        Ok(())
81    }
82}
83
84pub async fn accept_finalize<S>(
85    mut framed: Framed<S>,
86    acceptor: &mut Acceptor,
87) -> ConnectorResult<(Framed<S>, AcceptorResult)>
88where
89    S: FramedRead + FramedWrite,
90{
91    let mut buf = WriteBuf::new();
92
93    loop {
94        if let Some(result) = acceptor.get_result() {
95            return Ok((framed, result));
96        }
97        single_sequence_step(&mut framed, acceptor, &mut buf).await?;
98    }
99}
100
101#[instrument(level = "trace", skip_all, ret)]
102async fn perform_credssp_step<S, N>(
103    framed: &mut Framed<S>,
104    acceptor: &mut Acceptor,
105    network_client: &mut N,
106    buf: &mut WriteBuf,
107    client_computer_name: ServerName,
108    public_key: Vec<u8>,
109    kerberos_config: Option<KerberosServerConfig>,
110) -> ConnectorResult<()>
111where
112    S: FramedRead + FramedWrite,
113    N: NetworkClient,
114{
115    assert!(acceptor.should_perform_credssp());
116    let AcceptorState::Credssp { protocol, .. } = acceptor.state else {
117        unreachable!()
118    };
119
120    let result = credssp_loop(
121        framed,
122        acceptor,
123        network_client,
124        buf,
125        client_computer_name,
126        public_key,
127        kerberos_config,
128    )
129    .await;
130
131    if protocol.intersects(nego::SecurityProtocol::HYBRID_EX) {
132        trace!(?result, "HYBRID_EX");
133
134        let result = if result.is_ok() {
135            EarlyUserAuthResult::Success
136        } else {
137            EarlyUserAuthResult::AccessDenied
138        };
139
140        buf.clear();
141        result
142            .to_buffer(&mut *buf)
143            .map_err(|e| ironrdp_connector::custom_err!("to_buffer", e))?;
144        let response = &buf[..result.buffer_len()];
145        framed
146            .write_all(response)
147            .await
148            .map_err(|e| ironrdp_connector::custom_err!("write all", e))?;
149    }
150
151    result?;
152
153    acceptor.mark_credssp_as_done();
154
155    return Ok(());
156
157    async fn credssp_loop<S, N>(
158        framed: &mut Framed<S>,
159        acceptor: &mut Acceptor,
160        network_client: &mut N,
161        buf: &mut WriteBuf,
162        client_computer_name: ServerName,
163        public_key: Vec<u8>,
164        kerberos_config: Option<KerberosServerConfig>,
165    ) -> ConnectorResult<()>
166    where
167        S: FramedRead + FramedWrite,
168        N: NetworkClient,
169    {
170        let creds = acceptor
171            .creds
172            .as_ref()
173            .ok_or_else(|| general_err!("no credentials while doing credssp"))?;
174        let username = Username::new(&creds.username, None).map_err(|e| custom_err!("invalid username", e))?;
175        let identity = AuthIdentity {
176            username,
177            password: creds.password.clone().into(),
178        };
179
180        let mut sequence =
181            credssp::CredsspSequence::init(&identity, client_computer_name, public_key, kerberos_config)?;
182
183        loop {
184            let Some(next_pdu_hint) = sequence.next_pdu_hint()? else {
185                break;
186            };
187
188            debug!(
189                acceptor.state = ?acceptor.state,
190                hint = ?next_pdu_hint,
191                "Wait for PDU"
192            );
193
194            let pdu = framed
195                .read_by_hint(next_pdu_hint)
196                .await
197                .map_err(|e| ironrdp_connector::custom_err!("read frame by hint", e))?;
198
199            trace!(length = pdu.len(), "PDU received");
200
201            let Some(ts_request) = sequence.decode_client_message(&pdu)? else {
202                break;
203            };
204
205            let result = {
206                let mut generator = sequence.process_ts_request(ts_request);
207                resolve_generator(&mut generator, network_client).await
208            }; // drop generator
209
210            buf.clear();
211            let written = sequence.handle_process_result(result, buf)?;
212
213            if let Some(response_len) = written.size() {
214                let response = &buf[..response_len];
215                trace!(response_len, "Send response");
216                framed
217                    .write_all(response)
218                    .await
219                    .map_err(|e| ironrdp_connector::custom_err!("write all", e))?;
220            }
221        }
222
223        Ok(())
224    }
225}