diff options
author | robot-contrib <robot-contrib@yandex-team.ru> | 2022-04-23 01:34:18 +0300 |
---|---|---|
committer | robot-contrib <robot-contrib@yandex-team.ru> | 2022-04-23 01:34:18 +0300 |
commit | 70d823f7ee62199b67f5fbe469005124ffe1fe93 (patch) | |
tree | 82277ba9117d43c5a5f973825b38a2ffe7d95818 /contrib/libs/curl/lib/vtls | |
parent | 19b525690e0c7788c39d741ea94023b64ae31a89 (diff) | |
download | ydb-70d823f7ee62199b67f5fbe469005124ffe1fe93.tar.gz |
Update contrib/libs/curl to 7.82.0
ref:0a102f02466c720a2ee37f41ed197348e7b727bd
Diffstat (limited to 'contrib/libs/curl/lib/vtls')
20 files changed, 2282 insertions, 958 deletions
diff --git a/contrib/libs/curl/lib/vtls/bearssl.c b/contrib/libs/curl/lib/vtls/bearssl.c index 9b772d064d..77e22cf3e0 100644 --- a/contrib/libs/curl/lib/vtls/bearssl.c +++ b/contrib/libs/curl/lib/vtls/bearssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2019 - 2021, Michael Forney, <mforney@mforney.org> + * Copyright (C) 2019 - 2022, 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 @@ -39,8 +39,10 @@ struct x509_context { const br_x509_class *vtable; br_x509_minimal_context minimal; + br_x509_decoder_context decoder; bool verifyhost; bool verifypeer; + int cert_num; }; struct ssl_backend_data { @@ -159,6 +161,18 @@ static CURLcode load_cafile(struct cafile_source *source, if(strcmp(name, "CERTIFICATE") && strcmp(name, "X509 CERTIFICATE")) break; br_x509_decoder_init(&ca.xc, append_dn, &ca); + ca.in_cert = TRUE; + ca.dn_len = 0; + 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; + } + /* add trust anchor */ if(ca.anchors_len == SIZE_MAX / sizeof(ca.anchors[0])) { ca.err = CURLE_OUT_OF_MEMORY; goto fail; @@ -172,19 +186,8 @@ static CURLcode load_cafile(struct cafile_source *source, } 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; @@ -238,6 +241,8 @@ static CURLcode load_cafile(struct cafile_source *source, } while(source->type != CAFILE_SOURCE_BLOB); if(fp && ferror(fp)) ca.err = CURLE_READ_ERROR; + else if(ca.in_cert) + ca.err = CURLE_SSL_CACERT_BADFILE; fail: if(fp) @@ -260,6 +265,11 @@ static void x509_start_chain(const br_x509_class **ctx, { struct x509_context *x509 = (struct x509_context *)ctx; + if(!x509->verifypeer) { + x509->cert_num = 0; + return; + } + if(!x509->verifyhost) server_name = NULL; x509->minimal.vtable->start_chain(&x509->minimal.vtable, server_name); @@ -269,6 +279,13 @@ static void x509_start_cert(const br_x509_class **ctx, uint32_t length) { struct x509_context *x509 = (struct x509_context *)ctx; + if(!x509->verifypeer) { + /* Only decode the first cert in the chain to obtain the public key */ + if(x509->cert_num == 0) + br_x509_decoder_init(&x509->decoder, NULL, NULL); + return; + } + x509->minimal.vtable->start_cert(&x509->minimal.vtable, length); } @@ -277,6 +294,12 @@ static void x509_append(const br_x509_class **ctx, const unsigned char *buf, { struct x509_context *x509 = (struct x509_context *)ctx; + if(!x509->verifypeer) { + if(x509->cert_num == 0) + br_x509_decoder_push(&x509->decoder, buf, len); + return; + } + x509->minimal.vtable->append(&x509->minimal.vtable, buf, len); } @@ -284,21 +307,23 @@ static void x509_end_cert(const br_x509_class **ctx) { struct x509_context *x509 = (struct x509_context *)ctx; + if(!x509->verifypeer) { + x509->cert_num++; + return; + } + 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; + if(!x509->verifypeer) { + return br_x509_decoder_last_error(&x509->decoder); } - return err; + return x509->minimal.vtable->end_chain(&x509->minimal.vtable); } static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx, @@ -306,6 +331,15 @@ static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx, { struct x509_context *x509 = (struct x509_context *)ctx; + if(!x509->verifypeer) { + /* Nothing in the chain is verified, just return the public key of the + first certificate and allow its usage for both TLS_RSA_* and + TLS_ECDHE_* */ + if(usages) + *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; + return br_x509_decoder_get_pkey(&x509->decoder); + } + return x509->minimal.vtable->get_pkey(&x509->minimal.vtable, usages); } @@ -339,6 +373,8 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data, struct in_addr addr; #endif + DEBUGASSERT(backend); + switch(SSL_CONN_CONFIG(version)) { case CURL_SSLVERSION_SSLv2: failf(data, "BearSSL does not support SSLv2"); @@ -465,8 +501,16 @@ static CURLcode bearssl_connect_step1(struct Curl_easy *data, } hostname = NULL; } + else { + char *snihost = Curl_ssl_snihost(data, hostname, NULL); + if(!snihost) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + hostname = snihost; + } - if(!br_ssl_client_reset(&backend->ctx, hostname, 0)) + if(!br_ssl_client_reset(&backend->ctx, hostname, 1)) return CURLE_FAILED_INIT; backend->active = TRUE; @@ -488,6 +532,8 @@ static CURLcode bearssl_run_until(struct Curl_easy *data, ssize_t ret; int err; + DEBUGASSERT(backend); + for(;;) { state = br_ssl_engine_current_state(&backend->ctx.eng); if(state & BR_SSL_CLOSED) { @@ -560,6 +606,8 @@ static CURLcode bearssl_connect_step2(struct Curl_easy *data, struct ssl_backend_data *backend = connssl->backend; CURLcode ret; + DEBUGASSERT(backend); + ret = bearssl_run_until(data, conn, sockindex, BR_SSL_SENDAPP | BR_SSL_RECVAPP); if(ret == CURLE_AGAIN) @@ -582,6 +630,7 @@ static CURLcode bearssl_connect_step3(struct Curl_easy *data, CURLcode ret; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + DEBUGASSERT(backend); if(conn->bits.tls_enable_alpn) { const char *protocol; @@ -647,6 +696,8 @@ static ssize_t bearssl_send(struct Curl_easy *data, int sockindex, unsigned char *app; size_t applen; + DEBUGASSERT(backend); + for(;;) { *err = bearssl_run_until(data, conn, sockindex, BR_SSL_SENDAPP); if (*err != CURLE_OK) @@ -680,6 +731,8 @@ static ssize_t bearssl_recv(struct Curl_easy *data, int sockindex, unsigned char *app; size_t applen; + DEBUGASSERT(backend); + *err = bearssl_run_until(data, conn, sockindex, BR_SSL_RECVAPP); if(*err != CURLE_OK) return -1; @@ -805,6 +858,7 @@ static bool bearssl_data_pending(const struct connectdata *conn, { const struct ssl_connect_data *connssl = &conn->ssl[connindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP; } @@ -854,6 +908,7 @@ static void *bearssl_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); return &backend->ctx; } @@ -864,6 +919,8 @@ static void bearssl_close(struct Curl_easy *data, struct ssl_backend_data *backend = connssl->backend; size_t i; + DEBUGASSERT(backend); + if(backend->active) { br_ssl_engine_close(&backend->ctx.eng); (void)bearssl_run_until(data, conn, sockindex, BR_SSL_CLOSED); diff --git a/contrib/libs/curl/lib/vtls/gskit.c b/contrib/libs/curl/lib/vtls/gskit.c index e451f6aebe..56d48497db 100644 --- a/contrib/libs/curl/lib/vtls/gskit.c +++ b/contrib/libs/curl/lib/vtls/gskit.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -28,6 +28,7 @@ #include <qsoasync.h> #undef HAVE_SOCKETPAIR /* because the native one isn't good enough */ #include "socketpair.h" +#include "strerror.h" /* Some symbols are undefined/unsupported on OS400 versions < V7R1. */ #ifndef GSK_SSL_EXTN_SERVERNAME_REQUEST @@ -247,10 +248,10 @@ static CURLcode set_enum(struct Curl_easy *data, gsk_handle h, static CURLcode set_buffer(struct Curl_easy *data, gsk_handle h, - GSK_BUF_ID id, const char *buffer, bool unsupported_ok) + GSK_BUF_ID id, const char *buf, bool unsupported_ok) { char buffer[STRERROR_LEN]; - int rc = gsk_attribute_set_buffer(h, id, buffer, 0); + int rc = gsk_attribute_set_buffer(h, id, buf, 0); switch(rc) { case GSK_OK: @@ -513,6 +514,8 @@ static void cancel_async_handshake(struct connectdata *conn, int sockindex) struct ssl_connect_data *connssl = &conn->ssl[sockindex]; Qso_OverlappedIO_t cstat; + DEBUGASSERT(BACKEND); + if(QsoCancelOperation(conn->sock[sockindex], 0) > 0) QsoWaitForIOCompletion(BACKEND->iocport, &cstat, (struct timeval *) NULL); } @@ -520,6 +523,7 @@ static void cancel_async_handshake(struct connectdata *conn, int sockindex) static void close_async_handshake(struct ssl_connect_data *connssl) { + DEBUGASSERT(BACKEND); QsoDestroyIOCompletionPort(BACKEND->iocport); BACKEND->iocport = -1; } @@ -530,36 +534,36 @@ static int pipe_ssloverssl(struct connectdata *conn, int sockindex, #ifndef CURL_DISABLE_PROXY 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; + struct pollfd fds[2]; int n; int m; int i; int ret = 0; char buf[CURL_MAX_WRITE_SIZE]; + DEBUGASSERT(BACKEND); + DEBUGASSERT(connproxyssl->backend); + if(!connssl->use || !connproxyssl->use) return 0; /* No SSL over SSL: OK. */ - FD_ZERO(&fds_read); - FD_ZERO(&fds_write); - n = -1; + n = 1; + fds[0].fd = BACKEND->remotefd; + fds[1].fd = conn->sock[sockindex]; + if(directions & SOS_READ) { - FD_SET(BACKEND->remotefd, &fds_write); - n = BACKEND->remotefd; + fds[0].events |= POLLOUT; } 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]; + n = 2; + fds[0].events |= POLLIN; + fds[1].events |= POLLOUT; } - i = Curl_select(n + 1, &fds_read, &fds_write, NULL, 0); + i = Curl_poll(fds, n, 0); if(i < 0) return -1; /* Select error. */ - if(FD_ISSET(BACKEND->remotefd, &fds_write)) { + if(fds[0].revents & POLLOUT) { /* Try getting data from HTTPS proxy and pipe it upstream. */ n = 0; i = gsk_secure_soc_read(connproxyssl->backend->handle, @@ -581,8 +585,7 @@ static int pipe_ssloverssl(struct connectdata *conn, int sockindex, } } - if(FD_ISSET(BACKEND->remotefd, &fds_read) && - FD_ISSET(conn->sock[sockindex], &fds_write)) { + if((fds[0].revents & POLLIN) && (fds[1].revents & POLLOUT)) { /* Pipe data to HTTPS proxy. */ n = read(BACKEND->remotefd, buf, sizeof(buf)); if(n < 0) @@ -605,6 +608,7 @@ static int pipe_ssloverssl(struct connectdata *conn, int sockindex, static void close_one(struct ssl_connect_data *connssl, struct Curl_easy *data, struct connectdata *conn, int sockindex) { + DEBUGASSERT(BACKEND); if(BACKEND->handle) { gskit_status(data, gsk_secure_soc_close(&BACKEND->handle), "gsk_secure_soc_close()", 0); @@ -636,6 +640,8 @@ static ssize_t gskit_send(struct Curl_easy *data, int sockindex, CURLcode cc = CURLE_SEND_ERROR; int written; + DEBUGASSERT(BACKEND); + if(pipe_ssloverssl(conn, sockindex, SOS_WRITE) >= 0) { cc = gskit_status(data, gsk_secure_soc_write(BACKEND->handle, @@ -661,6 +667,8 @@ static ssize_t gskit_recv(struct Curl_easy *data, int num, char *buf, int nread; CURLcode cc = CURLE_RECV_ERROR; + DEBUGASSERT(BACKEND); + if(pipe_ssloverssl(conn, num, SOS_READ) >= 0) { int buffsize = buffersize > (size_t) INT_MAX? INT_MAX: (int) buffersize; cc = gskit_status(data, gsk_secure_soc_read(BACKEND->handle, @@ -734,6 +742,7 @@ static CURLcode gskit_connect_step1(struct Curl_easy *data, #endif /* Create SSL environment, start (preferably asynchronous) handshake. */ + DEBUGASSERT(BACKEND); BACKEND->handle = (gsk_handle) NULL; BACKEND->iocport = -1; @@ -830,8 +839,13 @@ static CURLcode gskit_connect_step1(struct Curl_easy *data, /* Process SNI. Ignore if not supported (on OS400 < V7R1). */ if(sni) { + char *snihost = Curl_ssl_snihost(data, sni, NULL); + if(!snihost) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } result = set_buffer(data, BACKEND->handle, - GSK_SSL_EXTN_SERVERNAME_REQUEST, sni, TRUE); + GSK_SSL_EXTN_SERVERNAME_REQUEST, snihost, TRUE); if(result == CURLE_UNSUPPORTED_PROTOCOL) result = CURLE_OK; } @@ -958,6 +972,7 @@ static CURLcode gskit_connect_step2(struct Curl_easy *data, CURLcode result; /* Poll or wait for end of SSL asynchronous handshake. */ + DEBUGASSERT(BACKEND); for(;;) { timediff_t timeout_ms = nonblocking? 0: Curl_timeleft(data, NULL, TRUE); @@ -1014,6 +1029,7 @@ static CURLcode gskit_connect_step3(struct Curl_easy *data, CURLcode result; /* SSL handshake done: gather certificate info and verify host. */ + DEBUGASSERT(BACKEND); if(gskit_status(data, gsk_attribute_get_cert_info(BACKEND->handle, GSK_PARTNER_CERT_INFO, @@ -1070,9 +1086,10 @@ static CURLcode gskit_connect_step3(struct Curl_easy *data, /* Check pinned public key. */ ptr = SSL_PINNED_PUB_KEY(); if(!result && ptr) { - curl_X509certificate x509; - curl_asn1Element *p; + struct Curl_X509certificate x509; + struct Curl_asn1Element *p; + memset(&x509, 0, sizeof(x509)); if(Curl_parseX509(&x509, cert, certend)) return CURLE_SSL_PINNEDPUBKEYNOTMATCH; p = &x509.subjectPublicKeyInfo; @@ -1205,6 +1222,8 @@ static int gskit_shutdown(struct Curl_easy *data, char buf[120]; int loop = 10; /* don't get stuck */ + DEBUGASSERT(BACKEND); + if(!BACKEND->handle) return 0; @@ -1268,6 +1287,7 @@ static int gskit_check_cxn(struct connectdata *cxn) int errlen; /* The only thing that can be tested here is at the socket level. */ + DEBUGASSERT(BACKEND); if(!BACKEND->handle) return 0; /* connection has been closed */ @@ -1287,6 +1307,7 @@ static void *gskit_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { (void)info; + DEBUGASSERT(BACKEND); return BACKEND->handle; } @@ -1308,6 +1329,7 @@ const struct Curl_ssl Curl_ssl_gskit = { Curl_none_cert_status_request, /* cert_status_request */ gskit_connect, /* connect */ gskit_connect_nonblocking, /* connect_nonblocking */ + Curl_ssl_getsock, /* getsock */ gskit_get_internals, /* get_internals */ gskit_close, /* close_one */ Curl_none_close_all, /* close_all */ diff --git a/contrib/libs/curl/lib/vtls/gtls.c b/contrib/libs/curl/lib/vtls/gtls.c index 18864aa4b2..57493767e5 100644 --- a/contrib/libs/curl/lib/vtls/gtls.c +++ b/contrib/libs/curl/lib/vtls/gtls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -202,9 +202,12 @@ static CURLcode handshake(struct Curl_easy *data, { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; - gnutls_session_t session = backend->session; + gnutls_session_t session; curl_socket_t sockfd = conn->sock[sockindex]; + DEBUGASSERT(backend); + session = backend->session; + for(;;) { timediff_t timeout_ms; int rc; @@ -406,6 +409,8 @@ gtls_connect_step1(struct Curl_easy *data, const char *tls13support; CURLcode result; + DEBUGASSERT(backend); + if(connssl->state == ssl_connection_complete) /* to make us tolerant against being called more than once for the same connection */ @@ -542,11 +547,15 @@ gtls_connect_step1(struct Curl_easy *data, #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"); + sni) { + size_t snilen; + char *snihost = Curl_ssl_snihost(data, hostname, &snilen); + if(!snihost || gnutls_server_name_set(session, GNUTLS_NAME_DNS, snihost, + snilen) < 0) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + } /* Use default priorities */ rc = gnutls_set_default_priority(session); @@ -697,7 +706,10 @@ gtls_connect_step1(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY if(conn->proxy_ssl[sockindex].use) { - transport_ptr = conn->proxy_ssl[sockindex].backend->session; + struct ssl_backend_data *proxy_backend; + proxy_backend = conn->proxy_ssl[sockindex].backend; + DEBUGASSERT(proxy_backend); + transport_ptr = proxy_backend->session; gnutls_transport_push = gtls_push_ssl; gnutls_transport_pull = gtls_pull_ssl; } @@ -1352,7 +1364,9 @@ gtls_connect_common(struct Curl_easy *data, /* Finish connecting once the handshake is done */ if(ssl_connect_1 == connssl->connecting_state) { struct ssl_backend_data *backend = connssl->backend; - gnutls_session_t session = backend->session; + gnutls_session_t session; + DEBUGASSERT(backend); + session = backend->session; rc = Curl_gtls_verifyserver(data, conn, session, sockindex); if(rc) return rc; @@ -1393,6 +1407,9 @@ static bool gtls_data_pending(const struct connectdata *conn, const struct ssl_connect_data *connssl = &conn->ssl[connindex]; bool res = FALSE; struct ssl_backend_data *backend = connssl->backend; + + DEBUGASSERT(backend); + if(backend->session && 0 != gnutls_record_check_pending(backend->session)) res = TRUE; @@ -1400,6 +1417,7 @@ static bool gtls_data_pending(const struct connectdata *conn, #ifndef CURL_DISABLE_PROXY connssl = &conn->proxy_ssl[connindex]; backend = connssl->backend; + DEBUGASSERT(backend); if(backend->session && 0 != gnutls_record_check_pending(backend->session)) res = TRUE; @@ -1417,7 +1435,10 @@ static ssize_t gtls_send(struct Curl_easy *data, struct connectdata *conn = data->conn; 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); + ssize_t rc; + + DEBUGASSERT(backend); + rc = gnutls_record_send(backend->session, mem, len); if(rc < 0) { *curlcode = (rc == GNUTLS_E_AGAIN) @@ -1433,6 +1454,8 @@ static ssize_t gtls_send(struct Curl_easy *data, static void close_one(struct ssl_connect_data *connssl) { struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + if(backend->session) { char buf[32]; /* Maybe the server has already sent a close notify alert. @@ -1475,6 +1498,8 @@ static int gtls_shutdown(struct Curl_easy *data, struct connectdata *conn, struct ssl_backend_data *backend = connssl->backend; int retval = 0; + DEBUGASSERT(backend); + #ifndef CURL_DISABLE_FTP /* This has only been tested on the proftpd server, and the mod_tls code sends a close notify alert without waiting for a close notify alert in @@ -1553,6 +1578,8 @@ static ssize_t gtls_recv(struct Curl_easy *data, /* connection data */ struct ssl_backend_data *backend = connssl->backend; ssize_t ret; + DEBUGASSERT(backend); + ret = gnutls_record_recv(backend->session, buf, buffersize); if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { *curlcode = CURLE_AGAIN; @@ -1624,6 +1651,7 @@ static void *gtls_get_internals(struct ssl_connect_data *connssl, { struct ssl_backend_data *backend = connssl->backend; (void)info; + DEBUGASSERT(backend); return backend->session; } diff --git a/contrib/libs/curl/lib/vtls/hostcheck.c b/contrib/libs/curl/lib/vtls/hostcheck.c new file mode 100644 index 0000000000..3ceaf93b84 --- /dev/null +++ b/contrib/libs/curl/lib/vtls/hostcheck.c @@ -0,0 +1,140 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_OPENSSL) \ + || defined(USE_GSKIT) \ + || defined(USE_SCHANNEL) +/* these backends use functions from this file */ + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif +#ifdef HAVE_NETINET_IN6_H +#error #include <netinet/in6.h> +#endif +#include "curl_memrchr.h" + +#include "hostcheck.h" +#include "strcase.h" +#include "hostip.h" + +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* check the two input strings with given length, but do not + assume they end in nul-bytes */ +static bool pmatch(const char *hostname, size_t hostlen, + const char *pattern, size_t patternlen) +{ + if(hostlen != patternlen) + return FALSE; + return strncasecompare(hostname, pattern, hostlen); +} + +/* + * Match a hostname against a wildcard pattern. + * E.g. + * "foo.host.com" matches "*.host.com". + * + * We use the matching rule described in RFC6125, section 6.4.3. + * https://datatracker.ietf.org/doc/html/rfc6125#section-6.4.3 + * + * In addition: ignore trailing dots in the host names and wildcards, so that + * the names are used normalized. This is what the browsers do. + * + * Do not allow wildcard matching on IP numbers. There are apparently + * certificates being used with an IP address in the CN field, thus making no + * apparent distinction between a name and an IP. We need to detect the use of + * an IP address and not wildcard match on such names. + * + * Return TRUE on a match. FALSE if not. + */ + +static bool hostmatch(const char *hostname, + size_t hostlen, + const char *pattern, + size_t patternlen) +{ + const char *pattern_label_end, *wildcard, *hostname_label_end; + size_t prefixlen, suffixlen; + + /* normalize pattern and hostname by stripping off trailing dots */ + DEBUGASSERT(patternlen); + if(hostname[hostlen-1]=='.') + hostlen--; + if(pattern[patternlen-1]=='.') + patternlen--; + + wildcard = memchr(pattern, '*', patternlen); + if(!wildcard) + return pmatch(hostname, hostlen, pattern, patternlen); + + /* detect IP address as hostname and fail the match if so */ + if(Curl_host_is_ipnum(hostname)) + return FALSE; + + /* We require at least 2 dots in the pattern to avoid too wide wildcard + match. */ + pattern_label_end = memchr(pattern, '.', patternlen); + if(!pattern_label_end || + (memrchr(pattern, '.', patternlen) == pattern_label_end) || + strncasecompare(pattern, "xn--", 4)) + return pmatch(hostname, hostlen, pattern, patternlen); + + hostname_label_end = memchr(hostname, '.', hostlen); + if(!hostname_label_end) + return FALSE; + else { + size_t skiphost = hostname_label_end - hostname; + size_t skiplen = pattern_label_end - pattern; + if(!pmatch(hostname_label_end, hostlen - skiphost, + pattern_label_end, patternlen - skiplen)) + return FALSE; + } + /* The wildcard must match at least one character, so the left-most + label of the hostname is at least as large as the left-most label + of the pattern. */ + if(hostname_label_end - hostname < pattern_label_end - pattern) + return FALSE; + + prefixlen = wildcard - pattern; + suffixlen = pattern_label_end - (wildcard + 1); + return strncasecompare(pattern, hostname, prefixlen) && + strncasecompare(wildcard + 1, hostname_label_end - suffixlen, + suffixlen) ? TRUE : FALSE; +} + +/* + * Curl_cert_hostcheck() returns TRUE if a match and FALSE if not. + */ +bool Curl_cert_hostcheck(const char *match, size_t matchlen, + const char *hostname, size_t hostlen) +{ + if(match && *match && hostname && *hostname) + return hostmatch(hostname, hostlen, match, matchlen); + return FALSE; +} + +#endif /* OPENSSL, GSKIT or schannel+wince */ diff --git a/contrib/libs/curl/lib/vtls/mesalink.h b/contrib/libs/curl/lib/vtls/hostcheck.h index 03f520c1dc..aa966403dd 100644 --- a/contrib/libs/curl/lib/vtls/mesalink.h +++ b/contrib/libs/curl/lib/vtls/hostcheck.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_MESALINK_H -#define HEADER_CURL_MESALINK_H +#ifndef HEADER_CURL_HOSTCHECK_H +#define HEADER_CURL_HOSTCHECK_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,8 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2017 - 2018, Yiming Jing, <jingyiming@baidu.com> - * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,11 +21,11 @@ * KIND, either express or implied. * ***************************************************************************/ -#include "curl_setup.h" -#ifdef USE_MESALINK +#include <curl/curl.h> -extern const struct Curl_ssl Curl_ssl_mesalink; +/* returns TRUE if there's a match */ +bool Curl_cert_hostcheck(const char *match_pattern, size_t matchlen, + const char *hostname, size_t hostlen); -#endif /* USE_MESALINK */ -#endif /* HEADER_CURL_MESALINK_H */ +#endif /* HEADER_CURL_HOSTCHECK_H */ diff --git a/contrib/libs/curl/lib/vtls/mbedtls.c b/contrib/libs/curl/lib/vtls/mbedtls.c index e177d3990d..d59ad92581 100644 --- a/contrib/libs/curl/lib/vtls/mbedtls.c +++ b/contrib/libs/curl/lib/vtls/mbedtls.c @@ -41,9 +41,6 @@ #error #include <mbedtls/net.h> #endif #error #include <mbedtls/ssl.h> -#if MBEDTLS_VERSION_NUMBER < 0x03000000 -#error #include <mbedtls/certs.h> -#endif #error #include <mbedtls/x509.h> #error #include <mbedtls/error.h> @@ -80,7 +77,9 @@ struct ssl_backend_data { int server_fd; mbedtls_x509_crt cacert; mbedtls_x509_crt clicert; +#ifdef MBEDTLS_X509_CRL_PARSE_C mbedtls_x509_crl crl; +#endif mbedtls_pk_context pk; mbedtls_ssl_config config; const char *protocols[3]; @@ -231,6 +230,8 @@ set_ssl_version_min_max(struct Curl_easy *data, struct connectdata *conn, long ssl_version_max = SSL_CONN_CONFIG(version_max); CURLcode result = CURLE_OK; + DEBUGASSERT(backend); + switch(ssl_version) { case CURL_SSLVERSION_DEFAULT: case CURL_SSLVERSION_TLSv1: @@ -286,6 +287,8 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, int ret = -1; char errorbuf[128]; + DEBUGASSERT(backend); + if((SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2) || (SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv3)) { failf(data, "Not supported SSL version"); @@ -335,11 +338,12 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); failf(data, "Error importing ca cert blob - mbedTLS: (-0x%04X) %s", -ret, errorbuf); - return ret; + return CURLE_SSL_CERTPROBLEM; } } if(ssl_cafile && verifypeer) { +#ifdef MBEDTLS_FS_IO ret = mbedtls_x509_crt_parse_file(&backend->cacert, ssl_cafile); if(ret<0) { @@ -348,9 +352,14 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, ssl_cafile, -ret, errorbuf); return CURLE_SSL_CACERT_BADFILE; } +#else + failf(data, "mbedtls: functions that use the filesystem not built in"); + return CURLE_NOT_BUILT_IN; +#endif } if(ssl_capath) { +#ifdef MBEDTLS_FS_IO ret = mbedtls_x509_crt_parse_path(&backend->cacert, ssl_capath); if(ret<0) { @@ -361,12 +370,17 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, if(verifypeer) return CURLE_SSL_CACERT_BADFILE; } +#else + failf(data, "mbedtls: functions that use the filesystem not built in"); + return CURLE_NOT_BUILT_IN; +#endif } /* Load the client certificate */ mbedtls_x509_crt_init(&backend->clicert); if(ssl_cert) { +#ifdef MBEDTLS_FS_IO ret = mbedtls_x509_crt_parse_file(&backend->clicert, ssl_cert); if(ret) { @@ -376,6 +390,10 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, return CURLE_SSL_CERTPROBLEM; } +#else + failf(data, "mbedtls: functions that use the filesystem not built in"); + return CURLE_NOT_BUILT_IN; +#endif } if(ssl_cert_blob) { @@ -388,7 +406,7 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, memcpy(newblob, ssl_cert_blob->data, ssl_cert_blob->len); newblob[ssl_cert_blob->len] = 0; /* null terminate */ ret = mbedtls_x509_crt_parse(&backend->clicert, newblob, - ssl_cert_blob->len); + ssl_cert_blob->len + 1); free(newblob); if(ret) { @@ -404,6 +422,7 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, if(SSL_SET_OPTION(key) || SSL_SET_OPTION(key_blob)) { if(SSL_SET_OPTION(key)) { +#ifdef MBEDTLS_FS_IO #if MBEDTLS_VERSION_NUMBER >= 0x03000000 ret = mbedtls_pk_parse_keyfile(&backend->pk, SSL_SET_OPTION(key), SSL_SET_OPTION(key_passwd), @@ -420,6 +439,10 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, SSL_SET_OPTION(key), -ret, errorbuf); return CURLE_SSL_CERTPROBLEM; } +#else + failf(data, "mbedtls: functions that use the filesystem not built in"); + return CURLE_NOT_BUILT_IN; +#endif } else { const struct curl_blob *ssl_key_blob = SSL_SET_OPTION(key_blob); @@ -452,9 +475,11 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, } /* Load the CRL */ +#ifdef MBEDTLS_X509_CRL_PARSE_C mbedtls_x509_crl_init(&backend->crl); if(ssl_crlfile) { +#ifdef MBEDTLS_FS_IO ret = mbedtls_x509_crl_parse_file(&backend->crl, ssl_crlfile); if(ret) { @@ -464,17 +489,21 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, return CURLE_SSL_CRL_BADFILE; } +#else + failf(data, "mbedtls: functions that use the filesystem not built in"); + return CURLE_NOT_BUILT_IN; +#endif + } +#else + if(ssl_crlfile) { + failf(data, "mbedtls: crl support not built in"); + return CURLE_NOT_BUILT_IN; } +#endif infof(data, "mbedTLS: Connecting to %s:%ld", hostname, port); mbedtls_ssl_config_init(&backend->config); - - 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, @@ -484,6 +513,12 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, return CURLE_SSL_CONNECT_ERROR; } + mbedtls_ssl_init(&backend->ssl); + if(mbedtls_ssl_setup(&backend->ssl, &backend->config)) { + failf(data, "mbedTLS: ssl_init failed"); + return CURLE_SSL_CONNECT_ERROR; + } + /* new profile with RSA min key len = 1024 ... */ mbedtls_ssl_conf_cert_profile(&backend->config, &mbedtls_x509_crt_profile_fr); @@ -555,18 +590,25 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn, mbedtls_ssl_conf_ca_chain(&backend->config, &backend->cacert, +#ifdef MBEDTLS_X509_CRL_PARSE_C &backend->crl); +#else + NULL); +#endif if(SSL_SET_OPTION(key) || SSL_SET_OPTION(key_blob)) { mbedtls_ssl_conf_own_cert(&backend->config, &backend->clicert, &backend->pk); } - 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; + { + char *snihost = Curl_ssl_snihost(data, hostname, NULL); + if(!snihost || mbedtls_ssl_set_hostname(&backend->ssl, snihost)) { + /* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and + the name to set in the SNI extension. So even if curl connects to a + host specified as an IP address, this function must be used. */ + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } } #ifdef HAS_ALPN @@ -627,6 +669,8 @@ mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn, const mbedtls_x509_crt *peercert; const char * const pinnedpubkey = SSL_PINNED_PUB_KEY(); + DEBUGASSERT(backend); + conn->recv[sockindex] = mbed_recv; conn->send[sockindex] = mbed_send; @@ -806,6 +850,7 @@ mbed_connect_step3(struct Curl_easy *data, struct connectdata *conn, struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + DEBUGASSERT(backend); if(SSL_SET_OPTION(primary.sessionid)) { int ret; @@ -862,6 +907,8 @@ static ssize_t mbed_send(struct Curl_easy *data, int sockindex, struct ssl_backend_data *backend = connssl->backend; int ret = -1; + DEBUGASSERT(backend); + ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len); if(ret < 0) { @@ -886,6 +933,8 @@ static void mbedtls_close(struct Curl_easy *data, char buf[32]; (void) data; + DEBUGASSERT(backend); + /* Maybe the server has already sent a close notify alert. Read it to avoid an RST on the TCP connection. */ (void)mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, sizeof(buf)); @@ -893,7 +942,9 @@ static void mbedtls_close(struct Curl_easy *data, mbedtls_pk_free(&backend->pk); mbedtls_x509_crt_free(&backend->clicert); mbedtls_x509_crt_free(&backend->cacert); +#ifdef MBEDTLS_X509_CRL_PARSE_C mbedtls_x509_crl_free(&backend->crl); +#endif mbedtls_ssl_config_free(&backend->config); mbedtls_ssl_free(&backend->ssl); mbedtls_ctr_drbg_free(&backend->ctr_drbg); @@ -912,6 +963,8 @@ static ssize_t mbed_recv(struct Curl_easy *data, int num, int ret = -1; ssize_t len = -1; + DEBUGASSERT(backend); + ret = mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, buffersize); @@ -1146,6 +1199,7 @@ static bool mbedtls_data_pending(const struct connectdata *conn, { const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); return mbedtls_ssl_get_bytes_avail(&backend->ssl) != 0; } @@ -1175,6 +1229,7 @@ static void *mbedtls_get_internals(struct ssl_connect_data *connssl, { struct ssl_backend_data *backend = connssl->backend; (void)info; + DEBUGASSERT(backend); return &backend->ssl; } diff --git a/contrib/libs/curl/lib/vtls/mesalink.c b/contrib/libs/curl/lib/vtls/mesalink.c deleted file mode 100644 index 35a916586e..0000000000 --- a/contrib/libs/curl/lib/vtls/mesalink.c +++ /dev/null @@ -1,679 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2017 - 2018, Yiming Jing, <jingyiming@baidu.com> - * Copyright (C) 1998 - 2021, 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; -}; - -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 Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - char *ciphers; - 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_HOST_NAME(); - size_t hostname_len = strlen(hostname); - SSL_METHOD *req_method = NULL; - curl_socket_t sockfd = conn->sock[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - - 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:"); - } - else { - infof(data, "successfully set certificate verify locations:"); - } - infof(data, " CAfile: %s", - SSL_CONN_CONFIG(CAfile) ? SSL_CONN_CONFIG(CAfile): "none"); - infof(data, " CApath: %s", - 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", - 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", 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(data); - if(!Curl_ssl_getsessionid(data, conn, - SSL_IS_PROXY() ? TRUE : FALSE, - &ssl_sessionid, NULL, sockindex)) { - /* we got a session id, use it! */ - if(!SSL_set_session(backend->handle, ssl_sessionid)) { - Curl_ssl_sessionid_unlock(data); - 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"); - } - Curl_ssl_sessionid_unlock(data); - } -#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 Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - int ret = -1; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - - 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", - 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; - bool isproxy = SSL_IS_PROXY() ? TRUE : FALSE; - struct ssl_backend_data *backend = connssl->backend; - - our_ssl_sessionid = SSL_get_session(backend->handle); - - Curl_ssl_sessionid_lock(data); - incache = - !(Curl_ssl_getsessionid(data, conn, isproxy, &old_ssl_sessionid, NULL, - sockindex)); - if(incache) { - if(old_ssl_sessionid != our_ssl_sessionid) { - infof(data, "old SSL session ID is stale, removing"); - Curl_ssl_delsessionid(data, old_ssl_sessionid); - incache = FALSE; - } - } - - if(!incache) { - result = - Curl_ssl_addsessionid(data, conn, isproxy, our_ssl_sessionid, 0, - sockindex, NULL); - if(result) { - Curl_ssl_sessionid_unlock(data); - failf(data, "failed to store ssl session"); - return result; - } - } - Curl_ssl_sessionid_unlock(data); - } -#endif /* MESALINK_HAVE_SESSION */ - - connssl->connecting_state = ssl_connect_done; - - return result; -} - -static ssize_t -mesalink_send(struct Curl_easy *data, int sockindex, const void *mem, - size_t len, CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - 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(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 -mesalink_close(struct Curl_easy *data, struct connectdata *conn, int sockindex) -{ - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - - (void) data; - - 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 Curl_easy *data, int num, char *buf, size_t buffersize, - CURLcode *curlcode) -{ - struct connectdata *conn = data->conn; - struct ssl_connect_data *connssl = &conn->ssl[num]; - struct ssl_backend_data *backend = connssl->backend; - 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(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 -mesalink_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "MesaLink/%s", MESALINK_VERSION_STRING); -} - -static int -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 -mesalink_shutdown(struct Curl_easy *data, - struct connectdata *conn, int sockindex) -{ - int retval = 0; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - struct ssl_backend_data *backend = connssl->backend; - - (void) data; - - if(backend->handle) { - SSL_free(backend->handle); - backend->handle = NULL; - } - return retval; -} - -static CURLcode -mesalink_connect_common(struct Curl_easy *data, struct connectdata *conn, - int sockindex, bool nonblocking, bool *done) -{ - CURLcode result; - struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - curl_socket_t sockfd = conn->sock[sockindex]; - timediff_t timeout_ms; - int what; - - /* check if the connection has already been established */ - if(ssl_connection_complete == connssl->state) { - *done = TRUE; - return CURLE_OK; - } - - if(ssl_connect_1 == connssl->connecting_state) { - /* 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(data, conn, sockindex); - if(result) - return result; - } - - while(ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state) { - - /* check 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(data, conn, sockindex); - - if(result || - (nonblocking && (ssl_connect_2 == connssl->connecting_state || - ssl_connect_2_reading == connssl->connecting_state || - ssl_connect_2_writing == connssl->connecting_state))) { - return result; - } - } /* repeat step2 until all transactions are done. */ - - if(ssl_connect_3 == connssl->connecting_state) { - result = 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 -mesalink_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, - int sockindex, bool *done) -{ - return mesalink_connect_common(data, conn, sockindex, TRUE, done); -} - -static CURLcode -mesalink_connect(struct Curl_easy *data, struct connectdata *conn, - int sockindex) -{ - CURLcode result; - bool done = FALSE; - - result = mesalink_connect_common(data, conn, sockindex, FALSE, &done); - if(result) - return result; - - DEBUGASSERT(done); - - return CURLE_OK; -} - -static void * -mesalink_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_mesalink = { - { CURLSSLBACKEND_MESALINK, "MesaLink" }, /* info */ - - SSLSUPP_SSL_CTX, - - sizeof(struct ssl_backend_data), - - mesalink_init, /* init */ - Curl_none_cleanup, /* cleanup */ - mesalink_version, /* version */ - Curl_none_check_cxn, /* check_cxn */ - mesalink_shutdown, /* shutdown */ - Curl_none_data_pending, /* data_pending */ - Curl_none_random, /* random */ - Curl_none_cert_status_request, /* cert_status_request */ - mesalink_connect, /* connect */ - mesalink_connect_nonblocking, /* connect_nonblocking */ - Curl_ssl_getsock, /* getsock */ - mesalink_get_internals, /* get_internals */ - 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 */ - NULL, /* sha256sum */ - NULL, /* associate_connection */ - NULL /* disassociate_connection */ -}; - -#endif diff --git a/contrib/libs/curl/lib/vtls/nss.c b/contrib/libs/curl/lib/vtls/nss.c index 2b44f05126..558e3bed39 100644 --- a/contrib/libs/curl/lib/vtls/nss.c +++ b/contrib/libs/curl/lib/vtls/nss.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -488,6 +488,9 @@ static CURLcode nss_create_object(struct ssl_connect_data *connssl, const int slot_id = (cacert) ? 0 : 1; char *slot_name = aprintf("PEM Token #%d", slot_id); struct ssl_backend_data *backend = connssl->backend; + + DEBUGASSERT(backend); + if(!slot_name) return CURLE_OUT_OF_MEMORY; @@ -882,8 +885,14 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg) !memcmp(ALPN_HTTP_1_1, buf, ALPN_HTTP_1_1_LENGTH)) { conn->negnpn = CURL_HTTP_VERSION_1_1; } - Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? - BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); + + /* This callback might get called when PR_Recv() is used within + * close_one() during a connection shutdown. At that point there might not + * be any "bundle" associated with the connection anymore. + */ + if(conn->bundle) + Curl_multiuse_state(data, conn->negnpn == CURL_HTTP_VERSION_2 ? + BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); } } @@ -1105,9 +1114,12 @@ static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl, { CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; struct ssl_backend_data *backend = connssl->backend; - struct Curl_easy *data = backend->data; + struct Curl_easy *data = NULL; CERTCertificate *cert; + DEBUGASSERT(backend); + data = backend->data; + if(!pinnedpubkey) /* no pinned public key specified */ return CURLE_OK; @@ -1158,10 +1170,15 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, { 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; + struct Curl_easy *data = NULL; + const char *nickname = NULL; static const char pem_slotname[] = "PEM Token #1"; + DEBUGASSERT(backend); + + data = backend->data; + nickname = backend->client_nickname; + if(backend->obj_clicert) { /* use the cert/key provided by PEM reader */ SECItem cert_der = { 0, NULL, 0 }; @@ -1529,6 +1546,8 @@ static int nss_check_cxn(struct connectdata *conn) int rc; char buf; + DEBUGASSERT(backend); + rc = PR_Recv(backend->handle, (void *)&buf, 1, PR_MSG_PEEK, PR_SecondsToInterval(1)); @@ -1545,7 +1564,11 @@ static void close_one(struct ssl_connect_data *connssl) { /* before the cleanup, check whether we are using a client certificate */ struct ssl_backend_data *backend = connssl->backend; - const bool client_cert = (backend->client_nickname != NULL) + bool client_cert = true; + + DEBUGASSERT(backend); + + client_cert = (backend->client_nickname != NULL) || (backend->obj_clicert != NULL); if(backend->handle) { @@ -1587,8 +1610,13 @@ static void nss_close(struct Curl_easy *data, struct connectdata *conn, struct ssl_connect_data *connssl_proxy = &conn->proxy_ssl[sockindex]; #endif struct ssl_backend_data *backend = connssl->backend; - (void)data; + + DEBUGASSERT(backend); +#ifndef CURL_DISABLE_PROXY + DEBUGASSERT(connssl_proxy->backend != NULL); +#endif + if(backend->handle #ifndef CURL_DISABLE_PROXY || connssl_proxy->backend->handle @@ -1816,6 +1844,8 @@ static CURLcode nss_fail_connect(struct ssl_connect_data *connssl, { struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + if(is_nss_error(curlerr)) { /* read NSPR error code */ PRErrorCode err = PR_GetError(); @@ -1842,6 +1872,9 @@ static CURLcode nss_set_blocking(struct ssl_connect_data *connssl, { PRSocketOptionData sock_opt; struct ssl_backend_data *backend = connssl->backend; + + DEBUGASSERT(backend); + sock_opt.option = PR_SockOpt_Nonblocking; sock_opt.value.non_blocking = !blocking; @@ -1865,7 +1898,6 @@ static CURLcode nss_setup_connect(struct Curl_easy *data, 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 @@ -1878,6 +1910,13 @@ static CURLcode nss_setup_connect(struct Curl_easy *data, SSL_LIBRARY_VERSION_TLS_1_0 #endif }; + char *snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); + if(!snihost) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + + DEBUGASSERT(backend); backend->data = data; @@ -2028,9 +2067,12 @@ static CURLcode nss_setup_connect(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY if(conn->proxy_ssl[sockindex].use) { + struct ssl_backend_data *proxy_backend; + proxy_backend = conn->proxy_ssl[sockindex].backend; DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state); - DEBUGASSERT(conn->proxy_ssl[sockindex].backend->handle != NULL); - nspr_io = conn->proxy_ssl[sockindex].backend->handle; + DEBUGASSERT(proxy_backend); + DEBUGASSERT(proxy_backend->handle); + nspr_io = proxy_backend->handle; second_layer = TRUE; } #endif @@ -2140,11 +2182,11 @@ static CURLcode nss_setup_connect(struct Curl_easy *data, goto error; /* propagate hostname to the TLS layer */ - if(SSL_SetURL(backend->handle, SSL_HOST_NAME()) != SECSuccess) + if(SSL_SetURL(backend->handle, snihost) != SECSuccess) goto error; /* prevent NSS from re-using the session for a different hostname */ - if(SSL_SetSockPeerID(backend->handle, SSL_HOST_NAME()) != SECSuccess) + if(SSL_SetSockPeerID(backend->handle, snihost) != SECSuccess) goto error; return CURLE_OK; @@ -2172,6 +2214,8 @@ static CURLcode nss_do_connect(struct Curl_easy *data, goto error; } + DEBUGASSERT(backend); + /* Force the handshake now */ timeout = PR_MillisecondsToInterval((PRUint32) time_left); if(SSL_ForceHandshakeWithTimeout(backend->handle, timeout) != SECSuccess) { @@ -2305,6 +2349,8 @@ static ssize_t nss_send(struct Curl_easy *data, /* transfer */ struct ssl_backend_data *backend = connssl->backend; ssize_t rc; + DEBUGASSERT(backend); + /* The SelectClientCert() hook uses this for infof() and failf() but the handle stored in nss_setup_connect() could have already been freed. */ backend->data = data; @@ -2344,6 +2390,8 @@ static ssize_t nss_recv(struct Curl_easy *data, /* transfer */ struct ssl_backend_data *backend = connssl->backend; ssize_t nread; + DEBUGASSERT(backend); + /* The SelectClientCert() hook uses this for infof() and failf() but the handle stored in nss_setup_connect() could have already been freed. */ backend->data = data; @@ -2442,6 +2490,7 @@ static void *nss_get_internals(struct ssl_connect_data *connssl, { struct ssl_backend_data *backend = connssl->backend; (void)info; + DEBUGASSERT(backend); return backend->handle; } diff --git a/contrib/libs/curl/lib/vtls/openssl.c b/contrib/libs/curl/lib/vtls/openssl.c index 8c0f946dd5..d3ed3e7ba0 100644 --- a/contrib/libs/curl/lib/vtls/openssl.c +++ b/contrib/libs/curl/lib/vtls/openssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -91,7 +91,6 @@ #endif #include "warnless.h" -#include "non-ascii.h" /* for Curl_convert_from_utf8 prototype */ /* The last #include files should be: */ #include "curl_memory.h" @@ -266,7 +265,7 @@ struct ssl_backend_data { #endif }; -static void ossl_associate_connection(struct Curl_easy *data, +static bool ossl_associate_connection(struct Curl_easy *data, struct connectdata *conn, int sockindex); @@ -1432,6 +1431,9 @@ static void ossl_closeone(struct Curl_easy *data, struct ssl_connect_data *connssl) { struct ssl_backend_data *backend = connssl->backend; + + DEBUGASSERT(backend); + if(backend->handle) { char buf[32]; set_logger(conn, data); @@ -1489,6 +1491,8 @@ static int ossl_shutdown(struct Curl_easy *data, struct ssl_backend_data *backend = connssl->backend; int loop = 10; + DEBUGASSERT(backend); + #ifndef CURL_DISABLE_FTP /* This has only been tested on the proftpd server, and the mod_tls code sends a close notify alert without waiting for a close notify alert in @@ -1610,54 +1614,26 @@ static void ossl_close_all(struct Curl_easy *data) /* ====================================================== */ /* - * Match subjectAltName against the host name. This requires a conversion - * in CURL_DOES_CONVERSIONS builds. + * Match subjectAltName against the host name. */ static bool subj_alt_hostcheck(struct Curl_easy *data, - const char *match_pattern, const char *hostname, + const char *match_pattern, + size_t matchlen, + const char *hostname, + size_t hostlen, 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\"", - 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)) { + if(Curl_cert_hostcheck(match_pattern, matchlen, hostname, hostlen)) { infof(data, " subjectAltName: host \"%s\" matched cert's \"%s\"", dispname, match_pattern); return TRUE; } return FALSE; } -#endif - /* Quote from RFC2818 section 3.1 "Server Identity" @@ -1698,6 +1674,7 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, bool iPAddress = FALSE; /* if a iPAddress field exists in the cert */ const char * const hostname = SSL_HOST_NAME(); const char * const dispname = SSL_HOST_DISPNAME(); + size_t hostlen = strlen(hostname); #ifdef ENABLE_IPV6 if(conn->bits.ipv6_ip && @@ -1760,7 +1737,9 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, 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)) { + subj_alt_hostcheck(data, + altptr, + altlen, hostname, hostlen, dispname)) { dnsmatched = TRUE; } break; @@ -1796,17 +1775,17 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, 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; + int i = -1; + unsigned char *peer_CN = NULL; + int peerlen = 0; /* 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) + if(name) { + int j; 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, @@ -1822,19 +1801,21 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, 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); + peerlen = ASN1_STRING_length(tmp); + if(peerlen >= 0) { + peer_CN = OPENSSL_malloc(peerlen + 1); if(peer_CN) { - memcpy(peer_CN, ASN1_STRING_get0_data(tmp), j); - peer_CN[j] = '\0'; + memcpy(peer_CN, ASN1_STRING_get0_data(tmp), peerlen); + peer_CN[peerlen] = '\0'; } + else + result = CURLE_OUT_OF_MEMORY; } } else /* not a UTF8 name */ - j = ASN1_STRING_to_UTF8(&peer_CN, tmp); + peerlen = ASN1_STRING_to_UTF8(&peer_CN, tmp); - if(peer_CN && (curlx_uztosi(strlen((char *)peer_CN)) != j)) { + if(peer_CN && (curlx_uztosi(strlen((char *)peer_CN)) != peerlen)) { /* there was a terminating zero before the end of string, this cannot match and we return failure! */ failf(data, "SSL: illegal cert name field"); @@ -1843,19 +1824,6 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, } } - 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 */ ; @@ -1864,7 +1832,8 @@ CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, "SSL: unable to obtain common name from peer certificate"); result = CURLE_PEER_FAILED_VERIFICATION; } - else if(!Curl_cert_hostcheck((const char *)peer_CN, hostname)) { + else if(!Curl_cert_hostcheck((const char *)peer_CN, + peerlen, hostname, hostlen)) { failf(data, "SSL: certificate subject name '%s' does not match " "target host name '%s'", peer_CN, dispname); result = CURLE_PEER_FAILED_VERIFICATION; @@ -1898,8 +1867,11 @@ static CURLcode verifystatus(struct Curl_easy *data, int cert_status, crl_reason; ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; int ret; + long len; + + DEBUGASSERT(backend); - long len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status); + len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status); if(!status) { failf(data, "No OCSP response received"); @@ -2158,7 +2130,10 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, struct connectdata *conn = userp; struct ssl_connect_data *connssl = &conn->ssl[0]; struct ssl_backend_data *backend = connssl->backend; - struct Curl_easy *data = backend->logger; + struct Curl_easy *data = NULL; + + DEBUGASSERT(backend); + data = backend->logger; if(!conn || !data || !data->set.fdebug || (direction != 0 && direction != 1)) @@ -2363,10 +2338,12 @@ set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn) case CURL_SSLVERSION_TLSv1_2: ossl_ssl_version_min = TLS1_2_VERSION; break; -#ifdef TLS1_3_VERSION case CURL_SSLVERSION_TLSv1_3: +#ifdef TLS1_3_VERSION ossl_ssl_version_min = TLS1_3_VERSION; break; +#else + return CURLE_NOT_BUILT_IN; #endif } @@ -2422,6 +2399,8 @@ set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn) #ifdef OPENSSL_IS_BORINGSSL typedef uint32_t ctx_option_t; +#elif OPENSSL_VERSION_NUMBER >= 0x30000000L +typedef uint64_t ctx_option_t; #else typedef long ctx_option_t; #endif @@ -2442,6 +2421,8 @@ set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, #ifdef TLS1_3_VERSION { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION); *ctx_options |= SSL_OP_NO_TLSv1_2; } @@ -2521,13 +2502,12 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) return 0; conn = (struct connectdata*) SSL_get_ex_data(ssl, connectdata_idx); - if(!conn) - return 0; - data = (struct Curl_easy *) SSL_get_ex_data(ssl, data_idx); - /* The sockindex has been stored as a pointer to an array element */ sockindex_ptr = (curl_socket_t*) SSL_get_ex_data(ssl, sockindex_idx); + if(!conn || !data || !sockindex_ptr) + return 0; + sockindex = (int)(sockindex_ptr - conn->sock); isproxy = SSL_get_ex_data(ssl, proxy_idx) ? TRUE : FALSE; @@ -2670,6 +2650,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, bool imported_native_ca = false; DEBUGASSERT(ssl_connect_1 == connssl->connecting_state); + DEBUGASSERT(backend); /* Make funny stuff to get random input */ result = ossl_seed(data); @@ -2736,8 +2717,8 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, implementations is desired." The "-no_ticket" option was introduced in OpenSSL 0.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 + disable "rfc4507bis session ticket support". rfc4507bis was later turned + into the proper RFC5077: https://datatracker.ietf.org/doc/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 @@ -2942,7 +2923,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, /* 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 */ + https://datatracker.ietf.org/doc/html/rfc5280 */ if((SSL_CONN_CONFIG(verifypeer) || SSL_CONN_CONFIG(verifyhost)) && (SSL_SET_OPTION(native_ca_store))) { X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx); @@ -3243,44 +3224,48 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data, (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) && #endif sni) { - size_t nlen = strlen(hostname); - if((long)nlen >= data->set.buffer_size) - /* this is seriously messed up */ + char *snihost = Curl_ssl_snihost(data, hostname, NULL); + if(!snihost || !SSL_set_tlsext_host_name(backend->handle, snihost)) { + failf(data, "Failed set SNI"); return CURLE_SSL_CONNECT_ERROR; - - /* RFC 6066 section 3 says the SNI field is case insensitive, but browsers - send the data lowercase and subsequently there are now numerous servers - out there that don't work unless the name is lowercased */ - Curl_strntolower(data->state.buffer, hostname, nlen); - data->state.buffer[nlen] = 0; - if(!SSL_set_tlsext_host_name(backend->handle, data->state.buffer)) - infof(data, "WARNING: failed to configure server name indication (SNI) " - "TLS extension"); + } } #endif - ossl_associate_connection(data, conn, sockindex); + if(!ossl_associate_connection(data, conn, sockindex)) { + /* Maybe the internal errors of SSL_get_ex_new_index or SSL_set_ex_data */ + failf(data, "SSL: ossl_associate_connection failed: %s", + ossl_strerror(ERR_get_error(), error_buffer, + sizeof(error_buffer))); + return CURLE_SSL_CONNECT_ERROR; + } - Curl_ssl_sessionid_lock(data); - if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE, - &ssl_sessionid, NULL, sockindex)) { - /* we got a session id, use it! */ - if(!SSL_set_session(backend->handle, ssl_sessionid)) { - Curl_ssl_sessionid_unlock(data); - failf(data, "SSL: SSL_set_session failed: %s", - ossl_strerror(ERR_get_error(), error_buffer, - sizeof(error_buffer))); - return CURLE_SSL_CONNECT_ERROR; + if(SSL_SET_OPTION(primary.sessionid)) { + Curl_ssl_sessionid_lock(data); + if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE, + &ssl_sessionid, NULL, sockindex)) { + /* we got a session id, use it! */ + if(!SSL_set_session(backend->handle, ssl_sessionid)) { + Curl_ssl_sessionid_unlock(data); + 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"); } - /* Informational message */ - infof(data, "SSL re-using session ID"); + Curl_ssl_sessionid_unlock(data); } - Curl_ssl_sessionid_unlock(data); #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; + struct ssl_backend_data *proxy_backend; + SSL* handle = NULL; + proxy_backend = conn->proxy_ssl[sockindex].backend; + DEBUGASSERT(proxy_backend); + handle = proxy_backend->handle; DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state); DEBUGASSERT(handle != NULL); DEBUGASSERT(bio != NULL); @@ -3310,6 +3295,7 @@ static CURLcode ossl_connect_step2(struct Curl_easy *data, DEBUGASSERT(ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); + DEBUGASSERT(backend); ERR_clear_error(); @@ -3571,6 +3557,8 @@ static CURLcode get_cert_chain(struct Curl_easy *data, BIO *mem; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + sk = SSL_get_peer_cert_chain(backend->handle); if(!sk) { return CURLE_OUT_OF_MEMORY; @@ -3883,6 +3871,8 @@ static CURLcode servercert(struct Curl_easy *data, BIO *mem = BIO_new(BIO_s_mem()); struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + if(!mem) { failf(data, "BIO_new return NULL, " OSSL_PACKAGE @@ -3953,9 +3943,20 @@ static CURLcode servercert(struct Curl_easy *data, /* e.g. match issuer name with provided issuer certificate */ if(SSL_CONN_CONFIG(issuercert) || SSL_CONN_CONFIG(issuercert_blob)) { - if(SSL_CONN_CONFIG(issuercert_blob)) + if(SSL_CONN_CONFIG(issuercert_blob)) { fp = BIO_new_mem_buf(SSL_CONN_CONFIG(issuercert_blob)->data, (int)SSL_CONN_CONFIG(issuercert_blob)->len); + if(!fp) { + failf(data, + "BIO_new_mem_buf 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; + } + } else { fp = BIO_new(BIO_s_file()); if(!fp) { @@ -4222,11 +4223,13 @@ static bool ossl_data_pending(const struct connectdata *conn, int connindex) { const struct ssl_connect_data *connssl = &conn->ssl[connindex]; + DEBUGASSERT(connssl->backend); 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]; + DEBUGASSERT(proxyssl->backend); if(proxyssl->backend->handle && SSL_pending(proxyssl->backend->handle)) return TRUE; } @@ -4253,6 +4256,8 @@ static ssize_t ossl_send(struct Curl_easy *data, struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + ERR_clear_error(); memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; @@ -4332,6 +4337,8 @@ static ssize_t ossl_recv(struct Curl_easy *data, /* transfer */ struct ssl_connect_data *connssl = &conn->ssl[num]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + ERR_clear_error(); buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; @@ -4531,20 +4538,22 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl, { /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */ struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); return info == CURLINFO_TLS_SESSION ? (void *)backend->ctx : (void *)backend->handle; } -static void ossl_associate_connection(struct Curl_easy *data, +static bool ossl_associate_connection(struct Curl_easy *data, struct connectdata *conn, int sockindex) { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); /* If we don't have SSL context, do nothing. */ if(!backend->handle) - return; + return FALSE; if(SSL_SET_OPTION(primary.sessionid)) { int data_idx = ossl_get_ssl_data_index(); @@ -4554,19 +4563,26 @@ static void ossl_associate_connection(struct Curl_easy *data, if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 && proxy_idx >= 0) { + int data_status, conn_status, sockindex_status, proxy_status; + /* Store the data needed for the "new session" callback. * The sockindex is stored as a pointer to an array element. */ - SSL_set_ex_data(backend->handle, data_idx, data); - SSL_set_ex_data(backend->handle, connectdata_idx, conn); - SSL_set_ex_data(backend->handle, sockindex_idx, conn->sock + sockindex); + data_status = SSL_set_ex_data(backend->handle, data_idx, data); + conn_status = SSL_set_ex_data(backend->handle, connectdata_idx, conn); + sockindex_status = SSL_set_ex_data(backend->handle, sockindex_idx, + conn->sock + sockindex); #ifndef CURL_DISABLE_PROXY - SSL_set_ex_data(backend->handle, proxy_idx, SSL_IS_PROXY() ? (void *) 1: - NULL); + proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, + SSL_IS_PROXY() ? (void *) 1 : NULL); #else - SSL_set_ex_data(backend->handle, proxy_idx, NULL); + proxy_status = SSL_set_ex_data(backend->handle, proxy_idx, NULL); #endif + if(data_status && conn_status && sockindex_status && proxy_status) + return TRUE; } + return FALSE; } + return TRUE; } /* @@ -4583,6 +4599,7 @@ static void ossl_disassociate_connection(struct Curl_easy *data, struct connectdata *conn = data->conn; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); /* If we don't have SSL context, do nothing. */ if(!backend->handle) diff --git a/contrib/libs/curl/lib/vtls/openssl.h b/contrib/libs/curl/lib/vtls/openssl.h index 28058453c0..7df642bc9a 100644 --- a/contrib/libs/curl/lib/vtls/openssl.h +++ b/contrib/libs/curl/lib/vtls/openssl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -30,11 +30,17 @@ * and ngtcp2.c */ -#include <openssl/x509v3.h> #include "urldata.h" +/* + * In an effort to avoid using 'X509 *' here, we instead use the struct + * x509_st version of the type so that we can forward-declare it here without + * having to include <openssl/x509v3.h>. Including that header causes name + * conflicts when libcurl is built with both Schannel and OpenSSL support. + */ +struct x509_st; CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn, - X509 *server_cert); + struct x509_st *server_cert); extern const struct Curl_ssl Curl_ssl_openssl; #endif /* USE_OPENSSL */ diff --git a/contrib/libs/curl/lib/vtls/rustls.c b/contrib/libs/curl/lib/vtls/rustls.c index 6dbb1ef3cd..0e651aed9d 100644 --- a/contrib/libs/curl/lib/vtls/rustls.c +++ b/contrib/libs/curl/lib/vtls/rustls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2020 - 2021, Jacob Hoffman-Andrews, + * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews, * <github@hoffman-andrews.com> * * This software is licensed as described in the file COPYING, which @@ -65,6 +65,7 @@ cr_data_pending(const struct connectdata *conn, int sockindex) { const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); return backend->data_pending; } @@ -118,7 +119,8 @@ cr_recv(struct Curl_easy *data, int sockindex, struct connectdata *conn = data->conn; struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; struct ssl_backend_data *const backend = connssl->backend; - struct rustls_connection *const rconn = backend->conn; + struct rustls_connection *rconn = NULL; + size_t n = 0; size_t tls_bytes_read = 0; size_t plain_bytes_copied = 0; @@ -126,6 +128,9 @@ cr_recv(struct Curl_easy *data, int sockindex, char errorbuf[255]; rustls_io_result io_error; + DEBUGASSERT(backend); + rconn = backend->conn; + io_error = rustls_connection_read_tls(rconn, read_cb, &conn->sock[sockindex], &tls_bytes_read); if(io_error == EAGAIN || io_error == EWOULDBLOCK) { @@ -215,13 +220,16 @@ cr_send(struct Curl_easy *data, int sockindex, struct connectdata *conn = data->conn; struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; struct ssl_backend_data *const backend = connssl->backend; - struct rustls_connection *const rconn = backend->conn; + struct rustls_connection *rconn = NULL; size_t plainwritten = 0; size_t tlswritten = 0; size_t tlswritten_total = 0; rustls_result rresult; rustls_io_result io_error; + DEBUGASSERT(backend); + rconn = backend->conn; + infof(data, "cr_send %ld bytes of plaintext", plainlen); if(plainlen > 0) { @@ -295,9 +303,13 @@ static CURLcode cr_init_backend(struct Curl_easy *data, struct connectdata *conn, struct ssl_backend_data *const backend) { - struct rustls_connection *rconn = backend->conn; + struct rustls_connection *rconn = NULL; struct rustls_client_config_builder *config_builder = NULL; - const char *const ssl_cafile = SSL_CONN_CONFIG(CAfile); + struct rustls_root_cert_store *roots = NULL; + const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob); + const char * const ssl_cafile = + /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ + (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile)); const bool verifypeer = SSL_CONN_CONFIG(verifypeer); const char *hostname = conn->host.name; char errorbuf[256]; @@ -308,6 +320,9 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn, { (const uint8_t *)ALPN_H2, ALPN_H2_LENGTH }, }; + DEBUGASSERT(backend); + rconn = backend->conn; + config_builder = rustls_client_config_builder_new(); #ifdef USE_HTTP2 infof(data, "offering ALPN for HTTP/1.1 and HTTP/2"); @@ -328,6 +343,29 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn, hostname = "example.invalid"; } } + else if(ca_info_blob) { + roots = rustls_root_cert_store_new(); + + /* Enable strict parsing only if verification isn't disabled. */ + result = rustls_root_cert_store_add_pem(roots, ca_info_blob->data, + ca_info_blob->len, verifypeer); + if(result != RUSTLS_RESULT_OK) { + failf(data, "failed to parse trusted certificates from blob"); + rustls_root_cert_store_free(roots); + rustls_client_config_free( + rustls_client_config_builder_build(config_builder)); + return CURLE_SSL_CACERT_BADFILE; + } + + result = rustls_client_config_builder_use_roots(config_builder, roots); + rustls_root_cert_store_free(roots); + if(result != RUSTLS_RESULT_OK) { + failf(data, "failed to load trusted certificates"); + rustls_client_config_free( + rustls_client_config_builder_build(config_builder)); + return CURLE_SSL_CACERT_BADFILE; + } + } else if(ssl_cafile) { result = rustls_client_config_builder_load_roots_from_file( config_builder, ssl_cafile); @@ -341,7 +379,14 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn, backend->config = rustls_client_config_builder_build(config_builder); DEBUGASSERT(rconn == NULL); - result = rustls_client_connection_new(backend->config, hostname, &rconn); + { + char *snihost = Curl_ssl_snihost(data, hostname, NULL); + if(!snihost) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + result = rustls_client_connection_new(backend->config, snihost, &rconn); + } if(result != RUSTLS_RESULT_OK) { rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen); failf(data, "rustls_client_connection_new: %.*s", errorlen, errorbuf); @@ -401,6 +446,8 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, curl_socket_t writefd; curl_socket_t readfd; + DEBUGASSERT(backend); + if(ssl_connection_none == connssl->state) { result = cr_init_backend(data, conn, connssl->backend); if(result != CURLE_OK) { @@ -495,7 +542,10 @@ cr_getsock(struct connectdata *conn, curl_socket_t *socks) struct ssl_connect_data *const connssl = &conn->ssl[FIRSTSOCKET]; curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; struct ssl_backend_data *const backend = connssl->backend; - struct rustls_connection *rconn = backend->conn; + struct rustls_connection *rconn = NULL; + + DEBUGASSERT(backend); + rconn = backend->conn; if(rustls_connection_wants_write(rconn)) { socks[0] = sockfd; @@ -514,6 +564,7 @@ cr_get_internals(struct ssl_connect_data *connssl, CURLINFO info UNUSED_PARAM) { struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); return &backend->conn; } @@ -526,6 +577,8 @@ cr_close(struct Curl_easy *data, struct connectdata *conn, CURLcode tmperr = CURLE_OK; ssize_t n = 0; + DEBUGASSERT(backend); + if(backend->conn) { rustls_connection_send_close_notify(backend->conn); n = cr_send(data, sockindex, NULL, 0, &tmperr); @@ -550,7 +603,8 @@ static size_t cr_version(char *buffer, size_t size) const struct Curl_ssl Curl_ssl_rustls = { { CURLSSLBACKEND_RUSTLS, "rustls" }, - SSLSUPP_TLS13_CIPHERSUITES, /* supports */ + SSLSUPP_CAINFO_BLOB | /* supports */ + SSLSUPP_TLS13_CIPHERSUITES, sizeof(struct ssl_backend_data), Curl_none_init, /* init */ diff --git a/contrib/libs/curl/lib/vtls/schannel.c b/contrib/libs/curl/lib/vtls/schannel.c index 0a8e60610d..04c8f3b6cf 100644 --- a/contrib/libs/curl/lib/vtls/schannel.c +++ b/contrib/libs/curl/lib/vtls/schannel.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de> * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> * @@ -325,13 +325,15 @@ get_alg_id_by_name(char *name) return 0; } +#define NUM_CIPHERS 47 /* There are 47 options listed above */ + static CURLcode set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers, ALG_ID *algIds) { char *startCur = ciphers; int algCount = 0; - while(startCur && (0 != *startCur) && (algCount < NUMOF_CIPHERS)) { + while(startCur && (0 != *startCur) && (algCount < NUM_CIPHERS)) { long alg = strtol(startCur, 0, 0); if(!alg) alg = get_alg_id_by_name(startCur); @@ -418,11 +420,14 @@ schannel_acquire_credential_handle(struct Curl_easy *data, { struct ssl_connect_data *connssl = &conn->ssl[sockindex]; SCHANNEL_CRED schannel_cred; + ALG_ID algIds[NUM_CIPHERS]; PCCERT_CONTEXT client_certs[1] = { NULL }; SECURITY_STATUS sspi_status = SEC_E_OK; CURLcode result; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + /* setup Schannel API options */ memset(&schannel_cred, 0, sizeof(schannel_cred)); schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; @@ -502,7 +507,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data, if(SSL_CONN_CONFIG(cipher_list)) { result = set_ssl_ciphers(&schannel_cred, SSL_CONN_CONFIG(cipher_list), - backend->algIds); + algIds); if(CURLE_OK != result) { failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG"); return result; @@ -765,11 +770,12 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, #ifdef ENABLE_IPV6 struct in6_addr addr6; #endif - TCHAR *host_name; CURLcode result; char * const hostname = SSL_HOST_NAME(); struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)", hostname, conn->remote_port)); @@ -846,10 +852,21 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, } if(!backend->cred) { + char *snihost; result = schannel_acquire_credential_handle(data, conn, sockindex); if(result != CURLE_OK) { return result; } + /* A hostname associated with the credential is needed by + InitializeSecurityContext for SNI and other reasons. */ + snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); + if(!snihost) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost); + if(!backend->cred->sni_hostname) + return CURLE_OUT_OF_MEMORY; } /* Warn if SNI is disabled due to use of an IP address */ @@ -936,10 +953,6 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, 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 @@ -948,13 +961,12 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn, 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->cred->cred_handle, NULL, backend->cred->sni_hostname, + backend->req_flags, 0, 0, (backend->use_alpn ? &inbuf_desc : NULL), 0, &backend->ctxt->ctxt_handle, &outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp); - curlx_unicodefree(host_name); - if(sspi_status != SEC_I_CONTINUE_NEEDED) { char buffer[STRERROR_LEN]; Curl_safefree(backend->ctxt); @@ -1027,15 +1039,16 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, SECURITY_STATUS sspi_status = SEC_E_OK; CURLcode result; bool doread; - char * const hostname = SSL_HOST_NAME(); const char *pubkey_ptr; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE; DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %hu (step 2/3)", - hostname, conn->remote_port)); + SSL_HOST_NAME(), conn->remote_port)); if(!backend->cred || !backend->ctxt) return CURLE_SSL_CONNECT_ERROR; @@ -1083,7 +1096,6 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, } for(;;) { - TCHAR *host_name; if(doread) { /* read encrypted handshake data from socket */ result = Curl_read_plain(conn->sock[sockindex], @@ -1136,17 +1148,12 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn, 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; - sspi_status = s_pSecFn->InitializeSecurityContext( &backend->cred->cred_handle, &backend->ctxt->ctxt_handle, - host_name, backend->req_flags, 0, 0, &inbuf_desc, 0, NULL, + backend->cred->sni_hostname, 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); @@ -1370,6 +1377,7 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn, struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + DEBUGASSERT(backend); DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)", @@ -1610,6 +1618,7 @@ schannel_connect_common(struct Curl_easy *data, struct connectdata *conn, */ { struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); conn->sslContext = &backend->ctxt->ctxt_handle; } #endif @@ -1640,6 +1649,8 @@ schannel_send(struct Curl_easy *data, int sockindex, CURLcode result; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + /* check if the maximum stream sizes were queried */ if(backend->stream_sizes.cbMaximumMessage == 0) { sspi_status = s_pSecFn->QueryContextAttributes( @@ -1788,6 +1799,8 @@ schannel_recv(struct Curl_easy *data, int sockindex, size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + /**************************************************************************** * Don't return or set backend->recv_unrecoverable_err unless in the cleanup. * The pattern for return error is set *err, optional infof, goto cleanup. @@ -2122,6 +2135,8 @@ static bool schannel_data_pending(const struct connectdata *conn, const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); + if(connssl->use) /* SSL/TLS is in use */ return (backend->decdata_offset > 0 || (backend->encdata_offset > 0 && !backend->encdata_is_incomplete)); @@ -2138,6 +2153,7 @@ static void schannel_session_free(void *ptr) cred->refcount--; if(cred->refcount == 0) { s_pSecFn->FreeCredentialsHandle(&cred->cred_handle); + curlx_unicodefree(cred->sni_hostname); Curl_safefree(cred); } } @@ -2157,6 +2173,7 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(data); + DEBUGASSERT(backend); if(connssl->use) { infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu", @@ -2170,7 +2187,6 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, SecBuffer outbuf; SecBufferDesc outbuf_desc; CURLcode result; - TCHAR *host_name; DWORD dwshut = SCHANNEL_SHUTDOWN; InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut)); @@ -2185,10 +2201,6 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, 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); @@ -2196,7 +2208,7 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, sspi_status = s_pSecFn->InitializeSecurityContext( &backend->cred->cred_handle, &backend->ctxt->ctxt_handle, - host_name, + backend->cred->sni_hostname, backend->req_flags, 0, 0, @@ -2207,8 +2219,6 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn, &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; @@ -2314,6 +2324,8 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, /* Result is returned to caller */ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; + DEBUGASSERT(backend); + /* if a path wasn't specified, don't pin */ if(!pinnedpubkey) return CURLE_OK; @@ -2434,6 +2446,7 @@ static void *schannel_get_internals(struct ssl_connect_data *connssl, { struct ssl_backend_data *backend = connssl->backend; (void)info; + DEBUGASSERT(backend); return &backend->ctxt->ctxt_handle; } diff --git a/contrib/libs/curl/lib/vtls/schannel.h b/contrib/libs/curl/lib/vtls/schannel.h index 77853aa30f..da60702771 100644 --- a/contrib/libs/curl/lib/vtls/schannel.h +++ b/contrib/libs/curl/lib/vtls/schannel.h @@ -8,7 +8,7 @@ * \___|\___/|_| \_\_____| * * Copyright (C) 2012, Marc Hoersken, <info@marc-hoersken.de>, et al. - * Copyright (C) 2012 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -71,11 +71,10 @@ CURLcode Curl_verify_certificate(struct Curl_easy *data, #endif #endif -#define NUMOF_CIPHERS 45 /* There are 45 listed in the MS headers */ - struct Curl_schannel_cred { CredHandle cred_handle; TimeStamp time_stamp; + TCHAR *sni_hostname; int refcount; }; @@ -104,7 +103,6 @@ struct ssl_backend_data { #ifdef HAS_MANUAL_VERIFY_API bool use_manual_cred_validation; /* true if manual cred validation is used */ #endif - ALG_ID algIds[NUMOF_CIPHERS]; }; #endif /* EXPOSE_SCHANNEL_INTERNAL_STRUCTS */ diff --git a/contrib/libs/curl/lib/vtls/schannel_verify.c b/contrib/libs/curl/lib/vtls/schannel_verify.c index 4966cd4945..202a814cd9 100644 --- a/contrib/libs/curl/lib/vtls/schannel_verify.c +++ b/contrib/libs/curl/lib/vtls/schannel_verify.c @@ -7,7 +7,7 @@ * * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de> * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> - * Copyright (C) 2012 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -465,6 +465,7 @@ static CURLcode verify_host(struct Curl_easy *data, CURLcode result = CURLE_PEER_FAILED_VERIFICATION; TCHAR *cert_hostname_buff = NULL; size_t cert_hostname_buff_index = 0; + size_t hostlen = strlen(conn_hostname); DWORD len = 0; DWORD actual_len = 0; @@ -520,10 +521,8 @@ static CURLcode verify_host(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY; } else { - int match_result; - - match_result = Curl_cert_hostcheck(cert_hostname, conn_hostname); - if(match_result == CURL_HOST_MATCH) { + if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname), + conn_hostname, hostlen)) { infof(data, "schannel: connection hostname (%s) validated " "against certificate name (%s)", @@ -577,6 +576,8 @@ CURLcode Curl_verify_certificate(struct Curl_easy *data, HCERTSTORE trust_store = NULL; const char * const conn_hostname = SSL_HOST_NAME(); + DEBUGASSERT(BACKEND); + sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, diff --git a/contrib/libs/curl/lib/vtls/sectransp.c b/contrib/libs/curl/lib/vtls/sectransp.c index f7a20b20b1..b2e1727278 100644 --- a/contrib/libs/curl/lib/vtls/sectransp.c +++ b/contrib/libs/curl/lib/vtls/sectransp.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 2012 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * Copyright (C) 2012 - 2017, Nick Zitzmann, <nickzman@gmail.com>. * * This software is licensed as described in the file COPYING, which @@ -603,7 +603,7 @@ const static struct st_cipher ciphertable[] = { CIPHER_WEAK_RC_ENCRYPTION), CIPHER_DEF(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC003 */ "ECDH-ECDSA-DES-CBC3-SHA", - CIPHER_STRONG_ENOUGH), + CIPHER_WEAK_3DES_ENCRYPTION), CIPHER_DEF(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC004 */ "ECDH-ECDSA-AES128-SHA", CIPHER_STRONG_ENOUGH), @@ -837,12 +837,14 @@ static OSStatus SocketRead(SSLConnectionRef connection, /*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; + int sock; OSStatus rtn = noErr; size_t bytesRead; ssize_t rrtn; int theErr; + DEBUGASSERT(backend); + sock = backend->ssl_sockfd; *dataLength = 0; for(;;) { @@ -898,13 +900,15 @@ static OSStatus SocketWrite(SSLConnectionRef connection, /*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; + int sock; ssize_t length; size_t dataLen = *dataLength; const UInt8 *dataPtr = (UInt8 *)data; OSStatus ortn; int theErr; + DEBUGASSERT(backend); + sock = backend->ssl_sockfd; *dataLength = 0; do { @@ -1376,6 +1380,8 @@ set_ssl_version_min_max(struct Curl_easy *data, struct connectdata *conn, long ssl_version_max = SSL_CONN_CONFIG(version_max); long max_supported_version_by_os; + DEBUGASSERT(backend); + /* 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. */ @@ -1684,6 +1690,8 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data, #if CURL_BUILD_MAC int darwinver_maj = 0, darwinver_min = 0; + DEBUGASSERT(backend); + GetDarwinVersionNumber(&darwinver_maj, &darwinver_min); #endif /* CURL_BUILD_MAC */ @@ -2028,8 +2036,13 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data, * 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)); + size_t snilen; + char *snihost = Curl_ssl_snihost(data, hostname, &snilen); + if(!snihost) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } + err = SSLSetPeerDomainName(backend->ssl_ctx, snihost, snilen); if(err != noErr) { infof(data, "WARNING: SSL: SSLSetPeerDomainName() failed: OSStatus %d", @@ -2542,6 +2555,7 @@ sectransp_connect_step2(struct Curl_easy *data, struct connectdata *conn, DEBUGASSERT(ssl_connect_2 == connssl->connecting_state || ssl_connect_2_reading == connssl->connecting_state || ssl_connect_2_writing == connssl->connecting_state); + DEBUGASSERT(backend); /* Here goes nothing: */ err = SSLHandshake(backend->ssl_ctx); @@ -2918,6 +2932,8 @@ collect_server_cert(struct Curl_easy *data, CFIndex i, count; SecTrustRef trust = NULL; + DEBUGASSERT(backend); + if(!show_verbose_server_cert && !data->set.ssl.certinfo) return CURLE_OK; @@ -3162,6 +3178,8 @@ static void sectransp_close(struct Curl_easy *data, struct connectdata *conn, (void) data; + DEBUGASSERT(backend); + if(backend->ssl_ctx) { (void)SSLClose(backend->ssl_ctx); #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS @@ -3190,6 +3208,8 @@ static int sectransp_shutdown(struct Curl_easy *data, char buf[120]; int loop = 10; /* avoid getting stuck */ + DEBUGASSERT(backend); + if(!backend->ssl_ctx) return 0; @@ -3269,6 +3289,8 @@ static int sectransp_check_cxn(struct connectdata *conn) OSStatus err; SSLSessionState state; + DEBUGASSERT(backend); + if(backend->ssl_ctx) { err = SSLGetSessionState(backend->ssl_ctx, &state); if(err == noErr) @@ -3286,6 +3308,8 @@ static bool sectransp_data_pending(const struct connectdata *conn, OSStatus err; size_t buffer; + DEBUGASSERT(backend); + if(backend->ssl_ctx) { /* SSL is in use */ err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer); if(err == noErr) @@ -3347,6 +3371,8 @@ static ssize_t sectransp_send(struct Curl_easy *data, size_t processed = 0UL; OSStatus err; + DEBUGASSERT(backend); + /* 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 @@ -3414,6 +3440,8 @@ static ssize_t sectransp_recv(struct Curl_easy *data, size_t processed = 0UL; OSStatus err; + DEBUGASSERT(backend); + again: err = SSLRead(backend->ssl_ctx, buf, buffersize, &processed); @@ -3463,6 +3491,7 @@ static void *sectransp_get_internals(struct ssl_connect_data *connssl, { struct ssl_backend_data *backend = connssl->backend; (void)info; + DEBUGASSERT(backend); return backend->ssl_ctx; } diff --git a/contrib/libs/curl/lib/vtls/vtls.c b/contrib/libs/curl/lib/vtls/vtls.c index 6007bbba0f..03b85ba065 100644 --- a/contrib/libs/curl/lib/vtls/vtls.c +++ b/contrib/libs/curl/lib/vtls/vtls.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -300,6 +300,8 @@ ssl_connect_init_proxy(struct connectdata *conn, int sockindex) pbdata = conn->proxy_ssl[sockindex].backend; conn->proxy_ssl[sockindex] = conn->ssl[sockindex]; + DEBUGASSERT(pbdata != NULL); + memset(&conn->ssl[sockindex], 0, sizeof(conn->ssl[sockindex])); memset(pbdata, 0, Curl_ssl->sizeof_ssl_backend_data); @@ -628,7 +630,8 @@ void Curl_ssl_associate_conn(struct Curl_easy *data, { if(Curl_ssl->associate_connection) { Curl_ssl->associate_connection(data, conn, FIRSTSOCKET); - if(conn->sock[SECONDARYSOCKET] && conn->bits.sock_accepted) + if((conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) && + conn->bits.sock_accepted) Curl_ssl->associate_connection(data, conn, SECONDARYSOCKET); } } @@ -638,7 +641,8 @@ void Curl_ssl_detach_conn(struct Curl_easy *data, { if(Curl_ssl->disassociate_connection) { Curl_ssl->disassociate_connection(data, FIRSTSOCKET); - if(conn->sock[SECONDARYSOCKET] && conn->bits.sock_accepted) + if((conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) && + conn->bits.sock_accepted) Curl_ssl->disassociate_connection(data, SECONDARYSOCKET); } } @@ -872,6 +876,32 @@ CURLcode Curl_ssl_random(struct Curl_easy *data, } /* + * Curl_ssl_snihost() converts the input host name to a suitable SNI name put + * in data->state.buffer. Returns a pointer to the name (or NULL if a problem) + * and stores the new length in 'olen'. + * + * SNI fields must not have any trailing dot and while RFC 6066 section 3 says + * the SNI field is case insensitive, browsers always send the data lowercase + * and subsequently there are numerous servers out there that don't work + * unless the name is lowercased. + */ + +char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen) +{ + size_t len = strlen(host); + if(len && (host[len-1] == '.')) + len--; + if((long)len >= data->set.buffer_size) + return NULL; + + Curl_strntolower(data->state.buffer, host, len); + data->state.buffer[len] = 0; + if(olen) + *olen = len; + return data->state.buffer; +} + +/* * Public key pem to der conversion */ @@ -969,7 +999,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, if(encode != CURLE_OK) return encode; - encode = Curl_base64_encode(data, (char *)sha256sumdigest, + encode = Curl_base64_encode((char *)sha256sumdigest, CURL_SHA256_DIGEST_LENGTH, &encoded, &encodedlen); Curl_safefree(sha256sumdigest); @@ -1296,8 +1326,6 @@ const struct Curl_ssl *Curl_ssl = &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 @@ -1329,9 +1357,6 @@ static const struct Curl_ssl *available_backends[] = { #if defined(USE_SCHANNEL) &Curl_ssl_schannel, #endif -#if defined(USE_MESALINK) - &Curl_ssl_mesalink, -#endif #if defined(USE_BEARSSL) &Curl_ssl_bearssl, #endif diff --git a/contrib/libs/curl/lib/vtls/vtls.h b/contrib/libs/curl/lib/vtls/vtls.h index c7bbba082d..af3b8d3c94 100644 --- a/contrib/libs/curl/lib/vtls/vtls.h +++ b/contrib/libs/curl/lib/vtls/vtls.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -85,7 +85,7 @@ struct Curl_ssl { CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen, unsigned char *sha256sum, size_t sha256sumlen); - void (*associate_connection)(struct Curl_easy *data, + bool (*associate_connection)(struct Curl_easy *data, struct connectdata *conn, int sockindex); void (*disassociate_connection)(struct Curl_easy *data, int sockindex); @@ -120,7 +120,6 @@ bool Curl_ssl_tls13_ciphersuites(void); #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 */ #include "rustls.h" /* rustls versions */ @@ -173,6 +172,7 @@ bool Curl_ssl_tls13_ciphersuites(void); data->set.str[STRING_SSL_PINNEDPUBLICKEY] #endif +char *Curl_ssl_snihost(struct Curl_easy *data, const char *host, size_t *olen); 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, diff --git a/contrib/libs/curl/lib/vtls/wolfssl.c b/contrib/libs/curl/lib/vtls/wolfssl.c index 242f193bc0..f1f786ffb6 100644 --- a/contrib/libs/curl/lib/vtls/wolfssl.c +++ b/contrib/libs/curl/lib/vtls/wolfssl.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -263,6 +263,8 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, #define use_sni(x) Curl_nop_stmt #endif + DEBUGASSERT(backend); + if(connssl->state == ssl_connection_complete) return CURLE_OK; @@ -462,12 +464,17 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn, if((hostname_len < USHRT_MAX) && (0 == Curl_inet_pton(AF_INET, hostname, &addr4)) && #ifdef ENABLE_IPV6 - (0 == Curl_inet_pton(AF_INET6, hostname, &addr6)) && + (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"); + ) { + size_t snilen; + char *snihost = Curl_ssl_snihost(data, hostname, &snilen); + if(!snihost || + wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME, snihost, + (unsigned short)snilen) != 1) { + failf(data, "Failed to set SNI"); + return CURLE_SSL_CONNECT_ERROR; + } } } #endif @@ -590,10 +597,11 @@ wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn, int ret = -1; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; struct ssl_backend_data *backend = connssl->backend; - const char * const hostname = SSL_HOST_NAME(); const char * const dispname = SSL_HOST_DISPNAME(); const char * const pinnedpubkey = SSL_PINNED_PUB_KEY(); + DEBUGASSERT(backend); + ERR_clear_error(); conn->recv[sockindex] = wolfssl_recv; @@ -601,9 +609,10 @@ wolfssl_connect_step2(struct Curl_easy *data, struct connectdata *conn, /* 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; + char *snihost = Curl_ssl_snihost(data, SSL_HOST_NAME(), NULL); + if(!snihost || + (wolfSSL_check_domain_name(backend->handle, snihost) == SSL_FAILURE)) + return CURLE_SSL_CONNECT_ERROR; } ret = SSL_connect(backend->handle); @@ -797,6 +806,7 @@ wolfssl_connect_step3(struct Curl_easy *data, struct connectdata *conn, struct ssl_backend_data *backend = connssl->backend; DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); + DEBUGASSERT(backend); if(SSL_SET_OPTION(primary.sessionid)) { bool incache; @@ -848,6 +858,8 @@ static ssize_t wolfssl_send(struct Curl_easy *data, int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; int rc; + DEBUGASSERT(backend); + ERR_clear_error(); rc = SSL_write(backend->handle, mem, memlen); @@ -880,6 +892,8 @@ static void wolfssl_close(struct Curl_easy *data, struct connectdata *conn, (void) data; + DEBUGASSERT(backend); + if(backend->handle) { char buf[32]; /* Maybe the server has already sent a close notify alert. @@ -908,17 +922,22 @@ static ssize_t wolfssl_recv(struct Curl_easy *data, int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; int nread; + DEBUGASSERT(backend); + ERR_clear_error(); nread = SSL_read(backend->handle, buf, buffsize); - if(nread < 0) { + 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_NONE: + /* FALLTHROUGH */ case SSL_ERROR_WANT_READ: + /* FALLTHROUGH */ case SSL_ERROR_WANT_WRITE: /* there's data pending, re-invoke SSL_read() */ *curlcode = CURLE_AGAIN; @@ -974,6 +993,7 @@ static bool wolfssl_data_pending(const struct connectdata *conn, { const struct ssl_connect_data *connssl = &conn->ssl[connindex]; struct ssl_backend_data *backend = connssl->backend; + DEBUGASSERT(backend); if(backend->handle) /* SSL is in use */ return (0 != SSL_pending(backend->handle)) ? TRUE : FALSE; else @@ -994,6 +1014,8 @@ static int wolfssl_shutdown(struct Curl_easy *data, struct connectdata *conn, (void) data; + DEBUGASSERT(backend); + if(backend->handle) { ERR_clear_error(); SSL_free(backend->handle); @@ -1173,6 +1195,7 @@ static void *wolfssl_get_internals(struct ssl_connect_data *connssl, { struct ssl_backend_data *backend = connssl->backend; (void)info; + DEBUGASSERT(backend); return backend->handle; } diff --git a/contrib/libs/curl/lib/vtls/x509asn1.c b/contrib/libs/curl/lib/vtls/x509asn1.c new file mode 100644 index 0000000000..f64acb83c9 --- /dev/null +++ b/contrib/libs/curl/lib/vtls/x509asn1.c @@ -0,0 +1,1409 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ + defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) + +#if defined(USE_GSKIT) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) +#define WANT_PARSEX509 /* uses Curl_parseX509() */ +#endif + +#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ + defined(USE_SCHANNEL) || defined(USE_SECTRANSP) +#define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */ +#define WANT_PARSEX509 /* ... uses Curl_parseX509() */ +#endif + +#if defined(USE_GSKIT) +#define WANT_VERIFYHOST /* uses Curl_verifyhost () */ +#define WANT_PARSEX509 /* ... uses Curl_parseX509() */ +#endif + +#include <curl/curl.h> +#include "urldata.h" +#include "strcase.h" +#include "hostcheck.h" +#include "vtls/vtls.h" +#include "sendf.h" +#include "inet_pton.h" +#include "curl_base64.h" +#include "x509asn1.h" +#include "dynbuf.h" + +/* The last 3 #include files should be in this order */ +#include "curl_printf.h" +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Constants. + */ + +/* Largest supported ASN.1 structure. */ +#define CURL_ASN1_MAX ((size_t) 0x40000) /* 256K */ + +/* ASN.1 classes. */ +#define CURL_ASN1_UNIVERSAL 0 +#define CURL_ASN1_APPLICATION 1 +#define CURL_ASN1_CONTEXT_SPECIFIC 2 +#define CURL_ASN1_PRIVATE 3 + +/* ASN.1 types. */ +#define CURL_ASN1_BOOLEAN 1 +#define CURL_ASN1_INTEGER 2 +#define CURL_ASN1_BIT_STRING 3 +#define CURL_ASN1_OCTET_STRING 4 +#define CURL_ASN1_NULL 5 +#define CURL_ASN1_OBJECT_IDENTIFIER 6 +#define CURL_ASN1_OBJECT_DESCRIPTOR 7 +#define CURL_ASN1_INSTANCE_OF 8 +#define CURL_ASN1_REAL 9 +#define CURL_ASN1_ENUMERATED 10 +#define CURL_ASN1_EMBEDDED 11 +#define CURL_ASN1_UTF8_STRING 12 +#define CURL_ASN1_RELATIVE_OID 13 +#define CURL_ASN1_SEQUENCE 16 +#define CURL_ASN1_SET 17 +#define CURL_ASN1_NUMERIC_STRING 18 +#define CURL_ASN1_PRINTABLE_STRING 19 +#define CURL_ASN1_TELETEX_STRING 20 +#define CURL_ASN1_VIDEOTEX_STRING 21 +#define CURL_ASN1_IA5_STRING 22 +#define CURL_ASN1_UTC_TIME 23 +#define CURL_ASN1_GENERALIZED_TIME 24 +#define CURL_ASN1_GRAPHIC_STRING 25 +#define CURL_ASN1_VISIBLE_STRING 26 +#define CURL_ASN1_GENERAL_STRING 27 +#define CURL_ASN1_UNIVERSAL_STRING 28 +#define CURL_ASN1_CHARACTER_STRING 29 +#define CURL_ASN1_BMP_STRING 30 + +#ifdef WANT_EXTRACT_CERTINFO +/* ASN.1 OID table entry. */ +struct Curl_OID { + const char *numoid; /* Dotted-numeric OID. */ + const char *textoid; /* OID name. */ +}; + +/* ASN.1 OIDs. */ +static const char cnOID[] = "2.5.4.3"; /* Common name. */ +static const char sanOID[] = "2.5.29.17"; /* Subject alternative name. */ + +static const struct Curl_OID OIDtable[] = { + { "1.2.840.10040.4.1", "dsa" }, + { "1.2.840.10040.4.3", "dsa-with-sha1" }, + { "1.2.840.10045.2.1", "ecPublicKey" }, + { "1.2.840.10045.3.0.1", "c2pnb163v1" }, + { "1.2.840.10045.4.1", "ecdsa-with-SHA1" }, + { "1.2.840.10046.2.1", "dhpublicnumber" }, + { "1.2.840.113549.1.1.1", "rsaEncryption" }, + { "1.2.840.113549.1.1.2", "md2WithRSAEncryption" }, + { "1.2.840.113549.1.1.4", "md5WithRSAEncryption" }, + { "1.2.840.113549.1.1.5", "sha1WithRSAEncryption" }, + { "1.2.840.113549.1.1.10", "RSASSA-PSS" }, + { "1.2.840.113549.1.1.14", "sha224WithRSAEncryption" }, + { "1.2.840.113549.1.1.11", "sha256WithRSAEncryption" }, + { "1.2.840.113549.1.1.12", "sha384WithRSAEncryption" }, + { "1.2.840.113549.1.1.13", "sha512WithRSAEncryption" }, + { "1.2.840.113549.2.2", "md2" }, + { "1.2.840.113549.2.5", "md5" }, + { "1.3.14.3.2.26", "sha1" }, + { cnOID, "CN" }, + { "2.5.4.4", "SN" }, + { "2.5.4.5", "serialNumber" }, + { "2.5.4.6", "C" }, + { "2.5.4.7", "L" }, + { "2.5.4.8", "ST" }, + { "2.5.4.9", "streetAddress" }, + { "2.5.4.10", "O" }, + { "2.5.4.11", "OU" }, + { "2.5.4.12", "title" }, + { "2.5.4.13", "description" }, + { "2.5.4.17", "postalCode" }, + { "2.5.4.41", "name" }, + { "2.5.4.42", "givenName" }, + { "2.5.4.43", "initials" }, + { "2.5.4.44", "generationQualifier" }, + { "2.5.4.45", "X500UniqueIdentifier" }, + { "2.5.4.46", "dnQualifier" }, + { "2.5.4.65", "pseudonym" }, + { "1.2.840.113549.1.9.1", "emailAddress" }, + { "2.5.4.72", "role" }, + { sanOID, "subjectAltName" }, + { "2.5.29.18", "issuerAltName" }, + { "2.5.29.19", "basicConstraints" }, + { "2.16.840.1.101.3.4.2.4", "sha224" }, + { "2.16.840.1.101.3.4.2.1", "sha256" }, + { "2.16.840.1.101.3.4.2.2", "sha384" }, + { "2.16.840.1.101.3.4.2.3", "sha512" }, + { (const char *) NULL, (const char *) NULL } +}; + +#endif /* WANT_EXTRACT_CERTINFO */ + +/* + * Lightweight ASN.1 parser. + * In particular, it does not check for syntactic/lexical errors. + * It is intended to support certificate information gathering for SSL backends + * that offer a mean to get certificates as a whole, but do not supply + * entry points to get particular certificate sub-fields. + * Please note there is no pretention here to rewrite a full SSL library. + */ + +static const char *getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end) + WARN_UNUSED_RESULT; + +static const char *getASN1Element(struct Curl_asn1Element *elem, + const char *beg, const char *end) +{ + unsigned char b; + unsigned long len; + struct Curl_asn1Element lelem; + + /* Get a single ASN.1 element into `elem', parse ASN.1 string at `beg' + ending at `end'. + Returns a pointer in source string after the parsed element, or NULL + if an error occurs. */ + if(!beg || !end || beg >= end || !*beg || + (size_t)(end - beg) > CURL_ASN1_MAX) + return NULL; + + /* Process header byte. */ + elem->header = beg; + b = (unsigned char) *beg++; + elem->constructed = (b & 0x20) != 0; + elem->class = (b >> 6) & 3; + b &= 0x1F; + if(b == 0x1F) + return NULL; /* Long tag values not supported here. */ + elem->tag = b; + + /* Process length. */ + if(beg >= end) + return NULL; + b = (unsigned char) *beg++; + if(!(b & 0x80)) + len = b; + else if(!(b &= 0x7F)) { + /* Unspecified length. Since we have all the data, we can determine the + effective length by skipping element until an end element is found. */ + if(!elem->constructed) + return NULL; + elem->beg = beg; + while(beg < end && *beg) { + beg = getASN1Element(&lelem, beg, end); + if(!beg) + return NULL; + } + if(beg >= end) + return NULL; + elem->end = beg; + return beg + 1; + } + else if((unsigned)b > (size_t)(end - beg)) + return NULL; /* Does not fit in source. */ + else { + /* Get long length. */ + len = 0; + do { + if(len & 0xFF000000L) + return NULL; /* Lengths > 32 bits are not supported. */ + len = (len << 8) | (unsigned char) *beg++; + } while(--b); + } + if(len > (size_t)(end - beg)) + return NULL; /* Element data does not fit in source. */ + elem->beg = beg; + elem->end = beg + len; + return elem->end; +} + +#ifdef WANT_EXTRACT_CERTINFO + +/* + * Search the null terminated OID or OID identifier in local table. + * Return the table entry pointer or NULL if not found. + */ +static const struct Curl_OID *searchOID(const char *oid) +{ + const struct Curl_OID *op; + for(op = OIDtable; op->numoid; op++) + if(!strcmp(op->numoid, oid) || strcasecompare(op->textoid, oid)) + return op; + + return NULL; +} + +/* + * Convert an ASN.1 Boolean value into its string representation. Return the + * dynamically allocated string, or NULL if source is not an ASN.1 Boolean + * value. + */ + +static const char *bool2str(const char *beg, const char *end) +{ + if(end - beg != 1) + return NULL; + return strdup(*beg? "TRUE": "FALSE"); +} + +/* + * Convert an ASN.1 octet string to a printable string. + * Return the dynamically allocated string, or NULL if an error occurs. + */ +static const char *octet2str(const char *beg, const char *end) +{ + struct dynbuf buf; + CURLcode result; + + Curl_dyn_init(&buf, 3 * CURL_ASN1_MAX + 1); + result = Curl_dyn_addn(&buf, "", 0); + + while(!result && beg < end) + result = Curl_dyn_addf(&buf, "%02x:", (unsigned char) *beg++); + + return Curl_dyn_ptr(&buf); +} + +static const char *bit2str(const char *beg, const char *end) +{ + /* Convert an ASN.1 bit string to a printable string. + Return the dynamically allocated string, or NULL if an error occurs. */ + + if(++beg > end) + return NULL; + return octet2str(beg, end); +} + +/* + * Convert an ASN.1 integer value into its string representation. + * Return the dynamically allocated string, or NULL if source is not an + * ASN.1 integer value. + */ +static const char *int2str(const char *beg, const char *end) +{ + unsigned long val = 0; + size_t n = end - beg; + + if(!n) + return NULL; + + if(n > 4) + return octet2str(beg, end); + + /* Represent integers <= 32-bit as a single value. */ + if(*beg & 0x80) + val = ~val; + + do + val = (val << 8) | *(const unsigned char *) beg++; + while(beg < end); + return curl_maprintf("%s%lx", val >= 10? "0x": "", val); +} + +/* + * Perform a lazy conversion from an ASN.1 typed string to UTF8. Allocate the + * destination buffer dynamically. The allocation size will normally be too + * large: this is to avoid buffer overflows. + * Terminate the string with a nul byte and return the converted + * string length. + */ +static ssize_t +utf8asn1str(char **to, int type, const char *from, const char *end) +{ + size_t inlength = end - from; + int size = 1; + size_t outlength; + char *buf; + + *to = NULL; + switch(type) { + case CURL_ASN1_BMP_STRING: + size = 2; + break; + case CURL_ASN1_UNIVERSAL_STRING: + size = 4; + break; + case CURL_ASN1_NUMERIC_STRING: + case CURL_ASN1_PRINTABLE_STRING: + case CURL_ASN1_TELETEX_STRING: + case CURL_ASN1_IA5_STRING: + case CURL_ASN1_VISIBLE_STRING: + case CURL_ASN1_UTF8_STRING: + break; + default: + return -1; /* Conversion not supported. */ + } + + if(inlength % size) + return -1; /* Length inconsistent with character size. */ + if(inlength / size > (SIZE_T_MAX - 1) / 4) + return -1; /* Too big. */ + buf = malloc(4 * (inlength / size) + 1); + if(!buf) + return -1; /* Not enough memory. */ + + if(type == CURL_ASN1_UTF8_STRING) { + /* Just copy. */ + outlength = inlength; + if(outlength) + memcpy(buf, from, outlength); + } + else { + for(outlength = 0; from < end;) { + int charsize; + unsigned int wc; + + wc = 0; + switch(size) { + case 4: + wc = (wc << 8) | *(const unsigned char *) from++; + wc = (wc << 8) | *(const unsigned char *) from++; + /* FALLTHROUGH */ + case 2: + wc = (wc << 8) | *(const unsigned char *) from++; + /* FALLTHROUGH */ + default: /* case 1: */ + wc = (wc << 8) | *(const unsigned char *) from++; + } + charsize = 1; + if(wc >= 0x00000080) { + if(wc >= 0x00000800) { + if(wc >= 0x00010000) { + if(wc >= 0x00200000) { + free(buf); + return -1; /* Invalid char. size for target encoding. */ + } + buf[outlength + 3] = (char) (0x80 | (wc & 0x3F)); + wc = (wc >> 6) | 0x00010000; + charsize++; + } + buf[outlength + 2] = (char) (0x80 | (wc & 0x3F)); + wc = (wc >> 6) | 0x00000800; + charsize++; + } + buf[outlength + 1] = (char) (0x80 | (wc & 0x3F)); + wc = (wc >> 6) | 0x000000C0; + charsize++; + } + buf[outlength] = (char) wc; + outlength += charsize; + } + } + buf[outlength] = '\0'; + *to = buf; + return outlength; +} + +/* + * Convert an ASN.1 String into its UTF-8 string representation. + * Return the dynamically allocated string, or NULL if an error occurs. + */ +static const char *string2str(int type, const char *beg, const char *end) +{ + char *buf; + if(utf8asn1str(&buf, type, beg, end) < 0) + return NULL; + return buf; +} + +/* + * Decimal ASCII encode unsigned integer `x' into the buflen sized buffer at + * buf. Return the total number of encoded digits, even if larger than + * `buflen'. + */ +static size_t encodeUint(char *buf, size_t buflen, unsigned int x) +{ + size_t i = 0; + unsigned int y = x / 10; + + if(y) { + i = encodeUint(buf, buflen, y); + x -= y * 10; + } + if(i < buflen) + buf[i] = (char) ('0' + x); + i++; + if(i < buflen) + buf[i] = '\0'; /* Store a terminator if possible. */ + return i; +} + +/* + * Convert an ASN.1 OID into its dotted string representation. + * Store the result in th `n'-byte buffer at `buf'. + * Return the converted string length, or 0 on errors. + */ +static size_t encodeOID(char *buf, size_t buflen, + const char *beg, const char *end) +{ + size_t i; + unsigned int x; + unsigned int y; + + /* Process the first two numbers. */ + y = *(const unsigned char *) beg++; + x = y / 40; + y -= x * 40; + i = encodeUint(buf, buflen, x); + if(i < buflen) + buf[i] = '.'; + i++; + if(i >= buflen) + i += encodeUint(NULL, 0, y); + else + i += encodeUint(buf + i, buflen - i, y); + + /* Process the trailing numbers. */ + while(beg < end) { + if(i < buflen) + buf[i] = '.'; + i++; + x = 0; + do { + if(x & 0xFF000000) + return 0; + y = *(const unsigned char *) beg++; + x = (x << 7) | (y & 0x7F); + } while(y & 0x80); + if(i >= buflen) + i += encodeUint(NULL, 0, x); + else + i += encodeUint(buf + i, buflen - i, x); + } + if(i < buflen) + buf[i] = '\0'; + return i; +} + +/* + * Convert an ASN.1 OID into its dotted or symbolic string representation. + * Return the dynamically allocated string, or NULL if an error occurs. + */ + +static const char *OID2str(const char *beg, const char *end, bool symbolic) +{ + char *buf = NULL; + if(beg < end) { + size_t buflen = encodeOID(NULL, 0, beg, end); + if(buflen) { + buf = malloc(buflen + 1); /* one extra for the zero byte */ + if(buf) { + encodeOID(buf, buflen, beg, end); + buf[buflen] = '\0'; + + if(symbolic) { + const struct Curl_OID *op = searchOID(buf); + if(op) { + free(buf); + buf = strdup(op->textoid); + } + } + } + } + } + return buf; +} + +static const char *GTime2str(const char *beg, const char *end) +{ + const char *tzp; + const char *fracp; + char sec1, sec2; + size_t fracl; + size_t tzl; + const char *sep = ""; + + /* Convert an ASN.1 Generalized time to a printable string. + Return the dynamically allocated string, or NULL if an error occurs. */ + + for(fracp = beg; fracp < end && *fracp >= '0' && *fracp <= '9'; fracp++) + ; + + /* Get seconds digits. */ + sec1 = '0'; + switch(fracp - beg - 12) { + case 0: + sec2 = '0'; + break; + case 2: + sec1 = fracp[-2]; + /* FALLTHROUGH */ + case 1: + sec2 = fracp[-1]; + break; + default: + return NULL; + } + + /* Scan for timezone, measure fractional seconds. */ + tzp = fracp; + fracl = 0; + if(fracp < end && (*fracp == '.' || *fracp == ',')) { + fracp++; + do + tzp++; + while(tzp < end && *tzp >= '0' && *tzp <= '9'); + /* Strip leading zeroes in fractional seconds. */ + for(fracl = tzp - fracp - 1; fracl && fracp[fracl - 1] == '0'; fracl--) + ; + } + + /* Process timezone. */ + if(tzp >= end) + ; /* Nothing to do. */ + else if(*tzp == 'Z') { + tzp = " GMT"; + end = tzp + 4; + } + else { + sep = " "; + tzp++; + } + + tzl = end - tzp; + return curl_maprintf("%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s", + beg, beg + 4, beg + 6, + beg + 8, beg + 10, sec1, sec2, + fracl? ".": "", (int)fracl, fracp, + sep, (int)tzl, tzp); +} + +/* + * Convert an ASN.1 UTC time to a printable string. + * Return the dynamically allocated string, or NULL if an error occurs. + */ +static const char *UTime2str(const char *beg, const char *end) +{ + const char *tzp; + size_t tzl; + const char *sec; + + for(tzp = beg; tzp < end && *tzp >= '0' && *tzp <= '9'; tzp++) + ; + /* Get the seconds. */ + sec = beg + 10; + switch(tzp - sec) { + case 0: + sec = "00"; + case 2: + break; + default: + return NULL; + } + + /* Process timezone. */ + if(tzp >= end) + return NULL; + if(*tzp == 'Z') { + tzp = "GMT"; + end = tzp + 3; + } + else + tzp++; + + tzl = end - tzp; + return curl_maprintf("%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s", + 20 - (*beg >= '5'), beg, beg + 2, beg + 4, + beg + 6, beg + 8, sec, + (int)tzl, tzp); +} + +/* + * Convert an ASN.1 element to a printable string. + * Return the dynamically allocated string, or NULL if an error occurs. + */ +static const char *ASN1tostr(struct Curl_asn1Element *elem, int type) +{ + if(elem->constructed) + return NULL; /* No conversion of structured elements. */ + + if(!type) + type = elem->tag; /* Type not forced: use element tag as type. */ + + switch(type) { + case CURL_ASN1_BOOLEAN: + return bool2str(elem->beg, elem->end); + case CURL_ASN1_INTEGER: + case CURL_ASN1_ENUMERATED: + return int2str(elem->beg, elem->end); + case CURL_ASN1_BIT_STRING: + return bit2str(elem->beg, elem->end); + case CURL_ASN1_OCTET_STRING: + return octet2str(elem->beg, elem->end); + case CURL_ASN1_NULL: + return strdup(""); + case CURL_ASN1_OBJECT_IDENTIFIER: + return OID2str(elem->beg, elem->end, TRUE); + case CURL_ASN1_UTC_TIME: + return UTime2str(elem->beg, elem->end); + case CURL_ASN1_GENERALIZED_TIME: + return GTime2str(elem->beg, elem->end); + case CURL_ASN1_UTF8_STRING: + case CURL_ASN1_NUMERIC_STRING: + case CURL_ASN1_PRINTABLE_STRING: + case CURL_ASN1_TELETEX_STRING: + case CURL_ASN1_IA5_STRING: + case CURL_ASN1_VISIBLE_STRING: + case CURL_ASN1_UNIVERSAL_STRING: + case CURL_ASN1_BMP_STRING: + return string2str(type, elem->beg, elem->end); + } + + return NULL; /* Unsupported. */ +} + +/* + * ASCII encode distinguished name at `dn' into the `buflen'-sized buffer at + * `buf'. + * + * Returns the total string length, even if larger than `buflen' or -1 on + * error. + */ +static ssize_t encodeDN(char *buf, size_t buflen, struct Curl_asn1Element *dn) +{ + struct Curl_asn1Element rdn; + struct Curl_asn1Element atv; + struct Curl_asn1Element oid; + struct Curl_asn1Element value; + size_t l = 0; + const char *p1; + const char *p2; + const char *p3; + const char *str; + + for(p1 = dn->beg; p1 < dn->end;) { + p1 = getASN1Element(&rdn, p1, dn->end); + if(!p1) + return -1; + for(p2 = rdn.beg; p2 < rdn.end;) { + p2 = getASN1Element(&atv, p2, rdn.end); + if(!p2) + return -1; + p3 = getASN1Element(&oid, atv.beg, atv.end); + if(!p3) + return -1; + if(!getASN1Element(&value, p3, atv.end)) + return -1; + str = ASN1tostr(&oid, 0); + if(!str) + return -1; + + /* Encode delimiter. + If attribute has a short uppercase name, delimiter is ", ". */ + if(l) { + for(p3 = str; isupper(*p3); p3++) + ; + for(p3 = (*p3 || p3 - str > 2)? "/": ", "; *p3; p3++) { + if(l < buflen) + buf[l] = *p3; + l++; + } + } + + /* Encode attribute name. */ + for(p3 = str; *p3; p3++) { + if(l < buflen) + buf[l] = *p3; + l++; + } + free((char *) str); + + /* Generate equal sign. */ + if(l < buflen) + buf[l] = '='; + l++; + + /* Generate value. */ + str = ASN1tostr(&value, 0); + if(!str) + return -1; + for(p3 = str; *p3; p3++) { + if(l < buflen) + buf[l] = *p3; + l++; + } + free((char *) str); + } + } + + return l; +} + +#endif /* WANT_EXTRACT_CERTINFO */ + +#ifdef WANT_PARSEX509 +/* + * ASN.1 parse an X509 certificate into structure subfields. + * Syntax is assumed to have already been checked by the SSL backend. + * See RFC 5280. + */ +int Curl_parseX509(struct Curl_X509certificate *cert, + const char *beg, const char *end) +{ + struct Curl_asn1Element elem; + struct Curl_asn1Element tbsCertificate; + const char *ccp; + static const char defaultVersion = 0; /* v1. */ + + cert->certificate.header = NULL; + cert->certificate.beg = beg; + cert->certificate.end = end; + + /* Get the sequence content. */ + if(!getASN1Element(&elem, beg, end)) + return -1; /* Invalid bounds/size. */ + beg = elem.beg; + end = elem.end; + + /* Get tbsCertificate. */ + beg = getASN1Element(&tbsCertificate, beg, end); + if(!beg) + return -1; + /* Skip the signatureAlgorithm. */ + beg = getASN1Element(&cert->signatureAlgorithm, beg, end); + if(!beg) + return -1; + /* Get the signatureValue. */ + if(!getASN1Element(&cert->signature, beg, end)) + return -1; + + /* Parse TBSCertificate. */ + beg = tbsCertificate.beg; + end = tbsCertificate.end; + /* Get optional version, get serialNumber. */ + cert->version.header = NULL; + cert->version.beg = &defaultVersion; + cert->version.end = &defaultVersion + sizeof(defaultVersion); + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + if(elem.tag == 0) { + if(!getASN1Element(&cert->version, elem.beg, elem.end)) + return -1; + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + } + cert->serialNumber = elem; + /* Get signature algorithm. */ + beg = getASN1Element(&cert->signatureAlgorithm, beg, end); + /* Get issuer. */ + beg = getASN1Element(&cert->issuer, beg, end); + if(!beg) + return -1; + /* Get notBefore and notAfter. */ + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + ccp = getASN1Element(&cert->notBefore, elem.beg, elem.end); + if(!ccp) + return -1; + if(!getASN1Element(&cert->notAfter, ccp, elem.end)) + return -1; + /* Get subject. */ + beg = getASN1Element(&cert->subject, beg, end); + if(!beg) + return -1; + /* Get subjectPublicKeyAlgorithm and subjectPublicKey. */ + beg = getASN1Element(&cert->subjectPublicKeyInfo, beg, end); + if(!beg) + return -1; + ccp = getASN1Element(&cert->subjectPublicKeyAlgorithm, + cert->subjectPublicKeyInfo.beg, + cert->subjectPublicKeyInfo.end); + if(!ccp) + return -1; + if(!getASN1Element(&cert->subjectPublicKey, ccp, + cert->subjectPublicKeyInfo.end)) + return -1; + /* Get optional issuerUiqueID, subjectUniqueID and extensions. */ + cert->issuerUniqueID.tag = cert->subjectUniqueID.tag = 0; + cert->extensions.tag = elem.tag = 0; + cert->issuerUniqueID.header = cert->subjectUniqueID.header = NULL; + cert->issuerUniqueID.beg = cert->issuerUniqueID.end = ""; + cert->subjectUniqueID.beg = cert->subjectUniqueID.end = ""; + cert->extensions.header = NULL; + cert->extensions.beg = cert->extensions.end = ""; + if(beg < end) { + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + } + if(elem.tag == 1) { + cert->issuerUniqueID = elem; + if(beg < end) { + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + } + } + if(elem.tag == 2) { + cert->subjectUniqueID = elem; + if(beg < end) { + beg = getASN1Element(&elem, beg, end); + if(!beg) + return -1; + } + } + if(elem.tag == 3) + if(!getASN1Element(&cert->extensions, elem.beg, elem.end)) + return -1; + return 0; +} + +#endif /* WANT_PARSEX509 */ + +#ifdef WANT_EXTRACT_CERTINFO + +/* + * Copy at most 64-characters, terminate with a newline and returns the + * effective number of stored characters. + */ +static size_t copySubstring(char *to, const char *from) +{ + size_t i; + for(i = 0; i < 64; i++) { + to[i] = *from; + if(!*from++) + break; + } + + to[i++] = '\n'; + return i; +} + +static const char *dumpAlgo(struct Curl_asn1Element *param, + const char *beg, const char *end) +{ + struct Curl_asn1Element oid; + + /* Get algorithm parameters and return algorithm name. */ + + beg = getASN1Element(&oid, beg, end); + if(!beg) + return NULL; + param->header = NULL; + param->tag = 0; + param->beg = param->end = end; + if(beg < end) + if(!getASN1Element(param, beg, end)) + return NULL; + return OID2str(oid.beg, oid.end, TRUE); +} + +/* return 0 on success, 1 on error */ +static int do_pubkey_field(struct Curl_easy *data, int certnum, + const char *label, struct Curl_asn1Element *elem) +{ + const char *output; + CURLcode result = CURLE_OK; + + /* Generate a certificate information record for the public key. */ + + output = ASN1tostr(elem, 0); + if(output) { + if(data->set.ssl.certinfo) + result = Curl_ssl_push_certinfo(data, certnum, label, output); + if(!certnum && !result) + infof(data, " %s: %s", label, output); + free((char *) output); + } + return result ? 1 : 0; +} + +/* return 0 on success, 1 on error */ +static int do_pubkey(struct Curl_easy *data, int certnum, + const char *algo, struct Curl_asn1Element *param, + struct Curl_asn1Element *pubkey) +{ + struct Curl_asn1Element elem; + struct Curl_asn1Element pk; + const char *p; + + /* Generate all information records for the public key. */ + + /* Get the public key (single element). */ + if(!getASN1Element(&pk, pubkey->beg + 1, pubkey->end)) + return 1; + + if(strcasecompare(algo, "rsaEncryption")) { + const char *q; + unsigned long len; + + p = getASN1Element(&elem, pk.beg, pk.end); + if(!p) + return 1; + + /* Compute key length. */ + for(q = elem.beg; !*q && q < elem.end; q++) + ; + len = (unsigned long)((elem.end - q) * 8); + if(len) { + unsigned int i; + for(i = *(unsigned char *) q; !(i & 0x80); i <<= 1) + len--; + } + if(len > 32) + elem.beg = q; /* Strip leading zero bytes. */ + if(!certnum) + infof(data, " RSA Public Key (%lu bits)", len); + if(data->set.ssl.certinfo) { + q = curl_maprintf("%lu", len); + if(q) { + CURLcode result = + Curl_ssl_push_certinfo(data, certnum, "RSA Public Key", q); + free((char *) q); + if(result) + return 1; + } + } + /* Generate coefficients. */ + if(do_pubkey_field(data, certnum, "rsa(n)", &elem)) + return 1; + if(!getASN1Element(&elem, p, pk.end)) + return 1; + if(do_pubkey_field(data, certnum, "rsa(e)", &elem)) + return 1; + } + else if(strcasecompare(algo, "dsa")) { + p = getASN1Element(&elem, param->beg, param->end); + if(p) { + if(do_pubkey_field(data, certnum, "dsa(p)", &elem)) + return 1; + p = getASN1Element(&elem, p, param->end); + if(p) { + if(do_pubkey_field(data, certnum, "dsa(q)", &elem)) + return 1; + if(getASN1Element(&elem, p, param->end)) { + if(do_pubkey_field(data, certnum, "dsa(g)", &elem)) + return 1; + if(do_pubkey_field(data, certnum, "dsa(pub_key)", &pk)) + return 1; + } + } + } + } + else if(strcasecompare(algo, "dhpublicnumber")) { + p = getASN1Element(&elem, param->beg, param->end); + if(p) { + if(do_pubkey_field(data, certnum, "dh(p)", &elem)) + return 1; + if(getASN1Element(&elem, param->beg, param->end)) { + if(do_pubkey_field(data, certnum, "dh(g)", &elem)) + return 1; + if(do_pubkey_field(data, certnum, "dh(pub_key)", &pk)) + return 1; + } + } + } + return 0; +} + +/* + * Convert an ASN.1 distinguished name into a printable string. + * Return the dynamically allocated string, or NULL if an error occurs. + */ +static const char *DNtostr(struct Curl_asn1Element *dn) +{ + char *buf = NULL; + ssize_t buflen = encodeDN(NULL, 0, dn); + + if(buflen >= 0) { + buf = malloc(buflen + 1); + if(buf) { + if(encodeDN(buf, buflen + 1, dn) == -1) { + free(buf); + return NULL; + } + buf[buflen] = '\0'; + } + } + return buf; +} + +CURLcode Curl_extract_certinfo(struct Curl_easy *data, + int certnum, + const char *beg, + const char *end) +{ + struct Curl_X509certificate cert; + struct Curl_asn1Element param; + const char *ccp; + char *cp1; + size_t cl1; + char *cp2; + CURLcode result = CURLE_OK; + unsigned long version; + size_t i; + size_t j; + + if(!data->set.ssl.certinfo) + if(certnum) + return CURLE_OK; + + /* Prepare the certificate information for curl_easy_getinfo(). */ + + /* Extract the certificate ASN.1 elements. */ + if(Curl_parseX509(&cert, beg, end)) + return CURLE_PEER_FAILED_VERIFICATION; + + /* Subject. */ + ccp = DNtostr(&cert.subject); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + if(data->set.ssl.certinfo) { + result = Curl_ssl_push_certinfo(data, certnum, "Subject", ccp); + if(result) + return result; + } + if(!certnum) + infof(data, "%2d Subject: %s", certnum, ccp); + free((char *) ccp); + + /* Issuer. */ + ccp = DNtostr(&cert.issuer); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + if(data->set.ssl.certinfo) { + result = Curl_ssl_push_certinfo(data, certnum, "Issuer", ccp); + } + if(!certnum) + infof(data, " Issuer: %s", ccp); + free((char *) ccp); + if(result) + return result; + + /* Version (always fits in less than 32 bits). */ + version = 0; + for(ccp = cert.version.beg; ccp < cert.version.end; ccp++) + version = (version << 8) | *(const unsigned char *) ccp; + if(data->set.ssl.certinfo) { + ccp = curl_maprintf("%lx", version); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + result = Curl_ssl_push_certinfo(data, certnum, "Version", ccp); + free((char *) ccp); + if(result) + return result; + } + if(!certnum) + infof(data, " Version: %lu (0x%lx)", version + 1, version); + + /* Serial number. */ + ccp = ASN1tostr(&cert.serialNumber, 0); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + if(data->set.ssl.certinfo) + result = Curl_ssl_push_certinfo(data, certnum, "Serial Number", ccp); + if(!certnum) + infof(data, " Serial Number: %s", ccp); + free((char *) ccp); + if(result) + return result; + + /* Signature algorithm .*/ + ccp = dumpAlgo(¶m, cert.signatureAlgorithm.beg, + cert.signatureAlgorithm.end); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + if(data->set.ssl.certinfo) + result = Curl_ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp); + if(!certnum) + infof(data, " Signature Algorithm: %s", ccp); + free((char *) ccp); + if(result) + return result; + + /* Start Date. */ + ccp = ASN1tostr(&cert.notBefore, 0); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + if(data->set.ssl.certinfo) + result = Curl_ssl_push_certinfo(data, certnum, "Start Date", ccp); + if(!certnum) + infof(data, " Start Date: %s", ccp); + free((char *) ccp); + if(result) + return result; + + /* Expire Date. */ + ccp = ASN1tostr(&cert.notAfter, 0); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + if(data->set.ssl.certinfo) + result = Curl_ssl_push_certinfo(data, certnum, "Expire Date", ccp); + if(!certnum) + infof(data, " Expire Date: %s", ccp); + free((char *) ccp); + if(result) + return result; + + /* Public Key Algorithm. */ + ccp = dumpAlgo(¶m, cert.subjectPublicKeyAlgorithm.beg, + cert.subjectPublicKeyAlgorithm.end); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + if(data->set.ssl.certinfo) + result = Curl_ssl_push_certinfo(data, certnum, "Public Key Algorithm", + ccp); + if(!result) { + int ret; + if(!certnum) + infof(data, " Public Key Algorithm: %s", ccp); + ret = do_pubkey(data, certnum, ccp, ¶m, &cert.subjectPublicKey); + if(ret) + result = CURLE_OUT_OF_MEMORY; /* the most likely error */ + } + free((char *) ccp); + if(result) + return result; + + /* Signature. */ + ccp = ASN1tostr(&cert.signature, 0); + if(!ccp) + return CURLE_OUT_OF_MEMORY; + if(data->set.ssl.certinfo) + result = Curl_ssl_push_certinfo(data, certnum, "Signature", ccp); + if(!certnum) + infof(data, " Signature: %s", ccp); + free((char *) ccp); + if(result) + return result; + + /* Generate PEM certificate. */ + result = Curl_base64_encode(cert.certificate.beg, + cert.certificate.end - cert.certificate.beg, + &cp1, &cl1); + if(result) + return result; + /* Compute the number of characters in final certificate string. Format is: + -----BEGIN CERTIFICATE-----\n + <max 64 base64 characters>\n + . + . + . + -----END CERTIFICATE-----\n + */ + i = 28 + cl1 + (cl1 + 64 - 1) / 64 + 26; + cp2 = malloc(i + 1); + if(!cp2) { + free(cp1); + return CURLE_OUT_OF_MEMORY; + } + /* Build the certificate string. */ + i = copySubstring(cp2, "-----BEGIN CERTIFICATE-----"); + for(j = 0; j < cl1; j += 64) + i += copySubstring(cp2 + i, cp1 + j); + i += copySubstring(cp2 + i, "-----END CERTIFICATE-----"); + cp2[i] = '\0'; + free(cp1); + if(data->set.ssl.certinfo) + result = Curl_ssl_push_certinfo(data, certnum, "Cert", cp2); + if(!certnum) + infof(data, "%s", cp2); + free(cp2); + return result; +} + +#endif /* WANT_EXTRACT_CERTINFO */ + +#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL + * or USE_SECTRANSP */ + +#ifdef WANT_VERIFYHOST + +static const char *checkOID(const char *beg, const char *end, + const char *oid) +{ + struct Curl_asn1Element e; + const char *ccp; + const char *p; + bool matched; + + /* Check if first ASN.1 element at `beg' is the given OID. + Return a pointer in the source after the OID if found, else NULL. */ + + ccp = getASN1Element(&e, beg, end); + if(!ccp || e.tag != CURL_ASN1_OBJECT_IDENTIFIER) + return NULL; + + p = OID2str(e.beg, e.end, FALSE); + if(!p) + return NULL; + + matched = !strcmp(p, oid); + free((char *) p); + return matched? ccp: NULL; +} + +CURLcode Curl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + const char *beg, const char *end) +{ + struct Curl_X509certificate cert; + struct Curl_asn1Element dn; + struct Curl_asn1Element elem; + struct Curl_asn1Element ext; + struct Curl_asn1Element name; + const char *p; + const char *q; + char *dnsname; + int matched = -1; + size_t addrlen = (size_t) -1; + ssize_t len; + const char * const hostname = SSL_HOST_NAME(); + const char * const dispname = SSL_HOST_DISPNAME(); + size_t hostlen = strlen(hostname); +#ifdef ENABLE_IPV6 + struct in6_addr addr; +#else + struct in_addr addr; +#endif + + /* Verify that connection server matches info in X509 certificate at + `beg'..`end'. */ + + if(!SSL_CONN_CONFIG(verifyhost)) + return CURLE_OK; + + if(Curl_parseX509(&cert, beg, end)) + return CURLE_PEER_FAILED_VERIFICATION; + + /* Get the server IP address. */ +#ifdef ENABLE_IPV6 + if(conn->bits.ipv6_ip && Curl_inet_pton(AF_INET6, hostname, &addr)) + addrlen = sizeof(struct in6_addr); + else +#endif + if(Curl_inet_pton(AF_INET, hostname, &addr)) + addrlen = sizeof(struct in_addr); + + /* Process extensions. */ + for(p = cert.extensions.beg; p < cert.extensions.end && matched != 1;) { + p = getASN1Element(&ext, p, cert.extensions.end); + if(!p) + return CURLE_PEER_FAILED_VERIFICATION; + + /* Check if extension is a subjectAlternativeName. */ + ext.beg = checkOID(ext.beg, ext.end, sanOID); + if(ext.beg) { + ext.beg = getASN1Element(&elem, ext.beg, ext.end); + if(!ext.beg) + return CURLE_PEER_FAILED_VERIFICATION; + /* Skip critical if present. */ + if(elem.tag == CURL_ASN1_BOOLEAN) { + ext.beg = getASN1Element(&elem, ext.beg, ext.end); + if(!ext.beg) + return CURLE_PEER_FAILED_VERIFICATION; + } + /* Parse the octet string contents: is a single sequence. */ + if(!getASN1Element(&elem, elem.beg, elem.end)) + return CURLE_PEER_FAILED_VERIFICATION; + /* Check all GeneralNames. */ + for(q = elem.beg; matched != 1 && q < elem.end;) { + q = getASN1Element(&name, q, elem.end); + if(!q) + break; + switch(name.tag) { + case 2: /* DNS name. */ + len = utf8asn1str(&dnsname, CURL_ASN1_IA5_STRING, + name.beg, name.end); + if(len > 0 && (size_t)len == strlen(dnsname)) + matched = Curl_cert_hostcheck(dnsname, + (size_t)len, hostname, hostlen); + else + matched = 0; + free(dnsname); + break; + + case 7: /* IP address. */ + matched = (size_t) (name.end - name.beg) == addrlen && + !memcmp(&addr, name.beg, addrlen); + break; + } + } + } + } + + switch(matched) { + case 1: + /* an alternative name matched the server hostname */ + infof(data, " subjectAltName: %s matched", dispname); + return CURLE_OK; + case 0: + /* an alternative name field existed, but didn't match and then + we MUST fail */ + infof(data, " subjectAltName does not match %s", dispname); + return CURLE_PEER_FAILED_VERIFICATION; + } + + /* Process subject. */ + name.header = NULL; + name.beg = name.end = ""; + q = cert.subject.beg; + /* we have to look to the last occurrence of a commonName in the + distinguished one to get the most significant one. */ + while(q < cert.subject.end) { + q = getASN1Element(&dn, q, cert.subject.end); + if(!q) + break; + for(p = dn.beg; p < dn.end;) { + p = getASN1Element(&elem, p, dn.end); + if(!p) + return CURLE_PEER_FAILED_VERIFICATION; + /* We have a DN's AttributeTypeAndValue: check it in case it's a CN. */ + elem.beg = checkOID(elem.beg, elem.end, cnOID); + if(elem.beg) + name = elem; /* Latch CN. */ + } + } + + /* Check the CN if found. */ + if(!getASN1Element(&elem, name.beg, name.end)) + failf(data, "SSL: unable to obtain common name from peer certificate"); + else { + len = utf8asn1str(&dnsname, elem.tag, elem.beg, elem.end); + if(len < 0) { + free(dnsname); + return CURLE_OUT_OF_MEMORY; + } + if(strlen(dnsname) != (size_t) len) /* Nul byte in string ? */ + failf(data, "SSL: illegal cert name field"); + else if(Curl_cert_hostcheck((const char *) dnsname, + len, hostname, hostlen)) { + infof(data, " common name: %s (matched)", dnsname); + free(dnsname); + return CURLE_OK; + } + else + failf(data, "SSL: certificate subject name '%s' does not match " + "target host name '%s'", dnsname, dispname); + free(dnsname); + } + + return CURLE_PEER_FAILED_VERIFICATION; +} + +#endif /* WANT_VERIFYHOST */ diff --git a/contrib/libs/curl/lib/vtls/x509asn1.h b/contrib/libs/curl/lib/vtls/x509asn1.h new file mode 100644 index 0000000000..db7df0ef12 --- /dev/null +++ b/contrib/libs/curl/lib/vtls/x509asn1.h @@ -0,0 +1,78 @@ +#ifndef HEADER_CURL_X509ASN1_H +#define HEADER_CURL_X509ASN1_H + +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_GSKIT) || defined(USE_NSS) || defined(USE_GNUTLS) || \ + defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) + +#include "urldata.h" + +/* + * Types. + */ + +/* ASN.1 parsed element. */ +struct Curl_asn1Element { + const char *header; /* Pointer to header byte. */ + const char *beg; /* Pointer to element data. */ + const char *end; /* Pointer to 1st byte after element. */ + unsigned char class; /* ASN.1 element class. */ + unsigned char tag; /* ASN.1 element tag. */ + bool constructed; /* Element is constructed. */ +}; + +/* X509 certificate: RFC 5280. */ +struct Curl_X509certificate { + struct Curl_asn1Element certificate; + struct Curl_asn1Element version; + struct Curl_asn1Element serialNumber; + struct Curl_asn1Element signatureAlgorithm; + struct Curl_asn1Element signature; + struct Curl_asn1Element issuer; + struct Curl_asn1Element notBefore; + struct Curl_asn1Element notAfter; + struct Curl_asn1Element subject; + struct Curl_asn1Element subjectPublicKeyInfo; + struct Curl_asn1Element subjectPublicKeyAlgorithm; + struct Curl_asn1Element subjectPublicKey; + struct Curl_asn1Element issuerUniqueID; + struct Curl_asn1Element subjectUniqueID; + struct Curl_asn1Element extensions; +}; + +/* + * Prototypes. + */ + +int Curl_parseX509(struct Curl_X509certificate *cert, + const char *beg, const char *end); +CURLcode Curl_extract_certinfo(struct Curl_easy *data, int certnum, + const char *beg, const char *end); +CURLcode Curl_verifyhost(struct Curl_easy *data, struct connectdata *conn, + const char *beg, const char *end); +#endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL + * or USE_SECTRANSP */ +#endif /* HEADER_CURL_X509ASN1_H */ |