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