xref: /curl/lib/vtls/vtls_scache.c (revision 3428b8ad)
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 
25 /* This file is for implementing all "generic" SSL functions that all libcurl
26    internals should use. It is then responsible for calling the proper
27    "backend" function.
28 
29    SSL-functions in libcurl should call functions in this source file, and not
30    to any specific SSL-layer.
31 
32    Curl_ssl_ - prefix for generic ones
33 
34    Note that this source code uses the functions of the configured SSL
35    backend via the global Curl_ssl instance.
36 
37    "SSL/TLS Strong Encryption: An Introduction"
38    https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html
39 */
40 
41 #include "curl_setup.h"
42 
43 #ifdef USE_SSL
44 
45 #ifdef HAVE_SYS_TYPES_H
46 #include <sys/types.h>
47 #endif
48 #ifdef HAVE_SYS_STAT_H
49 #include <sys/stat.h>
50 #endif
51 #ifdef HAVE_FCNTL_H
52 #include <fcntl.h>
53 #endif
54 
55 #include "urldata.h"
56 #include "cfilters.h"
57 
58 #include "vtls.h" /* generic SSL protos etc */
59 #include "vtls_int.h"
60 #include "vtls_scache.h"
61 
62 #include "strcase.h"
63 #include "url.h"
64 #include "llist.h"
65 #include "share.h"
66 #include "curl_trc.h"
67 #include "curl_sha256.h"
68 #include "warnless.h"
69 #include "curl_printf.h"
70 #include "strdup.h"
71 
72 /* The last #include files should be: */
73 #include "curl_memory.h"
74 #include "memdebug.h"
75 
76 /* a peer+tls-config we cache sessions for */
77 struct Curl_ssl_scache_peer {
78   char *ssl_peer_key;      /* id for peer + relevant TLS configuration */
79   char *clientcert;
80   char *srp_username;
81   char *srp_password;
82   struct Curl_llist sessions;
83   void *sobj;              /* object instance or NULL */
84   Curl_ssl_scache_obj_dtor *sobj_free; /* free `sobj` callback */
85   unsigned char key_salt[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
86   unsigned char key_hmac[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
87   size_t max_sessions;
88   long age;                /* just a number, the higher the more recent */
89   BIT(hmac_set);           /* if key_salt and key_hmac are present */
90 };
91 
92 struct Curl_ssl_scache {
93   struct Curl_ssl_scache_peer *peers;
94   size_t peer_count;
95   int default_lifetime_secs;
96   long age;
97 };
98 
cf_ssl_scache_clear_session(struct Curl_ssl_session * s)99 static void cf_ssl_scache_clear_session(struct Curl_ssl_session *s)
100 {
101   if(s->sdata) {
102     free((void *)s->sdata);
103     s->sdata = NULL;
104   }
105   s->sdata_len = 0;
106   s->ietf_tls_id = 0;
107   s->time_received = 0;
108   s->lifetime_secs = 0;
109   Curl_safefree(s->alpn);
110 }
111 
cf_ssl_scache_sesssion_ldestroy(void * udata,void * s)112 static void cf_ssl_scache_sesssion_ldestroy(void *udata, void *s)
113 {
114   (void)udata;
115   cf_ssl_scache_clear_session(s);
116   free(s);
117 }
118 
119 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)120 Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len,
121                         int ietf_tls_id, const char *alpn,
122                         curl_off_t time_received, long lifetime_secs,
123                         struct Curl_ssl_session **psession)
124 {
125   struct Curl_ssl_session *s;
126 
127   if(!sdata || !sdata_len) {
128     free(sdata);
129     return CURLE_BAD_FUNCTION_ARGUMENT;
130   }
131 
132   *psession = NULL;
133   s = calloc(1, sizeof(*s));
134   if(!s) {
135     free(sdata);
136     return CURLE_OUT_OF_MEMORY;
137   }
138 
139   s->ietf_tls_id = ietf_tls_id;
140   s->time_received = time_received;
141   if(lifetime_secs < 0)
142     lifetime_secs = -1; /* unknown */
143   else if((s->ietf_tls_id == CURL_IETF_PROTO_TLS1_3) &&
144           (lifetime_secs > CURL_SCACHE_MAX_13_LIFETIME_SEC))
145     lifetime_secs = CURL_SCACHE_MAX_13_LIFETIME_SEC;
146   else if(lifetime_secs > CURL_SCACHE_MAX_12_LIFETIME_SEC)
147     lifetime_secs = CURL_SCACHE_MAX_12_LIFETIME_SEC;
148 
149   s->lifetime_secs = (int)lifetime_secs;
150   s->sdata = sdata;
151   s->sdata_len = sdata_len;
152   if(alpn) {
153     s->alpn = strdup(alpn);
154     if(!s->alpn) {
155       cf_ssl_scache_sesssion_ldestroy(NULL, s);
156       return CURLE_OUT_OF_MEMORY;
157     }
158   }
159   *psession = s;
160   return CURLE_OK;
161 }
162 
Curl_ssl_session_destroy(struct Curl_ssl_session * s)163 void Curl_ssl_session_destroy(struct Curl_ssl_session *s)
164 {
165   if(s) {
166     /* if in the list, the list destructor takes care of it */
167     if(Curl_node_llist(&s->list))
168       Curl_node_remove(&s->list);
169     else {
170       cf_ssl_scache_sesssion_ldestroy(NULL, s);
171     }
172   }
173 }
174 
cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer * peer)175 static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer)
176 {
177   Curl_llist_destroy(&peer->sessions, NULL);
178   if(peer->sobj) {
179     DEBUGASSERT(peer->sobj_free);
180     if(peer->sobj_free)
181       peer->sobj_free(peer->sobj);
182     peer->sobj = NULL;
183   }
184   peer->sobj_free = NULL;
185   Curl_safefree(peer->clientcert);
186 #ifdef USE_TLS_SRP
187   Curl_safefree(peer->srp_username);
188   Curl_safefree(peer->srp_password);
189 #endif
190   Curl_safefree(peer->ssl_peer_key);
191   peer->age = 0;
192   peer->hmac_set = FALSE;
193 }
194 
cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer * peer,void * sobj,Curl_ssl_scache_obj_dtor * sobj_free)195 static void cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer *peer,
196                                        void *sobj,
197                                        Curl_ssl_scache_obj_dtor *sobj_free)
198 {
199   DEBUGASSERT(peer);
200   if(peer->sobj_free) {
201     peer->sobj_free(peer->sobj);
202   }
203   peer->sobj = sobj;
204   peer->sobj_free = sobj_free;
205 }
206 
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)207 static CURLcode cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer,
208                                         const char *ssl_peer_key,
209                                         const char *clientcert,
210                                         const char *srp_username,
211                                         const char *srp_password)
212 {
213   CURLcode result = CURLE_OUT_OF_MEMORY;
214 
215   DEBUGASSERT(!peer->ssl_peer_key);
216   peer->ssl_peer_key = strdup(ssl_peer_key);
217   if(!peer->ssl_peer_key)
218     goto out;
219   if(clientcert) {
220     peer->clientcert = strdup(clientcert);
221     if(!peer->clientcert)
222       goto out;
223   }
224   if(srp_username) {
225     peer->srp_username = strdup(srp_username);
226     if(!peer->srp_username)
227       goto out;
228   }
229   if(srp_password) {
230     peer->srp_password = strdup(srp_password);
231     if(!peer->srp_password)
232       goto out;
233   }
234   result = CURLE_OK;
235 out:
236   if(result)
237     cf_ssl_scache_clear_peer(peer);
238   return result;
239 }
240 
cf_scache_session_remove(struct Curl_ssl_scache_peer * peer,struct Curl_ssl_session * s)241 static void cf_scache_session_remove(struct Curl_ssl_scache_peer *peer,
242                                      struct Curl_ssl_session *s)
243 {
244   (void)peer;
245   DEBUGASSERT(Curl_node_llist(&s->list) == &peer->sessions);
246   Curl_ssl_session_destroy(s);
247 }
248 
cf_scache_session_expired(struct Curl_ssl_session * s,curl_off_t now)249 static bool cf_scache_session_expired(struct Curl_ssl_session *s,
250                                       curl_off_t now)
251 {
252   return (s->lifetime_secs > 0 &&
253          (s->time_received + s->lifetime_secs) < now);
254 }
255 
cf_scache_peer_remove_expired(struct Curl_ssl_scache_peer * peer,curl_off_t now)256 static void cf_scache_peer_remove_expired(struct Curl_ssl_scache_peer *peer,
257                                           curl_off_t now)
258 {
259   struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
260   while(n) {
261     struct Curl_ssl_session *s = Curl_node_elem(n);
262     n = Curl_node_next(n);
263     if(cf_scache_session_expired(s, now))
264       cf_scache_session_remove(peer, s);
265   }
266 }
267 
cf_scache_peer_remove_non13(struct Curl_ssl_scache_peer * peer)268 static void cf_scache_peer_remove_non13(struct Curl_ssl_scache_peer *peer)
269 {
270   struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
271   while(n) {
272     struct Curl_ssl_session *s = Curl_node_elem(n);
273     n = Curl_node_next(n);
274     if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3)
275       cf_scache_session_remove(peer, s);
276   }
277 }
278 
Curl_ssl_scache_create(size_t max_peers,size_t max_sessions_per_peer,struct Curl_ssl_scache ** pscache)279 CURLcode Curl_ssl_scache_create(size_t max_peers,
280                                 size_t max_sessions_per_peer,
281                                 struct Curl_ssl_scache **pscache)
282 {
283   struct Curl_ssl_scache *scache;
284   struct Curl_ssl_scache_peer *peers;
285   size_t i;
286 
287   *pscache = NULL;
288   peers = calloc(max_peers, sizeof(*peers));
289   if(!peers)
290     return CURLE_OUT_OF_MEMORY;
291 
292   scache = calloc(1, sizeof(*scache));
293   if(!scache) {
294     free(peers);
295     return CURLE_OUT_OF_MEMORY;
296   }
297 
298   scache->default_lifetime_secs = (24*60*60); /* 1 day */
299   scache->peer_count = max_peers;
300   scache->peers = peers;
301   scache->age = 1;
302   for(i = 0; i < scache->peer_count; ++i) {
303     scache->peers[i].max_sessions = max_sessions_per_peer;
304     Curl_llist_init(&scache->peers[i].sessions,
305                     cf_ssl_scache_sesssion_ldestroy);
306   }
307 
308   *pscache = scache;
309   return CURLE_OK;
310 }
311 
Curl_ssl_scache_destroy(struct Curl_ssl_scache * scache)312 void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache)
313 {
314   if(scache) {
315     size_t i;
316     for(i = 0; i < scache->peer_count; ++i) {
317       cf_ssl_scache_clear_peer(&scache->peers[i]);
318     }
319     free(scache->peers);
320     free(scache);
321   }
322 }
323 
324 /* Lock shared SSL session data */
Curl_ssl_scache_lock(struct Curl_easy * data)325 void Curl_ssl_scache_lock(struct Curl_easy *data)
326 {
327   if(CURL_SHARE_ssl_scache(data))
328     Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
329 }
330 
331 /* Unlock shared SSL session data */
Curl_ssl_scache_unlock(struct Curl_easy * data)332 void Curl_ssl_scache_unlock(struct Curl_easy *data)
333 {
334   if(CURL_SHARE_ssl_scache(data))
335     Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
336 }
337 
cf_ssl_peer_key_add_path(struct dynbuf * buf,const char * name,char * path)338 static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf,
339                                           const char *name,
340                                           char *path)
341 {
342   if(path && path[0]) {
343     /* We try to add absolute paths, so that the session key can stay
344      * valid when used in another process with different CWD. However,
345      * when a path does not exist, this does not work. Then, we add
346      * the path as is. */
347 #ifdef _WIN32
348     char abspath[_MAX_PATH];
349     if(_fullpath(abspath, path, _MAX_PATH))
350       return Curl_dyn_addf(buf, ":%s-%s", name, abspath);
351 #else
352     if(path[0] != '/') {
353       char *abspath = realpath(path, NULL);
354       if(abspath) {
355         CURLcode r = Curl_dyn_addf(buf, ":%s-%s", name, abspath);
356         (free)(abspath); /* allocated by libc, free without memdebug */
357         return r;
358       }
359     }
360 #endif
361     return Curl_dyn_addf(buf, ":%s-%s", name, path);
362   }
363   return CURLE_OK;
364 }
365 
cf_ssl_peer_key_add_hash(struct dynbuf * buf,const char * name,struct curl_blob * blob)366 static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf,
367                                           const char *name,
368                                           struct curl_blob *blob)
369 {
370   CURLcode r = CURLE_OK;
371   if(blob && blob->len) {
372     unsigned char hash[CURL_SHA256_DIGEST_LENGTH];
373     size_t i;
374 
375     r = Curl_dyn_addf(buf, ":%s-", name);
376     if(r)
377       goto out;
378     r = Curl_sha256it(hash, blob->data, blob->len);
379     if(r)
380       goto out;
381     for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) {
382       r = Curl_dyn_addf(buf, "%02x", hash[i]);
383       if(r)
384         goto out;
385     }
386   }
387 out:
388   return r;
389 }
390 
Curl_ssl_peer_key_make(struct Curl_cfilter * cf,const struct ssl_peer * peer,const char * tls_id,char ** ppeer_key)391 CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
392                                 const struct ssl_peer *peer,
393                                 const char *tls_id,
394                                 char **ppeer_key)
395 {
396   struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
397   struct dynbuf buf;
398   size_t key_len;
399   CURLcode r;
400 
401   *ppeer_key = NULL;
402   Curl_dyn_init(&buf, 10 * 1024);
403 
404   r = Curl_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port);
405   if(r)
406     goto out;
407 
408   switch(peer->transport) {
409   case TRNSPRT_TCP:
410     break;
411   case TRNSPRT_UDP:
412     r = Curl_dyn_add(&buf, ":UDP");
413     break;
414   case TRNSPRT_QUIC:
415     r = Curl_dyn_add(&buf, ":QUIC");
416     break;
417   case TRNSPRT_UNIX:
418     r = Curl_dyn_add(&buf, ":UNIX");
419     break;
420   default:
421     r = Curl_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport);
422     break;
423   }
424   if(r)
425     goto out;
426 
427   if(!ssl->verifypeer) {
428     r = Curl_dyn_add(&buf, ":NO-VRFY-PEER");
429     if(r)
430       goto out;
431   }
432   if(!ssl->verifyhost) {
433     r = Curl_dyn_add(&buf, ":NO-VRFY-HOST");
434     if(r)
435       goto out;
436   }
437   if(ssl->verifystatus) {
438     r = Curl_dyn_add(&buf, ":VRFY-STATUS");
439     if(r)
440       goto out;
441   }
442   if(!ssl->verifypeer || !ssl->verifyhost) {
443     if(cf->conn->bits.conn_to_host) {
444       r = Curl_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);
445       if(r)
446         goto out;
447     }
448     if(cf->conn->bits.conn_to_port) {
449       r = Curl_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port);
450       if(r)
451         goto out;
452     }
453   }
454 
455   if(ssl->version || ssl->version_max) {
456     r = Curl_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version,
457                       (ssl->version_max >> 16));
458     if(r)
459       goto out;
460   }
461   if(ssl->ssl_options) {
462     r = Curl_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options);
463     if(r)
464       goto out;
465   }
466   if(ssl->cipher_list) {
467     r = Curl_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list);
468     if(r)
469       goto out;
470   }
471   if(ssl->cipher_list13) {
472     r = Curl_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13);
473     if(r)
474       goto out;
475   }
476   if(ssl->curves) {
477     r = Curl_dyn_addf(&buf, ":CURVES-%s", ssl->curves);
478     if(r)
479       goto out;
480   }
481   if(ssl->verifypeer) {
482     r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile);
483     if(r)
484       goto out;
485     r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath);
486     if(r)
487       goto out;
488     r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile);
489     if(r)
490       goto out;
491     r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert);
492     if(r)
493       goto out;
494     if(ssl->cert_blob) {
495       r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob);
496       if(r)
497         goto out;
498     }
499     if(ssl->ca_info_blob) {
500       r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob);
501       if(r)
502         goto out;
503     }
504     if(ssl->issuercert_blob) {
505       r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob);
506       if(r)
507         goto out;
508     }
509   }
510   if(ssl->pinned_key && ssl->pinned_key[0]) {
511     r = Curl_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key);
512     if(r)
513       goto out;
514   }
515 
516   if(ssl->clientcert && ssl->clientcert[0]) {
517     r = Curl_dyn_add(&buf, ":CCERT");
518     if(r)
519       goto out;
520   }
521 #ifdef USE_TLS_SRP
522   if(ssl->username || ssl->password) {
523     r = Curl_dyn_add(&buf, ":SRP-AUTH");
524     if(r)
525       goto out;
526   }
527 #endif
528 
529   if(!tls_id || !tls_id[0]) {
530     r = CURLE_FAILED_INIT;
531     goto out;
532   }
533   r = Curl_dyn_addf(&buf, ":IMPL-%s", tls_id);
534   if(r)
535     goto out;
536 
537   *ppeer_key = Curl_dyn_take(&buf, &key_len);
538   /* we just added printable char, and dynbuf always 0 terminates,
539    * no need to track length */
540 
541 
542 out:
543   Curl_dyn_free(&buf);
544   return r;
545 }
546 
cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer * peer,struct ssl_primary_config * conn_config)547 static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer,
548                                      struct ssl_primary_config *conn_config)
549 {
550   if(!Curl_safecmp(peer->clientcert, conn_config->clientcert))
551     return FALSE;
552 #ifdef USE_TLS_SRP
553    if(Curl_timestrcmp(peer->srp_username, conn_config->username) ||
554       Curl_timestrcmp(peer->srp_password, conn_config->password))
555      return FALSE;
556 #endif
557   return TRUE;
558 }
559 
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)560 static CURLcode cf_ssl_find_peer(struct Curl_cfilter *cf,
561                                  struct Curl_easy *data,
562                                  struct Curl_ssl_scache *scache,
563                                  const char *ssl_peer_key,
564                                  struct Curl_ssl_scache_peer **ppeer)
565 {
566   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
567   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
568   size_t i, peer_key_len = 0;
569   CURLcode result = CURLE_OK;
570 
571   *ppeer = NULL;
572   if(!ssl_config || !ssl_config->primary.cache_session)
573     goto out;
574 
575   /* check for entries with known peer_key */
576   for(i = 0; scache && i < scache->peer_count; i++) {
577     if(scache->peers[i].ssl_peer_key &&
578        strcasecompare(ssl_peer_key, scache->peers[i].ssl_peer_key) &&
579        cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
580       /* yes, we have a cached session for this! */
581       *ppeer = &scache->peers[i];
582       goto out;
583     }
584   }
585   /* check for entries with HMAC set but no known peer_key */
586   for(i = 0; scache && i < scache->peer_count; i++) {
587     if(!scache->peers[i].ssl_peer_key &&
588        scache->peers[i].hmac_set &&
589        cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
590       /* possible entry with unknown peer_key, check hmac */
591       unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH];
592       if(!peer_key_len) /* we are lazy */
593         peer_key_len = strlen(ssl_peer_key);
594       result = Curl_hmacit(&Curl_HMAC_SHA256,
595                            scache->peers[i].key_salt,
596                            sizeof(scache->peers[i].key_salt),
597                            (const unsigned char *)ssl_peer_key,
598                            peer_key_len,
599                            my_hmac);
600       if(result)
601         goto out;
602       if(!memcmp(scache->peers[i].key_hmac, my_hmac, sizeof(my_hmac))) {
603         /* remember peer_key for future lookups */
604         scache->peers[i].ssl_peer_key = strdup(ssl_peer_key);
605         if(!scache->peers[i].ssl_peer_key) {
606           result = CURLE_OUT_OF_MEMORY;
607           goto out;
608         }
609         *ppeer = &scache->peers[i];
610         goto out;
611       }
612     }
613   }
614 out:
615   if(result)
616     CURL_TRC_CF(data, cf, "[SACHE] failure finding scache peer: %d", result);
617   return result;
618 }
619 
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)620 static CURLcode cf_ssl_add_peer(struct Curl_cfilter *cf,
621                                 struct Curl_easy *data,
622                                 struct Curl_ssl_scache *scache,
623                                 const char *ssl_peer_key,
624                                 struct Curl_ssl_scache_peer **ppeer)
625 {
626   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
627   struct Curl_ssl_scache_peer *peer = NULL;
628   size_t i;
629   CURLcode result;
630 
631   *ppeer = NULL;
632   result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
633   if(result || !scache->peer_count)
634     return result;
635 
636   if(peer) {
637     *ppeer = peer;
638     return CURLE_OK;
639   }
640 
641   /* not there, find empty or oldest peer */
642   for(i = 0; i < scache->peer_count; ++i) {
643     /* free peer entry? */
644     if(!scache->peers[i].ssl_peer_key && !scache->peers[i].hmac_set) {
645       peer = &scache->peers[i];
646       break;
647     }
648     /* peer without sessions and obj */
649     if(!scache->peers[i].sobj &&
650        !Curl_llist_count(&scache->peers[i].sessions)) {
651       peer = &scache->peers[i];
652       break;
653     }
654     /* remember "oldest" peer */
655     if(!peer || (scache->peers[i].age < peer->age)) {
656       peer = &scache->peers[i];
657     }
658   }
659   DEBUGASSERT(peer);
660   if(!peer)
661     return CURLE_OK;
662   /* clear previous peer and reinit */
663   cf_ssl_scache_clear_peer(peer);
664   result = cf_ssl_scache_peer_init(peer, ssl_peer_key,
665                                    conn_config->clientcert,
666 #ifdef USE_TLS_SRP
667                                    conn_config->username,
668                                    conn_config->password);
669 #else
670                                    NULL, NULL);
671 #endif
672   if(result)
673     goto out;
674   /* all ready */
675   *ppeer = peer;
676   result = CURLE_OK;
677 
678 out:
679   if(result) {
680     cf_ssl_scache_clear_peer(peer);
681     CURL_TRC_CF(data, cf, "[SACHE] failure adding peer: %d", result);
682   }
683   return result;
684 }
685 
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)686 static CURLcode cf_scache_peer_add_session(struct Curl_cfilter *cf,
687                                            struct Curl_easy *data,
688                                            struct Curl_ssl_scache *scache,
689                                            const char *ssl_peer_key,
690                                            struct Curl_ssl_session *s)
691 {
692   struct Curl_ssl_scache_peer *peer = NULL;
693   CURLcode result = CURLE_OUT_OF_MEMORY;
694   curl_off_t now = (curl_off_t)time(NULL);
695 
696   if(!scache || !scache->peer_count) {
697     Curl_ssl_session_destroy(s);
698     return CURLE_OK;
699   }
700 
701   if(!s->time_received)
702     s->time_received = now;
703   if(s->lifetime_secs < 0)
704     s->lifetime_secs = scache->default_lifetime_secs;
705 
706   if(cf_scache_session_expired(s, now)) {
707     CURL_TRC_CF(data, cf, "[SCACHE] add, session already expired");
708     Curl_ssl_session_destroy(s);
709     return CURLE_OK;
710   }
711 
712   result = cf_ssl_add_peer(cf, data, scache, ssl_peer_key, &peer);
713   if(result || !peer) {
714     CURL_TRC_CF(data, cf, "[SCACHE] unable to add scache peer: %d", result);
715     Curl_ssl_session_destroy(s);
716     goto out;
717   }
718 
719   /* A session not from TLSv1.3 replaces all other. */
720   if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3) {
721     Curl_llist_destroy(&peer->sessions, NULL);
722     Curl_llist_append(&peer->sessions, s, &s->list);
723   }
724   else {
725     /* Expire existing, append, trim from head to obey max_sessions */
726     cf_scache_peer_remove_expired(peer, now);
727     cf_scache_peer_remove_non13(peer);
728     Curl_llist_append(&peer->sessions, s, &s->list);
729     while(Curl_llist_count(&peer->sessions) > peer->max_sessions) {
730       Curl_node_remove(Curl_llist_head(&peer->sessions));
731     }
732   }
733 
734 out:
735   if(result) {
736     failf(data, "[SCACHE] failed to add session for %s, error=%d",
737           ssl_peer_key, result);
738   }
739   else
740     CURL_TRC_CF(data, cf, "[SCACHE] added session for %s [proto=0x%x, "
741                 "lifetime=%d, alpn=%s], peer has %zu sessions now",
742                 ssl_peer_key, s->ietf_tls_id, s->lifetime_secs, s->alpn,
743                 Curl_llist_count(&peer->sessions));
744   return result;
745 }
746 
Curl_ssl_scache_put(struct Curl_cfilter * cf,struct Curl_easy * data,const char * ssl_peer_key,struct Curl_ssl_session * s)747 CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf,
748                              struct Curl_easy *data,
749                              const char *ssl_peer_key,
750                              struct Curl_ssl_session *s)
751 {
752   struct Curl_ssl_scache *scache = data->state.ssl_scache;
753   CURLcode result;
754 
755   Curl_ssl_scache_lock(data);
756   result = cf_scache_peer_add_session(cf, data, scache, ssl_peer_key, s);
757   Curl_ssl_scache_unlock(data);
758   return result;
759 }
760 
Curl_ssl_scache_return(struct Curl_cfilter * cf,struct Curl_easy * data,const char * ssl_peer_key,struct Curl_ssl_session * s)761 void Curl_ssl_scache_return(struct Curl_cfilter *cf,
762                            struct Curl_easy *data,
763                            const char *ssl_peer_key,
764                            struct Curl_ssl_session *s)
765 {
766   /* See RFC 8446 C.4:
767    * "Clients SHOULD NOT reuse a ticket for multiple connections." */
768   if(s && s->ietf_tls_id < 0x304)
769     (void)Curl_ssl_scache_put(cf, data, ssl_peer_key, s);
770   else
771     Curl_ssl_session_destroy(s);
772 }
773 
Curl_ssl_scache_take(struct Curl_cfilter * cf,struct Curl_easy * data,const char * ssl_peer_key,struct Curl_ssl_session ** ps)774 CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf,
775                               struct Curl_easy *data,
776                               const char *ssl_peer_key,
777                               struct Curl_ssl_session **ps)
778 {
779   struct Curl_ssl_scache *scache = data->state.ssl_scache;
780   struct Curl_ssl_scache_peer *peer = NULL;
781   struct Curl_llist_node *n;
782   CURLcode result;
783 
784   *ps = NULL;
785   if(!scache)
786     return CURLE_OK;
787 
788   Curl_ssl_scache_lock(data);
789   result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
790   if(!result && peer) {
791     cf_scache_peer_remove_expired(peer, (curl_off_t)time(NULL));
792     n = Curl_llist_head(&peer->sessions);
793     if(n) {
794       *ps = Curl_node_take_elem(n);
795       (scache->age)++;            /* increase general age */
796       peer->age = scache->age; /* set this as used in this age */
797     }
798   }
799   Curl_ssl_scache_unlock(data);
800 
801   CURL_TRC_CF(data, cf, "[SCACHE] %s cached session for '%s'",
802               *ps ? "Found" : "No", ssl_peer_key);
803   return result;
804 }
805 
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)806 CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
807                                  struct Curl_easy *data,
808                                  const char *ssl_peer_key,
809                                  void *sobj,
810                                  Curl_ssl_scache_obj_dtor *sobj_free)
811 {
812   struct Curl_ssl_scache *scache = data->state.ssl_scache;
813   struct Curl_ssl_scache_peer *peer = NULL;
814   CURLcode result;
815 
816   DEBUGASSERT(sobj);
817   DEBUGASSERT(sobj_free);
818 
819   result = cf_ssl_add_peer(cf, data, scache, ssl_peer_key, &peer);
820   if(result || !peer) {
821     CURL_TRC_CF(data, cf, "[SCACHE] unable to add scache peer: %d", result);
822     goto out;
823   }
824 
825   cf_ssl_scache_peer_set_obj(peer, sobj, sobj_free);
826   sobj = NULL;  /* peer took ownership */
827 
828 out:
829   if(sobj && sobj_free)
830     sobj_free(sobj);
831   return result;
832 }
833 
Curl_ssl_scache_get_obj(struct Curl_cfilter * cf,struct Curl_easy * data,const char * ssl_peer_key,void ** sobj)834 bool Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
835                              struct Curl_easy *data,
836                              const char *ssl_peer_key,
837                              void **sobj)
838 {
839   struct Curl_ssl_scache *scache = data->state.ssl_scache;
840   struct Curl_ssl_scache_peer *peer = NULL;
841   CURLcode result;
842 
843   *sobj = NULL;
844   if(!scache)
845     return FALSE;
846 
847   result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
848   if(result)
849     return FALSE;
850 
851   if(peer)
852     *sobj = peer->sobj;
853 
854   CURL_TRC_CF(data, cf, "[SACHE] %s cached session for '%s'",
855               *sobj ? "Found" : "No", ssl_peer_key);
856   return !!*sobj;
857 }
858 
Curl_ssl_scache_remove_all(struct Curl_cfilter * cf,struct Curl_easy * data,const char * ssl_peer_key)859 void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
860                                 struct Curl_easy *data,
861                                 const char *ssl_peer_key)
862 {
863   struct Curl_ssl_scache *scache = data->state.ssl_scache;
864   struct Curl_ssl_scache_peer *peer = NULL;
865   CURLcode result;
866 
867   (void)cf;
868   if(!scache)
869     return;
870 
871   Curl_ssl_scache_lock(data);
872   result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
873   if(!result && peer)
874     cf_ssl_scache_clear_peer(peer);
875   Curl_ssl_scache_unlock(data);
876 }
877 
878 #endif /* USE_SSL */
879