From 526bf10a06332efc4505bee2d2cff7b959a169b8 Mon Sep 17 00:00:00 2001 From: pk910 Date: Sun, 27 May 2012 02:48:45 +0200 Subject: [PATCH] added SSL backend for IOMultiplexer --- Makefile.am | 1 + src/IOEngine.h | 2 +- src/IOHandler.c | 37 +++++++++- src/IOHandler.h | 13 +++- src/IOHandler_SSL.c | 161 ++++++++++++++++++++++++++++++++++++++++++++ src/IOHandler_SSL.h | 45 +++++++++++++ src/ServerSocket.c | 2 +- src/UserClient.c | 3 +- src/UserSession.c | 4 +- 9 files changed, 258 insertions(+), 10 deletions(-) create mode 100644 src/IOHandler_SSL.c create mode 100644 src/IOHandler_SSL.h diff --git a/Makefile.am b/Makefile.am index 1abc4d4..f72746c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/src/IOEngine.h b/src/IOEngine.h index 0f33e81..8f4053b 100644 --- a/src/IOEngine.h +++ b/src/IOEngine.h @@ -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); diff --git a/src/IOHandler.c b/src/IOHandler.c index 7a9f3b3..953340e 100644 --- a/src/IOHandler.c +++ b/src/IOHandler.c @@ -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)"; } diff --git a/src/IOHandler.h b/src/IOHandler.h index 76fb2cf..6d57ca5 100644 --- a/src/IOHandler.h +++ b/src/IOHandler.h @@ -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 index 0000000..64e2241 --- /dev/null +++ b/src/IOHandler_SSL.c @@ -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 . + */ +#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 index 0000000..8affca2 --- /dev/null +++ b/src/IOHandler_SSL.h @@ -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 . + */ +#ifndef _IOHandler_SSL_h +#define _IOHandler_SSL_h +#include "overall.h" + +struct IODescriptor; + +#ifdef HAVE_SSL +#include +#include +#include + +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 diff --git a/src/ServerSocket.c b/src/ServerSocket.c index 943228b..e2af54e 100644 --- a/src/ServerSocket.c +++ b/src/ServerSocket.c @@ -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; diff --git a/src/UserClient.c b/src/UserClient.c index 1b07506..762b0d6 100644 --- a/src/UserClient.c +++ b/src/UserClient.c @@ -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) { diff --git a/src/UserSession.c b/src/UserSession.c index 66dd2cf..e66d6e6 100644 --- a/src/UserSession.c +++ b/src/UserSession.c @@ -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) -- 2.20.1