]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * librb: a library used by ircd-ratbox and other things | |
3 | * mbedtls.c: ARM MbedTLS backend | |
4 | * | |
5 | * Copyright (C) 2007-2008 ircd-ratbox development team | |
6 | * Copyright (C) 2007-2008 Aaron Sethman <androsyn@ratbox.org> | |
7 | * Copyright (C) 2015 William Pitcock <nenolod@dereferenced.org> | |
8 | * Copyright (C) 2016 Aaron Jones <aaronmdjones@gmail.com> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or | |
13 | * (at your option) any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program; if not, write to the Free Software | |
22 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 | |
23 | * USA | |
24 | * | |
25 | */ | |
26 | ||
27 | #include <librb_config.h> | |
28 | #include <rb_lib.h> | |
29 | ||
30 | #ifdef HAVE_MBEDTLS | |
31 | ||
32 | #include <commio-int.h> | |
33 | #include <commio-ssl.h> | |
34 | #include <stdbool.h> | |
35 | ||
36 | #include "mbedtls_ratbox.h" | |
37 | ||
38 | typedef enum | |
39 | { | |
40 | RB_FD_TLS_DIRECTION_IN = 0, | |
41 | RB_FD_TLS_DIRECTION_OUT = 1 | |
42 | } rb_fd_tls_direction; | |
43 | ||
44 | #define RB_MAX_CIPHERSUITES 512 | |
45 | ||
46 | typedef struct | |
47 | { | |
48 | mbedtls_x509_crt crt; | |
49 | mbedtls_pk_context key; | |
50 | mbedtls_dhm_context dhp; | |
51 | mbedtls_ssl_config server_cfg; | |
52 | mbedtls_ssl_config client_cfg; | |
53 | size_t refcount; | |
54 | int suites[RB_MAX_CIPHERSUITES + 1]; | |
55 | } rb_mbedtls_cfg_context; | |
56 | ||
57 | typedef struct | |
58 | { | |
59 | rb_mbedtls_cfg_context *cfg; | |
60 | mbedtls_ssl_context ssl; | |
61 | } rb_mbedtls_ssl_context; | |
62 | ||
63 | #define SSL_C(x) ((rb_mbedtls_ssl_context *) (x)->ssl)->cfg | |
64 | #define SSL_P(x) &((rb_mbedtls_ssl_context *) (x)->ssl)->ssl | |
65 | ||
66 | static mbedtls_ctr_drbg_context ctr_drbg_ctx; | |
67 | static mbedtls_entropy_context entropy_ctx; | |
68 | ||
69 | static mbedtls_x509_crt dummy_ca_ctx; | |
70 | static rb_mbedtls_cfg_context *rb_mbedtls_cfg = NULL; | |
71 | ||
72 | ||
73 | ||
74 | struct ssl_connect | |
75 | { | |
76 | CNCB *callback; | |
77 | void *data; | |
78 | int timeout; | |
79 | }; | |
80 | ||
81 | static const char *rb_ssl_strerror(int); | |
82 | static void rb_ssl_connect_realcb(rb_fde_t *, int, struct ssl_connect *); | |
83 | ||
84 | ||
85 | ||
86 | /* | |
87 | * Internal MbedTLS-specific code | |
88 | */ | |
89 | ||
90 | static void | |
91 | rb_mbedtls_cfg_incref(rb_mbedtls_cfg_context *const cfg) | |
92 | { | |
93 | lrb_assert(cfg->refcount > 0); | |
94 | ||
95 | cfg->refcount++; | |
96 | } | |
97 | ||
98 | static void | |
99 | rb_mbedtls_cfg_decref(rb_mbedtls_cfg_context *const cfg) | |
100 | { | |
101 | if(cfg == NULL) | |
102 | return; | |
103 | ||
104 | lrb_assert(cfg->refcount > 0); | |
105 | ||
106 | if((--cfg->refcount) > 0) | |
107 | return; | |
108 | ||
109 | mbedtls_ssl_config_free(&cfg->client_cfg); | |
110 | mbedtls_ssl_config_free(&cfg->server_cfg); | |
111 | mbedtls_dhm_free(&cfg->dhp); | |
112 | mbedtls_pk_free(&cfg->key); | |
113 | mbedtls_x509_crt_free(&cfg->crt); | |
114 | ||
115 | rb_free(cfg); | |
116 | } | |
117 | ||
118 | static int | |
119 | rb_sock_net_recv(void *const context_ptr, unsigned char *const buf, const size_t count) | |
120 | { | |
121 | const int fd = rb_get_fd((rb_fde_t *)context_ptr); | |
122 | ||
123 | const int ret = (int) read(fd, buf, count); | |
124 | ||
125 | if(ret < 0 && rb_ignore_errno(errno)) | |
126 | return MBEDTLS_ERR_SSL_WANT_READ; | |
127 | ||
128 | return ret; | |
129 | } | |
130 | ||
131 | static int | |
132 | rb_sock_net_xmit(void *const context_ptr, const unsigned char *const buf, const size_t count) | |
133 | { | |
134 | const int fd = rb_get_fd((rb_fde_t *)context_ptr); | |
135 | ||
136 | const int ret = (int) write(fd, buf, count); | |
137 | ||
138 | if(ret < 0 && rb_ignore_errno(errno)) | |
139 | return MBEDTLS_ERR_SSL_WANT_WRITE; | |
140 | ||
141 | return ret; | |
142 | } | |
143 | ||
144 | static void | |
145 | rb_ssl_init_fd(rb_fde_t *const F, const rb_fd_tls_direction dir) | |
146 | { | |
147 | rb_mbedtls_ssl_context *const mbed_ssl_ctx = rb_malloc(sizeof *mbed_ssl_ctx); | |
148 | ||
149 | if(mbed_ssl_ctx == NULL) | |
150 | { | |
151 | rb_lib_log("%s: rb_malloc: allocation failure", __func__); | |
152 | rb_close(F); | |
153 | return; | |
154 | } | |
155 | ||
156 | mbedtls_ssl_config *mbed_config = NULL; | |
157 | ||
158 | switch(dir) | |
159 | { | |
160 | case RB_FD_TLS_DIRECTION_IN: | |
161 | mbed_config = &rb_mbedtls_cfg->server_cfg; | |
162 | break; | |
163 | case RB_FD_TLS_DIRECTION_OUT: | |
164 | mbed_config = &rb_mbedtls_cfg->client_cfg; | |
165 | break; | |
166 | } | |
167 | ||
168 | mbedtls_ssl_init(&mbed_ssl_ctx->ssl); | |
169 | ||
170 | int ret; | |
171 | ||
172 | if((ret = mbedtls_ssl_setup(&mbed_ssl_ctx->ssl, mbed_config)) != 0) | |
173 | { | |
174 | rb_lib_log("%s: ssl_setup: %s", __func__, rb_ssl_strerror(ret)); | |
175 | mbedtls_ssl_free(&mbed_ssl_ctx->ssl); | |
176 | rb_free(mbed_ssl_ctx); | |
177 | rb_close(F); | |
178 | return; | |
179 | } | |
180 | ||
181 | mbedtls_ssl_set_bio(&mbed_ssl_ctx->ssl, F, rb_sock_net_xmit, rb_sock_net_recv, NULL); | |
182 | ||
183 | rb_mbedtls_cfg_incref(rb_mbedtls_cfg); | |
184 | mbed_ssl_ctx->cfg = rb_mbedtls_cfg; | |
185 | ||
186 | F->ssl = mbed_ssl_ctx; | |
187 | } | |
188 | ||
189 | static rb_mbedtls_cfg_context * | |
190 | rb_mbedtls_cfg_new(void) | |
191 | { | |
192 | rb_mbedtls_cfg_context *const cfg = rb_malloc(sizeof *cfg); | |
193 | ||
194 | if(cfg == NULL) | |
195 | return NULL; | |
196 | ||
197 | mbedtls_x509_crt_init(&cfg->crt); | |
198 | mbedtls_pk_init(&cfg->key); | |
199 | mbedtls_dhm_init(&cfg->dhp); | |
200 | mbedtls_ssl_config_init(&cfg->server_cfg); | |
201 | mbedtls_ssl_config_init(&cfg->client_cfg); | |
202 | ||
203 | (void) memset(cfg->suites, 0x00, sizeof cfg->suites); | |
204 | ||
205 | cfg->refcount = 1; | |
206 | ||
207 | int ret; | |
208 | ||
209 | if((ret = mbedtls_ssl_config_defaults(&cfg->server_cfg, | |
210 | MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, | |
211 | MBEDTLS_SSL_PRESET_DEFAULT)) != 0) | |
212 | { | |
213 | rb_lib_log("%s: ssl_config_defaults (server): %s", __func__, rb_ssl_strerror(ret)); | |
214 | rb_mbedtls_cfg_decref(cfg); | |
215 | return NULL; | |
216 | } | |
217 | ||
218 | if((ret = mbedtls_ssl_config_defaults(&cfg->client_cfg, | |
219 | MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, | |
220 | MBEDTLS_SSL_PRESET_DEFAULT)) != 0) | |
221 | { | |
222 | rb_lib_log("%s: ssl_config_defaults (client): %s", __func__, rb_ssl_strerror(ret)); | |
223 | rb_mbedtls_cfg_decref(cfg); | |
224 | return NULL; | |
225 | } | |
226 | ||
227 | mbedtls_ssl_conf_rng(&cfg->server_cfg, mbedtls_ctr_drbg_random, &ctr_drbg_ctx); | |
228 | mbedtls_ssl_conf_rng(&cfg->client_cfg, mbedtls_ctr_drbg_random, &ctr_drbg_ctx); | |
229 | ||
230 | mbedtls_ssl_conf_ca_chain(&cfg->server_cfg, &dummy_ca_ctx, NULL); | |
231 | mbedtls_ssl_conf_ca_chain(&cfg->client_cfg, &dummy_ca_ctx, NULL); | |
232 | ||
233 | mbedtls_ssl_conf_authmode(&cfg->server_cfg, MBEDTLS_SSL_VERIFY_OPTIONAL); | |
234 | mbedtls_ssl_conf_authmode(&cfg->client_cfg, MBEDTLS_SSL_VERIFY_NONE); | |
235 | ||
236 | mbedtls_ssl_conf_min_version(&cfg->server_cfg, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_2); | |
237 | mbedtls_ssl_conf_min_version(&cfg->client_cfg, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_2); | |
238 | ||
239 | #ifdef MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE | |
240 | mbedtls_ssl_conf_legacy_renegotiation(&cfg->client_cfg, MBEDTLS_SSL_LEGACY_BREAK_HANDSHAKE); | |
241 | #endif | |
242 | ||
243 | #ifdef MBEDTLS_SSL_SESSION_TICKETS_DISABLED | |
244 | mbedtls_ssl_conf_session_tickets(&cfg->client_cfg, MBEDTLS_SSL_SESSION_TICKETS_DISABLED); | |
245 | #endif | |
246 | ||
247 | return cfg; | |
248 | } | |
249 | ||
250 | static void | |
251 | rb_ssl_accept_common(rb_fde_t *const F, void *const data) | |
252 | { | |
253 | lrb_assert(F != NULL); | |
254 | lrb_assert(F->accept != NULL); | |
255 | lrb_assert(F->accept->callback != NULL); | |
256 | lrb_assert(F->ssl != NULL); | |
257 | ||
258 | (void) data; | |
259 | ||
260 | const int ret = mbedtls_ssl_handshake(SSL_P(F)); | |
261 | ||
262 | switch(ret) | |
263 | { | |
264 | case 0: | |
265 | F->handshake_count++; | |
266 | break; | |
267 | case MBEDTLS_ERR_SSL_WANT_READ: | |
268 | rb_setselect(F, RB_SELECT_READ, rb_ssl_accept_common, NULL); | |
269 | return; | |
270 | case MBEDTLS_ERR_SSL_WANT_WRITE: | |
271 | rb_setselect(F, RB_SELECT_WRITE, rb_ssl_accept_common, NULL); | |
272 | return; | |
273 | default: | |
274 | errno = EIO; | |
275 | F->ssl_errno = (unsigned long) -ret; | |
276 | F->accept->callback(F, RB_ERROR_SSL, NULL, 0, F->accept->data); | |
277 | return; | |
278 | } | |
279 | ||
280 | rb_settimeout(F, 0, NULL, NULL); | |
281 | rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL); | |
282 | ||
283 | struct acceptdata *const ad = F->accept; | |
284 | F->accept = NULL; | |
285 | ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data); | |
286 | rb_free(ad); | |
287 | } | |
288 | ||
289 | static void | |
290 | rb_ssl_connect_common(rb_fde_t *const F, void *const data) | |
291 | { | |
292 | lrb_assert(F != NULL); | |
293 | lrb_assert(F->ssl != NULL); | |
294 | ||
295 | const int ret = mbedtls_ssl_handshake(SSL_P(F)); | |
296 | ||
297 | switch(ret) | |
298 | { | |
299 | case 0: | |
300 | F->handshake_count++; | |
301 | break; | |
302 | case MBEDTLS_ERR_SSL_WANT_READ: | |
303 | rb_setselect(F, RB_SELECT_READ, rb_ssl_connect_common, data); | |
304 | return; | |
305 | case MBEDTLS_ERR_SSL_WANT_WRITE: | |
306 | rb_setselect(F, RB_SELECT_WRITE, rb_ssl_connect_common, data); | |
307 | return; | |
308 | default: | |
309 | errno = EIO; | |
310 | F->ssl_errno = (unsigned long) -ret; | |
311 | rb_ssl_connect_realcb(F, RB_ERROR_SSL, data); | |
312 | return; | |
313 | } | |
314 | ||
315 | rb_ssl_connect_realcb(F, RB_OK, data); | |
316 | } | |
317 | ||
318 | static const char * | |
319 | rb_ssl_strerror(int err) | |
320 | { | |
321 | static char errbuf[512]; | |
322 | ||
323 | if (err < 0) | |
324 | err = -err; | |
325 | ||
326 | #ifdef MBEDTLS_ERROR_C | |
327 | char mbed_errbuf[512]; | |
328 | mbedtls_strerror(err, mbed_errbuf, sizeof mbed_errbuf); | |
329 | (void) snprintf(errbuf, sizeof errbuf, "-0x%X: %s", (unsigned int) err, mbed_errbuf); | |
330 | #else | |
331 | (void) snprintf(errbuf, sizeof errbuf, "-0x%X", (unsigned int) err); | |
332 | #endif | |
333 | ||
334 | return errbuf; | |
335 | } | |
336 | ||
337 | static int | |
338 | rb_make_certfp(const mbedtls_x509_crt *const peer_cert, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method) | |
339 | { | |
340 | size_t hashlen = 0; | |
341 | mbedtls_md_type_t md_type; | |
342 | bool spki = false; | |
343 | ||
344 | switch(method) | |
345 | { | |
346 | case RB_SSL_CERTFP_METH_CERT_SHA1: | |
347 | md_type = MBEDTLS_MD_SHA1; | |
348 | hashlen = RB_SSL_CERTFP_LEN_SHA1; | |
349 | break; | |
350 | case RB_SSL_CERTFP_METH_SPKI_SHA256: | |
351 | spki = true; | |
352 | case RB_SSL_CERTFP_METH_CERT_SHA256: | |
353 | md_type = MBEDTLS_MD_SHA256; | |
354 | hashlen = RB_SSL_CERTFP_LEN_SHA256; | |
355 | break; | |
356 | case RB_SSL_CERTFP_METH_SPKI_SHA512: | |
357 | spki = true; | |
358 | case RB_SSL_CERTFP_METH_CERT_SHA512: | |
359 | md_type = MBEDTLS_MD_SHA512; | |
360 | hashlen = RB_SSL_CERTFP_LEN_SHA512; | |
361 | break; | |
362 | default: | |
363 | return 0; | |
364 | } | |
365 | ||
366 | const mbedtls_md_info_t *const md_info = mbedtls_md_info_from_type(md_type); | |
367 | if(md_info == NULL) | |
368 | return 0; | |
369 | ||
370 | int ret; | |
371 | void* data = peer_cert->raw.p; | |
372 | size_t datalen = peer_cert->raw.len; | |
373 | ||
374 | if(spki) | |
375 | { | |
376 | // Compiler may complain about dropping const qualifier on the cast below | |
377 | // See <https://github.com/ARMmbed/mbedtls/issues/396> -- this is okay | |
378 | ||
379 | unsigned char der_pubkey[8192]; | |
380 | if((ret = mbedtls_pk_write_pubkey_der((mbedtls_pk_context *)&peer_cert->pk, | |
381 | der_pubkey, sizeof der_pubkey)) < 0) | |
382 | { | |
383 | rb_lib_log("rb_get_ssl_certfp: pk_write_pubkey_der: %s", rb_ssl_strerror(ret)); | |
384 | return 0; | |
385 | } | |
386 | data = der_pubkey + (sizeof(der_pubkey) - (size_t)ret); | |
387 | datalen = (size_t)ret; | |
388 | } | |
389 | ||
390 | if((ret = mbedtls_md(md_info, data, datalen, certfp)) != 0) | |
391 | { | |
392 | rb_lib_log("rb_get_ssl_certfp: mbedtls_md: %s", rb_ssl_strerror(ret)); | |
393 | return 0; | |
394 | } | |
395 | ||
396 | return (int) hashlen; | |
397 | } | |
398 | ||
399 | ||
400 | ||
401 | /* | |
402 | * External MbedTLS-specific code | |
403 | */ | |
404 | ||
405 | void | |
406 | rb_ssl_shutdown(rb_fde_t *const F) | |
407 | { | |
408 | if(F == NULL || F->ssl == NULL) | |
409 | return; | |
410 | ||
411 | for(int i = 0; i < 4; i++) | |
412 | { | |
413 | int ret = mbedtls_ssl_close_notify(SSL_P(F)); | |
414 | ||
415 | if(ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) | |
416 | break; | |
417 | } | |
418 | ||
419 | mbedtls_ssl_free(SSL_P(F)); | |
420 | rb_mbedtls_cfg_decref(SSL_C(F)); | |
421 | ||
422 | rb_free(F->ssl); | |
423 | F->ssl = NULL; | |
424 | } | |
425 | ||
426 | int | |
427 | rb_init_ssl(void) | |
428 | { | |
429 | mbedtls_ctr_drbg_init(&ctr_drbg_ctx); | |
430 | mbedtls_entropy_init(&entropy_ctx); | |
431 | ||
432 | int ret; | |
433 | ||
434 | if((ret = mbedtls_ctr_drbg_seed(&ctr_drbg_ctx, mbedtls_entropy_func, &entropy_ctx, | |
435 | (const unsigned char *)rb_mbedtls_personal_str, sizeof(rb_mbedtls_personal_str))) != 0) | |
436 | { | |
437 | rb_lib_log("%s: ctr_drbg_seed: %s", __func__, rb_ssl_strerror(ret)); | |
438 | return 0; | |
439 | } | |
440 | ||
441 | if((ret = mbedtls_x509_crt_parse_der(&dummy_ca_ctx, rb_mbedtls_dummy_ca_certificate, | |
442 | sizeof(rb_mbedtls_dummy_ca_certificate))) != 0) | |
443 | { | |
444 | rb_lib_log("%s: x509_crt_parse_der (Dummy CA): %s", __func__, rb_ssl_strerror(ret)); | |
445 | return 0; | |
446 | } | |
447 | ||
448 | rb_lib_log("%s: MbedTLS backend initialised", __func__); | |
449 | return 1; | |
450 | } | |
451 | ||
452 | int | |
453 | rb_setup_ssl_server(const char *const certfile, const char *keyfile, | |
454 | const char *const dhfile, const char *const cipherlist) | |
455 | { | |
456 | if(certfile == NULL) | |
457 | { | |
458 | rb_lib_log("%s: no certificate file specified", __func__); | |
459 | return 0; | |
460 | } | |
461 | ||
462 | if(keyfile == NULL) | |
463 | keyfile = certfile; | |
464 | ||
465 | rb_mbedtls_cfg_context *const newcfg = rb_mbedtls_cfg_new(); | |
466 | ||
467 | if(newcfg == NULL) | |
468 | { | |
469 | rb_lib_log("%s: rb_mbedtls_cfg_new: allocation failed", __func__); | |
470 | return 0; | |
471 | } | |
472 | ||
473 | int ret; | |
474 | ||
475 | if((ret = mbedtls_x509_crt_parse_file(&newcfg->crt, certfile)) != 0) | |
476 | { | |
477 | rb_lib_log("%s: x509_crt_parse_file ('%s'): %s", __func__, certfile, rb_ssl_strerror(ret)); | |
478 | rb_mbedtls_cfg_decref(newcfg); | |
479 | return 0; | |
480 | } | |
481 | if((ret = mbedtls_pk_parse_keyfile(&newcfg->key, keyfile, NULL)) != 0) | |
482 | { | |
483 | rb_lib_log("%s: pk_parse_keyfile ('%s'): %s", __func__, keyfile, rb_ssl_strerror(ret)); | |
484 | rb_mbedtls_cfg_decref(newcfg); | |
485 | return 0; | |
486 | } | |
487 | if((ret = mbedtls_pk_check_pair(&newcfg->crt.pk, &newcfg->key)) != 0) | |
488 | { | |
489 | rb_lib_log("%s: pk_check_pair: public/private key mismatch", __func__); | |
490 | rb_mbedtls_cfg_decref(newcfg); | |
491 | return 0; | |
492 | } | |
493 | if((ret = mbedtls_ssl_conf_own_cert(&newcfg->server_cfg, &newcfg->crt, &newcfg->key)) != 0) | |
494 | { | |
495 | rb_lib_log("%s: ssl_conf_own_cert (server): %s", __func__, rb_ssl_strerror(ret)); | |
496 | rb_mbedtls_cfg_decref(newcfg); | |
497 | return 0; | |
498 | } | |
499 | if((ret = mbedtls_ssl_conf_own_cert(&newcfg->client_cfg, &newcfg->crt, &newcfg->key)) != 0) | |
500 | { | |
501 | rb_lib_log("%s: ssl_conf_own_cert (client): %s", __func__, rb_ssl_strerror(ret)); | |
502 | rb_mbedtls_cfg_decref(newcfg); | |
503 | return 0; | |
504 | } | |
505 | ||
506 | ||
507 | /* Absense of DH parameters does not matter with mbedTLS, as it comes with its own defaults | |
508 | Thus, clients can still use DHE- ciphersuites, just over a weaker, common DH group | |
509 | So, we do not consider failure to parse DH parameters as fatal */ | |
510 | if(dhfile == NULL) | |
511 | { | |
512 | rb_lib_log("%s: no DH parameters file specified", __func__); | |
513 | } | |
514 | else | |
515 | { | |
516 | if((ret = mbedtls_dhm_parse_dhmfile(&newcfg->dhp, dhfile)) != 0) | |
517 | { | |
518 | rb_lib_log("%s: dhm_parse_dhmfile ('%s'): %s", __func__, dhfile, rb_ssl_strerror(ret)); | |
519 | } | |
520 | else if((ret = mbedtls_ssl_conf_dh_param_ctx(&newcfg->server_cfg, &newcfg->dhp)) != 0) | |
521 | { | |
522 | rb_lib_log("%s: ssl_conf_dh_param_ctx: %s", __func__, rb_ssl_strerror(ret)); | |
523 | } | |
524 | } | |
525 | ||
526 | ||
527 | const int *rb_ciphersuites = newcfg->suites; | |
528 | size_t suites_count = 0; | |
529 | ||
530 | if(cipherlist != NULL) | |
531 | { | |
532 | // The cipherlist is (const char *) -- we should not modify it | |
533 | char *const cipherlist_dup = strdup(cipherlist); | |
534 | ||
535 | if(cipherlist_dup != NULL) | |
536 | { | |
537 | char *cipher_str = cipherlist_dup; | |
538 | char *cipher_idx; | |
539 | ||
540 | do | |
541 | { | |
542 | // Arbitrary, but the same separator as OpenSSL uses | |
543 | cipher_idx = strchr(cipher_str, ':'); | |
544 | ||
545 | // This could legitimately be NULL (last ciphersuite in the list) | |
546 | if(cipher_idx != NULL) | |
547 | *cipher_idx = '\0'; | |
548 | ||
549 | size_t cipher_len = strlen(cipher_str); | |
550 | int cipher_idn = 0; | |
551 | ||
552 | // All MbedTLS ciphersuite names begin with these 4 characters | |
553 | if(cipher_len > 4 && strncmp(cipher_str, "TLS-", 4) == 0) | |
554 | cipher_idn = mbedtls_ssl_get_ciphersuite_id(cipher_str); | |
555 | ||
556 | // Prevent the same ciphersuite being added multiple times | |
557 | for(size_t x = 0; cipher_idn != 0 && newcfg->suites[x] != 0; x++) | |
558 | if(newcfg->suites[x] == cipher_idn) | |
559 | cipher_idn = 0; | |
560 | ||
561 | // Add the suite to the list | |
562 | if(cipher_idn != 0) | |
563 | newcfg->suites[suites_count++] = cipher_idn; | |
564 | ||
565 | // Advance the string to the next entry | |
566 | if(cipher_idx) | |
567 | cipher_str = cipher_idx + 1; | |
568 | ||
569 | } while(cipher_idx && suites_count < RB_MAX_CIPHERSUITES); | |
570 | ||
571 | if(suites_count == 0) | |
572 | rb_lib_log("%s: Ciphersuites provided, but could not parse any", __func__); | |
573 | ||
574 | free(cipherlist_dup); | |
575 | } | |
576 | else | |
577 | { | |
578 | rb_lib_log("%s: strdup: %s", __func__, strerror(errno)); | |
579 | } | |
580 | } | |
581 | else | |
582 | { | |
583 | rb_lib_log("%s: No ciphersuite list provided", __func__); | |
584 | } | |
585 | ||
586 | if(suites_count == 0) | |
587 | { | |
588 | rb_lib_log("%s: Using default ciphersuites", __func__); | |
589 | ||
590 | rb_ciphersuites = rb_mbedtls_ciphersuites; | |
591 | suites_count = (sizeof(rb_mbedtls_ciphersuites) / sizeof(rb_mbedtls_ciphersuites[0])) - 1; | |
592 | } | |
593 | ||
594 | mbedtls_ssl_conf_ciphersuites(&newcfg->server_cfg, rb_ciphersuites); | |
595 | mbedtls_ssl_conf_ciphersuites(&newcfg->client_cfg, rb_ciphersuites); | |
596 | rb_lib_log("%s: Configured %zu ciphersuites", __func__, suites_count); | |
597 | ||
598 | ||
599 | rb_mbedtls_cfg_decref(rb_mbedtls_cfg); | |
600 | rb_mbedtls_cfg = newcfg; | |
601 | ||
602 | rb_lib_log("%s: TLS configuration successful", __func__); | |
603 | return 1; | |
604 | } | |
605 | ||
606 | int | |
607 | rb_init_prng(const char *const path, prng_seed_t seed_type) | |
608 | { | |
609 | (void) path; | |
610 | (void) seed_type; | |
611 | ||
612 | rb_lib_log("%s: Skipping PRNG initialisation; not required by MbedTLS backend", __func__); | |
613 | return 1; | |
614 | } | |
615 | ||
616 | int | |
617 | rb_get_random(void *const buf, const size_t length) | |
618 | { | |
619 | int ret; | |
620 | ||
621 | if((ret = mbedtls_ctr_drbg_random(&ctr_drbg_ctx, buf, length)) != 0) | |
622 | { | |
623 | rb_lib_log("%s: ctr_drbg_random: %s", __func__, rb_ssl_strerror(ret)); | |
624 | return 0; | |
625 | } | |
626 | ||
627 | return 1; | |
628 | } | |
629 | ||
630 | const char * | |
631 | rb_get_ssl_strerror(rb_fde_t *const F) | |
632 | { | |
633 | const int err = (int) F->ssl_errno; | |
634 | return rb_ssl_strerror(err); | |
635 | } | |
636 | ||
637 | int | |
638 | rb_get_ssl_certfp(rb_fde_t *const F, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method) | |
639 | { | |
640 | const mbedtls_x509_crt *const peer_cert = mbedtls_ssl_get_peer_cert(SSL_P(F)); | |
641 | ||
642 | if(peer_cert == NULL) | |
643 | return 0; | |
644 | ||
645 | return rb_make_certfp(peer_cert, certfp, method); | |
646 | } | |
647 | ||
648 | int | |
649 | rb_get_ssl_certfp_file(const char *const filename, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method) | |
650 | { | |
651 | mbedtls_x509_crt cert; | |
652 | ||
653 | mbedtls_x509_crt_init(&cert); | |
654 | ||
655 | const int ret = mbedtls_x509_crt_parse_file(&cert, filename); | |
656 | ||
657 | if(ret != 0) | |
658 | return -1; | |
659 | ||
660 | const int len = rb_make_certfp(&cert, certfp, method); | |
661 | ||
662 | mbedtls_x509_crt_free(&cert); | |
663 | ||
664 | return len; | |
665 | } | |
666 | void | |
667 | rb_get_ssl_info(char *const buf, const size_t len) | |
668 | { | |
669 | char version_str[512]; | |
670 | ||
671 | mbedtls_version_get_string(version_str); | |
672 | ||
673 | (void) snprintf(buf, len, "ARM mbedTLS: compiled (v%s), library (v%s)", | |
674 | MBEDTLS_VERSION_STRING, version_str); | |
675 | } | |
676 | ||
677 | const char * | |
678 | rb_ssl_get_cipher(rb_fde_t *const F) | |
679 | { | |
680 | if(F == NULL || F->ssl == NULL) | |
681 | return NULL; | |
682 | ||
683 | static char buf[512]; | |
684 | ||
685 | const char *const version = mbedtls_ssl_get_version(SSL_P(F)); | |
686 | const char *const cipher = mbedtls_ssl_get_ciphersuite(SSL_P(F)); | |
687 | ||
688 | (void) snprintf(buf, sizeof buf, "%s, %s", version, cipher); | |
689 | ||
690 | return buf; | |
691 | } | |
692 | ||
693 | ssize_t | |
694 | rb_ssl_read(rb_fde_t *const F, void *const buf, const size_t count) | |
695 | { | |
696 | lrb_assert(F != NULL); | |
697 | lrb_assert(F->ssl != NULL); | |
698 | ||
699 | const int ret = mbedtls_ssl_read(SSL_P(F), buf, count); | |
700 | ||
701 | if(ret >= 0) | |
702 | return (ssize_t) ret; | |
703 | ||
704 | switch(ret) | |
705 | { | |
706 | case MBEDTLS_ERR_SSL_WANT_READ: | |
707 | errno = EAGAIN; | |
708 | return RB_RW_SSL_NEED_READ; | |
709 | case MBEDTLS_ERR_SSL_WANT_WRITE: | |
710 | errno = EAGAIN; | |
711 | return RB_RW_SSL_NEED_WRITE; | |
712 | default: | |
713 | errno = EIO; | |
714 | F->ssl_errno = (unsigned long) -ret; | |
715 | return RB_RW_SSL_ERROR; | |
716 | } | |
717 | } | |
718 | ||
719 | ssize_t | |
720 | rb_ssl_write(rb_fde_t *const F, const void *const buf, const size_t count) | |
721 | { | |
722 | lrb_assert(F != NULL); | |
723 | lrb_assert(F->ssl != NULL); | |
724 | ||
725 | const int ret = mbedtls_ssl_write(SSL_P(F), buf, count); | |
726 | ||
727 | if(ret >= 0) | |
728 | return (ssize_t) ret; | |
729 | ||
730 | switch(ret) | |
731 | { | |
732 | case MBEDTLS_ERR_SSL_WANT_READ: | |
733 | errno = EAGAIN; | |
734 | return RB_RW_SSL_NEED_READ; | |
735 | case MBEDTLS_ERR_SSL_WANT_WRITE: | |
736 | errno = EAGAIN; | |
737 | return RB_RW_SSL_NEED_WRITE; | |
738 | default: | |
739 | errno = EIO; | |
740 | F->ssl_errno = (unsigned long) -ret; | |
741 | return RB_RW_SSL_ERROR; | |
742 | } | |
743 | } | |
744 | ||
745 | ||
746 | ||
747 | /* | |
748 | * Internal library-agnostic code | |
749 | */ | |
750 | ||
751 | static void | |
752 | rb_ssl_connect_realcb(rb_fde_t *const F, const int status, struct ssl_connect *const sconn) | |
753 | { | |
754 | lrb_assert(F != NULL); | |
755 | lrb_assert(F->connect != NULL); | |
756 | ||
757 | F->connect->callback = sconn->callback; | |
758 | F->connect->data = sconn->data; | |
759 | ||
760 | rb_connect_callback(F, status); | |
761 | rb_free(sconn); | |
762 | } | |
763 | ||
764 | static void | |
765 | rb_ssl_timeout_cb(rb_fde_t *const F, void *const data) | |
766 | { | |
767 | (void) data; | |
768 | ||
769 | lrb_assert(F->accept != NULL); | |
770 | lrb_assert(F->accept->callback != NULL); | |
771 | ||
772 | F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data); | |
773 | } | |
774 | ||
775 | static void | |
776 | rb_ssl_tryconn_timeout_cb(rb_fde_t *const F, void *const data) | |
777 | { | |
778 | rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data); | |
779 | } | |
780 | ||
781 | static void | |
782 | rb_ssl_tryconn(rb_fde_t *const F, const int status, void *const data) | |
783 | { | |
784 | lrb_assert(F != NULL); | |
785 | ||
786 | struct ssl_connect *const sconn = data; | |
787 | ||
788 | if(status != RB_OK) | |
789 | { | |
790 | rb_ssl_connect_realcb(F, status, sconn); | |
791 | return; | |
792 | } | |
793 | ||
794 | F->type |= RB_FD_SSL; | |
795 | ||
796 | rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn); | |
797 | rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_OUT); | |
798 | rb_ssl_connect_common(F, sconn); | |
799 | } | |
800 | ||
801 | ||
802 | ||
803 | /* | |
804 | * External library-agnostic code | |
805 | */ | |
806 | ||
807 | int | |
808 | rb_supports_ssl(void) | |
809 | { | |
810 | return 1; | |
811 | } | |
812 | ||
813 | unsigned int | |
814 | rb_ssl_handshake_count(rb_fde_t *const F) | |
815 | { | |
816 | return F->handshake_count; | |
817 | } | |
818 | ||
819 | void | |
820 | rb_ssl_clear_handshake_count(rb_fde_t *const F) | |
821 | { | |
822 | F->handshake_count = 0; | |
823 | } | |
824 | ||
825 | void | |
826 | rb_ssl_start_accepted(rb_fde_t *const F, ACCB *const cb, void *const data, const int timeout) | |
827 | { | |
828 | F->type |= RB_FD_SSL; | |
829 | ||
830 | F->accept = rb_malloc(sizeof(struct acceptdata)); | |
831 | F->accept->callback = cb; | |
832 | F->accept->data = data; | |
833 | F->accept->addrlen = 0; | |
834 | (void) memset(&F->accept->S, 0x00, sizeof F->accept->S); | |
835 | ||
836 | rb_settimeout(F, timeout, rb_ssl_timeout_cb, NULL); | |
837 | rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_IN); | |
838 | rb_ssl_accept_common(F, NULL); | |
839 | } | |
840 | ||
841 | void | |
842 | rb_ssl_accept_setup(rb_fde_t *const srv_F, rb_fde_t *const cli_F, struct sockaddr *const st, const int addrlen) | |
843 | { | |
844 | cli_F->type |= RB_FD_SSL; | |
845 | ||
846 | cli_F->accept = rb_malloc(sizeof(struct acceptdata)); | |
847 | cli_F->accept->callback = srv_F->accept->callback; | |
848 | cli_F->accept->data = srv_F->accept->data; | |
849 | cli_F->accept->addrlen = (rb_socklen_t) addrlen; | |
850 | (void) memset(&cli_F->accept->S, 0x00, sizeof cli_F->accept->S); | |
851 | (void) memcpy(&cli_F->accept->S, st, (size_t) addrlen); | |
852 | ||
853 | rb_settimeout(cli_F, 10, rb_ssl_timeout_cb, NULL); | |
854 | rb_ssl_init_fd(cli_F, RB_FD_TLS_DIRECTION_IN); | |
855 | rb_ssl_accept_common(cli_F, NULL); | |
856 | } | |
857 | ||
858 | int | |
859 | rb_ssl_listen(rb_fde_t *const F, const int backlog, const int defer_accept) | |
860 | { | |
861 | int result = rb_listen(F, backlog, defer_accept); | |
862 | ||
863 | F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL; | |
864 | ||
865 | return result; | |
866 | } | |
867 | ||
868 | void | |
869 | rb_connect_tcp_ssl(rb_fde_t *const F, struct sockaddr *const dest, struct sockaddr *const clocal, | |
870 | CNCB *const callback, void *const data, const int timeout) | |
871 | { | |
872 | if(F == NULL) | |
873 | return; | |
874 | ||
875 | struct ssl_connect *const sconn = rb_malloc(sizeof *sconn); | |
876 | sconn->data = data; | |
877 | sconn->callback = callback; | |
878 | sconn->timeout = timeout; | |
879 | ||
880 | rb_connect_tcp(F, dest, clocal, rb_ssl_tryconn, sconn, timeout); | |
881 | } | |
882 | ||
883 | void | |
884 | rb_ssl_start_connected(rb_fde_t *const F, CNCB *const callback, void *const data, const int timeout) | |
885 | { | |
886 | if(F == NULL) | |
887 | return; | |
888 | ||
889 | struct ssl_connect *const sconn = rb_malloc(sizeof *sconn); | |
890 | sconn->data = data; | |
891 | sconn->callback = callback; | |
892 | sconn->timeout = timeout; | |
893 | ||
894 | F->connect = rb_malloc(sizeof(struct conndata)); | |
895 | F->connect->callback = callback; | |
896 | F->connect->data = data; | |
897 | ||
898 | F->type |= RB_FD_SSL; | |
899 | ||
900 | rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn); | |
901 | rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_OUT); | |
902 | rb_ssl_connect_common(F, sconn); | |
903 | } | |
904 | ||
905 | #endif /* HAVE_MBEDTLS */ |