Skip to content

Commit 3353021

Browse files
feat!: update sspi dependency (#839)
Newer version of sspi adds support for server-side Kerberos. This is relevant for the ironrdp-acceptor crate.
1 parent b09d46f commit 3353021

File tree

8 files changed

+78
-29
lines changed

8 files changed

+78
-29
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ironrdp-acceptor/src/credssp.rs

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
use ironrdp_connector::credssp::KerberosConfig;
1+
use ironrdp_async::AsyncNetworkClient;
22
use ironrdp_connector::sspi::credssp::{
3-
ClientMode, CredSspServer, CredentialsProxy, ServerError, ServerState, TsRequest,
3+
CredSspServer, CredentialsProxy, ServerError, ServerMode, ServerState, TsRequest,
44
};
5+
use ironrdp_connector::sspi::generator::{Generator, GeneratorState};
56
use ironrdp_connector::sspi::negotiate::ProtocolConfig;
6-
use ironrdp_connector::sspi::{self, AuthIdentity, Username};
7+
use ironrdp_connector::sspi::{self, AuthIdentity, KerberosServerConfig, NegotiateConfig, NetworkRequest, Username};
78
use ironrdp_connector::{
89
custom_err, general_err, ConnectorError, ConnectorErrorKind, ConnectorResult, ServerName, Written,
910
};
@@ -32,6 +33,9 @@ impl PduHint for CredsspTsRequestHint {
3233
}
3334
}
3435

36+
pub type CredsspProcessGenerator<'a> =
37+
Generator<'a, NetworkRequest, sspi::Result<Vec<u8>>, Result<ServerState, ServerError>>;
38+
3539
#[derive(Debug)]
3640
pub struct CredsspSequence<'a> {
3741
server: CredSspServer<CredentialsProxyImpl<'a>>,
@@ -64,6 +68,26 @@ impl CredentialsProxy for CredentialsProxyImpl<'_> {
6468
}
6569
}
6670

71+
pub(crate) async fn resolve_generator(
72+
generator: &mut CredsspProcessGenerator<'_>,
73+
network_client: &mut dyn AsyncNetworkClient,
74+
) -> Result<ServerState, ServerError> {
75+
let mut state = generator.start();
76+
77+
loop {
78+
match state {
79+
GeneratorState::Suspended(request) => {
80+
let response = network_client.send(&request).await.map_err(|err| ServerError {
81+
ts_request: None,
82+
error: sspi::Error::new(sspi::ErrorKind::InternalError, err),
83+
})?;
84+
state = generator.resume(Ok(response));
85+
}
86+
GeneratorState::Completed(client_state) => break client_state,
87+
}
88+
}
89+
}
90+
6791
impl<'a> CredsspSequence<'a> {
6892
pub fn next_pdu_hint(&self) -> ConnectorResult<Option<&dyn PduHint>> {
6993
match &self.state {
@@ -77,22 +101,21 @@ impl<'a> CredsspSequence<'a> {
77101
creds: &'a AuthIdentity,
78102
client_computer_name: ServerName,
79103
public_key: Vec<u8>,
80-
kerberos_config: Option<KerberosConfig>,
104+
krb_config: Option<KerberosServerConfig>,
81105
) -> ConnectorResult<Self> {
82106
let client_computer_name = client_computer_name.into_inner();
83107
let credentials = CredentialsProxyImpl::new(creds);
84-
let credssp_config: Box<dyn ProtocolConfig>;
85-
if let Some(ref krb_config) = kerberos_config {
86-
credssp_config = Box::new(Into::<sspi::KerberosConfig>::into(krb_config.clone()));
108+
109+
let credssp_config: Box<dyn ProtocolConfig> = if let Some(krb_config) = krb_config {
110+
Box::new(krb_config)
87111
} else {
88-
credssp_config = Box::<sspi::ntlm::NtlmConfig>::default();
89-
}
112+
Box::<sspi::ntlm::NtlmConfig>::default()
113+
};
90114

91-
debug!(?credssp_config);
92115
let server = CredSspServer::new(
93116
public_key,
94117
credentials,
95-
ClientMode::Negotiate(sspi::NegotiateConfig {
118+
ServerMode::Negotiate(NegotiateConfig {
96119
protocol_config: credssp_config,
97120
package_list: None,
98121
client_computer_name,
@@ -122,19 +145,22 @@ impl<'a> CredsspSequence<'a> {
122145
}
123146
}
124147

125-
pub fn process_ts_request(&mut self, request: TsRequest) -> Result<ServerState, Box<ServerError>> {
126-
Ok(self.server.process(request)?)
148+
pub fn process_ts_request(&mut self, request: TsRequest) -> CredsspProcessGenerator<'_> {
149+
self.server.process(request)
127150
}
128151

129152
pub fn handle_process_result(
130153
&mut self,
131-
result: Result<ServerState, Box<ServerError>>,
154+
result: Result<ServerState, ServerError>,
132155
output: &mut WriteBuf,
133156
) -> ConnectorResult<Written> {
134157
let (ts_request, next_state) = match result {
135158
Ok(ServerState::ReplyNeeded(ts_request)) => (Some(ts_request), CredsspState::Ongoing),
136159
Ok(ServerState::Finished(_id)) => (None, CredsspState::Finished),
137-
Err(err) => (Some(err.ts_request), CredsspState::ServerError(err.error)),
160+
Err(err) => (
161+
err.ts_request.map(|ts_request| *ts_request),
162+
CredsspState::ServerError(err.error),
163+
),
138164
};
139165

140166
self.state = next_state;

crates/ironrdp-acceptor/src/lib.rs

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
#[macro_use]
55
extern crate tracing;
66

7-
use ironrdp_async::{single_sequence_step, Framed, FramedRead, FramedWrite, StreamWrapper};
8-
use ironrdp_connector::credssp::KerberosConfig;
7+
use ironrdp_async::{single_sequence_step, AsyncNetworkClient, Framed, FramedRead, FramedWrite, StreamWrapper};
98
use ironrdp_connector::sspi::credssp::EarlyUserAuthResult;
10-
use ironrdp_connector::sspi::{AuthIdentity, Username};
9+
use ironrdp_connector::sspi::{AuthIdentity, KerberosServerConfig, Username};
1110
use ironrdp_connector::{custom_err, general_err, ConnectorResult, ServerName};
1211
use ironrdp_core::WriteBuf;
1312

@@ -23,6 +22,7 @@ use ironrdp_pdu::nego;
2322
pub use self::channel_connection::{ChannelConnectionSequence, ChannelConnectionState};
2423
pub use self::connection::{Acceptor, AcceptorResult, AcceptorState};
2524
pub use self::finalization::{FinalizationSequence, FinalizationState};
25+
use crate::credssp::resolve_generator;
2626

2727
pub enum BeginResult<S>
2828
where
@@ -58,7 +58,8 @@ pub async fn accept_credssp<S>(
5858
acceptor: &mut Acceptor,
5959
client_computer_name: ServerName,
6060
public_key: Vec<u8>,
61-
kerberos_config: Option<KerberosConfig>,
61+
kerberos_config: Option<KerberosServerConfig>,
62+
network_client: Option<&mut dyn AsyncNetworkClient>,
6263
) -> ConnectorResult<()>
6364
where
6465
S: FramedRead + FramedWrite,
@@ -73,6 +74,7 @@ where
7374
client_computer_name,
7475
public_key,
7576
kerberos_config,
77+
network_client,
7678
)
7779
.await
7880
} else {
@@ -104,7 +106,8 @@ async fn perform_credssp_step<S>(
104106
buf: &mut WriteBuf,
105107
client_computer_name: ServerName,
106108
public_key: Vec<u8>,
107-
kerberos_config: Option<KerberosConfig>,
109+
kerberos_config: Option<KerberosServerConfig>,
110+
network_client: Option<&mut dyn AsyncNetworkClient>,
108111
) -> ConnectorResult<()>
109112
where
110113
S: FramedRead + FramedWrite,
@@ -120,7 +123,8 @@ where
120123
buf: &mut WriteBuf,
121124
client_computer_name: ServerName,
122125
public_key: Vec<u8>,
123-
kerberos_config: Option<KerberosConfig>,
126+
kerberos_config: Option<KerberosServerConfig>,
127+
mut network_client: Option<&mut dyn AsyncNetworkClient>,
124128
) -> ConnectorResult<()>
125129
where
126130
S: FramedRead + FramedWrite,
@@ -160,7 +164,16 @@ where
160164
break;
161165
};
162166

163-
let result = sequence.process_ts_request(ts_request);
167+
let result = {
168+
let mut generator = sequence.process_ts_request(ts_request);
169+
170+
if let Some(network_client_ref) = network_client.as_deref_mut() {
171+
resolve_generator(&mut generator, network_client_ref).await
172+
} else {
173+
generator.resolve_to_result()
174+
}
175+
}; // drop generator
176+
164177
buf.clear();
165178
let written = sequence.handle_process_result(result, buf)?;
166179

@@ -176,7 +189,16 @@ where
176189
Ok(())
177190
}
178191

179-
let result = credssp_loop(framed, acceptor, buf, client_computer_name, public_key, kerberos_config).await;
192+
let result = credssp_loop(
193+
framed,
194+
acceptor,
195+
buf,
196+
client_computer_name,
197+
public_key,
198+
kerberos_config,
199+
network_client,
200+
)
201+
.await;
180202

181203
if protocol.intersects(nego::SecurityProtocol::HYBRID_EX) {
182204
trace!(?result, "HYBRID_EX");

crates/ironrdp-connector/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ ironrdp-core = { path = "../ironrdp-core", version = "0.1" } # public
2424
ironrdp-error = { path = "../ironrdp-error", version = "0.1" } # public
2525
ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.5", features = ["std"] } # public
2626
arbitrary = { version = "1", features = ["derive"], optional = true } # public
27-
sspi = "0.15" # public
27+
sspi = "0.16" # public
2828
url = "2.5" # public
2929
rand_core = { version = "0.6", features = ["std"] } # TODO: dependency injection?
3030
tracing = { version = "0.1", features = ["log"] }

crates/ironrdp-server/src/server.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ impl RdpServer {
318318
client_name.into(),
319319
pub_key.clone(),
320320
None,
321+
None,
321322
)
322323
.await?;
323324
}

crates/ironrdp-tokio/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ ironrdp-async = { path = "../ironrdp-async", version = "0.5" } # public
2727
ironrdp-connector = { path = "../ironrdp-connector", version = "0.5", optional = true }
2828
tokio = { version = "1", features = ["io-util"] }
2929
reqwest = { version = "0.12", default-features = false, features = ["http2", "system-proxy"], optional = true }
30-
sspi = { version = "0.15", features = ["network_client", "dns_resolver"], optional = true }
30+
sspi = { version = "0.16", features = ["network_client", "dns_resolver"], optional = true }
3131
url = { version = "2.5", optional = true }
3232

3333
[lints]

crates/ironrdp/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ async-trait = "0.1"
5959
image = { version = "0.25.6", default-features = false, features = ["png"] }
6060
pico-args = "0.5"
6161
x509-cert = { version = "0.2", default-features = false, features = ["std"] }
62-
sspi = { version = "0.15", features = ["network_client"] }
62+
sspi = { version = "0.16", features = ["network_client"] }
6363
tracing = { version = "0.1", features = ["log"] }
6464
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
6565
tokio-rustls = "0.26"

ffi/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ diplomat-runtime = "0.7"
1717
ironrdp = { path = "../crates/ironrdp", features = ["session", "connector", "dvc", "svc", "rdpdr", "rdpsnd", "graphics", "input", "cliprdr", "displaycontrol"] }
1818
ironrdp-cliprdr-native.path = "../crates/ironrdp-cliprdr-native"
1919
ironrdp-core = { path = "../crates/ironrdp-core", features = ["alloc"] }
20-
sspi = { version = "0.15", features = ["network_client"] }
20+
sspi = { version = "0.16", features = ["network_client"] }
2121
thiserror = "1"
2222
tracing = { version = "0.1", features = ["log"] }
2323
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

0 commit comments

Comments
 (0)