]> jfr.im git - irc/rqf/shadowircd.git/blame - libratbox/src/openssl.c
libratbox sync - r25796
[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
4414eb3c 207rb_ssl_accept_setup(rb_fde_t * F, rb_fde_t *new_F, struct sockaddr *st, int addrlen)
b57f37fb 208{
b57f37fb
WP
209 new_F->type |= RB_FD_SSL;
210 new_F->ssl = SSL_new(ssl_server_ctx);
211 new_F->accept = rb_malloc(sizeof(struct acceptdata));
212
213 new_F->accept->callback = F->accept->callback;
214 new_F->accept->data = F->accept->data;
215 rb_settimeout(new_F, 10, rb_ssl_timeout, NULL);
216 memcpy(&new_F->accept->S, st, addrlen);
217 new_F->accept->addrlen = addrlen;
218
4414eb3c 219 SSL_set_fd((SSL *) new_F->ssl, rb_get_fd(new_F));
033be687
VY
220 rb_setup_ssl_cb(new_F);
221 rb_ssl_accept_common(new_F);
b57f37fb
WP
222}
223
224static ssize_t
225rb_ssl_read_or_write(int r_or_w, rb_fde_t * F, void *rbuf, const void *wbuf, size_t count)
226{
227 ssize_t ret;
228 unsigned long err;
229 SSL *ssl = F->ssl;
230
231 if(r_or_w == 0)
232 ret = (ssize_t)SSL_read(ssl, rbuf, (int) count);
233 else
234 ret = (ssize_t)SSL_write(ssl, wbuf, (int) count);
235
236 if(ret < 0)
237 {
238 switch (SSL_get_error(ssl, ret))
239 {
240 case SSL_ERROR_WANT_READ:
241 errno = EAGAIN;
242 return RB_RW_SSL_NEED_READ;
243 case SSL_ERROR_WANT_WRITE:
244 errno = EAGAIN;
245 return RB_RW_SSL_NEED_WRITE;
246 case SSL_ERROR_ZERO_RETURN:
247 return 0;
248 case SSL_ERROR_SYSCALL:
249 err = get_last_err();
250 if(err == 0)
251 {
252 F->ssl_errno = 0;
253 return RB_RW_IO_ERROR;
254 }
255 break;
256 default:
257 err = get_last_err();
258 break;
259 }
260 F->ssl_errno = err;
261 if(err > 0)
262 {
263 errno = EIO; /* not great but... */
264 return RB_RW_SSL_ERROR;
265 }
266 return RB_RW_IO_ERROR;
267 }
268 return ret;
269}
270
271ssize_t
272rb_ssl_read(rb_fde_t * F, void *buf, size_t count)
273{
274 return rb_ssl_read_or_write(0, F, buf, NULL, count);
275}
276
277ssize_t
278rb_ssl_write(rb_fde_t * F, const void *buf, size_t count)
279{
280 return rb_ssl_read_or_write(1, F, NULL, buf, count);
281}
282
283int
284rb_init_ssl(void)
285{
286 int ret = 1;
033be687 287 char libratbox_data[] = "libratbox data";
b57f37fb
WP
288 SSL_load_error_strings();
289 SSL_library_init();
033be687 290 libratbox_index = SSL_get_ex_new_index(0, libratbox_data, NULL, NULL, NULL);
b57f37fb
WP
291 ssl_server_ctx = SSL_CTX_new(SSLv23_server_method());
292 if(ssl_server_ctx == NULL)
293 {
294 rb_lib_log("rb_init_openssl: Unable to initialize OpenSSL server context: %s",
295 ERR_error_string(ERR_get_error(), NULL));
296 ret = 0;
297 }
298 /* Disable SSLv2, make the client use our settings */
299 SSL_CTX_set_options(ssl_server_ctx, SSL_OP_NO_SSLv2 | SSL_OP_CIPHER_SERVER_PREFERENCE);
300
301 ssl_client_ctx = SSL_CTX_new(TLSv1_client_method());
302
303 if(ssl_client_ctx == NULL)
304 {
305 rb_lib_log("rb_init_openssl: Unable to initialize OpenSSL client context: %s",
306 ERR_error_string(ERR_get_error(), NULL));
307 ret = 0;
308 }
309 return ret;
310}
311
312
313int
314rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile)
315{
316 FILE *param;
317 DH *dh;
318 unsigned long err;
319 if(cert == NULL)
320 {
321 rb_lib_log("rb_setup_ssl_server: No certificate file");
322 return 0;
323 }
324 if(!SSL_CTX_use_certificate_file(ssl_server_ctx, cert, SSL_FILETYPE_PEM))
325 {
326 err = ERR_get_error();
327 rb_lib_log("rb_setup_ssl_server: Error loading certificate file [%s]: %s", cert,
328 ERR_error_string(err, NULL));
329 return 0;
330 }
331
332 if(keyfile == NULL)
333 {
334 rb_lib_log("rb_setup_ssl_server: No key file");
335 return 0;
336 }
337
338
339 if(!SSL_CTX_use_PrivateKey_file(ssl_server_ctx, keyfile, SSL_FILETYPE_PEM))
340 {
341 err = ERR_get_error();
342 rb_lib_log("rb_setup_ssl_server: Error loading keyfile [%s]: %s", keyfile,
343 ERR_error_string(err, NULL));
344 return 0;
345 }
346
347 if(dhfile != NULL)
348 {
349 /* DH parameters aren't necessary, but they are nice..if they didn't pass one..that is their problem */
350 param = fopen(dhfile, "r");
351 if(param != NULL)
352 {
353 dh = PEM_read_DHparams(param, NULL, NULL, NULL);
354 if(dh == NULL)
355 {
356 err = ERR_get_error();
357 rb_lib_log
358 ("rb_setup_ssl_server: Error loading DH params file [%s]: %s",
359 param, ERR_error_string(err, NULL));
360 fclose(param);
361 return 0;
362 }
363 SSL_CTX_set_tmp_dh(ssl_server_ctx, dh);
364 fclose(param);
365 }
366 }
367 return 1;
368}
369
370int
371rb_ssl_listen(rb_fde_t * F, int backlog)
372{
373 F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL;
374 return listen(F->fd, backlog);
375}
376
377struct ssl_connect
378{
379 CNCB *callback;
380 void *data;
381 int timeout;
382};
383
384static void
385rb_ssl_connect_realcb(rb_fde_t * F, int status, struct ssl_connect *sconn)
386{
387 F->connect->callback = sconn->callback;
388 F->connect->data = sconn->data;
389 rb_free(sconn);
390 rb_connect_callback(F, status);
391}
392
393static void
394rb_ssl_tryconn_timeout_cb(rb_fde_t * F, void *data)
395{
396 rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data);
397}
398
399static void
400rb_ssl_tryconn_cb(rb_fde_t * F, void *data)
401{
402 struct ssl_connect *sconn = data;
403 int ssl_err;
404 if(!SSL_is_init_finished((SSL *) F->ssl))
405 {
406 if((ssl_err = SSL_connect((SSL *) F->ssl)) <= 0)
407 {
408 switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err))
409 {
410 case SSL_ERROR_SYSCALL:
411 if(rb_ignore_errno(errno))
412 case SSL_ERROR_WANT_READ:
413 case SSL_ERROR_WANT_WRITE:
414 {
415 F->ssl_errno = get_last_err();
416 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE,
417 rb_ssl_tryconn_cb, sconn);
418 return;
419 }
420 default:
421 F->ssl_errno = get_last_err();
422 rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
423 return;
424 }
425 }
426 else
427 {
428 rb_ssl_connect_realcb(F, RB_OK, sconn);
429 }
430 }
431}
432
433static void
434rb_ssl_tryconn(rb_fde_t * F, int status, void *data)
435{
436 struct ssl_connect *sconn = data;
437 int ssl_err;
438 if(status != RB_OK)
439 {
440 rb_ssl_connect_realcb(F, status, sconn);
441 return;
442 }
443
444 F->type |= RB_FD_SSL;
445 F->ssl = SSL_new(ssl_client_ctx);
446 SSL_set_fd((SSL *) F->ssl, F->fd);
033be687 447 rb_setup_ssl_cb(F);
b57f37fb
WP
448 rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
449 if((ssl_err = SSL_connect((SSL *) F->ssl)) <= 0)
450 {
451 switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err))
452 {
453 case SSL_ERROR_SYSCALL:
454 if(rb_ignore_errno(errno))
455 case SSL_ERROR_WANT_READ:
456 case SSL_ERROR_WANT_WRITE:
457 {
458 F->ssl_errno = get_last_err();
459 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE,
460 rb_ssl_tryconn_cb, sconn);
461 return;
462 }
463 default:
464 F->ssl_errno = get_last_err();
465 rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
466 return;
467 }
468 }
469 else
470 {
471 rb_ssl_connect_realcb(F, RB_OK, sconn);
472 }
473}
474
475void
476rb_connect_tcp_ssl(rb_fde_t * F, struct sockaddr *dest,
477 struct sockaddr *clocal, int socklen, CNCB * callback, void *data, int timeout)
478{
479 struct ssl_connect *sconn;
480 if(F == NULL)
481 return;
482
483 sconn = rb_malloc(sizeof(struct ssl_connect));
484 sconn->data = data;
485 sconn->callback = callback;
486 sconn->timeout = timeout;
487 rb_connect_tcp(F, dest, clocal, socklen, rb_ssl_tryconn, sconn, timeout);
488
489}
490
491void
492rb_ssl_start_connected(rb_fde_t * F, CNCB * callback, void *data, int timeout)
493{
494 struct ssl_connect *sconn;
495 int ssl_err;
496 if(F == NULL)
497 return;
498
499 sconn = rb_malloc(sizeof(struct ssl_connect));
500 sconn->data = data;
501 sconn->callback = callback;
502 sconn->timeout = timeout;
503 F->connect = rb_malloc(sizeof(struct conndata));
504 F->connect->callback = callback;
505 F->connect->data = data;
506 F->type |= RB_FD_SSL;
507 F->ssl = SSL_new(ssl_client_ctx);
508
509 SSL_set_fd((SSL *) F->ssl, F->fd);
033be687 510 rb_setup_ssl_cb(F);
b57f37fb
WP
511 rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
512 if((ssl_err = SSL_connect((SSL *) F->ssl)) <= 0)
513 {
514 switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err))
515 {
516 case SSL_ERROR_SYSCALL:
517 if(rb_ignore_errno(errno))
518 case SSL_ERROR_WANT_READ:
519 case SSL_ERROR_WANT_WRITE:
520 {
521 F->ssl_errno = get_last_err();
522 rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE,
523 rb_ssl_tryconn_cb, sconn);
524 return;
525 }
526 default:
527 F->ssl_errno = get_last_err();
528 rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
529 return;
530 }
531 }
532 else
533 {
534 rb_ssl_connect_realcb(F, RB_OK, sconn);
535 }
536}
537
538int
539rb_init_prng(const char *path, prng_seed_t seed_type)
540{
541 if(seed_type == RB_PRNG_DEFAULT)
542 {
543#ifdef WIN32
544 RAND_screen();
545#endif
546 return RAND_status();
547 }
548 if(path == NULL)
549 return RAND_status();
550
551 switch (seed_type)
552 {
553 case RB_PRNG_EGD:
554 if(RAND_egd(path) == -1)
555 return -1;
556 break;
557 case RB_PRNG_FILE:
558 if(RAND_load_file(path, -1) == -1)
559 return -1;
560 break;
561#ifdef WIN32
562 case RB_PRNGWIN32:
563 RAND_screen();
564 break;
565#endif
566 default:
567 return -1;
568 }
569
570 return RAND_status();
571}
572
573int
574rb_get_random(void *buf, size_t length)
575{
4414eb3c
VY
576 int ret;
577
578 if((ret = RAND_bytes(buf, length)) == 0)
b57f37fb 579 {
4414eb3c
VY
580 /* remove the error from the queue */
581 ERR_get_error();
b57f37fb 582 }
4414eb3c 583 return ret;
b57f37fb
WP
584}
585
4414eb3c
VY
586int
587rb_get_pseudo_random(void *buf, size_t length)
588{
589 int ret;
590 ret = RAND_pseudo_bytes(buf, length);
591 if(ret < 0)
592 return 0;
593 return 1;
594}
b57f37fb
WP
595
596const char *
597rb_get_ssl_strerror(rb_fde_t * F)
598{
599 return ERR_error_string(F->ssl_errno, NULL);
600}
601
602int
603rb_supports_ssl(void)
604{
605 return 1;
606}
607
608#endif /* HAVE_OPESSL */