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 }; 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}