123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913 |
- /***************************************************************************
- * _ _ ____ _
- * 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;
- if(s->quic_tp) {
- free((void *)s->quic_tp);
- s->quic_tp = NULL;
- }
- s->quic_tp_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,
- size_t earlydata_max,
- struct Curl_ssl_session **psession)
- {
- return Curl_ssl_session_create2(sdata, sdata_len, ietf_tls_id, alpn,
- time_received, lifetime_secs,
- earlydata_max, NULL, 0, psession);
- }
- CURLcode
- Curl_ssl_session_create2(unsigned char *sdata, size_t sdata_len,
- int ietf_tls_id, const char *alpn,
- curl_off_t time_received, long lifetime_secs,
- size_t earlydata_max,
- unsigned char *quic_tp, size_t quic_tp_len,
- 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);
- free(quic_tp);
- 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->earlydata_max = earlydata_max;
- s->sdata = sdata;
- s->sdata_len = sdata_len;
- s->quic_tp = quic_tp;
- s->quic_tp_len = quic_tp_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);
- result = 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(result)
- goto out;
- 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, earlydata=%zu, quic_tp=%s], "
- "peer has %zu sessions now",
- ssl_peer_key, s->ietf_tls_id, s->lifetime_secs, s->alpn,
- s->earlydata_max, s->quic_tp ? "yes" : "no",
- 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;
- struct Curl_ssl_session *s = NULL;
- 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) {
- s = 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);
- if(s) {
- *ps = s;
- CURL_TRC_CF(data, cf, "[SCACHE] took session for %s [proto=0x%x, "
- "lifetime=%d, alpn=%s, earlydata=%zu, quic_tp=%s], "
- "%zu sessions remain",
- ssl_peer_key, s->ietf_tls_id, s->lifetime_secs, s->alpn,
- s->earlydata_max, s->quic_tp ? "yes" : "no",
- Curl_llist_count(&peer->sessions));
- }
- else {
- CURL_TRC_CF(data, cf, "[SCACHE] no cached session for %s", 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 */
|