struct Curl_cftype Curl_cft_h1_proxy = {
"H1-PROXY",
- CF_TYPE_IP_CONNECT,
+ CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
0,
cf_h1_proxy_destroy,
cf_h1_proxy_connect,
struct Curl_cftype Curl_cft_h2_proxy = {
"H2-PROXY",
- CF_TYPE_IP_CONNECT,
+ CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
CURL_LOG_LVL_NONE,
cf_h2_proxy_destroy,
cf_h2_proxy_connect,
struct Curl_cftype Curl_cft_haproxy = {
"HAPROXY",
- 0,
+ CF_TYPE_PROXY,
0,
cf_haproxy_destroy,
cf_haproxy_connect,
* connection, etc.
* CF_TYPE_SSL: provide SSL/TLS
* CF_TYPE_MULTIPLEX: provides multiplexing of easy handles
+ * CF_TYPE_PROXY provides proxying
*/
#define CF_TYPE_IP_CONNECT (1 << 0)
#define CF_TYPE_SSL (1 << 1)
#define CF_TYPE_MULTIPLEX (1 << 2)
+#define CF_TYPE_PROXY (1 << 3)
/* A connection filter type, e.g. specific implementation. */
struct Curl_cftype {
struct Curl_cftype Curl_cft_http_proxy = {
"HTTP-PROXY",
- CF_TYPE_IP_CONNECT,
+ CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
0,
http_proxy_cf_destroy,
http_proxy_cf_connect,
struct Curl_cftype Curl_cft_socks_proxy = {
"SOCKS-PROXYY",
- CF_TYPE_IP_CONNECT,
+ CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
0,
socks_proxy_cf_destroy,
socks_proxy_cf_connect,
char *dispname; /* display version of hostname */
char *sni; /* SNI version of hostname or NULL if not usable */
ssl_peer_type type; /* type of the peer information */
+ int port; /* port we are talking to */
+ int transport; /* TCP or QUIC */
};
struct ssl_primary_config {
long age; /* just a number, the higher the more recent */
int remote_port; /* remote port */
int conn_to_port; /* remote port for the connection (may be -1) */
+ int transport; /* TCP or QUIC */
struct ssl_primary_config ssl_config; /* setup for this session */
};
struct cf_ngtcp2_ctx {
struct cf_quic_ctx q;
struct ssl_peer peer;
- struct quic_tls_ctx tls;
+ struct curl_tls_ctx tls;
ngtcp2_path connected_path;
ngtcp2_conn *qconn;
ngtcp2_cid dcid;
(void)save;
}
-static CURLcode tls_ctx_setup(struct quic_tls_ctx *ctx,
- struct Curl_cfilter *cf,
- struct Curl_easy *data)
+#ifdef USE_OPENSSL
+/* The "new session" callback must return zero if the session can be removed
+ * or non-zero if the session has been put into the session cache.
+ */
+static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
{
+ struct Curl_cfilter *cf;
+ struct cf_ngtcp2_ctx *ctx;
+ struct Curl_easy *data;
+ ngtcp2_crypto_conn_ref *cref;
+
+ cref = (ngtcp2_crypto_conn_ref *)SSL_get_app_data(ssl);
+ cf = cref? cref->user_data : NULL;
+ ctx = cf? cf->ctx : NULL;
+ data = cf? CF_DATA_CURRENT(cf) : NULL;
+ if(cf && data && ctx) {
+ CURLcode result = Curl_ossl_add_session(cf, data, &ctx->peer,
+ ssl_sessionid);
+ return result? 0 : 1;
+ }
+ return 0;
+}
+#endif /* USE_OPENSSL */
+
+static CURLcode tls_ctx_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ void *user_data)
+{
+ struct curl_tls_ctx *ctx = user_data;
(void)cf;
#ifdef USE_OPENSSL
#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
- if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ssl_ctx) != 0) {
+ if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx)
+ != 0) {
failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
return CURLE_FAILED_INIT;
}
#else
- if(ngtcp2_crypto_quictls_configure_client_context(ctx->ssl_ctx) != 0) {
+ if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) {
failf(data, "ngtcp2_crypto_quictls_configure_client_context failed");
return CURLE_FAILED_INIT;
}
#endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */
+ /* Enable the session cache because it's a prerequisite for the
+ * "new session" callback. Use the "external storage" mode to prevent
+ * OpenSSL from creating an internal session cache.
+ */
+ SSL_CTX_set_session_cache_mode(ctx->ossl.ssl_ctx,
+ SSL_SESS_CACHE_CLIENT |
+ SSL_SESS_CACHE_NO_INTERNAL);
+ SSL_CTX_sess_set_new_cb(ctx->ossl.ssl_ctx, ossl_new_session_cb);
+
#elif defined(USE_GNUTLS)
- if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) {
+ if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls.session) != 0) {
failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed");
return CURLE_FAILED_INIT;
}
Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
H3_STREAM_POOL_SPARES);
- result = Curl_ssl_peer_init(&ctx->peer, cf);
+ result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
if(result)
return result;
#define H3_ALPN "\x2h3\x5h3-29"
result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
H3_ALPN, sizeof(H3_ALPN) - 1,
- tls_ctx_setup, &ctx->conn_ref);
+ tls_ctx_setup, &ctx->tls, &ctx->conn_ref);
if(result)
return result;
+#ifdef USE_OPENSSL
+ SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0);
+#endif
+
ctx->dcid.datalen = NGTCP2_MAX_CIDLEN;
result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN);
if(result)
if(rc)
return CURLE_QUIC_CONNECT_ERROR;
-#ifdef USE_GNUTLS
- ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls->session);
+#ifdef USE_OPENSSL
+ ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl);
+#elif defined(USE_GNUTLS)
+ ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session);
#else
ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ssl);
#endif
struct cf_osslq_ctx {
struct cf_quic_ctx q;
struct ssl_peer peer;
- struct quic_tls_ctx tls;
+ struct curl_tls_ctx tls;
struct cf_call_data call_data;
struct cf_osslq_h3conn h3;
struct curltime started_at; /* time the current attempt started */
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
- if(ctx && ctx->tls.ssl) {
+ if(ctx && ctx->tls.ossl.ssl) {
/* TODO: send connection close */
CURL_TRC_CF(data, cf, "cf_osslq_close()");
cf_osslq_ctx_clear(ctx);
(reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) {
result = CURLE_PEER_FAILED_VERIFICATION;
- lerr = SSL_get_verify_result(ctx->tls.ssl);
+ lerr = SSL_get_verify_result(ctx->tls.ossl.ssl);
if(lerr != X509_V_OK) {
ssl_config->certverifyresult = lerr;
msnprintf(ebuf, sizeof(ebuf),
Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
H3_STREAM_POOL_SPARES);
- result = Curl_ssl_peer_init(&ctx->peer, cf);
+ result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
if(result)
goto out;
#define H3_ALPN "\x2h3"
result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
H3_ALPN, sizeof(H3_ALPN) - 1,
- NULL, NULL);
+ NULL, NULL, NULL);
if(result)
goto out;
goto out;
}
- if(!SSL_set1_initial_peer_addr(ctx->tls.ssl, baddr)) {
+ if(!SSL_set1_initial_peer_addr(ctx->tls.ossl.ssl, baddr)) {
failf(data, "failed to set the initial peer address");
result = CURLE_FAILED_INIT;
goto out;
}
- if(!SSL_set_blocking_mode(ctx->tls.ssl, 0)) {
+ if(!SSL_set_blocking_mode(ctx->tls.ossl.ssl, 0)) {
failf(data, "failed to turn off blocking mode");
result = CURLE_FAILED_INIT;
goto out;
#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT
/* Added in OpenSSL v3.3.x */
- if(!SSL_set_feature_request_uint(ctx->tls.ssl, SSL_VALUE_QUIC_IDLE_TIMEOUT,
+ if(!SSL_set_feature_request_uint(ctx->tls.ossl.ssl,
+ SSL_VALUE_QUIC_IDLE_TIMEOUT,
CURL_QUIC_MAX_IDLE_MS)) {
CURL_TRC_CF(data, cf, "error setting idle timeout, ");
result = CURLE_FAILED_INIT;
}
#endif
- SSL_set_bio(ctx->tls.ssl, bio, bio);
+ SSL_set_bio(ctx->tls.ossl.ssl, bio, bio);
bio = NULL;
- SSL_set_connect_state(ctx->tls.ssl);
- SSL_set_incoming_stream_policy(ctx->tls.ssl,
+ SSL_set_connect_state(ctx->tls.ossl.ssl);
+ SSL_set_incoming_stream_policy(ctx->tls.ossl.ssl,
SSL_INCOMING_STREAM_POLICY_ACCEPT, 0);
/* setup the H3 things on top of the QUIC connection */
- result = cf_osslq_h3conn_init(ctx, ctx->tls.ssl, cf);
+ result = cf_osslq_h3conn_init(ctx, ctx->tls.ossl.ssl, cf);
out:
if(bio)
struct cf_osslq_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
- if(!ctx->tls.ssl)
+ if(!ctx->tls.ossl.ssl)
goto out;
ERR_clear_error();
/* 1. Check for new incoming streams */
while(1) {
- SSL *snew = SSL_accept_stream(ctx->tls.ssl, SSL_ACCEPT_STREAM_NO_BLOCK);
+ SSL *snew = SSL_accept_stream(ctx->tls.ossl.ssl,
+ SSL_ACCEPT_STREAM_NO_BLOCK);
if(!snew)
break;
(void)cf_osslq_h3conn_add_stream(&ctx->h3, snew, cf, data);
}
- if(!SSL_handle_events(ctx->tls.ssl)) {
- int detail = SSL_get_error(ctx->tls.ssl, 0);
+ if(!SSL_handle_events(ctx->tls.ossl.ssl)) {
+ int detail = SSL_get_error(ctx->tls.ossl.ssl, 0);
result = cf_osslq_ssl_err(cf, data, detail, CURLE_RECV_ERROR);
}
struct cf_osslq_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
- if(!ctx->tls.ssl || !ctx->h3.conn)
+ if(!ctx->tls.ossl.ssl || !ctx->h3.conn)
goto out;
for(;;) {
struct cf_osslq_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
- if(!ctx->tls.ssl)
+ if(!ctx->tls.ossl.ssl)
goto out;
ERR_clear_error();
if(result)
goto out;
- if(!SSL_handle_events(ctx->tls.ssl)) {
- int detail = SSL_get_error(ctx->tls.ssl, 0);
+ if(!SSL_handle_events(ctx->tls.ossl.ssl)) {
+ int detail = SSL_get_error(ctx->tls.ossl.ssl, 0);
result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR);
}
timediff_t timeoutms;
int is_infinite = TRUE;
- if(ctx->tls.ssl &&
- SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite) &&
+ if(ctx->tls.ossl.ssl &&
+ SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite) &&
!is_infinite) {
timeoutms = curlx_tvtoms(&tv);
/* QUIC want to be called again latest at the returned timeout */
result = cf_progress_egress(cf, data);
if(result)
goto out;
- if(SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite)) {
+ if(SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite)) {
timeoutms = curlx_tvtoms(&tv);
}
}
goto out;
}
- if(!ctx->tls.ssl) {
+ if(!ctx->tls.ossl.ssl) {
ctx->started_at = now;
result = cf_osslq_ctx_start(cf, data);
if(result)
}
ERR_clear_error();
- err = SSL_do_handshake(ctx->tls.ssl);
+ err = SSL_do_handshake(ctx->tls.ossl.ssl);
if(err == 1) {
/* connected */
}
}
else {
- int detail = SSL_get_error(ctx->tls.ssl, err);
+ int detail = SSL_get_error(ctx->tls.ossl.ssl, err);
switch(detail) {
case SSL_ERROR_WANT_READ:
ctx->q.last_io = now;
}
out:
- if(result == CURLE_RECV_ERROR && ctx->tls.ssl && ctx->protocol_shutdown) {
+ if(result == CURLE_RECV_ERROR && ctx->tls.ossl.ssl &&
+ ctx->protocol_shutdown) {
/* When a QUIC server instance is shutting down, it may send us a
* CONNECTION_CLOSE right away. Our connection then enters the DRAINING
* state. The CONNECT may work in the near future again. Indicate
}
DEBUGASSERT(stream->s.id == -1);
- *err = cf_osslq_stream_open(&stream->s, ctx->tls.ssl, 0,
+ *err = cf_osslq_stream_open(&stream->s, ctx->tls.ossl.ssl, 0,
&ctx->stream_bufcp, data);
if(*err) {
failf(data, "can't get bidi streams");
CF_DATA_SAVE(save, cf, data);
DEBUGASSERT(cf->connected);
- DEBUGASSERT(ctx->tls.ssl);
+ DEBUGASSERT(ctx->tls.ossl.ssl);
DEBUGASSERT(ctx->h3.conn);
*err = CURLE_OK;
CF_DATA_SAVE(save, cf, data);
DEBUGASSERT(cf->connected);
DEBUGASSERT(ctx);
- DEBUGASSERT(ctx->tls.ssl);
+ DEBUGASSERT(ctx->tls.ossl.ssl);
DEBUGASSERT(ctx->h3.conn);
*err = CURLE_OK;
CF_DATA_SAVE(save, cf, data);
*input_pending = FALSE;
- if(!ctx->tls.ssl)
+ if(!ctx->tls.ossl.ssl)
goto out;
#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT
{
timediff_t idletime;
uint64_t idle_ms = ctx->max_idle_ms;
- if(!SSL_get_value_uint(ctx->tls.ssl, SSL_VALUE_CLASS_FEATURE_NEGOTIATED,
+ if(!SSL_get_value_uint(ctx->tls.ossl.ssl,
+ SSL_VALUE_CLASS_FEATURE_NEGOTIATED,
SSL_VALUE_QUIC_IDLE_TIMEOUT, &idle_ms)) {
CURL_TRC_CF(data, cf, "error getting negotiated idle timeout, "
"assume connection is dead.");
{
struct cf_osslq_ctx *ctx = cf->ctx;
- if(!ctx->tls.ssl) {
+ if(!ctx->tls.ossl.ssl) {
/* NOP */
}
else if(!cf->connected) {
/* during handshake, transfer has not started yet. we always
* add our socket for polling if SSL wants to send/recv */
Curl_pollset_set(data, ps, ctx->q.sockfd,
- SSL_net_read_desired(ctx->tls.ssl),
- SSL_net_write_desired(ctx->tls.ssl));
+ SSL_net_read_desired(ctx->tls.ossl.ssl),
+ SSL_net_write_desired(ctx->tls.ossl.ssl));
}
else {
/* once connected, we only modify the socket if it is present.
Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
if(want_recv || want_send) {
Curl_pollset_set(data, ps, ctx->q.sockfd,
- SSL_net_read_desired(ctx->tls.ssl),
- SSL_net_write_desired(ctx->tls.ssl));
+ SSL_net_read_desired(ctx->tls.ossl.ssl),
+ SSL_net_write_desired(ctx->tls.ossl.ssl));
}
}
}
#ifdef SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL
/* Added in OpenSSL v3.3.x */
uint64_t v;
- if(!SSL_get_value_uint(ctx->tls.ssl, SSL_VALUE_CLASS_GENERIC,
+ if(!SSL_get_value_uint(ctx->tls.ossl.ssl, SSL_VALUE_CLASS_GENERIC,
SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL, &v)) {
CURL_TRC_CF(data, cf, "error getting available local bidi streams");
return CURLE_HTTP3;
struct cf_quiche_ctx {
struct cf_quic_ctx q;
struct ssl_peer peer;
- struct quic_tls_ctx tls;
+ struct curl_tls_ctx tls;
quiche_conn *qconn;
quiche_config *cfg;
quiche_h3_conn *h3c;
quiche_conn_free(ctx->qconn);
if(ctx->cfg)
quiche_config_free(ctx->cfg);
- /* quiche just freed ctx->tls.ssl */
- ctx->tls.ssl = NULL;
+ /* quiche just freed it */
+ ctx->tls.ossl.ssl = NULL;
Curl_vquic_tls_cleanup(&ctx->tls);
Curl_ssl_peer_cleanup(&ctx->peer);
vquic_ctx_free(&ctx->q);
return CURLE_OK;
}
else if(QUICHE_ERR_TLS_FAIL == nread) {
- long verify_ok = SSL_get_verify_result(ctx->tls.ssl);
+ long verify_ok = SSL_get_verify_result(ctx->tls.ossl.ssl);
if(verify_ok != X509_V_OK) {
failf(r->data, "SSL certificate problem: %s",
X509_verify_cert_error_string(verify_ok));
if(result)
return result;
- result = Curl_ssl_peer_init(&ctx->peer, cf);
+ result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
if(result)
return result;
result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
QUICHE_H3_APPLICATION_PROTOCOL,
sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1,
- NULL, cf);
+ NULL, NULL, cf);
if(result)
return result;
(struct sockaddr *)&ctx->q.local_addr,
ctx->q.local_addrlen,
&sockaddr->sa_addr, sockaddr->addrlen,
- ctx->cfg, ctx->tls.ssl, false);
+ ctx->cfg, ctx->tls.ossl.ssl, false);
if(!ctx->qconn) {
failf(data, "can't create quiche connection");
return CURLE_OUT_OF_MEMORY;
#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
#endif
-#ifdef USE_OPENSSL
-#define QUIC_CIPHERS \
- "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
- "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
-#define QUIC_GROUPS "P-256:X25519:P-384:P-521"
-#elif defined(USE_GNUTLS)
-#define QUIC_PRIORITY \
- "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
- "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
- "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
- "%DISABLE_TLS13_COMPAT_MODE"
-#elif defined(USE_WOLFSSL)
+#if defined(USE_WOLFSSL)
+
#define QUIC_CIPHERS \
"TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
"POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
#define QUIC_GROUPS "P-256:P-384:P-521"
-#endif
-
-
-#ifdef USE_OPENSSL
-
-static void keylog_callback(const SSL *ssl, const char *line)
-{
- (void)ssl;
- Curl_tls_keylog_write_line(line);
-}
-
-static CURLcode curl_ossl_init_ctx(struct quic_tls_ctx *ctx,
- struct Curl_cfilter *cf,
- struct Curl_easy *data,
- Curl_vquic_tls_ctx_setup *ctx_setup)
-{
- struct ssl_primary_config *conn_config;
- CURLcode result = CURLE_FAILED_INIT;
-
- DEBUGASSERT(!ctx->ssl_ctx);
-#ifdef USE_OPENSSL_QUIC
- ctx->ssl_ctx = SSL_CTX_new(OSSL_QUIC_client_method());
-#else
- ctx->ssl_ctx = SSL_CTX_new(TLS_method());
-#endif
- if(!ctx->ssl_ctx) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- conn_config = Curl_ssl_cf_get_primary_config(cf);
- if(!conn_config) {
- result = CURLE_FAILED_INIT;
- goto out;
- }
-
- if(ctx_setup) {
- result = ctx_setup(ctx, cf, data);
- if(result)
- goto out;
- }
-
- SSL_CTX_set_default_verify_paths(ctx->ssl_ctx);
-
- {
- const char *curves = conn_config->curves ?
- conn_config->curves : QUIC_GROUPS;
- if(!SSL_CTX_set1_curves_list(ctx->ssl_ctx, curves)) {
- failf(data, "failed setting curves list for QUIC: '%s'", curves);
- return CURLE_SSL_CIPHER;
- }
- }
-
-#ifndef OPENSSL_IS_BORINGSSL
- {
- const char *ciphers13 = conn_config->cipher_list13 ?
- conn_config->cipher_list13 : QUIC_CIPHERS;
- if(SSL_CTX_set_ciphersuites(ctx->ssl_ctx, ciphers13) != 1) {
- failf(data, "failed setting QUIC cipher suite: %s", ciphers13);
- return CURLE_SSL_CIPHER;
- }
- infof(data, "QUIC cipher selection: %s", ciphers13);
- }
-#endif
-
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
- SSL_CTX_set_keylog_callback(ctx->ssl_ctx, keylog_callback);
- }
-
- /* OpenSSL always tries to verify the peer, this only says whether it should
- * fail to connect if the verification fails, or if it should continue
- * anyway. In the latter case the result of the verification is checked with
- * SSL_get_verify_result() below. */
- SSL_CTX_set_verify(ctx->ssl_ctx, conn_config->verifypeer ?
- SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
-
- /* give application a chance to interfere with SSL set up. */
- if(data->set.ssl.fsslctx) {
- /* When a user callback is installed to modify the SSL_CTX,
- * we need to do the full initialization before calling it.
- * See: #11800 */
- if(!ctx->x509_store_setup) {
- result = Curl_ssl_setup_x509_store(cf, data, ctx->ssl_ctx);
- if(result)
- goto out;
- ctx->x509_store_setup = TRUE;
- }
- Curl_set_in_callback(data, true);
- result = (*data->set.ssl.fsslctx)(data, ctx->ssl_ctx,
- data->set.ssl.fsslctxp);
- Curl_set_in_callback(data, false);
- if(result) {
- failf(data, "error signaled by ssl ctx callback");
- goto out;
- }
- }
- result = CURLE_OK;
-
-out:
- if(result && ctx->ssl_ctx) {
- SSL_CTX_free(ctx->ssl_ctx);
- ctx->ssl_ctx = NULL;
- }
- return result;
-}
-
-static CURLcode curl_ossl_set_client_cert(struct quic_tls_ctx *ctx,
- struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- SSL_CTX *ssl_ctx = ctx->ssl_ctx;
- const struct ssl_config_data *ssl_config;
-
- ssl_config = Curl_ssl_cf_get_config(cf, data);
- DEBUGASSERT(ssl_config);
-
- if(ssl_config->primary.clientcert ||
- ssl_config->primary.cert_blob ||
- ssl_config->cert_type) {
- return Curl_ossl_set_client_cert(
- data, ssl_ctx, ssl_config->primary.clientcert,
- ssl_config->primary.cert_blob, ssl_config->cert_type,
- ssl_config->key, ssl_config->key_blob,
- ssl_config->key_type, ssl_config->key_passwd);
- }
-
- return CURLE_OK;
-}
-
-/** SSL callbacks ***/
-
-static CURLcode curl_ossl_init_ssl(struct quic_tls_ctx *ctx,
- struct Curl_easy *data,
- struct ssl_peer *peer,
- const char *alpn, size_t alpn_len,
- void *user_data)
-{
- DEBUGASSERT(!ctx->ssl);
- ctx->ssl = SSL_new(ctx->ssl_ctx);
-
- SSL_set_app_data(ctx->ssl, user_data);
- SSL_set_connect_state(ctx->ssl);
-#ifndef USE_OPENSSL_QUIC
- SSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
-#endif
-
- if(alpn)
- SSL_set_alpn_protos(ctx->ssl, (const uint8_t *)alpn, (int)alpn_len);
-
- if(peer->sni) {
- if(!SSL_set_tlsext_host_name(ctx->ssl, peer->sni)) {
- failf(data, "Failed set SNI");
- SSL_free(ctx->ssl);
- ctx->ssl = NULL;
- return CURLE_QUIC_CONNECT_ERROR;
- }
- }
- return CURLE_OK;
-}
-
-#elif defined(USE_GNUTLS)
-static int keylog_callback(gnutls_session_t session, const char *label,
- const gnutls_datum_t *secret)
-{
- gnutls_datum_t crandom;
- gnutls_datum_t srandom;
-
- gnutls_session_get_random(session, &crandom, &srandom);
- if(crandom.size != 32) {
- return -1;
- }
-
- Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
- return 0;
-}
-
-static CURLcode curl_gtls_init_ctx(struct quic_tls_ctx *ctx,
- struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct ssl_peer *peer,
- const char *alpn, size_t alpn_len,
- Curl_vquic_tls_ctx_setup *ctx_setup,
- void *user_data)
-{
- struct ssl_primary_config *conn_config;
- CURLcode result;
- gnutls_datum_t alpns[5];
- /* this will need some attention when HTTPS proxy over QUIC get fixed */
- long * const pverifyresult = &data->set.ssl.certverifyresult;
- int rc;
-
- conn_config = Curl_ssl_cf_get_primary_config(cf);
- if(!conn_config)
- return CURLE_FAILED_INIT;
-
- DEBUGASSERT(ctx->gtls == NULL);
- ctx->gtls = calloc(1, sizeof(*(ctx->gtls)));
- if(!ctx->gtls)
- return CURLE_OUT_OF_MEMORY;
-
- result = gtls_client_init(data, conn_config, &data->set.ssl,
- peer, ctx->gtls, pverifyresult);
- if(result)
- return result;
-
- gnutls_session_set_ptr(ctx->gtls->session, user_data);
-
- if(ctx_setup) {
- result = ctx_setup(ctx, cf, data);
- if(result)
- return result;
- }
-
- rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL);
- if(rc < 0) {
- CURL_TRC_CF(data, cf, "gnutls_priority_set_direct failed: %s\n",
- gnutls_strerror(rc));
- return CURLE_QUIC_CONNECT_ERROR;
- }
-
- /* 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(ctx->gtls->session, keylog_callback);
- }
-
- /* convert the ALPN string from our arguments to a list of strings
- * that gnutls wants and will convert internally back to this very
- * string for sending to the server. nice. */
- if(alpn) {
- size_t i, alen = alpn_len;
- unsigned char *s = (unsigned char *)alpn;
- unsigned char slen;
- for(i = 0; (i < ARRAYSIZE(alpns)) && alen; ++i) {
- slen = s[0];
- if(slen >= alen)
- return CURLE_FAILED_INIT;
- alpns[i].data = s + 1;
- alpns[i].size = slen;
- s += slen + 1;
- alen -= (size_t)slen + 1;
- }
- if(alen) /* not all alpn chars used, wrong format or too many */
- return CURLE_FAILED_INIT;
- if(i) {
- gnutls_alpn_set_protocols(ctx->gtls->session,
- alpns, (unsigned int)i,
- GNUTLS_ALPN_MANDATORY);
- }
- }
-
- return CURLE_OK;
-}
-#elif defined(USE_WOLFSSL)
#if defined(HAVE_SECRET_CALLBACK)
static void keylog_callback(const WOLFSSL *ssl, const char *line)
}
#endif
-static CURLcode curl_wssl_init_ctx(struct quic_tls_ctx *ctx,
+static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
- Curl_vquic_tls_ctx_setup *ctx_setup)
+ Curl_vquic_tls_ctx_setup *cb_setup,
+ void *cb_user_data)
{
struct ssl_primary_config *conn_config;
CURLcode result = CURLE_FAILED_INIT;
goto out;
}
- if(ctx_setup) {
- result = ctx_setup(ctx, cf, data);
+ if(cb_setup) {
+ result = cb_setup(cf, data, cb_user_data);
if(result)
goto out;
}
/** SSL callbacks ***/
-static CURLcode curl_wssl_init_ssl(struct quic_tls_ctx *ctx,
+static CURLcode curl_wssl_init_ssl(struct curl_tls_ctx *ctx,
struct Curl_easy *data,
struct ssl_peer *peer,
const char *alpn, size_t alpn_len,
}
#endif /* defined(USE_WOLFSSL) */
-CURLcode Curl_vquic_tls_init(struct quic_tls_ctx *ctx,
+CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
const char *alpn, size_t alpn_len,
- Curl_vquic_tls_ctx_setup *ctx_setup,
- void *user_data)
+ Curl_vquic_tls_ctx_setup *cb_setup,
+ void *cb_user_data, void *ssl_user_data)
{
CURLcode result;
#ifdef USE_OPENSSL
- result = curl_ossl_init_ctx(ctx, cf, data, ctx_setup);
- if(result)
- return result;
-
- result = curl_ossl_set_client_cert(ctx, cf, data);
- if(result)
- return result;
-
- return curl_ossl_init_ssl(ctx, data, peer, alpn, alpn_len, user_data);
+ (void)result;
+ return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, TRNSPRT_QUIC,
+ (const unsigned char *)alpn, alpn_len,
+ cb_setup, cb_user_data, NULL, ssl_user_data);
#elif defined(USE_GNUTLS)
(void)result;
- return curl_gtls_init_ctx(ctx, cf, data, peer, alpn, alpn_len,
- ctx_setup, user_data);
+ return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer,
+ (const unsigned char *)alpn, alpn_len,
+ cb_setup, cb_user_data, ssl_user_data);
#elif defined(USE_WOLFSSL)
- result = curl_wssl_init_ctx(ctx, cf, data, ctx_setup);
+ result = curl_wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data);
if(result)
return result;
- return curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, user_data);
+ return curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, ssl_user_data);
#else
#error "no TLS lib in used, should not happen"
return CURLE_FAILED_INIT;
#endif
}
-void Curl_vquic_tls_cleanup(struct quic_tls_ctx *ctx)
+void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx)
{
#ifdef USE_OPENSSL
- if(ctx->ssl)
- SSL_free(ctx->ssl);
- if(ctx->ssl_ctx)
- SSL_CTX_free(ctx->ssl_ctx);
+ if(ctx->ossl.ssl)
+ SSL_free(ctx->ossl.ssl);
+ if(ctx->ossl.ssl_ctx)
+ SSL_CTX_free(ctx->ossl.ssl_ctx);
#elif defined(USE_GNUTLS)
- if(ctx->gtls) {
- if(ctx->gtls->cred)
- gnutls_certificate_free_credentials(ctx->gtls->cred);
- if(ctx->gtls->session)
- gnutls_deinit(ctx->gtls->session);
- free(ctx->gtls);
- }
+ if(ctx->gtls.cred)
+ gnutls_certificate_free_credentials(ctx->gtls.cred);
+ if(ctx->gtls.session)
+ gnutls_deinit(ctx->gtls.session);
#elif defined(USE_WOLFSSL)
if(ctx->ssl)
wolfSSL_free(ctx->ssl);
memset(ctx, 0, sizeof(*ctx));
}
-CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx,
+CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx,
struct Curl_cfilter *cf,
struct Curl_easy *data)
{
#ifdef USE_OPENSSL
- if(!ctx->x509_store_setup) {
- CURLcode result = Curl_ssl_setup_x509_store(cf, data, ctx->ssl_ctx);
+ if(!ctx->ossl.x509_store_setup) {
+ CURLcode result = Curl_ssl_setup_x509_store(cf, data, ctx->ossl.ssl_ctx);
if(result)
return result;
- ctx->x509_store_setup = TRUE;
+ ctx->ossl.x509_store_setup = TRUE;
}
#else
(void)ctx; (void)cf; (void)data;
return CURLE_OK;
}
-CURLcode Curl_vquic_tls_verify_peer(struct quic_tls_ctx *ctx,
+CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer)
if(!conn_config)
return CURLE_FAILED_INIT;
- if(conn_config->verifyhost) {
#ifdef USE_OPENSSL
- X509 *server_cert;
- server_cert = SSL_get1_peer_certificate(ctx->ssl);
- if(!server_cert) {
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- result = Curl_ossl_verifyhost(data, cf->conn, peer, server_cert);
- X509_free(server_cert);
- if(result)
- return result;
+ (void)conn_config;
+ result = Curl_oss_check_peer_cert(cf, data, &ctx->ossl, peer);
#elif defined(USE_GNUTLS)
- result = Curl_gtls_verifyserver(data, ctx->gtls->session,
+ if(conn_config->verifyhost) {
+ result = Curl_gtls_verifyserver(data, ctx->gtls.session,
conn_config, &data->set.ssl, peer,
data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
if(result)
return result;
+ }
#elif defined(USE_WOLFSSL)
+ (void)data;
+ if(conn_config->verifyhost) {
if(!peer->sni ||
wolfSSL_check_domain_name(ctx->ssl, peer->sni) == SSL_FAILURE)
return CURLE_PEER_FAILED_VERIFICATION;
-#endif
- infof(data, "Verified certificate just fine");
}
- else
- infof(data, "Skipped certificate verification");
-#ifdef USE_OPENSSL
- if(data->set.ssl.certinfo)
- /* asked to gather certificate info */
- (void)Curl_ossl_certchain(data, ctx->ssl);
#endif
return result;
}
#include "curl_setup.h"
#include "bufq.h"
+#include "vtls/openssl.h"
#if defined(ENABLE_QUIC) && \
(defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL))
-struct quic_tls_ctx {
+struct curl_tls_ctx {
#ifdef USE_OPENSSL
- SSL_CTX *ssl_ctx;
- SSL *ssl;
+ struct ossl_ctx ossl;
#elif defined(USE_GNUTLS)
- struct gtls_instance *gtls;
+ struct gtls_ctx gtls;
#elif defined(USE_WOLFSSL)
WOLFSSL_CTX *ssl_ctx;
WOLFSSL *ssl;
#endif
- BIT(x509_store_setup); /* if x509 store has been set up */
};
/**
* - openssl/wolfssl: SSL_CTX* has just been created
* - gnutls: gtls_client_init() has run
*/
-typedef CURLcode Curl_vquic_tls_ctx_setup(struct quic_tls_ctx *ctx,
- struct Curl_cfilter *cf,
- struct Curl_easy *data);
+typedef CURLcode Curl_vquic_tls_ctx_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ void *cb_user_data);
/**
* Initialize the QUIC TLS instances based of the SSL configurations
* @param alpn the ALPN string in protocol format ((len+bytes+)+),
* may be NULL
* @param alpn_len the overall number of bytes in `alpn`
- * @param ctx_setup optional callback for very early TLS config
- * @param user_data optional pointer to set in TLS application context
+ * @param cb_setup optional callback for very early TLS config
+ ± @param cb_user_data user_data param for callback
+ * @param ssl_user_data optional pointer to set in TLS application context
*/
-CURLcode Curl_vquic_tls_init(struct quic_tls_ctx *ctx,
+CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
const char *alpn, size_t alpn_len,
- Curl_vquic_tls_ctx_setup *ctx_setup,
- void *user_data);
+ Curl_vquic_tls_ctx_setup *cb_setup,
+ void *cb_user_data,
+ void *ssl_user_data);
/**
* Cleanup all data that has been initialized.
*/
-void Curl_vquic_tls_cleanup(struct quic_tls_ctx *ctx);
+void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx);
-CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx,
+CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx,
struct Curl_cfilter *cf,
struct Curl_easy *data);
* After the QUIC basic handshake has been, verify that the peer
* (and its certificate) fulfill our requirements.
*/
-CURLcode Curl_vquic_tls_verify_peer(struct quic_tls_ctx *ctx,
+CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer);
CURL_TRC_CF(data, cf, "connect_step1, check session cache");
Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &session, NULL)) {
+ if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &session, NULL)) {
br_ssl_engine_set_session_parameters(&backend->ctx.eng, session);
session_set = 1;
infof(data, "BearSSL: reusing session ID");
return CURLE_OUT_OF_MEMORY;
br_ssl_engine_get_session_parameters(&backend->ctx.eng, session);
Curl_ssl_sessionid_lock(data);
- incache = !(Curl_ssl_getsessionid(cf, data, &oldsession, NULL));
+ incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer,
+ &oldsession, NULL));
if(incache)
Curl_ssl_delsessionid(data, oldsession);
- ret = Curl_ssl_addsessionid(cf, data, session, 0, &added);
+ ret = Curl_ssl_addsessionid(cf, data, &connssl->peer, session, 0, &added);
Curl_ssl_sessionid_unlock(data);
if(!added)
free(session);
#include "urldata.h"
#include "sendf.h"
#include "inet_pton.h"
+#include "keylog.h"
#include "gtls.h"
#include "vtls.h"
#include "vtls_int.h"
/* The last #include file should be: */
#include "memdebug.h"
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+#define QUIC_PRIORITY \
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
+ "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
+ "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
+ "%DISABLE_TLS13_COMPAT_MODE"
+
/* Enable GnuTLS debugging by defining GTLSDEBUG */
/*#define GTLSDEBUG */
# include <gnutls/ocsp.h>
struct gtls_ssl_backend_data {
- struct gtls_instance gtls;
+ struct gtls_ctx gtls;
};
static ssize_t gtls_push(void *s, const void *buf, size_t blen)
static CURLcode
set_ssl_version_min_max(struct Curl_easy *data,
+ struct ssl_peer *peer,
struct ssl_primary_config *conn_config,
const char **prioritylist,
const char *tls13support)
long ssl_version = conn_config->version;
long ssl_version_max = conn_config->version_max;
+ if(peer->transport == TRNSPRT_QUIC) {
+ if((ssl_version != CURL_SSLVERSION_DEFAULT) &&
+ (ssl_version < CURL_SSLVERSION_TLSv1_3)) {
+ failf(data, "QUIC needs at least TLS version 1.3");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ *prioritylist = QUIC_PRIORITY;
+ return CURLE_OK;
+ }
+
if((ssl_version == CURL_SSLVERSION_DEFAULT) ||
(ssl_version == CURL_SSLVERSION_TLSv1))
ssl_version = CURL_SSLVERSION_TLSv1_0;
return CURLE_SSL_CONNECT_ERROR;
}
-CURLcode gtls_client_init(struct Curl_easy *data,
- struct ssl_primary_config *config,
- struct ssl_config_data *ssl_config,
- struct ssl_peer *peer,
- struct gtls_instance *gtls,
- long *pverifyresult)
+static CURLcode gtls_client_init(struct Curl_easy *data,
+ struct ssl_primary_config *config,
+ struct ssl_config_data *ssl_config,
+ struct ssl_peer *peer,
+ struct gtls_ctx *gtls,
+ long *pverifyresult)
{
unsigned int init_flags;
int rc;
}
/* At this point we know we have a supported TLS version, so set it */
- result = set_ssl_version_min_max(data, config, &prioritylist, tls13support);
+ result = set_ssl_version_min_max(data, peer,
+ config, &prioritylist, tls13support);
if(result)
return result;
return CURLE_OK;
}
-static CURLcode
-gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+static int keylog_callback(gnutls_session_t session, const char *label,
+ const gnutls_datum_t *secret)
+{
+ gnutls_datum_t crandom;
+ gnutls_datum_t srandom;
+
+ gnutls_session_get_random(session, &crandom, &srandom);
+ if(crandom.size != 32) {
+ return -1;
+ }
+
+ Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
+ return 0;
+}
+
+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,
+ Curl_gtls_ctx_setup_cb *cb_setup,
+ void *cb_user_data,
+ void *ssl_user_data)
{
- struct ssl_connect_data *connssl = cf->ctx;
- struct gtls_ssl_backend_data *backend =
- (struct gtls_ssl_backend_data *)connssl->backend;
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);
long * const pverifyresult = &ssl_config->certverifyresult;
CURLcode result;
- DEBUGASSERT(backend);
-
- if(connssl->state == ssl_connection_complete)
- /* to make us tolerant against being called more than once for the
- same connection */
- return CURLE_OK;
+ DEBUGASSERT(gctx);
- result = gtls_client_init(data, conn_config, ssl_config,
- &connssl->peer,
- &backend->gtls, pverifyresult);
+ result = gtls_client_init(data, conn_config, ssl_config, peer,
+ gctx, pverifyresult);
if(result)
return result;
- if(connssl->alpn) {
- struct alpn_proto_buf proto;
- gnutls_datum_t alpn[ALPN_ENTRIES_MAX];
- size_t i;
+ gnutls_session_set_ptr(gctx->session, ssl_user_data);
+
+ if(cb_setup) {
+ result = cb_setup(cf, data, cb_user_data);
+ if(result)
+ return result;
+ }
- for(i = 0; i < connssl->alpn->count; ++i) {
- alpn[i].data = (unsigned char *)connssl->alpn->entries[i];
- alpn[i].size = (unsigned)strlen(connssl->alpn->entries[i]);
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+ gnutls_session_set_keylog_function(gctx->session, keylog_callback);
+ }
+
+ /* convert the ALPN string from our arguments to a list of strings
+ * that gnutls wants and will convert internally back to this very
+ * string for sending to the server. nice. */
+ if(alpn) {
+ gnutls_datum_t alpns[5];
+ size_t i, alen = alpn_len;
+ unsigned char *s = (unsigned char *)alpn;
+ unsigned char slen;
+ for(i = 0; (i < ARRAYSIZE(alpns)) && alen; ++i) {
+ slen = s[0];
+ if(slen >= alen)
+ return CURLE_FAILED_INIT;
+ alpns[i].data = s + 1;
+ alpns[i].size = slen;
+ s += slen + 1;
+ alen -= (size_t)slen + 1;
}
- if(gnutls_alpn_set_protocols(backend->gtls.session, alpn,
- (unsigned)connssl->alpn->count, 0)) {
+ if(alen) /* not all alpn chars used, wrong format or too many */
+ return CURLE_FAILED_INIT;
+ if(i && gnutls_alpn_set_protocols(gctx->session,
+ alpns, (unsigned int)i,
+ GNUTLS_ALPN_MANDATORY)) {
failf(data, "failed setting ALPN");
return CURLE_SSL_CONNECT_ERROR;
}
- Curl_alpn_to_proto_str(&proto, connssl->alpn);
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
/* This might be a reconnect, so we check for a session ID in the cache
size_t ssl_idsize;
Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, &ssl_idsize)) {
+ if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, &ssl_idsize)) {
/* we got a session id, use it! */
- gnutls_session_set_data(backend->gtls.session,
- ssl_sessionid, ssl_idsize);
+ gnutls_session_set_data(gctx->session, ssl_sessionid, ssl_idsize);
/* Informational message */
infof(data, "SSL reusing session ID");
}
Curl_ssl_sessionid_unlock(data);
}
+ return CURLE_OK;
+}
+
+static CURLcode
+gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct gtls_ssl_backend_data *backend =
+ (struct gtls_ssl_backend_data *)connssl->backend;
+ struct alpn_proto_buf proto;
+ CURLcode result;
+
+ DEBUGASSERT(backend);
+ DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
+
+ if(connssl->state == ssl_connection_complete)
+ /* to make us tolerant against being called more than once for the
+ same connection */
+ return CURLE_OK;
+
+ memset(&proto, 0, sizeof(proto));
+ if(connssl->alpn) {
+ result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
+ if(result) {
+ failf(data, "Error determining ALPN");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ result = Curl_gtls_ctx_init(&backend->gtls, cf, data, &connssl->peer,
+ proto.data, proto.len, NULL, NULL, cf);
+ if(result)
+ return result;
+
/* register callback functions and handle to send and receive data. */
gnutls_transport_set_ptr(backend->gtls.session, cf);
gnutls_session_get_data(session, connect_sessionid, &connect_idsize);
Curl_ssl_sessionid_lock(data);
- incache = !(Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL));
+ incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer,
+ &ssl_sessionid, NULL));
if(incache) {
/* there was one before in the cache, so instead of risking that the
previous one was rejected, we just kill that and store the new */
}
/* store this session id */
- result = Curl_ssl_addsessionid(cf, data, connect_sessionid,
- connect_idsize, &added);
+ result = Curl_ssl_addsessionid(cf, data, &connssl->peer,
+ connect_sessionid, connect_idsize,
+ &added);
Curl_ssl_sessionid_unlock(data);
if(!added)
free(connect_sessionid);
struct ssl_config_data;
struct ssl_peer;
-struct gtls_instance {
+struct gtls_ctx {
gnutls_session_t session;
gnutls_certificate_credentials_t cred;
#ifdef USE_GNUTLS_SRP
#endif
};
-CURLcode
-gtls_client_init(struct Curl_easy *data,
- struct ssl_primary_config *config,
- struct ssl_config_data *ssl_config,
- struct ssl_peer *peer,
- struct gtls_instance *gtls,
- long *pverifyresult);
+typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ void *user_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,
+ Curl_gtls_ctx_setup_cb *cb_setup,
+ void *cb_user_data,
+ void *ssl_user_data);
CURLcode
Curl_gtls_verifyserver(struct Curl_easy *data,
#include "curl_setup.h"
#if defined(USE_OPENSSL) || \
+ defined(USE_GNUTLS) || \
defined(USE_WOLFSSL) || \
(defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \
defined(USE_QUICHE)
}
#endif
- infof(data, "mbedTLS: Connecting to %s:%d", hostname, connssl->port);
+ infof(data, "mbedTLS: Connecting to %s:%d", hostname, connssl->peer.port);
mbedtls_ssl_config_init(&backend->config);
ret = mbedtls_ssl_config_defaults(&backend->config,
void *old_session = NULL;
Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &old_session, NULL)) {
+ if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &old_session, NULL)) {
ret = mbedtls_ssl_set_session(&backend->ssl, old_session);
if(ret) {
Curl_ssl_sessionid_unlock(data);
/* If there's already a matching session in the cache, delete it */
Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL))
+ if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
+ &old_ssl_sessionid, NULL))
Curl_ssl_delsessionid(data, old_ssl_sessionid);
- retcode = Curl_ssl_addsessionid(cf, data, our_ssl_sessionid,
- 0, &added);
+ retcode = Curl_ssl_addsessionid(cf, data, &connssl->peer,
+ our_ssl_sessionid, 0, &added);
Curl_ssl_sessionid_unlock(data);
if(!added) {
mbedtls_ssl_session_free(our_ssl_sessionid);
#define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
#endif /* !LIBRESSL_VERSION_NUMBER */
-struct ossl_ssl_backend_data {
- /* these ones requires specific SSL-types */
- SSL_CTX* ctx;
- SSL* handle;
- X509* server_cert;
- BIO_METHOD *bio_method;
- CURLcode io_result; /* result of last BIO cfilter operation */
-#ifndef HAVE_KEYLOG_CALLBACK
- /* Set to true once a valid keylog entry has been created to avoid dupes. */
- bool keylog_done;
-#endif
- bool x509_store_setup; /* x509 store has been set up */
-};
-
#if defined(HAVE_SSL_X509_STORE_SHARE)
struct multi_ssl_backend_data {
char *CAfile; /* CAfile path used to generate X509 store */
{
struct Curl_cfilter *cf = BIO_get_data(bio);
struct ssl_connect_data *connssl = cf->ctx;
- struct ossl_ssl_backend_data *backend =
- (struct ossl_ssl_backend_data *)connssl->backend;
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nwritten;
CURLcode result = CURLE_SEND_ERROR;
CURL_TRC_CF(data, cf, "ossl_bio_cf_out_write(len=%d) -> %d, err=%d",
blen, (int)nwritten, result);
BIO_clear_retry_flags(bio);
- backend->io_result = result;
+ octx->io_result = result;
if(nwritten < 0) {
if(CURLE_AGAIN == result)
BIO_set_retry_write(bio);
{
struct Curl_cfilter *cf = BIO_get_data(bio);
struct ssl_connect_data *connssl = cf->ctx;
- struct ossl_ssl_backend_data *backend =
- (struct ossl_ssl_backend_data *)connssl->backend;
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nread;
CURLcode result = CURLE_RECV_ERROR;
CURL_TRC_CF(data, cf, "ossl_bio_cf_in_read(len=%d) -> %d, err=%d",
blen, (int)nread, result);
BIO_clear_retry_flags(bio);
- backend->io_result = result;
+ octx->io_result = result;
if(nread < 0) {
if(CURLE_AGAIN == result)
BIO_set_retry_read(bio);
/* Before returning server replies to the SSL instance, we need
* to have setup the x509 store or verification will fail. */
- if(!backend->x509_store_setup) {
- result = Curl_ssl_setup_x509_store(cf, data, backend->ctx);
+ if(!octx->x509_store_setup) {
+ result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx);
if(result) {
- backend->io_result = result;
+ octx->io_result = result;
return -1;
}
- backend->x509_store_setup = TRUE;
+ octx->x509_store_setup = TRUE;
}
return (int)nread;
static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
- struct ossl_ssl_backend_data *backend =
- (struct ossl_ssl_backend_data *)connssl->backend;
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
(void)data;
- DEBUGASSERT(backend);
+ DEBUGASSERT(octx);
- if(backend->handle) {
+ if(octx->ssl) {
/* Send the TLS shutdown if we are still connected *and* if
* the peer did not already close the connection. */
if(cf->next && cf->next->connected && !connssl->peer_closed) {
/* Maybe the server has already sent a close notify alert.
Read it to avoid an RST on the TCP connection. */
ERR_clear_error();
- nread = SSL_read(backend->handle, buf, (int)sizeof(buf));
- err = SSL_get_error(backend->handle, nread);
+ nread = SSL_read(octx->ssl, buf, (int)sizeof(buf));
+ err = SSL_get_error(octx->ssl, nread);
if(!nread && err == SSL_ERROR_ZERO_RETURN) {
CURLcode result;
ssize_t n;
CURL_TRC_CF(data, cf, "not from sending TLS shutdown on "
"connection closed by peer");
}
- else if(SSL_shutdown(backend->handle) == 1) {
+ else if(SSL_shutdown(octx->ssl) == 1) {
CURL_TRC_CF(data, cf, "SSL shutdown finished");
}
else {
- nread = SSL_read(backend->handle, buf, (int)sizeof(buf));
- err = SSL_get_error(backend->handle, nread);
+ nread = SSL_read(octx->ssl, buf, (int)sizeof(buf));
+ err = SSL_get_error(octx->ssl, nread);
switch(err) {
case SSL_ERROR_NONE: /* this is not an error */
case SSL_ERROR_ZERO_RETURN: /* no more data */
}
ERR_clear_error();
- SSL_set_connect_state(backend->handle);
+ SSL_set_connect_state(octx->ssl);
}
- SSL_free(backend->handle);
- backend->handle = NULL;
+ SSL_free(octx->ssl);
+ octx->ssl = NULL;
}
- if(backend->ctx) {
- SSL_CTX_free(backend->ctx);
- backend->ctx = NULL;
- backend->x509_store_setup = FALSE;
+ if(octx->ssl_ctx) {
+ SSL_CTX_free(octx->ssl_ctx);
+ octx->ssl_ctx = NULL;
+ octx->x509_store_setup = FALSE;
}
- if(backend->bio_method) {
- ossl_bio_cf_method_free(backend->bio_method);
- backend->bio_method = NULL;
+ if(octx->bio_method) {
+ ossl_bio_cf_method_free(octx->bio_method);
+ octx->bio_method = NULL;
}
}
int buffsize;
int err;
bool done = FALSE;
- struct ossl_ssl_backend_data *backend =
- (struct ossl_ssl_backend_data *)connssl->backend;
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
int loop = 10;
- DEBUGASSERT(backend);
+ DEBUGASSERT(octx);
#ifndef CURL_DISABLE_FTP
/* This has only been tested on the proftpd server, and the mod_tls code
we do not send one. Let's hope other servers do the same... */
if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
- (void)SSL_shutdown(backend->handle);
+ (void)SSL_shutdown(octx->ssl);
#endif
- if(backend->handle) {
+ if(octx->ssl) {
buffsize = (int)sizeof(buf);
while(!done && loop--) {
int what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data),
/* Something to read, let's do it and hope that it is the close
notify alert from the server */
- nread = SSL_read(backend->handle, buf, buffsize);
- err = SSL_get_error(backend->handle, nread);
+ nread = SSL_read(octx->ssl, buf, buffsize);
+ err = SSL_get_error(octx->ssl, nread);
switch(err) {
case SSL_ERROR_NONE: /* this is not an error */
if(data->set.verbose) {
#ifdef HAVE_SSL_GET_SHUTDOWN
- switch(SSL_get_shutdown(backend->handle)) {
+ switch(SSL_get_shutdown(octx->ssl)) {
case SSL_SENT_SHUTDOWN:
infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN");
break;
#endif
}
- SSL_free(backend->handle);
- backend->handle = NULL;
+ SSL_free(octx->ssl);
+ octx->ssl = NULL;
}
return retval;
}
OCSP_BASICRESP *br = NULL;
X509_STORE *st = NULL;
STACK_OF(X509) *ch = NULL;
- struct ossl_ssl_backend_data *backend =
- (struct ossl_ssl_backend_data *)connssl->backend;
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
X509 *cert;
OCSP_CERTID *id = NULL;
int cert_status, crl_reason;
int ret;
long len;
- DEBUGASSERT(backend);
+ DEBUGASSERT(octx);
- len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status);
+ len = SSL_get_tlsext_status_ocsp_resp(octx->ssl, &status);
if(!status) {
failf(data, "No OCSP response received");
goto end;
}
- ch = SSL_get_peer_cert_chain(backend->handle);
+ ch = SSL_get_peer_cert_chain(octx->ssl);
if(!ch) {
failf(data, "Could not get peer certificate chain");
result = CURLE_SSL_INVALIDCERTSTATUS;
goto end;
}
- st = SSL_CTX_get_cert_store(backend->ctx);
+ st = SSL_CTX_get_cert_store(octx->ssl_ctx);
#if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \
(defined(LIBRESSL_VERSION_NUMBER) && \
}
/* Compute the certificate's ID */
- cert = SSL_get1_peer_certificate(backend->handle);
+ cert = SSL_get1_peer_certificate(octx->ssl);
if(!cert) {
failf(data, "Error getting peer certificate");
result = CURLE_SSL_INVALIDCERTSTATUS;
#ifdef TLS1_3_VERSION
{
struct ssl_connect_data *connssl = cf->ctx;
- struct ossl_ssl_backend_data *backend =
- (struct ossl_ssl_backend_data *)connssl->backend;
- DEBUGASSERT(backend);
- SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION);
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
+ DEBUGASSERT(octx);
+ SSL_CTX_set_max_proto_version(octx->ssl_ctx, TLS1_3_VERSION);
*ctx_options |= SSL_OP_NO_TLSv1_2;
}
#else
}
#endif
-/* The "new session" callback must return zero if the session can be removed
- * or non-zero if the session has been put into the session cache.
- */
-static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
+CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const struct ssl_peer *peer,
+ SSL_SESSION *ssl_sessionid)
{
- int res = 0;
- struct Curl_easy *data;
- struct Curl_cfilter *cf;
const struct ssl_config_data *config;
- struct ssl_connect_data *connssl;
bool isproxy;
+ CURLcode result = CURLE_WRITE_ERROR;
- cf = (struct Curl_cfilter*) SSL_get_app_data(ssl);
- connssl = cf? cf->ctx : NULL;
- data = connssl? CF_DATA_CURRENT(cf) : NULL;
- /* The sockindex has been stored as a pointer to an array element */
if(!cf || !data)
- return 0;
+ return result;
isproxy = Curl_ssl_cf_is_proxy(cf);
if(isproxy)
incache = FALSE;
else
- incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL));
+ incache = !(Curl_ssl_getsessionid(cf, data, peer,
+ &old_ssl_sessionid, NULL));
if(incache) {
if(old_ssl_sessionid != ssl_sessionid) {
infof(data, "old SSL session ID is stale, removing");
}
if(!incache) {
- if(!Curl_ssl_addsessionid(cf, data, ssl_sessionid,
+ if(!Curl_ssl_addsessionid(cf, data, peer, ssl_sessionid,
0 /* unknown size */, &added)) {
if(added) {
/* the session has been put into the session cache */
- res = 1;
+ result = CURLE_OK;
}
}
else
Curl_ssl_sessionid_unlock(data);
}
- return res;
+ return result;
+}
+
+/* The "new session" callback must return zero if the session can be removed
+ * or non-zero if the session has been put into the session cache.
+ */
+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;
+ CURLcode result;
+
+ cf = (struct Curl_cfilter*) SSL_get_app_data(ssl);
+ connssl = cf? cf->ctx : NULL;
+ data = connssl? CF_DATA_CURRENT(cf) : NULL;
+ result = Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid);
+ return result? 0 : 1;
}
static CURLcode load_cacert_from_memory(X509_STORE *store,
}
#endif /* HAVE_SSL_X509_STORE_SHARE */
-static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
- struct Curl_easy *data)
+CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer,
+ int transport, /* TCP or QUIC */
+ const unsigned char *alpn, size_t alpn_len,
+ Curl_ossl_ctx_setup_cb *cb_setup,
+ void *cb_user_data,
+ Curl_ossl_new_session_cb *cb_new_session,
+ void *ssl_user_data)
{
CURLcode result = CURLE_OK;
- char *ciphers;
+ const char *ciphers;
SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
- struct ssl_connect_data *connssl = cf->ctx;
ctx_option_t ctx_options = 0;
void *ssl_sessionid = NULL;
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);
- BIO *bio;
const long int ssl_version = conn_config->version;
char * const ssl_cert = ssl_config->primary.clientcert;
const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
const char * const ssl_cert_type = ssl_config->cert_type;
const bool verifypeer = conn_config->verifypeer;
char error_buffer[256];
- struct ossl_ssl_backend_data *backend =
- (struct ossl_ssl_backend_data *)connssl->backend;
-
- DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
- DEBUGASSERT(backend);
/* Make funny stuff to get random input */
result = ossl_seed(data);
ssl_config->certverifyresult = !X509_V_OK;
- /* check to see if we've been told to use an explicit SSL/TLS version */
-
- switch(ssl_version) {
- case CURL_SSLVERSION_DEFAULT:
- case CURL_SSLVERSION_TLSv1:
- case CURL_SSLVERSION_TLSv1_0:
- case CURL_SSLVERSION_TLSv1_1:
- case CURL_SSLVERSION_TLSv1_2:
- case CURL_SSLVERSION_TLSv1_3:
- /* it will be handled later with the context options */
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
- req_method = TLS_client_method();
+ switch(transport) {
+ case TRNSPRT_TCP:
+ /* check to see if we've been told to use an explicit SSL/TLS version */
+ switch(ssl_version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ case CURL_SSLVERSION_TLSv1_0:
+ case CURL_SSLVERSION_TLSv1_1:
+ case CURL_SSLVERSION_TLSv1_2:
+ case CURL_SSLVERSION_TLSv1_3:
+ /* it will be handled later with the context options */
+ #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+ req_method = TLS_client_method();
+ #else
+ req_method = SSLv23_client_method();
+ #endif
+ break;
+ case CURL_SSLVERSION_SSLv2:
+ failf(data, "No SSLv2 support");
+ return CURLE_NOT_BUILT_IN;
+ case CURL_SSLVERSION_SSLv3:
+ failf(data, "No SSLv3 support");
+ return CURLE_NOT_BUILT_IN;
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ break;
+ case TRNSPRT_QUIC:
+ if((ssl_version != CURL_SSLVERSION_DEFAULT) &&
+ (ssl_version < CURL_SSLVERSION_TLSv1_3)) {
+ failf(data, "QUIC needs at least TLS version 1.3");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+#ifdef USE_OPENSSL_QUIC
+ req_method = OSSL_QUIC_client_method();
#else
- req_method = SSLv23_client_method();
+ req_method = TLS_method();
#endif
break;
- case CURL_SSLVERSION_SSLv2:
- failf(data, "No SSLv2 support");
- return CURLE_NOT_BUILT_IN;
- case CURL_SSLVERSION_SSLv3:
- failf(data, "No SSLv3 support");
- return CURLE_NOT_BUILT_IN;
default:
- failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ failf(data, "unsupported transport %d in SSL init", transport);
return CURLE_SSL_CONNECT_ERROR;
}
- if(backend->ctx) {
- /* This happens when an error was encountered before in this
- * step and we are called to do it again. Get rid of any leftover
- * from the previous call. */
- ossl_close(cf, data);
- }
- backend->ctx = SSL_CTX_new(req_method);
- if(!backend->ctx) {
+ DEBUGASSERT(!octx->ssl_ctx);
+ octx->ssl_ctx = SSL_CTX_new(req_method);
+
+ if(!octx->ssl_ctx) {
failf(data, "SSL: couldn't create a context: %s",
ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer)));
return CURLE_OUT_OF_MEMORY;
}
+ if(cb_setup) {
+ result = cb_setup(cf, data, cb_user_data);
+ if(result)
+ return result;
+ }
+
#ifdef SSL_CTRL_SET_MSG_CALLBACK
if(data->set.fdebug && data->set.verbose) {
/* the SSL trace callback is only used for verbose logging */
- SSL_CTX_set_msg_callback(backend->ctx, ossl_trace);
- SSL_CTX_set_msg_callback_arg(backend->ctx, cf);
+ SSL_CTX_set_msg_callback(octx->ssl_ctx, ossl_trace);
+ SSL_CTX_set_msg_callback_arg(octx->ssl_ctx, cf);
}
#endif
ctx_options |= SSL_OP_NO_SSLv3;
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */
- result = ossl_set_ssl_version_min_max(cf, backend->ctx);
+ result = ossl_set_ssl_version_min_max(cf, octx->ssl_ctx);
#else
result = ossl_set_ssl_version_min_max_legacy(&ctx_options, cf, data);
#endif
return CURLE_SSL_CONNECT_ERROR;
}
- SSL_CTX_set_options(backend->ctx, ctx_options);
+ SSL_CTX_set_options(octx->ssl_ctx, ctx_options);
#ifdef HAS_ALPN
- if(connssl->alpn) {
- struct alpn_proto_buf proto;
-
- result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
- if(result ||
- SSL_CTX_set_alpn_protos(backend->ctx, proto.data, proto.len)) {
+ if(alpn) {
+ if(SSL_CTX_set_alpn_protos(octx->ssl_ctx, alpn, (int)alpn_len)) {
failf(data, "Error setting ALPN");
return CURLE_SSL_CONNECT_ERROR;
}
- Curl_alpn_to_proto_str(&proto, connssl->alpn);
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
#endif
if(ssl_cert || ssl_cert_blob || ssl_cert_type) {
if(!result &&
- !cert_stuff(data, backend->ctx,
+ !cert_stuff(data, octx->ssl_ctx,
ssl_cert, ssl_cert_blob, ssl_cert_type,
ssl_config->key, ssl_config->key_blob,
ssl_config->key_type, ssl_config->key_passwd))
}
ciphers = conn_config->cipher_list;
- if(!ciphers)
- ciphers = (char *)DEFAULT_CIPHER_SELECTION;
+ if(!ciphers && (peer->transport != TRNSPRT_QUIC))
+ ciphers = DEFAULT_CIPHER_SELECTION;
if(ciphers) {
- if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) {
+ if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, ciphers)) {
failf(data, "failed setting cipher list: %s", ciphers);
return CURLE_SSL_CIPHER;
}
#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
{
- char *ciphers13 = conn_config->cipher_list13;
+ const char *ciphers13 = conn_config->cipher_list13;
if(ciphers13) {
- if(!SSL_CTX_set_ciphersuites(backend->ctx, ciphers13)) {
+ if(!SSL_CTX_set_ciphersuites(octx->ssl_ctx, ciphers13)) {
failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13);
return CURLE_SSL_CIPHER;
}
#ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
/* OpenSSL 1.1.1 requires clients to opt-in for PHA */
- SSL_CTX_set_post_handshake_auth(backend->ctx, 1);
+ SSL_CTX_set_post_handshake_auth(octx->ssl_ctx, 1);
#endif
#ifdef HAVE_SSL_CTX_SET_EC_CURVES
{
- char *curves = conn_config->curves;
+ const char *curves = conn_config->curves;
if(curves) {
- if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) {
+ if(!SSL_CTX_set1_curves_list(octx->ssl_ctx, curves)) {
failf(data, "failed setting curves list: '%s'", curves);
return CURLE_SSL_CIPHER;
}
char * const ssl_password = ssl_config->primary.password;
infof(data, "Using TLS-SRP username: %s", ssl_username);
- if(!SSL_CTX_set_srp_username(backend->ctx, ssl_username)) {
+ if(!SSL_CTX_set_srp_username(octx->ssl_ctx, ssl_username)) {
failf(data, "Unable to set SRP user name");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
- if(!SSL_CTX_set_srp_password(backend->ctx, ssl_password)) {
+ if(!SSL_CTX_set_srp_password(octx->ssl_ctx, ssl_password)) {
failf(data, "failed setting SRP password");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
if(!conn_config->cipher_list) {
infof(data, "Setting cipher list SRP");
- if(!SSL_CTX_set_cipher_list(backend->ctx, "SRP")) {
+ if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, "SRP")) {
failf(data, "failed setting SRP cipher list");
return CURLE_SSL_CIPHER;
}
* fail to connect if the verification fails, or if it should continue
* anyway. In the latter case the result of the verification is checked with
* SSL_get_verify_result() below. */
- SSL_CTX_set_verify(backend->ctx,
+ SSL_CTX_set_verify(octx->ssl_ctx,
verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
/* Enable logging of secrets to the file specified in env SSLKEYLOGFILE. */
#ifdef HAVE_KEYLOG_CALLBACK
if(Curl_tls_keylog_enabled()) {
- SSL_CTX_set_keylog_callback(backend->ctx, ossl_keylog_callback);
+ SSL_CTX_set_keylog_callback(octx->ssl_ctx, ossl_keylog_callback);
}
#endif
- /* Enable the session cache because it's a prerequisite for the "new session"
- * callback. Use the "external storage" mode to prevent OpenSSL from creating
- * an internal session cache.
- */
- SSL_CTX_set_session_cache_mode(backend->ctx,
- SSL_SESS_CACHE_CLIENT |
- SSL_SESS_CACHE_NO_INTERNAL);
- SSL_CTX_sess_set_new_cb(backend->ctx, ossl_new_session_cb);
+ if(cb_new_session) {
+ /* Enable the session cache because it's a prerequisite for the
+ * "new session" callback. Use the "external storage" mode to prevent
+ * OpenSSL from creating an internal session cache.
+ */
+ SSL_CTX_set_session_cache_mode(octx->ssl_ctx,
+ SSL_SESS_CACHE_CLIENT |
+ SSL_SESS_CACHE_NO_INTERNAL);
+ SSL_CTX_sess_set_new_cb(octx->ssl_ctx, cb_new_session);
+ }
/* give application a chance to interfere with SSL set up. */
if(data->set.ssl.fsslctx) {
/* When a user callback is installed to modify the SSL_CTX,
* we need to do the full initialization before calling it.
* See: #11800 */
- if(!backend->x509_store_setup) {
- result = Curl_ssl_setup_x509_store(cf, data, backend->ctx);
+ if(!octx->x509_store_setup) {
+ result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx);
if(result)
return result;
- backend->x509_store_setup = TRUE;
+ octx->x509_store_setup = TRUE;
}
Curl_set_in_callback(data, true);
- result = (*data->set.ssl.fsslctx)(data, backend->ctx,
+ result = (*data->set.ssl.fsslctx)(data, octx->ssl_ctx,
data->set.ssl.fsslctxp);
Curl_set_in_callback(data, false);
if(result) {
}
/* Let's make an SSL structure */
- if(backend->handle)
- SSL_free(backend->handle);
- backend->handle = SSL_new(backend->ctx);
- if(!backend->handle) {
+ if(octx->ssl)
+ SSL_free(octx->ssl);
+ octx->ssl = SSL_new(octx->ssl_ctx);
+ if(!octx->ssl) {
failf(data, "SSL: couldn't create a context (handle)");
return CURLE_OUT_OF_MEMORY;
}
- SSL_set_app_data(backend->handle, cf);
+ SSL_set_app_data(octx->ssl, ssl_user_data);
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
!defined(OPENSSL_NO_OCSP)
if(conn_config->verifystatus)
- SSL_set_tlsext_status_type(backend->handle, TLSEXT_STATUSTYPE_ocsp);
+ SSL_set_tlsext_status_type(octx->ssl, TLSEXT_STATUSTYPE_ocsp);
#endif
#if (defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)) && \
defined(ALLOW_RENEG)
- SSL_set_renegotiate_mode(backend->handle, ssl_renegotiate_freely);
+ SSL_set_renegotiate_mode(octx->ssl, ssl_renegotiate_freely);
#endif
- SSL_set_connect_state(backend->handle);
+ SSL_set_connect_state(octx->ssl);
- backend->server_cert = 0x0;
+ octx->server_cert = 0x0;
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
- if(connssl->peer.sni) {
- if(!SSL_set_tlsext_host_name(backend->handle, connssl->peer.sni)) {
+ if(peer->sni) {
+ if(!SSL_set_tlsext_host_name(octx->ssl, peer->sni)) {
failf(data, "Failed set SNI");
return CURLE_SSL_CONNECT_ERROR;
}
}
#endif
- SSL_set_app_data(backend->handle, cf);
-
- connssl->reused_session = FALSE;
- if(ssl_config->primary.sessionid) {
+ octx->reused_session = FALSE;
+ if(ssl_config->primary.sessionid && transport == TRNSPRT_TCP) {
Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) {
+ if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, NULL)) {
/* we got a session id, use it! */
- if(!SSL_set_session(backend->handle, ssl_sessionid)) {
+ if(!SSL_set_session(octx->ssl, ssl_sessionid)) {
Curl_ssl_sessionid_unlock(data);
failf(data, "SSL: SSL_set_session failed: %s",
ossl_strerror(ERR_get_error(), error_buffer,
}
/* Informational message */
infof(data, "SSL reusing session ID");
- connssl->reused_session = TRUE;
+ octx->reused_session = TRUE;
}
Curl_ssl_sessionid_unlock(data);
}
- backend->bio_method = ossl_bio_cf_method_create();
- if(!backend->bio_method)
+ return CURLE_OK;
+}
+
+static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
+ struct alpn_proto_buf proto;
+ BIO *bio;
+ CURLcode result;
+
+ DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
+ DEBUGASSERT(octx);
+ memset(&proto, 0, sizeof(proto));
+#ifdef HAS_ALPN
+ if(connssl->alpn) {
+ result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
+ if(result) {
+ failf(data, "Error determining ALPN");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#endif
+
+ result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, TRNSPRT_TCP,
+ proto.data, proto.len, NULL, NULL,
+ ossl_new_session_cb, cf);
+ if(result)
+ return result;
+
+ octx->bio_method = ossl_bio_cf_method_create();
+ if(!octx->bio_method)
return CURLE_OUT_OF_MEMORY;
- bio = BIO_new(backend->bio_method);
+ bio = BIO_new(octx->bio_method);
if(!bio)
return CURLE_OUT_OF_MEMORY;
* We check on the function in configure, since libressl and friends
* each have their own versions to add support for this. */
BIO_up_ref(bio);
- SSL_set0_rbio(backend->handle, bio);
- SSL_set0_wbio(backend->handle, bio);
+ SSL_set0_rbio(octx->ssl, bio);
+ SSL_set0_wbio(octx->ssl, bio);
#else
- SSL_set_bio(backend->handle, bio, bio);
+ SSL_set_bio(octx->ssl, bio, bio);
#endif
- connssl->connecting_state = ssl_connect_2;
+ if(connssl->alpn) {
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+ }
+ connssl->connecting_state = ssl_connect_2;
return CURLE_OK;
}
{
int err;
struct ssl_connect_data *connssl = cf->ctx;
- struct ossl_ssl_backend_data *backend =
- (struct ossl_ssl_backend_data *)connssl->backend;
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
|| ssl_connect_2_reading == connssl->connecting_state
|| ssl_connect_2_writing == connssl->connecting_state);
- DEBUGASSERT(backend);
+ DEBUGASSERT(octx);
ERR_clear_error();
- err = SSL_connect(backend->handle);
+ err = SSL_connect(octx->ssl);
- if(!backend->x509_store_setup) {
+ if(!octx->x509_store_setup) {
/* After having send off the ClientHello, we prepare the x509
* store to verify the coming certificate from the server */
- CURLcode result = Curl_ssl_setup_x509_store(cf, data, backend->ctx);
+ CURLcode result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx);
if(result)
return result;
- backend->x509_store_setup = TRUE;
+ octx->x509_store_setup = TRUE;
}
#ifndef HAVE_KEYLOG_CALLBACK
/* If key logging is enabled, wait for the handshake to complete and then
* proceed with logging secrets (for TLS 1.2 or older).
*/
- ossl_log_tls12_secret(backend->handle, &backend->keylog_done);
+ ossl_log_tls12_secret(octx->ssl, &octx->keylog_done);
}
#endif
0 is "not successful but was shut down controlled"
<0 is "handshake was not successful, because a fatal error occurred" */
if(1 != err) {
- int detail = SSL_get_error(backend->handle, err);
+ int detail = SSL_get_error(octx->ssl, err);
if(SSL_ERROR_WANT_READ == detail) {
connssl->connecting_state = ssl_connect_2_reading;
return CURLE_OK;
}
#endif
- if(backend->io_result == CURLE_AGAIN) {
+ if(octx->io_result == CURLE_AGAIN) {
return CURLE_OK;
}
else {
(reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) {
result = CURLE_PEER_FAILED_VERIFICATION;
- lerr = SSL_get_verify_result(backend->handle);
+ lerr = SSL_get_verify_result(octx->ssl);
if(lerr != X509_V_OK) {
ssl_config->certverifyresult = lerr;
msnprintf(error_buffer, sizeof(error_buffer),
Curl_strerror(sockerr, extramsg, sizeof(extramsg));
failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ",
extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
- connssl->peer.hostname, connssl->port);
+ connssl->peer.hostname, connssl->peer.port);
return result;
}
connssl->connecting_state = ssl_connect_3;
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
- SSL_get_peer_signature_type_nid(backend->handle, &psigtype_nid);
+ SSL_get_peer_signature_type_nid(octx->ssl, &psigtype_nid);
#if (OPENSSL_VERSION_NUMBER >= 0x30200000L)
- negotiated_group_name = SSL_get0_group_name(backend->handle);
+ negotiated_group_name = SSL_get0_group_name(octx->ssl);
#else
negotiated_group_name =
- OBJ_nid2sn(SSL_get_negotiated_group(backend->handle) & 0x0000FFFF);
+ OBJ_nid2sn(SSL_get_negotiated_group(octx->ssl) & 0x0000FFFF);
#endif
#endif
/* Informational message */
infof(data, "SSL connection using %s / %s / %s / %s",
- SSL_get_version(backend->handle),
- SSL_get_cipher(backend->handle),
+ SSL_get_version(octx->ssl),
+ SSL_get_cipher(octx->ssl),
negotiated_group_name? negotiated_group_name : "[blank]",
OBJ_nid2sn(psigtype_nid));
if(connssl->alpn) {
const unsigned char *neg_protocol;
unsigned int len;
- SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len);
+ SSL_get0_alpn_selected(octx->ssl, &neg_protocol, &len);
return Curl_alpn_set_negotiated(cf, data, neg_protocol, len);
}
#define infof_certstack(data, ssl)
#endif
-/*
- * Get the server cert, verify it and show it, etc., only call failf() if the
- * 'strict' argument is TRUE as otherwise all this is for informational
- * purposes only!
- *
- * We check certificates to authenticate the server; otherwise we risk
- * man-in-the-middle attack.
- */
-static CURLcode servercert(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool strict)
+CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ossl_ctx *octx,
+ struct ssl_peer *peer)
{
struct connectdata *conn = cf->conn;
- struct ssl_connect_data *connssl = cf->ctx;
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);
CURLcode result = CURLE_OK;
char buffer[2048];
const char *ptr;
BIO *mem = BIO_new(BIO_s_mem());
- struct ossl_ssl_backend_data *backend =
- (struct ossl_ssl_backend_data *)connssl->backend;
+ bool strict = (conn_config->verifypeer || conn_config->verifyhost);
- DEBUGASSERT(backend);
+ DEBUGASSERT(octx);
if(!mem) {
failf(data,
if(data->set.ssl.certinfo)
/* asked to gather certificate info */
- (void)Curl_ossl_certchain(data, backend->handle);
+ (void)Curl_ossl_certchain(data, octx->ssl);
- backend->server_cert = SSL_get1_peer_certificate(backend->handle);
- if(!backend->server_cert) {
+ octx->server_cert = SSL_get1_peer_certificate(octx->ssl);
+ if(!octx->server_cert) {
BIO_free(mem);
if(!strict)
return CURLE_OK;
infof(data, "%s certificate:",
Curl_ssl_cf_is_proxy(cf)? "Proxy" : "Server");
- rc = x509_name_oneline(X509_get_subject_name(backend->server_cert),
+ rc = x509_name_oneline(X509_get_subject_name(octx->server_cert),
buffer, sizeof(buffer));
infof(data, " subject: %s", rc?"[NONE]":buffer);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
{
long len;
- ASN1_TIME_print(mem, X509_get0_notBefore(backend->server_cert));
+ ASN1_TIME_print(mem, X509_get0_notBefore(octx->server_cert));
len = BIO_get_mem_data(mem, (char **) &ptr);
infof(data, " start date: %.*s", (int)len, ptr);
(void)BIO_reset(mem);
- ASN1_TIME_print(mem, X509_get0_notAfter(backend->server_cert));
+ ASN1_TIME_print(mem, X509_get0_notAfter(octx->server_cert));
len = BIO_get_mem_data(mem, (char **) &ptr);
infof(data, " expire date: %.*s", (int)len, ptr);
(void)BIO_reset(mem);
BIO_free(mem);
if(conn_config->verifyhost) {
- result = Curl_ossl_verifyhost(data, conn, &connssl->peer,
- backend->server_cert);
+ result = Curl_ossl_verifyhost(data, conn, peer, octx->server_cert);
if(result) {
- X509_free(backend->server_cert);
- backend->server_cert = NULL;
+ X509_free(octx->server_cert);
+ octx->server_cert = NULL;
return result;
}
}
- rc = x509_name_oneline(X509_get_issuer_name(backend->server_cert),
+ rc = x509_name_oneline(X509_get_issuer_name(octx->server_cert),
buffer, sizeof(buffer));
if(rc) {
if(strict)
" error %s",
ossl_strerror(ERR_get_error(), error_buffer,
sizeof(error_buffer)) );
- X509_free(backend->server_cert);
- backend->server_cert = NULL;
+ X509_free(octx->server_cert);
+ octx->server_cert = NULL;
return CURLE_OUT_OF_MEMORY;
}
}
" error %s",
ossl_strerror(ERR_get_error(), error_buffer,
sizeof(error_buffer)) );
- X509_free(backend->server_cert);
- backend->server_cert = NULL;
+ X509_free(octx->server_cert);
+ octx->server_cert = NULL;
return CURLE_OUT_OF_MEMORY;
}
failf(data, "SSL: Unable to open issuer cert (%s)",
conn_config->issuercert);
BIO_free(fp);
- X509_free(backend->server_cert);
- backend->server_cert = NULL;
+ X509_free(octx->server_cert);
+ octx->server_cert = NULL;
return CURLE_SSL_ISSUER_ERROR;
}
}
conn_config->issuercert);
BIO_free(fp);
X509_free(issuer);
- X509_free(backend->server_cert);
- backend->server_cert = NULL;
+ X509_free(octx->server_cert);
+ octx->server_cert = NULL;
return CURLE_SSL_ISSUER_ERROR;
}
- if(X509_check_issued(issuer, backend->server_cert) != X509_V_OK) {
+ if(X509_check_issued(issuer, octx->server_cert) != X509_V_OK) {
if(strict)
failf(data, "SSL: Certificate issuer check failed (%s)",
conn_config->issuercert);
BIO_free(fp);
X509_free(issuer);
- X509_free(backend->server_cert);
- backend->server_cert = NULL;
+ X509_free(octx->server_cert);
+ octx->server_cert = NULL;
return CURLE_SSL_ISSUER_ERROR;
}
X509_free(issuer);
}
- lerr = SSL_get_verify_result(backend->handle);
+ lerr = SSL_get_verify_result(octx->ssl);
ssl_config->certverifyresult = lerr;
if(lerr != X509_V_OK) {
if(conn_config->verifypeer) {
infof(data, " SSL certificate verify ok.");
}
- infof_certstack(data, backend->handle);
+ infof_certstack(data, octx->ssl);
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
!defined(OPENSSL_NO_OCSP)
- if(conn_config->verifystatus && !connssl->reused_session) {
+ if(conn_config->verifystatus && !octx->reused_session) {
/* don't do this after Session ID reuse */
result = verifystatus(cf, data);
if(result) {
void *old_ssl_sessionid = NULL;
bool incache;
Curl_ssl_sessionid_lock(data);
- incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL));
+ incache = !(Curl_ssl_getsessionid(cf, data, peer,
+ &old_ssl_sessionid, 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(backend->server_cert);
- backend->server_cert = NULL;
+ X509_free(octx->server_cert);
+ octx->server_cert = NULL;
return result;
}
}
ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
#endif
if(!result && ptr) {
- result = ossl_pkp_pin_peer_pubkey(data, backend->server_cert, ptr);
+ result = ossl_pkp_pin_peer_pubkey(data, octx->server_cert, ptr);
if(result)
failf(data, "SSL: public key does not match pinned public key");
}
- X509_free(backend->server_cert);
- backend->server_cert = NULL;
- connssl->connecting_state = ssl_connect_done;
+ X509_free(octx->server_cert);
+ octx->server_cert = NULL;
return result;
}
{
CURLcode result = CURLE_OK;
struct ssl_connect_data *connssl = cf->ctx;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
* operations.
*/
- result = servercert(cf, data, conn_config->verifypeer ||
- conn_config->verifyhost);
-
+ result = Curl_oss_check_peer_cert(cf, data, octx, &connssl->peer);
if(!result)
connssl->connecting_state = ssl_connect_done;
const struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
- struct ossl_ssl_backend_data *backend =
- (struct ossl_ssl_backend_data *)connssl->backend;
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
(void)data;
- DEBUGASSERT(connssl && backend);
- if(backend->handle && SSL_pending(backend->handle))
+ DEBUGASSERT(connssl && octx);
+ if(octx->ssl && SSL_pending(octx->ssl))
return TRUE;
return FALSE;
}
int memlen;
int rc;
struct ssl_connect_data *connssl = cf->ctx;
- struct ossl_ssl_backend_data *backend =
- (struct ossl_ssl_backend_data *)connssl->backend;
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
(void)data;
- DEBUGASSERT(backend);
+ DEBUGASSERT(octx);
ERR_clear_error();
memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
- rc = SSL_write(backend->handle, mem, memlen);
+ rc = SSL_write(octx->ssl, mem, memlen);
if(rc <= 0) {
- err = SSL_get_error(backend->handle, rc);
+ err = SSL_get_error(octx->ssl, rc);
switch(err) {
case SSL_ERROR_WANT_READ:
{
int sockerr = SOCKERRNO;
- if(backend->io_result == CURLE_AGAIN) {
+ if(octx->io_result == CURLE_AGAIN) {
*curlcode = CURLE_AGAIN;
rc = -1;
goto out;
int buffsize;
struct connectdata *conn = cf->conn;
struct ssl_connect_data *connssl = cf->ctx;
- struct ossl_ssl_backend_data *backend =
- (struct ossl_ssl_backend_data *)connssl->backend;
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
(void)data;
- DEBUGASSERT(backend);
+ DEBUGASSERT(octx);
ERR_clear_error();
buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
- nread = (ssize_t)SSL_read(backend->handle, buf, buffsize);
+ nread = (ssize_t)SSL_read(octx->ssl, buf, buffsize);
if(nread <= 0) {
/* failed SSL_read */
- int err = SSL_get_error(backend->handle, (int)nread);
+ int err = SSL_get_error(octx->ssl, (int)nread);
switch(err) {
case SSL_ERROR_NONE: /* this is not an error */
/* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return
value/errno" */
/* https://www.openssl.org/docs/crypto/ERR_get_error.html */
- if(backend->io_result == CURLE_AGAIN) {
+ if(octx->io_result == CURLE_AGAIN) {
*curlcode = CURLE_AGAIN;
nread = -1;
goto out;
CURLINFO info)
{
/* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */
- struct ossl_ssl_backend_data *backend =
- (struct ossl_ssl_backend_data *)connssl->backend;
- DEBUGASSERT(backend);
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
+ DEBUGASSERT(octx);
return info == CURLINFO_TLS_SESSION ?
- (void *)backend->ctx : (void *)backend->handle;
+ (void *)octx->ssl_ctx : (void *)octx->ssl;
}
static void ossl_free_multi_ssl_backend_data(
#endif
SSLSUPP_HTTPS_PROXY,
- sizeof(struct ossl_ssl_backend_data),
+ sizeof(struct ossl_ctx),
ossl_init, /* init */
ossl_cleanup, /* cleanup */
#include "urldata.h"
+/* Struct to hold a Curl OpenSSL instance */
+struct ossl_ctx {
+ /* these ones requires specific SSL-types */
+ SSL_CTX* ssl_ctx;
+ SSL* ssl;
+ X509* server_cert;
+ BIO_METHOD *bio_method;
+ CURLcode io_result; /* result of last BIO cfilter operation */
+#ifndef HAVE_KEYLOG_CALLBACK
+ /* Set to true once a valid keylog entry has been created to avoid dupes. */
+ BIT(keylog_done);
+#endif
+ BIT(x509_store_setup); /* x509 store has been set up */
+ BIT(reused_session); /* session-ID was reused for this */
+};
+
+typedef CURLcode Curl_ossl_ctx_setup_cb(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ void *user_data);
+
+typedef int Curl_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid);
+
+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,
+ Curl_ossl_new_session_cb *cb_new_session,
+ void *ssl_user_data);
+
#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
#define SSL_get1_peer_certificate SSL_get_peer_certificate
#endif
struct Curl_easy *data,
SSL_CTX *ssl_ctx);
+/*
+ * Add a new session to the cache.
+ */
+CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const struct ssl_peer *peer,
+ SSL_SESSION *ssl_sessionid);
+
+/*
+ * Get the server cert, verify it and show it, etc., only call failf() if
+ * ssl config verifypeer or -host is set. Otherwise all this is for
+ * informational purposes only!
+ */
+CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ossl_ctx *octx,
+ struct ssl_peer *peer);
+
#endif /* USE_OPENSSL */
#endif /* HEADER_CURL_SSLUSE_H */
DEBUGASSERT(backend);
DEBUGF(infof(data,
"schannel: SSL/TLS connection with %s port %d (step 1/3)",
- connssl->peer.hostname, connssl->port));
+ connssl->peer.hostname, connssl->peer.port));
if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT,
VERSION_LESS_THAN_EQUAL)) {
/* check for an existing reusable credential handle */
if(ssl_config->primary.sessionid) {
Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, (void **)&old_cred, NULL)) {
+ if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
+ (void **)&old_cred, NULL)) {
backend->cred = old_cred;
DEBUGF(infof(data, "schannel: reusing existing credential handle"));
DEBUGF(infof(data,
"schannel: SSL/TLS connection with %s port %d (step 2/3)",
- connssl->peer.hostname, connssl->port));
+ connssl->peer.hostname, connssl->peer.port));
if(!backend->cred || !backend->ctxt)
return CURLE_SSL_CONNECT_ERROR;
DEBUGF(infof(data,
"schannel: SSL/TLS connection with %s port %d (step 3/3)",
- connssl->peer.hostname, connssl->port));
+ connssl->peer.hostname, connssl->peer.port));
if(!backend->cred)
return CURLE_SSL_CONNECT_ERROR;
struct Curl_schannel_cred *old_cred = NULL;
Curl_ssl_sessionid_lock(data);
- incache = !(Curl_ssl_getsessionid(cf, data, (void **)&old_cred, NULL));
+ incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer,
+ (void **)&old_cred, NULL));
if(incache) {
if(old_cred != backend->cred) {
DEBUGF(infof(data,
}
}
if(!incache) {
- result = Curl_ssl_addsessionid(cf, data, backend->cred,
+ result = Curl_ssl_addsessionid(cf, data, &connssl->peer, backend->cred,
sizeof(struct Curl_schannel_cred),
&added);
if(result) {
if(backend->ctxt) {
infof(data, "schannel: shutting down SSL/TLS connection with %s port %d",
- connssl->peer.hostname, connssl->port);
+ connssl->peer.hostname, connssl->peer.port);
}
if(backend->cred && backend->ctxt) {
size_t ssl_sessionid_len;
Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, (void **)&ssl_sessionid,
- &ssl_sessionid_len)) {
+ if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
+ (void **)&ssl_sessionid, &ssl_sessionid_len)) {
/* we got a session id, use it! */
err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
Curl_ssl_sessionid_unlock(data);
aprintf("%s:%d:%d:%s:%d",
ssl_cafile ? ssl_cafile : "(blob memory)",
verifypeer, conn_config->verifyhost, connssl->peer.hostname,
- connssl->port);
+ connssl->peer.port);
ssl_sessionid_len = strlen(ssl_sessionid);
err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
return CURLE_SSL_CONNECT_ERROR;
}
- result = Curl_ssl_addsessionid(cf, data, ssl_sessionid,
+ result = Curl_ssl_addsessionid(cf, data, &connssl->peer, ssl_sessionid,
ssl_sessionid_len, NULL);
Curl_ssl_sessionid_unlock(data);
if(result) {
*/
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 */
{
- struct ssl_connect_data *connssl = cf->ctx;
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;
if(!check->sessionid)
/* not session ID means blank entry */
continue;
- if(strcasecompare(connssl->peer.hostname, check->name) &&
+ 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)) &&
- (connssl->port == check->remote_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! */
DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d",
no_match? "Didn't find": "Found",
Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host",
- cf->conn->handler->scheme, connssl->peer.hostname,
- connssl->port));
+ cf->conn->handler->scheme, peer->hostname, peer->port));
return no_match;
}
*/
CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf,
struct Curl_easy *data,
+ const struct ssl_peer *peer,
void *ssl_sessionid,
size_t idsize,
bool *added)
{
- struct ssl_connect_data *connssl = cf->ctx;
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;
(void)ssl_config;
DEBUGASSERT(ssl_config->primary.sessionid);
- clone_host = strdup(connssl->peer.hostname);
+ clone_host = strdup(peer->hostname);
if(!clone_host)
return CURLE_OUT_OF_MEMORY; /* bail out */
store->conn_to_host = clone_conn_to_host; /* clone connect to host name */
store->conn_to_port = conn_to_port; /* connect to port number */
/* port number */
- store->remote_port = connssl->port;
+ store->remote_port = peer->port;
store->scheme = cf->conn->handler->scheme;
+ store->transport = peer->transport;
if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) {
Curl_free_primary_ssl_config(&store->ssl_config);
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,
+ int transport)
{
- struct ssl_connect_data *connssl = cf->ctx;
const char *ehostname, *edispname;
int eport;
}
}
- connssl->port = eport;
+ peer->port = eport;
+ peer->transport = transport;
return CURLE_OK;
}
goto out;
*done = FALSE;
- result = Curl_ssl_peer_init(&connssl->peer, cf);
+ result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP);
if(result)
goto out;
struct Curl_cftype Curl_cft_ssl_proxy = {
"SSL-PROXY",
- CF_TYPE_SSL,
+ CF_TYPE_SSL|CF_TYPE_PROXY,
CURL_LOG_LVL_NONE,
ssl_cf_destroy,
ssl_cf_connect,
bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf)
{
-#ifndef CURL_DISABLE_PROXY
- return (cf->cft == &Curl_cft_ssl_proxy);
-#else
- (void)cf;
- return FALSE;
-#endif
+ return (cf->cft->flags & CF_TYPE_SSL) && (cf->cft->flags & CF_TYPE_PROXY);
}
struct ssl_config_data *
/**
* Init SSL peer information for filter. Can be called repeatedly.
*/
-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, int transport);
/**
* Free all allocated data and reset peer information.
*/
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 */
- int port; /* remote port at origin */
BIT(use_alpn); /* if ALPN shall be used in handshake */
- BIT(reused_session); /* session-ID was reused for this */
BIT(peer_closed); /* peer has closed connection */
};
*/
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 */
/* add a new session ID
*/
CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf,
struct Curl_easy *data,
+ const struct ssl_peer *peer,
void *ssl_sessionid,
size_t idsize,
bool *added);
#if defined(WOLFSSL_ALLOW_TLSV10) && !defined(NO_OLD_TLS)
req_method = TLSv1_client_method();
use_sni(TRUE);
+ break;
#else
failf(data, "wolfSSL does not support TLS 1.0");
return CURLE_NOT_BUILT_IN;
#endif
- break;
case CURL_SSLVERSION_TLSv1_1:
#ifndef NO_OLD_TLS
req_method = TLSv1_1_client_method();
return CURLE_SSL_CONNECT_ERROR;
}
#endif
+ FALLTHROUGH();
default:
break;
}
void *ssl_sessionid = NULL;
Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) {
+ if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
+ &ssl_sessionid, NULL)) {
/* we got a session id, use it! */
if(!SSL_set_session(backend->handle, ssl_sessionid)) {
Curl_ssl_delsessionid(data, ssl_sessionid);
if(our_ssl_sessionid) {
Curl_ssl_sessionid_lock(data);
- incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL));
+ incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer,
+ &old_ssl_sessionid, NULL));
if(incache) {
if(old_ssl_sessionid != our_ssl_sessionid) {
infof(data, "old SSL session ID is stale, removing");
}
if(!incache) {
- result = Curl_ssl_addsessionid(cf, data, our_ssl_sessionid, 0, NULL);
+ result = Curl_ssl_addsessionid(cf, data, &connssl->peer,
+ our_ssl_sessionid, 0, NULL);
if(result) {
Curl_ssl_sessionid_unlock(data);
wolfSSL_SESSION_free(our_ssl_sessionid);
}
static void add_transfer(CURLM *multi, CURLSH *share,
- struct curl_slist *resolve, const char *url)
+ struct curl_slist *resolve,
+ const char *url, int http_version)
{
CURL *easy;
CURLMcode mc;
curl_easy_setopt(easy, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(easy, CURLOPT_AUTOREFERER, 1L);
curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1L);
- curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+ curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, http_version);
curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(easy, CURLOPT_WRITEDATA, NULL);
curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L);
int msgs_in_queue;
int add_more, waits, ongoing = 0;
char *host, *port;
+ int http_version = CURL_HTTP_VERSION_1_1;
- if(argc != 2) {
- fprintf(stderr, "%s URL\n", argv[0]);
+ if(argc != 3) {
+ fprintf(stderr, "%s proto URL\n", argv[0]);
exit(2);
}
- url = argv[1];
+ if(!strcmp("h2", argv[1]))
+ http_version = CURL_HTTP_VERSION_2;
+ else if(!strcmp("h3", argv[1]))
+ http_version = CURL_HTTP_VERSION_3ONLY;
+
+ url = argv[2];
cu = curl_url();
if(!cu) {
fprintf(stderr, "out of memory\n");
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
- add_transfer(multi, share, &resolve, url);
+ add_transfer(multi, share, &resolve, url, http_version);
++ongoing;
add_more = 6;
waits = 3;
}
else {
while(add_more) {
- add_transfer(multi, share, &resolve, url);
+ add_transfer(multi, share, &resolve, url, http_version);
++ongoing;
--add_more;
}
assert r.exit_code == 0, f'{client.dump_logs()}'
# Special client that tests TLS session reuse in parallel transfers
- def test_02_26_session_shared_reuse(self, env: Env, httpd, repeat):
- curl = CurlClient(env=env)
- url = f'https://{env.domain1}:{env.https_port}/data-100k'
+ # TODO: just uses a single connection for h2/h3. Not sure how to prevent that
+ @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
+ def test_02_26_session_shared_reuse(self, env: Env, proto, httpd, nghttpx, repeat):
+ url = f'https://{env.authority_for(env.domain1, proto)}/data-100k'
client = LocalClient(name='tls-session-reuse', env=env)
if not client.exists():
pytest.skip(f'example client not built: {client.name}')
- r = client.run(args=[url])
+ r = client.run(args=[proto, url])
r.check_exit_code(0)
# test on paused transfers, based on issue #11982