diff --git a/Cargo.lock b/Cargo.lock index 3ff56e04..1e4030d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/crates/ironrdp-acceptor/src/credssp.rs b/crates/ironrdp-acceptor/src/credssp.rs index 13adfe7f..f00ed749 100644 --- a/crates/ironrdp-acceptor/src/credssp.rs +++ b/crates/ironrdp-acceptor/src/credssp.rs @@ -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>, Result>; + #[derive(Debug)] pub struct CredsspSequence<'a> { server: CredSspServer>, @@ -64,6 +68,26 @@ impl CredentialsProxy for CredentialsProxyImpl<'_> { } } +pub(crate) async fn resolve_generator( + generator: &mut CredsspProcessGenerator<'_>, + network_client: &mut dyn AsyncNetworkClient, +) -> Result { + 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> { match &self.state { @@ -77,22 +101,21 @@ impl<'a> CredsspSequence<'a> { creds: &'a AuthIdentity, client_computer_name: ServerName, public_key: Vec, - kerberos_config: Option, + krb_config: Option, ) -> ConnectorResult { let client_computer_name = client_computer_name.into_inner(); let credentials = CredentialsProxyImpl::new(creds); - let credssp_config: Box; - if let Some(ref krb_config) = kerberos_config { - credssp_config = Box::new(Into::::into(krb_config.clone())); - } else { - credssp_config = Box::::default(); - } - debug!(?credssp_config); + let credssp_config: Box = if let Some(krb_config) = krb_config { + Box::new(krb_config) + } else { + Box::::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> { - 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>, + result: Result, output: &mut WriteBuf, ) -> ConnectorResult { 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; diff --git a/crates/ironrdp-acceptor/src/lib.rs b/crates/ironrdp-acceptor/src/lib.rs index 8b0812a8..7afe64fe 100644 --- a/crates/ironrdp-acceptor/src/lib.rs +++ b/crates/ironrdp-acceptor/src/lib.rs @@ -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 where @@ -58,7 +58,8 @@ pub async fn accept_credssp( acceptor: &mut Acceptor, client_computer_name: ServerName, public_key: Vec, - kerberos_config: Option, + kerberos_config: Option, + 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( buf: &mut WriteBuf, client_computer_name: ServerName, public_key: Vec, - kerberos_config: Option, + kerberos_config: Option, + 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, - kerberos_config: Option, + kerberos_config: Option, + 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"); diff --git a/crates/ironrdp-connector/Cargo.toml b/crates/ironrdp-connector/Cargo.toml index 28d571d8..2c81a34f 100644 --- a/crates/ironrdp-connector/Cargo.toml +++ b/crates/ironrdp-connector/Cargo.toml @@ -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"] } diff --git a/crates/ironrdp-server/src/server.rs b/crates/ironrdp-server/src/server.rs index cf5df7cd..c2a663a0 100644 --- a/crates/ironrdp-server/src/server.rs +++ b/crates/ironrdp-server/src/server.rs @@ -318,6 +318,7 @@ impl RdpServer { client_name.into(), pub_key.clone(), None, + None, ) .await?; } diff --git a/crates/ironrdp-tokio/Cargo.toml b/crates/ironrdp-tokio/Cargo.toml index a49c47aa..aeea53b6 100644 --- a/crates/ironrdp-tokio/Cargo.toml +++ b/crates/ironrdp-tokio/Cargo.toml @@ -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] diff --git a/crates/ironrdp/Cargo.toml b/crates/ironrdp/Cargo.toml index e68c98d6..789682f8 100644 --- a/crates/ironrdp/Cargo.toml +++ b/crates/ironrdp/Cargo.toml @@ -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" diff --git a/ffi/Cargo.toml b/ffi/Cargo.toml index fbefb2e7..b265a4f4 100644 --- a/ffi/Cargo.toml +++ b/ffi/Cargo.toml @@ -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"] }