added gnutls backend and moved backend code into new files upstream
authorpk910 <philipp@zoelle1.de>
Wed, 25 Nov 2015 18:05:26 +0000 (19:05 +0100)
committerpk910 <philipp@zoelle1.de>
Wed, 25 Nov 2015 18:05:26 +0000 (19:05 +0100)
gnutls backend is not working yet...

configure.in
include/ssl.h
ircd/ssl.c
ircd/ssl.gnutls.c [new file with mode: 0644]
ircd/ssl.openssl.c [new file with mode: 0644]

index d4205cfa3cf51734f64d3e7680d763c0a87094e2..fd1a4102f065306d3078efdbbc568dcbdc24d786 100644 (file)
@@ -226,24 +226,6 @@ else
 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],
@@ -733,6 +715,53 @@ AC_MSG_RESULT([$unet_cv_with_maxcon])
 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])
 
@@ -748,6 +777,8 @@ ircu is now hopefully configured for your system.
   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
index db4cad0f50fcc46f363a36e8e08e1034a80c5cca..03494cbc9231e9fcd8b54b0d563dbbed6962475f 100644 (file)
@@ -24,7 +24,7 @@ enum SSLDataType {
   SSLData_Client
 };
 
-#if defined(HAVE_GNUTLS_GNUTLS_H)
+#if defined(HAVE_GNUTLS)
 #include <gnutls/gnutls.h>
 
 struct SSLConnection {
@@ -39,7 +39,7 @@ struct SSLListener {
   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>
index 62f61470641b3bf1434d9dc9d0759cbb4a1de6bd..c58414ca8b4602aed10662a6b9160ac43039ee70 100644 (file)
@@ -17,7 +17,7 @@
  * 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) {}
diff --git a/ircd/ssl.gnutls.c b/ircd/ssl.gnutls.c
new file mode 100644 (file)
index 0000000..1b08540
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * 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;
+  }
+}
diff --git a/ircd/ssl.openssl.c b/ircd/ssl.openssl.c
new file mode 100644 (file)
index 0000000..f26a442
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ * 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;
+}