/src/libwebsockets/lib/tls/openssl/openssl-server.c
Line | Count | Source (jump to first uncovered line) |
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 | | /* select the ssl ctx from the selected vhost for this conn */ |
145 | 0 | SSL_set_SSL_CTX(ssl, vhost->tls.ssl_ctx); |
146 | |
|
147 | 0 | return SSL_TLSEXT_ERR_OK; |
148 | 0 | } |
149 | | #endif |
150 | | |
151 | | /* |
152 | | * this may now get called after the vhost creation, when certs become |
153 | | * available. |
154 | | */ |
155 | | int |
156 | | lws_tls_server_certs_load(struct lws_vhost *vhost, struct lws *wsi, |
157 | | const char *cert, const char *private_key, |
158 | | const char *mem_cert, size_t mem_cert_len, |
159 | | const char *mem_privkey, size_t mem_privkey_len) |
160 | 0 | { |
161 | 0 | #if !defined(OPENSSL_NO_EC) && defined(LWS_HAVE_EC_KEY_new_by_curve_name) && \ |
162 | 0 | ((OPENSSL_VERSION_NUMBER < 0x30000000l) || \ |
163 | 0 | defined(LWS_SUPPRESS_DEPRECATED_API_WARNINGS)) |
164 | 0 | const char *ecdh_curve = "prime256v1"; |
165 | 0 | #if !defined(LWS_WITH_BORINGSSL) && !defined(LWS_WITH_AWSLC) && defined(LWS_HAVE_SSL_EXTRA_CHAIN_CERTS) |
166 | 0 | STACK_OF(X509) *extra_certs = NULL; |
167 | 0 | #endif |
168 | 0 | EC_KEY *ecdh, *EC_key = NULL; |
169 | 0 | EVP_PKEY *pkey; |
170 | 0 | X509 *x = NULL; |
171 | 0 | int ecdh_nid; |
172 | 0 | int KeyType; |
173 | 0 | #endif |
174 | 0 | unsigned long error; |
175 | 0 | lws_filepos_t flen; |
176 | 0 | uint8_t *p; |
177 | 0 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
178 | 0 | int ret; |
179 | 0 | #endif |
180 | 0 | int n = (int)lws_tls_generic_cert_checks(vhost, cert, private_key), m; |
181 | |
|
182 | 0 | if (!cert && !private_key) |
183 | 0 | n = LWS_TLS_EXTANT_ALTERNATIVE; |
184 | |
|
185 | 0 | if (n == LWS_TLS_EXTANT_NO && (!mem_cert || !mem_privkey)) |
186 | 0 | return 0; |
187 | 0 | if (n == LWS_TLS_EXTANT_NO) |
188 | 0 | n = LWS_TLS_EXTANT_ALTERNATIVE; |
189 | |
|
190 | 0 | if (n == LWS_TLS_EXTANT_ALTERNATIVE && (!mem_cert || !mem_privkey)) |
191 | 0 | return 1; /* no alternative */ |
192 | | |
193 | 0 | if (n == LWS_TLS_EXTANT_ALTERNATIVE) { |
194 | |
|
195 | 0 | #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
196 | | |
197 | | /* |
198 | | * Although we have prepared update certs, we no longer have |
199 | | * the rights to read our own cert + key we saved. |
200 | | * |
201 | | * If we were passed copies in memory buffers, use those |
202 | | * in favour of the filepaths we normally want. |
203 | | */ |
204 | 0 | cert = NULL; |
205 | 0 | private_key = NULL; |
206 | 0 | } |
207 | | |
208 | | /* |
209 | | * use the multi-cert interface for backwards compatibility in the |
210 | | * both simple files case |
211 | | */ |
212 | |
|
213 | 0 | if (n != LWS_TLS_EXTANT_ALTERNATIVE && cert) { |
214 | | |
215 | | /* set the local certificate from CertFile */ |
216 | 0 | m = SSL_CTX_use_certificate_chain_file(vhost->tls.ssl_ctx, cert); |
217 | 0 | if (m != 1) { |
218 | 0 | const char *s; |
219 | 0 | error = ERR_peek_error(); |
220 | 0 | s = ERR_error_string(ERR_get_error(), (char *)vhost->context->pt[0].serv_buf); |
221 | |
|
222 | 0 | lwsl_err("problem getting cert '%s' %lu: %s\n", |
223 | 0 | cert, error, s); |
224 | |
|
225 | 0 | return 1; |
226 | 0 | } |
227 | | |
228 | 0 | if (!private_key) { |
229 | 0 | lwsl_err("ssl private key not set\n"); |
230 | 0 | return 1; |
231 | 0 | } else { |
232 | | /* set the private key from KeyFile */ |
233 | 0 | if (SSL_CTX_use_PrivateKey_file(vhost->tls.ssl_ctx, private_key, |
234 | 0 | SSL_FILETYPE_PEM) != 1) { |
235 | 0 | const char *s; |
236 | 0 | error = ERR_peek_error(); |
237 | 0 | s = ERR_error_string(ERR_get_error(), (char *)vhost->context->pt[0].serv_buf); |
238 | 0 | lwsl_err("ssl problem getting key '%s' %lu: %s\n", |
239 | 0 | private_key, error, s); |
240 | 0 | return 1; |
241 | 0 | } |
242 | 0 | } |
243 | | |
244 | 0 | return 0; |
245 | 0 | } |
246 | | |
247 | | /* otherwise allow for DER or PEM, file or memory image */ |
248 | | |
249 | 0 | if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, mem_cert, |
250 | 0 | mem_cert_len, &p, &flen)) { |
251 | 0 | lwsl_err("%s: couldn't read cert file\n", __func__); |
252 | |
|
253 | 0 | return 1; |
254 | 0 | } |
255 | | |
256 | 0 | #if !defined(USE_WOLFSSL) |
257 | 0 | ret = SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, SSL_SIZE_CAST(flen), p); |
258 | | #else |
259 | | ret = wolfSSL_CTX_use_certificate_buffer(vhost->tls.ssl_ctx, |
260 | | (uint8_t *)p, (int)flen, |
261 | | WOLFSSL_FILETYPE_ASN1); |
262 | | #endif |
263 | 0 | lws_free_set_NULL(p); |
264 | 0 | if (ret != 1) { |
265 | 0 | lwsl_err("%s: Problem loading cert\n", __func__); |
266 | |
|
267 | 0 | return 1; |
268 | 0 | } |
269 | | |
270 | 0 | if (lws_tls_alloc_pem_to_der_file(vhost->context, private_key, |
271 | 0 | mem_privkey, mem_privkey_len, |
272 | 0 | &p, &flen)) { |
273 | 0 | lwsl_notice("unable to convert memory privkey\n"); |
274 | |
|
275 | 0 | return 1; |
276 | 0 | } |
277 | | |
278 | 0 | #if !defined(USE_WOLFSSL) |
279 | 0 | ret = SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, vhost->tls.ssl_ctx, p, |
280 | | #if defined(LWS_WITH_BORINGSSL) || defined(LWS_WITH_AWSLC) |
281 | | (size_t) |
282 | | #else |
283 | 0 | (long)(long long) |
284 | 0 | #endif |
285 | 0 | flen); |
286 | 0 | if (ret != 1) { |
287 | 0 | ret = SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_EC, |
288 | 0 | vhost->tls.ssl_ctx, p, |
289 | | #if defined(LWS_WITH_BORINGSSL) || defined(LWS_WITH_AWSLC) |
290 | | (size_t) |
291 | | #else |
292 | 0 | (long)(long long) |
293 | 0 | #endif |
294 | 0 | flen); |
295 | 0 | } |
296 | | #else |
297 | | ret = wolfSSL_CTX_use_PrivateKey_buffer(vhost->tls.ssl_ctx, p, (long) flen, |
298 | | WOLFSSL_FILETYPE_ASN1); |
299 | | #endif |
300 | 0 | lws_free_set_NULL(p); |
301 | 0 | if (ret != 1) { |
302 | 0 | lwsl_notice("unable to use memory privkey\n"); |
303 | |
|
304 | 0 | return 1; |
305 | 0 | } |
306 | | |
307 | | #else |
308 | | /* |
309 | | * Although we have prepared update certs, we no longer have |
310 | | * the rights to read our own cert + key we saved. |
311 | | * |
312 | | * If we were passed copies in memory buffers, use those |
313 | | * instead. |
314 | | * |
315 | | * The passed memory-buffer cert image is in DER, and the |
316 | | * memory-buffer private key image is PEM. |
317 | | */ |
318 | | if (lws_tls_alloc_pem_to_der_file(vhost->context, cert, mem_cert, |
319 | | mem_cert_len, &p, &flen)) { |
320 | | lwsl_err("%s: couldn't convert pem to der\n", __func__); |
321 | | return 1; |
322 | | } |
323 | | #ifndef USE_WOLFSSL |
324 | | if (SSL_CTX_use_certificate_ASN1(vhost->tls.ssl_ctx, |
325 | | (int)flen, |
326 | | (uint8_t *)p) != 1) { |
327 | | #else |
328 | | if (wolfSSL_CTX_use_certificate_buffer(vhost->tls.ssl_ctx, |
329 | | (uint8_t *)p, |
330 | | (int)flen, |
331 | | WOLFSSL_FILETYPE_ASN1) != 1) { |
332 | | |
333 | | #endif |
334 | | lwsl_err("Problem loading update cert\n"); |
335 | | |
336 | | return 1; |
337 | | } |
338 | | |
339 | | if (lws_tls_alloc_pem_to_der_file(vhost->context, NULL, |
340 | | mem_privkey, mem_privkey_len, |
341 | | &p, &flen)) { |
342 | | lwsl_notice("unable to convert memory privkey\n"); |
343 | | |
344 | | return 1; |
345 | | } |
346 | | #ifndef USE_WOLFSSL |
347 | | if (SSL_CTX_use_PrivateKey_ASN1(EVP_PKEY_RSA, |
348 | | vhost->tls.ssl_ctx, p, |
349 | | (long)(long long)flen) != 1) { |
350 | | #else |
351 | | if (wolfSSL_CTX_use_PrivateKey_buffer(vhost->tls.ssl_ctx, p, |
352 | | (long)flen, WOLFSSL_FILETYPE_ASN1) != 1) { |
353 | | #endif |
354 | | lwsl_notice("unable to use memory privkey\n"); |
355 | | |
356 | | return 1; |
357 | | } |
358 | | |
359 | | goto check_key; |
360 | | } |
361 | | |
362 | | /* set the local certificate from CertFile */ |
363 | | m = SSL_CTX_use_certificate_chain_file(vhost->tls.ssl_ctx, cert); |
364 | | if (m != 1) { |
365 | | error = ERR_get_error(); |
366 | | lwsl_err("problem getting cert '%s' %lu: %s\n", |
367 | | cert, error, ERR_error_string(error, |
368 | | (char *)vhost->context->pt[0].serv_buf)); |
369 | | |
370 | | return 1; |
371 | | } |
372 | | |
373 | | if (n == LWS_TLS_EXTANT_ALTERNATIVE || !private_key) { |
374 | | lwsl_err("ssl private key not set\n"); |
375 | | return 1; |
376 | | } else { |
377 | | /* set the private key from KeyFile */ |
378 | | if (SSL_CTX_use_PrivateKey_file(vhost->tls.ssl_ctx, private_key, |
379 | | SSL_FILETYPE_PEM) != 1) { |
380 | | error = ERR_get_error(); |
381 | | lwsl_err("ssl problem getting key '%s' %lu: %s\n", |
382 | | private_key, error, |
383 | | ERR_error_string(error, |
384 | | (char *)vhost->context->pt[0].serv_buf)); |
385 | | return 1; |
386 | | } |
387 | | } |
388 | | |
389 | | check_key: |
390 | | #endif |
391 | | |
392 | | /* verify private key */ |
393 | 0 | if (!SSL_CTX_check_private_key(vhost->tls.ssl_ctx)) { |
394 | 0 | lwsl_err("Private SSL key doesn't match cert\n"); |
395 | |
|
396 | 0 | return 1; |
397 | 0 | } |
398 | | |
399 | | |
400 | 0 | #if !defined(OPENSSL_NO_EC) && defined(LWS_HAVE_EC_KEY_new_by_curve_name) && \ |
401 | 0 | ((OPENSSL_VERSION_NUMBER < 0x30000000l) || \ |
402 | 0 | defined(LWS_SUPPRESS_DEPRECATED_API_WARNINGS)) |
403 | 0 | if (vhost->tls.ecdh_curve[0]) |
404 | 0 | ecdh_curve = vhost->tls.ecdh_curve; |
405 | |
|
406 | 0 | ecdh_nid = OBJ_sn2nid(ecdh_curve); |
407 | 0 | if (NID_undef == ecdh_nid) { |
408 | 0 | lwsl_err("SSL: Unknown curve name '%s'", ecdh_curve); |
409 | 0 | return 1; |
410 | 0 | } |
411 | | |
412 | 0 | ecdh = EC_KEY_new_by_curve_name(ecdh_nid); |
413 | 0 | if (NULL == ecdh) { |
414 | 0 | lwsl_err("SSL: Unable to create curve '%s'", ecdh_curve); |
415 | 0 | return 1; |
416 | 0 | } |
417 | 0 | SSL_CTX_set_tmp_ecdh(vhost->tls.ssl_ctx, ecdh); |
418 | 0 | EC_KEY_free(ecdh); |
419 | |
|
420 | 0 | SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_SINGLE_ECDH_USE); |
421 | |
|
422 | 0 | lwsl_notice(" SSL ECDH curve '%s'\n", ecdh_curve); |
423 | |
|
424 | 0 | if (lws_check_opt(vhost->context->options, LWS_SERVER_OPTION_SSL_ECDH)) |
425 | 0 | lwsl_notice(" Using ECDH certificate support\n"); |
426 | | |
427 | | /* Get X509 certificate from ssl context */ |
428 | 0 | #if !defined(LWS_WITH_BORINGSSL) && !defined(LWS_WITH_AWSLC) && !defined(USE_WOLFSSL) |
429 | | #if !defined(LWS_HAVE_SSL_EXTRA_CHAIN_CERTS) |
430 | | x = sk_X509_value(vhost->tls.ssl_ctx->extra_certs, 0); |
431 | | #else |
432 | 0 | SSL_CTX_get_extra_chain_certs_only(vhost->tls.ssl_ctx, &extra_certs); |
433 | 0 | if (extra_certs) |
434 | 0 | x = sk_X509_value(extra_certs, 0); |
435 | 0 | else |
436 | 0 | lwsl_info("%s: no extra certs\n", __func__); |
437 | 0 | #endif |
438 | 0 | if (!x) { |
439 | | //lwsl_err("%s: x is NULL\n", __func__); |
440 | 0 | goto post_ecdh; |
441 | 0 | } |
442 | | #else |
443 | | return 0; |
444 | | #endif /* !boringssl */ |
445 | | |
446 | | /* Get the public key from certificate */ |
447 | 0 | pkey = X509_get_pubkey(x); |
448 | 0 | if (!pkey) { |
449 | 0 | lwsl_err("%s: pkey is NULL\n", __func__); |
450 | |
|
451 | 0 | return 1; |
452 | 0 | } |
453 | | /* Get the key type */ |
454 | 0 | KeyType = EVP_PKEY_type(EVP_PKEY_id(pkey)); |
455 | |
|
456 | 0 | if (EVP_PKEY_EC != KeyType) { |
457 | 0 | lwsl_notice("Key type is not EC\n"); |
458 | 0 | return 0; |
459 | 0 | } |
460 | | /* Get the key */ |
461 | 0 | EC_key = EVP_PKEY_get1_EC_KEY(pkey); |
462 | | /* Set ECDH parameter */ |
463 | 0 | if (!EC_key) { |
464 | 0 | lwsl_err("%s: ECDH key is NULL \n", __func__); |
465 | 0 | return 1; |
466 | 0 | } |
467 | 0 | SSL_CTX_set_tmp_ecdh(vhost->tls.ssl_ctx, EC_key); |
468 | |
|
469 | 0 | EC_KEY_free(EC_key); |
470 | |
|
471 | 0 | #if !defined(OPENSSL_NO_EC) && !defined(LWS_WITH_BORINGSSL) && !defined(LWS_WITH_AWSLC) && !defined(USE_WOLFSSL) |
472 | 0 | post_ecdh: |
473 | 0 | #endif |
474 | 0 | vhost->tls.skipped_certs = 0; |
475 | | #else |
476 | | lwsl_notice(" OpenSSL doesn't support ECDH\n"); |
477 | | #endif |
478 | |
|
479 | 0 | return 0; |
480 | 0 | } |
481 | | |
482 | | int |
483 | | lws_tls_server_vhost_backend_init(const struct lws_context_creation_info *info, |
484 | | struct lws_vhost *vhost, struct lws *wsi) |
485 | 0 | { |
486 | 0 | unsigned long error; |
487 | 0 | SSL_METHOD *method = (SSL_METHOD *)SSLv23_server_method(); |
488 | |
|
489 | 0 | if (!method) { |
490 | 0 | const char *s; |
491 | 0 | error = ERR_peek_error(); |
492 | 0 | s = ERR_error_string(ERR_get_error(), (char *)vhost->context->pt[0].serv_buf); |
493 | |
|
494 | 0 | lwsl_err("problem creating ssl method %lu: %s\n", |
495 | 0 | error, s); |
496 | 0 | return 1; |
497 | 0 | } |
498 | 0 | vhost->tls.ssl_ctx = SSL_CTX_new(method); /* create context */ |
499 | 0 | if (!vhost->tls.ssl_ctx) { |
500 | 0 | const char *s; |
501 | |
|
502 | 0 | error = ERR_peek_error(); |
503 | 0 | s = ERR_error_string(ERR_get_error(), (char *)vhost->context->pt[0].serv_buf); |
504 | 0 | lwsl_err("problem creating ssl context %lu: %s\n", |
505 | 0 | error, s); |
506 | 0 | return 1; |
507 | 0 | } |
508 | | /* Added for sniffing packets on hub side */ |
509 | 0 | #if defined(LWS_HAVE_SSL_CTX_set_keylog_callback) && \ |
510 | 0 | defined(LWS_WITH_TLS) && (!defined(LWS_WITHOUT_CLIENT) || !defined(LWS_WITHOUT_SERVER)) |
511 | 0 | SSL_CTX_set_keylog_callback(vhost->tls.ssl_ctx, lws_klog_dump); |
512 | 0 | #endif |
513 | |
|
514 | 0 | SSL_CTX_set_ex_data(vhost->tls.ssl_ctx, |
515 | 0 | openssl_SSL_CTX_private_data_index, |
516 | 0 | (char *)vhost->context); |
517 | | /* Disable SSLv2 and SSLv3 */ |
518 | 0 | SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_NO_SSLv2 | |
519 | 0 | SSL_OP_NO_SSLv3); |
520 | 0 | #ifdef SSL_OP_NO_COMPRESSION |
521 | 0 | SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_NO_COMPRESSION); |
522 | 0 | #endif |
523 | 0 | SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_SINGLE_DH_USE); |
524 | 0 | SSL_CTX_set_options(vhost->tls.ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); |
525 | |
|
526 | 0 | if (info->ssl_cipher_list) |
527 | 0 | SSL_CTX_set_cipher_list(vhost->tls.ssl_ctx, info->ssl_cipher_list); |
528 | |
|
529 | 0 | #if defined(LWS_HAVE_SSL_CTX_set_ciphersuites) |
530 | 0 | if (info->tls1_3_plus_cipher_list) |
531 | 0 | SSL_CTX_set_ciphersuites(vhost->tls.ssl_ctx, |
532 | 0 | info->tls1_3_plus_cipher_list); |
533 | 0 | #endif |
534 | |
|
535 | 0 | #if !defined(OPENSSL_NO_TLSEXT) |
536 | 0 | SSL_CTX_set_tlsext_servername_callback(vhost->tls.ssl_ctx, |
537 | 0 | lws_ssl_server_name_cb); |
538 | 0 | SSL_CTX_set_tlsext_servername_arg(vhost->tls.ssl_ctx, vhost->context); |
539 | 0 | #endif |
540 | |
|
541 | 0 | if (info->ssl_ca_filepath && |
542 | | #if defined(LWS_HAVE_SSL_CTX_load_verify_file) |
543 | | !SSL_CTX_load_verify_file(vhost->tls.ssl_ctx, |
544 | | info->ssl_ca_filepath)) { |
545 | | #else |
546 | 0 | !SSL_CTX_load_verify_locations(vhost->tls.ssl_ctx, |
547 | 0 | info->ssl_ca_filepath, NULL)) { |
548 | 0 | #endif |
549 | 0 | lwsl_err("%s: SSL_CTX_load_verify_locations unhappy\n", |
550 | 0 | __func__); |
551 | 0 | } |
552 | |
|
553 | 0 | SSL_OPT_TYPE ssl_options_set_value = (SSL_OPT_TYPE) info->ssl_options_set; |
554 | |
|
555 | 0 | if (info->ssl_options_set) |
556 | 0 | SSL_CTX_set_options(vhost->tls.ssl_ctx, ssl_options_set_value); |
557 | |
|
558 | 0 | #if (OPENSSL_VERSION_NUMBER >= 0x009080df) && !defined(USE_WOLFSSL) |
559 | | |
560 | | /* SSL_clear_options introduced in 0.9.8m */ |
561 | 0 | SSL_OPT_TYPE ssl_options_clear_value = (SSL_OPT_TYPE) info->ssl_options_clear; |
562 | |
|
563 | 0 | if (info->ssl_options_clear) { |
564 | 0 | SSL_CTX_clear_options(vhost->tls.ssl_ctx, ssl_options_clear_value); |
565 | 0 | } |
566 | |
|
567 | 0 | lwsl_info(" SSL options 0x%lX\n", |
568 | 0 | (unsigned long)SSL_CTX_get_options(vhost->tls.ssl_ctx)); |
569 | 0 | #endif |
570 | |
|
571 | 0 | if (!vhost->tls.use_ssl || |
572 | 0 | (!info->ssl_cert_filepath && !info->server_ssl_cert_mem)) |
573 | 0 | return 0; |
574 | | |
575 | 0 | lws_ssl_bind_passphrase(vhost->tls.ssl_ctx, 0, info); |
576 | |
|
577 | 0 | return lws_tls_server_certs_load(vhost, wsi, info->ssl_cert_filepath, |
578 | 0 | info->ssl_private_key_filepath, |
579 | 0 | info->server_ssl_cert_mem, |
580 | 0 | info->server_ssl_cert_mem_len, |
581 | 0 | info->server_ssl_private_key_mem, |
582 | 0 | info->server_ssl_private_key_mem_len); |
583 | 0 | } |
584 | | |
585 | | int |
586 | | lws_tls_server_new_nonblocking(struct lws *wsi, lws_sockfd_type accept_fd) |
587 | 0 | { |
588 | 0 | #if !defined(USE_WOLFSSL) |
589 | 0 | BIO *bio; |
590 | 0 | #endif |
591 | |
|
592 | 0 | errno = 0; |
593 | 0 | ERR_clear_error(); |
594 | 0 | wsi->tls.ssl = SSL_new(wsi->a.vhost->tls.ssl_ctx); |
595 | 0 | if (wsi->tls.ssl == NULL) { |
596 | 0 | lwsl_err("SSL_new failed: %d (errno %d)\n", |
597 | 0 | lws_ssl_get_error(wsi, 0), errno); |
598 | |
|
599 | 0 | lws_tls_err_describe_clear(); |
600 | 0 | return 1; |
601 | 0 | } |
602 | | |
603 | 0 | SSL_set_ex_data(wsi->tls.ssl, openssl_websocket_private_data_index, wsi); |
604 | 0 | SSL_set_fd(wsi->tls.ssl, (int)(lws_intptr_t)accept_fd); |
605 | |
|
606 | | #ifdef USE_WOLFSSL |
607 | | #ifdef USE_OLD_CYASSL |
608 | | CyaSSL_set_using_nonblock(wsi->tls.ssl, 1); |
609 | | #else |
610 | | wolfSSL_set_using_nonblock(wsi->tls.ssl, 1); |
611 | | #endif |
612 | | #else |
613 | |
|
614 | 0 | SSL_set_mode(wsi->tls.ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | |
615 | 0 | SSL_MODE_RELEASE_BUFFERS); |
616 | 0 | bio = SSL_get_rbio(wsi->tls.ssl); |
617 | 0 | if (bio) |
618 | 0 | BIO_set_nbio(bio, 1); /* nonblocking */ |
619 | 0 | else |
620 | 0 | lwsl_notice("NULL rbio\n"); |
621 | 0 | bio = SSL_get_wbio(wsi->tls.ssl); |
622 | 0 | if (bio) |
623 | 0 | BIO_set_nbio(bio, 1); /* nonblocking */ |
624 | 0 | else |
625 | 0 | lwsl_notice("NULL rbio\n"); |
626 | 0 | #endif |
627 | |
|
628 | 0 | #if defined (LWS_HAVE_SSL_SET_INFO_CALLBACK) |
629 | 0 | if (wsi->a.vhost->tls.ssl_info_event_mask) |
630 | 0 | SSL_set_info_callback(wsi->tls.ssl, lws_ssl_info_callback); |
631 | 0 | #endif |
632 | |
|
633 | 0 | return 0; |
634 | 0 | } |
635 | | |
636 | | enum lws_ssl_capable_status |
637 | | lws_tls_server_abort_connection(struct lws *wsi) |
638 | 0 | { |
639 | 0 | if (wsi->tls.use_ssl) |
640 | 0 | SSL_shutdown(wsi->tls.ssl); |
641 | 0 | SSL_free(wsi->tls.ssl); |
642 | |
|
643 | 0 | return LWS_SSL_CAPABLE_DONE; |
644 | 0 | } |
645 | | |
646 | | enum lws_ssl_capable_status |
647 | | lws_tls_server_accept(struct lws *wsi) |
648 | 0 | { |
649 | 0 | struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; |
650 | 0 | union lws_tls_cert_info_results ir; |
651 | 0 | int m, n; |
652 | |
|
653 | 0 | errno = 0; |
654 | 0 | ERR_clear_error(); |
655 | 0 | n = SSL_accept(wsi->tls.ssl); |
656 | |
|
657 | 0 | wsi->skip_fallback = 1; |
658 | |
|
659 | 0 | if (n == 1) { |
660 | 0 | n = lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_COMMON_NAME, &ir, |
661 | 0 | sizeof(ir.ns.name)); |
662 | 0 | if (!n) |
663 | 0 | lwsl_notice("%s: client cert CN '%s'\n", __func__, |
664 | 0 | ir.ns.name); |
665 | 0 | else |
666 | 0 | lwsl_info("%s: no client cert CN\n", __func__); |
667 | |
|
668 | 0 | lws_openssl_describe_cipher(wsi); |
669 | |
|
670 | 0 | if (SSL_pending(wsi->tls.ssl) && |
671 | 0 | lws_dll2_is_detached(&wsi->tls.dll_pending_tls)) |
672 | 0 | lws_dll2_add_head(&wsi->tls.dll_pending_tls, |
673 | 0 | &pt->tls.dll_pending_tls_owner); |
674 | |
|
675 | 0 | return LWS_SSL_CAPABLE_DONE; |
676 | 0 | } |
677 | | |
678 | 0 | m = lws_ssl_get_error(wsi, n); |
679 | 0 | lws_tls_err_describe_clear(); |
680 | |
|
681 | 0 | if (m == SSL_ERROR_SYSCALL || m == SSL_ERROR_SSL) |
682 | 0 | return LWS_SSL_CAPABLE_ERROR; |
683 | | |
684 | 0 | if (m == SSL_ERROR_WANT_READ || |
685 | 0 | (m != SSL_ERROR_ZERO_RETURN && SSL_want_read(wsi->tls.ssl))) { |
686 | 0 | if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { |
687 | 0 | lwsl_info("%s: WANT_READ change_pollfd failed\n", |
688 | 0 | __func__); |
689 | 0 | return LWS_SSL_CAPABLE_ERROR; |
690 | 0 | } |
691 | | |
692 | 0 | lwsl_info("SSL_ERROR_WANT_READ: m %d\n", m); |
693 | 0 | return LWS_SSL_CAPABLE_MORE_SERVICE_READ; |
694 | 0 | } |
695 | 0 | if (m == SSL_ERROR_WANT_WRITE || SSL_want_write(wsi->tls.ssl)) { |
696 | 0 | lwsl_debug("%s: WANT_WRITE\n", __func__); |
697 | |
|
698 | 0 | if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) { |
699 | 0 | lwsl_info("%s: WANT_WRITE change_pollfd failed\n", |
700 | 0 | __func__); |
701 | 0 | return LWS_SSL_CAPABLE_ERROR; |
702 | 0 | } |
703 | 0 | return LWS_SSL_CAPABLE_MORE_SERVICE_WRITE; |
704 | 0 | } |
705 | | |
706 | 0 | return LWS_SSL_CAPABLE_ERROR; |
707 | 0 | } |
708 | | |
709 | | #if defined(LWS_WITH_ACME) |
710 | | static int |
711 | | lws_tls_openssl_rsa_new_key(RSA **rsa, int bits) |
712 | | { |
713 | | BIGNUM *bn = BN_new(); |
714 | | int n; |
715 | | |
716 | | if (!bn) |
717 | | return 1; |
718 | | |
719 | | if (BN_set_word(bn, RSA_F4) != 1) { |
720 | | BN_free(bn); |
721 | | return 1; |
722 | | } |
723 | | |
724 | | *rsa = RSA_new(); |
725 | | if (!*rsa) { |
726 | | BN_free(bn); |
727 | | return 1; |
728 | | } |
729 | | |
730 | | n = RSA_generate_key_ex(*rsa, bits, bn, NULL); |
731 | | BN_free(bn); |
732 | | if (n == 1) |
733 | | return 0; |
734 | | |
735 | | RSA_free(*rsa); |
736 | | *rsa = NULL; |
737 | | |
738 | | return 1; |
739 | | } |
740 | | |
741 | | struct lws_tls_ss_pieces { |
742 | | X509 *x509; |
743 | | EVP_PKEY *pkey; |
744 | | RSA *rsa; |
745 | | }; |
746 | | |
747 | | int |
748 | | lws_tls_acme_sni_cert_create(struct lws_vhost *vhost, const char *san_a, |
749 | | const char *san_b) |
750 | | { |
751 | | GENERAL_NAMES *gens = sk_GENERAL_NAME_new_null(); |
752 | | GENERAL_NAME *gen = NULL; |
753 | | ASN1_IA5STRING *ia5 = NULL; |
754 | | X509_NAME *name; |
755 | | |
756 | | if (!gens) |
757 | | return 1; |
758 | | |
759 | | vhost->tls.ss = lws_zalloc(sizeof(*vhost->tls.ss), "sni cert"); |
760 | | if (!vhost->tls.ss) { |
761 | | GENERAL_NAMES_free(gens); |
762 | | return 1; |
763 | | } |
764 | | |
765 | | vhost->tls.ss->x509 = X509_new(); |
766 | | if (!vhost->tls.ss->x509) |
767 | | goto bail; |
768 | | |
769 | | ASN1_INTEGER_set(X509_get_serialNumber(vhost->tls.ss->x509), 1); |
770 | | X509_gmtime_adj(X509_get_notBefore(vhost->tls.ss->x509), 0); |
771 | | X509_gmtime_adj(X509_get_notAfter(vhost->tls.ss->x509), 3600); |
772 | | |
773 | | vhost->tls.ss->pkey = EVP_PKEY_new(); |
774 | | if (!vhost->tls.ss->pkey) |
775 | | goto bail0; |
776 | | |
777 | | if (lws_tls_openssl_rsa_new_key(&vhost->tls.ss->rsa, 4096)) |
778 | | goto bail1; |
779 | | |
780 | | if (!EVP_PKEY_assign_RSA(vhost->tls.ss->pkey, vhost->tls.ss->rsa)) |
781 | | goto bail2; |
782 | | |
783 | | X509_set_pubkey(vhost->tls.ss->x509, vhost->tls.ss->pkey); |
784 | | |
785 | | name = X509_get_subject_name(vhost->tls.ss->x509); |
786 | | X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, |
787 | | (unsigned char *)"GB", -1, -1, 0); |
788 | | X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, |
789 | | (unsigned char *)"somecompany", -1, -1, 0); |
790 | | if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_UTF8, |
791 | | (unsigned char *)"temp.acme.invalid", |
792 | | -1, -1, 0) != 1) { |
793 | | lwsl_notice("failed to add CN\n"); |
794 | | goto bail2; |
795 | | } |
796 | | X509_set_issuer_name(vhost->tls.ss->x509, name); |
797 | | |
798 | | /* add the SAN payloads */ |
799 | | |
800 | | gen = GENERAL_NAME_new(); |
801 | | ia5 = ASN1_IA5STRING_new(); |
802 | | if (!ASN1_STRING_set(ia5, san_a, -1)) { |
803 | | lwsl_notice("failed to set ia5\n"); |
804 | | GENERAL_NAME_free(gen); |
805 | | goto bail2; |
806 | | } |
807 | | GENERAL_NAME_set0_value(gen, GEN_DNS, ia5); |
808 | | sk_GENERAL_NAME_push(gens, gen); |
809 | | |
810 | | if (X509_add1_ext_i2d(vhost->tls.ss->x509, NID_subject_alt_name, |
811 | | gens, 0, X509V3_ADD_APPEND) != 1) |
812 | | goto bail2; |
813 | | |
814 | | GENERAL_NAMES_free(gens); |
815 | | |
816 | | if (san_b && san_b[0]) { |
817 | | gens = sk_GENERAL_NAME_new_null(); |
818 | | gen = GENERAL_NAME_new(); |
819 | | ia5 = ASN1_IA5STRING_new(); |
820 | | if (!ASN1_STRING_set(ia5, san_a, -1)) { |
821 | | lwsl_notice("failed to set ia5\n"); |
822 | | GENERAL_NAME_free(gen); |
823 | | goto bail2; |
824 | | } |
825 | | GENERAL_NAME_set0_value(gen, GEN_DNS, ia5); |
826 | | sk_GENERAL_NAME_push(gens, gen); |
827 | | |
828 | | if (X509_add1_ext_i2d(vhost->tls.ss->x509, NID_subject_alt_name, |
829 | | gens, 0, X509V3_ADD_APPEND) != 1) |
830 | | goto bail2; |
831 | | |
832 | | GENERAL_NAMES_free(gens); |
833 | | } |
834 | | |
835 | | /* sign it with our private key */ |
836 | | if (!X509_sign(vhost->tls.ss->x509, vhost->tls.ss->pkey, EVP_sha256())) |
837 | | goto bail2; |
838 | | |
839 | | #if 0 |
840 | | {/* useful to take a sample of a working cert for mbedtls to crib */ |
841 | | FILE *fp = fopen("/tmp/acme-temp-cert", "w+"); |
842 | | |
843 | | i2d_X509_fp(fp, vhost->tls.ss->x509); |
844 | | fclose(fp); |
845 | | } |
846 | | #endif |
847 | | |
848 | | /* tell the vhost to use our crafted certificate */ |
849 | | SSL_CTX_use_certificate(vhost->tls.ssl_ctx, vhost->tls.ss->x509); |
850 | | /* and to use our generated private key */ |
851 | | SSL_CTX_use_PrivateKey(vhost->tls.ssl_ctx, vhost->tls.ss->pkey); |
852 | | |
853 | | return 0; |
854 | | |
855 | | bail2: |
856 | | RSA_free(vhost->tls.ss->rsa); |
857 | | bail1: |
858 | | EVP_PKEY_free(vhost->tls.ss->pkey); |
859 | | bail0: |
860 | | X509_free(vhost->tls.ss->x509); |
861 | | bail: |
862 | | lws_free(vhost->tls.ss); |
863 | | GENERAL_NAMES_free(gens); |
864 | | |
865 | | return 1; |
866 | | } |
867 | | |
868 | | void |
869 | | lws_tls_acme_sni_cert_destroy(struct lws_vhost *vhost) |
870 | | { |
871 | | if (!vhost->tls.ss) |
872 | | return; |
873 | | |
874 | | EVP_PKEY_free(vhost->tls.ss->pkey); |
875 | | X509_free(vhost->tls.ss->x509); |
876 | | lws_free_set_NULL(vhost->tls.ss); |
877 | | } |
878 | | |
879 | | static int |
880 | | lws_tls_openssl_add_nid(X509_NAME *name, int nid, const char *value) |
881 | | { |
882 | | X509_NAME_ENTRY *e; |
883 | | int n; |
884 | | |
885 | | if (!value || value[0] == '\0') |
886 | | value = "none"; |
887 | | |
888 | | e = X509_NAME_ENTRY_create_by_NID(NULL, nid, MBSTRING_ASC, |
889 | | (unsigned char *)value, -1); |
890 | | if (!e) |
891 | | return 1; |
892 | | n = X509_NAME_add_entry(name, e, -1, 0); |
893 | | X509_NAME_ENTRY_free(e); |
894 | | |
895 | | return n != 1; |
896 | | } |
897 | | |
898 | | static int nid_list[] = { |
899 | | NID_countryName, /* LWS_TLS_REQ_ELEMENT_COUNTRY */ |
900 | | NID_stateOrProvinceName, /* LWS_TLS_REQ_ELEMENT_STATE */ |
901 | | NID_localityName, /* LWS_TLS_REQ_ELEMENT_LOCALITY */ |
902 | | NID_organizationName, /* LWS_TLS_REQ_ELEMENT_ORGANIZATION */ |
903 | | NID_commonName, /* LWS_TLS_REQ_ELEMENT_COMMON_NAME */ |
904 | | NID_subject_alt_name, /* LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME */ |
905 | | NID_pkcs9_emailAddress, /* LWS_TLS_REQ_ELEMENT_EMAIL */ |
906 | | }; |
907 | | |
908 | | int |
909 | | lws_tls_acme_sni_csr_create(struct lws_context *context, const char *elements[], |
910 | | uint8_t *csr, size_t csr_len, char **privkey_pem, |
911 | | size_t *privkey_len) |
912 | | { |
913 | | uint8_t *csr_in = csr; |
914 | | RSA *rsakey; |
915 | | X509_REQ *req; |
916 | | X509_NAME *subj; |
917 | | EVP_PKEY *pkey; |
918 | | char *p, *end; |
919 | | BIO *bio; |
920 | | long bio_len; |
921 | | int n, ret = -1; |
922 | | |
923 | | if (lws_tls_openssl_rsa_new_key(&rsakey, 4096)) |
924 | | return -1; |
925 | | |
926 | | pkey = EVP_PKEY_new(); |
927 | | if (!pkey) |
928 | | goto bail0; |
929 | | if (!EVP_PKEY_set1_RSA(pkey, rsakey)) |
930 | | goto bail1; |
931 | | |
932 | | req = X509_REQ_new(); |
933 | | if (!req) |
934 | | goto bail1; |
935 | | |
936 | | X509_REQ_set_pubkey(req, pkey); |
937 | | |
938 | | subj = X509_NAME_new(); |
939 | | if (!subj) |
940 | | goto bail2; |
941 | | |
942 | | for (n = 0; n < LWS_TLS_REQ_ELEMENT_COUNT; n++) |
943 | | if (elements[n] && |
944 | | lws_tls_openssl_add_nid(subj, nid_list[n], |
945 | | elements[n])) { |
946 | | lwsl_notice("%s: failed to add element %d\n", |
947 | | __func__, n); |
948 | | goto bail3; |
949 | | } |
950 | | |
951 | | if (X509_REQ_set_subject_name(req, subj) != 1) |
952 | | goto bail3; |
953 | | |
954 | | if (elements[LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME]) { |
955 | | STACK_OF(X509_EXTENSION) *exts; |
956 | | X509_EXTENSION *ext; |
957 | | char san[256]; |
958 | | |
959 | | exts = sk_X509_EXTENSION_new_null(); |
960 | | if (!exts) |
961 | | goto bail3; |
962 | | |
963 | | lws_snprintf(san, sizeof(san), "DNS:%s,DNS:%s", |
964 | | elements[LWS_TLS_REQ_ELEMENT_COMMON_NAME], |
965 | | elements[LWS_TLS_REQ_ELEMENT_SUBJECT_ALT_NAME]); |
966 | | |
967 | | ext = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, |
968 | | san); |
969 | | if (!ext) { |
970 | | sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); |
971 | | goto bail3; |
972 | | } |
973 | | sk_X509_EXTENSION_push(exts, ext); |
974 | | |
975 | | if (!X509_REQ_add_extensions(req, exts)) { |
976 | | sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); |
977 | | goto bail3; |
978 | | } |
979 | | sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free); |
980 | | } |
981 | | |
982 | | if (!X509_REQ_sign(req, pkey, EVP_sha256())) |
983 | | goto bail3; |
984 | | |
985 | | /* |
986 | | * issue the CSR as PEM to a BIO, and translate to b64urlenc without |
987 | | * headers, trailers, or whitespace |
988 | | */ |
989 | | |
990 | | bio = BIO_new(BIO_s_mem()); |
991 | | if (!bio) |
992 | | goto bail3; |
993 | | |
994 | | if (PEM_write_bio_X509_REQ(bio, req) != 1) { |
995 | | BIO_free(bio); |
996 | | goto bail3; |
997 | | } |
998 | | |
999 | | bio_len = BIO_get_mem_data(bio, &p); |
1000 | | end = p + bio_len; |
1001 | | |
1002 | | /* strip the header line */ |
1003 | | while (p < end && *p != '\n') |
1004 | | p++; |
1005 | | |
1006 | | while (p < end && csr_len) { |
1007 | | if (*p == '\n') { |
1008 | | p++; |
1009 | | continue; |
1010 | | } |
1011 | | |
1012 | | if (*p == '-') |
1013 | | break; |
1014 | | |
1015 | | if (*p == '+') |
1016 | | *csr++ = '-'; |
1017 | | else |
1018 | | if (*p == '/') |
1019 | | *csr++ = '_'; |
1020 | | else |
1021 | | *csr++ = (uint8_t)*p; |
1022 | | p++; |
1023 | | csr_len--; |
1024 | | } |
1025 | | BIO_free(bio); |
1026 | | if (!csr_len) { |
1027 | | lwsl_notice("%s: need %ld for CSR\n", __func__, bio_len); |
1028 | | goto bail3; |
1029 | | } |
1030 | | |
1031 | | /* |
1032 | | * Also return the private key as a PEM in memory |
1033 | | * (platform may not have a filesystem) |
1034 | | */ |
1035 | | bio = BIO_new(BIO_s_mem()); |
1036 | | if (!bio) |
1037 | | goto bail3; |
1038 | | |
1039 | | if (PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, 0, NULL) != 1) { |
1040 | | BIO_free(bio); |
1041 | | goto bail3; |
1042 | | } |
1043 | | bio_len = BIO_get_mem_data(bio, &p); |
1044 | | *privkey_pem = malloc((unsigned long)bio_len); /* malloc so user code can own / free */ |
1045 | | *privkey_len = (size_t)bio_len; |
1046 | | if (!*privkey_pem) { |
1047 | | lwsl_notice("%s: need %ld for private key\n", __func__, |
1048 | | bio_len); |
1049 | | BIO_free(bio); |
1050 | | goto bail3; |
1051 | | } |
1052 | | memcpy(*privkey_pem, p, (unsigned int)(int)(long long)bio_len); |
1053 | | BIO_free(bio); |
1054 | | |
1055 | | ret = lws_ptr_diff(csr, csr_in); |
1056 | | |
1057 | | bail3: |
1058 | | X509_NAME_free(subj); |
1059 | | bail2: |
1060 | | X509_REQ_free(req); |
1061 | | bail1: |
1062 | | EVP_PKEY_free(pkey); |
1063 | | bail0: |
1064 | | RSA_free(rsakey); |
1065 | | |
1066 | | return ret; |
1067 | | } |
1068 | | #endif |