added SSL backend for IOMultiplexer master
authorpk910 <philipp@zoelle1.de>
Sun, 27 May 2012 00:48:45 +0000 (02:48 +0200)
committerpk910 <philipp@zoelle1.de>
Sun, 27 May 2012 01:13:37 +0000 (03:13 +0200)
Makefile.am
src/IOEngine.h
src/IOHandler.c
src/IOHandler.h
src/IOHandler_SSL.c [new file with mode: 0644]
src/IOHandler_SSL.h [new file with mode: 0644]
src/ServerSocket.c
src/UserClient.c
src/UserSession.c

index 1abc4d42bd597851c414cd334392a4a354eca1fb..f72746cc54b96df653b5250ff36bf9bfaa71ce71 100644 (file)
@@ -14,6 +14,7 @@ transirc_SOURCES = src/version.c \
       src/IOEngine_select.c \
       src/IOEngine_epoll.c \
       src/IOEngine_kevent.c \
+      src/IOHandler_SSL.c \
       src/main.c \
       src/ServerSocket.c \
       src/tools.c \
index 0f33e81954cb13a83367beb329198a7b4375b97b..8f4053b4823126e661c5b52d45eab8b4134a82aa 100644 (file)
@@ -46,7 +46,7 @@ struct IOEngine {
     void (*cleanup)(void);
 };
 
-#define iohandler_wants_writes(IOFD) (IOFD->writebuf.bufpos || IOFD->state == IO_CONNECTING
+#define iohandler_wants_writes(IOFD) ((IOFD->writebuf.bufpos && !IOFD->ssl_hs_read) || IOFD->state == IO_CONNECTING || IOFD->ssl_hs_write
 
 void iohandler_log(enum IOLogType type, char *text, ...);
 void iohandler_events(struct IODescriptor *iofd, int readable, int writeable);
index 7a9f3b3d30e7aa4b098c7c83cae4dd031dbbe3dc..953340e506fb28ba5eae6748044fbc68fcb1b4ca 100644 (file)
@@ -16,6 +16,7 @@
  */
 #include "IOHandler.h"
 #include "IOEngine.h"
+#include "IOHandler_SSL.h"
 
 #define MAXLOG 1024
 
@@ -36,6 +37,7 @@ void iohandler_log(enum IOLogType type, char *text, ...) {
     
     //do something with logBuf
     //...
+    printf("%s", logBuf);
 }
 
 /* IO Engines */
@@ -46,6 +48,7 @@ extern struct IOEngine engine_epoll;
 struct IOEngine *engine = NULL;
 
 static void iohandler_init_engine() {
+    if(engine) return;
     //try other engines
     if(!engine && engine_kevent.init && engine_kevent.init())
         engine = &engine_kevent;
@@ -61,6 +64,7 @@ static void iohandler_init_engine() {
         }
     }
     iohandler_log(IOLOG_DEBUG, "using %s IO engine", engine->name);
+    iohandler_ssl_init();
 }
 
 static void iohandler_append(struct IODescriptor *descriptor) {
@@ -323,13 +327,14 @@ struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port,
     }
     connect(sockfd, dstaddr, dstaddrlen); //returns EINPROGRESS here (nonblocking)
     descriptor->state = IO_CONNECTING;
+    descriptor->ssl = (ssl ? 1 : 0);
     descriptor->read_lines = 1;
     engine->update(descriptor);
     iohandler_log(IOLOG_DEBUG, "added client socket (%d) connecting to %s:%d", sockfd, hostname, port);
     return descriptor;
 }
 
-struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, int ssl, iohandler_callback *callback) {
+struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, iohandler_callback *callback) {
     int sockfd;
     struct addrinfo hints, *res, *freeres;
     struct sockaddr_in *ip4 = NULL;
@@ -449,7 +454,11 @@ void iohandler_printf(struct IODescriptor *iofd, const char *text, ...) {
 void iohandler_try_write(struct IODescriptor *iofd) {
     if(!iofd->writebuf.bufpos) return;
     iohandler_log(IOLOG_DEBUG, "write writebuf (%d bytes) to socket (fd: %d)", iofd->writebuf.bufpos, iofd->fd);
-    int res = send(iofd->fd, iofd->writebuf.buffer, iofd->writebuf.bufpos, 0);
+    int res;
+    if(iofd->ssl_active)
+        res = iohandler_ssl_write(iofd, iofd->writebuf.buffer, iofd->writebuf.bufpos);
+    else
+        res = send(iofd->fd, iofd->writebuf.buffer, iofd->writebuf.bufpos, 0);
     if(res < 0) {
         if (errno != EAGAIN) {
             iohandler_log(IOLOG_ERROR, "could not write to socket (fd: %d): %d - %s", iofd->fd, errno, strerror(errno));
@@ -481,6 +490,8 @@ void iohandler_close(struct IODescriptor *iofd) {
         iohandler_try_write(iofd);
     }
     //close IODescriptor
+    if(iofd->ssl)
+        iohandler_ssl_disconnect(iofd);
     if(iofd->type == IOTYPE_SERVER || iofd->type == IOTYPE_CLIENT || iofd->type == IOTYPE_STDIN)
         close(iofd->fd);
     iohandler_remove(iofd, engine_remove);
@@ -502,6 +513,15 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) {
     callback_event.type = IOEVENT_IGNORE;
     callback_event.iofd = iofd;
     switch(iofd->state) {
+        case IO_SSLWAIT:
+            if(!readable && !writeable) {
+                callback_event.type = IOEVENT_SSLFAILED;
+                iofd->state = IO_CLOSED;
+            } else {
+                iohandler_log(IOLOG_DEBUG, "triggering iohandler_ssl_client_handshake for %s (fd: %d)", iohandler_iotype_name(iofd->type), iofd->fd);
+                iohandler_ssl_client_handshake(iofd);
+            }
+            break;
         case IO_CLOSED:
             if(iofd->type == IOTYPE_TIMER)
                 callback_event.type = IOEVENT_TIMEOUT;
@@ -525,6 +545,11 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) {
                 iofd->state = IO_CLOSED;
                                engine->update(iofd);
             } else if(writeable) {
+                if(iofd->ssl && !iofd->ssl_active) {
+                    iohandler_log(IOLOG_DEBUG, "triggering iohandler_ssl_connect for %s (fd: %d)", iohandler_iotype_name(iofd->type), iofd->fd);
+                    iohandler_ssl_connect(iofd);
+                    return;
+                }
                 callback_event.type = IOEVENT_CONNECTED;
                 iofd->state = IO_CONNECTED;
                 engine->update(iofd);
@@ -533,7 +558,11 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) {
         case IO_CONNECTED:
             if(readable) {
                 if(iofd->read_lines) {
-                    int bytes = recv(iofd->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos, 0);
+                    int bytes;
+                    if(iofd->ssl_active)
+                        bytes = iohandler_ssl_read(iofd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos);
+                    else
+                        bytes = recv(iofd->fd, iofd->readbuf.buffer + iofd->readbuf.bufpos, iofd->readbuf.buflen - iofd->readbuf.bufpos, 0);
                     if(bytes <= 0) {
                         if (errno != EAGAIN) {
                             iofd->state = IO_CLOSED;
@@ -631,6 +660,8 @@ char *iohandler_iostatus_name(enum IOStatus status) {
             return "IO_CONNECTING";
         case IO_CONNECTED:
             return "IO_CONNECTED";
+        case IO_SSLWAIT:
+            return "IO_SSLWAIT";
         default:
             return "(UNDEFINED)";
     }
index 76fb2cf82c89dd492352ea622a12f416e8065785..6d57ca541901329f2d485523279db163cc3f1078 100644 (file)
@@ -20,6 +20,7 @@
 
 struct IODescriptor;
 struct IOEvent;
+struct IOSSLNode;
 
 #define IOHANDLER_CALLBACK(NAME) void NAME(UNUSED_ARG(struct IOEvent *io_event))
 typedef IOHANDLER_CALLBACK(iohandler_callback);
@@ -36,7 +37,8 @@ enum IOStatus {
     IO_CLOSED, /* descriptor is dead (socket waiting for removal or timer) */
     IO_LISTENING, /* descriptor is waiting for connections (server socket) */
     IO_CONNECTING, /* descriptor is waiting for connection approval (connecting client socket) */
-    IO_CONNECTED /* descriptor is connected (connected client socket) */
+    IO_CONNECTED, /* descriptor is connected (connected client socket) */
+    IO_SSLWAIT /* waiting for SSL backend (e.g. handshake) */
 };
 
 enum IOEventType {
@@ -47,7 +49,8 @@ enum IOEventType {
     IOEVENT_NOTCONNECTED, /* client socket could not connect (errid valid) */
     IOEVENT_CLOSED, /* client socket lost connection (errid valid) */
     IOEVENT_ACCEPT, /* server socket accepted new connection (accept_fd valid) */
-    IOEVENT_TIMEOUT /* timer timed out */
+    IOEVENT_TIMEOUT, /* timer timed out */
+    IOEVENT_SSLFAILED /* failed to initialize SSL session */
 };
 
 struct IOBuffer {
@@ -66,6 +69,10 @@ struct IODescriptor {
     void *data;
     int read_lines : 1;
     int ssl : 1;
+    int ssl_active : 1;
+    int ssl_hs_read : 1;
+    int ssl_hs_write : 1;
+    struct IOSSLNode *sslnode;
     
     struct IODescriptor *next, *prev;
 };
@@ -83,7 +90,7 @@ struct IOEvent {
 struct IODescriptor *iohandler_add(int sockfd, enum IOType type, struct timeval *timeout, iohandler_callback *callback);
 struct IODescriptor *iohandler_timer(struct timeval timeout, iohandler_callback *callback);
 struct IODescriptor *iohandler_connect(const char *hostname, unsigned int port, int ssl, const char *bind, iohandler_callback *callback);
-struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, int ssl, iohandler_callback *callback);
+struct IODescriptor *iohandler_listen(const char *hostname, unsigned int port, iohandler_callback *callback);
 void iohandler_write(struct IODescriptor *iofd, const char *line);
 void iohandler_send(struct IODescriptor *iofd, const char *data, size_t datalen);
 void iohandler_printf(struct IODescriptor *iofd, const char *text, ...);
diff --git a/src/IOHandler_SSL.c b/src/IOHandler_SSL.c
new file mode 100644 (file)
index 0000000..64e2241
--- /dev/null
@@ -0,0 +1,161 @@
+/* IOHandler_SSL.c - TransparentIRC 0.1
+ * Copyright (C) 2011-2012  Philipp Kreil (pk910)
+ * 
+ * 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 3 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, see <http://www.gnu.org/licenses/>. 
+ */
+#include "IOHandler.h"
+#include "IOEngine.h"
+#include "IOHandler_SSL.h"
+
+void iohandler_ssl_init() {
+#ifdef HAVE_SSL
+    SSL_library_init();
+    SSL_load_error_strings();
+#endif
+}
+
+void iohandler_ssl_connect(struct IODescriptor *iofd) {
+#ifdef HAVE_SSL
+    iofd->state = IO_SSLWAIT;
+    struct IOSSLNode *sslnode = malloc(sizeof(*sslnode));
+    sslnode->sslContext = SSL_CTX_new(SSLv23_client_method());
+    if(!sslnode->sslContext)
+        goto ssl_connect_err;
+    sslnode->sslHandle = SSL_new(sslnode->sslContext);
+    if(!sslnode->sslHandle) 
+        goto ssl_connect_err;
+    if(!SSL_set_fd(sslnode->sslHandle, iofd->fd))
+        goto ssl_connect_err;
+    SSL_set_connect_state(sslnode->sslHandle);
+    iofd->sslnode = sslnode;
+    iohandler_ssl_client_handshake(iofd);
+    return;
+ssl_connect_err:
+    free(sslnode);
+    iohandler_events(iofd, 0, 0);
+#endif    
+}
+
+void iohandler_ssl_client_handshake(struct IODescriptor *iofd) {
+    // Perform an SSL handshake.
+    int ret = SSL_do_handshake(iofd->sslnode->sslHandle);
+    iofd->ssl_hs_read = 0;
+    iofd->ssl_hs_write = 0;
+    switch(SSL_get_error(iofd->sslnode->sslHandle, ret)) {
+        case SSL_ERROR_NONE:
+            iofd->state = IO_CONNECTING;
+            iofd->ssl_active = 1;
+            iohandler_log(IOLOG_DEBUG, "SSL handshake for %s (fd: %d) successful", iohandler_iotype_name(iofd->type), iofd->fd);
+            iohandler_events(iofd, 0, 1); //perform IOEVENT_CONNECTED event
+            break;
+        case SSL_ERROR_WANT_READ:
+            iofd->ssl_hs_read = 1;
+            iohandler_log(IOLOG_DEBUG, "SSL_do_handshake for %s (fd: %d) returned SSL_ERROR_WANT_READ", iohandler_iotype_name(iofd->type), iofd->fd);
+            break;
+        case SSL_ERROR_WANT_WRITE:
+            iofd->ssl_hs_write = 1;
+            iohandler_log(IOLOG_DEBUG, "SSL_do_handshake for %s (fd: %d) returned SSL_ERROR_WANT_WRITE", iohandler_iotype_name(iofd->type), iofd->fd);
+            break;
+        default:
+            iohandler_log(IOLOG_ERROR, "SSL_do_handshake for %s (fd: %d) failed with ", iohandler_iotype_name(iofd->type), iofd->fd);
+            iohandler_events(iofd, 0, 0);
+            break;
+    }
+}
+
+void iohandler_ssl_disconnect(struct IODescriptor *iofd) {
+#ifdef HAVE_SSL
+    if(!iofd->sslnode) return;
+    SSL_shutdown(iofd->sslnode->sslHandle);
+    SSL_free(iofd->sslnode->sslHandle);
+    SSL_CTX_free(iofd->sslnode->sslContext);
+    free(iofd->sslnode);
+    iofd->sslnode = NULL;
+    iofd->ssl_active = 0;
+#endif
+}
+
+int iohandler_ssl_read(struct IODescriptor *iofd, char *buffer, int len) {
+#ifdef HAVE_SSL
+    if(!iofd->sslnode) return 0;
+    int ret = SSL_read(iofd->sslnode->sslHandle, buffer, len);
+    int update = (iofd->ssl_hs_read || iofd->ssl_hs_write);
+    iofd->ssl_hs_read = 0;
+    iofd->ssl_hs_write = 0;
+    switch(SSL_get_error(iofd->sslnode->sslHandle, ret)) {
+        case SSL_ERROR_NONE:
+        case SSL_ERROR_ZERO_RETURN:
+            if(update)
+                iohandler_update(iofd);
+            return ret;
+            break;
+        case SSL_ERROR_WANT_READ:
+            iofd->ssl_hs_read = 1;
+            iohandler_update(iofd);
+            iohandler_log(IOLOG_DEBUG, "SSL_read for %s (fd: %d) returned SSL_ERROR_WANT_READ", iohandler_iotype_name(iofd->type), iofd->fd);
+            errno = EAGAIN;
+            return -1;
+            break;
+        case SSL_ERROR_WANT_WRITE:
+            iofd->ssl_hs_write = 1;
+            iohandler_update(iofd);
+            iohandler_log(IOLOG_DEBUG, "SSL_read for %s (fd: %d) returned SSL_ERROR_WANT_WRITE", iohandler_iotype_name(iofd->type), iofd->fd);
+            errno = EAGAIN;
+            return -1;
+            break;
+        default:
+            iohandler_log(IOLOG_ERROR, "SSL_read for %s (fd: %d) failed with ", iohandler_iotype_name(iofd->type), iofd->fd);
+            return -1;
+            break;
+    }
+#endif
+    return 0;
+}
+
+int iohandler_ssl_write(struct IODescriptor *iofd, char *buffer, int len) {
+#ifdef HAVE_SSL
+    if(!iofd->sslnode) return 0;
+    int ret = SSL_write(iofd->sslnode->sslHandle, buffer, len);
+    int update = (iofd->ssl_hs_read || iofd->ssl_hs_write);
+    iofd->ssl_hs_read = 0;
+    iofd->ssl_hs_write = 0;
+    switch(SSL_get_error(iofd->sslnode->sslHandle, ret)) {
+        case SSL_ERROR_NONE:
+        case SSL_ERROR_ZERO_RETURN:
+            if(update)
+                iohandler_update(iofd);
+            return ret;
+            break;
+        case SSL_ERROR_WANT_READ:
+            iofd->ssl_hs_read = 1;
+            iohandler_update(iofd);
+            iohandler_log(IOLOG_DEBUG, "SSL_write for %s (fd: %d) returned SSL_ERROR_WANT_READ", iohandler_iotype_name(iofd->type), iofd->fd);
+            errno = EAGAIN;
+            return -1;
+            break;
+        case SSL_ERROR_WANT_WRITE:
+            iofd->ssl_hs_write = 1;
+            iohandler_update(iofd);
+            iohandler_log(IOLOG_DEBUG, "SSL_write for %s (fd: %d) returned SSL_ERROR_WANT_WRITE", iohandler_iotype_name(iofd->type), iofd->fd);
+            errno = EAGAIN;
+            return -1;
+            break;
+        default:
+            iohandler_log(IOLOG_ERROR, "SSL_write for %s (fd: %d) failed with ", iohandler_iotype_name(iofd->type), iofd->fd);
+            return -1;
+            break;
+    }
+#endif
+    return 0;
+}
diff --git a/src/IOHandler_SSL.h b/src/IOHandler_SSL.h
new file mode 100644 (file)
index 0000000..8affca2
--- /dev/null
@@ -0,0 +1,45 @@
+/* IOHandler_SSL.h - TransparentIRC 0.1
+ * Copyright (C) 2011-2012  Philipp Kreil (pk910)
+ * 
+ * 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 3 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, see <http://www.gnu.org/licenses/>. 
+ */
+#ifndef _IOHandler_SSL_h
+#define _IOHandler_SSL_h
+#include "overall.h"
+
+struct IODescriptor;
+
+#ifdef HAVE_SSL
+#include <openssl/rand.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+struct IOSSLNode {
+    SSL *sslHandle;
+    SSL_CTX *sslContext;
+};
+#else
+struct IOSSLNode {
+    //just unused
+};
+#endif
+
+void iohandler_ssl_init();
+void iohandler_ssl_connect(struct IODescriptor *iofd);
+void iohandler_ssl_client_handshake(struct IODescriptor *iofd);
+void iohandler_ssl_disconnect(struct IODescriptor *iofd);
+int iohandler_ssl_read(struct IODescriptor *iofd, char *buffer, int len);
+int iohandler_ssl_write(struct IODescriptor *iofd, char *buffer, int len);
+
+#endif
index 943228b9d5efbe637a20ba41b978ed99279848f3..e2af54e8d0ba10f97c46c29f4bbcf4a8d066c7fe 100644 (file)
@@ -48,7 +48,7 @@ static void serversocket_delete(struct ServerSocket *server) {
 }
 
 struct ServerSocket *serversocket_listen(char *hostname, int port, int ssl) {
-    struct IODescriptor *iofd = iohandler_listen(hostname, port, ssl, serversocket_callback);
+    struct IODescriptor *iofd = iohandler_listen(hostname, port, serversocket_callback);
     if(!iofd) return NULL;
     struct ServerSocket *server = serversocket_create(iofd);
     return server;
index 1b075069ccc2fd7f8aa2b2c7c2f98109594e6c22..762b0d600f7a516747524f934beeef7c1a2a7cb8 100644 (file)
@@ -56,7 +56,8 @@ void userclient_accepted(struct ServerSocket *server, int sockfd) {
 
 void userclient_close(struct UserClient *client) {
     if(client->flags & USERCLIENT_LOGGED_IN) {
-        usersession_client_close(client->user);
+        if(client->user)
+            usersession_client_close(client->user);
     } else {
         struct UserLogin *login = client->user;
         if(client->flags & USERCLIENT_LOGIN_PROCESSING) {
index 66dd2cf415b28a2bef294d8fea757add120460dc..e66d6e6ae93864c168bddb67f7b4bd440a2e01e1 100644 (file)
@@ -41,8 +41,10 @@ static struct UserSession *usersession_add(char *username, char *password, char
 }
 
 static void usersession_close(struct UserSession *session) {
-    if(session->client)
+    if(session->client) {
+        session->client->user = NULL;
         userclient_close(session->client);
+    }
     if(session->irc)
         ircclient_close(session->irc);
     if(session->timer)