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_WITH_NETWORK)
29 #if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
30 OPENSSL_VERSION_NUMBER >= 0x10002000L)
31 static int
alpn_cb(SSL * s,const unsigned char ** out,unsigned char * outlen,const unsigned char * in,unsigned int inlen,void * arg)32 alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,
33 const unsigned char *in, unsigned int inlen, void *arg)
34 {
35 #if !defined(LWS_WITH_MBEDTLS)
36 struct alpn_ctx *alpn_ctx = (struct alpn_ctx *)arg;
37
38 if (SSL_select_next_proto((unsigned char **)out, outlen, alpn_ctx->data,
39 alpn_ctx->len, in, inlen) !=
40 OPENSSL_NPN_NEGOTIATED)
41 return SSL_TLSEXT_ERR_NOACK;
42 #endif
43
44 return SSL_TLSEXT_ERR_OK;
45 }
46 #endif
47
48 int
lws_tls_restrict_borrow(struct lws * wsi)49 lws_tls_restrict_borrow(struct lws *wsi)
50 {
51 struct lws_context *cx = wsi->a.context;
52
53 if (cx->simultaneous_ssl_restriction &&
54 cx->simultaneous_ssl >= cx->simultaneous_ssl_restriction) {
55 lwsl_notice("%s: tls connection limit %d\n", __func__,
56 cx->simultaneous_ssl);
57 return 1;
58 }
59
60 if (cx->simultaneous_ssl_handshake_restriction &&
61 cx->simultaneous_ssl_handshake >=
62 cx->simultaneous_ssl_handshake_restriction) {
63 lwsl_notice("%s: tls handshake limit %d\n", __func__,
64 cx->simultaneous_ssl);
65 return 1;
66 }
67
68 cx->simultaneous_ssl++;
69 cx->simultaneous_ssl_handshake++;
70 wsi->tls_borrowed_hs = 1;
71 wsi->tls_borrowed = 1;
72
73 lwsl_info("%s: %d -> %d\n", __func__,
74 cx->simultaneous_ssl - 1,
75 cx->simultaneous_ssl);
76
77 assert(!cx->simultaneous_ssl_restriction ||
78 cx->simultaneous_ssl <=
79 cx->simultaneous_ssl_restriction);
80 assert(!cx->simultaneous_ssl_handshake_restriction ||
81 cx->simultaneous_ssl_handshake <=
82 cx->simultaneous_ssl_handshake_restriction);
83
84 #if defined(LWS_WITH_SERVER)
85 lws_gate_accepts(cx,
86 (cx->simultaneous_ssl_restriction &&
87 cx->simultaneous_ssl == cx->simultaneous_ssl_restriction) ||
88 (cx->simultaneous_ssl_handshake_restriction &&
89 cx->simultaneous_ssl_handshake == cx->simultaneous_ssl_handshake_restriction));
90 #endif
91
92 return 0;
93 }
94
95 static void
_lws_tls_restrict_return(struct lws * wsi)96 _lws_tls_restrict_return(struct lws *wsi)
97 {
98 #if defined(LWS_WITH_SERVER)
99 struct lws_context *cx = wsi->a.context;
100
101 assert(cx->simultaneous_ssl_handshake >= 0);
102 assert(cx->simultaneous_ssl >= 0);
103
104 lws_gate_accepts(cx,
105 (cx->simultaneous_ssl_restriction &&
106 cx->simultaneous_ssl == cx->simultaneous_ssl_restriction) ||
107 (cx->simultaneous_ssl_handshake_restriction &&
108 cx->simultaneous_ssl_handshake == cx->simultaneous_ssl_handshake_restriction));
109 #endif
110 }
111
112 void
lws_tls_restrict_return_handshake(struct lws * wsi)113 lws_tls_restrict_return_handshake(struct lws *wsi)
114 {
115 struct lws_context *cx = wsi->a.context;
116
117 /* we're just returning the hs part */
118
119 if (!wsi->tls_borrowed_hs)
120 return;
121
122 wsi->tls_borrowed_hs = 0; /* return it one time per wsi */
123 cx->simultaneous_ssl_handshake--;
124
125 lwsl_info("%s: %d -> %d\n", __func__,
126 cx->simultaneous_ssl_handshake + 1,
127 cx->simultaneous_ssl_handshake);
128
129 _lws_tls_restrict_return(wsi);
130 }
131
132 void
lws_tls_restrict_return(struct lws * wsi)133 lws_tls_restrict_return(struct lws *wsi)
134 {
135 struct lws_context *cx = wsi->a.context;
136
137 if (!wsi->tls_borrowed)
138 return;
139
140 wsi->tls_borrowed = 0;
141 cx->simultaneous_ssl--;
142
143 lwsl_info("%s: %d -> %d\n", __func__,
144 cx->simultaneous_ssl + 1,
145 cx->simultaneous_ssl);
146
147 /* We're returning everything, even if hs didn't complete */
148
149 if (wsi->tls_borrowed_hs)
150 lws_tls_restrict_return_handshake(wsi);
151 else
152 _lws_tls_restrict_return(wsi);
153 }
154
155 void
lws_context_init_alpn(struct lws_vhost * vhost)156 lws_context_init_alpn(struct lws_vhost *vhost)
157 {
158 #if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
159 OPENSSL_VERSION_NUMBER >= 0x10002000L)
160 const char *alpn_comma = vhost->context->tls.alpn_default;
161
162 if (vhost->tls.alpn)
163 alpn_comma = vhost->tls.alpn;
164
165 lwsl_info(" Server '%s' advertising ALPN: %s\n",
166 vhost->name, alpn_comma);
167
168 vhost->tls.alpn_ctx.len = (uint8_t)lws_alpn_comma_to_openssl(alpn_comma,
169 vhost->tls.alpn_ctx.data,
170 sizeof(vhost->tls.alpn_ctx.data) - 1);
171
172 SSL_CTX_set_alpn_select_cb(vhost->tls.ssl_ctx, alpn_cb,
173 &vhost->tls.alpn_ctx);
174 #else
175 lwsl_err(" HTTP2 / ALPN configured "
176 "but not supported by OpenSSL 0x%lx\n",
177 OPENSSL_VERSION_NUMBER);
178 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
179 }
180
181 int
lws_tls_server_conn_alpn(struct lws * wsi)182 lws_tls_server_conn_alpn(struct lws *wsi)
183 {
184 #if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \
185 OPENSSL_VERSION_NUMBER >= 0x10002000L)
186 const unsigned char *name = NULL;
187 char cstr[10];
188 unsigned len;
189
190 lwsl_info("%s\n", __func__);
191
192 if (!wsi->tls.ssl) {
193 lwsl_err("%s: non-ssl\n", __func__);
194 return 0;
195 }
196
197 SSL_get0_alpn_selected(wsi->tls.ssl, &name, &len);
198 if (!len) {
199 lwsl_info("no ALPN upgrade\n");
200 return 0;
201 }
202
203 if (len > sizeof(cstr) - 1)
204 len = sizeof(cstr) - 1;
205
206 memcpy(cstr, name, len);
207 cstr[len] = '\0';
208
209 lwsl_info("%s: negotiated '%s' using ALPN\n", __func__, cstr);
210 wsi->tls.use_ssl |= LCCSCF_USE_SSL;
211
212 return lws_role_call_alpn_negotiated(wsi, (const char *)cstr);
213 #else
214 lwsl_err("%s: openssl too old\n", __func__);
215 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
216
217 return 0;
218 }
219 #endif
220
221 #if !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT)
222 #if defined(LWS_PLAT_FREERTOS) && !defined(LWS_AMAZON_RTOS)
alloc_file(struct lws_context * context,const char * filename,uint8_t ** buf,lws_filepos_t * amount)223 int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
224 lws_filepos_t *amount)
225 {
226 nvs_handle nvh;
227 size_t s;
228 int n = 0;
229
230 ESP_ERROR_CHECK(nvs_open("lws-station", NVS_READWRITE, &nvh));
231 if (nvs_get_blob(nvh, filename, NULL, &s) != ESP_OK) {
232 n = 1;
233 goto bail;
234 }
235 *buf = lws_malloc(s + 1, "alloc_file");
236 if (!*buf) {
237 n = 2;
238 goto bail;
239 }
240 if (nvs_get_blob(nvh, filename, (char *)*buf, &s) != ESP_OK) {
241 lws_free(*buf);
242 n = 1;
243 goto bail;
244 }
245
246 *amount = s;
247 (*buf)[s] = '\0';
248
249 lwsl_notice("%s: nvs: read %s, %d bytes\n", __func__, filename, (int)s);
250
251 bail:
252 nvs_close(nvh);
253
254 return n;
255 }
256 #else
alloc_file(struct lws_context * context,const char * filename,uint8_t ** buf,lws_filepos_t * amount)257 int alloc_file(struct lws_context *context, const char *filename, uint8_t **buf,
258 lws_filepos_t *amount)
259 {
260 FILE *f;
261 size_t s;
262 ssize_t m;
263 int n = 0;
264
265 f = fopen(filename, "rb");
266 if (f == NULL) {
267 n = 1;
268 goto bail;
269 }
270
271 if (fseek(f, 0, SEEK_END) != 0) {
272 n = 1;
273 goto bail;
274 }
275
276 m = (ssize_t)ftell(f);
277 if (m == -1l) {
278 n = 1;
279 goto bail;
280 }
281 s = (size_t)m;
282
283 if (fseek(f, 0, SEEK_SET) != 0) {
284 n = 1;
285 goto bail;
286 }
287
288 *buf = lws_malloc(s + 1, "alloc_file");
289 if (!*buf) {
290 n = 2;
291 goto bail;
292 }
293
294 if (fread(*buf, s, 1, f) != 1) {
295 lws_free(*buf);
296 n = 1;
297 goto bail;
298 }
299
300 *amount = s;
301
302 bail:
303 if (f)
304 fclose(f);
305
306 return n;
307
308 }
309 #endif
310
311 /*
312 * filename: NULL means use buffer inbuf length inlen directly, otherwise
313 * load the file "filename" into an allocated buffer.
314 *
315 * Allocates a separate DER output buffer if inbuf / inlen are the input,
316 * since the
317 *
318 * Contents may be PEM or DER: returns with buf pointing to DER and amount
319 * set to the DER length.
320 */
321
322 int
lws_tls_alloc_pem_to_der_file(struct lws_context * context,const char * filename,const char * inbuf,lws_filepos_t inlen,uint8_t ** buf,lws_filepos_t * amount)323 lws_tls_alloc_pem_to_der_file(struct lws_context *context, const char *filename,
324 const char *inbuf, lws_filepos_t inlen,
325 uint8_t **buf, lws_filepos_t *amount)
326 {
327 uint8_t *pem = NULL, *p, *end, *opem;
328 lws_filepos_t len;
329 uint8_t *q;
330 int n;
331
332 if (filename) {
333 n = alloc_file(context, filename, (uint8_t **)&pem, &len);
334 if (n)
335 return n;
336 } else {
337 pem = (uint8_t *)inbuf;
338 len = inlen;
339 }
340
341 opem = p = pem;
342 end = p + len;
343
344 if (strncmp((char *)p, "-----", 5)) {
345
346 /* take it as being already DER */
347
348 pem = lws_malloc((size_t)inlen, "alloc_der");
349 if (!pem)
350 return 1;
351
352 memcpy(pem, inbuf, (size_t)inlen);
353
354 *buf = pem;
355 *amount = inlen;
356
357 return 0;
358 }
359
360 /* PEM -> DER */
361
362 if (!filename) {
363 /* we don't know if it's in const memory... alloc the output */
364 pem = lws_malloc(((size_t)inlen * 3) / 4, "alloc_der");
365 if (!pem) {
366 lwsl_err("a\n");
367 return 1;
368 }
369
370
371 } /* else overwrite the allocated, b64 input with decoded DER */
372
373 /* trim the first line */
374
375 p += 5;
376 while (p < end && *p != '\n' && *p != '-')
377 p++;
378
379 if (*p != '-') {
380 goto bail;
381 }
382
383 while (p < end && *p != '\n')
384 p++;
385
386 if (p >= end) {
387 goto bail;
388 }
389
390 p++;
391
392 /* trim the last line */
393
394 q = (uint8_t *)end - 2;
395
396 while (q > opem && *q != '\n')
397 q--;
398
399 if (*q != '\n')
400 goto bail;
401
402 /* we can't write into the input buffer for mem, since it may be in RO
403 * const segment
404 */
405 if (filename)
406 *q = '\0';
407
408 n = lws_ptr_diff(q, p);
409 if (n == -1) /* coverity */
410 goto bail;
411 *amount = (unsigned int)lws_b64_decode_string_len((char *)p, n,
412 (char *)pem, (int)(long long)len);
413 *buf = (uint8_t *)pem;
414
415 return 0;
416
417 bail:
418 lws_free((uint8_t *)pem);
419
420 return 4;
421 }
422
423
424 #endif
425
426 #if !defined(LWS_PLAT_FREERTOS) && !defined(LWS_PLAT_OPTEE) && !defined(OPTEE_DEV_KIT)
427
428
429 static int
lws_tls_extant(const char * name)430 lws_tls_extant(const char *name)
431 {
432 /* it exists if we can open it... */
433 int fd = open(name, O_RDONLY);
434 char buf[1];
435 ssize_t n;
436
437 if (fd < 0)
438 return 1;
439
440 /* and we can read at least one byte out of it */
441 n = read(fd, buf, 1);
442 close(fd);
443
444 return n != 1;
445 }
446 #endif
447 /*
448 * Returns 0 if the filepath "name" exists and can be read from.
449 *
450 * In addition, if "name".upd exists, backup "name" to "name.old.1"
451 * and rename "name".upd to "name" before reporting its existence.
452 *
453 * There are four situations and three results possible:
454 *
455 * 1) LWS_TLS_EXTANT_NO: There are no certs at all (we are waiting for them to
456 * be provisioned). We also feel like this if we need privs we don't have
457 * any more to look in the directory.
458 *
459 * 2) There are provisioned certs written (xxx.upd) and we still have root
460 * privs... in this case we rename any existing cert to have a backup name
461 * and move the upd cert into place with the correct name. This then becomes
462 * situation 4 for the caller.
463 *
464 * 3) LWS_TLS_EXTANT_ALTERNATIVE: There are provisioned certs written (xxx.upd)
465 * but we no longer have the privs needed to read or rename them. In this
466 * case, indicate that the caller should use temp copies if any we do have
467 * rights to access. This is normal after we have updated the cert.
468 *
469 * But if we dropped privs, we can't detect the provisioned xxx.upd cert +
470 * key, because we can't see in the dir. So we have to upgrade NO to
471 * ALTERNATIVE when we actually have the in-memory alternative.
472 *
473 * 4) LWS_TLS_EXTANT_YES: The certs are present with the correct name and we
474 * have the rights to read them.
475 */
476
477 enum lws_tls_extant
lws_tls_use_any_upgrade_check_extant(const char * name)478 lws_tls_use_any_upgrade_check_extant(const char *name)
479 {
480 #if !defined(LWS_PLAT_OPTEE) && !defined(LWS_AMAZON_RTOS)
481
482 int n;
483
484 #if !defined(LWS_PLAT_FREERTOS)
485 char buf[256];
486
487 lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name);
488 if (!lws_tls_extant(buf)) {
489 /* ah there is an updated file... how about the desired file? */
490 if (!lws_tls_extant(name)) {
491 /* rename the desired file */
492 for (n = 0; n < 50; n++) {
493 lws_snprintf(buf, sizeof(buf) - 1,
494 "%s.old.%d", name, n);
495 if (!rename(name, buf))
496 break;
497 }
498 if (n == 50) {
499 lwsl_notice("unable to rename %s\n", name);
500
501 return LWS_TLS_EXTANT_ALTERNATIVE;
502 }
503 lws_snprintf(buf, sizeof(buf) - 1, "%s.upd", name);
504 }
505 /* desired file is out of the way, rename the updated file */
506 if (rename(buf, name)) {
507 lwsl_notice("unable to rename %s to %s\n", buf, name);
508
509 return LWS_TLS_EXTANT_ALTERNATIVE;
510 }
511 }
512
513 if (lws_tls_extant(name))
514 return LWS_TLS_EXTANT_NO;
515 #else
516 nvs_handle nvh;
517 size_t s = 8192;
518
519 if (nvs_open("lws-station", NVS_READWRITE, &nvh)) {
520 lwsl_notice("%s: can't open nvs\n", __func__);
521 return LWS_TLS_EXTANT_NO;
522 }
523
524 n = nvs_get_blob(nvh, name, NULL, &s);
525 nvs_close(nvh);
526
527 if (n)
528 return LWS_TLS_EXTANT_NO;
529 #endif
530 #endif
531 return LWS_TLS_EXTANT_YES;
532 }
533