diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 6171e7c..5b6f98b 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -859,7 +859,7 @@ dependencies = [ "bitflags 2.13.0", "core-foundation 0.10.1", "core-graphics-types", - "foreign-types", + "foreign-types 0.5.0", "libc", ] @@ -1672,6 +1672,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + [[package]] name = "foreign-types" version = "0.5.0" @@ -1679,7 +1688,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared", + "foreign-types-shared 0.3.1", ] [[package]] @@ -1693,6 +1702,12 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -3326,12 +3341,49 @@ dependencies = [ "pathdiff", ] +[[package]] +name = "openssl" +version = "0.10.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" +dependencies = [ + "bitflags 2.13.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + [[package]] name = "openssl-probe" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" +[[package]] +name = "openssl-sys" +version = "0.9.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b47e7e6bb2c38cd930d25a23b40fa52e068c10e85f3e03a7f5ba5aaca5713695" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -4105,6 +4157,7 @@ dependencies = [ "futures-util", "getrandom 0.2.17", "ironrdp-rdcleanpath", + "openssl", "roxmltree", "russh", "rustls", @@ -4118,6 +4171,7 @@ dependencies = [ "tauri-plugin-process", "tauri-plugin-updater", "tokio", + "tokio-openssl", "tokio-rustls", "tokio-tungstenite 0.24.0", "uuid", @@ -5771,6 +5825,17 @@ dependencies = [ "syn 2.0.118", ] +[[package]] +name = "tokio-openssl" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59df6849caa43bb7567f9a36f863c447d95a11d5903c9cc334ba32576a27eadd" +dependencies = [ + "openssl", + "openssl-sys", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.26.4" @@ -6246,6 +6311,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version-compare" version = "0.2.1" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 608b6d3..eb9c627 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -45,4 +45,6 @@ tokio-rustls = { version = "0.26", default-features = false, features = ["ring", webpki-roots = "0.26" # RDP: RDCleanPath PDU (same crate family as the ironrdp-web client) for the /rdp proxy. ironrdp-rdcleanpath = "0.2" +openssl = "0.10" +tokio-openssl = "0.6" diff --git a/src-tauri/src/gateway/rdp.rs b/src-tauri/src/gateway/rdp.rs index b657a68..9c15a4b 100644 --- a/src-tauri/src/gateway/rdp.rs +++ b/src-tauri/src/gateway/rdp.rs @@ -9,16 +9,14 @@ //! connection request; we reply with an RDCleanPath *response* (DER) carrying the X.224 confirm, //! the server cert chain and the resolved address; after that it's a raw byte relay. -use std::sync::Arc; - use axum::extract::ws::{Message, WebSocket}; use futures_util::stream::SplitStream; use futures_util::{SinkExt, StreamExt}; use ironrdp_rdcleanpath::{DetectionResult, RDCleanPath, RDCleanPathPdu}; +use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; -use tokio_rustls::client::TlsStream; -use tokio_rustls::TlsConnector; +use tokio_openssl::SslStream; use crate::gateway::SessionSpec; @@ -73,33 +71,49 @@ async fn negotiate_and_tls( x224_req: &[u8], host: &str, port: u16, -) -> Result<(TlsStream, Vec, Vec>), BoxErr> { +) -> Result<(SslStream, Vec, Vec>), BoxErr> { let mut tcp = TcpStream::connect((host, port)).await?; tcp.write_all(x224_req).await?; let x224_resp = read_tpkt(&mut tcp).await?; - // rustls 0.23 needs the process-default provider (ring) — shared with the relay client. - crate::gateway::relay::ensure_crypto_provider(); - let config = rustls::ClientConfig::builder() - .dangerous() - .with_custom_certificate_verifier(Arc::new(AcceptAllServerCert)) - .with_no_client_auth(); - let connector = TlsConnector::from(Arc::new(config)); - let tls = connector.connect(server_name_for(host)?, tcp).await?; + let mut builder = SslConnector::builder(SslMethod::tls())?; + builder.set_verify(SslVerifyMode::NONE); + // Support modern AND legacy ciphers (SECLEVEL=0) so Windows Server (2008/2012/2016+) all work. + let _ = builder.set_cipher_list("ALL:@SECLEVEL=0"); - let certs = tls - .get_ref() - .1 - .peer_certificates() - .map(|cs| cs.iter().map(|c| c.as_ref().to_vec()).collect()) - .unwrap_or_default(); + let connector = builder.build(); + let mut config = connector.configure()?; + if host.parse::().is_err() { + config.set_use_server_name_indication(true); + } else { + config.set_use_server_name_indication(false); + } + let ssl = config.into_ssl(host)?; + let mut tls = SslStream::new(ssl, tcp)?; + std::pin::Pin::new(&mut tls).connect().await?; + + let mut certs = Vec::new(); + if let Some(leaf) = tls.ssl().peer_certificate() { + if let Ok(der) = leaf.to_der() { + certs.push(der); + } + } + if let Some(chain) = tls.ssl().peer_cert_chain() { + for cert in chain { + if let Ok(der) = cert.to_der() { + if !certs.contains(&der) { + certs.push(der); + } + } + } + } Ok((tls, x224_resp, certs)) } async fn relay( mut ws_tx: futures_util::stream::SplitSink, mut ws_rx: SplitStream, - tls: TlsStream, + tls: SslStream, ) { let (mut tls_rd, mut tls_wr) = tokio::io::split(tls); @@ -187,60 +201,6 @@ fn split_host_port(target: &str) -> (String, u16) { } } -fn server_name_for(host: &str) -> Result, BoxErr> { - if let Ok(ip) = host.parse::() { - Ok(rustls::pki_types::ServerName::IpAddress(ip.into())) - } else { - Ok(rustls::pki_types::ServerName::try_from(host.to_string())?) - } -} - -/// TLS verifier that accepts any server certificate (RDP hosts use self-signed certs; the real -/// authentication is CredSSP/NLA, done by the client using the cert chain we return). -#[derive(Debug)] -struct AcceptAllServerCert; - -impl rustls::client::danger::ServerCertVerifier for AcceptAllServerCert { - fn verify_server_cert( - &self, - _end_entity: &rustls::pki_types::CertificateDer<'_>, - _intermediates: &[rustls::pki_types::CertificateDer<'_>], - _server_name: &rustls::pki_types::ServerName<'_>, - _ocsp_response: &[u8], - _now: rustls::pki_types::UnixTime, - ) -> Result { - Ok(rustls::client::danger::ServerCertVerified::assertion()) - } - - fn verify_tls12_signature( - &self, - _message: &[u8], - _cert: &rustls::pki_types::CertificateDer<'_>, - _dss: &rustls::DigitallySignedStruct, - ) -> Result { - Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) - } - - fn verify_tls13_signature( - &self, - _message: &[u8], - _cert: &rustls::pki_types::CertificateDer<'_>, - _dss: &rustls::DigitallySignedStruct, - ) -> Result { - Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) - } - - fn supported_verify_schemes(&self) -> Vec { - use rustls::SignatureScheme as S; - vec![ - S::RSA_PKCS1_SHA256, S::RSA_PKCS1_SHA384, S::RSA_PKCS1_SHA512, - S::ECDSA_NISTP256_SHA256, S::ECDSA_NISTP384_SHA384, - S::RSA_PSS_SHA256, S::RSA_PSS_SHA384, S::RSA_PSS_SHA512, - S::ED25519, - ] - } -} - #[cfg(test)] mod tests { use super::*;