• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "curl_setup.h"
26 
27 #ifdef USE_SSL
28 
29 #ifdef HAVE_SYS_TYPES_H
30 #include <sys/types.h>
31 #endif
32 #ifdef HAVE_SYS_STAT_H
33 #include <sys/stat.h>
34 #endif
35 #ifdef HAVE_FCNTL_H
36 #include <fcntl.h>
37 #endif
38 
39 #include "urldata.h"
40 #include "cfilters.h"
41 
42 #include "vtls.h" /* generic SSL protos etc */
43 #include "vtls_int.h"
44 #include "vtls_scache.h"
45 #include "vtls_spack.h"
46 
47 #include "strcase.h"
48 #include "url.h"
49 #include "llist.h"
50 #include "share.h"
51 #include "curl_trc.h"
52 #include "curl_sha256.h"
53 #include "rand.h"
54 #include "warnless.h"
55 #include "curl_printf.h"
56 #include "strdup.h"
57 
58 /* The last #include files should be: */
59 #include "curl_memory.h"
60 #include "memdebug.h"
61 
62 /* a peer+tls-config we cache sessions for */
63 struct Curl_ssl_scache_peer {
64   char *ssl_peer_key;      /* id for peer + relevant TLS configuration */
65   char *clientcert;
66   char *srp_username;
67   char *srp_password;
68   struct Curl_llist sessions;
69   void *sobj;              /* object instance or NULL */
70   Curl_ssl_scache_obj_dtor *sobj_free; /* free `sobj` callback */
71   unsigned char key_salt[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
72   unsigned char key_hmac[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
73   size_t max_sessions;
74   long age;                /* just a number, the higher the more recent */
75   BIT(hmac_set);           /* if key_salt and key_hmac are present */
76 };
77 
78 struct Curl_ssl_scache {
79   struct Curl_ssl_scache_peer *peers;
80   size_t peer_count;
81   int default_lifetime_secs;
82   long age;
83 };
84 
cf_ssl_scache_clear_session(struct Curl_ssl_session * s)85 static void cf_ssl_scache_clear_session(struct Curl_ssl_session *s)
86 {
87   if(s->sdata) {
88     free((void *)s->sdata);
89     s->sdata = NULL;
90   }
91   s->sdata_len = 0;
92   if(s->quic_tp) {
93     free((void *)s->quic_tp);
94     s->quic_tp = NULL;
95   }
96   s->quic_tp_len = 0;
97   s->ietf_tls_id = 0;
98   s->valid_until = 0;
99   Curl_safefree(s->alpn);
100 }
101 
cf_ssl_scache_sesssion_ldestroy(void * udata,void * s)102 static void cf_ssl_scache_sesssion_ldestroy(void *udata, void *s)
103 {
104   (void)udata;
105   cf_ssl_scache_clear_session(s);
106   free(s);
107 }
108 
109 CURLcode
Curl_ssl_session_create(unsigned char * sdata,size_t sdata_len,int ietf_tls_id,const char * alpn,curl_off_t valid_until,size_t earlydata_max,struct Curl_ssl_session ** psession)110 Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len,
111                         int ietf_tls_id, const char *alpn,
112                         curl_off_t valid_until, size_t earlydata_max,
113                         struct Curl_ssl_session **psession)
114 {
115   return Curl_ssl_session_create2(sdata, sdata_len, ietf_tls_id, alpn,
116                                   valid_until, earlydata_max,
117                                   NULL, 0, psession);
118 }
119 
120 CURLcode
Curl_ssl_session_create2(unsigned char * sdata,size_t sdata_len,int ietf_tls_id,const char * alpn,curl_off_t valid_until,size_t earlydata_max,unsigned char * quic_tp,size_t quic_tp_len,struct Curl_ssl_session ** psession)121 Curl_ssl_session_create2(unsigned char *sdata, size_t sdata_len,
122                          int ietf_tls_id, const char *alpn,
123                          curl_off_t valid_until, size_t earlydata_max,
124                          unsigned char *quic_tp, size_t quic_tp_len,
125                          struct Curl_ssl_session **psession)
126 {
127   struct Curl_ssl_session *s;
128 
129   if(!sdata || !sdata_len) {
130     free(sdata);
131     return CURLE_BAD_FUNCTION_ARGUMENT;
132   }
133 
134   *psession = NULL;
135   s = calloc(1, sizeof(*s));
136   if(!s) {
137     free(sdata);
138     free(quic_tp);
139     return CURLE_OUT_OF_MEMORY;
140   }
141 
142   s->ietf_tls_id = ietf_tls_id;
143   s->valid_until = valid_until;
144   s->earlydata_max = earlydata_max;
145   s->sdata = sdata;
146   s->sdata_len = sdata_len;
147   s->quic_tp = quic_tp;
148   s->quic_tp_len = quic_tp_len;
149   if(alpn) {
150     s->alpn = strdup(alpn);
151     if(!s->alpn) {
152       cf_ssl_scache_sesssion_ldestroy(NULL, s);
153       return CURLE_OUT_OF_MEMORY;
154     }
155   }
156   *psession = s;
157   return CURLE_OK;
158 }
159 
Curl_ssl_session_destroy(struct Curl_ssl_session * s)160 void Curl_ssl_session_destroy(struct Curl_ssl_session *s)
161 {
162   if(s) {
163     /* if in the list, the list destructor takes care of it */
164     if(Curl_node_llist(&s->list))
165       Curl_node_remove(&s->list);
166     else {
167       cf_ssl_scache_sesssion_ldestroy(NULL, s);
168     }
169   }
170 }
171 
cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer * peer)172 static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer)
173 {
174   Curl_llist_destroy(&peer->sessions, NULL);
175   if(peer->sobj) {
176     DEBUGASSERT(peer->sobj_free);
177     if(peer->sobj_free)
178       peer->sobj_free(peer->sobj);
179     peer->sobj = NULL;
180   }
181   peer->sobj_free = NULL;
182   Curl_safefree(peer->clientcert);
183 #ifdef USE_TLS_SRP
184   Curl_safefree(peer->srp_username);
185   Curl_safefree(peer->srp_password);
186 #endif
187   Curl_safefree(peer->ssl_peer_key);
188   peer->age = 0;
189   peer->hmac_set = FALSE;
190 }
191 
cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer * peer,void * sobj,Curl_ssl_scache_obj_dtor * sobj_free)192 static void cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer *peer,
193                                        void *sobj,
194                                        Curl_ssl_scache_obj_dtor *sobj_free)
195 {
196   DEBUGASSERT(peer);
197   if(peer->sobj_free) {
198     peer->sobj_free(peer->sobj);
199   }
200   peer->sobj = sobj;
201   peer->sobj_free = sobj_free;
202 }
203 
204 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,const unsigned char * salt,const unsigned char * hmac)205 cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer,
206                         const char *ssl_peer_key,
207                         const char *clientcert,
208                         const char *srp_username,
209                         const char *srp_password,
210                         const unsigned char *salt,
211                         const unsigned char *hmac)
212 {
213   CURLcode result = CURLE_OUT_OF_MEMORY;
214 
215   DEBUGASSERT(!peer->ssl_peer_key);
216   if(ssl_peer_key) {
217     peer->ssl_peer_key = strdup(ssl_peer_key);
218     if(!peer->ssl_peer_key)
219       goto out;
220     peer->hmac_set = FALSE;
221   }
222   else if(salt && hmac) {
223     memcpy(peer->key_salt, salt, sizeof(peer->key_salt));
224     memcpy(peer->key_hmac, hmac, sizeof(peer->key_hmac));
225     peer->hmac_set = TRUE;
226   }
227   else {
228     result = CURLE_BAD_FUNCTION_ARGUMENT;
229     goto out;
230   }
231   if(clientcert) {
232     peer->clientcert = strdup(clientcert);
233     if(!peer->clientcert)
234       goto out;
235   }
236   if(srp_username) {
237     peer->srp_username = strdup(srp_username);
238     if(!peer->srp_username)
239       goto out;
240   }
241   if(srp_password) {
242     peer->srp_password = strdup(srp_password);
243     if(!peer->srp_password)
244       goto out;
245   }
246   result = CURLE_OK;
247 out:
248   if(result)
249     cf_ssl_scache_clear_peer(peer);
250   return result;
251 }
252 
cf_scache_session_remove(struct Curl_ssl_scache_peer * peer,struct Curl_ssl_session * s)253 static void cf_scache_session_remove(struct Curl_ssl_scache_peer *peer,
254                                      struct Curl_ssl_session *s)
255 {
256   (void)peer;
257   DEBUGASSERT(Curl_node_llist(&s->list) == &peer->sessions);
258   Curl_ssl_session_destroy(s);
259 }
260 
cf_scache_session_expired(struct Curl_ssl_session * s,curl_off_t now)261 static bool cf_scache_session_expired(struct Curl_ssl_session *s,
262                                       curl_off_t now)
263 {
264   return (s->valid_until > 0) && (s->valid_until < now);
265 }
266 
cf_scache_peer_remove_expired(struct Curl_ssl_scache_peer * peer,curl_off_t now)267 static void cf_scache_peer_remove_expired(struct Curl_ssl_scache_peer *peer,
268                                           curl_off_t now)
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(cf_scache_session_expired(s, now))
275       cf_scache_session_remove(peer, s);
276   }
277 }
278 
cf_scache_peer_remove_non13(struct Curl_ssl_scache_peer * peer)279 static void cf_scache_peer_remove_non13(struct Curl_ssl_scache_peer *peer)
280 {
281   struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
282   while(n) {
283     struct Curl_ssl_session *s = Curl_node_elem(n);
284     n = Curl_node_next(n);
285     if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3)
286       cf_scache_session_remove(peer, s);
287   }
288 }
289 
Curl_ssl_scache_create(size_t max_peers,size_t max_sessions_per_peer,struct Curl_ssl_scache ** pscache)290 CURLcode Curl_ssl_scache_create(size_t max_peers,
291                                 size_t max_sessions_per_peer,
292                                 struct Curl_ssl_scache **pscache)
293 {
294   struct Curl_ssl_scache *scache;
295   struct Curl_ssl_scache_peer *peers;
296   size_t i;
297 
298   *pscache = NULL;
299   peers = calloc(max_peers, sizeof(*peers));
300   if(!peers)
301     return CURLE_OUT_OF_MEMORY;
302 
303   scache = calloc(1, sizeof(*scache));
304   if(!scache) {
305     free(peers);
306     return CURLE_OUT_OF_MEMORY;
307   }
308 
309   scache->default_lifetime_secs = (24*60*60); /* 1 day */
310   scache->peer_count = max_peers;
311   scache->peers = peers;
312   scache->age = 1;
313   for(i = 0; i < scache->peer_count; ++i) {
314     scache->peers[i].max_sessions = max_sessions_per_peer;
315     Curl_llist_init(&scache->peers[i].sessions,
316                     cf_ssl_scache_sesssion_ldestroy);
317   }
318 
319   *pscache = scache;
320   return CURLE_OK;
321 }
322 
Curl_ssl_scache_destroy(struct Curl_ssl_scache * scache)323 void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache)
324 {
325   if(scache) {
326     size_t i;
327     for(i = 0; i < scache->peer_count; ++i) {
328       cf_ssl_scache_clear_peer(&scache->peers[i]);
329     }
330     free(scache->peers);
331     free(scache);
332   }
333 }
334 
335 /* Lock shared SSL session data */
Curl_ssl_scache_lock(struct Curl_easy * data)336 void Curl_ssl_scache_lock(struct Curl_easy *data)
337 {
338   if(CURL_SHARE_ssl_scache(data))
339     Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
340 }
341 
342 /* Unlock shared SSL session data */
Curl_ssl_scache_unlock(struct Curl_easy * data)343 void Curl_ssl_scache_unlock(struct Curl_easy *data)
344 {
345   if(CURL_SHARE_ssl_scache(data))
346     Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
347 }
348 
cf_ssl_peer_key_add_path(struct dynbuf * buf,const char * name,char * path)349 static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf,
350                                           const char *name,
351                                           char *path)
352 {
353   if(path && path[0]) {
354     /* We try to add absolute paths, so that the session key can stay
355      * valid when used in another process with different CWD. However,
356      * when a path does not exist, this does not work. Then, we add
357      * the path as is. */
358 #ifdef _WIN32
359     char abspath[_MAX_PATH];
360     if(_fullpath(abspath, path, _MAX_PATH))
361       return Curl_dyn_addf(buf, ":%s-%s", name, abspath);
362 #else
363     if(path[0] != '/') {
364       char *abspath = realpath(path, NULL);
365       if(abspath) {
366         CURLcode r = Curl_dyn_addf(buf, ":%s-%s", name, abspath);
367         (free)(abspath); /* allocated by libc, free without memdebug */
368         return r;
369       }
370     }
371 #endif
372     return Curl_dyn_addf(buf, ":%s-%s", name, path);
373   }
374   return CURLE_OK;
375 }
376 
cf_ssl_peer_key_add_hash(struct dynbuf * buf,const char * name,struct curl_blob * blob)377 static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf,
378                                           const char *name,
379                                           struct curl_blob *blob)
380 {
381   CURLcode r = CURLE_OK;
382   if(blob && blob->len) {
383     unsigned char hash[CURL_SHA256_DIGEST_LENGTH];
384     size_t i;
385 
386     r = Curl_dyn_addf(buf, ":%s-", name);
387     if(r)
388       goto out;
389     r = Curl_sha256it(hash, blob->data, blob->len);
390     if(r)
391       goto out;
392     for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) {
393       r = Curl_dyn_addf(buf, "%02x", hash[i]);
394       if(r)
395         goto out;
396     }
397   }
398 out:
399   return r;
400 }
401 
Curl_ssl_peer_key_make(struct Curl_cfilter * cf,const struct ssl_peer * peer,const char * tls_id,char ** ppeer_key)402 CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
403                                 const struct ssl_peer *peer,
404                                 const char *tls_id,
405                                 char **ppeer_key)
406 {
407   struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
408   struct dynbuf buf;
409   size_t key_len;
410   CURLcode r;
411 
412   *ppeer_key = NULL;
413   Curl_dyn_init(&buf, 10 * 1024);
414 
415   r = Curl_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port);
416   if(r)
417     goto out;
418 
419   switch(peer->transport) {
420   case TRNSPRT_TCP:
421     break;
422   case TRNSPRT_UDP:
423     r = Curl_dyn_add(&buf, ":UDP");
424     break;
425   case TRNSPRT_QUIC:
426     r = Curl_dyn_add(&buf, ":QUIC");
427     break;
428   case TRNSPRT_UNIX:
429     r = Curl_dyn_add(&buf, ":UNIX");
430     break;
431   default:
432     r = Curl_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport);
433     break;
434   }
435   if(r)
436     goto out;
437 
438   if(!ssl->verifypeer) {
439     r = Curl_dyn_add(&buf, ":NO-VRFY-PEER");
440     if(r)
441       goto out;
442   }
443   if(!ssl->verifyhost) {
444     r = Curl_dyn_add(&buf, ":NO-VRFY-HOST");
445     if(r)
446       goto out;
447   }
448   if(ssl->verifystatus) {
449     r = Curl_dyn_add(&buf, ":VRFY-STATUS");
450     if(r)
451       goto out;
452   }
453   if(!ssl->verifypeer || !ssl->verifyhost) {
454     if(cf->conn->bits.conn_to_host) {
455       r = Curl_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);
456       if(r)
457         goto out;
458     }
459     if(cf->conn->bits.conn_to_port) {
460       r = Curl_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port);
461       if(r)
462         goto out;
463     }
464   }
465 
466   if(ssl->version || ssl->version_max) {
467     r = Curl_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version,
468                       (ssl->version_max >> 16));
469     if(r)
470       goto out;
471   }
472   if(ssl->ssl_options) {
473     r = Curl_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options);
474     if(r)
475       goto out;
476   }
477   if(ssl->cipher_list) {
478     r = Curl_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list);
479     if(r)
480       goto out;
481   }
482   if(ssl->cipher_list13) {
483     r = Curl_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13);
484     if(r)
485       goto out;
486   }
487   if(ssl->curves) {
488     r = Curl_dyn_addf(&buf, ":CURVES-%s", ssl->curves);
489     if(r)
490       goto out;
491   }
492   if(ssl->verifypeer) {
493     r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile);
494     if(r)
495       goto out;
496     r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath);
497     if(r)
498       goto out;
499     r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile);
500     if(r)
501       goto out;
502     r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert);
503     if(r)
504       goto out;
505     if(ssl->cert_blob) {
506       r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob);
507       if(r)
508         goto out;
509     }
510     if(ssl->ca_info_blob) {
511       r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob);
512       if(r)
513         goto out;
514     }
515     if(ssl->issuercert_blob) {
516       r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob);
517       if(r)
518         goto out;
519     }
520   }
521   if(ssl->pinned_key && ssl->pinned_key[0]) {
522     r = Curl_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key);
523     if(r)
524       goto out;
525   }
526 
527   if(ssl->clientcert && ssl->clientcert[0]) {
528     r = Curl_dyn_add(&buf, ":CCERT");
529     if(r)
530       goto out;
531   }
532 #ifdef USE_TLS_SRP
533   if(ssl->username || ssl->password) {
534     r = Curl_dyn_add(&buf, ":SRP-AUTH");
535     if(r)
536       goto out;
537   }
538 #endif
539 
540   if(!tls_id || !tls_id[0]) {
541     r = CURLE_FAILED_INIT;
542     goto out;
543   }
544   r = Curl_dyn_addf(&buf, ":IMPL-%s", tls_id);
545   if(r)
546     goto out;
547 
548   *ppeer_key = Curl_dyn_take(&buf, &key_len);
549   /* we just added printable char, and dynbuf always 0 terminates,
550    * no need to track length */
551 
552 
553 out:
554   Curl_dyn_free(&buf);
555   return r;
556 }
557 
cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer * peer,struct ssl_primary_config * conn_config)558 static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer,
559                                      struct ssl_primary_config *conn_config)
560 {
561   if(!conn_config) {
562     if(peer->clientcert)
563       return FALSE;
564 #ifdef USE_TLS_SRP
565     if(peer->srp_username || peer->srp_password)
566       return FALSE;
567 #endif
568     return TRUE;
569   }
570   else if(!Curl_safecmp(peer->clientcert, conn_config->clientcert))
571     return FALSE;
572 #ifdef USE_TLS_SRP
573    if(Curl_timestrcmp(peer->srp_username, conn_config->username) ||
574       Curl_timestrcmp(peer->srp_password, conn_config->password))
575      return FALSE;
576 #endif
577   return TRUE;
578 }
579 
580 static CURLcode
cf_ssl_find_peer_by_key(struct Curl_easy * data,struct Curl_ssl_scache * scache,const char * ssl_peer_key,struct ssl_primary_config * conn_config,struct Curl_ssl_scache_peer ** ppeer)581 cf_ssl_find_peer_by_key(struct Curl_easy *data,
582                         struct Curl_ssl_scache *scache,
583                         const char *ssl_peer_key,
584                         struct ssl_primary_config *conn_config,
585                         struct Curl_ssl_scache_peer **ppeer)
586 {
587   size_t i, peer_key_len = 0;
588   CURLcode result = CURLE_OK;
589 
590   *ppeer = NULL;
591   /* check for entries with known peer_key */
592   for(i = 0; scache && i < scache->peer_count; i++) {
593     if(scache->peers[i].ssl_peer_key &&
594        strcasecompare(ssl_peer_key, scache->peers[i].ssl_peer_key) &&
595        cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
596       /* yes, we have a cached session for this! */
597       *ppeer = &scache->peers[i];
598       goto out;
599     }
600   }
601   /* check for entries with HMAC set but no known peer_key */
602   for(i = 0; scache && i < scache->peer_count; i++) {
603     if(!scache->peers[i].ssl_peer_key &&
604        scache->peers[i].hmac_set &&
605        cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
606       /* possible entry with unknown peer_key, check hmac */
607       unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH];
608       if(!peer_key_len) /* we are lazy */
609         peer_key_len = strlen(ssl_peer_key);
610       result = Curl_hmacit(&Curl_HMAC_SHA256,
611                            scache->peers[i].key_salt,
612                            sizeof(scache->peers[i].key_salt),
613                            (const unsigned char *)ssl_peer_key,
614                            peer_key_len,
615                            my_hmac);
616       if(result)
617         goto out;
618       if(!memcmp(scache->peers[i].key_hmac, my_hmac, sizeof(my_hmac))) {
619         /* remember peer_key for future lookups */
620         CURL_TRC_SSLS(data, "peer entry %zu key recovered: %s",
621                       i, ssl_peer_key);
622         scache->peers[i].ssl_peer_key = strdup(ssl_peer_key);
623         if(!scache->peers[i].ssl_peer_key) {
624           result = CURLE_OUT_OF_MEMORY;
625           goto out;
626         }
627         *ppeer = &scache->peers[i];
628         goto out;
629       }
630     }
631   }
632   CURL_TRC_SSLS(data, "peer not found for %s", ssl_peer_key);
633 out:
634   return result;
635 }
636 
637 static struct Curl_ssl_scache_peer *
cf_ssl_get_free_peer(struct Curl_ssl_scache * scache)638 cf_ssl_get_free_peer(struct Curl_ssl_scache *scache)
639 {
640   struct Curl_ssl_scache_peer *peer = NULL;
641   size_t i;
642 
643   /* find empty or oldest peer */
644   for(i = 0; i < scache->peer_count; ++i) {
645     /* free peer entry? */
646     if(!scache->peers[i].ssl_peer_key && !scache->peers[i].hmac_set) {
647       peer = &scache->peers[i];
648       break;
649     }
650     /* peer without sessions and obj */
651     if(!scache->peers[i].sobj &&
652        !Curl_llist_count(&scache->peers[i].sessions)) {
653       peer = &scache->peers[i];
654       break;
655     }
656     /* remember "oldest" peer */
657     if(!peer || (scache->peers[i].age < peer->age)) {
658       peer = &scache->peers[i];
659     }
660   }
661   DEBUGASSERT(peer);
662   if(peer)
663     cf_ssl_scache_clear_peer(peer);
664   return peer;
665 }
666 
667 static CURLcode
cf_ssl_add_peer(struct Curl_easy * data,struct Curl_ssl_scache * scache,const char * ssl_peer_key,struct ssl_primary_config * conn_config,struct Curl_ssl_scache_peer ** ppeer)668 cf_ssl_add_peer(struct Curl_easy *data,
669                 struct Curl_ssl_scache *scache,
670                 const char *ssl_peer_key,
671                 struct ssl_primary_config *conn_config,
672                 struct Curl_ssl_scache_peer **ppeer)
673 {
674   struct Curl_ssl_scache_peer *peer = NULL;
675   CURLcode result = CURLE_OK;
676 
677   *ppeer = NULL;
678   if(ssl_peer_key) {
679     result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
680                                      &peer);
681     if(result || !scache->peer_count)
682       return result;
683   }
684 
685   if(peer) {
686     *ppeer = peer;
687     return CURLE_OK;
688   }
689 
690   peer = cf_ssl_get_free_peer(scache);
691   if(peer) {
692     const char *ccert = conn_config ? conn_config->clientcert : NULL;
693     const char *username = NULL, *password = NULL;
694 #ifdef USE_TLS_SRP
695     username = conn_config ? conn_config->username : NULL;
696     password = conn_config ? conn_config->password : NULL;
697 #endif
698     result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert,
699                                      username, password, NULL, NULL);
700     if(result)
701       goto out;
702     /* all ready */
703     *ppeer = peer;
704     result = CURLE_OK;
705   }
706 
707 out:
708   if(result) {
709     cf_ssl_scache_clear_peer(peer);
710   }
711   return result;
712 }
713 
cf_scache_peer_add_session(struct Curl_ssl_scache_peer * peer,struct Curl_ssl_session * s,curl_off_t now)714 static void cf_scache_peer_add_session(struct Curl_ssl_scache_peer *peer,
715                                        struct Curl_ssl_session *s,
716                                        curl_off_t now)
717 {
718   /* A session not from TLSv1.3 replaces all other. */
719   if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3) {
720     Curl_llist_destroy(&peer->sessions, NULL);
721     Curl_llist_append(&peer->sessions, s, &s->list);
722   }
723   else {
724     /* Expire existing, append, trim from head to obey max_sessions */
725     cf_scache_peer_remove_expired(peer, now);
726     cf_scache_peer_remove_non13(peer);
727     Curl_llist_append(&peer->sessions, s, &s->list);
728     while(Curl_llist_count(&peer->sessions) > peer->max_sessions) {
729       Curl_node_remove(Curl_llist_head(&peer->sessions));
730     }
731   }
732 }
733 
cf_scache_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)734 static CURLcode cf_scache_add_session(struct Curl_cfilter *cf,
735                                       struct Curl_easy *data,
736                                       struct Curl_ssl_scache *scache,
737                                       const char *ssl_peer_key,
738                                       struct Curl_ssl_session *s)
739 {
740   struct Curl_ssl_scache_peer *peer = NULL;
741   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
742   CURLcode result = CURLE_OUT_OF_MEMORY;
743   curl_off_t now = (curl_off_t)time(NULL);
744   curl_off_t max_lifetime;
745 
746   if(!scache || !scache->peer_count) {
747     Curl_ssl_session_destroy(s);
748     return CURLE_OK;
749   }
750 
751   if(s->valid_until <= 0)
752     s->valid_until = now + scache->default_lifetime_secs;
753 
754   max_lifetime = (s->ietf_tls_id == CURL_IETF_PROTO_TLS1_3) ?
755                  CURL_SCACHE_MAX_13_LIFETIME_SEC :
756                  CURL_SCACHE_MAX_12_LIFETIME_SEC;
757   if(s->valid_until > (now + max_lifetime))
758     s->valid_until = now + max_lifetime;
759 
760   if(cf_scache_session_expired(s, now)) {
761     CURL_TRC_SSLS(data, "add, session already expired");
762     Curl_ssl_session_destroy(s);
763     return CURLE_OK;
764   }
765 
766   result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer);
767   if(result || !peer) {
768     CURL_TRC_SSLS(data, "unable to add scache peer: %d", result);
769     Curl_ssl_session_destroy(s);
770     goto out;
771   }
772 
773   cf_scache_peer_add_session(peer, s, now);
774 
775 out:
776   if(result) {
777     failf(data, "[SCACHE] failed to add session for %s, error=%d",
778           ssl_peer_key, result);
779   }
780   else
781     CURL_TRC_SSLS(data, "added session for %s [proto=0x%x, "
782                   "valid_secs=%" FMT_OFF_T ", alpn=%s, earlydata=%zu, "
783                   "quic_tp=%s], peer has %zu sessions now",
784                   ssl_peer_key, s->ietf_tls_id, s->valid_until - now,
785                   s->alpn, s->earlydata_max, s->quic_tp ? "yes" : "no",
786                   peer ? Curl_llist_count(&peer->sessions) : 0);
787   return result;
788 }
789 
Curl_ssl_scache_put(struct Curl_cfilter * cf,struct Curl_easy * data,const char * ssl_peer_key,struct Curl_ssl_session * s)790 CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf,
791                              struct Curl_easy *data,
792                              const char *ssl_peer_key,
793                              struct Curl_ssl_session *s)
794 {
795   struct Curl_ssl_scache *scache = data->state.ssl_scache;
796   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
797   CURLcode result;
798   DEBUGASSERT(ssl_config);
799 
800   if(!scache || !ssl_config->primary.cache_session) {
801     Curl_ssl_session_destroy(s);
802     return CURLE_OK;
803   }
804 
805   Curl_ssl_scache_lock(data);
806   result = cf_scache_add_session(cf, data, scache, ssl_peer_key, s);
807   Curl_ssl_scache_unlock(data);
808   return result;
809 }
810 
Curl_ssl_scache_return(struct Curl_cfilter * cf,struct Curl_easy * data,const char * ssl_peer_key,struct Curl_ssl_session * s)811 void Curl_ssl_scache_return(struct Curl_cfilter *cf,
812                            struct Curl_easy *data,
813                            const char *ssl_peer_key,
814                            struct Curl_ssl_session *s)
815 {
816   /* See RFC 8446 C.4:
817    * "Clients SHOULD NOT reuse a ticket for multiple connections." */
818   if(s && s->ietf_tls_id < 0x304)
819     (void)Curl_ssl_scache_put(cf, data, ssl_peer_key, s);
820   else
821     Curl_ssl_session_destroy(s);
822 }
823 
Curl_ssl_scache_take(struct Curl_cfilter * cf,struct Curl_easy * data,const char * ssl_peer_key,struct Curl_ssl_session ** ps)824 CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf,
825                               struct Curl_easy *data,
826                               const char *ssl_peer_key,
827                               struct Curl_ssl_session **ps)
828 {
829   struct Curl_ssl_scache *scache = data->state.ssl_scache;
830   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
831   struct Curl_ssl_scache_peer *peer = NULL;
832   struct Curl_llist_node *n;
833   struct Curl_ssl_session *s = NULL;
834   CURLcode result;
835 
836   *ps = NULL;
837   if(!scache)
838     return CURLE_OK;
839 
840   Curl_ssl_scache_lock(data);
841   result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
842                                    &peer);
843   if(!result && peer) {
844     cf_scache_peer_remove_expired(peer, (curl_off_t)time(NULL));
845     n = Curl_llist_head(&peer->sessions);
846     if(n) {
847       s = Curl_node_take_elem(n);
848       (scache->age)++;            /* increase general age */
849       peer->age = scache->age; /* set this as used in this age */
850     }
851   }
852   Curl_ssl_scache_unlock(data);
853   if(s) {
854     *ps = s;
855     CURL_TRC_SSLS(data, "took session for %s [proto=0x%x, "
856                   "alpn=%s, earlydata=%zu, quic_tp=%s], %zu sessions remain",
857                   ssl_peer_key, s->ietf_tls_id, s->alpn,
858                   s->earlydata_max, s->quic_tp ? "yes" : "no",
859                   Curl_llist_count(&peer->sessions));
860   }
861   else {
862     CURL_TRC_SSLS(data, "no cached session for %s", ssl_peer_key);
863   }
864   return result;
865 }
866 
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)867 CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
868                                  struct Curl_easy *data,
869                                  const char *ssl_peer_key,
870                                  void *sobj,
871                                  Curl_ssl_scache_obj_dtor *sobj_free)
872 {
873   struct Curl_ssl_scache *scache = data->state.ssl_scache;
874   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
875   struct Curl_ssl_scache_peer *peer = NULL;
876   CURLcode result;
877 
878   DEBUGASSERT(sobj);
879   DEBUGASSERT(sobj_free);
880 
881   result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer);
882   if(result || !peer) {
883     CURL_TRC_SSLS(data, "unable to add scache peer: %d", result);
884     goto out;
885   }
886 
887   cf_ssl_scache_peer_set_obj(peer, sobj, sobj_free);
888   sobj = NULL;  /* peer took ownership */
889 
890 out:
891   if(sobj && sobj_free)
892     sobj_free(sobj);
893   return result;
894 }
895 
Curl_ssl_scache_get_obj(struct Curl_cfilter * cf,struct Curl_easy * data,const char * ssl_peer_key,void ** sobj)896 bool Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
897                              struct Curl_easy *data,
898                              const char *ssl_peer_key,
899                              void **sobj)
900 {
901   struct Curl_ssl_scache *scache = data->state.ssl_scache;
902   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
903   struct Curl_ssl_scache_peer *peer = NULL;
904   CURLcode result;
905 
906   *sobj = NULL;
907   if(!scache)
908     return FALSE;
909 
910   result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
911                                    &peer);
912   if(result)
913     return FALSE;
914 
915   if(peer)
916     *sobj = peer->sobj;
917 
918   CURL_TRC_SSLS(data, "%s cached session for '%s'",
919                 *sobj ? "Found" : "No", ssl_peer_key);
920   return !!*sobj;
921 }
922 
Curl_ssl_scache_remove_all(struct Curl_cfilter * cf,struct Curl_easy * data,const char * ssl_peer_key)923 void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
924                                 struct Curl_easy *data,
925                                 const char *ssl_peer_key)
926 {
927   struct Curl_ssl_scache *scache = data->state.ssl_scache;
928   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
929   struct Curl_ssl_scache_peer *peer = NULL;
930   CURLcode result;
931 
932   (void)cf;
933   if(!scache)
934     return;
935 
936   Curl_ssl_scache_lock(data);
937   result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
938                                    &peer);
939   if(!result && peer)
940     cf_ssl_scache_clear_peer(peer);
941   Curl_ssl_scache_unlock(data);
942 }
943 
944 #ifdef USE_SSLS_EXPORT
945 
946 #define CURL_SSL_TICKET_MAX   (16*1024)
947 
cf_ssl_scache_peer_set_hmac(struct Curl_ssl_scache_peer * peer)948 static CURLcode cf_ssl_scache_peer_set_hmac(struct Curl_ssl_scache_peer *peer)
949 {
950   CURLcode result;
951 
952   DEBUGASSERT(peer);
953   if(!peer->ssl_peer_key)
954     return CURLE_BAD_FUNCTION_ARGUMENT;
955 
956   result = Curl_rand(NULL, peer->key_salt, sizeof(peer->key_salt));
957   if(result)
958     return result;
959 
960   result = Curl_hmacit(&Curl_HMAC_SHA256,
961                        peer->key_salt, sizeof(peer->key_salt),
962                        (const unsigned char *)peer->ssl_peer_key,
963                        strlen(peer->ssl_peer_key),
964                        peer->key_hmac);
965   if(!result)
966     peer->hmac_set = TRUE;
967   return result;
968 }
969 
970 static CURLcode
cf_ssl_find_peer_by_hmac(struct Curl_ssl_scache * scache,const unsigned char * salt,const unsigned char * hmac,struct Curl_ssl_scache_peer ** ppeer)971 cf_ssl_find_peer_by_hmac(struct Curl_ssl_scache *scache,
972                          const unsigned char *salt,
973                          const unsigned char *hmac,
974                          struct Curl_ssl_scache_peer **ppeer)
975 {
976   size_t i;
977   CURLcode result = CURLE_OK;
978 
979   *ppeer = NULL;
980   /* look for an entry that matches salt+hmac exactly or has a known
981    * ssl_peer_key which salt+hmac's to the same. */
982   for(i = 0; scache && i < scache->peer_count; i++) {
983     struct Curl_ssl_scache_peer *peer = &scache->peers[i];
984     if(!cf_ssl_scache_match_auth(peer, NULL))
985       continue;
986     if(scache->peers[i].hmac_set &&
987        !memcmp(peer->key_salt, salt, sizeof(peer->key_salt)) &&
988        !memcmp(peer->key_hmac, hmac, sizeof(peer->key_hmac))) {
989       /* found exact match, return */
990       *ppeer = peer;
991       goto out;
992     }
993     else if(peer->ssl_peer_key) {
994       unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH];
995       /* compute hmac for the passed salt */
996       result = Curl_hmacit(&Curl_HMAC_SHA256,
997                            salt, sizeof(peer->key_salt),
998                            (const unsigned char *)peer->ssl_peer_key,
999                            strlen(peer->ssl_peer_key),
1000                            my_hmac);
1001       if(result)
1002         goto out;
1003       if(!memcmp(my_hmac, hmac, sizeof(my_hmac))) {
1004         /* cryptohash match, take over salt+hmac if no set and return */
1005         if(!peer->hmac_set) {
1006           memcpy(peer->key_salt, salt, sizeof(peer->key_salt));
1007           memcpy(peer->key_hmac, hmac, sizeof(peer->key_hmac));
1008           peer->hmac_set = TRUE;
1009         }
1010         *ppeer = peer;
1011         goto out;
1012       }
1013     }
1014   }
1015 out:
1016   return result;
1017 }
1018 
Curl_ssl_session_import(struct Curl_easy * data,const char * ssl_peer_key,const unsigned char * shmac,size_t shmac_len,const unsigned char * sdata,size_t sdata_len)1019 CURLcode Curl_ssl_session_import(struct Curl_easy *data,
1020                                  const char *ssl_peer_key,
1021                                  const unsigned char *shmac, size_t shmac_len,
1022                                  const unsigned char *sdata, size_t sdata_len)
1023 {
1024   struct Curl_ssl_scache *scache = data->state.ssl_scache;
1025   struct Curl_ssl_scache_peer *peer = NULL;
1026   struct Curl_ssl_session *s = NULL;
1027   bool locked = FALSE;
1028   CURLcode r;
1029 
1030   if(!scache) {
1031     r = CURLE_BAD_FUNCTION_ARGUMENT;
1032     goto out;
1033   }
1034   if(!ssl_peer_key && (!shmac || !shmac_len)) {
1035     r = CURLE_BAD_FUNCTION_ARGUMENT;
1036     goto out;
1037   }
1038 
1039   r = Curl_ssl_session_unpack(data, sdata, sdata_len, &s);
1040   if(r)
1041     goto out;
1042 
1043   Curl_ssl_scache_lock(data);
1044   locked = TRUE;
1045 
1046   if(ssl_peer_key) {
1047     r = cf_ssl_add_peer(data, scache, ssl_peer_key, NULL, &peer);
1048     if(r)
1049       goto out;
1050   }
1051   else if(shmac_len != (sizeof(peer->key_salt) + sizeof(peer->key_hmac))) {
1052     /* Either salt+hmac was garbled by caller or is from a curl version
1053      * that does things differently */
1054     r = CURLE_BAD_FUNCTION_ARGUMENT;
1055     goto out;
1056   }
1057   else {
1058     const unsigned char *salt = shmac;
1059     const unsigned char *hmac = shmac + sizeof(peer->key_salt);
1060 
1061     r = cf_ssl_find_peer_by_hmac(scache, salt, hmac, &peer);
1062     if(r)
1063       goto out;
1064     if(!peer) {
1065       peer = cf_ssl_get_free_peer(scache);
1066       if(peer) {
1067         r = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL,
1068                                     NULL, NULL, salt, hmac);
1069         if(r)
1070           goto out;
1071       }
1072     }
1073   }
1074 
1075   if(peer) {
1076     cf_scache_peer_add_session(peer, s, time(NULL));
1077     s = NULL; /* peer is now owner */
1078     CURL_TRC_SSLS(data, "successfully imported ticket for peer %s, now "
1079                   "with %zu tickets",
1080                   peer->ssl_peer_key ? peer->ssl_peer_key : "without key",
1081                   Curl_llist_count(&peer->sessions));
1082   }
1083 
1084 out:
1085   if(locked)
1086     Curl_ssl_scache_unlock(data);
1087   Curl_ssl_session_destroy(s);
1088   return r;
1089 }
1090 
Curl_ssl_session_export(struct Curl_easy * data,curl_ssls_export_cb * export_fn,void * userptr)1091 CURLcode Curl_ssl_session_export(struct Curl_easy *data,
1092                                  curl_ssls_export_cb *export_fn,
1093                                  void *userptr)
1094 {
1095   struct Curl_ssl_scache *scache = data->state.ssl_scache;
1096   struct Curl_ssl_scache_peer *peer;
1097   struct dynbuf sbuf, hbuf;
1098   struct Curl_llist_node *n;
1099   size_t i, npeers = 0, ntickets = 0;
1100   curl_off_t now = time(NULL);
1101   CURLcode r = CURLE_OK;
1102 
1103   if(!export_fn)
1104     return CURLE_BAD_FUNCTION_ARGUMENT;
1105   if(!scache)
1106     return CURLE_OK;
1107 
1108   Curl_ssl_scache_lock(data);
1109 
1110   Curl_dyn_init(&hbuf, (CURL_SHA256_DIGEST_LENGTH * 2) + 1);
1111   Curl_dyn_init(&sbuf, CURL_SSL_TICKET_MAX);
1112 
1113   for(i = 0; scache && i < scache->peer_count; i++) {
1114     peer = &scache->peers[i];
1115     if(!peer->ssl_peer_key && !peer->hmac_set)
1116       continue;  /* skip free entry */
1117     if(peer->clientcert || peer->srp_username || peer->srp_password)
1118       continue;  /* not exporting those */
1119 
1120     Curl_dyn_reset(&hbuf);
1121     cf_scache_peer_remove_expired(peer, now);
1122     n = Curl_llist_head(&peer->sessions);
1123     if(n)
1124       ++npeers;
1125     while(n) {
1126       struct Curl_ssl_session *s = Curl_node_elem(n);
1127       if(!peer->hmac_set) {
1128         r = cf_ssl_scache_peer_set_hmac(peer);
1129         if(r)
1130           goto out;
1131       }
1132       if(!Curl_dyn_len(&hbuf)) {
1133         r = Curl_dyn_addn(&hbuf, peer->key_salt, sizeof(peer->key_salt));
1134         if(r)
1135           goto out;
1136         r = Curl_dyn_addn(&hbuf, peer->key_hmac, sizeof(peer->key_hmac));
1137         if(r)
1138           goto out;
1139       }
1140       Curl_dyn_reset(&sbuf);
1141       r = Curl_ssl_session_pack(data, s, &sbuf);
1142       if(r)
1143         goto out;
1144 
1145       r = export_fn(data, userptr, peer->ssl_peer_key,
1146                     Curl_dyn_uptr(&hbuf), Curl_dyn_len(&hbuf),
1147                     Curl_dyn_uptr(&sbuf), Curl_dyn_len(&sbuf),
1148                     s->valid_until, s->ietf_tls_id,
1149                     s->alpn, s->earlydata_max);
1150       if(r)
1151         goto out;
1152       ++ntickets;
1153       n = Curl_node_next(n);
1154     }
1155 
1156   }
1157   r = CURLE_OK;
1158   CURL_TRC_SSLS(data, "exported %zu session tickets for %zu peers",
1159                 ntickets, npeers);
1160 
1161 out:
1162   Curl_ssl_scache_unlock(data);
1163   Curl_dyn_free(&hbuf);
1164   Curl_dyn_free(&sbuf);
1165   return r;
1166 }
1167 
1168 #endif /* USE_SSLS_EXPORT */
1169 
1170 #endif /* USE_SSL */
1171