summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Bratch <luke@bratch.co.uk>2024-03-30 16:09:37 +0000
committerLuke Bratch <luke@bratch.co.uk>2024-03-30 16:09:37 +0000
commit3612ac309895be690c8dc3080898dfb713b8a23e (patch)
tree0918a707e2f34a36ef944f02662997de2653a6f7
parent6bc5e61f1bd0217618aa799169b5439f013b6921 (diff)
Implement changing client-side TLS certificate and key paths, and reloading certificate/key at runtime when doing a REHASH (BLABOUNCER command or SIGHUP).
-rw-r--r--README7
-rw-r--r--TODO4
-rw-r--r--blabouncer.c29
-rw-r--r--functions.c35
-rw-r--r--functions.h3
-rw-r--r--message.c13
-rw-r--r--message.h3
7 files changed, 73 insertions, 21 deletions
diff --git a/README b/README
index 7381b49..5b217cf 100644
--- a/README
+++ b/README
@@ -23,7 +23,8 @@ If you don't specify one using "-c /path/to/configuration/file" then the example
for you at $HOME/.blabouncer/blabouncer.conf when starting for the first time.
Certain configuration options can be changed at runtime by changing them in the configuration file, then
-issuing a BLABOUNCER REHASH command, or by sending SIGHUP to the blabouncer process.
+issuing a BLABOUNCER REHASH command, or by sending SIGHUP to the blabouncer process. This will also
+reload the client-side TLS certificate and key if clienttls = "1".
These options can be changed by issuing a BLABOUNCER REHASH command or by sending SIGHUP to blabouncer:
- nicks
@@ -34,6 +35,8 @@ These options can be changed by issuing a BLABOUNCER REHASH command or by sendin
- logging
- replaylogging
- debug
+ - certfile
+ - keyfile
== Commands ==
@@ -41,7 +44,7 @@ Once connected to blabouncer with an IRC client, you can use the following speci
"BLABOUNCER REPLAY [[[days:]hours:]minutes]" (To replay a given length of time of replay log.)
"BLABOUNCER QUIT [quit message]" (To quit blabouncer, optionally sending [quit message] to the server.)
-"BLABOUNCER REHASH" (To reload settings from the configuration file, see above for details.)
+"BLABOUNCER REHASH" (To reload configuration file settings and the client-side TLS certificate/key - see above for details.)
"BLABOUNCER CLIENTCODE [clientcode]" (To set an identifier for the current client for auto replaying just
what this client has missed.)
"BLABOUNCER LISTCLIENTS" (To list all connected clients and their authentication status.)
diff --git a/TODO b/TODO
index 3c64735..c0000c8 100644
--- a/TODO
+++ b/TODO
@@ -12,8 +12,6 @@ macOS compiler may need limits.h included in structures.h.
Can memory usage be reduced further? (e.g. better channel struct management)
-Ability to load new certificate whilst running.
-
Remaining strncmps are safe but could do with length comparisons too. "Our initial MODE" comparison may need to be more complex.
"/topic" response goes to all clients.
@@ -48,7 +46,7 @@ Sun Apr 9 04:16:09 2023: Server PART: nick is ours ('bbounce2' vs 'bbounce2').
Crash when requesting 30 hour replay.
-Users in channel not being populated observed once - 27/07/2023 #pipewire on irc.oftc.net.
+Users in channel not being populated observed once - 27/07/2023 #pipewire on irc.oftc.net. (and again 14/09/2023)
(related to?):
11/06/2023 01:00:41 "processrawstring(): combined truncated message '2131365056' is too long (1006 out of a maximum of 512 characters), clearing it." seen during long/truncated/recombined 353 RPL_NAMREPLY #winehq on irc.libera.chat.
diff --git a/blabouncer.c b/blabouncer.c
index c56fc9d..72118ec 100644
--- a/blabouncer.c
+++ b/blabouncer.c
@@ -180,7 +180,7 @@ int connecttoircserver(SSL_CTX **serverctx, SSL **server_ssl, int *serversockfd,
// 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(SSL *server_ssl, char *str, int source, struct client *clients, int sourcefd, struct ircdstate *ircdstate, struct channel *channels,
- struct settings *settings, struct clientcodes *clientcodes) {
+ struct settings *settings, struct clientcodes *clientcodes, SSL_CTX *ctx) {
// Track which space-separated token within this response we're on
int counter = 0;
@@ -219,7 +219,7 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli
// Don't return if we got here because this means we didn't process something in processservermessage()
break;
case SOURCE_CLIENT: // If message(s) were from a real IRC client
- if (processclientmessage(server_ssl, str, clients, sourcefd, ircdstate, channels, settings, tokens, counter, clientcodes)) {
+ if (processclientmessage(server_ssl, str, clients, sourcefd, ircdstate, channels, settings, tokens, counter, clientcodes, ctx)) {
// We processed something so return true
free(strcopyPtr);
return 1;
@@ -265,7 +265,7 @@ int processircmessage(SSL *server_ssl, char *str, int source, struct client *cli
// Return 0 if something went wrong
// Return 1 if everything OK
int processrawstring(SSL *server_ssl, char *str, int source, struct client *clients, int sourcefd, struct ircdstate *ircdstate, struct channel *channels,
- struct settings *settings, struct clientcodes *clientcodes) {
+ struct settings *settings, struct clientcodes *clientcodes, SSL_CTX *ctx) {
// Copy to a temporary string so we still have the original in case it's not processed
char *strcopy = strdup(str);
// Keep track of initial pointer for free()ing later
@@ -334,7 +334,7 @@ int processrawstring(SSL *server_ssl, char *str, int source, struct client *clie
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(server_ssl, messagecopy, source, clients, sourcefd, ircdstate, channels, settings, clientcodes)) {
+ if (processircmessage(server_ssl, messagecopy, source, clients, sourcefd, ircdstate, channels, settings, clientcodes, ctx)) {
debugprint(DEBUG_FULL, "Message processed: \"%s\", NULLing...\n", messages[i]);
messages[i][0] = '\0';
}
@@ -518,7 +518,7 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) {
failuremsg[0] = '\0';
// Try to rehash...
- if (!rehash(settings, failuremsg)) {
+ if (!rehash(settings, failuremsg, ctx)) {
// ...or log and tell all clients if it failed
debugprint(DEBUG_CRIT, "SIGHUP REHASH failed: %s.\n", failuremsg);
if (!snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :SIGHUP REHASH failed: %s.", ircdstate.ircnick, failuremsg)) {
@@ -707,7 +707,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 responses/commands and only one didn't need relaying?
- if (!processrawstring(server_ssl, serverbuf, SOURCE_SERVER, clients, EXCEPT_NONE, &ircdstate, channels, settings, clientcodes)) {
+ if (!processrawstring(server_ssl, serverbuf, SOURCE_SERVER, clients, EXCEPT_NONE, &ircdstate, channels, settings, clientcodes, ctx)) {
fprintf(stderr, "Error: bouncer-server failed to process raw string.\n");
debugprint(DEBUG_CRIT, "Error: bouncer-server failed to process raw string.\n");
}
@@ -736,6 +736,8 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) {
}
// STDIN based commands for debugging
+
+ // STDIN: List all channels
if (strncmp(outgoingmsg, "listchannels", strlen("listchannels")) == 0) {
printf("STDIN command starting: listchannels\n");
debugprint(DEBUG_SOME, "dochat(): stdin: STDIN command starting: listchannels\n");
@@ -761,6 +763,19 @@ void dochat(int *serversockfd, int *clientsockfd, struct settings *settings) {
continue;
}
+ // STDIN: Reconfigure OpenSSL context
+ if (strncmp(outgoingmsg, "reopenssl", strlen("reopenssl")) == 0) {
+ printf("STDIN command starting: reopenssl\n");
+ debugprint(DEBUG_SOME, "dochat(): stdin: STDIN command starting: reopenssl\n");
+
+ configure_openssl_context(ctx, settings->certfile, settings->keyfile);
+
+ debugprint(DEBUG_SOME, "dochat(): stdin: STDIN command complete: reopenssl\n");
+ printf("STDIN command complete: reopenssl\n");
+
+ continue;
+ }
+
debugprint(DEBUG_FULL, "dochat(): stdin: '%s' not processed as a command, sending to server.\n", outgoingmsg);
// sourcefd = 0 as this is a trusted message
sendtoserver(server_ssl, outgoingmsg, strlen(outgoingmsg), 0, clients, settings);
@@ -919,7 +934,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 client responses/commands and only one didn't need relaying?
- if (!processrawstring(server_ssl, clientbuf, SOURCE_CLIENT, clients, i, &ircdstate, channels, settings, clientcodes)) {
+ if (!processrawstring(server_ssl, clientbuf, SOURCE_CLIENT, clients, i, &ircdstate, channels, settings, clientcodes, ctx)) {
fprintf(stderr, "Error: bouncer-client failed to process raw string.\n");
debugprint(DEBUG_CRIT, "Error: bouncer-client failed to process raw string.\n");
}
diff --git a/functions.c b/functions.c
index 377f1af..d0c0341 100644
--- a/functions.c
+++ b/functions.c
@@ -1076,8 +1076,9 @@ void cleanexit(SSL *server_ssl, struct client *clients, int sourcefd, struct irc
}
// Re-read the configuration file, setting 'failuremsg' to a failure message on failure.
+// 'ctx' is the client OpenSSL context for changing the certificate/key.
// Returns 1 on success or 0 on failure.
-int rehash(struct settings *settings, char *failuremsg) {
+int rehash(struct settings *settings, char *failuremsg, SSL_CTX *ctx) {
// TODO - Try to share some/all of this code with the initial main() settings loading
// What are the configured nick(s)?
@@ -1198,6 +1199,38 @@ int rehash(struct settings *settings, char *failuremsg) {
return 0;
}
+ // If clienttls = 1, re-read the certificate and key file paths (we don't support switching between TLS and non-TLS)
+ if (settings->clienttls) {
+
+ // What is the certificate file path?
+ char oldcertfile[PATH_MAX];
+ strcpy(oldcertfile, settings->certfile);
+ if (!getconfstr("certfile", settings->conffile, settings->certfile)) {
+ // If none provided, set to default
+ if (!snprintf(settings->certfile, PATH_MAX, "%s/cert.pem", settings->basedir)) {
+ strcpy(settings->certfile, oldcertfile);
+ strcpy(failuremsg, "didn't get 'certfile' from configuration file and failed to prepare default certfile location");
+ return 0;
+ }
+ }
+
+ // What is the key file path?
+ char oldkeyfile[PATH_MAX];
+ strcpy(oldkeyfile, settings->keyfile);
+ if (!getconfstr("keyfile", settings->conffile, settings->keyfile)) {
+
+ // If none provided, set to default
+ if (!snprintf(settings->keyfile, PATH_MAX, "%s/key.pem", settings->basedir)) {
+ strcpy(settings->keyfile, oldkeyfile);
+ strcpy(failuremsg, "didn't get 'keyfile' from configuration file and failed to prepare default keyfile location");
+ return 0;
+ }
+ }
+
+ // Reconfigure OpenSSL context in case the certificate or the key changed
+ configure_openssl_context(ctx, settings->certfile, settings->keyfile);
+ }
+
// All is good, no failure message, return 1.
failuremsg[0] = '\0';
return 1;
diff --git a/functions.h b/functions.h
index 6ec2c28..ff9d656 100644
--- a/functions.h
+++ b/functions.h
@@ -167,8 +167,9 @@ void tryautonick(struct ircdstate *ircdstate);
void cleanexit(SSL *server_ssl, struct client *clients, int sourcefd, struct ircdstate *ircdstate, struct settings *settings, char *quitmsg);
// Re-read the configuration file, setting 'failuremsg' to a failure message on failure.
+// 'ctx' is the client OpenSSL context for changing the certificate/key.
// Returns 1 on success or 0 on failure.
-int rehash(struct settings *settings, char *failuremsg);
+int rehash(struct settings *settings, char *failuremsg, SSL_CTX *ctx);
// Check the password provided in the string 'str' against what is in
// the settings structure 'settings'.
diff --git a/message.c b/message.c
index c45f56b..f9e65a2 100644
--- a/message.c
+++ b/message.c
@@ -189,9 +189,9 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int
if (strncmp(tokens[1], "JOIN", strlen(tokens[1])) == 0) {
debugprint(DEBUG_FULL, "Server JOIN found and it is: %s with length %zd! Next token is '%s'. Adding to local channel list if it's us.\n", tokens[0], strlen(tokens[0]), tokens[2]);
// Next token should be the channel name but it probably needs the leading ':' stripping
- debugprint(DEBUG_FULL, "processircmessage(): Channel name was '%s'\n", tokens[2]);
+ debugprint(DEBUG_FULL, "processservermessage(): Channel name was '%s'\n", tokens[2]);
stripprefix(tokens[2], 1);
- debugprint(DEBUG_FULL, "processircmessage(): Channel name now '%s'\n", tokens[2]);
+ debugprint(DEBUG_FULL, "processservermessage(): Channel name now '%s'\n", tokens[2]);
// If the user JOINing is us, then we must have joined a channel, so add to our local channel array.
// Copy to a temporary string so we still have the original in case we need it
@@ -850,7 +850,8 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int
// Process an IRC message that came from a client.
// Return 1 if we processed it, or 0 if we didn't.
int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int sourcefd, struct ircdstate *ircdstate,
- struct channel *channels, struct settings *settings, char tokens[MAXTOKENS][MAXDATASIZE], int counter, struct clientcodes *clientcodes) {
+ struct channel *channels, struct settings *settings, char tokens[MAXTOKENS][MAXDATASIZE], int counter,
+ struct clientcodes *clientcodes, SSL_CTX *ctx) {
// Index of client fd in clients array for use later
int clientindex = arrindex(clients, sourcefd);
if (clientindex < 0) {
@@ -999,7 +1000,7 @@ int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [[[days:]hours:]minutes]\" (To replay a given length of time of replay log.)", ircdstate->ircnick);
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REHASH\" (To reload settings from the configuration file, see README for which settings can be reloaded.)", ircdstate->ircnick);
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REHASH\" (To reload configuration file settings and the client-side TLS certificate/key - see README for details.)", ircdstate->ircnick);
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER CLIENTCODE [clientcode]\" (To set an identifier for the current client for auto replaying just what this client has missed.)", ircdstate->ircnick);
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
@@ -1422,7 +1423,7 @@ int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int
failuremsg[0] = '\0';
// Try to rehash...
- if (!rehash(settings, failuremsg)) {
+ if (!rehash(settings, failuremsg, ctx)) {
// ...or log and tell client if it failed
debugprint(DEBUG_CRIT, "REHASH failed: %s.\n", failuremsg);
if (!snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :REHASH failed: %s.", ircdstate->ircnick, failuremsg)) {
@@ -1545,7 +1546,7 @@ int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int
}
snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REPLAY [[[days:]hours:]minutes]\" (To replay a given length of time of replay log.)", ircdstate->ircnick);
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
- snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REHASH\" (To reload settings from the configuration file, see README for which settings can be reloaded.)", ircdstate->ircnick);
+ snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER REHASH\" (To reload configuration file settings and the client-side TLS certificate/key - see README for details.)", ircdstate->ircnick);
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :\"BLABOUNCER CLIENTCODE [clientcode]\" (To set an identifier for the current client for auto replaying just what this client has missed.)", ircdstate->ircnick);
sendtoclient(sourcefd, outgoingmsg, clients, settings, 0);
diff --git a/message.h b/message.h
index 5d6cfff..2bf4bab 100644
--- a/message.h
+++ b/message.h
@@ -32,6 +32,7 @@ int processservermessage(SSL *server_ssl, char *str, struct client *clients, int
// Process an IRC message that came from a client.
// Return 1 if we processed it, or 0 if we didn't.
int processclientmessage(SSL *server_ssl, char *str, struct client *clients, int sourcefd, struct ircdstate *ircdstate,
- struct channel *channels, struct settings *settings, char tokens[MAXTOKENS][MAXDATASIZE], int counter, struct clientcodes *clientcodes);
+ struct channel *channels, struct settings *settings, char tokens[MAXTOKENS][MAXDATASIZE], int counter,
+ struct clientcodes *clientcodes, SSL_CTX *ctx);
#endif