diff options
author | Curl Upstream <curl-library@lists.haxx.se> | 2023-02-20 07:24:52 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2023-02-21 14:54:46 (GMT) |
commit | 11ba4361aaecf2f1f82ef841146c4c90173d2aca (patch) | |
tree | 74bfabf9228b13168d5f822e360cd95a0ac581ae /lib/vtls/openssl.c | |
parent | dac458ddbf2b48168779821654a7e69cbd828c14 (diff) | |
download | CMake-11ba4361aaecf2f1f82ef841146c4c90173d2aca.zip CMake-11ba4361aaecf2f1f82ef841146c4c90173d2aca.tar.gz CMake-11ba4361aaecf2f1f82ef841146c4c90173d2aca.tar.bz2 |
curl 2023-02-20 (046209e5)
Code extracted from:
https://github.com/curl/curl.git
at commit 046209e561b7e9b5aab1aef7daebf29ee6e6e8c7 (curl-7_88_1).
Diffstat (limited to 'lib/vtls/openssl.c')
-rw-r--r-- | lib/vtls/openssl.c | 795 |
1 files changed, 331 insertions, 464 deletions
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index e7a1caa..6557783 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * 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 @@ -96,6 +96,7 @@ #include "curl_memory.h" #include "memdebug.h" + /* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS renegotiations when built with BoringSSL. Renegotiating is non-compliant with HTTP/2 and "an extremely dangerous protocol feature". Beware. @@ -260,6 +261,12 @@ #define HAVE_OPENSSL_VERSION #endif +#ifdef OPENSSL_IS_BORINGSSL +typedef uint32_t sslerr_t; +#else +typedef unsigned long sslerr_t; +#endif + /* * Whether the OpenSSL version has the API needed to support sharing an * X509_STORE between connections. The API is: @@ -277,17 +284,17 @@ #endif /* !LIBRESSL_VERSION_NUMBER */ struct ssl_backend_data { - struct Curl_easy *logger; /* transfer handle to pass trace logs to, only - using sockindex 0 */ /* these ones requires specific SSL-types */ SSL_CTX* ctx; SSL* handle; X509* server_cert; + BIO_METHOD *bio_method; CURLcode io_result; /* result of last BIO cfilter operation */ #ifndef HAVE_KEYLOG_CALLBACK /* Set to true once a valid keylog entry has been created to avoid dupes. */ bool keylog_done; #endif + bool x509_store_setup; /* x509 store has been set up */ }; #if defined(HAVE_SSL_X509_STORE_SHARE) @@ -702,14 +709,14 @@ static int bio_cf_out_write(BIO *bio, const char *buf, int blen) { struct Curl_cfilter *cf = BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; - struct Curl_easy *data = connssl->call_data; + struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nwritten; CURLcode result = CURLE_SEND_ERROR; DEBUGASSERT(data); nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result); - /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_out_write(len=%d) -> %d, err=%d"), - blen, (int)nwritten, result)); */ + DEBUGF(LOG_CF(data, cf, "bio_cf_out_write(len=%d) -> %d, err=%d", + blen, (int)nwritten, result)); BIO_clear_retry_flags(bio); connssl->backend->io_result = result; if(nwritten < 0) { @@ -723,7 +730,7 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen) { struct Curl_cfilter *cf = BIO_get_data(bio); struct ssl_connect_data *connssl = cf->ctx; - struct Curl_easy *data = connssl->call_data; + struct Curl_easy *data = CF_DATA_CURRENT(cf); ssize_t nread; CURLcode result = CURLE_RECV_ERROR; @@ -733,64 +740,75 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen) return 0; nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); - /* DEBUGF(infof(data, CFMSG(cf, "bio_cf_in_read(len=%d) -> %d, err=%d"), - blen, (int)nread, result)); */ + DEBUGF(LOG_CF(data, cf, "bio_cf_in_read(len=%d) -> %d, err=%d", + blen, (int)nread, result)); BIO_clear_retry_flags(bio); connssl->backend->io_result = result; if(nread < 0) { if(CURLE_AGAIN == result) BIO_set_retry_read(bio); } + + /* Before returning server replies to the SSL instance, we need + * to have setup the x509 store or verification will fail. */ + if(!connssl->backend->x509_store_setup) { + result = Curl_ssl_setup_x509_store(cf, data, connssl->backend->ctx); + if(result) { + connssl->backend->io_result = result; + return -1; + } + connssl->backend->x509_store_setup = TRUE; + } + return (int)nread; } -static BIO_METHOD *bio_cf_method = NULL; - #if USE_PRE_1_1_API static BIO_METHOD bio_cf_meth_1_0 = { - BIO_TYPE_MEM, - "OpenSSL CF BIO", - bio_cf_out_write, - bio_cf_in_read, - NULL, /* puts is never called */ - NULL, /* gets is never called */ - bio_cf_ctrl, - bio_cf_create, - bio_cf_destroy, - NULL + BIO_TYPE_MEM, + "OpenSSL CF BIO", + bio_cf_out_write, + bio_cf_in_read, + NULL, /* puts is never called */ + NULL, /* gets is never called */ + bio_cf_ctrl, + bio_cf_create, + bio_cf_destroy, + NULL }; -static void bio_cf_init_methods(void) +static BIO_METHOD *bio_cf_method_create(void) { - bio_cf_method = &bio_cf_meth_1_0; + return &bio_cf_meth_1_0; } -#define bio_cf_free_methods() Curl_nop_stmt +#define bio_cf_method_free(m) Curl_nop_stmt #else -static void bio_cf_init_methods(void) +static BIO_METHOD *bio_cf_method_create(void) { - bio_cf_method = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO"); - BIO_meth_set_write(bio_cf_method, &bio_cf_out_write); - BIO_meth_set_read(bio_cf_method, &bio_cf_in_read); - BIO_meth_set_ctrl(bio_cf_method, &bio_cf_ctrl); - BIO_meth_set_create(bio_cf_method, &bio_cf_create); - BIO_meth_set_destroy(bio_cf_method, &bio_cf_destroy); + BIO_METHOD *m = BIO_meth_new(BIO_TYPE_MEM, "OpenSSL CF BIO"); + if(m) { + BIO_meth_set_write(m, &bio_cf_out_write); + BIO_meth_set_read(m, &bio_cf_in_read); + BIO_meth_set_ctrl(m, &bio_cf_ctrl); + BIO_meth_set_create(m, &bio_cf_create); + BIO_meth_set_destroy(m, &bio_cf_destroy); + } + return m; } -static void bio_cf_free_methods(void) +static void bio_cf_method_free(BIO_METHOD *m) { - BIO_meth_free(bio_cf_method); + if(m) + BIO_meth_free(m); } #endif -static bool ossl_attach_data(struct Curl_cfilter *cf, - struct Curl_easy *data); - /* * Number of bytes to read from the random number seed file. This must be * a finite value (because some entropy "files" like /dev/urandom have @@ -922,54 +940,6 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size) return buf; } -/* Return an extra data index for the transfer data. - * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). - */ -static int ossl_get_ssl_data_index(void) -{ - static int ssl_ex_data_data_index = -1; - if(ssl_ex_data_data_index < 0) { - ssl_ex_data_data_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - } - return ssl_ex_data_data_index; -} - -/* Return an extra data index for the associated Curl_cfilter instance. - * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). - */ -static int ossl_get_ssl_cf_index(void) -{ - static int ssl_ex_data_cf_index = -1; - if(ssl_ex_data_cf_index < 0) { - ssl_ex_data_cf_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - } - return ssl_ex_data_cf_index; -} - -/* Return an extra data index for the sockindex. - * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). - */ -static int ossl_get_ssl_sockindex_index(void) -{ - static int sockindex_index = -1; - if(sockindex_index < 0) { - sockindex_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - } - return sockindex_index; -} - -/* Return an extra data index for proxy boolean. - * This index can be used with SSL_get_ex_data() and SSL_set_ex_data(). - */ -static int ossl_get_proxy_index(void) -{ - static int proxy_index = -1; - if(proxy_index < 0) { - proxy_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); - } - return proxy_index; -} - static int passwd_callback(char *buf, int num, int encrypting, void *global_passwd) { @@ -1246,7 +1216,7 @@ SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob, if(ret) { X509 *ca; - unsigned long err; + sslerr_t err; if(!SSL_CTX_clear_chain_certs(ctx)) { ret = 0; @@ -1768,14 +1738,8 @@ static int ossl_init(void) OpenSSL_add_all_algorithms(); #endif - bio_cf_init_methods(); Curl_tls_keylog_open(); - /* Initialize the extra data indexes */ - if(ossl_get_ssl_data_index() < 0 || ossl_get_ssl_cf_index() < 0 || - ossl_get_ssl_sockindex_index() < 0 || ossl_get_proxy_index() < 0) - return 0; - return 1; } @@ -1814,7 +1778,6 @@ static void ossl_cleanup(void) #endif Curl_tls_keylog_close(); - bio_cf_free_methods(); } /* @@ -1832,7 +1795,10 @@ static int ossl_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data) #ifdef MSG_PEEK char buf; ssize_t nread; - nread = recv((RECV_TYPE_ARG1)cf->conn->sock[cf->sockindex], + curl_socket_t sock = Curl_conn_cf_get_socket(cf, data); + if(sock == CURL_SOCKET_BAD) + return 0; /* no socket, consider closed */ + nread = recv((RECV_TYPE_ARG1)sock, (RECV_TYPE_ARG2)&buf, (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK); if(nread == 0) @@ -1960,19 +1926,15 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data) return list; } -#define set_logger(connssl, data) \ - connssl->backend->logger = data - static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; struct ssl_backend_data *backend = connssl->backend; + (void)data; DEBUGASSERT(backend); if(backend->handle) { - set_logger(connssl, data); - if(cf->next && cf->next->connected) { char buf[32]; /* Maybe the server has already sent a close notify alert. @@ -1989,6 +1951,11 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) if(backend->ctx) { SSL_CTX_free(backend->ctx); backend->ctx = NULL; + backend->x509_store_setup = FALSE; + } + if(backend->bio_method) { + bio_cf_method_free(backend->bio_method); + backend->bio_method = NULL; } } @@ -2026,7 +1993,7 @@ static int ossl_shutdown(struct Curl_cfilter *cf, if(backend->handle) { buffsize = (int)sizeof(buf); while(!done && loop--) { - int what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], + int what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data), SSL_SHUTDOWN_TIMEOUT); if(what > 0) { ERR_clear_error(); @@ -2155,6 +2122,22 @@ static bool subj_alt_hostcheck(struct Curl_easy *data, return FALSE; } +static CURLcode +ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + X509 *server_cert, const char *hostname, + const char *dispname); + +CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + X509 *server_cert) +{ + const char *hostname, *dispname; + int port; + + (void)conn; + Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port); + return ossl_verifyhost(data, conn, server_cert, hostname, dispname); +} + /* Quote from RFC2818 section 3.1 "Server Identity" If a subjectAltName extension of type dNSName is present, that MUST @@ -2177,8 +2160,10 @@ static bool subj_alt_hostcheck(struct Curl_easy *data, This function is now used from ngtcp2 (QUIC) as well. */ -CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - X509 *server_cert) +static CURLcode +ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + X509 *server_cert, const char *hostname, + const char *dispname) { bool matched = FALSE; int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */ @@ -2192,12 +2177,9 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, CURLcode result = CURLE_OK; bool dNSName = FALSE; /* if a dNSName field exists in the cert */ bool iPAddress = FALSE; /* if a iPAddress field exists in the cert */ - const char *hostname, *dispname; - int port; size_t hostlen; (void)conn; - Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &dispname, &port); hostlen = strlen(hostname); #ifndef ENABLE_IPV6 @@ -2660,24 +2642,15 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, const void *buf, size_t len, SSL *ssl, void *userp) { - char unknown[32]; - const char *verstr = NULL; - struct connectdata *conn = userp; - int cf_idx = ossl_get_ssl_cf_index(); - struct ssl_connect_data *connssl; + const char *verstr = "???"; + struct Curl_cfilter *cf = userp; struct Curl_easy *data = NULL; - struct Curl_cfilter *cf; - - DEBUGASSERT(cf_idx >= 0); - cf = (struct Curl_cfilter*) SSL_get_ex_data(ssl, cf_idx); - DEBUGASSERT(cf); - connssl = cf->ctx; - DEBUGASSERT(connssl); - DEBUGASSERT(connssl->backend); - data = connssl->backend->logger; + char unknown[32]; - if(!conn || !data || !data->set.fdebug || - (direction != 0 && direction != 1)) + if(!cf) + return; + data = CF_DATA_CURRENT(cf); + if(!data || !data->set.fdebug || (direction && direction != 1)) return; switch(ssl_ver) { @@ -2722,6 +2695,9 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, * For TLS 1.3, skip notification of the decrypted inner Content-Type. */ if(ssl_ver +#ifdef SSL3_RT_HEADER + && content_type != SSL3_RT_HEADER +#endif #ifdef SSL3_RT_INNER_CONTENT_TYPE && content_type != SSL3_RT_INNER_CONTENT_TYPE #endif @@ -2757,7 +2733,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, } txt_len = msnprintf(ssl_buf, sizeof(ssl_buf), - CFMSG(cf, "%s (%s), %s, %s (%d):\n"), + "%s (%s), %s, %s (%d):\n", verstr, direction?"OUT":"IN", tls_rt_name, msg_name, msg_type); if(0 <= txt_len && (unsigned)txt_len < sizeof(ssl_buf)) { @@ -2967,21 +2943,14 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) struct Curl_easy *data; struct Curl_cfilter *cf; const struct ssl_config_data *config; - curl_socket_t *sockindex_ptr; - int data_idx = ossl_get_ssl_data_index(); - int cf_idx = ossl_get_ssl_cf_index(); - int sockindex_idx = ossl_get_ssl_sockindex_index(); - int proxy_idx = ossl_get_proxy_index(); + struct ssl_connect_data *connssl; bool isproxy; - if(data_idx < 0 || cf_idx < 0 || sockindex_idx < 0 || proxy_idx < 0) - return 0; - - cf = (struct Curl_cfilter*) SSL_get_ex_data(ssl, cf_idx); - data = (struct Curl_easy *) SSL_get_ex_data(ssl, data_idx); + cf = (struct Curl_cfilter*) SSL_get_app_data(ssl); + connssl = cf? cf->ctx : NULL; + data = connssl? CF_DATA_CURRENT(cf) : NULL; /* The sockindex has been stored as a pointer to an array element */ - sockindex_ptr = (curl_socket_t*) SSL_get_ex_data(ssl, sockindex_idx); - if(!cf || !data || !sockindex_ptr) + if(!cf || !data) return 0; isproxy = Curl_ssl_cf_is_proxy(cf); @@ -3075,7 +3044,7 @@ static CURLcode load_cacert_from_memory(X509_STORE *store, BIO_free(cbio); /* if we didn't end up importing anything, treat that as an error */ - return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE); + return (count > 0) ? CURLE_OK : CURLE_SSL_CACERT_BADFILE; } static CURLcode populate_x509_store(struct Curl_cfilter *cf, @@ -3094,206 +3063,219 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, const char * const ssl_crlfile = ssl_config->primary.CRLfile; const bool verifypeer = conn_config->verifypeer; bool imported_native_ca = false; + bool imported_ca_info_blob = false; if(!store) return CURLE_OUT_OF_MEMORY; + if(verifypeer) { #if defined(USE_WIN32_CRYPTO) - /* Import certificates from the Windows root certificate store if requested. - https://stackoverflow.com/questions/9507184/ - https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 - https://datatracker.ietf.org/doc/html/rfc5280 */ - if((conn_config->verifypeer || conn_config->verifyhost) && - (ssl_config->native_ca_store)) { - HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT")); - - if(hStore) { - PCCERT_CONTEXT pContext = NULL; - /* The array of enhanced key usage OIDs will vary per certificate and is - declared outside of the loop so that rather than malloc/free each - iteration we can grow it with realloc, when necessary. */ - CERT_ENHKEY_USAGE *enhkey_usage = NULL; - DWORD enhkey_usage_size = 0; - - /* This loop makes a best effort to import all valid certificates from - the MS root store. If a certificate cannot be imported it is skipped. - 'result' is used to store only hard-fail conditions (such as out of - memory) that cause an early break. */ - result = CURLE_OK; - for(;;) { - X509 *x509; - FILETIME now; - BYTE key_usage[2]; - DWORD req_size; - const unsigned char *encoded_cert; + /* Import certificates from the Windows root certificate store if + requested. + https://stackoverflow.com/questions/9507184/ + https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037 + https://datatracker.ietf.org/doc/html/rfc5280 */ + if(ssl_config->native_ca_store) { + HCERTSTORE hStore = CertOpenSystemStore(0, TEXT("ROOT")); + + if(hStore) { + PCCERT_CONTEXT pContext = NULL; + /* The array of enhanced key usage OIDs will vary per certificate and + is declared outside of the loop so that rather than malloc/free each + iteration we can grow it with realloc, when necessary. */ + CERT_ENHKEY_USAGE *enhkey_usage = NULL; + DWORD enhkey_usage_size = 0; + + /* This loop makes a best effort to import all valid certificates from + the MS root store. If a certificate cannot be imported it is + skipped. 'result' is used to store only hard-fail conditions (such + as out of memory) that cause an early break. */ + result = CURLE_OK; + for(;;) { + X509 *x509; + FILETIME now; + BYTE key_usage[2]; + DWORD req_size; + const unsigned char *encoded_cert; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - char cert_name[256]; + char cert_name[256]; #endif - pContext = CertEnumCertificatesInStore(hStore, pContext); - if(!pContext) - break; + pContext = CertEnumCertificatesInStore(hStore, pContext); + if(!pContext) + break; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, - NULL, cert_name, sizeof(cert_name))) { - strcpy(cert_name, "Unknown"); - } - infof(data, "SSL: Checking cert \"%s\"", cert_name); + if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, + NULL, cert_name, sizeof(cert_name))) { + strcpy(cert_name, "Unknown"); + } + infof(data, "SSL: Checking cert \"%s\"", cert_name); #endif + encoded_cert = (const unsigned char *)pContext->pbCertEncoded; + if(!encoded_cert) + continue; - encoded_cert = (const unsigned char *)pContext->pbCertEncoded; - if(!encoded_cert) - continue; - - GetSystemTimeAsFileTime(&now); - if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 || - CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0) - continue; - - /* If key usage exists check for signing attribute */ - if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType, - pContext->pCertInfo, - key_usage, sizeof(key_usage))) { - if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE)) + GetSystemTimeAsFileTime(&now); + if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 || + CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0) continue; - } - else if(GetLastError()) - continue; - - /* If enhanced key usage exists check for server auth attribute. - * - * Note "In a Microsoft environment, a certificate might also have EKU - * extended properties that specify valid uses for the certificate." - * The call below checks both, and behavior varies depending on what is - * found. For more details see CertGetEnhancedKeyUsage doc. - */ - if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) { - if(req_size && req_size > enhkey_usage_size) { - void *tmp = realloc(enhkey_usage, req_size); - - if(!tmp) { - failf(data, "SSL: Out of memory allocating for OID list"); - result = CURLE_OUT_OF_MEMORY; - break; - } - enhkey_usage = (CERT_ENHKEY_USAGE *)tmp; - enhkey_usage_size = req_size; + /* If key usage exists check for signing attribute */ + if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType, + pContext->pCertInfo, + key_usage, sizeof(key_usage))) { + if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE)) + continue; } + else if(GetLastError()) + continue; - if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) { - if(!enhkey_usage->cUsageIdentifier) { - /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate is - good for all uses. If it returns zero, the certificate has no - valid uses." */ - if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND) - continue; + /* If enhanced key usage exists check for server auth attribute. + * + * Note "In a Microsoft environment, a certificate might also have + * EKU extended properties that specify valid uses for the + * certificate." The call below checks both, and behavior varies + * depending on what is found. For more details see + * CertGetEnhancedKeyUsage doc. + */ + if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) { + if(req_size && req_size > enhkey_usage_size) { + void *tmp = realloc(enhkey_usage, req_size); + + if(!tmp) { + failf(data, "SSL: Out of memory allocating for OID list"); + result = CURLE_OUT_OF_MEMORY; + break; + } + + enhkey_usage = (CERT_ENHKEY_USAGE *)tmp; + enhkey_usage_size = req_size; } - else { - DWORD i; - bool found = false; - - for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) { - if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */, - enhkey_usage->rgpszUsageIdentifier[i])) { - found = true; - break; - } + + if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) { + if(!enhkey_usage->cUsageIdentifier) { + /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate + is good for all uses. If it returns zero, the certificate + has no valid uses." */ + if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND) + continue; } + else { + DWORD i; + bool found = false; + + for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) { + if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */, + enhkey_usage->rgpszUsageIdentifier[i])) { + found = true; + break; + } + } - if(!found) - continue; + if(!found) + continue; + } } + else + continue; } else continue; - } - else - continue; - x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); - if(!x509) - continue; + x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded); + if(!x509) + continue; - /* Try to import the certificate. This may fail for legitimate reasons - such as duplicate certificate, which is allowed by MS but not - OpenSSL. */ - if(X509_STORE_add_cert(store, x509) == 1) { + /* Try to import the certificate. This may fail for legitimate + reasons such as duplicate certificate, which is allowed by MS but + not OpenSSL. */ + if(X509_STORE_add_cert(store, x509) == 1) { #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - infof(data, "SSL: Imported cert \"%s\"", cert_name); + infof(data, "SSL: Imported cert \"%s\"", cert_name); #endif - imported_native_ca = true; + imported_native_ca = true; + } + X509_free(x509); } - X509_free(x509); - } - free(enhkey_usage); - CertFreeCertificateContext(pContext); - CertCloseStore(hStore, 0); + free(enhkey_usage); + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); - if(result) - return result; + if(result) + return result; + } + if(imported_native_ca) + infof(data, "successfully imported Windows CA store"); + else + infof(data, "error importing Windows CA store, continuing anyway"); } - if(imported_native_ca) - infof(data, "successfully imported Windows CA store"); - else - infof(data, "error importing Windows CA store, continuing anyway"); - } #endif - - if(ca_info_blob) { - result = load_cacert_from_memory(store, ca_info_blob); - if(result) { - if(result == CURLE_OUT_OF_MEMORY || - (verifypeer && !imported_native_ca)) { + if(ca_info_blob) { + result = load_cacert_from_memory(store, ca_info_blob); + if(result) { failf(data, "error importing CA certificate blob"); return result; } - /* Only warn if no certificate verification is required. */ - infof(data, "error importing CA certificate blob, continuing anyway"); + else { + imported_ca_info_blob = true; + infof(data, "successfully imported CA certificate blob"); + } } - } - if(verifypeer && !imported_native_ca && (ssl_cafile || ssl_capath)) { + if(ssl_cafile || ssl_capath) { #if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) - /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ - if(ssl_cafile && - !X509_STORE_load_file(store, ssl_cafile)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate file: %s", ssl_cafile); - return CURLE_SSL_CACERT_BADFILE; - } - if(ssl_capath && - !X509_STORE_load_path(store, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate path: %s", ssl_capath); - return CURLE_SSL_CACERT_BADFILE; - } + /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ + if(ssl_cafile && !X509_STORE_load_file(store, ssl_cafile)) { + if(!imported_native_ca && !imported_ca_info_blob) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate file: %s", ssl_cafile); + return CURLE_SSL_CACERT_BADFILE; + } + else + infof(data, "error setting certificate file, continuing anyway"); + } + if(ssl_capath && !X509_STORE_load_path(store, ssl_capath)) { + if(!imported_native_ca && !imported_ca_info_blob) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate path: %s", ssl_capath); + return CURLE_SSL_CACERT_BADFILE; + } + else + infof(data, "error setting certificate path, continuing anyway"); + } #else - /* tell OpenSSL where to find CA certificates that are used to verify the - server's certificate. */ - if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) { - /* Fail if we insist on successfully verifying the server. */ - failf(data, "error setting certificate verify locations:" - " CAfile: %s CApath: %s", - ssl_cafile ? ssl_cafile : "none", - ssl_capath ? ssl_capath : "none"); - return CURLE_SSL_CACERT_BADFILE; - } + /* tell OpenSSL where to find CA certificates that are used to verify the + server's certificate. */ + if(!X509_STORE_load_locations(store, ssl_cafile, ssl_capath)) { + if(!imported_native_ca && !imported_ca_info_blob) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate verify locations:" + " CAfile: %s CApath: %s", + ssl_cafile ? ssl_cafile : "none", + ssl_capath ? ssl_capath : "none"); + return CURLE_SSL_CACERT_BADFILE; + } + else { + infof(data, "error setting certificate verify locations," + " continuing anyway"); + } + } #endif - infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); - infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); - } + infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none"); + infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none"); + } #ifdef CURL_CA_FALLBACK - if(verifypeer && - !ca_info_blob && !ssl_cafile && !ssl_capath && !imported_native_ca) { - /* verifying the peer without any CA certificates won't - work so use openssl's built-in default as fallback */ - X509_STORE_set_default_paths(store); - } + if(!ssl_cafile && !ssl_capath && + !imported_native_ca && !imported_ca_info_blob) { + /* verifying the peer without any CA certificates won't + work so use openssl's built-in default as fallback */ + X509_STORE_set_default_paths(store); + } #endif + } if(ssl_crlfile) { /* tell OpenSSL where to find CRL file that is used to check certificate @@ -3424,9 +3406,9 @@ static void set_cached_x509_store(struct Curl_cfilter *cf, } } -static CURLcode set_up_x509_store(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_backend_data *backend) +CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + SSL_CTX *ssl_ctx) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); @@ -3446,10 +3428,10 @@ static CURLcode set_up_x509_store(struct Curl_cfilter *cf, cached_store = get_cached_x509_store(cf, data); if(cached_store && cache_criteria_met && X509_STORE_up_ref(cached_store)) { - SSL_CTX_set_cert_store(backend->ctx, cached_store); + SSL_CTX_set_cert_store(ssl_ctx, cached_store); } else { - X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); + X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); result = populate_x509_store(cf, data, store); if(result == CURLE_OK && cache_criteria_met) { @@ -3460,11 +3442,11 @@ static CURLcode set_up_x509_store(struct Curl_cfilter *cf, return result; } #else /* HAVE_SSL_X509_STORE_SHARE */ -static CURLcode set_up_x509_store(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct ssl_backend_data *backend) +CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + SSL_CTX *ssl_ctx) { - X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); + X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx); return populate_x509_store(cf, data, store); } @@ -3494,9 +3476,6 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, #endif #endif const long int ssl_version = conn_config->version; -#ifdef USE_OPENSSL_SRP - const enum CURL_TLSAUTH ssl_authtype = ssl_config->primary.authtype; -#endif 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; @@ -3564,8 +3543,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, if(data->set.fdebug && data->set.verbose) { /* the SSL trace callback is only used for verbose logging */ SSL_CTX_set_msg_callback(backend->ctx, ossl_trace); - SSL_CTX_set_msg_callback_arg(backend->ctx, cf->conn); - set_logger(connssl, data); + SSL_CTX_set_msg_callback_arg(backend->ctx, cf); } #endif @@ -3661,36 +3639,17 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, SSL_CTX_set_options(backend->ctx, ctx_options); #ifdef HAS_ALPN - if(cf->conn->bits.tls_enable_alpn) { - int cur = 0; - unsigned char protocols[128]; - -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2 -#ifndef CURL_DISABLE_PROXY - && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy) -#endif - ) { - protocols[cur++] = ALPN_H2_LENGTH; - - memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH); - cur += ALPN_H2_LENGTH; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); - } -#endif - - protocols[cur++] = ALPN_HTTP_1_1_LENGTH; - memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); - cur += ALPN_HTTP_1_1_LENGTH; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); + if(connssl->alpn) { + struct alpn_proto_buf proto; - /* expects length prefixed preference ordered list of protocols in wire - * format - */ - if(SSL_CTX_set_alpn_protos(backend->ctx, protocols, cur)) { + result = Curl_alpn_to_proto_buf(&proto, connssl->alpn); + if(result || + SSL_CTX_set_alpn_protos(backend->ctx, proto.data, proto.len)) { failf(data, "Error setting ALPN"); return CURLE_SSL_CONNECT_ERROR; } + Curl_alpn_to_proto_str(&proto, connssl->alpn); + infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); } #endif @@ -3748,8 +3707,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, #endif #ifdef USE_OPENSSL_SRP - if((ssl_authtype == CURL_TLSAUTH_SRP) && - Curl_auth_allowed_to_host(data)) { + if(ssl_config->primary.username && Curl_auth_allowed_to_host(data)) { char * const ssl_username = ssl_config->primary.username; char * const ssl_password = ssl_config->primary.password; infof(data, "Using TLS-SRP username: %s", ssl_username); @@ -3773,10 +3731,6 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } #endif - result = set_up_x509_store(cf, data, backend); - if(result) - return result; - /* OpenSSL always tries to verify the peer, this only says whether it should * fail to connect if the verification fails, or if it should continue * anyway. In the latter case the result of the verification is checked with @@ -3820,6 +3774,8 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, return CURLE_OUT_OF_MEMORY; } + SSL_set_app_data(backend->handle, cf); + #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) if(conn_config->verifystatus) @@ -3847,13 +3803,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, } #endif - if(!ossl_attach_data(cf, data)) { - /* Maybe the internal errors of SSL_get_ex_new_index or SSL_set_ex_data */ - failf(data, "SSL: ossl_attach_data failed: %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - return CURLE_SSL_CONNECT_ERROR; - } + SSL_set_app_data(backend->handle, cf); if(ssl_config->primary.sessionid) { Curl_ssl_sessionid_lock(data); @@ -3872,13 +3822,26 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, Curl_ssl_sessionid_unlock(data); } - bio = BIO_new(bio_cf_method); + backend->bio_method = bio_cf_method_create(); + if(!backend->bio_method) + return CURLE_OUT_OF_MEMORY; + bio = BIO_new(backend->bio_method); if(!bio) return CURLE_OUT_OF_MEMORY; BIO_set_data(bio, cf); +#ifdef HAVE_SSL_SET0_WBIO + /* with OpenSSL v1.1.1 we get an alternative to SSL_set_bio() that works + * without backward compat quirks. Every call takes one reference, so we + * up it and pass. SSL* then owns it and will free. + * We check on the function in configure, since libressl and friends + * each have their own versions to add support for this. */ + BIO_up_ref(bio); + SSL_set0_rbio(backend->handle, bio); + SSL_set0_wbio(backend->handle, bio); +#else SSL_set_bio(backend->handle, bio, bio); - +#endif connssl->connecting_state = ssl_connect_2; return CURLE_OK; @@ -3899,6 +3862,16 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, ERR_clear_error(); err = SSL_connect(backend->handle); + + if(!backend->x509_store_setup) { + /* After having send off the ClientHello, we prepare the x509 + * store to verify the coming certificate from the server */ + CURLcode result = Curl_ssl_setup_x509_store(cf, data, backend->ctx); + if(result) + return result; + backend->x509_store_setup = TRUE; + } + #ifndef HAVE_KEYLOG_CALLBACK if(Curl_tls_keylog_enabled()) { /* If key logging is enabled, wait for the handshake to complete and then @@ -3933,7 +3906,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, } else { /* untreated error */ - unsigned long errdetail; + sslerr_t errdetail; char error_buffer[256]=""; CURLcode result; long lerr; @@ -4027,26 +4000,8 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, const unsigned char *neg_protocol; unsigned int len; SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len); - if(len) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, len, neg_protocol); - -#ifdef USE_HTTP2 - if(len == ALPN_H2_LENGTH && - !memcmp(ALPN_H2, neg_protocol, len)) { - cf->conn->alpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(len == ALPN_HTTP_1_1_LENGTH && - !memcmp(ALPN_HTTP_1_1, neg_protocol, ALPN_HTTP_1_1_LENGTH)) { - cf->conn->alpn = CURL_HTTP_VERSION_1_1; - } - } - else - infof(data, VTLS_INFOF_NO_ALPN); - Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); + return Curl_alpn_set_negotiated(cf, data, neg_protocol, len); } #endif @@ -4189,7 +4144,8 @@ static CURLcode servercert(struct Curl_cfilter *cf, BIO_free(mem); if(conn_config->verifyhost) { - result = Curl_ossl_verifyhost(data, conn, backend->server_cert); + result = ossl_verifyhost(data, conn, backend->server_cert, + connssl->hostname, connssl->dispname); if(result) { X509_free(backend->server_cert); backend->server_cert = NULL; @@ -4363,7 +4319,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, { CURLcode result = CURLE_OK; struct ssl_connect_data *connssl = cf->ctx; - curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; + curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); int what; /* check if the connection has already been established */ @@ -4402,8 +4358,9 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, } /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading || - connssl->connecting_state == ssl_connect_2_writing) { + if(!nonblocking && + (connssl->connecting_state == ssl_connect_2_reading || + connssl->connecting_state == ssl_connect_2_writing)) { curl_socket_t writefd = ssl_connect_2_writing == connssl->connecting_state?sockfd:CURL_SOCKET_BAD; @@ -4411,7 +4368,7 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, connssl->connecting_state?sockfd:CURL_SOCKET_BAD; what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking?0:timeout_ms); + timeout_ms); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); @@ -4419,11 +4376,6 @@ static CURLcode ossl_connect_common(struct Curl_cfilter *cf, goto out; } if(0 == what) { - if(nonblocking) { - *done = FALSE; - result = CURLE_OK; - goto out; - } /* timeout */ failf(data, "SSL connection timeout"); result = CURLE_OPERATION_TIMEDOUT; @@ -4511,7 +4463,7 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, 'size_t' */ int err; char error_buffer[256]; - unsigned long sslerror; + sslerr_t sslerror; int memlen; int rc; struct ssl_connect_data *connssl = cf->ctx; @@ -4523,7 +4475,6 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, ERR_clear_error(); memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; - set_logger(connssl, data); rc = SSL_write(backend->handle, mem, memlen); if(rc <= 0) { @@ -4620,7 +4571,6 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, ERR_clear_error(); buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; - set_logger(connssl, data); nread = (ssize_t)SSL_read(backend->handle, buf, buffsize); if(nread <= 0) { @@ -4838,89 +4788,6 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl, (void *)backend->ctx : (void *)backend->handle; } -static bool ossl_attach_data(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; - const struct ssl_config_data *config; - - DEBUGASSERT(backend); - - /* If we don't have SSL context, do nothing. */ - if(!backend->handle) - return FALSE; - - config = Curl_ssl_cf_get_config(cf, data); - if(config->primary.sessionid) { - int data_idx = ossl_get_ssl_data_index(); - int cf_idx = ossl_get_ssl_cf_index(); - int sockindex_idx = ossl_get_ssl_sockindex_index(); - int proxy_idx = ossl_get_proxy_index(); - - if(data_idx >= 0 && cf_idx >= 0 && sockindex_idx >= 0 && - proxy_idx >= 0) { - int data_status, cf_status, sockindex_status, proxy_status; - - /* Store the data needed for the "new session" callback. - * The sockindex is stored as a pointer to an array element. */ - data_status = SSL_set_ex_data(backend->handle, data_idx, data); - cf_status = SSL_set_ex_data(backend->handle, cf_idx, cf); - sockindex_status = SSL_set_ex_data(backend->handle, sockindex_idx, - cf->conn->sock + cf->sockindex); -#ifndef CURL_DISABLE_PROXY - proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, - Curl_ssl_cf_is_proxy(cf)? - (void *) 1 : NULL); -#else - proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, NULL); -#endif - if(data_status && cf_status && sockindex_status && proxy_status) - return TRUE; - } - return FALSE; - } - return TRUE; -} - -/* - * Starting with TLS 1.3, the ossl_new_session_cb callback gets called after - * the handshake. If the transfer that sets up the callback gets killed before - * this callback arrives, we must make sure to properly clear the data to - * avoid UAF problems. A future optimization could be to instead store another - * transfer that might still be using the same connection. - */ - -static void ossl_detach_data(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - struct ssl_connect_data *connssl = cf->ctx; - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - - /* If we don't have SSL context, do nothing. */ - if(!backend->handle) - return; - - if(ssl_config->primary.sessionid) { - int data_idx = ossl_get_ssl_data_index(); - int cf_idx = ossl_get_ssl_cf_index(); - int sockindex_idx = ossl_get_ssl_sockindex_index(); - int proxy_idx = ossl_get_proxy_index(); - - if(data_idx >= 0 && cf_idx >= 0 && sockindex_idx >= 0 && - proxy_idx >= 0) { - /* Disable references to data in "new session" callback to avoid - * accessing a stale pointer. */ - SSL_set_ex_data(backend->handle, data_idx, NULL); - SSL_set_ex_data(backend->handle, cf_idx, NULL); - SSL_set_ex_data(backend->handle, sockindex_idx, NULL); - SSL_set_ex_data(backend->handle, proxy_idx, NULL); - } - } -} - static void ossl_free_multi_ssl_backend_data( struct multi_ssl_backend_data *mbackend) { @@ -4974,8 +4841,8 @@ const struct Curl_ssl Curl_ssl_openssl = { #else NULL, /* sha256sum */ #endif - ossl_attach_data, /* use of data in this connection */ - ossl_detach_data, /* remote of data from this connection */ + NULL, /* use of data in this connection */ + NULL, /* remote of data from this connection */ ossl_free_multi_ssl_backend_data, /* free_multi_ssl_backend_data */ ossl_recv, /* recv decrypted data */ ossl_send, /* send data to encrypt */ |