tls: move key and cert_type fields into ssl_primary_config#21667
tls: move key and cert_type fields into ssl_primary_config#21667MegaManSec wants to merge 8 commits into
Conversation
cert_type, key, key_type, key_passwd and key_blob lived in ssl_config_data but not in ssl_primary_config, so they were invisible to match_ssl_primary_config() and to the TLS session cache peer key. Two easy handles sharing a connection pool could reuse each other's authenticated connections when they differed only on SSLKEY, SSLKEYTYPE, KEYPASSWD, SSLCERTTYPE or SSLKEYBLOB. The second handle would silently inherit the first handle's authenticated identity. Promote all five fields into ssl_primary_config so the conn-reuse predicate and session cache key cover the complete client credential set. Also replace the fixed ":CCERT" session cache marker with the actual clientcert path so sessions are not shared across different client certificates. Reported-By: Joshua Rogers (AISLE Research)
There was a problem hiding this comment.
Pull request overview
This PR moves client credential fields into ssl_primary_config so connection reuse and TLS session cache decisions can account for them consistently across TLS backends.
Changes:
- Promotes cert/key type, key path, key password, and key blob fields into
ssl_primary_config. - Updates TLS/SSH backend references to use the new field locations.
- Adds unit3303 for connection config matching of several client credential fields.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
lib/urldata.h |
Moves client credential fields into ssl_primary_config. |
lib/vtls/vtls.c |
Adds credential fields to config matching, cloning, cleanup, and setup. |
lib/vtls/vtls_scache.c |
Adds client cert/key path and key blob to TLS session cache peer key. |
lib/vtls/openssl.c |
Updates OpenSSL credential field accesses. |
lib/vtls/gtls.c |
Updates GnuTLS credential field accesses. |
lib/vtls/mbedtls.c |
Updates mbedTLS credential field accesses. |
lib/vtls/rustls.c |
Updates rustls key field accesses. |
lib/vtls/schannel.c |
Updates Schannel credential field accesses. |
lib/vtls/wolfssl.c |
Updates wolfSSL credential field accesses. |
lib/vssh/libssh.c |
Updates SSH key password access. |
lib/vssh/libssh2.c |
Updates SSH key password access. |
lib/ldap.c |
Updates LDAP TLS cert type access. |
tests/unit/unit3303.c |
Adds a unit test for connection config credential matching. |
tests/unit/Makefile.inc |
Includes the new unit test in the unit test list. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…n cache peer key Two handles sharing the same cert/key paths but differing only in cert_type, key_type or key_passwd would resolve to the same peer key and could share a TLS session ticket. With session resumption the server does not re-request the client certificate, so a handle with the wrong password could reuse a ticket established by a handle that authenticated correctly. Add cert_type and key_type as plain discriminators in the peer key string, and hash key_passwd via a new cf_ssl_peer_key_add_str_hash helper (mirroring cf_ssl_peer_key_add_hash for blobs) so the secret is never stored in plaintext in the cache key.
88d94ca to
52b7236
Compare
icing
left a comment
There was a problem hiding this comment.
I do not understand what this fixes. Talking about the password, for example. I'd be hesitant to put that into the session id, even as a hash. What is the benefit of matching this?
We are talking about a single user application where a connection/session might get reused after someone configured the wrong key password on a transfer? Why would anyone do this?
Extract the body of Curl_ssl_peer_key_make() into a new Curl_ssl_peer_key_build() that takes ssl_primary_config and peer descriptors directly, making the peer key logic unit-testable without a full Curl_cfilter. Group the mTLS credential fields (clientcert, key, key_blob, cert_type, key_type, key_passwd) into a static helper cf_ssl_peer_key_add_mtls() to keep the main function within the complexity budget. unit3304 verifies that Curl_ssl_peer_key_build() returns distinct keys when two configs differ only on key_passwd, key, key_type or cert_type.
Follow the same pattern as SRP credentials: do not embed key_passwd in the peer key string at all. Instead store it on the Curl_ssl_scache_peer struct and compare it at lookup time via Curl_timestrcmp() in cf_ssl_scache_match_auth(). Also mark peers with a key_passwd as non-exportable. Update unit3304 to reflect that key_passwd does not affect the peer key string.
cert_type and key_type are compared case-insensitively in match_ssl_primary_config (via curl_strequal), so the session cache peer key must also treat them case-insensitively. Use Curl_raw_toupper when building the :CT- and :KT- segments so "PEM" and "pem" map to the same peer key.
Fixed. Now we just do the same as the SRP credentials path, which isto store it as a separate field on As for what this PR is actually addressing, it's about two different handles in the same multi-handle sharing a connection pool or TLS session cache, i.e.
|
So, this protects against a coding mistake in the application, which would fail the connect otherwise because of the wrong password. Hmm... |
|
Is it a coding mistake in an application? Isn't this exactly the intended functionality of a multihandle? e.g. a server-side application using libcurl to forward requests on behalf of different tenants, each configured with their own mTLS client cert/key on separate handles, all sharing one connection pool. Without this fix, tenant B's handle can resume tenant A's cached TLS session.. |
|
sorry, not the cert path itself, but rather the two handles using the same certificate but different private keys or key passwords would be mixed up. |
Ah, ok. The key path and blob I understand. Thanks. |
|
sorry, I caused a merge conflict just now when I merged another PR... |
39de157 to
3406e8b
Compare
Add tests/data/test3303 and tests/data/test3304 so the unit tests are discovered by runtests.pl and included in the source tarball via Makefile.am.
|
The torture memory leak is probably fixed with this: diff --git a/tests/unit/unit3303.c b/tests/unit/unit3303.c
index e308085f8b..41bced542d 100644
--- a/tests/unit/unit3303.c
+++ b/tests/unit/unit3303.c
@@ -62,10 +62,12 @@ static CURLcode test_unit3303(const char *arg)
goto unit_test_abort;
}
conn = curlx_calloc(1, sizeof(*conn));
if(!conn || Curl_ssl_conn_config_init((struct Curl_easy *)curl, conn)) {
+ if(conn)
+ Curl_ssl_conn_config_cleanup(conn);
curlx_free(conn);
curl_easy_cleanup(curl);
curl_global_cleanup();
goto unit_test_abort;
} |
|
thanks! |
cert_type, key, key_type, key_passwd and key_blob lived in ssl_config_data but not in ssl_primary_config, so they were invisible to match_ssl_primary_config() and to the TLS session cache peer key. Two easy handles sharing a connection pool could reuse each other's authenticated connections when they differed only on SSLKEY, SSLKEYTYPE, KEYPASSWD, SSLCERTTYPE or SSLKEYBLOB. The second handle would silently inherit the first handle's authenticated identity. Promote all five fields into ssl_primary_config so the conn-reuse predicate and session cache key cover the complete client credential set. Also replace the fixed ":CCERT" session cache marker with the actual clientcert path so sessions are not shared across different client certificates. Verified by test 3303 and 3304 Reported-By: Joshua Rogers (AISLE Research) Closes curl#21667
- use `curlx_safefree()`. - drop redundant blocks. Follow-up to 7541ae5 curl#21667 Closes curl#21684
Description
cert_type,key,key_type,key_passwdandkey_bloblived inssl_config_databut notssl_primary_config, so they were invisible to bothmatch_ssl_primary_config(connection reuse) and the TLS session cache peer key and auth match. Onlyclientcertwas compared; the rest of the client credential set was silently ignored.Two concrete consequences:
Connection reuse: handles sharing the same certificate but differing in key file, key type, cert type, or key blob could incorrectly reuse each other's connections.
Session resumption bypass: a handle configured with the wrong key password would normally fail the TLS handshake — it cannot sign with the key. But if a correctly-configured handle had already cached a session, the misconfigured handle could resume that session without ever performing a handshake, bypassing key authentication entirely.
key_passwdis now stored on the session cache peer and compared withCurl_timestrcmpat lookup, following the same pattern as SRP credentials.Promote all five fields into
ssl_primary_configso both the conn-reuse predicate and the session cache cover the complete client credential set. Also replaces the fixed:CCERTsession cache marker with the actual client cert and key paths.Adds unit3303 to verify conn-reuse matching distinguishes handles differing on these fields, and unit3304 to verify the session cache peer key correctly includes key path, key type and cert type but not key password.