Coverage Report

Created: 2026-08-13 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebsockets/lib/tls/tls-network.c
Line
Count
Source
1
/*
2
 * libwebsockets - small server side websockets and web server implementation
3
 *
4
 * Copyright (C) 2010 - 2019 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
25
#include "private-lib-core.h"
26
27
/*
28
 * fakes POLLIN on all tls guys with buffered rx
29
 *
30
 * returns nonzero if any tls guys had POLLIN faked
31
 */
32
33
int
34
lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt)
35
0
{
36
0
  int ret = 0;
37
38
0
  lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1,
39
0
      lws_dll2_get_head(&pt->tls.dll_pending_tls_owner)) {
40
0
    struct lws *wsi = lws_container_of(p, struct lws,
41
0
               tls.dll_pending_tls);
42
43
    /*
44
     * ... allow custom event loop to override our POLLIN-setting
45
     * implementation if it knows how to do it better for its case
46
     */
47
             
48
0
    if (pt->context->event_loop_ops &&
49
0
        pt->context->event_loop_ops->fake_POLLIN_override)
50
0
      pt->context->event_loop_ops->fake_POLLIN_override(
51
0
              pt->context, pt->tid);
52
0
    else {         
53
0
      if (wsi->position_in_fds_table >= 0) {
54
55
0
        pt->fds[wsi->position_in_fds_table].revents = (short)
56
0
          (pt->fds[wsi->position_in_fds_table].revents |
57
0
           (pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN));
58
0
        ret |= pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN;
59
60
        // lwsl_notice("%s: faked POLLIN for %s, revents=0x%x\n", __func__, lws_wsi_tag(wsi), pt->fds[wsi->position_in_fds_table].revents);
61
0
      }
62
0
    }
63
64
0
  } lws_end_foreach_dll_safe(p, p1);
65
66
0
  return !!ret;
67
0
}
68
69
void
70
__lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
71
0
{
72
0
  lws_dll2_remove(&wsi->tls.dll_pending_tls);
73
0
}
74
75
void
76
lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
77
0
{
78
0
  struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
79
80
0
  lws_pt_lock(pt, __func__);
81
0
  __lws_ssl_remove_wsi_from_buffered_list(wsi);
82
0
  lws_pt_unlock(pt);
83
0
}
84
85
struct lws_tls_ctx_ref *
86
lws_tls_ctx_ref_create(struct lws_vhost *vh, lws_tls_ctx *ctx)
87
0
{
88
0
  struct lws_tls_ctx_ref *ref;
89
90
0
  if (!ctx)
91
0
    return NULL;
92
93
0
  ref = lws_zalloc(sizeof(*ref), "ctx_ref");
94
0
  if (!ref)
95
0
    return NULL;
96
97
0
  ref->vh = vh;
98
0
  ref->ctx = ctx;
99
0
  ref->refcount = 1;
100
101
0
  return ref;
102
0
}
103
104
struct lws_tls_ctx_ref *
105
lws_tls_ctx_ref_get(struct lws_vhost *vh)
106
0
{
107
0
  struct lws_tls_ctx_ref *ref;
108
109
0
  lws_vhost_lock(vh);
110
0
  ref = vh->tls.active_ctx_ref;
111
0
  if (ref)
112
0
    ref->refcount++;
113
0
  lws_vhost_unlock(vh);
114
115
0
  return ref;
116
0
}
117
118
void
119
lws_tls_ctx_ref_unref(struct lws_tls_ctx_ref *ref)
120
0
{
121
0
  struct lws_vhost *vh;
122
123
0
  if (!ref)
124
0
    return;
125
126
0
  vh = ref->vh;
127
0
  lws_vhost_lock(vh);
128
0
  if (--ref->refcount == 0) {
129
0
    lws_dll2_remove(&ref->list);
130
0
    lws_tls_vhost_backend_free_ctx(ref->ctx);
131
0
    lws_free(ref);
132
0
  }
133
0
  lws_vhost_unlock(vh);
134
0
}
135
136
void
137
lws_tls_ctx_ref_destroy_all(struct lws_vhost *vhost)
138
0
{
139
0
  if (vhost->tls.active_ctx_ref) {
140
0
    lws_tls_ctx_ref_unref(vhost->tls.active_ctx_ref);
141
0
    vhost->tls.active_ctx_ref = NULL;
142
0
    vhost->tls.ssl_ctx = NULL;
143
0
  }
144
145
0
  lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1,
146
0
           lws_dll2_get_head(&vhost->tls.retired_ctx_list)) {
147
0
    struct lws_tls_ctx_ref *r = lws_container_of(d, struct lws_tls_ctx_ref, list);
148
0
    lwsl_vhost_err(vhost, "Retired ctx_ref %p leaked with refcount %d", r, r->refcount);
149
    /* forcefully free it to avoid memory leak if WSI leaked */
150
0
    lws_dll2_remove(&r->list);
151
0
    lws_tls_vhost_backend_free_ctx(r->ctx);
152
0
    lws_free(r);
153
0
  } lws_end_foreach_dll_safe(d, d1);
154
0
}
155
156
157
#if defined(LWS_WITH_SERVER)
158
int
159
lws_tls_check_cert_lifetime(struct lws_vhost *v)
160
0
{
161
0
  time_t now = (time_t)lws_now_secs(), life = 0;
162
0
  struct lws_acme_cert_aging_args caa;
163
0
  union lws_tls_cert_info_results ir;
164
0
  int n;
165
166
  /* Check if the active cert context needs rotation under grace period policy */
167
0
  if (v->tls.ssl_ctx && v->tls.cfg_alloc_cert_path && v->tls.cfg_key_path &&
168
0
      (strstr(v->tls.cfg_alloc_cert_path, "-latest.crt") ||
169
0
       strstr(v->tls.cfg_alloc_cert_path, "-latest-fullchain.crt"))) {
170
0
    char resolved_cert[256];
171
0
    char resolved_key[256];
172
0
    time_t resolved_from = 0, resolved_to = 0;
173
0
    union lws_tls_cert_info_results loaded_from, loaded_to;
174
175
    /* Resolve what certificate path should be active right now */
176
0
    if (lws_tls_resolve_grace_period_certs(v->context,
177
0
                   v->tls.cfg_alloc_cert_path,
178
0
                   v->tls.cfg_key_path,
179
0
                   resolved_cert, sizeof(resolved_cert),
180
0
                   resolved_key, sizeof(resolved_key)) == 0) {
181
      /* Get validity of resolved cert file */
182
0
      if (lws_tls_cert_get_x509_validity(v->context, resolved_cert,
183
0
                 &resolved_from, &resolved_to) == 0) {
184
        /* Get validity of currently loaded cert context */
185
0
        if (lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_FROM,
186
0
                  &loaded_from, 0) == 0 &&
187
0
            lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_TO,
188
0
                  &loaded_to, 0) == 0) {
189
0
          if (resolved_from != loaded_from.time ||
190
0
              resolved_to != loaded_to.time) {
191
0
            lwsl_notice("%s: Active certificate for vhost %s is out of date. Rotating dynamically.\n",
192
0
                  __func__, v->name);
193
0
            lws_tls_cert_updated(v->context,
194
0
                     v->tls.cfg_alloc_cert_path,
195
0
                     v->tls.cfg_key_path,
196
0
                     NULL, 0, NULL, 0);
197
0
          }
198
0
        }
199
0
      }
200
0
    }
201
0
  }
202
203
0
  if (v->tls.ssl_ctx && !v->tls.skipped_certs) {
204
205
0
    if (now < 1542933698) /* Nov 23 2018 00:42 UTC */
206
      /* our clock is wrong and we can't judge the certs */
207
0
      return -1;
208
209
0
    n = lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_TO,
210
0
              &ir, 0);
211
0
    if (n)
212
0
      return 1;
213
214
0
    life = (ir.time - now) / (24 * 3600);
215
0
    lwsl_vhost_notice(v, "   vhost %s: cert expiry: %lldd", v->name,
216
0
          (long long)life);
217
0
  } else
218
0
    lwsl_vhost_info(v, "   vhost %s: no cert", v->name);
219
220
0
  memset(&caa, 0, sizeof(caa));
221
0
  caa.vh = v;
222
0
  lws_broadcast(&v->context->pt[0], LWS_CALLBACK_VHOST_CERT_AGING, (void *)&caa,
223
0
          (size_t)(ssize_t)life);
224
225
0
  return 0;
226
0
}
227
228
int
229
lws_tls_check_all_cert_lifetimes(struct lws_context *context)
230
0
{
231
0
  struct lws_vhost *v = context->vhost_list;
232
233
0
  while (v) {
234
0
    if (lws_tls_check_cert_lifetime(v) < 0)
235
0
      return -1;
236
0
    v = v->vhost_next;
237
0
  }
238
239
0
  return 0;
240
0
}
241
242
/*
243
 * LWS_TLS_EXTANT_NO         : skip adding the cert
244
 * LWS_TLS_EXTANT_YES        : use the cert and private key paths normally
245
 * LWS_TLS_EXTANT_ALTERNATIVE: normal paths not usable, try alternate if poss
246
 */
247
enum lws_tls_extant
248
lws_tls_generic_cert_checks(struct lws_vhost *vhost, const char *cert,
249
          const char *private_key)
250
0
{
251
0
  int n, m;
252
253
  /*
254
   * The user code can choose to either pass the cert and
255
   * key filepaths using the info members like this, or it can
256
   * leave them NULL; force the vhost SSL_CTX init using the info
257
   * options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and
258
   * set up the cert himself using the user callback
259
   * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which
260
   * happened just above and has the vhost SSL_CTX * in the user
261
   * parameter.
262
   */
263
264
0
  if (!cert || !private_key)
265
0
    return LWS_TLS_EXTANT_NO;
266
267
0
  n = (int)lws_tls_use_any_upgrade_check_extant(cert);
268
0
  if (n == LWS_TLS_EXTANT_ALTERNATIVE)
269
0
    return LWS_TLS_EXTANT_ALTERNATIVE;
270
0
  m = (int)lws_tls_use_any_upgrade_check_extant(private_key);
271
0
  if (m == LWS_TLS_EXTANT_ALTERNATIVE)
272
0
    return LWS_TLS_EXTANT_ALTERNATIVE;
273
274
0
  if ((n == LWS_TLS_EXTANT_NO || m == LWS_TLS_EXTANT_NO) &&
275
0
      (vhost->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) {
276
0
    lwsl_vhost_notice(vhost, "Ignoring missing %s or %s", cert, private_key);
277
0
    vhost->tls.skipped_certs = 1;
278
279
0
    return LWS_TLS_EXTANT_NO;
280
0
  }
281
282
  /*
283
   * the cert + key exist
284
   */
285
286
0
  return LWS_TLS_EXTANT_YES;
287
0
}
288
289
/*
290
 * update the cert for every vhost using the given path
291
 */
292
293
int
294
lws_tls_cert_updated(struct lws_context *context, const char *certpath,
295
         const char *keypath,
296
         const char *mem_cert, size_t len_mem_cert,
297
         const char *mem_privkey, size_t len_mem_privkey)
298
0
{
299
0
  struct lws wsi;
300
301
0
  wsi.a.context = context;
302
303
0
  lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) {
304
0
    wsi.a.vhost = v; /* not a real bound wsi */
305
0
    if (v->tls.cfg_alloc_cert_path && v->tls.cfg_key_path &&
306
0
        !strcmp(v->tls.cfg_alloc_cert_path, certpath) &&
307
0
        !strcmp(v->tls.cfg_key_path, keypath)) {
308
309
0
      lws_tls_ctx *old_ctx = v->tls.ssl_ctx;
310
0
      struct lws_tls_ctx_ref *old_ref = v->tls.active_ctx_ref;
311
312
0
      if (lws_tls_vhost_backend_create_ctx(v)) {
313
0
        lwsl_vhost_err(v, "Failed to recreate SSL_CTX");
314
0
        continue;
315
0
      }
316
317
0
      struct lws_tls_ctx_ref *new_ref = lws_tls_ctx_ref_create(v, v->tls.ssl_ctx);
318
0
      if (!new_ref) {
319
0
        lws_tls_vhost_backend_free_ctx(v->tls.ssl_ctx);
320
0
        v->tls.ssl_ctx = old_ctx;
321
0
        continue;
322
0
      }
323
324
0
      if (lws_tls_server_certs_load(v, &wsi, certpath, keypath,
325
0
              mem_cert, len_mem_cert,
326
0
              mem_privkey, len_mem_privkey)) {
327
        /* Failed to load new certs. Revert to old context */
328
0
        lws_tls_ctx_ref_unref(new_ref);
329
0
        v->tls.ssl_ctx = old_ctx;
330
0
        lwsl_vhost_err(v, "Failed to load updated certs");
331
0
        continue;
332
0
      }
333
334
      /* Successfully loaded. Commit new ref and retire old ref */
335
0
      v->tls.active_ctx_ref = new_ref;
336
337
0
      if (old_ref) {
338
0
        lws_dll2_add_tail(&old_ref->list, &v->tls.retired_ctx_list);
339
0
        lws_tls_ctx_ref_unref(old_ref);
340
0
      }
341
342
0
      if (v->tls.skipped_certs)
343
0
        lwsl_vhost_notice(v, "vhost %s: cert unset", v->name);
344
0
    }
345
0
  } lws_end_foreach_ll(v, vhost_next);
346
347
0
  return 0;
348
0
}
349
350
int
351
lws_gate_accepts(struct lws_context *context, int on)
352
0
{
353
0
  struct lws_vhost *v = context->vhost_list;
354
355
0
  if (context->tls_gate_accepts == (char)on)
356
0
    return 0;
357
358
0
  lwsl_cx_info(context, "on = %d", on);
359
360
0
  context->tls_gate_accepts = (char)on;
361
362
0
  while (v) {
363
0
    lws_start_foreach_dll(struct lws_dll2 *, d,
364
0
              lws_dll2_get_head(&v->listen_wsi)) {
365
0
      struct lws *wsi = lws_container_of(d, struct lws,
366
0
                 listen_list);
367
368
0
      if (v->tls.use_ssl &&
369
0
          lws_change_pollfd(wsi, on ? LWS_POLLIN : 0,
370
0
               on ? 0 : LWS_POLLIN))
371
0
        lwsl_cx_notice(context, "Unable to set POLLIN %d", on);
372
0
    } lws_end_foreach_dll(d);
373
374
0
    v = v->vhost_next;
375
0
  }
376
377
0
  return 0;
378
0
}
379
#endif
380
381
/* comma-separated alpn list, like "h2,http/1.1" to openssl alpn format */
382
383
int
384
lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len)
385
0
{
386
0
  uint8_t *oos = os, *plen = NULL;
387
388
0
  if (!comma)
389
0
    return 0;
390
391
0
  while (*comma && len > 0) {
392
0
    if (!plen && *comma == ' ') {
393
0
      comma++;
394
0
      continue;
395
0
    }
396
0
    if (!plen) {
397
0
      if (len < 2)
398
0
        break;
399
0
      plen = os++;
400
0
      len--;
401
0
    }
402
403
0
    if (*comma == ',') {
404
0
      *plen = (uint8_t)lws_ptr_diff(os, plen + 1);
405
0
      plen = NULL;
406
0
      comma++;
407
0
    } else {
408
0
      *os++ = (uint8_t)*comma++;
409
0
      len--;
410
0
    }
411
0
  }
412
413
0
  if (plen)
414
0
    *plen = (uint8_t)lws_ptr_diff(os, plen + 1);
415
416
0
  *os = 0;
417
418
0
  return lws_ptr_diff(os, oos);
419
0
}
420
421
422
423
424
void
425
lws_tls_cleanup_process(void)
426
0
{
427
#if defined(LWS_WITH_MBEDTLS)
428
  if (tls_ops_mbedtls.process_cleanup)
429
    tls_ops_mbedtls.process_cleanup();
430
#elif defined(LWS_WITH_SCHANNEL)
431
  if (tls_ops_schannel.process_cleanup)
432
    tls_ops_schannel.process_cleanup();
433
#elif defined(LWS_WITH_GNUTLS)
434
  if (tls_ops_gnutls.process_cleanup)
435
    tls_ops_gnutls.process_cleanup();
436
#elif defined(LWS_WITH_BEARSSL)
437
  if (tls_ops_bearssl.process_cleanup)
438
    tls_ops_bearssl.process_cleanup();
439
#elif defined(LWS_WITH_OPENHITLS)
440
  if (tls_ops_openhitls.process_cleanup)
441
    tls_ops_openhitls.process_cleanup();
442
#else
443
0
  if (tls_ops_openssl.process_cleanup)
444
0
    tls_ops_openssl.process_cleanup();
445
0
#endif
446
0
}