Skip to content

build: upgrade sspi to 0.19, picky to rc.22, fix NTLM fallback#1188

Open
Greg Lamberson (glamberson) wants to merge 2 commits intoDevolutions:masterfrom
lamco-admin:feat/sspi-0.19-upgrade
Open

build: upgrade sspi to 0.19, picky to rc.22, fix NTLM fallback#1188
Greg Lamberson (glamberson) wants to merge 2 commits intoDevolutions:masterfrom
lamco-admin:feat/sspi-0.19-upgrade

Conversation

@glamberson
Copy link
Copy Markdown
Contributor

Summary

Upgrade sspi from 0.18 to 0.19 and picky from rc.20 to rc.22, resolving the dependency conflicts reported in #1186 and the NTLM/SPNEGO compatibility issue in #1143.

Closes #1186. Supersedes #1143.

Credit

This PR synthesizes the investigations by Guillaume Gelin (@ramnes) (#1143) and sfwwslm (@sfwwslm) (#1186). Guillaume Gelin (@ramnes) identified the NTLM fallback fix and first attempted the sspi upgrade in March. sfwwslm (@sfwwslm) independently found the KerberosConfig conversion fix, the reqwest.rs re-export fix, and documented the remaining rand_core workspace conflict. The uuid pin workaround and auth_data trait implementation are the remaining pieces needed for a complete workspace build.

Test plan

  • All 5 xtask checks pass locally (fmt, lints, tests, typos, locks)
  • Full workspace builds including FFI crate
  • Verify CredSSP connection with mstsc (NTLM, no Kerberos)
  • Verify CredSSP connection with FreeRDP (NTLM, no Kerberos)

Upgrade sspi from 0.18 to 0.19 and picky from rc.20 to rc.22.

sspi 0.19 introduces a proper SPNEGO implementation that expects
SPNEGO-wrapped tokens, but mstsc and FreeRDP send raw NTLM tokens
in CredSSP. Use ServerMode::Ntlm directly when Kerberos is disabled
to maintain compatibility.

Code changes:
- acceptor: use ServerMode::Ntlm(NtlmConfig) when no Kerberos config
  instead of ServerMode::Negotiate with NtlmConfig as ProtocolConfig
- acceptor: implement CredentialsProxy::auth_data (new trait method)
- connector: KerberosConfig.hostname conversion needs unwrap_or_default
  (sspi 0.19 changed client_computer_name from Option<String> to String)
- tokio/reqwest: use sspi types through ironrdp-connector re-export to
  avoid version mismatch between direct and transitive sspi deps
- ffi: bump sspi dep to 0.19

Dependency workaround:
- Pin uuid to <1.21 in ironrdp-client and ironrdp-mstsgu. uuid 1.21+
  pulls getrandom 0.4 which needs stable rand_core 0.10.0, conflicting
  with picky rc.22's pinned rand_core = "=0.10.0-rc-3". Remove this pin
  when picky ships with stable RustCrypto deps.

Based on investigation by @ramnes (PR Devolutions#1143) and @sfwwslm (issue Devolutions#1186).
sspi::KerberosConfig {
kdc_url: val.kdc_proxy_url,
client_computer_name: val.hostname,
client_computer_name: val.hostname.unwrap_or_default(),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

question: Is the empty string a correct value at this place?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good question. I traced the code path through sspi and the answer is no, empty string is not correct here, but the fix belongs in sspi rather than in this conversion.

What happens with "": In sspi 0.19, client_computer_name flows unconditionally into generate_as_req_kdc_body() (generators.rs:272-274), which always constructs a HostAddress with addr_type = NET_BIOS_ADDR_TYPE and address = hostname.as_bytes(). There is no empty check. An empty string produces a 0-byte NetBIOS address in the AS-REQ addresses field, which is malformed per RFC 4120 section 5.2.5 (NetBIOS addresses should be 16 octets). It also ends up in EncKrbPrivPart.s_address for password change flows.

What sspi 0.18 did: The field was Option<String>, and None was guarded by unwrap_hostname() which returned Err("the hostname is not provided"), a clear, fast failure. sspi 0.19 made the field non-optional but removed that guard without adding empty-string validation, so the safety net is gone.

Why unwrap_or_default() here: IronRDP's KerberosConfig.hostname is Option<String> and sspi 0.19's client_computer_name is now String. The From impl needs to bridge the types. unwrap_or_default() is the minimal change that compiles, but it silently degrades from "clear error" to "malformed protocol message."

Practical scope: This conversion only runs when Kerberos is explicitly configured. Users who configure Kerberos but omit the hostname would have gotten an error with sspi 0.18 and now get silent protocol violations with 0.19. Most IronRDP users use NTLM (no Kerberos config), so the blast radius is small.

Since Pavlo Myroniuk (@TheBestTvarynka) is already tagged, I think the right fix is in sspi: either validate/reject empty client_computer_name, or skip the addresses field in AS-REQ when it is empty (the field is OPTIONAL per RFC 4120). Happy to adjust the IronRDP side to whatever approach the sspi team prefers: making hostname non-optional in KerberosConfig, using TryFrom instead of From, etc.

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

Updates the workspace’s authentication stack to sspi 0.19 and picky 7.0.0-rc.22, addressing build/dependency conflicts and improving CredSSP interoperability when Kerberos is disabled.

Changes:

  • Upgrade sspi (0.18 → 0.19) and picky (rc.20 → rc.22), plus associated lockfile updates.
  • Fix CredSSP server-side NTLM fallback by using ServerMode::Ntlm directly when Kerberos isn’t configured.
  • Align ironrdp-tokio to use ironrdp-connector::sspi re-exports and add WASM getrandom 0.4 feature enablement for picky’s transitive deps; pin uuid to <1.21 to avoid rand_core conflicts.

Reviewed changes

Copilot reviewed 11 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
ffi/Cargo.toml Bumps sspi dependency to 0.19 for FFI build.
crates/ironrdp/Cargo.toml Bumps sspi dependency to 0.19 for the CLI/tooling crate.
crates/ironrdp-web/src/lib.rs Marks getrandom4 as used to satisfy unused_crate_dependencies while enabling WASM features.
crates/ironrdp-web/Cargo.toml Adds getrandom 0.4.0-rc.0 (as getrandom4) with WASM features to support picky rc.22 transitive deps.
crates/ironrdp-tokio/src/reqwest.rs Switches to ironrdp_connector::sspi types to avoid sspi version mismatches.
crates/ironrdp-tokio/Cargo.toml Removes direct sspi dependency from the reqwest feature path; relies on connector re-export.
crates/ironrdp-mstsgu/Cargo.toml Pins uuid <1.21 to avoid rand_core conflict stemming from picky rc.22.
crates/ironrdp-connector/src/credssp.rs Adapts KerberosConfig conversion for sspi 0.19 API change (client_computer_name now String).
crates/ironrdp-connector/Cargo.toml Upgrades sspi to 0.19 and picky to =7.0.0-rc.22.
crates/ironrdp-client/Cargo.toml Pins uuid <1.21 to match workspace workaround.
crates/ironrdp-acceptor/src/credssp.rs Implements new CredentialsProxy::auth_data and uses ServerMode::Ntlm when Kerberos is disabled.
Cargo.lock Lockfile refresh for upgraded dependencies and conflict resolution.

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

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.

Help needed: newer picky/sspi versions unblock downstream integration, but IronRDP workspace still hits a dependency conflict

4 participants