lib: TLS session ticket caching reworked

Described in detail in internal doc TLS-SESSIONS.md

Main points:
- use a new `ssl_peer_key` for cache lookups by connection filters
- recognize differences between TLSv1.3 and other tickets
  * TLSv1.3 tickets are single-use, cache can hold several of them for a peer
  * TLSv1.2 are reused, keep only a single one per peer
- differentiate between ticket BLOB to store (that could be persisted) and object instances
- use put/take/return pattern for cache access
- remember TLS version, ALPN protocol, time received and lifetime of ticket
- auto-expire tickets after their lifetime

Closes #15774
This commit is contained in:
Stefan Eissing 2024-12-18 13:22:35 +01:00 committed by Daniel Stenberg
parent e5e2e09a75
commit fa0ccd9f1f
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
36 changed files with 1784 additions and 780 deletions

View file

@ -56,6 +56,7 @@ LIB_VTLS_CFILES = \
vtls/schannel_verify.c \
vtls/sectransp.c \
vtls/vtls.c \
vtls/vtls_scache.c \
vtls/wolfssl.c \
vtls/x509asn1.c
@ -74,6 +75,7 @@ LIB_VTLS_HFILES = \
vtls/sectransp.h \
vtls/vtls.h \
vtls/vtls_int.h \
vtls/vtls_scache.h \
vtls/wolfssl.h \
vtls/x509asn1.h

View file

@ -244,6 +244,18 @@ char *Curl_dyn_ptr(const struct dynbuf *s)
return s->bufr;
}
char *Curl_dyn_take(struct dynbuf *s, size_t *plen)
{
char *ptr = s->bufr;
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
*plen = s->leng;
s->bufr = NULL;
s->leng = 0;
s->allc = 0;
return ptr;
}
/*
* Returns an unsigned pointer to the buffer.
*/

View file

@ -39,6 +39,7 @@
#define Curl_dyn_uptr(a) curlx_dyn_uptr(a)
#define Curl_dyn_len(a) curlx_dyn_len(a)
#define Curl_dyn_reset(a) curlx_dyn_reset(a)
#define Curl_dyn_take(a,b) curlx_dyn_take(a,b)
#define Curl_dyn_tail(a,b) curlx_dyn_tail(a,b)
#define Curl_dyn_setlen(a,b) curlx_dyn_setlen(a,b)
#define curlx_dynbuf dynbuf /* for the struct name */
@ -75,6 +76,10 @@ size_t Curl_dyn_len(const struct dynbuf *s);
/* The implementation of this function exists in mprintf.c */
int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save);
/* Take the buffer out of the dynbuf. Caller has ownership and
* dynbuf resets to initial state. */
char *Curl_dyn_take(struct dynbuf *s, size_t *plen);
/* Dynamic buffer max sizes */
#define DYN_DOH_RESPONSE 3000
#define DYN_DOH_CNAME 256

View file

@ -134,16 +134,12 @@ Curl_llist_append(struct Curl_llist *list, const void *p,
Curl_llist_insert_next(list, list->_tail, p, ne);
}
/*
* @unittest: 1300
*/
void
Curl_node_uremove(struct Curl_llist_node *e, void *user)
void *Curl_node_take_elem(struct Curl_llist_node *e)
{
void *ptr;
struct Curl_llist *list;
if(!e)
return;
return NULL;
list = e->_list;
DEBUGASSERT(list);
@ -179,8 +175,23 @@ Curl_node_uremove(struct Curl_llist_node *e, void *user)
#endif
--list->_size;
return ptr;
}
/* call the dtor() last for when it actually frees the 'e' memory itself */
/*
* @unittest: 1300
*/
void
Curl_node_uremove(struct Curl_llist_node *e, void *user)
{
struct Curl_llist *list;
void *ptr;
if(!e)
return;
list = e->_list;
DEBUGASSERT(list);
ptr = Curl_node_take_elem(e);
if(list->_dtor)
list->_dtor(user, ptr);
}

View file

@ -75,6 +75,10 @@ size_t Curl_llist_count(struct Curl_llist *list);
/* Curl_node_elem() returns the custom data from a Curl_llist_node */
void *Curl_node_elem(struct Curl_llist_node *n);
/* Remove the node from the list and return the custom data
* from a Curl_llist_node. Will NOT incoke a registered `dtor`. */
void *Curl_node_take_elem(struct Curl_llist_node *);
/* Curl_node_next() returns the next element in a list from a given
Curl_llist_node */
struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n);

View file

@ -1588,8 +1588,8 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option,
data->hsts = NULL;
#endif
#ifdef USE_SSL
if(data->share->sslsession == data->state.session)
data->state.session = NULL;
if(data->share->ssl_scache == data->state.ssl_scache)
data->state.ssl_scache = NULL;
#endif
#ifdef USE_LIBPSL
if(data->psl == &data->share->psl)
@ -1632,10 +1632,8 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option,
}
#endif
#ifdef USE_SSL
if(data->share->sslsession) {
data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions;
data->state.session = data->share->sslsession;
}
if(data->share->ssl_scache)
data->state.ssl_scache = data->share->ssl_scache;
#endif
#ifdef USE_LIBPSL
if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL))

View file

@ -30,6 +30,7 @@
#include "share.h"
#include "psl.h"
#include "vtls/vtls.h"
#include "vtls/vtls_scache.h"
#include "hsts.h"
#include "url.h"
@ -108,12 +109,8 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
case CURL_LOCK_DATA_SSL_SESSION:
#ifdef USE_SSL
if(!share->sslsession) {
share->max_ssl_sessions = 8;
share->sslsession = calloc(share->max_ssl_sessions,
sizeof(struct Curl_ssl_session));
share->sessionage = 0;
if(!share->sslsession)
if(!share->ssl_scache) {
if(Curl_ssl_scache_create(8, 2, &share->ssl_scache))
res = CURLSHE_NOMEM;
}
#else
@ -174,7 +171,10 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
case CURL_LOCK_DATA_SSL_SESSION:
#ifdef USE_SSL
Curl_safefree(share->sslsession);
if(share->ssl_scache) {
Curl_ssl_scache_destroy(share->ssl_scache);
share->ssl_scache = NULL;
}
#else
res = CURLSHE_NOT_BUILT_IN;
#endif
@ -245,11 +245,9 @@ curl_share_cleanup(CURLSH *sh)
#endif
#ifdef USE_SSL
if(share->sslsession) {
size_t i;
for(i = 0; i < share->max_ssl_sessions; i++)
Curl_ssl_kill_session(&(share->sslsession[i]));
free(share->sslsession);
if(share->ssl_scache) {
Curl_ssl_scache_destroy(share->ssl_scache);
share->ssl_scache = NULL;
}
#endif

View file

@ -31,6 +31,8 @@
#include "urldata.h"
#include "conncache.h"
struct Curl_ssl_scache;
#define CURL_GOOD_SHARE 0x7e117a1e
#define GOOD_SHARE_HANDLE(x) ((x) && (x)->magic == CURL_GOOD_SHARE)
@ -58,9 +60,7 @@ struct Curl_share {
struct hsts *hsts;
#endif
#ifdef USE_SSL
struct Curl_ssl_session *sslsession;
size_t max_ssl_sessions;
long sessionage;
struct Curl_ssl_scache *ssl_scache;
#endif
};
@ -68,4 +68,9 @@ CURLSHcode Curl_share_lock(struct Curl_easy *, curl_lock_data,
curl_lock_access);
CURLSHcode Curl_share_unlock(struct Curl_easy *, curl_lock_data);
/* convenience macro to check if this handle is using a shared SSL spool */
#define CURL_SHARE_ssl_scache(data) (data->share && \
(data->share->specifier & \
(1<<CURL_LOCK_DATA_SSL_SESSION)))
#endif /* HEADER_CURL_SHARE_H */

View file

@ -72,6 +72,7 @@
#include "url.h"
#include "getinfo.h"
#include "vtls/vtls.h"
#include "vtls/vtls_scache.h"
#include "vquic/vquic.h"
#include "select.h"
#include "multiif.h"
@ -538,7 +539,7 @@ void Curl_init_CONNECT(struct Curl_easy *data)
*/
CURLcode Curl_pretransfer(struct Curl_easy *data)
{
CURLcode result;
CURLcode result = CURLE_OK;
if(!data->state.url && !data->set.uh) {
/* we cannot do anything without URL */
@ -577,12 +578,14 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
data->state.httpreq = data->set.method;
data->state.url = data->set.str[STRING_SET_URL];
/* Init the SSL session ID cache here. We do it here since we want to do it
after the *_setopt() calls (that could specify the size of the cache) but
before any transfer takes place. */
result = Curl_ssl_initsessions(data, data->set.general_ssl.max_ssl_sessions);
if(result)
return result;
#ifdef USE_SSL
if(!data->state.ssl_scache) {
result = Curl_ssl_scache_create(data->set.general_ssl.max_ssl_sessions,
2, &data->state.ssl_scache);
if(result)
return result;
}
#endif
data->state.requests = 0;
data->state.followlocation = 0; /* reset the location-follow counter */

View file

@ -271,21 +271,7 @@ enum protection_level {
/* SSL backend-specific data; declared differently by each SSL backend */
struct ssl_backend_data;
typedef enum {
CURL_SSL_PEER_DNS,
CURL_SSL_PEER_IPV4,
CURL_SSL_PEER_IPV6
} ssl_peer_type;
struct ssl_peer {
char *hostname; /* hostname for verification */
char *dispname; /* display version of hostname */
char *sni; /* SNI version of hostname or NULL if not usable */
ssl_peer_type type; /* type of the peer information */
int port; /* port we are talking to */
int transport; /* one of TRNSPRT_* defines */
};
struct Curl_ssl_scache_entry;
struct ssl_primary_config {
char *CApath; /* certificate dir (does not work on Windows) */
@ -341,24 +327,6 @@ struct ssl_general_config {
int ca_cache_timeout; /* Certificate store cache timeout (seconds) */
};
typedef void Curl_ssl_sessionid_dtor(void *sessionid, size_t idsize);
/* information stored about one single SSL session */
struct Curl_ssl_session {
char *name; /* hostname for which this ID was used */
char *conn_to_host; /* hostname for the connection (may be NULL) */
const char *scheme; /* protocol scheme used */
char *alpn; /* APLN TLS negotiated protocol string */
void *sessionid; /* as returned from the SSL layer */
size_t idsize; /* if known, otherwise 0 */
Curl_ssl_sessionid_dtor *sessionid_free; /* free `sessionid` callback */
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 */
};
#ifdef USE_WINDOWS_SSPI
#include "curl_sspi.h"
#endif
@ -1232,8 +1200,7 @@ struct UrlState {
curl_prot_t first_remote_protocol;
int retrycount; /* number of retries on a new connection */
struct Curl_ssl_session *session; /* array of 'max_ssl_sessions' size */
long sessionage; /* number of the most recent session */
struct Curl_ssl_scache *ssl_scache; /* TLS session pool */
int os_errno; /* filled in with errno whenever an error occurs */
long followlocation; /* redirect counter */
int requests; /* request counter: redirects + authentication retakes */

View file

@ -2131,7 +2131,8 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
ctx = cf ? cf->ctx : NULL;
data = cf ? CF_DATA_CURRENT(cf) : NULL;
if(cf && data && ctx) {
Curl_ossl_add_session(cf, data, &ctx->peer, ssl_sessionid);
Curl_ossl_add_session(cf, data, ctx->peer.scache_key, ssl_sessionid,
SSL_version(ssl), "h3");
return 1;
}
return 0;
@ -2158,7 +2159,8 @@ static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
}
switch(htype) {
case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: {
(void)Curl_gtls_update_session_id(cf, data, session, &ctx->peer, "h3");
(void)Curl_gtls_cache_session(cf, data, ctx->peer.scache_key,
session, -1, "h3");
break;
}
default:
@ -2181,7 +2183,8 @@ static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
struct Curl_easy *data = CF_DATA_CURRENT(cf);
DEBUGASSERT(data);
if(data && ctx) {
(void)wssl_cache_session(cf, data, &ctx->peer, session);
(void)Curl_wssl_cache_session(cf, data, ctx->peer.scache_key,
session, wolfSSL_version(ssl), "h3");
}
}
return 0;
@ -2258,10 +2261,6 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
int qfd;
DEBUGASSERT(ctx->initialized);
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,

View file

@ -1162,9 +1162,6 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
BIO_ADDR *baddr = NULL;
DEBUGASSERT(ctx->initialized);
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,

View file

@ -1278,10 +1278,6 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf,
if(result)
return result;
result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
if(result)
return result;
ctx->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
if(!ctx->cfg) {
failf(data, "cannot create quiche config");

View file

@ -50,6 +50,7 @@
#include "multiif.h"
#include "vtls/keylog.h"
#include "vtls/vtls.h"
#include "vtls/vtls_scache.h"
#include "vquic-tls.h"
/* The last 3 #include files should be in this order */
@ -221,7 +222,7 @@ static CURLcode wssl_init_ssl(struct curl_tls_ctx *ctx,
}
if(ssl_config->primary.cache_session) {
(void)wssl_setup_session(cf, data, &ctx->wssl, peer);
(void)Curl_wssl_setup_session(cf, data, &ctx->wssl, peer->scache_key);
}
return CURLE_OK;
@ -236,11 +237,26 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
Curl_vquic_tls_ctx_setup *cb_setup,
void *cb_user_data, void *ssl_user_data)
{
char tls_id[80];
CURLcode result;
#ifdef USE_OPENSSL
Curl_ossl_version(tls_id, sizeof(tls_id));
#elif defined(USE_GNUTLS)
Curl_gtls_version(tls_id, sizeof(tls_id));
#elif defined(USE_WOLFSSL)
Curl_wssl_version(tls_id, sizeof(tls_id));
#else
#error "no TLS lib in used, should not happen"
return CURLE_FAILED_INIT;
#endif
result = Curl_ssl_peer_init(peer, cf, tls_id, TRNSPRT_QUIC);
if(result)
return result;
#ifdef USE_OPENSSL
(void)result;
return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, TRNSPRT_QUIC,
return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer,
(const unsigned char *)alpn, alpn_len,
cb_setup, cb_user_data, NULL, ssl_user_data);
#elif defined(USE_GNUTLS)
@ -346,6 +362,9 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx,
}
#endif
/* on error, remove any session we might have in the pool */
if(result)
Curl_ssl_scache_remove_all(cf, data, peer->scache_key);
return result;
}

View file

@ -26,6 +26,7 @@
#include "curl_setup.h"
#include "bufq.h"
#include "vtls/vtls.h"
#include "vtls/openssl.h"
#if defined(USE_HTTP3) && \
@ -33,6 +34,8 @@
#include "vtls/wolfssl.h"
struct ssl_peer;
struct curl_tls_ctx {
#ifdef USE_OPENSSL
struct ossl_ctx ossl;

View file

@ -34,6 +34,7 @@
#include "inet_pton.h"
#include "vtls.h"
#include "vtls_int.h"
#include "vtls_scache.h"
#include "connect.h"
#include "select.h"
#include "multiif.h"
@ -609,20 +610,19 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable);
if(ssl_config->primary.cache_session) {
void *sdata;
size_t slen;
struct Curl_ssl_session *sc_session = NULL;
const br_ssl_session_parameters *session;
CURL_TRC_CF(data, cf, "connect_step1, check session cache");
Curl_ssl_sessionid_lock(data);
if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &sdata, &slen, NULL) &&
slen == sizeof(*session)) {
session = sdata;
ret = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key,
&sc_session);
if(!ret && sc_session && sc_session->sdata && sc_session->sdata_len) {
session = (br_ssl_session_parameters *)(void *)sc_session->sdata;
br_ssl_engine_set_session_parameters(&backend->ctx.eng, session);
session_set = 1;
infof(data, "BearSSL: reusing session ID");
/* single use of sessions */
Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session);
}
Curl_ssl_sessionid_unlock(data);
}
if(connssl->alpn) {
@ -804,12 +804,6 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf,
return ret;
}
static void bearssl_session_free(void *sessionid, size_t idsize)
{
(void)idsize;
free(sessionid);
}
static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
@ -832,17 +826,22 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
}
if(ssl_config->primary.cache_session) {
struct Curl_ssl_session *sc_session;
br_ssl_session_parameters *session;
session = malloc(sizeof(*session));
if(!session)
return CURLE_OUT_OF_MEMORY;
br_ssl_engine_get_session_parameters(&backend->ctx.eng, session);
Curl_ssl_sessionid_lock(data);
ret = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
session, sizeof(*session),
bearssl_session_free);
Curl_ssl_sessionid_unlock(data);
ret = Curl_ssl_session_create((unsigned char *)session, sizeof(*session),
(int)session->version,
connssl->negotiated.alpn,
0, -1, &sc_session);
if(!ret) {
ret = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key,
sc_session);
/* took ownership of `sc_session` */
}
if(ret)
return ret;
}

View file

@ -47,6 +47,7 @@
#include "gtls.h"
#include "vtls.h"
#include "vtls_int.h"
#include "vtls_scache.h"
#include "vauth/vauth.h"
#include "parsedate.h"
#include "connect.h" /* for the connect timeout */
@ -714,21 +715,17 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf,
return CURLE_OK;
}
static void gtls_sessionid_free(void *sessionid, size_t idsize)
{
(void)idsize;
free(sessionid);
}
CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf,
struct Curl_easy *data,
gnutls_session_t session,
struct ssl_peer *peer,
const char *alpn)
CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key,
gnutls_session_t session,
int lifetime_secs,
const char *alpn)
{
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
void *connect_sessionid;
size_t connect_idsize = 0;
struct Curl_ssl_session *sc_session;
unsigned char *sdata;
size_t sdata_len = 0;
CURLcode result = CURLE_OK;
if(!ssl_config->primary.cache_session)
@ -740,35 +737,57 @@ CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf,
detect that. */
/* get the session ID data size */
gnutls_session_get_data(session, NULL, &connect_idsize);
if(!connect_idsize) /* gnutls does this for some version combinations */
gnutls_session_get_data(session, NULL, &sdata_len);
if(!sdata_len) /* gnutls does this for some version combinations */
return CURLE_OK;
connect_sessionid = malloc(connect_idsize); /* get a buffer for it */
if(!connect_sessionid)
sdata = malloc(sdata_len); /* get a buffer for it */
if(!sdata)
return CURLE_OUT_OF_MEMORY;
/* extract session ID to the allocated buffer */
gnutls_session_get_data(session, connect_sessionid, &connect_idsize);
gnutls_session_get_data(session, sdata, &sdata_len);
CURL_TRC_CF(data, cf, "get session id (len=%zu, alpn=%s) and store in cache",
connect_idsize, alpn ? alpn : "-");
Curl_ssl_sessionid_lock(data);
/* store this session id, takes ownership */
result = Curl_ssl_set_sessionid(cf, data, peer, alpn,
connect_sessionid, connect_idsize,
gtls_sessionid_free);
Curl_ssl_sessionid_unlock(data);
sdata_len, alpn ? alpn : "-");
result = Curl_ssl_session_create(sdata, sdata_len,
Curl_glts_get_ietf_proto(session),
alpn, 0, lifetime_secs,
&sc_session);
/* call took ownership of `sdata`*/
if(!result) {
result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
/* took ownership of `sc_session` */
}
return result;
}
int Curl_glts_get_ietf_proto(gnutls_session_t session)
{
switch(gnutls_protocol_get_version(session)) {
case GNUTLS_SSL3:
return CURL_IETF_PROTO_SSL3;
case GNUTLS_TLS1_0:
return CURL_IETF_PROTO_TLS1;
case GNUTLS_TLS1_1:
return CURL_IETF_PROTO_TLS1_1;
case GNUTLS_TLS1_2:
return CURL_IETF_PROTO_TLS1_2;
case GNUTLS_TLS1_3:
return CURL_IETF_PROTO_TLS1_3;
default:
return CURL_IETF_PROTO_UNKNOWN;
}
}
static CURLcode cf_gtls_update_session_id(struct Curl_cfilter *cf,
struct Curl_easy *data,
gnutls_session_t session)
{
struct ssl_connect_data *connssl = cf->ctx;
return Curl_gtls_update_session_id(cf, data, session, &connssl->peer,
connssl->alpn_negotiated);
return Curl_gtls_cache_session(cf, data, connssl->peer.scache_key,
session, -1,
connssl->negotiated.alpn);
}
static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
@ -1058,9 +1077,11 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
struct Curl_ssl_session *scs = NULL;
gnutls_datum_t gtls_alpns[5];
size_t gtls_alpns_count = 0;
CURLcode result;
int rc;
DEBUGASSERT(gctx);
@ -1085,29 +1106,23 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
/* This might be a reconnect, so we check for a session ID in the cache
to speed up things */
if(conn_config->cache_session) {
void *ssl_sessionid;
size_t ssl_idsize;
char *session_alpn;
Curl_ssl_sessionid_lock(data);
if(!Curl_ssl_getsessionid(cf, data, peer,
&ssl_sessionid, &ssl_idsize, &session_alpn)) {
/* we got a session id, use it! */
int rc;
result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs);
if(result)
goto out;
rc = gnutls_session_set_data(gctx->session, ssl_sessionid, ssl_idsize);
if(rc < 0)
infof(data, "SSL failed to set session ID");
if(scs && scs->sdata && scs->sdata_len) {
/* we got a cached session, use it! */
rc = gnutls_session_set_data(gctx->session, scs->sdata, scs->sdata_len);
if(rc < 0) {
infof(data, "SSL session not accepted by GnuTLS, continuing without");
}
else {
infof(data, "SSL reusing session ID (size=%zu, alpn=%s)",
ssl_idsize, session_alpn ? session_alpn : "-");
#ifdef DEBUGBUILD
if((ssl_config->earlydata || !!getenv("CURL_USE_EARLYDATA")) &&
#else
infof(data, "SSL reusing session with ALPN '%s'",
scs->alpn ? scs->alpn : "-");
if(ssl_config->earlydata &&
#endif
!cf->conn->connect_only && connssl &&
(gnutls_protocol_get_version(gctx->session) == GNUTLS_TLS1_3) &&
Curl_alpn_contains_proto(connssl->alpn, session_alpn)) {
Curl_alpn_contains_proto(connssl->alpn, scs->alpn)) {
connssl->earlydata_max =
gnutls_record_get_max_early_data_size(gctx->session);
if((!connssl->earlydata_max ||
@ -1118,24 +1133,32 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
else {
CURL_TRC_CF(data, cf, "TLS session allows %zu earlydata bytes, "
"reusing ALPN '%s'",
connssl->earlydata_max, session_alpn);
connssl->earlydata_max, scs->alpn);
connssl->earlydata_state = ssl_earlydata_use;
connssl->state = ssl_connection_deferred;
result = Curl_alpn_set_negotiated(cf, data, connssl,
(const unsigned char *)session_alpn,
session_alpn ? strlen(session_alpn) : 0);
(const unsigned char *)scs->alpn,
scs->alpn ? strlen(scs->alpn) : 0);
if(result)
return result;
goto out;
/* We only try the ALPN protocol the session used before,
* otherwise we might send early data for the wrong protocol */
gtls_alpns[0].data = (unsigned char *)session_alpn;
gtls_alpns[0].size = (unsigned)strlen(session_alpn);
gtls_alpns_count = 1;
gtls_alpns[0].data = (unsigned char *)scs->alpn;
gtls_alpns[0].size = (unsigned)strlen(scs->alpn);
if(gnutls_alpn_set_protocols(gctx->session,
gtls_alpns, 1,
GNUTLS_ALPN_MANDATORY)) {
failf(data, "failed setting ALPN");
result = CURLE_SSL_CONNECT_ERROR;
goto out;
}
/* don't set again below */
gtls_alpns_count = 0;
alpn = NULL;
}
}
}
}
Curl_ssl_sessionid_unlock(data);
}
/* convert the ALPN string from our arguments to a list of strings that
@ -1143,19 +1166,21 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
* to the server. nice. */
if(!gtls_alpns_count && alpn && alpn_len) {
size_t i, alen = alpn_len;
unsigned char *s = (unsigned char *)alpn;
unsigned char *salpn = (unsigned char *)alpn;
unsigned char slen;
for(i = 0; (i < ARRAYSIZE(gtls_alpns)) && alen; ++i) {
slen = s[0];
slen = salpn[0];
if(slen >= alen)
return CURLE_FAILED_INIT;
gtls_alpns[i].data = s + 1;
gtls_alpns[i].data = salpn + 1;
gtls_alpns[i].size = slen;
s += slen + 1;
salpn += slen + 1;
alen -= (size_t)slen + 1;
}
if(alen) /* not all alpn chars used, wrong format or too many */
return CURLE_FAILED_INIT;
if(alen) { /* not all alpn chars used, wrong format or too many */
result = CURLE_FAILED_INIT;
goto out;
}
gtls_alpns_count = i;
}
@ -1164,10 +1189,12 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
gtls_alpns, (unsigned int)gtls_alpns_count,
GNUTLS_ALPN_MANDATORY)) {
failf(data, "failed setting ALPN");
return CURLE_SSL_CONNECT_ERROR;
result = CURLE_SSL_CONNECT_ERROR;
}
return CURLE_OK;
out:
Curl_ssl_scache_return(cf, data, peer->scache_key, scs);
return result;
}
static CURLcode
@ -1197,7 +1224,8 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
}
result = Curl_gtls_ctx_init(&backend->gtls, cf, data, &connssl->peer,
proto.data, proto.len, connssl, NULL, NULL, cf);
proto.data, proto.len,
connssl, NULL, NULL, cf);
if(result)
return result;
@ -2213,7 +2241,7 @@ out:
return ret;
}
static size_t gtls_version(char *buffer, size_t size)
size_t Curl_gtls_version(char *buffer, size_t size)
{
return msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL));
}
@ -2268,7 +2296,7 @@ const struct Curl_ssl Curl_ssl_gnutls = {
gtls_init, /* init */
gtls_cleanup, /* cleanup */
gtls_version, /* version */
Curl_gtls_version, /* version */
gtls_shutdown, /* shutdown */
gtls_data_pending, /* data_pending */
gtls_random, /* random */

View file

@ -47,6 +47,8 @@ struct ssl_config_data;
struct ssl_peer;
struct ssl_connect_data;
int Curl_glts_get_ietf_proto(gnutls_session_t session);
struct gtls_shared_creds {
gnutls_certificate_credentials_t creds;
char *CAfile; /* CAfile path used to generate X509 store */
@ -70,6 +72,8 @@ struct gtls_ctx {
BIT(sent_shutdown);
};
size_t Curl_gtls_version(char *buffer, size_t size);
typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf,
struct Curl_easy *data,
void *user_data);
@ -96,11 +100,12 @@ CURLcode Curl_gtls_verifyserver(struct Curl_easy *data,
const char *pinned_key);
/* Extract TLS session and place in cache, if configured. */
CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf,
struct Curl_easy *data,
gnutls_session_t session,
struct ssl_peer *peer,
const char *alpn);
CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key,
gnutls_session_t session,
int lifetime_secs,
const char *alpn);
extern const struct Curl_ssl Curl_ssl_gnutls;

View file

@ -64,6 +64,7 @@
#include "mbedtls.h"
#include "vtls.h"
#include "vtls_int.h"
#include "vtls_scache.h"
#include "x509asn1.h"
#include "parsedate.h"
#include "connect.h" /* for the connect timeout */
@ -875,29 +876,30 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
/* Check if there is a cached ID we can/should use here! */
if(ssl_config->primary.cache_session) {
void *sdata = NULL;
size_t slen = 0;
struct Curl_ssl_session *sc_session = NULL;
CURLcode result;
Curl_ssl_sessionid_lock(data);
if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
&sdata, &slen, NULL) && slen) {
result = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key,
&sc_session);
if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) {
mbedtls_ssl_session session;
mbedtls_ssl_session_init(&session);
ret = mbedtls_ssl_session_load(&session, sdata, slen);
ret = mbedtls_ssl_session_load(&session, sc_session->sdata,
sc_session->sdata_len);
if(ret) {
failf(data, "error loading cached session: -0x%x", -ret);
failf(data, "SSL session error loading: -0x%x", -ret);
}
else {
ret = mbedtls_ssl_set_session(&backend->ssl, &session);
if(ret)
failf(data, "error setting session: -0x%x", -ret);
failf(data, "SSL session error setting: -0x%x", -ret);
else
infof(data, "SSL reusing session ID");
}
mbedtls_ssl_session_free(&session);
}
Curl_ssl_sessionid_unlock(data);
Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session);
}
mbedtls_ssl_conf_ca_chain(&backend->config,
@ -1115,12 +1117,6 @@ pinnedpubkey_error:
return CURLE_OK;
}
static void mbedtls_session_free(void *session, size_t slen)
{
(void)slen;
free(session);
}
static CURLcode
mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data)
{
@ -1128,48 +1124,64 @@ mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data)
struct mbed_ssl_backend_data *backend =
(struct mbed_ssl_backend_data *)connssl->backend;
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
mbedtls_ssl_session session;
bool msession_alloced = FALSE;
struct Curl_ssl_session *sc_session = NULL;
unsigned char *sdata = NULL;
size_t slen = 0;
int ietf_tls_id;
CURLcode result = CURLE_OK;
int ret;
DEBUGASSERT(backend);
if(ssl_config->primary.cache_session) {
int ret;
mbedtls_ssl_session session;
unsigned char *sdata = NULL;
size_t slen = 0;
if(!ssl_config->primary.cache_session)
return CURLE_OK;
mbedtls_ssl_session_init(&session);
ret = mbedtls_ssl_get_session(&backend->ssl, &session);
if(ret) {
if(ret != MBEDTLS_ERR_SSL_ALLOC_FAILED)
mbedtls_ssl_session_free(&session);
failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret);
return CURLE_SSL_CONNECT_ERROR;
}
mbedtls_ssl_session_save(&session, NULL, 0, &slen);
if(!slen) {
failf(data, "failed to serialize session: length is 0");
}
else {
sdata = malloc(slen);
if(sdata) {
ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen);
if(ret) {
failf(data, "failed to serialize session: -0x%x", -ret);
}
else {
Curl_ssl_sessionid_lock(data);
result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
sdata, slen, mbedtls_session_free);
Curl_ssl_sessionid_unlock(data);
if(!result)
sdata = NULL;
}
}
}
mbedtls_ssl_session_free(&session);
free(sdata);
mbedtls_ssl_session_init(&session);
ret = mbedtls_ssl_get_session(&backend->ssl, &session);
msession_alloced = (ret != MBEDTLS_ERR_SSL_ALLOC_FAILED);
if(ret) {
failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret);
result = CURLE_SSL_CONNECT_ERROR;
goto out;
}
mbedtls_ssl_session_save(&session, NULL, 0, &slen);
if(!slen) {
failf(data, "failed to serialize session: length is 0");
goto out;
}
sdata = malloc(slen);
if(!sdata) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen);
if(ret) {
failf(data, "failed to serialize session: -0x%x", -ret);
goto out;
}
#if MBEDTLS_VERSION_NUMBER >= 0x03020000
ietf_tls_id = mbedtls_ssl_get_version_number(&backend->ssl);
#else
ietf_tls_id = CURL_IETF_PROTO_UNKNOWN;
#endif
result = Curl_ssl_session_create(sdata, slen,
ietf_tls_id,
connssl->negotiated.alpn, 0, -1,
&sc_session);
sdata = NULL; /* call took ownership */
if(!result)
result = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key,
sc_session);
out:
if(msession_alloced)
mbedtls_ssl_session_free(&session);
free(sdata);
return result;
}

View file

@ -56,6 +56,7 @@
#include "select.h"
#include "vtls.h"
#include "vtls_int.h"
#include "vtls_scache.h"
#include "vauth/vauth.h"
#include "keylog.h"
#include "strcase.h"
@ -960,8 +961,6 @@ static const char *SSL_ERROR_to_str(int err)
}
}
static size_t ossl_version(char *buffer, size_t size);
/* Return error string for last OpenSSL error
*/
static char *ossl_strerror(unsigned long error, char *buf, size_t size)
@ -970,7 +969,7 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size)
DEBUGASSERT(size);
*buf = '\0';
len = ossl_version(buf, size);
len = Curl_ossl_version(buf, size);
DEBUGASSERT(len < (size - 2));
if(len < (size - 2)) {
buf += len;
@ -2013,13 +2012,6 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
}
}
static void ossl_session_free(void *sessionid, size_t idsize)
{
/* free the ID */
(void)idsize;
free(sessionid);
}
/*
* This function is called when the 'data' struct is going away. Close
* down everything and free all resources!
@ -2873,20 +2865,23 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
const struct ssl_peer *peer,
SSL_SESSION *session)
const char *ssl_peer_key,
SSL_SESSION *session,
int ietf_tls_id,
const char *alpn)
{
const struct ssl_config_data *config;
unsigned char *der_session_buf = NULL;
CURLcode result = CURLE_OK;
size_t der_session_size;
unsigned char *der_session_buf;
unsigned char *der_session_ptr;
if(!cf || !data)
goto out;
config = Curl_ssl_cf_get_config(cf, data);
if(config->primary.cache_session) {
struct Curl_ssl_session *sc_session = NULL;
size_t der_session_size;
unsigned char *der_session_ptr;
der_session_size = i2d_SSL_SESSION(session, NULL);
if(der_session_size == 0) {
@ -2903,17 +2898,22 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
der_session_size = i2d_SSL_SESSION(session, &der_session_ptr);
if(der_session_size == 0) {
result = CURLE_OUT_OF_MEMORY;
free(der_session_buf);
goto out;
}
Curl_ssl_sessionid_lock(data);
result = Curl_ssl_set_sessionid(cf, data, peer, NULL, der_session_buf,
der_session_size, ossl_session_free);
Curl_ssl_sessionid_unlock(data);
result = Curl_ssl_session_create(der_session_buf, der_session_size,
ietf_tls_id, alpn, 0,
SSL_SESSION_get_timeout(session),
&sc_session);
der_session_buf = NULL; /* took ownership of sdata */
if(!result) {
result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
/* took ownership of `sc_session` */
}
}
out:
free(der_session_buf);
return result;
}
@ -2929,7 +2929,9 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
cf = (struct Curl_cfilter*) SSL_get_app_data(ssl);
connssl = cf ? cf->ctx : NULL;
data = connssl ? CF_DATA_CURRENT(cf) : NULL;
Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid);
if(data && connssl)
Curl_ossl_add_session(cf, data, connssl->peer.scache_key, ssl_sessionid,
SSL_version(ssl), connssl->negotiated.alpn);
return 0;
}
@ -3468,7 +3470,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
int transport, /* TCP or QUIC */
const unsigned char *alpn, size_t alpn_len,
Curl_ossl_ctx_setup_cb *cb_setup,
void *cb_user_data,
@ -3479,9 +3480,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
const char *ciphers;
SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
ctx_option_t ctx_options = 0;
SSL_SESSION *ssl_session = NULL;
const unsigned char *der_sessionid = NULL;
size_t der_sessionid_size = 0;
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
const long int ssl_version_min = conn_config->version;
@ -3498,7 +3496,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
ssl_config->certverifyresult = !X509_V_OK;
switch(transport) {
switch(peer->transport) {
case TRNSPRT_TCP:
/* check to see if we have been told to use an explicit SSL/TLS version */
switch(ssl_version_min) {
@ -3542,7 +3540,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
#endif
break;
default:
failf(data, "unsupported transport %d in SSL init", transport);
failf(data, "unsupported transport %d in SSL init", peer->transport);
return CURLE_SSL_CONNECT_ERROR;
}
@ -3965,32 +3963,36 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
octx->reused_session = FALSE;
if(ssl_config->primary.cache_session) {
Curl_ssl_sessionid_lock(data);
if(!Curl_ssl_getsessionid(cf, data, peer, (void **)&der_sessionid,
&der_sessionid_size, NULL)) {
/* we got a session id, use it! */
struct Curl_ssl_session *sc_session = NULL;
result = Curl_ssl_scache_take(cf, data, peer->scache_key, &sc_session);
if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) {
const unsigned char *der_sessionid = sc_session->sdata;
size_t der_sessionid_size = sc_session->sdata_len;
SSL_SESSION *ssl_session = NULL;
/* If OpenSSL does not accept the session from the cache, this
* is not an error. We just continue without it. */
ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid,
(long)der_sessionid_size);
(long)der_sessionid_size);
if(ssl_session) {
if(!SSL_set_session(octx->ssl, ssl_session)) {
Curl_ssl_sessionid_unlock(data);
SSL_SESSION_free(ssl_session);
failf(data, "SSL: SSL_set_session failed: %s",
infof(data, "SSL: SSL_set_session not accepted, "
"continuing without: %s",
ossl_strerror(ERR_get_error(), error_buffer,
sizeof(error_buffer)));
return CURLE_SSL_CONNECT_ERROR;
}
else {
infof(data, "SSL reusing session");
octx->reused_session = TRUE;
}
SSL_SESSION_free(ssl_session);
/* Informational message */
infof(data, "SSL reusing session ID");
octx->reused_session = TRUE;
}
else {
Curl_ssl_sessionid_unlock(data);
return CURLE_SSL_CONNECT_ERROR;
infof(data, "SSL session not accepted by OpenSSL, continuing without");
}
}
Curl_ssl_sessionid_unlock(data);
Curl_ssl_scache_return(cf, data, peer->scache_key, sc_session);
}
return CURLE_OK;
@ -4018,7 +4020,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
}
#endif
result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, TRNSPRT_TCP,
result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer,
proto.data, proto.len, NULL, NULL,
ossl_new_session_cb, cf);
if(result)
@ -4693,21 +4695,6 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
/* do not do this after Session ID reuse */
result = verifystatus(cf, data, octx);
if(result) {
/* when verifystatus failed, remove the session id from the cache again
if present */
if(!Curl_ssl_cf_is_proxy(cf)) {
void *old_ssl_sessionid = NULL;
bool incache;
Curl_ssl_sessionid_lock(data);
incache = !(Curl_ssl_getsessionid(cf, data, peer,
&old_ssl_sessionid, NULL, NULL));
if(incache) {
infof(data, "Remove session ID again from cache");
Curl_ssl_delsessionid(data, old_ssl_sessionid);
}
Curl_ssl_sessionid_unlock(data);
}
X509_free(octx->server_cert);
octx->server_cert = NULL;
return result;
@ -4757,6 +4744,9 @@ static CURLcode ossl_connect_step3(struct Curl_cfilter *cf,
result = Curl_oss_check_peer_cert(cf, data, octx, &connssl->peer);
if(!result)
connssl->connecting_state = ssl_connect_done;
else
/* on error, remove sessions we might have in the pool */
Curl_ssl_scache_remove_all(cf, data, connssl->peer.scache_key);
return result;
}
@ -5172,7 +5162,7 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex,
#endif
}
static size_t ossl_version(char *buffer, size_t size)
size_t Curl_ossl_version(char *buffer, size_t size)
{
#ifdef LIBRESSL_VERSION_NUMBER
#ifdef HAVE_OPENSSL_VERSION
@ -5336,7 +5326,7 @@ const struct Curl_ssl Curl_ssl_openssl = {
ossl_init, /* init */
ossl_cleanup, /* cleanup */
ossl_version, /* version */
Curl_ossl_version, /* version */
ossl_shutdown, /* shutdown */
ossl_data_pending, /* data_pending */
ossl_random, /* random */

View file

@ -36,6 +36,8 @@
#include "urldata.h"
struct ssl_peer;
/* Struct to hold a Curl OpenSSL instance */
struct ossl_ctx {
/* these ones requires specific SSL-types */
@ -53,6 +55,8 @@ struct ossl_ctx {
BIT(reused_session); /* session-ID was reused for this */
};
size_t Curl_ossl_version(char *buffer, size_t size);
typedef CURLcode Curl_ossl_ctx_setup_cb(struct Curl_cfilter *cf,
struct Curl_easy *data,
void *user_data);
@ -63,7 +67,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
int transport, /* TCP or QUIC */
const unsigned char *alpn, size_t alpn_len,
Curl_ossl_ctx_setup_cb *cb_setup,
void *cb_user_data,
@ -94,8 +97,10 @@ CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf,
*/
CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
const struct ssl_peer *peer,
SSL_SESSION *ssl_sessionid);
const char *ssl_peer_key,
SSL_SESSION *ssl_sessionid,
int ietf_tls_id,
const char *alpn);
/*
* Get the server cert, verify it and show it, etc., only call failf() if

View file

@ -41,6 +41,7 @@
#include "schannel_int.h"
#include "vtls.h"
#include "vtls_int.h"
#include "vtls_scache.h"
#include "strcase.h"
#include "sendf.h"
#include "connect.h" /* for the connect timeout */
@ -954,9 +955,9 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
/* check for an existing reusable credential handle */
if(ssl_config->primary.cache_session) {
Curl_ssl_sessionid_lock(data);
if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
(void **)&old_cred, NULL, NULL)) {
Curl_ssl_scache_lock(data);
if(Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key,
(void **)&old_cred)) {
backend->cred = old_cred;
DEBUGF(infof(data, "schannel: reusing existing credential handle"));
@ -966,7 +967,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
"schannel: incremented credential handle refcount = %d",
backend->cred->refcount));
}
Curl_ssl_sessionid_unlock(data);
Curl_ssl_scache_unlock(data);
}
if(!backend->cred) {
@ -1501,12 +1502,11 @@ add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, bool reverse_order,
return args->result == CURLE_OK;
}
static void schannel_session_free(void *sessionid, size_t idsize)
static void schannel_session_free(void *sessionid)
{
/* this is expected to be called under sessionid lock */
struct Curl_schannel_cred *cred = sessionid;
(void)idsize;
if(cred) {
cred->refcount--;
if(cred->refcount == 0) {
@ -1599,14 +1599,12 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
/* save the current session data for possible reuse */
if(ssl_config->primary.cache_session) {
Curl_ssl_sessionid_lock(data);
Curl_ssl_scache_lock(data);
/* Up ref count since call takes ownership */
backend->cred->refcount++;
result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
backend->cred,
sizeof(struct Curl_schannel_cred),
schannel_session_free);
Curl_ssl_sessionid_unlock(data);
result = Curl_ssl_scache_add_obj(cf, data, connssl->peer.scache_key,
backend->cred, schannel_session_free);
Curl_ssl_scache_unlock(data);
if(result)
return result;
}
@ -2445,9 +2443,9 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data)
/* free SSPI Schannel API credential handle */
if(backend->cred) {
Curl_ssl_sessionid_lock(data);
schannel_session_free(backend->cred, 0);
Curl_ssl_sessionid_unlock(data);
Curl_ssl_scache_lock(data);
schannel_session_free(backend->cred);
Curl_ssl_scache_unlock(data);
backend->cred = NULL;
}

View file

@ -38,6 +38,7 @@
#include "multiif.h"
#include "strcase.h"
#include "x509asn1.h"
#include "vtls_scache.h"
#include "strerror.h"
#include "cipher_suite.h"
@ -1020,7 +1021,7 @@ failed:
return ret;
}
static void sectransp_session_free(void *sessionid, size_t idsize)
static void sectransp_session_free(void *sessionid)
{
/* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a
cached session ID inside the Security framework. There is a private
@ -1028,7 +1029,6 @@ static void sectransp_session_free(void *sessionid, size_t idsize)
got your application rejected from the App Store due to the use of a
private API, so the best we can do is free up our own char array that we
created way back in sectransp_connect_step1... */
(void)idsize;
Curl_safefree(sessionid);
}
@ -1337,19 +1337,19 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
char *ssl_sessionid;
size_t ssl_sessionid_len;
Curl_ssl_sessionid_lock(data);
if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
(void **)&ssl_sessionid, &ssl_sessionid_len,
NULL)) {
Curl_ssl_scache_lock(data);
if(Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key,
(void **)&ssl_sessionid)) {
/* we got a session id, use it! */
err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
Curl_ssl_sessionid_unlock(data);
err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid,
strlen(ssl_sessionid));
Curl_ssl_scache_unlock(data);
if(err != noErr) {
failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err);
return CURLE_SSL_CONNECT_ERROR;
}
/* Informational message */
infof(data, "SSL reusing session ID");
else
infof(data, "SSL reusing session ID");
}
/* If there is not one, then let's make one up! This has to be done prior
to starting the handshake. */
@ -1363,15 +1363,17 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
if(err != noErr) {
Curl_ssl_sessionid_unlock(data);
Curl_ssl_scache_unlock(data);
failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err);
return CURLE_SSL_CONNECT_ERROR;
}
result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
ssl_sessionid, ssl_sessionid_len,
/* This is all a bit weird, as we have not handshaked yet.
* I hope this backend will go away soon. */
result = Curl_ssl_scache_add_obj(cf, data, connssl->peer.scache_key,
(void *)ssl_sessionid,
sectransp_session_free);
Curl_ssl_sessionid_unlock(data);
Curl_ssl_scache_unlock(data);
if(result)
return result;
}

View file

@ -55,6 +55,7 @@
#include "vtls.h" /* generic SSL protos etc */
#include "vtls_int.h"
#include "vtls_scache.h"
#include "openssl.h" /* OpenSSL versions */
#include "gtls.h" /* GnuTLS versions */
@ -74,6 +75,7 @@
#include "multiif.h"
#include "timeval.h"
#include "curl_md5.h"
#include "curl_sha256.h"
#include "warnless.h"
#include "curl_base64.h"
#include "curl_printf.h"
@ -88,11 +90,6 @@
#include "memdebug.h"
/* convenience macro to check if this handle is using a shared SSL session */
#define SSLSESSION_SHARED(data) (data->share && \
(data->share->specifier & \
(1<<CURL_LOCK_DATA_SSL_SESSION)))
#define CLONE_STRING(var) \
do { \
if(source->var) { \
@ -465,9 +462,10 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data,
if(!ctx)
return NULL;
ctx->ssl_impl = Curl_ssl;
ctx->alpn = alpn;
Curl_bufq_init2(&ctx->earlydata, CURL_SSL_EARLY_MAX, 1, BUFQ_OPT_NO_SPARES);
ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data);
ctx->backend = calloc(1, ctx->ssl_impl->sizeof_ssl_backend_data);
if(!ctx->backend) {
free(ctx);
return NULL;
@ -478,7 +476,7 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data,
static void cf_ctx_free(struct ssl_connect_data *ctx)
{
if(ctx) {
Curl_safefree(ctx->alpn_negotiated);
Curl_safefree(ctx->negotiated.alpn);
Curl_bufq_free(&ctx->earlydata);
free(ctx->backend);
free(ctx);
@ -496,7 +494,7 @@ static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data)
/* mark this is being ssl-enabled from here on. */
connssl->state = ssl_connection_negotiating;
result = Curl_ssl->connect_blocking(cf, data);
result = connssl->ssl_impl->connect_blocking(cf, data);
if(!result) {
DEBUGASSERT(connssl->state == ssl_connection_complete);
@ -509,275 +507,13 @@ static CURLcode
ssl_connect_nonblocking(struct Curl_cfilter *cf, struct Curl_easy *data,
bool *done)
{
struct ssl_connect_data *connssl = cf->ctx;
if(!ssl_prefs_check(data))
return CURLE_SSL_CONNECT_ERROR;
/* mark this is being ssl requested from here on. */
return Curl_ssl->connect_nonblocking(cf, data, done);
}
/*
* Lock shared SSL session data
*/
void Curl_ssl_sessionid_lock(struct Curl_easy *data)
{
if(SSLSESSION_SHARED(data))
Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
}
/*
* Unlock shared SSL session data
*/
void Curl_ssl_sessionid_unlock(struct Curl_easy *data)
{
if(SSLSESSION_SHARED(data))
Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
}
/*
* Check if there is a session ID for the given connection in the cache, and if
* there is one suitable, it is provided. Returns TRUE when no entry matched.
*/
bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
struct Curl_easy *data,
const struct ssl_peer *peer,
void **ssl_sessionid,
size_t *idsize, /* set 0 if unknown */
char **palpn)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
struct Curl_ssl_session *check;
size_t i;
long *general_age;
bool no_match = TRUE;
*ssl_sessionid = NULL;
if(palpn)
*palpn = NULL;
if(!ssl_config)
return TRUE;
DEBUGASSERT(ssl_config->primary.cache_session);
if(!ssl_config->primary.cache_session || !data->state.session)
/* session ID reuse is disabled or the session cache has not been
setup */
return TRUE;
/* Lock if shared */
if(SSLSESSION_SHARED(data))
general_age = &data->share->sessionage;
else
general_age = &data->state.sessionage;
for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
check = &data->state.session[i];
if(!check->sessionid)
/* not session ID means blank entry */
continue;
if(strcasecompare(peer->hostname, check->name) &&
((!cf->conn->bits.conn_to_host && !check->conn_to_host) ||
(cf->conn->bits.conn_to_host && check->conn_to_host &&
strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) &&
((!cf->conn->bits.conn_to_port && check->conn_to_port == -1) ||
(cf->conn->bits.conn_to_port && check->conn_to_port != -1 &&
cf->conn->conn_to_port == check->conn_to_port)) &&
(peer->port == check->remote_port) &&
(peer->transport == check->transport) &&
strcasecompare(cf->conn->handler->scheme, check->scheme) &&
match_ssl_primary_config(data, conn_config, &check->ssl_config)) {
/* yes, we have a session ID! */
(*general_age)++; /* increase general age */
check->age = *general_age; /* set this as used in this age */
*ssl_sessionid = check->sessionid;
if(idsize)
*idsize = check->idsize;
if(palpn)
*palpn = check->alpn;
no_match = FALSE;
break;
}
}
CURL_TRC_CF(data, cf, "%s cached session ID for %s://%s:%d",
no_match ? "No" : "Found",
cf->conn->handler->scheme, peer->hostname, peer->port);
return no_match;
}
/*
* Kill a single session ID entry in the cache.
*/
void Curl_ssl_kill_session(struct Curl_ssl_session *session)
{
if(session->sessionid) {
/* defensive check */
/* free the ID the SSL-layer specific way */
session->sessionid_free(session->sessionid, session->idsize);
session->sessionid = NULL;
session->sessionid_free = NULL;
session->age = 0; /* fresh */
free_primary_ssl_config(&session->ssl_config);
Curl_safefree(session->name);
Curl_safefree(session->conn_to_host);
Curl_safefree(session->alpn);
}
}
/*
* Delete the given session ID from the cache.
*/
void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid)
{
size_t i;
for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
struct Curl_ssl_session *check = &data->state.session[i];
if(check->sessionid == ssl_sessionid) {
Curl_ssl_kill_session(check);
break;
}
}
}
CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf,
struct Curl_easy *data,
const struct ssl_peer *peer,
const char *alpn,
void *ssl_sessionid,
size_t idsize,
Curl_ssl_sessionid_dtor *sessionid_free_cb)
{
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
size_t i;
struct Curl_ssl_session *store;
long oldest_age;
char *clone_host = NULL;
char *clone_conn_to_host = NULL;
char *clone_alpn = NULL;
int conn_to_port;
long *general_age;
void *old_sessionid;
size_t old_size;
CURLcode result = CURLE_OUT_OF_MEMORY;
DEBUGASSERT(ssl_sessionid);
DEBUGASSERT(sessionid_free_cb);
if(!data->state.session) {
sessionid_free_cb(ssl_sessionid, idsize);
return CURLE_OK;
}
if(!Curl_ssl_getsessionid(cf, data, peer, &old_sessionid, &old_size, NULL)) {
if((old_size == idsize) &&
((old_sessionid == ssl_sessionid) ||
(idsize && !memcmp(old_sessionid, ssl_sessionid, idsize)))) {
/* the very same */
sessionid_free_cb(ssl_sessionid, idsize);
return CURLE_OK;
}
Curl_ssl_delsessionid(data, old_sessionid);
}
store = &data->state.session[0];
oldest_age = data->state.session[0].age; /* zero if unused */
DEBUGASSERT(ssl_config->primary.cache_session);
(void)ssl_config;
clone_host = strdup(peer->hostname);
if(!clone_host)
goto out;
if(cf->conn->bits.conn_to_host) {
clone_conn_to_host = strdup(cf->conn->conn_to_host.name);
if(!clone_conn_to_host)
goto out;
}
clone_alpn = alpn ? strdup(alpn) : NULL;
if(alpn && !clone_alpn)
goto out;
if(cf->conn->bits.conn_to_port)
conn_to_port = cf->conn->conn_to_port;
else
conn_to_port = -1;
/* Now we should add the session ID and the hostname to the cache, (remove
the oldest if necessary) */
/* If using shared SSL session, lock! */
if(SSLSESSION_SHARED(data)) {
general_age = &data->share->sessionage;
}
else {
general_age = &data->state.sessionage;
}
/* find an empty slot for us, or find the oldest */
for(i = 1; (i < data->set.general_ssl.max_ssl_sessions) &&
data->state.session[i].sessionid; i++) {
if(data->state.session[i].age < oldest_age) {
oldest_age = data->state.session[i].age;
store = &data->state.session[i];
}
}
if(i == data->set.general_ssl.max_ssl_sessions)
/* cache is full, we must "kill" the oldest entry! */
Curl_ssl_kill_session(store);
else
store = &data->state.session[i]; /* use this slot */
/* now init the session struct wisely */
if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) {
free_primary_ssl_config(&store->ssl_config);
store->sessionid = NULL; /* let caller free sessionid */
goto out;
}
store->sessionid = ssl_sessionid;
store->idsize = idsize;
store->sessionid_free = sessionid_free_cb;
store->age = *general_age; /* set current age */
/* free it if there is one already present */
free(store->name);
free(store->conn_to_host);
store->name = clone_host; /* clone hostname */
clone_host = NULL;
store->conn_to_host = clone_conn_to_host; /* clone connect to hostname */
clone_conn_to_host = NULL;
store->conn_to_port = conn_to_port; /* connect to port number */
store->alpn = clone_alpn;
clone_alpn = NULL;
/* port number */
store->remote_port = peer->port;
store->scheme = cf->conn->handler->scheme;
store->transport = peer->transport;
result = CURLE_OK;
out:
free(clone_host);
free(clone_conn_to_host);
free(clone_alpn);
if(result) {
failf(data, "Failed to add Session ID to cache for %s://%s:%d [%s]",
store->scheme, store->name, store->remote_port,
Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server");
sessionid_free_cb(ssl_sessionid, idsize);
return result;
}
CURL_TRC_CF(data, cf, "Added Session ID to cache for %s://%s:%d [%s]",
store->scheme, store->name, store->remote_port,
Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server");
return CURLE_OK;
return connssl->ssl_impl->connect_nonblocking(cf, data, done);
}
CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
@ -791,14 +527,9 @@ CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
void Curl_ssl_close_all(struct Curl_easy *data)
{
/* kill the session ID cache if not shared */
if(data->state.session && !SSLSESSION_SHARED(data)) {
size_t i;
for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++)
/* the single-killer function handles empty table slots */
Curl_ssl_kill_session(&data->state.session[i]);
/* free the cache data */
Curl_safefree(data->state.session);
if(data->state.ssl_scache && !CURL_SHARE_ssl_scache(data)) {
Curl_ssl_scache_destroy(data->state.ssl_scache);
data->state.ssl_scache = NULL;
}
if(Curl_ssl->close_all)
@ -853,29 +584,6 @@ struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data)
return NULL;
}
/*
* This sets up a session ID cache to the specified size. Make sure this code
* is agnostic to what underlying SSL technology we use.
*/
CURLcode Curl_ssl_initsessions(struct Curl_easy *data, size_t amount)
{
struct Curl_ssl_session *session;
if(data->state.session)
/* this is just a precaution to prevent multiple inits */
return CURLE_OK;
session = calloc(amount, sizeof(struct Curl_ssl_session));
if(!session)
return CURLE_OUT_OF_MEMORY;
/* store the info in the SSL section */
data->set.general_ssl.max_ssl_sessions = amount;
data->state.session = session;
data->state.sessionage = 1; /* this is brand new */
return CURLE_OK;
}
static size_t multissl_version(char *buffer, size_t size);
void Curl_ssl_version(char *buffer, size_t size)
@ -1494,11 +1202,12 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
void Curl_ssl_peer_cleanup(struct ssl_peer *peer)
{
Curl_safefree(peer->sni);
if(peer->dispname != peer->hostname)
free(peer->dispname);
free(peer->sni);
free(peer->hostname);
peer->hostname = peer->sni = peer->dispname = NULL;
peer->dispname = NULL;
Curl_safefree(peer->hostname);
Curl_safefree(peer->scache_key);
peer->type = CURL_SSL_PEER_DNS;
}
@ -1506,7 +1215,7 @@ static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
if(connssl) {
Curl_ssl->close(cf, data);
connssl->ssl_impl->close(cf, data);
connssl->state = ssl_connection_none;
Curl_ssl_peer_cleanup(&connssl->peer);
}
@ -1532,7 +1241,9 @@ static ssl_peer_type get_peer_type(const char *hostname)
return CURL_SSL_PEER_DNS;
}
CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf,
CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
struct Curl_cfilter *cf,
const char *tls_id,
int transport)
{
const char *ehostname, *edispname;
@ -1594,7 +1305,8 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf,
peer->sni[len] = 0;
}
}
result = CURLE_OK;
result = Curl_ssl_peer_key_make(cf, peer, tls_id, &peer->scache_key);
out:
if(result)
@ -1657,7 +1369,9 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
*done = FALSE;
if(!connssl->peer.hostname) {
result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP);
char tls_id[80];
connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1);
result = Curl_ssl_peer_init(&connssl->peer, cf, tls_id, TRNSPRT_TCP);
if(result)
goto out;
}
@ -1686,11 +1400,13 @@ out:
static bool ssl_cf_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
bool result;
CF_DATA_SAVE(save, cf, data);
if(Curl_ssl->data_pending && Curl_ssl->data_pending(cf, data))
if(connssl->ssl_impl->data_pending &&
connssl->ssl_impl->data_pending(cf, data))
result = TRUE;
else
result = cf->next->cft->has_data_pending(cf->next, data);
@ -1702,6 +1418,7 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf,
struct Curl_easy *data, const void *buf, size_t len,
bool eos, CURLcode *err)
{
struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
ssize_t nwritten = 0;
@ -1710,7 +1427,7 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf,
*err = CURLE_OK;
if(len > 0) {
CF_DATA_SAVE(save, cf, data);
nwritten = Curl_ssl->send_plain(cf, data, buf, len, err);
nwritten = connssl->ssl_impl->send_plain(cf, data, buf, len, err);
CF_DATA_RESTORE(cf, save);
}
return nwritten;
@ -1720,12 +1437,13 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
struct Curl_easy *data, char *buf, size_t len,
CURLcode *err)
{
struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
ssize_t nread;
CF_DATA_SAVE(save, cf, data);
*err = CURLE_OK;
nread = Curl_ssl->recv_plain(cf, data, buf, len, err);
nread = connssl->ssl_impl->recv_plain(cf, data, buf, len, err);
if(nread > 0) {
DEBUGASSERT((size_t)nread <= len);
}
@ -1743,6 +1461,7 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
struct ssl_connect_data *connssl = cf->ctx;
CURLcode result = CURLE_OK;
*done = TRUE;
@ -1750,7 +1469,7 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf,
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
result = Curl_ssl->shut_down(cf, data, TRUE, done);
result = connssl->ssl_impl->shut_down(cf, data, TRUE, done);
CURL_TRC_CF(data, cf, "cf_shutdown -> %d, done=%d", result, *done);
CF_DATA_RESTORE(cf, save);
cf->shutdown = (result || *done);
@ -1762,10 +1481,11 @@ static void ssl_cf_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct easy_pollset *ps)
{
struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
Curl_ssl->adjust_pollset(cf, data, ps);
connssl->ssl_impl->adjust_pollset(cf, data, ps);
CF_DATA_RESTORE(cf, save);
}
@ -1971,9 +1691,10 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
/* get first SSL filter in chain, if any is present */
cf = get_ssl_filter(data->conn->cfilter[sockindex]);
if(cf) {
struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
result = Curl_ssl->get_internals(cf->ctx, info);
result = connssl->ssl_impl->get_internals(cf->ctx, info);
CF_DATA_RESTORE(cf, save);
}
}
@ -2006,7 +1727,7 @@ static CURLcode vtls_shutdown_blocking(struct Curl_cfilter *cf,
return CURLE_OPERATION_TIMEDOUT;
}
result = Curl_ssl->shut_down(cf, data, send_shutdown, done);
result = connssl->ssl_impl->shut_down(cf, data, send_shutdown, done);
if(result ||*done)
goto out;
@ -2158,28 +1879,28 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
#endif
;
if(connssl->alpn_negotiated) {
if(connssl->negotiated.alpn) {
/* When we ask for a specific ALPN protocol, we need the confirmation
* of it by the server, as we have installed protocol handler and
* connection filter chain for exactly this protocol. */
if(!proto_len) {
failf(data, "ALPN: asked for '%s' from previous session, "
"but server did not confirm it. Refusing to continue.",
connssl->alpn_negotiated);
connssl->negotiated.alpn);
result = CURLE_SSL_CONNECT_ERROR;
goto out;
}
else if((strlen(connssl->alpn_negotiated) != proto_len) ||
memcmp(connssl->alpn_negotiated, proto, proto_len)) {
else if((strlen(connssl->negotiated.alpn) != proto_len) ||
memcmp(connssl->negotiated.alpn, proto, proto_len)) {
failf(data, "ALPN: asked for '%s' from previous session, but server "
"selected '%.*s'. Refusing to continue.",
connssl->alpn_negotiated, (int)proto_len, proto);
connssl->negotiated.alpn, (int)proto_len, proto);
result = CURLE_SSL_CONNECT_ERROR;
goto out;
}
/* ALPN is exactly what we asked for, done. */
infof(data, "ALPN: server confirmed to use '%s'",
connssl->alpn_negotiated);
connssl->negotiated.alpn);
goto out;
}
@ -2190,11 +1911,11 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
result = CURLE_SSL_CONNECT_ERROR;
goto out;
}
connssl->alpn_negotiated = malloc(proto_len + 1);
if(!connssl->alpn_negotiated)
connssl->negotiated.alpn = malloc(proto_len + 1);
if(!connssl->negotiated.alpn)
return CURLE_OUT_OF_MEMORY;
memcpy(connssl->alpn_negotiated, proto, proto_len);
connssl->alpn_negotiated[proto_len] = 0;
memcpy(connssl->negotiated.alpn, proto, proto_len);
connssl->negotiated.alpn[proto_len] = 0;
}
if(proto && proto_len) {

View file

@ -28,7 +28,9 @@
struct connectdata;
struct ssl_config_data;
struct ssl_primary_config;
struct Curl_ssl_session;
struct Curl_cfilter;
struct Curl_easy;
struct dynbuf;
#define SSLSUPP_CA_PATH (1<<0) /* supports CAPATH */
#define SSLSUPP_CERTINFO (1<<1) /* supports CURLOPT_CERTINFO */
@ -63,9 +65,31 @@ struct Curl_ssl_session;
#define VTLS_INFOF_ALPN_DEFERRED \
"ALPN: deferred handshake for early data using '%.*s'."
/* Curl_multi SSL backend-specific data; declared differently by each SSL
backend */
struct Curl_cfilter;
/* IETF defined version numbers used in TLS protocol negotiation */
#define CURL_IETF_PROTO_UNKNOWN 0x0
#define CURL_IETF_PROTO_SSL3 0x0300
#define CURL_IETF_PROTO_TLS1 0x0301
#define CURL_IETF_PROTO_TLS1_1 0x0302
#define CURL_IETF_PROTO_TLS1_2 0x0303
#define CURL_IETF_PROTO_TLS1_3 0x0304
#define CURL_IETF_PROTO_DTLS1 0xFEFF
#define CURL_IETF_PROTO_DTLS1_2 0xFEFD
typedef enum {
CURL_SSL_PEER_DNS,
CURL_SSL_PEER_IPV4,
CURL_SSL_PEER_IPV6
} ssl_peer_type;
struct ssl_peer {
char *hostname; /* hostname for verification */
char *dispname; /* display version of hostname */
char *sni; /* SNI version of hostname or NULL if not usable */
char *scache_key; /* for lookups in session cache */
ssl_peer_type type; /* type of the peer information */
int port; /* port we are talking to */
int transport; /* one of TRNSPRT_* defines */
};
CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
const curl_ssl_backend ***avail);
@ -121,7 +145,9 @@ void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy);
* Init SSL peer information for filter. Can be called repeatedly.
*/
CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
struct Curl_cfilter *cf, int transport);
struct Curl_cfilter *cf,
const char *tls_id,
int transport);
/**
* Free all allocated data and reset peer information.
*/
@ -138,8 +164,6 @@ CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine);
CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data);
struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data);
/* init the SSL session ID cache */
CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t);
void Curl_ssl_version(char *buffer, size_t size);
/* Certificate information list handling. */
@ -155,33 +179,6 @@ CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data, int certnum,
/* Functions to be used by SSL library adaptation functions */
/* Lock session cache mutex.
* Call this before calling other Curl_ssl_*session* functions
* Caller should unlock this mutex as soon as possible, as it may block
* other SSL connection from making progress.
* The purpose of explicitly locking SSL session cache data is to allow
* individual SSL engines to manage session lifetime in their specific way.
*/
void Curl_ssl_sessionid_lock(struct Curl_easy *data);
/* Unlock session cache mutex */
void Curl_ssl_sessionid_unlock(struct Curl_easy *data);
/* Kill a single session ID entry in the cache
* Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
* This will call engine-specific curlssl_session_free function, which must
* take sessionid object ownership from sessionid cache
* (e.g. decrement refcount).
*/
void Curl_ssl_kill_session(struct Curl_ssl_session *session);
/* delete a session from the cache
* Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
* This will call engine-specific curlssl_session_free function, which must
* take sessionid object ownership from sessionid cache
* (e.g. decrement refcount).
*/
void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid);
/* get N random bytes into the buffer */
CURLcode Curl_ssl_random(struct Curl_easy *data, unsigned char *buffer,
size_t length);
@ -273,9 +270,7 @@ extern struct Curl_cftype Curl_cft_ssl_proxy;
#define Curl_ssl_set_engine(x,y) CURLE_NOT_BUILT_IN
#define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN
#define Curl_ssl_engines_list(x) NULL
#define Curl_ssl_initsessions(x,y) CURLE_OK
#define Curl_ssl_free_certinfo(x) Curl_nop_stmt
#define Curl_ssl_kill_session(x) Curl_nop_stmt
#define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN)
#define Curl_ssl_cert_status_request() FALSE
#define Curl_ssl_false_start() FALSE

View file

@ -26,9 +26,11 @@
#include "curl_setup.h"
#include "cfilters.h"
#include "urldata.h"
#include "vtls.h"
#ifdef USE_SSL
struct Curl_ssl;
struct ssl_connect_data;
/* see https://www.iana.org/assignments/tls-extensiontype-values/ */
@ -103,12 +105,15 @@ typedef enum {
/* Information in each SSL cfilter context: cf->ctx */
struct ssl_connect_data {
struct ssl_peer peer;
const struct Curl_ssl *ssl_impl; /* TLS backend for this filter */
struct ssl_peer peer; /* peer the filter talks to */
const struct alpn_spec *alpn; /* ALPN to use or NULL for none */
void *backend; /* vtls backend specific props */
struct cf_call_data call_data; /* data handle used in current call */
struct curltime handshake_done; /* time when handshake finished */
char *alpn_negotiated; /* negotiated ALPN value or NULL */
struct {
char *alpn; /* ALPN value or NULL */
} negotiated;
struct bufq earlydata; /* earlydata to be send to peer */
size_t earlydata_max; /* max earlydata allowed by peer */
size_t earlydata_skip; /* sending bytes to skip when earlydata
@ -193,43 +198,6 @@ void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
*/
bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf);
/* extract a session ID
* Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
* Caller must make sure that the ownership of returned sessionid object
* is properly taken (e.g. its refcount is incremented
* under sessionid mutex).
* @param cf the connection filter wanting to use it
* @param data the transfer involved
* @param peer the peer the filter wants to talk to
* @param sessionid on return the TLS session
* @param idsize on return the size of the TLS session data
* @param palpn on return the ALPN string used by the session,
* set to NULL when not interested
*/
bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
struct Curl_easy *data,
const struct ssl_peer *peer,
void **ssl_sessionid,
size_t *idsize, /* set 0 if unknown */
char **palpn);
/* Set a TLS session ID for `peer`. Replaces an existing session ID if
* not already the same.
* Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
* Call takes ownership of `ssl_sessionid`, using `sessionid_free_cb`
* to deallocate it. Is called in all outcomes, either right away or
* later when the session cache is cleaned up.
* Caller must ensure that it has properly shared ownership of this sessionid
* object with cache (e.g. incrementing refcount on success)
*/
CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf,
struct Curl_easy *data,
const struct ssl_peer *peer,
const char *alpn,
void *sessionid,
size_t sessionid_size,
Curl_ssl_sessionid_dtor *sessionid_free_cb);
#endif /* USE_SSL */
#endif /* HEADER_CURL_VTLS_INT_H */

876
lib/vtls/vtls_scache.c Normal file
View file

@ -0,0 +1,876 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/* This file is for implementing all "generic" SSL functions that all libcurl
internals should use. It is then responsible for calling the proper
"backend" function.
SSL-functions in libcurl should call functions in this source file, and not
to any specific SSL-layer.
Curl_ssl_ - prefix for generic ones
Note that this source code uses the functions of the configured SSL
backend via the global Curl_ssl instance.
"SSL/TLS Strong Encryption: An Introduction"
https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html
*/
#include "curl_setup.h"
#ifdef USE_SSL
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include "urldata.h"
#include "cfilters.h"
#include "vtls.h" /* generic SSL protos etc */
#include "vtls_int.h"
#include "vtls_scache.h"
#include "strcase.h"
#include "url.h"
#include "llist.h"
#include "share.h"
#include "curl_trc.h"
#include "curl_sha256.h"
#include "warnless.h"
#include "curl_printf.h"
#include "strdup.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/* a peer+tls-config we cache sessions for */
struct Curl_ssl_scache_peer {
char *ssl_peer_key; /* id for peer + relevant TLS configuration */
char *clientcert;
char *srp_username;
char *srp_password;
struct Curl_llist sessions;
void *sobj; /* object instance or NULL */
Curl_ssl_scache_obj_dtor *sobj_free; /* free `sobj` callback */
unsigned char key_salt[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
unsigned char key_hmac[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
size_t max_sessions;
long age; /* just a number, the higher the more recent */
BIT(hmac_set); /* if key_salt and key_hmac are present */
};
struct Curl_ssl_scache {
struct Curl_ssl_scache_peer *peers;
size_t peer_count;
int default_lifetime_secs;
long age;
};
static void cf_ssl_scache_clear_session(struct Curl_ssl_session *s)
{
if(s->sdata) {
free((void *)s->sdata);
s->sdata = NULL;
}
s->sdata_len = 0;
s->ietf_tls_id = 0;
s->time_received = 0;
s->lifetime_secs = 0;
Curl_safefree(s->alpn);
}
static void cf_ssl_scache_sesssion_ldestroy(void *udata, void *s)
{
(void)udata;
cf_ssl_scache_clear_session(s);
free(s);
}
CURLcode
Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len,
int ietf_tls_id, const char *alpn,
curl_off_t time_received, long lifetime_secs,
struct Curl_ssl_session **psession)
{
struct Curl_ssl_session *s;
if(!sdata || !sdata_len) {
free(sdata);
return CURLE_BAD_FUNCTION_ARGUMENT;
}
*psession = NULL;
s = calloc(1, sizeof(*s));
if(!s) {
free(sdata);
return CURLE_OUT_OF_MEMORY;
}
s->ietf_tls_id = ietf_tls_id;
s->time_received = time_received;
if(lifetime_secs < 0)
lifetime_secs = -1; /* unknown */
else if((s->ietf_tls_id == CURL_IETF_PROTO_TLS1_3) &&
(lifetime_secs > CURL_SCACHE_MAX_13_LIFETIME_SEC))
lifetime_secs = CURL_SCACHE_MAX_13_LIFETIME_SEC;
else if(lifetime_secs > CURL_SCACHE_MAX_12_LIFETIME_SEC)
lifetime_secs = CURL_SCACHE_MAX_12_LIFETIME_SEC;
s->lifetime_secs = (int)lifetime_secs;
s->sdata = sdata;
s->sdata_len = sdata_len;
if(alpn) {
s->alpn = strdup(alpn);
if(!s->alpn) {
cf_ssl_scache_sesssion_ldestroy(NULL, s);
return CURLE_OUT_OF_MEMORY;
}
}
*psession = s;
return CURLE_OK;
}
void Curl_ssl_session_destroy(struct Curl_ssl_session *s)
{
if(s) {
/* if in the list, the list destructor takes care of it */
if(Curl_node_llist(&s->list))
Curl_node_remove(&s->list);
else {
cf_ssl_scache_sesssion_ldestroy(NULL, s);
}
}
}
static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer)
{
Curl_llist_destroy(&peer->sessions, NULL);
if(peer->sobj) {
DEBUGASSERT(peer->sobj_free);
if(peer->sobj_free)
peer->sobj_free(peer->sobj);
peer->sobj = NULL;
}
peer->sobj_free = NULL;
Curl_safefree(peer->clientcert);
#ifdef USE_TLS_SRP
Curl_safefree(peer->srp_username);
Curl_safefree(peer->srp_password);
#endif
Curl_safefree(peer->ssl_peer_key);
peer->age = 0;
peer->hmac_set = FALSE;
}
static void cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer *peer,
void *sobj,
Curl_ssl_scache_obj_dtor *sobj_free)
{
DEBUGASSERT(peer);
if(peer->sobj_free) {
peer->sobj_free(peer->sobj);
}
peer->sobj = sobj;
peer->sobj_free = sobj_free;
}
static CURLcode cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer,
const char *ssl_peer_key,
const char *clientcert,
const char *srp_username,
const char *srp_password)
{
CURLcode result = CURLE_OUT_OF_MEMORY;
DEBUGASSERT(!peer->ssl_peer_key);
peer->ssl_peer_key = strdup(ssl_peer_key);
if(!peer->ssl_peer_key)
goto out;
if(clientcert) {
peer->clientcert = strdup(clientcert);
if(!peer->clientcert)
goto out;
}
if(srp_username) {
peer->srp_username = strdup(srp_username);
if(!peer->srp_username)
goto out;
}
if(srp_password) {
peer->srp_password = strdup(srp_password);
if(!peer->srp_password)
goto out;
}
result = CURLE_OK;
out:
if(result)
cf_ssl_scache_clear_peer(peer);
return result;
}
static void cf_scache_session_remove(struct Curl_ssl_scache_peer *peer,
struct Curl_ssl_session *s)
{
(void)peer;
DEBUGASSERT(Curl_node_llist(&s->list) == &peer->sessions);
Curl_ssl_session_destroy(s);
}
static bool cf_scache_session_expired(struct Curl_ssl_session *s,
curl_off_t now)
{
return (s->lifetime_secs > 0 &&
(s->time_received + s->lifetime_secs) < now);
}
static void cf_scache_peer_remove_expired(struct Curl_ssl_scache_peer *peer,
curl_off_t now)
{
struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
while(n) {
struct Curl_ssl_session *s = Curl_node_elem(n);
n = Curl_node_next(n);
if(cf_scache_session_expired(s, now))
cf_scache_session_remove(peer, s);
}
}
static void cf_scache_peer_remove_non13(struct Curl_ssl_scache_peer *peer)
{
struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
while(n) {
struct Curl_ssl_session *s = Curl_node_elem(n);
n = Curl_node_next(n);
if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3)
cf_scache_session_remove(peer, s);
}
}
CURLcode Curl_ssl_scache_create(size_t max_peers,
size_t max_sessions_per_peer,
struct Curl_ssl_scache **pscache)
{
struct Curl_ssl_scache *scache;
struct Curl_ssl_scache_peer *peers;
size_t i;
*pscache = NULL;
peers = calloc(max_peers, sizeof(*peers));
if(!peers)
return CURLE_OUT_OF_MEMORY;
scache = calloc(1, sizeof(*scache));
if(!scache) {
free(peers);
return CURLE_OUT_OF_MEMORY;
}
scache->default_lifetime_secs = (24*60*60); /* 1 day */
scache->peer_count = max_peers;
scache->peers = peers;
scache->age = 1;
for(i = 0; i < scache->peer_count; ++i) {
scache->peers[i].max_sessions = max_sessions_per_peer;
Curl_llist_init(&scache->peers[i].sessions,
cf_ssl_scache_sesssion_ldestroy);
}
*pscache = scache;
return CURLE_OK;
}
void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache)
{
if(scache) {
size_t i;
for(i = 0; i < scache->peer_count; ++i) {
cf_ssl_scache_clear_peer(&scache->peers[i]);
}
free(scache->peers);
free(scache);
}
}
/* Lock shared SSL session data */
void Curl_ssl_scache_lock(struct Curl_easy *data)
{
if(CURL_SHARE_ssl_scache(data))
Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
}
/* Unlock shared SSL session data */
void Curl_ssl_scache_unlock(struct Curl_easy *data)
{
if(CURL_SHARE_ssl_scache(data))
Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
}
static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf,
const char *name,
char *path)
{
if(path && path[0]) {
/* We try to add absolute paths, so that the session key can stay
* valid when used in another process with different CWD. However,
* when a path does not exist, this does not work. Then, we add
* the path as is. */
#ifdef _WIN32
char abspath[_MAX_PATH];
if(_fullpath(abspath, path, _MAX_PATH))
return Curl_dyn_addf(buf, ":%s-%s", name, abspath);
#else
if(path[0] != '/') {
char *abspath = realpath(path, NULL);
if(abspath) {
CURLcode r = Curl_dyn_addf(buf, ":%s-%s", name, abspath);
(free)(abspath); /* allocated by libc, free without memdebug */
return r;
}
}
#endif
return Curl_dyn_addf(buf, ":%s-%s", name, path);
}
return CURLE_OK;
}
static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf,
const char *name,
struct curl_blob *blob)
{
CURLcode r = CURLE_OK;
if(blob && blob->len) {
unsigned char hash[CURL_SHA256_DIGEST_LENGTH];
size_t i;
r = Curl_dyn_addf(buf, ":%s-", name);
if(r)
goto out;
r = Curl_sha256it(hash, blob->data, blob->len);
if(r)
goto out;
for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) {
r = Curl_dyn_addf(buf, "%02x", hash[i]);
if(r)
goto out;
}
}
out:
return r;
}
CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
const struct ssl_peer *peer,
const char *tls_id,
char **ppeer_key)
{
struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
struct dynbuf buf;
size_t key_len;
CURLcode r;
*ppeer_key = NULL;
Curl_dyn_init(&buf, 10 * 1024);
r = Curl_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port);
if(r)
goto out;
switch(peer->transport) {
case TRNSPRT_TCP:
break;
case TRNSPRT_UDP:
r = Curl_dyn_add(&buf, ":UDP");
break;
case TRNSPRT_QUIC:
r = Curl_dyn_add(&buf, ":QUIC");
break;
case TRNSPRT_UNIX:
r = Curl_dyn_add(&buf, ":UNIX");
break;
default:
r = Curl_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport);
break;
}
if(r)
goto out;
if(!ssl->verifypeer) {
r = Curl_dyn_add(&buf, ":NO-VRFY-PEER");
if(r)
goto out;
}
if(!ssl->verifyhost) {
r = Curl_dyn_add(&buf, ":NO-VRFY-HOST");
if(r)
goto out;
}
if(ssl->verifystatus) {
r = Curl_dyn_add(&buf, ":VRFY-STATUS");
if(r)
goto out;
}
if(!ssl->verifypeer || !ssl->verifyhost) {
if(cf->conn->bits.conn_to_host) {
r = Curl_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);
if(r)
goto out;
}
if(cf->conn->bits.conn_to_port) {
r = Curl_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port);
if(r)
goto out;
}
}
if(ssl->version || ssl->version_max) {
r = Curl_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version,
(ssl->version_max >> 16));
if(r)
goto out;
}
if(ssl->ssl_options) {
r = Curl_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options);
if(r)
goto out;
}
if(ssl->cipher_list) {
r = Curl_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list);
if(r)
goto out;
}
if(ssl->cipher_list13) {
r = Curl_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13);
if(r)
goto out;
}
if(ssl->curves) {
r = Curl_dyn_addf(&buf, ":CURVES-%s", ssl->curves);
if(r)
goto out;
}
if(ssl->verifypeer) {
r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile);
if(r)
goto out;
r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath);
if(r)
goto out;
r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile);
if(r)
goto out;
r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert);
if(r)
goto out;
if(ssl->cert_blob) {
r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob);
if(r)
goto out;
}
if(ssl->ca_info_blob) {
r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob);
if(r)
goto out;
}
if(ssl->issuercert_blob) {
r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob);
if(r)
goto out;
}
}
if(ssl->pinned_key && ssl->pinned_key[0]) {
r = Curl_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key);
if(r)
goto out;
}
if(ssl->clientcert && ssl->clientcert[0]) {
r = Curl_dyn_add(&buf, ":CCERT");
if(r)
goto out;
}
#ifdef USE_TLS_SRP
if(ssl->username || ssl->password) {
r = Curl_dyn_add(&buf, ":SRP-AUTH");
if(r)
goto out;
}
#endif
if(!tls_id || !tls_id[0]) {
r = CURLE_FAILED_INIT;
goto out;
}
r = Curl_dyn_addf(&buf, ":IMPL-%s", tls_id);
if(r)
goto out;
*ppeer_key = Curl_dyn_take(&buf, &key_len);
/* we just added printable char, and dynbuf always 0 terminates,
* no need to track length */
out:
Curl_dyn_free(&buf);
return r;
}
static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer,
struct ssl_primary_config *conn_config)
{
if(!Curl_safecmp(peer->clientcert, conn_config->clientcert))
return FALSE;
#ifdef USE_TLS_SRP
if(Curl_timestrcmp(peer->srp_username, conn_config->username) ||
Curl_timestrcmp(peer->srp_password, conn_config->password))
return FALSE;
#endif
return TRUE;
}
static CURLcode cf_ssl_find_peer(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct Curl_ssl_scache *scache,
const char *ssl_peer_key,
struct Curl_ssl_scache_peer **ppeer)
{
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);
size_t i, peer_key_len = 0;
CURLcode result = CURLE_OK;
*ppeer = NULL;
if(!ssl_config || !ssl_config->primary.cache_session)
goto out;
/* check for entries with known peer_key */
for(i = 0; scache && i < scache->peer_count; i++) {
if(scache->peers[i].ssl_peer_key &&
strcasecompare(ssl_peer_key, scache->peers[i].ssl_peer_key) &&
cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
/* yes, we have a cached session for this! */
*ppeer = &scache->peers[i];
goto out;
}
}
/* check for entries with HMAC set but no known peer_key */
for(i = 0; scache && i < scache->peer_count; i++) {
if(!scache->peers[i].ssl_peer_key &&
scache->peers[i].hmac_set &&
cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
/* possible entry with unknown peer_key, check hmac */
unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH];
if(!peer_key_len) /* we are lazy */
peer_key_len = strlen(ssl_peer_key);
(void)Curl_hmacit(&Curl_HMAC_SHA256,
scache->peers[i].key_salt,
sizeof(scache->peers[i].key_salt),
(const unsigned char *)ssl_peer_key,
peer_key_len,
my_hmac);
if(!memcmp(scache->peers[i].key_hmac, my_hmac, sizeof(my_hmac))) {
/* remember peer_key for future lookups */
scache->peers[i].ssl_peer_key = strdup(ssl_peer_key);
if(!scache->peers[i].ssl_peer_key) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
*ppeer = &scache->peers[i];
goto out;
}
}
}
out:
if(result)
CURL_TRC_CF(data, cf, "[SACHE] failure finding scache peer: %d", result);
return result;
}
static CURLcode cf_ssl_add_peer(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct Curl_ssl_scache *scache,
const char *ssl_peer_key,
struct Curl_ssl_scache_peer **ppeer)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct Curl_ssl_scache_peer *peer = NULL;
size_t i;
CURLcode result;
*ppeer = NULL;
result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
if(result || !scache->peer_count)
return result;
if(peer) {
*ppeer = peer;
return CURLE_OK;
}
/* not there, find empty or oldest peer */
for(i = 0; i < scache->peer_count; ++i) {
/* free peer entry? */
if(!scache->peers[i].ssl_peer_key && !scache->peers[i].hmac_set) {
peer = &scache->peers[i];
break;
}
/* peer without sessions and obj */
if(!scache->peers[i].sobj &&
!Curl_llist_count(&scache->peers[i].sessions)) {
peer = &scache->peers[i];
break;
}
/* remember "oldest" peer */
if(!peer || (scache->peers[i].age < peer->age)) {
peer = &scache->peers[i];
}
}
DEBUGASSERT(peer);
if(!peer)
return CURLE_OK;
/* clear previous peer and reinit */
cf_ssl_scache_clear_peer(peer);
result = cf_ssl_scache_peer_init(peer, ssl_peer_key,
conn_config->clientcert,
#ifdef USE_TLS_SRP
conn_config->username,
conn_config->password);
#else
NULL, NULL);
#endif
if(result)
goto out;
/* all ready */
*ppeer = peer;
result = CURLE_OK;
out:
if(result) {
cf_ssl_scache_clear_peer(peer);
CURL_TRC_CF(data, cf, "[SACHE] failure adding peer: %d", result);
}
return result;
}
static CURLcode cf_scache_peer_add_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct Curl_ssl_scache *scache,
const char *ssl_peer_key,
struct Curl_ssl_session *s)
{
struct Curl_ssl_scache_peer *peer = NULL;
CURLcode result = CURLE_OUT_OF_MEMORY;
curl_off_t now = (curl_off_t)time(NULL);
if(!scache || !scache->peer_count) {
Curl_ssl_session_destroy(s);
return CURLE_OK;
}
if(!s->time_received)
s->time_received = now;
if(s->lifetime_secs < 0)
s->lifetime_secs = scache->default_lifetime_secs;
if(cf_scache_session_expired(s, now)) {
CURL_TRC_CF(data, cf, "[SCACHE] add, session already expired");
Curl_ssl_session_destroy(s);
return CURLE_OK;
}
result = cf_ssl_add_peer(cf, data, scache, ssl_peer_key, &peer);
if(result || !peer) {
CURL_TRC_CF(data, cf, "[SCACHE] unable to add scache peer: %d", result);
Curl_ssl_session_destroy(s);
goto out;
}
/* A session not from TLSv1.3 replaces all other. */
if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3) {
Curl_llist_destroy(&peer->sessions, NULL);
Curl_llist_append(&peer->sessions, s, &s->list);
}
else {
/* Expire existing, append, trim from head to obey max_sessions */
cf_scache_peer_remove_expired(peer, now);
cf_scache_peer_remove_non13(peer);
Curl_llist_append(&peer->sessions, s, &s->list);
while(Curl_llist_count(&peer->sessions) > peer->max_sessions) {
Curl_node_remove(Curl_llist_head(&peer->sessions));
}
}
out:
if(result) {
failf(data, "[SCACHE] failed to add session for %s, error=%d",
ssl_peer_key, result);
}
else
CURL_TRC_CF(data, cf, "[SCACHE] added session for %s [proto=0x%x, "
"lifetime=%d, alpn=%s], peer has %zu sessions now",
ssl_peer_key, s->ietf_tls_id, s->lifetime_secs, s->alpn,
Curl_llist_count(&peer->sessions));
return result;
}
CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key,
struct Curl_ssl_session *s)
{
struct Curl_ssl_scache *scache = data->state.ssl_scache;
CURLcode result;
Curl_ssl_scache_lock(data);
result = cf_scache_peer_add_session(cf, data, scache, ssl_peer_key, s);
Curl_ssl_scache_unlock(data);
return result;
}
void Curl_ssl_scache_return(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key,
struct Curl_ssl_session *s)
{
/* See RFC 8446 C.4:
* "Clients SHOULD NOT reuse a ticket for multiple connections." */
if(s && s->ietf_tls_id < 0x304)
(void)Curl_ssl_scache_put(cf, data, ssl_peer_key, s);
else
Curl_ssl_session_destroy(s);
}
CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key,
struct Curl_ssl_session **ps)
{
struct Curl_ssl_scache *scache = data->state.ssl_scache;
struct Curl_ssl_scache_peer *peer = NULL;
struct Curl_llist_node *n;
CURLcode result;
*ps = NULL;
if(!scache)
return CURLE_OK;
Curl_ssl_scache_lock(data);
result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
if(!result && peer) {
cf_scache_peer_remove_expired(peer, (curl_off_t)time(NULL));
n = Curl_llist_head(&peer->sessions);
if(n) {
*ps = Curl_node_take_elem(n);
(scache->age)++; /* increase general age */
peer->age = scache->age; /* set this as used in this age */
}
}
Curl_ssl_scache_unlock(data);
CURL_TRC_CF(data, cf, "[SCACHE] %s cached session for '%s'",
*ps ? "Found" : "No", ssl_peer_key);
return result;
}
CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key,
void *sobj,
Curl_ssl_scache_obj_dtor *sobj_free)
{
struct Curl_ssl_scache *scache = data->state.ssl_scache;
struct Curl_ssl_scache_peer *peer = NULL;
CURLcode result;
DEBUGASSERT(sobj);
DEBUGASSERT(sobj_free);
result = cf_ssl_add_peer(cf, data, scache, ssl_peer_key, &peer);
if(result || !peer) {
CURL_TRC_CF(data, cf, "[SCACHE] unable to add scache peer: %d", result);
goto out;
}
cf_ssl_scache_peer_set_obj(peer, sobj, sobj_free);
sobj = NULL; /* peer took ownership */
out:
if(sobj && sobj_free)
sobj_free(sobj);
return result;
}
bool Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key,
void **sobj)
{
struct Curl_ssl_scache *scache = data->state.ssl_scache;
struct Curl_ssl_scache_peer *peer = NULL;
CURLcode result;
*sobj = NULL;
if(!scache)
return FALSE;
result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
if(result)
return FALSE;
if(peer)
*sobj = peer->sobj;
CURL_TRC_CF(data, cf, "[SACHE] %s cached session for '%s'",
*sobj ? "Found" : "No", ssl_peer_key);
return !!*sobj;
}
void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key)
{
struct Curl_ssl_scache *scache = data->state.ssl_scache;
struct Curl_ssl_scache_peer *peer = NULL;
CURLcode result;
(void)cf;
if(!scache)
return;
Curl_ssl_scache_lock(data);
result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
if(!result && peer)
cf_ssl_scache_clear_peer(peer);
Curl_ssl_scache_unlock(data);
}
#endif /* USE_SSL */

196
lib/vtls/vtls_scache.h Normal file
View file

@ -0,0 +1,196 @@
#ifndef HEADER_CURL_VTLS_SCACHE_H
#define HEADER_CURL_VTLS_SCACHE_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "cfilters.h"
#include "urldata.h"
#ifdef USE_SSL
struct Curl_cfilter;
struct Curl_easy;
struct Curl_ssl_scache;
struct Curl_ssl_session;
struct ssl_peer;
/* RFC 8446 (TLSv1.3) restrict lifetime to one week max, for
* other, less secure versions, we restrict it to a day */
#define CURL_SCACHE_MAX_13_LIFETIME_SEC (60*60*24*7)
#define CURL_SCACHE_MAX_12_LIFETIME_SEC (60*60*24)
/* Create a session cache for up to max_peers endpoints with a total
* of up to max_sessions SSL sessions per peer */
CURLcode Curl_ssl_scache_create(size_t max_peers,
size_t max_sessions_per_peer,
struct Curl_ssl_scache **pscache);
void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache);
/* Create a key from peer and TLS configuration information that is
* unique for how the connection filter wants to establish a TLS
* connection to the peer.
* If the filter is a TLS proxy filter, it will use the proxy relevant
* information.
* @param cf the connection filter wanting to use it
* @param peer the peer the filter wants to talk to
* @param tls_id identifier of TLS implementation for sessions. Should
* include full version if session data from other versions
* is to be avoided.
* @param ppeer_key on successful return, the key generated
*/
CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
const struct ssl_peer *peer,
const char *tls_id,
char **ppeer_key);
/* Lock session cache mutex.
* Call this before calling other Curl_ssl_*session* functions
* Caller should unlock this mutex as soon as possible, as it may block
* other SSL connection from making progress.
* The purpose of explicitly locking SSL session cache data is to allow
* individual SSL engines to manage session lifetime in their specific way.
*/
void Curl_ssl_scache_lock(struct Curl_easy *data);
/* Unlock session cache mutex */
void Curl_ssl_scache_unlock(struct Curl_easy *data);
/* Get TLS session object from the cache for the ssl_peer_ey.
* scache mutex must be locked (see Curl_ssl_scache_lock).
* Caller must make sure that the ownership of returned session object
* is properly taken (e.g. its refcount is incremented
* under scache mutex).
* @param cf the connection filter wanting to use it
* @param data the transfer involved
* @param ssl_peer_key the key for lookup
* @param sobj on return, the object for the peer key or NULL
*/
bool Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key,
void **sobj);
typedef void Curl_ssl_scache_obj_dtor(void *sobj);
/* Add a TLS session related object to the cache.
* Replaces an existing object with the same peer_key.
* scache mutex must be locked (see Curl_ssl_scache_lock).
* Call takes ownership of `sobj`, using `sobj_dtor_cb`
* to deallocate it. Is called in all outcomes, either right away or
* later when the session cache is cleaned up.
* Caller must ensure that it has properly shared ownership of `sobj`
* with cache (e.g. incrementing refcount on success)
* @param cf the connection filter wanting to use it
* @param data the transfer involved
* @param ssl_peer_key the key for lookup
* @param sobj the TLS session object
* @param sobj_free_cb callback to free the session objectt
*/
CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key,
void *sobj,
Curl_ssl_scache_obj_dtor *sobj_dtor_cb);
/* All about a SSL session ticket */
struct Curl_ssl_session {
const unsigned char *sdata; /* session ticket data, plain bytes */
size_t sdata_len; /* number of bytes in sdata */
curl_off_t time_received; /* seconds since EPOCH ticket was received */
int lifetime_secs; /* ticket lifetime (-1 unknown) */
int ietf_tls_id; /* TLS protocol identifier negotiated */
char *alpn; /* APLN TLS negotiated protocol string */
struct Curl_llist_node list; /* internal storage handling */
};
/* Create a `session` instance. Does NOT need locking.
* Takes ownership of `sdata` and `sobj` regardless of return code.
* @param sdata bytes of SSL session data or NULL (sobj then required)
* @param sdata_len amount of session data bytes
* @param ietf_tls_id IETF protocol version, e.g. 0x304 for TLSv1.3
* @param alpn ALPN protocol selected or NULL
* @param time_received seconds since EPOCH session was received, pass 0
* to have the value set to time of call
* @param lifetime_secs seconds of announced lifetime, <0 if unknown.
* values longer than 1 week will be capped as
* required by RFC 8446
* @param psession on return the scached session instance created
*/
CURLcode
Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len,
int ietf_tls_id, const char *alpn,
curl_off_t time_received, long lifetime_secs,
struct Curl_ssl_session **psession);
/* Destroy a `session` instance. Can be called with NULL.
* Does NOT need locking. */
void Curl_ssl_session_destroy(struct Curl_ssl_session *s);
/* Put the scache session into the cache. Does NOT need locking.
* Call takes ownership of `s` in all outcomes.
* @param cf the connection filter wanting to use it
* @param data the transfer involved
* @param ssl_peer_key the key for lookup
* @param s the scache session object
*/
CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key,
struct Curl_ssl_session *s);
/* Take a matching scache session from the cache. Does NOT need locking.
* @param cf the connection filter wanting to use it
* @param data the transfer involved
* @param ssl_peer_key the key for lookup
* @param s on return, the scache session object or NULL
*/
CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key,
struct Curl_ssl_session **ps);
/* Return a taken scache session to the cache. Does NOT need locking.
* Depending on TLS version and other criteria, it may cache it again
* or destroy it. Maybe called with a NULL session.
*/
void Curl_ssl_scache_return(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key,
struct Curl_ssl_session *s);
/* Remove all sessions and obj for the peer_key. Does NOT need locking. */
void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key);
#else /* USE_SSL */
#define Curl_ssl_scache_create(x,y) CURLE_OK
#define Curl_ssl_scache_destroy(x) CURLE_OK
#endif /* USE_SSL (else) */
#endif /* HEADER_CURL_VTLS_SCACHE_H */

View file

@ -60,6 +60,7 @@
#include "inet_pton.h"
#include "vtls.h"
#include "vtls_int.h"
#include "vtls_scache.h"
#include "keylog.h"
#include "parsedate.h"
#include "connect.h" /* for the connect timeout */
@ -395,57 +396,53 @@ static void wolfssl_bio_cf_free_methods(void)
#endif /* !USE_BIO_CHAIN */
static void wolfssl_session_free(void *sdata, size_t slen)
{
(void)slen;
free(sdata);
}
CURLcode wssl_cache_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
WOLFSSL_SESSION *session)
CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key,
WOLFSSL_SESSION *session,
int ietf_tls_id,
const char *alpn)
{
CURLcode result = CURLE_OK;
struct Curl_ssl_session *sc_session = NULL;
unsigned char *sdata = NULL;
unsigned int slen;
unsigned int sdata_len;
if(!session)
goto out;
slen = wolfSSL_i2d_SSL_SESSION(session, NULL);
if(slen <= 0) {
CURL_TRC_CF(data, cf, "fail to assess session length: %u", slen);
sdata_len = wolfSSL_i2d_SSL_SESSION(session, NULL);
if(sdata_len <= 0) {
CURL_TRC_CF(data, cf, "fail to assess session length: %u", sdata_len);
result = CURLE_FAILED_INIT;
goto out;
}
sdata = calloc(1, slen);
sdata = calloc(1, sdata_len);
if(!sdata) {
failf(data, "unable to allocate session buffer of %u bytes", slen);
failf(data, "unable to allocate session buffer of %u bytes", sdata_len);
result = CURLE_OUT_OF_MEMORY;
goto out;
}
slen = wolfSSL_i2d_SSL_SESSION(session, &sdata);
if(slen <= 0) {
CURL_TRC_CF(data, cf, "fail to serialize session: %u", slen);
sdata_len = wolfSSL_i2d_SSL_SESSION(session, &sdata);
if(sdata_len <= 0) {
CURL_TRC_CF(data, cf, "fail to serialize session: %u", sdata_len);
result = CURLE_FAILED_INIT;
goto out;
}
Curl_ssl_sessionid_lock(data);
result = Curl_ssl_set_sessionid(cf, data, peer, NULL,
sdata, slen, wolfssl_session_free);
Curl_ssl_sessionid_unlock(data);
if(result)
failf(data, "failed to add new ssl session to cache (%d)", result);
else {
CURL_TRC_CF(data, cf, "added new session to cache");
sdata = NULL;
result = Curl_ssl_session_create(sdata, sdata_len,
ietf_tls_id, alpn, 0,
wolfSSL_SESSION_get_timeout(session),
&sc_session);
sdata = NULL; /* took ownership of sdata */
if(!result) {
result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
/* took ownership of `sc_session` */
}
out:
free(sdata);
return 0;
return result;
}
static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
@ -460,32 +457,35 @@ static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
DEBUGASSERT(connssl);
DEBUGASSERT(data);
if(connssl && data) {
(void)wssl_cache_session(cf, data, &connssl->peer, session);
(void)Curl_wssl_cache_session(cf, data, connssl->peer.scache_key,
session, wolfSSL_version(ssl),
connssl->negotiated.alpn);
}
}
return 0;
}
CURLcode wssl_setup_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct wolfssl_ctx *wss,
struct ssl_peer *peer)
CURLcode Curl_wssl_setup_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct wolfssl_ctx *wss,
const char *ssl_peer_key)
{
void *psdata;
const unsigned char *sdata = NULL;
size_t slen = 0;
CURLcode result = CURLE_OK;
struct Curl_ssl_session *sc_session = NULL;
CURLcode result;
Curl_ssl_sessionid_lock(data);
if(!Curl_ssl_getsessionid(cf, data, peer, &psdata, &slen, NULL)) {
result = Curl_ssl_scache_take(cf, data, ssl_peer_key, &sc_session);
if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) {
WOLFSSL_SESSION *session;
sdata = psdata;
session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata, (long)slen);
/* wolfSSL changes the passed pointer for whatever reasons, yikes */
const unsigned char *sdata = sc_session->sdata;
session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata,
(long)sc_session->sdata_len);
if(session) {
int ret = wolfSSL_set_session(wss->handle, session);
if(ret != WOLFSSL_SUCCESS) {
Curl_ssl_delsessionid(data, psdata);
infof(data, "previous session not accepted (%d), "
Curl_ssl_session_destroy(sc_session);
sc_session = NULL;
infof(data, "cached session not accepted (%d), "
"removing from cache", ret);
}
else
@ -496,7 +496,7 @@ CURLcode wssl_setup_session(struct Curl_cfilter *cf,
failf(data, "could not decode previous session");
}
}
Curl_ssl_sessionid_unlock(data);
Curl_ssl_scache_return(cf, data, ssl_peer_key, sc_session);
return result;
}
@ -1188,7 +1188,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
/* Check if there is a cached ID we can/should use here! */
if(ssl_config->primary.cache_session) {
/* Set session from cache if there is one */
(void)wssl_setup_session(cf, data, backend, &connssl->peer);
(void)Curl_wssl_setup_session(cf, data, backend, connssl->peer.scache_key);
/* Register to get notified when a new session is received */
wolfSSL_set_app_data(backend->handle, cf);
wolfSSL_CTX_sess_set_new_cb(backend->ctx, wssl_vtls_new_session_cb);
@ -1792,7 +1792,7 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf,
}
static size_t wolfssl_version(char *buffer, size_t size)
size_t Curl_wssl_version(char *buffer, size_t size)
{
#if LIBWOLFSSL_VERSION_HEX >= 0x03006000
return msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version());
@ -2030,7 +2030,7 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
wolfssl_init, /* init */
wolfssl_cleanup, /* cleanup */
wolfssl_version, /* version */
Curl_wssl_version, /* version */
wolfssl_shutdown, /* shutdown */
wolfssl_data_pending, /* data_pending */
wolfssl_random, /* random */

View file

@ -47,19 +47,23 @@ struct wolfssl_ctx {
BIT(shutting_down); /* TLS is being shut down */
};
size_t Curl_wssl_version(char *buffer, size_t size);
CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct wolfssl_ctx *wssl);
CURLcode wssl_setup_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct wolfssl_ctx *wss,
struct ssl_peer *peer);
CURLcode Curl_wssl_setup_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct wolfssl_ctx *wss,
const char *ssl_peer_key);
CURLcode wssl_cache_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
WOLFSSL_SESSION *session);
CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *ssl_peer_key,
WOLFSSL_SESSION *session,
int ietf_tls_id,
const char *alpn);
#endif /* USE_WOLFSSL */