summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2019-05-13 00:07:15 +0100
committerLuke Bratch <luke@bratch.co.uk>2019-05-13 00:07:15 +0100
commit216f6a152333b38a8563c570eb237c27585deedb (patch)
tree1d376e9c40dcea8d1ed09213ebe820a8e761ea13
parent8869477885718844d368b48774f926489385e3b3 (diff)
Implement optional TLS for the server side.
-rw-r--r--blabouncer.c72
-rw-r--r--blabouncer.conf5
-rw-r--r--sockets.c21
-rw-r--r--sockets.h8
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);