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