tls: fix incomplete mTLS config in conn reuse and session cache
No binary (build failed)
—
(log yok)
| @@ -326,8 +326,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) | |
| #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; | |
| @@ -150,9 +150,14 @@ struct ssl_primary_config { | |
| 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) */ | |
| @@ -172,11 +177,6 @@ struct ssl_config_data { | |
| 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 */ | |
| @@ -862,7 +862,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, | |
| /* 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; | |
| @@ -875,7 +875,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, | |
| 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", | |
| @@ -888,7 +888,7 @@ static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, | |
| } | |
| 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; | |
| @@ -1147,7 +1147,7 @@ static CURLcode ssh_state_pkey_init(struct Curl_easy *data, | |
| 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 = ""; | |
| @@ -996,10 +996,11 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, | |
| 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 " | |
| @@ -1017,14 +1018,15 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, | |
| 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; | |
| } | |
| @@ -486,7 +486,7 @@ static CURLcode mbed_load_cacert(struct Curl_cfilter *cf, | |
| 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]; | |
| @@ -581,7 +581,7 @@ static CURLcode mbed_load_clicert(struct Curl_cfilter *cf, | |
| 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]; | |
| @@ -662,12 +662,12 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, | |
| 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), | |
| @@ -677,8 +677,8 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, | |
| 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) || | |
| @@ -689,7 +689,7 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, | |
| 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 | |
| @@ -698,8 +698,8 @@ static CURLcode mbed_load_privkey(struct Curl_cfilter *cf, | |
| #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). */ | |
| @@ -933,7 +933,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf, | |
| #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); | |
| } | |
| @@ -3677,7 +3677,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, | |
| 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]; | |
| @@ -3841,8 +3841,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, | |
| 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; | |
| @@ -845,14 +845,14 @@ init_config_builder_client_auth(struct Curl_easy *data, | |
| 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; | |
| } | |
| @@ -866,8 +866,9 @@ init_config_builder_client_auth(struct Curl_easy *data, | |
| 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; | |
| } | |
| @@ -1066,7 +1067,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf, | |
| } | |
| } | |
| - | 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, | |
| @@ -415,8 +415,8 @@ static CURLcode get_client_cert(struct Curl_easy *data, | |
| } | |
| } | |
| - | 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); | |
| @@ -466,15 +466,15 @@ static CURLcode get_client_cert(struct Curl_easy *data, | |
| 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)); | |
| @@ -205,6 +205,7 @@ static bool match_ssl_primary_config(struct Curl_easy *data, | |
| 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) && | |
| @@ -218,7 +219,11 @@ static bool match_ssl_primary_config(struct Curl_easy *data, | |
| 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; | |
| @@ -253,6 +258,7 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source, | |
| 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); | |
| @@ -263,6 +269,10 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source, | |
| 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); | |
| @@ -283,9 +293,14 @@ static void free_primary_ssl_config(struct ssl_primary_config *sslc) | |
| 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); | |
| @@ -337,12 +352,12 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data) | |
| 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; | |
| @@ -378,12 +393,12 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data) | |
| 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]; | |
| @@ -50,6 +50,7 @@ | |
| 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; | |
| @@ -123,6 +124,48 @@ out: | |
| 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" | |
| @@ -134,12 +177,12 @@ static bool cf_ssl_peer_key_is_global(const char *peer_key) | |
| (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; | |
| @@ -188,10 +231,10 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, | |
| 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; | |
| } | |
| @@ -266,11 +309,9 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, | |
| 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"); | |
| @@ -301,6 +342,16 @@ out: | |
| 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; | |
| @@ -409,6 +460,7 @@ static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer) | |
| } | |
| 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); | |
| @@ -437,8 +489,8 @@ static void cf_ssl_cache_peer_update(struct Curl_ssl_scache_peer *peer) | |
| * - 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))); | |
| } | |
| @@ -447,6 +499,7 @@ static CURLcode | |
| 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, | |
| @@ -475,6 +528,11 @@ cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, | |
| 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) | |
| @@ -616,7 +674,7 @@ static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, | |
| 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) | |
| @@ -626,6 +684,8 @@ static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer, | |
| } | |
| 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)) | |
| @@ -754,6 +814,7 @@ static CURLcode cf_ssl_add_peer(struct Curl_easy *data, | |
| 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; | |
| @@ -765,7 +826,7 @@ static CURLcode cf_ssl_add_peer(struct Curl_easy *data, | |
| "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; | |
| @@ -1144,7 +1205,7 @@ CURLcode Curl_ssl_session_import(struct Curl_easy *data, | |
| 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; | |
| @@ -66,6 +66,22 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, | |
| 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. | |
| @@ -920,10 +920,10 @@ static CURLcode wssl_client_cert(struct Curl_easy *data, | |
| #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) { | |
| @@ -954,7 +954,7 @@ static CURLcode wssl_client_cert(struct Curl_easy *data, | |
| 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, | |
| @@ -968,8 +968,8 @@ static CURLcode wssl_client_cert(struct Curl_easy *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) { | |
| @@ -994,7 +994,7 @@ static CURLcode wssl_client_cert(struct Curl_easy *data, | |
| 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, | |
| @@ -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 | |
| @@ -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> |
| @@ -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 |
| @@ -0,0 +1,127 @@ | |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, 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 |
| + | } |
| @@ -0,0 +1,168 @@ | |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, 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 |
| + | } |