Coverage Report

Created: 2025-08-03 06:52

/src/libwebsockets/lib/secure-streams/policy-common.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * libwebsockets - small server side websockets and web server implementation
3
 *
4
 * Copyright (C) 2019 - 2021 Andy Green <andy@warmcat.com>
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy
7
 * of this software and associated documentation files (the "Software"), to
8
 * deal in the Software without restriction, including without limitation the
9
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10
 * sell copies of the Software, and to permit persons to whom the Software is
11
 * furnished to do so, subject to the following conditions:
12
 *
13
 * The above copyright notice and this permission notice shall be included in
14
 * all copies or substantial portions of the Software.
15
 *
16
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22
 * IN THE SOFTWARE.
23
 *
24
 * This file contains the stuff related to secure streams policy, it's always
25
 * built if LWS_WITH_SECURE_STREAMS enabled.
26
 */
27
28
#include <private-lib-core.h>
29
30
#if defined(LWS_WITH_SYS_SMD)
31
const lws_ss_policy_t pol_smd = {
32
  .flags      = 0, /* have to set something for windows */
33
};
34
#endif
35
36
const lws_ss_policy_t *
37
lws_ss_policy_lookup(const struct lws_context *context, const char *streamtype)
38
0
{
39
0
  const lws_ss_policy_t *p = context->pss_policies;
40
41
0
  if (!streamtype)
42
0
    return NULL;
43
44
0
#if defined(LWS_WITH_SYS_SMD)
45
0
  if (!strcmp(streamtype, LWS_SMD_STREAMTYPENAME))
46
0
    return &pol_smd;
47
0
#endif
48
49
0
  while (p) {
50
0
    if (!strcmp(p->streamtype, streamtype))
51
0
      return p;
52
0
    p = p->next;
53
0
  }
54
55
0
  return NULL;
56
0
}
57
58
int
59
_lws_ss_set_metadata(lws_ss_metadata_t *omd, const char *name,
60
         const void *value, size_t len)
61
0
{
62
  /*
63
   * If there was already a heap-based value, it's about to go out of
64
   * scope due to us trashing the pointer.  So free it first and clear
65
   * its flag indicating it's heap-based.
66
   */
67
68
0
  if (omd->value_on_lws_heap) {
69
0
    lws_free_set_NULL(omd->value__may_own_heap);
70
0
    omd->value_on_lws_heap = 0;
71
0
  }
72
73
  // lwsl_notice("%s: %s %s\n", __func__, name, (const char *)value);
74
75
0
  omd->name = name;
76
0
  omd->value__may_own_heap = (void *)value;
77
0
  omd->length = len;
78
79
0
  return 0;
80
0
}
81
82
int
83
lws_ss_set_metadata(struct lws_ss_handle *h, const char *name,
84
        const void *value, size_t len)
85
0
{
86
0
  lws_ss_metadata_t *omd = lws_ss_get_handle_metadata(h, name);
87
88
0
  lws_service_assert_loop_thread(h->context, h->tsi);
89
90
0
  if (omd)
91
0
    return _lws_ss_set_metadata(omd, name, value, len);
92
93
#if defined(LWS_WITH_SS_DIRECT_PROTOCOL_STR)
94
  if (h->policy->flags & LWSSSPOLF_DIRECT_PROTO_STR) {
95
    omd = lws_ss_get_handle_instant_metadata(h, name);
96
    if (!omd) {
97
      omd = lws_zalloc(sizeof(*omd), "imetadata");
98
      if (!omd) {
99
        lwsl_err("%s OOM\n", __func__);
100
        return 1;
101
      }
102
      omd->name = name;
103
      omd->next = h->instant_metadata;
104
      h->instant_metadata = omd;
105
    }
106
    omd->value__may_own_heap = (void *)value;
107
    omd->length = len;
108
109
    return 0;
110
  }
111
#endif
112
113
0
  lwsl_info("%s: unknown metadata %s\n", __func__, name);
114
0
  return 1;
115
0
}
116
117
int
118
_lws_ss_alloc_set_metadata(lws_ss_metadata_t *omd, const char *name,
119
         const void *value, size_t len)
120
0
{
121
0
  uint8_t *p;
122
0
  int n;
123
124
0
  if (omd->value_on_lws_heap) {
125
0
    lws_free_set_NULL(omd->value__may_own_heap);
126
0
    omd->value_on_lws_heap = 0;
127
0
  }
128
129
0
  p = lws_malloc(len, __func__);
130
0
  if (!p)
131
0
    return 1;
132
133
0
  n = _lws_ss_set_metadata(omd, name, p, len);
134
0
  if (n) {
135
0
    lws_free(p);
136
0
    return n;
137
0
  }
138
139
0
  memcpy(p, value, len);
140
141
0
  omd->value_on_lws_heap = 1;
142
143
0
  return 0;
144
0
}
145
146
int
147
lws_ss_alloc_set_metadata(struct lws_ss_handle *h, const char *name,
148
        const void *value, size_t len)
149
0
{
150
0
  lws_ss_metadata_t *omd = lws_ss_get_handle_metadata(h, name);
151
152
0
  lws_service_assert_loop_thread(h->context, h->tsi);
153
154
0
  if (!omd) {
155
0
    lwsl_info("%s: unknown metadata %s\n", __func__, name);
156
0
    return 1;
157
0
  }
158
159
0
  return _lws_ss_alloc_set_metadata(omd, name, value, len);
160
0
}
161
162
int
163
lws_ss_get_metadata(struct lws_ss_handle *h, const char *name,
164
        const void **value, size_t *len)
165
0
{
166
0
  lws_ss_metadata_t *omd = lws_ss_get_handle_metadata(h, name);
167
#if defined(LWS_WITH_SS_DIRECT_PROTOCOL_STR)
168
  int n;
169
#endif
170
171
0
  lws_service_assert_loop_thread(h->context, h->tsi);
172
173
0
  if (omd) {
174
0
    *value = omd->value__may_own_heap;
175
0
    *len = omd->length;
176
177
0
    return 0;
178
0
  }
179
#if defined(LWS_WITH_SS_DIRECT_PROTOCOL_STR)
180
  if (!(h->policy->flags & LWSSSPOLF_DIRECT_PROTO_STR) || !h->wsi)
181
    goto bail;
182
183
  n = lws_http_string_to_known_header(name, strlen(name));
184
  if (n != LWS_HTTP_NO_KNOWN_HEADER) {
185
    *len = (size_t)lws_hdr_total_length(h->wsi, n);
186
    if (!*len)
187
      goto bail;
188
    *value = lws_hdr_simple_ptr(h->wsi, n);
189
    if (!*value)
190
      goto bail;
191
192
    return 0;
193
  }
194
#if defined(LWS_WITH_CUSTOM_HEADERS)
195
  n = lws_hdr_custom_length(h->wsi, (const char *)name,
196
          (int)strlen(name));
197
  if (n <= 0)
198
    goto bail;
199
  *value = lwsac_use(&h->imd_ac, (size_t)(n+1), (size_t)(n+1));
200
  if (!*value) {
201
    lwsl_err("%s ac OOM\n", __func__);
202
    return 1;
203
  }
204
  if (lws_hdr_custom_copy(h->wsi, (char *)(*value), n+1, name,
205
        (int)strlen(name))) {
206
    /* waste n+1 bytes until ss is destryed */
207
    goto bail;
208
  }
209
  *len = (size_t)n;
210
211
  return 0;
212
#endif
213
214
bail:
215
#endif
216
0
  lwsl_info("%s: unknown metadata %s\n", __func__, name);
217
218
0
  return 1;
219
0
}
220
221
lws_ss_metadata_t *
222
lws_ss_get_handle_metadata(struct lws_ss_handle *h, const char *name)
223
0
{
224
0
  int n;
225
226
0
  lws_service_assert_loop_thread(h->context, h->tsi);
227
228
0
  for (n = 0; n < h->policy->metadata_count; n++)
229
0
    if (!strcmp(name, h->metadata[n].name))
230
0
      return &h->metadata[n];
231
232
0
  return NULL;
233
0
}
234
235
#if defined(LWS_WITH_SS_DIRECT_PROTOCOL_STR)
236
lws_ss_metadata_t *
237
lws_ss_get_handle_instant_metadata(struct lws_ss_handle *h, const char *name)
238
{
239
  lws_ss_metadata_t *imd = h->instant_metadata;
240
241
  while (imd) {
242
    if (!strcmp(name, imd->name))
243
      return imd;
244
    imd = imd->next;
245
  }
246
247
  return NULL;
248
}
249
250
#endif
251
252
253
lws_ss_metadata_t *
254
lws_ss_policy_metadata(const lws_ss_policy_t *p, const char *name)
255
0
{
256
0
  lws_ss_metadata_t *pmd = p->metadata;
257
258
0
  while (pmd) {
259
0
    if (pmd->name && !strcmp(name, pmd->name))
260
0
      return pmd;
261
0
    pmd = pmd->next;
262
0
  }
263
264
0
  return NULL;
265
0
}
266
267
lws_ss_metadata_t *
268
lws_ss_policy_metadata_index(const lws_ss_policy_t *p, size_t index)
269
0
{
270
0
  lws_ss_metadata_t *pmd = p->metadata;
271
272
0
  while (pmd) {
273
0
    if (pmd->length == index)
274
0
      return pmd;
275
0
    pmd = pmd->next;
276
0
  }
277
278
0
  return NULL;
279
0
}
280
281
#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
282
static int
283
fe_lws_ss_destroy(struct lws_dll2 *d, void *user)
284
0
{
285
0
  lws_ss_handle_t *h = lws_container_of(d, lws_ss_handle_t, list);
286
287
0
  lws_ss_destroy(&h);
288
289
0
  return 0;
290
0
}
291
#endif
292
293
/*
294
 * Dynamic policy: we want to one-time create the vhost for the policy and the
295
 * trust store behind it.
296
 *
297
 * Static policy: We want to make use of a trust store / vhost from the policy and add to its
298
 * ss-refcount.
299
 */
300
301
struct lws_vhost *
302
lws_ss_policy_ref_trust_store(struct lws_context *context,
303
            const lws_ss_policy_t *pol, char doref)
304
0
{
305
0
  struct lws_context_creation_info i;
306
0
  struct lws_vhost *v;
307
0
  int n;
308
309
0
  memset(&i, 0, sizeof(i));
310
311
0
  if (!pol->trust.store) {
312
0
    v = lws_get_vhost_by_name(context, "_ss_default");
313
0
    if (!v) {
314
      /* corner case... there's no trust store used */
315
0
      i.options = context->options;
316
0
      i.vhost_name = "_ss_default";
317
0
      i.port = CONTEXT_PORT_NO_LISTEN;
318
0
      v = lws_create_vhost(context, &i);
319
0
      if (!v) {
320
0
        lwsl_err("%s: failed to create vhost %s\n",
321
0
           __func__, i.vhost_name);
322
323
0
        return NULL;
324
0
      }
325
0
    }
326
327
0
    goto accepted;
328
0
  }
329
0
  v = lws_get_vhost_by_name(context, pol->trust.store->name);
330
0
  if (v) {
331
0
    lwsl_debug("%s: vh already exists\n", __func__);
332
0
    goto accepted;
333
0
  }
334
335
0
  i.options = context->options;
336
0
  i.vhost_name = pol->trust.store->name;
337
0
  lwsl_debug("%s: %s\n", __func__, i.vhost_name);
338
0
#if defined(LWS_WITH_TLS) && defined(LWS_WITH_CLIENT)
339
0
  i.client_ssl_ca_mem = pol->trust.store->ssx509[0]->ca_der;
340
0
  i.client_ssl_ca_mem_len = (unsigned int)
341
0
      pol->trust.store->ssx509[0]->ca_der_len;
342
0
#endif
343
#if defined(LWS_ROLE_WS) && !defined(LWS_WITHOUT_EXTENSIONS)
344
  lwsl_err("%s: ctx ext %p\n", __func__, context->extensions);
345
  i.extensions = context->extensions;
346
#endif
347
0
  i.port = CONTEXT_PORT_NO_LISTEN;
348
0
  lwsl_info("%s: %s trust store initial '%s'\n", __func__,
349
0
      i.vhost_name, pol->trust.store->ssx509[0]->vhost_name);
350
351
0
  v = lws_create_vhost(context, &i);
352
0
  if (!v) {
353
0
    lwsl_err("%s: failed to create vhost %s\n",
354
0
       __func__, i.vhost_name);
355
0
    return NULL;
356
0
  } else
357
0
    v->from_ss_policy = 1;
358
359
0
  for (n = 1; v && n < pol->trust.store->count; n++) {
360
0
    lwsl_info("%s: add '%s' to trust store\n", __func__,
361
0
        pol->trust.store->ssx509[n]->vhost_name);
362
0
#if defined(LWS_WITH_TLS)
363
0
    if (lws_tls_client_vhost_extra_cert_mem(v,
364
0
        pol->trust.store->ssx509[n]->ca_der,
365
0
        pol->trust.store->ssx509[n]->ca_der_len)) {
366
0
      lwsl_err("%s: add extra cert failed\n",
367
0
          __func__);
368
0
      return NULL;
369
0
    }
370
0
#endif
371
0
  }
372
373
0
accepted:
374
#if defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) || defined(LWS_WITH_SECURE_STREAMS_CPP)
375
  if (doref)
376
    v->ss_refcount++;
377
#endif
378
379
0
  return v;
380
0
}
381
382
#if defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY) || defined(LWS_WITH_SECURE_STREAMS_CPP)
383
int
384
lws_ss_policy_unref_trust_store(struct lws_context *context,
385
        const lws_ss_policy_t *pol)
386
{
387
  struct lws_vhost *v;
388
  const char *name = "_ss_default";
389
390
  if (pol->trust.store)
391
    name = pol->trust.store->name;
392
393
  v = lws_get_vhost_by_name(context, name);
394
  if (!v || !v->from_ss_policy)
395
    return 0;
396
397
  assert(v->ss_refcount);
398
399
  v->ss_refcount--;
400
  if (!v->ss_refcount) {
401
    lwsl_notice("%s: destroying vh %s\n", __func__, name);
402
    lws_vhost_destroy(v);
403
  }
404
405
  return 1;
406
}
407
#endif
408
409
int
410
lws_ss_policy_set(struct lws_context *context, const char *name)
411
0
{
412
0
  int ret = 0;
413
414
0
#if !defined(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY)
415
0
  struct policy_cb_args *args = (struct policy_cb_args *)context->pol_args;
416
0
  const lws_ss_policy_t *pol;
417
0
  struct lws_vhost *v;
418
0
  lws_ss_x509_t *x;
419
0
  char buf[16];
420
0
  int m;
421
422
  /*
423
   * Parsing seems to have succeeded, and we're going to use the new
424
   * policy that's laid out in args->ac
425
   */
426
427
0
  if (!args)
428
0
    return 1;
429
430
0
  lejp_destruct(&args->jctx);
431
432
0
  if (context->ac_policy) {
433
0
    int n;
434
435
#if defined(LWS_WITH_SYS_METRICS)
436
    lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
437
             context->owner_mtr_dynpol.head) {
438
      lws_metric_policy_dyn_t *dm =
439
        lws_container_of(d, lws_metric_policy_dyn_t, list);
440
441
      lws_metric_policy_dyn_destroy(dm, 1); /* keep */
442
443
    } lws_end_foreach_dll_safe(d, d1);
444
#endif
445
446
    /*
447
     * any existing ss created with the old policy have to go away
448
     * now, since they point to the shortly-to-be-destroyed old
449
     * policy
450
     */
451
452
0
    for (n = 0; n < context->count_threads; n++) {
453
0
      struct lws_context_per_thread *pt = &context->pt[n];
454
455
0
      lws_dll2_foreach_safe(&pt->ss_owner, NULL, fe_lws_ss_destroy);
456
0
    }
457
458
    /*
459
     * So this is a bit fun-filled, we already had a policy in
460
     * force, perhaps it was the default policy that's just good for
461
     * fetching the real policy, and we're doing that now.
462
     *
463
     * We can destroy all the policy-related direct allocations
464
     * easily because they're cleanly in a single lwsac...
465
     */
466
0
    lwsac_free(&context->ac_policy);
467
468
    /*
469
     * ...but when we did the trust stores, we created vhosts for
470
     * each.  We need to destroy those now too, and recreate new
471
     * ones from the new policy, perhaps with different X.509s.
472
     *
473
     * Vhost destruction is inherently async, it can't be destroyed
474
     * until all of the wsi bound to it have closed, and, eg, libuv
475
     * means their closure is deferred until a later go around the
476
     * event loop.  SMP means we also have to wait for all the pts
477
     * to close their wsis that are bound on the vhost too.
478
     *
479
     * This marks the vhost as being destroyed so new things won't
480
     * use it, and starts the close of all wsi on this pt that are
481
     * bound to the wsi, and deals with the listen socket if any.
482
     * "being-destroyed" vhosts can't be found using get_vhost_by_
483
     * name(), so if a new vhost of the same name exists that isn't
484
     * being destroyed that will be the one found.
485
     *
486
     * When the number of wsi bound to the vhost gets to zero a
487
     * short time later, the vhost is actually destroyed.
488
     */
489
490
0
    v = context->vhost_list;
491
0
    while (v) {
492
0
      if (v->from_ss_policy) {
493
0
        struct lws_vhost *vh = v->vhost_next;
494
0
        lwsl_debug("%s: destroying %s\n", __func__, lws_vh_tag(v));
495
0
        lws_vhost_destroy(v);
496
0
        v = vh;
497
0
        continue;
498
0
      }
499
0
      v = v->vhost_next;
500
0
    }
501
0
  }
502
503
0
  context->pss_policies = args->heads[LTY_POLICY].p;
504
0
  context->ac_policy = args->ac;
505
506
0
  lws_humanize(buf, sizeof(buf), lwsac_total_alloc(args->ac),
507
0
      humanize_schema_si_bytes);
508
0
  if (lwsac_total_alloc(args->ac))
509
0
    m = (int)((lwsac_total_overhead(args->ac) * 100) /
510
0
        lwsac_total_alloc(args->ac));
511
0
  else
512
0
    m = 0;
513
514
0
  (void)m;
515
0
  lwsl_info("%s: %s, pad %d%c: %s\n", __func__, buf, m, '%', name);
516
517
  /* Create vhosts for each type of trust store */
518
519
  /*
520
   * We get called from context creation... instantiates
521
   * vhosts with client tls contexts set up for each unique CA.
522
   *
523
   * We create the vhosts by walking streamtype list and create vhosts
524
   * using trust store name if it's a client connection that doesn't
525
   * already exist.
526
   */
527
528
0
  pol = context->pss_policies;
529
0
  while (pol) {
530
0
    if (!(pol->flags & LWSSSPOLF_SERVER)) {
531
0
      v = lws_ss_policy_ref_trust_store(context, pol,
532
0
              0 /* no refcount inc */);
533
0
      if (!v)
534
0
        ret = 1;
535
0
    }
536
537
0
    pol = pol->next;
538
0
  }
539
540
#if defined(LWS_WITH_SOCKS5)
541
542
  /*
543
   * ... we need to go through every vhost updating its understanding of
544
   * which socks5 proxy to use...
545
   */
546
547
  v = context->vhost_list;
548
  while (v) {
549
    lws_set_socks(v, args->socks5_proxy);
550
    v = v->vhost_next;
551
  }
552
  if (context->vhost_system)
553
    lws_set_socks(context->vhost_system, args->socks5_proxy);
554
555
  if (args->socks5_proxy)
556
    lwsl_notice("%s: global socks5 proxy: %s\n", __func__,
557
          args->socks5_proxy);
558
#endif
559
560
  /*
561
   * For dynamic policy case, now we processed the x.509 CAs, we can free
562
   * all of our originals.  For static policy, they're in .rodata, nothing
563
   * to free.
564
   */
565
566
0
  x = args->heads[LTY_X509].x;
567
0
  while (x) {
568
    /*
569
     * Free all the client DER buffers now they have been parsed
570
     * into tls library X.509 objects
571
     */
572
0
    if (!x->keep) { /* used for server */
573
0
      lws_free((void *)x->ca_der);
574
0
      x->ca_der = NULL;
575
0
    }
576
577
0
    x = x->next;
578
0
  }
579
580
0
  context->last_policy = time(NULL);
581
#if defined(LWS_WITH_SYS_METRICS)
582
  if (context->pss_policies)
583
    ((lws_ss_policy_t *)context->pss_policies)->metrics =
584
            args->heads[LTY_METRICS].m;
585
#endif
586
587
  /* and we can discard the parsing args object now, invalidating args */
588
589
0
  lws_free_set_NULL(context->pol_args);
590
0
#endif
591
592
#if defined(LWS_WITH_SYS_METRICS)
593
  lws_metric_rebind_policies(context);
594
#endif
595
596
0
#if defined(LWS_WITH_SYS_SMD)
597
0
  (void)lws_smd_msg_printf(context, LWSSMDCL_SYSTEM_STATE,
598
0
         "{\"policy\":\"updated\",\"ts\":%lu}",
599
0
           (long)context->last_policy);
600
0
#endif
601
602
0
  return ret;
603
0
}