/src/libwebsockets/lib/tls/openssl/openssl-server.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 | | #include "private-lib-tls-openssl.h" |
27 | | |
28 | | /* |
29 | | * Care: many openssl apis return 1 for success. These are translated to the |
30 | | * lws convention of 0 for success. |
31 | | */ |
32 | | |
33 | | extern int openssl_websocket_private_data_index, |
34 | | openssl_SSL_CTX_private_data_index; |
35 | | |
36 | | int lws_openssl_describe_cipher(struct lws *wsi); |
37 | | |
38 | | static int |
39 | | OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) |
40 | 0 | { |
41 | 0 | SSL *ssl; |
42 | 0 | int n; |
43 | 0 | struct lws *wsi; |
44 | 0 | union lws_tls_cert_info_results ir; |
45 | 0 | X509 *topcert = X509_STORE_CTX_get_current_cert(x509_ctx); |
46 | |
|
47 | 0 | ssl = X509_STORE_CTX_get_ex_data(x509_ctx, |
48 | 0 | SSL_get_ex_data_X509_STORE_CTX_idx()); |
49 | | |
50 | | /* |
51 | | * !!! nasty openssl requires the index to come as a library-scope |
52 | | * static |
53 | | */ |
54 | 0 | wsi = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); |
55 | 0 | if (!wsi) |
56 | 0 | return 0; /* OpenSSL failure */ |
57 | | |
58 | 0 | n = lws_tls_openssl_cert_info(topcert, LWS_TLS_CERT_INFO_COMMON_NAME, |
59 | 0 | &ir, sizeof(ir.ns.name)); |
60 | 0 | if (!n) |
61 | 0 | lwsl_info("%s: client cert CN '%s'\n", __func__, ir.ns.name); |
62 | 0 | else |
63 | 0 | lwsl_info("%s: couldn't get client cert CN\n", __func__); |
64 | |
|
65 | 0 | n = wsi->a.vhost->protocols[0].callback(wsi, |
66 | 0 | LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION, |
67 | 0 | x509_ctx, ssl, (unsigned int)preverify_ok); |
68 | | |
69 | | /* convert return code from 0 = OK to 1 = OK */ |
70 | 0 | return !n; |
71 | 0 | } |
72 | | |
73 | | int |
74 | | lws_tls_server_client_cert_verify_config(struct lws_vhost *vh) |
75 | 0 | { |
76 | 0 | int verify_options = SSL_VERIFY_PEER; |
77 | | |
78 | | /* as a server, are we requiring clients to identify themselves? */ |
79 | |
|
80 | 0 | if (!lws_check_opt(vh->options, |
81 | 0 | LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT)) |
82 | 0 | return 0; |
83 | | |
84 | 0 | if (!lws_check_opt(vh->options, |
85 | 0 | LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED)) |
86 | 0 | verify_options |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; |
87 | |
|
88 | 0 | SSL_CTX_set_session_id_context(vh->tls.ssl_ctx, (uint8_t *)vh->context, |
89 | 0 | sizeof(void *)); |
90 | | |
91 | | /* absolutely require the client cert */ |
92 | 0 | SSL_CTX_set_verify(vh->tls.ssl_ctx, verify_options, |
93 | 0 | OpenSSL_verify_callback); |
94 | |
|
95 | 0 | return 0; |
96 | 0 | } |
97 | | |
98 | | #if defined(SSL_TLSEXT_ERR_NOACK) && !defined(OPENSSL_NO_TLSEXT) |
99 | | static int |
100 | | lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) |
101 | 0 | { |
102 | 0 | struct lws_context *context = (struct lws_context *)arg; |
103 | 0 | struct lws_vhost *vhost, *vh; |
104 | 0 | const char *servername; |
105 | |
|
106 | 0 | if (!ssl) |
107 | 0 | return SSL_TLSEXT_ERR_NOACK; |
108 | | |
109 | | /* |
110 | | * We can only get ssl accepted connections by using a vhost's ssl_ctx |
111 | | * find out which listening one took us and only match vhosts on the |
112 | | * same port. |
113 | | */ |
114 | 0 | vh = context->vhost_list; |
115 | 0 | while (vh) { |
116 | 0 | if (!vh->being_destroyed && |
117 | 0 | vh->tls.ssl_ctx == SSL_get_SSL_CTX(ssl)) |
118 | 0 | break; |
119 | 0 | vh = vh->vhost_next; |
120 | 0 | } |
121 | |
|
122 | 0 | if (!vh) { |
123 | 0 | assert(vh); /* can't match the incoming vh? */ |
124 | 0 | return SSL_TLSEXT_ERR_OK; |
125 | 0 | } |
126 | | |
127 | 0 | servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); |
128 | 0 | if (!servername) { |
129 | | /* the client doesn't know what hostname it wants */ |
130 | 0 | lwsl_info("SNI: Unknown ServerName\n"); |
131 | |
|
132 | 0 | return SSL_TLSEXT_ERR_OK; |
133 | 0 | } |
134 | | |
135 | 0 | vhost = lws_select_vhost(context, vh->listen_port, servername); |
136 | 0 | if (!vhost) { |
137 | 0 | lwsl_info("SNI: none: %s:%d\n", servername, vh->listen_port); |
138 | |
|
139 | 0 | return SSL_TLSEXT_ERR_OK; |
140 | 0 | } |
141 | | |
142 | 0 | lwsl_info("SNI: Found: %s:%d\n", servername, vh->listen_port); |
143 | |
|
144 | 0 | if (!vhost->tls.ssl_ctx) { |
145 | 0 | lwsl_info("SNI: %s has no tls ctx yet\n", servername); |
146 | 0 | return SSL_TLSEXT_ERR_OK; |
147 | 0 | } |
148 | | |
149 | | /* select the ssl ctx from the selected vhost for this conn */ |
150 | 0 | SSL_set_SSL_CTX(ssl, vhost->tls.ssl_ctx); |
151 | | |
152 | | /* |
153 | | * OpenSSL's SSL_set_SSL_CTX does NOT copy the verify mode or client CA list |
154 | | * from the new context to the active SSL object! We must do it manually |
155 | | * so SNI vhosts can have different client cert requirements than the default vhost. |
156 | | */ |
157 | 0 | SSL_set_verify(ssl, SSL_CTX_get_verify_mode(vhost->tls.ssl_ctx), |
158 | 0 | SSL_CTX_get_verify_callback(vhost->tls.ssl_ctx)); |
159 | |
|
160 | 0 | if (SSL_CTX_get_client_CA_list(vhost->tls.ssl_ctx)) |
161 | 0 | SSL_set_client_CA_list(ssl, SSL_dup_CA_list(SSL_CTX_get_client_CA_list(vhost->tls.ssl_ctx))); |
162 | |
|
163 | 0 | return SSL_TLSEXT_ERR_OK; |
164 | 0 | } |
165 | | #endif |
166 | | |
167 | | /* |
168 | | * this may now get called after the vhost creation, when certs become |
169 | | * available. |
170 | | */ |
171 | | int |
172 | | lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, |
173 | | const char *cert, const char *private_key, |
174 | | const char *mem_cert, size_t mem_cert_len, |
175 | | const char *mem_privkey, size_t mem_privkey_len) |
176 | 0 | { |
177 | 0 | #if !defined(OPENSSL_NO_EC) && defined(LWS_HAVE_EC_KEY_new_by_curve_name) && \ |
178 | 0 | ((OPENSSL_VERSION_NUMBER < 0x30000000l) || \ |
179 | 0 | defined(LWS_SUPPRESS_DEPRECATED_API_WARNINGS)) |
180 | 0 | const char *ecdh_curve = "prime256v1"; |
181 | 0 | #if !defined(LWS_WITH_BORINGSSL) && !defined(LWS_WITH_AWSLC) && defined(LWS_HAVE_SSL_EXTRA_CHAIN_CERTS) |
182 | 0 | STACK_OF(X509) *extra_certs = NULL; |
183 | 0 | #endif |
184 | 0 | EC_KEY *ecdh, *EC_key = NULL; |
185 | 0 | EVP_PKEY *pkey; |
186 | 0 | X509 *x = NULL; |
187 | 0 | int ecdh_nid; |
188 | 0 | int KeyType; |
189 | 0 | #endif |
190 | 0 | unsigned long error; |
191 | 0 | lws_filepos_t flen; |
192 | 0 | uint8_t *p; |
193 | 0 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
194 | 0 | int ret; |
195 | 0 | #endif |
196 | 0 | char resolved_cert[256]; |
197 | 0 | char resolved_key[256]; |
198 | |
|
199 | 0 | if (cert && private_key) { |
200 | 0 | if (lws_tls_resolve_grace_period_certs(vhost->context, cert, private_key, |
201 | 0 | resolved_cert, sizeof(resolved_cert), |
202 | 0 | resolved_key, sizeof(resolved_key)) == 0) { |
203 | 0 | cert = resolved_cert; |
204 | 0 | private_key = resolved_key; |
205 | 0 | } |
206 | 0 | } |
207 | |
|
208 | 0 | int n = (int)lws_tls_generic_cert_checks(vhost, cert, private_key), m; |
209 | |
|
210 | 0 | if (!cert && !private_key) |
211 | 0 | n = LWS_TLS_EXTANT_ALTERNATIVE; |
212 | |
|
213 | 0 | if (n == LWS_TLS_EXTANT_NO && (!mem_cert || !mem_privkey)) |
214 | 0 | return 0; |
215 | 0 | if (n == LWS_TLS_EXTANT_NO) |
216 | 0 | n = LWS_TLS_EXTANT_ALTERNATIVE; |
217 | |
|
218 | 0 | if (n == LWS_TLS_EXTANT_ALTERNATIVE && (!mem_cert || !mem_privkey)) |
219 | 0 | return 1; /* no alternative */ |
220 | | |
221 | 0 | if (n == LWS_TLS_EXTANT_ALTERNATIVE) { |
222 | |
|
223 | 0 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
224 | | |
225 | | /* |
226 | | * Although we have prepared update certs, we no longer have |
227 | | * the rights to read our own cert + key we saved. |
228 | | * |
229 | | * If we were passed copies in memory buffers, use those |
230 | | * in favour of the filepaths we normally want. |
231 | | */ |
232 | 0 | cert = NULL; |
233 | 0 | private_key = NULL; |
234 | 0 | } |
235 | | |
236 | | /* |
237 | | * use the multi-cert interface for backwards compatibility in the |
238 | | * both simple files case |
239 | | */ |
240 | |
|
241 | 0 | if (n != LWS_TLS_EXTANT_ALTERNATIVE && cert) { |
242 | | |
243 | | /* set the local certificate from CertFile */ |
244 | 0 | m = SSL_CTX_use_certificate_chain_file(vhost->tls.ssl_ctx, cert); |
245 | 0 | if (m != 1) { |
246 | 0 | const char *s; |
247 | 0 | char buf[256]; |
248 | 0 | error = ERR_peek_error(); |
249 | 0 | s = ERR_error_string(LWS_TLS_ERR_CAST(ERR_get_error()), buf); |
250 | |
|
251 | 0 | lwsl_err("problem getting cert '%s' %lu: %s\n", |
252 | 0 | cert, error, s); |
253 | |
|
254 | 0 | return 1; |
255 | 0 | } |
256 | | |
257 | 0 | if (!private_key) { |
258 | 0 | lwsl_err("ssl private key not set\n"); |
259 | 0 | return 1; |
260 | 0 | } else { |
261 | | /* set the private key from KeyFile */ |
262 | 0 | if (SSL_CTX_use_PrivateKey_file(vhost->tls.ssl_ctx, private_key, |
263 | 0 | SSL_FILETYPE_PEM) != 1) { |
264 | 0 | const char *s; |
265 | 0 | char buf[256]; |
266 | 0 | error = ERR_peek_error(); |
267 | 0 | s = ERR_error_string(LWS_TLS_ERR_CAST(ERR_get_error()), buf); |
268 | 0 | lwsl_err("ssl problem getting key '%s' %lu: %s\n", |
269 | 0 | private_key, error, s); |
270 | 0 | return 1; |
271 | 0 | } |
272 | 0 | } |
273 | | |
274 | 0 | return 0; |
275 | 0 | } |
276 | | |
277 | | /* otherwise allow for DER or PEM, file or memory image */ |
278 | | |
279 | 0 | if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, mem_cert, |
280 | 0 | mem_cert_len, &p, &flen)) { |
281 | 0 | lwsl_err("%s: couldn't read cert file\n", __func__); |
282 | |
|
283 | 0 | return 1; |
284 | 0 | } |
285 | | |
286 | 0 | #if !defined(USE_WOLFSSL) |
287 | 0 | ret = SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, SSL_SIZE_T_CAST(flen), p); |
288 | | #else |
289 | | ret = wolfSSL_CTX_use_certificate_buffer(vhost->tls.ssl_ctx, |
290 | | (uint8_t *)p, (int)flen, |
291 | | WOLFSSL_FILETYPE_ASN1); |
292 | | #endif |
293 | 0 | lws_free_set_NULL(p); |
294 | 0 | if (ret != 1) { |
295 | 0 | lwsl_err("%s: Problem loading cert\n", __func__); |
296 | |
|
297 | 0 | return 1; |
298 | 0 | } |
299 | | |
300 | 0 | if (lws_tls_alloc_pem_to_der_file(vhost->context, private_key, |
301 | 0 | mem_privkey, mem_privkey_len, |
302 | 0 | &p, &flen)) { |
303 | 0 | lwsl_notice("unable to convert memory privkey\n"); |
304 | |
|
305 | 0 | return 1; |
306 | 0 | } |
307 | | |
308 | 0 | #if !defined(USE_WOLFSSL) |
309 | 0 | ret = SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, vhost->tls.ssl_ctx, p, |
310 | | #if defined(LWS_WITH_BORINGSSL) || defined(LWS_WITH_AWSLC) |
311 | | (size_t) |
312 | | #else |
313 | 0 | (long)(long long) |
314 | 0 | #endif |
315 | 0 | flen); |
316 | 0 | if (ret != 1) { |
317 | 0 | ret = SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_EC, |
318 | 0 | vhost->tls.ssl_ctx, p, |
319 | | #if defined(LWS_WITH_BORINGSSL) || defined(LWS_WITH_AWSLC) |
320 | | (size_t) |
321 | | #else |
322 | 0 | (long)(long long) |
323 | 0 | #endif |
324 | 0 | flen); |
325 | 0 | } |
326 | | #else |
327 | | ret = wolfSSL_CTX_use_PrivateKey_buffer(vhost->tls.ssl_ctx, p, (long) flen, |
328 | | WOLFSSL_FILETYPE_ASN1); |
329 | | #endif |
330 | 0 | lws_free_set_NULL(p); |
331 | 0 | if (ret != 1) { |
332 | 0 | lwsl_notice("unable to use memory privkey\n"); |
333 | |
|
334 | 0 | return 1; |
335 | 0 | } |
336 | | |
337 | | #else |
338 | | /* |
339 | | * Although we have prepared update certs, we no longer have |
340 | | * the rights to read our own cert + key we saved. |
341 | | * |
342 | | * If we were passed copies in memory buffers, use those |
343 | | * instead. |
344 | | * |
345 | | * The passed memory-buffer cert image is in DER, and the |
346 | | * memory-buffer private key image is PEM. |
347 | | */ |
348 | | if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, mem_cert, |
349 | | mem_cert_len, &p, &flen)) { |
350 | | lwsl_err("%s: couldn't convert pem to der\n", __func__); |
351 | | return 1; |
352 | | } |
353 | | #ifndef USE_WOLFSSL |
354 | | if (SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, |
355 | | (int)flen, |
356 | | (uint8_t *)p) != 1) { |
357 | | #else |
358 | | if (wolfSSL_CTX_use_certificate_buffer(vhost->tls.ssl_ctx, |
359 | | (uint8_t *)p, |
360 | | (int)flen, |
361 | | WOLFSSL_FILETYPE_ASN1) != 1) { |
362 | | |
363 | | #endif |
364 | | lwsl_err("Problem loading update cert\n"); |
365 | | |
366 | | return 1; |
367 | | } |
368 | | |
369 | | if (lws_tls_alloc_pem_to_der_file(vhost->context, NULL, |
370 | | mem_privkey, mem_privkey_len, |
371 | | &p, &flen)) { |
372 | | lwsl_notice("unable to convert memory privkey\n"); |
373 | | |
374 | | return 1; |
375 | | } |
376 | | #ifndef USE_WOLFSSL |
377 | | if (SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, |
378 | | vhost->tls.ssl_ctx, p, |
379 | | (long)(long long)flen) != 1) { |
380 | | #else |
381 | | if (wolfSSL_CTX_use_PrivateKey_buffer(vhost->tls.ssl_ctx, p, |
382 | | (long)flen, WOLFSSL_FILETYPE_ASN1) != 1) { |
383 | | #endif |
384 | | lwsl_notice("unable to use memory privkey\n"); |
385 | | |
386 | | return 1; |
387 | | } |
388 | | |
389 | | goto check_key; |
390 | | } |
391 | | |
392 | | /* set the local certificate from CertFile */ |
393 | | m = SSL_CTX_use_certificate_chain_file(vhost->tls.ssl_ctx, cert); |
394 | | if (m != 1) { |
395 | | char buf[256]; |
396 | | error = ERR_get_error(); |
397 | | lwsl_err("problem getting cert '%s' %lu: %s\n", |
398 | | cert, error, ERR_error_string(LWS_TLS_ERR_CAST(error), buf)); |
399 | | |
400 | | return 1; |
401 | | } |
402 | | |
403 | | if (n == LWS_TLS_EXTANT_ALTERNATIVE || !private_key) { |
404 | | lwsl_err("ssl private key not set\n"); |
405 | | return 1; |
406 | | } else { |
407 | | /* set the private key from KeyFile */ |
408 | | if (SSL_CTX_use_PrivateKey_file(vhost->tls.ssl_ctx, private_key, |
409 | | SSL_FILETYPE_PEM) != 1) { |
410 | | char buf[256]; |
411 | | error = ERR_get_error(); |
412 | | lwsl_err("ssl problem getting key '%s' %lu: %s\n", |
413 | | private_key, error, |
414 | | ERR_error_string(LWS_TLS_ERR_CAST(error), buf)); |
415 | | return 1; |
416 | | } |
417 | | } |
418 | | |
419 | | check_key: |
420 | | #endif |
421 | | |
422 | | /* verify private key */ |
423 | 0 | if (!SSL_CTX_check_private_key(vhost->tls.ssl_ctx)) { |
424 | 0 | lwsl_err("Private SSL key doesn't match cert\n"); |
425 | |
|
426 | 0 | return 1; |
427 | 0 | } |
428 | | |
429 | | |
430 | 0 | #if !defined(OPENSSL_NO_EC) && defined(LWS_HAVE_EC_KEY_new_by_curve_name) && \ |
431 | 0 | ((OPENSSL_VERSION_NUMBER < 0x30000000l) || \ |
432 | 0 | defined(LWS_SUPPRESS_DEPRECATED_API_WARNINGS)) |
433 | 0 | if (vhost->tls.ecdh_curve[0]) |
434 | 0 | ecdh_curve = vhost->tls.ecdh_curve; |
435 | |
|
436 | 0 | ecdh_nid = OBJ_sn2nid(ecdh_curve); |
437 | 0 | if (NID_undef == ecdh_nid) { |
438 | 0 | lwsl_err("SSL: Unknown curve name '%s'", ecdh_curve); |
439 | 0 | return 1; |
440 | 0 | } |
441 | | |
442 | 0 | ecdh = EC_KEY_new_by_curve_name(ecdh_nid); |
443 | 0 | if (NULL == ecdh) { |
444 | 0 | lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve); |
445 | 0 | return 1; |
446 | 0 | } |
447 | 0 | SSL_CTX_set_tmp_ecdh(vhost->tls.ssl_ctx, ecdh); |
448 | 0 | EC_KEY_free(ecdh); |
449 | |
|
450 | 0 | SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_SINGLE_ECDH_USE); |
451 | |
|
452 | 0 | lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve); |
453 | |
|
454 | 0 | if (lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH)) |
455 | 0 | lwsl_notice(" Using ECDH certificate support\n"); |
456 | | |
457 | | /* Get X509 certificate from ssl context */ |
458 | 0 | #if !defined(LWS_WITH_BORINGSSL) && !defined(LWS_WITH_AWSLC) && !defined(USE_WOLFSSL) |
459 | | #if !defined(LWS_HAVE_SSL_EXTRA_CHAIN_CERTS) |
460 | | x = sk_X509_value(vhost->tls.ssl_ctx->extra_certs, 0); |
461 | | #else |
462 | 0 | SSL_CTX_get_extra_chain_certs_only(vhost->tls.ssl_ctx, &extra_certs); |
463 | 0 | if (extra_certs) |
464 | 0 | x = sk_X509_value(extra_certs, 0); |
465 | 0 | else |
466 | 0 | lwsl_info("%s: no extra certs\n", __func__); |
467 | 0 | #endif |
468 | 0 | if (!x) { |
469 | | //lwsl_err("%s: x is NULL\n", __func__); |
470 | 0 | goto post_ecdh; |
471 | 0 | } |
472 | | #else |
473 | | return 0; |
474 | | #endif /* !boringssl */ |
475 | | |
476 | | /* Get the public key from certificate */ |
477 | 0 | pkey = X509_get_pubkey(x); |
478 | 0 | if (!pkey) { |
479 | 0 | lwsl_err("%s: pkey is NULL\n", __func__); |
480 | |
|
481 | 0 | return 1; |
482 | 0 | } |
483 | | /* Get the key type */ |
484 | 0 | KeyType = EVP_PKEY_type(EVP_PKEY_id(pkey)); |
485 | |
|
486 | 0 | if (EVP_PKEY_EC != KeyType) { |
487 | 0 | lwsl_notice("Key type is not EC\n"); |
488 | 0 | return 0; |
489 | 0 | } |
490 | | /* Get the key */ |
491 | 0 | EC_key = EVP_PKEY_get1_EC_KEY(pkey); |
492 | | /* Set ECDH parameter */ |
493 | 0 | if (!EC_key) { |
494 | 0 | lwsl_err("%s: ECDH key is NULL \n", __func__); |
495 | 0 | return 1; |
496 | 0 | } |
497 | 0 | SSL_CTX_set_tmp_ecdh(vhost->tls.ssl_ctx, EC_key); |
498 | |
|
499 | 0 | EC_KEY_free(EC_key); |
500 | |
|
501 | 0 | #if !defined(OPENSSL_NO_EC) && !defined(LWS_WITH_BORINGSSL) && !defined(LWS_WITH_AWSLC) && !defined(USE_WOLFSSL) |
502 | 0 | post_ecdh: |
503 | 0 | #endif |
504 | 0 | vhost->tls.skipped_certs = 0; |
505 | | #else |
506 | | lwsl_notice(" OpenSSL doesn't support ECDH\n"); |
507 | | #endif |
508 | |
|
509 | 0 | return 0; |
510 | 0 | } |
511 | | |
512 | | |
513 | | |
514 | | int |
515 | | lws_tls_vhost_backend_create_ctx(struct lws_vhost *vhost) |
516 | 0 | { |
517 | 0 | unsigned long error; |
518 | 0 | SSL_METHOD *method = (SSL_METHOD *)SSLv23_server_method(); |
519 | 0 | struct lws_vhost_tls *tls = &vhost->tls; |
520 | |
|
521 | 0 | if (!method) { |
522 | 0 | const char *s; |
523 | 0 | char buf[256]; |
524 | 0 | error = ERR_peek_error(); |
525 | 0 | s = ERR_error_string(LWS_TLS_ERR_CAST(ERR_get_error()), buf); |
526 | |
|
527 | 0 | lwsl_err("problem creating ssl method %lu: %s\n", |
528 | 0 | error, s); |
529 | 0 | return 1; |
530 | 0 | } |
531 | 0 | tls->ssl_ctx = SSL_CTX_new(method); /* create context */ |
532 | 0 | if (!tls->ssl_ctx) { |
533 | 0 | const char *s; |
534 | 0 | char buf[256]; |
535 | |
|
536 | 0 | error = ERR_peek_error(); |
537 | 0 | s = ERR_error_string(LWS_TLS_ERR_CAST(ERR_get_error()), buf); |
538 | 0 | lwsl_err("problem creating ssl context %lu: %s\n", |
539 | 0 | error, s); |
540 | 0 | return 1; |
541 | 0 | } |
542 | | /* Added for sniffing packets on hub side */ |
543 | 0 | #if defined(LWS_WITH_TLS_KEYLOG) && \ |
544 | 0 | defined(LWS_WITH_TLS) && (defined(LWS_WITH_CLIENT) || defined(LWS_WITH_SERVER)) |
545 | 0 | SSL_CTX_set_keylog_callback(tls->ssl_ctx, lws_klog_dump); |
546 | 0 | #endif |
547 | |
|
548 | 0 | SSL_CTX_set_ex_data(tls->ssl_ctx, |
549 | 0 | openssl_SSL_CTX_private_data_index, |
550 | 0 | (char *)vhost->context); |
551 | | /* Disable SSLv2 and SSLv3 */ |
552 | 0 | SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_NO_SSLv2 | |
553 | 0 | SSL_OP_NO_SSLv3); |
554 | 0 | #ifdef SSL_OP_NO_COMPRESSION |
555 | 0 | SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_NO_COMPRESSION); |
556 | 0 | #endif |
557 | 0 | if (lws_check_opt(vhost->options, |
558 | 0 | LWS_SERVER_OPTION_OPENSSL_AUTO_DH_PARAMETERS)) |
559 | 0 | #if defined(LWS_HAVE_SSL_CTX_SET_ECDH_AUTO) || defined(LWS_WITH_BORINGSSL) |
560 | 0 | (void)SSL_CTX_set_ecdh_auto(tls->ssl_ctx, 1); |
561 | | #else |
562 | | SSL_CTX_set_dh_auto(tls->ssl_ctx, 1); |
563 | | #endif |
564 | |
|
565 | 0 | SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_SINGLE_DH_USE); |
566 | 0 | SSL_CTX_set_options(tls->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); |
567 | |
|
568 | 0 | if (tls->cfg_tls_ciphers_iana) { |
569 | 0 | char *p = lws_strdup(tls->cfg_tls_ciphers_iana); |
570 | 0 | if (p) { |
571 | 0 | char *q = p; |
572 | 0 | while (*q) { |
573 | 0 | if (*q == ',') |
574 | 0 | *q = ':'; |
575 | 0 | q++; |
576 | 0 | } |
577 | 0 | SSL_CTX_set_cipher_list(tls->ssl_ctx, p); |
578 | 0 | #if defined(LWS_HAVE_SSL_CTX_set_ciphersuites) |
579 | 0 | SSL_CTX_set_ciphersuites(tls->ssl_ctx, p); |
580 | 0 | #endif |
581 | 0 | lws_free(p); |
582 | 0 | } |
583 | 0 | } else if (tls->cfg_ssl_cipher_list) |
584 | 0 | SSL_CTX_set_cipher_list(tls->ssl_ctx, tls->cfg_ssl_cipher_list); |
585 | |
|
586 | 0 | #if defined(LWS_HAVE_SSL_CTX_set_ciphersuites) |
587 | 0 | if (tls->cfg_tls1_3_plus_cipher_list) |
588 | 0 | SSL_CTX_set_ciphersuites(tls->ssl_ctx, |
589 | 0 | tls->cfg_tls1_3_plus_cipher_list); |
590 | 0 | #endif |
591 | |
|
592 | 0 | #if !defined(OPENSSL_NO_TLSEXT) |
593 | 0 | SSL_CTX_set_tlsext_servername_callback(tls->ssl_ctx, |
594 | 0 | lws_ssl_server_name_cb); |
595 | 0 | SSL_CTX_set_tlsext_servername_arg(tls->ssl_ctx, vhost->context); |
596 | 0 | #endif |
597 | |
|
598 | 0 | if (tls->cfg_ssl_ca_filepath) { |
599 | | #if defined(LWS_HAVE_SSL_CTX_load_verify_file) |
600 | | if (!SSL_CTX_load_verify_file(tls->ssl_ctx, |
601 | | tls->cfg_ssl_ca_filepath)) { |
602 | | #else |
603 | 0 | if (!SSL_CTX_load_verify_locations(tls->ssl_ctx, |
604 | 0 | tls->cfg_ssl_ca_filepath, NULL)) { |
605 | 0 | #endif |
606 | 0 | lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", |
607 | 0 | __func__); |
608 | 0 | } else { |
609 | | /* |
610 | | * Provide the CA list to the client so it knows what client certs to send |
611 | | */ |
612 | 0 | STACK_OF(X509_NAME) *calist = SSL_load_client_CA_file(tls->cfg_ssl_ca_filepath); |
613 | 0 | if (!calist) { |
614 | 0 | lwsl_err("%s: SSL_load_client_CA_file failed to load %s\n", __func__, tls->cfg_ssl_ca_filepath); |
615 | 0 | } else { |
616 | 0 | lwsl_notice("%s: Loaded %d CAs for client CA list from %s\n", __func__, (int)sk_X509_NAME_num(calist), tls->cfg_ssl_ca_filepath); |
617 | 0 | SSL_CTX_set_client_CA_list(tls->ssl_ctx, calist); |
618 | 0 | } |
619 | 0 | } |
620 | 0 | } else if (tls->cfg_server_ssl_ca_mem && tls->cfg_server_ssl_ca_mem_len) { |
621 | 0 | lws_filepos_t amount = 0; |
622 | 0 | const uint8_t *up; |
623 | 0 | uint8_t *up1; |
624 | |
|
625 | 0 | if (lws_tls_alloc_pem_to_der_file(vhost->context, NULL, tls->cfg_server_ssl_ca_mem, |
626 | 0 | tls->cfg_server_ssl_ca_mem_len, &up1, &amount)) { |
627 | 0 | lwsl_err("%s: Unable to decode x.509 mem\n", __func__); |
628 | 0 | } else { |
629 | 0 | up = up1; |
630 | | #if defined(USE_WOLFSSL) |
631 | | X509 *client_CA = d2i_X509(NULL, &up, (int)amount); |
632 | | #else |
633 | 0 | X509 *client_CA = d2i_X509(NULL, &up, (long)amount); |
634 | 0 | #endif |
635 | 0 | if (!client_CA) { |
636 | 0 | lwsl_err("server CA: x509 parse failed\n"); |
637 | 0 | } else { |
638 | 0 | X509_STORE *x509_store = X509_STORE_new(); |
639 | 0 | if (!X509_STORE_add_cert(x509_store, client_CA)) { |
640 | 0 | X509_STORE_free(x509_store); |
641 | 0 | lwsl_err("Unable to load SSL server certs from " |
642 | 0 | "ssl_ca_mem -- server ssl isn't going to work\n"); |
643 | 0 | } else { |
644 | 0 | SSL_CTX_set_cert_store(tls->ssl_ctx, x509_store); |
645 | 0 | STACK_OF(X509_NAME) *calist = sk_X509_NAME_new_null(); |
646 | 0 | if (calist) { |
647 | 0 | X509_NAME *name = X509_get_subject_name(client_CA); |
648 | 0 | if (name) |
649 | 0 | sk_X509_NAME_push(calist, X509_NAME_dup(name)); |
650 | 0 | SSL_CTX_set_client_CA_list(tls->ssl_ctx, calist); |
651 | 0 | } |
652 | 0 | lwsl_notice("%s: vh %s: mem CA OK\n", __func__, vhost->name); |
653 | 0 | } |
654 | 0 | X509_free(client_CA); |
655 | 0 | } |
656 | 0 | lws_free(up1); |
657 | 0 | } |
658 | 0 | } |
659 | |
|
660 | 0 | SSL_OPT_TYPE ssl_options_set_value = (SSL_OPT_TYPE) tls->ssl_options_set; |
661 | |
|
662 | 0 | if (tls->ssl_options_set) |
663 | 0 | SSL_CTX_set_options(tls->ssl_ctx, ssl_options_set_value); |
664 | |
|
665 | 0 | #if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL) |
666 | | |
667 | | /* SSL_clear_options introduced in 0.9.8m */ |
668 | 0 | SSL_OPT_TYPE ssl_options_clear_value = (SSL_OPT_TYPE) tls->ssl_options_clear; |
669 | |
|
670 | 0 | if (tls->ssl_options_clear) { |
671 | 0 | SSL_CTX_clear_options(tls->ssl_ctx, ssl_options_clear_value); |
672 | 0 | } |
673 | |
|
674 | 0 | lwsl_info(" SSL options 0x%lX\n", |
675 | 0 | (unsigned long)SSL_CTX_get_options(tls->ssl_ctx)); |
676 | 0 | #endif |
677 | |
|
678 | 0 | return 0; |
679 | 0 | } |
680 | | |
681 | | int |
682 | | lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info, |
683 | | struct lws_vhost *vhost, struct lws *wsi) |
684 | 0 | { |
685 | 0 | int n, m; |
686 | |
|
687 | 0 | if (lws_tls_vhost_backend_create_ctx(vhost)) |
688 | 0 | return 1; |
689 | | |
690 | 0 | if (!vhost->tls.use_ssl || |
691 | 0 | (!info->ssl_cert_filepath && !info->server_ssl_cert_mem)) |
692 | 0 | return 0; |
693 | | |
694 | 0 | n = (int)lws_tls_generic_cert_checks(vhost, info->ssl_cert_filepath, |
695 | 0 | info->ssl_private_key_filepath); |
696 | |
|
697 | 0 | if (n == LWS_TLS_EXTANT_NO && |
698 | 0 | (vhost->options & LWS_SERVER_OPTION_IGNORE_MISSING_CERT)) { |
699 | 0 | lwsl_notice("No certs found, continuing without SSL_CTX\n"); |
700 | 0 | SSL_CTX_free(vhost->tls.ssl_ctx); |
701 | 0 | vhost->tls.ssl_ctx = NULL; |
702 | 0 | return 0; |
703 | 0 | } |
704 | | |
705 | 0 | lws_ssl_bind_passphrase(vhost->tls.ssl_ctx, 0, info); |
706 | |
|
707 | 0 | m = lws_tls_server_certs_load(vhost, wsi, info->ssl_cert_filepath, |
708 | 0 | info->ssl_private_key_filepath, |
709 | 0 | info->server_ssl_cert_mem, |
710 | 0 | info->server_ssl_cert_mem_len, |
711 | 0 | info->server_ssl_private_key_mem, |
712 | 0 | info->server_ssl_private_key_mem_len); |
713 | |
|
714 | 0 | if (m && n != LWS_TLS_EXTANT_NO) |
715 | 0 | return 1; |
716 | | |
717 | | #if defined(LWS_ROLE_QUIC) && !defined(LWS_WITH_MBEDTLS) && !defined(LWS_WITH_WOLFSSL) && !defined(LWS_WITH_SCHANNEL) && !defined(LWS_WITH_GNUTLS) && !defined(LWS_WITH_BEARSSL) |
718 | | lws_tls_quic_vhost_init(vhost->tls.ssl_ctx); |
719 | | #endif |
720 | | |
721 | 0 | return 0; |
722 | 0 | } |
723 | | |
724 | | int |
725 | | lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd) |
726 | 0 | { |
727 | 0 | #if !defined(USE_WOLFSSL) |
728 | 0 | BIO *bio; |
729 | 0 | #endif |
730 | |
|
731 | 0 | errno = 0; |
732 | 0 | ERR_clear_error(); |
733 | 0 | wsi->tls.ctx_ref = lws_tls_ctx_ref_get(wsi->a.vhost); |
734 | 0 | wsi->tls.ssl = SSL_new(wsi->tls.ctx_ref ? wsi->tls.ctx_ref->ctx : wsi->a.vhost->tls.ssl_ctx); |
735 | 0 | if (wsi->tls.ssl == NULL) { |
736 | 0 | lwsl_err("SSL_new failed: %d (errno %d)\n", |
737 | 0 | lws_ssl_get_error(wsi, 0), errno); |
738 | |
|
739 | 0 | lws_tls_err_describe_clear(); |
740 | 0 | return 1; |
741 | 0 | } |
742 | | |
743 | 0 | SSL_set_ex_data(wsi->tls.ssl, openssl_websocket_private_data_index, wsi); |
744 | 0 | SSL_set_fd(wsi->tls.ssl, (int)(lws_intptr_t)accept_fd); |
745 | |
|
746 | | #ifdef USE_WOLFSSL |
747 | | #ifdef USE_OLD_CYASSL |
748 | | CyaSSL_set_using_nonblock(wsi->tls.ssl, 1); |
749 | | #else |
750 | | wolfSSL_set_using_nonblock(wsi->tls.ssl, 1); |
751 | | #endif |
752 | | #else |
753 | |
|
754 | 0 | SSL_set_mode(wsi->tls.ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | |
755 | 0 | SSL_MODE_RELEASE_BUFFERS); |
756 | 0 | bio = SSL_get_rbio(wsi->tls.ssl); |
757 | 0 | if (bio) |
758 | 0 | BIO_set_nbio(bio, 1); /* nonblocking */ |
759 | 0 | else |
760 | 0 | lwsl_notice("NULL rbio\n"); |
761 | 0 | bio = SSL_get_wbio(wsi->tls.ssl); |
762 | 0 | if (bio) |
763 | 0 | BIO_set_nbio(bio, 1); /* nonblocking */ |
764 | 0 | else |
765 | 0 | lwsl_notice("NULL rbio\n"); |
766 | 0 | #endif |
767 | |
|
768 | 0 | #if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) |
769 | 0 | if (wsi->a.vhost->tls.ssl_info_event_mask) |
770 | 0 | SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback); |
771 | 0 | #endif |
772 | |
|
773 | 0 | return 0; |
774 | 0 | } |
775 | | |
776 | | enum lws_ssl_capable_status |
777 | | lws_tls_server_abort_connection(struct lws *wsi) |
778 | 0 | { |
779 | 0 | if (wsi->tls.use_ssl) |
780 | 0 | SSL_shutdown(wsi->tls.ssl); |
781 | 0 | SSL_free(wsi->tls.ssl); |
782 | |
|
783 | 0 | return LWS_SSL_CAPABLE_DONE; |
784 | 0 | } |
785 | | |
786 | | #if defined(LWS_WITH_TCP_TLS) |
787 | | enum lws_ssl_capable_status |
788 | | lws_tls_server_accept(struct lws *wsi) |
789 | 0 | { |
790 | 0 | struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; |
791 | 0 | union lws_tls_cert_info_results ir; |
792 | 0 | int m, n; |
793 | |
|
794 | 0 | errno = 0; |
795 | 0 | ERR_clear_error(); |
796 | |
|
797 | | #if defined(LWS_WITH_LATENCY) |
798 | | lws_usec_t _o_ssl_acc_start = lws_now_usecs(); |
799 | | #endif |
800 | |
|
801 | 0 | n = SSL_accept(wsi->tls.ssl); |
802 | |
|
803 | | #if defined(LWS_WITH_LATENCY) |
804 | | { |
805 | | unsigned int ms = (unsigned int)((lws_now_usecs() - _o_ssl_acc_start) / 1000); |
806 | | if (ms > 2 && !wsi->tls.ssl_accept_in_bg) |
807 | | lws_latency_note(pt, _o_ssl_acc_start, 2000, "ssl_accept:%dms", ms); |
808 | | } |
809 | | #endif |
810 | |
|
811 | 0 | wsi->skip_fallback = 1; |
812 | |
|
813 | 0 | if (n == 1) { |
814 | 0 | n = lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_COMMON_NAME, &ir, |
815 | 0 | sizeof(ir.ns.name)); |
816 | 0 | if (!n) |
817 | 0 | lwsl_notice("%s: client cert CN '%s'\n", __func__, |
818 | 0 | ir.ns.name); |
819 | 0 | else |
820 | 0 | lwsl_info("%s: no client cert CN\n", __func__); |
821 | |
|
822 | 0 | lws_openssl_describe_cipher(wsi); |
823 | |
|
824 | 0 | if (SSL_pending(wsi->tls.ssl) && |
825 | 0 | lws_dll2_is_detached(&wsi->tls.dll_pending_tls)) { |
826 | 0 | if (!wsi->tls.ssl_accept_in_bg) { |
827 | 0 | lws_dll2_add_head(&wsi->tls.dll_pending_tls, |
828 | 0 | &pt->tls.dll_pending_tls_owner); |
829 | 0 | } |
830 | 0 | } |
831 | |
|
832 | 0 | return LWS_SSL_CAPABLE_DONE; |
833 | 0 | } |
834 | | |
835 | 0 | m = lws_ssl_get_error(wsi, n); |
836 | 0 | lws_tls_err_describe_clear(); |
837 | |
|
838 | 0 | if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL) |
839 | 0 | return LWS_SSL_CAPABLE_ERROR; |
840 | | |
841 | 0 | if (m == SSL_ERROR_WANT_READ || |
842 | 0 | (m != SSL_ERROR_ZERO_RETURN && SSL_want_read(wsi->tls.ssl))) { |
843 | 0 | if (!wsi->tls.ssl_accept_in_bg && lws_change_pollfd(wsi, 0, LWS_POLLIN)) { |
844 | 0 | lwsl_info("%s: WANT_READ change_pollfd failed\n", |
845 | 0 | __func__); |
846 | 0 | return LWS_SSL_CAPABLE_ERROR; |
847 | 0 | } |
848 | | |
849 | 0 | lwsl_info("SSL_ERROR_WANT_READ: m %d\n", m); |
850 | 0 | return LWS_SSL_CAPABLE_MORE_SERVICE_READ; |
851 | 0 | } |
852 | 0 | if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) { |
853 | 0 | lwsl_debug("%s: WANT_WRITE\n", __func__); |
854 | |
|
855 | 0 | if (!wsi->tls.ssl_accept_in_bg && lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { |
856 | 0 | lwsl_info("%s: WANT_WRITE change_pollfd failed\n", |
857 | 0 | __func__); |
858 | 0 | return LWS_SSL_CAPABLE_ERROR; |
859 | 0 | } |
860 | 0 | return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE; |
861 | 0 | } |
862 | | |
863 | 0 | return LWS_SSL_CAPABLE_ERROR; |
864 | 0 | } |
865 | | #endif |
866 | | |
867 | | #if defined(LWS_WITH_ACME) |
868 | | static int |
869 | | lws_tls_openssl_rsa_new_key(RSA **rsa, int bits) |
870 | | { |
871 | | BIGNUM *bn = BN_new(); |
872 | | int n; |
873 | | |
874 | | if (!bn) |
875 | | return 1; |
876 | | |
877 | | if (BN_set_word(bn, RSA_F4) != 1) { |
878 | | BN_free(bn); |
879 | | return 1; |
880 | | } |
881 | | |
882 | | *rsa = RSA_new(); |
883 | | if (!*rsa) { |
884 | | BN_free(bn); |
885 | | return 1; |
886 | | } |
887 | | |
888 | | n = RSA_generate_key_ex(*rsa, bits, bn, NULL); |
889 | | BN_free(bn); |
890 | | if (n == 1) |
891 | | return 0; |
892 | | |
893 | | RSA_free(*rsa); |
894 | | *rsa = NULL; |
895 | | |
896 | | return 1; |
897 | | } |
898 | | |
899 | | struct lws_tls_ss_pieces { |
900 | | X509 *x509; |
901 | | EVP_PKEY *pkey; |
902 | | RSA *rsa; |
903 | | }; |
904 | | |
905 | | int |
906 | | lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a, |
907 | | const char *san_b) |
908 | | { |
909 | | GENERAL_NAMES *gens = sk_GENERAL_NAME_new_null(); |
910 | | GENERAL_NAME *gen = NULL; |
911 | | ASN1_IA5STRING *ia5 = NULL; |
912 | | X509_NAME *name; |
913 | | |
914 | | if (!gens) |
915 | | return 1; |
916 | | |
917 | | vhost->tls.ss = lws_zalloc(sizeof(*vhost->tls.ss), "sni cert"); |
918 | | if (!vhost->tls.ss) { |
919 | | GENERAL_NAMES_free(gens); |
920 | | return 1; |
921 | | } |
922 | | |
923 | | vhost->tls.ss->x509 = X509_new(); |
924 | | if (!vhost->tls.ss->x509) |
925 | | goto bail; |
926 | | |
927 | | ASN1_INTEGER_set(X509_get_serialNumber(vhost->tls.ss->x509), 1); |
928 | | X509_gmtime_adj(X509_get_notBefore(vhost->tls.ss->x509), 0); |
929 | | X509_gmtime_adj(X509_get_notAfter(vhost->tls.ss->x509), 3600); |
930 | | |
931 | | vhost->tls.ss->pkey = EVP_PKEY_new(); |
932 | | if (!vhost->tls.ss->pkey) |
933 | | goto bail0; |
934 | | |
935 | | if (lws_tls_openssl_rsa_new_key(&vhost->tls.ss->rsa, 4096)) |
936 | | goto bail1; |
937 | | |
938 | | if (!EVP_PKEY_assign_RSA(vhost->tls.ss->pkey, vhost->tls.ss->rsa)) |
939 | | goto bail2; |
940 | | |
941 | | X509_set_pubkey(vhost->tls.ss->x509, vhost->tls.ss->pkey); |
942 | | |
943 | | name = X509_get_subject_name(vhost->tls.ss->x509); |
944 | | X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, |
945 | | (unsigned char *)"GB", -1, -1, 0); |
946 | | X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, |
947 | | (unsigned char *)"somecompany", -1, -1, 0); |
948 | | if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_UTF8, |
949 | | (unsigned char *)"temp.acme.invalid", |
950 | | -1, -1, 0) != 1) { |
951 | | lwsl_notice("failed to add CN\n"); |
952 | | goto bail2; |
953 | | } |
954 | | X509_set_issuer_name(vhost->tls.ss->x509, name); |
955 | | |
956 | | /* add the SAN payloads */ |
957 | | |
958 | | gen = GENERAL_NAME_new(); |
959 | | ia5 = ASN1_IA5STRING_new(); |
960 | | if (!ASN1_STRING_set(ia5, san_a, -1)) { |
961 | | lwsl_notice("failed to set ia5\n"); |
962 | | GENERAL_NAME_free(gen); |
963 | | goto bail2; |
964 | | } |
965 | | GENERAL_NAME_set0_value(gen, GEN_DNS, ia5); |
966 | | sk_GENERAL_NAME_push(gens, gen); |
967 | | |
968 | | if (X509_add1_ext_i2d(vhost->tls.ss->x509, NID_subject_alt_name, |
969 | | gens, 0, X509V3_ADD_APPEND) != 1) |
970 | | goto bail2; |
971 | | |
972 | | GENERAL_NAMES_free(gens); |
973 | | |
974 | | if (san_b && san_b[0]) { |
975 | | gens = sk_GENERAL_NAME_new_null(); |
976 | | gen = GENERAL_NAME_new(); |
977 | | ia5 = ASN1_IA5STRING_new(); |
978 | | if (!ASN1_STRING_set(ia5, san_a, -1)) { |
979 | | lwsl_notice("failed to set ia5\n"); |
980 | | GENERAL_NAME_free(gen); |
981 | | goto bail2; |
982 | | } |
983 | | GENERAL_NAME_set0_value(gen, GEN_DNS, ia5); |
984 | | sk_GENERAL_NAME_push(gens, gen); |
985 | | |
986 | | if (X509_add1_ext_i2d(vhost->tls.ss->x509, NID_subject_alt_name, |
987 | | gens, 0, X509V3_ADD_APPEND) != 1) |
988 | | goto bail2; |
989 | | |
990 | | GENERAL_NAMES_free(gens); |
991 | | } |
992 | | |
993 | | /* sign it with our private key */ |
994 | | if (!X509_sign(vhost->tls.ss->x509, vhost->tls.ss->pkey, EVP_sha256())) |
995 | | goto bail2; |
996 | | |
997 | | #if 0 |
998 | | {/* useful to take a sample of a working cert for mbedtls to crib */ |
999 | | FILE *fp = fopen("/tmp/acme-temp-cert", "w+"); |
1000 | | |
1001 | | i2d_X509_fp(fp, vhost->tls.ss->x509); |
1002 | | fclose(fp); |
1003 | | } |
1004 | | #endif |
1005 | | |
1006 | | /* tell the vhost to use our crafted certificate */ |
1007 | | SSL_CTX_use_certificate(vhost->tls.ssl_ctx, vhost->tls.ss->x509); |
1008 | | /* and to use our generated private key */ |
1009 | | SSL_CTX_use_PrivateKey(vhost->tls.ssl_ctx, vhost->tls.ss->pkey); |
1010 | | |
1011 | | return 0; |
1012 | | |
1013 | | bail2: |
1014 | | RSA_free(vhost->tls.ss->rsa); |
1015 | | bail1: |
1016 | | EVP_PKEY_free(vhost->tls.ss->pkey); |
1017 | | bail0: |
1018 | | X509_free(vhost->tls.ss->x509); |
1019 | | bail: |
1020 | | lws_free(vhost->tls.ss); |
1021 | | GENERAL_NAMES_free(gens); |
1022 | | |
1023 | | return 1; |
1024 | | } |
1025 | | |
1026 | | void |
1027 | | lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost) |
1028 | | { |
1029 | | if (!vhost->tls.ss) |
1030 | | return; |
1031 | | |
1032 | | EVP_PKEY_free(vhost->tls.ss->pkey); |
1033 | | X509_free(vhost->tls.ss->x509); |
1034 | | lws_free_set_NULL(vhost->tls.ss); |
1035 | | } |
1036 | | |
1037 | | static int |
1038 | | lws_tls_openssl_add_nid(X509_NAME *name, int nid, const char *value) |
1039 | | { |
1040 | | X509_NAME_ENTRY *e; |
1041 | | int n; |
1042 | | |
1043 | | if (!value || value[0] == '\0') |
1044 | | value = "none"; |
1045 | | |
1046 | | e = X509_NAME_ENTRY_create_by_NID(NULL, nid, MBSTRING_ASC, |
1047 | | (const unsigned char *)value, -1); |
1048 | | if (!e) |
1049 | | return 1; |
1050 | | n = X509_NAME_add_entry(name, e, -1, 0); |
1051 | | X509_NAME_ENTRY_free(e); |
1052 | | |
1053 | | return n != 1; |
1054 | | } |
1055 | | |
1056 | | static int nid_list[] = { |
1057 | | NID_countryName, /* LWS_TLS_REQ_ELEMENT_COUNTRY */ |
1058 | | NID_stateOrProvinceName, /* LWS_TLS_REQ_ELEMENT_STATE */ |
1059 | | NID_localityName, /* LWS_TLS_REQ_ELEMENT_LOCALITY */ |
1060 | | NID_organizationName, /* LWS_TLS_REQ_ELEMENT_ORGANIZATION */ |
1061 | | NID_commonName, /* LWS_TLS_REQ_ELEMENT_COMMON_NAME */ |
1062 | | NID_subject_alt_name, /* LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME */ |
1063 | | NID_pkcs9_emailAddress, /* LWS_TLS_REQ_ELEMENT_EMAIL */ |
1064 | | }; |
1065 | | |
1066 | | int |
1067 | | lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[], |
1068 | | uint8_t *csr, size_t csr_len, char **privkey_pem, |
1069 | | size_t *privkey_len) |
1070 | | { |
1071 | | uint8_t *csr_in = csr; |
1072 | | RSA *rsakey; |
1073 | | X509_REQ *req; |
1074 | | X509_NAME *subj; |
1075 | | EVP_PKEY *pkey; |
1076 | | char *p, *end; |
1077 | | BIO *bio; |
1078 | | long bio_len; |
1079 | | int n, ret = -1; |
1080 | | |
1081 | | if (lws_tls_openssl_rsa_new_key(&rsakey, 4096)) |
1082 | | return -1; |
1083 | | |
1084 | | pkey = EVP_PKEY_new(); |
1085 | | if (!pkey) |
1086 | | goto bail0; |
1087 | | if (!EVP_PKEY_set1_RSA(pkey, rsakey)) |
1088 | | goto bail1; |
1089 | | |
1090 | | req = X509_REQ_new(); |
1091 | | if (!req) |
1092 | | goto bail1; |
1093 | | |
1094 | | X509_REQ_set_pubkey(req, pkey); |
1095 | | |
1096 | | subj = X509_NAME_new(); |
1097 | | if (!subj) |
1098 | | goto bail2; |
1099 | | |
1100 | | for (n = 0; n < LWS_TLS_REQ_ELEMENT_COUNT; n++) |
1101 | | if (elements[n] && |
1102 | | lws_tls_openssl_add_nid(subj, nid_list[n], |
1103 | | elements[n])) { |
1104 | | lwsl_notice("%s: failed to add element %d\n", |
1105 | | __func__, n); |
1106 | | goto bail3; |
1107 | | } |
1108 | | |
1109 | | if (X509_REQ_set_subject_name(req, subj) != 1) |
1110 | | goto bail3; |
1111 | | |
1112 | | if (elements[LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME]) { |
1113 | | STACK_OF(X509_EXTENSION) *exts; |
1114 | | X509_EXTENSION *ext; |
1115 | | char san[256]; |
1116 | | |
1117 | | exts = sk_X509_EXTENSION_new_null(); |
1118 | | if (!exts) |
1119 | | goto bail3; |
1120 | | |
1121 | | lws_snprintf(san, sizeof(san), "DNS:%s,DNS:%s", |
1122 | | elements[LWS_TLS_REQ_ELEMENT_COMMON_NAME], |
1123 | | elements[LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME]); |
1124 | | |
1125 | | ext = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, |
1126 | | san); |
1127 | | if (!ext) { |
1128 | | sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); |
1129 | | goto bail3; |
1130 | | } |
1131 | | sk_X509_EXTENSION_push(exts, ext); |
1132 | | |
1133 | | if (!X509_REQ_add_extensions(req, exts)) { |
1134 | | sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); |
1135 | | goto bail3; |
1136 | | } |
1137 | | sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); |
1138 | | } |
1139 | | |
1140 | | if (!X509_REQ_sign(req, pkey, EVP_sha256())) |
1141 | | goto bail3; |
1142 | | |
1143 | | /* |
1144 | | * issue the CSR as PEM to a BIO, and translate to b64urlenc without |
1145 | | * headers, trailers, or whitespace |
1146 | | */ |
1147 | | |
1148 | | bio = BIO_new(BIO_s_mem()); |
1149 | | if (!bio) |
1150 | | goto bail3; |
1151 | | |
1152 | | if (PEM_write_bio_X509_REQ(bio, req) != 1) { |
1153 | | BIO_free(bio); |
1154 | | goto bail3; |
1155 | | } |
1156 | | |
1157 | | bio_len = BIO_get_mem_data(bio, &p); |
1158 | | end = p + bio_len; |
1159 | | |
1160 | | /* strip the header line */ |
1161 | | while (p < end && *p != '\n') |
1162 | | p++; |
1163 | | |
1164 | | while (p < end && csr_len) { |
1165 | | if (*p == '\n') { |
1166 | | p++; |
1167 | | continue; |
1168 | | } |
1169 | | |
1170 | | if (*p == '-') |
1171 | | break; |
1172 | | |
1173 | | if (*p == '+') |
1174 | | *csr++ = '-'; |
1175 | | else |
1176 | | if (*p == '/') |
1177 | | *csr++ = '_'; |
1178 | | else |
1179 | | *csr++ = (uint8_t)*p; |
1180 | | p++; |
1181 | | csr_len--; |
1182 | | } |
1183 | | BIO_free(bio); |
1184 | | if (!csr_len) { |
1185 | | lwsl_notice("%s: need %ld for CSR\n", __func__, bio_len); |
1186 | | goto bail3; |
1187 | | } |
1188 | | |
1189 | | /* |
1190 | | * Also return the private key as a PEM in memory |
1191 | | * (platform may not have a filesystem) |
1192 | | */ |
1193 | | bio = BIO_new(BIO_s_mem()); |
1194 | | if (!bio) |
1195 | | goto bail3; |
1196 | | |
1197 | | if (PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, 0, NULL) != 1) { |
1198 | | BIO_free(bio); |
1199 | | goto bail3; |
1200 | | } |
1201 | | bio_len = BIO_get_mem_data(bio, &p); |
1202 | | *privkey_pem = malloc((unsigned long)bio_len); /* malloc so user code can own / free */ |
1203 | | *privkey_len = (size_t)bio_len; |
1204 | | if (!*privkey_pem) { |
1205 | | lwsl_notice("%s: need %ld for private key\n", __func__, |
1206 | | bio_len); |
1207 | | BIO_free(bio); |
1208 | | goto bail3; |
1209 | | } |
1210 | | memcpy(*privkey_pem, p, (unsigned int)(int)(long long)bio_len); |
1211 | | BIO_free(bio); |
1212 | | |
1213 | | ret = lws_ptr_diff(csr, csr_in); |
1214 | | |
1215 | | bail3: |
1216 | | X509_NAME_free(subj); |
1217 | | bail2: |
1218 | | X509_REQ_free(req); |
1219 | | bail1: |
1220 | | EVP_PKEY_free(pkey); |
1221 | | bail0: |
1222 | | RSA_free(rsakey); |
1223 | | |
1224 | | return ret; |
1225 | | } |
1226 | | |
1227 | | int |
1228 | | lws_tls_acme_sni_csr_create_ecdsa(struct lws_context *context, const char *elements[], |
1229 | | uint8_t *csr, size_t csr_len, char **privkey_pem, |
1230 | | size_t *privkey_len) |
1231 | | { |
1232 | | #if defined(OPENSSL_NO_EC) |
1233 | | return -1; |
1234 | | #else |
1235 | | uint8_t *csr_in = csr; |
1236 | | EC_KEY *eckey = NULL; |
1237 | | X509_REQ *req; |
1238 | | X509_NAME *subj; |
1239 | | EVP_PKEY *pkey; |
1240 | | char *p, *end; |
1241 | | BIO *bio; |
1242 | | long bio_len; |
1243 | | int n, ret = -1; |
1244 | | |
1245 | | eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); |
1246 | | if (!eckey) |
1247 | | return -1; |
1248 | | |
1249 | | if (!EC_KEY_generate_key(eckey)) { |
1250 | | EC_KEY_free(eckey); |
1251 | | return -1; |
1252 | | } |
1253 | | |
1254 | | pkey = EVP_PKEY_new(); |
1255 | | if (!pkey) |
1256 | | goto bail0; |
1257 | | if (!EVP_PKEY_assign_EC_KEY(pkey, eckey)) |
1258 | | goto bail1; |
1259 | | |
1260 | | req = X509_REQ_new(); |
1261 | | if (!req) |
1262 | | goto bail1; |
1263 | | |
1264 | | X509_REQ_set_pubkey(req, pkey); |
1265 | | |
1266 | | subj = X509_NAME_new(); |
1267 | | if (!subj) |
1268 | | goto bail2; |
1269 | | |
1270 | | for (n = 0; n < LWS_TLS_REQ_ELEMENT_COUNT; n++) |
1271 | | if (elements[n] && |
1272 | | lws_tls_openssl_add_nid(subj, nid_list[n], |
1273 | | elements[n])) { |
1274 | | lwsl_notice("%s: failed to add element %d\n", |
1275 | | __func__, n); |
1276 | | goto bail3; |
1277 | | } |
1278 | | |
1279 | | if (X509_REQ_set_subject_name(req, subj) != 1) |
1280 | | goto bail3; |
1281 | | |
1282 | | if (elements[LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME]) { |
1283 | | STACK_OF(X509_EXTENSION) *exts; |
1284 | | X509_EXTENSION *ext; |
1285 | | char san[256]; |
1286 | | |
1287 | | exts = sk_X509_EXTENSION_new_null(); |
1288 | | if (!exts) |
1289 | | goto bail3; |
1290 | | |
1291 | | lws_snprintf(san, sizeof(san), "DNS:%s,DNS:%s", |
1292 | | elements[LWS_TLS_REQ_ELEMENT_COMMON_NAME], |
1293 | | elements[LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME]); |
1294 | | |
1295 | | ext = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, |
1296 | | san); |
1297 | | if (!ext) { |
1298 | | sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); |
1299 | | goto bail3; |
1300 | | } |
1301 | | sk_X509_EXTENSION_push(exts, ext); |
1302 | | |
1303 | | if (!X509_REQ_add_extensions(req, exts)) { |
1304 | | sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); |
1305 | | goto bail3; |
1306 | | } |
1307 | | sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); |
1308 | | } |
1309 | | |
1310 | | if (!X509_REQ_sign(req, pkey, EVP_sha256())) |
1311 | | goto bail3; |
1312 | | |
1313 | | /* |
1314 | | * issue the CSR as PEM to a BIO, and translate to b64urlenc without |
1315 | | * headers, trailers, or whitespace |
1316 | | */ |
1317 | | |
1318 | | bio = BIO_new(BIO_s_mem()); |
1319 | | if (!bio) |
1320 | | goto bail3; |
1321 | | |
1322 | | if (PEM_write_bio_X509_REQ(bio, req) != 1) { |
1323 | | BIO_free(bio); |
1324 | | goto bail3; |
1325 | | } |
1326 | | |
1327 | | bio_len = BIO_get_mem_data(bio, &p); |
1328 | | end = p + bio_len; |
1329 | | |
1330 | | /* strip the header line */ |
1331 | | while (p < end && *p != '\n') |
1332 | | p++; |
1333 | | |
1334 | | while (p < end && csr_len) { |
1335 | | if (*p == '\n') { |
1336 | | p++; |
1337 | | continue; |
1338 | | } |
1339 | | |
1340 | | if (*p == '-') |
1341 | | break; |
1342 | | |
1343 | | if (*p == '+') |
1344 | | *csr++ = '-'; |
1345 | | else |
1346 | | if (*p == '/') |
1347 | | *csr++ = '_'; |
1348 | | else |
1349 | | *csr++ = (uint8_t)*p; |
1350 | | p++; |
1351 | | csr_len--; |
1352 | | } |
1353 | | BIO_free(bio); |
1354 | | if (!csr_len) { |
1355 | | lwsl_notice("%s: need %ld for CSR\n", __func__, bio_len); |
1356 | | goto bail3; |
1357 | | } |
1358 | | |
1359 | | /* |
1360 | | * Also return the private key as a PEM in memory |
1361 | | * (platform may not have a filesystem) |
1362 | | */ |
1363 | | bio = BIO_new(BIO_s_mem()); |
1364 | | if (!bio) |
1365 | | goto bail3; |
1366 | | |
1367 | | if (PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, 0, NULL) != 1) { |
1368 | | BIO_free(bio); |
1369 | | goto bail3; |
1370 | | } |
1371 | | bio_len = BIO_get_mem_data(bio, &p); |
1372 | | *privkey_pem = malloc((unsigned long)bio_len); /* malloc so user code can own / free */ |
1373 | | *privkey_len = (size_t)bio_len; |
1374 | | if (!*privkey_pem) { |
1375 | | lwsl_notice("%s: need %ld for private key\n", __func__, |
1376 | | bio_len); |
1377 | | BIO_free(bio); |
1378 | | goto bail3; |
1379 | | } |
1380 | | memcpy(*privkey_pem, p, (unsigned int)(int)(long long)bio_len); |
1381 | | BIO_free(bio); |
1382 | | |
1383 | | ret = lws_ptr_diff(csr, csr_in); |
1384 | | |
1385 | | bail3: |
1386 | | X509_NAME_free(subj); |
1387 | | bail2: |
1388 | | X509_REQ_free(req); |
1389 | | bail1: |
1390 | | EVP_PKEY_free(pkey); |
1391 | | return ret; |
1392 | | bail0: |
1393 | | if (eckey) |
1394 | | EC_KEY_free(eckey); |
1395 | | return -1; |
1396 | | #endif |
1397 | | } |
1398 | | #endif |