quic: implement CURLINFO_TLS_SSL_PTR

Replace the old Curl_ssl_get_internals() with a new connection filter
query to retrieve the information. Implement that filter query for TCP
and QUIC TLS filter types.

Add tests in client tls_session_reuse to use the info option and check
that pointers are returned.

Reported-by: Larry Campbell
Fixes #17801
Closes #17809
This commit is contained in:
Stefan Eissing 2025-07-03 12:06:41 +02:00 committed by Daniel Stenberg
parent 81693c77be
commit 2db8ae480f
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
13 changed files with 144 additions and 85 deletions

View File

@ -580,6 +580,19 @@ bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex)
return conn ? Curl_conn_cf_is_ssl(conn->cfilter[sockindex]) : FALSE;
}
bool Curl_conn_get_ssl_info(struct Curl_easy *data,
struct connectdata *conn, int sockindex,
struct curl_tlssessioninfo *info)
{
if(Curl_conn_is_ssl(conn, sockindex)) {
struct Curl_cfilter *cf = conn->cfilter[sockindex];
CURLcode result = cf ? cf->cft->query(cf, data, CF_QUERY_SSL_INFO,
NULL, (void *)info) : CURLE_UNKNOWN_OPTION;
return !result;
}
return FALSE;
}
bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex)
{
struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL;

View File

@ -32,6 +32,7 @@ struct Curl_easy;
struct Curl_dns_entry;
struct connectdata;
struct ip_quadruple;
struct curl_tlssessioninfo;
/* Callback to destroy resources held by this filter instance.
* Implementations MUST NOT chain calls to cf->next.
@ -151,6 +152,12 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
* - CF_QUERY_IP_INFO: res1 says if connection used IPv6, res2 is the
* ip quadruple
* - CF_QUERY_HOST_PORT: the remote hostname and port a filter talks to
* - CF_QUERY_SSL_INFO: fill out the passed curl_tlssessioninfo with the
* internal from the SSL secured connection when
* available.
* - CF_QUERY_SSL_CTX_INFO: same as CF_QUERY_SSL_INFO, but give the SSL_CTX
* when available, or the same internal pointer
* when the TLS stack does not differentiate.
*/
/* query res1 res2 */
#define CF_QUERY_MAX_CONCURRENT 1 /* number - */
@ -166,6 +173,8 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
* to NULL when not connected. */
#define CF_QUERY_REMOTE_ADDR 10 /* - `Curl_sockaddr_ex *` */
#define CF_QUERY_HOST_PORT 11 /* port const char * */
#define CF_QUERY_SSL_INFO 12 /* - struct curl_tlssessioninfo * */
#define CF_QUERY_SSL_CTX_INFO 13 /* - struct curl_tlssessioninfo * */
/**
* Query the cfilter for properties. Filters ignorant of a query will
@ -380,6 +389,15 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex);
*/
bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex);
/*
* Fill `info` with information about the TLS instance securing
* the connection when available, otherwise e.g. when
* Curl_conn_is_ssl() is FALSE, return FALSE.
*/
bool Curl_conn_get_ssl_info(struct Curl_easy *data,
struct connectdata *conn, int sockindex,
struct curl_tlssessioninfo *info);
/**
* Connection provides multiplexing of easy handles at `socketindex`.
*/

View File

@ -28,6 +28,7 @@
#include "urldata.h"
#include "getinfo.h"
#include "cfilters.h"
#include "vtls/vtls.h"
#include "connect.h" /* Curl_getconnectinfo() */
#include "progress.h"
@ -579,19 +580,14 @@ static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info,
struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **)
param_slistp;
struct curl_tlssessioninfo *tsi = &data->tsi;
#ifdef USE_SSL
struct connectdata *conn = data->conn;
#endif
/* we are exposing a pointer to internal memory with unknown
* lifetime here. */
*tsip = tsi;
tsi->backend = Curl_ssl_backend();
tsi->internals = NULL;
#ifdef USE_SSL
if(conn && tsi->backend != CURLSSLBACKEND_NONE) {
tsi->internals = Curl_ssl_get_internals(data, FIRSTSOCKET, info, 0);
if(!Curl_conn_get_ssl_info(data, data->conn, FIRSTSOCKET, tsi)) {
tsi->backend = Curl_ssl_backend();
tsi->internals = NULL;
}
#endif
}
break;
default:

View File

@ -2655,6 +2655,14 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
case CF_QUERY_HTTP_VERSION:
*pres1 = 30;
return CURLE_OK;
case CF_QUERY_SSL_INFO:
case CF_QUERY_SSL_CTX_INFO: {
struct curl_tlssessioninfo *info = pres2;
if(Curl_vquic_tls_get_ssl_info(&ctx->tls,
(query == CF_QUERY_SSL_INFO), info))
return CURLE_OK;
break;
}
default:
break;
}

View File

@ -2351,6 +2351,14 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
case CF_QUERY_HTTP_VERSION:
*pres1 = 30;
return CURLE_OK;
case CF_QUERY_SSL_INFO:
case CF_QUERY_SSL_CTX_INFO: {
struct curl_tlssessioninfo *info = pres2;
if(Curl_vquic_tls_get_ssl_info(&ctx->tls,
(query == CF_QUERY_SSL_INFO), info))
return CURLE_OK;
break;
}
default:
break;
}

View File

@ -1553,6 +1553,14 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
case CF_QUERY_HTTP_VERSION:
*pres1 = 30;
return CURLE_OK;
case CF_QUERY_SSL_INFO:
case CF_QUERY_SSL_CTX_INFO: {
struct curl_tlssessioninfo *info = pres2;
if(Curl_vquic_tls_get_ssl_info(&ctx->tls,
(query == CF_QUERY_SSL_INFO), info))
return CURLE_OK;
break;
}
default:
break;
}
@ -1658,20 +1666,4 @@ out:
return result;
}
bool Curl_conn_is_quiche(const struct Curl_easy *data,
const struct connectdata *conn,
int sockindex)
{
struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL;
(void)data;
for(; cf; cf = cf->next) {
if(cf->cft == &Curl_cft_http3)
return TRUE;
if(cf->cft->flags & CF_TYPE_IP_CONNECT)
return FALSE;
}
return FALSE;
}
#endif

View File

@ -41,10 +41,6 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
struct connectdata *conn,
const struct Curl_addrinfo *ai);
bool Curl_conn_is_quiche(const struct Curl_easy *data,
const struct connectdata *conn,
int sockindex);
#endif
#endif /* HEADER_CURL_VQUIC_CURL_QUICHE_H */

View File

@ -197,4 +197,28 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx,
}
bool Curl_vquic_tls_get_ssl_info(struct curl_tls_ctx *ctx,
bool give_ssl_ctx,
struct curl_tlssessioninfo *info)
{
#ifdef USE_OPENSSL
info->backend = CURLSSLBACKEND_OPENSSL;
info->internals = give_ssl_ctx ?
(void *)ctx->ossl.ssl_ctx : (void *)ctx->ossl.ssl;
return TRUE;
#elif defined(USE_GNUTLS)
(void)give_ssl_ctx; /* gnutls always returns its session */
info->backend = CURLSSLBACKEND_OPENSSL;
info->internals = ctx->gtls.session;
return TRUE;
#elif defined(USE_WOLFSSL)
info->backend = CURLSSLBACKEND_WOLFSSL;
info->internals = give_ssl_ctx ?
(void *)ctx->wssl.ssl_ctx : (void *)ctx->wssl.ssl;
return TRUE;
#else
return FALSE;
#endif
}
#endif /* !USE_HTTP3 && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */

View File

@ -37,6 +37,7 @@
struct ssl_peer;
struct Curl_ssl_session;
struct curl_tlssessioninfo;
struct curl_tls_ctx {
#ifdef USE_OPENSSL
@ -106,6 +107,10 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx,
struct Curl_easy *data,
struct ssl_peer *peer);
bool Curl_vquic_tls_get_ssl_info(struct curl_tls_ctx *ctx,
bool give_ssl_ctx,
struct curl_tlssessioninfo *info);
#endif /* !USE_HTTP3 && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */
#endif /* HEADER_CURL_VQUIC_TLS_H */

View File

@ -1558,6 +1558,18 @@ static CURLcode ssl_cf_query(struct Curl_cfilter *cf,
*when = connssl->handshake_done;
return CURLE_OK;
}
case CF_QUERY_SSL_INFO:
case CF_QUERY_SSL_CTX_INFO: {
struct curl_tlssessioninfo *info = pres2;
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
info->backend = Curl_ssl_backend();
info->internals = connssl->ssl_impl->get_internals(
cf->ctx, (query == CF_QUERY_SSL_INFO) ?
CURLINFO_TLS_SSL_PTR : CURLINFO_TLS_SESSION);
CF_DATA_RESTORE(cf, save);
return CURLE_OK;
}
default:
break;
}
@ -1727,40 +1739,6 @@ bool Curl_ssl_supports(struct Curl_easy *data, unsigned int ssl_option)
return (Curl_ssl->supports & ssl_option);
}
static struct Curl_cfilter *get_ssl_filter(struct Curl_cfilter *cf)
{
for(; cf; cf = cf->next) {
if(cf->cft == &Curl_cft_ssl)
return cf;
#ifndef CURL_DISABLE_PROXY
if(cf->cft == &Curl_cft_ssl_proxy)
return cf;
#endif
}
return NULL;
}
void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
CURLINFO info, int n)
{
void *result = NULL;
(void)n;
if(data->conn) {
struct Curl_cfilter *cf;
/* get first SSL filter in chain, if any is present */
cf = get_ssl_filter(data->conn->cfilter[sockindex]);
if(cf) {
struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
result = connssl->ssl_impl->get_internals(cf->ctx, info);
CF_DATA_RESTORE(cf, save);
}
}
return result;
}
static CURLcode vtls_shutdown_blocking(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool send_shutdown, bool *done)

View File

@ -233,16 +233,6 @@ CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at,
*/
bool Curl_ssl_supports(struct Curl_easy *data, unsigned int ssl_option);
/**
* Get the internal ssl instance (like OpenSSL's SSL*) from the filter
* chain at `sockindex` of type specified by `info`.
* For `n` == 0, the first active (top down) instance is returned.
* 1 gives the second active, etc.
* NULL is returned when no active SSL filter is present.
*/
void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
CURLINFO info, int n);
/**
* Get the ssl_config_data in `data` that is relevant for cfilter `cf`.
*/
@ -272,7 +262,6 @@ extern struct Curl_cftype Curl_cft_ssl_proxy;
#define Curl_ssl_free_certinfo(x) Curl_nop_stmt
#define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN)
#define Curl_ssl_cert_status_request() FALSE
#define Curl_ssl_get_internals(a,b,c,d) NULL
#define Curl_ssl_supports(a,b) FALSE
#define Curl_ssl_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN
#define Curl_ssl_cfilter_remove(a,b,c) CURLE_OK

View File

@ -1494,6 +1494,8 @@ static CURLcode add_parallel_transfers(struct GlobalConfig *global,
(void)curl_easy_setopt(per->curl, CURLOPT_PIPEWAIT,
global->parallel_connect ? 0L : 1L);
(void)curl_easy_setopt(per->curl, CURLOPT_PRIVATE, per);
/* curl does not use signals, switching this on saves some system calls */
(void)curl_easy_setopt(per->curl, CURLOPT_NOSIGNAL, 1L);
(void)curl_easy_setopt(per->curl, CURLOPT_XFERINFOFUNCTION, xferinfo_cb);
(void)curl_easy_setopt(per->curl, CURLOPT_XFERINFODATA, per);
(void)curl_easy_setopt(per->curl, CURLOPT_NOPROGRESS, 0L);

View File

@ -23,16 +23,40 @@
***************************************************************************/
#include "first.h"
static int tse_found_tls_session = FALSE;
static size_t write_tse_cb(char *ptr, size_t size, size_t nmemb, void *opaque)
{
CURL *easy = opaque;
(void)ptr;
(void)opaque;
if(!tse_found_tls_session) {
struct curl_tlssessioninfo *tlssession;
CURLcode rc;
rc = curl_easy_getinfo(easy, CURLINFO_TLS_SSL_PTR, &tlssession);
if(rc) {
curl_mfprintf(stderr, "curl_easy_getinfo(CURLINFO_TLS_SSL_PTR) "
"failed: %s\n", curl_easy_strerror(rc));
return rc;
}
if(tlssession->backend == CURLSSLBACKEND_NONE) {
curl_mfprintf(stderr, "curl_easy_getinfo(CURLINFO_TLS_SSL_PTR) "
"gave no backend\n");
return CURLE_FAILED_INIT;
}
if(!tlssession->internals) {
curl_mfprintf(stderr, "curl_easy_getinfo(CURLINFO_TLS_SSL_PTR) "
"missing\n");
return CURLE_FAILED_INIT;
}
tse_found_tls_session = TRUE;
}
return size * nmemb;
}
static int add_transfer(CURLM *multi, CURLSH *share,
struct curl_slist *resolve,
const char *url, long http_version)
static CURL *tse_add_transfer(CURLM *multi, CURLSH *share,
struct curl_slist *resolve,
const char *url, long http_version)
{
CURL *easy;
CURLMcode mc;
@ -40,7 +64,7 @@ static int add_transfer(CURLM *multi, CURLSH *share,
easy = curl_easy_init();
if(!easy) {
curl_mfprintf(stderr, "curl_easy_init failed\n");
return 1;
return NULL;
}
curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(easy, CURLOPT_DEBUGFUNCTION, debug_cb);
@ -51,7 +75,7 @@ static int add_transfer(CURLM *multi, CURLSH *share,
curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1L);
curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, http_version);
curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_tse_cb);
curl_easy_setopt(easy, CURLOPT_WRITEDATA, NULL);
curl_easy_setopt(easy, CURLOPT_WRITEDATA, easy);
curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L);
curl_easy_setopt(easy, CURLOPT_SSL_VERIFYPEER, 0L);
if(resolve)
@ -63,9 +87,9 @@ static int add_transfer(CURLM *multi, CURLSH *share,
curl_mfprintf(stderr, "curl_multi_add_handle: %s\n",
curl_multi_strerror(mc));
curl_easy_cleanup(easy);
return 1;
return NULL;
}
return 0;
return easy;
}
static int test_tls_session_reuse(int argc, char *argv[])
@ -132,7 +156,7 @@ static int test_tls_session_reuse(int argc, char *argv[])
curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
if(add_transfer(multi, share, resolve, url, http_version))
if(!tse_add_transfer(multi, share, resolve, url, http_version))
goto cleanup;
++ongoing;
add_more = 6;
@ -159,7 +183,7 @@ static int test_tls_session_reuse(int argc, char *argv[])
}
else {
while(add_more) {
if(add_transfer(multi, share, resolve, url, http_version))
if(!tse_add_transfer(multi, share, resolve, url, http_version))
goto cleanup;
++ongoing;
--add_more;
@ -203,6 +227,12 @@ static int test_tls_session_reuse(int argc, char *argv[])
} while(ongoing || add_more);
if(!tse_found_tls_session) {
curl_mfprintf(stderr, "CURLINFO_TLS_SSL_PTR not found during run\n");
exitcode = CURLE_FAILED_INIT;
goto cleanup;
}
curl_mfprintf(stderr, "exiting\n");
exitcode = 0;