]> jfr.im git - solanum.git/blob - libratbox/src/gnutls.c
GNUTLS backend. Untested.
[solanum.git] / libratbox / src / gnutls.c
1 /*
2 * libratbox: a library used by ircd-ratbox and other things
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 * Copyright (C) 2008 William Pitcock <nenolod@nenolod.net>
8 *
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.
13 *
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.
18 *
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
22 * USA
23 *
24 * $Id: commio.c 24808 2008-01-02 08:17:05Z androsyn $
25 */
26
27 #include <libratbox_config.h>
28 #include <ratbox_lib.h>
29
30 #ifdef HAVE_GNUTLS
31
32 #include <commio-int.h>
33 #include <commio-ssl.h>
34 #include <gnutls/gnutls.h>
35
36 static gnutls_certificate_credentials_t x509_cred;
37 static gnutls_dh_params_t dh_params;
38
39 void
40 rb_ssl_shutdown(rb_fde_t * F)
41 {
42 if(F == NULL || F->ssl == NULL)
43 return;
44
45 gnutls_bye((gnutls_session_t) F->ssl, GNUTLS_SHUT_RDWR);
46 gnutls_deinit((gnutls_session_t) F->ssl);
47 }
48
49 static void
50 rb_ssl_timeout(rb_fde_t * F, void *notused)
51 {
52 lrb_assert(F->accept != NULL);
53 F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data);
54 }
55
56 static void
57 rb_ssl_tryaccept(rb_fde_t * F, void *data)
58 {
59 int ssl_err;
60 lrb_assert(F->accept != NULL);
61 int flags;
62 struct acceptdata *ad;
63
64 if((ssl_err = gnutls_handshake((gnutls_session_t) F->ssl)) != 0)
65 {
66 switch (ssl_err)
67 {
68 case GNUTLS_E_INTERRUPTED:
69 if(rb_ignore_errno(errno))
70 case GNUTLS_E_AGAIN:
71 {
72 if(gnutls_record_get_direction((gnutls_session_t) F->ssl))
73 flags = RB_SELECT_WRITE;
74 else
75 flags = RB_SELECT_READ;
76
77 F->ssl_errno = ssl_err;
78 rb_setselect(F, flags, rb_ssl_tryaccept, NULL);
79 return;
80 }
81 break;
82 default:
83 F->ssl_errno = ssl_err;
84 F->accept->callback(F, RB_ERROR_SSL, NULL, 0, F->accept->data);
85 break;
86 }
87 return;
88 }
89 rb_settimeout(F, 0, NULL, NULL);
90 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL);
91
92 ad = F->accept;
93 F->accept = NULL;
94 ad->callback(F, RB_OK, (struct sockaddr *) &ad->S, ad->addrlen,
95 ad->data);
96 rb_free(ad);
97 }
98
99 void
100 rb_ssl_start_accepted(rb_fde_t * new_F, ACCB * cb, void *data, int timeout)
101 {
102 gnutls_session_t sess;
103 int ssl_err;
104
105 new_F->type |= RB_FD_SSL;
106
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);
112
113 new_F->ssl = sess;
114
115 new_F->accept = rb_malloc(sizeof(struct acceptdata));
116
117 new_F->accept->callback = cb;
118 new_F->accept->data = data;
119 rb_settimeout(new_F, timeout, rb_ssl_timeout, NULL);
120
121 new_F->accept->addrlen = 0;
122
123 gnutls_transport_set_ptr((gnutls_session_t) new_F->ssl, (gnutls_transport_ptr_t) rb_get_fd(new_F));
124
125 if((ssl_err = gnutls_handshake((gnutls_session_t) new_F->ssl)) != 0)
126 {
127 switch(ssl_err)
128 {
129 case GNUTLS_E_INTERRUPTED:
130 if(rb_ignore_errno(errno))
131 case GNUTLS_E_AGAIN:
132 {
133 int flags;
134
135 if(gnutls_record_get_direction((gnutls_session_t) new_F->ssl))
136 flags = RB_SELECT_WRITE;
137 else
138 flags = RB_SELECT_READ;
139
140 new_F->ssl_errno = ssl_err;
141 rb_setselect(new_F, flags, rb_ssl_tryaccept, NULL);
142 return;
143 }
144 break;
145 default:
146 new_F->ssl_errno = ssl_err;
147 new_F->accept->callback(new_F, RB_ERROR_SSL, NULL, 0, new_F->accept->data);
148 return;
149 }
150 }
151 else
152 {
153 struct acceptdata *ad;
154
155 rb_settimeout(new_F, 0, NULL, NULL);
156 rb_setselect(new_F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL);
157
158 ad = new_F->accept;
159 new_F->accept = NULL;
160 ad->callback(new_F, RB_OK, (struct sockaddr *) &ad->S, ad->addrlen,
161 ad->data);
162 rb_free(ad);
163 }
164 }
165
166 void
167 rb_ssl_accept_setup(rb_fde_t * F, int new_fd, struct sockaddr *st, int addrlen)
168 {
169 gnutls_session_t sess;
170 rb_fde_t *new_F;
171 int ssl_err;
172
173 new_F = rb_find_fd(new_fd);
174
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);
180
181 new_F->type |= RB_FD_SSL;
182 new_F->accept = rb_malloc(sizeof(struct acceptdata));
183
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;
189
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)
192 {
193 switch(ssl_err)
194 {
195 case GNUTLS_E_INTERRUPTED:
196 if(rb_ignore_errno(errno))
197 case GNUTLS_E_AGAIN:
198 {
199 int flags;
200
201 if(gnutls_record_get_direction((gnutls_session_t) new_F->ssl))
202 flags = RB_SELECT_WRITE;
203 else
204 flags = RB_SELECT_READ;
205
206 new_F->ssl_errno = ssl_err;
207 rb_setselect(new_F, flags, rb_ssl_tryaccept, NULL);
208 return;
209 }
210 break;
211 default:
212 new_F->ssl_errno = ssl_err;
213 new_F->accept->callback(new_F, RB_ERROR_SSL, NULL, 0, new_F->accept->data);
214 return;
215 }
216 }
217 else
218 {
219 struct acceptdata *ad;
220
221 rb_settimeout(new_F, 0, NULL, NULL);
222 rb_setselect(new_F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL);
223
224 ad = new_F->accept;
225 new_F->accept = NULL;
226 ad->callback(new_F, RB_OK, (struct sockaddr *) &ad->S, ad->addrlen,
227 ad->data);
228 rb_free(ad);
229 }
230 }
231
232 static ssize_t
233 rb_ssl_read_or_write(int r_or_w, rb_fde_t * F, void *rbuf, const void *wbuf, size_t count)
234 {
235 ssize_t ret;
236 unsigned long err;
237 gnutls_session_t ssl = F->ssl;
238
239 if(r_or_w == 0)
240 ret = gnutls_record_recv(ssl, rbuf, count);
241 else
242 ret = gnutls_record_send(ssl, wbuf, count);
243
244 if(ret < 0)
245 {
246 switch (ret)
247 {
248 case GNUTLS_E_AGAIN:
249 errno = EAGAIN;
250 if (gnutls_record_get_direction(ssl))
251 return RB_RW_SSL_NEED_WRITE;
252 else
253 return RB_RW_SSL_NEED_READ;
254 case GNUTLS_E_INTERRUPTED:
255 err = ret;
256 if(err == 0)
257 {
258 F->ssl_errno = 0;
259 return RB_RW_IO_ERROR;
260 }
261 break;
262 default:
263 err = ret;
264 break;
265 }
266 F->ssl_errno = err;
267 if(err > 0)
268 {
269 errno = EIO; /* not great but... */
270 return RB_RW_SSL_ERROR;
271 }
272 return RB_RW_IO_ERROR;
273 }
274 return ret;
275 }
276
277 ssize_t
278 rb_ssl_read(rb_fde_t * F, void *buf, size_t count)
279 {
280 return rb_ssl_read_or_write(0, F, buf, NULL, count);
281 }
282
283 ssize_t
284 rb_ssl_write(rb_fde_t * F, const void *buf, size_t count)
285 {
286 return rb_ssl_read_or_write(1, F, NULL, buf, count);
287 }
288
289 int
290 rb_init_ssl(void)
291 {
292 int ret = 1, g_ret;
293
294 gnutls_global_init();
295
296 if((g_ret = gnutls_dh_params_generate2(dh_params, 1024)) < 0)
297 {
298 rb_lib_log("rb_init_gnutls: Failed to generate GNUTLS DH params: %s", gnutls_strerror(g_ret));
299 ret = 0;
300 }
301
302 gnutls_certificate_set_dh_params(x509_cred, dh_params);
303
304 return ret;
305 }
306
307 int
308 rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile)
309 {
310 int ret = 0;
311
312 if((ret = gnutls_certificate_set_x509_key_file(x509_cred, cert, keyfile, GNUTLS_X509_FMT_PEM)) < 0)
313 {
314 rb_lib_log("rb_setup_ssl_server: Setting x509 keys up failed: %s", gnutls_strerror(ret));
315 return 0;
316 }
317
318 return 1;
319 }
320
321 int
322 rb_ssl_listen(rb_fde_t * F, int backlog)
323 {
324 F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL;
325 return listen(F->fd, backlog);
326 }
327
328 struct ssl_connect
329 {
330 CNCB *callback;
331 void *data;
332 int timeout;
333 };
334
335 static void
336 rb_ssl_connect_realcb(rb_fde_t * F, int status, struct ssl_connect *sconn)
337 {
338 F->connect->callback = sconn->callback;
339 F->connect->data = sconn->data;
340 rb_free(sconn);
341 rb_connect_callback(F, status);
342 }
343
344 static void
345 rb_ssl_tryconn_timeout_cb(rb_fde_t * F, void *data)
346 {
347 rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data);
348 }
349
350 static void
351 rb_ssl_tryconn_cb(rb_fde_t * F, void *data)
352 {
353 struct ssl_connect *sconn = data;
354 int ssl_err;
355
356 if((ssl_err = gnutls_handshake((gnutls_session_t) F->ssl)) != 0)
357 {
358 switch (ssl_err)
359 {
360 case GNUTLS_E_INTERRUPTED:
361 if(rb_ignore_errno(errno))
362 case GNUTLS_E_AGAIN:
363 {
364 F->ssl_errno = ssl_err;
365 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE,
366 rb_ssl_tryconn_cb, sconn);
367 return;
368 }
369 default:
370 F->ssl_errno = ssl_err;
371 rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
372 return;
373 }
374 }
375 else
376 {
377 rb_ssl_connect_realcb(F, RB_OK, sconn);
378 }
379 }
380
381 static void
382 rb_ssl_tryconn(rb_fde_t * F, int status, void *data)
383 {
384 gnutls_session_t sess;
385 struct ssl_connect *sconn = data;
386 int ssl_err;
387
388 if(status != RB_OK)
389 {
390 rb_ssl_connect_realcb(F, status, sconn);
391 return;
392 }
393
394 F->type |= RB_FD_SSL;
395
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);
401
402 F->ssl = sess;
403
404 rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
405 if((ssl_err = gnutls_handshake((gnutls_session_t) F->ssl)) != 0)
406 {
407 switch (ssl_err)
408 {
409 case GNUTLS_E_INTERRUPTED:
410 if(rb_ignore_errno(errno))
411 case GNUTLS_E_AGAIN:
412 {
413 F->ssl_errno = ssl_err;
414 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE,
415 rb_ssl_tryconn_cb, sconn);
416 return;
417 }
418 default:
419 F->ssl_errno = ssl_err;
420 rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
421 return;
422 }
423 }
424 else
425 {
426 rb_ssl_connect_realcb(F, RB_OK, sconn);
427 }
428 }
429
430 void
431 rb_connect_tcp_ssl(rb_fde_t * F, struct sockaddr *dest,
432 struct sockaddr *clocal, int socklen, CNCB * callback, void *data, int timeout)
433 {
434 struct ssl_connect *sconn;
435 if(F == NULL)
436 return;
437
438 sconn = rb_malloc(sizeof(struct ssl_connect));
439 sconn->data = data;
440 sconn->callback = callback;
441 sconn->timeout = timeout;
442 rb_connect_tcp(F, dest, clocal, socklen, rb_ssl_tryconn, sconn, timeout);
443 }
444
445 void
446 rb_ssl_start_connected(rb_fde_t * F, CNCB * callback, void *data, int timeout)
447 {
448 gnutls_session_t sess;
449 struct ssl_connect *sconn;
450 int ssl_err;
451 if(F == NULL)
452 return;
453
454 sconn = rb_malloc(sizeof(struct ssl_connect));
455 sconn->data = data;
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;
462
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);
468
469 F->ssl = sess;
470
471 rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
472 if((ssl_err = gnutls_handshake((gnutls_session_t) F->ssl)) != 0)
473 {
474 switch (ssl_err)
475 {
476 case GNUTLS_E_INTERRUPTED:
477 if(rb_ignore_errno(errno))
478 case GNUTLS_E_AGAIN:
479 {
480 F->ssl_errno = ssl_err;
481 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE,
482 rb_ssl_tryconn_cb, sconn);
483 return;
484 }
485 default:
486 F->ssl_errno = ssl_err;
487 rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
488 return;
489 }
490 }
491 else
492 {
493 rb_ssl_connect_realcb(F, RB_OK, sconn);
494 }
495 }
496
497 /* XXX: implement me */
498 int
499 rb_init_prng(const char *path, prng_seed_t seed_type)
500 {
501 return -1;
502 }
503
504 int
505 rb_get_random(void *buf, size_t length)
506 {
507 return -1;
508 }
509
510
511 const char *
512 rb_get_ssl_strerror(rb_fde_t * F)
513 {
514 return gnutls_strerror(F->ssl_errno);
515 }
516
517 int
518 rb_supports_ssl(void)
519 {
520 return 1;
521 }
522
523 #endif /* HAVE_GNUTLS */