gnutls backend is not working yet...
fi
AC_SUBST(ENGINE_C)
-have_gnutls="no"
-AC_CHECK_LIB(gnutls, gnutls_init, [
- AC_CHECK_HEADERS(gnutls/gnutls.h, [
- LIBS="$LIBS -lgnutls"
- have_gnutls="yes"
- ])
-])
-if test x"$have_gnutls" = xno; then
- if test x$is_win32 = xyes ; then
- openssl_deps="-lcrypto -lgdi32"
- else
- openssl_deps="-lcrypto"
- fi
- AC_CHECK_LIB([ssl],[SSL_library_init], [
- LIBS="$LIBS -lssl $openssl_deps"
- ], [], $openssl_deps)
-fi
-
dnl Now look for --enable-debug
AC_MSG_CHECKING([whether to enable debug mode])
AC_ARG_ENABLE([debug],
AC_DEFINE_UNQUOTED(MAXCONNECTIONS, $unet_cv_with_maxcon,
[Maximum number of network connections])
+unet_cv_enable_gnutls="no"
+unet_cv_enable_openssl="yes"
+
+AC_MSG_CHECKING([for GnuTLS])
+AC_ARG_ENABLE([gnutls],
+ [ --enable-gnutls Enables GnuTLS ssl backend.],
+ [unet_cv_enable_gnutls=$enable_gnutls],
+)
+AC_MSG_RESULT([$unet_cv_enable_gnutls])
+
+AC_MSG_CHECKING([for OpenSSL])
+AC_ARG_ENABLE([openssl],
+ [ --enable-openssl Enables OpenSSL ssl backend.],
+ [unet_cv_enable_openssl=$enable_openssl],
+)
+AC_MSG_RESULT([$unet_cv_enable_openssl])
+
+if test x"$unet_cv_enable_gnutls" = xyes; then
+ unet_cv_enable_gnutls="no";
+ AC_CHECK_LIB(gnutls, gnutls_init, [
+ AC_CHECK_HEADERS(gnutls/gnutls.h, [
+ unet_cv_enable_gnutls="yes";
+ ])
+ ])
+fi
+
+if test x"$unet_cv_enable_openssl" = xyes; then
+ unet_cv_enable_openssl="no";
+ AC_CHECK_LIB(ssl, SSL_read, [
+ AC_CHECK_LIB(crypto, X509_new, [
+ AC_CHECK_HEADERS(openssl/ssl.h openssl/err.h, [
+ unet_cv_enable_openssl="yes";
+ ])
+ ])
+ ])
+fi
+
+if test x"$unet_cv_enable_gnutls" = xyes; then
+ LIBS="$LIBS -lgnutls"
+ AC_DEFINE([HAVE_GNUTLS], 1, [Define if you are using GnuTLS])
+fi
+
+if test x"$unet_cv_enable_openssl" = xyes ; then
+ LIBS="$LIBS -lssl -lcrypto"
+ AC_DEFINE([HAVE_OPENSSL], 1, [Define if you are using OpenSSL])
+fi
+
dnl Finally really generate all output files:
AC_OUTPUT(Makefile ircd/Makefile ircd/test/Makefile, [echo timestamp > stamp-h])
Profile: $unet_cv_enable_profile
Owner/mode: $unet_cv_with_owner.$unet_cv_with_group ($unet_cv_with_mode)
Chroot: $unet_cv_with_chroot
+ OpenSSL: $unet_cv_enable_openssl
+ GnuTLS: $unet_cv_enable_gnutls
Domain: $unet_cv_with_domain
DPath: $unet_cv_with_dpath
SSLData_Client
};
-#if defined(HAVE_GNUTLS_GNUTLS_H)
+#if defined(HAVE_GNUTLS)
#include <gnutls/gnutls.h>
struct SSLConnection {
gnutls_certificate_credentials_t credentials;
};
-#elif defined(HAVE_OPENSSL_SSL_H)
+#elif defined(HAVE_OPENSSL)
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/** @file
- * @brief Implementation of functions for handling local clients.
+ * @brief Implementation of functions for handling ssl connections
* @version $Id$
*/
#include "config.h"
#define IOV_MAX 16 /**< minimum required length of an iovec array */
#endif
-#if defined(HAVE_OPENSSL_SSL_H)
-
-static struct SSLPendingConections {
- struct SSLConnection *connection;
- struct SSLPendingConections *next;
-
- void *data;
- enum SSLDataType datatype;
-};
-
-struct SSLPendingConections *firstPendingConection = NULL;
-int ssl_is_initialized = 0;
-
-static void ssl_init() {
- if(ssl_is_initialized)
- return;
- ssl_is_initialized = 1;
- SSL_library_init();
- OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */
- SSL_load_error_strings();
-}
-
-void ssl_free_connection(struct SSLConnection *connection) {
- SSL_CTX *context = NULL;
- if(FlagHas(&connection->flags, SSLFLAG_OUTGOING)) {
- struct SSLOutConnection *outconn = (struct SSLOutConnection *)connection;
- context = outconn->context;
- }
- SSL_shutdown(connection->session);
- SSL_free(connection->session);
- if(context)
- SSL_CTX_free(context);
- free(connection);
-}
-
-void ssl_free_listener(struct SSLListener *listener) {
- SSL_CTX_free(listener->context);
- free(listener);
-}
-
-static void ssl_handshake_completed(struct SSLConnection *connection, int success) {
- struct SSLPendingConections *pending, *lastPending = NULL;
- for(pending = firstPendingConection; pending; pending = pending->next) {
- if(pending->connection == connection) {
- if(lastPending)
- lastPending->next = pending->next;
- else
- firstPendingConection = pending->next;
- switch(pending->datatype) {
- case SSLData_Client: {
- struct Client *cptr = (struct Client *) pending->data;
- if(success) {
- if(FlagHas(&connection->flags, SSLFLAG_INCOMING))
- start_auth(cptr);
- else if(!completed_connection(cptr))
- exit_client_msg(cptr, cptr, &me, "Registration failed.");
- } else
- exit_client_msg(cptr, cptr, &me, "SSL Handshake failed.");
- }
- break;
- }
- free(pending);
- }
- lastPending = pending;
- }
-}
-
-static int ssl_handshake_outgoing(struct SSLConnection *connection) {
- int ret = SSL_do_handshake(connection->session);
- FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_R);
- FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_W);
-
- switch(SSL_get_error(connection->session, ret)) {
- case SSL_ERROR_NONE:
- FlagClr(&connection->flags, SSLFLAG_HANDSHAKE);
- FlagSet(&connection->flags, SSLFLAG_READY);
-
- ssl_handshake_completed(connection, 1);
- break;
- case SSL_ERROR_WANT_READ:
- FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
- break;
- case SSL_ERROR_WANT_WRITE:
- FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
- break;
- default:
-
- break;
- }
-}
-
-struct SSLConnection *ssl_create_connect(int fd, void *data, enum SSLDataType datatype) {
- struct SSLOutConnection *connection = malloc(sizeof(*connection));
- struct SSLConnection *sslconn = (struct SSLConnection *)connection;
- struct SSLPendingConections *pending = NULL;
-
- if(!connection)
- return NULL;
-
- if(!ssl_is_initialized)
- ssl_init();
-
- connection->context = SSL_CTX_new(SSLv23_client_method());
- if(!connection->context) {
- goto ssl_create_connect_failed;
- }
- connection->session = SSL_new(connection->context);
- if(!connection->session) {
- goto ssl_create_connect_failed;
- }
- if(!SSL_set_fd(connection->session, fd)) {
- goto ssl_create_connect_failed;
- }
- SSL_set_connect_state(connection->session);
- FlagSet(&connection->flags, SSLFLAG_OUTGOING);
- FlagSet(&connection->flags, SSLFLAG_HANDSHAKE);
-
- pending = malloc(sizeof(*pending));
- if(!pending) {
- goto ssl_create_connect_failed;
- }
- pending->connection = connection;
- pending->next = firstPendingConection;
- firstPendingConection = pending;
-
- pending->data = data;
- pending->datatype = datatype;
-
- return sslconn;
-ssl_create_connect_failed:
- free(connection);
- return NULL;
-}
-
-void ssl_start_handshake_connect(struct SSLConnection *connection) {
- ssl_handshake_outgoing(connection);
-}
-
-struct SSLListener *ssl_create_listener() {
- if(!ssl_is_initialized)
- ssl_init();
-
- struct SSLListener *listener = calloc(1, sizeof(*listener));
- listener->context = SSL_CTX_new(SSLv23_server_method());
- if(!listener->context) {
- goto ssl_create_listener_failed;
- }
-
- char *certfile = conf_get_local()->sslcertfile;
- char *keyfile = conf_get_local()->sslkeyfile;
- char *cafile = conf_get_local()->sslcafile;
-
- if(!certfile) {
- goto ssl_create_listener_failed;
- }
- if(!keyfile) {
- keyfile = certfile;
- }
-
- /* load certificate */
- if(SSL_CTX_use_certificate_file(listener->context, certfile, SSL_FILETYPE_PEM) <= 0) {
- goto ssl_create_listener_failed;
- }
- /* load keyfile */
- if(SSL_CTX_use_PrivateKey_file(listener->context, keyfile, SSL_FILETYPE_PEM) <= 0) {
- goto ssl_create_listener_failed;
- }
- /* check certificate and keyfile */
- if(!SSL_CTX_check_private_key(listener->context)) {
- goto ssl_create_listener_failed;
- }
- /* load cafile */
- if(cafile && cafile[0] && SSL_CTX_load_verify_locations(listener->context, cafile, NULL) <= 0) {
- goto ssl_create_listener_failed;
- }
- FlagSet(&listener->flags, SSLFLAG_READY);
- return listener;
-ssl_create_listener_failed:
- free(listener);
- return NULL;
-}
-
-static int ssl_handshake_incoming(struct SSLConnection *connection) {
- int result = SSL_accept(connection->session);
- FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_R);
- FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_W);
- switch(SSL_get_error(connection->session, result)) {
- case SSL_ERROR_NONE:
- FlagClr(&connection->flags, SSLFLAG_HANDSHAKE);
- FlagSet(&connection->flags, SSLFLAG_READY);
-
- ssl_handshake_completed(connection, 1);
- return 0;
- case SSL_ERROR_WANT_READ:
- FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
- return 1;
- case SSL_ERROR_WANT_WRITE:
- FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
- return 1;
- default:
- //unset connection!
- //Handshake error!
- ssl_handshake_completed(connection, 0);
- return 0;
- }
- return 0;
-}
-
-struct SSLConnection *ssl_start_handshake_listener(struct SSLListener *listener, int fd, void *data, enum SSLDataType datatype) {
- if(!listener)
- return NULL;
- struct SSLPendingConections *pending = NULL;
- struct SSLConnection *connection = malloc(sizeof(*connection));
- connection->session = SSL_new(listener->context);
- if(!connection->session) {
- goto ssl_start_handshake_listener_failed;
- }
- if(!SSL_set_fd(connection->session, fd)) {
- goto ssl_start_handshake_listener_failed;
- }
- FlagSet(&connection->flags, SSLFLAG_INCOMING);
- FlagSet(&connection->flags, SSLFLAG_HANDSHAKE);
- FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
-
- pending = malloc(sizeof(*pending));
- if(!pending) {
- goto ssl_start_handshake_listener_failed;
- }
- pending->connection = connection;
- pending->next = firstPendingConection;
- firstPendingConection = pending;
-
- pending->data = data;
- pending->datatype = datatype;
-
- ssl_handshake_incoming(connection);
- return connection;
-ssl_start_handshake_listener_failed:
- free(connection);
- return NULL;
-}
-
-IOResult ssl_recv_decrypt(struct SSLConnection *connection, char *buf, unsigned int buflen, unsigned int *len) {
- if(FlagHas(&connection->flags, SSLFLAG_HANDSHAKE)) {
- if(FlagHas(&connection->flags, SSLFLAG_INCOMING)) {
- ssl_handshake_incoming(connection);
- return IO_BLOCKED;
- }
- if(FlagHas(&connection->flags, SSLFLAG_OUTGOING)) {
- ssl_handshake_outgoing(connection);
- return IO_BLOCKED;
- }
- }
-
- *len = SSL_read(connection->session, buf, buflen);
- FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_R);
- int err = SSL_get_error(connection->session, *len);
- switch(err) {
- case SSL_ERROR_NONE:
- return IO_SUCCESS;
- case SSL_ERROR_ZERO_RETURN:
- return IO_FAILURE;
- case SSL_ERROR_WANT_READ:
- FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
- return IO_BLOCKED;
- case SSL_ERROR_WANT_WRITE:
- FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
- return IO_BLOCKED;
- case SSL_ERROR_SYSCALL:
- return IO_FAILURE;
- default:
- return IO_FAILURE;
- }
-}
-
-static ssize_t ssl_writev(SSL *ssl, const struct iovec *vector, int count) {
- char *buffer;
- register char *bp;
- size_t bytes, to_copy;
- int i;
-
- /* Find the total number of bytes to be written. */
- bytes = 0;
- for (i = 0; i < count; ++i)
- bytes += vector[i].iov_len;
-
- /* Allocate a temporary buffer to hold the data. */
- buffer = (char *) alloca (bytes);
-
- /* Copy the data into BUFFER. */
- to_copy = bytes;
- bp = buffer;
- for (i = 0; i < count; ++i) {
- size_t copy = ((vector[i].iov_len) > (to_copy) ? (to_copy) : (vector[i].iov_len));
- memcpy ((void *) bp, (void *) vector[i].iov_base, copy);
- bp += copy;
- to_copy -= copy;
- if (to_copy == 0)
- break;
- }
- return SSL_write(ssl, buffer, bytes);
-}
-
-IOResult ssl_send_encrypt_plain(struct SSLConnection *connection, char* buf, int len) {
- return SSL_write(connection->session, buf, len);
-}
-
-IOResult ssl_send_encrypt(struct SSLConnection *connection, struct MsgQ* buf, unsigned int *count_in, unsigned int *count_out) {
- int res;
- int count;
- struct iovec iov[IOV_MAX];
-
- assert(0 != buf);
- assert(0 != count_in);
- assert(0 != count_out);
-
- *count_in = 0;
- count = msgq_mapiov(buf, iov, IOV_MAX, count_in);
- res = ssl_writev(connection->session, iov, count);
-
- switch(SSL_get_error(connection->session, res)) {
- case SSL_ERROR_NONE:
- case SSL_ERROR_ZERO_RETURN:
- *count_out = (unsigned) res;
- return IO_SUCCESS;
- case SSL_ERROR_WANT_READ:
- FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
- return IO_BLOCKED;
- case SSL_ERROR_WANT_WRITE:
- FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
- return IO_BLOCKED;
- default:
- *count_out = 0;
- return IO_FAILURE;
- }
-}
-
-int ssl_connection_flush(struct SSLConnection *connection) {
- if(connection) {
- if(ssl_handshake(connection)) {
- if(FlagHas(&connection->flags, SSLFLAG_INCOMING)) {
- return ssl_handshake_incoming(connection);
- }
- if(FlagHas(&connection->flags, SSLFLAG_OUTGOING)) {
- return ssl_handshake_outgoing(connection);
- }
- }
- } else {
- struct SSLPendingConections *curr, *last = NULL, *next;
- for(curr = firstPendingConection; curr; curr = next) {
- next = curr->next;
- if(!ssl_connection_flush(curr->connection)) {
- // connection is already in auth process here, curr is freed!
- continue;
- }
- last = curr;
- }
- }
- return 0;
-}
-
-const char* ssl_get_cipher(struct SSLConnection *connection) {
- if(!connection)
- return NULL;
- return SSL_get_cipher(connection->session);
-}
-
+#if defined(HAVE_GNUTLS)
+#include "ssl.gnutls.c"
+#elif defined(HAVE_OPENSSL)
+#include "ssl.openssl.c"
#else
void ssl_free_connection(struct SSLConnection *connection) {}
void ssl_free_listener(struct SSLConnection *listener) {}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/ssl.gnutls.c
+ * Copyright (C) 2015 pk910 (Philipp Kreil)
+ *
+ * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/** @file
+ * @brief Implementation of functions for handling ssl connections
+ * @version $Id$
+ */
+#include <gnutls/gnutls.h>
+
+#ifndef GNUTLS_SEC_PARAM_LEGACY
+#define GNUTLS_SEC_PARAM_LEGACY 2
+#endif
+
+struct SSLPendingConections {
+ struct SSLConnection *connection;
+ struct SSLPendingConections *next;
+
+ void *data;
+ enum SSLDataType datatype;
+};
+
+struct SSLPendingConections *firstPendingConection = NULL;
+int ssl_is_initialized = 0;
+static gnutls_dh_params_t ssl_dh_params;
+static unsigned int ssl_dh_params_bits;
+
+static int ssl_generate_dh_params() {
+ ssl_dh_params_bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LEGACY);
+ gnutls_dh_params_init(&ssl_dh_params);
+ gnutls_dh_params_generate2(ssl_dh_params, ssl_dh_params_bits);
+ return 0;
+}
+
+static void ssl_init() {
+ if(ssl_is_initialized)
+ return;
+ ssl_is_initialized = 1;
+ int res;
+ res = gnutls_global_init();
+
+ if(res != GNUTLS_E_SUCCESS) {
+ //TODO: Log Errors
+ }
+}
+
+void ssl_free_connection(struct SSLConnection *connection) {
+ gnutls_bye(connection->session, GNUTLS_SHUT_RDWR);
+ if(connection->credentials)
+ gnutls_certificate_free_credentials(connection->credentials);
+ gnutls_deinit(connection->session);
+ free(connection);
+}
+
+void ssl_free_listener(struct SSLListener *listener) {
+ gnutls_certificate_free_credentials(listener->credentials);
+ gnutls_priority_deinit(listener->priority);
+ free(listener);
+}
+
+static void ssl_handshake_completed(struct SSLConnection *connection, int success) {
+ struct SSLPendingConections *pending, *lastPending = NULL;
+ for(pending = firstPendingConection; pending; pending = pending->next) {
+ if(pending->connection == connection) {
+ if(lastPending)
+ lastPending->next = pending->next;
+ else
+ firstPendingConection = pending->next;
+ switch(pending->datatype) {
+ case SSLData_Client: {
+ struct Client *cptr = (struct Client *) pending->data;
+ if(success) {
+ if(FlagHas(&connection->flags, SSLFLAG_INCOMING))
+ start_auth(cptr);
+ else if(!completed_connection(cptr))
+ exit_client_msg(cptr, cptr, &me, "Registration failed.");
+ } else
+ exit_client_msg(cptr, cptr, &me, "SSL Handshake failed.");
+ }
+ break;
+ }
+ free(pending);
+ }
+ lastPending = pending;
+ }
+}
+
+static int ssl_handshake_outgoing(struct SSLConnection *connection) {
+ int ret = gnutls_handshake(connection->session);
+ FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_R);
+ FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_W);
+
+ if(ret < 0) {
+ if(gnutls_error_is_fatal(ret) == 0) {
+ if(gnutls_record_get_direction(connection->session))
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
+ else
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
+ return 1;
+ } else {
+
+ return 0;
+ }
+ } else {
+ FlagClr(&connection->flags, SSLFLAG_HANDSHAKE);
+ FlagSet(&connection->flags, SSLFLAG_READY);
+ ssl_handshake_completed(connection, 1);
+ return 0;
+ }
+}
+
+struct SSLConnection *ssl_create_connect(int fd, void *data, enum SSLDataType datatype) {
+ struct SSLConnection *connection = malloc(sizeof(*connection));
+ struct SSLPendingConections *pending = NULL;
+
+ if(!connection)
+ return NULL;
+
+ if(!ssl_is_initialized)
+ ssl_init();
+
+ gnutls_certificate_allocate_credentials(&connection->credentials);
+ gnutls_init(&connection->session, GNUTLS_CLIENT);
+
+ gnutls_priority_set_direct(connection->session, "SECURE128:+SECURE192:-VERS-TLS-ALL:+VERS-TLS1.2", NULL);
+ gnutls_credentials_set(connection->session, GNUTLS_CRD_CERTIFICATE, connection->credentials);
+
+ gnutls_transport_set_ptr(connection->session, (gnutls_transport_ptr_t)fd);
+ //gnutls_handshake_set_timeout(connection->session, 30);
+
+ FlagSet(&connection->flags, SSLFLAG_OUTGOING);
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE);
+
+ pending = malloc(sizeof(*pending));
+ if(!pending) {
+ goto ssl_create_connect_failed;
+ }
+ pending->connection = connection;
+ pending->next = firstPendingConection;
+ firstPendingConection = pending;
+
+ pending->data = data;
+ pending->datatype = datatype;
+
+ ssl_handshake_outgoing(connection);
+
+ return connection;
+ssl_create_connect_failed:
+ free(connection);
+ return NULL;
+}
+
+void ssl_start_handshake_connect(struct SSLConnection *connection) {
+ ssl_handshake_outgoing(connection);
+}
+
+struct SSLListener *ssl_create_listener() {
+ if(!ssl_is_initialized)
+ ssl_init();
+
+ struct SSLListener *listener = calloc(1, sizeof(*listener));
+
+ gnutls_priority_init(&listener->priority, "SECURE128:+SECURE192:-VERS-TLS-ALL:+VERS-TLS1.2", NULL);
+ gnutls_certificate_allocate_credentials(&listener->credentials);
+
+ char *certfile = conf_get_local()->sslcertfile;
+ char *keyfile = conf_get_local()->sslkeyfile;
+ char *cafile = conf_get_local()->sslcafile;
+
+ if(!certfile) {
+ goto ssl_create_listener_failed;
+ }
+ if(!keyfile) {
+ keyfile = certfile;
+ }
+
+ /* load certificate */
+ if(gnutls_certificate_set_x509_key_file(listener->credentials, certfile, keyfile, GNUTLS_X509_FMT_PEM) < 0) {
+ goto ssl_create_listener_failed;
+ }
+ /* load cafile */
+ //TODO: ca file check!
+ /*
+ if(cafile && cafile[0] && SSL_CTX_load_verify_locations(listener->context, cafile, NULL) <= 0) {
+ goto ssl_create_listener_failed;
+ }
+ */
+
+ gnutls_certificate_set_dh_params(listener->credentials, ssl_dh_params);
+
+ FlagSet(&listener->flags, SSLFLAG_READY);
+ return listener;
+ssl_create_listener_failed:
+ free(listener);
+ return NULL;
+}
+
+struct SSLConnection *ssl_start_handshake_listener(struct SSLListener *listener, int fd, void *data, enum SSLDataType datatype) {
+ if(!listener)
+ return NULL;
+ struct SSLPendingConections *pending = NULL;
+ struct SSLConnection *connection = malloc(sizeof(*connection));
+
+ gnutls_init(&connection->session, GNUTLS_SERVER);
+ gnutls_priority_set(connection->session, listener->priority);
+ gnutls_credentials_set(connection->session, GNUTLS_CRD_CERTIFICATE, listener->credentials);
+ connection->credentials = NULL;
+ gnutls_dh_set_prime_bits(connection->session, ssl_dh_params_bits);
+ gnutls_certificate_server_set_request(connection->session, GNUTLS_CERT_IGNORE);
+
+ gnutls_transport_set_ptr(connection->session, (gnutls_transport_ptr_t)fd);
+
+ FlagSet(&connection->flags, SSLFLAG_INCOMING);
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE);
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
+
+ pending = malloc(sizeof(*pending));
+ if(!pending) {
+ goto ssl_start_handshake_listener_failed;
+ }
+ pending->connection = connection;
+ pending->next = firstPendingConection;
+ firstPendingConection = pending;
+
+ pending->data = data;
+ pending->datatype = datatype;
+
+ ssl_handshake_outgoing(connection);
+ return connection;
+ssl_start_handshake_listener_failed:
+ free(connection);
+ return NULL;
+}
+
+IOResult ssl_recv_decrypt(struct SSLConnection *connection, char *buf, unsigned int buflen, unsigned int *len) {
+ if(FlagHas(&connection->flags, SSLFLAG_HANDSHAKE)) {
+ ssl_handshake_outgoing(connection);
+ return IO_BLOCKED;
+ }
+
+ int ret = gnutls_record_recv(connection->session, buf, buflen);
+
+ if(ret == 0) {
+ return IO_FAILURE;
+ } else if(ret < 0 && gnutls_error_is_fatal(ret) == 0) {
+ if(ret == GNUTLS_E_REHANDSHAKE) {
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE);
+ ssl_handshake_outgoing(connection);
+ return IO_BLOCKED;
+ }
+ } else if(ret < 0) {
+ return IO_FAILURE;
+ }
+ *len = ret;
+ return IO_SUCCESS;
+}
+
+static ssize_t ssl_writev(gnutls_session_t ssl, const struct iovec *vector, int count) {
+ char *buffer;
+ register char *bp;
+ size_t bytes, to_copy;
+ int i;
+
+ /* Find the total number of bytes to be written. */
+ bytes = 0;
+ for (i = 0; i < count; ++i)
+ bytes += vector[i].iov_len;
+
+ /* Allocate a temporary buffer to hold the data. */
+ buffer = (char *) alloca (bytes);
+
+ /* Copy the data into BUFFER. */
+ to_copy = bytes;
+ bp = buffer;
+ for (i = 0; i < count; ++i) {
+ size_t copy = ((vector[i].iov_len) > (to_copy) ? (to_copy) : (vector[i].iov_len));
+ memcpy ((void *) bp, (void *) vector[i].iov_base, copy);
+ bp += copy;
+ to_copy -= copy;
+ if (to_copy == 0)
+ break;
+ }
+ return gnutls_record_send(ssl, buffer, bytes);
+}
+
+IOResult ssl_send_encrypt_plain(struct SSLConnection *connection, char* buf, int len) {
+ return gnutls_record_send(connection->session, buf, len);
+}
+
+IOResult ssl_send_encrypt(struct SSLConnection *connection, struct MsgQ* buf, unsigned int *count_in, unsigned int *count_out) {
+ int res;
+ int count;
+ struct iovec iov[IOV_MAX];
+
+ assert(0 != buf);
+ assert(0 != count_in);
+ assert(0 != count_out);
+
+ *count_in = 0;
+ count = msgq_mapiov(buf, iov, IOV_MAX, count_in);
+ res = ssl_writev(connection->session, iov, count);
+
+ if(res == 0) {
+ *count_out = 0;
+ return IO_FAILURE;
+ } else if(res < 0 && gnutls_error_is_fatal(res) == 0) {
+ if(res == GNUTLS_E_REHANDSHAKE) {
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE);
+ ssl_handshake_outgoing(connection);
+ return IO_BLOCKED;
+ }
+ } else if(res < 0) {
+ *count_out = 0;
+ return IO_FAILURE;
+ }
+ *count_out = (unsigned) res;
+ return IO_SUCCESS;
+}
+
+int ssl_connection_flush(struct SSLConnection *connection) {
+ if(connection) {
+ if(ssl_handshake(connection)) {
+ return ssl_handshake_outgoing(connection);
+ }
+ } else {
+ struct SSLPendingConections *curr, *last = NULL, *next;
+ for(curr = firstPendingConection; curr; curr = next) {
+ next = curr->next;
+ if(!ssl_connection_flush(curr->connection)) {
+ // connection is already in auth process here, curr is freed!
+ continue;
+ }
+ last = curr;
+ }
+ }
+ return 0;
+}
+
+const char* ssl_get_cipher(struct SSLConnection *connection) {
+ if(!connection)
+ return NULL;
+ static char buf[401];
+ const char *kx_name, *cipher_name, *mac_name;
+ unsigned int len, i;
+ char *dest;
+
+ kx_name = gnutls_kx_get_name(gnutls_kx_get(connection->session));
+ cipher_name = gnutls_cipher_get_name(gnutls_cipher_get(connection->session));
+ mac_name = gnutls_mac_get_name(gnutls_mac_get(connection->session));
+
+ if(!kx_name || !cipher_name || !mac_name) {
+ return "<invalid>";
+ }
+
+ len = strlen(kx_name) + strlen(cipher_name) + strlen(mac_name);
+ if(len > 395) {
+ return "<invalid>";
+ }
+ else {
+ dest = buf;
+ i = 0;
+ while((*dest++ = kx_name[i++])) /* empty */ ;
+ *(dest - 1) = '-';
+ i = 0;
+ while((*dest++ = cipher_name[i++])) /* empty */ ;
+ *(dest - 1) = '-';
+ i = 0;
+ while((*dest++ = mac_name[i++])) /* empty */ ;
+ return buf;
+ }
+}
--- /dev/null
+/*
+ * IRC - Internet Relay Chat, ircd/ssl.openssl.c
+ * Copyright (C) 2015 pk910 (Philipp Kreil)
+ *
+ * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+/** @file
+ * @brief Implementation of functions for handling ssl connections
+ * @version $Id$
+ */
+
+static struct SSLPendingConections {
+ struct SSLConnection *connection;
+ struct SSLPendingConections *next;
+
+ void *data;
+ enum SSLDataType datatype;
+};
+
+struct SSLPendingConections *firstPendingConection = NULL;
+int ssl_is_initialized = 0;
+
+static void ssl_init() {
+ if(ssl_is_initialized)
+ return;
+ ssl_is_initialized = 1;
+ SSL_library_init();
+ OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */
+ SSL_load_error_strings();
+}
+
+void ssl_free_connection(struct SSLConnection *connection) {
+ SSL_CTX *context = NULL;
+ if(FlagHas(&connection->flags, SSLFLAG_OUTGOING)) {
+ struct SSLOutConnection *outconn = (struct SSLOutConnection *)connection;
+ context = outconn->context;
+ }
+ SSL_shutdown(connection->session);
+ SSL_free(connection->session);
+ if(context)
+ SSL_CTX_free(context);
+ free(connection);
+}
+
+void ssl_free_listener(struct SSLListener *listener) {
+ SSL_CTX_free(listener->context);
+ free(listener);
+}
+
+static void ssl_handshake_completed(struct SSLConnection *connection, int success) {
+ struct SSLPendingConections *pending, *lastPending = NULL;
+ for(pending = firstPendingConection; pending; pending = pending->next) {
+ if(pending->connection == connection) {
+ if(lastPending)
+ lastPending->next = pending->next;
+ else
+ firstPendingConection = pending->next;
+ switch(pending->datatype) {
+ case SSLData_Client: {
+ struct Client *cptr = (struct Client *) pending->data;
+ if(success) {
+ if(FlagHas(&connection->flags, SSLFLAG_INCOMING))
+ start_auth(cptr);
+ else if(!completed_connection(cptr))
+ exit_client_msg(cptr, cptr, &me, "Registration failed.");
+ } else
+ exit_client_msg(cptr, cptr, &me, "SSL Handshake failed.");
+ }
+ break;
+ }
+ free(pending);
+ }
+ lastPending = pending;
+ }
+}
+
+static int ssl_handshake_outgoing(struct SSLConnection *connection) {
+ int ret = SSL_do_handshake(connection->session);
+ FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_R);
+ FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_W);
+
+ switch(SSL_get_error(connection->session, ret)) {
+ case SSL_ERROR_NONE:
+ FlagClr(&connection->flags, SSLFLAG_HANDSHAKE);
+ FlagSet(&connection->flags, SSLFLAG_READY);
+
+ ssl_handshake_completed(connection, 1);
+ break;
+ case SSL_ERROR_WANT_READ:
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
+ break;
+ default:
+
+ break;
+ }
+}
+
+struct SSLConnection *ssl_create_connect(int fd, void *data, enum SSLDataType datatype) {
+ struct SSLOutConnection *connection = malloc(sizeof(*connection));
+ struct SSLConnection *sslconn = (struct SSLConnection *)connection;
+ struct SSLPendingConections *pending = NULL;
+
+ if(!connection)
+ return NULL;
+
+ if(!ssl_is_initialized)
+ ssl_init();
+
+ connection->context = SSL_CTX_new(SSLv23_client_method());
+ if(!connection->context) {
+ goto ssl_create_connect_failed;
+ }
+ connection->session = SSL_new(connection->context);
+ if(!connection->session) {
+ goto ssl_create_connect_failed;
+ }
+ if(!SSL_set_fd(connection->session, fd)) {
+ goto ssl_create_connect_failed;
+ }
+ SSL_set_connect_state(connection->session);
+ FlagSet(&connection->flags, SSLFLAG_OUTGOING);
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE);
+
+ pending = malloc(sizeof(*pending));
+ if(!pending) {
+ goto ssl_create_connect_failed;
+ }
+ pending->connection = connection;
+ pending->next = firstPendingConection;
+ firstPendingConection = pending;
+
+ pending->data = data;
+ pending->datatype = datatype;
+
+ return sslconn;
+ssl_create_connect_failed:
+ free(connection);
+ return NULL;
+}
+
+void ssl_start_handshake_connect(struct SSLConnection *connection) {
+ ssl_handshake_outgoing(connection);
+}
+
+struct SSLListener *ssl_create_listener() {
+ if(!ssl_is_initialized)
+ ssl_init();
+
+ struct SSLListener *listener = calloc(1, sizeof(*listener));
+ listener->context = SSL_CTX_new(SSLv23_server_method());
+ if(!listener->context) {
+ goto ssl_create_listener_failed;
+ }
+
+ char *certfile = conf_get_local()->sslcertfile;
+ char *keyfile = conf_get_local()->sslkeyfile;
+ char *cafile = conf_get_local()->sslcafile;
+
+ if(!certfile) {
+ goto ssl_create_listener_failed;
+ }
+ if(!keyfile) {
+ keyfile = certfile;
+ }
+
+ /* load certificate */
+ if(SSL_CTX_use_certificate_file(listener->context, certfile, SSL_FILETYPE_PEM) <= 0) {
+ goto ssl_create_listener_failed;
+ }
+ /* load keyfile */
+ if(SSL_CTX_use_PrivateKey_file(listener->context, keyfile, SSL_FILETYPE_PEM) <= 0) {
+ goto ssl_create_listener_failed;
+ }
+ /* check certificate and keyfile */
+ if(!SSL_CTX_check_private_key(listener->context)) {
+ goto ssl_create_listener_failed;
+ }
+ /* load cafile */
+ if(cafile && cafile[0] && SSL_CTX_load_verify_locations(listener->context, cafile, NULL) <= 0) {
+ goto ssl_create_listener_failed;
+ }
+ FlagSet(&listener->flags, SSLFLAG_READY);
+ return listener;
+ssl_create_listener_failed:
+ free(listener);
+ return NULL;
+}
+
+static int ssl_handshake_incoming(struct SSLConnection *connection) {
+ int result = SSL_accept(connection->session);
+ FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_R);
+ FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_W);
+ switch(SSL_get_error(connection->session, result)) {
+ case SSL_ERROR_NONE:
+ FlagClr(&connection->flags, SSLFLAG_HANDSHAKE);
+ FlagSet(&connection->flags, SSLFLAG_READY);
+
+ ssl_handshake_completed(connection, 1);
+ return 0;
+ case SSL_ERROR_WANT_READ:
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
+ return 1;
+ case SSL_ERROR_WANT_WRITE:
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
+ return 1;
+ default:
+ //unset connection!
+ //Handshake error!
+ ssl_handshake_completed(connection, 0);
+ return 0;
+ }
+ return 0;
+}
+
+struct SSLConnection *ssl_start_handshake_listener(struct SSLListener *listener, int fd, void *data, enum SSLDataType datatype) {
+ if(!listener)
+ return NULL;
+ struct SSLPendingConections *pending = NULL;
+ struct SSLConnection *connection = malloc(sizeof(*connection));
+ connection->session = SSL_new(listener->context);
+ if(!connection->session) {
+ goto ssl_start_handshake_listener_failed;
+ }
+ if(!SSL_set_fd(connection->session, fd)) {
+ goto ssl_start_handshake_listener_failed;
+ }
+ FlagSet(&connection->flags, SSLFLAG_INCOMING);
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE);
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
+
+ pending = malloc(sizeof(*pending));
+ if(!pending) {
+ goto ssl_start_handshake_listener_failed;
+ }
+ pending->connection = connection;
+ pending->next = firstPendingConection;
+ firstPendingConection = pending;
+
+ pending->data = data;
+ pending->datatype = datatype;
+
+ ssl_handshake_incoming(connection);
+ return connection;
+ssl_start_handshake_listener_failed:
+ free(connection);
+ return NULL;
+}
+
+IOResult ssl_recv_decrypt(struct SSLConnection *connection, char *buf, unsigned int buflen, unsigned int *len) {
+ if(FlagHas(&connection->flags, SSLFLAG_HANDSHAKE)) {
+ if(FlagHas(&connection->flags, SSLFLAG_INCOMING)) {
+ ssl_handshake_incoming(connection);
+ return IO_BLOCKED;
+ }
+ if(FlagHas(&connection->flags, SSLFLAG_OUTGOING)) {
+ ssl_handshake_outgoing(connection);
+ return IO_BLOCKED;
+ }
+ }
+
+ *len = SSL_read(connection->session, buf, buflen);
+ FlagClr(&connection->flags, SSLFLAG_HANDSHAKE_R);
+ int err = SSL_get_error(connection->session, *len);
+ switch(err) {
+ case SSL_ERROR_NONE:
+ return IO_SUCCESS;
+ case SSL_ERROR_ZERO_RETURN:
+ return IO_FAILURE;
+ case SSL_ERROR_WANT_READ:
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
+ return IO_BLOCKED;
+ case SSL_ERROR_WANT_WRITE:
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
+ return IO_BLOCKED;
+ case SSL_ERROR_SYSCALL:
+ return IO_FAILURE;
+ default:
+ return IO_FAILURE;
+ }
+}
+
+static ssize_t ssl_writev(SSL *ssl, const struct iovec *vector, int count) {
+ char *buffer;
+ register char *bp;
+ size_t bytes, to_copy;
+ int i;
+
+ /* Find the total number of bytes to be written. */
+ bytes = 0;
+ for (i = 0; i < count; ++i)
+ bytes += vector[i].iov_len;
+
+ /* Allocate a temporary buffer to hold the data. */
+ buffer = (char *) alloca (bytes);
+
+ /* Copy the data into BUFFER. */
+ to_copy = bytes;
+ bp = buffer;
+ for (i = 0; i < count; ++i) {
+ size_t copy = ((vector[i].iov_len) > (to_copy) ? (to_copy) : (vector[i].iov_len));
+ memcpy ((void *) bp, (void *) vector[i].iov_base, copy);
+ bp += copy;
+ to_copy -= copy;
+ if (to_copy == 0)
+ break;
+ }
+ return SSL_write(ssl, buffer, bytes);
+}
+
+IOResult ssl_send_encrypt_plain(struct SSLConnection *connection, char* buf, int len) {
+ return SSL_write(connection->session, buf, len);
+}
+
+IOResult ssl_send_encrypt(struct SSLConnection *connection, struct MsgQ* buf, unsigned int *count_in, unsigned int *count_out) {
+ int res;
+ int count;
+ struct iovec iov[IOV_MAX];
+
+ assert(0 != buf);
+ assert(0 != count_in);
+ assert(0 != count_out);
+
+ *count_in = 0;
+ count = msgq_mapiov(buf, iov, IOV_MAX, count_in);
+ res = ssl_writev(connection->session, iov, count);
+
+ switch(SSL_get_error(connection->session, res)) {
+ case SSL_ERROR_NONE:
+ case SSL_ERROR_ZERO_RETURN:
+ *count_out = (unsigned) res;
+ return IO_SUCCESS;
+ case SSL_ERROR_WANT_READ:
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_R);
+ return IO_BLOCKED;
+ case SSL_ERROR_WANT_WRITE:
+ FlagSet(&connection->flags, SSLFLAG_HANDSHAKE_W);
+ return IO_BLOCKED;
+ default:
+ *count_out = 0;
+ return IO_FAILURE;
+ }
+}
+
+int ssl_connection_flush(struct SSLConnection *connection) {
+ if(connection) {
+ if(ssl_handshake(connection)) {
+ if(FlagHas(&connection->flags, SSLFLAG_INCOMING)) {
+ return ssl_handshake_incoming(connection);
+ }
+ if(FlagHas(&connection->flags, SSLFLAG_OUTGOING)) {
+ return ssl_handshake_outgoing(connection);
+ }
+ }
+ } else {
+ struct SSLPendingConections *curr, *last = NULL, *next;
+ for(curr = firstPendingConection; curr; curr = next) {
+ next = curr->next;
+ if(!ssl_connection_flush(curr->connection)) {
+ // connection is already in auth process here, curr is freed!
+ continue;
+ }
+ last = curr;
+ }
+ }
+ return 0;
+}
+
+const char* ssl_get_cipher(struct SSLConnection *connection) {
+ if(!connection)
+ return NULL;
+ static char buf[400];
+ char buf2[128];
+ int bits;
+ SSL_CIPHER *c;
+
+ buf[0] = '\0';
+ strcpy(buf, SSL_get_version(connection->session));
+ strcat(buf, "-");
+ strcat(buf, SSL_get_cipher(connection->session));
+ c = SSL_get_current_cipher(connection->session);
+ SSL_CIPHER_get_bits(c, &bits);
+ strcat(buf, "-");
+ ircd_snprintf(0, buf2, sizeof(buf2), "%d", bits);
+ strcat(buf, buf2);
+ strcat(buf, "bits");
+ return buf;
+}