diff --git a/Makefile b/Makefile index 533c33d..52811f2 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,11 @@ DEPS = $(OBJS:%.o=%.d) all: $(BIN_NAME) +ssl: CFLAGS += -DSSL_SUPPORT -Icrypto +ssl: LDFLAGS += -lcrypto +ssl: OBJS += obj/ssl.o +ssl: $(BIN_NAME) + $(BIN_NAME): obj/main.o $(OBJS) $(LD) $(LDFLAGS) -o $@ $^ diff --git a/src/main.c b/src/main.c index 62eab89..635ec29 100644 --- a/src/main.c +++ b/src/main.c @@ -10,6 +10,10 @@ #include "cgi.h" #include "util.h" +#ifdef SSL_SUPPORT +#include "ssl.h" +#endif + struct handlerSettings { struct fileSettings fileSettings; struct cgiSettings cgiSettings; @@ -78,7 +82,11 @@ int main(int argc, char** argv) { { .address = "0.0.0.0", .port = "1337", - .settings = settingsData + .settings = settingsData, + + #ifdef SSL_SUPPORT + .ssl_settings = NULL + #endif } } }, diff --git a/src/misc.h b/src/misc.h index e6068ad..a97f6b5 100644 --- a/src/misc.h +++ b/src/misc.h @@ -7,6 +7,10 @@ #include +#ifdef SSL_SUPPORT +#include "ssl.h" +#endif + #define PTHREAD_NULL ((pthread_t) 0) enum method { @@ -46,6 +50,11 @@ struct bind { const char* port; bool tls; union userData settings; + + #ifdef SSL_SUPPORT + struct ssl_settings* ssl_settings; + #endif + struct bind_private _private; }; diff --git a/src/networking.c b/src/networking.c index 21686ec..99a90a7 100644 --- a/src/networking.c +++ b/src/networking.c @@ -19,6 +19,10 @@ #include "status.h" #include "util.h" +#ifdef SSL_SUPPORT +#include "ssl.h" +#endif + struct networkingConfig networkingConfig; static inline long timespecDiffMs(struct timespec start, struct timespec end) { @@ -92,6 +96,12 @@ void cleanup() { struct connection* connection = link->data; if (connection->inUse == 0) { freed++; + + #ifdef SSL_SUPPORT + if (connection->sslConnection != NULL) + ssl_closeConnection(connection->sslConnection); + #endif + if (connection->metaData.path != NULL) free(connection->metaData.path); if (connection->metaData.queryString != NULL) @@ -647,6 +657,7 @@ void* listenThread(void* _bind) { } if (inet_ntop(family, addrPtr, &(peer.addr[0]), INET6_ADDRSTRLEN + 1) == NULL) { + free(connection); error("networking: Couldn't set peer addr string: %s", strerror(errno)); return NULL; } @@ -671,11 +682,28 @@ void* listenThread(void* _bind) { snprintf(&(peer.portStr[0]), 5 + 1, "%d", peer.port); + #ifdef SSL_SUPPORT + if (bindObj->ssl_settings != NULL) { + struct ssl_connection* sslConnection = ssl_initConnection(bindObj->ssl_settings, tmp); + if (sslConnection == NULL) { + free(connection); + error("networking: failed to open ssl connection"); + continue; + } + + connection->sslConnection = sslConnection; + connection->readfd = sslConnection->readfd; + connection->writefd = sslConnection->writefd; + } else { + connection->sslConnection = NULL; + connection->readfd = tmp; + connection->writefd = tmp; + } + #endif + connection->state = OPENED; connection->peer = peer; connection->bind = bindObj; - connection->readfd = tmp; - connection->writefd = tmp; connection->metaData = (struct metaData) { .path = NULL, .queryString = NULL diff --git a/src/networking.h b/src/networking.h index 26ac4e3..4515ef7 100644 --- a/src/networking.h +++ b/src/networking.h @@ -12,6 +12,10 @@ #include "headers.h" #include "misc.h" +#ifdef SSL_SUPPORT +#include "ssl.h" +#endif + #define NR_CONNECTION_STATE (5) enum connectionState { OPENED = 0, @@ -52,6 +56,9 @@ struct connection { char* currentHeader; struct timing timing; struct threads threads; + #ifdef SSL_SUPPORT + struct ssl_connection* sslConnection; + #endif }; struct binds { diff --git a/src/ssl.c b/src/ssl.c new file mode 100644 index 0000000..6f362dd --- /dev/null +++ b/src/ssl.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ssl.h" +#include "logging.h" + +void ssl_init() { + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); +} +void ssl_destroy() { + ERR_free_strings(); + EVP_cleanup(); +} + +int ssl_initSettings(struct ssl_settings* settings) { + SSL_CTX* ctx = SSL_CTX_new( SSLv23_server_method()); + + SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE); + if (!SSL_CTX_use_certificate_file(sslctx, settings->certificate, SSL_FILETYPE_PEM)) { + error("ssl: failed to set cert file for ctx: %s", ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + if (!SSL_CTX_use_PrivateKey_file(ctx, settings->privateKey, SSL_FILETYPE_PEM)) { + error("ssl: failed to set key file for ctx: %s", ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + _private.ctx = ctx; + + return 0; +} + +void* copyFromSslToFd(void* data) { + struct ssl_connection* connection = (struct ssl_connection*) data; + + char b; + while(SSL_read(connection->instance, &b, 1) == 1) { + write(connection->_readfd, &b, 1); + } + + close(connection->_writefd); +} + +void* copyFromFdToSsl(void* data) { + struct ssl_connection* connection = (struct ssl_connection*) data; + + char b; + while(read(connection->_writefd, &b, 1) == 1) { + SSL_write(connection->instance, &b, 1); + } +} + +struct ssl_connection* ssl_initConnection(struct ssl_settings* settings, int socket) { + struct ssl_connection* connection = malloc(sizeof(struct ssl_connection)); + if (connection == NULL) { + error("ssl: couldn't allocate for ssl connection: %s", strerror(errno)); + return NULL; + } + + connection->writefd = -1; + connection->readfd = -1; + connection->_writefd = -1; + connection->_readfd = -1; + connection->_threads[0] = PTHREAD_NULL; + connection->_threads[1] = PTHREAD_NULL; + + connection->instance = SSL_new(settings->_private.ctx); + + if (connection->instance == NULL) { + free(connection); + error("ssl: failed to create new connection: %s", ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + + SSL_set_fd(connection->instance, socket); + + if (SSL_accept(connection->instance) < 0) { + ssl_closeConnection(connection); + error("ssl: couldn't accept ssl connection: %s", ERR_error_string(ERR_get_error(), NULL)); + return NULL; + } + + int pipefd[2]; + if (pipe(&(pipefd[0])) < 0) { + ssl_closeConnection(connection); + error("ssl: couldn't create pipe: %s", strerror(errno)); + return NULL; + } + + connection->writefd = pipefd[1]; + connection->_writefd = pipefd[0]; + + if (pipe(&(pipefd[0])) < 0) { + ssl_closeConnection(connection); + error("ssl: couldn't create pipe: %s", strerror(errno)); + return NULL; + } + + connection->_readfd = pipefd[1]; + connection->readfd = pipefd[0]; + + if (pthread_create(&(connection->_threads[0]), NULL, ©FromSslToFd, connection) < 0) { + ssl_closeConnection(connection); + error("ssl: couldn't create copyToFd-thread"); + return NULL; + } + if (pthread_create(&(connection->_threads[1]), NULL, ©FromFdToSsl, connection) < 0) { + ssl_closeConnection(connection); + error("ssl: couldn't create copyToSsl-thread"); + return NULL; + } + + return connection; +} + + +int ssl_closeConnection(struct ssl_connection* connection) { + close(connection->writefd); + close(connection->readfd); + close(connection->_writefd); + close(connection->_readfd); + + if (connection->_threads[0] != PTHREAD_NULL) { + pthread_cancel(connection->_threads[0]); + pthread_join(connection->_threads[0]); + } + + if (connection->_threads[1] != PTHREAD_NULL) { + pthread_cancel(connection->_threads[1]); + pthread_join(connection->_threads[1]); + } + + SSL_shutdown(connection->instance); + SSL_free(connection->instance); + free(connection); +} diff --git a/src/ssl.h b/src/ssl.h new file mode 100644 index 0000000..5932698 --- /dev/null +++ b/src/ssl.h @@ -0,0 +1,32 @@ +#ifndef SSL_H +#define SSL_H + +#include + +#include + +struct ssl_connection { + SSL* instance; + int readfd; + int writefd; + int _readfd; + int _writefd; + pthread_t _threads[2]; +}; + +struct ssl_settings { + const char* privateKey; + const char* certificate; + struct { + SSL_CTX* ctx; + } _private; +}; + +void ssl_init(); +void ssl_destroy(); + +int ssl_initSettings(struct ssl_settings* settings); +struct ssl_connection* ssl_initConnection(struct ssl_settings* settings, int socket); +int ssl_closeConnection(struct ssl_connection* connection); + +#endif