build: upgrade sspi to 0.19, picky to rc.22, fix NTLM fallback#1188
build: upgrade sspi to 0.19, picky to rc.22, fix NTLM fallback#1188Greg Lamberson (glamberson) wants to merge 2 commits intoDevolutions:masterfrom
Conversation
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).
5b2ab75 to
f754aa9
Compare
| sspi::KerberosConfig { | ||
| kdc_url: val.kdc_proxy_url, | ||
| client_computer_name: val.hostname, | ||
| client_computer_name: val.hostname.unwrap_or_default(), |
There was a problem hiding this comment.
question: Is the empty string a correct value at this place?
There was a problem hiding this comment.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) andpicky(rc.20 → rc.22), plus associated lockfile updates. - Fix CredSSP server-side NTLM fallback by using
ServerMode::Ntlmdirectly when Kerberos isn’t configured. - Align
ironrdp-tokioto useironrdp-connector::sspire-exports and add WASMgetrandom 0.4feature enablement for picky’s transitive deps; pinuuidto<1.21to avoidrand_coreconflicts.
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.
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.
ServerMode::Ntlmdirectly when Kerberos is disabled, since sspi 0.19's SPNEGO implementation expects SPNEGO-wrapped tokens but mstsc/FreeRDP send raw NTLM tokens in CredSSP (Guillaume Gelin (@ramnes) identified this in fix(acceptor): upgrade sspi, use NTLM when no Kerberos #1143, confirmed by Pavlo Myroniuk (@TheBestTvarynka))CredentialsProxy::auth_datatrait method added in sspi 0.19KerberosConfigconversion (hostnamechanged fromOption<String>toStringin sspi 0.19, identified by sfwwslm (@sfwwslm) in Help needed: newer picky/sspi versions unblock downstream integration, but IronRDP workspace still hits a dependency conflict #1186)ironrdp-connector::sspire-export inironrdp-tokioand remove the direct sspi dependency to avoid version mismatch (also identified by sfwwslm (@sfwwslm))picky rc.22's exact-pinnedrand_core = "=0.10.0-rc-3"conflicting with uuid 1.21+'sgetrandom 0.4-> stablerand_core 0.10.0(documented by sfwwslm (@sfwwslm) in Help needed: newer picky/sspi versions unblock downstream integration, but IronRDP workspace still hits a dependency conflict #1186). This pin can be removed when picky ships with stable RustCrypto deps.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
KerberosConfigconversion fix, the reqwest.rs re-export fix, and documented the remainingrand_coreworkspace conflict. The uuid pin workaround andauth_datatrait implementation are the remaining pieces needed for a complete workspace build.Test plan