2 * libratbox: a library used by ircd-ratbox and other things
3 * gnutls.c: gnutls related code
5 * Copyright (C) 2007-2008 ircd-ratbox development team
6 * Copyright (C) 2007-2008 Aaron Sethman <androsyn@ratbox.org>
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.
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.
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
23 * $Id: gnutls.c 26092 2008-09-19 15:13:52Z androsyn $
26 #include <libratbox_config.h>
27 #include <ratbox_lib.h>
28 #include <commio-int.h>
29 #include <commio-ssl.h>
32 #include <gnutls/gnutls.h>
35 static gnutls_certificate_credentials x509
;
36 static gnutls_dh_params dh_params
;
40 #define SSL_P(x) *((gnutls_session_t *)F->ssl)
43 rb_ssl_shutdown(rb_fde_t
*F
)
46 if(F
== NULL
|| F
->ssl
== NULL
)
48 for(i
= 0; i
< 4; i
++)
50 if(gnutls_bye(SSL_P(F
), GNUTLS_SHUT_RDWR
) == GNUTLS_E_SUCCESS
)
53 gnutls_deinit(SSL_P(F
));
58 rb_ssl_handshake_count(rb_fde_t
*F
)
60 return F
->handshake_count
;
64 rb_ssl_clear_handshake_count(rb_fde_t
*F
)
66 F
->handshake_count
= 0;
70 rb_ssl_timeout(rb_fde_t
*F
, void *notused
)
72 lrb_assert(F
->accept
!= NULL
);
73 F
->accept
->callback(F
, RB_ERR_TIMEOUT
, NULL
, 0, F
->accept
->data
);
78 do_ssl_handshake(rb_fde_t
*F
, PF
* callback
)
83 ret
= gnutls_handshake(SSL_P(F
));
86 if((ret
== GNUTLS_E_INTERRUPTED
&& rb_ignore_errno(errno
)) || ret
== GNUTLS_E_AGAIN
)
88 if(gnutls_record_get_direction(SSL_P(F
)) == 0)
89 flags
= RB_SELECT_READ
;
91 flags
= RB_SELECT_WRITE
;
92 rb_setselect(F
, flags
, callback
, NULL
);
98 return 1; /* handshake is finished..go about life */
102 rb_ssl_tryaccept(rb_fde_t
*F
, void *data
)
105 struct acceptdata
*ad
;
107 lrb_assert(F
->accept
!= NULL
);
109 ret
= do_ssl_handshake(F
, rb_ssl_tryaccept
);
114 F
->accept
->callback(F
, RB_ERROR_SSL
, NULL
, 0, F
->accept
->data
);
117 /* do_ssl_handshake does the rb_setselect stuff */
124 rb_settimeout(F
, 0, NULL
, NULL
);
125 rb_setselect(F
, RB_SELECT_READ
| RB_SELECT_WRITE
, NULL
, NULL
);
129 ad
->callback(F
, RB_OK
, (struct sockaddr
*)&ad
->S
, ad
->addrlen
, ad
->data
);
135 rb_ssl_start_accepted(rb_fde_t
*new_F
, ACCB
* cb
, void *data
, int timeout
)
137 gnutls_session_t
*ssl
;
138 new_F
->type
|= RB_FD_SSL
;
139 ssl
= new_F
->ssl
= rb_malloc(sizeof(gnutls_session_t
));
140 new_F
->accept
= rb_malloc(sizeof(struct acceptdata
));
142 new_F
->accept
->callback
= cb
;
143 new_F
->accept
->data
= data
;
144 rb_settimeout(new_F
, timeout
, rb_ssl_timeout
, NULL
);
146 new_F
->accept
->addrlen
= 0;
148 gnutls_init(ssl
, GNUTLS_SERVER
);
149 gnutls_set_default_priority(*ssl
);
150 gnutls_credentials_set(*ssl
, GNUTLS_CRD_CERTIFICATE
, x509
);
151 gnutls_dh_set_prime_bits(*ssl
, 1024);
152 gnutls_transport_set_ptr(*ssl
, (gnutls_transport_ptr_t
) (long int)new_F
->fd
);
153 if(do_ssl_handshake(new_F
, rb_ssl_tryaccept
))
155 struct acceptdata
*ad
= new_F
->accept
;
156 new_F
->accept
= NULL
;
157 ad
->callback(new_F
, RB_OK
, (struct sockaddr
*)&ad
->S
, ad
->addrlen
, ad
->data
);
167 rb_ssl_accept_setup(rb_fde_t
*F
, rb_fde_t
*new_F
, struct sockaddr
*st
, int addrlen
)
169 new_F
->type
|= RB_FD_SSL
;
170 new_F
->ssl
= rb_malloc(sizeof(gnutls_session_t
));
171 new_F
->accept
= rb_malloc(sizeof(struct acceptdata
));
173 new_F
->accept
->callback
= F
->accept
->callback
;
174 new_F
->accept
->data
= F
->accept
->data
;
175 rb_settimeout(new_F
, 10, rb_ssl_timeout
, NULL
);
176 memcpy(&new_F
->accept
->S
, st
, addrlen
);
177 new_F
->accept
->addrlen
= addrlen
;
179 gnutls_init((gnutls_session_t
*) new_F
->ssl
, GNUTLS_SERVER
);
180 gnutls_set_default_priority(SSL_P(new_F
));
181 gnutls_credentials_set(SSL_P(new_F
), GNUTLS_CRD_CERTIFICATE
, x509
);
182 gnutls_dh_set_prime_bits(SSL_P(new_F
), 1024);
183 gnutls_transport_set_ptr(SSL_P(new_F
), (gnutls_transport_ptr_t
) (long int)rb_get_fd(new_F
));
184 if(do_ssl_handshake(F
, rb_ssl_tryaccept
))
186 struct acceptdata
*ad
= F
->accept
;
188 ad
->callback(F
, RB_OK
, (struct sockaddr
*)&ad
->S
, ad
->addrlen
, ad
->data
);
197 rb_ssl_read_or_write(int r_or_w
, rb_fde_t
*F
, void *rbuf
, const void *wbuf
, size_t count
)
200 gnutls_session_t
*ssl
= F
->ssl
;
203 ret
= gnutls_record_recv(*ssl
, rbuf
, count
);
205 ret
= gnutls_record_send(*ssl
, wbuf
, count
);
212 case GNUTLS_E_INTERRUPTED
:
213 if(rb_ignore_errno(errno
))
215 if(gnutls_record_get_direction(*ssl
) == 0)
216 return RB_RW_SSL_NEED_READ
;
218 return RB_RW_SSL_NEED_WRITE
;
224 return RB_RW_IO_ERROR
;
231 rb_ssl_read(rb_fde_t
*F
, void *buf
, size_t count
)
233 return rb_ssl_read_or_write(0, F
, buf
, NULL
, count
);
237 rb_ssl_write(rb_fde_t
*F
, const void *buf
, size_t count
)
239 return rb_ssl_read_or_write(1, F
, NULL
, buf
, count
);
243 rb_gcry_random_seed(void *unused
)
245 gcry_fast_random_poll();
251 gnutls_global_init();
253 if(gnutls_certificate_allocate_credentials(&x509
) != GNUTLS_E_SUCCESS
)
255 rb_lib_log("rb_init_ssl: Unable to allocate SSL/TLS certificate credentials");
258 rb_event_addish("rb_gcry_random_seed", rb_gcry_random_seed
, NULL
, 300);
263 rb_free_datum_t(gnutls_datum_t
* d
)
269 static gnutls_datum_t
*
270 rb_load_file_into_datum_t(const char *file
)
273 gnutls_datum_t
*datum
;
274 struct stat fileinfo
;
275 if((f
= fopen(file
, "r")) == NULL
)
277 if(fstat(fileno(f
), &fileinfo
))
280 datum
= rb_malloc(sizeof(gnutls_datum_t
));
282 if(fileinfo
.st_size
> 131072) /* deal with retards */
283 datum
->size
= 131072;
285 datum
->size
= fileinfo
.st_size
;
287 datum
->data
= rb_malloc(datum
->size
+ 1);
288 fread(datum
->data
, datum
->size
, 1, f
);
294 rb_setup_ssl_server(const char *cert
, const char *keyfile
, const char *dhfile
)
297 gnutls_datum_t
*d_cert
, *d_key
;
300 rb_lib_log("rb_setup_ssl_server: No certificate file");
304 if((d_cert
= rb_load_file_into_datum_t(cert
)) == NULL
)
306 rb_lib_log("rb_setup_ssl_server: Error loading certificate: %s", strerror(errno
));
310 if((d_key
= rb_load_file_into_datum_t(keyfile
)) == NULL
)
312 rb_lib_log("rb_setup_ssl_server: Error loading key: %s", strerror(errno
));
318 gnutls_certificate_set_x509_key_mem(x509
, d_cert
, d_key
,
319 GNUTLS_X509_FMT_PEM
)) != GNUTLS_E_SUCCESS
)
321 rb_lib_log("rb_setup_ssl_server: Error loading certificate or key file: %s",
322 gnutls_strerror(ret
));
325 rb_free_datum_t(d_cert
);
326 rb_free_datum_t(d_key
);
330 if(gnutls_dh_params_init(&dh_params
) == GNUTLS_E_SUCCESS
)
332 gnutls_datum_t
*data
;
334 data
= rb_load_file_into_datum_t(dhfile
);
337 xret
= gnutls_dh_params_import_pkcs3(dh_params
, data
,
338 GNUTLS_X509_FMT_PEM
);
341 ("rb_setup_ssl_server: Error parsing DH file: %s\n",
342 gnutls_strerror(xret
));
343 rb_free_datum_t(data
);
345 gnutls_certificate_set_dh_params(x509
, dh_params
);
348 rb_lib_log("rb_setup_ssl_server: Unable to setup DH parameters");
354 rb_ssl_listen(rb_fde_t
*F
, int backlog
)
356 F
->type
= RB_FD_SOCKET
| RB_FD_LISTEN
| RB_FD_SSL
;
357 return listen(F
->fd
, backlog
);
368 rb_ssl_connect_realcb(rb_fde_t
*F
, int status
, struct ssl_connect
*sconn
)
370 F
->connect
->callback
= sconn
->callback
;
371 F
->connect
->data
= sconn
->data
;
373 rb_connect_callback(F
, status
);
377 rb_ssl_tryconn_timeout_cb(rb_fde_t
*F
, void *data
)
379 rb_ssl_connect_realcb(F
, RB_ERR_TIMEOUT
, data
);
383 rb_ssl_tryconn_cb(rb_fde_t
*F
, void *data
)
385 struct ssl_connect
*sconn
= data
;
388 ret
= do_ssl_handshake(F
, rb_ssl_tryconn_cb
);
393 rb_ssl_connect_realcb(F
, RB_ERROR_SSL
, sconn
);
396 /* do_ssl_handshake does the rb_setselect stuff */
403 rb_ssl_connect_realcb(F
, RB_OK
, sconn
);
407 rb_ssl_tryconn(rb_fde_t
*F
, int status
, void *data
)
409 struct ssl_connect
*sconn
= data
;
412 rb_ssl_connect_realcb(F
, status
, sconn
);
416 F
->type
|= RB_FD_SSL
;
419 rb_settimeout(F
, sconn
->timeout
, rb_ssl_tryconn_timeout_cb
, sconn
);
420 F
->ssl
= rb_malloc(sizeof(gnutls_session_t
));
421 gnutls_init(F
->ssl
, GNUTLS_CLIENT
);
422 gnutls_set_default_priority(SSL_P(F
));
423 gnutls_dh_set_prime_bits(SSL_P(F
), 1024);
424 gnutls_transport_set_ptr(SSL_P(F
), (gnutls_transport_ptr_t
) (long int)F
->fd
);
426 if(do_ssl_handshake(F
, rb_ssl_tryconn_cb
))
428 rb_ssl_connect_realcb(F
, RB_OK
, sconn
);
433 rb_connect_tcp_ssl(rb_fde_t
*F
, struct sockaddr
*dest
,
434 struct sockaddr
*clocal
, int socklen
, CNCB
* callback
, void *data
, int timeout
)
436 struct ssl_connect
*sconn
;
440 sconn
= rb_malloc(sizeof(struct ssl_connect
));
442 sconn
->callback
= callback
;
443 sconn
->timeout
= timeout
;
444 rb_connect_tcp(F
, dest
, clocal
, socklen
, rb_ssl_tryconn
, sconn
, timeout
);
449 rb_ssl_start_connected(rb_fde_t
*F
, CNCB
* callback
, void *data
, int timeout
)
451 struct ssl_connect
*sconn
;
455 sconn
= rb_malloc(sizeof(struct ssl_connect
));
457 sconn
->callback
= callback
;
458 sconn
->timeout
= timeout
;
459 F
->connect
= rb_malloc(sizeof(struct conndata
));
460 F
->connect
->callback
= callback
;
461 F
->connect
->data
= data
;
462 F
->type
|= RB_FD_SSL
;
463 F
->ssl
= rb_malloc(sizeof(gnutls_session_t
));
465 gnutls_init(F
->ssl
, GNUTLS_CLIENT
);
466 gnutls_set_default_priority(SSL_P(F
));
467 gnutls_dh_set_prime_bits(SSL_P(F
), 1024);
468 gnutls_transport_set_ptr(SSL_P(F
), (gnutls_transport_ptr_t
) (long int)F
->fd
);
470 rb_settimeout(F
, sconn
->timeout
, rb_ssl_tryconn_timeout_cb
, sconn
);
472 if(do_ssl_handshake(F
, rb_ssl_tryconn_cb
))
474 rb_ssl_connect_realcb(F
, RB_OK
, sconn
);
479 rb_init_prng(const char *path
, prng_seed_t seed_type
)
481 gcry_fast_random_poll();
486 rb_get_random(void *buf
, size_t length
)
488 gcry_randomize(buf
, length
, GCRY_STRONG_RANDOM
);
493 rb_get_pseudo_random(void *buf
, size_t length
)
495 gcry_randomize(buf
, length
, GCRY_WEAK_RANDOM
);
500 rb_get_ssl_strerror(rb_fde_t
*F
)
502 return gnutls_strerror(F
->ssl_errno
);
506 rb_supports_ssl(void)
511 #endif /* HAVE_GNUTLS */