diff options
Diffstat (limited to 'Utilities/cmcurl/lib/vtls/openssl.c')
-rw-r--r-- | Utilities/cmcurl/lib/vtls/openssl.c | 897 |
1 files changed, 603 insertions, 294 deletions
diff --git a/Utilities/cmcurl/lib/vtls/openssl.c b/Utilities/cmcurl/lib/vtls/openssl.c index 1d09cad..790d358 100644 --- a/Utilities/cmcurl/lib/vtls/openssl.c +++ b/Utilities/cmcurl/lib/vtls/openssl.c @@ -41,11 +41,17 @@ #include "slist.h" #include "select.h" #include "vtls.h" +#include "keylog.h" #include "strcase.h" #include "hostcheck.h" #include "multiif.h" #include "strerror.h" #include "curl_printf.h" + +#if defined(USE_WIN32_CRYPTO) +#include <wincrypt.h> +#endif + #include <openssl/ssl.h> #include <openssl/rand.h> #include <openssl/x509v3.h> @@ -207,29 +213,17 @@ "ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH" #endif -#define ENABLE_SSLKEYLOGFILE - -#ifdef ENABLE_SSLKEYLOGFILE -typedef struct ssl_tap_state { - int master_key_length; - unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH]; - unsigned char client_random[SSL3_RANDOM_SIZE]; -} ssl_tap_state_t; -#endif /* ENABLE_SSLKEYLOGFILE */ - struct ssl_backend_data { /* these ones requires specific SSL-types */ SSL_CTX* ctx; SSL* handle; X509* server_cert; -#ifdef ENABLE_SSLKEYLOGFILE - /* tap_state holds the last seen master key if we're logging them */ - ssl_tap_state_t tap_state; +#ifndef HAVE_KEYLOG_CALLBACK + /* Set to true once a valid keylog entry has been created to avoid dupes. */ + bool keylog_done; #endif }; -#define BACKEND connssl->backend - /* * 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 @@ -238,57 +232,27 @@ struct ssl_backend_data { */ #define RAND_LOAD_LENGTH 1024 -#ifdef ENABLE_SSLKEYLOGFILE -/* The fp for the open SSLKEYLOGFILE, or NULL if not open */ -static FILE *keylog_file_fp; - #ifdef HAVE_KEYLOG_CALLBACK static void ossl_keylog_callback(const SSL *ssl, const char *line) { (void)ssl; - /* Using fputs here instead of fprintf since libcurl's fprintf replacement - may not be thread-safe. */ - if(keylog_file_fp && line && *line) { - char stackbuf[256]; - char *buf; - size_t linelen = strlen(line); - - if(linelen <= sizeof(stackbuf) - 2) - buf = stackbuf; - else { - buf = malloc(linelen + 2); - if(!buf) - return; - } - memcpy(buf, line, linelen); - buf[linelen] = '\n'; - buf[linelen + 1] = '\0'; - - fputs(buf, keylog_file_fp); - if(buf != stackbuf) - free(buf); - } + Curl_tls_keylog_write_line(line); } #else -#define KEYLOG_PREFIX "CLIENT_RANDOM " -#define KEYLOG_PREFIX_LEN (sizeof(KEYLOG_PREFIX) - 1) /* - * tap_ssl_key is called by libcurl to make the CLIENT_RANDOMs if the OpenSSL - * being used doesn't have native support for doing that. + * ossl_log_tls12_secret is called by libcurl to make the CLIENT_RANDOMs if the + * OpenSSL being used doesn't have native support for doing that. */ -static void tap_ssl_key(const SSL *ssl, ssl_tap_state_t *state) +static void +ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done) { - const char *hex = "0123456789ABCDEF"; - int pos, i; - char line[KEYLOG_PREFIX_LEN + 2 * SSL3_RANDOM_SIZE + 1 + - 2 * SSL_MAX_MASTER_KEY_LENGTH + 1 + 1]; const SSL_SESSION *session = SSL_get_session(ssl); unsigned char client_random[SSL3_RANDOM_SIZE]; unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH]; int master_key_length = 0; - if(!session || !keylog_file_fp) + if(!session || *keylog_done) return; #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ @@ -307,44 +271,17 @@ static void tap_ssl_key(const SSL *ssl, ssl_tap_state_t *state) } #endif + /* The handshake has not progressed sufficiently yet, or this is a TLS 1.3 + * session (when curl was built with older OpenSSL headers and running with + * newer OpenSSL runtime libraries). */ if(master_key_length <= 0) return; - /* Skip writing keys if there is no key or it did not change. */ - if(state->master_key_length == master_key_length && - !memcmp(state->master_key, master_key, master_key_length) && - !memcmp(state->client_random, client_random, SSL3_RANDOM_SIZE)) { - return; - } - - state->master_key_length = master_key_length; - memcpy(state->master_key, master_key, master_key_length); - memcpy(state->client_random, client_random, SSL3_RANDOM_SIZE); - - memcpy(line, KEYLOG_PREFIX, KEYLOG_PREFIX_LEN); - pos = KEYLOG_PREFIX_LEN; - - /* Client Random for SSLv3/TLS */ - for(i = 0; i < SSL3_RANDOM_SIZE; i++) { - line[pos++] = hex[client_random[i] >> 4]; - line[pos++] = hex[client_random[i] & 0xF]; - } - line[pos++] = ' '; - - /* Master Secret (size is at most SSL_MAX_MASTER_KEY_LENGTH) */ - for(i = 0; i < master_key_length; i++) { - line[pos++] = hex[master_key[i] >> 4]; - line[pos++] = hex[master_key[i] & 0xF]; - } - line[pos++] = '\n'; - line[pos] = '\0'; - - /* Using fputs here instead of fprintf since libcurl's fprintf replacement - may not be thread-safe. */ - fputs(line, keylog_file_fp); + *keylog_done = true; + Curl_tls_keylog_write("CLIENT_RANDOM", client_random, + master_key, master_key_length); } #endif /* !HAVE_KEYLOG_CALLBACK */ -#endif /* ENABLE_SSLKEYLOGFILE */ static const char *SSL_ERROR_to_str(int err) { @@ -618,12 +555,136 @@ static bool is_pkcs11_uri(const char *string) static CURLcode Curl_ossl_set_engine(struct Curl_easy *data, const char *engine); +static int +SSL_CTX_use_certificate_bio(SSL_CTX *ctx, BIO *in, int type, + const char *key_passwd) +{ + int ret = 0; + X509 *x = NULL; + + if(type == SSL_FILETYPE_ASN1) { + /* j = ERR_R_ASN1_LIB; */ + x = d2i_X509_bio(in, NULL); + } + else if(type == SSL_FILETYPE_PEM) { + /* ERR_R_PEM_LIB; */ + x = PEM_read_bio_X509(in, NULL, + passwd_callback, (void *)key_passwd); + } + else { + ret = 0; + goto end; + } + + if(x == NULL) { + ret = 0; + goto end; + } + + ret = SSL_CTX_use_certificate(ctx, x); + end: + X509_free(x); + return ret; +} + +static int +SSL_CTX_use_PrivateKey_bio(SSL_CTX *ctx, BIO* in, int type, + const char *key_passwd) +{ + int ret = 0; + EVP_PKEY *pkey = NULL; + + if(type == SSL_FILETYPE_PEM) + pkey = PEM_read_bio_PrivateKey(in, NULL, passwd_callback, + (void *)key_passwd); + else if(type == SSL_FILETYPE_ASN1) + pkey = d2i_PrivateKey_bio(in, NULL); + else { + ret = 0; + goto end; + } + if(pkey == NULL) { + ret = 0; + goto end; + } + ret = SSL_CTX_use_PrivateKey(ctx, pkey); + EVP_PKEY_free(pkey); + end: + return ret; +} + +static int +SSL_CTX_use_certificate_chain_bio(SSL_CTX *ctx, BIO* in, + const char *key_passwd) +{ +/* SSL_CTX_add1_chain_cert introduced in OpenSSL 1.0.2 */ +#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) /* 1.0.2 or later */ + int ret = 0; + X509 *x = NULL; + void *passwd_callback_userdata = (void *)key_passwd; + + ERR_clear_error(); + + x = PEM_read_bio_X509_AUX(in, NULL, + passwd_callback, (void *)key_passwd); + + if(x == NULL) { + ret = 0; + goto end; + } + + ret = SSL_CTX_use_certificate(ctx, x); + + if(ERR_peek_error() != 0) + ret = 0; + + if(ret) { + X509 *ca; + unsigned long err; + + if(!SSL_CTX_clear_chain_certs(ctx)) { + ret = 0; + goto end; + } + + while((ca = PEM_read_bio_X509(in, NULL, passwd_callback, + passwd_callback_userdata)) + != NULL) { + + if(!SSL_CTX_add0_chain_cert(ctx, ca)) { + X509_free(ca); + ret = 0; + goto end; + } + } + + err = ERR_peek_last_error(); + if((ERR_GET_LIB(err) == ERR_LIB_PEM) && + (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) + ERR_clear_error(); + else + ret = 0; + } + + end: + X509_free(x); + return ret; +#else + (void)ctx; /* unused */ + (void)in; /* unused */ + (void)key_passwd; /* unused */ + return 0; +#endif +} + static int cert_stuff(struct connectdata *conn, SSL_CTX* ctx, char *cert_file, + BIO *cert_bio, const char *cert_type, char *key_file, + BIO* key_bio, const char *key_type, char *key_passwd) { @@ -633,10 +694,11 @@ int cert_stuff(struct connectdata *conn, int file_type = do_file_type(cert_type); - if(cert_file || (file_type == SSL_FILETYPE_ENGINE)) { + if(cert_file || cert_bio || (file_type == SSL_FILETYPE_ENGINE)) { SSL *ssl; X509 *x509; int cert_done = 0; + int cert_use_result; if(key_passwd) { /* set the password in the callback userdata */ @@ -649,8 +711,10 @@ int cert_stuff(struct connectdata *conn, switch(file_type) { case SSL_FILETYPE_PEM: /* SSL_CTX_use_certificate_chain_file() only works on PEM files */ - if(SSL_CTX_use_certificate_chain_file(ctx, - cert_file) != 1) { + cert_use_result = cert_bio ? + SSL_CTX_use_certificate_chain_bio(ctx, cert_bio, key_passwd) : + SSL_CTX_use_certificate_chain_file(ctx, cert_file); + if(cert_use_result != 1) { failf(data, "could not load PEM client certificate, " OSSL_PACKAGE " error %s, " @@ -665,9 +729,12 @@ int cert_stuff(struct connectdata *conn, /* SSL_CTX_use_certificate_file() works with either PEM or ASN1, but we use the case above for PEM so this can only be performed with ASN1 files. */ - if(SSL_CTX_use_certificate_file(ctx, - cert_file, - file_type) != 1) { + + cert_use_result = cert_bio ? + SSL_CTX_use_certificate_bio(ctx, cert_bio, + file_type, key_passwd) : + SSL_CTX_use_certificate_file(ctx, cert_file, file_type); + if(cert_use_result != 1) { failf(data, "could not load ASN1 client certificate, " OSSL_PACKAGE " error %s, " @@ -747,27 +814,31 @@ int cert_stuff(struct connectdata *conn, PKCS12 *p12 = NULL; EVP_PKEY *pri; STACK_OF(X509) *ca = NULL; + if(!cert_bio) { + fp = BIO_new(BIO_s_file()); + if(fp == NULL) { + failf(data, + "BIO_new return NULL, " OSSL_PACKAGE + " error %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + return 0; + } - fp = BIO_new(BIO_s_file()); - if(fp == NULL) { - failf(data, - "BIO_new return NULL, " OSSL_PACKAGE - " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - return 0; + if(BIO_read_filename(fp, cert_file) <= 0) { + failf(data, "could not open PKCS12 file '%s'", cert_file); + BIO_free(fp); + return 0; + } } - if(BIO_read_filename(fp, cert_file) <= 0) { - failf(data, "could not open PKCS12 file '%s'", cert_file); + p12 = d2i_PKCS12_bio(cert_bio ? cert_bio : fp, NULL); + if(fp) BIO_free(fp); - return 0; - } - p12 = d2i_PKCS12_bio(fp, NULL); - BIO_free(fp); if(!p12) { - failf(data, "error reading PKCS12 file '%s'", cert_file); + failf(data, "error reading PKCS12 file '%s'", + cert_bio ? "(memory blob)" : cert_file); return 0; } @@ -848,8 +919,10 @@ int cert_stuff(struct connectdata *conn, return 0; } - if(!key_file) + if((!key_file) && (!key_bio)) { key_file = cert_file; + key_bio = cert_bio; + } else file_type = do_file_type(key_type); @@ -859,9 +932,12 @@ int cert_stuff(struct connectdata *conn, break; /* FALLTHROUGH */ case SSL_FILETYPE_ASN1: - if(SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type) != 1) { + cert_use_result = key_bio ? + SSL_CTX_use_PrivateKey_bio(ctx, key_bio, file_type, key_passwd) : + SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type); + if(cert_use_result != 1) { failf(data, "unable to set private key file: '%s' type %s", - key_file, key_type?key_type:"PEM"); + key_file?key_file:"(memory blob)", key_type?key_type:"PEM"); return 0; } break; @@ -1021,10 +1097,6 @@ static int x509_name_oneline(X509_NAME *a, char *buf, size_t size) */ static int Curl_ossl_init(void) { -#ifdef ENABLE_SSLKEYLOGFILE - char *keylog_file_name; -#endif - OPENSSL_load_builtin_modules(); #ifdef USE_OPENSSL_ENGINE @@ -1057,26 +1129,7 @@ static int Curl_ossl_init(void) OpenSSL_add_all_algorithms(); #endif -#ifdef ENABLE_SSLKEYLOGFILE - if(!keylog_file_fp) { - keylog_file_name = curl_getenv("SSLKEYLOGFILE"); - if(keylog_file_name) { - keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT); - if(keylog_file_fp) { -#ifdef WIN32 - if(setvbuf(keylog_file_fp, NULL, _IONBF, 0)) -#else - if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096)) -#endif - { - fclose(keylog_file_fp); - keylog_file_fp = NULL; - } - } - Curl_safefree(keylog_file_name); - } - } -#endif + Curl_tls_keylog_open(); /* Initialize the extra data indexes */ if(ossl_get_ssl_conn_index() < 0 || ossl_get_ssl_sockindex_index() < 0) @@ -1119,12 +1172,7 @@ static void Curl_ossl_cleanup(void) #endif #endif -#ifdef ENABLE_SSLKEYLOGFILE - if(keylog_file_fp) { - fclose(keylog_file_fp); - keylog_file_fp = NULL; - } -#endif + Curl_tls_keylog_close(); } /* @@ -1269,19 +1317,19 @@ static struct curl_slist *Curl_ossl_engines_list(struct Curl_easy *data) return list; } - static void ossl_close(struct ssl_connect_data *connssl) { - if(BACKEND->handle) { - (void)SSL_shutdown(BACKEND->handle); - SSL_set_connect_state(BACKEND->handle); + struct ssl_backend_data *backend = connssl->backend; + if(backend->handle) { + (void)SSL_shutdown(backend->handle); + SSL_set_connect_state(backend->handle); - SSL_free(BACKEND->handle); - BACKEND->handle = NULL; + SSL_free(backend->handle); + backend->handle = NULL; } - if(BACKEND->ctx) { - SSL_CTX_free(BACKEND->ctx); - BACKEND->ctx = NULL; + if(backend->ctx) { + SSL_CTX_free(backend->ctx); + backend->ctx = NULL; } } @@ -1291,7 +1339,9 @@ static void ossl_close(struct ssl_connect_data *connssl) static void Curl_ossl_close(struct connectdata *conn, int sockindex) { ossl_close(&conn->ssl[sockindex]); +#ifndef CURL_DISABLE_PROXY ossl_close(&conn->proxy_ssl[sockindex]); +#endif } /* @@ -1310,6 +1360,7 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex) int buffsize; int err; bool done = FALSE; + struct ssl_backend_data *backend = connssl->backend; #ifndef CURL_DISABLE_FTP /* This has only been tested on the proftpd server, and the mod_tls code @@ -1318,10 +1369,10 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex) we do not send one. Let's hope other servers do the same... */ if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE) - (void)SSL_shutdown(BACKEND->handle); + (void)SSL_shutdown(backend->handle); #endif - if(BACKEND->handle) { + if(backend->handle) { buffsize = (int)sizeof(buf); while(!done) { int what = SOCKET_READABLE(conn->sock[sockindex], @@ -1331,8 +1382,8 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex) /* 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 = (ssize_t)SSL_read(backend->handle, buf, buffsize); + err = SSL_get_error(backend->handle, (int)nread); switch(err) { case SSL_ERROR_NONE: /* this is not an error */ @@ -1377,7 +1428,7 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex) if(data->set.verbose) { #ifdef HAVE_SSL_GET_SHUTDOWN - switch(SSL_get_shutdown(BACKEND->handle)) { + switch(SSL_get_shutdown(backend->handle)) { case SSL_SENT_SHUTDOWN: infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN\n"); break; @@ -1392,8 +1443,8 @@ static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex) #endif } - SSL_free(BACKEND->handle); - BACKEND->handle = NULL; + SSL_free(backend->handle); + backend->handle = NULL; } return retval; } @@ -1517,10 +1568,16 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert) 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_IS_PROXY() ? conn->http_proxy.host.name : - conn->host.name; +#ifndef CURL_DISABLE_PROXY + const char * const hostname = SSL_IS_PROXY() ? + conn->http_proxy.host.name : conn->host.name; const char * const dispname = SSL_IS_PROXY() ? conn->http_proxy.host.dispname : conn->host.dispname; +#else + /* disabled proxy support */ + const char * const hostname = conn->host.name; + const char * const dispname = conn->host.dispname; +#endif #ifdef ENABLE_IPV6 if(conn->bits.ipv6_ip && @@ -1712,13 +1769,13 @@ static CURLcode verifystatus(struct connectdata *conn, const unsigned char *p; CURLcode result = CURLE_OK; struct Curl_easy *data = conn->data; - OCSP_RESPONSE *rsp = NULL; OCSP_BASICRESP *br = NULL; X509_STORE *st = NULL; STACK_OF(X509) *ch = NULL; + struct ssl_backend_data *backend = connssl->backend; - long len = SSL_get_tlsext_status_ocsp_resp(BACKEND->handle, &status); + long len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status); if(!status) { failf(data, "No OCSP response received"); @@ -1748,8 +1805,8 @@ static CURLcode verifystatus(struct connectdata *conn, goto end; } - ch = SSL_get_peer_cert_chain(BACKEND->handle); - st = SSL_CTX_get_cert_store(BACKEND->ctx); + ch = SSL_get_peer_cert_chain(backend->handle); + st = SSL_CTX_get_cert_store(backend->ctx); #if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \ (defined(LIBRESSL_VERSION_NUMBER) && \ @@ -1825,7 +1882,8 @@ static CURLcode verifystatus(struct connectdata *conn, } end: - if(br) OCSP_BASICRESP_free(br); + if(br) + OCSP_BASICRESP_free(br); OCSP_RESPONSE_free(rsp); return result; @@ -2270,7 +2328,7 @@ set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, #ifdef TLS1_3_VERSION { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - SSL_CTX_set_max_proto_version(BACKEND->ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION); *ctx_options |= SSL_OP_NO_TLSv1_2; } #else @@ -2398,27 +2456,39 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME bool sni; +#ifndef CURL_DISABLE_PROXY const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; +#else + const char * const hostname = conn->host.name; +#endif + #ifdef ENABLE_IPV6 struct in6_addr addr; #else struct in_addr addr; #endif #endif +#ifndef CURL_DISABLE_PROXY long * const certverifyresult = SSL_IS_PROXY() ? &data->set.proxy_ssl.certverifyresult : &data->set.ssl.certverifyresult; +#else + long * const certverifyresult = &data->set.ssl.certverifyresult; +#endif const long int ssl_version = SSL_CONN_CONFIG(version); #ifdef USE_TLS_SRP const enum CURL_TLSAUTH ssl_authtype = SSL_SET_OPTION(authtype); #endif char * const ssl_cert = SSL_SET_OPTION(cert); + const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(cert_blob); const char * const ssl_cert_type = SSL_SET_OPTION(cert_type); const char * const ssl_cafile = 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(CRLfile); char error_buffer[256]; + struct ssl_backend_data *backend = connssl->backend; + bool imported_native_ca = false; DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); @@ -2477,25 +2547,25 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) return CURLE_SSL_CONNECT_ERROR; } - if(BACKEND->ctx) - SSL_CTX_free(BACKEND->ctx); - BACKEND->ctx = SSL_CTX_new(req_method); + if(backend->ctx) + SSL_CTX_free(backend->ctx); + backend->ctx = SSL_CTX_new(req_method); - if(!BACKEND->ctx) { + if(!backend->ctx) { failf(data, "SSL: couldn't create a context: %s", ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer))); return CURLE_OUT_OF_MEMORY; } #ifdef SSL_MODE_RELEASE_BUFFERS - SSL_CTX_set_mode(BACKEND->ctx, SSL_MODE_RELEASE_BUFFERS); + SSL_CTX_set_mode(backend->ctx, SSL_MODE_RELEASE_BUFFERS); #endif #ifdef SSL_CTRL_SET_MSG_CALLBACK if(data->set.fdebug && data->set.verbose) { /* the SSL trace callback is only used for verbose logging */ - SSL_CTX_set_msg_callback(BACKEND->ctx, ssl_tls_trace); - SSL_CTX_set_msg_callback_arg(BACKEND->ctx, conn); + SSL_CTX_set_msg_callback(backend->ctx, ssl_tls_trace); + SSL_CTX_set_msg_callback_arg(backend->ctx, conn); } #endif @@ -2561,8 +2631,8 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) /* "--sslv2" option means SSLv2 only, disable all others */ case CURL_SSLVERSION_SSLv2: #if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */ - SSL_CTX_set_min_proto_version(BACKEND->ctx, SSL2_VERSION); - SSL_CTX_set_max_proto_version(BACKEND->ctx, SSL2_VERSION); + SSL_CTX_set_min_proto_version(backend->ctx, SSL2_VERSION); + SSL_CTX_set_max_proto_version(backend->ctx, SSL2_VERSION); #else ctx_options |= SSL_OP_NO_SSLv3; ctx_options |= SSL_OP_NO_TLSv1; @@ -2579,8 +2649,8 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) /* "--sslv3" option means SSLv3 only, disable all others */ case CURL_SSLVERSION_SSLv3: #if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */ - SSL_CTX_set_min_proto_version(BACKEND->ctx, SSL3_VERSION); - SSL_CTX_set_max_proto_version(BACKEND->ctx, SSL3_VERSION); + SSL_CTX_set_min_proto_version(backend->ctx, SSL3_VERSION); + SSL_CTX_set_max_proto_version(backend->ctx, SSL3_VERSION); #else ctx_options |= SSL_OP_NO_SSLv2; ctx_options |= SSL_OP_NO_TLSv1; @@ -2607,7 +2677,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) 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(backend->ctx, conn); #else result = set_ssl_version_min_max_legacy(&ctx_options, conn, sockindex); #endif @@ -2620,11 +2690,11 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) return CURLE_SSL_CONNECT_ERROR; } - SSL_CTX_set_options(BACKEND->ctx, ctx_options); + SSL_CTX_set_options(backend->ctx, ctx_options); #ifdef HAS_NPN if(conn->bits.tls_enable_npn) - SSL_CTX_set_next_proto_select_cb(BACKEND->ctx, select_next_proto_cb, conn); + SSL_CTX_set_next_proto_select_cb(backend->ctx, select_next_proto_cb, conn); #endif #ifdef HAS_ALPN @@ -2633,8 +2703,11 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) unsigned char protocols[128]; #ifdef USE_NGHTTP2 - if(data->set.httpversion >= CURL_HTTP_VERSION_2 && - (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy)) { + if(data->set.httpversion >= CURL_HTTP_VERSION_2 +#ifndef CURL_DISABLE_PROXY + && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) +#endif + ) { protocols[cur++] = NGHTTP2_PROTO_VERSION_ID_LEN; memcpy(&protocols[cur], NGHTTP2_PROTO_VERSION_ID, @@ -2652,14 +2725,37 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) /* expects length prefixed preference ordered list of protocols in wire * format */ - SSL_CTX_set_alpn_protos(BACKEND->ctx, protocols, cur); + SSL_CTX_set_alpn_protos(backend->ctx, protocols, cur); } #endif - if(ssl_cert || ssl_cert_type) { - if(!cert_stuff(conn, BACKEND->ctx, ssl_cert, ssl_cert_type, - SSL_SET_OPTION(key), SSL_SET_OPTION(key_type), - SSL_SET_OPTION(key_passwd))) { + if(ssl_cert || ssl_cert_blob || ssl_cert_type) { + BIO *ssl_cert_bio = NULL; + BIO *ssl_key_bio = NULL; + int result_cert_stuff; + if(ssl_cert_blob) { + /* the typecast of blob->len is fine since it is guaranteed to never be + larger than CURL_MAX_INPUT_LENGTH */ + ssl_cert_bio = BIO_new_mem_buf(ssl_cert_blob->data, + (int)ssl_cert_blob->len); + if(!ssl_cert_bio) + return CURLE_SSL_CERTPROBLEM; + } + if(SSL_SET_OPTION(key_blob)) { + ssl_key_bio = BIO_new_mem_buf(SSL_SET_OPTION(key_blob)->data, + (int)SSL_SET_OPTION(key_blob)->len); + if(!ssl_key_bio) + return CURLE_SSL_CERTPROBLEM; + } + result_cert_stuff = cert_stuff(conn, backend->ctx, + ssl_cert, ssl_cert_bio, ssl_cert_type, + SSL_SET_OPTION(key), ssl_key_bio, + SSL_SET_OPTION(key_type), SSL_SET_OPTION(key_passwd)); + if(ssl_cert_bio) + BIO_free(ssl_cert_bio); + if(ssl_key_bio) + BIO_free(ssl_key_bio); + if(!result_cert_stuff) { /* failf() is already done in cert_stuff() */ return CURLE_SSL_CERTPROBLEM; } @@ -2669,7 +2765,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) if(!ciphers) ciphers = (char *)DEFAULT_CIPHER_SELECTION; if(ciphers) { - if(!SSL_CTX_set_cipher_list(BACKEND->ctx, ciphers)) { + if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) { failf(data, "failed setting cipher list: %s", ciphers); return CURLE_SSL_CIPHER; } @@ -2680,7 +2776,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) { char *ciphers13 = SSL_CONN_CONFIG(cipher_list13); if(ciphers13) { - if(!SSL_CTX_set_ciphersuites(BACKEND->ctx, ciphers13)) { + if(!SSL_CTX_set_ciphersuites(backend->ctx, ciphers13)) { failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13); return CURLE_SSL_CIPHER; } @@ -2691,7 +2787,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) #ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH /* OpenSSL 1.1.1 requires clients to opt-in for PHA */ - SSL_CTX_set_post_handshake_auth(BACKEND->ctx, 1); + SSL_CTX_set_post_handshake_auth(backend->ctx, 1); #endif #ifdef USE_TLS_SRP @@ -2700,18 +2796,18 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) infof(data, "Using TLS-SRP username: %s\n", ssl_username); - if(!SSL_CTX_set_srp_username(BACKEND->ctx, ssl_username)) { + if(!SSL_CTX_set_srp_username(backend->ctx, ssl_username)) { failf(data, "Unable to set SRP user name"); return CURLE_BAD_FUNCTION_ARGUMENT; } - if(!SSL_CTX_set_srp_password(BACKEND->ctx, SSL_SET_OPTION(password))) { + if(!SSL_CTX_set_srp_password(backend->ctx, SSL_SET_OPTION(password))) { failf(data, "failed setting SRP password"); return CURLE_BAD_FUNCTION_ARGUMENT; } if(!SSL_CONN_CONFIG(cipher_list)) { infof(data, "Setting cipher list SRP\n"); - if(!SSL_CTX_set_cipher_list(BACKEND->ctx, "SRP")) { + if(!SSL_CTX_set_cipher_list(backend->ctx, "SRP")) { failf(data, "failed setting SRP cipher list"); return CURLE_SSL_CIPHER; } @@ -2719,11 +2815,186 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) } #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://tools.ietf.org/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 = CertOpenSystemStoreA((HCRYPTPROV_LEGACY)NULL, "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\"\n", 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(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\"\n", 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\n"); + else + infof(data, "error importing windows ca store, continuing anyway\n"); + } +#endif + +#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) + /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */ + { + if(ssl_cafile) { + if(!SSL_CTX_load_verify_file(backend->ctx, ssl_cafile)) { + if(verifypeer) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate file: %s", ssl_cafile); + return CURLE_SSL_CACERT_BADFILE; + } + /* Continue with a warning if no certificate verif is required. */ + infof(data, "error setting certificate file, continuing anyway\n"); + } + infof(data, " CAfile: %s\n", ssl_cafile); + } + if(ssl_capath) { + if(!SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) { + if(verifypeer) { + /* Fail if we insist on successfully verifying the server. */ + failf(data, "error setting certificate path: %s", ssl_capath); + return CURLE_SSL_CACERT_BADFILE; + } + /* Continue with a warning if no certificate verif is required. */ + infof(data, "error setting certificate path, continuing anyway\n"); + } + infof(data, " CApath: %s\n", ssl_capath); + } + } +#else if(ssl_cafile || ssl_capath) { /* tell SSL where to find CA certificates that are used to verify the servers certificate. */ - if(!SSL_CTX_load_verify_locations(BACKEND->ctx, ssl_cafile, ssl_capath)) { - if(verifypeer) { + if(!SSL_CTX_load_verify_locations(backend->ctx, ssl_cafile, ssl_capath)) { + if(verifypeer && !imported_native_ca) { /* Fail if we insist on successfully verifying the server. */ failf(data, "error setting certificate verify locations:\n" " CAfile: %s\n CApath: %s", @@ -2731,7 +3002,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) ssl_capath ? ssl_capath : "none"); return CURLE_SSL_CACERT_BADFILE; } - /* Just continue with a warning if no strict certificate verification + /* Just continue with a warning if no strict certificate verification is required. */ infof(data, "error setting certificate verify locations," " continuing anyway:\n"); @@ -2746,18 +3017,20 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) ssl_cafile ? ssl_cafile : "none", ssl_capath ? ssl_capath : "none"); } +#endif + #ifdef CURL_CA_FALLBACK - else if(verifypeer) { + if(verifypeer && !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); + SSL_CTX_set_default_verify_paths(backend->ctx); } #endif if(ssl_crlfile) { /* tell SSL where to find CRL file that is used to check certificate * revocation */ - lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(BACKEND->ctx), + 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)) ) { @@ -2766,7 +3039,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) } /* Everything is fine. */ infof(data, "successfully load CRL file:\n"); - X509_STORE_set_flags(SSL_CTX_get_cert_store(BACKEND->ctx), + 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\n", ssl_crlfile); @@ -2775,22 +3048,25 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) 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 which gives us the same - fix without as much of a performance hit (slight), so we prefer that if - available. + 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) && !defined(X509_V_FLAG_NO_ALT_CHAINS) - X509_STORE_set_flags(SSL_CTX_get_cert_store(BACKEND->ctx), +#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)) { + 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. */ - X509_STORE_set_flags(SSL_CTX_get_cert_store(BACKEND->ctx), + only, instead of needing the whole chain. + + Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we + cannot do partial chains with CRL check. + */ + X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx), X509_V_FLAG_PARTIAL_CHAIN); } #endif @@ -2800,13 +3076,13 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) * 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 * SSL_get_verify_result() below. */ - SSL_CTX_set_verify(BACKEND->ctx, + SSL_CTX_set_verify(backend->ctx, verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); /* Enable logging of secrets to the file specified in env SSLKEYLOGFILE. */ -#if defined(ENABLE_SSLKEYLOGFILE) && defined(HAVE_KEYLOG_CALLBACK) - if(keylog_file_fp) { - SSL_CTX_set_keylog_callback(BACKEND->ctx, ossl_keylog_callback); +#ifdef HAVE_KEYLOG_CALLBACK + if(Curl_tls_keylog_enabled()) { + SSL_CTX_set_keylog_callback(backend->ctx, ossl_keylog_callback); } #endif @@ -2814,14 +3090,14 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) * callback. Use the "external storage" mode to avoid that OpenSSL creates * an internal session cache. */ - SSL_CTX_set_session_cache_mode(BACKEND->ctx, + SSL_CTX_set_session_cache_mode(backend->ctx, SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL); - SSL_CTX_sess_set_new_cb(BACKEND->ctx, ossl_new_session_cb); + SSL_CTX_sess_set_new_cb(backend->ctx, ossl_new_session_cb); /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { Curl_set_in_callback(data, true); - result = (*data->set.ssl.fsslctx)(data, BACKEND->ctx, + result = (*data->set.ssl.fsslctx)(data, backend->ctx, data->set.ssl.fsslctxp); Curl_set_in_callback(data, false); if(result) { @@ -2831,10 +3107,10 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) } /* Lets make an SSL structure */ - if(BACKEND->handle) - SSL_free(BACKEND->handle); - BACKEND->handle = SSL_new(BACKEND->ctx); - if(!BACKEND->handle) { + if(backend->handle) + SSL_free(backend->handle); + backend->handle = SSL_new(backend->ctx); + if(!backend->handle) { failf(data, "SSL: couldn't create a context (handle)!"); return CURLE_OUT_OF_MEMORY; } @@ -2842,23 +3118,23 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \ !defined(OPENSSL_NO_OCSP) if(SSL_CONN_CONFIG(verifystatus)) - SSL_set_tlsext_status_type(BACKEND->handle, TLSEXT_STATUSTYPE_ocsp); + SSL_set_tlsext_status_type(backend->handle, TLSEXT_STATUSTYPE_ocsp); #endif #if defined(OPENSSL_IS_BORINGSSL) && defined(ALLOW_RENEG) - SSL_set_renegotiate_mode(BACKEND->handle, ssl_renegotiate_freely); + SSL_set_renegotiate_mode(backend->handle, ssl_renegotiate_freely); #endif - SSL_set_connect_state(BACKEND->handle); + SSL_set_connect_state(backend->handle); - BACKEND->server_cert = 0x0; + backend->server_cert = 0x0; #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) && #ifdef ENABLE_IPV6 (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) && #endif sni && - !SSL_set_tlsext_host_name(BACKEND->handle, hostname)) + !SSL_set_tlsext_host_name(backend->handle, hostname)) infof(data, "WARNING: failed to configure server name indication (SNI) " "TLS extension\n"); #endif @@ -2872,14 +3148,14 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) if(connectdata_idx >= 0 && sockindex_idx >= 0) { /* Store the data needed for the "new session" callback. * The sockindex is stored as a pointer to an array element. */ - SSL_set_ex_data(BACKEND->handle, connectdata_idx, conn); - SSL_set_ex_data(BACKEND->handle, sockindex_idx, conn->sock + sockindex); + SSL_set_ex_data(backend->handle, connectdata_idx, conn); + SSL_set_ex_data(backend->handle, sockindex_idx, conn->sock + sockindex); } Curl_ssl_sessionid_lock(conn); if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL, sockindex)) { /* we got a session id, use it! */ - if(!SSL_set_session(BACKEND->handle, ssl_sessionid)) { + if(!SSL_set_session(backend->handle, ssl_sessionid)) { Curl_ssl_sessionid_unlock(conn); failf(data, "SSL: SSL_set_session failed: %s", ossl_strerror(ERR_get_error(), error_buffer, @@ -2892,6 +3168,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) Curl_ssl_sessionid_unlock(conn); } +#ifndef CURL_DISABLE_PROXY if(conn->proxy_ssl[sockindex].use) { BIO *const bio = BIO_new(BIO_f_ssl()); SSL *handle = conn->proxy_ssl[sockindex].backend->handle; @@ -2899,9 +3176,11 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex) DEBUGASSERT(handle != NULL); DEBUGASSERT(bio != NULL); BIO_set_ssl(bio, handle, FALSE); - SSL_set_bio(BACKEND->handle, bio, bio); + SSL_set_bio(backend->handle, bio, bio); } - else if(!SSL_set_fd(BACKEND->handle, (int)sockfd)) { + 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))); @@ -2918,26 +3197,34 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex) struct Curl_easy *data = conn->data; int err; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; +#ifndef CURL_DISABLE_PROXY long * const certverifyresult = SSL_IS_PROXY() ? &data->set.proxy_ssl.certverifyresult : &data->set.ssl.certverifyresult; +#else + long * const certverifyresult = &data->set.ssl.certverifyresult; +#endif + struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); ERR_clear_error(); - err = SSL_connect(BACKEND->handle); - /* If keylogging is enabled but the keylog callback is not supported then log - secrets here, immediately after SSL_connect by using tap_ssl_key. */ -#if defined(ENABLE_SSLKEYLOGFILE) && !defined(HAVE_KEYLOG_CALLBACK) - tap_ssl_key(BACKEND->handle, &BACKEND->tap_state); + err = SSL_connect(backend->handle); +#ifndef HAVE_KEYLOG_CALLBACK + if(Curl_tls_keylog_enabled()) { + /* If key logging is enabled, wait for the handshake to complete and then + * proceed with logging secrets (for TLS 1.2 or older). + */ + ossl_log_tls12_secret(backend->handle, &backend->keylog_done); + } #endif /* 1 is fine 0 is "not successful but was shut down controlled" <0 is "handshake was not successful, because a fatal error occurred" */ if(1 != err) { - int detail = SSL_get_error(BACKEND->handle, err); + int detail = SSL_get_error(backend->handle, err); if(SSL_ERROR_WANT_READ == detail) { connssl->connecting_state = ssl_connect_2_reading; @@ -2977,7 +3264,7 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex) (reason == SSL_R_CERTIFICATE_VERIFY_FAILED)) { result = CURLE_PEER_FAILED_VERIFICATION; - lerr = SSL_get_verify_result(BACKEND->handle); + lerr = SSL_get_verify_result(backend->handle); if(lerr != X509_V_OK) { *certverifyresult = lerr; msnprintf(error_buffer, sizeof(error_buffer), @@ -3001,9 +3288,14 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex) * the SO_ERROR is also lost. */ if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) { +#ifndef CURL_DISABLE_PROXY const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name; const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port; +#else + const char * const hostname = conn->host.name; + const long int port = conn->remote_port; +#endif char extramsg[80]=""; int sockerr = SOCKERRNO; if(sockerr && detail == SSL_ERROR_SYSCALL) @@ -3026,8 +3318,8 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex) /* Informational message */ infof(data, "SSL connection using %s / %s\n", - get_ssl_version_txt(BACKEND->handle), - SSL_get_cipher(BACKEND->handle)); + get_ssl_version_txt(backend->handle), + SSL_get_cipher(backend->handle)); #ifdef HAS_ALPN /* Sets data and len to negotiated protocol, len is 0 if no protocol was @@ -3036,7 +3328,7 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex) if(conn->bits.tls_enable_alpn) { const unsigned char *neg_protocol; unsigned int len; - SSL_get0_alpn_selected(BACKEND->handle, &neg_protocol, &len); + SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len); if(len != 0) { infof(data, "ALPN, server accepted to use %.*s\n", len, neg_protocol); @@ -3171,8 +3463,9 @@ static CURLcode get_cert_chain(struct connectdata *conn, struct Curl_easy *data = conn->data; numcert_t numcerts; BIO *mem; + struct ssl_backend_data *backend = connssl->backend; - sk = SSL_get_peer_cert_chain(BACKEND->handle); + sk = SSL_get_peer_cert_chain(backend->handle); if(!sk) { return CURLE_OUT_OF_MEMORY; } @@ -3455,16 +3748,21 @@ static CURLcode servercert(struct connectdata *conn, char error_buffer[256]=""; char buffer[2048]; const char *ptr; +#ifndef CURL_DISABLE_PROXY long * const certverifyresult = SSL_IS_PROXY() ? &data->set.proxy_ssl.certverifyresult : &data->set.ssl.certverifyresult; +#else + long * const certverifyresult = &data->set.ssl.certverifyresult; +#endif BIO *mem = BIO_new(BIO_s_mem()); + struct ssl_backend_data *backend = connssl->backend; if(data->set.ssl.certinfo) /* we've been asked to gather certificate info! */ (void)get_cert_chain(conn, connssl); - BACKEND->server_cert = SSL_get_peer_certificate(BACKEND->handle); - if(!BACKEND->server_cert) { + backend->server_cert = SSL_get_peer_certificate(backend->handle); + if(!backend->server_cert) { BIO_free(mem); if(!strict) return CURLE_OK; @@ -3475,19 +3773,19 @@ static CURLcode servercert(struct connectdata *conn, infof(data, "%s certificate:\n", SSL_IS_PROXY() ? "Proxy" : "Server"); - rc = x509_name_oneline(X509_get_subject_name(BACKEND->server_cert), + rc = x509_name_oneline(X509_get_subject_name(backend->server_cert), buffer, sizeof(buffer)); infof(data, " subject: %s\n", rc?"[NONE]":buffer); #ifndef CURL_DISABLE_VERBOSE_STRINGS { long len; - ASN1_TIME_print(mem, X509_get0_notBefore(BACKEND->server_cert)); + ASN1_TIME_print(mem, X509_get0_notBefore(backend->server_cert)); len = BIO_get_mem_data(mem, (char **) &ptr); infof(data, " start date: %.*s\n", len, ptr); (void)BIO_reset(mem); - ASN1_TIME_print(mem, X509_get0_notAfter(BACKEND->server_cert)); + ASN1_TIME_print(mem, X509_get0_notAfter(backend->server_cert)); len = BIO_get_mem_data(mem, (char **) &ptr); infof(data, " expire date: %.*s\n", len, ptr); (void)BIO_reset(mem); @@ -3497,15 +3795,15 @@ static CURLcode servercert(struct connectdata *conn, BIO_free(mem); if(SSL_CONN_CONFIG(verifyhost)) { - result = verifyhost(conn, BACKEND->server_cert); + result = verifyhost(conn, backend->server_cert); if(result) { - X509_free(BACKEND->server_cert); - BACKEND->server_cert = NULL; + X509_free(backend->server_cert); + backend->server_cert = NULL; return result; } } - rc = x509_name_oneline(X509_get_issuer_name(BACKEND->server_cert), + rc = x509_name_oneline(X509_get_issuer_name(backend->server_cert), buffer, sizeof(buffer)); if(rc) { if(strict) @@ -3519,27 +3817,32 @@ static CURLcode servercert(struct connectdata *conn, deallocating the certificate. */ /* e.g. match issuer name with provided issuer certificate */ - if(SSL_SET_OPTION(issuercert)) { - fp = BIO_new(BIO_s_file()); - if(fp == NULL) { - failf(data, - "BIO_new return NULL, " OSSL_PACKAGE - " error %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer)) ); - X509_free(BACKEND->server_cert); - BACKEND->server_cert = NULL; - return CURLE_OUT_OF_MEMORY; - } + if(SSL_SET_OPTION(issuercert) || SSL_SET_OPTION(issuercert_blob)) { + if(SSL_SET_OPTION(issuercert_blob)) + fp = BIO_new_mem_buf(SSL_SET_OPTION(issuercert_blob)->data, + (int)SSL_SET_OPTION(issuercert_blob)->len); + else { + fp = BIO_new(BIO_s_file()); + if(fp == NULL) { + failf(data, + "BIO_new return NULL, " OSSL_PACKAGE + " error %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer)) ); + X509_free(backend->server_cert); + backend->server_cert = NULL; + return CURLE_OUT_OF_MEMORY; + } - if(BIO_read_filename(fp, SSL_SET_OPTION(issuercert)) <= 0) { - if(strict) - failf(data, "SSL: Unable to open issuer cert (%s)", - SSL_SET_OPTION(issuercert)); - BIO_free(fp); - X509_free(BACKEND->server_cert); - BACKEND->server_cert = NULL; - return CURLE_SSL_ISSUER_ERROR; + if(BIO_read_filename(fp, SSL_SET_OPTION(issuercert)) <= 0) { + if(strict) + failf(data, "SSL: Unable to open issuer cert (%s)", + SSL_SET_OPTION(issuercert)); + BIO_free(fp); + X509_free(backend->server_cert); + backend->server_cert = NULL; + return CURLE_SSL_ISSUER_ERROR; + } } issuer = PEM_read_bio_X509(fp, NULL, ZERO_NULL, NULL); @@ -3549,19 +3852,19 @@ static CURLcode servercert(struct connectdata *conn, SSL_SET_OPTION(issuercert)); BIO_free(fp); X509_free(issuer); - X509_free(BACKEND->server_cert); - BACKEND->server_cert = NULL; + X509_free(backend->server_cert); + backend->server_cert = NULL; return CURLE_SSL_ISSUER_ERROR; } - if(X509_check_issued(issuer, BACKEND->server_cert) != X509_V_OK) { + if(X509_check_issued(issuer, backend->server_cert) != X509_V_OK) { if(strict) failf(data, "SSL: Certificate issuer check failed (%s)", SSL_SET_OPTION(issuercert)); BIO_free(fp); X509_free(issuer); - X509_free(BACKEND->server_cert); - BACKEND->server_cert = NULL; + X509_free(backend->server_cert); + backend->server_cert = NULL; return CURLE_SSL_ISSUER_ERROR; } @@ -3571,7 +3874,7 @@ static CURLcode servercert(struct connectdata *conn, X509_free(issuer); } - lerr = *certverifyresult = SSL_get_verify_result(BACKEND->handle); + lerr = *certverifyresult = SSL_get_verify_result(backend->handle); if(*certverifyresult != X509_V_OK) { if(SSL_CONN_CONFIG(verifypeer)) { @@ -3596,8 +3899,8 @@ static CURLcode servercert(struct connectdata *conn, if(SSL_CONN_CONFIG(verifystatus)) { result = verifystatus(conn, connssl); if(result) { - X509_free(BACKEND->server_cert); - BACKEND->server_cert = NULL; + X509_free(backend->server_cert); + backend->server_cert = NULL; return result; } } @@ -3610,13 +3913,13 @@ static CURLcode servercert(struct connectdata *conn, ptr = SSL_IS_PROXY() ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; if(!result && ptr) { - result = pkp_pin_peer_pubkey(data, BACKEND->server_cert, ptr); + result = pkp_pin_peer_pubkey(data, backend->server_cert, ptr); if(result) failf(data, "SSL: public key does not match pinned public key!"); } - X509_free(BACKEND->server_cert); - BACKEND->server_cert = NULL; + X509_free(backend->server_cert); + backend->server_cert = NULL; connssl->connecting_state = ssl_connect_done; return result; @@ -3657,7 +3960,6 @@ static CURLcode ossl_connect_common(struct connectdata *conn, struct Curl_easy *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; curl_socket_t sockfd = conn->sock[sockindex]; - timediff_t timeout_ms; int what; /* check if the connection has already been established */ @@ -3668,7 +3970,7 @@ static CURLcode ossl_connect_common(struct connectdata *conn, if(ssl_connect_1 == connssl->connecting_state) { /* Find out how much more time we're allowed */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); + const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { /* no need to continue if time already is up */ @@ -3686,7 +3988,7 @@ static CURLcode ossl_connect_common(struct connectdata *conn, ssl_connect_2_writing == connssl->connecting_state) { /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); + const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { /* no need to continue if time already is up */ @@ -3704,7 +4006,7 @@ static CURLcode ossl_connect_common(struct connectdata *conn, connssl->connecting_state?sockfd:CURL_SOCKET_BAD; what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking?0:(time_t)timeout_ms); + nonblocking?0:timeout_ms); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); @@ -3783,14 +4085,15 @@ static bool Curl_ossl_data_pending(const struct connectdata *conn, int connindex) { const struct ssl_connect_data *connssl = &conn->ssl[connindex]; - const struct ssl_connect_data *proxyssl = &conn->proxy_ssl[connindex]; - if(connssl->backend->handle && SSL_pending(connssl->backend->handle)) return TRUE; - - if(proxyssl->backend->handle && SSL_pending(proxyssl->backend->handle)) - return TRUE; - +#ifndef CURL_DISABLE_PROXY + { + const struct ssl_connect_data *proxyssl = &conn->proxy_ssl[connindex]; + if(proxyssl->backend->handle && SSL_pending(proxyssl->backend->handle)) + return TRUE; + } +#endif return FALSE; } @@ -3810,14 +4113,15 @@ static ssize_t ossl_send(struct connectdata *conn, int memlen; int rc; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; ERR_clear_error(); memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; - rc = SSL_write(BACKEND->handle, mem, memlen); + rc = SSL_write(backend->handle, mem, memlen); if(rc <= 0) { - err = SSL_get_error(BACKEND->handle, rc); + err = SSL_get_error(backend->handle, rc); switch(err) { case SSL_ERROR_WANT_READ: @@ -3850,8 +4154,11 @@ static ssize_t ossl_send(struct connectdata *conn, 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 && - conn->proxy_ssl[sockindex].state == ssl_connection_complete) { + conn->ssl[sockindex].state == ssl_connection_complete +#ifndef CURL_DISABLE_PROXY + && conn->proxy_ssl[sockindex].state == ssl_connection_complete +#endif + ) { char ver[120]; Curl_ossl_version(ver, 120); failf(conn->data, "Error: %s does not support double SSL tunneling.", @@ -3884,14 +4191,15 @@ static ssize_t ossl_recv(struct connectdata *conn, /* connection data */ ssize_t nread; int buffsize; struct ssl_connect_data *connssl = &conn->ssl[num]; + struct ssl_backend_data *backend = connssl->backend; ERR_clear_error(); buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; - nread = (ssize_t)SSL_read(BACKEND->handle, buf, buffsize); + nread = (ssize_t)SSL_read(backend->handle, buf, buffsize); if(nread <= 0) { /* failed SSL_read */ - int err = SSL_get_error(BACKEND->handle, (int)nread); + int err = SSL_get_error(backend->handle, (int)nread); switch(err) { case SSL_ERROR_NONE: /* this is not an error */ @@ -4074,7 +4382,7 @@ static CURLcode Curl_ossl_sha256sum(const unsigned char *tmp, /* input */ unsigned int len = 0; (void) unused; - mdctx = EVP_MD_CTX_create(); + mdctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL); EVP_DigestUpdate(mdctx, tmp, tmplen); EVP_DigestFinal_ex(mdctx, sha256sum, &len); @@ -4097,8 +4405,9 @@ static void *Curl_ossl_get_internals(struct ssl_connect_data *connssl, CURLINFO info) { /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */ + struct ssl_backend_data *backend = connssl->backend; return info == CURLINFO_TLS_SESSION ? - (void *)BACKEND->ctx : (void *)BACKEND->handle; + (void *)backend->ctx : (void *)backend->handle; } const struct Curl_ssl Curl_ssl_openssl = { |