]> jfr.im git - solanum.git/blob - librb/src/gnutls.c
GNUTLS: Forward-port release/3.5 improvements
[solanum.git] / librb / src / gnutls.c
1 /*
2 * libratbox: a library used by ircd-ratbox and other things
3 * gnutls.c: gnutls related code
4 *
5 * Copyright (C) 2007-2008 ircd-ratbox development team
6 * Copyright (C) 2007-2008 Aaron Sethman <androsyn@ratbox.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21 * USA
22 *
23 */
24
25 #include <librb_config.h>
26 #include <rb_lib.h>
27
28 #ifdef HAVE_GNUTLS
29
30 #include <commio-int.h>
31 #include <commio-ssl.h>
32 #include <stdbool.h>
33
34 #include <gnutls/gnutls.h>
35 #include <gnutls/abstract.h>
36 #include <gnutls/x509.h>
37
38 #if (GNUTLS_VERSION_MAJOR < 3)
39 # include <gcrypt.h>
40 #else
41 # include <gnutls/crypto.h>
42 #endif
43
44 typedef enum
45 {
46 RB_FD_TLS_DIRECTION_IN = 0,
47 RB_FD_TLS_DIRECTION_OUT = 1
48 } rb_fd_tls_direction;
49
50 #define SSL_P(x) *((gnutls_session_t *) ((x)->ssl))
51
52
53
54 // Server side variables
55 static gnutls_certificate_credentials_t server_cert_key;
56 static gnutls_dh_params_t server_dhp;
57
58 // Client side variables
59 #define MAX_CERTS 6
60 static gnutls_x509_crt_t client_cert[MAX_CERTS];
61 static gnutls_x509_privkey_t client_key;
62 static unsigned int client_cert_count;
63
64 // Shared variables
65 static gnutls_priority_t default_priority;
66
67
68
69 struct ssl_connect
70 {
71 CNCB *callback;
72 void *data;
73 int timeout;
74 };
75
76 static const char *rb_ssl_strerror(int);
77 static void rb_ssl_connect_realcb(rb_fde_t *, int, struct ssl_connect *);
78
79 static ssize_t rb_sock_net_recv(gnutls_transport_ptr_t, void *, size_t);
80 static ssize_t rb_sock_net_xmit(gnutls_transport_ptr_t, const void *, size_t);
81
82
83
84 /*
85 * Internal GNUTLS-specific code
86 */
87
88 /*
89 * We only have one certificate to authenticate with, as both a client and server.
90 *
91 * Unfortunately, GNUTLS tries to be clever, and as client, will attempt to use a certificate that the server will
92 * trust. We usually use self-signed certs, though, so the result of this search is always nothing. Therefore, it
93 * uses no certificate to authenticate as a client. This is undesirable, as it breaks fingerprint authentication;
94 * e.g. the connect::fingerprint on the remote ircd will not match.
95 *
96 * Thus, we use this callback to force GNUTLS to authenticate with our (server) certificate as a client.
97 */
98 static int
99 rb_ssl_cert_auth_cb(gnutls_session_t session,
100 const gnutls_datum_t *const req_ca_rdn, const int req_ca_rdn_len,
101 const gnutls_pk_algorithm_t *const sign_algos, const int sign_algos_len,
102 #if (GNUTLS_VERSION_MAJOR < 3)
103 gnutls_retr_st *const st)
104 #else
105 gnutls_retr2_st *const st)
106 #endif
107 {
108 #if (GNUTLS_VERSION_MAJOR < 3)
109 st->type = GNUTLS_CRT_X509;
110 #else
111 st->cert_type = GNUTLS_CRT_X509;
112 st->key_type = GNUTLS_PRIVKEY_X509;
113 #endif
114
115 st->ncerts = client_cert_count;
116 st->cert.x509 = client_cert;
117 st->key.x509 = client_key;
118 st->deinit_all = 0;
119
120 return 0;
121 }
122
123 static void
124 rb_ssl_init_fd(rb_fde_t *const F, const rb_fd_tls_direction dir)
125 {
126 F->ssl = rb_malloc(sizeof(gnutls_session_t));
127
128 if(F->ssl == NULL)
129 {
130 rb_lib_log("%s: rb_malloc: allocation failure", __func__);
131 rb_close(F);
132 return;
133 }
134
135 unsigned int init_flags = 0;
136
137 switch(dir)
138 {
139 case RB_FD_TLS_DIRECTION_IN:
140 init_flags |= GNUTLS_SERVER;
141 break;
142 case RB_FD_TLS_DIRECTION_OUT:
143 init_flags |= GNUTLS_CLIENT;
144 break;
145 }
146
147 gnutls_init((gnutls_session_t *) F->ssl, init_flags);
148 gnutls_set_default_priority(SSL_P(F));
149 gnutls_credentials_set(SSL_P(F), GNUTLS_CRD_CERTIFICATE, server_cert_key);
150 gnutls_dh_set_prime_bits(SSL_P(F), 2048);
151 gnutls_priority_set(SSL_P(F), default_priority);
152
153 gnutls_transport_set_ptr(SSL_P(F), (gnutls_transport_ptr_t) F);
154 gnutls_transport_set_pull_function(SSL_P(F), rb_sock_net_recv);
155 gnutls_transport_set_push_function(SSL_P(F), rb_sock_net_xmit);
156
157 if(dir == RB_FD_TLS_DIRECTION_IN)
158 gnutls_certificate_server_set_request(SSL_P(F), GNUTLS_CERT_REQUEST);
159 }
160
161 static void
162 rb_ssl_accept_common(rb_fde_t *const F, void *const data)
163 {
164 lrb_assert(F != NULL);
165 lrb_assert(F->accept != NULL);
166 lrb_assert(F->accept->callback != NULL);
167 lrb_assert(F->ssl != NULL);
168
169 errno = 0;
170
171 const int ret = gnutls_handshake(SSL_P(F));
172 const int err = errno;
173
174 if(ret == GNUTLS_E_AGAIN || (ret == GNUTLS_E_INTERRUPTED && (err == 0 || rb_ignore_errno(err))))
175 {
176 unsigned int flags = (gnutls_record_get_direction(SSL_P(F)) == 0) ? RB_SELECT_READ : RB_SELECT_WRITE;
177 rb_setselect(F, flags, rb_ssl_accept_common, data);
178 return;
179 }
180
181 // These 2 calls may affect errno, which is why we save it above and restore it below
182 rb_settimeout(F, 0, NULL, NULL);
183 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL);
184
185 struct acceptdata *const ad = F->accept;
186 F->accept = NULL;
187
188 if(ret == GNUTLS_E_SUCCESS)
189 {
190 F->handshake_count++;
191 ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data);
192 }
193 else if(ret == GNUTLS_E_INTERRUPTED && err != 0)
194 {
195 errno = err;
196 ad->callback(F, RB_ERROR, NULL, 0, ad->data);
197 }
198 else
199 {
200 errno = EIO;
201 F->ssl_errno = (unsigned long) -ret;
202 ad->callback(F, RB_ERROR_SSL, NULL, 0, ad->data);
203 }
204
205 rb_free(ad);
206 }
207
208 static void
209 rb_ssl_connect_common(rb_fde_t *const F, void *const data)
210 {
211 lrb_assert(F != NULL);
212 lrb_assert(F->ssl != NULL);
213
214 errno = 0;
215
216 const int ret = gnutls_handshake(SSL_P(F));
217 const int err = errno;
218
219 if(ret == GNUTLS_E_AGAIN || (ret == GNUTLS_E_INTERRUPTED && (err == 0 || rb_ignore_errno(err))))
220 {
221 unsigned int flags = (gnutls_record_get_direction(SSL_P(F)) == 0) ? RB_SELECT_READ : RB_SELECT_WRITE;
222 rb_setselect(F, flags, rb_ssl_connect_common, data);
223 return;
224 }
225
226 // These 2 calls may affect errno, which is why we save it above and restore it below
227 rb_settimeout(F, 0, NULL, NULL);
228 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL);
229
230 struct ssl_connect *const sconn = data;
231
232 if(ret == GNUTLS_E_SUCCESS)
233 {
234 F->handshake_count++;
235 rb_ssl_connect_realcb(F, RB_OK, sconn);
236 }
237 else if(ret == GNUTLS_E_INTERRUPTED && err != 0)
238 {
239 errno = err;
240 rb_ssl_connect_realcb(F, RB_ERROR, sconn);
241 }
242 else
243 {
244 errno = EIO;
245 F->ssl_errno = (unsigned long) -ret;
246 rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
247 }
248 }
249
250 static const char *
251 rb_ssl_strerror(const int err)
252 {
253 return gnutls_strerror(err);
254 }
255
256 static ssize_t
257 rb_ssl_read_or_write(const int r_or_w, rb_fde_t *const F, void *const rbuf, const void *const wbuf, const size_t count)
258 {
259 ssize_t ret;
260
261 errno = 0;
262
263 if(r_or_w == 0)
264 ret = gnutls_record_recv(SSL_P(F), rbuf, count);
265 else
266 ret = gnutls_record_send(SSL_P(F), wbuf, count);
267
268 if(ret >= 0)
269 return ret;
270
271 if(ret == GNUTLS_E_AGAIN || (ret == GNUTLS_E_INTERRUPTED && (errno == 0 || rb_ignore_errno(errno))))
272 {
273 if(gnutls_record_get_direction(SSL_P(F)) == 0)
274 return RB_RW_SSL_NEED_READ;
275 else
276 return RB_RW_SSL_NEED_WRITE;
277 }
278
279 if(ret == GNUTLS_E_INTERRUPTED && errno != 0)
280 return RB_RW_IO_ERROR;
281
282 errno = EIO;
283 F->ssl_errno = (unsigned long) -ret;
284 return RB_RW_SSL_ERROR;
285 }
286
287 #if (GNUTLS_VERSION_MAJOR < 3)
288 static void
289 rb_gcry_random_seed(void *const unused)
290 {
291 gcry_fast_random_poll();
292 }
293 #endif
294
295 static void
296 rb_free_datum_t(gnutls_datum_t *const datum)
297 {
298 if(datum == NULL)
299 return;
300
301 rb_free(datum->data);
302 rb_free(datum);
303 }
304
305 static gnutls_datum_t *
306 rb_load_file_into_datum_t(const char *const file)
307 {
308 const int datum_fd = open(file, O_RDONLY);
309 if(datum_fd < 0)
310 return NULL;
311
312 struct stat fileinfo;
313 if(fstat(datum_fd, &fileinfo) != 0)
314 {
315 (void) close(datum_fd);
316 return NULL;
317 }
318
319 const size_t datum_size = (fileinfo.st_size < 131072) ? (size_t) fileinfo.st_size : 131072;
320 if(datum_size == 0)
321 {
322 (void) close(datum_fd);
323 return NULL;
324 }
325
326 gnutls_datum_t *datum;
327 if((datum = rb_malloc(sizeof *datum)) == NULL)
328 {
329 (void) close(datum_fd);
330 return NULL;
331 }
332 if((datum->data = rb_malloc(datum_size + 1)) == NULL)
333 {
334 rb_free(datum);
335 (void) close(datum_fd);
336 return NULL;
337 }
338
339 for(size_t data_read = 0; data_read < datum_size; )
340 {
341 ssize_t ret = read(datum_fd, ((unsigned char *)datum->data) + data_read, datum_size - data_read);
342
343 if(ret <= 0)
344 {
345 rb_free_datum_t(datum);
346 (void) close(datum_fd);
347 return NULL;
348 }
349
350 data_read += (size_t) ret;
351 }
352 (void) close(datum_fd);
353
354 datum->data[datum_size] = '\0';
355 datum->size = (unsigned int) datum_size;
356 return datum;
357 }
358
359 static int
360 make_certfp(gnutls_x509_crt_t cert, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method)
361 {
362 int hashlen;
363 gnutls_digest_algorithm_t md_type;
364
365 bool spki = false;
366
367 switch(method)
368 {
369 case RB_SSL_CERTFP_METH_CERT_SHA1:
370 hashlen = RB_SSL_CERTFP_LEN_SHA1;
371 md_type = GNUTLS_DIG_SHA1;
372 break;
373 case RB_SSL_CERTFP_METH_SPKI_SHA256:
374 spki = true;
375 case RB_SSL_CERTFP_METH_CERT_SHA256:
376 hashlen = RB_SSL_CERTFP_LEN_SHA256;
377 md_type = GNUTLS_DIG_SHA256;
378 break;
379 case RB_SSL_CERTFP_METH_SPKI_SHA512:
380 spki = true;
381 case RB_SSL_CERTFP_METH_CERT_SHA512:
382 hashlen = RB_SSL_CERTFP_LEN_SHA512;
383 md_type = GNUTLS_DIG_SHA512;
384 break;
385 default:
386 return 0;
387 }
388
389 if(! spki)
390 {
391 size_t digest_size = (size_t) hashlen;
392
393 if(gnutls_x509_crt_get_fingerprint(cert, md_type, certfp, &digest_size) != 0)
394 return 0;
395
396 return hashlen;
397 }
398
399 gnutls_pubkey_t pubkey;
400
401 if(gnutls_pubkey_init(&pubkey) != 0)
402 return 0;
403
404 if(gnutls_pubkey_import_x509(pubkey, cert, 0) != 0)
405 {
406 gnutls_pubkey_deinit(pubkey);
407 return 0;
408 }
409
410 unsigned char derkey[262144]; // Should be big enough to hold any SubjectPublicKeyInfo structure
411 size_t derkey_len = sizeof derkey;
412
413 if(gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, derkey, &derkey_len) != 0)
414 {
415 gnutls_pubkey_deinit(pubkey);
416 return 0;
417 }
418
419 gnutls_pubkey_deinit(pubkey);
420
421 if(gnutls_hash_fast(md_type, derkey, derkey_len, certfp) != 0)
422 return 0;
423
424 return hashlen;
425 }
426
427
428
429 /*
430 * External GNUTLS-specific code
431 */
432
433 void
434 rb_ssl_shutdown(rb_fde_t *const F)
435 {
436 if(F == NULL || F->ssl == NULL)
437 return;
438
439 for(int i = 0; i < 4; i++)
440 {
441 int ret = gnutls_bye(SSL_P(F), GNUTLS_SHUT_RDWR);
442
443 if(ret != GNUTLS_E_INTERRUPTED && ret != GNUTLS_E_AGAIN)
444 break;
445 }
446
447 gnutls_deinit(SSL_P(F));
448
449 rb_free(F->ssl);
450 F->ssl = NULL;
451 }
452
453 int
454 rb_init_ssl(void)
455 {
456 int ret;
457
458 if((ret = gnutls_global_init()) != GNUTLS_E_SUCCESS)
459 {
460 rb_lib_log("%s: gnutls_global_init: %s", __func__, rb_ssl_strerror(ret));
461 return 0;
462 }
463
464 #if (GNUTLS_VERSION_MAJOR < 3)
465 rb_event_addish("rb_gcry_random_seed", rb_gcry_random_seed, NULL, 300);
466 #endif
467
468 return 1;
469 }
470
471 int
472 rb_setup_ssl_server(const char *const certfile, const char *keyfile,
473 const char *const dhfile, const char *const cipherlist)
474 {
475 if(certfile == NULL)
476 {
477 rb_lib_log("%s: no certificate file specified", __func__);
478 return 0;
479 }
480
481 if(keyfile == NULL)
482 keyfile = certfile;
483
484
485 gnutls_datum_t *const d_cert = rb_load_file_into_datum_t(certfile);
486 if(d_cert == NULL)
487 {
488 rb_lib_log("%s: Error loading certificate: %s", __func__, strerror(errno));
489 return 0;
490 }
491
492 gnutls_datum_t *const d_key = rb_load_file_into_datum_t(keyfile);
493 if(d_key == NULL)
494 {
495 rb_lib_log("%s: Error loading key: %s", __func__, strerror(errno));
496 rb_free_datum_t(d_cert);
497 return 0;
498 }
499
500 int ret;
501
502 if((ret = gnutls_certificate_allocate_credentials(&server_cert_key)) != GNUTLS_E_SUCCESS)
503 {
504 rb_lib_log("%s: gnutls_certificate_allocate_credentials: %s", __func__, rb_ssl_strerror(ret));
505 rb_free_datum_t(d_cert);
506 rb_free_datum_t(d_key);
507 return 0;
508 }
509
510 #if (GNUTLS_VERSION_MAJOR < 3)
511 gnutls_certificate_client_set_retrieve_function(server_cert_key, rb_ssl_cert_auth_cb);
512 #else
513 gnutls_certificate_set_retrieve_function(server_cert_key, rb_ssl_cert_auth_cb);
514 #endif
515
516 if((ret = gnutls_certificate_set_x509_key_mem(server_cert_key, d_cert, d_key,
517 GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS)
518 {
519 rb_lib_log("%s: gnutls_certificate_set_x509_key_mem: %s", __func__, rb_ssl_strerror(ret));
520 gnutls_certificate_free_credentials(server_cert_key);
521 rb_free_datum_t(d_cert);
522 rb_free_datum_t(d_key);
523 return 0;
524 }
525 if((ret = gnutls_x509_crt_list_import(client_cert, &client_cert_count, d_cert, GNUTLS_X509_FMT_PEM,
526 GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED)) < 1)
527 {
528 rb_lib_log("%s: gnutls_x509_crt_list_import: %s", __func__, rb_ssl_strerror(ret));
529 gnutls_certificate_free_credentials(server_cert_key);
530 rb_free_datum_t(d_cert);
531 rb_free_datum_t(d_key);
532 return 0;
533 }
534 client_cert_count = (unsigned int) ret;
535
536 if((ret = gnutls_x509_privkey_init(&client_key)) != GNUTLS_E_SUCCESS)
537 {
538 rb_lib_log("%s: gnutls_x509_privkey_init: %s", __func__, rb_ssl_strerror(ret));
539 gnutls_certificate_free_credentials(server_cert_key);
540 for(unsigned int i = 0; i < client_cert_count; i++)
541 gnutls_x509_crt_deinit(client_cert[i]);
542 rb_free_datum_t(d_cert);
543 rb_free_datum_t(d_key);
544 return 0;
545 }
546 if((ret = gnutls_x509_privkey_import(client_key, d_key, GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS)
547 {
548 rb_lib_log("%s: gnutls_x509_privkey_import: %s", __func__, rb_ssl_strerror(ret));
549 gnutls_certificate_free_credentials(server_cert_key);
550 for(unsigned int i = 0; i < client_cert_count; i++)
551 gnutls_x509_crt_deinit(client_cert[i]);
552 gnutls_x509_privkey_deinit(client_key);
553 rb_free_datum_t(d_cert);
554 rb_free_datum_t(d_key);
555 return 0;
556 }
557
558 rb_free_datum_t(d_cert);
559 rb_free_datum_t(d_key);
560
561 if(dhfile != NULL)
562 {
563 gnutls_datum_t *const d_dhp = rb_load_file_into_datum_t(dhfile);
564
565 if(d_dhp == NULL)
566 {
567 rb_lib_log("%s: Error parsing DH parameters: %s", __func__, strerror(errno));
568 gnutls_certificate_free_credentials(server_cert_key);
569 for(unsigned int i = 0; i < client_cert_count; i++)
570 gnutls_x509_crt_deinit(client_cert[i]);
571 gnutls_x509_privkey_deinit(client_key);
572 return 0;
573 }
574 if((ret = gnutls_dh_params_init(&server_dhp)) != GNUTLS_E_SUCCESS)
575 {
576 rb_lib_log("%s: Error parsing DH parameters: %s", __func__, rb_ssl_strerror(ret));
577 gnutls_certificate_free_credentials(server_cert_key);
578 for(unsigned int i = 0; i < client_cert_count; i++)
579 gnutls_x509_crt_deinit(client_cert[i]);
580 gnutls_x509_privkey_deinit(client_key);
581 rb_free_datum_t(d_dhp);
582 return 0;
583 }
584 if((ret = gnutls_dh_params_import_pkcs3(server_dhp, d_dhp, GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS)
585 {
586 rb_lib_log("%s: Error parsing DH parameters: %s", __func__, rb_ssl_strerror(ret));
587 gnutls_certificate_free_credentials(server_cert_key);
588 for(unsigned int i = 0; i < client_cert_count; i++)
589 gnutls_x509_crt_deinit(client_cert[i]);
590 gnutls_x509_privkey_deinit(client_key);
591 gnutls_dh_params_deinit(server_dhp);
592 rb_free_datum_t(d_dhp);
593 return 0;
594 }
595
596 gnutls_certificate_set_dh_params(server_cert_key, server_dhp);
597 rb_free_datum_t(d_dhp);
598 }
599
600 const char *err = NULL;
601 if((ret = gnutls_priority_init(&default_priority, cipherlist, &err)) != GNUTLS_E_SUCCESS)
602 {
603 rb_lib_log("%s: gnutls_priority_init: %s, error begins at '%s'? -- using defaults instead",
604 __func__, rb_ssl_strerror(ret), err ? err : "<unknown>");
605
606 (void) gnutls_priority_init(&default_priority, NULL, &err);
607 }
608
609 rb_lib_log("%s: TLS configuration successful", __func__);
610 return 1;
611 }
612
613 int
614 rb_init_prng(const char *const path, prng_seed_t seed_type)
615 {
616 #if (GNUTLS_VERSION_MAJOR < 3)
617 gcry_fast_random_poll();
618 rb_lib_log("%s: PRNG initialised", __func__);
619 #else
620 rb_lib_log("%s: Skipping PRNG initialisation; not required by GNUTLS v3+ backend", __func__);
621 #endif
622 return 1;
623 }
624
625 int
626 rb_get_random(void *const buf, const size_t length)
627 {
628 #if (GNUTLS_VERSION_MAJOR < 3)
629 gcry_randomize(buf, length, GCRY_STRONG_RANDOM);
630 #else
631 gnutls_rnd(GNUTLS_RND_KEY, buf, length);
632 #endif
633 return 1;
634 }
635
636 const char *
637 rb_get_ssl_strerror(rb_fde_t *const F)
638 {
639 const int err = (int) F->ssl_errno;
640 return rb_ssl_strerror(-err);
641 }
642
643 int
644 rb_get_ssl_certfp(rb_fde_t *const F, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method)
645 {
646 if(gnutls_certificate_type_get(SSL_P(F)) != GNUTLS_CRT_X509)
647 return 0;
648
649 unsigned int cert_list_size = 0;
650 const gnutls_datum_t *const cert_list = gnutls_certificate_get_peers(SSL_P(F), &cert_list_size);
651 if(cert_list == NULL || cert_list_size < 1)
652 return 0;
653
654 gnutls_x509_crt_t peer_cert;
655 if(gnutls_x509_crt_init(&peer_cert) != 0)
656 return 0;
657
658 if(gnutls_x509_crt_import(peer_cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
659 {
660 gnutls_x509_crt_deinit(peer_cert);
661 return 0;
662 }
663
664 const int len = make_certfp(peer_cert, certfp, method);
665
666 gnutls_x509_crt_deinit(peer_cert);
667
668 return len;
669 }
670
671 int
672 rb_get_ssl_certfp_file(const char *const filename, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method)
673 {
674 gnutls_datum_t *const datum_cert = rb_load_file_into_datum_t(filename);
675 if(datum_cert == NULL)
676 return -1;
677
678 gnutls_x509_crt_t cert;
679 if(gnutls_x509_crt_init(&cert) != 0)
680 {
681 rb_free_datum_t(datum_cert);
682 return 0;
683 }
684 if(gnutls_x509_crt_import(cert, datum_cert, GNUTLS_X509_FMT_PEM) < 0)
685 {
686 gnutls_x509_crt_deinit(cert);
687 rb_free_datum_t(datum_cert);
688 return 0;
689 }
690
691 const int len = make_certfp(cert, certfp, method);
692
693 gnutls_x509_crt_deinit(cert);
694 rb_free_datum_t(datum_cert);
695
696 return len;
697 }
698
699 void
700 rb_get_ssl_info(char *const buf, const size_t len)
701 {
702 (void) snprintf(buf, len, "GNUTLS: compiled (v%s), library (v%s)",
703 LIBGNUTLS_VERSION, gnutls_check_version(NULL));
704 }
705
706 const char *
707 rb_ssl_get_cipher(rb_fde_t *const F)
708 {
709 if(F == NULL || F->ssl == NULL)
710 return NULL;
711
712 static char buf[512];
713
714 gnutls_protocol_t version_ptr = gnutls_protocol_get_version(SSL_P(F));
715 gnutls_cipher_algorithm_t cipher_ptr = gnutls_cipher_get(SSL_P(F));
716
717 const char *const version = gnutls_protocol_get_name(version_ptr);
718 const char *const cipher = gnutls_cipher_get_name(cipher_ptr);
719
720 (void) snprintf(buf, sizeof buf, "%s, %s", version, cipher);
721
722 return buf;
723 }
724
725 ssize_t
726 rb_ssl_read(rb_fde_t *const F, void *const buf, const size_t count)
727 {
728 return rb_ssl_read_or_write(0, F, buf, NULL, count);
729 }
730
731 ssize_t
732 rb_ssl_write(rb_fde_t *const F, const void *const buf, const size_t count)
733 {
734 return rb_ssl_read_or_write(1, F, NULL, buf, count);
735 }
736
737
738
739 /*
740 * Internal library-agnostic code
741 */
742
743 static void
744 rb_ssl_connect_realcb(rb_fde_t *const F, const int status, struct ssl_connect *const sconn)
745 {
746 lrb_assert(F != NULL);
747 lrb_assert(F->connect != NULL);
748
749 F->connect->callback = sconn->callback;
750 F->connect->data = sconn->data;
751
752 rb_connect_callback(F, status);
753 rb_free(sconn);
754 }
755
756 static void
757 rb_ssl_timeout_cb(rb_fde_t *const F, void *const data)
758 {
759 lrb_assert(F->accept != NULL);
760 lrb_assert(F->accept->callback != NULL);
761
762 F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data);
763 }
764
765 static void
766 rb_ssl_tryconn_timeout_cb(rb_fde_t *const F, void *const data)
767 {
768 rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data);
769 }
770
771 static void
772 rb_ssl_tryconn(rb_fde_t *const F, const int status, void *const data)
773 {
774 lrb_assert(F != NULL);
775
776 struct ssl_connect *const sconn = data;
777
778 if(status != RB_OK)
779 {
780 rb_ssl_connect_realcb(F, status, sconn);
781 return;
782 }
783
784 F->type |= RB_FD_SSL;
785
786 rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
787 rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_OUT);
788 rb_ssl_connect_common(F, sconn);
789 }
790
791 static ssize_t
792 rb_sock_net_recv(gnutls_transport_ptr_t context_ptr, void *const buf, const size_t count)
793 {
794 const int fd = rb_get_fd((rb_fde_t *)context_ptr);
795
796 return recv(fd, buf, count, 0);
797 }
798
799 static ssize_t
800 rb_sock_net_xmit(gnutls_transport_ptr_t context_ptr, const void *const buf, const size_t count)
801 {
802 const int fd = rb_get_fd((rb_fde_t *)context_ptr);
803
804 return send(fd, buf, count, 0);
805 }
806
807
808
809 /*
810 * External library-agnostic code
811 */
812
813 int
814 rb_supports_ssl(void)
815 {
816 return 1;
817 }
818
819 unsigned int
820 rb_ssl_handshake_count(rb_fde_t *const F)
821 {
822 return F->handshake_count;
823 }
824
825 void
826 rb_ssl_clear_handshake_count(rb_fde_t *const F)
827 {
828 F->handshake_count = 0;
829 }
830
831 void
832 rb_ssl_start_accepted(rb_fde_t *const F, ACCB *const cb, void *const data, const int timeout)
833 {
834 F->type |= RB_FD_SSL;
835
836 F->accept = rb_malloc(sizeof(struct acceptdata));
837 F->accept->callback = cb;
838 F->accept->data = data;
839 F->accept->addrlen = 0;
840 (void) memset(&F->accept->S, 0x00, sizeof F->accept->S);
841
842 rb_settimeout(F, timeout, rb_ssl_timeout_cb, NULL);
843 rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_IN);
844 rb_ssl_accept_common(F, NULL);
845 }
846
847 void
848 rb_ssl_accept_setup(rb_fde_t *const srv_F, rb_fde_t *const cli_F, struct sockaddr *const st, const int addrlen)
849 {
850 cli_F->type |= RB_FD_SSL;
851
852 cli_F->accept = rb_malloc(sizeof(struct acceptdata));
853 cli_F->accept->callback = srv_F->accept->callback;
854 cli_F->accept->data = srv_F->accept->data;
855 cli_F->accept->addrlen = (rb_socklen_t) addrlen;
856 (void) memset(&cli_F->accept->S, 0x00, sizeof cli_F->accept->S);
857 (void) memcpy(&cli_F->accept->S, st, (size_t) addrlen);
858
859 rb_settimeout(cli_F, 10, rb_ssl_timeout_cb, NULL);
860 rb_ssl_init_fd(cli_F, RB_FD_TLS_DIRECTION_IN);
861 rb_ssl_accept_common(cli_F, NULL);
862 }
863
864 int
865 rb_ssl_listen(rb_fde_t *const F, const int backlog, const int defer_accept)
866 {
867 int result = rb_listen(F, backlog, defer_accept);
868
869 F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL;
870
871 return result;
872 }
873
874 void
875 rb_connect_tcp_ssl(rb_fde_t *const F, struct sockaddr *const dest, struct sockaddr *const clocal,
876 CNCB *const callback, void *const data, const int timeout)
877 {
878 if(F == NULL)
879 return;
880
881 struct ssl_connect *const sconn = rb_malloc(sizeof *sconn);
882 sconn->data = data;
883 sconn->callback = callback;
884 sconn->timeout = timeout;
885
886 rb_connect_tcp(F, dest, clocal, rb_ssl_tryconn, sconn, timeout);
887 }
888
889 void
890 rb_ssl_start_connected(rb_fde_t *const F, CNCB *const callback, void *const data, const int timeout)
891 {
892 if(F == NULL)
893 return;
894
895 struct ssl_connect *const sconn = rb_malloc(sizeof *sconn);
896 sconn->data = data;
897 sconn->callback = callback;
898 sconn->timeout = timeout;
899
900 F->connect = rb_malloc(sizeof(struct conndata));
901 F->connect->callback = callback;
902 F->connect->data = data;
903
904 F->type |= RB_FD_SSL;
905
906 rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
907 rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_OUT);
908 rb_ssl_connect_common(F, sconn);
909 }
910
911 #endif /* HAVE_GNUTLS */