]> jfr.im git - solanum.git/blame - librb/src/gnutls.c
cppcheck: fix various warnings/errors
[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
79static ssize_t rb_sock_net_recv(gnutls_transport_ptr_t, void *, size_t);
80static 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 */
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
123static void
56fbe141 124rb_ssl_init_fd(rb_fde_t *const F, const rb_fd_tls_direction dir)
2bd29df9 125{
56fbe141 126 F->ssl = rb_malloc(sizeof(gnutls_session_t));
2bd29df9 127
56fbe141
AJ
128 if(F->ssl == NULL)
129 {
130 rb_lib_log("%s: rb_malloc: allocation failure", __func__);
131 rb_close(F);
132 return;
133 }
2bd29df9 134
56fbe141 135 unsigned int init_flags = 0;
2bd29df9 136
56fbe141 137 switch(dir)
2bd29df9 138 {
56fbe141
AJ
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;
2bd29df9 145 }
56fbe141
AJ
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);
2bd29df9
AB
159}
160
161static void
56fbe141 162rb_ssl_accept_common(rb_fde_t *const F, void *const data)
2bd29df9 163{
56fbe141 164 lrb_assert(F != NULL);
2bd29df9 165 lrb_assert(F->accept != NULL);
56fbe141
AJ
166 lrb_assert(F->accept->callback != NULL);
167 lrb_assert(F->ssl != NULL);
2bd29df9 168
56fbe141 169 errno = 0;
2bd29df9 170
56fbe141
AJ
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);
2bd29df9 178 return;
56fbe141 179 }
2bd29df9 180
56fbe141 181 // These 2 calls may affect errno, which is why we save it above and restore it below
2bd29df9
AB
182 rb_settimeout(F, 0, NULL, NULL);
183 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL);
55abcbb2 184
56fbe141
AJ
185 struct acceptdata *const ad = F->accept;
186 F->accept = NULL;
187
188 if(ret == GNUTLS_E_SUCCESS)
189 {
190 F->handshake_count++;
2bd29df9 191 ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data);
56fbe141
AJ
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 }
2bd29df9 198 else
56fbe141
AJ
199 {
200 errno = EIO;
201 F->ssl_errno = (unsigned long) -ret;
2bd29df9 202 ad->callback(F, RB_ERROR_SSL, NULL, 0, ad->data);
56fbe141 203 }
2bd29df9
AB
204
205 rb_free(ad);
206}
207
56fbe141
AJ
208static void
209rb_ssl_connect_common(rb_fde_t *const F, void *const data)
2bd29df9 210{
56fbe141
AJ
211 lrb_assert(F != NULL);
212 lrb_assert(F->ssl != NULL);
2bd29df9 213
56fbe141 214 errno = 0;
2bd29df9 215
56fbe141
AJ
216 const int ret = gnutls_handshake(SSL_P(F));
217 const int err = errno;
2bd29df9 218
56fbe141 219 if(ret == GNUTLS_E_AGAIN || (ret == GNUTLS_E_INTERRUPTED && (err == 0 || rb_ignore_errno(err))))
2bd29df9 220 {
56fbe141
AJ
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;
2bd29df9
AB
224 }
225
56fbe141
AJ
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);
2bd29df9 229
56fbe141 230 struct ssl_connect *const sconn = data;
673ec98e 231
56fbe141 232 if(ret == GNUTLS_E_SUCCESS)
2bd29df9 233 {
56fbe141
AJ
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);
2bd29df9
AB
247 }
248}
249
56fbe141
AJ
250static const char *
251rb_ssl_strerror(const int err)
252{
253 return gnutls_strerror(err);
254}
2bd29df9
AB
255
256static ssize_t
56fbe141 257rb_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
258{
259 ssize_t ret;
56fbe141
AJ
260
261 errno = 0;
2bd29df9
AB
262
263 if(r_or_w == 0)
56fbe141 264 ret = gnutls_record_recv(SSL_P(F), rbuf, count);
2bd29df9 265 else
56fbe141 266 ret = gnutls_record_send(SSL_P(F), wbuf, count);
2bd29df9 267
56fbe141
AJ
268 if(ret >= 0)
269 return ret;
270
271 if(ret == GNUTLS_E_AGAIN || (ret == GNUTLS_E_INTERRUPTED && (errno == 0 || rb_ignore_errno(errno))))
2bd29df9 272 {
56fbe141
AJ
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;
2bd29df9 277 }
2bd29df9 278
56fbe141
AJ
279 if(ret == GNUTLS_E_INTERRUPTED && errno != 0)
280 return RB_RW_IO_ERROR;
2bd29df9 281
56fbe141
AJ
282 errno = EIO;
283 F->ssl_errno = (unsigned long) -ret;
284 return RB_RW_SSL_ERROR;
2bd29df9
AB
285}
286
c56f5979 287#if (GNUTLS_VERSION_MAJOR < 3)
2bd29df9 288static void
56fbe141 289rb_gcry_random_seed(void *const unused)
2bd29df9
AB
290{
291 gcry_fast_random_poll();
292}
c56f5979 293#endif
2bd29df9 294
56fbe141
AJ
295static void
296rb_free_datum_t(gnutls_datum_t *const datum)
2bd29df9 297{
56fbe141
AJ
298 if(datum == NULL)
299 return;
c56f5979 300
56fbe141
AJ
301 rb_free(datum->data);
302 rb_free(datum);
2bd29df9
AB
303}
304
56fbe141
AJ
305static gnutls_datum_t *
306rb_load_file_into_datum_t(const char *const file)
2bd29df9 307{
56fbe141
AJ
308 const int datum_fd = open(file, O_RDONLY);
309 if(datum_fd < 0)
310 return NULL;
2bd29df9 311
56fbe141
AJ
312 struct stat fileinfo;
313 if(fstat(datum_fd, &fileinfo) != 0)
314 {
315 (void) close(datum_fd);
316 return NULL;
317 }
2bd29df9 318
56fbe141
AJ
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 }
2bd29df9 325
2bd29df9 326 gnutls_datum_t *datum;
56fbe141
AJ
327 if((datum = rb_malloc(sizeof *datum)) == NULL)
328 {
329 (void) close(datum_fd);
2bd29df9 330 return NULL;
56fbe141
AJ
331 }
332 if((datum->data = rb_malloc(datum_size + 1)) == NULL)
333 {
334 rb_free(datum);
335 (void) close(datum_fd);
2bd29df9 336 return NULL;
56fbe141 337 }
2bd29df9 338
56fbe141
AJ
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);
2bd29df9 342
56fbe141
AJ
343 if(ret <= 0)
344 {
345 rb_free_datum_t(datum);
346 (void) close(datum_fd);
347 return NULL;
348 }
31646e89 349
56fbe141 350 data_read += (size_t) ret;
31646e89 351 }
56fbe141
AJ
352 (void) close(datum_fd);
353
354 datum->data[datum_size] = '\0';
355 datum->size = (unsigned int) datum_size;
2bd29df9
AB
356 return datum;
357}
358
56fbe141
AJ
359static int
360make_certfp(gnutls_x509_crt_t cert, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method)
2bd29df9 361{
56fbe141
AJ
362 int hashlen;
363 gnutls_digest_algorithm_t md_type;
0fe9dd41 364
56fbe141
AJ
365 bool spki = false;
366
367 switch(method)
2bd29df9 368 {
56fbe141
AJ
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:
2bd29df9
AB
386 return 0;
387 }
388
56fbe141 389 if(! spki)
2bd29df9 390 {
56fbe141
AJ
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;
2bd29df9
AB
397 }
398
56fbe141
AJ
399 gnutls_pubkey_t pubkey;
400
401 if(gnutls_pubkey_init(&pubkey) != 0)
2bd29df9 402 return 0;
2bd29df9 403
56fbe141 404 if(gnutls_pubkey_import_x509(pubkey, cert, 0) != 0)
2bd29df9 405 {
56fbe141 406 gnutls_pubkey_deinit(pubkey);
2bd29df9
AB
407 return 0;
408 }
409
56fbe141
AJ
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)
2bd29df9 414 {
56fbe141 415 gnutls_pubkey_deinit(pubkey);
2bd29df9
AB
416 return 0;
417 }
2bd29df9 418
56fbe141
AJ
419 gnutls_pubkey_deinit(pubkey);
420
421 if(gnutls_hash_fast(md_type, derkey, derkey_len, certfp) != 0)
2bd29df9 422 return 0;
2bd29df9 423
56fbe141
AJ
424 return hashlen;
425}
2bd29df9 426
c1725bda 427
56fbe141
AJ
428
429/*
430 * External GNUTLS-specific code
431 */
432
433void
434rb_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++)
673ec98e 440 {
56fbe141
AJ
441 int ret = gnutls_bye(SSL_P(F), GNUTLS_SHUT_RDWR);
442
443 if(ret != GNUTLS_E_INTERRUPTED && ret != GNUTLS_E_AGAIN)
444 break;
673ec98e
AC
445 }
446
56fbe141
AJ
447 gnutls_deinit(SSL_P(F));
448
449 rb_free(F->ssl);
450 F->ssl = NULL;
2bd29df9
AB
451}
452
453int
56fbe141 454rb_init_ssl(void)
2bd29df9 455{
56fbe141 456 int ret;
2bd29df9 457
56fbe141
AJ
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 }
2bd29df9 463
56fbe141
AJ
464#if (GNUTLS_VERSION_MAJOR < 3)
465 rb_event_addish("rb_gcry_random_seed", rb_gcry_random_seed, NULL, 300);
466#endif
2bd29df9 467
56fbe141 468 return 1;
2bd29df9
AB
469}
470
56fbe141
AJ
471int
472rb_setup_ssl_server(const char *const certfile, const char *keyfile,
473 const char *const dhfile, const char *const cipherlist)
2bd29df9 474{
56fbe141
AJ
475 if(certfile == NULL)
476 {
477 rb_lib_log("%s: no certificate file specified", __func__);
478 return 0;
479 }
2bd29df9 480
56fbe141
AJ
481 if(keyfile == NULL)
482 keyfile = certfile;
2bd29df9 483
2bd29df9 484
56fbe141
AJ
485 gnutls_datum_t *const d_cert = rb_load_file_into_datum_t(certfile);
486 if(d_cert == NULL)
2bd29df9 487 {
56fbe141
AJ
488 rb_lib_log("%s: Error loading certificate: %s", __func__, strerror(errno));
489 return 0;
2bd29df9 490 }
2bd29df9 491
56fbe141
AJ
492 gnutls_datum_t *const d_key = rb_load_file_into_datum_t(keyfile);
493 if(d_key == NULL)
2bd29df9 494 {
56fbe141
AJ
495 rb_lib_log("%s: Error loading key: %s", __func__, strerror(errno));
496 rb_free_datum_t(d_cert);
497 return 0;
2bd29df9
AB
498 }
499
56fbe141 500 int ret;
2bd29df9 501
56fbe141
AJ
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 }
2bd29df9 509
56fbe141
AJ
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
2bd29df9 515
56fbe141
AJ
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;
2bd29df9 535
56fbe141
AJ
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 }
2bd29df9 557
56fbe141
AJ
558 rb_free_datum_t(d_cert);
559 rb_free_datum_t(d_key);
2bd29df9 560
56fbe141
AJ
561 if(dhfile != NULL)
562 {
563 gnutls_datum_t *const d_dhp = rb_load_file_into_datum_t(dhfile);
2bd29df9 564
56fbe141
AJ
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 }
2bd29df9 595
56fbe141
AJ
596 gnutls_certificate_set_dh_params(server_cert_key, server_dhp);
597 rb_free_datum_t(d_dhp);
598 }
2bd29df9 599
56fbe141
AJ
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>");
2bd29df9 605
56fbe141
AJ
606 (void) gnutls_priority_init(&default_priority, NULL, &err);
607 }
2bd29df9 608
56fbe141
AJ
609 rb_lib_log("%s: TLS configuration successful", __func__);
610 return 1;
2bd29df9
AB
611}
612
613int
56fbe141 614rb_init_prng(const char *const path, prng_seed_t seed_type)
2bd29df9 615{
56fbe141 616#if (GNUTLS_VERSION_MAJOR < 3)
2bd29df9 617 gcry_fast_random_poll();
56fbe141
AJ
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__);
7aa40f6d 621#endif
2bd29df9
AB
622 return 1;
623}
624
625int
56fbe141 626rb_get_random(void *const buf, const size_t length)
2bd29df9 627{
56fbe141 628#if (GNUTLS_VERSION_MAJOR < 3)
2bd29df9 629 gcry_randomize(buf, length, GCRY_STRONG_RANDOM);
7aa40f6d
AC
630#else
631 gnutls_rnd(GNUTLS_RND_KEY, buf, length);
632#endif
2bd29df9
AB
633 return 1;
634}
635
2bd29df9 636const char *
56fbe141 637rb_get_ssl_strerror(rb_fde_t *const F)
2bd29df9 638{
56fbe141
AJ
639 const int err = (int) F->ssl_errno;
640 return rb_ssl_strerror(-err);
2bd29df9
AB
641}
642
56fbe141
AJ
643int
644rb_get_ssl_certfp(rb_fde_t *const F, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method)
2bd29df9 645{
56fbe141
AJ
646 if(gnutls_certificate_type_get(SSL_P(F)) != GNUTLS_CRT_X509)
647 return 0;
2bd29df9 648
56fbe141
AJ
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)
e6bbb410 656 return 0;
e6bbb410 657
56fbe141 658 if(gnutls_x509_crt_import(peer_cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
2bd29df9 659 {
56fbe141
AJ
660 gnutls_x509_crt_deinit(peer_cert);
661 return 0;
cf430c1a 662 }
cf430c1a 663
56fbe141 664 const int len = make_certfp(peer_cert, certfp, method);
cf430c1a 665
56fbe141 666 gnutls_x509_crt_deinit(peer_cert);
2bd29df9 667
03469187
SA
668 return len;
669}
670
671int
56fbe141 672rb_get_ssl_certfp_file(const char *const filename, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method)
03469187 673{
56fbe141
AJ
674 gnutls_datum_t *const datum_cert = rb_load_file_into_datum_t(filename);
675 if(datum_cert == NULL)
676 return -1;
03469187 677
56fbe141
AJ
678 gnutls_x509_crt_t cert;
679 if(gnutls_x509_crt_init(&cert) != 0)
03469187 680 {
56fbe141 681 rb_free_datum_t(datum_cert);
03469187
SA
682 return 0;
683 }
56fbe141 684 if(gnutls_x509_crt_import(cert, datum_cert, GNUTLS_X509_FMT_PEM) < 0)
03469187
SA
685 {
686 gnutls_x509_crt_deinit(cert);
56fbe141 687 rb_free_datum_t(datum_cert);
03469187
SA
688 return 0;
689 }
690
56fbe141
AJ
691 const int len = make_certfp(cert, certfp, method);
692
03469187 693 gnutls_x509_crt_deinit(cert);
56fbe141
AJ
694 rb_free_datum_t(datum_cert);
695
03469187
SA
696 return len;
697}
698
56fbe141
AJ
699void
700rb_get_ssl_info(char *const buf, const size_t len)
03469187 701{
56fbe141
AJ
702 (void) snprintf(buf, len, "GNUTLS: compiled (v%s), library (v%s)",
703 LIBGNUTLS_VERSION, gnutls_check_version(NULL));
704}
03469187 705
56fbe141
AJ
706const char *
707rb_ssl_get_cipher(rb_fde_t *const F)
708{
709 if(F == NULL || F->ssl == NULL)
710 return NULL;
03469187 711
56fbe141 712 static char buf[512];
03469187 713
56fbe141
AJ
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));
2bd29df9 716
56fbe141
AJ
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
725ssize_t
726rb_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
731ssize_t
732rb_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);
2bd29df9
AB
735}
736
56fbe141
AJ
737
738
739/*
740 * Internal library-agnostic code
741 */
742
743static void
744rb_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
756static void
757rb_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
765static void
766rb_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
771static void
772rb_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
791static ssize_t
792rb_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
799static ssize_t
800rb_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
2bd29df9
AB
813int
814rb_supports_ssl(void)
815{
816 return 1;
817}
818
56fbe141
AJ
819unsigned int
820rb_ssl_handshake_count(rb_fde_t *const F)
821{
822 return F->handshake_count;
823}
824
2bd29df9 825void
56fbe141 826rb_ssl_clear_handshake_count(rb_fde_t *const F)
2bd29df9 827{
56fbe141 828 F->handshake_count = 0;
2bd29df9 829}
55abcbb2 830
56fbe141
AJ
831void
832rb_ssl_start_accepted(rb_fde_t *const F, ACCB *const cb, void *const data, const int timeout)
833b2f9c 833{
56fbe141 834 F->type |= RB_FD_SSL;
833b2f9c 835
56fbe141
AJ
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);
49068542 841
56fbe141
AJ
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}
49068542 846
56fbe141
AJ
847void
848rb_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;
49068542 851
56fbe141
AJ
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);
49068542 858
56fbe141
AJ
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}
833b2f9c 863
56fbe141
AJ
864int
865rb_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
874void
875rb_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
889void
890rb_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);
833b2f9c 909}
55abcbb2 910
2bd29df9 911#endif /* HAVE_GNUTLS */