summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2019-05-12 00:42:57 +0100
committerLuke Bratch <luke@bratch.co.uk>2019-05-12 00:42:57 +0100
commit01c0e36bb3f6c8345d4a94b157b68a0c0f4c85cf (patch)
tree6d71e15784e052380de8747430e5fb0702db73aa
parent34d410dc82e05f4255ec23a9deaff212b7903955 (diff)
Implement TLS using OpenSSL.
-rw-r--r--Makefile2
-rw-r--r--blabouncer.c102
-rw-r--r--sockets.c41
-rw-r--r--sockets.h10
4 files changed, 124 insertions, 31 deletions
diff --git a/Makefile b/Makefile
index 5bf17a3..f297c74 100644
--- a/Makefile
+++ b/Makefile
@@ -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);
}
diff --git a/sockets.c b/sockets.c
index 40079c2..142be21 100644
--- a/sockets.c
+++ b/sockets.c
@@ -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);
+ }
+}
diff --git a/sockets.h b/sockets.h
index 3c8fcbf..08ca457 100644
--- a/sockets.h
+++ b/sockets.h
@@ -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