mirror of
https://github.com/curl/curl.git
synced 2026-07-10 04:27:21 +03:00
FTP: fix TLS session reuse on the data connection
FTP servers using SSL can be configured to check TLS session reuse on the DATA connection. They hand out a new session on every CONTROL connect and require to see the client using exactly that one when up-/downloading on DATA. This means: 1. We have to configure the SSL filter on the DATA connection with exactly the same peers. 2. We have to remember the SSL session on the CONTROL connection - separately from the session cache. The SSL filter on the DATA connection then looks for a session at the CONTROL filter and, if present, uses that. Tests: Enable `require_ssl_reuse` in our pytest setup for vsftpd. This reproduces the problem reported in #22225 and verifies the fix. Skip ftp+SSL pytests for rustls, as we have no possibility to reuse sessions in that backend. Schannel: we do not run these tests with the backend. I expect it has similar problems but am not able to verify. Reported-by: Laurent Sabourin Fixes #22225 Closes #22246
This commit is contained in:
parent
0a7ec0ea4d
commit
84ecfb3ecc
17 changed files with 485 additions and 190 deletions
|
|
@ -273,12 +273,15 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf,
|
|||
else
|
||||
#endif
|
||||
{
|
||||
/* Another FTP quirk: when adding SSL verification, to a DATA
|
||||
* connection, always verify against the control's origin */
|
||||
struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET);
|
||||
struct Curl_peer *peer =
|
||||
Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
result = Curl_cf_ssl_insert_after(cf, data, origin, peer);
|
||||
/* FTP is a bitch. Wherever we really connect to on the DATA
|
||||
* (secondary) connection, many servers require TLS sessions reuse
|
||||
* to prove they are talking to the same client.
|
||||
* For the TLS session lookup to work, we need to instantiate
|
||||
* the SSL filter with the same peers as FIRSTSOCKET. See #22225
|
||||
* Meaning: cf->sockindex does not matter here. */
|
||||
result = Curl_cf_ssl_insert_after(cf, data,
|
||||
Curl_conn_get_origin(cf->conn, FIRSTSOCKET),
|
||||
Curl_conn_get_destination(cf->conn, FIRSTSOCKET));
|
||||
}
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d",
|
||||
|
|
|
|||
|
|
@ -650,7 +650,7 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
|
|||
}
|
||||
#endif
|
||||
Curl_ossl_add_session(cf, data, &ctx->tls.ossl, ctx->ssl_peer.scache_key,
|
||||
ssl_sessionid, "h3", quic_tp, quic_tp_len);
|
||||
ssl_sessionid, "h3", quic_tp, quic_tp_len, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -721,7 +721,8 @@ static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
|
|||
quic_tp_len = (size_t)tplen;
|
||||
}
|
||||
(void)Curl_gtls_cache_session(cf, data, ctx->ssl_peer.scache_key,
|
||||
session, 0, "h3", quic_tp, quic_tp_len);
|
||||
session, 0, "h3", quic_tp, quic_tp_len,
|
||||
NULL);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
@ -760,7 +761,7 @@ static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
|
|||
}
|
||||
(void)Curl_wssl_cache_session(cf, data, ctx->ssl_peer.scache_key,
|
||||
session, wolfSSL_version(ssl),
|
||||
"h3", quic_tp, quic_tp_len);
|
||||
"h3", quic_tp, quic_tp_len, NULL);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
|
|||
133
lib/vtls/gtls.c
133
lib/vtls/gtls.c
|
|
@ -720,14 +720,17 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf,
|
|||
curl_off_t valid_until,
|
||||
const char *alpn,
|
||||
unsigned char *quic_tp,
|
||||
size_t quic_tp_len)
|
||||
size_t quic_tp_len,
|
||||
struct Curl_ssl_session **psession)
|
||||
{
|
||||
struct Curl_ssl_session *sc_session;
|
||||
struct Curl_ssl_session *sc_session = NULL, *sc_dup = NULL;
|
||||
unsigned char *sdata, *qtp_clone = NULL;
|
||||
size_t sdata_len = 0;
|
||||
size_t earlydata_max = 0;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(psession)
|
||||
*psession = NULL;
|
||||
if(!Curl_ssl_scache_use(cf, data))
|
||||
return CURLE_OK;
|
||||
|
||||
|
|
@ -766,10 +769,20 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf,
|
|||
qtp_clone, quic_tp_len,
|
||||
&sc_session);
|
||||
/* call took ownership of `sdata` and `qtp_clone` */
|
||||
if(!result && psession && /* return a duplicate if asked for and FTP */
|
||||
(cf->conn->scheme->family == CURLPROTO_FTP))
|
||||
result = Curl_ssl_session_dup(sc_session, &sc_dup);
|
||||
if(!result) {
|
||||
result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
|
||||
/* took ownership of `sc_session` */
|
||||
sc_session = NULL;
|
||||
}
|
||||
if(!result && psession) {
|
||||
*psession = sc_dup;
|
||||
sc_dup = NULL;
|
||||
}
|
||||
Curl_ssl_session_destroy(sc_session);
|
||||
Curl_ssl_session_destroy(sc_dup);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
|
@ -780,9 +793,17 @@ static CURLcode cf_gtls_update_session_id(struct Curl_cfilter *cf,
|
|||
gnutls_session_t session)
|
||||
{
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
return Curl_gtls_cache_session(cf, data, connssl->peer.scache_key,
|
||||
session, 0, connssl->negotiated.alpn,
|
||||
NULL, 0);
|
||||
struct Curl_ssl_session *scs = NULL;
|
||||
CURLcode result;
|
||||
|
||||
result = Curl_gtls_cache_session(cf, data, connssl->peer.scache_key,
|
||||
session, 0, connssl->negotiated.alpn,
|
||||
NULL, 0, &scs);
|
||||
if(!result) {
|
||||
Curl_ssl_session_destroy(connssl->session);
|
||||
connssl->session = scs;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
|
||||
|
|
@ -1104,6 +1125,55 @@ static CURLcode gtls_on_session_reuse(struct Curl_cfilter *cf,
|
|||
}
|
||||
#endif
|
||||
|
||||
static CURLcode gtls_apply_session(
|
||||
struct gtls_ctx *gctx,
|
||||
struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct ssl_peer *peer,
|
||||
struct alpn_spec *alpns,
|
||||
Curl_gtls_init_session_reuse_cb *sess_reuse_cb,
|
||||
struct Curl_ssl_session *scs,
|
||||
bool *pwas_setup)
|
||||
{
|
||||
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
|
||||
CURLcode result = CURLE_OK;
|
||||
int rc;
|
||||
|
||||
if(scs && scs->sdata && scs->sdata_len &&
|
||||
(!scs->alpn || Curl_alpn_contains_proto(alpns, scs->alpn))) {
|
||||
/* we got a cached session, use it! */
|
||||
|
||||
result = gtls_client_init(cf, data, peer, scs->earlydata_max, gctx);
|
||||
if(result)
|
||||
goto out;
|
||||
*pwas_setup = TRUE;
|
||||
|
||||
rc = gnutls_session_set_data(gctx->session, scs->sdata, scs->sdata_len);
|
||||
if(rc < 0)
|
||||
infof(data, "SSL session not accepted by GnuTLS, continuing without");
|
||||
else {
|
||||
infof(data, "SSL reusing session with ALPN '%s'",
|
||||
scs->alpn ? scs->alpn : "-");
|
||||
if(ssl_config->earlydata && scs->alpn &&
|
||||
!cf->conn->bits.connect_only) {
|
||||
bool do_early_data = FALSE;
|
||||
if(sess_reuse_cb) {
|
||||
result = sess_reuse_cb(cf, data, alpns, scs, &do_early_data);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
if(do_early_data) {
|
||||
/* We only try the ALPN protocol the session used before,
|
||||
* otherwise we might send early data for the wrong protocol */
|
||||
Curl_alpn_restrict_to(alpns, scs->alpn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
|
||||
struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
|
|
@ -1115,57 +1185,40 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
|
|||
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[ALPN_ENTRIES_MAX];
|
||||
size_t gtls_alpns_count = 0;
|
||||
bool gtls_session_setup = FALSE;
|
||||
struct alpn_spec alpns;
|
||||
CURLcode result = CURLE_OK;
|
||||
int rc;
|
||||
|
||||
DEBUGASSERT(gctx);
|
||||
Curl_alpn_copy(&alpns, alpns_requested);
|
||||
|
||||
if((cf->sockindex == SECONDARYSOCKET) && !(cf->cft->flags & CF_TYPE_PROXY)) {
|
||||
/* FTP is a bitch. On TLS secured transfers, it is a common server
|
||||
* option to require the client to use the SAME TLS session as on
|
||||
* the control connection or it fails the request. See #22225. */
|
||||
scs = Curl_ssl_get_cf_session(data, cf->cft, FIRSTSOCKET);
|
||||
if(scs) {
|
||||
result = gtls_apply_session(gctx, cf, data, peer, &alpns,
|
||||
sess_reuse_cb, scs, >ls_session_setup);
|
||||
scs = NULL;
|
||||
if(!result && gtls_session_setup) {
|
||||
CURL_TRC_CF(data, cf, "applied SSL session from control connection");
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 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 && !conn_config->verifystatus) {
|
||||
if(!gtls_session_setup && conn_config->cache_session &&
|
||||
!conn_config->verifystatus) {
|
||||
result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
if(scs && scs->sdata && scs->sdata_len &&
|
||||
(!scs->alpn || Curl_alpn_contains_proto(&alpns, scs->alpn))) {
|
||||
/* we got a cached session, use it! */
|
||||
|
||||
result = gtls_client_init(cf, data, peer, scs->earlydata_max, gctx);
|
||||
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 session not accepted by GnuTLS, continuing without");
|
||||
else {
|
||||
infof(data, "SSL reusing session with ALPN '%s'",
|
||||
scs->alpn ? scs->alpn : "-");
|
||||
if(ssl_config->earlydata && scs->alpn &&
|
||||
!cf->conn->bits.connect_only) {
|
||||
bool do_early_data = FALSE;
|
||||
if(sess_reuse_cb) {
|
||||
result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
if(do_early_data) {
|
||||
/* We only try the ALPN protocol the session used before,
|
||||
* otherwise we might send early data for the wrong protocol */
|
||||
Curl_alpn_restrict_to(&alpns, scs->alpn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
result = gtls_apply_session(gctx, cf, data, peer, &alpns,
|
||||
sess_reuse_cb, scs, >ls_session_setup);
|
||||
}
|
||||
|
||||
if(!gtls_session_setup) {
|
||||
|
|
|
|||
|
|
@ -105,7 +105,8 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf,
|
|||
struct ssl_peer *peer,
|
||||
const char *pinned_key);
|
||||
|
||||
/* Extract TLS session and place in cache, if configured. */
|
||||
/* Extract TLS session and place in cache, if configured. Return
|
||||
* a copy of the session if desired. */
|
||||
CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
const char *ssl_peer_key,
|
||||
|
|
@ -113,7 +114,8 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf,
|
|||
curl_off_t valid_until,
|
||||
const char *alpn,
|
||||
unsigned char *quic_tp,
|
||||
size_t quic_tp_len);
|
||||
size_t quic_tp_len,
|
||||
struct Curl_ssl_session **psession);
|
||||
|
||||
/* Report properties of a successful handshake */
|
||||
void Curl_gtls_report_handshake(struct Curl_easy *data, struct gtls_ctx *gctx);
|
||||
|
|
|
|||
|
|
@ -786,6 +786,38 @@ static CURLcode mbed_load_crl(struct Curl_cfilter *cf,
|
|||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static bool mbed_apply_session(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_ssl_session *sc_session)
|
||||
{
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
struct mbed_ssl_backend_data *backend =
|
||||
(struct mbed_ssl_backend_data *)connssl->backend;
|
||||
|
||||
if(sc_session && sc_session->sdata && sc_session->sdata_len) {
|
||||
mbedtls_ssl_session session;
|
||||
int ret;
|
||||
|
||||
mbedtls_ssl_session_init(&session);
|
||||
ret = mbedtls_ssl_session_load(&session, sc_session->sdata,
|
||||
sc_session->sdata_len);
|
||||
if(ret) {
|
||||
failf(data, "SSL session error loading: -0x%x", (unsigned int)-ret);
|
||||
}
|
||||
else {
|
||||
ret = mbedtls_ssl_set_session(&backend->ssl, &session);
|
||||
if(ret)
|
||||
failf(data, "SSL session error setting: -0x%x", (unsigned int)-ret);
|
||||
}
|
||||
mbedtls_ssl_session_free(&session);
|
||||
if(!ret) {
|
||||
infof(data, "SSL reusing session ID");
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
|
|
@ -795,6 +827,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf,
|
|||
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);
|
||||
int ret;
|
||||
bool session_applied = FALSE;
|
||||
CURLcode result;
|
||||
char errorbuf[128];
|
||||
|
||||
|
|
@ -899,8 +932,22 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf,
|
|||
MBEDTLS_SSL_SESSION_TICKETS_DISABLED);
|
||||
#endif
|
||||
|
||||
if((cf->sockindex == SECONDARYSOCKET) && !(cf->cft->flags & CF_TYPE_PROXY)) {
|
||||
/* FTP is a bitch. On TLS secured transfers, it is a common server
|
||||
* option to require the client to use the SAME TLS session as on
|
||||
* the control connection or it fails the request. See #22225. */
|
||||
struct Curl_ssl_session *scs =
|
||||
Curl_ssl_get_cf_session(data, cf->cft, FIRSTSOCKET);
|
||||
if(scs) {
|
||||
if(mbed_apply_session(cf, data, scs)) {
|
||||
CURL_TRC_CF(data, cf, "applied SSL session from control connection");
|
||||
session_applied = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if there is a cached ID we can/should use here! */
|
||||
if(Curl_ssl_scache_use(cf, data)) {
|
||||
if(!session_applied && Curl_ssl_scache_use(cf, data)) {
|
||||
struct Curl_ssl_session *sc_session = NULL;
|
||||
CURLcode sresult = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key,
|
||||
&sc_session);
|
||||
|
|
@ -1196,13 +1243,22 @@ static CURLcode mbed_new_session(struct Curl_cfilter *cf,
|
|||
connssl->negotiated.alpn, 0, 0,
|
||||
&sc_session);
|
||||
sdata = NULL; /* call took ownership */
|
||||
if(!result)
|
||||
if(!result && /* return a duplicate if asked for and FTP */
|
||||
(cf->conn->scheme->family == CURLPROTO_FTP)) {
|
||||
Curl_ssl_session_destroy(connssl->session);
|
||||
result = Curl_ssl_session_dup(sc_session, &connssl->session);
|
||||
}
|
||||
|
||||
if(!result) {
|
||||
result = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key,
|
||||
sc_session);
|
||||
sc_session = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
if(msession_alloced)
|
||||
mbedtls_ssl_session_free(&session);
|
||||
Curl_ssl_session_destroy(sc_session);
|
||||
curlx_free(sdata);
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2637,17 +2637,20 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
|
|||
SSL_SESSION *session,
|
||||
const char *alpn,
|
||||
unsigned char *quic_tp,
|
||||
size_t quic_tp_len)
|
||||
size_t quic_tp_len,
|
||||
struct Curl_ssl_session **psession)
|
||||
{
|
||||
struct Curl_ssl_session *sc_session = NULL, *sc_dup = NULL;
|
||||
unsigned char *der_session_buf = NULL;
|
||||
unsigned char *qtp_clone = NULL;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(psession)
|
||||
*psession = NULL;
|
||||
if(!cf || !data)
|
||||
goto out;
|
||||
|
||||
if(Curl_ssl_scache_use(cf, data)) {
|
||||
struct Curl_ssl_session *sc_session = NULL;
|
||||
size_t der_session_size;
|
||||
unsigned char *der_session_ptr;
|
||||
size_t earlydata_max = 0;
|
||||
|
|
@ -2689,17 +2692,29 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
|
|||
earlydata_max, qtp_clone, quic_tp_len,
|
||||
&sc_session);
|
||||
der_session_buf = NULL; /* took ownership of sdata */
|
||||
if(!result) {
|
||||
#ifdef USE_APPLE_SECTRUST
|
||||
if(!result)
|
||||
sc_session->sectrust_verified = octx->sectrust_verified;
|
||||
#endif
|
||||
if(!result && psession && /* return a duplicate if asked for and FTP */
|
||||
(cf->conn->scheme->family == CURLPROTO_FTP)) {
|
||||
result = Curl_ssl_session_dup(sc_session, &sc_dup);
|
||||
}
|
||||
if(!result) {
|
||||
result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
|
||||
/* took ownership of `sc_session` */
|
||||
sc_session = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
curlx_free(der_session_buf);
|
||||
if(!result && psession) {
|
||||
*psession = sc_dup;
|
||||
sc_dup = NULL;
|
||||
}
|
||||
Curl_ssl_session_destroy(sc_session);
|
||||
Curl_ssl_session_destroy(sc_dup);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -2713,8 +2728,14 @@ 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;
|
||||
struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
|
||||
struct Curl_ssl_session *session = NULL;
|
||||
Curl_ossl_add_session(cf, data, octx, connssl->peer.scache_key,
|
||||
ssl_sessionid, connssl->negotiated.alpn, NULL, 0);
|
||||
ssl_sessionid, connssl->negotiated.alpn, NULL,
|
||||
0, &session);
|
||||
if(session) { /* remember current TLS session */
|
||||
Curl_ssl_session_destroy(connssl->session);
|
||||
connssl->session = session;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -3319,6 +3340,82 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
|
|||
return result;
|
||||
}
|
||||
|
||||
static bool ossl_apply_session(
|
||||
struct ossl_ctx *octx,
|
||||
struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct alpn_spec *alpns,
|
||||
Curl_ossl_init_session_reuse_cb *sess_reuse_cb,
|
||||
struct Curl_ssl_session *scs)
|
||||
{
|
||||
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
|
||||
struct ssl_primary_config *conn_cfg = Curl_ssl_cf_get_primary_config(cf);
|
||||
const unsigned char *der_sessionid = scs->sdata;
|
||||
size_t der_sessionid_size = scs->sdata_len;
|
||||
SSL_SESSION *ssl_session = NULL;
|
||||
|
||||
/* If OpenSSL does not accept the session from the cache, this
|
||||
* is not an error. We continue without it. */
|
||||
ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid,
|
||||
(long)der_sessionid_size);
|
||||
if(ssl_session) {
|
||||
if(!SSL_set_session(octx->ssl, ssl_session)) {
|
||||
VERBOSE(char error_buffer[256]);
|
||||
infof(data, "SSL: SSL_set_session not accepted, "
|
||||
"continuing without: %s",
|
||||
ossl_strerror(ERR_get_error(), error_buffer,
|
||||
sizeof(error_buffer)));
|
||||
}
|
||||
else {
|
||||
if(conn_cfg->verifypeer &&
|
||||
(SSL_get_verify_result(octx->ssl) != X509_V_OK)
|
||||
#ifdef USE_APPLE_SECTRUST
|
||||
/* if sectrust is used and verified the session before */
|
||||
&& (!ssl_config->native_ca_store || !scs->sectrust_verified)
|
||||
#endif
|
||||
) {
|
||||
/* Session was from unverified connection, cannot reuse here */
|
||||
SSL_set_session(octx->ssl, NULL);
|
||||
infof(data, "SSL session not peer verified, not reusing");
|
||||
}
|
||||
else {
|
||||
infof(data, "SSL reusing session with ALPN '%s'",
|
||||
scs->alpn ? scs->alpn : "-");
|
||||
octx->reused_session = TRUE;
|
||||
#ifdef USE_APPLE_SECTRUST
|
||||
octx->sectrust_session = scs->sectrust_verified;
|
||||
#endif
|
||||
infof(data, "SSL verify result: %lx",
|
||||
(unsigned long)SSL_get_verify_result(octx->ssl));
|
||||
#ifdef HAVE_OPENSSL_EARLYDATA
|
||||
if(ssl_config->earlydata && scs->alpn &&
|
||||
SSL_SESSION_get_max_early_data(ssl_session) &&
|
||||
!cf->conn->bits.connect_only &&
|
||||
(SSL_version(octx->ssl) == TLS1_3_VERSION)) {
|
||||
bool do_early_data = FALSE;
|
||||
if(sess_reuse_cb)
|
||||
(void)sess_reuse_cb(cf, data, alpns, scs, &do_early_data);
|
||||
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)alpns;
|
||||
(void)ssl_config;
|
||||
(void)sess_reuse_cb;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
SSL_SESSION_free(ssl_session);
|
||||
}
|
||||
else {
|
||||
infof(data, "SSL session not accepted by OpenSSL, continuing without");
|
||||
}
|
||||
return (bool)octx->reused_session;
|
||||
}
|
||||
|
||||
static CURLcode ossl_init_session_and_alpns(
|
||||
struct ossl_ctx *octx,
|
||||
struct Curl_cfilter *cf,
|
||||
|
|
@ -3327,7 +3424,6 @@ static CURLcode ossl_init_session_and_alpns(
|
|||
const struct alpn_spec *alpns_requested,
|
||||
Curl_ossl_init_session_reuse_cb *sess_reuse_cb)
|
||||
{
|
||||
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
|
||||
struct ssl_primary_config *conn_cfg = Curl_ssl_cf_get_primary_config(cf);
|
||||
struct alpn_spec alpns;
|
||||
CURLcode result;
|
||||
|
|
@ -3335,78 +3431,26 @@ static CURLcode ossl_init_session_and_alpns(
|
|||
Curl_alpn_copy(&alpns, alpns_requested);
|
||||
|
||||
octx->reused_session = FALSE;
|
||||
if(Curl_ssl_scache_use(cf, data) && !conn_cfg->verifystatus) {
|
||||
|
||||
if((cf->sockindex == SECONDARYSOCKET) && !(cf->cft->flags & CF_TYPE_PROXY)) {
|
||||
/* FTP is a bitch. On TLS secured transfers, it is a common server
|
||||
* option to require the client to use the SAME TLS session as on
|
||||
* the control connection or it fails the request. See #22225. */
|
||||
struct Curl_ssl_session *scs =
|
||||
Curl_ssl_get_cf_session(data, cf->cft, FIRSTSOCKET);
|
||||
if(scs) {
|
||||
if(ossl_apply_session(octx, cf, data, &alpns, sess_reuse_cb, scs))
|
||||
CURL_TRC_CF(data, cf, "applied SSL session from control connection");
|
||||
}
|
||||
}
|
||||
|
||||
if(!octx->reused_session &&
|
||||
Curl_ssl_scache_use(cf, data) && !conn_cfg->verifystatus) {
|
||||
struct Curl_ssl_session *scs = NULL;
|
||||
|
||||
result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs);
|
||||
if(!result && scs && scs->sdata && scs->sdata_len) {
|
||||
const unsigned char *der_sessionid = scs->sdata;
|
||||
size_t der_sessionid_size = scs->sdata_len;
|
||||
SSL_SESSION *ssl_session = NULL;
|
||||
|
||||
/* If OpenSSL does not accept the session from the cache, this
|
||||
* is not an error. We continue without it. */
|
||||
ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid,
|
||||
(long)der_sessionid_size);
|
||||
if(ssl_session) {
|
||||
if(!SSL_set_session(octx->ssl, ssl_session)) {
|
||||
VERBOSE(char error_buffer[256]);
|
||||
infof(data, "SSL: SSL_set_session not accepted, "
|
||||
"continuing without: %s",
|
||||
ossl_strerror(ERR_get_error(), error_buffer,
|
||||
sizeof(error_buffer)));
|
||||
}
|
||||
else {
|
||||
if(conn_cfg->verifypeer &&
|
||||
(SSL_get_verify_result(octx->ssl) != X509_V_OK)
|
||||
#ifdef USE_APPLE_SECTRUST
|
||||
/* if sectrust is used and verified the session before */
|
||||
&& (!ssl_config->native_ca_store || !scs->sectrust_verified)
|
||||
#endif
|
||||
) {
|
||||
/* Session was from unverified connection, cannot reuse here */
|
||||
SSL_set_session(octx->ssl, NULL);
|
||||
infof(data, "SSL session not peer verified, not reusing");
|
||||
}
|
||||
else {
|
||||
infof(data, "SSL reusing session with ALPN '%s'",
|
||||
scs->alpn ? scs->alpn : "-");
|
||||
octx->reused_session = TRUE;
|
||||
#ifdef USE_APPLE_SECTRUST
|
||||
octx->sectrust_session = scs->sectrust_verified;
|
||||
#endif
|
||||
infof(data, "SSL verify result: %lx",
|
||||
(unsigned long)SSL_get_verify_result(octx->ssl));
|
||||
#ifdef HAVE_OPENSSL_EARLYDATA
|
||||
if(ssl_config->earlydata && scs->alpn &&
|
||||
SSL_SESSION_get_max_early_data(ssl_session) &&
|
||||
!cf->conn->bits.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) {
|
||||
SSL_SESSION_free(ssl_session);
|
||||
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)ssl_config;
|
||||
(void)sess_reuse_cb;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
SSL_SESSION_free(ssl_session);
|
||||
}
|
||||
else {
|
||||
infof(data, "SSL session not accepted by OpenSSL, continuing without");
|
||||
}
|
||||
(void)ossl_apply_session(octx, cf, data, &alpns, sess_reuse_cb, scs);
|
||||
}
|
||||
Curl_ssl_scache_return(cf, data, peer->scache_key, scs);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,7 +191,8 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
|
|||
SSL_SESSION *session,
|
||||
const char *alpn,
|
||||
unsigned char *quic_tp,
|
||||
size_t quic_tp_len);
|
||||
size_t quic_tp_len,
|
||||
struct Curl_ssl_session **psession);
|
||||
|
||||
/*
|
||||
* Get the server cert, verify it and show it, etc., only call failf() if
|
||||
|
|
|
|||
|
|
@ -877,19 +877,6 @@ void Curl_ssl_peer_cleanup(struct ssl_peer *peer)
|
|||
peer->type = CURL_SSL_PEER_DNS;
|
||||
}
|
||||
|
||||
static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
if(connssl) {
|
||||
connssl->ssl_impl->close(cf, data);
|
||||
connssl->state = ssl_connection_none;
|
||||
connssl->connecting_state = ssl_connect_1;
|
||||
connssl->prefs_checked = FALSE;
|
||||
Curl_ssl_peer_cleanup(&connssl->peer);
|
||||
}
|
||||
cf->connected = FALSE;
|
||||
}
|
||||
|
||||
static ssl_peer_type get_peer_type(const char *hostname)
|
||||
{
|
||||
if(hostname && hostname[0]) {
|
||||
|
|
@ -957,13 +944,18 @@ out:
|
|||
|
||||
static void ssl_cf_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct cf_call_data save;
|
||||
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
cf_close(cf, data);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
cf_ctx_free(cf->ctx);
|
||||
cf->ctx = NULL;
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
if(connssl) {
|
||||
connssl->ssl_impl->close(cf, data);
|
||||
connssl->state = ssl_connection_none;
|
||||
connssl->connecting_state = ssl_connect_1;
|
||||
connssl->prefs_checked = FALSE;
|
||||
Curl_ssl_peer_cleanup(&connssl->peer);
|
||||
Curl_ssl_session_destroy(connssl->session);
|
||||
cf_ctx_free(connssl);
|
||||
cf->ctx = NULL;
|
||||
}
|
||||
cf->connected = FALSE;
|
||||
}
|
||||
|
||||
static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
|
||||
|
|
@ -1776,4 +1768,30 @@ CURLcode Curl_on_session_reuse(struct Curl_cfilter *cf,
|
|||
return result;
|
||||
}
|
||||
|
||||
struct Curl_ssl_session *Curl_ssl_get_cf_session(struct Curl_easy *data,
|
||||
const struct Curl_cftype *cft,
|
||||
int sockindex)
|
||||
{
|
||||
if(data->conn &&
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
((cft == &Curl_cft_ssl) || (cft == &Curl_cft_ssl_proxy))) {
|
||||
#else
|
||||
(cft == &Curl_cft_ssl)) {
|
||||
#endif
|
||||
struct Curl_cfilter *cf1 = data->conn->cfilter[sockindex];
|
||||
for(; cf1; cf1 = cf1->next) {
|
||||
/* A tunneling proxy does not offer end2end encryption, even if
|
||||
* it does SSL itself (e.g. QUIC H3 proxy) */
|
||||
if(cf1->cft == cft)
|
||||
break;
|
||||
}
|
||||
if(cf1) {
|
||||
struct ssl_connect_data *connssl = cf1->ctx;
|
||||
if(connssl)
|
||||
return connssl->session;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* USE_SSL */
|
||||
|
|
|
|||
|
|
@ -116,6 +116,7 @@ struct ssl_connect_data {
|
|||
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 Curl_ssl_session *session; /* TLS session in use or NULL */
|
||||
struct curltime handshake_done; /* time when handshake finished */
|
||||
struct {
|
||||
char *alpn; /* ALPN value or NULL */
|
||||
|
|
@ -203,6 +204,13 @@ CURLcode Curl_on_session_reuse(struct Curl_cfilter *cf,
|
|||
struct alpn_spec *alpns,
|
||||
struct Curl_ssl_session *scs,
|
||||
bool *do_early_data, bool early_data_allowed);
|
||||
|
||||
/* Retrieve the SSL session held at the filter of type `cft` at
|
||||
* data's connection at `sockindex` or NULL if not found/available. */
|
||||
struct Curl_ssl_session *Curl_ssl_get_cf_session(struct Curl_easy *data,
|
||||
const struct Curl_cftype *cft,
|
||||
int sockindex);
|
||||
|
||||
#endif /* USE_SSL */
|
||||
|
||||
#endif /* HEADER_CURL_VTLS_INT_H */
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
#include "curl_trc.h"
|
||||
#include "curl_sha256.h"
|
||||
#include "rand.h"
|
||||
|
||||
#include "curlx/strdup.h"
|
||||
|
||||
/* a peer+tls-config we cache sessions for */
|
||||
struct Curl_ssl_scache_peer {
|
||||
|
|
@ -456,6 +456,54 @@ void Curl_ssl_session_destroy(struct Curl_ssl_session *s)
|
|||
}
|
||||
}
|
||||
|
||||
CURLcode Curl_ssl_session_dup(struct Curl_ssl_session *src,
|
||||
struct Curl_ssl_session **pdest)
|
||||
{
|
||||
struct Curl_ssl_session *dest = NULL;
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
|
||||
if(!src || !pdest)
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
*pdest = NULL;
|
||||
|
||||
dest = curlx_calloc(1, sizeof(*dest));
|
||||
if(!dest)
|
||||
goto out;
|
||||
|
||||
dest->ietf_tls_id = src->ietf_tls_id;
|
||||
dest->valid_until = src->valid_until;
|
||||
dest->earlydata_max = src->earlydata_max;
|
||||
dest->sectrust_verified = src->sectrust_verified;
|
||||
if(src->sdata_len) {
|
||||
dest->sdata = curlx_memdup(src->sdata, src->sdata_len);
|
||||
if(!dest->sdata)
|
||||
goto out;
|
||||
dest->sdata_len = src->sdata_len;
|
||||
}
|
||||
if(src->quic_tp_len) {
|
||||
dest->quic_tp = curlx_memdup(src->quic_tp, src->quic_tp_len);
|
||||
if(!dest->quic_tp)
|
||||
goto out;
|
||||
dest->quic_tp_len = src->quic_tp_len;
|
||||
}
|
||||
if(src->alpn) {
|
||||
dest->alpn = curlx_strdup(src->alpn);
|
||||
if(!dest->alpn)
|
||||
goto out;
|
||||
}
|
||||
result = CURLE_OK;
|
||||
|
||||
out:
|
||||
if(!result)
|
||||
*pdest = dest;
|
||||
else {
|
||||
*pdest = NULL;
|
||||
if(dest)
|
||||
Curl_ssl_session_destroy(dest);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer)
|
||||
{
|
||||
Curl_llist_destroy(&peer->sessions, NULL);
|
||||
|
|
|
|||
|
|
@ -157,6 +157,10 @@ CURLcode Curl_ssl_session_create2(void *sdata, size_t sdata_len,
|
|||
unsigned char *quic_tp, size_t quic_tp_len,
|
||||
struct Curl_ssl_session **psession);
|
||||
|
||||
/* Duplicate an ssl session */
|
||||
CURLcode Curl_ssl_session_dup(struct Curl_ssl_session *src,
|
||||
struct Curl_ssl_session **pdest);
|
||||
|
||||
/* Destroy a `session` instance. Can be called with NULL.
|
||||
* Does NOT need locking. */
|
||||
void Curl_ssl_session_destroy(struct Curl_ssl_session *s);
|
||||
|
|
|
|||
|
|
@ -397,14 +397,17 @@ CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
|
|||
int ietf_tls_id,
|
||||
const char *alpn,
|
||||
unsigned char *quic_tp,
|
||||
size_t quic_tp_len)
|
||||
size_t quic_tp_len,
|
||||
struct Curl_ssl_session **pscs)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct Curl_ssl_session *sc_session = NULL;
|
||||
struct Curl_ssl_session *sc_session = NULL, *sc_dup = NULL;
|
||||
unsigned char *sdata = NULL, *sdata_ptr, *qtp_clone = NULL;
|
||||
unsigned int sdata_len;
|
||||
unsigned int earlydata_max = 0;
|
||||
|
||||
if(pscs)
|
||||
*pscs = NULL;
|
||||
if(!session)
|
||||
goto out;
|
||||
|
||||
|
|
@ -446,13 +449,23 @@ CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
|
|||
earlydata_max, qtp_clone, quic_tp_len,
|
||||
&sc_session);
|
||||
sdata = NULL; /* took ownership of sdata */
|
||||
if(!result && pscs && /* return a duplicate if asked for and FTP */
|
||||
(cf->conn->scheme->family == CURLPROTO_FTP))
|
||||
result = Curl_ssl_session_dup(sc_session, &sc_dup);
|
||||
if(!result) {
|
||||
result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
|
||||
/* took ownership of `sc_session` */
|
||||
sc_session = NULL;
|
||||
}
|
||||
|
||||
out:
|
||||
curlx_free(sdata);
|
||||
if(!result && pscs) {
|
||||
*pscs = sc_dup;
|
||||
sc_dup = NULL;
|
||||
}
|
||||
Curl_ssl_session_destroy(sc_session);
|
||||
Curl_ssl_session_destroy(sc_dup);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -465,12 +478,17 @@ static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
|
|||
if(cf && session) {
|
||||
struct ssl_connect_data *connssl = cf->ctx;
|
||||
struct Curl_easy *data = CF_DATA_CURRENT(cf);
|
||||
struct Curl_ssl_session *scs = NULL;
|
||||
DEBUGASSERT(connssl);
|
||||
DEBUGASSERT(data);
|
||||
if(connssl && data) {
|
||||
(void)Curl_wssl_cache_session(cf, data, connssl->peer.scache_key,
|
||||
session, wolfSSL_version(ssl),
|
||||
connssl->negotiated.alpn, NULL, 0);
|
||||
connssl->negotiated.alpn, NULL, 0, &scs);
|
||||
if(scs) {
|
||||
Curl_ssl_session_destroy(connssl->session);
|
||||
connssl->session = scs;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -498,36 +516,35 @@ static CURLcode wssl_on_session_reuse(struct Curl_cfilter *cf,
|
|||
connssl->earlydata_max);
|
||||
}
|
||||
|
||||
static CURLcode wssl_setup_session(
|
||||
static bool wssl_apply_session(
|
||||
struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct wssl_ctx *wss,
|
||||
struct alpn_spec *alpns,
|
||||
const char *ssl_peer_key,
|
||||
Curl_wssl_init_session_reuse_cb *sess_reuse_cb)
|
||||
Curl_wssl_init_session_reuse_cb *sess_reuse_cb,
|
||||
struct Curl_ssl_session *scs)
|
||||
{
|
||||
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
|
||||
struct Curl_ssl_session *scs = NULL;
|
||||
CURLcode result;
|
||||
CURLcode result = CURLE_OK;
|
||||
WOLFSSL_SESSION *session = NULL;
|
||||
bool success = FALSE;
|
||||
|
||||
result = Curl_ssl_scache_take(cf, data, ssl_peer_key, &scs);
|
||||
if(!result && scs && scs->sdata && scs->sdata_len &&
|
||||
if(scs && scs->sdata && scs->sdata_len &&
|
||||
(!scs->alpn || Curl_alpn_contains_proto(alpns, scs->alpn))) {
|
||||
WOLFSSL_SESSION *session;
|
||||
/* wolfSSL changes the passed pointer for whatever reasons, yikes */
|
||||
const unsigned char *sdata = scs->sdata;
|
||||
session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata, (long)scs->sdata_len);
|
||||
if(session) {
|
||||
int ret = wolfSSL_set_session(wss->ssl, session);
|
||||
if(ret != WOLFSSL_SUCCESS) {
|
||||
Curl_ssl_session_destroy(scs);
|
||||
scs = NULL;
|
||||
infof(data, "cached session not accepted (%d), "
|
||||
"removing from cache", ret);
|
||||
goto out;
|
||||
}
|
||||
else {
|
||||
infof(data, "SSL reusing session with ALPN '%s'",
|
||||
scs->alpn ? scs->alpn : "-");
|
||||
success = TRUE;
|
||||
if(ssl_config->earlydata &&
|
||||
!cf->conn->bits.connect_only &&
|
||||
!strcmp("TLSv1.3", wolfSSL_get_version(wss->ssl))) {
|
||||
|
|
@ -535,7 +552,6 @@ static CURLcode wssl_setup_session(
|
|||
if(sess_reuse_cb) {
|
||||
result = sess_reuse_cb(cf, data, alpns, scs, &do_early_data);
|
||||
if(result) {
|
||||
wolfSSL_SESSION_free(session);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
|
@ -554,15 +570,14 @@ static CURLcode wssl_setup_session(
|
|||
#endif
|
||||
}
|
||||
}
|
||||
wolfSSL_SESSION_free(session);
|
||||
}
|
||||
else {
|
||||
failf(data, "could not decode previous session");
|
||||
}
|
||||
}
|
||||
out:
|
||||
Curl_ssl_scache_return(cf, data, ssl_peer_key, scs);
|
||||
return result;
|
||||
wolfSSL_SESSION_free(session);
|
||||
return success;
|
||||
}
|
||||
|
||||
static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf,
|
||||
|
|
@ -1151,6 +1166,9 @@ static CURLcode wssl_init_ssl_handle(
|
|||
unsigned char transport,
|
||||
Curl_wssl_init_session_reuse_cb *sess_reuse_cb)
|
||||
{
|
||||
struct Curl_ssl_session *scs = NULL;
|
||||
bool session_applied = FALSE;
|
||||
|
||||
/* Let's make an SSL structure */
|
||||
wctx->ssl = wolfSSL_new(wctx->ssl_ctx);
|
||||
if(!wctx->ssl) {
|
||||
|
|
@ -1170,11 +1188,30 @@ static CURLcode wssl_init_ssl_handle(
|
|||
(void)transport;
|
||||
#endif
|
||||
|
||||
if((cf->sockindex == SECONDARYSOCKET) && !(cf->cft->flags & CF_TYPE_PROXY)) {
|
||||
/* FTP is a bitch. On TLS secured transfers, it is a common server
|
||||
* option to require the client to use the SAME TLS session as on
|
||||
* the control connection or it fails the request. See #22225. */
|
||||
scs = Curl_ssl_get_cf_session(data, cf->cft, FIRSTSOCKET);
|
||||
if(scs) {
|
||||
session_applied = wssl_apply_session(cf, data, wctx, alpns,
|
||||
sess_reuse_cb, scs);
|
||||
if(session_applied)
|
||||
CURL_TRC_CF(data, cf, "applied SSL session from control connection");
|
||||
scs = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if there is a cached ID we can/should use here! */
|
||||
if(Curl_ssl_scache_use(cf, data)) {
|
||||
if(!session_applied && Curl_ssl_scache_use(cf, data)) {
|
||||
/* Set session from cache if there is one */
|
||||
(void)wssl_setup_session(cf, data, wctx, alpns, peer->scache_key,
|
||||
sess_reuse_cb);
|
||||
CURLcode result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs);
|
||||
if(!result && scs) {
|
||||
if(wssl_apply_session(cf, data, wctx, alpns, sess_reuse_cb, scs))
|
||||
Curl_ssl_scache_return(cf, data, peer->scache_key, scs);
|
||||
else
|
||||
Curl_ssl_session_destroy(scs);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ALPN
|
||||
|
|
|
|||
|
|
@ -88,7 +88,8 @@ CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
|
|||
int ietf_tls_id,
|
||||
const char *alpn,
|
||||
unsigned char *quic_tp,
|
||||
size_t quic_tp_len);
|
||||
size_t quic_tp_len,
|
||||
struct Curl_ssl_session **pscs);
|
||||
#endif
|
||||
|
||||
CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf,
|
||||
|
|
|
|||
|
|
@ -106,9 +106,7 @@ def httpd(env) -> Generator[Httpd, None, None]:
|
|||
@pytest.fixture(scope="session")
|
||||
def nghttpx(env, httpd) -> Generator[Union[Nghttpx, bool], None, None]:
|
||||
nghttpx = NghttpxQuic(env=env)
|
||||
if nghttpx.exists():
|
||||
if not nghttpx.supports_h3() and env.have_h3_curl():
|
||||
log.warning("nghttpx does not support QUIC, but curl does")
|
||||
if nghttpx.exists() and nghttpx.supports_h3() and env.have_h3_curl():
|
||||
nghttpx.clear_logs()
|
||||
assert nghttpx.initial_start()
|
||||
yield nghttpx
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ log = logging.getLogger(__name__)
|
|||
|
||||
|
||||
@pytest.mark.skipif(condition=not Env.has_vsftpd(), reason="missing vsftpd")
|
||||
@pytest.mark.skipif(condition=Env.curl_uses_lib('rustls-ffi'),
|
||||
reason="rustls does not support TLS session reuse")
|
||||
@pytest.mark.skipif(condition=Env.curl_uses_lib('libressl'),
|
||||
reason="libressl fails on TLS session reuse")
|
||||
class TestVsFTPD:
|
||||
|
||||
SUPPORTS_SSL = True
|
||||
|
|
@ -101,31 +105,39 @@ class TestVsFTPD:
|
|||
self.check_downloads(curl, srcfile, count)
|
||||
r.check_stats_timelines()
|
||||
|
||||
@pytest.mark.parametrize("docname", [
|
||||
'data-1k', 'data-1m', 'data-10m'
|
||||
@pytest.mark.parametrize("docname,count,secure", [
|
||||
['data-1k', 10, True],
|
||||
['data-1m', 5, True],
|
||||
['data-1m', 5, False],
|
||||
['data-10m', 2,True]
|
||||
])
|
||||
def test_31_03_download_10_serial(self, env: Env, vsftpds: VsFTPD, docname):
|
||||
def test_31_03_download_10_serial(self, env: Env, vsftpds: VsFTPD, docname, count, secure):
|
||||
curl = CurlClient(env=env)
|
||||
srcfile = os.path.join(vsftpds.docs_dir, f'{docname}')
|
||||
count = 10
|
||||
url = f'ftp://{env.ftp_domain}:{vsftpds.port}/{docname}?[0-{count-1}]'
|
||||
r = curl.ftp_ssl_get(urls=[url], with_stats=True)
|
||||
xargs = []
|
||||
if not secure:
|
||||
xargs.append('--insecure')
|
||||
r = curl.ftp_ssl_get(urls=[url], with_stats=True, extra_args=xargs)
|
||||
r.check_stats(count=count, http_status=226)
|
||||
self.check_downloads(curl, srcfile, count)
|
||||
assert r.total_connects == count + 1, 'should reuse the control conn'
|
||||
r.check_stats_timelines()
|
||||
|
||||
@pytest.mark.parametrize("docname", [
|
||||
'data-1k', 'data-1m', 'data-10m'
|
||||
@pytest.mark.parametrize("docname,count,secure", [
|
||||
['data-1k', 10, True],
|
||||
['data-1m', 5, True],
|
||||
['data-1m', 5, False],
|
||||
['data-10m', 2,True]
|
||||
])
|
||||
def test_31_04_download_10_parallel(self, env: Env, vsftpds: VsFTPD, docname):
|
||||
def test_31_04_download_10_parallel(self, env: Env, vsftpds: VsFTPD, docname, count, secure):
|
||||
curl = CurlClient(env=env)
|
||||
srcfile = os.path.join(vsftpds.docs_dir, f'{docname}')
|
||||
count = 10
|
||||
url = f'ftp://{env.ftp_domain}:{vsftpds.port}/{docname}?[0-{count-1}]'
|
||||
r = curl.ftp_ssl_get(urls=[url], with_stats=True, extra_args=[
|
||||
'--parallel'
|
||||
])
|
||||
xargs = ['--parallel']
|
||||
if not secure:
|
||||
xargs.append('--insecure')
|
||||
r = curl.ftp_ssl_get(urls=[url], with_stats=True, extra_args=xargs)
|
||||
r.check_stats(count=count, http_status=226)
|
||||
self.check_downloads(curl, srcfile, count)
|
||||
assert r.total_connects > count + 1, 'should have used several control conns'
|
||||
|
|
|
|||
|
|
@ -37,6 +37,10 @@ log = logging.getLogger(__name__)
|
|||
|
||||
|
||||
@pytest.mark.skipif(condition=not Env.has_vsftpd(), reason="missing vsftpd")
|
||||
@pytest.mark.skipif(condition=Env.curl_uses_lib('rustls-ffi'),
|
||||
reason="rustls does not support TLS session reuse")
|
||||
@pytest.mark.skipif(condition=Env.curl_uses_lib('libressl'),
|
||||
reason="libressl fails on TLS session reuse")
|
||||
class TestFtpsVsFTPD:
|
||||
|
||||
SUPPORTS_SSL = True
|
||||
|
|
@ -101,15 +105,20 @@ class TestFtpsVsFTPD:
|
|||
self.check_downloads(curl, srcfile, count)
|
||||
r.check_stats_timelines()
|
||||
|
||||
@pytest.mark.parametrize("docname", [
|
||||
'data-1k', 'data-1m', 'data-10m'
|
||||
@pytest.mark.parametrize("docname,count,secure", [
|
||||
['data-1k', 10, True],
|
||||
['data-1m', 5, True],
|
||||
['data-1m', 5, False],
|
||||
['data-10m', 2,True]
|
||||
])
|
||||
def test_32_03_download_10_serial(self, env: Env, vsftpds: VsFTPD, docname):
|
||||
def test_32_03_download_10_serial(self, env: Env, vsftpds: VsFTPD, docname, count, secure):
|
||||
curl = CurlClient(env=env)
|
||||
srcfile = os.path.join(vsftpds.docs_dir, f'{docname}')
|
||||
count = 10
|
||||
url = f'ftps://{env.ftp_domain}:{vsftpds.port}/{docname}?[0-{count-1}]'
|
||||
r = curl.ftp_get(urls=[url], with_stats=True)
|
||||
xargs = []
|
||||
if not secure:
|
||||
xargs.append('--insecure')
|
||||
r = curl.ftp_get(urls=[url], with_stats=True, extra_args=xargs)
|
||||
r.check_stats(count=count, http_status=226)
|
||||
self.check_downloads(curl, srcfile, count)
|
||||
assert r.total_connects == count + 1, 'should reuse the control conn'
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ class VsFTPD:
|
|||
f'rsa_cert_file={creds.cert_file}',
|
||||
f'rsa_private_key_file={creds.pkey_file}',
|
||||
# require_ssl_reuse=YES means ctrl and data connection need to use the same session
|
||||
'require_ssl_reuse=NO',
|
||||
'require_ssl_reuse=YES',
|
||||
])
|
||||
if self._ssl_implicit:
|
||||
conf.extend([
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue