Skip to content

Commit 4d974b2

Browse files
WIP
1 parent 71080c0 commit 4d974b2

File tree

5 files changed

+88
-31
lines changed

5 files changed

+88
-31
lines changed

Cargo.lock

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

devolutions-gateway/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ job-queue = { path = "../crates/job-queue" }
2828
job-queue-libsql = { path = "../crates/job-queue-libsql" }
2929
ironrdp-pdu = { version = "0.5", features = ["std"] }
3030
ironrdp-core = { version = "0.1", features = ["std"] }
31-
ironrdp-rdcleanpath = "0.1"
3231
ironrdp-tokio = { version = "0.4", default-features = false }
3332
ironrdp-connector = { version = "0.5" }
3433
ironrdp-acceptor = { version = "0.5" }
34+
ironrdp-rdcleanpath = { path = "C:\\dev\\IronRDP\\crates\\ironrdp-rdcleanpath" }
3535
ceviche = "0.6.1"
3636
picky-krb = "0.9"
3737
network-scanner = { version = "0.0.0", path = "../crates/network-scanner" }

devolutions-gateway/src/generic_client.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ where
7777
if conf.debug.dump_tokens {
7878
debug!(token, "**DEBUG OPTION**");
7979
}
80+
info!(pdu = ?pdu, "Received preconnection blob");
8081

8182
let source_ip = client_addr.ip();
8283

devolutions-gateway/src/rd_clean_path.rs

Lines changed: 82 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ struct CleanPathResult {
164164
destination: TargetAddr,
165165
server_addr: SocketAddr,
166166
server_stream: tokio_rustls::client::TlsStream<tokio::net::TcpStream>,
167-
x224_rsp: Vec<u8>,
167+
x224_rsp: Option<Vec<u8>>,
168168
}
169169

170170
async fn process_cleanpath(
@@ -234,45 +234,99 @@ async fn process_cleanpath(
234234
debug!(%selected_target, "Connected to destination server");
235235
span.record("target", selected_target.to_string());
236236

237-
// Send preconnection blob if applicable
238-
if let Some(pcb) = cleanpath_pdu.preconnection_blob {
239-
server_stream.write_all(pcb.as_bytes()).await?;
240-
}
237+
// Preconnection Blob (PCB) is currently only used for Hyper-V VMs.
238+
//
239+
// Connection sequence with Hyper-V VMs (PCB enabled):
240+
// ┌─────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
241+
// │ handled by │ │ handled by IronRDP client │
242+
// │ Gateway │ │ │
243+
// └─────────────────────┘ └─────────────────────────────────────────────────────────────┘
244+
// │PCB → TLS handshake │ → │CredSSP → X224 connection request → X224 connection response │
245+
// └─────────────────────┘ └─────────────────────────────────────────────────────────────┘
246+
//
247+
// Connection sequence without Hyper-V VMs (PCB disabled):
248+
// ┌─────────────────────────────────────────────────────────────┐ ┌──────────────────────┐
249+
// │ handled by Gateway │ │ handled by IronRDP │
250+
// │ │ │ client │
251+
// └─────────────────────────────────────────────────────────────┘ └──────────────────────┘
252+
// │X224 connection request → X224 connection response → TLS hs │ → │CredSSP → ... │
253+
// └─────────────────────────────────────────────────────────────┘ └──────────────────────┘
254+
//
255+
// Summary:
256+
// - With PCB: Gateway handles (1) sending PCB, (2) TLS handshake, then leaves CredSSP
257+
// and X224 connection request/response to IronRDP client
258+
// - Without PCB: Gateway handles (1) X224 connection request, (2) X224 connection response,
259+
// then leaves TLS handshake and CredSSP to IronRDP client
260+
let (server_stream, x224_rsp) = if let Some(pcb_string) = cleanpath_pdu.preconnection_blob {
261+
let pcb = ironrdp_pdu::pcb::PreconnectionBlob {
262+
version: ironrdp_pdu::pcb::PcbVersion::V2,
263+
id: 0,
264+
v2_payload: Some(pcb_string),
265+
};
266+
267+
let encoded = ironrdp_core::encode_vec(&pcb)
268+
.context("failed to encode preconnection blob")
269+
.map_err(CleanPathError::BadRequest)?;
270+
271+
server_stream.write_all(&encoded).await?;
272+
273+
let server_stream = crate::tls::connect(selected_target.host(), server_stream)
274+
.await
275+
.map_err(|source| CleanPathError::TlsHandshake {
276+
source,
277+
target_server: selected_target.to_owned(),
278+
})?;
241279

242-
// Send X224 connection request
243-
let x224_req = cleanpath_pdu
244-
.x224_connection_pdu
245-
.context("request is missing X224 connection PDU")
246-
.map_err(CleanPathError::BadRequest)?;
247-
server_stream.write_all(x224_req.as_bytes()).await?;
280+
(server_stream, None)
281+
} else {
282+
debug!("Preconnection blob sent");
248283

249-
// Receive server X224 connection response
284+
// Send X224 connection request
285+
let x224_req = cleanpath_pdu
286+
.x224_connection_pdu
287+
.context("request is missing X224 connection PDU")
288+
.map_err(CleanPathError::BadRequest)?;
250289

251-
trace!("Receiving X224 response");
290+
server_stream.write_all(x224_req.as_bytes()).await?;
252291

253-
let x224_rsp = read_x224_response(&mut server_stream)
254-
.await
255-
.with_context(|| format!("read X224 response from {selected_target}"))
256-
.map_err(CleanPathError::BadRequest)?;
292+
let server_stream = crate::tls::connect(selected_target.host().to_owned(), server_stream)
293+
.await
294+
.map_err(|source| CleanPathError::TlsHandshake {
295+
source,
296+
target_server: selected_target.to_owned(),
297+
})?;
298+
debug!("X224 connection request sent");
257299

258-
trace!("Establishing TLS connection with server");
300+
// Receive server X224 connection response
259301

260-
// Establish TLS connection with server
302+
trace!("Receiving X224 response");
261303

262-
let server_stream = crate::tls::connect(selected_target.host().to_owned(), server_stream)
263-
.await
264-
.map_err(|source| CleanPathError::TlsHandshake {
265-
source,
266-
target_server: selected_target.to_owned(),
267-
})?;
304+
let x224_rsp = read_x224_response(&mut server_stream)
305+
.await
306+
.with_context(|| format!("read X224 response from {selected_target}"))
307+
.map_err(CleanPathError::BadRequest)?;
308+
309+
trace!("Establishing TLS connection with server");
310+
311+
// Establish TLS connection with server
312+
313+
let server_stream = crate::tls::connect(selected_target.host(), server_stream)
314+
.await
315+
.map_err(|source| CleanPathError::TlsHandshake {
316+
source,
317+
target_server: selected_target.to_owned(),
318+
})?;
319+
320+
(server_stream, Some(x224_rsp))
321+
};
268322

269-
Ok(CleanPathResult {
323+
return Ok(CleanPathResult {
270324
destination: selected_target.to_owned(),
271325
claims,
272326
server_addr,
273327
server_stream,
274328
x224_rsp,
275-
})
329+
});
276330
}
277331

278332
#[allow(clippy::too_many_arguments)]
@@ -295,7 +349,7 @@ pub async fn handle(
295349
.await
296350
.context("couldn’t read clean cleanpath PDU")?;
297351

298-
trace!("Processing RDCleanPath");
352+
trace!(RDCleanPath = ?cleanpath_pdu,"Processing RDCleanPath");
299353

300354
let CleanPathResult {
301355
claims,

devolutions-gateway/src/rdp_pcb.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ fn decode_pcb(buf: &[u8]) -> Result<Option<(PreconnectionBlob, usize)>, io::Erro
5454
Ok(pcb) => {
5555
let pdu_size = ironrdp_core::size(&pcb);
5656
let read_len = cursor.pos();
57+
info!(
58+
pdu_size,
59+
read_len, "read preconnection blob (size: {}, read: {})", pdu_size, read_len
60+
);
5761

5862
// NOTE: sanity check (reporting the wrong number will corrupt the communication)
5963
if read_len != pdu_size {

0 commit comments

Comments
 (0)