]> jfr.im git - irc/rqf/shadowircd.git/blame - libratbox/src/openssl.c
Removed TS5 description as it is no longer supported
[irc/rqf/shadowircd.git] / libratbox / src / openssl.c
CommitLineData
b57f37fb
WP
1/*
2 * libratbox: a library used by ircd-ratbox and other things
3 * openssl.c: openssl related code
4 *
5 * Copyright (C) 2007-2008 ircd-ratbox development team
6 * Copyright (C) 2007-2008 Aaron Sethman <androsyn@ratbox.org>
7 *
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.
12 *
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.
17 *
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
21 * USA
22 *
23 * $Id: commio.c 24808 2008-01-02 08:17:05Z androsyn $
24 */
25
26#include <libratbox_config.h>
27#include <ratbox_lib.h>
28
29#ifdef HAVE_OPENSSL
30
31#include <commio-int.h>
32#include <commio-ssl.h>
33#include <openssl/ssl.h>
34#include <openssl/dh.h>
35#include <openssl/err.h>
36#include <openssl/rand.h>
37
38static SSL_CTX *ssl_server_ctx;
39static SSL_CTX *ssl_client_ctx;
033be687 40static int libratbox_index = -1;
b57f37fb
WP
41
42static unsigned long get_last_err(void)
43{
44 unsigned long t_err, err = 0;
45 err = ERR_get_error();
46 if(err == 0)
47 return 0;
48
49 while((t_err = ERR_get_error()) > 0)
50 err = t_err;
51
52 return err;
53}
54
55void
56rb_ssl_shutdown(rb_fde_t * F)
57{
58 int i;
59 if(F == NULL || F->ssl == NULL)
60 return;
61 SSL_set_shutdown((SSL *) F->ssl, SSL_RECEIVED_SHUTDOWN);
62
63 for (i = 0; i < 4; i++)
64 {
65 if(SSL_shutdown((SSL *) F->ssl))
66 break;
67 }
68 get_last_err();
69 SSL_free((SSL *) F->ssl);
70}
71
033be687
VY
72unsigned int
73rb_ssl_handshake_count(rb_fde_t *F)
74{
75 return F->handshake_count;
76}
77
78void
79rb_ssl_clear_handshake_count(rb_fde_t *F)
80{
81 F->handshake_count = 0;
82}
83
b57f37fb 84static void
8f40f4bb 85rb_ssl_timeout(rb_fde_t * F, void *notused)
b57f37fb 86{
8f40f4bb
VY
87 lrb_assert(F->accept != NULL);
88 F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data);
b57f37fb
WP
89}
90
91
033be687
VY
92static void rb_ssl_info_callback(SSL *ssl, int where, int ret)
93{
94 if(where & SSL_CB_HANDSHAKE_START)
95 {
96 rb_fde_t *F = SSL_get_ex_data(ssl, libratbox_index);
97 if(F == NULL)
98 return;
99 F->handshake_count++;
100 }
101}
102
103static void
104rb_setup_ssl_cb(rb_fde_t *F)
105{
106 SSL_set_ex_data(F->ssl, libratbox_index, (char *)F);
107 SSL_set_info_callback((SSL *)F->ssl, (void *)rb_ssl_info_callback);
108}
109
b57f37fb
WP
110static void
111rb_ssl_tryaccept(rb_fde_t * F, void *data)
112{
113 int ssl_err;
114 lrb_assert(F->accept != NULL);
8f40f4bb 115 int flags;
b68b0b2c 116 struct acceptdata *ad;
b57f37fb
WP
117
118 if(!SSL_is_init_finished((SSL *) F->ssl))
119 {
120 if((ssl_err = SSL_accept((SSL *) F->ssl)) <= 0)
121 {
122 switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err))
123 {
b57f37fb
WP
124 case SSL_ERROR_WANT_READ:
125 case SSL_ERROR_WANT_WRITE:
8f40f4bb
VY
126 if(ssl_err == SSL_ERROR_WANT_WRITE)
127 flags = RB_SELECT_WRITE;
128 else
129 flags = RB_SELECT_READ;
130 F->ssl_errno = get_last_err();
131 rb_setselect(F, flags, rb_ssl_tryaccept, NULL);
132 break;
133 case SSL_ERROR_SYSCALL:
134 F->accept->callback(F, RB_ERROR, NULL, 0, F->accept->data);
135 break;
b57f37fb
WP
136 default:
137 F->ssl_errno = get_last_err();
138 F->accept->callback(F, RB_ERROR_SSL, NULL, 0, F->accept->data);
139 break;
140 }
141 return;
142 }
143 }
144 rb_settimeout(F, 0, NULL, NULL);
145 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL);
b68b0b2c
JT
146
147 ad = F->accept;
b57f37fb 148 F->accept = NULL;
b68b0b2c
JT
149 ad->callback(F, RB_OK, (struct sockaddr *) &ad->S, ad->addrlen,
150 ad->data);
151 rb_free(ad);
b57f37fb
WP
152
153}
154
033be687
VY
155
156static void
157rb_ssl_accept_common(rb_fde_t *new_F)
b57f37fb
WP
158{
159 int ssl_err;
b57f37fb
WP
160 if((ssl_err = SSL_accept((SSL *) new_F->ssl)) <= 0)
161 {
162 switch (ssl_err = SSL_get_error((SSL *) new_F->ssl, ssl_err))
163 {
164 case SSL_ERROR_SYSCALL:
165 if(rb_ignore_errno(errno))
166 case SSL_ERROR_WANT_READ:
167 case SSL_ERROR_WANT_WRITE:
168 {
169 new_F->ssl_errno = get_last_err();
170 rb_setselect(new_F, RB_SELECT_READ | RB_SELECT_WRITE,
171 rb_ssl_tryaccept, NULL);
172 return;
173 }
174 default:
175 new_F->ssl_errno = get_last_err();
176 new_F->accept->callback(new_F, RB_ERROR_SSL, NULL, 0, new_F->accept->data);
177 return;
178 }
179 }
180 else
181 {
182 rb_ssl_tryaccept(new_F, NULL);
183 }
184}
185
033be687
VY
186void
187rb_ssl_start_accepted(rb_fde_t * new_F, ACCB * cb, void *data, int timeout)
188{
189 new_F->type |= RB_FD_SSL;
190 new_F->ssl = SSL_new(ssl_server_ctx);
191 new_F->accept = rb_malloc(sizeof(struct acceptdata));
192
193 new_F->accept->callback = cb;
194 new_F->accept->data = data;
195 rb_settimeout(new_F, timeout, rb_ssl_timeout, NULL);
196
197 new_F->accept->addrlen = 0;
198 SSL_set_fd((SSL *) new_F->ssl, rb_get_fd(new_F));
199 rb_setup_ssl_cb(new_F);
200 rb_ssl_accept_common(new_F);
201}
202
b57f37fb
WP
203
204
205
206void
207rb_ssl_accept_setup(rb_fde_t * F, int new_fd, struct sockaddr *st, int addrlen)
208{
209 rb_fde_t *new_F;
b57f37fb
WP
210
211 new_F = rb_find_fd(new_fd);
033be687
VY
212 if(new_F == NULL)
213 return;
b57f37fb
WP
214 new_F->type |= RB_FD_SSL;
215 new_F->ssl = SSL_new(ssl_server_ctx);
216 new_F->accept = rb_malloc(sizeof(struct acceptdata));
217
218 new_F->accept->callback = F->accept->callback;
219 new_F->accept->data = F->accept->data;
220 rb_settimeout(new_F, 10, rb_ssl_timeout, NULL);
221 memcpy(&new_F->accept->S, st, addrlen);
222 new_F->accept->addrlen = addrlen;
223
224 SSL_set_fd((SSL *) new_F->ssl, new_fd);
033be687
VY
225 rb_setup_ssl_cb(new_F);
226 rb_ssl_accept_common(new_F);
b57f37fb
WP
227}
228
229static ssize_t
230rb_ssl_read_or_write(int r_or_w, rb_fde_t * F, void *rbuf, const void *wbuf, size_t count)
231{
232 ssize_t ret;
233 unsigned long err;
234 SSL *ssl = F->ssl;
235
236 if(r_or_w == 0)
237 ret = (ssize_t)SSL_read(ssl, rbuf, (int) count);
238 else
239 ret = (ssize_t)SSL_write(ssl, wbuf, (int) count);
240
241 if(ret < 0)
242 {
243 switch (SSL_get_error(ssl, ret))
244 {
245 case SSL_ERROR_WANT_READ:
246 errno = EAGAIN;
247 return RB_RW_SSL_NEED_READ;
248 case SSL_ERROR_WANT_WRITE:
249 errno = EAGAIN;
250 return RB_RW_SSL_NEED_WRITE;
251 case SSL_ERROR_ZERO_RETURN:
252 return 0;
253 case SSL_ERROR_SYSCALL:
254 err = get_last_err();
255 if(err == 0)
256 {
257 F->ssl_errno = 0;
258 return RB_RW_IO_ERROR;
259 }
260 break;
261 default:
262 err = get_last_err();
263 break;
264 }
265 F->ssl_errno = err;
266 if(err > 0)
267 {
268 errno = EIO; /* not great but... */
269 return RB_RW_SSL_ERROR;
270 }
271 return RB_RW_IO_ERROR;
272 }
273 return ret;
274}
275
276ssize_t
277rb_ssl_read(rb_fde_t * F, void *buf, size_t count)
278{
279 return rb_ssl_read_or_write(0, F, buf, NULL, count);
280}
281
282ssize_t
283rb_ssl_write(rb_fde_t * F, const void *buf, size_t count)
284{
285 return rb_ssl_read_or_write(1, F, NULL, buf, count);
286}
287
288int
289rb_init_ssl(void)
290{
291 int ret = 1;
033be687 292 char libratbox_data[] = "libratbox data";
b57f37fb
WP
293 SSL_load_error_strings();
294 SSL_library_init();
033be687 295 libratbox_index = SSL_get_ex_new_index(0, libratbox_data, NULL, NULL, NULL);
b57f37fb
WP
296 ssl_server_ctx = SSL_CTX_new(SSLv23_server_method());
297 if(ssl_server_ctx == NULL)
298 {
299 rb_lib_log("rb_init_openssl: Unable to initialize OpenSSL server context: %s",
300 ERR_error_string(ERR_get_error(), NULL));
301 ret = 0;
302 }
303 /* Disable SSLv2, make the client use our settings */
304 SSL_CTX_set_options(ssl_server_ctx, SSL_OP_NO_SSLv2 | SSL_OP_CIPHER_SERVER_PREFERENCE);
305
306 ssl_client_ctx = SSL_CTX_new(TLSv1_client_method());
307
308 if(ssl_client_ctx == NULL)
309 {
310 rb_lib_log("rb_init_openssl: Unable to initialize OpenSSL client context: %s",
311 ERR_error_string(ERR_get_error(), NULL));
312 ret = 0;
313 }
314 return ret;
315}
316
317
318int
319rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile)
320{
321 FILE *param;
322 DH *dh;
323 unsigned long err;
324 if(cert == NULL)
325 {
326 rb_lib_log("rb_setup_ssl_server: No certificate file");
327 return 0;
328 }
329 if(!SSL_CTX_use_certificate_file(ssl_server_ctx, cert, SSL_FILETYPE_PEM))
330 {
331 err = ERR_get_error();
332 rb_lib_log("rb_setup_ssl_server: Error loading certificate file [%s]: %s", cert,
333 ERR_error_string(err, NULL));
334 return 0;
335 }
336
337 if(keyfile == NULL)
338 {
339 rb_lib_log("rb_setup_ssl_server: No key file");
340 return 0;
341 }
342
343
344 if(!SSL_CTX_use_PrivateKey_file(ssl_server_ctx, keyfile, SSL_FILETYPE_PEM))
345 {
346 err = ERR_get_error();
347 rb_lib_log("rb_setup_ssl_server: Error loading keyfile [%s]: %s", keyfile,
348 ERR_error_string(err, NULL));
349 return 0;
350 }
351
352 if(dhfile != NULL)
353 {
354 /* DH parameters aren't necessary, but they are nice..if they didn't pass one..that is their problem */
355 param = fopen(dhfile, "r");
356 if(param != NULL)
357 {
358 dh = PEM_read_DHparams(param, NULL, NULL, NULL);
359 if(dh == NULL)
360 {
361 err = ERR_get_error();
362 rb_lib_log
363 ("rb_setup_ssl_server: Error loading DH params file [%s]: %s",
364 param, ERR_error_string(err, NULL));
365 fclose(param);
366 return 0;
367 }
368 SSL_CTX_set_tmp_dh(ssl_server_ctx, dh);
369 fclose(param);
370 }
371 }
372 return 1;
373}
374
375int
376rb_ssl_listen(rb_fde_t * F, int backlog)
377{
378 F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL;
379 return listen(F->fd, backlog);
380}
381
382struct ssl_connect
383{
384 CNCB *callback;
385 void *data;
386 int timeout;
387};
388
389static void
390rb_ssl_connect_realcb(rb_fde_t * F, int status, struct ssl_connect *sconn)
391{
392 F->connect->callback = sconn->callback;
393 F->connect->data = sconn->data;
394 rb_free(sconn);
395 rb_connect_callback(F, status);
396}
397
398static void
399rb_ssl_tryconn_timeout_cb(rb_fde_t * F, void *data)
400{
401 rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data);
402}
403
404static void
405rb_ssl_tryconn_cb(rb_fde_t * F, void *data)
406{
407 struct ssl_connect *sconn = data;
408 int ssl_err;
409 if(!SSL_is_init_finished((SSL *) F->ssl))
410 {
411 if((ssl_err = SSL_connect((SSL *) F->ssl)) <= 0)
412 {
413 switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err))
414 {
415 case SSL_ERROR_SYSCALL:
416 if(rb_ignore_errno(errno))
417 case SSL_ERROR_WANT_READ:
418 case SSL_ERROR_WANT_WRITE:
419 {
420 F->ssl_errno = get_last_err();
421 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE,
422 rb_ssl_tryconn_cb, sconn);
423 return;
424 }
425 default:
426 F->ssl_errno = get_last_err();
427 rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
428 return;
429 }
430 }
431 else
432 {
433 rb_ssl_connect_realcb(F, RB_OK, sconn);
434 }
435 }
436}
437
438static void
439rb_ssl_tryconn(rb_fde_t * F, int status, void *data)
440{
441 struct ssl_connect *sconn = data;
442 int ssl_err;
443 if(status != RB_OK)
444 {
445 rb_ssl_connect_realcb(F, status, sconn);
446 return;
447 }
448
449 F->type |= RB_FD_SSL;
450 F->ssl = SSL_new(ssl_client_ctx);
451 SSL_set_fd((SSL *) F->ssl, F->fd);
033be687 452 rb_setup_ssl_cb(F);
b57f37fb
WP
453 rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
454 if((ssl_err = SSL_connect((SSL *) F->ssl)) <= 0)
455 {
456 switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err))
457 {
458 case SSL_ERROR_SYSCALL:
459 if(rb_ignore_errno(errno))
460 case SSL_ERROR_WANT_READ:
461 case SSL_ERROR_WANT_WRITE:
462 {
463 F->ssl_errno = get_last_err();
464 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE,
465 rb_ssl_tryconn_cb, sconn);
466 return;
467 }
468 default:
469 F->ssl_errno = get_last_err();
470 rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
471 return;
472 }
473 }
474 else
475 {
476 rb_ssl_connect_realcb(F, RB_OK, sconn);
477 }
478}
479
480void
481rb_connect_tcp_ssl(rb_fde_t * F, struct sockaddr *dest,
482 struct sockaddr *clocal, int socklen, CNCB * callback, void *data, int timeout)
483{
484 struct ssl_connect *sconn;
485 if(F == NULL)
486 return;
487
488 sconn = rb_malloc(sizeof(struct ssl_connect));
489 sconn->data = data;
490 sconn->callback = callback;
491 sconn->timeout = timeout;
492 rb_connect_tcp(F, dest, clocal, socklen, rb_ssl_tryconn, sconn, timeout);
493
494}
495
496void
497rb_ssl_start_connected(rb_fde_t * F, CNCB * callback, void *data, int timeout)
498{
499 struct ssl_connect *sconn;
500 int ssl_err;
501 if(F == NULL)
502 return;
503
504 sconn = rb_malloc(sizeof(struct ssl_connect));
505 sconn->data = data;
506 sconn->callback = callback;
507 sconn->timeout = timeout;
508 F->connect = rb_malloc(sizeof(struct conndata));
509 F->connect->callback = callback;
510 F->connect->data = data;
511 F->type |= RB_FD_SSL;
512 F->ssl = SSL_new(ssl_client_ctx);
513
514 SSL_set_fd((SSL *) F->ssl, F->fd);
033be687 515 rb_setup_ssl_cb(F);
b57f37fb
WP
516 rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
517 if((ssl_err = SSL_connect((SSL *) F->ssl)) <= 0)
518 {
519 switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err))
520 {
521 case SSL_ERROR_SYSCALL:
522 if(rb_ignore_errno(errno))
523 case SSL_ERROR_WANT_READ:
524 case SSL_ERROR_WANT_WRITE:
525 {
526 F->ssl_errno = get_last_err();
527 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE,
528 rb_ssl_tryconn_cb, sconn);
529 return;
530 }
531 default:
532 F->ssl_errno = get_last_err();
533 rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
534 return;
535 }
536 }
537 else
538 {
539 rb_ssl_connect_realcb(F, RB_OK, sconn);
540 }
541}
542
543int
544rb_init_prng(const char *path, prng_seed_t seed_type)
545{
546 if(seed_type == RB_PRNG_DEFAULT)
547 {
548#ifdef WIN32
549 RAND_screen();
550#endif
551 return RAND_status();
552 }
553 if(path == NULL)
554 return RAND_status();
555
556 switch (seed_type)
557 {
558 case RB_PRNG_EGD:
559 if(RAND_egd(path) == -1)
560 return -1;
561 break;
562 case RB_PRNG_FILE:
563 if(RAND_load_file(path, -1) == -1)
564 return -1;
565 break;
566#ifdef WIN32
567 case RB_PRNGWIN32:
568 RAND_screen();
569 break;
570#endif
571 default:
572 return -1;
573 }
574
575 return RAND_status();
576}
577
578int
579rb_get_random(void *buf, size_t length)
580{
581 if(RAND_status())
582 {
583 if(RAND_bytes(buf, length) > 0)
584 return 1;
585 }
586 else
587 {
588 if(RAND_pseudo_bytes(buf, length) >= 0)
589 return 1;
590 }
591 return 0;
592}
593
594
595const char *
596rb_get_ssl_strerror(rb_fde_t * F)
597{
598 return ERR_error_string(F->ssl_errno, NULL);
599}
600
601int
602rb_supports_ssl(void)
603{
604 return 1;
605}
606
607#endif /* HAVE_OPESSL */