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>
7 * Copyright (C) 2008 William Pitcock <nenolod@nenolod.net>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
24 * $Id: commio.c 24808 2008-01-02 08:17:05Z androsyn $
27 #include <libratbox_config.h>
28 #include <ratbox_lib.h>
32 #include <commio-int.h>
33 #include <commio-ssl.h>
34 #include <gnutls/gnutls.h>
36 static gnutls_certificate_credentials_t x509_cred
;
37 static gnutls_dh_params_t dh_params
;
40 rb_ssl_shutdown(rb_fde_t
* F
)
42 if(F
== NULL
|| F
->ssl
== NULL
)
45 gnutls_bye((gnutls_session_t
) F
->ssl
, GNUTLS_SHUT_RDWR
);
46 gnutls_deinit((gnutls_session_t
) F
->ssl
);
50 rb_ssl_timeout(rb_fde_t
* F
, void *notused
)
52 lrb_assert(F
->accept
!= NULL
);
53 F
->accept
->callback(F
, RB_ERR_TIMEOUT
, NULL
, 0, F
->accept
->data
);
57 rb_ssl_tryaccept(rb_fde_t
* F
, void *data
)
60 lrb_assert(F
->accept
!= NULL
);
62 struct acceptdata
*ad
;
64 if((ssl_err
= gnutls_handshake((gnutls_session_t
) F
->ssl
)) != 0)
68 case GNUTLS_E_INTERRUPTED
:
69 if(rb_ignore_errno(errno
))
72 if(gnutls_record_get_direction((gnutls_session_t
) F
->ssl
))
73 flags
= RB_SELECT_WRITE
;
75 flags
= RB_SELECT_READ
;
77 F
->ssl_errno
= ssl_err
;
78 rb_setselect(F
, flags
, rb_ssl_tryaccept
, NULL
);
83 F
->ssl_errno
= ssl_err
;
84 F
->accept
->callback(F
, RB_ERROR_SSL
, NULL
, 0, F
->accept
->data
);
89 rb_settimeout(F
, 0, NULL
, NULL
);
90 rb_setselect(F
, RB_SELECT_READ
| RB_SELECT_WRITE
, NULL
, NULL
);
94 ad
->callback(F
, RB_OK
, (struct sockaddr
*) &ad
->S
, ad
->addrlen
,
100 rb_ssl_start_accepted(rb_fde_t
* new_F
, ACCB
* cb
, void *data
, int timeout
)
102 gnutls_session_t sess
;
105 new_F
->type
|= RB_FD_SSL
;
107 gnutls_init(&sess
, GNUTLS_SERVER
);
108 gnutls_set_default_priority(sess
);
109 gnutls_credentials_set(sess
, GNUTLS_CRD_CERTIFICATE
, x509_cred
);
110 gnutls_dh_set_prime_bits(sess
, 1024);
111 gnutls_certificate_server_set_request(sess
, GNUTLS_CERT_REQUEST
);
115 new_F
->accept
= rb_malloc(sizeof(struct acceptdata
));
117 new_F
->accept
->callback
= cb
;
118 new_F
->accept
->data
= data
;
119 rb_settimeout(new_F
, timeout
, rb_ssl_timeout
, NULL
);
121 new_F
->accept
->addrlen
= 0;
123 gnutls_transport_set_ptr((gnutls_session_t
) new_F
->ssl
, (gnutls_transport_ptr_t
) rb_get_fd(new_F
));
125 if((ssl_err
= gnutls_handshake((gnutls_session_t
) new_F
->ssl
)) != 0)
129 case GNUTLS_E_INTERRUPTED
:
130 if(rb_ignore_errno(errno
))
135 if(gnutls_record_get_direction((gnutls_session_t
) new_F
->ssl
))
136 flags
= RB_SELECT_WRITE
;
138 flags
= RB_SELECT_READ
;
140 new_F
->ssl_errno
= ssl_err
;
141 rb_setselect(new_F
, flags
, rb_ssl_tryaccept
, NULL
);
146 new_F
->ssl_errno
= ssl_err
;
147 new_F
->accept
->callback(new_F
, RB_ERROR_SSL
, NULL
, 0, new_F
->accept
->data
);
153 struct acceptdata
*ad
;
155 rb_settimeout(new_F
, 0, NULL
, NULL
);
156 rb_setselect(new_F
, RB_SELECT_READ
| RB_SELECT_WRITE
, NULL
, NULL
);
159 new_F
->accept
= NULL
;
160 ad
->callback(new_F
, RB_OK
, (struct sockaddr
*) &ad
->S
, ad
->addrlen
,
167 rb_ssl_accept_setup(rb_fde_t
* F
, int new_fd
, struct sockaddr
*st
, int addrlen
)
169 gnutls_session_t sess
;
173 new_F
= rb_find_fd(new_fd
);
175 gnutls_init(&sess
, GNUTLS_SERVER
);
176 gnutls_set_default_priority(sess
);
177 gnutls_credentials_set(sess
, GNUTLS_CRD_CERTIFICATE
, x509_cred
);
178 gnutls_dh_set_prime_bits(sess
, 1024);
179 gnutls_certificate_server_set_request(sess
, GNUTLS_CERT_REQUEST
);
181 new_F
->type
|= RB_FD_SSL
;
182 new_F
->accept
= rb_malloc(sizeof(struct acceptdata
));
184 new_F
->accept
->callback
= F
->accept
->callback
;
185 new_F
->accept
->data
= F
->accept
->data
;
186 rb_settimeout(new_F
, 10, rb_ssl_timeout
, NULL
);
187 memcpy(&new_F
->accept
->S
, st
, addrlen
);
188 new_F
->accept
->addrlen
= addrlen
;
190 gnutls_transport_set_ptr((gnutls_session_t
) new_F
->ssl
, (gnutls_transport_ptr_t
) rb_get_fd(new_F
));
191 if((ssl_err
= gnutls_handshake((gnutls_session_t
) new_F
->ssl
)) != 0)
195 case GNUTLS_E_INTERRUPTED
:
196 if(rb_ignore_errno(errno
))
201 if(gnutls_record_get_direction((gnutls_session_t
) new_F
->ssl
))
202 flags
= RB_SELECT_WRITE
;
204 flags
= RB_SELECT_READ
;
206 new_F
->ssl_errno
= ssl_err
;
207 rb_setselect(new_F
, flags
, rb_ssl_tryaccept
, NULL
);
212 new_F
->ssl_errno
= ssl_err
;
213 new_F
->accept
->callback(new_F
, RB_ERROR_SSL
, NULL
, 0, new_F
->accept
->data
);
219 struct acceptdata
*ad
;
221 rb_settimeout(new_F
, 0, NULL
, NULL
);
222 rb_setselect(new_F
, RB_SELECT_READ
| RB_SELECT_WRITE
, NULL
, NULL
);
225 new_F
->accept
= NULL
;
226 ad
->callback(new_F
, RB_OK
, (struct sockaddr
*) &ad
->S
, ad
->addrlen
,
233 rb_ssl_read_or_write(int r_or_w
, rb_fde_t
* F
, void *rbuf
, const void *wbuf
, size_t count
)
237 gnutls_session_t ssl
= F
->ssl
;
240 ret
= gnutls_record_recv(ssl
, rbuf
, count
);
242 ret
= gnutls_record_send(ssl
, wbuf
, count
);
250 if (gnutls_record_get_direction(ssl
))
251 return RB_RW_SSL_NEED_WRITE
;
253 return RB_RW_SSL_NEED_READ
;
254 case GNUTLS_E_INTERRUPTED
:
259 return RB_RW_IO_ERROR
;
269 errno
= EIO
; /* not great but... */
270 return RB_RW_SSL_ERROR
;
272 return RB_RW_IO_ERROR
;
278 rb_ssl_read(rb_fde_t
* F
, void *buf
, size_t count
)
280 return rb_ssl_read_or_write(0, F
, buf
, NULL
, count
);
284 rb_ssl_write(rb_fde_t
* F
, const void *buf
, size_t count
)
286 return rb_ssl_read_or_write(1, F
, NULL
, buf
, count
);
294 gnutls_global_init();
296 if((g_ret
= gnutls_dh_params_generate2(dh_params
, 1024)) < 0)
298 rb_lib_log("rb_init_gnutls: Failed to generate GNUTLS DH params: %s", gnutls_strerror(g_ret
));
302 gnutls_certificate_set_dh_params(x509_cred
, dh_params
);
308 rb_setup_ssl_server(const char *cert
, const char *keyfile
, const char *dhfile
)
312 if((ret
= gnutls_certificate_set_x509_key_file(x509_cred
, cert
, keyfile
, GNUTLS_X509_FMT_PEM
)) < 0)
314 rb_lib_log("rb_setup_ssl_server: Setting x509 keys up failed: %s", gnutls_strerror(ret
));
322 rb_ssl_listen(rb_fde_t
* F
, int backlog
)
324 F
->type
= RB_FD_SOCKET
| RB_FD_LISTEN
| RB_FD_SSL
;
325 return listen(F
->fd
, backlog
);
336 rb_ssl_connect_realcb(rb_fde_t
* F
, int status
, struct ssl_connect
*sconn
)
338 F
->connect
->callback
= sconn
->callback
;
339 F
->connect
->data
= sconn
->data
;
341 rb_connect_callback(F
, status
);
345 rb_ssl_tryconn_timeout_cb(rb_fde_t
* F
, void *data
)
347 rb_ssl_connect_realcb(F
, RB_ERR_TIMEOUT
, data
);
351 rb_ssl_tryconn_cb(rb_fde_t
* F
, void *data
)
353 struct ssl_connect
*sconn
= data
;
356 if((ssl_err
= gnutls_handshake((gnutls_session_t
) F
->ssl
)) != 0)
360 case GNUTLS_E_INTERRUPTED
:
361 if(rb_ignore_errno(errno
))
364 F
->ssl_errno
= ssl_err
;
365 rb_setselect(F
, RB_SELECT_READ
| RB_SELECT_WRITE
,
366 rb_ssl_tryconn_cb
, sconn
);
370 F
->ssl_errno
= ssl_err
;
371 rb_ssl_connect_realcb(F
, RB_ERROR_SSL
, sconn
);
377 rb_ssl_connect_realcb(F
, RB_OK
, sconn
);
382 rb_ssl_tryconn(rb_fde_t
* F
, int status
, void *data
)
384 gnutls_session_t sess
;
385 struct ssl_connect
*sconn
= data
;
390 rb_ssl_connect_realcb(F
, status
, sconn
);
394 F
->type
|= RB_FD_SSL
;
396 gnutls_init(&sess
, GNUTLS_CLIENT
);
397 gnutls_set_default_priority(sess
);
398 gnutls_credentials_set(sess
, GNUTLS_CRD_CERTIFICATE
, x509_cred
);
399 gnutls_dh_set_prime_bits(sess
, 1024);
400 gnutls_transport_set_ptr(sess
, (gnutls_transport_ptr_t
) F
->fd
);
404 rb_settimeout(F
, sconn
->timeout
, rb_ssl_tryconn_timeout_cb
, sconn
);
405 if((ssl_err
= gnutls_handshake((gnutls_session_t
) F
->ssl
)) != 0)
409 case GNUTLS_E_INTERRUPTED
:
410 if(rb_ignore_errno(errno
))
413 F
->ssl_errno
= ssl_err
;
414 rb_setselect(F
, RB_SELECT_READ
| RB_SELECT_WRITE
,
415 rb_ssl_tryconn_cb
, sconn
);
419 F
->ssl_errno
= ssl_err
;
420 rb_ssl_connect_realcb(F
, RB_ERROR_SSL
, sconn
);
426 rb_ssl_connect_realcb(F
, RB_OK
, sconn
);
431 rb_connect_tcp_ssl(rb_fde_t
* F
, struct sockaddr
*dest
,
432 struct sockaddr
*clocal
, int socklen
, CNCB
* callback
, void *data
, int timeout
)
434 struct ssl_connect
*sconn
;
438 sconn
= rb_malloc(sizeof(struct ssl_connect
));
440 sconn
->callback
= callback
;
441 sconn
->timeout
= timeout
;
442 rb_connect_tcp(F
, dest
, clocal
, socklen
, rb_ssl_tryconn
, sconn
, timeout
);
446 rb_ssl_start_connected(rb_fde_t
* F
, CNCB
* callback
, void *data
, int timeout
)
448 gnutls_session_t sess
;
449 struct ssl_connect
*sconn
;
454 sconn
= rb_malloc(sizeof(struct ssl_connect
));
456 sconn
->callback
= callback
;
457 sconn
->timeout
= timeout
;
458 F
->connect
= rb_malloc(sizeof(struct conndata
));
459 F
->connect
->callback
= callback
;
460 F
->connect
->data
= data
;
461 F
->type
|= RB_FD_SSL
;
463 gnutls_init(&sess
, GNUTLS_CLIENT
);
464 gnutls_set_default_priority(sess
);
465 gnutls_credentials_set(sess
, GNUTLS_CRD_CERTIFICATE
, x509_cred
);
466 gnutls_dh_set_prime_bits(sess
, 1024);
467 gnutls_transport_set_ptr(sess
, (gnutls_transport_ptr_t
) F
->fd
);
471 rb_settimeout(F
, sconn
->timeout
, rb_ssl_tryconn_timeout_cb
, sconn
);
472 if((ssl_err
= gnutls_handshake((gnutls_session_t
) F
->ssl
)) != 0)
476 case GNUTLS_E_INTERRUPTED
:
477 if(rb_ignore_errno(errno
))
480 F
->ssl_errno
= ssl_err
;
481 rb_setselect(F
, RB_SELECT_READ
| RB_SELECT_WRITE
,
482 rb_ssl_tryconn_cb
, sconn
);
486 F
->ssl_errno
= ssl_err
;
487 rb_ssl_connect_realcb(F
, RB_ERROR_SSL
, sconn
);
493 rb_ssl_connect_realcb(F
, RB_OK
, sconn
);
497 /* XXX: implement me */
499 rb_init_prng(const char *path
, prng_seed_t seed_type
)
505 rb_get_random(void *buf
, size_t length
)
512 rb_get_ssl_strerror(rb_fde_t
* F
)
514 return gnutls_strerror(F
->ssl_errno
);
518 rb_supports_ssl(void)
523 #endif /* HAVE_GNUTLS */