Merge da5453381f into e7751571eb
This commit is contained in:
commit
89c2135c13
@ -89,11 +89,13 @@ could be a privacy violation and unexpected.
|
||||
## CURLSSLOPT_EARLYDATA
|
||||
|
||||
Tell libcurl to try sending application data as TLS1.3 early data. This option
|
||||
is supported for GnuTLS and wolfSSL. This option works on a best effort basis,
|
||||
is supported for GnuTLS, wolfSSL, quictls and OpenSSL (but not BoringSSL
|
||||
or AWS-LC). It works on TCP and QUIC connections using ngtcp2.
|
||||
This option works on a best effort basis,
|
||||
in cases when it wasn't possible to send early data the request is resent
|
||||
normally post-handshake.
|
||||
This option does not work when using QUIC.
|
||||
(Added in 8.11.0 for GnuTLS and 8.13.0 for wolfSSL)
|
||||
(Added in 8.11.0 for GnuTLS and 8.13.0 for wolfSSL, quictls and OpenSSL)
|
||||
|
||||
# DEFAULT
|
||||
|
||||
|
||||
@ -482,6 +482,11 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data)
|
||||
if(ctx->use_earlydata)
|
||||
Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at);
|
||||
if(ctx->use_earlydata) {
|
||||
#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)
|
||||
ctx->earlydata_accepted =
|
||||
(SSL_get_early_data_status(ctx->tls.ossl.ssl) !=
|
||||
SSL_EARLY_DATA_REJECTED);
|
||||
#endif
|
||||
#ifdef USE_GNUTLS
|
||||
int flags = gnutls_session_get_flags(ctx->tls.gtls.session);
|
||||
ctx->earlydata_accepted = !!(flags & GNUTLS_SFLAGS_EARLY_DATA);
|
||||
@ -2183,8 +2188,24 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
|
||||
ctx = cf ? cf->ctx : NULL;
|
||||
data = cf ? CF_DATA_CURRENT(cf) : NULL;
|
||||
if(cf && data && ctx) {
|
||||
unsigned char *quic_tp = NULL;
|
||||
size_t quic_tp_len = 0;
|
||||
#ifdef HAVE_OPENSSL_EARLYDATA
|
||||
ngtcp2_ssize tplen;
|
||||
uint8_t tpbuf[256];
|
||||
|
||||
tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf,
|
||||
sizeof(tpbuf));
|
||||
if(tplen < 0)
|
||||
CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s",
|
||||
ngtcp2_strerror((int)tplen));
|
||||
else {
|
||||
quic_tp = (unsigned char *)tpbuf;
|
||||
quic_tp_len = (size_t)tplen;
|
||||
}
|
||||
#endif
|
||||
Curl_ossl_add_session(cf, data, ctx->peer.scache_key, ssl_sessionid,
|
||||
SSL_version(ssl), "h3");
|
||||
SSL_version(ssl), "h3", quic_tp, quic_tp_len);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
@ -2355,6 +2376,9 @@ static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf,
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
*do_early_data = FALSE;
|
||||
#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)
|
||||
ctx->earlydata_max = scs->earlydata_max;
|
||||
#endif
|
||||
#ifdef USE_GNUTLS
|
||||
ctx->earlydata_max =
|
||||
gnutls_record_get_max_early_data_size(ctx->tls.gtls.session);
|
||||
@ -2366,7 +2390,8 @@ static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf,
|
||||
ctx->earlydata_max = 0;
|
||||
#endif /* WOLFSSL_EARLY_DATA */
|
||||
#endif
|
||||
#if defined(USE_GNUTLS) || defined(USE_WOLFSSL)
|
||||
#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \
|
||||
(defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA))
|
||||
if((!ctx->earlydata_max)) {
|
||||
CURL_TRC_CF(data, cf, "SSL session does not allow earlydata");
|
||||
}
|
||||
@ -2394,7 +2419,7 @@ static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf,
|
||||
}
|
||||
}
|
||||
}
|
||||
#else /* USE_GNUTLS */
|
||||
#else /* not supported in the TLS backend */
|
||||
(void)data;
|
||||
(void)ctx;
|
||||
(void)scs;
|
||||
|
||||
@ -88,7 +88,8 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
|
||||
#ifdef USE_OPENSSL
|
||||
(void)result;
|
||||
return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, alpns,
|
||||
cb_setup, cb_user_data, NULL, ssl_user_data);
|
||||
cb_setup, cb_user_data, NULL, ssl_user_data,
|
||||
session_reuse_cb);
|
||||
#elif defined(USE_GNUTLS)
|
||||
return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer, alpns,
|
||||
cb_setup, cb_user_data, ssl_user_data,
|
||||
|
||||
@ -1780,8 +1780,7 @@ static CURLcode gtls_send_earlydata(struct Curl_cfilter *cf,
|
||||
Curl_bufq_skip(&connssl->earlydata, (size_t)n);
|
||||
}
|
||||
/* sent everything there was */
|
||||
infof(data, "SSL sending %" FMT_OFF_T " bytes of early data",
|
||||
connssl->earlydata_skip);
|
||||
infof(data, "SSL sending %zu bytes of early data", connssl->earlydata_skip);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -62,6 +62,7 @@
|
||||
#include "strcase.h"
|
||||
#include "hostcheck.h"
|
||||
#include "multiif.h"
|
||||
#include "strdup.h"
|
||||
#include "strerror.h"
|
||||
#include "curl_printf.h"
|
||||
|
||||
@ -2927,10 +2928,13 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
|
||||
const char *ssl_peer_key,
|
||||
SSL_SESSION *session,
|
||||
int ietf_tls_id,
|
||||
const char *alpn)
|
||||
const char *alpn,
|
||||
unsigned char *quic_tp,
|
||||
size_t quic_tp_len)
|
||||
{
|
||||
const struct ssl_config_data *config;
|
||||
unsigned char *der_session_buf = NULL;
|
||||
unsigned char *qtp_clone = NULL;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(!cf || !data)
|
||||
@ -2941,6 +2945,7 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
|
||||
struct Curl_ssl_session *sc_session = NULL;
|
||||
size_t der_session_size;
|
||||
unsigned char *der_session_ptr;
|
||||
size_t earlydata_max = 0;
|
||||
|
||||
der_session_size = i2d_SSL_SESSION(session, NULL);
|
||||
if(der_session_size == 0) {
|
||||
@ -2960,11 +2965,23 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = Curl_ssl_session_create(der_session_buf, der_session_size,
|
||||
ietf_tls_id, alpn,
|
||||
(curl_off_t)time(NULL) +
|
||||
SSL_SESSION_get_timeout(session), 0,
|
||||
&sc_session);
|
||||
#ifdef HAVE_OPENSSL_EARLYDATA
|
||||
earlydata_max = SSL_SESSION_get_max_early_data(session);
|
||||
#endif
|
||||
if(quic_tp && quic_tp_len) {
|
||||
qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len);
|
||||
if(!qtp_clone) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
result = Curl_ssl_session_create2(der_session_buf, der_session_size,
|
||||
ietf_tls_id, alpn,
|
||||
(curl_off_t)time(NULL) +
|
||||
SSL_SESSION_get_timeout(session),
|
||||
earlydata_max, qtp_clone, quic_tp_len,
|
||||
&sc_session);
|
||||
der_session_buf = NULL; /* took ownership of sdata */
|
||||
if(!result) {
|
||||
result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
|
||||
@ -2987,7 +3004,8 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
|
||||
struct Curl_easy *data = CF_DATA_CURRENT(cf);
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
Curl_ossl_add_session(cf, data, connssl->peer.scache_key, ssl_sessionid,
|
||||
SSL_version(ssl), connssl->negotiated.alpn);
|
||||
SSL_version(ssl), connssl->negotiated.alpn,
|
||||
NULL, 0);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -3527,11 +3545,12 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
|
||||
struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct ssl_peer *peer,
|
||||
const struct alpn_spec *alpns,
|
||||
const struct alpn_spec *alpns_requested,
|
||||
Curl_ossl_ctx_setup_cb *cb_setup,
|
||||
void *cb_user_data,
|
||||
Curl_ossl_new_session_cb *cb_new_session,
|
||||
void *ssl_user_data)
|
||||
void *ssl_user_data,
|
||||
Curl_ossl_init_session_reuse_cb *sess_reuse_cb)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
const char *ciphers;
|
||||
@ -3545,12 +3564,14 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
|
||||
const char * const ssl_cert_type = ssl_config->cert_type;
|
||||
const bool verifypeer = conn_config->verifypeer;
|
||||
char error_buffer[256];
|
||||
struct alpn_spec alpns;
|
||||
|
||||
/* Make funny stuff to get random input */
|
||||
result = ossl_seed(data);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
Curl_alpn_copy(&alpns, alpns_requested);
|
||||
ssl_config->certverifyresult = !X509_V_OK;
|
||||
|
||||
switch(peer->transport) {
|
||||
@ -3722,22 +3743,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
|
||||
SSL_CTX_set_mode(octx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
|
||||
#endif
|
||||
|
||||
#ifdef HAS_ALPN_OPENSSL
|
||||
if(alpns && alpns->count) {
|
||||
struct alpn_proto_buf proto;
|
||||
memset(&proto, 0, sizeof(proto));
|
||||
result = Curl_alpn_to_proto_buf(&proto, alpns);
|
||||
if(result) {
|
||||
failf(data, "Error determining ALPN");
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
if(SSL_CTX_set_alpn_protos(octx->ssl_ctx, proto.data, (int)proto.len)) {
|
||||
failf(data, "Error setting ALPN");
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if(ssl_cert || ssl_cert_blob || ssl_cert_type) {
|
||||
if(!result &&
|
||||
!cert_stuff(data, octx->ssl_ctx,
|
||||
@ -4020,12 +4025,12 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
|
||||
|
||||
octx->reused_session = FALSE;
|
||||
if(ssl_config->primary.cache_session) {
|
||||
struct Curl_ssl_session *sc_session = NULL;
|
||||
struct Curl_ssl_session *scs = NULL;
|
||||
|
||||
result = Curl_ssl_scache_take(cf, data, peer->scache_key, &sc_session);
|
||||
if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) {
|
||||
const unsigned char *der_sessionid = sc_session->sdata;
|
||||
size_t der_sessionid_size = sc_session->sdata_len;
|
||||
result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs);
|
||||
if(!result && scs && scs->sdata && scs->sdata_len) {
|
||||
const unsigned char *der_sessionid = scs->sdata;
|
||||
size_t der_sessionid_size = scs->sdata_len;
|
||||
SSL_SESSION *ssl_session = NULL;
|
||||
|
||||
/* If OpenSSL does not accept the session from the cache, this
|
||||
@ -4040,8 +4045,29 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
|
||||
sizeof(error_buffer)));
|
||||
}
|
||||
else {
|
||||
infof(data, "SSL reusing session");
|
||||
infof(data, "SSL reusing session with ALPN '%s'",
|
||||
scs->alpn ? scs->alpn : "-");
|
||||
octx->reused_session = TRUE;
|
||||
#ifdef HAVE_OPENSSL_EARLYDATA
|
||||
if(ssl_config->earlydata && scs->alpn &&
|
||||
SSL_SESSION_get_max_early_data(ssl_session) &&
|
||||
!cf->conn->connect_only &&
|
||||
(SSL_version(octx->ssl) == TLS1_3_VERSION)) {
|
||||
bool do_early_data = FALSE;
|
||||
if(sess_reuse_cb) {
|
||||
result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
if(do_early_data) {
|
||||
/* We only try the ALPN protocol the session used before,
|
||||
* otherwise we might send early data for the wrong protocol */
|
||||
Curl_alpn_restrict_to(&alpns, scs->alpn);
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)sess_reuse_cb;
|
||||
#endif
|
||||
}
|
||||
SSL_SESSION_free(ssl_session);
|
||||
}
|
||||
@ -4049,12 +4075,58 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
|
||||
infof(data, "SSL session not accepted by OpenSSL, continuing without");
|
||||
}
|
||||
}
|
||||
Curl_ssl_scache_return(cf, data, peer->scache_key, sc_session);
|
||||
Curl_ssl_scache_return(cf, data, peer->scache_key, scs);
|
||||
}
|
||||
|
||||
#ifdef HAS_ALPN_OPENSSL
|
||||
if(alpns.count) {
|
||||
struct alpn_proto_buf proto;
|
||||
memset(&proto, 0, sizeof(proto));
|
||||
result = Curl_alpn_to_proto_buf(&proto, &alpns);
|
||||
if(result) {
|
||||
failf(data, "Error determining ALPN");
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
if(SSL_set_alpn_protos(octx->ssl, proto.data, (int)proto.len)) {
|
||||
failf(data, "Error setting ALPN");
|
||||
return CURLE_SSL_CONNECT_ERROR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode ossl_on_session_reuse(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct alpn_spec *alpns,
|
||||
struct Curl_ssl_session *scs,
|
||||
bool *do_early_data)
|
||||
{
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
*do_early_data = FALSE;
|
||||
connssl->earlydata_max = scs->earlydata_max;
|
||||
if(!connssl->earlydata_max) {
|
||||
CURL_TRC_CF(data, cf, "SSL session does not allow earlydata");
|
||||
}
|
||||
else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) {
|
||||
CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data");
|
||||
}
|
||||
else {
|
||||
infof(data, "SSL session allows %zu bytes of early data, "
|
||||
"reusing ALPN '%s'", connssl->earlydata_max, scs->alpn);
|
||||
connssl->earlydata_state = ssl_earlydata_await;
|
||||
connssl->state = ssl_connection_deferred;
|
||||
result = Curl_alpn_set_negotiated(cf, data, connssl,
|
||||
(const unsigned char *)scs->alpn,
|
||||
scs->alpn ? strlen(scs->alpn) : 0);
|
||||
*do_early_data = !result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
@ -4068,7 +4140,8 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
|
||||
|
||||
result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer,
|
||||
connssl->alpn, NULL, NULL,
|
||||
ossl_new_session_cb, cf);
|
||||
ossl_new_session_cb, cf,
|
||||
ossl_on_session_reuse);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
@ -4094,7 +4167,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
|
||||
#endif
|
||||
|
||||
#ifdef HAS_ALPN_OPENSSL
|
||||
if(connssl->alpn) {
|
||||
if(connssl->alpn && (connssl->state != ssl_connection_deferred)) {
|
||||
struct alpn_proto_buf proto;
|
||||
memset(&proto, 0, sizeof(proto));
|
||||
Curl_alpn_to_proto_str(&proto, connssl->alpn);
|
||||
@ -4209,27 +4282,25 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
|
||||
if(SSL_ERROR_WANT_READ == detail) {
|
||||
CURL_TRC_CF(data, cf, "SSL_connect() -> want recv");
|
||||
connssl->io_need = CURL_SSL_IO_NEED_RECV;
|
||||
return CURLE_OK;
|
||||
return CURLE_AGAIN;
|
||||
}
|
||||
if(SSL_ERROR_WANT_WRITE == detail) {
|
||||
CURL_TRC_CF(data, cf, "SSL_connect() -> want send");
|
||||
connssl->io_need = CURL_SSL_IO_NEED_SEND;
|
||||
return CURLE_OK;
|
||||
return CURLE_AGAIN;
|
||||
}
|
||||
#ifdef SSL_ERROR_WANT_ASYNC
|
||||
if(SSL_ERROR_WANT_ASYNC == detail) {
|
||||
CURL_TRC_CF(data, cf, "SSL_connect() -> want async");
|
||||
connssl->io_need = CURL_SSL_IO_NEED_RECV;
|
||||
connssl->connecting_state = ssl_connect_2;
|
||||
return CURLE_OK;
|
||||
return CURLE_AGAIN;
|
||||
}
|
||||
#endif
|
||||
#ifdef SSL_ERROR_WANT_RETRY_VERIFY
|
||||
if(SSL_ERROR_WANT_RETRY_VERIFY == detail) {
|
||||
CURL_TRC_CF(data, cf, "SSL_connect() -> want retry_verify");
|
||||
connssl->io_need = CURL_SSL_IO_NEED_RECV;
|
||||
connssl->connecting_state = ssl_connect_2;
|
||||
return CURLE_OK;
|
||||
return CURLE_AGAIN;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
@ -4787,15 +4858,92 @@ static CURLcode ossl_connect_step3(struct Curl_cfilter *cf,
|
||||
*/
|
||||
|
||||
result = Curl_oss_check_peer_cert(cf, data, octx, &connssl->peer);
|
||||
if(!result)
|
||||
connssl->connecting_state = ssl_connect_done;
|
||||
else
|
||||
if(result)
|
||||
/* on error, remove sessions we might have in the pool */
|
||||
Curl_ssl_scache_remove_all(cf, data, connssl->peer.scache_key);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENSSL_EARLYDATA
|
||||
static CURLcode ossl_send_earlydata(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
|
||||
CURLcode result = CURLE_OK;
|
||||
const unsigned char *buf;
|
||||
size_t blen, nwritten;
|
||||
int rc;
|
||||
|
||||
DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sending);
|
||||
octx->io_result = CURLE_OK;
|
||||
while(Curl_bufq_peek(&connssl->earlydata, &buf, &blen)) {
|
||||
nwritten = 0;
|
||||
rc = SSL_write_early_data(octx->ssl, buf, blen, &nwritten);
|
||||
CURL_TRC_CF(data, cf, "SSL_write_early_data(len=%zu) -> %d, %zu",
|
||||
blen, rc, nwritten);
|
||||
if(rc <= 0) {
|
||||
long sslerror;
|
||||
char error_buffer[256];
|
||||
int err = SSL_get_error(octx->ssl, rc);
|
||||
|
||||
switch(err) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
connssl->io_need = CURL_SSL_IO_NEED_RECV;
|
||||
result = CURLE_AGAIN;
|
||||
goto out;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
connssl->io_need = CURL_SSL_IO_NEED_SEND;
|
||||
result = CURLE_AGAIN;
|
||||
goto out;
|
||||
case SSL_ERROR_SYSCALL: {
|
||||
int sockerr = SOCKERRNO;
|
||||
|
||||
if(octx->io_result == CURLE_AGAIN) {
|
||||
result = CURLE_AGAIN;
|
||||
goto out;
|
||||
}
|
||||
sslerror = ERR_get_error();
|
||||
if(sslerror)
|
||||
ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
|
||||
else if(sockerr)
|
||||
Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
|
||||
else
|
||||
msnprintf(error_buffer, sizeof(error_buffer), "%s",
|
||||
SSL_ERROR_to_str(err));
|
||||
|
||||
failf(data, OSSL_PACKAGE " SSL_write:early_data: %s, errno %d",
|
||||
error_buffer, sockerr);
|
||||
result = CURLE_SEND_ERROR;
|
||||
goto out;
|
||||
}
|
||||
case SSL_ERROR_SSL: {
|
||||
/* A failure in the SSL library occurred, usually a protocol error.
|
||||
The OpenSSL error queue contains more information on the error. */
|
||||
sslerror = ERR_get_error();
|
||||
failf(data, "SSL_write_early_data() error: %s",
|
||||
ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)));
|
||||
result = CURLE_SEND_ERROR;
|
||||
goto out;
|
||||
}
|
||||
default:
|
||||
/* a true error */
|
||||
failf(data, OSSL_PACKAGE " SSL_write_early_data: %s, errno %d",
|
||||
SSL_ERROR_to_str(err), SOCKERRNO);
|
||||
result = CURLE_SEND_ERROR;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
Curl_bufq_skip(&connssl->earlydata, nwritten);
|
||||
}
|
||||
/* sent everything there was */
|
||||
infof(data, "SSL sending %zu bytes of early data", connssl->earlydata_skip);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
#endif /* HAVE_OPENSSL_EARLYDATA */
|
||||
|
||||
static CURLcode ossl_connect(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
bool *done)
|
||||
@ -4813,29 +4961,63 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf,
|
||||
connssl->io_need = CURL_SSL_IO_NEED_NONE;
|
||||
|
||||
if(ssl_connect_1 == connssl->connecting_state) {
|
||||
CURL_TRC_CF(data, cf, "ossl_connect, step1");
|
||||
result = ossl_connect_step1(cf, data);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(ssl_connect_2 == connssl->connecting_state) {
|
||||
CURL_TRC_CF(data, cf, "ossl_connect, step2");
|
||||
#ifdef HAVE_OPENSSL_EARLYDATA
|
||||
if(connssl->earlydata_state == ssl_earlydata_await) {
|
||||
goto out;
|
||||
}
|
||||
else if(connssl->earlydata_state == ssl_earlydata_sending) {
|
||||
result = ossl_send_earlydata(cf, data);
|
||||
if(result)
|
||||
goto out;
|
||||
connssl->earlydata_state = ssl_earlydata_sent;
|
||||
}
|
||||
#endif
|
||||
DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) ||
|
||||
(connssl->earlydata_state == ssl_earlydata_sent));
|
||||
|
||||
result = ossl_connect_step2(cf, data);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(ssl_connect_3 == connssl->connecting_state) {
|
||||
CURL_TRC_CF(data, cf, "ossl_connect, step3");
|
||||
result = ossl_connect_step3(cf, data);
|
||||
if(result)
|
||||
goto out;
|
||||
connssl->connecting_state = ssl_connect_done;
|
||||
#ifdef HAVE_OPENSSL_EARLYDATA
|
||||
if(connssl->earlydata_state > ssl_earlydata_none) {
|
||||
struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
|
||||
/* We should be in this state by now */
|
||||
DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sent);
|
||||
connssl->earlydata_state =
|
||||
(SSL_get_early_data_status(octx->ssl) == SSL_EARLY_DATA_ACCEPTED) ?
|
||||
ssl_earlydata_accepted : ssl_earlydata_rejected;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if(ssl_connect_done == connssl->connecting_state) {
|
||||
CURL_TRC_CF(data, cf, "ossl_connect, done");
|
||||
connssl->state = ssl_connection_complete;
|
||||
*done = TRUE;
|
||||
}
|
||||
|
||||
out:
|
||||
if(result == CURLE_AGAIN) {
|
||||
*done = FALSE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
*done = ((connssl->state == ssl_connection_complete) ||
|
||||
(connssl->state == ssl_connection_deferred));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -49,8 +49,16 @@
|
||||
#define HAVE_KEYLOG_CALLBACK
|
||||
#endif
|
||||
|
||||
/* Check for OpenSSL 1.1.1 which has early data support. */
|
||||
#undef HAVE_OPENSSL_EARLYDATA
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100010L && defined(TLS1_3_VERSION) && \
|
||||
!defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
|
||||
#define HAVE_OPENSSL_EARLYDATA
|
||||
#endif
|
||||
|
||||
struct alpn_spec;
|
||||
struct ssl_peer;
|
||||
struct Curl_ssl_session;
|
||||
|
||||
/* Struct to hold a curl OpenSSL instance */
|
||||
struct ossl_ctx {
|
||||
@ -76,6 +84,11 @@ typedef CURLcode Curl_ossl_ctx_setup_cb(struct Curl_cfilter *cf,
|
||||
void *user_data);
|
||||
|
||||
typedef int Curl_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid);
|
||||
typedef CURLcode Curl_ossl_init_session_reuse_cb(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct alpn_spec *alpns,
|
||||
struct Curl_ssl_session *scs,
|
||||
bool *do_early_data);
|
||||
|
||||
CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
|
||||
struct Curl_cfilter *cf,
|
||||
@ -85,7 +98,8 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
|
||||
Curl_ossl_ctx_setup_cb *cb_setup,
|
||||
void *cb_user_data,
|
||||
Curl_ossl_new_session_cb *cb_new_session,
|
||||
void *ssl_user_data);
|
||||
void *ssl_user_data,
|
||||
Curl_ossl_init_session_reuse_cb *sess_reuse_cb);
|
||||
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
|
||||
#define SSL_get1_peer_certificate SSL_get_peer_certificate
|
||||
@ -114,7 +128,9 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
|
||||
const char *ssl_peer_key,
|
||||
SSL_SESSION *ssl_sessionid,
|
||||
int ietf_tls_id,
|
||||
const char *alpn);
|
||||
const char *alpn,
|
||||
unsigned char *quic_tp,
|
||||
size_t quic_tp_len);
|
||||
|
||||
/*
|
||||
* Get the server cert, verify it and show it, etc., only call failf() if
|
||||
|
||||
@ -1635,8 +1635,7 @@ static CURLcode wssl_send_earlydata(struct Curl_cfilter *cf,
|
||||
connssl->earlydata_state = ssl_earlydata_sent;
|
||||
if(!Curl_ssl_cf_is_proxy(cf))
|
||||
Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip);
|
||||
infof(data, "SSL sending %" FMT_OFF_T " bytes of early data",
|
||||
connssl->earlydata_skip);
|
||||
infof(data, "SSL sending %zu bytes of early data", connssl->earlydata_skip);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -577,9 +577,10 @@ class TestDownload:
|
||||
@pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx")
|
||||
@pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
|
||||
def test_02_32_earlydata(self, env: Env, httpd, nghttpx, proto):
|
||||
if not env.curl_uses_lib('gnutls') and not env.curl_uses_lib('wolfssl'):
|
||||
pytest.skip('TLS earlydata only implemented in GnuTLS/wolfSSL')
|
||||
if proto == 'h3' and not env.have_h3():
|
||||
if not env.curl_can_early_data():
|
||||
pytest.skip('TLS earlydata not implemented')
|
||||
if proto == 'h3' and \
|
||||
(not env.have_h3() or not env.curl_can_h3_early_data()):
|
||||
pytest.skip("h3 not supported")
|
||||
count = 2
|
||||
docname = 'data-10k'
|
||||
|
||||
@ -703,9 +703,10 @@ class TestUpload:
|
||||
# of 128K.
|
||||
])
|
||||
def test_07_70_put_earlydata(self, env: Env, httpd, nghttpx, proto, upload_size, exp_early):
|
||||
if not env.curl_uses_lib('gnutls') and not env.curl_uses_lib('wolfssl'):
|
||||
pytest.skip('TLS earlydata only implemented in GnuTLS/wolfSSL')
|
||||
if proto == 'h3' and not env.have_h3():
|
||||
if not env.curl_can_early_data():
|
||||
pytest.skip('TLS earlydata not implemented')
|
||||
if proto == 'h3' and \
|
||||
(not env.have_h3() or not env.curl_can_h3_early_data()):
|
||||
pytest.skip("h3 not supported")
|
||||
count = 2
|
||||
# we want this test to always connect to nghttpx, since it is
|
||||
|
||||
@ -207,9 +207,10 @@ class TestCaddy:
|
||||
|
||||
@pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
|
||||
def test_08_08_earlydata(self, env: Env, httpd, caddy, proto):
|
||||
if not env.curl_uses_lib('gnutls') and not env.curl_uses_lib('wolfssl'):
|
||||
pytest.skip('TLS earlydata only implemented in GnuTLS/wolfSSL')
|
||||
if proto == 'h3' and not env.have_h3():
|
||||
if not env.curl_can_early_data():
|
||||
pytest.skip('TLS earlydata not implemented')
|
||||
if proto == 'h3' and \
|
||||
(not env.have_h3() or not env.curl_can_h3_early_data()):
|
||||
pytest.skip("h3 not supported")
|
||||
count = 2
|
||||
docname = 'data10k.data'
|
||||
|
||||
@ -252,8 +252,8 @@ class TestSSLUse:
|
||||
if proto == 'h3' and not env.have_h3():
|
||||
pytest.skip("h3 not supported")
|
||||
if not env.curl_uses_lib('openssl') and \
|
||||
not env.curl_uses_lib('gnutls') and \
|
||||
not env.curl_uses_lib('quictls'):
|
||||
not env.curl_uses_lib('gnutls') and \
|
||||
not env.curl_uses_lib('quictls'):
|
||||
pytest.skip("TLS library does not support --cert-status")
|
||||
curl = CurlClient(env=env)
|
||||
domain = 'localhost'
|
||||
@ -318,8 +318,8 @@ class TestSSLUse:
|
||||
if not env.have_h3():
|
||||
pytest.skip("h3 not supported")
|
||||
if not env.curl_uses_lib('quictls') and \
|
||||
not env.curl_uses_lib('gnutls') and \
|
||||
not env.curl_uses_lib('wolfssl'):
|
||||
not env.curl_uses_lib('gnutls') and \
|
||||
not env.curl_uses_lib('wolfssl'):
|
||||
pytest.skip("QUIC session reuse not implemented")
|
||||
count = 2
|
||||
docname = 'data-10k'
|
||||
|
||||
@ -382,6 +382,18 @@ class Env:
|
||||
def curl_is_debug() -> bool:
|
||||
return Env.CONFIG.curl_is_debug
|
||||
|
||||
@staticmethod
|
||||
def curl_can_early_data() -> bool:
|
||||
return Env.curl_uses_lib('gnutls') or \
|
||||
Env.curl_uses_lib('wolfssl') or \
|
||||
Env.curl_uses_lib('quictls') or \
|
||||
Env.curl_uses_lib('openssl')
|
||||
|
||||
@staticmethod
|
||||
def curl_can_h3_early_data() -> bool:
|
||||
return Env.curl_can_early_data() and \
|
||||
Env.curl_uses_lib('ngtcp2')
|
||||
|
||||
@staticmethod
|
||||
def have_h3() -> bool:
|
||||
return Env.have_h3_curl() and Env.have_h3_server()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user