summaryrefslogtreecommitdiff
path: root/libs/libcurl/src/vtls
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libcurl/src/vtls')
-rw-r--r--libs/libcurl/src/vtls/bearssl.c181
-rw-r--r--libs/libcurl/src/vtls/bearssl.h2
-rw-r--r--libs/libcurl/src/vtls/cipher_suite.c16
-rw-r--r--libs/libcurl/src/vtls/cipher_suite.h2
-rw-r--r--libs/libcurl/src/vtls/gtls.c803
-rw-r--r--libs/libcurl/src/vtls/gtls.h35
-rw-r--r--libs/libcurl/src/vtls/hostcheck.c13
-rw-r--r--libs/libcurl/src/vtls/keylog.c39
-rw-r--r--libs/libcurl/src/vtls/keylog.h15
-rw-r--r--libs/libcurl/src/vtls/mbedtls.c408
-rw-r--r--libs/libcurl/src/vtls/mbedtls.h2
-rw-r--r--libs/libcurl/src/vtls/mbedtls_threadlock.c8
-rw-r--r--libs/libcurl/src/vtls/mbedtls_threadlock.h2
-rw-r--r--libs/libcurl/src/vtls/openssl.c1916
-rw-r--r--libs/libcurl/src/vtls/openssl.h51
-rw-r--r--libs/libcurl/src/vtls/rustls.c983
-rw-r--r--libs/libcurl/src/vtls/rustls.h2
-rw-r--r--libs/libcurl/src/vtls/schannel.c554
-rw-r--r--libs/libcurl/src/vtls/schannel.h10
-rw-r--r--libs/libcurl/src/vtls/schannel_int.h14
-rw-r--r--libs/libcurl/src/vtls/schannel_verify.c166
-rw-r--r--libs/libcurl/src/vtls/sectransp.c301
-rw-r--r--libs/libcurl/src/vtls/sectransp.h2
-rw-r--r--libs/libcurl/src/vtls/vtls.c952
-rw-r--r--libs/libcurl/src/vtls/vtls.h82
-rw-r--r--libs/libcurl/src/vtls/vtls_int.h88
-rw-r--r--libs/libcurl/src/vtls/vtls_scache.c1235
-rw-r--r--libs/libcurl/src/vtls/vtls_scache.h217
-rw-r--r--libs/libcurl/src/vtls/vtls_spack.c346
-rw-r--r--libs/libcurl/src/vtls/vtls_spack.h43
-rw-r--r--libs/libcurl/src/vtls/wolfssl.c1605
-rw-r--r--libs/libcurl/src/vtls/wolfssl.h61
-rw-r--r--libs/libcurl/src/vtls/x509asn1.c214
-rw-r--r--libs/libcurl/src/vtls/x509asn1.h18
34 files changed, 6222 insertions, 4164 deletions
diff --git a/libs/libcurl/src/vtls/bearssl.c b/libs/libcurl/src/vtls/bearssl.c
index f4d0cc43b3..4df187dc50 100644
--- a/libs/libcurl/src/vtls/bearssl.c
+++ b/libs/libcurl/src/vtls/bearssl.c
@@ -21,7 +21,7 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_BEARSSL
@@ -29,19 +29,20 @@
#include "bearssl.h"
#include "cipher_suite.h"
-#include "urldata.h"
-#include "sendf.h"
-#include "inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
#include "vtls.h"
#include "vtls_int.h"
-#include "connect.h"
-#include "select.h"
-#include "multiif.h"
-#include "curl_printf.h"
+#include "vtls_scache.h"
+#include "../connect.h"
+#include "../select.h"
+#include "../multiif.h"
+#include "../curl_printf.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
struct x509_context {
const br_x509_class *vtable;
@@ -152,7 +153,7 @@ static CURLcode load_cafile(struct cafile_source *source,
}
else if(source->type == CAFILE_SOURCE_BLOB) {
n = source->len;
- p = (unsigned char *) source->data;
+ p = (const unsigned char *) source->data;
}
while(n) {
pushed = br_pem_decoder_push(&pc, p, n);
@@ -337,7 +338,7 @@ static unsigned x509_end_chain(const br_x509_class **ctx)
static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx,
unsigned *usages)
{
- struct x509_context *x509 = (struct x509_context *)ctx;
+ struct x509_context *x509 = (struct x509_context *)CURL_UNCONST(ctx);
if(!x509->verifypeer) {
/* Nothing in the chain is verified, just return the public key of the
@@ -483,7 +484,7 @@ static const uint16_t ciphertable[] = {
BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */
};
-#define NUM_OF_CIPHERS (sizeof(ciphertable) / sizeof(ciphertable[0]))
+#define NUM_OF_CIPHERS CURL_ARRAYSIZE(ciphertable)
static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data,
br_ssl_engine_context *ssl_eng,
@@ -609,20 +610,19 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable);
if(ssl_config->primary.cache_session) {
- void *sdata;
- size_t slen;
- const br_ssl_session_parameters *session;
-
- CURL_TRC_CF(data, cf, "connect_step1, check session cache");
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &sdata, &slen, NULL) &&
- slen == sizeof(*session)) {
- session = sdata;
+ struct Curl_ssl_session *sc_session = NULL;
+
+ ret = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key,
+ &sc_session);
+ if(!ret && sc_session && sc_session->sdata && sc_session->sdata_len) {
+ const br_ssl_session_parameters *session;
+ session = (const br_ssl_session_parameters *)sc_session->sdata;
br_ssl_engine_set_session_parameters(&backend->ctx.eng, session);
session_set = 1;
infof(data, "BearSSL: reusing session ID");
+ /* single use of sessions */
+ Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session);
}
- Curl_ssl_sessionid_unlock(data);
}
if(connssl->alpn) {
@@ -729,7 +729,7 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
return CURLE_OK;
if(state & BR_SSL_SENDREC) {
buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len);
- ret = Curl_conn_cf_send(cf->next, data, (char *)buf, len, FALSE,
+ ret = Curl_conn_cf_send(cf->next, data, (const char *)buf, len, FALSE,
&result);
CURL_TRC_CF(data, cf, "ssl_send(len=%zu) -> %zd, %d", len, ret, result);
if(ret <= 0) {
@@ -804,12 +804,6 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf,
return ret;
}
-static void bearssl_session_free(void *sessionid, size_t idsize)
-{
- (void)idsize;
- free(sessionid);
-}
-
static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
@@ -832,17 +826,22 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
}
if(ssl_config->primary.cache_session) {
+ struct Curl_ssl_session *sc_session;
br_ssl_session_parameters *session;
session = malloc(sizeof(*session));
if(!session)
return CURLE_OUT_OF_MEMORY;
br_ssl_engine_get_session_parameters(&backend->ctx.eng, session);
- Curl_ssl_sessionid_lock(data);
- ret = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
- session, sizeof(*session),
- bearssl_session_free);
- Curl_ssl_sessionid_unlock(data);
+ ret = Curl_ssl_session_create((unsigned char *)session, sizeof(*session),
+ (int)session->version,
+ connssl->negotiated.alpn,
+ 0, 0, &sc_session);
+ if(!ret) {
+ ret = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key,
+ sc_session);
+ /* took ownership of `sc_session` */
+ }
if(ret)
return ret;
}
@@ -912,18 +911,14 @@ static ssize_t bearssl_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
return applen;
}
-static CURLcode bearssl_connect_common(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool nonblocking,
- bool *done)
+static CURLcode bearssl_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
{
CURLcode ret;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
- timediff_t timeout_ms;
- int what;
- CURL_TRC_CF(data, cf, "connect_common(blocking=%d)", !nonblocking);
+ CURL_TRC_CF(data, cf, "connect()");
/* check if the connection has already been established */
if(ssl_connection_complete == connssl->state) {
CURL_TRC_CF(data, cf, "connect_common, connected");
@@ -931,61 +926,18 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf,
return CURLE_OK;
}
+ *done = FALSE;
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
+
if(ssl_connect_1 == connssl->connecting_state) {
ret = bearssl_connect_step1(cf, data);
if(ret)
return ret;
}
- while(ssl_connect_2 == 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 is available. */
- if(connssl->io_need) {
- curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
- sockfd : CURL_SOCKET_BAD;
- curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
- sockfd : CURL_SOCKET_BAD;
-
- CURL_TRC_CF(data, cf, "connect_common, check socket");
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking ? 0 : timeout_ms);
- CURL_TRC_CF(data, cf, "connect_common, check socket -> %d", what);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
- }
- else if(0 == what) {
- if(nonblocking) {
- *done = FALSE;
- return CURLE_OK;
- }
- else {
- /* timeout */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
- }
- /* socket is readable or writable */
- }
-
- /* Run transaction, and return to the caller if it failed or if this
- * connection is done nonblocking and this loop would execute again. This
- * permits the owner of a multi handle to abort a connection attempt
- * before step2 has completed while ensuring that a client using select()
- * or epoll() will always have a valid fdset to wait on.
- */
- connssl->io_need = CURL_SSL_IO_NEED_NONE;
+ if(ssl_connect_2 == connssl->connecting_state) {
ret = bearssl_connect_step2(cf, data);
- if(ret || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
+ if(ret)
return ret;
}
@@ -999,11 +951,6 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf,
connssl->state = ssl_connection_complete;
*done = TRUE;
}
- else
- *done = FALSE;
-
- /* Reset our connect state machine */
- connssl->connecting_state = ssl_connect_1;
return CURLE_OK;
}
@@ -1045,28 +992,6 @@ static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM,
return CURLE_OK;
}
-static CURLcode bearssl_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- CURLcode ret;
- bool done = FALSE;
-
- ret = bearssl_connect_common(cf, data, FALSE, &done);
- if(ret)
- return ret;
-
- DEBUGASSERT(done);
-
- return CURLE_OK;
-}
-
-static CURLcode bearssl_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
-{
- return bearssl_connect_common(cf, data, TRUE, done);
-}
-
static void *bearssl_get_internals(struct ssl_connect_data *connssl,
CURLINFO info UNUSED_PARAM)
{
@@ -1154,27 +1079,23 @@ const struct Curl_ssl Curl_ssl_bearssl = {
sizeof(struct bearssl_ssl_backend_data),
- Curl_none_init, /* init */
- Curl_none_cleanup, /* cleanup */
+ NULL, /* init */
+ NULL, /* cleanup */
bearssl_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
bearssl_shutdown, /* shutdown */
bearssl_data_pending, /* data_pending */
bearssl_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
bearssl_connect, /* connect */
- bearssl_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
bearssl_get_internals, /* get_internals */
bearssl_close, /* close_one */
- Curl_none_close_all, /* close_all */
- 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, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
bearssl_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
bearssl_recv, /* recv decrypted data */
bearssl_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/bearssl.h b/libs/libcurl/src/vtls/bearssl.h
index 76857f9419..aca1a7bd6a 100644
--- a/libs/libcurl/src/vtls/bearssl.h
+++ b/libs/libcurl/src/vtls/bearssl.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_BEARSSL
diff --git a/libs/libcurl/src/vtls/cipher_suite.c b/libs/libcurl/src/vtls/cipher_suite.c
index ca7c5ba46d..68bf3b6305 100644
--- a/libs/libcurl/src/vtls/cipher_suite.c
+++ b/libs/libcurl/src/vtls/cipher_suite.c
@@ -21,13 +21,13 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \
defined(USE_BEARSSL) || defined(USE_RUSTLS)
#include "cipher_suite.h"
-#include "curl_printf.h"
-#include "strcase.h"
+#include "../curl_printf.h"
+#include "../strcase.h"
#include <string.h>
/*
@@ -725,7 +725,7 @@ static const struct cs_entry cs_list [] = {
CS_ENTRY(0xCCAE, RSA,PSK,CHACHA20,POLY1305,,,,),
#endif
};
-#define CS_LIST_LEN (sizeof(cs_list) / sizeof(cs_list[0]))
+#define CS_LIST_LEN CURL_ARRAYSIZE(cs_list)
static int cs_str_to_zip(const char *cs_str, size_t cs_len,
uint8_t zip[6])
@@ -786,12 +786,12 @@ static int cs_zip_to_str(const uint8_t zip[6],
/* unzip the 8 indexes */
indexes[0] = zip[0] >> 2;
- indexes[1] = ((zip[0] << 4) & 0x3F) | zip[1] >> 4;
- indexes[2] = ((zip[1] << 2) & 0x3F) | zip[2] >> 6;
+ indexes[1] = (uint8_t)(((zip[0] << 4) & 0x3F) | zip[1] >> 4);
+ indexes[2] = (uint8_t)(((zip[1] << 2) & 0x3F) | zip[2] >> 6);
indexes[3] = ((zip[2] << 0) & 0x3F);
indexes[4] = zip[3] >> 2;
- indexes[5] = ((zip[3] << 4) & 0x3F) | zip[4] >> 4;
- indexes[6] = ((zip[4] << 2) & 0x3F) | zip[5] >> 6;
+ indexes[5] = (uint8_t)(((zip[3] << 4) & 0x3F) | zip[4] >> 4);
+ indexes[6] = (uint8_t)(((zip[4] << 2) & 0x3F) | zip[5] >> 6);
indexes[7] = ((zip[5] << 0) & 0x3F);
if(indexes[0] == CS_TXT_IDX_TLS)
diff --git a/libs/libcurl/src/vtls/cipher_suite.h b/libs/libcurl/src/vtls/cipher_suite.h
index 8f3381d1b9..f25de29b52 100644
--- a/libs/libcurl/src/vtls/cipher_suite.h
+++ b/libs/libcurl/src/vtls/cipher_suite.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \
defined(USE_BEARSSL) || defined(USE_RUSTLS)
diff --git a/libs/libcurl/src/vtls/gtls.c b/libs/libcurl/src/vtls/gtls.c
index 398af77bf3..7f2401afd5 100644
--- a/libs/libcurl/src/vtls/gtls.c
+++ b/libs/libcurl/src/vtls/gtls.c
@@ -30,7 +30,7 @@
* since they were not present in 1.0.X.
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_GNUTLS
@@ -40,36 +40,28 @@
#include <gnutls/crypto.h>
#include <nettle/sha2.h>
-#include "urldata.h"
-#include "sendf.h"
-#include "inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
#include "keylog.h"
#include "gtls.h"
#include "vtls.h"
#include "vtls_int.h"
-#include "vauth/vauth.h"
-#include "parsedate.h"
-#include "connect.h" /* for the connect timeout */
-#include "progress.h"
-#include "select.h"
-#include "strcase.h"
-#include "warnless.h"
+#include "vtls_scache.h"
+#include "../vauth/vauth.h"
+#include "../parsedate.h"
+#include "../connect.h" /* for the connect timeout */
+#include "../progress.h"
+#include "../select.h"
+#include "../strcase.h"
+#include "../strdup.h"
+#include "../curlx/warnless.h"
#include "x509asn1.h"
-#include "multiif.h"
-#include "curl_printf.h"
-#include "curl_memory.h"
+#include "../multiif.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
/* The last #include file should be: */
-#include "memdebug.h"
-
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
-#define QUIC_PRIORITY \
- "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
- "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
- "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
- "%DISABLE_TLS13_COMPAT_MODE"
+#include "../memdebug.h"
/* Enable GnuTLS debugging by defining GTLSDEBUG */
/*#define GTLSDEBUG */
@@ -108,6 +100,7 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen)
blen, nwritten, result);
backend->gtls.io_result = result;
if(nwritten < 0) {
+ /* !checksrc! disable ERRNOVAR 1 */
gnutls_transport_set_errno(backend->gtls.session,
(CURLE_AGAIN == result) ? EAGAIN : EINVAL);
nwritten = -1;
@@ -129,6 +122,7 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen)
if(!backend->gtls.shared_creds->trust_setup) {
result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls);
if(result) {
+ /* !checksrc! disable ERRNOVAR 1 */
gnutls_transport_set_errno(backend->gtls.session, EINVAL);
backend->gtls.io_result = result;
return -1;
@@ -140,6 +134,7 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen)
blen, nread, result);
backend->gtls.io_result = result;
if(nread < 0) {
+ /* !checksrc! disable ERRNOVAR 1 */
gnutls_transport_set_errno(backend->gtls.session,
(CURLE_AGAIN == result) ? EAGAIN : EINVAL);
nread = -1;
@@ -238,117 +233,70 @@ static void unload_file(gnutls_datum_t data)
}
-/* this function does a SSL/TLS (re-)handshake */
+/* this function does an SSL/TLS (re-)handshake */
static CURLcode handshake(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool duringconnect,
- bool nonblocking)
+ struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
struct gtls_ssl_backend_data *backend =
(struct gtls_ssl_backend_data *)connssl->backend;
gnutls_session_t session;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
+ int rc;
DEBUGASSERT(backend);
session = backend->gtls.session;
- connssl->connecting_state = ssl_connect_2;
-
- for(;;) {
- timediff_t timeout_ms;
- int rc;
-
- /* check allowed time left */
- timeout_ms = Curl_timeleft(data, NULL, duringconnect);
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
-
- /* if ssl is expecting something, check if it is available. */
- if(connssl->io_need) {
- int what;
- curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
- sockfd : CURL_SOCKET_BAD;
- curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
- sockfd : CURL_SOCKET_BAD;
-
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking ? 0 :
- timeout_ms ? timeout_ms : 1000);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
- }
- else if(0 == what) {
- if(nonblocking)
- return CURLE_AGAIN;
- else if(timeout_ms) {
- /* timeout */
- failf(data, "SSL connection timeout at %ld", (long)timeout_ms);
- return CURLE_OPERATION_TIMEDOUT;
- }
- }
- /* socket is readable or writable */
- }
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
+ backend->gtls.io_result = CURLE_OK;
+ rc = gnutls_handshake(session);
- connssl->io_need = CURL_SSL_IO_NEED_NONE;
- backend->gtls.io_result = CURLE_OK;
- rc = gnutls_handshake(session);
+ if(!backend->gtls.shared_creds->trust_setup) {
+ /* After having send off the ClientHello, we prepare the trust
+ * store to verify the coming certificate from the server */
+ CURLcode result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls);
+ if(result)
+ return result;
+ }
- if(!backend->gtls.shared_creds->trust_setup) {
- /* After having send off the ClientHello, we prepare the trust
- * store to verify the coming certificate from the server */
- CURLcode result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls);
- if(result)
- return result;
- }
+ if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
+ connssl->io_need =
+ gnutls_record_get_direction(session) ?
+ CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV;
+ return CURLE_AGAIN;
+ }
+ else if((rc < 0) && !gnutls_error_is_fatal(rc)) {
+ const char *strerr = NULL;
- if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
- connssl->io_need =
- gnutls_record_get_direction(session) ?
- CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV;
- continue;
+ if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
+ gnutls_alert_description_t alert = gnutls_alert_get(session);
+ strerr = gnutls_alert_get_name(alert);
}
- else if((rc < 0) && !gnutls_error_is_fatal(rc)) {
- const char *strerr = NULL;
- if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
- gnutls_alert_description_t alert = gnutls_alert_get(session);
- strerr = gnutls_alert_get_name(alert);
- }
+ if(!strerr)
+ strerr = gnutls_strerror(rc);
- if(!strerr)
- strerr = gnutls_strerror(rc);
+ infof(data, "gnutls_handshake() warning: %s", strerr);
+ return CURLE_AGAIN;
+ }
+ else if((rc < 0) && backend->gtls.io_result) {
+ return backend->gtls.io_result;
+ }
+ else if(rc < 0) {
+ const char *strerr = NULL;
- infof(data, "gnutls_handshake() warning: %s", strerr);
- continue;
+ if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
+ gnutls_alert_description_t alert = gnutls_alert_get(session);
+ strerr = gnutls_alert_get_name(alert);
}
- else if((rc < 0) && backend->gtls.io_result) {
- return backend->gtls.io_result;
- }
- else if(rc < 0) {
- const char *strerr = NULL;
-
- if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
- gnutls_alert_description_t alert = gnutls_alert_get(session);
- strerr = gnutls_alert_get_name(alert);
- }
-
- if(!strerr)
- strerr = gnutls_strerror(rc);
- failf(data, "GnuTLS, handshake failed: %s", strerr);
- return CURLE_SSL_CONNECT_ERROR;
- }
+ if(!strerr)
+ strerr = gnutls_strerror(rc);
- /* Reset our connect state machine */
- connssl->connecting_state = ssl_connect_1;
- return CURLE_OK;
+ failf(data, "GnuTLS, handshake failed: %s", strerr);
+ return CURLE_SSL_CONNECT_ERROR;
}
+
+ return CURLE_OK;
}
static gnutls_x509_crt_fmt_t gnutls_do_file_type(const char *type)
@@ -368,12 +316,18 @@ static gnutls_x509_crt_fmt_t gnutls_do_file_type(const char *type)
*/
#define GNUTLS_SRP "+SRP"
+#define QUIC_PRIORITY \
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
+ "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
+ "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
+ "%DISABLE_TLS13_COMPAT_MODE"
+
static CURLcode
gnutls_set_ssl_version_min_max(struct Curl_easy *data,
struct ssl_peer *peer,
struct ssl_primary_config *conn_config,
const char **prioritylist,
- const char *tls13support)
+ bool tls13support)
{
long ssl_version = conn_config->version;
long ssl_version_max = conn_config->version_max;
@@ -472,7 +426,7 @@ CURLcode Curl_gtls_shared_creds_create(struct Curl_easy *data,
}
shared->refcount = 1;
- shared->time = Curl_now();
+ shared->time = curlx_now();
*pcreds = shared;
return CURLE_OK;
}
@@ -586,8 +540,8 @@ static bool gtls_shared_creds_expired(const struct Curl_easy *data,
const struct gtls_shared_creds *sc)
{
const struct ssl_general_config *cfg = &data->set.general_ssl;
- struct curltime now = Curl_now();
- timediff_t elapsed_ms = Curl_timediff(now, sc->time);
+ struct curltime now = curlx_now();
+ timediff_t elapsed_ms = curlx_timediff(now, sc->time);
timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
if(timeout_ms < 0)
@@ -613,7 +567,7 @@ gtls_get_cached_creds(struct Curl_cfilter *cf, struct Curl_easy *data)
if(data->multi) {
shared_creds = Curl_hash_pick(&data->multi->proto_hash,
- (void *)MPROTO_GTLS_X509_KEY,
+ CURL_UNCONST(MPROTO_GTLS_X509_KEY),
sizeof(MPROTO_GTLS_X509_KEY)-1);
if(shared_creds && shared_creds->creds &&
!gtls_shared_creds_expired(data, shared_creds) &&
@@ -657,7 +611,7 @@ static void gtls_set_cached_creds(struct Curl_cfilter *cf,
return;
if(!Curl_hash_add2(&data->multi->proto_hash,
- (void *)MPROTO_GTLS_X509_KEY,
+ CURL_UNCONST(MPROTO_GTLS_X509_KEY),
sizeof(MPROTO_GTLS_X509_KEY)-1,
sc, gtls_shared_creds_hash_free)) {
Curl_gtls_shared_creds_free(&sc); /* down reference again */
@@ -714,21 +668,20 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf,
return CURLE_OK;
}
-static void gtls_sessionid_free(void *sessionid, size_t idsize)
-{
- (void)idsize;
- free(sessionid);
-}
-
-CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- gnutls_session_t session,
- struct ssl_peer *peer,
- const char *alpn)
+CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ gnutls_session_t session,
+ curl_off_t valid_until,
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len)
{
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- void *connect_sessionid;
- size_t connect_idsize = 0;
+ struct Curl_ssl_session *sc_session;
+ unsigned char *sdata, *qtp_clone = NULL;
+ size_t sdata_len = 0;
+ size_t earlydata_max = 0;
CURLcode result = CURLE_OK;
if(!ssl_config->primary.cache_session)
@@ -740,35 +693,68 @@ CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf,
detect that. */
/* get the session ID data size */
- gnutls_session_get_data(session, NULL, &connect_idsize);
- if(!connect_idsize) /* gnutls does this for some version combinations */
+ gnutls_session_get_data(session, NULL, &sdata_len);
+ if(!sdata_len) /* gnutls does this for some version combinations */
return CURLE_OK;
- connect_sessionid = malloc(connect_idsize); /* get a buffer for it */
- if(!connect_sessionid)
+ sdata = malloc(sdata_len); /* get a buffer for it */
+ if(!sdata)
return CURLE_OUT_OF_MEMORY;
/* extract session ID to the allocated buffer */
- gnutls_session_get_data(session, connect_sessionid, &connect_idsize);
-
- CURL_TRC_CF(data, cf, "get session id (len=%zu, alpn=%s) and store in cache",
- connect_idsize, alpn ? alpn : "-");
- Curl_ssl_sessionid_lock(data);
- /* store this session id, takes ownership */
- result = Curl_ssl_set_sessionid(cf, data, peer, alpn,
- connect_sessionid, connect_idsize,
- gtls_sessionid_free);
- Curl_ssl_sessionid_unlock(data);
+ gnutls_session_get_data(session, sdata, &sdata_len);
+ earlydata_max = gnutls_record_get_max_early_data_size(session);
+
+ CURL_TRC_CF(data, cf, "get session id (len=%zu, alpn=%s, earlymax=%zu) "
+ "and store in cache", sdata_len, alpn ? alpn : "-",
+ earlydata_max);
+ if(quic_tp && quic_tp_len) {
+ qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len);
+ if(!qtp_clone) {
+ free(sdata);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ result = Curl_ssl_session_create2(sdata, sdata_len,
+ Curl_glts_get_ietf_proto(session),
+ alpn, valid_until, earlydata_max,
+ qtp_clone, quic_tp_len,
+ &sc_session);
+ /* call took ownership of `sdata` and `qtp_clone` */
+ if(!result) {
+ result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
+ /* took ownership of `sc_session` */
+ }
return result;
}
+int Curl_glts_get_ietf_proto(gnutls_session_t session)
+{
+ switch(gnutls_protocol_get_version(session)) {
+ case GNUTLS_SSL3:
+ return CURL_IETF_PROTO_SSL3;
+ case GNUTLS_TLS1_0:
+ return CURL_IETF_PROTO_TLS1;
+ case GNUTLS_TLS1_1:
+ return CURL_IETF_PROTO_TLS1_1;
+ case GNUTLS_TLS1_2:
+ return CURL_IETF_PROTO_TLS1_2;
+ case GNUTLS_TLS1_3:
+ return CURL_IETF_PROTO_TLS1_3;
+ default:
+ return CURL_IETF_PROTO_UNKNOWN;
+ }
+}
+
static CURLcode cf_gtls_update_session_id(struct Curl_cfilter *cf,
struct Curl_easy *data,
gnutls_session_t session)
{
struct ssl_connect_data *connssl = cf->ctx;
- return Curl_gtls_update_session_id(cf, data, session, &connssl->peer,
- connssl->alpn_negotiated);
+ return Curl_gtls_cache_session(cf, data, connssl->peer.scache_key,
+ session, 0, connssl->negotiated.alpn,
+ NULL, 0);
}
static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
@@ -797,9 +783,67 @@ static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
return 0;
}
+static CURLcode gtls_set_priority(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct gtls_ctx *gtls,
+ const char *priority)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct dynbuf buf;
+ const char *err = NULL;
+ CURLcode result = CURLE_OK;
+ int rc;
+
+ curlx_dyn_init(&buf, 4096);
+
+#ifdef USE_GNUTLS_SRP
+ if(conn_config->username) {
+ /* Only add SRP to the cipher list if SRP is requested. Otherwise
+ * GnuTLS will disable TLS 1.3 support. */
+ result = curlx_dyn_add(&buf, priority);
+ if(!result)
+ result = curlx_dyn_add(&buf, ":" GNUTLS_SRP);
+ if(result)
+ goto out;
+ priority = curlx_dyn_ptr(&buf);
+ }
+#endif
+
+ if(conn_config->cipher_list) {
+ if((conn_config->cipher_list[0] == '+') ||
+ (conn_config->cipher_list[0] == '-') ||
+ (conn_config->cipher_list[0] == '!')) {
+ /* add it to out own */
+ if(!curlx_dyn_len(&buf)) { /* not added yet */
+ result = curlx_dyn_add(&buf, priority);
+ if(result)
+ goto out;
+ }
+ result = curlx_dyn_addf(&buf, ":%s", conn_config->cipher_list);
+ if(result)
+ goto out;
+ priority = curlx_dyn_ptr(&buf);
+ }
+ else /* replace our own completely */
+ priority = conn_config->cipher_list;
+ }
+
+ infof(data, "GnuTLS priority: %s", priority);
+ rc = gnutls_priority_set_direct(gtls->session, priority, &err);
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data, "Error %d setting GnuTLS priority: %s", rc, err);
+ result = CURLE_SSL_CONNECT_ERROR;
+ }
+
+out:
+ curlx_dyn_free(&buf);
+ return result;
+}
+
static CURLcode gtls_client_init(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
+ size_t earlydata_max,
struct gtls_ctx *gtls)
{
struct ssl_primary_config *config = Curl_ssl_cf_get_primary_config(cf);
@@ -808,8 +852,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
int rc;
bool sni = TRUE; /* default is SNI enabled */
const char *prioritylist;
- const char *err = NULL;
- const char *tls13support;
+ bool tls13support;
CURLcode result;
if(!gtls_inited)
@@ -853,6 +896,14 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
/* Initialize TLS session as a client */
init_flags = GNUTLS_CLIENT;
+ if(peer->transport == TRNSPRT_QUIC && earlydata_max > 0)
+ init_flags |= GNUTLS_ENABLE_EARLY_DATA | GNUTLS_NO_END_OF_EARLY_DATA;
+ else if(earlydata_max > 0 && earlydata_max != 0xFFFFFFFFUL)
+ /* See https://gitlab.com/gnutls/gnutls/-/issues/1619
+ * We cannot differentiate between a session announcing no earldata
+ * and one announcing 0xFFFFFFFFUL. On TCP+TLS, this is unlikely, but
+ * on QUIC this is common. */
+ init_flags |= GNUTLS_ENABLE_EARLY_DATA;
#if defined(GNUTLS_FORCE_CLIENT_CERT)
init_flags |= GNUTLS_FORCE_CLIENT_CERT;
@@ -874,6 +925,8 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
init_flags |= GNUTLS_NO_STATUS_REQUEST;
#endif
+ CURL_TRC_CF(data, cf, "gnutls_init(flags=%x), earlydata=%zu",
+ init_flags, earlydata_max);
rc = gnutls_init(&gtls->session, init_flags);
if(rc != GNUTLS_E_SUCCESS) {
failf(data, "gnutls_init() failed: %d", rc);
@@ -894,7 +947,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
return CURLE_SSL_CONNECT_ERROR;
/* "In GnuTLS 3.6.5, TLS 1.3 is enabled by default" */
- tls13support = gnutls_check_version("3.6.5");
+ tls13support = !!gnutls_check_version("3.6.5");
/* Ensure +SRP comes at the *end* of all relevant strings so that it can be
* removed if a runtime error indicates that SRP is not supported by this
@@ -919,33 +972,9 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
if(result)
return result;
-#ifdef USE_GNUTLS_SRP
- /* Only add SRP to the cipher list if SRP is requested. Otherwise
- * GnuTLS will disable TLS 1.3 support. */
- if(config->username) {
- char *prioritysrp = aprintf("%s:" GNUTLS_SRP, prioritylist);
- if(!prioritysrp)
- return CURLE_OUT_OF_MEMORY;
- rc = gnutls_priority_set_direct(gtls->session, prioritysrp, &err);
- free(prioritysrp);
-
- if((rc == GNUTLS_E_INVALID_REQUEST) && err) {
- infof(data, "This GnuTLS does not support SRP");
- }
- }
- else {
-#endif
- infof(data, "GnuTLS ciphers: %s", prioritylist);
- rc = gnutls_priority_set_direct(gtls->session, prioritylist, &err);
-#ifdef USE_GNUTLS_SRP
- }
-#endif
-
- if(rc != GNUTLS_E_SUCCESS) {
- failf(data, "Error %d setting GnuTLS cipher list starting with %s",
- rc, err);
- return CURLE_SSL_CONNECT_ERROR;
- }
+ result = gtls_set_priority(cf, data, gtls, prioritylist);
+ if(result)
+ return result;
if(config->clientcert) {
if(!gtls->shared_creds->trust_setup) {
@@ -965,7 +994,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
return CURLE_SSL_CONNECT_ERROR;
}
}
- else if(ssl_config->key_passwd) {
+ else {
const unsigned int supported_key_encryption_algorithms =
GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR |
GNUTLS_PKCS_USE_PKCS12_RC2_40 | GNUTLS_PKCS_USE_PBES2_3DES |
@@ -980,22 +1009,12 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
supported_key_encryption_algorithms);
if(rc != GNUTLS_E_SUCCESS) {
failf(data,
- "error reading X.509 potentially-encrypted key file: %s",
+ "error reading X.509 %skey file: %s",
+ ssl_config->key_passwd ? "potentially-encrypted " : "",
gnutls_strerror(rc));
return CURLE_SSL_CONNECT_ERROR;
}
}
- else {
- if(gnutls_certificate_set_x509_key_file(
- gtls->shared_creds->creds,
- config->clientcert,
- ssl_config->key ? ssl_config->key : config->clientcert,
- gnutls_do_file_type(ssl_config->cert_type) ) !=
- GNUTLS_E_SUCCESS) {
- failf(data, "error reading X.509 key or certificate file");
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
}
#ifdef USE_GNUTLS_SRP
@@ -1046,117 +1065,136 @@ static int keylog_callback(gnutls_session_t session, const char *label,
return 0;
}
+static CURLcode gtls_on_session_reuse(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct alpn_spec *alpns,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct gtls_ssl_backend_data *backend =
+ (struct gtls_ssl_backend_data *)connssl->backend;
+ CURLcode result = CURLE_OK;
+
+ *do_early_data = FALSE;
+ connssl->earlydata_max =
+ gnutls_record_get_max_early_data_size(backend->gtls.session);
+ if((!connssl->earlydata_max || connssl->earlydata_max == 0xFFFFFFFFUL)) {
+ /* Seems to be GnuTLS way to signal no EarlyData in session */
+ CURL_TRC_CF(data, cf, "SSL session does not allow earlydata");
+ }
+ else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) {
+ CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data");
+ }
+ else {
+ infof(data, "SSL session allows %zu bytes of early data, "
+ "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn);
+ connssl->earlydata_state = ssl_earlydata_await;
+ connssl->state = ssl_connection_deferred;
+ result = Curl_alpn_set_negotiated(cf, data, connssl,
+ (const unsigned char *)scs->alpn,
+ scs->alpn ? strlen(scs->alpn) : 0);
+ *do_early_data = !result;
+ }
+ return result;
+}
+
CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- const unsigned char *alpn, size_t alpn_len,
- struct ssl_connect_data *connssl,
+ const struct alpn_spec *alpns_requested,
Curl_gtls_ctx_setup_cb *cb_setup,
void *cb_user_data,
- void *ssl_user_data)
+ void *ssl_user_data,
+ Curl_gtls_init_session_reuse_cb *sess_reuse_cb)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- gnutls_datum_t gtls_alpns[5];
+ struct Curl_ssl_session *scs = NULL;
+ gnutls_datum_t gtls_alpns[ALPN_ENTRIES_MAX];
size_t gtls_alpns_count = 0;
- CURLcode result;
+ bool gtls_session_setup = FALSE;
+ struct alpn_spec alpns;
+ CURLcode result = CURLE_OK;
+ int rc;
DEBUGASSERT(gctx);
+ Curl_alpn_copy(&alpns, alpns_requested);
- result = gtls_client_init(cf, data, peer, gctx);
- if(result)
- return result;
-
- gnutls_session_set_ptr(gctx->session, ssl_user_data);
-
- if(cb_setup) {
- result = cb_setup(cf, data, cb_user_data);
+ /* This might be a reconnect, so we check for a session ID in the cache
+ to speed up things. We need to do this before constructing the gnutls
+ session since we need to set flags depending on the kind of reuse. */
+ if(conn_config->cache_session) {
+ result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs);
if(result)
- return result;
- }
+ goto out;
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
- gnutls_session_set_keylog_function(gctx->session, keylog_callback);
- }
+ if(scs && scs->sdata && scs->sdata_len &&
+ (!scs->alpn || Curl_alpn_contains_proto(&alpns, scs->alpn))) {
+ /* we got a cached session, use it! */
- /* This might be a reconnect, so we check for a session ID in the cache
- to speed up things */
- if(conn_config->cache_session) {
- void *ssl_sessionid;
- size_t ssl_idsize;
- char *session_alpn;
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, peer,
- &ssl_sessionid, &ssl_idsize, &session_alpn)) {
- /* we got a session id, use it! */
- int rc;
-
- rc = gnutls_session_set_data(gctx->session, ssl_sessionid, ssl_idsize);
+ result = gtls_client_init(cf, data, peer, scs->earlydata_max, gctx);
+ if(result)
+ goto out;
+ gtls_session_setup = TRUE;
+
+ rc = gnutls_session_set_data(gctx->session, scs->sdata, scs->sdata_len);
if(rc < 0)
- infof(data, "SSL failed to set session ID");
+ infof(data, "SSL session not accepted by GnuTLS, continuing without");
else {
- infof(data, "SSL reusing session ID (size=%zu, alpn=%s)",
- ssl_idsize, session_alpn ? session_alpn : "-");
-#ifdef DEBUGBUILD
- if((ssl_config->earlydata || !!getenv("CURL_USE_EARLYDATA")) &&
-#else
- if(ssl_config->earlydata &&
-#endif
- !cf->conn->connect_only && connssl &&
- (gnutls_protocol_get_version(gctx->session) == GNUTLS_TLS1_3) &&
- Curl_alpn_contains_proto(connssl->alpn, session_alpn)) {
- connssl->earlydata_max =
- gnutls_record_get_max_early_data_size(gctx->session);
- if((!connssl->earlydata_max ||
- connssl->earlydata_max == 0xFFFFFFFFUL)) {
- /* Seems to be GnuTLS way to signal no EarlyData in session */
- CURL_TRC_CF(data, cf, "TLS session does not allow earlydata");
- }
- else {
- CURL_TRC_CF(data, cf, "TLS session allows %zu earlydata bytes, "
- "reusing ALPN '%s'",
- connssl->earlydata_max, session_alpn);
- connssl->earlydata_state = ssl_earlydata_use;
- connssl->state = ssl_connection_deferred;
- result = Curl_alpn_set_negotiated(cf, data, connssl,
- (const unsigned char *)session_alpn,
- session_alpn ? strlen(session_alpn) : 0);
+ infof(data, "SSL reusing session with ALPN '%s'",
+ scs->alpn ? scs->alpn : "-");
+ if(ssl_config->earlydata && scs->alpn &&
+ !cf->conn->connect_only &&
+ (gnutls_protocol_get_version(gctx->session) == GNUTLS_TLS1_3)) {
+ bool do_early_data = FALSE;
+ if(sess_reuse_cb) {
+ result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data);
if(result)
- return result;
+ goto out;
+ }
+ if(do_early_data) {
/* We only try the ALPN protocol the session used before,
* otherwise we might send early data for the wrong protocol */
- gtls_alpns[0].data = (unsigned char *)session_alpn;
- gtls_alpns[0].size = (unsigned)strlen(session_alpn);
- gtls_alpns_count = 1;
+ Curl_alpn_restrict_to(&alpns, scs->alpn);
}
}
}
}
- Curl_ssl_sessionid_unlock(data);
+ }
+
+ if(!gtls_session_setup) {
+ result = gtls_client_init(cf, data, peer, 0, gctx);
+ if(result)
+ goto out;
+ }
+
+ gnutls_session_set_ptr(gctx->session, ssl_user_data);
+
+ if(cb_setup) {
+ result = cb_setup(cf, data, cb_user_data);
+ if(result)
+ goto out;
+ }
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+ gnutls_session_set_keylog_function(gctx->session, keylog_callback);
}
/* convert the ALPN string from our arguments to a list of strings that
* gnutls wants and will convert internally back to this string for sending
* to the server. nice. */
- if(!gtls_alpns_count && alpn && alpn_len) {
- size_t i, alen = alpn_len;
- unsigned char *s = (unsigned char *)alpn;
- unsigned char slen;
- for(i = 0; (i < ARRAYSIZE(gtls_alpns)) && alen; ++i) {
- slen = s[0];
- if(slen >= alen)
- return CURLE_FAILED_INIT;
- gtls_alpns[i].data = s + 1;
- gtls_alpns[i].size = slen;
- s += slen + 1;
- alen -= (size_t)slen + 1;
+ if(!gtls_alpns_count && alpns.count) {
+ size_t i;
+ DEBUGASSERT(CURL_ARRAYSIZE(gtls_alpns) >= alpns.count);
+ for(i = 0; i < alpns.count; ++i) {
+ gtls_alpns[i].data = (unsigned char *)alpns.entries[i];
+ gtls_alpns[i].size = (unsigned int)strlen(alpns.entries[i]);
}
- if(alen) /* not all alpn chars used, wrong format or too many */
- return CURLE_FAILED_INIT;
- gtls_alpns_count = i;
+ gtls_alpns_count = alpns.count;
}
if(gtls_alpns_count &&
@@ -1164,10 +1202,12 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
gtls_alpns, (unsigned int)gtls_alpns_count,
GNUTLS_ALPN_MANDATORY)) {
failf(data, "failed setting ALPN");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
}
- return CURLE_OK;
+out:
+ Curl_ssl_scache_return(cf, data, peer->scache_key, scs);
+ return result;
}
static CURLcode
@@ -1176,32 +1216,24 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
struct ssl_connect_data *connssl = cf->ctx;
struct gtls_ssl_backend_data *backend =
(struct gtls_ssl_backend_data *)connssl->backend;
- struct alpn_proto_buf proto;
CURLcode result;
DEBUGASSERT(backend);
- DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
if(connssl->state == ssl_connection_complete)
/* to make us tolerant against being called more than once for the
same connection */
return CURLE_OK;
- memset(&proto, 0, sizeof(proto));
- if(connssl->alpn) {
- result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
- if(result) {
- failf(data, "Error determining ALPN");
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
-
result = Curl_gtls_ctx_init(&backend->gtls, cf, data, &connssl->peer,
- proto.data, proto.len, connssl, NULL, NULL, cf);
+ connssl->alpn, NULL, NULL, cf,
+ gtls_on_session_reuse);
if(result)
return result;
if(connssl->alpn && (connssl->state != ssl_connection_deferred)) {
+ struct alpn_proto_buf proto;
+ memset(&proto, 0, sizeof(proto));
Curl_alpn_to_proto_str(&proto, connssl->alpn);
infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
@@ -1560,10 +1592,10 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
unsigned char addrbuf[sizeof(struct use_addr)];
size_t addrlen = 0;
- if(Curl_inet_pton(AF_INET, peer->hostname, addrbuf) > 0)
+ if(curlx_inet_pton(AF_INET, peer->hostname, addrbuf) > 0)
addrlen = 4;
#ifdef USE_IPV6
- else if(Curl_inet_pton(AF_INET6, peer->hostname, addrbuf) > 0)
+ else if(curlx_inet_pton(AF_INET6, peer->hostname, addrbuf) > 0)
addrlen = 16;
#endif
@@ -1749,30 +1781,6 @@ out:
return result;
}
-static CURLcode gtls_set_earlydata(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const void *buf, size_t blen)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- ssize_t nwritten = 0;
- CURLcode result = CURLE_OK;
-
- DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_use);
- DEBUGASSERT(Curl_bufq_is_empty(&connssl->earlydata));
- if(blen) {
- if(blen > connssl->earlydata_max)
- blen = connssl->earlydata_max;
- nwritten = Curl_bufq_write(&connssl->earlydata, buf, blen, &result);
- CURL_TRC_CF(data, cf, "gtls_set_earlydata(len=%zu) -> %zd",
- blen, nwritten);
- if(nwritten < 0)
- return result;
- }
- connssl->earlydata_state = ssl_earlydata_sending;
- connssl->earlydata_skip = Curl_bufq_len(&connssl->earlydata);
- return CURLE_OK;
-}
-
static CURLcode gtls_send_earlydata(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
@@ -1807,8 +1815,7 @@ static CURLcode gtls_send_earlydata(struct Curl_cfilter *cf,
Curl_bufq_skip(&connssl->earlydata, (size_t)n);
}
/* sent everything there was */
- infof(data, "SSL sending %" FMT_OFF_T " bytes of early data",
- connssl->earlydata_skip);
+ infof(data, "SSL sending %zu bytes of early data", connssl->earlydata_skip);
out:
return result;
}
@@ -1822,17 +1829,22 @@ out:
'ssl_connect_2' (doing handshake with the server), and
'ssl_connect_3' (verifying and getting stats).
*/
-static CURLcode
-gtls_connect_common(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool nonblocking,
- bool *done) {
+static CURLcode gtls_connect_common(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done) {
struct ssl_connect_data *connssl = cf->ctx;
struct gtls_ssl_backend_data *backend =
(struct gtls_ssl_backend_data *)connssl->backend;
CURLcode result = CURLE_OK;
DEBUGASSERT(backend);
+ /* check if the connection has already been established */
+ if(ssl_connection_complete == connssl->state) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ *done = FALSE;
/* Initiate the connection, if not already done */
if(connssl->connecting_state == ssl_connect_1) {
@@ -1843,7 +1855,7 @@ gtls_connect_common(struct Curl_cfilter *cf,
}
if(connssl->connecting_state == ssl_connect_2) {
- if(connssl->earlydata_state == ssl_earlydata_use) {
+ if(connssl->earlydata_state == ssl_earlydata_await) {
goto out;
}
else if(connssl->earlydata_state == ssl_earlydata_sending) {
@@ -1851,13 +1863,11 @@ gtls_connect_common(struct Curl_cfilter *cf,
if(result)
goto out;
connssl->earlydata_state = ssl_earlydata_sent;
- if(!Curl_ssl_cf_is_proxy(cf))
- Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip);
}
DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) ||
(connssl->earlydata_state == ssl_earlydata_sent));
- result = handshake(cf, data, TRUE, nonblocking);
+ result = handshake(cf, data);
if(result)
goto out;
connssl->connecting_state = ssl_connect_3;
@@ -1872,7 +1882,6 @@ gtls_connect_common(struct Curl_cfilter *cf,
goto out;
connssl->state = ssl_connection_complete;
- connssl->connecting_state = ssl_connect_1;
rc = gnutls_alpn_get_selected_protocol(backend->gtls.session, &proto);
if(rc) { /* No ALPN from server */
@@ -1885,79 +1894,43 @@ gtls_connect_common(struct Curl_cfilter *cf,
if(result)
goto out;
- if(connssl->earlydata_state == ssl_earlydata_sent) {
- if(gnutls_session_get_flags(backend->gtls.session) &
- GNUTLS_SFLAGS_EARLY_DATA) {
- connssl->earlydata_state = ssl_earlydata_accepted;
- infof(data, "Server accepted %zu bytes of TLS early data.",
- connssl->earlydata_skip);
- }
- else {
- connssl->earlydata_state = ssl_earlydata_rejected;
- if(!Curl_ssl_cf_is_proxy(cf))
- Curl_pgrsEarlyData(data, -(curl_off_t)connssl->earlydata_skip);
- infof(data, "Server rejected TLS early data.");
- connssl->earlydata_skip = 0;
- }
+ if(connssl->earlydata_state > ssl_earlydata_none) {
+ /* We should be in this state by now */
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sent);
+ connssl->earlydata_state =
+ (gnutls_session_get_flags(backend->gtls.session) &
+ GNUTLS_SFLAGS_EARLY_DATA) ?
+ ssl_earlydata_accepted : ssl_earlydata_rejected;
}
+ connssl->connecting_state = ssl_connect_done;
}
+ if(connssl->connecting_state == ssl_connect_done)
+ DEBUGASSERT(connssl->state == ssl_connection_complete);
+
out:
if(result == CURLE_AGAIN) {
*done = FALSE;
return CURLE_OK;
}
- *done = ((connssl->connecting_state == ssl_connect_1) ||
+ *done = ((connssl->state == ssl_connection_complete) ||
(connssl->state == ssl_connection_deferred));
+ CURL_TRC_CF(data, cf, "gtls_connect_common() -> %d, done=%d", result, *done);
return result;
}
-static CURLcode gtls_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
+static CURLcode gtls_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
{
struct ssl_connect_data *connssl = cf->ctx;
- if(connssl->state == ssl_connection_deferred) {
+ if((connssl->state == ssl_connection_deferred) &&
+ (connssl->earlydata_state == ssl_earlydata_await)) {
/* We refuse to be pushed, we are waiting for someone to send/recv. */
*done = TRUE;
return CURLE_OK;
}
- return gtls_connect_common(cf, data, TRUE, done);
-}
-
-static CURLcode gtls_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- CURLcode result;
- bool done = FALSE;
-
- result = gtls_connect_common(cf, data, FALSE, &done);
- if(result)
- return result;
-
- DEBUGASSERT(done);
-
- return CURLE_OK;
-}
-
-static CURLcode gtls_connect_deferred(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const void *buf,
- size_t blen,
- bool *done)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- CURLcode result = CURLE_OK;
-
- DEBUGASSERT(connssl->state == ssl_connection_deferred);
- *done = FALSE;
- if(connssl->earlydata_state == ssl_earlydata_use) {
- result = gtls_set_earlydata(cf, data, buf, blen);
- if(result)
- return result;
- }
-
- return gtls_connect_common(cf, data, TRUE, done);
+ return gtls_connect_common(cf, data, done);
}
static bool gtls_data_pending(struct Curl_cfilter *cf,
@@ -1987,38 +1960,9 @@ static ssize_t gtls_send(struct Curl_cfilter *cf,
ssize_t rc;
size_t nwritten, total_written = 0;
+ (void)data;
DEBUGASSERT(backend);
- if(connssl->state == ssl_connection_deferred) {
- bool done = FALSE;
- *curlcode = gtls_connect_deferred(cf, data, buf, blen, &done);
- if(*curlcode) {
- rc = -1;
- goto out;
- }
- else if(!done) {
- *curlcode = CURLE_AGAIN;
- rc = -1;
- goto out;
- }
- DEBUGASSERT(connssl->state == ssl_connection_complete);
- }
-
- if(connssl->earlydata_skip) {
- if(connssl->earlydata_skip >= blen) {
- connssl->earlydata_skip -= blen;
- *curlcode = CURLE_OK;
- rc = (ssize_t)blen;
- goto out;
- }
- else {
- total_written += connssl->earlydata_skip;
- buf = ((const char *)buf) + connssl->earlydata_skip;
- blen -= connssl->earlydata_skip;
- connssl->earlydata_skip = 0;
- }
- }
-
while(blen) {
backend->gtls.io_result = CURLE_OK;
rc = gnutls_record_send(backend->gtls.session, buf, blen);
@@ -2039,7 +1983,7 @@ static ssize_t gtls_send(struct Curl_cfilter *cf,
nwritten = (size_t)rc;
total_written += nwritten;
DEBUGASSERT(nwritten <= blen);
- buf = (char *)buf + nwritten;
+ buf = (char *)CURL_UNCONST(buf) + nwritten;
blen -= nwritten;
}
rc = total_written;
@@ -2165,21 +2109,6 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf,
(void)data;
DEBUGASSERT(backend);
- if(connssl->state == ssl_connection_deferred) {
- bool done = FALSE;
- *curlcode = gtls_connect_deferred(cf, data, NULL, 0, &done);
- if(*curlcode) {
- ret = -1;
- goto out;
- }
- else if(!done) {
- *curlcode = CURLE_AGAIN;
- ret = -1;
- goto out;
- }
- DEBUGASSERT(connssl->state == ssl_connection_complete);
- }
-
ret = gnutls_record_recv(backend->gtls.session, buf, buffersize);
if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
*curlcode = CURLE_AGAIN;
@@ -2190,9 +2119,8 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf,
if(ret == GNUTLS_E_REHANDSHAKE) {
/* BLOCKING call, this is bad but a work-around for now. Fixing this "the
proper way" takes a whole lot of work. */
- CURLcode result = handshake(cf, data, FALSE, FALSE);
+ CURLcode result = handshake(cf, data);
if(result)
- /* handshake() writes error message on its own */
*curlcode = result;
else
*curlcode = CURLE_AGAIN; /* then return as if this was a wouldblock */
@@ -2213,7 +2141,7 @@ out:
return ret;
}
-static size_t gtls_version(char *buffer, size_t size)
+size_t Curl_gtls_version(char *buffer, size_t size)
{
return msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL));
}
@@ -2262,31 +2190,28 @@ const struct Curl_ssl Curl_ssl_gnutls = {
SSLSUPP_CERTINFO |
SSLSUPP_PINNEDPUBKEY |
SSLSUPP_HTTPS_PROXY |
+ SSLSUPP_CIPHER_LIST |
SSLSUPP_CA_CACHE,
sizeof(struct gtls_ssl_backend_data),
gtls_init, /* init */
gtls_cleanup, /* cleanup */
- gtls_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
+ Curl_gtls_version, /* version */
gtls_shutdown, /* shutdown */
gtls_data_pending, /* data_pending */
gtls_random, /* random */
gtls_cert_status_request, /* cert_status_request */
gtls_connect, /* connect */
- gtls_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
gtls_get_internals, /* get_internals */
gtls_close, /* close_one */
- Curl_none_close_all, /* close_all */
- 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, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
gtls_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
gtls_recv, /* recv decrypted data */
gtls_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/gtls.h b/libs/libcurl/src/vtls/gtls.h
index 4f3c089bc6..dc8de777af 100644
--- a/libs/libcurl/src/vtls/gtls.h
+++ b/libs/libcurl/src/vtls/gtls.h
@@ -24,13 +24,13 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#include <curl/curl.h>
#ifdef USE_GNUTLS
#include <gnutls/gnutls.h>
-#include "timeval.h"
+#include "../curlx/timeval.h"
#ifdef HAVE_GNUTLS_SRP
/* the function exists */
@@ -42,10 +42,14 @@
struct Curl_easy;
struct Curl_cfilter;
+struct alpn_spec;
struct ssl_primary_config;
struct ssl_config_data;
struct ssl_peer;
struct ssl_connect_data;
+struct Curl_ssl_session;
+
+int Curl_glts_get_ietf_proto(gnutls_session_t session);
struct gtls_shared_creds {
gnutls_certificate_credentials_t creds;
@@ -70,19 +74,27 @@ struct gtls_ctx {
BIT(sent_shutdown);
};
+size_t Curl_gtls_version(char *buffer, size_t size);
+
typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf,
struct Curl_easy *data,
void *user_data);
+typedef CURLcode Curl_gtls_init_session_reuse_cb(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct alpn_spec *alpns,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data);
+
CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- const unsigned char *alpn, size_t alpn_len,
- struct ssl_connect_data *connssl,
+ const struct alpn_spec *alpns,
Curl_gtls_ctx_setup_cb *cb_setup,
void *cb_user_data,
- void *ssl_user_data);
+ void *ssl_user_data,
+ Curl_gtls_init_session_reuse_cb *sess_reuse_cb);
CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf,
struct Curl_easy *data,
@@ -96,11 +108,14 @@ CURLcode Curl_gtls_verifyserver(struct Curl_easy *data,
const char *pinned_key);
/* Extract TLS session and place in cache, if configured. */
-CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- gnutls_session_t session,
- struct ssl_peer *peer,
- const char *alpn);
+CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ gnutls_session_t session,
+ curl_off_t valid_until,
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len);
extern const struct Curl_ssl Curl_ssl_gnutls;
diff --git a/libs/libcurl/src/vtls/hostcheck.c b/libs/libcurl/src/vtls/hostcheck.c
index 79ed869e87..482725cb87 100644
--- a/libs/libcurl/src/vtls/hostcheck.c
+++ b/libs/libcurl/src/vtls/hostcheck.c
@@ -22,7 +22,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_OPENSSL) \
|| defined(USE_SCHANNEL)
@@ -34,15 +34,14 @@
#ifdef HAVE_NETINET_IN6_H
#include <netinet/in6.h>
#endif
-#include "curl_memrchr.h"
-
+#include "../curl_memrchr.h"
#include "hostcheck.h"
-#include "strcase.h"
-#include "hostip.h"
+#include "../strcase.h"
+#include "../hostip.h"
-#include "curl_memory.h"
+#include "../curl_memory.h"
/* The last #include file should be: */
-#include "memdebug.h"
+#include "../memdebug.h"
/* check the two input strings with given length, but do not
assume they end in nul-bytes */
diff --git a/libs/libcurl/src/vtls/keylog.c b/libs/libcurl/src/vtls/keylog.c
index e403114934..3e6bdff54e 100644
--- a/libs/libcurl/src/vtls/keylog.c
+++ b/libs/libcurl/src/vtls/keylog.c
@@ -21,32 +21,22 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_OPENSSL) || \
defined(USE_GNUTLS) || \
defined(USE_WOLFSSL) || \
(defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \
- defined(USE_QUICHE)
+ defined(USE_QUICHE) || \
+ defined(USE_RUSTLS)
#include "keylog.h"
#include <curl/curl.h>
+#include "../escape.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
-
-#define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1)
-
-#define CLIENT_RANDOM_SIZE 32
-
-/*
- * The master secret in TLS 1.2 and before is always 48 bytes. In TLS 1.3, the
- * secret size depends on the cipher suite's hash function which is 32 bytes
- * for SHA-256 and 48 bytes for SHA-384.
- */
-#define SECRET_MAXLEN 48
-
+#include "../curl_memory.h"
+#include "../memdebug.h"
/* The fp for the open SSLKEYLOGFILE, or NULL if not open */
static FILE *keylog_file_fp;
@@ -104,7 +94,7 @@ Curl_tls_keylog_write_line(const char *line)
linelen = strlen(line);
if(linelen == 0 || linelen > sizeof(buf) - 2) {
- /* Empty line or too big to fit in a LF and NUL. */
+ /* Empty line or too big to fit in an LF and NUL. */
return FALSE;
}
@@ -125,10 +115,9 @@ Curl_tls_keylog_write(const char *label,
const unsigned char client_random[CLIENT_RANDOM_SIZE],
const unsigned char *secret, size_t secretlen)
{
- const char *hex = "0123456789ABCDEF";
size_t pos, i;
- char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 +
- 2 * SECRET_MAXLEN + 1 + 1];
+ unsigned char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 +
+ 2 * SECRET_MAXLEN + 1 + 1];
if(!keylog_file_fp) {
return FALSE;
@@ -145,22 +134,22 @@ Curl_tls_keylog_write(const char *label,
/* Client Random */
for(i = 0; i < CLIENT_RANDOM_SIZE; i++) {
- line[pos++] = hex[client_random[i] >> 4];
- line[pos++] = hex[client_random[i] & 0xF];
+ Curl_hexbyte(&line[pos], client_random[i], FALSE);
+ pos += 2;
}
line[pos++] = ' ';
/* Secret */
for(i = 0; i < secretlen; i++) {
- line[pos++] = hex[secret[i] >> 4];
- line[pos++] = hex[secret[i] & 0xF];
+ Curl_hexbyte(&line[pos], secret[i], FALSE);
+ pos += 2;
}
line[pos++] = '\n';
line[pos] = '\0';
/* Using fputs here instead of fprintf since libcurl's fprintf replacement
may not be thread-safe. */
- fputs(line, keylog_file_fp);
+ fputs((char *)line, keylog_file_fp);
return TRUE;
}
diff --git a/libs/libcurl/src/vtls/keylog.h b/libs/libcurl/src/vtls/keylog.h
index 3a48017f81..88151dea2d 100644
--- a/libs/libcurl/src/vtls/keylog.h
+++ b/libs/libcurl/src/vtls/keylog.h
@@ -23,7 +23,18 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
+
+#define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1)
+
+#define CLIENT_RANDOM_SIZE 32
+
+/*
+ * The master secret in TLS 1.2 and before is always 48 bytes. In TLS 1.3, the
+ * secret size depends on the cipher suite's hash function which is 32 bytes
+ * for SHA-256 and 48 bytes for SHA-384.
+ */
+#define SECRET_MAXLEN 48
/*
* Opens the TLS key log file if requested by the user. The SSLKEYLOGFILE
@@ -50,7 +61,7 @@ bool Curl_tls_keylog_write(const char *label,
const unsigned char *secret, size_t secretlen);
/*
- * Appends a line to the key log file, ensure it is terminated by a LF.
+ * Appends a line to the key log file, ensure it is terminated by an LF.
* Returns true iff the key log file is open and a valid line was provided.
*/
bool Curl_tls_keylog_write_line(const char *line);
diff --git a/libs/libcurl/src/vtls/mbedtls.c b/libs/libcurl/src/vtls/mbedtls.c
index 20226b74bd..0404c83407 100644
--- a/libs/libcurl/src/vtls/mbedtls.c
+++ b/libs/libcurl/src/vtls/mbedtls.c
@@ -29,7 +29,7 @@
*
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_MBEDTLS
@@ -54,35 +54,33 @@
# ifdef MBEDTLS_DEBUG
# include <mbedtls/debug.h>
# endif
-#endif
+#endif /* MBEDTLS_VERSION_MAJOR >= 2 */
#include "cipher_suite.h"
-#include "strcase.h"
-#include "urldata.h"
-#include "sendf.h"
-#include "inet_pton.h"
+#include "../strcase.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
#include "mbedtls.h"
#include "vtls.h"
#include "vtls_int.h"
+#include "vtls_scache.h"
#include "x509asn1.h"
-#include "parsedate.h"
-#include "connect.h" /* for the connect timeout */
-#include "select.h"
-#include "multiif.h"
+#include "../parsedate.h"
+#include "../connect.h" /* for the connect timeout */
+#include "../select.h"
+#include "../multiif.h"
#include "mbedtls_threadlock.h"
-#include "strdup.h"
+#include "../strdup.h"
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/* ALPN for http2 */
-#ifdef USE_HTTP2
-# undef HAS_ALPN
-# ifdef MBEDTLS_SSL_ALPN
-# define HAS_ALPN
-# endif
+#if defined(USE_HTTP2) && defined(MBEDTLS_SSL_ALPN)
+# define HAS_ALPN_MBEDTLS
#endif
struct mbed_ssl_backend_data {
@@ -96,33 +94,40 @@ struct mbed_ssl_backend_data {
#endif
mbedtls_pk_context pk;
mbedtls_ssl_config config;
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_MBEDTLS
const char *protocols[3];
#endif
int *ciphersuites;
+ size_t send_blocked_len;
BIT(initialized); /* mbedtls_ssl_context is initialized */
BIT(sent_shutdown);
+ BIT(send_blocked);
};
/* apply threading? */
-#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \
- defined(_WIN32)
-#define THREADING_SUPPORT
+#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \
+ defined(_WIN32)
+#define HAS_THREADING_SUPPORT
#endif
#ifndef MBEDTLS_ERROR_C
#define mbedtls_strerror(a,b,c) b[0] = 0
#endif
+/* PSA can be used independently of TLS 1.3 */
+#if defined(MBEDTLS_USE_PSA_CRYPTO) && MBEDTLS_VERSION_NUMBER >= 0x03060000
+#define HAS_PSA_SUPPORT
+#endif
+
#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && MBEDTLS_VERSION_NUMBER >= 0x03060000
-#define TLS13_SUPPORT
+#define HAS_TLS13_SUPPORT
#endif
-#if defined(TLS13_SUPPORT) && defined(MBEDTLS_SSL_SESSION_TICKETS)
+#if defined(HAS_TLS13_SUPPORT) && defined(MBEDTLS_SSL_SESSION_TICKETS)
#define HAS_SESSION_TICKETS
#endif
-#if defined(THREADING_SUPPORT)
+#ifdef HAS_THREADING_SUPPORT
static mbedtls_entropy_context ts_entropy;
static int entropy_init_initialized = 0;
@@ -160,7 +165,7 @@ static int entropy_func_mutex(void *data, unsigned char *output, size_t len)
return ret;
}
-#endif /* THREADING_SUPPORT */
+#endif /* HAS_THREADING_SUPPORT */
#ifdef MBEDTLS_DEBUG
static void mbed_debug(void *context, int level, const char *f_name,
@@ -193,7 +198,7 @@ static int mbedtls_bio_cf_write(void *bio,
if(!data)
return 0;
- nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, FALSE,
+ nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, blen, FALSE,
&result);
CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %zd, err=%d",
blen, nwritten, result);
@@ -268,7 +273,13 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data,
#else
/* mbedTLS 3.2.0 (2022) introduced new methods for setting TLS version */
mbedtls_ssl_protocol_version ver_min = MBEDTLS_SSL_VERSION_TLS1_2;
- mbedtls_ssl_protocol_version ver_max = MBEDTLS_SSL_VERSION_TLS1_2;
+ mbedtls_ssl_protocol_version ver_max =
+#ifdef HAS_TLS13_SUPPORT
+ MBEDTLS_SSL_VERSION_TLS1_3
+#else
+ MBEDTLS_SSL_VERSION_TLS1_2
+#endif
+ ;
#endif
switch(conn_config->version) {
@@ -287,10 +298,14 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data,
case CURL_SSLVERSION_TLSv1_1:
#endif
case CURL_SSLVERSION_TLSv1_2:
- /* ver_min = MBEDTLS_SSL_VERSION_TLS1_2; */
+#if MBEDTLS_VERSION_NUMBER < 0x03020000
+ ver_min = MBEDTLS_SSL_MINOR_VERSION_3; /* TLS 1.2 */
+#else
+ ver_min = MBEDTLS_SSL_VERSION_TLS1_2;
+#endif
break;
case CURL_SSLVERSION_TLSv1_3:
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
ver_min = MBEDTLS_SSL_VERSION_TLS1_3;
break;
#endif
@@ -304,12 +319,16 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data,
case CURL_SSLVERSION_MAX_DEFAULT:
case CURL_SSLVERSION_MAX_NONE:
case CURL_SSLVERSION_MAX_TLSv1_3:
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
ver_max = MBEDTLS_SSL_VERSION_TLS1_3;
break;
#endif
case CURL_SSLVERSION_MAX_TLSv1_2:
- /* ver_max = MBEDTLS_SSL_VERSION_TLS1_2; */
+#if MBEDTLS_VERSION_NUMBER < 0x03020000
+ ver_max = MBEDTLS_SSL_MINOR_VERSION_3; /* TLS 1.2 */
+#else
+ ver_max = MBEDTLS_SSL_VERSION_TLS1_2;
+#endif
break;
#if MBEDTLS_VERSION_NUMBER < 0x03000000
case CURL_SSLVERSION_MAX_TLSv1_1:
@@ -395,7 +414,7 @@ mbed_set_selected_ciphers(struct Curl_easy *data,
if(!selected)
return CURLE_OUT_OF_MEMORY;
-#ifndef TLS13_SUPPORT
+#ifndef HAS_TLS13_SUPPORT
(void) ciphers13, (void) j;
#else
if(!ciphers13) {
@@ -443,7 +462,7 @@ add_ciphers:
selected[count++] = id;
}
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
if(ciphers == ciphers13 && ciphers12) {
ciphers = ciphers12;
goto add_ciphers;
@@ -585,17 +604,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_NOT_BUILT_IN;
}
-#ifdef TLS13_SUPPORT
- ret = psa_crypto_init();
- if(ret != PSA_SUCCESS) {
- mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
- failf(data, "mbedTLS psa_crypto_init returned (-0x%04X) %s",
- -ret, errorbuf);
- return CURLE_SSL_CONNECT_ERROR;
- }
-#endif /* TLS13_SUPPORT */
-
-#ifdef THREADING_SUPPORT
+#ifdef HAS_THREADING_SUPPORT
mbedtls_ctr_drbg_init(&backend->ctr_drbg);
ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, entropy_func_mutex,
@@ -618,7 +627,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
-ret, errorbuf);
return CURLE_FAILED_INIT;
}
-#endif /* THREADING_SUPPORT */
+#endif /* HAS_THREADING_SUPPORT */
/* Load the trusted CA */
mbedtls_x509_crt_init(&backend->cacert);
@@ -731,6 +740,9 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key,
ssl_config->key_passwd);
#endif
+ if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) ||
+ mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY)))
+ ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
if(ret) {
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
@@ -759,6 +771,9 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
(const unsigned char *)passwd,
passwd ? strlen(passwd) : 0);
#endif
+ if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) ||
+ mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY)))
+ ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
if(ret) {
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
@@ -767,10 +782,6 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_SSL_CERTPROBLEM;
}
}
-
- if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) ||
- mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY)))
- ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
}
/* Load the CRL */
@@ -812,7 +823,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_SSL_CONNECT_ERROR;
}
-#ifdef MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED
+#if defined(HAS_SESSION_TICKETS) && MBEDTLS_VERSION_NUMBER >= 0x03060100
/* New in mbedTLS 3.6.1, need to enable, default is now disabled */
mbedtls_ssl_conf_tls13_enable_signal_new_session_tickets(&backend->config,
MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED);
@@ -851,7 +862,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
mbedtls_bio_cf_read,
NULL /* rev_timeout() */);
-#ifndef TLS13_SUPPORT
+#ifndef HAS_TLS13_SUPPORT
if(conn_config->cipher_list) {
CURLcode result = mbed_set_selected_ciphers(data, backend,
conn_config->cipher_list,
@@ -885,29 +896,30 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
/* Check if there is a cached ID we can/should use here! */
if(ssl_config->primary.cache_session) {
- void *sdata = NULL;
- size_t slen = 0;
+ struct Curl_ssl_session *sc_session = NULL;
+ CURLcode result;
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
- &sdata, &slen, NULL) && slen) {
+ result = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key,
+ &sc_session);
+ if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) {
mbedtls_ssl_session session;
mbedtls_ssl_session_init(&session);
- ret = mbedtls_ssl_session_load(&session, sdata, slen);
+ ret = mbedtls_ssl_session_load(&session, sc_session->sdata,
+ sc_session->sdata_len);
if(ret) {
- failf(data, "error loading cached session: -0x%x", -ret);
+ failf(data, "SSL session error loading: -0x%x", -ret);
}
else {
ret = mbedtls_ssl_set_session(&backend->ssl, &session);
if(ret)
- failf(data, "error setting session: -0x%x", -ret);
+ failf(data, "SSL session error setting: -0x%x", -ret);
else
infof(data, "SSL reusing session ID");
}
mbedtls_ssl_session_free(&session);
}
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session);
}
mbedtls_ssl_conf_ca_chain(&backend->config,
@@ -932,7 +944,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_SSL_CONNECT_ERROR;
}
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_MBEDTLS
if(connssl->alpn) {
struct alpn_proto_buf proto;
size_t i;
@@ -1110,7 +1122,7 @@ pinnedpubkey_error:
}
}
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_MBEDTLS
if(connssl->alpn) {
const char *proto = mbedtls_ssl_get_alpn_protocol(&backend->ssl);
@@ -1125,12 +1137,6 @@ pinnedpubkey_error:
return CURLE_OK;
}
-static void mbedtls_session_free(void *session, size_t slen)
-{
- (void)slen;
- free(session);
-}
-
static CURLcode
mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data)
{
@@ -1138,48 +1144,64 @@ mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data)
struct mbed_ssl_backend_data *backend =
(struct mbed_ssl_backend_data *)connssl->backend;
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ mbedtls_ssl_session session;
+ bool msession_alloced = FALSE;
+ struct Curl_ssl_session *sc_session = NULL;
+ unsigned char *sdata = NULL;
+ size_t slen = 0;
+ int ietf_tls_id;
CURLcode result = CURLE_OK;
+ int ret;
DEBUGASSERT(backend);
- if(ssl_config->primary.cache_session) {
- int ret;
- mbedtls_ssl_session session;
- unsigned char *sdata = NULL;
- size_t slen = 0;
+ if(!ssl_config->primary.cache_session)
+ return CURLE_OK;
- mbedtls_ssl_session_init(&session);
- ret = mbedtls_ssl_get_session(&backend->ssl, &session);
- if(ret) {
- if(ret != MBEDTLS_ERR_SSL_ALLOC_FAILED)
- mbedtls_ssl_session_free(&session);
- failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret);
- return CURLE_SSL_CONNECT_ERROR;
- }
+ mbedtls_ssl_session_init(&session);
+ ret = mbedtls_ssl_get_session(&backend->ssl, &session);
+ msession_alloced = (ret != MBEDTLS_ERR_SSL_ALLOC_FAILED);
+ if(ret) {
+ failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret);
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
+ }
- mbedtls_ssl_session_save(&session, NULL, 0, &slen);
- if(!slen) {
- failf(data, "failed to serialize session: length is 0");
- }
- else {
- sdata = malloc(slen);
- if(sdata) {
- ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen);
- if(ret) {
- failf(data, "failed to serialize session: -0x%x", -ret);
- }
- else {
- Curl_ssl_sessionid_lock(data);
- result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
- sdata, slen, mbedtls_session_free);
- Curl_ssl_sessionid_unlock(data);
- if(!result)
- sdata = NULL;
- }
- }
- }
- mbedtls_ssl_session_free(&session);
- free(sdata);
+ mbedtls_ssl_session_save(&session, NULL, 0, &slen);
+ if(!slen) {
+ failf(data, "failed to serialize session: length is 0");
+ goto out;
}
+
+ sdata = malloc(slen);
+ if(!sdata) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen);
+ if(ret) {
+ failf(data, "failed to serialize session: -0x%x", -ret);
+ goto out;
+ }
+
+#if MBEDTLS_VERSION_NUMBER >= 0x03020000
+ ietf_tls_id = mbedtls_ssl_get_version_number(&backend->ssl);
+#else
+ ietf_tls_id = CURL_IETF_PROTO_UNKNOWN;
+#endif
+ result = Curl_ssl_session_create(sdata, slen,
+ ietf_tls_id,
+ connssl->negotiated.alpn, 0, 0,
+ &sc_session);
+ sdata = NULL; /* call took ownership */
+ if(!result)
+ result = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key,
+ sc_session);
+
+out:
+ if(msession_alloced)
+ mbedtls_ssl_session_free(&session);
+ free(sdata);
return result;
}
@@ -1194,27 +1216,41 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data,
(void)data;
DEBUGASSERT(backend);
- ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len);
+ /* mbedtls is picky when a mbedtls_ssl_write) was previously blocked.
+ * It requires to be called with the same amount of bytes again, or it
+ * will lose bytes, e.g. reporting all was sent but they were not.
+ * Remember the blocked length and use that when set. */
+ if(backend->send_blocked) {
+ DEBUGASSERT(backend->send_blocked_len <= len);
+ CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> previously blocked "
+ "on %zu bytes", len, backend->send_blocked_len);
+ len = backend->send_blocked_len;
+ }
+
+ ret = mbedtls_ssl_write(&backend->ssl, (const unsigned char *)mem, len);
if(ret < 0) {
CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> -0x%04X",
len, -ret);
*curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_WRITE)
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
|| (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
#endif
) ? CURLE_AGAIN : CURLE_SEND_ERROR;
ret = -1;
+ if((*curlcode == CURLE_AGAIN) && !backend->send_blocked) {
+ backend->send_blocked = TRUE;
+ backend->send_blocked_len = len;
+ }
+ }
+ else {
+ CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> %d", len, ret);
+ backend->send_blocked = FALSE;
}
return ret;
}
-static void mbedtls_close_all(struct Curl_easy *data)
-{
- (void)data;
-}
-
static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool send_shutdown, bool *done)
@@ -1270,7 +1306,7 @@ static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf,
* WANT_READ, but has not encountered an EAGAIN. */
if(ret == MBEDTLS_ERR_SSL_WANT_READ)
ret = mbedtls_ssl_read(&backend->ssl, buf, sizeof(buf));
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
if(ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
continue;
#endif
@@ -1324,9 +1360,9 @@ static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data)
mbedtls_ssl_config_free(&backend->config);
mbedtls_ssl_free(&backend->ssl);
mbedtls_ctr_drbg_free(&backend->ctr_drbg);
-#ifndef THREADING_SUPPORT
+#ifndef HAS_THREADING_SUPPORT
mbedtls_entropy_free(&backend->entropy);
-#endif /* THREADING_SUPPORT */
+#endif /* HAS_THREADING_SUPPORT */
backend->initialized = FALSE;
}
}
@@ -1420,16 +1456,12 @@ static CURLcode mbedtls_random(struct Curl_easy *data,
#endif
}
-static CURLcode
-mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
- bool nonblocking,
- bool *done)
+static CURLcode mbedtls_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
{
CURLcode retcode;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
- timediff_t timeout_ms;
- int what;
/* check if the connection has already been established */
if(ssl_connection_complete == connssl->state) {
@@ -1437,73 +1469,20 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
return CURLE_OK;
}
- if(ssl_connect_1 == connssl->connecting_state) {
- /* Find out how much more time we are allowed */
- timeout_ms = Curl_timeleft(data, NULL, TRUE);
+ *done = FALSE;
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
+ if(ssl_connect_1 == connssl->connecting_state) {
retcode = mbed_connect_step1(cf, data);
if(retcode)
return retcode;
}
- while(ssl_connect_2 == 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 is available. */
- if(connssl->io_need) {
- curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
- sockfd : CURL_SOCKET_BAD;
- curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
- 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.
- */
- connssl->io_need = CURL_SSL_IO_NEED_NONE;
+ if(ssl_connect_2 == connssl->connecting_state) {
retcode = mbed_connect_step2(cf, data);
- if(retcode ||
- (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
+ if(retcode)
return retcode;
-
- } /* repeat step2 until all transactions are done. */
+ }
if(ssl_connect_3 == connssl->connecting_state) {
/* For tls1.3 we get notified about new sessions */
@@ -1528,34 +1507,6 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
connssl->state = ssl_connection_complete;
*done = TRUE;
}
- else
- *done = FALSE;
-
- /* Reset our connect state machine */
- connssl->connecting_state = ssl_connect_1;
-
- return CURLE_OK;
-}
-
-static CURLcode mbedtls_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
-{
- return mbed_connect_common(cf, data, TRUE, done);
-}
-
-
-static CURLcode mbedtls_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- CURLcode retcode;
- bool done = FALSE;
-
- retcode = mbed_connect_common(cf, data, FALSE, &done);
- if(retcode)
- return retcode;
-
- DEBUGASSERT(done);
return CURLE_OK;
}
@@ -1568,15 +1519,29 @@ static int mbedtls_init(void)
{
if(!Curl_mbedtlsthreadlock_thread_setup())
return 0;
-#ifdef THREADING_SUPPORT
+#ifdef HAS_THREADING_SUPPORT
entropy_init_mutex(&ts_entropy);
#endif
+#ifdef HAS_PSA_SUPPORT
+ {
+ int ret;
+#ifdef HAS_THREADING_SUPPORT
+ Curl_mbedtlsthreadlock_lock_function(0);
+#endif
+ ret = psa_crypto_init();
+#ifdef HAS_THREADING_SUPPORT
+ Curl_mbedtlsthreadlock_unlock_function(0);
+#endif
+ if(ret != PSA_SUCCESS)
+ return 0;
+ }
+#endif /* HAS_PSA_SUPPORT */
return 1;
}
static void mbedtls_cleanup(void)
{
-#ifdef THREADING_SUPPORT
+#ifdef HAS_THREADING_SUPPORT
entropy_cleanup_mutex(&ts_entropy);
#endif
(void)Curl_mbedtlsthreadlock_thread_cleanup();
@@ -1599,7 +1564,6 @@ static CURLcode mbedtls_sha256sum(const unsigned char *input,
unsigned char *sha256sum,
size_t sha256len UNUSED_PARAM)
{
- /* TODO: explain this for different mbedtls 2.x vs 3 version */
(void)sha256len;
#if MBEDTLS_VERSION_NUMBER < 0x02070000
mbedtls_sha256(input, inputlen, sha256sum, 0);
@@ -1633,7 +1597,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
SSLSUPP_CERTINFO |
SSLSUPP_PINNEDPUBKEY |
SSLSUPP_SSL_CTX |
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
SSLSUPP_TLS13_CIPHERSUITES |
#endif
SSLSUPP_HTTPS_PROXY |
@@ -1644,24 +1608,20 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
mbedtls_init, /* init */
mbedtls_cleanup, /* cleanup */
mbedtls_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
mbedtls_shutdown, /* shutdown */
mbedtls_data_pending, /* data_pending */
mbedtls_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
mbedtls_connect, /* connect */
- mbedtls_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
mbedtls_get_internals, /* get_internals */
mbedtls_close, /* close_one */
- mbedtls_close_all, /* close_all */
- 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, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
mbedtls_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
mbed_recv, /* recv decrypted data */
mbed_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/mbedtls.h b/libs/libcurl/src/vtls/mbedtls.h
index 91ede31e02..33ba492336 100644
--- a/libs/libcurl/src/vtls/mbedtls.h
+++ b/libs/libcurl/src/vtls/mbedtls.h
@@ -24,7 +24,7 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_MBEDTLS
diff --git a/libs/libcurl/src/vtls/mbedtls_threadlock.c b/libs/libcurl/src/vtls/mbedtls_threadlock.c
index d6d20328a7..ed8531f9f1 100644
--- a/libs/libcurl/src/vtls/mbedtls_threadlock.c
+++ b/libs/libcurl/src/vtls/mbedtls_threadlock.c
@@ -22,7 +22,7 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_MBEDTLS) && \
((defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \
@@ -36,10 +36,10 @@
#endif
#include "mbedtls_threadlock.h"
-#include "curl_printf.h"
-#include "curl_memory.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
/* The last #include file should be: */
-#include "memdebug.h"
+#include "../memdebug.h"
/* number of thread locks */
#define NUMT 2
diff --git a/libs/libcurl/src/vtls/mbedtls_threadlock.h b/libs/libcurl/src/vtls/mbedtls_threadlock.h
index ae651c3866..e99b051cc0 100644
--- a/libs/libcurl/src/vtls/mbedtls_threadlock.h
+++ b/libs/libcurl/src/vtls/mbedtls_threadlock.h
@@ -24,7 +24,7 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_MBEDTLS
diff --git a/libs/libcurl/src/vtls/openssl.c b/libs/libcurl/src/vtls/openssl.c
index 86931089b1..2556b5c57f 100644
--- a/libs/libcurl/src/vtls/openssl.c
+++ b/libs/libcurl/src/vtls/openssl.c
@@ -27,14 +27,14 @@
* but vtls.c should ever call or use these functions.
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_QUICHE) || defined(USE_OPENSSL)
#include <limits.h>
/* Wincrypt must be included before anything that could include OpenSSL. */
-#if defined(USE_WIN32_CRYPTO)
+#ifdef USE_WIN32_CRYPTO
#include <wincrypt.h>
/* Undefine wincrypt conflicting symbols for BoringSSL. */
#undef X509_NAME
@@ -45,24 +45,27 @@
#undef OCSP_RESPONSE
#endif
-#include "urldata.h"
-#include "sendf.h"
-#include "formdata.h" /* for the boundary function */
-#include "url.h" /* for the ssl config check function */
-#include "inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../formdata.h" /* for the boundary function */
+#include "../url.h" /* for the ssl config check function */
+#include "../curlx/inet_pton.h"
#include "openssl.h"
-#include "connect.h"
-#include "slist.h"
-#include "select.h"
+#include "../connect.h"
+#include "../slist.h"
+#include "../select.h"
#include "vtls.h"
#include "vtls_int.h"
-#include "vauth/vauth.h"
+#include "vtls_scache.h"
+#include "../vauth/vauth.h"
#include "keylog.h"
-#include "strcase.h"
+#include "../strcase.h"
#include "hostcheck.h"
-#include "multiif.h"
-#include "strerror.h"
-#include "curl_printf.h"
+#include "../multiif.h"
+#include "../curlx/strparse.h"
+#include "../strdup.h"
+#include "../strerror.h"
+#include "../curl_printf.h"
#include <openssl/ssl.h>
#include <openssl/rand.h>
@@ -82,37 +85,49 @@
#include <openssl/tls1.h>
#include <openssl/evp.h>
-#ifdef USE_ECH
-# ifndef OPENSSL_IS_BORINGSSL
+#ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST
+#define USE_ECH_OPENSSL
+#endif
+
+#ifdef USE_ECH_OPENSSL
+# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
# include <openssl/ech.h>
# endif
-# include "curl_base64.h"
-# define ECH_ENABLED(__data__) \
- (__data__->set.tls_ech && \
- !(__data__->set.tls_ech & CURLECH_DISABLE)\
- )
-#endif /* USE_ECH */
-
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP)
+#endif /* USE_ECH_OPENSSL */
+
+#ifndef OPENSSL_NO_OCSP
#include <openssl/ocsp.h>
#endif
-#if (OPENSSL_VERSION_NUMBER >= 0x0090700fL) && /* 0.9.7 or later */ \
- !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_UI_CONSOLE)
+#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_UI_CONSOLE)
#define USE_OPENSSL_ENGINE
#include <openssl/engine.h>
#endif
-#include "warnless.h"
+#ifdef LIBRESSL_VERSION_NUMBER
+# /* As of LibreSSL 2.0.0-4.0.0: OPENSSL_VERSION_NUMBER == 0x20000000L */
+# if LIBRESSL_VERSION_NUMBER < 0x2090100fL /* 2019-04-13 */
+# error "LibreSSL 2.9.1 or later required"
+# endif
+#elif OPENSSL_VERSION_NUMBER < 0x1000201fL /* 2015-03-19 */
+# error "OpenSSL 1.0.2a or later required"
+#endif
-/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#if OPENSSL_VERSION_NUMBER >= 0x3000000fL && !defined(OPENSSL_NO_UI_CONSOLE)
+#include <openssl/provider.h>
+#include <openssl/store.h>
+/* this is used in the following conditions to make them easier to read */
+#define OPENSSL_HAS_PROVIDERS
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+static void ossl_provider_cleanup(struct Curl_easy *data);
#endif
+#include "../curlx/warnless.h"
+
+/* The last #include files should be: */
+#include "../curl_memory.h"
+#include "../memdebug.h"
+
/* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS
renegotiations when built with BoringSSL. Renegotiating is non-compliant
with HTTP/2 and "an extremely dangerous protocol feature". Beware.
@@ -124,57 +139,28 @@
#error "OPENSSL_VERSION_NUMBER not defined"
#endif
-#ifdef USE_OPENSSL_ENGINE
+#if defined(USE_OPENSSL_ENGINE) || defined(OPENSSL_HAS_PROVIDERS)
#include <openssl/ui.h>
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x00909000L
-#define SSL_METHOD_QUAL const
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+#define OSSL_UI_METHOD_CAST(x) (x)
#else
-#define SSL_METHOD_QUAL
+#define OSSL_UI_METHOD_CAST(x) CURL_UNCONST(x)
#endif
-#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
-#define HAVE_ERR_REMOVE_THREAD_STATE 1
-#endif
-
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && /* OpenSSL 1.1.0+ */ \
- !(defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER < 0x20700000L)
-#define SSLEAY_VERSION_NUMBER OPENSSL_VERSION_NUMBER
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* OpenSSL 1.1.0+ and LibreSSL */
#define HAVE_X509_GET0_EXTENSIONS 1 /* added in 1.1.0 -pre1 */
#define HAVE_OPAQUE_EVP_PKEY 1 /* since 1.1.0 -pre3 */
#define HAVE_OPAQUE_RSA_DSA_DH 1 /* since 1.1.0 -pre5 */
-#define CONST_EXTS const
#define HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED 1
-
-/* funny typecast define due to difference in API */
-#ifdef LIBRESSL_VERSION_NUMBER
-#define ARG2_X509_signature_print (X509_ALGOR *)
-#else
-#define ARG2_X509_signature_print
-#endif
-
#else
/* For OpenSSL before 1.1.0 */
#define ASN1_STRING_get0_data(x) ASN1_STRING_data(x)
#define X509_get0_notBefore(x) X509_get_notBefore(x)
#define X509_get0_notAfter(x) X509_get_notAfter(x)
-#define CONST_EXTS /* nope */
-#ifndef LIBRESSL_VERSION_NUMBER
#define OpenSSL_version_num() SSLeay()
#endif
-#endif
-
-#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* 1.0.2 or later */ \
- !(defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER < 0x20700000L)
-#define HAVE_X509_GET0_SIGNATURE 1
-#endif
-
-#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) /* 1.0.2 or later */
-#define HAVE_SSL_GET_SHUTDOWN 1
-#endif
#if OPENSSL_VERSION_NUMBER >= 0x10002003L && \
OPENSSL_VERSION_NUMBER <= 0x10002FFFL && \
@@ -182,11 +168,6 @@
#define HAVE_SSL_COMP_FREE_COMPRESSION_METHODS 1
#endif
-#if (OPENSSL_VERSION_NUMBER < 0x0090808fL)
-/* not present in older OpenSSL */
-#define OPENSSL_load_builtin_modules(x)
-#endif
-
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
#define HAVE_EVP_PKEY_GET_PARAMS 1
#endif
@@ -200,18 +181,6 @@
#define FREE_PKEY_PARAM_BIGNUM(name)
#endif
-/*
- * Whether SSL_CTX_set_keylog_callback is available.
- * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287
- * BoringSSL: supported since d28f59c27bac (committed 2015-11-19)
- * LibreSSL: not supported. 3.5.0+ has a stub function that does nothing.
- */
-#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \
- !defined(LIBRESSL_VERSION_NUMBER)) || \
- defined(OPENSSL_IS_BORINGSSL)
-#define HAVE_KEYLOG_CALLBACK
-#endif
-
/* Whether SSL_CTX_set_ciphersuites is available.
* OpenSSL: supported since 1.1.1 (commit a53b5be6a05)
* BoringSSL: no
@@ -223,35 +192,32 @@
LIBRESSL_VERSION_NUMBER >= 0x3040100fL)) && \
!defined(OPENSSL_IS_BORINGSSL)
#define HAVE_SSL_CTX_SET_CIPHERSUITES
- #if !defined(OPENSSL_IS_AWSLC)
+ #ifndef OPENSSL_IS_AWSLC
#define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
#endif
#endif
-/*
- * Whether SSL_CTX_set1_curves_list is available.
- * OpenSSL: supported since 1.0.2, see
- * https://docs.openssl.org/master/man3/SSL_CTX_set1_curves/
- * BoringSSL: supported since 5fd1807d95f7 (committed 2016-09-30)
- * LibreSSL: since 2.5.3 (April 12, 2017)
+/* Whether SSL_CTX_set1_sigalgs_list is available
+ * OpenSSL: supported since 1.0.2 (commit 0b362de5f575)
+ * BoringSSL: supported since 0.20240913.0 (commit 826ce15)
+ * LibreSSL: no
*/
-#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) || \
- defined(OPENSSL_IS_BORINGSSL)
-#define HAVE_SSL_CTX_SET_EC_CURVES
+#if (OPENSSL_VERSION_NUMBER >= 0x10002000L && \
+ !defined(LIBRESSL_VERSION_NUMBER))
+ #define HAVE_SSL_CTX_SET1_SIGALGS
#endif
-#if defined(LIBRESSL_VERSION_NUMBER)
+#ifdef LIBRESSL_VERSION_NUMBER
#define OSSL_PACKAGE "LibreSSL"
#elif defined(OPENSSL_IS_BORINGSSL)
#define OSSL_PACKAGE "BoringSSL"
#elif defined(OPENSSL_IS_AWSLC)
#define OSSL_PACKAGE "AWS-LC"
+#elif (defined(USE_NGTCP2) && defined(USE_NGHTTP3) && \
+ !defined(OPENSSL_QUIC_API2)) || defined(USE_MSH3)
+#define OSSL_PACKAGE "quictls"
#else
-# if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_MSH3)
-# define OSSL_PACKAGE "quictls"
-# else
-# define OSSL_PACKAGE "OpenSSL"
-#endif
+#define OSSL_PACKAGE "OpenSSL"
#endif
#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
@@ -285,8 +251,6 @@ typedef int numcert_t;
#endif
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
- !(defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER < 0x2070100fL) && \
!defined(OPENSSL_IS_BORINGSSL) && \
!defined(OPENSSL_IS_AWSLC)
#define HAVE_OPENSSL_VERSION
@@ -307,13 +271,6 @@ typedef unsigned long sslerr_t;
#define HAVE_SSL_X509_STORE_SHARE
#endif
-/* What API version do we use? */
-#if defined(LIBRESSL_VERSION_NUMBER)
-#define USE_PRE_1_1_API (LIBRESSL_VERSION_NUMBER < 0x2070000f)
-#else /* !LIBRESSL_VERSION_NUMBER */
-#define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
-#endif /* !LIBRESSL_VERSION_NUMBER */
-
static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl);
static CURLcode push_certinfo(struct Curl_easy *data,
@@ -377,10 +334,16 @@ static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len)
static CURLcode X509V3_ext(struct Curl_easy *data,
int certnum,
- CONST_EXTS STACK_OF(X509_EXTENSION) *exts)
+ const STACK_OF(X509_EXTENSION) *extsarg)
{
int i;
CURLcode result = CURLE_OK;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+ !defined(LIBRESSL_VERSION_NUMBER)
+ const STACK_OF(X509_EXTENSION) *exts = extsarg;
+#else
+ STACK_OF(X509_EXTENSION) *exts = CURL_UNCONST(extsarg);
+#endif
if((int)sk_X509_EXTENSION_num(exts) <= 0)
/* no extensions, bail out */
@@ -469,7 +432,7 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl)
if(result)
break;
-#if defined(HAVE_X509_GET0_SIGNATURE) && defined(HAVE_X509_GET0_EXTENSIONS)
+#ifdef HAVE_X509_GET0_EXTENSIONS
{
const X509_ALGOR *sigalg = NULL;
X509_PUBKEY *xpubkey = NULL;
@@ -686,21 +649,19 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl)
#ifdef USE_OPENSSL
-#if USE_PRE_1_1_API
-#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER < 0x2070000fL
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
#define BIO_set_init(x,v) ((x)->init=(v))
#define BIO_get_data(x) ((x)->ptr)
#define BIO_set_data(x,v) ((x)->ptr=(v))
-#endif
#define BIO_get_shutdown(x) ((x)->shutdown)
#define BIO_set_shutdown(x,v) ((x)->shutdown=(v))
-#endif /* USE_PRE_1_1_API */
+#endif /* HAVE_PRE_1_1_API */
static int ossl_bio_cf_create(BIO *bio)
{
BIO_set_shutdown(bio, 1);
BIO_set_init(bio, 1);
-#if USE_PRE_1_1_API
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
bio->num = -1;
#endif
BIO_set_data(bio, NULL);
@@ -737,9 +698,11 @@ static long ossl_bio_cf_ctrl(BIO *bio, int cmd, long num, void *ptr)
ret = 1;
break;
#ifdef BIO_CTRL_EOF
- case BIO_CTRL_EOF:
+ case BIO_CTRL_EOF: {
/* EOF has been reached on input? */
- return (!cf->next || !cf->next->connected);
+ struct ssl_connect_data *connssl = cf->ctx;
+ return connssl->peer_closed;
+ }
#endif
default:
ret = 0;
@@ -817,7 +780,7 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen)
return (int)nread;
}
-#if USE_PRE_1_1_API
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
static BIO_METHOD ossl_bio_cf_meth_1_0 = {
BIO_TYPE_MEM,
@@ -863,14 +826,6 @@ static void ossl_bio_cf_method_free(BIO_METHOD *m)
#endif
-/*
- * Number of bytes to read from the random number seed file. This must be
- * a finite value (because some entropy "files" like /dev/urandom have
- * an infinite length), but must be large enough to provide enough
- * entropy to properly seed OpenSSL's PRNG.
- */
-#define RAND_LOAD_LENGTH 1024
-
#ifdef HAVE_KEYLOG_CALLBACK
static void ossl_keylog_callback(const SSL *ssl, const char *line)
{
@@ -894,9 +849,7 @@ ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done)
if(!session || *keylog_done)
return;
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
- !(defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
/* ssl->s3 is not checked in OpenSSL 1.1.0-pre6, but let's assume that
* we have a valid SSL context if we have a non-NULL session. */
SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE);
@@ -943,15 +896,15 @@ static const char *SSL_ERROR_to_str(int err)
return "SSL_ERROR_WANT_CONNECT";
case SSL_ERROR_WANT_ACCEPT:
return "SSL_ERROR_WANT_ACCEPT";
-#if defined(SSL_ERROR_WANT_ASYNC)
+#ifdef SSL_ERROR_WANT_ASYNC
case SSL_ERROR_WANT_ASYNC:
return "SSL_ERROR_WANT_ASYNC";
#endif
-#if defined(SSL_ERROR_WANT_ASYNC_JOB)
+#ifdef SSL_ERROR_WANT_ASYNC_JOB
case SSL_ERROR_WANT_ASYNC_JOB:
return "SSL_ERROR_WANT_ASYNC_JOB";
#endif
-#if defined(SSL_ERROR_WANT_EARLY)
+#ifdef SSL_ERROR_WANT_EARLY
case SSL_ERROR_WANT_EARLY:
return "SSL_ERROR_WANT_EARLY";
#endif
@@ -960,8 +913,6 @@ static const char *SSL_ERROR_to_str(int err)
}
}
-static size_t ossl_version(char *buffer, size_t size);
-
/* Return error string for last OpenSSL error
*/
static char *ossl_strerror(unsigned long error, char *buf, size_t size)
@@ -970,7 +921,7 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size)
DEBUGASSERT(size);
*buf = '\0';
- len = ossl_version(buf, size);
+ len = Curl_ossl_version(buf, size);
DEBUGASSERT(len < (size - 2));
if(len < (size - 2)) {
buf += len;
@@ -996,14 +947,14 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size)
}
static int passwd_callback(char *buf, int num, int encrypting,
- void *global_passwd)
+ void *password)
{
DEBUGASSERT(0 == encrypting);
- if(!encrypting && num >= 0) {
- int klen = curlx_uztosi(strlen((char *)global_passwd));
+ if(!encrypting && num >= 0 && password) {
+ int klen = curlx_uztosi(strlen((char *)password));
if(num > klen) {
- memcpy(buf, global_passwd, klen + 1);
+ memcpy(buf, password, klen + 1);
return klen;
}
}
@@ -1015,7 +966,7 @@ static int passwd_callback(char *buf, int num, int encrypting,
*/
static bool rand_enough(void)
{
- return (0 != RAND_status());
+ return 0 != RAND_status();
}
static CURLcode ossl_seed(struct Curl_easy *data)
@@ -1043,13 +994,13 @@ static CURLcode ossl_seed(struct Curl_easy *data)
size_t len = sizeof(randb);
size_t i, i_max;
for(i = 0, i_max = len / sizeof(struct curltime); i < i_max; ++i) {
- struct curltime tv = Curl_now();
+ struct curltime tv = curlx_now();
Curl_wait_ms(1);
tv.tv_sec *= (time_t)i + 1;
tv.tv_usec *= (int)i + 2;
- tv.tv_sec ^= ((Curl_now().tv_sec + (time_t)Curl_now().tv_usec) *
+ tv.tv_sec ^= ((curlx_now().tv_sec + (time_t)curlx_now().tv_usec) *
(time_t)(i + 3)) << 8;
- tv.tv_usec ^= (int) ((Curl_now().tv_sec + (time_t)Curl_now().tv_usec) *
+ tv.tv_usec ^= (int) ((curlx_now().tv_sec + (time_t)curlx_now().tv_usec) *
(time_t)(i + 4)) << 16;
memcpy(&randb[i * sizeof(struct curltime)], &tv,
sizeof(struct curltime));
@@ -1057,6 +1008,14 @@ static CURLcode ossl_seed(struct Curl_easy *data)
RAND_add(randb, (int)len, (double)len/2);
} while(!rand_enough());
+ /*
+ * Number of bytes to read from the random number seed file. This must be
+ * a finite value (because some entropy "files" like /dev/urandom have
+ * an infinite length), but must be large enough to provide enough
+ * entropy to properly seed OpenSSL's PRNG.
+ */
+# define RAND_LOAD_LENGTH 1024
+
{
/* generates a default path for the random seed file */
char fname[256];
@@ -1071,8 +1030,8 @@ static CURLcode ossl_seed(struct Curl_easy *data)
}
infof(data, "libcurl is now using a weak random seed");
- return (rand_enough() ? CURLE_OK :
- CURLE_SSL_CONNECT_ERROR /* confusing error code */);
+ return rand_enough() ? CURLE_OK :
+ CURLE_SSL_CONNECT_ERROR; /* confusing error code */
#endif
}
@@ -1082,6 +1041,9 @@ static CURLcode ossl_seed(struct Curl_easy *data)
#ifndef SSL_FILETYPE_PKCS12
#define SSL_FILETYPE_PKCS12 43
#endif
+#ifndef SSL_FILETYPE_PROVIDER
+#define SSL_FILETYPE_PROVIDER 44
+#endif
static int ossl_do_file_type(const char *type)
{
if(!type || !type[0])
@@ -1090,6 +1052,8 @@ static int ossl_do_file_type(const char *type)
return SSL_FILETYPE_PEM;
if(strcasecompare(type, "DER"))
return SSL_FILETYPE_ASN1;
+ if(strcasecompare(type, "PROV"))
+ return SSL_FILETYPE_PROVIDER;
if(strcasecompare(type, "ENG"))
return SSL_FILETYPE_ENGINE;
if(strcasecompare(type, "P12"))
@@ -1097,7 +1061,7 @@ static int ossl_do_file_type(const char *type)
return -1;
}
-#ifdef USE_OPENSSL_ENGINE
+#if defined(USE_OPENSSL_ENGINE) || defined(OPENSSL_HAS_PROVIDERS)
/*
* Supply default password to the engine user interface conversation.
* The password is passed by OpenSSL engine from ENGINE_load_private_key()
@@ -1145,16 +1109,19 @@ static int ssl_ui_writer(UI *ui, UI_STRING *uis)
*/
static bool is_pkcs11_uri(const char *string)
{
- return (string && strncasecompare(string, "pkcs11:", 7));
+ return string && strncasecompare(string, "pkcs11:", 7);
}
#endif
static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine);
+#if defined(OPENSSL_HAS_PROVIDERS)
+static CURLcode ossl_set_provider(struct Curl_easy *data,
+ const char *provider);
+#endif
-static int
-SSL_CTX_use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob,
- int type, const char *key_passwd)
+static int use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob,
+ int type, const char *key_passwd)
{
int ret = 0;
X509 *x = NULL;
@@ -1171,7 +1138,7 @@ SSL_CTX_use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob,
else if(type == SSL_FILETYPE_PEM) {
/* ERR_R_PEM_LIB; */
x = PEM_read_bio_X509(in, NULL,
- passwd_callback, (void *)key_passwd);
+ passwd_callback, CURL_UNCONST(key_passwd));
}
else {
ret = 0;
@@ -1190,9 +1157,8 @@ end:
return ret;
}
-static int
-SSL_CTX_use_PrivateKey_blob(SSL_CTX *ctx, const struct curl_blob *blob,
- int type, const char *key_passwd)
+static int use_privatekey_blob(SSL_CTX *ctx, const struct curl_blob *blob,
+ int type, const char *key_passwd)
{
int ret = 0;
EVP_PKEY *pkey = NULL;
@@ -1202,17 +1168,15 @@ SSL_CTX_use_PrivateKey_blob(SSL_CTX *ctx, const struct curl_blob *blob,
if(type == SSL_FILETYPE_PEM)
pkey = PEM_read_bio_PrivateKey(in, NULL, passwd_callback,
- (void *)key_passwd);
+ CURL_UNCONST(key_passwd));
else if(type == SSL_FILETYPE_ASN1)
pkey = d2i_PrivateKey_bio(in, NULL);
- else {
- ret = 0;
+ else
goto end;
- }
- if(!pkey) {
- ret = 0;
+
+ if(!pkey)
goto end;
- }
+
ret = SSL_CTX_use_PrivateKey(ctx, pkey);
EVP_PKEY_free(pkey);
end:
@@ -1221,16 +1185,11 @@ end:
}
static int
-SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob,
- const char *key_passwd)
+use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob,
+ const char *key_passwd)
{
-/* SSL_CTX_add1_chain_cert introduced in OpenSSL 1.0.2 */
-#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* OpenSSL 1.0.2 or later */ \
- !(defined(LIBRESSL_VERSION_NUMBER) && \
- (LIBRESSL_VERSION_NUMBER < 0x2090100fL)) /* LibreSSL 2.9.1 or later */
int ret = 0;
X509 *x = NULL;
- void *passwd_callback_userdata = (void *)key_passwd;
BIO *in = BIO_new_mem_buf(blob->data, (int)(blob->len));
if(!in)
return CURLE_OUT_OF_MEMORY;
@@ -1238,12 +1197,9 @@ SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob,
ERR_clear_error();
x = PEM_read_bio_X509_AUX(in, NULL,
- passwd_callback, (void *)key_passwd);
-
- if(!x) {
- ret = 0;
+ passwd_callback, CURL_UNCONST(key_passwd));
+ if(!x)
goto end;
- }
ret = SSL_CTX_use_certificate(ctx, x);
@@ -1260,7 +1216,7 @@ SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob,
}
while((ca = PEM_read_bio_X509(in, NULL, passwd_callback,
- passwd_callback_userdata))
+ CURL_UNCONST(key_passwd)))
!= NULL) {
if(!SSL_CTX_add0_chain_cert(ctx, ca)) {
@@ -1282,12 +1238,6 @@ end:
X509_free(x);
BIO_free(in);
return ret;
-#else
- (void)ctx; /* unused */
- (void)blob; /* unused */
- (void)key_passwd; /* unused */
- return 0;
-#endif
}
static
@@ -1306,7 +1256,8 @@ int cert_stuff(struct Curl_easy *data,
int file_type = ossl_do_file_type(cert_type);
- if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE)) {
+ if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE) ||
+ (file_type == SSL_FILETYPE_PROVIDER)) {
SSL *ssl;
X509 *x509;
int cert_done = 0;
@@ -1324,7 +1275,7 @@ int cert_stuff(struct Curl_easy *data,
case SSL_FILETYPE_PEM:
/* SSL_CTX_use_certificate_chain_file() only works on PEM files */
cert_use_result = cert_blob ?
- SSL_CTX_use_certificate_chain_blob(ctx, cert_blob, key_passwd) :
+ use_certificate_chain_blob(ctx, cert_blob, key_passwd) :
SSL_CTX_use_certificate_chain_file(ctx, cert_file);
if(cert_use_result != 1) {
failf(data,
@@ -1344,8 +1295,7 @@ int cert_stuff(struct Curl_easy *data,
ASN1 files. */
cert_use_result = cert_blob ?
- SSL_CTX_use_certificate_blob(ctx, cert_blob,
- file_type, key_passwd) :
+ use_certificate_blob(ctx, cert_blob, file_type, key_passwd) :
SSL_CTX_use_certificate_file(ctx, cert_file, file_type);
if(cert_use_result != 1) {
failf(data,
@@ -1383,7 +1333,7 @@ int cert_stuff(struct Curl_easy *data,
/* Does the engine supports LOAD_CERT_CTRL ? */
if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
- 0, (void *)cmd_name, NULL)) {
+ 0, CURL_UNCONST(cmd_name), NULL)) {
failf(data, "ssl engine does not support loading certificates");
return 0;
}
@@ -1418,9 +1368,70 @@ int cert_stuff(struct Curl_easy *data,
}
}
break;
-#else
- failf(data, "file type ENG for certificate not implemented");
- return 0;
+#endif
+#if defined(OPENSSL_HAS_PROVIDERS)
+ /* fall through to compatible provider */
+ case SSL_FILETYPE_PROVIDER:
+ {
+ /* Implicitly use pkcs11 provider if none was provided and the
+ * cert_file is a PKCS#11 URI */
+ if(!data->state.provider) {
+ if(is_pkcs11_uri(cert_file)) {
+ if(ossl_set_provider(data, "pkcs11") != CURLE_OK) {
+ return 0;
+ }
+ }
+ }
+
+ if(data->state.provider) {
+ /* Load the certificate from the provider */
+ OSSL_STORE_INFO *info = NULL;
+ X509 *cert = NULL;
+ OSSL_STORE_CTX *store =
+ OSSL_STORE_open_ex(cert_file, data->state.libctx,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if(!store) {
+ failf(data, "Failed to open OpenSSL store: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+ if(OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT) != 1) {
+ failf(data, "Failed to set store preference. Ignoring the error: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ }
+
+ info = OSSL_STORE_load(store);
+ if(info) {
+ int ossl_type = OSSL_STORE_INFO_get_type(info);
+
+ if(ossl_type == OSSL_STORE_INFO_CERT)
+ cert = OSSL_STORE_INFO_get1_CERT(info);
+ OSSL_STORE_INFO_free(info);
+ }
+ OSSL_STORE_close(store);
+ if(!cert) {
+ failf(data, "No cert found in the openssl store: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+
+ if(SSL_CTX_use_certificate(ctx, cert) != 1) {
+ failf(data, "unable to set client certificate [%s]",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+ X509_free(cert); /* we do not need the handle any more... */
+ }
+ else {
+ failf(data, "crypto provider not set, cannot load certificate");
+ return 0;
+ }
+ }
+ break;
#endif
case SSL_FILETYPE_PKCS12:
@@ -1469,8 +1480,7 @@ int cert_stuff(struct Curl_easy *data,
PKCS12_PBE_add();
- if(!PKCS12_parse(p12, key_passwd, &pri, &x509,
- &ca)) {
+ if(!PKCS12_parse(p12, key_passwd, &pri, &x509, &ca)) {
failf(data,
"could not parse PKCS12 file, check password, " OSSL_PACKAGE
" error %s",
@@ -1554,7 +1564,7 @@ fail:
FALLTHROUGH();
case SSL_FILETYPE_ASN1:
cert_use_result = key_blob ?
- SSL_CTX_use_PrivateKey_blob(ctx, key_blob, file_type, key_passwd) :
+ use_privatekey_blob(ctx, key_blob, file_type, key_passwd) :
SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type);
if(cert_use_result != 1) {
failf(data, "unable to set private key file: '%s' type %s",
@@ -1580,7 +1590,7 @@ fail:
if(data->state.engine) {
UI_METHOD *ui_method =
- UI_create_method((char *)"curl user interface");
+ UI_create_method(OSSL_UI_METHOD_CAST("curl user interface"));
if(!ui_method) {
failf(data, "unable do create " OSSL_PACKAGE
" user-interface method");
@@ -1611,10 +1621,87 @@ fail:
}
}
break;
-#else
- failf(data, "file type ENG for private key not supported");
- return 0;
#endif
+#if defined(OPENSSL_HAS_PROVIDERS)
+ /* fall through to compatible provider */
+ case SSL_FILETYPE_PROVIDER:
+ {
+ /* Implicitly use pkcs11 provider if none was provided and the
+ * key_file is a PKCS#11 URI */
+ if(!data->state.provider) {
+ if(is_pkcs11_uri(key_file)) {
+ if(ossl_set_provider(data, "pkcs11") != CURLE_OK) {
+ return 0;
+ }
+ }
+ }
+
+ if(data->state.provider) {
+ /* Load the private key from the provider */
+ EVP_PKEY *priv_key = NULL;
+ OSSL_STORE_CTX *store = NULL;
+ OSSL_STORE_INFO *info = NULL;
+ UI_METHOD *ui_method =
+ UI_create_method(OSSL_UI_METHOD_CAST("curl user interface"));
+ if(!ui_method) {
+ failf(data, "unable do create " OSSL_PACKAGE
+ " user-interface method");
+ return 0;
+ }
+ UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL()));
+ UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL()));
+ UI_method_set_reader(ui_method, ssl_ui_reader);
+ UI_method_set_writer(ui_method, ssl_ui_writer);
+
+ store = OSSL_STORE_open_ex(key_file, data->state.libctx,
+ data->state.propq, ui_method, NULL, NULL,
+ NULL, NULL);
+ if(!store) {
+ failf(data, "Failed to open OpenSSL store: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+ if(OSSL_STORE_expect(store, OSSL_STORE_INFO_PKEY) != 1) {
+ failf(data, "Failed to set store preference. Ignoring the error: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ }
+
+ info = OSSL_STORE_load(store);
+ if(info) {
+ int ossl_type = OSSL_STORE_INFO_get_type(info);
+
+ if(ossl_type == OSSL_STORE_INFO_PKEY)
+ priv_key = OSSL_STORE_INFO_get1_PKEY(info);
+ OSSL_STORE_INFO_free(info);
+ }
+ OSSL_STORE_close(store);
+ UI_destroy_method(ui_method);
+ if(!priv_key) {
+ failf(data, "No private key found in the openssl store: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+
+ if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) {
+ failf(data, "unable to set private key [%s]",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ EVP_PKEY_free(priv_key);
+ return 0;
+ }
+ EVP_PKEY_free(priv_key); /* we do not need the handle any more... */
+ }
+ else {
+ failf(data, "crypto provider not set, cannot load private key");
+ return 0;
+ }
+ }
+ break;
+#endif
+
case SSL_FILETYPE_PKCS12:
if(!cert_done) {
failf(data, "file type P12 for private key not supported");
@@ -1689,11 +1776,11 @@ static CURLcode x509_name_oneline(X509_NAME *a, struct dynbuf *d)
CURLcode result = CURLE_OUT_OF_MEMORY;
if(bio_out) {
- Curl_dyn_reset(d);
+ curlx_dyn_reset(d);
rc = X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_SPLUS_SPC);
if(rc != -1) {
BIO_get_mem_ptr(bio_out, &biomem);
- result = Curl_dyn_addn(d, biomem->data, biomem->length);
+ result = curlx_dyn_addn(d, biomem->data, biomem->length);
BIO_free(bio_out);
}
}
@@ -1708,8 +1795,7 @@ static CURLcode x509_name_oneline(X509_NAME *a, struct dynbuf *d)
*/
static int ossl_init(void)
{
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
- (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2070000fL)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
const uint64_t flags =
#ifdef OPENSSL_INIT_ENGINE_ALL_BUILTIN
/* not present in BoringSSL */
@@ -1759,8 +1845,7 @@ static int ossl_init(void)
/* Global cleanup */
static void ossl_cleanup(void)
{
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
- (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2070000fL)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
/* OpenSSL 1.1 deprecates all these cleanup functions and
turns them into no-ops in OpenSSL 1.0 compatibility mode */
#else
@@ -1776,11 +1861,7 @@ static void ossl_cleanup(void)
ERR_free_strings();
/* Free thread local error state, destroying hash upon zero refcount */
-#ifdef HAVE_ERR_REMOVE_THREAD_STATE
ERR_remove_thread_state(NULL);
-#else
- ERR_remove_state(0);
-#endif
/* Free all memory allocated by all configuration modules */
CONF_modules_free();
@@ -1793,47 +1874,39 @@ static void ossl_cleanup(void)
Curl_tls_keylog_close();
}
-/* Selects an OpenSSL crypto engine
+/* Selects an OpenSSL crypto engine or provider.
*/
-static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine)
+static CURLcode ossl_set_engine(struct Curl_easy *data, const char *name)
{
#ifdef USE_OPENSSL_ENGINE
- ENGINE *e;
+ CURLcode result = CURLE_SSL_ENGINE_NOTFOUND;
+ ENGINE *e = ENGINE_by_id(name);
-#if OPENSSL_VERSION_NUMBER >= 0x00909000L
- e = ENGINE_by_id(engine);
-#else
- /* avoid memory leak */
- for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) {
- const char *e_id = ENGINE_get_id(e);
- if(!strcmp(engine, e_id))
- break;
- }
-#endif
-
- if(!e) {
- failf(data, "SSL Engine '%s' not found", engine);
- return CURLE_SSL_ENGINE_NOTFOUND;
- }
+ if(e) {
- if(data->state.engine) {
- ENGINE_finish(data->state.engine);
- ENGINE_free(data->state.engine);
- data->state.engine = NULL;
- }
- if(!ENGINE_init(e)) {
- char buf[256];
-
- ENGINE_free(e);
- failf(data, "Failed to initialise SSL Engine '%s': %s",
- engine, ossl_strerror(ERR_get_error(), buf, sizeof(buf)));
- return CURLE_SSL_ENGINE_INITFAILED;
+ if(data->state.engine) {
+ ENGINE_finish(data->state.engine);
+ ENGINE_free(data->state.engine);
+ data->state.engine = NULL;
+ }
+ if(!ENGINE_init(e)) {
+ char buf[256];
+
+ ENGINE_free(e);
+ failf(data, "Failed to initialise SSL Engine '%s': %s",
+ name, ossl_strerror(ERR_get_error(), buf, sizeof(buf)));
+ result = CURLE_SSL_ENGINE_INITFAILED;
+ e = NULL;
+ }
+ data->state.engine = e;
+ return result;
}
- data->state.engine = e;
- return CURLE_OK;
+#endif
+#ifdef OPENSSL_HAS_PROVIDERS
+ return ossl_set_provider(data, name);
#else
- (void)engine;
- failf(data, "SSL Engine not supported");
+ (void)name;
+ failf(data, "OpenSSL engine not found");
return CURLE_SSL_ENGINE_NOTFOUND;
#endif
}
@@ -1882,6 +1955,102 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data)
return list;
}
+#if defined(OPENSSL_HAS_PROVIDERS)
+
+static void ossl_provider_cleanup(struct Curl_easy *data)
+{
+ if(data->state.baseprov) {
+ OSSL_PROVIDER_unload(data->state.baseprov);
+ data->state.baseprov = NULL;
+ }
+ if(data->state.provider) {
+ OSSL_PROVIDER_unload(data->state.provider);
+ data->state.provider = NULL;
+ }
+ OSSL_LIB_CTX_free(data->state.libctx);
+ data->state.libctx = NULL;
+ Curl_safefree(data->state.propq);
+ data->state.provider_loaded = FALSE;
+}
+
+#define MAX_PROVIDER_LEN 128 /* reasonable */
+
+/* Selects an OpenSSL crypto provider.
+ *
+ * A provider might need an associated property, a string passed on to
+ * OpenSSL. Specify this as [PROVIDER][:PROPERTY]: separate the name and the
+ * property with a colon. No colon means no property is set.
+ *
+ * An example provider + property looks like "tpm2:?provider=tpm2".
+ */
+static CURLcode ossl_set_provider(struct Curl_easy *data, const char *iname)
+{
+ char name[MAX_PROVIDER_LEN + 1];
+ struct Curl_str prov;
+ const char *propq = NULL;
+
+ if(!iname) {
+ /* clear and cleanup provider use */
+ ossl_provider_cleanup(data);
+ return CURLE_OK;
+ }
+ if(curlx_str_until(&iname, &prov, MAX_PROVIDER_LEN, ':'))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ if(!curlx_str_single(&iname, ':'))
+ /* there was a colon, get the propq until the end of string */
+ propq = iname;
+
+ /* we need the name in a buffer, null-terminated */
+ memcpy(name, curlx_str(&prov), curlx_strlen(&prov));
+ name[curlx_strlen(&prov)] = 0;
+
+ if(!data->state.libctx) {
+ OSSL_LIB_CTX *libctx = OSSL_LIB_CTX_new();
+ if(!libctx)
+ return CURLE_OUT_OF_MEMORY;
+ if(propq) {
+ data->state.propq = strdup(propq);
+ if(!data->state.propq) {
+ OSSL_LIB_CTX_free(libctx);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ data->state.libctx = libctx;
+ }
+
+ if(OSSL_PROVIDER_available(data->state.libctx, name)) {
+ /* already loaded through the configuration - no action needed */
+ data->state.provider_loaded = TRUE;
+ return CURLE_OK;
+ }
+
+ data->state.provider =
+ OSSL_PROVIDER_try_load(data->state.libctx, name, 1);
+ if(!data->state.provider) {
+ char error_buffer[256];
+ failf(data, "Failed to initialize provider: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ ossl_provider_cleanup(data);
+ return CURLE_SSL_ENGINE_NOTFOUND;
+ }
+
+ /* load the base provider as well */
+ data->state.baseprov =
+ OSSL_PROVIDER_try_load(data->state.libctx, "base", 1);
+ if(!data->state.baseprov) {
+ ossl_provider_cleanup(data);
+ failf(data, "Failed to load base");
+ return CURLE_SSL_ENGINE_NOTFOUND;
+ }
+ else
+ data->state.provider_loaded = TRUE;
+ return CURLE_OK;
+}
+#endif
+
+
static CURLcode ossl_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool send_shutdown, bool *done)
@@ -2021,13 +2190,6 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
}
}
-static void ossl_session_free(void *sessionid, size_t idsize)
-{
- /* free the ID */
- (void)idsize;
- free(sessionid);
-}
-
/*
* This function is called when the 'data' struct is going away. Close
* down everything and free all resources!
@@ -2043,8 +2205,10 @@ static void ossl_close_all(struct Curl_easy *data)
#else
(void)data;
#endif
-#if !defined(HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED) && \
- defined(HAVE_ERR_REMOVE_THREAD_STATE)
+#ifdef OPENSSL_HAS_PROVIDERS
+ ossl_provider_cleanup(data);
+#endif
+#ifndef HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED
/* OpenSSL 1.0.1 and 1.0.2 build an error queue that is stored per-thread
so we need to clean it here in case the thread will be killed. All OpenSSL
code should extract the error in association with the error so clearing
@@ -2121,14 +2285,14 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
hostlen = strlen(peer->hostname);
switch(peer->type) {
case CURL_SSL_PEER_IPV4:
- if(!Curl_inet_pton(AF_INET, peer->hostname, &addr))
+ if(!curlx_inet_pton(AF_INET, peer->hostname, &addr))
return CURLE_PEER_FAILED_VERIFICATION;
target = GEN_IPADD;
addrlen = sizeof(struct in_addr);
break;
#ifdef USE_IPV6
case CURL_SSL_PEER_IPV6:
- if(!Curl_inet_pton(AF_INET6, peer->hostname, &addr))
+ if(!curlx_inet_pton(AF_INET6, peer->hostname, &addr))
return CURLE_PEER_FAILED_VERIFICATION;
target = GEN_IPADD;
addrlen = sizeof(struct in6_addr);
@@ -2174,7 +2338,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
/* only check alternatives of the same type the target is */
if(check->type == target) {
/* get data and length */
- const char *altptr = (char *)ASN1_STRING_get0_data(check->d.ia5);
+ const char *altptr = (const char *)ASN1_STRING_get0_data(check->d.ia5);
size_t altlen = (size_t) ASN1_STRING_length(check->d.ia5);
switch(target) {
@@ -2260,7 +2424,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
if(tmp) {
if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
cnlen = ASN1_STRING_length(tmp);
- cn = (unsigned char *) ASN1_STRING_get0_data(tmp);
+ cn = (unsigned char *)CURL_UNCONST(ASN1_STRING_get0_data(tmp));
}
else { /* not a UTF8 name */
cnlen = ASN1_STRING_to_UTF8(&cn, tmp);
@@ -2302,14 +2466,13 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
return result;
}
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
- !defined(OPENSSL_NO_OCSP)
+#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP)
static CURLcode verifystatus(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ossl_ctx *octx)
{
int i, ocsp_status;
-#if defined(OPENSSL_IS_AWSLC)
+#ifdef OPENSSL_IS_AWSLC
const uint8_t *status;
#else
unsigned char *status;
@@ -2368,34 +2531,6 @@ static CURLcode verifystatus(struct Curl_cfilter *cf,
}
st = SSL_CTX_get_cert_store(octx->ssl_ctx);
-#if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \
- (defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER <= 0x2040200fL))
- /* The authorized responder cert in the OCSP response MUST be signed by the
- peer cert's issuer (see RFC6960 section 4.2.2.2). If that is a root cert,
- no problem, but if it is an intermediate cert OpenSSL has a bug where it
- expects this issuer to be present in the chain embedded in the OCSP
- response. So we add it if necessary. */
-
- /* First make sure the peer cert chain includes both a peer and an issuer,
- and the OCSP response contains a responder cert. */
- if(sk_X509_num(ch) >= 2 && sk_X509_num(br->certs) >= 1) {
- X509 *responder = sk_X509_value(br->certs, sk_X509_num(br->certs) - 1);
-
- /* Find issuer of responder cert and add it to the OCSP response chain */
- for(i = 0; i < sk_X509_num(ch); i++) {
- X509 *issuer = sk_X509_value(ch, i);
- if(X509_check_issued(issuer, responder) == X509_V_OK) {
- if(!OCSP_basic_add1_cert(br, issuer)) {
- failf(data, "Could not add issuer cert to OCSP response");
- result = CURLE_SSL_INVALIDCERTSTATUS;
- goto end;
- }
- }
- }
- }
-#endif
-
if(OCSP_basic_verify(br, ch, st, 0) <= 0) {
failf(data, "OCSP response verification failed");
result = CURLE_SSL_INVALIDCERTSTATUS;
@@ -2667,15 +2802,15 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
tls_rt_name = "";
if(content_type == SSL3_RT_CHANGE_CIPHER_SPEC) {
- msg_type = *(char *)buf;
+ msg_type = *(const char *)buf;
msg_name = "Change cipher spec";
}
else if(content_type == SSL3_RT_ALERT) {
- msg_type = (((char *)buf)[0] << 8) + ((char *)buf)[1];
+ msg_type = (((const char *)buf)[0] << 8) + ((const char *)buf)[1];
msg_name = SSL_alert_desc_string_long(msg_type);
}
else {
- msg_type = *(char *)buf;
+ msg_type = *(const char *)buf;
msg_name = ssl_msg_type(ssl_ver, msg_type);
}
@@ -2687,7 +2822,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
}
Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT :
- CURLINFO_SSL_DATA_IN, (char *)buf, len);
+ CURLINFO_SSL_DATA_IN, (const char *)buf, len);
(void) ssl;
}
#endif
@@ -2695,11 +2830,9 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
#ifdef USE_OPENSSL
/* ====================================================== */
-/* Check for OpenSSL 1.0.2 which has ALPN support. */
-#undef HAS_ALPN
-#if OPENSSL_VERSION_NUMBER >= 0x10002000L \
- && !defined(OPENSSL_NO_TLSEXT)
-# define HAS_ALPN 1
+/* Check for ALPN support. */
+#ifndef OPENSSL_NO_TLSEXT
+# define HAS_ALPN_OPENSSL
#endif
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */
@@ -2831,20 +2964,10 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
#endif
FALLTHROUGH();
case CURL_SSLVERSION_TLSv1_2:
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1_1;
-#else
- failf(data, OSSL_PACKAGE " was built without TLS 1.2 support");
- return CURLE_NOT_BUILT_IN;
-#endif
FALLTHROUGH();
case CURL_SSLVERSION_TLSv1_1:
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1;
-#else
- failf(data, OSSL_PACKAGE " was built without TLS 1.1 support");
- return CURLE_NOT_BUILT_IN;
-#endif
FALLTHROUGH();
case CURL_SSLVERSION_TLSv1_0:
case CURL_SSLVERSION_TLSv1:
@@ -2853,14 +2976,10 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
switch(ssl_version_max) {
case CURL_SSLVERSION_MAX_TLSv1_0:
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1_1;
-#endif
FALLTHROUGH();
case CURL_SSLVERSION_MAX_TLSv1_1:
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1_2;
-#endif
FALLTHROUGH();
case CURL_SSLVERSION_MAX_TLSv1_2:
#ifdef TLS1_3_VERSION
@@ -2881,20 +3000,27 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
- const struct ssl_peer *peer,
- SSL_SESSION *session)
+ const char *ssl_peer_key,
+ SSL_SESSION *session,
+ int ietf_tls_id,
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len)
{
const struct ssl_config_data *config;
+ unsigned char *der_session_buf = NULL;
+ unsigned char *qtp_clone = NULL;
CURLcode result = CURLE_OK;
- size_t der_session_size;
- unsigned char *der_session_buf;
- unsigned char *der_session_ptr;
if(!cf || !data)
goto out;
config = Curl_ssl_cf_get_config(cf, data);
if(config->primary.cache_session) {
+ struct Curl_ssl_session *sc_session = NULL;
+ size_t der_session_size;
+ unsigned char *der_session_ptr;
+ size_t earlydata_max = 0;
der_session_size = i2d_SSL_SESSION(session, NULL);
if(der_session_size == 0) {
@@ -2911,17 +3037,35 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
der_session_size = i2d_SSL_SESSION(session, &der_session_ptr);
if(der_session_size == 0) {
result = CURLE_OUT_OF_MEMORY;
- free(der_session_buf);
goto out;
}
- Curl_ssl_sessionid_lock(data);
- result = Curl_ssl_set_sessionid(cf, data, peer, NULL, der_session_buf,
- der_session_size, ossl_session_free);
- Curl_ssl_sessionid_unlock(data);
+#ifdef HAVE_OPENSSL_EARLYDATA
+ earlydata_max = SSL_SESSION_get_max_early_data(session);
+#endif
+ if(quic_tp && quic_tp_len) {
+ qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len);
+ if(!qtp_clone) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ }
+
+ result = Curl_ssl_session_create2(der_session_buf, der_session_size,
+ ietf_tls_id, alpn,
+ (curl_off_t)time(NULL) +
+ SSL_SESSION_get_timeout(session),
+ earlydata_max, qtp_clone, quic_tp_len,
+ &sc_session);
+ der_session_buf = NULL; /* took ownership of sdata */
+ if(!result) {
+ result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
+ /* took ownership of `sc_session` */
+ }
}
out:
+ free(der_session_buf);
return result;
}
@@ -2930,14 +3074,14 @@ out:
*/
static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
{
- struct Curl_cfilter *cf;
- struct Curl_easy *data;
- struct ssl_connect_data *connssl;
-
- cf = (struct Curl_cfilter*) SSL_get_app_data(ssl);
- connssl = cf ? cf->ctx : NULL;
- data = connssl ? CF_DATA_CURRENT(cf) : NULL;
- Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid);
+ struct Curl_cfilter *cf = (struct Curl_cfilter*) SSL_get_app_data(ssl);
+ if(cf) {
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
+ struct ssl_connect_data *connssl = cf->ctx;
+ Curl_ossl_add_session(cf, data, connssl->peer.scache_key, ssl_sessionid,
+ SSL_version(ssl), connssl->negotiated.alpn,
+ NULL, 0);
+ }
return 0;
}
@@ -2997,7 +3141,7 @@ static CURLcode load_cacert_from_memory(X509_STORE *store,
return (count > 0) ? CURLE_OK : CURLE_SSL_CACERT_BADFILE;
}
-#if defined(USE_WIN32_CRYPTO)
+#ifdef USE_WIN32_CRYPTO
static CURLcode import_windows_cert_store(struct Curl_easy *data,
const char *name,
X509_STORE *store,
@@ -3141,9 +3285,9 @@ static CURLcode import_windows_cert_store(struct Curl_easy *data,
}
#endif
-static CURLcode populate_x509_store(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- X509_STORE *store)
+static CURLcode ossl_populate_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ X509_STORE *store)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
@@ -3159,13 +3303,13 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
bool imported_native_ca = FALSE;
bool imported_ca_info_blob = FALSE;
- CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d",
+ CURL_TRC_CF(data, cf, "ossl_populate_x509_store, path=%s, blob=%d",
ssl_cafile ? ssl_cafile : "none", !!ca_info_blob);
if(!store)
return CURLE_OUT_OF_MEMORY;
if(verifypeer) {
-#if defined(USE_WIN32_CRYPTO)
+#ifdef USE_WIN32_CRYPTO
/* Import certificates from the Windows root certificate store if
requested.
https://stackoverflow.com/questions/9507184/
@@ -3177,7 +3321,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
"CA" /* Intermediate Certification Authorities */
};
size_t i;
- for(i = 0; i < ARRAYSIZE(storeNames); ++i) {
+ for(i = 0; i < CURL_ARRAYSIZE(storeNames); ++i) {
bool imported = FALSE;
result = import_windows_cert_store(data, storeNames[i], store,
&imported);
@@ -3283,7 +3427,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
https://web.archive.org/web/20190422050538/
rt.openssl.org/Ticket/Display.html?id=3621
*/
-#if defined(X509_V_FLAG_TRUSTED_FIRST)
+#ifdef X509_V_FLAG_TRUSTED_FIRST
X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST);
#endif
#ifdef X509_V_FLAG_PARTIAL_CHAIN
@@ -3304,7 +3448,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
return result;
}
-#if defined(HAVE_SSL_X509_STORE_SHARE)
+#ifdef HAVE_SSL_X509_STORE_SHARE
/* key to use at `multi->proto_hash` */
#define MPROTO_OSSL_X509_KEY "tls:ossl:x509:share"
@@ -3330,15 +3474,15 @@ static void oss_x509_share_free(void *key, size_t key_len, void *p)
}
static bool
-cached_x509_store_expired(const struct Curl_easy *data,
- const struct ossl_x509_share *mb)
+ossl_cached_x509_store_expired(const struct Curl_easy *data,
+ const struct ossl_x509_share *mb)
{
const struct ssl_general_config *cfg = &data->set.general_ssl;
if(cfg->ca_cache_timeout < 0)
return FALSE;
else {
- struct curltime now = Curl_now();
- timediff_t elapsed_ms = Curl_timediff(now, mb->time);
+ struct curltime now = curlx_now();
+ timediff_t elapsed_ms = curlx_timediff(now, mb->time);
timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
return elapsed_ms >= timeout_ms;
@@ -3346,8 +3490,8 @@ cached_x509_store_expired(const struct Curl_easy *data,
}
static bool
-cached_x509_store_different(struct Curl_cfilter *cf,
- const struct ossl_x509_share *mb)
+ossl_cached_x509_store_different(struct Curl_cfilter *cf,
+ const struct ossl_x509_share *mb)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
if(!mb->CAfile || !conn_config->CAfile)
@@ -3356,8 +3500,8 @@ cached_x509_store_different(struct Curl_cfilter *cf,
return strcmp(mb->CAfile, conn_config->CAfile);
}
-static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf,
- const struct Curl_easy *data)
+static X509_STORE *ossl_get_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
{
struct Curl_multi *multi = data->multi;
struct ossl_x509_share *share;
@@ -3365,20 +3509,20 @@ static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf,
DEBUGASSERT(multi);
share = multi ? Curl_hash_pick(&multi->proto_hash,
- (void *)MPROTO_OSSL_X509_KEY,
+ CURL_UNCONST(MPROTO_OSSL_X509_KEY),
sizeof(MPROTO_OSSL_X509_KEY)-1) : NULL;
if(share && share->store &&
- !cached_x509_store_expired(data, share) &&
- !cached_x509_store_different(cf, share)) {
+ !ossl_cached_x509_store_expired(data, share) &&
+ !ossl_cached_x509_store_different(cf, share)) {
store = share->store;
}
return store;
}
-static void set_cached_x509_store(struct Curl_cfilter *cf,
- const struct Curl_easy *data,
- X509_STORE *store)
+static void ossl_set_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data,
+ X509_STORE *store)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct Curl_multi *multi = data->multi;
@@ -3388,7 +3532,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
if(!multi)
return;
share = Curl_hash_pick(&multi->proto_hash,
- (void *)MPROTO_OSSL_X509_KEY,
+ CURL_UNCONST(MPROTO_OSSL_X509_KEY),
sizeof(MPROTO_OSSL_X509_KEY)-1);
if(!share) {
@@ -3396,7 +3540,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
if(!share)
return;
if(!Curl_hash_add2(&multi->proto_hash,
- (void *)MPROTO_OSSL_X509_KEY,
+ CURL_UNCONST(MPROTO_OSSL_X509_KEY),
sizeof(MPROTO_OSSL_X509_KEY)-1,
share, oss_x509_share_free)) {
free(share);
@@ -3420,7 +3564,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
free(share->CAfile);
}
- share->time = Curl_now();
+ share->time = curlx_now();
share->store = store;
share->CAfile = CAfile;
}
@@ -3446,16 +3590,16 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
!ssl_config->primary.CRLfile &&
!ssl_config->native_ca_store;
- cached_store = get_cached_x509_store(cf, data);
+ cached_store = ossl_get_cached_x509_store(cf, data);
if(cached_store && cache_criteria_met && X509_STORE_up_ref(cached_store)) {
SSL_CTX_set_cert_store(ssl_ctx, cached_store);
}
else {
X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
- result = populate_x509_store(cf, data, store);
+ result = ossl_populate_x509_store(cf, data, store);
if(result == CURLE_OK && cache_criteria_met) {
- set_cached_x509_store(cf, data, store);
+ ossl_set_cached_x509_store(cf, data, store);
}
}
@@ -3468,48 +3612,303 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
{
X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
- return populate_x509_store(cf, data, store);
+ return ossl_populate_x509_store(cf, data, store);
}
#endif /* HAVE_SSL_X509_STORE_SHARE */
-CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
+
+static CURLcode
+ossl_init_session_and_alpns(struct ossl_ctx *octx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- int transport, /* TCP or QUIC */
- const unsigned char *alpn, size_t alpn_len,
- Curl_ossl_ctx_setup_cb *cb_setup,
- void *cb_user_data,
- Curl_ossl_new_session_cb *cb_new_session,
- void *ssl_user_data)
+ const struct alpn_spec *alpns_requested,
+ Curl_ossl_init_session_reuse_cb *sess_reuse_cb)
{
- CURLcode result = CURLE_OK;
- const char *ciphers;
- SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
- ctx_option_t ctx_options = 0;
- SSL_SESSION *ssl_session = NULL;
- const unsigned char *der_sessionid = NULL;
- size_t der_sessionid_size = 0;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- const long int ssl_version_min = conn_config->version;
- char * const ssl_cert = ssl_config->primary.clientcert;
- const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
- const char * const ssl_cert_type = ssl_config->cert_type;
- const bool verifypeer = conn_config->verifypeer;
+ struct alpn_spec alpns;
char error_buffer[256];
+ CURLcode result;
- /* Make funny stuff to get random input */
- result = ossl_seed(data);
- if(result)
- return result;
+ Curl_alpn_copy(&alpns, alpns_requested);
- ssl_config->certverifyresult = !X509_V_OK;
+ octx->reused_session = FALSE;
+ if(ssl_config->primary.cache_session) {
+ struct Curl_ssl_session *scs = NULL;
+
+ result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs);
+ if(!result && scs && scs->sdata && scs->sdata_len) {
+ const unsigned char *der_sessionid = scs->sdata;
+ size_t der_sessionid_size = scs->sdata_len;
+ SSL_SESSION *ssl_session = NULL;
+
+ /* If OpenSSL does not accept the session from the cache, this
+ * is not an error. We just continue without it. */
+ ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid,
+ (long)der_sessionid_size);
+ if(ssl_session) {
+ if(!SSL_set_session(octx->ssl, ssl_session)) {
+ infof(data, "SSL: SSL_set_session not accepted, "
+ "continuing without: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ }
+ else {
+ infof(data, "SSL reusing session with ALPN '%s'",
+ scs->alpn ? scs->alpn : "-");
+ octx->reused_session = TRUE;
+#ifdef HAVE_OPENSSL_EARLYDATA
+ if(ssl_config->earlydata && scs->alpn &&
+ SSL_SESSION_get_max_early_data(ssl_session) &&
+ !cf->conn->connect_only &&
+ (SSL_version(octx->ssl) == TLS1_3_VERSION)) {
+ bool do_early_data = FALSE;
+ if(sess_reuse_cb) {
+ result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data);
+ if(result)
+ return result;
+ }
+ if(do_early_data) {
+ /* We only try the ALPN protocol the session used before,
+ * otherwise we might send early data for the wrong protocol */
+ Curl_alpn_restrict_to(&alpns, scs->alpn);
+ }
+ }
+#else
+ (void)sess_reuse_cb;
+#endif
+ }
+ SSL_SESSION_free(ssl_session);
+ }
+ else {
+ infof(data, "SSL session not accepted by OpenSSL, continuing without");
+ }
+ }
+ Curl_ssl_scache_return(cf, data, peer->scache_key, scs);
+ }
+
+#ifdef HAS_ALPN_OPENSSL
+ if(alpns.count) {
+ struct alpn_proto_buf proto;
+ memset(&proto, 0, sizeof(proto));
+ result = Curl_alpn_to_proto_buf(&proto, &alpns);
+ if(result) {
+ failf(data, "Error determining ALPN");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ if(SSL_set_alpn_protos(octx->ssl, proto.data, (int)proto.len)) {
+ failf(data, "Error setting ALPN");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#endif
+
+ return CURLE_OK;
+}
+
+#ifdef USE_ECH_OPENSSL
+static CURLcode ossl_init_ech(struct ossl_ctx *octx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer)
+{
+ unsigned char *ech_config = NULL;
+ size_t ech_config_len = 0;
+ char *outername = data->set.str[STRING_ECH_PUBLIC];
+ int trying_ech_now = 0;
+ CURLcode result;
+
+ if(!ECH_ENABLED(data))
+ return CURLE_OK;
+
+ if(data->set.tls_ech & CURLECH_GREASE) {
+ infof(data, "ECH: will GREASE ClientHello");
+# if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+ SSL_set_enable_ech_grease(octx->ssl, 1);
+# else
+ SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE);
+# endif
+ }
+ else if(data->set.tls_ech & CURLECH_CLA_CFG) {
+# if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+ /* have to do base64 decode here for BoringSSL */
+ const char *b64 = data->set.str[STRING_ECH_CONFIG];
- switch(transport) {
+ if(!b64) {
+ infof(data, "ECH: ECHConfig from command line empty");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ ech_config_len = 2 * strlen(b64);
+ result = curlx_base64_decode(b64, &ech_config, &ech_config_len);
+ if(result || !ech_config) {
+ infof(data, "ECH: cannot base64 decode ECHConfig from command line");
+ if(data->set.tls_ech & CURLECH_HARD)
+ return result;
+ }
+ if(SSL_set1_ech_config_list(octx->ssl, ech_config,
+ ech_config_len) != 1) {
+ infof(data, "ECH: SSL_ECH_set1_ech_config_list failed");
+ if(data->set.tls_ech & CURLECH_HARD) {
+ free(ech_config);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ free(ech_config);
+ trying_ech_now = 1;
+# else
+ ech_config = (unsigned char *) data->set.str[STRING_ECH_CONFIG];
+ if(!ech_config) {
+ infof(data, "ECH: ECHConfig from command line empty");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ ech_config_len = strlen(data->set.str[STRING_ECH_CONFIG]);
+ if(SSL_set1_ech_config_list(octx->ssl, ech_config,
+ ech_config_len) != 1) {
+ infof(data, "ECH: SSL_ECH_set1_ech_config_list failed");
+ if(data->set.tls_ech & CURLECH_HARD)
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else
+ trying_ech_now = 1;
+# endif
+ infof(data, "ECH: ECHConfig from command line");
+ }
+ else {
+ struct Curl_dns_entry *dns = NULL;
+
+ if(peer->hostname)
+ dns = Curl_dnscache_get(data, peer->hostname, peer->port,
+ cf->conn->ip_version);
+ if(!dns) {
+ infof(data, "ECH: requested but no DNS info available");
+ if(data->set.tls_ech & CURLECH_HARD)
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else {
+ struct Curl_https_rrinfo *rinfo = NULL;
+
+ rinfo = dns->hinfo;
+ if(rinfo && rinfo->echconfiglist) {
+ unsigned char *ecl = rinfo->echconfiglist;
+ size_t elen = rinfo->echconfiglist_len;
+
+ infof(data, "ECH: ECHConfig from DoH HTTPS RR");
+ if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) {
+ infof(data, "ECH: SSL_set1_ech_config_list failed");
+ if(data->set.tls_ech & CURLECH_HARD)
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else {
+ trying_ech_now = 1;
+ infof(data, "ECH: imported ECHConfigList of length %zu", elen);
+ }
+ }
+ else {
+ infof(data, "ECH: requested but no ECHConfig available");
+ if(data->set.tls_ech & CURLECH_HARD)
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ Curl_resolv_unlink(data, &dns);
+ }
+ }
+# if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+ if(trying_ech_now && outername) {
+ infof(data, "ECH: setting public_name not supported with BoringSSL");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+# else
+ if(trying_ech_now && outername) {
+ infof(data, "ECH: inner: '%s', outer: '%s'",
+ peer->hostname ? peer->hostname : "NULL", outername);
+ result = SSL_ech_set1_server_names(octx->ssl,
+ peer->hostname, outername,
+ 0 /* do send outer */);
+ if(result != 1) {
+ infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+# endif /* OPENSSL_IS_BORINGSSL || OPENSSL_IS_AWSLC */
+ if(trying_ech_now
+ && SSL_set_min_proto_version(octx->ssl, TLS1_3_VERSION) != 1) {
+ infof(data, "ECH: cannot force TLSv1.3 [ERROR]");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ return CURLE_OK;
+}
+#endif /* USE_ECH_OPENSSL */
+
+
+static CURLcode ossl_init_ssl(struct ossl_ctx *octx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer,
+ const struct alpn_spec *alpns_requested,
+ void *ssl_user_data,
+ Curl_ossl_init_session_reuse_cb *sess_reuse_cb)
+{
+ /* Let's make an SSL structure */
+ if(octx->ssl)
+ SSL_free(octx->ssl);
+ octx->ssl = SSL_new(octx->ssl_ctx);
+ if(!octx->ssl) {
+ failf(data, "SSL: could not create a context (handle)");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ SSL_set_app_data(octx->ssl, ssl_user_data);
+
+#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP)
+ if(Curl_ssl_cf_get_primary_config(cf)->verifystatus)
+ SSL_set_tlsext_status_type(octx->ssl, TLSEXT_STATUSTYPE_ocsp);
+#endif
+
+#if (defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)) && \
+ defined(ALLOW_RENEG)
+ SSL_set_renegotiate_mode(octx->ssl, ssl_renegotiate_freely);
+#endif
+
+ SSL_set_connect_state(octx->ssl);
+
+ octx->server_cert = NULL;
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ if(peer->sni) {
+ if(!SSL_set_tlsext_host_name(octx->ssl, peer->sni)) {
+ failf(data, "Failed set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+#ifdef USE_ECH_OPENSSL
+ {
+ CURLcode result = ossl_init_ech(octx, cf, data, peer);
+ if(result)
+ return result;
+ }
+#endif /* USE_ECH_OPENSSL */
+
+#endif
+
+ return ossl_init_session_and_alpns(octx, cf, data, peer,
+ alpns_requested, sess_reuse_cb);
+}
+
+
+static CURLcode ossl_init_method(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer,
+ const SSL_METHOD **pmethod,
+ unsigned int *pssl_version_min)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+
+ *pmethod = NULL;
+ *pssl_version_min = conn_config->version;
+ switch(peer->transport) {
case TRNSPRT_TCP:
/* check to see if we have been told to use an explicit SSL/TLS version */
- switch(ssl_version_min) {
+ switch(*pssl_version_min) {
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
case CURL_SSLVERSION_TLSv1_0:
@@ -3518,9 +3917,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
case CURL_SSLVERSION_TLSv1_3:
/* it will be handled later with the context options */
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
- req_method = TLS_client_method();
+ *pmethod = TLS_client_method();
#else
- req_method = SSLv23_client_method();
+ *pmethod = SSLv23_client_method();
#endif
break;
case CURL_SSLVERSION_SSLv2:
@@ -3535,6 +3934,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
}
break;
case TRNSPRT_QUIC:
+ *pssl_version_min = CURL_SSLVERSION_TLSv1_3;
if(conn_config->version_max &&
(conn_config->version_max != CURL_SSLVERSION_MAX_TLSv1_3)) {
failf(data, "QUIC needs at least TLS version 1.3");
@@ -3542,21 +3942,65 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
}
#ifdef USE_OPENSSL_QUIC
- req_method = OSSL_QUIC_client_method();
+ *pmethod = OSSL_QUIC_client_method();
#elif (OPENSSL_VERSION_NUMBER >= 0x10100000L)
- req_method = TLS_method();
+ *pmethod = TLS_method();
#else
- req_method = SSLv23_client_method();
+ *pmethod = SSLv23_client_method();
#endif
break;
default:
- failf(data, "unsupported transport %d in SSL init", transport);
+ failf(data, "unsupported transport %d in SSL init", peer->transport);
return CURLE_SSL_CONNECT_ERROR;
}
+ return *pmethod ? CURLE_OK : CURLE_SSL_CONNECT_ERROR;
+}
+
+
+CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer,
+ const struct alpn_spec *alpns_requested,
+ Curl_ossl_ctx_setup_cb *cb_setup,
+ void *cb_user_data,
+ Curl_ossl_new_session_cb *cb_new_session,
+ void *ssl_user_data,
+ Curl_ossl_init_session_reuse_cb *sess_reuse_cb)
+{
+ CURLcode result = CURLE_OK;
+ const char *ciphers;
+ const SSL_METHOD *req_method = NULL;
+ ctx_option_t ctx_options = 0;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ char * const ssl_cert = ssl_config->primary.clientcert;
+ const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
+ const char * const ssl_cert_type = ssl_config->cert_type;
+ const bool verifypeer = conn_config->verifypeer;
+ unsigned int ssl_version_min;
+ char error_buffer[256];
+
+ /* Make funny stuff to get random input */
+ result = ossl_seed(data);
+ if(result)
+ return result;
+
+ ssl_config->certverifyresult = !X509_V_OK;
+
+ result = ossl_init_method(cf, data, peer, &req_method, &ssl_version_min);
+ if(result)
+ return result;
+ DEBUGASSERT(req_method);
DEBUGASSERT(!octx->ssl_ctx);
- octx->ssl_ctx = SSL_CTX_new(req_method);
+ octx->ssl_ctx =
+#ifdef OPENSSL_HAS_PROVIDERS
+ data->state.libctx ?
+ SSL_CTX_new_ex(data->state.libctx, data->state.propq, req_method):
+#endif
+ SSL_CTX_new(req_method);
if(!octx->ssl_ctx) {
failf(data, "SSL: could not create a context: %s",
@@ -3606,7 +4050,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
CVE-2010-4180 when using previous OpenSSL versions we no longer enable
this option regardless of OpenSSL version and SSL_OP_ALL definition.
- OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability:
+ OpenSSL added a work-around for an SSL 3.0/TLS 1.0 CBC vulnerability:
https://web.archive.org/web/20240114184648/openssl.org/~bodo/tls-cbc.txt.
In 0.9.6e they added a bit to SSL_OP_ALL that _disables_ that work-around
despite the fact that SSL_OP_ALL is documented to do "rather harmless"
@@ -3674,31 +4118,10 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
SSL_CTX_set_mode(octx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
#endif
-#ifdef HAS_ALPN
- if(alpn && alpn_len) {
- if(SSL_CTX_set_alpn_protos(octx->ssl_ctx, alpn, (int)alpn_len)) {
- failf(data, "Error setting ALPN");
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
-#endif
-
- if(ssl_cert || ssl_cert_blob || ssl_cert_type) {
- if(!result &&
- !cert_stuff(data, octx->ssl_ctx,
- ssl_cert, ssl_cert_blob, ssl_cert_type,
- ssl_config->key, ssl_config->key_blob,
- ssl_config->key_type, ssl_config->key_passwd))
- result = CURLE_SSL_CERTPROBLEM;
- if(result)
- /* failf() is already done in cert_stuff() */
- return result;
- }
-
ciphers = conn_config->cipher_list;
if(!ciphers && (peer->transport != TRNSPRT_QUIC))
ciphers = DEFAULT_CIPHER_SELECTION;
- if(ciphers) {
+ if(ciphers && (ssl_version_min < CURL_SSLVERSION_TLSv1_3)) {
if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, ciphers)) {
failf(data, "failed setting cipher list: %s", ciphers);
return CURLE_SSL_CIPHER;
@@ -3709,7 +4132,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
{
const char *ciphers13 = conn_config->cipher_list13;
- if(ciphers13) {
+ if(ciphers13 &&
+ (!conn_config->version_max ||
+ (conn_config->version_max >= CURL_SSLVERSION_MAX_TLSv1_3))) {
if(!SSL_CTX_set_ciphersuites(octx->ssl_ctx, ciphers13)) {
failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13);
return CURLE_SSL_CIPHER;
@@ -3719,21 +4144,51 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
}
#endif
+ if(ssl_cert || ssl_cert_blob || ssl_cert_type) {
+ if(!result &&
+ !cert_stuff(data, octx->ssl_ctx,
+ ssl_cert, ssl_cert_blob, ssl_cert_type,
+ ssl_config->key, ssl_config->key_blob,
+ ssl_config->key_type, ssl_config->key_passwd))
+ result = CURLE_SSL_CERTPROBLEM;
+ if(result)
+ /* failf() is already done in cert_stuff() */
+ return result;
+ }
+
#ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
/* OpenSSL 1.1.1 requires clients to opt-in for PHA */
SSL_CTX_set_post_handshake_auth(octx->ssl_ctx, 1);
#endif
-#ifdef HAVE_SSL_CTX_SET_EC_CURVES
{
const char *curves = conn_config->curves;
if(curves) {
- if(!SSL_CTX_set1_curves_list(octx->ssl_ctx, curves)) {
+#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+#define OSSL_CURVE_CAST(x) (x)
+#else
+#define OSSL_CURVE_CAST(x) (char *)CURL_UNCONST(x)
+#endif
+ if(!SSL_CTX_set1_curves_list(octx->ssl_ctx, OSSL_CURVE_CAST(curves))) {
failf(data, "failed setting curves list: '%s'", curves);
return CURLE_SSL_CIPHER;
}
}
}
+
+#ifdef HAVE_SSL_CTX_SET1_SIGALGS
+#define OSSL_SIGALG_CAST(x) OSSL_CURVE_CAST(x)
+ {
+ const char *signature_algorithms = conn_config->signature_algorithms;
+ if(signature_algorithms) {
+ if(!SSL_CTX_set1_sigalgs_list(octx->ssl_ctx,
+ OSSL_SIGALG_CAST(signature_algorithms))) {
+ failf(data, "failed setting signature algorithms: '%s'",
+ signature_algorithms);
+ return CURLE_SSL_CIPHER;
+ }
+ }
+ }
#endif
#ifdef USE_OPENSSL_SRP
@@ -3807,201 +4262,38 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
}
}
- /* Let's make an SSL structure */
- if(octx->ssl)
- SSL_free(octx->ssl);
- octx->ssl = SSL_new(octx->ssl_ctx);
- if(!octx->ssl) {
- failf(data, "SSL: could not create a context (handle)");
- return CURLE_OUT_OF_MEMORY;
- }
-
- SSL_set_app_data(octx->ssl, ssl_user_data);
-
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
- !defined(OPENSSL_NO_OCSP)
- if(conn_config->verifystatus)
- SSL_set_tlsext_status_type(octx->ssl, TLSEXT_STATUSTYPE_ocsp);
-#endif
-
-#if (defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)) && \
- defined(ALLOW_RENEG)
- SSL_set_renegotiate_mode(octx->ssl, ssl_renegotiate_freely);
-#endif
+ return ossl_init_ssl(octx, cf, data, peer, alpns_requested,
+ ssl_user_data, sess_reuse_cb);
+}
- SSL_set_connect_state(octx->ssl);
+static CURLcode ossl_on_session_reuse(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct alpn_spec *alpns,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ CURLcode result = CURLE_OK;
- octx->server_cert = 0x0;
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
- if(peer->sni) {
- if(!SSL_set_tlsext_host_name(octx->ssl, peer->sni)) {
- failf(data, "Failed set SNI");
- return CURLE_SSL_CONNECT_ERROR;
- }
+ *do_early_data = FALSE;
+ connssl->earlydata_max = scs->earlydata_max;
+ if(!connssl->earlydata_max) {
+ CURL_TRC_CF(data, cf, "SSL session does not allow earlydata");
}
-
-#ifdef USE_ECH
- if(ECH_ENABLED(data)) {
- unsigned char *ech_config = NULL;
- size_t ech_config_len = 0;
- char *outername = data->set.str[STRING_ECH_PUBLIC];
- int trying_ech_now = 0;
-
- if(data->set.tls_ech & CURLECH_GREASE) {
- infof(data, "ECH: will GREASE ClientHello");
-# ifdef OPENSSL_IS_BORINGSSL
- SSL_set_enable_ech_grease(octx->ssl, 1);
-# else
- SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE);
-# endif
- }
- else if(data->set.tls_ech & CURLECH_CLA_CFG) {
-# ifdef OPENSSL_IS_BORINGSSL
- /* have to do base64 decode here for boring */
- const char *b64 = data->set.str[STRING_ECH_CONFIG];
-
- if(!b64) {
- infof(data, "ECH: ECHConfig from command line empty");
- return CURLE_SSL_CONNECT_ERROR;
- }
- ech_config_len = 2 * strlen(b64);
- result = Curl_base64_decode(b64, &ech_config, &ech_config_len);
- if(result || !ech_config) {
- infof(data, "ECH: cannot base64 decode ECHConfig from command line");
- if(data->set.tls_ech & CURLECH_HARD)
- return result;
- }
- if(SSL_set1_ech_config_list(octx->ssl, ech_config,
- ech_config_len) != 1) {
- infof(data, "ECH: SSL_ECH_set1_echconfig failed");
- if(data->set.tls_ech & CURLECH_HARD) {
- free(ech_config);
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
- free(ech_config);
- trying_ech_now = 1;
-# else
- ech_config = (unsigned char *) data->set.str[STRING_ECH_CONFIG];
- if(!ech_config) {
- infof(data, "ECH: ECHConfig from command line empty");
- return CURLE_SSL_CONNECT_ERROR;
- }
- ech_config_len = strlen(data->set.str[STRING_ECH_CONFIG]);
- if(SSL_ech_set1_echconfig(octx->ssl, ech_config, ech_config_len) != 1) {
- infof(data, "ECH: SSL_ECH_set1_echconfig failed");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
- }
- else
- trying_ech_now = 1;
-# endif
- infof(data, "ECH: ECHConfig from command line");
- }
- else {
- struct Curl_dns_entry *dns = NULL;
-
- if(peer->hostname)
- dns = Curl_fetch_addr(data, peer->hostname, peer->port);
- if(!dns) {
- infof(data, "ECH: requested but no DNS info available");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
- }
- else {
- struct Curl_https_rrinfo *rinfo = NULL;
-
- rinfo = dns->hinfo;
- if(rinfo && rinfo->echconfiglist) {
- unsigned char *ecl = rinfo->echconfiglist;
- size_t elen = rinfo->echconfiglist_len;
-
- infof(data, "ECH: ECHConfig from DoH HTTPS RR");
-# ifndef OPENSSL_IS_BORINGSSL
- if(SSL_ech_set1_echconfig(octx->ssl, ecl, elen) != 1) {
- infof(data, "ECH: SSL_ECH_set1_echconfig failed");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
- }
-# else
- if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) {
- infof(data, "ECH: SSL_set1_ech_config_list failed (boring)");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
- }
-# endif
- else {
- trying_ech_now = 1;
- infof(data, "ECH: imported ECHConfigList of length %zu", elen);
- }
- }
- else {
- infof(data, "ECH: requested but no ECHConfig available");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
- }
- Curl_resolv_unlink(data, &dns);
- }
- }
-# ifdef OPENSSL_IS_BORINGSSL
- if(trying_ech_now && outername) {
- infof(data, "ECH: setting public_name not supported with BoringSSL");
- return CURLE_SSL_CONNECT_ERROR;
- }
-# else
- if(trying_ech_now && outername) {
- infof(data, "ECH: inner: '%s', outer: '%s'",
- peer->hostname ? peer->hostname : "NULL", outername);
- result = SSL_ech_set_server_names(octx->ssl,
- peer->hostname, outername,
- 0 /* do send outer */);
- if(result != 1) {
- infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result);
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
-# endif /* not BORING */
- if(trying_ech_now
- && SSL_set_min_proto_version(octx->ssl, TLS1_3_VERSION) != 1) {
- infof(data, "ECH: cannot force TLSv1.3 [ERROR]");
- return CURLE_SSL_CONNECT_ERROR;
- }
+ else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) {
+ CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data");
}
-#endif /* USE_ECH */
-
-#endif
-
- octx->reused_session = FALSE;
- if(ssl_config->primary.cache_session) {
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, peer, (void **)&der_sessionid,
- &der_sessionid_size, NULL)) {
- /* we got a session id, use it! */
- ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid,
- (long)der_sessionid_size);
- if(ssl_session) {
- if(!SSL_set_session(octx->ssl, ssl_session)) {
- Curl_ssl_sessionid_unlock(data);
- SSL_SESSION_free(ssl_session);
- failf(data, "SSL: SSL_set_session failed: %s",
- ossl_strerror(ERR_get_error(), error_buffer,
- sizeof(error_buffer)));
- return CURLE_SSL_CONNECT_ERROR;
- }
- SSL_SESSION_free(ssl_session);
- /* Informational message */
- infof(data, "SSL reusing session ID");
- octx->reused_session = TRUE;
- }
- else {
- Curl_ssl_sessionid_unlock(data);
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
- Curl_ssl_sessionid_unlock(data);
+ else {
+ infof(data, "SSL session allows %zu bytes of early data, "
+ "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn);
+ connssl->earlydata_state = ssl_earlydata_await;
+ connssl->state = ssl_connection_deferred;
+ result = Curl_alpn_set_negotiated(cf, data, connssl,
+ (const unsigned char *)scs->alpn,
+ scs->alpn ? strlen(scs->alpn) : 0);
+ *do_early_data = !result;
}
-
- return CURLE_OK;
+ return result;
}
static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
@@ -4009,26 +4301,16 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
{
struct ssl_connect_data *connssl = cf->ctx;
struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
- struct alpn_proto_buf proto;
BIO *bio;
CURLcode result;
DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
DEBUGASSERT(octx);
- memset(&proto, 0, sizeof(proto));
-#ifdef HAS_ALPN
- if(connssl->alpn) {
- result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
- if(result) {
- failf(data, "Error determining ALPN");
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
-#endif
- result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, TRNSPRT_TCP,
- proto.data, proto.len, NULL, NULL,
- ossl_new_session_cb, cf);
+ result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer,
+ connssl->alpn, NULL, NULL,
+ ossl_new_session_cb, cf,
+ ossl_on_session_reuse);
if(result)
return result;
@@ -4053,8 +4335,10 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
SSL_set_bio(octx->ssl, bio, bio);
#endif
-#ifdef HAS_ALPN
- if(connssl->alpn) {
+#ifdef HAS_ALPN_OPENSSL
+ if(connssl->alpn && (connssl->state != ssl_connection_deferred)) {
+ struct alpn_proto_buf proto;
+ memset(&proto, 0, sizeof(proto));
Curl_alpn_to_proto_str(&proto, connssl->alpn);
infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
@@ -4063,7 +4347,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
return CURLE_OK;
}
-#ifdef USE_ECH
+#ifdef USE_ECH_OPENSSL
/* If we have retry configs, then trace those out */
static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl,
int reason)
@@ -4071,7 +4355,7 @@ static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl,
CURLcode result = CURLE_OK;
size_t rcl = 0;
int rv = 1;
-# ifndef OPENSSL_IS_BORINGSSL
+# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
char *inner = NULL;
unsigned char *rcs = NULL;
char *outer = NULL;
@@ -4086,40 +4370,38 @@ static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl,
/* nothing to trace if not doing ECH */
if(!ECH_ENABLED(data))
return;
-# ifndef OPENSSL_IS_BORINGSSL
- rv = SSL_ech_get_retry_config(ssl, &rcs, &rcl);
+# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
+ rv = SSL_ech_get1_retry_config(ssl, &rcs, &rcl);
# else
SSL_get0_ech_retry_configs(ssl, &rcs, &rcl);
rv = (int)rcl;
# endif
if(rv && rcs) {
-# define HEXSTR_MAX 800
char *b64str = NULL;
size_t blen = 0;
- result = Curl_base64_encode((const char *)rcs, rcl,
- &b64str, &blen);
- if(!result && b64str)
+ result = curlx_base64_encode((const char *)rcs, rcl, &b64str, &blen);
+ if(!result && b64str) {
infof(data, "ECH: retry_configs %s", b64str);
- free(b64str);
-# ifndef OPENSSL_IS_BORINGSSL
- rv = SSL_ech_get_status(ssl, &inner, &outer);
- infof(data, "ECH: retry_configs for %s from %s, %d %d",
- inner ? inner : "NULL", outer ? outer : "NULL", reason, rv);
+ free(b64str);
+#if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
+ rv = SSL_ech_get1_status(ssl, &inner, &outer);
+ infof(data, "ECH: retry_configs for %s from %s, %d %d",
+ inner ? inner : "NULL", outer ? outer : "NULL", reason, rv);
#else
- rv = SSL_ech_accepted(ssl);
- servername_type = SSL_get_servername_type(ssl);
- inner = SSL_get_servername(ssl, servername_type);
- SSL_get0_ech_name_override(ssl, &outer, &out_name_len);
- /* TODO: get the inner from boring */
- infof(data, "ECH: retry_configs for %s from %s, %d %d",
- inner ? inner : "NULL", outer ? outer : "NULL", reason, rv);
+ rv = SSL_ech_accepted(ssl);
+ servername_type = SSL_get_servername_type(ssl);
+ inner = SSL_get_servername(ssl, servername_type);
+ SSL_get0_ech_name_override(ssl, &outer, &out_name_len);
+ infof(data, "ECH: retry_configs for %s from %s, %d %d",
+ inner ? inner : "NULL", outer ? outer : "NULL", reason, rv);
#endif
+ }
}
else
infof(data, "ECH: no retry_configs (rv = %d)", rv);
-# ifndef OPENSSL_IS_BORINGSSL
+# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
OPENSSL_free((void *)rcs);
# endif
return;
@@ -4169,27 +4451,25 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
if(SSL_ERROR_WANT_READ == detail) {
CURL_TRC_CF(data, cf, "SSL_connect() -> want recv");
connssl->io_need = CURL_SSL_IO_NEED_RECV;
- return CURLE_OK;
+ return CURLE_AGAIN;
}
if(SSL_ERROR_WANT_WRITE == detail) {
CURL_TRC_CF(data, cf, "SSL_connect() -> want send");
connssl->io_need = CURL_SSL_IO_NEED_SEND;
- return CURLE_OK;
+ return CURLE_AGAIN;
}
#ifdef SSL_ERROR_WANT_ASYNC
if(SSL_ERROR_WANT_ASYNC == detail) {
CURL_TRC_CF(data, cf, "SSL_connect() -> want async");
connssl->io_need = CURL_SSL_IO_NEED_RECV;
- connssl->connecting_state = ssl_connect_2;
- return CURLE_OK;
+ return CURLE_AGAIN;
}
#endif
#ifdef SSL_ERROR_WANT_RETRY_VERIFY
if(SSL_ERROR_WANT_RETRY_VERIFY == detail) {
CURL_TRC_CF(data, cf, "SSL_connect() -> want retry_verify");
connssl->io_need = CURL_SSL_IO_NEED_RECV;
- connssl->connecting_state = ssl_connect_2;
- return CURLE_OK;
+ return CURLE_AGAIN;
}
#endif
else {
@@ -4220,16 +4500,13 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
lerr = SSL_get_verify_result(octx->ssl);
if(lerr != X509_V_OK) {
ssl_config->certverifyresult = lerr;
- msnprintf(error_buffer, sizeof(error_buffer),
- "SSL certificate problem: %s",
- X509_verify_cert_error_string(lerr));
+ failf(data, "SSL certificate problem: %s",
+ X509_verify_cert_error_string(lerr));
}
- else {
+ else
failf(data, "%s", "SSL certificate verification failed");
- return result;
- }
}
-#if defined(SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)
+#ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED
/* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on
OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */
else if((lib == ERR_LIB_SSL) &&
@@ -4241,9 +4518,9 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)));
}
#endif
-#ifdef USE_ECH
+#ifdef USE_ECH_OPENSSL
else if((lib == ERR_LIB_SSL) &&
-# ifndef OPENSSL_IS_BORINGSSL
+# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
(reason == SSL_R_ECH_REQUIRED)) {
# else
(reason == SSL_R_ECH_REJECTED)) {
@@ -4278,7 +4555,6 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ",
extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
connssl->peer.hostname, connssl->peer.port);
- return result;
}
return result;
@@ -4308,14 +4584,14 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
negotiated_group_name ? negotiated_group_name : "[blank]",
OBJ_nid2sn(psigtype_nid));
-#ifdef USE_ECH
-# ifndef OPENSSL_IS_BORINGSSL
+#ifdef USE_ECH_OPENSSL
+# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
if(ECH_ENABLED(data)) {
char *inner = NULL, *outer = NULL;
const char *status = NULL;
int rv;
- rv = SSL_ech_get_status(octx->ssl, &inner, &outer);
+ rv = SSL_ech_get1_status(octx->ssl, &inner, &outer);
switch(rv) {
case SSL_ECH_STATUS_SUCCESS:
status = "succeeded";
@@ -4367,10 +4643,10 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
else {
infof(data, "ECH: result: status is not attempted");
}
-# endif /* BORING */
-#endif /* USE_ECH */
+# endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */
+#endif /* USE_ECH_OPENSSL */
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_OPENSSL
/* Sets data and len to negotiated protocol, len is 0 if no protocol was
* negotiated
*/
@@ -4496,7 +4772,7 @@ static void infof_certstack(struct Curl_easy *data, const SSL *ssl)
sizeof(group_name), NULL);
msnprintf(group_name_final, sizeof(group_name_final), "/%s", group_name);
}
- type_name = EVP_PKEY_get0_type_name(current_pkey);
+ type_name = current_pkey ? EVP_PKEY_get0_type_name(current_pkey) : NULL;
#else
get_group_name = 0;
type_name = NULL;
@@ -4536,7 +4812,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
DEBUGASSERT(octx);
- Curl_dyn_init(&dname, MAX_CERT_NAME_LENGTH);
+ curlx_dyn_init(&dname, MAX_CERT_NAME_LENGTH);
if(!mem) {
failf(data,
@@ -4566,19 +4842,20 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
result = x509_name_oneline(X509_get_subject_name(octx->server_cert),
&dname);
- infof(data, " subject: %s", result ? "[NONE]" : Curl_dyn_ptr(&dname));
+ infof(data, " subject: %s", result ? "[NONE]" : curlx_dyn_ptr(&dname));
#ifndef CURL_DISABLE_VERBOSE_STRINGS
{
+ char *buf;
long len;
ASN1_TIME_print(mem, X509_get0_notBefore(octx->server_cert));
- len = BIO_get_mem_data(mem, (char **) &ptr);
- infof(data, " start date: %.*s", (int)len, ptr);
+ len = BIO_get_mem_data(mem, (char **) &buf);
+ infof(data, " start date: %.*s", (int)len, buf);
(void)BIO_reset(mem);
ASN1_TIME_print(mem, X509_get0_notAfter(octx->server_cert));
- len = BIO_get_mem_data(mem, (char **) &ptr);
- infof(data, " expire date: %.*s", (int)len, ptr);
+ len = BIO_get_mem_data(mem, (char **) &buf);
+ infof(data, " expire date: %.*s", (int)len, buf);
(void)BIO_reset(mem);
}
#endif
@@ -4590,7 +4867,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
if(result) {
X509_free(octx->server_cert);
octx->server_cert = NULL;
- Curl_dyn_free(&dname);
+ curlx_dyn_free(&dname);
return result;
}
}
@@ -4603,8 +4880,8 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
result = CURLE_PEER_FAILED_VERIFICATION;
}
else {
- infof(data, " issuer: %s", Curl_dyn_ptr(&dname));
- Curl_dyn_free(&dname);
+ infof(data, " issuer: %s", curlx_dyn_ptr(&dname));
+ curlx_dyn_free(&dname);
/* We could do all sorts of certificate verification stuff here before
deallocating the certificate. */
@@ -4699,27 +4976,11 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
}
infof_certstack(data, octx->ssl);
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
- !defined(OPENSSL_NO_OCSP)
+#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP)
if(conn_config->verifystatus && !octx->reused_session) {
/* do not do this after Session ID reuse */
result = verifystatus(cf, data, octx);
if(result) {
- /* when verifystatus failed, remove the session id from the cache again
- if present */
- if(!Curl_ssl_cf_is_proxy(cf)) {
- void *old_ssl_sessionid = NULL;
- bool incache;
- Curl_ssl_sessionid_lock(data);
- incache = !(Curl_ssl_getsessionid(cf, data, peer,
- &old_ssl_sessionid, NULL, NULL));
- if(incache) {
- infof(data, "Remove session ID again from cache");
- Curl_ssl_delsessionid(data, old_ssl_sessionid);
- }
- Curl_ssl_sessionid_unlock(data);
- }
-
X509_free(octx->server_cert);
octx->server_cert = NULL;
return result;
@@ -4767,134 +5028,169 @@ static CURLcode ossl_connect_step3(struct Curl_cfilter *cf,
*/
result = Curl_oss_check_peer_cert(cf, data, octx, &connssl->peer);
- if(!result)
- connssl->connecting_state = ssl_connect_done;
+ if(result)
+ /* on error, remove sessions we might have in the pool */
+ Curl_ssl_scache_remove_all(cf, data, connssl->peer.scache_key);
return result;
}
-static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool nonblocking,
- bool *done)
+#ifdef HAVE_OPENSSL_EARLYDATA
+static CURLcode ossl_send_earlydata(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
+ CURLcode result = CURLE_OK;
+ const unsigned char *buf;
+ size_t blen, nwritten;
+ int rc;
+
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sending);
+ octx->io_result = CURLE_OK;
+ while(Curl_bufq_peek(&connssl->earlydata, &buf, &blen)) {
+ nwritten = 0;
+ rc = SSL_write_early_data(octx->ssl, buf, blen, &nwritten);
+ CURL_TRC_CF(data, cf, "SSL_write_early_data(len=%zu) -> %d, %zu",
+ blen, rc, nwritten);
+ if(rc <= 0) {
+ long sslerror;
+ char error_buffer[256];
+ int err = SSL_get_error(octx->ssl, rc);
+
+ switch(err) {
+ case SSL_ERROR_WANT_READ:
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
+ result = CURLE_AGAIN;
+ goto out;
+ case SSL_ERROR_WANT_WRITE:
+ connssl->io_need = CURL_SSL_IO_NEED_SEND;
+ result = CURLE_AGAIN;
+ goto out;
+ case SSL_ERROR_SYSCALL: {
+ int sockerr = SOCKERRNO;
+
+ if(octx->io_result == CURLE_AGAIN) {
+ result = CURLE_AGAIN;
+ goto out;
+ }
+ sslerror = ERR_get_error();
+ if(sslerror)
+ ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
+ else if(sockerr)
+ Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
+ else
+ msnprintf(error_buffer, sizeof(error_buffer), "%s",
+ SSL_ERROR_to_str(err));
+
+ failf(data, OSSL_PACKAGE " SSL_write:early_data: %s, errno %d",
+ error_buffer, sockerr);
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ case SSL_ERROR_SSL: {
+ /* A failure in the SSL library occurred, usually a protocol error.
+ The OpenSSL error queue contains more information on the error. */
+ sslerror = ERR_get_error();
+ failf(data, "SSL_write_early_data() error: %s",
+ ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)));
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ default:
+ /* a true error */
+ failf(data, OSSL_PACKAGE " SSL_write_early_data: %s, errno %d",
+ SSL_ERROR_to_str(err), SOCKERRNO);
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ }
+ Curl_bufq_skip(&connssl->earlydata, nwritten);
+ }
+ /* sent everything there was */
+ infof(data, "SSL sending %zu bytes of early data", connssl->earlydata_skip);
+out:
+ return result;
+}
+#endif /* HAVE_OPENSSL_EARLYDATA */
+
+static CURLcode ossl_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
{
CURLcode result = CURLE_OK;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
- int what;
- connssl->io_need = CURL_SSL_IO_NEED_NONE;
/* 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 are allowed */
- const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time is already up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
+ *done = FALSE;
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
+ if(ssl_connect_1 == connssl->connecting_state) {
+ CURL_TRC_CF(data, cf, "ossl_connect, step1");
result = ossl_connect_step1(cf, data);
if(result)
goto out;
}
- while(ssl_connect_2 == connssl->connecting_state) {
-
- /* check allowed time left */
- const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- result = CURLE_OPERATION_TIMEDOUT;
+ if(ssl_connect_2 == connssl->connecting_state) {
+ CURL_TRC_CF(data, cf, "ossl_connect, step2");
+#ifdef HAVE_OPENSSL_EARLYDATA
+ if(connssl->earlydata_state == ssl_earlydata_await) {
goto out;
}
-
- /* if ssl is expecting something, check if it is available. */
- if(!nonblocking && connssl->io_need) {
- curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
- sockfd : CURL_SOCKET_BAD;
- curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
- sockfd : CURL_SOCKET_BAD;
-
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- timeout_ms);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- result = CURLE_SSL_CONNECT_ERROR;
- goto out;
- }
- if(0 == what) {
- /* timeout */
- failf(data, "SSL connection timeout");
- result = CURLE_OPERATION_TIMEDOUT;
+ else if(connssl->earlydata_state == ssl_earlydata_sending) {
+ result = ossl_send_earlydata(cf, data);
+ if(result)
goto out;
- }
- /* socket is readable or writable */
+ connssl->earlydata_state = ssl_earlydata_sent;
}
+#endif
+ DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) ||
+ (connssl->earlydata_state == ssl_earlydata_sent));
- /* Run transaction, and return to the caller if it failed or if this
- * connection is done nonblocking and this loop would execute again. This
- * permits the owner of a multi handle to abort a connection attempt
- * before step2 has completed while ensuring that a client using select()
- * or epoll() will always have a valid fdset to wait on.
- */
result = ossl_connect_step2(cf, data);
- if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
+ if(result)
goto out;
-
- } /* repeat step2 until all transactions are done. */
+ }
if(ssl_connect_3 == connssl->connecting_state) {
+ CURL_TRC_CF(data, cf, "ossl_connect, step3");
result = ossl_connect_step3(cf, data);
if(result)
goto out;
+ connssl->connecting_state = ssl_connect_done;
+#ifdef HAVE_OPENSSL_EARLYDATA
+ if(connssl->earlydata_state > ssl_earlydata_none) {
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
+ /* We should be in this state by now */
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sent);
+ connssl->earlydata_state =
+ (SSL_get_early_data_status(octx->ssl) == SSL_EARLY_DATA_ACCEPTED) ?
+ ssl_earlydata_accepted : ssl_earlydata_rejected;
+ }
+#endif
}
if(ssl_connect_done == connssl->connecting_state) {
+ CURL_TRC_CF(data, cf, "ossl_connect, done");
connssl->state = ssl_connection_complete;
- *done = TRUE;
}
- else
- *done = FALSE;
-
- /* Reset our connect state machine */
- connssl->connecting_state = ssl_connect_1;
out:
+ if(result == CURLE_AGAIN) {
+ *done = FALSE;
+ return CURLE_OK;
+ }
+ *done = ((connssl->state == ssl_connection_complete) ||
+ (connssl->state == ssl_connection_deferred));
return result;
}
-static CURLcode ossl_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
-{
- return ossl_connect_common(cf, data, TRUE, done);
-}
-
-static CURLcode ossl_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- CURLcode result;
- bool done = FALSE;
-
- result = ossl_connect_common(cf, data, FALSE, &done);
- if(result)
- return result;
-
- DEBUGASSERT(done);
-
- return CURLE_OK;
-}
-
static bool ossl_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
@@ -5168,10 +5464,10 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex,
}
/* Append "tls-server-end-point:" */
- if(Curl_dyn_addn(binding, prefix, sizeof(prefix) - 1) != CURLE_OK)
+ if(curlx_dyn_addn(binding, prefix, sizeof(prefix) - 1) != CURLE_OK)
return CURLE_OUT_OF_MEMORY;
/* Append digest */
- if(Curl_dyn_addn(binding, buf, length))
+ if(curlx_dyn_addn(binding, buf, length))
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
@@ -5184,10 +5480,9 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex,
#endif
}
-static size_t ossl_version(char *buffer, size_t size)
+size_t Curl_ossl_version(char *buffer, size_t size)
{
#ifdef LIBRESSL_VERSION_NUMBER
-#ifdef HAVE_OPENSSL_VERSION
char *p;
size_t count;
const char *ver = OpenSSL_version(OPENSSL_VERSION);
@@ -5201,13 +5496,6 @@ static size_t ossl_version(char *buffer, size_t size)
*p = '_';
}
return count;
-#else
- return msnprintf(buffer, size, "%s/%lx.%lx.%lx",
- OSSL_PACKAGE,
- (LIBRESSL_VERSION_NUMBER >> 28) & 0xf,
- (LIBRESSL_VERSION_NUMBER >> 20) & 0xff,
- (LIBRESSL_VERSION_NUMBER >> 12) & 0xff);
-#endif
#elif defined(OPENSSL_IS_BORINGSSL)
#ifdef CURL_BORINGSSL_VERSION
return msnprintf(buffer, size, "%s/%s",
@@ -5231,25 +5519,19 @@ static size_t ossl_version(char *buffer, size_t size)
sub[2]='\0';
sub[1]='\0';
ssleay_value = OpenSSL_version_num();
- if(ssleay_value < 0x906000) {
- ssleay_value = SSLEAY_VERSION_NUMBER;
- sub[0]='\0';
- }
- else {
- if(ssleay_value&0xff0) {
- int minor_ver = (ssleay_value >> 4) & 0xff;
- if(minor_ver > 26) {
- /* handle extended version introduced for 0.9.8za */
- sub[1] = (char) ((minor_ver - 1) % 26 + 'a' + 1);
- sub[0] = 'z';
- }
- else {
- sub[0] = (char) (minor_ver + 'a' - 1);
- }
+ if(ssleay_value&0xff0) {
+ int minor_ver = (ssleay_value >> 4) & 0xff;
+ if(minor_ver > 26) {
+ /* handle extended version introduced for 0.9.8za */
+ sub[1] = (char) ((minor_ver - 1) % 26 + 'a' + 1);
+ sub[0] = 'z';
+ }
+ else {
+ sub[0] = (char) (minor_ver + 'a' - 1);
}
- else
- sub[0]='\0';
}
+ else
+ sub[0]='\0';
return msnprintf(buffer, size, "%s/%lx.%lx.%lx%s"
#ifdef OPENSSL_FIPS
@@ -5279,10 +5561,10 @@ static CURLcode ossl_random(struct Curl_easy *data,
}
/* RAND_bytes() returns 1 on success, 0 otherwise. */
rc = RAND_bytes(entropy, (ossl_valsize_t)curlx_uztosi(length));
- return (rc == 1 ? CURLE_OK : CURLE_FAILED_INIT);
+ return rc == 1 ? CURLE_OK : CURLE_FAILED_INIT;
}
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+#ifndef OPENSSL_NO_SHA256
static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */
size_t tmplen,
unsigned char *sha256sum /* output */,
@@ -5308,8 +5590,7 @@ static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */
static bool ossl_cert_status_request(void)
{
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
- !defined(OPENSSL_NO_OCSP)
+#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP)
return TRUE;
#else
return FALSE;
@@ -5337,7 +5618,10 @@ const struct Curl_ssl Curl_ssl_openssl = {
#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
SSLSUPP_TLS13_CIPHERSUITES |
#endif
-#ifdef USE_ECH
+#ifdef HAVE_SSL_CTX_SET1_SIGALGS
+ SSLSUPP_SIGNATURE_ALGORITHMS |
+#endif
+#ifdef USE_ECH_OPENSSL
SSLSUPP_ECH |
#endif
SSLSUPP_CA_CACHE |
@@ -5348,29 +5632,25 @@ const struct Curl_ssl Curl_ssl_openssl = {
ossl_init, /* init */
ossl_cleanup, /* cleanup */
- ossl_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
+ Curl_ossl_version, /* version */
ossl_shutdown, /* shutdown */
ossl_data_pending, /* data_pending */
ossl_random, /* random */
ossl_cert_status_request, /* cert_status_request */
ossl_connect, /* connect */
- ossl_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
ossl_get_internals, /* get_internals */
ossl_close, /* close_one */
ossl_close_all, /* close_all */
- ossl_set_engine, /* set_engine */
+ ossl_set_engine, /* set_engine or provider */
ossl_set_engine_default, /* set_engine_default */
ossl_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+ NULL, /* false_start */
+#ifndef OPENSSL_NO_SHA256
ossl_sha256sum, /* sha256sum */
#else
NULL, /* sha256sum */
#endif
- NULL, /* use of data in this connection */
- NULL, /* remote of data from this connection */
ossl_recv, /* recv decrypted data */
ossl_send, /* send data to encrypt */
ossl_get_channel_binding /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/openssl.h b/libs/libcurl/src/vtls/openssl.h
index e662e23a8d..1ca175c942 100644
--- a/libs/libcurl/src/vtls/openssl.h
+++ b/libs/libcurl/src/vtls/openssl.h
@@ -24,19 +24,43 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_OPENSSL
/*
* This header should only be needed to get included by vtls.c, openssl.c
* and ngtcp2.c
*/
+#include <openssl/opensslv.h>
#include <openssl/ossl_typ.h>
#include <openssl/ssl.h>
-#include "urldata.h"
+#include "../urldata.h"
-/* Struct to hold a Curl OpenSSL instance */
+/*
+ * Whether SSL_CTX_set_keylog_callback is available.
+ * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287
+ * BoringSSL: supported since d28f59c27bac (committed 2015-11-19)
+ * LibreSSL: not supported. 3.5.0+ has a stub function that does nothing.
+ */
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \
+ !defined(LIBRESSL_VERSION_NUMBER)) || \
+ defined(OPENSSL_IS_BORINGSSL)
+#define HAVE_KEYLOG_CALLBACK
+#endif
+
+/* Check for OpenSSL 1.1.1 which has early data support. */
+#undef HAVE_OPENSSL_EARLYDATA
+#if OPENSSL_VERSION_NUMBER >= 0x10100010L && defined(TLS1_3_VERSION) && \
+ !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
+#define HAVE_OPENSSL_EARLYDATA
+#endif
+
+struct alpn_spec;
+struct ssl_peer;
+struct Curl_ssl_session;
+
+/* Struct to hold a curl OpenSSL instance */
struct ossl_ctx {
/* these ones requires specific SSL-types */
SSL_CTX* ssl_ctx;
@@ -53,22 +77,29 @@ struct ossl_ctx {
BIT(reused_session); /* session-ID was reused for this */
};
+size_t Curl_ossl_version(char *buffer, size_t size);
+
typedef CURLcode Curl_ossl_ctx_setup_cb(struct Curl_cfilter *cf,
struct Curl_easy *data,
void *user_data);
typedef int Curl_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid);
+typedef CURLcode Curl_ossl_init_session_reuse_cb(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct alpn_spec *alpns,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data);
CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- int transport, /* TCP or QUIC */
- const unsigned char *alpn, size_t alpn_len,
+ const struct alpn_spec *alpns,
Curl_ossl_ctx_setup_cb *cb_setup,
void *cb_user_data,
Curl_ossl_new_session_cb *cb_new_session,
- void *ssl_user_data);
+ void *ssl_user_data,
+ Curl_ossl_init_session_reuse_cb *sess_reuse_cb);
#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
#define SSL_get1_peer_certificate SSL_get_peer_certificate
@@ -94,8 +125,12 @@ CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf,
*/
CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
- const struct ssl_peer *peer,
- SSL_SESSION *ssl_sessionid);
+ const char *ssl_peer_key,
+ SSL_SESSION *ssl_sessionid,
+ int ietf_tls_id,
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len);
/*
* Get the server cert, verify it and show it, etc., only call failf() if
diff --git a/libs/libcurl/src/vtls/rustls.c b/libs/libcurl/src/vtls/rustls.c
index b10328d4aa..4876175212 100644
--- a/libs/libcurl/src/vtls/rustls.c
+++ b/libs/libcurl/src/vtls/rustls.c
@@ -8,6 +8,7 @@
* Copyright (C) Jacob Hoffman-Andrews,
* <github@hoffman-andrews.com>
* Copyright (C) kpcyrd, <kpcyrd@archlinux.org>
+ * Copyright (C) Daniel McCarney, <daniel@binaryparadox.net>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -23,27 +24,24 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_RUSTLS
-#include "curl_printf.h"
+#include "../curl_printf.h"
-#include <errno.h>
#include <rustls.h>
-#include "inet_pton.h"
-#include "urldata.h"
-#include "sendf.h"
+#include "../curlx/inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
#include "vtls.h"
#include "vtls_int.h"
#include "rustls.h"
-#include "select.h"
-#include "strerror.h"
-#include "multiif.h"
-#include "connect.h" /* for the connect timeout */
+#include "keylog.h"
+#include "../strerror.h"
#include "cipher_suite.h"
-#include "rand.h"
+#include "x509asn1.h"
struct rustls_ssl_backend_data
{
@@ -55,7 +53,7 @@ struct rustls_ssl_backend_data
};
/* For a given rustls_result error code, return the best-matching CURLcode. */
-static CURLcode map_error(rustls_result r)
+static CURLcode map_error(const rustls_result r)
{
if(rustls_result_is_cert_error(r)) {
return CURLE_PEER_FAILED_VERIFICATION;
@@ -70,10 +68,19 @@ static CURLcode map_error(rustls_result r)
}
}
+static void
+rustls_failf(struct Curl_easy *data, const rustls_result rr, const char *msg)
+{
+ char errorbuf[STRERROR_LEN];
+ size_t errorlen;
+ rustls_error(rr, errorbuf, sizeof(errorbuf), &errorlen);
+ failf(data, "%s: %.*s", msg, (int)errorlen, errorbuf);
+}
+
static bool
cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data)
{
- struct ssl_connect_data *ctx = cf->ctx;
+ const struct ssl_connect_data *ctx = cf->ctx;
struct rustls_ssl_backend_data *backend;
(void)data;
@@ -90,7 +97,7 @@ struct io_ctx {
static int
read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
{
- struct io_ctx *io_ctx = userdata;
+ const struct io_ctx *io_ctx = userdata;
struct ssl_connect_data *const connssl = io_ctx->cf->ctx;
CURLcode result;
int ret = 0;
@@ -98,6 +105,7 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
(char *)buf, len, &result);
if(nread < 0) {
nread = 0;
+ /* !checksrc! disable ERRNOVAR 4 */
if(CURLE_AGAIN == result)
ret = EAGAIN;
else
@@ -114,7 +122,7 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
static int
write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
{
- struct io_ctx *io_ctx = userdata;
+ const struct io_ctx *io_ctx = userdata;
CURLcode result;
int ret = 0;
ssize_t nwritten = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data,
@@ -136,7 +144,7 @@ write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
static ssize_t tls_recv_more(struct Curl_cfilter *cf,
struct Curl_easy *data, CURLcode *err)
{
- struct ssl_connect_data *const connssl = cf->ctx;
+ const struct ssl_connect_data *const connssl = cf->ctx;
struct rustls_ssl_backend_data *const backend =
(struct rustls_ssl_backend_data *)connssl->backend;
struct io_ctx io_ctx;
@@ -162,11 +170,7 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf,
rresult = rustls_connection_process_new_packets(backend->conn);
if(rresult != RUSTLS_RESULT_OK) {
- char errorbuf[255];
- size_t errorlen;
- rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_connection_process_new_packets: %.*s",
- (int)errorlen, errorbuf);
+ rustls_failf(data, rresult, "rustls_connection_process_new_packets");
*err = map_error(rresult);
return -1;
}
@@ -192,7 +196,7 @@ static ssize_t
cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *plainbuf, size_t plainlen, CURLcode *err)
{
- struct ssl_connect_data *const connssl = cf->ctx;
+ const struct ssl_connect_data *const connssl = cf->ctx;
struct rustls_ssl_backend_data *const backend =
(struct rustls_ssl_backend_data *)connssl->backend;
struct rustls_connection *rconn = NULL;
@@ -232,10 +236,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
}
else if(rresult != RUSTLS_RESULT_OK) {
/* n always equals 0 in this case, do not need to check it */
- char errorbuf[255];
- size_t errorlen;
- rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_connection_read: %.*s", (int)errorlen, errorbuf);
+ rustls_failf(data, rresult, "rustls_connection_read");
*err = CURLE_RECV_ERROR;
nread = -1;
goto out;
@@ -278,7 +279,6 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data,
rustls_io_result io_error;
size_t tlswritten = 0;
size_t tlswritten_total = 0;
- CURLcode result = CURLE_OK;
io_ctx.cf = cf;
io_ctx.data = data;
@@ -304,7 +304,7 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data,
CURL_TRC_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten);
tlswritten_total += tlswritten;
}
- return result;
+ return CURLE_OK;
}
/*
@@ -321,14 +321,11 @@ static ssize_t
cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *plainbuf, size_t plainlen, CURLcode *err)
{
- struct ssl_connect_data *const connssl = cf->ctx;
+ const struct ssl_connect_data *const connssl = cf->ctx;
struct rustls_ssl_backend_data *const backend =
(struct rustls_ssl_backend_data *)connssl->backend;
struct rustls_connection *rconn = NULL;
size_t plainwritten = 0;
- rustls_result rresult;
- char errorbuf[256];
- size_t errorlen;
const unsigned char *buf = plainbuf;
size_t blen = plainlen;
ssize_t nwritten = 0;
@@ -360,11 +357,11 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
}
if(blen > 0) {
+ rustls_result rresult;
CURL_TRC_CF(data, cf, "cf_send: adding %zu plain bytes to Rustls", blen);
rresult = rustls_connection_write(rconn, buf, blen, &plainwritten);
if(rresult != RUSTLS_RESULT_OK) {
- rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_connection_write: %.*s", (int)errorlen, errorbuf);
+ rustls_failf(data, rresult, "rustls_connection_write");
*err = CURLE_WRITE_ERROR;
return -1;
}
@@ -418,9 +415,9 @@ read_file_into(const char *filename,
while(!feof(f)) {
uint8_t buf[256];
- size_t rr = fread(buf, 1, sizeof(buf), f);
+ const size_t rr = fread(buf, 1, sizeof(buf), f);
if(rr == 0 ||
- CURLE_OK != Curl_dyn_addn(out, buf, rr)) {
+ CURLE_OK != curlx_dyn_addn(out, buf, rr)) {
fclose(f);
return 0;
}
@@ -436,8 +433,8 @@ cr_get_selected_ciphers(struct Curl_easy *data,
const struct rustls_supported_ciphersuite **selected,
size_t *selected_size)
{
- size_t supported_len = *selected_size;
- size_t default_len = rustls_default_crypto_provider_ciphersuites_len();
+ const size_t supported_len = *selected_size;
+ const size_t default_len = rustls_default_crypto_provider_ciphersuites_len();
const struct rustls_supported_ciphersuite *entry;
const char *ciphers = ciphers12;
size_t count = 0, default13_count = 0, i, j;
@@ -522,247 +519,608 @@ add_ciphers:
*selected_size = count;
}
+static void
+cr_keylog_log_cb(struct rustls_str label,
+ const uint8_t *client_random, size_t client_random_len,
+ const uint8_t *secret, size_t secret_len)
+{
+ char clabel[KEYLOG_LABEL_MAXLEN];
+ (void)client_random_len;
+ DEBUGASSERT(client_random_len == CLIENT_RANDOM_SIZE);
+ /* Turning a "rustls_str" into a null delimited "c" string */
+ msnprintf(clabel, label.len + 1, "%.*s", (int)label.len, label.data);
+ Curl_tls_keylog_write(clabel, client_random, secret, secret_len);
+}
+
static CURLcode
-cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
- struct rustls_ssl_backend_data *const backend)
+init_config_builder(struct Curl_easy *data,
+ const struct ssl_primary_config *conn_config,
+ struct rustls_client_config_builder **config_builder)
{
- struct ssl_connect_data *connssl = cf->ctx;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ const struct rustls_supported_ciphersuite **cipher_suites = NULL;
struct rustls_crypto_provider_builder *custom_provider_builder = NULL;
const struct rustls_crypto_provider *custom_provider = NULL;
- struct rustls_connection *rconn = NULL;
- struct rustls_client_config_builder *config_builder = NULL;
- const struct rustls_root_cert_store *roots = NULL;
- struct rustls_root_cert_store_builder *roots_builder = NULL;
- struct rustls_web_pki_server_cert_verifier_builder *verifier_builder = NULL;
- struct rustls_server_cert_verifier *server_cert_verifier = NULL;
- const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
- const char * const ssl_cafile =
- /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
- (ca_info_blob ? NULL : conn_config->CAfile);
- const bool verifypeer = conn_config->verifypeer;
- char errorbuf[256];
- size_t errorlen;
- rustls_result result;
- DEBUGASSERT(backend);
- rconn = backend->conn;
+ uint16_t tls_versions[2] = {
+ RUSTLS_TLS_VERSION_TLSV1_2,
+ RUSTLS_TLS_VERSION_TLSV1_3,
+ };
+ size_t tls_versions_len = 2;
+ size_t cipher_suites_len =
+ rustls_default_crypto_provider_ciphersuites_len();
- {
- uint16_t tls_versions[2] = {
- RUSTLS_TLS_VERSION_TLSV1_2,
- RUSTLS_TLS_VERSION_TLSV1_3,
- };
- size_t tls_versions_len = 2;
- const struct rustls_supported_ciphersuite **cipher_suites;
- size_t cipher_suites_len =
- rustls_default_crypto_provider_ciphersuites_len();
-
- switch(conn_config->version) {
- case CURL_SSLVERSION_DEFAULT:
- case CURL_SSLVERSION_TLSv1:
- case CURL_SSLVERSION_TLSv1_0:
- case CURL_SSLVERSION_TLSv1_1:
- case CURL_SSLVERSION_TLSv1_2:
- break;
- case CURL_SSLVERSION_TLSv1_3:
- tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
+ CURLcode result = CURLE_OK;
+ rustls_result rr;
+
+ switch(conn_config->version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ case CURL_SSLVERSION_TLSv1_0:
+ case CURL_SSLVERSION_TLSv1_1:
+ case CURL_SSLVERSION_TLSv1_2:
+ break;
+ case CURL_SSLVERSION_TLSv1_3:
+ tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
+ tls_versions_len = 1;
+ break;
+ default:
+ failf(data, "rustls: unsupported minimum TLS version value");
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto cleanup;
+ }
+
+ switch(conn_config->version_max) {
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ case CURL_SSLVERSION_MAX_NONE:
+ case CURL_SSLVERSION_MAX_TLSv1_3:
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_2:
+ if(tls_versions[0] == RUSTLS_TLS_VERSION_TLSV1_2) {
tls_versions_len = 1;
break;
- default:
- failf(data, "rustls: unsupported minimum TLS version value");
- return CURLE_BAD_FUNCTION_ARGUMENT;
}
+ FALLTHROUGH();
+ case CURL_SSLVERSION_MAX_TLSv1_1:
+ case CURL_SSLVERSION_MAX_TLSv1_0:
+ default:
+ failf(data, "rustls: unsupported maximum TLS version value");
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto cleanup;
+ }
- switch(conn_config->version_max) {
- case CURL_SSLVERSION_MAX_DEFAULT:
- case CURL_SSLVERSION_MAX_NONE:
- case CURL_SSLVERSION_MAX_TLSv1_3:
- break;
- case CURL_SSLVERSION_MAX_TLSv1_2:
- if(tls_versions[0] == RUSTLS_TLS_VERSION_TLSV1_2) {
- tls_versions_len = 1;
- break;
- }
- FALLTHROUGH();
- case CURL_SSLVERSION_MAX_TLSv1_1:
- case CURL_SSLVERSION_MAX_TLSv1_0:
- default:
- failf(data, "rustls: unsupported maximum TLS version value");
- return CURLE_BAD_FUNCTION_ARGUMENT;
- }
+#if defined(USE_ECH)
+ if(ECH_ENABLED(data)) {
+ tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
+ tls_versions_len = 1;
+ infof(data, "rustls: ECH enabled, forcing TLSv1.3");
+ }
+#endif /* USE_ECH */
- cipher_suites = malloc(sizeof(cipher_suites) * (cipher_suites_len));
- if(!cipher_suites)
- return CURLE_OUT_OF_MEMORY;
-
- cr_get_selected_ciphers(data,
- conn_config->cipher_list,
- conn_config->cipher_list13,
- cipher_suites, &cipher_suites_len);
- if(cipher_suites_len == 0) {
- failf(data, "rustls: no supported cipher in list");
- free(cipher_suites);
- return CURLE_SSL_CIPHER;
- }
+ cipher_suites = malloc(sizeof(cipher_suites) * (cipher_suites_len));
+ if(!cipher_suites) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto cleanup;
+ }
- result = rustls_crypto_provider_builder_new_from_default(
- &custom_provider_builder);
- if(result != RUSTLS_RESULT_OK) {
- failf(data,
- "rustls: failed to create crypto provider builder from default");
- return CURLE_SSL_CIPHER;
- }
+ cr_get_selected_ciphers(data,
+ conn_config->cipher_list,
+ conn_config->cipher_list13,
+ cipher_suites, &cipher_suites_len);
+ if(cipher_suites_len == 0) {
+ failf(data, "rustls: no supported cipher in list");
+ result = CURLE_SSL_CIPHER;
+ goto cleanup;
+ }
- result =
- rustls_crypto_provider_builder_set_cipher_suites(
- custom_provider_builder,
- cipher_suites,
- cipher_suites_len);
- if(result != RUSTLS_RESULT_OK) {
- failf(data,
- "rustls: failed to set ciphersuites for crypto provider builder");
- rustls_crypto_provider_builder_free(custom_provider_builder);
- return CURLE_SSL_CIPHER;
- }
+ rr = rustls_crypto_provider_builder_new_from_default(
+ &custom_provider_builder);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr,
+ "failed to create crypto provider builder from default");
+ result = CURLE_SSL_CIPHER;
+ goto cleanup;
+ }
- result = rustls_crypto_provider_builder_build(
- custom_provider_builder, &custom_provider);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to build custom crypto provider");
- rustls_crypto_provider_builder_free(custom_provider_builder);
- return CURLE_SSL_CIPHER;
- }
+ rr =
+ rustls_crypto_provider_builder_set_cipher_suites(
+ custom_provider_builder,
+ cipher_suites,
+ cipher_suites_len);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr,
+ "failed to set ciphersuites for crypto provider builder");
+ result = CURLE_SSL_CIPHER;
+ goto cleanup;
+ }
- result = rustls_client_config_builder_new_custom(custom_provider,
+ rr = rustls_crypto_provider_builder_build(
+ custom_provider_builder, &custom_provider);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to build custom crypto provider");
+ result = CURLE_SSL_CIPHER;
+ goto cleanup;
+ }
+
+ rr = rustls_client_config_builder_new_custom(custom_provider,
tls_versions,
tls_versions_len,
- &config_builder);
+ config_builder);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to create client config builder");
+ result = CURLE_SSL_CIPHER;
+ goto cleanup;
+ }
+
+cleanup:
+ if(cipher_suites) {
free(cipher_suites);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to create client config");
- return CURLE_SSL_CIPHER;
- }
}
+ if(custom_provider_builder) {
+ rustls_crypto_provider_builder_free(custom_provider_builder);
+ }
+ if(custom_provider) {
+ rustls_crypto_provider_free(custom_provider);
+ }
+ return result;
+}
- rustls_crypto_provider_builder_free(custom_provider_builder);
- rustls_crypto_provider_free(custom_provider);
+static void
+init_config_builder_alpn(struct Curl_easy *data,
+ const struct ssl_connect_data *connssl,
+ struct rustls_client_config_builder *config_builder) {
+ struct alpn_proto_buf proto;
+ rustls_slice_bytes alpn[ALPN_ENTRIES_MAX];
+ size_t i;
- if(connssl->alpn) {
- struct alpn_proto_buf proto;
- rustls_slice_bytes alpn[ALPN_ENTRIES_MAX];
- size_t i;
+ for(i = 0; i < connssl->alpn->count; ++i) {
+ alpn[i].data = (const uint8_t *)connssl->alpn->entries[i];
+ alpn[i].len = strlen(connssl->alpn->entries[i]);
+ }
+ rustls_client_config_builder_set_alpn_protocols(config_builder, alpn,
+ connssl->alpn->count);
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+}
- for(i = 0; i < connssl->alpn->count; ++i) {
- alpn[i].data = (const uint8_t *)connssl->alpn->entries[i];
- alpn[i].len = strlen(connssl->alpn->entries[i]);
- }
- rustls_client_config_builder_set_alpn_protocols(config_builder, alpn,
- connssl->alpn->count);
- Curl_alpn_to_proto_str(&proto, connssl->alpn);
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+static CURLcode
+init_config_builder_verifier_crl(
+ struct Curl_easy *data,
+ const struct ssl_primary_config *conn_config,
+ struct rustls_web_pki_server_cert_verifier_builder *builder)
+{
+ CURLcode result = CURLE_OK;
+ struct dynbuf crl_contents;
+ rustls_result rr;
+
+ curlx_dyn_init(&crl_contents, DYN_CRLFILE_SIZE);
+ if(!read_file_into(conn_config->CRLfile, &crl_contents)) {
+ failf(data, "rustls: failed to read revocation list file");
+ result = CURLE_SSL_CRL_BADFILE;
+ goto cleanup;
}
- if(!verifypeer) {
- rustls_client_config_builder_dangerous_set_certificate_verifier(
- config_builder, cr_verify_none);
+
+ rr = rustls_web_pki_server_cert_verifier_builder_add_crl(
+ builder,
+ curlx_dyn_uptr(&crl_contents),
+ curlx_dyn_len(&crl_contents));
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to parse revocation list");
+ result = CURLE_SSL_CRL_BADFILE;
+ goto cleanup;
}
- else if(ca_info_blob || ssl_cafile) {
- roots_builder = rustls_root_cert_store_builder_new();
-
- if(ca_info_blob) {
- /* Enable strict parsing only if verification is not disabled. */
- result = rustls_root_cert_store_builder_add_pem(roots_builder,
- ca_info_blob->data,
- ca_info_blob->len,
- verifypeer);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to parse trusted certificates from blob");
- rustls_root_cert_store_builder_free(roots_builder);
- rustls_client_config_builder_free(config_builder);
- return CURLE_SSL_CACERT_BADFILE;
- }
+
+cleanup:
+ curlx_dyn_free(&crl_contents);
+ return result;
+}
+
+static CURLcode
+init_config_builder_verifier(struct Curl_easy *data,
+ struct rustls_client_config_builder *builder,
+ const struct ssl_primary_config *conn_config,
+ const struct curl_blob *ca_info_blob,
+ const char * const ssl_cafile) {
+ const struct rustls_root_cert_store *roots = NULL;
+ struct rustls_root_cert_store_builder *roots_builder = NULL;
+ struct rustls_web_pki_server_cert_verifier_builder *verifier_builder = NULL;
+ struct rustls_server_cert_verifier *server_cert_verifier = NULL;
+ rustls_result rr = RUSTLS_RESULT_OK;
+ CURLcode result = CURLE_OK;
+
+ roots_builder = rustls_root_cert_store_builder_new();
+ if(ca_info_blob) {
+ rr = rustls_root_cert_store_builder_add_pem(roots_builder,
+ ca_info_blob->data,
+ ca_info_blob->len,
+ 1);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to parse trusted certificates from blob");
+
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
}
- else if(ssl_cafile) {
- /* Enable strict parsing only if verification is not disabled. */
- result = rustls_root_cert_store_builder_load_roots_from_file(
- roots_builder, ssl_cafile, verifypeer);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to load trusted certificates");
- rustls_root_cert_store_builder_free(roots_builder);
- rustls_client_config_builder_free(config_builder);
- return CURLE_SSL_CACERT_BADFILE;
- }
+ }
+ else if(ssl_cafile) {
+ rr = rustls_root_cert_store_builder_load_roots_from_file(roots_builder,
+ ssl_cafile,
+ 1);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to load trusted certificates");
+
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
}
+ }
- result = rustls_root_cert_store_builder_build(roots_builder, &roots);
- rustls_root_cert_store_builder_free(roots_builder);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to build trusted root certificate store");
- rustls_client_config_builder_free(config_builder);
- return CURLE_SSL_CACERT_BADFILE;
+ rr = rustls_root_cert_store_builder_build(roots_builder, &roots);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to build trusted root certificate store");
+ result = CURLE_SSL_CACERT_BADFILE;
+ }
+
+ verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots);
+
+ if(conn_config->CRLfile) {
+ result = init_config_builder_verifier_crl(data,
+ conn_config,
+ verifier_builder);
+ if(result != CURLE_OK) {
+ goto cleanup;
}
+ }
- verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots);
+ rr = rustls_web_pki_server_cert_verifier_builder_build(
+ verifier_builder, &server_cert_verifier);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to build certificate verifier");
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
+ }
+
+ rustls_client_config_builder_set_server_verifier(builder,
+ server_cert_verifier);
+cleanup:
+ if(roots_builder) {
+ rustls_root_cert_store_builder_free(roots_builder);
+ }
+ if(roots) {
rustls_root_cert_store_free(roots);
+ }
+ if(verifier_builder) {
+ rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
+ }
+ if(server_cert_verifier) {
+ rustls_server_cert_verifier_free(server_cert_verifier);
+ }
- if(conn_config->CRLfile) {
- struct dynbuf crl_contents;
- Curl_dyn_init(&crl_contents, SIZE_MAX);
- if(!read_file_into(conn_config->CRLfile, &crl_contents)) {
- failf(data, "rustls: failed to read revocation list file");
- Curl_dyn_free(&crl_contents);
- rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
- return CURLE_SSL_CRL_BADFILE;
- }
+ return result;
+}
- result = rustls_web_pki_server_cert_verifier_builder_add_crl(
- verifier_builder,
- Curl_dyn_uptr(&crl_contents),
- Curl_dyn_len(&crl_contents));
- Curl_dyn_free(&crl_contents);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to parse revocation list");
- rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
- return CURLE_SSL_CRL_BADFILE;
- }
+static CURLcode
+init_config_builder_platform_verifier(
+ struct Curl_easy *data,
+ struct rustls_client_config_builder *builder)
+{
+ struct rustls_server_cert_verifier *server_cert_verifier = NULL;
+ CURLcode result = CURLE_OK;
+ rustls_result rr;
+
+ rr = rustls_platform_server_cert_verifier(&server_cert_verifier);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to create platform certificate verifier");
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
+ }
+
+ rustls_client_config_builder_set_server_verifier(builder,
+ server_cert_verifier);
+
+cleanup:
+ if(server_cert_verifier) {
+ rustls_server_cert_verifier_free(server_cert_verifier);
+ }
+ return result;
+}
+
+static CURLcode
+init_config_builder_keylog(struct Curl_easy *data,
+ struct rustls_client_config_builder *builder)
+{
+ rustls_result rr;
+
+ Curl_tls_keylog_open();
+ if(!Curl_tls_keylog_enabled()) {
+ return CURLE_OK;
+ }
+
+ rr = rustls_client_config_builder_set_key_log(builder,
+ cr_keylog_log_cb,
+ NULL);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "rustls_client_config_builder_set_key_log");
+ Curl_tls_keylog_close();
+ return map_error(rr);
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode
+init_config_builder_client_auth(struct Curl_easy *data,
+ const struct ssl_primary_config *conn_config,
+ const struct ssl_config_data *ssl_config,
+ struct rustls_client_config_builder *builder)
+{
+ struct dynbuf cert_contents;
+ struct dynbuf key_contents;
+ rustls_result rr;
+ const struct rustls_certified_key *certified_key = NULL;
+ CURLcode result = CURLE_OK;
+
+ if(conn_config->clientcert && !ssl_config->key) {
+ failf(data, "rustls: must provide key with certificate '%s'",
+ conn_config->clientcert);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ else if(!conn_config->clientcert && ssl_config->key) {
+ failf(data, "rustls: must provide certificate with key '%s'",
+ conn_config->clientcert);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+
+ curlx_dyn_init(&cert_contents, DYN_CERTFILE_SIZE);
+ curlx_dyn_init(&key_contents, DYN_KEYFILE_SIZE);
+
+ if(!read_file_into(conn_config->clientcert, &cert_contents)) {
+ failf(data, "rustls: failed to read client certificate file: '%s'",
+ conn_config->clientcert);
+ result = CURLE_SSL_CERTPROBLEM;
+ goto cleanup;
+ }
+
+ if(!read_file_into(ssl_config->key, &key_contents)) {
+ failf(data, "rustls: failed to read key file: '%s'", ssl_config->key);
+ result = CURLE_SSL_CERTPROBLEM;
+ goto cleanup;
+ }
+
+ rr = rustls_certified_key_build(curlx_dyn_uptr(&cert_contents),
+ curlx_dyn_len(&cert_contents),
+ curlx_dyn_uptr(&key_contents),
+ curlx_dyn_len(&key_contents),
+ &certified_key);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "rustls: failed to build certified key");
+ result = CURLE_SSL_CERTPROBLEM;
+ goto cleanup;
+ }
+
+ rr = rustls_certified_key_keys_match(certified_key);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data,
+ rr,
+ "rustls: client certificate and keypair files do not match:");
+
+ result = CURLE_SSL_CERTPROBLEM;
+ goto cleanup;
+ }
+
+ rr = rustls_client_config_builder_set_certified_key(builder,
+ &certified_key,
+ 1);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "rustls: failed to set certified key");
+ result = CURLE_SSL_CERTPROBLEM;
+ goto cleanup;
+ }
+
+cleanup:
+ curlx_dyn_free(&cert_contents);
+ curlx_dyn_free(&key_contents);
+ if(certified_key) {
+ rustls_certified_key_free(certified_key);
+ }
+ return result;
+}
+
+#if defined(USE_ECH)
+static CURLcode
+init_config_builder_ech(struct Curl_easy *data,
+ const struct ssl_connect_data *connssl,
+ struct rustls_client_config_builder *builder)
+{
+ const rustls_hpke *hpke = rustls_supported_hpke();
+ unsigned char *ech_config = NULL;
+ size_t ech_config_len = 0;
+ struct Curl_dns_entry *dns = NULL;
+ struct Curl_https_rrinfo *rinfo = NULL;
+ CURLcode result = CURLE_OK;
+ rustls_result rr;
+
+ if(!hpke) {
+ failf(data,
+ "rustls: ECH unavailable, rustls-ffi built without "
+ "HPKE compatible crypto provider");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+
+ if(data->set.str[STRING_ECH_PUBLIC]) {
+ failf(data, "rustls: ECH outername not supported");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+
+ if(data->set.tls_ech == CURLECH_GREASE) {
+ rr = rustls_client_config_builder_enable_ech_grease(builder, hpke);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "rustls: failed to configure ECH GREASE");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
}
+ return CURLE_OK;
+ }
- result = rustls_web_pki_server_cert_verifier_builder_build(
- verifier_builder, &server_cert_verifier);
- rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to build certificate verifier");
- rustls_server_cert_verifier_free(server_cert_verifier);
+ if(data->set.tls_ech & CURLECH_CLA_CFG && data->set.str[STRING_ECH_CONFIG]) {
+ const char *b64 = data->set.str[STRING_ECH_CONFIG];
+ size_t decode_result;
+ if(!b64) {
+ infof(data, "rustls: ECHConfig from command line empty");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+ /* rustls-ffi expects the raw TLS encoded ECHConfigList bytes */
+ decode_result = curlx_base64_decode(b64, &ech_config, &ech_config_len);
+ if(decode_result || !ech_config) {
+ infof(data, "rustls: cannot base64 decode ECHConfig from command line");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+ }
+ else {
+ if(connssl->peer.hostname) {
+ dns = Curl_dnscache_get(data, connssl->peer.hostname,
+ connssl->peer.port, data->conn->ip_version);
+ }
+ if(!dns) {
+ failf(data, "rustls: ECH requested but no DNS info available");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+ rinfo = dns->hinfo;
+ if(!rinfo || !rinfo->echconfiglist) {
+ failf(data, "rustls: ECH requested but no ECHConfig available");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+ ech_config = rinfo->echconfiglist;
+ ech_config_len = rinfo->echconfiglist_len;
+ }
+
+ rr = rustls_client_config_builder_enable_ech(builder,
+ ech_config,
+ ech_config_len,
+ hpke);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "rustls: failed to configure ECH");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+cleanup:
+ /* if we base64 decoded, we can free now */
+ if(data->set.tls_ech & CURLECH_CLA_CFG && data->set.str[STRING_ECH_CONFIG]) {
+ free(ech_config);
+ }
+ if(dns) {
+ Curl_resolv_unlink(data, &dns);
+ }
+ return result;
+}
+#endif /* USE_ECH */
+
+static CURLcode
+cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
+ struct rustls_ssl_backend_data *const backend)
+{
+ const struct ssl_connect_data *connssl = cf->ctx;
+ const struct ssl_primary_config *conn_config =
+ Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct rustls_connection *rconn = NULL;
+ struct rustls_client_config_builder *config_builder = NULL;
+
+ const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+ const char * const ssl_cafile =
+ /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
+ (ca_info_blob ? NULL : conn_config->CAfile);
+ CURLcode result = CURLE_OK;
+ rustls_result rr;
+
+ DEBUGASSERT(backend);
+ rconn = backend->conn;
+
+ result = init_config_builder(data, conn_config, &config_builder);
+ if(result != CURLE_OK) {
+ return result;
+ }
+
+ if(connssl->alpn) {
+ init_config_builder_alpn(data, connssl, config_builder);
+ }
+
+ if(!conn_config->verifypeer) {
+ rustls_client_config_builder_dangerous_set_certificate_verifier(
+ config_builder, cr_verify_none);
+ }
+ else if(ssl_config->native_ca_store) {
+ result = init_config_builder_platform_verifier(data, config_builder);
+ if(result != CURLE_OK) {
+ rustls_client_config_builder_free(config_builder);
+ return result;
+ }
+ }
+ else if(ca_info_blob || ssl_cafile) {
+ result = init_config_builder_verifier(data,
+ config_builder,
+ conn_config,
+ ca_info_blob,
+ ssl_cafile);
+ if(result != CURLE_OK) {
rustls_client_config_builder_free(config_builder);
- return CURLE_SSL_CACERT_BADFILE;
+ return result;
}
+ }
- rustls_client_config_builder_set_server_verifier(config_builder,
- server_cert_verifier);
- rustls_server_cert_verifier_free(server_cert_verifier);
+ if(conn_config->clientcert || ssl_config->key) {
+ result = init_config_builder_client_auth(data,
+ conn_config,
+ ssl_config,
+ config_builder);
+ if(result != CURLE_OK) {
+ rustls_client_config_builder_free(config_builder);
+ return result;
+ }
+ }
+
+#if defined(USE_ECH)
+ if(ECH_ENABLED(data)) {
+ result = init_config_builder_ech(data, connssl, config_builder);
+ if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) {
+ rustls_client_config_builder_free(config_builder);
+ return result;
+ }
+ }
+#endif /* USE_ECH */
+
+ result = init_config_builder_keylog(data, config_builder);
+ if(result != CURLE_OK) {
+ rustls_client_config_builder_free(config_builder);
+ return result;
}
- result = rustls_client_config_builder_build(
+ rr = rustls_client_config_builder_build(
config_builder,
&backend->config);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to build client config");
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to build client config");
+ rustls_client_config_builder_free(config_builder);
rustls_client_config_free(backend->config);
return CURLE_SSL_CONNECT_ERROR;
}
DEBUGASSERT(rconn == NULL);
- result = rustls_client_connection_new(backend->config,
- connssl->peer.hostname, &rconn);
- if(result != RUSTLS_RESULT_OK) {
- rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_client_connection_new: %.*s", (int)errorlen, errorbuf);
+ rr = rustls_client_connection_new(backend->config,
+ connssl->peer.hostname,
+ &rconn);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, result, "rustls_client_connection_new");
return CURLE_COULDNT_CONNECT;
}
DEBUGASSERT(rconn);
rustls_connection_set_userdata(rconn, backend);
backend->conn = rconn;
- return CURLE_OK;
+
+ return result;
}
static void
@@ -779,48 +1137,36 @@ cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data,
/* Given an established network connection, do a TLS handshake.
*
- * If `blocking` is true, this function will block until the handshake is
- * complete. Otherwise it will return as soon as I/O would block.
- *
- * For the non-blocking I/O case, this function will set `*done` to true
- * once the handshake is complete. This function never reads the value of
- * `*done*`.
+ * This function will set `*done` to true once the handshake is complete.
+ * This function never reads the value of `*done*`.
*/
static CURLcode
-cr_connect_common(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool blocking,
- bool *done)
+cr_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data, bool *done)
{
struct ssl_connect_data *const connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
- struct rustls_ssl_backend_data *const backend =
+ const struct rustls_ssl_backend_data *const backend =
(struct rustls_ssl_backend_data *)connssl->backend;
- struct rustls_connection *rconn = NULL;
+ const struct rustls_connection *rconn = NULL;
CURLcode tmperr = CURLE_OK;
int result;
- int what;
bool wants_read;
bool wants_write;
- curl_socket_t writefd;
- curl_socket_t readfd;
- timediff_t timeout_ms;
- timediff_t socket_check_timeout;
DEBUGASSERT(backend);
- CURL_TRC_CF(data, cf, "cr_connect_common, state=%d", connssl->state);
+ CURL_TRC_CF(data, cf, "cr_connect, state=%d", connssl->state);
*done = FALSE;
+
if(!backend->conn) {
result = cr_init_backend(cf, data,
(struct rustls_ssl_backend_data *)connssl->backend);
- CURL_TRC_CF(data, cf, "cr_connect_common, init backend -> %d", result);
+ CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", result);
if(result != CURLE_OK) {
return result;
}
connssl->state = ssl_connection_negotiating;
}
-
rconn = backend->conn;
/* Read/write data until the handshake is done or the socket would block. */
@@ -846,8 +1192,10 @@ cr_connect_common(struct Curl_cfilter *cf,
}
/* REALLY Done with the handshake. */
{
- uint16_t proto = rustls_connection_get_protocol_version(rconn);
- uint16_t cipher = rustls_connection_get_negotiated_ciphersuite(rconn);
+ const uint16_t proto =
+ rustls_connection_get_protocol_version(rconn);
+ const uint16_t cipher =
+ rustls_connection_get_negotiated_ciphersuite(rconn);
char buf[64] = "";
const char *ver = "TLS version unknown";
if(proto == RUSTLS_TLS_VERSION_TLSV1_3)
@@ -858,6 +1206,43 @@ cr_connect_common(struct Curl_cfilter *cf,
infof(data, "rustls: handshake complete, %s, cipher: %s",
ver, buf);
}
+ if(data->set.ssl.certinfo) {
+ size_t num_certs = 0;
+ while(rustls_connection_get_peer_certificate(rconn, (int)num_certs)) {
+ num_certs++;
+ }
+ result = Curl_ssl_init_certinfo(data, (int)num_certs);
+ if(result)
+ return result;
+ for(size_t i = 0; i < num_certs; i++) {
+ const rustls_certificate *cert;
+ const unsigned char *der_data;
+ size_t der_len;
+ rustls_result rresult = RUSTLS_RESULT_OK;
+ cert = rustls_connection_get_peer_certificate(rconn, i);
+ DEBUGASSERT(cert); /* Should exist since we counted already */
+ rresult = rustls_certificate_get_der(cert, &der_data, &der_len);
+ if(rresult != RUSTLS_RESULT_OK) {
+ char errorbuf[255];
+ size_t errorlen;
+ rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
+ failf(data,
+ "Failed getting DER of server certificate #%ld: %.*s", i,
+ (int)errorlen, errorbuf);
+ return map_error(rresult);
+ }
+ {
+ const char *beg;
+ const char *end;
+ beg = (const char *)der_data;
+ end = (const char *)(der_data + der_len);
+ result = Curl_extract_certinfo(data, (int)i, beg, end);
+ if(result)
+ return result;
+ }
+ }
+ }
+
connssl->state = ssl_connection_complete;
*done = TRUE;
return CURLE_OK;
@@ -868,50 +1253,14 @@ cr_connect_common(struct Curl_cfilter *cf,
wants_write = rustls_connection_wants_write(rconn) ||
backend->plain_out_buffered;
DEBUGASSERT(wants_read || wants_write);
- writefd = wants_write ? sockfd : CURL_SOCKET_BAD;
- readfd = wants_read ? sockfd : CURL_SOCKET_BAD;
-
- /* 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, "rustls: operation timed out before socket check");
- return CURLE_OPERATION_TIMEDOUT;
- }
-
- socket_check_timeout = blocking ? timeout_ms : 0;
-
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- socket_check_timeout);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
- }
- if(blocking && 0 == what) {
- failf(data, "rustls: connection timeout after %" FMT_TIMEDIFF_T " ms",
- socket_check_timeout);
- return CURLE_OPERATION_TIMEDOUT;
- }
- if(0 == what) {
- CURL_TRC_CF(data, cf, "Curl_socket_check: %s would block",
- wants_read && wants_write ? "writing and reading" :
- wants_write ? "writing" : "reading");
- if(wants_write)
- connssl->io_need |= CURL_SSL_IO_NEED_SEND;
- if(wants_read)
- connssl->io_need |= CURL_SSL_IO_NEED_RECV;
- return CURLE_OK;
- }
- /* socket is readable or writable */
if(wants_write) {
CURL_TRC_CF(data, cf, "rustls_connection wants us to write_tls.");
cr_send(cf, data, NULL, 0, &tmperr);
if(tmperr == CURLE_AGAIN) {
CURL_TRC_CF(data, cf, "writing would block");
- /* fall through */
+ connssl->io_need = CURL_SSL_IO_NEED_SEND;
+ return CURLE_OK;
}
else if(tmperr != CURLE_OK) {
return tmperr;
@@ -923,7 +1272,8 @@ cr_connect_common(struct Curl_cfilter *cf,
if(tls_recv_more(cf, data, &tmperr) < 0) {
if(tmperr == CURLE_AGAIN) {
CURL_TRC_CF(data, cf, "reading would block");
- /* fall through */
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
+ return CURLE_OK;
}
else if(tmperr == CURLE_RECV_ERROR) {
return CURLE_SSL_CONNECT_ERROR;
@@ -940,20 +1290,6 @@ cr_connect_common(struct Curl_cfilter *cf,
DEBUGASSERT(FALSE);
}
-static CURLcode
-cr_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data, bool *done)
-{
- return cr_connect_common(cf, data, false, done);
-}
-
-static CURLcode
-cr_connect_blocking(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
- bool done; /* unused */
- return cr_connect_common(cf, data, true, &done);
-}
-
static void *
cr_get_internals(struct ssl_connect_data *connssl,
CURLINFO info UNUSED_PARAM)
@@ -967,14 +1303,13 @@ cr_get_internals(struct ssl_connect_data *connssl,
static CURLcode
cr_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool send_shutdown, bool *done)
+ const bool send_shutdown, bool *done)
{
struct ssl_connect_data *connssl = cf->ctx;
struct rustls_ssl_backend_data *backend =
(struct rustls_ssl_backend_data *)connssl->backend;
CURLcode result = CURLE_OK;
ssize_t nwritten, nread;
- char buf[1024];
size_t i;
DEBUGASSERT(backend);
@@ -1007,6 +1342,7 @@ cr_shutdown(struct Curl_cfilter *cf,
}
for(i = 0; i < 10; ++i) {
+ char buf[1024];
nread = cr_recv(cf, data, buf, (int)sizeof(buf), &result);
if(nread <= 0)
break;
@@ -1036,7 +1372,7 @@ out:
static void
cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- struct ssl_connect_data *connssl = cf->ctx;
+ const struct ssl_connect_data *connssl = cf->ctx;
struct rustls_ssl_backend_data *backend =
(struct rustls_ssl_backend_data *)connssl->backend;
@@ -1054,7 +1390,7 @@ cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
static size_t cr_version(char *buffer, size_t size)
{
- struct rustls_str ver = rustls_version();
+ const struct rustls_str ver = rustls_version();
return msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data);
}
@@ -1068,35 +1404,38 @@ cr_random(struct Curl_easy *data, unsigned char *entropy, size_t length)
return map_error(rresult);
}
+static void cr_cleanup(void)
+{
+ Curl_tls_keylog_close();
+}
+
const struct Curl_ssl Curl_ssl_rustls = {
{ CURLSSLBACKEND_RUSTLS, "rustls" },
SSLSUPP_CAINFO_BLOB | /* supports */
SSLSUPP_HTTPS_PROXY |
SSLSUPP_CIPHER_LIST |
- SSLSUPP_TLS13_CIPHERSUITES,
+ SSLSUPP_TLS13_CIPHERSUITES |
+ SSLSUPP_CERTINFO |
+ SSLSUPP_ECH,
sizeof(struct rustls_ssl_backend_data),
- Curl_none_init, /* init */
- Curl_none_cleanup, /* cleanup */
+ NULL, /* init */
+ cr_cleanup, /* cleanup */
cr_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
cr_shutdown, /* shutdown */
cr_data_pending, /* data_pending */
cr_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
- cr_connect_blocking, /* connect */
- cr_connect_nonblocking, /* connect_nonblocking */
+ NULL, /* cert_status_request */
+ cr_connect, /* connect */
Curl_ssl_adjust_pollset, /* adjust_pollset */
cr_get_internals, /* get_internals */
cr_close, /* close_one */
- Curl_none_close_all, /* close_all */
- 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, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
NULL, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
cr_recv, /* recv decrypted data */
cr_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/rustls.h b/libs/libcurl/src/vtls/rustls.h
index d7c56de190..5e084eb785 100644
--- a/libs/libcurl/src/vtls/rustls.h
+++ b/libs/libcurl/src/vtls/rustls.h
@@ -25,7 +25,7 @@
#ifndef HEADER_CURL_RUSTLS_H
#define HEADER_CURL_RUSTLS_H
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_RUSTLS
diff --git a/libs/libcurl/src/vtls/schannel.c b/libs/libcurl/src/vtls/schannel.c
index 85d018d322..a8b12d65e9 100644
--- a/libs/libcurl/src/vtls/schannel.c
+++ b/libs/libcurl/src/vtls/schannel.c
@@ -29,7 +29,7 @@
* but vtls.c should ever call or use these functions.
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_SCHANNEL
@@ -41,23 +41,26 @@
#include "schannel_int.h"
#include "vtls.h"
#include "vtls_int.h"
-#include "strcase.h"
-#include "sendf.h"
-#include "connect.h" /* for the connect timeout */
-#include "strerror.h"
-#include "select.h" /* for the socket readiness */
-#include "inet_pton.h" /* for IP addr SNI check */
-#include "curl_multibyte.h"
-#include "warnless.h"
+#include "vtls_scache.h"
+#include "../strcase.h"
+#include "../sendf.h"
+#include "../connect.h" /* for the connect timeout */
+#include "../strerror.h"
+#include "../select.h" /* for the socket readiness */
+#include "../curlx/inet_pton.h" /* for IP addr SNI check */
+#include "../curlx/multibyte.h"
+#include "../curlx/warnless.h"
#include "x509asn1.h"
-#include "curl_printf.h"
-#include "multiif.h"
-#include "version_win32.h"
-#include "rand.h"
+#include "../curl_printf.h"
+#include "../multiif.h"
+#include "../system_win32.h"
+#include "../curlx/version_win32.h"
+#include "../rand.h"
+#include "../curlx/strparse.h"
/* The last #include file should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/* Some verbose debug messages are wrapped by SCH_DEV() instead of DEBUGF()
* and only shown if CURL_SCHANNEL_DEV_DEBUG was defined at build time. These
@@ -70,19 +73,6 @@
#define SCH_DEV(x) do { } while(0)
#endif
-/* ALPN requires version 8.1 of the Windows SDK, which was
- shipped with Visual Studio 2013, aka _MSC_VER 1800:
-
- https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx
-*/
-#if defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_)
-# define HAS_ALPN 1
-#endif
-
-#ifndef BCRYPT_CHACHA20_POLY1305_ALGORITHM
-#define BCRYPT_CHACHA20_POLY1305_ALGORITHM L"CHACHA20_POLY1305"
-#endif
-
#ifndef BCRYPT_CHAIN_MODE_CCM
#define BCRYPT_CHAIN_MODE_CCM L"ChainingModeCCM"
#endif
@@ -148,13 +138,34 @@
*/
#ifndef CALG_SHA_256
-# define CALG_SHA_256 0x0000800c
+#define CALG_SHA_256 0x0000800c
+#endif
+
+/* Work around typo in CeGCC (as of 0.59.1) w32api headers */
+#if defined(__MINGW32CE__) && \
+ !defined(ALG_CLASS_DHASH) && defined(ALG_CLASS_HASH)
+#define ALG_CLASS_DHASH ALG_CLASS_HASH
#endif
#ifndef PKCS12_NO_PERSIST_KEY
#define PKCS12_NO_PERSIST_KEY 0x00008000
#endif
+#ifndef CERT_FIND_HAS_PRIVATE_KEY
+#define CERT_FIND_HAS_PRIVATE_KEY (21 << CERT_COMPARE_SHIFT)
+#endif
+
+/* ALPN requires version 8.1 of the Windows SDK, which was
+ shipped with Visual Studio 2013, aka _MSC_VER 1800:
+ https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx
+ Or mingw-w64 9.0 or upper.
+*/
+#if (defined(__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 9) || \
+ (defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_))
+#define HAS_ALPN_SCHANNEL
+static bool s_win_has_alpn;
+#endif
+
static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *pinnedpubkey);
@@ -231,8 +242,6 @@ schannel_set_ssl_version_min_max(DWORD *enabled_protocols,
return CURLE_OK;
}
-/* longest is 26, buffer is slightly bigger */
-#define LONGEST_ALG_ID 32
#define CIPHEROPTION(x) {#x, x}
struct algo {
@@ -349,9 +358,9 @@ static const struct algo algs[]= {
};
static int
-get_alg_id_by_name(char *name)
+get_alg_id_by_name(const char *name)
{
- char *nameEnd = strchr(name, ':');
+ const char *nameEnd = strchr(name, ':');
size_t n = nameEnd ? (size_t)(nameEnd - name) : strlen(name);
int i;
@@ -368,12 +377,13 @@ static CURLcode
set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers,
ALG_ID *algIds)
{
- char *startCur = ciphers;
+ const char *startCur = ciphers;
int algCount = 0;
while(startCur && (0 != *startCur) && (algCount < NUM_CIPHERS)) {
- long alg = strtol(startCur, 0, 0);
- if(!alg)
+ curl_off_t alg;
+ if(curlx_str_number(&startCur, &alg, INT_MAX) || !alg)
alg = get_alg_id_by_name(startCur);
+
if(alg)
algIds[algCount++] = (ALG_ID)alg;
else if(!strncmp(startCur, "USE_STRONG_CRYPTO",
@@ -451,11 +461,6 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path,
}
#endif
-static bool algo(const char *check, char *namep, size_t nlen)
-{
- return (strlen(check) == nlen) && !strncmp(check, namep, nlen);
-}
-
static CURLcode
schannel_acquire_credential_handle(struct Curl_cfilter *cf,
struct Curl_easy *data)
@@ -610,6 +615,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
WCHAR* pszPassword;
size_t pwd_len = 0;
int str_w_len = 0;
+ int cert_find_flags;
const char *cert_showfilename_error = blob ?
"(memory blob)" : data->set.ssl.primary.clientcert;
curlx_unicodefree(cert_path);
@@ -658,8 +664,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
else
pszPassword[0] = 0;
- if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
- VERSION_GREATER_THAN_EQUAL))
+ if(Curl_isVistaOrGreater)
cert_store = PFXImportCertStore(&datablob, pszPassword,
PKCS12_NO_PERSIST_KEY);
else
@@ -682,9 +687,17 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
return CURLE_SSL_CERTPROBLEM;
}
+ /* CERT_FIND_HAS_PRIVATE_KEY is only available in Windows 8 / Server
+ 2012, (NT v6.2). For earlier versions we use CURL_FIND_ANY. */
+ if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL))
+ cert_find_flags = CERT_FIND_HAS_PRIVATE_KEY;
+ else
+ cert_find_flags = CERT_FIND_ANY;
+
client_certs[0] = CertFindCertificateInStore(
cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0,
- CERT_FIND_ANY, NULL, NULL);
+ cert_find_flags, NULL, NULL);
if(!client_certs[0]) {
failf(data, "schannel: Failed to get certificate from file %s"
@@ -781,187 +794,14 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT,
VERSION_GREATER_THAN_EQUAL)) {
- char *ciphers13 = 0;
-
- bool disable_aes_gcm_sha384 = FALSE;
- bool disable_aes_gcm_sha256 = FALSE;
- bool disable_chacha_poly = FALSE;
- bool disable_aes_ccm_8_sha256 = FALSE;
- bool disable_aes_ccm_sha256 = FALSE;
-
SCH_CREDENTIALS credentials = { 0 };
TLS_PARAMETERS tls_parameters = { 0 };
- CRYPTO_SETTINGS crypto_settings[4] = { { 0 } };
- UNICODE_STRING blocked_ccm_modes[1] = { { 0 } };
- UNICODE_STRING blocked_gcm_modes[1] = { { 0 } };
-
- int crypto_settings_idx = 0;
-
-
- /* If TLS 1.3 ciphers are explicitly listed, then
- * disable all the ciphers and re-enable which
- * ciphers the user has provided.
- */
- ciphers13 = conn_config->cipher_list13;
- if(ciphers13) {
- const int remaining_ciphers = 5;
-
- /* detect which remaining ciphers to enable
- and then disable everything else.
- */
-
- char *startCur = ciphers13;
- int algCount = 0;
- char *nameEnd;
-
- disable_aes_gcm_sha384 = TRUE;
- disable_aes_gcm_sha256 = TRUE;
- disable_chacha_poly = TRUE;
- disable_aes_ccm_8_sha256 = TRUE;
- disable_aes_ccm_sha256 = TRUE;
-
- while(startCur && (0 != *startCur) && (algCount < remaining_ciphers)) {
- size_t n;
- char *namep;
- nameEnd = strchr(startCur, ':');
- n = nameEnd ? (size_t)(nameEnd - startCur) : strlen(startCur);
- namep = startCur;
-
- if(disable_aes_gcm_sha384 &&
- algo("TLS_AES_256_GCM_SHA384", namep, n)) {
- disable_aes_gcm_sha384 = FALSE;
- }
- else if(disable_aes_gcm_sha256
- && algo("TLS_AES_128_GCM_SHA256", namep, n)) {
- disable_aes_gcm_sha256 = FALSE;
- }
- else if(disable_chacha_poly
- && algo("TLS_CHACHA20_POLY1305_SHA256", namep, n)) {
- disable_chacha_poly = FALSE;
- }
- else if(disable_aes_ccm_8_sha256
- && algo("TLS_AES_128_CCM_8_SHA256", namep, n)) {
- disable_aes_ccm_8_sha256 = FALSE;
- }
- else if(disable_aes_ccm_sha256
- && algo("TLS_AES_128_CCM_SHA256", namep, n)) {
- disable_aes_ccm_sha256 = FALSE;
- }
- else {
- failf(data, "schannel: Unknown TLS 1.3 cipher: %.*s", (int)n, namep);
- return CURLE_SSL_CIPHER;
- }
-
- startCur = nameEnd;
- if(startCur)
- startCur++;
-
- algCount++;
- }
- }
-
- if(disable_aes_gcm_sha384 && disable_aes_gcm_sha256
- && disable_chacha_poly && disable_aes_ccm_8_sha256
- && disable_aes_ccm_sha256) {
- failf(data, "schannel: All available TLS 1.3 ciphers were disabled");
- return CURLE_SSL_CIPHER;
- }
-
- /* Disable TLS_AES_128_CCM_8_SHA256 and/or TLS_AES_128_CCM_SHA256 */
- if(disable_aes_ccm_8_sha256 || disable_aes_ccm_sha256) {
- /*
- Disallow AES_CCM algorithm.
- */
- blocked_ccm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_CCM);
- blocked_ccm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_CCM);
- blocked_ccm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_CCM;
-
- crypto_settings[crypto_settings_idx].eAlgorithmUsage =
- TlsParametersCngAlgUsageCipher;
- crypto_settings[crypto_settings_idx].rgstrChainingModes =
- blocked_ccm_modes;
- crypto_settings[crypto_settings_idx].cChainingModes =
- ARRAYSIZE(blocked_ccm_modes);
- crypto_settings[crypto_settings_idx].strCngAlgId.Length =
- sizeof(BCRYPT_AES_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
- sizeof(BCRYPT_AES_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
- (PWSTR)BCRYPT_AES_ALGORITHM;
-
- /* only disabling one of the CCM modes */
- if(disable_aes_ccm_8_sha256 != disable_aes_ccm_sha256) {
- if(disable_aes_ccm_8_sha256)
- crypto_settings[crypto_settings_idx].dwMinBitLength = 128;
- else /* disable_aes_ccm_sha256 */
- crypto_settings[crypto_settings_idx].dwMaxBitLength = 64;
- }
-
- crypto_settings_idx++;
- }
-
- /* Disable TLS_AES_256_GCM_SHA384 and/or TLS_AES_128_GCM_SHA256 */
- if(disable_aes_gcm_sha384 || disable_aes_gcm_sha256) {
-
- /*
- Disallow AES_GCM algorithm
- */
- blocked_gcm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_GCM);
- blocked_gcm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_GCM);
- blocked_gcm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_GCM;
-
- /* if only one is disabled, then explicitly disable the
- digest cipher suite (sha384 or sha256) */
- if(disable_aes_gcm_sha384 != disable_aes_gcm_sha256) {
- crypto_settings[crypto_settings_idx].eAlgorithmUsage =
- TlsParametersCngAlgUsageDigest;
- crypto_settings[crypto_settings_idx].strCngAlgId.Length =
- sizeof(disable_aes_gcm_sha384 ?
- BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
- sizeof(disable_aes_gcm_sha384 ?
- BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
- (PWSTR)(disable_aes_gcm_sha384 ?
- BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM);
- }
- else { /* Disable both AES_GCM ciphers */
- crypto_settings[crypto_settings_idx].eAlgorithmUsage =
- TlsParametersCngAlgUsageCipher;
- crypto_settings[crypto_settings_idx].strCngAlgId.Length =
- sizeof(BCRYPT_AES_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
- sizeof(BCRYPT_AES_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
- (PWSTR)BCRYPT_AES_ALGORITHM;
- }
-
- crypto_settings[crypto_settings_idx].rgstrChainingModes =
- blocked_gcm_modes;
- crypto_settings[crypto_settings_idx].cChainingModes = 1;
-
- crypto_settings_idx++;
- }
-
- /*
- Disable ChaCha20-Poly1305.
- */
- if(disable_chacha_poly) {
- crypto_settings[crypto_settings_idx].eAlgorithmUsage =
- TlsParametersCngAlgUsageCipher;
- crypto_settings[crypto_settings_idx].strCngAlgId.Length =
- sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
- sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
- (PWSTR)BCRYPT_CHACHA20_POLY1305_ALGORITHM;
- crypto_settings_idx++;
- }
+ CRYPTO_SETTINGS crypto_settings[1] = { { 0 } };
tls_parameters.pDisabledCrypto = crypto_settings;
/* The number of blocked suites */
- tls_parameters.cDisabledCrypto = (DWORD)crypto_settings_idx;
+ tls_parameters.cDisabledCrypto = (DWORD)0;
credentials.pTlsParameters = &tls_parameters;
credentials.cTlsParameters = 1;
@@ -979,16 +819,16 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
#endif
sspi_status =
- Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
- SECPKG_CRED_OUTBOUND, NULL,
- &credentials, NULL, NULL,
- &backend->cred->cred_handle,
- &backend->cred->time_stamp);
+ Curl_pSecFn->AcquireCredentialsHandle(NULL,
+ (TCHAR *)CURL_UNCONST(UNISP_NAME),
+ SECPKG_CRED_OUTBOUND, NULL,
+ &credentials, NULL, NULL,
+ &backend->cred->cred_handle,
+ &backend->cred->time_stamp);
}
else {
- /* Pre-Windows 10 1809 or the user set a legacy algorithm list. Although MS
- does not document it, currently Schannel will not negotiate TLS 1.3 when
- SCHANNEL_CRED is used. */
+ /* Pre-Windows 10 1809 or the user set a legacy algorithm list.
+ Schannel will not negotiate TLS 1.3 when SCHANNEL_CRED is used. */
ALG_ID algIds[NUM_CIPHERS];
char *ciphers = conn_config->cipher_list;
SCHANNEL_CRED schannel_cred = { 0 };
@@ -998,16 +838,10 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
if(ciphers) {
if((enabled_protocols & SP_PROT_TLS1_3_CLIENT)) {
- infof(data, "schannel: WARNING: This version of Schannel may "
- "negotiate a less-secure TLS version than TLS 1.3 because the "
+ infof(data, "schannel: WARNING: This version of Schannel "
+ "negotiates a less-secure TLS version than TLS 1.3 because the "
"user set an algorithm cipher list.");
}
- if(conn_config->cipher_list13) {
- failf(data, "schannel: This version of Schannel does not support "
- "setting an algorithm cipher list and TLS 1.3 cipher list at "
- "the same time");
- return CURLE_SSL_CIPHER;
- }
result = set_ssl_ciphers(&schannel_cred, ciphers, algIds);
if(CURLE_OK != result) {
failf(data, "schannel: Failed setting algorithm cipher list");
@@ -1026,11 +860,12 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
#endif
sspi_status =
- Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
- SECPKG_CRED_OUTBOUND, NULL,
- &schannel_cred, NULL, NULL,
- &backend->cred->cred_handle,
- &backend->cred->time_stamp);
+ Curl_pSecFn->AcquireCredentialsHandle(NULL,
+ (TCHAR *)CURL_UNCONST(UNISP_NAME),
+ SECPKG_CRED_OUTBOUND, NULL,
+ &schannel_cred, NULL, NULL,
+ &backend->cred->cred_handle,
+ &backend->cred->time_stamp);
}
#ifdef HAS_CLIENT_CERT_PATH
@@ -1066,17 +901,18 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
struct ssl_connect_data *connssl = cf->ctx;
struct schannel_ssl_backend_data *backend =
(struct schannel_ssl_backend_data *)connssl->backend;
+#ifndef UNDER_CE
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+#endif
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
SecBuffer outbuf;
SecBufferDesc outbuf_desc;
SecBuffer inbuf;
SecBufferDesc inbuf_desc;
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
unsigned char alpn_buffer[128];
#endif
SECURITY_STATUS sspi_status = SEC_E_OK;
- struct Curl_schannel_cred *old_cred = NULL;
CURLcode result;
DEBUGASSERT(backend);
@@ -1092,21 +928,15 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
"connect to some servers due to lack of SNI, algorithms, etc.");
}
-#ifdef HAS_ALPN
- /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above.
- Also it does not seem to be supported for WINE, see curl bug #983. */
- backend->use_alpn = connssl->alpn &&
- !GetProcAddress(GetModuleHandle(TEXT("ntdll")),
- "wine_get_version") &&
- curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT,
- VERSION_GREATER_THAN_EQUAL);
+#ifdef HAS_ALPN_SCHANNEL
+ backend->use_alpn = connssl->alpn && s_win_has_alpn;
#else
backend->use_alpn = FALSE;
#endif
-#ifdef _WIN32_WCE
+#ifdef UNDER_CE
#ifdef HAS_MANUAL_VERIFY_API
- /* certificate validation on CE does not seem to work right; we will
+ /* certificate validation on Windows CE does not seem to work right; we will
* do it following a more manual process. */
backend->use_manual_cred_validation = TRUE;
#else
@@ -1139,9 +969,10 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
/* check for an existing reusable credential handle */
if(ssl_config->primary.cache_session) {
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
- (void **)&old_cred, NULL, NULL)) {
+ struct Curl_schannel_cred *old_cred;
+ Curl_ssl_scache_lock(data);
+ old_cred = Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key);
+ if(old_cred) {
backend->cred = old_cred;
DEBUGF(infof(data, "schannel: reusing existing credential handle"));
@@ -1151,13 +982,13 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
"schannel: incremented credential handle refcount = %d",
backend->cred->refcount));
}
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_unlock(data);
}
if(!backend->cred) {
char *snihost;
result = schannel_acquire_credential_handle(cf, data);
- if(result)
+ if(result || !backend->cred)
return result;
/* schannel_acquire_credential_handle() sets backend->cred accordingly or
it returns error otherwise. */
@@ -1175,7 +1006,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
infof(data, "schannel: using IP address, SNI is not supported by OS.");
}
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
if(backend->use_alpn) {
int cur = 0;
int list_start_index = 0;
@@ -1223,7 +1054,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
}
-#else /* HAS_ALPN */
+#else /* HAS_ALPN_SCHANNEL */
InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
#endif
@@ -1686,12 +1517,11 @@ add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, bool reverse_order,
return args->result == CURLE_OK;
}
-static void schannel_session_free(void *sessionid, size_t idsize)
+static void schannel_session_free(void *sessionid)
{
/* this is expected to be called under sessionid lock */
struct Curl_schannel_cred *cred = sessionid;
- (void)idsize;
if(cred) {
cred->refcount--;
if(cred->refcount == 0) {
@@ -1718,7 +1548,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
CURLcode result = CURLE_OK;
SECURITY_STATUS sspi_status = SEC_E_OK;
CERT_CONTEXT *ccert_context = NULL;
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
SecPkgContext_ApplicationProtocol alpn_result;
#endif
@@ -1747,7 +1577,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_SSL_CONNECT_ERROR;
}
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
if(backend->use_alpn) {
sspi_status =
Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
@@ -1784,14 +1614,12 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
/* save the current session data for possible reuse */
if(ssl_config->primary.cache_session) {
- Curl_ssl_sessionid_lock(data);
+ Curl_ssl_scache_lock(data);
/* Up ref count since call takes ownership */
backend->cred->refcount++;
- result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
- backend->cred,
- sizeof(struct Curl_schannel_cred),
- schannel_session_free);
- Curl_ssl_sessionid_unlock(data);
+ result = Curl_ssl_scache_add_obj(cf, data, connssl->peer.scache_key,
+ backend->cred, schannel_session_free);
+ Curl_ssl_scache_unlock(data);
if(result)
return result;
}
@@ -1816,6 +1644,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
args.data = data;
args.idx = 0;
args.certs_count = certs_count;
+ args.result = CURLE_OK;
traverse_cert_store(ccert_context, add_cert_to_certinfo, &args);
result = args.result;
}
@@ -1829,16 +1658,12 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_OK;
}
-static CURLcode
-schannel_connect_common(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool nonblocking, bool *done)
+static CURLcode schannel_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
{
- CURLcode result;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
- timediff_t timeout_ms;
- int what;
+ CURLcode result;
/* check if the connection has already been established */
if(ssl_connection_complete == connssl->state) {
@@ -1846,73 +1671,19 @@ schannel_connect_common(struct Curl_cfilter *cf,
return CURLE_OK;
}
- if(ssl_connect_1 == connssl->connecting_state) {
- /* check out how much more time we are allowed */
- timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL/TLS connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
+ *done = FALSE;
+ if(ssl_connect_1 == connssl->connecting_state) {
result = schannel_connect_step1(cf, data);
if(result)
return result;
}
- while(ssl_connect_2 == connssl->connecting_state) {
-
- /* check out how much more time we are allowed */
- timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL/TLS connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
-
- /* if ssl is expecting something, check if it is available. */
- if(connssl->io_need) {
-
- curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
- sockfd : CURL_SOCKET_BAD;
- curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
- sockfd : CURL_SOCKET_BAD;
-
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking ? 0 : timeout_ms);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
- }
- else if(0 == what) {
- if(nonblocking) {
- *done = FALSE;
- return CURLE_OK;
- }
- else {
- /* timeout */
- failf(data, "SSL/TLS connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
- }
- /* socket is readable or writable */
- }
-
- /* Run transaction, and return to the caller if it failed or if
- * this connection is part of a multi handle and this loop would
- * execute again. This permits the owner of a multi handle to
- * abort a connection attempt before step2 has completed while
- * ensuring that a client using select() or epoll() will always
- * have a valid fdset to wait on.
- */
+ if(ssl_connect_2 == connssl->connecting_state) {
result = schannel_connect_step2(cf, data);
- if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
+ if(result)
return result;
-
- } /* repeat step2 until all transactions are done. */
+ }
if(ssl_connect_3 == connssl->connecting_state) {
result = schannel_connect_step3(cf, data);
@@ -1939,11 +1710,6 @@ schannel_connect_common(struct Curl_cfilter *cf,
*done = TRUE;
}
- else
- *done = FALSE;
-
- /* reset our connection state machine */
- connssl->connecting_state = ssl_connect_1;
return CURLE_OK;
}
@@ -2189,7 +1955,6 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
backend->encdata_offset),
size, err);
if(*err) {
- nread = -1;
if(*err == CURLE_AGAIN)
SCH_DEV(infof(data, "schannel: recv returned CURLE_AGAIN"));
else if(*err == CURLE_RECV_ERROR)
@@ -2316,7 +2081,7 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
connssl->connecting_state = ssl_connect_2;
connssl->io_need = CURL_SSL_IO_NEED_SEND;
backend->recv_renegotiating = TRUE;
- *err = schannel_connect_common(cf, data, FALSE, &done);
+ *err = schannel_connect(cf, data, &done);
backend->recv_renegotiating = FALSE;
if(*err) {
infof(data, "schannel: renegotiation failed");
@@ -2427,28 +2192,6 @@ cleanup:
return *err ? -1 : 0;
}
-static CURLcode schannel_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
-{
- return schannel_connect_common(cf, data, TRUE, done);
-}
-
-static CURLcode schannel_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- CURLcode result;
- bool done = FALSE;
-
- result = schannel_connect_common(cf, data, FALSE, &done);
- if(result)
- return result;
-
- DEBUGASSERT(done);
-
- return CURLE_OK;
-}
-
static bool schannel_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
@@ -2460,11 +2203,11 @@ static bool schannel_data_pending(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
if(backend->ctxt) /* SSL/TLS is in use */
- return (backend->decdata_offset > 0 ||
- (backend->encdata_offset > 0 && !backend->encdata_is_incomplete) ||
- backend->recv_connection_closed ||
- backend->recv_sspi_close_notify ||
- backend->recv_unrecoverable_err);
+ return backend->decdata_offset > 0 ||
+ (backend->encdata_offset > 0 && !backend->encdata_is_incomplete) ||
+ backend->recv_connection_closed ||
+ backend->recv_sspi_close_notify ||
+ backend->recv_unrecoverable_err;
else
return FALSE;
}
@@ -2554,7 +2297,6 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf,
Curl_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
if(!result) {
if(written < (ssize_t)outbuf.cbBuffer) {
- /* TODO: handle partial sends */
failf(data, "schannel: failed to send close msg: %s"
" (bytes written: %zd)", curl_easy_strerror(result), written);
result = CURLE_SEND_ERROR;
@@ -2630,9 +2372,9 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data)
/* free SSPI Schannel API credential handle */
if(backend->cred) {
- Curl_ssl_sessionid_lock(data);
- schannel_session_free(backend->cred, 0);
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_lock(data);
+ schannel_session_free(backend->cred);
+ Curl_ssl_scache_unlock(data);
backend->cred = NULL;
}
@@ -2654,7 +2396,35 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data)
static int schannel_init(void)
{
- return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
+#if defined(HAS_ALPN_SCHANNEL) && !defined(UNDER_CE)
+ bool wine = FALSE;
+ bool wine_has_alpn = FALSE;
+
+#ifndef CURL_WINDOWS_UWP
+ typedef const char *(APIENTRY *WINE_GET_VERSION_FN)(void);
+ /* GetModuleHandle() not available for UWP.
+ Assume no WINE because WINE has no UWP support. */
+ WINE_GET_VERSION_FN p_wine_get_version =
+ CURLX_FUNCTION_CAST(WINE_GET_VERSION_FN,
+ (GetProcAddress(GetModuleHandleA("ntdll"),
+ "wine_get_version")));
+ wine = !!p_wine_get_version;
+ if(wine) {
+ const char *wine_version = p_wine_get_version(); /* e.g. "6.0.2" */
+ /* Assume ALPN support with WINE 6.0 or upper */
+ wine_has_alpn = wine_version && atoi(wine_version) >= 6;
+ }
+#endif
+ if(wine)
+ s_win_has_alpn = wine_has_alpn;
+ else {
+ /* ALPN is supported on Windows 8.1 / Server 2012 R2 and above. */
+ s_win_has_alpn = curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL);
+ }
+#endif /* HAS_ALPN_SCHANNEL && !UNDER_CE */
+
+ return Curl_sspi_global_init() == CURLE_OK ? 1 : 0;
}
static void schannel_cleanup(void)
@@ -2777,7 +2547,12 @@ static void schannel_checksum(const unsigned char *input,
if(!CryptCreateHash(hProv, algId, 0, 0, &hHash))
break; /* failed */
+#ifdef __MINGW32CE__
+ /* workaround for CeGCC, should be (const BYTE*) */
+ if(!CryptHashData(hHash, (BYTE*)CURL_UNCONST(input), (DWORD)inputlen, 0))
+#else
if(!CryptHashData(hHash, input, (DWORD)inputlen, 0))
+#endif
break; /* failed */
/* get hash size */
@@ -2841,7 +2616,7 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf,
}
share = Curl_hash_pick(&multi->proto_hash,
- (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY,
+ CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY),
sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1);
if(!share || !share->cert_store) {
return NULL;
@@ -2857,8 +2632,8 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf,
negative timeout means retain forever. */
timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
if(timeout_ms >= 0) {
- now = Curl_now();
- elapsed_ms = Curl_timediff(now, share->time);
+ now = curlx_now();
+ elapsed_ms = curlx_timediff(now, share->time);
if(elapsed_ms >= timeout_ms) {
return NULL;
}
@@ -2919,7 +2694,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
}
share = Curl_hash_pick(&multi->proto_hash,
- (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY,
+ CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY),
sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1);
if(!share) {
share = calloc(1, sizeof(*share));
@@ -2927,7 +2702,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
return FALSE;
}
if(!Curl_hash_add2(&multi->proto_hash,
- (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY,
+ CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY),
sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1,
share, schannel_cert_share_free)) {
free(share);
@@ -2957,7 +2732,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
}
free(share->CAfile);
- share->time = Curl_now();
+ share->time = curlx_now();
share->cert_store = cert_store;
share->CAinfo_blob_size = CAinfo_blob_size;
share->CAfile = CAfile;
@@ -2974,7 +2749,6 @@ const struct Curl_ssl Curl_ssl_schannel = {
#ifndef CURL_WINDOWS_UWP
SSLSUPP_PINNEDPUBKEY |
#endif
- SSLSUPP_TLS13_CIPHERSUITES |
SSLSUPP_CA_CACHE |
SSLSUPP_HTTPS_PROXY |
SSLSUPP_CIPHER_LIST,
@@ -2984,24 +2758,20 @@ const struct Curl_ssl Curl_ssl_schannel = {
schannel_init, /* init */
schannel_cleanup, /* cleanup */
schannel_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
schannel_shutdown, /* shutdown */
schannel_data_pending, /* data_pending */
schannel_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
schannel_connect, /* connect */
- schannel_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
schannel_get_internals, /* get_internals */
schannel_close, /* close_one */
- Curl_none_close_all, /* close_all */
- 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, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
schannel_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
schannel_recv, /* recv decrypted data */
schannel_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/schannel.h b/libs/libcurl/src/vtls/schannel.h
index 438012d434..a0fb515aa7 100644
--- a/libs/libcurl/src/vtls/schannel.h
+++ b/libs/libcurl/src/vtls/schannel.h
@@ -24,13 +24,13 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_SCHANNEL
#ifdef _MSC_VER
#pragma warning(push)
-#pragma warning(disable: 4201)
+#pragma warning(disable:4201)
#endif
#include <subauth.h>
#ifdef _MSC_VER
@@ -50,10 +50,10 @@
#include <schnlsp.h>
#include <schannel.h>
-#include "curl_sspi.h"
+#include "../curl_sspi.h"
-#include "cfilters.h"
-#include "urldata.h"
+#include "../cfilters.h"
+#include "../urldata.h"
/* <wincrypt.h> has been included via the above <schnlsp.h>.
* Or in case of ldap.c, it was included via <winldap.h>.
diff --git a/libs/libcurl/src/vtls/schannel_int.h b/libs/libcurl/src/vtls/schannel_int.h
index 52d6fd5214..410a4808a2 100644
--- a/libs/libcurl/src/vtls/schannel_int.h
+++ b/libs/libcurl/src/vtls/schannel_int.h
@@ -24,7 +24,7 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_SCHANNEL
@@ -150,17 +150,17 @@ struct schannel_ssl_backend_data {
cannot be decrypted without another recv() (that is, status is
SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds
more bytes into encdata then set this back to false. */
- bool encdata_is_incomplete;
unsigned long req_flags, ret_flags;
CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
- bool recv_sspi_close_notify; /* true if connection closed by close_notify */
- bool recv_connection_closed; /* true if connection closed, regardless how */
- bool recv_renegotiating; /* true if recv is doing renegotiation */
- bool use_alpn; /* true if ALPN is used for this connection */
+ BIT(recv_sspi_close_notify); /* true if connection closed by close_notify */
+ BIT(recv_connection_closed); /* true if connection closed, regardless how */
+ BIT(recv_renegotiating); /* true if recv is doing renegotiation */
+ BIT(use_alpn); /* true if ALPN is used for this connection */
#ifdef HAS_MANUAL_VERIFY_API
- bool use_manual_cred_validation; /* true if manual cred validation is used */
+ BIT(use_manual_cred_validation); /* true if manual cred validation is used */
#endif
BIT(sent_shutdown);
+ BIT(encdata_is_incomplete);
};
/* key to use at `multi->proto_hash` */
diff --git a/libs/libcurl/src/vtls/schannel_verify.c b/libs/libcurl/src/vtls/schannel_verify.c
index ee960ed2c5..b4d7241790 100644
--- a/libs/libcurl/src/vtls/schannel_verify.c
+++ b/libs/libcurl/src/vtls/schannel_verify.c
@@ -29,7 +29,7 @@
* only be invoked by code in schannel.c.
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_SCHANNEL
#ifndef USE_WINDOWS_SSPI
@@ -39,24 +39,41 @@
#include "schannel.h"
#include "schannel_int.h"
-#include "inet_pton.h"
+#include "../curlx/inet_pton.h"
#include "vtls.h"
#include "vtls_int.h"
-#include "sendf.h"
-#include "strerror.h"
-#include "curl_multibyte.h"
-#include "curl_printf.h"
+#include "../sendf.h"
+#include "../strerror.h"
+#include "../curlx/winapi.h"
+#include "../curlx/multibyte.h"
+#include "../curl_printf.h"
#include "hostcheck.h"
-#include "version_win32.h"
+#include "../curlx/version_win32.h"
/* The last #include file should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
#define BACKEND ((struct schannel_ssl_backend_data *)connssl->backend)
#ifdef HAS_MANUAL_VERIFY_API
+#ifdef __MINGW32CE__
+#define CERT_QUERY_OBJECT_BLOB 0x00000002
+#define CERT_QUERY_CONTENT_CERT 1
+#define CERT_QUERY_CONTENT_FLAG_CERT (1 << CERT_QUERY_CONTENT_CERT)
+#define CERT_QUERY_FORMAT_BINARY 1
+#define CERT_QUERY_FORMAT_BASE64_ENCODED 2
+#define CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED 3
+#define CERT_QUERY_FORMAT_FLAG_ALL \
+ (1 << CERT_QUERY_FORMAT_BINARY) | \
+ (1 << CERT_QUERY_FORMAT_BASE64_ENCODED) | \
+ (1 << CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED)
+#define CERT_CHAIN_REVOCATION_CHECK_CHAIN 0x20000000
+#define CERT_NAME_DISABLE_IE4_UTF8_FLAG 0x00010000
+#define CERT_TRUST_IS_OFFLINE_REVOCATION 0x01000000
+#endif /* __MINGW32CE__ */
+
#define MAX_CAFILE_SIZE 1048576 /* 1 MiB */
#define BEGIN_CERT "-----BEGIN CERTIFICATE-----"
#define END_CERT "\n-----END CERTIFICATE-----"
@@ -76,6 +93,7 @@ struct cert_chain_engine_config_win7 {
HCERTSTORE hExclusiveTrustedPeople;
};
+#ifndef UNDER_CE
static int is_cr_or_lf(char c)
{
return c == '\r' || c == '\n';
@@ -138,13 +156,13 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store,
}
else {
CERT_BLOB cert_blob;
- CERT_CONTEXT *cert_context = NULL;
+ const CERT_CONTEXT *cert_context = NULL;
BOOL add_cert_result = FALSE;
DWORD actual_content_type = 0;
DWORD cert_size = (DWORD)
((end_cert_ptr + end_cert_len) - begin_cert_ptr);
- cert_blob.pbData = (BYTE *)begin_cert_ptr;
+ cert_blob.pbData = (BYTE *)CURL_UNCONST(begin_cert_ptr);
cert_blob.cbData = cert_size;
if(!CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
&cert_blob,
@@ -157,12 +175,12 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store,
NULL,
NULL,
(const void **)&cert_context)) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data,
"schannel: failed to extract certificate from CA file "
"'%s': %s",
ca_file_text,
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
more_certs = 0;
}
@@ -186,13 +204,13 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store,
NULL);
CertFreeCertificateContext(cert_context);
if(!add_cert_result) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data,
"schannel: failed to add certificate from CA file '%s' "
"to certificate store: %s",
ca_file_text,
- Curl_winapi_strerror(GetLastError(), buffer,
- sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer,
+ sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
more_certs = 0;
}
@@ -232,13 +250,13 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
size_t ca_file_bufsize = 0;
DWORD total_bytes_read = 0;
- ca_file_tstr = curlx_convert_UTF8_to_tchar((char *)ca_file);
+ ca_file_tstr = curlx_convert_UTF8_to_tchar(ca_file);
if(!ca_file_tstr) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data,
"schannel: invalid path name for CA file '%s': %s",
ca_file,
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
goto cleanup;
}
@@ -256,21 +274,21 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(ca_file_handle == INVALID_HANDLE_VALUE) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data,
"schannel: failed to open CA file '%s': %s",
ca_file,
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
goto cleanup;
}
if(!GetFileSizeEx(ca_file_handle, &file_size)) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data,
"schannel: failed to determine size of CA file '%s': %s",
ca_file,
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
goto cleanup;
}
@@ -296,11 +314,11 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
if(!ReadFile(ca_file_handle, ca_file_buffer + total_bytes_read,
bytes_to_read, &bytes_read, NULL)) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data,
"schannel: failed to read from CA file '%s': %s",
ca_file,
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
goto cleanup;
}
@@ -313,7 +331,7 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
}
}
- /* Null terminate the buffer */
+ /* null-terminate the buffer */
ca_file_buffer[ca_file_bufsize] = '\0';
result = add_certs_data_to_store(trust_store,
@@ -330,9 +348,11 @@ cleanup:
return result;
}
+#endif
#endif /* HAS_MANUAL_VERIFY_API */
+#ifndef UNDER_CE
/*
* Returns the number of characters necessary to populate all the host_names.
* If host_names is not NULL, populate it with all the hostnames. Each string
@@ -380,6 +400,9 @@ static DWORD cert_get_name_string(struct Curl_easy *data,
(void)Win8_compat;
#endif
+ if(!alt_name_info)
+ return 0;
+
compute_content = host_names != NULL && length != 0;
/* Initialize default return values. */
@@ -442,14 +465,14 @@ static bool get_num_host_info(struct num_ip_data *ip_blob,
struct in6_addr ia6;
bool result = FALSE;
- int res = Curl_inet_pton(AF_INET, hostname, &ia);
+ int res = curlx_inet_pton(AF_INET, hostname, &ia);
if(res) {
ip_blob->size = sizeof(struct in_addr);
memcpy(&ip_blob->bData.ia, &ia, sizeof(struct in_addr));
result = TRUE;
}
else {
- res = Curl_inet_pton(AF_INET6, hostname, &ia6);
+ res = curlx_inet_pton(AF_INET6, hostname, &ia6);
if(res) {
ip_blob->size = sizeof(struct in6_addr);
memcpy(&ip_blob->bData.ia6, &ia6, sizeof(struct in6_addr));
@@ -511,15 +534,68 @@ static bool get_alt_name_info(struct Curl_easy *data,
#endif
return result;
}
+#endif /* !UNDER_CE */
/* Verify the server's hostname */
CURLcode Curl_verify_host(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
- struct ssl_connect_data *connssl = cf->ctx;
- SECURITY_STATUS sspi_status;
CURLcode result = CURLE_PEER_FAILED_VERIFICATION;
+ struct ssl_connect_data *connssl = cf->ctx;
CERT_CONTEXT *pCertContextServer = NULL;
+#ifdef UNDER_CE
+ TCHAR cert_hostname_buff[256];
+ DWORD len;
+
+ /* This code does not support certificates with multiple alternative names.
+ * Right now we are only asking for the first preferred alternative name.
+ * Instead we would need to do all via CERT_NAME_SEARCH_ALL_NAMES_FLAG
+ * (If Windows CE supports that?) and run this section in a loop for each.
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/aa376086.aspx
+ * curl: (51) schannel: CertGetNameString() certificate hostname
+ * (.google.com) did not match connection (google.com)
+ */
+ len = CertGetNameString(pCertContextServer,
+ CERT_NAME_DNS_TYPE,
+ CERT_NAME_DISABLE_IE4_UTF8_FLAG,
+ NULL,
+ cert_hostname_buff,
+ 256);
+ if(len > 0) {
+ /* Comparing the cert name and the connection hostname encoded as UTF-8
+ * is acceptable since both values are assumed to use ASCII
+ * (or some equivalent) encoding
+ */
+ char *cert_hostname = curlx_convert_tchar_to_UTF8(cert_hostname_buff);
+ if(!cert_hostname) {
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ else{
+ const char *conn_hostname = connssl->peer.hostname;
+ if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname),
+ conn_hostname, strlen(conn_hostname))) {
+ infof(data,
+ "schannel: connection hostname (%s) validated "
+ "against certificate name (%s)\n",
+ conn_hostname, cert_hostname);
+ result = CURLE_OK;
+ }
+ else{
+ failf(data,
+ "schannel: connection hostname (%s) "
+ "does not match certificate name (%s)",
+ conn_hostname, cert_hostname);
+ }
+ Curl_safefree(cert_hostname);
+ }
+ }
+ else {
+ failf(data,
+ "schannel: CertGetNameString did not provide any "
+ "certificate name information");
+ }
+#else
+ SECURITY_STATUS sspi_status;
TCHAR *cert_hostname_buff = NULL;
size_t cert_hostname_buff_index = 0;
const char *conn_hostname = connssl->peer.hostname;
@@ -539,7 +615,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf,
&pCertContextServer);
if((sspi_status != SEC_E_OK) || !pCertContextServer) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data, "schannel: Failed to read remote certificate context: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
goto cleanup;
@@ -554,7 +630,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf,
}
}
- if(p->size) {
+ if(p->size && alt_name_info) {
for(i = 0; i < alt_name_info->cAltEntry; ++i) {
PCERT_ALT_NAME_ENTRY entry = &alt_name_info->rgAltEntry[i];
if(entry->dwAltNameChoice == CERT_ALT_NAME_IP_ADDRESS) {
@@ -571,7 +647,6 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf,
}
}
}
-
else {
/* Determine the size of the string needed for the cert hostname */
len = cert_get_name_string(data, pCertContextServer,
@@ -665,6 +740,7 @@ cleanup:
if(pCertContextServer)
CertFreeCertificateContext(pCertContextServer);
+#endif /* !UNDER_CE */
return result;
}
@@ -682,23 +758,26 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
CERT_CONTEXT *pCertContextServer = NULL;
const CERT_CHAIN_CONTEXT *pChainContext = NULL;
HCERTCHAINENGINE cert_chain_engine = NULL;
+#ifndef UNDER_CE
HCERTSTORE trust_store = NULL;
HCERTSTORE own_trust_store = NULL;
+#endif /* !UNDER_CE */
DEBUGASSERT(BACKEND);
sspi_status =
Curl_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
- SECPKG_ATTR_REMOTE_CERT_CONTEXT,
- &pCertContextServer);
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+ &pCertContextServer);
if((sspi_status != SEC_E_OK) || !pCertContextServer) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data, "schannel: Failed to read remote certificate context: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
result = CURLE_PEER_FAILED_VERIFICATION;
}
+#ifndef UNDER_CE
if(result == CURLE_OK &&
(conn_config->CAfile || conn_config->ca_info_blob) &&
BACKEND->use_manual_cred_validation) {
@@ -728,9 +807,9 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
CERT_STORE_CREATE_NEW_FLAG,
NULL);
if(!trust_store) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data, "schannel: failed to create certificate store: %s",
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
}
else {
@@ -775,14 +854,15 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
CertCreateCertificateChainEngine(
(CERT_CHAIN_ENGINE_CONFIG *)&engine_config, &cert_chain_engine);
if(!create_engine_result) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data,
"schannel: failed to create certificate chain engine: %s",
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
}
}
}
+#endif /* !UNDER_CE */
if(result == CURLE_OK) {
CERT_CHAIN_PARA ChainPara;
@@ -799,9 +879,9 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
CERT_CHAIN_REVOCATION_CHECK_CHAIN),
NULL,
&pChainContext)) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data, "schannel: CertGetCertificateChain failed: %s",
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
pChainContext = NULL;
result = CURLE_PEER_FAILED_VERIFICATION;
}
@@ -849,6 +929,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
}
}
+#ifndef UNDER_CE
if(cert_chain_engine) {
CertFreeCertificateChainEngine(cert_chain_engine);
}
@@ -856,6 +937,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
if(own_trust_store) {
CertCloseStore(own_trust_store, 0);
}
+#endif /* !UNDER_CE */
if(pChainContext)
CertFreeCertificateChain(pChainContext);
diff --git a/libs/libcurl/src/vtls/sectransp.c b/libs/libcurl/src/vtls/sectransp.c
index bf1d44dbdf..d05dd8a3f8 100644
--- a/libs/libcurl/src/vtls/sectransp.c
+++ b/libs/libcurl/src/vtls/sectransp.c
@@ -28,17 +28,18 @@
* TLS/SSL layer. No code but vtls.c should ever call or use these functions.
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_SECTRANSP
-#include "urldata.h" /* for the Curl_easy definition */
-#include "curl_base64.h"
-#include "strtok.h"
-#include "multiif.h"
-#include "strcase.h"
+#include "../urldata.h" /* for the Curl_easy definition */
+#include "../curlx/base64.h"
+#include "../curlx/strparse.h"
+#include "../multiif.h"
+#include "../strcase.h"
#include "x509asn1.h"
-#include "strerror.h"
+#include "vtls_scache.h"
+#include "../strerror.h"
#include "cipher_suite.h"
#ifdef __clang__
@@ -51,6 +52,11 @@
#pragma GCC diagnostic ignored "-Waddress"
#endif
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
#include <limits.h>
#include <Security/Security.h>
@@ -128,19 +134,19 @@
#include <sys/sysctl.h>
#endif /* CURL_BUILD_MAC */
-#include "sendf.h"
-#include "inet_pton.h"
-#include "connect.h"
-#include "select.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
+#include "../connect.h"
+#include "../select.h"
#include "vtls.h"
#include "vtls_int.h"
#include "sectransp.h"
-#include "curl_printf.h"
-#include "strdup.h"
+#include "../curl_printf.h"
+#include "../strdup.h"
-#include "curl_memory.h"
+#include "../curl_memory.h"
/* The last #include file should be: */
-#include "memdebug.h"
+#include "../memdebug.h"
/* From MacTypes.h (which we cannot include because it is not present in
@@ -205,9 +211,6 @@ static const uint16_t default_ciphers[] = {
#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */
};
-#define DEFAULT_CIPHERS_LEN sizeof(default_ciphers)/sizeof(default_ciphers[0])
-
-
/* pinned public key support tests */
/* version 1 supports macOS 10.12+ and iOS 10+ */
@@ -260,7 +263,7 @@ static OSStatus sectransp_bio_cf_in_read(SSLConnectionRef connection,
void *buf,
size_t *dataLength) /* IN/OUT */
{
- struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
+ const struct Curl_cfilter *cf = (const struct Curl_cfilter *)connection;
struct ssl_connect_data *connssl = cf->ctx;
struct st_ssl_backend_data *backend =
(struct st_ssl_backend_data *)connssl->backend;
@@ -300,7 +303,7 @@ static OSStatus sectransp_bio_cf_out_write(SSLConnectionRef connection,
const void *buf,
size_t *dataLength) /* IN/OUT */
{
- struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
+ const struct Curl_cfilter *cf = (const struct Curl_cfilter *)connection;
struct ssl_connect_data *connssl = cf->ctx;
struct st_ssl_backend_data *backend =
(struct st_ssl_backend_data *)connssl->backend;
@@ -335,30 +338,28 @@ static OSStatus sectransp_bio_cf_out_write(SSLConnectionRef connection,
CF_INLINE void GetDarwinVersionNumber(int *major, int *minor)
{
int mib[2];
- char *os_version;
size_t os_version_len;
- char *os_version_major, *os_version_minor;
- char *tok_buf;
+ char buf[256];
/* Get the Darwin kernel version from the kernel using sysctl(): */
mib[0] = CTL_KERN;
mib[1] = KERN_OSRELEASE;
if(sysctl(mib, 2, NULL, &os_version_len, NULL, 0) == -1)
return;
- os_version = malloc(os_version_len*sizeof(char));
- if(!os_version)
- return;
- if(sysctl(mib, 2, os_version, &os_version_len, NULL, 0) == -1) {
- free(os_version);
- return;
+ if(os_version_len < sizeof(buf)) {
+ if(sysctl(mib, 2, buf, &os_version_len, NULL, 0) != -1) {
+ const char *os = buf;
+ curl_off_t fnum;
+ curl_off_t snum;
+ /* Parse the version: */
+ if(!curlx_str_number(&os, &fnum, INT_MAX) &&
+ !curlx_str_single(&os, '.') &&
+ !curlx_str_number(&os, &snum, INT_MAX)) {
+ *major = (int)fnum;
+ *minor = (int)snum;
+ }
+ }
}
-
- /* Parse the version: */
- os_version_major = strtok_r(os_version, ".", &tok_buf);
- os_version_minor = strtok_r(NULL, ".", &tok_buf);
- *major = atoi(os_version_major);
- *minor = atoi(os_version_minor);
- free(os_version);
}
#endif /* CURL_BUILD_MAC */
@@ -535,8 +536,8 @@ static OSStatus CopyIdentityWithLabel(char *label,
for(i = 0; i < keys_list_count; i++) {
OSStatus err = noErr;
SecCertificateRef cert = NULL;
- SecIdentityRef identity =
- (SecIdentityRef) CFArrayGetValueAtIndex(keys_list, i);
+ const void *item = CFArrayGetValueAtIndex(keys_list, i);
+ SecIdentityRef identity = (SecIdentityRef)CURL_UNCONST(item);
err = SecIdentityCopyCertificate(identity, &cert);
if(err == noErr) {
CFStringRef common_name = NULL;
@@ -665,22 +666,22 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath,
count = CFArrayGetCount(items);
for(i = 0; i < count; i++) {
- CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(items, i);
- CFTypeID itemID = CFGetTypeID(item);
+ const CFTypeRef item = CFArrayGetValueAtIndex(items, i);
+ CFTypeID itemID = CFGetTypeID(item);
if(itemID == CFDictionaryGetTypeID()) {
- CFTypeRef identity = (CFTypeRef) CFDictionaryGetValue(
- (CFDictionaryRef) item,
- kSecImportItemIdentity);
+ const CFTypeRef identity = CFDictionaryGetValue(
+ (CFDictionaryRef)item,
+ kSecImportItemIdentity);
CFRetain(identity);
- *out_cert_and_key = (SecIdentityRef) identity;
+ *out_cert_and_key = (SecIdentityRef)CURL_UNCONST(identity);
break;
}
#if CURL_BUILD_MAC_10_7
else if(itemID == SecCertificateGetTypeID()) {
status = SecIdentityCreateWithCertificate(NULL,
- (SecCertificateRef) item,
- out_cert_and_key);
+ (SecCertificateRef)CURL_UNCONST(item),
+ out_cert_and_key);
break;
}
#endif
@@ -921,7 +922,7 @@ static CURLcode sectransp_set_default_ciphers(struct Curl_easy *data,
/* Intersect the ciphers supported by Secure Transport with the default
* ciphers, using the order of the former. */
for(i = 0; i < supported_len; i++) {
- for(j = 0; j < DEFAULT_CIPHERS_LEN; j++) {
+ for(j = 0; j < CURL_ARRAYSIZE(default_ciphers); j++) {
if(default_ciphers[j] == ciphers[i]) {
ciphers[count++] = ciphers[i];
break;
@@ -1015,7 +1016,7 @@ failed:
return ret;
}
-static void sectransp_session_free(void *sessionid, size_t idsize)
+static void sectransp_session_free(void *sessionid)
{
/* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a
cached session ID inside the Security framework. There is a private
@@ -1023,7 +1024,6 @@ static void sectransp_session_free(void *sessionid, size_t idsize)
got your application rejected from the App Store due to the use of a
private API, so the best we can do is free up our own char array that we
created way back in sectransp_connect_step1... */
- (void)idsize;
Curl_safefree(sessionid);
}
@@ -1091,10 +1091,13 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
if(result != CURLE_OK)
return result;
-#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \
- defined(HAVE_BUILTIN_AVAILABLE)
if(connssl->alpn) {
+#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11
+#ifdef HAVE_BUILTIN_AVAILABLE
if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) {
+#else
+ if(&SSLSetALPNProtocols && &SSLCopyALPNProtocols) {
+#endif
struct alpn_proto_buf proto;
size_t i;
CFStringRef cstr;
@@ -1116,8 +1119,8 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
Curl_alpn_to_proto_str(&proto, connssl->alpn);
infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
+#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */
}
-#endif
if(ssl_config->key) {
infof(data, "WARNING: SSL: CURLOPT_SSLKEY is ignored by Secure "
@@ -1332,19 +1335,20 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
char *ssl_sessionid;
size_t ssl_sessionid_len;
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
- (void **)&ssl_sessionid, &ssl_sessionid_len,
- NULL)) {
+ Curl_ssl_scache_lock(data);
+ ssl_sessionid = Curl_ssl_scache_get_obj(cf, data,
+ connssl->peer.scache_key);
+ if(ssl_sessionid) {
/* we got a session id, use it! */
- err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
- Curl_ssl_sessionid_unlock(data);
+ err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid,
+ strlen(ssl_sessionid));
+ Curl_ssl_scache_unlock(data);
if(err != noErr) {
failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err);
return CURLE_SSL_CONNECT_ERROR;
}
- /* Informational message */
- infof(data, "SSL reusing session ID");
+ else
+ infof(data, "SSL reusing session ID");
}
/* If there is not one, then let's make one up! This has to be done prior
to starting the handshake. */
@@ -1358,15 +1362,17 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
if(err != noErr) {
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_unlock(data);
failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err);
return CURLE_SSL_CONNECT_ERROR;
}
- result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
- ssl_sessionid, ssl_sessionid_len,
+ /* This is all a bit weird, as we have not handshaked yet.
+ * I hope this backend will go away soon. */
+ result = Curl_ssl_scache_add_obj(cf, data, connssl->peer.scache_key,
+ (void *)ssl_sessionid,
sectransp_session_free);
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_unlock(data);
if(result)
return result;
}
@@ -1429,7 +1435,7 @@ static long pem_to_der(const char *in, unsigned char **out, size_t *outlen)
}
b64[j] = '\0';
- err = Curl_base64_decode((const char *)b64, out, outlen);
+ err = curlx_base64_decode((const char *)b64, out, outlen);
free(b64);
if(err) {
free(*out);
@@ -1448,7 +1454,7 @@ static int read_cert(const char *file, unsigned char **out, size_t *outlen)
unsigned char buf[512];
struct dynbuf certs;
- Curl_dyn_init(&certs, MAX_CERTS_SIZE);
+ curlx_dyn_init(&certs, MAX_CERTS_SIZE);
fd = open(file, 0);
if(fd < 0)
@@ -1460,18 +1466,18 @@ static int read_cert(const char *file, unsigned char **out, size_t *outlen)
break;
if(n < 0) {
close(fd);
- Curl_dyn_free(&certs);
+ curlx_dyn_free(&certs);
return -1;
}
- if(Curl_dyn_addn(&certs, buf, n)) {
+ if(curlx_dyn_addn(&certs, buf, n)) {
close(fd);
return -1;
}
}
close(fd);
- *out = Curl_dyn_uptr(&certs);
- *outlen = Curl_dyn_len(&certs);
+ *out = curlx_dyn_uptr(&certs);
+ *outlen = curlx_dyn_len(&certs);
return 0;
}
@@ -1504,9 +1510,11 @@ static CURLcode append_cert_to_array(struct Curl_easy *data,
case CURLE_OK:
break;
case CURLE_PEER_FAILED_VERIFICATION:
+ CFRelease(cacert);
return CURLE_SSL_CACERT_BADFILE;
case CURLE_OUT_OF_MEMORY:
default:
+ CFRelease(cacert);
return result;
}
free(certp);
@@ -1687,7 +1695,8 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
const char *pinnedpubkey)
{ /* Scratch */
size_t pubkeylen, realpubkeylen, spkiHeaderLength = 24;
- unsigned char *pubkey = NULL, *realpubkey = NULL;
+ const unsigned char *pubkey = NULL;
+ unsigned char *realpubkey = NULL;
const unsigned char *spkiHeader = NULL;
CFDataRef publicKeyBits = NULL;
@@ -1737,7 +1746,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
#endif /* SECTRANSP_PINNEDPUBKEY_V2 */
pubkeylen = (size_t)CFDataGetLength(publicKeyBits);
- pubkey = (unsigned char *)CFDataGetBytePtr(publicKeyBits);
+ pubkey = (const unsigned char *)CFDataGetBytePtr(publicKeyBits);
switch(pubkeylen) {
case 526:
@@ -2083,10 +2092,13 @@ check_handshake:
break;
}
-#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \
- defined(HAVE_BUILTIN_AVAILABLE)
if(connssl->alpn) {
+#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11
+#ifdef HAVE_BUILTIN_AVAILABLE
if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) {
+#else
+ if(&SSLSetALPNProtocols && &SSLCopyALPNProtocols) {
+#endif
CFArrayRef alpnArr = NULL;
CFStringRef chosenProtocol = NULL;
err = SSLCopyALPNProtocols(backend->ssl_ctx, &alpnArr);
@@ -2113,8 +2125,8 @@ check_handshake:
if(alpnArr)
CFRelease(alpnArr);
}
+#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */
}
-#endif
return CURLE_OK;
}
@@ -2122,7 +2134,7 @@ check_handshake:
static CURLcode
add_cert_to_certinfo(struct Curl_easy *data,
- SecCertificateRef server_cert,
+ const SecCertificateRef server_cert,
int idx)
{
CURLcode result = CURLE_OK;
@@ -2142,7 +2154,7 @@ add_cert_to_certinfo(struct Curl_easy *data,
static CURLcode
collect_server_cert_single(struct Curl_cfilter *cf, struct Curl_easy *data,
- SecCertificateRef server_cert,
+ const SecCertificateRef server_cert,
CFIndex idx)
{
CURLcode result = CURLE_OK;
@@ -2239,8 +2251,8 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf,
if(ssl_config->certinfo)
result = Curl_ssl_init_certinfo(data, (int)count);
for(i = 0L ; !result && (i < count) ; i++) {
- server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs,
- i);
+ const void *item = CFArrayGetValueAtIndex(server_certs, i);
+ server_cert = (SecCertificateRef)CURL_UNCONST(item);
result = collect_server_cert_single(cf, data, server_cert, i);
}
CFRelease(server_certs);
@@ -2256,7 +2268,8 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf,
if(ssl_config->certinfo)
result = Curl_ssl_init_certinfo(data, (int)count);
for(i = 0L ; !result && (i < count) ; i++) {
- server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, i);
+ const void *item = CFArrayGetValueAtIndex(server_certs, i);
+ server_cert = (SecCertificateRef)CURL_UNCONST(item);
result = collect_server_cert_single(cf, data, server_cert, i);
}
CFRelease(server_certs);
@@ -2283,15 +2296,12 @@ static CURLcode sectransp_connect_step3(struct Curl_cfilter *cf,
return CURLE_OK;
}
-static CURLcode
-sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
- bool nonblocking,
- bool *done)
+static CURLcode sectransp_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
{
CURLcode result;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
- int what;
/* check if the connection has already been established */
if(ssl_connection_complete == connssl->state) {
@@ -2299,73 +2309,20 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
return CURLE_OK;
}
- if(ssl_connect_1 == connssl->connecting_state) {
- /* Find out how much more time we are allowed */
- const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
+ *done = FALSE;
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
+ if(ssl_connect_1 == connssl->connecting_state) {
result = sectransp_connect_step1(cf, data);
if(result)
return result;
}
- while(ssl_connect_2 == connssl->connecting_state) {
-
- /* check allowed time left */
- const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
-
- /* if ssl is expecting something, check if it is available. */
- if(connssl->io_need) {
-
- curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
- sockfd : CURL_SOCKET_BAD;
- curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
- sockfd : CURL_SOCKET_BAD;
-
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking ? 0 : timeout_ms);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
- }
- else if(0 == what) {
- if(nonblocking) {
- *done = FALSE;
- return CURLE_OK;
- }
- else {
- /* timeout */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
- }
- /* socket is readable or writable */
- }
-
- /* Run transaction, and return to the caller if it failed or if this
- * connection is done nonblocking and this loop would execute again. This
- * permits the owner of a multi handle to abort a connection attempt
- * before step2 has completed while ensuring that a client using select()
- * or epoll() will always have a valid fdset to wait on.
- */
+ if(ssl_connect_2 == connssl->connecting_state) {
result = sectransp_connect_step2(cf, data);
- if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
+ if(result)
return result;
-
- } /* repeat step2 until all transactions are done. */
-
+ }
if(ssl_connect_3 == connssl->connecting_state) {
result = sectransp_connect_step3(cf, data);
@@ -2378,34 +2335,6 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
connssl->state = ssl_connection_complete;
*done = TRUE;
}
- else
- *done = FALSE;
-
- /* Reset our connect state machine */
- connssl->connecting_state = ssl_connect_1;
-
- return CURLE_OK;
-}
-
-static CURLcode sectransp_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
-{
- return sectransp_connect_common(cf, data, TRUE, done);
-}
-
-static CURLcode sectransp_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- CURLcode result;
- bool done = FALSE;
-
- result = sectransp_connect_common(cf, data, FALSE, &done);
-
- if(result)
- return result;
-
- DEBUGASSERT(done);
return CURLE_OK;
}
@@ -2424,7 +2353,7 @@ static CURLcode sectransp_shutdown(struct Curl_cfilter *cf,
struct st_ssl_backend_data *backend =
(struct st_ssl_backend_data *)connssl->backend;
CURLcode result = CURLE_OK;
- ssize_t nread;
+ ssize_t nread = 0;
char buf[1024];
size_t i;
@@ -2540,7 +2469,7 @@ static bool sectransp_data_pending(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
if(backend->ssl_ctx) { /* SSL is in use */
- CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending");
+ CURL_TRC_CF((struct Curl_easy *)CURL_UNCONST(data), cf, "data_pending");
err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer);
if(err == noErr)
return buffer > 0UL;
@@ -2742,32 +2671,32 @@ const struct Curl_ssl Curl_ssl_sectransp = {
sizeof(struct st_ssl_backend_data),
- Curl_none_init, /* init */
- Curl_none_cleanup, /* cleanup */
+ NULL, /* init */
+ NULL, /* cleanup */
sectransp_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
sectransp_shutdown, /* shutdown */
sectransp_data_pending, /* data_pending */
sectransp_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
sectransp_connect, /* connect */
- sectransp_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
sectransp_get_internals, /* get_internals */
sectransp_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
sectransp_false_start, /* false_start */
sectransp_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
sectransp_recv, /* recv decrypted data */
sectransp_send, /* send data to encrypt */
NULL, /* get_channel_binding */
};
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic pop
+#endif
+
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
diff --git a/libs/libcurl/src/vtls/sectransp.h b/libs/libcurl/src/vtls/sectransp.h
index 3b79169ab3..9011f17508 100644
--- a/libs/libcurl/src/vtls/sectransp.h
+++ b/libs/libcurl/src/vtls/sectransp.h
@@ -24,7 +24,7 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_SECTRANSP
diff --git a/libs/libcurl/src/vtls/vtls.c b/libs/libcurl/src/vtls/vtls.c
index 0a90bf46fd..f707e92577 100644
--- a/libs/libcurl/src/vtls/vtls.c
+++ b/libs/libcurl/src/vtls/vtls.c
@@ -38,7 +38,7 @@
https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
@@ -50,11 +50,12 @@
#include <fcntl.h>
#endif
-#include "urldata.h"
-#include "cfilters.h"
+#include "../urldata.h"
+#include "../cfilters.h"
#include "vtls.h" /* generic SSL protos etc */
#include "vtls_int.h"
+#include "vtls_scache.h"
#include "openssl.h" /* OpenSSL versions */
#include "gtls.h" /* GnuTLS versions */
@@ -65,34 +66,30 @@
#include "bearssl.h" /* BearSSL versions */
#include "rustls.h" /* Rustls versions */
-#include "slist.h"
-#include "sendf.h"
-#include "strcase.h"
-#include "url.h"
-#include "progress.h"
-#include "share.h"
-#include "multiif.h"
-#include "timeval.h"
-#include "curl_md5.h"
-#include "warnless.h"
-#include "curl_base64.h"
-#include "curl_printf.h"
-#include "inet_pton.h"
-#include "connect.h"
-#include "select.h"
-#include "strdup.h"
-#include "rand.h"
+#include "../slist.h"
+#include "../sendf.h"
+#include "../strcase.h"
+#include "../url.h"
+#include "../progress.h"
+#include "../share.h"
+#include "../multiif.h"
+#include "../curlx/timeval.h"
+#include "../curl_md5.h"
+#include "../curl_sha256.h"
+#include "../curlx/warnless.h"
+#include "../curlx/base64.h"
+#include "../curl_printf.h"
+#include "../curlx/inet_pton.h"
+#include "../connect.h"
+#include "../select.h"
+#include "../strdup.h"
+#include "../rand.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
-/* convenience macro to check if this handle is using a shared SSL session */
-#define SSLSESSION_SHARED(data) (data->share && \
- (data->share->specifier & \
- (1<<CURL_LOCK_DATA_SSL_SESSION)))
-
#define CLONE_STRING(var) \
do { \
if(source->var) { \
@@ -157,17 +154,19 @@ static const struct alpn_spec ALPN_SPEC_H2_H11 = {
};
#endif
-static const struct alpn_spec *alpn_get_spec(int httpwant, bool use_alpn)
+static const struct alpn_spec *
+alpn_get_spec(http_majors allowed, bool use_alpn)
{
if(!use_alpn)
return NULL;
#ifdef USE_HTTP2
- if(httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE)
+ if(allowed & CURL_HTTP_V2x) {
+ if(allowed & CURL_HTTP_V1x)
+ return &ALPN_SPEC_H2_H11;
return &ALPN_SPEC_H2;
- if(httpwant >= CURL_HTTP_VERSION_2)
- return &ALPN_SPEC_H2_H11;
+ }
#else
- (void)httpwant;
+ (void)allowed;
#endif
/* Use the ALPN protocol "http/1.1" for HTTP/1.x.
Avoid "http/1.0" because some servers do not support it. */
@@ -216,6 +215,7 @@ match_ssl_primary_config(struct Curl_easy *data,
strcasecompare(c1->cipher_list, c2->cipher_list) &&
strcasecompare(c1->cipher_list13, c2->cipher_list13) &&
strcasecompare(c1->curves, c2->curves) &&
+ strcasecompare(c1->signature_algorithms, c2->signature_algorithms) &&
strcasecompare(c1->CRLfile, c2->CRLfile) &&
strcasecompare(c1->pinned_key, c2->pinned_key))
return TRUE;
@@ -260,6 +260,7 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source,
CLONE_STRING(cipher_list13);
CLONE_STRING(pinned_key);
CLONE_STRING(curves);
+ CLONE_STRING(signature_algorithms);
CLONE_STRING(CRLfile);
#ifdef USE_TLS_SRP
CLONE_STRING(username);
@@ -282,6 +283,7 @@ static void free_primary_ssl_config(struct ssl_primary_config *sslc)
Curl_safefree(sslc->ca_info_blob);
Curl_safefree(sslc->issuercert_blob);
Curl_safefree(sslc->curves);
+ Curl_safefree(sslc->signature_algorithms);
Curl_safefree(sslc->CRLfile);
#ifdef USE_TLS_SRP
Curl_safefree(sslc->username);
@@ -300,6 +302,8 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data)
data->set.str[STRING_SSL_CIPHER_LIST];
data->set.ssl.primary.cipher_list13 =
data->set.str[STRING_SSL_CIPHER13_LIST];
+ data->set.ssl.primary.signature_algorithms =
+ data->set.str[STRING_SSL_SIGNATURE_ALGORITHMS];
data->set.ssl.primary.pinned_key =
data->set.str[STRING_SSL_PINNEDPUBLICKEY];
data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT];
@@ -426,7 +430,9 @@ int Curl_ssl_init(void)
return 1;
init_ssl = TRUE; /* never again */
- return Curl_ssl->init();
+ if(Curl_ssl->init)
+ return Curl_ssl->init();
+ return 1;
}
static bool ssl_prefs_check(struct Curl_easy *data)
@@ -463,9 +469,10 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data,
if(!ctx)
return NULL;
+ ctx->ssl_impl = Curl_ssl;
ctx->alpn = alpn;
Curl_bufq_init2(&ctx->earlydata, CURL_SSL_EARLY_MAX, 1, BUFQ_OPT_NO_SPARES);
- ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data);
+ ctx->backend = calloc(1, ctx->ssl_impl->sizeof_ssl_backend_data);
if(!ctx->backend) {
free(ctx);
return NULL;
@@ -476,308 +483,13 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data,
static void cf_ctx_free(struct ssl_connect_data *ctx)
{
if(ctx) {
- Curl_safefree(ctx->alpn_negotiated);
+ Curl_safefree(ctx->negotiated.alpn);
Curl_bufq_free(&ctx->earlydata);
free(ctx->backend);
free(ctx);
}
}
-static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- CURLcode result;
-
- if(!ssl_prefs_check(data))
- return CURLE_SSL_CONNECT_ERROR;
-
- /* mark this is being ssl-enabled from here on. */
- connssl->state = ssl_connection_negotiating;
-
- result = Curl_ssl->connect_blocking(cf, data);
-
- if(!result) {
- DEBUGASSERT(connssl->state == ssl_connection_complete);
- }
-
- return result;
-}
-
-static CURLcode
-ssl_connect_nonblocking(struct Curl_cfilter *cf, struct Curl_easy *data,
- bool *done)
-{
- if(!ssl_prefs_check(data))
- return CURLE_SSL_CONNECT_ERROR;
-
- /* mark this is being ssl requested from here on. */
- return Curl_ssl->connect_nonblocking(cf, data, done);
-}
-
-/*
- * Lock shared SSL session data
- */
-void Curl_ssl_sessionid_lock(struct Curl_easy *data)
-{
- if(SSLSESSION_SHARED(data))
- Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
-}
-
-/*
- * Unlock shared SSL session data
- */
-void Curl_ssl_sessionid_unlock(struct Curl_easy *data)
-{
- if(SSLSESSION_SHARED(data))
- Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
-}
-
-/*
- * Check if there is a session ID for the given connection in the cache, and if
- * there is one suitable, it is provided. Returns TRUE when no entry matched.
- */
-bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct ssl_peer *peer,
- void **ssl_sessionid,
- size_t *idsize, /* set 0 if unknown */
- char **palpn)
-{
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
- struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- struct Curl_ssl_session *check;
- size_t i;
- long *general_age;
- bool no_match = TRUE;
-
- *ssl_sessionid = NULL;
- if(palpn)
- *palpn = NULL;
- if(!ssl_config)
- return TRUE;
-
- DEBUGASSERT(ssl_config->primary.cache_session);
-
- if(!ssl_config->primary.cache_session || !data->state.session)
- /* session ID reuse is disabled or the session cache has not been
- setup */
- return TRUE;
-
- /* Lock if shared */
- if(SSLSESSION_SHARED(data))
- general_age = &data->share->sessionage;
- else
- general_age = &data->state.sessionage;
-
- for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
- check = &data->state.session[i];
- if(!check->sessionid)
- /* not session ID means blank entry */
- continue;
- if(strcasecompare(peer->hostname, check->name) &&
- ((!cf->conn->bits.conn_to_host && !check->conn_to_host) ||
- (cf->conn->bits.conn_to_host && check->conn_to_host &&
- strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) &&
- ((!cf->conn->bits.conn_to_port && check->conn_to_port == -1) ||
- (cf->conn->bits.conn_to_port && check->conn_to_port != -1 &&
- cf->conn->conn_to_port == check->conn_to_port)) &&
- (peer->port == check->remote_port) &&
- (peer->transport == check->transport) &&
- strcasecompare(cf->conn->handler->scheme, check->scheme) &&
- match_ssl_primary_config(data, conn_config, &check->ssl_config)) {
- /* yes, we have a session ID! */
- (*general_age)++; /* increase general age */
- check->age = *general_age; /* set this as used in this age */
- *ssl_sessionid = check->sessionid;
- if(idsize)
- *idsize = check->idsize;
- if(palpn)
- *palpn = check->alpn;
- no_match = FALSE;
- break;
- }
- }
-
- CURL_TRC_CF(data, cf, "%s cached session ID for %s://%s:%d",
- no_match ? "No" : "Found",
- cf->conn->handler->scheme, peer->hostname, peer->port);
- return no_match;
-}
-
-/*
- * Kill a single session ID entry in the cache.
- */
-void Curl_ssl_kill_session(struct Curl_ssl_session *session)
-{
- if(session->sessionid) {
- /* defensive check */
-
- /* free the ID the SSL-layer specific way */
- session->sessionid_free(session->sessionid, session->idsize);
-
- session->sessionid = NULL;
- session->sessionid_free = NULL;
- session->age = 0; /* fresh */
-
- free_primary_ssl_config(&session->ssl_config);
-
- Curl_safefree(session->name);
- Curl_safefree(session->conn_to_host);
- Curl_safefree(session->alpn);
- }
-}
-
-/*
- * Delete the given session ID from the cache.
- */
-void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid)
-{
- size_t i;
-
- for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
- struct Curl_ssl_session *check = &data->state.session[i];
-
- if(check->sessionid == ssl_sessionid) {
- Curl_ssl_kill_session(check);
- break;
- }
- }
-}
-
-CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct ssl_peer *peer,
- const char *alpn,
- void *ssl_sessionid,
- size_t idsize,
- Curl_ssl_sessionid_dtor *sessionid_free_cb)
-{
- struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
- size_t i;
- struct Curl_ssl_session *store;
- long oldest_age;
- char *clone_host = NULL;
- char *clone_conn_to_host = NULL;
- char *clone_alpn = NULL;
- int conn_to_port;
- long *general_age;
- void *old_sessionid;
- size_t old_size;
- CURLcode result = CURLE_OUT_OF_MEMORY;
-
- DEBUGASSERT(ssl_sessionid);
- DEBUGASSERT(sessionid_free_cb);
-
- if(!data->state.session) {
- sessionid_free_cb(ssl_sessionid, idsize);
- return CURLE_OK;
- }
-
- if(!Curl_ssl_getsessionid(cf, data, peer, &old_sessionid, &old_size, NULL)) {
- if((old_size == idsize) &&
- ((old_sessionid == ssl_sessionid) ||
- (idsize && !memcmp(old_sessionid, ssl_sessionid, idsize)))) {
- /* the very same */
- sessionid_free_cb(ssl_sessionid, idsize);
- return CURLE_OK;
- }
- Curl_ssl_delsessionid(data, old_sessionid);
- }
-
- store = &data->state.session[0];
- oldest_age = data->state.session[0].age; /* zero if unused */
- DEBUGASSERT(ssl_config->primary.cache_session);
- (void)ssl_config;
-
- clone_host = strdup(peer->hostname);
- if(!clone_host)
- goto out;
-
- if(cf->conn->bits.conn_to_host) {
- clone_conn_to_host = strdup(cf->conn->conn_to_host.name);
- if(!clone_conn_to_host)
- goto out;
- }
-
- clone_alpn = alpn ? strdup(alpn) : NULL;
- if(alpn && !clone_alpn)
- goto out;
-
- if(cf->conn->bits.conn_to_port)
- conn_to_port = cf->conn->conn_to_port;
- else
- conn_to_port = -1;
-
- /* Now we should add the session ID and the hostname to the cache, (remove
- the oldest if necessary) */
-
- /* If using shared SSL session, lock! */
- if(SSLSESSION_SHARED(data)) {
- general_age = &data->share->sessionage;
- }
- else {
- general_age = &data->state.sessionage;
- }
-
- /* find an empty slot for us, or find the oldest */
- for(i = 1; (i < data->set.general_ssl.max_ssl_sessions) &&
- data->state.session[i].sessionid; i++) {
- if(data->state.session[i].age < oldest_age) {
- oldest_age = data->state.session[i].age;
- store = &data->state.session[i];
- }
- }
- if(i == data->set.general_ssl.max_ssl_sessions)
- /* cache is full, we must "kill" the oldest entry! */
- Curl_ssl_kill_session(store);
- else
- store = &data->state.session[i]; /* use this slot */
-
- /* now init the session struct wisely */
- if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) {
- free_primary_ssl_config(&store->ssl_config);
- store->sessionid = NULL; /* let caller free sessionid */
- goto out;
- }
- store->sessionid = ssl_sessionid;
- store->idsize = idsize;
- store->sessionid_free = sessionid_free_cb;
- store->age = *general_age; /* set current age */
- /* free it if there is one already present */
- free(store->name);
- free(store->conn_to_host);
- store->name = clone_host; /* clone hostname */
- clone_host = NULL;
- store->conn_to_host = clone_conn_to_host; /* clone connect to hostname */
- clone_conn_to_host = NULL;
- store->conn_to_port = conn_to_port; /* connect to port number */
- store->alpn = clone_alpn;
- clone_alpn = NULL;
- /* port number */
- store->remote_port = peer->port;
- store->scheme = cf->conn->handler->scheme;
- store->transport = peer->transport;
-
- result = CURLE_OK;
-
-out:
- free(clone_host);
- free(clone_conn_to_host);
- free(clone_alpn);
- if(result) {
- failf(data, "Failed to add Session ID to cache for %s://%s:%d [%s]",
- store->scheme, store->name, store->remote_port,
- Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server");
- sessionid_free_cb(ssl_sessionid, idsize);
- return result;
- }
- CURL_TRC_CF(data, cf, "Added Session ID to cache for %s://%s:%d [%s]",
- store->scheme, store->name, store->remote_port,
- Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server");
- return CURLE_OK;
-}
-
CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
struct dynbuf *binding)
{
@@ -788,18 +500,8 @@ CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
void Curl_ssl_close_all(struct Curl_easy *data)
{
- /* kill the session ID cache if not shared */
- if(data->state.session && !SSLSESSION_SHARED(data)) {
- size_t i;
- for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++)
- /* the single-killer function handles empty table slots */
- Curl_ssl_kill_session(&data->state.session[i]);
-
- /* free the cache data */
- Curl_safefree(data->state.session);
- }
-
- Curl_ssl->close_all(data);
+ if(Curl_ssl->close_all)
+ Curl_ssl->close_all(data);
}
void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -828,43 +530,26 @@ void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
*/
CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine)
{
- return Curl_ssl->set_engine(data, engine);
+ if(Curl_ssl->set_engine)
+ return Curl_ssl->set_engine(data, engine);
+ return CURLE_NOT_BUILT_IN;
}
/* Selects the default SSL crypto engine
*/
CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data)
{
- return Curl_ssl->set_engine_default(data);
+ if(Curl_ssl->set_engine_default)
+ return Curl_ssl->set_engine_default(data);
+ return CURLE_NOT_BUILT_IN;
}
/* Return list of OpenSSL crypto engine names. */
struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data)
{
- return Curl_ssl->engines_list(data);
-}
-
-/*
- * This sets up a session ID cache to the specified size. Make sure this code
- * is agnostic to what underlying SSL technology we use.
- */
-CURLcode Curl_ssl_initsessions(struct Curl_easy *data, size_t amount)
-{
- struct Curl_ssl_session *session;
-
- if(data->state.session)
- /* this is just a precaution to prevent multiple inits */
- return CURLE_OK;
-
- session = calloc(amount, sizeof(struct Curl_ssl_session));
- if(!session)
- return CURLE_OUT_OF_MEMORY;
-
- /* store the info in the SSL section */
- data->set.general_ssl.max_ssl_sessions = amount;
- data->state.session = session;
- data->state.sessionage = 1; /* this is brand new */
- return CURLE_OK;
+ if(Curl_ssl->engines_list)
+ return Curl_ssl->engines_list(data);
+ return NULL;
}
static size_t multissl_version(char *buffer, size_t size);
@@ -931,17 +616,17 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
DEBUGASSERT(certnum < ci->num_of_certs);
- Curl_dyn_init(&build, CURL_X509_STR_MAX);
+ curlx_dyn_init(&build, CURL_X509_STR_MAX);
- if(Curl_dyn_add(&build, label) ||
- Curl_dyn_addn(&build, ":", 1) ||
- Curl_dyn_addn(&build, value, valuelen))
+ if(curlx_dyn_add(&build, label) ||
+ curlx_dyn_addn(&build, ":", 1) ||
+ curlx_dyn_addn(&build, value, valuelen))
return CURLE_OUT_OF_MEMORY;
nl = Curl_slist_append_nodup(ci->certinfo[certnum],
- Curl_dyn_ptr(&build));
+ curlx_dyn_ptr(&build));
if(!nl) {
- Curl_dyn_free(&build);
+ curlx_dyn_free(&build);
curl_slist_free_all(ci->certinfo[certnum]);
result = CURLE_OUT_OF_MEMORY;
}
@@ -950,7 +635,7 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
return result;
}
-/* get 32 bits of random */
+/* get length bytes of randomness */
CURLcode Curl_ssl_random(struct Curl_easy *data,
unsigned char *entropy,
size_t length)
@@ -978,7 +663,7 @@ static CURLcode pubkey_pem_to_der(const char *pem,
if(!pem)
return CURLE_BAD_CONTENT_ENCODING;
- Curl_dyn_init(&pbuf, MAX_PINNED_PUBKEY_SIZE);
+ curlx_dyn_init(&pbuf, MAX_PINNED_PUBKEY_SIZE);
begin_pos = strstr(pem, "-----BEGIN PUBLIC KEY-----");
if(!begin_pos)
@@ -1006,16 +691,19 @@ static CURLcode pubkey_pem_to_der(const char *pem,
*/
while(pem_count < pem_len) {
if('\n' != pem[pem_count] && '\r' != pem[pem_count]) {
- result = Curl_dyn_addn(&pbuf, &pem[pem_count], 1);
+ result = curlx_dyn_addn(&pbuf, &pem[pem_count], 1);
if(result)
return result;
}
++pem_count;
}
- result = Curl_base64_decode(Curl_dyn_ptr(&pbuf), der, der_len);
-
- Curl_dyn_free(&pbuf);
+ if(curlx_dyn_len(&pbuf)) {
+ result = curlx_base64_decode(curlx_dyn_ptr(&pbuf), der, der_len);
+ curlx_dyn_free(&pbuf);
+ }
+ else
+ result = CURLE_BAD_CONTENT_ENCODING;
return result;
}
@@ -1059,9 +747,9 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
sha256sumdigest, CURL_SHA256_DIGEST_LENGTH);
if(!encode)
- encode = Curl_base64_encode((char *)sha256sumdigest,
- CURL_SHA256_DIGEST_LENGTH, &encoded,
- &encodedlen);
+ encode = curlx_base64_encode((char *)sha256sumdigest,
+ CURL_SHA256_DIGEST_LENGTH, &encoded,
+ &encodedlen);
Curl_safefree(sha256sumdigest);
if(encode)
@@ -1080,8 +768,8 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
do {
end_pos = strstr(begin_pos, ";sha256//");
/*
- * if there is an end_pos, null terminate,
- * otherwise it will go to the end of the original string
+ * if there is an end_pos, null-terminate, otherwise it will go to the
+ * end of the original string
*/
if(end_pos)
end_pos[0] = '\0';
@@ -1116,7 +804,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
if(!fp)
return result;
- Curl_dyn_init(&buf, MAX_PINNED_PUBKEY_SIZE);
+ curlx_dyn_init(&buf, MAX_PINNED_PUBKEY_SIZE);
/* Determine the file's size */
if(fseek(fp, 0, SEEK_END))
@@ -1144,23 +832,23 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
size_t want = left > sizeof(buffer) ? sizeof(buffer) : left;
if(want != fread(buffer, 1, want, fp))
goto end;
- if(Curl_dyn_addn(&buf, buffer, want))
+ if(curlx_dyn_addn(&buf, buffer, want))
goto end;
left -= want;
} while(left);
/* If the sizes are the same, it cannot be base64 encoded, must be der */
if(pubkeylen == size) {
- if(!memcmp(pubkey, Curl_dyn_ptr(&buf), pubkeylen))
+ if(!memcmp(pubkey, curlx_dyn_ptr(&buf), pubkeylen))
result = CURLE_OK;
goto end;
}
/*
- * Otherwise we will assume it is PEM and try to decode it
- * after placing null terminator
+ * Otherwise we will assume it is PEM and try to decode it after placing
+ * null-terminator
*/
- pem_read = pubkey_pem_to_der(Curl_dyn_ptr(&buf), &pem_ptr, &pem_len);
+ pem_read = pubkey_pem_to_der(curlx_dyn_ptr(&buf), &pem_ptr, &pem_len);
/* if it was not read successfully, exit */
if(pem_read)
goto end;
@@ -1172,7 +860,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
if(pubkeylen == pem_len && !memcmp(pubkey, pem_ptr, pubkeylen))
result = CURLE_OK;
end:
- Curl_dyn_free(&buf);
+ curlx_dyn_free(&buf);
Curl_safefree(pem_ptr);
fclose(fp);
}
@@ -1185,96 +873,18 @@ end:
*/
bool Curl_ssl_cert_status_request(void)
{
- return Curl_ssl->cert_status_request();
+ if(Curl_ssl->cert_status_request)
+ return Curl_ssl->cert_status_request();
+ return FALSE;
}
/*
* Check whether the SSL backend supports false start.
*/
-bool Curl_ssl_false_start(struct Curl_easy *data)
-{
- (void)data;
- return Curl_ssl->false_start();
-}
-
-/*
- * Default implementations for unsupported functions.
- */
-
-int Curl_none_init(void)
-{
- return 1;
-}
-
-void Curl_none_cleanup(void)
-{ }
-
-CURLcode Curl_none_shutdown(struct Curl_cfilter *cf UNUSED_PARAM,
- struct Curl_easy *data UNUSED_PARAM,
- bool send_shutdown UNUSED_PARAM,
- bool *done)
-{
- (void)data;
- (void)cf;
- (void)send_shutdown;
- /* Every SSL backend should have a shutdown implementation. Until we
- * have implemented that, we put this fake in place. */
- *done = TRUE;
- return CURLE_OK;
-}
-
-int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
- (void)cf;
- (void)data;
- return -1;
-}
-
-void Curl_none_close_all(struct Curl_easy *data UNUSED_PARAM)
-{
- (void)data;
-}
-
-void Curl_none_session_free(void *ptr UNUSED_PARAM)
-{
- (void)ptr;
-}
-
-bool Curl_none_data_pending(struct Curl_cfilter *cf UNUSED_PARAM,
- const struct Curl_easy *data UNUSED_PARAM)
-{
- (void)cf;
- (void)data;
- return 0;
-}
-
-bool Curl_none_cert_status_request(void)
-{
- return FALSE;
-}
-
-CURLcode Curl_none_set_engine(struct Curl_easy *data UNUSED_PARAM,
- const char *engine UNUSED_PARAM)
-{
- (void)data;
- (void)engine;
- return CURLE_NOT_BUILT_IN;
-}
-
-CURLcode Curl_none_set_engine_default(struct Curl_easy *data UNUSED_PARAM)
-{
- (void)data;
- return CURLE_NOT_BUILT_IN;
-}
-
-struct curl_slist *Curl_none_engines_list(struct Curl_easy *data UNUSED_PARAM)
-{
- (void)data;
- return (struct curl_slist *)NULL;
-}
-
-bool Curl_none_false_start(void)
+bool Curl_ssl_false_start(void)
{
+ if(Curl_ssl->false_start)
+ return Curl_ssl->false_start();
return FALSE;
}
@@ -1282,24 +892,17 @@ static int multissl_init(void)
{
if(multissl_setup(NULL))
return 1;
- return Curl_ssl->init();
+ if(Curl_ssl->init)
+ return Curl_ssl->init();
+ return 1;
}
static CURLcode multissl_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
+ struct Curl_easy *data, bool *done)
{
if(multissl_setup(NULL))
return CURLE_FAILED_INIT;
- return Curl_ssl->connect_blocking(cf, data);
-}
-
-static CURLcode multissl_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
-{
- if(multissl_setup(NULL))
- return CURLE_FAILED_INIT;
- return Curl_ssl->connect_nonblocking(cf, data, done);
+ return Curl_ssl->do_connect(cf, data, done);
}
static void multissl_adjust_pollset(struct Curl_cfilter *cf,
@@ -1351,26 +954,22 @@ static const struct Curl_ssl Curl_ssl_multi = {
(size_t)-1, /* something insanely large to be on the safe side */
multissl_init, /* init */
- Curl_none_cleanup, /* cleanup */
+ NULL, /* cleanup */
multissl_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
- Curl_none_shutdown, /* shutdown */
- Curl_none_data_pending, /* data_pending */
+ NULL, /* shutdown */
+ NULL, /* data_pending */
NULL, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
multissl_connect, /* connect */
- multissl_connect_nonblocking, /* connect_nonblocking */
- multissl_adjust_pollset, /* adjust_pollset */
+ multissl_adjust_pollset, /* adjust_pollset */
multissl_get_internals, /* get_internals */
multissl_close, /* close_one */
- Curl_none_close_all, /* close_all */
- 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, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
NULL, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
multissl_recv_plain, /* recv decrypted data */
multissl_send_plain, /* send data to encrypt */
NULL, /* get_channel_binding */
@@ -1432,7 +1031,8 @@ void Curl_ssl_cleanup(void)
{
if(init_ssl) {
/* only cleanup if we did a previous init */
- Curl_ssl->cleanup();
+ if(Curl_ssl->cleanup)
+ Curl_ssl->cleanup();
#if defined(CURL_WITH_MULTI_SSL)
Curl_ssl = &Curl_ssl_multi;
#endif
@@ -1482,8 +1082,8 @@ static size_t multissl_version(char *buffer, size_t size)
static int multissl_setup(const struct Curl_ssl *backend)
{
- const char *env;
- char *env_tmp;
+ int i;
+ char *env;
if(Curl_ssl != &Curl_ssl_multi)
return 1;
@@ -1496,25 +1096,31 @@ static int multissl_setup(const struct Curl_ssl *backend)
if(!available_backends[0])
return 1;
- env = env_tmp = curl_getenv("CURL_SSL_BACKEND");
-#ifdef CURL_DEFAULT_SSL_BACKEND
- if(!env)
- env = CURL_DEFAULT_SSL_BACKEND;
-#endif
+ env = curl_getenv("CURL_SSL_BACKEND");
if(env) {
- int i;
for(i = 0; available_backends[i]; i++) {
if(strcasecompare(env, available_backends[i]->info.name)) {
Curl_ssl = available_backends[i];
- free(env_tmp);
+ free(env);
return 0;
}
}
}
+#ifdef CURL_DEFAULT_SSL_BACKEND
+ for(i = 0; available_backends[i]; i++) {
+ if(strcasecompare(CURL_DEFAULT_SSL_BACKEND,
+ available_backends[i]->info.name)) {
+ Curl_ssl = available_backends[i];
+ free(env);
+ return 0;
+ }
+ }
+#endif
+
/* Fall back to first available backend */
Curl_ssl = available_backends[0];
- free(env_tmp);
+ free(env);
return 0;
}
@@ -1565,11 +1171,12 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
void Curl_ssl_peer_cleanup(struct ssl_peer *peer)
{
+ Curl_safefree(peer->sni);
if(peer->dispname != peer->hostname)
free(peer->dispname);
- free(peer->sni);
- free(peer->hostname);
- peer->hostname = peer->sni = peer->dispname = NULL;
+ peer->dispname = NULL;
+ Curl_safefree(peer->hostname);
+ Curl_safefree(peer->scache_key);
peer->type = CURL_SSL_PEER_DNS;
}
@@ -1577,7 +1184,7 @@ static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
if(connssl) {
- Curl_ssl->close(cf, data);
+ connssl->ssl_impl->close(cf, data);
connssl->state = ssl_connection_none;
Curl_ssl_peer_cleanup(&connssl->peer);
}
@@ -1592,10 +1199,10 @@ static ssl_peer_type get_peer_type(const char *hostname)
#else
struct in_addr addr;
#endif
- if(Curl_inet_pton(AF_INET, hostname, &addr))
+ if(curlx_inet_pton(AF_INET, hostname, &addr))
return CURL_SSL_PEER_IPV4;
#ifdef USE_IPV6
- else if(Curl_inet_pton(AF_INET6, hostname, &addr)) {
+ else if(curlx_inet_pton(AF_INET6, hostname, &addr)) {
return CURL_SSL_PEER_IPV6;
}
#endif
@@ -1603,7 +1210,9 @@ static ssl_peer_type get_peer_type(const char *hostname)
return CURL_SSL_PEER_DNS;
}
-CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf,
+CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
+ struct Curl_cfilter *cf,
+ const char *tls_id,
int transport)
{
const char *ehostname, *edispname;
@@ -1665,7 +1274,8 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf,
peer->sni[len] = 0;
}
}
- result = CURLE_OK;
+
+ result = Curl_ssl_peer_key_make(cf, peer, tls_id, &peer->scache_key);
out:
if(result)
@@ -1698,13 +1308,13 @@ static void ssl_cf_close(struct Curl_cfilter *cf,
static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
CURLcode result;
- if(cf->connected) {
+ if(cf->connected && (connssl->state != ssl_connection_deferred)) {
*done = TRUE;
return CURLE_OK;
}
@@ -1715,38 +1325,41 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
}
if(!cf->next->connected) {
- result = cf->next->cft->do_connect(cf->next, data, blocking, done);
+ result = cf->next->cft->do_connect(cf->next, data, done);
if(result || !*done)
return result;
}
CF_DATA_SAVE(save, cf, data);
CURL_TRC_CF(data, cf, "cf_connect()");
- DEBUGASSERT(data->conn);
- DEBUGASSERT(data->conn == cf->conn);
DEBUGASSERT(connssl);
*done = FALSE;
if(!connssl->peer.hostname) {
- result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP);
+ char tls_id[80];
+ connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1);
+ result = Curl_ssl_peer_init(&connssl->peer, cf, tls_id, TRNSPRT_TCP);
if(result)
goto out;
}
- if(blocking) {
- result = ssl_connect(cf, data);
- *done = (result == CURLE_OK);
- }
- else {
- result = ssl_connect_nonblocking(cf, data, done);
+ if(!connssl->prefs_checked) {
+ if(!ssl_prefs_check(data))
+ return CURLE_SSL_CONNECT_ERROR;
+ connssl->prefs_checked = TRUE;
}
+ result = connssl->ssl_impl->do_connect(cf, data, done);
+
if(!result && *done) {
cf->connected = TRUE;
- connssl->handshake_done = Curl_now();
+ if(connssl->state == ssl_connection_complete)
+ connssl->handshake_done = curlx_now();
/* Connection can be deferred when sending early data */
DEBUGASSERT(connssl->state == ssl_connection_complete ||
connssl->state == ssl_connection_deferred);
+ DEBUGASSERT(connssl->state != ssl_connection_deferred ||
+ connssl->earlydata_state > ssl_earlydata_none);
}
out:
CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", result, *done);
@@ -1754,14 +1367,87 @@ out:
return result;
}
+static CURLcode ssl_cf_set_earlydata(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *buf, size_t blen)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ ssize_t nwritten = 0;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_await);
+ DEBUGASSERT(Curl_bufq_is_empty(&connssl->earlydata));
+ if(blen) {
+ if(blen > connssl->earlydata_max)
+ blen = connssl->earlydata_max;
+ nwritten = Curl_bufq_write(&connssl->earlydata, buf, blen, &result);
+ CURL_TRC_CF(data, cf, "ssl_cf_set_earlydata(len=%zu) -> %zd",
+ blen, nwritten);
+ if(nwritten < 0)
+ return result;
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssl_cf_connect_deferred(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *buf, size_t blen,
+ bool *done)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(connssl->state == ssl_connection_deferred);
+ *done = FALSE;
+ if(connssl->earlydata_state == ssl_earlydata_await) {
+ result = ssl_cf_set_earlydata(cf, data, buf, blen);
+ if(result)
+ return result;
+ /* we buffered any early data we'd like to send. Actually
+ * do the connect now which sends it and performs the handshake. */
+ connssl->earlydata_state = ssl_earlydata_sending;
+ connssl->earlydata_skip = Curl_bufq_len(&connssl->earlydata);
+ }
+
+ result = ssl_cf_connect(cf, data, done);
+
+ if(!result && *done) {
+ Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done);
+ switch(connssl->earlydata_state) {
+ case ssl_earlydata_none:
+ break;
+ case ssl_earlydata_accepted:
+ if(!Curl_ssl_cf_is_proxy(cf))
+ Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip);
+ infof(data, "Server accepted %zu bytes of TLS early data.",
+ connssl->earlydata_skip);
+ break;
+ case ssl_earlydata_rejected:
+ if(!Curl_ssl_cf_is_proxy(cf))
+ Curl_pgrsEarlyData(data, -(curl_off_t)connssl->earlydata_skip);
+ infof(data, "Server rejected TLS early data.");
+ connssl->earlydata_skip = 0;
+ break;
+ default:
+ /* This should not happen. Either we do not use early data or we
+ * should know if it was accepted or not. */
+ DEBUGASSERT(NULL);
+ break;
+ }
+ }
+ return result;
+}
+
static bool ssl_cf_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
+ struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
bool result;
CF_DATA_SAVE(save, cf, data);
- if(Curl_ssl->data_pending(cf, data))
+ if(connssl->ssl_impl->data_pending &&
+ connssl->ssl_impl->data_pending(cf, data))
result = TRUE;
else
result = cf->next->cft->has_data_pending(cf->next, data);
@@ -1770,20 +1456,57 @@ static bool ssl_cf_data_pending(struct Curl_cfilter *cf,
}
static ssize_t ssl_cf_send(struct Curl_cfilter *cf,
- struct Curl_easy *data, const void *buf, size_t len,
+ struct Curl_easy *data,
+ const void *buf, size_t blen,
bool eos, CURLcode *err)
{
+ struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
- ssize_t nwritten = 0;
+ ssize_t nwritten = 0, early_written = 0;
(void)eos;
- /* OpenSSL and maybe other TLS libs do not like 0-length writes. Skip. */
*err = CURLE_OK;
- if(len > 0) {
- CF_DATA_SAVE(save, cf, data);
- nwritten = Curl_ssl->send_plain(cf, data, buf, len, err);
- CF_DATA_RESTORE(cf, save);
+ CF_DATA_SAVE(save, cf, data);
+
+ if(connssl->state == ssl_connection_deferred) {
+ bool done = FALSE;
+ *err = ssl_cf_connect_deferred(cf, data, buf, blen, &done);
+ if(*err) {
+ nwritten = -1;
+ goto out;
+ }
+ else if(!done) {
+ *err = CURLE_AGAIN;
+ nwritten = -1;
+ goto out;
+ }
+ DEBUGASSERT(connssl->state == ssl_connection_complete);
+ }
+
+ if(connssl->earlydata_skip) {
+ if(connssl->earlydata_skip >= blen) {
+ connssl->earlydata_skip -= blen;
+ *err = CURLE_OK;
+ nwritten = (ssize_t)blen;
+ goto out;
+ }
+ else {
+ early_written = connssl->earlydata_skip;
+ buf = ((const char *)buf) + connssl->earlydata_skip;
+ blen -= connssl->earlydata_skip;
+ connssl->earlydata_skip = 0;
+ }
}
+
+ /* OpenSSL and maybe other TLS libs do not like 0-length writes. Skip. */
+ if(blen > 0)
+ nwritten = connssl->ssl_impl->send_plain(cf, data, buf, blen, err);
+
+ if(nwritten >= 0)
+ nwritten += early_written;
+
+out:
+ CF_DATA_RESTORE(cf, save);
return nwritten;
}
@@ -1791,12 +1514,28 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
struct Curl_easy *data, char *buf, size_t len,
CURLcode *err)
{
+ struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
ssize_t nread;
CF_DATA_SAVE(save, cf, data);
*err = CURLE_OK;
- nread = Curl_ssl->recv_plain(cf, data, buf, len, err);
+ if(connssl->state == ssl_connection_deferred) {
+ bool done = FALSE;
+ *err = ssl_cf_connect_deferred(cf, data, NULL, 0, &done);
+ if(*err) {
+ nread = -1;
+ goto out;
+ }
+ else if(!done) {
+ *err = CURLE_AGAIN;
+ nread = -1;
+ goto out;
+ }
+ DEBUGASSERT(connssl->state == ssl_connection_complete);
+ }
+
+ nread = connssl->ssl_impl->recv_plain(cf, data, buf, len, err);
if(nread > 0) {
DEBUGASSERT((size_t)nread <= len);
}
@@ -1804,6 +1543,8 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
/* eof */
*err = CURLE_OK;
}
+
+out:
CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len,
nread, *err);
CF_DATA_RESTORE(cf, save);
@@ -1814,14 +1555,17 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
+ struct ssl_connect_data *connssl = cf->ctx;
CURLcode result = CURLE_OK;
*done = TRUE;
- if(!cf->shutdown) {
+ /* If we have done the SSL handshake, shut down the connection cleanly */
+ if(cf->connected && (connssl->state == ssl_connection_complete) &&
+ !cf->shutdown && Curl_ssl->shut_down) {
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
- result = Curl_ssl->shut_down(cf, data, TRUE, done);
+ result = connssl->ssl_impl->shut_down(cf, data, TRUE, done);
CURL_TRC_CF(data, cf, "cf_shutdown -> %d, done=%d", result, *done);
CF_DATA_RESTORE(cf, save);
cf->shutdown = (result || *done);
@@ -1833,42 +1577,14 @@ static void ssl_cf_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct easy_pollset *ps)
{
+ struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
- Curl_ssl->adjust_pollset(cf, data, ps);
+ connssl->ssl_impl->adjust_pollset(cf, data, ps);
CF_DATA_RESTORE(cf, save);
}
-static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- int event, int arg1, void *arg2)
-{
- struct cf_call_data save;
-
- (void)arg1;
- (void)arg2;
- switch(event) {
- case CF_CTRL_DATA_ATTACH:
- if(Curl_ssl->attach_data) {
- CF_DATA_SAVE(save, cf, data);
- Curl_ssl->attach_data(cf, data);
- CF_DATA_RESTORE(cf, save);
- }
- break;
- case CF_CTRL_DATA_DETACH:
- if(Curl_ssl->detach_data) {
- CF_DATA_SAVE(save, cf, data);
- Curl_ssl->detach_data(cf, data);
- CF_DATA_RESTORE(cf, save);
- }
- break;
- default:
- break;
- }
- return CURLE_OK;
-}
-
static CURLcode ssl_cf_query(struct Curl_cfilter *cf,
struct Curl_easy *data,
int query, int *pres1, void *pres2)
@@ -1893,28 +1609,9 @@ static CURLcode ssl_cf_query(struct Curl_cfilter *cf,
static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data,
bool *input_pending)
{
- struct cf_call_data save;
- int result;
/*
* This function tries to determine connection status.
- *
- * Return codes:
- * 1 means the connection is still in place
- * 0 means the connection has been closed
- * -1 means the connection status is unknown
*/
- CF_DATA_SAVE(save, cf, data);
- result = Curl_ssl->check_cxn(cf, data);
- CF_DATA_RESTORE(cf, save);
- if(result > 0) {
- *input_pending = TRUE;
- return TRUE;
- }
- if(result == 0) {
- *input_pending = FALSE;
- return FALSE;
- }
- /* ssl backend does not know */
return cf->next ?
cf->next->cft->is_alive(cf->next, data, input_pending) :
FALSE; /* pessimistic in absence of data */
@@ -1933,7 +1630,7 @@ struct Curl_cftype Curl_cft_ssl = {
ssl_cf_data_pending,
ssl_cf_send,
ssl_cf_recv,
- ssl_cf_cntrl,
+ Curl_cf_def_cntrl,
cf_ssl_is_alive,
Curl_cf_def_conn_keep_alive,
ssl_cf_query,
@@ -1954,7 +1651,7 @@ struct Curl_cftype Curl_cft_ssl_proxy = {
ssl_cf_data_pending,
ssl_cf_send,
ssl_cf_recv,
- ssl_cf_cntrl,
+ Curl_cf_def_cntrl,
cf_ssl_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_def_query,
@@ -1972,8 +1669,14 @@ static CURLcode cf_ssl_create(struct Curl_cfilter **pcf,
DEBUGASSERT(data->conn);
- ctx = cf_ctx_new(data, alpn_get_spec(data->state.httpwant,
+#ifdef CURL_DISABLE_HTTP
+ /* We only support ALPN for HTTP so far. */
+ DEBUGASSERT(!conn->bits.tls_enable_alpn);
+ ctx = cf_ctx_new(data, NULL);
+#else
+ ctx = cf_ctx_new(data, alpn_get_spec(data->state.http_neg.wanted,
conn->bits.tls_enable_alpn));
+#endif
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
@@ -2023,16 +1726,16 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf,
struct ssl_connect_data *ctx;
CURLcode result;
bool use_alpn = conn->bits.tls_enable_alpn;
- int httpwant = CURL_HTTP_VERSION_1_1;
+ http_majors allowed = CURL_HTTP_V1x;
#ifdef USE_HTTP2
if(conn->http_proxy.proxytype == CURLPROXY_HTTPS2) {
use_alpn = TRUE;
- httpwant = CURL_HTTP_VERSION_2;
+ allowed = (CURL_HTTP_V1x|CURL_HTTP_V2x);
}
#endif
- ctx = cf_ctx_new(data, alpn_get_spec(httpwant, use_alpn));
+ ctx = cf_ctx_new(data, alpn_get_spec(allowed, use_alpn));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
@@ -2090,9 +1793,10 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
/* get first SSL filter in chain, if any is present */
cf = get_ssl_filter(data->conn->cfilter[sockindex]);
if(cf) {
+ struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
- result = Curl_ssl->get_internals(cf->ctx, info);
+ result = connssl->ssl_impl->get_internals(cf->ctx, info);
CF_DATA_RESTORE(cf, save);
}
}
@@ -2125,7 +1829,7 @@ static CURLcode vtls_shutdown_blocking(struct Curl_cfilter *cf,
return CURLE_OPERATION_TIMEDOUT;
}
- result = Curl_ssl->shut_down(cf, data, send_shutdown, done);
+ result = connssl->ssl_impl->shut_down(cf, data, send_shutdown, done);
if(result ||*done)
goto out;
@@ -2163,7 +1867,7 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
if(cf->cft == &Curl_cft_ssl) {
bool done;
CURL_TRC_CF(data, cf, "shutdown and remove SSL, start");
- Curl_shutdown_start(data, sockindex, NULL);
+ Curl_shutdown_start(data, sockindex, 0, NULL);
result = vtls_shutdown_blocking(cf, data, send_shutdown, &done);
Curl_shutdown_clear(data, sockindex);
if(!result && !done) /* blocking failed? */
@@ -2261,6 +1965,24 @@ bool Curl_alpn_contains_proto(const struct alpn_spec *spec,
return FALSE;
}
+void Curl_alpn_restrict_to(struct alpn_spec *spec, const char *proto)
+{
+ size_t plen = strlen(proto);
+ DEBUGASSERT(plen < sizeof(spec->entries[0]));
+ if(plen < sizeof(spec->entries[0])) {
+ memcpy(spec->entries[0], proto, plen + 1);
+ spec->count = 1;
+ }
+}
+
+void Curl_alpn_copy(struct alpn_spec *dest, const struct alpn_spec *src)
+{
+ if(src)
+ memcpy(dest, src, sizeof(*dest));
+ else
+ memset(dest, 0, sizeof(*dest));
+}
+
CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_connect_data *connssl,
@@ -2277,28 +1999,28 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
#endif
;
- if(connssl->alpn_negotiated) {
+ if(connssl->negotiated.alpn) {
/* When we ask for a specific ALPN protocol, we need the confirmation
* of it by the server, as we have installed protocol handler and
* connection filter chain for exactly this protocol. */
if(!proto_len) {
failf(data, "ALPN: asked for '%s' from previous session, "
"but server did not confirm it. Refusing to continue.",
- connssl->alpn_negotiated);
+ connssl->negotiated.alpn);
result = CURLE_SSL_CONNECT_ERROR;
goto out;
}
- else if((strlen(connssl->alpn_negotiated) != proto_len) ||
- memcmp(connssl->alpn_negotiated, proto, proto_len)) {
+ else if((strlen(connssl->negotiated.alpn) != proto_len) ||
+ memcmp(connssl->negotiated.alpn, proto, proto_len)) {
failf(data, "ALPN: asked for '%s' from previous session, but server "
"selected '%.*s'. Refusing to continue.",
- connssl->alpn_negotiated, (int)proto_len, proto);
+ connssl->negotiated.alpn, (int)proto_len, proto);
result = CURLE_SSL_CONNECT_ERROR;
goto out;
}
/* ALPN is exactly what we asked for, done. */
infof(data, "ALPN: server confirmed to use '%s'",
- connssl->alpn_negotiated);
+ connssl->negotiated.alpn);
goto out;
}
@@ -2309,11 +2031,11 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
result = CURLE_SSL_CONNECT_ERROR;
goto out;
}
- connssl->alpn_negotiated = malloc(proto_len + 1);
- if(!connssl->alpn_negotiated)
+ connssl->negotiated.alpn = malloc(proto_len + 1);
+ if(!connssl->negotiated.alpn)
return CURLE_OUT_OF_MEMORY;
- memcpy(connssl->alpn_negotiated, proto, proto_len);
- connssl->alpn_negotiated[proto_len] = 0;
+ memcpy(connssl->negotiated.alpn, proto, proto_len);
+ connssl->negotiated.alpn[proto_len] = 0;
}
if(proto && proto_len) {
@@ -2336,8 +2058,8 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
else {
*palpn = CURL_HTTP_VERSION_NONE;
failf(data, "unsupported ALPN protocol: '%.*s'", (int)proto_len, proto);
- /* TODO: do we want to fail this? Previous code just ignored it and
- * some vtls backends even ignore the return code of this function. */
+ /* Previous code just ignored it and some vtls backends even ignore the
+ * return code of this function. */
/* return CURLE_NOT_BUILT_IN; */
goto out;
}
diff --git a/libs/libcurl/src/vtls/vtls.h b/libs/libcurl/src/vtls/vtls.h
index 171a5d8e55..0617758f92 100644
--- a/libs/libcurl/src/vtls/vtls.h
+++ b/libs/libcurl/src/vtls/vtls.h
@@ -23,12 +23,14 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
struct connectdata;
struct ssl_config_data;
struct ssl_primary_config;
-struct Curl_ssl_session;
+struct Curl_cfilter;
+struct Curl_easy;
+struct dynbuf;
#define SSLSUPP_CA_PATH (1<<0) /* supports CAPATH */
#define SSLSUPP_CERTINFO (1<<1) /* supports CURLOPT_CERTINFO */
@@ -40,6 +42,15 @@ struct Curl_ssl_session;
#define SSLSUPP_ECH (1<<7)
#define SSLSUPP_CA_CACHE (1<<8)
#define SSLSUPP_CIPHER_LIST (1<<9) /* supports TLS 1.0-1.2 ciphersuites */
+#define SSLSUPP_SIGNATURE_ALGORITHMS (1<<10) /* supports TLS sigalgs */
+
+#ifdef USE_ECH
+# include "../curlx/base64.h"
+# define ECH_ENABLED(__data__) \
+ (__data__->set.tls_ech && \
+ !(__data__->set.tls_ech & CURLECH_DISABLE)\
+ )
+#endif /* USE_ECH */
#define ALPN_ACCEPTED "ALPN: server accepted "
@@ -55,9 +66,31 @@ struct Curl_ssl_session;
#define VTLS_INFOF_ALPN_DEFERRED \
"ALPN: deferred handshake for early data using '%.*s'."
-/* Curl_multi SSL backend-specific data; declared differently by each SSL
- backend */
-struct Curl_cfilter;
+/* IETF defined version numbers used in TLS protocol negotiation */
+#define CURL_IETF_PROTO_UNKNOWN 0x0
+#define CURL_IETF_PROTO_SSL3 0x0300
+#define CURL_IETF_PROTO_TLS1 0x0301
+#define CURL_IETF_PROTO_TLS1_1 0x0302
+#define CURL_IETF_PROTO_TLS1_2 0x0303
+#define CURL_IETF_PROTO_TLS1_3 0x0304
+#define CURL_IETF_PROTO_DTLS1 0xFEFF
+#define CURL_IETF_PROTO_DTLS1_2 0xFEFD
+
+typedef enum {
+ CURL_SSL_PEER_DNS,
+ CURL_SSL_PEER_IPV4,
+ CURL_SSL_PEER_IPV6
+} ssl_peer_type;
+
+struct ssl_peer {
+ char *hostname; /* hostname for verification */
+ char *dispname; /* display version of hostname */
+ char *sni; /* SNI version of hostname or NULL if not usable */
+ char *scache_key; /* for lookups in session cache */
+ ssl_peer_type type; /* type of the peer information */
+ int port; /* port we are talking to */
+ int transport; /* one of TRNSPRT_* defines */
+};
CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
const curl_ssl_backend ***avail);
@@ -113,7 +146,9 @@ void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy);
* Init SSL peer information for filter. Can be called repeatedly.
*/
CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
- struct Curl_cfilter *cf, int transport);
+ struct Curl_cfilter *cf,
+ const char *tls_id,
+ int transport);
/**
* Free all allocated data and reset peer information.
*/
@@ -130,8 +165,6 @@ CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine);
CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data);
struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data);
-/* init the SSL session ID cache */
-CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t);
void Curl_ssl_version(char *buffer, size_t size);
/* Certificate information list handling. */
@@ -147,33 +180,6 @@ CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data, int certnum,
/* Functions to be used by SSL library adaptation functions */
-/* Lock session cache mutex.
- * Call this before calling other Curl_ssl_*session* functions
- * Caller should unlock this mutex as soon as possible, as it may block
- * other SSL connection from making progress.
- * The purpose of explicitly locking SSL session cache data is to allow
- * individual SSL engines to manage session lifetime in their specific way.
- */
-void Curl_ssl_sessionid_lock(struct Curl_easy *data);
-
-/* Unlock session cache mutex */
-void Curl_ssl_sessionid_unlock(struct Curl_easy *data);
-
-/* Kill a single session ID entry in the cache
- * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
- * This will call engine-specific curlssl_session_free function, which must
- * take sessionid object ownership from sessionid cache
- * (e.g. decrement refcount).
- */
-void Curl_ssl_kill_session(struct Curl_ssl_session *session);
-/* delete a session from the cache
- * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
- * This will call engine-specific curlssl_session_free function, which must
- * take sessionid object ownership from sessionid cache
- * (e.g. decrement refcount).
- */
-void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid);
-
/* get N random bytes into the buffer */
CURLcode Curl_ssl_random(struct Curl_easy *data, unsigned char *buffer,
size_t length);
@@ -184,7 +190,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
bool Curl_ssl_cert_status_request(void);
-bool Curl_ssl_false_start(struct Curl_easy *data);
+bool Curl_ssl_false_start(void);
/* The maximum size of the SSL channel binding is 85 bytes, as defined in
* RFC 5929, Section 4.1. The 'tls-server-end-point:' prefix is 21 bytes long,
@@ -265,12 +271,10 @@ extern struct Curl_cftype Curl_cft_ssl_proxy;
#define Curl_ssl_set_engine(x,y) CURLE_NOT_BUILT_IN
#define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN
#define Curl_ssl_engines_list(x) NULL
-#define Curl_ssl_initsessions(x,y) CURLE_OK
#define Curl_ssl_free_certinfo(x) Curl_nop_stmt
-#define Curl_ssl_kill_session(x) Curl_nop_stmt
#define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN)
#define Curl_ssl_cert_status_request() FALSE
-#define Curl_ssl_false_start(a) FALSE
+#define Curl_ssl_false_start() FALSE
#define Curl_ssl_get_internals(a,b,c,d) NULL
#define Curl_ssl_supports(a,b) FALSE
#define Curl_ssl_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN
diff --git a/libs/libcurl/src/vtls/vtls_int.h b/libs/libcurl/src/vtls/vtls_int.h
index 250273ef9d..e4a9e35325 100644
--- a/libs/libcurl/src/vtls/vtls_int.h
+++ b/libs/libcurl/src/vtls/vtls_int.h
@@ -23,12 +23,14 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
-#include "cfilters.h"
-#include "urldata.h"
+#include "../curl_setup.h"
+#include "../cfilters.h"
+#include "../urldata.h"
+#include "vtls.h"
#ifdef USE_SSL
+struct Curl_ssl;
struct ssl_connect_data;
/* see https://www.iana.org/assignments/tls-extensiontype-values/ */
@@ -47,7 +49,7 @@ struct ssl_connect_data;
#define ALPN_PROTO_BUF_MAX (ALPN_ENTRIES_MAX * (ALPN_NAME_MAX + 1))
struct alpn_spec {
- const char entries[ALPN_ENTRIES_MAX][ALPN_NAME_MAX];
+ char entries[ALPN_ENTRIES_MAX][ALPN_NAME_MAX];
size_t count; /* number of entries */
};
@@ -60,6 +62,8 @@ CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf,
const struct alpn_spec *spec);
CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf,
const struct alpn_spec *spec);
+void Curl_alpn_restrict_to(struct alpn_spec *spec, const char *proto);
+void Curl_alpn_copy(struct alpn_spec *dest, const struct alpn_spec *src);
CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
struct Curl_easy *data,
@@ -87,7 +91,7 @@ typedef enum {
typedef enum {
ssl_earlydata_none,
- ssl_earlydata_use,
+ ssl_earlydata_await,
ssl_earlydata_sending,
ssl_earlydata_sent,
ssl_earlydata_accepted,
@@ -103,12 +107,15 @@ typedef enum {
/* Information in each SSL cfilter context: cf->ctx */
struct ssl_connect_data {
- struct ssl_peer peer;
+ const struct Curl_ssl *ssl_impl; /* TLS backend for this filter */
+ struct ssl_peer peer; /* peer the filter talks to */
const struct alpn_spec *alpn; /* ALPN to use or NULL for none */
void *backend; /* vtls backend specific props */
struct cf_call_data call_data; /* data handle used in current call */
struct curltime handshake_done; /* time when handshake finished */
- char *alpn_negotiated; /* negotiated ALPN value or NULL */
+ struct {
+ char *alpn; /* ALPN value or NULL */
+ } negotiated;
struct bufq earlydata; /* earlydata to be send to peer */
size_t earlydata_max; /* max earlydata allowed by peer */
size_t earlydata_skip; /* sending bytes to skip when earlydata
@@ -119,6 +126,7 @@ struct ssl_connect_data {
int io_need; /* TLS signals special SEND/RECV needs */
BIT(use_alpn); /* if ALPN shall be used in handshake */
BIT(peer_closed); /* peer has closed connection */
+ BIT(prefs_checked); /* SSL preferences have been checked */
};
@@ -142,7 +150,6 @@ struct Curl_ssl {
void (*cleanup)(void);
size_t (*version)(char *buffer, size_t size);
- int (*check_cxn)(struct Curl_cfilter *cf, struct Curl_easy *data);
CURLcode (*shut_down)(struct Curl_cfilter *cf, struct Curl_easy *data,
bool send_shutdown, bool *done);
bool (*data_pending)(struct Curl_cfilter *cf,
@@ -153,11 +160,8 @@ struct Curl_ssl {
size_t length);
bool (*cert_status_request)(void);
- CURLcode (*connect_blocking)(struct Curl_cfilter *cf,
- struct Curl_easy *data);
- CURLcode (*connect_nonblocking)(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done);
+ CURLcode (*do_connect)(struct Curl_cfilter *cf, struct Curl_easy *data,
+ bool *done);
/* During handshake/shutdown, adjust the pollset to include the socket
* for POLLOUT or POLLIN as needed. Mandatory. */
@@ -174,10 +178,6 @@ struct Curl_ssl {
bool (*false_start)(void);
CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen,
unsigned char *sha256sum, size_t sha256sumlen);
-
- bool (*attach_data)(struct Curl_cfilter *cf, struct Curl_easy *data);
- void (*detach_data)(struct Curl_cfilter *cf, struct Curl_easy *data);
-
ssize_t (*recv_plain)(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *code);
ssize_t (*send_plain)(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -190,66 +190,14 @@ struct Curl_ssl {
extern const struct Curl_ssl *Curl_ssl;
-
-int Curl_none_init(void);
-void Curl_none_cleanup(void);
-CURLcode Curl_none_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data,
- bool send_shutdown, bool *done);
-int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data);
-void Curl_none_close_all(struct Curl_easy *data);
-void Curl_none_session_free(void *ptr);
-bool Curl_none_data_pending(struct Curl_cfilter *cf,
- const struct Curl_easy *data);
-bool Curl_none_cert_status_request(void);
-CURLcode Curl_none_set_engine(struct Curl_easy *data, const char *engine);
-CURLcode Curl_none_set_engine_default(struct Curl_easy *data);
-struct curl_slist *Curl_none_engines_list(struct Curl_easy *data);
-bool Curl_none_false_start(void);
void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
- struct easy_pollset *ps);
+ struct easy_pollset *ps);
/**
* Get the SSL filter below the given one or NULL if there is none.
*/
bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf);
-/* extract a session ID
- * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
- * Caller must make sure that the ownership of returned sessionid object
- * is properly taken (e.g. its refcount is incremented
- * under sessionid mutex).
- * @param cf the connection filter wanting to use it
- * @param data the transfer involved
- * @param peer the peer the filter wants to talk to
- * @param sessionid on return the TLS session
- * @param idsize on return the size of the TLS session data
- * @param palpn on return the ALPN string used by the session,
- * set to NULL when not interested
- */
-bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct ssl_peer *peer,
- void **ssl_sessionid,
- size_t *idsize, /* set 0 if unknown */
- char **palpn);
-
-/* Set a TLS session ID for `peer`. Replaces an existing session ID if
- * not already the same.
- * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
- * Call takes ownership of `ssl_sessionid`, using `sessionid_free_cb`
- * to deallocate it. Is called in all outcomes, either right away or
- * later when the session cache is cleaned up.
- * Caller must ensure that it has properly shared ownership of this sessionid
- * object with cache (e.g. incrementing refcount on success)
- */
-CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct ssl_peer *peer,
- const char *alpn,
- void *sessionid,
- size_t sessionid_size,
- Curl_ssl_sessionid_dtor *sessionid_free_cb);
-
#endif /* USE_SSL */
#endif /* HEADER_CURL_VTLS_INT_H */
diff --git a/libs/libcurl/src/vtls/vtls_scache.c b/libs/libcurl/src/vtls/vtls_scache.c
new file mode 100644
index 0000000000..3e1825d635
--- /dev/null
+++ b/libs/libcurl/src/vtls/vtls_scache.c
@@ -0,0 +1,1235 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 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.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "../curl_setup.h"
+
+#ifdef USE_SSL
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "../urldata.h"
+#include "../cfilters.h"
+
+#include "vtls.h" /* generic SSL protos etc */
+#include "vtls_int.h"
+#include "vtls_scache.h"
+#include "vtls_spack.h"
+
+#include "../strcase.h"
+#include "../url.h"
+#include "../llist.h"
+#include "../share.h"
+#include "../curl_trc.h"
+#include "../curl_sha256.h"
+#include "../rand.h"
+#include "../curlx/warnless.h"
+#include "../curl_printf.h"
+#include "../strdup.h"
+
+/* The last #include files should be: */
+#include "../curl_memory.h"
+#include "../memdebug.h"
+
+
+static bool cf_ssl_peer_key_is_global(const char *peer_key);
+
+/* a peer+tls-config we cache sessions for */
+struct Curl_ssl_scache_peer {
+ char *ssl_peer_key; /* id for peer + relevant TLS configuration */
+ char *clientcert;
+ char *srp_username;
+ char *srp_password;
+ struct Curl_llist sessions;
+ void *sobj; /* object instance or NULL */
+ Curl_ssl_scache_obj_dtor *sobj_free; /* free `sobj` callback */
+ unsigned char key_salt[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
+ unsigned char key_hmac[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
+ size_t max_sessions;
+ long age; /* just a number, the higher the more recent */
+ BIT(hmac_set); /* if key_salt and key_hmac are present */
+ BIT(exportable); /* sessions for this peer can be exported */
+};
+
+#define CURL_SCACHE_MAGIC 0x000e1551
+
+#define GOOD_SCACHE(x) ((x) && (x)->magic == CURL_SCACHE_MAGIC)
+
+struct Curl_ssl_scache {
+ unsigned int magic;
+ struct Curl_ssl_scache_peer *peers;
+ size_t peer_count;
+ int default_lifetime_secs;
+ long age;
+};
+
+static struct Curl_ssl_scache *cf_ssl_scache_get(struct Curl_easy *data)
+{
+ struct Curl_ssl_scache *scache = NULL;
+ /* If a share is present, its ssl_scache has preference over the multi */
+ if(data->share && data->share->ssl_scache)
+ scache = data->share->ssl_scache;
+ else if(data->multi && data->multi->ssl_scache)
+ scache = data->multi->ssl_scache;
+ if(scache && !GOOD_SCACHE(scache)) {
+ failf(data, "transfer would use an invalid scache at %p, denied",
+ (void *)scache);
+ DEBUGASSERT(0);
+ return NULL;
+ }
+ return scache;
+}
+
+static void cf_ssl_scache_session_ldestroy(void *udata, void *obj)
+{
+ struct Curl_ssl_session *s = obj;
+ (void)udata;
+ free(CURL_UNCONST(s->sdata));
+ free(CURL_UNCONST(s->quic_tp));
+ free((void *)s->alpn);
+ free(s);
+}
+
+CURLcode
+Curl_ssl_session_create(void *sdata, size_t sdata_len,
+ int ietf_tls_id, const char *alpn,
+ curl_off_t valid_until, size_t earlydata_max,
+ struct Curl_ssl_session **psession)
+{
+ return Curl_ssl_session_create2(sdata, sdata_len, ietf_tls_id, alpn,
+ valid_until, earlydata_max,
+ NULL, 0, psession);
+}
+
+CURLcode
+Curl_ssl_session_create2(void *sdata, size_t sdata_len,
+ int ietf_tls_id, const char *alpn,
+ curl_off_t valid_until, size_t earlydata_max,
+ unsigned char *quic_tp, size_t quic_tp_len,
+ struct Curl_ssl_session **psession)
+{
+ struct Curl_ssl_session *s;
+
+ if(!sdata || !sdata_len) {
+ free(sdata);
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ *psession = NULL;
+ s = calloc(1, sizeof(*s));
+ if(!s) {
+ free(sdata);
+ free(quic_tp);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ s->ietf_tls_id = ietf_tls_id;
+ s->valid_until = valid_until;
+ s->earlydata_max = earlydata_max;
+ s->sdata = sdata;
+ s->sdata_len = sdata_len;
+ s->quic_tp = quic_tp;
+ s->quic_tp_len = quic_tp_len;
+ if(alpn) {
+ s->alpn = strdup(alpn);
+ if(!s->alpn) {
+ cf_ssl_scache_session_ldestroy(NULL, s);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ *psession = s;
+ return CURLE_OK;
+}
+
+void Curl_ssl_session_destroy(struct Curl_ssl_session *s)
+{
+ if(s) {
+ /* if in the list, the list destructor takes care of it */
+ if(Curl_node_llist(&s->list))
+ Curl_node_remove(&s->list);
+ else {
+ cf_ssl_scache_session_ldestroy(NULL, s);
+ }
+ }
+}
+
+static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer)
+{
+ Curl_llist_destroy(&peer->sessions, NULL);
+ if(peer->sobj) {
+ DEBUGASSERT(peer->sobj_free);
+ if(peer->sobj_free)
+ peer->sobj_free(peer->sobj);
+ peer->sobj = NULL;
+ }
+ peer->sobj_free = NULL;
+ Curl_safefree(peer->clientcert);
+#ifdef USE_TLS_SRP
+ Curl_safefree(peer->srp_username);
+ Curl_safefree(peer->srp_password);
+#endif
+ Curl_safefree(peer->ssl_peer_key);
+ peer->age = 0;
+ peer->hmac_set = FALSE;
+}
+
+static void cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer *peer,
+ void *sobj,
+ Curl_ssl_scache_obj_dtor *sobj_free)
+{
+ DEBUGASSERT(peer);
+ if(peer->sobj_free) {
+ peer->sobj_free(peer->sobj);
+ }
+ peer->sobj = sobj;
+ peer->sobj_free = sobj_free;
+}
+
+static void cf_ssl_cache_peer_update(struct Curl_ssl_scache_peer *peer)
+{
+ /* The sessions of this peer are exportable if
+ * - it has no confidential information
+ * - its peer key is not yet known, because sessions were
+ * imported using only the salt+hmac
+ * - the peer key is global, e.g. carrying no relative paths */
+ peer->exportable = (!peer->clientcert && !peer->srp_username &&
+ !peer->srp_password &&
+ (!peer->ssl_peer_key ||
+ cf_ssl_peer_key_is_global(peer->ssl_peer_key)));
+}
+
+static CURLcode
+cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer,
+ const char *ssl_peer_key,
+ const char *clientcert,
+ const char *srp_username,
+ const char *srp_password,
+ const unsigned char *salt,
+ const unsigned char *hmac)
+{
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+
+ DEBUGASSERT(!peer->ssl_peer_key);
+ if(ssl_peer_key) {
+ peer->ssl_peer_key = strdup(ssl_peer_key);
+ if(!peer->ssl_peer_key)
+ goto out;
+ peer->hmac_set = FALSE;
+ }
+ else if(salt && hmac) {
+ memcpy(peer->key_salt, salt, sizeof(peer->key_salt));
+ memcpy(peer->key_hmac, hmac, sizeof(peer->key_hmac));
+ peer->hmac_set = TRUE;
+ }
+ else {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ if(clientcert) {
+ peer->clientcert = strdup(clientcert);
+ if(!peer->clientcert)
+ goto out;
+ }
+ if(srp_username) {
+ peer->srp_username = strdup(srp_username);
+ if(!peer->srp_username)
+ goto out;
+ }
+ if(srp_password) {
+ peer->srp_password = strdup(srp_password);
+ if(!peer->srp_password)
+ goto out;
+ }
+
+ cf_ssl_cache_peer_update(peer);
+ result = CURLE_OK;
+out:
+ if(result)
+ cf_ssl_scache_clear_peer(peer);
+ return result;
+}
+
+static void cf_scache_session_remove(struct Curl_ssl_scache_peer *peer,
+ struct Curl_ssl_session *s)
+{
+ (void)peer;
+ DEBUGASSERT(Curl_node_llist(&s->list) == &peer->sessions);
+ Curl_ssl_session_destroy(s);
+}
+
+static bool cf_scache_session_expired(struct Curl_ssl_session *s,
+ curl_off_t now)
+{
+ return (s->valid_until > 0) && (s->valid_until < now);
+}
+
+static void cf_scache_peer_remove_expired(struct Curl_ssl_scache_peer *peer,
+ curl_off_t now)
+{
+ struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
+ while(n) {
+ struct Curl_ssl_session *s = Curl_node_elem(n);
+ n = Curl_node_next(n);
+ if(cf_scache_session_expired(s, now))
+ cf_scache_session_remove(peer, s);
+ }
+}
+
+static void cf_scache_peer_remove_non13(struct Curl_ssl_scache_peer *peer)
+{
+ struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
+ while(n) {
+ struct Curl_ssl_session *s = Curl_node_elem(n);
+ n = Curl_node_next(n);
+ if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3)
+ cf_scache_session_remove(peer, s);
+ }
+}
+
+CURLcode Curl_ssl_scache_create(size_t max_peers,
+ size_t max_sessions_per_peer,
+ struct Curl_ssl_scache **pscache)
+{
+ struct Curl_ssl_scache *scache;
+ struct Curl_ssl_scache_peer *peers;
+ size_t i;
+
+ *pscache = NULL;
+ peers = calloc(max_peers, sizeof(*peers));
+ if(!peers)
+ return CURLE_OUT_OF_MEMORY;
+
+ scache = calloc(1, sizeof(*scache));
+ if(!scache) {
+ free(peers);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ scache->magic = CURL_SCACHE_MAGIC;
+ scache->default_lifetime_secs = (24*60*60); /* 1 day */
+ scache->peer_count = max_peers;
+ scache->peers = peers;
+ scache->age = 1;
+ for(i = 0; i < scache->peer_count; ++i) {
+ scache->peers[i].max_sessions = max_sessions_per_peer;
+ Curl_llist_init(&scache->peers[i].sessions,
+ cf_ssl_scache_session_ldestroy);
+ }
+
+ *pscache = scache;
+ return CURLE_OK;
+}
+
+void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache)
+{
+ if(scache && GOOD_SCACHE(scache)) {
+ size_t i;
+ scache->magic = 0;
+ for(i = 0; i < scache->peer_count; ++i) {
+ cf_ssl_scache_clear_peer(&scache->peers[i]);
+ }
+ free(scache->peers);
+ free(scache);
+ }
+}
+
+/* Lock shared SSL session data */
+void Curl_ssl_scache_lock(struct Curl_easy *data)
+{
+ if(CURL_SHARE_ssl_scache(data))
+ Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
+}
+
+/* Unlock shared SSL session data */
+void Curl_ssl_scache_unlock(struct Curl_easy *data)
+{
+ if(CURL_SHARE_ssl_scache(data))
+ Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
+}
+
+static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf,
+ const char *name,
+ char *path,
+ bool *is_local)
+{
+ if(path && path[0]) {
+ /* We try to add absolute paths, so that the session key can stay
+ * valid when used in another process with different CWD. However,
+ * when a path does not exist, this does not work. Then, we add
+ * the path as is. */
+#ifdef UNDER_CE
+ (void)is_local;
+ return curlx_dyn_addf(buf, ":%s-%s", name, path);
+#elif defined(_WIN32)
+ char abspath[_MAX_PATH];
+ if(_fullpath(abspath, path, _MAX_PATH))
+ return curlx_dyn_addf(buf, ":%s-%s", name, abspath);
+ *is_local = TRUE;
+#elif defined(HAVE_REALPATH)
+ if(path[0] != '/') {
+ char *abspath = realpath(path, NULL);
+ if(abspath) {
+ CURLcode r = curlx_dyn_addf(buf, ":%s-%s", name, abspath);
+ (free)(abspath); /* allocated by libc, free without memdebug */
+ return r;
+ }
+ *is_local = TRUE;
+ }
+#endif
+ return curlx_dyn_addf(buf, ":%s-%s", name, path);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf,
+ const char *name,
+ struct curl_blob *blob)
+{
+ CURLcode r = CURLE_OK;
+ if(blob && blob->len) {
+ unsigned char hash[CURL_SHA256_DIGEST_LENGTH];
+ size_t i;
+
+ r = curlx_dyn_addf(buf, ":%s-", name);
+ if(r)
+ goto out;
+ r = Curl_sha256it(hash, blob->data, blob->len);
+ if(r)
+ goto out;
+ for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) {
+ r = curlx_dyn_addf(buf, "%02x", hash[i]);
+ if(r)
+ goto out;
+ }
+ }
+out:
+ return r;
+}
+
+#define CURL_SSLS_LOCAL_SUFFIX ":L"
+#define CURL_SSLS_GLOBAL_SUFFIX ":G"
+
+static bool cf_ssl_peer_key_is_global(const char *peer_key)
+{
+ size_t len = peer_key ? strlen(peer_key) : 0;
+ return (len > 2) &&
+ (peer_key[len - 1] == 'G') &&
+ (peer_key[len - 2] == ':');
+}
+
+CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
+ const struct ssl_peer *peer,
+ const char *tls_id,
+ char **ppeer_key)
+{
+ struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
+ struct dynbuf buf;
+ size_t key_len;
+ bool is_local = FALSE;
+ CURLcode r;
+
+ *ppeer_key = NULL;
+ curlx_dyn_init(&buf, 10 * 1024);
+
+ r = curlx_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port);
+ if(r)
+ goto out;
+
+ switch(peer->transport) {
+ case TRNSPRT_TCP:
+ break;
+ case TRNSPRT_UDP:
+ r = curlx_dyn_add(&buf, ":UDP");
+ break;
+ case TRNSPRT_QUIC:
+ r = curlx_dyn_add(&buf, ":QUIC");
+ break;
+ case TRNSPRT_UNIX:
+ r = curlx_dyn_add(&buf, ":UNIX");
+ break;
+ default:
+ r = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport);
+ break;
+ }
+ if(r)
+ goto out;
+
+ if(!ssl->verifypeer) {
+ r = curlx_dyn_add(&buf, ":NO-VRFY-PEER");
+ if(r)
+ goto out;
+ }
+ if(!ssl->verifyhost) {
+ r = curlx_dyn_add(&buf, ":NO-VRFY-HOST");
+ if(r)
+ goto out;
+ }
+ if(ssl->verifystatus) {
+ r = curlx_dyn_add(&buf, ":VRFY-STATUS");
+ if(r)
+ goto out;
+ }
+ if(!ssl->verifypeer || !ssl->verifyhost) {
+ if(cf->conn->bits.conn_to_host) {
+ r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);
+ if(r)
+ goto out;
+ }
+ if(cf->conn->bits.conn_to_port) {
+ r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port);
+ if(r)
+ goto out;
+ }
+ }
+
+ if(ssl->version || ssl->version_max) {
+ r = curlx_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version,
+ (ssl->version_max >> 16));
+ if(r)
+ goto out;
+ }
+ if(ssl->ssl_options) {
+ r = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options);
+ if(r)
+ goto out;
+ }
+ if(ssl->cipher_list) {
+ r = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list);
+ if(r)
+ goto out;
+ }
+ if(ssl->cipher_list13) {
+ r = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13);
+ if(r)
+ goto out;
+ }
+ if(ssl->curves) {
+ r = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves);
+ if(r)
+ goto out;
+ }
+ if(ssl->verifypeer) {
+ r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local);
+ if(r)
+ goto out;
+ r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local);
+ if(r)
+ goto out;
+ r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local);
+ if(r)
+ goto out;
+ r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local);
+ if(r)
+ goto out;
+ if(ssl->cert_blob) {
+ r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob);
+ if(r)
+ goto out;
+ }
+ if(ssl->ca_info_blob) {
+ r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob);
+ if(r)
+ goto out;
+ }
+ if(ssl->issuercert_blob) {
+ r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob);
+ if(r)
+ goto out;
+ }
+ }
+ if(ssl->pinned_key && ssl->pinned_key[0]) {
+ r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key);
+ if(r)
+ goto out;
+ }
+
+ if(ssl->clientcert && ssl->clientcert[0]) {
+ r = curlx_dyn_add(&buf, ":CCERT");
+ if(r)
+ goto out;
+ }
+#ifdef USE_TLS_SRP
+ if(ssl->username || ssl->password) {
+ r = curlx_dyn_add(&buf, ":SRP-AUTH");
+ if(r)
+ goto out;
+ }
+#endif
+
+ if(!tls_id || !tls_id[0]) {
+ r = CURLE_FAILED_INIT;
+ goto out;
+ }
+ r = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id);
+ if(r)
+ goto out;
+
+ r = curlx_dyn_addf(&buf, is_local ?
+ CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX);
+ if(r)
+ goto out;
+
+ *ppeer_key = curlx_dyn_take(&buf, &key_len);
+ /* we just added printable char, and dynbuf always null-terminates, no need
+ * to track length */
+
+out:
+ curlx_dyn_free(&buf);
+ return r;
+}
+
+static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer,
+ struct ssl_primary_config *conn_config)
+{
+ if(!conn_config) {
+ if(peer->clientcert)
+ return FALSE;
+#ifdef USE_TLS_SRP
+ if(peer->srp_username || peer->srp_password)
+ return FALSE;
+#endif
+ return TRUE;
+ }
+ else if(!Curl_safecmp(peer->clientcert, conn_config->clientcert))
+ return FALSE;
+#ifdef USE_TLS_SRP
+ if(Curl_timestrcmp(peer->srp_username, conn_config->username) ||
+ Curl_timestrcmp(peer->srp_password, conn_config->password))
+ return FALSE;
+#endif
+ return TRUE;
+}
+
+static CURLcode
+cf_ssl_find_peer_by_key(struct Curl_easy *data,
+ struct Curl_ssl_scache *scache,
+ const char *ssl_peer_key,
+ struct ssl_primary_config *conn_config,
+ struct Curl_ssl_scache_peer **ppeer)
+{
+ size_t i, peer_key_len = 0;
+ CURLcode result = CURLE_OK;
+
+ *ppeer = NULL;
+ if(!GOOD_SCACHE(scache)) {
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ CURL_TRC_SSLS(data, "find peer slot for %s among %zu slots",
+ ssl_peer_key, scache->peer_count);
+
+ /* check for entries with known peer_key */
+ for(i = 0; scache && i < scache->peer_count; i++) {
+ if(scache->peers[i].ssl_peer_key &&
+ strcasecompare(ssl_peer_key, scache->peers[i].ssl_peer_key) &&
+ cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
+ /* yes, we have a cached session for this! */
+ *ppeer = &scache->peers[i];
+ goto out;
+ }
+ }
+ /* check for entries with HMAC set but no known peer_key */
+ for(i = 0; scache && i < scache->peer_count; i++) {
+ if(!scache->peers[i].ssl_peer_key &&
+ scache->peers[i].hmac_set &&
+ cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
+ /* possible entry with unknown peer_key, check hmac */
+ unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH];
+ if(!peer_key_len) /* we are lazy */
+ peer_key_len = strlen(ssl_peer_key);
+ result = Curl_hmacit(&Curl_HMAC_SHA256,
+ scache->peers[i].key_salt,
+ sizeof(scache->peers[i].key_salt),
+ (const unsigned char *)ssl_peer_key,
+ peer_key_len,
+ my_hmac);
+ if(result)
+ goto out;
+ if(!memcmp(scache->peers[i].key_hmac, my_hmac, sizeof(my_hmac))) {
+ /* remember peer_key for future lookups */
+ CURL_TRC_SSLS(data, "peer entry %zu key recovered: %s",
+ i, ssl_peer_key);
+ scache->peers[i].ssl_peer_key = strdup(ssl_peer_key);
+ if(!scache->peers[i].ssl_peer_key) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ cf_ssl_cache_peer_update(&scache->peers[i]);
+ *ppeer = &scache->peers[i];
+ goto out;
+ }
+ }
+ }
+ CURL_TRC_SSLS(data, "peer not found for %s", ssl_peer_key);
+out:
+ return result;
+}
+
+static struct Curl_ssl_scache_peer *
+cf_ssl_get_free_peer(struct Curl_ssl_scache *scache)
+{
+ struct Curl_ssl_scache_peer *peer = NULL;
+ size_t i;
+
+ /* find empty or oldest peer */
+ for(i = 0; i < scache->peer_count; ++i) {
+ /* free peer entry? */
+ if(!scache->peers[i].ssl_peer_key && !scache->peers[i].hmac_set) {
+ peer = &scache->peers[i];
+ break;
+ }
+ /* peer without sessions and obj */
+ if(!scache->peers[i].sobj &&
+ !Curl_llist_count(&scache->peers[i].sessions)) {
+ peer = &scache->peers[i];
+ break;
+ }
+ /* remember "oldest" peer */
+ if(!peer || (scache->peers[i].age < peer->age)) {
+ peer = &scache->peers[i];
+ }
+ }
+ DEBUGASSERT(peer);
+ if(peer)
+ cf_ssl_scache_clear_peer(peer);
+ return peer;
+}
+
+static CURLcode
+cf_ssl_add_peer(struct Curl_easy *data,
+ struct Curl_ssl_scache *scache,
+ const char *ssl_peer_key,
+ struct ssl_primary_config *conn_config,
+ struct Curl_ssl_scache_peer **ppeer)
+{
+ struct Curl_ssl_scache_peer *peer = NULL;
+ CURLcode result = CURLE_OK;
+
+ *ppeer = NULL;
+ if(ssl_peer_key) {
+ result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
+ &peer);
+ if(result || !scache->peer_count)
+ return result;
+ }
+
+ if(peer) {
+ *ppeer = peer;
+ return CURLE_OK;
+ }
+
+ peer = cf_ssl_get_free_peer(scache);
+ if(peer) {
+ const char *ccert = conn_config ? conn_config->clientcert : NULL;
+ const char *username = NULL, *password = NULL;
+#ifdef USE_TLS_SRP
+ username = conn_config ? conn_config->username : NULL;
+ password = conn_config ? conn_config->password : NULL;
+#endif
+ result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert,
+ username, password, NULL, NULL);
+ if(result)
+ goto out;
+ /* all ready */
+ *ppeer = peer;
+ result = CURLE_OK;
+ }
+
+out:
+ if(result) {
+ cf_ssl_scache_clear_peer(peer);
+ }
+ return result;
+}
+
+static void cf_scache_peer_add_session(struct Curl_ssl_scache_peer *peer,
+ struct Curl_ssl_session *s,
+ curl_off_t now)
+{
+ /* A session not from TLSv1.3 replaces all other. */
+ if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3) {
+ Curl_llist_destroy(&peer->sessions, NULL);
+ Curl_llist_append(&peer->sessions, s, &s->list);
+ }
+ else {
+ /* Expire existing, append, trim from head to obey max_sessions */
+ cf_scache_peer_remove_expired(peer, now);
+ cf_scache_peer_remove_non13(peer);
+ Curl_llist_append(&peer->sessions, s, &s->list);
+ while(Curl_llist_count(&peer->sessions) > peer->max_sessions) {
+ Curl_node_remove(Curl_llist_head(&peer->sessions));
+ }
+ }
+}
+
+static CURLcode cf_scache_add_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct Curl_ssl_scache *scache,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session *s)
+{
+ struct Curl_ssl_scache_peer *peer = NULL;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+ curl_off_t now = (curl_off_t)time(NULL);
+ curl_off_t max_lifetime;
+
+ if(!scache || !scache->peer_count) {
+ Curl_ssl_session_destroy(s);
+ return CURLE_OK;
+ }
+
+ if(s->valid_until <= 0)
+ s->valid_until = now + scache->default_lifetime_secs;
+
+ max_lifetime = (s->ietf_tls_id == CURL_IETF_PROTO_TLS1_3) ?
+ CURL_SCACHE_MAX_13_LIFETIME_SEC :
+ CURL_SCACHE_MAX_12_LIFETIME_SEC;
+ if(s->valid_until > (now + max_lifetime))
+ s->valid_until = now + max_lifetime;
+
+ if(cf_scache_session_expired(s, now)) {
+ CURL_TRC_SSLS(data, "add, session already expired");
+ Curl_ssl_session_destroy(s);
+ return CURLE_OK;
+ }
+
+ result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer);
+ if(result || !peer) {
+ CURL_TRC_SSLS(data, "unable to add scache peer: %d", result);
+ Curl_ssl_session_destroy(s);
+ goto out;
+ }
+
+ cf_scache_peer_add_session(peer, s, now);
+
+out:
+ if(result) {
+ failf(data, "[SCACHE] failed to add session for %s, error=%d",
+ ssl_peer_key, result);
+ }
+ else
+ CURL_TRC_SSLS(data, "added session for %s [proto=0x%x, "
+ "valid_secs=%" FMT_OFF_T ", alpn=%s, earlydata=%zu, "
+ "quic_tp=%s], peer has %zu sessions now",
+ ssl_peer_key, s->ietf_tls_id, s->valid_until - now,
+ s->alpn, s->earlydata_max, s->quic_tp ? "yes" : "no",
+ peer ? Curl_llist_count(&peer->sessions) : 0);
+ return result;
+}
+
+CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session *s)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ CURLcode result;
+ DEBUGASSERT(ssl_config);
+
+ if(!scache || !ssl_config->primary.cache_session) {
+ Curl_ssl_session_destroy(s);
+ return CURLE_OK;
+ }
+
+ Curl_ssl_scache_lock(data);
+ result = cf_scache_add_session(cf, data, scache, ssl_peer_key, s);
+ Curl_ssl_scache_unlock(data);
+ return result;
+}
+
+void Curl_ssl_scache_return(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session *s)
+{
+ /* See RFC 8446 C.4:
+ * "Clients SHOULD NOT reuse a ticket for multiple connections." */
+ if(s && s->ietf_tls_id < 0x304)
+ (void)Curl_ssl_scache_put(cf, data, ssl_peer_key, s);
+ else
+ Curl_ssl_session_destroy(s);
+}
+
+CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session **ps)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_ssl_scache_peer *peer = NULL;
+ struct Curl_llist_node *n;
+ struct Curl_ssl_session *s = NULL;
+ CURLcode result;
+
+ *ps = NULL;
+ if(!scache)
+ return CURLE_OK;
+
+ Curl_ssl_scache_lock(data);
+ result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
+ &peer);
+ if(!result && peer) {
+ cf_scache_peer_remove_expired(peer, (curl_off_t)time(NULL));
+ n = Curl_llist_head(&peer->sessions);
+ if(n) {
+ s = Curl_node_take_elem(n);
+ (scache->age)++; /* increase general age */
+ peer->age = scache->age; /* set this as used in this age */
+ }
+ }
+ Curl_ssl_scache_unlock(data);
+ if(s) {
+ *ps = s;
+ CURL_TRC_SSLS(data, "took session for %s [proto=0x%x, "
+ "alpn=%s, earlydata=%zu, quic_tp=%s], %zu sessions remain",
+ ssl_peer_key, s->ietf_tls_id, s->alpn,
+ s->earlydata_max, s->quic_tp ? "yes" : "no",
+ Curl_llist_count(&peer->sessions));
+ }
+ else {
+ CURL_TRC_SSLS(data, "no cached session for %s", ssl_peer_key);
+ }
+ return result;
+}
+
+CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ void *sobj,
+ Curl_ssl_scache_obj_dtor *sobj_free)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_ssl_scache_peer *peer = NULL;
+ CURLcode result;
+
+ DEBUGASSERT(sobj);
+ DEBUGASSERT(sobj_free);
+
+ if(!scache) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+
+ result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer);
+ if(result || !peer) {
+ CURL_TRC_SSLS(data, "unable to add scache peer: %d", result);
+ goto out;
+ }
+
+ cf_ssl_scache_peer_set_obj(peer, sobj, sobj_free);
+ sobj = NULL; /* peer took ownership */
+
+out:
+ if(sobj && sobj_free)
+ sobj_free(sobj);
+ return result;
+}
+
+void *Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_ssl_scache_peer *peer = NULL;
+ CURLcode result;
+ void *sobj;
+
+ if(!scache)
+ return NULL;
+
+ result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
+ &peer);
+ if(result)
+ return NULL;
+
+ sobj = peer ? peer->sobj : NULL;
+
+ CURL_TRC_SSLS(data, "%s cached session for '%s'",
+ sobj ? "Found" : "No", ssl_peer_key);
+ return sobj;
+}
+
+void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_ssl_scache_peer *peer = NULL;
+ CURLcode result;
+
+ (void)cf;
+ if(!scache)
+ return;
+
+ Curl_ssl_scache_lock(data);
+ result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
+ &peer);
+ if(!result && peer)
+ cf_ssl_scache_clear_peer(peer);
+ Curl_ssl_scache_unlock(data);
+}
+
+#ifdef USE_SSLS_EXPORT
+
+#define CURL_SSL_TICKET_MAX (16*1024)
+
+static CURLcode cf_ssl_scache_peer_set_hmac(struct Curl_ssl_scache_peer *peer)
+{
+ CURLcode result;
+
+ DEBUGASSERT(peer);
+ if(!peer->ssl_peer_key)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ result = Curl_rand(NULL, peer->key_salt, sizeof(peer->key_salt));
+ if(result)
+ return result;
+
+ result = Curl_hmacit(&Curl_HMAC_SHA256,
+ peer->key_salt, sizeof(peer->key_salt),
+ (const unsigned char *)peer->ssl_peer_key,
+ strlen(peer->ssl_peer_key),
+ peer->key_hmac);
+ if(!result)
+ peer->hmac_set = TRUE;
+ return result;
+}
+
+static CURLcode
+cf_ssl_find_peer_by_hmac(struct Curl_ssl_scache *scache,
+ const unsigned char *salt,
+ const unsigned char *hmac,
+ struct Curl_ssl_scache_peer **ppeer)
+{
+ size_t i;
+ CURLcode result = CURLE_OK;
+
+ *ppeer = NULL;
+ if(!GOOD_SCACHE(scache))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ /* look for an entry that matches salt+hmac exactly or has a known
+ * ssl_peer_key which salt+hmac's to the same. */
+ for(i = 0; scache && i < scache->peer_count; i++) {
+ struct Curl_ssl_scache_peer *peer = &scache->peers[i];
+ if(!cf_ssl_scache_match_auth(peer, NULL))
+ continue;
+ if(scache->peers[i].hmac_set &&
+ !memcmp(peer->key_salt, salt, sizeof(peer->key_salt)) &&
+ !memcmp(peer->key_hmac, hmac, sizeof(peer->key_hmac))) {
+ /* found exact match, return */
+ *ppeer = peer;
+ goto out;
+ }
+ else if(peer->ssl_peer_key) {
+ unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH];
+ /* compute hmac for the passed salt */
+ result = Curl_hmacit(&Curl_HMAC_SHA256,
+ salt, sizeof(peer->key_salt),
+ (const unsigned char *)peer->ssl_peer_key,
+ strlen(peer->ssl_peer_key),
+ my_hmac);
+ if(result)
+ goto out;
+ if(!memcmp(my_hmac, hmac, sizeof(my_hmac))) {
+ /* cryptohash match, take over salt+hmac if no set and return */
+ if(!peer->hmac_set) {
+ memcpy(peer->key_salt, salt, sizeof(peer->key_salt));
+ memcpy(peer->key_hmac, hmac, sizeof(peer->key_hmac));
+ peer->hmac_set = TRUE;
+ }
+ *ppeer = peer;
+ goto out;
+ }
+ }
+ }
+out:
+ return result;
+}
+
+CURLcode Curl_ssl_session_import(struct Curl_easy *data,
+ const char *ssl_peer_key,
+ const unsigned char *shmac, size_t shmac_len,
+ const void *sdata, size_t sdata_len)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct Curl_ssl_scache_peer *peer = NULL;
+ struct Curl_ssl_session *s = NULL;
+ bool locked = FALSE;
+ CURLcode r;
+
+ if(!scache) {
+ r = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ if(!ssl_peer_key && (!shmac || !shmac_len)) {
+ r = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+
+ r = Curl_ssl_session_unpack(data, sdata, sdata_len, &s);
+ if(r)
+ goto out;
+
+ Curl_ssl_scache_lock(data);
+ locked = TRUE;
+
+ if(ssl_peer_key) {
+ r = cf_ssl_add_peer(data, scache, ssl_peer_key, NULL, &peer);
+ if(r)
+ goto out;
+ }
+ else if(shmac_len != (sizeof(peer->key_salt) + sizeof(peer->key_hmac))) {
+ /* Either salt+hmac was garbled by caller or is from a curl version
+ * that does things differently */
+ r = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ else {
+ const unsigned char *salt = shmac;
+ const unsigned char *hmac = shmac + sizeof(peer->key_salt);
+
+ r = cf_ssl_find_peer_by_hmac(scache, salt, hmac, &peer);
+ if(r)
+ goto out;
+ if(!peer) {
+ peer = cf_ssl_get_free_peer(scache);
+ if(peer) {
+ r = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL,
+ NULL, NULL, salt, hmac);
+ if(r)
+ goto out;
+ }
+ }
+ }
+
+ if(peer) {
+ cf_scache_peer_add_session(peer, s, time(NULL));
+ s = NULL; /* peer is now owner */
+ CURL_TRC_SSLS(data, "successfully imported ticket for peer %s, now "
+ "with %zu tickets",
+ peer->ssl_peer_key ? peer->ssl_peer_key : "without key",
+ Curl_llist_count(&peer->sessions));
+ }
+
+out:
+ if(locked)
+ Curl_ssl_scache_unlock(data);
+ Curl_ssl_session_destroy(s);
+ return r;
+}
+
+CURLcode Curl_ssl_session_export(struct Curl_easy *data,
+ curl_ssls_export_cb *export_fn,
+ void *userptr)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct Curl_ssl_scache_peer *peer;
+ struct dynbuf sbuf, hbuf;
+ struct Curl_llist_node *n;
+ size_t i, npeers = 0, ntickets = 0;
+ curl_off_t now = time(NULL);
+ CURLcode r = CURLE_OK;
+
+ if(!export_fn)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ if(!scache)
+ return CURLE_OK;
+
+ Curl_ssl_scache_lock(data);
+
+ curlx_dyn_init(&hbuf, (CURL_SHA256_DIGEST_LENGTH * 2) + 1);
+ curlx_dyn_init(&sbuf, CURL_SSL_TICKET_MAX);
+
+ for(i = 0; scache && i < scache->peer_count; i++) {
+ peer = &scache->peers[i];
+ if(!peer->ssl_peer_key && !peer->hmac_set)
+ continue; /* skip free entry */
+ if(!peer->exportable)
+ continue;
+
+ curlx_dyn_reset(&hbuf);
+ cf_scache_peer_remove_expired(peer, now);
+ n = Curl_llist_head(&peer->sessions);
+ if(n)
+ ++npeers;
+ while(n) {
+ struct Curl_ssl_session *s = Curl_node_elem(n);
+ if(!peer->hmac_set) {
+ r = cf_ssl_scache_peer_set_hmac(peer);
+ if(r)
+ goto out;
+ }
+ if(!curlx_dyn_len(&hbuf)) {
+ r = curlx_dyn_addn(&hbuf, peer->key_salt, sizeof(peer->key_salt));
+ if(r)
+ goto out;
+ r = curlx_dyn_addn(&hbuf, peer->key_hmac, sizeof(peer->key_hmac));
+ if(r)
+ goto out;
+ }
+ curlx_dyn_reset(&sbuf);
+ r = Curl_ssl_session_pack(data, s, &sbuf);
+ if(r)
+ goto out;
+
+ r = export_fn(data, userptr, peer->ssl_peer_key,
+ curlx_dyn_uptr(&hbuf), curlx_dyn_len(&hbuf),
+ curlx_dyn_uptr(&sbuf), curlx_dyn_len(&sbuf),
+ s->valid_until, s->ietf_tls_id,
+ s->alpn, s->earlydata_max);
+ if(r)
+ goto out;
+ ++ntickets;
+ n = Curl_node_next(n);
+ }
+
+ }
+ r = CURLE_OK;
+ CURL_TRC_SSLS(data, "exported %zu session tickets for %zu peers",
+ ntickets, npeers);
+
+out:
+ Curl_ssl_scache_unlock(data);
+ curlx_dyn_free(&hbuf);
+ curlx_dyn_free(&sbuf);
+ return r;
+}
+
+#endif /* USE_SSLS_EXPORT */
+
+#endif /* USE_SSL */
diff --git a/libs/libcurl/src/vtls/vtls_scache.h b/libs/libcurl/src/vtls/vtls_scache.h
new file mode 100644
index 0000000000..3f2a3978c8
--- /dev/null
+++ b/libs/libcurl/src/vtls/vtls_scache.h
@@ -0,0 +1,217 @@
+#ifndef HEADER_CURL_VTLS_SCACHE_H
+#define HEADER_CURL_VTLS_SCACHE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 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.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "../curl_setup.h"
+#include "../cfilters.h"
+#include "../urldata.h"
+
+#ifdef USE_SSL
+
+struct Curl_cfilter;
+struct Curl_easy;
+struct Curl_ssl_scache;
+struct Curl_ssl_session;
+struct ssl_peer;
+
+/* RFC 8446 (TLSv1.3) restrict lifetime to one week max, for
+ * other, less secure versions, we restrict it to a day */
+#define CURL_SCACHE_MAX_13_LIFETIME_SEC (60*60*24*7)
+#define CURL_SCACHE_MAX_12_LIFETIME_SEC (60*60*24)
+
+/* Create a session cache for up to max_peers endpoints with a total
+ * of up to max_sessions SSL sessions per peer */
+CURLcode Curl_ssl_scache_create(size_t max_peers,
+ size_t max_sessions_per_peer,
+ struct Curl_ssl_scache **pscache);
+
+void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache);
+
+/* Create a key from peer and TLS configuration information that is
+ * unique for how the connection filter wants to establish a TLS
+ * connection to the peer.
+ * If the filter is a TLS proxy filter, it will use the proxy relevant
+ * information.
+ * @param cf the connection filter wanting to use it
+ * @param peer the peer the filter wants to talk to
+ * @param tls_id identifier of TLS implementation for sessions. Should
+ * include full version if session data from other versions
+ * is to be avoided.
+ * @param ppeer_key on successful return, the key generated
+ */
+CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
+ const struct ssl_peer *peer,
+ const char *tls_id,
+ char **ppeer_key);
+
+/* Lock session cache mutex.
+ * Call this before calling other Curl_ssl_*session* functions
+ * Caller should unlock this mutex as soon as possible, as it may block
+ * other SSL connection from making progress.
+ * The purpose of explicitly locking SSL session cache data is to allow
+ * individual SSL engines to manage session lifetime in their specific way.
+ */
+void Curl_ssl_scache_lock(struct Curl_easy *data);
+
+/* Unlock session cache mutex */
+void Curl_ssl_scache_unlock(struct Curl_easy *data);
+
+/* Get TLS session object from the cache for the ssl_peer_ey.
+ * scache mutex must be locked (see Curl_ssl_scache_lock).
+ * Caller must make sure that the ownership of returned session object
+ * is properly taken (e.g. its refcount is incremented
+ * under scache mutex).
+ * @param cf the connection filter wanting to use it
+ * @param data the transfer involved
+ * @param ssl_peer_key the key for lookup
+ * @retval sobj the object for the peer key or NULL
+ */
+void *Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key);
+
+typedef void Curl_ssl_scache_obj_dtor(void *sobj);
+
+/* Add a TLS session related object to the cache.
+ * Replaces an existing object with the same peer_key.
+ * scache mutex must be locked (see Curl_ssl_scache_lock).
+ * Call takes ownership of `sobj`, using `sobj_dtor_cb`
+ * to deallocate it. Is called in all outcomes, either right away or
+ * later when the session cache is cleaned up.
+ * Caller must ensure that it has properly shared ownership of `sobj`
+ * with cache (e.g. incrementing refcount on success)
+ * @param cf the connection filter wanting to use it
+ * @param data the transfer involved
+ * @param ssl_peer_key the key for lookup
+ * @param sobj the TLS session object
+ * @param sobj_free_cb callback to free the session objectt
+ */
+CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ void *sobj,
+ Curl_ssl_scache_obj_dtor *sobj_dtor_cb);
+
+/* All about an SSL session ticket */
+struct Curl_ssl_session {
+ const void *sdata; /* session ticket data, plain bytes */
+ size_t sdata_len; /* number of bytes in sdata */
+ curl_off_t valid_until; /* seconds since EPOCH until ticket expires */
+ int ietf_tls_id; /* TLS protocol identifier negotiated */
+ char *alpn; /* APLN TLS negotiated protocol string */
+ size_t earlydata_max; /* max 0-RTT data supported by peer */
+ const unsigned char *quic_tp; /* Optional QUIC transport param bytes */
+ size_t quic_tp_len; /* number of bytes in quic_tp */
+ struct Curl_llist_node list; /* internal storage handling */
+};
+
+/* Create a `session` instance. Does NOT need locking.
+ * Takes ownership of `sdata` and `sobj` regardless of return code.
+ * @param sdata bytes of SSL session data or NULL (sobj then required)
+ * @param sdata_len amount of session data bytes
+ * @param ietf_tls_id IETF protocol version, e.g. 0x304 for TLSv1.3
+ * @param alpn ALPN protocol selected or NULL
+ * @param valid_until seconds since EPOCH when session expires, pass 0
+ * in case this is not known.
+ * @param psession on return the scached session instance created
+ */
+CURLcode
+Curl_ssl_session_create(void *sdata, size_t sdata_len,
+ int ietf_tls_id, const char *alpn,
+ curl_off_t valid_until,
+ size_t earlydata_max,
+ struct Curl_ssl_session **psession);
+
+/* Variation of session creation with quic transport parameter bytes,
+ * Takes ownership of `quic_tp` regardless of return code. */
+CURLcode
+Curl_ssl_session_create2(void *sdata, size_t sdata_len,
+ int ietf_tls_id, const char *alpn,
+ curl_off_t valid_until,
+ size_t earlydata_max,
+ unsigned char *quic_tp, size_t quic_tp_len,
+ struct Curl_ssl_session **psession);
+
+/* Destroy a `session` instance. Can be called with NULL.
+ * Does NOT need locking. */
+void Curl_ssl_session_destroy(struct Curl_ssl_session *s);
+
+/* Put the scache session into the cache. Does NOT need locking.
+ * Call takes ownership of `s` in all outcomes.
+ * @param cf the connection filter wanting to use it
+ * @param data the transfer involved
+ * @param ssl_peer_key the key for lookup
+ * @param s the scache session object
+ */
+CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session *s);
+
+/* Take a matching scache session from the cache. Does NOT need locking.
+ * @param cf the connection filter wanting to use it
+ * @param data the transfer involved
+ * @param ssl_peer_key the key for lookup
+ * @param s on return, the scache session object or NULL
+ */
+CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session **ps);
+
+/* Return a taken scache session to the cache. Does NOT need locking.
+ * Depending on TLS version and other criteria, it may cache it again
+ * or destroy it. Maybe called with a NULL session.
+ */
+void Curl_ssl_scache_return(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session *s);
+
+/* Remove all sessions and obj for the peer_key. Does NOT need locking. */
+void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key);
+
+#ifdef USE_SSLS_EXPORT
+
+CURLcode Curl_ssl_session_import(struct Curl_easy *data,
+ const char *ssl_peer_key,
+ const unsigned char *shmac, size_t shmac_len,
+ const void *sdata, size_t sdata_len);
+
+CURLcode Curl_ssl_session_export(struct Curl_easy *data,
+ curl_ssls_export_cb *export_fn,
+ void *userptr);
+
+#endif /* USE_SSLS_EXPORT */
+
+#else /* USE_SSL */
+
+#define Curl_ssl_scache_create(x,y,z) ((void)x, CURLE_OK)
+#define Curl_ssl_scache_destroy(x) do {} while(0)
+
+#endif /* USE_SSL (else) */
+
+#endif /* HEADER_CURL_VTLS_SCACHE_H */
diff --git a/libs/libcurl/src/vtls/vtls_spack.c b/libs/libcurl/src/vtls/vtls_spack.c
new file mode 100644
index 0000000000..39555f226b
--- /dev/null
+++ b/libs/libcurl/src/vtls/vtls_spack.c
@@ -0,0 +1,346 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 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.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "../curl_setup.h"
+
+#ifdef USE_SSLS_EXPORT
+
+#include "../urldata.h"
+#include "../curl_trc.h"
+#include "vtls_scache.h"
+#include "vtls_spack.h"
+#include "../strdup.h"
+
+/* The last #include files should be: */
+#include "../curl_memory.h"
+#include "../memdebug.h"
+
+#ifdef _MSC_VER
+#if _MSC_VER >= 1600
+#include <stdint.h>
+#else
+typedef unsigned char uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#endif
+#endif /* _MSC_VER */
+
+#ifndef UINT16_MAX
+#define UINT16_MAX 0xffff
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX 0xffffffff
+#endif
+
+#define CURL_SPACK_VERSION 0x01
+#define CURL_SPACK_IETF_ID 0x02
+#define CURL_SPACK_VALID_UNTIL 0x03
+#define CURL_SPACK_TICKET 0x04
+#define CURL_SPACK_ALPN 0x05
+#define CURL_SPACK_EARLYDATA 0x06
+#define CURL_SPACK_QUICTP 0x07
+
+static CURLcode spack_enc8(struct dynbuf *buf, uint8_t b)
+{
+ return curlx_dyn_addn(buf, &b, 1);
+}
+
+static CURLcode
+spack_dec8(uint8_t *val, const uint8_t **src, const uint8_t *end)
+{
+ if(end - *src < 1)
+ return CURLE_READ_ERROR;
+ *val = **src;
+ *src += 1;
+ return CURLE_OK;
+}
+
+static CURLcode spack_enc16(struct dynbuf *buf, uint16_t val)
+{
+ uint8_t nval[2];
+ nval[0] = (uint8_t)(val >> 8);
+ nval[1] = (uint8_t)val;
+ return curlx_dyn_addn(buf, nval, sizeof(nval));
+}
+
+static CURLcode
+spack_dec16(uint16_t *val, const uint8_t **src, const uint8_t *end)
+{
+ if(end - *src < 2)
+ return CURLE_READ_ERROR;
+ *val = (uint16_t)((*src)[0] << 8 | (*src)[1]);
+ *src += 2;
+ return CURLE_OK;
+}
+
+static CURLcode spack_enc32(struct dynbuf *buf, uint32_t val)
+{
+ uint8_t nval[4];
+ nval[0] = (uint8_t)(val >> 24);
+ nval[1] = (uint8_t)(val >> 16);
+ nval[2] = (uint8_t)(val >> 8);
+ nval[3] = (uint8_t)val;
+ return curlx_dyn_addn(buf, nval, sizeof(nval));
+}
+
+static CURLcode
+spack_dec32(uint32_t *val, const uint8_t **src, const uint8_t *end)
+{
+ if(end - *src < 4)
+ return CURLE_READ_ERROR;
+ *val = (uint32_t)(*src)[0] << 24 | (uint32_t)(*src)[1] << 16 |
+ (uint32_t)(*src)[2] << 8 | (*src)[3];
+ *src += 4;
+ return CURLE_OK;
+}
+
+static CURLcode spack_enc64(struct dynbuf *buf, uint64_t val)
+{
+ uint8_t nval[8];
+ nval[0] = (uint8_t)(val >> 56);
+ nval[1] = (uint8_t)(val >> 48);
+ nval[2] = (uint8_t)(val >> 40);
+ nval[3] = (uint8_t)(val >> 32); \
+ nval[4] = (uint8_t)(val >> 24);
+ nval[5] = (uint8_t)(val >> 16);
+ nval[6] = (uint8_t)(val >> 8);
+ nval[7] = (uint8_t)val;
+ return curlx_dyn_addn(buf, nval, sizeof(nval));
+}
+
+static CURLcode
+spack_dec64(uint64_t *val, const uint8_t **src, const uint8_t *end)
+{
+ if(end - *src < 8)
+ return CURLE_READ_ERROR;
+ *val = (uint64_t)(*src)[0] << 56 | (uint64_t)(*src)[1] << 48 |
+ (uint64_t)(*src)[2] << 40 | (uint64_t)(*src)[3] << 32 |
+ (uint64_t)(*src)[4] << 24 | (uint64_t)(*src)[5] << 16 |
+ (uint64_t)(*src)[6] << 8 | (*src)[7];
+ *src += 8;
+ return CURLE_OK;
+}
+
+static CURLcode spack_encstr16(struct dynbuf *buf, const char *s)
+{
+ size_t slen = strlen(s);
+ CURLcode r;
+ if(slen > UINT16_MAX)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ r = spack_enc16(buf, (uint16_t)slen);
+ if(!r) {
+ r = curlx_dyn_addn(buf, s, slen);
+ }
+ return r;
+}
+
+static CURLcode
+spack_decstr16(char **val, const uint8_t **src, const uint8_t *end)
+{
+ uint16_t slen;
+ CURLcode r;
+
+ *val = NULL;
+ r = spack_dec16(&slen, src, end);
+ if(r)
+ return r;
+ if(end - *src < slen)
+ return CURLE_READ_ERROR;
+ *val = Curl_memdup0((const char *)(*src), slen);
+ *src += slen;
+ return *val ? CURLE_OK : CURLE_OUT_OF_MEMORY;
+}
+
+static CURLcode spack_encdata16(struct dynbuf *buf,
+ const uint8_t *data, size_t data_len)
+{
+ CURLcode r;
+ if(data_len > UINT16_MAX)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ r = spack_enc16(buf, (uint16_t)data_len);
+ if(!r) {
+ r = curlx_dyn_addn(buf, data, data_len);
+ }
+ return r;
+}
+
+static CURLcode
+spack_decdata16(uint8_t **val, size_t *val_len,
+ const uint8_t **src, const uint8_t *end)
+{
+ uint16_t data_len;
+ CURLcode r;
+
+ *val = NULL;
+ r = spack_dec16(&data_len, src, end);
+ if(r)
+ return r;
+ if(end - *src < data_len)
+ return CURLE_READ_ERROR;
+ *val = Curl_memdup0((const char *)(*src), data_len);
+ *val_len = data_len;
+ *src += data_len;
+ return *val ? CURLE_OK : CURLE_OUT_OF_MEMORY;
+}
+
+CURLcode Curl_ssl_session_pack(struct Curl_easy *data,
+ struct Curl_ssl_session *s,
+ struct dynbuf *buf)
+{
+ CURLcode r;
+ DEBUGASSERT(s->sdata);
+ DEBUGASSERT(s->sdata_len);
+
+ if(s->valid_until < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ r = spack_enc8(buf, CURL_SPACK_VERSION);
+ if(!r)
+ r = spack_enc8(buf, CURL_SPACK_TICKET);
+ if(!r)
+ r = spack_encdata16(buf, s->sdata, s->sdata_len);
+ if(!r)
+ r = spack_enc8(buf, CURL_SPACK_IETF_ID);
+ if(!r)
+ r = spack_enc16(buf, (uint16_t)s->ietf_tls_id);
+ if(!r)
+ r = spack_enc8(buf, CURL_SPACK_VALID_UNTIL);
+ if(!r)
+ r = spack_enc64(buf, (uint64_t)s->valid_until);
+ if(!r && s->alpn) {
+ r = spack_enc8(buf, CURL_SPACK_ALPN);
+ if(!r)
+ r = spack_encstr16(buf, s->alpn);
+ }
+ if(!r && s->earlydata_max) {
+ if(s->earlydata_max > UINT32_MAX)
+ r = CURLE_BAD_FUNCTION_ARGUMENT;
+ if(!r)
+ r = spack_enc8(buf, CURL_SPACK_EARLYDATA);
+ if(!r)
+ r = spack_enc32(buf, (uint32_t)s->earlydata_max);
+ }
+ if(!r && s->quic_tp && s->quic_tp_len) {
+ r = spack_enc8(buf, CURL_SPACK_QUICTP);
+ if(!r)
+ r = spack_encdata16(buf, s->quic_tp, s->quic_tp_len);
+ }
+
+ if(r)
+ CURL_TRC_SSLS(data, "error packing data: %d", r);
+ return r;
+}
+
+CURLcode Curl_ssl_session_unpack(struct Curl_easy *data,
+ const void *bufv, size_t buflen,
+ struct Curl_ssl_session **ps)
+{
+ struct Curl_ssl_session *s = NULL;
+ const unsigned char *buf = (const unsigned char *)bufv;
+ const unsigned char *end = buf + buflen;
+ uint8_t val8, *pval8;
+ uint16_t val16;
+ uint32_t val32;
+ uint64_t val64;
+ CURLcode r;
+
+ DEBUGASSERT(buf);
+ DEBUGASSERT(buflen);
+ *ps = NULL;
+
+ r = spack_dec8(&val8, &buf, end);
+ if(r)
+ goto out;
+ if(val8 != CURL_SPACK_VERSION) {
+ r = CURLE_READ_ERROR;
+ goto out;
+ }
+
+ s = calloc(1, sizeof(*s));
+ if(!s) {
+ r = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ while(buf < end) {
+ r = spack_dec8(&val8, &buf, end);
+ if(r)
+ goto out;
+
+ switch(val8) {
+ case CURL_SPACK_ALPN:
+ r = spack_decstr16(&s->alpn, &buf, end);
+ if(r)
+ goto out;
+ break;
+ case CURL_SPACK_EARLYDATA:
+ r = spack_dec32(&val32, &buf, end);
+ if(r)
+ goto out;
+ s->earlydata_max = val32;
+ break;
+ case CURL_SPACK_IETF_ID:
+ r = spack_dec16(&val16, &buf, end);
+ if(r)
+ goto out;
+ s->ietf_tls_id = val16;
+ break;
+ case CURL_SPACK_QUICTP: {
+ r = spack_decdata16(&pval8, &s->quic_tp_len, &buf, end);
+ if(r)
+ goto out;
+ s->quic_tp = pval8;
+ break;
+ }
+ case CURL_SPACK_TICKET: {
+ r = spack_decdata16(&pval8, &s->sdata_len, &buf, end);
+ if(r)
+ goto out;
+ s->sdata = pval8;
+ break;
+ }
+ case CURL_SPACK_VALID_UNTIL:
+ r = spack_dec64(&val64, &buf, end);
+ if(r)
+ goto out;
+ s->valid_until = (curl_off_t)val64;
+ break;
+ default: /* unknown tag */
+ r = CURLE_READ_ERROR;
+ goto out;
+ }
+ }
+
+out:
+ if(r) {
+ CURL_TRC_SSLS(data, "error unpacking data: %d", r);
+ Curl_ssl_session_destroy(s);
+ }
+ else
+ *ps = s;
+ return r;
+}
+
+#endif /* USE_SSLS_EXPORT */
diff --git a/libs/libcurl/src/vtls/vtls_spack.h b/libs/libcurl/src/vtls/vtls_spack.h
new file mode 100644
index 0000000000..e1dfc18200
--- /dev/null
+++ b/libs/libcurl/src/vtls/vtls_spack.h
@@ -0,0 +1,43 @@
+#ifndef HEADER_CURL_VTLS_SPACK_H
+#define HEADER_CURL_VTLS_SPACK_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 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.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "../curl_setup.h"
+
+#ifdef USE_SSLS_EXPORT
+
+struct dynbuf;
+struct Curl_ssl_session;
+
+CURLcode Curl_ssl_session_pack(struct Curl_easy *data,
+ struct Curl_ssl_session *s,
+ struct dynbuf *buf);
+
+CURLcode Curl_ssl_session_unpack(struct Curl_easy *data,
+ const void *bufv, size_t buflen,
+ struct Curl_ssl_session **ps);
+
+#endif /* USE_SSLS_EXPORT */
+
+#endif /* HEADER_CURL_VTLS_SPACK_H */
diff --git a/libs/libcurl/src/vtls/wolfssl.c b/libs/libcurl/src/vtls/wolfssl.c
index 9a05f82946..0971895859 100644
--- a/libs/libcurl/src/vtls/wolfssl.c
+++ b/libs/libcurl/src/vtls/wolfssl.c
@@ -28,13 +28,14 @@
*
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_WOLFSSL
#define WOLFSSL_OPTIONS_IGNORE_SYS
-#include <wolfssl/version.h>
#include <wolfssl/options.h>
+#include <wolfssl/version.h>
+
#if LIBWOLFSSL_VERSION_HEX < 0x03004006 /* wolfSSL 3.4.6 (2015) */
#error "wolfSSL version should be at least 3.4.6"
@@ -55,36 +56,34 @@
#include <limits.h>
-#include "urldata.h"
-#include "sendf.h"
-#include "inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
#include "vtls.h"
#include "vtls_int.h"
+#include "vtls_scache.h"
#include "keylog.h"
-#include "parsedate.h"
-#include "connect.h" /* for the connect timeout */
-#include "select.h"
-#include "strcase.h"
+#include "../parsedate.h"
+#include "../connect.h" /* for the connect timeout */
+#include "../progress.h"
+#include "../select.h"
+#include "../strcase.h"
+#include "../strdup.h"
#include "x509asn1.h"
-#include "curl_printf.h"
-#include "multiif.h"
+#include "../curl_printf.h"
+#include "../multiif.h"
-#include <wolfssl/openssl/ssl.h>
#include <wolfssl/ssl.h>
#include <wolfssl/error-ssl.h>
#include "wolfssl.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
-#ifdef USE_ECH
-# include "curl_base64.h"
-# define ECH_ENABLED(__data__) \
- (__data__->set.tls_ech && \
- !(__data__->set.tls_ech & CURLECH_DISABLE)\
- )
-#endif /* USE_ECH */
+#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
+#define USE_ECH_WOLFSSL
+#endif
/* KEEP_PEER_CERT is a product of the presence of build time symbol
OPENSSL_EXTRA without NO_CERTS, depending on the version. KEEP_PEER_CERT is
@@ -97,11 +96,11 @@
#endif
#endif
-#ifdef HAVE_WOLFSSL_BIO
+#ifdef HAVE_WOLFSSL_BIO_NEW
#define USE_BIO_CHAIN
-#ifdef HAVE_WOLFSSL_FULL_BIO
+#ifdef HAVE_WOLFSSL_BIO_SET_SHUTDOWN
#define USE_FULL_BIO
-#else /* HAVE_WOLFSSL_FULL_BIO */
+#else /* HAVE_WOLFSSL_BIO_SET_SHUTDOWN */
#undef USE_FULL_BIO
#endif
/* wolfSSL 5.7.4 and older do not have these symbols, but only the
@@ -115,10 +114,14 @@
#define wolfSSL_BIO_set_retry_read BIO_set_retry_read
#endif /* !WOLFSSL_BIO_CTRL_GET_CLOSE */
-#else /* HAVE_WOLFSSL_BIO */
+#else /* HAVE_WOLFSSL_BIO_NEW */
#undef USE_BIO_CHAIN
#endif
+static CURLcode wssl_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done);
+
#ifdef OPENSSL_EXTRA
/*
* Availability note:
@@ -126,13 +129,13 @@
* wolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that
* option is not set, then TLS 1.3 will not be logged.
* For TLS 1.2 and before, we use wolfSSL_get_keys().
- * SSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA
+ * wolfSSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA
* (--enable-opensslextra or --enable-all).
*/
#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13)
static int
-wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret,
- int secretSz, void *ctx)
+wssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret,
+ int secretSz, void *ctx)
{
const char *label;
unsigned char client_random[SSL3_RANDOM_SIZE];
@@ -168,7 +171,7 @@ wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret,
return 0;
}
- if(SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE) == 0) {
+ if(wolfSSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE) == 0) {
/* Should never happen as wolfSSL_KeepArrays() was called before. */
return 0;
}
@@ -178,8 +181,7 @@ wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret,
}
#endif /* defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) */
-static void
-wolfssl_log_tls12_secret(WOLFSSL *ssl)
+static void wssl_log_tls12_secret(WOLFSSL *ssl)
{
unsigned char *ms, *sr, *cr;
unsigned int msLen, srLen, crLen, i, x = 0;
@@ -221,7 +223,7 @@ wolfssl_log_tls12_secret(WOLFSSL *ssl)
}
#endif /* OPENSSL_EXTRA */
-static int wolfssl_do_file_type(const char *type)
+static int wssl_do_file_type(const char *type)
{
if(!type || !type[0])
return WOLFSSL_FILETYPE_PEM;
@@ -239,19 +241,19 @@ struct group_name_map {
};
static const struct group_name_map gnm[] = {
- { WOLFSSL_KYBER_LEVEL1, "KYBER_LEVEL1" },
- { WOLFSSL_KYBER_LEVEL3, "KYBER_LEVEL3" },
- { WOLFSSL_KYBER_LEVEL5, "KYBER_LEVEL5" },
- { WOLFSSL_P256_KYBER_LEVEL1, "P256_KYBER_LEVEL1" },
- { WOLFSSL_P384_KYBER_LEVEL3, "P384_KYBER_LEVEL3" },
- { WOLFSSL_P521_KYBER_LEVEL5, "P521_KYBER_LEVEL5" },
+ { WOLFSSL_ML_KEM_512, "ML_KEM_512" },
+ { WOLFSSL_ML_KEM_768, "ML_KEM_768" },
+ { WOLFSSL_ML_KEM_1024, "ML_KEM_1024" },
+ { WOLFSSL_P256_ML_KEM_512, "P256_ML_KEM_512" },
+ { WOLFSSL_P384_ML_KEM_768, "P384_ML_KEM_768" },
+ { WOLFSSL_P521_ML_KEM_1024, "P521_ML_KEM_1024" },
{ 0, NULL }
};
#endif
#ifdef USE_BIO_CHAIN
-static int wolfssl_bio_cf_create(WOLFSSL_BIO *bio)
+static int wssl_bio_cf_create(WOLFSSL_BIO *bio)
{
#ifdef USE_FULL_BIO
wolfSSL_BIO_set_shutdown(bio, 1);
@@ -260,14 +262,14 @@ static int wolfssl_bio_cf_create(WOLFSSL_BIO *bio)
return 1;
}
-static int wolfssl_bio_cf_destroy(WOLFSSL_BIO *bio)
+static int wssl_bio_cf_destroy(WOLFSSL_BIO *bio)
{
if(!bio)
return 0;
return 1;
}
-static long wolfssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
+static long wssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
{
struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
long ret = 1;
@@ -297,9 +299,11 @@ static long wolfssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
ret = 1;
break;
#ifdef WOLFSSL_BIO_CTRL_EOF
- case WOLFSSL_BIO_CTRL_EOF:
+ case WOLFSSL_BIO_CTRL_EOF: {
/* EOF has been reached on input? */
- return (!cf->next || !cf->next->connected);
+ struct ssl_connect_data *connssl = cf->ctx;
+ return connssl->peer_closed;
+ }
#endif
default:
ret = 0;
@@ -308,30 +312,29 @@ static long wolfssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
return ret;
}
-static int wolfssl_bio_cf_out_write(WOLFSSL_BIO *bio,
- const char *buf, int blen)
+static int wssl_bio_cf_out_write(WOLFSSL_BIO *bio,
+ const char *buf, int blen)
{
struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nwritten, skiplen = 0;
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
- if(backend->shutting_down && backend->io_send_blocked_len &&
- (backend->io_send_blocked_len < blen)) {
+ if(wssl->shutting_down && wssl->io_send_blocked_len &&
+ (wssl->io_send_blocked_len < blen)) {
/* bug in wolfSSL: <https://github.com/wolfSSL/wolfssl/issues/7784>
* It adds the close notify message again every time we retry
* sending during shutdown. */
CURL_TRC_CF(data, cf, "bio_write, shutdown restrict send of %d"
- " to %d bytes", blen, backend->io_send_blocked_len);
- skiplen = (ssize_t)(blen - backend->io_send_blocked_len);
- blen = backend->io_send_blocked_len;
+ " to %d bytes", blen, wssl->io_send_blocked_len);
+ skiplen = (ssize_t)(blen - wssl->io_send_blocked_len);
+ blen = wssl->io_send_blocked_len;
}
nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &result);
- backend->io_result = result;
+ wssl->io_result = result;
CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %zd, %d",
blen, nwritten, result);
#ifdef USE_FULL_BIO
@@ -339,20 +342,19 @@ static int wolfssl_bio_cf_out_write(WOLFSSL_BIO *bio,
#endif
if(nwritten < 0 && CURLE_AGAIN == result) {
wolfSSL_BIO_set_retry_write(bio);
- if(backend->shutting_down && !backend->io_send_blocked_len)
- backend->io_send_blocked_len = blen;
+ if(wssl->shutting_down && !wssl->io_send_blocked_len)
+ wssl->io_send_blocked_len = blen;
}
else if(!result && skiplen)
nwritten += skiplen;
return (int)nwritten;
}
-static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
+static int wssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
{
struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nread;
CURLcode result = CURLE_OK;
@@ -362,8 +364,20 @@ static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
if(!buf)
return 0;
+ if((connssl->connecting_state == ssl_connect_2) &&
+ !wssl->x509_store_setup) {
+ /* During handshake, init the x509 store before receiving the
+ * server response. This allows sending of ClientHello without delay. */
+ result = Curl_wssl_setup_x509_store(cf, data, wssl);
+ if(result) {
+ CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", result);
+ wssl->io_result = result;
+ return -1;
+ }
+ }
+
nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
- backend->io_result = result;
+ wssl->io_result = result;
CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %zd, %d", blen, nread, result);
#ifdef USE_FULL_BIO
wolfSSL_BIO_clear_retry_flags(bio);
@@ -375,82 +389,93 @@ static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
return (int)nread;
}
-static WOLFSSL_BIO_METHOD *wolfssl_bio_cf_method = NULL;
+static WOLFSSL_BIO_METHOD *wssl_bio_cf_method = NULL;
-static void wolfssl_bio_cf_init_methods(void)
+static void wssl_bio_cf_init_methods(void)
{
- wolfssl_bio_cf_method = wolfSSL_BIO_meth_new(WOLFSSL_BIO_MEMORY,
+ wssl_bio_cf_method = wolfSSL_BIO_meth_new(WOLFSSL_BIO_MEMORY,
"wolfSSL CF BIO");
- wolfSSL_BIO_meth_set_write(wolfssl_bio_cf_method, &wolfssl_bio_cf_out_write);
- wolfSSL_BIO_meth_set_read(wolfssl_bio_cf_method, &wolfssl_bio_cf_in_read);
- wolfSSL_BIO_meth_set_ctrl(wolfssl_bio_cf_method, &wolfssl_bio_cf_ctrl);
- wolfSSL_BIO_meth_set_create(wolfssl_bio_cf_method, &wolfssl_bio_cf_create);
- wolfSSL_BIO_meth_set_destroy(wolfssl_bio_cf_method, &wolfssl_bio_cf_destroy);
+ wolfSSL_BIO_meth_set_write(wssl_bio_cf_method, &wssl_bio_cf_out_write);
+ wolfSSL_BIO_meth_set_read(wssl_bio_cf_method, &wssl_bio_cf_in_read);
+ wolfSSL_BIO_meth_set_ctrl(wssl_bio_cf_method, &wssl_bio_cf_ctrl);
+ wolfSSL_BIO_meth_set_create(wssl_bio_cf_method, &wssl_bio_cf_create);
+ wolfSSL_BIO_meth_set_destroy(wssl_bio_cf_method, &wssl_bio_cf_destroy);
}
-static void wolfssl_bio_cf_free_methods(void)
+static void wssl_bio_cf_free_methods(void)
{
- wolfSSL_BIO_meth_free(wolfssl_bio_cf_method);
+ wolfSSL_BIO_meth_free(wssl_bio_cf_method);
}
#else /* USE_BIO_CHAIN */
-#define wolfssl_bio_cf_init_methods() Curl_nop_stmt
-#define wolfssl_bio_cf_free_methods() Curl_nop_stmt
+#define wssl_bio_cf_init_methods() Curl_nop_stmt
+#define wssl_bio_cf_free_methods() Curl_nop_stmt
#endif /* !USE_BIO_CHAIN */
-static void wolfssl_session_free(void *sdata, size_t slen)
-{
- (void)slen;
- free(sdata);
-}
-
-CURLcode wssl_cache_session(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct ssl_peer *peer,
- WOLFSSL_SESSION *session)
+CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ WOLFSSL_SESSION *session,
+ int ietf_tls_id,
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len)
{
CURLcode result = CURLE_OK;
- unsigned char *sdata = NULL;
- unsigned int slen;
+ struct Curl_ssl_session *sc_session = NULL;
+ unsigned char *sdata = NULL, *qtp_clone = NULL;
+ unsigned int sdata_len;
+ unsigned int earlydata_max = 0;
if(!session)
goto out;
- slen = wolfSSL_i2d_SSL_SESSION(session, NULL);
- if(slen <= 0) {
- CURL_TRC_CF(data, cf, "fail to assess session length: %u", slen);
+ sdata_len = wolfSSL_i2d_SSL_SESSION(session, NULL);
+ if(sdata_len <= 0) {
+ CURL_TRC_CF(data, cf, "fail to assess session length: %u", sdata_len);
result = CURLE_FAILED_INIT;
goto out;
}
- sdata = calloc(1, slen);
+ sdata = calloc(1, sdata_len);
if(!sdata) {
- failf(data, "unable to allocate session buffer of %u bytes", slen);
+ failf(data, "unable to allocate session buffer of %u bytes", sdata_len);
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- slen = wolfSSL_i2d_SSL_SESSION(session, &sdata);
- if(slen <= 0) {
- CURL_TRC_CF(data, cf, "fail to serialize session: %u", slen);
+ sdata_len = wolfSSL_i2d_SSL_SESSION(session, &sdata);
+ if(sdata_len <= 0) {
+ CURL_TRC_CF(data, cf, "fail to serialize session: %u", sdata_len);
result = CURLE_FAILED_INIT;
goto out;
}
+ if(quic_tp && quic_tp_len) {
+ qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len);
+ if(!qtp_clone) {
+ free(sdata);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+#ifdef WOLFSSL_EARLY_DATA
+ earlydata_max = wolfSSL_SESSION_get_max_early_data(session);
+#endif
- Curl_ssl_sessionid_lock(data);
- result = Curl_ssl_set_sessionid(cf, data, peer, NULL,
- sdata, slen, wolfssl_session_free);
- Curl_ssl_sessionid_unlock(data);
- if(result)
- failf(data, "failed to add new ssl session to cache (%d)", result);
- else {
- CURL_TRC_CF(data, cf, "added new session to cache");
- sdata = NULL;
+ result = Curl_ssl_session_create2(sdata, sdata_len,
+ ietf_tls_id, alpn,
+ (curl_off_t)time(NULL) +
+ wolfSSL_SESSION_get_timeout(session),
+ earlydata_max, qtp_clone, quic_tp_len,
+ &sc_session);
+ sdata = NULL; /* took ownership of sdata */
+ if(!result) {
+ result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
+ /* took ownership of `sc_session` */
}
out:
free(sdata);
- return 0;
+ return result;
}
static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
@@ -465,50 +490,122 @@ static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
DEBUGASSERT(connssl);
DEBUGASSERT(data);
if(connssl && data) {
- (void)wssl_cache_session(cf, data, &connssl->peer, session);
+ (void)Curl_wssl_cache_session(cf, data, connssl->peer.scache_key,
+ session, wolfSSL_version(ssl),
+ connssl->negotiated.alpn, NULL, 0);
}
}
return 0;
}
-CURLcode wssl_setup_session(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct wolfssl_ctx *wss,
- struct ssl_peer *peer)
+static CURLcode wssl_on_session_reuse(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct alpn_spec *alpns,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data)
{
- void *psdata;
- const unsigned char *sdata = NULL;
- size_t slen = 0;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
CURLcode result = CURLE_OK;
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, peer, &psdata, &slen, NULL)) {
+ *do_early_data = FALSE;
+#ifdef WOLFSSL_EARLY_DATA
+ connssl->earlydata_max = wolfSSL_SESSION_get_max_early_data(
+ wolfSSL_get_session(wssl->ssl));
+#else
+ (void)wssl;
+ connssl->earlydata_max = 0;
+#endif
+
+ if(!connssl->earlydata_max) {
+ /* Seems to be GnuTLS way to signal no EarlyData in session */
+ CURL_TRC_CF(data, cf, "SSL session does not allow earlydata");
+ }
+ else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) {
+ CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data");
+ }
+ else {
+ infof(data, "SSL session allows %zu bytes of early data, "
+ "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn);
+ connssl->earlydata_state = ssl_earlydata_await;
+ connssl->state = ssl_connection_deferred;
+ result = Curl_alpn_set_negotiated(cf, data, connssl,
+ (const unsigned char *)scs->alpn,
+ scs->alpn ? strlen(scs->alpn) : 0);
+ *do_early_data = !result;
+ }
+ return result;
+}
+
+static CURLcode
+wssl_setup_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct wssl_ctx *wss,
+ struct alpn_spec *alpns,
+ const char *ssl_peer_key,
+ Curl_wssl_init_session_reuse_cb *sess_reuse_cb)
+{
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct Curl_ssl_session *scs = NULL;
+ CURLcode result;
+
+ result = Curl_ssl_scache_take(cf, data, ssl_peer_key, &scs);
+ if(!result && scs && scs->sdata && scs->sdata_len &&
+ (!scs->alpn || Curl_alpn_contains_proto(alpns, scs->alpn))) {
WOLFSSL_SESSION *session;
- sdata = psdata;
- session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata, (long)slen);
+ /* wolfSSL changes the passed pointer for whatever reasons, yikes */
+ const unsigned char *sdata = scs->sdata;
+ session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata, (long)scs->sdata_len);
if(session) {
- int ret = wolfSSL_set_session(wss->handle, session);
+ int ret = wolfSSL_set_session(wss->ssl, session);
if(ret != WOLFSSL_SUCCESS) {
- Curl_ssl_delsessionid(data, psdata);
- infof(data, "previous session not accepted (%d), "
+ Curl_ssl_session_destroy(scs);
+ scs = NULL;
+ infof(data, "cached session not accepted (%d), "
"removing from cache", ret);
}
- else
- infof(data, "SSL reusing session ID");
+ else {
+ infof(data, "SSL reusing session with ALPN '%s'",
+ scs->alpn ? scs->alpn : "-");
+ if(ssl_config->earlydata &&
+ !cf->conn->connect_only &&
+ !strcmp("TLSv1.3", wolfSSL_get_version(wss->ssl))) {
+ bool do_early_data = FALSE;
+ if(sess_reuse_cb) {
+ result = sess_reuse_cb(cf, data, alpns, scs, &do_early_data);
+ if(result)
+ goto out;
+ }
+#ifdef WOLFSSL_EARLY_DATA
+ if(do_early_data) {
+ unsigned int edmax = (scs->earlydata_max < UINT_MAX) ?
+ (unsigned int)scs->earlydata_max : UINT_MAX;
+ /* We only try the ALPN protocol the session used before,
+ * otherwise we might send early data for the wrong protocol */
+ Curl_alpn_restrict_to(alpns, scs->alpn);
+ wolfSSL_set_max_early_data(wss->ssl, edmax);
+ }
+#else
+ /* Should never enable when not supported */
+ DEBUGASSERT(!do_early_data);
+#endif
+ }
+ }
wolfSSL_SESSION_free(session);
}
else {
failf(data, "could not decode previous session");
}
}
- Curl_ssl_sessionid_unlock(data);
+out:
+ Curl_ssl_scache_return(cf, data, ssl_peer_key, scs);
return result;
}
-static CURLcode populate_x509_store(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- WOLFSSL_X509_STORE *store,
- struct wolfssl_ctx *wssl)
+static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ WOLFSSL_X509_STORE *store,
+ struct wssl_ctx *wssl)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
@@ -518,57 +615,61 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
const char * const ssl_capath = conn_config->CApath;
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
bool imported_native_ca = FALSE;
+ bool imported_ca_info_blob = FALSE;
+
+ /* We do not want to do this again, no matter the outcome */
+ wssl->x509_store_setup = TRUE;
-#if !defined(NO_FILESYSTEM) && defined(WOLFSSL_SYS_CA_CERTS)
+#ifndef NO_FILESYSTEM
/* load native CA certificates */
if(ssl_config->native_ca_store) {
- if(wolfSSL_CTX_load_system_CA_certs(wssl->ctx) != WOLFSSL_SUCCESS) {
+#ifdef WOLFSSL_SYS_CA_CERTS
+ if(wolfSSL_CTX_load_system_CA_certs(wssl->ssl_ctx) != WOLFSSL_SUCCESS) {
infof(data, "error importing native CA store, continuing anyway");
}
else {
imported_native_ca = TRUE;
infof(data, "successfully imported native CA store");
- wssl->x509_store_setup = TRUE;
}
+#else
+ infof(data, "ignoring native CA option because wolfSSL was built without "
+ "native CA support");
+#endif
}
#endif /* !NO_FILESYSTEM */
/* load certificate blob */
if(ca_info_blob) {
- if(wolfSSL_CTX_load_verify_buffer(wssl->ctx, ca_info_blob->data,
+ if(wolfSSL_CTX_load_verify_buffer(wssl->ssl_ctx, ca_info_blob->data,
(long)ca_info_blob->len,
WOLFSSL_FILETYPE_PEM) !=
WOLFSSL_SUCCESS) {
- if(imported_native_ca) {
- infof(data, "error importing CA certificate blob, continuing anyway");
- }
- else {
- failf(data, "error importing CA certificate blob");
- return CURLE_SSL_CACERT_BADFILE;
- }
+ failf(data, "error importing CA certificate blob");
+ return CURLE_SSL_CACERT_BADFILE;
}
else {
+ imported_ca_info_blob = TRUE;
infof(data, "successfully imported CA certificate blob");
- wssl->x509_store_setup = TRUE;
}
}
#ifndef NO_FILESYSTEM
/* load trusted cacert from file if not blob */
- CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d",
+ CURL_TRC_CF(data, cf, "wssl_populate_x509_store, path=%s, blob=%d",
ssl_cafile ? ssl_cafile : "none", !!ca_info_blob);
if(!store)
return CURLE_OUT_OF_MEMORY;
- if((ssl_cafile || ssl_capath) && (!wssl->x509_store_setup)) {
+ if(ssl_cafile || ssl_capath) {
int rc =
- wolfSSL_CTX_load_verify_locations_ex(wssl->ctx,
+ wolfSSL_CTX_load_verify_locations_ex(wssl->ssl_ctx,
ssl_cafile,
ssl_capath,
WOLFSSL_LOAD_FLAG_IGNORE_ERR);
if(WOLFSSL_SUCCESS != rc) {
- if(conn_config->verifypeer) {
+ if(conn_config->verifypeer &&
+ !imported_native_ca && !imported_ca_info_blob) {
/* Fail if we insist on successfully verifying the server. */
failf(data, "error setting certificate verify locations:"
" CAfile: %s CApath: %s",
@@ -592,7 +693,6 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
}
#endif
(void)store;
- wssl->x509_store_setup = TRUE;
return CURLE_OK;
}
@@ -620,12 +720,12 @@ static void wssl_x509_share_free(void *key, size_t key_len, void *p)
}
static bool
-cached_x509_store_expired(const struct Curl_easy *data,
- const struct wssl_x509_share *mb)
+wssl_cached_x509_store_expired(const struct Curl_easy *data,
+ const struct wssl_x509_share *mb)
{
const struct ssl_general_config *cfg = &data->set.general_ssl;
- struct curltime now = Curl_now();
- timediff_t elapsed_ms = Curl_timediff(now, mb->time);
+ struct curltime now = curlx_now();
+ timediff_t elapsed_ms = curlx_timediff(now, mb->time);
timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
if(timeout_ms < 0)
@@ -635,8 +735,8 @@ cached_x509_store_expired(const struct Curl_easy *data,
}
static bool
-cached_x509_store_different(struct Curl_cfilter *cf,
- const struct wssl_x509_share *mb)
+wssl_cached_x509_store_different(struct Curl_cfilter *cf,
+ const struct wssl_x509_share *mb)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
if(!mb->CAfile || !conn_config->CAfile)
@@ -645,8 +745,8 @@ cached_x509_store_different(struct Curl_cfilter *cf,
return strcmp(mb->CAfile, conn_config->CAfile);
}
-static WOLFSSL_X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf,
- const struct Curl_easy *data)
+static WOLFSSL_X509_STORE *wssl_get_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
{
struct Curl_multi *multi = data->multi;
struct wssl_x509_share *share;
@@ -654,20 +754,20 @@ static WOLFSSL_X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf,
DEBUGASSERT(multi);
share = multi ? Curl_hash_pick(&multi->proto_hash,
- (void *)MPROTO_WSSL_X509_KEY,
+ CURL_UNCONST(MPROTO_WSSL_X509_KEY),
sizeof(MPROTO_WSSL_X509_KEY)-1) : NULL;
if(share && share->store &&
- !cached_x509_store_expired(data, share) &&
- !cached_x509_store_different(cf, share)) {
+ !wssl_cached_x509_store_expired(data, share) &&
+ !wssl_cached_x509_store_different(cf, share)) {
store = share->store;
}
return store;
}
-static void set_cached_x509_store(struct Curl_cfilter *cf,
- const struct Curl_easy *data,
- WOLFSSL_X509_STORE *store)
+static void wssl_set_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data,
+ WOLFSSL_X509_STORE *store)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct Curl_multi *multi = data->multi;
@@ -677,7 +777,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
if(!multi)
return;
share = Curl_hash_pick(&multi->proto_hash,
- (void *)MPROTO_WSSL_X509_KEY,
+ CURL_UNCONST(MPROTO_WSSL_X509_KEY),
sizeof(MPROTO_WSSL_X509_KEY)-1);
if(!share) {
@@ -685,7 +785,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
if(!share)
return;
if(!Curl_hash_add2(&multi->proto_hash,
- (void *)MPROTO_WSSL_X509_KEY,
+ CURL_UNCONST(MPROTO_WSSL_X509_KEY),
sizeof(MPROTO_WSSL_X509_KEY)-1,
share, wssl_x509_share_free)) {
free(share);
@@ -709,7 +809,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
free(share->CAfile);
}
- share->time = Curl_now();
+ share->time = curlx_now();
share->store = store;
share->CAfile = CAfile;
}
@@ -717,7 +817,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
struct Curl_easy *data,
- struct wolfssl_ctx *wssl)
+ struct wssl_ctx *wssl)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
@@ -735,12 +835,14 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
!ssl_config->primary.CRLfile &&
!ssl_config->native_ca_store;
- cached_store = cache_criteria_met ? get_cached_x509_store(cf, data) : NULL;
- if(cached_store && wolfSSL_CTX_get_cert_store(wssl->ctx) == cached_store) {
+ cached_store = cache_criteria_met ? wssl_get_cached_x509_store(cf, data)
+ : NULL;
+ if(cached_store &&
+ wolfSSL_CTX_get_cert_store(wssl->ssl_ctx) == cached_store) {
/* The cached store is already in use, do nothing. */
}
else if(cached_store && wolfSSL_X509_STORE_up_ref(cached_store)) {
- wolfSSL_CTX_set_cert_store(wssl->ctx, cached_store);
+ wolfSSL_CTX_set_cert_store(wssl->ssl_ctx, cached_store);
}
else if(cache_criteria_met) {
/* wolfSSL's initial store in CTX is not shareable by default.
@@ -750,17 +852,17 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
failf(data, "SSL: could not create a X509 store");
return CURLE_OUT_OF_MEMORY;
}
- wolfSSL_CTX_set_cert_store(wssl->ctx, store);
+ wolfSSL_CTX_set_cert_store(wssl->ssl_ctx, store);
- result = populate_x509_store(cf, data, store, wssl);
+ result = wssl_populate_x509_store(cf, data, store, wssl);
if(!result) {
- set_cached_x509_store(cf, data, store);
+ wssl_set_cached_x509_store(cf, data, store);
}
}
else {
/* We never share the CTX's store, use it. */
- WOLFSSL_X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx);
- result = populate_x509_store(cf, data, store, wssl);
+ WOLFSSL_X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ssl_ctx);
+ result = wssl_populate_x509_store(cf, data, store, wssl);
}
return result;
@@ -773,20 +875,20 @@ wssl_add_default_ciphers(bool tls13, struct dynbuf *buf)
int i;
char *str;
- for(i = 0; (str = wolfSSL_get_cipher_list(i)); i++) {
+ for(i = 0; (str = wolfSSL_get_cipher_list(i)) != NULL; i++) {
size_t n;
if((strncmp(str, "TLS13", 5) == 0) != tls13)
continue;
/* if there already is data in the string, add colon separator */
- if(Curl_dyn_len(buf)) {
- CURLcode result = Curl_dyn_addn(buf, ":", 1);
+ if(curlx_dyn_len(buf)) {
+ CURLcode result = curlx_dyn_addn(buf, ":", 1);
if(result)
return result;
}
n = strlen(str);
- if(Curl_dyn_addn(buf, str, n))
+ if(curlx_dyn_addn(buf, str, n))
return CURLE_OUT_OF_MEMORY;
}
@@ -834,30 +936,41 @@ wssl_legacy_CTX_set_max_proto_version(WOLFSSL_CTX* ctx, int version)
#define wolfSSL_CTX_set_max_proto_version wssl_legacy_CTX_set_max_proto_version
#endif
-/*
- * This function loads all the client/CA certificates and CRLs. Setup the TLS
- * layer and do all necessary magic.
- */
-static CURLcode
-wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+#define QUIC_CIPHERS \
+ "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
+ "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
+#define QUIC_GROUPS "P-256:P-384:P-521"
+
+CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer,
+ const struct alpn_spec *alpns_requested,
+ Curl_wssl_ctx_setup_cb *cb_setup,
+ void *cb_user_data,
+ void *ssl_user_data,
+ Curl_wssl_init_session_reuse_cb *sess_reuse_cb)
{
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct ssl_primary_config *conn_config;
+ WOLFSSL_METHOD* req_method = NULL;
+ struct alpn_spec alpns;
int res;
char *curves;
- struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
- const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- WOLFSSL_METHOD* req_method = NULL;
#ifdef WOLFSSL_HAVE_KYBER
word16 pqkem = 0;
size_t idx = 0;
#endif
+ CURLcode result = CURLE_FAILED_INIT;
- DEBUGASSERT(backend);
-
- if(connssl->state == ssl_connection_complete)
- return CURLE_OK;
+ DEBUGASSERT(!wctx->ssl_ctx);
+ DEBUGASSERT(!wctx->ssl);
+ conn_config = Curl_ssl_cf_get_primary_config(cf);
+ if(!conn_config) {
+ result = CURLE_FAILED_INIT;
+ goto out;
+ }
+ Curl_alpn_copy(&alpns, alpns_requested);
#if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */
req_method = wolfSSLv23_client_method();
@@ -866,58 +979,62 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
#endif
if(!req_method) {
failf(data, "wolfSSL: could not create a client method");
- return CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
}
- if(backend->ctx)
- wolfSSL_CTX_free(backend->ctx);
+ if(wctx->ssl_ctx)
+ wolfSSL_CTX_free(wctx->ssl_ctx);
- backend->ctx = wolfSSL_CTX_new(req_method);
- if(!backend->ctx) {
+ wctx->ssl_ctx = wolfSSL_CTX_new(req_method);
+ if(!wctx->ssl_ctx) {
failf(data, "wolfSSL: could not create a context");
- return CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
}
switch(conn_config->version) {
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
case CURL_SSLVERSION_TLSv1_0:
- res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_VERSION);
+ res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_VERSION);
break;
case CURL_SSLVERSION_TLSv1_1:
- res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_1_VERSION);
+ res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_1_VERSION);
break;
case CURL_SSLVERSION_TLSv1_2:
- res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_2_VERSION);
+ res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_2_VERSION);
break;
#ifdef WOLFSSL_TLS13
case CURL_SSLVERSION_TLSv1_3:
- res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_3_VERSION);
+ res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_3_VERSION);
break;
#endif
default:
failf(data, "wolfSSL: unsupported minimum TLS version value");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
if(res != WOLFSSL_SUCCESS) {
failf(data, "wolfSSL: failed set the minimum TLS version");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
switch(conn_config->version_max) {
#ifdef WOLFSSL_TLS13
case CURL_SSLVERSION_MAX_TLSv1_3:
- res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION);
+ res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_3_VERSION);
break;
#endif
case CURL_SSLVERSION_MAX_TLSv1_2:
- res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_2_VERSION);
+ res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_2_VERSION);
break;
case CURL_SSLVERSION_MAX_TLSv1_1:
- res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_1_VERSION);
+ res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_1_VERSION);
break;
case CURL_SSLVERSION_MAX_TLSv1_0:
- res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_VERSION);
+ res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_VERSION);
break;
case CURL_SSLVERSION_MAX_DEFAULT:
case CURL_SSLVERSION_MAX_NONE:
@@ -925,20 +1042,23 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
break;
default:
failf(data, "wolfSSL: unsupported maximum TLS version value");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
if(res != WOLFSSL_SUCCESS) {
failf(data, "wolfSSL: failed set the maximum TLS version");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
#ifndef WOLFSSL_TLS13
{
char *ciphers = conn_config->cipher_list;
if(ciphers) {
- if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) {
+ if(!SSL_CTX_set_cipher_list(wctx->ssl_ctx, ciphers)) {
failf(data, "failed setting cipher list: %s", ciphers);
- return CURLE_SSL_CIPHER;
+ result = CURLE_SSL_CIPHER;
+ goto out;
}
infof(data, "Cipher selection: %s", ciphers);
}
@@ -949,40 +1069,42 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
const char *ciphers12 = conn_config->cipher_list;
const char *ciphers13 = conn_config->cipher_list13;
struct dynbuf c;
- CURLcode result;
- Curl_dyn_init(&c, MAX_CIPHER_LEN);
+ curlx_dyn_init(&c, MAX_CIPHER_LEN);
if(ciphers13)
- result = Curl_dyn_add(&c, ciphers13);
+ result = curlx_dyn_add(&c, ciphers13);
else
result = wssl_add_default_ciphers(TRUE, &c);
if(!result) {
if(ciphers12) {
- if(Curl_dyn_len(&c))
- result = Curl_dyn_addn(&c, ":", 1);
+ if(curlx_dyn_len(&c))
+ result = curlx_dyn_addn(&c, ":", 1);
if(!result)
- result = Curl_dyn_add(&c, ciphers12);
+ result = curlx_dyn_add(&c, ciphers12);
}
else
result = wssl_add_default_ciphers(FALSE, &c);
}
if(result)
- return result;
+ goto out;
- if(!wolfSSL_CTX_set_cipher_list(backend->ctx, Curl_dyn_ptr(&c))) {
- failf(data, "failed setting cipher list: %s", Curl_dyn_ptr(&c));
- Curl_dyn_free(&c);
- return CURLE_SSL_CIPHER;
+ if(!wolfSSL_CTX_set_cipher_list(wctx->ssl_ctx, curlx_dyn_ptr(&c))) {
+ failf(data, "failed setting cipher list: %s", curlx_dyn_ptr(&c));
+ curlx_dyn_free(&c);
+ result = CURLE_SSL_CIPHER;
+ goto out;
}
- infof(data, "Cipher selection: %s", Curl_dyn_ptr(&c));
- Curl_dyn_free(&c);
+ infof(data, "Cipher selection: %s", curlx_dyn_ptr(&c));
+ curlx_dyn_free(&c);
}
#endif
curves = conn_config->curves;
- if(curves) {
+ if(!curves && cf->conn->transport == TRNSPRT_QUIC)
+ curves = (char *)CURL_UNCONST(QUIC_GROUPS);
+ if(curves) {
#ifdef WOLFSSL_HAVE_KYBER
for(idx = 0; gnm[idx].name != NULL; idx++) {
if(strncmp(curves, gnm[idx].name, strlen(gnm[idx].name)) == 0) {
@@ -994,9 +1116,10 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
if(pqkem == 0)
#endif
{
- if(!wolfSSL_CTX_set1_curves_list(backend->ctx, curves)) {
+ if(!wolfSSL_CTX_set1_curves_list(wctx->ssl_ctx, curves)) {
failf(data, "failed setting curves list: '%s'", curves);
- return CURLE_SSL_CIPHER;
+ result = CURLE_SSL_CIPHER;
+ goto out;
}
}
}
@@ -1008,30 +1131,32 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
const char *key_file = ssl_config->key;
const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
const struct curl_blob *key_blob = ssl_config->key_blob;
- int file_type = wolfssl_do_file_type(ssl_config->cert_type);
+ int file_type = wssl_do_file_type(ssl_config->cert_type);
int rc;
switch(file_type) {
case WOLFSSL_FILETYPE_PEM:
rc = cert_blob ?
- wolfSSL_CTX_use_certificate_chain_buffer(backend->ctx,
+ wolfSSL_CTX_use_certificate_chain_buffer(wctx->ssl_ctx,
cert_blob->data,
(long)cert_blob->len) :
- wolfSSL_CTX_use_certificate_chain_file(backend->ctx, cert_file);
+ wolfSSL_CTX_use_certificate_chain_file(wctx->ssl_ctx, cert_file);
break;
case WOLFSSL_FILETYPE_ASN1:
rc = cert_blob ?
- wolfSSL_CTX_use_certificate_buffer(backend->ctx, cert_blob->data,
+ wolfSSL_CTX_use_certificate_buffer(wctx->ssl_ctx, cert_blob->data,
(long)cert_blob->len, file_type) :
- wolfSSL_CTX_use_certificate_file(backend->ctx, cert_file, file_type);
+ wolfSSL_CTX_use_certificate_file(wctx->ssl_ctx, cert_file, file_type);
break;
default:
failf(data, "unknown cert type");
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
}
if(rc != 1) {
failf(data, "unable to use client certificate");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
if(!key_blob && !key_file) {
@@ -1039,53 +1164,57 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
key_file = cert_file;
}
else
- file_type = wolfssl_do_file_type(ssl_config->key_type);
+ file_type = wssl_do_file_type(ssl_config->key_type);
rc = key_blob ?
- wolfSSL_CTX_use_PrivateKey_buffer(backend->ctx, key_blob->data,
+ wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data,
(long)key_blob->len, file_type) :
- wolfSSL_CTX_use_PrivateKey_file(backend->ctx, key_file, file_type);
+ wolfSSL_CTX_use_PrivateKey_file(wctx->ssl_ctx, key_file, file_type);
if(rc != 1) {
failf(data, "unable to set private key");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
}
#else /* NO_FILESYSTEM */
if(ssl_config->primary.cert_blob) {
const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
const struct curl_blob *key_blob = ssl_config->key_blob;
- int file_type = wolfssl_do_file_type(ssl_config->cert_type);
+ int file_type = wssl_do_file_type(ssl_config->cert_type);
int rc;
switch(file_type) {
case WOLFSSL_FILETYPE_PEM:
- rc = wolfSSL_CTX_use_certificate_chain_buffer(backend->ctx,
+ rc = wolfSSL_CTX_use_certificate_chain_buffer(wctx->ssl_ctx,
cert_blob->data,
(long)cert_blob->len);
break;
case WOLFSSL_FILETYPE_ASN1:
- rc = wolfSSL_CTX_use_certificate_buffer(backend->ctx, cert_blob->data,
+ rc = wolfSSL_CTX_use_certificate_buffer(wctx->ssl_ctx, cert_blob->data,
(long)cert_blob->len, file_type);
break;
default:
failf(data, "unknown cert type");
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
}
if(rc != 1) {
failf(data, "unable to use client certificate");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
if(!key_blob)
key_blob = cert_blob;
else
- file_type = wolfssl_do_file_type(ssl_config->key_type);
+ file_type = wssl_do_file_type(ssl_config->key_type);
- if(wolfSSL_CTX_use_PrivateKey_buffer(backend->ctx, key_blob->data,
+ if(wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data,
(long)key_blob->len,
file_type) != 1) {
failf(data, "unable to set private key");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
}
#endif /* !NO_FILESYSTEM */
@@ -1094,37 +1223,49 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
* fail to connect if the verification fails, or if it should continue
* anyway. In the latter case the result of the verification is checked with
* SSL_get_verify_result() below. */
- wolfSSL_CTX_set_verify(backend->ctx,
+ wolfSSL_CTX_set_verify(wctx->ssl_ctx,
conn_config->verifypeer ? WOLFSSL_VERIFY_PEER :
WOLFSSL_VERIFY_NONE, NULL);
#ifdef HAVE_SNI
- if(connssl->peer.sni) {
- size_t sni_len = strlen(connssl->peer.sni);
+ if(peer->sni) {
+ size_t sni_len = strlen(peer->sni);
if((sni_len < USHRT_MAX)) {
- if(wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME,
- connssl->peer.sni,
- (unsigned short)sni_len) != 1) {
+ if(wolfSSL_CTX_UseSNI(wctx->ssl_ctx, WOLFSSL_SNI_HOST_NAME,
+ peer->sni, (unsigned short)sni_len) != 1) {
failf(data, "Failed to set SNI");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
+ CURL_TRC_CF(data, cf, "set SNI '%s'", peer->sni);
}
}
#endif
+ if(ssl_config->primary.cache_session &&
+ cf->conn->transport != TRNSPRT_QUIC) {
+ /* Register to get notified when a new session is received */
+ wolfSSL_CTX_sess_set_new_cb(wctx->ssl_ctx, wssl_vtls_new_session_cb);
+ }
+
+ if(cb_setup) {
+ result = cb_setup(cf, data, cb_user_data);
+ if(result)
+ goto out;
+ }
+
/* give application a chance to interfere with SSL set up. */
if(data->set.ssl.fsslctx) {
- CURLcode result;
- if(!backend->x509_store_setup) {
- result = Curl_wssl_setup_x509_store(cf, data, backend);
+ if(!wctx->x509_store_setup) {
+ result = Curl_wssl_setup_x509_store(cf, data, wctx);
if(result)
- return result;
+ goto out;
}
- result = (*data->set.ssl.fsslctx)(data, backend->ctx,
+ result = (*data->set.ssl.fsslctx)(data, wctx->ssl_ctx,
data->set.ssl.fsslctxp);
if(result) {
failf(data, "error signaled by ssl ctx callback");
- return result;
+ goto out;
}
}
#ifdef NO_FILESYSTEM
@@ -1133,82 +1274,87 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
" with \"no filesystem\". Either disable peer verification"
" (insecure) or if you are building an application with libcurl you"
" can load certificates via CURLOPT_SSL_CTX_FUNCTION.");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
#endif
/* Let's make an SSL structure */
- if(backend->handle)
- wolfSSL_free(backend->handle);
- backend->handle = wolfSSL_new(backend->ctx);
- if(!backend->handle) {
+ wctx->ssl = wolfSSL_new(wctx->ssl_ctx);
+ if(!wctx->ssl) {
failf(data, "SSL: could not create a handle");
- return CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
}
+ wolfSSL_set_app_data(wctx->ssl, ssl_user_data);
+#ifdef WOLFSSL_QUIC
+ if(cf->conn->transport == TRNSPRT_QUIC)
+ wolfSSL_set_quic_use_legacy_codepoint(wctx->ssl, 0);
+#endif
+
#ifdef WOLFSSL_HAVE_KYBER
if(pqkem) {
- if(wolfSSL_UseKeyShare(backend->handle, pqkem) != WOLFSSL_SUCCESS) {
+ if(wolfSSL_UseKeyShare(wctx->ssl, pqkem) != WOLFSSL_SUCCESS) {
failf(data, "unable to use PQ KEM");
}
}
#endif
+ /* Check if there is a cached ID we can/should use here! */
+ if(ssl_config->primary.cache_session) {
+ /* Set session from cache if there is one */
+ (void)wssl_setup_session(cf, data, wctx, &alpns,
+ peer->scache_key, sess_reuse_cb);
+ }
+
#ifdef HAVE_ALPN
- if(connssl->alpn) {
+ if(alpns.count) {
struct alpn_proto_buf proto;
- CURLcode result;
+ memset(&proto, 0, sizeof(proto));
+ Curl_alpn_to_proto_str(&proto, &alpns);
- result = Curl_alpn_to_proto_str(&proto, connssl->alpn);
- if(result ||
- wolfSSL_UseALPN(backend->handle,
- (char *)proto.data, (unsigned int)proto.len,
+ if(wolfSSL_UseALPN(wctx->ssl, (char *)proto.data,
+ (unsigned int)proto.len,
WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != WOLFSSL_SUCCESS) {
failf(data, "SSL: failed setting ALPN protocols");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+ CURL_TRC_CF(data, cf, "set ALPN: %s", proto.data);
}
#endif /* HAVE_ALPN */
#ifdef OPENSSL_EXTRA
if(Curl_tls_keylog_enabled()) {
/* Ensure the Client Random is preserved. */
- wolfSSL_KeepArrays(backend->handle);
+ wolfSSL_KeepArrays(wctx->ssl);
#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13)
- wolfSSL_set_tls13_secret_cb(backend->handle,
- wolfssl_tls13_secret_callback, NULL);
+ wolfSSL_set_tls13_secret_cb(wctx->ssl,
+ wssl_tls13_secret_callback, NULL);
#endif
}
#endif /* OPENSSL_EXTRA */
#ifdef HAVE_SECURE_RENEGOTIATION
- if(wolfSSL_UseSecureRenegotiation(backend->handle) != SSL_SUCCESS) {
+ if(wolfSSL_UseSecureRenegotiation(wctx->ssl) != SSL_SUCCESS) {
failf(data, "SSL: failed setting secure renegotiation");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
#endif /* HAVE_SECURE_RENEGOTIATION */
- /* Check if there is a cached ID we can/should use here! */
- if(ssl_config->primary.cache_session) {
- /* Set session from cache if there is one */
- (void)wssl_setup_session(cf, data, backend, &connssl->peer);
- /* Register to get notified when a new session is received */
- wolfSSL_set_app_data(backend->handle, cf);
- wolfSSL_CTX_sess_set_new_cb(backend->ctx, wssl_vtls_new_session_cb);
- }
-
-#ifdef USE_ECH
+#ifdef USE_ECH_WOLFSSL
if(ECH_ENABLED(data)) {
int trying_ech_now = 0;
if(data->set.str[STRING_ECH_PUBLIC]) {
infof(data, "ECH: outername not (yet) supported with wolfSSL");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
if(data->set.tls_ech == CURLECH_GREASE) {
- infof(data, "ECH: GREASE'd ECH not yet supported for wolfSSL");
- return CURLE_SSL_CONNECT_ERROR;
+ infof(data, "ECH: GREASE is done by default by wolfSSL: no need to ask");
}
if(data->set.tls_ech & CURLECH_CLA_CFG
&& data->set.str[STRING_ECH_CONFIG]) {
@@ -1217,10 +1363,12 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
b64len = (word32) strlen(b64val);
if(b64len
- && wolfSSL_SetEchConfigsBase64(backend->handle, b64val, b64len)
+ && wolfSSL_SetEchConfigsBase64(wctx->ssl, b64val, b64len)
!= WOLFSSL_SUCCESS) {
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
+ if(data->set.tls_ech & CURLECH_HARD) {
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
+ }
}
else {
trying_ech_now = 1;
@@ -1228,13 +1376,17 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
}
}
else {
+ struct ssl_connect_data *connssl = cf->ctx;
struct Curl_dns_entry *dns = NULL;
- dns = Curl_fetch_addr(data, connssl->peer.hostname, connssl->peer.port);
+ dns = Curl_dnscache_get(data, connssl->peer.hostname, connssl->peer.port,
+ cf->conn->ip_version);
if(!dns) {
infof(data, "ECH: requested but no DNS info available");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
+ if(data->set.tls_ech & CURLECH_HARD) {
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
+ }
}
else {
struct Curl_https_rrinfo *rinfo = NULL;
@@ -1245,11 +1397,13 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
size_t elen = rinfo->echconfiglist_len;
infof(data, "ECH: ECHConfig from DoH HTTPS RR");
- if(wolfSSL_SetEchConfigs(backend->handle, ecl, (word32) elen) !=
+ if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32) elen) !=
WOLFSSL_SUCCESS) {
infof(data, "ECH: wolfSSL_SetEchConfigs failed");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
+ if(data->set.tls_ech & CURLECH_HARD) {
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
+ }
}
else {
trying_ech_now = 1;
@@ -1258,49 +1412,107 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
}
else {
infof(data, "ECH: requested but no ECHConfig available");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
+ if(data->set.tls_ech & CURLECH_HARD) {
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
+ }
}
Curl_resolv_unlink(data, &dns);
}
}
- if(trying_ech_now
- && SSL_set_min_proto_version(backend->handle, TLS1_3_VERSION) != 1) {
+ if(trying_ech_now && wolfSSL_set_min_proto_version(wctx->ssl,
+ TLS1_3_VERSION) != 1) {
infof(data, "ECH: cannot force TLSv1.3 [ERROR]");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
}
-#endif /* USE_ECH */
+#endif /* USE_ECH_WOLFSSL */
+
+ result = CURLE_OK;
+
+out:
+ if(result && wctx->ssl) {
+ wolfSSL_free(wctx->ssl);
+ wctx->ssl = NULL;
+ }
+ if(result && wctx->ssl_ctx) {
+ wolfSSL_CTX_free(wctx->ssl_ctx);
+ wctx->ssl_ctx = NULL;
+ }
+ return result;
+}
+
+/*
+ * This function loads all the client/CA certificates and CRLs. Setup the TLS
+ * layer and do all necessary magic.
+ */
+static CURLcode
+wssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ CURLcode result;
+
+ DEBUGASSERT(wssl);
+
+ if(connssl->state == ssl_connection_complete)
+ return CURLE_OK;
+
+ result = Curl_wssl_ctx_init(wssl, cf, data, &connssl->peer,
+ connssl->alpn, NULL, NULL, cf,
+ wssl_on_session_reuse);
+ if(result)
+ return result;
+
+#ifdef HAVE_ALPN
+ if(connssl->alpn && (connssl->state != ssl_connection_deferred)) {
+ struct alpn_proto_buf proto;
+ memset(&proto, 0, sizeof(proto));
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+ }
+#endif
+
+ /* Enable RFC2818 checks */
+ if(conn_config->verifyhost) {
+ char *snihost = connssl->peer.sni ?
+ connssl->peer.sni : connssl->peer.hostname;
+ if(wolfSSL_check_domain_name(wssl->ssl, snihost) !=
+ WOLFSSL_SUCCESS) {
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
#ifdef USE_BIO_CHAIN
{
WOLFSSL_BIO *bio;
- bio = wolfSSL_BIO_new(wolfssl_bio_cf_method);
+ bio = wolfSSL_BIO_new(wssl_bio_cf_method);
if(!bio)
return CURLE_OUT_OF_MEMORY;
wolfSSL_BIO_set_data(bio, cf);
- wolfSSL_set_bio(backend->handle, bio, bio);
+ wolfSSL_set_bio(wssl->ssl, bio, bio);
}
#else /* USE_BIO_CHAIN */
/* pass the raw socket into the SSL layer */
- if(!wolfSSL_set_fd(backend->handle,
+ if(!wolfSSL_set_fd(wssl->ssl,
(int)Curl_conn_cf_get_socket(cf, data))) {
- failf(data, "SSL: SSL_set_fd failed");
+ failf(data, "SSL: wolfSSL_set_fd failed");
return CURLE_SSL_CONNECT_ERROR;
}
#endif /* !USE_BIO_CHAIN */
- connssl->connecting_state = ssl_connect_2;
return CURLE_OK;
}
-static char *wolfssl_strerror(unsigned long error, char *buf,
- unsigned long size)
+static char *wssl_strerror(unsigned long error, char *buf,
+ unsigned long size)
{
DEBUGASSERT(size > 40);
*buf = '\0';
@@ -1316,15 +1528,10 @@ static char *wolfssl_strerror(unsigned long error, char *buf,
return buf;
}
-
-static CURLcode
-wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
+CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct wssl_ctx *wssl)
{
- int ret = -1;
- struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
#ifndef CURL_DISABLE_PROXY
const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf) ?
data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
@@ -1333,29 +1540,143 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
const char * const pinnedpubkey = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
#endif
- DEBUGASSERT(backend);
+ if(pinnedpubkey) {
+#ifdef KEEP_PEER_CERT
+ WOLFSSL_X509 *x509;
+ const char *x509_der;
+ int x509_der_len;
+ struct Curl_X509certificate x509_parsed;
+ struct Curl_asn1Element *pubkey;
+ CURLcode result;
+
+ x509 = wolfSSL_get_peer_certificate(wssl->ssl);
+ if(!x509) {
+ failf(data, "SSL: failed retrieving server certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
- wolfSSL_ERR_clear_error();
+ x509_der = (const char *)wolfSSL_X509_get_der(x509, &x509_der_len);
+ if(!x509_der) {
+ failf(data, "SSL: failed retrieving ASN.1 server certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
- /* Enable RFC2818 checks */
- if(conn_config->verifyhost) {
- char *snihost = connssl->peer.sni ?
- connssl->peer.sni : connssl->peer.hostname;
- if(wolfSSL_check_domain_name(backend->handle, snihost) == WOLFSSL_FAILURE)
- return CURLE_SSL_CONNECT_ERROR;
+ memset(&x509_parsed, 0, sizeof(x509_parsed));
+ if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+ pubkey = &x509_parsed.subjectPublicKeyInfo;
+ if(!pubkey->header || pubkey->end <= pubkey->header) {
+ failf(data, "SSL: failed retrieving public key from server certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
+ result = Curl_pin_peer_pubkey(data,
+ pinnedpubkey,
+ (const unsigned char *)pubkey->header,
+ (size_t)(pubkey->end - pubkey->header));
+ wolfSSL_FreeX509(x509);
+ if(result) {
+ failf(data, "SSL: public key does not match pinned public key");
+ return result;
+ }
+#else
+ failf(data, "Library lacks pinning support built-in");
+ return CURLE_NOT_BUILT_IN;
+#endif
}
+ return CURLE_OK;
+}
- if(!backend->x509_store_setup) {
- /* After having send off the ClientHello, we prepare the x509
- * store to verify the coming certificate from the server */
- CURLcode result;
- result = Curl_wssl_setup_x509_store(cf, data, backend);
+#ifdef WOLFSSL_EARLY_DATA
+static CURLcode wssl_send_earlydata(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+ CURLcode result = CURLE_OK;
+ const unsigned char *buf;
+ size_t blen;
+
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sending);
+ wssl->io_result = CURLE_OK;
+ while(Curl_bufq_peek(&connssl->earlydata, &buf, &blen)) {
+ int nwritten = 0, rc;
+
+ wolfSSL_ERR_clear_error();
+ rc = wolfSSL_write_early_data(wssl->ssl, buf, (int)blen, &nwritten);
+ CURL_TRC_CF(data, cf, "wolfSSL_write_early_data(len=%zu) -> %d, %d",
+ blen, rc, nwritten);
+ if(rc < 0) {
+ int err = wolfSSL_get_error(wssl->ssl, rc);
+ switch(err) {
+ case WOLFSSL_ERROR_NONE: /* just did not get anything */
+ case WOLFSSL_ERROR_WANT_READ:
+ case WOLFSSL_ERROR_WANT_WRITE:
+ result = CURLE_AGAIN;
+ break;
+ default: {
+ char error_buffer[256];
+ int detail = wolfSSL_get_error(wssl->ssl, err);
+ CURL_TRC_CF(data, cf, "SSL send early data, error: '%s'(%d)",
+ wssl_strerror((unsigned long)err, error_buffer,
+ sizeof(error_buffer)),
+ detail);
+ result = CURLE_SEND_ERROR;
+ break;
+ }
+ }
+ goto out;
+ }
+
+ Curl_bufq_skip(&connssl->earlydata, (size_t)nwritten);
+ }
+ /* sent everything there was */
+ connssl->earlydata_state = ssl_earlydata_sent;
+ if(!Curl_ssl_cf_is_proxy(cf))
+ Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip);
+ infof(data, "SSL sending %zu bytes of early data", connssl->earlydata_skip);
+out:
+ return result;
+}
+#endif /* WOLFSSL_EARLY_DATA */
+
+static CURLcode wssl_handshake(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ int ret = -1, detail;
+ CURLcode result;
+
+ DEBUGASSERT(wssl);
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
+
+#ifdef WOLFSSL_EARLY_DATA
+ if(connssl->earlydata_state == ssl_earlydata_sending) {
+ result = wssl_send_earlydata(cf, data);
if(result)
return result;
}
+ DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) ||
+ (connssl->earlydata_state == ssl_earlydata_sent));
+#else
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_none);
+#endif /* WOLFSSL_EARLY_DATA */
- connssl->io_need = CURL_SSL_IO_NEED_NONE;
- ret = wolfSSL_connect(backend->handle);
+ wolfSSL_ERR_clear_error();
+ ret = wolfSSL_connect(wssl->ssl);
+
+ if(!wssl->x509_store_setup) {
+ /* After having send off the ClientHello, we prepare the x509
+ * store to verify the coming certificate from the server */
+ result = Curl_wssl_setup_x509_store(cf, data, wssl);
+ if(result) {
+ CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", result);
+ return result;
+ }
+ }
#ifdef OPENSSL_EXTRA
if(Curl_tls_keylog_enabled()) {
@@ -1371,67 +1692,49 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
* changes, the worst case is that no key is logged on error.
*/
if(ret == WOLFSSL_SUCCESS ||
- (!wolfSSL_want_read(backend->handle) &&
- !wolfSSL_want_write(backend->handle))) {
- wolfssl_log_tls12_secret(backend->handle);
+ (!wolfSSL_want_read(wssl->ssl) &&
+ !wolfSSL_want_write(wssl->ssl))) {
+ wssl_log_tls12_secret(wssl->ssl);
/* Client Random and master secrets are no longer needed, erase these.
* Ignored while the handshake is still in progress. */
- wolfSSL_FreeArrays(backend->handle);
+ wolfSSL_FreeArrays(wssl->ssl);
}
}
#endif /* OPENSSL_EXTRA */
- if(ret != 1) {
- int detail = wolfSSL_get_error(backend->handle, ret);
+ detail = wolfSSL_get_error(wssl->ssl, ret);
+ CURL_TRC_CF(data, cf, "wolfSSL_connect() -> %d, detail=%d", ret, detail);
+ if(ret == WOLFSSL_SUCCESS) {
+ return CURLE_OK;
+ }
+ else {
if(WOLFSSL_ERROR_WANT_READ == detail) {
connssl->io_need = CURL_SSL_IO_NEED_RECV;
- return CURLE_OK;
+ return CURLE_AGAIN;
}
else if(WOLFSSL_ERROR_WANT_WRITE == detail) {
connssl->io_need = CURL_SSL_IO_NEED_SEND;
- return CURLE_OK;
+ return CURLE_AGAIN;
}
- /* There is no easy way to override only the CN matching.
- * This will enable the override of both mismatching SubjectAltNames
- * as also mismatching CN fields */
else if(DOMAIN_NAME_MISMATCH == detail) {
-#if 1
+ /* There is no easy way to override only the CN matching.
+ * This will enable the override of both mismatching SubjectAltNames
+ * as also mismatching CN fields */
failf(data, " subject alt name(s) or common name do not match \"%s\"",
connssl->peer.dispname);
return CURLE_PEER_FAILED_VERIFICATION;
-#else
- /* When the wolfssl_check_domain_name() is used and you desire to
- * continue on a DOMAIN_NAME_MISMATCH, i.e. 'ssl_config.verifyhost
- * == 0', CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA
- * error. The only way to do this is currently to switch the
- * Wolfssl_check_domain_name() in and out based on the
- * 'ssl_config.verifyhost' value. */
- if(conn_config->verifyhost) {
- failf(data,
- " subject alt name(s) or common name do not match \"%s\"\n",
- connssl->dispname);
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- else {
- infof(data,
- " subject alt name(s) and/or common name do not match \"%s\"",
- connssl->dispname);
- return CURLE_OK;
- }
-#endif
}
else if(ASN_NO_SIGNER_E == detail) {
if(conn_config->verifypeer) {
failf(data, " CA signer not available for verification");
return CURLE_SSL_CACERT_BADFILE;
}
- else {
- /* Just continue with a warning if no strict certificate
- verification is required. */
- infof(data, "CA signer not available for verification, "
- "continuing anyway");
- }
+ /* Just continue with a warning if no strict certificate
+ verification is required. */
+ infof(data, "CA signer not available for verification, "
+ "continuing anyway");
+ return CURLE_OK;
}
else if(ASN_AFTER_DATE_E == detail) {
failf(data, "server verification failed: certificate has expired.");
@@ -1441,7 +1744,16 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
failf(data, "server verification failed: certificate not valid yet.");
return CURLE_PEER_FAILED_VERIFICATION;
}
-#ifdef USE_ECH
+ else if(wssl->io_result) {
+ switch(wssl->io_result) {
+ case CURLE_SEND_ERROR:
+ case CURLE_RECV_ERROR:
+ return CURLE_SSL_CONNECT_ERROR;
+ default:
+ return wssl->io_result;
+ }
+ }
+#ifdef USE_ECH_WOLFSSL
else if(-1 == detail) {
/* try access a retry_config ECHConfigList for tracing */
byte echConfigs[1000];
@@ -1449,7 +1761,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
int rv = 0;
/* this currently does not produce the retry_configs */
- rv = wolfSSL_GetEchConfigs(backend->handle, echConfigs,
+ rv = wolfSSL_GetEchConfigs(wssl->ssl, echConfigs,
&echConfigsLen);
if(rv != WOLFSSL_SUCCESS) {
infof(data, "Failed to get ECHConfigs");
@@ -1458,160 +1770,100 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
char *b64str = NULL;
size_t blen = 0;
- rv = Curl_base64_encode((const char *)echConfigs, echConfigsLen,
- &b64str, &blen);
- if(!rv && b64str)
+ result = curlx_base64_encode((const char *)echConfigs, echConfigsLen,
+ &b64str, &blen);
+ if(!result && b64str)
infof(data, "ECH: (not yet) retry_configs %s", b64str);
free(b64str);
}
+ return CURLE_SSL_CONNECT_ERROR;
}
#endif
- else if(backend->io_result == CURLE_AGAIN) {
- return CURLE_OK;
- }
else {
char error_buffer[256];
failf(data, "SSL_connect failed with error %d: %s", detail,
- wolfssl_strerror((unsigned long)detail, error_buffer,
- sizeof(error_buffer)));
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
-
- if(pinnedpubkey) {
-#ifdef KEEP_PEER_CERT
- WOLFSSL_X509 *x509;
- const char *x509_der;
- int x509_der_len;
- struct Curl_X509certificate x509_parsed;
- struct Curl_asn1Element *pubkey;
- CURLcode result;
-
- x509 = wolfSSL_get_peer_certificate(backend->handle);
- if(!x509) {
- failf(data, "SSL: failed retrieving server certificate");
- return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
- }
-
- x509_der = (const char *)wolfSSL_X509_get_der(x509, &x509_der_len);
- if(!x509_der) {
- failf(data, "SSL: failed retrieving ASN.1 server certificate");
- return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
- }
-
- memset(&x509_parsed, 0, sizeof(x509_parsed));
- if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
- return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
-
- pubkey = &x509_parsed.subjectPublicKeyInfo;
- if(!pubkey->header || pubkey->end <= pubkey->header) {
- failf(data, "SSL: failed retrieving public key from server certificate");
- return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
- }
-
- result = Curl_pin_peer_pubkey(data,
- pinnedpubkey,
- (const unsigned char *)pubkey->header,
- (size_t)(pubkey->end - pubkey->header));
- wolfSSL_FreeX509(x509);
- if(result) {
- failf(data, "SSL: public key does not match pinned public key");
- return result;
- }
-#else
- failf(data, "Library lacks pinning support built-in");
- return CURLE_NOT_BUILT_IN;
-#endif
- }
-
-#ifdef HAVE_ALPN
- if(connssl->alpn) {
- int rc;
- char *protocol = NULL;
- unsigned short protocol_len = 0;
-
- rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len);
-
- if(rc == WOLFSSL_SUCCESS) {
- Curl_alpn_set_negotiated(cf, data, connssl,
- (const unsigned char *)protocol, protocol_len);
- }
- else if(rc == WOLFSSL_ALPN_NOT_FOUND)
- Curl_alpn_set_negotiated(cf, data, connssl, NULL, 0);
- else {
- failf(data, "ALPN, failure getting protocol, error %d", rc);
+ wssl_strerror((unsigned long)detail, error_buffer,
+ sizeof(error_buffer)));
return CURLE_SSL_CONNECT_ERROR;
}
}
-#endif /* HAVE_ALPN */
-
- connssl->connecting_state = ssl_connect_3;
-#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010)
- infof(data, "SSL connection using %s / %s",
- wolfSSL_get_version(backend->handle),
- wolfSSL_get_cipher_name(backend->handle));
-#else
- infof(data, "SSL connected");
-#endif
-
- return CURLE_OK;
}
-static ssize_t wolfssl_send(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const void *mem,
- size_t len,
- CURLcode *curlcode)
+static ssize_t wssl_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *buf, size_t blen,
+ CURLcode *curlcode)
{
struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
- int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
- int rc;
-
- DEBUGASSERT(backend);
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+ size_t total_written = 0;
+ ssize_t nwritten = -1;
+ DEBUGASSERT(wssl);
wolfSSL_ERR_clear_error();
- rc = wolfSSL_write(backend->handle, mem, memlen);
- if(rc <= 0) {
- int err = wolfSSL_get_error(backend->handle, rc);
+ if(blen) {
+ int memlen = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen;
+ int rc;
- switch(err) {
- case WOLFSSL_ERROR_WANT_READ:
- case WOLFSSL_ERROR_WANT_WRITE:
- /* there is data pending, re-invoke SSL_write() */
- CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len);
- *curlcode = CURLE_AGAIN;
- return -1;
- default:
- if(backend->io_result == CURLE_AGAIN) {
- CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len);
+ rc = wolfSSL_write(wssl->ssl, buf, memlen);
+ if(rc <= 0) {
+ int err = wolfSSL_get_error(wssl->ssl, rc);
+
+ switch(err) {
+ case WOLFSSL_ERROR_WANT_READ:
+ case WOLFSSL_ERROR_WANT_WRITE:
+ /* there is data pending, re-invoke wolfSSL_write() */
+ if(total_written) {
+ *curlcode = CURLE_OK;
+ nwritten = total_written;
+ goto out;
+ }
*curlcode = CURLE_AGAIN;
- return -1;
- }
- CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d, %d", len, rc, err);
- {
- char error_buffer[256];
- failf(data, "SSL write: %s, errno %d",
- wolfssl_strerror((unsigned long)err, error_buffer,
- sizeof(error_buffer)),
- SOCKERRNO);
+ nwritten = -1;
+ goto out;
+
+ default:
+ if(wssl->io_result == CURLE_AGAIN) {
+ if(total_written) {
+ *curlcode = CURLE_OK;
+ nwritten = total_written;
+ goto out;
+ }
+ *curlcode = CURLE_AGAIN;
+ nwritten = -1;
+ goto out;
+ }
+ {
+ char error_buffer[256];
+ failf(data, "SSL write: %s, errno %d",
+ wssl_strerror((unsigned long)err, error_buffer,
+ sizeof(error_buffer)),
+ SOCKERRNO);
+ }
+ *curlcode = CURLE_SEND_ERROR;
+ nwritten = -1;
+ goto out;
}
- *curlcode = CURLE_SEND_ERROR;
- return -1;
}
+ else
+ total_written += rc;
}
- CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d", len, rc);
- return rc;
+
+ *curlcode = CURLE_OK;
+ nwritten = total_written;
+out:
+ CURL_TRC_CF(data, cf, "wssl_send(len=%zu) -> %" FMT_OFF_T ", %d",
+ blen, nwritten, *curlcode);
+ return nwritten;
}
-static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool send_shutdown, bool *done)
+static CURLcode wssl_shutdown(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool send_shutdown, bool *done)
{
struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *wctx = (struct wolfssl_ctx *)connssl->backend;
+ struct wssl_ctx *wctx = (struct wssl_ctx *)connssl->backend;
CURLcode result = CURLE_OK;
char buf[1024];
char error_buffer[256];
@@ -1620,7 +1872,7 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
int detail;
DEBUGASSERT(wctx);
- if(!wctx->handle || cf->shutdown) {
+ if(!wctx->ssl || cf->shutdown) {
*done = TRUE;
goto out;
}
@@ -1628,12 +1880,12 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
wctx->shutting_down = TRUE;
connssl->io_need = CURL_SSL_IO_NEED_NONE;
*done = FALSE;
- if(!(wolfSSL_get_shutdown(wctx->handle) & WOLFSSL_SENT_SHUTDOWN)) {
+ if(!(wolfSSL_get_shutdown(wctx->ssl) & WOLFSSL_SENT_SHUTDOWN)) {
/* We have not started the shutdown from our side yet. Check
* if the server already sent us one. */
wolfSSL_ERR_clear_error();
- nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf));
- err = wolfSSL_get_error(wctx->handle, nread);
+ nread = wolfSSL_read(wctx->ssl, buf, (int)sizeof(buf));
+ err = wolfSSL_get_error(wctx->ssl, nread);
CURL_TRC_CF(data, cf, "wolfSSL_read, nread=%d, err=%d", nread, err);
if(!nread && err == WOLFSSL_ERROR_ZERO_RETURN) {
bool input_pending;
@@ -1655,16 +1907,16 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
}
}
- /* SSL should now have started the shutdown from our side. Since it
+ /* wolfSSL should now have started the shutdown from our side. Since it
* was not complete, we are lacking the close notify from the server. */
if(send_shutdown) {
wolfSSL_ERR_clear_error();
- if(wolfSSL_shutdown(wctx->handle) == 1) {
+ if(wolfSSL_shutdown(wctx->ssl) == 1) {
CURL_TRC_CF(data, cf, "SSL shutdown finished");
*done = TRUE;
goto out;
}
- if(WOLFSSL_ERROR_WANT_WRITE == wolfSSL_get_error(wctx->handle, nread)) {
+ if(WOLFSSL_ERROR_WANT_WRITE == wolfSSL_get_error(wctx->ssl, nread)) {
CURL_TRC_CF(data, cf, "SSL shutdown still wants to send");
connssl->io_need = CURL_SSL_IO_NEED_SEND;
goto out;
@@ -1675,11 +1927,11 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
for(i = 0; i < 10; ++i) {
wolfSSL_ERR_clear_error();
- nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf));
+ nread = wolfSSL_read(wctx->ssl, buf, (int)sizeof(buf));
if(nread <= 0)
break;
}
- err = wolfSSL_get_error(wctx->handle, nread);
+ err = wolfSSL_get_error(wctx->ssl, nread);
switch(err) {
case WOLFSSL_ERROR_ZERO_RETURN: /* no more data */
CURL_TRC_CF(data, cf, "SSL shutdown received");
@@ -1687,7 +1939,7 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
break;
case WOLFSSL_ERROR_NONE: /* just did not get anything */
case WOLFSSL_ERROR_WANT_READ:
- /* SSL has send its notify and now wants to read the reply
+ /* wolfSSL has send its notify and now wants to read the reply
* from the server. We are not really interested in that. */
CURL_TRC_CF(data, cf, "SSL shutdown sent, want receive");
connssl->io_need = CURL_SSL_IO_NEED_RECV;
@@ -1697,10 +1949,10 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
connssl->io_need = CURL_SSL_IO_NEED_SEND;
break;
default:
- detail = wolfSSL_get_error(wctx->handle, err);
+ detail = wolfSSL_get_error(wctx->ssl, err);
CURL_TRC_CF(data, cf, "SSL shutdown, error: '%s'(%d)",
- wolfssl_strerror((unsigned long)err, error_buffer,
- sizeof(error_buffer)),
+ wssl_strerror((unsigned long)err, error_buffer,
+ sizeof(error_buffer)),
detail);
result = CURLE_RECV_ERROR;
break;
@@ -1711,92 +1963,90 @@ out:
return result;
}
-static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+static void wssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
(void) data;
- DEBUGASSERT(backend);
+ DEBUGASSERT(wssl);
- if(backend->handle) {
- wolfSSL_free(backend->handle);
- backend->handle = NULL;
+ if(wssl->ssl) {
+ wolfSSL_free(wssl->ssl);
+ wssl->ssl = NULL;
}
- if(backend->ctx) {
- wolfSSL_CTX_free(backend->ctx);
- backend->ctx = NULL;
+ if(wssl->ssl_ctx) {
+ wolfSSL_CTX_free(wssl->ssl_ctx);
+ wssl->ssl_ctx = NULL;
}
}
-static ssize_t wolfssl_recv(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- char *buf, size_t blen,
- CURLcode *curlcode)
+static ssize_t wssl_recv(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *buf, size_t blen,
+ CURLcode *curlcode)
{
struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
int buffsize = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen;
int nread;
- DEBUGASSERT(backend);
+ DEBUGASSERT(wssl);
wolfSSL_ERR_clear_error();
*curlcode = CURLE_OK;
- nread = wolfSSL_read(backend->handle, buf, buffsize);
+ nread = wolfSSL_read(wssl->ssl, buf, buffsize);
if(nread <= 0) {
- int err = wolfSSL_get_error(backend->handle, nread);
+ int err = wolfSSL_get_error(wssl->ssl, nread);
switch(err) {
case WOLFSSL_ERROR_ZERO_RETURN: /* no more data */
- CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen);
+ CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen);
*curlcode = CURLE_OK;
return 0;
case WOLFSSL_ERROR_NONE:
case WOLFSSL_ERROR_WANT_READ:
case WOLFSSL_ERROR_WANT_WRITE:
- if(!backend->io_result && connssl->peer_closed) {
- CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen);
+ if(!wssl->io_result && connssl->peer_closed) {
+ CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen);
*curlcode = CURLE_OK;
return 0;
}
/* there is data pending, re-invoke wolfSSL_read() */
- CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen);
+ CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen);
*curlcode = CURLE_AGAIN;
return -1;
default:
- if(backend->io_result == CURLE_AGAIN) {
- CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen);
+ if(wssl->io_result == CURLE_AGAIN) {
+ CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen);
*curlcode = CURLE_AGAIN;
return -1;
}
- else if(!backend->io_result && connssl->peer_closed) {
- CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen);
+ else if(!wssl->io_result && connssl->peer_closed) {
+ CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen);
*curlcode = CURLE_OK;
return 0;
}
else {
char error_buffer[256];
failf(data, "SSL read: %s, errno %d",
- wolfssl_strerror((unsigned long)err, error_buffer,
- sizeof(error_buffer)),
+ wssl_strerror((unsigned long)err, error_buffer,
+ sizeof(error_buffer)),
SOCKERRNO);
}
*curlcode = CURLE_RECV_ERROR;
return -1;
}
}
- CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> %d", blen, nread);
+
+ CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> %d", blen, nread);
return nread;
}
-
-static size_t wolfssl_version(char *buffer, size_t size)
+size_t Curl_wssl_version(char *buffer, size_t size)
{
#if LIBWOLFSSL_VERSION_HEX >= 0x03006000
return msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version());
@@ -1806,7 +2056,7 @@ static size_t wolfssl_version(char *buffer, size_t size)
}
-static int wolfssl_init(void)
+static int wssl_init(void)
{
int ret;
@@ -1814,14 +2064,14 @@ static int wolfssl_init(void)
Curl_tls_keylog_open();
#endif
ret = (wolfSSL_Init() == WOLFSSL_SUCCESS);
- wolfssl_bio_cf_init_methods();
+ wssl_bio_cf_init_methods();
return ret;
}
-static void wolfssl_cleanup(void)
+static void wssl_cleanup(void)
{
- wolfssl_bio_cf_free_methods();
+ wssl_bio_cf_free_methods();
wolfSSL_Cleanup();
#ifdef OPENSSL_EXTRA
Curl_tls_keylog_close();
@@ -1829,31 +2079,29 @@ static void wolfssl_cleanup(void)
}
-static bool wolfssl_data_pending(struct Curl_cfilter *cf,
- const struct Curl_easy *data)
+static bool wssl_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
{
struct ssl_connect_data *ctx = cf->ctx;
- struct wolfssl_ctx *backend;
+ struct wssl_ctx *wssl;
(void)data;
DEBUGASSERT(ctx && ctx->backend);
- backend = (struct wolfssl_ctx *)ctx->backend;
- if(backend->handle) /* SSL is in use */
- return wolfSSL_pending(backend->handle);
+ wssl = (struct wssl_ctx *)ctx->backend;
+ if(wssl->ssl) /* wolfSSL is in use */
+ return wolfSSL_pending(wssl->ssl);
else
return FALSE;
}
-static CURLcode
-wolfssl_connect_common(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool nonblocking,
- bool *done)
+static CURLcode wssl_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
{
- CURLcode result;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+ CURLcode result = CURLE_OK;
/* check if the connection has already been established */
if(ssl_connection_complete == connssl->state) {
@@ -1861,116 +2109,108 @@ wolfssl_connect_common(struct Curl_cfilter *cf,
return CURLE_OK;
}
- if(ssl_connect_1 == connssl->connecting_state) {
- /* Find out how much more time we are allowed */
- const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
+ *done = FALSE;
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
- result = wolfssl_connect_step1(cf, data);
+ if(ssl_connect_1 == connssl->connecting_state) {
+ result = wssl_connect_step1(cf, data);
if(result)
return result;
+ connssl->connecting_state = ssl_connect_2;
}
- while(ssl_connect_2 == connssl->connecting_state) {
-
- /* check allowed time left */
- const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+ if(ssl_connect_2 == connssl->connecting_state) {
+ if(connssl->earlydata_state == ssl_earlydata_await) {
+ /* We defer the handshake until request data arrives. */
+ DEBUGASSERT(connssl->state == ssl_connection_deferred);
+ goto out;
+ }
+ result = wssl_handshake(cf, data);
+ if(result == CURLE_AGAIN)
+ goto out;
+ wssl->hs_result = result;
+ connssl->connecting_state = ssl_connect_3;
+ }
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
+ if(ssl_connect_3 == connssl->connecting_state) {
+ /* Once the handshake has errored, it stays in that state and will
+ * error again on every call. */
+ if(wssl->hs_result) {
+ result = wssl->hs_result;
+ goto out;
}
+ result = Curl_wssl_verify_pinned(cf, data, wssl);
+ if(result) {
+ wssl->hs_result = result;
+ goto out;
+ }
+ /* handhshake was done without errors */
+#ifdef HAVE_ALPN
+ if(connssl->alpn) {
+ int rc;
+ char *protocol = NULL;
+ unsigned short protocol_len = 0;
+
+ rc = wolfSSL_ALPN_GetProtocol(wssl->ssl, &protocol, &protocol_len);
- /* if ssl is expecting something, check if it is available. */
- if(connssl->io_need) {
- curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
- sockfd : CURL_SOCKET_BAD;
- curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
- sockfd : CURL_SOCKET_BAD;
- int what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking ? 0 : timeout_ms);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
+ if(rc == WOLFSSL_SUCCESS) {
+ Curl_alpn_set_negotiated(cf, data, connssl,
+ (const unsigned char *)protocol,
+ protocol_len);
}
- else if(0 == what) {
- if(nonblocking) {
- *done = FALSE;
- return CURLE_OK;
- }
- else {
- /* timeout */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
+ else if(rc == WOLFSSL_ALPN_NOT_FOUND)
+ Curl_alpn_set_negotiated(cf, data, connssl, NULL, 0);
+ else {
+ failf(data, "ALPN, failure getting protocol, error %d", rc);
+ wssl->hs_result = result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
- /* socket is readable or writable */
}
+#endif /* HAVE_ALPN */
- /* Run transaction, and return to the caller if it failed or if
- * this connection is part of a multi handle and this loop would
- * execute again. This permits the owner of a multi handle to
- * abort a connection attempt before step2 has completed while
- * ensuring that a client using select() or epoll() will always
- * have a valid fdset to wait on.
- */
- result = wolfssl_connect_step2(cf, data);
- if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
- return result;
- } /* repeat step2 until all transactions are done. */
+#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010)
+ infof(data, "SSL connection using %s / %s",
+ wolfSSL_get_version(wssl->ssl),
+ wolfSSL_get_cipher_name(wssl->ssl));
+#else
+ infof(data, "SSL connected");
+#endif
- if(ssl_connect_3 == connssl->connecting_state) {
- /* In other backends, this is where we verify the certificate, but
- * wolfSSL already does that as part of the handshake. */
connssl->connecting_state = ssl_connect_done;
+ connssl->state = ssl_connection_complete;
+
+#ifdef WOLFSSL_EARLY_DATA
+ if(connssl->earlydata_state > ssl_earlydata_none) {
+ /* We should be in this state by now */
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sent);
+ connssl->earlydata_state =
+ (wolfSSL_get_early_data_status(wssl->ssl) ==
+ WOLFSSL_EARLY_DATA_REJECTED) ?
+ ssl_earlydata_rejected : ssl_earlydata_accepted;
+ }
+#endif /* WOLFSSL_EARLY_DATA */
}
- if(ssl_connect_done == connssl->connecting_state) {
- connssl->state = ssl_connection_complete;
+ if((connssl->connecting_state == ssl_connect_done) ||
+ (connssl->state == ssl_connection_deferred)) {
*done = TRUE;
}
- else
- *done = FALSE;
-
- /* Reset our connect state machine */
- connssl->connecting_state = ssl_connect_1;
-
- return CURLE_OK;
-}
-
-static CURLcode wolfssl_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
-{
- return wolfssl_connect_common(cf, data, TRUE, done);
-}
-
-
-static CURLcode wolfssl_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- CURLcode result;
- bool done = FALSE;
-
- result = wolfssl_connect_common(cf, data, FALSE, &done);
- if(result)
- return result;
-
- DEBUGASSERT(done);
-
- return CURLE_OK;
+out:
+ if(result) {
+ *done = FALSE;
+ if(result == CURLE_AGAIN)
+ return CURLE_OK;
+ }
+ else if((connssl->connecting_state == ssl_connect_done) ||
+ (connssl->state == ssl_connection_deferred)) {
+ *done = TRUE;
+ }
+ return result;
}
-static CURLcode wolfssl_random(struct Curl_easy *data,
- unsigned char *entropy, size_t length)
+static CURLcode wssl_random(struct Curl_easy *data,
+ unsigned char *entropy, size_t length)
{
WC_RNG rng;
(void)data;
@@ -1985,10 +2225,10 @@ static CURLcode wolfssl_random(struct Curl_easy *data,
return CURLE_OK;
}
-static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */
- size_t tmplen,
- unsigned char *sha256sum /* output */,
- size_t unused)
+static CURLcode wssl_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum /* output */,
+ size_t unused)
{
wc_Sha256 SHA256pw;
(void)unused;
@@ -1999,14 +2239,13 @@ static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */
return CURLE_OK;
}
-static void *wolfssl_get_internals(struct ssl_connect_data *connssl,
- CURLINFO info UNUSED_PARAM)
+static void *wssl_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
{
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
(void)info;
- DEBUGASSERT(backend);
- return backend->handle;
+ DEBUGASSERT(wssl);
+ return wssl->ssl;
}
const struct Curl_ssl Curl_ssl_wolfssl = {
@@ -2020,7 +2259,7 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
#endif
SSLSUPP_CA_PATH |
SSLSUPP_CAINFO_BLOB |
-#ifdef USE_ECH
+#ifdef USE_ECH_WOLFSSL
SSLSUPP_ECH |
#endif
SSLSUPP_SSL_CTX |
@@ -2030,31 +2269,27 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
SSLSUPP_CA_CACHE |
SSLSUPP_CIPHER_LIST,
- sizeof(struct wolfssl_ctx),
-
- wolfssl_init, /* init */
- wolfssl_cleanup, /* cleanup */
- wolfssl_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
- wolfssl_shutdown, /* shutdown */
- wolfssl_data_pending, /* data_pending */
- wolfssl_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
- wolfssl_connect, /* connect */
- wolfssl_connect_nonblocking, /* connect_nonblocking */
+ sizeof(struct wssl_ctx),
+
+ wssl_init, /* init */
+ wssl_cleanup, /* cleanup */
+ Curl_wssl_version, /* version */
+ wssl_shutdown, /* shutdown */
+ wssl_data_pending, /* data_pending */
+ wssl_random, /* random */
+ NULL, /* cert_status_request */
+ wssl_connect, /* connect */
Curl_ssl_adjust_pollset, /* adjust_pollset */
- wolfssl_get_internals, /* get_internals */
- wolfssl_close, /* close_one */
- Curl_none_close_all, /* close_all */
- 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 */
- wolfssl_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
- wolfssl_recv, /* recv decrypted data */
- wolfssl_send, /* send data to encrypt */
+ wssl_get_internals, /* get_internals */
+ wssl_close, /* close_one */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
+ wssl_sha256sum, /* sha256sum */
+ wssl_recv, /* recv decrypted data */
+ wssl_send, /* send data to encrypt */
NULL, /* get_channel_binding */
};
diff --git a/libs/libcurl/src/vtls/wolfssl.h b/libs/libcurl/src/vtls/wolfssl.h
index 0a4d253a58..f5f30b2018 100644
--- a/libs/libcurl/src/vtls/wolfssl.h
+++ b/libs/libcurl/src/vtls/wolfssl.h
@@ -23,43 +23,70 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_WOLFSSL
-#include "urldata.h"
+#include "../urldata.h"
+
+struct alpn_spec;
+struct ssl_peer;
+struct Curl_ssl_session;
struct WOLFSSL;
-typedef struct WOLFSSL WOLFSSL;
struct WOLFSSL_CTX;
-typedef struct WOLFSSL_CTX WOLFSSL_CTX;
struct WOLFSSL_SESSION;
-typedef struct WOLFSSL_SESSION WOLFSSL_SESSION;
extern const struct Curl_ssl Curl_ssl_wolfssl;
-struct wolfssl_ctx {
- WOLFSSL_CTX *ctx;
- WOLFSSL *handle;
+struct wssl_ctx {
+ struct WOLFSSL_CTX *ssl_ctx;
+ struct WOLFSSL *ssl;
CURLcode io_result; /* result of last BIO cfilter operation */
+ CURLcode hs_result; /* result of handshake */
int io_send_blocked_len; /* length of last BIO write that EAGAINed */
BIT(x509_store_setup); /* x509 store has been set up */
BIT(shutting_down); /* TLS is being shut down */
};
-CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct wolfssl_ctx *wssl);
+size_t Curl_wssl_version(char *buffer, size_t size);
-CURLcode wssl_setup_session(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct wolfssl_ctx *wss,
- struct ssl_peer *peer);
+typedef CURLcode Curl_wssl_ctx_setup_cb(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ void *user_data);
+
+typedef CURLcode Curl_wssl_init_session_reuse_cb(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct alpn_spec *alpns,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data);
-CURLcode wssl_cache_session(struct Curl_cfilter *cf,
+CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx,
+ struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- WOLFSSL_SESSION *session);
+ const struct alpn_spec *alpns,
+ Curl_wssl_ctx_setup_cb *cb_setup,
+ void *cb_user_data,
+ void *ssl_user_data,
+ Curl_wssl_init_session_reuse_cb *sess_reuse_cb);
+
+CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct wssl_ctx *wssl);
+
+CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct WOLFSSL_SESSION *session,
+ int ietf_tls_id,
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len);
+
+CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct wssl_ctx *wssl);
#endif /* USE_WOLFSSL */
diff --git a/libs/libcurl/src/vtls/x509asn1.c b/libs/libcurl/src/vtls/x509asn1.c
index 6e1fbfab49..1b4821484c 100644
--- a/libs/libcurl/src/vtls/x509asn1.c
+++ b/libs/libcurl/src/vtls/x509asn1.c
@@ -22,39 +22,39 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \
defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
- defined(USE_MBEDTLS)
+ defined(USE_MBEDTLS) || defined(USE_RUSTLS)
-#if defined(USE_WOLFSSL) || defined(USE_SCHANNEL)
+#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
+ defined(USE_MBEDTLS) || defined(USE_WOLFSSL) || defined(USE_RUSTLS)
#define WANT_PARSEX509 /* uses Curl_parseX509() */
#endif
#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
- defined(USE_MBEDTLS)
+ defined(USE_MBEDTLS) || defined(USE_RUSTLS)
#define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */
-#define WANT_PARSEX509 /* ... uses Curl_parseX509() */
#endif
#include <curl/curl.h>
-#include "urldata.h"
-#include "strcase.h"
-#include "curl_ctype.h"
+#include "../urldata.h"
+#include "../strcase.h"
+#include "../curl_ctype.h"
#include "hostcheck.h"
-#include "vtls/vtls.h"
-#include "vtls/vtls_int.h"
-#include "sendf.h"
-#include "inet_pton.h"
-#include "curl_base64.h"
+#include "vtls.h"
+#include "vtls_int.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
+#include "../curlx/base64.h"
#include "x509asn1.h"
-#include "dynbuf.h"
+#include "../curlx/dynbuf.h"
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/*
* Constants.
@@ -64,10 +64,10 @@
#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
+/* #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
@@ -76,27 +76,27 @@
#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_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_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_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_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_GRAPHIC_STRING 25 */
#define CURL_ASN1_VISIBLE_STRING 26
-#define CURL_ASN1_GENERAL_STRING 27
+/* #define CURL_ASN1_GENERAL_STRING 27 */
#define CURL_ASN1_UNIVERSAL_STRING 28
-#define CURL_ASN1_CHARACTER_STRING 29
+/* #define CURL_ASN1_CHARACTER_STRING 29 */
#define CURL_ASN1_BMP_STRING 30
@@ -178,8 +178,11 @@ 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)
+#define CURL_ASN1_MAX_RECURSIONS 16
+
+static const char *getASN1Element_(struct Curl_asn1Element *elem,
+ const char *beg, const char *end,
+ size_t lvl)
{
unsigned char b;
size_t len;
@@ -190,7 +193,8 @@ static const char *getASN1Element(struct Curl_asn1Element *elem,
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)
+ ((size_t)(end - beg) > CURL_ASN1_MAX) ||
+ lvl >= CURL_ASN1_MAX_RECURSIONS)
return NULL;
/* Process header byte. */
@@ -216,7 +220,7 @@ static const char *getASN1Element(struct Curl_asn1Element *elem,
return NULL;
elem->beg = beg;
while(beg < end && *beg) {
- beg = getASN1Element(&lelem, beg, end);
+ beg = getASN1Element_(&lelem, beg, end, lvl + 1);
if(!beg)
return NULL;
}
@@ -243,10 +247,16 @@ static const char *getASN1Element(struct Curl_asn1Element *elem,
return elem->end;
}
+static const char *getASN1Element(struct Curl_asn1Element *elem,
+ const char *beg, const char *end)
+{
+ return getASN1Element_(elem, beg, end, 0);
+}
+
#ifdef WANT_EXTRACT_CERTINFO
/*
- * Search the null terminated OID or OID identifier in local table.
+ * 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)
@@ -259,6 +269,17 @@ static const struct Curl_OID *searchOID(const char *oid)
return NULL;
}
+#ifdef UNITTESTS
+/* used by unit1657.c */
+CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
+ const char *beg, const char *end)
+{
+ if(getASN1Element(elem, beg, end))
+ return CURLE_OK;
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+}
+#endif
+
/*
* Convert an ASN.1 Boolean value into its string representation.
*
@@ -270,7 +291,7 @@ static CURLcode bool2str(struct dynbuf *store,
{
if(end - beg != 1)
return CURLE_BAD_FUNCTION_ARGUMENT;
- return Curl_dyn_add(store, *beg ? "TRUE": "FALSE");
+ return curlx_dyn_add(store, *beg ? "TRUE": "FALSE");
}
/*
@@ -284,7 +305,7 @@ static CURLcode octet2str(struct dynbuf *store,
CURLcode result = CURLE_OK;
while(!result && beg < end)
- result = Curl_dyn_addf(store, "%02x:", (unsigned char) *beg++);
+ result = curlx_dyn_addf(store, "%02x:", (unsigned char) *beg++);
return result;
}
@@ -323,7 +344,7 @@ static CURLcode int2str(struct dynbuf *store,
do
val = (val << 8) | *(const unsigned char *) beg++;
while(beg < end);
- return Curl_dyn_addf(store, "%s%x", val >= 10 ? "0x" : "", val);
+ return curlx_dyn_addf(store, "%s%x", val >= 10 ? "0x" : "", val);
}
/*
@@ -366,7 +387,7 @@ utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end)
if(type == CURL_ASN1_UTF8_STRING) {
/* Just copy. */
if(inlength)
- result = Curl_dyn_addn(to, from, inlength);
+ result = curlx_dyn_addn(to, from, inlength);
}
else {
while(!result && (from < end)) {
@@ -405,7 +426,7 @@ utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end)
charsize++;
}
buf[0] = (char) wc;
- result = Curl_dyn_addn(to, buf, charsize);
+ result = curlx_dyn_addn(to, buf, charsize);
}
}
return result;
@@ -428,7 +449,7 @@ static CURLcode encodeOID(struct dynbuf *store,
x = y / 40;
y -= x * 40;
- result = Curl_dyn_addf(store, "%u.%u", x, y);
+ result = curlx_dyn_addf(store, "%u.%u", x, y);
if(result)
return result;
@@ -441,7 +462,7 @@ static CURLcode encodeOID(struct dynbuf *store,
y = *(const unsigned char *) beg++;
x = (x << 7) | (y & 0x7F);
} while(y & 0x80);
- result = Curl_dyn_addf(store, ".%u", x);
+ result = curlx_dyn_addf(store, ".%u", x);
}
return result;
}
@@ -459,16 +480,16 @@ static CURLcode OID2str(struct dynbuf *store,
if(beg < end) {
if(symbolic) {
struct dynbuf buf;
- Curl_dyn_init(&buf, CURL_X509_STR_MAX);
+ curlx_dyn_init(&buf, CURL_X509_STR_MAX);
result = encodeOID(&buf, beg, end);
if(!result) {
- const struct Curl_OID *op = searchOID(Curl_dyn_ptr(&buf));
+ const struct Curl_OID *op = searchOID(curlx_dyn_ptr(&buf));
if(op)
- result = Curl_dyn_add(store, op->textoid);
+ result = curlx_dyn_add(store, op->textoid);
else
- result = Curl_dyn_add(store, Curl_dyn_ptr(&buf));
- Curl_dyn_free(&buf);
+ result = curlx_dyn_add(store, curlx_dyn_ptr(&buf));
+ curlx_dyn_free(&buf);
}
}
else
@@ -547,12 +568,12 @@ static CURLcode GTime2str(struct dynbuf *store,
tzl = end - tzp;
}
- return Curl_dyn_addf(store,
- "%.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);
+ return curlx_dyn_addf(store,
+ "%.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);
}
#ifdef UNITTESTS
@@ -601,10 +622,10 @@ static CURLcode UTime2str(struct dynbuf *store,
tzp++;
tzl = end - tzp;
- return Curl_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
- 20 - (*beg >= '5'), beg, beg + 2, beg + 4,
- beg + 6, beg + 8, sec,
- (int)tzl, tzp);
+ return curlx_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
+ 20 - (*beg >= '5'), beg, beg + 2, beg + 4,
+ beg + 6, beg + 8, sec,
+ (int)tzl, tzp);
}
/*
@@ -637,7 +658,7 @@ static CURLcode ASN1tostr(struct dynbuf *store,
result = octet2str(store, elem->beg, elem->end);
break;
case CURL_ASN1_NULL:
- result = Curl_dyn_addn(store, "", 1);
+ result = curlx_dyn_addn(store, "", 1);
break;
case CURL_ASN1_OBJECT_IDENTIFIER:
result = OID2str(store, elem->beg, elem->end, TRUE);
@@ -681,7 +702,7 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
CURLcode result = CURLE_OK;
bool added = FALSE;
struct dynbuf temp;
- Curl_dyn_init(&temp, CURL_X509_STR_MAX);
+ curlx_dyn_init(&temp, CURL_X509_STR_MAX);
for(p1 = dn->beg; p1 < dn->end;) {
p1 = getASN1Element(&rdn, p1, dn->end);
@@ -704,12 +725,12 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
result = CURLE_BAD_FUNCTION_ARGUMENT;
goto error;
}
- Curl_dyn_reset(&temp);
+ curlx_dyn_reset(&temp);
result = ASN1tostr(&temp, &oid, 0);
if(result)
goto error;
- str = Curl_dyn_ptr(&temp);
+ str = curlx_dyn_ptr(&temp);
if(!str) {
result = CURLE_BAD_FUNCTION_ARGUMENT;
@@ -722,20 +743,20 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
;
if(added) {
if(p3 - str > 2)
- result = Curl_dyn_addn(store, "/", 1);
+ result = curlx_dyn_addn(store, "/", 1);
else
- result = Curl_dyn_addn(store, ", ", 2);
+ result = curlx_dyn_addn(store, ", ", 2);
if(result)
goto error;
}
/* Encode attribute name. */
- result = Curl_dyn_add(store, str);
+ result = curlx_dyn_add(store, str);
if(result)
goto error;
/* Generate equal sign. */
- result = Curl_dyn_addn(store, "=", 1);
+ result = curlx_dyn_addn(store, "=", 1);
if(result)
goto error;
@@ -743,12 +764,12 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
result = ASN1tostr(store, &value, 0);
if(result)
goto error;
- Curl_dyn_reset(&temp);
+ curlx_dyn_reset(&temp);
added = TRUE; /* use separator for next */
}
}
error:
- Curl_dyn_free(&temp);
+ curlx_dyn_free(&temp);
return result;
}
@@ -926,8 +947,8 @@ static CURLcode ssl_push_certinfo_dyn(struct Curl_easy *data,
const char *label,
struct dynbuf *ptr)
{
- size_t valuelen = Curl_dyn_len(ptr);
- char *value = Curl_dyn_ptr(ptr);
+ size_t valuelen = curlx_dyn_len(ptr);
+ char *value = curlx_dyn_ptr(ptr);
CURLcode result = Curl_ssl_push_certinfo_len(data, certnum, label,
value, valuelen);
@@ -945,7 +966,7 @@ static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum,
CURLcode result;
struct dynbuf out;
- Curl_dyn_init(&out, CURL_X509_STR_MAX);
+ curlx_dyn_init(&out, CURL_X509_STR_MAX);
/* Generate a certificate information record for the public key. */
@@ -953,7 +974,7 @@ static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum,
if(!result) {
if(data->set.ssl.certinfo)
result = ssl_push_certinfo_dyn(data, certnum, label, &out);
- Curl_dyn_free(&out);
+ curlx_dyn_free(&out);
}
return result;
}
@@ -1005,7 +1026,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum,
len = ((elem.end - q) * 8);
if(len) {
unsigned int i;
- for(i = *(unsigned char *) q; !(i & 0x80); i <<= 1)
+ for(i = *(const unsigned char *) q; !(i & 0x80); i <<= 1)
len--;
}
if(len > 32)
@@ -1089,7 +1110,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(certnum)
return CURLE_OK;
- Curl_dyn_init(&out, CURL_X509_STR_MAX);
+ curlx_dyn_init(&out, CURL_X509_STR_MAX);
/* Prepare the certificate information for curl_easy_getinfo(). */
/* Extract the certificate ASN.1 elements. */
@@ -1105,7 +1126,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(result)
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Issuer. */
result = DNtostr(&out, &cert.issuer);
@@ -1116,20 +1137,20 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(result)
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Version (always fits in less than 32 bits). */
version = 0;
for(ptr = cert.version.beg; ptr < cert.version.end; ptr++)
version = (version << 8) | *(const unsigned char *) ptr;
if(data->set.ssl.certinfo) {
- result = Curl_dyn_addf(&out, "%x", version);
+ result = curlx_dyn_addf(&out, "%x", version);
if(result)
goto done;
result = ssl_push_certinfo_dyn(data, certnum, "Version", &out);
if(result)
goto done;
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
}
/* Serial number. */
@@ -1141,7 +1162,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(result)
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Signature algorithm .*/
result = dumpAlgo(&out, &param, cert.signatureAlgorithm.beg,
@@ -1154,7 +1175,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(result)
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Start Date. */
result = ASN1tostr(&out, &cert.notBefore, 0);
@@ -1165,7 +1186,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(result)
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Expire Date. */
result = ASN1tostr(&out, &cert.notAfter, 0);
@@ -1176,7 +1197,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(result)
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Public Key Algorithm. */
result = dumpAlgo(&out, &param, cert.subjectPublicKeyAlgorithm.beg,
@@ -1190,13 +1211,13 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
goto done;
}
- rc = do_pubkey(data, certnum, Curl_dyn_ptr(&out),
+ rc = do_pubkey(data, certnum, curlx_dyn_ptr(&out),
&param, &cert.subjectPublicKey);
if(rc) {
result = CURLE_OUT_OF_MEMORY; /* the most likely error */
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Signature. */
result = ASN1tostr(&out, &cert.signature, 0);
@@ -1207,12 +1228,12 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(result)
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Generate PEM certificate. */
- result = Curl_base64_encode(cert.certificate.beg,
- cert.certificate.end - cert.certificate.beg,
- &certptr, &clen);
+ result = curlx_base64_encode(cert.certificate.beg,
+ cert.certificate.end - cert.certificate.beg,
+ &certptr, &clen);
if(result)
goto done;
@@ -1225,22 +1246,22 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
-----END CERTIFICATE-----\n
*/
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Build the certificate string. */
- result = Curl_dyn_add(&out, "-----BEGIN CERTIFICATE-----\n");
+ result = curlx_dyn_add(&out, "-----BEGIN CERTIFICATE-----\n");
if(!result) {
size_t j = 0;
while(!result && (j < clen)) {
size_t chunksize = (clen - j) > 64 ? 64 : (clen - j);
- result = Curl_dyn_addn(&out, &certptr[j], chunksize);
+ result = curlx_dyn_addn(&out, &certptr[j], chunksize);
if(!result)
- result = Curl_dyn_addn(&out, "\n", 1);
+ result = curlx_dyn_addn(&out, "\n", 1);
j += chunksize;
}
if(!result)
- result = Curl_dyn_add(&out, "-----END CERTIFICATE-----\n");
+ result = curlx_dyn_add(&out, "-----END CERTIFICATE-----\n");
}
free(certptr);
if(!result)
@@ -1250,10 +1271,11 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
done:
if(result)
failf(data, "Failed extracting certificate chain");
- Curl_dyn_free(&out);
+ curlx_dyn_free(&out);
return result;
}
#endif /* WANT_EXTRACT_CERTINFO */
-#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */
+#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP
+ or USE_MBEDTLS or USE_RUSTLS */
diff --git a/libs/libcurl/src/vtls/x509asn1.h b/libs/libcurl/src/vtls/x509asn1.h
index b0155d11a2..3b933cb231 100644
--- a/libs/libcurl/src/vtls/x509asn1.h
+++ b/libs/libcurl/src/vtls/x509asn1.h
@@ -25,14 +25,14 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \
defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
- defined(USE_MBEDTLS)
+ defined(USE_MBEDTLS) || defined(USE_RUSTLS)
-#include "cfilters.h"
-#include "urldata.h"
+#include "../cfilters.h"
+#include "../urldata.h"
/*
* Types.
@@ -45,7 +45,7 @@ struct Curl_asn1Element {
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. */
+ BIT(constructed); /* Element is constructed. */
};
/* X509 certificate: RFC 5280. */
@@ -80,13 +80,17 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data,
#ifdef UNITTESTS
#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
- defined(USE_MBEDTLS)
+ defined(USE_MBEDTLS) || defined(USE_RUSTLS)
/* used by unit1656.c */
CURLcode Curl_x509_GTime2str(struct dynbuf *store,
const char *beg, const char *end);
+/* used by unit1657.c */
+CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
+ const char *beg, const char *end);
#endif
#endif
-#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */
+#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP
+ or USE_MBEDTLS or USE_RUSTLS */
#endif /* HEADER_CURL_X509ASN1_H */