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