]> jfr.im git - solanum.git/blob - libratbox/src/gnutls.c
Make sure x509_cred and dh_params objects are allocated.
[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 gnutls_certificate_allocate_credentials(&x509_cred);
297 gnutls_dh_params_init(&dh_params);
298
299 if((g_ret = gnutls_dh_params_generate2(dh_params, 1024)) < 0)
300 {
301 rb_lib_log("rb_init_gnutls: Failed to generate GNUTLS DH params: %s", gnutls_strerror(g_ret));
302 ret = 0;
303 }
304
305 gnutls_certificate_set_dh_params(x509_cred, dh_params);
306
307 return ret;
308 }
309
310 int
311 rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile)
312 {
313 int ret = 0;
314
315 if((ret = gnutls_certificate_set_x509_key_file(x509_cred, cert, keyfile, GNUTLS_X509_FMT_PEM)) < 0)
316 {
317 rb_lib_log("rb_setup_ssl_server: Setting x509 keys up failed: %s", gnutls_strerror(ret));
318 return 0;
319 }
320
321 return 1;
322 }
323
324 int
325 rb_ssl_listen(rb_fde_t * F, int backlog)
326 {
327 F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL;
328 return listen(F->fd, backlog);
329 }
330
331 struct ssl_connect
332 {
333 CNCB *callback;
334 void *data;
335 int timeout;
336 };
337
338 static void
339 rb_ssl_connect_realcb(rb_fde_t * F, int status, struct ssl_connect *sconn)
340 {
341 F->connect->callback = sconn->callback;
342 F->connect->data = sconn->data;
343 rb_free(sconn);
344 rb_connect_callback(F, status);
345 }
346
347 static void
348 rb_ssl_tryconn_timeout_cb(rb_fde_t * F, void *data)
349 {
350 rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data);
351 }
352
353 static void
354 rb_ssl_tryconn_cb(rb_fde_t * F, void *data)
355 {
356 struct ssl_connect *sconn = data;
357 int ssl_err;
358
359 if((ssl_err = gnutls_handshake((gnutls_session_t) F->ssl)) != 0)
360 {
361 switch (ssl_err)
362 {
363 case GNUTLS_E_INTERRUPTED:
364 if(rb_ignore_errno(errno))
365 case GNUTLS_E_AGAIN:
366 {
367 F->ssl_errno = ssl_err;
368 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE,
369 rb_ssl_tryconn_cb, sconn);
370 return;
371 }
372 default:
373 F->ssl_errno = ssl_err;
374 rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
375 return;
376 }
377 }
378 else
379 {
380 rb_ssl_connect_realcb(F, RB_OK, sconn);
381 }
382 }
383
384 static void
385 rb_ssl_tryconn(rb_fde_t * F, int status, void *data)
386 {
387 gnutls_session_t sess;
388 struct ssl_connect *sconn = data;
389 int ssl_err;
390
391 if(status != RB_OK)
392 {
393 rb_ssl_connect_realcb(F, status, sconn);
394 return;
395 }
396
397 F->type |= RB_FD_SSL;
398
399 gnutls_init(&sess, GNUTLS_CLIENT);
400 gnutls_set_default_priority(sess);
401 gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE, x509_cred);
402 gnutls_dh_set_prime_bits(sess, 1024);
403 gnutls_transport_set_ptr(sess, (gnutls_transport_ptr_t) F->fd);
404
405 F->ssl = sess;
406
407 rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
408 if((ssl_err = gnutls_handshake((gnutls_session_t) F->ssl)) != 0)
409 {
410 switch (ssl_err)
411 {
412 case GNUTLS_E_INTERRUPTED:
413 if(rb_ignore_errno(errno))
414 case GNUTLS_E_AGAIN:
415 {
416 F->ssl_errno = ssl_err;
417 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE,
418 rb_ssl_tryconn_cb, sconn);
419 return;
420 }
421 default:
422 F->ssl_errno = ssl_err;
423 rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
424 return;
425 }
426 }
427 else
428 {
429 rb_ssl_connect_realcb(F, RB_OK, sconn);
430 }
431 }
432
433 void
434 rb_connect_tcp_ssl(rb_fde_t * F, struct sockaddr *dest,
435 struct sockaddr *clocal, int socklen, CNCB * callback, void *data, int timeout)
436 {
437 struct ssl_connect *sconn;
438 if(F == NULL)
439 return;
440
441 sconn = rb_malloc(sizeof(struct ssl_connect));
442 sconn->data = data;
443 sconn->callback = callback;
444 sconn->timeout = timeout;
445 rb_connect_tcp(F, dest, clocal, socklen, rb_ssl_tryconn, sconn, timeout);
446 }
447
448 void
449 rb_ssl_start_connected(rb_fde_t * F, CNCB * callback, void *data, int timeout)
450 {
451 gnutls_session_t sess;
452 struct ssl_connect *sconn;
453 int ssl_err;
454 if(F == NULL)
455 return;
456
457 sconn = rb_malloc(sizeof(struct ssl_connect));
458 sconn->data = data;
459 sconn->callback = callback;
460 sconn->timeout = timeout;
461 F->connect = rb_malloc(sizeof(struct conndata));
462 F->connect->callback = callback;
463 F->connect->data = data;
464 F->type |= RB_FD_SSL;
465
466 gnutls_init(&sess, GNUTLS_CLIENT);
467 gnutls_set_default_priority(sess);
468 gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE, x509_cred);
469 gnutls_dh_set_prime_bits(sess, 1024);
470 gnutls_transport_set_ptr(sess, (gnutls_transport_ptr_t) F->fd);
471
472 F->ssl = sess;
473
474 rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
475 if((ssl_err = gnutls_handshake((gnutls_session_t) F->ssl)) != 0)
476 {
477 switch (ssl_err)
478 {
479 case GNUTLS_E_INTERRUPTED:
480 if(rb_ignore_errno(errno))
481 case GNUTLS_E_AGAIN:
482 {
483 F->ssl_errno = ssl_err;
484 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE,
485 rb_ssl_tryconn_cb, sconn);
486 return;
487 }
488 default:
489 F->ssl_errno = ssl_err;
490 rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
491 return;
492 }
493 }
494 else
495 {
496 rb_ssl_connect_realcb(F, RB_OK, sconn);
497 }
498 }
499
500 /* XXX: implement me */
501 int
502 rb_init_prng(const char *path, prng_seed_t seed_type)
503 {
504 return -1;
505 }
506
507 int
508 rb_get_random(void *buf, size_t length)
509 {
510 return -1;
511 }
512
513
514 const char *
515 rb_get_ssl_strerror(rb_fde_t * F)
516 {
517 return gnutls_strerror(F->ssl_errno);
518 }
519
520 int
521 rb_supports_ssl(void)
522 {
523 return 1;
524 }
525
526 #endif /* HAVE_GNUTLS */