diff options
| author | dartraiden <wowemuh@gmail.com> | 2025-04-21 09:33:29 +0300 |
|---|---|---|
| committer | dartraiden <wowemuh@gmail.com> | 2025-04-21 09:50:38 +0300 |
| commit | cf6ba06cd445f1f4554701637d5bab581acfba98 (patch) | |
| tree | 0d9b618df1c8f888cb37221be0590f4a677fe477 /libs/libcurl/src/vtls | |
| parent | 842ec200cd37ae05f2a9c56f2a4040088d2ac917 (diff) | |
libcurl: update to 8.13.0
Diffstat (limited to 'libs/libcurl/src/vtls')
23 files changed, 2650 insertions, 2291 deletions
diff --git a/libs/libcurl/src/vtls/bearssl.c b/libs/libcurl/src/vtls/bearssl.c index 24a0ea0646..bf3f77e8be 100644 --- a/libs/libcurl/src/vtls/bearssl.c +++ b/libs/libcurl/src/vtls/bearssl.c @@ -153,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);
@@ -338,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
@@ -484,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,
@@ -611,12 +611,12 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, if(ssl_config->primary.cache_session) {
struct Curl_ssl_session *sc_session = NULL;
- const br_ssl_session_parameters *session;
ret = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key,
&sc_session);
if(!ret && sc_session && sc_session->sdata && sc_session->sdata_len) {
- session = (br_ssl_session_parameters *)(void *)sc_session->sdata;
+ 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");
@@ -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) {
@@ -911,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");
@@ -930,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;
}
@@ -998,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;
}
@@ -1044,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)
{
@@ -1161,7 +1087,6 @@ const struct Curl_ssl Curl_ssl_bearssl = { bearssl_random, /* random */
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 */
diff --git a/libs/libcurl/src/vtls/cipher_suite.c b/libs/libcurl/src/vtls/cipher_suite.c index ca7c5ba46d..202a1452b0 100644 --- a/libs/libcurl/src/vtls/cipher_suite.c +++ b/libs/libcurl/src/vtls/cipher_suite.c @@ -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/gtls.c b/libs/libcurl/src/vtls/gtls.c index 56bae80332..09b4e22352 100644 --- a/libs/libcurl/src/vtls/gtls.c +++ b/libs/libcurl/src/vtls/gtls.c @@ -63,12 +63,6 @@ /* The last #include file should be: */
#include "memdebug.h"
-#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"
-
/* Enable GnuTLS debugging by defining GTLSDEBUG */
/*#define GTLSDEBUG */
@@ -106,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;
@@ -127,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;
@@ -138,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,115 +235,68 @@ static void unload_file(gnutls_datum_t data) /* this function does a 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)
@@ -366,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;
@@ -611,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) &&
@@ -655,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 */
@@ -827,6 +783,63 @@ 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;
+
+ Curl_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 = Curl_dyn_add(&buf, priority);
+ if(!result)
+ result = Curl_dyn_add(&buf, ":" GNUTLS_SRP);
+ if(result)
+ goto out;
+ priority = Curl_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(!Curl_dyn_len(&buf)) { /* not added yet */
+ result = Curl_dyn_add(&buf, priority);
+ if(result)
+ goto out;
+ }
+ result = Curl_dyn_addf(&buf, ":%s", conn_config->cipher_list);
+ if(result)
+ goto out;
+ priority = Curl_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:
+ Curl_dyn_free(&buf);
+ return result;
+}
+
static CURLcode gtls_client_init(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
@@ -839,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)
@@ -935,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
@@ -960,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) {
@@ -1006,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 |
@@ -1021,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
@@ -1089,6 +1067,7 @@ static int keylog_callback(gnutls_session_t session, const char *label, 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)
{
@@ -1104,13 +1083,13 @@ static CURLcode gtls_on_session_reuse(struct Curl_cfilter *cf, /* 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(connssl->alpn, scs->alpn)) {
+ 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_use;
+ connssl->earlydata_state = ssl_earlydata_await;
connssl->state = ssl_connection_deferred;
result = Curl_alpn_set_negotiated(cf, data, connssl,
(const unsigned char *)scs->alpn,
@@ -1124,7 +1103,7 @@ 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,
+ const struct alpn_spec *alpns_requested,
Curl_gtls_ctx_setup_cb *cb_setup,
void *cb_user_data,
void *ssl_user_data,
@@ -1133,13 +1112,16 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, 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 *scs = NULL;
- gnutls_datum_t gtls_alpns[5];
+ gnutls_datum_t gtls_alpns[ALPN_ENTRIES_MAX];
size_t gtls_alpns_count = 0;
bool gtls_session_setup = FALSE;
- CURLcode result;
+ struct alpn_spec alpns;
+ CURLcode result = CURLE_OK;
int rc;
DEBUGASSERT(gctx);
+ Curl_alpn_copy(&alpns, alpns_requested);
+
/* 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. */
@@ -1148,7 +1130,8 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, if(result)
goto out;
- if(scs && scs->sdata && scs->sdata_len) {
+ if(scs && scs->sdata && scs->sdata_len &&
+ (!scs->alpn || Curl_alpn_contains_proto(&alpns, scs->alpn))) {
/* we got a cached session, use it! */
result = gtls_client_init(cf, data, peer, scs->earlydata_max, gctx);
@@ -1162,30 +1145,19 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, else {
infof(data, "SSL reusing session with ALPN '%s'",
scs->alpn ? scs->alpn : "-");
- if(ssl_config->earlydata &&
+ 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, scs, &do_early_data);
+ result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data);
if(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 *)scs->alpn;
- gtls_alpns[0].size = (unsigned)strlen(scs->alpn);
- if(gnutls_alpn_set_protocols(gctx->session,
- gtls_alpns, 1,
- GNUTLS_ALPN_MANDATORY)) {
- failf(data, "failed setting ALPN");
- result = CURLE_SSL_CONNECT_ERROR;
- goto out;
- }
- /* don't set again below */
- gtls_alpns_count = 0;
- alpn = NULL;
+ Curl_alpn_restrict_to(&alpns, scs->alpn);
}
}
}
@@ -1215,24 +1187,14 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, /* 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 *salpn = (unsigned char *)alpn;
- unsigned char slen;
- for(i = 0; (i < CURL_ARRAYSIZE(gtls_alpns)) && alen; ++i) {
- slen = salpn[0];
- if(slen >= alen)
- return CURLE_FAILED_INIT;
- gtls_alpns[i].data = salpn + 1;
- gtls_alpns[i].size = slen;
- salpn += 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 */
- result = CURLE_FAILED_INIT;
- goto out;
- }
- gtls_alpns_count = i;
+ gtls_alpns_count = alpns.count;
}
if(gtls_alpns_count &&
@@ -1254,33 +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,
- NULL, NULL, cf, gtls_on_session_reuse);
+ 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);
}
@@ -1639,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
@@ -1828,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)
{
@@ -1886,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;
}
@@ -1901,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) {
@@ -1922,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) {
@@ -1930,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;
@@ -1951,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 */
@@ -1964,82 +1894,43 @@ gtls_connect_common(struct Curl_cfilter *cf, if(result)
goto out;
- if(connssl->earlydata_state == ssl_earlydata_sent) {
- /* report the true time the handshake was done */
- connssl->handshake_done = Curl_now();
- Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done);
- 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,
@@ -2069,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);
@@ -2121,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;
@@ -2247,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;
@@ -2272,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 */
@@ -2344,6 +2190,7 @@ 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),
@@ -2356,7 +2203,6 @@ const struct Curl_ssl Curl_ssl_gnutls = { 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 */
diff --git a/libs/libcurl/src/vtls/gtls.h b/libs/libcurl/src/vtls/gtls.h index 7a45758a32..da1f57f8c8 100644 --- a/libs/libcurl/src/vtls/gtls.h +++ b/libs/libcurl/src/vtls/gtls.h @@ -42,6 +42,7 @@ struct Curl_easy;
struct Curl_cfilter;
+struct alpn_spec;
struct ssl_primary_config;
struct ssl_config_data;
struct ssl_peer;
@@ -81,6 +82,7 @@ typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf, 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);
@@ -88,7 +90,7 @@ 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,
+ const struct alpn_spec *alpns,
Curl_gtls_ctx_setup_cb *cb_setup,
void *cb_user_data,
void *ssl_user_data,
diff --git a/libs/libcurl/src/vtls/keylog.c b/libs/libcurl/src/vtls/keylog.c index e403114934..c922879d05 100644 --- a/libs/libcurl/src/vtls/keylog.c +++ b/libs/libcurl/src/vtls/keylog.c @@ -27,7 +27,8 @@ 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>
@@ -36,18 +37,6 @@ #include "curl_memory.h"
#include "memdebug.h"
-#define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1)
-
-#define CLIENT_RANDOM_SIZE 32
-
-/*
- * The master secret in TLS 1.2 and before is always 48 bytes. In TLS 1.3, the
- * secret size depends on the cipher suite's hash function which is 32 bytes
- * for SHA-256 and 48 bytes for SHA-384.
- */
-#define SECRET_MAXLEN 48
-
-
/* The fp for the open SSLKEYLOGFILE, or NULL if not open */
static FILE *keylog_file_fp;
diff --git a/libs/libcurl/src/vtls/keylog.h b/libs/libcurl/src/vtls/keylog.h index 3a48017f81..41e901db73 100644 --- a/libs/libcurl/src/vtls/keylog.h +++ b/libs/libcurl/src/vtls/keylog.h @@ -25,6 +25,17 @@ ***************************************************************************/
#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
* environment variable specifies the output file.
diff --git a/libs/libcurl/src/vtls/mbedtls.c b/libs/libcurl/src/vtls/mbedtls.c index c0e98e12c5..851802f147 100644 --- a/libs/libcurl/src/vtls/mbedtls.c +++ b/libs/libcurl/src/vtls/mbedtls.c @@ -198,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);
@@ -726,6 +726,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));
@@ -754,6 +757,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));
@@ -762,10 +768,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 */
@@ -1211,7 +1213,7 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, len = backend->send_blocked_len;
}
- ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, 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",
@@ -1440,16 +1442,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) {
@@ -1457,73 +1455,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 */
@@ -1548,34 +1493,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;
}
@@ -1682,7 +1599,6 @@ const struct Curl_ssl Curl_ssl_mbedtls = { mbedtls_random, /* random */
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 */
diff --git a/libs/libcurl/src/vtls/openssl.c b/libs/libcurl/src/vtls/openssl.c index 97c25ef655..6044d4d073 100644 --- a/libs/libcurl/src/vtls/openssl.c +++ b/libs/libcurl/src/vtls/openssl.c @@ -34,7 +34,7 @@ #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
@@ -62,6 +62,7 @@ #include "strcase.h"
#include "hostcheck.h"
#include "multiif.h"
+#include "strdup.h"
#include "strerror.h"
#include "curl_printf.h"
@@ -83,7 +84,7 @@ #include <openssl/tls1.h>
#include <openssl/evp.h>
-#if defined(HAVE_SSL_SET1_ECH_CONFIG_LIST)
+#ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST
#define USE_ECH_OPENSSL
#endif
@@ -93,17 +94,25 @@ # endif
#endif /* USE_ECH_OPENSSL */
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP)
+#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
-#if OPENSSL_VERSION_NUMBER >= 0x03000000fL && !defined(OPENSSL_NO_UI_CONSOLE)
+#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
+
+#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 */
@@ -131,49 +140,24 @@ #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 >= 0x10002003L && \
OPENSSL_VERSION_NUMBER <= 0x10002FFFL && \
@@ -181,11 +165,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
@@ -210,35 +189,21 @@ 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)
- */
-#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) || \
- defined(OPENSSL_IS_BORINGSSL)
-#define HAVE_SSL_CTX_SET_EC_CURVES
-#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(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)
@@ -272,8 +237,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
@@ -294,13 +257,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,
@@ -364,10 +320,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 */
@@ -456,7 +418,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;
@@ -673,21 +635,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);
@@ -804,7 +764,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,
@@ -850,14 +810,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)
{
@@ -881,9 +833,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);
@@ -930,15 +880,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
@@ -981,14 +931,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;
}
}
@@ -1042,6 +992,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];
@@ -1164,7 +1122,7 @@ static int 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;
@@ -1194,7 +1152,7 @@ static int 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
@@ -1214,13 +1172,8 @@ static int 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;
@@ -1228,7 +1181,7 @@ 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);
+ passwd_callback, CURL_UNCONST(key_passwd));
if(!x)
goto end;
@@ -1247,7 +1200,7 @@ 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)) {
@@ -1269,12 +1222,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
@@ -1296,9 +1243,7 @@ int cert_stuff(struct Curl_easy *data, if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE) ||
(file_type == SSL_FILETYPE_PROVIDER)) {
SSL *ssl;
- X509 *x509 = NULL;
- EVP_PKEY *pri = NULL;
- STACK_OF(X509) *ca = NULL;
+ X509 *x509;
int cert_done = 0;
int cert_use_result;
@@ -1372,7 +1317,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;
}
@@ -1461,7 +1406,7 @@ int cert_stuff(struct Curl_easy *data, failf(data, "No cert found in the openssl store: %s",
ossl_strerror(ERR_get_error(), error_buffer,
sizeof(error_buffer)));
- goto fail;
+ return 0;
}
if(SSL_CTX_use_certificate(ctx, cert) != 1) {
@@ -1487,6 +1432,8 @@ int cert_stuff(struct Curl_easy *data, {
BIO *cert_bio = NULL;
PKCS12 *p12 = NULL;
+ EVP_PKEY *pri;
+ STACK_OF(X509) *ca = NULL;
if(cert_blob) {
cert_bio = BIO_new_mem_buf(cert_blob->data, (int)(cert_blob->len));
if(!cert_bio) {
@@ -1637,7 +1584,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");
@@ -1673,9 +1620,9 @@ fail: case SSL_FILETYPE_PROVIDER:
{
/* Implicitly use pkcs11 provider if none was provided and the
- * cert_file is a PKCS#11 URI */
+ * key_file is a PKCS#11 URI */
if(!data->state.provider) {
- if(is_pkcs11_uri(cert_file)) {
+ if(is_pkcs11_uri(key_file)) {
if(ossl_set_provider(data, "pkcs11") != CURLE_OK) {
return 0;
}
@@ -1688,7 +1635,7 @@ fail: OSSL_STORE_CTX *store = NULL;
OSSL_STORE_INFO *info = NULL;
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");
@@ -1735,7 +1682,7 @@ fail: failf(data, "No private key found in the openssl store: %s",
ossl_strerror(ERR_get_error(), error_buffer,
sizeof(error_buffer)));
- goto fail;
+ return 0;
}
if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) {
@@ -1851,8 +1798,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 */
@@ -1902,8 +1848,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
@@ -1919,11 +1864,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();
@@ -1941,18 +1882,7 @@ static void ossl_cleanup(void) static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine)
{
#ifdef USE_OPENSSL_ENGINE
- ENGINE *e;
-
-#if OPENSSL_VERSION_NUMBER >= 0x00909000L
- e = ENGINE_by_id(engine);
-#else
- /* avoid memory leak */
- for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) {
- const char *e_id = ENGINE_get_id(e);
- if(!strcmp(engine, e_id))
- break;
- }
-#endif
+ ENGINE *e = ENGINE_by_id(engine);
if(!e) {
failf(data, "SSL Engine '%s' not found", engine);
@@ -2211,8 +2141,7 @@ 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)
+#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
@@ -2289,14 +2218,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);
@@ -2342,7 +2271,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) {
@@ -2428,7 +2357,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);
@@ -2470,14 +2399,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;
@@ -2536,34 +2464,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;
@@ -2835,15 +2735,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);
}
@@ -2855,7 +2755,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
@@ -2863,9 +2763,8 @@ 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. */
-#if OPENSSL_VERSION_NUMBER >= 0x10002000L \
- && !defined(OPENSSL_NO_TLSEXT)
+/* Check for ALPN support. */
+#ifndef OPENSSL_NO_TLSEXT
# define HAS_ALPN_OPENSSL
#endif
@@ -2998,20 +2897,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:
@@ -3020,14 +2909,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
@@ -3051,10 +2936,13 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, const char *ssl_peer_key,
SSL_SESSION *session,
int ietf_tls_id,
- const char *alpn)
+ 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;
if(!cf || !data)
@@ -3065,6 +2953,7 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, 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) {
@@ -3084,11 +2973,23 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, goto out;
}
- result = Curl_ssl_session_create(der_session_buf, der_session_size,
- ietf_tls_id, alpn,
- (curl_off_t)time(NULL) +
- SSL_SESSION_get_timeout(session), 0,
- &sc_session);
+#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);
@@ -3111,7 +3012,8 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) 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);
+ SSL_version(ssl), connssl->negotiated.alpn,
+ NULL, 0);
}
return 0;
}
@@ -3172,7 +3074,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,
@@ -3340,7 +3242,7 @@ static CURLcode ossl_populate_x509_store(struct Curl_cfilter *cf, 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/
@@ -3458,7 +3360,7 @@ static CURLcode ossl_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
@@ -3479,7 +3381,7 @@ static CURLcode ossl_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"
@@ -3540,7 +3442,7 @@ static X509_STORE *ossl_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 &&
!ossl_cached_x509_store_expired(data, share) &&
@@ -3563,7 +3465,7 @@ static void ossl_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) {
@@ -3571,7 +3473,7 @@ static void ossl_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);
@@ -3651,15 +3553,16 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- const unsigned char *alpn, size_t alpn_len,
+ 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)
+ void *ssl_user_data,
+ Curl_ossl_init_session_reuse_cb *sess_reuse_cb)
{
CURLcode result = CURLE_OK;
const char *ciphers;
- SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
+ 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);
@@ -3669,12 +3572,14 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, const char * const ssl_cert_type = ssl_config->cert_type;
const bool verifypeer = conn_config->verifypeer;
char error_buffer[256];
+ struct alpn_spec alpns;
/* 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;
switch(peer->transport) {
@@ -3846,15 +3751,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, SSL_CTX_set_mode(octx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
#endif
- if(alpn && alpn_len) {
-#ifdef HAS_ALPN_OPENSSL
- 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,
@@ -3898,17 +3794,20 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, 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;
}
}
}
-#endif
#ifdef USE_OPENSSL_SRP
if(ssl_config->primary.username && Curl_auth_allowed_to_host(data)) {
@@ -3992,8 +3891,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, SSL_set_app_data(octx->ssl, ssl_user_data);
-#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)
SSL_set_tlsext_status_type(octx->ssl, TLSEXT_STATUSTYPE_ocsp);
#endif
@@ -4140,12 +4038,12 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, octx->reused_session = FALSE;
if(ssl_config->primary.cache_session) {
- struct Curl_ssl_session *sc_session = NULL;
+ struct Curl_ssl_session *scs = NULL;
- result = Curl_ssl_scache_take(cf, data, peer->scache_key, &sc_session);
- if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) {
- const unsigned char *der_sessionid = sc_session->sdata;
- size_t der_sessionid_size = sc_session->sdata_len;
+ 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
@@ -4160,8 +4058,29 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, sizeof(error_buffer)));
}
else {
- infof(data, "SSL reusing session");
+ 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);
}
@@ -4169,37 +4088,73 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, infof(data, "SSL session not accepted by OpenSSL, continuing without");
}
}
- Curl_ssl_scache_return(cf, data, peer->scache_key, sc_session);
+ 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;
}
+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;
+
+ *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");
+ }
+ 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 ossl_connect_step1(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
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_OPENSSL
- 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,
- proto.data, proto.len, NULL, NULL,
- ossl_new_session_cb, cf);
+ connssl->alpn, NULL, NULL,
+ ossl_new_session_cb, cf,
+ ossl_on_session_reuse);
if(result)
return result;
@@ -4225,7 +4180,9 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, #endif
#ifdef HAS_ALPN_OPENSSL
- if(connssl->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);
}
@@ -4338,27 +4295,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 {
@@ -4395,7 +4350,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, else
failf(data, "%s", "SSL certificate verification failed");
}
-#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) &&
@@ -4661,7 +4616,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;
@@ -4735,15 +4690,16 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, #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
@@ -4864,8 +4820,7 @@ 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);
@@ -4917,137 +4872,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;
- else
+ 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)
{
@@ -5340,7 +5327,6 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex, 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);
@@ -5354,13 +5340,6 @@ size_t Curl_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",
@@ -5384,25 +5363,19 @@ size_t Curl_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
@@ -5435,7 +5408,7 @@ static CURLcode ossl_random(struct Curl_easy *data, 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 */,
@@ -5461,8 +5434,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;
@@ -5507,7 +5479,6 @@ const struct Curl_ssl Curl_ssl_openssl = { 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 */
@@ -5516,7 +5487,7 @@ const struct Curl_ssl Curl_ssl_openssl = { ossl_set_engine_default, /* set_engine_default */
ossl_engines_list, /* engines_list */
NULL, /* false_start */
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+#ifndef OPENSSL_NO_SHA256
ossl_sha256sum, /* sha256sum */
#else
NULL, /* sha256sum */
diff --git a/libs/libcurl/src/vtls/openssl.h b/libs/libcurl/src/vtls/openssl.h index bd5aafba96..f72f6dea35 100644 --- a/libs/libcurl/src/vtls/openssl.h +++ b/libs/libcurl/src/vtls/openssl.h @@ -49,7 +49,16 @@ #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 {
@@ -75,16 +84,22 @@ typedef CURLcode Curl_ossl_ctx_setup_cb(struct Curl_cfilter *cf, 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,
- 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
@@ -113,7 +128,9 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, const char *ssl_peer_key,
SSL_SESSION *ssl_sessionid,
int ietf_tls_id,
- const char *alpn);
+ 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 ca987d7eed..f8e668e17a 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
@@ -29,7 +30,6 @@ #include "curl_printf.h"
-#include <errno.h>
#include <rustls.h>
#include "inet_pton.h"
@@ -38,12 +38,10 @@ #include "vtls.h"
#include "vtls_int.h"
#include "rustls.h"
-#include "select.h"
+#include "keylog.h"
#include "strerror.h"
-#include "multiif.h"
-#include "connect.h" /* for the connect timeout */
#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,7 +415,7 @@ 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)) {
fclose(f);
@@ -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,607 @@ 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;
+ }
+
+ 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;
+ }
- result = rustls_client_config_builder_new_custom(custom_provider,
+ 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;
+
+ Curl_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,
+ Curl_dyn_uptr(&crl_contents),
+ Curl_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:
+ Curl_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;
+ }
+
+ Curl_dyn_init(&cert_contents, SIZE_MAX);
+ Curl_dyn_init(&key_contents, SIZE_MAX);
+
+ 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(Curl_dyn_uptr(&cert_contents),
+ Curl_dyn_len(&cert_contents),
+ Curl_dyn_uptr(&key_contents),
+ Curl_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:
+ Curl_dyn_free(&cert_contents);
+ Curl_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 = Curl_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_fetch_addr(
+ data,
+ connssl->peer.hostname,
+ connssl->peer.port);
+ }
+ 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(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 +1136,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 +1191,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 +1205,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 +1252,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 +1271,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 +1289,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 +1302,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 +1341,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 +1371,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 +1389,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,23 +1403,29 @@ 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),
NULL, /* init */
- NULL, /* cleanup */
+ cr_cleanup, /* cleanup */
cr_version, /* version */
cr_shutdown, /* shutdown */
cr_data_pending, /* data_pending */
cr_random, /* random */
NULL, /* cert_status_request */
- cr_connect_blocking, /* connect */
- cr_connect_nonblocking, /* connect_nonblocking */
+ cr_connect, /* connect */
Curl_ssl_adjust_pollset, /* adjust_pollset */
cr_get_internals, /* get_internals */
cr_close, /* close_one */
diff --git a/libs/libcurl/src/vtls/schannel.c b/libs/libcurl/src/vtls/schannel.c index e0a978f1db..f685011aca 100644 --- a/libs/libcurl/src/vtls/schannel.c +++ b/libs/libcurl/src/vtls/schannel.c @@ -53,8 +53,10 @@ #include "x509asn1.h"
#include "curl_printf.h"
#include "multiif.h"
+#include "system_win32.h"
#include "version_win32.h"
#include "rand.h"
+#include "strparse.h"
/* The last #include file should be: */
#include "curl_memory.h"
@@ -71,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_SCHANNEL
-#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
@@ -152,10 +141,27 @@ #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
+/* 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);
@@ -232,8 +238,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 {
@@ -350,9 +354,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;
@@ -369,12 +373,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(Curl_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",
@@ -654,8 +659,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
@@ -802,11 +806,12 @@ 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.
@@ -842,11 +847,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
@@ -882,7 +888,9 @@ 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;
@@ -892,7 +900,6 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) unsigned char alpn_buffer[128];
#endif
SECURITY_STATUS sspi_status = SEC_E_OK;
- struct Curl_schannel_cred *old_cred = NULL;
CURLcode result;
DEBUGASSERT(backend);
@@ -909,20 +916,14 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) }
#ifdef HAS_ALPN_SCHANNEL
- /* 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);
+ 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
@@ -955,9 +956,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) {
+ struct Curl_schannel_cred *old_cred;
Curl_ssl_scache_lock(data);
- if(Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key,
- (void **)&old_cred)) {
+ 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"));
@@ -973,7 +975,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *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. */
@@ -1629,6 +1631,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;
}
@@ -1642,16 +1645,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) {
@@ -1659,73 +1658,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);
@@ -1752,11 +1697,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;
}
@@ -2002,7 +1942,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)
@@ -2129,7 +2068,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");
@@ -2240,28 +2179,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)
{
@@ -2466,6 +2383,34 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data) static int schannel_init(void)
{
+#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;
}
@@ -2589,7 +2534,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 */
@@ -2653,7 +2603,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;
@@ -2731,7 +2681,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));
@@ -2739,7 +2689,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);
@@ -2800,7 +2750,6 @@ const struct Curl_ssl Curl_ssl_schannel = { schannel_random, /* random */
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 */
diff --git a/libs/libcurl/src/vtls/schannel_verify.c b/libs/libcurl/src/vtls/schannel_verify.c index 42f7f517e4..26b3805cc6 100644 --- a/libs/libcurl/src/vtls/schannel_verify.c +++ b/libs/libcurl/src/vtls/schannel_verify.c @@ -57,6 +57,22 @@ #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 +92,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 +155,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,
@@ -162,7 +179,7 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, "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;
}
@@ -191,8 +208,8 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store, "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 +249,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];
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;
}
@@ -260,7 +277,7 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, 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;
}
@@ -270,7 +287,7 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, 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;
}
@@ -300,7 +317,7 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store, 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;
}
@@ -330,9 +347,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 +399,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 +464,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 +533,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;
@@ -664,6 +739,7 @@ cleanup: if(pCertContextServer)
CertFreeCertificateContext(pCertContextServer);
+#endif /* !UNDER_CE */
return result;
}
@@ -681,15 +757,17 @@ 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];
@@ -698,6 +776,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, 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) {
@@ -729,7 +808,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, if(!trust_store) {
char buffer[STRERROR_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 {
@@ -777,11 +856,12 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, char buffer[STRERROR_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;
@@ -800,7 +880,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, &pChainContext)) {
char buffer[STRERROR_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;
}
@@ -848,6 +928,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, }
}
+#ifndef UNDER_CE
if(cert_chain_engine) {
CertFreeCertificateChainEngine(cert_chain_engine);
}
@@ -855,6 +936,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 10dfe1e92e..9cb51f2c53 100644 --- a/libs/libcurl/src/vtls/sectransp.c +++ b/libs/libcurl/src/vtls/sectransp.c @@ -34,7 +34,7 @@ #include "urldata.h" /* for the Curl_easy definition */
#include "curl_base64.h"
-#include "strtok.h"
+#include "strparse.h"
#include "multiif.h"
#include "strcase.h"
#include "x509asn1.h"
@@ -211,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+ */
@@ -266,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;
@@ -306,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;
@@ -341,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(!Curl_str_number(&os, &fnum, INT_MAX) &&
+ !Curl_str_single(&os, '.') &&
+ !Curl_str_number(&os, &snum, INT_MAX)) {
+ *major = (int)fnum;
+ *minor = (int)snum;
+ }
+ }
}
-
- /* Parse the version: */
- os_version_major = Curl_strtok_r(os_version, ".", &tok_buf);
- os_version_minor = Curl_strtok_r(NULL, ".", &tok_buf);
- *major = atoi(os_version_major);
- *minor = atoi(os_version_minor);
- free(os_version);
}
#endif /* CURL_BUILD_MAC */
@@ -541,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;
@@ -671,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
@@ -927,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;
@@ -1096,10 +1091,13 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, if(result != CURLE_OK)
return result;
+ if(connssl->alpn) {
#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \
defined(HAVE_BUILTIN_AVAILABLE)
- if(connssl->alpn) {
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;
@@ -1122,7 +1120,6 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
}
-#endif
if(ssl_config->key) {
infof(data, "WARNING: SSL: CURLOPT_SSLKEY is ignored by Secure "
@@ -1338,8 +1335,9 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, size_t ssl_sessionid_len;
Curl_ssl_scache_lock(data);
- if(Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key,
- (void **)&ssl_sessionid)) {
+ 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,
strlen(ssl_sessionid));
@@ -1696,7 +1694,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;
@@ -1746,7 +1745,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:
@@ -2092,10 +2091,13 @@ check_handshake: break;
}
+ if(connssl->alpn) {
#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \
defined(HAVE_BUILTIN_AVAILABLE)
- if(connssl->alpn) {
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);
@@ -2123,7 +2125,6 @@ check_handshake: CFRelease(alpnArr);
}
}
-#endif
return CURLE_OK;
}
@@ -2131,7 +2132,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;
@@ -2151,7 +2152,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;
@@ -2248,8 +2249,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);
@@ -2265,7 +2266,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);
@@ -2292,15 +2294,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) {
@@ -2308,73 +2307,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);
@@ -2387,34 +2333,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;
}
@@ -2549,7 +2467,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;
@@ -2759,7 +2677,6 @@ const struct Curl_ssl Curl_ssl_sectransp = { sectransp_random, /* random */
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 */
diff --git a/libs/libcurl/src/vtls/vtls.c b/libs/libcurl/src/vtls/vtls.c index 182744bb6e..0247d77577 100644 --- a/libs/libcurl/src/vtls/vtls.c +++ b/libs/libcurl/src/vtls/vtls.c @@ -154,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. */
@@ -483,39 +485,6 @@ static void cf_ctx_free(struct ssl_connect_data *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 = connssl->ssl_impl->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)
-{
- struct ssl_connect_data *connssl = cf->ctx;
-
- if(!ssl_prefs_check(data))
- return CURLE_SSL_CONNECT_ERROR;
-
- /* mark this is being ssl requested from here on. */
- return connssl->ssl_impl->connect_nonblocking(cf, data, done);
-}
-
CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
struct dynbuf *binding)
{
@@ -921,20 +890,11 @@ static int multissl_init(void) }
static CURLcode multissl_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- 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)
+ 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,
@@ -993,7 +953,6 @@ static const struct Curl_ssl Curl_ssl_multi = { NULL, /* random */
NULL, /* cert_status_request */
multissl_connect, /* connect */
- multissl_connect_nonblocking, /* connect_nonblocking */
multissl_adjust_pollset, /* adjust_pollset */
multissl_get_internals, /* get_internals */
multissl_close, /* close_one */
@@ -1232,10 +1191,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
@@ -1341,13 +1300,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;
}
@@ -1358,15 +1317,13 @@ 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;
@@ -1378,14 +1335,14 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, 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;
if(connssl->state == ssl_connection_complete)
@@ -1393,6 +1350,8 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, /* 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);
@@ -1400,6 +1359,77 @@ 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)
{
@@ -1418,21 +1448,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 = connssl->ssl_impl->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;
}
@@ -1446,6 +1512,21 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, CF_DATA_SAVE(save, cf, data);
*err = CURLE_OK;
+ 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);
@@ -1454,6 +1535,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);
@@ -1468,7 +1551,9 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf, CURLcode result = CURLE_OK;
*done = TRUE;
- if(!cf->shutdown && Curl_ssl->shut_down) {
+ /* 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);
@@ -1576,7 +1661,7 @@ static CURLcode cf_ssl_create(struct Curl_cfilter **pcf, DEBUGASSERT(data->conn);
- ctx = cf_ctx_new(data, alpn_get_spec(data->state.httpwant,
+ ctx = cf_ctx_new(data, alpn_get_spec(data->state.http_neg.wanted,
conn->bits.tls_enable_alpn));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
@@ -1627,16 +1712,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;
@@ -1768,7 +1853,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? */
@@ -1866,6 +1951,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,
diff --git a/libs/libcurl/src/vtls/vtls_int.h b/libs/libcurl/src/vtls/vtls_int.h index bb37346396..59657883df 100644 --- a/libs/libcurl/src/vtls/vtls_int.h +++ b/libs/libcurl/src/vtls/vtls_int.h @@ -49,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 */
};
@@ -62,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,
@@ -89,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,
@@ -124,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 */
};
@@ -157,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. */
diff --git a/libs/libcurl/src/vtls/vtls_scache.c b/libs/libcurl/src/vtls/vtls_scache.c index c78ef7e9a8..d06c6cf9f9 100644 --- a/libs/libcurl/src/vtls/vtls_scache.c +++ b/libs/libcurl/src/vtls/vtls_scache.c @@ -59,6 +59,9 @@ #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 */
@@ -73,6 +76,7 @@ struct Curl_ssl_scache_peer { 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
@@ -104,32 +108,18 @@ static struct Curl_ssl_scache *cf_ssl_scache_get(struct Curl_easy *data) return scache;
}
-static void cf_ssl_scache_clear_session(struct Curl_ssl_session *s)
-{
- if(s->sdata) {
- free((void *)s->sdata);
- s->sdata = NULL;
- }
- s->sdata_len = 0;
- if(s->quic_tp) {
- free((void *)s->quic_tp);
- s->quic_tp = NULL;
- }
- s->quic_tp_len = 0;
- s->ietf_tls_id = 0;
- s->valid_until = 0;
- Curl_safefree(s->alpn);
-}
-
-static void cf_ssl_scache_sesssion_ldestroy(void *udata, void *s)
+static void cf_ssl_scache_sesssion_ldestroy(void *udata, void *obj)
{
+ struct Curl_ssl_session *s = obj;
(void)udata;
- cf_ssl_scache_clear_session(s);
+ free(CURL_UNCONST(s->sdata));
+ free(CURL_UNCONST(s->quic_tp));
+ free((void *)s->alpn);
free(s);
}
CURLcode
-Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len,
+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)
@@ -140,7 +130,7 @@ Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len, }
CURLcode
-Curl_ssl_session_create2(unsigned char *sdata, size_t sdata_len,
+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,
@@ -223,6 +213,19 @@ static void cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer *peer, 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,
@@ -265,6 +268,8 @@ cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer, if(!peer->srp_password)
goto out;
}
+
+ cf_ssl_cache_peer_update(peer);
result = CURLE_OK;
out:
if(result)
@@ -372,17 +377,22 @@ void Curl_ssl_scache_unlock(struct Curl_easy *data) static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf,
const char *name,
- char *path)
+ 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 _WIN32
+#ifdef UNDER_CE
+ (void)is_local;
+ return Curl_dyn_addf(buf, ":%s-%s", name, path);
+#elif defined(_WIN32)
char abspath[_MAX_PATH];
if(_fullpath(abspath, path, _MAX_PATH))
return Curl_dyn_addf(buf, ":%s-%s", name, abspath);
+ *is_local = TRUE;
#elif defined(HAVE_REALPATH)
if(path[0] != '/') {
char *abspath = realpath(path, NULL);
@@ -391,6 +401,7 @@ static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf, (free)(abspath); /* allocated by libc, free without memdebug */
return r;
}
+ *is_local = TRUE;
}
#endif
return Curl_dyn_addf(buf, ":%s-%s", name, path);
@@ -423,6 +434,17 @@ 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,
@@ -431,6 +453,7 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, 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;
@@ -514,16 +537,16 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, goto out;
}
if(ssl->verifypeer) {
- r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile);
+ 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);
+ 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);
+ 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);
+ r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local);
if(r)
goto out;
if(ssl->cert_blob) {
@@ -569,6 +592,11 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf, if(r)
goto out;
+ r = Curl_dyn_addf(&buf, is_local ?
+ CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX);
+ if(r)
+ goto out;
+
*ppeer_key = Curl_dyn_take(&buf, &key_len);
/* we just added printable char, and dynbuf always 0 terminates,
* no need to track length */
@@ -655,6 +683,7 @@ cf_ssl_find_peer_by_key(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY;
goto out;
}
+ cf_ssl_cache_peer_update(&scache->peers[i]);
*ppeer = &scache->peers[i];
goto out;
}
@@ -832,10 +861,6 @@ CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf, Curl_ssl_session_destroy(s);
return CURLE_OK;
}
- if(!GOOD_SCACHE(scache)) {
- Curl_ssl_session_destroy(s);
- return CURLE_BAD_FUNCTION_ARGUMENT;
- }
Curl_ssl_scache_lock(data);
result = cf_scache_add_session(cf, data, scache, ssl_peer_key, s);
@@ -933,31 +958,29 @@ out: return result;
}
-bool Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const char *ssl_peer_key,
- void **sobj)
+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;
- *sobj = NULL;
if(!scache)
- return FALSE;
+ return NULL;
result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
&peer);
if(result)
- return FALSE;
+ return NULL;
- if(peer)
- *sobj = peer->sobj;
+ sobj = peer ? peer->sobj : NULL;
CURL_TRC_SSLS(data, "%s cached session for '%s'",
- *sobj ? "Found" : "No", ssl_peer_key);
- return !!*sobj;
+ sobj ? "Found" : "No", ssl_peer_key);
+ return sobj;
}
void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
@@ -1062,7 +1085,7 @@ out: CURLcode Curl_ssl_session_import(struct Curl_easy *data,
const char *ssl_peer_key,
const unsigned char *shmac, size_t shmac_len,
- const unsigned char *sdata, size_t sdata_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;
@@ -1157,8 +1180,8 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, peer = &scache->peers[i];
if(!peer->ssl_peer_key && !peer->hmac_set)
continue; /* skip free entry */
- if(peer->clientcert || peer->srp_username || peer->srp_password)
- continue; /* not exporting those */
+ if(!peer->exportable)
+ continue;
Curl_dyn_reset(&hbuf);
cf_scache_peer_remove_expired(peer, now);
diff --git a/libs/libcurl/src/vtls/vtls_scache.h b/libs/libcurl/src/vtls/vtls_scache.h index e9b5b76e36..3dea24024d 100644 --- a/libs/libcurl/src/vtls/vtls_scache.h +++ b/libs/libcurl/src/vtls/vtls_scache.h @@ -85,12 +85,11 @@ void Curl_ssl_scache_unlock(struct Curl_easy *data); * @param cf the connection filter wanting to use it
* @param data the transfer involved
* @param ssl_peer_key the key for lookup
- * @param sobj on return, the object for the peer key or NULL
+ * @retval sobj the object for the peer key or NULL
*/
-bool Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const char *ssl_peer_key,
- void **sobj);
+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);
@@ -116,7 +115,7 @@ CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf, /* All about a SSL session ticket */
struct Curl_ssl_session {
- const unsigned char *sdata; /* session ticket data, plain bytes */
+ 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 */
@@ -138,7 +137,7 @@ struct Curl_ssl_session { * @param psession on return the scached session instance created
*/
CURLcode
-Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len,
+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,
@@ -147,7 +146,7 @@ Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len, /* Variation of session creation with quic transport parameter bytes,
* Takes ownership of `quic_tp` regardless of return code. */
CURLcode
-Curl_ssl_session_create2(unsigned char *sdata, size_t sdata_len,
+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,
@@ -200,7 +199,7 @@ void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf, CURLcode Curl_ssl_session_import(struct Curl_easy *data,
const char *ssl_peer_key,
const unsigned char *shmac, size_t shmac_len,
- const unsigned char *sdata, size_t sdata_len);
+ const void *sdata, size_t sdata_len);
CURLcode Curl_ssl_session_export(struct Curl_easy *data,
curl_ssls_export_cb *export_fn,
diff --git a/libs/libcurl/src/vtls/vtls_spack.c b/libs/libcurl/src/vtls/vtls_spack.c index 22c84e8a38..b1bca85dbc 100644 --- a/libs/libcurl/src/vtls/vtls_spack.c +++ b/libs/libcurl/src/vtls/vtls_spack.c @@ -254,10 +254,11 @@ CURLcode Curl_ssl_session_pack(struct Curl_easy *data, }
CURLcode Curl_ssl_session_unpack(struct Curl_easy *data,
- const unsigned char *buf, size_t buflen,
+ 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;
diff --git a/libs/libcurl/src/vtls/vtls_spack.h b/libs/libcurl/src/vtls/vtls_spack.h index ec48853e4c..62f9da6944 100644 --- a/libs/libcurl/src/vtls/vtls_spack.h +++ b/libs/libcurl/src/vtls/vtls_spack.h @@ -35,7 +35,7 @@ CURLcode Curl_ssl_session_pack(struct Curl_easy *data, struct dynbuf *buf);
CURLcode Curl_ssl_session_unpack(struct Curl_easy *data,
- const unsigned char *buf, size_t buflen,
+ const void *bufv, size_t buflen,
struct Curl_ssl_session **ps);
#endif /* USE_SSLS_EXPORT */
diff --git a/libs/libcurl/src/vtls/wolfssl.c b/libs/libcurl/src/vtls/wolfssl.c index edd60e3e1a..3cd67a2fe5 100644 --- a/libs/libcurl/src/vtls/wolfssl.c +++ b/libs/libcurl/src/vtls/wolfssl.c @@ -36,6 +36,7 @@ #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"
#endif
@@ -64,8 +65,10 @@ #include "keylog.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"
@@ -115,6 +118,10 @@ #undef USE_BIO_CHAIN
#endif
+static CURLcode wssl_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done);
+
#ifdef OPENSSL_EXTRA
/*
* Availability note:
@@ -122,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];
@@ -164,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;
}
@@ -174,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;
@@ -217,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;
@@ -235,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);
@@ -256,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;
@@ -304,30 +310,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
@@ -335,20 +340,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;
@@ -358,8 +362,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);
@@ -371,28 +387,28 @@ 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 */
@@ -401,12 +417,15 @@ CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf, const char *ssl_peer_key,
WOLFSSL_SESSION *session,
int ietf_tls_id,
- const char *alpn)
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len)
{
CURLcode result = CURLE_OK;
struct Curl_ssl_session *sc_session = NULL;
- unsigned char *sdata = NULL;
+ unsigned char *sdata = NULL, *qtp_clone = NULL;
unsigned int sdata_len;
+ unsigned int earlydata_max = 0;
if(!session)
goto out;
@@ -429,12 +448,23 @@ CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf, 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
- result = Curl_ssl_session_create(sdata, sdata_len,
- ietf_tls_id, alpn,
- (curl_off_t)time(NULL) +
- wolfSSL_SESSION_get_timeout(session), 0,
- &sc_session);
+ 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);
@@ -460,51 +490,112 @@ static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) if(connssl && data) {
(void)Curl_wssl_cache_session(cf, data, connssl->peer.scache_key,
session, wolfSSL_version(ssl),
- connssl->negotiated.alpn);
+ connssl->negotiated.alpn, NULL, 0);
}
}
return 0;
}
-CURLcode Curl_wssl_setup_session(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct wolfssl_ctx *wss,
- const char *ssl_peer_key)
+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)
{
- struct Curl_ssl_session *sc_session = NULL;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+ CURLcode result = CURLE_OK;
+
+ *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, &sc_session);
- if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) {
+ 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;
/* wolfSSL changes the passed pointer for whatever reasons, yikes */
- const unsigned char *sdata = sc_session->sdata;
- session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata,
- (long)sc_session->sdata_len);
+ 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_session_destroy(sc_session);
- sc_session = NULL;
+ 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;
+ }
+ 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);
+ }
+ }
+ }
wolfSSL_SESSION_free(session);
}
else {
failf(data, "could not decode previous session");
}
}
- Curl_ssl_scache_return(cf, data, ssl_peer_key, sc_session);
+out:
+ Curl_ssl_scache_return(cf, data, ssl_peer_key, scs);
return result;
}
static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf,
struct Curl_easy *data,
WOLFSSL_X509_STORE *store,
- struct wolfssl_ctx *wssl)
+ 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;
@@ -514,38 +605,41 @@ static CURLcode wssl_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;
-#if !defined(NO_FILESYSTEM) && defined(WOLFSSL_SYS_CA_CERTS)
+ /* We do not want to do this again, no matter the outcome */
+ wssl->x509_store_setup = TRUE;
+
+#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;
}
}
@@ -557,14 +651,15 @@ static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf, 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",
@@ -588,7 +683,6 @@ static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf, }
#endif
(void)store;
- wssl->x509_store_setup = TRUE;
return CURLE_OK;
}
@@ -650,7 +744,7 @@ static WOLFSSL_X509_STORE *wssl_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 &&
!wssl_cached_x509_store_expired(data, share) &&
@@ -673,7 +767,7 @@ static void wssl_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) {
@@ -681,7 +775,7 @@ static void wssl_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);
@@ -713,7 +807,7 @@ static void wssl_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);
@@ -733,11 +827,12 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, cached_store = cache_criteria_met ? wssl_get_cached_x509_store(cf, data)
: NULL;
- if(cached_store && wolfSSL_CTX_get_cert_store(wssl->ctx) == cached_store) {
+ 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.
@@ -747,7 +842,7 @@ 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 = wssl_populate_x509_store(cf, data, store, wssl);
if(!result) {
@@ -756,7 +851,7 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, }
else {
/* We never share the CTX's store, use it. */
- WOLFSSL_X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx);
+ WOLFSSL_X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ssl_ctx);
result = wssl_populate_x509_store(cf, data, store, wssl);
}
@@ -831,30 +926,49 @@ 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"
+
+#if defined(HAVE_SECRET_CALLBACK)
+static void keylog_callback(const WOLFSSL *ssl, const char *line)
{
+ (void)ssl;
+ Curl_tls_keylog_write_line(line);
+}
+#endif
+
+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();
@@ -863,58 +977,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:
@@ -922,20 +1040,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);
}
@@ -946,7 +1067,6 @@ 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);
if(ciphers13)
@@ -965,12 +1085,13 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) 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))) {
+ if(!wolfSSL_CTX_set_cipher_list(wctx->ssl_ctx, Curl_dyn_ptr(&c))) {
failf(data, "failed setting cipher list: %s", Curl_dyn_ptr(&c));
Curl_dyn_free(&c);
- return CURLE_SSL_CIPHER;
+ result = CURLE_SSL_CIPHER;
+ goto out;
}
infof(data, "Cipher selection: %s", Curl_dyn_ptr(&c));
Curl_dyn_free(&c);
@@ -978,8 +1099,10 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) #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) {
@@ -991,9 +1114,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;
}
}
}
@@ -1005,30 +1129,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) {
@@ -1036,53 +1162,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 */
@@ -1091,37 +1221,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
@@ -1130,82 +1272,89 @@ 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)Curl_wssl_setup_session(cf, data, backend, connssl->peer.scache_key);
- /* 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_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;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
if(data->set.tls_ech & CURLECH_CLA_CFG
&& data->set.str[STRING_ECH_CONFIG]) {
@@ -1214,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;
@@ -1225,13 +1376,16 @@ 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);
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;
@@ -1242,11 +1396,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;
@@ -1255,49 +1411,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 && wolfSSL_set_min_proto_version(backend->handle,
+ 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_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 HAS_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';
@@ -1313,15 +1527,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)
+static CURLcode wssl_verify_pinned(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
- 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] :
@@ -1330,29 +1539,144 @@ 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
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+ WOLFSSL_X509 *x509;
+ const char *x509_der;
+ int x509_der_len;
+ struct Curl_X509certificate x509_parsed;
+ struct Curl_asn1Element *pubkey;
+ CURLcode result;
- wolfSSL_ERR_clear_error();
+ x509 = wolfSSL_get_peer_certificate(wssl->ssl);
+ if(!x509) {
+ failf(data, "SSL: failed retrieving 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;
+ 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
}
+ 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()) {
@@ -1368,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.");
@@ -1438,6 +1744,15 @@ 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;
}
+ 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 */
@@ -1446,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");
@@ -1455,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 = Curl_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)));
+ wssl_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);
- 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];
@@ -1617,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;
}
@@ -1625,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;
@@ -1652,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;
@@ -1672,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");
@@ -1684,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;
@@ -1694,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;
@@ -1708,91 +1963,89 @@ 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;
}
-
size_t Curl_wssl_version(char *buffer, size_t size)
{
#if LIBWOLFSSL_VERSION_HEX >= 0x03006000
@@ -1803,7 +2056,7 @@ size_t Curl_wssl_version(char *buffer, size_t size) }
-static int wolfssl_init(void)
+static int wssl_init(void)
{
int ret;
@@ -1811,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();
@@ -1826,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) {
@@ -1858,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 = wssl_verify_pinned(cf, data);
+ 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;
@@ -1982,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;
@@ -1996,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 = {
@@ -2027,28 +2269,27 @@ const struct Curl_ssl Curl_ssl_wolfssl = { SSLSUPP_CA_CACHE |
SSLSUPP_CIPHER_LIST,
- sizeof(struct wolfssl_ctx),
+ sizeof(struct wssl_ctx),
- wolfssl_init, /* init */
- wolfssl_cleanup, /* cleanup */
+ wssl_init, /* init */
+ wssl_cleanup, /* cleanup */
Curl_wssl_version, /* version */
- wolfssl_shutdown, /* shutdown */
- wolfssl_data_pending, /* data_pending */
- wolfssl_random, /* random */
+ wssl_shutdown, /* shutdown */
+ wssl_data_pending, /* data_pending */
+ wssl_random, /* random */
NULL, /* cert_status_request */
- wolfssl_connect, /* connect */
- wolfssl_connect_nonblocking, /* connect_nonblocking */
+ wssl_connect, /* connect */
Curl_ssl_adjust_pollset, /* adjust_pollset */
- wolfssl_get_internals, /* get_internals */
- wolfssl_close, /* close_one */
+ 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 */
- wolfssl_sha256sum, /* sha256sum */
- wolfssl_recv, /* recv decrypted data */
- wolfssl_send, /* send data to encrypt */
+ 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 24f676366c..cf4bff8e2c 100644 --- a/libs/libcurl/src/vtls/wolfssl.h +++ b/libs/libcurl/src/vtls/wolfssl.h @@ -29,19 +29,21 @@ #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 */
@@ -49,21 +51,43 @@ struct wolfssl_ctx { size_t Curl_wssl_version(char *buffer, size_t size);
+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 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,
+ 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 wolfssl_ctx *wssl);
+ struct wssl_ctx *wssl);
CURLcode Curl_wssl_setup_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
- struct wolfssl_ctx *wss,
+ struct wssl_ctx *wss,
const char *ssl_peer_key);
CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key,
- WOLFSSL_SESSION *session,
+ struct WOLFSSL_SESSION *session,
int ietf_tls_id,
- const char *alpn);
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len);
#endif /* USE_WOLFSSL */
diff --git a/libs/libcurl/src/vtls/x509asn1.c b/libs/libcurl/src/vtls/x509asn1.c index f38137e0f2..082b5191d9 100644 --- a/libs/libcurl/src/vtls/x509asn1.c +++ b/libs/libcurl/src/vtls/x509asn1.c @@ -26,16 +26,16 @@ #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>
@@ -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
@@ -1026,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)
@@ -1277,4 +1277,5 @@ done: #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 670a565302..34ba802d0f 100644 --- a/libs/libcurl/src/vtls/x509asn1.h +++ b/libs/libcurl/src/vtls/x509asn1.h @@ -29,7 +29,7 @@ #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"
@@ -80,7 +80,7 @@ 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,
@@ -91,5 +91,6 @@ CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem, #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 */
|
