mirror of
https://github.com/curl/curl.git
synced 2026-08-01 19:40:32 +03:00
tls: fix incomplete mTLS config in conn reuse and session cache
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 #21667
This commit is contained in:
parent
a4dca608e1
commit
7541ae569d
19 changed files with 512 additions and 81 deletions
|
|
@ -287,7 +287,7 @@ test3200 test3201 test3202 test3203 test3204 test3205 test3206 test3207 \
|
|||
test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \
|
||||
test3216 test3217 test3218 test3219 test3220 \
|
||||
\
|
||||
test3300 test3301 test3302 \
|
||||
test3300 test3301 test3302 test3303 test3304 \
|
||||
\
|
||||
test4000 test4001
|
||||
|
||||
|
|
|
|||
20
tests/data/test3303
Normal file
20
tests/data/test3303
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="US-ASCII"?>
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
unittest
|
||||
TLS
|
||||
mTLS
|
||||
</keywords>
|
||||
</info>
|
||||
|
||||
# Client-side
|
||||
<client>
|
||||
<features>
|
||||
unittest
|
||||
</features>
|
||||
<name>
|
||||
conn-reuse match distinguishes mTLS key, cert_type, key_type and key_passwd fields
|
||||
</name>
|
||||
</client>
|
||||
</testcase>
|
||||
20
tests/data/test3304
Normal file
20
tests/data/test3304
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?xml version="1.0" encoding="US-ASCII"?>
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
unittest
|
||||
TLS
|
||||
mTLS
|
||||
</keywords>
|
||||
</info>
|
||||
|
||||
# Client-side
|
||||
<client>
|
||||
<features>
|
||||
unittest
|
||||
</features>
|
||||
<name>
|
||||
TLS session cache peer key discriminates on mTLS key, key_type and cert_type fields
|
||||
</name>
|
||||
</client>
|
||||
</testcase>
|
||||
|
|
@ -47,4 +47,4 @@ TESTS_C = \
|
|||
unit2600.c unit2601.c unit2602.c unit2603.c unit2604.c unit2605.c \
|
||||
unit3200.c unit3205.c \
|
||||
unit3211.c unit3212.c unit3213.c unit3214.c unit3216.c unit3219.c \
|
||||
unit3300.c unit3301.c unit3302.c
|
||||
unit3300.c unit3301.c unit3302.c unit3303.c unit3304.c
|
||||
|
|
|
|||
127
tests/unit/unit3303.c
Normal file
127
tests/unit/unit3303.c
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "unitcheck.h"
|
||||
#include "urldata.h"
|
||||
|
||||
#ifdef USE_SSL
|
||||
#include "vtls/vtls.h"
|
||||
#endif
|
||||
|
||||
static CURLcode test_unit3303(const char *arg)
|
||||
{
|
||||
UNITTEST_BEGIN_SIMPLE
|
||||
|
||||
#ifdef USE_SSL
|
||||
{
|
||||
CURL *curl;
|
||||
struct connectdata *conn;
|
||||
struct ssl_primary_config *primary;
|
||||
char *saved;
|
||||
static char alt_passwd[] = "wrong";
|
||||
static char alt_key[] = "other.key";
|
||||
static char alt_ktype[] = "DER";
|
||||
static char alt_ctype[] = "P12";
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
curl = curl_easy_init();
|
||||
if(!curl) {
|
||||
curl_global_cleanup();
|
||||
goto unit_test_abort;
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_SSLCERT, "client.pem");
|
||||
curl_easy_setopt(curl, CURLOPT_SSLKEY, "client.key");
|
||||
curl_easy_setopt(curl, CURLOPT_KEYPASSWD, "secret");
|
||||
curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM");
|
||||
curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM");
|
||||
|
||||
if(Curl_ssl_easy_config_complete((struct Curl_easy *)curl)) {
|
||||
curl_easy_cleanup(curl);
|
||||
curl_global_cleanup();
|
||||
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;
|
||||
}
|
||||
|
||||
/* Baseline: identical config must match. */
|
||||
fail_unless(Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn,
|
||||
FALSE),
|
||||
"identical mTLS config should match");
|
||||
|
||||
primary = &((struct Curl_easy *)curl)->set.ssl.primary;
|
||||
|
||||
/* Different key_passwd must not match. */
|
||||
saved = primary->key_passwd;
|
||||
primary->key_passwd = alt_passwd;
|
||||
fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn,
|
||||
FALSE),
|
||||
"different key_passwd must not reuse conn");
|
||||
primary->key_passwd = saved;
|
||||
|
||||
/* Different key path must not match. */
|
||||
saved = primary->key;
|
||||
primary->key = alt_key;
|
||||
fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn,
|
||||
FALSE),
|
||||
"different key must not reuse conn");
|
||||
primary->key = saved;
|
||||
|
||||
/* Different key type must not match. */
|
||||
saved = primary->key_type;
|
||||
primary->key_type = alt_ktype;
|
||||
fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn,
|
||||
FALSE),
|
||||
"different key_type must not reuse conn");
|
||||
primary->key_type = saved;
|
||||
|
||||
/* Different cert type must not match. */
|
||||
saved = primary->cert_type;
|
||||
primary->cert_type = alt_ctype;
|
||||
fail_unless(!Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn,
|
||||
FALSE),
|
||||
"different cert_type must not reuse conn");
|
||||
primary->cert_type = saved;
|
||||
|
||||
/* All fields restored: must match again. */
|
||||
fail_unless(Curl_ssl_conn_config_match((struct Curl_easy *)curl, conn,
|
||||
FALSE),
|
||||
"restored mTLS config should match");
|
||||
|
||||
Curl_ssl_conn_config_cleanup(conn);
|
||||
curlx_free(conn);
|
||||
curl_easy_cleanup(curl);
|
||||
curl_global_cleanup();
|
||||
}
|
||||
#endif /* USE_SSL */
|
||||
|
||||
UNITTEST_END_SIMPLE
|
||||
}
|
||||
168
tests/unit/unit3304.c
Normal file
168
tests/unit/unit3304.c
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
/* Unit tests for TLS session cache peer key discrimination on mTLS fields.
|
||||
* Verifies that Curl_ssl_peer_key_build() produces distinct keys when two
|
||||
* handles differ only on key, key_type or cert_type. key_passwd is NOT
|
||||
* embedded in the peer key; it is compared separately at session lookup via
|
||||
* cf_ssl_scache_match_auth(), following the same pattern as SRP
|
||||
* credentials. */
|
||||
|
||||
#include "unitcheck.h"
|
||||
#include "urldata.h"
|
||||
#include "peer.h"
|
||||
|
||||
#ifdef USE_SSL
|
||||
#include "vtls/vtls.h"
|
||||
#include "vtls/vtls_scache.h"
|
||||
#endif
|
||||
|
||||
static CURLcode test_unit3304(const char *arg)
|
||||
{
|
||||
UNITTEST_BEGIN_SIMPLE
|
||||
|
||||
#ifdef USE_SSL
|
||||
{
|
||||
struct Curl_peer dest;
|
||||
struct ssl_peer peer;
|
||||
struct ssl_primary_config ssl;
|
||||
char *key1 = NULL;
|
||||
char *key2 = NULL;
|
||||
static char base_hostname[] = "example.com";
|
||||
static char base_cert[] = "client.pem";
|
||||
static char base_key[] = "client.key";
|
||||
static char base_passwd[] = "secret";
|
||||
static char base_ctype[] = "PEM";
|
||||
static char base_ktype[] = "PEM";
|
||||
static char alt_key[] = "other.key";
|
||||
static char alt_ktype[] = "DER";
|
||||
static char alt_ctype[] = "P12";
|
||||
static char lc_ctype[] = "pem";
|
||||
static char lc_ktype[] = "pem";
|
||||
|
||||
memset(&dest, 0, sizeof(dest));
|
||||
dest.hostname = base_hostname;
|
||||
dest.port = 443;
|
||||
|
||||
memset(&peer, 0, sizeof(peer));
|
||||
peer.dest = &dest;
|
||||
peer.transport = TRNSPRT_TCP;
|
||||
|
||||
memset(&ssl, 0, sizeof(ssl));
|
||||
ssl.verifypeer = TRUE;
|
||||
ssl.verifyhost = TRUE;
|
||||
ssl.clientcert = base_cert;
|
||||
ssl.key = base_key;
|
||||
ssl.key_passwd = base_passwd;
|
||||
ssl.cert_type = base_ctype;
|
||||
ssl.key_type = base_ktype;
|
||||
|
||||
/* Baseline: same config produces same key. */
|
||||
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
|
||||
"peer key build failed");
|
||||
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
|
||||
"peer key build failed");
|
||||
fail_unless(key1 && key2 && !strcmp(key1, key2),
|
||||
"identical config should produce identical peer key");
|
||||
curlx_free(key1); key1 = NULL;
|
||||
curlx_free(key2); key2 = NULL;
|
||||
|
||||
/* key_passwd is NOT in the peer key: lookup uses timing-safe comparison
|
||||
* via cf_ssl_scache_match_auth(), same as SRP credentials. */
|
||||
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
|
||||
"peer key build failed");
|
||||
ssl.key_passwd = NULL;
|
||||
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
|
||||
"peer key build failed");
|
||||
fail_unless(key1 && key2 && !strcmp(key1, key2),
|
||||
"key_passwd must not affect the peer key");
|
||||
curlx_free(key1); key1 = NULL;
|
||||
curlx_free(key2); key2 = NULL;
|
||||
ssl.key_passwd = base_passwd;
|
||||
|
||||
/* Different key path must produce a different peer key. */
|
||||
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
|
||||
"peer key build failed");
|
||||
ssl.key = alt_key;
|
||||
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
|
||||
"peer key build failed");
|
||||
fail_unless(key1 && key2 && strcmp(key1, key2),
|
||||
"different key must produce different peer key");
|
||||
curlx_free(key1); key1 = NULL;
|
||||
curlx_free(key2); key2 = NULL;
|
||||
ssl.key = base_key;
|
||||
|
||||
/* Different key_type must produce a different peer key. */
|
||||
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
|
||||
"peer key build failed");
|
||||
ssl.key_type = alt_ktype;
|
||||
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
|
||||
"peer key build failed");
|
||||
fail_unless(key1 && key2 && strcmp(key1, key2),
|
||||
"different key_type must produce different peer key");
|
||||
curlx_free(key1); key1 = NULL;
|
||||
curlx_free(key2); key2 = NULL;
|
||||
ssl.key_type = base_ktype;
|
||||
|
||||
/* Different cert_type must produce a different peer key. */
|
||||
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
|
||||
"peer key build failed");
|
||||
ssl.cert_type = alt_ctype;
|
||||
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
|
||||
"peer key build failed");
|
||||
fail_unless(key1 && key2 && strcmp(key1, key2),
|
||||
"different cert_type must produce different peer key");
|
||||
curlx_free(key1); key1 = NULL;
|
||||
curlx_free(key2); key2 = NULL;
|
||||
ssl.cert_type = base_ctype;
|
||||
|
||||
/* cert_type is case-insensitive: "PEM" and "pem" must produce the
|
||||
* same peer key, consistent with the conn-reuse comparison. */
|
||||
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
|
||||
"peer key build failed");
|
||||
ssl.cert_type = lc_ctype;
|
||||
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
|
||||
"peer key build failed");
|
||||
fail_unless(key1 && key2 && !strcmp(key1, key2),
|
||||
"cert_type case must not affect peer key");
|
||||
curlx_free(key1); key1 = NULL;
|
||||
curlx_free(key2); key2 = NULL;
|
||||
ssl.cert_type = base_ctype;
|
||||
|
||||
/* key_type is case-insensitive: "PEM" and "pem" must produce the
|
||||
* same peer key. */
|
||||
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
|
||||
"peer key build failed");
|
||||
ssl.key_type = lc_ktype;
|
||||
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
|
||||
"peer key build failed");
|
||||
fail_unless(key1 && key2 && !strcmp(key1, key2),
|
||||
"key_type case must not affect peer key");
|
||||
curlx_free(key1); key1 = NULL;
|
||||
curlx_free(key2); key2 = NULL;
|
||||
}
|
||||
#endif /* USE_SSL */
|
||||
|
||||
UNITTEST_END_SIMPLE
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue