From ef37a3acf7d8bac5bd6342e91474f9e2658295d0 Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Mon, 31 Aug 2026 15:51:22 -0400 Subject: [PATCH 1/2] Implement custom certificate handler option --- include/curl/curl.h | 23 +++++++++++++++++++++++ lib/easyoptions.c | 5 ++++- lib/setopt.c | 22 ++++++++++++++++++++++ lib/urldata.h | 6 ++++++ lib/vtls/openssl.c | 14 ++++++++++++++ lib/vtls/schannel.c | 6 +++++- lib/vtls/schannel_verify.c | 8 ++++++++ lib/vtls/unitytls.c | 34 ++++++++++++++++++++++++++++++++++ lib/vtls/vtls.c | 35 +++++++++++++++++++++++++++++++++++ lib/vtls/vtls.h | 11 +++++++++++ lib/vtls/vtls_scache.c | 6 ++++++ 11 files changed, 168 insertions(+), 2 deletions(-) diff --git a/include/curl/curl.h b/include/curl/curl.h index f38bf97cba6e..3bf4c11de791 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -788,6 +788,17 @@ typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ mbedtls_ssl_config */ void *userptr); +/* Unity extension: CURLOPT_UNITY_CERTVERIFY_FUNCTION callback prototype. + Called with the DER-encoded leaf certificate of the peer once it is + available, and fully replaces the TLS backend's own peer verification. + Return CURLE_OK to accept the peer or CURLE_PEER_FAILED_VERIFICATION to + reject it. Unlike curl_ssl_ctx_callback this carries no backend-specific + types, so one implementation serves every backend. */ +typedef CURLcode (*curl_unity_certverify_callback)(CURL *curl, + const unsigned char *der, + size_t derlen, + void *userptr); + #define CURLPROXY_HTTP 0L /* added in 7.10, new in 7.19.4 default is to use CONNECT HTTP/1.1 */ #define CURLPROXY_HTTP_1_0 1L /* force to use CONNECT HTTP/1.0 @@ -2259,6 +2270,18 @@ typedef enum { /* set TLS supported signature algorithms */ CURLOPT(CURLOPT_SSL_SIGNATURE_ALGORITHMS, CURLOPTTYPE_STRINGPOINT, 328), + /* Unity extensions. Numbered from 900 to stay clear of upstream option + numbers, so a curl update can never silently collide with these. */ + + /* callback that verifies the peer certificate, replacing the backend's own + verification entirely */ + CURLOPT(CURLOPT_UNITY_CERTVERIFY_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 900), + + /* userdata passed to CURLOPT_UNITY_CERTVERIFY_FUNCTION. Also identifies the + verifier for connection reuse: connections are only shared between + transfers using the same callback and userdata. */ + CURLOPT(CURLOPT_UNITY_CERTVERIFY_DATA, CURLOPTTYPE_CBPOINT, 901), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/easyoptions.c b/lib/easyoptions.c index 4c0b4f0ce4ad..4475e657e630 100644 --- a/lib/easyoptions.c +++ b/lib/easyoptions.c @@ -355,6 +355,9 @@ const struct curl_easyoption Curl_easyopts[] = { { "TRAILERFUNCTION", CURLOPT_TRAILERFUNCTION, CURLOT_FUNCTION, 0 }, { "TRANSFERTEXT", CURLOPT_TRANSFERTEXT, CURLOT_LONG, 0 }, { "TRANSFER_ENCODING", CURLOPT_TRANSFER_ENCODING, CURLOT_LONG, 0 }, + { "UNITY_CERTVERIFY_DATA", CURLOPT_UNITY_CERTVERIFY_DATA, CURLOT_CBPTR, 0 }, + { "UNITY_CERTVERIFY_FUNCTION", CURLOPT_UNITY_CERTVERIFY_FUNCTION, + CURLOT_FUNCTION, 0 }, { "UNIX_SOCKET_PATH", CURLOPT_UNIX_SOCKET_PATH, CURLOT_STRING, 0 }, { "UNRESTRICTED_AUTH", CURLOPT_UNRESTRICTED_AUTH, CURLOT_LONG, 0 }, { "UPKEEP_INTERVAL_MS", CURLOPT_UPKEEP_INTERVAL_MS, CURLOT_LONG, 0 }, @@ -385,6 +388,6 @@ const struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { - return (CURLOPT_LASTENTRY % 10000) != (328 + 1); + return (CURLOPT_LASTENTRY % 10000) != (901 + 1); } #endif diff --git a/lib/setopt.c b/lib/setopt.c index b8a632748c7a..35ba8f04c8b3 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2074,6 +2074,16 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, */ s->ioctl_client = ptr; break; + case CURLOPT_UNITY_CERTVERIFY_DATA: + /* + * Unity: userdata for CURLOPT_UNITY_CERTVERIFY_FUNCTION. Might be NULL. + */ +#ifdef USE_SSL + s->ssl.primary.unity_certverify_userp = ptr; + break; +#else + return CURLE_NOT_BUILT_IN; +#endif case CURLOPT_SSL_CTX_DATA: /* * Set an SSL_CTX callback parameter pointer @@ -2622,6 +2632,18 @@ static CURLcode setopt_func(struct Curl_easy *data, CURLoption option, #endif return CURLE_NOT_BUILT_IN; + case CURLOPT_UNITY_CERTVERIFY_FUNCTION: + /* + * Unity: set a callback that verifies the peer certificate. + */ +#ifdef USE_SSL + s->ssl.primary.unity_certverify = + va_arg(param, curl_unity_certverify_callback); + break; +#else + return CURLE_NOT_BUILT_IN; +#endif + case CURLOPT_SOCKOPTFUNCTION: /* * socket callback function: called after socket() but before connect() diff --git a/lib/urldata.h b/lib/urldata.h index f35bfee053d0..664038633605 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -156,6 +156,12 @@ struct ssl_primary_config { char *password; /* TLS password (for, e.g., SRP) */ #endif char *curves; /* list of curves to use */ + /* Unity: peer verification callback and its userdata. These live in the + primary config rather than in ssl_config_data so that they take part in + the connection reuse check, i.e. a connection verified by one callback is + never reused by a transfer carrying a different one. */ + curl_unity_certverify_callback unity_certverify; + void *unity_certverify_userp; uint32_t version_max; /* max supported version the client wants to use */ uint8_t ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ uint8_t version; /* what version the client wants to use */ diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 4629ca444435..23e026bb3bed 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -4771,6 +4771,20 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, infof_certstack(data, octx->ssl); #endif + if(conn_config->unity_certverify) { + unsigned char *der = NULL; + int derlen = i2d_X509(server_cert, &der); + + if(derlen <= 0) { + failf(data, "SSL: could not DER encode peer certificate"); + result = CURLE_PEER_FAILED_VERIFICATION; + goto out; + } + result = Curl_unity_certverify(cf, data, der, (size_t)derlen); + OPENSSL_free(der); + goto out; + } + if(conn_config->verifyhost) { result = ossl_verifyhost(data, conn, peer, server_cert); if(result) diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index fb5ff0e9d8e6..bff6b78eb8d9 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -865,6 +865,9 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, else backend->use_manual_cred_validation = FALSE; + if(conn_config->unity_certverify) + backend->use_manual_cred_validation = TRUE; + backend->cred = NULL; /* check for an existing reusable credential handle */ @@ -1469,7 +1472,8 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, } } - if(conn_config->verifypeer && backend->use_manual_cred_validation) { + if(backend->use_manual_cred_validation && + (conn_config->verifypeer || conn_config->unity_certverify)) { /* Certificate verification also verifies the hostname if verifyhost */ return Curl_verify_certificate(cf, data); } diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index 9be6fe311ccd..be4a2719bf83 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -684,6 +684,14 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, result = CURLE_PEER_FAILED_VERIFICATION; } + if(result == CURLE_OK && conn_config->unity_certverify) { + result = Curl_unity_certverify(cf, data, + pCertContextServer->pbCertEncoded, + pCertContextServer->cbCertEncoded); + CertFreeCertificateContext(pCertContextServer); + return result; + } + if(result == CURLE_OK && (conn_config->CAfile || conn_config->ca_info_blob) && BACKEND->use_manual_cred_validation) { diff --git a/lib/vtls/unitytls.c b/lib/vtls/unitytls.c index bbbdec49737e..d6c7ffb2f106 100644 --- a/lib/vtls/unitytls.c +++ b/lib/vtls/unitytls.c @@ -290,6 +290,40 @@ static unitytls_x509verify_result unitytls_on_verify(void* userData, unitytls_x5 const char* const hostname = connssl->peer.hostname; unitytls_x509verify_result verify_result = UNITYTLS_X509VERIFY_SUCCESS; + if(conn_config->unity_certverify) { + const unitytls_x509verify_result fatal = + (unitytls_x509verify_result)UNITYTLS_X509VERIFY_FATAL_ERROR; + unitytls_x509_ref leaf; + size_t derlen; + UInt8* der; + CURLcode result; + + leaf = unitytls->unitytls_x509list_get_x509(chain, 0, errorState); + if(errorState->code != UNITYTLS_SUCCESS) + return fatal; + + derlen = unitytls->unitytls_x509_export_der(leaf, NULL, 0, errorState); + if(errorState->code != UNITYTLS_SUCCESS || !derlen) + return fatal; + + der = curlx_malloc(derlen); + if(!der) + return fatal; + + unitytls->unitytls_x509_export_der(leaf, der, derlen, errorState); + if(errorState->code != UNITYTLS_SUCCESS) { + curlx_free(der); + return fatal; + } + + result = Curl_unity_certverify(cf, CF_DATA_CURRENT(cf), der, derlen); + curlx_free(der); + + if(result) + return (unitytls_x509verify_result)UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED; + return (unitytls_x509verify_result)UNITYTLS_X509VERIFY_SUCCESS; + } + /* According to documentation the options verifypeer and verifyhost are independent of each other! */ /* UnityTls however, verifies both the certificate as well as the hostname in the same call. */ if(verifypeer || verifyhost) { diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index a3c6892191f4..9dd6d12827fd 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -61,6 +61,7 @@ #include "vtls/unitytls.h" /* UnityTls version */ #include "slist.h" +#include "multiif.h" #include "curl_trc.h" #include "strcase.h" #include "url.h" @@ -203,6 +204,8 @@ static bool match_ssl_primary_config(struct Curl_easy *data, (c1->verifypeer == c2->verifypeer) && (c1->verifyhost == c2->verifyhost) && (c1->verifystatus == c2->verifystatus) && + (c1->unity_certverify == c2->unity_certverify) && + (c1->unity_certverify_userp == c2->unity_certverify_userp) && blobcmp(c1->cert_blob, c2->cert_blob) && blobcmp(c1->ca_info_blob, c2->ca_info_blob) && blobcmp(c1->issuercert_blob, c2->issuercert_blob) && @@ -225,6 +228,36 @@ static bool match_ssl_primary_config(struct Curl_easy *data, return FALSE; } +CURLcode Curl_unity_certverify(struct Curl_cfilter *cf, + struct Curl_easy *data, + const unsigned char *der, + size_t derlen) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + CURLcode result; + + DEBUGASSERT(conn_config->unity_certverify); + + /* Fail closed: without a certificate the callback cannot make a decision. */ + if(!der || !derlen) { + failf(data, "TLS: no peer certificate to verify"); + return CURLE_PEER_FAILED_VERIFICATION; + } + + Curl_set_in_callback(data, TRUE); + result = conn_config->unity_certverify(data, der, derlen, + conn_config->unity_certverify_userp); + Curl_set_in_callback(data, FALSE); + + if(result) { + failf(data, "TLS: peer certificate rejected by verification callback"); + return CURLE_PEER_FAILED_VERIFICATION; + } + + infof(data, "TLS: peer certificate accepted by verification callback"); + return CURLE_OK; +} + bool Curl_ssl_conn_config_match(struct Curl_easy *data, struct connectdata *candidate, bool proxy) @@ -250,6 +283,8 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source, dest->verifystatus = source->verifystatus; dest->cache_session = source->cache_session; dest->ssl_options = source->ssl_options; + dest->unity_certverify = source->unity_certverify; + dest->unity_certverify_userp = source->unity_certverify_userp; CLONE_BLOB(cert_blob); CLONE_BLOB(ca_info_blob); diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index 6db67cf7482e..cc5b8c45d376 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -144,6 +144,17 @@ bool Curl_ssl_conn_config_match(struct Curl_easy *data, * `verifyhost` and `verifystatus`. */ void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); +/** + * Unity: hand the DER-encoded peer certificate to the + * CURLOPT_UNITY_CERTVERIFY_FUNCTION callback and translate its answer into a + * CURLcode. Only call this when `unity_certverify` is set on the connection's + * primary SSL config; it then replaces the backend's own peer verification. + */ +CURLcode Curl_unity_certverify(struct Curl_cfilter *cf, + struct Curl_easy *data, + const unsigned char *der, + size_t derlen); + /** * Init SSL peer information for filter. Can be called repeatedly. */ diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 59fa256bc31d..4cbfad02e521 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -186,6 +186,12 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, if(r) goto out; } + if(ssl->unity_certverify) { + r = curlx_dyn_addf(&buf, ":UNITY-CERTVERIFY-%p", + ssl->unity_certverify_userp); + if(r) + goto out; + } if(!ssl->verifypeer || !ssl->verifyhost) { if(cf->conn->bits.conn_to_host) { r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name); From 78e614a79da294761f2cca903d1c26a8e2dc9c8a Mon Sep 17 00:00:00 2001 From: Simon Lemay Date: Tue, 1 Sep 2026 12:17:04 -0400 Subject: [PATCH 2/2] Guard new code behind UNITY_CERTVERIFY --- include/curl/curl.h | 4 ++++ lib/easyoptions.c | 6 ++++++ lib/setopt.c | 4 ++++ lib/urldata.h | 2 ++ lib/vtls/openssl.c | 2 ++ lib/vtls/schannel.c | 11 +++++++++++ lib/vtls/schannel_verify.c | 2 ++ lib/vtls/unitytls.c | 2 ++ lib/vtls/vtls.c | 8 ++++++++ lib/vtls/vtls.h | 2 ++ lib/vtls/vtls_scache.c | 7 +++++++ 11 files changed, 50 insertions(+) diff --git a/include/curl/curl.h b/include/curl/curl.h index 3bf4c11de791..db2c53cf89f5 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -788,6 +788,7 @@ typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ mbedtls_ssl_config */ void *userptr); +#if UNITY_CERTVERIFY /* Unity extension: CURLOPT_UNITY_CERTVERIFY_FUNCTION callback prototype. Called with the DER-encoded leaf certificate of the peer once it is available, and fully replaces the TLS backend's own peer verification. @@ -798,6 +799,7 @@ typedef CURLcode (*curl_unity_certverify_callback)(CURL *curl, const unsigned char *der, size_t derlen, void *userptr); +#endif /* UNITY_CERTVERIFY */ #define CURLPROXY_HTTP 0L /* added in 7.10, new in 7.19.4 default is to use CONNECT HTTP/1.1 */ @@ -2270,6 +2272,7 @@ typedef enum { /* set TLS supported signature algorithms */ CURLOPT(CURLOPT_SSL_SIGNATURE_ALGORITHMS, CURLOPTTYPE_STRINGPOINT, 328), +#if UNITY_CERTVERIFY /* Unity extensions. Numbered from 900 to stay clear of upstream option numbers, so a curl update can never silently collide with these. */ @@ -2281,6 +2284,7 @@ typedef enum { verifier for connection reuse: connections are only shared between transfers using the same callback and userdata. */ CURLOPT(CURLOPT_UNITY_CERTVERIFY_DATA, CURLOPTTYPE_CBPOINT, 901), +#endif /* UNITY_CERTVERIFY */ CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/easyoptions.c b/lib/easyoptions.c index 4475e657e630..a559362c8e83 100644 --- a/lib/easyoptions.c +++ b/lib/easyoptions.c @@ -355,9 +355,11 @@ const struct curl_easyoption Curl_easyopts[] = { { "TRAILERFUNCTION", CURLOPT_TRAILERFUNCTION, CURLOT_FUNCTION, 0 }, { "TRANSFERTEXT", CURLOPT_TRANSFERTEXT, CURLOT_LONG, 0 }, { "TRANSFER_ENCODING", CURLOPT_TRANSFER_ENCODING, CURLOT_LONG, 0 }, +#if UNITY_CERTVERIFY { "UNITY_CERTVERIFY_DATA", CURLOPT_UNITY_CERTVERIFY_DATA, CURLOT_CBPTR, 0 }, { "UNITY_CERTVERIFY_FUNCTION", CURLOPT_UNITY_CERTVERIFY_FUNCTION, CURLOT_FUNCTION, 0 }, +#endif /* UNITY_CERTVERIFY */ { "UNIX_SOCKET_PATH", CURLOPT_UNIX_SOCKET_PATH, CURLOT_STRING, 0 }, { "UNRESTRICTED_AUTH", CURLOPT_UNRESTRICTED_AUTH, CURLOT_LONG, 0 }, { "UPKEEP_INTERVAL_MS", CURLOPT_UPKEEP_INTERVAL_MS, CURLOT_LONG, 0 }, @@ -388,6 +390,10 @@ const struct curl_easyoption Curl_easyopts[] = { */ int Curl_easyopts_check(void) { +#if UNITY_CERTVERIFY return (CURLOPT_LASTENTRY % 10000) != (901 + 1); +#else + return (CURLOPT_LASTENTRY % 10000) != (328 + 1); +#endif /* UNITY_CERTVERIFY */ } #endif diff --git a/lib/setopt.c b/lib/setopt.c index 35ba8f04c8b3..a23b5151a79f 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2074,6 +2074,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, */ s->ioctl_client = ptr; break; +#if UNITY_CERTVERIFY case CURLOPT_UNITY_CERTVERIFY_DATA: /* * Unity: userdata for CURLOPT_UNITY_CERTVERIFY_FUNCTION. Might be NULL. @@ -2084,6 +2085,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, #else return CURLE_NOT_BUILT_IN; #endif +#endif /* UNITY_CERTVERIFY */ case CURLOPT_SSL_CTX_DATA: /* * Set an SSL_CTX callback parameter pointer @@ -2632,6 +2634,7 @@ static CURLcode setopt_func(struct Curl_easy *data, CURLoption option, #endif return CURLE_NOT_BUILT_IN; +#if UNITY_CERTVERIFY case CURLOPT_UNITY_CERTVERIFY_FUNCTION: /* * Unity: set a callback that verifies the peer certificate. @@ -2643,6 +2646,7 @@ static CURLcode setopt_func(struct Curl_easy *data, CURLoption option, #else return CURLE_NOT_BUILT_IN; #endif +#endif /* UNITY_CERTVERIFY */ case CURLOPT_SOCKOPTFUNCTION: /* diff --git a/lib/urldata.h b/lib/urldata.h index 664038633605..83d21d954984 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -156,12 +156,14 @@ struct ssl_primary_config { char *password; /* TLS password (for, e.g., SRP) */ #endif char *curves; /* list of curves to use */ +#if UNITY_CERTVERIFY /* Unity: peer verification callback and its userdata. These live in the primary config rather than in ssl_config_data so that they take part in the connection reuse check, i.e. a connection verified by one callback is never reused by a transfer carrying a different one. */ curl_unity_certverify_callback unity_certverify; void *unity_certverify_userp; +#endif /* UNITY_CERTVERIFY */ uint32_t version_max; /* max supported version the client wants to use */ uint8_t ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */ uint8_t version; /* what version the client wants to use */ diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 23e026bb3bed..f3dc8c93bfaf 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -4771,6 +4771,7 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, infof_certstack(data, octx->ssl); #endif +#if UNITY_CERTVERIFY if(conn_config->unity_certverify) { unsigned char *der = NULL; int derlen = i2d_X509(server_cert, &der); @@ -4784,6 +4785,7 @@ CURLcode Curl_ossl_check_peer_cert(struct Curl_cfilter *cf, OPENSSL_free(der); goto out; } +#endif /* UNITY_CERTVERIFY */ if(conn_config->verifyhost) { result = ossl_verifyhost(data, conn, peer, server_cert); diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index bff6b78eb8d9..e1c8251af31c 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -865,8 +865,13 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, else backend->use_manual_cred_validation = FALSE; +#if UNITY_CERTVERIFY + /* Schannel must not reject the peer during the handshake when a verification + callback is installed; the callback has the final say. Manual validation + moves the decision into Curl_verify_certificate, where it is consulted. */ if(conn_config->unity_certverify) backend->use_manual_cred_validation = TRUE; +#endif /* UNITY_CERTVERIFY */ backend->cred = NULL; @@ -1472,8 +1477,14 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf, } } +#if UNITY_CERTVERIFY + /* A verification callback needs the peer certificate whether or not + verifypeer is set, and Curl_verify_certificate hands it over. */ if(backend->use_manual_cred_validation && (conn_config->verifypeer || conn_config->unity_certverify)) { +#else + if(conn_config->verifypeer && backend->use_manual_cred_validation) { +#endif /* UNITY_CERTVERIFY */ /* Certificate verification also verifies the hostname if verifyhost */ return Curl_verify_certificate(cf, data); } diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index be4a2719bf83..fc797349b0eb 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -684,6 +684,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, result = CURLE_PEER_FAILED_VERIFICATION; } +#if UNITY_CERTVERIFY if(result == CURLE_OK && conn_config->unity_certverify) { result = Curl_unity_certverify(cf, data, pCertContextServer->pbCertEncoded, @@ -691,6 +692,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, CertFreeCertificateContext(pCertContextServer); return result; } +#endif /* UNITY_CERTVERIFY */ if(result == CURLE_OK && (conn_config->CAfile || conn_config->ca_info_blob) && diff --git a/lib/vtls/unitytls.c b/lib/vtls/unitytls.c index d6c7ffb2f106..a6eed64e92ae 100644 --- a/lib/vtls/unitytls.c +++ b/lib/vtls/unitytls.c @@ -290,6 +290,7 @@ static unitytls_x509verify_result unitytls_on_verify(void* userData, unitytls_x5 const char* const hostname = connssl->peer.hostname; unitytls_x509verify_result verify_result = UNITYTLS_X509VERIFY_SUCCESS; +#if UNITY_CERTVERIFY if(conn_config->unity_certverify) { const unitytls_x509verify_result fatal = (unitytls_x509verify_result)UNITYTLS_X509VERIFY_FATAL_ERROR; @@ -323,6 +324,7 @@ static unitytls_x509verify_result unitytls_on_verify(void* userData, unitytls_x5 return (unitytls_x509verify_result)UNITYTLS_X509VERIFY_FLAG_NOT_TRUSTED; return (unitytls_x509verify_result)UNITYTLS_X509VERIFY_SUCCESS; } +#endif /* UNITY_CERTVERIFY */ /* According to documentation the options verifypeer and verifyhost are independent of each other! */ /* UnityTls however, verifies both the certificate as well as the hostname in the same call. */ diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 9dd6d12827fd..416fe9c74cde 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -61,7 +61,9 @@ #include "vtls/unitytls.h" /* UnityTls version */ #include "slist.h" +#if UNITY_CERTVERIFY #include "multiif.h" +#endif /* UNITY_CERTVERIFY */ #include "curl_trc.h" #include "strcase.h" #include "url.h" @@ -204,8 +206,10 @@ static bool match_ssl_primary_config(struct Curl_easy *data, (c1->verifypeer == c2->verifypeer) && (c1->verifyhost == c2->verifyhost) && (c1->verifystatus == c2->verifystatus) && +#if UNITY_CERTVERIFY (c1->unity_certverify == c2->unity_certverify) && (c1->unity_certverify_userp == c2->unity_certverify_userp) && +#endif /* UNITY_CERTVERIFY */ blobcmp(c1->cert_blob, c2->cert_blob) && blobcmp(c1->ca_info_blob, c2->ca_info_blob) && blobcmp(c1->issuercert_blob, c2->issuercert_blob) && @@ -228,6 +232,7 @@ static bool match_ssl_primary_config(struct Curl_easy *data, return FALSE; } +#if UNITY_CERTVERIFY CURLcode Curl_unity_certverify(struct Curl_cfilter *cf, struct Curl_easy *data, const unsigned char *der, @@ -257,6 +262,7 @@ CURLcode Curl_unity_certverify(struct Curl_cfilter *cf, infof(data, "TLS: peer certificate accepted by verification callback"); return CURLE_OK; } +#endif /* UNITY_CERTVERIFY */ bool Curl_ssl_conn_config_match(struct Curl_easy *data, struct connectdata *candidate, @@ -283,8 +289,10 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source, dest->verifystatus = source->verifystatus; dest->cache_session = source->cache_session; dest->ssl_options = source->ssl_options; +#if UNITY_CERTVERIFY dest->unity_certverify = source->unity_certverify; dest->unity_certverify_userp = source->unity_certverify_userp; +#endif /* UNITY_CERTVERIFY */ CLONE_BLOB(cert_blob); CLONE_BLOB(ca_info_blob); diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index cc5b8c45d376..5e415736b373 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -144,6 +144,7 @@ bool Curl_ssl_conn_config_match(struct Curl_easy *data, * `verifyhost` and `verifystatus`. */ void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); +#if UNITY_CERTVERIFY /** * Unity: hand the DER-encoded peer certificate to the * CURLOPT_UNITY_CERTVERIFY_FUNCTION callback and translate its answer into a @@ -154,6 +155,7 @@ CURLcode Curl_unity_certverify(struct Curl_cfilter *cf, struct Curl_easy *data, const unsigned char *der, size_t derlen); +#endif /* UNITY_CERTVERIFY */ /** * Init SSL peer information for filter. Can be called repeatedly. diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 4cbfad02e521..52c83698f9df 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -186,12 +186,19 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, if(r) goto out; } +#if UNITY_CERTVERIFY + /* A verification callback replaces the backend's own peer verification, and + on Schannel it also changes the credential flags cached under this key + (SCH_CRED_MANUAL_CRED_VALIDATION). Without this, a credential established + under a callback could be picked up by a transfer that has none, which + would then neither validate automatically nor call a callback. */ if(ssl->unity_certverify) { r = curlx_dyn_addf(&buf, ":UNITY-CERTVERIFY-%p", ssl->unity_certverify_userp); if(r) goto out; } +#endif /* UNITY_CERTVERIFY */ if(!ssl->verifypeer || !ssl->verifyhost) { if(cf->conn->bits.conn_to_host) { r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);