Skip to content

feat(connector): add allow_encryption_level_none config option#1214

Open
Dietmar Maurer (maurerdietmar) wants to merge 1 commit intoDevolutions:masterfrom
maurerdietmar:allow_encryption_level_none
Open

feat(connector): add allow_encryption_level_none config option#1214
Dietmar Maurer (maurerdietmar) wants to merge 1 commit intoDevolutions:masterfrom
maurerdietmar:allow_encryption_level_none

Conversation

@maurerdietmar
Copy link
Copy Markdown

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).

Copy link
Copy Markdown
Member

@CBenoit Benoît Cortier (CBenoit) left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking good to me! Thank you!

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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: bool on ironrdp_connector::Config (defaulted to false across 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.

Comment on lines +313 to +321
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) {
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
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) {

Copilot uses AI. Check for mistakes.
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",));
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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)",
));

Copilot uses AI. Check for mistakes.
Comment on lines +207 to +211
///
/// 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,
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
@@ -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),
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested 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.

Copilot uses AI. Check for mistakes.
Comment on lines 342 to 346
desktop_scale_factor: 0, // Default to 0 per FreeRDP
enable_tls: true,
enable_credssp: true,
allow_encryption_level_none: false,
credentials: connector::Credentials::UsernamePassword {
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
@CBenoit
Copy link
Copy Markdown
Member

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>
@maurerdietmar
Copy link
Copy Markdown
Author

just fixed the compilation error.

@CBenoit Benoît Cortier (CBenoit) enabled auto-merge (squash) April 10, 2026 10:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants