diff options
author | monster <monster@ydb.tech> | 2022-07-07 14:41:37 +0300 |
---|---|---|
committer | monster <monster@ydb.tech> | 2022-07-07 14:41:37 +0300 |
commit | 06e5c21a835c0e923506c4ff27929f34e00761c2 (patch) | |
tree | 75efcbc6854ef9bd476eb8bf00cc5c900da436a2 /contrib/libs/curl/lib/vtls | |
parent | 03f024c4412e3aa613bb543cf1660176320ba8f4 (diff) | |
download | ydb-06e5c21a835c0e923506c4ff27929f34e00761c2.tar.gz |
fix ya.make
Diffstat (limited to 'contrib/libs/curl/lib/vtls')
-rw-r--r-- | contrib/libs/curl/lib/vtls/gskit.c | 1327 | ||||
-rw-r--r-- | contrib/libs/curl/lib/vtls/gtls.c | 1702 | ||||
-rw-r--r-- | contrib/libs/curl/lib/vtls/mbedtls.c | 1271 | ||||
-rw-r--r-- | contrib/libs/curl/lib/vtls/nss.c | 2541 | ||||
-rw-r--r-- | contrib/libs/curl/lib/vtls/schannel.c | 2488 | ||||
-rw-r--r-- | contrib/libs/curl/lib/vtls/schannel_verify.c | 742 |
6 files changed, 0 insertions, 10071 deletions
diff --git a/contrib/libs/curl/lib/vtls/gskit.c b/contrib/libs/curl/lib/vtls/gskit.c deleted file mode 100644 index 7a65f92f20..0000000000 --- a/contrib/libs/curl/lib/vtls/gskit.c +++ /dev/null @@ -1,1327 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -#include "curl_setup.h" - -#ifdef USE_GSKIT - -#include <gskssl.h> -#include <qsoasync.h> -#undef HAVE_SOCKETPAIR /* because the native one isn't good enough */ -#include "socketpair.h" -#include "strerror.h" - -/* Some symbols are undefined/unsupported on OS400 versions < V7R1. */ -#ifndef GSK_SSL_EXTN_SERVERNAME_REQUEST -#define GSK_SSL_EXTN_SERVERNAME_REQUEST 230 -#endif - -#ifndef GSK_TLSV10_CIPHER_SPECS -#define GSK_TLSV10_CIPHER_SPECS 236 -#endif - -#ifndef GSK_TLSV11_CIPHER_SPECS -#define GSK_TLSV11_CIPHER_SPECS 237 -#endif - -#ifndef GSK_TLSV12_CIPHER_SPECS -#define GSK_TLSV12_CIPHER_SPECS 238 -#endif - -#ifndef GSK_PROTOCOL_TLSV11 -#define GSK_PROTOCOL_TLSV11 437 -#endif - -#ifndef GSK_PROTOCOL_TLSV12 -#define GSK_PROTOCOL_TLSV12 438 -#endif - -#ifndef GSK_FALSE -#define GSK_FALSE 0 -#endif - -#ifndef GSK_TRUE -#define GSK_TRUE 1 -#endif - - -#include <limits.h> - -#include <curl/curl.h> -#include "urldata.h" -#include "sendf.h" -#include "gskit.h" -#include "vtls.h" -#include "connect.h" /* for the connect timeout */ -#include "select.h" -#include "strcase.h" -#include "timediff.h" -#include "x509asn1.h" -#include "curl_printf.h" - -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - - -/* Directions. */ -#define SOS_READ 0x01 -#define SOS_WRITE 0x02 - -/* SSL version flags. */ -#define CURL_GSKPROTO_SSLV2 0 -#define CURL_GSKPROTO_SSLV2_MASK (1 << CURL_GSKPROTO_SSLV2) -#define CURL_GSKPROTO_SSLV3 1 -#define CURL_GSKPROTO_SSLV3_MASK (1 << CURL_GSKPROTO_SSLV3) -#define CURL_GSKPROTO_TLSV10 2 -#define CURL_GSKPROTO_TLSV10_MASK (1 << CURL_GSKPROTO_TLSV10) -#define CURL_GSKPROTO_TLSV11 3 -#define CURL_GSKPROTO_TLSV11_MASK (1 << CURL_GSKPROTO_TLSV11) -#define CURL_GSKPROTO_TLSV12 4 -#define CURL_GSKPROTO_TLSV12_MASK (1 << CURL_GSKPROTO_TLSV12) -#define CURL_GSKPROTO_LAST 5 - -struct ssl_backend_data { - gsk_handle handle; - int iocport; -#ifndef CURL_DISABLE_PROXY - int localfd; - int remotefd; -#endif -}; - -#define BACKEND connssl->backend - -/* Supported ciphers. */ -struct gskit_cipher { - const char *name; /* Cipher name. */ - const char *gsktoken; /* Corresponding token for GSKit String. */ - unsigned int versions; /* SSL version flags. */ -}; - -static const struct gskit_cipher ciphertable[] = { - { "null-md5", "01", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "null-sha", "02", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "exp-rc4-md5", "03", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK }, - { "rc4-md5", "04", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "rc4-sha", "05", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "exp-rc2-cbc-md5", "06", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK }, - { "exp-des-cbc-sha", "09", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK }, - { "des-cbc3-sha", "0A", - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK }, - { "aes128-sha", "2F", - CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK | - CURL_GSKPROTO_TLSV12_MASK }, - { "aes256-sha", "35", - CURL_GSKPROTO_TLSV10_MASK | CURL_GSKPROTO_TLSV11_MASK | - CURL_GSKPROTO_TLSV12_MASK }, - { "null-sha256", "3B", CURL_GSKPROTO_TLSV12_MASK }, - { "aes128-sha256", "3C", CURL_GSKPROTO_TLSV12_MASK }, - { "aes256-sha256", "3D", CURL_GSKPROTO_TLSV12_MASK }, - { "aes128-gcm-sha256", - "9C", CURL_GSKPROTO_TLSV12_MASK }, - { "aes256-gcm-sha384", - "9D", CURL_GSKPROTO_TLSV12_MASK }, - { "rc4-md5", "1", CURL_GSKPROTO_SSLV2_MASK }, - { "exp-rc4-md5", "2", CURL_GSKPROTO_SSLV2_MASK }, - { "rc2-md5", "3", CURL_GSKPROTO_SSLV2_MASK }, - { "exp-rc2-md5", "4", CURL_GSKPROTO_SSLV2_MASK }, - { "des-cbc-md5", "6", CURL_GSKPROTO_SSLV2_MASK }, - { "des-cbc3-md5", "7", CURL_GSKPROTO_SSLV2_MASK }, - { (const char *) NULL, (const char *) NULL, 0 } -}; - - -static bool is_separator(char c) -{ - /* Return whether character is a cipher list separator. */ - switch(c) { - case ' ': - case '\t': - case ':': - case ',': - case ';': - return true; - } - return false; -} - - -static CURLcode gskit_status(struct Curl_easy *data, int rc, - const char *procname, CURLcode defcode) -{ - char buffer[STRERROR_LEN]; - /* Process GSKit status and map it to a CURLcode. */ - switch(rc) { - case GSK_OK: - case GSK_OS400_ASYNCHRONOUS_SOC_INIT: - return CURLE_OK; - case GSK_KEYRING_OPEN_ERROR: - case GSK_OS400_ERROR_NO_ACCESS: - return CURLE_SSL_CACERT_BADFILE; - case GSK_INSUFFICIENT_STORAGE: - return CURLE_OUT_OF_MEMORY; - case GSK_ERROR_BAD_V2_CIPHER: - case GSK_ERROR_BAD_V3_CIPHER: - case GSK_ERROR_NO_CIPHERS: - return CURLE_SSL_CIPHER; - case GSK_OS400_ERROR_NOT_TRUSTED_ROOT: - case GSK_ERROR_CERT_VALIDATION: - return CURLE_PEER_FAILED_VERIFICATION; - case GSK_OS400_ERROR_TIMED_OUT: - return CURLE_OPERATION_TIMEDOUT; - case GSK_WOULD_BLOCK: - return CURLE_AGAIN; - case GSK_OS400_ERROR_NOT_REGISTERED: - break; - case GSK_ERROR_IO: - switch(errno) { - case ENOMEM: - return CURLE_OUT_OF_MEMORY; - default: - failf(data, "%s I/O error: %s", procname, - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - } - break; - default: - failf(data, "%s: %s", procname, gsk_strerror(rc)); - break; - } - return defcode; -} - - -static CURLcode set_enum(struct Curl_easy *data, gsk_handle h, - GSK_ENUM_ID id, GSK_ENUM_VALUE value, bool unsupported_ok) -{ - char buffer[STRERROR_LEN]; - int rc = gsk_attribute_set_enum(h, id, value); - - switch(rc) { - case GSK_OK: - return CURLE_OK; - case GSK_ERROR_IO: - failf(data, "gsk_attribute_set_enum() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - case GSK_ATTRIBUTE_INVALID_ID: - if(unsupported_ok) - return CURLE_UNSUPPORTED_PROTOCOL; - default: - failf(data, "gsk_attribute_set_enum(): %s", gsk_strerror(rc)); - break; - } - return CURLE_SSL_CONNECT_ERROR; -} - - -static CURLcode set_buffer(struct Curl_easy *data, gsk_handle h, - GSK_BUF_ID id, const char *buf, bool unsupported_ok) -{ - char buffer[STRERROR_LEN]; - int rc = gsk_attribute_set_buffer(h, id, buf, 0); - - switch(rc) { - case GSK_OK: - return CURLE_OK; - case GSK_ERROR_IO: - failf(data, "gsk_attribute_set_buffer() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - case GSK_ATTRIBUTE_INVALID_ID: - if(unsupported_ok) - return CURLE_UNSUPPORTED_PROTOCOL; - default: - failf(data, "gsk_attribute_set_buffer(): %s", gsk_strerror(rc)); - break; - } - return CURLE_SSL_CONNECT_ERROR; -} - - -static CURLcode set_numeric(struct Curl_easy *data, - gsk_handle h, GSK_NUM_ID id, int value) -{ - char buffer[STRERROR_LEN]; - int rc = gsk_attribute_set_numeric_value(h, id, value); - - switch(rc) { - case GSK_OK: - return CURLE_OK; - case GSK_ERROR_IO: - failf(data, "gsk_attribute_set_numeric_value() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - break; - default: - failf(data, "gsk_attribute_set_numeric_value(): %s", gsk_strerror(rc)); - break; - } - return CURLE_SSL_CONNECT_ERROR; -} - - -static CURLcode set_ciphers(struct Curl_easy *data, - gsk_handle h, unsigned int *protoflags) -{ - struct connectdata *conn = data->conn; - const char *cipherlist = SSL_CONN_CONFIG(cipher_list); - const char *clp; - const struct gskit_cipher *ctp; - int i; - int l; - bool unsupported; - CURLcode result; - struct { - char *buf; - char *ptr; - } ciphers[CURL_GSKPROTO_LAST]; - - /* Compile cipher list into GSKit-compatible cipher lists. */ - - if(!cipherlist) - return CURLE_OK; - while(is_separator(*cipherlist)) /* Skip initial separators. */ - cipherlist++; - if(!*cipherlist) - return CURLE_OK; - - /* We allocate GSKit buffers of the same size as the input string: since - GSKit tokens are always shorter than their cipher names, allocated buffers - will always be large enough to accommodate the result. */ - l = strlen(cipherlist) + 1; - memset((char *) ciphers, 0, sizeof(ciphers)); - for(i = 0; i < CURL_GSKPROTO_LAST; i++) { - ciphers[i].buf = malloc(l); - if(!ciphers[i].buf) { - while(i--) - free(ciphers[i].buf); - return CURLE_OUT_OF_MEMORY; - } - ciphers[i].ptr = ciphers[i].buf; - *ciphers[i].ptr = '\0'; - } - - /* Process each cipher in input string. */ - unsupported = FALSE; - result = CURLE_OK; - for(;;) { - for(clp = cipherlist; *cipherlist && !is_separator(*cipherlist);) - cipherlist++; - l = cipherlist - clp; - if(!l) - break; - /* Search the cipher in our table. */ - for(ctp = ciphertable; ctp->name; ctp++) - if(strncasecompare(ctp->name, clp, l) && !ctp->name[l]) - break; - if(!ctp->name) { - failf(data, "Unknown cipher %.*s", l, clp); - result = CURLE_SSL_CIPHER; - } - else { - unsupported |= !(ctp->versions & (CURL_GSKPROTO_SSLV2_MASK | - CURL_GSKPROTO_SSLV3_MASK | CURL_GSKPROTO_TLSV10_MASK)); - for(i = 0; i < CURL_GSKPROTO_LAST; i++) { - if(ctp->versions & (1 << i)) { - strcpy(ciphers[i].ptr, ctp->gsktoken); - ciphers[i].ptr += strlen(ctp->gsktoken); - } - } - } - - /* Advance to next cipher name or end of string. */ - while(is_separator(*cipherlist)) - cipherlist++; - } - - /* Disable protocols with empty cipher lists. */ - for(i = 0; i < CURL_GSKPROTO_LAST; i++) { - if(!(*protoflags & (1 << i)) || !ciphers[i].buf[0]) { - *protoflags &= ~(1 << i); - ciphers[i].buf[0] = '\0'; - } - } - - /* Try to set-up TLSv1.1 and TLSv2.1 ciphers. */ - if(*protoflags & CURL_GSKPROTO_TLSV11_MASK) { - result = set_buffer(data, h, GSK_TLSV11_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_TLSV11].buf, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(unsupported) { - failf(data, "TLSv1.1-only ciphers are not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - if(!result && (*protoflags & CURL_GSKPROTO_TLSV12_MASK)) { - result = set_buffer(data, h, GSK_TLSV12_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_TLSV12].buf, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(unsupported) { - failf(data, "TLSv1.2-only ciphers are not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - - /* Try to set-up TLSv1.0 ciphers. If not successful, concatenate them to - the SSLv3 ciphers. OS/400 prior to version 7.1 will understand it. */ - if(!result && (*protoflags & CURL_GSKPROTO_TLSV10_MASK)) { - result = set_buffer(data, h, GSK_TLSV10_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_TLSV10].buf, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - strcpy(ciphers[CURL_GSKPROTO_SSLV3].ptr, - ciphers[CURL_GSKPROTO_TLSV10].ptr); - } - } - - /* Set-up other ciphers. */ - if(!result && (*protoflags & CURL_GSKPROTO_SSLV3_MASK)) - result = set_buffer(data, h, GSK_V3_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_SSLV3].buf, FALSE); - if(!result && (*protoflags & CURL_GSKPROTO_SSLV2_MASK)) - result = set_buffer(data, h, GSK_V2_CIPHER_SPECS, - ciphers[CURL_GSKPROTO_SSLV2].buf, FALSE); - - /* Clean-up. */ - for(i = 0; i < CURL_GSKPROTO_LAST; i++) - free(ciphers[i].buf); - - return result; -} - - -static int gskit_init(void) -{ - /* No initialization needed. */ - return 1; -} - - -static void gskit_cleanup(void) -{ - /* Nothing to do. */ -} - - -static CURLcode init_environment(struct Curl_easy *data, - gsk_handle *envir, const char *appid, - const char *file, const char *label, - const char *password) -{ - int rc; - CURLcode result; - gsk_handle h; - - /* Creates the GSKit environment. */ - - rc = gsk_environment_open(&h); - switch(rc) { - case GSK_OK: - break; - case GSK_INSUFFICIENT_STORAGE: - return CURLE_OUT_OF_MEMORY; - default: - failf(data, "gsk_environment_open(): %s", gsk_strerror(rc)); - return CURLE_SSL_CONNECT_ERROR; - } - - result = set_enum(data, h, GSK_SESSION_TYPE, GSK_CLIENT_SESSION, FALSE); - if(!result && appid) - result = set_buffer(data, h, GSK_OS400_APPLICATION_ID, appid, FALSE); - if(!result && file) - result = set_buffer(data, h, GSK_KEYRING_FILE, file, FALSE); - if(!result && label) - result = set_buffer(data, h, GSK_KEYRING_LABEL, label, FALSE); - if(!result && password) - result = set_buffer(data, h, GSK_KEYRING_PW, password, FALSE); - - if(!result) { - /* Locate CAs, Client certificate and key according to our settings. - Note: this call may be blocking for some tenths of seconds. */ - result = gskit_status(data, gsk_environment_init(h), - "gsk_environment_init()", CURLE_SSL_CERTPROBLEM); - if(!result) { - *envir = h; - return result; - } - } - /* Error: rollback. */ - gsk_environment_close(&h); - return result; -} - - -static void cancel_async_handshake(struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - Qso_OverlappedIO_t cstat; - - DEBUGASSERT(BACKEND); - - if(QsoCancelOperation(conn->sock[sockindex], 0) > 0) - QsoWaitForIOCompletion(BACKEND->iocport, &cstat, (struct timeval *) NULL); -} - - -static void close_async_handshake(struct ssl_connect_data *connssl) -{ - DEBUGASSERT(BACKEND); - QsoDestroyIOCompletionPort(BACKEND->iocport); - BACKEND->iocport = -1; -} - -static int pipe_ssloverssl(struct connectdata *conn, int sockindex, - int directions) -{ -#ifndef CURL_DISABLE_PROXY - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_connect_data *connproxyssl = &conn->proxy_ssl[sockindex]; - struct pollfd fds[2]; - int n; - int m; - int i; - int ret = 0; - char buf[CURL_MAX_WRITE_SIZE]; - - DEBUGASSERT(BACKEND); - DEBUGASSERT(connproxyssl->backend); - - if(!connssl->use || !connproxyssl->use) - return 0; /* No SSL over SSL: OK. */ - - n = 1; - fds[0].fd = BACKEND->remotefd; - fds[1].fd = conn->sock[sockindex]; - - if(directions & SOS_READ) { - fds[0].events |= POLLOUT; - } - if(directions & SOS_WRITE) { - n = 2; - fds[0].events |= POLLIN; - fds[1].events |= POLLOUT; - } - i = Curl_poll(fds, n, 0); - if(i < 0) - return -1; /* Select error. */ - - if(fds[0].revents & POLLOUT) { - /* Try getting data from HTTPS proxy and pipe it upstream. */ - n = 0; - i = gsk_secure_soc_read(connproxyssl->backend->handle, - buf, sizeof(buf), &n); - switch(i) { - case GSK_OK: - if(n) { - i = write(BACKEND->remotefd, buf, n); - if(i < 0) - return -1; - ret = 1; - } - break; - case GSK_OS400_ERROR_TIMED_OUT: - case GSK_WOULD_BLOCK: - break; - default: - return -1; - } - } - - if((fds[0].revents & POLLIN) && (fds[1].revents & POLLOUT)) { - /* Pipe data to HTTPS proxy. */ - n = read(BACKEND->remotefd, buf, sizeof(buf)); - if(n < 0) - return -1; - if(n) { - i = gsk_secure_soc_write(connproxyssl->backend->handle, buf, n, &m); - if(i != GSK_OK || n != m) - return -1; - ret = 1; - } - } - - return ret; /* OK */ -#else - return 0; -#endif -} - - -static void close_one(struct ssl_connect_data *connssl, struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - DEBUGASSERT(BACKEND); - if(BACKEND->handle) { - gskit_status(data, gsk_secure_soc_close(&BACKEND->handle), - "gsk_secure_soc_close()", 0); - /* Last chance to drain output. */ - while(pipe_ssloverssl(conn, sockindex, SOS_WRITE) > 0) - ; - BACKEND->handle = (gsk_handle) NULL; -#ifndef CURL_DISABLE_PROXY - if(BACKEND->localfd >= 0) { - close(BACKEND->localfd); - BACKEND->localfd = -1; - } - if(BACKEND->remotefd >= 0) { - close(BACKEND->remotefd); - BACKEND->remotefd = -1; - } -#endif - } - if(BACKEND->iocport >= 0) - close_async_handshake(connssl); -} - - -static ssize_t gskit_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - CURLcode cc = CURLE_SEND_ERROR; - int written; - - DEBUGASSERT(BACKEND); - - if(pipe_ssloverssl(conn, sockindex, SOS_WRITE) >= 0) { - cc = gskit_status(data, - gsk_secure_soc_write(BACKEND->handle, - (char *) mem, (int) len, &written), - "gsk_secure_soc_write()", CURLE_SEND_ERROR); - if(cc == CURLE_OK) - if(pipe_ssloverssl(conn, sockindex, SOS_WRITE) < 0) - cc = CURLE_SEND_ERROR; - } - if(cc != CURLE_OK) { - *curlcode = cc; - written = -1; - } - return (ssize_t) written; /* number of bytes */ -} - - -static ssize_t gskit_recv(struct Curl_easy *data, int num, char *buf, - size_t buffersize, CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[num]; - int nread; - CURLcode cc = CURLE_RECV_ERROR; - - DEBUGASSERT(BACKEND); - - if(pipe_ssloverssl(conn, num, SOS_READ) >= 0) { - int buffsize = buffersize > (size_t) INT_MAX? INT_MAX: (int) buffersize; - cc = gskit_status(data, gsk_secure_soc_read(BACKEND->handle, - buf, buffsize, &nread), - "gsk_secure_soc_read()", CURLE_RECV_ERROR); - } - switch(cc) { - case CURLE_OK: - break; - case CURLE_OPERATION_TIMEDOUT: - cc = CURLE_AGAIN; - default: - *curlcode = cc; - nread = -1; - break; - } - return (ssize_t) nread; -} - -static CURLcode -set_ssl_version_min_max(unsigned int *protoflags, struct Curl_easy *data) -{ - struct connectdata *conn = data->conn; - long ssl_version = SSL_CONN_CONFIG(version); - long ssl_version_max = SSL_CONN_CONFIG(version_max); - long i = ssl_version; - switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - ssl_version_max = CURL_SSLVERSION_TLSv1_2; - break; - } - for(; i <= (ssl_version_max >> 16); ++i) { - switch(i) { - case CURL_SSLVERSION_TLSv1_0: - *protoflags |= CURL_GSKPROTO_TLSV10_MASK; - break; - case CURL_SSLVERSION_TLSv1_1: - *protoflags |= CURL_GSKPROTO_TLSV11_MASK; - break; - case CURL_SSLVERSION_TLSv1_2: - *protoflags |= CURL_GSKPROTO_TLSV11_MASK; - break; - case CURL_SSLVERSION_TLSv1_3: - failf(data, "GSKit: TLS 1.3 is not yet supported"); - return CURLE_SSL_CONNECT_ERROR; - } - } - - return CURLE_OK; -} - -static CURLcode gskit_connect_step1(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - gsk_handle envir; - CURLcode result; - const char * const keyringfile = SSL_CONN_CONFIG(CAfile); - const char * const keyringpwd = SSL_SET_OPTION(key_passwd); - const char * const keyringlabel = SSL_SET_OPTION(primary.clientcert); - const long int ssl_version = SSL_CONN_CONFIG(version); - const bool verifypeer = SSL_CONN_CONFIG(verifypeer); - const char * const hostname = SSL_HOST_NAME(); - const char *sni; - unsigned int protoflags = 0; - Qso_OverlappedIO_t commarea; -#ifndef CURL_DISABLE_PROXY - int sockpair[2]; - static const int sobufsize = CURL_MAX_WRITE_SIZE; -#endif - - /* Create SSL environment, start (preferably asynchronous) handshake. */ - DEBUGASSERT(BACKEND); - - BACKEND->handle = (gsk_handle) NULL; - BACKEND->iocport = -1; -#ifndef CURL_DISABLE_PROXY - BACKEND->localfd = -1; - BACKEND->remotefd = -1; -#endif - - /* GSKit supports two ways of specifying an SSL context: either by - * application identifier (that should have been defined at the system - * level) or by keyring file, password and certificate label. - * Local certificate name (CURLOPT_SSLCERT) is used to hold either the - * application identifier of the certificate label. - * Key password (CURLOPT_KEYPASSWD) holds the keyring password. - * It is not possible to have different keyrings for the CAs and the - * local certificate. We thus use the CA file (CURLOPT_CAINFO) to identify - * the keyring file. - * If no key password is given and the keyring is the system keyring, - * application identifier mode is tried first, as recommended in IBM doc. - */ - - envir = (gsk_handle) NULL; - - if(keyringlabel && *keyringlabel && !keyringpwd && - !strcmp(keyringfile, CURL_CA_BUNDLE)) { - /* Try application identifier mode. */ - init_environment(data, &envir, keyringlabel, (const char *) NULL, - (const char *) NULL, (const char *) NULL); - } - - if(!envir) { - /* Use keyring mode. */ - result = init_environment(data, &envir, (const char *) NULL, - keyringfile, keyringlabel, keyringpwd); - if(result) - return result; - } - - /* Create secure session. */ - result = gskit_status(data, gsk_secure_soc_open(envir, &BACKEND->handle), - "gsk_secure_soc_open()", CURLE_SSL_CONNECT_ERROR); - gsk_environment_close(&envir); - if(result) - return result; - -#ifndef CURL_DISABLE_PROXY - /* Establish a pipelining socket pair for SSL over SSL. */ - if(conn->proxy_ssl[sockindex].use) { - if(Curl_socketpair(0, 0, 0, sockpair)) - return CURLE_SSL_CONNECT_ERROR; - BACKEND->localfd = sockpair[0]; - BACKEND->remotefd = sockpair[1]; - setsockopt(BACKEND->localfd, SOL_SOCKET, SO_RCVBUF, - (void *) &sobufsize, sizeof(sobufsize)); - setsockopt(BACKEND->remotefd, SOL_SOCKET, SO_RCVBUF, - (void *) &sobufsize, sizeof(sobufsize)); - setsockopt(BACKEND->localfd, SOL_SOCKET, SO_SNDBUF, - (void *) &sobufsize, sizeof(sobufsize)); - setsockopt(BACKEND->remotefd, SOL_SOCKET, SO_SNDBUF, - (void *) &sobufsize, sizeof(sobufsize)); - curlx_nonblock(BACKEND->localfd, TRUE); - curlx_nonblock(BACKEND->remotefd, TRUE); - } -#endif - - /* Determine which SSL/TLS version should be enabled. */ - sni = hostname; - switch(ssl_version) { - case CURL_SSLVERSION_SSLv2: - protoflags = CURL_GSKPROTO_SSLV2_MASK; - sni = NULL; - break; - case CURL_SSLVERSION_SSLv3: - protoflags = CURL_GSKPROTO_SSLV3_MASK; - sni = NULL; - break; - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - protoflags = CURL_GSKPROTO_TLSV10_MASK | - CURL_GSKPROTO_TLSV11_MASK | CURL_GSKPROTO_TLSV12_MASK; - break; - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - result = set_ssl_version_min_max(&protoflags, data); - if(result != CURLE_OK) - return result; - break; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - - /* Process SNI. Ignore if not supported (on OS400 < V7R1). */ - if(sni) { - char *snihost = Curl_ssl_snihost(data, sni, NULL); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - result = set_buffer(data, BACKEND->handle, - GSK_SSL_EXTN_SERVERNAME_REQUEST, snihost, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) - result = CURLE_OK; - } - - /* Set session parameters. */ - if(!result) { - /* Compute the handshake timeout. Since GSKit granularity is 1 second, - we round up the required value. */ - timediff_t timeout = Curl_timeleft(data, NULL, TRUE); - if(timeout < 0) - result = CURLE_OPERATION_TIMEDOUT; - else - result = set_numeric(data, BACKEND->handle, GSK_HANDSHAKE_TIMEOUT, - (timeout + 999) / 1000); - } - if(!result) - result = set_numeric(data, BACKEND->handle, GSK_OS400_READ_TIMEOUT, 1); - if(!result) -#ifndef CURL_DISABLE_PROXY - result = set_numeric(data, BACKEND->handle, GSK_FD, BACKEND->localfd >= 0? - BACKEND->localfd: conn->sock[sockindex]); -#else - result = set_numeric(data, BACKEND->handle, GSK_FD, - conn->sock[sockindex]); -#endif - if(!result) - result = set_ciphers(data, BACKEND->handle, &protoflags); - if(!protoflags) { - failf(data, "No SSL protocol/cipher combination enabled"); - result = CURLE_SSL_CIPHER; - } - if(!result) - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_SSLV2, - (protoflags & CURL_GSKPROTO_SSLV2_MASK)? - GSK_PROTOCOL_SSLV2_ON: GSK_PROTOCOL_SSLV2_OFF, FALSE); - if(!result) - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_SSLV3, - (protoflags & CURL_GSKPROTO_SSLV3_MASK)? - GSK_PROTOCOL_SSLV3_ON: GSK_PROTOCOL_SSLV3_OFF, FALSE); - if(!result) - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV1, - (protoflags & CURL_GSKPROTO_TLSV10_MASK)? - GSK_PROTOCOL_TLSV1_ON: GSK_PROTOCOL_TLSV1_OFF, FALSE); - if(!result) { - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV11, - (protoflags & CURL_GSKPROTO_TLSV11_MASK)? - GSK_TRUE: GSK_FALSE, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(protoflags == CURL_GSKPROTO_TLSV11_MASK) { - failf(data, "TLS 1.1 not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - if(!result) { - result = set_enum(data, BACKEND->handle, GSK_PROTOCOL_TLSV12, - (protoflags & CURL_GSKPROTO_TLSV12_MASK)? - GSK_TRUE: GSK_FALSE, TRUE); - if(result == CURLE_UNSUPPORTED_PROTOCOL) { - result = CURLE_OK; - if(protoflags == CURL_GSKPROTO_TLSV12_MASK) { - failf(data, "TLS 1.2 not yet supported"); - result = CURLE_SSL_CIPHER; - } - } - } - if(!result) - result = set_enum(data, BACKEND->handle, GSK_SERVER_AUTH_TYPE, - verifypeer? GSK_SERVER_AUTH_FULL: - GSK_SERVER_AUTH_PASSTHRU, FALSE); - - if(!result) { - /* Start handshake. Try asynchronous first. */ - memset(&commarea, 0, sizeof(commarea)); - BACKEND->iocport = QsoCreateIOCompletionPort(); - if(BACKEND->iocport != -1) { - result = gskit_status(data, - gsk_secure_soc_startInit(BACKEND->handle, - BACKEND->iocport, - &commarea), - "gsk_secure_soc_startInit()", - CURLE_SSL_CONNECT_ERROR); - if(!result) { - connssl->connecting_state = ssl_connect_2; - return CURLE_OK; - } - else - close_async_handshake(connssl); - } - else if(errno != ENOBUFS) - result = gskit_status(data, GSK_ERROR_IO, - "QsoCreateIOCompletionPort()", 0); -#ifndef CURL_DISABLE_PROXY - else if(conn->proxy_ssl[sockindex].use) { - /* Cannot pipeline while handshaking synchronously. */ - result = CURLE_SSL_CONNECT_ERROR; - } -#endif - else { - /* No more completion port available. Use synchronous IO. */ - result = gskit_status(data, gsk_secure_soc_init(BACKEND->handle), - "gsk_secure_soc_init()", CURLE_SSL_CONNECT_ERROR); - if(!result) { - connssl->connecting_state = ssl_connect_3; - return CURLE_OK; - } - } - } - - /* Error: rollback. */ - close_one(connssl, data, conn, sockindex); - return result; -} - - -static CURLcode gskit_connect_step2(struct Curl_easy *data, - struct connectdata *conn, int sockindex, - bool nonblocking) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - Qso_OverlappedIO_t cstat; - struct timeval stmv; - CURLcode result; - - /* Poll or wait for end of SSL asynchronous handshake. */ - DEBUGASSERT(BACKEND); - - for(;;) { - timediff_t timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE); - stmv.tv_sec = 0; - stmv.tv_usec = 0; - if(timeout_ms < 0) - timeout_ms = 0; - switch(QsoWaitForIOCompletion(BACKEND->iocport, &cstat, - curlx_mstotv(&stmv, timeout_ms))) { - case 1: /* Operation complete. */ - break; - case -1: /* An error occurred: handshake still in progress. */ - if(errno == EINTR) { - if(nonblocking) - return CURLE_OK; - continue; /* Retry. */ - } - if(errno != ETIME) { - char buffer[STRERROR_LEN]; - failf(data, "QsoWaitForIOCompletion() I/O error: %s", - Curl_strerror(errno, buffer, sizeof(buffer))); - cancel_async_handshake(conn, sockindex); - close_async_handshake(connssl); - return CURLE_SSL_CONNECT_ERROR; - } - /* FALL INTO... */ - case 0: /* Handshake in progress, timeout occurred. */ - if(nonblocking) - return CURLE_OK; - cancel_async_handshake(conn, sockindex); - close_async_handshake(connssl); - return CURLE_OPERATION_TIMEDOUT; - } - break; - } - result = gskit_status(data, cstat.returnValue, "SSL handshake", - CURLE_SSL_CONNECT_ERROR); - if(!result) - connssl->connecting_state = ssl_connect_3; - close_async_handshake(connssl); - return result; -} - - -static CURLcode gskit_connect_step3(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - const gsk_cert_data_elem *cdev; - int cdec; - const gsk_cert_data_elem *p; - const char *cert = (const char *) NULL; - const char *certend; - const char *ptr; - CURLcode result; - - /* SSL handshake done: gather certificate info and verify host. */ - DEBUGASSERT(BACKEND); - - if(gskit_status(data, gsk_attribute_get_cert_info(BACKEND->handle, - GSK_PARTNER_CERT_INFO, - &cdev, &cdec), - "gsk_attribute_get_cert_info()", CURLE_SSL_CONNECT_ERROR) == - CURLE_OK) { - int i; - - infof(data, "Server certificate:"); - p = cdev; - for(i = 0; i++ < cdec; p++) - switch(p->cert_data_id) { - case CERT_BODY_DER: - cert = p->cert_data_p; - certend = cert + cdev->cert_data_l; - break; - case CERT_DN_PRINTABLE: - infof(data, "\t subject: %.*s", p->cert_data_l, p->cert_data_p); - break; - case CERT_ISSUER_DN_PRINTABLE: - infof(data, "\t issuer: %.*s", p->cert_data_l, p->cert_data_p); - break; - case CERT_VALID_FROM: - infof(data, "\t start date: %.*s", p->cert_data_l, p->cert_data_p); - break; - case CERT_VALID_TO: - infof(data, "\t expire date: %.*s", p->cert_data_l, p->cert_data_p); - break; - } - } - - /* Verify host. */ - result = Curl_verifyhost(data, conn, cert, certend); - if(result) - return result; - - /* The only place GSKit can get the whole CA chain is a validation - callback where no user data pointer is available. Therefore it's not - possible to copy this chain into our structures for CAINFO. - However the server certificate may be available, thus we can return - info about it. */ - if(data->set.ssl.certinfo) { - result = Curl_ssl_init_certinfo(data, 1); - if(result) - return result; - - if(cert) { - result = Curl_extract_certinfo(data, 0, cert, certend); - if(result) - return result; - } - } - - /* Check pinned public key. */ - ptr = SSL_PINNED_PUB_KEY(); - if(!result && ptr) { - struct Curl_X509certificate x509; - struct Curl_asn1Element *p; - - memset(&x509, 0, sizeof(x509)); - if(Curl_parseX509(&x509, cert, certend)) - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; - p = &x509.subjectPublicKeyInfo; - result = Curl_pin_peer_pubkey(data, ptr, p->header, p->end - p->header); - if(result) { - failf(data, "SSL: public key does not match pinned public key"); - return result; - } - } - - connssl->connecting_state = ssl_connect_done; - return CURLE_OK; -} - - -static CURLcode gskit_connect_common(struct Curl_easy *data, - struct connectdata *conn, int sockindex, - bool nonblocking, bool *done) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - timediff_t timeout_ms; - CURLcode result = CURLE_OK; - - *done = connssl->state == ssl_connection_complete; - if(*done) - return CURLE_OK; - - /* Step 1: create session, start handshake. */ - if(connssl->connecting_state == ssl_connect_1) { - /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - result = CURLE_OPERATION_TIMEDOUT; - } - else - result = gskit_connect_step1(data, conn, sockindex); - } - - /* Handle handshake pipelining. */ - if(!result) - if(pipe_ssloverssl(conn, sockindex, SOS_READ | SOS_WRITE) < 0) - result = CURLE_SSL_CONNECT_ERROR; - - /* Step 2: check if handshake is over. */ - if(!result && connssl->connecting_state == ssl_connect_2) { - /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - result = CURLE_OPERATION_TIMEDOUT; - } - else - result = gskit_connect_step2(data, conn, sockindex, nonblocking); - } - - /* Handle handshake pipelining. */ - if(!result) - if(pipe_ssloverssl(conn, sockindex, SOS_READ | SOS_WRITE) < 0) - result = CURLE_SSL_CONNECT_ERROR; - - /* Step 3: gather certificate info, verify host. */ - if(!result && connssl->connecting_state == ssl_connect_3) - result = gskit_connect_step3(data, conn, sockindex); - - if(result) - close_one(connssl, data, conn, sockindex); - else if(connssl->connecting_state == ssl_connect_done) { - connssl->state = ssl_connection_complete; - connssl->connecting_state = ssl_connect_1; - conn->recv[sockindex] = gskit_recv; - conn->send[sockindex] = gskit_send; - *done = TRUE; - } - - return result; -} - - -static CURLcode gskit_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, bool *done) -{ - CURLcode result; - - result = gskit_connect_common(data, conn, sockindex, TRUE, done); - if(*done || result) - conn->ssl[sockindex].connecting_state = ssl_connect_1; - return result; -} - - -static CURLcode gskit_connect(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - CURLcode result; - bool done; - - conn->ssl[sockindex].connecting_state = ssl_connect_1; - result = gskit_connect_common(data, conn, sockindex, FALSE, &done); - if(result) - return result; - - DEBUGASSERT(done); - - return CURLE_OK; -} - - -static void gskit_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - close_one(&conn->ssl[sockindex], data, conn, sockindex); -#ifndef CURL_DISABLE_PROXY - close_one(&conn->proxy_ssl[sockindex], data, conn, sockindex); -#endif -} - - -static int gskit_shutdown(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - int what; - int rc; - char buf[120]; - int loop = 10; /* don't get stuck */ - - DEBUGASSERT(BACKEND); - - if(!BACKEND->handle) - return 0; - -#ifndef CURL_DISABLE_FTP - if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE) - return 0; -#endif - - close_one(connssl, data, conn, sockindex); - rc = 0; - what = SOCKET_READABLE(conn->sock[sockindex], - SSL_SHUTDOWN_TIMEOUT); - - while(loop--) { - ssize_t nread; - - if(what < 0) { - /* anything that gets here is fatally bad */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - rc = -1; - break; - } - - if(!what) { /* timeout */ - failf(data, "SSL shutdown timeout"); - break; - } - - /* Something to read, let's do it and hope that it is the close - notify alert from the server. No way to gsk_secure_soc_read() now, so - use read(). */ - - nread = read(conn->sock[sockindex], buf, sizeof(buf)); - - if(nread < 0) { - char buffer[STRERROR_LEN]; - failf(data, "read: %s", Curl_strerror(errno, buffer, sizeof(buffer))); - rc = -1; - } - - if(nread <= 0) - break; - - what = SOCKET_READABLE(conn->sock[sockindex], 0); - } - - return rc; -} - - -static size_t gskit_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "GSKit"); -} - - -static int gskit_check_cxn(struct connectdata *cxn) -{ - struct ssl_connect_data *connssl = &cxn->ssl[FIRSTSOCKET]; - int err; - int errlen; - - /* The only thing that can be tested here is at the socket level. */ - DEBUGASSERT(BACKEND); - - if(!BACKEND->handle) - return 0; /* connection has been closed */ - - err = 0; - errlen = sizeof(err); - - if(getsockopt(cxn->sock[FIRSTSOCKET], SOL_SOCKET, SO_ERROR, - (unsigned char *) &err, &errlen) || - errlen != sizeof(err) || err) - return 0; /* connection has been closed */ - - return -1; /* connection status unknown */ -} - -static void *gskit_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - (void)info; - DEBUGASSERT(BACKEND); - return BACKEND->handle; -} - -const struct Curl_ssl Curl_ssl_gskit = { - { CURLSSLBACKEND_GSKIT, "gskit" }, /* info */ - - SSLSUPP_CERTINFO | - SSLSUPP_PINNEDPUBKEY, - - sizeof(struct ssl_backend_data), - - gskit_init, /* init */ - gskit_cleanup, /* cleanup */ - gskit_version, /* version */ - gskit_check_cxn, /* check_cxn */ - gskit_shutdown, /* shutdown */ - Curl_none_data_pending, /* data_pending */ - Curl_none_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - gskit_connect, /* connect */ - gskit_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - gskit_get_internals, /* get_internals */ - gskit_close, /* close_one */ - Curl_none_close_all, /* close_all */ - /* No session handling for GSKit */ - Curl_none_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - NULL, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#endif /* USE_GSKIT */ diff --git a/contrib/libs/curl/lib/vtls/gtls.c b/contrib/libs/curl/lib/vtls/gtls.c deleted file mode 100644 index dd82755852..0000000000 --- a/contrib/libs/curl/lib/vtls/gtls.c +++ /dev/null @@ -1,1702 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -/* - * Source file for all GnuTLS-specific code for the TLS/SSL layer. No code - * but vtls.c should ever call or use these functions. - * - * Note: don't use the GnuTLS' *_t variable type names in this source code, - * since they were not present in 1.0.X. - */ - -#include "curl_setup.h" - -#ifdef USE_GNUTLS - -#include <gnutls/abstract.h> -#include <gnutls/gnutls.h> -#include <gnutls/x509.h> -#include <gnutls/crypto.h> -#include <nettle/sha2.h> - -#include "urldata.h" -#include "sendf.h" -#include "inet_pton.h" -#include "gtls.h" -#include "vtls.h" -#include "parsedate.h" -#include "connect.h" /* for the connect timeout */ -#include "select.h" -#include "strcase.h" -#include "warnless.h" -#include "x509asn1.h" -#include "multiif.h" -#include "curl_printf.h" -#include "curl_memory.h" -/* The last #include file should be: */ -#include "memdebug.h" - -#ifdef HAVE_GNUTLS_SRP -/* the function exists */ -#ifdef USE_TLS_SRP -/* the functionality is not disabled */ -#define USE_GNUTLS_SRP -#endif -#endif - -/* Enable GnuTLS debugging by defining GTLSDEBUG */ -/*#define GTLSDEBUG */ - -#ifdef GTLSDEBUG -static void tls_log_func(int level, const char *str) -{ - fprintf(stderr, "|<%d>| %s", level, str); -} -#endif -static bool gtls_inited = FALSE; - -#if !defined(GNUTLS_VERSION_NUMBER) || (GNUTLS_VERSION_NUMBER < 0x03010a) -#error "too old GnuTLS version" -#endif - -# include <gnutls/ocsp.h> - -struct ssl_backend_data { - gnutls_session_t session; - gnutls_certificate_credentials_t cred; -#ifdef USE_GNUTLS_SRP - gnutls_srp_client_credentials_t srp_client_cred; -#endif -}; - -static ssize_t gtls_push(void *s, const void *buf, size_t len) -{ - curl_socket_t sock = *(curl_socket_t *)s; - ssize_t ret = swrite(sock, buf, len); - return ret; -} - -static ssize_t gtls_pull(void *s, void *buf, size_t len) -{ - curl_socket_t sock = *(curl_socket_t *)s; - ssize_t ret = sread(sock, buf, len); - return ret; -} - -static ssize_t gtls_push_ssl(void *s, const void *buf, size_t len) -{ - return gnutls_record_send((gnutls_session_t) s, buf, len); -} - -static ssize_t gtls_pull_ssl(void *s, void *buf, size_t len) -{ - return gnutls_record_recv((gnutls_session_t) s, buf, len); -} - -/* gtls_init() - * - * Global GnuTLS init, called from Curl_ssl_init(). This calls functions that - * are not thread-safe and thus this function itself is not thread-safe and - * must only be called from within curl_global_init() to keep the thread - * situation under control! - */ -static int gtls_init(void) -{ - int ret = 1; - if(!gtls_inited) { - ret = gnutls_global_init()?0:1; -#ifdef GTLSDEBUG - gnutls_global_set_log_function(tls_log_func); - gnutls_global_set_log_level(2); -#endif - gtls_inited = TRUE; - } - return ret; -} - -static void gtls_cleanup(void) -{ - if(gtls_inited) { - gnutls_global_deinit(); - gtls_inited = FALSE; - } -} - -#ifndef CURL_DISABLE_VERBOSE_STRINGS -static void showtime(struct Curl_easy *data, - const char *text, - time_t stamp) -{ - struct tm buffer; - const struct tm *tm = &buffer; - char str[96]; - CURLcode result = Curl_gmtime(stamp, &buffer); - if(result) - return; - - msnprintf(str, - sizeof(str), - " %s: %s, %02d %s %4d %02d:%02d:%02d GMT", - text, - Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], - tm->tm_mday, - Curl_month[tm->tm_mon], - tm->tm_year + 1900, - tm->tm_hour, - tm->tm_min, - tm->tm_sec); - infof(data, "%s", str); -} -#endif - -static gnutls_datum_t load_file(const char *file) -{ - FILE *f; - gnutls_datum_t loaded_file = { NULL, 0 }; - long filelen; - void *ptr; - - f = fopen(file, "rb"); - if(!f) - return loaded_file; - if(fseek(f, 0, SEEK_END) != 0 - || (filelen = ftell(f)) < 0 - || fseek(f, 0, SEEK_SET) != 0 - || !(ptr = malloc((size_t)filelen))) - goto out; - if(fread(ptr, 1, (size_t)filelen, f) < (size_t)filelen) { - free(ptr); - goto out; - } - - loaded_file.data = ptr; - loaded_file.size = (unsigned int)filelen; -out: - fclose(f); - return loaded_file; -} - -static void unload_file(gnutls_datum_t data) -{ - free(data.data); -} - - -/* this function does a SSL/TLS (re-)handshake */ -static CURLcode handshake(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool duringconnect, - bool nonblocking) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - gnutls_session_t session; - curl_socket_t sockfd = conn->sock[sockindex]; - - DEBUGASSERT(backend); - session = backend->session; - - for(;;) { - timediff_t timeout_ms; - int rc; - - /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, duringconnect); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading - || connssl->connecting_state == ssl_connect_2_writing) { - int what; - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking?0: - timeout_ms?timeout_ms:1000); - if(what < 0) { - /* fatal error */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - return CURLE_SSL_CONNECT_ERROR; - } - else if(0 == what) { - if(nonblocking) - return CURLE_OK; - else if(timeout_ms) { - /* timeout */ - failf(data, "SSL connection timeout at %ld", (long)timeout_ms); - return CURLE_OPERATION_TIMEDOUT; - } - } - /* socket is readable or writable */ - } - - rc = gnutls_handshake(session); - - if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { - connssl->connecting_state = - gnutls_record_get_direction(session)? - ssl_connect_2_writing:ssl_connect_2_reading; - continue; - } - else if((rc < 0) && !gnutls_error_is_fatal(rc)) { - const char *strerr = NULL; - - if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) { - int alert = gnutls_alert_get(session); - strerr = gnutls_alert_get_name(alert); - } - - if(!strerr) - strerr = gnutls_strerror(rc); - - infof(data, "gnutls_handshake() warning: %s", strerr); - continue; - } - else if(rc < 0) { - const char *strerr = NULL; - - if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) { - int alert = gnutls_alert_get(session); - strerr = gnutls_alert_get_name(alert); - } - - if(!strerr) - strerr = gnutls_strerror(rc); - - failf(data, "gnutls_handshake() failed: %s", strerr); - return CURLE_SSL_CONNECT_ERROR; - } - - /* Reset our connect state machine */ - connssl->connecting_state = ssl_connect_1; - return CURLE_OK; - } -} - -static gnutls_x509_crt_fmt_t do_file_type(const char *type) -{ - if(!type || !type[0]) - return GNUTLS_X509_FMT_PEM; - if(strcasecompare(type, "PEM")) - return GNUTLS_X509_FMT_PEM; - if(strcasecompare(type, "DER")) - return GNUTLS_X509_FMT_DER; - return GNUTLS_X509_FMT_PEM; /* default to PEM */ -} - -#define GNUTLS_CIPHERS "NORMAL:-ARCFOUR-128:-CTYPE-ALL:+CTYPE-X509" -/* If GnuTLS was compiled without support for SRP it will error out if SRP is - requested in the priority string, so treat it specially - */ -#define GNUTLS_SRP "+SRP" - -static CURLcode -set_ssl_version_min_max(struct Curl_easy *data, - const char **prioritylist, - const char *tls13support) -{ - struct connectdata *conn = data->conn; - long ssl_version = SSL_CONN_CONFIG(version); - long ssl_version_max = SSL_CONN_CONFIG(version_max); - - if((ssl_version == CURL_SSLVERSION_DEFAULT) || - (ssl_version == CURL_SSLVERSION_TLSv1)) - ssl_version = CURL_SSLVERSION_TLSv1_0; - if(ssl_version_max == CURL_SSLVERSION_MAX_NONE) - ssl_version_max = CURL_SSLVERSION_MAX_DEFAULT; - if(!tls13support) { - /* If the running GnuTLS doesn't support TLS 1.3, we must not specify a - prioritylist involving that since it will make GnuTLS return an en - error back at us */ - if((ssl_version_max == CURL_SSLVERSION_MAX_TLSv1_3) || - (ssl_version_max == CURL_SSLVERSION_MAX_DEFAULT)) { - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; - } - } - else if(ssl_version_max == CURL_SSLVERSION_MAX_DEFAULT) { - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_3; - } - - switch(ssl_version | ssl_version_max) { - case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_0: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.0"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_1: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.1:+VERS-TLS1.0"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_2: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.2:+VERS-TLS1.1:+VERS-TLS1.0"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_1: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.1"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_2: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.2:+VERS-TLS1.1"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_2: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.2"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_3 | CURL_SSLVERSION_MAX_TLSv1_3: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.3"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_3: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_TLSv1_3: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.3:+VERS-TLS1.2:+VERS-TLS1.1"; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_TLSv1_3: - *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:" - "+VERS-TLS1.3:+VERS-TLS1.2"; - return CURLE_OK; - } - - failf(data, "GnuTLS: cannot set ssl protocol"); - return CURLE_SSL_CONNECT_ERROR; -} - -static CURLcode -gtls_connect_step1(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - unsigned int init_flags; - gnutls_session_t session; - int rc; - bool sni = TRUE; /* default is SNI enabled */ - void *transport_ptr = NULL; - gnutls_push_func gnutls_transport_push = NULL; - gnutls_pull_func gnutls_transport_pull = NULL; -#ifdef ENABLE_IPV6 - struct in6_addr addr; -#else - struct in_addr addr; -#endif - const char *prioritylist; - const char *err = NULL; - const char * const hostname = SSL_HOST_NAME(); - long * const certverifyresult = &SSL_SET_OPTION_LVALUE(certverifyresult); - const char *tls13support; - CURLcode result; - - DEBUGASSERT(backend); - - if(connssl->state == ssl_connection_complete) - /* to make us tolerant against being called more than once for the - same connection */ - return CURLE_OK; - - if(!gtls_inited) - gtls_init(); - - /* Initialize certverifyresult to OK */ - *certverifyresult = 0; - - if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2) { - failf(data, "GnuTLS does not support SSLv2"); - return CURLE_SSL_CONNECT_ERROR; - } - else if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv3) - sni = FALSE; /* SSLv3 has no SNI */ - - /* allocate a cred struct */ - rc = gnutls_certificate_allocate_credentials(&backend->cred); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc)); - return CURLE_SSL_CONNECT_ERROR; - } - -#ifdef USE_GNUTLS_SRP - if((SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP) && - Curl_allow_auth_to_host(data)) { - infof(data, "Using TLS-SRP username: %s", - SSL_SET_OPTION(primary.username)); - - rc = gnutls_srp_allocate_client_credentials(&backend->srp_client_cred); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_srp_allocate_client_cred() failed: %s", - gnutls_strerror(rc)); - return CURLE_OUT_OF_MEMORY; - } - - rc = gnutls_srp_set_client_credentials(backend->srp_client_cred, - SSL_SET_OPTION(primary.username), - SSL_SET_OPTION(primary.password)); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_srp_set_client_cred() failed: %s", - gnutls_strerror(rc)); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - } -#endif - - if(SSL_CONN_CONFIG(CAfile)) { - /* set the trusted CA cert bundle file */ - gnutls_certificate_set_verify_flags(backend->cred, - GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT); - - rc = gnutls_certificate_set_x509_trust_file(backend->cred, - SSL_CONN_CONFIG(CAfile), - GNUTLS_X509_FMT_PEM); - if(rc < 0) { - infof(data, "error reading ca cert file %s (%s)", - SSL_CONN_CONFIG(CAfile), gnutls_strerror(rc)); - if(SSL_CONN_CONFIG(verifypeer)) { - *certverifyresult = rc; - return CURLE_SSL_CACERT_BADFILE; - } - } - else - infof(data, "found %d certificates in %s", rc, - SSL_CONN_CONFIG(CAfile)); - } - - if(SSL_CONN_CONFIG(CApath)) { - /* set the trusted CA cert directory */ - rc = gnutls_certificate_set_x509_trust_dir(backend->cred, - SSL_CONN_CONFIG(CApath), - GNUTLS_X509_FMT_PEM); - if(rc < 0) { - infof(data, "error reading ca cert file %s (%s)", - SSL_CONN_CONFIG(CApath), gnutls_strerror(rc)); - if(SSL_CONN_CONFIG(verifypeer)) { - *certverifyresult = rc; - return CURLE_SSL_CACERT_BADFILE; - } - } - else - infof(data, "found %d certificates in %s", - rc, SSL_CONN_CONFIG(CApath)); - } - -#ifdef CURL_CA_FALLBACK - /* use system ca certificate store as fallback */ - if(SSL_CONN_CONFIG(verifypeer) && - !(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(CApath))) { - /* this ignores errors on purpose */ - gnutls_certificate_set_x509_system_trust(backend->cred); - } -#endif - - if(SSL_SET_OPTION(primary.CRLfile)) { - /* set the CRL list file */ - rc = gnutls_certificate_set_x509_crl_file(backend->cred, - SSL_SET_OPTION(primary.CRLfile), - GNUTLS_X509_FMT_PEM); - if(rc < 0) { - failf(data, "error reading crl file %s (%s)", - SSL_SET_OPTION(primary.CRLfile), gnutls_strerror(rc)); - return CURLE_SSL_CRL_BADFILE; - } - else - infof(data, "found %d CRL in %s", - rc, SSL_SET_OPTION(primary.CRLfile)); - } - - /* Initialize TLS session as a client */ - init_flags = GNUTLS_CLIENT; - -#if defined(GNUTLS_FORCE_CLIENT_CERT) - init_flags |= GNUTLS_FORCE_CLIENT_CERT; -#endif - -#if defined(GNUTLS_NO_TICKETS) - /* Disable TLS session tickets */ - init_flags |= GNUTLS_NO_TICKETS; -#endif - - rc = gnutls_init(&backend->session, init_flags); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_init() failed: %d", rc); - return CURLE_SSL_CONNECT_ERROR; - } - - /* convenient assign */ - session = backend->session; - - if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) && -#ifdef ENABLE_IPV6 - (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) && -#endif - sni) { - size_t snilen; - char *snihost = Curl_ssl_snihost(data, hostname, &snilen); - if(!snihost || gnutls_server_name_set(session, GNUTLS_NAME_DNS, snihost, - snilen) < 0) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - } - - /* Use default priorities */ - rc = gnutls_set_default_priority(session); - if(rc != GNUTLS_E_SUCCESS) - return CURLE_SSL_CONNECT_ERROR; - - /* "In GnuTLS 3.6.5, TLS 1.3 is enabled by default" */ - tls13support = gnutls_check_version("3.6.5"); - - /* Ensure +SRP comes at the *end* of all relevant strings so that it can be - * removed if a run-time error indicates that SRP is not supported by this - * GnuTLS version */ - - if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2 || - SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv3) { - failf(data, "GnuTLS does not support SSLv2 or SSLv3"); - return CURLE_SSL_CONNECT_ERROR; - } - - if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_TLSv1_3) { - if(!tls13support) { - failf(data, "This GnuTLS installation does not support TLS 1.3"); - return CURLE_SSL_CONNECT_ERROR; - } - } - - /* At this point we know we have a supported TLS version, so set it */ - result = set_ssl_version_min_max(data, &prioritylist, tls13support); - if(result) - return result; - -#ifdef USE_GNUTLS_SRP - /* Only add SRP to the cipher list if SRP is requested. Otherwise - * GnuTLS will disable TLS 1.3 support. */ - if(SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP) { - size_t len = strlen(prioritylist); - - char *prioritysrp = malloc(len + sizeof(GNUTLS_SRP) + 1); - if(!prioritysrp) - return CURLE_OUT_OF_MEMORY; - strcpy(prioritysrp, prioritylist); - strcpy(prioritysrp + len, ":" GNUTLS_SRP); - rc = gnutls_priority_set_direct(session, prioritysrp, &err); - free(prioritysrp); - - if((rc == GNUTLS_E_INVALID_REQUEST) && err) { - infof(data, "This GnuTLS does not support SRP"); - } - } - else { -#endif - infof(data, "GnuTLS ciphers: %s", prioritylist); - rc = gnutls_priority_set_direct(session, prioritylist, &err); -#ifdef USE_GNUTLS_SRP - } -#endif - - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "Error %d setting GnuTLS cipher list starting with %s", - rc, err); - return CURLE_SSL_CONNECT_ERROR; - } - - if(conn->bits.tls_enable_alpn) { - int cur = 0; - gnutls_datum_t protocols[2]; - -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2 -#ifndef CURL_DISABLE_PROXY - && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy) -#endif - ) { - protocols[cur].data = (unsigned char *)ALPN_H2; - protocols[cur].size = ALPN_H2_LENGTH; - cur++; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); - } -#endif - - protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1; - protocols[cur].size = ALPN_HTTP_1_1_LENGTH; - cur++; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); - - if(gnutls_alpn_set_protocols(session, protocols, cur, 0)) { - failf(data, "failed setting ALPN"); - return CURLE_SSL_CONNECT_ERROR; - } - } - - if(SSL_SET_OPTION(primary.clientcert)) { - if(SSL_SET_OPTION(key_passwd)) { - const unsigned int supported_key_encryption_algorithms = - GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR | - GNUTLS_PKCS_USE_PKCS12_RC2_40 | GNUTLS_PKCS_USE_PBES2_3DES | - GNUTLS_PKCS_USE_PBES2_AES_128 | GNUTLS_PKCS_USE_PBES2_AES_192 | - GNUTLS_PKCS_USE_PBES2_AES_256; - rc = gnutls_certificate_set_x509_key_file2( - backend->cred, - SSL_SET_OPTION(primary.clientcert), - SSL_SET_OPTION(key) ? - SSL_SET_OPTION(key) : SSL_SET_OPTION(primary.clientcert), - do_file_type(SSL_SET_OPTION(cert_type)), - SSL_SET_OPTION(key_passwd), - supported_key_encryption_algorithms); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, - "error reading X.509 potentially-encrypted key file: %s", - gnutls_strerror(rc)); - return CURLE_SSL_CONNECT_ERROR; - } - } - else { - if(gnutls_certificate_set_x509_key_file( - backend->cred, - SSL_SET_OPTION(primary.clientcert), - SSL_SET_OPTION(key) ? - SSL_SET_OPTION(key) : SSL_SET_OPTION(primary.clientcert), - do_file_type(SSL_SET_OPTION(cert_type)) ) != - GNUTLS_E_SUCCESS) { - failf(data, "error reading X.509 key or certificate file"); - return CURLE_SSL_CONNECT_ERROR; - } - } - } - -#ifdef USE_GNUTLS_SRP - /* put the credentials to the current session */ - if(SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP) { - rc = gnutls_credentials_set(session, GNUTLS_CRD_SRP, - backend->srp_client_cred); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); - return CURLE_SSL_CONNECT_ERROR; - } - } - else -#endif - { - rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, - backend->cred); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc)); - return CURLE_SSL_CONNECT_ERROR; - } - } - -#ifndef CURL_DISABLE_PROXY - if(conn->proxy_ssl[sockindex].use) { - struct ssl_backend_data *proxy_backend; - proxy_backend = conn->proxy_ssl[sockindex].backend; - DEBUGASSERT(proxy_backend); - transport_ptr = proxy_backend->session; - gnutls_transport_push = gtls_push_ssl; - gnutls_transport_pull = gtls_pull_ssl; - } - else -#endif - { - /* file descriptor for the socket */ - transport_ptr = &conn->sock[sockindex]; - gnutls_transport_push = gtls_push; - gnutls_transport_pull = gtls_pull; - } - - /* set the connection handle */ - gnutls_transport_set_ptr(session, transport_ptr); - - /* register callback functions to send and receive data. */ - gnutls_transport_set_push_function(session, gnutls_transport_push); - gnutls_transport_set_pull_function(session, gnutls_transport_pull); - - if(SSL_CONN_CONFIG(verifystatus)) { - rc = gnutls_ocsp_status_request_enable_client(session, NULL, 0, NULL); - if(rc != GNUTLS_E_SUCCESS) { - failf(data, "gnutls_ocsp_status_request_enable_client() failed: %d", rc); - return CURLE_SSL_CONNECT_ERROR; - } - } - - /* This might be a reconnect, so we check for a session ID in the cache - to speed up things */ - if(SSL_SET_OPTION(primary.sessionid)) { - void *ssl_sessionid; - size_t ssl_idsize; - - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, - SSL_IS_PROXY() ? TRUE : FALSE, - &ssl_sessionid, &ssl_idsize, sockindex)) { - /* we got a session id, use it! */ - gnutls_session_set_data(session, ssl_sessionid, ssl_idsize); - - /* Informational message */ - infof(data, "SSL re-using session ID"); - } - Curl_ssl_sessionid_unlock(data); - } - - return CURLE_OK; -} - -static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, - gnutls_x509_crt_t cert, - const char *pinnedpubkey) -{ - /* Scratch */ - size_t len1 = 0, len2 = 0; - unsigned char *buff1 = NULL; - - gnutls_pubkey_t key = NULL; - - /* Result is returned to caller */ - CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - - /* if a path wasn't specified, don't pin */ - if(!pinnedpubkey) - return CURLE_OK; - - if(!cert) - return result; - - do { - int ret; - - /* Begin Gyrations to get the public key */ - gnutls_pubkey_init(&key); - - ret = gnutls_pubkey_import_x509(key, cert, 0); - if(ret < 0) - break; /* failed */ - - ret = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, NULL, &len1); - if(ret != GNUTLS_E_SHORT_MEMORY_BUFFER || len1 == 0) - break; /* failed */ - - buff1 = malloc(len1); - if(!buff1) - break; /* failed */ - - len2 = len1; - - ret = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, buff1, &len2); - if(ret < 0 || len1 != len2) - break; /* failed */ - - /* End Gyrations */ - - /* The one good exit point */ - result = Curl_pin_peer_pubkey(data, pinnedpubkey, buff1, len1); - } while(0); - - if(key) - gnutls_pubkey_deinit(key); - - Curl_safefree(buff1); - - return result; -} - -static Curl_recv gtls_recv; -static Curl_send gtls_send; - -CURLcode -Curl_gtls_verifyserver(struct Curl_easy *data, - struct connectdata *conn, - gnutls_session_t session, - int sockindex) -{ - unsigned int cert_list_size; - const gnutls_datum_t *chainp; - unsigned int verify_status = 0; - gnutls_x509_crt_t x509_cert, x509_issuer; - gnutls_datum_t issuerp; - gnutls_datum_t certfields; - char certname[65] = ""; /* limited to 64 chars by ASN.1 */ - size_t size; - time_t certclock; - const char *ptr; - int rc; - gnutls_datum_t proto; - CURLcode result = CURLE_OK; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - unsigned int algo; - unsigned int bits; - gnutls_protocol_t version = gnutls_protocol_get_version(session); -#endif - const char * const hostname = SSL_HOST_NAME(); - long * const certverifyresult = &SSL_SET_OPTION_LVALUE(certverifyresult); - - /* the name of the cipher suite used, e.g. ECDHE_RSA_AES_256_GCM_SHA384. */ - ptr = gnutls_cipher_suite_get_name(gnutls_kx_get(session), - gnutls_cipher_get(session), - gnutls_mac_get(session)); - - infof(data, "SSL connection using %s / %s", - gnutls_protocol_get_name(version), ptr); - - /* This function will return the peer's raw certificate (chain) as sent by - the peer. These certificates are in raw format (DER encoded for - X.509). In case of a X.509 then a certificate list may be present. The - first certificate in the list is the peer's certificate, following the - issuer's certificate, then the issuer's issuer etc. */ - - chainp = gnutls_certificate_get_peers(session, &cert_list_size); - if(!chainp) { - if(SSL_CONN_CONFIG(verifypeer) || - SSL_CONN_CONFIG(verifyhost) || - SSL_CONN_CONFIG(issuercert)) { -#ifdef USE_GNUTLS_SRP - if(SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP - && SSL_SET_OPTION(primary.username) - && !SSL_CONN_CONFIG(verifypeer) - && gnutls_cipher_get(session)) { - /* no peer cert, but auth is ok if we have SRP user and cipher and no - peer verify */ - } - else { -#endif - failf(data, "failed to get server cert"); - *certverifyresult = GNUTLS_E_NO_CERTIFICATE_FOUND; - return CURLE_PEER_FAILED_VERIFICATION; -#ifdef USE_GNUTLS_SRP - } -#endif - } - infof(data, " common name: WARNING couldn't obtain"); - } - - if(data->set.ssl.certinfo && chainp) { - unsigned int i; - - result = Curl_ssl_init_certinfo(data, cert_list_size); - if(result) - return result; - - for(i = 0; i < cert_list_size; i++) { - const char *beg = (const char *) chainp[i].data; - const char *end = beg + chainp[i].size; - - result = Curl_extract_certinfo(data, i, beg, end); - if(result) - return result; - } - } - - if(SSL_CONN_CONFIG(verifypeer)) { - /* This function will try to verify the peer's certificate and return its - status (trusted, invalid etc.). The value of status should be one or - more of the gnutls_certificate_status_t enumerated elements bitwise - or'd. To avoid denial of service attacks some default upper limits - regarding the certificate key size and chain size are set. To override - them use gnutls_certificate_set_verify_limits(). */ - - rc = gnutls_certificate_verify_peers2(session, &verify_status); - if(rc < 0) { - failf(data, "server cert verify failed: %d", rc); - *certverifyresult = rc; - return CURLE_SSL_CONNECT_ERROR; - } - - *certverifyresult = verify_status; - - /* verify_status is a bitmask of gnutls_certificate_status bits */ - if(verify_status & GNUTLS_CERT_INVALID) { - if(SSL_CONN_CONFIG(verifypeer)) { - failf(data, "server certificate verification failed. CAfile: %s " - "CRLfile: %s", SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile): - "none", - SSL_SET_OPTION(primary.CRLfile) ? - SSL_SET_OPTION(primary.CRLfile) : "none"); - return CURLE_PEER_FAILED_VERIFICATION; - } - else - infof(data, " server certificate verification FAILED"); - } - else - infof(data, " server certificate verification OK"); - } - else - infof(data, " server certificate verification SKIPPED"); - - if(SSL_CONN_CONFIG(verifystatus)) { - if(gnutls_ocsp_status_request_is_checked(session, 0) == 0) { - gnutls_datum_t status_request; - gnutls_ocsp_resp_t ocsp_resp; - - gnutls_ocsp_cert_status_t status; - gnutls_x509_crl_reason_t reason; - - rc = gnutls_ocsp_status_request_get(session, &status_request); - - infof(data, " server certificate status verification FAILED"); - - if(rc == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) { - failf(data, "No OCSP response received"); - return CURLE_SSL_INVALIDCERTSTATUS; - } - - if(rc < 0) { - failf(data, "Invalid OCSP response received"); - return CURLE_SSL_INVALIDCERTSTATUS; - } - - gnutls_ocsp_resp_init(&ocsp_resp); - - rc = gnutls_ocsp_resp_import(ocsp_resp, &status_request); - if(rc < 0) { - failf(data, "Invalid OCSP response received"); - return CURLE_SSL_INVALIDCERTSTATUS; - } - - (void)gnutls_ocsp_resp_get_single(ocsp_resp, 0, NULL, NULL, NULL, NULL, - &status, NULL, NULL, NULL, &reason); - - switch(status) { - case GNUTLS_OCSP_CERT_GOOD: - break; - - case GNUTLS_OCSP_CERT_REVOKED: { - const char *crl_reason; - - switch(reason) { - default: - case GNUTLS_X509_CRLREASON_UNSPECIFIED: - crl_reason = "unspecified reason"; - break; - - case GNUTLS_X509_CRLREASON_KEYCOMPROMISE: - crl_reason = "private key compromised"; - break; - - case GNUTLS_X509_CRLREASON_CACOMPROMISE: - crl_reason = "CA compromised"; - break; - - case GNUTLS_X509_CRLREASON_AFFILIATIONCHANGED: - crl_reason = "affiliation has changed"; - break; - - case GNUTLS_X509_CRLREASON_SUPERSEDED: - crl_reason = "certificate superseded"; - break; - - case GNUTLS_X509_CRLREASON_CESSATIONOFOPERATION: - crl_reason = "operation has ceased"; - break; - - case GNUTLS_X509_CRLREASON_CERTIFICATEHOLD: - crl_reason = "certificate is on hold"; - break; - - case GNUTLS_X509_CRLREASON_REMOVEFROMCRL: - crl_reason = "will be removed from delta CRL"; - break; - - case GNUTLS_X509_CRLREASON_PRIVILEGEWITHDRAWN: - crl_reason = "privilege withdrawn"; - break; - - case GNUTLS_X509_CRLREASON_AACOMPROMISE: - crl_reason = "AA compromised"; - break; - } - - failf(data, "Server certificate was revoked: %s", crl_reason); - break; - } - - default: - case GNUTLS_OCSP_CERT_UNKNOWN: - failf(data, "Server certificate status is unknown"); - break; - } - - gnutls_ocsp_resp_deinit(ocsp_resp); - - return CURLE_SSL_INVALIDCERTSTATUS; - } - else - infof(data, " server certificate status verification OK"); - } - else - infof(data, " server certificate status verification SKIPPED"); - - /* initialize an X.509 certificate structure. */ - gnutls_x509_crt_init(&x509_cert); - - if(chainp) - /* convert the given DER or PEM encoded Certificate to the native - gnutls_x509_crt_t format */ - gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER); - - if(SSL_CONN_CONFIG(issuercert)) { - gnutls_x509_crt_init(&x509_issuer); - issuerp = load_file(SSL_CONN_CONFIG(issuercert)); - gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM); - rc = gnutls_x509_crt_check_issuer(x509_cert, x509_issuer); - gnutls_x509_crt_deinit(x509_issuer); - unload_file(issuerp); - if(rc <= 0) { - failf(data, "server certificate issuer check failed (IssuerCert: %s)", - SSL_CONN_CONFIG(issuercert)?SSL_CONN_CONFIG(issuercert):"none"); - gnutls_x509_crt_deinit(x509_cert); - return CURLE_SSL_ISSUER_ERROR; - } - infof(data, " server certificate issuer check OK (Issuer Cert: %s)", - SSL_CONN_CONFIG(issuercert)?SSL_CONN_CONFIG(issuercert):"none"); - } - - size = sizeof(certname); - rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME, - 0, /* the first and only one */ - FALSE, - certname, - &size); - if(rc) { - infof(data, "error fetching CN from cert:%s", - gnutls_strerror(rc)); - } - - /* This function will check if the given certificate's subject matches the - given hostname. This is a basic implementation of the matching described - in RFC2818 (HTTPS), which takes into account wildcards, and the subject - alternative name PKIX extension. Returns non zero on success, and zero on - failure. */ - rc = gnutls_x509_crt_check_hostname(x509_cert, hostname); -#if GNUTLS_VERSION_NUMBER < 0x030306 - /* Before 3.3.6, gnutls_x509_crt_check_hostname() didn't check IP - addresses. */ - if(!rc) { -#ifdef ENABLE_IPV6 - #define use_addr in6_addr -#else - #define use_addr in_addr -#endif - unsigned char addrbuf[sizeof(struct use_addr)]; - size_t addrlen = 0; - - if(Curl_inet_pton(AF_INET, hostname, addrbuf) > 0) - addrlen = 4; -#ifdef ENABLE_IPV6 - else if(Curl_inet_pton(AF_INET6, hostname, addrbuf) > 0) - addrlen = 16; -#endif - - if(addrlen) { - unsigned char certaddr[sizeof(struct use_addr)]; - int i; - - for(i = 0; ; i++) { - size_t certaddrlen = sizeof(certaddr); - int ret = gnutls_x509_crt_get_subject_alt_name(x509_cert, i, certaddr, - &certaddrlen, NULL); - /* If this happens, it wasn't an IP address. */ - if(ret == GNUTLS_E_SHORT_MEMORY_BUFFER) - continue; - if(ret < 0) - break; - if(ret != GNUTLS_SAN_IPADDRESS) - continue; - if(certaddrlen == addrlen && !memcmp(addrbuf, certaddr, addrlen)) { - rc = 1; - break; - } - } - } - } -#endif - if(!rc) { - if(SSL_CONN_CONFIG(verifyhost)) { - failf(data, "SSL: certificate subject name (%s) does not match " - "target host name '%s'", certname, SSL_HOST_DISPNAME()); - gnutls_x509_crt_deinit(x509_cert); - return CURLE_PEER_FAILED_VERIFICATION; - } - else - infof(data, " common name: %s (does not match '%s')", - certname, SSL_HOST_DISPNAME()); - } - else - infof(data, " common name: %s (matched)", certname); - - /* Check for time-based validity */ - certclock = gnutls_x509_crt_get_expiration_time(x509_cert); - - if(certclock == (time_t)-1) { - if(SSL_CONN_CONFIG(verifypeer)) { - failf(data, "server cert expiration date verify failed"); - *certverifyresult = GNUTLS_CERT_EXPIRED; - gnutls_x509_crt_deinit(x509_cert); - return CURLE_SSL_CONNECT_ERROR; - } - else - infof(data, " server certificate expiration date verify FAILED"); - } - else { - if(certclock < time(NULL)) { - if(SSL_CONN_CONFIG(verifypeer)) { - failf(data, "server certificate expiration date has passed."); - *certverifyresult = GNUTLS_CERT_EXPIRED; - gnutls_x509_crt_deinit(x509_cert); - return CURLE_PEER_FAILED_VERIFICATION; - } - else - infof(data, " server certificate expiration date FAILED"); - } - else - infof(data, " server certificate expiration date OK"); - } - - certclock = gnutls_x509_crt_get_activation_time(x509_cert); - - if(certclock == (time_t)-1) { - if(SSL_CONN_CONFIG(verifypeer)) { - failf(data, "server cert activation date verify failed"); - *certverifyresult = GNUTLS_CERT_NOT_ACTIVATED; - gnutls_x509_crt_deinit(x509_cert); - return CURLE_SSL_CONNECT_ERROR; - } - else - infof(data, " server certificate activation date verify FAILED"); - } - else { - if(certclock > time(NULL)) { - if(SSL_CONN_CONFIG(verifypeer)) { - failf(data, "server certificate not activated yet."); - *certverifyresult = GNUTLS_CERT_NOT_ACTIVATED; - gnutls_x509_crt_deinit(x509_cert); - return CURLE_PEER_FAILED_VERIFICATION; - } - else - infof(data, " server certificate activation date FAILED"); - } - else - infof(data, " server certificate activation date OK"); - } - - ptr = SSL_PINNED_PUB_KEY(); - if(ptr) { - result = pkp_pin_peer_pubkey(data, x509_cert, ptr); - if(result != CURLE_OK) { - failf(data, "SSL: public key does not match pinned public key"); - gnutls_x509_crt_deinit(x509_cert); - return result; - } - } - - /* Show: - - - subject - - start date - - expire date - - common name - - issuer - - */ - -#ifndef CURL_DISABLE_VERBOSE_STRINGS - /* public key algorithm's parameters */ - algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits); - infof(data, " certificate public key: %s", - gnutls_pk_algorithm_get_name(algo)); - - /* version of the X.509 certificate. */ - infof(data, " certificate version: #%d", - gnutls_x509_crt_get_version(x509_cert)); - - - rc = gnutls_x509_crt_get_dn2(x509_cert, &certfields); - if(rc) - infof(data, "Failed to get certificate name"); - else { - infof(data, " subject: %s", certfields.data); - - certclock = gnutls_x509_crt_get_activation_time(x509_cert); - showtime(data, "start date", certclock); - - certclock = gnutls_x509_crt_get_expiration_time(x509_cert); - showtime(data, "expire date", certclock); - - gnutls_free(certfields.data); - } - - rc = gnutls_x509_crt_get_issuer_dn2(x509_cert, &certfields); - if(rc) - infof(data, "Failed to get certificate issuer"); - else { - infof(data, " issuer: %s", certfields.data); - - gnutls_free(certfields.data); - } -#endif - - gnutls_x509_crt_deinit(x509_cert); - - if(conn->bits.tls_enable_alpn) { - rc = gnutls_alpn_get_selected_protocol(session, &proto); - if(rc == 0) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, proto.size, - proto.data); - -#ifdef USE_HTTP2 - if(proto.size == ALPN_H2_LENGTH && - !memcmp(ALPN_H2, proto.data, - ALPN_H2_LENGTH)) { - conn->negnpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(proto.size == ALPN_HTTP_1_1_LENGTH && - !memcmp(ALPN_HTTP_1_1, proto.data, ALPN_HTTP_1_1_LENGTH)) { - conn->negnpn = CURL_HTTP_VERSION_1_1; - } - } - else - infof(data, VTLS_INFOF_NO_ALPN); - - Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - } - - conn->ssl[sockindex].state = ssl_connection_complete; - - if(SSL_SET_OPTION(primary.sessionid)) { - /* we always unconditionally get the session id here, as even if we - already got it from the cache and asked to use it in the connection, it - might've been rejected and then a new one is in use now and we need to - detect that. */ - void *connect_sessionid; - size_t connect_idsize = 0; - - /* get the session ID data size */ - gnutls_session_get_data(session, NULL, &connect_idsize); - connect_sessionid = malloc(connect_idsize); /* get a buffer for it */ - - if(connect_sessionid) { - bool incache; - bool added = FALSE; - void *ssl_sessionid; - - /* extract session ID to the allocated buffer */ - gnutls_session_get_data(session, connect_sessionid, &connect_idsize); - - Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(data, conn, - SSL_IS_PROXY() ? TRUE : FALSE, - &ssl_sessionid, NULL, sockindex)); - if(incache) { - /* there was one before in the cache, so instead of risking that the - previous one was rejected, we just kill that and store the new */ - Curl_ssl_delsessionid(data, ssl_sessionid); - } - - /* store this session id */ - result = Curl_ssl_addsessionid(data, conn, - SSL_IS_PROXY() ? TRUE : FALSE, - connect_sessionid, connect_idsize, - sockindex, &added); - Curl_ssl_sessionid_unlock(data); - if(!added) - free(connect_sessionid); - if(result) { - result = CURLE_OUT_OF_MEMORY; - } - } - else - result = CURLE_OUT_OF_MEMORY; - } - - return result; -} - - -/* - * This function is called after the TCP connect has completed. Setup the TLS - * layer and do all necessary magic. - */ -/* We use connssl->connecting_state to keep track of the connection status; - there are three states: 'ssl_connect_1' (not started yet or complete), - 'ssl_connect_2_reading' (waiting for data from server), and - 'ssl_connect_2_writing' (waiting to be able to write). - */ -static CURLcode -gtls_connect_common(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool nonblocking, - bool *done) -{ - int rc; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - - /* Initiate the connection, if not already done */ - if(ssl_connect_1 == connssl->connecting_state) { - rc = gtls_connect_step1(data, conn, sockindex); - if(rc) - return rc; - } - - rc = handshake(data, conn, sockindex, TRUE, nonblocking); - if(rc) - /* handshake() sets its own error message with failf() */ - return rc; - - /* Finish connecting once the handshake is done */ - if(ssl_connect_1 == connssl->connecting_state) { - struct ssl_backend_data *backend = connssl->backend; - gnutls_session_t session; - DEBUGASSERT(backend); - session = backend->session; - rc = Curl_gtls_verifyserver(data, conn, session, sockindex); - if(rc) - return rc; - conn->recv[sockindex] = gtls_recv; - conn->send[sockindex] = gtls_send; - } - - *done = ssl_connect_1 == connssl->connecting_state; - - return CURLE_OK; -} - -static CURLcode gtls_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, bool *done) -{ - return gtls_connect_common(data, conn, sockindex, TRUE, done); -} - -static CURLcode gtls_connect(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - CURLcode result; - bool done = FALSE; - - result = gtls_connect_common(data, conn, sockindex, FALSE, &done); - if(result) - return result; - - DEBUGASSERT(done); - - return CURLE_OK; -} - -static bool gtls_data_pending(const struct connectdata *conn, - int connindex) -{ - const struct ssl_connect_data *connssl = &conn->ssl[connindex]; - bool res = FALSE; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - if(backend->session && - 0 != gnutls_record_check_pending(backend->session)) - res = TRUE; - -#ifndef CURL_DISABLE_PROXY - connssl = &conn->proxy_ssl[connindex]; - backend = connssl->backend; - DEBUGASSERT(backend); - if(backend->session && - 0 != gnutls_record_check_pending(backend->session)) - res = TRUE; -#endif - - return res; -} - -static ssize_t gtls_send(struct Curl_easy *data, - int sockindex, - const void *mem, - size_t len, - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - ssize_t rc; - - DEBUGASSERT(backend); - rc = gnutls_record_send(backend->session, mem, len); - - if(rc < 0) { - *curlcode = (rc == GNUTLS_E_AGAIN) - ? CURLE_AGAIN - : CURLE_SEND_ERROR; - - rc = -1; - } - - return rc; -} - -static void close_one(struct ssl_connect_data *connssl) -{ - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - - if(backend->session) { - char buf[32]; - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)gnutls_record_recv(backend->session, buf, sizeof(buf)); - gnutls_bye(backend->session, GNUTLS_SHUT_WR); - gnutls_deinit(backend->session); - backend->session = NULL; - } - if(backend->cred) { - gnutls_certificate_free_credentials(backend->cred); - backend->cred = NULL; - } -#ifdef USE_GNUTLS_SRP - if(backend->srp_client_cred) { - gnutls_srp_free_client_credentials(backend->srp_client_cred); - backend->srp_client_cred = NULL; - } -#endif -} - -static void gtls_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - (void) data; - close_one(&conn->ssl[sockindex]); -#ifndef CURL_DISABLE_PROXY - close_one(&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 gtls_shutdown(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - int retval = 0; - - DEBUGASSERT(backend); - -#ifndef CURL_DISABLE_FTP - /* This has only been tested on the proftpd server, and the mod_tls code - sends a close notify alert without waiting for a close notify alert in - response. Thus we wait for a close notify alert from the server, but - we do not send one. Let's hope other servers do the same... */ - - if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE) - gnutls_bye(backend->session, GNUTLS_SHUT_WR); -#endif - - if(backend->session) { - ssize_t result; - bool done = FALSE; - char buf[120]; - - while(!done) { - int what = SOCKET_READABLE(conn->sock[sockindex], - SSL_SHUTDOWN_TIMEOUT); - if(what > 0) { - /* Something to read, let's do it and hope that it is the close - notify alert from the server */ - result = gnutls_record_recv(backend->session, - buf, sizeof(buf)); - switch(result) { - case 0: - /* This is the expected response. There was no data but only - the close notify alert */ - done = TRUE; - break; - case GNUTLS_E_AGAIN: - case GNUTLS_E_INTERRUPTED: - infof(data, "GNUTLS_E_AGAIN || GNUTLS_E_INTERRUPTED"); - break; - default: - retval = -1; - done = TRUE; - break; - } - } - else if(0 == what) { - /* timeout */ - failf(data, "SSL shutdown timeout"); - done = TRUE; - } - else { - /* anything that gets here is fatally bad */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - retval = -1; - done = TRUE; - } - } - gnutls_deinit(backend->session); - } - gnutls_certificate_free_credentials(backend->cred); - -#ifdef USE_GNUTLS_SRP - if(SSL_SET_OPTION(primary.authtype) == CURL_TLSAUTH_SRP - && SSL_SET_OPTION(primary.username) != NULL) - gnutls_srp_free_client_credentials(backend->srp_client_cred); -#endif - - backend->cred = NULL; - backend->session = NULL; - - return retval; -} - -static ssize_t gtls_recv(struct Curl_easy *data, /* connection data */ - int num, /* socketindex */ - char *buf, /* store read data here */ - size_t buffersize, /* max amount to read */ - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[num]; - struct ssl_backend_data *backend = connssl->backend; - ssize_t ret; - - DEBUGASSERT(backend); - - ret = gnutls_record_recv(backend->session, buf, buffersize); - if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { - *curlcode = CURLE_AGAIN; - return -1; - } - - if(ret == GNUTLS_E_REHANDSHAKE) { - /* BLOCKING call, this is bad but a work-around for now. Fixing this "the - proper way" takes a whole lot of work. */ - CURLcode result = handshake(data, conn, num, FALSE, FALSE); - if(result) - /* handshake() writes error message on its own */ - *curlcode = result; - else - *curlcode = CURLE_AGAIN; /* then return as if this was a wouldblock */ - return -1; - } - - if(ret < 0) { - failf(data, "GnuTLS recv error (%d): %s", - - (int)ret, gnutls_strerror((int)ret)); - *curlcode = CURLE_RECV_ERROR; - return -1; - } - - return ret; -} - -static void gtls_session_free(void *ptr) -{ - free(ptr); -} - -static size_t gtls_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL)); -} - -/* data might be NULL! */ -static CURLcode gtls_random(struct Curl_easy *data, - unsigned char *entropy, size_t length) -{ - int rc; - (void)data; - rc = gnutls_rnd(GNUTLS_RND_RANDOM, entropy, length); - return rc?CURLE_FAILED_INIT:CURLE_OK; -} - -static CURLcode gtls_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, - unsigned char *sha256sum, /* output */ - size_t sha256len) -{ - struct sha256_ctx SHA256pw; - sha256_init(&SHA256pw); - sha256_update(&SHA256pw, (unsigned int)tmplen, tmp); - sha256_digest(&SHA256pw, (unsigned int)sha256len, sha256sum); - return CURLE_OK; -} - -static bool gtls_cert_status_request(void) -{ - return TRUE; -} - -static void *gtls_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct ssl_backend_data *backend = connssl->backend; - (void)info; - DEBUGASSERT(backend); - return backend->session; -} - -const struct Curl_ssl Curl_ssl_gnutls = { - { CURLSSLBACKEND_GNUTLS, "gnutls" }, /* info */ - - SSLSUPP_CA_PATH | - SSLSUPP_CERTINFO | - SSLSUPP_PINNEDPUBKEY | - SSLSUPP_HTTPS_PROXY, - - sizeof(struct ssl_backend_data), - - gtls_init, /* init */ - gtls_cleanup, /* cleanup */ - gtls_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - gtls_shutdown, /* shutdown */ - gtls_data_pending, /* data_pending */ - gtls_random, /* random */ - gtls_cert_status_request, /* cert_status_request */ - gtls_connect, /* connect */ - gtls_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - gtls_get_internals, /* get_internals */ - gtls_close, /* close_one */ - Curl_none_close_all, /* close_all */ - gtls_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - gtls_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#endif /* USE_GNUTLS */ diff --git a/contrib/libs/curl/lib/vtls/mbedtls.c b/contrib/libs/curl/lib/vtls/mbedtls.c deleted file mode 100644 index 70b51b8bdd..0000000000 --- a/contrib/libs/curl/lib/vtls/mbedtls.c +++ /dev/null @@ -1,1271 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com> - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -/* - * Source file for all mbedTLS-specific code for the TLS/SSL layer. No code - * but vtls.c should ever call or use these functions. - * - */ - -#include "curl_setup.h" - -#ifdef USE_MBEDTLS - -/* Define this to enable lots of debugging for mbedTLS */ -/* #define MBEDTLS_DEBUG */ - -#error #include <mbedtls/version.h> -#if MBEDTLS_VERSION_NUMBER >= 0x02040000 -#error #include <mbedtls/net_sockets.h> -#else -#error #include <mbedtls/net.h> -#endif -#error #include <mbedtls/ssl.h> -#error #include <mbedtls/x509.h> - -#error #include <mbedtls/error.h> -#error #include <mbedtls/entropy.h> -#error #include <mbedtls/ctr_drbg.h> -#error #include <mbedtls/sha256.h> - -#if MBEDTLS_VERSION_MAJOR >= 2 -# ifdef MBEDTLS_DEBUG -# error #include <mbedtls/debug.h> -# endif -#endif - -#include "urldata.h" -#include "sendf.h" -#include "inet_pton.h" -#include "mbedtls.h" -#include "vtls.h" -#include "parsedate.h" -#include "connect.h" /* for the connect timeout */ -#include "select.h" -#include "multiif.h" -#error #include "mbedtls_threadlock.h" - -/* The last 3 #include files should be in this order */ -#include "curl_printf.h" -#include "curl_memory.h" -#include "memdebug.h" - -/* ALPN for http2 */ -#ifdef USE_HTTP2 -# undef HAS_ALPN -# ifdef MBEDTLS_SSL_ALPN -# define HAS_ALPN -# endif -#endif - -struct ssl_backend_data { - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_entropy_context entropy; - mbedtls_ssl_context ssl; - mbedtls_x509_crt cacert; - mbedtls_x509_crt clicert; -#ifdef MBEDTLS_X509_CRL_PARSE_C - mbedtls_x509_crl crl; -#endif - mbedtls_pk_context pk; - mbedtls_ssl_config config; -#ifdef HAS_ALPN - const char *protocols[3]; -#endif -}; - -/* apply threading? */ -#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32) -#define THREADING_SUPPORT -#endif - -#ifndef MBEDTLS_ERROR_C -#define mbedtls_strerror(a,b,c) b[0] = 0 -#endif - -#if defined(THREADING_SUPPORT) -static mbedtls_entropy_context ts_entropy; - -static int entropy_init_initialized = 0; - -/* start of entropy_init_mutex() */ -static void entropy_init_mutex(mbedtls_entropy_context *ctx) -{ - /* lock 0 = entropy_init_mutex() */ - Curl_mbedtlsthreadlock_lock_function(0); - if(entropy_init_initialized == 0) { - mbedtls_entropy_init(ctx); - entropy_init_initialized = 1; - } - Curl_mbedtlsthreadlock_unlock_function(0); -} -/* end of entropy_init_mutex() */ - -/* start of entropy_func_mutex() */ -static int entropy_func_mutex(void *data, unsigned char *output, size_t len) -{ - int ret; - /* lock 1 = entropy_func_mutex() */ - Curl_mbedtlsthreadlock_lock_function(1); - ret = mbedtls_entropy_func(data, output, len); - Curl_mbedtlsthreadlock_unlock_function(1); - - return ret; -} -/* end of entropy_func_mutex() */ - -#endif /* THREADING_SUPPORT */ - -#ifdef MBEDTLS_DEBUG -static void mbed_debug(void *context, int level, const char *f_name, - int line_nb, const char *line) -{ - struct Curl_easy *data = NULL; - - if(!context) - return; - - data = (struct Curl_easy *)context; - - infof(data, "%s", line); - (void) level; -} -#else -#endif - -/* - * profile - */ -static const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_fr = -{ - /* Hashes from SHA-1 and above */ - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA1) | - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_RIPEMD160) | - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA224) | - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA256) | - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA384) | - MBEDTLS_X509_ID_FLAG(MBEDTLS_MD_SHA512), - 0xFFFFFFF, /* Any PK alg */ - 0xFFFFFFF, /* Any curve */ - 1024, /* RSA min key len */ -}; - -/* See https://tls.mbed.org/discussions/generic/ - howto-determine-exact-buffer-len-for-mbedtls_pk_write_pubkey_der -*/ -#define RSA_PUB_DER_MAX_BYTES (38 + 2 * MBEDTLS_MPI_MAX_SIZE) -#define ECP_PUB_DER_MAX_BYTES (30 + 2 * MBEDTLS_ECP_MAX_BYTES) - -#define PUB_DER_MAX_BYTES (RSA_PUB_DER_MAX_BYTES > ECP_PUB_DER_MAX_BYTES ? \ - RSA_PUB_DER_MAX_BYTES : ECP_PUB_DER_MAX_BYTES) - -static Curl_recv mbed_recv; -static Curl_send mbed_send; - -static CURLcode mbedtls_version_from_curl(int *mbedver, long version) -{ -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 - switch(version) { - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - *mbedver = MBEDTLS_SSL_MINOR_VERSION_3; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_3: - break; - } -#else - switch(version) { - case CURL_SSLVERSION_TLSv1_0: - *mbedver = MBEDTLS_SSL_MINOR_VERSION_1; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_1: - *mbedver = MBEDTLS_SSL_MINOR_VERSION_2; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_2: - *mbedver = MBEDTLS_SSL_MINOR_VERSION_3; - return CURLE_OK; - case CURL_SSLVERSION_TLSv1_3: - break; - } -#endif - - return CURLE_SSL_CONNECT_ERROR; -} - -static CURLcode -set_ssl_version_min_max(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 - int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_3; - int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_3; -#else - int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_1; - int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_1; -#endif - long ssl_version = SSL_CONN_CONFIG(version); - long ssl_version_max = SSL_CONN_CONFIG(version_max); - CURLcode result = CURLE_OK; - - DEBUGASSERT(backend); - - switch(ssl_version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - ssl_version = CURL_SSLVERSION_TLSv1_0; - break; - } - - switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; - break; - } - - result = mbedtls_version_from_curl(&mbedtls_ver_min, ssl_version); - if(result) { - failf(data, "unsupported min version passed via CURLOPT_SSLVERSION"); - return result; - } - result = mbedtls_version_from_curl(&mbedtls_ver_max, ssl_version_max >> 16); - if(result) { - failf(data, "unsupported max version passed via CURLOPT_SSLVERSION"); - return result; - } - - mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, - mbedtls_ver_min); - mbedtls_ssl_conf_max_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, - mbedtls_ver_max); - - return result; -} - -static CURLcode -mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); - const char * const ssl_cafile = - /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); - const bool verifypeer = SSL_CONN_CONFIG(verifypeer); - const char * const ssl_capath = SSL_CONN_CONFIG(CApath); - char * const ssl_cert = SSL_SET_OPTION(primary.clientcert); - const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob); - const char * const ssl_crlfile = SSL_SET_OPTION(primary.CRLfile); - const char * const hostname = SSL_HOST_NAME(); -#ifndef CURL_DISABLE_VERBOSE_STRINGS - const long int port = SSL_HOST_PORT(); -#endif - int ret = -1; - char errorbuf[128]; - - DEBUGASSERT(backend); - - if((SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2) || - (SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv3)) { - failf(data, "Not supported SSL version"); - return CURLE_NOT_BUILT_IN; - } - -#ifdef THREADING_SUPPORT - entropy_init_mutex(&ts_entropy); - mbedtls_ctr_drbg_init(&backend->ctr_drbg); - - ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, entropy_func_mutex, - &ts_entropy, NULL, 0); - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s", - -ret, errorbuf); - return CURLE_FAILED_INIT; - } -#else - mbedtls_entropy_init(&backend->entropy); - mbedtls_ctr_drbg_init(&backend->ctr_drbg); - - ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, mbedtls_entropy_func, - &backend->entropy, NULL, 0); - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s", - -ret, errorbuf); - return CURLE_FAILED_INIT; - } -#endif /* THREADING_SUPPORT */ - - /* Load the trusted CA */ - mbedtls_x509_crt_init(&backend->cacert); - - if(ca_info_blob && verifypeer) { - /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null - terminated even when provided the exact length, forcing us to waste - extra memory here. */ - unsigned char *newblob = malloc(ca_info_blob->len + 1); - if(!newblob) - return CURLE_OUT_OF_MEMORY; - memcpy(newblob, ca_info_blob->data, ca_info_blob->len); - newblob[ca_info_blob->len] = 0; /* null terminate */ - ret = mbedtls_x509_crt_parse(&backend->cacert, newblob, - ca_info_blob->len + 1); - free(newblob); - if(ret<0) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error importing ca cert blob - mbedTLS: (-0x%04X) %s", - -ret, errorbuf); - return CURLE_SSL_CERTPROBLEM; - } - } - - if(ssl_cafile && verifypeer) { -#ifdef MBEDTLS_FS_IO - ret = mbedtls_x509_crt_parse_file(&backend->cacert, ssl_cafile); - - if(ret<0) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading ca cert file %s - mbedTLS: (-0x%04X) %s", - ssl_cafile, -ret, errorbuf); - return CURLE_SSL_CACERT_BADFILE; - } -#else - failf(data, "mbedtls: functions that use the filesystem not built in"); - return CURLE_NOT_BUILT_IN; -#endif - } - - if(ssl_capath) { -#ifdef MBEDTLS_FS_IO - ret = mbedtls_x509_crt_parse_path(&backend->cacert, ssl_capath); - - if(ret<0) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading ca cert path %s - mbedTLS: (-0x%04X) %s", - ssl_capath, -ret, errorbuf); - - if(verifypeer) - return CURLE_SSL_CACERT_BADFILE; - } -#else - failf(data, "mbedtls: functions that use the filesystem not built in"); - return CURLE_NOT_BUILT_IN; -#endif - } - - /* Load the client certificate */ - mbedtls_x509_crt_init(&backend->clicert); - - if(ssl_cert) { -#ifdef MBEDTLS_FS_IO - ret = mbedtls_x509_crt_parse_file(&backend->clicert, ssl_cert); - - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading client cert file %s - mbedTLS: (-0x%04X) %s", - ssl_cert, -ret, errorbuf); - - return CURLE_SSL_CERTPROBLEM; - } -#else - failf(data, "mbedtls: functions that use the filesystem not built in"); - return CURLE_NOT_BUILT_IN; -#endif - } - - if(ssl_cert_blob) { - /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null - terminated even when provided the exact length, forcing us to waste - extra memory here. */ - unsigned char *newblob = malloc(ssl_cert_blob->len + 1); - if(!newblob) - return CURLE_OUT_OF_MEMORY; - memcpy(newblob, ssl_cert_blob->data, ssl_cert_blob->len); - newblob[ssl_cert_blob->len] = 0; /* null terminate */ - ret = mbedtls_x509_crt_parse(&backend->clicert, newblob, - ssl_cert_blob->len + 1); - free(newblob); - - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s", - SSL_SET_OPTION(key), -ret, errorbuf); - return CURLE_SSL_CERTPROBLEM; - } - } - - /* Load the client private key */ - mbedtls_pk_init(&backend->pk); - - if(SSL_SET_OPTION(key) || SSL_SET_OPTION(key_blob)) { - if(SSL_SET_OPTION(key)) { -#ifdef MBEDTLS_FS_IO -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 - ret = mbedtls_pk_parse_keyfile(&backend->pk, SSL_SET_OPTION(key), - SSL_SET_OPTION(key_passwd), - mbedtls_ctr_drbg_random, - &backend->ctr_drbg); -#else - ret = mbedtls_pk_parse_keyfile(&backend->pk, SSL_SET_OPTION(key), - SSL_SET_OPTION(key_passwd)); -#endif - - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s", - SSL_SET_OPTION(key), -ret, errorbuf); - return CURLE_SSL_CERTPROBLEM; - } -#else - failf(data, "mbedtls: functions that use the filesystem not built in"); - return CURLE_NOT_BUILT_IN; -#endif - } - else { - const struct curl_blob *ssl_key_blob = SSL_SET_OPTION(key_blob); - const unsigned char *key_data = - (const unsigned char *)ssl_key_blob->data; - const char *passwd = SSL_SET_OPTION(key_passwd); -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 - ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, - (const unsigned char *)passwd, - passwd ? strlen(passwd) : 0, - mbedtls_ctr_drbg_random, - &backend->ctr_drbg); -#else - ret = mbedtls_pk_parse_key(&backend->pk, key_data, ssl_key_blob->len, - (const unsigned char *)passwd, - passwd ? strlen(passwd) : 0); -#endif - - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error parsing private key - mbedTLS: (-0x%04X) %s", - -ret, errorbuf); - return CURLE_SSL_CERTPROBLEM; - } - } - - if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) || - mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY))) - ret = MBEDTLS_ERR_PK_TYPE_MISMATCH; - } - - /* Load the CRL */ -#ifdef MBEDTLS_X509_CRL_PARSE_C - mbedtls_x509_crl_init(&backend->crl); - - if(ssl_crlfile) { -#ifdef MBEDTLS_FS_IO - ret = mbedtls_x509_crl_parse_file(&backend->crl, ssl_crlfile); - - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "Error reading CRL file %s - mbedTLS: (-0x%04X) %s", - ssl_crlfile, -ret, errorbuf); - - return CURLE_SSL_CRL_BADFILE; - } -#else - failf(data, "mbedtls: functions that use the filesystem not built in"); - return CURLE_NOT_BUILT_IN; -#endif - } -#else - if(ssl_crlfile) { - failf(data, "mbedtls: crl support not built in"); - return CURLE_NOT_BUILT_IN; - } -#endif - - infof(data, "mbedTLS: Connecting to %s:%ld", hostname, port); - - mbedtls_ssl_config_init(&backend->config); - ret = mbedtls_ssl_config_defaults(&backend->config, - MBEDTLS_SSL_IS_CLIENT, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT); - if(ret) { - failf(data, "mbedTLS: ssl_config failed"); - return CURLE_SSL_CONNECT_ERROR; - } - - mbedtls_ssl_init(&backend->ssl); - if(mbedtls_ssl_setup(&backend->ssl, &backend->config)) { - failf(data, "mbedTLS: ssl_init failed"); - return CURLE_SSL_CONNECT_ERROR; - } - - /* new profile with RSA min key len = 1024 ... */ - mbedtls_ssl_conf_cert_profile(&backend->config, - &mbedtls_x509_crt_profile_fr); - - switch(SSL_CONN_CONFIG(version)) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: -#if MBEDTLS_VERSION_NUMBER < 0x03000000 - mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3, - MBEDTLS_SSL_MINOR_VERSION_1); - infof(data, "mbedTLS: Set min SSL version to TLS 1.0"); - break; -#endif - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - { - CURLcode result = set_ssl_version_min_max(data, conn, sockindex); - if(result != CURLE_OK) - return result; - break; - } - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - - mbedtls_ssl_conf_authmode(&backend->config, MBEDTLS_SSL_VERIFY_OPTIONAL); - - mbedtls_ssl_conf_rng(&backend->config, mbedtls_ctr_drbg_random, - &backend->ctr_drbg); - mbedtls_ssl_set_bio(&backend->ssl, &conn->sock[sockindex], - mbedtls_net_send, - mbedtls_net_recv, - NULL /* rev_timeout() */); - - mbedtls_ssl_conf_ciphersuites(&backend->config, - mbedtls_ssl_list_ciphersuites()); - -#if defined(MBEDTLS_SSL_RENEGOTIATION) - mbedtls_ssl_conf_renegotiation(&backend->config, - MBEDTLS_SSL_RENEGOTIATION_ENABLED); -#endif - -#if defined(MBEDTLS_SSL_SESSION_TICKETS) - mbedtls_ssl_conf_session_tickets(&backend->config, - MBEDTLS_SSL_SESSION_TICKETS_DISABLED); -#endif - - /* Check if there's a cached ID we can/should use here! */ - if(SSL_SET_OPTION(primary.sessionid)) { - void *old_session = NULL; - - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, - SSL_IS_PROXY() ? TRUE : FALSE, - &old_session, NULL, sockindex)) { - ret = mbedtls_ssl_set_session(&backend->ssl, old_session); - if(ret) { - Curl_ssl_sessionid_unlock(data); - failf(data, "mbedtls_ssl_set_session returned -0x%x", -ret); - return CURLE_SSL_CONNECT_ERROR; - } - infof(data, "mbedTLS re-using session"); - } - Curl_ssl_sessionid_unlock(data); - } - - mbedtls_ssl_conf_ca_chain(&backend->config, - &backend->cacert, -#ifdef MBEDTLS_X509_CRL_PARSE_C - &backend->crl); -#else - NULL); -#endif - - if(SSL_SET_OPTION(key) || SSL_SET_OPTION(key_blob)) { - mbedtls_ssl_conf_own_cert(&backend->config, - &backend->clicert, &backend->pk); - } - { - char *snihost = Curl_ssl_snihost(data, hostname, NULL); - if(!snihost || mbedtls_ssl_set_hostname(&backend->ssl, snihost)) { - /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and - the name to set in the SNI extension. So even if curl connects to a - host specified as an IP address, this function must be used. */ - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - } - -#ifdef HAS_ALPN - if(conn->bits.tls_enable_alpn) { - const char **p = &backend->protocols[0]; -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2) - *p++ = ALPN_H2; -#endif - *p++ = ALPN_HTTP_1_1; - *p = NULL; - /* this function doesn't clone the protocols array, which is why we need - to keep it around */ - if(mbedtls_ssl_conf_alpn_protocols(&backend->config, - &backend->protocols[0])) { - failf(data, "Failed setting ALPN protocols"); - return CURLE_SSL_CONNECT_ERROR; - } - for(p = &backend->protocols[0]; *p; ++p) - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, *p); - } -#endif - -#ifdef MBEDTLS_DEBUG - /* In order to make that work in mbedtls MBEDTLS_DEBUG_C must be defined. */ - mbedtls_ssl_conf_dbg(&backend->config, mbed_debug, data); - /* - 0 No debug - * - 1 Error - * - 2 State change - * - 3 Informational - * - 4 Verbose - */ - mbedtls_debug_set_threshold(4); -#endif - - /* give application a chance to interfere with mbedTLS set up. */ - if(data->set.ssl.fsslctx) { - ret = (*data->set.ssl.fsslctx)(data, &backend->config, - data->set.ssl.fsslctxp); - if(ret) { - failf(data, "error signaled by ssl ctx callback"); - return ret; - } - } - - connssl->connecting_state = ssl_connect_2; - - return CURLE_OK; -} - -static CURLcode -mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - int ret; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - const mbedtls_x509_crt *peercert; - const char * const pinnedpubkey = SSL_PINNED_PUB_KEY(); - - DEBUGASSERT(backend); - - conn->recv[sockindex] = mbed_recv; - conn->send[sockindex] = mbed_send; - - ret = mbedtls_ssl_handshake(&backend->ssl); - - if(ret == MBEDTLS_ERR_SSL_WANT_READ) { - connssl->connecting_state = ssl_connect_2_reading; - return CURLE_OK; - } - else if(ret == MBEDTLS_ERR_SSL_WANT_WRITE) { - connssl->connecting_state = ssl_connect_2_writing; - return CURLE_OK; - } - else if(ret) { - char errorbuf[128]; - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "ssl_handshake returned - mbedTLS: (-0x%04X) %s", - -ret, errorbuf); - return CURLE_SSL_CONNECT_ERROR; - } - - infof(data, "mbedTLS: Handshake complete, cipher is %s", - mbedtls_ssl_get_ciphersuite(&backend->ssl)); - - ret = mbedtls_ssl_get_verify_result(&backend->ssl); - - if(!SSL_CONN_CONFIG(verifyhost)) - /* Ignore hostname errors if verifyhost is disabled */ - ret &= ~MBEDTLS_X509_BADCERT_CN_MISMATCH; - - if(ret && SSL_CONN_CONFIG(verifypeer)) { - if(ret & MBEDTLS_X509_BADCERT_EXPIRED) - failf(data, "Cert verify failed: BADCERT_EXPIRED"); - - else if(ret & MBEDTLS_X509_BADCERT_REVOKED) - failf(data, "Cert verify failed: BADCERT_REVOKED"); - - else if(ret & MBEDTLS_X509_BADCERT_CN_MISMATCH) - failf(data, "Cert verify failed: BADCERT_CN_MISMATCH"); - - else if(ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED) - failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED"); - - else if(ret & MBEDTLS_X509_BADCERT_FUTURE) - failf(data, "Cert verify failed: BADCERT_FUTURE"); - - return CURLE_PEER_FAILED_VERIFICATION; - } - - peercert = mbedtls_ssl_get_peer_cert(&backend->ssl); - - if(peercert && data->set.verbose) { - const size_t bufsize = 16384; - char *buffer = malloc(bufsize); - - if(!buffer) - return CURLE_OUT_OF_MEMORY; - - if(mbedtls_x509_crt_info(buffer, bufsize, "* ", peercert) > 0) - infof(data, "Dumping cert info: %s", buffer); - else - infof(data, "Unable to dump certificate information"); - - free(buffer); - } - - if(pinnedpubkey) { - int size; - CURLcode result; - mbedtls_x509_crt *p = NULL; - unsigned char *pubkey = NULL; - -#if MBEDTLS_VERSION_NUMBER == 0x03000000 - if(!peercert || !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p) || - !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len)) { -#else - if(!peercert || !peercert->raw.p || !peercert->raw.len) { -#endif - failf(data, "Failed due to missing peer certificate"); - return CURLE_SSL_PINNEDPUBKEYNOTMATCH; - } - - p = calloc(1, sizeof(*p)); - - if(!p) - return CURLE_OUT_OF_MEMORY; - - pubkey = malloc(PUB_DER_MAX_BYTES); - - if(!pubkey) { - result = CURLE_OUT_OF_MEMORY; - goto pinnedpubkey_error; - } - - mbedtls_x509_crt_init(p); - - /* Make a copy of our const peercert because mbedtls_pk_write_pubkey_der - needs a non-const key, for now. - https://github.com/ARMmbed/mbedtls/issues/396 */ -#if MBEDTLS_VERSION_NUMBER == 0x03000000 - if(mbedtls_x509_crt_parse_der(p, - peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p), - peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len))) { -#else - if(mbedtls_x509_crt_parse_der(p, peercert->raw.p, peercert->raw.len)) { -#endif - failf(data, "Failed copying peer certificate"); - result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - goto pinnedpubkey_error; - } - -#if MBEDTLS_VERSION_NUMBER == 0x03000000 - size = mbedtls_pk_write_pubkey_der(&p->MBEDTLS_PRIVATE(pk), pubkey, - PUB_DER_MAX_BYTES); -#else - size = mbedtls_pk_write_pubkey_der(&p->pk, pubkey, PUB_DER_MAX_BYTES); -#endif - - if(size <= 0) { - failf(data, "Failed copying public key from peer certificate"); - result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - goto pinnedpubkey_error; - } - - /* mbedtls_pk_write_pubkey_der writes data at the end of the buffer. */ - result = Curl_pin_peer_pubkey(data, - pinnedpubkey, - &pubkey[PUB_DER_MAX_BYTES - size], size); - pinnedpubkey_error: - mbedtls_x509_crt_free(p); - free(p); - free(pubkey); - if(result) { - return result; - } - } - -#ifdef HAS_ALPN - if(conn->bits.tls_enable_alpn) { - const char *next_protocol = mbedtls_ssl_get_alpn_protocol(&backend->ssl); - - if(next_protocol) { - infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, next_protocol); -#ifdef USE_HTTP2 - if(!strncmp(next_protocol, ALPN_H2, ALPN_H2_LENGTH) && - !next_protocol[ALPN_H2_LENGTH]) { - conn->negnpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(!strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH) && - !next_protocol[ALPN_HTTP_1_1_LENGTH]) { - conn->negnpn = CURL_HTTP_VERSION_1_1; - } - } - else { - infof(data, VTLS_INFOF_NO_ALPN); - } - Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - } -#endif - - connssl->connecting_state = ssl_connect_3; - infof(data, "SSL connected"); - - return CURLE_OK; -} - -static CURLcode -mbed_connect_step3(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - CURLcode retcode = CURLE_OK; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); - DEBUGASSERT(backend); - - if(SSL_SET_OPTION(primary.sessionid)) { - int ret; - mbedtls_ssl_session *our_ssl_sessionid; - void *old_ssl_sessionid = NULL; - bool isproxy = SSL_IS_PROXY() ? TRUE : FALSE; - bool added = FALSE; - - our_ssl_sessionid = malloc(sizeof(mbedtls_ssl_session)); - if(!our_ssl_sessionid) - return CURLE_OUT_OF_MEMORY; - - mbedtls_ssl_session_init(our_ssl_sessionid); - - ret = mbedtls_ssl_get_session(&backend->ssl, our_ssl_sessionid); - if(ret) { - if(ret != MBEDTLS_ERR_SSL_ALLOC_FAILED) - mbedtls_ssl_session_free(our_ssl_sessionid); - free(our_ssl_sessionid); - failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret); - return CURLE_SSL_CONNECT_ERROR; - } - - /* If there's already a matching session in the cache, delete it */ - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, isproxy, &old_ssl_sessionid, NULL, - sockindex)) - Curl_ssl_delsessionid(data, old_ssl_sessionid); - - retcode = Curl_ssl_addsessionid(data, conn, isproxy, our_ssl_sessionid, - 0, sockindex, &added); - Curl_ssl_sessionid_unlock(data); - if(!added) { - mbedtls_ssl_session_free(our_ssl_sessionid); - free(our_ssl_sessionid); - } - if(retcode) { - failf(data, "failed to store ssl session"); - return retcode; - } - } - - connssl->connecting_state = ssl_connect_done; - - return CURLE_OK; -} - -static ssize_t mbed_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - int ret = -1; - - DEBUGASSERT(backend); - - ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len); - - if(ret < 0) { - *curlcode = (ret == MBEDTLS_ERR_SSL_WANT_WRITE) ? - CURLE_AGAIN : CURLE_SEND_ERROR; - ret = -1; - } - - return ret; -} - -static void mbedtls_close_all(struct Curl_easy *data) -{ - (void)data; -} - -static void mbedtls_close(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - char buf[32]; - (void) data; - - DEBUGASSERT(backend); - - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, sizeof(buf)); - - mbedtls_pk_free(&backend->pk); - mbedtls_x509_crt_free(&backend->clicert); - mbedtls_x509_crt_free(&backend->cacert); -#ifdef MBEDTLS_X509_CRL_PARSE_C - mbedtls_x509_crl_free(&backend->crl); -#endif - mbedtls_ssl_config_free(&backend->config); - mbedtls_ssl_free(&backend->ssl); - mbedtls_ctr_drbg_free(&backend->ctr_drbg); -#ifndef THREADING_SUPPORT - mbedtls_entropy_free(&backend->entropy); -#endif /* THREADING_SUPPORT */ -} - -static ssize_t mbed_recv(struct Curl_easy *data, int num, - char *buf, size_t buffersize, - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[num]; - struct ssl_backend_data *backend = connssl->backend; - int ret = -1; - ssize_t len = -1; - - DEBUGASSERT(backend); - - ret = mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, - buffersize); - - if(ret <= 0) { - if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) - return 0; - - *curlcode = (ret == MBEDTLS_ERR_SSL_WANT_READ) ? - CURLE_AGAIN : CURLE_RECV_ERROR; - return -1; - } - - len = ret; - - return len; -} - -static void mbedtls_session_free(void *ptr) -{ - mbedtls_ssl_session_free(ptr); - free(ptr); -} - -static size_t mbedtls_version(char *buffer, size_t size) -{ -#ifdef MBEDTLS_VERSION_C - /* if mbedtls_version_get_number() is available it is better */ - unsigned int version = mbedtls_version_get_number(); - return msnprintf(buffer, size, "mbedTLS/%u.%u.%u", version>>24, - (version>>16)&0xff, (version>>8)&0xff); -#else - return msnprintf(buffer, size, "mbedTLS/%s", MBEDTLS_VERSION_STRING); -#endif -} - -static CURLcode mbedtls_random(struct Curl_easy *data, - unsigned char *entropy, size_t length) -{ -#if defined(MBEDTLS_CTR_DRBG_C) - int ret = -1; - char errorbuf[128]; - mbedtls_entropy_context ctr_entropy; - mbedtls_ctr_drbg_context ctr_drbg; - mbedtls_entropy_init(&ctr_entropy); - mbedtls_ctr_drbg_init(&ctr_drbg); - - ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, - &ctr_entropy, NULL, 0); - - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedtls_ctr_drbg_seed returned (-0x%04X) %s", - -ret, errorbuf); - } - else { - ret = mbedtls_ctr_drbg_random(&ctr_drbg, entropy, length); - - if(ret) { - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "mbedtls_ctr_drbg_random returned (-0x%04X) %s", - -ret, errorbuf); - } - } - - mbedtls_ctr_drbg_free(&ctr_drbg); - mbedtls_entropy_free(&ctr_entropy); - - return ret == 0 ? CURLE_OK : CURLE_FAILED_INIT; -#elif defined(MBEDTLS_HAVEGE_C) - mbedtls_havege_state hs; - mbedtls_havege_init(&hs); - mbedtls_havege_random(&hs, entropy, length); - mbedtls_havege_free(&hs); - return CURLE_OK; -#else - return CURLE_NOT_BUILT_IN; -#endif -} - -static CURLcode -mbed_connect_common(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, - bool nonblocking, - bool *done) -{ - CURLcode retcode; - 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 */ - if(ssl_connection_complete == connssl->state) { - *done = TRUE; - return CURLE_OK; - } - - if(ssl_connect_1 == connssl->connecting_state) { - /* Find out how much more time we're allowed */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - retcode = mbed_connect_step1(data, conn, sockindex); - if(retcode) - return retcode; - } - - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { - - /* check allowed time left */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading - || connssl->connecting_state == ssl_connect_2_writing) { - - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state?sockfd:CURL_SOCKET_BAD; - - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking ? 0 : timeout_ms); - if(what < 0) { - /* fatal error */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - return CURLE_SSL_CONNECT_ERROR; - } - else if(0 == what) { - if(nonblocking) { - *done = FALSE; - return CURLE_OK; - } - else { - /* timeout */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - } - /* socket is readable or writable */ - } - - /* Run transaction, and return to the caller if it failed or if - * this connection is part of a multi handle and this loop would - * execute again. This permits the owner of a multi handle to - * abort a connection attempt before step2 has completed while - * ensuring that a client using select() or epoll() will always - * have a valid fdset to wait on. - */ - retcode = mbed_connect_step2(data, conn, sockindex); - if(retcode || (nonblocking && - (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) - return retcode; - - } /* repeat step2 until all transactions are done. */ - - if(ssl_connect_3 == connssl->connecting_state) { - retcode = mbed_connect_step3(data, conn, sockindex); - if(retcode) - return retcode; - } - - if(ssl_connect_done == connssl->connecting_state) { - connssl->state = ssl_connection_complete; - conn->recv[sockindex] = mbed_recv; - conn->send[sockindex] = mbed_send; - *done = TRUE; - } - else - *done = FALSE; - - /* Reset our connect state machine */ - connssl->connecting_state = ssl_connect_1; - - return CURLE_OK; -} - -static CURLcode mbedtls_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, bool *done) -{ - return mbed_connect_common(data, conn, sockindex, TRUE, done); -} - - -static CURLcode mbedtls_connect(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - CURLcode retcode; - bool done = FALSE; - - retcode = mbed_connect_common(data, conn, sockindex, FALSE, &done); - if(retcode) - return retcode; - - DEBUGASSERT(done); - - return CURLE_OK; -} - -/* - * return 0 error initializing SSL - * return 1 SSL initialized successfully - */ -static int mbedtls_init(void) -{ - return Curl_mbedtlsthreadlock_thread_setup(); -} - -static void mbedtls_cleanup(void) -{ - (void)Curl_mbedtlsthreadlock_thread_cleanup(); -} - -static bool mbedtls_data_pending(const struct connectdata *conn, - int sockindex) -{ - const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - return mbedtls_ssl_get_bytes_avail(&backend->ssl) != 0; -} - -static CURLcode mbedtls_sha256sum(const unsigned char *input, - size_t inputlen, - unsigned char *sha256sum, - size_t sha256len UNUSED_PARAM) -{ - /* TODO: explain this for different mbedtls 2.x vs 3 version */ - (void)sha256len; -#if MBEDTLS_VERSION_NUMBER < 0x02070000 - mbedtls_sha256(input, inputlen, sha256sum, 0); -#else - /* returns 0 on success, otherwise failure */ -#if MBEDTLS_VERSION_NUMBER >= 0x03000000 - if(mbedtls_sha256(input, inputlen, sha256sum, 0) != 0) -#else - if(mbedtls_sha256_ret(input, inputlen, sha256sum, 0) != 0) -#endif - return CURLE_BAD_FUNCTION_ARGUMENT; -#endif - return CURLE_OK; -} - -static void *mbedtls_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct ssl_backend_data *backend = connssl->backend; - (void)info; - DEBUGASSERT(backend); - return &backend->ssl; -} - -const struct Curl_ssl Curl_ssl_mbedtls = { - { CURLSSLBACKEND_MBEDTLS, "mbedtls" }, /* info */ - - SSLSUPP_CA_PATH | - SSLSUPP_CAINFO_BLOB | - SSLSUPP_PINNEDPUBKEY | - SSLSUPP_SSL_CTX, - - sizeof(struct ssl_backend_data), - - mbedtls_init, /* init */ - mbedtls_cleanup, /* cleanup */ - mbedtls_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - Curl_none_shutdown, /* shutdown */ - mbedtls_data_pending, /* data_pending */ - mbedtls_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - mbedtls_connect, /* connect */ - mbedtls_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - mbedtls_get_internals, /* get_internals */ - mbedtls_close, /* close_one */ - mbedtls_close_all, /* close_all */ - mbedtls_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - mbedtls_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#endif /* USE_MBEDTLS */ diff --git a/contrib/libs/curl/lib/vtls/nss.c b/contrib/libs/curl/lib/vtls/nss.c deleted file mode 100644 index cb0509ff5b..0000000000 --- a/contrib/libs/curl/lib/vtls/nss.c +++ /dev/null @@ -1,2541 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -/* - * Source file for all NSS-specific code for the TLS/SSL layer. No code - * but vtls.c should ever call or use these functions. - */ - -#include "curl_setup.h" - -#ifdef USE_NSS - -#include "urldata.h" -#include "sendf.h" -#include "formdata.h" /* for the boundary function */ -#include "url.h" /* for the ssl config check function */ -#include "connect.h" -#include "strcase.h" -#include "select.h" -#include "vtls.h" -#include "llist.h" -#include "multiif.h" -#include "curl_printf.h" -#include "nssg.h" -#include <nspr.h> -#include <nss.h> -#include <ssl.h> -#include <sslerr.h> -#include <secerr.h> -#include <secmod.h> -#include <sslproto.h> -#include <prtypes.h> -#include <pk11pub.h> -#include <prio.h> -#include <secitem.h> -#include <secport.h> -#include <certdb.h> -#include <base64.h> -#include <cert.h> -#include <prerror.h> -#include <keyhi.h> /* for SECKEY_DestroyPublicKey() */ -#include <private/pprio.h> /* for PR_ImportTCPSocket */ - -#define NSSVERNUM ((NSS_VMAJOR<<16)|(NSS_VMINOR<<8)|NSS_VPATCH) - -#if NSSVERNUM >= 0x030f00 /* 3.15.0 */ -#include <ocsp.h> -#endif - -#include "strcase.h" -#include "warnless.h" -#include "x509asn1.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -#define SSL_DIR "/etc/pki/nssdb" - -/* enough to fit the string "PEM Token #[0|1]" */ -#define SLOTSIZE 13 - -struct ssl_backend_data { - PRFileDesc *handle; - char *client_nickname; - struct Curl_easy *data; - struct Curl_llist obj_list; - PK11GenericObject *obj_clicert; -}; - -static PRLock *nss_initlock = NULL; -static PRLock *nss_crllock = NULL; -static PRLock *nss_findslot_lock = NULL; -static PRLock *nss_trustload_lock = NULL; -static struct Curl_llist nss_crl_list; -static NSSInitContext *nss_context = NULL; -static volatile int initialized = 0; - -/* type used to wrap pointers as list nodes */ -struct ptr_list_wrap { - void *ptr; - struct Curl_llist_element node; -}; - -struct cipher_s { - const char *name; - int num; -}; - -#define PK11_SETATTRS(_attr, _idx, _type, _val, _len) do { \ - CK_ATTRIBUTE *ptr = (_attr) + ((_idx)++); \ - ptr->type = (_type); \ - ptr->pValue = (_val); \ - ptr->ulValueLen = (_len); \ -} while(0) - -#define CERT_NewTempCertificate __CERT_NewTempCertificate - -#define NUM_OF_CIPHERS sizeof(cipherlist)/sizeof(cipherlist[0]) -static const struct cipher_s cipherlist[] = { - /* SSL2 cipher suites */ - {"rc4", SSL_EN_RC4_128_WITH_MD5}, - {"rc4-md5", SSL_EN_RC4_128_WITH_MD5}, - {"rc4export", SSL_EN_RC4_128_EXPORT40_WITH_MD5}, - {"rc2", SSL_EN_RC2_128_CBC_WITH_MD5}, - {"rc2export", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5}, - {"des", SSL_EN_DES_64_CBC_WITH_MD5}, - {"desede3", SSL_EN_DES_192_EDE3_CBC_WITH_MD5}, - /* SSL3/TLS cipher suites */ - {"rsa_rc4_128_md5", SSL_RSA_WITH_RC4_128_MD5}, - {"rsa_rc4_128_sha", SSL_RSA_WITH_RC4_128_SHA}, - {"rsa_3des_sha", SSL_RSA_WITH_3DES_EDE_CBC_SHA}, - {"rsa_des_sha", SSL_RSA_WITH_DES_CBC_SHA}, - {"rsa_rc4_40_md5", SSL_RSA_EXPORT_WITH_RC4_40_MD5}, - {"rsa_rc2_40_md5", SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5}, - {"rsa_null_md5", SSL_RSA_WITH_NULL_MD5}, - {"rsa_null_sha", SSL_RSA_WITH_NULL_SHA}, - {"fips_3des_sha", SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA}, - {"fips_des_sha", SSL_RSA_FIPS_WITH_DES_CBC_SHA}, - {"fortezza", SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA}, - {"fortezza_rc4_128_sha", SSL_FORTEZZA_DMS_WITH_RC4_128_SHA}, - {"fortezza_null", SSL_FORTEZZA_DMS_WITH_NULL_SHA}, - {"dhe_rsa_3des_sha", SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA}, - {"dhe_dss_3des_sha", SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA}, - {"dhe_rsa_des_sha", SSL_DHE_RSA_WITH_DES_CBC_SHA}, - {"dhe_dss_des_sha", SSL_DHE_DSS_WITH_DES_CBC_SHA}, - /* TLS 1.0: Exportable 56-bit Cipher Suites. */ - {"rsa_des_56_sha", TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA}, - {"rsa_rc4_56_sha", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA}, - /* Ephemeral DH with RC4 bulk encryption */ - {"dhe_dss_rc4_128_sha", TLS_DHE_DSS_WITH_RC4_128_SHA}, - /* AES ciphers. */ - {"dhe_dss_aes_128_cbc_sha", TLS_DHE_DSS_WITH_AES_128_CBC_SHA}, - {"dhe_dss_aes_256_cbc_sha", TLS_DHE_DSS_WITH_AES_256_CBC_SHA}, - {"dhe_rsa_aes_128_cbc_sha", TLS_DHE_RSA_WITH_AES_128_CBC_SHA}, - {"dhe_rsa_aes_256_cbc_sha", TLS_DHE_RSA_WITH_AES_256_CBC_SHA}, - {"rsa_aes_128_sha", TLS_RSA_WITH_AES_128_CBC_SHA}, - {"rsa_aes_256_sha", TLS_RSA_WITH_AES_256_CBC_SHA}, - /* ECC ciphers. */ - {"ecdh_ecdsa_null_sha", TLS_ECDH_ECDSA_WITH_NULL_SHA}, - {"ecdh_ecdsa_rc4_128_sha", TLS_ECDH_ECDSA_WITH_RC4_128_SHA}, - {"ecdh_ecdsa_3des_sha", TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdh_ecdsa_aes_128_sha", TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA}, - {"ecdh_ecdsa_aes_256_sha", TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA}, - {"ecdhe_ecdsa_null_sha", TLS_ECDHE_ECDSA_WITH_NULL_SHA}, - {"ecdhe_ecdsa_rc4_128_sha", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA}, - {"ecdhe_ecdsa_3des_sha", TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdhe_ecdsa_aes_128_sha", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA}, - {"ecdhe_ecdsa_aes_256_sha", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, - {"ecdh_rsa_null_sha", TLS_ECDH_RSA_WITH_NULL_SHA}, - {"ecdh_rsa_128_sha", TLS_ECDH_RSA_WITH_RC4_128_SHA}, - {"ecdh_rsa_3des_sha", TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdh_rsa_aes_128_sha", TLS_ECDH_RSA_WITH_AES_128_CBC_SHA}, - {"ecdh_rsa_aes_256_sha", TLS_ECDH_RSA_WITH_AES_256_CBC_SHA}, - {"ecdhe_rsa_null", TLS_ECDHE_RSA_WITH_NULL_SHA}, - {"ecdhe_rsa_rc4_128_sha", TLS_ECDHE_RSA_WITH_RC4_128_SHA}, - {"ecdhe_rsa_3des_sha", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA}, - {"ecdhe_rsa_aes_128_sha", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, - {"ecdhe_rsa_aes_256_sha", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, - {"ecdh_anon_null_sha", TLS_ECDH_anon_WITH_NULL_SHA}, - {"ecdh_anon_rc4_128sha", TLS_ECDH_anon_WITH_RC4_128_SHA}, - {"ecdh_anon_3des_sha", TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA}, - {"ecdh_anon_aes_128_sha", TLS_ECDH_anon_WITH_AES_128_CBC_SHA}, - {"ecdh_anon_aes_256_sha", TLS_ECDH_anon_WITH_AES_256_CBC_SHA}, -#ifdef TLS_RSA_WITH_NULL_SHA256 - /* new HMAC-SHA256 cipher suites specified in RFC */ - {"rsa_null_sha_256", TLS_RSA_WITH_NULL_SHA256}, - {"rsa_aes_128_cbc_sha_256", TLS_RSA_WITH_AES_128_CBC_SHA256}, - {"rsa_aes_256_cbc_sha_256", TLS_RSA_WITH_AES_256_CBC_SHA256}, - {"dhe_rsa_aes_128_cbc_sha_256", TLS_DHE_RSA_WITH_AES_128_CBC_SHA256}, - {"dhe_rsa_aes_256_cbc_sha_256", TLS_DHE_RSA_WITH_AES_256_CBC_SHA256}, - {"ecdhe_ecdsa_aes_128_cbc_sha_256", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}, - {"ecdhe_rsa_aes_128_cbc_sha_256", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256}, -#endif -#ifdef TLS_RSA_WITH_AES_128_GCM_SHA256 - /* AES GCM cipher suites in RFC 5288 and RFC 5289 */ - {"rsa_aes_128_gcm_sha_256", TLS_RSA_WITH_AES_128_GCM_SHA256}, - {"dhe_rsa_aes_128_gcm_sha_256", TLS_DHE_RSA_WITH_AES_128_GCM_SHA256}, - {"dhe_dss_aes_128_gcm_sha_256", TLS_DHE_DSS_WITH_AES_128_GCM_SHA256}, - {"ecdhe_ecdsa_aes_128_gcm_sha_256", TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}, - {"ecdh_ecdsa_aes_128_gcm_sha_256", TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256}, - {"ecdhe_rsa_aes_128_gcm_sha_256", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, - {"ecdh_rsa_aes_128_gcm_sha_256", TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256}, -#endif -#ifdef TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - /* cipher suites using SHA384 */ - {"rsa_aes_256_gcm_sha_384", TLS_RSA_WITH_AES_256_GCM_SHA384}, - {"dhe_rsa_aes_256_gcm_sha_384", TLS_DHE_RSA_WITH_AES_256_GCM_SHA384}, - {"dhe_dss_aes_256_gcm_sha_384", TLS_DHE_DSS_WITH_AES_256_GCM_SHA384}, - {"ecdhe_ecdsa_aes_256_sha_384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384}, - {"ecdhe_rsa_aes_256_sha_384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384}, - {"ecdhe_ecdsa_aes_256_gcm_sha_384", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384}, - {"ecdhe_rsa_aes_256_gcm_sha_384", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384}, -#endif -#ifdef TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 - /* chacha20-poly1305 cipher suites */ - {"ecdhe_rsa_chacha20_poly1305_sha_256", - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, - {"ecdhe_ecdsa_chacha20_poly1305_sha_256", - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256}, - {"dhe_rsa_chacha20_poly1305_sha_256", - TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, -#endif -#ifdef TLS_AES_256_GCM_SHA384 - {"aes_128_gcm_sha_256", TLS_AES_128_GCM_SHA256}, - {"aes_256_gcm_sha_384", TLS_AES_256_GCM_SHA384}, - {"chacha20_poly1305_sha_256", TLS_CHACHA20_POLY1305_SHA256}, -#endif -#ifdef TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 - /* AES CBC cipher suites in RFC 5246. Introduced in NSS release 3.20 */ - {"dhe_dss_aes_128_sha_256", TLS_DHE_DSS_WITH_AES_128_CBC_SHA256}, - {"dhe_dss_aes_256_sha_256", TLS_DHE_DSS_WITH_AES_256_CBC_SHA256}, -#endif -#ifdef TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA - /* Camellia cipher suites in RFC 4132/5932. - Introduced in NSS release 3.12 */ - {"dhe_rsa_camellia_128_sha", TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA}, - {"dhe_dss_camellia_128_sha", TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA}, - {"dhe_rsa_camellia_256_sha", TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA}, - {"dhe_dss_camellia_256_sha", TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA}, - {"rsa_camellia_128_sha", TLS_RSA_WITH_CAMELLIA_128_CBC_SHA}, - {"rsa_camellia_256_sha", TLS_RSA_WITH_CAMELLIA_256_CBC_SHA}, -#endif -#ifdef TLS_RSA_WITH_SEED_CBC_SHA - /* SEED cipher suite in RFC 4162. Introduced in NSS release 3.12.3 */ - {"rsa_seed_sha", TLS_RSA_WITH_SEED_CBC_SHA}, -#endif -}; - -#if defined(WIN32) -static const char *pem_library = "nsspem.dll"; -static const char *trust_library = "nssckbi.dll"; -#elif defined(__APPLE__) -static const char *pem_library = "libnsspem.dylib"; -static const char *trust_library = "libnssckbi.dylib"; -#else -static const char *pem_library = "libnsspem.so"; -static const char *trust_library = "libnssckbi.so"; -#endif - -static SECMODModule *pem_module = NULL; -static SECMODModule *trust_module = NULL; - -/* NSPR I/O layer we use to detect blocking direction during SSL handshake */ -static PRDescIdentity nspr_io_identity = PR_INVALID_IO_LAYER; -static PRIOMethods nspr_io_methods; - -static const char *nss_error_to_name(PRErrorCode code) -{ - const char *name = PR_ErrorToName(code); - if(name) - return name; - - return "unknown error"; -} - -static void nss_print_error_message(struct Curl_easy *data, PRUint32 err) -{ - failf(data, "%s", PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT)); -} - -static char *nss_sslver_to_name(PRUint16 nssver) -{ - switch(nssver) { - case SSL_LIBRARY_VERSION_2: - return strdup("SSLv2"); - case SSL_LIBRARY_VERSION_3_0: - return strdup("SSLv3"); - case SSL_LIBRARY_VERSION_TLS_1_0: - return strdup("TLSv1.0"); -#ifdef SSL_LIBRARY_VERSION_TLS_1_1 - case SSL_LIBRARY_VERSION_TLS_1_1: - return strdup("TLSv1.1"); -#endif -#ifdef SSL_LIBRARY_VERSION_TLS_1_2 - case SSL_LIBRARY_VERSION_TLS_1_2: - return strdup("TLSv1.2"); -#endif -#ifdef SSL_LIBRARY_VERSION_TLS_1_3 - case SSL_LIBRARY_VERSION_TLS_1_3: - return strdup("TLSv1.3"); -#endif - default: - return curl_maprintf("0x%04x", nssver); - } -} - -/* the longest cipher name this supports */ -#define MAX_CIPHER_LENGTH 128 - -static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc *model, - const char *cipher_list) -{ - unsigned int i; - const char *cipher; - - /* use accessors to avoid dynamic linking issues after an update of NSS */ - const PRUint16 num_implemented_ciphers = SSL_GetNumImplementedCiphers(); - const PRUint16 *implemented_ciphers = SSL_GetImplementedCiphers(); - if(!implemented_ciphers) - return SECFailure; - - /* First disable all ciphers. This uses a different max value in case - * NSS adds more ciphers later we don't want them available by - * accident - */ - for(i = 0; i < num_implemented_ciphers; i++) { - SSL_CipherPrefSet(model, implemented_ciphers[i], PR_FALSE); - } - - cipher = cipher_list; - - while(cipher && cipher[0]) { - const char *end; - char name[MAX_CIPHER_LENGTH + 1]; - size_t len; - bool found = FALSE; - while((*cipher) && (ISSPACE(*cipher))) - ++cipher; - - end = strpbrk(cipher, ":, "); - if(end) - len = end - cipher; - else - len = strlen(cipher); - - if(len > MAX_CIPHER_LENGTH) { - failf(data, "Bad cipher list"); - return SECFailure; - } - else if(len) { - memcpy(name, cipher, len); - name[len] = 0; - - for(i = 0; i<NUM_OF_CIPHERS; i++) { - if(strcasecompare(name, cipherlist[i].name)) { - /* Enable the selected cipher */ - if(SSL_CipherPrefSet(model, cipherlist[i].num, PR_TRUE) != - SECSuccess) { - failf(data, "cipher-suite not supported by NSS: %s", name); - return SECFailure; - } - found = TRUE; - break; - } - } - } - - if(!found && len) { - failf(data, "Unknown cipher: %s", name); - return SECFailure; - } - if(end) - cipher = ++end; - else - break; - } - - return SECSuccess; -} - -/* - * Return true if at least one cipher-suite is enabled. Used to determine - * if we need to call NSS_SetDomesticPolicy() to enable the default ciphers. - */ -static bool any_cipher_enabled(void) -{ - unsigned int i; - - for(i = 0; i<NUM_OF_CIPHERS; i++) { - PRInt32 policy = 0; - SSL_CipherPolicyGet(cipherlist[i].num, &policy); - if(policy) - return TRUE; - } - - return FALSE; -} - -/* - * Determine whether the nickname passed in is a filename that needs to - * be loaded as a PEM or a regular NSS nickname. - * - * returns 1 for a file - * returns 0 for not a file (NSS nickname) - */ -static int is_file(const char *filename) -{ - struct_stat st; - - if(!filename) - return 0; - - if(stat(filename, &st) == 0) - if(S_ISREG(st.st_mode) || S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode)) - return 1; - - return 0; -} - -/* Check if the given string is filename or nickname of a certificate. If the - * given string is recognized as filename, return NULL. If the given string is - * recognized as nickname, return a duplicated string. The returned string - * should be later deallocated using free(). If the OOM failure occurs, we - * return NULL, too. - */ -static char *dup_nickname(struct Curl_easy *data, const char *str) -{ - const char *n; - - if(!is_file(str)) - /* no such file exists, use the string as nickname */ - return strdup(str); - - /* search the first slash; we require at least one slash in a file name */ - n = strchr(str, '/'); - if(!n) { - infof(data, "WARNING: certificate file name \"%s\" handled as nickname; " - "please use \"./%s\" to force file name", str, str); - return strdup(str); - } - - /* we'll use the PEM reader to read the certificate from file */ - return NULL; -} - -/* Lock/unlock wrapper for PK11_FindSlotByName() to work around race condition - * in nssSlot_IsTokenPresent() causing spurious SEC_ERROR_NO_TOKEN. For more - * details, go to <https://bugzilla.mozilla.org/1297397>. - */ -static PK11SlotInfo* nss_find_slot_by_name(const char *slot_name) -{ - PK11SlotInfo *slot; - PR_Lock(nss_findslot_lock); - slot = PK11_FindSlotByName(slot_name); - PR_Unlock(nss_findslot_lock); - return slot; -} - -/* wrap 'ptr' as list node and tail-insert into 'list' */ -static CURLcode insert_wrapped_ptr(struct Curl_llist *list, void *ptr) -{ - struct ptr_list_wrap *wrap = malloc(sizeof(*wrap)); - if(!wrap) - return CURLE_OUT_OF_MEMORY; - - wrap->ptr = ptr; - Curl_llist_insert_next(list, list->tail, wrap, &wrap->node); - return CURLE_OK; -} - -/* Call PK11_CreateGenericObject() with the given obj_class and filename. If - * the call succeeds, append the object handle to the list of objects so that - * the object can be destroyed in nss_close(). */ -static CURLcode nss_create_object(struct ssl_connect_data *connssl, - CK_OBJECT_CLASS obj_class, - const char *filename, bool cacert) -{ - PK11SlotInfo *slot; - PK11GenericObject *obj; - CK_BBOOL cktrue = CK_TRUE; - CK_BBOOL ckfalse = CK_FALSE; - CK_ATTRIBUTE attrs[/* max count of attributes */ 4]; - int attr_cnt = 0; - CURLcode result = (cacert) - ? CURLE_SSL_CACERT_BADFILE - : CURLE_SSL_CERTPROBLEM; - - const int slot_id = (cacert) ? 0 : 1; - char *slot_name = aprintf("PEM Token #%d", slot_id); - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - if(!slot_name) - return CURLE_OUT_OF_MEMORY; - - slot = nss_find_slot_by_name(slot_name); - free(slot_name); - if(!slot) - return result; - - PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class)); - PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL)); - PK11_SETATTRS(attrs, attr_cnt, CKA_LABEL, (unsigned char *)filename, - (CK_ULONG)strlen(filename) + 1); - - if(CKO_CERTIFICATE == obj_class) { - CK_BBOOL *pval = (cacert) ? (&cktrue) : (&ckfalse); - PK11_SETATTRS(attrs, attr_cnt, CKA_TRUST, pval, sizeof(*pval)); - } - - /* PK11_CreateManagedGenericObject() was introduced in NSS 3.34 because - * PK11_DestroyGenericObject() does not release resources allocated by - * PK11_CreateGenericObject() early enough. */ - obj = -#ifdef HAVE_PK11_CREATEMANAGEDGENERICOBJECT - PK11_CreateManagedGenericObject -#else - PK11_CreateGenericObject -#endif - (slot, attrs, attr_cnt, PR_FALSE); - - PK11_FreeSlot(slot); - if(!obj) - return result; - - if(insert_wrapped_ptr(&backend->obj_list, obj) != CURLE_OK) { - PK11_DestroyGenericObject(obj); - return CURLE_OUT_OF_MEMORY; - } - - if(!cacert && CKO_CERTIFICATE == obj_class) - /* store reference to a client certificate */ - backend->obj_clicert = obj; - - return CURLE_OK; -} - -/* Destroy the NSS object whose handle is given by ptr. This function is - * a callback of Curl_llist_alloc() used by Curl_llist_destroy() to destroy - * NSS objects in nss_close() */ -static void nss_destroy_object(void *user, void *ptr) -{ - struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr; - PK11GenericObject *obj = (PK11GenericObject *) wrap->ptr; - (void) user; - PK11_DestroyGenericObject(obj); - free(wrap); -} - -/* same as nss_destroy_object() but for CRL items */ -static void nss_destroy_crl_item(void *user, void *ptr) -{ - struct ptr_list_wrap *wrap = (struct ptr_list_wrap *) ptr; - SECItem *crl_der = (SECItem *) wrap->ptr; - (void) user; - SECITEM_FreeItem(crl_der, PR_TRUE); - free(wrap); -} - -static CURLcode nss_load_cert(struct ssl_connect_data *ssl, - const char *filename, PRBool cacert) -{ - CURLcode result = (cacert) - ? CURLE_SSL_CACERT_BADFILE - : CURLE_SSL_CERTPROBLEM; - - /* libnsspem.so leaks memory if the requested file does not exist. For more - * details, go to <https://bugzilla.redhat.com/734760>. */ - if(is_file(filename)) - result = nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert); - - if(!result && !cacert) { - /* we have successfully loaded a client certificate */ - char *nickname = NULL; - char *n = strrchr(filename, '/'); - if(n) - n++; - - /* The following undocumented magic helps to avoid a SIGSEGV on call - * of PK11_ReadRawAttribute() from SelectClientCert() when using an - * immature version of libnsspem.so. For more details, go to - * <https://bugzilla.redhat.com/733685>. */ - nickname = aprintf("PEM Token #1:%s", n); - if(nickname) { - CERTCertificate *cert = PK11_FindCertFromNickname(nickname, NULL); - if(cert) - CERT_DestroyCertificate(cert); - - free(nickname); - } - } - - return result; -} - -/* add given CRL to cache if it is not already there */ -static CURLcode nss_cache_crl(SECItem *crl_der) -{ - CERTCertDBHandle *db = CERT_GetDefaultCertDB(); - CERTSignedCrl *crl = SEC_FindCrlByDERCert(db, crl_der, 0); - if(crl) { - /* CRL already cached */ - SEC_DestroyCrl(crl); - SECITEM_FreeItem(crl_der, PR_TRUE); - return CURLE_OK; - } - - /* acquire lock before call of CERT_CacheCRL() and accessing nss_crl_list */ - PR_Lock(nss_crllock); - - if(SECSuccess != CERT_CacheCRL(db, crl_der)) { - /* unable to cache CRL */ - SECITEM_FreeItem(crl_der, PR_TRUE); - PR_Unlock(nss_crllock); - return CURLE_SSL_CRL_BADFILE; - } - - /* store the CRL item so that we can free it in nss_cleanup() */ - if(insert_wrapped_ptr(&nss_crl_list, crl_der) != CURLE_OK) { - if(SECSuccess == CERT_UncacheCRL(db, crl_der)) - SECITEM_FreeItem(crl_der, PR_TRUE); - PR_Unlock(nss_crllock); - return CURLE_OUT_OF_MEMORY; - } - - /* we need to clear session cache, so that the CRL could take effect */ - SSL_ClearSessionCache(); - PR_Unlock(nss_crllock); - return CURLE_OK; -} - -static CURLcode nss_load_crl(const char *crlfilename) -{ - PRFileDesc *infile; - PRFileInfo info; - SECItem filedata = { 0, NULL, 0 }; - SECItem *crl_der = NULL; - char *body; - - infile = PR_Open(crlfilename, PR_RDONLY, 0); - if(!infile) - return CURLE_SSL_CRL_BADFILE; - - if(PR_SUCCESS != PR_GetOpenFileInfo(infile, &info)) - goto fail; - - if(!SECITEM_AllocItem(NULL, &filedata, info.size + /* zero ended */ 1)) - goto fail; - - if(info.size != PR_Read(infile, filedata.data, info.size)) - goto fail; - - crl_der = SECITEM_AllocItem(NULL, NULL, 0U); - if(!crl_der) - goto fail; - - /* place a trailing zero right after the visible data */ - body = (char *)filedata.data; - body[--filedata.len] = '\0'; - - body = strstr(body, "-----BEGIN"); - if(body) { - /* assume ASCII */ - char *trailer; - char *begin = PORT_Strchr(body, '\n'); - if(!begin) - begin = PORT_Strchr(body, '\r'); - if(!begin) - goto fail; - - trailer = strstr(++begin, "-----END"); - if(!trailer) - goto fail; - - /* retrieve DER from ASCII */ - *trailer = '\0'; - if(ATOB_ConvertAsciiToItem(crl_der, begin)) - goto fail; - - SECITEM_FreeItem(&filedata, PR_FALSE); - } - else - /* assume DER */ - *crl_der = filedata; - - PR_Close(infile); - return nss_cache_crl(crl_der); - -fail: - PR_Close(infile); - SECITEM_FreeItem(crl_der, PR_TRUE); - SECITEM_FreeItem(&filedata, PR_FALSE); - return CURLE_SSL_CRL_BADFILE; -} - -static CURLcode nss_load_key(struct Curl_easy *data, struct connectdata *conn, - int sockindex, char *key_file) -{ - PK11SlotInfo *slot, *tmp; - SECStatus status; - CURLcode result; - struct ssl_connect_data *ssl = conn->ssl; - - (void)sockindex; /* unused */ - - result = nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE); - if(result) { - PR_SetError(SEC_ERROR_BAD_KEY, 0); - return result; - } - - slot = nss_find_slot_by_name("PEM Token #1"); - if(!slot) - return CURLE_SSL_CERTPROBLEM; - - /* This will force the token to be seen as re-inserted */ - tmp = SECMOD_WaitForAnyTokenEvent(pem_module, 0, 0); - if(tmp) - PK11_FreeSlot(tmp); - if(!PK11_IsPresent(slot)) { - PK11_FreeSlot(slot); - return CURLE_SSL_CERTPROBLEM; - } - - status = PK11_Authenticate(slot, PR_TRUE, SSL_SET_OPTION(key_passwd)); - PK11_FreeSlot(slot); - - return (SECSuccess == status) ? CURLE_OK : CURLE_SSL_CERTPROBLEM; -} - -static int display_error(struct Curl_easy *data, PRInt32 err, - const char *filename) -{ - switch(err) { - case SEC_ERROR_BAD_PASSWORD: - failf(data, "Unable to load client key: Incorrect password"); - return 1; - case SEC_ERROR_UNKNOWN_CERT: - failf(data, "Unable to load certificate %s", filename); - return 1; - default: - break; - } - return 0; /* The caller will print a generic error */ -} - -static CURLcode cert_stuff(struct Curl_easy *data, struct connectdata *conn, - int sockindex, char *cert_file, char *key_file) -{ - CURLcode result; - - if(cert_file) { - result = nss_load_cert(&conn->ssl[sockindex], cert_file, PR_FALSE); - if(result) { - const PRErrorCode err = PR_GetError(); - if(!display_error(data, err, cert_file)) { - const char *err_name = nss_error_to_name(err); - failf(data, "unable to load client cert: %d (%s)", err, err_name); - } - - return result; - } - } - - if(key_file || (is_file(cert_file))) { - if(key_file) - result = nss_load_key(data, conn, sockindex, key_file); - else - /* In case the cert file also has the key */ - result = nss_load_key(data, conn, sockindex, cert_file); - if(result) { - const PRErrorCode err = PR_GetError(); - if(!display_error(data, err, key_file)) { - const char *err_name = nss_error_to_name(err); - failf(data, "unable to load client key: %d (%s)", err, err_name); - } - - return result; - } - } - - return CURLE_OK; -} - -static char *nss_get_password(PK11SlotInfo *slot, PRBool retry, void *arg) -{ - (void)slot; /* unused */ - - if(retry || !arg) - return NULL; - else - return (char *)PORT_Strdup((char *)arg); -} - -/* bypass the default SSL_AuthCertificate() hook in case we do not want to - * verify peer */ -static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig, - PRBool isServer) -{ - struct Curl_easy *data = (struct Curl_easy *)arg; - struct connectdata *conn = data->conn; - -#ifdef SSL_ENABLE_OCSP_STAPLING - if(SSL_CONN_CONFIG(verifystatus)) { - SECStatus cacheResult; - - const SECItemArray *csa = SSL_PeerStapledOCSPResponses(fd); - if(!csa) { - failf(data, "Invalid OCSP response"); - return SECFailure; - } - - if(csa->len == 0) { - failf(data, "No OCSP response received"); - return SECFailure; - } - - cacheResult = CERT_CacheOCSPResponseFromSideChannel( - CERT_GetDefaultCertDB(), SSL_PeerCertificate(fd), - PR_Now(), &csa->items[0], arg - ); - - if(cacheResult != SECSuccess) { - failf(data, "Invalid OCSP response"); - return cacheResult; - } - } -#endif - - if(!SSL_CONN_CONFIG(verifypeer)) { - infof(data, "skipping SSL peer certificate verification"); - return SECSuccess; - } - - return SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer); -} - -/** - * Inform the application that the handshake is complete. - */ -static void HandshakeCallback(PRFileDesc *sock, void *arg) -{ - struct Curl_easy *data = (struct Curl_easy *)arg; - struct connectdata *conn = data->conn; - unsigned int buflenmax = 50; - unsigned char buf[50]; - unsigned int buflen; - SSLNextProtoState state; - - if(!conn->bits.tls_enable_npn && !conn->bits.tls_enable_alpn) { - return; - } - - if(SSL_GetNextProto(sock, &state, buf, &buflen, buflenmax) == SECSuccess) { - - switch(state) { -#if NSSVERNUM >= 0x031a00 /* 3.26.0 */ - /* used by NSS internally to implement 0-RTT */ - case SSL_NEXT_PROTO_EARLY_VALUE: - /* fall through! */ -#endif - case SSL_NEXT_PROTO_NO_SUPPORT: - case SSL_NEXT_PROTO_NO_OVERLAP: - infof(data, VTLS_INFOF_NO_ALPN); - return; -#ifdef SSL_ENABLE_ALPN - case SSL_NEXT_PROTO_SELECTED: - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, buflen, buf); - break; -#endif - case SSL_NEXT_PROTO_NEGOTIATED: - infof(data, "NPN, server accepted to use %.*s", buflen, buf); - break; - } - -#ifdef USE_HTTP2 - if(buflen == ALPN_H2_LENGTH && - !memcmp(ALPN_H2, buf, ALPN_H2_LENGTH)) { - conn->negnpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(buflen == ALPN_HTTP_1_1_LENGTH && - !memcmp(ALPN_HTTP_1_1, buf, ALPN_HTTP_1_1_LENGTH)) { - conn->negnpn = CURL_HTTP_VERSION_1_1; - } - - /* This callback might get called when PR_Recv() is used within - * close_one() during a connection shutdown. At that point there might not - * be any "bundle" associated with the connection anymore. - */ - if(conn->bundle) - Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - } -} - -#if NSSVERNUM >= 0x030f04 /* 3.15.4 */ -static SECStatus CanFalseStartCallback(PRFileDesc *sock, void *client_data, - PRBool *canFalseStart) -{ - struct Curl_easy *data = (struct Curl_easy *)client_data; - - SSLChannelInfo channelInfo; - SSLCipherSuiteInfo cipherInfo; - - SECStatus rv; - PRBool negotiatedExtension; - - *canFalseStart = PR_FALSE; - - if(SSL_GetChannelInfo(sock, &channelInfo, sizeof(channelInfo)) != SECSuccess) - return SECFailure; - - if(SSL_GetCipherSuiteInfo(channelInfo.cipherSuite, &cipherInfo, - sizeof(cipherInfo)) != SECSuccess) - return SECFailure; - - /* Prevent version downgrade attacks from TLS 1.2, and avoid False Start for - * TLS 1.3 and later. See https://bugzilla.mozilla.org/show_bug.cgi?id=861310 - */ - if(channelInfo.protocolVersion != SSL_LIBRARY_VERSION_TLS_1_2) - goto end; - - /* Only allow ECDHE key exchange algorithm. - * See https://bugzilla.mozilla.org/show_bug.cgi?id=952863 */ - if(cipherInfo.keaType != ssl_kea_ecdh) - goto end; - - /* Prevent downgrade attacks on the symmetric cipher. We do not allow CBC - * mode due to BEAST, POODLE, and other attacks on the MAC-then-Encrypt - * design. See https://bugzilla.mozilla.org/show_bug.cgi?id=1109766 */ - if(cipherInfo.symCipher != ssl_calg_aes_gcm) - goto end; - - /* Enforce ALPN or NPN to do False Start, as an indicator of server - * compatibility. */ - rv = SSL_HandshakeNegotiatedExtension(sock, ssl_app_layer_protocol_xtn, - &negotiatedExtension); - if(rv != SECSuccess || !negotiatedExtension) { - rv = SSL_HandshakeNegotiatedExtension(sock, ssl_next_proto_nego_xtn, - &negotiatedExtension); - } - - if(rv != SECSuccess || !negotiatedExtension) - goto end; - - *canFalseStart = PR_TRUE; - - infof(data, "Trying TLS False Start"); - -end: - return SECSuccess; -} -#endif - -static void display_cert_info(struct Curl_easy *data, - CERTCertificate *cert) -{ - char *subject, *issuer, *common_name; - PRExplodedTime printableTime; - char timeString[256]; - PRTime notBefore, notAfter; - - subject = CERT_NameToAscii(&cert->subject); - issuer = CERT_NameToAscii(&cert->issuer); - common_name = CERT_GetCommonName(&cert->subject); - infof(data, "subject: %s", subject); - - CERT_GetCertTimes(cert, ¬Before, ¬After); - PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime); - PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); - infof(data, " start date: %s", timeString); - PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime); - PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); - infof(data, " expire date: %s", timeString); - infof(data, " common name: %s", common_name); - infof(data, " issuer: %s", issuer); - - PR_Free(subject); - PR_Free(issuer); - PR_Free(common_name); -} - -/* A number of certs that will never occur in a real server handshake */ -#define TOO_MANY_CERTS 300 - -static CURLcode display_conn_info(struct Curl_easy *data, PRFileDesc *sock) -{ - CURLcode result = CURLE_OK; - SSLChannelInfo channel; - SSLCipherSuiteInfo suite; - CERTCertificate *cert; - CERTCertificate *cert2; - CERTCertificate *cert3; - PRTime now; - - if(SSL_GetChannelInfo(sock, &channel, sizeof(channel)) == - SECSuccess && channel.length == sizeof(channel) && - channel.cipherSuite) { - if(SSL_GetCipherSuiteInfo(channel.cipherSuite, - &suite, sizeof(suite)) == SECSuccess) { - infof(data, "SSL connection using %s", suite.cipherSuiteName); - } - } - - cert = SSL_PeerCertificate(sock); - if(cert) { - infof(data, "Server certificate:"); - - if(!data->set.ssl.certinfo) { - display_cert_info(data, cert); - CERT_DestroyCertificate(cert); - } - else { - /* Count certificates in chain. */ - int i = 1; - now = PR_Now(); - if(!cert->isRoot) { - cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA); - while(cert2) { - i++; - if(i >= TOO_MANY_CERTS) { - CERT_DestroyCertificate(cert2); - failf(data, "certificate loop"); - return CURLE_SSL_CERTPROBLEM; - } - if(cert2->isRoot) { - CERT_DestroyCertificate(cert2); - break; - } - cert3 = CERT_FindCertIssuer(cert2, now, certUsageSSLCA); - CERT_DestroyCertificate(cert2); - cert2 = cert3; - } - } - - result = Curl_ssl_init_certinfo(data, i); - if(!result) { - for(i = 0; cert; cert = cert2) { - result = Curl_extract_certinfo(data, i++, (char *)cert->derCert.data, - (char *)cert->derCert.data + - cert->derCert.len); - if(result) - break; - - if(cert->isRoot) { - CERT_DestroyCertificate(cert); - break; - } - - cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA); - CERT_DestroyCertificate(cert); - } - } - } - } - - return result; -} - -static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) -{ - struct Curl_easy *data = (struct Curl_easy *)arg; - struct connectdata *conn = data->conn; - PRErrorCode err = PR_GetError(); - CERTCertificate *cert; - - /* remember the cert verification result */ - SSL_SET_OPTION_LVALUE(certverifyresult) = err; - - if(err == SSL_ERROR_BAD_CERT_DOMAIN && !SSL_CONN_CONFIG(verifyhost)) - /* we are asked not to verify the host name */ - return SECSuccess; - - /* print only info about the cert, the error is printed off the callback */ - cert = SSL_PeerCertificate(sock); - if(cert) { - infof(data, "Server certificate:"); - display_cert_info(data, cert); - CERT_DestroyCertificate(cert); - } - - return SECFailure; -} - -/** - * - * Check that the Peer certificate's issuer certificate matches the one found - * by issuer_nickname. This is not exactly the way OpenSSL and GNU TLS do the - * issuer check, so we provide comments that mimic the OpenSSL - * X509_check_issued function (in x509v3/v3_purp.c) - */ -static SECStatus check_issuer_cert(PRFileDesc *sock, - char *issuer_nickname) -{ - CERTCertificate *cert, *cert_issuer, *issuer; - SECStatus res = SECSuccess; - void *proto_win = NULL; - - cert = SSL_PeerCertificate(sock); - cert_issuer = CERT_FindCertIssuer(cert, PR_Now(), certUsageObjectSigner); - - proto_win = SSL_RevealPinArg(sock); - issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win); - - if((!cert_issuer) || (!issuer)) - res = SECFailure; - else if(SECITEM_CompareItem(&cert_issuer->derCert, - &issuer->derCert) != SECEqual) - res = SECFailure; - - CERT_DestroyCertificate(cert); - CERT_DestroyCertificate(issuer); - CERT_DestroyCertificate(cert_issuer); - return res; -} - -static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl, - const char *pinnedpubkey) -{ - CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - struct ssl_backend_data *backend = connssl->backend; - struct Curl_easy *data = NULL; - CERTCertificate *cert; - - DEBUGASSERT(backend); - data = backend->data; - - if(!pinnedpubkey) - /* no pinned public key specified */ - return CURLE_OK; - - /* get peer certificate */ - cert = SSL_PeerCertificate(backend->handle); - if(cert) { - /* extract public key from peer certificate */ - SECKEYPublicKey *pubkey = CERT_ExtractPublicKey(cert); - if(pubkey) { - /* encode the public key as DER */ - SECItem *cert_der = PK11_DEREncodePublicKey(pubkey); - if(cert_der) { - /* compare the public key with the pinned public key */ - result = Curl_pin_peer_pubkey(data, pinnedpubkey, cert_der->data, - cert_der->len); - SECITEM_FreeItem(cert_der, PR_TRUE); - } - SECKEY_DestroyPublicKey(pubkey); - } - CERT_DestroyCertificate(cert); - } - - /* report the resulting status */ - switch(result) { - case CURLE_OK: - infof(data, "pinned public key verified successfully"); - break; - case CURLE_SSL_PINNEDPUBKEYNOTMATCH: - failf(data, "failed to verify pinned public key"); - break; - default: - /* OOM, etc. */ - break; - } - - return result; -} - -/** - * - * Callback to pick the SSL client certificate. - */ -static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, - struct CERTDistNamesStr *caNames, - struct CERTCertificateStr **pRetCert, - struct SECKEYPrivateKeyStr **pRetKey) -{ - struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg; - struct ssl_backend_data *backend = connssl->backend; - struct Curl_easy *data = NULL; - const char *nickname = NULL; - static const char pem_slotname[] = "PEM Token #1"; - - DEBUGASSERT(backend); - - data = backend->data; - nickname = backend->client_nickname; - - if(backend->obj_clicert) { - /* use the cert/key provided by PEM reader */ - SECItem cert_der = { 0, NULL, 0 }; - void *proto_win = SSL_RevealPinArg(sock); - struct CERTCertificateStr *cert; - struct SECKEYPrivateKeyStr *key; - - PK11SlotInfo *slot = nss_find_slot_by_name(pem_slotname); - if(!slot) { - failf(data, "NSS: PK11 slot not found: %s", pem_slotname); - return SECFailure; - } - - if(PK11_ReadRawAttribute(PK11_TypeGeneric, backend->obj_clicert, CKA_VALUE, - &cert_der) != SECSuccess) { - failf(data, "NSS: CKA_VALUE not found in PK11 generic object"); - PK11_FreeSlot(slot); - return SECFailure; - } - - cert = PK11_FindCertFromDERCertItem(slot, &cert_der, proto_win); - SECITEM_FreeItem(&cert_der, PR_FALSE); - if(!cert) { - failf(data, "NSS: client certificate from file not found"); - PK11_FreeSlot(slot); - return SECFailure; - } - - key = PK11_FindPrivateKeyFromCert(slot, cert, NULL); - PK11_FreeSlot(slot); - if(!key) { - failf(data, "NSS: private key from file not found"); - CERT_DestroyCertificate(cert); - return SECFailure; - } - - infof(data, "NSS: client certificate from file"); - display_cert_info(data, cert); - - *pRetCert = cert; - *pRetKey = key; - return SECSuccess; - } - - /* use the default NSS hook */ - if(SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames, - pRetCert, pRetKey) - || !*pRetCert) { - - if(!nickname) - failf(data, "NSS: client certificate not found (nickname not " - "specified)"); - else - failf(data, "NSS: client certificate not found: %s", nickname); - - return SECFailure; - } - - /* get certificate nickname if any */ - nickname = (*pRetCert)->nickname; - if(!nickname) - nickname = "[unknown]"; - - if(!strncmp(nickname, pem_slotname, sizeof(pem_slotname) - 1U)) { - failf(data, "NSS: refusing previously loaded certificate from file: %s", - nickname); - return SECFailure; - } - - if(!*pRetKey) { - failf(data, "NSS: private key not found for certificate: %s", nickname); - return SECFailure; - } - - infof(data, "NSS: using client certificate: %s", nickname); - display_cert_info(data, *pRetCert); - return SECSuccess; -} - -/* update blocking direction in case of PR_WOULD_BLOCK_ERROR */ -static void nss_update_connecting_state(ssl_connect_state state, void *secret) -{ - struct ssl_connect_data *connssl = (struct ssl_connect_data *)secret; - if(PR_GetError() != PR_WOULD_BLOCK_ERROR) - /* an unrelated error is passing by */ - return; - - switch(connssl->connecting_state) { - case ssl_connect_2: - case ssl_connect_2_reading: - case ssl_connect_2_writing: - break; - default: - /* we are not called from an SSL handshake */ - return; - } - - /* update the state accordingly */ - connssl->connecting_state = state; -} - -/* recv() wrapper we use to detect blocking direction during SSL handshake */ -static PRInt32 nspr_io_recv(PRFileDesc *fd, void *buf, PRInt32 amount, - PRIntn flags, PRIntervalTime timeout) -{ - const PRRecvFN recv_fn = fd->lower->methods->recv; - const PRInt32 rv = recv_fn(fd->lower, buf, amount, flags, timeout); - if(rv < 0) - /* check for PR_WOULD_BLOCK_ERROR and update blocking direction */ - nss_update_connecting_state(ssl_connect_2_reading, fd->secret); - return rv; -} - -/* send() wrapper we use to detect blocking direction during SSL handshake */ -static PRInt32 nspr_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount, - PRIntn flags, PRIntervalTime timeout) -{ - const PRSendFN send_fn = fd->lower->methods->send; - const PRInt32 rv = send_fn(fd->lower, buf, amount, flags, timeout); - if(rv < 0) - /* check for PR_WOULD_BLOCK_ERROR and update blocking direction */ - nss_update_connecting_state(ssl_connect_2_writing, fd->secret); - return rv; -} - -/* close() wrapper to avoid assertion failure due to fd->secret != NULL */ -static PRStatus nspr_io_close(PRFileDesc *fd) -{ - const PRCloseFN close_fn = PR_GetDefaultIOMethods()->close; - fd->secret = NULL; - return close_fn(fd); -} - -/* load a PKCS #11 module */ -static CURLcode nss_load_module(SECMODModule **pmod, const char *library, - const char *name) -{ - char *config_string; - SECMODModule *module = *pmod; - if(module) - /* already loaded */ - return CURLE_OK; - - config_string = aprintf("library=%s name=%s", library, name); - if(!config_string) - return CURLE_OUT_OF_MEMORY; - - module = SECMOD_LoadUserModule(config_string, NULL, PR_FALSE); - free(config_string); - - if(module && module->loaded) { - /* loaded successfully */ - *pmod = module; - return CURLE_OK; - } - - if(module) - SECMOD_DestroyModule(module); - return CURLE_FAILED_INIT; -} - -/* unload a PKCS #11 module */ -static void nss_unload_module(SECMODModule **pmod) -{ - SECMODModule *module = *pmod; - if(!module) - /* not loaded */ - return; - - if(SECMOD_UnloadUserModule(module) != SECSuccess) - /* unload failed */ - return; - - SECMOD_DestroyModule(module); - *pmod = NULL; -} - -/* data might be NULL */ -static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir) -{ - NSSInitParameters initparams; - PRErrorCode err; - const char *err_name; - - if(nss_context) - return CURLE_OK; - - memset((void *) &initparams, '\0', sizeof(initparams)); - initparams.length = sizeof(initparams); - - if(cert_dir) { - char *certpath = aprintf("sql:%s", cert_dir); - if(!certpath) - return CURLE_OUT_OF_MEMORY; - - infof(data, "Initializing NSS with certpath: %s", certpath); - nss_context = NSS_InitContext(certpath, "", "", "", &initparams, - NSS_INIT_READONLY | NSS_INIT_PK11RELOAD); - free(certpath); - - if(nss_context) - return CURLE_OK; - - err = PR_GetError(); - err_name = nss_error_to_name(err); - infof(data, "Unable to initialize NSS database: %d (%s)", err, err_name); - } - - infof(data, "Initializing NSS with certpath: none"); - nss_context = NSS_InitContext("", "", "", "", &initparams, NSS_INIT_READONLY - | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN - | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD); - if(nss_context) - return CURLE_OK; - - err = PR_GetError(); - err_name = nss_error_to_name(err); - failf(data, "Unable to initialize NSS: %d (%s)", err, err_name); - return CURLE_SSL_CACERT_BADFILE; -} - -/* data might be NULL */ -static CURLcode nss_setup(struct Curl_easy *data) -{ - char *cert_dir; - struct_stat st; - CURLcode result; - - if(initialized) - return CURLE_OK; - - /* list of all CRL items we need to destroy in nss_cleanup() */ - Curl_llist_init(&nss_crl_list, nss_destroy_crl_item); - - /* First we check if $SSL_DIR points to a valid dir */ - cert_dir = getenv("SSL_DIR"); - if(cert_dir) { - if((stat(cert_dir, &st) != 0) || - (!S_ISDIR(st.st_mode))) { - cert_dir = NULL; - } - } - - /* Now we check if the default location is a valid dir */ - if(!cert_dir) { - if((stat(SSL_DIR, &st) == 0) && - (S_ISDIR(st.st_mode))) { - cert_dir = (char *)SSL_DIR; - } - } - - if(nspr_io_identity == PR_INVALID_IO_LAYER) { - /* allocate an identity for our own NSPR I/O layer */ - nspr_io_identity = PR_GetUniqueIdentity("libcurl"); - if(nspr_io_identity == PR_INVALID_IO_LAYER) - return CURLE_OUT_OF_MEMORY; - - /* the default methods just call down to the lower I/O layer */ - memcpy(&nspr_io_methods, PR_GetDefaultIOMethods(), - sizeof(nspr_io_methods)); - - /* override certain methods in the table by our wrappers */ - nspr_io_methods.recv = nspr_io_recv; - nspr_io_methods.send = nspr_io_send; - nspr_io_methods.close = nspr_io_close; - } - - result = nss_init_core(data, cert_dir); - if(result) - return result; - - if(!any_cipher_enabled()) - NSS_SetDomesticPolicy(); - - initialized = 1; - - return CURLE_OK; -} - -/** - * Global SSL init - * - * @retval 0 error initializing SSL - * @retval 1 SSL initialized successfully - */ -static int nss_init(void) -{ - /* curl_global_init() is not thread-safe so this test is ok */ - if(!nss_initlock) { - PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); - nss_initlock = PR_NewLock(); - nss_crllock = PR_NewLock(); - nss_findslot_lock = PR_NewLock(); - nss_trustload_lock = PR_NewLock(); - } - - /* We will actually initialize NSS later */ - - return 1; -} - -/* data might be NULL */ -CURLcode Curl_nss_force_init(struct Curl_easy *data) -{ - CURLcode result; - if(!nss_initlock) { - if(data) - failf(data, "unable to initialize NSS, curl_global_init() should have " - "been called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL"); - return CURLE_FAILED_INIT; - } - - PR_Lock(nss_initlock); - result = nss_setup(data); - PR_Unlock(nss_initlock); - - return result; -} - -/* Global cleanup */ -static void nss_cleanup(void) -{ - /* This function isn't required to be threadsafe and this is only done - * as a safety feature. - */ - PR_Lock(nss_initlock); - if(initialized) { - /* Free references to client certificates held in the SSL session cache. - * Omitting this hampers destruction of the security module owning - * the certificates. */ - SSL_ClearSessionCache(); - - nss_unload_module(&pem_module); - nss_unload_module(&trust_module); - NSS_ShutdownContext(nss_context); - nss_context = NULL; - } - - /* destroy all CRL items */ - Curl_llist_destroy(&nss_crl_list, NULL); - - PR_Unlock(nss_initlock); - - PR_DestroyLock(nss_initlock); - PR_DestroyLock(nss_crllock); - PR_DestroyLock(nss_findslot_lock); - PR_DestroyLock(nss_trustload_lock); - nss_initlock = NULL; - - initialized = 0; -} - -/* - * This function uses SSL_peek to determine connection status. - * - * Return codes: - * 1 means the connection is still in place - * 0 means the connection has been closed - * -1 means the connection status is unknown - */ -static int nss_check_cxn(struct connectdata *conn) -{ - struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET]; - struct ssl_backend_data *backend = connssl->backend; - int rc; - char buf; - - DEBUGASSERT(backend); - - rc = - PR_Recv(backend->handle, (void *)&buf, 1, PR_MSG_PEEK, - PR_SecondsToInterval(1)); - if(rc > 0) - return 1; /* connection still in place */ - - if(rc == 0) - return 0; /* connection has been closed */ - - return -1; /* connection status unknown */ -} - -static void close_one(struct ssl_connect_data *connssl) -{ - /* before the cleanup, check whether we are using a client certificate */ - struct ssl_backend_data *backend = connssl->backend; - bool client_cert = true; - - DEBUGASSERT(backend); - - client_cert = (backend->client_nickname != NULL) - || (backend->obj_clicert != NULL); - - if(backend->handle) { - char buf[32]; - /* Maybe the server has already sent a close notify alert. - Read it to avoid an RST on the TCP connection. */ - (void)PR_Recv(backend->handle, buf, (int)sizeof(buf), 0, - PR_INTERVAL_NO_WAIT); - } - - free(backend->client_nickname); - backend->client_nickname = NULL; - - /* destroy all NSS objects in order to avoid failure of NSS shutdown */ - Curl_llist_destroy(&backend->obj_list, NULL); - backend->obj_clicert = NULL; - - if(backend->handle) { - if(client_cert) - /* A server might require different authentication based on the - * particular path being requested by the client. To support this - * scenario, we must ensure that a connection will never reuse the - * authentication data from a previous connection. */ - SSL_InvalidateSession(backend->handle); - - PR_Close(backend->handle); - backend->handle = NULL; - } -} - -/* - * This function is called when an SSL connection is closed. - */ -static void nss_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; -#ifndef CURL_DISABLE_PROXY - struct ssl_connect_data *connssl_proxy = &conn->proxy_ssl[sockindex]; -#endif - struct ssl_backend_data *backend = connssl->backend; - (void)data; - - DEBUGASSERT(backend); -#ifndef CURL_DISABLE_PROXY - DEBUGASSERT(connssl_proxy->backend != NULL); -#endif - - if(backend->handle -#ifndef CURL_DISABLE_PROXY - || connssl_proxy->backend->handle -#endif - ) { - /* NSS closes the socket we previously handed to it, so we must mark it - as closed to avoid double close */ - fake_sclose(conn->sock[sockindex]); - conn->sock[sockindex] = CURL_SOCKET_BAD; - } - -#ifndef CURL_DISABLE_PROXY - if(backend->handle) - /* nss_close(connssl) will transitively close also - connssl_proxy->backend->handle if both are used. Clear it to avoid - a double close leading to crash. */ - connssl_proxy->backend->handle = NULL; - - close_one(connssl_proxy); -#endif - close_one(connssl); -} - -/* return true if NSS can provide error code (and possibly msg) for the - error */ -static bool is_nss_error(CURLcode err) -{ - switch(err) { - case CURLE_PEER_FAILED_VERIFICATION: - case CURLE_SSL_CERTPROBLEM: - case CURLE_SSL_CONNECT_ERROR: - case CURLE_SSL_ISSUER_ERROR: - return true; - - default: - return false; - } -} - -/* return true if the given error code is related to a client certificate */ -static bool is_cc_error(PRInt32 err) -{ - switch(err) { - case SSL_ERROR_BAD_CERT_ALERT: - case SSL_ERROR_EXPIRED_CERT_ALERT: - case SSL_ERROR_REVOKED_CERT_ALERT: - return true; - - default: - return false; - } -} - -static Curl_recv nss_recv; -static Curl_send nss_send; - -static CURLcode nss_load_ca_certificates(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ - const char *cafile = SSL_CONN_CONFIG(CAfile); - const char *capath = SSL_CONN_CONFIG(CApath); - bool use_trust_module; - CURLcode result = CURLE_OK; - - /* treat empty string as unset */ - if(cafile && !cafile[0]) - cafile = NULL; - if(capath && !capath[0]) - capath = NULL; - - infof(data, " CAfile: %s", cafile ? cafile : "none"); - infof(data, " CApath: %s", capath ? capath : "none"); - - /* load libnssckbi.so if no other trust roots were specified */ - use_trust_module = !cafile && !capath; - - PR_Lock(nss_trustload_lock); - if(use_trust_module && !trust_module) { - /* libnssckbi.so needed but not yet loaded --> load it! */ - result = nss_load_module(&trust_module, trust_library, "trust"); - infof(data, "%s %s", (result) ? "failed to load" : "loaded", - trust_library); - if(result == CURLE_FAILED_INIT) - /* If libnssckbi.so is not available (or fails to load), one can still - use CA certificates stored in NSS database. Ignore the failure. */ - result = CURLE_OK; - } - else if(!use_trust_module && trust_module) { - /* libnssckbi.so not needed but already loaded --> unload it! */ - infof(data, "unloading %s", trust_library); - nss_unload_module(&trust_module); - } - PR_Unlock(nss_trustload_lock); - - if(cafile) - result = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE); - - if(result) - return result; - - if(capath) { - struct_stat st; - if(stat(capath, &st) == -1) - return CURLE_SSL_CACERT_BADFILE; - - if(S_ISDIR(st.st_mode)) { - PRDirEntry *entry; - PRDir *dir = PR_OpenDir(capath); - if(!dir) - return CURLE_SSL_CACERT_BADFILE; - - while((entry = - PR_ReadDir(dir, (PRDirFlags)(PR_SKIP_BOTH | PR_SKIP_HIDDEN)))) { - char *fullpath = aprintf("%s/%s", capath, entry->name); - if(!fullpath) { - PR_CloseDir(dir); - return CURLE_OUT_OF_MEMORY; - } - - if(CURLE_OK != nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE)) - /* This is purposefully tolerant of errors so non-PEM files can - * be in the same directory */ - infof(data, "failed to load '%s' from CURLOPT_CAPATH", fullpath); - - free(fullpath); - } - - PR_CloseDir(dir); - } - else - infof(data, "WARNING: CURLOPT_CAPATH not a directory (%s)", capath); - } - - return CURLE_OK; -} - -static CURLcode nss_sslver_from_curl(PRUint16 *nssver, long version) -{ - switch(version) { - case CURL_SSLVERSION_SSLv2: - *nssver = SSL_LIBRARY_VERSION_2; - return CURLE_OK; - - case CURL_SSLVERSION_SSLv3: - return CURLE_NOT_BUILT_IN; - - case CURL_SSLVERSION_TLSv1_0: - *nssver = SSL_LIBRARY_VERSION_TLS_1_0; - return CURLE_OK; - - case CURL_SSLVERSION_TLSv1_1: -#ifdef SSL_LIBRARY_VERSION_TLS_1_1 - *nssver = SSL_LIBRARY_VERSION_TLS_1_1; - return CURLE_OK; -#else - return CURLE_SSL_CONNECT_ERROR; -#endif - - case CURL_SSLVERSION_TLSv1_2: -#ifdef SSL_LIBRARY_VERSION_TLS_1_2 - *nssver = SSL_LIBRARY_VERSION_TLS_1_2; - return CURLE_OK; -#else - return CURLE_SSL_CONNECT_ERROR; -#endif - - case CURL_SSLVERSION_TLSv1_3: -#ifdef SSL_LIBRARY_VERSION_TLS_1_3 - *nssver = SSL_LIBRARY_VERSION_TLS_1_3; - return CURLE_OK; -#else - return CURLE_SSL_CONNECT_ERROR; -#endif - - default: - return CURLE_SSL_CONNECT_ERROR; - } -} - -static CURLcode nss_init_sslver(SSLVersionRange *sslver, - struct Curl_easy *data, - struct connectdata *conn) -{ - CURLcode result; - const long min = SSL_CONN_CONFIG(version); - const long max = SSL_CONN_CONFIG(version_max); - SSLVersionRange vrange; - - switch(min) { - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_DEFAULT: - /* Bump our minimum TLS version if NSS has stricter requirements. */ - if(SSL_VersionRangeGetDefault(ssl_variant_stream, &vrange) != SECSuccess) - return CURLE_SSL_CONNECT_ERROR; - if(sslver->min < vrange.min) - sslver->min = vrange.min; - break; - default: - result = nss_sslver_from_curl(&sslver->min, min); - if(result) { - failf(data, "unsupported min version passed via CURLOPT_SSLVERSION"); - return result; - } - } - - switch(max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - break; - default: - result = nss_sslver_from_curl(&sslver->max, max >> 16); - if(result) { - failf(data, "unsupported max version passed via CURLOPT_SSLVERSION"); - return result; - } - } - - return CURLE_OK; -} - -static CURLcode nss_fail_connect(struct ssl_connect_data *connssl, - struct Curl_easy *data, - CURLcode curlerr) -{ - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - if(is_nss_error(curlerr)) { - /* read NSPR error code */ - PRErrorCode err = PR_GetError(); - if(is_cc_error(err)) - curlerr = CURLE_SSL_CERTPROBLEM; - - /* print the error number and error string */ - infof(data, "NSS error %d (%s)", err, nss_error_to_name(err)); - - /* print a human-readable message describing the error if available */ - nss_print_error_message(data, err); - } - - /* cleanup on connection failure */ - Curl_llist_destroy(&backend->obj_list, NULL); - - return curlerr; -} - -/* Switch the SSL socket into blocking or non-blocking mode. */ -static CURLcode nss_set_blocking(struct ssl_connect_data *connssl, - struct Curl_easy *data, - bool blocking) -{ - PRSocketOptionData sock_opt; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - sock_opt.option = PR_SockOpt_Nonblocking; - sock_opt.value.non_blocking = !blocking; - - if(PR_SetSocketOption(backend->handle, &sock_opt) != PR_SUCCESS) - return nss_fail_connect(connssl, data, CURLE_SSL_CONNECT_ERROR); - - return CURLE_OK; -} - -static CURLcode nss_setup_connect(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - PRFileDesc *model = NULL; - PRFileDesc *nspr_io = NULL; - PRFileDesc *nspr_io_stub = NULL; - PRBool ssl_no_cache; - PRBool ssl_cbc_random_iv; - curl_socket_t sockfd = conn->sock[sockindex]; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - CURLcode result; - bool second_layer = FALSE; - SSLVersionRange sslver_supported; - SSLVersionRange sslver = { - SSL_LIBRARY_VERSION_TLS_1_0, /* min */ -#ifdef SSL_LIBRARY_VERSION_TLS_1_3 - SSL_LIBRARY_VERSION_TLS_1_3 /* max */ -#elif defined SSL_LIBRARY_VERSION_TLS_1_2 - SSL_LIBRARY_VERSION_TLS_1_2 -#elif defined SSL_LIBRARY_VERSION_TLS_1_1 - SSL_LIBRARY_VERSION_TLS_1_1 -#else - SSL_LIBRARY_VERSION_TLS_1_0 -#endif - }; - char *snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - - DEBUGASSERT(backend); - - backend->data = data; - - /* list of all NSS objects we need to destroy in nss_do_close() */ - Curl_llist_init(&backend->obj_list, nss_destroy_object); - - PR_Lock(nss_initlock); - result = nss_setup(data); - if(result) { - PR_Unlock(nss_initlock); - goto error; - } - - PK11_SetPasswordFunc(nss_get_password); - - result = nss_load_module(&pem_module, pem_library, "PEM"); - PR_Unlock(nss_initlock); - if(result == CURLE_FAILED_INIT) - infof(data, "WARNING: failed to load NSS PEM library %s. Using " - "OpenSSL PEM certificates will not work.", pem_library); - else if(result) - goto error; - - result = CURLE_SSL_CONNECT_ERROR; - - model = PR_NewTCPSocket(); - if(!model) - goto error; - model = SSL_ImportFD(NULL, model); - - if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess) - goto error; - if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess) - goto error; - if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess) - goto error; - - /* do not use SSL cache if disabled or we are not going to verify peer */ - ssl_no_cache = (SSL_SET_OPTION(primary.sessionid) - && SSL_CONN_CONFIG(verifypeer)) ? PR_FALSE : PR_TRUE; - if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess) - goto error; - - /* enable/disable the requested SSL version(s) */ - if(nss_init_sslver(&sslver, data, conn) != CURLE_OK) - goto error; - if(SSL_VersionRangeGetSupported(ssl_variant_stream, - &sslver_supported) != SECSuccess) - goto error; - if(sslver_supported.max < sslver.max && sslver_supported.max >= sslver.min) { - char *sslver_req_str, *sslver_supp_str; - sslver_req_str = nss_sslver_to_name(sslver.max); - sslver_supp_str = nss_sslver_to_name(sslver_supported.max); - if(sslver_req_str && sslver_supp_str) - infof(data, "Falling back from %s to max supported SSL version (%s)", - sslver_req_str, sslver_supp_str); - free(sslver_req_str); - free(sslver_supp_str); - sslver.max = sslver_supported.max; - } - if(SSL_VersionRangeSet(model, &sslver) != SECSuccess) - goto error; - - ssl_cbc_random_iv = !SSL_SET_OPTION(enable_beast); -#ifdef SSL_CBC_RANDOM_IV - /* unless the user explicitly asks to allow the protocol vulnerability, we - use the work-around */ - if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess) - infof(data, "WARNING: failed to set SSL_CBC_RANDOM_IV = %d", - ssl_cbc_random_iv); -#else - if(ssl_cbc_random_iv) - infof(data, "WARNING: support for SSL_CBC_RANDOM_IV not compiled in"); -#endif - - if(SSL_CONN_CONFIG(cipher_list)) { - if(set_ciphers(data, model, SSL_CONN_CONFIG(cipher_list)) != SECSuccess) { - result = CURLE_SSL_CIPHER; - goto error; - } - } - - if(!SSL_CONN_CONFIG(verifypeer) && SSL_CONN_CONFIG(verifyhost)) - infof(data, "WARNING: ignoring value of ssl.verifyhost"); - - /* bypass the default SSL_AuthCertificate() hook in case we do not want to - * verify peer */ - if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, data) != SECSuccess) - goto error; - - /* not checked yet */ - SSL_SET_OPTION_LVALUE(certverifyresult) = 0; - - if(SSL_BadCertHook(model, BadCertHandler, data) != SECSuccess) - goto error; - - if(SSL_HandshakeCallback(model, HandshakeCallback, data) != SECSuccess) - goto error; - - { - const CURLcode rv = nss_load_ca_certificates(data, conn, sockindex); - if((rv == CURLE_SSL_CACERT_BADFILE) && !SSL_CONN_CONFIG(verifypeer)) - /* not a fatal error because we are not going to verify the peer */ - infof(data, "WARNING: CA certificates failed to load"); - else if(rv) { - result = rv; - goto error; - } - } - - if(SSL_SET_OPTION(primary.CRLfile)) { - const CURLcode rv = nss_load_crl(SSL_SET_OPTION(primary.CRLfile)); - if(rv) { - result = rv; - goto error; - } - infof(data, " CRLfile: %s", SSL_SET_OPTION(primary.CRLfile)); - } - - if(SSL_SET_OPTION(primary.clientcert)) { - char *nickname = dup_nickname(data, SSL_SET_OPTION(primary.clientcert)); - if(nickname) { - /* we are not going to use libnsspem.so to read the client cert */ - backend->obj_clicert = NULL; - } - else { - CURLcode rv = cert_stuff(data, conn, sockindex, - SSL_SET_OPTION(primary.clientcert), - SSL_SET_OPTION(key)); - if(rv) { - /* failf() is already done in cert_stuff() */ - result = rv; - goto error; - } - } - - /* store the nickname for SelectClientCert() called during handshake */ - backend->client_nickname = nickname; - } - else - backend->client_nickname = NULL; - - if(SSL_GetClientAuthDataHook(model, SelectClientCert, - (void *)connssl) != SECSuccess) { - result = CURLE_SSL_CERTPROBLEM; - goto error; - } - -#ifndef CURL_DISABLE_PROXY - if(conn->proxy_ssl[sockindex].use) { - struct ssl_backend_data *proxy_backend; - proxy_backend = conn->proxy_ssl[sockindex].backend; - DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state); - DEBUGASSERT(proxy_backend); - DEBUGASSERT(proxy_backend->handle); - nspr_io = proxy_backend->handle; - second_layer = TRUE; - } -#endif - else { - /* wrap OS file descriptor by NSPR's file descriptor abstraction */ - nspr_io = PR_ImportTCPSocket(sockfd); - if(!nspr_io) - goto error; - } - - /* create our own NSPR I/O layer */ - nspr_io_stub = PR_CreateIOLayerStub(nspr_io_identity, &nspr_io_methods); - if(!nspr_io_stub) { - if(!second_layer) - PR_Close(nspr_io); - goto error; - } - - /* make the per-connection data accessible from NSPR I/O callbacks */ - nspr_io_stub->secret = (void *)connssl; - - /* push our new layer to the NSPR I/O stack */ - if(PR_PushIOLayer(nspr_io, PR_TOP_IO_LAYER, nspr_io_stub) != PR_SUCCESS) { - if(!second_layer) - PR_Close(nspr_io); - PR_Close(nspr_io_stub); - goto error; - } - - /* import our model socket onto the current I/O stack */ - backend->handle = SSL_ImportFD(model, nspr_io); - if(!backend->handle) { - if(!second_layer) - PR_Close(nspr_io); - goto error; - } - - PR_Close(model); /* We don't need this any more */ - model = NULL; - - /* This is the password associated with the cert that we're using */ - if(SSL_SET_OPTION(key_passwd)) { - SSL_SetPKCS11PinArg(backend->handle, SSL_SET_OPTION(key_passwd)); - } - -#ifdef SSL_ENABLE_OCSP_STAPLING - if(SSL_CONN_CONFIG(verifystatus)) { - if(SSL_OptionSet(backend->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE) - != SECSuccess) - goto error; - } -#endif - -#ifdef SSL_ENABLE_NPN - if(SSL_OptionSet(backend->handle, SSL_ENABLE_NPN, conn->bits.tls_enable_npn - ? PR_TRUE : PR_FALSE) != SECSuccess) - goto error; -#endif - -#ifdef SSL_ENABLE_ALPN - if(SSL_OptionSet(backend->handle, SSL_ENABLE_ALPN, conn->bits.tls_enable_alpn - ? PR_TRUE : PR_FALSE) != SECSuccess) - goto error; -#endif - -#if NSSVERNUM >= 0x030f04 /* 3.15.4 */ - if(data->set.ssl.falsestart) { - if(SSL_OptionSet(backend->handle, SSL_ENABLE_FALSE_START, PR_TRUE) - != SECSuccess) - goto error; - - if(SSL_SetCanFalseStartCallback(backend->handle, CanFalseStartCallback, - data) != SECSuccess) - goto error; - } -#endif - -#if defined(SSL_ENABLE_NPN) || defined(SSL_ENABLE_ALPN) - if(conn->bits.tls_enable_npn || 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) -#endif - ) { - protocols[cur++] = ALPN_H2_LENGTH; - memcpy(&protocols[cur], ALPN_H2, ALPN_H2_LENGTH); - cur += ALPN_H2_LENGTH; - } -#endif - protocols[cur++] = ALPN_HTTP_1_1_LENGTH; - memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); - cur += ALPN_HTTP_1_1_LENGTH; - - if(SSL_SetNextProtoNego(backend->handle, protocols, cur) != SECSuccess) - goto error; - } -#endif - - - /* Force handshake on next I/O */ - if(SSL_ResetHandshake(backend->handle, /* asServer */ PR_FALSE) - != SECSuccess) - goto error; - - /* propagate hostname to the TLS layer */ - if(SSL_SetURL(backend->handle, snihost) != SECSuccess) - goto error; - - /* prevent NSS from re-using the session for a different hostname */ - if(SSL_SetSockPeerID(backend->handle, snihost) != SECSuccess) - goto error; - - return CURLE_OK; - -error: - if(model) - PR_Close(model); - - return nss_fail_connect(connssl, data, result); -} - -static CURLcode nss_do_connect(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - CURLcode result = CURLE_SSL_CONNECT_ERROR; - PRUint32 timeout; - - /* check timeout situation */ - const timediff_t time_left = Curl_timeleft(data, NULL, TRUE); - if(time_left < 0) { - failf(data, "timed out before SSL handshake"); - result = CURLE_OPERATION_TIMEDOUT; - goto error; - } - - DEBUGASSERT(backend); - - /* Force the handshake now */ - timeout = PR_MillisecondsToInterval((PRUint32) time_left); - if(SSL_ForceHandshakeWithTimeout(backend->handle, timeout) != SECSuccess) { - if(PR_GetError() == PR_WOULD_BLOCK_ERROR) - /* blocking direction is updated by nss_update_connecting_state() */ - return CURLE_AGAIN; - else if(SSL_SET_OPTION(certverifyresult) == SSL_ERROR_BAD_CERT_DOMAIN) - result = CURLE_PEER_FAILED_VERIFICATION; - else if(SSL_SET_OPTION(certverifyresult) != 0) - result = CURLE_PEER_FAILED_VERIFICATION; - goto error; - } - - result = display_conn_info(data, backend->handle); - if(result) - goto error; - - if(SSL_CONN_CONFIG(issuercert)) { - SECStatus ret = SECFailure; - char *nickname = dup_nickname(data, SSL_CONN_CONFIG(issuercert)); - if(nickname) { - /* we support only nicknames in case of issuercert for now */ - ret = check_issuer_cert(backend->handle, nickname); - free(nickname); - } - - if(SECFailure == ret) { - infof(data, "SSL certificate issuer check failed"); - result = CURLE_SSL_ISSUER_ERROR; - goto error; - } - else { - infof(data, "SSL certificate issuer check ok"); - } - } - - result = cmp_peer_pubkey(connssl, SSL_PINNED_PUB_KEY()); - if(result) - /* status already printed */ - goto error; - - return CURLE_OK; - -error: - return nss_fail_connect(connssl, data, result); -} - -static CURLcode nss_connect_common(struct Curl_easy *data, - struct connectdata *conn, int sockindex, - bool *done) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - const bool blocking = (done == NULL); - CURLcode result; - - if(connssl->state == ssl_connection_complete) { - if(!blocking) - *done = TRUE; - return CURLE_OK; - } - - if(connssl->connecting_state == ssl_connect_1) { - result = nss_setup_connect(data, conn, sockindex); - if(result) - /* we do not expect CURLE_AGAIN from nss_setup_connect() */ - return result; - - connssl->connecting_state = ssl_connect_2; - } - - /* enable/disable blocking mode before handshake */ - result = nss_set_blocking(connssl, data, blocking); - if(result) - return result; - - result = nss_do_connect(data, conn, sockindex); - switch(result) { - case CURLE_OK: - break; - case CURLE_AGAIN: - /* CURLE_AGAIN in non-blocking mode is not an error */ - if(!blocking) - return CURLE_OK; - else - return result; - default: - return result; - } - - if(blocking) { - /* in blocking mode, set NSS non-blocking mode _after_ SSL handshake */ - result = nss_set_blocking(connssl, data, /* blocking */ FALSE); - if(result) - return result; - } - else - /* signal completed SSL handshake */ - *done = TRUE; - - connssl->state = ssl_connection_complete; - conn->recv[sockindex] = nss_recv; - conn->send[sockindex] = nss_send; - - /* ssl_connect_done is never used outside, go back to the initial state */ - connssl->connecting_state = ssl_connect_1; - - return CURLE_OK; -} - -static CURLcode nss_connect(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - return nss_connect_common(data, conn, sockindex, /* blocking */ NULL); -} - -static CURLcode nss_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, bool *done) -{ - return nss_connect_common(data, conn, sockindex, done); -} - -static ssize_t nss_send(struct Curl_easy *data, /* transfer */ - int sockindex, /* socketindex */ - const void *mem, /* send this data */ - size_t len, /* amount to write */ - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - ssize_t rc; - - DEBUGASSERT(backend); - - /* The SelectClientCert() hook uses this for infof() and failf() but the - handle stored in nss_setup_connect() could have already been freed. */ - backend->data = data; - - rc = PR_Send(backend->handle, mem, (int)len, 0, PR_INTERVAL_NO_WAIT); - if(rc < 0) { - PRInt32 err = PR_GetError(); - if(err == PR_WOULD_BLOCK_ERROR) - *curlcode = CURLE_AGAIN; - else { - /* print the error number and error string */ - const char *err_name = nss_error_to_name(err); - infof(data, "SSL write: error %d (%s)", err, err_name); - - /* print a human-readable message describing the error if available */ - nss_print_error_message(data, err); - - *curlcode = (is_cc_error(err)) - ? CURLE_SSL_CERTPROBLEM - : CURLE_SEND_ERROR; - } - - return -1; - } - - return rc; /* number of bytes */ -} - -static ssize_t nss_recv(struct Curl_easy *data, /* transfer */ - int sockindex, /* socketindex */ - char *buf, /* store read data here */ - size_t buffersize, /* max amount to read */ - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - ssize_t nread; - - DEBUGASSERT(backend); - - /* The SelectClientCert() hook uses this for infof() and failf() but the - handle stored in nss_setup_connect() could have already been freed. */ - backend->data = data; - - nread = PR_Recv(backend->handle, buf, (int)buffersize, 0, - PR_INTERVAL_NO_WAIT); - if(nread < 0) { - /* failed SSL read */ - PRInt32 err = PR_GetError(); - - if(err == PR_WOULD_BLOCK_ERROR) - *curlcode = CURLE_AGAIN; - else { - /* print the error number and error string */ - const char *err_name = nss_error_to_name(err); - infof(data, "SSL read: errno %d (%s)", err, err_name); - - /* print a human-readable message describing the error if available */ - nss_print_error_message(data, err); - - *curlcode = (is_cc_error(err)) - ? CURLE_SSL_CERTPROBLEM - : CURLE_RECV_ERROR; - } - - return -1; - } - - return nread; -} - -static size_t nss_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "NSS/%s", NSS_GetVersion()); -} - -/* data might be NULL */ -static int Curl_nss_seed(struct Curl_easy *data) -{ - /* make sure that NSS is initialized */ - return !!Curl_nss_force_init(data); -} - -/* data might be NULL */ -static CURLcode nss_random(struct Curl_easy *data, - unsigned char *entropy, - size_t length) -{ - Curl_nss_seed(data); /* Initiate the seed if not already done */ - - if(SECSuccess != PK11_GenerateRandom(entropy, curlx_uztosi(length))) - /* signal a failure */ - return CURLE_FAILED_INIT; - - return CURLE_OK; -} - -static CURLcode nss_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, - unsigned char *sha256sum, /* output */ - size_t sha256len) -{ - PK11Context *SHA256pw = PK11_CreateDigestContext(SEC_OID_SHA256); - unsigned int SHA256out; - - if(!SHA256pw) - return CURLE_NOT_BUILT_IN; - - PK11_DigestOp(SHA256pw, tmp, curlx_uztoui(tmplen)); - PK11_DigestFinal(SHA256pw, sha256sum, &SHA256out, curlx_uztoui(sha256len)); - PK11_DestroyContext(SHA256pw, PR_TRUE); - - return CURLE_OK; -} - -static bool nss_cert_status_request(void) -{ -#ifdef SSL_ENABLE_OCSP_STAPLING - return TRUE; -#else - return FALSE; -#endif -} - -static bool nss_false_start(void) -{ -#if NSSVERNUM >= 0x030f04 /* 3.15.4 */ - return TRUE; -#else - return FALSE; -#endif -} - -static void *nss_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct ssl_backend_data *backend = connssl->backend; - (void)info; - DEBUGASSERT(backend); - return backend->handle; -} - -const struct Curl_ssl Curl_ssl_nss = { - { CURLSSLBACKEND_NSS, "nss" }, /* info */ - - SSLSUPP_CA_PATH | - SSLSUPP_CERTINFO | - SSLSUPP_PINNEDPUBKEY | - SSLSUPP_HTTPS_PROXY, - - sizeof(struct ssl_backend_data), - - nss_init, /* init */ - nss_cleanup, /* cleanup */ - nss_version, /* version */ - nss_check_cxn, /* check_cxn */ - /* NSS has no shutdown function provided and thus always fail */ - Curl_none_shutdown, /* shutdown */ - Curl_none_data_pending, /* data_pending */ - nss_random, /* random */ - nss_cert_status_request, /* cert_status_request */ - nss_connect, /* connect */ - nss_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - nss_get_internals, /* get_internals */ - nss_close, /* close_one */ - Curl_none_close_all, /* close_all */ - /* NSS has its own session ID cache */ - Curl_none_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - nss_false_start, /* false_start */ - nss_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#endif /* USE_NSS */ diff --git a/contrib/libs/curl/lib/vtls/schannel.c b/contrib/libs/curl/lib/vtls/schannel.c deleted file mode 100644 index dfec66d51f..0000000000 --- a/contrib/libs/curl/lib/vtls/schannel.c +++ /dev/null @@ -1,2488 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de> - * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -/* - * Source file for all Schannel-specific code for the TLS/SSL layer. No code - * but vtls.c should ever call or use these functions. - */ - -#include "curl_setup.h" - -#ifdef USE_SCHANNEL - -#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS - -#ifndef USE_WINDOWS_SSPI -# error "Can't compile SCHANNEL support without SSPI." -#endif - -#include "schannel.h" -#include "vtls.h" -#include "strcase.h" -#include "sendf.h" -#include "connect.h" /* for the connect timeout */ -#include "strerror.h" -#include "select.h" /* for the socket readiness */ -#include "inet_pton.h" /* for IP addr SNI check */ -#include "curl_multibyte.h" -#include "warnless.h" -#include "x509asn1.h" -#include "curl_printf.h" -#include "multiif.h" -#include "version_win32.h" - -/* The last #include file should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* ALPN requires version 8.1 of the Windows SDK, which was - shipped with Visual Studio 2013, aka _MSC_VER 1800: - - https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx -*/ -#if defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_) -# define HAS_ALPN 1 -#endif - -#ifndef UNISP_NAME_A -#define UNISP_NAME_A "Microsoft Unified Security Protocol Provider" -#endif - -#ifndef UNISP_NAME_W -#define UNISP_NAME_W L"Microsoft Unified Security Protocol Provider" -#endif - -#ifndef UNISP_NAME -#ifdef UNICODE -#define UNISP_NAME UNISP_NAME_W -#else -#define UNISP_NAME UNISP_NAME_A -#endif -#endif - -#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX) -#define HAS_CLIENT_CERT_PATH -#endif - -#ifdef HAS_CLIENT_CERT_PATH -#ifdef UNICODE -#define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_W -#else -#define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_A -#endif -#endif - -#ifndef SP_PROT_SSL2_CLIENT -#define SP_PROT_SSL2_CLIENT 0x00000008 -#endif - -#ifndef SP_PROT_SSL3_CLIENT -#define SP_PROT_SSL3_CLIENT 0x00000008 -#endif - -#ifndef SP_PROT_TLS1_CLIENT -#define SP_PROT_TLS1_CLIENT 0x00000080 -#endif - -#ifndef SP_PROT_TLS1_0_CLIENT -#define SP_PROT_TLS1_0_CLIENT SP_PROT_TLS1_CLIENT -#endif - -#ifndef SP_PROT_TLS1_1_CLIENT -#define SP_PROT_TLS1_1_CLIENT 0x00000200 -#endif - -#ifndef SP_PROT_TLS1_2_CLIENT -#define SP_PROT_TLS1_2_CLIENT 0x00000800 -#endif - -#ifndef SCH_USE_STRONG_CRYPTO -#define SCH_USE_STRONG_CRYPTO 0x00400000 -#endif - -#ifndef SECBUFFER_ALERT -#define SECBUFFER_ALERT 17 -#endif - -/* Both schannel buffer sizes must be > 0 */ -#define CURL_SCHANNEL_BUFFER_INIT_SIZE 4096 -#define CURL_SCHANNEL_BUFFER_FREE_SIZE 1024 - -#define CERT_THUMBPRINT_STR_LEN 40 -#define CERT_THUMBPRINT_DATA_LEN 20 - -/* Uncomment to force verbose output - * #define infof(x, y, ...) printf(y, __VA_ARGS__) - * #define failf(x, y, ...) printf(y, __VA_ARGS__) - */ - -#ifndef CALG_SHA_256 -# define CALG_SHA_256 0x0000800c -#endif - -/* Work around typo in classic MinGW's w32api up to version 5.0, - see https://osdn.net/projects/mingw/ticket/38391 */ -#if !defined(ALG_CLASS_DHASH) && defined(ALG_CLASS_HASH) -#define ALG_CLASS_DHASH ALG_CLASS_HASH -#endif - -static Curl_recv schannel_recv; -static Curl_send schannel_send; - -static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, - struct connectdata *conn, int sockindex, - const char *pinnedpubkey); - -static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType, - void *BufDataPtr, unsigned long BufByteSize) -{ - buffer->cbBuffer = BufByteSize; - buffer->BufferType = BufType; - buffer->pvBuffer = BufDataPtr; -} - -static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr, - unsigned long NumArrElem) -{ - desc->ulVersion = SECBUFFER_VERSION; - desc->pBuffers = BufArr; - desc->cBuffers = NumArrElem; -} - -static CURLcode -set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct Curl_easy *data, - struct connectdata *conn) -{ - long ssl_version = SSL_CONN_CONFIG(version); - long ssl_version_max = SSL_CONN_CONFIG(version_max); - long i = ssl_version; - - switch(ssl_version_max) { - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_DEFAULT: - ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; - break; - } - for(; i <= (ssl_version_max >> 16); ++i) { - switch(i) { - case CURL_SSLVERSION_TLSv1_0: - schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_0_CLIENT; - break; - case CURL_SSLVERSION_TLSv1_1: - schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_1_CLIENT; - break; - case CURL_SSLVERSION_TLSv1_2: - schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_2_CLIENT; - break; - case CURL_SSLVERSION_TLSv1_3: - failf(data, "schannel: TLS 1.3 is not yet supported"); - return CURLE_SSL_CONNECT_ERROR; - } - } - return CURLE_OK; -} - -/*longest is 26, buffer is slightly bigger*/ -#define LONGEST_ALG_ID 32 -#define CIPHEROPTION(X) \ - if(strcmp(#X, tmp) == 0) \ - return X - -static int -get_alg_id_by_name(char *name) -{ - char tmp[LONGEST_ALG_ID] = { 0 }; - char *nameEnd = strchr(name, ':'); - size_t n = nameEnd ? min((size_t)(nameEnd - name), LONGEST_ALG_ID - 1) : \ - min(strlen(name), LONGEST_ALG_ID - 1); - strncpy(tmp, name, n); - tmp[n] = 0; - CIPHEROPTION(CALG_MD2); - CIPHEROPTION(CALG_MD4); - CIPHEROPTION(CALG_MD5); - CIPHEROPTION(CALG_SHA); - CIPHEROPTION(CALG_SHA1); - CIPHEROPTION(CALG_MAC); - CIPHEROPTION(CALG_RSA_SIGN); - CIPHEROPTION(CALG_DSS_SIGN); -/*ifdefs for the options that are defined conditionally in wincrypt.h*/ -#ifdef CALG_NO_SIGN - CIPHEROPTION(CALG_NO_SIGN); -#endif - CIPHEROPTION(CALG_RSA_KEYX); - CIPHEROPTION(CALG_DES); -#ifdef CALG_3DES_112 - CIPHEROPTION(CALG_3DES_112); -#endif - CIPHEROPTION(CALG_3DES); - CIPHEROPTION(CALG_DESX); - CIPHEROPTION(CALG_RC2); - CIPHEROPTION(CALG_RC4); - CIPHEROPTION(CALG_SEAL); -#ifdef CALG_DH_SF - CIPHEROPTION(CALG_DH_SF); -#endif - CIPHEROPTION(CALG_DH_EPHEM); -#ifdef CALG_AGREEDKEY_ANY - CIPHEROPTION(CALG_AGREEDKEY_ANY); -#endif -#ifdef CALG_HUGHES_MD5 - CIPHEROPTION(CALG_HUGHES_MD5); -#endif - CIPHEROPTION(CALG_SKIPJACK); -#ifdef CALG_TEK - CIPHEROPTION(CALG_TEK); -#endif - CIPHEROPTION(CALG_CYLINK_MEK); - CIPHEROPTION(CALG_SSL3_SHAMD5); -#ifdef CALG_SSL3_MASTER - CIPHEROPTION(CALG_SSL3_MASTER); -#endif -#ifdef CALG_SCHANNEL_MASTER_HASH - CIPHEROPTION(CALG_SCHANNEL_MASTER_HASH); -#endif -#ifdef CALG_SCHANNEL_MAC_KEY - CIPHEROPTION(CALG_SCHANNEL_MAC_KEY); -#endif -#ifdef CALG_SCHANNEL_ENC_KEY - CIPHEROPTION(CALG_SCHANNEL_ENC_KEY); -#endif -#ifdef CALG_PCT1_MASTER - CIPHEROPTION(CALG_PCT1_MASTER); -#endif -#ifdef CALG_SSL2_MASTER - CIPHEROPTION(CALG_SSL2_MASTER); -#endif -#ifdef CALG_TLS1_MASTER - CIPHEROPTION(CALG_TLS1_MASTER); -#endif -#ifdef CALG_RC5 - CIPHEROPTION(CALG_RC5); -#endif -#ifdef CALG_HMAC - CIPHEROPTION(CALG_HMAC); -#endif -#ifdef CALG_TLS1PRF - CIPHEROPTION(CALG_TLS1PRF); -#endif -#ifdef CALG_HASH_REPLACE_OWF - CIPHEROPTION(CALG_HASH_REPLACE_OWF); -#endif -#ifdef CALG_AES_128 - CIPHEROPTION(CALG_AES_128); -#endif -#ifdef CALG_AES_192 - CIPHEROPTION(CALG_AES_192); -#endif -#ifdef CALG_AES_256 - CIPHEROPTION(CALG_AES_256); -#endif -#ifdef CALG_AES - CIPHEROPTION(CALG_AES); -#endif -#ifdef CALG_SHA_256 - CIPHEROPTION(CALG_SHA_256); -#endif -#ifdef CALG_SHA_384 - CIPHEROPTION(CALG_SHA_384); -#endif -#ifdef CALG_SHA_512 - CIPHEROPTION(CALG_SHA_512); -#endif -#ifdef CALG_ECDH - CIPHEROPTION(CALG_ECDH); -#endif -#ifdef CALG_ECMQV - CIPHEROPTION(CALG_ECMQV); -#endif -#ifdef CALG_ECDSA - CIPHEROPTION(CALG_ECDSA); -#endif -#ifdef CALG_ECDH_EPHEM - CIPHEROPTION(CALG_ECDH_EPHEM); -#endif - return 0; -} - -#define NUM_CIPHERS 47 /* There are 47 options listed above */ - -static CURLcode -set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers, - ALG_ID *algIds) -{ - char *startCur = ciphers; - int algCount = 0; - while(startCur && (0 != *startCur) && (algCount < NUM_CIPHERS)) { - long alg = strtol(startCur, 0, 0); - if(!alg) - alg = get_alg_id_by_name(startCur); - if(alg) - algIds[algCount++] = alg; - else if(!strncmp(startCur, "USE_STRONG_CRYPTO", - sizeof("USE_STRONG_CRYPTO") - 1) || - !strncmp(startCur, "SCH_USE_STRONG_CRYPTO", - sizeof("SCH_USE_STRONG_CRYPTO") - 1)) - schannel_cred->dwFlags |= SCH_USE_STRONG_CRYPTO; - else - return CURLE_SSL_CIPHER; - startCur = strchr(startCur, ':'); - if(startCur) - startCur++; - } - schannel_cred->palgSupportedAlgs = algIds; - schannel_cred->cSupportedAlgs = algCount; - return CURLE_OK; -} - -#ifdef HAS_CLIENT_CERT_PATH - -/* Function allocates memory for store_path only if CURLE_OK is returned */ -static CURLcode -get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path, - TCHAR **thumbprint) -{ - TCHAR *sep; - TCHAR *store_path_start; - size_t store_name_len; - - sep = _tcschr(path, TEXT('\\')); - if(!sep) - return CURLE_SSL_CERTPROBLEM; - - store_name_len = sep - path; - - if(_tcsncmp(path, TEXT("CurrentUser"), store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_CURRENT_USER; - else if(_tcsncmp(path, TEXT("LocalMachine"), store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE; - else if(_tcsncmp(path, TEXT("CurrentService"), store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_CURRENT_SERVICE; - else if(_tcsncmp(path, TEXT("Services"), store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_SERVICES; - else if(_tcsncmp(path, TEXT("Users"), store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_USERS; - else if(_tcsncmp(path, TEXT("CurrentUserGroupPolicy"), - store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY; - else if(_tcsncmp(path, TEXT("LocalMachineGroupPolicy"), - store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY; - else if(_tcsncmp(path, TEXT("LocalMachineEnterprise"), - store_name_len) == 0) - *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE; - else - return CURLE_SSL_CERTPROBLEM; - - store_path_start = sep + 1; - - sep = _tcschr(store_path_start, TEXT('\\')); - if(!sep) - return CURLE_SSL_CERTPROBLEM; - - *thumbprint = sep + 1; - if(_tcslen(*thumbprint) != CERT_THUMBPRINT_STR_LEN) - return CURLE_SSL_CERTPROBLEM; - - *sep = TEXT('\0'); - *store_path = _tcsdup(store_path_start); - *sep = TEXT('\\'); - if(!*store_path) - return CURLE_OUT_OF_MEMORY; - - return CURLE_OK; -} -#endif -static CURLcode -schannel_acquire_credential_handle(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - SCHANNEL_CRED schannel_cred; - ALG_ID algIds[NUM_CIPHERS]; - PCCERT_CONTEXT client_certs[1] = { NULL }; - SECURITY_STATUS sspi_status = SEC_E_OK; - CURLcode result; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - /* setup Schannel API options */ - memset(&schannel_cred, 0, sizeof(schannel_cred)); - schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; - - if(conn->ssl_config.verifypeer) { -#ifdef HAS_MANUAL_VERIFY_API - if(backend->use_manual_cred_validation) - schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION; - else -#endif - schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION; - - if(SSL_SET_OPTION(no_revoke)) { - schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | - SCH_CRED_IGNORE_REVOCATION_OFFLINE; - - DEBUGF(infof(data, "schannel: disabled server certificate revocation " - "checks")); - } - else if(SSL_SET_OPTION(revoke_best_effort)) { - schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | - SCH_CRED_IGNORE_REVOCATION_OFFLINE | SCH_CRED_REVOCATION_CHECK_CHAIN; - - DEBUGF(infof(data, "schannel: ignore revocation offline errors")); - } - else { - schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN; - - DEBUGF(infof(data, - "schannel: checking server certificate revocation")); - } - } - else { - schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | - SCH_CRED_IGNORE_NO_REVOCATION_CHECK | - SCH_CRED_IGNORE_REVOCATION_OFFLINE; - DEBUGF(infof(data, - "schannel: disabled server cert revocation checks")); - } - - if(!conn->ssl_config.verifyhost) { - schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK; - DEBUGF(infof(data, "schannel: verifyhost setting prevents Schannel from " - "comparing the supplied target name with the subject " - "names in server certificates.")); - } - - if(!SSL_SET_OPTION(auto_client_cert)) { - schannel_cred.dwFlags &= ~SCH_CRED_USE_DEFAULT_CREDS; - schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; - infof(data, "schannel: disabled automatic use of client certificate"); - } - else - infof(data, "schannel: enabled automatic use of client certificate"); - - switch(conn->ssl_config.version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_TLSv1_0: - case CURL_SSLVERSION_TLSv1_1: - case CURL_SSLVERSION_TLSv1_2: - case CURL_SSLVERSION_TLSv1_3: - { - result = set_ssl_version_min_max(&schannel_cred, data, conn); - if(result != CURLE_OK) - return result; - break; - } - case CURL_SSLVERSION_SSLv3: - case CURL_SSLVERSION_SSLv2: - failf(data, "SSL versions not supported"); - return CURLE_NOT_BUILT_IN; - default: - failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); - return CURLE_SSL_CONNECT_ERROR; - } - - if(SSL_CONN_CONFIG(cipher_list)) { - result = set_ssl_ciphers(&schannel_cred, SSL_CONN_CONFIG(cipher_list), - algIds); - if(CURLE_OK != result) { - failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG"); - return result; - } - } - - -#ifdef HAS_CLIENT_CERT_PATH - /* client certificate */ - if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) { - DWORD cert_store_name = 0; - TCHAR *cert_store_path = NULL; - TCHAR *cert_thumbprint_str = NULL; - CRYPT_HASH_BLOB cert_thumbprint; - BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN]; - HCERTSTORE cert_store = NULL; - FILE *fInCert = NULL; - void *certdata = NULL; - size_t certsize = 0; - bool blob = data->set.ssl.primary.cert_blob != NULL; - TCHAR *cert_path = NULL; - if(blob) { - certdata = data->set.ssl.primary.cert_blob->data; - certsize = data->set.ssl.primary.cert_blob->len; - } - else { - cert_path = curlx_convert_UTF8_to_tchar( - data->set.ssl.primary.clientcert); - if(!cert_path) - return CURLE_OUT_OF_MEMORY; - - result = get_cert_location(cert_path, &cert_store_name, - &cert_store_path, &cert_thumbprint_str); - - if(result && (data->set.ssl.primary.clientcert[0]!='\0')) - fInCert = fopen(data->set.ssl.primary.clientcert, "rb"); - - if(result && !fInCert) { - failf(data, "schannel: Failed to get certificate location" - " or file for %s", - data->set.ssl.primary.clientcert); - curlx_unicodefree(cert_path); - return result; - } - } - - if((fInCert || blob) && (data->set.ssl.cert_type) && - (!strcasecompare(data->set.ssl.cert_type, "P12"))) { - failf(data, "schannel: certificate format compatibility error " - " for %s", - blob ? "(memory blob)" : data->set.ssl.primary.clientcert); - curlx_unicodefree(cert_path); - return CURLE_SSL_CERTPROBLEM; - } - - if(fInCert || blob) { - /* Reading a .P12 or .pfx file, like the example at bottom of - https://social.msdn.microsoft.com/Forums/windowsdesktop/ - en-US/3e7bc95f-b21a-4bcd-bd2c-7f996718cae5 - */ - CRYPT_DATA_BLOB datablob; - WCHAR* pszPassword; - size_t pwd_len = 0; - int str_w_len = 0; - const char *cert_showfilename_error = blob ? - "(memory blob)" : data->set.ssl.primary.clientcert; - curlx_unicodefree(cert_path); - if(fInCert) { - long cert_tell = 0; - bool continue_reading = fseek(fInCert, 0, SEEK_END) == 0; - if(continue_reading) - cert_tell = ftell(fInCert); - if(cert_tell < 0) - continue_reading = FALSE; - else - certsize = (size_t)cert_tell; - if(continue_reading) - continue_reading = fseek(fInCert, 0, SEEK_SET) == 0; - if(continue_reading) - certdata = malloc(certsize + 1); - if((!certdata) || - ((int) fread(certdata, certsize, 1, fInCert) != 1)) - continue_reading = FALSE; - fclose(fInCert); - if(!continue_reading) { - failf(data, "schannel: Failed to read cert file %s", - data->set.ssl.primary.clientcert); - free(certdata); - return CURLE_SSL_CERTPROBLEM; - } - } - - /* Convert key-pair data to the in-memory certificate store */ - datablob.pbData = (BYTE*)certdata; - datablob.cbData = (DWORD)certsize; - - if(data->set.ssl.key_passwd) - pwd_len = strlen(data->set.ssl.key_passwd); - pszPassword = (WCHAR*)malloc(sizeof(WCHAR)*(pwd_len + 1)); - if(pszPassword) { - if(pwd_len > 0) - str_w_len = MultiByteToWideChar(CP_UTF8, - MB_ERR_INVALID_CHARS, - data->set.ssl.key_passwd, (int)pwd_len, - pszPassword, (int)(pwd_len + 1)); - - if((str_w_len >= 0) && (str_w_len <= (int)pwd_len)) - pszPassword[str_w_len] = 0; - else - pszPassword[0] = 0; - - cert_store = PFXImportCertStore(&datablob, pszPassword, 0); - free(pszPassword); - } - if(!blob) - free(certdata); - if(!cert_store) { - DWORD errorcode = GetLastError(); - if(errorcode == ERROR_INVALID_PASSWORD) - failf(data, "schannel: Failed to import cert file %s, " - "password is bad", - cert_showfilename_error); - else - failf(data, "schannel: Failed to import cert file %s, " - "last error is 0x%x", - cert_showfilename_error, errorcode); - return CURLE_SSL_CERTPROBLEM; - } - - client_certs[0] = CertFindCertificateInStore( - cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, - CERT_FIND_ANY, NULL, NULL); - - if(!client_certs[0]) { - failf(data, "schannel: Failed to get certificate from file %s" - ", last error is 0x%x", - cert_showfilename_error, GetLastError()); - CertCloseStore(cert_store, 0); - return CURLE_SSL_CERTPROBLEM; - } - - schannel_cred.cCreds = 1; - schannel_cred.paCred = client_certs; - } - else { - cert_store = - CertOpenStore(CURL_CERT_STORE_PROV_SYSTEM, 0, - (HCRYPTPROV)NULL, - CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name, - cert_store_path); - if(!cert_store) { - failf(data, "schannel: Failed to open cert store %x %s, " - "last error is 0x%x", - cert_store_name, cert_store_path, GetLastError()); - free(cert_store_path); - curlx_unicodefree(cert_path); - return CURLE_SSL_CERTPROBLEM; - } - free(cert_store_path); - - cert_thumbprint.pbData = cert_thumbprint_data; - cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN; - - if(!CryptStringToBinary(cert_thumbprint_str, - CERT_THUMBPRINT_STR_LEN, - CRYPT_STRING_HEX, - cert_thumbprint_data, - &cert_thumbprint.cbData, - NULL, NULL)) { - curlx_unicodefree(cert_path); - CertCloseStore(cert_store, 0); - return CURLE_SSL_CERTPROBLEM; - } - - client_certs[0] = CertFindCertificateInStore( - cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, - CERT_FIND_HASH, &cert_thumbprint, NULL); - - curlx_unicodefree(cert_path); - - if(client_certs[0]) { - schannel_cred.cCreds = 1; - schannel_cred.paCred = client_certs; - } - else { - /* CRYPT_E_NOT_FOUND / E_INVALIDARG */ - CertCloseStore(cert_store, 0); - return CURLE_SSL_CERTPROBLEM; - } - } - CertCloseStore(cert_store, 0); - } -#else - if(data->set.ssl.primary.clientcert || data->set.ssl.primary.cert_blob) { - failf(data, "schannel: client cert support not built in"); - return CURLE_NOT_BUILT_IN; - } -#endif - - /* allocate memory for the re-usable credential handle */ - backend->cred = (struct Curl_schannel_cred *) - calloc(1, sizeof(struct Curl_schannel_cred)); - if(!backend->cred) { - failf(data, "schannel: unable to allocate memory"); - - if(client_certs[0]) - CertFreeCertificateContext(client_certs[0]); - - return CURLE_OUT_OF_MEMORY; - } - backend->cred->refcount = 1; - - sspi_status = - s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME, - SECPKG_CRED_OUTBOUND, NULL, - &schannel_cred, NULL, NULL, - &backend->cred->cred_handle, - &backend->cred->time_stamp); - - if(client_certs[0]) - CertFreeCertificateContext(client_certs[0]); - - if(sspi_status != SEC_E_OK) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: AcquireCredentialsHandle failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - Curl_safefree(backend->cred); - switch(sspi_status) { - case SEC_E_INSUFFICIENT_MEMORY: - return CURLE_OUT_OF_MEMORY; - case SEC_E_NO_CREDENTIALS: - case SEC_E_SECPKG_NOT_FOUND: - case SEC_E_NOT_OWNER: - case SEC_E_UNKNOWN_CREDENTIALS: - case SEC_E_INTERNAL_ERROR: - default: - return CURLE_SSL_CONNECT_ERROR; - } - } - - return CURLE_OK; -} - -static CURLcode -schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - ssize_t written = -1; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - SecBuffer outbuf; - SecBufferDesc outbuf_desc; - SecBuffer inbuf; - SecBufferDesc inbuf_desc; -#ifdef HAS_ALPN - unsigned char alpn_buffer[128]; -#endif - SECURITY_STATUS sspi_status = SEC_E_OK; - struct Curl_schannel_cred *old_cred = NULL; - struct in_addr addr; -#ifdef ENABLE_IPV6 - struct in6_addr addr6; -#endif - CURLcode result; - char * const hostname = SSL_HOST_NAME(); - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - DEBUGF(infof(data, - "schannel: SSL/TLS connection with %s port %hu (step 1/3)", - hostname, conn->remote_port)); - - if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT, - VERSION_LESS_THAN_EQUAL)) { - /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and - algorithms that may not be supported by all servers. */ - infof(data, "schannel: Windows version is old and may not be able to " - "connect to some servers due to lack of SNI, algorithms, etc."); - } - -#ifdef HAS_ALPN - /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above. - Also it doesn't seem to be supported for Wine, see curl bug #983. */ - backend->use_alpn = conn->bits.tls_enable_alpn && - !GetProcAddress(GetModuleHandle(TEXT("ntdll")), - "wine_get_version") && - curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL); -#else - backend->use_alpn = false; -#endif - -#ifdef _WIN32_WCE -#ifdef HAS_MANUAL_VERIFY_API - /* certificate validation on CE doesn't seem to work right; we'll - * do it following a more manual process. */ - backend->use_manual_cred_validation = true; -#else -#error "compiler too old to support requisite manual cert verify for Win CE" -#endif -#else -#ifdef HAS_MANUAL_VERIFY_API - if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) { - if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) { - backend->use_manual_cred_validation = true; - } - else { - failf(data, "schannel: this version of Windows is too old to support " - "certificate verification via CA bundle file."); - return CURLE_SSL_CACERT_BADFILE; - } - } - else - backend->use_manual_cred_validation = false; -#else - if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) { - failf(data, "schannel: CA cert support not built in"); - return CURLE_NOT_BUILT_IN; - } -#endif -#endif - - backend->cred = NULL; - - /* check for an existing re-usable credential handle */ - if(SSL_SET_OPTION(primary.sessionid)) { - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, - SSL_IS_PROXY() ? TRUE : FALSE, - (void **)&old_cred, NULL, sockindex)) { - backend->cred = old_cred; - DEBUGF(infof(data, "schannel: re-using existing credential handle")); - - /* increment the reference counter of the credential/session handle */ - backend->cred->refcount++; - DEBUGF(infof(data, - "schannel: incremented credential handle refcount = %d", - backend->cred->refcount)); - } - Curl_ssl_sessionid_unlock(data); - } - - if(!backend->cred) { - char *snihost; - result = schannel_acquire_credential_handle(data, conn, sockindex); - if(result != CURLE_OK) { - return result; - } - /* A hostname associated with the credential is needed by - InitializeSecurityContext for SNI and other reasons. */ - snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); - if(!snihost) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost); - if(!backend->cred->sni_hostname) - return CURLE_OUT_OF_MEMORY; - } - - /* Warn if SNI is disabled due to use of an IP address */ - if(Curl_inet_pton(AF_INET, hostname, &addr) -#ifdef ENABLE_IPV6 - || Curl_inet_pton(AF_INET6, hostname, &addr6) -#endif - ) { - infof(data, "schannel: using IP address, SNI is not supported by OS."); - } - -#ifdef HAS_ALPN - if(backend->use_alpn) { - int cur = 0; - int list_start_index = 0; - unsigned int *extension_len = NULL; - unsigned short* list_len = NULL; - - /* The first four bytes will be an unsigned int indicating number - of bytes of data in the rest of the buffer. */ - extension_len = (unsigned int *)(&alpn_buffer[cur]); - cur += sizeof(unsigned int); - - /* The next four bytes are an indicator that this buffer will contain - ALPN data, as opposed to NPN, for example. */ - *(unsigned int *)&alpn_buffer[cur] = - SecApplicationProtocolNegotiationExt_ALPN; - cur += sizeof(unsigned int); - - /* The next two bytes will be an unsigned short indicating the number - of bytes used to list the preferred protocols. */ - list_len = (unsigned short*)(&alpn_buffer[cur]); - cur += sizeof(unsigned short); - - list_start_index = cur; - -#ifdef USE_HTTP2 - if(data->state.httpwant >= CURL_HTTP_VERSION_2) { - alpn_buffer[cur++] = ALPN_H2_LENGTH; - memcpy(&alpn_buffer[cur], ALPN_H2, ALPN_H2_LENGTH); - cur += ALPN_H2_LENGTH; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2); - } -#endif - - alpn_buffer[cur++] = ALPN_HTTP_1_1_LENGTH; - memcpy(&alpn_buffer[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); - cur += ALPN_HTTP_1_1_LENGTH; - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1); - - *list_len = curlx_uitous(cur - list_start_index); - *extension_len = *list_len + sizeof(unsigned int) + sizeof(unsigned short); - - InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur); - InitSecBufferDesc(&inbuf_desc, &inbuf, 1); - } - else { - InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&inbuf_desc, &inbuf, 1); - } -#else /* HAS_ALPN */ - InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&inbuf_desc, &inbuf, 1); -#endif - - /* setup output buffer */ - InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&outbuf_desc, &outbuf, 1); - - /* security request flags */ - backend->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | - ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | - ISC_REQ_STREAM; - - if(!SSL_SET_OPTION(auto_client_cert)) { - backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; - } - - /* allocate memory for the security context handle */ - backend->ctxt = (struct Curl_schannel_ctxt *) - calloc(1, sizeof(struct Curl_schannel_ctxt)); - if(!backend->ctxt) { - failf(data, "schannel: unable to allocate memory"); - return CURLE_OUT_OF_MEMORY; - } - - /* Schannel InitializeSecurityContext: - https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx - - At the moment we don't pass inbuf unless we're using ALPN since we only - use it for that, and Wine (for which we currently disable ALPN) is giving - us problems with inbuf regardless. https://github.com/curl/curl/issues/983 - */ - sspi_status = s_pSecFn->InitializeSecurityContext( - &backend->cred->cred_handle, NULL, backend->cred->sni_hostname, - backend->req_flags, 0, 0, - (backend->use_alpn ? &inbuf_desc : NULL), - 0, &backend->ctxt->ctxt_handle, - &outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp); - - if(sspi_status != SEC_I_CONTINUE_NEEDED) { - char buffer[STRERROR_LEN]; - Curl_safefree(backend->ctxt); - switch(sspi_status) { - case SEC_E_INSUFFICIENT_MEMORY: - failf(data, "schannel: initial InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_OUT_OF_MEMORY; - case SEC_E_WRONG_PRINCIPAL: - failf(data, "schannel: SNI or certificate check failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_PEER_FAILED_VERIFICATION; - /* - case SEC_E_INVALID_HANDLE: - case SEC_E_INVALID_TOKEN: - case SEC_E_LOGON_DENIED: - case SEC_E_TARGET_UNKNOWN: - case SEC_E_NO_AUTHENTICATING_AUTHORITY: - case SEC_E_INTERNAL_ERROR: - case SEC_E_NO_CREDENTIALS: - case SEC_E_UNSUPPORTED_FUNCTION: - case SEC_E_APPLICATION_PROTOCOL_MISMATCH: - */ - default: - failf(data, "schannel: initial InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_SSL_CONNECT_ERROR; - } - } - - DEBUGF(infof(data, "schannel: sending initial handshake data: " - "sending %lu bytes.", outbuf.cbBuffer)); - - /* send initial handshake data which is now stored in output buffer */ - result = Curl_write_plain(data, conn->sock[sockindex], outbuf.pvBuffer, - outbuf.cbBuffer, &written); - s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); - if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) { - failf(data, "schannel: failed to send initial handshake data: " - "sent %zd of %lu bytes", written, outbuf.cbBuffer); - return CURLE_SSL_CONNECT_ERROR; - } - - DEBUGF(infof(data, "schannel: sent initial handshake data: " - "sent %zd bytes", written)); - - backend->recv_unrecoverable_err = CURLE_OK; - backend->recv_sspi_close_notify = false; - backend->recv_connection_closed = false; - backend->encdata_is_incomplete = false; - - /* continue to second handshake step */ - connssl->connecting_state = ssl_connect_2; - - return CURLE_OK; -} - -static CURLcode -schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - int i; - ssize_t nread = -1, written = -1; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - unsigned char *reallocated_buffer; - SecBuffer outbuf[3]; - SecBufferDesc outbuf_desc; - SecBuffer inbuf[2]; - SecBufferDesc inbuf_desc; - SECURITY_STATUS sspi_status = SEC_E_OK; - CURLcode result; - bool doread; - const char *pubkey_ptr; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE; - - DEBUGF(infof(data, - "schannel: SSL/TLS connection with %s port %hu (step 2/3)", - SSL_HOST_NAME(), conn->remote_port)); - - if(!backend->cred || !backend->ctxt) - return CURLE_SSL_CONNECT_ERROR; - - /* buffer to store previously received and decrypted data */ - if(!backend->decdata_buffer) { - backend->decdata_offset = 0; - backend->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; - backend->decdata_buffer = malloc(backend->decdata_length); - if(!backend->decdata_buffer) { - failf(data, "schannel: unable to allocate memory"); - return CURLE_OUT_OF_MEMORY; - } - } - - /* buffer to store previously received and encrypted data */ - if(!backend->encdata_buffer) { - backend->encdata_is_incomplete = false; - backend->encdata_offset = 0; - backend->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; - backend->encdata_buffer = malloc(backend->encdata_length); - if(!backend->encdata_buffer) { - failf(data, "schannel: unable to allocate memory"); - return CURLE_OUT_OF_MEMORY; - } - } - - /* if we need a bigger buffer to read a full message, increase buffer now */ - if(backend->encdata_length - backend->encdata_offset < - CURL_SCHANNEL_BUFFER_FREE_SIZE) { - /* increase internal encrypted data buffer */ - size_t reallocated_length = backend->encdata_offset + - CURL_SCHANNEL_BUFFER_FREE_SIZE; - reallocated_buffer = realloc(backend->encdata_buffer, - reallocated_length); - - if(!reallocated_buffer) { - failf(data, "schannel: unable to re-allocate memory"); - return CURLE_OUT_OF_MEMORY; - } - else { - backend->encdata_buffer = reallocated_buffer; - backend->encdata_length = reallocated_length; - } - } - - for(;;) { - if(doread) { - /* read encrypted handshake data from socket */ - result = Curl_read_plain(conn->sock[sockindex], - (char *) (backend->encdata_buffer + - backend->encdata_offset), - backend->encdata_length - - backend->encdata_offset, - &nread); - if(result == CURLE_AGAIN) { - if(connssl->connecting_state != ssl_connect_2_writing) - connssl->connecting_state = ssl_connect_2_reading; - DEBUGF(infof(data, "schannel: failed to receive handshake, " - "need more data")); - return CURLE_OK; - } - else if((result != CURLE_OK) || (nread == 0)) { - failf(data, "schannel: failed to receive handshake, " - "SSL/TLS connection failed"); - return CURLE_SSL_CONNECT_ERROR; - } - - /* increase encrypted data buffer offset */ - backend->encdata_offset += nread; - backend->encdata_is_incomplete = false; - DEBUGF(infof(data, "schannel: encrypted data got %zd", nread)); - } - - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); - - /* setup input buffers */ - InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(backend->encdata_offset), - curlx_uztoul(backend->encdata_offset)); - InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&inbuf_desc, inbuf, 2); - - /* setup output buffers */ - InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0); - InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0); - InitSecBuffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&outbuf_desc, outbuf, 3); - - if(!inbuf[0].pvBuffer) { - failf(data, "schannel: unable to allocate memory"); - return CURLE_OUT_OF_MEMORY; - } - - /* copy received handshake data into input buffer */ - memcpy(inbuf[0].pvBuffer, backend->encdata_buffer, - backend->encdata_offset); - - sspi_status = s_pSecFn->InitializeSecurityContext( - &backend->cred->cred_handle, &backend->ctxt->ctxt_handle, - backend->cred->sni_hostname, backend->req_flags, - 0, 0, &inbuf_desc, 0, NULL, - &outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp); - - /* free buffer for received handshake data */ - Curl_safefree(inbuf[0].pvBuffer); - - /* check if the handshake was incomplete */ - if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { - backend->encdata_is_incomplete = true; - connssl->connecting_state = ssl_connect_2_reading; - DEBUGF(infof(data, - "schannel: received incomplete message, need more data")); - return CURLE_OK; - } - - /* If the server has requested a client certificate, attempt to continue - the handshake without one. This will allow connections to servers which - request a client certificate but do not require it. */ - if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS && - !(backend->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) { - backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; - connssl->connecting_state = ssl_connect_2_writing; - DEBUGF(infof(data, - "schannel: a client certificate has been requested")); - return CURLE_OK; - } - - /* check if the handshake needs to be continued */ - if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) { - for(i = 0; i < 3; i++) { - /* search for handshake tokens that need to be send */ - if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) { - DEBUGF(infof(data, "schannel: sending next handshake data: " - "sending %lu bytes.", outbuf[i].cbBuffer)); - - /* send handshake token to server */ - result = Curl_write_plain(data, conn->sock[sockindex], - outbuf[i].pvBuffer, outbuf[i].cbBuffer, - &written); - if((result != CURLE_OK) || - (outbuf[i].cbBuffer != (size_t) written)) { - failf(data, "schannel: failed to send next handshake data: " - "sent %zd of %lu bytes", written, outbuf[i].cbBuffer); - return CURLE_SSL_CONNECT_ERROR; - } - } - - /* free obsolete buffer */ - if(outbuf[i].pvBuffer) { - s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer); - } - } - } - else { - char buffer[STRERROR_LEN]; - switch(sspi_status) { - case SEC_E_INSUFFICIENT_MEMORY: - failf(data, "schannel: next InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_OUT_OF_MEMORY; - case SEC_E_WRONG_PRINCIPAL: - failf(data, "schannel: SNI or certificate check failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_PEER_FAILED_VERIFICATION; - case SEC_E_UNTRUSTED_ROOT: - failf(data, "schannel: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_PEER_FAILED_VERIFICATION; - /* - case SEC_E_INVALID_HANDLE: - case SEC_E_INVALID_TOKEN: - case SEC_E_LOGON_DENIED: - case SEC_E_TARGET_UNKNOWN: - case SEC_E_NO_AUTHENTICATING_AUTHORITY: - case SEC_E_INTERNAL_ERROR: - case SEC_E_NO_CREDENTIALS: - case SEC_E_UNSUPPORTED_FUNCTION: - case SEC_E_APPLICATION_PROTOCOL_MISMATCH: - */ - default: - failf(data, "schannel: next InitializeSecurityContext failed: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - return CURLE_SSL_CONNECT_ERROR; - } - } - - /* check if there was additional remaining encrypted data */ - if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) { - DEBUGF(infof(data, "schannel: encrypted data length: %lu", - inbuf[1].cbBuffer)); - /* - There are two cases where we could be getting extra data here: - 1) If we're renegotiating a connection and the handshake is already - complete (from the server perspective), it can encrypted app data - (not handshake data) in an extra buffer at this point. - 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a - connection and this extra data is part of the handshake. - We should process the data immediately; waiting for the socket to - be ready may fail since the server is done sending handshake data. - */ - /* check if the remaining data is less than the total amount - and therefore begins after the already processed data */ - if(backend->encdata_offset > inbuf[1].cbBuffer) { - memmove(backend->encdata_buffer, - (backend->encdata_buffer + backend->encdata_offset) - - inbuf[1].cbBuffer, inbuf[1].cbBuffer); - backend->encdata_offset = inbuf[1].cbBuffer; - if(sspi_status == SEC_I_CONTINUE_NEEDED) { - doread = FALSE; - continue; - } - } - } - else { - backend->encdata_offset = 0; - } - break; - } - - /* check if the handshake needs to be continued */ - if(sspi_status == SEC_I_CONTINUE_NEEDED) { - connssl->connecting_state = ssl_connect_2_reading; - return CURLE_OK; - } - - /* check if the handshake is complete */ - if(sspi_status == SEC_E_OK) { - connssl->connecting_state = ssl_connect_3; - DEBUGF(infof(data, "schannel: SSL/TLS handshake complete")); - } - - pubkey_ptr = SSL_PINNED_PUB_KEY(); - if(pubkey_ptr) { - result = pkp_pin_peer_pubkey(data, conn, sockindex, pubkey_ptr); - if(result) { - failf(data, "SSL: public key does not match pinned public key"); - return result; - } - } - -#ifdef HAS_MANUAL_VERIFY_API - if(conn->ssl_config.verifypeer && backend->use_manual_cred_validation) { - return Curl_verify_certificate(data, conn, sockindex); - } -#endif - - return CURLE_OK; -} - -static bool -valid_cert_encoding(const CERT_CONTEXT *cert_context) -{ - return (cert_context != NULL) && - ((cert_context->dwCertEncodingType & X509_ASN_ENCODING) != 0) && - (cert_context->pbCertEncoded != NULL) && - (cert_context->cbCertEncoded > 0); -} - -typedef bool(*Read_crt_func)(const CERT_CONTEXT *ccert_context, void *arg); - -static void -traverse_cert_store(const CERT_CONTEXT *context, Read_crt_func func, - void *arg) -{ - const CERT_CONTEXT *current_context = NULL; - bool should_continue = true; - while(should_continue && - (current_context = CertEnumCertificatesInStore( - context->hCertStore, - current_context)) != NULL) - should_continue = func(current_context, arg); - - if(current_context) - CertFreeCertificateContext(current_context); -} - -static bool -cert_counter_callback(const CERT_CONTEXT *ccert_context, void *certs_count) -{ - if(valid_cert_encoding(ccert_context)) - (*(int *)certs_count)++; - return true; -} - -struct Adder_args -{ - struct Curl_easy *data; - CURLcode result; - int idx; - int certs_count; -}; - -static bool -add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, void *raw_arg) -{ - struct Adder_args *args = (struct Adder_args*)raw_arg; - args->result = CURLE_OK; - if(valid_cert_encoding(ccert_context)) { - const char *beg = (const char *) ccert_context->pbCertEncoded; - const char *end = beg + ccert_context->cbCertEncoded; - int insert_index = (args->certs_count - 1) - args->idx; - args->result = Curl_extract_certinfo(args->data, insert_index, - beg, end); - args->idx++; - } - return args->result == CURLE_OK; -} - -static CURLcode -schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - CURLcode result = CURLE_OK; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - SECURITY_STATUS sspi_status = SEC_E_OK; - CERT_CONTEXT *ccert_context = NULL; - bool isproxy = SSL_IS_PROXY(); -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) - const char * const hostname = SSL_HOST_NAME(); -#endif -#ifdef HAS_ALPN - SecPkgContext_ApplicationProtocol alpn_result; -#endif - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); - DEBUGASSERT(backend); - - DEBUGF(infof(data, - "schannel: SSL/TLS connection with %s port %hu (step 3/3)", - hostname, conn->remote_port)); - - if(!backend->cred) - return CURLE_SSL_CONNECT_ERROR; - - /* check if the required context attributes are met */ - if(backend->ret_flags != backend->req_flags) { - if(!(backend->ret_flags & ISC_RET_SEQUENCE_DETECT)) - failf(data, "schannel: failed to setup sequence detection"); - if(!(backend->ret_flags & ISC_RET_REPLAY_DETECT)) - failf(data, "schannel: failed to setup replay detection"); - if(!(backend->ret_flags & ISC_RET_CONFIDENTIALITY)) - failf(data, "schannel: failed to setup confidentiality"); - if(!(backend->ret_flags & ISC_RET_ALLOCATED_MEMORY)) - failf(data, "schannel: failed to setup memory allocation"); - if(!(backend->ret_flags & ISC_RET_STREAM)) - failf(data, "schannel: failed to setup stream orientation"); - return CURLE_SSL_CONNECT_ERROR; - } - -#ifdef HAS_ALPN - if(backend->use_alpn) { - sspi_status = - s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, - SECPKG_ATTR_APPLICATION_PROTOCOL, - &alpn_result); - - if(sspi_status != SEC_E_OK) { - failf(data, "schannel: failed to retrieve ALPN result"); - return CURLE_SSL_CONNECT_ERROR; - } - - if(alpn_result.ProtoNegoStatus == - SecApplicationProtocolNegotiationStatus_Success) { - - infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, - alpn_result.ProtocolIdSize, alpn_result.ProtocolId); - -#ifdef USE_HTTP2 - if(alpn_result.ProtocolIdSize == ALPN_H2_LENGTH && - !memcmp(ALPN_H2, alpn_result.ProtocolId, ALPN_H2_LENGTH)) { - conn->negnpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH && - !memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId, - ALPN_HTTP_1_1_LENGTH)) { - conn->negnpn = CURL_HTTP_VERSION_1_1; - } - } - else - infof(data, VTLS_INFOF_NO_ALPN); - Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); - } -#endif - - /* save the current session data for possible re-use */ - if(SSL_SET_OPTION(primary.sessionid)) { - bool incache; - bool added = FALSE; - struct Curl_schannel_cred *old_cred = NULL; - - Curl_ssl_sessionid_lock(data); - incache = !(Curl_ssl_getsessionid(data, conn, isproxy, (void **)&old_cred, - NULL, sockindex)); - if(incache) { - if(old_cred != backend->cred) { - DEBUGF(infof(data, - "schannel: old credential handle is stale, removing")); - /* we're not taking old_cred ownership here, no refcount++ is needed */ - Curl_ssl_delsessionid(data, (void *)old_cred); - incache = FALSE; - } - } - if(!incache) { - result = Curl_ssl_addsessionid(data, conn, isproxy, backend->cred, - sizeof(struct Curl_schannel_cred), - sockindex, &added); - if(result) { - Curl_ssl_sessionid_unlock(data); - failf(data, "schannel: failed to store credential handle"); - return result; - } - else if(added) { - /* this cred session is now also referenced by sessionid cache */ - backend->cred->refcount++; - DEBUGF(infof(data, - "schannel: stored credential handle in session cache")); - } - } - Curl_ssl_sessionid_unlock(data); - } - - if(data->set.ssl.certinfo) { - int certs_count = 0; - sspi_status = - s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, - SECPKG_ATTR_REMOTE_CERT_CONTEXT, - &ccert_context); - - if((sspi_status != SEC_E_OK) || !ccert_context) { - failf(data, "schannel: failed to retrieve remote cert context"); - return CURLE_PEER_FAILED_VERIFICATION; - } - - traverse_cert_store(ccert_context, cert_counter_callback, &certs_count); - - result = Curl_ssl_init_certinfo(data, certs_count); - if(!result) { - struct Adder_args args; - args.data = data; - args.idx = 0; - args.certs_count = certs_count; - traverse_cert_store(ccert_context, add_cert_to_certinfo, &args); - result = args.result; - } - CertFreeCertificateContext(ccert_context); - if(result) - return result; - } - - connssl->connecting_state = ssl_connect_done; - - return CURLE_OK; -} - -static CURLcode -schannel_connect_common(struct Curl_easy *data, struct connectdata *conn, - int sockindex, bool nonblocking, bool *done) -{ - CURLcode result; - 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 */ - if(ssl_connection_complete == connssl->state) { - *done = TRUE; - return CURLE_OK; - } - - if(ssl_connect_1 == connssl->connecting_state) { - /* check out how much more time we're allowed */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL/TLS connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - result = schannel_connect_step1(data, conn, sockindex); - if(result) - return result; - } - - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { - - /* check out how much more time we're allowed */ - timeout_ms = Curl_timeleft(data, NULL, TRUE); - - if(timeout_ms < 0) { - /* no need to continue if time already is up */ - failf(data, "SSL/TLS connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - /* if ssl is expecting something, check if it's available. */ - if(connssl->connecting_state == ssl_connect_2_reading - || connssl->connecting_state == ssl_connect_2_writing) { - - curl_socket_t writefd = ssl_connect_2_writing == - connssl->connecting_state ? sockfd : CURL_SOCKET_BAD; - curl_socket_t readfd = ssl_connect_2_reading == - connssl->connecting_state ? sockfd : CURL_SOCKET_BAD; - - what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, - nonblocking ? 0 : timeout_ms); - if(what < 0) { - /* fatal error */ - failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO); - return CURLE_SSL_CONNECT_ERROR; - } - else if(0 == what) { - if(nonblocking) { - *done = FALSE; - return CURLE_OK; - } - else { - /* timeout */ - failf(data, "SSL/TLS connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - } - /* socket is readable or writable */ - } - - /* Run transaction, and return to the caller if it failed or if - * this connection is part of a multi handle and this loop would - * execute again. This permits the owner of a multi handle to - * abort a connection attempt before step2 has completed while - * ensuring that a client using select() or epoll() will always - * have a valid fdset to wait on. - */ - result = schannel_connect_step2(data, conn, sockindex); - 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; - - } /* repeat step2 until all transactions are done. */ - - if(ssl_connect_3 == connssl->connecting_state) { - result = schannel_connect_step3(data, conn, sockindex); - if(result) - return result; - } - - if(ssl_connect_done == connssl->connecting_state) { - connssl->state = ssl_connection_complete; - conn->recv[sockindex] = schannel_recv; - conn->send[sockindex] = schannel_send; - -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - /* When SSPI is used in combination with Schannel - * we need the Schannel context to create the Schannel - * binding to pass the IIS extended protection checks. - * Available on Windows 7 or later. - */ - { - struct ssl_backend_data *backend = connssl->backend; - DEBUGASSERT(backend); - conn->sslContext = &backend->ctxt->ctxt_handle; - } -#endif - - *done = TRUE; - } - else - *done = FALSE; - - /* reset our connection state machine */ - connssl->connecting_state = ssl_connect_1; - - return CURLE_OK; -} - -static ssize_t -schannel_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t len, CURLcode *err) -{ - ssize_t written = -1; - size_t data_len = 0; - unsigned char *ptr = NULL; - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - SecBuffer outbuf[4]; - SecBufferDesc outbuf_desc; - SECURITY_STATUS sspi_status = SEC_E_OK; - CURLcode result; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - /* check if the maximum stream sizes were queried */ - if(backend->stream_sizes.cbMaximumMessage == 0) { - sspi_status = s_pSecFn->QueryContextAttributes( - &backend->ctxt->ctxt_handle, - SECPKG_ATTR_STREAM_SIZES, - &backend->stream_sizes); - if(sspi_status != SEC_E_OK) { - *err = CURLE_SEND_ERROR; - return -1; - } - } - - /* check if the buffer is longer than the maximum message length */ - if(len > backend->stream_sizes.cbMaximumMessage) { - len = backend->stream_sizes.cbMaximumMessage; - } - - /* calculate the complete message length and allocate a buffer for it */ - data_len = backend->stream_sizes.cbHeader + len + - backend->stream_sizes.cbTrailer; - ptr = (unsigned char *) malloc(data_len); - if(!ptr) { - *err = CURLE_OUT_OF_MEMORY; - return -1; - } - - /* setup output buffers (header, data, trailer, empty) */ - InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER, - ptr, backend->stream_sizes.cbHeader); - InitSecBuffer(&outbuf[1], SECBUFFER_DATA, - ptr + backend->stream_sizes.cbHeader, curlx_uztoul(len)); - InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER, - ptr + backend->stream_sizes.cbHeader + len, - backend->stream_sizes.cbTrailer); - InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&outbuf_desc, outbuf, 4); - - /* copy data into output buffer */ - memcpy(outbuf[1].pvBuffer, buf, len); - - /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */ - sspi_status = s_pSecFn->EncryptMessage(&backend->ctxt->ctxt_handle, 0, - &outbuf_desc, 0); - - /* check if the message was encrypted */ - if(sspi_status == SEC_E_OK) { - written = 0; - - /* send the encrypted message including header, data and trailer */ - len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer; - - /* - It's important to send the full message which includes the header, - encrypted payload, and trailer. Until the client receives all the - data a coherent message has not been delivered and the client - can't read any of it. - - If we wanted to buffer the unwritten encrypted bytes, we would - tell the client that all data it has requested to be sent has been - sent. The unwritten encrypted bytes would be the first bytes to - send on the next invocation. - Here's the catch with this - if we tell the client that all the - bytes have been sent, will the client call this method again to - send the buffered data? Looking at who calls this function, it - seems the answer is NO. - */ - - /* send entire message or fail */ - while(len > (size_t)written) { - ssize_t this_write = 0; - int what; - timediff_t timeout_ms = Curl_timeleft(data, NULL, FALSE); - if(timeout_ms < 0) { - /* we already got the timeout */ - failf(data, "schannel: timed out sending data " - "(bytes sent: %zd)", written); - *err = CURLE_OPERATION_TIMEDOUT; - written = -1; - break; - } - else if(!timeout_ms) - timeout_ms = TIMEDIFF_T_MAX; - what = SOCKET_WRITABLE(conn->sock[sockindex], timeout_ms); - if(what < 0) { - /* fatal error */ - failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - *err = CURLE_SEND_ERROR; - written = -1; - break; - } - else if(0 == what) { - failf(data, "schannel: timed out sending data " - "(bytes sent: %zd)", written); - *err = CURLE_OPERATION_TIMEDOUT; - written = -1; - break; - } - /* socket is writable */ - - result = Curl_write_plain(data, conn->sock[sockindex], ptr + written, - len - written, &this_write); - if(result == CURLE_AGAIN) - continue; - else if(result != CURLE_OK) { - *err = result; - written = -1; - break; - } - - written += this_write; - } - } - else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) { - *err = CURLE_OUT_OF_MEMORY; - } - else{ - *err = CURLE_SEND_ERROR; - } - - Curl_safefree(ptr); - - if(len == (size_t)written) - /* Encrypted message including header, data and trailer entirely sent. - The return value is the number of unencrypted bytes that were sent. */ - written = outbuf[1].cbBuffer; - - return written; -} - -static ssize_t -schannel_recv(struct Curl_easy *data, int sockindex, - char *buf, size_t len, CURLcode *err) -{ - size_t size = 0; - ssize_t nread = -1; - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - unsigned char *reallocated_buffer; - size_t reallocated_length; - bool done = FALSE; - SecBuffer inbuf[4]; - SecBufferDesc inbuf_desc; - SECURITY_STATUS sspi_status = SEC_E_OK; - /* we want the length of the encrypted buffer to be at least large enough - that it can hold all the bytes requested and some TLS record overhead. */ - size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - /**************************************************************************** - * Don't return or set backend->recv_unrecoverable_err unless in the cleanup. - * The pattern for return error is set *err, optional infof, goto cleanup. - * - * Our priority is to always return as much decrypted data to the caller as - * possible, even if an error occurs. The state of the decrypted buffer must - * always be valid. Transfer of decrypted data to the caller's buffer is - * handled in the cleanup. - */ - - DEBUGF(infof(data, "schannel: client wants to read %zu bytes", len)); - *err = CURLE_OK; - - if(len && len <= backend->decdata_offset) { - infof(data, "schannel: enough decrypted data is already available"); - goto cleanup; - } - else if(backend->recv_unrecoverable_err) { - *err = backend->recv_unrecoverable_err; - infof(data, "schannel: an unrecoverable error occurred in a prior call"); - goto cleanup; - } - else if(backend->recv_sspi_close_notify) { - /* once a server has indicated shutdown there is no more encrypted data */ - infof(data, "schannel: server indicated shutdown in a prior call"); - goto cleanup; - } - - /* It's debatable what to return when !len. Regardless we can't return - immediately because there may be data to decrypt (in the case we want to - decrypt all encrypted cached data) so handle !len later in cleanup. - */ - else if(len && !backend->recv_connection_closed) { - /* increase enc buffer in order to fit the requested amount of data */ - size = backend->encdata_length - backend->encdata_offset; - if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE || - backend->encdata_length < min_encdata_length) { - reallocated_length = backend->encdata_offset + - CURL_SCHANNEL_BUFFER_FREE_SIZE; - if(reallocated_length < min_encdata_length) { - reallocated_length = min_encdata_length; - } - reallocated_buffer = realloc(backend->encdata_buffer, - reallocated_length); - if(!reallocated_buffer) { - *err = CURLE_OUT_OF_MEMORY; - failf(data, "schannel: unable to re-allocate memory"); - goto cleanup; - } - - backend->encdata_buffer = reallocated_buffer; - backend->encdata_length = reallocated_length; - size = backend->encdata_length - backend->encdata_offset; - DEBUGF(infof(data, "schannel: encdata_buffer resized %zu", - backend->encdata_length)); - } - - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); - - /* read encrypted data from socket */ - *err = Curl_read_plain(conn->sock[sockindex], - (char *)(backend->encdata_buffer + - backend->encdata_offset), - size, &nread); - if(*err) { - nread = -1; - if(*err == CURLE_AGAIN) - DEBUGF(infof(data, - "schannel: Curl_read_plain returned CURLE_AGAIN")); - else if(*err == CURLE_RECV_ERROR) - infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR"); - else - infof(data, "schannel: Curl_read_plain returned error %d", *err); - } - else if(nread == 0) { - backend->recv_connection_closed = true; - DEBUGF(infof(data, "schannel: server closed the connection")); - } - else if(nread > 0) { - backend->encdata_offset += (size_t)nread; - backend->encdata_is_incomplete = false; - DEBUGF(infof(data, "schannel: encrypted data got %zd", nread)); - } - } - - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); - - /* decrypt loop */ - while(backend->encdata_offset > 0 && sspi_status == SEC_E_OK && - (!len || backend->decdata_offset < len || - backend->recv_connection_closed)) { - /* prepare data buffer for DecryptMessage call */ - InitSecBuffer(&inbuf[0], SECBUFFER_DATA, backend->encdata_buffer, - curlx_uztoul(backend->encdata_offset)); - - /* we need 3 more empty input buffers for possible output */ - InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); - InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0); - InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&inbuf_desc, inbuf, 4); - - /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx - */ - sspi_status = s_pSecFn->DecryptMessage(&backend->ctxt->ctxt_handle, - &inbuf_desc, 0, NULL); - - /* check if everything went fine (server may want to renegotiate - or shutdown the connection context) */ - if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE || - sspi_status == SEC_I_CONTEXT_EXPIRED) { - /* check for successfully decrypted data, even before actual - renegotiation or shutdown of the connection context */ - if(inbuf[1].BufferType == SECBUFFER_DATA) { - DEBUGF(infof(data, "schannel: decrypted data length: %lu", - inbuf[1].cbBuffer)); - - /* increase buffer in order to fit the received amount of data */ - size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ? - inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE; - if(backend->decdata_length - backend->decdata_offset < size || - backend->decdata_length < len) { - /* increase internal decrypted data buffer */ - reallocated_length = backend->decdata_offset + size; - /* make sure that the requested amount of data fits */ - if(reallocated_length < len) { - reallocated_length = len; - } - reallocated_buffer = realloc(backend->decdata_buffer, - reallocated_length); - if(!reallocated_buffer) { - *err = CURLE_OUT_OF_MEMORY; - failf(data, "schannel: unable to re-allocate memory"); - goto cleanup; - } - backend->decdata_buffer = reallocated_buffer; - backend->decdata_length = reallocated_length; - } - - /* copy decrypted data to internal buffer */ - size = inbuf[1].cbBuffer; - if(size) { - memcpy(backend->decdata_buffer + backend->decdata_offset, - inbuf[1].pvBuffer, size); - backend->decdata_offset += size; - } - - DEBUGF(infof(data, "schannel: decrypted data added: %zu", size)); - DEBUGF(infof(data, - "schannel: decrypted cached: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); - } - - /* check for remaining encrypted data */ - if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) { - DEBUGF(infof(data, "schannel: encrypted data length: %lu", - inbuf[3].cbBuffer)); - - /* check if the remaining data is less than the total amount - * and therefore begins after the already processed data - */ - if(backend->encdata_offset > inbuf[3].cbBuffer) { - /* move remaining encrypted data forward to the beginning of - buffer */ - memmove(backend->encdata_buffer, - (backend->encdata_buffer + backend->encdata_offset) - - inbuf[3].cbBuffer, inbuf[3].cbBuffer); - backend->encdata_offset = inbuf[3].cbBuffer; - } - - DEBUGF(infof(data, - "schannel: encrypted cached: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); - } - else { - /* reset encrypted buffer offset, because there is no data remaining */ - backend->encdata_offset = 0; - } - - /* check if server wants to renegotiate the connection context */ - if(sspi_status == SEC_I_RENEGOTIATE) { - infof(data, "schannel: remote party requests renegotiation"); - if(*err && *err != CURLE_AGAIN) { - infof(data, "schannel: can't renegotiate, an error is pending"); - goto cleanup; - } - if(backend->encdata_offset) { - *err = CURLE_RECV_ERROR; - infof(data, "schannel: can't renegotiate, " - "encrypted data available"); - goto cleanup; - } - /* begin renegotiation */ - infof(data, "schannel: renegotiating SSL/TLS connection"); - connssl->state = ssl_connection_negotiating; - connssl->connecting_state = ssl_connect_2_writing; - *err = schannel_connect_common(data, conn, sockindex, FALSE, &done); - if(*err) { - infof(data, "schannel: renegotiation failed"); - goto cleanup; - } - /* now retry receiving data */ - sspi_status = SEC_E_OK; - infof(data, "schannel: SSL/TLS connection renegotiated"); - continue; - } - /* check if the server closed the connection */ - else if(sspi_status == SEC_I_CONTEXT_EXPIRED) { - /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not - returned so we have to work around that in cleanup. */ - backend->recv_sspi_close_notify = true; - if(!backend->recv_connection_closed) { - backend->recv_connection_closed = true; - infof(data, "schannel: server closed the connection"); - } - goto cleanup; - } - } - else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { - backend->encdata_is_incomplete = true; - if(!*err) - *err = CURLE_AGAIN; - infof(data, "schannel: failed to decrypt data, need more data"); - goto cleanup; - } - else { -#ifndef CURL_DISABLE_VERBOSE_STRINGS - char buffer[STRERROR_LEN]; -#endif - *err = CURLE_RECV_ERROR; - infof(data, "schannel: failed to read data from server: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - goto cleanup; - } - } - - DEBUGF(infof(data, - "schannel: encrypted data buffer: offset %zu length %zu", - backend->encdata_offset, backend->encdata_length)); - - DEBUGF(infof(data, - "schannel: decrypted data buffer: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); - - cleanup: - /* Warning- there is no guarantee the encdata state is valid at this point */ - DEBUGF(infof(data, "schannel: schannel_recv cleanup")); - - /* Error if the connection has closed without a close_notify. - - The behavior here is a matter of debate. We don't want to be vulnerable - to a truncation attack however there's some browser precedent for - ignoring the close_notify for compatibility reasons. - - Additionally, Windows 2000 (v5.0) is a special case since it seems it - doesn't return close_notify. In that case if the connection was closed we - assume it was graceful (close_notify) since there doesn't seem to be a - way to tell. - */ - if(len && !backend->decdata_offset && backend->recv_connection_closed && - !backend->recv_sspi_close_notify) { - bool isWin2k = curlx_verify_windows_version(5, 0, 0, PLATFORM_WINNT, - VERSION_EQUAL); - - if(isWin2k && sspi_status == SEC_E_OK) - backend->recv_sspi_close_notify = true; - else { - *err = CURLE_RECV_ERROR; - infof(data, "schannel: server closed abruptly (missing close_notify)"); - } - } - - /* Any error other than CURLE_AGAIN is an unrecoverable error. */ - if(*err && *err != CURLE_AGAIN) - backend->recv_unrecoverable_err = *err; - - size = len < backend->decdata_offset ? len : backend->decdata_offset; - if(size) { - memcpy(buf, backend->decdata_buffer, size); - memmove(backend->decdata_buffer, backend->decdata_buffer + size, - backend->decdata_offset - size); - backend->decdata_offset -= size; - DEBUGF(infof(data, "schannel: decrypted data returned %zu", size)); - DEBUGF(infof(data, - "schannel: decrypted data buffer: offset %zu length %zu", - backend->decdata_offset, backend->decdata_length)); - *err = CURLE_OK; - return (ssize_t)size; - } - - if(!*err && !backend->recv_connection_closed) - *err = CURLE_AGAIN; - - /* It's debatable what to return when !len. We could return whatever error - we got from decryption but instead we override here so the return is - consistent. - */ - if(!len) - *err = CURLE_OK; - - return *err ? -1 : 0; -} - -static CURLcode schannel_connect_nonblocking(struct Curl_easy *data, - struct connectdata *conn, - int sockindex, bool *done) -{ - return schannel_connect_common(data, conn, sockindex, TRUE, done); -} - -static CURLcode schannel_connect(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - CURLcode result; - bool done = FALSE; - - result = schannel_connect_common(data, conn, sockindex, FALSE, &done); - if(result) - return result; - - DEBUGASSERT(done); - - return CURLE_OK; -} - -static bool schannel_data_pending(const struct connectdata *conn, - int sockindex) -{ - const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(backend); - - if(connssl->use) /* SSL/TLS is in use */ - return (backend->decdata_offset > 0 || - (backend->encdata_offset > 0 && !backend->encdata_is_incomplete)); - else - return FALSE; -} - -static void schannel_session_free(void *ptr) -{ - /* this is expected to be called under sessionid lock */ - struct Curl_schannel_cred *cred = ptr; - - if(cred) { - cred->refcount--; - if(cred->refcount == 0) { - s_pSecFn->FreeCredentialsHandle(&cred->cred_handle); - curlx_unicodefree(cred->sni_hostname); - Curl_safefree(cred); - } - } -} - -/* shut down the SSL connection and clean up related memory. - this function can be called multiple times on the same connection including - if the SSL connection failed (eg connection made but failed handshake). */ -static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - /* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx - * Shutting Down an Schannel Connection - */ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - char * const hostname = SSL_HOST_NAME(); - struct ssl_backend_data *backend = connssl->backend; - - DEBUGASSERT(data); - DEBUGASSERT(backend); - - if(connssl->use) { - infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu", - hostname, conn->remote_port); - } - - if(connssl->use && backend->cred && backend->ctxt) { - SecBufferDesc BuffDesc; - SecBuffer Buffer; - SECURITY_STATUS sspi_status; - SecBuffer outbuf; - SecBufferDesc outbuf_desc; - CURLcode result; - DWORD dwshut = SCHANNEL_SHUTDOWN; - - InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut)); - InitSecBufferDesc(&BuffDesc, &Buffer, 1); - - sspi_status = s_pSecFn->ApplyControlToken(&backend->ctxt->ctxt_handle, - &BuffDesc); - - if(sspi_status != SEC_E_OK) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: ApplyControlToken failure: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - } - - /* setup output buffer */ - InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); - InitSecBufferDesc(&outbuf_desc, &outbuf, 1); - - sspi_status = s_pSecFn->InitializeSecurityContext( - &backend->cred->cred_handle, - &backend->ctxt->ctxt_handle, - backend->cred->sni_hostname, - backend->req_flags, - 0, - 0, - NULL, - 0, - &backend->ctxt->ctxt_handle, - &outbuf_desc, - &backend->ret_flags, - &backend->ctxt->time_stamp); - - if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) { - /* send close message which is in output buffer */ - ssize_t written; - result = Curl_write_plain(data, conn->sock[sockindex], outbuf.pvBuffer, - outbuf.cbBuffer, &written); - - s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); - if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) { - infof(data, "schannel: failed to send close msg: %s" - " (bytes written: %zd)", curl_easy_strerror(result), written); - } - } - } - - /* free SSPI Schannel API security context handle */ - if(backend->ctxt) { - DEBUGF(infof(data, "schannel: clear security context handle")); - s_pSecFn->DeleteSecurityContext(&backend->ctxt->ctxt_handle); - Curl_safefree(backend->ctxt); - } - - /* free SSPI Schannel API credential handle */ - if(backend->cred) { - Curl_ssl_sessionid_lock(data); - schannel_session_free(backend->cred); - Curl_ssl_sessionid_unlock(data); - backend->cred = NULL; - } - - /* free internal buffer for received encrypted data */ - if(backend->encdata_buffer) { - Curl_safefree(backend->encdata_buffer); - backend->encdata_length = 0; - backend->encdata_offset = 0; - backend->encdata_is_incomplete = false; - } - - /* free internal buffer for received decrypted data */ - if(backend->decdata_buffer) { - Curl_safefree(backend->decdata_buffer); - backend->decdata_length = 0; - backend->decdata_offset = 0; - } - - return CURLE_OK; -} - -static void schannel_close(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - if(conn->ssl[sockindex].use) - /* Curl_ssl_shutdown resets the socket state and calls schannel_shutdown */ - Curl_ssl_shutdown(data, conn, sockindex); - else - schannel_shutdown(data, conn, sockindex); -} - -static int schannel_init(void) -{ - return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0); -} - -static void schannel_cleanup(void) -{ - Curl_sspi_global_cleanup(); -} - -static size_t schannel_version(char *buffer, size_t size) -{ - size = msnprintf(buffer, size, "Schannel"); - - return size; -} - -static CURLcode schannel_random(struct Curl_easy *data UNUSED_PARAM, - unsigned char *entropy, size_t length) -{ - HCRYPTPROV hCryptProv = 0; - - (void)data; - - if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - return CURLE_FAILED_INIT; - - if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) { - CryptReleaseContext(hCryptProv, 0UL); - return CURLE_FAILED_INIT; - } - - CryptReleaseContext(hCryptProv, 0UL); - return CURLE_OK; -} - -static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, - struct connectdata *conn, int sockindex, - const char *pinnedpubkey) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - CERT_CONTEXT *pCertContextServer = NULL; - - /* Result is returned to caller */ - CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - - DEBUGASSERT(backend); - - /* if a path wasn't specified, don't pin */ - if(!pinnedpubkey) - return CURLE_OK; - - do { - SECURITY_STATUS sspi_status; - const char *x509_der; - DWORD x509_der_len; - struct Curl_X509certificate x509_parsed; - struct Curl_asn1Element *pubkey; - - sspi_status = - s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle, - SECPKG_ATTR_REMOTE_CERT_CONTEXT, - &pCertContextServer); - - if((sspi_status != SEC_E_OK) || !pCertContextServer) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: Failed to read remote certificate context: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - break; /* failed */ - } - - - if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) && - (pCertContextServer->cbCertEncoded > 0))) - break; - - x509_der = (const char *)pCertContextServer->pbCertEncoded; - x509_der_len = pCertContextServer->cbCertEncoded; - memset(&x509_parsed, 0, sizeof(x509_parsed)); - if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len)) - break; - - pubkey = &x509_parsed.subjectPublicKeyInfo; - if(!pubkey->header || pubkey->end <= pubkey->header) { - failf(data, "SSL: failed retrieving public key from server certificate"); - break; - } - - result = Curl_pin_peer_pubkey(data, - pinnedpubkey, - (const unsigned char *)pubkey->header, - (size_t)(pubkey->end - pubkey->header)); - if(result) { - failf(data, "SSL: public key does not match pinned public key"); - } - } while(0); - - if(pCertContextServer) - CertFreeCertificateContext(pCertContextServer); - - return result; -} - -static void schannel_checksum(const unsigned char *input, - size_t inputlen, - unsigned char *checksum, - size_t checksumlen, - DWORD provType, - const unsigned int algId) -{ - HCRYPTPROV hProv = 0; - HCRYPTHASH hHash = 0; - DWORD cbHashSize = 0; - DWORD dwHashSizeLen = (DWORD)sizeof(cbHashSize); - DWORD dwChecksumLen = (DWORD)checksumlen; - - /* since this can fail in multiple ways, zero memory first so we never - * return old data - */ - memset(checksum, 0, checksumlen); - - if(!CryptAcquireContext(&hProv, NULL, NULL, provType, - CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) - return; /* failed */ - - do { - if(!CryptCreateHash(hProv, algId, 0, 0, &hHash)) - break; /* failed */ - - /* workaround for original MinGW, should be (const BYTE*) */ - if(!CryptHashData(hHash, (BYTE*)input, (DWORD)inputlen, 0)) - break; /* failed */ - - /* get hash size */ - if(!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&cbHashSize, - &dwHashSizeLen, 0)) - break; /* failed */ - - /* check hash size */ - if(checksumlen < cbHashSize) - break; /* failed */ - - if(CryptGetHashParam(hHash, HP_HASHVAL, checksum, &dwChecksumLen, 0)) - break; /* failed */ - } while(0); - - if(hHash) - CryptDestroyHash(hHash); - - if(hProv) - CryptReleaseContext(hProv, 0); -} - -static CURLcode schannel_sha256sum(const unsigned char *input, - size_t inputlen, - unsigned char *sha256sum, - size_t sha256len) -{ - schannel_checksum(input, inputlen, sha256sum, sha256len, - PROV_RSA_AES, CALG_SHA_256); - return CURLE_OK; -} - -static void *schannel_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct ssl_backend_data *backend = connssl->backend; - (void)info; - DEBUGASSERT(backend); - return &backend->ctxt->ctxt_handle; -} - -const struct Curl_ssl Curl_ssl_schannel = { - { CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */ - - SSLSUPP_CERTINFO | -#ifdef HAS_MANUAL_VERIFY_API - SSLSUPP_CAINFO_BLOB | -#endif - SSLSUPP_PINNEDPUBKEY, - - sizeof(struct ssl_backend_data), - - schannel_init, /* init */ - schannel_cleanup, /* cleanup */ - schannel_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - schannel_shutdown, /* shutdown */ - schannel_data_pending, /* data_pending */ - schannel_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - schannel_connect, /* connect */ - schannel_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - schannel_get_internals, /* get_internals */ - schannel_close, /* close_one */ - Curl_none_close_all, /* close_all */ - schannel_session_free, /* session_free */ - Curl_none_set_engine, /* set_engine */ - Curl_none_set_engine_default, /* set_engine_default */ - Curl_none_engines_list, /* engines_list */ - Curl_none_false_start, /* false_start */ - schannel_sha256sum, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#endif /* USE_SCHANNEL */ diff --git a/contrib/libs/curl/lib/vtls/schannel_verify.c b/contrib/libs/curl/lib/vtls/schannel_verify.c deleted file mode 100644 index 4dc2d14e56..0000000000 --- a/contrib/libs/curl/lib/vtls/schannel_verify.c +++ /dev/null @@ -1,742 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de> - * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> - * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -/* - * Source file for Schannel-specific certificate verification. This code should - * only be invoked by code in schannel.c. - */ - -#include "curl_setup.h" - -#ifdef USE_SCHANNEL -#ifndef USE_WINDOWS_SSPI -# error "Can't compile SCHANNEL support without SSPI." -#endif - -#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS -#include "schannel.h" - -#ifdef HAS_MANUAL_VERIFY_API - -#include "vtls.h" -#include "sendf.h" -#include "strerror.h" -#include "curl_multibyte.h" -#include "curl_printf.h" -#include "hostcheck.h" -#include "version_win32.h" - -/* The last #include file should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -#define BACKEND connssl->backend - -#define MAX_CAFILE_SIZE 1048576 /* 1 MiB */ -#define BEGIN_CERT "-----BEGIN CERTIFICATE-----" -#define END_CERT "\n-----END CERTIFICATE-----" - -struct cert_chain_engine_config_win7 { - DWORD cbSize; - HCERTSTORE hRestrictedRoot; - HCERTSTORE hRestrictedTrust; - HCERTSTORE hRestrictedOther; - DWORD cAdditionalStore; - HCERTSTORE *rghAdditionalStore; - DWORD dwFlags; - DWORD dwUrlRetrievalTimeout; - DWORD MaximumCachedCertificates; - DWORD CycleDetectionModulus; - HCERTSTORE hExclusiveRoot; - HCERTSTORE hExclusiveTrustedPeople; -}; - -static int is_cr_or_lf(char c) -{ - return c == '\r' || c == '\n'; -} - -/* Search the substring needle,needlelen into string haystack,haystacklen - * Strings don't need to be terminated by a '\0'. - * Similar of OSX/Linux memmem (not available on Visual Studio). - * Return position of beginning of first occurrence or NULL if not found - */ -static const char *c_memmem(const void *haystack, size_t haystacklen, - const void *needle, size_t needlelen) -{ - const char *p; - char first; - const char *str_limit = (const char *)haystack + haystacklen; - if(!needlelen || needlelen > haystacklen) - return NULL; - first = *(const char *)needle; - for(p = (const char *)haystack; p <= (str_limit - needlelen); p++) - if(((*p) == first) && (memcmp(p, needle, needlelen) == 0)) - return p; - - return NULL; -} - -static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, - const char *ca_buffer, - size_t ca_buffer_size, - const char *ca_file_text, - struct Curl_easy *data) -{ - const size_t begin_cert_len = strlen(BEGIN_CERT); - const size_t end_cert_len = strlen(END_CERT); - CURLcode result = CURLE_OK; - int num_certs = 0; - bool more_certs = 1; - const char *current_ca_file_ptr = ca_buffer; - const char *ca_buffer_limit = ca_buffer + ca_buffer_size; - - while(more_certs && (current_ca_file_ptr<ca_buffer_limit)) { - const char *begin_cert_ptr = c_memmem(current_ca_file_ptr, - ca_buffer_limit-current_ca_file_ptr, - BEGIN_CERT, - begin_cert_len); - if(!begin_cert_ptr || !is_cr_or_lf(begin_cert_ptr[begin_cert_len])) { - more_certs = 0; - } - else { - const char *end_cert_ptr = c_memmem(begin_cert_ptr, - ca_buffer_limit-begin_cert_ptr, - END_CERT, - end_cert_len); - if(!end_cert_ptr) { - failf(data, - "schannel: CA file '%s' is not correctly formatted", - ca_file_text); - result = CURLE_SSL_CACERT_BADFILE; - more_certs = 0; - } - else { - CERT_BLOB cert_blob; - CERT_CONTEXT *cert_context = NULL; - BOOL add_cert_result = FALSE; - DWORD actual_content_type = 0; - DWORD cert_size = (DWORD) - ((end_cert_ptr + end_cert_len) - begin_cert_ptr); - - cert_blob.pbData = (BYTE *)begin_cert_ptr; - cert_blob.cbData = cert_size; - if(!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, - &cert_blob, - CERT_QUERY_CONTENT_FLAG_CERT, - CERT_QUERY_FORMAT_FLAG_ALL, - 0, - NULL, - &actual_content_type, - NULL, - NULL, - NULL, - (const void **)&cert_context)) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: failed to extract certificate from CA file " - "'%s': %s", - ca_file_text, - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - more_certs = 0; - } - else { - current_ca_file_ptr = begin_cert_ptr + cert_size; - - /* Sanity check that the cert_context object is the right type */ - if(CERT_QUERY_CONTENT_CERT != actual_content_type) { - failf(data, - "schannel: unexpected content type '%d' when extracting " - "certificate from CA file '%s'", - actual_content_type, ca_file_text); - result = CURLE_SSL_CACERT_BADFILE; - more_certs = 0; - } - else { - add_cert_result = - CertAddCertificateContextToStore(trust_store, - cert_context, - CERT_STORE_ADD_ALWAYS, - NULL); - CertFreeCertificateContext(cert_context); - if(!add_cert_result) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: failed to add certificate from CA file '%s' " - "to certificate store: %s", - ca_file_text, - Curl_winapi_strerror(GetLastError(), buffer, - sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - more_certs = 0; - } - else { - num_certs++; - } - } - } - } - } - } - - if(result == CURLE_OK) { - if(!num_certs) { - infof(data, - "schannel: did not add any certificates from CA file '%s'", - ca_file_text); - } - else { - infof(data, - "schannel: added %d certificate(s) from CA file '%s'", - num_certs, ca_file_text); - } - } - return result; -} - -static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, - const char *ca_file, - struct Curl_easy *data) -{ - CURLcode result; - HANDLE ca_file_handle = INVALID_HANDLE_VALUE; - LARGE_INTEGER file_size; - char *ca_file_buffer = NULL; - TCHAR *ca_file_tstr = NULL; - size_t ca_file_bufsize = 0; - DWORD total_bytes_read = 0; - - ca_file_tstr = curlx_convert_UTF8_to_tchar((char *)ca_file); - if(!ca_file_tstr) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: invalid path name for CA file '%s': %s", - ca_file, - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - goto cleanup; - } - - /* - * Read the CA file completely into memory before parsing it. This - * optimizes for the common case where the CA file will be relatively - * small ( < 1 MiB ). - */ - ca_file_handle = CreateFile(ca_file_tstr, - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - if(ca_file_handle == INVALID_HANDLE_VALUE) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: failed to open CA file '%s': %s", - ca_file, - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - goto cleanup; - } - - if(!GetFileSizeEx(ca_file_handle, &file_size)) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: failed to determine size of CA file '%s': %s", - ca_file, - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - goto cleanup; - } - - if(file_size.QuadPart > MAX_CAFILE_SIZE) { - failf(data, - "schannel: CA file exceeds max size of %u bytes", - MAX_CAFILE_SIZE); - result = CURLE_SSL_CACERT_BADFILE; - goto cleanup; - } - - ca_file_bufsize = (size_t)file_size.QuadPart; - ca_file_buffer = (char *)malloc(ca_file_bufsize + 1); - if(!ca_file_buffer) { - result = CURLE_OUT_OF_MEMORY; - goto cleanup; - } - - while(total_bytes_read < ca_file_bufsize) { - DWORD bytes_to_read = (DWORD)(ca_file_bufsize - total_bytes_read); - DWORD bytes_read = 0; - - if(!ReadFile(ca_file_handle, ca_file_buffer + total_bytes_read, - bytes_to_read, &bytes_read, NULL)) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: failed to read from CA file '%s': %s", - ca_file, - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - goto cleanup; - } - if(bytes_read == 0) { - /* Premature EOF -- adjust the bufsize to the new value */ - ca_file_bufsize = total_bytes_read; - } - else { - total_bytes_read += bytes_read; - } - } - - /* Null terminate the buffer */ - ca_file_buffer[ca_file_bufsize] = '\0'; - - result = add_certs_data_to_store(trust_store, - ca_file_buffer, ca_file_bufsize, - ca_file, - data); - -cleanup: - if(ca_file_handle != INVALID_HANDLE_VALUE) { - CloseHandle(ca_file_handle); - } - Curl_safefree(ca_file_buffer); - curlx_unicodefree(ca_file_tstr); - - return result; -} - -/* - * Returns the number of characters necessary to populate all the host_names. - * If host_names is not NULL, populate it with all the host names. Each string - * in the host_names is null-terminated and the last string is double - * null-terminated. If no DNS names are found, a single null-terminated empty - * string is returned. - */ -static DWORD cert_get_name_string(struct Curl_easy *data, - CERT_CONTEXT *cert_context, - LPTSTR host_names, - DWORD length) -{ - DWORD actual_length = 0; - BOOL compute_content = FALSE; - CERT_INFO *cert_info = NULL; - CERT_EXTENSION *extension = NULL; - CRYPT_DECODE_PARA decode_para = {0, 0, 0}; - CERT_ALT_NAME_INFO *alt_name_info = NULL; - DWORD alt_name_info_size = 0; - BOOL ret_val = FALSE; - LPTSTR current_pos = NULL; - DWORD i; - - /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */ - if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT, - VERSION_GREATER_THAN_EQUAL)) { -#ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG - /* CertGetNameString will provide the 8-bit character string without - * any decoding */ - DWORD name_flags = - CERT_NAME_DISABLE_IE4_UTF8_FLAG | CERT_NAME_SEARCH_ALL_NAMES_FLAG; - actual_length = CertGetNameString(cert_context, - CERT_NAME_DNS_TYPE, - name_flags, - NULL, - host_names, - length); - return actual_length; -#endif - } - - compute_content = host_names != NULL && length != 0; - - /* Initialize default return values. */ - actual_length = 1; - if(compute_content) { - *host_names = '\0'; - } - - if(!cert_context) { - failf(data, "schannel: Null certificate context."); - return actual_length; - } - - cert_info = cert_context->pCertInfo; - if(!cert_info) { - failf(data, "schannel: Null certificate info."); - return actual_length; - } - - extension = CertFindExtension(szOID_SUBJECT_ALT_NAME2, - cert_info->cExtension, - cert_info->rgExtension); - if(!extension) { - failf(data, "schannel: CertFindExtension() returned no extension."); - return actual_length; - } - - decode_para.cbSize = sizeof(CRYPT_DECODE_PARA); - - ret_val = - CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - szOID_SUBJECT_ALT_NAME2, - extension->Value.pbData, - extension->Value.cbData, - CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, - &decode_para, - &alt_name_info, - &alt_name_info_size); - if(!ret_val) { - failf(data, - "schannel: CryptDecodeObjectEx() returned no alternate name " - "information."); - return actual_length; - } - - current_pos = host_names; - - /* Iterate over the alternate names and populate host_names. */ - for(i = 0; i < alt_name_info->cAltEntry; i++) { - const CERT_ALT_NAME_ENTRY *entry = &alt_name_info->rgAltEntry[i]; - wchar_t *dns_w = NULL; - size_t current_length = 0; - - if(entry->dwAltNameChoice != CERT_ALT_NAME_DNS_NAME) { - continue; - } - if(!entry->pwszDNSName) { - infof(data, "schannel: Empty DNS name."); - continue; - } - current_length = wcslen(entry->pwszDNSName) + 1; - if(!compute_content) { - actual_length += (DWORD)current_length; - continue; - } - /* Sanity check to prevent buffer overrun. */ - if((actual_length + current_length) > length) { - failf(data, "schannel: Not enough memory to list all host names."); - break; - } - dns_w = entry->pwszDNSName; - /* pwszDNSName is in ia5 string format and hence doesn't contain any - * non-ascii characters. */ - while(*dns_w != '\0') { - *current_pos++ = (char)(*dns_w++); - } - *current_pos++ = '\0'; - actual_length += (DWORD)current_length; - } - if(compute_content) { - /* Last string has double null-terminator. */ - *current_pos = '\0'; - } - return actual_length; -} - -static CURLcode verify_host(struct Curl_easy *data, - CERT_CONTEXT *pCertContextServer, - const char * const conn_hostname) -{ - CURLcode result = CURLE_PEER_FAILED_VERIFICATION; - TCHAR *cert_hostname_buff = NULL; - size_t cert_hostname_buff_index = 0; - size_t hostlen = strlen(conn_hostname); - DWORD len = 0; - DWORD actual_len = 0; - - /* Determine the size of the string needed for the cert hostname */ - len = cert_get_name_string(data, pCertContextServer, NULL, 0); - if(len == 0) { - failf(data, - "schannel: CertGetNameString() returned no " - "certificate name information"); - result = CURLE_PEER_FAILED_VERIFICATION; - goto cleanup; - } - - /* CertGetNameString guarantees that the returned name will not contain - * embedded null bytes. This appears to be undocumented behavior. - */ - cert_hostname_buff = (LPTSTR)malloc(len * sizeof(TCHAR)); - if(!cert_hostname_buff) { - result = CURLE_OUT_OF_MEMORY; - goto cleanup; - } - actual_len = cert_get_name_string( - data, pCertContextServer, (LPTSTR)cert_hostname_buff, len); - - /* Sanity check */ - if(actual_len != len) { - failf(data, - "schannel: CertGetNameString() returned certificate " - "name information of unexpected size"); - result = CURLE_PEER_FAILED_VERIFICATION; - goto cleanup; - } - - /* If HAVE_CERT_NAME_SEARCH_ALL_NAMES is available, the output - * will contain all DNS names, where each name is null-terminated - * and the last DNS name is double null-terminated. Due to this - * encoding, use the length of the buffer to iterate over all names. - */ - result = CURLE_PEER_FAILED_VERIFICATION; - while(cert_hostname_buff_index < len && - cert_hostname_buff[cert_hostname_buff_index] != TEXT('\0') && - result == CURLE_PEER_FAILED_VERIFICATION) { - - char *cert_hostname; - - /* Comparing the cert name and the connection hostname encoded as UTF-8 - * is acceptable since both values are assumed to use ASCII - * (or some equivalent) encoding - */ - cert_hostname = curlx_convert_tchar_to_UTF8( - &cert_hostname_buff[cert_hostname_buff_index]); - if(!cert_hostname) { - result = CURLE_OUT_OF_MEMORY; - } - else { - if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname), - conn_hostname, hostlen)) { - infof(data, - "schannel: connection hostname (%s) validated " - "against certificate name (%s)", - conn_hostname, cert_hostname); - result = CURLE_OK; - } - else { - size_t cert_hostname_len; - - infof(data, - "schannel: connection hostname (%s) did not match " - "against certificate name (%s)", - conn_hostname, cert_hostname); - - cert_hostname_len = - _tcslen(&cert_hostname_buff[cert_hostname_buff_index]); - - /* Move on to next cert name */ - cert_hostname_buff_index += cert_hostname_len + 1; - - result = CURLE_PEER_FAILED_VERIFICATION; - } - curlx_unicodefree(cert_hostname); - } - } - - if(result == CURLE_PEER_FAILED_VERIFICATION) { - failf(data, - "schannel: CertGetNameString() failed to match " - "connection hostname (%s) against server certificate names", - conn_hostname); - } - else if(result != CURLE_OK) - failf(data, "schannel: server certificate name verification failed"); - -cleanup: - Curl_safefree(cert_hostname_buff); - - return result; -} - -CURLcode Curl_verify_certificate(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - SECURITY_STATUS sspi_status; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - CURLcode result = CURLE_OK; - CERT_CONTEXT *pCertContextServer = NULL; - const CERT_CHAIN_CONTEXT *pChainContext = NULL; - HCERTCHAINENGINE cert_chain_engine = NULL; - HCERTSTORE trust_store = NULL; - const char * const conn_hostname = SSL_HOST_NAME(); - - DEBUGASSERT(BACKEND); - - sspi_status = - s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, - SECPKG_ATTR_REMOTE_CERT_CONTEXT, - &pCertContextServer); - - if((sspi_status != SEC_E_OK) || !pCertContextServer) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: Failed to read remote certificate context: %s", - Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); - result = CURLE_PEER_FAILED_VERIFICATION; - } - - if(result == CURLE_OK && - (SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) && - BACKEND->use_manual_cred_validation) { - /* - * Create a chain engine that uses the certificates in the CA file as - * trusted certificates. This is only supported on Windows 7+. - */ - - if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT, - VERSION_LESS_THAN)) { - failf(data, "schannel: this version of Windows is too old to support " - "certificate verification via CA bundle file."); - result = CURLE_SSL_CACERT_BADFILE; - } - else { - /* Open the certificate store */ - trust_store = CertOpenStore(CERT_STORE_PROV_MEMORY, - 0, - (HCRYPTPROV)NULL, - CERT_STORE_CREATE_NEW_FLAG, - NULL); - if(!trust_store) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: failed to create certificate store: %s", - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - } - else { - const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); - if(ca_info_blob) { - result = add_certs_data_to_store(trust_store, - (const char *)ca_info_blob->data, - ca_info_blob->len, - "(memory blob)", - data); - } - else { - result = add_certs_file_to_store(trust_store, - SSL_CONN_CONFIG(CAfile), - data); - } - } - } - - if(result == CURLE_OK) { - struct cert_chain_engine_config_win7 engine_config; - BOOL create_engine_result; - - memset(&engine_config, 0, sizeof(engine_config)); - engine_config.cbSize = sizeof(engine_config); - engine_config.hExclusiveRoot = trust_store; - - /* CertCreateCertificateChainEngine will check the expected size of the - * CERT_CHAIN_ENGINE_CONFIG structure and fail if the specified size - * does not match the expected size. When this occurs, it indicates that - * CAINFO is not supported on the version of Windows in use. - */ - create_engine_result = - CertCreateCertificateChainEngine( - (CERT_CHAIN_ENGINE_CONFIG *)&engine_config, &cert_chain_engine); - if(!create_engine_result) { - char buffer[STRERROR_LEN]; - failf(data, - "schannel: failed to create certificate chain engine: %s", - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - result = CURLE_SSL_CACERT_BADFILE; - } - } - } - - if(result == CURLE_OK) { - CERT_CHAIN_PARA ChainPara; - - memset(&ChainPara, 0, sizeof(ChainPara)); - ChainPara.cbSize = sizeof(ChainPara); - - if(!CertGetCertificateChain(cert_chain_engine, - pCertContextServer, - NULL, - pCertContextServer->hCertStore, - &ChainPara, - (SSL_SET_OPTION(no_revoke) ? 0 : - CERT_CHAIN_REVOCATION_CHECK_CHAIN), - NULL, - &pChainContext)) { - char buffer[STRERROR_LEN]; - failf(data, "schannel: CertGetCertificateChain failed: %s", - Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer))); - pChainContext = NULL; - result = CURLE_PEER_FAILED_VERIFICATION; - } - - if(result == CURLE_OK) { - CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0]; - DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED); - dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus; - - if(data->set.ssl.revoke_best_effort) { - /* Ignore errors when root certificates are missing the revocation - * list URL, or when the list could not be downloaded because the - * server is currently unreachable. */ - dwTrustErrorMask &= ~(DWORD)(CERT_TRUST_REVOCATION_STATUS_UNKNOWN | - CERT_TRUST_IS_OFFLINE_REVOCATION); - } - - if(dwTrustErrorMask) { - if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_REVOKED"); - else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_PARTIAL_CHAIN"); - else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_UNTRUSTED_ROOT"); - else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_IS_NOT_TIME_VALID"); - else if(dwTrustErrorMask & CERT_TRUST_REVOCATION_STATUS_UNKNOWN) - failf(data, "schannel: CertGetCertificateChain trust error" - " CERT_TRUST_REVOCATION_STATUS_UNKNOWN"); - else - failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x", - dwTrustErrorMask); - result = CURLE_PEER_FAILED_VERIFICATION; - } - } - } - - if(result == CURLE_OK) { - if(SSL_CONN_CONFIG(verifyhost)) { - result = verify_host(data, pCertContextServer, conn_hostname); - } - } - - if(cert_chain_engine) { - CertFreeCertificateChainEngine(cert_chain_engine); - } - - if(trust_store) { - CertCloseStore(trust_store, 0); - } - - if(pChainContext) - CertFreeCertificateChain(pChainContext); - - if(pCertContextServer) - CertFreeCertificateContext(pCertContextServer); - - return result; -} - -#endif /* HAS_MANUAL_VERIFY_API */ -#endif /* USE_SCHANNEL */ |