curl · Report #4123646
TLS session cache key omits native_ca_store: a session validated with CURLSSLOPT_NATIVE_CA can be resumed by a transfer that disabled it (incomplete f
How it was found
Summary
CVE-2026-11564 fixed the connection pool so that a connection whose certificate was
validated with the native CA store is not reused by a later transfer that switched to
custom CA material. That same field, native_ca_store, is compared for connection reuse
but is NOT part of the TLS session cache key. So a TLS session established and validated
with CURLSSLOPT_NATIVE_CA can be resumed, on a brand-new connection, by a transfer that
has native CA turned off. Resumption sends no server Certificate, so the second transfer
never validates the peer and ends up trusting a server its own CA configuration would have
rejected. Same certificate-validation bypass as CVE-2026-11564, reached through the session
cache instead of the connection pool — i.e. that fix is incomplete.
Exact source lines:
-
native_ca_storeIS required to match for connection reuse:lib/vtls/vtls_config.c:148(c1->native_ca_store == c2->native_ca_store) && -
native_ca_storeis NEVER added to the session cache key.ssl_peer_key_build()inlib/vtls/vtls_scache.cbuilds it; theverifypeerblock (aroundlib/vtls/vtls_scache.c:292-317) adds CAfile, CApath, Issuer, CAInfoBlob, issuercert_blob — but notnative_ca_store. Verify:git grep -n native_ca_store lib/vtls/vtls_scache.creturns 0 hits. -
CURLSSLOPT_NATIVE_CAonly flips the flag (lib/vtls/vtls_config.c:254); it does not change CAfile/CApath, so two configs differing only innative_ca_storebuild a byte-identical session key.
Affected version
master @ 8e3722b7 (8.23.1-DEV). Any build where CURLSSLOPT_NATIVE_CA is meaningful
(OpenSSL with the native store loaded, Schannel, Secure Transport).
Steps To Reproduce
Preconditions: two transfers to the same https origin that share a TLS session cache (two
easy handles sharing a CURLSHOPT_SSL_SESSION share, or two transfers on one multi handle),
TLS resumption enabled (default), and a server whose certificate chains to a root present in
the OS native store but NOT in the file bundle (realistic with an enterprise/MDM OS root).
- Handle A ->
https://target,CURLSSLOPT_NATIVE_CAset, verifypeer on, default/empty CAfile. A validates the server cert against the native store and stores the TLS session (Curl_ssl_scache_put). - Handle B -> same
https://target,CURLSSLOPT_NATIVE_CANOT set, same CAfile, verifypeer on. B builds the same session key, opens a new connection, andCurl_ssl_scache_get_sessionreturns A's cached session. TLS resumes; no certificate is sent or validated. - Result: B completes the handshake and trusts a server whose certificate its own (bundle-only) configuration would have rejected.
Negative control: give A and B the same native_ca_store value -> no bypass (intended
behaviour). The bug is only the config mismatch being invisible to the session key.
The attached verify.sh proves the code-level asymmetry against a fresh clone (native_ca_store
required for connection reuse, 0 occurrences in the session-key builder).
Suggested fix
Add native_ca_store to the verifypeer part of the session key in ssl_peer_key_build()
(lib/vtls/vtls_scache.c), exactly as it is compared in match_ssl_primary_config()
(lib/vtls/vtls_config.c:148). The ECH fields (ech/ech_config/ech_public) are also
compared for reuse but absent from the key — worth adding in the same change.
Impact
Summary
A transfer that deliberately restricts trust to a file CA bundle can resume a TLS session that was only ever validated against the operating-system native CA store, and therefore accept a server certificate it would otherwise reject.
An active network attacker who holds a certificate trusted by one store but not the other (for example a cert chaining to a corporate/MDM root present in the OS store but absent from the application's bundle) can MITM the stricter transfer: it resumes the session and never re-validates the peer.
This is the same impact as CVE-2026-11564 (certificate validation bypass, CWE-295), reached through TLS session resumption rather than connection pooling, so the fix for that CVE is incomplete.
Not a bypass when: native CA is not used, the two configs use the same native_ca_store
value, or no session cache is shared across differing configs. Hence Low severity, matching
the parent CVE.