• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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