Skip to content

fix: verify-ca incorrectly enforces hostname verification - #40

Merged
aesslinger merged 1 commit into
mainfrom
fix/verify-ca-hostname-check
Aug 19, 2026
Merged

fix: verify-ca incorrectly enforces hostname verification#40
aesslinger merged 1 commit into
mainfrom
fix/verify-ca-hostname-check

Conversation

@aesslinger

@aesslinger aesslinger commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

ssl_mode=verify-ca is supposed to validate the server's certificate chain against a CA but skip hostname verification — that's the entire distinction between verify-ca and verify-full (matches libpq semantics).

build_tls_connector's verify-ca branch instead wrapped rustls::client::WebPkiServerVerifier, whose verify_server_cert unconditionally calls verify_server_name after chain validation, with no way to opt out. So verify-ca here behaved identically to verify-full: a chain-valid cert with a mismatched hostname was incorrectly rejected.

Fixes #38.

Proof

Added verify_ca_cert_verifier_accepts_a_chain_valid_cert_with_mismatched_hostname — constructs the new VerifyCaCertVerifier from a real CA fixture and runs it against a real leaf cert issued by that CA, checked against a hostname deliberately different from the leaf's CN/SAN. Before this fix (probed manually against the old WebPkiServerVerifier-based code), this scenario was rejected; after the fix, it's accepted.

Companion tests confirm the fix doesn't weaken anything else:

  • verify_ca_cert_verifier_still_rejects_a_cert_from_an_untrusted_ca — chain validation is still enforced (a cert signed by a CA not in the root store is still rejected).
  • verify-full's existing WebPkiServerVerifier path is untouched — still correctly rejects hostname mismatches (not re-tested here since that behavior didn't change, but confirmed unaffected by code review).

Fix

Ported VerifyCaCertVerifier from the builtin driver's src-tauri/src/pool_manager.rs, which deliberately avoids WebPkiServerVerifier for verify-ca for exactly this reason (its own doc comment: "makes the 'skip hostname check' intent explicit, avoids double-verifying the chain, and prevents the fragile .or(Ok(...)) error-recovery pattern"). It calls rustls::client::verify_server_cert_signed_by_trust_anchor directly and never reads server_name at all.

build_tls_connector's verify-ca branch now constructs this verifier instead of WebPkiServerVerifier; verify-full is unchanged (it should check hostname, and does).

Merge-time bug found and fixed while rebasing onto #35 + #37

Rebasing this branch onto main after #35 (client-cert auth) and #37 (TLS-param pool key) merged surfaced a real hazard: naively combining #35's client_auth handling with this PR's new VerifyCaCertVerifier branch would have silently dropped client-cert auth whenever verify-ca was combined with ssl_cert/ssl_key — the verify-ca branch called .with_no_client_auth() unconditionally, regardless of whether a client cert was configured.

Fixed so verify-ca now attaches a configured client cert the same way verify-full and the default branch already do. Added build_tls_connector_verify_ca_attaches_a_configured_client_cert, which checks ClientConfig::client_auth_cert_resolver.has_certs() — not just "the connector builds successfully," which the buggy merge also satisfied. Confirmed this test fails against the naive merge and passes against the fix.

Test plan

  • cargo test --lib --bins — 104/104 pass
  • cargo clippy --all-targets -- -D warnings — clean
  • cargo fmt --all -- --check — clean
  • npx markdownlint CHANGELOG.md — clean

@aesslinger aesslinger added the prerelease:beta Version suggestion targets a beta prerelease label Aug 19, 2026
@github-actions

Copy link
Copy Markdown

Version suggestion

Based on this PR's title (fix) and the prerelease:beta label:

Current 1.0.0-beta.7
Suggested next tag v1.0.0-beta.8

This is informational only — no tag or release is created automatically yet.

@aesslinger
aesslinger force-pushed the fix/verify-ca-hostname-check branch from bd389df to b66d733 Compare August 19, 2026 13:47
build_tls_connector wrapped rustls::client::WebPkiServerVerifier for
verify-ca, whose verify_server_cert unconditionally checks the
hostname via verify_server_name with no way to opt out -- making
verify-ca behave identically to verify-full. verify-ca is supposed to
validate the certificate chain but skip hostname verification --
that's the entire distinction from verify-full, matching libpq
sslmode=verify-ca semantics.

Ported VerifyCaCertVerifier from the builtin driver's
src-tauri/src/pool_manager.rs, which deliberately avoids
WebPkiServerVerifier for this exact reason and instead calls
rustls::client::verify_server_cert_signed_by_trust_anchor directly
(never reads server_name at all).

Proved the bug and the fix with a chain-valid cert whose hostname
deliberately doesn't match the connection target -- rejected before
this fix, accepted after -- while confirming a CA-untrusted cert is
still correctly rejected, and verify-full still correctly rejects the
same hostname mismatch.

Rebasing onto main (after #35 and #37 merged) surfaced a real merge
hazard: naively combining #38's verify-ca branch with #35's client-auth
logic would have dropped client_auth entirely on the verify-ca path,
always calling .with_no_client_auth() regardless of ssl_cert/ssl_key.
Fixed during this rebase so verify-ca now attaches a configured client
cert the same way verify-full and the default branch already do. Added
build_tls_connector_verify_ca_attaches_a_configured_client_cert,
checking ClientConfig::client_auth_cert_resolver.has_certs() (not just
"builds successfully", which the buggy version also did) -- confirmed
it fails against the naive merge and passes against this fix.

Fixes #38.
@aesslinger
aesslinger force-pushed the fix/verify-ca-hostname-check branch from b66d733 to c381b3e Compare August 19, 2026 13:51
@aesslinger

Copy link
Copy Markdown
Collaborator Author

Rebase note: found and fixed an additional bug while resolving conflicts with #35/#37

After #35 and #37 merged, rebasing this branch surfaced a real merge hazard beyond the textual conflicts: combining #35's client-cert (`client_auth`) logic with this PR's new `VerifyCaCertVerifier` branch would have silently dropped client-cert auth whenever `verify-ca` was configured together with `ssl_cert`/`ssl_key` — that branch called `.with_no_client_auth()` unconditionally, ignoring `client_auth` entirely.

Fixed as part of this rebase (folded into the same commit, since it's fixing a bug my own conflict resolution would otherwise have introduced, not new scope): `verify-ca` now attaches a configured client cert the same way `verify-full` and the default branch already do.

Added a regression test, `build_tls_connector_verify_ca_attaches_a_configured_client_cert`, which checks `ClientConfig::client_auth_cert_resolver.has_certs()` directly — deliberately not just "the connector builds successfully," since the buggy merge also built successfully (it just silently omitted the client cert). Confirmed via a scratch revert that the test fails against the naive merge and passes against the fix.

PR description updated with the same details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

prerelease:beta Version suggestion targets a beta prerelease

Projects

None yet

Development

Successfully merging this pull request may close these issues.

verify-ca mode incorrectly enforces hostname verification

1 participant