From af6f5d479f3b221b10f6c967a91fb94dbef85d52 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Tue, 10 Jun 2008 20:33:15 -0500 Subject: [PATCH] GNUTLS backend. Untested. --- TODO | 2 +- libratbox/src/Makefile.am | 1 + libratbox/src/Makefile.in | 4 +- libratbox/src/gnutls.c | 523 ++++++++++++++++++++++++++++++++++++++ libratbox/src/nossl.c | 2 +- 5 files changed, 529 insertions(+), 3 deletions(-) create mode 100644 libratbox/src/gnutls.c diff --git a/TODO b/TODO index c1aa49b..e87cc1d 100644 --- a/TODO +++ b/TODO @@ -19,6 +19,7 @@ [x] ssl channelmode (done by extban and chm_compat) [ ] acknowledgement message for SSL users like '* *** You are connected using SSL cipher "DHE RSA-AES 128 CBC-SHA"' [ ] ssl stuff files generator in ./configure time + [ ] gnutls backend for at least SSL connections (replacing libcrypto use in m_challenge would be nice too) [x] merge some stuff from ircd-seven directly (to be determined what) [x] remote d:lines support [F] kline/xline/resv sync @@ -41,4 +42,3 @@ [x] merge m_join.c and m_sjoin.c in one module (same functions, done in ratbox3) [x] create chmode.h and put there all declarations of chm_* - this will make some modules clean [?] Move oper override server WALLOPS to global server notices? - diff --git a/libratbox/src/Makefile.am b/libratbox/src/Makefile.am index 5b9db53..47ff031 100644 --- a/libratbox/src/Makefile.am +++ b/libratbox/src/Makefile.am @@ -11,6 +11,7 @@ libratbox_la_SOURCES = \ balloc.c \ commio.c \ openssl.c \ + gnutls.c \ nossl.c \ event.c \ ratbox_lib.c \ diff --git a/libratbox/src/Makefile.in b/libratbox/src/Makefile.in index f79515d..bf7009b 100644 --- a/libratbox/src/Makefile.in +++ b/libratbox/src/Makefile.in @@ -55,7 +55,7 @@ libLTLIBRARIES_INSTALL = $(INSTALL) LTLIBRARIES = $(lib_LTLIBRARIES) libratbox_la_DEPENDENCIES = am_libratbox_la_OBJECTS = unix.lo win32.lo crypt.lo balloc.lo \ - commio.lo openssl.lo nossl.lo event.lo ratbox_lib.lo \ + commio.lo openssl.lo gnutls.lo nossl.lo event.lo ratbox_lib.lo \ rb_memory.lo linebuf.lo snprintf.lo tools.lo helper.lo \ devpoll.lo epoll.lo poll.lo ports.lo sigio.lo select.lo \ kqueue.lo rawbuf.lo patricia.lo @@ -216,6 +216,7 @@ libratbox_la_SOURCES = \ balloc.c \ commio.c \ openssl.c \ + gnutls.c \ nossl.c \ event.c \ ratbox_lib.c \ @@ -312,6 +313,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/devpoll.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/epoll.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/event.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnutls.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/helper.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kqueue.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/linebuf.Plo@am__quote@ diff --git a/libratbox/src/gnutls.c b/libratbox/src/gnutls.c new file mode 100644 index 0000000..5442bc1 --- /dev/null +++ b/libratbox/src/gnutls.c @@ -0,0 +1,523 @@ +/* + * libratbox: a library used by ircd-ratbox and other things + * gnutls.c: gnutls related code + * + * Copyright (C) 2007-2008 ircd-ratbox development team + * Copyright (C) 2007-2008 Aaron Sethman + * Copyright (C) 2008 William Pitcock + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + * + * $Id: commio.c 24808 2008-01-02 08:17:05Z androsyn $ + */ + +#include +#include + +#ifdef HAVE_GNUTLS + +#include +#include +#include + +static gnutls_certificate_credentials_t x509_cred; +static gnutls_dh_params_t dh_params; + +void +rb_ssl_shutdown(rb_fde_t * F) +{ + if(F == NULL || F->ssl == NULL) + return; + + gnutls_bye((gnutls_session_t) F->ssl, GNUTLS_SHUT_RDWR); + gnutls_deinit((gnutls_session_t) F->ssl); +} + +static void +rb_ssl_timeout(rb_fde_t * F, void *notused) +{ + lrb_assert(F->accept != NULL); + F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data); +} + +static void +rb_ssl_tryaccept(rb_fde_t * F, void *data) +{ + int ssl_err; + lrb_assert(F->accept != NULL); + int flags; + struct acceptdata *ad; + + if((ssl_err = gnutls_handshake((gnutls_session_t) F->ssl)) != 0) + { + switch (ssl_err) + { + case GNUTLS_E_INTERRUPTED: + if(rb_ignore_errno(errno)) + case GNUTLS_E_AGAIN: + { + if(gnutls_record_get_direction((gnutls_session_t) F->ssl)) + flags = RB_SELECT_WRITE; + else + flags = RB_SELECT_READ; + + F->ssl_errno = ssl_err; + rb_setselect(F, flags, rb_ssl_tryaccept, NULL); + return; + } + break; + default: + F->ssl_errno = ssl_err; + F->accept->callback(F, RB_ERROR_SSL, NULL, 0, F->accept->data); + break; + } + return; + } + rb_settimeout(F, 0, NULL, NULL); + rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL); + + ad = F->accept; + F->accept = NULL; + ad->callback(F, RB_OK, (struct sockaddr *) &ad->S, ad->addrlen, + ad->data); + rb_free(ad); +} + +void +rb_ssl_start_accepted(rb_fde_t * new_F, ACCB * cb, void *data, int timeout) +{ + gnutls_session_t sess; + int ssl_err; + + new_F->type |= RB_FD_SSL; + + gnutls_init(&sess, GNUTLS_SERVER); + gnutls_set_default_priority(sess); + gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE, x509_cred); + gnutls_dh_set_prime_bits(sess, 1024); + gnutls_certificate_server_set_request(sess, GNUTLS_CERT_REQUEST); + + new_F->ssl = sess; + + new_F->accept = rb_malloc(sizeof(struct acceptdata)); + + new_F->accept->callback = cb; + new_F->accept->data = data; + rb_settimeout(new_F, timeout, rb_ssl_timeout, NULL); + + new_F->accept->addrlen = 0; + + gnutls_transport_set_ptr((gnutls_session_t) new_F->ssl, (gnutls_transport_ptr_t) rb_get_fd(new_F)); + + if((ssl_err = gnutls_handshake((gnutls_session_t) new_F->ssl)) != 0) + { + switch(ssl_err) + { + case GNUTLS_E_INTERRUPTED: + if(rb_ignore_errno(errno)) + case GNUTLS_E_AGAIN: + { + int flags; + + if(gnutls_record_get_direction((gnutls_session_t) new_F->ssl)) + flags = RB_SELECT_WRITE; + else + flags = RB_SELECT_READ; + + new_F->ssl_errno = ssl_err; + rb_setselect(new_F, flags, rb_ssl_tryaccept, NULL); + return; + } + break; + default: + new_F->ssl_errno = ssl_err; + new_F->accept->callback(new_F, RB_ERROR_SSL, NULL, 0, new_F->accept->data); + return; + } + } + else + { + struct acceptdata *ad; + + rb_settimeout(new_F, 0, NULL, NULL); + rb_setselect(new_F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL); + + ad = new_F->accept; + new_F->accept = NULL; + ad->callback(new_F, RB_OK, (struct sockaddr *) &ad->S, ad->addrlen, + ad->data); + rb_free(ad); + } +} + +void +rb_ssl_accept_setup(rb_fde_t * F, int new_fd, struct sockaddr *st, int addrlen) +{ + gnutls_session_t sess; + rb_fde_t *new_F; + int ssl_err; + + new_F = rb_find_fd(new_fd); + + gnutls_init(&sess, GNUTLS_SERVER); + gnutls_set_default_priority(sess); + gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE, x509_cred); + gnutls_dh_set_prime_bits(sess, 1024); + gnutls_certificate_server_set_request(sess, GNUTLS_CERT_REQUEST); + + new_F->type |= RB_FD_SSL; + new_F->accept = rb_malloc(sizeof(struct acceptdata)); + + new_F->accept->callback = F->accept->callback; + new_F->accept->data = F->accept->data; + rb_settimeout(new_F, 10, rb_ssl_timeout, NULL); + memcpy(&new_F->accept->S, st, addrlen); + new_F->accept->addrlen = addrlen; + + gnutls_transport_set_ptr((gnutls_session_t) new_F->ssl, (gnutls_transport_ptr_t) rb_get_fd(new_F)); + if((ssl_err = gnutls_handshake((gnutls_session_t) new_F->ssl)) != 0) + { + switch(ssl_err) + { + case GNUTLS_E_INTERRUPTED: + if(rb_ignore_errno(errno)) + case GNUTLS_E_AGAIN: + { + int flags; + + if(gnutls_record_get_direction((gnutls_session_t) new_F->ssl)) + flags = RB_SELECT_WRITE; + else + flags = RB_SELECT_READ; + + new_F->ssl_errno = ssl_err; + rb_setselect(new_F, flags, rb_ssl_tryaccept, NULL); + return; + } + break; + default: + new_F->ssl_errno = ssl_err; + new_F->accept->callback(new_F, RB_ERROR_SSL, NULL, 0, new_F->accept->data); + return; + } + } + else + { + struct acceptdata *ad; + + rb_settimeout(new_F, 0, NULL, NULL); + rb_setselect(new_F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL); + + ad = new_F->accept; + new_F->accept = NULL; + ad->callback(new_F, RB_OK, (struct sockaddr *) &ad->S, ad->addrlen, + ad->data); + rb_free(ad); + } +} + +static ssize_t +rb_ssl_read_or_write(int r_or_w, rb_fde_t * F, void *rbuf, const void *wbuf, size_t count) +{ + ssize_t ret; + unsigned long err; + gnutls_session_t ssl = F->ssl; + + if(r_or_w == 0) + ret = gnutls_record_recv(ssl, rbuf, count); + else + ret = gnutls_record_send(ssl, wbuf, count); + + if(ret < 0) + { + switch (ret) + { + case GNUTLS_E_AGAIN: + errno = EAGAIN; + if (gnutls_record_get_direction(ssl)) + return RB_RW_SSL_NEED_WRITE; + else + return RB_RW_SSL_NEED_READ; + case GNUTLS_E_INTERRUPTED: + err = ret; + if(err == 0) + { + F->ssl_errno = 0; + return RB_RW_IO_ERROR; + } + break; + default: + err = ret; + break; + } + F->ssl_errno = err; + if(err > 0) + { + errno = EIO; /* not great but... */ + return RB_RW_SSL_ERROR; + } + return RB_RW_IO_ERROR; + } + return ret; +} + +ssize_t +rb_ssl_read(rb_fde_t * F, void *buf, size_t count) +{ + return rb_ssl_read_or_write(0, F, buf, NULL, count); +} + +ssize_t +rb_ssl_write(rb_fde_t * F, const void *buf, size_t count) +{ + return rb_ssl_read_or_write(1, F, NULL, buf, count); +} + +int +rb_init_ssl(void) +{ + int ret = 1, g_ret; + + gnutls_global_init(); + + if((g_ret = gnutls_dh_params_generate2(dh_params, 1024)) < 0) + { + rb_lib_log("rb_init_gnutls: Failed to generate GNUTLS DH params: %s", gnutls_strerror(g_ret)); + ret = 0; + } + + gnutls_certificate_set_dh_params(x509_cred, dh_params); + + return ret; +} + +int +rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile) +{ + int ret = 0; + + if((ret = gnutls_certificate_set_x509_key_file(x509_cred, cert, keyfile, GNUTLS_X509_FMT_PEM)) < 0) + { + rb_lib_log("rb_setup_ssl_server: Setting x509 keys up failed: %s", gnutls_strerror(ret)); + return 0; + } + + return 1; +} + +int +rb_ssl_listen(rb_fde_t * F, int backlog) +{ + F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL; + return listen(F->fd, backlog); +} + +struct ssl_connect +{ + CNCB *callback; + void *data; + int timeout; +}; + +static void +rb_ssl_connect_realcb(rb_fde_t * F, int status, struct ssl_connect *sconn) +{ + F->connect->callback = sconn->callback; + F->connect->data = sconn->data; + rb_free(sconn); + rb_connect_callback(F, status); +} + +static void +rb_ssl_tryconn_timeout_cb(rb_fde_t * F, void *data) +{ + rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data); +} + +static void +rb_ssl_tryconn_cb(rb_fde_t * F, void *data) +{ + struct ssl_connect *sconn = data; + int ssl_err; + + if((ssl_err = gnutls_handshake((gnutls_session_t) F->ssl)) != 0) + { + switch (ssl_err) + { + case GNUTLS_E_INTERRUPTED: + if(rb_ignore_errno(errno)) + case GNUTLS_E_AGAIN: + { + F->ssl_errno = ssl_err; + rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, + rb_ssl_tryconn_cb, sconn); + return; + } + default: + F->ssl_errno = ssl_err; + rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn); + return; + } + } + else + { + rb_ssl_connect_realcb(F, RB_OK, sconn); + } +} + +static void +rb_ssl_tryconn(rb_fde_t * F, int status, void *data) +{ + gnutls_session_t sess; + struct ssl_connect *sconn = data; + int ssl_err; + + if(status != RB_OK) + { + rb_ssl_connect_realcb(F, status, sconn); + return; + } + + F->type |= RB_FD_SSL; + + gnutls_init(&sess, GNUTLS_CLIENT); + gnutls_set_default_priority(sess); + gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE, x509_cred); + gnutls_dh_set_prime_bits(sess, 1024); + gnutls_transport_set_ptr(sess, (gnutls_transport_ptr_t) F->fd); + + F->ssl = sess; + + rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn); + if((ssl_err = gnutls_handshake((gnutls_session_t) F->ssl)) != 0) + { + switch (ssl_err) + { + case GNUTLS_E_INTERRUPTED: + if(rb_ignore_errno(errno)) + case GNUTLS_E_AGAIN: + { + F->ssl_errno = ssl_err; + rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, + rb_ssl_tryconn_cb, sconn); + return; + } + default: + F->ssl_errno = ssl_err; + rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn); + return; + } + } + else + { + rb_ssl_connect_realcb(F, RB_OK, sconn); + } +} + +void +rb_connect_tcp_ssl(rb_fde_t * F, struct sockaddr *dest, + struct sockaddr *clocal, int socklen, CNCB * callback, void *data, int timeout) +{ + struct ssl_connect *sconn; + if(F == NULL) + return; + + sconn = rb_malloc(sizeof(struct ssl_connect)); + sconn->data = data; + sconn->callback = callback; + sconn->timeout = timeout; + rb_connect_tcp(F, dest, clocal, socklen, rb_ssl_tryconn, sconn, timeout); +} + +void +rb_ssl_start_connected(rb_fde_t * F, CNCB * callback, void *data, int timeout) +{ + gnutls_session_t sess; + struct ssl_connect *sconn; + int ssl_err; + if(F == NULL) + return; + + sconn = rb_malloc(sizeof(struct ssl_connect)); + sconn->data = data; + sconn->callback = callback; + sconn->timeout = timeout; + F->connect = rb_malloc(sizeof(struct conndata)); + F->connect->callback = callback; + F->connect->data = data; + F->type |= RB_FD_SSL; + + gnutls_init(&sess, GNUTLS_CLIENT); + gnutls_set_default_priority(sess); + gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE, x509_cred); + gnutls_dh_set_prime_bits(sess, 1024); + gnutls_transport_set_ptr(sess, (gnutls_transport_ptr_t) F->fd); + + F->ssl = sess; + + rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn); + if((ssl_err = gnutls_handshake((gnutls_session_t) F->ssl)) != 0) + { + switch (ssl_err) + { + case GNUTLS_E_INTERRUPTED: + if(rb_ignore_errno(errno)) + case GNUTLS_E_AGAIN: + { + F->ssl_errno = ssl_err; + rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, + rb_ssl_tryconn_cb, sconn); + return; + } + default: + F->ssl_errno = ssl_err; + rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn); + return; + } + } + else + { + rb_ssl_connect_realcb(F, RB_OK, sconn); + } +} + +/* XXX: implement me */ +int +rb_init_prng(const char *path, prng_seed_t seed_type) +{ + return -1; +} + +int +rb_get_random(void *buf, size_t length) +{ + return -1; +} + + +const char * +rb_get_ssl_strerror(rb_fde_t * F) +{ + return gnutls_strerror(F->ssl_errno); +} + +int +rb_supports_ssl(void) +{ + return 1; +} + +#endif /* HAVE_GNUTLS */ diff --git a/libratbox/src/nossl.c b/libratbox/src/nossl.c index 1dd8dea..c27a0c7 100644 --- a/libratbox/src/nossl.c +++ b/libratbox/src/nossl.c @@ -27,7 +27,7 @@ #include #include -#ifndef HAVE_OPENSSL +#if !defined(HAVE_OPENSSL) && !defined(HAVE_GNUTLS) #include #include