feat!: update sspi dependency (#839)

Newer version of sspi adds support for server-side Kerberos.
This is relevant for the ironrdp-acceptor crate.
This commit is contained in:
Pavlo Myroniuk
2025-07-08 08:45:30 -04:00
committed by GitHub
parent b09d46f8f2
commit 33530212c4
8 changed files with 79 additions and 30 deletions
Generated
+2 -2
View File
@@ -5051,9 +5051,9 @@ dependencies = [
[[package]]
name = "sspi"
version = "0.15.14"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "214ee905dcdd4b7ab11404b616e58dc6944d80fe8592fbdc13abc87d7e2bff0a"
checksum = "6ebd88036b841e6e50370c1d2b7711ae1fc04d4269abda80427cda4b8230a53d"
dependencies = [
"async-dnssd",
"async-recursion",
+42 -16
View File
@@ -1,9 +1,10 @@
use ironrdp_connector::credssp::KerberosConfig;
use ironrdp_async::AsyncNetworkClient;
use ironrdp_connector::sspi::credssp::{
ClientMode, CredSspServer, CredentialsProxy, ServerError, ServerState, TsRequest,
CredSspServer, CredentialsProxy, ServerError, ServerMode, ServerState, TsRequest,
};
use ironrdp_connector::sspi::generator::{Generator, GeneratorState};
use ironrdp_connector::sspi::negotiate::ProtocolConfig;
use ironrdp_connector::sspi::{self, AuthIdentity, Username};
use ironrdp_connector::sspi::{self, AuthIdentity, KerberosServerConfig, NegotiateConfig, NetworkRequest, Username};
use ironrdp_connector::{
custom_err, general_err, ConnectorError, ConnectorErrorKind, ConnectorResult, ServerName, Written,
};
@@ -32,6 +33,9 @@ impl PduHint for CredsspTsRequestHint {
}
}
pub type CredsspProcessGenerator<'a> =
Generator<'a, NetworkRequest, sspi::Result<Vec<u8>>, Result<ServerState, ServerError>>;
#[derive(Debug)]
pub struct CredsspSequence<'a> {
server: CredSspServer<CredentialsProxyImpl<'a>>,
@@ -64,6 +68,26 @@ impl CredentialsProxy for CredentialsProxyImpl<'_> {
}
}
pub(crate) async fn resolve_generator(
generator: &mut CredsspProcessGenerator<'_>,
network_client: &mut dyn AsyncNetworkClient,
) -> Result<ServerState, ServerError> {
let mut state = generator.start();
loop {
match state {
GeneratorState::Suspended(request) => {
let response = network_client.send(&request).await.map_err(|err| ServerError {
ts_request: None,
error: sspi::Error::new(sspi::ErrorKind::InternalError, err),
})?;
state = generator.resume(Ok(response));
}
GeneratorState::Completed(client_state) => break client_state,
}
}
}
impl<'a> CredsspSequence<'a> {
pub fn next_pdu_hint(&self) -> ConnectorResult<Option<&dyn PduHint>> {
match &self.state {
@@ -77,22 +101,21 @@ impl<'a> CredsspSequence<'a> {
creds: &'a AuthIdentity,
client_computer_name: ServerName,
public_key: Vec<u8>,
kerberos_config: Option<KerberosConfig>,
krb_config: Option<KerberosServerConfig>,
) -> ConnectorResult<Self> {
let client_computer_name = client_computer_name.into_inner();
let credentials = CredentialsProxyImpl::new(creds);
let credssp_config: Box<dyn ProtocolConfig>;
if let Some(ref krb_config) = kerberos_config {
credssp_config = Box::new(Into::<sspi::KerberosConfig>::into(krb_config.clone()));
} else {
credssp_config = Box::<sspi::ntlm::NtlmConfig>::default();
}
debug!(?credssp_config);
let credssp_config: Box<dyn ProtocolConfig> = if let Some(krb_config) = krb_config {
Box::new(krb_config)
} else {
Box::<sspi::ntlm::NtlmConfig>::default()
};
let server = CredSspServer::new(
public_key,
credentials,
ClientMode::Negotiate(sspi::NegotiateConfig {
ServerMode::Negotiate(NegotiateConfig {
protocol_config: credssp_config,
package_list: None,
client_computer_name,
@@ -122,19 +145,22 @@ impl<'a> CredsspSequence<'a> {
}
}
pub fn process_ts_request(&mut self, request: TsRequest) -> Result<ServerState, Box<ServerError>> {
Ok(self.server.process(request)?)
pub fn process_ts_request(&mut self, request: TsRequest) -> CredsspProcessGenerator<'_> {
self.server.process(request)
}
pub fn handle_process_result(
&mut self,
result: Result<ServerState, Box<ServerError>>,
result: Result<ServerState, ServerError>,
output: &mut WriteBuf,
) -> ConnectorResult<Written> {
let (ts_request, next_state) = match result {
Ok(ServerState::ReplyNeeded(ts_request)) => (Some(ts_request), CredsspState::Ongoing),
Ok(ServerState::Finished(_id)) => (None, CredsspState::Finished),
Err(err) => (Some(err.ts_request), CredsspState::ServerError(err.error)),
Err(err) => (
err.ts_request.map(|ts_request| *ts_request),
CredsspState::ServerError(err.error),
),
};
self.state = next_state;
+30 -8
View File
@@ -4,10 +4,9 @@
#[macro_use]
extern crate tracing;
use ironrdp_async::{single_sequence_step, Framed, FramedRead, FramedWrite, StreamWrapper};
use ironrdp_connector::credssp::KerberosConfig;
use ironrdp_async::{single_sequence_step, AsyncNetworkClient, Framed, FramedRead, FramedWrite, StreamWrapper};
use ironrdp_connector::sspi::credssp::EarlyUserAuthResult;
use ironrdp_connector::sspi::{AuthIdentity, Username};
use ironrdp_connector::sspi::{AuthIdentity, KerberosServerConfig, Username};
use ironrdp_connector::{custom_err, general_err, ConnectorResult, ServerName};
use ironrdp_core::WriteBuf;
@@ -23,6 +22,7 @@ use ironrdp_pdu::nego;
pub use self::channel_connection::{ChannelConnectionSequence, ChannelConnectionState};
pub use self::connection::{Acceptor, AcceptorResult, AcceptorState};
pub use self::finalization::{FinalizationSequence, FinalizationState};
use crate::credssp::resolve_generator;
pub enum BeginResult<S>
where
@@ -58,7 +58,8 @@ pub async fn accept_credssp<S>(
acceptor: &mut Acceptor,
client_computer_name: ServerName,
public_key: Vec<u8>,
kerberos_config: Option<KerberosConfig>,
kerberos_config: Option<KerberosServerConfig>,
network_client: Option<&mut dyn AsyncNetworkClient>,
) -> ConnectorResult<()>
where
S: FramedRead + FramedWrite,
@@ -73,6 +74,7 @@ where
client_computer_name,
public_key,
kerberos_config,
network_client,
)
.await
} else {
@@ -104,7 +106,8 @@ async fn perform_credssp_step<S>(
buf: &mut WriteBuf,
client_computer_name: ServerName,
public_key: Vec<u8>,
kerberos_config: Option<KerberosConfig>,
kerberos_config: Option<KerberosServerConfig>,
network_client: Option<&mut dyn AsyncNetworkClient>,
) -> ConnectorResult<()>
where
S: FramedRead + FramedWrite,
@@ -120,7 +123,8 @@ where
buf: &mut WriteBuf,
client_computer_name: ServerName,
public_key: Vec<u8>,
kerberos_config: Option<KerberosConfig>,
kerberos_config: Option<KerberosServerConfig>,
mut network_client: Option<&mut dyn AsyncNetworkClient>,
) -> ConnectorResult<()>
where
S: FramedRead + FramedWrite,
@@ -160,7 +164,16 @@ where
break;
};
let result = sequence.process_ts_request(ts_request);
let result = {
let mut generator = sequence.process_ts_request(ts_request);
if let Some(network_client_ref) = network_client.as_deref_mut() {
resolve_generator(&mut generator, network_client_ref).await
} else {
generator.resolve_to_result()
}
}; // drop generator
buf.clear();
let written = sequence.handle_process_result(result, buf)?;
@@ -176,7 +189,16 @@ where
Ok(())
}
let result = credssp_loop(framed, acceptor, buf, client_computer_name, public_key, kerberos_config).await;
let result = credssp_loop(
framed,
acceptor,
buf,
client_computer_name,
public_key,
kerberos_config,
network_client,
)
.await;
if protocol.intersects(nego::SecurityProtocol::HYBRID_EX) {
trace!(?result, "HYBRID_EX");
+1 -1
View File
@@ -24,7 +24,7 @@ ironrdp-core = { path = "../ironrdp-core", version = "0.1" } # public
ironrdp-error = { path = "../ironrdp-error", version = "0.1" } # public
ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.5", features = ["std"] } # public
arbitrary = { version = "1", features = ["derive"], optional = true } # public
sspi = "0.15" # public
sspi = "0.16" # public
url = "2.5" # public
rand_core = { version = "0.6", features = ["std"] } # TODO: dependency injection?
tracing = { version = "0.1", features = ["log"] }
+1
View File
@@ -318,6 +318,7 @@ impl RdpServer {
client_name.into(),
pub_key.clone(),
None,
None,
)
.await?;
}
+1 -1
View File
@@ -27,7 +27,7 @@ ironrdp-async = { path = "../ironrdp-async", version = "0.5" } # public
ironrdp-connector = { path = "../ironrdp-connector", version = "0.5", optional = true }
tokio = { version = "1", features = ["io-util"] }
reqwest = { version = "0.12", default-features = false, features = ["http2", "system-proxy"], optional = true }
sspi = { version = "0.15", features = ["network_client", "dns_resolver"], optional = true }
sspi = { version = "0.16", features = ["network_client", "dns_resolver"], optional = true }
url = { version = "2.5", optional = true }
[lints]
+1 -1
View File
@@ -59,7 +59,7 @@ async-trait = "0.1"
image = { version = "0.25.6", default-features = false, features = ["png"] }
pico-args = "0.5"
x509-cert = { version = "0.2", default-features = false, features = ["std"] }
sspi = { version = "0.15", features = ["network_client"] }
sspi = { version = "0.16", features = ["network_client"] }
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tokio-rustls = "0.26"
+1 -1
View File
@@ -17,7 +17,7 @@ diplomat-runtime = "0.7"
ironrdp = { path = "../crates/ironrdp", features = ["session", "connector", "dvc", "svc", "rdpdr", "rdpsnd", "graphics", "input", "cliprdr", "displaycontrol"] }
ironrdp-cliprdr-native.path = "../crates/ironrdp-cliprdr-native"
ironrdp-core = { path = "../crates/ironrdp-core", features = ["alloc"] }
sspi = { version = "0.15", features = ["network_client"] }
sspi = { version = "0.16", features = ["network_client"] }
thiserror = "1"
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }