ironrdp_blocking/
connector.rs

1use std::io::{Read, Write};
2
3use ironrdp_connector::credssp::{CredsspProcessGenerator, CredsspSequence, KerberosConfig};
4use ironrdp_connector::sspi::credssp::ClientState;
5use ironrdp_connector::sspi::generator::GeneratorState;
6use ironrdp_connector::sspi::network_client::NetworkClient;
7use ironrdp_connector::{
8    general_err, ClientConnector, ClientConnectorState, ConnectionResult, ConnectorError, ConnectorResult,
9    Sequence as _, ServerName, State as _,
10};
11use ironrdp_core::WriteBuf;
12use tracing::{debug, info, instrument, trace};
13
14use crate::framed::Framed;
15
16#[non_exhaustive]
17pub struct ShouldUpgrade;
18
19#[instrument(skip_all)]
20pub fn connect_begin<S>(framed: &mut Framed<S>, connector: &mut ClientConnector) -> ConnectorResult<ShouldUpgrade>
21where
22    S: Sync + Read + Write,
23{
24    let mut buf = WriteBuf::new();
25
26    info!("Begin connection procedure");
27
28    while !connector.should_perform_security_upgrade() {
29        single_sequence_step(framed, connector, &mut buf)?;
30    }
31
32    Ok(ShouldUpgrade)
33}
34
35/// # Panics
36///
37/// Panics if connector state is not [ClientConnectorState::EnhancedSecurityUpgrade].
38pub fn skip_connect_begin(connector: &mut ClientConnector) -> ShouldUpgrade {
39    assert!(connector.should_perform_security_upgrade());
40    ShouldUpgrade
41}
42
43#[non_exhaustive]
44pub struct Upgraded;
45
46#[instrument(skip_all)]
47pub fn mark_as_upgraded(_: ShouldUpgrade, connector: &mut ClientConnector) -> Upgraded {
48    trace!("Marked as upgraded");
49    connector.mark_security_upgrade_as_done();
50    Upgraded
51}
52
53#[instrument(skip_all)]
54pub fn connect_finalize<S>(
55    _: Upgraded,
56    mut connector: ClientConnector,
57    framed: &mut Framed<S>,
58    network_client: &mut impl NetworkClient,
59    server_name: ServerName,
60    server_public_key: Vec<u8>,
61    kerberos_config: Option<KerberosConfig>,
62) -> ConnectorResult<ConnectionResult>
63where
64    S: Read + Write,
65{
66    let mut buf = WriteBuf::new();
67
68    debug!("CredSSP procedure");
69
70    if connector.should_perform_credssp() {
71        perform_credssp_step(
72            &mut connector,
73            framed,
74            network_client,
75            &mut buf,
76            server_name,
77            server_public_key,
78            kerberos_config,
79        )?;
80    }
81
82    debug!("Remaining of connection sequence");
83
84    let result = loop {
85        single_sequence_step(framed, &mut connector, &mut buf)?;
86
87        if let ClientConnectorState::Connected { result } = connector.state {
88            break result;
89        }
90    };
91
92    info!("Connected with success");
93
94    Ok(result)
95}
96
97fn resolve_generator(
98    generator: &mut CredsspProcessGenerator<'_>,
99    network_client: &mut impl NetworkClient,
100) -> ConnectorResult<ClientState> {
101    let mut state = generator.start();
102
103    loop {
104        match state {
105            GeneratorState::Suspended(request) => {
106                let response = network_client.send(&request).map_err(|e| {
107                    ConnectorError::new("network client send", ironrdp_connector::ConnectorErrorKind::Credssp(e))
108                })?;
109                state = generator.resume(Ok(response));
110            }
111            GeneratorState::Completed(client_state) => {
112                break client_state
113                    .map_err(|e| ConnectorError::new("CredSSP", ironrdp_connector::ConnectorErrorKind::Credssp(e)))
114            }
115        }
116    }
117}
118
119#[instrument(level = "trace", skip_all)]
120fn perform_credssp_step<S>(
121    connector: &mut ClientConnector,
122    framed: &mut Framed<S>,
123    network_client: &mut impl NetworkClient,
124    buf: &mut WriteBuf,
125    server_name: ServerName,
126    server_public_key: Vec<u8>,
127    kerberos_config: Option<KerberosConfig>,
128) -> ConnectorResult<()>
129where
130    S: Read + Write,
131{
132    assert!(connector.should_perform_credssp());
133
134    let selected_protocol = match connector.state {
135        ClientConnectorState::Credssp { selected_protocol, .. } => selected_protocol,
136        _ => return Err(general_err!("invalid connector state for CredSSP sequence")),
137    };
138
139    let (mut sequence, mut ts_request) = CredsspSequence::init(
140        connector.config.credentials.clone(),
141        connector.config.domain.as_deref(),
142        selected_protocol,
143        server_name,
144        server_public_key,
145        kerberos_config,
146    )?;
147
148    loop {
149        let client_state = {
150            let mut generator = sequence.process_ts_request(ts_request);
151            resolve_generator(&mut generator, network_client)?
152        }; // drop generator
153
154        buf.clear();
155        let written = sequence.handle_process_result(client_state, buf)?;
156
157        if let Some(response_len) = written.size() {
158            let response = &buf[..response_len];
159            trace!(response_len, "Send response");
160            framed
161                .write_all(response)
162                .map_err(|e| ironrdp_connector::custom_err!("write all", e))?;
163        }
164
165        let Some(next_pdu_hint) = sequence.next_pdu_hint() else {
166            break;
167        };
168
169        debug!(
170            connector.state = connector.state.name(),
171            hint = ?next_pdu_hint,
172            "Wait for PDU"
173        );
174
175        let pdu = framed
176            .read_by_hint(next_pdu_hint)
177            .map_err(|e| ironrdp_connector::custom_err!("read frame by hint", e))?;
178
179        trace!(length = pdu.len(), "PDU received");
180
181        if let Some(next_request) = sequence.decode_server_message(&pdu)? {
182            ts_request = next_request;
183        } else {
184            break;
185        }
186    }
187
188    connector.mark_credssp_as_done();
189
190    Ok(())
191}
192
193pub fn single_sequence_step<S>(
194    framed: &mut Framed<S>,
195    connector: &mut ClientConnector,
196    buf: &mut WriteBuf,
197) -> ConnectorResult<()>
198where
199    S: Read + Write,
200{
201    buf.clear();
202
203    let written = if let Some(next_pdu_hint) = connector.next_pdu_hint() {
204        debug!(
205            connector.state = connector.state.name(),
206            hint = ?next_pdu_hint,
207            "Wait for PDU"
208        );
209
210        let pdu = framed
211            .read_by_hint(next_pdu_hint)
212            .map_err(|e| ironrdp_connector::custom_err!("read frame by hint", e))?;
213
214        trace!(length = pdu.len(), "PDU received");
215
216        connector.step(&pdu, buf)?
217    } else {
218        connector.step_no_input(buf)?
219    };
220
221    if let Some(response_len) = written.size() {
222        let response = &buf[..response_len];
223        trace!(response_len, "Send response");
224        framed
225            .write_all(response)
226            .map_err(|e| ironrdp_connector::custom_err!("write all", e))?;
227    }
228
229    Ok(())
230}