Coverage Report

Created: 2026-04-01 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebsockets/lib/tls/tls.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
#include "private-lib-tls.h"
27
28
#if defined(LWS_HAVE_SSL_CTX_set_keylog_callback) && defined(LWS_WITH_NETWORK) && \
29
  defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS) && \
30
  !defined(LWS_WITH_GNUTLS) && \
31
  (!defined(LWS_WITHOUT_CLIENT) || !defined(LWS_WITHOUT_SERVER))
32
void
33
lws_klog_dump(const SSL *ssl, const char *line)
34
0
{
35
0
  struct lws *wsi = (struct lws *)SSL_get_ex_data(ssl,
36
0
            openssl_websocket_private_data_index);
37
0
  char path[128], hdr[128], ts[64];
38
0
  size_t w = 0, wx = 0;
39
0
  int fd, t;
40
41
0
  if (!wsi || !wsi->a.context->keylog_file[0] || !wsi->a.vhost)
42
0
    return;
43
44
0
  lws_snprintf(path, sizeof(path), "%s.%s", wsi->a.context->keylog_file,
45
0
      wsi->a.vhost->name);
46
47
0
  fd = open(path, O_CREAT | O_RDWR | O_APPEND, 0600);
48
0
  if (fd == -1) {
49
0
    lwsl_vhost_warn(wsi->a.vhost, "Failed to append %s", path);
50
0
    return;
51
0
  }
52
53
  /* the first item in the chunk */
54
0
  if (!strncmp(line, "SERVER_HANDSHAKE_TRAFFIC_SECRET", 31)) {
55
0
    w += (size_t)write(fd, "\n# ", 3);
56
0
    wx += 3;
57
0
    t = lwsl_timestamp(LLL_WARN, ts, sizeof(ts));
58
0
    wx += (size_t)t;
59
0
    w += (size_t)write(fd, ts, (size_t)t);
60
61
0
    t = lws_snprintf(hdr, sizeof(hdr), "%s\n", wsi->lc.gutag);
62
0
    w += (size_t)write(fd, hdr, (size_t)t);
63
0
    wx += (size_t)t;
64
65
0
    lwsl_vhost_warn(wsi->a.vhost, "appended ssl keylog: %s", path);
66
0
  }
67
68
0
  wx += strlen(line) + 1;
69
0
  w += (size_t)write(fd, line, 
70
#if defined(WIN32)
71
      (unsigned int)
72
#endif
73
0
      strlen(line));
74
0
  w += (size_t)write(fd, "\n", 1);
75
0
  close(fd);
76
77
0
  if (w != wx) {
78
0
    lwsl_vhost_warn(wsi->a.vhost, "Failed to write %s", path);
79
0
    return;
80
0
  }
81
0
}
82
#endif
83
84
85
#if defined(LWS_WITH_NETWORK)
86
#if (!defined(LWS_WITH_MBEDTLS) && !defined(LWS_WITH_SCHANNEL) && defined(OPENSSL_VERSION_NUMBER) && \
87
          OPENSSL_VERSION_NUMBER >= 0x10002000L)
88
static int
89
alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
90
  const unsigned char *in, unsigned int inlen, void *arg)
91
0
{
92
0
#if !defined(LWS_WITH_MBEDTLS)
93
0
  struct alpn_ctx *alpn_ctx = (struct alpn_ctx *)arg;
94
95
0
  if (SSL_select_next_proto((unsigned char **)out, outlen, alpn_ctx->data,
96
0
          alpn_ctx->len, in, inlen) !=
97
0
      OPENSSL_NPN_NEGOTIATED)
98
0
    return SSL_TLSEXT_ERR_NOACK;
99
0
#endif
100
101
0
  return SSL_TLSEXT_ERR_OK;
102
0
}
103
#endif
104
105
int
106
lws_tls_restrict_borrow(struct lws *wsi)
107
0
{
108
0
  struct lws_context *cx = wsi->a.context;
109
110
0
  if (cx->simultaneous_ssl_restriction &&
111
0
      cx->simultaneous_ssl >= cx->simultaneous_ssl_restriction) {
112
0
    lwsl_notice("%s: tls connection limit %d\n", __func__,
113
0
          cx->simultaneous_ssl);
114
0
    return 1;
115
0
  }
116
117
0
  if (cx->simultaneous_ssl_handshake_restriction &&
118
0
      cx->simultaneous_ssl_handshake >=
119
0
          cx->simultaneous_ssl_handshake_restriction) {
120
0
    lwsl_notice("%s: tls handshake limit %d\n", __func__,
121
0
          cx->simultaneous_ssl_handshake);
122
0
    return 1;
123
0
  }
124
125
0
  cx->simultaneous_ssl++;
126
0
  cx->simultaneous_ssl_handshake++;
127
0
  wsi->tls_borrowed_hs = 1;
128
0
  wsi->tls_borrowed = 1;
129
130
0
  lwsl_info("%s: %d -> %d\n", __func__,
131
0
      cx->simultaneous_ssl - 1,
132
0
      cx->simultaneous_ssl);
133
134
0
  assert(!cx->simultaneous_ssl_restriction ||
135
0
      cx->simultaneous_ssl <=
136
0
        cx->simultaneous_ssl_restriction);
137
0
  assert(!cx->simultaneous_ssl_handshake_restriction ||
138
0
      cx->simultaneous_ssl_handshake <=
139
0
        cx->simultaneous_ssl_handshake_restriction);
140
141
0
#if defined(LWS_WITH_SERVER)
142
0
  lws_gate_accepts(cx,
143
0
      (cx->simultaneous_ssl_restriction &&
144
0
       cx->simultaneous_ssl == cx->simultaneous_ssl_restriction) ||
145
0
      (cx->simultaneous_ssl_handshake_restriction &&
146
0
       cx->simultaneous_ssl_handshake == cx->simultaneous_ssl_handshake_restriction));
147
0
#endif
148
149
0
  return 0;
150
0
}
151
152
static void
153
_lws_tls_restrict_return(struct lws *wsi)
154
0
{
155
0
#if defined(LWS_WITH_SERVER)
156
0
  struct lws_context *cx = wsi->a.context;
157
158
0
  assert(cx->simultaneous_ssl_handshake >= 0);
159
0
  assert(cx->simultaneous_ssl >= 0);
160
161
0
  lws_gate_accepts(cx,
162
0
      (cx->simultaneous_ssl_restriction &&
163
0
       cx->simultaneous_ssl == cx->simultaneous_ssl_restriction) ||
164
0
      (cx->simultaneous_ssl_handshake_restriction &&
165
0
       cx->simultaneous_ssl_handshake == cx->simultaneous_ssl_handshake_restriction));
166
0
#endif
167
0
}
168
169
void
170
lws_tls_restrict_return_handshake(struct lws *wsi)
171
0
{
172
0
  struct lws_context *cx = wsi->a.context;
173
174
  /* we're just returning the hs part */
175
176
0
  if (!wsi->tls_borrowed_hs)
177
0
    return;
178
179
0
  wsi->tls_borrowed_hs = 0; /* return it one time per wsi */
180
0
  cx->simultaneous_ssl_handshake--;
181
182
0
  lwsl_info("%s:  %d -> %d\n", __func__,
183
0
      cx->simultaneous_ssl_handshake + 1,
184
0
      cx->simultaneous_ssl_handshake);
185
186
0
  _lws_tls_restrict_return(wsi);
187
0
}
188
189
void
190
lws_tls_restrict_return(struct lws *wsi)
191
0
{
192
0
  struct lws_context *cx = wsi->a.context;
193
194
0
  if (!wsi->tls_borrowed)
195
0
    return;
196
197
0
  wsi->tls_borrowed = 0;
198
0
  cx->simultaneous_ssl--;
199
200
0
  lwsl_info("%s: %d -> %d\n", __func__,
201
0
      cx->simultaneous_ssl + 1,
202
0
      cx->simultaneous_ssl);
203
204
  /* We're returning everything, even if hs didn't complete */
205
206
0
  if (wsi->tls_borrowed_hs)
207
0
    lws_tls_restrict_return_handshake(wsi);
208
0
  else
209
0
    _lws_tls_restrict_return(wsi);
210
0
}
211
212
void
213
lws_context_init_alpn(struct lws_vhost *vhost)
214
0
{
215
0
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
216
0
          OPENSSL_VERSION_NUMBER >= 0x10002000L) || \
217
0
          defined(LWS_WITH_GNUTLS)
218
0
  const char *alpn_comma = vhost->context->tls.alpn_default;
219
220
0
  if (vhost->tls.alpn)
221
0
    alpn_comma = vhost->tls.alpn;
222
223
0
  lwsl_info(" Server '%s' advertising ALPN: %s\n",
224
0
        vhost->name, alpn_comma);
225
226
0
  vhost->tls.alpn_ctx.len = (uint8_t)lws_alpn_comma_to_openssl(alpn_comma,
227
0
          vhost->tls.alpn_ctx.data,
228
0
          sizeof(vhost->tls.alpn_ctx.data) - 1);
229
230
#if defined(LWS_WITH_GNUTLS)
231
  /* GnuTLS ALPN is set per-session, nothing to do here for CTX */
232
#elif defined(LWS_WITH_MBEDTLS)
233
  /* MbedTLS ALPN is set per-session, nothing to do here for CTX */
234
#else
235
0
  SSL_CTX_set_alpn_select_cb(vhost->tls.ssl_ctx, alpn_cb,
236
0
           &vhost->tls.alpn_ctx);
237
0
#endif
238
#else
239
#if !defined(LWS_WITH_SCHANNEL) && !defined(LWS_WITH_GNUTLS)
240
  lwsl_err(" HTTP2 / ALPN configured "
241
     "but not supported by OpenSSL 0x%lx\n",
242
     OPENSSL_VERSION_NUMBER);
243
#endif
244
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
245
0
}
246
247
int
248
lws_tls_server_conn_alpn(struct lws *wsi)
249
0
{
250
0
#if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
251
0
          OPENSSL_VERSION_NUMBER >= 0x10002000L) || \
252
0
          defined(LWS_WITH_GNUTLS)
253
0
  const unsigned char *name = NULL;
254
0
  char cstr[10];
255
0
  unsigned int len = 0;
256
257
0
  lwsl_info("%s\n", __func__);
258
259
0
  if (!wsi->tls.ssl) {
260
0
    lwsl_err("%s: non-ssl\n", __func__);
261
0
    return 0;
262
0
  }
263
264
#if defined(LWS_WITH_GNUTLS)
265
  {
266
    gnutls_datum_t selected;
267
    if (gnutls_alpn_get_selected_protocol((gnutls_session_t)wsi->tls.ssl, &selected) == 0) {
268
      name = selected.data;
269
      len = selected.size;
270
    }
271
  }
272
#else
273
0
  SSL_get0_alpn_selected(wsi->tls.ssl, &name, &len);
274
0
#endif
275
0
  if (!len) {
276
0
    lwsl_info("no ALPN upgrade\n");
277
0
    return 0;
278
0
  }
279
280
0
  if (len > sizeof(cstr) - 1)
281
0
    len = sizeof(cstr) - 1;
282
283
0
  memcpy(cstr, name, len);
284
0
  cstr[len] = '\0';
285
286
0
  lwsl_info("%s: negotiated '%s' using ALPN\n", __func__, cstr);
287
0
  wsi->tls.use_ssl |= LCCSCF_USE_SSL;
288
289
0
  return lws_role_call_alpn_negotiated(wsi, (const char *)cstr);
290
#else
291
  lwsl_err("%s: openssl/gnutls too old\n", __func__);
292
#endif
293
294
0
  return 0;
295
0
}
296
#endif
297
298
#if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT)
299
#if defined(LWS_PLAT_FREERTOS) && !defined(LWS_AMAZON_RTOS)
300
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
301
         lws_filepos_t *amount)
302
{
303
  nvs_handle nvh;
304
  size_t s;
305
  int n = 0;
306
307
  ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
308
  if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) {
309
    n = 1;
310
    goto bail;
311
  }
312
  *buf = lws_malloc(s + 1, "alloc_file");
313
  if (!*buf) {
314
    n = 2;
315
    goto bail;
316
  }
317
  if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) {
318
    lws_free(*buf);
319
    n = 1;
320
    goto bail;
321
  }
322
323
  *amount = s;
324
  (*buf)[s] = '\0';
325
326
  lwsl_notice("%s: nvs: read %s, %d bytes\n", __func__, filename, (int)s);
327
328
bail:
329
  nvs_close(nvh);
330
331
  return n;
332
}
333
#else
334
int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
335
    lws_filepos_t *amount)
336
0
{
337
0
  FILE *f;
338
0
  size_t s;
339
0
  ssize_t m;
340
0
  int n = 0;
341
342
0
  f = fopen(filename, "rb");
343
0
  if (f == NULL) {
344
0
    n = 1;
345
0
    goto bail;
346
0
  }
347
348
0
  if (fseek(f, 0, SEEK_END) != 0) {
349
0
    n = 1;
350
0
    goto bail;
351
0
  }
352
353
0
  m = (ssize_t)ftell(f);
354
0
  if (m == -1l) {
355
0
    n = 1;
356
0
    goto bail;
357
0
  }
358
0
  s = (size_t)m;
359
360
0
  if (fseek(f, 0, SEEK_SET) != 0) {
361
0
    n = 1;
362
0
    goto bail;
363
0
  }
364
365
0
  *buf = lws_malloc(s + 1, "alloc_file");
366
0
  if (!*buf) {
367
0
    n = 2;
368
0
    goto bail;
369
0
  }
370
371
0
  if (fread(*buf, s, 1, f) != 1) {
372
0
    lws_free(*buf);
373
0
    n = 1;
374
0
    goto bail;
375
0
  }
376
377
0
  *amount = s;
378
379
0
bail:
380
0
  if (f)
381
0
    fclose(f);
382
383
0
  return n;
384
385
0
}
386
#endif
387
388
/*
389
 * filename: NULL means use buffer inbuf length inlen directly, otherwise
390
 *           load the file "filename" into an allocated buffer.
391
 *
392
 * Allocates a separate DER output buffer if inbuf / inlen are the input,
393
 * since the
394
 *
395
 * Contents may be PEM or DER: returns with buf pointing to DER and amount
396
 * set to the DER length.
397
 */
398
399
LWS_VISIBLE int
400
lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename,
401
            const char *inbuf, lws_filepos_t inlen,
402
            uint8_t **buf, lws_filepos_t *amount)
403
0
{
404
0
  uint8_t *pem = NULL, *p, *end, *opem;
405
0
  lws_filepos_t len;
406
0
  uint8_t *q;
407
0
  int n;
408
409
0
  if (filename) {
410
0
    n = alloc_file(context, filename, (uint8_t **)&pem, &len);
411
0
    if (n)
412
0
      return n;
413
0
  } else {
414
0
    pem = (uint8_t *)inbuf;
415
0
    len = inlen;
416
0
  }
417
418
0
  if (len && pem[len - 1] == '\0')
419
0
    len--;
420
421
0
  opem = p = pem;
422
0
  end = p + len;
423
424
0
  if (strncmp((char *)p, "-----", 5)) {
425
426
    /* take it as being already DER */
427
428
0
    pem = lws_malloc((size_t)inlen, "alloc_der");
429
0
    if (!pem)
430
0
      return 1;
431
432
0
    memcpy(pem, inbuf, (size_t)inlen);
433
434
0
    *buf = pem;
435
0
    *amount = inlen;
436
437
0
    return 0;
438
0
  }
439
440
  /* PEM -> DER */
441
442
0
  if (!filename) {
443
    /* we don't know if it's in const memory... alloc the output */
444
0
    pem = lws_malloc(((size_t)(inlen + 3) * 3) / 4, "alloc_der");
445
0
    if (!pem) {
446
0
      lwsl_err("a\n");
447
0
      return 1;
448
0
    }
449
450
451
0
  } /* else overwrite the allocated, b64 input with decoded DER */
452
453
  /* trim the first line */
454
455
0
  p += 5;
456
0
  while (p < end && *p != '\n' && *p != '-')
457
0
    p++;
458
459
0
  if (*p != '-') {
460
0
    goto bail;
461
0
  }
462
463
0
  while (p < end && *p != '\n')
464
0
    p++;
465
466
0
  if (p >= end) {
467
0
    goto bail;
468
0
  }
469
470
0
  p++;
471
472
  /* trim the last line */
473
474
0
  q = (uint8_t *)end - 2;
475
476
0
  while (q > opem && *q != '\n')
477
0
    q--;
478
479
0
  if (*q != '\n')
480
0
    goto bail;
481
482
  /* we can't write into the input buffer for mem, since it may be in RO
483
   * const segment
484
   */
485
0
  if (filename)
486
0
    *q = '\0';
487
488
0
  n = lws_ptr_diff(q, p);
489
0
  if (n == -1) /* coverity */
490
0
    goto bail;
491
492
0
    lwsl_info("%s: PEM payload len %d\n", __func__, n);
493
0
    lwsl_hexdump_info(p, (size_t)n);
494
495
0
  n = lws_b64_decode_string_len((char *)p, n,
496
0
              (char *)pem, (int)(long long)len);
497
0
  if (n < 0) {
498
0
    lwsl_err("%s: base64 pem decode failed\n", __func__);
499
0
    goto bail;
500
0
  }
501
502
0
  *amount = (unsigned int)n;
503
0
  *buf = (uint8_t *)pem;
504
505
0
  return 0;
506
507
0
bail:
508
0
  lws_free((uint8_t *)pem);
509
510
0
  return 4;
511
0
}
512
513
514
#endif
515
516
#if !defined(LWS_PLAT_FREERTOS) && !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT)
517
518
519
static int
520
lws_tls_extant(const char *name)
521
0
{
522
  /* it exists if we can open it... */
523
0
  int fd = open(name, O_RDONLY);
524
0
  char buf[1];
525
0
  ssize_t n;
526
527
0
  if (fd < 0)
528
0
    return 1;
529
530
  /* and we can read at least one byte out of it */
531
0
  n = read(fd, buf, 1);
532
0
  close(fd);
533
534
0
  return n != 1;
535
0
}
536
#endif
537
/*
538
 * Returns 0 if the filepath "name" exists and can be read from.
539
 *
540
 * In addition, if "name".upd exists, backup "name" to "name.old.1"
541
 * and rename "name".upd to "name" before reporting its existence.
542
 *
543
 * There are four situations and three results possible:
544
 *
545
 * 1) LWS_TLS_EXTANT_NO: There are no certs at all (we are waiting for them to
546
 *    be provisioned).  We also feel like this if we need privs we don't have
547
 *    any more to look in the directory.
548
 *
549
 * 2) There are provisioned certs written (xxx.upd) and we still have root
550
 *    privs... in this case we rename any existing cert to have a backup name
551
 *    and move the upd cert into place with the correct name.  This then becomes
552
 *    situation 4 for the caller.
553
 *
554
 * 3) LWS_TLS_EXTANT_ALTERNATIVE: There are provisioned certs written (xxx.upd)
555
 *    but we no longer have the privs needed to read or rename them.  In this
556
 *    case, indicate that the caller should use temp copies if any we do have
557
 *    rights to access.  This is normal after we have updated the cert.
558
 *
559
 *    But if we dropped privs, we can't detect the provisioned xxx.upd cert +
560
 *    key, because we can't see in the dir.  So we have to upgrade NO to
561
 *    ALTERNATIVE when we actually have the in-memory alternative.
562
 *
563
 * 4) LWS_TLS_EXTANT_YES: The certs are present with the correct name and we
564
 *    have the rights to read them.
565
 */
566
567
enum lws_tls_extant
568
lws_tls_use_any_upgrade_check_extant(const char *name)
569
0
{
570
0
#if !defined(LWS_PLAT_OPTEE) && !defined(LWS_AMAZON_RTOS)
571
572
0
  int n;
573
574
0
#if !defined(LWS_PLAT_FREERTOS)
575
0
  char buf[256];
576
577
0
  lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name);
578
0
  if (!lws_tls_extant(buf)) {
579
    /* ah there is an updated file... how about the desired file? */
580
0
    if (!lws_tls_extant(name)) {
581
      /* rename the desired file */
582
0
      for (n = 0; n < 50; n++) {
583
0
        lws_snprintf(buf, sizeof(buf) - 1,
584
0
               "%s.old.%d", name, n);
585
0
        if (!rename(name, buf))
586
0
          break;
587
0
      }
588
0
      if (n == 50) {
589
0
        lwsl_notice("unable to rename %s\n", name);
590
591
0
        return LWS_TLS_EXTANT_ALTERNATIVE;
592
0
      }
593
0
      lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name);
594
0
    }
595
    /* desired file is out of the way, rename the updated file */
596
0
    if (rename(buf, name)) {
597
0
      lwsl_notice("unable to rename %s to %s\n", buf, name);
598
599
0
      return LWS_TLS_EXTANT_ALTERNATIVE;
600
0
    }
601
0
  }
602
603
0
  if (lws_tls_extant(name))
604
0
    return LWS_TLS_EXTANT_NO;
605
#else
606
  nvs_handle nvh;
607
  size_t s = 8192;
608
609
  if (nvs_open("lws-station", NVS_READWRITE, &nvh)) {
610
    lwsl_notice("%s: can't open nvs\n", __func__);
611
    return LWS_TLS_EXTANT_NO;
612
  }
613
614
  n = nvs_get_blob(nvh, name, NULL, &s);
615
  nvs_close(nvh);
616
617
  if (n)
618
    return LWS_TLS_EXTANT_NO;
619
#endif
620
0
#endif
621
0
  return LWS_TLS_EXTANT_YES;
622
0
}
623
624
LWS_VISIBLE int
625
lws_tls_cert_get_x509_remaining(struct lws_context *context, const char *filepath, int *days_left, int *total_days)
626
0
{
627
0
  struct lws_x509_cert *x = NULL;
628
0
  union lws_tls_cert_info_results cri, cri1;
629
0
  uint8_t *p;
630
0
  lws_filepos_t amount;
631
0
  int res = -1;
632
633
0
  *days_left = 0;
634
0
  *total_days = 0;
635
636
0
  if (alloc_file(context, filepath, &p, &amount))
637
0
    return 1;
638
639
0
  p[amount] = '\0';
640
641
0
  if (lws_x509_create(&x))
642
0
    goto bail;
643
644
0
  if (lws_x509_parse_from_pem(x, p, (size_t)amount))
645
0
    goto bail_destroy;
646
647
0
  if (!lws_x509_info(x, LWS_TLS_CERT_INFO_VALIDITY_FROM, &cri, 0) &&
648
0
      !lws_x509_info(x, LWS_TLS_CERT_INFO_VALIDITY_TO, &cri1, 0)) {
649
0
    time_t now = time(NULL);
650
651
0
    *days_left = (int)((cri1.time - now) / (24 * 3600));
652
0
    *total_days = (int)((cri1.time - cri.time) / (24 * 3600));
653
0
    res = 0;
654
0
  }
655
656
0
bail_destroy:
657
0
  lws_x509_destroy(&x);
658
0
bail:
659
0
  lws_free(p);
660
661
0
  return res;
662
0
}