]> jfr.im git - solanum.git/blame - libratbox/src/gnutls.c
Make sure x509_cred and dh_params objects are allocated.
[solanum.git] / libratbox / src / gnutls.c
CommitLineData
fc8711d1
AC
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
36static gnutls_certificate_credentials_t x509_cred;
37static gnutls_dh_params_t dh_params;
38
39void
40rb_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
49static void
50rb_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
56static void
57rb_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
99void
100rb_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
166void
167rb_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
232static ssize_t
233rb_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
277ssize_t
278rb_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
283ssize_t
284rb_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
289int
290rb_init_ssl(void)
291{
292 int ret = 1, g_ret;
293
294 gnutls_global_init();
295
f17c2ef8
AC
296 gnutls_certificate_allocate_credentials(&x509_cred);
297 gnutls_dh_params_init(&dh_params);
298
fc8711d1
AC
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
310int
311rb_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
324int
325rb_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
331struct ssl_connect
332{
333 CNCB *callback;
334 void *data;
335 int timeout;
336};
337
338static void
339rb_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
347static void
348rb_ssl_tryconn_timeout_cb(rb_fde_t * F, void *data)
349{
350 rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data);
351}
352
353static void
354rb_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
384static void
385rb_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
433void
434rb_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
448void
449rb_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 */
501int
502rb_init_prng(const char *path, prng_seed_t seed_type)
503{
504 return -1;
505}
506
507int
508rb_get_random(void *buf, size_t length)
509{
510 return -1;
511}
512
513
514const char *
515rb_get_ssl_strerror(rb_fde_t * F)
516{
517 return gnutls_strerror(F->ssl_errno);
518}
519
520int
521rb_supports_ssl(void)
522{
523 return 1;
524}
525
526#endif /* HAVE_GNUTLS */