/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 | } |