/src/libwebsockets/lib/tls/tls-network.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 | | |
27 | | /* |
28 | | * fakes POLLIN on all tls guys with buffered rx |
29 | | * |
30 | | * returns nonzero if any tls guys had POLLIN faked |
31 | | */ |
32 | | |
33 | | int |
34 | | lws_tls_fake_POLLIN_for_buffered(struct lws_context_per_thread *pt) |
35 | 0 | { |
36 | 0 | int ret = 0; |
37 | |
|
38 | 0 | lws_start_foreach_dll_safe(struct lws_dll2 *, p, p1, |
39 | 0 | lws_dll2_get_head(&pt->tls.dll_pending_tls_owner)) { |
40 | 0 | struct lws *wsi = lws_container_of(p, struct lws, |
41 | 0 | tls.dll_pending_tls); |
42 | | |
43 | | /* |
44 | | * ... allow custom event loop to override our POLLIN-setting |
45 | | * implementation if it knows how to do it better for its case |
46 | | */ |
47 | | |
48 | 0 | if (pt->context->event_loop_ops && |
49 | 0 | pt->context->event_loop_ops->fake_POLLIN_override) |
50 | 0 | pt->context->event_loop_ops->fake_POLLIN_override( |
51 | 0 | pt->context, pt->tid); |
52 | 0 | else { |
53 | 0 | if (wsi->position_in_fds_table >= 0) { |
54 | |
|
55 | 0 | pt->fds[wsi->position_in_fds_table].revents = (short) |
56 | 0 | (pt->fds[wsi->position_in_fds_table].revents | |
57 | 0 | (pt->fds[wsi->position_in_fds_table].events & LWS_POLLIN)); |
58 | 0 | ret |= pt->fds[wsi->position_in_fds_table].revents & LWS_POLLIN; |
59 | | |
60 | | // lwsl_notice("%s: faked POLLIN for %s, revents=0x%x\n", __func__, lws_wsi_tag(wsi), pt->fds[wsi->position_in_fds_table].revents); |
61 | 0 | } |
62 | 0 | } |
63 | |
|
64 | 0 | } lws_end_foreach_dll_safe(p, p1); |
65 | |
|
66 | 0 | return !!ret; |
67 | 0 | } |
68 | | |
69 | | void |
70 | | __lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi) |
71 | 0 | { |
72 | 0 | lws_dll2_remove(&wsi->tls.dll_pending_tls); |
73 | 0 | } |
74 | | |
75 | | void |
76 | | lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi) |
77 | 0 | { |
78 | 0 | struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; |
79 | |
|
80 | 0 | lws_pt_lock(pt, __func__); |
81 | 0 | __lws_ssl_remove_wsi_from_buffered_list(wsi); |
82 | 0 | lws_pt_unlock(pt); |
83 | 0 | } |
84 | | |
85 | | struct lws_tls_ctx_ref * |
86 | | lws_tls_ctx_ref_create(struct lws_vhost *vh, lws_tls_ctx *ctx) |
87 | 0 | { |
88 | 0 | struct lws_tls_ctx_ref *ref; |
89 | |
|
90 | 0 | if (!ctx) |
91 | 0 | return NULL; |
92 | | |
93 | 0 | ref = lws_zalloc(sizeof(*ref), "ctx_ref"); |
94 | 0 | if (!ref) |
95 | 0 | return NULL; |
96 | | |
97 | 0 | ref->vh = vh; |
98 | 0 | ref->ctx = ctx; |
99 | 0 | ref->refcount = 1; |
100 | |
|
101 | 0 | return ref; |
102 | 0 | } |
103 | | |
104 | | struct lws_tls_ctx_ref * |
105 | | lws_tls_ctx_ref_get(struct lws_vhost *vh) |
106 | 0 | { |
107 | 0 | struct lws_tls_ctx_ref *ref; |
108 | |
|
109 | 0 | lws_vhost_lock(vh); |
110 | 0 | ref = vh->tls.active_ctx_ref; |
111 | 0 | if (ref) |
112 | 0 | ref->refcount++; |
113 | 0 | lws_vhost_unlock(vh); |
114 | |
|
115 | 0 | return ref; |
116 | 0 | } |
117 | | |
118 | | void |
119 | | lws_tls_ctx_ref_unref(struct lws_tls_ctx_ref *ref) |
120 | 0 | { |
121 | 0 | struct lws_vhost *vh; |
122 | |
|
123 | 0 | if (!ref) |
124 | 0 | return; |
125 | | |
126 | 0 | vh = ref->vh; |
127 | 0 | lws_vhost_lock(vh); |
128 | 0 | if (--ref->refcount == 0) { |
129 | 0 | lws_dll2_remove(&ref->list); |
130 | 0 | lws_tls_vhost_backend_free_ctx(ref->ctx); |
131 | 0 | lws_free(ref); |
132 | 0 | } |
133 | 0 | lws_vhost_unlock(vh); |
134 | 0 | } |
135 | | |
136 | | void |
137 | | lws_tls_ctx_ref_destroy_all(struct lws_vhost *vhost) |
138 | 0 | { |
139 | 0 | if (vhost->tls.active_ctx_ref) { |
140 | 0 | lws_tls_ctx_ref_unref(vhost->tls.active_ctx_ref); |
141 | 0 | vhost->tls.active_ctx_ref = NULL; |
142 | 0 | vhost->tls.ssl_ctx = NULL; |
143 | 0 | } |
144 | |
|
145 | 0 | lws_start_foreach_dll_safe(struct lws_dll2 *, d, d1, |
146 | 0 | lws_dll2_get_head(&vhost->tls.retired_ctx_list)) { |
147 | 0 | struct lws_tls_ctx_ref *r = lws_container_of(d, struct lws_tls_ctx_ref, list); |
148 | 0 | lwsl_vhost_err(vhost, "Retired ctx_ref %p leaked with refcount %d", r, r->refcount); |
149 | | /* forcefully free it to avoid memory leak if WSI leaked */ |
150 | 0 | lws_dll2_remove(&r->list); |
151 | 0 | lws_tls_vhost_backend_free_ctx(r->ctx); |
152 | 0 | lws_free(r); |
153 | 0 | } lws_end_foreach_dll_safe(d, d1); |
154 | 0 | } |
155 | | |
156 | | |
157 | | #if defined(LWS_WITH_SERVER) |
158 | | int |
159 | | lws_tls_check_cert_lifetime(struct lws_vhost *v) |
160 | 0 | { |
161 | 0 | time_t now = (time_t)lws_now_secs(), life = 0; |
162 | 0 | struct lws_acme_cert_aging_args caa; |
163 | 0 | union lws_tls_cert_info_results ir; |
164 | 0 | int n; |
165 | | |
166 | | /* Check if the active cert context needs rotation under grace period policy */ |
167 | 0 | if (v->tls.ssl_ctx && v->tls.cfg_alloc_cert_path && v->tls.cfg_key_path && |
168 | 0 | (strstr(v->tls.cfg_alloc_cert_path, "-latest.crt") || |
169 | 0 | strstr(v->tls.cfg_alloc_cert_path, "-latest-fullchain.crt"))) { |
170 | 0 | char resolved_cert[256]; |
171 | 0 | char resolved_key[256]; |
172 | 0 | time_t resolved_from = 0, resolved_to = 0; |
173 | 0 | union lws_tls_cert_info_results loaded_from, loaded_to; |
174 | | |
175 | | /* Resolve what certificate path should be active right now */ |
176 | 0 | if (lws_tls_resolve_grace_period_certs(v->context, |
177 | 0 | v->tls.cfg_alloc_cert_path, |
178 | 0 | v->tls.cfg_key_path, |
179 | 0 | resolved_cert, sizeof(resolved_cert), |
180 | 0 | resolved_key, sizeof(resolved_key)) == 0) { |
181 | | /* Get validity of resolved cert file */ |
182 | 0 | if (lws_tls_cert_get_x509_validity(v->context, resolved_cert, |
183 | 0 | &resolved_from, &resolved_to) == 0) { |
184 | | /* Get validity of currently loaded cert context */ |
185 | 0 | if (lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_FROM, |
186 | 0 | &loaded_from, 0) == 0 && |
187 | 0 | lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_TO, |
188 | 0 | &loaded_to, 0) == 0) { |
189 | 0 | if (resolved_from != loaded_from.time || |
190 | 0 | resolved_to != loaded_to.time) { |
191 | 0 | lwsl_notice("%s: Active certificate for vhost %s is out of date. Rotating dynamically.\n", |
192 | 0 | __func__, v->name); |
193 | 0 | lws_tls_cert_updated(v->context, |
194 | 0 | v->tls.cfg_alloc_cert_path, |
195 | 0 | v->tls.cfg_key_path, |
196 | 0 | NULL, 0, NULL, 0); |
197 | 0 | } |
198 | 0 | } |
199 | 0 | } |
200 | 0 | } |
201 | 0 | } |
202 | |
|
203 | 0 | if (v->tls.ssl_ctx && !v->tls.skipped_certs) { |
204 | |
|
205 | 0 | if (now < 1542933698) /* Nov 23 2018 00:42 UTC */ |
206 | | /* our clock is wrong and we can't judge the certs */ |
207 | 0 | return -1; |
208 | | |
209 | 0 | n = lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_TO, |
210 | 0 | &ir, 0); |
211 | 0 | if (n) |
212 | 0 | return 1; |
213 | | |
214 | 0 | life = (ir.time - now) / (24 * 3600); |
215 | 0 | lwsl_vhost_notice(v, " vhost %s: cert expiry: %lldd", v->name, |
216 | 0 | (long long)life); |
217 | 0 | } else |
218 | 0 | lwsl_vhost_info(v, " vhost %s: no cert", v->name); |
219 | | |
220 | 0 | memset(&caa, 0, sizeof(caa)); |
221 | 0 | caa.vh = v; |
222 | 0 | lws_broadcast(&v->context->pt[0], LWS_CALLBACK_VHOST_CERT_AGING, (void *)&caa, |
223 | 0 | (size_t)(ssize_t)life); |
224 | |
|
225 | 0 | return 0; |
226 | 0 | } |
227 | | |
228 | | int |
229 | | lws_tls_check_all_cert_lifetimes(struct lws_context *context) |
230 | 0 | { |
231 | 0 | struct lws_vhost *v = context->vhost_list; |
232 | |
|
233 | 0 | while (v) { |
234 | 0 | if (lws_tls_check_cert_lifetime(v) < 0) |
235 | 0 | return -1; |
236 | 0 | v = v->vhost_next; |
237 | 0 | } |
238 | | |
239 | 0 | return 0; |
240 | 0 | } |
241 | | |
242 | | /* |
243 | | * LWS_TLS_EXTANT_NO : skip adding the cert |
244 | | * LWS_TLS_EXTANT_YES : use the cert and private key paths normally |
245 | | * LWS_TLS_EXTANT_ALTERNATIVE: normal paths not usable, try alternate if poss |
246 | | */ |
247 | | enum lws_tls_extant |
248 | | lws_tls_generic_cert_checks(struct lws_vhost *vhost, const char *cert, |
249 | | const char *private_key) |
250 | 0 | { |
251 | 0 | int n, m; |
252 | | |
253 | | /* |
254 | | * The user code can choose to either pass the cert and |
255 | | * key filepaths using the info members like this, or it can |
256 | | * leave them NULL; force the vhost SSL_CTX init using the info |
257 | | * options flag LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX; and |
258 | | * set up the cert himself using the user callback |
259 | | * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, which |
260 | | * happened just above and has the vhost SSL_CTX * in the user |
261 | | * parameter. |
262 | | */ |
263 | |
|
264 | 0 | if (!cert || !private_key) |
265 | 0 | return LWS_TLS_EXTANT_NO; |
266 | | |
267 | 0 | n = (int)lws_tls_use_any_upgrade_check_extant(cert); |
268 | 0 | if (n == LWS_TLS_EXTANT_ALTERNATIVE) |
269 | 0 | return LWS_TLS_EXTANT_ALTERNATIVE; |
270 | 0 | m = (int)lws_tls_use_any_upgrade_check_extant(private_key); |
271 | 0 | if (m == LWS_TLS_EXTANT_ALTERNATIVE) |
272 | 0 | return LWS_TLS_EXTANT_ALTERNATIVE; |
273 | | |
274 | 0 | if ((n == LWS_TLS_EXTANT_NO || m == LWS_TLS_EXTANT_NO) && |
275 | 0 | (vhost->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) { |
276 | 0 | lwsl_vhost_notice(vhost, "Ignoring missing %s or %s", cert, private_key); |
277 | 0 | vhost->tls.skipped_certs = 1; |
278 | |
|
279 | 0 | return LWS_TLS_EXTANT_NO; |
280 | 0 | } |
281 | | |
282 | | /* |
283 | | * the cert + key exist |
284 | | */ |
285 | | |
286 | 0 | return LWS_TLS_EXTANT_YES; |
287 | 0 | } |
288 | | |
289 | | /* |
290 | | * update the cert for every vhost using the given path |
291 | | */ |
292 | | |
293 | | int |
294 | | lws_tls_cert_updated(struct lws_context *context, const char *certpath, |
295 | | const char *keypath, |
296 | | const char *mem_cert, size_t len_mem_cert, |
297 | | const char *mem_privkey, size_t len_mem_privkey) |
298 | 0 | { |
299 | 0 | struct lws wsi; |
300 | |
|
301 | 0 | wsi.a.context = context; |
302 | |
|
303 | 0 | lws_start_foreach_ll(struct lws_vhost *, v, context->vhost_list) { |
304 | 0 | wsi.a.vhost = v; /* not a real bound wsi */ |
305 | 0 | if (v->tls.cfg_alloc_cert_path && v->tls.cfg_key_path && |
306 | 0 | !strcmp(v->tls.cfg_alloc_cert_path, certpath) && |
307 | 0 | !strcmp(v->tls.cfg_key_path, keypath)) { |
308 | |
|
309 | 0 | lws_tls_ctx *old_ctx = v->tls.ssl_ctx; |
310 | 0 | struct lws_tls_ctx_ref *old_ref = v->tls.active_ctx_ref; |
311 | |
|
312 | 0 | if (lws_tls_vhost_backend_create_ctx(v)) { |
313 | 0 | lwsl_vhost_err(v, "Failed to recreate SSL_CTX"); |
314 | 0 | continue; |
315 | 0 | } |
316 | | |
317 | 0 | struct lws_tls_ctx_ref *new_ref = lws_tls_ctx_ref_create(v, v->tls.ssl_ctx); |
318 | 0 | if (!new_ref) { |
319 | 0 | lws_tls_vhost_backend_free_ctx(v->tls.ssl_ctx); |
320 | 0 | v->tls.ssl_ctx = old_ctx; |
321 | 0 | continue; |
322 | 0 | } |
323 | | |
324 | 0 | if (lws_tls_server_certs_load(v, &wsi, certpath, keypath, |
325 | 0 | mem_cert, len_mem_cert, |
326 | 0 | mem_privkey, len_mem_privkey)) { |
327 | | /* Failed to load new certs. Revert to old context */ |
328 | 0 | lws_tls_ctx_ref_unref(new_ref); |
329 | 0 | v->tls.ssl_ctx = old_ctx; |
330 | 0 | lwsl_vhost_err(v, "Failed to load updated certs"); |
331 | 0 | continue; |
332 | 0 | } |
333 | | |
334 | | /* Successfully loaded. Commit new ref and retire old ref */ |
335 | 0 | v->tls.active_ctx_ref = new_ref; |
336 | |
|
337 | 0 | if (old_ref) { |
338 | 0 | lws_dll2_add_tail(&old_ref->list, &v->tls.retired_ctx_list); |
339 | 0 | lws_tls_ctx_ref_unref(old_ref); |
340 | 0 | } |
341 | |
|
342 | 0 | if (v->tls.skipped_certs) |
343 | 0 | lwsl_vhost_notice(v, "vhost %s: cert unset", v->name); |
344 | 0 | } |
345 | 0 | } lws_end_foreach_ll(v, vhost_next); |
346 | |
|
347 | 0 | return 0; |
348 | 0 | } |
349 | | |
350 | | int |
351 | | lws_gate_accepts(struct lws_context *context, int on) |
352 | 0 | { |
353 | 0 | struct lws_vhost *v = context->vhost_list; |
354 | |
|
355 | 0 | if (context->tls_gate_accepts == (char)on) |
356 | 0 | return 0; |
357 | | |
358 | 0 | lwsl_cx_info(context, "on = %d", on); |
359 | |
|
360 | 0 | context->tls_gate_accepts = (char)on; |
361 | |
|
362 | 0 | while (v) { |
363 | 0 | lws_start_foreach_dll(struct lws_dll2 *, d, |
364 | 0 | lws_dll2_get_head(&v->listen_wsi)) { |
365 | 0 | struct lws *wsi = lws_container_of(d, struct lws, |
366 | 0 | listen_list); |
367 | |
|
368 | 0 | if (v->tls.use_ssl && |
369 | 0 | lws_change_pollfd(wsi, on ? LWS_POLLIN : 0, |
370 | 0 | on ? 0 : LWS_POLLIN)) |
371 | 0 | lwsl_cx_notice(context, "Unable to set POLLIN %d", on); |
372 | 0 | } lws_end_foreach_dll(d); |
373 | |
|
374 | 0 | v = v->vhost_next; |
375 | 0 | } |
376 | |
|
377 | 0 | return 0; |
378 | 0 | } |
379 | | #endif |
380 | | |
381 | | /* comma-separated alpn list, like "h2,http/1.1" to openssl alpn format */ |
382 | | |
383 | | int |
384 | | lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len) |
385 | 0 | { |
386 | 0 | uint8_t *oos = os, *plen = NULL; |
387 | |
|
388 | 0 | if (!comma) |
389 | 0 | return 0; |
390 | | |
391 | 0 | while (*comma && len > 0) { |
392 | 0 | if (!plen && *comma == ' ') { |
393 | 0 | comma++; |
394 | 0 | continue; |
395 | 0 | } |
396 | 0 | if (!plen) { |
397 | 0 | if (len < 2) |
398 | 0 | break; |
399 | 0 | plen = os++; |
400 | 0 | len--; |
401 | 0 | } |
402 | | |
403 | 0 | if (*comma == ',') { |
404 | 0 | *plen = (uint8_t)lws_ptr_diff(os, plen + 1); |
405 | 0 | plen = NULL; |
406 | 0 | comma++; |
407 | 0 | } else { |
408 | 0 | *os++ = (uint8_t)*comma++; |
409 | 0 | len--; |
410 | 0 | } |
411 | 0 | } |
412 | |
|
413 | 0 | if (plen) |
414 | 0 | *plen = (uint8_t)lws_ptr_diff(os, plen + 1); |
415 | |
|
416 | 0 | *os = 0; |
417 | |
|
418 | 0 | return lws_ptr_diff(os, oos); |
419 | 0 | } |
420 | | |
421 | | |
422 | | |
423 | | |
424 | | void |
425 | | lws_tls_cleanup_process(void) |
426 | 0 | { |
427 | | #if defined(LWS_WITH_MBEDTLS) |
428 | | if (tls_ops_mbedtls.process_cleanup) |
429 | | tls_ops_mbedtls.process_cleanup(); |
430 | | #elif defined(LWS_WITH_SCHANNEL) |
431 | | if (tls_ops_schannel.process_cleanup) |
432 | | tls_ops_schannel.process_cleanup(); |
433 | | #elif defined(LWS_WITH_GNUTLS) |
434 | | if (tls_ops_gnutls.process_cleanup) |
435 | | tls_ops_gnutls.process_cleanup(); |
436 | | #elif defined(LWS_WITH_BEARSSL) |
437 | | if (tls_ops_bearssl.process_cleanup) |
438 | | tls_ops_bearssl.process_cleanup(); |
439 | | #elif defined(LWS_WITH_OPENHITLS) |
440 | | if (tls_ops_openhitls.process_cleanup) |
441 | | tls_ops_openhitls.process_cleanup(); |
442 | | #else |
443 | 0 | if (tls_ops_openssl.process_cleanup) |
444 | 0 | tls_ops_openssl.process_cleanup(); |
445 | 0 | #endif |
446 | 0 | } |