[IOMultiplexerV2] added GnuTLS support
authorpk910 <philipp@zoelle1.de>
Sat, 15 Mar 2014 21:50:20 +0000 (22:50 +0100)
committerpk910 <philipp@zoelle1.de>
Sun, 16 Mar 2014 14:12:27 +0000 (15:12 +0100)
configure.ac
src/IOHandler/IOHandler_config.h
src/IOHandler/IOSSLBackend.c
src/IOHandler/IOSSLBackend.h
src/IOHandler_test/socket/iotest.c

index d9cdd6a4c28ddbb50187cba47517b4483768095d..fb64def438c9dc92db995917c48d955f1163fe95 100644 (file)
@@ -36,13 +36,22 @@ AC_CHECK_FUNCS([usleep select socket inet_pton inet_ntop])
 AC_CHECK_HEADERS([fcntl.h sys/socket.h sys/select.h sys/time.h sys/types.h unistd.h windows.h winsock2.h errno.h sys/epoll.h sys/event.h])
 
 AC_CHECK_LIB(ws2_32, main, [ LIBS="$LIBS -lws2_32" ], [])
-AC_CHECK_LIB(ssl, SSL_read, [
-  AC_CHECK_LIB(crypto, X509_new, [
-    AC_CHECK_HEADERS(openssl/ssl.h openssl/err.h openssl/rand.h, [
-      LIBS="$LIBS -lssl -lcrypto"
-    ])
+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
+  AC_CHECK_LIB(ssl, SSL_read, [
+    AC_CHECK_LIB(crypto, X509_new, [
+      AC_CHECK_HEADERS(openssl/ssl.h openssl/err.h openssl/rand.h, [
+        LIBS="$LIBS -lssl -lcrypto"
+      ])
+    ])
+  ])
+fi
 AC_CHECK_LIB(pthread, pthread_create, [
   AC_CHECK_HEADERS(pthread.h, [
     LIBS="$LIBS -lpthread"
index 7e83d8c295d08335bf8916acec3720f5bd4ab2ca..4053a2aeec2a5ea9dbc7858648abe63817032bb9 100644 (file)
  AC_CHECK_HEADERS([fcntl.h sys/socket.h sys/select.h sys/time.h sys/types.h unistd.h windows.h winsock2.h errno.h sys/epoll.h sys/event.h])
  
  AC_CHECK_LIB(ws2_32, main, [ LIBS="$LIBS -lws2_32" ], [])
- AC_CHECK_LIB(ssl, SSL_read, [
  AC_CHECK_LIB(crypto, X509_new, [
-     AC_CHECK_HEADERS(openssl/ssl.h openssl/err.h openssl/rand.h, [
-       LIBS="$LIBS -lssl -lcrypto"
-     ])
+ 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
+   AC_CHECK_LIB(ssl, SSL_read, [
+     AC_CHECK_LIB(crypto, X509_new, [
+       AC_CHECK_HEADERS(openssl/ssl.h openssl/err.h openssl/rand.h, [
+         LIBS="$LIBS -lssl -lcrypto"
+       ])
+     ])
+   ])
+ fi
  AC_CHECK_LIB(pthread, pthread_create, [
    AC_CHECK_HEADERS(pthread.h, [
      LIBS="$LIBS -lpthread"
index 6e595454eebd6e6a03c02198e123fa651f4d5362..ab0f26ebb4726596f9427ddddfdbc1a48a5e7252 100644 (file)
 #include "IOSockets.h"
 #include "IOSSLBackend.h"
 
-#ifdef HAVE_OPENSSL_SSL_H
+#if defined(HAVE_GNUTLS_GNUTLS_H)
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+/* GnuTLS Backend */
+static gnutls_dh_params_t dh_params;
+
+static int generate_dh_params() {
+       unsigned int bits = gnutls_sec_param_to_pk_bits(GNUTLS_PK_DH, GNUTLS_SEC_PARAM_LEGACY);
+       gnutls_dh_params_init(&dh_params);
+       gnutls_dh_params_generate2(dh_params, bits);
+       return 0;
+}
+
+void iossl_init() {
+    int ret;
+       if((ret = gnutls_global_init()) != GNUTLS_E_SUCCESS) {
+               iolog_trigger(IOLOG_ERROR, "SSL: gnutls_global_init(): failed (%d)", ret);
+               //TODO: Error handling?
+               return;
+       }
+       generate_dh_params();
+}
+
+// Client
+void iossl_connect(struct _IOSocket *iosock) {
+    struct IOSSLDescriptor *sslnode = malloc(sizeof(*sslnode));
+       int err;
+       
+       err = gnutls_certificate_allocate_credentials(&sslnode->ssl.client.credentials);
+       if(err < 0) {
+               goto ssl_connect_err;
+       }
+       
+       gnutls_init(&sslnode->ssl.client.session, GNUTLS_CLIENT);
+       
+       gnutls_priority_set_direct(sslnode->ssl.client.session, "NONE:+VERS-TLS1.0:+VERS-SSL3.0:+CIPHER-ALL:+COMP-ALL:+RSA:+DHE-RSA:+DHE-DSS:+MAC-ALL", NULL);
+       gnutls_credentials_set(sslnode->ssl.client.session, GNUTLS_CRD_CERTIFICATE, sslnode->ssl.client.credentials);
+    
+       gnutls_transport_set_int(sslnode->ssl.client.session, iosock->fd);
+       gnutls_handshake_set_timeout(sslnode->ssl.client.session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+       
+    iosock->sslnode = sslnode;
+       iosock->socket_flags |= IOSOCKETFLAG_SSL_HANDSHAKE;
+    iossl_client_handshake(iosock);
+    return;
+ssl_connect_err:
+    free(sslnode);
+    iosocket_events_callback(iosock, 0, 0);
+}
+
+void iossl_client_handshake(struct _IOSocket *iosock) {
+    // Perform an SSL handshake.
+    int ret = gnutls_handshake(iosock->sslnode->ssl.client.session);
+    iosock->socket_flags &= ~IOSOCKETFLAG_SSL_WANTWRITE;
+       
+       if(ret < 0) {
+               if(gnutls_error_is_fatal(ret) == 0) {
+                       if(gnutls_record_get_direction(iosock->sslnode->ssl.client.session)) {
+                               iosock->socket_flags |= IOSOCKETFLAG_SSL_WANTWRITE;
+                               iolog_trigger(IOLOG_DEBUG, "gnutls_handshake for fd %d wants to write...", iosock->fd);
+                       } else {
+                               iolog_trigger(IOLOG_DEBUG, "gnutls_handshake for fd %d wants to read...", iosock->fd);
+                       }
+               } else {
+                       iolog_trigger(IOLOG_ERROR, "gnutls_handshake for fd %d failed with %s", iosock->fd, gnutls_strerror(ret));
+                       iosocket_events_callback(iosock, 0, 0);
+               }
+       } else {
+               char *desc;
+               desc = gnutls_session_get_desc(iosock->sslnode->ssl.client.session);
+               iolog_trigger(IOLOG_DEBUG, "SSL handshake for fd %d successful: %s", iosock->fd, desc);
+               gnutls_free(desc);
+               iosock->socket_flags |= IOSOCKETFLAG_SSL_ESTABLISHED;
+               iosocket_events_callback(iosock, 0, 0); //perform IOEVENT_CONNECTED event
+       }
+}
+
+
+// Server
+void iossl_listen(struct _IOSocket *iosock, const char *certfile, const char *keyfile) {
+    struct IOSSLDescriptor *sslnode = malloc(sizeof(*sslnode));
+       
+       gnutls_priority_init(&sslnode->ssl.server.priority, "NONE:+VERS-TLS1.0:+VERS-SSL3.0:+CIPHER-ALL:+COMP-ALL:+RSA:+DHE-RSA:+DHE-DSS:+MAC-ALL", NULL);
+       
+       gnutls_certificate_allocate_credentials(&sslnode->ssl.server.credentials);
+       int ret = gnutls_certificate_set_x509_key_file(sslnode->ssl.server.credentials, certfile, keyfile, GNUTLS_X509_FMT_PEM);
+    if (ret < 0) {
+               iolog_trigger(IOLOG_ERROR, "SSL: could not load server certificate/key (%s %s)", certfile, keyfile);
+               goto ssl_listen_err;
+       }
+       
+       gnutls_certificate_set_dh_params(sslnode->ssl.server.credentials, dh_params);
+       
+    iosock->sslnode = sslnode;
+    iosock->socket_flags |= IOSOCKETFLAG_SSL_ESTABLISHED;
+    return;
+ssl_listen_err:
+    free(sslnode);
+    iosock->sslnode = NULL;
+    iosocket_events_callback(iosock, 0, 0);
+}
+
+void iossl_client_accepted(struct _IOSocket *iosock, struct _IOSocket *new_iosock) {
+    struct IOSSLDescriptor *sslnode = malloc(sizeof(*sslnode));
+    
+       gnutls_init(&sslnode->ssl.client.session, GNUTLS_SERVER);
+       gnutls_priority_set(sslnode->ssl.client.session, iosock->sslnode->ssl.server.priority);
+       gnutls_credentials_set(sslnode->ssl.client.session, GNUTLS_CRD_CERTIFICATE, iosock->sslnode->ssl.server.credentials);
+       
+       /* We don't request any certificate from the client.
+        * If we did we would need to verify it.
+        */
+       gnutls_certificate_server_set_request(sslnode->ssl.client.session, GNUTLS_CERT_IGNORE);
+       
+       gnutls_transport_set_int(sslnode->ssl.client.session, new_iosock->fd);
+       
+    new_iosock->sslnode = sslnode;
+    new_iosock->socket_flags |= IOSOCKETFLAG_SSL_HANDSHAKE;
+    return;
+       /*
+ssl_accept_err:
+    free(sslnode);
+    iosock->sslnode = NULL;
+    iosocket_events_callback(new_iosock, 0, 0);
+       */
+}
+
+void iossl_server_handshake(struct _IOSocket *iosock) {
+    return iossl_client_handshake(iosock);
+}
+
+void iossl_disconnect(struct _IOSocket *iosock) {
+    if(!iosock->sslnode) return;
+       
+       if((iosock->socket_flags & IOSOCKETFLAG_LISTENING)) {
+               gnutls_certificate_free_credentials(iosock->sslnode->ssl.server.credentials);
+               gnutls_priority_deinit(iosock->sslnode->ssl.server.priority);
+       } else {
+               gnutls_bye(iosock->sslnode->ssl.client.session, GNUTLS_SHUT_RDWR);
+               gnutls_certificate_free_credentials(iosock->sslnode->ssl.client.credentials);
+               gnutls_deinit(iosock->sslnode->ssl.client.session);
+       }
+       
+    free(iosock->sslnode);
+    iosock->sslnode = NULL;
+    iosock->socket_flags &= ~IOSOCKETFLAG_SSLSOCKET;
+}
+
+static void iossl_rehandshake(struct _IOSocket *iosock, int hsflag) {
+       int ret = gnutls_handshake(iosock->sslnode->ssl.client.session);
+    iosock->socket_flags &= ~IOSOCKETFLAG_SSL_WANTWRITE;
+       
+       if(ret < 0) {
+               if(gnutls_error_is_fatal(ret) == 0) {
+                       if(gnutls_record_get_direction(iosock->sslnode->ssl.client.session)) {
+                               iosock->socket_flags |= IOSOCKETFLAG_SSL_WANTWRITE | hsflag;
+                               iolog_trigger(IOLOG_DEBUG, "gnutls_handshake for fd %d wants to write...", iosock->fd);
+                       } else {
+                               iosock->socket_flags |= hsflag;
+                               iolog_trigger(IOLOG_DEBUG, "gnutls_handshake for fd %d wants to read...", iosock->fd);
+                       }
+               } else {
+                       iolog_trigger(IOLOG_ERROR, "gnutls_handshake for fd %d failed: %s", iosock->fd, gnutls_strerror(ret));
+                       //TODO: Error Action?
+               }
+       } else {
+               char *desc;
+               desc = gnutls_session_get_desc(iosock->sslnode->ssl.client.session);
+               iolog_trigger(IOLOG_DEBUG, "SSL handshake for fd %d successful: %s", iosock->fd, desc);
+               gnutls_free(desc);
+               iosock->socket_flags &= ~hsflag;
+       }
+}
+
+int iossl_read(struct _IOSocket *iosock, char *buffer, int len) {
+    if((iosock->socket_flags & (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_ESTABLISHED)) != (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_ESTABLISHED))
+               return 0;
+       if((iosock->socket_flags & IOSOCKETFLAG_SSL_READHS)) {
+               iossl_rehandshake(iosock, IOSOCKETFLAG_SSL_READHS);
+               errno = EAGAIN;
+               return -1;
+       }
+    int ret = gnutls_record_recv(iosock->sslnode->ssl.client.session, buffer, len);
+       if(ret == 0) {
+               //TLS session closed
+               //TODO: Action?
+       } else if(ret < 0 && gnutls_error_is_fatal(ret) == 0) {
+               iolog_trigger(IOLOG_WARNING, "gnutls_record_recv for fd %d returned %s", iosock->fd, gnutls_strerror(ret));
+               if(ret == GNUTLS_E_REHANDSHAKE) {
+                       iossl_rehandshake(iosock, IOSOCKETFLAG_SSL_READHS);
+                       errno = EAGAIN;
+                       return -1;
+               }
+       } else if(ret < 0) {
+               iolog_trigger(IOLOG_ERROR, "gnutls_record_recv for fd %d failed: %s", iosock->fd, gnutls_strerror(ret));
+               errno = ret;
+               return ret;
+       }
+    return ret;
+}
+
+int iossl_write(struct _IOSocket *iosock, char *buffer, int len) {
+    if((iosock->socket_flags & (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_ESTABLISHED)) != (IOSOCKETFLAG_SSLSOCKET | IOSOCKETFLAG_SSL_ESTABLISHED))
+               return 0;
+       if((iosock->socket_flags & IOSOCKETFLAG_SSL_WRITEHS)) {
+               iossl_rehandshake(iosock, IOSOCKETFLAG_SSL_WRITEHS);
+               errno = EAGAIN;
+               return -1;
+       }
+    int ret = gnutls_record_send(iosock->sslnode->ssl.client.session, buffer, len);
+    if(ret < 0 && gnutls_error_is_fatal(ret) == 0) {
+               iolog_trigger(IOLOG_WARNING, "gnutls_record_send for fd %d returned %s", iosock->fd, gnutls_strerror(ret));
+               if(ret == GNUTLS_E_REHANDSHAKE) {
+                       iossl_rehandshake(iosock, IOSOCKETFLAG_SSL_WRITEHS);
+                       errno = EAGAIN;
+                       return -1;
+               }
+       } else if(ret < 0) {
+               iolog_trigger(IOLOG_ERROR, "gnutls_record_send for fd %d failed: %s", iosock->fd, gnutls_strerror(ret));
+               errno = ret;
+               return ret;
+       }
+    return ret;
+}
+
+#elif defined(HAVE_OPENSSL_SSL_H)
 /* OpenSSL Backend */
 
 
@@ -131,7 +357,7 @@ ssl_listen_err:
 
 void iossl_client_accepted(struct _IOSocket *iosock, struct _IOSocket *new_iosock) {
     struct IOSSLDescriptor *sslnode = malloc(sizeof(*sslnode));
-    sslnode->sslHandle = SSL_new(sslnode->sslContext);
+    sslnode->sslHandle = SSL_new(iosock->sslnode->sslContext);
     if(!sslnode->sslHandle) {
         iossl_error();
         iolog_trigger(IOLOG_ERROR, "SSL: could not create client SSL Handle");
index 879537e9fdbc5557ac796f2515a13e8fb5a5fdfc..68253e84c01b1b8b5c18c153b498f2592614e9c2 100644 (file)
 
 struct _IOSocket;
 
-#ifdef HAVE_OPENSSL_SSL_H
+#if defined(HAVE_GNUTLS_GNUTLS_H)
+#include <gnutls/gnutls.h>
+struct IOSSLDescriptor {
+       union {
+               struct {
+                       gnutls_session_t session;
+                       gnutls_certificate_client_credentials credentials;
+               } client;
+               struct {
+                       gnutls_priority_t priority;
+                       gnutls_certificate_credentials_t credentials;
+               } server;
+       } ssl;
+};
+
+#elif defined(HAVE_OPENSSL_SSL_H)
 #include <openssl/rand.h>
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 
 struct IOSSLDescriptor {
-       unsigned int flags : 8;
     SSL *sslHandle;
     SSL_CTX *sslContext;
 };
index 0069b02d29b135914d8d943c83b8435339f8b584..867ca44c873cbee16b468ac36822838415b80141 100644 (file)
@@ -65,5 +65,5 @@ static IOSOCKET_CALLBACK(io_callback) {
 }
 
 static IOLOG_CALLBACK(io_log) {
-    //printf("%s", line);
+       printf("%s", message);
 }