Coverage Report

Created: 2026-10-15 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/vtls/vtls_scache.c
Line
Count
Source
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
#include "curl_setup.h"
25
26
#ifdef USE_SSL
27
28
#include "urldata.h"
29
#include "cfilters.h"
30
31
#include "vtls/vtls.h" /* generic SSL protos etc */
32
#include "vtls/vtls_int.h"
33
#include "vtls/vtls_scache.h"
34
#include "vtls/vtls_spack.h"
35
36
#include "strcase.h"
37
#include "url.h"
38
#include "llist.h"
39
#include "curl_share.h"
40
#include "curl_threads.h"
41
#include "curl_trc.h"
42
#include "curl_sha256.h"
43
#include "rand.h"
44
#include "curlx/strdup.h"
45
46
/* a peer+tls-config we cache sessions for */
47
struct Curl_ssl_scache_peer {
48
  char *ssl_peer_key;      /* id for peer + relevant TLS configuration */
49
  char *clientcert;
50
  char *key_passwd;
51
  struct Curl_llist sessions;
52
  void *sobj;              /* object instance or NULL */
53
  Curl_ssl_scache_obj_dtor *sobj_free; /* free `sobj` callback */
54
  unsigned char key_salt[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
55
  unsigned char key_hmac[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
56
  size_t max_sessions;
57
  long age;                /* a number, the higher the more recent */
58
  BIT(hmac_set);           /* if key_salt and key_hmac are present */
59
  BIT(exportable);         /* sessions for this peer can be exported */
60
};
61
62
7.89M
#define CURL_SCACHE_MAGIC 0x000e1551
63
64
7.30M
#define GOOD_SCACHE(x) ((x) && (x)->magic == CURL_SCACHE_MAGIC)
65
66
/* Prefix the length to keep field boundaries unambiguous. */
67
static CURLcode cf_ssl_peer_key_add_string(struct dynbuf *buf,
68
                                           const char *name,
69
                                           const char *value)
70
260k
{
71
260k
  return curlx_dyn_addf(buf, ":%s-%zu:%s", name, strlen(value), value);
72
260k
}
73
74
static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf,
75
                                         const char *name,
76
                                         const char *path,
77
                                         bool *is_local)
78
646k
{
79
646k
  if(path && path[0]) {
80
    /* We try to add absolute paths, so that the session key can stay valid
81
     * when used in another process with different CWD. When a path does not
82
     * exist, this does not work. Then, we add the path as is. */
83
#ifdef _WIN32
84
    char *abspath = _fullpath(NULL, path, 0);
85
    if(abspath) {
86
      CURLcode result = cf_ssl_peer_key_add_string(buf, name, abspath);
87
      /* !checksrc! disable BANNEDFUNC 1 */
88
      free(abspath); /* allocated by CRT, use system free() */
89
      return result;
90
    }
91
    *is_local = TRUE;
92
#elif defined(HAVE_REALPATH)
93
26.8k
    if(path[0] != '/') {
94
1.67k
      char *abspath = realpath(path, NULL);
95
1.67k
      if(abspath) {
96
126
        CURLcode result = cf_ssl_peer_key_add_string(buf, name, abspath);
97
        /* !checksrc! disable BANNEDFUNC 1 */
98
126
        free(abspath); /* allocated by libc, free without memdebug */
99
126
        return result;
100
126
      }
101
1.55k
      *is_local = TRUE;
102
1.55k
    }
103
26.7k
#endif
104
26.7k
    return cf_ssl_peer_key_add_string(buf, name, path);
105
26.8k
  }
106
619k
  return CURLE_OK;
107
646k
}
108
109
static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf,
110
                                         const char *name,
111
                                         struct curl_blob *blob)
112
137k
{
113
137k
  CURLcode result = CURLE_OK;
114
137k
  if(blob && blob->len) {
115
137k
    unsigned char hash[CURL_SHA256_DIGEST_LENGTH];
116
137k
    size_t i;
117
118
137k
    result = curlx_dyn_addf(buf, ":%s-", name);
119
137k
    if(result)
120
21
      goto out;
121
137k
    result = Curl_sha256it(hash, blob->data, blob->len);
122
137k
    if(result)
123
0
      goto out;
124
4.53M
    for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) {
125
4.39M
      result = curlx_dyn_addf(buf, "%02x", hash[i]);
126
4.39M
      if(result)
127
28
        goto out;
128
4.39M
    }
129
137k
  }
130
137k
out:
131
137k
  return result;
132
137k
}
133
134
static CURLcode cf_ssl_peer_key_add_mtls(struct dynbuf *buf,
135
                                         struct ssl_filter_config *ssl,
136
                                         bool *is_local)
137
173k
{
138
173k
  CURLcode result = CURLE_OK;
139
173k
  if(ssl->clientcert && ssl->clientcert[0]) {
140
272
    result = cf_ssl_peer_key_add_path(buf, "CCERT", ssl->clientcert, is_local);
141
272
    if(result)
142
17
      goto out;
143
272
  }
144
173k
  if(ssl->key && ssl->key[0]) {
145
107
    result = cf_ssl_peer_key_add_path(buf, "KEY", ssl->key, is_local);
146
107
    if(result)
147
9
      goto out;
148
107
  }
149
173k
  if(ssl->key_blob) {
150
0
    result = cf_ssl_peer_key_add_hash(buf, "KEYBlob", ssl->key_blob);
151
0
    if(result)
152
0
      goto out;
153
0
  }
154
173k
  if(ssl->cert_type && ssl->cert_type[0]) {
155
390
    size_t i;
156
390
    result = curlx_dyn_addf(buf, ":CT-%zu:", strlen(ssl->cert_type));
157
57.3k
    for(i = 0; !result && ssl->cert_type[i]; i++) {
158
56.9k
      char c = Curl_raw_toupper(ssl->cert_type[i]);
159
56.9k
      result = curlx_dyn_addn(buf, &c, 1);
160
56.9k
    }
161
390
    if(result)
162
6
      goto out;
163
390
  }
164
173k
  if(ssl->key_type && ssl->key_type[0]) {
165
158
    size_t i;
166
158
    result = curlx_dyn_addf(buf, ":KT-%zu:", strlen(ssl->key_type));
167
57.8k
    for(i = 0; !result && ssl->key_type[i]; i++) {
168
57.6k
      char c = Curl_raw_toupper(ssl->key_type[i]);
169
57.6k
      result = curlx_dyn_addn(buf, &c, 1);
170
57.6k
    }
171
158
  }
172
173k
out:
173
173k
  return result;
174
173k
}
175
176
1.43k
#define CURL_SSLS_LOCAL_SUFFIX     ":L"
177
172k
#define CURL_SSLS_GLOBAL_SUFFIX    ":G"
178
179
static bool cf_ssl_peer_key_is_global(const char *peer_key)
180
104k
{
181
104k
  size_t len = peer_key ? strlen(peer_key) : 0;
182
104k
  return (len > 2) &&
183
104k
         (peer_key[len - 1] == 'G') &&
184
104k
         (peer_key[len - 2] == ':');
185
104k
}
186
187
static CURLcode ssl_peer_key_add_transport(struct dynbuf *buf,
188
                                           uint8_t transport)
189
173k
{
190
173k
  switch(transport) {
191
168k
  case TRNSPRT_TCP:
192
168k
    return CURLE_OK;
193
0
  case TRNSPRT_UDP:
194
0
    return curlx_dyn_add(buf, ":UDP");
195
5.77k
  case TRNSPRT_QUIC:
196
5.77k
    return curlx_dyn_add(buf, ":QUIC");
197
0
  case TRNSPRT_UNIX:
198
0
    return curlx_dyn_add(buf, ":UNIX");
199
0
  default:
200
0
    return curlx_dyn_addf(buf, ":TRNSPRT-%d", transport);
201
173k
  }
202
173k
}
203
204
static CURLcode ssl_peer_key_add_vrfy(struct dynbuf *buf,
205
                                      struct ssl_filter_config *ssl,
206
                                      const struct ssl_peer *peer)
207
173k
{
208
173k
  CURLcode result;
209
210
173k
  if(!ssl->verifypeer) {
211
12.2k
    result = curlx_dyn_add(buf, ":NO-VRFY-PEER");
212
12.2k
    if(result)
213
10
      return result;
214
12.2k
  }
215
173k
  if(!ssl->verifyhost) {
216
17.3k
    result = curlx_dyn_add(buf, ":NO-VRFY-HOST");
217
17.3k
    if(result)
218
6
      return result;
219
17.3k
  }
220
173k
  if(ssl->verifystatus) {
221
89
    result = curlx_dyn_add(buf, ":VRFY-STATUS");
222
89
    if(result)
223
0
      return result;
224
89
  }
225
173k
  if((!ssl->verifypeer || !ssl->verifyhost) &&
226
29.1k
     peer->peer && !Curl_peer_equal(peer->origin, peer->peer)) {
227
16.5k
    result = cf_ssl_peer_key_add_string(buf, "CHOST", peer->peer->hostname);
228
16.5k
    if(!result)
229
16.5k
      result = curlx_dyn_addf(buf, ":CPORT-%u", peer->peer->port);
230
16.5k
    if(result)
231
4
      return result;
232
16.5k
  }
233
173k
  return CURLE_OK;
234
173k
}
235
236
static CURLcode ssl_peer_key_build(struct ssl_filter_config *ssl,
237
                                   const struct ssl_peer *peer,
238
                                   const char *tls_id,
239
                                   char **ppeer_key)
240
173k
{
241
173k
  struct dynbuf buf;
242
173k
  size_t key_len;
243
173k
  bool is_local = FALSE;
244
173k
  CURLcode result;
245
246
173k
  *ppeer_key = NULL;
247
173k
  curlx_dyn_init(&buf, 10 * 1024);
248
249
173k
  result = curlx_dyn_addf(&buf, "%s:%d:KEY-V2",
250
173k
                          peer->origin->hostname, peer->origin->port);
251
173k
  if(result)
252
36
    goto out;
253
173k
  result = ssl_peer_key_add_transport(&buf, peer->transport);
254
173k
  if(result)
255
0
    goto out;
256
173k
  result = ssl_peer_key_add_vrfy(&buf, ssl, peer);
257
173k
  if(result)
258
20
    goto out;
259
173k
  if(ssl->version || ssl->version_max) {
260
173k
    result = curlx_dyn_addf(&buf, ":TLSVER-%d-%u", ssl->version,
261
173k
                            (ssl->version_max >> 16));
262
173k
    if(result)
263
22
      goto out;
264
173k
  }
265
173k
  if(ssl->ssl_options) {
266
361
    result = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options);
267
361
    if(result)
268
4
      goto out;
269
361
  }
270
173k
  if(ssl->cipher_list) {
271
13.0k
    result = cf_ssl_peer_key_add_string(&buf, "CIPHER", ssl->cipher_list);
272
13.0k
    if(result)
273
16
      goto out;
274
13.0k
  }
275
173k
  if(ssl->cipher_list13) {
276
3.65k
    result = cf_ssl_peer_key_add_string(&buf, "CIPHER13", ssl->cipher_list13);
277
3.65k
    if(result)
278
16
      goto out;
279
3.65k
  }
280
173k
  if(ssl->curves) {
281
25.0k
    result = cf_ssl_peer_key_add_string(&buf, "CURVES", ssl->curves);
282
25.0k
    if(result)
283
20
      goto out;
284
25.0k
  }
285
173k
  if(ssl->signature_algorithms) {
286
647
    result = cf_ssl_peer_key_add_string(&buf, "SIGALGS",
287
647
                                        ssl->signature_algorithms);
288
647
    if(result)
289
3
      goto out;
290
647
  }
291
173k
  if(ssl->verifypeer) {
292
161k
    result = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local);
293
161k
    if(result)
294
15
      goto out;
295
161k
    result = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local);
296
161k
    if(result)
297
20
      goto out;
298
161k
    result = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local);
299
161k
    if(result)
300
15
      goto out;
301
161k
    result = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert,
302
161k
                                      &is_local);
303
161k
    if(result)
304
10
      goto out;
305
161k
    if(ssl->ca_info_blob) {
306
137k
      result = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob);
307
137k
      if(result)
308
49
        goto out;
309
137k
    }
310
161k
    if(ssl->issuercert_blob) {
311
0
      result = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob",
312
0
                                        ssl->issuercert_blob);
313
0
      if(result)
314
0
        goto out;
315
0
    }
316
161k
  }
317
173k
  if(ssl->cert_blob) {
318
0
    result = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob);
319
0
    if(result)
320
0
      goto out;
321
0
  }
322
173k
  if(ssl->pinned_key && ssl->pinned_key[0]) {
323
880
    result = cf_ssl_peer_key_add_string(&buf, "Pinned", ssl->pinned_key);
324
880
    if(result)
325
21
      goto out;
326
880
  }
327
328
173k
  result = cf_ssl_peer_key_add_mtls(&buf, ssl, &is_local);
329
173k
  if(result)
330
38
    goto out;
331
332
173k
  if(!tls_id || !tls_id[0]) {
333
0
    result = CURLE_FAILED_INIT;
334
0
    goto out;
335
0
  }
336
173k
  result = cf_ssl_peer_key_add_string(&buf, "IMPL", tls_id);
337
173k
  if(result)
338
34
    goto out;
339
340
173k
  result = curlx_dyn_addf(&buf, is_local ?
341
172k
                          CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX);
342
173k
  if(result)
343
5
    goto out;
344
345
173k
  *ppeer_key = curlx_dyn_take(&buf, &key_len);
346
  /* we added printable char, and dynbuf always null-terminates, no need
347
   * to track length */
348
349
173k
out:
350
173k
  curlx_dyn_free(&buf);
351
173k
  return result;
352
173k
}
353
354
CURLcode Curl_ssl_peer_key_make(const struct ssl_peer *peer,
355
                                struct ssl_filter_config *sslc,
356
                                const char *tls_id,
357
                                char **ppeer_key)
358
173k
{
359
173k
  return ssl_peer_key_build(sslc, peer, tls_id, ppeer_key);
360
173k
}
361
362
struct Curl_ssl_scache {
363
  unsigned int magic;
364
  struct Curl_ssl_scache_peer *peers;
365
  size_t peer_count;
366
  int default_lifetime_secs;
367
  long age;
368
#ifdef USE_MUTEX
369
  curl_mutex_t mutex;
370
  curl_thread_id_t locking_thread;
371
#endif
372
  BIT(is_locked);
373
};
374
375
static struct Curl_ssl_scache *cf_ssl_scache_get(struct Curl_easy *data)
376
6.23M
{
377
6.23M
  struct Curl_ssl_scache *scache = NULL;
378
  /* If a share is present, its ssl_scache has preference over the multi */
379
6.23M
  if(data->share && data->share->ssl_scache)
380
50
    scache = data->share->ssl_scache;
381
6.23M
  else if(data->multi && data->multi->ssl_scache)
382
6.23M
    scache = data->multi->ssl_scache;
383
6.23M
  if(scache && !GOOD_SCACHE(scache)) {
384
0
    failf(data, "transfer would use an invalid scache at %p, denied",
385
0
          (void *)scache);
386
0
    DEBUGASSERT(0);
387
0
    return NULL;
388
0
  }
389
6.23M
  return scache;
390
6.23M
}
391
392
static void cf_ssl_scache_session_ldestroy(void *udata, void *obj)
393
317k
{
394
317k
  struct Curl_ssl_session *s = obj;
395
317k
  (void)udata;
396
317k
  curlx_free(s->sdata);
397
317k
  curlx_free(s->quic_tp);
398
317k
  curlx_free(s->alpn);
399
317k
  curlx_free(s);
400
317k
}
401
402
CURLcode Curl_ssl_session_create(void *sdata, size_t sdata_len,
403
                                 int ietf_tls_id, const char *alpn,
404
                                 curl_off_t valid_until, size_t earlydata_max,
405
                                 struct Curl_ssl_session **psession)
406
41.8k
{
407
41.8k
  return Curl_ssl_session_create2(sdata, sdata_len, ietf_tls_id, alpn,
408
41.8k
                                  valid_until, earlydata_max,
409
41.8k
                                  NULL, 0, psession);
410
41.8k
}
411
412
CURLcode Curl_ssl_session_create2(void *sdata, size_t sdata_len,
413
                                  int ietf_tls_id, const char *alpn,
414
                                  curl_off_t valid_until, size_t earlydata_max,
415
                                  unsigned char *quic_tp, size_t quic_tp_len,
416
                                  struct Curl_ssl_session **psession)
417
229k
{
418
229k
  struct Curl_ssl_session *s;
419
420
229k
  if(!sdata || !sdata_len) {
421
0
    curlx_free(sdata);
422
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
423
0
  }
424
425
229k
  *psession = NULL;
426
229k
  s = curlx_calloc(1, sizeof(*s));
427
229k
  if(!s) {
428
68
    curlx_free(sdata);
429
68
    curlx_free(quic_tp);
430
68
    return CURLE_OUT_OF_MEMORY;
431
68
  }
432
433
229k
  s->ietf_tls_id = ietf_tls_id;
434
229k
  s->valid_until = valid_until;
435
229k
  s->earlydata_max = earlydata_max;
436
229k
  s->sdata = sdata;
437
229k
  s->sdata_len = sdata_len;
438
229k
  s->quic_tp = quic_tp;
439
229k
  s->quic_tp_len = quic_tp_len;
440
229k
  if(alpn) {
441
180k
    s->alpn = curlx_strdup(alpn);
442
180k
    if(!s->alpn) {
443
32
      cf_ssl_scache_session_ldestroy(NULL, s);
444
32
      return CURLE_OUT_OF_MEMORY;
445
32
    }
446
180k
  }
447
228k
  *psession = s;
448
228k
  return CURLE_OK;
449
229k
}
450
451
void Curl_ssl_session_destroy(struct Curl_ssl_session *s)
452
880k
{
453
880k
  if(s) {
454
    /* if in the list, the list destructor takes care of it */
455
21.4k
    if(Curl_node_llist(&s->list))
456
0
      Curl_node_remove(&s->list);
457
21.4k
    else {
458
21.4k
      cf_ssl_scache_session_ldestroy(NULL, s);
459
21.4k
    }
460
21.4k
  }
461
880k
}
462
463
CURLcode Curl_ssl_session_dup(struct Curl_ssl_session *src,
464
                              struct Curl_ssl_session **pdest)
465
0
{
466
0
  struct Curl_ssl_session *dest = NULL;
467
0
  CURLcode result = CURLE_OUT_OF_MEMORY;
468
469
0
  if(!src || !pdest)
470
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
471
0
  *pdest = NULL;
472
473
0
  dest = curlx_calloc(1, sizeof(*dest));
474
0
  if(!dest)
475
0
    goto out;
476
477
0
  dest->ietf_tls_id = src->ietf_tls_id;
478
0
  dest->valid_until = src->valid_until;
479
0
  dest->earlydata_max = src->earlydata_max;
480
0
  dest->sectrust_verified = src->sectrust_verified;
481
0
  if(src->sdata_len) {
482
0
    dest->sdata = curlx_memdup(src->sdata, src->sdata_len);
483
0
    if(!dest->sdata)
484
0
      goto out;
485
0
    dest->sdata_len = src->sdata_len;
486
0
  }
487
0
  if(src->quic_tp_len) {
488
0
    dest->quic_tp = curlx_memdup(src->quic_tp, src->quic_tp_len);
489
0
    if(!dest->quic_tp)
490
0
      goto out;
491
0
    dest->quic_tp_len = src->quic_tp_len;
492
0
  }
493
0
  if(src->alpn) {
494
0
    dest->alpn = curlx_strdup(src->alpn);
495
0
    if(!dest->alpn)
496
0
      goto out;
497
0
  }
498
0
  result = CURLE_OK;
499
500
0
out:
501
0
  if(!result)
502
0
    *pdest = dest;
503
0
  else {
504
0
    *pdest = NULL;
505
0
    if(dest)
506
0
      Curl_ssl_session_destroy(dest);
507
0
  }
508
0
  return result;
509
0
}
510
511
static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer)
512
14.9M
{
513
14.9M
  Curl_llist_destroy(&peer->sessions, NULL);
514
14.9M
  if(peer->sobj) {
515
0
    DEBUGASSERT(peer->sobj_free);
516
0
    if(peer->sobj_free)
517
0
      peer->sobj_free(peer->sobj);
518
0
    peer->sobj = NULL;
519
0
  }
520
14.9M
  peer->sobj_free = NULL;
521
14.9M
  curlx_safefree(peer->clientcert);
522
14.9M
  curlx_safefree(peer->key_passwd);
523
14.9M
  curlx_safefree(peer->ssl_peer_key);
524
14.9M
  peer->age = 0;
525
14.9M
  peer->hmac_set = FALSE;
526
14.9M
}
527
528
static void cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer *peer,
529
                                       void *sobj,
530
                                       Curl_ssl_scache_obj_dtor *sobj_free)
531
0
{
532
0
  DEBUGASSERT(peer);
533
0
  if(peer->sobj_free) {
534
0
    peer->sobj_free(peer->sobj);
535
0
  }
536
0
  peer->sobj = sobj;
537
0
  peer->sobj_free = sobj_free;
538
0
}
539
540
static void cf_ssl_cache_peer_update(struct Curl_ssl_scache_peer *peer)
541
104k
{
542
  /* The sessions of this peer are exportable if
543
   * - it has no confidential information
544
   * - its peer key is not yet known, because sessions were
545
   *   imported using only the salt+hmac
546
   * - the peer key is global, e.g. carrying no relative paths */
547
104k
  peer->exportable = (!peer->clientcert && !peer->key_passwd &&
548
104k
                      (!peer->ssl_peer_key ||
549
104k
                       cf_ssl_peer_key_is_global(peer->ssl_peer_key)));
550
104k
}
551
552
static CURLcode cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer,
553
                                        const char *ssl_peer_key,
554
                                        const char *clientcert,
555
                                        const char *key_passwd,
556
                                        const unsigned char *salt,
557
                                        const unsigned char *hmac)
558
104k
{
559
104k
  CURLcode result = CURLE_OUT_OF_MEMORY;
560
561
104k
  DEBUGASSERT(!peer->ssl_peer_key);
562
104k
  if(ssl_peer_key) {
563
104k
    peer->ssl_peer_key = curlx_strdup(ssl_peer_key);
564
104k
    if(!peer->ssl_peer_key)
565
44
      goto out;
566
104k
    peer->hmac_set = FALSE;
567
104k
  }
568
0
  else if(salt && hmac) {
569
0
    memcpy(peer->key_salt, salt, sizeof(peer->key_salt));
570
0
    memcpy(peer->key_hmac, hmac, sizeof(peer->key_hmac));
571
0
    peer->hmac_set = TRUE;
572
0
  }
573
0
  else {
574
0
    result = CURLE_BAD_FUNCTION_ARGUMENT;
575
0
    goto out;
576
0
  }
577
104k
  if(clientcert) {
578
0
    peer->clientcert = curlx_strdup(clientcert);
579
0
    if(!peer->clientcert)
580
0
      goto out;
581
0
  }
582
104k
  if(key_passwd) {
583
0
    peer->key_passwd = curlx_strdup(key_passwd);
584
0
    if(!peer->key_passwd)
585
0
      goto out;
586
0
  }
587
104k
  cf_ssl_cache_peer_update(peer);
588
104k
  result = CURLE_OK;
589
104k
out:
590
104k
  if(result)
591
44
    cf_ssl_scache_clear_peer(peer);
592
104k
  return result;
593
104k
}
594
595
static void cf_scache_session_remove(struct Curl_ssl_scache_peer *peer,
596
                                     struct Curl_ssl_session *s)
597
0
{
598
0
  (void)peer;
599
0
  DEBUGASSERT(Curl_node_llist(&s->list) == &peer->sessions);
600
0
  Curl_ssl_session_destroy(s);
601
0
}
602
603
static bool cf_scache_session_expired(struct Curl_ssl_session *s,
604
                                      curl_off_t now)
605
708k
{
606
708k
  return (s->valid_until > 0) && (s->valid_until < now);
607
708k
}
608
609
static void cf_scache_peer_remove_expired(struct Curl_ssl_scache_peer *peer,
610
                                          curl_off_t now)
611
422k
{
612
422k
  struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
613
899k
  while(n) {
614
477k
    struct Curl_ssl_session *s = Curl_node_elem(n);
615
477k
    n = Curl_node_next(n);
616
477k
    if(cf_scache_session_expired(s, now))
617
0
      cf_scache_session_remove(peer, s);
618
477k
  }
619
422k
}
620
621
static void cf_scache_peer_remove_non13(struct Curl_ssl_scache_peer *peer)
622
310k
{
623
310k
  struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
624
605k
  while(n) {
625
294k
    struct Curl_ssl_session *s = Curl_node_elem(n);
626
294k
    n = Curl_node_next(n);
627
294k
    if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3)
628
0
      cf_scache_session_remove(peer, s);
629
294k
  }
630
310k
}
631
632
CURLcode Curl_ssl_scache_create(size_t max_peers,
633
                                size_t max_sessions_per_peer,
634
                                struct Curl_ssl_scache **pscache)
635
593k
{
636
593k
  struct Curl_ssl_scache *scache;
637
593k
  struct Curl_ssl_scache_peer *peers;
638
593k
  size_t i;
639
640
593k
  *pscache = NULL;
641
593k
  peers = curlx_calloc(max_peers, sizeof(*peers));
642
593k
  if(!peers)
643
72
    return CURLE_OUT_OF_MEMORY;
644
645
593k
  scache = curlx_calloc(1, sizeof(*scache));
646
593k
  if(!scache) {
647
56
    curlx_free(peers);
648
56
    return CURLE_OUT_OF_MEMORY;
649
56
  }
650
651
593k
  scache->magic = CURL_SCACHE_MAGIC;
652
593k
  scache->default_lifetime_secs = (24 * 60 * 60); /* 1 day */
653
593k
  scache->peer_count = max_peers;
654
593k
  scache->peers = peers;
655
593k
  scache->age = 1;
656
15.3M
  for(i = 0; i < scache->peer_count; ++i) {
657
14.8M
    scache->peers[i].max_sessions = max_sessions_per_peer;
658
14.8M
    Curl_llist_init(&scache->peers[i].sessions,
659
14.8M
                    cf_ssl_scache_session_ldestroy);
660
14.8M
  }
661
593k
#ifdef USE_MUTEX
662
593k
  Curl_mutex_init(&scache->mutex);
663
593k
#endif
664
593k
  *pscache = scache;
665
593k
  return CURLE_OK;
666
593k
}
667
668
void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache)
669
593k
{
670
593k
  if(GOOD_SCACHE(scache)) {
671
593k
    size_t i;
672
593k
    scache->magic = 0;
673
15.3M
    for(i = 0; i < scache->peer_count; ++i) {
674
14.8M
      cf_ssl_scache_clear_peer(&scache->peers[i]);
675
14.8M
    }
676
593k
    curlx_free(scache->peers);
677
593k
#ifdef USE_MUTEX
678
593k
    Curl_mutex_destroy(&scache->mutex);
679
593k
#endif
680
593k
    curlx_free(scache);
681
593k
  }
682
593k
}
683
684
bool Curl_ssl_scache_use(struct Curl_cfilter *cf, struct Curl_easy *data)
685
374k
{
686
374k
  if(cf_ssl_scache_get(data)) {
687
374k
    struct ssl_filter_config *conn_config =
688
374k
      Curl_ssl_cf_get_filter_config(cf);
689
374k
    return conn_config ? conn_config->cache_session : FALSE;
690
374k
  }
691
0
  return FALSE;
692
374k
}
693
694
/* Lock shared SSL session data */
695
void Curl_ssl_scache_lock(struct Curl_easy *data)
696
589k
{
697
589k
  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
698
589k
  if(scache) {
699
589k
    if(CURL_SHARE_ssl_scache(data))
700
0
      Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION,
701
0
                      CURL_LOCK_ACCESS_SINGLE);
702
589k
#ifdef USE_MUTEX
703
589k
    Curl_mutex_acquire(&scache->mutex);
704
589k
    scache->locking_thread = Curl_thread_get_current_id();
705
589k
    DEBUGASSERT(!scache->is_locked);
706
589k
    scache->is_locked = TRUE;
707
589k
    Curl_mutex_release(&scache->mutex);
708
#else
709
    DEBUGASSERT(!scache->is_locked);
710
    scache->is_locked = TRUE;
711
#endif
712
589k
  }
713
589k
}
714
715
/* Unlock shared SSL session data */
716
void Curl_ssl_scache_unlock(struct Curl_easy *data)
717
589k
{
718
589k
  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
719
589k
  if(scache) {
720
589k
#ifdef USE_MUTEX
721
589k
    Curl_mutex_acquire(&scache->mutex);
722
589k
    scache->locking_thread = 0;
723
589k
    DEBUGASSERT(scache->is_locked);
724
589k
    scache->is_locked = FALSE;
725
589k
    Curl_mutex_release(&scache->mutex);
726
#else
727
    DEBUGASSERT(scache->is_locked);
728
    scache->is_locked = FALSE;
729
#endif
730
589k
    if(CURL_SHARE_ssl_scache(data))
731
0
      Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
732
589k
  }
733
589k
}
734
735
bool Curl_ssl_scache_is_locked_by_current_thread(struct Curl_easy *data)
736
4.09M
{
737
4.09M
  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
738
4.09M
  bool locked = FALSE;
739
4.09M
  if(!scache)
740
2.81k
    return FALSE;
741
4.09M
#ifdef USE_MUTEX
742
4.09M
  Curl_mutex_acquire(&scache->mutex);
743
4.09M
  locked = scache->is_locked && Curl_thread_is_current(scache->locking_thread);
744
4.09M
  Curl_mutex_release(&scache->mutex);
745
#else
746
  locked = (bool)scache->is_locked;
747
#endif
748
4.09M
  return locked;
749
4.09M
}
750
751
static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer,
752
                                     struct ssl_filter_config *conn_config)
753
237k
{
754
237k
  if(!conn_config) {
755
88.1k
    if(peer->clientcert || peer->key_passwd)
756
0
      return FALSE;
757
88.1k
    return TRUE;
758
88.1k
  }
759
149k
  else if(!Curl_safecmp(peer->clientcert, conn_config->clientcert))
760
0
    return FALSE;
761
149k
  if(Curl_timestrcmp(peer->key_passwd, conn_config->key_passwd))
762
0
    return FALSE;
763
149k
  return TRUE;
764
149k
}
765
766
static CURLcode cf_ssl_find_peer_by_key(struct Curl_easy *data,
767
                                        struct Curl_ssl_scache *scache,
768
                                        const char *ssl_peer_key,
769
                                        struct ssl_filter_config *conn_config,
770
                                        struct Curl_ssl_scache_peer **ppeer)
771
389k
{
772
389k
  size_t i, peer_key_len = 0;
773
389k
  CURLcode result = CURLE_OK;
774
775
389k
  *ppeer = NULL;
776
389k
  if(!GOOD_SCACHE(scache)) {
777
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
778
0
  }
779
780
389k
  CURL_TRC_SSLS(data, "find peer slot for %s among %zu slots",
781
389k
                ssl_peer_key, scache->peer_count);
782
783
  /* check for entries with known peer_key */
784
6.37M
  for(i = 0; scache && i < scache->peer_count; i++) {
785
6.13M
    if(scache->peers[i].ssl_peer_key &&
786
157k
       !strcmp(ssl_peer_key, scache->peers[i].ssl_peer_key) &&
787
149k
       cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
788
      /* yes, we have a cached session for this! */
789
149k
      *ppeer = &scache->peers[i];
790
149k
      goto out;
791
149k
    }
792
6.13M
  }
793
  /* check for entries with HMAC set but no known peer_key */
794
6.22M
  for(i = 0; scache && i < scache->peer_count; i++) {
795
5.98M
    if(!scache->peers[i].ssl_peer_key &&
796
5.97M
       scache->peers[i].hmac_set &&
797
0
       cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
798
      /* possible entry with unknown peer_key, check hmac */
799
0
      unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH];
800
0
      if(!peer_key_len) /* we are lazy */
801
0
        peer_key_len = strlen(ssl_peer_key);
802
0
      result = Curl_hmacit(&Curl_HMAC_SHA256,
803
0
                           scache->peers[i].key_salt,
804
0
                           sizeof(scache->peers[i].key_salt),
805
0
                           (const unsigned char *)ssl_peer_key,
806
0
                           peer_key_len,
807
0
                           my_hmac);
808
0
      if(result)
809
0
        goto out;
810
0
      if(!memcmp(scache->peers[i].key_hmac, my_hmac, sizeof(my_hmac))) {
811
        /* remember peer_key for future lookups */
812
0
        CURL_TRC_SSLS(data, "peer entry %zu key recovered: %s",
813
0
                      i, ssl_peer_key);
814
0
        scache->peers[i].ssl_peer_key = curlx_strdup(ssl_peer_key);
815
0
        if(!scache->peers[i].ssl_peer_key) {
816
0
          result = CURLE_OUT_OF_MEMORY;
817
0
          goto out;
818
0
        }
819
0
        cf_ssl_cache_peer_update(&scache->peers[i]);
820
0
        *ppeer = &scache->peers[i];
821
0
        goto out;
822
0
      }
823
0
    }
824
5.98M
  }
825
239k
  CURL_TRC_SSLS(data, "peer not found for %s", ssl_peer_key);
826
389k
out:
827
389k
  return result;
828
239k
}
829
830
static struct Curl_ssl_scache_peer *cf_ssl_get_free_peer(
831
  struct Curl_ssl_scache *scache)
832
104k
{
833
104k
  struct Curl_ssl_scache_peer *peer = NULL;
834
104k
  size_t i;
835
836
  /* find empty or oldest peer */
837
106k
  for(i = 0; i < scache->peer_count; ++i) {
838
    /* free peer entry? */
839
106k
    if(!scache->peers[i].ssl_peer_key && !scache->peers[i].hmac_set) {
840
104k
      peer = &scache->peers[i];
841
104k
      break;
842
104k
    }
843
    /* peer without sessions and obj */
844
1.95k
    if(!scache->peers[i].sobj &&
845
1.95k
       !Curl_llist_count(&scache->peers[i].sessions)) {
846
0
      peer = &scache->peers[i];
847
0
      break;
848
0
    }
849
    /* remember "oldest" peer */
850
1.95k
    if(!peer || (scache->peers[i].age < peer->age)) {
851
1.55k
      peer = &scache->peers[i];
852
1.55k
    }
853
1.95k
  }
854
104k
  DEBUGASSERT(peer);
855
104k
  if(peer)
856
104k
    cf_ssl_scache_clear_peer(peer);
857
104k
  return peer;
858
104k
}
859
860
static CURLcode cf_ssl_add_peer(struct Curl_easy *data,
861
                                struct Curl_ssl_scache *scache,
862
                                const char *ssl_peer_key,
863
                                struct ssl_filter_config *conn_config,
864
                                struct Curl_ssl_scache_peer **ppeer)
865
230k
{
866
230k
  struct Curl_ssl_scache_peer *peer = NULL;
867
230k
  CURLcode result = CURLE_OK;
868
869
230k
  *ppeer = NULL;
870
230k
  if(ssl_peer_key) {
871
230k
    result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
872
230k
                                     &peer);
873
230k
    if(result || !scache->peer_count)
874
0
      return result;
875
230k
  }
876
877
230k
  if(peer) {
878
126k
    *ppeer = peer;
879
126k
    return CURLE_OK;
880
126k
  }
881
882
104k
  peer = cf_ssl_get_free_peer(scache);
883
104k
  if(peer) {
884
104k
    char buffer[64];
885
104k
    const char *ccert = conn_config ? conn_config->clientcert : NULL;
886
104k
    const char *kpasswd = conn_config ? conn_config->key_passwd : NULL;
887
104k
    if(!ccert && conn_config && conn_config->cert_blob) {
888
      /* when using a client cert blob, create a name for it */
889
0
      curl_msnprintf(buffer, sizeof(buffer),
890
0
                     "cert-%p", conn_config->cert_blob->data);
891
0
      ccert = buffer; /* data is strduped by cf_ssl_scache_peer_init */
892
0
    }
893
104k
    result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert, kpasswd,
894
104k
                                     NULL, NULL);
895
104k
    if(result)
896
44
      goto out;
897
    /* all ready */
898
104k
    *ppeer = peer;
899
104k
    result = CURLE_OK;
900
104k
  }
901
902
104k
out:
903
104k
  if(result) {
904
44
    cf_ssl_scache_clear_peer(peer);
905
44
  }
906
104k
  return result;
907
104k
}
908
909
static void cf_scache_peer_add_session(struct Curl_ssl_scache_peer *peer,
910
                                       struct Curl_ssl_session *s,
911
                                       curl_off_t now)
912
318k
{
913
  /* A session not from TLSv1.3 replaces all other. */
914
318k
  if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3) {
915
8.16k
    Curl_llist_destroy(&peer->sessions, NULL);
916
8.16k
    Curl_llist_append(&peer->sessions, s, &s->list);
917
8.16k
  }
918
310k
  else {
919
    /* Expire existing, append, trim from head to obey max_sessions */
920
310k
    cf_scache_peer_remove_expired(peer, now);
921
310k
    cf_scache_peer_remove_non13(peer);
922
310k
    Curl_llist_append(&peer->sessions, s, &s->list);
923
396k
    while(Curl_llist_count(&peer->sessions) > peer->max_sessions) {
924
85.9k
      Curl_node_remove(Curl_llist_head(&peer->sessions));
925
85.9k
    }
926
310k
  }
927
318k
}
928
929
static CURLcode cf_scache_add_session(struct Curl_cfilter *cf,
930
                                      struct Curl_easy *data,
931
                                      struct Curl_ssl_scache *scache,
932
                                      const char *ssl_peer_key,
933
                                      struct Curl_ssl_session *s)
934
230k
{
935
230k
  struct Curl_ssl_scache_peer *peer = NULL;
936
230k
  struct ssl_filter_config *conn_config = Curl_ssl_cf_get_filter_config(cf);
937
230k
  CURLcode result = CURLE_OUT_OF_MEMORY;
938
230k
  curl_off_t now = (curl_off_t)time(NULL);
939
230k
  curl_off_t max_lifetime;
940
941
230k
  if(!scache || !scache->peer_count) {
942
0
    Curl_ssl_session_destroy(s);
943
0
    return CURLE_OK;
944
0
  }
945
946
230k
  if(s->valid_until <= 0)
947
85.7k
    s->valid_until = now + scache->default_lifetime_secs;
948
949
230k
  max_lifetime = (s->ietf_tls_id == CURL_IETF_PROTO_TLS1_3) ?
950
224k
                 CURL_SCACHE_MAX_13_LIFETIME_SEC :
951
230k
                 CURL_SCACHE_MAX_12_LIFETIME_SEC;
952
230k
  if(s->valid_until > (now + max_lifetime))
953
0
    s->valid_until = now + max_lifetime;
954
955
230k
  if(cf_scache_session_expired(s, now)) {
956
0
    CURL_TRC_SSLS(data, "add, session already expired");
957
0
    Curl_ssl_session_destroy(s);
958
0
    return CURLE_OK;
959
0
  }
960
961
230k
  result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer);
962
230k
  if(result || !peer) {
963
44
    CURL_TRC_SSLS(data, "unable to add scache peer: %d", (int)result);
964
44
    Curl_ssl_session_destroy(s);
965
44
    goto out;
966
44
  }
967
968
230k
  cf_scache_peer_add_session(peer, s, now);
969
970
230k
out:
971
230k
  if(result) {
972
44
    failf(data, "[SCACHE] failed to add session for %s, error=%d",
973
44
          ssl_peer_key, (int)result);
974
44
  }
975
230k
  else
976
230k
    CURL_TRC_SSLS(data, "added session for %s [proto=0x%x, "
977
230k
                  "valid_secs=%" FMT_OFF_T ", alpn=%s, earlydata=%zu, "
978
230k
                  "quic_tp=%s], peer has %zu sessions now", ssl_peer_key,
979
230k
                  (unsigned int)s->ietf_tls_id, s->valid_until - now,
980
230k
                  s->alpn, s->earlydata_max, s->quic_tp ? "yes" : "no",
981
230k
                  peer ? Curl_llist_count(&peer->sessions) : 0);
982
230k
  return result;
983
230k
}
984
985
CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf,
986
                             struct Curl_easy *data,
987
                             const char *ssl_peer_key,
988
                             struct Curl_ssl_session *s)
989
230k
{
990
230k
  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
991
230k
  struct ssl_filter_config *conn_config = Curl_ssl_cf_get_filter_config(cf);
992
230k
  CURLcode result;
993
230k
  DEBUGASSERT(conn_config);
994
995
230k
  if(!scache || !conn_config->cache_session) {
996
0
    Curl_ssl_session_destroy(s);
997
0
    return CURLE_OK;
998
0
  }
999
1000
230k
  Curl_ssl_scache_lock(data);
1001
230k
  result = cf_scache_add_session(cf, data, scache, ssl_peer_key, s);
1002
230k
  Curl_ssl_scache_unlock(data);
1003
230k
  return result;
1004
230k
}
1005
1006
void Curl_ssl_scache_return(struct Curl_cfilter *cf,
1007
                            struct Curl_easy *data,
1008
                            const char *ssl_peer_key,
1009
                            struct Curl_ssl_session *s)
1010
158k
{
1011
  /* See RFC 8446 C.4:
1012
   * "Clients SHOULD NOT reuse a ticket for multiple connections." */
1013
158k
  if(s && s->ietf_tls_id < 0x304)
1014
1.96k
    (void)Curl_ssl_scache_put(cf, data, ssl_peer_key, s);
1015
156k
  else
1016
156k
    Curl_ssl_session_destroy(s);
1017
158k
}
1018
1019
CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf,
1020
                              struct Curl_easy *data,
1021
                              const char *ssl_peer_key,
1022
                              struct Curl_ssl_session **ps)
1023
157k
{
1024
157k
  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
1025
157k
  struct ssl_filter_config *conn_config = Curl_ssl_cf_get_filter_config(cf);
1026
157k
  struct Curl_ssl_scache_peer *peer = NULL;
1027
157k
  struct Curl_llist_node *n;
1028
157k
  struct Curl_ssl_session *s = NULL;
1029
157k
  CURLcode result;
1030
1031
157k
  *ps = NULL;
1032
157k
  if(!scache)
1033
0
    return CURLE_OK;
1034
1035
157k
  Curl_ssl_scache_lock(data);
1036
157k
  result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
1037
157k
                                   &peer);
1038
157k
  if(!result && peer) {
1039
23.3k
    cf_scache_peer_remove_expired(peer, (curl_off_t)time(NULL));
1040
23.3k
    n = Curl_llist_head(&peer->sessions);
1041
23.3k
    if(n) {
1042
23.3k
      s = Curl_node_take_elem(n);
1043
23.3k
      scache->age++;           /* increase general age */
1044
23.3k
      peer->age = scache->age; /* set this as used in this age */
1045
23.3k
    }
1046
23.3k
  }
1047
157k
  if(s) {
1048
23.3k
    *ps = s;
1049
23.3k
    CURL_TRC_SSLS(data, "took session for %s [proto=0x%x, "
1050
23.3k
                  "alpn=%s, earlydata=%zu, quic_tp=%s], %zu sessions remain",
1051
23.3k
                  ssl_peer_key, (unsigned int)s->ietf_tls_id, s->alpn,
1052
23.3k
                  s->earlydata_max, s->quic_tp ? "yes" : "no",
1053
23.3k
                  Curl_llist_count(&peer->sessions));
1054
23.3k
  }
1055
134k
  else {
1056
134k
    CURL_TRC_SSLS(data, "no cached session for %s", ssl_peer_key);
1057
134k
  }
1058
157k
  Curl_ssl_scache_unlock(data);
1059
157k
  return result;
1060
157k
}
1061
1062
CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
1063
                                 struct Curl_easy *data,
1064
                                 const char *ssl_peer_key,
1065
                                 void *sobj,
1066
                                 Curl_ssl_scache_obj_dtor *sobj_dtor_cb)
1067
0
{
1068
0
  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
1069
0
  struct ssl_filter_config *conn_config = Curl_ssl_cf_get_filter_config(cf);
1070
0
  struct Curl_ssl_scache_peer *peer = NULL;
1071
0
  CURLcode result;
1072
1073
0
  DEBUGASSERT(sobj);
1074
0
  DEBUGASSERT(sobj_dtor_cb);
1075
1076
0
  if(!scache) {
1077
0
    result = CURLE_BAD_FUNCTION_ARGUMENT;
1078
0
    goto out;
1079
0
  }
1080
1081
0
  result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer);
1082
0
  if(result || !peer) {
1083
0
    CURL_TRC_SSLS(data, "unable to add scache peer: %d", (int)result);
1084
0
    goto out;
1085
0
  }
1086
1087
0
  cf_ssl_scache_peer_set_obj(peer, sobj, sobj_dtor_cb);
1088
0
  sobj = NULL;  /* peer took ownership */
1089
1090
0
out:
1091
0
  if(sobj && sobj_dtor_cb)
1092
0
    sobj_dtor_cb(sobj);
1093
0
  return result;
1094
0
}
1095
1096
void *Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
1097
                              struct Curl_easy *data,
1098
                              const char *ssl_peer_key)
1099
0
{
1100
0
  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
1101
0
  struct ssl_filter_config *conn_config = Curl_ssl_cf_get_filter_config(cf);
1102
0
  struct Curl_ssl_scache_peer *peer = NULL;
1103
0
  CURLcode result;
1104
0
  void *sobj;
1105
1106
0
  if(!scache)
1107
0
    return NULL;
1108
1109
0
  result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
1110
0
                                   &peer);
1111
0
  if(result)
1112
0
    return NULL;
1113
1114
0
  sobj = peer ? peer->sobj : NULL;
1115
1116
0
  CURL_TRC_SSLS(data, "%s cached session for '%s'",
1117
0
                sobj ? "Found" : "No", ssl_peer_key);
1118
0
  return sobj;
1119
0
}
1120
1121
void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
1122
                                struct Curl_easy *data,
1123
                                const char *ssl_peer_key)
1124
618
{
1125
618
  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
1126
618
  struct ssl_filter_config *conn_config = Curl_ssl_cf_get_filter_config(cf);
1127
618
  struct Curl_ssl_scache_peer *peer = NULL;
1128
618
  CURLcode result;
1129
1130
618
  (void)cf;
1131
618
  if(!scache)
1132
0
    return;
1133
1134
618
  Curl_ssl_scache_lock(data);
1135
618
  result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
1136
618
                                   &peer);
1137
618
  if(!result && peer)
1138
71
    cf_ssl_scache_clear_peer(peer);
1139
618
  Curl_ssl_scache_unlock(data);
1140
618
}
1141
1142
#ifdef USE_SSLS_EXPORT
1143
1144
112k
#define CURL_SSL_TICKET_MAX   (16 * 1024)
1145
1146
static CURLcode cf_ssl_scache_peer_set_hmac(struct Curl_ssl_scache_peer *peer)
1147
88.2k
{
1148
88.2k
  CURLcode result;
1149
1150
88.2k
  DEBUGASSERT(peer);
1151
88.2k
  if(!peer->ssl_peer_key)
1152
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
1153
1154
88.2k
  result = Curl_rand(NULL, peer->key_salt, sizeof(peer->key_salt));
1155
88.2k
  if(result)
1156
0
    return result;
1157
1158
88.2k
  result = Curl_hmacit(&Curl_HMAC_SHA256,
1159
88.2k
                       peer->key_salt, sizeof(peer->key_salt),
1160
88.2k
                       (const unsigned char *)peer->ssl_peer_key,
1161
88.2k
                       strlen(peer->ssl_peer_key),
1162
88.2k
                       peer->key_hmac);
1163
88.2k
  if(!result)
1164
88.1k
    peer->hmac_set = TRUE;
1165
88.2k
  return result;
1166
88.2k
}
1167
1168
static CURLcode cf_ssl_find_peer_by_hmac(struct Curl_ssl_scache *scache,
1169
                                         const unsigned char *salt,
1170
                                         const unsigned char *hmac,
1171
                                         struct Curl_ssl_scache_peer **ppeer)
1172
88.1k
{
1173
88.1k
  size_t i;
1174
88.1k
  CURLcode result = CURLE_OK;
1175
1176
88.1k
  *ppeer = NULL;
1177
88.1k
  if(!GOOD_SCACHE(scache))
1178
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
1179
1180
  /* look for an entry that matches salt+hmac exactly or has a known
1181
   * ssl_peer_key which salt+hmac's to the same. */
1182
88.1k
  for(i = 0; scache && i < scache->peer_count; i++) {
1183
88.1k
    struct Curl_ssl_scache_peer *peer = &scache->peers[i];
1184
88.1k
    if(!cf_ssl_scache_match_auth(peer, NULL))
1185
0
      continue;
1186
88.1k
    if(scache->peers[i].hmac_set &&
1187
88.1k
       !memcmp(peer->key_salt, salt, sizeof(peer->key_salt)) &&
1188
88.1k
       !memcmp(peer->key_hmac, hmac, sizeof(peer->key_hmac))) {
1189
      /* found exact match, return */
1190
88.1k
      *ppeer = peer;
1191
88.1k
      goto out;
1192
88.1k
    }
1193
0
    else if(peer->ssl_peer_key) {
1194
0
      unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH];
1195
      /* compute hmac for the passed salt */
1196
0
      result = Curl_hmacit(&Curl_HMAC_SHA256,
1197
0
                           salt, sizeof(peer->key_salt),
1198
0
                           (const unsigned char *)peer->ssl_peer_key,
1199
0
                           strlen(peer->ssl_peer_key),
1200
0
                           my_hmac);
1201
0
      if(result)
1202
0
        goto out;
1203
0
      if(!memcmp(my_hmac, hmac, sizeof(my_hmac))) {
1204
        /* cryptohash match, take over salt+hmac if no set and return */
1205
0
        if(!peer->hmac_set) {
1206
0
          memcpy(peer->key_salt, salt, sizeof(peer->key_salt));
1207
0
          memcpy(peer->key_hmac, hmac, sizeof(peer->key_hmac));
1208
0
          peer->hmac_set = TRUE;
1209
0
        }
1210
0
        *ppeer = peer;
1211
0
        goto out;
1212
0
      }
1213
0
    }
1214
88.1k
  }
1215
88.1k
out:
1216
88.1k
  return result;
1217
88.1k
}
1218
1219
CURLcode Curl_ssl_session_import(struct Curl_easy *data,
1220
                                 const char *ssl_peer_key,
1221
                                 const unsigned char *shmac, size_t shmac_len,
1222
                                 const void *sdata, size_t sdata_len)
1223
88.1k
{
1224
88.1k
  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
1225
88.1k
  struct Curl_ssl_scache_peer *peer = NULL;
1226
88.1k
  struct Curl_ssl_session *s = NULL;
1227
88.1k
  bool have_lock = FALSE;
1228
88.1k
  CURLcode result;
1229
1230
88.1k
  if(!scache || !sdata || !sdata_len) {
1231
0
    result = CURLE_BAD_FUNCTION_ARGUMENT;
1232
0
    goto out;
1233
0
  }
1234
88.1k
  if(!ssl_peer_key && (!shmac || !shmac_len)) {
1235
0
    result = CURLE_BAD_FUNCTION_ARGUMENT;
1236
0
    goto out;
1237
0
  }
1238
1239
88.1k
  result = Curl_ssl_session_unpack(data, sdata, sdata_len, &s);
1240
88.1k
  if(result)
1241
46
    goto out;
1242
1243
88.1k
  Curl_ssl_scache_lock(data);
1244
88.1k
  have_lock = TRUE;
1245
1246
88.1k
  if(ssl_peer_key) {
1247
0
    result = cf_ssl_add_peer(data, scache, ssl_peer_key, NULL, &peer);
1248
0
    if(result)
1249
0
      goto out;
1250
0
  }
1251
88.1k
  else if(shmac_len != (sizeof(peer->key_salt) + sizeof(peer->key_hmac))) {
1252
    /* Either salt+hmac was garbled by caller or is from a curl version
1253
     * that does things differently */
1254
0
    result = CURLE_BAD_FUNCTION_ARGUMENT;
1255
0
    goto out;
1256
0
  }
1257
88.1k
  else {
1258
88.1k
    const unsigned char *salt = shmac;
1259
88.1k
    const unsigned char *hmac = shmac + sizeof(peer->key_salt);
1260
1261
88.1k
    result = cf_ssl_find_peer_by_hmac(scache, salt, hmac, &peer);
1262
88.1k
    if(result)
1263
0
      goto out;
1264
88.1k
    if(!peer) {
1265
0
      peer = cf_ssl_get_free_peer(scache);
1266
0
      if(peer) {
1267
0
        result = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL, NULL,
1268
0
                                         salt, hmac);
1269
0
        if(result)
1270
0
          goto out;
1271
0
      }
1272
0
    }
1273
88.1k
  }
1274
1275
88.1k
  if(peer) {
1276
88.1k
    cf_scache_peer_add_session(peer, s, time(NULL));
1277
88.1k
    s = NULL; /* peer is now owner */
1278
88.1k
    CURL_TRC_SSLS(data, "successfully imported ticket for peer %s, now "
1279
88.1k
                  "with %zu tickets",
1280
88.1k
                  peer->ssl_peer_key ? peer->ssl_peer_key : "without key",
1281
88.1k
                  Curl_llist_count(&peer->sessions));
1282
88.1k
  }
1283
1284
88.1k
out:
1285
88.1k
  if(scache && have_lock)
1286
88.1k
    Curl_ssl_scache_unlock(data);
1287
88.1k
  Curl_ssl_session_destroy(s);
1288
88.1k
  return result;
1289
88.1k
}
1290
1291
CURLcode Curl_ssl_session_export(struct Curl_easy *data,
1292
                                 curl_ssls_export_cb *export_fn,
1293
                                 void *userptr)
1294
112k
{
1295
112k
  struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
1296
112k
  struct Curl_ssl_scache_peer *peer;
1297
112k
  struct dynbuf sbuf, hbuf;
1298
112k
  struct Curl_llist_node *n;
1299
112k
  size_t i;
1300
112k
  curl_off_t now = time(NULL);
1301
112k
  CURLcode result = CURLE_OK;
1302
112k
#ifdef CURLVERBOSE
1303
112k
  size_t npeers = 0, ntickets = 0;
1304
112k
#endif
1305
1306
112k
  if(!export_fn)
1307
0
    return CURLE_BAD_FUNCTION_ARGUMENT;
1308
1309
112k
  Curl_ssl_scache_lock(data);
1310
1311
112k
  curlx_dyn_init(&hbuf, (CURL_SHA256_DIGEST_LENGTH * 2) + 1);
1312
112k
  curlx_dyn_init(&sbuf, CURL_SSL_TICKET_MAX);
1313
1314
2.92M
  for(i = 0; scache && i < scache->peer_count; i++) {
1315
2.81M
    peer = &scache->peers[i];
1316
2.81M
    if(!peer->ssl_peer_key && !peer->hmac_set)
1317
2.72M
      continue; /* skip free entry */
1318
88.2k
    if(!peer->exportable)
1319
0
      continue;
1320
1321
88.2k
    curlx_dyn_reset(&hbuf);
1322
88.2k
    cf_scache_peer_remove_expired(peer, now);
1323
88.2k
    n = Curl_llist_head(&peer->sessions);
1324
88.2k
    if(n)
1325
88.2k
      VERBOSE(++npeers);
1326
225k
    while(n) {
1327
137k
      struct Curl_ssl_session *s = Curl_node_elem(n);
1328
137k
      if(!peer->hmac_set) {
1329
88.2k
        result = cf_ssl_scache_peer_set_hmac(peer);
1330
88.2k
        if(result)
1331
40
          goto out;
1332
88.2k
      }
1333
137k
      if(!curlx_dyn_len(&hbuf)) {
1334
88.2k
        result = curlx_dyn_addn(&hbuf, peer->key_salt, sizeof(peer->key_salt));
1335
88.2k
        if(result)
1336
19
          goto out;
1337
88.2k
        result = curlx_dyn_addn(&hbuf, peer->key_hmac, sizeof(peer->key_hmac));
1338
88.2k
        if(result)
1339
20
          goto out;
1340
88.2k
      }
1341
137k
      curlx_dyn_reset(&sbuf);
1342
137k
      result = Curl_ssl_session_pack(data, s, &sbuf);
1343
137k
      if(result)
1344
43
        goto out;
1345
1346
137k
      result = export_fn(data, userptr, peer->ssl_peer_key,
1347
137k
                         curlx_dyn_uptr(&hbuf), curlx_dyn_len(&hbuf),
1348
137k
                         curlx_dyn_uptr(&sbuf), curlx_dyn_len(&sbuf),
1349
137k
                         s->valid_until, s->ietf_tls_id,
1350
137k
                         s->alpn, s->earlydata_max);
1351
137k
      if(result)
1352
0
        goto out;
1353
137k
      VERBOSE(++ntickets);
1354
137k
      n = Curl_node_next(n);
1355
137k
    }
1356
88.2k
  }
1357
112k
  result = CURLE_OK;
1358
112k
  CURL_TRC_SSLS(data, "exported %zu session tickets for %zu peers",
1359
112k
                ntickets, npeers);
1360
1361
112k
out:
1362
112k
  Curl_ssl_scache_unlock(data);
1363
112k
  curlx_dyn_free(&hbuf);
1364
112k
  curlx_dyn_free(&sbuf);
1365
112k
  return result;
1366
112k
}
1367
1368
#endif /* USE_SSLS_EXPORT */
1369
1370
#endif /* USE_SSL */