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