diff options
author | Curl Upstream <curl-library@lists.haxx.se> | 2022-12-21 07:00:59 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2023-01-27 20:58:30 (GMT) |
commit | dac458ddbf2b48168779821654a7e69cbd828c14 (patch) | |
tree | 8191fa2b92ceb3469c8ac5878b4ded3112f5f16c /lib/vtls/openssl.c | |
parent | ec122fff08ab9a8e56fb90126ecedb99c759011b (diff) | |
download | CMake-dac458ddbf2b48168779821654a7e69cbd828c14.zip CMake-dac458ddbf2b48168779821654a7e69cbd828c14.tar.gz CMake-dac458ddbf2b48168779821654a7e69cbd828c14.tar.bz2 |
curl 2022-12-21 (c12fb3dd)
Code extracted from:
https://github.com/curl/curl.git
at commit c12fb3ddaf48e709a7a4deaa55ec485e4df163ee (curl-7_87_0).
Diffstat (limited to 'lib/vtls/openssl.c')
-rw-r--r-- | lib/vtls/openssl.c | 1439 |
1 files changed, 903 insertions, 536 deletions
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index ad2efa5..e7a1caa 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -55,6 +55,7 @@ #include "slist.h" #include "select.h" #include "vtls.h" +#include "vtls_int.h" #include "vauth/vauth.h" #include "keylog.h" #include "strcase.h" @@ -259,6 +260,22 @@ #define HAVE_OPENSSL_VERSION #endif +/* + * Whether the OpenSSL version has the API needed to support sharing an + * X509_STORE between connections. The API is: + * * `X509_STORE_up_ref` -- Introduced: OpenSSL 1.1.0. + */ +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* OpenSSL >= 1.1.0 */ +#define HAVE_SSL_X509_STORE_SHARE +#endif + +/* What API version do we use? */ +#if defined(LIBRESSL_VERSION_NUMBER) +#define USE_PRE_1_1_API (LIBRESSL_VERSION_NUMBER < 0x2070000f) +#else /* !LIBRESSL_VERSION_NUMBER */ +#define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L) +#endif /* !LIBRESSL_VERSION_NUMBER */ + struct ssl_backend_data { struct Curl_easy *logger; /* transfer handle to pass trace logs to, only using sockindex 0 */ @@ -266,12 +283,21 @@ struct ssl_backend_data { SSL_CTX* ctx; SSL* handle; X509* server_cert; + 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 }; +#if defined(HAVE_SSL_X509_STORE_SHARE) +struct multi_ssl_backend_data { + char *CAfile; /* CAfile path used to generate X509 store */ + X509_STORE *store; /* cached X509 store or NULL if none */ + struct curltime time; /* when the cached store was created */ +}; +#endif /* HAVE_SSL_X509_STORE_SHARE */ + #define push_certinfo(_label, _num) \ do { \ long info_len = BIO_get_mem_data(mem, &ptr); \ @@ -610,9 +636,160 @@ CURLcode Curl_ossl_certchain(struct Curl_easy *data, SSL *ssl) #ifdef USE_OPENSSL -static bool ossl_associate_connection(struct Curl_easy *data, - struct connectdata *conn, - int sockindex); +#if USE_PRE_1_1_API +#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER < 0x2070000fL +#define BIO_set_init(x,v) ((x)->init=(v)) +#define BIO_get_data(x) ((x)->ptr) +#define BIO_set_data(x,v) ((x)->ptr=(v)) +#endif +#define BIO_get_shutdown(x) ((x)->shutdown) +#define BIO_set_shutdown(x,v) ((x)->shutdown=(v)) +#endif /* USE_PRE_1_1_API */ + +static int bio_cf_create(BIO *bio) +{ + BIO_set_shutdown(bio, 1); + BIO_set_init(bio, 1); +#if USE_PRE_1_1_API + bio->num = -1; +#endif + BIO_set_data(bio, NULL); + return 1; +} + +static int bio_cf_destroy(BIO *bio) +{ + if(!bio) + return 0; + return 1; +} + +static long bio_cf_ctrl(BIO *bio, int cmd, long num, void *ptr) +{ + struct Curl_cfilter *cf = BIO_get_data(bio); + long ret = 1; + + (void)cf; + (void)ptr; + switch(cmd) { + case BIO_CTRL_GET_CLOSE: + ret = (long)BIO_get_shutdown(bio); + break; + case BIO_CTRL_SET_CLOSE: + BIO_set_shutdown(bio, (int)num); + break; + case BIO_CTRL_FLUSH: + /* we do no delayed writes, but if we ever would, this + * needs to trigger it. */ + ret = 1; + break; + case BIO_CTRL_DUP: + ret = 1; + break; +#ifdef BIO_CTRL_EOF + case BIO_CTRL_EOF: + /* EOF has been reached on input? */ + return (!cf->next || !cf->next->connected); +#endif + default: + ret = 0; + break; + } + return ret; +} + +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; + 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)); */ + BIO_clear_retry_flags(bio); + connssl->backend->io_result = result; + if(nwritten < 0) { + if(CURLE_AGAIN == result) + BIO_set_retry_write(bio); + } + return (int)nwritten; +} + +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; + ssize_t nread; + CURLcode result = CURLE_RECV_ERROR; + + DEBUGASSERT(data); + /* OpenSSL catches this case, so should we. */ + if(!buf) + 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)); */ + BIO_clear_retry_flags(bio); + connssl->backend->io_result = result; + if(nread < 0) { + if(CURLE_AGAIN == result) + BIO_set_retry_read(bio); + } + 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 +}; + +static void bio_cf_init_methods(void) +{ + bio_cf_method = &bio_cf_meth_1_0; +} + +#define bio_cf_free_methods() Curl_nop_stmt + +#else + +static void bio_cf_init_methods(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); +} + +static void bio_cf_free_methods(void) +{ + BIO_meth_free(bio_cf_method); +} + +#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 @@ -711,12 +888,25 @@ static const char *SSL_ERROR_to_str(int err) } } +static size_t ossl_version(char *buffer, size_t size); + /* Return error string for last OpenSSL error */ static char *ossl_strerror(unsigned long error, char *buf, size_t size) { - if(size) + size_t len; + DEBUGASSERT(size); + *buf = '\0'; + + len = ossl_version(buf, size); + DEBUGASSERT(len < (size - 2)); + if(len < (size - 2)) { + buf += len; + size -= (len + 2); + *buf++ = ':'; + *buf++ = ' '; *buf = '\0'; + } #ifdef OPENSSL_IS_BORINGSSL ERR_error_string_n((uint32_t)error, buf, size); @@ -724,7 +914,7 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size) ERR_error_string_n(error, buf, size); #endif - if(size > 1 && !*buf) { + if(!*buf) { strncpy(buf, (error ? "Unknown error" : "No error"), size); buf[size - 1] = '\0'; } @@ -744,16 +934,16 @@ static int ossl_get_ssl_data_index(void) return ssl_ex_data_data_index; } -/* Return an extra data index for the connection data. +/* 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_conn_index(void) +static int ossl_get_ssl_cf_index(void) { - static int ssl_ex_data_conn_index = -1; - if(ssl_ex_data_conn_index < 0) { - ssl_ex_data_conn_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); + 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_conn_index; + return ssl_ex_data_cf_index; } /* Return an extra data index for the sockindex. @@ -1578,10 +1768,11 @@ 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_conn_index() < 0 || + 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; @@ -1623,6 +1814,7 @@ static void ossl_cleanup(void) #endif Curl_tls_keylog_close(); + bio_cf_free_methods(); } /* @@ -1633,15 +1825,16 @@ static void ossl_cleanup(void) * 0 means the connection has been closed * -1 means the connection status is unknown */ -static int ossl_check_cxn(struct connectdata *conn) +static int ossl_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data) { /* SSL_peek takes data out of the raw recv buffer without peeking so we use recv MSG_PEEK instead. Bug #795 */ #ifdef MSG_PEEK char buf; ssize_t nread; - nread = recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf, - (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK); + nread = recv((RECV_TYPE_ARG1)cf->conn->sock[cf->sockindex], + (RECV_TYPE_ARG2)&buf, (RECV_TYPE_ARG3)1, + (RECV_TYPE_ARG4)MSG_PEEK); if(nread == 0) return 0; /* connection has been closed */ if(nread == 1) @@ -1674,6 +1867,7 @@ static int ossl_check_cxn(struct connectdata *conn) return 0; /* connection has been closed */ } #endif + (void)data; return -1; /* connection status unknown */ } @@ -1766,33 +1960,28 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data) return list; } -#define set_logger(conn, data) \ - conn->ssl[0].backend->logger = data +#define set_logger(connssl, data) \ + connssl->backend->logger = data -static void ossl_closeone(struct Curl_easy *data, - struct connectdata *conn, - struct ssl_connect_data *connssl) +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; DEBUGASSERT(backend); if(backend->handle) { - char buf[32]; - set_logger(conn, data); - /* - * The conn->sock[0] socket is passed to openssl with SSL_set_fd(). Make - * sure the socket is not closed before calling OpenSSL functions that - * will use it. - */ - DEBUGASSERT(conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD); + set_logger(connssl, data); - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)SSL_read(backend->handle, buf, (int)sizeof(buf)); + if(cf->next && cf->next->connected) { + char buf[32]; + /* Maybe the server has already sent a close notify alert. + Read it to avoid an RST on the TCP connection. */ + (void)SSL_read(backend->handle, buf, (int)sizeof(buf)); - (void)SSL_shutdown(backend->handle); - SSL_set_connect_state(backend->handle); + (void)SSL_shutdown(backend->handle); + SSL_set_connect_state(backend->handle); + } SSL_free(backend->handle); backend->handle = NULL; @@ -1804,30 +1993,18 @@ static void ossl_closeone(struct Curl_easy *data, } /* - * This function is called when an SSL connection is closed. - */ -static void ossl_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - ossl_closeone(data, conn, &conn->ssl[sockindex]); -#ifndef CURL_DISABLE_PROXY - ossl_closeone(data, conn, &conn->proxy_ssl[sockindex]); -#endif -} - -/* * This function is called to shut down the SSL layer but keep the * socket open (CCC - Clear Command Channel) */ -static int ossl_shutdown(struct Curl_easy *data, - struct connectdata *conn, int sockindex) +static int ossl_shutdown(struct Curl_cfilter *cf, + struct Curl_easy *data) { int retval = 0; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_connect_data *connssl = cf->ctx; char buf[256]; /* We will use this for the OpenSSL error buffer, so it has to be at least 256 bytes long. */ unsigned long sslerror; - ssize_t nread; + int nread; int buffsize; int err; bool done = FALSE; @@ -1849,15 +2026,15 @@ static int ossl_shutdown(struct Curl_easy *data, if(backend->handle) { buffsize = (int)sizeof(buf); while(!done && loop--) { - int what = SOCKET_READABLE(conn->sock[sockindex], + int what = SOCKET_READABLE(cf->conn->sock[cf->sockindex], SSL_SHUTDOWN_TIMEOUT); if(what > 0) { ERR_clear_error(); /* Something to read, let's do it and hope that it is the close notify alert from the server */ - nread = (ssize_t)SSL_read(backend->handle, buf, buffsize); - err = SSL_get_error(backend->handle, (int)nread); + nread = SSL_read(backend->handle, buf, buffsize); + err = SSL_get_error(backend->handle, nread); switch(err) { case SSL_ERROR_NONE: /* this is not an error */ @@ -2015,9 +2192,18 @@ 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 * const hostname = SSL_HOST_NAME(); - const char * const dispname = SSL_HOST_DISPNAME(); - size_t hostlen = strlen(hostname); + 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 + /* Silence compiler warnings for unused params */ + (void) conn; +#endif #ifdef ENABLE_IPV6 if(conn->bits.ipv6_ip && @@ -2193,9 +2379,10 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) -static CURLcode verifystatus(struct Curl_easy *data, - struct ssl_connect_data *connssl) +static CURLcode verifystatus(struct Curl_cfilter *cf, + struct Curl_easy *data) { + struct ssl_connect_data *connssl = cf->ctx; int i, ocsp_status; unsigned char *status; const unsigned char *p; @@ -2476,18 +2663,24 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, char unknown[32]; const char *verstr = NULL; struct connectdata *conn = userp; - struct ssl_connect_data *connssl = &conn->ssl[0]; - struct ssl_backend_data *backend = connssl->backend; + int cf_idx = ossl_get_ssl_cf_index(); + struct ssl_connect_data *connssl; struct Curl_easy *data = NULL; + struct Curl_cfilter *cf; - DEBUGASSERT(backend); - data = backend->logger; + 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; if(!conn || !data || !data->set.fdebug || (direction != 0 && direction != 1)) return; - switch(ssl_ver) { + switch(ssl_ver) { #ifdef SSL2_VERSION /* removed in recent versions */ case SSL2_VERSION: verstr = "SSLv2"; @@ -2563,7 +2756,8 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, msg_name = ssl_msg_type(ssl_ver, msg_type); } - txt_len = msnprintf(ssl_buf, sizeof(ssl_buf), "%s (%s), %s, %s (%d):\n", + txt_len = msnprintf(ssl_buf, sizeof(ssl_buf), + CFMSG(cf, "%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)) { @@ -2595,10 +2789,11 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */ static CURLcode -set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn) +set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) { + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); /* first, TLS min version... */ - long curl_ssl_version_min = SSL_CONN_CONFIG(version); + long curl_ssl_version_min = conn_config->version; long curl_ssl_version_max; /* convert curl min SSL version option to OpenSSL constant */ @@ -2642,7 +2837,7 @@ set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn) } /* ... then, TLS max version */ - curl_ssl_version_max = SSL_CONN_CONFIG(version_max); + curl_ssl_version_max = conn_config->version_max; /* convert curl max SSL version option to OpenSSL constant */ switch(curl_ssl_version_max) { @@ -2690,11 +2885,12 @@ typedef long ctx_option_t; #if (OPENSSL_VERSION_NUMBER < 0x10100000L) /* 1.1.0 */ static CURLcode set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, - struct Curl_easy *data, - struct connectdata *conn, int sockindex) + struct Curl_cfilter *cf, + struct Curl_easy *data) { - long ssl_version = SSL_CONN_CONFIG(version); - long ssl_version_max = SSL_CONN_CONFIG(version_max); + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + long ssl_version = conn_config->version; + long ssl_version_max = conn_config->version_max; (void) data; /* In case it's unused. */ @@ -2702,14 +2898,12 @@ set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, case CURL_SSLVERSION_TLSv1_3: #ifdef TLS1_3_VERSION { - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION); + struct ssl_connect_data *connssl = cf->ctx; + DEBUGASSERT(connssl->backend); + SSL_CTX_set_max_proto_version(connssl->backend->ctx, TLS1_3_VERSION); *ctx_options |= SSL_OP_NO_TLSv1_2; } #else - (void)sockindex; (void)ctx_options; failf(data, OSSL_PACKAGE " was built without TLS 1.3 support"); return CURLE_NOT_BUILT_IN; @@ -2770,31 +2964,30 @@ set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) { int res = 0; - struct connectdata *conn; struct Curl_easy *data; - int sockindex; + struct Curl_cfilter *cf; + const struct ssl_config_data *config; curl_socket_t *sockindex_ptr; int data_idx = ossl_get_ssl_data_index(); - int connectdata_idx = ossl_get_ssl_conn_index(); + int cf_idx = ossl_get_ssl_cf_index(); int sockindex_idx = ossl_get_ssl_sockindex_index(); int proxy_idx = ossl_get_proxy_index(); bool isproxy; - if(data_idx < 0 || connectdata_idx < 0 || sockindex_idx < 0 || proxy_idx < 0) + if(data_idx < 0 || cf_idx < 0 || sockindex_idx < 0 || proxy_idx < 0) return 0; - conn = (struct connectdata*) SSL_get_ex_data(ssl, connectdata_idx); + cf = (struct Curl_cfilter*) SSL_get_ex_data(ssl, cf_idx); data = (struct Curl_easy *) SSL_get_ex_data(ssl, data_idx); /* 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(!conn || !data || !sockindex_ptr) + if(!cf || !data || !sockindex_ptr) return 0; - sockindex = (int)(sockindex_ptr - conn->sock); - - isproxy = SSL_get_ex_data(ssl, proxy_idx) ? TRUE : FALSE; + isproxy = Curl_ssl_cf_is_proxy(cf); - if(SSL_SET_OPTION(primary.sessionid)) { + config = Curl_ssl_cf_get_config(cf, data); + if(config->primary.sessionid) { bool incache; bool added = FALSE; void *old_ssl_sessionid = NULL; @@ -2803,8 +2996,7 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) if(isproxy) incache = FALSE; else - incache = !(Curl_ssl_getsessionid(data, conn, isproxy, - &old_ssl_sessionid, NULL, sockindex)); + incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL)); if(incache) { if(old_ssl_sessionid != ssl_sessionid) { infof(data, "old SSL session ID is stale, removing"); @@ -2814,8 +3006,8 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) } if(!incache) { - if(!Curl_ssl_addsessionid(data, conn, isproxy, ssl_sessionid, - 0 /* unknown size */, sockindex, &added)) { + if(!Curl_ssl_addsessionid(cf, data, ssl_sessionid, + 0 /* unknown size */, &added)) { if(added) { /* the session has been put into the session cache */ res = 1; @@ -2830,7 +3022,7 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) return res; } -static CURLcode load_cacert_from_memory(SSL_CTX *ctx, +static CURLcode load_cacert_from_memory(X509_STORE *store, const struct curl_blob *ca_info_blob) { /* these need to be freed at the end */ @@ -2839,16 +3031,11 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx, /* everything else is just a reference */ int i, count = 0; - X509_STORE *cts = NULL; X509_INFO *itmp = NULL; if(ca_info_blob->len > (size_t)INT_MAX) return CURLE_SSL_CACERT_BADFILE; - cts = SSL_CTX_get_cert_store(ctx); - if(!cts) - return CURLE_OUT_OF_MEMORY; - cbio = BIO_new_mem_buf(ca_info_blob->data, (int)ca_info_blob->len); if(!cbio) return CURLE_OUT_OF_MEMORY; @@ -2863,7 +3050,7 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx, for(i = 0; i < (int)sk_X509_INFO_num(inf); ++i) { itmp = sk_X509_INFO_value(inf, i); if(itmp->x509) { - if(X509_STORE_add_cert(cts, itmp->x509)) { + if(X509_STORE_add_cert(store, itmp->x509)) { ++count; } else { @@ -2873,7 +3060,7 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx, } } if(itmp->crl) { - if(X509_STORE_add_crl(cts, itmp->crl)) { + if(X509_STORE_add_crl(store, itmp->crl)) { ++count; } else { @@ -2891,21 +3078,414 @@ static CURLcode load_cacert_from_memory(SSL_CTX *ctx, return (count > 0 ? CURLE_OK : CURLE_SSL_CACERT_BADFILE); } -static CURLcode ossl_connect_step1(struct Curl_easy *data, - struct connectdata *conn, int sockindex) +static CURLcode populate_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + X509_STORE *store) +{ + 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); + CURLcode result = CURLE_OK; + X509_LOOKUP *lookup = NULL; + const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ca_info_blob ? NULL : conn_config->CAfile); + const char * const ssl_capath = conn_config->CApath; + const char * const ssl_crlfile = ssl_config->primary.CRLfile; + const bool verifypeer = conn_config->verifypeer; + bool imported_native_ca = false; + + if(!store) + return CURLE_OUT_OF_MEMORY; + +#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; +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + char cert_name[256]; +#endif + + 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); +#endif + + 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)) + 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(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; + } + } + else + continue; + } + else + 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) { +#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) + infof(data, "SSL: Imported cert \"%s\"", cert_name); +#endif + imported_native_ca = true; + } + X509_free(x509); + } + + free(enhkey_usage); + CertFreeCertificateContext(pContext); + CertCloseStore(hStore, 0); + + 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"); + } +#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)) { + 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"); + } + } + + if(verifypeer && !imported_native_ca && (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; + } +#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; + } +#endif + 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); + } +#endif + + if(ssl_crlfile) { + /* tell OpenSSL where to find CRL file that is used to check certificate + * revocation */ + lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); + if(!lookup || + (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) { + failf(data, "error loading CRL file: %s", ssl_crlfile); + return CURLE_SSL_CRL_BADFILE; + } + /* Everything is fine. */ + infof(data, "successfully loaded CRL file:"); + X509_STORE_set_flags(store, + X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); + + infof(data, " CRLfile: %s", ssl_crlfile); + } + + if(verifypeer) { + /* Try building a chain using issuers in the trusted store first to avoid + problems with server-sent legacy intermediates. Newer versions of + OpenSSL do alternate chain checking by default but we do not know how to + determine that in a reliable manner. + https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest + */ +#if defined(X509_V_FLAG_TRUSTED_FIRST) + X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST); +#endif +#ifdef X509_V_FLAG_PARTIAL_CHAIN + if(!ssl_config->no_partialchain && !ssl_crlfile) { + /* Have intermediate certificates in the trust store be treated as + trust-anchors, in the same way as self-signed root CA certificates + are. This allows users to verify servers using the intermediate cert + only, instead of needing the whole chain. + + Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we + cannot do partial chains with a CRL check. + */ + X509_STORE_set_flags(store, X509_V_FLAG_PARTIAL_CHAIN); + } +#endif + } + + return result; +} + +#if defined(HAVE_SSL_X509_STORE_SHARE) +static bool cached_x509_store_expired(const struct Curl_easy *data, + const struct multi_ssl_backend_data *mb) +{ + const struct ssl_general_config *cfg = &data->set.general_ssl; + struct curltime now = Curl_now(); + timediff_t elapsed_ms = Curl_timediff(now, mb->time); + timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000; + + if(timeout_ms < 0) + return false; + + return elapsed_ms >= timeout_ms; +} + +static bool cached_x509_store_different( + struct Curl_cfilter *cf, + const struct multi_ssl_backend_data *mb) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + if(!mb->CAfile || !conn_config->CAfile) + return mb->CAfile != conn_config->CAfile; + + return strcmp(mb->CAfile, conn_config->CAfile); +} + +static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, + const struct Curl_easy *data) +{ + struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; + X509_STORE *store = NULL; + + if(multi && + multi->ssl_backend_data && + multi->ssl_backend_data->store && + !cached_x509_store_expired(data, multi->ssl_backend_data) && + !cached_x509_store_different(cf, multi->ssl_backend_data)) { + store = multi->ssl_backend_data->store; + } + + return store; +} + +static void set_cached_x509_store(struct Curl_cfilter *cf, + const struct Curl_easy *data, + X509_STORE *store) +{ + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); + struct Curl_multi *multi = data->multi_easy ? data->multi_easy : data->multi; + struct multi_ssl_backend_data *mbackend; + + if(!multi) + return; + + if(!multi->ssl_backend_data) { + multi->ssl_backend_data = calloc(1, sizeof(struct multi_ssl_backend_data)); + if(!multi->ssl_backend_data) + return; + } + + mbackend = multi->ssl_backend_data; + + if(X509_STORE_up_ref(store)) { + char *CAfile = NULL; + + if(conn_config->CAfile) { + CAfile = strdup(conn_config->CAfile); + if(!CAfile) { + X509_STORE_free(store); + return; + } + } + + if(mbackend->store) { + X509_STORE_free(mbackend->store); + free(mbackend->CAfile); + } + + mbackend->time = Curl_now(); + mbackend->store = store; + mbackend->CAfile = CAfile; + } +} + +static CURLcode set_up_x509_store(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct ssl_backend_data *backend) +{ + 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); + CURLcode result = CURLE_OK; + X509_STORE *cached_store; + bool cache_criteria_met; + + /* Consider the X509 store cacheable if it comes exclusively from a CAfile, + or no source is provided and we are falling back to openssl's built-in + default. */ + cache_criteria_met = (data->set.general_ssl.ca_cache_timeout != 0) && + conn_config->verifypeer && + !conn_config->CApath && + !conn_config->ca_info_blob && + !ssl_config->primary.CRLfile && + !ssl_config->native_ca_store; + + 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); + } + else { + X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); + + result = populate_x509_store(cf, data, store); + if(result == CURLE_OK && cache_criteria_met) { + set_cached_x509_store(cf, data, store); + } + } + + 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) +{ + X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); + + return populate_x509_store(cf, data, store); +} +#endif /* HAVE_SSL_X509_STORE_SHARE */ + +static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, + struct Curl_easy *data) { CURLcode result = CURLE_OK; char *ciphers; SSL_METHOD_QUAL SSL_METHOD *req_method = NULL; - X509_LOOKUP *lookup = NULL; - curl_socket_t sockfd = conn->sock[sockindex]; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_connect_data *connssl = cf->ctx; ctx_option_t ctx_options = 0; void *ssl_sessionid = NULL; + 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); + BIO *bio; #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME bool sni; - const char * const hostname = SSL_HOST_NAME(); + const char *hostname = connssl->hostname; #ifdef ENABLE_IPV6 struct in6_addr addr; @@ -2913,23 +3493,16 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, struct in_addr addr; #endif #endif - const long int ssl_version = SSL_CONN_CONFIG(version); + const long int ssl_version = conn_config->version; #ifdef USE_OPENSSL_SRP - const enum CURL_TLSAUTH ssl_authtype = SSL_SET_OPTION(primary.authtype); + const enum CURL_TLSAUTH ssl_authtype = ssl_config->primary.authtype; #endif - char * const ssl_cert = SSL_SET_OPTION(primary.clientcert); - const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob); - const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); - const char * const ssl_cert_type = SSL_SET_OPTION(cert_type); - const char * const ssl_cafile = - /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); - const char * const ssl_capath = SSL_CONN_CONFIG(CApath); - const bool verifypeer = SSL_CONN_CONFIG(verifypeer); - const char * const ssl_crlfile = SSL_SET_OPTION(primary.CRLfile); + 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 bool verifypeer = conn_config->verifypeer; char error_buffer[256]; struct ssl_backend_data *backend = connssl->backend; - bool imported_native_ca = false; DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); DEBUGASSERT(backend); @@ -2939,7 +3512,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, if(result) return result; - SSL_SET_OPTION_LVALUE(certverifyresult) = !X509_V_OK; + ssl_config->certverifyresult = !X509_V_OK; /* check to see if we've been told to use an explicit SSL/TLS version */ @@ -2969,7 +3542,12 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, return CURLE_SSL_CONNECT_ERROR; } - DEBUGASSERT(!backend->ctx); + if(backend->ctx) { + /* This happens when an error was encountered before in this + * step and we are called to do it again. Get rid of any leftover + * from the previous call. */ + ossl_close(cf, data); + } backend->ctx = SSL_CTX_new(req_method); if(!backend->ctx) { @@ -2986,8 +3564,8 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, 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, conn); - set_logger(conn, data); + SSL_CTX_set_msg_callback_arg(backend->ctx, cf->conn); + set_logger(connssl, data); } #endif @@ -3045,7 +3623,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS /* unless the user explicitly asks to allow the protocol vulnerability we use the work-around */ - if(!SSL_SET_OPTION(enable_beast)) + if(!ssl_config->enable_beast) ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; #endif @@ -3067,10 +3645,9 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, ctx_options |= SSL_OP_NO_SSLv3; #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */ - result = set_ssl_version_min_max(backend->ctx, conn); + result = set_ssl_version_min_max(cf, backend->ctx); #else - result = set_ssl_version_min_max_legacy(&ctx_options, data, conn, - sockindex); + result = set_ssl_version_min_max_legacy(&ctx_options, cf, data); #endif if(result != CURLE_OK) return result; @@ -3084,14 +3661,14 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, SSL_CTX_set_options(backend->ctx, ctx_options); #ifdef HAS_ALPN - if(conn->bits.tls_enable_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 - && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) + && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy) #endif ) { protocols[cur++] = ALPN_H2_LENGTH; @@ -3121,15 +3698,15 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, if(!result && !cert_stuff(data, backend->ctx, ssl_cert, ssl_cert_blob, ssl_cert_type, - SSL_SET_OPTION(key), SSL_SET_OPTION(key_blob), - SSL_SET_OPTION(key_type), SSL_SET_OPTION(key_passwd))) + ssl_config->key, ssl_config->key_blob, + ssl_config->key_type, ssl_config->key_passwd)) result = CURLE_SSL_CERTPROBLEM; if(result) /* failf() is already done in cert_stuff() */ return result; } - ciphers = SSL_CONN_CONFIG(cipher_list); + ciphers = conn_config->cipher_list; if(!ciphers) ciphers = (char *)DEFAULT_CIPHER_SELECTION; if(ciphers) { @@ -3142,7 +3719,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES { - char *ciphers13 = SSL_CONN_CONFIG(cipher_list13); + char *ciphers13 = conn_config->cipher_list13; if(ciphers13) { if(!SSL_CTX_set_ciphersuites(backend->ctx, ciphers13)) { failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13); @@ -3160,7 +3737,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, #ifdef HAVE_SSL_CTX_SET_EC_CURVES { - char *curves = SSL_CONN_CONFIG(curves); + char *curves = conn_config->curves; if(curves) { if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) { failf(data, "failed setting curves list: '%s'", curves); @@ -3173,8 +3750,8 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, #ifdef USE_OPENSSL_SRP if((ssl_authtype == CURL_TLSAUTH_SRP) && Curl_auth_allowed_to_host(data)) { - char * const ssl_username = SSL_SET_OPTION(primary.username); - char * const ssl_password = SSL_SET_OPTION(primary.password); + 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); if(!SSL_CTX_set_srp_username(backend->ctx, ssl_username)) { @@ -3185,7 +3762,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, failf(data, "failed setting SRP password"); return CURLE_BAD_FUNCTION_ARGUMENT; } - if(!SSL_CONN_CONFIG(cipher_list)) { + if(!conn_config->cipher_list) { infof(data, "Setting cipher list SRP"); if(!SSL_CTX_set_cipher_list(backend->ctx, "SRP")) { @@ -3196,249 +3773,9 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, } #endif - -#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((SSL_CONN_CONFIG(verifypeer) || SSL_CONN_CONFIG(verifyhost)) && - (SSL_SET_OPTION(native_ca_store))) { - X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); - 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]; -#endif - - 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); -#endif - - 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)) - 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(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; - } - } - else - continue; - } - else - 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) { -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - infof(data, "SSL: Imported cert \"%s\"", cert_name); -#endif - imported_native_ca = true; - } - X509_free(x509); - } - - free(enhkey_usage); - CertFreeCertificateContext(pContext); - CertCloseStore(hStore, 0); - - 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"); - } -#endif - - if(ca_info_blob) { - result = load_cacert_from_memory(backend->ctx, ca_info_blob); - if(result) { - if(result == CURLE_OUT_OF_MEMORY || - (verifypeer && !imported_native_ca)) { - 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"); - } - } - - if(verifypeer && !imported_native_ca && (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 && - !SSL_CTX_load_verify_file(backend->ctx, 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 && - !SSL_CTX_load_verify_dir(backend->ctx, 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; - } -#else - /* tell OpenSSL where to find CA certificates that are used to verify the - server's certificate. */ - if(!SSL_CTX_load_verify_locations(backend->ctx, 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; - } -#endif - 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 */ - SSL_CTX_set_default_verify_paths(backend->ctx); - } -#endif - - if(ssl_crlfile) { - /* tell OpenSSL where to find CRL file that is used to check certificate - * revocation */ - lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(backend->ctx), - X509_LOOKUP_file()); - if(!lookup || - (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) { - failf(data, "error loading CRL file: %s", ssl_crlfile); - return CURLE_SSL_CRL_BADFILE; - } - /* Everything is fine. */ - infof(data, "successfully loaded CRL file:"); - X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), - X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); - - infof(data, " CRLfile: %s", ssl_crlfile); - } - - if(verifypeer) { - /* Try building a chain using issuers in the trusted store first to avoid - problems with server-sent legacy intermediates. Newer versions of - OpenSSL do alternate chain checking by default but we do not know how to - determine that in a reliable manner. - https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest - */ -#if defined(X509_V_FLAG_TRUSTED_FIRST) - X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), - X509_V_FLAG_TRUSTED_FIRST); -#endif -#ifdef X509_V_FLAG_PARTIAL_CHAIN - if(!SSL_SET_OPTION(no_partialchain) && !ssl_crlfile) { - /* Have intermediate certificates in the trust store be treated as - trust-anchors, in the same way as self-signed root CA certificates - are. This allows users to verify servers using the intermediate cert - only, instead of needing the whole chain. - - Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we - cannot do partial chains with a CRL check. - */ - X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), - X509_V_FLAG_PARTIAL_CHAIN); - } -#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 @@ -3485,7 +3822,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) - if(SSL_CONN_CONFIG(verifystatus)) + if(conn_config->verifystatus) SSL_set_tlsext_status_type(backend->handle, TLSEXT_STATUSTYPE_ocsp); #endif @@ -3510,18 +3847,17 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, } #endif - if(!ossl_associate_connection(data, conn, sockindex)) { + 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_associate_connection failed: %s", + failf(data, "SSL: ossl_attach_data failed: %s", ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); return CURLE_SSL_CONNECT_ERROR; } - if(SSL_SET_OPTION(primary.sessionid)) { + if(ssl_config->primary.sessionid) { Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE, - &ssl_sessionid, NULL, sockindex)) { + if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) { /* we got a session id, use it! */ if(!SSL_set_session(backend->handle, ssl_sessionid)) { Curl_ssl_sessionid_unlock(data); @@ -3536,40 +3872,25 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, Curl_ssl_sessionid_unlock(data); } -#ifndef CURL_DISABLE_PROXY - if(conn->proxy_ssl[sockindex].use) { - BIO *const bio = BIO_new(BIO_f_ssl()); - struct ssl_backend_data *proxy_backend; - SSL* handle = NULL; - proxy_backend = conn->proxy_ssl[sockindex].backend; - DEBUGASSERT(proxy_backend); - handle = proxy_backend->handle; - DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state); - DEBUGASSERT(handle != NULL); - DEBUGASSERT(bio != NULL); - BIO_set_ssl(bio, handle, FALSE); - SSL_set_bio(backend->handle, bio, bio); - } - else -#endif - if(!SSL_set_fd(backend->handle, (int)sockfd)) { - /* pass the raw socket into the SSL layers */ - failf(data, "SSL: SSL_set_fd failed: %s", - ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer))); - return CURLE_SSL_CONNECT_ERROR; - } + bio = BIO_new(bio_cf_method); + if(!bio) + return CURLE_OUT_OF_MEMORY; + + BIO_set_data(bio, cf); + SSL_set_bio(backend->handle, bio, bio); connssl->connecting_state = ssl_connect_2; return CURLE_OK; } -static CURLcode ossl_connect_step2(struct Curl_easy *data, - struct connectdata *conn, int sockindex) +static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, + struct Curl_easy *data) { int err; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_connect_data *connssl = cf->ctx; struct ssl_backend_data *backend = connssl->backend; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); DEBUGASSERT(ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); @@ -3607,6 +3928,9 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data, return CURLE_OK; } #endif + else if(backend->io_result == CURLE_AGAIN) { + return CURLE_OK; + } else { /* untreated error */ unsigned long errdetail; @@ -3634,7 +3958,7 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data, lerr = SSL_get_verify_result(backend->handle); if(lerr != X509_V_OK) { - SSL_SET_OPTION_LVALUE(certverifyresult) = lerr; + ssl_config->certverifyresult = lerr; msnprintf(error_buffer, sizeof(error_buffer), "SSL certificate problem: %s", X509_verify_cert_error_string(lerr)); @@ -3669,15 +3993,14 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data, * the SO_ERROR is also lost. */ if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) { - const char * const hostname = SSL_HOST_NAME(); - const long int port = SSL_HOST_PORT(); char extramsg[80]=""; int sockerr = SOCKERRNO; + if(sockerr && detail == SSL_ERROR_SYSCALL) Curl_strerror(sockerr, extramsg, sizeof(extramsg)); - failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%ld ", + failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ", extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), - hostname, port); + connssl->hostname, connssl->port); return result; } @@ -3700,7 +4023,7 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data, /* Sets data and len to negotiated protocol, len is 0 if no protocol was * negotiated */ - if(conn->bits.tls_enable_alpn) { + if(cf->conn->bits.tls_enable_alpn) { const unsigned char *neg_protocol; unsigned int len; SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len); @@ -3710,19 +4033,19 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data, #ifdef USE_HTTP2 if(len == ALPN_H2_LENGTH && !memcmp(ALPN_H2, neg_protocol, len)) { - conn->alpn = CURL_HTTP_VERSION_2; + 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)) { - conn->alpn = CURL_HTTP_VERSION_1_1; + cf->conn->alpn = CURL_HTTP_VERSION_1_1; } } else infof(data, VTLS_INFOF_NO_ALPN); - Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ? + Curl_multiuse_state(data, cf->conn->alpn == CURL_HTTP_VERSION_2 ? BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); } #endif @@ -3797,11 +4120,14 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert, * We check certificates to authenticate the server; otherwise we risk * man-in-the-middle attack. */ -static CURLcode servercert(struct Curl_easy *data, - struct connectdata *conn, - struct ssl_connect_data *connssl, +static CURLcode servercert(struct Curl_cfilter *cf, + struct Curl_easy *data, bool strict) { + struct connectdata *conn = cf->conn; + struct ssl_connect_data *connssl = cf->ctx; + struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); CURLcode result = CURLE_OK; int rc; long lerr; @@ -3838,7 +4164,8 @@ static CURLcode servercert(struct Curl_easy *data, return CURLE_PEER_FAILED_VERIFICATION; } - infof(data, "%s certificate:", SSL_IS_PROXY() ? "Proxy" : "Server"); + infof(data, "%s certificate:", + Curl_ssl_cf_is_proxy(cf)? "Proxy" : "Server"); rc = x509_name_oneline(X509_get_subject_name(backend->server_cert), buffer, sizeof(buffer)); @@ -3861,7 +4188,7 @@ static CURLcode servercert(struct Curl_easy *data, BIO_free(mem); - if(SSL_CONN_CONFIG(verifyhost)) { + if(conn_config->verifyhost) { result = Curl_ossl_verifyhost(data, conn, backend->server_cert); if(result) { X509_free(backend->server_cert); @@ -3884,10 +4211,10 @@ static CURLcode servercert(struct Curl_easy *data, deallocating the certificate. */ /* e.g. match issuer name with provided issuer certificate */ - if(SSL_CONN_CONFIG(issuercert) || SSL_CONN_CONFIG(issuercert_blob)) { - if(SSL_CONN_CONFIG(issuercert_blob)) { - fp = BIO_new_mem_buf(SSL_CONN_CONFIG(issuercert_blob)->data, - (int)SSL_CONN_CONFIG(issuercert_blob)->len); + if(conn_config->issuercert || conn_config->issuercert_blob) { + if(conn_config->issuercert_blob) { + fp = BIO_new_mem_buf(conn_config->issuercert_blob->data, + (int)conn_config->issuercert_blob->len); if(!fp) { failf(data, "BIO_new_mem_buf NULL, " OSSL_PACKAGE @@ -3912,10 +4239,10 @@ static CURLcode servercert(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } - if(BIO_read_filename(fp, SSL_CONN_CONFIG(issuercert)) <= 0) { + if(BIO_read_filename(fp, conn_config->issuercert) <= 0) { if(strict) failf(data, "SSL: Unable to open issuer cert (%s)", - SSL_CONN_CONFIG(issuercert)); + conn_config->issuercert); BIO_free(fp); X509_free(backend->server_cert); backend->server_cert = NULL; @@ -3927,7 +4254,7 @@ static CURLcode servercert(struct Curl_easy *data, if(!issuer) { if(strict) failf(data, "SSL: Unable to read issuer cert (%s)", - SSL_CONN_CONFIG(issuercert)); + conn_config->issuercert); BIO_free(fp); X509_free(issuer); X509_free(backend->server_cert); @@ -3938,7 +4265,7 @@ static CURLcode servercert(struct Curl_easy *data, if(X509_check_issued(issuer, backend->server_cert) != X509_V_OK) { if(strict) failf(data, "SSL: Certificate issuer check failed (%s)", - SSL_CONN_CONFIG(issuercert)); + conn_config->issuercert); BIO_free(fp); X509_free(issuer); X509_free(backend->server_cert); @@ -3947,15 +4274,15 @@ static CURLcode servercert(struct Curl_easy *data, } infof(data, " SSL certificate issuer check ok (%s)", - SSL_CONN_CONFIG(issuercert)); + conn_config->issuercert); BIO_free(fp); X509_free(issuer); } lerr = SSL_get_verify_result(backend->handle); - SSL_SET_OPTION_LVALUE(certverifyresult) = lerr; + ssl_config->certverifyresult = lerr; if(lerr != X509_V_OK) { - if(SSL_CONN_CONFIG(verifypeer)) { + if(conn_config->verifypeer) { /* We probably never reach this, because SSL_connect() will fail and we return earlier if verifypeer is set? */ if(strict) @@ -3974,8 +4301,8 @@ static CURLcode servercert(struct Curl_easy *data, #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) - if(SSL_CONN_CONFIG(verifystatus)) { - result = verifystatus(data, connssl); + if(conn_config->verifystatus) { + result = verifystatus(cf, data); if(result) { X509_free(backend->server_cert); backend->server_cert = NULL; @@ -3988,7 +4315,9 @@ static CURLcode servercert(struct Curl_easy *data, /* when not strict, we don't bother about the verify cert problems */ result = CURLE_OK; - ptr = SSL_PINNED_PUB_KEY(); + ptr = Curl_ssl_cf_is_proxy(cf)? + data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]: + data->set.str[STRING_SSL_PINNEDPUBLICKEY]; if(!result && ptr) { result = pkp_pin_peer_pubkey(data, backend->server_cert, ptr); if(result) @@ -4002,11 +4331,12 @@ static CURLcode servercert(struct Curl_easy *data, return result; } -static CURLcode ossl_connect_step3(struct Curl_easy *data, - struct connectdata *conn, int sockindex) +static CURLcode ossl_connect_step3(struct Curl_cfilter *cf, + struct Curl_easy *data) { CURLcode result = CURLE_OK; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_connect_data *connssl = cf->ctx; + struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); @@ -4017,8 +4347,8 @@ static CURLcode ossl_connect_step3(struct Curl_easy *data, * operations. */ - result = servercert(data, conn, connssl, (SSL_CONN_CONFIG(verifypeer) || - SSL_CONN_CONFIG(verifyhost))); + result = servercert(cf, data, conn_config->verifypeer || + conn_config->verifyhost); if(!result) connssl->connecting_state = ssl_connect_done; @@ -4026,18 +4356,14 @@ static CURLcode ossl_connect_step3(struct Curl_easy *data, return result; } -static Curl_recv ossl_recv; -static Curl_send ossl_send; - -static CURLcode ossl_connect_common(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, +static CURLcode ossl_connect_common(struct Curl_cfilter *cf, + struct Curl_easy *data, bool nonblocking, bool *done) { - CURLcode result; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - curl_socket_t sockfd = conn->sock[sockindex]; + CURLcode result = CURLE_OK; + struct ssl_connect_data *connssl = cf->ctx; + curl_socket_t sockfd = cf->conn->sock[cf->sockindex]; int what; /* check if the connection has already been established */ @@ -4056,9 +4382,9 @@ static CURLcode ossl_connect_common(struct Curl_easy *data, return CURLE_OPERATION_TIMEDOUT; } - result = ossl_connect_step1(data, conn, sockindex); + result = ossl_connect_step1(cf, data); if(result) - return result; + goto out; } while(ssl_connect_2 == connssl->connecting_state || @@ -4071,7 +4397,8 @@ static CURLcode ossl_connect_common(struct Curl_easy *data, if(timeout_ms < 0) { /* no need to continue if time already is up */ failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; + result = CURLE_OPERATION_TIMEDOUT; + goto out; } /* if ssl is expecting something, check if it's available. */ @@ -4088,16 +4415,19 @@ static CURLcode ossl_connect_common(struct Curl_easy *data, if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - return CURLE_SSL_CONNECT_ERROR; + result = CURLE_SSL_CONNECT_ERROR; + goto out; } if(0 == what) { if(nonblocking) { *done = FALSE; - return CURLE_OK; + result = CURLE_OK; + goto out; } /* timeout */ failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; + result = CURLE_OPERATION_TIMEDOUT; + goto out; } /* socket is readable or writable */ } @@ -4108,25 +4438,23 @@ static CURLcode ossl_connect_common(struct Curl_easy *data, * before step2 has completed while ensuring that a client using select() * or epoll() will always have a valid fdset to wait on. */ - result = ossl_connect_step2(data, conn, sockindex); + result = ossl_connect_step2(cf, data); if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state))) - return result; + goto out; } /* repeat step2 until all transactions are done. */ if(ssl_connect_3 == connssl->connecting_state) { - result = ossl_connect_step3(data, conn, sockindex); + result = ossl_connect_step3(cf, data); if(result) - return result; + goto out; } if(ssl_connect_done == connssl->connecting_state) { connssl->state = ssl_connection_complete; - conn->recv[sockindex] = ossl_recv; - conn->send[sockindex] = ossl_send; *done = TRUE; } else @@ -4135,24 +4463,24 @@ static CURLcode ossl_connect_common(struct Curl_easy *data, /* Reset our connect state machine */ connssl->connecting_state = ssl_connect_1; - return CURLE_OK; +out: + return result; } -static CURLcode ossl_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, +static CURLcode ossl_connect_nonblocking(struct Curl_cfilter *cf, + struct Curl_easy *data, bool *done) { - return ossl_connect_common(data, conn, sockindex, TRUE, done); + return ossl_connect_common(cf, data, TRUE, done); } -static CURLcode ossl_connect(struct Curl_easy *data, struct connectdata *conn, - int sockindex) +static CURLcode ossl_connect(struct Curl_cfilter *cf, + struct Curl_easy *data) { CURLcode result; bool done = FALSE; - result = ossl_connect_common(data, conn, sockindex, FALSE, &done); + result = ossl_connect_common(cf, data, FALSE, &done); if(result) return result; @@ -4161,28 +4489,20 @@ static CURLcode ossl_connect(struct Curl_easy *data, struct connectdata *conn, return CURLE_OK; } -static bool ossl_data_pending(const struct connectdata *conn, - int connindex) +static bool ossl_data_pending(struct Curl_cfilter *cf, + const struct Curl_easy *data) { - const struct ssl_connect_data *connssl = &conn->ssl[connindex]; - DEBUGASSERT(connssl->backend); - if(connssl->backend->handle && SSL_pending(connssl->backend->handle)) + struct ssl_connect_data *ctx = cf->ctx; + + (void)data; + DEBUGASSERT(ctx && ctx->backend); + if(ctx->backend->handle && SSL_pending(ctx->backend->handle)) return TRUE; -#ifndef CURL_DISABLE_PROXY - { - const struct ssl_connect_data *proxyssl = &conn->proxy_ssl[connindex]; - DEBUGASSERT(proxyssl->backend); - if(proxyssl->backend->handle && SSL_pending(proxyssl->backend->handle)) - return TRUE; - } -#endif return FALSE; } -static size_t ossl_version(char *buffer, size_t size); - -static ssize_t ossl_send(struct Curl_easy *data, - int sockindex, +static ssize_t ossl_send(struct Curl_cfilter *cf, + struct Curl_easy *data, const void *mem, size_t len, CURLcode *curlcode) @@ -4194,16 +4514,16 @@ static ssize_t ossl_send(struct Curl_easy *data, unsigned long sslerror; int memlen; int rc; - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_connect_data *connssl = cf->ctx; struct ssl_backend_data *backend = connssl->backend; + (void)data; DEBUGASSERT(backend); ERR_clear_error(); memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; - set_logger(conn, data); + set_logger(connssl, data); rc = SSL_write(backend->handle, mem, memlen); if(rc <= 0) { @@ -4216,10 +4536,17 @@ static ssize_t ossl_send(struct Curl_easy *data, should be called again later. This is basically an EWOULDBLOCK equivalent. */ *curlcode = CURLE_AGAIN; - return -1; + rc = -1; + goto out; case SSL_ERROR_SYSCALL: { int sockerr = SOCKERRNO; + + if(backend->io_result == CURLE_AGAIN) { + *curlcode = CURLE_AGAIN; + rc = -1; + goto out; + } sslerror = ERR_get_error(); if(sslerror) ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)); @@ -4232,18 +4559,20 @@ static ssize_t ossl_send(struct Curl_easy *data, failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", error_buffer, sockerr); *curlcode = CURLE_SEND_ERROR; - return -1; + rc = -1; + goto out; } - case SSL_ERROR_SSL: + case SSL_ERROR_SSL: { /* A failure in the SSL library occurred, usually a protocol error. The OpenSSL error queue contains more information on the error. */ + struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next); + struct ssl_connect_data *connssl_next = cf_ssl_next? + cf_ssl_next->ctx : NULL; sslerror = ERR_get_error(); if(ERR_GET_LIB(sslerror) == ERR_LIB_SSL && ERR_GET_REASON(sslerror) == SSL_R_BIO_NOT_SET && - conn->ssl[sockindex].state == ssl_connection_complete -#ifndef CURL_DISABLE_PROXY - && conn->proxy_ssl[sockindex].state == ssl_connection_complete -#endif + connssl->state == ssl_connection_complete && + (connssl_next && connssl_next->state == ssl_connection_complete) ) { char ver[120]; (void)ossl_version(ver, sizeof(ver)); @@ -4253,20 +4582,26 @@ static ssize_t ossl_send(struct Curl_easy *data, failf(data, "SSL_write() error: %s", ossl_strerror(sslerror, error_buffer, sizeof(error_buffer))); *curlcode = CURLE_SEND_ERROR; - return -1; + rc = -1; + goto out; + } + default: + /* a true error */ + failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", + SSL_ERROR_to_str(err), SOCKERRNO); + *curlcode = CURLE_SEND_ERROR; + rc = -1; + goto out; } - /* a true error */ - failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", - SSL_ERROR_to_str(err), SOCKERRNO); - *curlcode = CURLE_SEND_ERROR; - return -1; } *curlcode = CURLE_OK; + +out: return (ssize_t)rc; /* number of bytes */ } -static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */ - int num, /* socketindex */ +static ssize_t ossl_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, /* transfer */ char *buf, /* store read data here */ size_t buffersize, /* max amount to read */ CURLcode *curlcode) @@ -4275,17 +4610,19 @@ static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */ unsigned long sslerror; ssize_t nread; int buffsize; - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[num]; + struct connectdata *conn = cf->conn; + struct ssl_connect_data *connssl = cf->ctx; struct ssl_backend_data *backend = connssl->backend; + (void)data; DEBUGASSERT(backend); ERR_clear_error(); buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; - set_logger(conn, data); + set_logger(connssl, data); nread = (ssize_t)SSL_read(backend->handle, buf, buffsize); + if(nread <= 0) { /* failed SSL_read */ int err = SSL_get_error(backend->handle, (int)nread); @@ -4295,7 +4632,7 @@ static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */ break; case SSL_ERROR_ZERO_RETURN: /* no more data */ /* close_notify alert */ - if(num == FIRSTSOCKET) + if(cf->sockindex == FIRSTSOCKET) /* mark the connection for close if it is indeed the control connection */ connclose(conn, "TLS close_notify"); @@ -4304,11 +4641,17 @@ static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */ case SSL_ERROR_WANT_WRITE: /* there's data pending, re-invoke SSL_read() */ *curlcode = CURLE_AGAIN; - return -1; + nread = -1; + goto out; default: /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return value/errno" */ /* https://www.openssl.org/docs/crypto/ERR_get_error.html */ + if(backend->io_result == CURLE_AGAIN) { + *curlcode = CURLE_AGAIN; + nread = -1; + goto out; + } sslerror = ERR_get_error(); if((nread < 0) || sslerror) { /* If the return code was negative or there actually is an error in the @@ -4325,7 +4668,8 @@ static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */ failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d", error_buffer, sockerr); *curlcode = CURLE_RECV_ERROR; - return -1; + nread = -1; + goto out; } /* For debug builds be a little stricter and error on any SSL_ERROR_SYSCALL. For example a server may have closed the connection @@ -4348,11 +4692,14 @@ static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */ " (Fatal because this is a curl debug build)", error_buffer, sockerr); *curlcode = CURLE_RECV_ERROR; - return -1; + nread = -1; + goto out; } #endif } } + +out: return nread; } @@ -4364,7 +4711,7 @@ static size_t ossl_version(char *buffer, size_t size) int count; const char *ver = OpenSSL_version(OPENSSL_VERSION); const char expected[] = OSSL_PACKAGE " "; /* ie "LibreSSL " */ - if(Curl_strncasecompare(ver, expected, sizeof(expected) - 1)) { + if(strncasecompare(ver, expected, sizeof(expected) - 1)) { ver += sizeof(expected) - 1; } count = msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, ver); @@ -4491,41 +4838,44 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl, (void *)backend->ctx : (void *)backend->handle; } -static bool ossl_associate_connection(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) +static bool ossl_attach_data(struct Curl_cfilter *cf, + struct Curl_easy *data) { - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + 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; - if(SSL_SET_OPTION(primary.sessionid)) { + config = Curl_ssl_cf_get_config(cf, data); + if(config->primary.sessionid) { int data_idx = ossl_get_ssl_data_index(); - int connectdata_idx = ossl_get_ssl_conn_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 && connectdata_idx >= 0 && sockindex_idx >= 0 && + if(data_idx >= 0 && cf_idx >= 0 && sockindex_idx >= 0 && proxy_idx >= 0) { - int data_status, conn_status, sockindex_status, proxy_status; + 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); - conn_status = SSL_set_ex_data(backend->handle, connectdata_idx, conn); + cf_status = SSL_set_ex_data(backend->handle, cf_idx, cf); sockindex_status = SSL_set_ex_data(backend->handle, sockindex_idx, - conn->sock + sockindex); + cf->conn->sock + cf->sockindex); #ifndef CURL_DISABLE_PROXY proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, - SSL_IS_PROXY() ? (void *) 1 : NULL); + 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 && conn_status && sockindex_status && proxy_status) + if(data_status && cf_status && sockindex_status && proxy_status) return TRUE; } return FALSE; @@ -4541,11 +4891,11 @@ static bool ossl_associate_connection(struct Curl_easy *data, * transfer that might still be using the same connection. */ -static void ossl_disassociate_connection(struct Curl_easy *data, - int sockindex) +static void ossl_detach_data(struct Curl_cfilter *cf, + struct Curl_easy *data) { - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + 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); @@ -4553,24 +4903,38 @@ static void ossl_disassociate_connection(struct Curl_easy *data, if(!backend->handle) return; - if(SSL_SET_OPTION(primary.sessionid)) { + if(ssl_config->primary.sessionid) { int data_idx = ossl_get_ssl_data_index(); - int connectdata_idx = ossl_get_ssl_conn_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 && connectdata_idx >= 0 && sockindex_idx >= 0 && + 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, connectdata_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) +{ +#if defined(HAVE_SSL_X509_STORE_SHARE) + if(mbackend->store) { + X509_STORE_free(mbackend->store); + } + free(mbackend->CAfile); + free(mbackend); +#else /* HAVE_SSL_X509_STORE_SHARE */ + (void)mbackend; +#endif /* HAVE_SSL_X509_STORE_SHARE */ +} + const struct Curl_ssl Curl_ssl_openssl = { { CURLSSLBACKEND_OPENSSL, "openssl" }, /* info */ @@ -4596,7 +4960,7 @@ const struct Curl_ssl Curl_ssl_openssl = { ossl_cert_status_request, /* cert_status_request */ ossl_connect, /* connect */ ossl_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ + Curl_ssl_get_select_socks,/* getsock */ ossl_get_internals, /* get_internals */ ossl_close, /* close_one */ ossl_close_all, /* close_all */ @@ -4610,8 +4974,11 @@ const struct Curl_ssl Curl_ssl_openssl = { #else NULL, /* sha256sum */ #endif - ossl_associate_connection, /* associate_connection */ - ossl_disassociate_connection /* disassociate_connection */ + ossl_attach_data, /* use of data in this connection */ + ossl_detach_data, /* 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 */ }; #endif /* USE_OPENSSL */ |