Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/ironrdp-capture-replay/src/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ impl ReplayRouter {
compression_type: activation.compression_type,
enable_server_pointer: false,
pointer_software_rendering: false,
security: None,
}
.build();
Ok(Self {
Expand Down
3 changes: 3 additions & 0 deletions crates/ironrdp-client/src/rdp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3071,6 +3071,7 @@ async fn active_session(
compression_type: connection_result.compression_type,
enable_server_pointer: connection_result.enable_server_pointer,
pointer_software_rendering: connection_result.pointer_software_rendering,
security: connection_result.security,
}
.build();
#[cfg(feature = "udp")]
Expand Down Expand Up @@ -5285,6 +5286,7 @@ mod tests {
compression_type: None,
enable_server_pointer: false,
pointer_software_rendering: false,
security: None,
}
.build();

Expand Down Expand Up @@ -5322,6 +5324,7 @@ mod tests {
compression_type: None,
enable_server_pointer: false,
pointer_software_rendering: false,
security: None,
}
.build();

Expand Down
1 change: 1 addition & 0 deletions crates/ironrdp-connector/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ ironrdp-svc = { path = "../ironrdp-svc", version = "0.8" } # public
ironrdp-core = { path = "../ironrdp-core", version = "0.2" } # public
ironrdp-error = { path = "../ironrdp-error", version = "0.2" } # public
ironrdp-pdu = { path = "../ironrdp-pdu", version = "0.9", features = ["std"] } # public
ironrdp-rdpsec = { path = "../ironrdp-rdpsec" }
sspi = { version = "0.21", features = ["scard"] }
url = "2.5" # public
rand = { version = "0.9", features = ["std"] } # TODO: dependency injection?
Expand Down
379 changes: 366 additions & 13 deletions crates/ironrdp-connector/src/connection.rs

Large diffs are not rendered by default.

66 changes: 56 additions & 10 deletions crates/ironrdp-connector/src/license_exchange.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
use core::fmt::Debug;
use core::panic::RefUnwindSafe;
use core::{fmt, mem};
use std::borrow::Cow;
use std::str;
use std::sync::Arc;

use ironrdp_core::WriteBuf;
use ironrdp_core::{Encode, WriteBuf, encode_buf, encode_vec};
use ironrdp_pdu::PduHint;
use ironrdp_pdu::mcs;
use ironrdp_pdu::rdp::headers::BASIC_SECURITY_HEADER_SIZE;
use ironrdp_pdu::rdp::server_license::{self, LicenseInformation, LicensePdu, ServerLicenseError};
use ironrdp_pdu::x224::X224;
use ironrdp_rdpsec::{SEC_LICENSE_ENCRYPT_CS, SEC_LICENSE_PKT, SharedSecurity};
use rand::RngCore as _;
use tracing::{debug, error, info, trace};

Expand Down Expand Up @@ -64,6 +69,9 @@ pub struct LicenseExchangeSequence {
pub domain: Option<String>,
pub hardware_id: [u32; 4],
pub license_cache: Arc<dyn LicenseCache>,
/// Standard RDP Security cryptor, carried over from the connector when the
/// GCC exchange selected the standard path. `None` otherwise.
pub security: Option<SharedSecurity>,
}

// Use RefUnwindSafe so that types that embed LicenseCache remain UnwindSafe
Expand Down Expand Up @@ -100,8 +108,43 @@ impl LicenseExchangeSequence {
domain,
hardware_id,
license_cache,
security: None,
}
}

/// Send a license PDU, applying the Standard RDP Security envelope when
/// the cryptor is armed. License PDUs embed their own 4-byte security
/// header; on the encrypted wire it dissolves into the envelope (type
/// bits in `SEC_LICENSE_PKT | SEC_LICENSE_ENCRYPT_CS`) while the
/// plaintext path keeps the full PDU shape. Licensing traffic rides the
/// I/O channel and always carries the plain MAC.
fn send_license_pdu<T: Encode>(
&mut self,
initiator_id: u16,
channel_id: u16,
msg: &T,
output: &mut WriteBuf,
) -> ConnectorResult<usize> {
let Some(security) = &self.security else {
return encode_send_data_request(initiator_id, channel_id, msg, output);
};

let encoded = encode_vec(msg).map_err(ConnectorError::encode)?;
let body = encoded
.get(BASIC_SECURITY_HEADER_SIZE..)
.ok_or_else(|| general_err!("license PDU too short to carry a BASIC_SECURITY_HEADER"))?;
let envelope = security
.lock()
.unwrap()
.encrypt_envelope(SEC_LICENSE_PKT | SEC_LICENSE_ENCRYPT_CS, body, false);

let pdu = mcs::SendDataRequest {
initiator_id,
channel_id,
user_data: Cow::Owned(envelope),
};
encode_buf(&X224(pdu), output).map_err(ConnectorError::encode)
}
}

impl Sequence for LicenseExchangeSequence {
Expand Down Expand Up @@ -134,7 +177,7 @@ impl Sequence for LicenseExchangeSequence {

LicenseExchangeState::NewLicenseRequest => {
let send_data_indication_ctx =
ironrdp_pdu::mcs::decode_send_data_indication(input).map_err(ConnectorError::decode)?;
mcs::decode_send_data_indication(input).map_err(ConnectorError::decode)?;
let license_pdu = send_data_indication_ctx
.decode_user_data::<LicensePdu>()
.map_err(ConnectorError::decode)
Expand Down Expand Up @@ -178,10 +221,11 @@ impl Sequence for LicenseExchangeSequence {
trace!(?encryption_data, "Successfully generated Client License Info");
trace!(message = ?client_license_info, "Send");

let written = encode_send_data_request::<LicensePdu>(
let pdu: LicensePdu = client_license_info.into();
let written = self.send_license_pdu(
send_data_indication_ctx.initiator_id,
send_data_indication_ctx.channel_id,
&client_license_info.into(),
&pdu,
output,
)?;

Expand Down Expand Up @@ -209,10 +253,11 @@ impl Sequence for LicenseExchangeSequence {
trace!(?encryption_data, "Successfully generated Client New License Request");
trace!(message = ?new_license_request, "Send");

let written = encode_send_data_request::<LicensePdu>(
let pdu: LicensePdu = new_license_request.into();
let written = self.send_license_pdu(
send_data_indication_ctx.initiator_id,
send_data_indication_ctx.channel_id,
&new_license_request.into(),
&pdu,
output,
)?;

Expand Down Expand Up @@ -268,7 +313,7 @@ impl Sequence for LicenseExchangeSequence {

LicenseExchangeState::PlatformChallenge { encryption_data } => {
let send_data_indication_ctx =
ironrdp_pdu::mcs::decode_send_data_indication(input).map_err(ConnectorError::decode)?;
mcs::decode_send_data_indication(input).map_err(ConnectorError::decode)?;

let license_pdu = send_data_indication_ctx
.decode_user_data::<LicensePdu>()
Expand All @@ -289,10 +334,11 @@ impl Sequence for LicenseExchangeSequence {

debug!(message = ?challenge_response, "Send");

let written = encode_send_data_request::<LicensePdu>(
let pdu: LicensePdu = challenge_response.into();
let written = self.send_license_pdu(
send_data_indication_ctx.initiator_id,
send_data_indication_ctx.channel_id,
&challenge_response.into(),
&pdu,
output,
)?;

Expand Down Expand Up @@ -322,7 +368,7 @@ impl Sequence for LicenseExchangeSequence {

LicenseExchangeState::UpgradeLicense { encryption_data } => {
let send_data_indication_ctx =
ironrdp_pdu::mcs::decode_send_data_indication(input).map_err(ConnectorError::decode)?;
mcs::decode_send_data_indication(input).map_err(ConnectorError::decode)?;

let license_pdu = send_data_indication_ctx
.decode_user_data::<LicensePdu>()
Expand Down
12 changes: 12 additions & 0 deletions crates/ironrdp-rdpsec/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "ironrdp-rdpsec"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "Standard RDP Security (PROTOCOL_RDP) crypto engine: key schedule, MAC, RC4 stream"

[dependencies]
num-bigint = "0.4"
md-5 = "0.10"
sha1 = "0.10"
digest = "0.10"
Loading
Loading