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