feat(connector): add allow_encryption_level_none config option#1214
feat(connector): add allow_encryption_level_none config option#1214Dietmar Maurer (maurerdietmar) wants to merge 1 commit intoDevolutions:masterfrom
allow_encryption_level_none config option#1214Conversation
Benoît Cortier (CBenoit)
left a comment
There was a problem hiding this comment.
This is looking good to me! Thank you!
There was a problem hiding this comment.
Pull request overview
Adds an explicit connector configuration flag to permit a no-TLS / no-CredSSP / no RDP-layer encryption connection when the server negotiates PROTOCOL_RDP with ENCRYPTION_LEVEL_NONE / ENCRYPTION_METHOD_NONE, intended for deployments where the transport is secured externally.
Changes:
- Introduces
allow_encryption_level_none: boolonironrdp_connector::Config(defaulted tofalseacross call sites). - Updates the connector negotiation state machine to allow Standard RDP Security only when explicitly enabled and to skip the enhanced security upgrade in that case.
- Propagates the new field to various config construction sites (FFI builder, CLI config, WASM session config, examples, tests).
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
crates/ironrdp-connector/src/lib.rs |
Adds the new config flag with security-focused documentation. |
crates/ironrdp-connector/src/connection.rs |
Changes negotiation flow to permit/short-circuit Standard RDP Security when allowed. |
ffi/src/connector/config.rs |
Updates FFI config build to populate the new field (currently hardcoded false). |
crates/ironrdp-client/src/config.rs |
Updates CLI config construction to include the new field (default false). |
crates/ironrdp-web/src/session.rs |
Updates WASM session config construction to include the new field (default false). |
crates/ironrdp/examples/screenshot.rs |
Updates example config construction to include the new field (default false). |
crates/ironrdp-testsuite-extra/tests/mod.rs |
Updates test default config construction to include the new field (default false). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if selected_protocol.is_standard_rdp_security() && requested_protocol.is_standard_rdp_security() { | ||
| // Both sides agreed on standard RDP security — skip the | ||
| // enhanced security upgrade and go straight to basic | ||
| // settings exchange. | ||
| ( | ||
| Written::Nothing, | ||
| ClientConnectorState::BasicSettingsExchangeSendInitial { selected_protocol }, | ||
| ) | ||
| } else if !selected_protocol.intersects(requested_protocol) { |
There was a problem hiding this comment.
The new Standard RDP Security path skips EnhancedSecurityUpgrade, but ironrdp_async::connect_begin currently loops until should_perform_security_upgrade() becomes true (see crates/ironrdp-async/src/connector.rs:26-30). With this branch, connect_begin will never return for PROTOCOL_RDP/no-security connections, and connect_finalize can’t be called because it requires an Upgraded token. Consider updating the async/IO wrappers to support a “no-upgrade required” path (e.g., return an enum from connect_begin, and allow connect_finalize to accept either upgraded or plain streams).
| if selected_protocol.is_standard_rdp_security() && requested_protocol.is_standard_rdp_security() { | |
| // Both sides agreed on standard RDP security — skip the | |
| // enhanced security upgrade and go straight to basic | |
| // settings exchange. | |
| ( | |
| Written::Nothing, | |
| ClientConnectorState::BasicSettingsExchangeSendInitial { selected_protocol }, | |
| ) | |
| } else if !selected_protocol.intersects(requested_protocol) { | |
| if !selected_protocol.intersects(requested_protocol) { |
| if security_protocol.is_standard_rdp_security() { | ||
| return Err(reason_err!("Initiation", "standard RDP security is not supported",)); | ||
| if security_protocol.is_standard_rdp_security() && !self.config.allow_encryption_level_none { | ||
| return Err(reason_err!("Initiation", "ENCRYPTION_LEVEL_NONE is not allowed",)); |
There was a problem hiding this comment.
This error is triggered when the client is negotiating Standard RDP Security (protocol field is empty), but at this stage we don’t yet know whether the server will select ENCRYPTION_LEVEL_NONE vs requiring RC4. The message “ENCRYPTION_LEVEL_NONE is not allowed” is misleading for users who simply disabled TLS/NLA. Consider rewording to something like “Standard RDP security without TLS/NLA is disabled by default; set allow_encryption_level_none to enable (plaintext)” and keep the explicit security warning context.
| return Err(reason_err!("Initiation", "ENCRYPTION_LEVEL_NONE is not allowed",)); | |
| return Err(reason_err!( | |
| "Initiation", | |
| "Standard RDP security without TLS/NLA is disabled by default; set allow_encryption_level_none to enable (plaintext)", | |
| )); |
| /// | ||
| /// Do **not** enable this unless the underlying transport is already | ||
| /// encrypted. Without TLS, all RDP traffic (including credentials) is | ||
| /// sent in plaintext. | ||
| pub allow_encryption_level_none: bool, |
There was a problem hiding this comment.
Config is a public struct with public fields; adding a new public field is a semver-breaking change for downstream users who construct Config via struct literals. If this crate aims to preserve semver compatibility, consider making Config #[non_exhaustive] and/or providing a builder/constructor so new options can be added without breaking consumers (or ensure the next release version reflects the breaking change).
| @@ -176,6 +176,7 @@ pub mod ffi { | |||
| domain: self.domain.clone(), | |||
| enable_tls: self.enable_tls.unwrap_or(false), | |||
| enable_credssp: self.enable_credssp.unwrap_or(true), | |||
There was a problem hiding this comment.
allow_encryption_level_none is hardcoded to false in the FFI builder, and there’s no corresponding ConfigBuilder field/setter. If this option is meant to be available to FFI consumers, it should be plumbed through as an Option<bool> with a setter similar to set_enable_tls / set_enable_credssp. If it’s intentionally not exposed for safety, consider documenting that decision in the FFI API (otherwise it looks like an incomplete wiring change).
| enable_credssp: self.enable_credssp.unwrap_or(true), | |
| enable_credssp: self.enable_credssp.unwrap_or(true), | |
| // Intentionally not exposed through the FFI builder: allowing | |
| // encryption level "none" weakens transport security, so FFI | |
| // consumers are always pinned to the safe default here. |
| desktop_scale_factor: 0, // Default to 0 per FreeRDP | ||
| enable_tls: true, | ||
| enable_credssp: true, | ||
| allow_encryption_level_none: false, | ||
| credentials: connector::Credentials::UsernamePassword { |
There was a problem hiding this comment.
This PR introduces a new connector behavior (allowing PROTOCOL_RDP with no security), but the testsuite currently only exercises TLS-secured connections (RdpServer::builder().with_tls(...)). Consider adding coverage here for both cases: (1) default config rejects no-security negotiation, and (2) when allow_encryption_level_none = true + TLS/NLA disabled, the client can connect to a server built with with_no_security() and complete the handshake.
|
There is a compilation error at crates/ironrdp-testsuite-core/tests/session/autodetect.rs:23:5, you need to add the new field to the struct initialization |
Add a new `Config` flag that permits the connector to accept `ENCRYPTION_LEVEL_NONE` / `ENCRYPTION_METHOD_NONE` from the server. This does not imply full Standard RDP Security (RC4) support; it only allows the degenerate case where the server selects `PROTOCOL_RDP` with no encryption at all. When enabled, the connector skips the TLS/CredSSP upgrade and proceeds directly to basic settings exchange. This is intended for setups where the transport is already secured by other means (e.g., a TLS WebSocket proxy or SSH tunnel). Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
7330926 to
f8fb2dd
Compare
|
just fixed the compilation error. |
Add a new
Configflag that permits the connector to acceptENCRYPTION_LEVEL_NONE/ENCRYPTION_METHOD_NONEfrom the server. This does not imply full Standard RDP Security (RC4) support; it only allows the degenerate case where the server selectsPROTOCOL_RDPwith no encryption at all. When enabled, the connector skips the TLS/CredSSP upgrade and proceeds directly to basic settings exchange.This is intended for setups where the transport is already secured by other means (e.g., a TLS WebSocket proxy or SSH tunnel).