diff options
Diffstat (limited to 'libs/libcurl/src/vtls')
| -rw-r--r-- | libs/libcurl/src/vtls/bearssl.c | 56 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/gtls.c | 309 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/gtls.h | 27 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/mbedtls.c | 226 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/openssl.c | 528 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/openssl.h | 26 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/rustls.c | 19 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/schannel.c | 72 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/schannel.h | 2 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/sectransp.c | 60 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/vtls.c | 605 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/vtls.h | 79 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/vtls_int.h | 68 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/vtls_scache.c | 1213 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/vtls_scache.h | 218 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/vtls_spack.c | 345 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/vtls_spack.h | 43 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/wolfssl.c | 180 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/wolfssl.h | 20 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/x509asn1.c | 29 | ||||
| -rw-r--r-- | libs/libcurl/src/vtls/x509asn1.h | 3 |
21 files changed, 2910 insertions, 1218 deletions
diff --git a/libs/libcurl/src/vtls/bearssl.c b/libs/libcurl/src/vtls/bearssl.c index f4d0cc43b3..24a0ea0646 100644 --- a/libs/libcurl/src/vtls/bearssl.c +++ b/libs/libcurl/src/vtls/bearssl.c @@ -34,6 +34,7 @@ #include "inet_pton.h"
#include "vtls.h"
#include "vtls_int.h"
+#include "vtls_scache.h"
#include "connect.h"
#include "select.h"
#include "multiif.h"
@@ -609,20 +610,19 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable);
if(ssl_config->primary.cache_session) {
- void *sdata;
- size_t slen;
+ struct Curl_ssl_session *sc_session = NULL;
const br_ssl_session_parameters *session;
- CURL_TRC_CF(data, cf, "connect_step1, check session cache");
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &sdata, &slen, NULL) &&
- slen == sizeof(*session)) {
- session = sdata;
+ 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;
br_ssl_engine_set_session_parameters(&backend->ctx.eng, session);
session_set = 1;
infof(data, "BearSSL: reusing session ID");
+ /* single use of sessions */
+ Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session);
}
- Curl_ssl_sessionid_unlock(data);
}
if(connssl->alpn) {
@@ -804,12 +804,6 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf, return ret;
}
-static void bearssl_session_free(void *sessionid, size_t idsize)
-{
- (void)idsize;
- free(sessionid);
-}
-
static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
@@ -832,17 +826,22 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, }
if(ssl_config->primary.cache_session) {
+ struct Curl_ssl_session *sc_session;
br_ssl_session_parameters *session;
session = malloc(sizeof(*session));
if(!session)
return CURLE_OUT_OF_MEMORY;
br_ssl_engine_get_session_parameters(&backend->ctx.eng, session);
- Curl_ssl_sessionid_lock(data);
- ret = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
- session, sizeof(*session),
- bearssl_session_free);
- Curl_ssl_sessionid_unlock(data);
+ ret = Curl_ssl_session_create((unsigned char *)session, sizeof(*session),
+ (int)session->version,
+ connssl->negotiated.alpn,
+ 0, 0, &sc_session);
+ if(!ret) {
+ ret = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key,
+ sc_session);
+ /* took ownership of `sc_session` */
+ }
if(ret)
return ret;
}
@@ -1154,27 +1153,24 @@ const struct Curl_ssl Curl_ssl_bearssl = { sizeof(struct bearssl_ssl_backend_data),
- Curl_none_init, /* init */
- Curl_none_cleanup, /* cleanup */
+ NULL, /* init */
+ NULL, /* cleanup */
bearssl_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
bearssl_shutdown, /* shutdown */
bearssl_data_pending, /* data_pending */
bearssl_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
bearssl_connect, /* connect */
bearssl_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
bearssl_get_internals, /* get_internals */
bearssl_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
bearssl_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
bearssl_recv, /* recv decrypted data */
bearssl_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/gtls.c b/libs/libcurl/src/vtls/gtls.c index 398af77bf3..56bae80332 100644 --- a/libs/libcurl/src/vtls/gtls.c +++ b/libs/libcurl/src/vtls/gtls.c @@ -47,12 +47,14 @@ #include "gtls.h"
#include "vtls.h"
#include "vtls_int.h"
+#include "vtls_scache.h"
#include "vauth/vauth.h"
#include "parsedate.h"
#include "connect.h" /* for the connect timeout */
#include "progress.h"
#include "select.h"
#include "strcase.h"
+#include "strdup.h"
#include "warnless.h"
#include "x509asn1.h"
#include "multiif.h"
@@ -61,10 +63,6 @@ /* The last #include file should be: */
#include "memdebug.h"
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
#define QUIC_PRIORITY \
"NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
"+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
@@ -714,21 +712,20 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf, return CURLE_OK;
}
-static void gtls_sessionid_free(void *sessionid, size_t idsize)
-{
- (void)idsize;
- free(sessionid);
-}
-
-CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- gnutls_session_t session,
- struct ssl_peer *peer,
- const char *alpn)
+CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ gnutls_session_t session,
+ curl_off_t valid_until,
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len)
{
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- void *connect_sessionid;
- size_t connect_idsize = 0;
+ struct Curl_ssl_session *sc_session;
+ unsigned char *sdata, *qtp_clone = NULL;
+ size_t sdata_len = 0;
+ size_t earlydata_max = 0;
CURLcode result = CURLE_OK;
if(!ssl_config->primary.cache_session)
@@ -740,35 +737,68 @@ CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf, detect that. */
/* get the session ID data size */
- gnutls_session_get_data(session, NULL, &connect_idsize);
- if(!connect_idsize) /* gnutls does this for some version combinations */
+ gnutls_session_get_data(session, NULL, &sdata_len);
+ if(!sdata_len) /* gnutls does this for some version combinations */
return CURLE_OK;
- connect_sessionid = malloc(connect_idsize); /* get a buffer for it */
- if(!connect_sessionid)
+ sdata = malloc(sdata_len); /* get a buffer for it */
+ if(!sdata)
return CURLE_OUT_OF_MEMORY;
/* extract session ID to the allocated buffer */
- gnutls_session_get_data(session, connect_sessionid, &connect_idsize);
-
- CURL_TRC_CF(data, cf, "get session id (len=%zu, alpn=%s) and store in cache",
- connect_idsize, alpn ? alpn : "-");
- Curl_ssl_sessionid_lock(data);
- /* store this session id, takes ownership */
- result = Curl_ssl_set_sessionid(cf, data, peer, alpn,
- connect_sessionid, connect_idsize,
- gtls_sessionid_free);
- Curl_ssl_sessionid_unlock(data);
+ gnutls_session_get_data(session, sdata, &sdata_len);
+ earlydata_max = gnutls_record_get_max_early_data_size(session);
+
+ CURL_TRC_CF(data, cf, "get session id (len=%zu, alpn=%s, earlymax=%zu) "
+ "and store in cache", sdata_len, alpn ? alpn : "-",
+ earlydata_max);
+ if(quic_tp && quic_tp_len) {
+ qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len);
+ if(!qtp_clone) {
+ free(sdata);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ result = Curl_ssl_session_create2(sdata, sdata_len,
+ Curl_glts_get_ietf_proto(session),
+ alpn, valid_until, earlydata_max,
+ qtp_clone, quic_tp_len,
+ &sc_session);
+ /* call took ownership of `sdata` and `qtp_clone` */
+ if(!result) {
+ result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
+ /* took ownership of `sc_session` */
+ }
return result;
}
+int Curl_glts_get_ietf_proto(gnutls_session_t session)
+{
+ switch(gnutls_protocol_get_version(session)) {
+ case GNUTLS_SSL3:
+ return CURL_IETF_PROTO_SSL3;
+ case GNUTLS_TLS1_0:
+ return CURL_IETF_PROTO_TLS1;
+ case GNUTLS_TLS1_1:
+ return CURL_IETF_PROTO_TLS1_1;
+ case GNUTLS_TLS1_2:
+ return CURL_IETF_PROTO_TLS1_2;
+ case GNUTLS_TLS1_3:
+ return CURL_IETF_PROTO_TLS1_3;
+ default:
+ return CURL_IETF_PROTO_UNKNOWN;
+ }
+}
+
static CURLcode cf_gtls_update_session_id(struct Curl_cfilter *cf,
struct Curl_easy *data,
gnutls_session_t session)
{
struct ssl_connect_data *connssl = cf->ctx;
- return Curl_gtls_update_session_id(cf, data, session, &connssl->peer,
- connssl->alpn_negotiated);
+ return Curl_gtls_cache_session(cf, data, connssl->peer.scache_key,
+ session, 0, connssl->negotiated.alpn,
+ NULL, 0);
}
static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
@@ -800,6 +830,7 @@ static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype, static CURLcode gtls_client_init(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
+ size_t earlydata_max,
struct gtls_ctx *gtls)
{
struct ssl_primary_config *config = Curl_ssl_cf_get_primary_config(cf);
@@ -853,6 +884,14 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, /* Initialize TLS session as a client */
init_flags = GNUTLS_CLIENT;
+ if(peer->transport == TRNSPRT_QUIC && earlydata_max > 0)
+ init_flags |= GNUTLS_ENABLE_EARLY_DATA | GNUTLS_NO_END_OF_EARLY_DATA;
+ else if(earlydata_max > 0 && earlydata_max != 0xFFFFFFFFUL)
+ /* See https://gitlab.com/gnutls/gnutls/-/issues/1619
+ * We cannot differentiate between a session announcing no earldata
+ * and one announcing 0xFFFFFFFFUL. On TCP+TLS, this is unlikely, but
+ * on QUIC this is common. */
+ init_flags |= GNUTLS_ENABLE_EARLY_DATA;
#if defined(GNUTLS_FORCE_CLIENT_CERT)
init_flags |= GNUTLS_FORCE_CLIENT_CERT;
@@ -874,6 +913,8 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, init_flags |= GNUTLS_NO_STATUS_REQUEST;
#endif
+ CURL_TRC_CF(data, cf, "gnutls_init(flags=%x), earlydata=%zu",
+ init_flags, earlydata_max);
rc = gnutls_init(>ls->session, init_flags);
if(rc != GNUTLS_E_SUCCESS) {
failf(data, "gnutls_init() failed: %d", rc);
@@ -1046,96 +1087,129 @@ static int keylog_callback(gnutls_session_t session, const char *label, return 0;
}
+static CURLcode gtls_on_session_reuse(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct gtls_ssl_backend_data *backend =
+ (struct gtls_ssl_backend_data *)connssl->backend;
+ CURLcode result = CURLE_OK;
+
+ *do_early_data = FALSE;
+ connssl->earlydata_max =
+ gnutls_record_get_max_early_data_size(backend->gtls.session);
+ if((!connssl->earlydata_max || connssl->earlydata_max == 0xFFFFFFFFUL)) {
+ /* Seems to be GnuTLS way to signal no EarlyData in session */
+ CURL_TRC_CF(data, cf, "SSL session does not allow earlydata");
+ }
+ else if(!Curl_alpn_contains_proto(connssl->alpn, 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->state = ssl_connection_deferred;
+ result = Curl_alpn_set_negotiated(cf, data, connssl,
+ (const unsigned char *)scs->alpn,
+ scs->alpn ? strlen(scs->alpn) : 0);
+ *do_early_data = !result;
+ }
+ return result;
+}
+
CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
const unsigned char *alpn, size_t alpn_len,
- struct ssl_connect_data *connssl,
Curl_gtls_ctx_setup_cb *cb_setup,
void *cb_user_data,
- void *ssl_user_data)
+ void *ssl_user_data,
+ Curl_gtls_init_session_reuse_cb *sess_reuse_cb)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct Curl_ssl_session *scs = NULL;
gnutls_datum_t gtls_alpns[5];
size_t gtls_alpns_count = 0;
+ bool gtls_session_setup = FALSE;
CURLcode result;
+ int rc;
DEBUGASSERT(gctx);
-
- result = gtls_client_init(cf, data, peer, gctx);
- if(result)
- return result;
-
- gnutls_session_set_ptr(gctx->session, ssl_user_data);
-
- if(cb_setup) {
- result = cb_setup(cf, data, cb_user_data);
+ /* This might be a reconnect, so we check for a session ID in the cache
+ to speed up things. We need to do this before constructing the gnutls
+ session since we need to set flags depending on the kind of reuse. */
+ if(conn_config->cache_session) {
+ result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs);
if(result)
- return result;
- }
+ goto out;
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
- gnutls_session_set_keylog_function(gctx->session, keylog_callback);
- }
+ if(scs && scs->sdata && scs->sdata_len) {
+ /* we got a cached session, use it! */
- /* This might be a reconnect, so we check for a session ID in the cache
- to speed up things */
- if(conn_config->cache_session) {
- void *ssl_sessionid;
- size_t ssl_idsize;
- char *session_alpn;
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, peer,
- &ssl_sessionid, &ssl_idsize, &session_alpn)) {
- /* we got a session id, use it! */
- int rc;
-
- rc = gnutls_session_set_data(gctx->session, ssl_sessionid, ssl_idsize);
+ result = gtls_client_init(cf, data, peer, scs->earlydata_max, gctx);
+ if(result)
+ goto out;
+ gtls_session_setup = TRUE;
+
+ rc = gnutls_session_set_data(gctx->session, scs->sdata, scs->sdata_len);
if(rc < 0)
- infof(data, "SSL failed to set session ID");
+ infof(data, "SSL session not accepted by GnuTLS, continuing without");
else {
- infof(data, "SSL reusing session ID (size=%zu, alpn=%s)",
- ssl_idsize, session_alpn ? session_alpn : "-");
-#ifdef DEBUGBUILD
- if((ssl_config->earlydata || !!getenv("CURL_USE_EARLYDATA")) &&
-#else
+ infof(data, "SSL reusing session with ALPN '%s'",
+ scs->alpn ? scs->alpn : "-");
if(ssl_config->earlydata &&
-#endif
- !cf->conn->connect_only && connssl &&
- (gnutls_protocol_get_version(gctx->session) == GNUTLS_TLS1_3) &&
- Curl_alpn_contains_proto(connssl->alpn, session_alpn)) {
- connssl->earlydata_max =
- gnutls_record_get_max_early_data_size(gctx->session);
- if((!connssl->earlydata_max ||
- connssl->earlydata_max == 0xFFFFFFFFUL)) {
- /* Seems to be GnuTLS way to signal no EarlyData in session */
- CURL_TRC_CF(data, cf, "TLS session does not allow earlydata");
- }
- else {
- CURL_TRC_CF(data, cf, "TLS session allows %zu earlydata bytes, "
- "reusing ALPN '%s'",
- connssl->earlydata_max, session_alpn);
- connssl->earlydata_state = ssl_earlydata_use;
- connssl->state = ssl_connection_deferred;
- result = Curl_alpn_set_negotiated(cf, data, connssl,
- (const unsigned char *)session_alpn,
- session_alpn ? strlen(session_alpn) : 0);
+ !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);
if(result)
- return result;
+ goto out;
+ }
+ if(do_early_data) {
/* We only try the ALPN protocol the session used before,
* otherwise we might send early data for the wrong protocol */
- gtls_alpns[0].data = (unsigned char *)session_alpn;
- gtls_alpns[0].size = (unsigned)strlen(session_alpn);
- gtls_alpns_count = 1;
+ 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_ssl_sessionid_unlock(data);
+ }
+
+ if(!gtls_session_setup) {
+ result = gtls_client_init(cf, data, peer, 0, gctx);
+ if(result)
+ goto out;
+ }
+
+ gnutls_session_set_ptr(gctx->session, ssl_user_data);
+
+ if(cb_setup) {
+ result = cb_setup(cf, data, cb_user_data);
+ if(result)
+ goto out;
+ }
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+ gnutls_session_set_keylog_function(gctx->session, keylog_callback);
}
/* convert the ALPN string from our arguments to a list of strings that
@@ -1143,19 +1217,21 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, * to the server. nice. */
if(!gtls_alpns_count && alpn && alpn_len) {
size_t i, alen = alpn_len;
- unsigned char *s = (unsigned char *)alpn;
+ unsigned char *salpn = (unsigned char *)alpn;
unsigned char slen;
- for(i = 0; (i < ARRAYSIZE(gtls_alpns)) && alen; ++i) {
- slen = s[0];
+ for(i = 0; (i < CURL_ARRAYSIZE(gtls_alpns)) && alen; ++i) {
+ slen = salpn[0];
if(slen >= alen)
return CURLE_FAILED_INIT;
- gtls_alpns[i].data = s + 1;
+ gtls_alpns[i].data = salpn + 1;
gtls_alpns[i].size = slen;
- s += slen + 1;
+ salpn += slen + 1;
alen -= (size_t)slen + 1;
}
- if(alen) /* not all alpn chars used, wrong format or too many */
- return CURLE_FAILED_INIT;
+ if(alen) { /* not all alpn chars used, wrong format or too many */
+ result = CURLE_FAILED_INIT;
+ goto out;
+ }
gtls_alpns_count = i;
}
@@ -1164,10 +1240,12 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx, gtls_alpns, (unsigned int)gtls_alpns_count,
GNUTLS_ALPN_MANDATORY)) {
failf(data, "failed setting ALPN");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
}
- return CURLE_OK;
+out:
+ Curl_ssl_scache_return(cf, data, peer->scache_key, scs);
+ return result;
}
static CURLcode
@@ -1197,7 +1275,8 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) }
result = Curl_gtls_ctx_init(&backend->gtls, cf, data, &connssl->peer,
- proto.data, proto.len, connssl, NULL, NULL, cf);
+ proto.data, proto.len,
+ NULL, NULL, cf, gtls_on_session_reuse);
if(result)
return result;
@@ -1886,6 +1965,9 @@ gtls_connect_common(struct Curl_cfilter *cf, 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;
@@ -2213,7 +2295,7 @@ out: return ret;
}
-static size_t gtls_version(char *buffer, size_t size)
+size_t Curl_gtls_version(char *buffer, size_t size)
{
return msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL));
}
@@ -2268,8 +2350,7 @@ const struct Curl_ssl Curl_ssl_gnutls = { gtls_init, /* init */
gtls_cleanup, /* cleanup */
- gtls_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
+ Curl_gtls_version, /* version */
gtls_shutdown, /* shutdown */
gtls_data_pending, /* data_pending */
gtls_random, /* random */
@@ -2279,14 +2360,12 @@ const struct Curl_ssl Curl_ssl_gnutls = { Curl_ssl_adjust_pollset, /* adjust_pollset */
gtls_get_internals, /* get_internals */
gtls_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
gtls_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
gtls_recv, /* recv decrypted data */
gtls_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/gtls.h b/libs/libcurl/src/vtls/gtls.h index 4f3c089bc6..7a45758a32 100644 --- a/libs/libcurl/src/vtls/gtls.h +++ b/libs/libcurl/src/vtls/gtls.h @@ -46,6 +46,9 @@ struct ssl_primary_config; struct ssl_config_data;
struct ssl_peer;
struct ssl_connect_data;
+struct Curl_ssl_session;
+
+int Curl_glts_get_ietf_proto(gnutls_session_t session);
struct gtls_shared_creds {
gnutls_certificate_credentials_t creds;
@@ -70,19 +73,26 @@ struct gtls_ctx { BIT(sent_shutdown);
};
+size_t Curl_gtls_version(char *buffer, size_t size);
+
typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf,
struct Curl_easy *data,
void *user_data);
+typedef CURLcode Curl_gtls_init_session_reuse_cb(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data);
+
CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
const unsigned char *alpn, size_t alpn_len,
- struct ssl_connect_data *connssl,
Curl_gtls_ctx_setup_cb *cb_setup,
void *cb_user_data,
- void *ssl_user_data);
+ void *ssl_user_data,
+ Curl_gtls_init_session_reuse_cb *sess_reuse_cb);
CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf,
struct Curl_easy *data,
@@ -96,11 +106,14 @@ CURLcode Curl_gtls_verifyserver(struct Curl_easy *data, const char *pinned_key);
/* Extract TLS session and place in cache, if configured. */
-CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- gnutls_session_t session,
- struct ssl_peer *peer,
- const char *alpn);
+CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ gnutls_session_t session,
+ curl_off_t valid_until,
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len);
extern const struct Curl_ssl Curl_ssl_gnutls;
diff --git a/libs/libcurl/src/vtls/mbedtls.c b/libs/libcurl/src/vtls/mbedtls.c index ae2acdb0a0..c0e98e12c5 100644 --- a/libs/libcurl/src/vtls/mbedtls.c +++ b/libs/libcurl/src/vtls/mbedtls.c @@ -64,6 +64,7 @@ #include "mbedtls.h"
#include "vtls.h"
#include "vtls_int.h"
+#include "vtls_scache.h"
#include "x509asn1.h"
#include "parsedate.h"
#include "connect.h" /* for the connect timeout */
@@ -78,11 +79,8 @@ #include "memdebug.h"
/* ALPN for http2 */
-#ifdef USE_HTTP2
-# undef HAS_ALPN
-# ifdef MBEDTLS_SSL_ALPN
-# define HAS_ALPN
-# endif
+#if defined(USE_HTTP2) && defined(MBEDTLS_SSL_ALPN)
+# define HAS_ALPN_MBEDTLS
#endif
struct mbed_ssl_backend_data {
@@ -96,33 +94,40 @@ struct mbed_ssl_backend_data { #endif
mbedtls_pk_context pk;
mbedtls_ssl_config config;
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_MBEDTLS
const char *protocols[3];
#endif
int *ciphersuites;
+ size_t send_blocked_len;
BIT(initialized); /* mbedtls_ssl_context is initialized */
BIT(sent_shutdown);
+ BIT(send_blocked);
};
/* apply threading? */
-#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \
- defined(_WIN32)
-#define THREADING_SUPPORT
+#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \
+ defined(_WIN32)
+#define HAS_THREADING_SUPPORT
#endif
#ifndef MBEDTLS_ERROR_C
#define mbedtls_strerror(a,b,c) b[0] = 0
#endif
+/* PSA can be used independently of TLS 1.3 */
+#if defined(MBEDTLS_USE_PSA_CRYPTO) && MBEDTLS_VERSION_NUMBER >= 0x03060000
+#define HAS_PSA_SUPPORT
+#endif
+
#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && MBEDTLS_VERSION_NUMBER >= 0x03060000
-#define TLS13_SUPPORT
+#define HAS_TLS13_SUPPORT
#endif
-#if defined(TLS13_SUPPORT) && defined(MBEDTLS_SSL_SESSION_TICKETS)
+#if defined(HAS_TLS13_SUPPORT) && defined(MBEDTLS_SSL_SESSION_TICKETS)
#define HAS_SESSION_TICKETS
#endif
-#ifdef THREADING_SUPPORT
+#ifdef HAS_THREADING_SUPPORT
static mbedtls_entropy_context ts_entropy;
static int entropy_init_initialized = 0;
@@ -160,7 +165,7 @@ static int entropy_func_mutex(void *data, unsigned char *output, size_t len) return ret;
}
-#endif /* THREADING_SUPPORT */
+#endif /* HAS_THREADING_SUPPORT */
#ifdef MBEDTLS_DEBUG
static void mbed_debug(void *context, int level, const char *f_name,
@@ -290,7 +295,7 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, /* ver_min = MBEDTLS_SSL_VERSION_TLS1_2; */
break;
case CURL_SSLVERSION_TLSv1_3:
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
ver_min = MBEDTLS_SSL_VERSION_TLS1_3;
break;
#endif
@@ -304,7 +309,7 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data, case CURL_SSLVERSION_MAX_DEFAULT:
case CURL_SSLVERSION_MAX_NONE:
case CURL_SSLVERSION_MAX_TLSv1_3:
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
ver_max = MBEDTLS_SSL_VERSION_TLS1_3;
break;
#endif
@@ -395,7 +400,7 @@ mbed_set_selected_ciphers(struct Curl_easy *data, if(!selected)
return CURLE_OUT_OF_MEMORY;
-#ifndef TLS13_SUPPORT
+#ifndef HAS_TLS13_SUPPORT
(void) ciphers13, (void) j;
#else
if(!ciphers13) {
@@ -443,7 +448,7 @@ add_ciphers: selected[count++] = id;
}
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
if(ciphers == ciphers13 && ciphers12) {
ciphers = ciphers12;
goto add_ciphers;
@@ -585,7 +590,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_NOT_BUILT_IN;
}
-#ifdef THREADING_SUPPORT
+#ifdef HAS_THREADING_SUPPORT
mbedtls_ctr_drbg_init(&backend->ctr_drbg);
ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, entropy_func_mutex,
@@ -608,7 +613,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) -ret, errorbuf);
return CURLE_FAILED_INIT;
}
-#endif /* THREADING_SUPPORT */
+#endif /* HAS_THREADING_SUPPORT */
/* Load the trusted CA */
mbedtls_x509_crt_init(&backend->cacert);
@@ -802,7 +807,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR;
}
-#ifdef MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED
+#if defined(HAS_SESSION_TICKETS) && MBEDTLS_VERSION_NUMBER >= 0x03060100
/* New in mbedTLS 3.6.1, need to enable, default is now disabled */
mbedtls_ssl_conf_tls13_enable_signal_new_session_tickets(&backend->config,
MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED);
@@ -841,7 +846,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) mbedtls_bio_cf_read,
NULL /* rev_timeout() */);
-#ifndef TLS13_SUPPORT
+#ifndef HAS_TLS13_SUPPORT
if(conn_config->cipher_list) {
CURLcode result = mbed_set_selected_ciphers(data, backend,
conn_config->cipher_list,
@@ -875,29 +880,30 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* Check if there is a cached ID we can/should use here! */
if(ssl_config->primary.cache_session) {
- void *sdata = NULL;
- size_t slen = 0;
+ struct Curl_ssl_session *sc_session = NULL;
+ CURLcode result;
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
- &sdata, &slen, NULL) && slen) {
+ result = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key,
+ &sc_session);
+ if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) {
mbedtls_ssl_session session;
mbedtls_ssl_session_init(&session);
- ret = mbedtls_ssl_session_load(&session, sdata, slen);
+ ret = mbedtls_ssl_session_load(&session, sc_session->sdata,
+ sc_session->sdata_len);
if(ret) {
- failf(data, "error loading cached session: -0x%x", -ret);
+ failf(data, "SSL session error loading: -0x%x", -ret);
}
else {
ret = mbedtls_ssl_set_session(&backend->ssl, &session);
if(ret)
- failf(data, "error setting session: -0x%x", -ret);
+ failf(data, "SSL session error setting: -0x%x", -ret);
else
infof(data, "SSL reusing session ID");
}
mbedtls_ssl_session_free(&session);
}
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session);
}
mbedtls_ssl_conf_ca_chain(&backend->config,
@@ -922,7 +928,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR;
}
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_MBEDTLS
if(connssl->alpn) {
struct alpn_proto_buf proto;
size_t i;
@@ -1100,7 +1106,7 @@ pinnedpubkey_error: }
}
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_MBEDTLS
if(connssl->alpn) {
const char *proto = mbedtls_ssl_get_alpn_protocol(&backend->ssl);
@@ -1115,12 +1121,6 @@ pinnedpubkey_error: return CURLE_OK;
}
-static void mbedtls_session_free(void *session, size_t slen)
-{
- (void)slen;
- free(session);
-}
-
static CURLcode
mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data)
{
@@ -1128,48 +1128,64 @@ mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data) struct mbed_ssl_backend_data *backend =
(struct mbed_ssl_backend_data *)connssl->backend;
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ mbedtls_ssl_session session;
+ bool msession_alloced = FALSE;
+ struct Curl_ssl_session *sc_session = NULL;
+ unsigned char *sdata = NULL;
+ size_t slen = 0;
+ int ietf_tls_id;
CURLcode result = CURLE_OK;
+ int ret;
DEBUGASSERT(backend);
- if(ssl_config->primary.cache_session) {
- int ret;
- mbedtls_ssl_session session;
- unsigned char *sdata = NULL;
- size_t slen = 0;
+ if(!ssl_config->primary.cache_session)
+ return CURLE_OK;
- mbedtls_ssl_session_init(&session);
- ret = mbedtls_ssl_get_session(&backend->ssl, &session);
- if(ret) {
- if(ret != MBEDTLS_ERR_SSL_ALLOC_FAILED)
- mbedtls_ssl_session_free(&session);
- failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret);
- return CURLE_SSL_CONNECT_ERROR;
- }
+ mbedtls_ssl_session_init(&session);
+ ret = mbedtls_ssl_get_session(&backend->ssl, &session);
+ msession_alloced = (ret != MBEDTLS_ERR_SSL_ALLOC_FAILED);
+ if(ret) {
+ failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret);
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
+ }
- mbedtls_ssl_session_save(&session, NULL, 0, &slen);
- if(!slen) {
- failf(data, "failed to serialize session: length is 0");
- }
- else {
- sdata = malloc(slen);
- if(sdata) {
- ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen);
- if(ret) {
- failf(data, "failed to serialize session: -0x%x", -ret);
- }
- else {
- Curl_ssl_sessionid_lock(data);
- result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
- sdata, slen, mbedtls_session_free);
- Curl_ssl_sessionid_unlock(data);
- if(!result)
- sdata = NULL;
- }
- }
- }
- mbedtls_ssl_session_free(&session);
- free(sdata);
+ mbedtls_ssl_session_save(&session, NULL, 0, &slen);
+ if(!slen) {
+ failf(data, "failed to serialize session: length is 0");
+ goto out;
+ }
+
+ sdata = malloc(slen);
+ if(!sdata) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen);
+ if(ret) {
+ failf(data, "failed to serialize session: -0x%x", -ret);
+ goto out;
}
+
+#if MBEDTLS_VERSION_NUMBER >= 0x03020000
+ ietf_tls_id = mbedtls_ssl_get_version_number(&backend->ssl);
+#else
+ ietf_tls_id = CURL_IETF_PROTO_UNKNOWN;
+#endif
+ result = Curl_ssl_session_create(sdata, slen,
+ ietf_tls_id,
+ connssl->negotiated.alpn, 0, 0,
+ &sc_session);
+ sdata = NULL; /* call took ownership */
+ if(!result)
+ result = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key,
+ sc_session);
+
+out:
+ if(msession_alloced)
+ mbedtls_ssl_session_free(&session);
+ free(sdata);
return result;
}
@@ -1184,27 +1200,41 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, (void)data;
DEBUGASSERT(backend);
+ /* mbedtls is picky when a mbedtls_ssl_write) was previously blocked.
+ * It requires to be called with the same amount of bytes again, or it
+ * will lose bytes, e.g. reporting all was sent but they were not.
+ * Remember the blocked length and use that when set. */
+ if(backend->send_blocked) {
+ DEBUGASSERT(backend->send_blocked_len <= len);
+ CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> previously blocked "
+ "on %zu bytes", len, backend->send_blocked_len);
+ len = backend->send_blocked_len;
+ }
+
ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len);
if(ret < 0) {
CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> -0x%04X",
len, -ret);
*curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_WRITE)
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
|| (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
#endif
) ? CURLE_AGAIN : CURLE_SEND_ERROR;
ret = -1;
+ if((*curlcode == CURLE_AGAIN) && !backend->send_blocked) {
+ backend->send_blocked = TRUE;
+ backend->send_blocked_len = len;
+ }
+ }
+ else {
+ CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> %d", len, ret);
+ backend->send_blocked = FALSE;
}
return ret;
}
-static void mbedtls_close_all(struct Curl_easy *data)
-{
- (void)data;
-}
-
static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool send_shutdown, bool *done)
@@ -1260,7 +1290,7 @@ static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf, * WANT_READ, but has not encountered an EAGAIN. */
if(ret == MBEDTLS_ERR_SSL_WANT_READ)
ret = mbedtls_ssl_read(&backend->ssl, buf, sizeof(buf));
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
if(ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
continue;
#endif
@@ -1314,9 +1344,9 @@ static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data) mbedtls_ssl_config_free(&backend->config);
mbedtls_ssl_free(&backend->ssl);
mbedtls_ctr_drbg_free(&backend->ctr_drbg);
-#ifndef THREADING_SUPPORT
+#ifndef HAS_THREADING_SUPPORT
mbedtls_entropy_free(&backend->entropy);
-#endif /* THREADING_SUPPORT */
+#endif /* HAS_THREADING_SUPPORT */
backend->initialized = FALSE;
}
}
@@ -1558,29 +1588,29 @@ static int mbedtls_init(void) {
if(!Curl_mbedtlsthreadlock_thread_setup())
return 0;
-#ifdef THREADING_SUPPORT
+#ifdef HAS_THREADING_SUPPORT
entropy_init_mutex(&ts_entropy);
#endif
-#ifdef TLS13_SUPPORT
+#ifdef HAS_PSA_SUPPORT
{
int ret;
-#ifdef THREADING_SUPPORT
+#ifdef HAS_THREADING_SUPPORT
Curl_mbedtlsthreadlock_lock_function(0);
#endif
ret = psa_crypto_init();
-#ifdef THREADING_SUPPORT
+#ifdef HAS_THREADING_SUPPORT
Curl_mbedtlsthreadlock_unlock_function(0);
#endif
if(ret != PSA_SUCCESS)
return 0;
}
-#endif /* TLS13_SUPPORT */
+#endif /* HAS_PSA_SUPPORT */
return 1;
}
static void mbedtls_cleanup(void)
{
-#ifdef THREADING_SUPPORT
+#ifdef HAS_THREADING_SUPPORT
entropy_cleanup_mutex(&ts_entropy);
#endif
(void)Curl_mbedtlsthreadlock_thread_cleanup();
@@ -1603,7 +1633,6 @@ static CURLcode mbedtls_sha256sum(const unsigned char *input, unsigned char *sha256sum,
size_t sha256len UNUSED_PARAM)
{
- /* TODO: explain this for different mbedtls 2.x vs 3 version */
(void)sha256len;
#if MBEDTLS_VERSION_NUMBER < 0x02070000
mbedtls_sha256(input, inputlen, sha256sum, 0);
@@ -1637,7 +1666,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = { SSLSUPP_CERTINFO |
SSLSUPP_PINNEDPUBKEY |
SSLSUPP_SSL_CTX |
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
SSLSUPP_TLS13_CIPHERSUITES |
#endif
SSLSUPP_HTTPS_PROXY |
@@ -1648,24 +1677,21 @@ const struct Curl_ssl Curl_ssl_mbedtls = { mbedtls_init, /* init */
mbedtls_cleanup, /* cleanup */
mbedtls_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
mbedtls_shutdown, /* shutdown */
mbedtls_data_pending, /* data_pending */
mbedtls_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
mbedtls_connect, /* connect */
mbedtls_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
mbedtls_get_internals, /* get_internals */
mbedtls_close, /* close_one */
- mbedtls_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
mbedtls_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
mbed_recv, /* recv decrypted data */
mbed_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/openssl.c b/libs/libcurl/src/vtls/openssl.c index ed4dd5df1f..97c25ef655 100644 --- a/libs/libcurl/src/vtls/openssl.c +++ b/libs/libcurl/src/vtls/openssl.c @@ -56,6 +56,7 @@ #include "select.h"
#include "vtls.h"
#include "vtls_int.h"
+#include "vtls_scache.h"
#include "vauth/vauth.h"
#include "keylog.h"
#include "strcase.h"
@@ -82,16 +83,15 @@ #include <openssl/tls1.h>
#include <openssl/evp.h>
-#ifdef USE_ECH
+#if defined(HAVE_SSL_SET1_ECH_CONFIG_LIST)
+#define USE_ECH_OPENSSL
+#endif
+
+#ifdef USE_ECH_OPENSSL
# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
# include <openssl/ech.h>
# endif
-# include "curl_base64.h"
-# define ECH_ENABLED(__data__) \
- (__data__->set.tls_ech && \
- !(__data__->set.tls_ech & CURLECH_DISABLE)\
- )
-#endif /* USE_ECH */
+#endif /* USE_ECH_OPENSSL */
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP)
#include <openssl/ocsp.h>
@@ -103,16 +103,19 @@ #include <openssl/engine.h>
#endif
+#if OPENSSL_VERSION_NUMBER >= 0x03000000fL && !defined(OPENSSL_NO_UI_CONSOLE)
+#include <openssl/provider.h>
+#include <openssl/store.h>
+/* this is used in the following conditions to make them easier to read */
+#define OPENSSL_HAS_PROVIDERS
+#endif
+
#include "warnless.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
/* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS
renegotiations when built with BoringSSL. Renegotiating is non-compliant
with HTTP/2 and "an extremely dangerous protocol feature". Beware.
@@ -124,7 +127,7 @@ #error "OPENSSL_VERSION_NUMBER not defined"
#endif
-#ifdef USE_OPENSSL_ENGINE
+#if defined(USE_OPENSSL_ENGINE) || defined(OPENSSL_HAS_PROVIDERS)
#include <openssl/ui.h>
#endif
@@ -172,10 +175,6 @@ #define HAVE_X509_GET0_SIGNATURE 1
#endif
-#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) /* 1.0.2 or later */
-#define HAVE_SSL_GET_SHUTDOWN 1
-#endif
-
#if OPENSSL_VERSION_NUMBER >= 0x10002003L && \
OPENSSL_VERSION_NUMBER <= 0x10002FFFL && \
!defined(OPENSSL_NO_COMP)
@@ -200,18 +199,6 @@ #define FREE_PKEY_PARAM_BIGNUM(name)
#endif
-/*
- * Whether SSL_CTX_set_keylog_callback is available.
- * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287
- * BoringSSL: supported since d28f59c27bac (committed 2015-11-19)
- * LibreSSL: not supported. 3.5.0+ has a stub function that does nothing.
- */
-#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \
- !defined(LIBRESSL_VERSION_NUMBER)) || \
- defined(OPENSSL_IS_BORINGSSL)
-#define HAVE_KEYLOG_CALLBACK
-#endif
-
/* Whether SSL_CTX_set_ciphersuites is available.
* OpenSSL: supported since 1.1.1 (commit a53b5be6a05)
* BoringSSL: no
@@ -739,7 +726,7 @@ static long ossl_bio_cf_ctrl(BIO *bio, int cmd, long num, void *ptr) #ifdef BIO_CTRL_EOF
case BIO_CTRL_EOF:
/* EOF has been reached on input? */
- return (!cf->next || !cf->next->connected);
+ return !cf->next || !cf->next->connected;
#endif
default:
ret = 0;
@@ -960,8 +947,6 @@ static const char *SSL_ERROR_to_str(int err) }
}
-static size_t ossl_version(char *buffer, size_t size);
-
/* Return error string for last OpenSSL error
*/
static char *ossl_strerror(unsigned long error, char *buf, size_t size)
@@ -970,7 +955,7 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size) DEBUGASSERT(size);
*buf = '\0';
- len = ossl_version(buf, size);
+ len = Curl_ossl_version(buf, size);
DEBUGASSERT(len < (size - 2));
if(len < (size - 2)) {
buf += len;
@@ -1015,7 +1000,7 @@ static int passwd_callback(char *buf, int num, int encrypting, */
static bool rand_enough(void)
{
- return (0 != RAND_status());
+ return 0 != RAND_status();
}
static CURLcode ossl_seed(struct Curl_easy *data)
@@ -1071,8 +1056,8 @@ static CURLcode ossl_seed(struct Curl_easy *data) }
infof(data, "libcurl is now using a weak random seed");
- return (rand_enough() ? CURLE_OK :
- CURLE_SSL_CONNECT_ERROR /* confusing error code */);
+ return rand_enough() ? CURLE_OK :
+ CURLE_SSL_CONNECT_ERROR; /* confusing error code */
#endif
}
@@ -1082,6 +1067,9 @@ static CURLcode ossl_seed(struct Curl_easy *data) #ifndef SSL_FILETYPE_PKCS12
#define SSL_FILETYPE_PKCS12 43
#endif
+#ifndef SSL_FILETYPE_PROVIDER
+#define SSL_FILETYPE_PROVIDER 44
+#endif
static int ossl_do_file_type(const char *type)
{
if(!type || !type[0])
@@ -1090,6 +1078,8 @@ static int ossl_do_file_type(const char *type) return SSL_FILETYPE_PEM;
if(strcasecompare(type, "DER"))
return SSL_FILETYPE_ASN1;
+ if(strcasecompare(type, "PROV"))
+ return SSL_FILETYPE_PROVIDER;
if(strcasecompare(type, "ENG"))
return SSL_FILETYPE_ENGINE;
if(strcasecompare(type, "P12"))
@@ -1097,7 +1087,7 @@ static int ossl_do_file_type(const char *type) return -1;
}
-#ifdef USE_OPENSSL_ENGINE
+#if defined(USE_OPENSSL_ENGINE) || defined(OPENSSL_HAS_PROVIDERS)
/*
* Supply default password to the engine user interface conversation.
* The password is passed by OpenSSL engine from ENGINE_load_private_key()
@@ -1145,12 +1135,16 @@ static int ssl_ui_writer(UI *ui, UI_STRING *uis) */
static bool is_pkcs11_uri(const char *string)
{
- return (string && strncasecompare(string, "pkcs11:", 7));
+ return string && strncasecompare(string, "pkcs11:", 7);
}
#endif
static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine);
+#if !defined(USE_OPENSSL_ENGINE) && defined(OPENSSL_HAS_PROVIDERS)
+static CURLcode ossl_set_provider(struct Curl_easy *data,
+ const char *provider);
+#endif
static int use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob,
int type, const char *key_passwd)
@@ -1299,9 +1293,12 @@ int cert_stuff(struct Curl_easy *data, int file_type = ossl_do_file_type(cert_type);
- if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE)) {
+ if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE) ||
+ (file_type == SSL_FILETYPE_PROVIDER)) {
SSL *ssl;
- X509 *x509;
+ X509 *x509 = NULL;
+ EVP_PKEY *pri = NULL;
+ STACK_OF(X509) *ca = NULL;
int cert_done = 0;
int cert_use_result;
@@ -1410,8 +1407,79 @@ int cert_stuff(struct Curl_easy *data, }
}
break;
+#elif defined(OPENSSL_HAS_PROVIDERS)
+ /* fall through to compatible provider */
+ case SSL_FILETYPE_PROVIDER:
+ {
+ /* Implicitly use pkcs11 provider if none was provided and the
+ * cert_file is a PKCS#11 URI */
+ if(!data->state.provider) {
+ if(is_pkcs11_uri(cert_file)) {
+ if(ossl_set_provider(data, "pkcs11") != CURLE_OK) {
+ return 0;
+ }
+ }
+ }
+
+ if(data->state.provider) {
+ /* Load the certificate from the provider */
+ OSSL_STORE_CTX *store = NULL;
+ OSSL_STORE_INFO *info = NULL;
+ X509 *cert = NULL;
+ store = OSSL_STORE_open(cert_file, NULL, NULL, NULL, NULL);
+ if(!store) {
+ failf(data, "Failed to open OpenSSL store: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+ if(OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT) != 1) {
+ failf(data, "Failed to set store preference. Ignoring the error: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ }
+
+ for(info = OSSL_STORE_load(store);
+ info != NULL;
+ info = OSSL_STORE_load(store)) {
+ int ossl_type = OSSL_STORE_INFO_get_type(info);
+
+ if(ossl_type == OSSL_STORE_INFO_CERT) {
+ cert = OSSL_STORE_INFO_get1_CERT(info);
+ }
+ else {
+ failf(data, "Ignoring object not matching our type: %d",
+ ossl_type);
+ OSSL_STORE_INFO_free(info);
+ continue;
+ }
+ OSSL_STORE_INFO_free(info);
+ break;
+ }
+ OSSL_STORE_close(store);
+ if(!cert) {
+ failf(data, "No cert found in the openssl store: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ goto fail;
+ }
+
+ if(SSL_CTX_use_certificate(ctx, cert) != 1) {
+ failf(data, "unable to set client certificate [%s]",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+ X509_free(cert); /* we do not need the handle any more... */
+ }
+ else {
+ failf(data, "crypto provider not set, cannot load certificate");
+ return 0;
+ }
+ }
+ break;
#else
- failf(data, "file type ENG for certificate not implemented");
+ failf(data, "file type ENG nor PROV for certificate not implemented");
return 0;
#endif
@@ -1419,8 +1487,6 @@ 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) {
@@ -1461,8 +1527,7 @@ int cert_stuff(struct Curl_easy *data, PKCS12_PBE_add();
- if(!PKCS12_parse(p12, key_passwd, &pri, &x509,
- &ca)) {
+ if(!PKCS12_parse(p12, key_passwd, &pri, &x509, &ca)) {
failf(data,
"could not parse PKCS12 file, check password, " OSSL_PACKAGE
" error %s",
@@ -1603,10 +1668,96 @@ fail: }
}
break;
+#elif defined(OPENSSL_HAS_PROVIDERS)
+ /* fall through to compatible provider */
+ case SSL_FILETYPE_PROVIDER:
+ {
+ /* Implicitly use pkcs11 provider if none was provided and the
+ * cert_file is a PKCS#11 URI */
+ if(!data->state.provider) {
+ if(is_pkcs11_uri(cert_file)) {
+ if(ossl_set_provider(data, "pkcs11") != CURLE_OK) {
+ return 0;
+ }
+ }
+ }
+
+ if(data->state.provider) {
+ /* Load the private key from the provider */
+ EVP_PKEY *priv_key = NULL;
+ OSSL_STORE_CTX *store = NULL;
+ OSSL_STORE_INFO *info = NULL;
+ UI_METHOD *ui_method =
+ UI_create_method((char *)"curl user interface");
+ if(!ui_method) {
+ failf(data, "unable do create " OSSL_PACKAGE
+ " user-interface method");
+ return 0;
+ }
+ UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL()));
+ UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL()));
+ UI_method_set_reader(ui_method, ssl_ui_reader);
+ UI_method_set_writer(ui_method, ssl_ui_writer);
+
+ store = OSSL_STORE_open(key_file, ui_method, NULL, NULL, NULL);
+ if(!store) {
+ failf(data, "Failed to open OpenSSL store: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+ if(OSSL_STORE_expect(store, OSSL_STORE_INFO_PKEY) != 1) {
+ failf(data, "Failed to set store preference. Ignoring the error: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ }
+
+ for(info = OSSL_STORE_load(store);
+ info != NULL;
+ info = OSSL_STORE_load(store)) {
+ int ossl_type = OSSL_STORE_INFO_get_type(info);
+
+ if(ossl_type == OSSL_STORE_INFO_PKEY) {
+ priv_key = OSSL_STORE_INFO_get1_PKEY(info);
+ }
+ else {
+ failf(data, "Ignoring object not matching our type: %d",
+ ossl_type);
+ OSSL_STORE_INFO_free(info);
+ continue;
+ }
+ OSSL_STORE_INFO_free(info);
+ break;
+ }
+ OSSL_STORE_close(store);
+ UI_destroy_method(ui_method);
+ if(!priv_key) {
+ failf(data, "No private key found in the openssl store: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ goto fail;
+ }
+
+ if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) {
+ failf(data, "unable to set private key [%s]",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ EVP_PKEY_free(priv_key);
+ return 0;
+ }
+ EVP_PKEY_free(priv_key); /* we do not need the handle any more... */
+ }
+ else {
+ failf(data, "crypto provider not set, cannot load private key");
+ return 0;
+ }
+ }
+ break;
#else
- failf(data, "file type ENG for private key not supported");
+ failf(data, "file type ENG nor PROV for private key not implemented");
return 0;
#endif
+
case SSL_FILETYPE_PKCS12:
if(!cert_done) {
failf(data, "file type P12 for private key not supported");
@@ -1874,6 +2025,38 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data) return list;
}
+#if !defined(USE_OPENSSL_ENGINE) && defined(OPENSSL_HAS_PROVIDERS)
+/* Selects an OpenSSL crypto provider
+ */
+static CURLcode ossl_set_provider(struct Curl_easy *data, const char *provider)
+{
+ OSSL_PROVIDER *pkcs11_provider = NULL;
+ char error_buffer[256];
+
+ if(OSSL_PROVIDER_available(NULL, provider)) {
+ /* already loaded through the configuration - no action needed */
+ data->state.provider = TRUE;
+ return CURLE_OK;
+ }
+ if(data->state.provider_failed) {
+ return CURLE_SSL_ENGINE_NOTFOUND;
+ }
+
+ pkcs11_provider = OSSL_PROVIDER_try_load(NULL, provider, 1);
+ if(!pkcs11_provider) {
+ failf(data, "Failed to initialize provider: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ /* Do not attempt to load it again */
+ data->state.provider_failed = TRUE;
+ return CURLE_SSL_ENGINE_NOTFOUND;
+ }
+ data->state.provider = TRUE;
+ return CURLE_OK;
+}
+#endif
+
+
static CURLcode ossl_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool send_shutdown, bool *done)
@@ -2013,13 +2196,6 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) }
}
-static void ossl_session_free(void *sessionid, size_t idsize)
-{
- /* free the ID */
- (void)idsize;
- free(sessionid);
-}
-
/*
* This function is called when the 'data' struct is going away. Close
* down everything and free all resources!
@@ -2688,10 +2864,9 @@ static void ossl_trace(int direction, int ssl_ver, int content_type, /* ====================================================== */
/* Check for OpenSSL 1.0.2 which has ALPN support. */
-#undef HAS_ALPN
#if OPENSSL_VERSION_NUMBER >= 0x10002000L \
&& !defined(OPENSSL_NO_TLSEXT)
-# define HAS_ALPN 1
+# define HAS_ALPN_OPENSSL
#endif
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */
@@ -2873,20 +3048,23 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options, CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
- const struct ssl_peer *peer,
- SSL_SESSION *session)
+ const char *ssl_peer_key,
+ SSL_SESSION *session,
+ int ietf_tls_id,
+ const char *alpn)
{
const struct ssl_config_data *config;
+ unsigned char *der_session_buf = NULL;
CURLcode result = CURLE_OK;
- size_t der_session_size;
- unsigned char *der_session_buf;
- unsigned char *der_session_ptr;
if(!cf || !data)
goto out;
config = Curl_ssl_cf_get_config(cf, data);
if(config->primary.cache_session) {
+ struct Curl_ssl_session *sc_session = NULL;
+ size_t der_session_size;
+ unsigned char *der_session_ptr;
der_session_size = i2d_SSL_SESSION(session, NULL);
if(der_session_size == 0) {
@@ -2903,17 +3081,23 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf, der_session_size = i2d_SSL_SESSION(session, &der_session_ptr);
if(der_session_size == 0) {
result = CURLE_OUT_OF_MEMORY;
- free(der_session_buf);
goto out;
}
- Curl_ssl_sessionid_lock(data);
- result = Curl_ssl_set_sessionid(cf, data, peer, NULL, der_session_buf,
- der_session_size, ossl_session_free);
- Curl_ssl_sessionid_unlock(data);
+ 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);
+ der_session_buf = NULL; /* took ownership of sdata */
+ if(!result) {
+ result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
+ /* took ownership of `sc_session` */
+ }
}
out:
+ free(der_session_buf);
return result;
}
@@ -2922,14 +3106,13 @@ out: */
static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
{
- struct Curl_cfilter *cf;
- struct Curl_easy *data;
- struct ssl_connect_data *connssl;
-
- cf = (struct Curl_cfilter*) SSL_get_app_data(ssl);
- connssl = cf ? cf->ctx : NULL;
- data = connssl ? CF_DATA_CURRENT(cf) : NULL;
- Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid);
+ struct Curl_cfilter *cf = (struct Curl_cfilter*) SSL_get_app_data(ssl);
+ if(cf) {
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
+ struct ssl_connect_data *connssl = cf->ctx;
+ Curl_ossl_add_session(cf, data, connssl->peer.scache_key, ssl_sessionid,
+ SSL_version(ssl), connssl->negotiated.alpn);
+ }
return 0;
}
@@ -3133,9 +3316,9 @@ static CURLcode import_windows_cert_store(struct Curl_easy *data, }
#endif
-static CURLcode populate_x509_store(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- X509_STORE *store)
+static CURLcode ossl_populate_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ X509_STORE *store)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
@@ -3151,7 +3334,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, bool imported_native_ca = FALSE;
bool imported_ca_info_blob = FALSE;
- CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d",
+ CURL_TRC_CF(data, cf, "ossl_populate_x509_store, path=%s, blob=%d",
ssl_cafile ? ssl_cafile : "none", !!ca_info_blob);
if(!store)
return CURLE_OUT_OF_MEMORY;
@@ -3169,7 +3352,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, "CA" /* Intermediate Certification Authorities */
};
size_t i;
- for(i = 0; i < ARRAYSIZE(storeNames); ++i) {
+ for(i = 0; i < CURL_ARRAYSIZE(storeNames); ++i) {
bool imported = FALSE;
result = import_windows_cert_store(data, storeNames[i], store,
&imported);
@@ -3322,8 +3505,8 @@ static void oss_x509_share_free(void *key, size_t key_len, void *p) }
static bool
-cached_x509_store_expired(const struct Curl_easy *data,
- const struct ossl_x509_share *mb)
+ossl_cached_x509_store_expired(const struct Curl_easy *data,
+ const struct ossl_x509_share *mb)
{
const struct ssl_general_config *cfg = &data->set.general_ssl;
if(cfg->ca_cache_timeout < 0)
@@ -3338,8 +3521,8 @@ cached_x509_store_expired(const struct Curl_easy *data, }
static bool
-cached_x509_store_different(struct Curl_cfilter *cf,
- const struct ossl_x509_share *mb)
+ossl_cached_x509_store_different(struct Curl_cfilter *cf,
+ const struct ossl_x509_share *mb)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
if(!mb->CAfile || !conn_config->CAfile)
@@ -3348,8 +3531,8 @@ cached_x509_store_different(struct Curl_cfilter *cf, return strcmp(mb->CAfile, conn_config->CAfile);
}
-static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf,
- const struct Curl_easy *data)
+static X509_STORE *ossl_get_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
{
struct Curl_multi *multi = data->multi;
struct ossl_x509_share *share;
@@ -3360,17 +3543,17 @@ static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, (void *)MPROTO_OSSL_X509_KEY,
sizeof(MPROTO_OSSL_X509_KEY)-1) : NULL;
if(share && share->store &&
- !cached_x509_store_expired(data, share) &&
- !cached_x509_store_different(cf, share)) {
+ !ossl_cached_x509_store_expired(data, share) &&
+ !ossl_cached_x509_store_different(cf, share)) {
store = share->store;
}
return store;
}
-static void set_cached_x509_store(struct Curl_cfilter *cf,
- const struct Curl_easy *data,
- X509_STORE *store)
+static void ossl_set_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data,
+ X509_STORE *store)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct Curl_multi *multi = data->multi;
@@ -3438,16 +3621,16 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, !ssl_config->primary.CRLfile &&
!ssl_config->native_ca_store;
- cached_store = get_cached_x509_store(cf, data);
+ cached_store = ossl_get_cached_x509_store(cf, data);
if(cached_store && cache_criteria_met && X509_STORE_up_ref(cached_store)) {
SSL_CTX_set_cert_store(ssl_ctx, cached_store);
}
else {
X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
- result = populate_x509_store(cf, data, store);
+ result = ossl_populate_x509_store(cf, data, store);
if(result == CURLE_OK && cache_criteria_met) {
- set_cached_x509_store(cf, data, store);
+ ossl_set_cached_x509_store(cf, data, store);
}
}
@@ -3460,7 +3643,7 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf, {
X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
- return populate_x509_store(cf, data, store);
+ return ossl_populate_x509_store(cf, data, store);
}
#endif /* HAVE_SSL_X509_STORE_SHARE */
@@ -3468,7 +3651,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- int transport, /* TCP or QUIC */
const unsigned char *alpn, size_t alpn_len,
Curl_ossl_ctx_setup_cb *cb_setup,
void *cb_user_data,
@@ -3479,12 +3661,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, const char *ciphers;
SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
ctx_option_t ctx_options = 0;
- SSL_SESSION *ssl_session = NULL;
- const unsigned char *der_sessionid = NULL;
- size_t der_sessionid_size = 0;
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- const long int ssl_version_min = conn_config->version;
+ unsigned int ssl_version_min = conn_config->version;
char * const ssl_cert = ssl_config->primary.clientcert;
const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
const char * const ssl_cert_type = ssl_config->cert_type;
@@ -3498,7 +3677,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, ssl_config->certverifyresult = !X509_V_OK;
- switch(transport) {
+ switch(peer->transport) {
case TRNSPRT_TCP:
/* check to see if we have been told to use an explicit SSL/TLS version */
switch(ssl_version_min) {
@@ -3527,6 +3706,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, }
break;
case TRNSPRT_QUIC:
+ ssl_version_min = CURL_SSLVERSION_TLSv1_3;
if(conn_config->version_max &&
(conn_config->version_max != CURL_SSLVERSION_MAX_TLSv1_3)) {
failf(data, "QUIC needs at least TLS version 1.3");
@@ -3542,7 +3722,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, #endif
break;
default:
- failf(data, "unsupported transport %d in SSL init", transport);
+ failf(data, "unsupported transport %d in SSL init", peer->transport);
return CURLE_SSL_CONNECT_ERROR;
}
@@ -3667,7 +3847,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, #endif
if(alpn && alpn_len) {
-#ifdef HAS_ALPN
+#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;
@@ -3690,7 +3870,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, ciphers = conn_config->cipher_list;
if(!ciphers && (peer->transport != TRNSPRT_QUIC))
ciphers = DEFAULT_CIPHER_SELECTION;
- if(ciphers) {
+ if(ciphers && (ssl_version_min < CURL_SSLVERSION_TLSv1_3)) {
if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, ciphers)) {
failf(data, "failed setting cipher list: %s", ciphers);
return CURLE_SSL_CIPHER;
@@ -3701,7 +3881,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
{
const char *ciphers13 = conn_config->cipher_list13;
- if(ciphers13) {
+ if(ciphers13 &&
+ (!conn_config->version_max ||
+ (conn_config->version_max >= CURL_SSLVERSION_MAX_TLSv1_3))) {
if(!SSL_CTX_set_ciphersuites(octx->ssl_ctx, ciphers13)) {
failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13);
return CURLE_SSL_CIPHER;
@@ -3832,7 +4014,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, }
}
-#ifdef USE_ECH
+#ifdef USE_ECH_OPENSSL
if(ECH_ENABLED(data)) {
unsigned char *ech_config = NULL;
size_t ech_config_len = 0;
@@ -3865,7 +4047,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, }
if(SSL_set1_ech_config_list(octx->ssl, ech_config,
ech_config_len) != 1) {
- infof(data, "ECH: SSL_ECH_set1_echconfig failed");
+ infof(data, "ECH: SSL_ECH_set1_ech_config_list failed");
if(data->set.tls_ech & CURLECH_HARD) {
free(ech_config);
return CURLE_SSL_CONNECT_ERROR;
@@ -3880,8 +4062,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, return CURLE_SSL_CONNECT_ERROR;
}
ech_config_len = strlen(data->set.str[STRING_ECH_CONFIG]);
- if(SSL_ech_set1_echconfig(octx->ssl, ech_config, ech_config_len) != 1) {
- infof(data, "ECH: SSL_ECH_set1_echconfig failed");
+ if(SSL_set1_ech_config_list(octx->ssl, ech_config,
+ ech_config_len) != 1) {
+ infof(data, "ECH: SSL_ECH_set1_ech_config_list failed");
if(data->set.tls_ech & CURLECH_HARD)
return CURLE_SSL_CONNECT_ERROR;
}
@@ -3909,19 +4092,11 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, size_t elen = rinfo->echconfiglist_len;
infof(data, "ECH: ECHConfig from DoH HTTPS RR");
-# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
- if(SSL_ech_set1_echconfig(octx->ssl, ecl, elen) != 1) {
- infof(data, "ECH: SSL_ECH_set1_echconfig failed");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
- }
-# else
if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) {
- infof(data, "ECH: SSL_set1_ech_config_list failed (BoringSSL)");
+ infof(data, "ECH: SSL_set1_ech_config_list failed");
if(data->set.tls_ech & CURLECH_HARD)
return CURLE_SSL_CONNECT_ERROR;
}
-# endif
else {
trying_ech_now = 1;
infof(data, "ECH: imported ECHConfigList of length %zu", elen);
@@ -3944,7 +4119,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, if(trying_ech_now && outername) {
infof(data, "ECH: inner: '%s', outer: '%s'",
peer->hostname ? peer->hostname : "NULL", outername);
- result = SSL_ech_set_server_names(octx->ssl,
+ result = SSL_ech_set1_server_names(octx->ssl,
peer->hostname, outername,
0 /* do send outer */);
if(result != 1) {
@@ -3959,38 +4134,42 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, return CURLE_SSL_CONNECT_ERROR;
}
}
-#endif /* USE_ECH */
+#endif /* USE_ECH_OPENSSL */
#endif
octx->reused_session = FALSE;
if(ssl_config->primary.cache_session) {
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, peer, (void **)&der_sessionid,
- &der_sessionid_size, NULL)) {
- /* we got a session id, use it! */
+ struct Curl_ssl_session *sc_session = 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;
+ SSL_SESSION *ssl_session = NULL;
+
+ /* If OpenSSL does not accept the session from the cache, this
+ * is not an error. We just continue without it. */
ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid,
- (long)der_sessionid_size);
+ (long)der_sessionid_size);
if(ssl_session) {
if(!SSL_set_session(octx->ssl, ssl_session)) {
- Curl_ssl_sessionid_unlock(data);
- SSL_SESSION_free(ssl_session);
- failf(data, "SSL: SSL_set_session failed: %s",
+ infof(data, "SSL: SSL_set_session not accepted, "
+ "continuing without: %s",
ossl_strerror(ERR_get_error(), error_buffer,
sizeof(error_buffer)));
- return CURLE_SSL_CONNECT_ERROR;
+ }
+ else {
+ infof(data, "SSL reusing session");
+ octx->reused_session = TRUE;
}
SSL_SESSION_free(ssl_session);
- /* Informational message */
- infof(data, "SSL reusing session ID");
- octx->reused_session = TRUE;
}
else {
- Curl_ssl_sessionid_unlock(data);
- return CURLE_SSL_CONNECT_ERROR;
+ infof(data, "SSL session not accepted by OpenSSL, continuing without");
}
}
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_return(cf, data, peer->scache_key, sc_session);
}
return CURLE_OK;
@@ -4008,7 +4187,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
DEBUGASSERT(octx);
memset(&proto, 0, sizeof(proto));
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_OPENSSL
if(connssl->alpn) {
result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
if(result) {
@@ -4018,7 +4197,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, }
#endif
- result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, TRNSPRT_TCP,
+ result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer,
proto.data, proto.len, NULL, NULL,
ossl_new_session_cb, cf);
if(result)
@@ -4045,7 +4224,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, SSL_set_bio(octx->ssl, bio, bio);
#endif
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_OPENSSL
if(connssl->alpn) {
Curl_alpn_to_proto_str(&proto, connssl->alpn);
infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
@@ -4055,7 +4234,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, return CURLE_OK;
}
-#ifdef USE_ECH
+#ifdef USE_ECH_OPENSSL
/* If we have retry configs, then trace those out */
static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl,
int reason)
@@ -4079,35 +4258,33 @@ static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl, if(!ECH_ENABLED(data))
return;
# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
- rv = SSL_ech_get_retry_config(ssl, &rcs, &rcl);
+ rv = SSL_ech_get1_retry_config(ssl, &rcs, &rcl);
# else
SSL_get0_ech_retry_configs(ssl, &rcs, &rcl);
rv = (int)rcl;
# endif
if(rv && rcs) {
-# define HEXSTR_MAX 800
char *b64str = NULL;
size_t blen = 0;
- result = Curl_base64_encode((const char *)rcs, rcl,
- &b64str, &blen);
- if(!result && b64str)
+ result = Curl_base64_encode((const char *)rcs, rcl, &b64str, &blen);
+ if(!result && b64str) {
infof(data, "ECH: retry_configs %s", b64str);
- free(b64str);
-# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
- rv = SSL_ech_get_status(ssl, &inner, &outer);
- infof(data, "ECH: retry_configs for %s from %s, %d %d",
- inner ? inner : "NULL", outer ? outer : "NULL", reason, rv);
-# else
- rv = SSL_ech_accepted(ssl);
- servername_type = SSL_get_servername_type(ssl);
- inner = SSL_get_servername(ssl, servername_type);
- SSL_get0_ech_name_override(ssl, &outer, &out_name_len);
- /* TODO: get the inner from BoringSSL */
- infof(data, "ECH: retry_configs for %s from %s, %d %d",
- inner ? inner : "NULL", outer ? outer : "NULL", reason, rv);
-# endif
+ free(b64str);
+#if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
+ rv = SSL_ech_get1_status(ssl, &inner, &outer);
+ infof(data, "ECH: retry_configs for %s from %s, %d %d",
+ inner ? inner : "NULL", outer ? outer : "NULL", reason, rv);
+#else
+ rv = SSL_ech_accepted(ssl);
+ servername_type = SSL_get_servername_type(ssl);
+ inner = SSL_get_servername(ssl, servername_type);
+ SSL_get0_ech_name_override(ssl, &outer, &out_name_len);
+ infof(data, "ECH: retry_configs for %s from %s, %d %d",
+ inner ? inner : "NULL", outer ? outer : "NULL", reason, rv);
+#endif
+ }
}
else
infof(data, "ECH: no retry_configs (rv = %d)", rv);
@@ -4230,7 +4407,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)));
}
#endif
-#ifdef USE_ECH
+#ifdef USE_ECH_OPENSSL
else if((lib == ERR_LIB_SSL) &&
# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
(reason == SSL_R_ECH_REQUIRED)) {
@@ -4296,14 +4473,14 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, negotiated_group_name ? negotiated_group_name : "[blank]",
OBJ_nid2sn(psigtype_nid));
-#ifdef USE_ECH
+#ifdef USE_ECH_OPENSSL
# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
if(ECH_ENABLED(data)) {
char *inner = NULL, *outer = NULL;
const char *status = NULL;
int rv;
- rv = SSL_ech_get_status(octx->ssl, &inner, &outer);
+ rv = SSL_ech_get1_status(octx->ssl, &inner, &outer);
switch(rv) {
case SSL_ECH_STATUS_SUCCESS:
status = "succeeded";
@@ -4356,9 +4533,9 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, infof(data, "ECH: result: status is not attempted");
}
# endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */
-#endif /* USE_ECH */
+#endif /* USE_ECH_OPENSSL */
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_OPENSSL
/* Sets data and len to negotiated protocol, len is 0 if no protocol was
* negotiated
*/
@@ -4693,21 +4870,6 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, /* do not do this after Session ID reuse */
result = verifystatus(cf, data, octx);
if(result) {
- /* when verifystatus failed, remove the session id from the cache again
- if present */
- if(!Curl_ssl_cf_is_proxy(cf)) {
- void *old_ssl_sessionid = NULL;
- bool incache;
- Curl_ssl_sessionid_lock(data);
- incache = !(Curl_ssl_getsessionid(cf, data, peer,
- &old_ssl_sessionid, NULL, NULL));
- if(incache) {
- infof(data, "Remove session ID again from cache");
- Curl_ssl_delsessionid(data, old_ssl_sessionid);
- }
- Curl_ssl_sessionid_unlock(data);
- }
-
X509_free(octx->server_cert);
octx->server_cert = NULL;
return result;
@@ -4757,6 +4919,9 @@ 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
+ /* on error, remove sessions we might have in the pool */
+ Curl_ssl_scache_remove_all(cf, data, connssl->peer.scache_key);
return result;
}
@@ -5172,7 +5337,7 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex, #endif
}
-static size_t ossl_version(char *buffer, size_t size)
+size_t Curl_ossl_version(char *buffer, size_t size)
{
#ifdef LIBRESSL_VERSION_NUMBER
#ifdef HAVE_OPENSSL_VERSION
@@ -5267,7 +5432,7 @@ static CURLcode ossl_random(struct Curl_easy *data, }
/* RAND_bytes() returns 1 on success, 0 otherwise. */
rc = RAND_bytes(entropy, (ossl_valsize_t)curlx_uztosi(length));
- return (rc == 1 ? CURLE_OK : CURLE_FAILED_INIT);
+ return rc == 1 ? CURLE_OK : CURLE_FAILED_INIT;
}
#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
@@ -5325,7 +5490,7 @@ const struct Curl_ssl Curl_ssl_openssl = { #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
SSLSUPP_TLS13_CIPHERSUITES |
#endif
-#ifdef USE_ECH
+#ifdef USE_ECH_OPENSSL
SSLSUPP_ECH |
#endif
SSLSUPP_CA_CACHE |
@@ -5336,8 +5501,7 @@ const struct Curl_ssl Curl_ssl_openssl = { ossl_init, /* init */
ossl_cleanup, /* cleanup */
- ossl_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
+ Curl_ossl_version, /* version */
ossl_shutdown, /* shutdown */
ossl_data_pending, /* data_pending */
ossl_random, /* random */
@@ -5351,14 +5515,12 @@ const struct Curl_ssl Curl_ssl_openssl = { ossl_set_engine, /* set_engine */
ossl_set_engine_default, /* set_engine_default */
ossl_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
+ NULL, /* false_start */
#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
ossl_sha256sum, /* sha256sum */
#else
NULL, /* sha256sum */
#endif
- NULL, /* use of data in this connection */
- NULL, /* remote of data from this connection */
ossl_recv, /* recv decrypted data */
ossl_send, /* send data to encrypt */
ossl_get_channel_binding /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/openssl.h b/libs/libcurl/src/vtls/openssl.h index e662e23a8d..bd5aafba96 100644 --- a/libs/libcurl/src/vtls/openssl.h +++ b/libs/libcurl/src/vtls/openssl.h @@ -31,12 +31,27 @@ * This header should only be needed to get included by vtls.c, openssl.c
* and ngtcp2.c
*/
+#include <openssl/opensslv.h>
#include <openssl/ossl_typ.h>
#include <openssl/ssl.h>
#include "urldata.h"
-/* Struct to hold a Curl OpenSSL instance */
+/*
+ * Whether SSL_CTX_set_keylog_callback is available.
+ * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287
+ * BoringSSL: supported since d28f59c27bac (committed 2015-11-19)
+ * LibreSSL: not supported. 3.5.0+ has a stub function that does nothing.
+ */
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \
+ !defined(LIBRESSL_VERSION_NUMBER)) || \
+ defined(OPENSSL_IS_BORINGSSL)
+#define HAVE_KEYLOG_CALLBACK
+#endif
+
+struct ssl_peer;
+
+/* Struct to hold a curl OpenSSL instance */
struct ossl_ctx {
/* these ones requires specific SSL-types */
SSL_CTX* ssl_ctx;
@@ -53,6 +68,8 @@ struct ossl_ctx { BIT(reused_session); /* session-ID was reused for this */
};
+size_t Curl_ossl_version(char *buffer, size_t size);
+
typedef CURLcode Curl_ossl_ctx_setup_cb(struct Curl_cfilter *cf,
struct Curl_easy *data,
void *user_data);
@@ -63,7 +80,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- int transport, /* TCP or QUIC */
const unsigned char *alpn, size_t alpn_len,
Curl_ossl_ctx_setup_cb *cb_setup,
void *cb_user_data,
@@ -94,8 +110,10 @@ CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf, */
CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
- const struct ssl_peer *peer,
- SSL_SESSION *ssl_sessionid);
+ const char *ssl_peer_key,
+ SSL_SESSION *ssl_sessionid,
+ int ietf_tls_id,
+ const char *alpn);
/*
* Get the server cert, verify it and show it, etc., only call failf() if
diff --git a/libs/libcurl/src/vtls/rustls.c b/libs/libcurl/src/vtls/rustls.c index b10328d4aa..ca987d7eed 100644 --- a/libs/libcurl/src/vtls/rustls.c +++ b/libs/libcurl/src/vtls/rustls.c @@ -1076,27 +1076,24 @@ const struct Curl_ssl Curl_ssl_rustls = { SSLSUPP_TLS13_CIPHERSUITES,
sizeof(struct rustls_ssl_backend_data),
- Curl_none_init, /* init */
- Curl_none_cleanup, /* cleanup */
+ NULL, /* init */
+ NULL, /* cleanup */
cr_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
cr_shutdown, /* shutdown */
cr_data_pending, /* data_pending */
cr_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
cr_connect_blocking, /* connect */
cr_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
cr_get_internals, /* get_internals */
cr_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
NULL, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
cr_recv, /* recv decrypted data */
cr_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/schannel.c b/libs/libcurl/src/vtls/schannel.c index 1c9ecc673a..e0a978f1db 100644 --- a/libs/libcurl/src/vtls/schannel.c +++ b/libs/libcurl/src/vtls/schannel.c @@ -41,6 +41,7 @@ #include "schannel_int.h"
#include "vtls.h"
#include "vtls_int.h"
+#include "vtls_scache.h"
#include "strcase.h"
#include "sendf.h"
#include "connect.h" /* for the connect timeout */
@@ -76,7 +77,7 @@ https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx
*/
#if defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_)
-# define HAS_ALPN 1
+# define HAS_ALPN_SCHANNEL
#endif
#ifndef BCRYPT_CHACHA20_POLY1305_ALGORITHM
@@ -148,7 +149,7 @@ */
#ifndef CALG_SHA_256
-# define CALG_SHA_256 0x0000800c
+#define CALG_SHA_256 0x0000800c
#endif
#ifndef PKCS12_NO_PERSIST_KEY
@@ -887,7 +888,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) SecBufferDesc outbuf_desc;
SecBuffer inbuf;
SecBufferDesc inbuf_desc;
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
unsigned char alpn_buffer[128];
#endif
SECURITY_STATUS sspi_status = SEC_E_OK;
@@ -907,7 +908,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) "connect to some servers due to lack of SNI, algorithms, etc.");
}
-#ifdef HAS_ALPN
+#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 &&
@@ -954,9 +955,9 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* check for an existing reusable credential handle */
if(ssl_config->primary.cache_session) {
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
- (void **)&old_cred, NULL, NULL)) {
+ Curl_ssl_scache_lock(data);
+ if(Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key,
+ (void **)&old_cred)) {
backend->cred = old_cred;
DEBUGF(infof(data, "schannel: reusing existing credential handle"));
@@ -966,7 +967,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) "schannel: incremented credential handle refcount = %d",
backend->cred->refcount));
}
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_unlock(data);
}
if(!backend->cred) {
@@ -990,7 +991,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) infof(data, "schannel: using IP address, SNI is not supported by OS.");
}
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
if(backend->use_alpn) {
int cur = 0;
int list_start_index = 0;
@@ -1038,7 +1039,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
}
-#else /* HAS_ALPN */
+#else /* HAS_ALPN_SCHANNEL */
InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
#endif
@@ -1501,12 +1502,11 @@ add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, bool reverse_order, return args->result == CURLE_OK;
}
-static void schannel_session_free(void *sessionid, size_t idsize)
+static void schannel_session_free(void *sessionid)
{
/* this is expected to be called under sessionid lock */
struct Curl_schannel_cred *cred = sessionid;
- (void)idsize;
if(cred) {
cred->refcount--;
if(cred->refcount == 0) {
@@ -1533,7 +1533,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) CURLcode result = CURLE_OK;
SECURITY_STATUS sspi_status = SEC_E_OK;
CERT_CONTEXT *ccert_context = NULL;
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
SecPkgContext_ApplicationProtocol alpn_result;
#endif
@@ -1562,7 +1562,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) return CURLE_SSL_CONNECT_ERROR;
}
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
if(backend->use_alpn) {
sspi_status =
Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
@@ -1599,14 +1599,12 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data) /* save the current session data for possible reuse */
if(ssl_config->primary.cache_session) {
- Curl_ssl_sessionid_lock(data);
+ Curl_ssl_scache_lock(data);
/* Up ref count since call takes ownership */
backend->cred->refcount++;
- result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
- backend->cred,
- sizeof(struct Curl_schannel_cred),
- schannel_session_free);
- Curl_ssl_sessionid_unlock(data);
+ result = Curl_ssl_scache_add_obj(cf, data, connssl->peer.scache_key,
+ backend->cred, schannel_session_free);
+ Curl_ssl_scache_unlock(data);
if(result)
return result;
}
@@ -2275,11 +2273,11 @@ static bool schannel_data_pending(struct Curl_cfilter *cf, DEBUGASSERT(backend);
if(backend->ctxt) /* SSL/TLS is in use */
- return (backend->decdata_offset > 0 ||
- (backend->encdata_offset > 0 && !backend->encdata_is_incomplete) ||
- backend->recv_connection_closed ||
- backend->recv_sspi_close_notify ||
- backend->recv_unrecoverable_err);
+ return backend->decdata_offset > 0 ||
+ (backend->encdata_offset > 0 && !backend->encdata_is_incomplete) ||
+ backend->recv_connection_closed ||
+ backend->recv_sspi_close_notify ||
+ backend->recv_unrecoverable_err;
else
return FALSE;
}
@@ -2369,7 +2367,6 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, Curl_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
if(!result) {
if(written < (ssize_t)outbuf.cbBuffer) {
- /* TODO: handle partial sends */
failf(data, "schannel: failed to send close msg: %s"
" (bytes written: %zd)", curl_easy_strerror(result), written);
result = CURLE_SEND_ERROR;
@@ -2445,9 +2442,9 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data) /* free SSPI Schannel API credential handle */
if(backend->cred) {
- Curl_ssl_sessionid_lock(data);
- schannel_session_free(backend->cred, 0);
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_lock(data);
+ schannel_session_free(backend->cred);
+ Curl_ssl_scache_unlock(data);
backend->cred = NULL;
}
@@ -2469,7 +2466,7 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data) static int schannel_init(void)
{
- return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
+ return Curl_sspi_global_init() == CURLE_OK ? 1 : 0;
}
static void schannel_cleanup(void)
@@ -2798,24 +2795,21 @@ const struct Curl_ssl Curl_ssl_schannel = { schannel_init, /* init */
schannel_cleanup, /* cleanup */
schannel_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
schannel_shutdown, /* shutdown */
schannel_data_pending, /* data_pending */
schannel_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
schannel_connect, /* connect */
schannel_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
schannel_get_internals, /* get_internals */
schannel_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
schannel_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
schannel_recv, /* recv decrypted data */
schannel_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/schannel.h b/libs/libcurl/src/vtls/schannel.h index 438012d434..e901eacfbd 100644 --- a/libs/libcurl/src/vtls/schannel.h +++ b/libs/libcurl/src/vtls/schannel.h @@ -30,7 +30,7 @@ #ifdef _MSC_VER
#pragma warning(push)
-#pragma warning(disable: 4201)
+#pragma warning(disable:4201)
#endif
#include <subauth.h>
#ifdef _MSC_VER
diff --git a/libs/libcurl/src/vtls/sectransp.c b/libs/libcurl/src/vtls/sectransp.c index 765ce36d92..10dfe1e92e 100644 --- a/libs/libcurl/src/vtls/sectransp.c +++ b/libs/libcurl/src/vtls/sectransp.c @@ -38,6 +38,7 @@ #include "multiif.h"
#include "strcase.h"
#include "x509asn1.h"
+#include "vtls_scache.h"
#include "strerror.h"
#include "cipher_suite.h"
@@ -51,6 +52,11 @@ #pragma GCC diagnostic ignored "-Waddress"
#endif
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
#include <limits.h>
#include <Security/Security.h>
@@ -1015,7 +1021,7 @@ failed: return ret;
}
-static void sectransp_session_free(void *sessionid, size_t idsize)
+static void sectransp_session_free(void *sessionid)
{
/* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a
cached session ID inside the Security framework. There is a private
@@ -1023,7 +1029,6 @@ static void sectransp_session_free(void *sessionid, size_t idsize) got your application rejected from the App Store due to the use of a
private API, so the best we can do is free up our own char array that we
created way back in sectransp_connect_step1... */
- (void)idsize;
Curl_safefree(sessionid);
}
@@ -1332,19 +1337,19 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, char *ssl_sessionid;
size_t ssl_sessionid_len;
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
- (void **)&ssl_sessionid, &ssl_sessionid_len,
- NULL)) {
+ Curl_ssl_scache_lock(data);
+ if(Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key,
+ (void **)&ssl_sessionid)) {
/* we got a session id, use it! */
- err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
- Curl_ssl_sessionid_unlock(data);
+ err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid,
+ strlen(ssl_sessionid));
+ Curl_ssl_scache_unlock(data);
if(err != noErr) {
failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err);
return CURLE_SSL_CONNECT_ERROR;
}
- /* Informational message */
- infof(data, "SSL reusing session ID");
+ else
+ infof(data, "SSL reusing session ID");
}
/* If there is not one, then let's make one up! This has to be done prior
to starting the handshake. */
@@ -1358,15 +1363,17 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
if(err != noErr) {
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_unlock(data);
failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err);
return CURLE_SSL_CONNECT_ERROR;
}
- result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
- ssl_sessionid, ssl_sessionid_len,
+ /* This is all a bit weird, as we have not handshaked yet.
+ * I hope this backend will go away soon. */
+ result = Curl_ssl_scache_add_obj(cf, data, connssl->peer.scache_key,
+ (void *)ssl_sessionid,
sectransp_session_free);
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_unlock(data);
if(result)
return result;
}
@@ -1504,9 +1511,11 @@ static CURLcode append_cert_to_array(struct Curl_easy *data, case CURLE_OK:
break;
case CURLE_PEER_FAILED_VERIFICATION:
+ CFRelease(cacert);
return CURLE_SSL_CACERT_BADFILE;
case CURLE_OUT_OF_MEMORY:
default:
+ CFRelease(cacert);
return result;
}
free(certp);
@@ -2424,7 +2433,7 @@ static CURLcode sectransp_shutdown(struct Curl_cfilter *cf, struct st_ssl_backend_data *backend =
(struct st_ssl_backend_data *)connssl->backend;
CURLcode result = CURLE_OK;
- ssize_t nread;
+ ssize_t nread = 0;
char buf[1024];
size_t i;
@@ -2742,32 +2751,33 @@ const struct Curl_ssl Curl_ssl_sectransp = { sizeof(struct st_ssl_backend_data),
- Curl_none_init, /* init */
- Curl_none_cleanup, /* cleanup */
+ NULL, /* init */
+ NULL, /* cleanup */
sectransp_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
sectransp_shutdown, /* shutdown */
sectransp_data_pending, /* data_pending */
sectransp_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
sectransp_connect, /* connect */
sectransp_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
sectransp_get_internals, /* get_internals */
sectransp_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
sectransp_false_start, /* false_start */
sectransp_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
sectransp_recv, /* recv decrypted data */
sectransp_send, /* send data to encrypt */
NULL, /* get_channel_binding */
};
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic pop
+#endif
+
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
diff --git a/libs/libcurl/src/vtls/vtls.c b/libs/libcurl/src/vtls/vtls.c index 0a90bf46fd..182744bb6e 100644 --- a/libs/libcurl/src/vtls/vtls.c +++ b/libs/libcurl/src/vtls/vtls.c @@ -55,6 +55,7 @@ #include "vtls.h" /* generic SSL protos etc */
#include "vtls_int.h"
+#include "vtls_scache.h"
#include "openssl.h" /* OpenSSL versions */
#include "gtls.h" /* GnuTLS versions */
@@ -74,6 +75,7 @@ #include "multiif.h"
#include "timeval.h"
#include "curl_md5.h"
+#include "curl_sha256.h"
#include "warnless.h"
#include "curl_base64.h"
#include "curl_printf.h"
@@ -88,11 +90,6 @@ #include "memdebug.h"
-/* convenience macro to check if this handle is using a shared SSL session */
-#define SSLSESSION_SHARED(data) (data->share && \
- (data->share->specifier & \
- (1<<CURL_LOCK_DATA_SSL_SESSION)))
-
#define CLONE_STRING(var) \
do { \
if(source->var) { \
@@ -426,7 +423,9 @@ int Curl_ssl_init(void) return 1;
init_ssl = TRUE; /* never again */
- return Curl_ssl->init();
+ if(Curl_ssl->init)
+ return Curl_ssl->init();
+ return 1;
}
static bool ssl_prefs_check(struct Curl_easy *data)
@@ -463,9 +462,10 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, if(!ctx)
return NULL;
+ ctx->ssl_impl = Curl_ssl;
ctx->alpn = alpn;
Curl_bufq_init2(&ctx->earlydata, CURL_SSL_EARLY_MAX, 1, BUFQ_OPT_NO_SPARES);
- ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data);
+ ctx->backend = calloc(1, ctx->ssl_impl->sizeof_ssl_backend_data);
if(!ctx->backend) {
free(ctx);
return NULL;
@@ -476,7 +476,7 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data, static void cf_ctx_free(struct ssl_connect_data *ctx)
{
if(ctx) {
- Curl_safefree(ctx->alpn_negotiated);
+ Curl_safefree(ctx->negotiated.alpn);
Curl_bufq_free(&ctx->earlydata);
free(ctx->backend);
free(ctx);
@@ -494,7 +494,7 @@ static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data) /* mark this is being ssl-enabled from here on. */
connssl->state = ssl_connection_negotiating;
- result = Curl_ssl->connect_blocking(cf, data);
+ result = connssl->ssl_impl->connect_blocking(cf, data);
if(!result) {
DEBUGASSERT(connssl->state == ssl_connection_complete);
@@ -507,275 +507,13 @@ 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 Curl_ssl->connect_nonblocking(cf, data, done);
-}
-
-/*
- * Lock shared SSL session data
- */
-void Curl_ssl_sessionid_lock(struct Curl_easy *data)
-{
- if(SSLSESSION_SHARED(data))
- Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
-}
-
-/*
- * Unlock shared SSL session data
- */
-void Curl_ssl_sessionid_unlock(struct Curl_easy *data)
-{
- if(SSLSESSION_SHARED(data))
- Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
-}
-
-/*
- * Check if there is a session ID for the given connection in the cache, and if
- * there is one suitable, it is provided. Returns TRUE when no entry matched.
- */
-bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct ssl_peer *peer,
- void **ssl_sessionid,
- size_t *idsize, /* set 0 if unknown */
- char **palpn)
-{
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
- struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- struct Curl_ssl_session *check;
- size_t i;
- long *general_age;
- bool no_match = TRUE;
-
- *ssl_sessionid = NULL;
- if(palpn)
- *palpn = NULL;
- if(!ssl_config)
- return TRUE;
-
- DEBUGASSERT(ssl_config->primary.cache_session);
-
- if(!ssl_config->primary.cache_session || !data->state.session)
- /* session ID reuse is disabled or the session cache has not been
- setup */
- return TRUE;
-
- /* Lock if shared */
- if(SSLSESSION_SHARED(data))
- general_age = &data->share->sessionage;
- else
- general_age = &data->state.sessionage;
-
- for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
- check = &data->state.session[i];
- if(!check->sessionid)
- /* not session ID means blank entry */
- continue;
- if(strcasecompare(peer->hostname, check->name) &&
- ((!cf->conn->bits.conn_to_host && !check->conn_to_host) ||
- (cf->conn->bits.conn_to_host && check->conn_to_host &&
- strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) &&
- ((!cf->conn->bits.conn_to_port && check->conn_to_port == -1) ||
- (cf->conn->bits.conn_to_port && check->conn_to_port != -1 &&
- cf->conn->conn_to_port == check->conn_to_port)) &&
- (peer->port == check->remote_port) &&
- (peer->transport == check->transport) &&
- strcasecompare(cf->conn->handler->scheme, check->scheme) &&
- match_ssl_primary_config(data, conn_config, &check->ssl_config)) {
- /* yes, we have a session ID! */
- (*general_age)++; /* increase general age */
- check->age = *general_age; /* set this as used in this age */
- *ssl_sessionid = check->sessionid;
- if(idsize)
- *idsize = check->idsize;
- if(palpn)
- *palpn = check->alpn;
- no_match = FALSE;
- break;
- }
- }
-
- CURL_TRC_CF(data, cf, "%s cached session ID for %s://%s:%d",
- no_match ? "No" : "Found",
- cf->conn->handler->scheme, peer->hostname, peer->port);
- return no_match;
-}
-
-/*
- * Kill a single session ID entry in the cache.
- */
-void Curl_ssl_kill_session(struct Curl_ssl_session *session)
-{
- if(session->sessionid) {
- /* defensive check */
-
- /* free the ID the SSL-layer specific way */
- session->sessionid_free(session->sessionid, session->idsize);
-
- session->sessionid = NULL;
- session->sessionid_free = NULL;
- session->age = 0; /* fresh */
-
- free_primary_ssl_config(&session->ssl_config);
-
- Curl_safefree(session->name);
- Curl_safefree(session->conn_to_host);
- Curl_safefree(session->alpn);
- }
-}
-
-/*
- * Delete the given session ID from the cache.
- */
-void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid)
-{
- size_t i;
-
- for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
- struct Curl_ssl_session *check = &data->state.session[i];
-
- if(check->sessionid == ssl_sessionid) {
- Curl_ssl_kill_session(check);
- break;
- }
- }
-}
-
-CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct ssl_peer *peer,
- const char *alpn,
- void *ssl_sessionid,
- size_t idsize,
- Curl_ssl_sessionid_dtor *sessionid_free_cb)
-{
- struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
- size_t i;
- struct Curl_ssl_session *store;
- long oldest_age;
- char *clone_host = NULL;
- char *clone_conn_to_host = NULL;
- char *clone_alpn = NULL;
- int conn_to_port;
- long *general_age;
- void *old_sessionid;
- size_t old_size;
- CURLcode result = CURLE_OUT_OF_MEMORY;
-
- DEBUGASSERT(ssl_sessionid);
- DEBUGASSERT(sessionid_free_cb);
-
- if(!data->state.session) {
- sessionid_free_cb(ssl_sessionid, idsize);
- return CURLE_OK;
- }
-
- if(!Curl_ssl_getsessionid(cf, data, peer, &old_sessionid, &old_size, NULL)) {
- if((old_size == idsize) &&
- ((old_sessionid == ssl_sessionid) ||
- (idsize && !memcmp(old_sessionid, ssl_sessionid, idsize)))) {
- /* the very same */
- sessionid_free_cb(ssl_sessionid, idsize);
- return CURLE_OK;
- }
- Curl_ssl_delsessionid(data, old_sessionid);
- }
-
- store = &data->state.session[0];
- oldest_age = data->state.session[0].age; /* zero if unused */
- DEBUGASSERT(ssl_config->primary.cache_session);
- (void)ssl_config;
-
- clone_host = strdup(peer->hostname);
- if(!clone_host)
- goto out;
-
- if(cf->conn->bits.conn_to_host) {
- clone_conn_to_host = strdup(cf->conn->conn_to_host.name);
- if(!clone_conn_to_host)
- goto out;
- }
-
- clone_alpn = alpn ? strdup(alpn) : NULL;
- if(alpn && !clone_alpn)
- goto out;
-
- if(cf->conn->bits.conn_to_port)
- conn_to_port = cf->conn->conn_to_port;
- else
- conn_to_port = -1;
-
- /* Now we should add the session ID and the hostname to the cache, (remove
- the oldest if necessary) */
-
- /* If using shared SSL session, lock! */
- if(SSLSESSION_SHARED(data)) {
- general_age = &data->share->sessionage;
- }
- else {
- general_age = &data->state.sessionage;
- }
-
- /* find an empty slot for us, or find the oldest */
- for(i = 1; (i < data->set.general_ssl.max_ssl_sessions) &&
- data->state.session[i].sessionid; i++) {
- if(data->state.session[i].age < oldest_age) {
- oldest_age = data->state.session[i].age;
- store = &data->state.session[i];
- }
- }
- if(i == data->set.general_ssl.max_ssl_sessions)
- /* cache is full, we must "kill" the oldest entry! */
- Curl_ssl_kill_session(store);
- else
- store = &data->state.session[i]; /* use this slot */
-
- /* now init the session struct wisely */
- if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) {
- free_primary_ssl_config(&store->ssl_config);
- store->sessionid = NULL; /* let caller free sessionid */
- goto out;
- }
- store->sessionid = ssl_sessionid;
- store->idsize = idsize;
- store->sessionid_free = sessionid_free_cb;
- store->age = *general_age; /* set current age */
- /* free it if there is one already present */
- free(store->name);
- free(store->conn_to_host);
- store->name = clone_host; /* clone hostname */
- clone_host = NULL;
- store->conn_to_host = clone_conn_to_host; /* clone connect to hostname */
- clone_conn_to_host = NULL;
- store->conn_to_port = conn_to_port; /* connect to port number */
- store->alpn = clone_alpn;
- clone_alpn = NULL;
- /* port number */
- store->remote_port = peer->port;
- store->scheme = cf->conn->handler->scheme;
- store->transport = peer->transport;
-
- result = CURLE_OK;
-
-out:
- free(clone_host);
- free(clone_conn_to_host);
- free(clone_alpn);
- if(result) {
- failf(data, "Failed to add Session ID to cache for %s://%s:%d [%s]",
- store->scheme, store->name, store->remote_port,
- Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server");
- sessionid_free_cb(ssl_sessionid, idsize);
- return result;
- }
- CURL_TRC_CF(data, cf, "Added Session ID to cache for %s://%s:%d [%s]",
- store->scheme, store->name, store->remote_port,
- Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server");
- return CURLE_OK;
+ return connssl->ssl_impl->connect_nonblocking(cf, data, done);
}
CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
@@ -788,18 +526,8 @@ CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex, void Curl_ssl_close_all(struct Curl_easy *data)
{
- /* kill the session ID cache if not shared */
- if(data->state.session && !SSLSESSION_SHARED(data)) {
- size_t i;
- for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++)
- /* the single-killer function handles empty table slots */
- Curl_ssl_kill_session(&data->state.session[i]);
-
- /* free the cache data */
- Curl_safefree(data->state.session);
- }
-
- Curl_ssl->close_all(data);
+ if(Curl_ssl->close_all)
+ Curl_ssl->close_all(data);
}
void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -828,43 +556,26 @@ void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, */
CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine)
{
- return Curl_ssl->set_engine(data, engine);
+ if(Curl_ssl->set_engine)
+ return Curl_ssl->set_engine(data, engine);
+ return CURLE_NOT_BUILT_IN;
}
/* Selects the default SSL crypto engine
*/
CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data)
{
- return Curl_ssl->set_engine_default(data);
+ if(Curl_ssl->set_engine_default)
+ return Curl_ssl->set_engine_default(data);
+ return CURLE_NOT_BUILT_IN;
}
/* Return list of OpenSSL crypto engine names. */
struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data)
{
- return Curl_ssl->engines_list(data);
-}
-
-/*
- * This sets up a session ID cache to the specified size. Make sure this code
- * is agnostic to what underlying SSL technology we use.
- */
-CURLcode Curl_ssl_initsessions(struct Curl_easy *data, size_t amount)
-{
- struct Curl_ssl_session *session;
-
- if(data->state.session)
- /* this is just a precaution to prevent multiple inits */
- return CURLE_OK;
-
- session = calloc(amount, sizeof(struct Curl_ssl_session));
- if(!session)
- return CURLE_OUT_OF_MEMORY;
-
- /* store the info in the SSL section */
- data->set.general_ssl.max_ssl_sessions = amount;
- data->state.session = session;
- data->state.sessionage = 1; /* this is brand new */
- return CURLE_OK;
+ if(Curl_ssl->engines_list)
+ return Curl_ssl->engines_list(data);
+ return NULL;
}
static size_t multissl_version(char *buffer, size_t size);
@@ -950,7 +661,7 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data, return result;
}
-/* get 32 bits of random */
+/* get length bytes of randomness */
CURLcode Curl_ssl_random(struct Curl_easy *data,
unsigned char *entropy,
size_t length)
@@ -1185,96 +896,18 @@ end: */
bool Curl_ssl_cert_status_request(void)
{
- return Curl_ssl->cert_status_request();
+ if(Curl_ssl->cert_status_request)
+ return Curl_ssl->cert_status_request();
+ return FALSE;
}
/*
* Check whether the SSL backend supports false start.
*/
-bool Curl_ssl_false_start(struct Curl_easy *data)
-{
- (void)data;
- return Curl_ssl->false_start();
-}
-
-/*
- * Default implementations for unsupported functions.
- */
-
-int Curl_none_init(void)
-{
- return 1;
-}
-
-void Curl_none_cleanup(void)
-{ }
-
-CURLcode Curl_none_shutdown(struct Curl_cfilter *cf UNUSED_PARAM,
- struct Curl_easy *data UNUSED_PARAM,
- bool send_shutdown UNUSED_PARAM,
- bool *done)
-{
- (void)data;
- (void)cf;
- (void)send_shutdown;
- /* Every SSL backend should have a shutdown implementation. Until we
- * have implemented that, we put this fake in place. */
- *done = TRUE;
- return CURLE_OK;
-}
-
-int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
- (void)cf;
- (void)data;
- return -1;
-}
-
-void Curl_none_close_all(struct Curl_easy *data UNUSED_PARAM)
-{
- (void)data;
-}
-
-void Curl_none_session_free(void *ptr UNUSED_PARAM)
-{
- (void)ptr;
-}
-
-bool Curl_none_data_pending(struct Curl_cfilter *cf UNUSED_PARAM,
- const struct Curl_easy *data UNUSED_PARAM)
-{
- (void)cf;
- (void)data;
- return 0;
-}
-
-bool Curl_none_cert_status_request(void)
-{
- return FALSE;
-}
-
-CURLcode Curl_none_set_engine(struct Curl_easy *data UNUSED_PARAM,
- const char *engine UNUSED_PARAM)
-{
- (void)data;
- (void)engine;
- return CURLE_NOT_BUILT_IN;
-}
-
-CURLcode Curl_none_set_engine_default(struct Curl_easy *data UNUSED_PARAM)
-{
- (void)data;
- return CURLE_NOT_BUILT_IN;
-}
-
-struct curl_slist *Curl_none_engines_list(struct Curl_easy *data UNUSED_PARAM)
-{
- (void)data;
- return (struct curl_slist *)NULL;
-}
-
-bool Curl_none_false_start(void)
+bool Curl_ssl_false_start(void)
{
+ if(Curl_ssl->false_start)
+ return Curl_ssl->false_start();
return FALSE;
}
@@ -1282,7 +915,9 @@ static int multissl_init(void) {
if(multissl_setup(NULL))
return 1;
- return Curl_ssl->init();
+ if(Curl_ssl->init)
+ return Curl_ssl->init();
+ return 1;
}
static CURLcode multissl_connect(struct Curl_cfilter *cf,
@@ -1351,26 +986,23 @@ static const struct Curl_ssl Curl_ssl_multi = { (size_t)-1, /* something insanely large to be on the safe side */
multissl_init, /* init */
- Curl_none_cleanup, /* cleanup */
+ NULL, /* cleanup */
multissl_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
- Curl_none_shutdown, /* shutdown */
- Curl_none_data_pending, /* data_pending */
+ NULL, /* shutdown */
+ NULL, /* data_pending */
NULL, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
multissl_connect, /* connect */
multissl_connect_nonblocking, /* connect_nonblocking */
- multissl_adjust_pollset, /* adjust_pollset */
+ multissl_adjust_pollset, /* adjust_pollset */
multissl_get_internals, /* get_internals */
multissl_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
NULL, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
multissl_recv_plain, /* recv decrypted data */
multissl_send_plain, /* send data to encrypt */
NULL, /* get_channel_binding */
@@ -1432,7 +1064,8 @@ void Curl_ssl_cleanup(void) {
if(init_ssl) {
/* only cleanup if we did a previous init */
- Curl_ssl->cleanup();
+ if(Curl_ssl->cleanup)
+ Curl_ssl->cleanup();
#if defined(CURL_WITH_MULTI_SSL)
Curl_ssl = &Curl_ssl_multi;
#endif
@@ -1482,8 +1115,8 @@ static size_t multissl_version(char *buffer, size_t size) static int multissl_setup(const struct Curl_ssl *backend)
{
- const char *env;
- char *env_tmp;
+ int i;
+ char *env;
if(Curl_ssl != &Curl_ssl_multi)
return 1;
@@ -1496,25 +1129,31 @@ static int multissl_setup(const struct Curl_ssl *backend) if(!available_backends[0])
return 1;
- env = env_tmp = curl_getenv("CURL_SSL_BACKEND");
-#ifdef CURL_DEFAULT_SSL_BACKEND
- if(!env)
- env = CURL_DEFAULT_SSL_BACKEND;
-#endif
+ env = curl_getenv("CURL_SSL_BACKEND");
if(env) {
- int i;
for(i = 0; available_backends[i]; i++) {
if(strcasecompare(env, available_backends[i]->info.name)) {
Curl_ssl = available_backends[i];
- free(env_tmp);
+ free(env);
return 0;
}
}
}
+#ifdef CURL_DEFAULT_SSL_BACKEND
+ for(i = 0; available_backends[i]; i++) {
+ if(strcasecompare(CURL_DEFAULT_SSL_BACKEND,
+ available_backends[i]->info.name)) {
+ Curl_ssl = available_backends[i];
+ free(env);
+ return 0;
+ }
+ }
+#endif
+
/* Fall back to first available backend */
Curl_ssl = available_backends[0];
- free(env_tmp);
+ free(env);
return 0;
}
@@ -1565,11 +1204,12 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, void Curl_ssl_peer_cleanup(struct ssl_peer *peer)
{
+ Curl_safefree(peer->sni);
if(peer->dispname != peer->hostname)
free(peer->dispname);
- free(peer->sni);
- free(peer->hostname);
- peer->hostname = peer->sni = peer->dispname = NULL;
+ peer->dispname = NULL;
+ Curl_safefree(peer->hostname);
+ Curl_safefree(peer->scache_key);
peer->type = CURL_SSL_PEER_DNS;
}
@@ -1577,7 +1217,7 @@ static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) {
struct ssl_connect_data *connssl = cf->ctx;
if(connssl) {
- Curl_ssl->close(cf, data);
+ connssl->ssl_impl->close(cf, data);
connssl->state = ssl_connection_none;
Curl_ssl_peer_cleanup(&connssl->peer);
}
@@ -1603,7 +1243,9 @@ static ssl_peer_type get_peer_type(const char *hostname) return CURL_SSL_PEER_DNS;
}
-CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf,
+CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
+ struct Curl_cfilter *cf,
+ const char *tls_id,
int transport)
{
const char *ehostname, *edispname;
@@ -1665,7 +1307,8 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf, peer->sni[len] = 0;
}
}
- result = CURLE_OK;
+
+ result = Curl_ssl_peer_key_make(cf, peer, tls_id, &peer->scache_key);
out:
if(result)
@@ -1728,7 +1371,9 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, *done = FALSE;
if(!connssl->peer.hostname) {
- result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP);
+ char tls_id[80];
+ connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1);
+ result = Curl_ssl_peer_init(&connssl->peer, cf, tls_id, TRNSPRT_TCP);
if(result)
goto out;
}
@@ -1743,7 +1388,8 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf, if(!result && *done) {
cf->connected = TRUE;
- connssl->handshake_done = Curl_now();
+ if(connssl->state == ssl_connection_complete)
+ connssl->handshake_done = Curl_now();
/* Connection can be deferred when sending early data */
DEBUGASSERT(connssl->state == ssl_connection_complete ||
connssl->state == ssl_connection_deferred);
@@ -1757,11 +1403,13 @@ out: static bool ssl_cf_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
+ struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
bool result;
CF_DATA_SAVE(save, cf, data);
- if(Curl_ssl->data_pending(cf, data))
+ if(connssl->ssl_impl->data_pending &&
+ connssl->ssl_impl->data_pending(cf, data))
result = TRUE;
else
result = cf->next->cft->has_data_pending(cf->next, data);
@@ -1773,6 +1421,7 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len,
bool eos, CURLcode *err)
{
+ struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
ssize_t nwritten = 0;
@@ -1781,7 +1430,7 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf, *err = CURLE_OK;
if(len > 0) {
CF_DATA_SAVE(save, cf, data);
- nwritten = Curl_ssl->send_plain(cf, data, buf, len, err);
+ nwritten = connssl->ssl_impl->send_plain(cf, data, buf, len, err);
CF_DATA_RESTORE(cf, save);
}
return nwritten;
@@ -1791,12 +1440,13 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len,
CURLcode *err)
{
+ struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
ssize_t nread;
CF_DATA_SAVE(save, cf, data);
*err = CURLE_OK;
- nread = Curl_ssl->recv_plain(cf, data, buf, len, err);
+ nread = connssl->ssl_impl->recv_plain(cf, data, buf, len, err);
if(nread > 0) {
DEBUGASSERT((size_t)nread <= len);
}
@@ -1814,14 +1464,15 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data,
bool *done)
{
+ struct ssl_connect_data *connssl = cf->ctx;
CURLcode result = CURLE_OK;
*done = TRUE;
- if(!cf->shutdown) {
+ if(!cf->shutdown && Curl_ssl->shut_down) {
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
- result = Curl_ssl->shut_down(cf, data, TRUE, done);
+ result = connssl->ssl_impl->shut_down(cf, data, TRUE, done);
CURL_TRC_CF(data, cf, "cf_shutdown -> %d, done=%d", result, *done);
CF_DATA_RESTORE(cf, save);
cf->shutdown = (result || *done);
@@ -1833,42 +1484,14 @@ static void ssl_cf_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
struct easy_pollset *ps)
{
+ struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
- Curl_ssl->adjust_pollset(cf, data, ps);
+ connssl->ssl_impl->adjust_pollset(cf, data, ps);
CF_DATA_RESTORE(cf, save);
}
-static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- int event, int arg1, void *arg2)
-{
- struct cf_call_data save;
-
- (void)arg1;
- (void)arg2;
- switch(event) {
- case CF_CTRL_DATA_ATTACH:
- if(Curl_ssl->attach_data) {
- CF_DATA_SAVE(save, cf, data);
- Curl_ssl->attach_data(cf, data);
- CF_DATA_RESTORE(cf, save);
- }
- break;
- case CF_CTRL_DATA_DETACH:
- if(Curl_ssl->detach_data) {
- CF_DATA_SAVE(save, cf, data);
- Curl_ssl->detach_data(cf, data);
- CF_DATA_RESTORE(cf, save);
- }
- break;
- default:
- break;
- }
- return CURLE_OK;
-}
-
static CURLcode ssl_cf_query(struct Curl_cfilter *cf,
struct Curl_easy *data,
int query, int *pres1, void *pres2)
@@ -1893,28 +1516,9 @@ static CURLcode ssl_cf_query(struct Curl_cfilter *cf, static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data,
bool *input_pending)
{
- struct cf_call_data save;
- int result;
/*
* This function tries to determine connection status.
- *
- * Return codes:
- * 1 means the connection is still in place
- * 0 means the connection has been closed
- * -1 means the connection status is unknown
*/
- CF_DATA_SAVE(save, cf, data);
- result = Curl_ssl->check_cxn(cf, data);
- CF_DATA_RESTORE(cf, save);
- if(result > 0) {
- *input_pending = TRUE;
- return TRUE;
- }
- if(result == 0) {
- *input_pending = FALSE;
- return FALSE;
- }
- /* ssl backend does not know */
return cf->next ?
cf->next->cft->is_alive(cf->next, data, input_pending) :
FALSE; /* pessimistic in absence of data */
@@ -1933,7 +1537,7 @@ struct Curl_cftype Curl_cft_ssl = { ssl_cf_data_pending,
ssl_cf_send,
ssl_cf_recv,
- ssl_cf_cntrl,
+ Curl_cf_def_cntrl,
cf_ssl_is_alive,
Curl_cf_def_conn_keep_alive,
ssl_cf_query,
@@ -1954,7 +1558,7 @@ struct Curl_cftype Curl_cft_ssl_proxy = { ssl_cf_data_pending,
ssl_cf_send,
ssl_cf_recv,
- ssl_cf_cntrl,
+ Curl_cf_def_cntrl,
cf_ssl_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_def_query,
@@ -2090,9 +1694,10 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, /* get first SSL filter in chain, if any is present */
cf = get_ssl_filter(data->conn->cfilter[sockindex]);
if(cf) {
+ struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
- result = Curl_ssl->get_internals(cf->ctx, info);
+ result = connssl->ssl_impl->get_internals(cf->ctx, info);
CF_DATA_RESTORE(cf, save);
}
}
@@ -2125,7 +1730,7 @@ static CURLcode vtls_shutdown_blocking(struct Curl_cfilter *cf, return CURLE_OPERATION_TIMEDOUT;
}
- result = Curl_ssl->shut_down(cf, data, send_shutdown, done);
+ result = connssl->ssl_impl->shut_down(cf, data, send_shutdown, done);
if(result ||*done)
goto out;
@@ -2277,28 +1882,28 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, #endif
;
- if(connssl->alpn_negotiated) {
+ if(connssl->negotiated.alpn) {
/* When we ask for a specific ALPN protocol, we need the confirmation
* of it by the server, as we have installed protocol handler and
* connection filter chain for exactly this protocol. */
if(!proto_len) {
failf(data, "ALPN: asked for '%s' from previous session, "
"but server did not confirm it. Refusing to continue.",
- connssl->alpn_negotiated);
+ connssl->negotiated.alpn);
result = CURLE_SSL_CONNECT_ERROR;
goto out;
}
- else if((strlen(connssl->alpn_negotiated) != proto_len) ||
- memcmp(connssl->alpn_negotiated, proto, proto_len)) {
+ else if((strlen(connssl->negotiated.alpn) != proto_len) ||
+ memcmp(connssl->negotiated.alpn, proto, proto_len)) {
failf(data, "ALPN: asked for '%s' from previous session, but server "
"selected '%.*s'. Refusing to continue.",
- connssl->alpn_negotiated, (int)proto_len, proto);
+ connssl->negotiated.alpn, (int)proto_len, proto);
result = CURLE_SSL_CONNECT_ERROR;
goto out;
}
/* ALPN is exactly what we asked for, done. */
infof(data, "ALPN: server confirmed to use '%s'",
- connssl->alpn_negotiated);
+ connssl->negotiated.alpn);
goto out;
}
@@ -2309,11 +1914,11 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, result = CURLE_SSL_CONNECT_ERROR;
goto out;
}
- connssl->alpn_negotiated = malloc(proto_len + 1);
- if(!connssl->alpn_negotiated)
+ connssl->negotiated.alpn = malloc(proto_len + 1);
+ if(!connssl->negotiated.alpn)
return CURLE_OUT_OF_MEMORY;
- memcpy(connssl->alpn_negotiated, proto, proto_len);
- connssl->alpn_negotiated[proto_len] = 0;
+ memcpy(connssl->negotiated.alpn, proto, proto_len);
+ connssl->negotiated.alpn[proto_len] = 0;
}
if(proto && proto_len) {
@@ -2336,8 +1941,8 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf, else {
*palpn = CURL_HTTP_VERSION_NONE;
failf(data, "unsupported ALPN protocol: '%.*s'", (int)proto_len, proto);
- /* TODO: do we want to fail this? Previous code just ignored it and
- * some vtls backends even ignore the return code of this function. */
+ /* Previous code just ignored it and some vtls backends even ignore the
+ * return code of this function. */
/* return CURLE_NOT_BUILT_IN; */
goto out;
}
diff --git a/libs/libcurl/src/vtls/vtls.h b/libs/libcurl/src/vtls/vtls.h index 171a5d8e55..3c438a2dba 100644 --- a/libs/libcurl/src/vtls/vtls.h +++ b/libs/libcurl/src/vtls/vtls.h @@ -28,7 +28,9 @@ struct connectdata;
struct ssl_config_data;
struct ssl_primary_config;
-struct Curl_ssl_session;
+struct Curl_cfilter;
+struct Curl_easy;
+struct dynbuf;
#define SSLSUPP_CA_PATH (1<<0) /* supports CAPATH */
#define SSLSUPP_CERTINFO (1<<1) /* supports CURLOPT_CERTINFO */
@@ -41,6 +43,14 @@ struct Curl_ssl_session; #define SSLSUPP_CA_CACHE (1<<8)
#define SSLSUPP_CIPHER_LIST (1<<9) /* supports TLS 1.0-1.2 ciphersuites */
+#ifdef USE_ECH
+# include "curl_base64.h"
+# define ECH_ENABLED(__data__) \
+ (__data__->set.tls_ech && \
+ !(__data__->set.tls_ech & CURLECH_DISABLE)\
+ )
+#endif /* USE_ECH */
+
#define ALPN_ACCEPTED "ALPN: server accepted "
#define VTLS_INFOF_NO_ALPN \
@@ -55,9 +65,31 @@ struct Curl_ssl_session; #define VTLS_INFOF_ALPN_DEFERRED \
"ALPN: deferred handshake for early data using '%.*s'."
-/* Curl_multi SSL backend-specific data; declared differently by each SSL
- backend */
-struct Curl_cfilter;
+/* IETF defined version numbers used in TLS protocol negotiation */
+#define CURL_IETF_PROTO_UNKNOWN 0x0
+#define CURL_IETF_PROTO_SSL3 0x0300
+#define CURL_IETF_PROTO_TLS1 0x0301
+#define CURL_IETF_PROTO_TLS1_1 0x0302
+#define CURL_IETF_PROTO_TLS1_2 0x0303
+#define CURL_IETF_PROTO_TLS1_3 0x0304
+#define CURL_IETF_PROTO_DTLS1 0xFEFF
+#define CURL_IETF_PROTO_DTLS1_2 0xFEFD
+
+typedef enum {
+ CURL_SSL_PEER_DNS,
+ CURL_SSL_PEER_IPV4,
+ CURL_SSL_PEER_IPV6
+} ssl_peer_type;
+
+struct ssl_peer {
+ char *hostname; /* hostname for verification */
+ char *dispname; /* display version of hostname */
+ char *sni; /* SNI version of hostname or NULL if not usable */
+ char *scache_key; /* for lookups in session cache */
+ ssl_peer_type type; /* type of the peer information */
+ int port; /* port we are talking to */
+ int transport; /* one of TRNSPRT_* defines */
+};
CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
const curl_ssl_backend ***avail);
@@ -113,7 +145,9 @@ void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy); * Init SSL peer information for filter. Can be called repeatedly.
*/
CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
- struct Curl_cfilter *cf, int transport);
+ struct Curl_cfilter *cf,
+ const char *tls_id,
+ int transport);
/**
* Free all allocated data and reset peer information.
*/
@@ -130,8 +164,6 @@ CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine); CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data);
struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data);
-/* init the SSL session ID cache */
-CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t);
void Curl_ssl_version(char *buffer, size_t size);
/* Certificate information list handling. */
@@ -147,33 +179,6 @@ CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data, int certnum, /* Functions to be used by SSL library adaptation functions */
-/* Lock session cache mutex.
- * Call this before calling other Curl_ssl_*session* functions
- * Caller should unlock this mutex as soon as possible, as it may block
- * other SSL connection from making progress.
- * The purpose of explicitly locking SSL session cache data is to allow
- * individual SSL engines to manage session lifetime in their specific way.
- */
-void Curl_ssl_sessionid_lock(struct Curl_easy *data);
-
-/* Unlock session cache mutex */
-void Curl_ssl_sessionid_unlock(struct Curl_easy *data);
-
-/* Kill a single session ID entry in the cache
- * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
- * This will call engine-specific curlssl_session_free function, which must
- * take sessionid object ownership from sessionid cache
- * (e.g. decrement refcount).
- */
-void Curl_ssl_kill_session(struct Curl_ssl_session *session);
-/* delete a session from the cache
- * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
- * This will call engine-specific curlssl_session_free function, which must
- * take sessionid object ownership from sessionid cache
- * (e.g. decrement refcount).
- */
-void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid);
-
/* get N random bytes into the buffer */
CURLcode Curl_ssl_random(struct Curl_easy *data, unsigned char *buffer,
size_t length);
@@ -184,7 +189,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, bool Curl_ssl_cert_status_request(void);
-bool Curl_ssl_false_start(struct Curl_easy *data);
+bool Curl_ssl_false_start(void);
/* The maximum size of the SSL channel binding is 85 bytes, as defined in
* RFC 5929, Section 4.1. The 'tls-server-end-point:' prefix is 21 bytes long,
@@ -265,12 +270,10 @@ extern struct Curl_cftype Curl_cft_ssl_proxy; #define Curl_ssl_set_engine(x,y) CURLE_NOT_BUILT_IN
#define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN
#define Curl_ssl_engines_list(x) NULL
-#define Curl_ssl_initsessions(x,y) CURLE_OK
#define Curl_ssl_free_certinfo(x) Curl_nop_stmt
-#define Curl_ssl_kill_session(x) Curl_nop_stmt
#define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN)
#define Curl_ssl_cert_status_request() FALSE
-#define Curl_ssl_false_start(a) FALSE
+#define Curl_ssl_false_start() FALSE
#define Curl_ssl_get_internals(a,b,c,d) NULL
#define Curl_ssl_supports(a,b) FALSE
#define Curl_ssl_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN
diff --git a/libs/libcurl/src/vtls/vtls_int.h b/libs/libcurl/src/vtls/vtls_int.h index 250273ef9d..bb37346396 100644 --- a/libs/libcurl/src/vtls/vtls_int.h +++ b/libs/libcurl/src/vtls/vtls_int.h @@ -26,9 +26,11 @@ #include "curl_setup.h"
#include "cfilters.h"
#include "urldata.h"
+#include "vtls.h"
#ifdef USE_SSL
+struct Curl_ssl;
struct ssl_connect_data;
/* see https://www.iana.org/assignments/tls-extensiontype-values/ */
@@ -103,12 +105,15 @@ typedef enum { /* Information in each SSL cfilter context: cf->ctx */
struct ssl_connect_data {
- struct ssl_peer peer;
+ const struct Curl_ssl *ssl_impl; /* TLS backend for this filter */
+ struct ssl_peer peer; /* peer the filter talks to */
const struct alpn_spec *alpn; /* ALPN to use or NULL for none */
void *backend; /* vtls backend specific props */
struct cf_call_data call_data; /* data handle used in current call */
struct curltime handshake_done; /* time when handshake finished */
- char *alpn_negotiated; /* negotiated ALPN value or NULL */
+ struct {
+ char *alpn; /* ALPN value or NULL */
+ } negotiated;
struct bufq earlydata; /* earlydata to be send to peer */
size_t earlydata_max; /* max earlydata allowed by peer */
size_t earlydata_skip; /* sending bytes to skip when earlydata
@@ -142,7 +147,6 @@ struct Curl_ssl { void (*cleanup)(void);
size_t (*version)(char *buffer, size_t size);
- int (*check_cxn)(struct Curl_cfilter *cf, struct Curl_easy *data);
CURLcode (*shut_down)(struct Curl_cfilter *cf, struct Curl_easy *data,
bool send_shutdown, bool *done);
bool (*data_pending)(struct Curl_cfilter *cf,
@@ -174,10 +178,6 @@ struct Curl_ssl { bool (*false_start)(void);
CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen,
unsigned char *sha256sum, size_t sha256sumlen);
-
- bool (*attach_data)(struct Curl_cfilter *cf, struct Curl_easy *data);
- void (*detach_data)(struct Curl_cfilter *cf, struct Curl_easy *data);
-
ssize_t (*recv_plain)(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *code);
ssize_t (*send_plain)(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -190,66 +190,14 @@ struct Curl_ssl { extern const struct Curl_ssl *Curl_ssl;
-
-int Curl_none_init(void);
-void Curl_none_cleanup(void);
-CURLcode Curl_none_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data,
- bool send_shutdown, bool *done);
-int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data);
-void Curl_none_close_all(struct Curl_easy *data);
-void Curl_none_session_free(void *ptr);
-bool Curl_none_data_pending(struct Curl_cfilter *cf,
- const struct Curl_easy *data);
-bool Curl_none_cert_status_request(void);
-CURLcode Curl_none_set_engine(struct Curl_easy *data, const char *engine);
-CURLcode Curl_none_set_engine_default(struct Curl_easy *data);
-struct curl_slist *Curl_none_engines_list(struct Curl_easy *data);
-bool Curl_none_false_start(void);
void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
- struct easy_pollset *ps);
+ struct easy_pollset *ps);
/**
* Get the SSL filter below the given one or NULL if there is none.
*/
bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf);
-/* extract a session ID
- * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
- * Caller must make sure that the ownership of returned sessionid object
- * is properly taken (e.g. its refcount is incremented
- * under sessionid mutex).
- * @param cf the connection filter wanting to use it
- * @param data the transfer involved
- * @param peer the peer the filter wants to talk to
- * @param sessionid on return the TLS session
- * @param idsize on return the size of the TLS session data
- * @param palpn on return the ALPN string used by the session,
- * set to NULL when not interested
- */
-bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct ssl_peer *peer,
- void **ssl_sessionid,
- size_t *idsize, /* set 0 if unknown */
- char **palpn);
-
-/* Set a TLS session ID for `peer`. Replaces an existing session ID if
- * not already the same.
- * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
- * Call takes ownership of `ssl_sessionid`, using `sessionid_free_cb`
- * to deallocate it. Is called in all outcomes, either right away or
- * later when the session cache is cleaned up.
- * Caller must ensure that it has properly shared ownership of this sessionid
- * object with cache (e.g. incrementing refcount on success)
- */
-CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct ssl_peer *peer,
- const char *alpn,
- void *sessionid,
- size_t sessionid_size,
- Curl_ssl_sessionid_dtor *sessionid_free_cb);
-
#endif /* USE_SSL */
#endif /* HEADER_CURL_VTLS_INT_H */
diff --git a/libs/libcurl/src/vtls/vtls_scache.c b/libs/libcurl/src/vtls/vtls_scache.c new file mode 100644 index 0000000000..c78ef7e9a8 --- /dev/null +++ b/libs/libcurl/src/vtls/vtls_scache.c @@ -0,0 +1,1213 @@ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_SSL
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "urldata.h"
+#include "cfilters.h"
+
+#include "vtls.h" /* generic SSL protos etc */
+#include "vtls_int.h"
+#include "vtls_scache.h"
+#include "vtls_spack.h"
+
+#include "strcase.h"
+#include "url.h"
+#include "llist.h"
+#include "share.h"
+#include "curl_trc.h"
+#include "curl_sha256.h"
+#include "rand.h"
+#include "warnless.h"
+#include "curl_printf.h"
+#include "strdup.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* a peer+tls-config we cache sessions for */
+struct Curl_ssl_scache_peer {
+ char *ssl_peer_key; /* id for peer + relevant TLS configuration */
+ char *clientcert;
+ char *srp_username;
+ char *srp_password;
+ struct Curl_llist sessions;
+ void *sobj; /* object instance or NULL */
+ Curl_ssl_scache_obj_dtor *sobj_free; /* free `sobj` callback */
+ unsigned char key_salt[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
+ unsigned char key_hmac[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
+ size_t max_sessions;
+ long age; /* just a number, the higher the more recent */
+ BIT(hmac_set); /* if key_salt and key_hmac are present */
+};
+
+#define CURL_SCACHE_MAGIC 0x000e1551
+
+#define GOOD_SCACHE(x) ((x) && (x)->magic == CURL_SCACHE_MAGIC)
+
+struct Curl_ssl_scache {
+ unsigned int magic;
+ struct Curl_ssl_scache_peer *peers;
+ size_t peer_count;
+ int default_lifetime_secs;
+ long age;
+};
+
+static struct Curl_ssl_scache *cf_ssl_scache_get(struct Curl_easy *data)
+{
+ struct Curl_ssl_scache *scache = NULL;
+ /* If a share is present, its ssl_scache has preference over the multi */
+ if(data->share && data->share->ssl_scache)
+ scache = data->share->ssl_scache;
+ else if(data->multi && data->multi->ssl_scache)
+ scache = data->multi->ssl_scache;
+ if(scache && !GOOD_SCACHE(scache)) {
+ failf(data, "transfer would use an invalid scache at %p, denied",
+ (void *)scache);
+ DEBUGASSERT(0);
+ return NULL;
+ }
+ return scache;
+}
+
+static void cf_ssl_scache_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)
+{
+ (void)udata;
+ cf_ssl_scache_clear_session(s);
+ free(s);
+}
+
+CURLcode
+Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len,
+ int ietf_tls_id, const char *alpn,
+ curl_off_t valid_until, size_t earlydata_max,
+ struct Curl_ssl_session **psession)
+{
+ return Curl_ssl_session_create2(sdata, sdata_len, ietf_tls_id, alpn,
+ valid_until, earlydata_max,
+ NULL, 0, psession);
+}
+
+CURLcode
+Curl_ssl_session_create2(unsigned char *sdata, size_t sdata_len,
+ int ietf_tls_id, const char *alpn,
+ curl_off_t valid_until, size_t earlydata_max,
+ unsigned char *quic_tp, size_t quic_tp_len,
+ struct Curl_ssl_session **psession)
+{
+ struct Curl_ssl_session *s;
+
+ if(!sdata || !sdata_len) {
+ free(sdata);
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ *psession = NULL;
+ s = calloc(1, sizeof(*s));
+ if(!s) {
+ free(sdata);
+ free(quic_tp);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ s->ietf_tls_id = ietf_tls_id;
+ s->valid_until = valid_until;
+ s->earlydata_max = earlydata_max;
+ s->sdata = sdata;
+ s->sdata_len = sdata_len;
+ s->quic_tp = quic_tp;
+ s->quic_tp_len = quic_tp_len;
+ if(alpn) {
+ s->alpn = strdup(alpn);
+ if(!s->alpn) {
+ cf_ssl_scache_sesssion_ldestroy(NULL, s);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ *psession = s;
+ return CURLE_OK;
+}
+
+void Curl_ssl_session_destroy(struct Curl_ssl_session *s)
+{
+ if(s) {
+ /* if in the list, the list destructor takes care of it */
+ if(Curl_node_llist(&s->list))
+ Curl_node_remove(&s->list);
+ else {
+ cf_ssl_scache_sesssion_ldestroy(NULL, s);
+ }
+ }
+}
+
+static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer)
+{
+ Curl_llist_destroy(&peer->sessions, NULL);
+ if(peer->sobj) {
+ DEBUGASSERT(peer->sobj_free);
+ if(peer->sobj_free)
+ peer->sobj_free(peer->sobj);
+ peer->sobj = NULL;
+ }
+ peer->sobj_free = NULL;
+ Curl_safefree(peer->clientcert);
+#ifdef USE_TLS_SRP
+ Curl_safefree(peer->srp_username);
+ Curl_safefree(peer->srp_password);
+#endif
+ Curl_safefree(peer->ssl_peer_key);
+ peer->age = 0;
+ peer->hmac_set = FALSE;
+}
+
+static void cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer *peer,
+ void *sobj,
+ Curl_ssl_scache_obj_dtor *sobj_free)
+{
+ DEBUGASSERT(peer);
+ if(peer->sobj_free) {
+ peer->sobj_free(peer->sobj);
+ }
+ peer->sobj = sobj;
+ peer->sobj_free = sobj_free;
+}
+
+static CURLcode
+cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer,
+ const char *ssl_peer_key,
+ const char *clientcert,
+ const char *srp_username,
+ const char *srp_password,
+ const unsigned char *salt,
+ const unsigned char *hmac)
+{
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+
+ DEBUGASSERT(!peer->ssl_peer_key);
+ if(ssl_peer_key) {
+ peer->ssl_peer_key = strdup(ssl_peer_key);
+ if(!peer->ssl_peer_key)
+ goto out;
+ peer->hmac_set = FALSE;
+ }
+ else if(salt && hmac) {
+ memcpy(peer->key_salt, salt, sizeof(peer->key_salt));
+ memcpy(peer->key_hmac, hmac, sizeof(peer->key_hmac));
+ peer->hmac_set = TRUE;
+ }
+ else {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ if(clientcert) {
+ peer->clientcert = strdup(clientcert);
+ if(!peer->clientcert)
+ goto out;
+ }
+ if(srp_username) {
+ peer->srp_username = strdup(srp_username);
+ if(!peer->srp_username)
+ goto out;
+ }
+ if(srp_password) {
+ peer->srp_password = strdup(srp_password);
+ if(!peer->srp_password)
+ goto out;
+ }
+ result = CURLE_OK;
+out:
+ if(result)
+ cf_ssl_scache_clear_peer(peer);
+ return result;
+}
+
+static void cf_scache_session_remove(struct Curl_ssl_scache_peer *peer,
+ struct Curl_ssl_session *s)
+{
+ (void)peer;
+ DEBUGASSERT(Curl_node_llist(&s->list) == &peer->sessions);
+ Curl_ssl_session_destroy(s);
+}
+
+static bool cf_scache_session_expired(struct Curl_ssl_session *s,
+ curl_off_t now)
+{
+ return (s->valid_until > 0) && (s->valid_until < now);
+}
+
+static void cf_scache_peer_remove_expired(struct Curl_ssl_scache_peer *peer,
+ curl_off_t now)
+{
+ struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
+ while(n) {
+ struct Curl_ssl_session *s = Curl_node_elem(n);
+ n = Curl_node_next(n);
+ if(cf_scache_session_expired(s, now))
+ cf_scache_session_remove(peer, s);
+ }
+}
+
+static void cf_scache_peer_remove_non13(struct Curl_ssl_scache_peer *peer)
+{
+ struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
+ while(n) {
+ struct Curl_ssl_session *s = Curl_node_elem(n);
+ n = Curl_node_next(n);
+ if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3)
+ cf_scache_session_remove(peer, s);
+ }
+}
+
+CURLcode Curl_ssl_scache_create(size_t max_peers,
+ size_t max_sessions_per_peer,
+ struct Curl_ssl_scache **pscache)
+{
+ struct Curl_ssl_scache *scache;
+ struct Curl_ssl_scache_peer *peers;
+ size_t i;
+
+ *pscache = NULL;
+ peers = calloc(max_peers, sizeof(*peers));
+ if(!peers)
+ return CURLE_OUT_OF_MEMORY;
+
+ scache = calloc(1, sizeof(*scache));
+ if(!scache) {
+ free(peers);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ scache->magic = CURL_SCACHE_MAGIC;
+ scache->default_lifetime_secs = (24*60*60); /* 1 day */
+ scache->peer_count = max_peers;
+ scache->peers = peers;
+ scache->age = 1;
+ for(i = 0; i < scache->peer_count; ++i) {
+ scache->peers[i].max_sessions = max_sessions_per_peer;
+ Curl_llist_init(&scache->peers[i].sessions,
+ cf_ssl_scache_sesssion_ldestroy);
+ }
+
+ *pscache = scache;
+ return CURLE_OK;
+}
+
+void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache)
+{
+ if(scache && GOOD_SCACHE(scache)) {
+ size_t i;
+ scache->magic = 0;
+ for(i = 0; i < scache->peer_count; ++i) {
+ cf_ssl_scache_clear_peer(&scache->peers[i]);
+ }
+ free(scache->peers);
+ free(scache);
+ }
+}
+
+/* Lock shared SSL session data */
+void Curl_ssl_scache_lock(struct Curl_easy *data)
+{
+ if(CURL_SHARE_ssl_scache(data))
+ Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
+}
+
+/* Unlock shared SSL session data */
+void Curl_ssl_scache_unlock(struct Curl_easy *data)
+{
+ if(CURL_SHARE_ssl_scache(data))
+ Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
+}
+
+static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf,
+ const char *name,
+ char *path)
+{
+ 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
+ char abspath[_MAX_PATH];
+ if(_fullpath(abspath, path, _MAX_PATH))
+ return Curl_dyn_addf(buf, ":%s-%s", name, abspath);
+#elif defined(HAVE_REALPATH)
+ if(path[0] != '/') {
+ char *abspath = realpath(path, NULL);
+ if(abspath) {
+ CURLcode r = Curl_dyn_addf(buf, ":%s-%s", name, abspath);
+ (free)(abspath); /* allocated by libc, free without memdebug */
+ return r;
+ }
+ }
+#endif
+ return Curl_dyn_addf(buf, ":%s-%s", name, path);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf,
+ const char *name,
+ struct curl_blob *blob)
+{
+ CURLcode r = CURLE_OK;
+ if(blob && blob->len) {
+ unsigned char hash[CURL_SHA256_DIGEST_LENGTH];
+ size_t i;
+
+ r = Curl_dyn_addf(buf, ":%s-", name);
+ if(r)
+ goto out;
+ r = Curl_sha256it(hash, blob->data, blob->len);
+ if(r)
+ goto out;
+ for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) {
+ r = Curl_dyn_addf(buf, "%02x", hash[i]);
+ if(r)
+ goto out;
+ }
+ }
+out:
+ return r;
+}
+
+CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
+ const struct ssl_peer *peer,
+ const char *tls_id,
+ char **ppeer_key)
+{
+ struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
+ struct dynbuf buf;
+ size_t key_len;
+ CURLcode r;
+
+ *ppeer_key = NULL;
+ Curl_dyn_init(&buf, 10 * 1024);
+
+ r = Curl_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port);
+ if(r)
+ goto out;
+
+ switch(peer->transport) {
+ case TRNSPRT_TCP:
+ break;
+ case TRNSPRT_UDP:
+ r = Curl_dyn_add(&buf, ":UDP");
+ break;
+ case TRNSPRT_QUIC:
+ r = Curl_dyn_add(&buf, ":QUIC");
+ break;
+ case TRNSPRT_UNIX:
+ r = Curl_dyn_add(&buf, ":UNIX");
+ break;
+ default:
+ r = Curl_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport);
+ break;
+ }
+ if(r)
+ goto out;
+
+ if(!ssl->verifypeer) {
+ r = Curl_dyn_add(&buf, ":NO-VRFY-PEER");
+ if(r)
+ goto out;
+ }
+ if(!ssl->verifyhost) {
+ r = Curl_dyn_add(&buf, ":NO-VRFY-HOST");
+ if(r)
+ goto out;
+ }
+ if(ssl->verifystatus) {
+ r = Curl_dyn_add(&buf, ":VRFY-STATUS");
+ if(r)
+ goto out;
+ }
+ if(!ssl->verifypeer || !ssl->verifyhost) {
+ if(cf->conn->bits.conn_to_host) {
+ r = Curl_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);
+ if(r)
+ goto out;
+ }
+ if(cf->conn->bits.conn_to_port) {
+ r = Curl_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port);
+ if(r)
+ goto out;
+ }
+ }
+
+ if(ssl->version || ssl->version_max) {
+ r = Curl_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version,
+ (ssl->version_max >> 16));
+ if(r)
+ goto out;
+ }
+ if(ssl->ssl_options) {
+ r = Curl_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options);
+ if(r)
+ goto out;
+ }
+ if(ssl->cipher_list) {
+ r = Curl_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list);
+ if(r)
+ goto out;
+ }
+ if(ssl->cipher_list13) {
+ r = Curl_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13);
+ if(r)
+ goto out;
+ }
+ if(ssl->curves) {
+ r = Curl_dyn_addf(&buf, ":CURVES-%s", ssl->curves);
+ if(r)
+ goto out;
+ }
+ if(ssl->verifypeer) {
+ r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile);
+ if(r)
+ goto out;
+ r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath);
+ if(r)
+ goto out;
+ r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile);
+ if(r)
+ goto out;
+ r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert);
+ if(r)
+ goto out;
+ if(ssl->cert_blob) {
+ r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob);
+ if(r)
+ goto out;
+ }
+ if(ssl->ca_info_blob) {
+ r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob);
+ if(r)
+ goto out;
+ }
+ if(ssl->issuercert_blob) {
+ r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob);
+ if(r)
+ goto out;
+ }
+ }
+ if(ssl->pinned_key && ssl->pinned_key[0]) {
+ r = Curl_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key);
+ if(r)
+ goto out;
+ }
+
+ if(ssl->clientcert && ssl->clientcert[0]) {
+ r = Curl_dyn_add(&buf, ":CCERT");
+ if(r)
+ goto out;
+ }
+#ifdef USE_TLS_SRP
+ if(ssl->username || ssl->password) {
+ r = Curl_dyn_add(&buf, ":SRP-AUTH");
+ if(r)
+ goto out;
+ }
+#endif
+
+ if(!tls_id || !tls_id[0]) {
+ r = CURLE_FAILED_INIT;
+ goto out;
+ }
+ r = Curl_dyn_addf(&buf, ":IMPL-%s", tls_id);
+ 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 */
+
+
+out:
+ Curl_dyn_free(&buf);
+ return r;
+}
+
+static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer,
+ struct ssl_primary_config *conn_config)
+{
+ if(!conn_config) {
+ if(peer->clientcert)
+ return FALSE;
+#ifdef USE_TLS_SRP
+ if(peer->srp_username || peer->srp_password)
+ return FALSE;
+#endif
+ return TRUE;
+ }
+ else if(!Curl_safecmp(peer->clientcert, conn_config->clientcert))
+ return FALSE;
+#ifdef USE_TLS_SRP
+ if(Curl_timestrcmp(peer->srp_username, conn_config->username) ||
+ Curl_timestrcmp(peer->srp_password, conn_config->password))
+ return FALSE;
+#endif
+ return TRUE;
+}
+
+static CURLcode
+cf_ssl_find_peer_by_key(struct Curl_easy *data,
+ struct Curl_ssl_scache *scache,
+ const char *ssl_peer_key,
+ struct ssl_primary_config *conn_config,
+ struct Curl_ssl_scache_peer **ppeer)
+{
+ size_t i, peer_key_len = 0;
+ CURLcode result = CURLE_OK;
+
+ *ppeer = NULL;
+ if(!GOOD_SCACHE(scache)) {
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ CURL_TRC_SSLS(data, "find peer slot for %s among %zu slots",
+ ssl_peer_key, scache->peer_count);
+
+ /* check for entries with known peer_key */
+ for(i = 0; scache && i < scache->peer_count; i++) {
+ if(scache->peers[i].ssl_peer_key &&
+ strcasecompare(ssl_peer_key, scache->peers[i].ssl_peer_key) &&
+ cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
+ /* yes, we have a cached session for this! */
+ *ppeer = &scache->peers[i];
+ goto out;
+ }
+ }
+ /* check for entries with HMAC set but no known peer_key */
+ for(i = 0; scache && i < scache->peer_count; i++) {
+ if(!scache->peers[i].ssl_peer_key &&
+ scache->peers[i].hmac_set &&
+ cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
+ /* possible entry with unknown peer_key, check hmac */
+ unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH];
+ if(!peer_key_len) /* we are lazy */
+ peer_key_len = strlen(ssl_peer_key);
+ result = Curl_hmacit(&Curl_HMAC_SHA256,
+ scache->peers[i].key_salt,
+ sizeof(scache->peers[i].key_salt),
+ (const unsigned char *)ssl_peer_key,
+ peer_key_len,
+ my_hmac);
+ if(result)
+ goto out;
+ if(!memcmp(scache->peers[i].key_hmac, my_hmac, sizeof(my_hmac))) {
+ /* remember peer_key for future lookups */
+ CURL_TRC_SSLS(data, "peer entry %zu key recovered: %s",
+ i, ssl_peer_key);
+ scache->peers[i].ssl_peer_key = strdup(ssl_peer_key);
+ if(!scache->peers[i].ssl_peer_key) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ *ppeer = &scache->peers[i];
+ goto out;
+ }
+ }
+ }
+ CURL_TRC_SSLS(data, "peer not found for %s", ssl_peer_key);
+out:
+ return result;
+}
+
+static struct Curl_ssl_scache_peer *
+cf_ssl_get_free_peer(struct Curl_ssl_scache *scache)
+{
+ struct Curl_ssl_scache_peer *peer = NULL;
+ size_t i;
+
+ /* find empty or oldest peer */
+ for(i = 0; i < scache->peer_count; ++i) {
+ /* free peer entry? */
+ if(!scache->peers[i].ssl_peer_key && !scache->peers[i].hmac_set) {
+ peer = &scache->peers[i];
+ break;
+ }
+ /* peer without sessions and obj */
+ if(!scache->peers[i].sobj &&
+ !Curl_llist_count(&scache->peers[i].sessions)) {
+ peer = &scache->peers[i];
+ break;
+ }
+ /* remember "oldest" peer */
+ if(!peer || (scache->peers[i].age < peer->age)) {
+ peer = &scache->peers[i];
+ }
+ }
+ DEBUGASSERT(peer);
+ if(peer)
+ cf_ssl_scache_clear_peer(peer);
+ return peer;
+}
+
+static CURLcode
+cf_ssl_add_peer(struct Curl_easy *data,
+ struct Curl_ssl_scache *scache,
+ const char *ssl_peer_key,
+ struct ssl_primary_config *conn_config,
+ struct Curl_ssl_scache_peer **ppeer)
+{
+ struct Curl_ssl_scache_peer *peer = NULL;
+ CURLcode result = CURLE_OK;
+
+ *ppeer = NULL;
+ if(ssl_peer_key) {
+ result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
+ &peer);
+ if(result || !scache->peer_count)
+ return result;
+ }
+
+ if(peer) {
+ *ppeer = peer;
+ return CURLE_OK;
+ }
+
+ peer = cf_ssl_get_free_peer(scache);
+ if(peer) {
+ const char *ccert = conn_config ? conn_config->clientcert : NULL;
+ const char *username = NULL, *password = NULL;
+#ifdef USE_TLS_SRP
+ username = conn_config ? conn_config->username : NULL;
+ password = conn_config ? conn_config->password : NULL;
+#endif
+ result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert,
+ username, password, NULL, NULL);
+ if(result)
+ goto out;
+ /* all ready */
+ *ppeer = peer;
+ result = CURLE_OK;
+ }
+
+out:
+ if(result) {
+ cf_ssl_scache_clear_peer(peer);
+ }
+ return result;
+}
+
+static void cf_scache_peer_add_session(struct Curl_ssl_scache_peer *peer,
+ struct Curl_ssl_session *s,
+ curl_off_t now)
+{
+ /* A session not from TLSv1.3 replaces all other. */
+ if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3) {
+ Curl_llist_destroy(&peer->sessions, NULL);
+ Curl_llist_append(&peer->sessions, s, &s->list);
+ }
+ else {
+ /* Expire existing, append, trim from head to obey max_sessions */
+ cf_scache_peer_remove_expired(peer, now);
+ cf_scache_peer_remove_non13(peer);
+ Curl_llist_append(&peer->sessions, s, &s->list);
+ while(Curl_llist_count(&peer->sessions) > peer->max_sessions) {
+ Curl_node_remove(Curl_llist_head(&peer->sessions));
+ }
+ }
+}
+
+static CURLcode cf_scache_add_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct Curl_ssl_scache *scache,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session *s)
+{
+ struct Curl_ssl_scache_peer *peer = NULL;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+ curl_off_t now = (curl_off_t)time(NULL);
+ curl_off_t max_lifetime;
+
+ if(!scache || !scache->peer_count) {
+ Curl_ssl_session_destroy(s);
+ return CURLE_OK;
+ }
+
+ if(s->valid_until <= 0)
+ s->valid_until = now + scache->default_lifetime_secs;
+
+ max_lifetime = (s->ietf_tls_id == CURL_IETF_PROTO_TLS1_3) ?
+ CURL_SCACHE_MAX_13_LIFETIME_SEC :
+ CURL_SCACHE_MAX_12_LIFETIME_SEC;
+ if(s->valid_until > (now + max_lifetime))
+ s->valid_until = now + max_lifetime;
+
+ if(cf_scache_session_expired(s, now)) {
+ CURL_TRC_SSLS(data, "add, session already expired");
+ Curl_ssl_session_destroy(s);
+ return CURLE_OK;
+ }
+
+ result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer);
+ if(result || !peer) {
+ CURL_TRC_SSLS(data, "unable to add scache peer: %d", result);
+ Curl_ssl_session_destroy(s);
+ goto out;
+ }
+
+ cf_scache_peer_add_session(peer, s, now);
+
+out:
+ if(result) {
+ failf(data, "[SCACHE] failed to add session for %s, error=%d",
+ ssl_peer_key, result);
+ }
+ else
+ CURL_TRC_SSLS(data, "added session for %s [proto=0x%x, "
+ "valid_secs=%" FMT_OFF_T ", alpn=%s, earlydata=%zu, "
+ "quic_tp=%s], peer has %zu sessions now",
+ ssl_peer_key, s->ietf_tls_id, s->valid_until - now,
+ s->alpn, s->earlydata_max, s->quic_tp ? "yes" : "no",
+ peer ? Curl_llist_count(&peer->sessions) : 0);
+ return result;
+}
+
+CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session *s)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ CURLcode result;
+ DEBUGASSERT(ssl_config);
+
+ if(!scache || !ssl_config->primary.cache_session) {
+ Curl_ssl_session_destroy(s);
+ return CURLE_OK;
+ }
+ 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);
+ Curl_ssl_scache_unlock(data);
+ return result;
+}
+
+void Curl_ssl_scache_return(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session *s)
+{
+ /* See RFC 8446 C.4:
+ * "Clients SHOULD NOT reuse a ticket for multiple connections." */
+ if(s && s->ietf_tls_id < 0x304)
+ (void)Curl_ssl_scache_put(cf, data, ssl_peer_key, s);
+ else
+ Curl_ssl_session_destroy(s);
+}
+
+CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session **ps)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_ssl_scache_peer *peer = NULL;
+ struct Curl_llist_node *n;
+ struct Curl_ssl_session *s = NULL;
+ CURLcode result;
+
+ *ps = NULL;
+ if(!scache)
+ return CURLE_OK;
+
+ Curl_ssl_scache_lock(data);
+ result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
+ &peer);
+ if(!result && peer) {
+ cf_scache_peer_remove_expired(peer, (curl_off_t)time(NULL));
+ n = Curl_llist_head(&peer->sessions);
+ if(n) {
+ s = Curl_node_take_elem(n);
+ (scache->age)++; /* increase general age */
+ peer->age = scache->age; /* set this as used in this age */
+ }
+ }
+ Curl_ssl_scache_unlock(data);
+ if(s) {
+ *ps = s;
+ CURL_TRC_SSLS(data, "took session for %s [proto=0x%x, "
+ "alpn=%s, earlydata=%zu, quic_tp=%s], %zu sessions remain",
+ ssl_peer_key, s->ietf_tls_id, s->alpn,
+ s->earlydata_max, s->quic_tp ? "yes" : "no",
+ Curl_llist_count(&peer->sessions));
+ }
+ else {
+ CURL_TRC_SSLS(data, "no cached session for %s", ssl_peer_key);
+ }
+ return result;
+}
+
+CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ void *sobj,
+ Curl_ssl_scache_obj_dtor *sobj_free)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_ssl_scache_peer *peer = NULL;
+ CURLcode result;
+
+ DEBUGASSERT(sobj);
+ DEBUGASSERT(sobj_free);
+
+ if(!scache) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+
+ result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer);
+ if(result || !peer) {
+ CURL_TRC_SSLS(data, "unable to add scache peer: %d", result);
+ goto out;
+ }
+
+ cf_ssl_scache_peer_set_obj(peer, sobj, sobj_free);
+ sobj = NULL; /* peer took ownership */
+
+out:
+ if(sobj && sobj_free)
+ sobj_free(sobj);
+ return result;
+}
+
+bool Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ void **sobj)
+{
+ 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;
+
+ *sobj = NULL;
+ if(!scache)
+ return FALSE;
+
+ result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
+ &peer);
+ if(result)
+ return FALSE;
+
+ if(peer)
+ *sobj = peer->sobj;
+
+ CURL_TRC_SSLS(data, "%s cached session for '%s'",
+ *sobj ? "Found" : "No", ssl_peer_key);
+ return !!*sobj;
+}
+
+void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_ssl_scache_peer *peer = NULL;
+ CURLcode result;
+
+ (void)cf;
+ if(!scache)
+ return;
+
+ Curl_ssl_scache_lock(data);
+ result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
+ &peer);
+ if(!result && peer)
+ cf_ssl_scache_clear_peer(peer);
+ Curl_ssl_scache_unlock(data);
+}
+
+#ifdef USE_SSLS_EXPORT
+
+#define CURL_SSL_TICKET_MAX (16*1024)
+
+static CURLcode cf_ssl_scache_peer_set_hmac(struct Curl_ssl_scache_peer *peer)
+{
+ CURLcode result;
+
+ DEBUGASSERT(peer);
+ if(!peer->ssl_peer_key)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ result = Curl_rand(NULL, peer->key_salt, sizeof(peer->key_salt));
+ if(result)
+ return result;
+
+ result = Curl_hmacit(&Curl_HMAC_SHA256,
+ peer->key_salt, sizeof(peer->key_salt),
+ (const unsigned char *)peer->ssl_peer_key,
+ strlen(peer->ssl_peer_key),
+ peer->key_hmac);
+ if(!result)
+ peer->hmac_set = TRUE;
+ return result;
+}
+
+static CURLcode
+cf_ssl_find_peer_by_hmac(struct Curl_ssl_scache *scache,
+ const unsigned char *salt,
+ const unsigned char *hmac,
+ struct Curl_ssl_scache_peer **ppeer)
+{
+ size_t i;
+ CURLcode result = CURLE_OK;
+
+ *ppeer = NULL;
+ if(!GOOD_SCACHE(scache))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ /* look for an entry that matches salt+hmac exactly or has a known
+ * ssl_peer_key which salt+hmac's to the same. */
+ for(i = 0; scache && i < scache->peer_count; i++) {
+ struct Curl_ssl_scache_peer *peer = &scache->peers[i];
+ if(!cf_ssl_scache_match_auth(peer, NULL))
+ continue;
+ if(scache->peers[i].hmac_set &&
+ !memcmp(peer->key_salt, salt, sizeof(peer->key_salt)) &&
+ !memcmp(peer->key_hmac, hmac, sizeof(peer->key_hmac))) {
+ /* found exact match, return */
+ *ppeer = peer;
+ goto out;
+ }
+ else if(peer->ssl_peer_key) {
+ unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH];
+ /* compute hmac for the passed salt */
+ result = Curl_hmacit(&Curl_HMAC_SHA256,
+ salt, sizeof(peer->key_salt),
+ (const unsigned char *)peer->ssl_peer_key,
+ strlen(peer->ssl_peer_key),
+ my_hmac);
+ if(result)
+ goto out;
+ if(!memcmp(my_hmac, hmac, sizeof(my_hmac))) {
+ /* cryptohash match, take over salt+hmac if no set and return */
+ if(!peer->hmac_set) {
+ memcpy(peer->key_salt, salt, sizeof(peer->key_salt));
+ memcpy(peer->key_hmac, hmac, sizeof(peer->key_hmac));
+ peer->hmac_set = TRUE;
+ }
+ *ppeer = peer;
+ goto out;
+ }
+ }
+ }
+out:
+ return result;
+}
+
+CURLcode Curl_ssl_session_import(struct Curl_easy *data,
+ const char *ssl_peer_key,
+ const unsigned char *shmac, size_t shmac_len,
+ const unsigned char *sdata, size_t sdata_len)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct Curl_ssl_scache_peer *peer = NULL;
+ struct Curl_ssl_session *s = NULL;
+ bool locked = FALSE;
+ CURLcode r;
+
+ if(!scache) {
+ r = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ if(!ssl_peer_key && (!shmac || !shmac_len)) {
+ r = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+
+ r = Curl_ssl_session_unpack(data, sdata, sdata_len, &s);
+ if(r)
+ goto out;
+
+ Curl_ssl_scache_lock(data);
+ locked = TRUE;
+
+ if(ssl_peer_key) {
+ r = cf_ssl_add_peer(data, scache, ssl_peer_key, NULL, &peer);
+ if(r)
+ goto out;
+ }
+ else if(shmac_len != (sizeof(peer->key_salt) + sizeof(peer->key_hmac))) {
+ /* Either salt+hmac was garbled by caller or is from a curl version
+ * that does things differently */
+ r = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ else {
+ const unsigned char *salt = shmac;
+ const unsigned char *hmac = shmac + sizeof(peer->key_salt);
+
+ r = cf_ssl_find_peer_by_hmac(scache, salt, hmac, &peer);
+ if(r)
+ goto out;
+ if(!peer) {
+ peer = cf_ssl_get_free_peer(scache);
+ if(peer) {
+ r = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL,
+ NULL, NULL, salt, hmac);
+ if(r)
+ goto out;
+ }
+ }
+ }
+
+ if(peer) {
+ cf_scache_peer_add_session(peer, s, time(NULL));
+ s = NULL; /* peer is now owner */
+ CURL_TRC_SSLS(data, "successfully imported ticket for peer %s, now "
+ "with %zu tickets",
+ peer->ssl_peer_key ? peer->ssl_peer_key : "without key",
+ Curl_llist_count(&peer->sessions));
+ }
+
+out:
+ if(locked)
+ Curl_ssl_scache_unlock(data);
+ Curl_ssl_session_destroy(s);
+ return r;
+}
+
+CURLcode Curl_ssl_session_export(struct Curl_easy *data,
+ curl_ssls_export_cb *export_fn,
+ void *userptr)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct Curl_ssl_scache_peer *peer;
+ struct dynbuf sbuf, hbuf;
+ struct Curl_llist_node *n;
+ size_t i, npeers = 0, ntickets = 0;
+ curl_off_t now = time(NULL);
+ CURLcode r = CURLE_OK;
+
+ if(!export_fn)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ if(!scache)
+ return CURLE_OK;
+
+ Curl_ssl_scache_lock(data);
+
+ Curl_dyn_init(&hbuf, (CURL_SHA256_DIGEST_LENGTH * 2) + 1);
+ Curl_dyn_init(&sbuf, CURL_SSL_TICKET_MAX);
+
+ for(i = 0; scache && i < scache->peer_count; i++) {
+ peer = &scache->peers[i];
+ if(!peer->ssl_peer_key && !peer->hmac_set)
+ continue; /* skip free entry */
+ if(peer->clientcert || peer->srp_username || peer->srp_password)
+ continue; /* not exporting those */
+
+ Curl_dyn_reset(&hbuf);
+ cf_scache_peer_remove_expired(peer, now);
+ n = Curl_llist_head(&peer->sessions);
+ if(n)
+ ++npeers;
+ while(n) {
+ struct Curl_ssl_session *s = Curl_node_elem(n);
+ if(!peer->hmac_set) {
+ r = cf_ssl_scache_peer_set_hmac(peer);
+ if(r)
+ goto out;
+ }
+ if(!Curl_dyn_len(&hbuf)) {
+ r = Curl_dyn_addn(&hbuf, peer->key_salt, sizeof(peer->key_salt));
+ if(r)
+ goto out;
+ r = Curl_dyn_addn(&hbuf, peer->key_hmac, sizeof(peer->key_hmac));
+ if(r)
+ goto out;
+ }
+ Curl_dyn_reset(&sbuf);
+ r = Curl_ssl_session_pack(data, s, &sbuf);
+ if(r)
+ goto out;
+
+ r = export_fn(data, userptr, peer->ssl_peer_key,
+ Curl_dyn_uptr(&hbuf), Curl_dyn_len(&hbuf),
+ Curl_dyn_uptr(&sbuf), Curl_dyn_len(&sbuf),
+ s->valid_until, s->ietf_tls_id,
+ s->alpn, s->earlydata_max);
+ if(r)
+ goto out;
+ ++ntickets;
+ n = Curl_node_next(n);
+ }
+
+ }
+ r = CURLE_OK;
+ CURL_TRC_SSLS(data, "exported %zu session tickets for %zu peers",
+ ntickets, npeers);
+
+out:
+ Curl_ssl_scache_unlock(data);
+ Curl_dyn_free(&hbuf);
+ Curl_dyn_free(&sbuf);
+ return r;
+}
+
+#endif /* USE_SSLS_EXPORT */
+
+#endif /* USE_SSL */
diff --git a/libs/libcurl/src/vtls/vtls_scache.h b/libs/libcurl/src/vtls/vtls_scache.h new file mode 100644 index 0000000000..e9b5b76e36 --- /dev/null +++ b/libs/libcurl/src/vtls/vtls_scache.h @@ -0,0 +1,218 @@ +#ifndef HEADER_CURL_VTLS_SCACHE_H
+#define HEADER_CURL_VTLS_SCACHE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+#include "cfilters.h"
+#include "urldata.h"
+
+#ifdef USE_SSL
+
+struct Curl_cfilter;
+struct Curl_easy;
+struct Curl_ssl_scache;
+struct Curl_ssl_session;
+struct ssl_peer;
+
+/* RFC 8446 (TLSv1.3) restrict lifetime to one week max, for
+ * other, less secure versions, we restrict it to a day */
+#define CURL_SCACHE_MAX_13_LIFETIME_SEC (60*60*24*7)
+#define CURL_SCACHE_MAX_12_LIFETIME_SEC (60*60*24)
+
+/* Create a session cache for up to max_peers endpoints with a total
+ * of up to max_sessions SSL sessions per peer */
+CURLcode Curl_ssl_scache_create(size_t max_peers,
+ size_t max_sessions_per_peer,
+ struct Curl_ssl_scache **pscache);
+
+void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache);
+
+/* Create a key from peer and TLS configuration information that is
+ * unique for how the connection filter wants to establish a TLS
+ * connection to the peer.
+ * If the filter is a TLS proxy filter, it will use the proxy relevant
+ * information.
+ * @param cf the connection filter wanting to use it
+ * @param peer the peer the filter wants to talk to
+ * @param tls_id identifier of TLS implementation for sessions. Should
+ * include full version if session data from other versions
+ * is to be avoided.
+ * @param ppeer_key on successful return, the key generated
+ */
+CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
+ const struct ssl_peer *peer,
+ const char *tls_id,
+ char **ppeer_key);
+
+/* Lock session cache mutex.
+ * Call this before calling other Curl_ssl_*session* functions
+ * Caller should unlock this mutex as soon as possible, as it may block
+ * other SSL connection from making progress.
+ * The purpose of explicitly locking SSL session cache data is to allow
+ * individual SSL engines to manage session lifetime in their specific way.
+ */
+void Curl_ssl_scache_lock(struct Curl_easy *data);
+
+/* Unlock session cache mutex */
+void Curl_ssl_scache_unlock(struct Curl_easy *data);
+
+/* Get TLS session object from the cache for the ssl_peer_ey.
+ * scache mutex must be locked (see Curl_ssl_scache_lock).
+ * Caller must make sure that the ownership of returned session object
+ * is properly taken (e.g. its refcount is incremented
+ * under scache mutex).
+ * @param cf the connection filter wanting to use it
+ * @param data the transfer involved
+ * @param ssl_peer_key the key for lookup
+ * @param sobj on return, 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);
+
+typedef void Curl_ssl_scache_obj_dtor(void *sobj);
+
+/* Add a TLS session related object to the cache.
+ * Replaces an existing object with the same peer_key.
+ * scache mutex must be locked (see Curl_ssl_scache_lock).
+ * Call takes ownership of `sobj`, using `sobj_dtor_cb`
+ * to deallocate it. Is called in all outcomes, either right away or
+ * later when the session cache is cleaned up.
+ * Caller must ensure that it has properly shared ownership of `sobj`
+ * with cache (e.g. incrementing refcount on success)
+ * @param cf the connection filter wanting to use it
+ * @param data the transfer involved
+ * @param ssl_peer_key the key for lookup
+ * @param sobj the TLS session object
+ * @param sobj_free_cb callback to free the session objectt
+ */
+CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ void *sobj,
+ Curl_ssl_scache_obj_dtor *sobj_dtor_cb);
+
+/* All about a SSL session ticket */
+struct Curl_ssl_session {
+ const unsigned char *sdata; /* session ticket data, plain bytes */
+ size_t sdata_len; /* number of bytes in sdata */
+ curl_off_t valid_until; /* seconds since EPOCH until ticket expires */
+ int ietf_tls_id; /* TLS protocol identifier negotiated */
+ char *alpn; /* APLN TLS negotiated protocol string */
+ size_t earlydata_max; /* max 0-RTT data supported by peer */
+ const unsigned char *quic_tp; /* Optional QUIC transport param bytes */
+ size_t quic_tp_len; /* number of bytes in quic_tp */
+ struct Curl_llist_node list; /* internal storage handling */
+};
+
+/* Create a `session` instance. Does NOT need locking.
+ * Takes ownership of `sdata` and `sobj` regardless of return code.
+ * @param sdata bytes of SSL session data or NULL (sobj then required)
+ * @param sdata_len amount of session data bytes
+ * @param ietf_tls_id IETF protocol version, e.g. 0x304 for TLSv1.3
+ * @param alpn ALPN protocol selected or NULL
+ * @param valid_until seconds since EPOCH when session expires, pass 0
+ * in case this is not known.
+ * @param psession on return the scached session instance created
+ */
+CURLcode
+Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len,
+ int ietf_tls_id, const char *alpn,
+ curl_off_t valid_until,
+ size_t earlydata_max,
+ struct Curl_ssl_session **psession);
+
+/* Variation of session creation with quic transport parameter bytes,
+ * Takes ownership of `quic_tp` regardless of return code. */
+CURLcode
+Curl_ssl_session_create2(unsigned char *sdata, size_t sdata_len,
+ int ietf_tls_id, const char *alpn,
+ curl_off_t valid_until,
+ size_t earlydata_max,
+ unsigned char *quic_tp, size_t quic_tp_len,
+ struct Curl_ssl_session **psession);
+
+/* Destroy a `session` instance. Can be called with NULL.
+ * Does NOT need locking. */
+void Curl_ssl_session_destroy(struct Curl_ssl_session *s);
+
+/* Put the scache session into the cache. Does NOT need locking.
+ * Call takes ownership of `s` in all outcomes.
+ * @param cf the connection filter wanting to use it
+ * @param data the transfer involved
+ * @param ssl_peer_key the key for lookup
+ * @param s the scache session object
+ */
+CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session *s);
+
+/* Take a matching scache session from the cache. Does NOT need locking.
+ * @param cf the connection filter wanting to use it
+ * @param data the transfer involved
+ * @param ssl_peer_key the key for lookup
+ * @param s on return, the scache session object or NULL
+ */
+CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session **ps);
+
+/* Return a taken scache session to the cache. Does NOT need locking.
+ * Depending on TLS version and other criteria, it may cache it again
+ * or destroy it. Maybe called with a NULL session.
+ */
+void Curl_ssl_scache_return(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session *s);
+
+/* Remove all sessions and obj for the peer_key. Does NOT need locking. */
+void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key);
+
+#ifdef USE_SSLS_EXPORT
+
+CURLcode Curl_ssl_session_import(struct Curl_easy *data,
+ const char *ssl_peer_key,
+ const unsigned char *shmac, size_t shmac_len,
+ const unsigned char *sdata, size_t sdata_len);
+
+CURLcode Curl_ssl_session_export(struct Curl_easy *data,
+ curl_ssls_export_cb *export_fn,
+ void *userptr);
+
+#endif /* USE_SSLS_EXPORT */
+
+#else /* USE_SSL */
+
+#define Curl_ssl_scache_create(x,y,z) ((void)x, CURLE_OK)
+#define Curl_ssl_scache_destroy(x) do {} while(0)
+
+#endif /* USE_SSL (else) */
+
+#endif /* HEADER_CURL_VTLS_SCACHE_H */
diff --git a/libs/libcurl/src/vtls/vtls_spack.c b/libs/libcurl/src/vtls/vtls_spack.c new file mode 100644 index 0000000000..22c84e8a38 --- /dev/null +++ b/libs/libcurl/src/vtls/vtls_spack.c @@ -0,0 +1,345 @@ +/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_SSLS_EXPORT
+
+#include "urldata.h"
+#include "curl_trc.h"
+#include "vtls_scache.h"
+#include "vtls_spack.h"
+#include "strdup.h"
+
+/* The last #include files should be: */
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifdef _MSC_VER
+#if _MSC_VER >= 1600
+#include <stdint.h>
+#else
+typedef unsigned char uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#endif
+#endif /* _MSC_VER */
+
+#ifndef UINT16_MAX
+#define UINT16_MAX 0xffff
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX 0xffffffff
+#endif
+
+#define CURL_SPACK_VERSION 0x01
+#define CURL_SPACK_IETF_ID 0x02
+#define CURL_SPACK_VALID_UNTIL 0x03
+#define CURL_SPACK_TICKET 0x04
+#define CURL_SPACK_ALPN 0x05
+#define CURL_SPACK_EARLYDATA 0x06
+#define CURL_SPACK_QUICTP 0x07
+
+static CURLcode spack_enc8(struct dynbuf *buf, uint8_t b)
+{
+ return Curl_dyn_addn(buf, &b, 1);
+}
+
+static CURLcode
+spack_dec8(uint8_t *val, const uint8_t **src, const uint8_t *end)
+{
+ if(end - *src < 1)
+ return CURLE_READ_ERROR;
+ *val = **src;
+ *src += 1;
+ return CURLE_OK;
+}
+
+static CURLcode spack_enc16(struct dynbuf *buf, uint16_t val)
+{
+ uint8_t nval[2];
+ nval[0] = (uint8_t)(val >> 8);
+ nval[1] = (uint8_t)val;
+ return Curl_dyn_addn(buf, nval, sizeof(nval));
+}
+
+static CURLcode
+spack_dec16(uint16_t *val, const uint8_t **src, const uint8_t *end)
+{
+ if(end - *src < 2)
+ return CURLE_READ_ERROR;
+ *val = (uint16_t)((*src)[0] << 8 | (*src)[1]);
+ *src += 2;
+ return CURLE_OK;
+}
+
+static CURLcode spack_enc32(struct dynbuf *buf, uint32_t val)
+{
+ uint8_t nval[4];
+ nval[0] = (uint8_t)(val >> 24);
+ nval[1] = (uint8_t)(val >> 16);
+ nval[2] = (uint8_t)(val >> 8);
+ nval[3] = (uint8_t)val;
+ return Curl_dyn_addn(buf, nval, sizeof(nval));
+}
+
+static CURLcode
+spack_dec32(uint32_t *val, const uint8_t **src, const uint8_t *end)
+{
+ if(end - *src < 4)
+ return CURLE_READ_ERROR;
+ *val = (uint32_t)(*src)[0] << 24 | (uint32_t)(*src)[1] << 16 |
+ (uint32_t)(*src)[2] << 8 | (*src)[3];
+ *src += 4;
+ return CURLE_OK;
+}
+
+static CURLcode spack_enc64(struct dynbuf *buf, uint64_t val)
+{
+ uint8_t nval[8];
+ nval[0] = (uint8_t)(val >> 56);
+ nval[1] = (uint8_t)(val >> 48);
+ nval[2] = (uint8_t)(val >> 40);
+ nval[3] = (uint8_t)(val >> 32); \
+ nval[4] = (uint8_t)(val >> 24);
+ nval[5] = (uint8_t)(val >> 16);
+ nval[6] = (uint8_t)(val >> 8);
+ nval[7] = (uint8_t)val;
+ return Curl_dyn_addn(buf, nval, sizeof(nval));
+}
+
+static CURLcode
+spack_dec64(uint64_t *val, const uint8_t **src, const uint8_t *end)
+{
+ if(end - *src < 8)
+ return CURLE_READ_ERROR;
+ *val = (uint64_t)(*src)[0] << 56 | (uint64_t)(*src)[1] << 48 |
+ (uint64_t)(*src)[2] << 40 | (uint64_t)(*src)[3] << 32 |
+ (uint64_t)(*src)[4] << 24 | (uint64_t)(*src)[5] << 16 |
+ (uint64_t)(*src)[6] << 8 | (*src)[7];
+ *src += 8;
+ return CURLE_OK;
+}
+
+static CURLcode spack_encstr16(struct dynbuf *buf, const char *s)
+{
+ size_t slen = strlen(s);
+ CURLcode r;
+ if(slen > UINT16_MAX)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ r = spack_enc16(buf, (uint16_t)slen);
+ if(!r) {
+ r = Curl_dyn_addn(buf, s, slen);
+ }
+ return r;
+}
+
+static CURLcode
+spack_decstr16(char **val, const uint8_t **src, const uint8_t *end)
+{
+ uint16_t slen;
+ CURLcode r;
+
+ *val = NULL;
+ r = spack_dec16(&slen, src, end);
+ if(r)
+ return r;
+ if(end - *src < slen)
+ return CURLE_READ_ERROR;
+ *val = Curl_memdup0((const char *)(*src), slen);
+ *src += slen;
+ return *val ? CURLE_OK : CURLE_OUT_OF_MEMORY;
+}
+
+static CURLcode spack_encdata16(struct dynbuf *buf,
+ const uint8_t *data, size_t data_len)
+{
+ CURLcode r;
+ if(data_len > UINT16_MAX)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ r = spack_enc16(buf, (uint16_t)data_len);
+ if(!r) {
+ r = Curl_dyn_addn(buf, data, data_len);
+ }
+ return r;
+}
+
+static CURLcode
+spack_decdata16(uint8_t **val, size_t *val_len,
+ const uint8_t **src, const uint8_t *end)
+{
+ uint16_t data_len;
+ CURLcode r;
+
+ *val = NULL;
+ r = spack_dec16(&data_len, src, end);
+ if(r)
+ return r;
+ if(end - *src < data_len)
+ return CURLE_READ_ERROR;
+ *val = Curl_memdup0((const char *)(*src), data_len);
+ *val_len = data_len;
+ *src += data_len;
+ return *val ? CURLE_OK : CURLE_OUT_OF_MEMORY;
+}
+
+CURLcode Curl_ssl_session_pack(struct Curl_easy *data,
+ struct Curl_ssl_session *s,
+ struct dynbuf *buf)
+{
+ CURLcode r;
+ DEBUGASSERT(s->sdata);
+ DEBUGASSERT(s->sdata_len);
+
+ if(s->valid_until < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ r = spack_enc8(buf, CURL_SPACK_VERSION);
+ if(!r)
+ r = spack_enc8(buf, CURL_SPACK_TICKET);
+ if(!r)
+ r = spack_encdata16(buf, s->sdata, s->sdata_len);
+ if(!r)
+ r = spack_enc8(buf, CURL_SPACK_IETF_ID);
+ if(!r)
+ r = spack_enc16(buf, (uint16_t)s->ietf_tls_id);
+ if(!r)
+ r = spack_enc8(buf, CURL_SPACK_VALID_UNTIL);
+ if(!r)
+ r = spack_enc64(buf, (uint64_t)s->valid_until);
+ if(!r && s->alpn) {
+ r = spack_enc8(buf, CURL_SPACK_ALPN);
+ if(!r)
+ r = spack_encstr16(buf, s->alpn);
+ }
+ if(!r && s->earlydata_max) {
+ if(s->earlydata_max > UINT32_MAX)
+ r = CURLE_BAD_FUNCTION_ARGUMENT;
+ if(!r)
+ r = spack_enc8(buf, CURL_SPACK_EARLYDATA);
+ if(!r)
+ r = spack_enc32(buf, (uint32_t)s->earlydata_max);
+ }
+ if(!r && s->quic_tp && s->quic_tp_len) {
+ r = spack_enc8(buf, CURL_SPACK_QUICTP);
+ if(!r)
+ r = spack_encdata16(buf, s->quic_tp, s->quic_tp_len);
+ }
+
+ if(r)
+ CURL_TRC_SSLS(data, "error packing data: %d", r);
+ return r;
+}
+
+CURLcode Curl_ssl_session_unpack(struct Curl_easy *data,
+ const unsigned char *buf, size_t buflen,
+ struct Curl_ssl_session **ps)
+{
+ struct Curl_ssl_session *s = NULL;
+ const unsigned char *end = buf + buflen;
+ uint8_t val8, *pval8;
+ uint16_t val16;
+ uint32_t val32;
+ uint64_t val64;
+ CURLcode r;
+
+ DEBUGASSERT(buf);
+ DEBUGASSERT(buflen);
+ *ps = NULL;
+
+ r = spack_dec8(&val8, &buf, end);
+ if(r)
+ goto out;
+ if(val8 != CURL_SPACK_VERSION) {
+ r = CURLE_READ_ERROR;
+ goto out;
+ }
+
+ s = calloc(1, sizeof(*s));
+ if(!s) {
+ r = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ while(buf < end) {
+ r = spack_dec8(&val8, &buf, end);
+ if(r)
+ goto out;
+
+ switch(val8) {
+ case CURL_SPACK_ALPN:
+ r = spack_decstr16(&s->alpn, &buf, end);
+ if(r)
+ goto out;
+ break;
+ case CURL_SPACK_EARLYDATA:
+ r = spack_dec32(&val32, &buf, end);
+ if(r)
+ goto out;
+ s->earlydata_max = val32;
+ break;
+ case CURL_SPACK_IETF_ID:
+ r = spack_dec16(&val16, &buf, end);
+ if(r)
+ goto out;
+ s->ietf_tls_id = val16;
+ break;
+ case CURL_SPACK_QUICTP: {
+ r = spack_decdata16(&pval8, &s->quic_tp_len, &buf, end);
+ if(r)
+ goto out;
+ s->quic_tp = pval8;
+ break;
+ }
+ case CURL_SPACK_TICKET: {
+ r = spack_decdata16(&pval8, &s->sdata_len, &buf, end);
+ if(r)
+ goto out;
+ s->sdata = pval8;
+ break;
+ }
+ case CURL_SPACK_VALID_UNTIL:
+ r = spack_dec64(&val64, &buf, end);
+ if(r)
+ goto out;
+ s->valid_until = (curl_off_t)val64;
+ break;
+ default: /* unknown tag */
+ r = CURLE_READ_ERROR;
+ goto out;
+ }
+ }
+
+out:
+ if(r) {
+ CURL_TRC_SSLS(data, "error unpacking data: %d", r);
+ Curl_ssl_session_destroy(s);
+ }
+ else
+ *ps = s;
+ return r;
+}
+
+#endif /* USE_SSLS_EXPORT */
diff --git a/libs/libcurl/src/vtls/vtls_spack.h b/libs/libcurl/src/vtls/vtls_spack.h new file mode 100644 index 0000000000..ec48853e4c --- /dev/null +++ b/libs/libcurl/src/vtls/vtls_spack.h @@ -0,0 +1,43 @@ +#ifndef HEADER_CURL_VTLS_SPACK_H
+#define HEADER_CURL_VTLS_SPACK_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#ifdef USE_SSLS_EXPORT
+
+struct dynbuf;
+struct Curl_ssl_session;
+
+CURLcode Curl_ssl_session_pack(struct Curl_easy *data,
+ struct Curl_ssl_session *s,
+ struct dynbuf *buf);
+
+CURLcode Curl_ssl_session_unpack(struct Curl_easy *data,
+ const unsigned char *buf, size_t buflen,
+ struct Curl_ssl_session **ps);
+
+#endif /* USE_SSLS_EXPORT */
+
+#endif /* HEADER_CURL_VTLS_SPACK_H */
diff --git a/libs/libcurl/src/vtls/wolfssl.c b/libs/libcurl/src/vtls/wolfssl.c index a624d3ff0c..edd60e3e1a 100644 --- a/libs/libcurl/src/vtls/wolfssl.c +++ b/libs/libcurl/src/vtls/wolfssl.c @@ -60,6 +60,7 @@ #include "inet_pton.h"
#include "vtls.h"
#include "vtls_int.h"
+#include "vtls_scache.h"
#include "keylog.h"
#include "parsedate.h"
#include "connect.h" /* for the connect timeout */
@@ -69,7 +70,6 @@ #include "curl_printf.h"
#include "multiif.h"
-#include <wolfssl/openssl/ssl.h>
#include <wolfssl/ssl.h>
#include <wolfssl/error-ssl.h>
#include "wolfssl.h"
@@ -78,13 +78,9 @@ #include "curl_memory.h"
#include "memdebug.h"
-#ifdef USE_ECH
-# include "curl_base64.h"
-# define ECH_ENABLED(__data__) \
- (__data__->set.tls_ech && \
- !(__data__->set.tls_ech & CURLECH_DISABLE)\
- )
-#endif /* USE_ECH */
+#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
+#define USE_ECH_WOLFSSL
+#endif
/* KEEP_PEER_CERT is a product of the presence of build time symbol
OPENSSL_EXTRA without NO_CERTS, depending on the version. KEEP_PEER_CERT is
@@ -299,7 +295,7 @@ static long wolfssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr) #ifdef WOLFSSL_BIO_CTRL_EOF
case WOLFSSL_BIO_CTRL_EOF:
/* EOF has been reached on input? */
- return (!cf->next || !cf->next->connected);
+ return !cf->next || !cf->next->connected;
#endif
default:
ret = 0;
@@ -400,57 +396,54 @@ static void wolfssl_bio_cf_free_methods(void) #endif /* !USE_BIO_CHAIN */
-static void wolfssl_session_free(void *sdata, size_t slen)
-{
- (void)slen;
- free(sdata);
-}
-
-CURLcode wssl_cache_session(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct ssl_peer *peer,
- WOLFSSL_SESSION *session)
+CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ WOLFSSL_SESSION *session,
+ int ietf_tls_id,
+ const char *alpn)
{
CURLcode result = CURLE_OK;
+ struct Curl_ssl_session *sc_session = NULL;
unsigned char *sdata = NULL;
- unsigned int slen;
+ unsigned int sdata_len;
if(!session)
goto out;
- slen = wolfSSL_i2d_SSL_SESSION(session, NULL);
- if(slen <= 0) {
- CURL_TRC_CF(data, cf, "fail to assess session length: %u", slen);
+ sdata_len = wolfSSL_i2d_SSL_SESSION(session, NULL);
+ if(sdata_len <= 0) {
+ CURL_TRC_CF(data, cf, "fail to assess session length: %u", sdata_len);
result = CURLE_FAILED_INIT;
goto out;
}
- sdata = calloc(1, slen);
+ sdata = calloc(1, sdata_len);
if(!sdata) {
- failf(data, "unable to allocate session buffer of %u bytes", slen);
+ failf(data, "unable to allocate session buffer of %u bytes", sdata_len);
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- slen = wolfSSL_i2d_SSL_SESSION(session, &sdata);
- if(slen <= 0) {
- CURL_TRC_CF(data, cf, "fail to serialize session: %u", slen);
+ sdata_len = wolfSSL_i2d_SSL_SESSION(session, &sdata);
+ if(sdata_len <= 0) {
+ CURL_TRC_CF(data, cf, "fail to serialize session: %u", sdata_len);
result = CURLE_FAILED_INIT;
goto out;
}
- Curl_ssl_sessionid_lock(data);
- result = Curl_ssl_set_sessionid(cf, data, peer, NULL,
- sdata, slen, wolfssl_session_free);
- Curl_ssl_sessionid_unlock(data);
- if(result)
- failf(data, "failed to add new ssl session to cache (%d)", result);
- else {
- CURL_TRC_CF(data, cf, "added new session to cache");
- sdata = NULL;
+ result = Curl_ssl_session_create(sdata, sdata_len,
+ ietf_tls_id, alpn,
+ (curl_off_t)time(NULL) +
+ wolfSSL_SESSION_get_timeout(session), 0,
+ &sc_session);
+ sdata = NULL; /* took ownership of sdata */
+ if(!result) {
+ result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
+ /* took ownership of `sc_session` */
}
out:
free(sdata);
- return 0;
+ return result;
}
static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
@@ -465,32 +458,35 @@ static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) DEBUGASSERT(connssl);
DEBUGASSERT(data);
if(connssl && data) {
- (void)wssl_cache_session(cf, data, &connssl->peer, session);
+ (void)Curl_wssl_cache_session(cf, data, connssl->peer.scache_key,
+ session, wolfSSL_version(ssl),
+ connssl->negotiated.alpn);
}
}
return 0;
}
-CURLcode wssl_setup_session(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct wolfssl_ctx *wss,
- struct ssl_peer *peer)
+CURLcode Curl_wssl_setup_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct wolfssl_ctx *wss,
+ const char *ssl_peer_key)
{
- void *psdata;
- const unsigned char *sdata = NULL;
- size_t slen = 0;
- CURLcode result = CURLE_OK;
+ struct Curl_ssl_session *sc_session = NULL;
+ CURLcode result;
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, peer, &psdata, &slen, NULL)) {
+ result = Curl_ssl_scache_take(cf, data, ssl_peer_key, &sc_session);
+ if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) {
WOLFSSL_SESSION *session;
- sdata = psdata;
- session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata, (long)slen);
+ /* wolfSSL changes the passed pointer for whatever reasons, yikes */
+ const unsigned char *sdata = sc_session->sdata;
+ session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata,
+ (long)sc_session->sdata_len);
if(session) {
int ret = wolfSSL_set_session(wss->handle, session);
if(ret != WOLFSSL_SUCCESS) {
- Curl_ssl_delsessionid(data, psdata);
- infof(data, "previous session not accepted (%d), "
+ Curl_ssl_session_destroy(sc_session);
+ sc_session = NULL;
+ infof(data, "cached session not accepted (%d), "
"removing from cache", ret);
}
else
@@ -501,14 +497,14 @@ CURLcode wssl_setup_session(struct Curl_cfilter *cf, failf(data, "could not decode previous session");
}
}
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_return(cf, data, ssl_peer_key, sc_session);
return result;
}
-static CURLcode populate_x509_store(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- WOLFSSL_X509_STORE *store,
- struct wolfssl_ctx *wssl)
+static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ WOLFSSL_X509_STORE *store,
+ struct wolfssl_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;
@@ -556,7 +552,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf, #ifndef NO_FILESYSTEM
/* load trusted cacert from file if not blob */
- CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d",
+ CURL_TRC_CF(data, cf, "wssl_populate_x509_store, path=%s, blob=%d",
ssl_cafile ? ssl_cafile : "none", !!ca_info_blob);
if(!store)
return CURLE_OUT_OF_MEMORY;
@@ -620,8 +616,8 @@ static void wssl_x509_share_free(void *key, size_t key_len, void *p) }
static bool
-cached_x509_store_expired(const struct Curl_easy *data,
- const struct wssl_x509_share *mb)
+wssl_cached_x509_store_expired(const struct Curl_easy *data,
+ const struct wssl_x509_share *mb)
{
const struct ssl_general_config *cfg = &data->set.general_ssl;
struct curltime now = Curl_now();
@@ -635,8 +631,8 @@ cached_x509_store_expired(const struct Curl_easy *data, }
static bool
-cached_x509_store_different(struct Curl_cfilter *cf,
- const struct wssl_x509_share *mb)
+wssl_cached_x509_store_different(struct Curl_cfilter *cf,
+ const struct wssl_x509_share *mb)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
if(!mb->CAfile || !conn_config->CAfile)
@@ -645,8 +641,8 @@ cached_x509_store_different(struct Curl_cfilter *cf, return strcmp(mb->CAfile, conn_config->CAfile);
}
-static WOLFSSL_X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf,
- const struct Curl_easy *data)
+static WOLFSSL_X509_STORE *wssl_get_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
{
struct Curl_multi *multi = data->multi;
struct wssl_x509_share *share;
@@ -657,17 +653,17 @@ static WOLFSSL_X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf, (void *)MPROTO_WSSL_X509_KEY,
sizeof(MPROTO_WSSL_X509_KEY)-1) : NULL;
if(share && share->store &&
- !cached_x509_store_expired(data, share) &&
- !cached_x509_store_different(cf, share)) {
+ !wssl_cached_x509_store_expired(data, share) &&
+ !wssl_cached_x509_store_different(cf, share)) {
store = share->store;
}
return store;
}
-static void set_cached_x509_store(struct Curl_cfilter *cf,
- const struct Curl_easy *data,
- WOLFSSL_X509_STORE *store)
+static void wssl_set_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data,
+ WOLFSSL_X509_STORE *store)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct Curl_multi *multi = data->multi;
@@ -735,7 +731,8 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, !ssl_config->primary.CRLfile &&
!ssl_config->native_ca_store;
- cached_store = cache_criteria_met ? get_cached_x509_store(cf, data) : NULL;
+ 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) {
/* The cached store is already in use, do nothing. */
}
@@ -752,15 +749,15 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf, }
wolfSSL_CTX_set_cert_store(wssl->ctx, store);
- result = populate_x509_store(cf, data, store, wssl);
+ result = wssl_populate_x509_store(cf, data, store, wssl);
if(!result) {
- set_cached_x509_store(cf, data, store);
+ wssl_set_cached_x509_store(cf, data, store);
}
}
else {
/* We never share the CTX's store, use it. */
WOLFSSL_X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx);
- result = populate_x509_store(cf, data, store, wssl);
+ result = wssl_populate_x509_store(cf, data, store, wssl);
}
return result;
@@ -773,7 +770,7 @@ wssl_add_default_ciphers(bool tls13, struct dynbuf *buf) int i;
char *str;
- for(i = 0; (str = wolfSSL_get_cipher_list(i)); i++) {
+ for(i = 0; (str = wolfSSL_get_cipher_list(i)) != NULL; i++) {
size_t n;
if((strncmp(str, "TLS13", 5) == 0) != tls13)
continue;
@@ -1192,13 +1189,13 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) /* Check if there is a cached ID we can/should use here! */
if(ssl_config->primary.cache_session) {
/* Set session from cache if there is one */
- (void)wssl_setup_session(cf, data, backend, &connssl->peer);
+ (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
+#ifdef USE_ECH_WOLFSSL
if(ECH_ENABLED(data)) {
int trying_ech_now = 0;
@@ -1265,14 +1262,14 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) }
}
- if(trying_ech_now
- && SSL_set_min_proto_version(backend->handle, TLS1_3_VERSION) != 1) {
+ if(trying_ech_now && wolfSSL_set_min_proto_version(backend->handle,
+ TLS1_3_VERSION) != 1) {
infof(data, "ECH: cannot force TLSv1.3 [ERROR]");
return CURLE_SSL_CONNECT_ERROR;
}
}
-#endif /* USE_ECH */
+#endif /* USE_ECH_WOLFSSL */
#ifdef USE_BIO_CHAIN
{
@@ -1441,7 +1438,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) failf(data, "server verification failed: certificate not valid yet.");
return CURLE_PEER_FAILED_VERIFICATION;
}
-#ifdef USE_ECH
+#ifdef USE_ECH_WOLFSSL
else if(-1 == detail) {
/* try access a retry_config ECHConfigList for tracing */
byte echConfigs[1000];
@@ -1796,7 +1793,7 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf, }
-static size_t wolfssl_version(char *buffer, size_t size)
+size_t Curl_wssl_version(char *buffer, size_t size)
{
#if LIBWOLFSSL_VERSION_HEX >= 0x03006000
return msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version());
@@ -2020,7 +2017,7 @@ const struct Curl_ssl Curl_ssl_wolfssl = { #endif
SSLSUPP_CA_PATH |
SSLSUPP_CAINFO_BLOB |
-#ifdef USE_ECH
+#ifdef USE_ECH_WOLFSSL
SSLSUPP_ECH |
#endif
SSLSUPP_SSL_CTX |
@@ -2034,25 +2031,22 @@ const struct Curl_ssl Curl_ssl_wolfssl = { wolfssl_init, /* init */
wolfssl_cleanup, /* cleanup */
- wolfssl_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
+ Curl_wssl_version, /* version */
wolfssl_shutdown, /* shutdown */
wolfssl_data_pending, /* data_pending */
wolfssl_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
wolfssl_connect, /* connect */
wolfssl_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
wolfssl_get_internals, /* get_internals */
wolfssl_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
wolfssl_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
wolfssl_recv, /* recv decrypted data */
wolfssl_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/wolfssl.h b/libs/libcurl/src/vtls/wolfssl.h index 0a4d253a58..24f676366c 100644 --- a/libs/libcurl/src/vtls/wolfssl.h +++ b/libs/libcurl/src/vtls/wolfssl.h @@ -47,19 +47,23 @@ struct wolfssl_ctx { BIT(shutting_down); /* TLS is being shut down */
};
+size_t Curl_wssl_version(char *buffer, size_t size);
+
CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct wolfssl_ctx *wssl);
-CURLcode wssl_setup_session(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct wolfssl_ctx *wss,
- struct ssl_peer *peer);
+CURLcode Curl_wssl_setup_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct wolfssl_ctx *wss,
+ const char *ssl_peer_key);
-CURLcode wssl_cache_session(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct ssl_peer *peer,
- WOLFSSL_SESSION *session);
+CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ WOLFSSL_SESSION *session,
+ int ietf_tls_id,
+ const char *alpn);
#endif /* USE_WOLFSSL */
diff --git a/libs/libcurl/src/vtls/x509asn1.c b/libs/libcurl/src/vtls/x509asn1.c index 6e1fbfab49..f38137e0f2 100644 --- a/libs/libcurl/src/vtls/x509asn1.c +++ b/libs/libcurl/src/vtls/x509asn1.c @@ -178,8 +178,11 @@ static const char *getASN1Element(struct Curl_asn1Element *elem, const char *beg, const char *end)
WARN_UNUSED_RESULT;
-static const char *getASN1Element(struct Curl_asn1Element *elem,
- const char *beg, const char *end)
+#define CURL_ASN1_MAX_RECURSIONS 16
+
+static const char *getASN1Element_(struct Curl_asn1Element *elem,
+ const char *beg, const char *end,
+ size_t lvl)
{
unsigned char b;
size_t len;
@@ -190,7 +193,8 @@ static const char *getASN1Element(struct Curl_asn1Element *elem, Returns a pointer in source string after the parsed element, or NULL
if an error occurs. */
if(!beg || !end || beg >= end || !*beg ||
- (size_t)(end - beg) > CURL_ASN1_MAX)
+ ((size_t)(end - beg) > CURL_ASN1_MAX) ||
+ lvl >= CURL_ASN1_MAX_RECURSIONS)
return NULL;
/* Process header byte. */
@@ -216,7 +220,7 @@ static const char *getASN1Element(struct Curl_asn1Element *elem, return NULL;
elem->beg = beg;
while(beg < end && *beg) {
- beg = getASN1Element(&lelem, beg, end);
+ beg = getASN1Element_(&lelem, beg, end, lvl + 1);
if(!beg)
return NULL;
}
@@ -243,6 +247,12 @@ static const char *getASN1Element(struct Curl_asn1Element *elem, return elem->end;
}
+static const char *getASN1Element(struct Curl_asn1Element *elem,
+ const char *beg, const char *end)
+{
+ return getASN1Element_(elem, beg, end, 0);
+}
+
#ifdef WANT_EXTRACT_CERTINFO
/*
@@ -259,6 +269,17 @@ static const struct Curl_OID *searchOID(const char *oid) return NULL;
}
+#ifdef UNITTESTS
+/* used by unit1657.c */
+CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
+ const char *beg, const char *end)
+{
+ if(getASN1Element(elem, beg, end))
+ return CURLE_OK;
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+}
+#endif
+
/*
* Convert an ASN.1 Boolean value into its string representation.
*
diff --git a/libs/libcurl/src/vtls/x509asn1.h b/libs/libcurl/src/vtls/x509asn1.h index b0155d11a2..670a565302 100644 --- a/libs/libcurl/src/vtls/x509asn1.h +++ b/libs/libcurl/src/vtls/x509asn1.h @@ -85,6 +85,9 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data, /* used by unit1656.c */
CURLcode Curl_x509_GTime2str(struct dynbuf *store,
const char *beg, const char *end);
+/* used by unit1657.c */
+CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
+ const char *beg, const char *end);
#endif
#endif
|
