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