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 if(s->quic_tp) {
107 free((void *)s->quic_tp);
108 s->quic_tp = NULL;
109 }
110 s->quic_tp_len = 0;
111 s->ietf_tls_id = 0;
112 s->time_received = 0;
113 s->lifetime_secs = 0;
114 Curl_safefree(s->alpn);
115 }
116
cf_ssl_scache_sesssion_ldestroy(void * udata,void * s)117 static void cf_ssl_scache_sesssion_ldestroy(void *udata, void *s)
118 {
119 (void)udata;
120 cf_ssl_scache_clear_session(s);
121 free(s);
122 }
123
124 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)125 Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len,
126 int ietf_tls_id, const char *alpn,
127 curl_off_t time_received, long lifetime_secs,
128 size_t earlydata_max,
129 struct Curl_ssl_session **psession)
130 {
131 return Curl_ssl_session_create2(sdata, sdata_len, ietf_tls_id, alpn,
132 time_received, lifetime_secs,
133 earlydata_max, NULL, 0, psession);
134 }
135
136 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)137 Curl_ssl_session_create2(unsigned char *sdata, size_t sdata_len,
138 int ietf_tls_id, const char *alpn,
139 curl_off_t time_received, long lifetime_secs,
140 size_t earlydata_max,
141 unsigned char *quic_tp, size_t quic_tp_len,
142 struct Curl_ssl_session **psession)
143 {
144 struct Curl_ssl_session *s;
145
146 if(!sdata || !sdata_len) {
147 free(sdata);
148 return CURLE_BAD_FUNCTION_ARGUMENT;
149 }
150
151 *psession = NULL;
152 s = calloc(1, sizeof(*s));
153 if(!s) {
154 free(sdata);
155 free(quic_tp);
156 return CURLE_OUT_OF_MEMORY;
157 }
158
159 s->ietf_tls_id = ietf_tls_id;
160 s->time_received = time_received;
161 if(lifetime_secs < 0)
162 lifetime_secs = -1; /* unknown */
163 else if((s->ietf_tls_id == CURL_IETF_PROTO_TLS1_3) &&
164 (lifetime_secs > CURL_SCACHE_MAX_13_LIFETIME_SEC))
165 lifetime_secs = CURL_SCACHE_MAX_13_LIFETIME_SEC;
166 else if(lifetime_secs > CURL_SCACHE_MAX_12_LIFETIME_SEC)
167 lifetime_secs = CURL_SCACHE_MAX_12_LIFETIME_SEC;
168
169 s->lifetime_secs = (int)lifetime_secs;
170 s->earlydata_max = earlydata_max;
171 s->sdata = sdata;
172 s->sdata_len = sdata_len;
173 s->quic_tp = quic_tp;
174 s->quic_tp_len = quic_tp_len;
175 if(alpn) {
176 s->alpn = strdup(alpn);
177 if(!s->alpn) {
178 cf_ssl_scache_sesssion_ldestroy(NULL, s);
179 return CURLE_OUT_OF_MEMORY;
180 }
181 }
182 *psession = s;
183 return CURLE_OK;
184 }
185
Curl_ssl_session_destroy(struct Curl_ssl_session * s)186 void Curl_ssl_session_destroy(struct Curl_ssl_session *s)
187 {
188 if(s) {
189 /* if in the list, the list destructor takes care of it */
190 if(Curl_node_llist(&s->list))
191 Curl_node_remove(&s->list);
192 else {
193 cf_ssl_scache_sesssion_ldestroy(NULL, s);
194 }
195 }
196 }
197
cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer * peer)198 static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer)
199 {
200 Curl_llist_destroy(&peer->sessions, NULL);
201 if(peer->sobj) {
202 DEBUGASSERT(peer->sobj_free);
203 if(peer->sobj_free)
204 peer->sobj_free(peer->sobj);
205 peer->sobj = NULL;
206 }
207 peer->sobj_free = NULL;
208 Curl_safefree(peer->clientcert);
209 #ifdef USE_TLS_SRP
210 Curl_safefree(peer->srp_username);
211 Curl_safefree(peer->srp_password);
212 #endif
213 Curl_safefree(peer->ssl_peer_key);
214 peer->age = 0;
215 peer->hmac_set = FALSE;
216 }
217
cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer * peer,void * sobj,Curl_ssl_scache_obj_dtor * sobj_free)218 static void cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer *peer,
219 void *sobj,
220 Curl_ssl_scache_obj_dtor *sobj_free)
221 {
222 DEBUGASSERT(peer);
223 if(peer->sobj_free) {
224 peer->sobj_free(peer->sobj);
225 }
226 peer->sobj = sobj;
227 peer->sobj_free = sobj_free;
228 }
229
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)230 static CURLcode cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer,
231 const char *ssl_peer_key,
232 const char *clientcert,
233 const char *srp_username,
234 const char *srp_password)
235 {
236 CURLcode result = CURLE_OUT_OF_MEMORY;
237
238 DEBUGASSERT(!peer->ssl_peer_key);
239 peer->ssl_peer_key = strdup(ssl_peer_key);
240 if(!peer->ssl_peer_key)
241 goto out;
242 if(clientcert) {
243 peer->clientcert = strdup(clientcert);
244 if(!peer->clientcert)
245 goto out;
246 }
247 if(srp_username) {
248 peer->srp_username = strdup(srp_username);
249 if(!peer->srp_username)
250 goto out;
251 }
252 if(srp_password) {
253 peer->srp_password = strdup(srp_password);
254 if(!peer->srp_password)
255 goto out;
256 }
257 result = CURLE_OK;
258 out:
259 if(result)
260 cf_ssl_scache_clear_peer(peer);
261 return result;
262 }
263
cf_scache_session_remove(struct Curl_ssl_scache_peer * peer,struct Curl_ssl_session * s)264 static void cf_scache_session_remove(struct Curl_ssl_scache_peer *peer,
265 struct Curl_ssl_session *s)
266 {
267 (void)peer;
268 DEBUGASSERT(Curl_node_llist(&s->list) == &peer->sessions);
269 Curl_ssl_session_destroy(s);
270 }
271
cf_scache_session_expired(struct Curl_ssl_session * s,curl_off_t now)272 static bool cf_scache_session_expired(struct Curl_ssl_session *s,
273 curl_off_t now)
274 {
275 return (s->lifetime_secs > 0 &&
276 (s->time_received + s->lifetime_secs) < now);
277 }
278
cf_scache_peer_remove_expired(struct Curl_ssl_scache_peer * peer,curl_off_t now)279 static void cf_scache_peer_remove_expired(struct Curl_ssl_scache_peer *peer,
280 curl_off_t now)
281 {
282 struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
283 while(n) {
284 struct Curl_ssl_session *s = Curl_node_elem(n);
285 n = Curl_node_next(n);
286 if(cf_scache_session_expired(s, now))
287 cf_scache_session_remove(peer, s);
288 }
289 }
290
cf_scache_peer_remove_non13(struct Curl_ssl_scache_peer * peer)291 static void cf_scache_peer_remove_non13(struct Curl_ssl_scache_peer *peer)
292 {
293 struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
294 while(n) {
295 struct Curl_ssl_session *s = Curl_node_elem(n);
296 n = Curl_node_next(n);
297 if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3)
298 cf_scache_session_remove(peer, s);
299 }
300 }
301
Curl_ssl_scache_create(size_t max_peers,size_t max_sessions_per_peer,struct Curl_ssl_scache ** pscache)302 CURLcode Curl_ssl_scache_create(size_t max_peers,
303 size_t max_sessions_per_peer,
304 struct Curl_ssl_scache **pscache)
305 {
306 struct Curl_ssl_scache *scache;
307 struct Curl_ssl_scache_peer *peers;
308 size_t i;
309
310 *pscache = NULL;
311 peers = calloc(max_peers, sizeof(*peers));
312 if(!peers)
313 return CURLE_OUT_OF_MEMORY;
314
315 scache = calloc(1, sizeof(*scache));
316 if(!scache) {
317 free(peers);
318 return CURLE_OUT_OF_MEMORY;
319 }
320
321 scache->default_lifetime_secs = (24*60*60); /* 1 day */
322 scache->peer_count = max_peers;
323 scache->peers = peers;
324 scache->age = 1;
325 for(i = 0; i < scache->peer_count; ++i) {
326 scache->peers[i].max_sessions = max_sessions_per_peer;
327 Curl_llist_init(&scache->peers[i].sessions,
328 cf_ssl_scache_sesssion_ldestroy);
329 }
330
331 *pscache = scache;
332 return CURLE_OK;
333 }
334
Curl_ssl_scache_destroy(struct Curl_ssl_scache * scache)335 void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache)
336 {
337 if(scache) {
338 size_t i;
339 for(i = 0; i < scache->peer_count; ++i) {
340 cf_ssl_scache_clear_peer(&scache->peers[i]);
341 }
342 free(scache->peers);
343 free(scache);
344 }
345 }
346
347 /* Lock shared SSL session data */
Curl_ssl_scache_lock(struct Curl_easy * data)348 void Curl_ssl_scache_lock(struct Curl_easy *data)
349 {
350 if(CURL_SHARE_ssl_scache(data))
351 Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
352 }
353
354 /* Unlock shared SSL session data */
Curl_ssl_scache_unlock(struct Curl_easy * data)355 void Curl_ssl_scache_unlock(struct Curl_easy *data)
356 {
357 if(CURL_SHARE_ssl_scache(data))
358 Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
359 }
360
cf_ssl_peer_key_add_path(struct dynbuf * buf,const char * name,char * path)361 static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf,
362 const char *name,
363 char *path)
364 {
365 if(path && path[0]) {
366 /* We try to add absolute paths, so that the session key can stay
367 * valid when used in another process with different CWD. However,
368 * when a path does not exist, this does not work. Then, we add
369 * the path as is. */
370 #ifdef _WIN32
371 char abspath[_MAX_PATH];
372 if(_fullpath(abspath, path, _MAX_PATH))
373 return Curl_dyn_addf(buf, ":%s-%s", name, abspath);
374 #else
375 if(path[0] != '/') {
376 char *abspath = realpath(path, NULL);
377 if(abspath) {
378 CURLcode r = Curl_dyn_addf(buf, ":%s-%s", name, abspath);
379 (free)(abspath); /* allocated by libc, free without memdebug */
380 return r;
381 }
382 }
383 #endif
384 return Curl_dyn_addf(buf, ":%s-%s", name, path);
385 }
386 return CURLE_OK;
387 }
388
cf_ssl_peer_key_add_hash(struct dynbuf * buf,const char * name,struct curl_blob * blob)389 static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf,
390 const char *name,
391 struct curl_blob *blob)
392 {
393 CURLcode r = CURLE_OK;
394 if(blob && blob->len) {
395 unsigned char hash[CURL_SHA256_DIGEST_LENGTH];
396 size_t i;
397
398 r = Curl_dyn_addf(buf, ":%s-", name);
399 if(r)
400 goto out;
401 r = Curl_sha256it(hash, blob->data, blob->len);
402 if(r)
403 goto out;
404 for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) {
405 r = Curl_dyn_addf(buf, "%02x", hash[i]);
406 if(r)
407 goto out;
408 }
409 }
410 out:
411 return r;
412 }
413
Curl_ssl_peer_key_make(struct Curl_cfilter * cf,const struct ssl_peer * peer,const char * tls_id,char ** ppeer_key)414 CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
415 const struct ssl_peer *peer,
416 const char *tls_id,
417 char **ppeer_key)
418 {
419 struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
420 struct dynbuf buf;
421 size_t key_len;
422 CURLcode r;
423
424 *ppeer_key = NULL;
425 Curl_dyn_init(&buf, 10 * 1024);
426
427 r = Curl_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port);
428 if(r)
429 goto out;
430
431 switch(peer->transport) {
432 case TRNSPRT_TCP:
433 break;
434 case TRNSPRT_UDP:
435 r = Curl_dyn_add(&buf, ":UDP");
436 break;
437 case TRNSPRT_QUIC:
438 r = Curl_dyn_add(&buf, ":QUIC");
439 break;
440 case TRNSPRT_UNIX:
441 r = Curl_dyn_add(&buf, ":UNIX");
442 break;
443 default:
444 r = Curl_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport);
445 break;
446 }
447 if(r)
448 goto out;
449
450 if(!ssl->verifypeer) {
451 r = Curl_dyn_add(&buf, ":NO-VRFY-PEER");
452 if(r)
453 goto out;
454 }
455 if(!ssl->verifyhost) {
456 r = Curl_dyn_add(&buf, ":NO-VRFY-HOST");
457 if(r)
458 goto out;
459 }
460 if(ssl->verifystatus) {
461 r = Curl_dyn_add(&buf, ":VRFY-STATUS");
462 if(r)
463 goto out;
464 }
465 if(!ssl->verifypeer || !ssl->verifyhost) {
466 if(cf->conn->bits.conn_to_host) {
467 r = Curl_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);
468 if(r)
469 goto out;
470 }
471 if(cf->conn->bits.conn_to_port) {
472 r = Curl_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port);
473 if(r)
474 goto out;
475 }
476 }
477
478 if(ssl->version || ssl->version_max) {
479 r = Curl_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version,
480 (ssl->version_max >> 16));
481 if(r)
482 goto out;
483 }
484 if(ssl->ssl_options) {
485 r = Curl_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options);
486 if(r)
487 goto out;
488 }
489 if(ssl->cipher_list) {
490 r = Curl_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list);
491 if(r)
492 goto out;
493 }
494 if(ssl->cipher_list13) {
495 r = Curl_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13);
496 if(r)
497 goto out;
498 }
499 if(ssl->curves) {
500 r = Curl_dyn_addf(&buf, ":CURVES-%s", ssl->curves);
501 if(r)
502 goto out;
503 }
504 if(ssl->verifypeer) {
505 r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile);
506 if(r)
507 goto out;
508 r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath);
509 if(r)
510 goto out;
511 r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile);
512 if(r)
513 goto out;
514 r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert);
515 if(r)
516 goto out;
517 if(ssl->cert_blob) {
518 r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob);
519 if(r)
520 goto out;
521 }
522 if(ssl->ca_info_blob) {
523 r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob);
524 if(r)
525 goto out;
526 }
527 if(ssl->issuercert_blob) {
528 r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob);
529 if(r)
530 goto out;
531 }
532 }
533 if(ssl->pinned_key && ssl->pinned_key[0]) {
534 r = Curl_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key);
535 if(r)
536 goto out;
537 }
538
539 if(ssl->clientcert && ssl->clientcert[0]) {
540 r = Curl_dyn_add(&buf, ":CCERT");
541 if(r)
542 goto out;
543 }
544 #ifdef USE_TLS_SRP
545 if(ssl->username || ssl->password) {
546 r = Curl_dyn_add(&buf, ":SRP-AUTH");
547 if(r)
548 goto out;
549 }
550 #endif
551
552 if(!tls_id || !tls_id[0]) {
553 r = CURLE_FAILED_INIT;
554 goto out;
555 }
556 r = Curl_dyn_addf(&buf, ":IMPL-%s", tls_id);
557 if(r)
558 goto out;
559
560 *ppeer_key = Curl_dyn_take(&buf, &key_len);
561 /* we just added printable char, and dynbuf always 0 terminates,
562 * no need to track length */
563
564
565 out:
566 Curl_dyn_free(&buf);
567 return r;
568 }
569
cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer * peer,struct ssl_primary_config * conn_config)570 static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer,
571 struct ssl_primary_config *conn_config)
572 {
573 if(!Curl_safecmp(peer->clientcert, conn_config->clientcert))
574 return FALSE;
575 #ifdef USE_TLS_SRP
576 if(Curl_timestrcmp(peer->srp_username, conn_config->username) ||
577 Curl_timestrcmp(peer->srp_password, conn_config->password))
578 return FALSE;
579 #endif
580 return TRUE;
581 }
582
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)583 static CURLcode cf_ssl_find_peer(struct Curl_cfilter *cf,
584 struct Curl_easy *data,
585 struct Curl_ssl_scache *scache,
586 const char *ssl_peer_key,
587 struct Curl_ssl_scache_peer **ppeer)
588 {
589 struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
590 struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
591 size_t i, peer_key_len = 0;
592 CURLcode result = CURLE_OK;
593
594 *ppeer = NULL;
595 if(!ssl_config || !ssl_config->primary.cache_session)
596 goto out;
597
598 /* check for entries with known peer_key */
599 for(i = 0; scache && i < scache->peer_count; i++) {
600 if(scache->peers[i].ssl_peer_key &&
601 strcasecompare(ssl_peer_key, scache->peers[i].ssl_peer_key) &&
602 cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
603 /* yes, we have a cached session for this! */
604 *ppeer = &scache->peers[i];
605 goto out;
606 }
607 }
608 /* check for entries with HMAC set but no known peer_key */
609 for(i = 0; scache && i < scache->peer_count; i++) {
610 if(!scache->peers[i].ssl_peer_key &&
611 scache->peers[i].hmac_set &&
612 cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
613 /* possible entry with unknown peer_key, check hmac */
614 unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH];
615 if(!peer_key_len) /* we are lazy */
616 peer_key_len = strlen(ssl_peer_key);
617 result = Curl_hmacit(&Curl_HMAC_SHA256,
618 scache->peers[i].key_salt,
619 sizeof(scache->peers[i].key_salt),
620 (const unsigned char *)ssl_peer_key,
621 peer_key_len,
622 my_hmac);
623 if(result)
624 goto out;
625 if(!memcmp(scache->peers[i].key_hmac, my_hmac, sizeof(my_hmac))) {
626 /* remember peer_key for future lookups */
627 scache->peers[i].ssl_peer_key = strdup(ssl_peer_key);
628 if(!scache->peers[i].ssl_peer_key) {
629 result = CURLE_OUT_OF_MEMORY;
630 goto out;
631 }
632 *ppeer = &scache->peers[i];
633 goto out;
634 }
635 }
636 }
637 out:
638 if(result)
639 CURL_TRC_CF(data, cf, "[SACHE] failure finding scache peer: %d", result);
640 return result;
641 }
642
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)643 static CURLcode cf_ssl_add_peer(struct Curl_cfilter *cf,
644 struct Curl_easy *data,
645 struct Curl_ssl_scache *scache,
646 const char *ssl_peer_key,
647 struct Curl_ssl_scache_peer **ppeer)
648 {
649 struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
650 struct Curl_ssl_scache_peer *peer = NULL;
651 size_t i;
652 CURLcode result;
653
654 *ppeer = NULL;
655 result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
656 if(result || !scache->peer_count)
657 return result;
658
659 if(peer) {
660 *ppeer = peer;
661 return CURLE_OK;
662 }
663
664 /* not there, find empty or oldest peer */
665 for(i = 0; i < scache->peer_count; ++i) {
666 /* free peer entry? */
667 if(!scache->peers[i].ssl_peer_key && !scache->peers[i].hmac_set) {
668 peer = &scache->peers[i];
669 break;
670 }
671 /* peer without sessions and obj */
672 if(!scache->peers[i].sobj &&
673 !Curl_llist_count(&scache->peers[i].sessions)) {
674 peer = &scache->peers[i];
675 break;
676 }
677 /* remember "oldest" peer */
678 if(!peer || (scache->peers[i].age < peer->age)) {
679 peer = &scache->peers[i];
680 }
681 }
682 DEBUGASSERT(peer);
683 if(!peer)
684 return CURLE_OK;
685 /* clear previous peer and reinit */
686 cf_ssl_scache_clear_peer(peer);
687 result = cf_ssl_scache_peer_init(peer, ssl_peer_key,
688 conn_config->clientcert,
689 #ifdef USE_TLS_SRP
690 conn_config->username,
691 conn_config->password);
692 #else
693 NULL, NULL);
694 #endif
695 if(result)
696 goto out;
697 /* all ready */
698 *ppeer = peer;
699 result = CURLE_OK;
700
701 out:
702 if(result) {
703 cf_ssl_scache_clear_peer(peer);
704 CURL_TRC_CF(data, cf, "[SACHE] failure adding peer: %d", result);
705 }
706 return result;
707 }
708
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)709 static CURLcode cf_scache_peer_add_session(struct Curl_cfilter *cf,
710 struct Curl_easy *data,
711 struct Curl_ssl_scache *scache,
712 const char *ssl_peer_key,
713 struct Curl_ssl_session *s)
714 {
715 struct Curl_ssl_scache_peer *peer = NULL;
716 CURLcode result = CURLE_OUT_OF_MEMORY;
717 curl_off_t now = (curl_off_t)time(NULL);
718
719 if(!scache || !scache->peer_count) {
720 Curl_ssl_session_destroy(s);
721 return CURLE_OK;
722 }
723
724 if(!s->time_received)
725 s->time_received = now;
726 if(s->lifetime_secs < 0)
727 s->lifetime_secs = scache->default_lifetime_secs;
728
729 if(cf_scache_session_expired(s, now)) {
730 CURL_TRC_CF(data, cf, "[SCACHE] add, session already expired");
731 Curl_ssl_session_destroy(s);
732 return CURLE_OK;
733 }
734
735 result = cf_ssl_add_peer(cf, data, scache, ssl_peer_key, &peer);
736 if(result || !peer) {
737 CURL_TRC_CF(data, cf, "[SCACHE] unable to add scache peer: %d", result);
738 Curl_ssl_session_destroy(s);
739 goto out;
740 }
741
742 /* A session not from TLSv1.3 replaces all other. */
743 if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3) {
744 Curl_llist_destroy(&peer->sessions, NULL);
745 Curl_llist_append(&peer->sessions, s, &s->list);
746 }
747 else {
748 /* Expire existing, append, trim from head to obey max_sessions */
749 cf_scache_peer_remove_expired(peer, now);
750 cf_scache_peer_remove_non13(peer);
751 Curl_llist_append(&peer->sessions, s, &s->list);
752 while(Curl_llist_count(&peer->sessions) > peer->max_sessions) {
753 Curl_node_remove(Curl_llist_head(&peer->sessions));
754 }
755 }
756
757 out:
758 if(result) {
759 failf(data, "[SCACHE] failed to add session for %s, error=%d",
760 ssl_peer_key, result);
761 }
762 else
763 CURL_TRC_CF(data, cf, "[SCACHE] added session for %s [proto=0x%x, "
764 "lifetime=%d, alpn=%s, earlydata=%zu, quic_tp=%s], "
765 "peer has %zu sessions now",
766 ssl_peer_key, s->ietf_tls_id, s->lifetime_secs, s->alpn,
767 s->earlydata_max, s->quic_tp ? "yes" : "no",
768 Curl_llist_count(&peer->sessions));
769 return result;
770 }
771
Curl_ssl_scache_put(struct Curl_cfilter * cf,struct Curl_easy * data,const char * ssl_peer_key,struct Curl_ssl_session * s)772 CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf,
773 struct Curl_easy *data,
774 const char *ssl_peer_key,
775 struct Curl_ssl_session *s)
776 {
777 struct Curl_ssl_scache *scache = data->state.ssl_scache;
778 CURLcode result;
779
780 Curl_ssl_scache_lock(data);
781 result = cf_scache_peer_add_session(cf, data, scache, ssl_peer_key, s);
782 Curl_ssl_scache_unlock(data);
783 return result;
784 }
785
Curl_ssl_scache_return(struct Curl_cfilter * cf,struct Curl_easy * data,const char * ssl_peer_key,struct Curl_ssl_session * s)786 void Curl_ssl_scache_return(struct Curl_cfilter *cf,
787 struct Curl_easy *data,
788 const char *ssl_peer_key,
789 struct Curl_ssl_session *s)
790 {
791 /* See RFC 8446 C.4:
792 * "Clients SHOULD NOT reuse a ticket for multiple connections." */
793 if(s && s->ietf_tls_id < 0x304)
794 (void)Curl_ssl_scache_put(cf, data, ssl_peer_key, s);
795 else
796 Curl_ssl_session_destroy(s);
797 }
798
Curl_ssl_scache_take(struct Curl_cfilter * cf,struct Curl_easy * data,const char * ssl_peer_key,struct Curl_ssl_session ** ps)799 CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf,
800 struct Curl_easy *data,
801 const char *ssl_peer_key,
802 struct Curl_ssl_session **ps)
803 {
804 struct Curl_ssl_scache *scache = data->state.ssl_scache;
805 struct Curl_ssl_scache_peer *peer = NULL;
806 struct Curl_llist_node *n;
807 struct Curl_ssl_session *s = NULL;
808 CURLcode result;
809
810 *ps = NULL;
811 if(!scache)
812 return CURLE_OK;
813
814 Curl_ssl_scache_lock(data);
815 result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
816 if(!result && peer) {
817 cf_scache_peer_remove_expired(peer, (curl_off_t)time(NULL));
818 n = Curl_llist_head(&peer->sessions);
819 if(n) {
820 s = Curl_node_take_elem(n);
821 (scache->age)++; /* increase general age */
822 peer->age = scache->age; /* set this as used in this age */
823 }
824 }
825 Curl_ssl_scache_unlock(data);
826 if(s) {
827 *ps = s;
828 CURL_TRC_CF(data, cf, "[SCACHE] took session for %s [proto=0x%x, "
829 "lifetime=%d, alpn=%s, earlydata=%zu, quic_tp=%s], "
830 "%zu sessions remain",
831 ssl_peer_key, s->ietf_tls_id, s->lifetime_secs, s->alpn,
832 s->earlydata_max, s->quic_tp ? "yes" : "no",
833 Curl_llist_count(&peer->sessions));
834 }
835 else {
836 CURL_TRC_CF(data, cf, "[SCACHE] no cached session for %s", ssl_peer_key);
837 }
838 return result;
839 }
840
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)841 CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
842 struct Curl_easy *data,
843 const char *ssl_peer_key,
844 void *sobj,
845 Curl_ssl_scache_obj_dtor *sobj_free)
846 {
847 struct Curl_ssl_scache *scache = data->state.ssl_scache;
848 struct Curl_ssl_scache_peer *peer = NULL;
849 CURLcode result;
850
851 DEBUGASSERT(sobj);
852 DEBUGASSERT(sobj_free);
853
854 result = cf_ssl_add_peer(cf, data, scache, ssl_peer_key, &peer);
855 if(result || !peer) {
856 CURL_TRC_CF(data, cf, "[SCACHE] unable to add scache peer: %d", result);
857 goto out;
858 }
859
860 cf_ssl_scache_peer_set_obj(peer, sobj, sobj_free);
861 sobj = NULL; /* peer took ownership */
862
863 out:
864 if(sobj && sobj_free)
865 sobj_free(sobj);
866 return result;
867 }
868
Curl_ssl_scache_get_obj(struct Curl_cfilter * cf,struct Curl_easy * data,const char * ssl_peer_key,void ** sobj)869 bool Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
870 struct Curl_easy *data,
871 const char *ssl_peer_key,
872 void **sobj)
873 {
874 struct Curl_ssl_scache *scache = data->state.ssl_scache;
875 struct Curl_ssl_scache_peer *peer = NULL;
876 CURLcode result;
877
878 *sobj = NULL;
879 if(!scache)
880 return FALSE;
881
882 result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
883 if(result)
884 return FALSE;
885
886 if(peer)
887 *sobj = peer->sobj;
888
889 CURL_TRC_CF(data, cf, "[SACHE] %s cached session for '%s'",
890 *sobj ? "Found" : "No", ssl_peer_key);
891 return !!*sobj;
892 }
893
Curl_ssl_scache_remove_all(struct Curl_cfilter * cf,struct Curl_easy * data,const char * ssl_peer_key)894 void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
895 struct Curl_easy *data,
896 const char *ssl_peer_key)
897 {
898 struct Curl_ssl_scache *scache = data->state.ssl_scache;
899 struct Curl_ssl_scache_peer *peer = NULL;
900 CURLcode result;
901
902 (void)cf;
903 if(!scache)
904 return;
905
906 Curl_ssl_scache_lock(data);
907 result = cf_ssl_find_peer(cf, data, scache, ssl_peer_key, &peer);
908 if(!result && peer)
909 cf_ssl_scache_clear_peer(peer);
910 Curl_ssl_scache_unlock(data);
911 }
912
913 #endif /* USE_SSL */
914