Tags: Devolutions/sspi-rs
Tags
fix(dns): avoid panic on short SRV record RDATA on macOS (#706) We've received crash reports from a client application on macOS showing > thread " (46265) panicked at src/dns.rs:172:56: range end index 2 out of range for slice of length O note: run with 'RUST_BACKTRACE=1' environment variable to display a backtrace It's the first we've seen of it, but this client reports that it crashes _consistently_ in his environment. The cited line lands exactly on `rdata[0..2]` inside the SRV-record parser. Presumably the `rdata` buffer was empty so slicing it caused a panic. But why is the record empty? I'm not sure. Every callback from the DNS query returns a `DnsSrvRecord `, where the RDATA is at least 6 bytes (priority + weight + port, then the target name). The code assumes a well formed SRV record. We see some (potential) defects in the code: 1. The parser is unguarded and trusts `rdata` to always be at least 6 bytes 2. I believe (but am not sure) that the normal "this realm has no SRV record" outcome is **not** an empty callback, but a timeout. mDNSResponder doesn't call back in time, our timeout fires and we return an empty list. I guess in this case _something_ causes the SRV query to be answered immediately, with a negative (empty) response. I'm equally not sure what that could be: VPN resolver, enterprise DNS, a local stub resolver.... Not sure. The (blind) fix: In `src/dns.rs`, in the macOS/iOS path: - From → TryFrom, returning `Err` when `rdata.len() < 6` instead of slicing - The query loop now filters on the `ADD` flag (the correct signal for "this is a real, added record"), skipping negative responses and removals rather than parsing them. - Both skip cases are logged (`debug!`/`warn!`) with the record's `rr_type`, `rr_class`, and `rdata_len`, so if a resolver sends something unexpected again, we can see exactly what it returned instead of guessing. For a realm with no SRV records, the empty/negative answer is ignored, we now fall through to the UDP query and then return an empty host list.
feat: support for Kerberos cross realm referral (#694) RDP/NLA via FreeRDP and sspi-rs failed when a parent-domain user (richard@rjm.local) authenticated to a host in a child domain (TERMSRV/WIN-UE7FOENEK0D.dev.rjm.local) with > InvalidToken: Asn1 error: "Expected Application number tag 15 but got: 30" (Kerberos app tags: 15 = `AP-REP`, 30 = `KRB-ERROR` — i.e. we expected an `AP-REP` from the server but got a `KRB-ERROR`.) ### Root cause sspi-rs has no cross-realm referral chasing. A KDC only issues tickets for principals in its own realm; for a service in another realm it returns a referral `TGT` (sname = krbtgt/<NEXT_REALM>), and the client must re-send the `TGS-REQ` to that next realm. Instead, sspi-rs: 1. Sent the `TGS-REQ` to the home KDC (RJM.LOCAL) → got back a referral `TGT` for krbtgt/DEV.RJM.LOCAL. 2. Stuffed that referral `TGT` directly into the `AP_REQ` as if it were the service ticket. 3. The target (WIN-UE7FOENEK0D) couldn't decrypt a ticket encrypted with the trust key; returned `KRB-ERROR` 41 (`KRB_AP_ERR_MODIFIED`). 4. sspi-rs tried to parse that `KRB-ERROR` as an `AP-REP` ---> `InvalidToken`. ### What we changed Referral chasing loop in the `TGS` exchange (src/kerberos/client/mod.rs): after each `TGS-REP`, if the returned ticket sname is krbtgt/<NEXT_REALM>, re-issue the `TGS-REQ` for the same SPN to that realm using the referral `TGT`, chaining the session key and authenticator each hop, until the real service ticket comes back. Bounded to 10 hops with a no-progress guard. Per-realm KDC routing (src/kerberos/mod.rs): split send into `send`/`send_to` and added `send_for_realm`. The pinned kdc_url (KDC proxy) stays authoritative for the home realm only; referral hops resolve through `detect_kdc_url(realm)`. Helper and tests: extracted` referral_target_realm(sname)` (testable predicate) with 5 unit tests. ### Key decisions Reuse the existing per-realm resolution chain rather than add new config. Referral hops route via the existing `SSPI_KDC_URL_<REALM>` env → krb5.conf → DNS SRV (_kerberos._tcp.<realm>) fallback. No new FFI/config plumbing; set one env var (or rely on DNS) and it works. Pinned KDC = home realm only. A KDC proxy pinned to the home DC can't decrypt a krbtgt/<NEXT_REALM> referral ticket, so referral hops must reach the target realm's KDC. ### For further discussion (Because I'm trying to keep these changes focussed) Windows DNS SRV limitations ([src/dns.rs](vscode-webview://0o7221v7omgideiac71911ugm38hnasf8rt28rpk2ejj6sje9p7m/src/dns.rs)): the Windows path forces port :88 (ignores the SRV port) and reads only the first SRV record (no multi-DC failover). Dotted env-var friction: SSPI_KDC_URL_DEV.RJM.LOCAL breaks PowerShell $env: parsing. Add underscore-normalized aliasing (SSPI_KDC_URL_DEV_RJM_LOCAL)? U2U + referral interaction: `additional_tickets` is only carried on the first hop; edge case, likely fine, but worth a sanity check. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PreviousNext