Skip to content

fix(dgw): correct CredSSP credential injection for RDCleanPath#1668

Merged
Benoît Cortier (CBenoit) merged 2 commits intomasterfrom
review/rdcleanpath-credential-injection
Feb 3, 2026
Merged

fix(dgw): correct CredSSP credential injection for RDCleanPath#1668
Benoît Cortier (CBenoit) merged 2 commits intomasterfrom
review/rdcleanpath-credential-injection

Conversation

@CBenoit
Copy link
Copy Markdown
Member

@CBenoit Benoît Cortier (CBenoit) commented Jan 30, 2026

Fixes several issues in the RDCleanPath credential injection flow:

  • Used wrong public key for client-side CredSSP: was using target server's public key instead of gateway's own public key. When performing credential injection, the client performs CredSSP authentication against the gateway.

  • Hardcoded HYBRID_EX security protocol regardless of client support: now properly negotiates based on what the client actually advertises.

  • Global cache for gateway public key only stored one entry: if multiple TLS acceptors with different certificates existed, wrong key could be returned. Now uses per-acceptor caching keyed by config pointer.

  • Missing intercept_connect_confirm call: the Connect Initial PDU wasn't being intercepted to update the server_selected_protocol field, causing protocol mismatch issues.

  • Token mismatch not verified: credential entry token wasn't compared against the received cleanpath PDU token.

  • Redundant authorization: handle_with_credential_injection was re-authorizing the token when process_cleanpath already does it. Now reuses claims from process_cleanpath.

  • Unused _credential_store parameter in process_cleanpath.

  • TLS utilities (extract_tls_server_public_key, GetPeerCert, get_cached_gateway_public_key) moved from rdp_proxy.rs to tls.rs.

  • Unnecessary Sync + 'static bounds on async stream types reduced to just Send.

  • Replaced #[allow(...)] with #[expect(...)] for suppressions.

  • Simplified NetworkClient struct into send_network_request function.

  • Various error handling improvements using .context() instead of anyhow::anyhow!().

  • Updated sspi-rs with important fixes.

Changelog: ignore

Fixes several issues in the RDCleanPath credential injection flow:

- Used wrong public key for client-side CredSSP: was using target
  server's public key instead of gateway's own public key. When
  performing credential injection, the client performs CredSSP
  authentication against the gateway.

- Hardcoded HYBRID_EX security protocol regardless of client support:
  now properly negotiates based on what the client actually advertises.

- Global cache for gateway public key only stored one entry: if multiple
  TLS acceptors with different certificates existed, wrong key could be
  returned. Now uses per-acceptor caching keyed by config pointer.

- Missing `intercept_connect_confirm` call: the Connect Initial PDU
  wasn't being intercepted to update the server_selected_protocol field,
  causing protocol mismatch issues.

- Token mismatch not verified: credential entry token wasn't compared
  against the received cleanpath PDU token.

- Redundant authorization: `handle_with_credential_injection` was
  re-authorizing the token when `process_cleanpath` already does it.
  Now reuses claims from `process_cleanpath`.

- Unused `_credential_store` parameter in `process_cleanpath`.

- TLS utilities (`extract_tls_server_public_key`, `GetPeerCert`,
  `get_cached_gateway_public_key`) moved from rdp_proxy.rs to tls.rs.

- Unnecessary `Sync + 'static` bounds on async stream types reduced to
  just `Send`.

- Replaced `#[allow(...)]` with `#[expect(...)]` for suppressions.

- Simplified `NetworkClient` struct into `send_network_request` function.

- Various error handling improvements using `.context()` instead of
  `anyhow::anyhow!()`.

- Updated sspi-rs with important fixes.

Changelog: ignore
Copy link
Copy Markdown
Contributor

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

This PR fixes several critical issues in the RDCleanPath credential injection flow for CredSSP authentication. The main problem was that the gateway was using the wrong public key (target server's instead of its own) when performing client-side CredSSP, along with other protocol negotiation and caching issues.

Changes:

  • Fixed client-side CredSSP to use gateway's public key instead of target server's public key
  • Implemented proper security protocol negotiation based on client capabilities (HYBRID vs HYBRID_EX)
  • Refactored TLS certificate caching to use per-acceptor storage instead of a global single-entry cache
  • Added missing intercept_connect_confirm call and token mismatch verification
  • Refactored code organization by moving TLS utilities to tls.rs and simplifying network client handling
  • Updated sspi-rs dependency from 0.18.5 to 0.18.7 with important fixes

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.

File Description
devolutions-gateway/src/tls.rs Added per-acceptor certificate chain caching function and utility traits for extracting public keys from TLS streams
devolutions-gateway/src/rdp_proxy.rs Removed TLS utilities (moved to tls.rs), simplified NetworkClient to a function, removed unnecessary trait bounds, updated to use new caching
devolutions-gateway/src/rd_clean_path.rs Fixed credential injection to use gateway cert chain, added proper protocol negotiation, token verification, and intercept_connect_confirm call
Cargo.lock Updated sspi-rs to 0.18.7 with related transitive dependency changes

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread devolutions-gateway/src/rd_clean_path.rs Outdated
Comment thread Cargo.lock
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

disabled auto-merge in order to fix copilot comment

@CBenoit Benoît Cortier (CBenoit) merged commit 178ae36 into master Feb 3, 2026
40 checks passed
@CBenoit Benoît Cortier (CBenoit) deleted the review/rdcleanpath-credential-injection branch February 3, 2026 16:42
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