From 216f6a152333b38a8563c570eb237c27585deedb Mon Sep 17 00:00:00 2001 From: Luke Bratch Date: Mon, 13 May 2019 00:07:15 +0100 Subject: Implement optional TLS for the server side. --- blabouncer.c | 72 +++++++++++++++++++++++++++++++++++++++------------------ blabouncer.conf | 5 +++- sockets.c | 21 +++++++++++++---- sockets.h | 8 +++++-- 4 files changed, 76 insertions(+), 30 deletions(-) diff --git a/blabouncer.c b/blabouncer.c index c883ed6..0f70deb 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -9,7 +9,6 @@ // - "01:53:47 -!- ServerMode/#test [b] by irc.tghost.co.uk" on existing clients when new client connects // - Keep track of changing user nicks/modes // - Should replay log do more than PRIVMSGs? -// - Implement TLS on real IRCd server side // - Consider moving the three fd-related arrays into one struct // - Check authentication before even getting to the send functions to save unnecessary processing // - Configurable auto channels @@ -17,6 +16,7 @@ // - Only send some things to the requesting client (e.g. LIST replies) // - Normal (non-replay) log // - Alert when clients connect/authenticate/disconnect +// - Perhaps rename arr_ssl and server_ssl since they may not even be OpenSSL sockets // // Example WHOIS reply: // BOUNCER-SERVER RECEIVED: :irc.tghost.co.uk 307 blabounce l_bratch :is identified for this nick @@ -102,6 +102,7 @@ struct settings { char certfile[PATH_MAX]; char keyfile[PATH_MAX]; int clienttls; + int servertls; }; // Return index of requested client FD within arr_clients @@ -229,7 +230,7 @@ int sendtoallclients(int *clientsockfd, int fdmax, int arr_clients[], char *str, // Client FD and arrays needed to make sure anything relayed from a client is from an authenticated client. // clientfd of "0" means trusted, used when we are sending things ourselves that weren't relayed // from a real client. -int sendtoserver(int *serversockfd, char *str, int str_len, int clientfd, int arr_clients[], int arr_authed[]) { +int sendtoserver(SSL *server_ssl, char *str, int str_len, int clientfd, int arr_clients[], int arr_authed[], struct settings *settings) { appendcrlf(str); // Do this just before sending so callers don't need to worry about it str_len = strlen(str); // Recalculate str_len in case it changed (TODO: so do we even need to pass it to this function?) @@ -250,7 +251,7 @@ int sendtoserver(int *serversockfd, char *str, int str_len, int clientfd, int ar } printf("sendtoserver(): sending %s to IRC server (length %d).\n", str, str_len); - if (send(*serversockfd, str, str_len, 0) == -1) { // 0 is bitwise OR for no send() flags + if (socksend(server_ssl, str, str_len, settings->servertls) == -1) { printf("error: sendtoserver() send()\n"); return 0; } @@ -444,7 +445,7 @@ int removechannel(struct channel *channels, char *name) { // Return 1 if we processed something and expect the caller to not need to do anything more // Return 0 if we didn't process it and the caller might want to do something //int processircmessage(int *serversockfd, int *clientsockfd, char *str, int source) { -int processircmessage(int *serversockfd, int *clientsockfd, char *str, int source, int fdmax, int arr_clients[], int sourcefd, struct ircdstrings *ircdstrings, struct channel *channels, int arr_authed[], SSL **arr_ssl, struct settings *settings) { +int processircmessage(SSL *server_ssl, int *clientsockfd, char *str, int source, int fdmax, int arr_clients[], int sourcefd, struct ircdstrings *ircdstrings, struct channel *channels, int arr_authed[], SSL **arr_ssl, struct settings *settings) { // Track which space-separated token within this response we're on int counter = 0; @@ -481,7 +482,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc exit(1); } // sourcefd = 0 as this is a trusted response - sendtoserver(serversockfd, outgoingmsg, strlen(outgoingmsg), 0, arr_clients, arr_authed); + sendtoserver(server_ssl, outgoingmsg, strlen(outgoingmsg), 0, arr_clients, arr_authed, settings); // We processed something so return true return 1; @@ -821,7 +822,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc // Just send NICK to server and let it change all clients' nicks if needed if (strncmp(tokens[0], "NICK", strlen(tokens[0])) == 0) { printf("Client NICK found and it is: %s with length %zd! Sending to server...\n", tokens[0], strlen(tokens[0])); - sendtoserver(serversockfd, str, strlen(str), sourcefd, arr_clients, arr_authed); + sendtoserver(server_ssl, str, strlen(str), sourcefd, arr_clients, arr_authed, settings); return 1; } @@ -829,7 +830,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc if (strncmp(tokens[0], "PRIVMSG", strlen(tokens[0])) == 0) { printf("Client PRIVMSG found and it is: %s with length %zd! Sending to server then back to other clients...\n", tokens[0], strlen(tokens[0])); // Send original request straight to server - sendtoserver(serversockfd, str, strlen(str), sourcefd, arr_clients, arr_authed); + sendtoserver(server_ssl, str, strlen(str), sourcefd, arr_clients, arr_authed, settings); // Rebuild to full PRIVMSG string and relay to all other clients char outgoingmsg[MAXDATASIZE]; // String to send to client @@ -850,7 +851,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc // Just send JOIN to server and let it talk back to clients as required if (strncmp(tokens[0], "JOIN", strlen(tokens[0])) == 0) { printf("Client JOIN found and it is: %s with length %zd! Sending to server...\n", tokens[0], strlen(tokens[0])); - sendtoserver(serversockfd, str, strlen(str), sourcefd, arr_clients, arr_authed); + sendtoserver(server_ssl, str, strlen(str), sourcefd, arr_clients, arr_authed, settings); return 1; } @@ -866,14 +867,14 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc // Just send PART to server and let it talk back to clients as required if (strncmp(tokens[0], "PART", strlen(tokens[0])) == 0) { printf("Client PART found and it is: %s with length %zd! Sending to server...\n", tokens[0], strlen(tokens[0])); - sendtoserver(serversockfd, str, strlen(str), sourcefd, arr_clients, arr_authed); + sendtoserver(server_ssl, str, strlen(str), sourcefd, arr_clients, arr_authed, settings); return 1; } // Just send TOPIC to server and let it talk back to clients as required if (strncmp(tokens[0], "TOPIC", strlen(tokens[0])) == 0) { printf("Client TOPIC found and it is: %s with length %zd! Sending to server...\n", tokens[0], strlen(tokens[0])); - sendtoserver(serversockfd, str, strlen(str), sourcefd, arr_clients, arr_authed); + sendtoserver(server_ssl, str, strlen(str), sourcefd, arr_clients, arr_authed, settings); return 1; } @@ -906,7 +907,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc // // Return 0 if something went wrong // Return 1 if everything OK -int processrawstring(int *serversockfd, int *clientsockfd, char *str, int source, int fdmax, int arr_clients[], int sourcefd, struct ircdstrings *ircdstrings, struct channel *channels, int arr_authed[], SSL **arr_ssl, struct settings *settings) { +int processrawstring(SSL *server_ssl, int *clientsockfd, char *str, int source, int fdmax, int arr_clients[], int sourcefd, struct ircdstrings *ircdstrings, struct channel *channels, int arr_authed[], SSL **arr_ssl, struct settings *settings) { // Copy to a temporary string so we still have the original in case it's not processed char *strcopy = strdup(str); @@ -961,7 +962,7 @@ int processrawstring(int *serversockfd, int *clientsockfd, char *str, int source for (int i = 0; i < messagecount; i++) { // Copy to a temporary string so we still have the original in case it's not processed char *messagecopy = strdup(messages[i]); - if (processircmessage(serversockfd, clientsockfd, messagecopy, source, fdmax, arr_clients, sourcefd, ircdstrings, channels, arr_authed, arr_ssl, settings)) { + if (processircmessage(server_ssl, clientsockfd, messagecopy, source, fdmax, arr_clients, sourcefd, ircdstrings, channels, arr_authed, arr_ssl, settings)) { printf("Message processed: \"%s\", NULLing...\n", messages[i]); messages[i][0] = '\0'; } @@ -982,7 +983,7 @@ int processrawstring(int *serversockfd, int *clientsockfd, char *str, int source case SOURCE_CLIENT: // If message(s) were from a real IRC client // Send to server printf("bouncer-client: sending unprocessed client message \"%s\" to the server, length %zd.\n", messages[i], strlen(messages[i])); - sendtoserver(serversockfd, messages[i], strlen(messages[i]), sourcefd, arr_clients, arr_authed); + sendtoserver(server_ssl, messages[i], strlen(messages[i]), sourcefd, arr_clients, arr_authed, settings); printf("bouncer-client: sending unprocessed client message \"%s\" to all other clients, length %zd.\n", messages[i], strlen(messages[i])); // send the same thing to all *other* clients (all except for source fd) @@ -1030,6 +1031,29 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { arr_authed[i] = 0; } + // Initialise OpenSSL (used for both client and server) + init_openssl(); + + // OpenSSL for server side if configured + SSL *server_ssl; // Need to create this either way as referenced later + if (settings->servertls) { + printf("server openssl start.\n"); + SSL_CTX *serverctx; + serverctx = create_openssl_context(SOURCE_SERVER); + configure_openssl_context(serverctx, NULL, NULL); + server_ssl = SSL_new(serverctx); + SSL_set_fd(server_ssl, *serversockfd); + if (SSL_connect(server_ssl) == -1) { + ERR_print_errors_fp(stderr); + } else { + printf("SSL_connect() success.\n"); + } + printf("server openssl complete.\n"); + } else { + // If not using TLS then just slap the serversockfd into server_ssl by casting it + server_ssl = (SSL*)(long int)*serversockfd; + } + // <============================================= // Initialise IRC connecting/registration state @@ -1057,13 +1081,13 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { // Send our NICK snprintf(outgoingmsg, MAXDATASIZE, "NICK %s", ircdstrings.ircnick); // TODO - Check for success (with return code) // sourcefd = 0 as this is a trusted message - sendtoserver(serversockfd, outgoingmsg, strlen(outgoingmsg), 0, arr_clients, arr_authed); + sendtoserver(server_ssl, outgoingmsg, strlen(outgoingmsg), 0, arr_clients, arr_authed, settings); // Send our USER snprintf(outgoingmsg, MAXDATASIZE, "USER %s 8 * : %s", ircdstrings.ircusername, settings->ircrealname); // TODO - Check for success (with return code) // TODO - Send a more intelligent/correct USER string // sourcefd = 0 as this is a trusted message - sendtoserver(serversockfd, outgoingmsg, strlen(outgoingmsg), 0, arr_clients, arr_authed); + sendtoserver(server_ssl, outgoingmsg, strlen(outgoingmsg), 0, arr_clients, arr_authed, settings); // Struct of channels we're in struct channel *channels; @@ -1075,10 +1099,9 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { // If using client TLS if (settings->clienttls) { - // Initialise OpenSSL - init_openssl(); - ctx = create_context(); - configure_context(ctx, settings->certfile, settings->keyfile); + // Set up and configure client OpenSSL context + ctx = create_openssl_context(SOURCE_CLIENT); + configure_openssl_context(ctx, settings->certfile, settings->keyfile); } while (1) { @@ -1113,7 +1136,7 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { if (FD_ISSET(*serversockfd, &rfds)) { printf("reading server socket!\n"); - if ((servernumbytes = recv(*serversockfd, serverbuf, MAXRCVSIZE - 1, 0)) == -1) { + if ((servernumbytes = sockread(server_ssl, serverbuf, MAXRCVSIZE - 1, settings->servertls)) == -1) { printf("receive error (-1), exiting...\n"); perror("recv"); exit(1); @@ -1128,7 +1151,7 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { // Try to process received string (which should contain one or more server responses/commands) // TODO - What if there were two server respones/commands and only one didn't need relaying? - if (!processrawstring(serversockfd, clientsockfd, serverbuf, SOURCE_SERVER, fdmax, arr_clients, EXCEPT_NONE, &ircdstrings, channels, arr_authed, arr_ssl, settings)) { + if (!processrawstring(server_ssl, clientsockfd, serverbuf, SOURCE_SERVER, fdmax, arr_clients, EXCEPT_NONE, &ircdstrings, channels, arr_authed, arr_ssl, settings)) { fprintf(stderr, "Error: bouncer-server failed to process raw string.\n"); exit(1); } @@ -1169,7 +1192,7 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { } // sourcefd = 0 as this is a trusted message - sendtoserver(serversockfd, outgoingmsg, strlen(outgoingmsg), 0, arr_clients, arr_authed); + sendtoserver(server_ssl, outgoingmsg, strlen(outgoingmsg), 0, arr_clients, arr_authed, settings); } // go through all the remaining sockets to see if there's anything from the client sockets (either new connections or existing clients sending messages) @@ -1259,7 +1282,7 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) { // Try to process received string (which should contain one or more client responses/commands) // TODO - What if there were two server respones/commands and only one didn't need relaying? - if (!processrawstring(serversockfd, clientsockfd, clientbuf, SOURCE_CLIENT, fdmax, arr_clients, i, &ircdstrings, channels, arr_authed, arr_ssl, settings)) { + if (!processrawstring(server_ssl, clientsockfd, clientbuf, SOURCE_CLIENT, fdmax, arr_clients, i, &ircdstrings, channels, arr_authed, arr_ssl, settings)) { fprintf(stderr, "Error: bouncer-client failed to process raw string.\n"); exit(1); } @@ -1332,6 +1355,9 @@ int main(int argc, char *argv[]) { exit(1); } + // Should the bouncer use TLS for the IRC server? + settings.servertls = getconfint("servertls", settings.conffile); + // Should the bouncer use TLS for clients? settings.clienttls = getconfint("clienttls", settings.conffile); diff --git a/blabouncer.conf b/blabouncer.conf index 72646de..d90d35b 100644 --- a/blabouncer.conf +++ b/blabouncer.conf @@ -21,11 +21,14 @@ clientport = "1234" # If "0" then certfile and keyfile need not be set clienttls = "1" +# Enable TLS for the bouncer connecting to the IRC server ("1" for yes or "0" for no) +servertls = "1" + # Real IRC server the bouncer connects to ircserver = "irc.blatech.net" # Real IRC server port -ircserverport = "6667" +ircserverport = "6697" # Certificate file # If clienttls = "0" then this need not be set diff --git a/sockets.c b/sockets.c index c5ea41f..943b15c 100644 --- a/sockets.c +++ b/sockets.c @@ -135,11 +135,17 @@ void cleanup_openssl() { EVP_cleanup(); } -SSL_CTX *create_context() { +// Create OpenSSL context, type = 0 for IRC server-side (OpenSSL client) +// or type = 1 for bouncer client-side (OpenSSL server) +SSL_CTX *create_openssl_context(int type) { const SSL_METHOD *method; SSL_CTX *ctx; - method = SSLv23_server_method(); + if (type == 0) { + method = SSLv23_client_method(); + } else { + method = SSLv23_server_method(); + } ctx = SSL_CTX_new(method); if (!ctx) { @@ -151,10 +157,17 @@ SSL_CTX *create_context() { return ctx; } -void configure_context(SSL_CTX *ctx, char *certfile, char *keyfile) { +// Configure OpenSSL context, with certfile and keyfile provided if +// IRC server-side or set to NULL if bouncer client-side +void configure_openssl_context(SSL_CTX *ctx, char *certfile, char *keyfile) { SSL_CTX_set_ecdh_auto(ctx, 1); - /* Set the key and cert */ + /* Set the key and cert if set or return if not */ + + if (certfile == NULL || keyfile == NULL) { + return; + } + if (SSL_CTX_use_certificate_file(ctx, certfile, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); diff --git a/sockets.h b/sockets.h index 099fc53..64cbd44 100644 --- a/sockets.h +++ b/sockets.h @@ -30,9 +30,13 @@ void init_openssl(); void cleanup_openssl(); -SSL_CTX *create_context(); +// Create OpenSSL context, type = 0 for IRC server-side (OpenSSL client) +// or type = 1 for bouncer client-side (OpenSSL server) +SSL_CTX *create_openssl_context(int type); -void configure_context(SSL_CTX *ctx, char *certfile, char *keyfile); +// Configure OpenSSL context, with certfile and keyfile provided if +// IRC server-side or set to NULL if bouncer client-side +void configure_openssl_context(SSL_CTX *ctx, char *certfile, char *keyfile); // Read from a socket, whether or not using TLS int sockread(SSL *fd, char *buf, int bufsize, int tls); -- cgit v1.2.3