#ifdef LDAP_OPT_X_TLS
if(conn->ssl_config.verifypeer) {
/* OpenLDAP SDK supports BASE64 files. */
- if(data->set.ssl.cert_type &&
- !curl_strequal(data->set.ssl.cert_type, "PEM")) {
+ if(data->set.ssl.primary.cert_type &&
+ !curl_strequal(data->set.ssl.primary.cert_type, "PEM")) {
failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type");
result = CURLE_SSL_CERTPROBLEM;
goto quit;
char *signature_algorithms; /* list of signature algorithms to use */
char *pinned_key;
char *CRLfile; /* CRL to check certificate revocation */
+ char *cert_type; /* format for certificate (default: PEM) */
+ char *key; /* private key filename */
+ char *key_type; /* format for private key (default: PEM) */
+ char *key_passwd; /* plain text private key password */
struct curl_blob *cert_blob;
struct curl_blob *ca_info_blob;
struct curl_blob *issuercert_blob;
+ struct curl_blob *key_blob;
#ifdef USE_TLS_SRP
char *username; /* TLS username (for, e.g., SRP) */
char *password; /* TLS password (for, e.g., SRP) */
long certverifyresult; /* result from the certificate verification */
curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */
void *fsslctxp; /* parameter for call back */
- char *cert_type; /* format for certificate (default: PEM) */
- char *key; /* private key filename */
- struct curl_blob *key_blob;
- char *key_type; /* format for private key (default: PEM) */
- char *key_passwd; /* plain text private key password */
BIT(certinfo); /* gather lots of certificate info */
BIT(earlydata); /* use TLS 1.3 early data */
BIT(enable_beast); /* allow this flaw for interoperability's sake */
/* Two choices, (1) private key was given on CMD,
* (2) use the "default" keys. */
if(data->set.str[STRING_SSH_PRIVATE_KEY]) {
- if(sshc->pubkey && !data->set.ssl.key_passwd) {
+ if(sshc->pubkey && !data->set.ssl.primary.key_passwd) {
rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL, sshc->pubkey);
if(rc == SSH_AUTH_AGAIN)
return SSH_AGAIN;
rc = ssh_pki_import_privkey_file(data->
set.str[STRING_SSH_PRIVATE_KEY],
- data->set.ssl.key_passwd, NULL,
+ data->set.ssl.primary.key_passwd, NULL,
NULL, &sshc->privkey);
if(rc != SSH_OK) {
failf(data, "Could not load private key file %s",
}
else {
rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL,
- data->set.ssl.key_passwd);
+ data->set.ssl.primary.key_passwd);
if(rc == SSH_AUTH_AGAIN)
return SSH_AGAIN;
return CURLE_OUT_OF_MEMORY;
}
- sshc->passphrase = data->set.ssl.key_passwd;
+ sshc->passphrase = data->set.ssl.primary.key_passwd;
if(!sshc->passphrase)
sshc->passphrase = "";
if(result)
return result;
}
- if(ssl_config->cert_type && curl_strequal(ssl_config->cert_type, "P12")) {
+ if(ssl_config->primary.cert_type &&
+ curl_strequal(ssl_config->primary.cert_type, "P12")) {
rc = gnutls_certificate_set_x509_simple_pkcs12_file(
gtls->shared_creds->creds, config->clientcert, GNUTLS_X509_FMT_DER,
- ssl_config->key_passwd ? ssl_config->key_passwd : "");
+ ssl_config->primary.key_passwd ? ssl_config->primary.key_passwd : "");
if(rc != GNUTLS_E_SUCCESS) {
failf(data,
"error reading X.509 potentially-encrypted key or certificate "
rc = gnutls_certificate_set_x509_key_file2(
gtls->shared_creds->creds,
config->clientcert,
- ssl_config->key ? ssl_config->key : config->clientcert,
- gnutls_do_file_type(ssl_config->cert_type),
- ssl_config->key_passwd,
+ ssl_config->primary.key ? ssl_config->primary.key :
+ config->clientcert,
+ gnutls_do_file_type(ssl_config->primary.cert_type),
+ ssl_config->primary.key_passwd,
supported_key_encryption_algorithms);
if(rc != GNUTLS_E_SUCCESS) {
failf(data,
"error reading X.509 %skey file: %s",
- ssl_config->key_passwd ? "potentially-encrypted " : "",
+ ssl_config->primary.key_passwd ? "potentially-encrypted " : "",
gnutls_strerror(rc));
return CURLE_SSL_CONNECT_ERROR;
}
const char * const ssl_capath = conn_config->CApath;
#ifdef MBEDTLS_PEM_PARSE_C
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- const char * const ssl_cert_type = ssl_config->cert_type;
+ const char * const ssl_cert_type = ssl_config->primary.cert_type;
#endif
int ret = -1;
char errorbuf[128];
char * const ssl_cert = ssl_config->primary.clientcert;
const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
#ifdef MBEDTLS_PEM_PARSE_C
- const char * const ssl_cert_type = ssl_config->cert_type;
+ const char * const ssl_cert_type = ssl_config->primary.cert_type;
#endif
int ret = -1;
char errorbuf[128];
mbedtls_pk_init(&backend->pk);
- if(ssl_config->key || ssl_config->key_blob) {
- if(ssl_config->key) {
+ if(ssl_config->primary.key || ssl_config->primary.key_blob) {
+ if(ssl_config->primary.key) {
#ifdef MBEDTLS_FS_IO
#if MBEDTLS_VERSION_NUMBER >= 0x04000000
- ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key,
- ssl_config->key_passwd);
+ ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->primary.key,
+ ssl_config->primary.key_passwd);
if(ret == 0 &&
!(mbedtls_pk_can_do_psa(&backend->pk,
PSA_ALG_RSA_PKCS1V15_SIGN(PSA_ALG_ANY_HASH),
PSA_KEY_USAGE_SIGN_HASH)))
ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
#else
- ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key,
- ssl_config->key_passwd,
+ ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->primary.key,
+ ssl_config->primary.key_passwd,
mbedtls_ctr_drbg_random,
&rng.drbg);
if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) ||
if(ret) {
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
failf(data, "mbedTLS: error reading private key %s: (-0x%04X) %s",
- ssl_config->key, -ret, errorbuf);
+ ssl_config->primary.key, -ret, errorbuf);
return CURLE_SSL_CERTPROBLEM;
}
#else
#endif
}
else {
- const struct curl_blob *ssl_key_blob = ssl_config->key_blob;
- const char *passwd = ssl_config->key_passwd;
+ const struct curl_blob *ssl_key_blob = ssl_config->primary.key_blob;
+ const char *passwd = ssl_config->primary.key_passwd;
/* Unfortunately, mbedtls_pk_parse_key() requires the data to be
null-terminated if the data is PEM encoded (even when provided the
exact length). */
#endif
);
- if(ssl_config->key || ssl_config->key_blob) {
+ if(ssl_config->primary.key || ssl_config->primary.key_blob) {
mbedtls_ssl_conf_own_cert(&backend->config, &backend->clicert,
&backend->pk);
}
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
char * const ssl_cert = ssl_config->primary.clientcert;
const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
- const char * const ssl_cert_type = ssl_config->cert_type;
+ const char * const ssl_cert_type = ssl_config->primary.cert_type;
unsigned int ssl_version_min;
char error_buffer[256];
if(ssl_cert || ssl_cert_blob || ssl_cert_type) {
result = client_cert(data, octx->ssl_ctx,
ssl_cert, ssl_cert_blob, ssl_cert_type,
- ssl_config->key, ssl_config->key_blob,
- ssl_config->key_type, ssl_config->key_passwd);
+ ssl_config->primary.key, ssl_config->primary.key_blob,
+ ssl_config->primary.key_type,
+ ssl_config->primary.key_passwd);
if(result)
/* failf() is already done in client_cert() */
return result;
const struct rustls_certified_key *certified_key = NULL;
CURLcode result = CURLE_OK;
- if(conn_config->clientcert && !ssl_config->key) {
+ if(conn_config->clientcert && !ssl_config->primary.key) {
failf(data, "rustls: must provide key with certificate '%s'",
conn_config->clientcert);
return CURLE_SSL_CERTPROBLEM;
}
- else if(!conn_config->clientcert && ssl_config->key) {
+ else if(!conn_config->clientcert && ssl_config->primary.key) {
failf(data, "rustls: must provide certificate with key '%s'",
- ssl_config->key);
+ ssl_config->primary.key);
return CURLE_SSL_CERTPROBLEM;
}
goto cleanup;
}
- if(!read_file_into(ssl_config->key, &key_contents)) {
- failf(data, "rustls: failed to read key file: '%s'", ssl_config->key);
+ if(!read_file_into(ssl_config->primary.key, &key_contents)) {
+ failf(data, "rustls: failed to read key file: '%s'",
+ ssl_config->primary.key);
result = CURLE_SSL_CERTPROBLEM;
goto cleanup;
}
}
}
- if(conn_config->clientcert || ssl_config->key) {
+ if(conn_config->clientcert || ssl_config->primary.key) {
result = init_config_builder_client_auth(data,
conn_config,
ssl_config,
}
}
- if((fInCert || blob) && data->set.ssl.cert_type &&
- !curl_strequal(data->set.ssl.cert_type, "P12")) {
+ if((fInCert || blob) && data->set.ssl.primary.cert_type &&
+ !curl_strequal(data->set.ssl.primary.cert_type, "P12")) {
failf(data, "schannel: certificate format compatibility error "
"for %s",
blob ? "(memory blob)" : data->set.ssl.primary.clientcert);
datablob.pbData = (BYTE *)certdata;
datablob.cbData = (DWORD)certsize;
- if(data->set.ssl.key_passwd)
- pwd_len = strlen(data->set.ssl.key_passwd);
+ if(data->set.ssl.primary.key_passwd)
+ pwd_len = strlen(data->set.ssl.primary.key_passwd);
pszPassword = (WCHAR *)curlx_malloc(sizeof(WCHAR) * (pwd_len + 1));
if(pszPassword) {
int str_w_len = 0;
if(pwd_len > 0)
str_w_len = MultiByteToWideChar(CP_UTF8,
MB_ERR_INVALID_CHARS,
- data->set.ssl.key_passwd,
+ data->set.ssl.primary.key_passwd,
(int)pwd_len,
pszPassword, (int)(pwd_len + 1));
blobcmp(c1->cert_blob, c2->cert_blob) &&
blobcmp(c1->ca_info_blob, c2->ca_info_blob) &&
blobcmp(c1->issuercert_blob, c2->issuercert_blob) &&
+ blobcmp(c1->key_blob, c2->key_blob) &&
Curl_safecmp(c1->CApath, c2->CApath) &&
Curl_safecmp(c1->CAfile, c2->CAfile) &&
Curl_safecmp(c1->issuercert, c2->issuercert) &&
curl_strequal(c1->curves, c2->curves) &&
curl_strequal(c1->signature_algorithms, c2->signature_algorithms) &&
Curl_safecmp(c1->CRLfile, c2->CRLfile) &&
- Curl_safecmp(c1->pinned_key, c2->pinned_key))
+ Curl_safecmp(c1->pinned_key, c2->pinned_key) &&
+ curl_strequal(c1->cert_type, c2->cert_type) &&
+ Curl_safecmp(c1->key, c2->key) &&
+ curl_strequal(c1->key_type, c2->key_type) &&
+ !Curl_timestrcmp(c1->key_passwd, c2->key_passwd))
return TRUE;
return FALSE;
CLONE_BLOB(cert_blob);
CLONE_BLOB(ca_info_blob);
CLONE_BLOB(issuercert_blob);
+ CLONE_BLOB(key_blob);
CLONE_STRING(CApath);
CLONE_STRING(CAfile);
CLONE_STRING(issuercert);
CLONE_STRING(curves);
CLONE_STRING(signature_algorithms);
CLONE_STRING(CRLfile);
+ CLONE_STRING(cert_type);
+ CLONE_STRING(key);
+ CLONE_STRING(key_type);
+ CLONE_STRING(key_passwd);
#ifdef USE_TLS_SRP
CLONE_STRING(username);
CLONE_STRING(password);
curlx_safefree(sslc->cert_blob);
curlx_safefree(sslc->ca_info_blob);
curlx_safefree(sslc->issuercert_blob);
+ curlx_safefree(sslc->key_blob);
curlx_safefree(sslc->curves);
curlx_safefree(sslc->signature_algorithms);
curlx_safefree(sslc->CRLfile);
+ curlx_safefree(sslc->cert_type);
+ curlx_safefree(sslc->key);
+ curlx_safefree(sslc->key_type);
+ curlx_safefree(sslc->key_passwd);
#ifdef USE_TLS_SRP
curlx_safefree(sslc->username);
curlx_safefree(sslc->password);
sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME];
sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD];
#endif
- sslc->cert_type = data->set.str[STRING_CERT_TYPE];
- sslc->key = data->set.str[STRING_KEY];
- sslc->key_type = data->set.str[STRING_KEY_TYPE];
- sslc->key_passwd = data->set.str[STRING_KEY_PASSWD];
+ sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE];
+ sslc->primary.key = data->set.str[STRING_KEY];
+ sslc->primary.key_type = data->set.str[STRING_KEY_TYPE];
+ sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD];
sslc->primary.clientcert = data->set.str[STRING_CERT];
- sslc->key_blob = data->set.blobs[BLOB_KEY];
+ sslc->primary.key_blob = data->set.blobs[BLOB_KEY];
#ifndef CURL_DISABLE_PROXY
sslc = &data->set.proxy_ssl;
sslc->primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY];
sslc->primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY];
sslc->primary.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY];
- sslc->cert_type = data->set.str[STRING_CERT_TYPE_PROXY];
- sslc->key = data->set.str[STRING_KEY_PROXY];
- sslc->key_type = data->set.str[STRING_KEY_TYPE_PROXY];
- sslc->key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY];
+ sslc->primary.cert_type = data->set.str[STRING_CERT_TYPE_PROXY];
+ sslc->primary.key = data->set.str[STRING_KEY_PROXY];
+ sslc->primary.key_type = data->set.str[STRING_KEY_TYPE_PROXY];
+ sslc->primary.key_passwd = data->set.str[STRING_KEY_PASSWD_PROXY];
sslc->primary.clientcert = data->set.str[STRING_CERT_PROXY];
- sslc->key_blob = data->set.blobs[BLOB_KEY_PROXY];
+ sslc->primary.key_blob = data->set.blobs[BLOB_KEY_PROXY];
#ifdef USE_TLS_SRP
sslc->primary.username = data->set.str[STRING_TLSAUTH_USERNAME_PROXY];
sslc->primary.password = data->set.str[STRING_TLSAUTH_PASSWORD_PROXY];
struct Curl_ssl_scache_peer {
char *ssl_peer_key; /* id for peer + relevant TLS configuration */
char *clientcert;
+ char *key_passwd;
char *srp_username;
char *srp_password;
struct Curl_llist sessions;
return result;
}
+static CURLcode cf_ssl_peer_key_add_mtls(struct dynbuf *buf,
+ struct ssl_primary_config *ssl,
+ bool *is_local)
+{
+ CURLcode result = CURLE_OK;
+ if(ssl->clientcert && ssl->clientcert[0]) {
+ result = cf_ssl_peer_key_add_path(buf, "CCERT", ssl->clientcert, is_local);
+ if(result)
+ goto out;
+ }
+ if(ssl->key && ssl->key[0]) {
+ result = cf_ssl_peer_key_add_path(buf, "KEY", ssl->key, is_local);
+ if(result)
+ goto out;
+ }
+ if(ssl->key_blob) {
+ result = cf_ssl_peer_key_add_hash(buf, "KEYBlob", ssl->key_blob);
+ if(result)
+ goto out;
+ }
+ if(ssl->cert_type && ssl->cert_type[0]) {
+ size_t i;
+ result = curlx_dyn_add(buf, ":CT-");
+ for(i = 0; !result && ssl->cert_type[i]; i++) {
+ char c = Curl_raw_toupper(ssl->cert_type[i]);
+ result = curlx_dyn_addn(buf, &c, 1);
+ }
+ if(result)
+ goto out;
+ }
+ if(ssl->key_type && ssl->key_type[0]) {
+ size_t i;
+ result = curlx_dyn_add(buf, ":KT-");
+ for(i = 0; !result && ssl->key_type[i]; i++) {
+ char c = Curl_raw_toupper(ssl->key_type[i]);
+ result = curlx_dyn_addn(buf, &c, 1);
+ }
+ }
+out:
+ return result;
+}
+
#define CURL_SSLS_LOCAL_SUFFIX ":L"
#define CURL_SSLS_GLOBAL_SUFFIX ":G"
(peer_key[len - 2] == ':');
}
-CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
- const struct ssl_peer *peer,
- const char *tls_id,
- char **ppeer_key)
+CURLcode Curl_ssl_peer_key_build(struct ssl_primary_config *ssl,
+ const struct ssl_peer *peer,
+ const struct Curl_peer *via_peer,
+ const char *tls_id,
+ char **ppeer_key)
{
- struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
struct dynbuf buf;
size_t key_len;
bool is_local = FALSE;
goto out;
}
if(!ssl->verifypeer || !ssl->verifyhost) {
- if(cf->conn->via_peer) {
+ if(via_peer) {
result = curlx_dyn_addf(&buf, ":CHOST-%s:CPORT-%u",
- cf->conn->via_peer->hostname,
- cf->conn->via_peer->port);
+ via_peer->hostname,
+ via_peer->port);
if(result)
goto out;
}
goto out;
}
- if(ssl->clientcert && ssl->clientcert[0]) {
- result = curlx_dyn_add(&buf, ":CCERT");
- if(result)
- goto out;
- }
+ result = cf_ssl_peer_key_add_mtls(&buf, ssl, &is_local);
+ if(result)
+ goto out;
#ifdef USE_TLS_SRP
if(ssl->username || ssl->password) {
result = curlx_dyn_add(&buf, ":SRP-AUTH");
return result;
}
+CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
+ const struct ssl_peer *peer,
+ const char *tls_id,
+ char **ppeer_key)
+{
+ struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
+ return Curl_ssl_peer_key_build(ssl, peer, cf->conn->via_peer, tls_id,
+ ppeer_key);
+}
+
struct Curl_ssl_scache {
unsigned int magic;
struct Curl_ssl_scache_peer *peers;
}
peer->sobj_free = NULL;
curlx_safefree(peer->clientcert);
+ curlx_safefree(peer->key_passwd);
#ifdef USE_TLS_SRP
curlx_safefree(peer->srp_username);
curlx_safefree(peer->srp_password);
* - its peer key is not yet known, because sessions were
* imported using only the salt+hmac
* - the peer key is global, e.g. carrying no relative paths */
- peer->exportable = (!peer->clientcert && !peer->srp_username &&
- !peer->srp_password &&
+ peer->exportable = (!peer->clientcert && !peer->key_passwd &&
+ !peer->srp_username && !peer->srp_password &&
(!peer->ssl_peer_key ||
cf_ssl_peer_key_is_global(peer->ssl_peer_key)));
}
cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer,
const char *ssl_peer_key,
const char *clientcert,
+ const char *key_passwd,
const char *srp_username,
const char *srp_password,
const unsigned char *salt,
if(!peer->clientcert)
goto out;
}
+ if(key_passwd) {
+ peer->key_passwd = curlx_strdup(key_passwd);
+ if(!peer->key_passwd)
+ goto out;
+ }
if(srp_username) {
peer->srp_username = curlx_strdup(srp_username);
if(!peer->srp_username)
struct ssl_primary_config *conn_config)
{
if(!conn_config) {
- if(peer->clientcert)
+ if(peer->clientcert || peer->key_passwd)
return FALSE;
#ifdef USE_TLS_SRP
if(peer->srp_username || peer->srp_password)
}
else if(!Curl_safecmp(peer->clientcert, conn_config->clientcert))
return FALSE;
+ if(Curl_timestrcmp(peer->key_passwd, conn_config->key_passwd))
+ return FALSE;
#ifdef USE_TLS_SRP
if(Curl_timestrcmp(peer->srp_username, conn_config->username) ||
Curl_timestrcmp(peer->srp_password, conn_config->password))
if(peer) {
char buffer[64];
const char *ccert = conn_config ? conn_config->clientcert : NULL;
+ const char *kpasswd = conn_config ? conn_config->key_passwd : NULL;
const char *username = NULL, *password = NULL;
#ifdef USE_TLS_SRP
username = conn_config ? conn_config->username : NULL;
"cert-%p", conn_config->cert_blob->data);
ccert = buffer; /* data is strduped by cf_ssl_scache_peer_init */
}
- result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert,
+ result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert, kpasswd,
username, password, NULL, NULL);
if(result)
goto out;
if(!peer) {
peer = cf_ssl_get_free_peer(scache);
if(peer) {
- result = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL,
+ result = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL, NULL,
NULL, NULL, salt, hmac);
if(result)
goto out;
const char *tls_id,
char **ppeer_key);
+/**
+ * Like Curl_ssl_peer_key_make() but takes the primary config and peer
+ * descriptors directly, without requiring a Curl_cfilter. Exposed for
+ * unit testing.
+ * @param ssl the primary SSL config to key on
+ * @param peer the peer the filter wants to talk to
+ * @param via_peer the connecting-through peer, or NULL
+ * @param tls_id identifier of TLS implementation for sessions
+ * @param ppeer_key on successful return, the key generated
+ */
+CURLcode Curl_ssl_peer_key_build(struct ssl_primary_config *ssl,
+ const struct ssl_peer *peer,
+ const struct Curl_peer *via_peer,
+ const char *tls_id,
+ char **ppeer_key);
+
/* Return if there is a session cache shall be used.
* An ssl session might not be configured or not available for
* "connect-only" transfers.
#ifndef NO_FILESYSTEM
if(ssl_config->primary.cert_blob || ssl_config->primary.clientcert) {
const char *cert_file = ssl_config->primary.clientcert;
- const char *key_file = ssl_config->key;
+ const char *key_file = ssl_config->primary.key;
const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
- const struct curl_blob *key_blob = ssl_config->key_blob;
- int file_type = wssl_do_file_type(ssl_config->cert_type);
+ const struct curl_blob *key_blob = ssl_config->primary.key_blob;
+ int file_type = wssl_do_file_type(ssl_config->primary.cert_type);
int rc;
switch(file_type) {
key_file = cert_file;
}
else
- file_type = wssl_do_file_type(ssl_config->key_type);
+ file_type = wssl_do_file_type(ssl_config->primary.key_type);
rc = key_blob ?
wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data,
#else /* NO_FILESYSTEM */
if(ssl_config->primary.cert_blob) {
const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
- const struct curl_blob *key_blob = ssl_config->key_blob;
- int file_type = wssl_do_file_type(ssl_config->cert_type);
+ const struct curl_blob *key_blob = ssl_config->primary.key_blob;
+ int file_type = wssl_do_file_type(ssl_config->primary.cert_type);
int rc;
switch(file_type) {
if(!key_blob)
key_blob = cert_blob;
else
- file_type = wssl_do_file_type(ssl_config->key_type);
+ file_type = wssl_do_file_type(ssl_config->primary.key_type);
if(wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data,
(long)key_blob->len,
test3208 test3209 test3210 test3211 test3212 test3213 test3214 test3215 \
test3216 test3217 test3218 test3219 test3220 \
\
-test3300 test3301 test3302 \
+test3300 test3301 test3302 test3303 test3304 \
\
test4000 test4001
--- /dev/null
+<?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>
--- /dev/null
+<?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>
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
--- /dev/null
+/***************************************************************************
+ * _ _ ____ _
+ * 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
+}
--- /dev/null
+/***************************************************************************
+ * _ _ ____ _
+ * 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
+}