Skip to content

feat (4/5): spec-correct WinRM HTTP/HTTPS transport + connection-oriented auth#23

Merged
irvingouj@Devolutions (irvingoujAtDevolution) merged 3 commits into
stack/03-writeprogress-nilfrom
stack/04-winrm-transport-auth
Jun 23, 2026
Merged

feat (4/5): spec-correct WinRM HTTP/HTTPS transport + connection-oriented auth#23
irvingouj@Devolutions (irvingoujAtDevolution) merged 3 commits into
stack/03-writeprogress-nilfrom
stack/04-winrm-transport-auth

Conversation

@irvingoujAtDevolution

Copy link
Copy Markdown
Collaborator

Stack 4 of 5 — base: stack/03-writeprogress-nil (stacked on #22).

Makes the WinRM transport/auth spec-correct:

  • HTTP uses SSPI message sealing (application/HTTP-SPNEGO-session-encrypted).
  • HTTPS is unsealed (TLS provides confidentiality): plain application/soap+xml
    with connection-oriented auth (RFC 4559) — the connection is authenticated once
    during the handshake (first op rides the SPNEGO legs), then reused for every op.
  • SSPI INTEGRITY (sign) is requested unconditionally; CONFIDENTIALITY (seal)
    only when sealing — this is what lets the server trust the unsealed HTTPS connection
    (and yields the NTLM MIC / SPNEGO mechListMIC).
  • EPA channel binding (tls-server-end-point) learned from the server cert.
  • AlreadyComplete handled consistently across all four transports (tokio/gateway/sync/web).
  • Fail fast on terminal 401 (PwshCoreError::Auth), try_join! so a failed handshake
    short-circuits, and the non-interactive client exits non-zero on failure / clean on
    success. Basic over plain HTTP refused unless --http-insecure.

Verified end-to-end against a real DC.

…ed auth

Over plain HTTP, SSPI message sealing is used (application/HTTP-SPNEGO-session-
encrypted). Over HTTPS, TLS provides confidentiality so the body is sent plain
(application/soap+xml) and authentication is connection-oriented (RFC 4559): the
connection is authenticated once during the handshake (the first operation rides
the SPNEGO challenge legs) and every subsequent operation reuses that authenticated
connection. requires_sspi_sealing() is therefore true only for plain HTTP.

Key points:
- SSPI INTEGRITY (sign) is requested unconditionally; CONFIDENTIALITY (seal) only
  when sealing. INTEGRITY over HTTPS lets the server trust the connection and
  produces the NTLM MIC / SPNEGO mechListMIC.
- EPA channel binding (tls-server-end-point): learned from the server cert after a
  401 and applied on a restarted auth sequence.
- AlreadyComplete is handled consistently across all four transports (tokio direct,
  gateway, sync, web).
- Fail fast on terminal auth rejection (401 -> PwshCoreError::Auth), try_join! so a
  failed handshake short-circuits, and the non-interactive client exits non-zero on
  failure / clean on success (command_completed is authoritative).
- Basic over plain HTTP is refused unless --http-insecure is given.
…lding

Add the 10-case transport_auth_matrix asserting the WinRM rule against a real
server: HTTP+SSPI seals; HTTPS+SSPI is unsealed (TLS); Basic refused over plain
HTTP unless forced, allowed (unsealed) over HTTPS. Helpers: sealed() detects the
multipart/encrypted envelope, connected() detects pipeline completion.

Also makes the shared e2e scaffolding auth-aware: e2e_pwsh_config gains
default_auth_method(); native_pty_matrix and pty_harness use it (the default DC
refuses Basic over HTTP), and fall back to the standard e2e host/creds out of the box.
test (5/5): transport × auth × sealing e2e matrix
@irvingoujAtDevolution irvingouj@Devolutions (irvingoujAtDevolution) merged commit f3b1f6f into stack/03-writeprogress-nil Jun 23, 2026
@irvingoujAtDevolution irvingouj@Devolutions (irvingoujAtDevolution) deleted the stack/04-winrm-transport-auth branch June 23, 2026 16:54
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.

1 participant