aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/curl/lib/vtls
diff options
context:
space:
mode:
authormonster <monster@ydb.tech>2022-07-07 14:41:37 +0300
committermonster <monster@ydb.tech>2022-07-07 14:41:37 +0300
commit06e5c21a835c0e923506c4ff27929f34e00761c2 (patch)
tree75efcbc6854ef9bd476eb8bf00cc5c900da436a2 /contrib/libs/curl/lib/vtls
parent03f024c4412e3aa613bb543cf1660176320ba8f4 (diff)
downloadydb-06e5c21a835c0e923506c4ff27929f34e00761c2.tar.gz
fix ya.make
Diffstat (limited to 'contrib/libs/curl/lib/vtls')
-rw-r--r--contrib/libs/curl/lib/vtls/gskit.c1327
-rw-r--r--contrib/libs/curl/lib/vtls/gtls.c1702
-rw-r--r--contrib/libs/curl/lib/vtls/mbedtls.c1271
-rw-r--r--contrib/libs/curl/lib/vtls/nss.c2541
-rw-r--r--contrib/libs/curl/lib/vtls/schannel.c2488
-rw-r--r--contrib/libs/curl/lib/vtls/schannel_verify.c742
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, &notBefore, &notAfter);
- 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 */