aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/curl/lib/vtls
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /contrib/libs/curl/lib/vtls
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'contrib/libs/curl/lib/vtls')
-rw-r--r--contrib/libs/curl/lib/vtls/bearssl.c877
-rw-r--r--contrib/libs/curl/lib/vtls/bearssl.h32
-rw-r--r--contrib/libs/curl/lib/vtls/gskit.c1288
-rw-r--r--contrib/libs/curl/lib/vtls/gskit.h38
-rw-r--r--contrib/libs/curl/lib/vtls/gtls.c1698
-rw-r--r--contrib/libs/curl/lib/vtls/gtls.h34
-rw-r--r--contrib/libs/curl/lib/vtls/keylog.c156
-rw-r--r--contrib/libs/curl/lib/vtls/keylog.h56
-rw-r--r--contrib/libs/curl/lib/vtls/mbedtls.c1112
-rw-r--r--contrib/libs/curl/lib/vtls/mbedtls.h32
-rw-r--r--contrib/libs/curl/lib/vtls/mbedtls_threadlock.c144
-rw-r--r--contrib/libs/curl/lib/vtls/mesalink.c661
-rw-r--r--contrib/libs/curl/lib/vtls/mesalink.h32
-rw-r--r--contrib/libs/curl/lib/vtls/nss.c2464
-rw-r--r--contrib/libs/curl/lib/vtls/nssg.h39
-rw-r--r--contrib/libs/curl/lib/vtls/openssl.c4488
-rw-r--r--contrib/libs/curl/lib/vtls/openssl.h37
-rw-r--r--contrib/libs/curl/lib/vtls/schannel.c2444
-rw-r--r--contrib/libs/curl/lib/vtls/schannel.h108
-rw-r--r--contrib/libs/curl/lib/vtls/schannel_verify.c700
-rw-r--r--contrib/libs/curl/lib/vtls/sectransp.c3326
-rw-r--r--contrib/libs/curl/lib/vtls/sectransp.h32
-rw-r--r--contrib/libs/curl/lib/vtls/vtls.c1426
-rw-r--r--contrib/libs/curl/lib/vtls/vtls.h291
-rw-r--r--contrib/libs/curl/lib/vtls/wolfssl.c1149
-rw-r--r--contrib/libs/curl/lib/vtls/wolfssl.h31
26 files changed, 22695 insertions, 0 deletions
diff --git a/contrib/libs/curl/lib/vtls/bearssl.c b/contrib/libs/curl/lib/vtls/bearssl.c
new file mode 100644
index 0000000000..b0c3dc2f07
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/bearssl.c
@@ -0,0 +1,877 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2019 - 2020, Michael Forney, <mforney@mforney.org>
+ *
+ * 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_BEARSSL
+
+#include <bearssl.h>
+
+#include "bearssl.h"
+#include "urldata.h"
+#include "sendf.h"
+#include "inet_pton.h"
+#include "vtls.h"
+#include "connect.h"
+#include "select.h"
+#include "multiif.h"
+#include "curl_printf.h"
+#include "curl_memory.h"
+
+struct x509_context {
+ const br_x509_class *vtable;
+ br_x509_minimal_context minimal;
+ bool verifyhost;
+ bool verifypeer;
+};
+
+struct ssl_backend_data {
+ br_ssl_client_context ctx;
+ struct x509_context x509;
+ unsigned char buf[BR_SSL_BUFSIZE_BIDI];
+ br_x509_trust_anchor *anchors;
+ size_t anchors_len;
+ const char *protocols[2];
+ /* SSL client context is active */
+ bool active;
+ /* size of pending write, yet to be flushed */
+ size_t pending_write;
+};
+
+struct cafile_parser {
+ CURLcode err;
+ bool in_cert;
+ br_x509_decoder_context xc;
+ /* array of trust anchors loaded from CAfile */
+ br_x509_trust_anchor *anchors;
+ size_t anchors_len;
+ /* buffer for DN data */
+ unsigned char dn[1024];
+ size_t dn_len;
+};
+
+static void append_dn(void *ctx, const void *buf, size_t len)
+{
+ struct cafile_parser *ca = ctx;
+
+ if(ca->err != CURLE_OK || !ca->in_cert)
+ return;
+ if(sizeof(ca->dn) - ca->dn_len < len) {
+ ca->err = CURLE_FAILED_INIT;
+ return;
+ }
+ memcpy(ca->dn + ca->dn_len, buf, len);
+ ca->dn_len += len;
+}
+
+static void x509_push(void *ctx, const void *buf, size_t len)
+{
+ struct cafile_parser *ca = ctx;
+
+ if(ca->in_cert)
+ br_x509_decoder_push(&ca->xc, buf, len);
+}
+
+static CURLcode load_cafile(const char *path, br_x509_trust_anchor **anchors,
+ size_t *anchors_len)
+{
+ struct cafile_parser ca;
+ br_pem_decoder_context pc;
+ br_x509_trust_anchor *ta;
+ size_t ta_size;
+ br_x509_trust_anchor *new_anchors;
+ size_t new_anchors_len;
+ br_x509_pkey *pkey;
+ FILE *fp;
+ unsigned char buf[BUFSIZ], *p;
+ const char *name;
+ size_t n, i, pushed;
+
+ fp = fopen(path, "rb");
+ if(!fp)
+ return CURLE_SSL_CACERT_BADFILE;
+
+ ca.err = CURLE_OK;
+ ca.in_cert = FALSE;
+ ca.anchors = NULL;
+ ca.anchors_len = 0;
+ br_pem_decoder_init(&pc);
+ br_pem_decoder_setdest(&pc, x509_push, &ca);
+ for(;;) {
+ n = fread(buf, 1, sizeof(buf), fp);
+ if(n == 0)
+ break;
+ p = buf;
+ while(n) {
+ pushed = br_pem_decoder_push(&pc, p, n);
+ if(ca.err)
+ goto fail;
+ p += pushed;
+ n -= pushed;
+
+ switch(br_pem_decoder_event(&pc)) {
+ case 0:
+ break;
+ case BR_PEM_BEGIN_OBJ:
+ name = br_pem_decoder_name(&pc);
+ if(strcmp(name, "CERTIFICATE") && strcmp(name, "X509 CERTIFICATE"))
+ break;
+ br_x509_decoder_init(&ca.xc, append_dn, &ca);
+ if(ca.anchors_len == SIZE_MAX / sizeof(ca.anchors[0])) {
+ ca.err = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ new_anchors_len = ca.anchors_len + 1;
+ new_anchors = realloc(ca.anchors,
+ new_anchors_len * sizeof(ca.anchors[0]));
+ if(!new_anchors) {
+ ca.err = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ ca.anchors = new_anchors;
+ ca.anchors_len = new_anchors_len;
+ ca.in_cert = TRUE;
+ ca.dn_len = 0;
+ ta = &ca.anchors[ca.anchors_len - 1];
+ ta->dn.data = NULL;
+ break;
+ case BR_PEM_END_OBJ:
+ if(!ca.in_cert)
+ break;
+ ca.in_cert = FALSE;
+ if(br_x509_decoder_last_error(&ca.xc)) {
+ ca.err = CURLE_SSL_CACERT_BADFILE;
+ goto fail;
+ }
+ ta->flags = 0;
+ if(br_x509_decoder_isCA(&ca.xc))
+ ta->flags |= BR_X509_TA_CA;
+ pkey = br_x509_decoder_get_pkey(&ca.xc);
+ if(!pkey) {
+ ca.err = CURLE_SSL_CACERT_BADFILE;
+ goto fail;
+ }
+ ta->pkey = *pkey;
+
+ /* calculate space needed for trust anchor data */
+ ta_size = ca.dn_len;
+ switch(pkey->key_type) {
+ case BR_KEYTYPE_RSA:
+ ta_size += pkey->key.rsa.nlen + pkey->key.rsa.elen;
+ break;
+ case BR_KEYTYPE_EC:
+ ta_size += pkey->key.ec.qlen;
+ break;
+ default:
+ ca.err = CURLE_FAILED_INIT;
+ goto fail;
+ }
+
+ /* fill in trust anchor DN and public key data */
+ ta->dn.data = malloc(ta_size);
+ if(!ta->dn.data) {
+ ca.err = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ memcpy(ta->dn.data, ca.dn, ca.dn_len);
+ ta->dn.len = ca.dn_len;
+ switch(pkey->key_type) {
+ case BR_KEYTYPE_RSA:
+ ta->pkey.key.rsa.n = ta->dn.data + ta->dn.len;
+ memcpy(ta->pkey.key.rsa.n, pkey->key.rsa.n, pkey->key.rsa.nlen);
+ ta->pkey.key.rsa.e = ta->pkey.key.rsa.n + ta->pkey.key.rsa.nlen;
+ memcpy(ta->pkey.key.rsa.e, pkey->key.rsa.e, pkey->key.rsa.elen);
+ break;
+ case BR_KEYTYPE_EC:
+ ta->pkey.key.ec.q = ta->dn.data + ta->dn.len;
+ memcpy(ta->pkey.key.ec.q, pkey->key.ec.q, pkey->key.ec.qlen);
+ break;
+ }
+ break;
+ default:
+ ca.err = CURLE_SSL_CACERT_BADFILE;
+ goto fail;
+ }
+ }
+ }
+ if(ferror(fp))
+ ca.err = CURLE_READ_ERROR;
+
+fail:
+ fclose(fp);
+ if(ca.err == CURLE_OK) {
+ *anchors = ca.anchors;
+ *anchors_len = ca.anchors_len;
+ }
+ else {
+ for(i = 0; i < ca.anchors_len; ++i)
+ free(ca.anchors[i].dn.data);
+ free(ca.anchors);
+ }
+
+ return ca.err;
+}
+
+static void x509_start_chain(const br_x509_class **ctx,
+ const char *server_name)
+{
+ struct x509_context *x509 = (struct x509_context *)ctx;
+
+ if(!x509->verifyhost)
+ server_name = NULL;
+ x509->minimal.vtable->start_chain(&x509->minimal.vtable, server_name);
+}
+
+static void x509_start_cert(const br_x509_class **ctx, uint32_t length)
+{
+ struct x509_context *x509 = (struct x509_context *)ctx;
+
+ x509->minimal.vtable->start_cert(&x509->minimal.vtable, length);
+}
+
+static void x509_append(const br_x509_class **ctx, const unsigned char *buf,
+ size_t len)
+{
+ struct x509_context *x509 = (struct x509_context *)ctx;
+
+ x509->minimal.vtable->append(&x509->minimal.vtable, buf, len);
+}
+
+static void x509_end_cert(const br_x509_class **ctx)
+{
+ struct x509_context *x509 = (struct x509_context *)ctx;
+
+ x509->minimal.vtable->end_cert(&x509->minimal.vtable);
+}
+
+static unsigned x509_end_chain(const br_x509_class **ctx)
+{
+ struct x509_context *x509 = (struct x509_context *)ctx;
+ unsigned err;
+
+ err = x509->minimal.vtable->end_chain(&x509->minimal.vtable);
+ if(err && !x509->verifypeer) {
+ /* ignore any X.509 errors */
+ err = BR_ERR_OK;
+ }
+
+ return err;
+}
+
+static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx,
+ unsigned *usages)
+{
+ struct x509_context *x509 = (struct x509_context *)ctx;
+
+ return x509->minimal.vtable->get_pkey(&x509->minimal.vtable, usages);
+}
+
+static const br_x509_class x509_vtable = {
+ sizeof(struct x509_context),
+ x509_start_chain,
+ x509_start_cert,
+ x509_append,
+ x509_end_cert,
+ x509_end_chain,
+ x509_get_pkey
+};
+
+static CURLcode bearssl_connect_step1(struct connectdata *conn, int sockindex)
+{
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile);
+#ifndef CURL_DISABLE_PROXY
+ const char *hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
+ conn->host.name;
+#else
+ const char *hostname = conn->host.name;
+#endif
+ const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
+ const bool verifyhost = SSL_CONN_CONFIG(verifyhost);
+ CURLcode ret;
+ unsigned version_min, version_max;
+#ifdef ENABLE_IPV6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif
+
+ switch(SSL_CONN_CONFIG(version)) {
+ case CURL_SSLVERSION_SSLv2:
+ failf(data, "BearSSL does not support SSLv2");
+ return CURLE_SSL_CONNECT_ERROR;
+ case CURL_SSLVERSION_SSLv3:
+ failf(data, "BearSSL does not support SSLv3");
+ return CURLE_SSL_CONNECT_ERROR;
+ case CURL_SSLVERSION_TLSv1_0:
+ version_min = BR_TLS10;
+ version_max = BR_TLS10;
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+ version_min = BR_TLS11;
+ version_max = BR_TLS11;
+ break;
+ case CURL_SSLVERSION_TLSv1_2:
+ version_min = BR_TLS12;
+ version_max = BR_TLS12;
+ break;
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ version_min = BR_TLS10;
+ version_max = BR_TLS12;
+ break;
+ default:
+ failf(data, "BearSSL: unknown CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(ssl_cafile) {
+ ret = load_cafile(ssl_cafile, &backend->anchors, &backend->anchors_len);
+ if(ret != CURLE_OK) {
+ if(verifypeer) {
+ failf(data, "error setting certificate verify locations:\n"
+ " CAfile: %s\n", ssl_cafile);
+ return ret;
+ }
+ infof(data, "error setting certificate verify locations,"
+ " continuing anyway:\n");
+ }
+ }
+
+ /* initialize SSL context */
+ br_ssl_client_init_full(&backend->ctx, &backend->x509.minimal,
+ backend->anchors, backend->anchors_len);
+ br_ssl_engine_set_versions(&backend->ctx.eng, version_min, version_max);
+ br_ssl_engine_set_buffer(&backend->ctx.eng, backend->buf,
+ sizeof(backend->buf), 1);
+
+ /* initialize X.509 context */
+ backend->x509.vtable = &x509_vtable;
+ backend->x509.verifypeer = verifypeer;
+ backend->x509.verifyhost = verifyhost;
+ br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable);
+
+ if(SSL_SET_OPTION(primary.sessionid)) {
+ void *session;
+
+ Curl_ssl_sessionid_lock(conn);
+ if(!Curl_ssl_getsessionid(conn, &session, NULL, sockindex)) {
+ br_ssl_engine_set_session_parameters(&backend->ctx.eng, session);
+ infof(data, "BearSSL: re-using session ID\n");
+ }
+ Curl_ssl_sessionid_unlock(conn);
+ }
+
+ if(conn->bits.tls_enable_alpn) {
+ int cur = 0;
+
+ /* NOTE: when adding more protocols here, increase the size of the
+ * protocols array in `struct ssl_backend_data`.
+ */
+
+#ifdef USE_NGHTTP2
+ if(data->set.httpversion >= CURL_HTTP_VERSION_2
+#ifndef CURL_DISABLE_PROXY
+ && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy)
+#endif
+ ) {
+ backend->protocols[cur++] = NGHTTP2_PROTO_VERSION_ID;
+ infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
+ }
+#endif
+
+ backend->protocols[cur++] = ALPN_HTTP_1_1;
+ infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1);
+
+ br_ssl_engine_set_protocol_names(&backend->ctx.eng,
+ backend->protocols, cur);
+ }
+
+ if((1 == Curl_inet_pton(AF_INET, hostname, &addr))
+#ifdef ENABLE_IPV6
+ || (1 == Curl_inet_pton(AF_INET6, hostname, &addr))
+#endif
+ ) {
+ if(verifyhost) {
+ failf(data, "BearSSL: "
+ "host verification of IP address is not supported");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ hostname = NULL;
+ }
+
+ if(!br_ssl_client_reset(&backend->ctx, hostname, 0))
+ return CURLE_FAILED_INIT;
+ backend->active = TRUE;
+
+ connssl->connecting_state = ssl_connect_2;
+
+ return CURLE_OK;
+}
+
+static CURLcode bearssl_run_until(struct connectdata *conn, int sockindex,
+ unsigned target)
+{
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ curl_socket_t sockfd = conn->sock[sockindex];
+ unsigned state;
+ unsigned char *buf;
+ size_t len;
+ ssize_t ret;
+ int err;
+
+ for(;;) {
+ state = br_ssl_engine_current_state(&backend->ctx.eng);
+ if(state & BR_SSL_CLOSED) {
+ err = br_ssl_engine_last_error(&backend->ctx.eng);
+ switch(err) {
+ case BR_ERR_OK:
+ /* TLS close notify */
+ if(connssl->state != ssl_connection_complete) {
+ failf(data, "SSL: connection closed during handshake");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ return CURLE_OK;
+ case BR_ERR_X509_EXPIRED:
+ failf(data, "SSL: X.509 verification: "
+ "certificate is expired or not yet valid");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case BR_ERR_X509_BAD_SERVER_NAME:
+ failf(data, "SSL: X.509 verification: "
+ "expected server name was not found in the chain");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case BR_ERR_X509_NOT_TRUSTED:
+ failf(data, "SSL: X.509 verification: "
+ "chain could not be linked to a trust anchor");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ /* X.509 errors are documented to have the range 32..63 */
+ if(err >= 32 && err < 64)
+ return CURLE_PEER_FAILED_VERIFICATION;
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ if(state & target)
+ return CURLE_OK;
+ if(state & BR_SSL_SENDREC) {
+ buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len);
+ ret = swrite(sockfd, buf, len);
+ if(ret == -1) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ if(connssl->state != ssl_connection_complete)
+ connssl->connecting_state = ssl_connect_2_writing;
+ return CURLE_AGAIN;
+ }
+ return CURLE_WRITE_ERROR;
+ }
+ br_ssl_engine_sendrec_ack(&backend->ctx.eng, ret);
+ }
+ else if(state & BR_SSL_RECVREC) {
+ buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len);
+ ret = sread(sockfd, buf, len);
+ if(ret == 0) {
+ failf(data, "SSL: EOF without close notify");
+ return CURLE_READ_ERROR;
+ }
+ if(ret == -1) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ if(connssl->state != ssl_connection_complete)
+ connssl->connecting_state = ssl_connect_2_reading;
+ return CURLE_AGAIN;
+ }
+ return CURLE_READ_ERROR;
+ }
+ br_ssl_engine_recvrec_ack(&backend->ctx.eng, ret);
+ }
+ }
+}
+
+static CURLcode bearssl_connect_step2(struct connectdata *conn, int sockindex)
+{
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ CURLcode ret;
+
+ ret = bearssl_run_until(conn, sockindex, BR_SSL_SENDAPP | BR_SSL_RECVAPP);
+ if(ret == CURLE_AGAIN)
+ return CURLE_OK;
+ if(ret == CURLE_OK) {
+ if(br_ssl_engine_current_state(&backend->ctx.eng) == BR_SSL_CLOSED) {
+ failf(data, "SSL: connection closed during handshake");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ connssl->connecting_state = ssl_connect_3;
+ }
+ return ret;
+}
+
+static CURLcode bearssl_connect_step3(struct connectdata *conn, int sockindex)
+{
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ CURLcode ret;
+
+ DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+
+ if(conn->bits.tls_enable_alpn) {
+ const char *protocol;
+
+ protocol = br_ssl_engine_get_selected_protocol(&backend->ctx.eng);
+ if(protocol) {
+ infof(data, "ALPN, server accepted to use %s\n", protocol);
+
+#ifdef USE_NGHTTP2
+ if(!strcmp(protocol, NGHTTP2_PROTO_VERSION_ID))
+ conn->negnpn = CURL_HTTP_VERSION_2;
+ else
+#endif
+ if(!strcmp(protocol, ALPN_HTTP_1_1))
+ conn->negnpn = CURL_HTTP_VERSION_1_1;
+ else
+ infof(data, "ALPN, unrecognized protocol %s\n", protocol);
+ Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ }
+ else
+ infof(data, "ALPN, server did not agree to a protocol\n");
+ }
+
+ if(SSL_SET_OPTION(primary.sessionid)) {
+ bool incache;
+ void *oldsession;
+ br_ssl_session_parameters *session;
+
+ session = malloc(sizeof(*session));
+ if(!session)
+ return CURLE_OUT_OF_MEMORY;
+ br_ssl_engine_get_session_parameters(&backend->ctx.eng, session);
+ Curl_ssl_sessionid_lock(conn);
+ incache = !(Curl_ssl_getsessionid(conn, &oldsession, NULL, sockindex));
+ if(incache)
+ Curl_ssl_delsessionid(conn, oldsession);
+ ret = Curl_ssl_addsessionid(conn, session, 0, sockindex);
+ Curl_ssl_sessionid_unlock(conn);
+ if(ret) {
+ free(session);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ connssl->connecting_state = ssl_connect_done;
+
+ return CURLE_OK;
+}
+
+static ssize_t bearssl_send(struct connectdata *conn, int sockindex,
+ const void *buf, size_t len, CURLcode *err)
+{
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ unsigned char *app;
+ size_t applen;
+
+ for(;;) {
+ *err = bearssl_run_until(conn, sockindex, BR_SSL_SENDAPP);
+ if (*err != CURLE_OK)
+ return -1;
+ app = br_ssl_engine_sendapp_buf(&backend->ctx.eng, &applen);
+ if(!app) {
+ failf(data, "SSL: connection closed during write");
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ if(backend->pending_write) {
+ applen = backend->pending_write;
+ backend->pending_write = 0;
+ return applen;
+ }
+ if(applen > len)
+ applen = len;
+ memcpy(app, buf, applen);
+ br_ssl_engine_sendapp_ack(&backend->ctx.eng, applen);
+ br_ssl_engine_flush(&backend->ctx.eng, 0);
+ backend->pending_write = applen;
+ }
+}
+
+static ssize_t bearssl_recv(struct connectdata *conn, int sockindex,
+ char *buf, size_t len, CURLcode *err)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ unsigned char *app;
+ size_t applen;
+
+ *err = bearssl_run_until(conn, sockindex, BR_SSL_RECVAPP);
+ if(*err != CURLE_OK)
+ return -1;
+ app = br_ssl_engine_recvapp_buf(&backend->ctx.eng, &applen);
+ if(!app)
+ return 0;
+ if(applen > len)
+ applen = len;
+ memcpy(buf, app, applen);
+ br_ssl_engine_recvapp_ack(&backend->ctx.eng, applen);
+
+ return applen;
+}
+
+static CURLcode bearssl_connect_common(struct connectdata *conn,
+ int sockindex,
+ bool nonblocking,
+ bool *done)
+{
+ CURLcode ret;
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ curl_socket_t sockfd = conn->sock[sockindex];
+ timediff_t timeout_ms;
+ int what;
+
+ /* check if the connection has already been established */
+ if(ssl_connection_complete == connssl->state) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ if(ssl_connect_1 == connssl->connecting_state) {
+ ret = bearssl_connect_step1(conn, sockindex);
+ if(ret)
+ return ret;
+ }
+
+ 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(ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state) {
+
+ 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 done nonblocking 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.
+ */
+ ret = bearssl_connect_step2(conn, sockindex);
+ if(ret || (nonblocking &&
+ (ssl_connect_2 == connssl->connecting_state ||
+ ssl_connect_2_reading == connssl->connecting_state ||
+ ssl_connect_2_writing == connssl->connecting_state)))
+ return ret;
+ }
+
+ if(ssl_connect_3 == connssl->connecting_state) {
+ ret = bearssl_connect_step3(conn, sockindex);
+ if(ret)
+ return ret;
+ }
+
+ if(ssl_connect_done == connssl->connecting_state) {
+ connssl->state = ssl_connection_complete;
+ conn->recv[sockindex] = bearssl_recv;
+ conn->send[sockindex] = bearssl_send;
+ *done = TRUE;
+ }
+ else
+ *done = FALSE;
+
+ /* Reset our connect state machine */
+ connssl->connecting_state = ssl_connect_1;
+
+ return CURLE_OK;
+}
+
+static size_t Curl_bearssl_version(char *buffer, size_t size)
+{
+ return msnprintf(buffer, size, "BearSSL");
+}
+
+static bool Curl_bearssl_data_pending(const struct connectdata *conn,
+ int connindex)
+{
+ const struct ssl_connect_data *connssl = &conn->ssl[connindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP;
+}
+
+static CURLcode Curl_bearssl_random(struct Curl_easy *data UNUSED_PARAM,
+ unsigned char *entropy, size_t length)
+{
+ static br_hmac_drbg_context ctx;
+ static bool seeded = FALSE;
+
+ if(!seeded) {
+ br_prng_seeder seeder;
+
+ br_hmac_drbg_init(&ctx, &br_sha256_vtable, NULL, 0);
+ seeder = br_prng_seeder_system(NULL);
+ if(!seeder || !seeder(&ctx.vtable))
+ return CURLE_FAILED_INIT;
+ seeded = TRUE;
+ }
+ br_hmac_drbg_generate(&ctx, entropy, length);
+
+ return CURLE_OK;
+}
+
+static CURLcode Curl_bearssl_connect(struct connectdata *conn, int sockindex)
+{
+ CURLcode ret;
+ bool done = FALSE;
+
+ ret = bearssl_connect_common(conn, sockindex, FALSE, &done);
+ if(ret)
+ return ret;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+static CURLcode Curl_bearssl_connect_nonblocking(struct connectdata *conn,
+ int sockindex, bool *done)
+{
+ return bearssl_connect_common(conn, sockindex, TRUE, done);
+}
+
+static void *Curl_bearssl_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ struct ssl_backend_data *backend = connssl->backend;
+ return &backend->ctx;
+}
+
+static void Curl_bearssl_close(struct connectdata *conn, int sockindex)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ size_t i;
+
+ if(backend->active) {
+ br_ssl_engine_close(&backend->ctx.eng);
+ (void)bearssl_run_until(conn, sockindex, BR_SSL_CLOSED);
+ }
+ for(i = 0; i < backend->anchors_len; ++i)
+ free(backend->anchors[i].dn.data);
+ free(backend->anchors);
+}
+
+static void Curl_bearssl_session_free(void *ptr)
+{
+ free(ptr);
+}
+
+static CURLcode Curl_bearssl_md5sum(unsigned char *input,
+ size_t inputlen,
+ unsigned char *md5sum,
+ size_t md5len UNUSED_PARAM)
+{
+ br_md5_context ctx;
+
+ br_md5_init(&ctx);
+ br_md5_update(&ctx, input, inputlen);
+ br_md5_out(&ctx, md5sum);
+ return CURLE_OK;
+}
+
+static CURLcode Curl_bearssl_sha256sum(const unsigned char *input,
+ size_t inputlen,
+ unsigned char *sha256sum,
+ size_t sha256len UNUSED_PARAM)
+{
+ br_sha256_context ctx;
+
+ br_sha256_init(&ctx);
+ br_sha256_update(&ctx, input, inputlen);
+ br_sha256_out(&ctx, sha256sum);
+ return CURLE_OK;
+}
+
+const struct Curl_ssl Curl_ssl_bearssl = {
+ { CURLSSLBACKEND_BEARSSL, "bearssl" },
+ 0,
+ sizeof(struct ssl_backend_data),
+
+ Curl_none_init,
+ Curl_none_cleanup,
+ Curl_bearssl_version,
+ Curl_none_check_cxn,
+ Curl_none_shutdown,
+ Curl_bearssl_data_pending,
+ Curl_bearssl_random,
+ Curl_none_cert_status_request,
+ Curl_bearssl_connect,
+ Curl_bearssl_connect_nonblocking,
+ Curl_bearssl_get_internals,
+ Curl_bearssl_close,
+ Curl_none_close_all,
+ Curl_bearssl_session_free,
+ Curl_none_set_engine,
+ Curl_none_set_engine_default,
+ Curl_none_engines_list,
+ Curl_none_false_start,
+ Curl_bearssl_md5sum,
+ Curl_bearssl_sha256sum
+};
+
+#endif /* USE_BEARSSL */
diff --git a/contrib/libs/curl/lib/vtls/bearssl.h b/contrib/libs/curl/lib/vtls/bearssl.h
new file mode 100644
index 0000000000..d72b7d0e26
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/bearssl.h
@@ -0,0 +1,32 @@
+#ifndef HEADER_CURL_BEARSSL_H
+#define HEADER_CURL_BEARSSL_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2019 - 2020, Michael Forney, <mforney@mforney.org>
+ *
+ * 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_BEARSSL
+
+extern const struct Curl_ssl Curl_ssl_bearssl;
+
+#endif /* USE_BEARSSL */
+#endif /* HEADER_CURL_BEARSSL_H */
diff --git a/contrib/libs/curl/lib/vtls/gskit.c b/contrib/libs/curl/lib/vtls/gskit.c
new file mode 100644
index 0000000000..17584c750f
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/gskit.c
@@ -0,0 +1,1288 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, 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"
+
+/* 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 "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;
+ int localfd;
+ int remotefd;
+};
+
+#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)
+{
+ /* 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, strerror(errno));
+ 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)
+{
+ 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", strerror(errno));
+ 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 *buffer, bool unsupported_ok)
+{
+ int rc = gsk_attribute_set_buffer(h, id, buffer, 0);
+
+ switch(rc) {
+ case GSK_OK:
+ return CURLE_OK;
+ case GSK_ERROR_IO:
+ failf(data, "gsk_attribute_set_buffer() I/O error: %s", strerror(errno));
+ 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)
+{
+ 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",
+ strerror(errno));
+ break;
+ default:
+ failf(data, "gsk_attribute_set_numeric_value(): %s", gsk_strerror(rc));
+ break;
+ }
+ return CURLE_SSL_CONNECT_ERROR;
+}
+
+
+static CURLcode set_callback(struct Curl_easy *data,
+ gsk_handle h, GSK_CALLBACK_ID id, void *info)
+{
+ int rc = gsk_attribute_set_callback(h, id, info);
+
+ switch(rc) {
+ case GSK_OK:
+ return CURLE_OK;
+ case GSK_ERROR_IO:
+ failf(data, "gsk_attribute_set_callback() I/O error: %s", strerror(errno));
+ break;
+ default:
+ failf(data, "gsk_attribute_set_callback(): %s", gsk_strerror(rc));
+ break;
+ }
+ return CURLE_SSL_CONNECT_ERROR;
+}
+
+
+static CURLcode set_ciphers(struct connectdata *conn,
+ gsk_handle h, unsigned int *protoflags)
+{
+ struct Curl_easy *data = conn->data;
+ 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 Curl_gskit_init(void)
+{
+ /* No initialisation needed. */
+
+ return 1;
+}
+
+
+static void Curl_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;
+
+ if(QsoCancelOperation(conn->sock[sockindex], 0) > 0)
+ QsoWaitForIOCompletion(BACKEND->iocport, &cstat, (struct timeval *) NULL);
+}
+
+
+static void close_async_handshake(struct ssl_connect_data *connssl)
+{
+ QsoDestroyIOCompletionPort(BACKEND->iocport);
+ BACKEND->iocport = -1;
+}
+
+static int pipe_ssloverssl(struct connectdata *conn, int sockindex,
+ int directions)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_connect_data *connproxyssl = &conn->proxy_ssl[sockindex];
+ fd_set fds_read;
+ fd_set fds_write;
+ int n;
+ int m;
+ int i;
+ int ret = 0;
+ char buf[CURL_MAX_WRITE_SIZE];
+
+ if(!connssl->use || !connproxyssl->use)
+ return 0; /* No SSL over SSL: OK. */
+
+ FD_ZERO(&fds_read);
+ FD_ZERO(&fds_write);
+ n = -1;
+ if(directions & SOS_READ) {
+ FD_SET(BACKEND->remotefd, &fds_write);
+ n = BACKEND->remotefd;
+ }
+ if(directions & SOS_WRITE) {
+ FD_SET(BACKEND->remotefd, &fds_read);
+ n = BACKEND->remotefd;
+ FD_SET(conn->sock[sockindex], &fds_write);
+ if(n < conn->sock[sockindex])
+ n = conn->sock[sockindex];
+ }
+ i = Curl_select(n + 1, &fds_read, &fds_write, NULL, 0);
+ if(i < 0)
+ return -1; /* Select error. */
+
+ if(FD_ISSET(BACKEND->remotefd, &fds_write)) {
+ /* 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(FD_ISSET(BACKEND->remotefd, &fds_read) &&
+ FD_ISSET(conn->sock[sockindex], &fds_write)) {
+ /* 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 */
+}
+
+
+static void close_one(struct ssl_connect_data *connssl,
+ struct connectdata *conn, int sockindex)
+{
+ if(BACKEND->handle) {
+ gskit_status(conn->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;
+ if(BACKEND->localfd >= 0) {
+ close(BACKEND->localfd);
+ BACKEND->localfd = -1;
+ }
+ if(BACKEND->remotefd >= 0) {
+ close(BACKEND->remotefd);
+ BACKEND->remotefd = -1;
+ }
+ }
+ if(BACKEND->iocport >= 0)
+ close_async_handshake(connssl);
+}
+
+
+static ssize_t gskit_send(struct connectdata *conn, int sockindex,
+ const void *mem, size_t len, CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct Curl_easy *data = conn->data;
+ CURLcode cc = CURLE_SEND_ERROR;
+ int written;
+
+ 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 connectdata *conn, int num, char *buf,
+ size_t buffersize, CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[num];
+ struct Curl_easy *data = conn->data;
+ int nread;
+ CURLcode cc = CURLE_RECV_ERROR;
+
+ 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 connectdata *conn)
+{
+ struct Curl_easy *data = conn->data;
+ 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 connectdata *conn, int sockindex)
+{
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ gsk_handle envir;
+ CURLcode result;
+ int rc;
+ 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_IS_PROXY()? conn->http_proxy.host.name:
+ conn->host.name;
+ const char *sni;
+ unsigned int protoflags = 0;
+ Qso_OverlappedIO_t commarea;
+ int sockpair[2];
+ static const int sobufsize = CURL_MAX_WRITE_SIZE;
+
+ /* Create SSL environment, start (preferably asynchronous) handshake. */
+
+ BACKEND->handle = (gsk_handle) NULL;
+ BACKEND->iocport = -1;
+ BACKEND->localfd = -1;
+ BACKEND->remotefd = -1;
+
+ /* 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;
+
+ /* 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);
+ }
+
+ /* 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, conn);
+ 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) {
+ result = set_buffer(data, BACKEND->handle,
+ GSK_SSL_EXTN_SERVERNAME_REQUEST, sni, 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)
+ result = set_numeric(data, BACKEND->handle, GSK_FD, BACKEND->localfd >= 0?
+ BACKEND->localfd: conn->sock[sockindex]);
+ if(!result)
+ result = set_ciphers(conn, 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);
+ else if(conn->proxy_ssl[sockindex].use) {
+ /* Cannot pipeline while handshaking synchronously. */
+ result = CURLE_SSL_CONNECT_ERROR;
+ }
+ 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, conn, sockindex);
+ return result;
+}
+
+
+static CURLcode gskit_connect_step2(struct connectdata *conn, int sockindex,
+ bool nonblocking)
+{
+ struct Curl_easy *data = conn->data;
+ 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. */
+
+ for(;;) {
+ timediff_t timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE);
+ if(timeout_ms < 0)
+ timeout_ms = 0;
+ stmv.tv_sec = timeout_ms / 1000;
+ stmv.tv_usec = (timeout_ms - stmv.tv_sec * 1000) * 1000;
+ switch(QsoWaitForIOCompletion(BACKEND->iocport, &cstat, &stmv)) {
+ 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) {
+ failf(data, "QsoWaitForIOCompletion() I/O error: %s", strerror(errno));
+ 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 connectdata *conn, int sockindex)
+{
+ struct Curl_easy *data = conn->data;
+ 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. */
+
+ 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:\n");
+ 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\n", p->cert_data_l, p->cert_data_p);
+ break;
+ case CERT_ISSUER_DN_PRINTABLE:
+ infof(data, "\t issuer: %.*s\n", p->cert_data_l, p->cert_data_p);
+ break;
+ case CERT_VALID_FROM:
+ infof(data, "\t start date: %.*s\n", p->cert_data_l, p->cert_data_p);
+ break;
+ case CERT_VALID_TO:
+ infof(data, "\t expire date: %.*s\n", p->cert_data_l, p->cert_data_p);
+ break;
+ }
+ }
+
+ /* Verify host. */
+ result = Curl_verifyhost(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(conn, 0, cert, certend);
+ if(result)
+ return result;
+ }
+ }
+
+ /* Check pinned public key. */
+ ptr = SSL_IS_PROXY() ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
+ if(!result && ptr) {
+ curl_X509certificate x509;
+ curl_asn1Element *p;
+
+ 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 connectdata *conn, int sockindex,
+ bool nonblocking, bool *done)
+{
+ struct Curl_easy *data = conn->data;
+ 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(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(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(conn, sockindex);
+
+ if(result)
+ close_one(connssl, 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 Curl_gskit_connect_nonblocking(struct connectdata *conn,
+ int sockindex, bool *done)
+{
+ CURLcode result;
+
+ result = gskit_connect_common(conn, sockindex, TRUE, done);
+ if(*done || result)
+ conn->ssl[sockindex].connecting_state = ssl_connect_1;
+ return result;
+}
+
+
+static CURLcode Curl_gskit_connect(struct connectdata *conn, int sockindex)
+{
+ CURLcode result;
+ bool done;
+
+ conn->ssl[sockindex].connecting_state = ssl_connect_1;
+ result = gskit_connect_common(conn, sockindex, FALSE, &done);
+ if(result)
+ return result;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+
+static void Curl_gskit_close(struct connectdata *conn, int sockindex)
+{
+ close_one(&conn->ssl[sockindex], conn, sockindex);
+ close_one(&conn->proxy_ssl[sockindex], conn, sockindex);
+}
+
+
+static int Curl_gskit_shutdown(struct connectdata *conn, int sockindex)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct Curl_easy *data = conn->data;
+ int what;
+ int rc;
+ char buf[120];
+
+ if(!BACKEND->handle)
+ return 0;
+
+#ifndef CURL_DISABLE_FTP
+ if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE)
+ return 0;
+#endif
+
+ close_one(connssl, conn, sockindex);
+ rc = 0;
+ what = SOCKET_READABLE(conn->sock[sockindex],
+ SSL_SHUTDOWN_TIMEOUT);
+
+ for(;;) {
+ 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) {
+ failf(data, "read: %s", strerror(errno));
+ rc = -1;
+ }
+
+ if(nread <= 0)
+ break;
+
+ what = SOCKET_READABLE(conn->sock[sockindex], 0);
+ }
+
+ return rc;
+}
+
+
+static size_t Curl_gskit_version(char *buffer, size_t size)
+{
+ return msnprintf(buffer, size, "GSKit");
+}
+
+
+static int Curl_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. */
+
+ 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 *Curl_gskit_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ (void)info;
+ return BACKEND->handle;
+}
+
+const struct Curl_ssl Curl_ssl_gskit = {
+ { CURLSSLBACKEND_GSKIT, "gskit" }, /* info */
+
+ SSLSUPP_CERTINFO |
+ SSLSUPP_PINNEDPUBKEY,
+
+ sizeof(struct ssl_backend_data),
+
+ Curl_gskit_init, /* init */
+ Curl_gskit_cleanup, /* cleanup */
+ Curl_gskit_version, /* version */
+ Curl_gskit_check_cxn, /* check_cxn */
+ Curl_gskit_shutdown, /* shutdown */
+ Curl_none_data_pending, /* data_pending */
+ Curl_none_random, /* random */
+ Curl_none_cert_status_request, /* cert_status_request */
+ Curl_gskit_connect, /* connect */
+ Curl_gskit_connect_nonblocking, /* connect_nonblocking */
+ Curl_gskit_get_internals, /* get_internals */
+ Curl_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 */
+ Curl_none_md5sum, /* md5sum */
+ NULL /* sha256sum */
+};
+
+#endif /* USE_GSKIT */
diff --git a/contrib/libs/curl/lib/vtls/gskit.h b/contrib/libs/curl/lib/vtls/gskit.h
new file mode 100644
index 0000000000..202df7e07c
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/gskit.h
@@ -0,0 +1,38 @@
+#ifndef HEADER_CURL_GSKIT_H
+#define HEADER_CURL_GSKIT_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, 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"
+
+/*
+ * This header should only be needed to get included by vtls.c and gskit.c
+ */
+
+#include "urldata.h"
+
+#ifdef USE_GSKIT
+
+extern const struct Curl_ssl Curl_ssl_gskit;
+
+#endif /* USE_GSKIT */
+
+#endif /* HEADER_CURL_GSKIT_H */
diff --git a/contrib/libs/curl/lib/vtls/gtls.c b/contrib/libs/curl/lib/vtls/gtls.c
new file mode 100644
index 0000000000..e848c3f05a
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/gtls.c
@@ -0,0 +1,1698 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, 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>
+
+#ifdef USE_GNUTLS_NETTLE
+#include <gnutls/crypto.h>
+#include <nettle/md5.h>
+#include <nettle/sha2.h>
+#else
+#include <gcrypt.h>
+#endif
+
+#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"
+
+/* 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 HAVE_GNUTLS_SRP
+ gnutls_srp_client_credentials_t srp_client_cred;
+#endif
+};
+
+static ssize_t Curl_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 Curl_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 Curl_gtls_push_ssl(void *s, const void *buf, size_t len)
+{
+ return gnutls_record_send((gnutls_session_t) s, buf, len);
+}
+
+static ssize_t Curl_gtls_pull_ssl(void *s, void *buf, size_t len)
+{
+ return gnutls_record_recv((gnutls_session_t) s, buf, len);
+}
+
+/* Curl_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 Curl_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 Curl_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),
+ "\t %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\n", 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 connectdata *conn,
+ int sockindex,
+ bool duringconnect,
+ bool nonblocking)
+{
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ gnutls_session_t session = backend->session;
+ curl_socket_t sockfd = conn->sock[sockindex];
+
+ 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 == NULL)
+ strerr = gnutls_strerror(rc);
+
+ infof(data, "gnutls_handshake() warning: %s\n", 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 == NULL)
+ 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(const char **prioritylist, struct connectdata *conn)
+{
+ struct Curl_easy *data = conn->data;
+ long ssl_version = SSL_CONN_CONFIG(version);
+ long ssl_version_max = SSL_CONN_CONFIG(version_max);
+
+ if(ssl_version_max == CURL_SSLVERSION_MAX_NONE) {
+ ssl_version_max = CURL_SSLVERSION_MAX_DEFAULT;
+ }
+ 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.0:+VERS-TLS1.1";
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_0 | CURL_SSLVERSION_MAX_TLSv1_2:
+ *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+ "+VERS-TLS1.0:+VERS-TLS1.1:+VERS-TLS1.2";
+ 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.1:+VERS-TLS1.2";
+ 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_DEFAULT:
+ *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+ "+VERS-TLS1.0:+VERS-TLS1.1:+VERS-TLS1.2"
+ ":+VERS-TLS1.3";
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_1 | CURL_SSLVERSION_MAX_DEFAULT:
+ *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+ "+VERS-TLS1.1:+VERS-TLS1.2"
+ ":+VERS-TLS1.3";
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_2 | CURL_SSLVERSION_MAX_DEFAULT:
+ *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+ "+VERS-TLS1.2"
+ ":+VERS-TLS1.3";
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_3 | CURL_SSLVERSION_MAX_DEFAULT:
+ *prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0:-VERS-TLS-ALL:"
+ "+VERS-TLS1.2"
+ ":+VERS-TLS1.3";
+ return CURLE_OK;
+ }
+
+ failf(data, "GnuTLS: cannot set ssl protocol");
+ return CURLE_SSL_CONNECT_ERROR;
+}
+
+static CURLcode
+gtls_connect_step1(struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_easy *data = conn->data;
+ 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);
+
+ 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)
+ Curl_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 HAVE_GNUTLS_SRP
+ if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP) {
+ infof(data, "Using TLS-SRP username: %s\n", SSL_SET_OPTION(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(username),
+ SSL_SET_OPTION(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)\n",
+ 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\n", 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)\n",
+ 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\n",
+ 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))) {
+ gnutls_certificate_set_x509_system_trust(backend->cred);
+ }
+#endif
+
+ if(SSL_SET_OPTION(CRLfile)) {
+ /* set the CRL list file */
+ rc = gnutls_certificate_set_x509_crl_file(backend->cred,
+ SSL_SET_OPTION(CRLfile),
+ GNUTLS_X509_FMT_PEM);
+ if(rc < 0) {
+ failf(data, "error reading crl file %s (%s)",
+ SSL_SET_OPTION(CRLfile), gnutls_strerror(rc));
+ return CURLE_SSL_CRL_BADFILE;
+ }
+ else
+ infof(data, "found %d CRL in %s\n",
+ rc, SSL_SET_OPTION(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 &&
+ (gnutls_server_name_set(session, GNUTLS_NAME_DNS, hostname,
+ strlen(hostname)) < 0))
+ infof(data, "WARNING: failed to configure server name indication (SNI) "
+ "TLS extension\n");
+
+ /* Use default priorities */
+ rc = gnutls_set_default_priority(session);
+ if(rc != GNUTLS_E_SUCCESS)
+ return CURLE_SSL_CONNECT_ERROR;
+
+ /* 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 */
+ switch(SSL_CONN_CONFIG(version)) {
+ case CURL_SSLVERSION_SSLv3:
+ prioritylist = GNUTLS_CIPHERS ":-VERS-TLS-ALL:+VERS-SSL3.0";
+ break;
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ prioritylist = GNUTLS_CIPHERS ":-VERS-SSL3.0"
+#ifdef HAS_TLS13
+ ":+VERS-TLS1.3"
+#endif
+ ;
+ break;
+ 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(&prioritylist, conn);
+ if(result != CURLE_OK)
+ return result;
+ break;
+ }
+ case CURL_SSLVERSION_SSLv2:
+ failf(data, "GnuTLS does not support SSLv2");
+ return CURLE_SSL_CONNECT_ERROR;
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+#ifdef HAVE_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(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\n");
+ }
+ }
+ else {
+#endif
+ rc = gnutls_priority_set_direct(session, prioritylist, &err);
+#ifdef HAVE_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_NGHTTP2
+ if(data->set.httpversion >= CURL_HTTP_VERSION_2
+#ifndef CURL_DISABLE_PROXY
+ && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy)
+#endif
+ ) {
+ protocols[cur].data = (unsigned char *)NGHTTP2_PROTO_VERSION_ID;
+ protocols[cur].size = NGHTTP2_PROTO_VERSION_ID_LEN;
+ cur++;
+ infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
+ }
+#endif
+
+ protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1;
+ protocols[cur].size = ALPN_HTTP_1_1_LENGTH;
+ cur++;
+ infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1);
+
+ gnutls_alpn_set_protocols(session, protocols, cur, 0);
+ }
+
+ 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 HAVE_GNUTLS_SRP
+ /* put the credentials to the current session */
+ if(SSL_SET_OPTION(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) {
+ transport_ptr = conn->proxy_ssl[sockindex].backend->session;
+ gnutls_transport_push = Curl_gtls_push_ssl;
+ gnutls_transport_pull = Curl_gtls_pull_ssl;
+ }
+ else
+#endif
+ {
+ /* file descriptor for the socket */
+ transport_ptr = &conn->sock[sockindex];
+ gnutls_transport_push = Curl_gtls_push;
+ gnutls_transport_pull = Curl_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(conn);
+ if(!Curl_ssl_getsessionid(conn, &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\n");
+ }
+ Curl_ssl_sessionid_unlock(conn);
+ }
+
+ 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(NULL == pinnedpubkey)
+ return CURLE_OK;
+
+ if(NULL == 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(NULL == 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(NULL != key)
+ gnutls_pubkey_deinit(key);
+
+ Curl_safefree(buff1);
+
+ return result;
+}
+
+static Curl_recv gtls_recv;
+static Curl_send gtls_send;
+
+static CURLcode
+gtls_connect_step3(struct connectdata *conn,
+ 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;
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ gnutls_session_t session = backend->session;
+ 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\n",
+ 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_SET_OPTION(issuercert)) {
+#ifdef HAVE_GNUTLS_SRP
+ if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP
+ && SSL_SET_OPTION(username) != NULL
+ && !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 HAVE_GNUTLS_SRP
+ }
+#endif
+ }
+ infof(data, "\t common name: WARNING couldn't obtain\n");
+ }
+
+ 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(conn, 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(CRLfile)?SSL_SET_OPTION(CRLfile):"none");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else
+ infof(data, "\t server certificate verification FAILED\n");
+ }
+ else
+ infof(data, "\t server certificate verification OK\n");
+ }
+ else
+ infof(data, "\t server certificate verification SKIPPED\n");
+
+ 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, "\t server certificate status verification FAILED\n");
+
+ 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, "\t server certificate status verification OK\n");
+ }
+ else
+ infof(data, "\t server certificate status verification SKIPPED\n");
+
+ /* 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_SET_OPTION(issuercert)) {
+ gnutls_x509_crt_init(&x509_issuer);
+ issuerp = load_file(SSL_SET_OPTION(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_SET_OPTION(issuercert)?SSL_SET_OPTION(issuercert):"none");
+ gnutls_x509_crt_deinit(x509_cert);
+ return CURLE_SSL_ISSUER_ERROR;
+ }
+ infof(data, "\t server certificate issuer check OK (Issuer Cert: %s)\n",
+ SSL_SET_OPTION(issuercert)?SSL_SET_OPTION(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\n",
+ 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, "\t common name: %s (does not match '%s')\n",
+ certname, SSL_HOST_DISPNAME());
+ }
+ else
+ infof(data, "\t common name: %s (matched)\n", 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, "\t server certificate expiration date verify FAILED\n");
+ }
+ 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, "\t server certificate expiration date FAILED\n");
+ }
+ else
+ infof(data, "\t server certificate expiration date OK\n");
+ }
+
+ 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, "\t server certificate activation date verify FAILED\n");
+ }
+ 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, "\t server certificate activation date FAILED\n");
+ }
+ else
+ infof(data, "\t server certificate activation date OK\n");
+ }
+
+ ptr = SSL_IS_PROXY() ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
+ 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, "\t certificate public key: %s\n",
+ gnutls_pk_algorithm_get_name(algo));
+
+ /* version of the X.509 certificate. */
+ infof(data, "\t certificate version: #%d\n",
+ 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\n");
+ else {
+ infof(data, "\t subject: %s\n", 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\n");
+ else {
+ infof(data, "\t issuer: %s\n", 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, "ALPN, server accepted to use %.*s\n", proto.size,
+ proto.data);
+
+#ifdef USE_NGHTTP2
+ if(proto.size == NGHTTP2_PROTO_VERSION_ID_LEN &&
+ !memcmp(NGHTTP2_PROTO_VERSION_ID, proto.data,
+ NGHTTP2_PROTO_VERSION_ID_LEN)) {
+ 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, "ALPN, server did not agree to a protocol\n");
+
+ Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ }
+
+ conn->ssl[sockindex].state = ssl_connection_complete;
+ conn->recv[sockindex] = gtls_recv;
+ conn->send[sockindex] = gtls_send;
+
+ 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;
+ void *ssl_sessionid;
+
+ /* extract session ID to the allocated buffer */
+ gnutls_session_get_data(session, connect_sessionid, &connect_idsize);
+
+ Curl_ssl_sessionid_lock(conn);
+ incache = !(Curl_ssl_getsessionid(conn, &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(conn, ssl_sessionid);
+ }
+
+ /* store this session id */
+ result = Curl_ssl_addsessionid(conn, connect_sessionid, connect_idsize,
+ sockindex);
+ Curl_ssl_sessionid_unlock(conn);
+ if(result) {
+ free(connect_sessionid);
+ 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 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(conn, sockindex);
+ if(rc)
+ return rc;
+ }
+
+ rc = handshake(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) {
+ rc = gtls_connect_step3(conn, sockindex);
+ if(rc)
+ return rc;
+ }
+
+ *done = ssl_connect_1 == connssl->connecting_state;
+
+ return CURLE_OK;
+}
+
+static CURLcode Curl_gtls_connect_nonblocking(struct connectdata *conn,
+ int sockindex, bool *done)
+{
+ return gtls_connect_common(conn, sockindex, TRUE, done);
+}
+
+static CURLcode Curl_gtls_connect(struct connectdata *conn, int sockindex)
+{
+ CURLcode result;
+ bool done = FALSE;
+
+ result = gtls_connect_common(conn, sockindex, FALSE, &done);
+ if(result)
+ return result;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+static bool Curl_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;
+ if(backend->session &&
+ 0 != gnutls_record_check_pending(backend->session))
+ res = TRUE;
+
+#ifndef CURL_DISABLE_PROXY
+ connssl = &conn->proxy_ssl[connindex];
+ backend = connssl->backend;
+ if(backend->session &&
+ 0 != gnutls_record_check_pending(backend->session))
+ res = TRUE;
+#endif
+
+ return res;
+}
+
+static ssize_t gtls_send(struct connectdata *conn,
+ int sockindex,
+ const void *mem,
+ size_t len,
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ ssize_t 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;
+ if(backend->session) {
+ 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 HAVE_GNUTLS_SRP
+ if(backend->srp_client_cred) {
+ gnutls_srp_free_client_credentials(backend->srp_client_cred);
+ backend->srp_client_cred = NULL;
+ }
+#endif
+}
+
+static void Curl_gtls_close(struct connectdata *conn, int sockindex)
+{
+ 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 Curl_gtls_shutdown(struct connectdata *conn, int sockindex)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ int retval = 0;
+ struct Curl_easy *data = conn->data;
+
+#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\n");
+ 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 HAVE_GNUTLS_SRP
+ if(SSL_SET_OPTION(authtype) == CURL_TLSAUTH_SRP
+ && SSL_SET_OPTION(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 connectdata *conn, /* connection data */
+ int num, /* socketindex */
+ char *buf, /* store read data here */
+ size_t buffersize, /* max amount to read */
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[num];
+ struct ssl_backend_data *backend = connssl->backend;
+ ssize_t ret;
+
+ 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(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(conn->data, "GnuTLS recv error (%d): %s",
+
+ (int)ret, gnutls_strerror((int)ret));
+ *curlcode = CURLE_RECV_ERROR;
+ return -1;
+ }
+
+ return ret;
+}
+
+static void Curl_gtls_session_free(void *ptr)
+{
+ free(ptr);
+}
+
+static size_t Curl_gtls_version(char *buffer, size_t size)
+{
+ return msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL));
+}
+
+#ifndef USE_GNUTLS_NETTLE
+static int Curl_gtls_seed(struct Curl_easy *data)
+{
+ /* we have the "SSL is seeded" boolean static to prevent multiple
+ time-consuming seedings in vain */
+ static bool ssl_seeded = FALSE;
+
+ /* Quickly add a bit of entropy */
+ gcry_fast_random_poll();
+
+ if(!ssl_seeded || data->set.str[STRING_SSL_RANDOM_FILE] ||
+ data->set.str[STRING_SSL_EGDSOCKET]) {
+ ssl_seeded = TRUE;
+ }
+ return 0;
+}
+#endif
+
+/* data might be NULL! */
+static CURLcode Curl_gtls_random(struct Curl_easy *data,
+ unsigned char *entropy, size_t length)
+{
+#if defined(USE_GNUTLS_NETTLE)
+ int rc;
+ (void)data;
+ rc = gnutls_rnd(GNUTLS_RND_RANDOM, entropy, length);
+ return rc?CURLE_FAILED_INIT:CURLE_OK;
+#elif defined(USE_GNUTLS)
+ if(data)
+ Curl_gtls_seed(data); /* Initiate the seed if not already done */
+ gcry_randomize(entropy, length, GCRY_STRONG_RANDOM);
+#endif
+ return CURLE_OK;
+}
+
+static CURLcode Curl_gtls_md5sum(unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *md5sum, /* output */
+ size_t md5len)
+{
+#if defined(USE_GNUTLS_NETTLE)
+ struct md5_ctx MD5pw;
+ md5_init(&MD5pw);
+ md5_update(&MD5pw, (unsigned int)tmplen, tmp);
+ md5_digest(&MD5pw, (unsigned int)md5len, md5sum);
+#elif defined(USE_GNUTLS)
+ gcry_md_hd_t MD5pw;
+ gcry_md_open(&MD5pw, GCRY_MD_MD5, 0);
+ gcry_md_write(MD5pw, tmp, tmplen);
+ memcpy(md5sum, gcry_md_read(MD5pw, 0), md5len);
+ gcry_md_close(MD5pw);
+#endif
+ return CURLE_OK;
+}
+
+static CURLcode Curl_gtls_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum, /* output */
+ size_t sha256len)
+{
+#if defined(USE_GNUTLS_NETTLE)
+ struct sha256_ctx SHA256pw;
+ sha256_init(&SHA256pw);
+ sha256_update(&SHA256pw, (unsigned int)tmplen, tmp);
+ sha256_digest(&SHA256pw, (unsigned int)sha256len, sha256sum);
+#elif defined(USE_GNUTLS)
+ gcry_md_hd_t SHA256pw;
+ gcry_md_open(&SHA256pw, GCRY_MD_SHA256, 0);
+ gcry_md_write(SHA256pw, tmp, tmplen);
+ memcpy(sha256sum, gcry_md_read(SHA256pw, 0), sha256len);
+ gcry_md_close(SHA256pw);
+#endif
+ return CURLE_OK;
+}
+
+static bool Curl_gtls_cert_status_request(void)
+{
+ return TRUE;
+}
+
+static void *Curl_gtls_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ struct ssl_backend_data *backend = connssl->backend;
+ (void)info;
+ 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),
+
+ Curl_gtls_init, /* init */
+ Curl_gtls_cleanup, /* cleanup */
+ Curl_gtls_version, /* version */
+ Curl_none_check_cxn, /* check_cxn */
+ Curl_gtls_shutdown, /* shutdown */
+ Curl_gtls_data_pending, /* data_pending */
+ Curl_gtls_random, /* random */
+ Curl_gtls_cert_status_request, /* cert_status_request */
+ Curl_gtls_connect, /* connect */
+ Curl_gtls_connect_nonblocking, /* connect_nonblocking */
+ Curl_gtls_get_internals, /* get_internals */
+ Curl_gtls_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ Curl_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 */
+ Curl_gtls_md5sum, /* md5sum */
+ Curl_gtls_sha256sum /* sha256sum */
+};
+
+#endif /* USE_GNUTLS */
diff --git a/contrib/libs/curl/lib/vtls/gtls.h b/contrib/libs/curl/lib/vtls/gtls.h
new file mode 100644
index 0000000000..1a146a3a93
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/gtls.h
@@ -0,0 +1,34 @@
+#ifndef HEADER_CURL_GTLS_H
+#define HEADER_CURL_GTLS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, 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_GNUTLS
+
+#include "urldata.h"
+
+extern const struct Curl_ssl Curl_ssl_gnutls;
+
+#endif /* USE_GNUTLS */
+#endif /* HEADER_CURL_GTLS_H */
diff --git a/contrib/libs/curl/lib/vtls/keylog.c b/contrib/libs/curl/lib/vtls/keylog.c
new file mode 100644
index 0000000000..a45945f8f5
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/keylog.c
@@ -0,0 +1,156 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, 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"
+
+#include "keylog.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1)
+
+#define CLIENT_RANDOM_SIZE 32
+
+/*
+ * The master secret in TLS 1.2 and before is always 48 bytes. In TLS 1.3, the
+ * secret size depends on the cipher suite's hash function which is 32 bytes
+ * for SHA-256 and 48 bytes for SHA-384.
+ */
+#define SECRET_MAXLEN 48
+
+
+/* The fp for the open SSLKEYLOGFILE, or NULL if not open */
+static FILE *keylog_file_fp;
+
+void
+Curl_tls_keylog_open(void)
+{
+ char *keylog_file_name;
+
+ if(!keylog_file_fp) {
+ keylog_file_name = curl_getenv("SSLKEYLOGFILE");
+ if(keylog_file_name) {
+ keylog_file_fp = fopen(keylog_file_name, FOPEN_APPENDTEXT);
+ if(keylog_file_fp) {
+#ifdef WIN32
+ if(setvbuf(keylog_file_fp, NULL, _IONBF, 0))
+#else
+ if(setvbuf(keylog_file_fp, NULL, _IOLBF, 4096))
+#endif
+ {
+ fclose(keylog_file_fp);
+ keylog_file_fp = NULL;
+ }
+ }
+ Curl_safefree(keylog_file_name);
+ }
+ }
+}
+
+void
+Curl_tls_keylog_close(void)
+{
+ if(keylog_file_fp) {
+ fclose(keylog_file_fp);
+ keylog_file_fp = NULL;
+ }
+}
+
+bool
+Curl_tls_keylog_enabled(void)
+{
+ return keylog_file_fp != NULL;
+}
+
+bool
+Curl_tls_keylog_write_line(const char *line)
+{
+ /* The current maximum valid keylog line length LF and NUL is 195. */
+ size_t linelen;
+ char buf[256];
+
+ if(!keylog_file_fp || !line) {
+ return false;
+ }
+
+ linelen = strlen(line);
+ if(linelen == 0 || linelen > sizeof(buf) - 2) {
+ /* Empty line or too big to fit in a LF and NUL. */
+ return false;
+ }
+
+ memcpy(buf, line, linelen);
+ if(line[linelen - 1] != '\n') {
+ buf[linelen++] = '\n';
+ }
+ buf[linelen] = '\0';
+
+ /* Using fputs here instead of fprintf since libcurl's fprintf replacement
+ may not be thread-safe. */
+ fputs(buf, keylog_file_fp);
+ return true;
+}
+
+bool
+Curl_tls_keylog_write(const char *label,
+ const unsigned char client_random[CLIENT_RANDOM_SIZE],
+ const unsigned char *secret, size_t secretlen)
+{
+ const char *hex = "0123456789ABCDEF";
+ size_t pos, i;
+ char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 +
+ 2 * SECRET_MAXLEN + 1 + 1];
+
+ if(!keylog_file_fp) {
+ return false;
+ }
+
+ pos = strlen(label);
+ if(pos > KEYLOG_LABEL_MAXLEN || !secretlen || secretlen > SECRET_MAXLEN) {
+ /* Should never happen - sanity check anyway. */
+ return false;
+ }
+
+ memcpy(line, label, pos);
+ line[pos++] = ' ';
+
+ /* Client Random */
+ for(i = 0; i < CLIENT_RANDOM_SIZE; i++) {
+ line[pos++] = hex[client_random[i] >> 4];
+ line[pos++] = hex[client_random[i] & 0xF];
+ }
+ line[pos++] = ' ';
+
+ /* Secret */
+ for(i = 0; i < secretlen; i++) {
+ line[pos++] = hex[secret[i] >> 4];
+ line[pos++] = hex[secret[i] & 0xF];
+ }
+ line[pos++] = '\n';
+ line[pos] = '\0';
+
+ /* Using fputs here instead of fprintf since libcurl's fprintf replacement
+ may not be thread-safe. */
+ fputs(line, keylog_file_fp);
+ return true;
+}
diff --git a/contrib/libs/curl/lib/vtls/keylog.h b/contrib/libs/curl/lib/vtls/keylog.h
new file mode 100644
index 0000000000..63626da90a
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/keylog.h
@@ -0,0 +1,56 @@
+#ifndef HEADER_CURL_KEYLOG_H
+#define HEADER_CURL_KEYLOG_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, 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"
+
+/*
+ * Opens the TLS key log file if requested by the user. The SSLKEYLOGFILE
+ * environment variable specifies the output file.
+ */
+void Curl_tls_keylog_open(void);
+
+/*
+ * Closes the TLS key log file if not already.
+ */
+void Curl_tls_keylog_close(void);
+
+/*
+ * Returns true if the user successfully enabled the TLS key log file.
+ */
+bool Curl_tls_keylog_enabled(void);
+
+/*
+ * Appends a key log file entry.
+ * Returns true iff the key log file is open and a valid entry was provided.
+ */
+bool Curl_tls_keylog_write(const char *label,
+ const unsigned char client_random[32],
+ const unsigned char *secret, size_t secretlen);
+
+/*
+ * Appends a line to the key log file, ensure it is terminated by a LF.
+ * Returns true iff the key log file is open and a valid line was provided.
+ */
+bool Curl_tls_keylog_write_line(const char *line);
+
+#endif /* HEADER_CURL_KEYLOG_H */
diff --git a/contrib/libs/curl/lib/vtls/mbedtls.c b/contrib/libs/curl/lib/vtls/mbedtls.c
new file mode 100644
index 0000000000..e30f660fbb
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/mbedtls.c
@@ -0,0 +1,1112 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
+ * Copyright (C) 2012 - 2020, 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 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/certs.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"
+
+struct ssl_backend_data {
+ mbedtls_ctr_drbg_context ctr_drbg;
+ mbedtls_entropy_context entropy;
+ mbedtls_ssl_context ssl;
+ int server_fd;
+ mbedtls_x509_crt cacert;
+ mbedtls_x509_crt clicert;
+ mbedtls_x509_crl crl;
+ mbedtls_pk_context pk;
+ mbedtls_ssl_config config;
+ const char *protocols[3];
+};
+
+/* apply threading? */
+#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
+#define THREADING_SUPPORT
+#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
+
+/* ALPN for http2? */
+#ifdef USE_NGHTTP2
+# undef HAS_ALPN
+# ifdef MBEDTLS_SSL_ALPN
+# define HAS_ALPN
+# endif
+#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)
+{
+ 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;
+ }
+ return CURLE_SSL_CONNECT_ERROR;
+}
+
+static CURLcode
+set_ssl_version_min_max(struct connectdata *conn, int sockindex)
+{
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_1;
+ int mbedtls_ver_max = MBEDTLS_SSL_MINOR_VERSION_1;
+ long ssl_version = SSL_CONN_CONFIG(version);
+ long ssl_version_max = SSL_CONN_CONFIG(version_max);
+ CURLcode result = CURLE_OK;
+
+ 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 connectdata *conn,
+ int sockindex)
+{
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ const char * const ssl_cafile = 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 char * const ssl_crlfile = SSL_SET_OPTION(CRLfile);
+#ifndef CURL_DISABLE_PROXY
+ const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
+ conn->host.name;
+ const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
+#else
+ const char * const hostname = conn->host.name;
+ const long int port = conn->remote_port;
+#endif
+ int ret = -1;
+ char errorbuf[128];
+ errorbuf[0] = 0;
+
+ /* mbedTLS only supports SSLv3 and TLSv1 */
+ if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2) {
+ failf(data, "mbedTLS does not support SSLv2");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+#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) {
+#ifdef MBEDTLS_ERROR_C
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+#endif /* MBEDTLS_ERROR_C */
+ failf(data, "Failed - mbedTLS: ctr_drbg_init returned (-0x%04X) %s\n",
+ -ret, errorbuf);
+ }
+#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) {
+#ifdef MBEDTLS_ERROR_C
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+#endif /* MBEDTLS_ERROR_C */
+ failf(data, "Failed - mbedTLS: ctr_drbg_init returned (-0x%04X) %s\n",
+ -ret, errorbuf);
+ }
+#endif /* THREADING_SUPPORT */
+
+ /* Load the trusted CA */
+ mbedtls_x509_crt_init(&backend->cacert);
+
+ if(ssl_cafile) {
+ ret = mbedtls_x509_crt_parse_file(&backend->cacert, ssl_cafile);
+
+ if(ret<0) {
+#ifdef MBEDTLS_ERROR_C
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+#endif /* MBEDTLS_ERROR_C */
+ failf(data, "Error reading ca cert file %s - mbedTLS: (-0x%04X) %s",
+ ssl_cafile, -ret, errorbuf);
+
+ if(verifypeer)
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ }
+
+ if(ssl_capath) {
+ ret = mbedtls_x509_crt_parse_path(&backend->cacert, ssl_capath);
+
+ if(ret<0) {
+#ifdef MBEDTLS_ERROR_C
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+#endif /* MBEDTLS_ERROR_C */
+ failf(data, "Error reading ca cert path %s - mbedTLS: (-0x%04X) %s",
+ ssl_capath, -ret, errorbuf);
+
+ if(verifypeer)
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ }
+
+ /* Load the client certificate */
+ mbedtls_x509_crt_init(&backend->clicert);
+
+ if(ssl_cert) {
+ ret = mbedtls_x509_crt_parse_file(&backend->clicert, ssl_cert);
+
+ if(ret) {
+#ifdef MBEDTLS_ERROR_C
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+#endif /* MBEDTLS_ERROR_C */
+ failf(data, "Error reading client cert file %s - mbedTLS: (-0x%04X) %s",
+ ssl_cert, -ret, errorbuf);
+
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ }
+
+ /* Load the client private key */
+ mbedtls_pk_init(&backend->pk);
+
+ if(SSL_SET_OPTION(key)) {
+ ret = mbedtls_pk_parse_keyfile(&backend->pk, SSL_SET_OPTION(key),
+ SSL_SET_OPTION(key_passwd));
+ 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;
+
+ if(ret) {
+#ifdef MBEDTLS_ERROR_C
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+#endif /* MBEDTLS_ERROR_C */
+ failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s",
+ SSL_SET_OPTION(key), -ret, errorbuf);
+
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ }
+
+ /* Load the CRL */
+ mbedtls_x509_crl_init(&backend->crl);
+
+ if(ssl_crlfile) {
+ ret = mbedtls_x509_crl_parse_file(&backend->crl, ssl_crlfile);
+
+ if(ret) {
+#ifdef MBEDTLS_ERROR_C
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+#endif /* MBEDTLS_ERROR_C */
+ failf(data, "Error reading CRL file %s - mbedTLS: (-0x%04X) %s",
+ ssl_crlfile, -ret, errorbuf);
+
+ return CURLE_SSL_CRL_BADFILE;
+ }
+ }
+
+ infof(data, "mbedTLS: Connecting to %s:%ld\n", hostname, port);
+
+ mbedtls_ssl_config_init(&backend->config);
+
+ mbedtls_ssl_init(&backend->ssl);
+ if(mbedtls_ssl_setup(&backend->ssl, &backend->config)) {
+ failf(data, "mbedTLS: ssl_init failed");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ 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;
+ }
+
+ /* 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:
+ 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\n");
+ break;
+ case CURL_SSLVERSION_SSLv3:
+ mbedtls_ssl_conf_min_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3,
+ MBEDTLS_SSL_MINOR_VERSION_0);
+ mbedtls_ssl_conf_max_version(&backend->config, MBEDTLS_SSL_MAJOR_VERSION_3,
+ MBEDTLS_SSL_MINOR_VERSION_0);
+ infof(data, "mbedTLS: Set SSL version to SSLv3\n");
+ break;
+ 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(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(conn);
+ if(!Curl_ssl_getsessionid(conn, &old_session, NULL, sockindex)) {
+ ret = mbedtls_ssl_set_session(&backend->ssl, old_session);
+ if(ret) {
+ Curl_ssl_sessionid_unlock(conn);
+ failf(data, "mbedtls_ssl_set_session returned -0x%x", -ret);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ infof(data, "mbedTLS re-using session\n");
+ }
+ Curl_ssl_sessionid_unlock(conn);
+ }
+
+ mbedtls_ssl_conf_ca_chain(&backend->config,
+ &backend->cacert,
+ &backend->crl);
+
+ if(SSL_SET_OPTION(key)) {
+ mbedtls_ssl_conf_own_cert(&backend->config,
+ &backend->clicert, &backend->pk);
+ }
+ if(mbedtls_ssl_set_hostname(&backend->ssl, hostname)) {
+ /* 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, "couldn't set hostname in mbedTLS");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+#ifdef HAS_ALPN
+ if(conn->bits.tls_enable_alpn) {
+ const char **p = &backend->protocols[0];
+#ifdef USE_NGHTTP2
+ if(data->set.httpversion >= CURL_HTTP_VERSION_2)
+ *p++ = NGHTTP2_PROTO_VERSION_ID;
+#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, "ALPN, offering %s\n", *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 connectdata *conn,
+ int sockindex)
+{
+ int ret;
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ const mbedtls_x509_crt *peercert;
+#ifndef CURL_DISABLE_PROXY
+ const char * const pinnedpubkey = SSL_IS_PROXY() ?
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
+#else
+ const char * const pinnedpubkey =
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
+#endif
+
+ 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];
+ errorbuf[0] = 0;
+#ifdef MBEDTLS_ERROR_C
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+#endif /* MBEDTLS_ERROR_C */
+ failf(data, "ssl_handshake returned - mbedTLS: (-0x%04X) %s",
+ -ret, errorbuf);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ infof(data, "mbedTLS: Handshake complete, cipher is %s\n",
+ 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:\n%s\n", buffer);
+ else
+ infof(data, "Unable to dump certificate information.\n");
+
+ free(buffer);
+ }
+
+ if(pinnedpubkey) {
+ int size;
+ CURLcode result;
+ mbedtls_x509_crt *p;
+ unsigned char pubkey[PUB_DER_MAX_BYTES];
+
+ if(!peercert || !peercert->raw.p || !peercert->raw.len) {
+ failf(data, "Failed due to missing peer certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
+ p = calloc(1, sizeof(*p));
+
+ if(!p)
+ return CURLE_OUT_OF_MEMORY;
+
+ 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_x509_crt_parse_der(p, peercert->raw.p, peercert->raw.len)) {
+ failf(data, "Failed copying peer certificate");
+ mbedtls_x509_crt_free(p);
+ free(p);
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
+ size = mbedtls_pk_write_pubkey_der(&p->pk, pubkey, PUB_DER_MAX_BYTES);
+
+ if(size <= 0) {
+ failf(data, "Failed copying public key from peer certificate");
+ mbedtls_x509_crt_free(p);
+ free(p);
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
+ /* 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);
+ if(result) {
+ mbedtls_x509_crt_free(p);
+ free(p);
+ return result;
+ }
+
+ mbedtls_x509_crt_free(p);
+ free(p);
+ }
+
+#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, "ALPN, server accepted to use %s\n", next_protocol);
+#ifdef USE_NGHTTP2
+ if(!strncmp(next_protocol, NGHTTP2_PROTO_VERSION_ID,
+ NGHTTP2_PROTO_VERSION_ID_LEN) &&
+ !next_protocol[NGHTTP2_PROTO_VERSION_ID_LEN]) {
+ 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, "ALPN, server did not agree to a protocol\n");
+ }
+ Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ }
+#endif
+
+ connssl->connecting_state = ssl_connect_3;
+ infof(data, "SSL connected\n");
+
+ return CURLE_OK;
+}
+
+static CURLcode
+mbed_connect_step3(struct connectdata *conn,
+ int sockindex)
+{
+ CURLcode retcode = CURLE_OK;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ struct Curl_easy *data = conn->data;
+
+ DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+
+ if(SSL_SET_OPTION(primary.sessionid)) {
+ int ret;
+ mbedtls_ssl_session *our_ssl_sessionid;
+ void *old_ssl_sessionid = NULL;
+
+ 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(conn);
+ if(!Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL, sockindex))
+ Curl_ssl_delsessionid(conn, old_ssl_sessionid);
+
+ retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid, 0, sockindex);
+ Curl_ssl_sessionid_unlock(conn);
+ if(retcode) {
+ mbedtls_ssl_session_free(our_ssl_sessionid);
+ free(our_ssl_sessionid);
+ failf(data, "failed to store ssl session");
+ return retcode;
+ }
+ }
+
+ connssl->connecting_state = ssl_connect_done;
+
+ return CURLE_OK;
+}
+
+static ssize_t mbed_send(struct connectdata *conn, int sockindex,
+ const void *mem, size_t len,
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ int ret = -1;
+
+ 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 Curl_mbedtls_close_all(struct Curl_easy *data)
+{
+ (void)data;
+}
+
+static void Curl_mbedtls_close(struct connectdata *conn, int sockindex)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ mbedtls_pk_free(&backend->pk);
+ mbedtls_x509_crt_free(&backend->clicert);
+ mbedtls_x509_crt_free(&backend->cacert);
+ mbedtls_x509_crl_free(&backend->crl);
+ 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 connectdata *conn, int num,
+ char *buf, size_t buffersize,
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[num];
+ struct ssl_backend_data *backend = connssl->backend;
+ int ret = -1;
+ ssize_t len = -1;
+
+ memset(buf, 0, buffersize);
+ 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 Curl_mbedtls_session_free(void *ptr)
+{
+ mbedtls_ssl_session_free(ptr);
+ free(ptr);
+}
+
+static size_t Curl_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 Curl_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);
+ errorbuf[0] = 0;
+
+ ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func,
+ &ctr_entropy, NULL, 0);
+
+ if(ret) {
+#ifdef MBEDTLS_ERROR_C
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+#endif /* MBEDTLS_ERROR_C */
+ failf(data, "Failed - mbedTLS: ctr_drbg_seed returned (-0x%04X) %s\n",
+ -ret, errorbuf);
+ }
+ else {
+ ret = mbedtls_ctr_drbg_random(&ctr_drbg, entropy, length);
+
+ if(ret) {
+#ifdef MBEDTLS_ERROR_C
+ mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+#endif /* MBEDTLS_ERROR_C */
+ failf(data, "mbedTLS: ctr_drbg_init returned (-0x%04X) %s\n",
+ -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 connectdata *conn,
+ int sockindex,
+ bool nonblocking,
+ bool *done)
+{
+ CURLcode retcode;
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ curl_socket_t sockfd = conn->sock[sockindex];
+ timediff_t timeout_ms;
+ int what;
+
+ /* check if the connection has already been established */
+ 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(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(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(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 Curl_mbedtls_connect_nonblocking(struct connectdata *conn,
+ int sockindex, bool *done)
+{
+ return mbed_connect_common(conn, sockindex, TRUE, done);
+}
+
+
+static CURLcode Curl_mbedtls_connect(struct connectdata *conn, int sockindex)
+{
+ CURLcode retcode;
+ bool done = FALSE;
+
+ retcode = mbed_connect_common(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 Curl_mbedtls_init(void)
+{
+ return Curl_mbedtlsthreadlock_thread_setup();
+}
+
+static void Curl_mbedtls_cleanup(void)
+{
+ (void)Curl_mbedtlsthreadlock_thread_cleanup();
+}
+
+static bool Curl_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;
+ return mbedtls_ssl_get_bytes_avail(&backend->ssl) != 0;
+}
+
+static CURLcode Curl_mbedtls_sha256sum(const unsigned char *input,
+ size_t inputlen,
+ unsigned char *sha256sum,
+ size_t sha256len UNUSED_PARAM)
+{
+ (void)sha256len;
+#if MBEDTLS_VERSION_NUMBER < 0x02070000
+ mbedtls_sha256(input, inputlen, sha256sum, 0);
+#else
+ /* returns 0 on success, otherwise failure */
+ if(mbedtls_sha256_ret(input, inputlen, sha256sum, 0) != 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+#endif
+ return CURLE_OK;
+}
+
+static void *Curl_mbedtls_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ struct ssl_backend_data *backend = connssl->backend;
+ (void)info;
+ return &backend->ssl;
+}
+
+const struct Curl_ssl Curl_ssl_mbedtls = {
+ { CURLSSLBACKEND_MBEDTLS, "mbedtls" }, /* info */
+
+ SSLSUPP_CA_PATH |
+ SSLSUPP_PINNEDPUBKEY |
+ SSLSUPP_SSL_CTX,
+
+ sizeof(struct ssl_backend_data),
+
+ Curl_mbedtls_init, /* init */
+ Curl_mbedtls_cleanup, /* cleanup */
+ Curl_mbedtls_version, /* version */
+ Curl_none_check_cxn, /* check_cxn */
+ Curl_none_shutdown, /* shutdown */
+ Curl_mbedtls_data_pending, /* data_pending */
+ Curl_mbedtls_random, /* random */
+ Curl_none_cert_status_request, /* cert_status_request */
+ Curl_mbedtls_connect, /* connect */
+ Curl_mbedtls_connect_nonblocking, /* connect_nonblocking */
+ Curl_mbedtls_get_internals, /* get_internals */
+ Curl_mbedtls_close, /* close_one */
+ Curl_mbedtls_close_all, /* close_all */
+ Curl_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 */
+ Curl_none_md5sum, /* md5sum */
+ Curl_mbedtls_sha256sum /* sha256sum */
+};
+
+#endif /* USE_MBEDTLS */
diff --git a/contrib/libs/curl/lib/vtls/mbedtls.h b/contrib/libs/curl/lib/vtls/mbedtls.h
new file mode 100644
index 0000000000..1abd331ea9
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/mbedtls.h
@@ -0,0 +1,32 @@
+#ifndef HEADER_CURL_MBEDTLS_H
+#define HEADER_CURL_MBEDTLS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2010, 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.
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#ifdef USE_MBEDTLS
+
+extern const struct Curl_ssl Curl_ssl_mbedtls;
+
+#endif /* USE_MBEDTLS */
+#endif /* HEADER_CURL_MBEDTLS_H */
diff --git a/contrib/libs/curl/lib/vtls/mbedtls_threadlock.c b/contrib/libs/curl/lib/vtls/mbedtls_threadlock.c
new file mode 100644
index 0000000000..d3c4698131
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/mbedtls_threadlock.c
@@ -0,0 +1,144 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2013 - 2020, 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.
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#if defined(USE_MBEDTLS) && \
+ ((defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \
+ (defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)))
+
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+# include <pthread.h>
+# define MBEDTLS_MUTEX_T pthread_mutex_t
+#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)
+# include <process.h>
+# define MBEDTLS_MUTEX_T HANDLE
+#endif
+
+#error #include "mbedtls_threadlock.h"
+#include "curl_printf.h"
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* number of thread locks */
+#define NUMT 2
+
+/* This array will store all of the mutexes available to Mbedtls. */
+static MBEDTLS_MUTEX_T *mutex_buf = NULL;
+
+int Curl_mbedtlsthreadlock_thread_setup(void)
+{
+ int i;
+
+ mutex_buf = calloc(NUMT * sizeof(MBEDTLS_MUTEX_T), 1);
+ if(!mutex_buf)
+ return 0; /* error, no number of threads defined */
+
+ for(i = 0; i < NUMT; i++) {
+ int ret;
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+ ret = pthread_mutex_init(&mutex_buf[i], NULL);
+ if(ret)
+ return 0; /* pthread_mutex_init failed */
+#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)
+ mutex_buf[i] = CreateMutex(0, FALSE, 0);
+ if(mutex_buf[i] == 0)
+ return 0; /* CreateMutex failed */
+#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */
+ }
+
+ return 1; /* OK */
+}
+
+int Curl_mbedtlsthreadlock_thread_cleanup(void)
+{
+ int i;
+
+ if(!mutex_buf)
+ return 0; /* error, no threads locks defined */
+
+ for(i = 0; i < NUMT; i++) {
+ int ret;
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+ ret = pthread_mutex_destroy(&mutex_buf[i]);
+ if(ret)
+ return 0; /* pthread_mutex_destroy failed */
+#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)
+ ret = CloseHandle(mutex_buf[i]);
+ if(!ret)
+ return 0; /* CloseHandle failed */
+#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */
+ }
+ free(mutex_buf);
+ mutex_buf = NULL;
+
+ return 1; /* OK */
+}
+
+int Curl_mbedtlsthreadlock_lock_function(int n)
+{
+ if(n < NUMT) {
+ int ret;
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+ ret = pthread_mutex_lock(&mutex_buf[n]);
+ if(ret) {
+ DEBUGF(fprintf(stderr,
+ "Error: mbedtlsthreadlock_lock_function failed\n"));
+ return 0; /* pthread_mutex_lock failed */
+ }
+#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)
+ ret = (WaitForSingleObject(mutex_buf[n], INFINITE) == WAIT_FAILED?1:0);
+ if(ret) {
+ DEBUGF(fprintf(stderr,
+ "Error: mbedtlsthreadlock_lock_function failed\n"));
+ return 0; /* pthread_mutex_lock failed */
+ }
+#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */
+ }
+ return 1; /* OK */
+}
+
+int Curl_mbedtlsthreadlock_unlock_function(int n)
+{
+ if(n < NUMT) {
+ int ret;
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+ ret = pthread_mutex_unlock(&mutex_buf[n]);
+ if(ret) {
+ DEBUGF(fprintf(stderr,
+ "Error: mbedtlsthreadlock_unlock_function failed\n"));
+ return 0; /* pthread_mutex_unlock failed */
+ }
+#elif defined(USE_THREADS_WIN32) && defined(HAVE_PROCESS_H)
+ ret = ReleaseMutex(mutex_buf[n]);
+ if(!ret) {
+ DEBUGF(fprintf(stderr,
+ "Error: mbedtlsthreadlock_unlock_function failed\n"));
+ return 0; /* pthread_mutex_lock failed */
+ }
+#endif /* USE_THREADS_POSIX && HAVE_PTHREAD_H */
+ }
+ return 1; /* OK */
+}
+
+#endif /* USE_MBEDTLS */
diff --git a/contrib/libs/curl/lib/vtls/mesalink.c b/contrib/libs/curl/lib/vtls/mesalink.c
new file mode 100644
index 0000000000..309786cf83
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/mesalink.c
@@ -0,0 +1,661 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2017 - 2018, Yiming Jing, <jingyiming@baidu.com>
+ * Copyright (C) 1998 - 2020, 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 MesaLink-specific code for the TLS/SSL layer. No code
+ * but vtls.c should ever call or use these functions.
+ *
+ */
+
+/*
+ * Based upon the CyaSSL implementation in cyassl.c and cyassl.h:
+ * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * Thanks for code and inspiration!
+ */
+
+#include "curl_setup.h"
+
+#ifdef USE_MESALINK
+
+#include <mesalink/options.h>
+#include <mesalink/version.h>
+
+#include "urldata.h"
+#include "sendf.h"
+#include "inet_pton.h"
+#include "vtls.h"
+#include "parsedate.h"
+#include "connect.h" /* for the connect timeout */
+#include "select.h"
+#include "strcase.h"
+#include "x509asn1.h"
+#include "curl_printf.h"
+
+#include "mesalink.h"
+#include <mesalink/openssl/ssl.h>
+#include <mesalink/openssl/err.h>
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define MESALINK_MAX_ERROR_SZ 80
+
+struct ssl_backend_data
+{
+ SSL_CTX *ctx;
+ SSL *handle;
+};
+
+#define BACKEND connssl->backend
+
+static Curl_recv mesalink_recv;
+static Curl_send mesalink_send;
+
+static int do_file_type(const char *type)
+{
+ if(!type || !type[0])
+ return SSL_FILETYPE_PEM;
+ if(strcasecompare(type, "PEM"))
+ return SSL_FILETYPE_PEM;
+ if(strcasecompare(type, "DER"))
+ return SSL_FILETYPE_ASN1;
+ return -1;
+}
+
+/*
+ * This function loads all the client/CA certificates and CRLs. Setup the TLS
+ * layer and do all necessary magic.
+ */
+static CURLcode
+mesalink_connect_step1(struct connectdata *conn, int sockindex)
+{
+ char *ciphers;
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct in_addr addr4;
+#ifdef ENABLE_IPV6
+ struct in6_addr addr6;
+#endif
+ const char *const hostname =
+ SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name;
+ size_t hostname_len = strlen(hostname);
+
+ SSL_METHOD *req_method = NULL;
+ curl_socket_t sockfd = conn->sock[sockindex];
+
+ if(connssl->state == ssl_connection_complete)
+ return CURLE_OK;
+
+ if(SSL_CONN_CONFIG(version_max) != CURL_SSLVERSION_MAX_NONE) {
+ failf(data, "MesaLink does not support to set maximum SSL/TLS version");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ switch(SSL_CONN_CONFIG(version)) {
+ case CURL_SSLVERSION_SSLv3:
+ case CURL_SSLVERSION_TLSv1:
+ case CURL_SSLVERSION_TLSv1_0:
+ case CURL_SSLVERSION_TLSv1_1:
+ failf(data, "MesaLink does not support SSL 3.0, TLS 1.0, or TLS 1.1");
+ return CURLE_NOT_BUILT_IN;
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1_2:
+ req_method = TLSv1_2_client_method();
+ break;
+ case CURL_SSLVERSION_TLSv1_3:
+ req_method = TLSv1_3_client_method();
+ break;
+ case CURL_SSLVERSION_SSLv2:
+ failf(data, "MesaLink does not support SSLv2");
+ return CURLE_SSL_CONNECT_ERROR;
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(!req_method) {
+ failf(data, "SSL: couldn't create a method!");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(BACKEND->ctx)
+ SSL_CTX_free(BACKEND->ctx);
+ BACKEND->ctx = SSL_CTX_new(req_method);
+
+ if(!BACKEND->ctx) {
+ failf(data, "SSL: couldn't create a context!");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ SSL_CTX_set_verify(
+ BACKEND->ctx, SSL_CONN_CONFIG(verifypeer) ?
+ SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
+
+ if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(CApath)) {
+ if(!SSL_CTX_load_verify_locations(BACKEND->ctx, SSL_CONN_CONFIG(CAfile),
+ SSL_CONN_CONFIG(CApath))) {
+ if(SSL_CONN_CONFIG(verifypeer)) {
+ failf(data,
+ "error setting certificate verify locations: "
+ " CAfile: %s CApath: %s",
+ SSL_CONN_CONFIG(CAfile) ?
+ SSL_CONN_CONFIG(CAfile) : "none",
+ SSL_CONN_CONFIG(CApath) ?
+ SSL_CONN_CONFIG(CApath) : "none");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ infof(data,
+ "error setting certificate verify locations,"
+ " continuing anyway:\n");
+ }
+ else {
+ infof(data, "successfully set certificate verify locations:\n");
+ }
+ infof(data, " CAfile: %s\n",
+ SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile): "none");
+ infof(data, " CApath: %s\n",
+ SSL_CONN_CONFIG(CApath) ? SSL_CONN_CONFIG(CApath): "none");
+ }
+
+ if(SSL_SET_OPTION(primary.clientcert) && SSL_SET_OPTION(key)) {
+ int file_type = do_file_type(SSL_SET_OPTION(cert_type));
+
+ if(SSL_CTX_use_certificate_chain_file(BACKEND->ctx,
+ SSL_SET_OPTION(primary.clientcert),
+ file_type) != 1) {
+ failf(data, "unable to use client certificate (no key or wrong pass"
+ " phrase?)");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ file_type = do_file_type(SSL_SET_OPTION(key_type));
+ if(SSL_CTX_use_PrivateKey_file(BACKEND->ctx, SSL_SET_OPTION(key),
+ file_type) != 1) {
+ failf(data, "unable to set private key");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ infof(data,
+ "client cert: %s\n",
+ SSL_CONN_CONFIG(clientcert)?
+ SSL_CONN_CONFIG(clientcert): "none");
+ }
+
+ ciphers = SSL_CONN_CONFIG(cipher_list);
+ if(ciphers) {
+#ifdef MESALINK_HAVE_CIPHER
+ if(!SSL_CTX_set_cipher_list(BACKEND->ctx, ciphers)) {
+ failf(data, "failed setting cipher list: %s", ciphers);
+ return CURLE_SSL_CIPHER;
+ }
+#endif
+ infof(data, "Cipher selection: %s\n", ciphers);
+ }
+
+ if(BACKEND->handle)
+ SSL_free(BACKEND->handle);
+ BACKEND->handle = SSL_new(BACKEND->ctx);
+ if(!BACKEND->handle) {
+ failf(data, "SSL: couldn't create a context (handle)!");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if((hostname_len < USHRT_MAX) &&
+ (0 == Curl_inet_pton(AF_INET, hostname, &addr4))
+#ifdef ENABLE_IPV6
+ && (0 == Curl_inet_pton(AF_INET6, hostname, &addr6))
+#endif
+ ) {
+ /* hostname is not a valid IP address */
+ if(SSL_set_tlsext_host_name(BACKEND->handle, hostname) != SSL_SUCCESS) {
+ failf(data,
+ "WARNING: failed to configure server name indication (SNI) "
+ "TLS extension\n");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ else {
+#ifdef CURLDEBUG
+ /* Check if the hostname is 127.0.0.1 or [::1];
+ * otherwise reject because MesaLink always wants a valid DNS Name
+ * specified in RFC 5280 Section 7.2 */
+ if(strncmp(hostname, "127.0.0.1", 9) == 0
+#ifdef ENABLE_IPV6
+ || strncmp(hostname, "[::1]", 5) == 0
+#endif
+ ) {
+ SSL_set_tlsext_host_name(BACKEND->handle, "localhost");
+ }
+ else
+#endif
+ {
+ failf(data,
+ "ERROR: MesaLink does not accept an IP address as a hostname\n");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+#ifdef MESALINK_HAVE_SESSION
+ if(SSL_SET_OPTION(primary.sessionid)) {
+ void *ssl_sessionid = NULL;
+
+ Curl_ssl_sessionid_lock(conn);
+ if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL, sockindex)) {
+ /* we got a session id, use it! */
+ if(!SSL_set_session(BACKEND->handle, ssl_sessionid)) {
+ Curl_ssl_sessionid_unlock(conn);
+ failf(
+ data,
+ "SSL: SSL_set_session failed: %s",
+ ERR_error_string(SSL_get_error(BACKEND->handle, 0), error_buffer));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ /* Informational message */
+ infof(data, "SSL re-using session ID\n");
+ }
+ Curl_ssl_sessionid_unlock(conn);
+ }
+#endif /* MESALINK_HAVE_SESSION */
+
+ if(SSL_set_fd(BACKEND->handle, (int)sockfd) != SSL_SUCCESS) {
+ failf(data, "SSL: SSL_set_fd failed");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ connssl->connecting_state = ssl_connect_2;
+ return CURLE_OK;
+}
+
+static CURLcode
+mesalink_connect_step2(struct connectdata *conn, int sockindex)
+{
+ int ret = -1;
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+ conn->recv[sockindex] = mesalink_recv;
+ conn->send[sockindex] = mesalink_send;
+
+ ret = SSL_connect(BACKEND->handle);
+ if(ret != SSL_SUCCESS) {
+ int detail = SSL_get_error(BACKEND->handle, ret);
+
+ if(SSL_ERROR_WANT_CONNECT == detail || SSL_ERROR_WANT_READ == detail) {
+ connssl->connecting_state = ssl_connect_2_reading;
+ return CURLE_OK;
+ }
+ else {
+ char error_buffer[MESALINK_MAX_ERROR_SZ];
+ failf(data,
+ "SSL_connect failed with error %d: %s",
+ detail,
+ ERR_error_string_n(detail, error_buffer, sizeof(error_buffer)));
+ ERR_print_errors_fp(stderr);
+ if(detail && SSL_CONN_CONFIG(verifypeer)) {
+ detail &= ~0xFF;
+ if(detail == TLS_ERROR_WEBPKI_ERRORS) {
+ failf(data, "Cert verify failed");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ }
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ connssl->connecting_state = ssl_connect_3;
+ infof(data,
+ "SSL connection using %s / %s\n",
+ SSL_get_version(BACKEND->handle),
+ SSL_get_cipher_name(BACKEND->handle));
+
+ return CURLE_OK;
+}
+
+static CURLcode
+mesalink_connect_step3(struct connectdata *conn, int sockindex)
+{
+ CURLcode result = CURLE_OK;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+ DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+
+#ifdef MESALINK_HAVE_SESSION
+ if(SSL_SET_OPTION(primary.sessionid)) {
+ bool incache;
+ SSL_SESSION *our_ssl_sessionid;
+ void *old_ssl_sessionid = NULL;
+
+ our_ssl_sessionid = SSL_get_session(BACKEND->handle);
+
+ Curl_ssl_sessionid_lock(conn);
+ incache =
+ !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL, sockindex));
+ if(incache) {
+ if(old_ssl_sessionid != our_ssl_sessionid) {
+ infof(data, "old SSL session ID is stale, removing\n");
+ Curl_ssl_delsessionid(conn, old_ssl_sessionid);
+ incache = FALSE;
+ }
+ }
+
+ if(!incache) {
+ result = Curl_ssl_addsessionid(
+ conn, our_ssl_sessionid, 0 /* unknown size */, sockindex);
+ if(result) {
+ Curl_ssl_sessionid_unlock(conn);
+ failf(data, "failed to store ssl session");
+ return result;
+ }
+ }
+ Curl_ssl_sessionid_unlock(conn);
+ }
+#endif /* MESALINK_HAVE_SESSION */
+
+ connssl->connecting_state = ssl_connect_done;
+
+ return result;
+}
+
+static ssize_t
+mesalink_send(struct connectdata *conn, int sockindex, const void *mem,
+ size_t len, CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ char error_buffer[MESALINK_MAX_ERROR_SZ];
+ int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
+ int rc = SSL_write(BACKEND->handle, mem, memlen);
+
+ if(rc < 0) {
+ int err = SSL_get_error(BACKEND->handle, rc);
+ switch(err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ /* there's data pending, re-invoke SSL_write() */
+ *curlcode = CURLE_AGAIN;
+ return -1;
+ default:
+ failf(conn->data,
+ "SSL write: %s, errno %d",
+ ERR_error_string_n(err, error_buffer, sizeof(error_buffer)),
+ SOCKERRNO);
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+ }
+ return rc;
+}
+
+static void
+Curl_mesalink_close(struct connectdata *conn, int sockindex)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+ if(BACKEND->handle) {
+ (void)SSL_shutdown(BACKEND->handle);
+ SSL_free(BACKEND->handle);
+ BACKEND->handle = NULL;
+ }
+ if(BACKEND->ctx) {
+ SSL_CTX_free(BACKEND->ctx);
+ BACKEND->ctx = NULL;
+ }
+}
+
+static ssize_t
+mesalink_recv(struct connectdata *conn, int num, char *buf, size_t buffersize,
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[num];
+ char error_buffer[MESALINK_MAX_ERROR_SZ];
+ int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
+ int nread = SSL_read(BACKEND->handle, buf, buffsize);
+
+ if(nread <= 0) {
+ int err = SSL_get_error(BACKEND->handle, nread);
+
+ switch(err) {
+ case SSL_ERROR_ZERO_RETURN: /* no more data */
+ case IO_ERROR_CONNECTION_ABORTED:
+ break;
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ /* there's data pending, re-invoke SSL_read() */
+ *curlcode = CURLE_AGAIN;
+ return -1;
+ default:
+ failf(conn->data,
+ "SSL read: %s, errno %d",
+ ERR_error_string_n(err, error_buffer, sizeof(error_buffer)),
+ SOCKERRNO);
+ *curlcode = CURLE_RECV_ERROR;
+ return -1;
+ }
+ }
+ return nread;
+}
+
+static size_t
+Curl_mesalink_version(char *buffer, size_t size)
+{
+ return msnprintf(buffer, size, "MesaLink/%s", MESALINK_VERSION_STRING);
+}
+
+static int
+Curl_mesalink_init(void)
+{
+ return (SSL_library_init() == SSL_SUCCESS);
+}
+
+/*
+ * This function is called to shut down the SSL layer but keep the
+ * socket open (CCC - Clear Command Channel)
+ */
+static int
+Curl_mesalink_shutdown(struct connectdata *conn, int sockindex)
+{
+ int retval = 0;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+ if(BACKEND->handle) {
+ SSL_free(BACKEND->handle);
+ BACKEND->handle = NULL;
+ }
+ return retval;
+}
+
+static CURLcode
+mesalink_connect_common(struct connectdata *conn, int sockindex,
+ bool nonblocking, bool *done)
+{
+ CURLcode result;
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ curl_socket_t sockfd = conn->sock[sockindex];
+ timediff_t timeout_ms;
+ int what;
+
+ /* check if the connection has already been established */
+ 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;
+ }
+
+ result = mesalink_connect_step1(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 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.
+ */
+ result = mesalink_connect_step2(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 = mesalink_connect_step3(conn, sockindex);
+ if(result)
+ return result;
+ }
+
+ if(ssl_connect_done == connssl->connecting_state) {
+ connssl->state = ssl_connection_complete;
+ conn->recv[sockindex] = mesalink_recv;
+ conn->send[sockindex] = mesalink_send;
+ *done = TRUE;
+ }
+ else
+ *done = FALSE;
+
+ /* Reset our connect state machine */
+ connssl->connecting_state = ssl_connect_1;
+
+ return CURLE_OK;
+}
+
+static CURLcode
+Curl_mesalink_connect_nonblocking(struct connectdata *conn, int sockindex,
+ bool *done)
+{
+ return mesalink_connect_common(conn, sockindex, TRUE, done);
+}
+
+static CURLcode
+Curl_mesalink_connect(struct connectdata *conn, int sockindex)
+{
+ CURLcode result;
+ bool done = FALSE;
+
+ result = mesalink_connect_common(conn, sockindex, FALSE, &done);
+ if(result)
+ return result;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+static void *
+Curl_mesalink_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ (void)info;
+ return BACKEND->handle;
+}
+
+const struct Curl_ssl Curl_ssl_mesalink = {
+ { CURLSSLBACKEND_MESALINK, "MesaLink" }, /* info */
+
+ SSLSUPP_SSL_CTX,
+
+ sizeof(struct ssl_backend_data),
+
+ Curl_mesalink_init, /* init */
+ Curl_none_cleanup, /* cleanup */
+ Curl_mesalink_version, /* version */
+ Curl_none_check_cxn, /* check_cxn */
+ Curl_mesalink_shutdown, /* shutdown */
+ Curl_none_data_pending, /* data_pending */
+ Curl_none_random, /* random */
+ Curl_none_cert_status_request, /* cert_status_request */
+ Curl_mesalink_connect, /* connect */
+ Curl_mesalink_connect_nonblocking, /* connect_nonblocking */
+ Curl_mesalink_get_internals, /* get_internals */
+ Curl_mesalink_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ 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 */
+ Curl_none_md5sum, /* md5sum */
+ NULL /* sha256sum */
+};
+
+#endif
diff --git a/contrib/libs/curl/lib/vtls/mesalink.h b/contrib/libs/curl/lib/vtls/mesalink.h
new file mode 100644
index 0000000000..03f520c1dc
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/mesalink.h
@@ -0,0 +1,32 @@
+#ifndef HEADER_CURL_MESALINK_H
+#define HEADER_CURL_MESALINK_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2017 - 2018, Yiming Jing, <jingyiming@baidu.com>
+ * Copyright (C) 1998 - 2020, 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_MESALINK
+
+extern const struct Curl_ssl Curl_ssl_mesalink;
+
+#endif /* USE_MESALINK */
+#endif /* HEADER_CURL_MESALINK_H */
diff --git a/contrib/libs/curl/lib/vtls/nss.c b/contrib/libs/curl/lib/vtls/nss.c
new file mode 100644
index 0000000000..59649ccc3a
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/nss.c
@@ -0,0 +1,2464 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, 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},
+ /* 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},
+ /* 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
+};
+
+#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);
+ }
+}
+
+static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc * model,
+ char *cipher_list)
+{
+ unsigned int i;
+ PRBool cipher_state[NUM_OF_CIPHERS];
+ PRBool found;
+ 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);
+ }
+
+ /* Set every entry in our list to false */
+ for(i = 0; i < NUM_OF_CIPHERS; i++) {
+ cipher_state[i] = PR_FALSE;
+ }
+
+ cipher = cipher_list;
+
+ while(cipher_list && (cipher_list[0])) {
+ while((*cipher) && (ISSPACE(*cipher)))
+ ++cipher;
+
+ cipher_list = strchr(cipher, ',');
+ if(cipher_list) {
+ *cipher_list++ = '\0';
+ }
+
+ found = PR_FALSE;
+
+ for(i = 0; i<NUM_OF_CIPHERS; i++) {
+ if(strcasecompare(cipher, cipherlist[i].name)) {
+ cipher_state[i] = PR_TRUE;
+ found = PR_TRUE;
+ break;
+ }
+ }
+
+ if(found == PR_FALSE) {
+ failf(data, "Unknown cipher in list: %s", cipher);
+ return SECFailure;
+ }
+
+ if(cipher_list) {
+ cipher = cipher_list;
+ }
+ }
+
+ /* Finally actually enable the selected ciphers */
+ for(i = 0; i<NUM_OF_CIPHERS; i++) {
+ if(!cipher_state[i])
+ continue;
+
+ if(SSL_CipherPrefSet(model, cipherlist[i].num, PR_TRUE) != SECSuccess) {
+ failf(data, "cipher-suite not supported by NSS: %s", cipherlist[i].name);
+ return SECFailure;
+ }
+ }
+
+ 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 == NULL)
+ 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\n", 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 Curl_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;
+ 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 Curl_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 */
+ CERTCertificate *cert;
+ 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) {
+ 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 Curl_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 connectdata *conn, int sockindex,
+ char *key_file)
+{
+ PK11SlotInfo *slot, *tmp;
+ SECStatus status;
+ CURLcode result;
+ struct ssl_connect_data *ssl = conn->ssl;
+ struct Curl_easy *data = conn->data;
+
+ (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 connectdata *conn, PRInt32 err,
+ const char *filename)
+{
+ switch(err) {
+ case SEC_ERROR_BAD_PASSWORD:
+ failf(conn->data, "Unable to load client key: Incorrect password");
+ return 1;
+ case SEC_ERROR_UNKNOWN_CERT:
+ failf(conn->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 connectdata *conn, int sockindex,
+ char *cert_file, char *key_file)
+{
+ struct Curl_easy *data = conn->data;
+ 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(conn, 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(conn, sockindex, key_file);
+ else
+ /* In case the cert file also has the key */
+ result = nss_load_key(conn, sockindex, cert_file);
+ if(result) {
+ const PRErrorCode err = PR_GetError();
+ if(!display_error(conn, 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 || NULL == 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 connectdata *conn = (struct connectdata *)arg;
+
+#ifdef SSL_ENABLE_OCSP_STAPLING
+ if(SSL_CONN_CONFIG(verifystatus)) {
+ SECStatus cacheResult;
+
+ const SECItemArray *csa = SSL_PeerStapledOCSPResponses(fd);
+ if(!csa) {
+ failf(conn->data, "Invalid OCSP response");
+ return SECFailure;
+ }
+
+ if(csa->len == 0) {
+ failf(conn->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(conn->data, "Invalid OCSP response");
+ return cacheResult;
+ }
+ }
+#endif
+
+ if(!SSL_CONN_CONFIG(verifypeer)) {
+ infof(conn->data, "skipping SSL peer certificate verification\n");
+ 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 connectdata *conn = (struct connectdata*) arg;
+ 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(conn->data, "ALPN/NPN, server did not agree to a protocol\n");
+ return;
+#ifdef SSL_ENABLE_ALPN
+ case SSL_NEXT_PROTO_SELECTED:
+ infof(conn->data, "ALPN, server accepted to use %.*s\n", buflen, buf);
+ break;
+#endif
+ case SSL_NEXT_PROTO_NEGOTIATED:
+ infof(conn->data, "NPN, server accepted to use %.*s\n", buflen, buf);
+ break;
+ }
+
+#ifdef USE_NGHTTP2
+ if(buflen == NGHTTP2_PROTO_VERSION_ID_LEN &&
+ !memcmp(NGHTTP2_PROTO_VERSION_ID, buf, NGHTTP2_PROTO_VERSION_ID_LEN)) {
+ 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;
+ }
+ Curl_multiuse_state(conn, 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 connectdata *conn = client_data;
+ struct Curl_easy *data = conn->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\n");
+
+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, "\tsubject: %s\n", 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, "\tstart date: %s\n", timeString);
+ PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime);
+ PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime);
+ infof(data, "\texpire date: %s\n", timeString);
+ infof(data, "\tcommon name: %s\n", common_name);
+ infof(data, "\tissuer: %s\n", issuer);
+
+ PR_Free(subject);
+ PR_Free(issuer);
+ PR_Free(common_name);
+}
+
+static CURLcode display_conn_info(struct connectdata *conn, PRFileDesc *sock)
+{
+ CURLcode result = CURLE_OK;
+ SSLChannelInfo channel;
+ SSLCipherSuiteInfo suite;
+ CERTCertificate *cert;
+ CERTCertificate *cert2;
+ CERTCertificate *cert3;
+ PRTime now;
+ int i;
+
+ if(SSL_GetChannelInfo(sock, &channel, sizeof(channel)) ==
+ SECSuccess && channel.length == sizeof(channel) &&
+ channel.cipherSuite) {
+ if(SSL_GetCipherSuiteInfo(channel.cipherSuite,
+ &suite, sizeof(suite)) == SECSuccess) {
+ infof(conn->data, "SSL connection using %s\n", suite.cipherSuiteName);
+ }
+ }
+
+ cert = SSL_PeerCertificate(sock);
+ if(cert) {
+ infof(conn->data, "Server certificate:\n");
+
+ if(!conn->data->set.ssl.certinfo) {
+ display_cert_info(conn->data, cert);
+ CERT_DestroyCertificate(cert);
+ }
+ else {
+ /* Count certificates in chain. */
+ now = PR_Now();
+ i = 1;
+ if(!cert->isRoot) {
+ cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA);
+ while(cert2) {
+ i++;
+ if(cert2->isRoot) {
+ CERT_DestroyCertificate(cert2);
+ break;
+ }
+ cert3 = CERT_FindCertIssuer(cert2, now, certUsageSSLCA);
+ CERT_DestroyCertificate(cert2);
+ cert2 = cert3;
+ }
+ }
+
+ result = Curl_ssl_init_certinfo(conn->data, i);
+ if(!result) {
+ for(i = 0; cert; cert = cert2) {
+ result = Curl_extract_certinfo(conn, 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 connectdata *conn = (struct connectdata *)arg;
+ struct Curl_easy *data = conn->data;
+ 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:\n");
+ 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 = backend->data;
+ CERTCertificate *cert;
+
+ 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!\n");
+ 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 = backend->data;
+ const char *nickname = backend->client_nickname;
+ static const char pem_slotname[] = "PEM Token #1";
+
+ 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(NULL == 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(NULL == 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(NULL == key) {
+ failf(data, "NSS: private key from file not found");
+ CERT_DestroyCertificate(cert);
+ return SECFailure;
+ }
+
+ infof(data, "NSS: client certificate from file\n");
+ 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)
+ || NULL == *pRetCert) {
+
+ if(NULL == 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(NULL == 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(NULL == *pRetKey) {
+ failf(data, "NSS: private key not found for certificate: %s", nickname);
+ return SECFailure;
+ }
+
+ infof(data, "NSS: using client certificate: %s\n", 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 != NULL)
+ 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\n", certpath);
+ nss_context = NSS_InitContext(certpath, "", "", "", &initparams,
+ NSS_INIT_READONLY | NSS_INIT_PK11RELOAD);
+ free(certpath);
+
+ if(nss_context != NULL)
+ return CURLE_OK;
+
+ err = PR_GetError();
+ err_name = nss_error_to_name(err);
+ infof(data, "Unable to initialize NSS database: %d (%s)\n", err, err_name);
+ }
+
+ infof(data, "Initializing NSS with certpath: none\n");
+ 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 != NULL)
+ 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_init(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 Curl_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 Curl_nss_init(void)
+{
+ /* curl_global_init() is not thread-safe so this test is ok */
+ if(nss_initlock == NULL) {
+ 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_init(data);
+ PR_Unlock(nss_initlock);
+
+ return result;
+}
+
+/* Global cleanup */
+static void Curl_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 Curl_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;
+
+ 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 nss_close(struct ssl_connect_data *connssl)
+{
+ /* before the cleanup, check whether we are using a client certificate */
+ struct ssl_backend_data *backend = connssl->backend;
+ const bool client_cert = (backend->client_nickname != NULL)
+ || (backend->obj_clicert != NULL);
+
+ 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 Curl_nss_close(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;
+
+ 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;
+
+ nss_close(connssl_proxy);
+#endif
+ nss_close(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 connectdata *conn,
+ int sockindex)
+{
+ struct Curl_easy *data = conn->data;
+ 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\n", cafile ? cafile : "none");
+ infof(data, " CApath: %s\n", 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\n", (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\n", 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\n", fullpath);
+
+ free(fullpath);
+ }
+
+ PR_CloseDir(dir);
+ }
+ else
+ infof(data, "warning: CURLOPT_CAPATH not a directory (%s)\n", 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:
+ *nssver = SSL_LIBRARY_VERSION_3_0;
+ return CURLE_OK;
+
+ 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)
+{
+ PRErrorCode err = 0;
+ struct ssl_backend_data *backend = connssl->backend;
+
+ if(is_nss_error(curlerr)) {
+ /* read NSPR error code */
+ 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)\n", 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)
+{
+ static PRSocketOptionData sock_opt;
+ struct ssl_backend_data *backend = connssl->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 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;
+ struct Curl_easy *data = conn->data;
+ 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
+ };
+
+ backend->data = data;
+
+ /* list of all NSS objects we need to destroy in Curl_nss_close() */
+ Curl_llist_init(&backend->obj_list, nss_destroy_object);
+
+ PR_Lock(nss_initlock);
+ result = nss_init(conn->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.\n", 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)\n",
+ 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\n",
+ ssl_cbc_random_iv);
+#else
+ if(ssl_cbc_random_iv)
+ infof(data, "warning: support for SSL_CBC_RANDOM_IV not compiled in\n");
+#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\n");
+
+ /* bypass the default SSL_AuthCertificate() hook in case we do not want to
+ * verify peer */
+ if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, conn) != SECSuccess)
+ goto error;
+
+ /* not checked yet */
+ SSL_SET_OPTION_LVALUE(certverifyresult) = 0;
+
+ if(SSL_BadCertHook(model, BadCertHandler, conn) != SECSuccess)
+ goto error;
+
+ if(SSL_HandshakeCallback(model, HandshakeCallback, conn) != SECSuccess)
+ goto error;
+
+ {
+ const CURLcode rv = nss_load_ca_certificates(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\n");
+ else if(rv) {
+ result = rv;
+ goto error;
+ }
+ }
+
+ if(SSL_SET_OPTION(CRLfile)) {
+ const CURLcode rv = nss_load_crl(SSL_SET_OPTION(CRLfile));
+ if(rv) {
+ result = rv;
+ goto error;
+ }
+ infof(data, " CRLfile: %s\n", SSL_SET_OPTION(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(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) {
+ DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state);
+ DEBUGASSERT(conn->proxy_ssl[sockindex].backend->handle != NULL);
+ nspr_io = conn->proxy_ssl[sockindex].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,
+ conn) != 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_NGHTTP2
+ if(data->set.httpversion >= CURL_HTTP_VERSION_2
+#ifndef CURL_DISABLE_PROXY
+ && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy)
+#endif
+ ) {
+ protocols[cur++] = NGHTTP2_PROTO_VERSION_ID_LEN;
+ memcpy(&protocols[cur], NGHTTP2_PROTO_VERSION_ID,
+ NGHTTP2_PROTO_VERSION_ID_LEN);
+ cur += NGHTTP2_PROTO_VERSION_ID_LEN;
+ }
+#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, SSL_HOST_NAME()) != SECSuccess)
+ goto error;
+
+ /* prevent NSS from re-using the session for a different hostname */
+ if(SSL_SetSockPeerID(backend->handle, SSL_HOST_NAME()) != 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 connectdata *conn, int sockindex)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ struct Curl_easy *data = conn->data;
+ 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;
+ }
+
+ /* 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(conn, backend->handle);
+ if(result)
+ goto error;
+
+ if(SSL_SET_OPTION(issuercert)) {
+ SECStatus ret = SECFailure;
+ char *nickname = dup_nickname(data, SSL_SET_OPTION(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\n");
+ result = CURLE_SSL_ISSUER_ERROR;
+ goto error;
+ }
+ else {
+ infof(data, "SSL certificate issuer check ok\n");
+ }
+ }
+
+ 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 connectdata *conn, int sockindex,
+ bool *done)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct Curl_easy *data = conn->data;
+ 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(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(conn, sockindex);
+ switch(result) {
+ case CURLE_OK:
+ break;
+ case CURLE_AGAIN:
+ if(!blocking)
+ /* CURLE_AGAIN in non-blocking mode is not an error */
+ return CURLE_OK;
+ /* FALLTHROUGH */
+ 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 Curl_nss_connect(struct connectdata *conn, int sockindex)
+{
+ return nss_connect_common(conn, sockindex, /* blocking */ NULL);
+}
+
+static CURLcode Curl_nss_connect_nonblocking(struct connectdata *conn,
+ int sockindex, bool *done)
+{
+ return nss_connect_common(conn, sockindex, done);
+}
+
+static ssize_t nss_send(struct connectdata *conn, /* connection data */
+ int sockindex, /* socketindex */
+ const void *mem, /* send this data */
+ size_t len, /* amount to write */
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ ssize_t rc;
+
+ /* The SelectClientCert() hook uses this for infof() and failf() but the
+ handle stored in nss_setup_connect() could have already been freed. */
+ backend->data = conn->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(conn->data, "SSL write: error %d (%s)\n", err, err_name);
+
+ /* print a human-readable message describing the error if available */
+ nss_print_error_message(conn->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 connectdata *conn, /* connection data */
+ int sockindex, /* socketindex */
+ char *buf, /* store read data here */
+ size_t buffersize, /* max amount to read */
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ ssize_t nread;
+
+ /* The SelectClientCert() hook uses this for infof() and failf() but the
+ handle stored in nss_setup_connect() could have already been freed. */
+ backend->data = conn->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(conn->data, "SSL read: errno %d (%s)\n", err, err_name);
+
+ /* print a human-readable message describing the error if available */
+ nss_print_error_message(conn->data, err);
+
+ *curlcode = (is_cc_error(err))
+ ? CURLE_SSL_CERTPROBLEM
+ : CURLE_RECV_ERROR;
+ }
+
+ return -1;
+ }
+
+ return nread;
+}
+
+static size_t Curl_nss_version(char *buffer, size_t size)
+{
+ return msnprintf(buffer, size, "NSS/%s", NSS_VERSION);
+}
+
+/* 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 Curl_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 Curl_nss_md5sum(unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *md5sum, /* output */
+ size_t md5len)
+{
+ PK11Context *MD5pw = PK11_CreateDigestContext(SEC_OID_MD5);
+ unsigned int MD5out;
+
+ if(!MD5pw)
+ return CURLE_NOT_BUILT_IN;
+
+ PK11_DigestOp(MD5pw, tmp, curlx_uztoui(tmplen));
+ PK11_DigestFinal(MD5pw, md5sum, &MD5out, curlx_uztoui(md5len));
+ PK11_DestroyContext(MD5pw, PR_TRUE);
+
+ return CURLE_OK;
+}
+
+static CURLcode Curl_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 Curl_nss_cert_status_request(void)
+{
+#ifdef SSL_ENABLE_OCSP_STAPLING
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+static bool Curl_nss_false_start(void)
+{
+#if NSSVERNUM >= 0x030f04 /* 3.15.4 */
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+static void *Curl_nss_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ struct ssl_backend_data *backend = connssl->backend;
+ (void)info;
+ 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),
+
+ Curl_nss_init, /* init */
+ Curl_nss_cleanup, /* cleanup */
+ Curl_nss_version, /* version */
+ Curl_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 */
+ Curl_nss_random, /* random */
+ Curl_nss_cert_status_request, /* cert_status_request */
+ Curl_nss_connect, /* connect */
+ Curl_nss_connect_nonblocking, /* connect_nonblocking */
+ Curl_nss_get_internals, /* get_internals */
+ Curl_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 */
+ Curl_nss_false_start, /* false_start */
+ Curl_nss_md5sum, /* md5sum */
+ Curl_nss_sha256sum /* sha256sum */
+};
+
+#endif /* USE_NSS */
diff --git a/contrib/libs/curl/lib/vtls/nssg.h b/contrib/libs/curl/lib/vtls/nssg.h
new file mode 100644
index 0000000000..37b364647b
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/nssg.h
@@ -0,0 +1,39 @@
+#ifndef HEADER_CURL_NSSG_H
+#define HEADER_CURL_NSSG_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, 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_NSS
+/*
+ * This header should only be needed to get included by vtls.c and nss.c
+ */
+
+#include "urldata.h"
+
+/* initialize NSS library if not already */
+CURLcode Curl_nss_force_init(struct Curl_easy *data);
+
+extern const struct Curl_ssl Curl_ssl_nss;
+
+#endif /* USE_NSS */
+#endif /* HEADER_CURL_NSSG_H */
diff --git a/contrib/libs/curl/lib/vtls/openssl.c b/contrib/libs/curl/lib/vtls/openssl.c
new file mode 100644
index 0000000000..e9c535f8f4
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/openssl.c
@@ -0,0 +1,4488 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, 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 OpenSSL-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_OPENSSL
+
+#include <limits.h>
+
+/* Wincrypt must be included before anything that could include OpenSSL. */
+#if defined(USE_WIN32_CRYPTO)
+#include <wincrypt.h>
+/* Undefine wincrypt conflicting symbols for BoringSSL. */
+#undef X509_NAME
+#undef X509_EXTENSIONS
+#undef PKCS7_ISSUER_AND_SERIAL
+#undef PKCS7_SIGNER_INFO
+#undef OCSP_REQUEST
+#undef OCSP_RESPONSE
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "formdata.h" /* for the boundary function */
+#include "url.h" /* for the ssl config check function */
+#include "inet_pton.h"
+#include "openssl.h"
+#include "connect.h"
+#include "slist.h"
+#include "select.h"
+#include "vtls.h"
+#include "keylog.h"
+#include "strcase.h"
+#include "hostcheck.h"
+#include "multiif.h"
+#include "strerror.h"
+#include "curl_printf.h"
+
+#include <openssl/ssl.h>
+#include <openssl/rand.h>
+#include <openssl/x509v3.h>
+#ifndef OPENSSL_NO_DSA
+#include <openssl/dsa.h>
+#endif
+#include <openssl/dh.h>
+#include <openssl/err.h>
+#include <openssl/md5.h>
+#include <openssl/conf.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/bio.h>
+#include <openssl/buffer.h>
+#include <openssl/pkcs12.h>
+
+#ifdef USE_AMISSL
+#include "amigaos.h"
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP)
+#include <openssl/ocsp.h>
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090700fL) && /* 0.9.7 or later */ \
+ !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_UI_CONSOLE)
+#define USE_OPENSSL_ENGINE
+#include <openssl/engine.h>
+#endif
+
+#include "warnless.h"
+#include "non-ascii.h" /* for Curl_convert_from_utf8 prototype */
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS
+ renegotiations when built with BoringSSL. Renegotiating is non-compliant
+ with HTTP/2 and "an extremely dangerous protocol feature". Beware.
+
+#define ALLOW_RENEG 1
+ */
+
+#ifndef OPENSSL_VERSION_NUMBER
+#error "OPENSSL_VERSION_NUMBER not defined"
+#endif
+
+#ifdef USE_OPENSSL_ENGINE
+#include <openssl/ui.h>
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x00909000L
+#define SSL_METHOD_QUAL const
+#else
+#define SSL_METHOD_QUAL
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
+#define HAVE_ERR_REMOVE_THREAD_STATE 1
+#endif
+
+#if !defined(HAVE_SSLV2_CLIENT_METHOD) || \
+ OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0+ has no SSLv2 */
+#undef OPENSSL_NO_SSL2 /* undef first to avoid compiler warnings */
+#define OPENSSL_NO_SSL2
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && /* OpenSSL 1.1.0+ */ \
+ !(defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#define SSLEAY_VERSION_NUMBER OPENSSL_VERSION_NUMBER
+#define HAVE_X509_GET0_EXTENSIONS 1 /* added in 1.1.0 -pre1 */
+#define HAVE_OPAQUE_EVP_PKEY 1 /* since 1.1.0 -pre3 */
+#define HAVE_OPAQUE_RSA_DSA_DH 1 /* since 1.1.0 -pre5 */
+#define CONST_EXTS const
+#define HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED 1
+
+/* funny typecast define due to difference in API */
+#ifdef LIBRESSL_VERSION_NUMBER
+#define ARG2_X509_signature_print (X509_ALGOR *)
+#else
+#define ARG2_X509_signature_print
+#endif
+
+#else
+/* For OpenSSL before 1.1.0 */
+#define ASN1_STRING_get0_data(x) ASN1_STRING_data(x)
+#define X509_get0_notBefore(x) X509_get_notBefore(x)
+#define X509_get0_notAfter(x) X509_get_notAfter(x)
+#define CONST_EXTS /* nope */
+#ifndef LIBRESSL_VERSION_NUMBER
+#define OpenSSL_version_num() SSLeay()
+#endif
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* 1.0.2 or later */ \
+ !(defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#define HAVE_X509_GET0_SIGNATURE 1
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) /* 1.0.2 or later */
+#define HAVE_SSL_GET_SHUTDOWN 1
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x10002003L && \
+ OPENSSL_VERSION_NUMBER <= 0x10002FFFL && \
+ !defined(OPENSSL_NO_COMP)
+#define HAVE_SSL_COMP_FREE_COMPRESSION_METHODS 1
+#endif
+
+#if (OPENSSL_VERSION_NUMBER < 0x0090808fL)
+/* not present in older OpenSSL */
+#define OPENSSL_load_builtin_modules(x)
+#endif
+
+/*
+ * Whether SSL_CTX_set_keylog_callback is available.
+ * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287
+ * BoringSSL: supported since d28f59c27bac (committed 2015-11-19)
+ * LibreSSL: unsupported in at least 2.7.2 (explicitly check for it since it
+ * lies and pretends to be OpenSSL 2.0.0).
+ */
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \
+ !defined(LIBRESSL_VERSION_NUMBER)) || \
+ defined(OPENSSL_IS_BORINGSSL)
+#define HAVE_KEYLOG_CALLBACK
+#endif
+
+/* Whether SSL_CTX_set_ciphersuites is available.
+ * OpenSSL: supported since 1.1.1 (commit a53b5be6a05)
+ * BoringSSL: no
+ * LibreSSL: no
+ */
+#if ((OPENSSL_VERSION_NUMBER >= 0x10101000L) && \
+ !defined(LIBRESSL_VERSION_NUMBER) && \
+ !defined(OPENSSL_IS_BORINGSSL))
+#define HAVE_SSL_CTX_SET_CIPHERSUITES
+#define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
+/* SET_EC_CURVES available under the same preconditions: see
+ * https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set1_groups.html
+ */
+#define HAVE_SSL_CTX_SET_EC_CURVES
+#endif
+
+#if defined(LIBRESSL_VERSION_NUMBER)
+#define OSSL_PACKAGE "LibreSSL"
+#elif defined(OPENSSL_IS_BORINGSSL)
+#define OSSL_PACKAGE "BoringSSL"
+#else
+#define OSSL_PACKAGE "OpenSSL"
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+/* up2date versions of OpenSSL maintain the default reasonably secure without
+ * breaking compatibility, so it is better not to override the default by curl
+ */
+#define DEFAULT_CIPHER_SELECTION NULL
+#else
+/* ... but it is not the case with old versions of OpenSSL */
+#define DEFAULT_CIPHER_SELECTION \
+ "ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH"
+#endif
+
+#ifdef HAVE_OPENSSL_SRP
+/* the function exists */
+#ifdef USE_TLS_SRP
+/* the functionality is not disabled */
+#define USE_OPENSSL_SRP
+#endif
+#endif
+
+struct ssl_backend_data {
+ /* these ones requires specific SSL-types */
+ SSL_CTX* ctx;
+ SSL* handle;
+ X509* server_cert;
+#ifndef HAVE_KEYLOG_CALLBACK
+ /* Set to true once a valid keylog entry has been created to avoid dupes. */
+ bool keylog_done;
+#endif
+};
+
+/*
+ * Number of bytes to read from the random number seed file. This must be
+ * a finite value (because some entropy "files" like /dev/urandom have
+ * an infinite length), but must be large enough to provide enough
+ * entropy to properly seed OpenSSL's PRNG.
+ */
+#define RAND_LOAD_LENGTH 1024
+
+#ifdef HAVE_KEYLOG_CALLBACK
+static void ossl_keylog_callback(const SSL *ssl, const char *line)
+{
+ (void)ssl;
+
+ Curl_tls_keylog_write_line(line);
+}
+#else
+/*
+ * ossl_log_tls12_secret is called by libcurl to make the CLIENT_RANDOMs if the
+ * OpenSSL being used doesn't have native support for doing that.
+ */
+static void
+ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done)
+{
+ const SSL_SESSION *session = SSL_get_session(ssl);
+ unsigned char client_random[SSL3_RANDOM_SIZE];
+ unsigned char master_key[SSL_MAX_MASTER_KEY_LENGTH];
+ int master_key_length = 0;
+
+ if(!session || *keylog_done)
+ return;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+ !(defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+ /* ssl->s3 is not checked in openssl 1.1.0-pre6, but let's assume that
+ * we have a valid SSL context if we have a non-NULL session. */
+ SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE);
+ master_key_length = (int)
+ SSL_SESSION_get_master_key(session, master_key, SSL_MAX_MASTER_KEY_LENGTH);
+#else
+ if(ssl->s3 && session->master_key_length > 0) {
+ master_key_length = session->master_key_length;
+ memcpy(master_key, session->master_key, session->master_key_length);
+ memcpy(client_random, ssl->s3->client_random, SSL3_RANDOM_SIZE);
+ }
+#endif
+
+ /* The handshake has not progressed sufficiently yet, or this is a TLS 1.3
+ * session (when curl was built with older OpenSSL headers and running with
+ * newer OpenSSL runtime libraries). */
+ if(master_key_length <= 0)
+ return;
+
+ *keylog_done = true;
+ Curl_tls_keylog_write("CLIENT_RANDOM", client_random,
+ master_key, master_key_length);
+}
+#endif /* !HAVE_KEYLOG_CALLBACK */
+
+static const char *SSL_ERROR_to_str(int err)
+{
+ switch(err) {
+ case SSL_ERROR_NONE:
+ return "SSL_ERROR_NONE";
+ case SSL_ERROR_SSL:
+ return "SSL_ERROR_SSL";
+ case SSL_ERROR_WANT_READ:
+ return "SSL_ERROR_WANT_READ";
+ case SSL_ERROR_WANT_WRITE:
+ return "SSL_ERROR_WANT_WRITE";
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ return "SSL_ERROR_WANT_X509_LOOKUP";
+ case SSL_ERROR_SYSCALL:
+ return "SSL_ERROR_SYSCALL";
+ case SSL_ERROR_ZERO_RETURN:
+ return "SSL_ERROR_ZERO_RETURN";
+ case SSL_ERROR_WANT_CONNECT:
+ return "SSL_ERROR_WANT_CONNECT";
+ case SSL_ERROR_WANT_ACCEPT:
+ return "SSL_ERROR_WANT_ACCEPT";
+#if defined(SSL_ERROR_WANT_ASYNC)
+ case SSL_ERROR_WANT_ASYNC:
+ return "SSL_ERROR_WANT_ASYNC";
+#endif
+#if defined(SSL_ERROR_WANT_ASYNC_JOB)
+ case SSL_ERROR_WANT_ASYNC_JOB:
+ return "SSL_ERROR_WANT_ASYNC_JOB";
+#endif
+#if defined(SSL_ERROR_WANT_EARLY)
+ case SSL_ERROR_WANT_EARLY:
+ return "SSL_ERROR_WANT_EARLY";
+#endif
+ default:
+ return "SSL_ERROR unknown";
+ }
+}
+
+/* Return error string for last OpenSSL error
+ */
+static char *ossl_strerror(unsigned long error, char *buf, size_t size)
+{
+ if(size)
+ *buf = '\0';
+
+#ifdef OPENSSL_IS_BORINGSSL
+ ERR_error_string_n((uint32_t)error, buf, size);
+#else
+ ERR_error_string_n(error, buf, size);
+#endif
+
+ if(size > 1 && !*buf) {
+ strncpy(buf, (error ? "Unknown error" : "No error"), size);
+ buf[size - 1] = '\0';
+ }
+
+ return buf;
+}
+
+/* Return an extra data index for the connection data.
+ * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
+ */
+static int ossl_get_ssl_conn_index(void)
+{
+ static int ssl_ex_data_conn_index = -1;
+ if(ssl_ex_data_conn_index < 0) {
+ ssl_ex_data_conn_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
+ }
+ return ssl_ex_data_conn_index;
+}
+
+/* Return an extra data index for the sockindex.
+ * This index can be used with SSL_get_ex_data() and SSL_set_ex_data().
+ */
+static int ossl_get_ssl_sockindex_index(void)
+{
+ static int ssl_ex_data_sockindex_index = -1;
+ if(ssl_ex_data_sockindex_index < 0) {
+ ssl_ex_data_sockindex_index = SSL_get_ex_new_index(0, NULL, NULL, NULL,
+ NULL);
+ }
+ return ssl_ex_data_sockindex_index;
+}
+
+static int passwd_callback(char *buf, int num, int encrypting,
+ void *global_passwd)
+{
+ DEBUGASSERT(0 == encrypting);
+
+ if(!encrypting) {
+ int klen = curlx_uztosi(strlen((char *)global_passwd));
+ if(num > klen) {
+ memcpy(buf, global_passwd, klen + 1);
+ return klen;
+ }
+ }
+ return 0;
+}
+
+/*
+ * rand_enough() returns TRUE if we have seeded the random engine properly.
+ */
+static bool rand_enough(void)
+{
+ return (0 != RAND_status()) ? TRUE : FALSE;
+}
+
+static CURLcode Curl_ossl_seed(struct Curl_easy *data)
+{
+ /* we have the "SSL is seeded" boolean static to prevent multiple
+ time-consuming seedings in vain */
+ static bool ssl_seeded = FALSE;
+ char fname[256];
+
+ if(ssl_seeded)
+ return CURLE_OK;
+
+ if(rand_enough()) {
+ /* OpenSSL 1.1.0+ will return here */
+ ssl_seeded = TRUE;
+ return CURLE_OK;
+ }
+
+#ifndef RANDOM_FILE
+ /* if RANDOM_FILE isn't defined, we only perform this if an option tells
+ us to! */
+ if(data->set.str[STRING_SSL_RANDOM_FILE])
+#define RANDOM_FILE "" /* doesn't matter won't be used */
+#endif
+ {
+ /* let the option override the define */
+ RAND_load_file((data->set.str[STRING_SSL_RANDOM_FILE]?
+ data->set.str[STRING_SSL_RANDOM_FILE]:
+ RANDOM_FILE),
+ RAND_LOAD_LENGTH);
+ if(rand_enough())
+ return CURLE_OK;
+ }
+
+#if defined(HAVE_RAND_EGD)
+ /* only available in OpenSSL 0.9.5 and later */
+ /* EGD_SOCKET is set at configure time or not at all */
+#ifndef EGD_SOCKET
+ /* If we don't have the define set, we only do this if the egd-option
+ is set */
+ if(data->set.str[STRING_SSL_EGDSOCKET])
+#define EGD_SOCKET "" /* doesn't matter won't be used */
+#endif
+ {
+ /* If there's an option and a define, the option overrides the
+ define */
+ int ret = RAND_egd(data->set.str[STRING_SSL_EGDSOCKET]?
+ data->set.str[STRING_SSL_EGDSOCKET]:EGD_SOCKET);
+ if(-1 != ret) {
+ if(rand_enough())
+ return CURLE_OK;
+ }
+ }
+#endif
+
+ /* fallback to a custom seeding of the PRNG using a hash based on a current
+ time */
+ do {
+ unsigned char randb[64];
+ size_t len = sizeof(randb);
+ size_t i, i_max;
+ for(i = 0, i_max = len / sizeof(struct curltime); i < i_max; ++i) {
+ struct curltime tv = Curl_now();
+ Curl_wait_ms(1);
+ tv.tv_sec *= i + 1;
+ tv.tv_usec *= (unsigned int)i + 2;
+ tv.tv_sec ^= ((Curl_now().tv_sec + Curl_now().tv_usec) *
+ (i + 3)) << 8;
+ tv.tv_usec ^= (unsigned int) ((Curl_now().tv_sec +
+ Curl_now().tv_usec) *
+ (i + 4)) << 16;
+ memcpy(&randb[i * sizeof(struct curltime)], &tv,
+ sizeof(struct curltime));
+ }
+ RAND_add(randb, (int)len, (double)len/2);
+ } while(!rand_enough());
+
+ /* generates a default path for the random seed file */
+ fname[0] = 0; /* blank it first */
+ RAND_file_name(fname, sizeof(fname));
+ if(fname[0]) {
+ /* we got a file name to try */
+ RAND_load_file(fname, RAND_LOAD_LENGTH);
+ if(rand_enough())
+ return CURLE_OK;
+ }
+
+ infof(data, "libcurl is now using a weak random seed!\n");
+ return (rand_enough() ? CURLE_OK :
+ CURLE_SSL_CONNECT_ERROR /* confusing error code */);
+}
+
+#ifndef SSL_FILETYPE_ENGINE
+#define SSL_FILETYPE_ENGINE 42
+#endif
+#ifndef SSL_FILETYPE_PKCS12
+#define SSL_FILETYPE_PKCS12 43
+#endif
+static int do_file_type(const char *type)
+{
+ if(!type || !type[0])
+ return SSL_FILETYPE_PEM;
+ if(strcasecompare(type, "PEM"))
+ return SSL_FILETYPE_PEM;
+ if(strcasecompare(type, "DER"))
+ return SSL_FILETYPE_ASN1;
+ if(strcasecompare(type, "ENG"))
+ return SSL_FILETYPE_ENGINE;
+ if(strcasecompare(type, "P12"))
+ return SSL_FILETYPE_PKCS12;
+ return -1;
+}
+
+#ifdef USE_OPENSSL_ENGINE
+/*
+ * Supply default password to the engine user interface conversation.
+ * The password is passed by OpenSSL engine from ENGINE_load_private_key()
+ * last argument to the ui and can be obtained by UI_get0_user_data(ui) here.
+ */
+static int ssl_ui_reader(UI *ui, UI_STRING *uis)
+{
+ const char *password;
+ switch(UI_get_string_type(uis)) {
+ case UIT_PROMPT:
+ case UIT_VERIFY:
+ password = (const char *)UI_get0_user_data(ui);
+ if(password && (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD)) {
+ UI_set_result(ui, uis, password);
+ return 1;
+ }
+ default:
+ break;
+ }
+ return (UI_method_get_reader(UI_OpenSSL()))(ui, uis);
+}
+
+/*
+ * Suppress interactive request for a default password if available.
+ */
+static int ssl_ui_writer(UI *ui, UI_STRING *uis)
+{
+ switch(UI_get_string_type(uis)) {
+ case UIT_PROMPT:
+ case UIT_VERIFY:
+ if(UI_get0_user_data(ui) &&
+ (UI_get_input_flags(uis) & UI_INPUT_FLAG_DEFAULT_PWD)) {
+ return 1;
+ }
+ default:
+ break;
+ }
+ return (UI_method_get_writer(UI_OpenSSL()))(ui, uis);
+}
+
+/*
+ * Check if a given string is a PKCS#11 URI
+ */
+static bool is_pkcs11_uri(const char *string)
+{
+ return (string && strncasecompare(string, "pkcs11:", 7));
+}
+
+#endif
+
+static CURLcode Curl_ossl_set_engine(struct Curl_easy *data,
+ const char *engine);
+
+static int
+SSL_CTX_use_certificate_bio(SSL_CTX *ctx, BIO *in, int type,
+ const char *key_passwd)
+{
+ int ret = 0;
+ X509 *x = NULL;
+
+ if(type == SSL_FILETYPE_ASN1) {
+ /* j = ERR_R_ASN1_LIB; */
+ x = d2i_X509_bio(in, NULL);
+ }
+ else if(type == SSL_FILETYPE_PEM) {
+ /* ERR_R_PEM_LIB; */
+ x = PEM_read_bio_X509(in, NULL,
+ passwd_callback, (void *)key_passwd);
+ }
+ else {
+ ret = 0;
+ goto end;
+ }
+
+ if(x == NULL) {
+ ret = 0;
+ goto end;
+ }
+
+ ret = SSL_CTX_use_certificate(ctx, x);
+ end:
+ X509_free(x);
+ return ret;
+}
+
+static int
+SSL_CTX_use_PrivateKey_bio(SSL_CTX *ctx, BIO* in, int type,
+ const char *key_passwd)
+{
+ int ret = 0;
+ EVP_PKEY *pkey = NULL;
+
+ if(type == SSL_FILETYPE_PEM)
+ pkey = PEM_read_bio_PrivateKey(in, NULL, passwd_callback,
+ (void *)key_passwd);
+ else if(type == SSL_FILETYPE_ASN1)
+ pkey = d2i_PrivateKey_bio(in, NULL);
+ else {
+ ret = 0;
+ goto end;
+ }
+ if(pkey == NULL) {
+ ret = 0;
+ goto end;
+ }
+ ret = SSL_CTX_use_PrivateKey(ctx, pkey);
+ EVP_PKEY_free(pkey);
+ end:
+ return ret;
+}
+
+static int
+SSL_CTX_use_certificate_chain_bio(SSL_CTX *ctx, BIO* in,
+ const char *key_passwd)
+{
+/* SSL_CTX_add1_chain_cert introduced in OpenSSL 1.0.2 */
+#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* OpenSSL 1.0.2 or later */ \
+ !(defined(LIBRESSL_VERSION_NUMBER) && \
+ (LIBRESSL_VERSION_NUMBER < 0x2090100fL)) /* LibreSSL 2.9.1 or later */
+ int ret = 0;
+ X509 *x = NULL;
+ void *passwd_callback_userdata = (void *)key_passwd;
+
+ ERR_clear_error();
+
+ x = PEM_read_bio_X509_AUX(in, NULL,
+ passwd_callback, (void *)key_passwd);
+
+ if(x == NULL) {
+ ret = 0;
+ goto end;
+ }
+
+ ret = SSL_CTX_use_certificate(ctx, x);
+
+ if(ERR_peek_error() != 0)
+ ret = 0;
+
+ if(ret) {
+ X509 *ca;
+ unsigned long err;
+
+ if(!SSL_CTX_clear_chain_certs(ctx)) {
+ ret = 0;
+ goto end;
+ }
+
+ while((ca = PEM_read_bio_X509(in, NULL, passwd_callback,
+ passwd_callback_userdata))
+ != NULL) {
+
+ if(!SSL_CTX_add0_chain_cert(ctx, ca)) {
+ X509_free(ca);
+ ret = 0;
+ goto end;
+ }
+ }
+
+ err = ERR_peek_last_error();
+ if((ERR_GET_LIB(err) == ERR_LIB_PEM) &&
+ (ERR_GET_REASON(err) == PEM_R_NO_START_LINE))
+ ERR_clear_error();
+ else
+ ret = 0;
+ }
+
+ end:
+ X509_free(x);
+ return ret;
+#else
+ (void)ctx; /* unused */
+ (void)in; /* unused */
+ (void)key_passwd; /* unused */
+ return 0;
+#endif
+}
+
+static
+int cert_stuff(struct connectdata *conn,
+ SSL_CTX* ctx,
+ char *cert_file,
+ BIO *cert_bio,
+ const char *cert_type,
+ char *key_file,
+ BIO* key_bio,
+ const char *key_type,
+ char *key_passwd)
+{
+ struct Curl_easy *data = conn->data;
+ char error_buffer[256];
+ bool check_privkey = TRUE;
+
+ int file_type = do_file_type(cert_type);
+
+ if(cert_file || cert_bio || (file_type == SSL_FILETYPE_ENGINE)) {
+ SSL *ssl;
+ X509 *x509;
+ int cert_done = 0;
+ int cert_use_result;
+
+ if(key_passwd) {
+ /* set the password in the callback userdata */
+ SSL_CTX_set_default_passwd_cb_userdata(ctx, key_passwd);
+ /* Set passwd callback: */
+ SSL_CTX_set_default_passwd_cb(ctx, passwd_callback);
+ }
+
+
+ switch(file_type) {
+ case SSL_FILETYPE_PEM:
+ /* SSL_CTX_use_certificate_chain_file() only works on PEM files */
+ cert_use_result = cert_bio ?
+ SSL_CTX_use_certificate_chain_bio(ctx, cert_bio, key_passwd) :
+ SSL_CTX_use_certificate_chain_file(ctx, cert_file);
+ if(cert_use_result != 1) {
+ failf(data,
+ "could not load PEM client certificate, " OSSL_PACKAGE
+ " error %s, "
+ "(no key found, wrong pass phrase, or wrong file format?)",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ return 0;
+ }
+ break;
+
+ case SSL_FILETYPE_ASN1:
+ /* SSL_CTX_use_certificate_file() works with either PEM or ASN1, but
+ we use the case above for PEM so this can only be performed with
+ ASN1 files. */
+
+ cert_use_result = cert_bio ?
+ SSL_CTX_use_certificate_bio(ctx, cert_bio,
+ file_type, key_passwd) :
+ SSL_CTX_use_certificate_file(ctx, cert_file, file_type);
+ if(cert_use_result != 1) {
+ failf(data,
+ "could not load ASN1 client certificate, " OSSL_PACKAGE
+ " error %s, "
+ "(no key found, wrong pass phrase, or wrong file format?)",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ return 0;
+ }
+ break;
+ case SSL_FILETYPE_ENGINE:
+#if defined(USE_OPENSSL_ENGINE) && defined(ENGINE_CTRL_GET_CMD_FROM_NAME)
+ {
+ /* Implicitly use pkcs11 engine if none was provided and the
+ * cert_file is a PKCS#11 URI */
+ if(!data->state.engine) {
+ if(is_pkcs11_uri(cert_file)) {
+ if(Curl_ossl_set_engine(data, "pkcs11") != CURLE_OK) {
+ return 0;
+ }
+ }
+ }
+
+ if(data->state.engine) {
+ const char *cmd_name = "LOAD_CERT_CTRL";
+ struct {
+ const char *cert_id;
+ X509 *cert;
+ } params;
+
+ params.cert_id = cert_file;
+ params.cert = NULL;
+
+ /* Does the engine supports LOAD_CERT_CTRL ? */
+ if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
+ 0, (void *)cmd_name, NULL)) {
+ failf(data, "ssl engine does not support loading certificates");
+ return 0;
+ }
+
+ /* Load the certificate from the engine */
+ if(!ENGINE_ctrl_cmd(data->state.engine, cmd_name,
+ 0, &params, NULL, 1)) {
+ failf(data, "ssl engine cannot load client cert with id"
+ " '%s' [%s]", cert_file,
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+
+ if(!params.cert) {
+ failf(data, "ssl engine didn't initialized the certificate "
+ "properly.");
+ return 0;
+ }
+
+ if(SSL_CTX_use_certificate(ctx, params.cert) != 1) {
+ failf(data, "unable to set client certificate");
+ X509_free(params.cert);
+ return 0;
+ }
+ X509_free(params.cert); /* we don't need the handle any more... */
+ }
+ else {
+ failf(data, "crypto engine not set, can't load certificate");
+ return 0;
+ }
+ }
+ break;
+#else
+ failf(data, "file type ENG for certificate not implemented");
+ return 0;
+#endif
+
+ case SSL_FILETYPE_PKCS12:
+ {
+ BIO *fp = NULL;
+ PKCS12 *p12 = NULL;
+ EVP_PKEY *pri;
+ STACK_OF(X509) *ca = NULL;
+ if(!cert_bio) {
+ fp = BIO_new(BIO_s_file());
+ if(fp == NULL) {
+ failf(data,
+ "BIO_new return NULL, " OSSL_PACKAGE
+ " error %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ return 0;
+ }
+
+ if(BIO_read_filename(fp, cert_file) <= 0) {
+ failf(data, "could not open PKCS12 file '%s'", cert_file);
+ BIO_free(fp);
+ return 0;
+ }
+ }
+
+ p12 = d2i_PKCS12_bio(cert_bio ? cert_bio : fp, NULL);
+ if(fp)
+ BIO_free(fp);
+
+ if(!p12) {
+ failf(data, "error reading PKCS12 file '%s'",
+ cert_bio ? "(memory blob)" : cert_file);
+ return 0;
+ }
+
+ PKCS12_PBE_add();
+
+ if(!PKCS12_parse(p12, key_passwd, &pri, &x509,
+ &ca)) {
+ failf(data,
+ "could not parse PKCS12 file, check password, " OSSL_PACKAGE
+ " error %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ PKCS12_free(p12);
+ return 0;
+ }
+
+ PKCS12_free(p12);
+
+ if(SSL_CTX_use_certificate(ctx, x509) != 1) {
+ failf(data,
+ "could not load PKCS12 client certificate, " OSSL_PACKAGE
+ " error %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ goto fail;
+ }
+
+ if(SSL_CTX_use_PrivateKey(ctx, pri) != 1) {
+ failf(data, "unable to use private key from PKCS12 file '%s'",
+ cert_file);
+ goto fail;
+ }
+
+ if(!SSL_CTX_check_private_key (ctx)) {
+ failf(data, "private key from PKCS12 file '%s' "
+ "does not match certificate in same file", cert_file);
+ goto fail;
+ }
+ /* Set Certificate Verification chain */
+ if(ca) {
+ while(sk_X509_num(ca)) {
+ /*
+ * Note that sk_X509_pop() is used below to make sure the cert is
+ * removed from the stack properly before getting passed to
+ * SSL_CTX_add_extra_chain_cert(), which takes ownership. Previously
+ * we used sk_X509_value() instead, but then we'd clean it in the
+ * subsequent sk_X509_pop_free() call.
+ */
+ X509 *x = sk_X509_pop(ca);
+ if(!SSL_CTX_add_client_CA(ctx, x)) {
+ X509_free(x);
+ failf(data, "cannot add certificate to client CA list");
+ goto fail;
+ }
+ if(!SSL_CTX_add_extra_chain_cert(ctx, x)) {
+ X509_free(x);
+ failf(data, "cannot add certificate to certificate chain");
+ goto fail;
+ }
+ }
+ }
+
+ cert_done = 1;
+ fail:
+ EVP_PKEY_free(pri);
+ X509_free(x509);
+#ifdef USE_AMISSL
+ sk_X509_pop_free(ca, Curl_amiga_X509_free);
+#else
+ sk_X509_pop_free(ca, X509_free);
+#endif
+ if(!cert_done)
+ return 0; /* failure! */
+ break;
+ }
+ default:
+ failf(data, "not supported file type '%s' for certificate", cert_type);
+ return 0;
+ }
+
+ if((!key_file) && (!key_bio)) {
+ key_file = cert_file;
+ key_bio = cert_bio;
+ }
+ else
+ file_type = do_file_type(key_type);
+
+ switch(file_type) {
+ case SSL_FILETYPE_PEM:
+ if(cert_done)
+ break;
+ /* FALLTHROUGH */
+ case SSL_FILETYPE_ASN1:
+ cert_use_result = key_bio ?
+ SSL_CTX_use_PrivateKey_bio(ctx, key_bio, file_type, key_passwd) :
+ SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type);
+ if(cert_use_result != 1) {
+ failf(data, "unable to set private key file: '%s' type %s",
+ key_file?key_file:"(memory blob)", key_type?key_type:"PEM");
+ return 0;
+ }
+ break;
+ case SSL_FILETYPE_ENGINE:
+#ifdef USE_OPENSSL_ENGINE
+ { /* XXXX still needs some work */
+ EVP_PKEY *priv_key = NULL;
+
+ /* Implicitly use pkcs11 engine if none was provided and the
+ * key_file is a PKCS#11 URI */
+ if(!data->state.engine) {
+ if(is_pkcs11_uri(key_file)) {
+ if(Curl_ossl_set_engine(data, "pkcs11") != CURLE_OK) {
+ return 0;
+ }
+ }
+ }
+
+ if(data->state.engine) {
+ UI_METHOD *ui_method =
+ UI_create_method((char *)"curl user interface");
+ if(!ui_method) {
+ failf(data, "unable do create " OSSL_PACKAGE
+ " user-interface method");
+ return 0;
+ }
+ UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL()));
+ UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL()));
+ UI_method_set_reader(ui_method, ssl_ui_reader);
+ UI_method_set_writer(ui_method, ssl_ui_writer);
+ /* the typecast below was added to please mingw32 */
+ priv_key = (EVP_PKEY *)
+ ENGINE_load_private_key(data->state.engine, key_file,
+ ui_method,
+ key_passwd);
+ UI_destroy_method(ui_method);
+ if(!priv_key) {
+ failf(data, "failed to load private key from crypto engine");
+ return 0;
+ }
+ if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) {
+ failf(data, "unable to set private key");
+ EVP_PKEY_free(priv_key);
+ return 0;
+ }
+ EVP_PKEY_free(priv_key); /* we don't need the handle any more... */
+ }
+ else {
+ failf(data, "crypto engine not set, can't load private key");
+ return 0;
+ }
+ }
+ break;
+#else
+ failf(data, "file type ENG for private key not supported");
+ return 0;
+#endif
+ case SSL_FILETYPE_PKCS12:
+ if(!cert_done) {
+ failf(data, "file type P12 for private key not supported");
+ return 0;
+ }
+ break;
+ default:
+ failf(data, "not supported file type for private key");
+ return 0;
+ }
+
+ ssl = SSL_new(ctx);
+ if(!ssl) {
+ failf(data, "unable to create an SSL structure");
+ return 0;
+ }
+
+ x509 = SSL_get_certificate(ssl);
+
+ /* This version was provided by Evan Jordan and is supposed to not
+ leak memory as the previous version: */
+ if(x509) {
+ EVP_PKEY *pktmp = X509_get_pubkey(x509);
+ EVP_PKEY_copy_parameters(pktmp, SSL_get_privatekey(ssl));
+ EVP_PKEY_free(pktmp);
+ }
+
+#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_IS_BORINGSSL)
+ {
+ /* If RSA is used, don't check the private key if its flags indicate
+ * it doesn't support it. */
+ EVP_PKEY *priv_key = SSL_get_privatekey(ssl);
+ int pktype;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+ pktype = EVP_PKEY_id(priv_key);
+#else
+ pktype = priv_key->type;
+#endif
+ if(pktype == EVP_PKEY_RSA) {
+ RSA *rsa = EVP_PKEY_get1_RSA(priv_key);
+ if(RSA_flags(rsa) & RSA_METHOD_FLAG_NO_CHECK)
+ check_privkey = FALSE;
+ RSA_free(rsa); /* Decrement reference count */
+ }
+ }
+#endif
+
+ SSL_free(ssl);
+
+ /* If we are using DSA, we can copy the parameters from
+ * the private key */
+
+ if(check_privkey == TRUE) {
+ /* Now we know that a key and cert have been set against
+ * the SSL context */
+ if(!SSL_CTX_check_private_key(ctx)) {
+ failf(data, "Private key does not match the certificate public key");
+ return 0;
+ }
+ }
+ }
+ return 1;
+}
+
+/* returns non-zero on failure */
+static int x509_name_oneline(X509_NAME *a, char *buf, size_t size)
+{
+ BIO *bio_out = BIO_new(BIO_s_mem());
+ BUF_MEM *biomem;
+ int rc;
+
+ if(!bio_out)
+ return 1; /* alloc failed! */
+
+ rc = X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_SPLUS_SPC);
+ BIO_get_mem_ptr(bio_out, &biomem);
+
+ if((size_t)biomem->length < size)
+ size = biomem->length;
+ else
+ size--; /* don't overwrite the buffer end */
+
+ memcpy(buf, biomem->data, size);
+ buf[size] = 0;
+
+ BIO_free(bio_out);
+
+ return !rc;
+}
+
+/**
+ * Global SSL init
+ *
+ * @retval 0 error initializing SSL
+ * @retval 1 SSL initialized successfully
+ */
+static int Curl_ossl_init(void)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
+ !defined(LIBRESSL_VERSION_NUMBER)
+ const uint64_t flags =
+#ifdef OPENSSL_INIT_ENGINE_ALL_BUILTIN
+ /* not present in BoringSSL */
+ OPENSSL_INIT_ENGINE_ALL_BUILTIN |
+#endif
+#ifdef CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG
+ OPENSSL_INIT_NO_LOAD_CONFIG |
+#else
+ OPENSSL_INIT_LOAD_CONFIG |
+#endif
+ 0;
+ OPENSSL_init_ssl(flags, NULL);
+#else
+ OPENSSL_load_builtin_modules();
+
+#ifdef USE_OPENSSL_ENGINE
+ ENGINE_load_builtin_engines();
+#endif
+
+/* CONF_MFLAGS_DEFAULT_SECTION was introduced some time between 0.9.8b and
+ 0.9.8e */
+#ifndef CONF_MFLAGS_DEFAULT_SECTION
+#define CONF_MFLAGS_DEFAULT_SECTION 0x0
+#endif
+
+#ifndef CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG
+ CONF_modules_load_file(NULL, NULL,
+ CONF_MFLAGS_DEFAULT_SECTION|
+ CONF_MFLAGS_IGNORE_MISSING_FILE);
+#endif
+
+ /* Lets get nice error messages */
+ SSL_load_error_strings();
+
+ /* Init the global ciphers and digests */
+ if(!SSLeay_add_ssl_algorithms())
+ return 0;
+
+ OpenSSL_add_all_algorithms();
+#endif
+
+ Curl_tls_keylog_open();
+
+ /* Initialize the extra data indexes */
+ if(ossl_get_ssl_conn_index() < 0 || ossl_get_ssl_sockindex_index() < 0)
+ return 0;
+
+ return 1;
+}
+
+/* Global cleanup */
+static void Curl_ossl_cleanup(void)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
+ !defined(LIBRESSL_VERSION_NUMBER)
+ /* OpenSSL 1.1 deprecates all these cleanup functions and
+ turns them into no-ops in OpenSSL 1.0 compatibility mode */
+#else
+ /* Free ciphers and digests lists */
+ EVP_cleanup();
+
+#ifdef USE_OPENSSL_ENGINE
+ /* Free engine list */
+ ENGINE_cleanup();
+#endif
+
+ /* Free OpenSSL error strings */
+ ERR_free_strings();
+
+ /* Free thread local error state, destroying hash upon zero refcount */
+#ifdef HAVE_ERR_REMOVE_THREAD_STATE
+ ERR_remove_thread_state(NULL);
+#else
+ ERR_remove_state(0);
+#endif
+
+ /* Free all memory allocated by all configuration modules */
+ CONF_modules_free();
+
+#ifdef HAVE_SSL_COMP_FREE_COMPRESSION_METHODS
+ SSL_COMP_free_compression_methods();
+#endif
+#endif
+
+ Curl_tls_keylog_close();
+}
+
+/*
+ * This function is used 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 Curl_ossl_check_cxn(struct connectdata *conn)
+{
+ /* SSL_peek takes data out of the raw recv buffer without peeking so we use
+ recv MSG_PEEK instead. Bug #795 */
+#ifdef MSG_PEEK
+ char buf;
+ ssize_t nread;
+ nread = recv((RECV_TYPE_ARG1)conn->sock[FIRSTSOCKET], (RECV_TYPE_ARG2)&buf,
+ (RECV_TYPE_ARG3)1, (RECV_TYPE_ARG4)MSG_PEEK);
+ if(nread == 0)
+ return 0; /* connection has been closed */
+ if(nread == 1)
+ return 1; /* connection still in place */
+ else if(nread == -1) {
+ int err = SOCKERRNO;
+ if(err == EINPROGRESS ||
+#if defined(EAGAIN) && (EAGAIN != EWOULDBLOCK)
+ err == EAGAIN ||
+#endif
+ err == EWOULDBLOCK)
+ return 1; /* connection still in place */
+ if(err == ECONNRESET ||
+#ifdef ECONNABORTED
+ err == ECONNABORTED ||
+#endif
+#ifdef ENETDOWN
+ err == ENETDOWN ||
+#endif
+#ifdef ENETRESET
+ err == ENETRESET ||
+#endif
+#ifdef ESHUTDOWN
+ err == ESHUTDOWN ||
+#endif
+#ifdef ETIMEDOUT
+ err == ETIMEDOUT ||
+#endif
+ err == ENOTCONN)
+ return 0; /* connection has been closed */
+ }
+#endif
+ return -1; /* connection status unknown */
+}
+
+/* Selects an OpenSSL crypto engine
+ */
+static CURLcode Curl_ossl_set_engine(struct Curl_easy *data,
+ const char *engine)
+{
+#ifdef USE_OPENSSL_ENGINE
+ ENGINE *e;
+
+#if OPENSSL_VERSION_NUMBER >= 0x00909000L
+ e = ENGINE_by_id(engine);
+#else
+ /* avoid memory leak */
+ for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) {
+ const char *e_id = ENGINE_get_id(e);
+ if(!strcmp(engine, e_id))
+ break;
+ }
+#endif
+
+ if(!e) {
+ failf(data, "SSL Engine '%s' not found", engine);
+ return CURLE_SSL_ENGINE_NOTFOUND;
+ }
+
+ if(data->state.engine) {
+ ENGINE_finish(data->state.engine);
+ ENGINE_free(data->state.engine);
+ data->state.engine = NULL;
+ }
+ if(!ENGINE_init(e)) {
+ char buf[256];
+
+ ENGINE_free(e);
+ failf(data, "Failed to initialise SSL Engine '%s':\n%s",
+ engine, ossl_strerror(ERR_get_error(), buf, sizeof(buf)));
+ return CURLE_SSL_ENGINE_INITFAILED;
+ }
+ data->state.engine = e;
+ return CURLE_OK;
+#else
+ (void)engine;
+ failf(data, "SSL Engine not supported");
+ return CURLE_SSL_ENGINE_NOTFOUND;
+#endif
+}
+
+/* Sets engine as default for all SSL operations
+ */
+static CURLcode Curl_ossl_set_engine_default(struct Curl_easy *data)
+{
+#ifdef USE_OPENSSL_ENGINE
+ if(data->state.engine) {
+ if(ENGINE_set_default(data->state.engine, ENGINE_METHOD_ALL) > 0) {
+ infof(data, "set default crypto engine '%s'\n",
+ ENGINE_get_id(data->state.engine));
+ }
+ else {
+ failf(data, "set default crypto engine '%s' failed",
+ ENGINE_get_id(data->state.engine));
+ return CURLE_SSL_ENGINE_SETFAILED;
+ }
+ }
+#else
+ (void) data;
+#endif
+ return CURLE_OK;
+}
+
+/* Return list of OpenSSL crypto engine names.
+ */
+static struct curl_slist *Curl_ossl_engines_list(struct Curl_easy *data)
+{
+ struct curl_slist *list = NULL;
+#ifdef USE_OPENSSL_ENGINE
+ struct curl_slist *beg;
+ ENGINE *e;
+
+ for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) {
+ beg = curl_slist_append(list, ENGINE_get_id(e));
+ if(!beg) {
+ curl_slist_free_all(list);
+ return NULL;
+ }
+ list = beg;
+ }
+#endif
+ (void) data;
+ return list;
+}
+
+static void ossl_close(struct ssl_connect_data *connssl)
+{
+ struct ssl_backend_data *backend = connssl->backend;
+ if(backend->handle) {
+ (void)SSL_shutdown(backend->handle);
+ SSL_set_connect_state(backend->handle);
+
+ SSL_free(backend->handle);
+ backend->handle = NULL;
+ }
+ if(backend->ctx) {
+ SSL_CTX_free(backend->ctx);
+ backend->ctx = NULL;
+ }
+}
+
+/*
+ * This function is called when an SSL connection is closed.
+ */
+static void Curl_ossl_close(struct connectdata *conn, int sockindex)
+{
+ ossl_close(&conn->ssl[sockindex]);
+#ifndef CURL_DISABLE_PROXY
+ ossl_close(&conn->proxy_ssl[sockindex]);
+#endif
+}
+
+/*
+ * This function is called to shut down the SSL layer but keep the
+ * socket open (CCC - Clear Command Channel)
+ */
+static int Curl_ossl_shutdown(struct connectdata *conn, int sockindex)
+{
+ int retval = 0;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct Curl_easy *data = conn->data;
+ char buf[256]; /* We will use this for the OpenSSL error buffer, so it has
+ to be at least 256 bytes long. */
+ unsigned long sslerror;
+ ssize_t nread;
+ int buffsize;
+ int err;
+ bool done = FALSE;
+ struct ssl_backend_data *backend = connssl->backend;
+
+#ifndef CURL_DISABLE_FTP
+ /* This has only been tested on the proftpd server, and the mod_tls code
+ 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)
+ (void)SSL_shutdown(backend->handle);
+#endif
+
+ if(backend->handle) {
+ buffsize = (int)sizeof(buf);
+ while(!done) {
+ int what = SOCKET_READABLE(conn->sock[sockindex],
+ SSL_SHUTDOWN_TIMEOUT);
+ if(what > 0) {
+ ERR_clear_error();
+
+ /* Something to read, let's do it and hope that it is the close
+ notify alert from the server */
+ nread = (ssize_t)SSL_read(backend->handle, buf, buffsize);
+ err = SSL_get_error(backend->handle, (int)nread);
+
+ switch(err) {
+ case SSL_ERROR_NONE: /* this is not an error */
+ case SSL_ERROR_ZERO_RETURN: /* no more data */
+ /* This is the expected response. There was no data but only
+ the close notify alert */
+ done = TRUE;
+ break;
+ case SSL_ERROR_WANT_READ:
+ /* there's data pending, re-invoke SSL_read() */
+ infof(data, "SSL_ERROR_WANT_READ\n");
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ /* SSL wants a write. Really odd. Let's bail out. */
+ infof(data, "SSL_ERROR_WANT_WRITE\n");
+ done = TRUE;
+ break;
+ default:
+ /* openssl/ssl.h says "look at error stack/return value/errno" */
+ sslerror = ERR_get_error();
+ failf(conn->data, OSSL_PACKAGE " SSL_read on shutdown: %s, errno %d",
+ (sslerror ?
+ ossl_strerror(sslerror, buf, sizeof(buf)) :
+ SSL_ERROR_to_str(err)),
+ SOCKERRNO);
+ 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;
+ }
+ } /* while()-loop for the select() */
+
+ if(data->set.verbose) {
+#ifdef HAVE_SSL_GET_SHUTDOWN
+ switch(SSL_get_shutdown(backend->handle)) {
+ case SSL_SENT_SHUTDOWN:
+ infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN\n");
+ break;
+ case SSL_RECEIVED_SHUTDOWN:
+ infof(data, "SSL_get_shutdown() returned SSL_RECEIVED_SHUTDOWN\n");
+ break;
+ case SSL_SENT_SHUTDOWN|SSL_RECEIVED_SHUTDOWN:
+ infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN|"
+ "SSL_RECEIVED__SHUTDOWN\n");
+ break;
+ }
+#endif
+ }
+
+ SSL_free(backend->handle);
+ backend->handle = NULL;
+ }
+ return retval;
+}
+
+static void Curl_ossl_session_free(void *ptr)
+{
+ /* free the ID */
+ SSL_SESSION_free(ptr);
+}
+
+/*
+ * This function is called when the 'data' struct is going away. Close
+ * down everything and free all resources!
+ */
+static void Curl_ossl_close_all(struct Curl_easy *data)
+{
+#ifdef USE_OPENSSL_ENGINE
+ if(data->state.engine) {
+ ENGINE_finish(data->state.engine);
+ ENGINE_free(data->state.engine);
+ data->state.engine = NULL;
+ }
+#else
+ (void)data;
+#endif
+#if !defined(HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED) && \
+ defined(HAVE_ERR_REMOVE_THREAD_STATE)
+ /* OpenSSL 1.0.1 and 1.0.2 build an error queue that is stored per-thread
+ so we need to clean it here in case the thread will be killed. All OpenSSL
+ code should extract the error in association with the error so clearing
+ this queue here should be harmless at worst. */
+ ERR_remove_thread_state(NULL);
+#endif
+}
+
+/* ====================================================== */
+
+/*
+ * Match subjectAltName against the host name. This requires a conversion
+ * in CURL_DOES_CONVERSIONS builds.
+ */
+static bool subj_alt_hostcheck(struct Curl_easy *data,
+ const char *match_pattern, const char *hostname,
+ const char *dispname)
+#ifdef CURL_DOES_CONVERSIONS
+{
+ bool res = FALSE;
+
+ /* Curl_cert_hostcheck uses host encoding, but we get ASCII from
+ OpenSSl.
+ */
+ char *match_pattern2 = strdup(match_pattern);
+
+ if(match_pattern2) {
+ if(Curl_convert_from_network(data, match_pattern2,
+ strlen(match_pattern2)) == CURLE_OK) {
+ if(Curl_cert_hostcheck(match_pattern2, hostname)) {
+ res = TRUE;
+ infof(data,
+ " subjectAltName: host \"%s\" matched cert's \"%s\"\n",
+ dispname, match_pattern2);
+ }
+ }
+ free(match_pattern2);
+ }
+ else {
+ failf(data,
+ "SSL: out of memory when allocating temporary for subjectAltName");
+ }
+ return res;
+}
+#else
+{
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+ (void)dispname;
+ (void)data;
+#endif
+ if(Curl_cert_hostcheck(match_pattern, hostname)) {
+ infof(data, " subjectAltName: host \"%s\" matched cert's \"%s\"\n",
+ dispname, match_pattern);
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif
+
+
+/* Quote from RFC2818 section 3.1 "Server Identity"
+
+ If a subjectAltName extension of type dNSName is present, that MUST
+ be used as the identity. Otherwise, the (most specific) Common Name
+ field in the Subject field of the certificate MUST be used. Although
+ the use of the Common Name is existing practice, it is deprecated and
+ Certification Authorities are encouraged to use the dNSName instead.
+
+ Matching is performed using the matching rules specified by
+ [RFC2459]. If more than one identity of a given type is present in
+ the certificate (e.g., more than one dNSName name, a match in any one
+ of the set is considered acceptable.) Names may contain the wildcard
+ character * which is considered to match any single domain name
+ component or component fragment. E.g., *.a.com matches foo.a.com but
+ not bar.foo.a.com. f*.com matches foo.com but not bar.com.
+
+ In some cases, the URI is specified as an IP address rather than a
+ hostname. In this case, the iPAddress subjectAltName must be present
+ in the certificate and must exactly match the IP in the URI.
+
+*/
+static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)
+{
+ bool matched = FALSE;
+ int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */
+ size_t addrlen = 0;
+ struct Curl_easy *data = conn->data;
+ STACK_OF(GENERAL_NAME) *altnames;
+#ifdef ENABLE_IPV6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif
+ CURLcode result = CURLE_OK;
+ bool dNSName = FALSE; /* if a dNSName field exists in the cert */
+ bool iPAddress = FALSE; /* if a iPAddress field exists in the cert */
+ const char * const hostname = SSL_HOST_NAME();
+ const char * const dispname = SSL_HOST_DISPNAME();
+
+#ifdef ENABLE_IPV6
+ if(conn->bits.ipv6_ip &&
+ Curl_inet_pton(AF_INET6, hostname, &addr)) {
+ target = GEN_IPADD;
+ addrlen = sizeof(struct in6_addr);
+ }
+ else
+#endif
+ if(Curl_inet_pton(AF_INET, hostname, &addr)) {
+ target = GEN_IPADD;
+ addrlen = sizeof(struct in_addr);
+ }
+
+ /* get a "list" of alternative names */
+ altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL);
+
+ if(altnames) {
+#ifdef OPENSSL_IS_BORINGSSL
+ size_t numalts;
+ size_t i;
+#else
+ int numalts;
+ int i;
+#endif
+ bool dnsmatched = FALSE;
+ bool ipmatched = FALSE;
+
+ /* get amount of alternatives, RFC2459 claims there MUST be at least
+ one, but we don't depend on it... */
+ numalts = sk_GENERAL_NAME_num(altnames);
+
+ /* loop through all alternatives - until a dnsmatch */
+ for(i = 0; (i < numalts) && !dnsmatched; i++) {
+ /* get a handle to alternative name number i */
+ const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i);
+
+ if(check->type == GEN_DNS)
+ dNSName = TRUE;
+ else if(check->type == GEN_IPADD)
+ iPAddress = TRUE;
+
+ /* only check alternatives of the same type the target is */
+ if(check->type == target) {
+ /* get data and length */
+ const char *altptr = (char *)ASN1_STRING_get0_data(check->d.ia5);
+ size_t altlen = (size_t) ASN1_STRING_length(check->d.ia5);
+
+ switch(target) {
+ case GEN_DNS: /* name/pattern comparison */
+ /* The OpenSSL man page explicitly says: "In general it cannot be
+ assumed that the data returned by ASN1_STRING_data() is null
+ terminated or does not contain embedded nulls." But also that
+ "The actual format of the data will depend on the actual string
+ type itself: for example for an IA5String the data will be ASCII"
+
+ It has been however verified that in 0.9.6 and 0.9.7, IA5String
+ is always null-terminated.
+ */
+ if((altlen == strlen(altptr)) &&
+ /* if this isn't true, there was an embedded zero in the name
+ string and we cannot match it. */
+ subj_alt_hostcheck(data, altptr, hostname, dispname)) {
+ dnsmatched = TRUE;
+ }
+ break;
+
+ case GEN_IPADD: /* IP address comparison */
+ /* compare alternative IP address if the data chunk is the same size
+ our server IP address is */
+ if((altlen == addrlen) && !memcmp(altptr, &addr, altlen)) {
+ ipmatched = TRUE;
+ infof(data,
+ " subjectAltName: host \"%s\" matched cert's IP address!\n",
+ dispname);
+ }
+ break;
+ }
+ }
+ }
+ GENERAL_NAMES_free(altnames);
+
+ if(dnsmatched || ipmatched)
+ matched = TRUE;
+ }
+
+ if(matched)
+ /* an alternative name matched */
+ ;
+ else if(dNSName || iPAddress) {
+ infof(data, " subjectAltName does not match %s\n", dispname);
+ failf(data, "SSL: no alternative certificate subject name matches "
+ "target host name '%s'", dispname);
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else {
+ /* we have to look to the last occurrence of a commonName in the
+ distinguished one to get the most significant one. */
+ int j, i = -1;
+
+ /* The following is done because of a bug in 0.9.6b */
+
+ unsigned char *nulstr = (unsigned char *)"";
+ unsigned char *peer_CN = nulstr;
+
+ X509_NAME *name = X509_get_subject_name(server_cert);
+ if(name)
+ while((j = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0)
+ i = j;
+
+ /* we have the name entry and we will now convert this to a string
+ that we can use for comparison. Doing this we support BMPstring,
+ UTF8 etc. */
+
+ if(i >= 0) {
+ ASN1_STRING *tmp =
+ X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i));
+
+ /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input
+ is already UTF-8 encoded. We check for this case and copy the raw
+ string manually to avoid the problem. This code can be made
+ conditional in the future when OpenSSL has been fixed. */
+ if(tmp) {
+ if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
+ j = ASN1_STRING_length(tmp);
+ if(j >= 0) {
+ peer_CN = OPENSSL_malloc(j + 1);
+ if(peer_CN) {
+ memcpy(peer_CN, ASN1_STRING_get0_data(tmp), j);
+ peer_CN[j] = '\0';
+ }
+ }
+ }
+ else /* not a UTF8 name */
+ j = ASN1_STRING_to_UTF8(&peer_CN, tmp);
+
+ if(peer_CN && (curlx_uztosi(strlen((char *)peer_CN)) != j)) {
+ /* there was a terminating zero before the end of string, this
+ cannot match and we return failure! */
+ failf(data, "SSL: illegal cert name field");
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ }
+ }
+
+ if(peer_CN == nulstr)
+ peer_CN = NULL;
+ else {
+ /* convert peer_CN from UTF8 */
+ CURLcode rc = Curl_convert_from_utf8(data, (char *)peer_CN,
+ strlen((char *)peer_CN));
+ /* Curl_convert_from_utf8 calls failf if unsuccessful */
+ if(rc) {
+ OPENSSL_free(peer_CN);
+ return rc;
+ }
+ }
+
+ if(result)
+ /* error already detected, pass through */
+ ;
+ else if(!peer_CN) {
+ failf(data,
+ "SSL: unable to obtain common name from peer certificate");
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else if(!Curl_cert_hostcheck((const char *)peer_CN, hostname)) {
+ failf(data, "SSL: certificate subject name '%s' does not match "
+ "target host name '%s'", peer_CN, dispname);
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else {
+ infof(data, " common name: %s (matched)\n", peer_CN);
+ }
+ if(peer_CN)
+ OPENSSL_free(peer_CN);
+ }
+
+ return result;
+}
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
+ !defined(OPENSSL_NO_OCSP)
+static CURLcode verifystatus(struct connectdata *conn,
+ struct ssl_connect_data *connssl)
+{
+ int i, ocsp_status;
+ unsigned char *status;
+ const unsigned char *p;
+ CURLcode result = CURLE_OK;
+ struct Curl_easy *data = conn->data;
+ OCSP_RESPONSE *rsp = NULL;
+ OCSP_BASICRESP *br = NULL;
+ X509_STORE *st = NULL;
+ STACK_OF(X509) *ch = NULL;
+ struct ssl_backend_data *backend = connssl->backend;
+ X509 *cert;
+ OCSP_CERTID *id = NULL;
+ int cert_status, crl_reason;
+ ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
+ int ret;
+
+ long len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status);
+
+ if(!status) {
+ failf(data, "No OCSP response received");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+ p = status;
+ rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
+ if(!rsp) {
+ failf(data, "Invalid OCSP response");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ ocsp_status = OCSP_response_status(rsp);
+ if(ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+ failf(data, "Invalid OCSP response status: %s (%d)",
+ OCSP_response_status_str(ocsp_status), ocsp_status);
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ br = OCSP_response_get1_basic(rsp);
+ if(!br) {
+ failf(data, "Invalid OCSP response");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ ch = SSL_get_peer_cert_chain(backend->handle);
+ st = SSL_CTX_get_cert_store(backend->ctx);
+
+#if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER <= 0x2040200fL))
+ /* The authorized responder cert in the OCSP response MUST be signed by the
+ peer cert's issuer (see RFC6960 section 4.2.2.2). If that's a root cert,
+ no problem, but if it's an intermediate cert OpenSSL has a bug where it
+ expects this issuer to be present in the chain embedded in the OCSP
+ response. So we add it if necessary. */
+
+ /* First make sure the peer cert chain includes both a peer and an issuer,
+ and the OCSP response contains a responder cert. */
+ if(sk_X509_num(ch) >= 2 && sk_X509_num(br->certs) >= 1) {
+ X509 *responder = sk_X509_value(br->certs, sk_X509_num(br->certs) - 1);
+
+ /* Find issuer of responder cert and add it to the OCSP response chain */
+ for(i = 0; i < sk_X509_num(ch); i++) {
+ X509 *issuer = sk_X509_value(ch, i);
+ if(X509_check_issued(issuer, responder) == X509_V_OK) {
+ if(!OCSP_basic_add1_cert(br, issuer)) {
+ failf(data, "Could not add issuer cert to OCSP response");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+ }
+ }
+ }
+#endif
+
+ if(OCSP_basic_verify(br, ch, st, 0) <= 0) {
+ failf(data, "OCSP response verification failed");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ /* Compute the certificate's ID */
+ cert = SSL_get_peer_certificate(backend->handle);
+ if(!cert) {
+ failf(data, "Error getting peer certficate");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ for(i = 0; i < sk_X509_num(ch); i++) {
+ X509 *issuer = sk_X509_value(ch, i);
+ if(X509_check_issued(issuer, cert) == X509_V_OK) {
+ id = OCSP_cert_to_id(EVP_sha1(), cert, issuer);
+ break;
+ }
+ }
+ X509_free(cert);
+
+ if(!id) {
+ failf(data, "Error computing OCSP ID");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ /* Find the single OCSP response corresponding to the certificate ID */
+ ret = OCSP_resp_find_status(br, id, &cert_status, &crl_reason, &rev,
+ &thisupd, &nextupd);
+ OCSP_CERTID_free(id);
+ if(ret != 1) {
+ failf(data, "Could not find certificate ID in OCSP response");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ /* Validate the corresponding single OCSP response */
+ if(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)) {
+ failf(data, "OCSP response has expired");
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+ infof(data, "SSL certificate status: %s (%d)\n",
+ OCSP_cert_status_str(cert_status), cert_status);
+
+ switch(cert_status) {
+ case V_OCSP_CERTSTATUS_GOOD:
+ break;
+
+ case V_OCSP_CERTSTATUS_REVOKED:
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ failf(data, "SSL certificate revocation reason: %s (%d)",
+ OCSP_crl_reason_str(crl_reason), crl_reason);
+ goto end;
+
+ case V_OCSP_CERTSTATUS_UNKNOWN:
+ default:
+ result = CURLE_SSL_INVALIDCERTSTATUS;
+ goto end;
+ }
+
+end:
+ if(br)
+ OCSP_BASICRESP_free(br);
+ OCSP_RESPONSE_free(rsp);
+
+ return result;
+}
+#endif
+
+#endif /* USE_OPENSSL */
+
+/* The SSL_CTRL_SET_MSG_CALLBACK doesn't exist in ancient OpenSSL versions
+ and thus this cannot be done there. */
+#ifdef SSL_CTRL_SET_MSG_CALLBACK
+
+static const char *ssl_msg_type(int ssl_ver, int msg)
+{
+#ifdef SSL2_VERSION_MAJOR
+ if(ssl_ver == SSL2_VERSION_MAJOR) {
+ switch(msg) {
+ case SSL2_MT_ERROR:
+ return "Error";
+ case SSL2_MT_CLIENT_HELLO:
+ return "Client hello";
+ case SSL2_MT_CLIENT_MASTER_KEY:
+ return "Client key";
+ case SSL2_MT_CLIENT_FINISHED:
+ return "Client finished";
+ case SSL2_MT_SERVER_HELLO:
+ return "Server hello";
+ case SSL2_MT_SERVER_VERIFY:
+ return "Server verify";
+ case SSL2_MT_SERVER_FINISHED:
+ return "Server finished";
+ case SSL2_MT_REQUEST_CERTIFICATE:
+ return "Request CERT";
+ case SSL2_MT_CLIENT_CERTIFICATE:
+ return "Client CERT";
+ }
+ }
+ else
+#endif
+ if(ssl_ver == SSL3_VERSION_MAJOR) {
+ switch(msg) {
+ case SSL3_MT_HELLO_REQUEST:
+ return "Hello request";
+ case SSL3_MT_CLIENT_HELLO:
+ return "Client hello";
+ case SSL3_MT_SERVER_HELLO:
+ return "Server hello";
+#ifdef SSL3_MT_NEWSESSION_TICKET
+ case SSL3_MT_NEWSESSION_TICKET:
+ return "Newsession Ticket";
+#endif
+ case SSL3_MT_CERTIFICATE:
+ return "Certificate";
+ case SSL3_MT_SERVER_KEY_EXCHANGE:
+ return "Server key exchange";
+ case SSL3_MT_CLIENT_KEY_EXCHANGE:
+ return "Client key exchange";
+ case SSL3_MT_CERTIFICATE_REQUEST:
+ return "Request CERT";
+ case SSL3_MT_SERVER_DONE:
+ return "Server finished";
+ case SSL3_MT_CERTIFICATE_VERIFY:
+ return "CERT verify";
+ case SSL3_MT_FINISHED:
+ return "Finished";
+#ifdef SSL3_MT_CERTIFICATE_STATUS
+ case SSL3_MT_CERTIFICATE_STATUS:
+ return "Certificate Status";
+#endif
+#ifdef SSL3_MT_ENCRYPTED_EXTENSIONS
+ case SSL3_MT_ENCRYPTED_EXTENSIONS:
+ return "Encrypted Extensions";
+#endif
+#ifdef SSL3_MT_END_OF_EARLY_DATA
+ case SSL3_MT_END_OF_EARLY_DATA:
+ return "End of early data";
+#endif
+#ifdef SSL3_MT_KEY_UPDATE
+ case SSL3_MT_KEY_UPDATE:
+ return "Key update";
+#endif
+#ifdef SSL3_MT_NEXT_PROTO
+ case SSL3_MT_NEXT_PROTO:
+ return "Next protocol";
+#endif
+#ifdef SSL3_MT_MESSAGE_HASH
+ case SSL3_MT_MESSAGE_HASH:
+ return "Message hash";
+#endif
+ }
+ }
+ return "Unknown";
+}
+
+static const char *tls_rt_type(int type)
+{
+ switch(type) {
+#ifdef SSL3_RT_HEADER
+ case SSL3_RT_HEADER:
+ return "TLS header";
+#endif
+ case SSL3_RT_CHANGE_CIPHER_SPEC:
+ return "TLS change cipher";
+ case SSL3_RT_ALERT:
+ return "TLS alert";
+ case SSL3_RT_HANDSHAKE:
+ return "TLS handshake";
+ case SSL3_RT_APPLICATION_DATA:
+ return "TLS app data";
+ default:
+ return "TLS Unknown";
+ }
+}
+
+
+/*
+ * Our callback from the SSL/TLS layers.
+ */
+static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
+ const void *buf, size_t len, SSL *ssl,
+ void *userp)
+{
+ struct Curl_easy *data;
+ char unknown[32];
+ const char *verstr = NULL;
+ struct connectdata *conn = userp;
+
+ if(!conn || !conn->data || !conn->data->set.fdebug ||
+ (direction != 0 && direction != 1))
+ return;
+
+ data = conn->data;
+
+ switch(ssl_ver) {
+#ifdef SSL2_VERSION /* removed in recent versions */
+ case SSL2_VERSION:
+ verstr = "SSLv2";
+ break;
+#endif
+#ifdef SSL3_VERSION
+ case SSL3_VERSION:
+ verstr = "SSLv3";
+ break;
+#endif
+ case TLS1_VERSION:
+ verstr = "TLSv1.0";
+ break;
+#ifdef TLS1_1_VERSION
+ case TLS1_1_VERSION:
+ verstr = "TLSv1.1";
+ break;
+#endif
+#ifdef TLS1_2_VERSION
+ case TLS1_2_VERSION:
+ verstr = "TLSv1.2";
+ break;
+#endif
+#ifdef TLS1_3_VERSION
+ case TLS1_3_VERSION:
+ verstr = "TLSv1.3";
+ break;
+#endif
+ case 0:
+ break;
+ default:
+ msnprintf(unknown, sizeof(unknown), "(%x)", ssl_ver);
+ verstr = unknown;
+ break;
+ }
+
+ /* Log progress for interesting records only (like Handshake or Alert), skip
+ * all raw record headers (content_type == SSL3_RT_HEADER or ssl_ver == 0).
+ * For TLS 1.3, skip notification of the decrypted inner Content Type.
+ */
+ if(ssl_ver
+#ifdef SSL3_RT_INNER_CONTENT_TYPE
+ && content_type != SSL3_RT_INNER_CONTENT_TYPE
+#endif
+ ) {
+ const char *msg_name, *tls_rt_name;
+ char ssl_buf[1024];
+ int msg_type, txt_len;
+
+ /* the info given when the version is zero is not that useful for us */
+
+ ssl_ver >>= 8; /* check the upper 8 bits only below */
+
+ /* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL
+ * always pass-up content-type as 0. But the interesting message-type
+ * is at 'buf[0]'.
+ */
+ if(ssl_ver == SSL3_VERSION_MAJOR && content_type)
+ tls_rt_name = tls_rt_type(content_type);
+ else
+ tls_rt_name = "";
+
+ if(content_type == SSL3_RT_CHANGE_CIPHER_SPEC) {
+ msg_type = *(char *)buf;
+ msg_name = "Change cipher spec";
+ }
+ else if(content_type == SSL3_RT_ALERT) {
+ msg_type = (((char *)buf)[0] << 8) + ((char *)buf)[1];
+ msg_name = SSL_alert_desc_string_long(msg_type);
+ }
+ else {
+ msg_type = *(char *)buf;
+ msg_name = ssl_msg_type(ssl_ver, msg_type);
+ }
+
+ txt_len = msnprintf(ssl_buf, sizeof(ssl_buf), "%s (%s), %s, %s (%d):\n",
+ verstr, direction?"OUT":"IN",
+ tls_rt_name, msg_name, msg_type);
+ if(0 <= txt_len && (unsigned)txt_len < sizeof(ssl_buf)) {
+ Curl_debug(data, CURLINFO_TEXT, ssl_buf, (size_t)txt_len);
+ }
+ }
+
+ Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT :
+ CURLINFO_SSL_DATA_IN, (char *)buf, len);
+ (void) ssl;
+}
+#endif
+
+#ifdef USE_OPENSSL
+/* ====================================================== */
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+# define use_sni(x) sni = (x)
+#else
+# define use_sni(x) Curl_nop_stmt
+#endif
+
+/* Check for OpenSSL 1.0.2 which has ALPN support. */
+#undef HAS_ALPN
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L \
+ && !defined(OPENSSL_NO_TLSEXT)
+# define HAS_ALPN 1
+#endif
+
+/* Check for OpenSSL 1.0.1 which has NPN support. */
+#undef HAS_NPN
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L \
+ && !defined(OPENSSL_NO_TLSEXT) \
+ && !defined(OPENSSL_NO_NEXTPROTONEG)
+# define HAS_NPN 1
+#endif
+
+#ifdef HAS_NPN
+
+/*
+ * in is a list of length prefixed strings. this function has to select
+ * the protocol we want to use from the list and write its string into out.
+ */
+
+static int
+select_next_protocol(unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen,
+ const char *key, unsigned int keylen)
+{
+ unsigned int i;
+ for(i = 0; i + keylen <= inlen; i += in[i] + 1) {
+ if(memcmp(&in[i + 1], key, keylen) == 0) {
+ *out = (unsigned char *) &in[i + 1];
+ *outlen = in[i];
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int
+select_next_proto_cb(SSL *ssl,
+ unsigned char **out, unsigned char *outlen,
+ const unsigned char *in, unsigned int inlen,
+ void *arg)
+{
+ struct connectdata *conn = (struct connectdata*) arg;
+
+ (void)ssl;
+
+#ifdef USE_NGHTTP2
+ if(conn->data->set.httpversion >= CURL_HTTP_VERSION_2 &&
+ !select_next_protocol(out, outlen, in, inlen, NGHTTP2_PROTO_VERSION_ID,
+ NGHTTP2_PROTO_VERSION_ID_LEN)) {
+ infof(conn->data, "NPN, negotiated HTTP2 (%s)\n",
+ NGHTTP2_PROTO_VERSION_ID);
+ conn->negnpn = CURL_HTTP_VERSION_2;
+ return SSL_TLSEXT_ERR_OK;
+ }
+#endif
+
+ if(!select_next_protocol(out, outlen, in, inlen, ALPN_HTTP_1_1,
+ ALPN_HTTP_1_1_LENGTH)) {
+ infof(conn->data, "NPN, negotiated HTTP1.1\n");
+ conn->negnpn = CURL_HTTP_VERSION_1_1;
+ return SSL_TLSEXT_ERR_OK;
+ }
+
+ infof(conn->data, "NPN, no overlap, use HTTP1.1\n");
+ *out = (unsigned char *)ALPN_HTTP_1_1;
+ *outlen = ALPN_HTTP_1_1_LENGTH;
+ conn->negnpn = CURL_HTTP_VERSION_1_1;
+
+ return SSL_TLSEXT_ERR_OK;
+}
+#endif /* HAS_NPN */
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static const char *
+get_ssl_version_txt(SSL *ssl)
+{
+ if(!ssl)
+ return "";
+
+ switch(SSL_version(ssl)) {
+#ifdef TLS1_3_VERSION
+ case TLS1_3_VERSION:
+ return "TLSv1.3";
+#endif
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ case TLS1_2_VERSION:
+ return "TLSv1.2";
+ case TLS1_1_VERSION:
+ return "TLSv1.1";
+#endif
+ case TLS1_VERSION:
+ return "TLSv1.0";
+ case SSL3_VERSION:
+ return "SSLv3";
+ case SSL2_VERSION:
+ return "SSLv2";
+ }
+ return "unknown";
+}
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */
+static CURLcode
+set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn)
+{
+ /* first, TLS min version... */
+ long curl_ssl_version_min = SSL_CONN_CONFIG(version);
+ long curl_ssl_version_max;
+
+ /* convert cURL min SSL version option to OpenSSL constant */
+#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
+ uint16_t ossl_ssl_version_min = 0;
+ uint16_t ossl_ssl_version_max = 0;
+#else
+ long ossl_ssl_version_min = 0;
+ long ossl_ssl_version_max = 0;
+#endif
+ switch(curl_ssl_version_min) {
+ case CURL_SSLVERSION_TLSv1: /* TLS 1.x */
+ case CURL_SSLVERSION_TLSv1_0:
+ ossl_ssl_version_min = TLS1_VERSION;
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+ ossl_ssl_version_min = TLS1_1_VERSION;
+ break;
+ case CURL_SSLVERSION_TLSv1_2:
+ ossl_ssl_version_min = TLS1_2_VERSION;
+ break;
+#ifdef TLS1_3_VERSION
+ case CURL_SSLVERSION_TLSv1_3:
+ ossl_ssl_version_min = TLS1_3_VERSION;
+ break;
+#endif
+ }
+
+ /* CURL_SSLVERSION_DEFAULT means that no option was selected.
+ We don't want to pass 0 to SSL_CTX_set_min_proto_version as
+ it would enable all versions down to the lowest supported by
+ the library.
+ So we skip this, and stay with the OS default
+ */
+ if(curl_ssl_version_min != CURL_SSLVERSION_DEFAULT) {
+ if(!SSL_CTX_set_min_proto_version(ctx, ossl_ssl_version_min)) {
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ /* ... then, TLS max version */
+ curl_ssl_version_max = SSL_CONN_CONFIG(version_max);
+
+ /* convert cURL max SSL version option to OpenSSL constant */
+ switch(curl_ssl_version_max) {
+ case CURL_SSLVERSION_MAX_TLSv1_0:
+ ossl_ssl_version_max = TLS1_VERSION;
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_1:
+ ossl_ssl_version_max = TLS1_1_VERSION;
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_2:
+ ossl_ssl_version_max = TLS1_2_VERSION;
+ break;
+#ifdef TLS1_3_VERSION
+ case CURL_SSLVERSION_MAX_TLSv1_3:
+ ossl_ssl_version_max = TLS1_3_VERSION;
+ break;
+#endif
+ case CURL_SSLVERSION_MAX_NONE: /* none selected */
+ case CURL_SSLVERSION_MAX_DEFAULT: /* max selected */
+ default:
+ /* SSL_CTX_set_max_proto_version states that:
+ setting the maximum to 0 will enable
+ protocol versions up to the highest version
+ supported by the library */
+ ossl_ssl_version_max = 0;
+ break;
+ }
+
+ if(!SSL_CTX_set_max_proto_version(ctx, ossl_ssl_version_max)) {
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ return CURLE_OK;
+}
+#endif
+
+#ifdef OPENSSL_IS_BORINGSSL
+typedef uint32_t ctx_option_t;
+#else
+typedef long ctx_option_t;
+#endif
+
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L) /* 1.1.0 */
+static CURLcode
+set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
+ struct connectdata *conn, int sockindex)
+{
+#if (OPENSSL_VERSION_NUMBER < 0x1000100FL) || !defined(TLS1_3_VERSION)
+ /* convoluted #if condition just to avoid compiler warnings on unused
+ variable */
+ struct Curl_easy *data = conn->data;
+#endif
+ long ssl_version = SSL_CONN_CONFIG(version);
+ long ssl_version_max = SSL_CONN_CONFIG(version_max);
+
+ switch(ssl_version) {
+ case CURL_SSLVERSION_TLSv1_3:
+#ifdef TLS1_3_VERSION
+ {
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION);
+ *ctx_options |= SSL_OP_NO_TLSv1_2;
+ }
+#else
+ (void)sockindex;
+ (void)ctx_options;
+ failf(data, OSSL_PACKAGE " was built without TLS 1.3 support");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ /* FALLTHROUGH */
+ case CURL_SSLVERSION_TLSv1_2:
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ *ctx_options |= SSL_OP_NO_TLSv1_1;
+#else
+ failf(data, OSSL_PACKAGE " was built without TLS 1.2 support");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ /* FALLTHROUGH */
+ case CURL_SSLVERSION_TLSv1_1:
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ *ctx_options |= SSL_OP_NO_TLSv1;
+#else
+ failf(data, OSSL_PACKAGE " was built without TLS 1.1 support");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ /* FALLTHROUGH */
+ case CURL_SSLVERSION_TLSv1_0:
+ case CURL_SSLVERSION_TLSv1:
+ break;
+ }
+
+ switch(ssl_version_max) {
+ case CURL_SSLVERSION_MAX_TLSv1_0:
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ *ctx_options |= SSL_OP_NO_TLSv1_1;
+#endif
+ /* FALLTHROUGH */
+ case CURL_SSLVERSION_MAX_TLSv1_1:
+#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ *ctx_options |= SSL_OP_NO_TLSv1_2;
+#endif
+ /* FALLTHROUGH */
+ case CURL_SSLVERSION_MAX_TLSv1_2:
+#ifdef TLS1_3_VERSION
+ *ctx_options |= SSL_OP_NO_TLSv1_3;
+#endif
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_3:
+#ifdef TLS1_3_VERSION
+ break;
+#else
+ failf(data, OSSL_PACKAGE " was built without TLS 1.3 support");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ }
+ return CURLE_OK;
+}
+#endif
+
+/* The "new session" callback must return zero if the session can be removed
+ * or non-zero if the session has been put into the session cache.
+ */
+static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
+{
+ int res = 0;
+ struct connectdata *conn;
+ struct Curl_easy *data;
+ int sockindex;
+ curl_socket_t *sockindex_ptr;
+ int connectdata_idx = ossl_get_ssl_conn_index();
+ int sockindex_idx = ossl_get_ssl_sockindex_index();
+
+ if(connectdata_idx < 0 || sockindex_idx < 0)
+ return 0;
+
+ conn = (struct connectdata*) SSL_get_ex_data(ssl, connectdata_idx);
+ if(!conn)
+ return 0;
+
+ data = conn->data;
+
+ /* The sockindex has been stored as a pointer to an array element */
+ sockindex_ptr = (curl_socket_t*) SSL_get_ex_data(ssl, sockindex_idx);
+ sockindex = (int)(sockindex_ptr - conn->sock);
+
+ if(SSL_SET_OPTION(primary.sessionid)) {
+ bool incache;
+ void *old_ssl_sessionid = NULL;
+
+ Curl_ssl_sessionid_lock(conn);
+ incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL,
+ sockindex));
+ if(incache) {
+ if(old_ssl_sessionid != ssl_sessionid) {
+ infof(data, "old SSL session ID is stale, removing\n");
+ Curl_ssl_delsessionid(conn, old_ssl_sessionid);
+ incache = FALSE;
+ }
+ }
+
+ if(!incache) {
+ if(!Curl_ssl_addsessionid(conn, ssl_sessionid,
+ 0 /* unknown size */, sockindex)) {
+ /* the session has been put into the session cache */
+ res = 1;
+ }
+ else
+ failf(data, "failed to store ssl session");
+ }
+ Curl_ssl_sessionid_unlock(conn);
+ }
+
+ return res;
+}
+
+static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
+{
+ CURLcode result = CURLE_OK;
+ char *ciphers;
+ struct Curl_easy *data = conn->data;
+ SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
+ X509_LOOKUP *lookup = NULL;
+ curl_socket_t sockfd = conn->sock[sockindex];
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ ctx_option_t ctx_options = 0;
+
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ bool sni;
+ const char * const hostname = SSL_HOST_NAME();
+
+#ifdef ENABLE_IPV6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif
+#endif
+ const long int ssl_version = SSL_CONN_CONFIG(version);
+#ifdef USE_OPENSSL_SRP
+ const enum CURL_TLSAUTH ssl_authtype = SSL_SET_OPTION(authtype);
+#endif
+ char * const ssl_cert = SSL_SET_OPTION(primary.clientcert);
+ const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob);
+ const char * const ssl_cert_type = SSL_SET_OPTION(cert_type);
+ const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile);
+ const char * const ssl_capath = SSL_CONN_CONFIG(CApath);
+ const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
+ const char * const ssl_crlfile = SSL_SET_OPTION(CRLfile);
+ char error_buffer[256];
+ struct ssl_backend_data *backend = connssl->backend;
+ bool imported_native_ca = false;
+
+ DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
+
+ /* Make funny stuff to get random input */
+ result = Curl_ossl_seed(data);
+ if(result)
+ return result;
+
+ SSL_SET_OPTION_LVALUE(certverifyresult) = !X509_V_OK;
+
+ /* check to see if we've been told to use an explicit SSL/TLS version */
+
+ switch(ssl_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:
+ /* it will be handled later with the context options */
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+ req_method = TLS_client_method();
+#else
+ req_method = SSLv23_client_method();
+#endif
+ use_sni(TRUE);
+ break;
+ case CURL_SSLVERSION_SSLv2:
+#ifdef OPENSSL_NO_SSL2
+ failf(data, OSSL_PACKAGE " was built without SSLv2 support");
+ return CURLE_NOT_BUILT_IN;
+#else
+#ifdef USE_OPENSSL_SRP
+ if(ssl_authtype == CURL_TLSAUTH_SRP)
+ return CURLE_SSL_CONNECT_ERROR;
+#endif
+ req_method = SSLv2_client_method();
+ use_sni(FALSE);
+ break;
+#endif
+ case CURL_SSLVERSION_SSLv3:
+#ifdef OPENSSL_NO_SSL3_METHOD
+ failf(data, OSSL_PACKAGE " was built without SSLv3 support");
+ return CURLE_NOT_BUILT_IN;
+#else
+#ifdef USE_OPENSSL_SRP
+ if(ssl_authtype == CURL_TLSAUTH_SRP)
+ return CURLE_SSL_CONNECT_ERROR;
+#endif
+ req_method = SSLv3_client_method();
+ use_sni(FALSE);
+ break;
+#endif
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(backend->ctx)
+ SSL_CTX_free(backend->ctx);
+ backend->ctx = SSL_CTX_new(req_method);
+
+ if(!backend->ctx) {
+ failf(data, "SSL: couldn't create a context: %s",
+ ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer)));
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+#ifdef SSL_MODE_RELEASE_BUFFERS
+ SSL_CTX_set_mode(backend->ctx, SSL_MODE_RELEASE_BUFFERS);
+#endif
+
+#ifdef SSL_CTRL_SET_MSG_CALLBACK
+ if(data->set.fdebug && data->set.verbose) {
+ /* the SSL trace callback is only used for verbose logging */
+ SSL_CTX_set_msg_callback(backend->ctx, ssl_tls_trace);
+ SSL_CTX_set_msg_callback_arg(backend->ctx, conn);
+ }
+#endif
+
+ /* OpenSSL contains code to work-around lots of bugs and flaws in various
+ SSL-implementations. SSL_CTX_set_options() is used to enabled those
+ work-arounds. The man page for this option states that SSL_OP_ALL enables
+ all the work-arounds and that "It is usually safe to use SSL_OP_ALL to
+ enable the bug workaround options if compatibility with somewhat broken
+ implementations is desired."
+
+ The "-no_ticket" option was introduced in Openssl0.9.8j. It's a flag to
+ disable "rfc4507bis session ticket support". rfc4507bis was later turned
+ into the proper RFC5077 it seems: https://tools.ietf.org/html/rfc5077
+
+ The enabled extension concerns the session management. I wonder how often
+ libcurl stops a connection and then resumes a TLS session. also, sending
+ the session data is some overhead. .I suggest that you just use your
+ proposed patch (which explicitly disables TICKET).
+
+ If someone writes an application with libcurl and openssl who wants to
+ enable the feature, one can do this in the SSL callback.
+
+ SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG option enabling allowed proper
+ interoperability with web server Netscape Enterprise Server 2.0.1 which
+ was released back in 1996.
+
+ Due to CVE-2010-4180, option SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG has
+ become ineffective as of OpenSSL 0.9.8q and 1.0.0c. In order to mitigate
+ CVE-2010-4180 when using previous OpenSSL versions we no longer enable
+ this option regardless of OpenSSL version and SSL_OP_ALL definition.
+
+ OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability
+ (https://www.openssl.org/~bodo/tls-cbc.txt). In 0.9.6e they added a bit to
+ SSL_OP_ALL that _disables_ that work-around despite the fact that
+ SSL_OP_ALL is documented to do "rather harmless" workarounds. In order to
+ keep the secure work-around, the SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit
+ must not be set.
+ */
+
+ ctx_options = SSL_OP_ALL;
+
+#ifdef SSL_OP_NO_TICKET
+ ctx_options |= SSL_OP_NO_TICKET;
+#endif
+
+#ifdef SSL_OP_NO_COMPRESSION
+ ctx_options |= SSL_OP_NO_COMPRESSION;
+#endif
+
+#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
+ /* mitigate CVE-2010-4180 */
+ ctx_options &= ~SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG;
+#endif
+
+#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+ /* unless the user explicitly ask to allow the protocol vulnerability we
+ use the work-around */
+ if(!SSL_SET_OPTION(enable_beast))
+ ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
+#endif
+
+ switch(ssl_version) {
+ /* "--sslv2" option means SSLv2 only, disable all others */
+ case CURL_SSLVERSION_SSLv2:
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */
+ SSL_CTX_set_min_proto_version(backend->ctx, SSL2_VERSION);
+ SSL_CTX_set_max_proto_version(backend->ctx, SSL2_VERSION);
+#else
+ ctx_options |= SSL_OP_NO_SSLv3;
+ ctx_options |= SSL_OP_NO_TLSv1;
+# if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ ctx_options |= SSL_OP_NO_TLSv1_1;
+ ctx_options |= SSL_OP_NO_TLSv1_2;
+# ifdef TLS1_3_VERSION
+ ctx_options |= SSL_OP_NO_TLSv1_3;
+# endif
+# endif
+#endif
+ break;
+
+ /* "--sslv3" option means SSLv3 only, disable all others */
+ case CURL_SSLVERSION_SSLv3:
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */
+ SSL_CTX_set_min_proto_version(backend->ctx, SSL3_VERSION);
+ SSL_CTX_set_max_proto_version(backend->ctx, SSL3_VERSION);
+#else
+ ctx_options |= SSL_OP_NO_SSLv2;
+ ctx_options |= SSL_OP_NO_TLSv1;
+# if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ ctx_options |= SSL_OP_NO_TLSv1_1;
+ ctx_options |= SSL_OP_NO_TLSv1_2;
+# ifdef TLS1_3_VERSION
+ ctx_options |= SSL_OP_NO_TLSv1_3;
+# endif
+# endif
+#endif
+ break;
+
+ /* "--tlsv<x.y>" options mean TLS >= version <x.y> */
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1: /* TLS >= version 1.0 */
+ case CURL_SSLVERSION_TLSv1_0: /* TLS >= version 1.0 */
+ case CURL_SSLVERSION_TLSv1_1: /* TLS >= version 1.1 */
+ case CURL_SSLVERSION_TLSv1_2: /* TLS >= version 1.2 */
+ case CURL_SSLVERSION_TLSv1_3: /* TLS >= version 1.3 */
+ /* asking for any TLS version as the minimum, means no SSL versions
+ allowed */
+ ctx_options |= SSL_OP_NO_SSLv2;
+ ctx_options |= SSL_OP_NO_SSLv3;
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */
+ result = set_ssl_version_min_max(backend->ctx, conn);
+#else
+ result = set_ssl_version_min_max_legacy(&ctx_options, conn, sockindex);
+#endif
+ if(result != CURLE_OK)
+ return result;
+ break;
+
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ SSL_CTX_set_options(backend->ctx, ctx_options);
+
+#ifdef HAS_NPN
+ if(conn->bits.tls_enable_npn)
+ SSL_CTX_set_next_proto_select_cb(backend->ctx, select_next_proto_cb, conn);
+#endif
+
+#ifdef HAS_ALPN
+ if(conn->bits.tls_enable_alpn) {
+ int cur = 0;
+ unsigned char protocols[128];
+
+#ifdef USE_NGHTTP2
+ if(data->set.httpversion >= CURL_HTTP_VERSION_2
+#ifndef CURL_DISABLE_PROXY
+ && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy)
+#endif
+ ) {
+ protocols[cur++] = NGHTTP2_PROTO_VERSION_ID_LEN;
+
+ memcpy(&protocols[cur], NGHTTP2_PROTO_VERSION_ID,
+ NGHTTP2_PROTO_VERSION_ID_LEN);
+ cur += NGHTTP2_PROTO_VERSION_ID_LEN;
+ infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
+ }
+#endif
+
+ protocols[cur++] = ALPN_HTTP_1_1_LENGTH;
+ memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
+ cur += ALPN_HTTP_1_1_LENGTH;
+ infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1);
+
+ /* expects length prefixed preference ordered list of protocols in wire
+ * format
+ */
+ SSL_CTX_set_alpn_protos(backend->ctx, protocols, cur);
+ }
+#endif
+
+ if(ssl_cert || ssl_cert_blob || ssl_cert_type) {
+ BIO *ssl_cert_bio = NULL;
+ BIO *ssl_key_bio = NULL;
+ if(ssl_cert_blob) {
+ /* the typecast of blob->len is fine since it is guaranteed to never be
+ larger than CURL_MAX_INPUT_LENGTH */
+ ssl_cert_bio = BIO_new_mem_buf(ssl_cert_blob->data,
+ (int)ssl_cert_blob->len);
+ if(!ssl_cert_bio)
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ if(!result && SSL_SET_OPTION(key_blob)) {
+ ssl_key_bio = BIO_new_mem_buf(SSL_SET_OPTION(key_blob)->data,
+ (int)SSL_SET_OPTION(key_blob)->len);
+ if(!ssl_key_bio)
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ if(!result &&
+ !cert_stuff(conn, backend->ctx,
+ ssl_cert, ssl_cert_bio, ssl_cert_type,
+ SSL_SET_OPTION(key), ssl_key_bio,
+ SSL_SET_OPTION(key_type), SSL_SET_OPTION(key_passwd)))
+ result = CURLE_SSL_CERTPROBLEM;
+ if(ssl_cert_bio)
+ BIO_free(ssl_cert_bio);
+ if(ssl_key_bio)
+ BIO_free(ssl_key_bio);
+ if(result)
+ /* failf() is already done in cert_stuff() */
+ return result;
+ }
+
+ ciphers = SSL_CONN_CONFIG(cipher_list);
+ if(!ciphers)
+ ciphers = (char *)DEFAULT_CIPHER_SELECTION;
+ if(ciphers) {
+ if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) {
+ failf(data, "failed setting cipher list: %s", ciphers);
+ return CURLE_SSL_CIPHER;
+ }
+ infof(data, "Cipher selection: %s\n", ciphers);
+ }
+
+#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
+ {
+ char *ciphers13 = SSL_CONN_CONFIG(cipher_list13);
+ if(ciphers13) {
+ if(!SSL_CTX_set_ciphersuites(backend->ctx, ciphers13)) {
+ failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13);
+ return CURLE_SSL_CIPHER;
+ }
+ infof(data, "TLS 1.3 cipher selection: %s\n", ciphers13);
+ }
+ }
+#endif
+
+#ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
+ /* OpenSSL 1.1.1 requires clients to opt-in for PHA */
+ SSL_CTX_set_post_handshake_auth(backend->ctx, 1);
+#endif
+
+#ifdef HAVE_SSL_CTX_SET_EC_CURVES
+ {
+ char *curves = SSL_CONN_CONFIG(curves);
+ if(curves) {
+ if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) {
+ failf(data, "failed setting curves list: '%s'", curves);
+ return CURLE_SSL_CIPHER;
+ }
+ }
+ }
+#endif
+
+#ifdef USE_OPENSSL_SRP
+ if(ssl_authtype == CURL_TLSAUTH_SRP) {
+ char * const ssl_username = SSL_SET_OPTION(username);
+
+ infof(data, "Using TLS-SRP username: %s\n", ssl_username);
+
+ if(!SSL_CTX_set_srp_username(backend->ctx, ssl_username)) {
+ failf(data, "Unable to set SRP user name");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ if(!SSL_CTX_set_srp_password(backend->ctx, SSL_SET_OPTION(password))) {
+ failf(data, "failed setting SRP password");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ if(!SSL_CONN_CONFIG(cipher_list)) {
+ infof(data, "Setting cipher list SRP\n");
+
+ if(!SSL_CTX_set_cipher_list(backend->ctx, "SRP")) {
+ failf(data, "failed setting SRP cipher list");
+ return CURLE_SSL_CIPHER;
+ }
+ }
+ }
+#endif
+
+
+#if defined(USE_WIN32_CRYPTO)
+ /* Import certificates from the Windows root certificate store if requested.
+ https://stackoverflow.com/questions/9507184/
+ https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037
+ https://tools.ietf.org/html/rfc5280 */
+ if((SSL_CONN_CONFIG(verifypeer) || SSL_CONN_CONFIG(verifyhost)) &&
+ (SSL_SET_OPTION(native_ca_store))) {
+ X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx);
+ HCERTSTORE hStore = CertOpenSystemStore((HCRYPTPROV_LEGACY)NULL,
+ TEXT("ROOT"));
+
+ if(hStore) {
+ PCCERT_CONTEXT pContext = NULL;
+ /* The array of enhanced key usage OIDs will vary per certificate and is
+ declared outside of the loop so that rather than malloc/free each
+ iteration we can grow it with realloc, when necessary. */
+ CERT_ENHKEY_USAGE *enhkey_usage = NULL;
+ DWORD enhkey_usage_size = 0;
+
+ /* This loop makes a best effort to import all valid certificates from
+ the MS root store. If a certificate cannot be imported it is skipped.
+ 'result' is used to store only hard-fail conditions (such as out of
+ memory) that cause an early break. */
+ result = CURLE_OK;
+ for(;;) {
+ X509 *x509;
+ FILETIME now;
+ BYTE key_usage[2];
+ DWORD req_size;
+ const unsigned char *encoded_cert;
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ char cert_name[256];
+#endif
+
+ pContext = CertEnumCertificatesInStore(hStore, pContext);
+ if(!pContext)
+ break;
+
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
+ NULL, cert_name, sizeof(cert_name))) {
+ strcpy(cert_name, "Unknown");
+ }
+ infof(data, "SSL: Checking cert \"%s\"\n", cert_name);
+#endif
+
+ encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
+ if(!encoded_cert)
+ continue;
+
+ GetSystemTimeAsFileTime(&now);
+ if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
+ CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
+ continue;
+
+ /* If key usage exists check for signing attribute */
+ if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType,
+ pContext->pCertInfo,
+ key_usage, sizeof(key_usage))) {
+ if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
+ continue;
+ }
+ else if(GetLastError())
+ continue;
+
+ /* If enhanced key usage exists check for server auth attribute.
+ *
+ * Note "In a Microsoft environment, a certificate might also have EKU
+ * extended properties that specify valid uses for the certificate."
+ * The call below checks both, and behavior varies depending on what is
+ * found. For more details see CertGetEnhancedKeyUsage doc.
+ */
+ if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) {
+ if(req_size && req_size > enhkey_usage_size) {
+ void *tmp = realloc(enhkey_usage, req_size);
+
+ if(!tmp) {
+ failf(data, "SSL: Out of memory allocating for OID list");
+ result = CURLE_OUT_OF_MEMORY;
+ break;
+ }
+
+ enhkey_usage = (CERT_ENHKEY_USAGE *)tmp;
+ enhkey_usage_size = req_size;
+ }
+
+ if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) {
+ if(!enhkey_usage->cUsageIdentifier) {
+ /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate is
+ good for all uses. If it returns zero, the certificate has no
+ valid uses." */
+ if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
+ continue;
+ }
+ else {
+ DWORD i;
+ bool found = false;
+
+ for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) {
+ if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */,
+ enhkey_usage->rgpszUsageIdentifier[i])) {
+ found = true;
+ break;
+ }
+ }
+
+ if(!found)
+ continue;
+ }
+ }
+ else
+ continue;
+ }
+ else
+ continue;
+
+ x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
+ if(!x509)
+ continue;
+
+ /* Try to import the certificate. This may fail for legitimate reasons
+ such as duplicate certificate, which is allowed by MS but not
+ OpenSSL. */
+ if(X509_STORE_add_cert(store, x509) == 1) {
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+ infof(data, "SSL: Imported cert \"%s\"\n", cert_name);
+#endif
+ imported_native_ca = true;
+ }
+ X509_free(x509);
+ }
+
+ free(enhkey_usage);
+ CertFreeCertificateContext(pContext);
+ CertCloseStore(hStore, 0);
+
+ if(result)
+ return result;
+ }
+ if(imported_native_ca)
+ infof(data, "successfully imported windows ca store\n");
+ else
+ infof(data, "error importing windows ca store, continuing anyway\n");
+ }
+#endif
+
+#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
+ /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */
+ {
+ if(ssl_cafile) {
+ if(!SSL_CTX_load_verify_file(backend->ctx, ssl_cafile)) {
+ if(verifypeer && !imported_native_ca) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate file: %s", ssl_cafile);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ /* Continue with a warning if no certificate verif is required. */
+ infof(data, "error setting certificate file, continuing anyway\n");
+ }
+ infof(data, " CAfile: %s\n", ssl_cafile);
+ }
+ if(ssl_capath) {
+ if(!SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) {
+ if(verifypeer && !imported_native_ca) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate path: %s", ssl_capath);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ /* Continue with a warning if no certificate verif is required. */
+ infof(data, "error setting certificate path, continuing anyway\n");
+ }
+ infof(data, " CApath: %s\n", ssl_capath);
+ }
+ }
+#else
+ if(ssl_cafile || ssl_capath) {
+ /* tell SSL where to find CA certificates that are used to verify
+ the servers certificate. */
+ if(!SSL_CTX_load_verify_locations(backend->ctx, ssl_cafile, ssl_capath)) {
+ if(verifypeer && !imported_native_ca) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate verify locations:"
+ " CAfile: %s CApath: %s",
+ ssl_cafile ? ssl_cafile : "none",
+ ssl_capath ? ssl_capath : "none");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ /* Just continue with a warning if no strict certificate verification
+ is required. */
+ infof(data, "error setting certificate verify locations,"
+ " continuing anyway:\n");
+ }
+ else {
+ /* Everything is fine. */
+ infof(data, "successfully set certificate verify locations:\n");
+ }
+ infof(data, " CAfile: %s\n", ssl_cafile ? ssl_cafile : "none");
+ infof(data, " CApath: %s\n", ssl_capath ? ssl_capath : "none");
+ }
+#endif
+
+#ifdef CURL_CA_FALLBACK
+ if(verifypeer && !ssl_cafile && !ssl_capath && !imported_native_ca) {
+ /* verifying the peer without any CA certificates won't
+ work so use openssl's built in default as fallback */
+ SSL_CTX_set_default_verify_paths(backend->ctx);
+ }
+#endif
+
+ if(ssl_crlfile) {
+ /* tell SSL where to find CRL file that is used to check certificate
+ * revocation */
+ lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(backend->ctx),
+ X509_LOOKUP_file());
+ if(!lookup ||
+ (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) {
+ failf(data, "error loading CRL file: %s", ssl_crlfile);
+ return CURLE_SSL_CRL_BADFILE;
+ }
+ /* Everything is fine. */
+ infof(data, "successfully load CRL file:\n");
+ X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx),
+ X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
+
+ infof(data, " CRLfile: %s\n", ssl_crlfile);
+ }
+
+ if(verifypeer) {
+ /* Try building a chain using issuers in the trusted store first to avoid
+ problems with server-sent legacy intermediates. Newer versions of
+ OpenSSL do alternate chain checking by default but we do not know how to
+ determine that in a reliable manner.
+ https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest
+ */
+#if defined(X509_V_FLAG_TRUSTED_FIRST)
+ X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx),
+ X509_V_FLAG_TRUSTED_FIRST);
+#endif
+#ifdef X509_V_FLAG_PARTIAL_CHAIN
+ if(!SSL_SET_OPTION(no_partialchain) && !ssl_crlfile) {
+ /* Have intermediate certificates in the trust store be treated as
+ trust-anchors, in the same way as self-signed root CA certificates
+ are. This allows users to verify servers using the intermediate cert
+ only, instead of needing the whole chain.
+
+ Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we
+ cannot do partial chains with CRL check.
+ */
+ X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx),
+ X509_V_FLAG_PARTIAL_CHAIN);
+ }
+#endif
+ }
+
+ /* SSL always tries to verify the peer, this only says whether it should
+ * fail to connect if the verification fails, or if it should continue
+ * anyway. In the latter case the result of the verification is checked with
+ * SSL_get_verify_result() below. */
+ SSL_CTX_set_verify(backend->ctx,
+ verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
+
+ /* Enable logging of secrets to the file specified in env SSLKEYLOGFILE. */
+#ifdef HAVE_KEYLOG_CALLBACK
+ if(Curl_tls_keylog_enabled()) {
+ SSL_CTX_set_keylog_callback(backend->ctx, ossl_keylog_callback);
+ }
+#endif
+
+ /* Enable the session cache because it's a prerequisite for the "new session"
+ * callback. Use the "external storage" mode to avoid that OpenSSL creates
+ * an internal session cache.
+ */
+ SSL_CTX_set_session_cache_mode(backend->ctx,
+ SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_INTERNAL);
+ SSL_CTX_sess_set_new_cb(backend->ctx, ossl_new_session_cb);
+
+ /* give application a chance to interfere with SSL set up. */
+ if(data->set.ssl.fsslctx) {
+ Curl_set_in_callback(data, true);
+ result = (*data->set.ssl.fsslctx)(data, backend->ctx,
+ data->set.ssl.fsslctxp);
+ Curl_set_in_callback(data, false);
+ if(result) {
+ failf(data, "error signaled by ssl ctx callback");
+ return result;
+ }
+ }
+
+ /* Lets make an SSL structure */
+ if(backend->handle)
+ SSL_free(backend->handle);
+ backend->handle = SSL_new(backend->ctx);
+ if(!backend->handle) {
+ failf(data, "SSL: couldn't create a context (handle)!");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
+ !defined(OPENSSL_NO_OCSP)
+ if(SSL_CONN_CONFIG(verifystatus))
+ SSL_set_tlsext_status_type(backend->handle, TLSEXT_STATUSTYPE_ocsp);
+#endif
+
+#if defined(OPENSSL_IS_BORINGSSL) && defined(ALLOW_RENEG)
+ SSL_set_renegotiate_mode(backend->handle, ssl_renegotiate_freely);
+#endif
+
+ SSL_set_connect_state(backend->handle);
+
+ backend->server_cert = 0x0;
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) &&
+#ifdef ENABLE_IPV6
+ (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) &&
+#endif
+ sni &&
+ !SSL_set_tlsext_host_name(backend->handle, hostname))
+ infof(data, "WARNING: failed to configure server name indication (SNI) "
+ "TLS extension\n");
+#endif
+
+ /* Check if there's a cached ID we can/should use here! */
+ if(SSL_SET_OPTION(primary.sessionid)) {
+ void *ssl_sessionid = NULL;
+ int connectdata_idx = ossl_get_ssl_conn_index();
+ int sockindex_idx = ossl_get_ssl_sockindex_index();
+
+ if(connectdata_idx >= 0 && sockindex_idx >= 0) {
+ /* Store the data needed for the "new session" callback.
+ * The sockindex is stored as a pointer to an array element. */
+ SSL_set_ex_data(backend->handle, connectdata_idx, conn);
+ SSL_set_ex_data(backend->handle, sockindex_idx, conn->sock + sockindex);
+ }
+
+ Curl_ssl_sessionid_lock(conn);
+ if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL, sockindex)) {
+ /* we got a session id, use it! */
+ if(!SSL_set_session(backend->handle, ssl_sessionid)) {
+ Curl_ssl_sessionid_unlock(conn);
+ failf(data, "SSL: SSL_set_session failed: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ /* Informational message */
+ infof(data, "SSL re-using session ID\n");
+ }
+ Curl_ssl_sessionid_unlock(conn);
+ }
+
+#ifndef CURL_DISABLE_PROXY
+ if(conn->proxy_ssl[sockindex].use) {
+ BIO *const bio = BIO_new(BIO_f_ssl());
+ SSL *handle = conn->proxy_ssl[sockindex].backend->handle;
+ DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state);
+ DEBUGASSERT(handle != NULL);
+ DEBUGASSERT(bio != NULL);
+ BIO_set_ssl(bio, handle, FALSE);
+ SSL_set_bio(backend->handle, bio, bio);
+ }
+ else
+#endif
+ if(!SSL_set_fd(backend->handle, (int)sockfd)) {
+ /* pass the raw socket into the SSL layers */
+ failf(data, "SSL: SSL_set_fd failed: %s",
+ ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer)));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ connssl->connecting_state = ssl_connect_2;
+
+ return CURLE_OK;
+}
+
+static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex)
+{
+ struct Curl_easy *data = conn->data;
+ int err;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
+ || ssl_connect_2_reading == connssl->connecting_state
+ || ssl_connect_2_writing == connssl->connecting_state);
+
+ ERR_clear_error();
+
+ err = SSL_connect(backend->handle);
+#ifndef HAVE_KEYLOG_CALLBACK
+ if(Curl_tls_keylog_enabled()) {
+ /* If key logging is enabled, wait for the handshake to complete and then
+ * proceed with logging secrets (for TLS 1.2 or older).
+ */
+ ossl_log_tls12_secret(backend->handle, &backend->keylog_done);
+ }
+#endif
+
+ /* 1 is fine
+ 0 is "not successful but was shut down controlled"
+ <0 is "handshake was not successful, because a fatal error occurred" */
+ if(1 != err) {
+ int detail = SSL_get_error(backend->handle, err);
+
+ if(SSL_ERROR_WANT_READ == detail) {
+ connssl->connecting_state = ssl_connect_2_reading;
+ return CURLE_OK;
+ }
+ if(SSL_ERROR_WANT_WRITE == detail) {
+ connssl->connecting_state = ssl_connect_2_writing;
+ return CURLE_OK;
+ }
+#ifdef SSL_ERROR_WANT_ASYNC
+ if(SSL_ERROR_WANT_ASYNC == detail) {
+ connssl->connecting_state = ssl_connect_2;
+ return CURLE_OK;
+ }
+#endif
+ else {
+ /* untreated error */
+ unsigned long errdetail;
+ char error_buffer[256]="";
+ CURLcode result;
+ long lerr;
+ int lib;
+ int reason;
+
+ /* the connection failed, we're not waiting for anything else. */
+ connssl->connecting_state = ssl_connect_2;
+
+ /* Get the earliest error code from the thread's error queue and removes
+ the entry. */
+ errdetail = ERR_get_error();
+
+ /* Extract which lib and reason */
+ lib = ERR_GET_LIB(errdetail);
+ reason = ERR_GET_REASON(errdetail);
+
+ if((lib == ERR_LIB_SSL) &&
+ ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) ||
+ (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) {
+ result = CURLE_PEER_FAILED_VERIFICATION;
+
+ lerr = SSL_get_verify_result(backend->handle);
+ if(lerr != X509_V_OK) {
+ SSL_SET_OPTION_LVALUE(certverifyresult) = lerr;
+ msnprintf(error_buffer, sizeof(error_buffer),
+ "SSL certificate problem: %s",
+ X509_verify_cert_error_string(lerr));
+ }
+ else
+ /* strcpy() is fine here as long as the string fits within
+ error_buffer */
+ strcpy(error_buffer, "SSL certificate verification failed");
+ }
+ else {
+ result = CURLE_SSL_CONNECT_ERROR;
+ ossl_strerror(errdetail, error_buffer, sizeof(error_buffer));
+ }
+
+ /* detail is already set to the SSL error above */
+
+ /* If we e.g. use SSLv2 request-method and the server doesn't like us
+ * (RST connection etc.), OpenSSL gives no explanation whatsoever and
+ * the SO_ERROR is also lost.
+ */
+ if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
+ const char * const hostname = SSL_HOST_NAME();
+#ifndef CURL_DISABLE_PROXY
+ const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
+#else
+ const long int port = conn->remote_port;
+#endif
+ char extramsg[80]="";
+ int sockerr = SOCKERRNO;
+ if(sockerr && detail == SSL_ERROR_SYSCALL)
+ Curl_strerror(sockerr, extramsg, sizeof(extramsg));
+ failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%ld ",
+ extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
+ hostname, port);
+ return result;
+ }
+
+ /* Could be a CERT problem */
+ failf(data, "%s", error_buffer);
+
+ return result;
+ }
+ }
+ else {
+ /* we have been connected fine, we're not waiting for anything else. */
+ connssl->connecting_state = ssl_connect_3;
+
+ /* Informational message */
+ infof(data, "SSL connection using %s / %s\n",
+ get_ssl_version_txt(backend->handle),
+ SSL_get_cipher(backend->handle));
+
+#ifdef HAS_ALPN
+ /* Sets data and len to negotiated protocol, len is 0 if no protocol was
+ * negotiated
+ */
+ if(conn->bits.tls_enable_alpn) {
+ const unsigned char *neg_protocol;
+ unsigned int len;
+ SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len);
+ if(len != 0) {
+ infof(data, "ALPN, server accepted to use %.*s\n", len, neg_protocol);
+
+#ifdef USE_NGHTTP2
+ if(len == NGHTTP2_PROTO_VERSION_ID_LEN &&
+ !memcmp(NGHTTP2_PROTO_VERSION_ID, neg_protocol, len)) {
+ conn->negnpn = CURL_HTTP_VERSION_2;
+ }
+ else
+#endif
+ if(len == ALPN_HTTP_1_1_LENGTH &&
+ !memcmp(ALPN_HTTP_1_1, neg_protocol, ALPN_HTTP_1_1_LENGTH)) {
+ conn->negnpn = CURL_HTTP_VERSION_1_1;
+ }
+ }
+ else
+ infof(data, "ALPN, server did not agree to a protocol\n");
+
+ Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ }
+#endif
+
+ return CURLE_OK;
+ }
+}
+
+static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len)
+{
+ int i, ilen;
+
+ ilen = (int)len;
+ if(ilen < 0)
+ return 1; /* buffer too big */
+
+ i = i2t_ASN1_OBJECT(buf, ilen, a);
+
+ if(i >= ilen)
+ return 1; /* buffer too small */
+
+ return 0;
+}
+
+#define push_certinfo(_label, _num) \
+do { \
+ long info_len = BIO_get_mem_data(mem, &ptr); \
+ Curl_ssl_push_certinfo_len(data, _num, _label, ptr, info_len); \
+ if(1 != BIO_reset(mem)) \
+ break; \
+} while(0)
+
+static void pubkey_show(struct Curl_easy *data,
+ BIO *mem,
+ int num,
+ const char *type,
+ const char *name,
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+ const
+#endif
+ BIGNUM *bn)
+{
+ char *ptr;
+ char namebuf[32];
+
+ msnprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name);
+
+ if(bn)
+ BN_print(mem, bn);
+ push_certinfo(namebuf, num);
+}
+
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+#define print_pubkey_BN(_type, _name, _num) \
+ pubkey_show(data, mem, _num, #_type, #_name, _name)
+
+#else
+#define print_pubkey_BN(_type, _name, _num) \
+do { \
+ if(_type->_name) { \
+ pubkey_show(data, mem, _num, #_type, #_name, _type->_name); \
+ } \
+} while(0)
+#endif
+
+static void X509V3_ext(struct Curl_easy *data,
+ int certnum,
+ CONST_EXTS STACK_OF(X509_EXTENSION) *exts)
+{
+ int i;
+
+ if((int)sk_X509_EXTENSION_num(exts) <= 0)
+ /* no extensions, bail out */
+ return;
+
+ for(i = 0; i < (int)sk_X509_EXTENSION_num(exts); i++) {
+ ASN1_OBJECT *obj;
+ X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
+ BUF_MEM *biomem;
+ char namebuf[128];
+ BIO *bio_out = BIO_new(BIO_s_mem());
+
+ if(!bio_out)
+ return;
+
+ obj = X509_EXTENSION_get_object(ext);
+
+ asn1_object_dump(obj, namebuf, sizeof(namebuf));
+
+ if(!X509V3_EXT_print(bio_out, ext, 0, 0))
+ ASN1_STRING_print(bio_out, (ASN1_STRING *)X509_EXTENSION_get_data(ext));
+
+ BIO_get_mem_ptr(bio_out, &biomem);
+ Curl_ssl_push_certinfo_len(data, certnum, namebuf, biomem->data,
+ biomem->length);
+ BIO_free(bio_out);
+ }
+}
+
+#ifdef OPENSSL_IS_BORINGSSL
+typedef size_t numcert_t;
+#else
+typedef int numcert_t;
+#endif
+
+static CURLcode get_cert_chain(struct connectdata *conn,
+ struct ssl_connect_data *connssl)
+{
+ CURLcode result;
+ STACK_OF(X509) *sk;
+ int i;
+ struct Curl_easy *data = conn->data;
+ numcert_t numcerts;
+ BIO *mem;
+ struct ssl_backend_data *backend = connssl->backend;
+
+ sk = SSL_get_peer_cert_chain(backend->handle);
+ if(!sk) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ numcerts = sk_X509_num(sk);
+
+ result = Curl_ssl_init_certinfo(data, (int)numcerts);
+ if(result) {
+ return result;
+ }
+
+ mem = BIO_new(BIO_s_mem());
+
+ for(i = 0; i < (int)numcerts; i++) {
+ ASN1_INTEGER *num;
+ X509 *x = sk_X509_value(sk, i);
+ EVP_PKEY *pubkey = NULL;
+ int j;
+ char *ptr;
+ const ASN1_BIT_STRING *psig = NULL;
+
+ X509_NAME_print_ex(mem, X509_get_subject_name(x), 0, XN_FLAG_ONELINE);
+ push_certinfo("Subject", i);
+
+ X509_NAME_print_ex(mem, X509_get_issuer_name(x), 0, XN_FLAG_ONELINE);
+ push_certinfo("Issuer", i);
+
+ BIO_printf(mem, "%lx", X509_get_version(x));
+ push_certinfo("Version", i);
+
+ num = X509_get_serialNumber(x);
+ if(num->type == V_ASN1_NEG_INTEGER)
+ BIO_puts(mem, "-");
+ for(j = 0; j < num->length; j++)
+ BIO_printf(mem, "%02x", num->data[j]);
+ push_certinfo("Serial Number", i);
+
+#if defined(HAVE_X509_GET0_SIGNATURE) && defined(HAVE_X509_GET0_EXTENSIONS)
+ {
+ const X509_ALGOR *sigalg = NULL;
+ X509_PUBKEY *xpubkey = NULL;
+ ASN1_OBJECT *pubkeyoid = NULL;
+
+ X509_get0_signature(&psig, &sigalg, x);
+ if(sigalg) {
+ i2a_ASN1_OBJECT(mem, sigalg->algorithm);
+ push_certinfo("Signature Algorithm", i);
+ }
+
+ xpubkey = X509_get_X509_PUBKEY(x);
+ if(xpubkey) {
+ X509_PUBKEY_get0_param(&pubkeyoid, NULL, NULL, NULL, xpubkey);
+ if(pubkeyoid) {
+ i2a_ASN1_OBJECT(mem, pubkeyoid);
+ push_certinfo("Public Key Algorithm", i);
+ }
+ }
+
+ X509V3_ext(data, i, X509_get0_extensions(x));
+ }
+#else
+ {
+ /* before OpenSSL 1.0.2 */
+ X509_CINF *cinf = x->cert_info;
+
+ i2a_ASN1_OBJECT(mem, cinf->signature->algorithm);
+ push_certinfo("Signature Algorithm", i);
+
+ i2a_ASN1_OBJECT(mem, cinf->key->algor->algorithm);
+ push_certinfo("Public Key Algorithm", i);
+
+ X509V3_ext(data, i, cinf->extensions);
+
+ psig = x->signature;
+ }
+#endif
+
+ ASN1_TIME_print(mem, X509_get0_notBefore(x));
+ push_certinfo("Start date", i);
+
+ ASN1_TIME_print(mem, X509_get0_notAfter(x));
+ push_certinfo("Expire date", i);
+
+ pubkey = X509_get_pubkey(x);
+ if(!pubkey)
+ infof(data, " Unable to load public key\n");
+ else {
+ int pktype;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+ pktype = EVP_PKEY_id(pubkey);
+#else
+ pktype = pubkey->type;
+#endif
+ switch(pktype) {
+ case EVP_PKEY_RSA:
+ {
+ RSA *rsa;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+ rsa = EVP_PKEY_get0_RSA(pubkey);
+#else
+ rsa = pubkey->pkey.rsa;
+#endif
+
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+ {
+ const BIGNUM *n;
+ const BIGNUM *e;
+
+ RSA_get0_key(rsa, &n, &e, NULL);
+ BIO_printf(mem, "%d", BN_num_bits(n));
+ push_certinfo("RSA Public Key", i);
+ print_pubkey_BN(rsa, n, i);
+ print_pubkey_BN(rsa, e, i);
+ }
+#else
+ BIO_printf(mem, "%d", BN_num_bits(rsa->n));
+ push_certinfo("RSA Public Key", i);
+ print_pubkey_BN(rsa, n, i);
+ print_pubkey_BN(rsa, e, i);
+#endif
+
+ break;
+ }
+ case EVP_PKEY_DSA:
+ {
+#ifndef OPENSSL_NO_DSA
+ DSA *dsa;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+ dsa = EVP_PKEY_get0_DSA(pubkey);
+#else
+ dsa = pubkey->pkey.dsa;
+#endif
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+ {
+ const BIGNUM *p;
+ const BIGNUM *q;
+ const BIGNUM *g;
+ const BIGNUM *pub_key;
+
+ DSA_get0_pqg(dsa, &p, &q, &g);
+ DSA_get0_key(dsa, &pub_key, NULL);
+
+ print_pubkey_BN(dsa, p, i);
+ print_pubkey_BN(dsa, q, i);
+ print_pubkey_BN(dsa, g, i);
+ print_pubkey_BN(dsa, pub_key, i);
+ }
+#else
+ print_pubkey_BN(dsa, p, i);
+ print_pubkey_BN(dsa, q, i);
+ print_pubkey_BN(dsa, g, i);
+ print_pubkey_BN(dsa, pub_key, i);
+#endif
+#endif /* !OPENSSL_NO_DSA */
+ break;
+ }
+ case EVP_PKEY_DH:
+ {
+ DH *dh;
+#ifdef HAVE_OPAQUE_EVP_PKEY
+ dh = EVP_PKEY_get0_DH(pubkey);
+#else
+ dh = pubkey->pkey.dh;
+#endif
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+ {
+ const BIGNUM *p;
+ const BIGNUM *q;
+ const BIGNUM *g;
+ const BIGNUM *pub_key;
+ DH_get0_pqg(dh, &p, &q, &g);
+ DH_get0_key(dh, &pub_key, NULL);
+ print_pubkey_BN(dh, p, i);
+ print_pubkey_BN(dh, q, i);
+ print_pubkey_BN(dh, g, i);
+ print_pubkey_BN(dh, pub_key, i);
+ }
+#else
+ print_pubkey_BN(dh, p, i);
+ print_pubkey_BN(dh, g, i);
+ print_pubkey_BN(dh, pub_key, i);
+#endif
+ break;
+ }
+ }
+ EVP_PKEY_free(pubkey);
+ }
+
+ if(psig) {
+ for(j = 0; j < psig->length; j++)
+ BIO_printf(mem, "%02x:", psig->data[j]);
+ push_certinfo("Signature", i);
+ }
+
+ PEM_write_bio_X509(mem, x);
+ push_certinfo("Cert", i);
+ }
+
+ BIO_free(mem);
+
+ return CURLE_OK;
+}
+
+/*
+ * Heavily modified from:
+ * https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#OpenSSL
+ */
+static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert,
+ const char *pinnedpubkey)
+{
+ /* Scratch */
+ int len1 = 0, len2 = 0;
+ unsigned char *buff1 = NULL, *temp = 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 {
+ /* Begin Gyrations to get the subjectPublicKeyInfo */
+ /* Thanks to Viktor Dukhovni on the OpenSSL mailing list */
+
+ /* https://groups.google.com/group/mailing.openssl.users/browse_thread
+ /thread/d61858dae102c6c7 */
+ len1 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), NULL);
+ if(len1 < 1)
+ break; /* failed */
+
+ buff1 = temp = malloc(len1);
+ if(!buff1)
+ break; /* failed */
+
+ /* https://www.openssl.org/docs/crypto/d2i_X509.html */
+ len2 = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), &temp);
+
+ /*
+ * These checks are verifying we got back the same values as when we
+ * sized the buffer. It's pretty weak since they should always be the
+ * same. But it gives us something to test.
+ */
+ if((len1 != len2) || !temp || ((temp - buff1) != len1))
+ break; /* failed */
+
+ /* End Gyrations */
+
+ /* The one good exit point */
+ result = Curl_pin_peer_pubkey(data, pinnedpubkey, buff1, len1);
+ } while(0);
+
+ if(buff1)
+ free(buff1);
+
+ return result;
+}
+
+/*
+ * Get the server cert, verify it and show it etc, only call failf() if the
+ * 'strict' argument is TRUE as otherwise all this is for informational
+ * purposes only!
+ *
+ * We check certificates to authenticate the server; otherwise we risk
+ * man-in-the-middle attack.
+ */
+static CURLcode servercert(struct connectdata *conn,
+ struct ssl_connect_data *connssl,
+ bool strict)
+{
+ CURLcode result = CURLE_OK;
+ int rc;
+ long lerr;
+ struct Curl_easy *data = conn->data;
+ X509 *issuer;
+ BIO *fp = NULL;
+ char error_buffer[256]="";
+ char buffer[2048];
+ const char *ptr;
+ BIO *mem = BIO_new(BIO_s_mem());
+ struct ssl_backend_data *backend = connssl->backend;
+
+ if(data->set.ssl.certinfo)
+ /* we've been asked to gather certificate info! */
+ (void)get_cert_chain(conn, connssl);
+
+ backend->server_cert = SSL_get_peer_certificate(backend->handle);
+ if(!backend->server_cert) {
+ BIO_free(mem);
+ if(!strict)
+ return CURLE_OK;
+
+ failf(data, "SSL: couldn't get peer certificate!");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ infof(data, "%s certificate:\n", SSL_IS_PROXY() ? "Proxy" : "Server");
+
+ rc = x509_name_oneline(X509_get_subject_name(backend->server_cert),
+ buffer, sizeof(buffer));
+ infof(data, " subject: %s\n", rc?"[NONE]":buffer);
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ {
+ long len;
+ ASN1_TIME_print(mem, X509_get0_notBefore(backend->server_cert));
+ len = BIO_get_mem_data(mem, (char **) &ptr);
+ infof(data, " start date: %.*s\n", len, ptr);
+ (void)BIO_reset(mem);
+
+ ASN1_TIME_print(mem, X509_get0_notAfter(backend->server_cert));
+ len = BIO_get_mem_data(mem, (char **) &ptr);
+ infof(data, " expire date: %.*s\n", len, ptr);
+ (void)BIO_reset(mem);
+ }
+#endif
+
+ BIO_free(mem);
+
+ if(SSL_CONN_CONFIG(verifyhost)) {
+ result = verifyhost(conn, backend->server_cert);
+ if(result) {
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ return result;
+ }
+ }
+
+ rc = x509_name_oneline(X509_get_issuer_name(backend->server_cert),
+ buffer, sizeof(buffer));
+ if(rc) {
+ if(strict)
+ failf(data, "SSL: couldn't get X509-issuer name!");
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else {
+ infof(data, " issuer: %s\n", buffer);
+
+ /* We could do all sorts of certificate verification stuff here before
+ deallocating the certificate. */
+
+ /* e.g. match issuer name with provided issuer certificate */
+ if(SSL_SET_OPTION(issuercert) || SSL_SET_OPTION(issuercert_blob)) {
+ if(SSL_SET_OPTION(issuercert_blob))
+ fp = BIO_new_mem_buf(SSL_SET_OPTION(issuercert_blob)->data,
+ (int)SSL_SET_OPTION(issuercert_blob)->len);
+ else {
+ fp = BIO_new(BIO_s_file());
+ if(fp == NULL) {
+ failf(data,
+ "BIO_new return NULL, " OSSL_PACKAGE
+ " error %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)) );
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(BIO_read_filename(fp, SSL_SET_OPTION(issuercert)) <= 0) {
+ if(strict)
+ failf(data, "SSL: Unable to open issuer cert (%s)",
+ SSL_SET_OPTION(issuercert));
+ BIO_free(fp);
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ return CURLE_SSL_ISSUER_ERROR;
+ }
+ }
+
+ issuer = PEM_read_bio_X509(fp, NULL, ZERO_NULL, NULL);
+ if(!issuer) {
+ if(strict)
+ failf(data, "SSL: Unable to read issuer cert (%s)",
+ SSL_SET_OPTION(issuercert));
+ BIO_free(fp);
+ X509_free(issuer);
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ return CURLE_SSL_ISSUER_ERROR;
+ }
+
+ if(X509_check_issued(issuer, backend->server_cert) != X509_V_OK) {
+ if(strict)
+ failf(data, "SSL: Certificate issuer check failed (%s)",
+ SSL_SET_OPTION(issuercert));
+ BIO_free(fp);
+ X509_free(issuer);
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ return CURLE_SSL_ISSUER_ERROR;
+ }
+
+ infof(data, " SSL certificate issuer check ok (%s)\n",
+ SSL_SET_OPTION(issuercert));
+ BIO_free(fp);
+ X509_free(issuer);
+ }
+
+ lerr = SSL_get_verify_result(backend->handle);
+ SSL_SET_OPTION_LVALUE(certverifyresult) = lerr;
+ if(lerr != X509_V_OK) {
+ if(SSL_CONN_CONFIG(verifypeer)) {
+ /* We probably never reach this, because SSL_connect() will fail
+ and we return earlier if verifypeer is set? */
+ if(strict)
+ failf(data, "SSL certificate verify result: %s (%ld)",
+ X509_verify_cert_error_string(lerr), lerr);
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else
+ infof(data, " SSL certificate verify result: %s (%ld),"
+ " continuing anyway.\n",
+ X509_verify_cert_error_string(lerr), lerr);
+ }
+ else
+ infof(data, " SSL certificate verify ok.\n");
+ }
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
+ !defined(OPENSSL_NO_OCSP)
+ if(SSL_CONN_CONFIG(verifystatus)) {
+ result = verifystatus(conn, connssl);
+ if(result) {
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ return result;
+ }
+ }
+#endif
+
+ if(!strict)
+ /* when not strict, we don't bother about the verify cert problems */
+ result = CURLE_OK;
+
+ ptr = SSL_IS_PROXY() ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
+ if(!result && ptr) {
+ result = pkp_pin_peer_pubkey(data, backend->server_cert, ptr);
+ if(result)
+ failf(data, "SSL: public key does not match pinned public key!");
+ }
+
+ X509_free(backend->server_cert);
+ backend->server_cert = NULL;
+ connssl->connecting_state = ssl_connect_done;
+
+ return result;
+}
+
+static CURLcode ossl_connect_step3(struct connectdata *conn, int sockindex)
+{
+ CURLcode result = CURLE_OK;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+ DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+
+ /*
+ * We check certificates to authenticate the server; otherwise we risk
+ * man-in-the-middle attack; NEVERTHELESS, if we're told explicitly not to
+ * verify the peer ignore faults and failures from the server cert
+ * operations.
+ */
+
+ result = servercert(conn, connssl, (SSL_CONN_CONFIG(verifypeer) ||
+ SSL_CONN_CONFIG(verifyhost)));
+
+ if(!result)
+ connssl->connecting_state = ssl_connect_done;
+
+ return result;
+}
+
+static Curl_recv ossl_recv;
+static Curl_send ossl_send;
+
+static CURLcode ossl_connect_common(struct connectdata *conn,
+ int sockindex,
+ bool nonblocking,
+ bool *done)
+{
+ CURLcode result;
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ curl_socket_t sockfd = conn->sock[sockindex];
+ 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 */
+ const timediff_t 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;
+ }
+
+ result = ossl_connect_step1(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 allowed time left */
+ const timediff_t 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;
+ }
+ if(0 == what) {
+ if(nonblocking) {
+ *done = FALSE;
+ return CURLE_OK;
+ }
+ /* 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 done nonblocking 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 = ossl_connect_step2(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 = ossl_connect_step3(conn, sockindex);
+ if(result)
+ return result;
+ }
+
+ if(ssl_connect_done == connssl->connecting_state) {
+ connssl->state = ssl_connection_complete;
+ conn->recv[sockindex] = ossl_recv;
+ conn->send[sockindex] = ossl_send;
+ *done = TRUE;
+ }
+ else
+ *done = FALSE;
+
+ /* Reset our connect state machine */
+ connssl->connecting_state = ssl_connect_1;
+
+ return CURLE_OK;
+}
+
+static CURLcode Curl_ossl_connect_nonblocking(struct connectdata *conn,
+ int sockindex,
+ bool *done)
+{
+ return ossl_connect_common(conn, sockindex, TRUE, done);
+}
+
+static CURLcode Curl_ossl_connect(struct connectdata *conn, int sockindex)
+{
+ CURLcode result;
+ bool done = FALSE;
+
+ result = ossl_connect_common(conn, sockindex, FALSE, &done);
+ if(result)
+ return result;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+static bool Curl_ossl_data_pending(const struct connectdata *conn,
+ int connindex)
+{
+ const struct ssl_connect_data *connssl = &conn->ssl[connindex];
+ if(connssl->backend->handle && SSL_pending(connssl->backend->handle))
+ return TRUE;
+#ifndef CURL_DISABLE_PROXY
+ {
+ const struct ssl_connect_data *proxyssl = &conn->proxy_ssl[connindex];
+ if(proxyssl->backend->handle && SSL_pending(proxyssl->backend->handle))
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
+
+static size_t Curl_ossl_version(char *buffer, size_t size);
+
+static ssize_t ossl_send(struct connectdata *conn,
+ int sockindex,
+ const void *mem,
+ size_t len,
+ CURLcode *curlcode)
+{
+ /* SSL_write() is said to return 'int' while write() and send() returns
+ 'size_t' */
+ int err;
+ char error_buffer[256];
+ unsigned long sslerror;
+ int memlen;
+ int rc;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+
+ ERR_clear_error();
+
+ memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
+ rc = SSL_write(backend->handle, mem, memlen);
+
+ if(rc <= 0) {
+ err = SSL_get_error(backend->handle, rc);
+
+ switch(err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ /* The operation did not complete; the same TLS/SSL I/O function
+ should be called again later. This is basically an EWOULDBLOCK
+ equivalent. */
+ *curlcode = CURLE_AGAIN;
+ return -1;
+ case SSL_ERROR_SYSCALL:
+ {
+ int sockerr = SOCKERRNO;
+ sslerror = ERR_get_error();
+ if(sslerror)
+ ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
+ else if(sockerr)
+ Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
+ else {
+ strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer));
+ error_buffer[sizeof(error_buffer) - 1] = '\0';
+ }
+ failf(conn->data, OSSL_PACKAGE " SSL_write: %s, errno %d",
+ error_buffer, sockerr);
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+ case SSL_ERROR_SSL:
+ /* A failure in the SSL library occurred, usually a protocol error.
+ The OpenSSL error queue contains more information on the error. */
+ sslerror = ERR_get_error();
+ if(ERR_GET_LIB(sslerror) == ERR_LIB_SSL &&
+ ERR_GET_REASON(sslerror) == SSL_R_BIO_NOT_SET &&
+ conn->ssl[sockindex].state == ssl_connection_complete
+#ifndef CURL_DISABLE_PROXY
+ && conn->proxy_ssl[sockindex].state == ssl_connection_complete
+#endif
+ ) {
+ char ver[120];
+ Curl_ossl_version(ver, 120);
+ failf(conn->data, "Error: %s does not support double SSL tunneling.",
+ ver);
+ }
+ else
+ failf(conn->data, "SSL_write() error: %s",
+ ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)));
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+ /* a true error */
+ failf(conn->data, OSSL_PACKAGE " SSL_write: %s, errno %d",
+ SSL_ERROR_to_str(err), SOCKERRNO);
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+ *curlcode = CURLE_OK;
+ return (ssize_t)rc; /* number of bytes */
+}
+
+static ssize_t ossl_recv(struct connectdata *conn, /* connection data */
+ int num, /* socketindex */
+ char *buf, /* store read data here */
+ size_t buffersize, /* max amount to read */
+ CURLcode *curlcode)
+{
+ char error_buffer[256];
+ unsigned long sslerror;
+ ssize_t nread;
+ int buffsize;
+ struct ssl_connect_data *connssl = &conn->ssl[num];
+ struct ssl_backend_data *backend = connssl->backend;
+
+ ERR_clear_error();
+
+ buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
+ nread = (ssize_t)SSL_read(backend->handle, buf, buffsize);
+ if(nread <= 0) {
+ /* failed SSL_read */
+ int err = SSL_get_error(backend->handle, (int)nread);
+
+ switch(err) {
+ case SSL_ERROR_NONE: /* this is not an error */
+ break;
+ case SSL_ERROR_ZERO_RETURN: /* no more data */
+ /* close_notify alert */
+ if(num == FIRSTSOCKET)
+ /* mark the connection for close if it is indeed the control
+ connection */
+ connclose(conn, "TLS close_notify");
+ break;
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ /* there's data pending, re-invoke SSL_read() */
+ *curlcode = CURLE_AGAIN;
+ return -1;
+ default:
+ /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return
+ value/errno" */
+ /* https://www.openssl.org/docs/crypto/ERR_get_error.html */
+ sslerror = ERR_get_error();
+ if((nread < 0) || sslerror) {
+ /* If the return code was negative or there actually is an error in the
+ queue */
+ int sockerr = SOCKERRNO;
+ if(sslerror)
+ ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
+ else if(sockerr && err == SSL_ERROR_SYSCALL)
+ Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
+ else {
+ strncpy(error_buffer, SSL_ERROR_to_str(err), sizeof(error_buffer));
+ error_buffer[sizeof(error_buffer) - 1] = '\0';
+ }
+ failf(conn->data, OSSL_PACKAGE " SSL_read: %s, errno %d",
+ error_buffer, sockerr);
+ *curlcode = CURLE_RECV_ERROR;
+ return -1;
+ }
+ /* For debug builds be a little stricter and error on any
+ SSL_ERROR_SYSCALL. For example a server may have closed the connection
+ abruptly without a close_notify alert. For compatibility with older
+ peers we don't do this by default. #4624
+
+ We can use this to gauge how many users may be affected, and
+ if it goes ok eventually transition to allow in dev and release with
+ the newest OpenSSL: #if (OPENSSL_VERSION_NUMBER >= 0x10101000L) */
+#ifdef DEBUGBUILD
+ if(err == SSL_ERROR_SYSCALL) {
+ int sockerr = SOCKERRNO;
+ if(sockerr)
+ Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
+ else {
+ msnprintf(error_buffer, sizeof(error_buffer),
+ "Connection closed abruptly");
+ }
+ failf(conn->data, OSSL_PACKAGE " SSL_read: %s, errno %d"
+ " (Fatal because this is a curl debug build)",
+ error_buffer, sockerr);
+ *curlcode = CURLE_RECV_ERROR;
+ return -1;
+ }
+#endif
+ }
+ }
+ return nread;
+}
+
+static size_t Curl_ossl_version(char *buffer, size_t size)
+{
+#ifdef LIBRESSL_VERSION_NUMBER
+#if LIBRESSL_VERSION_NUMBER < 0x2070100fL
+ return msnprintf(buffer, size, "%s/%lx.%lx.%lx",
+ OSSL_PACKAGE,
+ (LIBRESSL_VERSION_NUMBER>>28)&0xf,
+ (LIBRESSL_VERSION_NUMBER>>20)&0xff,
+ (LIBRESSL_VERSION_NUMBER>>12)&0xff);
+#else /* OpenSSL_version() first appeared in LibreSSL 2.7.1 */
+ char *p;
+ int count;
+ const char *ver = OpenSSL_version(OPENSSL_VERSION);
+ const char expected[] = OSSL_PACKAGE " "; /* ie "LibreSSL " */
+ if(Curl_strncasecompare(ver, expected, sizeof(expected) - 1)) {
+ ver += sizeof(expected) - 1;
+ }
+ count = msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, ver);
+ for(p = buffer; *p; ++p) {
+ if(ISSPACE(*p))
+ *p = '_';
+ }
+ return count;
+#endif
+#elif defined(OPENSSL_IS_BORINGSSL)
+ return msnprintf(buffer, size, OSSL_PACKAGE);
+#elif defined(HAVE_OPENSSL_VERSION) && defined(OPENSSL_VERSION_STRING)
+ return msnprintf(buffer, size, "%s/%s",
+ OSSL_PACKAGE, OpenSSL_version(OPENSSL_VERSION_STRING));
+#else
+ /* not LibreSSL, BoringSSL and not using OpenSSL_version */
+
+ char sub[3];
+ unsigned long ssleay_value;
+ sub[2]='\0';
+ sub[1]='\0';
+ ssleay_value = OpenSSL_version_num();
+ if(ssleay_value < 0x906000) {
+ ssleay_value = SSLEAY_VERSION_NUMBER;
+ sub[0]='\0';
+ }
+ else {
+ if(ssleay_value&0xff0) {
+ int minor_ver = (ssleay_value >> 4) & 0xff;
+ if(minor_ver > 26) {
+ /* handle extended version introduced for 0.9.8za */
+ sub[1] = (char) ((minor_ver - 1) % 26 + 'a' + 1);
+ sub[0] = 'z';
+ }
+ else {
+ sub[0] = (char) (minor_ver + 'a' - 1);
+ }
+ }
+ else
+ sub[0]='\0';
+ }
+
+ return msnprintf(buffer, size, "%s/%lx.%lx.%lx%s"
+#ifdef OPENSSL_FIPS
+ "-fips"
+#endif
+ ,
+ OSSL_PACKAGE,
+ (ssleay_value>>28)&0xf,
+ (ssleay_value>>20)&0xff,
+ (ssleay_value>>12)&0xff,
+ sub);
+#endif /* OPENSSL_IS_BORINGSSL */
+}
+
+/* can be called with data == NULL */
+static CURLcode Curl_ossl_random(struct Curl_easy *data,
+ unsigned char *entropy, size_t length)
+{
+ int rc;
+ if(data) {
+ if(Curl_ossl_seed(data)) /* Initiate the seed if not already done */
+ return CURLE_FAILED_INIT; /* couldn't seed for some reason */
+ }
+ else {
+ if(!rand_enough())
+ return CURLE_FAILED_INIT;
+ }
+ /* RAND_bytes() returns 1 on success, 0 otherwise. */
+ rc = RAND_bytes(entropy, curlx_uztosi(length));
+ return (rc == 1 ? CURLE_OK : CURLE_FAILED_INIT);
+}
+
+static CURLcode Curl_ossl_md5sum(unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *md5sum /* output */,
+ size_t unused)
+{
+ EVP_MD_CTX *mdctx;
+ unsigned int len = 0;
+ (void) unused;
+
+ mdctx = EVP_MD_CTX_create();
+ if(!mdctx)
+ return CURLE_OUT_OF_MEMORY;
+ EVP_DigestInit(mdctx, EVP_md5());
+ EVP_DigestUpdate(mdctx, tmp, tmplen);
+ EVP_DigestFinal_ex(mdctx, md5sum, &len);
+ EVP_MD_CTX_destroy(mdctx);
+ return CURLE_OK;
+}
+
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+static CURLcode Curl_ossl_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum /* output */,
+ size_t unused)
+{
+ EVP_MD_CTX *mdctx;
+ unsigned int len = 0;
+ (void) unused;
+
+ mdctx = EVP_MD_CTX_create();
+ if(!mdctx)
+ return CURLE_OUT_OF_MEMORY;
+ EVP_DigestInit(mdctx, EVP_sha256());
+ EVP_DigestUpdate(mdctx, tmp, tmplen);
+ EVP_DigestFinal_ex(mdctx, sha256sum, &len);
+ EVP_MD_CTX_destroy(mdctx);
+ return CURLE_OK;
+}
+#endif
+
+static bool Curl_ossl_cert_status_request(void)
+{
+#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
+ !defined(OPENSSL_NO_OCSP)
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
+static void *Curl_ossl_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info)
+{
+ /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */
+ struct ssl_backend_data *backend = connssl->backend;
+ return info == CURLINFO_TLS_SESSION ?
+ (void *)backend->ctx : (void *)backend->handle;
+}
+
+const struct Curl_ssl Curl_ssl_openssl = {
+ { CURLSSLBACKEND_OPENSSL, "openssl" }, /* info */
+
+ SSLSUPP_CA_PATH |
+ SSLSUPP_CERTINFO |
+ SSLSUPP_PINNEDPUBKEY |
+ SSLSUPP_SSL_CTX |
+#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
+ SSLSUPP_TLS13_CIPHERSUITES |
+#endif
+ SSLSUPP_HTTPS_PROXY,
+
+ sizeof(struct ssl_backend_data),
+
+ Curl_ossl_init, /* init */
+ Curl_ossl_cleanup, /* cleanup */
+ Curl_ossl_version, /* version */
+ Curl_ossl_check_cxn, /* check_cxn */
+ Curl_ossl_shutdown, /* shutdown */
+ Curl_ossl_data_pending, /* data_pending */
+ Curl_ossl_random, /* random */
+ Curl_ossl_cert_status_request, /* cert_status_request */
+ Curl_ossl_connect, /* connect */
+ Curl_ossl_connect_nonblocking, /* connect_nonblocking */
+ Curl_ossl_get_internals, /* get_internals */
+ Curl_ossl_close, /* close_one */
+ Curl_ossl_close_all, /* close_all */
+ Curl_ossl_session_free, /* session_free */
+ Curl_ossl_set_engine, /* set_engine */
+ Curl_ossl_set_engine_default, /* set_engine_default */
+ Curl_ossl_engines_list, /* engines_list */
+ Curl_none_false_start, /* false_start */
+ Curl_ossl_md5sum, /* md5sum */
+#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+ Curl_ossl_sha256sum /* sha256sum */
+#else
+ NULL /* sha256sum */
+#endif
+};
+
+#endif /* USE_OPENSSL */
diff --git a/contrib/libs/curl/lib/vtls/openssl.h b/contrib/libs/curl/lib/vtls/openssl.h
new file mode 100644
index 0000000000..2f6e1b2db8
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/openssl.h
@@ -0,0 +1,37 @@
+#ifndef HEADER_CURL_SSLUSE_H
+#define HEADER_CURL_SSLUSE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, 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_OPENSSL
+/*
+ * This header should only be needed to get included by vtls.c and openssl.c
+ */
+
+#include "urldata.h"
+
+extern const struct Curl_ssl Curl_ssl_openssl;
+
+#endif /* USE_OPENSSL */
+#endif /* HEADER_CURL_SSLUSE_H */
diff --git a/contrib/libs/curl/lib/vtls/schannel.c b/contrib/libs/curl/lib/vtls/schannel.c
new file mode 100644
index 0000000000..d7bc38917f
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/schannel.c
@@ -0,0 +1,2444 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
+ * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
+ * Copyright (C) 2012 - 2020, 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 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 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
+
+#define BACKEND connssl->backend
+
+static Curl_recv schannel_recv;
+static Curl_send schannel_send;
+
+static CURLcode pkp_pin_peer_pubkey(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 connectdata *conn)
+{
+ struct Curl_easy *data = conn->data;
+ 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
+#if !defined(__W32API_MAJOR_VERSION) || \
+ !defined(__W32API_MINOR_VERSION) || \
+ defined(__MINGW64_VERSION_MAJOR) || \
+ (__W32API_MAJOR_VERSION > 5) || \
+ ((__W32API_MAJOR_VERSION == 5) && (__W32API_MINOR_VERSION > 0))
+ /* CALG_TLS1PRF has a syntax error in MinGW's w32api up to version 5.0,
+ see https://osdn.net/projects/mingw/ticket/38391 */
+ 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;
+}
+
+static CURLcode
+set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers)
+{
+ char *startCur = ciphers;
+ int algCount = 0;
+ static ALG_ID algIds[45]; /*There are 45 listed in the MS headers*/
+ while(startCur && (0 != *startCur) && (algCount < 45)) {
+ long alg = strtol(startCur, 0, 0);
+ if(!alg)
+ alg = get_alg_id_by_name(startCur);
+ if(alg)
+ algIds[algCount++] = alg;
+ 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 == NULL)
+ return CURLE_SSL_CERTPROBLEM;
+
+ store_name_len = sep - path;
+
+ if(_tcsnccmp(path, TEXT("CurrentUser"), store_name_len) == 0)
+ *store_name = CERT_SYSTEM_STORE_CURRENT_USER;
+ else if(_tcsnccmp(path, TEXT("LocalMachine"), store_name_len) == 0)
+ *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE;
+ else if(_tcsnccmp(path, TEXT("CurrentService"), store_name_len) == 0)
+ *store_name = CERT_SYSTEM_STORE_CURRENT_SERVICE;
+ else if(_tcsnccmp(path, TEXT("Services"), store_name_len) == 0)
+ *store_name = CERT_SYSTEM_STORE_SERVICES;
+ else if(_tcsnccmp(path, TEXT("Users"), store_name_len) == 0)
+ *store_name = CERT_SYSTEM_STORE_USERS;
+ else if(_tcsnccmp(path, TEXT("CurrentUserGroupPolicy"),
+ store_name_len) == 0)
+ *store_name = CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY;
+ else if(_tcsnccmp(path, TEXT("LocalMachineGroupPolicy"),
+ store_name_len) == 0)
+ *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY;
+ else if(_tcsnccmp(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 == NULL)
+ 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 == NULL)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+}
+#endif
+
+static CURLcode
+schannel_connect_step1(struct connectdata *conn, int sockindex)
+{
+ ssize_t written = -1;
+ struct Curl_easy *data = conn->data;
+ 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
+ SCHANNEL_CRED schannel_cred;
+ PCCERT_CONTEXT client_certs[1] = { NULL };
+ 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
+ TCHAR *host_name;
+ CURLcode result;
+#ifndef CURL_DISABLE_PROXY
+ char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
+ conn->host.name;
+#else
+ char * const hostname = conn->host.name;
+#endif
+
+ DEBUGF(infof(data,
+ "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
+ hostname, conn->remote_port));
+
+ if(curlx_verify_windows_version(5, 1, 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.\n");
+ }
+
+#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, 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)) {
+ if(curlx_verify_windows_version(6, 1, 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)) {
+ 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(conn);
+ if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL, sockindex)) {
+ BACKEND->cred = old_cred;
+ DEBUGF(infof(data, "schannel: re-using existing credential handle\n"));
+
+ /* increment the reference counter of the credential/session handle */
+ BACKEND->cred->refcount++;
+ DEBUGF(infof(data,
+ "schannel: incremented credential handle refcount = %d\n",
+ BACKEND->cred->refcount));
+ }
+ Curl_ssl_sessionid_unlock(conn);
+ }
+
+ if(!BACKEND->cred) {
+ /* 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(data->set.ssl.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\n"));
+ }
+ else if(data->set.ssl.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\n"));
+ }
+ }
+ 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\n"));
+ }
+
+ 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.\n"));
+ }
+
+ 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, conn);
+ if(result != CURLE_OK)
+ return result;
+ break;
+ }
+ case CURL_SSLVERSION_SSLv3:
+ schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
+ break;
+ case CURL_SSLVERSION_SSLv2:
+ schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
+ break;
+ 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));
+ 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 != NULL)
+ 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 == NULL) {
+ 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] == NULL) {
+ 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;
+
+ /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx
+ */
+ 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;
+ }
+ }
+ }
+
+ /* 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.\n");
+ }
+
+#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_NGHTTP2
+ if(data->set.httpversion >= CURL_HTTP_VERSION_2) {
+ memcpy(&alpn_buffer[cur], NGHTTP2_PROTO_ALPN, NGHTTP2_PROTO_ALPN_LEN);
+ cur += NGHTTP2_PROTO_ALPN_LEN;
+ infof(data, "schannel: ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
+ }
+#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, "schannel: ALPN, offering %s\n", 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);
+
+ /* setup request flags */
+ BACKEND->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_STREAM;
+
+ /* 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;
+ }
+
+ host_name = curlx_convert_UTF8_to_tchar(hostname);
+ if(!host_name)
+ 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, host_name, 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);
+
+ curlx_unicodefree(host_name);
+
+ 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...\n", outbuf.cbBuffer));
+
+ /* send initial handshake data which is now stored in output buffer */
+ result = Curl_write_plain(conn, 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\n", 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 connectdata *conn, int sockindex)
+{
+ int i;
+ ssize_t nread = -1, written = -1;
+ struct Curl_easy *data = conn->data;
+ 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;
+#ifndef CURL_DISABLE_PROXY
+ char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
+ conn->host.name;
+#else
+ char * const hostname = conn->host.name;
+#endif
+ const char *pubkey_ptr;
+
+ doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
+
+ DEBUGF(infof(data,
+ "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n",
+ hostname, 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 == NULL) {
+ BACKEND->decdata_offset = 0;
+ BACKEND->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
+ BACKEND->decdata_buffer = malloc(BACKEND->decdata_length);
+ if(BACKEND->decdata_buffer == NULL) {
+ failf(data, "schannel: unable to allocate memory");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ /* buffer to store previously received and encrypted data */
+ if(BACKEND->encdata_buffer == NULL) {
+ 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 == NULL) {
+ 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 == NULL) {
+ 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(;;) {
+ TCHAR *host_name;
+ 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\n"));
+ 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\n", nread));
+ }
+
+ DEBUGF(infof(data,
+ "schannel: encrypted data buffer: offset %zu length %zu\n",
+ 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 == NULL) {
+ 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);
+
+ host_name = curlx_convert_UTF8_to_tchar(hostname);
+ if(!host_name)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx
+ */
+ sspi_status = s_pSecFn->InitializeSecurityContext(
+ &BACKEND->cred->cred_handle, &BACKEND->ctxt->ctxt_handle,
+ host_name, BACKEND->req_flags, 0, 0, &inbuf_desc, 0, NULL,
+ &outbuf_desc, &BACKEND->ret_flags, &BACKEND->ctxt->time_stamp);
+
+ curlx_unicodefree(host_name);
+
+ /* 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\n"));
+ 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\n"));
+ 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...\n", outbuf[i].cbBuffer));
+
+ /* send handshake token to server */
+ result = Curl_write_plain(conn, 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 != NULL) {
+ 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\n",
+ 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\n"));
+ }
+
+ pubkey_ptr = SSL_IS_PROXY() ?
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
+ if(pubkey_ptr) {
+ result = pkp_pin_peer_pubkey(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(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 connectdata *conn;
+ 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->conn, insert_index, beg, end);
+ args->idx++;
+ }
+ return args->result == CURLE_OK;
+}
+
+static CURLcode
+schannel_connect_step3(struct connectdata *conn, int sockindex)
+{
+ CURLcode result = CURLE_OK;
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ SECURITY_STATUS sspi_status = SEC_E_OK;
+ CERT_CONTEXT *ccert_context = NULL;
+#ifdef DEBUGBUILD
+ const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
+ conn->host.name;
+#endif
+#ifdef HAS_ALPN
+ SecPkgContext_ApplicationProtocol alpn_result;
+#endif
+
+ DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+
+ DEBUGF(infof(data,
+ "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
+ 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, "schannel: ALPN, server accepted to use %.*s\n",
+ alpn_result.ProtocolIdSize, alpn_result.ProtocolId);
+
+#ifdef USE_NGHTTP2
+ if(alpn_result.ProtocolIdSize == NGHTTP2_PROTO_VERSION_ID_LEN &&
+ !memcmp(NGHTTP2_PROTO_VERSION_ID, alpn_result.ProtocolId,
+ NGHTTP2_PROTO_VERSION_ID_LEN)) {
+ 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, "ALPN, server did not agree to a protocol\n");
+ Curl_multiuse_state(conn, 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;
+ struct Curl_schannel_cred *old_cred = NULL;
+
+ Curl_ssl_sessionid_lock(conn);
+ incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL,
+ sockindex));
+ if(incache) {
+ if(old_cred != BACKEND->cred) {
+ DEBUGF(infof(data,
+ "schannel: old credential handle is stale, removing\n"));
+ /* we're not taking old_cred ownership here, no refcount++ is needed */
+ Curl_ssl_delsessionid(conn, (void *)old_cred);
+ incache = FALSE;
+ }
+ }
+ if(!incache) {
+ result = Curl_ssl_addsessionid(conn, (void *)BACKEND->cred,
+ sizeof(struct Curl_schannel_cred),
+ sockindex);
+ if(result) {
+ Curl_ssl_sessionid_unlock(conn);
+ failf(data, "schannel: failed to store credential handle");
+ return result;
+ }
+ else {
+ /* this cred session is now also referenced by sessionid cache */
+ BACKEND->cred->refcount++;
+ DEBUGF(infof(data,
+ "schannel: stored credential handle in session cache\n"));
+ }
+ }
+ Curl_ssl_sessionid_unlock(conn);
+ }
+
+ 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 == NULL)) {
+ 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.conn = conn;
+ 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 connectdata *conn, int sockindex,
+ bool nonblocking, bool *done)
+{
+ CURLcode result;
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ curl_socket_t sockfd = conn->sock[sockindex];
+ timediff_t timeout_ms;
+ int what;
+
+ /* check if the connection has already been established */
+ 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(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(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(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.
+ */
+ 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 connectdata *conn, int sockindex,
+ const void *buf, size_t len, CURLcode *err)
+{
+ ssize_t written = -1;
+ size_t data_len = 0;
+ unsigned char *data = NULL;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ SecBuffer outbuf[4];
+ SecBufferDesc outbuf_desc;
+ SECURITY_STATUS sspi_status = SEC_E_OK;
+ CURLcode result;
+
+ /* 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;
+ data = (unsigned char *) malloc(data_len);
+ if(data == NULL) {
+ *err = CURLE_OUT_OF_MEMORY;
+ return -1;
+ }
+
+ /* setup output buffers (header, data, trailer, empty) */
+ InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER,
+ data, BACKEND->stream_sizes.cbHeader);
+ InitSecBuffer(&outbuf[1], SECBUFFER_DATA,
+ data + BACKEND->stream_sizes.cbHeader, curlx_uztoul(len));
+ InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER,
+ data + 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(conn->data, NULL, FALSE);
+ if(timeout_ms < 0) {
+ /* we already got the timeout */
+ failf(conn->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(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
+ *err = CURLE_SEND_ERROR;
+ written = -1;
+ break;
+ }
+ else if(0 == what) {
+ failf(conn->data, "schannel: timed out sending data "
+ "(bytes sent: %zd)", written);
+ *err = CURLE_OPERATION_TIMEDOUT;
+ written = -1;
+ break;
+ }
+ /* socket is writable */
+
+ result = Curl_write_plain(conn, conn->sock[sockindex], data + 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(data);
+
+ 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 connectdata *conn, int sockindex,
+ char *buf, size_t len, CURLcode *err)
+{
+ size_t size = 0;
+ ssize_t nread = -1;
+ struct Curl_easy *data = conn->data;
+ 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;
+
+ /****************************************************************************
+ * 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\n", len));
+ *err = CURLE_OK;
+
+ if(len && len <= BACKEND->decdata_offset) {
+ infof(data, "schannel: enough decrypted data is already available\n");
+ goto cleanup;
+ }
+ else if(BACKEND->recv_unrecoverable_err) {
+ *err = BACKEND->recv_unrecoverable_err;
+ infof(data, "schannel: an unrecoverable error occurred in a prior call\n");
+ 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\n");
+ goto cleanup;
+ }
+ else if(!len) {
+ /* 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.
+ */
+ ; /* do nothing */
+ }
+ else if(!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 == NULL) {
+ *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\n",
+ BACKEND->encdata_length));
+ }
+
+ DEBUGF(infof(data,
+ "schannel: encrypted data buffer: offset %zu length %zu\n",
+ 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\n"));
+ else if(*err == CURLE_RECV_ERROR)
+ infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR\n");
+ else
+ infof(data, "schannel: Curl_read_plain returned error %d\n", *err);
+ }
+ else if(nread == 0) {
+ BACKEND->recv_connection_closed = true;
+ DEBUGF(infof(data, "schannel: server closed the connection\n"));
+ }
+ else if(nread > 0) {
+ BACKEND->encdata_offset += (size_t)nread;
+ BACKEND->encdata_is_incomplete = false;
+ DEBUGF(infof(data, "schannel: encrypted data got %zd\n", nread));
+ }
+ }
+
+ DEBUGF(infof(data,
+ "schannel: encrypted data buffer: offset %zu length %zu\n",
+ 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\n",
+ 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 == NULL) {
+ *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\n", size));
+ DEBUGF(infof(data,
+ "schannel: decrypted cached: offset %zu length %zu\n",
+ 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\n",
+ 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\n",
+ 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\n");
+ if(*err && *err != CURLE_AGAIN) {
+ infof(data, "schannel: can't renogotiate, an error is pending\n");
+ goto cleanup;
+ }
+ if(BACKEND->encdata_offset) {
+ *err = CURLE_RECV_ERROR;
+ infof(data, "schannel: can't renogotiate, "
+ "encrypted data available\n");
+ goto cleanup;
+ }
+ /* begin renegotiation */
+ infof(data, "schannel: renegotiating SSL/TLS connection\n");
+ connssl->state = ssl_connection_negotiating;
+ connssl->connecting_state = ssl_connect_2_writing;
+ *err = schannel_connect_common(conn, sockindex, FALSE, &done);
+ if(*err) {
+ infof(data, "schannel: renegotiation failed\n");
+ goto cleanup;
+ }
+ /* now retry receiving data */
+ sspi_status = SEC_E_OK;
+ infof(data, "schannel: SSL/TLS connection renegotiated\n");
+ 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\n");
+ }
+ 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\n");
+ 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\n",
+ Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
+ goto cleanup;
+ }
+ }
+
+ DEBUGF(infof(data,
+ "schannel: encrypted data buffer: offset %zu length %zu\n",
+ BACKEND->encdata_offset, BACKEND->encdata_length));
+
+ DEBUGF(infof(data,
+ "schannel: decrypted data buffer: offset %zu length %zu\n",
+ 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\n"));
+
+ /* 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, 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)\n");
+ }
+ }
+
+ /* 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\n", size));
+ DEBUGF(infof(data,
+ "schannel: decrypted data buffer: offset %zu length %zu\n",
+ 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 Curl_schannel_connect_nonblocking(struct connectdata *conn,
+ int sockindex, bool *done)
+{
+ return schannel_connect_common(conn, sockindex, TRUE, done);
+}
+
+static CURLcode Curl_schannel_connect(struct connectdata *conn, int sockindex)
+{
+ CURLcode result;
+ bool done = FALSE;
+
+ result = schannel_connect_common(conn, sockindex, FALSE, &done);
+ if(result)
+ return result;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+static bool Curl_schannel_data_pending(const struct connectdata *conn,
+ int sockindex)
+{
+ const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+ 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 Curl_schannel_close(struct connectdata *conn, int sockindex)
+{
+ if(conn->ssl[sockindex].use)
+ /* if the SSL/TLS channel hasn't been shut down yet, do that now. */
+ Curl_ssl_shutdown(conn, sockindex);
+}
+
+static void Curl_schannel_session_free(void *ptr)
+{
+ /* this is expected to be called under sessionid lock */
+ struct Curl_schannel_cred *cred = ptr;
+
+ cred->refcount--;
+ if(cred->refcount == 0) {
+ s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
+ Curl_safefree(cred);
+ }
+}
+
+static int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
+{
+ /* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx
+ * Shutting Down an Schannel Connection
+ */
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+#ifndef CURL_DISABLE_PROXY
+ char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
+ conn->host.name;
+#else
+ char * const hostname = conn->host.name;
+#endif
+
+ DEBUGASSERT(data);
+
+ infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n",
+ hostname, conn->remote_port);
+
+ if(BACKEND->cred && BACKEND->ctxt) {
+ SecBufferDesc BuffDesc;
+ SecBuffer Buffer;
+ SECURITY_STATUS sspi_status;
+ SecBuffer outbuf;
+ SecBufferDesc outbuf_desc;
+ CURLcode result;
+ TCHAR *host_name;
+ 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)));
+ }
+
+ host_name = curlx_convert_UTF8_to_tchar(hostname);
+ if(!host_name)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* 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,
+ host_name,
+ BACKEND->req_flags,
+ 0,
+ 0,
+ NULL,
+ 0,
+ &BACKEND->ctxt->ctxt_handle,
+ &outbuf_desc,
+ &BACKEND->ret_flags,
+ &BACKEND->ctxt->time_stamp);
+
+ curlx_unicodefree(host_name);
+
+ 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(conn, 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)\n", curl_easy_strerror(result), written);
+ }
+ }
+ }
+
+ /* free SSPI Schannel API security context handle */
+ if(BACKEND->ctxt) {
+ DEBUGF(infof(data, "schannel: clear security context handle\n"));
+ s_pSecFn->DeleteSecurityContext(&BACKEND->ctxt->ctxt_handle);
+ Curl_safefree(BACKEND->ctxt);
+ }
+
+ /* free SSPI Schannel API credential handle */
+ if(BACKEND->cred) {
+ /*
+ * When this function is called from Curl_schannel_close() the connection
+ * might not have an associated transfer so the check for conn->data is
+ * necessary.
+ */
+ Curl_ssl_sessionid_lock(conn);
+ Curl_schannel_session_free(BACKEND->cred);
+ Curl_ssl_sessionid_unlock(conn);
+ BACKEND->cred = NULL;
+ }
+
+ /* free internal buffer for received encrypted data */
+ if(BACKEND->encdata_buffer != NULL) {
+ 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 != NULL) {
+ Curl_safefree(BACKEND->decdata_buffer);
+ BACKEND->decdata_length = 0;
+ BACKEND->decdata_offset = 0;
+ }
+
+ return CURLE_OK;
+}
+
+static int Curl_schannel_init(void)
+{
+ return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
+}
+
+static void Curl_schannel_cleanup(void)
+{
+ Curl_sspi_global_cleanup();
+}
+
+static size_t Curl_schannel_version(char *buffer, size_t size)
+{
+ size = msnprintf(buffer, size, "Schannel");
+
+ return size;
+}
+
+static CURLcode Curl_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 connectdata *conn, int sockindex,
+ const char *pinnedpubkey)
+{
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ CERT_CONTEXT *pCertContextServer = 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;
+
+ 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 == NULL)) {
+ 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 Curl_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 Curl_schannel_md5sum(unsigned char *input,
+ size_t inputlen,
+ unsigned char *md5sum,
+ size_t md5len)
+{
+ Curl_schannel_checksum(input, inputlen, md5sum, md5len,
+ PROV_RSA_FULL, CALG_MD5);
+ return CURLE_OK;
+}
+
+static CURLcode Curl_schannel_sha256sum(const unsigned char *input,
+ size_t inputlen,
+ unsigned char *sha256sum,
+ size_t sha256len)
+{
+ Curl_schannel_checksum(input, inputlen, sha256sum, sha256len,
+ PROV_RSA_AES, CALG_SHA_256);
+ return CURLE_OK;
+}
+
+static void *Curl_schannel_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ (void)info;
+ return &BACKEND->ctxt->ctxt_handle;
+}
+
+const struct Curl_ssl Curl_ssl_schannel = {
+ { CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */
+
+ SSLSUPP_CERTINFO |
+ SSLSUPP_PINNEDPUBKEY,
+
+ sizeof(struct ssl_backend_data),
+
+ Curl_schannel_init, /* init */
+ Curl_schannel_cleanup, /* cleanup */
+ Curl_schannel_version, /* version */
+ Curl_none_check_cxn, /* check_cxn */
+ Curl_schannel_shutdown, /* shutdown */
+ Curl_schannel_data_pending, /* data_pending */
+ Curl_schannel_random, /* random */
+ Curl_none_cert_status_request, /* cert_status_request */
+ Curl_schannel_connect, /* connect */
+ Curl_schannel_connect_nonblocking, /* connect_nonblocking */
+ Curl_schannel_get_internals, /* get_internals */
+ Curl_schannel_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ Curl_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 */
+ Curl_schannel_md5sum, /* md5sum */
+ Curl_schannel_sha256sum /* sha256sum */
+};
+
+#endif /* USE_SCHANNEL */
diff --git a/contrib/libs/curl/lib/vtls/schannel.h b/contrib/libs/curl/lib/vtls/schannel.h
new file mode 100644
index 0000000000..085b3f455c
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/schannel.h
@@ -0,0 +1,108 @@
+#ifndef HEADER_CURL_SCHANNEL_H
+#define HEADER_CURL_SCHANNEL_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al.
+ * Copyright (C) 2012 - 2020, 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_SCHANNEL
+
+#include <schnlsp.h>
+#include <schannel.h>
+#include "curl_sspi.h"
+
+#include "urldata.h"
+
+/* <wincrypt.h> has been included via the above <schnlsp.h>.
+ * Or in case of ldap.c, it was included via <winldap.h>.
+ * And since <wincrypt.h> has this:
+ * #define X509_NAME ((LPCSTR) 7)
+ *
+ * And in BoringSSL's <openssl/base.h> there is:
+ * typedef struct X509_name_st X509_NAME;
+ * etc.
+ *
+ * this will cause all kinds of C-preprocessing paste errors in
+ * BoringSSL's <openssl/x509.h>: So just undefine those defines here
+ * (and only here).
+ */
+#if defined(HAVE_BORINGSSL) || defined(OPENSSL_IS_BORINGSSL)
+# undef X509_NAME
+# undef X509_CERT_PAIR
+# undef X509_EXTENSIONS
+#endif
+
+extern const struct Curl_ssl Curl_ssl_schannel;
+
+CURLcode Curl_verify_certificate(struct connectdata *conn, int sockindex);
+
+/* structs to expose only in schannel.c and schannel_verify.c */
+#ifdef EXPOSE_SCHANNEL_INTERNAL_STRUCTS
+
+#ifdef __MINGW32__
+#include <_mingw.h>
+#ifdef __MINGW64_VERSION_MAJOR
+#define HAS_MANUAL_VERIFY_API
+#endif
+#else
+#include <wincrypt.h>
+#ifdef CERT_CHAIN_REVOCATION_CHECK_CHAIN
+#define HAS_MANUAL_VERIFY_API
+#endif
+#endif
+
+struct Curl_schannel_cred {
+ CredHandle cred_handle;
+ TimeStamp time_stamp;
+ int refcount;
+};
+
+struct Curl_schannel_ctxt {
+ CtxtHandle ctxt_handle;
+ TimeStamp time_stamp;
+};
+
+struct ssl_backend_data {
+ struct Curl_schannel_cred *cred;
+ struct Curl_schannel_ctxt *ctxt;
+ SecPkgContext_StreamSizes stream_sizes;
+ size_t encdata_length, decdata_length;
+ size_t encdata_offset, decdata_offset;
+ unsigned char *encdata_buffer, *decdata_buffer;
+ /* encdata_is_incomplete: if encdata contains only a partial record that
+ can't be decrypted without another Curl_read_plain (that is, status is
+ SEC_E_INCOMPLETE_MESSAGE) then set this true. after Curl_read_plain writes
+ more bytes into encdata then set this back to false. */
+ bool encdata_is_incomplete;
+ unsigned long req_flags, ret_flags;
+ CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
+ bool recv_sspi_close_notify; /* true if connection closed by close_notify */
+ bool recv_connection_closed; /* true if connection closed, regardless how */
+ bool use_alpn; /* true if ALPN is used for this connection */
+#ifdef HAS_MANUAL_VERIFY_API
+ bool use_manual_cred_validation; /* true if manual cred validation is used */
+#endif
+};
+#endif /* EXPOSE_SCHANNEL_INTERNAL_STRUCTS */
+
+#endif /* USE_SCHANNEL */
+#endif /* HEADER_CURL_SCHANNEL_H */
diff --git a/contrib/libs/curl/lib/vtls/schannel_verify.c b/contrib/libs/curl/lib/vtls/schannel_verify.c
new file mode 100644
index 0000000000..31b3b2f02f
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/schannel_verify.c
@@ -0,0 +1,700 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
+ * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
+ * Copyright (C) 2012 - 2020, 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';
+}
+
+static CURLcode add_certs_to_store(HCERTSTORE trust_store,
+ const char *ca_file,
+ struct connectdata *conn)
+{
+ CURLcode result;
+ struct Curl_easy *data = conn->data;
+ HANDLE ca_file_handle = INVALID_HANDLE_VALUE;
+ LARGE_INTEGER file_size;
+ char *ca_file_buffer = NULL;
+ char *current_ca_file_ptr = NULL;
+ TCHAR *ca_file_tstr = NULL;
+ size_t ca_file_bufsize = 0;
+ DWORD total_bytes_read = 0;
+ bool more_certs = 0;
+ int num_certs = 0;
+ size_t END_CERT_LEN;
+
+ 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;
+ }
+
+ result = CURLE_OK;
+ 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';
+
+ if(result != CURLE_OK) {
+ goto cleanup;
+ }
+
+ END_CERT_LEN = strlen(END_CERT);
+
+ more_certs = 1;
+ current_ca_file_ptr = ca_file_buffer;
+ while(more_certs && *current_ca_file_ptr != '\0') {
+ char *begin_cert_ptr = strstr(current_ca_file_ptr, BEGIN_CERT);
+ if(!begin_cert_ptr || !is_cr_or_lf(begin_cert_ptr[strlen(BEGIN_CERT)])) {
+ more_certs = 0;
+ }
+ else {
+ char *end_cert_ptr = strstr(begin_cert_ptr, END_CERT);
+ if(!end_cert_ptr) {
+ failf(data,
+ "schannel: CA file '%s' is not correctly formatted",
+ ca_file);
+ 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,
+ 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);
+ 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,
+ 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'\n",
+ ca_file);
+ }
+ else {
+ infof(data,
+ "schannel: added %d certificate(s) from CA file '%s'\n",
+ num_certs, ca_file);
+ }
+ }
+
+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, 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 == NULL) {
+ 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;
+ 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 {
+ int match_result;
+
+ match_result = Curl_cert_hostcheck(cert_hostname, conn_hostname);
+ if(match_result == CURL_HOST_MATCH) {
+ infof(data,
+ "schannel: connection hostname (%s) validated "
+ "against certificate name (%s)\n",
+ 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)\n",
+ 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:
+ curlx_unicodefree(cert_hostname_buff);
+
+ return result;
+}
+
+CURLcode Curl_verify_certificate(struct connectdata *conn, int sockindex)
+{
+ SECURITY_STATUS sspi_status;
+ struct Curl_easy *data = conn->data;
+ 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;
+#ifndef CURL_DISABLE_PROXY
+ const char * const conn_hostname = SSL_IS_PROXY() ?
+ conn->http_proxy.host.name :
+ conn->host.name;
+#else
+ const char * const conn_hostname = conn->host.name;
+#endif
+
+ sspi_status =
+ s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+ &pCertContextServer);
+
+ if((sspi_status != SEC_E_OK) || (pCertContextServer == NULL)) {
+ 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) &&
+ 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, 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 {
+ result = add_certs_to_store(trust_store, SSL_CONN_CONFIG(CAfile),
+ conn);
+ }
+ }
+
+ 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,
+ (data->set.ssl.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(conn->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 */
diff --git a/contrib/libs/curl/lib/vtls/sectransp.c b/contrib/libs/curl/lib/vtls/sectransp.c
new file mode 100644
index 0000000000..8ef60cb1f3
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/sectransp.c
@@ -0,0 +1,3326 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2017, Nick Zitzmann, <nickzman@gmail.com>.
+ * Copyright (C) 2012 - 2020, 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 iOS and macOS SecureTransport-specific code for the
+ * TLS/SSL layer. No code but vtls.c should ever call or use these functions.
+ */
+
+#include "curl_setup.h"
+
+#include "urldata.h" /* for the Curl_easy definition */
+#include "curl_base64.h"
+#include "strtok.h"
+#include "multiif.h"
+
+#ifdef USE_SECTRANSP
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wtautological-pointer-compare"
+#endif /* __clang__ */
+
+#include <limits.h>
+
+#include <Security/Security.h>
+/* For some reason, when building for iOS, the omnibus header above does
+ * not include SecureTransport.h as of iOS SDK 5.1. */
+#include <Security/SecureTransport.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <CommonCrypto/CommonDigest.h>
+
+/* The Security framework has changed greatly between iOS and different macOS
+ versions, and we will try to support as many of them as we can (back to
+ Leopard and iOS 5) by using macros and weak-linking.
+
+ In general, you want to build this using the most recent OS SDK, since some
+ features require curl to be built against the latest SDK. TLS 1.1 and 1.2
+ support, for instance, require the macOS 10.8 SDK or later. TLS 1.3
+ requires the macOS 10.13 or iOS 11 SDK or later. */
+#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE))
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050
+#error "The Secure Transport back-end requires Leopard or later."
+#endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1050 */
+
+#define CURL_BUILD_IOS 0
+#define CURL_BUILD_IOS_7 0
+#define CURL_BUILD_IOS_9 0
+#define CURL_BUILD_IOS_11 0
+#define CURL_BUILD_MAC 1
+/* This is the maximum API level we are allowed to use when building: */
+#define CURL_BUILD_MAC_10_5 MAC_OS_X_VERSION_MAX_ALLOWED >= 1050
+#define CURL_BUILD_MAC_10_6 MAC_OS_X_VERSION_MAX_ALLOWED >= 1060
+#define CURL_BUILD_MAC_10_7 MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
+#define CURL_BUILD_MAC_10_8 MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
+#define CURL_BUILD_MAC_10_9 MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
+#define CURL_BUILD_MAC_10_11 MAC_OS_X_VERSION_MAX_ALLOWED >= 101100
+#define CURL_BUILD_MAC_10_13 MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
+/* These macros mean "the following code is present to allow runtime backward
+ compatibility with at least this cat or earlier":
+ (You set this at build-time using the compiler command line option
+ "-mmacosx-version-min.") */
+#define CURL_SUPPORT_MAC_10_5 MAC_OS_X_VERSION_MIN_REQUIRED <= 1050
+#define CURL_SUPPORT_MAC_10_6 MAC_OS_X_VERSION_MIN_REQUIRED <= 1060
+#define CURL_SUPPORT_MAC_10_7 MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
+#define CURL_SUPPORT_MAC_10_8 MAC_OS_X_VERSION_MIN_REQUIRED <= 1080
+#define CURL_SUPPORT_MAC_10_9 MAC_OS_X_VERSION_MIN_REQUIRED <= 1090
+
+#elif TARGET_OS_EMBEDDED || TARGET_OS_IPHONE
+#define CURL_BUILD_IOS 1
+#define CURL_BUILD_IOS_7 __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
+#define CURL_BUILD_IOS_9 __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000
+#define CURL_BUILD_IOS_11 __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000
+#define CURL_BUILD_MAC 0
+#define CURL_BUILD_MAC_10_5 0
+#define CURL_BUILD_MAC_10_6 0
+#define CURL_BUILD_MAC_10_7 0
+#define CURL_BUILD_MAC_10_8 0
+#define CURL_BUILD_MAC_10_9 0
+#define CURL_BUILD_MAC_10_11 0
+#define CURL_BUILD_MAC_10_13 0
+#define CURL_SUPPORT_MAC_10_5 0
+#define CURL_SUPPORT_MAC_10_6 0
+#define CURL_SUPPORT_MAC_10_7 0
+#define CURL_SUPPORT_MAC_10_8 0
+#define CURL_SUPPORT_MAC_10_9 0
+
+#else
+#error "The Secure Transport back-end requires iOS or macOS."
+#endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */
+
+#if CURL_BUILD_MAC
+#include <sys/sysctl.h>
+#endif /* CURL_BUILD_MAC */
+
+#include "urldata.h"
+#include "sendf.h"
+#include "inet_pton.h"
+#include "connect.h"
+#include "select.h"
+#include "vtls.h"
+#include "sectransp.h"
+#include "curl_printf.h"
+#include "strdup.h"
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* From MacTypes.h (which we can't include because it isn't present in iOS: */
+#define ioErr -36
+#define paramErr -50
+
+struct ssl_backend_data {
+ SSLContextRef ssl_ctx;
+ curl_socket_t ssl_sockfd;
+ bool ssl_direction; /* true if writing, false if reading */
+ size_t ssl_write_buffered_length;
+};
+
+/* pinned public key support tests */
+
+/* version 1 supports macOS 10.12+ and iOS 10+ */
+#if ((TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || \
+ (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200))
+#define SECTRANSP_PINNEDPUBKEY_V1 1
+#endif
+
+/* version 2 supports MacOSX 10.7+ */
+#if (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
+#define SECTRANSP_PINNEDPUBKEY_V2 1
+#endif
+
+#if defined(SECTRANSP_PINNEDPUBKEY_V1) || defined(SECTRANSP_PINNEDPUBKEY_V2)
+/* this backend supports CURLOPT_PINNEDPUBLICKEY */
+#define SECTRANSP_PINNEDPUBKEY 1
+#endif /* SECTRANSP_PINNEDPUBKEY */
+
+#ifdef SECTRANSP_PINNEDPUBKEY
+/* both new and old APIs return rsa keys missing the spki header (not DER) */
+static const unsigned char rsa4096SpkiHeader[] = {
+ 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00};
+
+static const unsigned char rsa2048SpkiHeader[] = {
+ 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d,
+ 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+ 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00};
+#ifdef SECTRANSP_PINNEDPUBKEY_V1
+/* the *new* version doesn't return DER encoded ecdsa certs like the old... */
+static const unsigned char ecDsaSecp256r1SpkiHeader[] = {
+ 0x30, 0x59, 0x30, 0x13, 0x06, 0x07,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
+ 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48,
+ 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
+ 0x42, 0x00};
+
+static const unsigned char ecDsaSecp384r1SpkiHeader[] = {
+ 0x30, 0x76, 0x30, 0x10, 0x06, 0x07,
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
+ 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04,
+ 0x00, 0x22, 0x03, 0x62, 0x00};
+#endif /* SECTRANSP_PINNEDPUBKEY_V1 */
+#endif /* SECTRANSP_PINNEDPUBKEY */
+
+/* The following two functions were ripped from Apple sample code,
+ * with some modifications: */
+static OSStatus SocketRead(SSLConnectionRef connection,
+ void *data, /* owned by
+ * caller, data
+ * RETURNED */
+ size_t *dataLength) /* IN/OUT */
+{
+ size_t bytesToGo = *dataLength;
+ size_t initLen = bytesToGo;
+ UInt8 *currData = (UInt8 *)data;
+ /*int sock = *(int *)connection;*/
+ struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection;
+ struct ssl_backend_data *backend = connssl->backend;
+ int sock = backend->ssl_sockfd;
+ OSStatus rtn = noErr;
+ size_t bytesRead;
+ ssize_t rrtn;
+ int theErr;
+
+ *dataLength = 0;
+
+ for(;;) {
+ bytesRead = 0;
+ rrtn = read(sock, currData, bytesToGo);
+ if(rrtn <= 0) {
+ /* this is guesswork... */
+ theErr = errno;
+ if(rrtn == 0) { /* EOF = server hung up */
+ /* the framework will turn this into errSSLClosedNoNotify */
+ rtn = errSSLClosedGraceful;
+ }
+ else /* do the switch */
+ switch(theErr) {
+ case ENOENT:
+ /* connection closed */
+ rtn = errSSLClosedGraceful;
+ break;
+ case ECONNRESET:
+ rtn = errSSLClosedAbort;
+ break;
+ case EAGAIN:
+ rtn = errSSLWouldBlock;
+ backend->ssl_direction = false;
+ break;
+ default:
+ rtn = ioErr;
+ break;
+ }
+ break;
+ }
+ else {
+ bytesRead = rrtn;
+ }
+ bytesToGo -= bytesRead;
+ currData += bytesRead;
+
+ if(bytesToGo == 0) {
+ /* filled buffer with incoming data, done */
+ break;
+ }
+ }
+ *dataLength = initLen - bytesToGo;
+
+ return rtn;
+}
+
+static OSStatus SocketWrite(SSLConnectionRef connection,
+ const void *data,
+ size_t *dataLength) /* IN/OUT */
+{
+ size_t bytesSent = 0;
+ /*int sock = *(int *)connection;*/
+ struct ssl_connect_data *connssl = (struct ssl_connect_data *)connection;
+ struct ssl_backend_data *backend = connssl->backend;
+ int sock = backend->ssl_sockfd;
+ ssize_t length;
+ size_t dataLen = *dataLength;
+ const UInt8 *dataPtr = (UInt8 *)data;
+ OSStatus ortn;
+ int theErr;
+
+ *dataLength = 0;
+
+ do {
+ length = write(sock,
+ (char *)dataPtr + bytesSent,
+ dataLen - bytesSent);
+ } while((length > 0) &&
+ ( (bytesSent += length) < dataLen) );
+
+ if(length <= 0) {
+ theErr = errno;
+ if(theErr == EAGAIN) {
+ ortn = errSSLWouldBlock;
+ backend->ssl_direction = true;
+ }
+ else {
+ ortn = ioErr;
+ }
+ }
+ else {
+ ortn = noErr;
+ }
+ *dataLength = bytesSent;
+ return ortn;
+}
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+CF_INLINE const char *SSLCipherNameForNumber(SSLCipherSuite cipher)
+{
+ switch(cipher) {
+ /* SSL version 3.0 */
+ case SSL_RSA_WITH_NULL_MD5:
+ return "SSL_RSA_WITH_NULL_MD5";
+ break;
+ case SSL_RSA_WITH_NULL_SHA:
+ return "SSL_RSA_WITH_NULL_SHA";
+ break;
+ case SSL_RSA_EXPORT_WITH_RC4_40_MD5:
+ return "SSL_RSA_EXPORT_WITH_RC4_40_MD5";
+ break;
+ case SSL_RSA_WITH_RC4_128_MD5:
+ return "SSL_RSA_WITH_RC4_128_MD5";
+ break;
+ case SSL_RSA_WITH_RC4_128_SHA:
+ return "SSL_RSA_WITH_RC4_128_SHA";
+ break;
+ case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5:
+ return "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5";
+ break;
+ case SSL_RSA_WITH_IDEA_CBC_SHA:
+ return "SSL_RSA_WITH_IDEA_CBC_SHA";
+ break;
+ case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA:
+ return "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA";
+ break;
+ case SSL_RSA_WITH_DES_CBC_SHA:
+ return "SSL_RSA_WITH_DES_CBC_SHA";
+ break;
+ case SSL_RSA_WITH_3DES_EDE_CBC_SHA:
+ return "SSL_RSA_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA:
+ return "SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA";
+ break;
+ case SSL_DH_DSS_WITH_DES_CBC_SHA:
+ return "SSL_DH_DSS_WITH_DES_CBC_SHA";
+ break;
+ case SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ return "SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA:
+ return "SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA";
+ break;
+ case SSL_DH_RSA_WITH_DES_CBC_SHA:
+ return "SSL_DH_RSA_WITH_DES_CBC_SHA";
+ break;
+ case SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ return "SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA:
+ return "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA";
+ break;
+ case SSL_DHE_DSS_WITH_DES_CBC_SHA:
+ return "SSL_DHE_DSS_WITH_DES_CBC_SHA";
+ break;
+ case SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ return "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA:
+ return "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA";
+ break;
+ case SSL_DHE_RSA_WITH_DES_CBC_SHA:
+ return "SSL_DHE_RSA_WITH_DES_CBC_SHA";
+ break;
+ case SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ return "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5:
+ return "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5";
+ break;
+ case SSL_DH_anon_WITH_RC4_128_MD5:
+ return "SSL_DH_anon_WITH_RC4_128_MD5";
+ break;
+ case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA:
+ return "SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA";
+ break;
+ case SSL_DH_anon_WITH_DES_CBC_SHA:
+ return "SSL_DH_anon_WITH_DES_CBC_SHA";
+ break;
+ case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA:
+ return "SSL_DH_anon_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case SSL_FORTEZZA_DMS_WITH_NULL_SHA:
+ return "SSL_FORTEZZA_DMS_WITH_NULL_SHA";
+ break;
+ case SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA:
+ return "SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA";
+ break;
+ /* TLS 1.0 with AES (RFC 3268)
+ (Apparently these are used in SSLv3 implementations as well.) */
+ case TLS_RSA_WITH_AES_128_CBC_SHA:
+ return "TLS_RSA_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ return "TLS_DH_DSS_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ return "TLS_DH_RSA_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ return "TLS_DHE_DSS_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ return "TLS_DHE_RSA_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+ return "TLS_DH_anon_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA:
+ return "TLS_RSA_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ return "TLS_DH_DSS_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ return "TLS_DH_RSA_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ return "TLS_DHE_DSS_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ return "TLS_DHE_RSA_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+ return "TLS_DH_anon_WITH_AES_256_CBC_SHA";
+ break;
+ /* SSL version 2.0 */
+ case SSL_RSA_WITH_RC2_CBC_MD5:
+ return "SSL_RSA_WITH_RC2_CBC_MD5";
+ break;
+ case SSL_RSA_WITH_IDEA_CBC_MD5:
+ return "SSL_RSA_WITH_IDEA_CBC_MD5";
+ break;
+ case SSL_RSA_WITH_DES_CBC_MD5:
+ return "SSL_RSA_WITH_DES_CBC_MD5";
+ break;
+ case SSL_RSA_WITH_3DES_EDE_CBC_MD5:
+ return "SSL_RSA_WITH_3DES_EDE_CBC_MD5";
+ break;
+ }
+ return "SSL_NULL_WITH_NULL_NULL";
+}
+
+CF_INLINE const char *TLSCipherNameForNumber(SSLCipherSuite cipher)
+{
+ switch(cipher) {
+ /* TLS 1.0 with AES (RFC 3268) */
+ case TLS_RSA_WITH_AES_128_CBC_SHA:
+ return "TLS_RSA_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+ return "TLS_DH_DSS_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+ return "TLS_DH_RSA_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+ return "TLS_DHE_DSS_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ return "TLS_DHE_RSA_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+ return "TLS_DH_anon_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA:
+ return "TLS_RSA_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+ return "TLS_DH_DSS_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+ return "TLS_DH_RSA_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+ return "TLS_DHE_DSS_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ return "TLS_DHE_RSA_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+ return "TLS_DH_anon_WITH_AES_256_CBC_SHA";
+ break;
+#if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS
+ /* TLS 1.0 with ECDSA (RFC 4492) */
+ case TLS_ECDH_ECDSA_WITH_NULL_SHA:
+ return "TLS_ECDH_ECDSA_WITH_NULL_SHA";
+ break;
+ case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+ return "TLS_ECDH_ECDSA_WITH_RC4_128_SHA";
+ break;
+ case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+ return "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+ return "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+ return "TLS_ECDHE_ECDSA_WITH_NULL_SHA";
+ break;
+ case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+ return "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA";
+ break;
+ case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+ return "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+ return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_ECDH_RSA_WITH_NULL_SHA:
+ return "TLS_ECDH_RSA_WITH_NULL_SHA";
+ break;
+ case TLS_ECDH_RSA_WITH_RC4_128_SHA:
+ return "TLS_ECDH_RSA_WITH_RC4_128_SHA";
+ break;
+ case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+ return "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+ return "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_ECDHE_RSA_WITH_NULL_SHA:
+ return "TLS_ECDHE_RSA_WITH_NULL_SHA";
+ break;
+ case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+ return "TLS_ECDHE_RSA_WITH_RC4_128_SHA";
+ break;
+ case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ return "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_ECDH_anon_WITH_NULL_SHA:
+ return "TLS_ECDH_anon_WITH_NULL_SHA";
+ break;
+ case TLS_ECDH_anon_WITH_RC4_128_SHA:
+ return "TLS_ECDH_anon_WITH_RC4_128_SHA";
+ break;
+ case TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
+ return "TLS_ECDH_anon_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
+ return "TLS_ECDH_anon_WITH_AES_256_CBC_SHA";
+ break;
+#endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+ /* TLS 1.2 (RFC 5246) */
+ case TLS_RSA_WITH_NULL_MD5:
+ return "TLS_RSA_WITH_NULL_MD5";
+ break;
+ case TLS_RSA_WITH_NULL_SHA:
+ return "TLS_RSA_WITH_NULL_SHA";
+ break;
+ case TLS_RSA_WITH_RC4_128_MD5:
+ return "TLS_RSA_WITH_RC4_128_MD5";
+ break;
+ case TLS_RSA_WITH_RC4_128_SHA:
+ return "TLS_RSA_WITH_RC4_128_SHA";
+ break;
+ case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_RSA_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case TLS_RSA_WITH_NULL_SHA256:
+ return "TLS_RSA_WITH_NULL_SHA256";
+ break;
+ case TLS_RSA_WITH_AES_128_CBC_SHA256:
+ return "TLS_RSA_WITH_AES_128_CBC_SHA256";
+ break;
+ case TLS_RSA_WITH_AES_256_CBC_SHA256:
+ return "TLS_RSA_WITH_AES_256_CBC_SHA256";
+ break;
+ case TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+ return "TLS_DH_DSS_WITH_AES_128_CBC_SHA256";
+ break;
+ case TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+ return "TLS_DH_RSA_WITH_AES_128_CBC_SHA256";
+ break;
+ case TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+ return "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256";
+ break;
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ return "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256";
+ break;
+ case TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+ return "TLS_DH_DSS_WITH_AES_256_CBC_SHA256";
+ break;
+ case TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+ return "TLS_DH_RSA_WITH_AES_256_CBC_SHA256";
+ break;
+ case TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+ return "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256";
+ break;
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+ return "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256";
+ break;
+ case TLS_DH_anon_WITH_RC4_128_MD5:
+ return "TLS_DH_anon_WITH_RC4_128_MD5";
+ break;
+ case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+ return "TLS_DH_anon_WITH_AES_128_CBC_SHA256";
+ break;
+ case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+ return "TLS_DH_anon_WITH_AES_256_CBC_SHA256";
+ break;
+ /* TLS 1.2 with AES GCM (RFC 5288) */
+ case TLS_RSA_WITH_AES_128_GCM_SHA256:
+ return "TLS_RSA_WITH_AES_128_GCM_SHA256";
+ break;
+ case TLS_RSA_WITH_AES_256_GCM_SHA384:
+ return "TLS_RSA_WITH_AES_256_GCM_SHA384";
+ break;
+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ return "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256";
+ break;
+ case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+ return "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384";
+ break;
+ case TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+ return "TLS_DH_RSA_WITH_AES_128_GCM_SHA256";
+ break;
+ case TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+ return "TLS_DH_RSA_WITH_AES_256_GCM_SHA384";
+ break;
+ case TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+ return "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256";
+ break;
+ case TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+ return "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384";
+ break;
+ case TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+ return "TLS_DH_DSS_WITH_AES_128_GCM_SHA256";
+ break;
+ case TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+ return "TLS_DH_DSS_WITH_AES_256_GCM_SHA384";
+ break;
+ case TLS_DH_anon_WITH_AES_128_GCM_SHA256:
+ return "TLS_DH_anon_WITH_AES_128_GCM_SHA256";
+ break;
+ case TLS_DH_anon_WITH_AES_256_GCM_SHA384:
+ return "TLS_DH_anon_WITH_AES_256_GCM_SHA384";
+ break;
+ /* TLS 1.2 with elliptic curve ciphers (RFC 5289) */
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ return "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256";
+ break;
+ case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+ return "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384";
+ break;
+ case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+ return "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256";
+ break;
+ case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+ return "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384";
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ return "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256";
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+ return "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384";
+ break;
+ case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+ return "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256";
+ break;
+ case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+ return "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384";
+ break;
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ return "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256";
+ break;
+ case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+ return "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384";
+ break;
+ case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+ return "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256";
+ break;
+ case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+ return "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384";
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+ return "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256";
+ break;
+ case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+ return "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384";
+ break;
+ case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+ return "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256";
+ break;
+ case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+ return "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384";
+ break;
+ case TLS_EMPTY_RENEGOTIATION_INFO_SCSV:
+ return "TLS_EMPTY_RENEGOTIATION_INFO_SCSV";
+ break;
+#else
+ case SSL_RSA_WITH_NULL_MD5:
+ return "TLS_RSA_WITH_NULL_MD5";
+ break;
+ case SSL_RSA_WITH_NULL_SHA:
+ return "TLS_RSA_WITH_NULL_SHA";
+ break;
+ case SSL_RSA_WITH_RC4_128_MD5:
+ return "TLS_RSA_WITH_RC4_128_MD5";
+ break;
+ case SSL_RSA_WITH_RC4_128_SHA:
+ return "TLS_RSA_WITH_RC4_128_SHA";
+ break;
+ case SSL_RSA_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_RSA_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case SSL_DH_anon_WITH_RC4_128_MD5:
+ return "TLS_DH_anon_WITH_RC4_128_MD5";
+ break;
+ case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA";
+ break;
+#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
+#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7
+ /* TLS PSK (RFC 4279): */
+ case TLS_PSK_WITH_RC4_128_SHA:
+ return "TLS_PSK_WITH_RC4_128_SHA";
+ break;
+ case TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_PSK_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case TLS_PSK_WITH_AES_128_CBC_SHA:
+ return "TLS_PSK_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_PSK_WITH_AES_256_CBC_SHA:
+ return "TLS_PSK_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_DHE_PSK_WITH_RC4_128_SHA:
+ return "TLS_DHE_PSK_WITH_RC4_128_SHA";
+ break;
+ case TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+ return "TLS_DHE_PSK_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+ return "TLS_DHE_PSK_WITH_AES_256_CBC_SHA";
+ break;
+ case TLS_RSA_PSK_WITH_RC4_128_SHA:
+ return "TLS_RSA_PSK_WITH_RC4_128_SHA";
+ break;
+ case TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+ return "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA";
+ break;
+ case TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+ return "TLS_RSA_PSK_WITH_AES_128_CBC_SHA";
+ break;
+ case TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+ return "TLS_RSA_PSK_WITH_AES_256_CBC_SHA";
+ break;
+ /* More TLS PSK (RFC 4785): */
+ case TLS_PSK_WITH_NULL_SHA:
+ return "TLS_PSK_WITH_NULL_SHA";
+ break;
+ case TLS_DHE_PSK_WITH_NULL_SHA:
+ return "TLS_DHE_PSK_WITH_NULL_SHA";
+ break;
+ case TLS_RSA_PSK_WITH_NULL_SHA:
+ return "TLS_RSA_PSK_WITH_NULL_SHA";
+ break;
+ /* Even more TLS PSK (RFC 5487): */
+ case TLS_PSK_WITH_AES_128_GCM_SHA256:
+ return "TLS_PSK_WITH_AES_128_GCM_SHA256";
+ break;
+ case TLS_PSK_WITH_AES_256_GCM_SHA384:
+ return "TLS_PSK_WITH_AES_256_GCM_SHA384";
+ break;
+ case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+ return "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256";
+ break;
+ case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+ return "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384";
+ break;
+ case TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+ return "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256";
+ break;
+ case TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+ return "TLS_PSK_WITH_AES_256_GCM_SHA384";
+ break;
+ case TLS_PSK_WITH_AES_128_CBC_SHA256:
+ return "TLS_PSK_WITH_AES_128_CBC_SHA256";
+ break;
+ case TLS_PSK_WITH_AES_256_CBC_SHA384:
+ return "TLS_PSK_WITH_AES_256_CBC_SHA384";
+ break;
+ case TLS_PSK_WITH_NULL_SHA256:
+ return "TLS_PSK_WITH_NULL_SHA256";
+ break;
+ case TLS_PSK_WITH_NULL_SHA384:
+ return "TLS_PSK_WITH_NULL_SHA384";
+ break;
+ case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+ return "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256";
+ break;
+ case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+ return "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384";
+ break;
+ case TLS_DHE_PSK_WITH_NULL_SHA256:
+ return "TLS_DHE_PSK_WITH_NULL_SHA256";
+ break;
+ case TLS_DHE_PSK_WITH_NULL_SHA384:
+ return "TLS_RSA_PSK_WITH_NULL_SHA384";
+ break;
+ case TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+ return "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256";
+ break;
+ case TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+ return "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384";
+ break;
+ case TLS_RSA_PSK_WITH_NULL_SHA256:
+ return "TLS_RSA_PSK_WITH_NULL_SHA256";
+ break;
+ case TLS_RSA_PSK_WITH_NULL_SHA384:
+ return "TLS_RSA_PSK_WITH_NULL_SHA384";
+ break;
+#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */
+#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11
+ /* New ChaCha20+Poly1305 cipher-suites used by TLS 1.3: */
+ case TLS_AES_128_GCM_SHA256:
+ return "TLS_AES_128_GCM_SHA256";
+ break;
+ case TLS_AES_256_GCM_SHA384:
+ return "TLS_AES_256_GCM_SHA384";
+ break;
+ case TLS_CHACHA20_POLY1305_SHA256:
+ return "TLS_CHACHA20_POLY1305_SHA256";
+ break;
+ case TLS_AES_128_CCM_SHA256:
+ return "TLS_AES_128_CCM_SHA256";
+ break;
+ case TLS_AES_128_CCM_8_SHA256:
+ return "TLS_AES_128_CCM_8_SHA256";
+ break;
+ case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+ return "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256";
+ break;
+ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+ return "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256";
+ break;
+#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */
+ }
+ return "TLS_NULL_WITH_NULL_NULL";
+}
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
+
+#if CURL_BUILD_MAC
+CF_INLINE void GetDarwinVersionNumber(int *major, int *minor)
+{
+ int mib[2];
+ char *os_version;
+ size_t os_version_len;
+ char *os_version_major, *os_version_minor;
+ char *tok_buf;
+
+ /* Get the Darwin kernel version from the kernel using sysctl(): */
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_OSRELEASE;
+ if(sysctl(mib, 2, NULL, &os_version_len, NULL, 0) == -1)
+ return;
+ os_version = malloc(os_version_len*sizeof(char));
+ if(!os_version)
+ return;
+ if(sysctl(mib, 2, os_version, &os_version_len, NULL, 0) == -1) {
+ free(os_version);
+ return;
+ }
+
+ /* Parse the version: */
+ os_version_major = strtok_r(os_version, ".", &tok_buf);
+ os_version_minor = strtok_r(NULL, ".", &tok_buf);
+ *major = atoi(os_version_major);
+ *minor = atoi(os_version_minor);
+ free(os_version);
+}
+#endif /* CURL_BUILD_MAC */
+
+/* Apple provides a myriad of ways of getting information about a certificate
+ into a string. Some aren't available under iOS or newer cats. So here's
+ a unified function for getting a string describing the certificate that
+ ought to work in all cats starting with Leopard. */
+CF_INLINE CFStringRef getsubject(SecCertificateRef cert)
+{
+ CFStringRef server_cert_summary = CFSTR("(null)");
+
+#if CURL_BUILD_IOS
+ /* iOS: There's only one way to do this. */
+ server_cert_summary = SecCertificateCopySubjectSummary(cert);
+#else
+#if CURL_BUILD_MAC_10_7
+ /* Lion & later: Get the long description if we can. */
+ if(SecCertificateCopyLongDescription != NULL)
+ server_cert_summary =
+ SecCertificateCopyLongDescription(NULL, cert, NULL);
+ else
+#endif /* CURL_BUILD_MAC_10_7 */
+#if CURL_BUILD_MAC_10_6
+ /* Snow Leopard: Get the certificate summary. */
+ if(SecCertificateCopySubjectSummary != NULL)
+ server_cert_summary = SecCertificateCopySubjectSummary(cert);
+ else
+#endif /* CURL_BUILD_MAC_10_6 */
+ /* Leopard is as far back as we go... */
+ (void)SecCertificateCopyCommonName(cert, &server_cert_summary);
+#endif /* CURL_BUILD_IOS */
+ return server_cert_summary;
+}
+
+static CURLcode CopyCertSubject(struct Curl_easy *data,
+ SecCertificateRef cert, char **certp)
+{
+ CFStringRef c = getsubject(cert);
+ CURLcode result = CURLE_OK;
+ const char *direct;
+ char *cbuf = NULL;
+ *certp = NULL;
+
+ if(!c) {
+ failf(data, "SSL: invalid CA certificate subject");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ /* If the subject is already available as UTF-8 encoded (ie 'direct') then
+ use that, else convert it. */
+ direct = CFStringGetCStringPtr(c, kCFStringEncodingUTF8);
+ if(direct) {
+ *certp = strdup(direct);
+ if(!*certp) {
+ failf(data, "SSL: out of memory");
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else {
+ size_t cbuf_size = ((size_t)CFStringGetLength(c) * 4) + 1;
+ cbuf = calloc(cbuf_size, 1);
+ if(cbuf) {
+ if(!CFStringGetCString(c, cbuf, cbuf_size,
+ kCFStringEncodingUTF8)) {
+ failf(data, "SSL: invalid CA certificate subject");
+ result = CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else
+ /* pass back the buffer */
+ *certp = cbuf;
+ }
+ else {
+ failf(data, "SSL: couldn't allocate %zu bytes of memory", cbuf_size);
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ }
+ if(result)
+ free(cbuf);
+ CFRelease(c);
+ return result;
+}
+
+#if CURL_SUPPORT_MAC_10_6
+/* The SecKeychainSearch API was deprecated in Lion, and using it will raise
+ deprecation warnings, so let's not compile this unless it's necessary: */
+static OSStatus CopyIdentityWithLabelOldSchool(char *label,
+ SecIdentityRef *out_c_a_k)
+{
+ OSStatus status = errSecItemNotFound;
+ SecKeychainAttributeList attr_list;
+ SecKeychainAttribute attr;
+ SecKeychainSearchRef search = NULL;
+ SecCertificateRef cert = NULL;
+
+ /* Set up the attribute list: */
+ attr_list.count = 1L;
+ attr_list.attr = &attr;
+
+ /* Set up our lone search criterion: */
+ attr.tag = kSecLabelItemAttr;
+ attr.data = label;
+ attr.length = (UInt32)strlen(label);
+
+ /* Start searching: */
+ status = SecKeychainSearchCreateFromAttributes(NULL,
+ kSecCertificateItemClass,
+ &attr_list,
+ &search);
+ if(status == noErr) {
+ status = SecKeychainSearchCopyNext(search,
+ (SecKeychainItemRef *)&cert);
+ if(status == noErr && cert) {
+ /* If we found a certificate, does it have a private key? */
+ status = SecIdentityCreateWithCertificate(NULL, cert, out_c_a_k);
+ CFRelease(cert);
+ }
+ }
+
+ if(search)
+ CFRelease(search);
+ return status;
+}
+#endif /* CURL_SUPPORT_MAC_10_6 */
+
+static OSStatus CopyIdentityWithLabel(char *label,
+ SecIdentityRef *out_cert_and_key)
+{
+ OSStatus status = errSecItemNotFound;
+
+#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS
+ CFArrayRef keys_list;
+ CFIndex keys_list_count;
+ CFIndex i;
+ CFStringRef common_name;
+
+ /* SecItemCopyMatching() was introduced in iOS and Snow Leopard.
+ kSecClassIdentity was introduced in Lion. If both exist, let's use them
+ to find the certificate. */
+ if(SecItemCopyMatching != NULL && kSecClassIdentity != NULL) {
+ CFTypeRef keys[5];
+ CFTypeRef values[5];
+ CFDictionaryRef query_dict;
+ CFStringRef label_cf = CFStringCreateWithCString(NULL, label,
+ kCFStringEncodingUTF8);
+
+ /* Set up our search criteria and expected results: */
+ values[0] = kSecClassIdentity; /* we want a certificate and a key */
+ keys[0] = kSecClass;
+ values[1] = kCFBooleanTrue; /* we want a reference */
+ keys[1] = kSecReturnRef;
+ values[2] = kSecMatchLimitAll; /* kSecMatchLimitOne would be better if the
+ * label matching below worked correctly */
+ keys[2] = kSecMatchLimit;
+ /* identity searches need a SecPolicyRef in order to work */
+ values[3] = SecPolicyCreateSSL(false, NULL);
+ keys[3] = kSecMatchPolicy;
+ /* match the name of the certificate (doesn't work in macOS 10.12.1) */
+ values[4] = label_cf;
+ keys[4] = kSecAttrLabel;
+ query_dict = CFDictionaryCreate(NULL, (const void **)keys,
+ (const void **)values, 5L,
+ &kCFCopyStringDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks);
+ CFRelease(values[3]);
+
+ /* Do we have a match? */
+ status = SecItemCopyMatching(query_dict, (CFTypeRef *) &keys_list);
+
+ /* Because kSecAttrLabel matching doesn't work with kSecClassIdentity,
+ * we need to find the correct identity ourselves */
+ if(status == noErr) {
+ keys_list_count = CFArrayGetCount(keys_list);
+ *out_cert_and_key = NULL;
+ status = 1;
+ for(i = 0; i<keys_list_count; i++) {
+ OSStatus err = noErr;
+ SecCertificateRef cert = NULL;
+ SecIdentityRef identity =
+ (SecIdentityRef) CFArrayGetValueAtIndex(keys_list, i);
+ err = SecIdentityCopyCertificate(identity, &cert);
+ if(err == noErr) {
+#if CURL_BUILD_IOS
+ common_name = SecCertificateCopySubjectSummary(cert);
+#elif CURL_BUILD_MAC_10_7
+ SecCertificateCopyCommonName(cert, &common_name);
+#endif
+ if(CFStringCompare(common_name, label_cf, 0) == kCFCompareEqualTo) {
+ CFRelease(cert);
+ CFRelease(common_name);
+ CFRetain(identity);
+ *out_cert_and_key = identity;
+ status = noErr;
+ break;
+ }
+ CFRelease(common_name);
+ }
+ CFRelease(cert);
+ }
+ }
+
+ if(keys_list)
+ CFRelease(keys_list);
+ CFRelease(query_dict);
+ CFRelease(label_cf);
+ }
+ else {
+#if CURL_SUPPORT_MAC_10_6
+ /* On Leopard and Snow Leopard, fall back to SecKeychainSearch. */
+ status = CopyIdentityWithLabelOldSchool(label, out_cert_and_key);
+#endif /* CURL_SUPPORT_MAC_10_6 */
+ }
+#elif CURL_SUPPORT_MAC_10_6
+ /* For developers building on older cats, we have no choice but to fall back
+ to SecKeychainSearch. */
+ status = CopyIdentityWithLabelOldSchool(label, out_cert_and_key);
+#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */
+ return status;
+}
+
+static OSStatus CopyIdentityFromPKCS12File(const char *cPath,
+ const struct curl_blob *blob,
+ const char *cPassword,
+ SecIdentityRef *out_cert_and_key)
+{
+ OSStatus status = errSecItemNotFound;
+ CFURLRef pkcs_url = NULL;
+ CFStringRef password = cPassword ? CFStringCreateWithCString(NULL,
+ cPassword, kCFStringEncodingUTF8) : NULL;
+ CFDataRef pkcs_data = NULL;
+
+ /* We can import P12 files on iOS or OS X 10.7 or later: */
+ /* These constants are documented as having first appeared in 10.6 but they
+ raise linker errors when used on that cat for some reason. */
+#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS
+ bool resource_imported;
+
+ if(blob) {
+ pkcs_data = CFDataCreate(kCFAllocatorDefault,
+ (const unsigned char *)blob->data, blob->len);
+ status = (pkcs_data != NULL) ? errSecSuccess : errSecAllocate;
+ resource_imported = (pkcs_data != NULL);
+ }
+ else {
+ pkcs_url =
+ CFURLCreateFromFileSystemRepresentation(NULL,
+ (const UInt8 *)cPath,
+ strlen(cPath), false);
+ resource_imported =
+ CFURLCreateDataAndPropertiesFromResource(NULL,
+ pkcs_url, &pkcs_data,
+ NULL, NULL, &status);
+ }
+
+ if(resource_imported) {
+ CFArrayRef items = NULL;
+
+ /* On iOS SecPKCS12Import will never add the client certificate to the
+ * Keychain.
+ *
+ * It gives us back a SecIdentityRef that we can use directly. */
+#if CURL_BUILD_IOS
+ const void *cKeys[] = {kSecImportExportPassphrase};
+ const void *cValues[] = {password};
+ CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues,
+ password ? 1L : 0L, NULL, NULL);
+
+ if(options != NULL) {
+ status = SecPKCS12Import(pkcs_data, options, &items);
+ CFRelease(options);
+ }
+
+
+ /* On macOS SecPKCS12Import will always add the client certificate to
+ * the Keychain.
+ *
+ * As this doesn't match iOS, and apps may not want to see their client
+ * certificate saved in the user's keychain, we use SecItemImport
+ * with a NULL keychain to avoid importing it.
+ *
+ * This returns a SecCertificateRef from which we can construct a
+ * SecIdentityRef.
+ */
+#elif CURL_BUILD_MAC_10_7
+ SecItemImportExportKeyParameters keyParams;
+ SecExternalFormat inputFormat = kSecFormatPKCS12;
+ SecExternalItemType inputType = kSecItemTypeCertificate;
+
+ memset(&keyParams, 0x00, sizeof(keyParams));
+ keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+ keyParams.passphrase = password;
+
+ status = SecItemImport(pkcs_data, NULL, &inputFormat, &inputType,
+ 0, &keyParams, NULL, &items);
+#endif
+
+
+ /* Extract the SecIdentityRef */
+ if(status == errSecSuccess && items && CFArrayGetCount(items)) {
+ CFIndex i, count;
+ count = CFArrayGetCount(items);
+
+ for(i = 0; i < count; i++) {
+ CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(items, i);
+ CFTypeID itemID = CFGetTypeID(item);
+
+ if(itemID == CFDictionaryGetTypeID()) {
+ CFTypeRef identity = (CFTypeRef) CFDictionaryGetValue(
+ (CFDictionaryRef) item,
+ kSecImportItemIdentity);
+ CFRetain(identity);
+ *out_cert_and_key = (SecIdentityRef) identity;
+ break;
+ }
+#if CURL_BUILD_MAC_10_7
+ else if(itemID == SecCertificateGetTypeID()) {
+ status = SecIdentityCreateWithCertificate(NULL,
+ (SecCertificateRef) item,
+ out_cert_and_key);
+ break;
+ }
+#endif
+ }
+ }
+
+ if(items)
+ CFRelease(items);
+ CFRelease(pkcs_data);
+ }
+#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */
+ if(password)
+ CFRelease(password);
+ if(pkcs_url)
+ CFRelease(pkcs_url);
+ return status;
+}
+
+/* This code was borrowed from nss.c, with some modifications:
+ * 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
+ */
+CF_INLINE bool is_file(const char *filename)
+{
+ struct_stat st;
+
+ if(filename == NULL)
+ return false;
+
+ if(stat(filename, &st) == 0)
+ return S_ISREG(st.st_mode);
+ return false;
+}
+
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+static CURLcode sectransp_version_from_curl(SSLProtocol *darwinver,
+ long ssl_version)
+{
+ switch(ssl_version) {
+ case CURL_SSLVERSION_TLSv1_0:
+ *darwinver = kTLSProtocol1;
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_1:
+ *darwinver = kTLSProtocol11;
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_2:
+ *darwinver = kTLSProtocol12;
+ return CURLE_OK;
+ case CURL_SSLVERSION_TLSv1_3:
+ /* TLS 1.3 support first appeared in iOS 11 and macOS 10.13 */
+#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1
+ if(__builtin_available(macOS 10.13, iOS 11.0, *)) {
+ *darwinver = kTLSProtocol13;
+ return CURLE_OK;
+ }
+#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) &&
+ HAVE_BUILTIN_AVAILABLE == 1 */
+ break;
+ }
+ return CURLE_SSL_CONNECT_ERROR;
+}
+#endif
+
+static CURLcode
+set_ssl_version_min_max(struct connectdata *conn, int sockindex)
+{
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ long ssl_version = SSL_CONN_CONFIG(version);
+ long ssl_version_max = SSL_CONN_CONFIG(version_max);
+ long max_supported_version_by_os;
+
+ /* macOS 10.5-10.7 supported TLS 1.0 only.
+ macOS 10.8 and later, and iOS 5 and later, added TLS 1.1 and 1.2.
+ macOS 10.13 and later, and iOS 11 and later, added TLS 1.3. */
+#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1
+ if(__builtin_available(macOS 10.13, iOS 11.0, *)) {
+ max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_3;
+ }
+ else {
+ max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2;
+ }
+#else
+ max_supported_version_by_os = CURL_SSLVERSION_MAX_TLSv1_2;
+#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) &&
+ HAVE_BUILTIN_AVAILABLE == 1 */
+
+ 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 = max_supported_version_by_os;
+ break;
+ }
+
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+ if(SSLSetProtocolVersionMax != NULL) {
+ SSLProtocol darwin_ver_min = kTLSProtocol1;
+ SSLProtocol darwin_ver_max = kTLSProtocol1;
+ CURLcode result = sectransp_version_from_curl(&darwin_ver_min,
+ ssl_version);
+ if(result) {
+ failf(data, "unsupported min version passed via CURLOPT_SSLVERSION");
+ return result;
+ }
+ result = sectransp_version_from_curl(&darwin_ver_max,
+ ssl_version_max >> 16);
+ if(result) {
+ failf(data, "unsupported max version passed via CURLOPT_SSLVERSION");
+ return result;
+ }
+
+ (void)SSLSetProtocolVersionMin(backend->ssl_ctx, darwin_ver_min);
+ (void)SSLSetProtocolVersionMax(backend->ssl_ctx, darwin_ver_max);
+ return result;
+ }
+ else {
+#if CURL_SUPPORT_MAC_10_8
+ long i = ssl_version;
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kSSLProtocolAll,
+ false);
+ for(; i <= (ssl_version_max >> 16); i++) {
+ switch(i) {
+ case CURL_SSLVERSION_TLSv1_0:
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kTLSProtocol1,
+ true);
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kTLSProtocol11,
+ true);
+ break;
+ case CURL_SSLVERSION_TLSv1_2:
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kTLSProtocol12,
+ true);
+ break;
+ case CURL_SSLVERSION_TLSv1_3:
+ failf(data, "Your version of the OS does not support TLSv1.3");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ return CURLE_OK;
+#endif /* CURL_SUPPORT_MAC_10_8 */
+ }
+#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
+ failf(data, "Secure Transport: cannot set SSL protocol");
+ return CURLE_SSL_CONNECT_ERROR;
+}
+
+
+static CURLcode sectransp_connect_step1(struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_easy *data = conn->data;
+ curl_socket_t sockfd = conn->sock[sockindex];
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile);
+ const struct curl_blob *ssl_cablob = NULL;
+ const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
+ char * const ssl_cert = SSL_SET_OPTION(primary.clientcert);
+ const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob);
+#ifndef CURL_DISABLE_PROXY
+ const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
+ conn->host.name;
+ const long int port = SSL_IS_PROXY() ? conn->port : conn->remote_port;
+#else
+ const char * const hostname = conn->host.name;
+ const long int port = conn->remote_port;
+#endif
+#ifdef ENABLE_IPV6
+ struct in6_addr addr;
+#else
+ struct in_addr addr;
+#endif /* ENABLE_IPV6 */
+ size_t all_ciphers_count = 0UL, allowed_ciphers_count = 0UL, i;
+ SSLCipherSuite *all_ciphers = NULL, *allowed_ciphers = NULL;
+ OSStatus err = noErr;
+#if CURL_BUILD_MAC
+ int darwinver_maj = 0, darwinver_min = 0;
+
+ GetDarwinVersionNumber(&darwinver_maj, &darwinver_min);
+#endif /* CURL_BUILD_MAC */
+
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+ if(SSLCreateContext != NULL) { /* use the newer API if available */
+ if(backend->ssl_ctx)
+ CFRelease(backend->ssl_ctx);
+ backend->ssl_ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
+ if(!backend->ssl_ctx) {
+ failf(data, "SSL: couldn't create a context!");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else {
+ /* The old ST API does not exist under iOS, so don't compile it: */
+#if CURL_SUPPORT_MAC_10_8
+ if(backend->ssl_ctx)
+ (void)SSLDisposeContext(backend->ssl_ctx);
+ err = SSLNewContext(false, &(backend->ssl_ctx));
+ if(err != noErr) {
+ failf(data, "SSL: couldn't create a context: OSStatus %d", err);
+ return CURLE_OUT_OF_MEMORY;
+ }
+#endif /* CURL_SUPPORT_MAC_10_8 */
+ }
+#else
+ if(backend->ssl_ctx)
+ (void)SSLDisposeContext(backend->ssl_ctx);
+ err = SSLNewContext(false, &(backend->ssl_ctx));
+ if(err != noErr) {
+ failf(data, "SSL: couldn't create a context: OSStatus %d", err);
+ return CURLE_OUT_OF_MEMORY;
+ }
+#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
+ backend->ssl_write_buffered_length = 0UL; /* reset buffered write length */
+
+ /* check to see if we've been told to use an explicit SSL/TLS version */
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+ if(SSLSetProtocolVersionMax != NULL) {
+ switch(conn->ssl_config.version) {
+ case CURL_SSLVERSION_TLSv1:
+ (void)SSLSetProtocolVersionMin(backend->ssl_ctx, kTLSProtocol1);
+#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1
+ if(__builtin_available(macOS 10.13, iOS 11.0, *)) {
+ (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol13);
+ }
+ else {
+ (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol12);
+ }
+#else
+ (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kTLSProtocol12);
+#endif /* (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) &&
+ HAVE_BUILTIN_AVAILABLE == 1 */
+ break;
+ case CURL_SSLVERSION_DEFAULT:
+ 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(conn, sockindex);
+ if(result != CURLE_OK)
+ return result;
+ break;
+ }
+ case CURL_SSLVERSION_SSLv3:
+ err = SSLSetProtocolVersionMin(backend->ssl_ctx, kSSLProtocol3);
+ if(err != noErr) {
+ failf(data, "Your version of the OS does not support SSLv3");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kSSLProtocol3);
+ break;
+ case CURL_SSLVERSION_SSLv2:
+ err = SSLSetProtocolVersionMin(backend->ssl_ctx, kSSLProtocol2);
+ if(err != noErr) {
+ failf(data, "Your version of the OS does not support SSLv2");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ (void)SSLSetProtocolVersionMax(backend->ssl_ctx, kSSLProtocol2);
+ break;
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ else {
+#if CURL_SUPPORT_MAC_10_8
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kSSLProtocolAll,
+ false);
+ switch(conn->ssl_config.version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kTLSProtocol1,
+ true);
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kTLSProtocol11,
+ true);
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kTLSProtocol12,
+ true);
+ break;
+ 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(conn, sockindex);
+ if(result != CURLE_OK)
+ return result;
+ break;
+ }
+ case CURL_SSLVERSION_SSLv3:
+ err = SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kSSLProtocol3,
+ true);
+ if(err != noErr) {
+ failf(data, "Your version of the OS does not support SSLv3");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ break;
+ case CURL_SSLVERSION_SSLv2:
+ err = SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kSSLProtocol2,
+ true);
+ if(err != noErr) {
+ failf(data, "Your version of the OS does not support SSLv2");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ break;
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif /* CURL_SUPPORT_MAC_10_8 */
+ }
+#else
+ if(conn->ssl_config.version_max != CURL_SSLVERSION_MAX_NONE) {
+ failf(data, "Your version of the OS does not support to set maximum"
+ " SSL/TLS version");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocolAll, false);
+ switch(conn->ssl_config.version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ case CURL_SSLVERSION_TLSv1_0:
+ (void)SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kTLSProtocol1,
+ true);
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+ failf(data, "Your version of the OS does not support TLSv1.1");
+ return CURLE_SSL_CONNECT_ERROR;
+ case CURL_SSLVERSION_TLSv1_2:
+ failf(data, "Your version of the OS does not support TLSv1.2");
+ return CURLE_SSL_CONNECT_ERROR;
+ case CURL_SSLVERSION_TLSv1_3:
+ failf(data, "Your version of the OS does not support TLSv1.3");
+ return CURLE_SSL_CONNECT_ERROR;
+ case CURL_SSLVERSION_SSLv2:
+ err = SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kSSLProtocol2,
+ true);
+ if(err != noErr) {
+ failf(data, "Your version of the OS does not support SSLv2");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ break;
+ case CURL_SSLVERSION_SSLv3:
+ err = SSLSetProtocolVersionEnabled(backend->ssl_ctx,
+ kSSLProtocol3,
+ true);
+ if(err != noErr) {
+ failf(data, "Your version of the OS does not support SSLv3");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ break;
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
+
+#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1
+ if(conn->bits.tls_enable_alpn) {
+ if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) {
+ CFMutableArrayRef alpnArr = CFArrayCreateMutable(NULL, 0,
+ &kCFTypeArrayCallBacks);
+
+#ifdef USE_NGHTTP2
+ if(data->set.httpversion >= CURL_HTTP_VERSION_2
+#ifndef CURL_DISABLE_PROXY
+ && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy)
+#endif
+ ) {
+ CFArrayAppendValue(alpnArr, CFSTR(NGHTTP2_PROTO_VERSION_ID));
+ infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
+ }
+#endif
+
+ CFArrayAppendValue(alpnArr, CFSTR(ALPN_HTTP_1_1));
+ infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1);
+
+ /* expects length prefixed preference ordered list of protocols in wire
+ * format
+ */
+ err = SSLSetALPNProtocols(backend->ssl_ctx, alpnArr);
+ if(err != noErr)
+ infof(data, "WARNING: failed to set ALPN protocols; OSStatus %d\n",
+ err);
+ CFRelease(alpnArr);
+ }
+ }
+#endif
+
+ if(SSL_SET_OPTION(key)) {
+ infof(data, "WARNING: SSL: CURLOPT_SSLKEY is ignored by Secure "
+ "Transport. The private key must be in the Keychain.\n");
+ }
+
+ if(ssl_cert || ssl_cert_blob) {
+ bool is_cert_data = ssl_cert_blob != NULL;
+ bool is_cert_file = (!is_cert_data) && is_file(ssl_cert);
+ SecIdentityRef cert_and_key = NULL;
+
+ /* User wants to authenticate with a client cert. Look for it:
+ If we detect that this is a file on disk, then let's load it.
+ Otherwise, assume that the user wants to use an identity loaded
+ from the Keychain. */
+ if(is_cert_file || is_cert_data) {
+ if(!SSL_SET_OPTION(cert_type))
+ infof(data, "WARNING: SSL: Certificate type not set, assuming "
+ "PKCS#12 format.\n");
+ else if(strncmp(SSL_SET_OPTION(cert_type), "P12",
+ strlen(SSL_SET_OPTION(cert_type))) != 0)
+ infof(data, "WARNING: SSL: The Security framework only supports "
+ "loading identities that are in PKCS#12 format.\n");
+
+ err = CopyIdentityFromPKCS12File(ssl_cert, ssl_cert_blob,
+ SSL_SET_OPTION(key_passwd), &cert_and_key);
+ }
+ else
+ err = CopyIdentityWithLabel(ssl_cert, &cert_and_key);
+
+ if(err == noErr && cert_and_key) {
+ SecCertificateRef cert = NULL;
+ CFTypeRef certs_c[1];
+ CFArrayRef certs;
+
+ /* If we found one, print it out: */
+ err = SecIdentityCopyCertificate(cert_and_key, &cert);
+ if(err == noErr) {
+ char *certp;
+ CURLcode result = CopyCertSubject(data, cert, &certp);
+ if(!result) {
+ infof(data, "Client certificate: %s\n", certp);
+ free(certp);
+ }
+
+ CFRelease(cert);
+ if(result == CURLE_PEER_FAILED_VERIFICATION)
+ return CURLE_SSL_CERTPROBLEM;
+ if(result)
+ return result;
+ }
+ certs_c[0] = cert_and_key;
+ certs = CFArrayCreate(NULL, (const void **)certs_c, 1L,
+ &kCFTypeArrayCallBacks);
+ err = SSLSetCertificate(backend->ssl_ctx, certs);
+ if(certs)
+ CFRelease(certs);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetCertificate() failed: OSStatus %d", err);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ CFRelease(cert_and_key);
+ }
+ else {
+ const char *cert_showfilename_error =
+ is_cert_data ? "(memory blob)" : ssl_cert;
+
+ switch(err) {
+ case errSecAuthFailed: case -25264: /* errSecPkcs12VerifyFailure */
+ failf(data, "SSL: Incorrect password for the certificate \"%s\" "
+ "and its private key.", cert_showfilename_error);
+ break;
+ case -26275: /* errSecDecode */ case -25257: /* errSecUnknownFormat */
+ failf(data, "SSL: Couldn't make sense of the data in the "
+ "certificate \"%s\" and its private key.",
+ cert_showfilename_error);
+ break;
+ case -25260: /* errSecPassphraseRequired */
+ failf(data, "SSL The certificate \"%s\" requires a password.",
+ cert_showfilename_error);
+ break;
+ case errSecItemNotFound:
+ failf(data, "SSL: Can't find the certificate \"%s\" and its private "
+ "key in the Keychain.", cert_showfilename_error);
+ break;
+ default:
+ failf(data, "SSL: Can't load the certificate \"%s\" and its private "
+ "key: OSStatus %d", cert_showfilename_error, err);
+ break;
+ }
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ }
+
+ /* SSL always tries to verify the peer, this only says whether it should
+ * fail to connect if the verification fails, or if it should continue
+ * anyway. In the latter case the result of the verification is checked with
+ * SSL_get_verify_result() below. */
+#if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS
+ /* Snow Leopard introduced the SSLSetSessionOption() function, but due to
+ a library bug with the way the kSSLSessionOptionBreakOnServerAuth flag
+ works, it doesn't work as expected under Snow Leopard, Lion or
+ Mountain Lion.
+ So we need to call SSLSetEnableCertVerify() on those older cats in order
+ to disable certificate validation if the user turned that off.
+ (SecureTransport will always validate the certificate chain by
+ default.)
+ Note:
+ Darwin 11.x.x is Lion (10.7)
+ Darwin 12.x.x is Mountain Lion (10.8)
+ Darwin 13.x.x is Mavericks (10.9)
+ Darwin 14.x.x is Yosemite (10.10)
+ Darwin 15.x.x is El Capitan (10.11)
+ */
+#if CURL_BUILD_MAC
+ if(SSLSetSessionOption != NULL && darwinver_maj >= 13) {
+#else
+ if(SSLSetSessionOption != NULL) {
+#endif /* CURL_BUILD_MAC */
+ bool break_on_auth = !conn->ssl_config.verifypeer ||
+ ssl_cafile || ssl_cablob;
+ err = SSLSetSessionOption(backend->ssl_ctx,
+ kSSLSessionOptionBreakOnServerAuth,
+ break_on_auth);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetSessionOption() failed: OSStatus %d", err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ else {
+#if CURL_SUPPORT_MAC_10_8
+ err = SSLSetEnableCertVerify(backend->ssl_ctx,
+ conn->ssl_config.verifypeer?true:false);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif /* CURL_SUPPORT_MAC_10_8 */
+ }
+#else
+ err = SSLSetEnableCertVerify(backend->ssl_ctx,
+ conn->ssl_config.verifypeer?true:false);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */
+
+ if((ssl_cafile || ssl_cablob) && verifypeer) {
+ bool is_cert_data = ssl_cablob != NULL;
+ bool is_cert_file = (!is_cert_data) && is_file(ssl_cafile);
+
+ if(!(is_cert_file || is_cert_data)) {
+ failf(data, "SSL: can't load CA certificate file %s", ssl_cafile);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ }
+
+ /* Configure hostname check. SNI is used if available.
+ * Both hostname check and SNI require SSLSetPeerDomainName().
+ * Also: the verifyhost setting influences SNI usage */
+ if(conn->ssl_config.verifyhost) {
+ err = SSLSetPeerDomainName(backend->ssl_ctx, hostname,
+ strlen(hostname));
+
+ if(err != noErr) {
+ infof(data, "WARNING: SSL: SSLSetPeerDomainName() failed: OSStatus %d\n",
+ err);
+ }
+
+ if((Curl_inet_pton(AF_INET, hostname, &addr))
+ #ifdef ENABLE_IPV6
+ || (Curl_inet_pton(AF_INET6, hostname, &addr))
+ #endif
+ ) {
+ infof(data, "WARNING: using IP address, SNI is being disabled by "
+ "the OS.\n");
+ }
+ }
+ else {
+ infof(data, "WARNING: disabling hostname validation also disables SNI.\n");
+ }
+
+ /* Disable cipher suites that ST supports but are not safe. These ciphers
+ are unlikely to be used in any case since ST gives other ciphers a much
+ higher priority, but it's probably better that we not connect at all than
+ to give the user a false sense of security if the server only supports
+ insecure ciphers. (Note: We don't care about SSLv2-only ciphers.) */
+ err = SSLGetNumberSupportedCiphers(backend->ssl_ctx, &all_ciphers_count);
+ if(err != noErr) {
+ failf(data, "SSL: SSLGetNumberSupportedCiphers() failed: OSStatus %d",
+ err);
+ return CURLE_SSL_CIPHER;
+ }
+ all_ciphers = malloc(all_ciphers_count*sizeof(SSLCipherSuite));
+ if(!all_ciphers) {
+ failf(data, "SSL: Failed to allocate memory for all ciphers");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ allowed_ciphers = malloc(all_ciphers_count*sizeof(SSLCipherSuite));
+ if(!allowed_ciphers) {
+ Curl_safefree(all_ciphers);
+ failf(data, "SSL: Failed to allocate memory for allowed ciphers");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ err = SSLGetSupportedCiphers(backend->ssl_ctx, all_ciphers,
+ &all_ciphers_count);
+ if(err != noErr) {
+ Curl_safefree(all_ciphers);
+ Curl_safefree(allowed_ciphers);
+ return CURLE_SSL_CIPHER;
+ }
+ for(i = 0UL ; i < all_ciphers_count ; i++) {
+#if CURL_BUILD_MAC
+ /* There's a known bug in early versions of Mountain Lion where ST's ECC
+ ciphers (cipher suite 0xC001 through 0xC032) simply do not work.
+ Work around the problem here by disabling those ciphers if we are
+ running in an affected version of OS X. */
+ if(darwinver_maj == 12 && darwinver_min <= 3 &&
+ all_ciphers[i] >= 0xC001 && all_ciphers[i] <= 0xC032) {
+ continue;
+ }
+#endif /* CURL_BUILD_MAC */
+ switch(all_ciphers[i]) {
+ /* Disable NULL ciphersuites: */
+ case SSL_NULL_WITH_NULL_NULL:
+ case SSL_RSA_WITH_NULL_MD5:
+ case SSL_RSA_WITH_NULL_SHA:
+ case 0x003B: /* TLS_RSA_WITH_NULL_SHA256 */
+ case SSL_FORTEZZA_DMS_WITH_NULL_SHA:
+ case 0xC001: /* TLS_ECDH_ECDSA_WITH_NULL_SHA */
+ case 0xC006: /* TLS_ECDHE_ECDSA_WITH_NULL_SHA */
+ case 0xC00B: /* TLS_ECDH_RSA_WITH_NULL_SHA */
+ case 0xC010: /* TLS_ECDHE_RSA_WITH_NULL_SHA */
+ case 0x002C: /* TLS_PSK_WITH_NULL_SHA */
+ case 0x002D: /* TLS_DHE_PSK_WITH_NULL_SHA */
+ case 0x002E: /* TLS_RSA_PSK_WITH_NULL_SHA */
+ case 0x00B0: /* TLS_PSK_WITH_NULL_SHA256 */
+ case 0x00B1: /* TLS_PSK_WITH_NULL_SHA384 */
+ case 0x00B4: /* TLS_DHE_PSK_WITH_NULL_SHA256 */
+ case 0x00B5: /* TLS_DHE_PSK_WITH_NULL_SHA384 */
+ case 0x00B8: /* TLS_RSA_PSK_WITH_NULL_SHA256 */
+ case 0x00B9: /* TLS_RSA_PSK_WITH_NULL_SHA384 */
+ /* Disable anonymous ciphersuites: */
+ case SSL_DH_anon_EXPORT_WITH_RC4_40_MD5:
+ case SSL_DH_anon_WITH_RC4_128_MD5:
+ case SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA:
+ case SSL_DH_anon_WITH_DES_CBC_SHA:
+ case SSL_DH_anon_WITH_3DES_EDE_CBC_SHA:
+ case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+ case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+ case 0xC015: /* TLS_ECDH_anon_WITH_NULL_SHA */
+ case 0xC016: /* TLS_ECDH_anon_WITH_RC4_128_SHA */
+ case 0xC017: /* TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA */
+ case 0xC018: /* TLS_ECDH_anon_WITH_AES_128_CBC_SHA */
+ case 0xC019: /* TLS_ECDH_anon_WITH_AES_256_CBC_SHA */
+ case 0x006C: /* TLS_DH_anon_WITH_AES_128_CBC_SHA256 */
+ case 0x006D: /* TLS_DH_anon_WITH_AES_256_CBC_SHA256 */
+ case 0x00A6: /* TLS_DH_anon_WITH_AES_128_GCM_SHA256 */
+ case 0x00A7: /* TLS_DH_anon_WITH_AES_256_GCM_SHA384 */
+ /* Disable weak key ciphersuites: */
+ case SSL_RSA_EXPORT_WITH_RC4_40_MD5:
+ case SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5:
+ case SSL_RSA_EXPORT_WITH_DES40_CBC_SHA:
+ case SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA:
+ case SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA:
+ case SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA:
+ case SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA:
+ case SSL_RSA_WITH_DES_CBC_SHA:
+ case SSL_DH_DSS_WITH_DES_CBC_SHA:
+ case SSL_DH_RSA_WITH_DES_CBC_SHA:
+ case SSL_DHE_DSS_WITH_DES_CBC_SHA:
+ case SSL_DHE_RSA_WITH_DES_CBC_SHA:
+ /* Disable IDEA: */
+ case SSL_RSA_WITH_IDEA_CBC_SHA:
+ case SSL_RSA_WITH_IDEA_CBC_MD5:
+ /* Disable RC4: */
+ case SSL_RSA_WITH_RC4_128_MD5:
+ case SSL_RSA_WITH_RC4_128_SHA:
+ case 0xC002: /* TLS_ECDH_ECDSA_WITH_RC4_128_SHA */
+ case 0xC007: /* TLS_ECDHE_ECDSA_WITH_RC4_128_SHA*/
+ case 0xC00C: /* TLS_ECDH_RSA_WITH_RC4_128_SHA */
+ case 0xC011: /* TLS_ECDHE_RSA_WITH_RC4_128_SHA */
+ case 0x008A: /* TLS_PSK_WITH_RC4_128_SHA */
+ case 0x008E: /* TLS_DHE_PSK_WITH_RC4_128_SHA */
+ case 0x0092: /* TLS_RSA_PSK_WITH_RC4_128_SHA */
+ break;
+ default: /* enable everything else */
+ allowed_ciphers[allowed_ciphers_count++] = all_ciphers[i];
+ break;
+ }
+ }
+ err = SSLSetEnabledCiphers(backend->ssl_ctx, allowed_ciphers,
+ allowed_ciphers_count);
+ Curl_safefree(all_ciphers);
+ Curl_safefree(allowed_ciphers);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err);
+ return CURLE_SSL_CIPHER;
+ }
+
+#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7
+ /* We want to enable 1/n-1 when using a CBC cipher unless the user
+ specifically doesn't want us doing that: */
+ if(SSLSetSessionOption != NULL) {
+ SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionSendOneByteRecord,
+ !data->set.ssl.enable_beast);
+ SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionFalseStart,
+ data->set.ssl.falsestart); /* false start support */
+ }
+#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */
+
+ /* Check if there's a cached ID we can/should use here! */
+ if(SSL_SET_OPTION(primary.sessionid)) {
+ char *ssl_sessionid;
+ size_t ssl_sessionid_len;
+
+ Curl_ssl_sessionid_lock(conn);
+ if(!Curl_ssl_getsessionid(conn, (void **)&ssl_sessionid,
+ &ssl_sessionid_len, sockindex)) {
+ /* we got a session id, use it! */
+ err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
+ Curl_ssl_sessionid_unlock(conn);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ /* Informational message */
+ infof(data, "SSL re-using session ID\n");
+ }
+ /* If there isn't one, then let's make one up! This has to be done prior
+ to starting the handshake. */
+ else {
+ CURLcode result;
+ ssl_sessionid =
+ aprintf("%s:%d:%d:%s:%ld", ssl_cafile,
+ verifypeer, SSL_CONN_CONFIG(verifyhost), hostname, port);
+ ssl_sessionid_len = strlen(ssl_sessionid);
+
+ err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
+ if(err != noErr) {
+ Curl_ssl_sessionid_unlock(conn);
+ failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ result = Curl_ssl_addsessionid(conn, ssl_sessionid, ssl_sessionid_len,
+ sockindex);
+ Curl_ssl_sessionid_unlock(conn);
+ if(result) {
+ failf(data, "failed to store ssl session");
+ return result;
+ }
+ }
+ }
+
+ err = SSLSetIOFuncs(backend->ssl_ctx, SocketRead, SocketWrite);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetIOFuncs() failed: OSStatus %d", err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ /* pass the raw socket into the SSL layers */
+ /* We need to store the FD in a constant memory address, because
+ * SSLSetConnection() will not copy that address. I've found that
+ * conn->sock[sockindex] may change on its own. */
+ backend->ssl_sockfd = sockfd;
+ err = SSLSetConnection(backend->ssl_ctx, connssl);
+ if(err != noErr) {
+ failf(data, "SSL: SSLSetConnection() failed: %d", err);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ connssl->connecting_state = ssl_connect_2;
+ return CURLE_OK;
+}
+
+static long pem_to_der(const char *in, unsigned char **out, size_t *outlen)
+{
+ char *sep_start, *sep_end, *cert_start, *cert_end;
+ size_t i, j, err;
+ size_t len;
+ unsigned char *b64;
+
+ /* Jump through the separators at the beginning of the certificate. */
+ sep_start = strstr(in, "-----");
+ if(sep_start == NULL)
+ return 0;
+ cert_start = strstr(sep_start + 1, "-----");
+ if(cert_start == NULL)
+ return -1;
+
+ cert_start += 5;
+
+ /* Find separator after the end of the certificate. */
+ cert_end = strstr(cert_start, "-----");
+ if(cert_end == NULL)
+ return -1;
+
+ sep_end = strstr(cert_end + 1, "-----");
+ if(sep_end == NULL)
+ return -1;
+ sep_end += 5;
+
+ len = cert_end - cert_start;
+ b64 = malloc(len + 1);
+ if(!b64)
+ return -1;
+
+ /* Create base64 string without linefeeds. */
+ for(i = 0, j = 0; i < len; i++) {
+ if(cert_start[i] != '\r' && cert_start[i] != '\n')
+ b64[j++] = cert_start[i];
+ }
+ b64[j] = '\0';
+
+ err = Curl_base64_decode((const char *)b64, out, outlen);
+ free(b64);
+ if(err) {
+ free(*out);
+ return -1;
+ }
+
+ return sep_end - in;
+}
+
+static int read_cert(const char *file, unsigned char **out, size_t *outlen)
+{
+ int fd;
+ ssize_t n, len = 0, cap = 512;
+ unsigned char buf[512], *data;
+
+ fd = open(file, 0);
+ if(fd < 0)
+ return -1;
+
+ data = malloc(cap);
+ if(!data) {
+ close(fd);
+ return -1;
+ }
+
+ for(;;) {
+ n = read(fd, buf, sizeof(buf));
+ if(n < 0) {
+ close(fd);
+ free(data);
+ return -1;
+ }
+ else if(n == 0) {
+ close(fd);
+ break;
+ }
+
+ if(len + n >= cap) {
+ cap *= 2;
+ data = Curl_saferealloc(data, cap);
+ if(!data) {
+ close(fd);
+ return -1;
+ }
+ }
+
+ memcpy(data + len, buf, n);
+ len += n;
+ }
+ data[len] = '\0';
+
+ *out = data;
+ *outlen = len;
+
+ return 0;
+}
+
+static int append_cert_to_array(struct Curl_easy *data,
+ unsigned char *buf, size_t buflen,
+ CFMutableArrayRef array)
+{
+ CFDataRef certdata = CFDataCreate(kCFAllocatorDefault, buf, buflen);
+ char *certp;
+ CURLcode result;
+ if(!certdata) {
+ failf(data, "SSL: failed to allocate array for CA certificate");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ SecCertificateRef cacert =
+ SecCertificateCreateWithData(kCFAllocatorDefault, certdata);
+ CFRelease(certdata);
+ if(!cacert) {
+ failf(data, "SSL: failed to create SecCertificate from CA certificate");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+
+ /* Check if cacert is valid. */
+ result = CopyCertSubject(data, cacert, &certp);
+ switch(result) {
+ case CURLE_OK:
+ break;
+ case CURLE_PEER_FAILED_VERIFICATION:
+ return CURLE_SSL_CACERT_BADFILE;
+ case CURLE_OUT_OF_MEMORY:
+ default:
+ return result;
+ }
+ free(certp);
+
+ CFArrayAppendValue(array, cacert);
+ CFRelease(cacert);
+
+ return CURLE_OK;
+}
+
+static CURLcode verify_cert(const char *cafile, struct Curl_easy *data,
+ SSLContextRef ctx)
+{
+ int n = 0, rc;
+ long res;
+ unsigned char *certbuf, *der;
+ size_t buflen, derlen, offset = 0;
+
+ if(read_cert(cafile, &certbuf, &buflen) < 0) {
+ failf(data, "SSL: failed to read or invalid CA certificate");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+
+ /*
+ * Certbuf now contains the contents of the certificate file, which can be
+ * - a single DER certificate,
+ * - a single PEM certificate or
+ * - a bunch of PEM certificates (certificate bundle).
+ *
+ * Go through certbuf, and convert any PEM certificate in it into DER
+ * format.
+ */
+ CFMutableArrayRef array = CFArrayCreateMutable(kCFAllocatorDefault, 0,
+ &kCFTypeArrayCallBacks);
+ if(array == NULL) {
+ free(certbuf);
+ failf(data, "SSL: out of memory creating CA certificate array");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ while(offset < buflen) {
+ n++;
+
+ /*
+ * Check if the certificate is in PEM format, and convert it to DER. If
+ * this fails, we assume the certificate is in DER format.
+ */
+ res = pem_to_der((const char *)certbuf + offset, &der, &derlen);
+ if(res < 0) {
+ free(certbuf);
+ CFRelease(array);
+ failf(data, "SSL: invalid CA certificate #%d (offset %zu) in bundle",
+ n, offset);
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ offset += res;
+
+ if(res == 0 && offset == 0) {
+ /* This is not a PEM file, probably a certificate in DER format. */
+ rc = append_cert_to_array(data, certbuf, buflen, array);
+ free(certbuf);
+ if(rc != CURLE_OK) {
+ CFRelease(array);
+ return rc;
+ }
+ break;
+ }
+ else if(res == 0) {
+ /* No more certificates in the bundle. */
+ free(certbuf);
+ break;
+ }
+
+ rc = append_cert_to_array(data, der, derlen, array);
+ free(der);
+ if(rc != CURLE_OK) {
+ free(certbuf);
+ CFRelease(array);
+ return rc;
+ }
+ }
+
+ SecTrustRef trust;
+ OSStatus ret = SSLCopyPeerTrust(ctx, &trust);
+ if(trust == NULL) {
+ failf(data, "SSL: error getting certificate chain");
+ CFRelease(array);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else if(ret != noErr) {
+ CFRelease(array);
+ failf(data, "SSLCopyPeerTrust() returned error %d", ret);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ ret = SecTrustSetAnchorCertificates(trust, array);
+ if(ret != noErr) {
+ CFRelease(array);
+ CFRelease(trust);
+ failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ ret = SecTrustSetAnchorCertificatesOnly(trust, true);
+ if(ret != noErr) {
+ CFRelease(array);
+ CFRelease(trust);
+ failf(data, "SecTrustSetAnchorCertificatesOnly() returned error %d", ret);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ SecTrustResultType trust_eval = 0;
+ ret = SecTrustEvaluate(trust, &trust_eval);
+ CFRelease(array);
+ CFRelease(trust);
+ if(ret != noErr) {
+ failf(data, "SecTrustEvaluate() returned error %d", ret);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+
+ switch(trust_eval) {
+ case kSecTrustResultUnspecified:
+ case kSecTrustResultProceed:
+ return CURLE_OK;
+
+ case kSecTrustResultRecoverableTrustFailure:
+ case kSecTrustResultDeny:
+ default:
+ failf(data, "SSL: certificate verification failed (result: %d)",
+ trust_eval);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+}
+
+#ifdef SECTRANSP_PINNEDPUBKEY
+static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
+ SSLContextRef ctx,
+ const char *pinnedpubkey)
+{ /* Scratch */
+ size_t pubkeylen, realpubkeylen, spkiHeaderLength = 24;
+ unsigned char *pubkey = NULL, *realpubkey = NULL;
+ const unsigned char *spkiHeader = NULL;
+ CFDataRef publicKeyBits = 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(!ctx)
+ return result;
+
+ do {
+ SecTrustRef trust;
+ OSStatus ret = SSLCopyPeerTrust(ctx, &trust);
+ if(ret != noErr || trust == NULL)
+ break;
+
+ SecKeyRef keyRef = SecTrustCopyPublicKey(trust);
+ CFRelease(trust);
+ if(keyRef == NULL)
+ break;
+
+#ifdef SECTRANSP_PINNEDPUBKEY_V1
+
+ publicKeyBits = SecKeyCopyExternalRepresentation(keyRef, NULL);
+ CFRelease(keyRef);
+ if(publicKeyBits == NULL)
+ break;
+
+#elif SECTRANSP_PINNEDPUBKEY_V2
+
+ OSStatus success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL,
+ &publicKeyBits);
+ CFRelease(keyRef);
+ if(success != errSecSuccess || publicKeyBits == NULL)
+ break;
+
+#endif /* SECTRANSP_PINNEDPUBKEY_V2 */
+
+ pubkeylen = CFDataGetLength(publicKeyBits);
+ pubkey = (unsigned char *)CFDataGetBytePtr(publicKeyBits);
+
+ switch(pubkeylen) {
+ case 526:
+ /* 4096 bit RSA pubkeylen == 526 */
+ spkiHeader = rsa4096SpkiHeader;
+ break;
+ case 270:
+ /* 2048 bit RSA pubkeylen == 270 */
+ spkiHeader = rsa2048SpkiHeader;
+ break;
+#ifdef SECTRANSP_PINNEDPUBKEY_V1
+ case 65:
+ /* ecDSA secp256r1 pubkeylen == 65 */
+ spkiHeader = ecDsaSecp256r1SpkiHeader;
+ spkiHeaderLength = 26;
+ break;
+ case 97:
+ /* ecDSA secp384r1 pubkeylen == 97 */
+ spkiHeader = ecDsaSecp384r1SpkiHeader;
+ spkiHeaderLength = 23;
+ break;
+ default:
+ infof(data, "SSL: unhandled public key length: %d\n", pubkeylen);
+#elif SECTRANSP_PINNEDPUBKEY_V2
+ default:
+ /* ecDSA secp256r1 pubkeylen == 91 header already included?
+ * ecDSA secp384r1 header already included too
+ * we assume rest of algorithms do same, so do nothing
+ */
+ result = Curl_pin_peer_pubkey(data, pinnedpubkey, pubkey,
+ pubkeylen);
+#endif /* SECTRANSP_PINNEDPUBKEY_V2 */
+ continue; /* break from loop */
+ }
+
+ realpubkeylen = pubkeylen + spkiHeaderLength;
+ realpubkey = malloc(realpubkeylen);
+ if(!realpubkey)
+ break;
+
+ memcpy(realpubkey, spkiHeader, spkiHeaderLength);
+ memcpy(realpubkey + spkiHeaderLength, pubkey, pubkeylen);
+
+ result = Curl_pin_peer_pubkey(data, pinnedpubkey, realpubkey,
+ realpubkeylen);
+
+ } while(0);
+
+ Curl_safefree(realpubkey);
+ if(publicKeyBits != NULL)
+ CFRelease(publicKeyBits);
+
+ return result;
+}
+#endif /* SECTRANSP_PINNEDPUBKEY */
+
+static CURLcode
+sectransp_connect_step2(struct connectdata *conn, int sockindex)
+{
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ OSStatus err;
+ SSLCipherSuite cipher;
+ SSLProtocol protocol = 0;
+#ifndef CURL_DISABLE_PROXY
+ const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
+ conn->host.name;
+#else
+ const char * const hostname = conn->host.name;
+#endif
+
+ DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
+ || ssl_connect_2_reading == connssl->connecting_state
+ || ssl_connect_2_writing == connssl->connecting_state);
+
+ /* Here goes nothing: */
+ err = SSLHandshake(backend->ssl_ctx);
+
+ if(err != noErr) {
+ switch(err) {
+ case errSSLWouldBlock: /* they're not done with us yet */
+ connssl->connecting_state = backend->ssl_direction ?
+ ssl_connect_2_writing : ssl_connect_2_reading;
+ return CURLE_OK;
+
+ /* The below is errSSLServerAuthCompleted; it's not defined in
+ Leopard's headers */
+ case -9841:
+ if(SSL_CONN_CONFIG(CAfile) && SSL_CONN_CONFIG(verifypeer)) {
+ CURLcode result = verify_cert(SSL_CONN_CONFIG(CAfile), data,
+ backend->ssl_ctx);
+ if(result)
+ return result;
+ }
+ /* the documentation says we need to call SSLHandshake() again */
+ return sectransp_connect_step2(conn, sockindex);
+
+ /* Problem with encrypt / decrypt */
+ case errSSLPeerDecodeError:
+ failf(data, "Decode failed");
+ break;
+ case errSSLDecryptionFail:
+ case errSSLPeerDecryptionFail:
+ failf(data, "Decryption failed");
+ break;
+ case errSSLPeerDecryptError:
+ failf(data, "A decryption error occurred");
+ break;
+ case errSSLBadCipherSuite:
+ failf(data, "A bad SSL cipher suite was encountered");
+ break;
+ case errSSLCrypto:
+ failf(data, "An underlying cryptographic error was encountered");
+ break;
+#if CURL_BUILD_MAC_10_11 || CURL_BUILD_IOS_9
+ case errSSLWeakPeerEphemeralDHKey:
+ failf(data, "Indicates a weak ephemeral Diffie-Hellman key");
+ break;
+#endif
+
+ /* Problem with the message record validation */
+ case errSSLBadRecordMac:
+ case errSSLPeerBadRecordMac:
+ failf(data, "A record with a bad message authentication code (MAC) "
+ "was encountered");
+ break;
+ case errSSLRecordOverflow:
+ case errSSLPeerRecordOverflow:
+ failf(data, "A record overflow occurred");
+ break;
+
+ /* Problem with zlib decompression */
+ case errSSLPeerDecompressFail:
+ failf(data, "Decompression failed");
+ break;
+
+ /* Problem with access */
+ case errSSLPeerAccessDenied:
+ failf(data, "Access was denied");
+ break;
+ case errSSLPeerInsufficientSecurity:
+ failf(data, "There is insufficient security for this operation");
+ break;
+
+ /* These are all certificate problems with the server: */
+ case errSSLXCertChainInvalid:
+ failf(data, "SSL certificate problem: Invalid certificate chain");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLUnknownRootCert:
+ failf(data, "SSL certificate problem: Untrusted root certificate");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLNoRootCert:
+ failf(data, "SSL certificate problem: No root certificate");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLCertNotYetValid:
+ failf(data, "SSL certificate problem: The certificate chain had a "
+ "certificate that is not yet valid");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLCertExpired:
+ case errSSLPeerCertExpired:
+ failf(data, "SSL certificate problem: Certificate chain had an "
+ "expired certificate");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLBadCert:
+ case errSSLPeerBadCert:
+ failf(data, "SSL certificate problem: Couldn't understand the server "
+ "certificate format");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLPeerUnsupportedCert:
+ failf(data, "SSL certificate problem: An unsupported certificate "
+ "format was encountered");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLPeerCertRevoked:
+ failf(data, "SSL certificate problem: The certificate was revoked");
+ return CURLE_PEER_FAILED_VERIFICATION;
+ case errSSLPeerCertUnknown:
+ failf(data, "SSL certificate problem: The certificate is unknown");
+ return CURLE_PEER_FAILED_VERIFICATION;
+
+ /* These are all certificate problems with the client: */
+ case errSecAuthFailed:
+ failf(data, "SSL authentication failed");
+ break;
+ case errSSLPeerHandshakeFail:
+ failf(data, "SSL peer handshake failed, the server most likely "
+ "requires a client certificate to connect");
+ break;
+ case errSSLPeerUnknownCA:
+ failf(data, "SSL server rejected the client certificate due to "
+ "the certificate being signed by an unknown certificate "
+ "authority");
+ break;
+
+ /* This error is raised if the server's cert didn't match the server's
+ host name: */
+ case errSSLHostNameMismatch:
+ failf(data, "SSL certificate peer verification failed, the "
+ "certificate did not match \"%s\"\n", conn->host.dispname);
+ return CURLE_PEER_FAILED_VERIFICATION;
+
+ /* Problem with SSL / TLS negotiation */
+ case errSSLNegotiation:
+ failf(data, "Could not negotiate an SSL cipher suite with the server");
+ break;
+ case errSSLBadConfiguration:
+ failf(data, "A configuration error occurred");
+ break;
+ case errSSLProtocol:
+ failf(data, "SSL protocol error");
+ break;
+ case errSSLPeerProtocolVersion:
+ failf(data, "A bad protocol version was encountered");
+ break;
+ case errSSLPeerNoRenegotiation:
+ failf(data, "No renegotiation is allowed");
+ break;
+
+ /* Generic handshake errors: */
+ case errSSLConnectionRefused:
+ failf(data, "Server dropped the connection during the SSL handshake");
+ break;
+ case errSSLClosedAbort:
+ failf(data, "Server aborted the SSL handshake");
+ break;
+ case errSSLClosedGraceful:
+ failf(data, "The connection closed gracefully");
+ break;
+ case errSSLClosedNoNotify:
+ failf(data, "The server closed the session with no notification");
+ break;
+ /* Sometimes paramErr happens with buggy ciphers: */
+ case paramErr:
+ case errSSLInternal:
+ case errSSLPeerInternalError:
+ failf(data, "Internal SSL engine error encountered during the "
+ "SSL handshake");
+ break;
+ case errSSLFatalAlert:
+ failf(data, "Fatal SSL engine error encountered during the SSL "
+ "handshake");
+ break;
+ /* Unclassified error */
+ case errSSLBufferOverflow:
+ failf(data, "An insufficient buffer was provided");
+ break;
+ case errSSLIllegalParam:
+ failf(data, "An illegal parameter was encountered");
+ break;
+ case errSSLModuleAttach:
+ failf(data, "Module attach failure");
+ break;
+ case errSSLSessionNotFound:
+ failf(data, "An attempt to restore an unknown session failed");
+ break;
+ case errSSLPeerExportRestriction:
+ failf(data, "An export restriction occurred");
+ break;
+ case errSSLPeerUserCancelled:
+ failf(data, "The user canceled the operation");
+ break;
+ case errSSLPeerUnexpectedMsg:
+ failf(data, "Peer rejected unexpected message");
+ break;
+#if CURL_BUILD_MAC_10_11 || CURL_BUILD_IOS_9
+ /* Treaing non-fatal error as fatal like before */
+ case errSSLClientHelloReceived:
+ failf(data, "A non-fatal result for providing a server name "
+ "indication");
+ break;
+#endif
+
+ /* Error codes defined in the enum but should never be returned.
+ We list them here just in case. */
+#if CURL_BUILD_MAC_10_6
+ /* Only returned when kSSLSessionOptionBreakOnCertRequested is set */
+ case errSSLClientCertRequested:
+ failf(data, "The server has requested a client certificate");
+ break;
+#endif
+#if CURL_BUILD_MAC_10_9
+ /* Alias for errSSLLast, end of error range */
+ case errSSLUnexpectedRecord:
+ failf(data, "Unexpected (skipped) record in DTLS");
+ break;
+#endif
+ default:
+ /* May also return codes listed in Security Framework Result Codes */
+ failf(data, "Unknown SSL protocol error in connection to %s:%d",
+ hostname, err);
+ break;
+ }
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else {
+ /* we have been connected fine, we're not waiting for anything else. */
+ connssl->connecting_state = ssl_connect_3;
+
+#ifdef SECTRANSP_PINNEDPUBKEY
+ if(data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]) {
+ CURLcode result = pkp_pin_peer_pubkey(data, backend->ssl_ctx,
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]);
+ if(result) {
+ failf(data, "SSL: public key does not match pinned public key!");
+ return result;
+ }
+ }
+#endif /* SECTRANSP_PINNEDPUBKEY */
+
+ /* Informational message */
+ (void)SSLGetNegotiatedCipher(backend->ssl_ctx, &cipher);
+ (void)SSLGetNegotiatedProtocolVersion(backend->ssl_ctx, &protocol);
+ switch(protocol) {
+ case kSSLProtocol2:
+ infof(data, "SSL 2.0 connection using %s\n",
+ SSLCipherNameForNumber(cipher));
+ break;
+ case kSSLProtocol3:
+ infof(data, "SSL 3.0 connection using %s\n",
+ SSLCipherNameForNumber(cipher));
+ break;
+ case kTLSProtocol1:
+ infof(data, "TLS 1.0 connection using %s\n",
+ TLSCipherNameForNumber(cipher));
+ break;
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+ case kTLSProtocol11:
+ infof(data, "TLS 1.1 connection using %s\n",
+ TLSCipherNameForNumber(cipher));
+ break;
+ case kTLSProtocol12:
+ infof(data, "TLS 1.2 connection using %s\n",
+ TLSCipherNameForNumber(cipher));
+ break;
+#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
+#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11
+ case kTLSProtocol13:
+ infof(data, "TLS 1.3 connection using %s\n",
+ TLSCipherNameForNumber(cipher));
+ break;
+#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */
+ default:
+ infof(data, "Unknown protocol connection\n");
+ break;
+ }
+
+#if(CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && HAVE_BUILTIN_AVAILABLE == 1
+ if(conn->bits.tls_enable_alpn) {
+ if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) {
+ CFArrayRef alpnArr = NULL;
+ CFStringRef chosenProtocol = NULL;
+ err = SSLCopyALPNProtocols(backend->ssl_ctx, &alpnArr);
+
+ if(err == noErr && alpnArr && CFArrayGetCount(alpnArr) >= 1)
+ chosenProtocol = CFArrayGetValueAtIndex(alpnArr, 0);
+
+#ifdef USE_NGHTTP2
+ if(chosenProtocol &&
+ !CFStringCompare(chosenProtocol, CFSTR(NGHTTP2_PROTO_VERSION_ID),
+ 0)) {
+ conn->negnpn = CURL_HTTP_VERSION_2;
+ }
+ else
+#endif
+ if(chosenProtocol &&
+ !CFStringCompare(chosenProtocol, CFSTR(ALPN_HTTP_1_1), 0)) {
+ conn->negnpn = CURL_HTTP_VERSION_1_1;
+ }
+ else
+ infof(data, "ALPN, server did not agree to a protocol\n");
+
+ Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+
+ /* chosenProtocol is a reference to the string within alpnArr
+ and doesn't need to be freed separately */
+ if(alpnArr)
+ CFRelease(alpnArr);
+ }
+ }
+#endif
+
+ return CURLE_OK;
+ }
+}
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+/* This should be called during step3 of the connection at the earliest */
+static void
+show_verbose_server_cert(struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ CFArrayRef server_certs = NULL;
+ SecCertificateRef server_cert;
+ OSStatus err;
+ CFIndex i, count;
+ SecTrustRef trust = NULL;
+
+ if(!backend->ssl_ctx)
+ return;
+
+#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS
+#if CURL_BUILD_IOS
+#pragma unused(server_certs)
+ err = SSLCopyPeerTrust(backend->ssl_ctx, &trust);
+ /* For some reason, SSLCopyPeerTrust() can return noErr and yet return
+ a null trust, so be on guard for that: */
+ if(err == noErr && trust) {
+ count = SecTrustGetCertificateCount(trust);
+ for(i = 0L ; i < count ; i++) {
+ CURLcode result;
+ char *certp;
+ server_cert = SecTrustGetCertificateAtIndex(trust, i);
+ result = CopyCertSubject(data, server_cert, &certp);
+ if(!result) {
+ infof(data, "Server certificate: %s\n", certp);
+ free(certp);
+ }
+ }
+ CFRelease(trust);
+ }
+#else
+ /* SSLCopyPeerCertificates() is deprecated as of Mountain Lion.
+ The function SecTrustGetCertificateAtIndex() is officially present
+ in Lion, but it is unfortunately also present in Snow Leopard as
+ private API and doesn't work as expected. So we have to look for
+ a different symbol to make sure this code is only executed under
+ Lion or later. */
+ if(SecTrustEvaluateAsync != NULL) {
+#pragma unused(server_certs)
+ err = SSLCopyPeerTrust(backend->ssl_ctx, &trust);
+ /* For some reason, SSLCopyPeerTrust() can return noErr and yet return
+ a null trust, so be on guard for that: */
+ if(err == noErr && trust) {
+ count = SecTrustGetCertificateCount(trust);
+ for(i = 0L ; i < count ; i++) {
+ char *certp;
+ CURLcode result;
+ server_cert = SecTrustGetCertificateAtIndex(trust, i);
+ result = CopyCertSubject(data, server_cert, &certp);
+ if(!result) {
+ infof(data, "Server certificate: %s\n", certp);
+ free(certp);
+ }
+ }
+ CFRelease(trust);
+ }
+ }
+ else {
+#if CURL_SUPPORT_MAC_10_8
+ err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs);
+ /* Just in case SSLCopyPeerCertificates() returns null too... */
+ if(err == noErr && server_certs) {
+ count = CFArrayGetCount(server_certs);
+ for(i = 0L ; i < count ; i++) {
+ char *certp;
+ CURLcode result;
+ server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs,
+ i);
+ result = CopyCertSubject(data, server_cert, &certp);
+ if(!result) {
+ infof(data, "Server certificate: %s\n", certp);
+ free(certp);
+ }
+ }
+ CFRelease(server_certs);
+ }
+#endif /* CURL_SUPPORT_MAC_10_8 */
+ }
+#endif /* CURL_BUILD_IOS */
+#else
+#pragma unused(trust)
+ err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs);
+ if(err == noErr) {
+ count = CFArrayGetCount(server_certs);
+ for(i = 0L ; i < count ; i++) {
+ CURLcode result;
+ char *certp;
+ server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, i);
+ result = CopyCertSubject(data, server_cert, &certp);
+ if(!result) {
+ infof(data, "Server certificate: %s\n", certp);
+ free(certp);
+ }
+ }
+ CFRelease(server_certs);
+ }
+#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */
+}
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
+
+static CURLcode
+sectransp_connect_step3(struct connectdata *conn,
+ int sockindex)
+{
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+
+ /* There is no step 3!
+ * Well, okay, if verbose mode is on, let's print the details of the
+ * server certificates. */
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ if(data->set.verbose)
+ show_verbose_server_cert(conn, sockindex);
+#endif
+
+ connssl->connecting_state = ssl_connect_done;
+ return CURLE_OK;
+}
+
+static Curl_recv sectransp_recv;
+static Curl_send sectransp_send;
+
+static CURLcode
+sectransp_connect_common(struct connectdata *conn,
+ int sockindex,
+ bool nonblocking,
+ bool *done)
+{
+ CURLcode result;
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ curl_socket_t sockfd = conn->sock[sockindex];
+ 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 */
+ const timediff_t 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;
+ }
+
+ result = sectransp_connect_step1(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 allowed time left */
+ const timediff_t 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 done nonblocking 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 = sectransp_connect_step2(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 = sectransp_connect_step3(conn, sockindex);
+ if(result)
+ return result;
+ }
+
+ if(ssl_connect_done == connssl->connecting_state) {
+ connssl->state = ssl_connection_complete;
+ conn->recv[sockindex] = sectransp_recv;
+ conn->send[sockindex] = sectransp_send;
+ *done = TRUE;
+ }
+ else
+ *done = FALSE;
+
+ /* Reset our connect state machine */
+ connssl->connecting_state = ssl_connect_1;
+
+ return CURLE_OK;
+}
+
+static CURLcode Curl_sectransp_connect_nonblocking(struct connectdata *conn,
+ int sockindex, bool *done)
+{
+ return sectransp_connect_common(conn, sockindex, TRUE, done);
+}
+
+static CURLcode Curl_sectransp_connect(struct connectdata *conn, int sockindex)
+{
+ CURLcode result;
+ bool done = FALSE;
+
+ result = sectransp_connect_common(conn, sockindex, FALSE, &done);
+
+ if(result)
+ return result;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+static void Curl_sectransp_close(struct connectdata *conn, int sockindex)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+
+ if(backend->ssl_ctx) {
+ (void)SSLClose(backend->ssl_ctx);
+#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
+ if(SSLCreateContext != NULL)
+ CFRelease(backend->ssl_ctx);
+#if CURL_SUPPORT_MAC_10_8
+ else
+ (void)SSLDisposeContext(backend->ssl_ctx);
+#endif /* CURL_SUPPORT_MAC_10_8 */
+#else
+ (void)SSLDisposeContext(backend->ssl_ctx);
+#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */
+ backend->ssl_ctx = NULL;
+ }
+ backend->ssl_sockfd = 0;
+}
+
+static int Curl_sectransp_shutdown(struct connectdata *conn, int sockindex)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ struct Curl_easy *data = conn->data;
+ ssize_t nread;
+ int what;
+ int rc;
+ char buf[120];
+
+ if(!backend->ssl_ctx)
+ return 0;
+
+#ifndef CURL_DISABLE_FTP
+ if(data->set.ftp_ccc != CURLFTPSSL_CCC_ACTIVE)
+ return 0;
+#endif
+
+ Curl_sectransp_close(conn, sockindex);
+
+ rc = 0;
+
+ what = SOCKET_READABLE(conn->sock[sockindex], SSL_SHUTDOWN_TIMEOUT);
+
+ for(;;) {
+ 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 SSL_Read now, so use read(). */
+
+ nread = read(conn->sock[sockindex], buf, sizeof(buf));
+
+ if(nread < 0) {
+ failf(data, "read: %s", strerror(errno));
+ rc = -1;
+ }
+
+ if(nread <= 0)
+ break;
+
+ what = SOCKET_READABLE(conn->sock[sockindex], 0);
+ }
+
+ return rc;
+}
+
+static void Curl_sectransp_session_free(void *ptr)
+{
+ /* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a
+ cached session ID inside the Security framework. There is a private
+ function that does this, but I don't want to have to explain to you why I
+ got your application rejected from the App Store due to the use of a
+ private API, so the best we can do is free up our own char array that we
+ created way back in sectransp_connect_step1... */
+ Curl_safefree(ptr);
+}
+
+static size_t Curl_sectransp_version(char *buffer, size_t size)
+{
+ return msnprintf(buffer, size, "SecureTransport");
+}
+
+/*
+ * This function uses SSLGetSessionState 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 Curl_sectransp_check_cxn(struct connectdata *conn)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
+ struct ssl_backend_data *backend = connssl->backend;
+ OSStatus err;
+ SSLSessionState state;
+
+ if(backend->ssl_ctx) {
+ err = SSLGetSessionState(backend->ssl_ctx, &state);
+ if(err == noErr)
+ return state == kSSLConnected || state == kSSLHandshake;
+ return -1;
+ }
+ return 0;
+}
+
+static bool Curl_sectransp_data_pending(const struct connectdata *conn,
+ int connindex)
+{
+ const struct ssl_connect_data *connssl = &conn->ssl[connindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ OSStatus err;
+ size_t buffer;
+
+ if(backend->ssl_ctx) { /* SSL is in use */
+ err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer);
+ if(err == noErr)
+ return buffer > 0UL;
+ return false;
+ }
+ else
+ return false;
+}
+
+static CURLcode Curl_sectransp_random(struct Curl_easy *data UNUSED_PARAM,
+ unsigned char *entropy, size_t length)
+{
+ /* arc4random_buf() isn't available on cats older than Lion, so let's
+ do this manually for the benefit of the older cats. */
+ size_t i;
+ u_int32_t random_number = 0;
+
+ (void)data;
+
+ for(i = 0 ; i < length ; i++) {
+ if(i % sizeof(u_int32_t) == 0)
+ random_number = arc4random();
+ entropy[i] = random_number & 0xFF;
+ random_number >>= 8;
+ }
+ i = random_number = 0;
+ return CURLE_OK;
+}
+
+static CURLcode Curl_sectransp_md5sum(unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *md5sum, /* output */
+ size_t md5len)
+{
+ (void)md5len;
+ (void)CC_MD5(tmp, (CC_LONG)tmplen, md5sum);
+ return CURLE_OK;
+}
+
+static CURLcode Curl_sectransp_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum, /* output */
+ size_t sha256len)
+{
+ assert(sha256len >= CURL_SHA256_DIGEST_LENGTH);
+ (void)CC_SHA256(tmp, (CC_LONG)tmplen, sha256sum);
+ return CURLE_OK;
+}
+
+static bool Curl_sectransp_false_start(void)
+{
+#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7
+ if(SSLSetSessionOption != NULL)
+ return TRUE;
+#endif
+ return FALSE;
+}
+
+static ssize_t sectransp_send(struct connectdata *conn,
+ int sockindex,
+ const void *mem,
+ size_t len,
+ CURLcode *curlcode)
+{
+ /*struct Curl_easy *data = conn->data;*/
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ size_t processed = 0UL;
+ OSStatus err;
+
+ /* The SSLWrite() function works a little differently than expected. The
+ fourth argument (processed) is currently documented in Apple's
+ documentation as: "On return, the length, in bytes, of the data actually
+ written."
+
+ Now, one could interpret that as "written to the socket," but actually,
+ it returns the amount of data that was written to a buffer internal to
+ the SSLContextRef instead. So it's possible for SSLWrite() to return
+ errSSLWouldBlock and a number of bytes "written" because those bytes were
+ encrypted and written to a buffer, not to the socket.
+
+ So if this happens, then we need to keep calling SSLWrite() over and
+ over again with no new data until it quits returning errSSLWouldBlock. */
+
+ /* Do we have buffered data to write from the last time we were called? */
+ if(backend->ssl_write_buffered_length) {
+ /* Write the buffered data: */
+ err = SSLWrite(backend->ssl_ctx, NULL, 0UL, &processed);
+ switch(err) {
+ case noErr:
+ /* processed is always going to be 0 because we didn't write to
+ the buffer, so return how much was written to the socket */
+ processed = backend->ssl_write_buffered_length;
+ backend->ssl_write_buffered_length = 0UL;
+ break;
+ case errSSLWouldBlock: /* argh, try again */
+ *curlcode = CURLE_AGAIN;
+ return -1L;
+ default:
+ failf(conn->data, "SSLWrite() returned error %d", err);
+ *curlcode = CURLE_SEND_ERROR;
+ return -1L;
+ }
+ }
+ else {
+ /* We've got new data to write: */
+ err = SSLWrite(backend->ssl_ctx, mem, len, &processed);
+ if(err != noErr) {
+ switch(err) {
+ case errSSLWouldBlock:
+ /* Data was buffered but not sent, we have to tell the caller
+ to try sending again, and remember how much was buffered */
+ backend->ssl_write_buffered_length = len;
+ *curlcode = CURLE_AGAIN;
+ return -1L;
+ default:
+ failf(conn->data, "SSLWrite() returned error %d", err);
+ *curlcode = CURLE_SEND_ERROR;
+ return -1L;
+ }
+ }
+ }
+ return (ssize_t)processed;
+}
+
+static ssize_t sectransp_recv(struct connectdata *conn,
+ int num,
+ char *buf,
+ size_t buffersize,
+ CURLcode *curlcode)
+{
+ /*struct Curl_easy *data = conn->data;*/
+ struct ssl_connect_data *connssl = &conn->ssl[num];
+ struct ssl_backend_data *backend = connssl->backend;
+ size_t processed = 0UL;
+ OSStatus err;
+
+ again:
+ err = SSLRead(backend->ssl_ctx, buf, buffersize, &processed);
+
+ if(err != noErr) {
+ switch(err) {
+ case errSSLWouldBlock: /* return how much we read (if anything) */
+ if(processed)
+ return (ssize_t)processed;
+ *curlcode = CURLE_AGAIN;
+ return -1L;
+ break;
+
+ /* errSSLClosedGraceful - server gracefully shut down the SSL session
+ errSSLClosedNoNotify - server hung up on us instead of sending a
+ closure alert notice, read() is returning 0
+ Either way, inform the caller that the server disconnected. */
+ case errSSLClosedGraceful:
+ case errSSLClosedNoNotify:
+ *curlcode = CURLE_OK;
+ return -1L;
+ break;
+
+ /* The below is errSSLPeerAuthCompleted; it's not defined in
+ Leopard's headers */
+ case -9841:
+ if(SSL_CONN_CONFIG(CAfile) && SSL_CONN_CONFIG(verifypeer)) {
+ CURLcode result = verify_cert(SSL_CONN_CONFIG(CAfile), conn->data,
+ backend->ssl_ctx);
+ if(result)
+ return result;
+ }
+ goto again;
+ default:
+ failf(conn->data, "SSLRead() return error %d", err);
+ *curlcode = CURLE_RECV_ERROR;
+ return -1L;
+ break;
+ }
+ }
+ return (ssize_t)processed;
+}
+
+static void *Curl_sectransp_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ struct ssl_backend_data *backend = connssl->backend;
+ (void)info;
+ return backend->ssl_ctx;
+}
+
+const struct Curl_ssl Curl_ssl_sectransp = {
+ { CURLSSLBACKEND_SECURETRANSPORT, "secure-transport" }, /* info */
+
+#ifdef SECTRANSP_PINNEDPUBKEY
+ SSLSUPP_PINNEDPUBKEY,
+#else
+ 0,
+#endif /* SECTRANSP_PINNEDPUBKEY */
+
+ sizeof(struct ssl_backend_data),
+
+ Curl_none_init, /* init */
+ Curl_none_cleanup, /* cleanup */
+ Curl_sectransp_version, /* version */
+ Curl_sectransp_check_cxn, /* check_cxn */
+ Curl_sectransp_shutdown, /* shutdown */
+ Curl_sectransp_data_pending, /* data_pending */
+ Curl_sectransp_random, /* random */
+ Curl_none_cert_status_request, /* cert_status_request */
+ Curl_sectransp_connect, /* connect */
+ Curl_sectransp_connect_nonblocking, /* connect_nonblocking */
+ Curl_sectransp_get_internals, /* get_internals */
+ Curl_sectransp_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ Curl_sectransp_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_sectransp_false_start, /* false_start */
+ Curl_sectransp_md5sum, /* md5sum */
+ Curl_sectransp_sha256sum /* sha256sum */
+};
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif /* USE_SECTRANSP */
diff --git a/contrib/libs/curl/lib/vtls/sectransp.h b/contrib/libs/curl/lib/vtls/sectransp.h
new file mode 100644
index 0000000000..0febd6613a
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/sectransp.h
@@ -0,0 +1,32 @@
+#ifndef HEADER_CURL_SECTRANSP_H
+#define HEADER_CURL_SECTRANSP_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2012 - 2014, Nick Zitzmann, <nickzman@gmail.com>.
+ * Copyright (C) 2012 - 2020, 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_SECTRANSP
+
+extern const struct Curl_ssl Curl_ssl_sectransp;
+
+#endif /* USE_SECTRANSP */
+#endif /* HEADER_CURL_SECTRANSP_H */
diff --git a/contrib/libs/curl/lib/vtls/vtls.c b/contrib/libs/curl/lib/vtls/vtls.c
new file mode 100644
index 0000000000..3bd51fdaf2
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/vtls.c
@@ -0,0 +1,1426 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, 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.
+ *
+ ***************************************************************************/
+
+/* This file is for implementing all "generic" SSL functions that all libcurl
+ internals should use. It is then responsible for calling the proper
+ "backend" function.
+
+ SSL-functions in libcurl should call functions in this source file, and not
+ to any specific SSL-layer.
+
+ Curl_ssl_ - prefix for generic ones
+
+ Note that this source code uses the functions of the configured SSL
+ backend via the global Curl_ssl instance.
+
+ "SSL/TLS Strong Encryption: An Introduction"
+ https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html
+*/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "urldata.h"
+
+#include "vtls.h" /* generic SSL protos etc */
+#include "slist.h"
+#include "sendf.h"
+#include "strcase.h"
+#include "url.h"
+#include "progress.h"
+#include "share.h"
+#include "multiif.h"
+#include "timeval.h"
+#include "curl_md5.h"
+#include "warnless.h"
+#include "curl_base64.h"
+#include "curl_printf.h"
+#include "strdup.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* convenience macro to check if this handle is using a shared SSL session */
+#define SSLSESSION_SHARED(data) (data->share && \
+ (data->share->specifier & \
+ (1<<CURL_LOCK_DATA_SSL_SESSION)))
+
+#define CLONE_STRING(var) \
+ if(source->var) { \
+ dest->var = strdup(source->var); \
+ if(!dest->var) \
+ return FALSE; \
+ } \
+ else \
+ dest->var = NULL;
+
+#define CLONE_BLOB(var) \
+ if(blobdup(&dest->var, source->var)) \
+ return FALSE;
+
+static CURLcode blobdup(struct curl_blob **dest,
+ struct curl_blob *src)
+{
+ DEBUGASSERT(dest);
+ DEBUGASSERT(!*dest);
+ if(src) {
+ /* only if there's data to dupe! */
+ struct curl_blob *d;
+ d = malloc(sizeof(struct curl_blob) + src->len);
+ if(!d)
+ return CURLE_OUT_OF_MEMORY;
+ d->len = src->len;
+ /* Always duplicate because the connection may survive longer than the
+ handle that passed in the blob. */
+ d->flags = CURL_BLOB_COPY;
+ d->data = (void *)((char *)d + sizeof(struct curl_blob));
+ memcpy(d->data, src->data, src->len);
+ *dest = d;
+ }
+ return CURLE_OK;
+}
+
+/* returns TRUE if the blobs are identical */
+static bool blobcmp(struct curl_blob *first, struct curl_blob *second)
+{
+ if(!first && !second) /* both are NULL */
+ return TRUE;
+ if(!first || !second) /* one is NULL */
+ return FALSE;
+ if(first->len != second->len) /* different sizes */
+ return FALSE;
+ return !memcmp(first->data, second->data, first->len); /* same data */
+}
+
+bool
+Curl_ssl_config_matches(struct ssl_primary_config *data,
+ struct ssl_primary_config *needle)
+{
+ if((data->version == needle->version) &&
+ (data->version_max == needle->version_max) &&
+ (data->verifypeer == needle->verifypeer) &&
+ (data->verifyhost == needle->verifyhost) &&
+ (data->verifystatus == needle->verifystatus) &&
+ blobcmp(data->cert_blob, needle->cert_blob) &&
+ Curl_safe_strcasecompare(data->CApath, needle->CApath) &&
+ Curl_safe_strcasecompare(data->CAfile, needle->CAfile) &&
+ Curl_safe_strcasecompare(data->clientcert, needle->clientcert) &&
+ Curl_safe_strcasecompare(data->random_file, needle->random_file) &&
+ Curl_safe_strcasecompare(data->egdsocket, needle->egdsocket) &&
+ Curl_safe_strcasecompare(data->cipher_list, needle->cipher_list) &&
+ Curl_safe_strcasecompare(data->cipher_list13, needle->cipher_list13) &&
+ Curl_safe_strcasecompare(data->curves, needle->curves) &&
+ Curl_safe_strcasecompare(data->pinned_key, needle->pinned_key))
+ return TRUE;
+
+ return FALSE;
+}
+
+bool
+Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
+ struct ssl_primary_config *dest)
+{
+ dest->version = source->version;
+ dest->version_max = source->version_max;
+ dest->verifypeer = source->verifypeer;
+ dest->verifyhost = source->verifyhost;
+ dest->verifystatus = source->verifystatus;
+ dest->sessionid = source->sessionid;
+
+ CLONE_BLOB(cert_blob);
+ CLONE_STRING(CApath);
+ CLONE_STRING(CAfile);
+ CLONE_STRING(clientcert);
+ CLONE_STRING(random_file);
+ CLONE_STRING(egdsocket);
+ CLONE_STRING(cipher_list);
+ CLONE_STRING(cipher_list13);
+ CLONE_STRING(pinned_key);
+ CLONE_STRING(curves);
+
+ return TRUE;
+}
+
+void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc)
+{
+ Curl_safefree(sslc->CApath);
+ Curl_safefree(sslc->CAfile);
+ Curl_safefree(sslc->clientcert);
+ Curl_safefree(sslc->random_file);
+ Curl_safefree(sslc->egdsocket);
+ Curl_safefree(sslc->cipher_list);
+ Curl_safefree(sslc->cipher_list13);
+ Curl_safefree(sslc->pinned_key);
+ Curl_safefree(sslc->cert_blob);
+ Curl_safefree(sslc->curves);
+}
+
+#ifdef USE_SSL
+static int multissl_init(const struct Curl_ssl *backend);
+#endif
+
+int Curl_ssl_backend(void)
+{
+#ifdef USE_SSL
+ multissl_init(NULL);
+ return Curl_ssl->info.id;
+#else
+ return (int)CURLSSLBACKEND_NONE;
+#endif
+}
+
+#ifdef USE_SSL
+
+/* "global" init done? */
+static bool init_ssl = FALSE;
+
+/**
+ * Global SSL init
+ *
+ * @retval 0 error initializing SSL
+ * @retval 1 SSL initialized successfully
+ */
+int Curl_ssl_init(void)
+{
+ /* make sure this is only done once */
+ if(init_ssl)
+ return 1;
+ init_ssl = TRUE; /* never again */
+
+ return Curl_ssl->init();
+}
+
+#if defined(CURL_WITH_MULTI_SSL)
+static const struct Curl_ssl Curl_ssl_multi;
+#endif
+
+/* Global cleanup */
+void Curl_ssl_cleanup(void)
+{
+ if(init_ssl) {
+ /* only cleanup if we did a previous init */
+ Curl_ssl->cleanup();
+#if defined(CURL_WITH_MULTI_SSL)
+ Curl_ssl = &Curl_ssl_multi;
+#endif
+ init_ssl = FALSE;
+ }
+}
+
+static bool ssl_prefs_check(struct Curl_easy *data)
+{
+ /* check for CURLOPT_SSLVERSION invalid parameter value */
+ const long sslver = data->set.ssl.primary.version;
+ if((sslver < 0) || (sslver >= CURL_SSLVERSION_LAST)) {
+ failf(data, "Unrecognized parameter value passed via CURLOPT_SSLVERSION");
+ return FALSE;
+ }
+
+ switch(data->set.ssl.primary.version_max) {
+ case CURL_SSLVERSION_MAX_NONE:
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ break;
+
+ default:
+ if((data->set.ssl.primary.version_max >> 16) < sslver) {
+ failf(data, "CURL_SSLVERSION_MAX incompatible with CURL_SSLVERSION");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+#ifndef CURL_DISABLE_PROXY
+static CURLcode
+ssl_connect_init_proxy(struct connectdata *conn, int sockindex)
+{
+ DEBUGASSERT(conn->bits.proxy_ssl_connected[sockindex]);
+ if(ssl_connection_complete == conn->ssl[sockindex].state &&
+ !conn->proxy_ssl[sockindex].use) {
+ struct ssl_backend_data *pbdata;
+
+ if(!(Curl_ssl->supports & SSLSUPP_HTTPS_PROXY))
+ return CURLE_NOT_BUILT_IN;
+
+ /* The pointers to the ssl backend data, which is opaque here, are swapped
+ rather than move the contents. */
+ pbdata = conn->proxy_ssl[sockindex].backend;
+ conn->proxy_ssl[sockindex] = conn->ssl[sockindex];
+
+ memset(&conn->ssl[sockindex], 0, sizeof(conn->ssl[sockindex]));
+ memset(pbdata, 0, Curl_ssl->sizeof_ssl_backend_data);
+
+ conn->ssl[sockindex].backend = pbdata;
+ }
+ return CURLE_OK;
+}
+#endif
+
+CURLcode
+Curl_ssl_connect(struct connectdata *conn, int sockindex)
+{
+ CURLcode result;
+
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.proxy_ssl_connected[sockindex]) {
+ result = ssl_connect_init_proxy(conn, sockindex);
+ if(result)
+ return result;
+ }
+#endif
+
+ if(!ssl_prefs_check(conn->data))
+ return CURLE_SSL_CONNECT_ERROR;
+
+ /* mark this is being ssl-enabled from here on. */
+ conn->ssl[sockindex].use = TRUE;
+ conn->ssl[sockindex].state = ssl_connection_negotiating;
+
+ result = Curl_ssl->connect_blocking(conn, sockindex);
+
+ if(!result)
+ Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
+
+ return result;
+}
+
+CURLcode
+Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex,
+ bool *done)
+{
+ CURLcode result;
+#ifndef CURL_DISABLE_PROXY
+ if(conn->bits.proxy_ssl_connected[sockindex]) {
+ result = ssl_connect_init_proxy(conn, sockindex);
+ if(result)
+ return result;
+ }
+#endif
+ if(!ssl_prefs_check(conn->data))
+ return CURLE_SSL_CONNECT_ERROR;
+
+ /* mark this is being ssl requested from here on. */
+ conn->ssl[sockindex].use = TRUE;
+ result = Curl_ssl->connect_nonblocking(conn, sockindex, done);
+ if(!result && *done)
+ Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
+ return result;
+}
+
+/*
+ * Lock shared SSL session data
+ */
+void Curl_ssl_sessionid_lock(struct connectdata *conn)
+{
+ if(SSLSESSION_SHARED(conn->data))
+ Curl_share_lock(conn->data,
+ CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
+}
+
+/*
+ * Unlock shared SSL session data
+ */
+void Curl_ssl_sessionid_unlock(struct connectdata *conn)
+{
+ if(SSLSESSION_SHARED(conn->data))
+ Curl_share_unlock(conn->data, CURL_LOCK_DATA_SSL_SESSION);
+}
+
+/*
+ * Check if there's a session ID for the given connection in the cache, and if
+ * there's one suitable, it is provided. Returns TRUE when no entry matched.
+ */
+bool Curl_ssl_getsessionid(struct connectdata *conn,
+ void **ssl_sessionid,
+ size_t *idsize, /* set 0 if unknown */
+ int sockindex)
+{
+ struct Curl_ssl_session *check;
+ struct Curl_easy *data = conn->data;
+ size_t i;
+ long *general_age;
+ bool no_match = TRUE;
+
+#ifndef CURL_DISABLE_PROXY
+ const bool isProxy = CONNECT_PROXY_SSL();
+ struct ssl_primary_config * const ssl_config = isProxy ?
+ &conn->proxy_ssl_config :
+ &conn->ssl_config;
+ const char * const name = isProxy ?
+ conn->http_proxy.host.name : conn->host.name;
+ int port = isProxy ? (int)conn->port : conn->remote_port;
+#else
+ /* no proxy support */
+ struct ssl_primary_config * const ssl_config = &conn->ssl_config;
+ const char * const name = conn->host.name;
+ int port = conn->remote_port;
+ (void)sockindex;
+#endif
+ *ssl_sessionid = NULL;
+
+ DEBUGASSERT(SSL_SET_OPTION(primary.sessionid));
+
+ if(!SSL_SET_OPTION(primary.sessionid))
+ /* session ID re-use is disabled */
+ return TRUE;
+
+ /* Lock if shared */
+ if(SSLSESSION_SHARED(data))
+ general_age = &data->share->sessionage;
+ else
+ general_age = &data->state.sessionage;
+
+ for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
+ check = &data->state.session[i];
+ if(!check->sessionid)
+ /* not session ID means blank entry */
+ continue;
+ if(strcasecompare(name, check->name) &&
+ ((!conn->bits.conn_to_host && !check->conn_to_host) ||
+ (conn->bits.conn_to_host && check->conn_to_host &&
+ strcasecompare(conn->conn_to_host.name, check->conn_to_host))) &&
+ ((!conn->bits.conn_to_port && check->conn_to_port == -1) ||
+ (conn->bits.conn_to_port && check->conn_to_port != -1 &&
+ conn->conn_to_port == check->conn_to_port)) &&
+ (port == check->remote_port) &&
+ strcasecompare(conn->handler->scheme, check->scheme) &&
+ Curl_ssl_config_matches(ssl_config, &check->ssl_config)) {
+ /* yes, we have a session ID! */
+ (*general_age)++; /* increase general age */
+ check->age = *general_age; /* set this as used in this age */
+ *ssl_sessionid = check->sessionid;
+ if(idsize)
+ *idsize = check->idsize;
+ no_match = FALSE;
+ break;
+ }
+ }
+
+ return no_match;
+}
+
+/*
+ * Kill a single session ID entry in the cache.
+ */
+void Curl_ssl_kill_session(struct Curl_ssl_session *session)
+{
+ if(session->sessionid) {
+ /* defensive check */
+
+ /* free the ID the SSL-layer specific way */
+ Curl_ssl->session_free(session->sessionid);
+
+ session->sessionid = NULL;
+ session->age = 0; /* fresh */
+
+ Curl_free_primary_ssl_config(&session->ssl_config);
+
+ Curl_safefree(session->name);
+ Curl_safefree(session->conn_to_host);
+ }
+}
+
+/*
+ * Delete the given session ID from the cache.
+ */
+void Curl_ssl_delsessionid(struct connectdata *conn, void *ssl_sessionid)
+{
+ size_t i;
+ struct Curl_easy *data = conn->data;
+
+ for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
+ struct Curl_ssl_session *check = &data->state.session[i];
+
+ if(check->sessionid == ssl_sessionid) {
+ Curl_ssl_kill_session(check);
+ break;
+ }
+ }
+}
+
+/*
+ * Store session id in the session cache. The ID passed on to this function
+ * must already have been extracted and allocated the proper way for the SSL
+ * layer. Curl_XXXX_session_free() will be called to free/kill the session ID
+ * later on.
+ */
+CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
+ void *ssl_sessionid,
+ size_t idsize,
+ int sockindex)
+{
+ size_t i;
+ struct Curl_easy *data = conn->data; /* the mother of all structs */
+ struct Curl_ssl_session *store = &data->state.session[0];
+ long oldest_age = data->state.session[0].age; /* zero if unused */
+ char *clone_host;
+ char *clone_conn_to_host;
+ int conn_to_port;
+ long *general_age;
+#ifndef CURL_DISABLE_PROXY
+ const bool isProxy = CONNECT_PROXY_SSL();
+ struct ssl_primary_config * const ssl_config = isProxy ?
+ &conn->proxy_ssl_config :
+ &conn->ssl_config;
+ const char *hostname = isProxy ? conn->http_proxy.host.name :
+ conn->host.name;
+#else
+ /* proxy support disabled */
+ const bool isProxy = FALSE;
+ struct ssl_primary_config * const ssl_config = &conn->ssl_config;
+ const char *hostname = conn->host.name;
+ (void)sockindex;
+#endif
+ DEBUGASSERT(SSL_SET_OPTION(primary.sessionid));
+
+ clone_host = strdup(hostname);
+ if(!clone_host)
+ return CURLE_OUT_OF_MEMORY; /* bail out */
+
+ if(conn->bits.conn_to_host) {
+ clone_conn_to_host = strdup(conn->conn_to_host.name);
+ if(!clone_conn_to_host) {
+ free(clone_host);
+ return CURLE_OUT_OF_MEMORY; /* bail out */
+ }
+ }
+ else
+ clone_conn_to_host = NULL;
+
+ if(conn->bits.conn_to_port)
+ conn_to_port = conn->conn_to_port;
+ else
+ conn_to_port = -1;
+
+ /* Now we should add the session ID and the host name to the cache, (remove
+ the oldest if necessary) */
+
+ /* If using shared SSL session, lock! */
+ if(SSLSESSION_SHARED(data)) {
+ general_age = &data->share->sessionage;
+ }
+ else {
+ general_age = &data->state.sessionage;
+ }
+
+ /* find an empty slot for us, or find the oldest */
+ for(i = 1; (i < data->set.general_ssl.max_ssl_sessions) &&
+ data->state.session[i].sessionid; i++) {
+ if(data->state.session[i].age < oldest_age) {
+ oldest_age = data->state.session[i].age;
+ store = &data->state.session[i];
+ }
+ }
+ if(i == data->set.general_ssl.max_ssl_sessions)
+ /* cache is full, we must "kill" the oldest entry! */
+ Curl_ssl_kill_session(store);
+ else
+ store = &data->state.session[i]; /* use this slot */
+
+ /* now init the session struct wisely */
+ store->sessionid = ssl_sessionid;
+ store->idsize = idsize;
+ store->age = *general_age; /* set current age */
+ /* free it if there's one already present */
+ free(store->name);
+ free(store->conn_to_host);
+ store->name = clone_host; /* clone host name */
+ store->conn_to_host = clone_conn_to_host; /* clone connect to host name */
+ store->conn_to_port = conn_to_port; /* connect to port number */
+ /* port number */
+ store->remote_port = isProxy ? (int)conn->port : conn->remote_port;
+ store->scheme = conn->handler->scheme;
+
+ if(!Curl_clone_primary_ssl_config(ssl_config, &store->ssl_config)) {
+ Curl_free_primary_ssl_config(&store->ssl_config);
+ store->sessionid = NULL; /* let caller free sessionid */
+ free(clone_host);
+ free(clone_conn_to_host);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ return CURLE_OK;
+}
+
+
+void Curl_ssl_close_all(struct Curl_easy *data)
+{
+ /* kill the session ID cache if not shared */
+ if(data->state.session && !SSLSESSION_SHARED(data)) {
+ size_t i;
+ for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++)
+ /* the single-killer function handles empty table slots */
+ Curl_ssl_kill_session(&data->state.session[i]);
+
+ /* free the cache data */
+ Curl_safefree(data->state.session);
+ }
+
+ Curl_ssl->close_all(data);
+}
+
+#if defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \
+ defined(USE_SECTRANSP) || defined(USE_NSS) || \
+ defined(USE_MBEDTLS) || defined(USE_WOLFSSL) || defined(USE_BEARSSL)
+int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
+
+ if(connssl->connecting_state == ssl_connect_2_writing) {
+ /* write mode */
+ socks[0] = conn->sock[FIRSTSOCKET];
+ return GETSOCK_WRITESOCK(0);
+ }
+ if(connssl->connecting_state == ssl_connect_2_reading) {
+ /* read mode */
+ socks[0] = conn->sock[FIRSTSOCKET];
+ return GETSOCK_READSOCK(0);
+ }
+
+ return GETSOCK_BLANK;
+}
+#else
+int Curl_ssl_getsock(struct connectdata *conn,
+ curl_socket_t *socks)
+{
+ (void)conn;
+ (void)socks;
+ return GETSOCK_BLANK;
+}
+/* USE_OPENSSL || USE_GNUTLS || USE_SCHANNEL || USE_SECTRANSP || USE_NSS */
+#endif
+
+void Curl_ssl_close(struct connectdata *conn, int sockindex)
+{
+ DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
+ Curl_ssl->close_one(conn, sockindex);
+ conn->ssl[sockindex].state = ssl_connection_none;
+}
+
+CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex)
+{
+ if(Curl_ssl->shut_down(conn, sockindex))
+ return CURLE_SSL_SHUTDOWN_FAILED;
+
+ conn->ssl[sockindex].use = FALSE; /* get back to ordinary socket usage */
+ conn->ssl[sockindex].state = ssl_connection_none;
+
+ conn->recv[sockindex] = Curl_recv_plain;
+ conn->send[sockindex] = Curl_send_plain;
+
+ return CURLE_OK;
+}
+
+/* Selects an SSL crypto engine
+ */
+CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine)
+{
+ return Curl_ssl->set_engine(data, engine);
+}
+
+/* Selects the default SSL crypto engine
+ */
+CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data)
+{
+ return Curl_ssl->set_engine_default(data);
+}
+
+/* Return list of OpenSSL crypto engine names. */
+struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data)
+{
+ return Curl_ssl->engines_list(data);
+}
+
+/*
+ * This sets up a session ID cache to the specified size. Make sure this code
+ * is agnostic to what underlying SSL technology we use.
+ */
+CURLcode Curl_ssl_initsessions(struct Curl_easy *data, size_t amount)
+{
+ struct Curl_ssl_session *session;
+
+ if(data->state.session)
+ /* this is just a precaution to prevent multiple inits */
+ return CURLE_OK;
+
+ session = calloc(amount, sizeof(struct Curl_ssl_session));
+ if(!session)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* store the info in the SSL section */
+ data->set.general_ssl.max_ssl_sessions = amount;
+ data->state.session = session;
+ data->state.sessionage = 1; /* this is brand new */
+ return CURLE_OK;
+}
+
+static size_t Curl_multissl_version(char *buffer, size_t size);
+
+size_t Curl_ssl_version(char *buffer, size_t size)
+{
+#ifdef CURL_WITH_MULTI_SSL
+ return Curl_multissl_version(buffer, size);
+#else
+ return Curl_ssl->version(buffer, size);
+#endif
+}
+
+/*
+ * This function tries 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
+ */
+int Curl_ssl_check_cxn(struct connectdata *conn)
+{
+ return Curl_ssl->check_cxn(conn);
+}
+
+bool Curl_ssl_data_pending(const struct connectdata *conn,
+ int connindex)
+{
+ return Curl_ssl->data_pending(conn, connindex);
+}
+
+void Curl_ssl_free_certinfo(struct Curl_easy *data)
+{
+ struct curl_certinfo *ci = &data->info.certs;
+
+ if(ci->num_of_certs) {
+ /* free all individual lists used */
+ int i;
+ for(i = 0; i<ci->num_of_certs; i++) {
+ curl_slist_free_all(ci->certinfo[i]);
+ ci->certinfo[i] = NULL;
+ }
+
+ free(ci->certinfo); /* free the actual array too */
+ ci->certinfo = NULL;
+ ci->num_of_certs = 0;
+ }
+}
+
+CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num)
+{
+ struct curl_certinfo *ci = &data->info.certs;
+ struct curl_slist **table;
+
+ /* Free any previous certificate information structures */
+ Curl_ssl_free_certinfo(data);
+
+ /* Allocate the required certificate information structures */
+ table = calloc((size_t) num, sizeof(struct curl_slist *));
+ if(!table)
+ return CURLE_OUT_OF_MEMORY;
+
+ ci->num_of_certs = num;
+ ci->certinfo = table;
+
+ return CURLE_OK;
+}
+
+/*
+ * 'value' is NOT a null-terminated string
+ */
+CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
+ int certnum,
+ const char *label,
+ const char *value,
+ size_t valuelen)
+{
+ struct curl_certinfo *ci = &data->info.certs;
+ char *output;
+ struct curl_slist *nl;
+ CURLcode result = CURLE_OK;
+ size_t labellen = strlen(label);
+ size_t outlen = labellen + 1 + valuelen + 1; /* label:value\0 */
+
+ output = malloc(outlen);
+ if(!output)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* sprintf the label and colon */
+ msnprintf(output, outlen, "%s:", label);
+
+ /* memcpy the value (it might not be null-terminated) */
+ memcpy(&output[labellen + 1], value, valuelen);
+
+ /* null-terminate the output */
+ output[labellen + 1 + valuelen] = 0;
+
+ nl = Curl_slist_append_nodup(ci->certinfo[certnum], output);
+ if(!nl) {
+ free(output);
+ curl_slist_free_all(ci->certinfo[certnum]);
+ result = CURLE_OUT_OF_MEMORY;
+ }
+
+ ci->certinfo[certnum] = nl;
+ return result;
+}
+
+/*
+ * This is a convenience function for push_certinfo_len that takes a zero
+ * terminated value.
+ */
+CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data,
+ int certnum,
+ const char *label,
+ const char *value)
+{
+ size_t valuelen = strlen(value);
+
+ return Curl_ssl_push_certinfo_len(data, certnum, label, value, valuelen);
+}
+
+CURLcode Curl_ssl_random(struct Curl_easy *data,
+ unsigned char *entropy,
+ size_t length)
+{
+ return Curl_ssl->random(data, entropy, length);
+}
+
+/*
+ * Public key pem to der conversion
+ */
+
+static CURLcode pubkey_pem_to_der(const char *pem,
+ unsigned char **der, size_t *der_len)
+{
+ char *stripped_pem, *begin_pos, *end_pos;
+ size_t pem_count, stripped_pem_count = 0, pem_len;
+ CURLcode result;
+
+ /* if no pem, exit. */
+ if(!pem)
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ begin_pos = strstr(pem, "-----BEGIN PUBLIC KEY-----");
+ if(!begin_pos)
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ pem_count = begin_pos - pem;
+ /* Invalid if not at beginning AND not directly following \n */
+ if(0 != pem_count && '\n' != pem[pem_count - 1])
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ /* 26 is length of "-----BEGIN PUBLIC KEY-----" */
+ pem_count += 26;
+
+ /* Invalid if not directly following \n */
+ end_pos = strstr(pem + pem_count, "\n-----END PUBLIC KEY-----");
+ if(!end_pos)
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ pem_len = end_pos - pem;
+
+ stripped_pem = malloc(pem_len - pem_count + 1);
+ if(!stripped_pem)
+ return CURLE_OUT_OF_MEMORY;
+
+ /*
+ * Here we loop through the pem array one character at a time between the
+ * correct indices, and place each character that is not '\n' or '\r'
+ * into the stripped_pem array, which should represent the raw base64 string
+ */
+ while(pem_count < pem_len) {
+ if('\n' != pem[pem_count] && '\r' != pem[pem_count])
+ stripped_pem[stripped_pem_count++] = pem[pem_count];
+ ++pem_count;
+ }
+ /* Place the null terminator in the correct place */
+ stripped_pem[stripped_pem_count] = '\0';
+
+ result = Curl_base64_decode(stripped_pem, der, der_len);
+
+ Curl_safefree(stripped_pem);
+
+ return result;
+}
+
+/*
+ * Generic pinned public key check.
+ */
+
+CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
+ const char *pinnedpubkey,
+ const unsigned char *pubkey, size_t pubkeylen)
+{
+ FILE *fp;
+ unsigned char *buf = NULL, *pem_ptr = NULL;
+ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+ /* if a path wasn't specified, don't pin */
+ if(!pinnedpubkey)
+ return CURLE_OK;
+ if(!pubkey || !pubkeylen)
+ return result;
+
+ /* only do this if pinnedpubkey starts with "sha256//", length 8 */
+ if(strncmp(pinnedpubkey, "sha256//", 8) == 0) {
+ CURLcode encode;
+ size_t encodedlen, pinkeylen;
+ char *encoded, *pinkeycopy, *begin_pos, *end_pos;
+ unsigned char *sha256sumdigest;
+
+ if(!Curl_ssl->sha256sum) {
+ /* without sha256 support, this cannot match */
+ return result;
+ }
+
+ /* compute sha256sum of public key */
+ sha256sumdigest = malloc(CURL_SHA256_DIGEST_LENGTH);
+ if(!sha256sumdigest)
+ return CURLE_OUT_OF_MEMORY;
+ encode = Curl_ssl->sha256sum(pubkey, pubkeylen,
+ sha256sumdigest, CURL_SHA256_DIGEST_LENGTH);
+
+ if(encode != CURLE_OK)
+ return encode;
+
+ encode = Curl_base64_encode(data, (char *)sha256sumdigest,
+ CURL_SHA256_DIGEST_LENGTH, &encoded,
+ &encodedlen);
+ Curl_safefree(sha256sumdigest);
+
+ if(encode)
+ return encode;
+
+ infof(data, "\t public key hash: sha256//%s\n", encoded);
+
+ /* it starts with sha256//, copy so we can modify it */
+ pinkeylen = strlen(pinnedpubkey) + 1;
+ pinkeycopy = malloc(pinkeylen);
+ if(!pinkeycopy) {
+ Curl_safefree(encoded);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ memcpy(pinkeycopy, pinnedpubkey, pinkeylen);
+ /* point begin_pos to the copy, and start extracting keys */
+ begin_pos = pinkeycopy;
+ do {
+ end_pos = strstr(begin_pos, ";sha256//");
+ /*
+ * if there is an end_pos, null terminate,
+ * otherwise it'll go to the end of the original string
+ */
+ if(end_pos)
+ end_pos[0] = '\0';
+
+ /* compare base64 sha256 digests, 8 is the length of "sha256//" */
+ if(encodedlen == strlen(begin_pos + 8) &&
+ !memcmp(encoded, begin_pos + 8, encodedlen)) {
+ result = CURLE_OK;
+ break;
+ }
+
+ /*
+ * change back the null-terminator we changed earlier,
+ * and look for next begin
+ */
+ if(end_pos) {
+ end_pos[0] = ';';
+ begin_pos = strstr(end_pos, "sha256//");
+ }
+ } while(end_pos && begin_pos);
+ Curl_safefree(encoded);
+ Curl_safefree(pinkeycopy);
+ return result;
+ }
+
+ fp = fopen(pinnedpubkey, "rb");
+ if(!fp)
+ return result;
+
+ do {
+ long filesize;
+ size_t size, pem_len;
+ CURLcode pem_read;
+
+ /* Determine the file's size */
+ if(fseek(fp, 0, SEEK_END))
+ break;
+ filesize = ftell(fp);
+ if(fseek(fp, 0, SEEK_SET))
+ break;
+ if(filesize < 0 || filesize > MAX_PINNED_PUBKEY_SIZE)
+ break;
+
+ /*
+ * if the size of our certificate is bigger than the file
+ * size then it can't match
+ */
+ size = curlx_sotouz((curl_off_t) filesize);
+ if(pubkeylen > size)
+ break;
+
+ /*
+ * Allocate buffer for the pinned key
+ * With 1 additional byte for null terminator in case of PEM key
+ */
+ buf = malloc(size + 1);
+ if(!buf)
+ break;
+
+ /* Returns number of elements read, which should be 1 */
+ if((int) fread(buf, size, 1, fp) != 1)
+ break;
+
+ /* If the sizes are the same, it can't be base64 encoded, must be der */
+ if(pubkeylen == size) {
+ if(!memcmp(pubkey, buf, pubkeylen))
+ result = CURLE_OK;
+ break;
+ }
+
+ /*
+ * Otherwise we will assume it's PEM and try to decode it
+ * after placing null terminator
+ */
+ buf[size] = '\0';
+ pem_read = pubkey_pem_to_der((const char *)buf, &pem_ptr, &pem_len);
+ /* if it wasn't read successfully, exit */
+ if(pem_read)
+ break;
+
+ /*
+ * if the size of our certificate doesn't match the size of
+ * the decoded file, they can't be the same, otherwise compare
+ */
+ if(pubkeylen == pem_len && !memcmp(pubkey, pem_ptr, pubkeylen))
+ result = CURLE_OK;
+ } while(0);
+
+ Curl_safefree(buf);
+ Curl_safefree(pem_ptr);
+ fclose(fp);
+
+ return result;
+}
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+CURLcode Curl_ssl_md5sum(unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *md5sum, /* output */
+ size_t md5len)
+{
+ return Curl_ssl->md5sum(tmp, tmplen, md5sum, md5len);
+}
+#endif
+
+/*
+ * Check whether the SSL backend supports the status_request extension.
+ */
+bool Curl_ssl_cert_status_request(void)
+{
+ return Curl_ssl->cert_status_request();
+}
+
+/*
+ * Check whether the SSL backend supports false start.
+ */
+bool Curl_ssl_false_start(void)
+{
+ return Curl_ssl->false_start();
+}
+
+/*
+ * Check whether the SSL backend supports setting TLS 1.3 cipher suites
+ */
+bool Curl_ssl_tls13_ciphersuites(void)
+{
+ return Curl_ssl->supports & SSLSUPP_TLS13_CIPHERSUITES;
+}
+
+/*
+ * Default implementations for unsupported functions.
+ */
+
+int Curl_none_init(void)
+{
+ return 1;
+}
+
+void Curl_none_cleanup(void)
+{ }
+
+int Curl_none_shutdown(struct connectdata *conn UNUSED_PARAM,
+ int sockindex UNUSED_PARAM)
+{
+ (void)conn;
+ (void)sockindex;
+ return 0;
+}
+
+int Curl_none_check_cxn(struct connectdata *conn UNUSED_PARAM)
+{
+ (void)conn;
+ return -1;
+}
+
+CURLcode Curl_none_random(struct Curl_easy *data UNUSED_PARAM,
+ unsigned char *entropy UNUSED_PARAM,
+ size_t length UNUSED_PARAM)
+{
+ (void)data;
+ (void)entropy;
+ (void)length;
+ return CURLE_NOT_BUILT_IN;
+}
+
+void Curl_none_close_all(struct Curl_easy *data UNUSED_PARAM)
+{
+ (void)data;
+}
+
+void Curl_none_session_free(void *ptr UNUSED_PARAM)
+{
+ (void)ptr;
+}
+
+bool Curl_none_data_pending(const struct connectdata *conn UNUSED_PARAM,
+ int connindex UNUSED_PARAM)
+{
+ (void)conn;
+ (void)connindex;
+ return 0;
+}
+
+bool Curl_none_cert_status_request(void)
+{
+ return FALSE;
+}
+
+CURLcode Curl_none_set_engine(struct Curl_easy *data UNUSED_PARAM,
+ const char *engine UNUSED_PARAM)
+{
+ (void)data;
+ (void)engine;
+ return CURLE_NOT_BUILT_IN;
+}
+
+CURLcode Curl_none_set_engine_default(struct Curl_easy *data UNUSED_PARAM)
+{
+ (void)data;
+ return CURLE_NOT_BUILT_IN;
+}
+
+struct curl_slist *Curl_none_engines_list(struct Curl_easy *data UNUSED_PARAM)
+{
+ (void)data;
+ return (struct curl_slist *)NULL;
+}
+
+bool Curl_none_false_start(void)
+{
+ return FALSE;
+}
+
+#ifndef CURL_DISABLE_CRYPTO_AUTH
+CURLcode Curl_none_md5sum(unsigned char *input, size_t inputlen,
+ unsigned char *md5sum, size_t md5len UNUSED_PARAM)
+{
+ struct MD5_context *MD5pw;
+
+ (void)md5len;
+
+ MD5pw = Curl_MD5_init(Curl_DIGEST_MD5);
+ if(!MD5pw)
+ return CURLE_OUT_OF_MEMORY;
+ Curl_MD5_update(MD5pw, input, curlx_uztoui(inputlen));
+ Curl_MD5_final(MD5pw, md5sum);
+ return CURLE_OK;
+}
+#else
+CURLcode Curl_none_md5sum(unsigned char *input UNUSED_PARAM,
+ size_t inputlen UNUSED_PARAM,
+ unsigned char *md5sum UNUSED_PARAM,
+ size_t md5len UNUSED_PARAM)
+{
+ (void)input;
+ (void)inputlen;
+ (void)md5sum;
+ (void)md5len;
+ return CURLE_NOT_BUILT_IN;
+}
+#endif
+
+static int Curl_multissl_init(void)
+{
+ if(multissl_init(NULL))
+ return 1;
+ return Curl_ssl->init();
+}
+
+static CURLcode Curl_multissl_connect(struct connectdata *conn, int sockindex)
+{
+ if(multissl_init(NULL))
+ return CURLE_FAILED_INIT;
+ return Curl_ssl->connect_blocking(conn, sockindex);
+}
+
+static CURLcode Curl_multissl_connect_nonblocking(struct connectdata *conn,
+ int sockindex, bool *done)
+{
+ if(multissl_init(NULL))
+ return CURLE_FAILED_INIT;
+ return Curl_ssl->connect_nonblocking(conn, sockindex, done);
+}
+
+static void *Curl_multissl_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info)
+{
+ if(multissl_init(NULL))
+ return NULL;
+ return Curl_ssl->get_internals(connssl, info);
+}
+
+static void Curl_multissl_close(struct connectdata *conn, int sockindex)
+{
+ if(multissl_init(NULL))
+ return;
+ Curl_ssl->close_one(conn, sockindex);
+}
+
+static const struct Curl_ssl Curl_ssl_multi = {
+ { CURLSSLBACKEND_NONE, "multi" }, /* info */
+ 0, /* supports nothing */
+ (size_t)-1, /* something insanely large to be on the safe side */
+
+ Curl_multissl_init, /* init */
+ Curl_none_cleanup, /* cleanup */
+ Curl_multissl_version, /* version */
+ Curl_none_check_cxn, /* check_cxn */
+ Curl_none_shutdown, /* shutdown */
+ Curl_none_data_pending, /* data_pending */
+ Curl_none_random, /* random */
+ Curl_none_cert_status_request, /* cert_status_request */
+ Curl_multissl_connect, /* connect */
+ Curl_multissl_connect_nonblocking, /* connect_nonblocking */
+ Curl_multissl_get_internals, /* get_internals */
+ Curl_multissl_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ 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 */
+ Curl_none_md5sum, /* md5sum */
+ NULL /* sha256sum */
+};
+
+const struct Curl_ssl *Curl_ssl =
+#if defined(CURL_WITH_MULTI_SSL)
+ &Curl_ssl_multi;
+#elif defined(USE_WOLFSSL)
+ &Curl_ssl_wolfssl;
+#elif defined(USE_SECTRANSP)
+ &Curl_ssl_sectransp;
+#elif defined(USE_GNUTLS)
+ &Curl_ssl_gnutls;
+#elif defined(USE_GSKIT)
+ &Curl_ssl_gskit;
+#elif defined(USE_MBEDTLS)
+ &Curl_ssl_mbedtls;
+#elif defined(USE_NSS)
+ &Curl_ssl_nss;
+#elif defined(USE_OPENSSL)
+ &Curl_ssl_openssl;
+#elif defined(USE_SCHANNEL)
+ &Curl_ssl_schannel;
+#elif defined(USE_MESALINK)
+ &Curl_ssl_mesalink;
+#elif defined(USE_BEARSSL)
+ &Curl_ssl_bearssl;
+#else
+#error "Missing struct Curl_ssl for selected SSL backend"
+#endif
+
+static const struct Curl_ssl *available_backends[] = {
+#if defined(USE_WOLFSSL)
+ &Curl_ssl_wolfssl,
+#endif
+#if defined(USE_SECTRANSP)
+ &Curl_ssl_sectransp,
+#endif
+#if defined(USE_GNUTLS)
+ &Curl_ssl_gnutls,
+#endif
+#if defined(USE_GSKIT)
+ &Curl_ssl_gskit,
+#endif
+#if defined(USE_MBEDTLS)
+ &Curl_ssl_mbedtls,
+#endif
+#if defined(USE_NSS)
+ &Curl_ssl_nss,
+#endif
+#if defined(USE_OPENSSL)
+ &Curl_ssl_openssl,
+#endif
+#if defined(USE_SCHANNEL)
+ &Curl_ssl_schannel,
+#endif
+#if defined(USE_MESALINK)
+ &Curl_ssl_mesalink,
+#endif
+#if defined(USE_BEARSSL)
+ &Curl_ssl_bearssl,
+#endif
+ NULL
+};
+
+static size_t Curl_multissl_version(char *buffer, size_t size)
+{
+ static const struct Curl_ssl *selected;
+ static char backends[200];
+ static size_t backends_len;
+ const struct Curl_ssl *current;
+
+ current = Curl_ssl == &Curl_ssl_multi ? available_backends[0] : Curl_ssl;
+
+ if(current != selected) {
+ char *p = backends;
+ char *end = backends + sizeof(backends);
+ int i;
+
+ selected = current;
+
+ backends[0] = '\0';
+
+ for(i = 0; available_backends[i]; ++i) {
+ char vb[200];
+ bool paren = (selected != available_backends[i]);
+
+ if(available_backends[i]->version(vb, sizeof(vb))) {
+ p += msnprintf(p, end - p, "%s%s%s%s", (p != backends ? " " : ""),
+ (paren ? "(" : ""), vb, (paren ? ")" : ""));
+ }
+ }
+
+ backends_len = p - backends;
+ }
+
+ if(!size)
+ return 0;
+
+ if(size <= backends_len) {
+ strncpy(buffer, backends, size - 1);
+ buffer[size - 1] = '\0';
+ return size - 1;
+ }
+
+ strcpy(buffer, backends);
+ return backends_len;
+}
+
+static int multissl_init(const struct Curl_ssl *backend)
+{
+ const char *env;
+ char *env_tmp;
+
+ if(Curl_ssl != &Curl_ssl_multi)
+ return 1;
+
+ if(backend) {
+ Curl_ssl = backend;
+ return 0;
+ }
+
+ if(!available_backends[0])
+ return 1;
+
+ env = env_tmp = curl_getenv("CURL_SSL_BACKEND");
+#ifdef CURL_DEFAULT_SSL_BACKEND
+ if(!env)
+ env = CURL_DEFAULT_SSL_BACKEND;
+#endif
+ if(env) {
+ int i;
+ for(i = 0; available_backends[i]; i++) {
+ if(strcasecompare(env, available_backends[i]->info.name)) {
+ Curl_ssl = available_backends[i];
+ curl_free(env_tmp);
+ return 0;
+ }
+ }
+ }
+
+ /* Fall back to first available backend */
+ Curl_ssl = available_backends[0];
+ curl_free(env_tmp);
+ return 0;
+}
+
+CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
+ const curl_ssl_backend ***avail)
+{
+ int i;
+
+ if(avail)
+ *avail = (const curl_ssl_backend **)&available_backends;
+
+ if(Curl_ssl != &Curl_ssl_multi)
+ return id == Curl_ssl->info.id ||
+ (name && strcasecompare(name, Curl_ssl->info.name)) ?
+ CURLSSLSET_OK :
+#if defined(CURL_WITH_MULTI_SSL)
+ CURLSSLSET_TOO_LATE;
+#else
+ CURLSSLSET_UNKNOWN_BACKEND;
+#endif
+
+ for(i = 0; available_backends[i]; i++) {
+ if(available_backends[i]->info.id == id ||
+ (name && strcasecompare(available_backends[i]->info.name, name))) {
+ multissl_init(available_backends[i]);
+ return CURLSSLSET_OK;
+ }
+ }
+
+ return CURLSSLSET_UNKNOWN_BACKEND;
+}
+
+#else /* USE_SSL */
+CURLsslset curl_global_sslset(curl_sslbackend id, const char *name,
+ const curl_ssl_backend ***avail)
+{
+ (void)id;
+ (void)name;
+ (void)avail;
+ return CURLSSLSET_NO_BACKENDS;
+}
+
+#endif /* !USE_SSL */
diff --git a/contrib/libs/curl/lib/vtls/vtls.h b/contrib/libs/curl/lib/vtls/vtls.h
new file mode 100644
index 0000000000..f4cab9988f
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/vtls.h
@@ -0,0 +1,291 @@
+#ifndef HEADER_CURL_VTLS_H
+#define HEADER_CURL_VTLS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, 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"
+
+struct connectdata;
+struct ssl_connect_data;
+
+#define SSLSUPP_CA_PATH (1<<0) /* supports CAPATH */
+#define SSLSUPP_CERTINFO (1<<1) /* supports CURLOPT_CERTINFO */
+#define SSLSUPP_PINNEDPUBKEY (1<<2) /* supports CURLOPT_PINNEDPUBLICKEY */
+#define SSLSUPP_SSL_CTX (1<<3) /* supports CURLOPT_SSL_CTX */
+#define SSLSUPP_HTTPS_PROXY (1<<4) /* supports access via HTTPS proxies */
+#define SSLSUPP_TLS13_CIPHERSUITES (1<<5) /* supports TLS 1.3 ciphersuites */
+
+struct Curl_ssl {
+ /*
+ * This *must* be the first entry to allow returning the list of available
+ * backends in curl_global_sslset().
+ */
+ curl_ssl_backend info;
+ unsigned int supports; /* bitfield, see above */
+ size_t sizeof_ssl_backend_data;
+
+ int (*init)(void);
+ void (*cleanup)(void);
+
+ size_t (*version)(char *buffer, size_t size);
+ int (*check_cxn)(struct connectdata *cxn);
+ int (*shut_down)(struct connectdata *conn, int sockindex);
+ bool (*data_pending)(const struct connectdata *conn,
+ int connindex);
+
+ /* return 0 if a find random is filled in */
+ CURLcode (*random)(struct Curl_easy *data, unsigned char *entropy,
+ size_t length);
+ bool (*cert_status_request)(void);
+
+ CURLcode (*connect_blocking)(struct connectdata *conn, int sockindex);
+ CURLcode (*connect_nonblocking)(struct connectdata *conn, int sockindex,
+ bool *done);
+ void *(*get_internals)(struct ssl_connect_data *connssl, CURLINFO info);
+ void (*close_one)(struct connectdata *conn, int sockindex);
+ void (*close_all)(struct Curl_easy *data);
+ void (*session_free)(void *ptr);
+
+ CURLcode (*set_engine)(struct Curl_easy *data, const char *engine);
+ CURLcode (*set_engine_default)(struct Curl_easy *data);
+ struct curl_slist *(*engines_list)(struct Curl_easy *data);
+
+ bool (*false_start)(void);
+
+ CURLcode (*md5sum)(unsigned char *input, size_t inputlen,
+ unsigned char *md5sum, size_t md5sumlen);
+ CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen,
+ unsigned char *sha256sum, size_t sha256sumlen);
+};
+
+#ifdef USE_SSL
+extern const struct Curl_ssl *Curl_ssl;
+#endif
+
+int Curl_none_init(void);
+void Curl_none_cleanup(void);
+int Curl_none_shutdown(struct connectdata *conn, int sockindex);
+int Curl_none_check_cxn(struct connectdata *conn);
+CURLcode Curl_none_random(struct Curl_easy *data, unsigned char *entropy,
+ size_t length);
+void Curl_none_close_all(struct Curl_easy *data);
+void Curl_none_session_free(void *ptr);
+bool Curl_none_data_pending(const struct connectdata *conn, int connindex);
+bool Curl_none_cert_status_request(void);
+CURLcode Curl_none_set_engine(struct Curl_easy *data, const char *engine);
+CURLcode Curl_none_set_engine_default(struct Curl_easy *data);
+struct curl_slist *Curl_none_engines_list(struct Curl_easy *data);
+bool Curl_none_false_start(void);
+bool Curl_ssl_tls13_ciphersuites(void);
+CURLcode Curl_none_md5sum(unsigned char *input, size_t inputlen,
+ unsigned char *md5sum, size_t md5len);
+
+#include "openssl.h" /* OpenSSL versions */
+#include "gtls.h" /* GnuTLS versions */
+#include "nssg.h" /* NSS versions */
+#include "gskit.h" /* Global Secure ToolKit versions */
+#include "wolfssl.h" /* wolfSSL versions */
+#include "schannel.h" /* Schannel SSPI version */
+#include "sectransp.h" /* SecureTransport (Darwin) version */
+#include "mbedtls.h" /* mbedTLS versions */
+#include "mesalink.h" /* MesaLink versions */
+#include "bearssl.h" /* BearSSL versions */
+
+#ifndef MAX_PINNED_PUBKEY_SIZE
+#define MAX_PINNED_PUBKEY_SIZE 1048576 /* 1MB */
+#endif
+
+#ifndef CURL_SHA256_DIGEST_LENGTH
+#define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */
+#endif
+
+/* see https://tools.ietf.org/html/draft-ietf-tls-applayerprotoneg-04 */
+#define ALPN_HTTP_1_1_LENGTH 8
+#define ALPN_HTTP_1_1 "http/1.1"
+
+/* set of helper macros for the backends to access the correct fields. For the
+ proxy or for the remote host - to properly support HTTPS proxy */
+#ifndef CURL_DISABLE_PROXY
+#define SSL_IS_PROXY() \
+ (CURLPROXY_HTTPS == conn->http_proxy.proxytype && \
+ ssl_connection_complete != \
+ conn->proxy_ssl[conn->sock[SECONDARYSOCKET] == \
+ CURL_SOCKET_BAD ? FIRSTSOCKET : SECONDARYSOCKET].state)
+#define SSL_SET_OPTION(var) \
+ (SSL_IS_PROXY() ? data->set.proxy_ssl.var : data->set.ssl.var)
+#define SSL_SET_OPTION_LVALUE(var) \
+ (*(SSL_IS_PROXY() ? &data->set.proxy_ssl.var : &data->set.ssl.var))
+#define SSL_CONN_CONFIG(var) \
+ (SSL_IS_PROXY() ? conn->proxy_ssl_config.var : conn->ssl_config.var)
+#define SSL_HOST_NAME() \
+ (SSL_IS_PROXY() ? conn->http_proxy.host.name : conn->host.name)
+#define SSL_HOST_DISPNAME() \
+ (SSL_IS_PROXY() ? conn->http_proxy.host.dispname : conn->host.dispname)
+#define SSL_PINNED_PUB_KEY() (SSL_IS_PROXY() \
+ ? data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] \
+ : data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG])
+#else
+#define SSL_IS_PROXY() FALSE
+#define SSL_SET_OPTION(var) data->set.ssl.var
+#define SSL_SET_OPTION_LVALUE(var) data->set.ssl.var
+#define SSL_CONN_CONFIG(var) conn->ssl_config.var
+#define SSL_HOST_NAME() conn->host.name
+#define SSL_HOST_DISPNAME() conn->host.dispname
+#define SSL_PINNED_PUB_KEY() \
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]
+#endif
+
+bool Curl_ssl_config_matches(struct ssl_primary_config *data,
+ struct ssl_primary_config *needle);
+bool Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
+ struct ssl_primary_config *dest);
+void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc);
+int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks);
+
+int Curl_ssl_backend(void);
+
+#ifdef USE_SSL
+int Curl_ssl_init(void);
+void Curl_ssl_cleanup(void);
+CURLcode Curl_ssl_connect(struct connectdata *conn, int sockindex);
+CURLcode Curl_ssl_connect_nonblocking(struct connectdata *conn,
+ int sockindex,
+ bool *done);
+/* tell the SSL stuff to close down all open information regarding
+ connections (and thus session ID caching etc) */
+void Curl_ssl_close_all(struct Curl_easy *data);
+void Curl_ssl_close(struct connectdata *conn, int sockindex);
+CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex);
+CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine);
+/* Sets engine as default for all SSL operations */
+CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data);
+struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data);
+
+/* init the SSL session ID cache */
+CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t);
+size_t Curl_ssl_version(char *buffer, size_t size);
+bool Curl_ssl_data_pending(const struct connectdata *conn,
+ int connindex);
+int Curl_ssl_check_cxn(struct connectdata *conn);
+
+/* Certificate information list handling. */
+
+void Curl_ssl_free_certinfo(struct Curl_easy *data);
+CURLcode Curl_ssl_init_certinfo(struct Curl_easy *data, int num);
+CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, int certnum,
+ const char *label, const char *value,
+ size_t valuelen);
+CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data, int certnum,
+ const char *label, const char *value);
+
+/* Functions to be used by SSL library adaptation functions */
+
+/* Lock session cache mutex.
+ * Call this before calling other Curl_ssl_*session* functions
+ * Caller should unlock this mutex as soon as possible, as it may block
+ * other SSL connection from making progress.
+ * The purpose of explicitly locking SSL session cache data is to allow
+ * individual SSL engines to manage session lifetime in their specific way.
+ */
+void Curl_ssl_sessionid_lock(struct connectdata *conn);
+
+/* Unlock session cache mutex */
+void Curl_ssl_sessionid_unlock(struct connectdata *conn);
+
+/* extract a session ID
+ * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
+ * Caller must make sure that the ownership of returned sessionid object
+ * is properly taken (e.g. its refcount is incremented
+ * under sessionid mutex).
+ */
+bool Curl_ssl_getsessionid(struct connectdata *conn,
+ void **ssl_sessionid,
+ size_t *idsize, /* set 0 if unknown */
+ int sockindex);
+/* add a new session ID
+ * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
+ * Caller must ensure that it has properly shared ownership of this sessionid
+ * object with cache (e.g. incrementing refcount on success)
+ */
+CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
+ void *ssl_sessionid,
+ size_t idsize,
+ int sockindex);
+/* Kill a single session ID entry in the cache
+ * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
+ * This will call engine-specific curlssl_session_free function, which must
+ * take sessionid object ownership from sessionid cache
+ * (e.g. decrement refcount).
+ */
+void Curl_ssl_kill_session(struct Curl_ssl_session *session);
+/* delete a session from the cache
+ * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
+ * This will call engine-specific curlssl_session_free function, which must
+ * take sessionid object ownership from sessionid cache
+ * (e.g. decrement refcount).
+ */
+void Curl_ssl_delsessionid(struct connectdata *conn, void *ssl_sessionid);
+
+/* get N random bytes into the buffer */
+CURLcode Curl_ssl_random(struct Curl_easy *data, unsigned char *buffer,
+ size_t length);
+CURLcode Curl_ssl_md5sum(unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *md5sum, /* output */
+ size_t md5len);
+/* Check pinned public key. */
+CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
+ const char *pinnedpubkey,
+ const unsigned char *pubkey, size_t pubkeylen);
+
+bool Curl_ssl_cert_status_request(void);
+
+bool Curl_ssl_false_start(void);
+
+#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */
+
+#else /* if not USE_SSL */
+
+/* When SSL support is not present, just define away these function calls */
+#define Curl_ssl_init() 1
+#define Curl_ssl_cleanup() Curl_nop_stmt
+#define Curl_ssl_connect(x,y) CURLE_NOT_BUILT_IN
+#define Curl_ssl_close_all(x) Curl_nop_stmt
+#define Curl_ssl_close(x,y) Curl_nop_stmt
+#define Curl_ssl_shutdown(x,y) CURLE_NOT_BUILT_IN
+#define Curl_ssl_set_engine(x,y) CURLE_NOT_BUILT_IN
+#define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN
+#define Curl_ssl_engines_list(x) NULL
+#define Curl_ssl_send(a,b,c,d,e) -1
+#define Curl_ssl_recv(a,b,c,d,e) -1
+#define Curl_ssl_initsessions(x,y) CURLE_OK
+#define Curl_ssl_data_pending(x,y) 0
+#define Curl_ssl_check_cxn(x) 0
+#define Curl_ssl_free_certinfo(x) Curl_nop_stmt
+#define Curl_ssl_connect_nonblocking(x,y,z) CURLE_NOT_BUILT_IN
+#define Curl_ssl_kill_session(x) Curl_nop_stmt
+#define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN)
+#define Curl_ssl_cert_status_request() FALSE
+#define Curl_ssl_false_start() FALSE
+#define Curl_ssl_tls13_ciphersuites() FALSE
+#endif
+
+#endif /* HEADER_CURL_VTLS_H */
diff --git a/contrib/libs/curl/lib/vtls/wolfssl.c b/contrib/libs/curl/lib/vtls/wolfssl.c
new file mode 100644
index 0000000000..ac9818824d
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/wolfssl.c
@@ -0,0 +1,1149 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, 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 wolfSSL 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_WOLFSSL
+
+#define WOLFSSL_OPTIONS_IGNORE_SYS
+#error #include <wolfssl/version.h>
+#error #include <wolfssl/options.h>
+
+/* To determine what functions are available we rely on one or both of:
+ - the user's options.h generated by wolfSSL
+ - the symbols detected by curl's configure
+ Since they are markedly different from one another, and one or the other may
+ not be available, we do some checking below to bring things in sync. */
+
+/* HAVE_ALPN is wolfSSL's build time symbol for enabling ALPN in options.h. */
+#ifndef HAVE_ALPN
+#ifdef HAVE_WOLFSSL_USEALPN
+#define HAVE_ALPN
+#endif
+#endif
+
+/* WOLFSSL_ALLOW_SSLV3 is wolfSSL's build time symbol for enabling SSLv3 in
+ options.h, but is only seen in >= 3.6.6 since that's when they started
+ disabling SSLv3 by default. */
+#ifndef WOLFSSL_ALLOW_SSLV3
+#if (LIBWOLFSSL_VERSION_HEX < 0x03006006) || \
+ defined(HAVE_WOLFSSLV3_CLIENT_METHOD)
+#define WOLFSSL_ALLOW_SSLV3
+#endif
+#endif
+
+#include <limits.h>
+
+#include "urldata.h"
+#include "sendf.h"
+#include "inet_pton.h"
+#include "vtls.h"
+#include "keylog.h"
+#include "parsedate.h"
+#include "connect.h" /* for the connect timeout */
+#include "select.h"
+#include "strcase.h"
+#include "x509asn1.h"
+#include "curl_printf.h"
+#include "multiif.h"
+
+#error #include <wolfssl/openssl/ssl.h>
+#error #include <wolfssl/ssl.h>
+#error #include <wolfssl/error-ssl.h>
+#include "wolfssl.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* KEEP_PEER_CERT is a product of the presence of build time symbol
+ OPENSSL_EXTRA without NO_CERTS, depending on the version. KEEP_PEER_CERT is
+ in wolfSSL's settings.h, and the latter two are build time symbols in
+ options.h. */
+#ifndef KEEP_PEER_CERT
+#if defined(HAVE_WOLFSSL_GET_PEER_CERTIFICATE) || \
+ (defined(OPENSSL_EXTRA) && !defined(NO_CERTS))
+#define KEEP_PEER_CERT
+#endif
+#endif
+
+struct ssl_backend_data {
+ SSL_CTX* ctx;
+ SSL* handle;
+};
+
+static Curl_recv wolfssl_recv;
+static Curl_send wolfssl_send;
+
+#ifdef OPENSSL_EXTRA
+/*
+ * Availability note:
+ * The TLS 1.3 secret callback (wolfSSL_set_tls13_secret_cb) was added in
+ * WolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that
+ * option is not set, then TLS 1.3 will not be logged.
+ * For TLS 1.2 and before, we use wolfSSL_get_keys().
+ * SSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA
+ * (--enable-opensslextra or --enable-all).
+ */
+#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13)
+static int
+wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret,
+ int secretSz, void *ctx)
+{
+ const char *label;
+ unsigned char client_random[SSL3_RANDOM_SIZE];
+ (void)ctx;
+
+ if(!ssl || !Curl_tls_keylog_enabled()) {
+ return 0;
+ }
+
+ switch(id) {
+ case CLIENT_EARLY_TRAFFIC_SECRET:
+ label = "CLIENT_EARLY_TRAFFIC_SECRET";
+ break;
+ case CLIENT_HANDSHAKE_TRAFFIC_SECRET:
+ label = "CLIENT_HANDSHAKE_TRAFFIC_SECRET";
+ break;
+ case SERVER_HANDSHAKE_TRAFFIC_SECRET:
+ label = "SERVER_HANDSHAKE_TRAFFIC_SECRET";
+ break;
+ case CLIENT_TRAFFIC_SECRET:
+ label = "CLIENT_TRAFFIC_SECRET_0";
+ break;
+ case SERVER_TRAFFIC_SECRET:
+ label = "SERVER_TRAFFIC_SECRET_0";
+ break;
+ case EARLY_EXPORTER_SECRET:
+ label = "EARLY_EXPORTER_SECRET";
+ break;
+ case EXPORTER_SECRET:
+ label = "EXPORTER_SECRET";
+ break;
+ default:
+ return 0;
+ }
+
+ if(SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE) == 0) {
+ /* Should never happen as wolfSSL_KeepArrays() was called before. */
+ return 0;
+ }
+
+ Curl_tls_keylog_write(label, client_random, secret, secretSz);
+ return 0;
+}
+#endif /* defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) */
+
+static void
+wolfssl_log_tls12_secret(SSL *ssl)
+{
+ unsigned char *ms, *sr, *cr;
+ unsigned int msLen, srLen, crLen, i, x = 0;
+
+#if LIBWOLFSSL_VERSION_HEX >= 0x0300d000 /* >= 3.13.0 */
+ /* wolfSSL_GetVersion is available since 3.13, we use it instead of
+ * SSL_version since the latter relies on OPENSSL_ALL (--enable-opensslall or
+ * --enable-all). Failing to perform this check could result in an unusable
+ * key log line when TLS 1.3 is actually negotiated. */
+ switch(wolfSSL_GetVersion(ssl)) {
+ case WOLFSSL_SSLV3:
+ case WOLFSSL_TLSV1:
+ case WOLFSSL_TLSV1_1:
+ case WOLFSSL_TLSV1_2:
+ break;
+ default:
+ /* TLS 1.3 does not use this mechanism, the "master secret" returned below
+ * is not directly usable. */
+ return;
+ }
+#endif
+
+ if(SSL_get_keys(ssl, &ms, &msLen, &sr, &srLen, &cr, &crLen) != SSL_SUCCESS) {
+ return;
+ }
+
+ /* Check for a missing master secret and skip logging. That can happen if
+ * curl rejects the server certificate and aborts the handshake.
+ */
+ for(i = 0; i < msLen; i++) {
+ x |= ms[i];
+ }
+ if(x == 0) {
+ return;
+ }
+
+ Curl_tls_keylog_write("CLIENT_RANDOM", cr, ms, msLen);
+}
+#endif /* OPENSSL_EXTRA */
+
+static int do_file_type(const char *type)
+{
+ if(!type || !type[0])
+ return SSL_FILETYPE_PEM;
+ if(strcasecompare(type, "PEM"))
+ return SSL_FILETYPE_PEM;
+ if(strcasecompare(type, "DER"))
+ return SSL_FILETYPE_ASN1;
+ return -1;
+}
+
+/*
+ * This function loads all the client/CA certificates and CRLs. Setup the TLS
+ * layer and do all necessary magic.
+ */
+static CURLcode
+wolfssl_connect_step1(struct connectdata *conn,
+ int sockindex)
+{
+ char *ciphers;
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ SSL_METHOD* req_method = NULL;
+ curl_socket_t sockfd = conn->sock[sockindex];
+#ifdef HAVE_SNI
+ bool sni = FALSE;
+#define use_sni(x) sni = (x)
+#else
+#define use_sni(x) Curl_nop_stmt
+#endif
+
+ if(connssl->state == ssl_connection_complete)
+ return CURLE_OK;
+
+ if(SSL_CONN_CONFIG(version_max) != CURL_SSLVERSION_MAX_NONE) {
+ failf(data, "wolfSSL does not support to set maximum SSL/TLS version");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ /* check to see if we've been told to use an explicit SSL/TLS version */
+ switch(SSL_CONN_CONFIG(version)) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+#if LIBWOLFSSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */
+ /* minimum protocol version is set later after the CTX object is created */
+ req_method = SSLv23_client_method();
+#else
+ infof(data, "wolfSSL <3.3.0 cannot be configured to use TLS 1.0-1.2, "
+ "TLS 1.0 is used exclusively\n");
+ req_method = TLSv1_client_method();
+#endif
+ use_sni(TRUE);
+ break;
+ case CURL_SSLVERSION_TLSv1_0:
+#ifdef WOLFSSL_ALLOW_TLSV10
+ req_method = TLSv1_client_method();
+ use_sni(TRUE);
+#else
+ failf(data, "wolfSSL does not support TLS 1.0");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ break;
+ case CURL_SSLVERSION_TLSv1_1:
+ req_method = TLSv1_1_client_method();
+ use_sni(TRUE);
+ break;
+ case CURL_SSLVERSION_TLSv1_2:
+ req_method = TLSv1_2_client_method();
+ use_sni(TRUE);
+ break;
+ case CURL_SSLVERSION_TLSv1_3:
+#ifdef WOLFSSL_TLS13
+ req_method = wolfTLSv1_3_client_method();
+ use_sni(TRUE);
+ break;
+#else
+ failf(data, "wolfSSL: TLS 1.3 is not yet supported");
+ return CURLE_SSL_CONNECT_ERROR;
+#endif
+ case CURL_SSLVERSION_SSLv3:
+#ifdef WOLFSSL_ALLOW_SSLV3
+ req_method = SSLv3_client_method();
+ use_sni(FALSE);
+#else
+ failf(data, "wolfSSL does not support SSLv3");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ break;
+ case CURL_SSLVERSION_SSLv2:
+ failf(data, "wolfSSL does not support SSLv2");
+ return CURLE_SSL_CONNECT_ERROR;
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ if(!req_method) {
+ failf(data, "SSL: couldn't create a method!");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ if(backend->ctx)
+ SSL_CTX_free(backend->ctx);
+ backend->ctx = SSL_CTX_new(req_method);
+
+ if(!backend->ctx) {
+ failf(data, "SSL: couldn't create a context!");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ switch(SSL_CONN_CONFIG(version)) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+#if LIBWOLFSSL_VERSION_HEX > 0x03004006 /* > 3.4.6 */
+ /* Versions 3.3.0 to 3.4.6 we know the minimum protocol version is
+ * whatever minimum version of TLS was built in and at least TLS 1.0. For
+ * later library versions that could change (eg TLS 1.0 built in but
+ * defaults to TLS 1.1) so we have this short circuit evaluation to find
+ * the minimum supported TLS version.
+ */
+ if((wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1) != 1) &&
+ (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_1) != 1) &&
+ (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_2) != 1)
+#ifdef WOLFSSL_TLS13
+ && (wolfSSL_CTX_SetMinVersion(backend->ctx, WOLFSSL_TLSV1_3) != 1)
+#endif
+ ) {
+ failf(data, "SSL: couldn't set the minimum protocol version");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif
+ break;
+ }
+
+ ciphers = SSL_CONN_CONFIG(cipher_list);
+ if(ciphers) {
+ if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) {
+ failf(data, "failed setting cipher list: %s", ciphers);
+ return CURLE_SSL_CIPHER;
+ }
+ infof(data, "Cipher selection: %s\n", ciphers);
+ }
+
+#ifndef NO_FILESYSTEM
+ /* load trusted cacert */
+ if(SSL_CONN_CONFIG(CAfile)) {
+ if(1 != SSL_CTX_load_verify_locations(backend->ctx,
+ SSL_CONN_CONFIG(CAfile),
+ SSL_CONN_CONFIG(CApath))) {
+ if(SSL_CONN_CONFIG(verifypeer)) {
+ /* Fail if we insist on successfully verifying the server. */
+ failf(data, "error setting certificate verify locations:"
+ " CAfile: %s CApath: %s",
+ SSL_CONN_CONFIG(CAfile)?
+ SSL_CONN_CONFIG(CAfile): "none",
+ SSL_CONN_CONFIG(CApath)?
+ SSL_CONN_CONFIG(CApath) : "none");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ else {
+ /* Just continue with a warning if no strict certificate
+ verification is required. */
+ infof(data, "error setting certificate verify locations,"
+ " continuing anyway:\n");
+ }
+ }
+ else {
+ /* Everything is fine. */
+ infof(data, "successfully set certificate verify locations:\n");
+ }
+ infof(data, " CAfile: %s\n",
+ SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile) : "none");
+ infof(data, " CApath: %s\n",
+ SSL_CONN_CONFIG(CApath) ? SSL_CONN_CONFIG(CApath) : "none");
+ }
+
+ /* Load the client certificate, and private key */
+ if(SSL_SET_OPTION(primary.clientcert) && SSL_SET_OPTION(key)) {
+ int file_type = do_file_type(SSL_SET_OPTION(cert_type));
+
+ if(SSL_CTX_use_certificate_file(backend->ctx,
+ SSL_SET_OPTION(primary.clientcert),
+ file_type) != 1) {
+ failf(data, "unable to use client certificate (no key or wrong pass"
+ " phrase?)");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ file_type = do_file_type(SSL_SET_OPTION(key_type));
+ if(SSL_CTX_use_PrivateKey_file(backend->ctx, SSL_SET_OPTION(key),
+ file_type) != 1) {
+ failf(data, "unable to set private key");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#endif /* !NO_FILESYSTEM */
+
+ /* SSL always tries to verify the peer, this only says whether it should
+ * fail to connect if the verification fails, or if it should continue
+ * anyway. In the latter case the result of the verification is checked with
+ * SSL_get_verify_result() below. */
+ SSL_CTX_set_verify(backend->ctx,
+ SSL_CONN_CONFIG(verifypeer)?SSL_VERIFY_PEER:
+ SSL_VERIFY_NONE,
+ NULL);
+
+#ifdef HAVE_SNI
+ if(sni) {
+ struct in_addr addr4;
+#ifdef ENABLE_IPV6
+ struct in6_addr addr6;
+#endif
+#ifndef CURL_DISABLE_PROXY
+ const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
+ conn->host.name;
+#else
+ const char * const hostname = conn->host.name;
+#endif
+ size_t hostname_len = strlen(hostname);
+ if((hostname_len < USHRT_MAX) &&
+ (0 == Curl_inet_pton(AF_INET, hostname, &addr4)) &&
+#ifdef ENABLE_IPV6
+ (0 == Curl_inet_pton(AF_INET6, hostname, &addr6)) &&
+#endif
+ (wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, hostname,
+ (unsigned short)hostname_len) != 1)) {
+ infof(data, "WARNING: failed to configure server name indication (SNI) "
+ "TLS extension\n");
+ }
+ }
+#endif
+
+ /* give application a chance to interfere with SSL set up. */
+ if(data->set.ssl.fsslctx) {
+ CURLcode result = (*data->set.ssl.fsslctx)(data, backend->ctx,
+ data->set.ssl.fsslctxp);
+ if(result) {
+ failf(data, "error signaled by ssl ctx callback");
+ return result;
+ }
+ }
+#ifdef NO_FILESYSTEM
+ else if(SSL_CONN_CONFIG(verifypeer)) {
+ failf(data, "SSL: Certificates can't be loaded because wolfSSL was built"
+ " with \"no filesystem\". Either disable peer verification"
+ " (insecure) or if you are building an application with libcurl you"
+ " can load certificates via CURLOPT_SSL_CTX_FUNCTION.");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#endif
+
+ /* Let's make an SSL structure */
+ if(backend->handle)
+ SSL_free(backend->handle);
+ backend->handle = SSL_new(backend->ctx);
+ if(!backend->handle) {
+ failf(data, "SSL: couldn't create a context (handle)!");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+#ifdef HAVE_ALPN
+ if(conn->bits.tls_enable_alpn) {
+ char protocols[128];
+ *protocols = '\0';
+
+ /* wolfSSL's ALPN protocol name list format is a comma separated string of
+ protocols in descending order of preference, eg: "h2,http/1.1" */
+
+#ifdef USE_NGHTTP2
+ if(data->set.httpversion >= CURL_HTTP_VERSION_2) {
+ strcpy(protocols + strlen(protocols), NGHTTP2_PROTO_VERSION_ID ",");
+ infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
+ }
+#endif
+
+ strcpy(protocols + strlen(protocols), ALPN_HTTP_1_1);
+ infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1);
+
+ if(wolfSSL_UseALPN(backend->handle, protocols,
+ (unsigned)strlen(protocols),
+ WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != SSL_SUCCESS) {
+ failf(data, "SSL: failed setting ALPN protocols");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#endif /* HAVE_ALPN */
+
+#ifdef OPENSSL_EXTRA
+ if(Curl_tls_keylog_enabled()) {
+ /* Ensure the Client Random is preserved. */
+ wolfSSL_KeepArrays(backend->handle);
+#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13)
+ wolfSSL_set_tls13_secret_cb(backend->handle,
+ wolfssl_tls13_secret_callback, NULL);
+#endif
+ }
+#endif /* OPENSSL_EXTRA */
+
+ /* Check if there's a cached ID we can/should use here! */
+ if(SSL_SET_OPTION(primary.sessionid)) {
+ void *ssl_sessionid = NULL;
+
+ Curl_ssl_sessionid_lock(conn);
+ if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL, sockindex)) {
+ /* we got a session id, use it! */
+ if(!SSL_set_session(backend->handle, ssl_sessionid)) {
+ char error_buffer[WOLFSSL_MAX_ERROR_SZ];
+ Curl_ssl_sessionid_unlock(conn);
+ failf(data, "SSL: SSL_set_session failed: %s",
+ ERR_error_string(SSL_get_error(backend->handle, 0),
+ error_buffer));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ /* Informational message */
+ infof(data, "SSL re-using session ID\n");
+ }
+ Curl_ssl_sessionid_unlock(conn);
+ }
+
+ /* pass the raw socket into the SSL layer */
+ if(!SSL_set_fd(backend->handle, (int)sockfd)) {
+ failf(data, "SSL: SSL_set_fd failed");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ connssl->connecting_state = ssl_connect_2;
+ return CURLE_OK;
+}
+
+
+static CURLcode
+wolfssl_connect_step2(struct connectdata *conn,
+ int sockindex)
+{
+ int ret = -1;
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+#ifndef CURL_DISABLE_PROXY
+ const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
+ conn->host.name;
+ const char * const dispname = SSL_IS_PROXY() ?
+ conn->http_proxy.host.dispname : conn->host.dispname;
+ const char * const pinnedpubkey = SSL_IS_PROXY() ?
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
+#else
+ const char * const hostname = conn->host.name;
+ const char * const dispname = conn->host.dispname;
+ const char * const pinnedpubkey =
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
+#endif
+
+ conn->recv[sockindex] = wolfssl_recv;
+ conn->send[sockindex] = wolfssl_send;
+
+ /* Enable RFC2818 checks */
+ if(SSL_CONN_CONFIG(verifyhost)) {
+ ret = wolfSSL_check_domain_name(backend->handle, hostname);
+ if(ret == SSL_FAILURE)
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ ret = SSL_connect(backend->handle);
+
+#ifdef OPENSSL_EXTRA
+ if(Curl_tls_keylog_enabled()) {
+ /* If key logging is enabled, wait for the handshake to complete and then
+ * proceed with logging secrets (for TLS 1.2 or older).
+ *
+ * During the handshake (ret==-1), wolfSSL_want_read() is true as it waits
+ * for the server response. At that point the master secret is not yet
+ * available, so we must not try to read it.
+ * To log the secret on completion with a handshake failure, detect
+ * completion via the observation that there is nothing to read or write.
+ * Note that OpenSSL SSL_want_read() is always true here. If wolfSSL ever
+ * changes, the worst case is that no key is logged on error.
+ */
+ if(ret == SSL_SUCCESS ||
+ (!wolfSSL_want_read(backend->handle) &&
+ !wolfSSL_want_write(backend->handle))) {
+ wolfssl_log_tls12_secret(backend->handle);
+ /* Client Random and master secrets are no longer needed, erase these.
+ * Ignored while the handshake is still in progress. */
+ wolfSSL_FreeArrays(backend->handle);
+ }
+ }
+#endif /* OPENSSL_EXTRA */
+
+ if(ret != 1) {
+ char error_buffer[WOLFSSL_MAX_ERROR_SZ];
+ int detail = SSL_get_error(backend->handle, ret);
+
+ if(SSL_ERROR_WANT_READ == detail) {
+ connssl->connecting_state = ssl_connect_2_reading;
+ return CURLE_OK;
+ }
+ else if(SSL_ERROR_WANT_WRITE == detail) {
+ connssl->connecting_state = ssl_connect_2_writing;
+ return CURLE_OK;
+ }
+ /* There is no easy way to override only the CN matching.
+ * This will enable the override of both mismatching SubjectAltNames
+ * as also mismatching CN fields */
+ else if(DOMAIN_NAME_MISMATCH == detail) {
+#if 1
+ failf(data, "\tsubject alt name(s) or common name do not match \"%s\"\n",
+ dispname);
+ return CURLE_PEER_FAILED_VERIFICATION;
+#else
+ /* When the wolfssl_check_domain_name() is used and you desire to
+ * continue on a DOMAIN_NAME_MISMATCH, i.e. 'conn->ssl_config.verifyhost
+ * == 0', CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA
+ * error. The only way to do this is currently to switch the
+ * Wolfssl_check_domain_name() in and out based on the
+ * 'conn->ssl_config.verifyhost' value. */
+ if(SSL_CONN_CONFIG(verifyhost)) {
+ failf(data,
+ "\tsubject alt name(s) or common name do not match \"%s\"\n",
+ dispname);
+ return CURLE_PEER_FAILED_VERIFICATION;
+ }
+ else {
+ infof(data,
+ "\tsubject alt name(s) and/or common name do not match \"%s\"\n",
+ dispname);
+ return CURLE_OK;
+ }
+#endif
+ }
+#if LIBWOLFSSL_VERSION_HEX >= 0x02007000 /* 2.7.0 */
+ else if(ASN_NO_SIGNER_E == detail) {
+ if(SSL_CONN_CONFIG(verifypeer)) {
+ failf(data, "\tCA signer not available for verification\n");
+ return CURLE_SSL_CACERT_BADFILE;
+ }
+ else {
+ /* Just continue with a warning if no strict certificate
+ verification is required. */
+ infof(data, "CA signer not available for verification, "
+ "continuing anyway\n");
+ }
+ }
+#endif
+ else {
+ failf(data, "SSL_connect failed with error %d: %s", detail,
+ ERR_error_string(detail, error_buffer));
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ if(pinnedpubkey) {
+#ifdef KEEP_PEER_CERT
+ X509 *x509;
+ const char *x509_der;
+ int x509_der_len;
+ struct Curl_X509certificate x509_parsed;
+ struct Curl_asn1Element *pubkey;
+ CURLcode result;
+
+ x509 = SSL_get_peer_certificate(backend->handle);
+ if(!x509) {
+ failf(data, "SSL: failed retrieving server certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
+ x509_der = (const char *)wolfSSL_X509_get_der(x509, &x509_der_len);
+ if(!x509_der) {
+ failf(data, "SSL: failed retrieving ASN.1 server certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
+ memset(&x509_parsed, 0, sizeof(x509_parsed));
+ if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+ pubkey = &x509_parsed.subjectPublicKeyInfo;
+ if(!pubkey->header || pubkey->end <= pubkey->header) {
+ failf(data, "SSL: failed retrieving public key from server certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
+ 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!");
+ return result;
+ }
+#else
+ failf(data, "Library lacks pinning support built-in");
+ return CURLE_NOT_BUILT_IN;
+#endif
+ }
+
+#ifdef HAVE_ALPN
+ if(conn->bits.tls_enable_alpn) {
+ int rc;
+ char *protocol = NULL;
+ unsigned short protocol_len = 0;
+
+ rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len);
+
+ if(rc == SSL_SUCCESS) {
+ infof(data, "ALPN, server accepted to use %.*s\n", protocol_len,
+ protocol);
+
+ if(protocol_len == ALPN_HTTP_1_1_LENGTH &&
+ !memcmp(protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH))
+ conn->negnpn = CURL_HTTP_VERSION_1_1;
+#ifdef USE_NGHTTP2
+ else if(data->set.httpversion >= CURL_HTTP_VERSION_2 &&
+ protocol_len == NGHTTP2_PROTO_VERSION_ID_LEN &&
+ !memcmp(protocol, NGHTTP2_PROTO_VERSION_ID,
+ NGHTTP2_PROTO_VERSION_ID_LEN))
+ conn->negnpn = CURL_HTTP_VERSION_2;
+#endif
+ else
+ infof(data, "ALPN, unrecognized protocol %.*s\n", protocol_len,
+ protocol);
+ Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ?
+ BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
+ }
+ else if(rc == SSL_ALPN_NOT_FOUND)
+ infof(data, "ALPN, server did not agree to a protocol\n");
+ else {
+ failf(data, "ALPN, failure getting protocol, error %d", rc);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#endif /* HAVE_ALPN */
+
+ connssl->connecting_state = ssl_connect_3;
+#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010)
+ infof(data, "SSL connection using %s / %s\n",
+ wolfSSL_get_version(backend->handle),
+ wolfSSL_get_cipher_name(backend->handle));
+#else
+ infof(data, "SSL connected\n");
+#endif
+
+ return CURLE_OK;
+}
+
+
+static CURLcode
+wolfssl_connect_step3(struct connectdata *conn,
+ int sockindex)
+{
+ CURLcode result = CURLE_OK;
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+
+ DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
+
+ if(SSL_SET_OPTION(primary.sessionid)) {
+ bool incache;
+ SSL_SESSION *our_ssl_sessionid;
+ void *old_ssl_sessionid = NULL;
+
+ our_ssl_sessionid = SSL_get_session(backend->handle);
+
+ Curl_ssl_sessionid_lock(conn);
+ incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL,
+ sockindex));
+ if(incache) {
+ if(old_ssl_sessionid != our_ssl_sessionid) {
+ infof(data, "old SSL session ID is stale, removing\n");
+ Curl_ssl_delsessionid(conn, old_ssl_sessionid);
+ incache = FALSE;
+ }
+ }
+
+ if(!incache) {
+ result = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
+ 0 /* unknown size */, sockindex);
+ if(result) {
+ Curl_ssl_sessionid_unlock(conn);
+ failf(data, "failed to store ssl session");
+ return result;
+ }
+ }
+ Curl_ssl_sessionid_unlock(conn);
+ }
+
+ connssl->connecting_state = ssl_connect_done;
+
+ return result;
+}
+
+
+static ssize_t wolfssl_send(struct connectdata *conn,
+ int sockindex,
+ const void *mem,
+ size_t len,
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ char error_buffer[WOLFSSL_MAX_ERROR_SZ];
+ int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
+ int rc = SSL_write(backend->handle, mem, memlen);
+
+ if(rc < 0) {
+ int err = SSL_get_error(backend->handle, rc);
+
+ switch(err) {
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ /* there's data pending, re-invoke SSL_write() */
+ *curlcode = CURLE_AGAIN;
+ return -1;
+ default:
+ failf(conn->data, "SSL write: %s, errno %d",
+ ERR_error_string(err, error_buffer),
+ SOCKERRNO);
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+ }
+ return rc;
+}
+
+static void Curl_wolfssl_close(struct connectdata *conn, int sockindex)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+
+ if(backend->handle) {
+ (void)SSL_shutdown(backend->handle);
+ SSL_free(backend->handle);
+ backend->handle = NULL;
+ }
+ if(backend->ctx) {
+ SSL_CTX_free(backend->ctx);
+ backend->ctx = NULL;
+ }
+}
+
+static ssize_t wolfssl_recv(struct connectdata *conn,
+ int num,
+ char *buf,
+ size_t buffersize,
+ CURLcode *curlcode)
+{
+ struct ssl_connect_data *connssl = &conn->ssl[num];
+ struct ssl_backend_data *backend = connssl->backend;
+ char error_buffer[WOLFSSL_MAX_ERROR_SZ];
+ int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
+ int nread = SSL_read(backend->handle, buf, buffsize);
+
+ if(nread < 0) {
+ int err = SSL_get_error(backend->handle, nread);
+
+ switch(err) {
+ case SSL_ERROR_ZERO_RETURN: /* no more data */
+ break;
+ case SSL_ERROR_WANT_READ:
+ case SSL_ERROR_WANT_WRITE:
+ /* there's data pending, re-invoke SSL_read() */
+ *curlcode = CURLE_AGAIN;
+ return -1;
+ default:
+ failf(conn->data, "SSL read: %s, errno %d",
+ ERR_error_string(err, error_buffer),
+ SOCKERRNO);
+ *curlcode = CURLE_RECV_ERROR;
+ return -1;
+ }
+ }
+ return nread;
+}
+
+
+static void Curl_wolfssl_session_free(void *ptr)
+{
+ (void)ptr;
+ /* wolfSSL reuses sessions on own, no free */
+}
+
+
+static size_t Curl_wolfssl_version(char *buffer, size_t size)
+{
+#if LIBWOLFSSL_VERSION_HEX >= 0x03006000
+ return msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version());
+#elif defined(WOLFSSL_VERSION)
+ return msnprintf(buffer, size, "wolfSSL/%s", WOLFSSL_VERSION);
+#endif
+}
+
+
+static int Curl_wolfssl_init(void)
+{
+#ifdef OPENSSL_EXTRA
+ Curl_tls_keylog_open();
+#endif
+ return (wolfSSL_Init() == SSL_SUCCESS);
+}
+
+
+static void Curl_wolfssl_cleanup(void)
+{
+ wolfSSL_Cleanup();
+#ifdef OPENSSL_EXTRA
+ Curl_tls_keylog_close();
+#endif
+}
+
+
+static bool Curl_wolfssl_data_pending(const struct connectdata *conn,
+ int connindex)
+{
+ const struct ssl_connect_data *connssl = &conn->ssl[connindex];
+ struct ssl_backend_data *backend = connssl->backend;
+ if(backend->handle) /* SSL is in use */
+ return (0 != SSL_pending(backend->handle)) ? TRUE : FALSE;
+ else
+ return FALSE;
+}
+
+
+/*
+ * This function is called to shut down the SSL layer but keep the
+ * socket open (CCC - Clear Command Channel)
+ */
+static int Curl_wolfssl_shutdown(struct connectdata *conn, int sockindex)
+{
+ int retval = 0;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ struct ssl_backend_data *backend = connssl->backend;
+
+ if(backend->handle) {
+ SSL_free(backend->handle);
+ backend->handle = NULL;
+ }
+ return retval;
+}
+
+
+static CURLcode
+wolfssl_connect_common(struct connectdata *conn,
+ int sockindex,
+ bool nonblocking,
+ bool *done)
+{
+ CURLcode result;
+ struct Curl_easy *data = conn->data;
+ struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+ curl_socket_t sockfd = conn->sock[sockindex];
+ 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 */
+ const timediff_t 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;
+ }
+
+ result = wolfssl_connect_step1(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 allowed time left */
+ const timediff_t 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.
+ */
+ result = wolfssl_connect_step2(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 = wolfssl_connect_step3(conn, sockindex);
+ if(result)
+ return result;
+ }
+
+ if(ssl_connect_done == connssl->connecting_state) {
+ connssl->state = ssl_connection_complete;
+ conn->recv[sockindex] = wolfssl_recv;
+ conn->send[sockindex] = wolfssl_send;
+ *done = TRUE;
+ }
+ else
+ *done = FALSE;
+
+ /* Reset our connect state machine */
+ connssl->connecting_state = ssl_connect_1;
+
+ return CURLE_OK;
+}
+
+
+static CURLcode Curl_wolfssl_connect_nonblocking(struct connectdata *conn,
+ int sockindex, bool *done)
+{
+ return wolfssl_connect_common(conn, sockindex, TRUE, done);
+}
+
+
+static CURLcode Curl_wolfssl_connect(struct connectdata *conn, int sockindex)
+{
+ CURLcode result;
+ bool done = FALSE;
+
+ result = wolfssl_connect_common(conn, sockindex, FALSE, &done);
+ if(result)
+ return result;
+
+ DEBUGASSERT(done);
+
+ return CURLE_OK;
+}
+
+static CURLcode Curl_wolfssl_random(struct Curl_easy *data,
+ unsigned char *entropy, size_t length)
+{
+ WC_RNG rng;
+ (void)data;
+ if(wc_InitRng(&rng))
+ return CURLE_FAILED_INIT;
+ if(length > UINT_MAX)
+ return CURLE_FAILED_INIT;
+ if(wc_RNG_GenerateBlock(&rng, entropy, (unsigned)length))
+ return CURLE_FAILED_INIT;
+ if(wc_FreeRng(&rng))
+ return CURLE_FAILED_INIT;
+ return CURLE_OK;
+}
+
+static CURLcode Curl_wolfssl_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum /* output */,
+ size_t unused)
+{
+ wc_Sha256 SHA256pw;
+ (void)unused;
+ wc_InitSha256(&SHA256pw);
+ wc_Sha256Update(&SHA256pw, tmp, (word32)tmplen);
+ wc_Sha256Final(&SHA256pw, sha256sum);
+ return CURLE_OK;
+}
+
+static void *Curl_wolfssl_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
+{
+ struct ssl_backend_data *backend = connssl->backend;
+ (void)info;
+ return backend->handle;
+}
+
+const struct Curl_ssl Curl_ssl_wolfssl = {
+ { CURLSSLBACKEND_WOLFSSL, "WolfSSL" }, /* info */
+
+#ifdef KEEP_PEER_CERT
+ SSLSUPP_PINNEDPUBKEY |
+#endif
+ SSLSUPP_SSL_CTX,
+
+ sizeof(struct ssl_backend_data),
+
+ Curl_wolfssl_init, /* init */
+ Curl_wolfssl_cleanup, /* cleanup */
+ Curl_wolfssl_version, /* version */
+ Curl_none_check_cxn, /* check_cxn */
+ Curl_wolfssl_shutdown, /* shutdown */
+ Curl_wolfssl_data_pending, /* data_pending */
+ Curl_wolfssl_random, /* random */
+ Curl_none_cert_status_request, /* cert_status_request */
+ Curl_wolfssl_connect, /* connect */
+ Curl_wolfssl_connect_nonblocking, /* connect_nonblocking */
+ Curl_wolfssl_get_internals, /* get_internals */
+ Curl_wolfssl_close, /* close_one */
+ Curl_none_close_all, /* close_all */
+ Curl_wolfssl_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 */
+ Curl_none_md5sum, /* md5sum */
+ Curl_wolfssl_sha256sum /* sha256sum */
+};
+
+#endif
diff --git a/contrib/libs/curl/lib/vtls/wolfssl.h b/contrib/libs/curl/lib/vtls/wolfssl.h
new file mode 100644
index 0000000000..d411e6913e
--- /dev/null
+++ b/contrib/libs/curl/lib/vtls/wolfssl.h
@@ -0,0 +1,31 @@
+#ifndef HEADER_CURL_WOLFSSL_H
+#define HEADER_CURL_WOLFSSL_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, 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_WOLFSSL
+
+extern const struct Curl_ssl Curl_ssl_wolfssl;
+
+#endif /* USE_WOLFSSL */
+#endif /* HEADER_CURL_WOLFSSL_H */