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