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