diff options
| author | Luke Bratch <luke@bratch.co.uk> | 2019-05-12 00:42:57 +0100 | 
|---|---|---|
| committer | Luke Bratch <luke@bratch.co.uk> | 2019-05-12 00:42:57 +0100 | 
| commit | 01c0e36bb3f6c8345d4a94b157b68a0c0f4c85cf (patch) | |
| tree | 6d71e15784e052380de8747430e5fb0702db73aa | |
| parent | 34d410dc82e05f4255ec23a9deaff212b7903955 (diff) | |
Implement TLS using OpenSSL.
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | blabouncer.c | 102 | ||||
| -rw-r--r-- | sockets.c | 41 | ||||
| -rw-r--r-- | sockets.h | 10 | 
4 files changed, 124 insertions, 31 deletions
| @@ -5,4 +5,4 @@ DEPS = functions.h sockets.h config.h replay.h  	$(CC) -Wall -Wextra -c -o $@ $<  blabouncer: blabouncer.o functions.o sockets.o config.o replay.o -	$(CC) -Wall -Wextra -o blabouncer blabouncer.o functions.o sockets.o config.o replay.o +	$(CC) -Wall -Wextra -lssl -lcrypto -o blabouncer blabouncer.o functions.o sockets.o config.o replay.o diff --git a/blabouncer.c b/blabouncer.c index c748ebc..2ac787e 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -9,7 +9,9 @@  // - "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 relay log do more than PRIVMSGs? -// - Implement TLS +// - Make certificate paths configurable +// - Rename "ssl" to "arr_ssl" and remove the old send() based commented lines when ready +// - Consider moving the three fd-related arrays into one struct  // - Check authentication before even getting to the send functions to save unnecessary processing  //  // Example WHOIS reply: @@ -30,6 +32,9 @@  #include <arpa/inet.h>  #include <sys/select.h>  #include <time.h> +#include <openssl/ssl.h> +#include <openssl/err.h> +#include <openssl/bio.h>  #include "functions.h"  #include "sockets.h" @@ -78,8 +83,24 @@ struct ircdstrings {  int debugmode = 0; +// Return index of requested client FD within arr_clients +// This is used because at least arr_clients, arr_authed, and ssl share the same element order. +// TODO - Use this wherever we are calculating the position (various places) instead of +// duplicating code. +int arrindex(int arr_clients[], int clientfd) { +  // Find the client in the clients array and make sure they are authenticated +  for (int i = 0; i < MAXCLIENTS; i++) { +    if (arr_clients[i] == clientfd) { +      return i; +    } +  } + +  // Something went wrong, we didn't find it +  return 0; +} +  // Send whatever string to a specific client by providing the FD -int sendtoclient(int fd, char *str, int arr_clients[], int arr_authed[]) { +int sendtoclient(int fd, char *str, int arr_clients[], int arr_authed[], SSL **ssl) {    appendcrlf(str); // Do this just before sending so callers don't need to worry about it    // Find the client in the clients array and make sure they are authenticated @@ -94,7 +115,8 @@ int sendtoclient(int fd, char *str, int arr_clients[], int arr_authed[]) {    }    printf("sendtoclient(): sending \"%s\" (length %zd) to client with fd %d.\n", str, strlen(str), fd); -  if (send(fd, str, strlen(str), 0) == -1) { +//  if (send(fd, str, strlen(str), 0) == -1) { +  if (SSL_write(ssl[arrindex(arr_clients, fd)], str, strlen(str)) == -1) {      perror("error: sendtoclient() send()\n");      return 0;    } @@ -127,7 +149,7 @@ int disconnectclient(int fd, int arr_clients[], int arr_authed[]) {  // "except" is used to send to all clients _except_ the fd provided (except = 0 (EXCEPT_NONE) avoids this, i.e. sends to all)  // "except" is really the "sourcefd" and is also used as part of the authentication check - this is messy and they should perhaps be two separate arguments.  // TODO - is passing str_len useful if we're appendcrlfing and then using strlen(str) in the send?  I guess not...  (As long as we're always null terminated in the correct place.) -int sendtoallclients(int *clientsockfd, int fdmax, int arr_clients[], char *str, int except, int arr_authed[]) { +int sendtoallclients(int *clientsockfd, int fdmax, int arr_clients[], char *str, int except, int arr_authed[], SSL **ssl) {    char *sendertype; @@ -173,7 +195,8 @@ int sendtoallclients(int *clientsockfd, int fdmax, int arr_clients[], char *str,            continue;          }          printf("sendtoallclients(): %s: sending '%s' to client with fd %d.\n", sendertype, str, i); -        if (send(i, str, strlen(str), 0) == -1) { +//        if (send(i, str, strlen(str), 0) == -1) { +          if (SSL_write(ssl[arrindex(arr_clients, i)], str, strlen(str)) == -1) {            perror("error: sendtoallclients() send()\n");          }        } @@ -402,7 +425,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[]) { +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 **ssl) {    // Track which space-separated token within this response we're on    int counter = 0; @@ -511,7 +534,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc            }            // And then send to all clients -          sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed); +          sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed, ssl);            return 1;          } @@ -532,7 +555,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc            }            // And then send to all clients -          sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed); +          sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed, ssl);            return 1;          } @@ -599,7 +622,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc            setchanneltopicwhotime(channels, tokens[2], prefixcopy, timenowstr);            // And then finally relay to all clients -          sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed); +          sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed, ssl);            return 1;          } @@ -607,7 +630,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc          if (strncmp(tokens[1], "PRIVMSG", strlen(tokens[1])) == 0) {            printf("Server PRIVMSG found and it is: %s with length %zd!  Next token is '%s'.  Relaying to all clients.\n", tokens[0], strlen(tokens[0]), tokens[2]); -          sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed); +          sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed, ssl);            // Write to relay log            writerelayline(str); @@ -647,13 +670,13 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc          // Send IRC greeting strings (001/RPL_WELCOME, 002/RPL_YOURHOST, 003/RPL_CREATED, 004/RPL_MYINFO) to client          snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting001); -        sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed); +        sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, ssl);          snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting002); -        sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed); +        sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, ssl);          snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting003); -        sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed); +        sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, ssl);          snprintf(outgoingmsg, MAXDATASIZE, "%s", ircdstrings->greeting004); -        sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed); +        sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, ssl);          // Get the channel count so we can enumerate over all channels.          // Storing separately so we can skip over blank channels. @@ -674,7 +697,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc              fprintf(stderr, "Error while preparing USER just connected, channel JOIN responses!\n");              exit(1);            } -          sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed); +          sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, ssl);            // Send topic (or lack thereof) to client            // If there isn't one set (we guess this if topic timestamp is 0), send 331 RPL_NOTOPIC @@ -685,7 +708,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc                exit(1);              }              // ..and send it to the client -            sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed); +            sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, ssl);            // If there is one set, send 332 RPL_TOPIC and 333 RPL_TOPICWHOTIME            } else {              // Prepare the topic message... @@ -694,7 +717,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc                exit(1);              }              // ..and send it to the client -            sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed); +            sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, ssl);              // Next prepare the topic who/when message...              if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s 333 %s %s %s %s", ircdstrings->ircdname, ircdstrings->ircnick, channels[i].name, channels[i].topicwho, channels[i].topicwhen)) { @@ -702,7 +725,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc                exit(1);              }              // ..and send it to the client -            sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed); +            sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, ssl);            }            // Send list of names @@ -722,7 +745,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc                fprintf(stderr, "Error while preparing USER just connected, channel NAMES responses!\n");                exit(1);              } -            sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed); +            sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, ssl);            }            // Once all names are sent, send the "end of /NAMES" 366 (RPL_ENDOFNAMES) message @@ -730,7 +753,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc              fprintf(stderr, "Error while preparing USER just connected, end of NAMES response!\n");              exit(1);            } -          sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed); +          sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, ssl);          }          // Send the client however many relay lines have been requested @@ -752,7 +775,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc              exit(1);            }            printf("Sending relay line: '%s'.\n", outgoingmsg); -          sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed); +          sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, ssl);          }          return 1; @@ -767,7 +790,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc            fprintf(stderr, "Error while preparing PONG response!\n");            exit(1);          } -        sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed); +        sendtoclient(sourcefd, outgoingmsg, arr_clients, arr_authed, ssl);          // We processed something so return true          return 1; @@ -800,7 +823,7 @@ int processircmessage(int *serversockfd, int *clientsockfd, char *str, int sourc            exit(1);          }          // Send to all except source client -        sendtoallclients(clientsockfd, fdmax, arr_clients, outgoingmsg, sourcefd, arr_authed); +        sendtoallclients(clientsockfd, fdmax, arr_clients, outgoingmsg, sourcefd, arr_authed, ssl);          // Write to relay log          writerelayline(outgoingmsg); @@ -867,7 +890,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[]) { +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 **ssl) {    // Copy to a temporary string so we still have the original in case it's not processed    char *strcopy = strdup(str); @@ -896,7 +919,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)) { +    if (processircmessage(serversockfd, clientsockfd, messagecopy, source, fdmax, arr_clients, sourcefd, ircdstrings, channels, arr_authed, ssl)) {        printf("Message processed: \"%s\", NULLing...\n", messages[i]);        messages[i][0] = '\0';      } @@ -912,7 +935,7 @@ int processrawstring(int *serversockfd, int *clientsockfd, char *str, int source            // Relay/send to all clients ("except" = 0 because this should send to all clients)            // TODO - Is this really going to send the original string if we have messed it with it in processrawstring() and friends!?            printf("bouncer-server: sending unprocessed server messasge \"%s\" to all clients, length %zd.\n", messages[i], strlen(messages[i])); -          sendtoallclients(clientsockfd, fdmax, arr_clients, messages[i], EXCEPT_NONE, arr_authed); +          sendtoallclients(clientsockfd, fdmax, arr_clients, messages[i], EXCEPT_NONE, arr_authed, ssl);            break;          case SOURCE_CLIENT: // If message(s) were from a real IRC client            // Send to server @@ -921,7 +944,7 @@ int processrawstring(int *serversockfd, int *clientsockfd, char *str, int source            printf("bouncer-client: sending unprocessed client messasge \"%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) -          sendtoallclients(clientsockfd, fdmax, arr_clients, messages[i], sourcefd, arr_authed); +          sendtoallclients(clientsockfd, fdmax, arr_clients, messages[i], sourcefd, arr_authed, ssl);            break;          default:            fprintf(stderr, "Unexpected raw IRC string source for unprocessed messasge \"%s\", length %zd.!\n", messages[i], strlen(messages[i])); @@ -944,6 +967,7 @@ void dochat(int *serversockfd, int *clientsockfd) {    int outgoingmsgrc; // Return code from getstdin() for outgoing message    int arr_clients[MAXCLIENTS]; // Array of all client FDs - 0 means not connected, greater than 0 means connected and the value is the fd number (so we know which ones to try to read)    int arr_authed[MAXCLIENTS]; // Array of client authentication statuses - 0 means not authenticated, 1 means authenticated.  Element numbers match those of arr_clients. +  SSL *ssl[MAXCLIENTS]; // Array of OpenSSL structures    int num_clients = 0; // Current number of clients    int fdmax; // highest numbered socket fd @@ -1003,6 +1027,14 @@ void dochat(int *serversockfd, int *clientsockfd) {    channels = malloc(sizeof(struct channel) * MAXCHANNELS);    // =============================================> +  // OpenSSL context for client side (that clients connect to) +  SSL_CTX *ctx; + +  // Initialise OpenSSL +  init_openssl(); +  ctx = create_context(); +  configure_context(ctx); +    while (1) {      printf("top of loop, fdmax %d.\n", fdmax);      FD_ZERO(&rfds); // clear entries from fd set @@ -1050,7 +1082,7 @@ void dochat(int *serversockfd, int *clientsockfd) {        // 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)) { +      if (!processrawstring(serversockfd, clientsockfd, serverbuf, SOURCE_SERVER, fdmax, arr_clients, EXCEPT_NONE, &ircdstrings, channels, arr_authed, ssl)) {          fprintf(stderr, "Error: bouncer-server failed to process raw string.\n");          exit(1);        } @@ -1129,6 +1161,15 @@ void dochat(int *serversockfd, int *clientsockfd) {                    arr_clients[j] = newfd;                    // Ensure its authentication status is set to 0                    arr_authed[j] = 0; +                  // Set as OpenSSL FD and SSL_accept it +                  ssl[j] = SSL_new(ctx); +                  SSL_set_fd(ssl[j], newfd); +                  if (SSL_accept(ssl[j]) <= 0) { +                    printf("SSL_accept failed for fd %d.\n", j); +                    ERR_print_errors_fp(stderr); +                  } else { +                    printf("SSL_accept succeeded for fd %d.\n", j); +                  }                    break;                  }              } @@ -1140,7 +1181,8 @@ void dochat(int *serversockfd, int *clientsockfd) {          } else {            printf("...previous connection!\n");            // handle data from a client -          if ((clientnumbytes = recv(i, clientbuf, sizeof clientbuf, 0)) <= 0) { +//          if ((clientnumbytes = recv(i, clientbuf, sizeof clientbuf, 0)) <= 0) { +          if ((clientnumbytes = SSL_read(ssl[arrindex(arr_clients, i)], clientbuf, sizeof clientbuf)) <= 0) {              // got error or connection closed by client              if (clientnumbytes == 0) {                // connection closed @@ -1166,7 +1208,7 @@ void dochat(int *serversockfd, int *clientsockfd) {              // 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)) { +            if (!processrawstring(serversockfd, clientsockfd, clientbuf, SOURCE_CLIENT, fdmax, arr_clients, i, &ircdstrings, channels, arr_authed, ssl)) {                fprintf(stderr, "Error: bouncer-client failed to process raw string.\n");                exit(1);              } @@ -126,3 +126,44 @@ int createclientsocket(char *listenport) {    return listener;  } + +void init_openssl() { +  SSL_load_error_strings(); +  OpenSSL_add_ssl_algorithms(); +} + + +void cleanup_openssl() { +  EVP_cleanup(); +} + +SSL_CTX *create_context() { +  const SSL_METHOD *method; +  SSL_CTX *ctx; + +  method = SSLv23_server_method(); + +  ctx = SSL_CTX_new(method); +  if (!ctx) { +    perror("Unable to create SSL context"); +    ERR_print_errors_fp(stderr); +    exit(EXIT_FAILURE); +  } + +  return ctx; +} + +void configure_context(SSL_CTX *ctx) { +  SSL_CTX_set_ecdh_auto(ctx, 1); + +  /* Set the key and cert */ +  if (SSL_CTX_use_certificate_file(ctx, "cert.pem", SSL_FILETYPE_PEM) <= 0) { +    ERR_print_errors_fp(stderr); +    exit(EXIT_FAILURE); +  } + +  if (SSL_CTX_use_PrivateKey_file(ctx, "key.pem", SSL_FILETYPE_PEM) <= 0 ) { +    ERR_print_errors_fp(stderr); +    exit(EXIT_FAILURE); +  } +} @@ -12,6 +12,8 @@  #include <sys/socket.h>  #include <arpa/inet.h>  #include <sys/select.h> +#include <openssl/ssl.h> +#include <openssl/err.h>  #define BACKLOG 10 // maximum length to which the queue of pending connections for sockfd may grow @@ -26,4 +28,12 @@ int createserversocket(char *host, char *port);  // Create listening socket to listen for bouncer client connections  int createclientsocket(char *listenport); +void init_openssl(); + +void cleanup_openssl(); + +SSL_CTX *create_context(); + +void configure_context(SSL_CTX *ctx); +  #endif | 
