mirror of
https://github.com/Devolutions/IronRDP.git
synced 2025-08-03 06:42:16 +00:00
WIP, not working
This commit is contained in:
parent
41ad74dbf1
commit
64f4f3cc1a
12 changed files with 156 additions and 52 deletions
|
@ -132,6 +132,7 @@ where
|
|||
server_name,
|
||||
server_public_key,
|
||||
kerberos_config,
|
||||
connector.config.vmconnect.is_some(),
|
||||
)?;
|
||||
|
||||
loop {
|
||||
|
|
|
@ -137,6 +137,7 @@ where
|
|||
server_name,
|
||||
server_public_key,
|
||||
kerberos_config,
|
||||
connector.config.vmconnect.is_some(),
|
||||
)?;
|
||||
|
||||
loop {
|
||||
|
|
|
@ -239,9 +239,9 @@ struct Args {
|
|||
#[clap(long, value_parser, num_args = 1.., value_delimiter = ',')]
|
||||
codecs: Vec<String>,
|
||||
|
||||
/// The Preconnection Blob
|
||||
/// The Virtual Machine ID for Hyper-V, used as Pre Connection Blob
|
||||
#[clap(long)]
|
||||
pcb: Option<String>,
|
||||
vmconnect: Option<String>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
@ -353,7 +353,7 @@ impl Config {
|
|||
request_data: None,
|
||||
pointer_software_rendering: true,
|
||||
performance_flags: PerformanceFlags::default(),
|
||||
pcb: args.pcb,
|
||||
vmconnect: args.vmconnect,
|
||||
};
|
||||
|
||||
let rdcleanpath = args
|
||||
|
|
|
@ -147,10 +147,9 @@ async fn connect(
|
|||
connector.attach_static_channel(cliprdr);
|
||||
}
|
||||
|
||||
|
||||
let should_upgrade = ironrdp_tokio::connect_begin(&mut framed, &mut connector).await?;
|
||||
|
||||
debug!("TLS upgrade");
|
||||
debug!(destination = ?config.destination,"TLS upgrade");
|
||||
|
||||
// Ensure there is no leftover
|
||||
let (initial_stream, leftover_bytes) = framed.into_inner();
|
||||
|
@ -294,7 +293,7 @@ where
|
|||
{
|
||||
// RDCleanPath request
|
||||
|
||||
let connector::ClientConnectorState::ConnectionInitiationSendRequest = connector.state else {
|
||||
let connector::ClientConnectorState::ConnectionInitiationSendRequest { .. } = connector.state else {
|
||||
return Err(connector::general_err!("invalid connector state (send request)"));
|
||||
};
|
||||
|
||||
|
|
|
@ -36,7 +36,10 @@ pub enum ClientConnectorState {
|
|||
#[default]
|
||||
Consumed,
|
||||
|
||||
ConnectionInitiationSendRequest,
|
||||
PreconnectionBlob,
|
||||
ConnectionInitiationSendRequest {
|
||||
selected_protocol: Option<nego::SecurityProtocol>,
|
||||
},
|
||||
ConnectionInitiationWaitConfirm {
|
||||
requested_protocol: nego::SecurityProtocol,
|
||||
},
|
||||
|
@ -88,7 +91,8 @@ impl State for ClientConnectorState {
|
|||
fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Consumed => "Consumed",
|
||||
Self::ConnectionInitiationSendRequest => "ConnectionInitiationSendRequest",
|
||||
Self::PreconnectionBlob => "PreconnectionBlob",
|
||||
Self::ConnectionInitiationSendRequest { .. } => "ConnectionInitiationSendRequest",
|
||||
Self::ConnectionInitiationWaitConfirm { .. } => "ConnectionInitiationWaitResponse",
|
||||
Self::EnhancedSecurityUpgrade { .. } => "EnhancedSecurityUpgrade",
|
||||
Self::Credssp { .. } => "Credssp",
|
||||
|
@ -132,7 +136,7 @@ impl ClientConnector {
|
|||
pub fn new(config: Config, client_addr: SocketAddr) -> Self {
|
||||
Self {
|
||||
config,
|
||||
state: ClientConnectorState::ConnectionInitiationSendRequest,
|
||||
state: ClientConnectorState::PreconnectionBlob,
|
||||
client_addr,
|
||||
static_channels: StaticChannelSet::new(),
|
||||
}
|
||||
|
@ -180,7 +184,8 @@ impl Sequence for ClientConnector {
|
|||
fn next_pdu_hint(&self) -> Option<&dyn PduHint> {
|
||||
match &self.state {
|
||||
ClientConnectorState::Consumed => None,
|
||||
ClientConnectorState::ConnectionInitiationSendRequest => None,
|
||||
ClientConnectorState::PreconnectionBlob => None,
|
||||
ClientConnectorState::ConnectionInitiationSendRequest { .. } => None,
|
||||
ClientConnectorState::ConnectionInitiationWaitConfirm { .. } => Some(&ironrdp_pdu::X224_HINT),
|
||||
ClientConnectorState::EnhancedSecurityUpgrade { .. } => None,
|
||||
ClientConnectorState::Credssp { .. } => None,
|
||||
|
@ -211,12 +216,86 @@ impl Sequence for ClientConnector {
|
|||
ClientConnectorState::Consumed => {
|
||||
return Err(general_err!("connector sequence state is consumed (this is a bug)",))
|
||||
}
|
||||
ClientConnectorState::PreconnectionBlob => 'state: {
|
||||
let Some(connection_id) = self.config.vmconnect.as_ref() else {
|
||||
break 'state (
|
||||
Written::Nothing,
|
||||
ClientConnectorState::ConnectionInitiationSendRequest {
|
||||
selected_protocol: None,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
let pcb = ironrdp_pdu::pcb::PreconnectionBlob {
|
||||
version: ironrdp_pdu::pcb::PcbVersion::V2,
|
||||
id: 0,
|
||||
// v2_payload: Some(connection_id.to_owned()),
|
||||
v2_payload: Some(format!("{connection_id};EnhancedMode=1").into()),
|
||||
};
|
||||
|
||||
debug!(message = ?pcb, "Send");
|
||||
|
||||
let written = ironrdp_core::encode_buf(&pcb, output).map_err(ConnectorError::encode)?;
|
||||
|
||||
let mut security_protocol = nego::SecurityProtocol::empty();
|
||||
if self.config.enable_tls {
|
||||
security_protocol.insert(nego::SecurityProtocol::SSL);
|
||||
}
|
||||
|
||||
if self.config.enable_credssp {
|
||||
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/902b090b-9cb3-4efc-92bf-ee13373371e3
|
||||
// The spec is stating that `PROTOCOL_SSL` "SHOULD" also be set when using `PROTOCOL_HYBRID`.
|
||||
// > PROTOCOL_HYBRID (0x00000002)
|
||||
// > Credential Security Support Provider protocol (CredSSP) (section 5.4.5.2).
|
||||
// > If this flag is set, then the PROTOCOL_SSL (0x00000001) flag SHOULD also be set
|
||||
// > because Transport Layer Security (TLS) is a subset of CredSSP.
|
||||
// However, crucially, it’s not strictly required (not "MUST").
|
||||
// In fact, we purposefully choose to not set `PROTOCOL_SSL` unless `enable_winlogon` is `true`.
|
||||
// This tells the server that we are not going to accept downgrading NLA to TLS security.
|
||||
security_protocol.insert(nego::SecurityProtocol::HYBRID | nego::SecurityProtocol::HYBRID_EX);
|
||||
}
|
||||
|
||||
if security_protocol.is_standard_rdp_security() {
|
||||
return Err(reason_err!("Initiation", "standard RDP security is not supported",));
|
||||
}
|
||||
|
||||
break 'state (
|
||||
Written::from_size(written)?,
|
||||
ClientConnectorState::EnhancedSecurityUpgrade {
|
||||
selected_protocol: security_protocol,
|
||||
},
|
||||
);
|
||||
}
|
||||
//== Connection Initiation ==//
|
||||
// Exchange supported security protocols and a few other connection flags.
|
||||
ClientConnectorState::ConnectionInitiationSendRequest => 'state: {
|
||||
ClientConnectorState::ConnectionInitiationSendRequest { selected_protocol } => 'state: {
|
||||
debug!("Connection Initiation");
|
||||
|
||||
if self.config.vmconnect.is_some() {
|
||||
let selected_protocol = selected_protocol.ok_or(reason_err!(
|
||||
"Initiation",
|
||||
"VMConnect requires a selected protocol at ConnectionInitiationSendRequest state",
|
||||
))?;
|
||||
|
||||
let connection_request = nego::ConnectionRequest {
|
||||
nego_data: None,
|
||||
flags: nego::RequestFlags::empty(),
|
||||
protocol: selected_protocol,
|
||||
};
|
||||
|
||||
debug!(message = ?connection_request, "Send");
|
||||
|
||||
let written =
|
||||
ironrdp_core::encode_buf(&X224(connection_request), output).map_err(ConnectorError::encode)?;
|
||||
|
||||
break 'state (
|
||||
Written::from_size(written)?,
|
||||
ClientConnectorState::ConnectionInitiationWaitConfirm {
|
||||
requested_protocol: selected_protocol,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let mut security_protocol = nego::SecurityProtocol::empty();
|
||||
|
||||
if self.config.enable_tls {
|
||||
|
@ -240,23 +319,6 @@ impl Sequence for ClientConnector {
|
|||
return Err(reason_err!("Initiation", "standard RDP security is not supported",));
|
||||
}
|
||||
|
||||
// If there's pcb, we send it in the first message.
|
||||
if let Some(pcb) = &self.config.pcb {
|
||||
let pcb = ironrdp_pdu::pcb::PreconnectionBlob {
|
||||
version: ironrdp_pdu::pcb::PcbVersion::V2,
|
||||
id: 0,
|
||||
v2_payload: Some(pcb.to_owned()),
|
||||
};
|
||||
let written = ironrdp_core::encode_buf(&pcb, output).map_err(ConnectorError::encode)?;
|
||||
|
||||
break 'state (
|
||||
Written::from_size(written)?,
|
||||
ClientConnectorState::EnhancedSecurityUpgrade {
|
||||
selected_protocol: security_protocol,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let connection_request = nego::ConnectionRequest {
|
||||
nego_data: self.config.request_data.clone().or_else(|| {
|
||||
self.config
|
||||
|
@ -306,7 +368,10 @@ impl Sequence for ClientConnector {
|
|||
|
||||
(
|
||||
Written::Nothing,
|
||||
ClientConnectorState::EnhancedSecurityUpgrade { selected_protocol },
|
||||
match self.config.vmconnect.is_some() {
|
||||
false => ClientConnectorState::EnhancedSecurityUpgrade { selected_protocol },
|
||||
true => ClientConnectorState::BasicSettingsExchangeSendInitial { selected_protocol },
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -328,10 +393,20 @@ impl Sequence for ClientConnector {
|
|||
}
|
||||
|
||||
//== CredSSP ==//
|
||||
ClientConnectorState::Credssp { selected_protocol } => (
|
||||
ClientConnectorState::Credssp { selected_protocol } => 'state: {
|
||||
if self.config.vmconnect.is_some() {
|
||||
break 'state (
|
||||
Written::Nothing,
|
||||
ClientConnectorState::ConnectionInitiationSendRequest {
|
||||
selected_protocol: Some(selected_protocol),
|
||||
},
|
||||
);
|
||||
}
|
||||
(
|
||||
Written::Nothing,
|
||||
ClientConnectorState::BasicSettingsExchangeSendInitial { selected_protocol },
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
//== Basic Settings Exchange ==//
|
||||
// Exchange basic settings including Core Data, Security Data and Network Data.
|
||||
|
|
|
@ -70,6 +70,7 @@ pub struct CredsspSequence {
|
|||
client: CredSspClient,
|
||||
state: CredsspState,
|
||||
selected_protocol: nego::SecurityProtocol,
|
||||
vmconnect: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -96,18 +97,17 @@ impl CredsspSequence {
|
|||
server_name: ServerName,
|
||||
server_public_key: Vec<u8>,
|
||||
kerberos_config: Option<KerberosConfig>,
|
||||
vmconnect: bool,
|
||||
) -> ConnectorResult<(Self, credssp::TsRequest)> {
|
||||
let credentials: Option<sspi::Credentials> = match &credentials {
|
||||
let credentials: sspi::Credentials = match &credentials {
|
||||
Credentials::UsernamePassword { username, password } => {
|
||||
let username = Username::new(username, domain).map_err(|e| custom_err!("invalid username", e))?;
|
||||
|
||||
Some(
|
||||
sspi::AuthIdentity {
|
||||
username,
|
||||
password: password.to_owned().into(),
|
||||
}
|
||||
.into(),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
Credentials::SmartCard { pin, config } => match config {
|
||||
Some(config) => {
|
||||
|
@ -128,13 +128,17 @@ impl CredsspSequence {
|
|||
private_key_file_index: None,
|
||||
private_key: Some(key.into()),
|
||||
};
|
||||
Some(sspi::Credentials::SmartCard(Box::new(identity)))
|
||||
sspi::Credentials::SmartCard(Box::new(identity))
|
||||
}
|
||||
None => {
|
||||
return Err(general_err!("smart card configuration missing"));
|
||||
}
|
||||
},
|
||||
Credentials::None => None,
|
||||
Credentials::None => sspi::AuthIdentity {
|
||||
username: Username::new("", None).map_err(|e| custom_err!("invalid username", e))?,
|
||||
password: String::new().into(),
|
||||
}
|
||||
.into(),
|
||||
};
|
||||
|
||||
let server_name = server_name.into_inner();
|
||||
|
@ -151,7 +155,7 @@ impl CredsspSequence {
|
|||
|
||||
let client = CredSspClient::new(
|
||||
server_public_key,
|
||||
credentials,
|
||||
Some(credentials),
|
||||
credssp::CredSspMode::WithCredentials,
|
||||
credssp::ClientMode::Negotiate(sspi::NegotiateConfig {
|
||||
protocol_config: credssp_config,
|
||||
|
@ -166,6 +170,7 @@ impl CredsspSequence {
|
|||
client,
|
||||
state: CredsspState::Ongoing,
|
||||
selected_protocol: protocol,
|
||||
vmconnect,
|
||||
};
|
||||
|
||||
let initial_request = credssp::TsRequest::default();
|
||||
|
@ -213,12 +218,28 @@ impl CredsspSequence {
|
|||
CredsspState::Ongoing => {
|
||||
let (ts_request_from_client, next_state) = match result {
|
||||
ClientState::ReplyNeeded(ts_request) => (ts_request, CredsspState::Ongoing),
|
||||
// ClientState::FinalMessage(ts_request) => (
|
||||
// ts_request,
|
||||
// if self.selected_protocol.contains(nego::SecurityProtocol::HYBRID_EX) {
|
||||
// CredsspState::EarlyUserAuthResult
|
||||
// } else {
|
||||
// CredsspState::Finished
|
||||
// },
|
||||
// ),
|
||||
ClientState::FinalMessage(ts_request) => (
|
||||
ts_request,
|
||||
if self.selected_protocol.contains(nego::SecurityProtocol::HYBRID_EX) {
|
||||
CredsspState::EarlyUserAuthResult
|
||||
} else {
|
||||
CredsspState::Finished
|
||||
// @Irving: I don't understand the security protocol, and how it interfares with the vmconnect
|
||||
// So I'll just proceed to make VM Connect work for now, remind me about this when you see this
|
||||
// comment in Pull Request
|
||||
match (
|
||||
self.selected_protocol.contains(nego::SecurityProtocol::HYBRID_EX),
|
||||
self.vmconnect,
|
||||
) {
|
||||
(true, false) => CredsspState::EarlyUserAuthResult,
|
||||
// (false, false) => CredsspState::Finished,
|
||||
// (true, true) => CredsspState::Finished,
|
||||
// (false, true) => CredsspState::Finished,
|
||||
_ => CredsspState::Finished,
|
||||
},
|
||||
),
|
||||
};
|
||||
|
|
|
@ -195,7 +195,7 @@ pub struct Config {
|
|||
pub pointer_software_rendering: bool,
|
||||
pub performance_flags: PerformanceFlags,
|
||||
|
||||
pub pcb: Option<String>,
|
||||
pub vmconnect: Option<String>,
|
||||
}
|
||||
|
||||
ironrdp_core::assert_impl!(Config: Send, Sync);
|
||||
|
|
|
@ -313,6 +313,9 @@ impl Encode for ExtendedClientOptionalInfo {
|
|||
dst.write_array(reconnect_cookie);
|
||||
}
|
||||
|
||||
dst.write_u16(0); // reserved1
|
||||
dst.write_u16(0); // reserved2
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -336,6 +339,8 @@ impl Encode for ExtendedClientOptionalInfo {
|
|||
size += RECONNECT_COOKIE_LENGTH_SIZE + RECONNECT_COOKIE_LEN;
|
||||
}
|
||||
|
||||
size += 2 * 2; // reserved1 and reserved2
|
||||
|
||||
size
|
||||
}
|
||||
}
|
||||
|
|
|
@ -862,7 +862,7 @@ fn build_config(
|
|||
hardware_id: None,
|
||||
license_cache: None,
|
||||
// TODO: implement this
|
||||
pcb: None,
|
||||
vmconnect: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -201,7 +201,7 @@ pub mod ffi {
|
|||
hardware_id: None,
|
||||
license_cache: None,
|
||||
// TODO: implement this
|
||||
pcb: None,
|
||||
vmconnect: None,
|
||||
};
|
||||
tracing::debug!(config=?inner_config, "Built config");
|
||||
Ok(Box::new(Config(inner_config)))
|
||||
|
|
|
@ -33,7 +33,7 @@ pub mod ffi {
|
|||
.ok_or_else(|| ValueConsumedError::for_item("ClientConnectorState"))?
|
||||
{
|
||||
ironrdp::connector::ClientConnectorState::Consumed => ClientConnectorStateType::Consumed,
|
||||
ironrdp::connector::ClientConnectorState::ConnectionInitiationSendRequest => {
|
||||
ironrdp::connector::ClientConnectorState::ConnectionInitiationSendRequest { .. } => {
|
||||
ClientConnectorStateType::ConnectionInitiationSendRequest
|
||||
}
|
||||
ironrdp::connector::ClientConnectorState::ConnectionInitiationWaitConfirm { .. } => {
|
||||
|
|
|
@ -68,6 +68,8 @@ pub mod ffi {
|
|||
server_name.into(),
|
||||
server_public_key.to_owned(),
|
||||
kerbero_configs.map(|config| config.0.clone()),
|
||||
// @Irving: TODO, enable vmconnect for FFI
|
||||
false,
|
||||
)?;
|
||||
|
||||
Ok(Box::new(CredsspSequenceInitResult {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue