Greetings po||ux,
Here is the patch. Sorry it is a bit messy. I was not able to figure out how to edit the autoconf files so I just edited the makefile directly to add the libpcre library:
Only in ngircd/src/ngircd: censor.c /* Copyright (C) 2019 by jrmu jrmu@lecturify.com
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. This software is offered as-is, without any warranty.
*/
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <pcre.h>
#define OVECCOUNT 30 /* should be a multiple of 3 */
/** * Indicates whether a message matches a list of regular expressions. * * @param msg The message to test * @param msg_len The length of msg * @param re An array of compiled regular expressions * @param re_len The length of re * @param out The matched string to return or NULL * @return Boolean for whether the message should be censored. */ bool censor(char *msg, size_t msg_len, pcre **re, size_t re_len, char *match) { int rc; int ovector[OVECCOUNT];
/* actual length needed because pcre_exec scans past \0 */ int len = strnlen(msg, msg_len);
for (unsigned int i = 0; i < re_len && re[i]; i++) {
rc = pcre_exec(re[i], NULL, msg, len, 0, 0, ovector, OVECCOUNT); if (rc >= 0) { strlcpy(match, msg+ovector[0], ovector[1]-ovector[0]+1); return true; } } return false; } diff -ru ngircd-barton/src/ngircd/client.c ngircd/src/ngircd/client.c --- ngircd-barton/src/ngircd/client.c Wed Sep 4 00:28:34 2019 +++ ngircd/src/ngircd/client.c Mon Sep 16 07:57:43 2019 @@ -337,7 +337,9 @@ assert(Client != NULL); assert(Hostname != NULL);
- if (Conf_CloakHost[0]) { + /* Only cloak the hostmask if it has not yet been cloaked (the period + * indicates it's still an IP address). */ + if (Conf_CloakHost[0] && strchr(Client->host, '.')) { char cloak[GETID_LEN];
strlcpy(cloak, Hostname, GETID_LEN); diff -ru ngircd-barton/src/ngircd/client.h ngircd/src/ngircd/client.h --- ngircd-barton/src/ngircd/client.h Wed Sep 4 00:28:34 2019 +++ ngircd/src/ngircd/client.h Mon Sep 16 07:57:43 2019 @@ -27,7 +27,7 @@ #define CLIENT_UNKNOWNSERVER 0x0080 /* unregistered server connection */ #define CLIENT_GOTPASS_2813 0x0100 /* client did send PASS, RFC 2813 style */ #ifndef STRICT_RFC -# define CLIENT_WAITAUTHPING 0x0200 /* waiting for AUTH PONG from client */ +#define CLIENT_WAITAUTHPING 0x0200 /* waiting for AUTH PONG from client */ #endif #define CLIENT_WAITCAPEND 0x0400 /* waiting for "CAP END" command */ #define CLIENT_ANY 0xFFFF diff -ru ngircd-barton/src/ngircd/conf.c ngircd/src/ngircd/conf.c --- ngircd-barton/src/ngircd/conf.c Wed Sep 4 00:28:34 2019 +++ ngircd/src/ngircd/conf.c Mon Sep 16 12:22:32 2019 @@ -68,6 +68,8 @@ char *Var, char *Arg )); static void Handle_CHANNEL PARAMS((const char *File, int Line, char *Var, char *Arg )); +static void Handle_CENSOR PARAMS((const char *File, int Line, + char *Var, char *Arg ));
static void Config_Error PARAMS((const int Level, const char *Format, ...));
@@ -332,6 +334,8 @@ bool config_valid; size_t predef_channel_count; struct Conf_Channel *predef_chan; + size_t predef_censor_count; + struct Conf_Censor *predef_censor;
Use_Log = false;
@@ -388,7 +392,7 @@ printf(" MaxConnectionsIP = %d\n", Conf_MaxConnectionsIP); printf(" MaxJoins = %d\n", Conf_MaxJoins > 0 ? Conf_MaxJoins : -1); printf(" MaxNickLength = %u\n", Conf_MaxNickLength - 1); - printf(" MaxPenaltyTime = %ld\n", Conf_MaxPenaltyTime); + printf(" MaxPenaltyTime = %lld\n", Conf_MaxPenaltyTime); printf(" MaxListSize = %d\n", Conf_MaxListSize); printf(" PingTimeout = %d\n", Conf_PingTimeout); printf(" PongTimeout = %d\n", Conf_PongTimeout); @@ -490,6 +494,17 @@ printf(" KeyFile = %s\n\n", predef_chan->keyfile); }
+ predef_censor_count = array_length(&Conf_Censors, sizeof(*predef_censor)); + predef_censor = array_start(&Conf_Censors); + + for (i = 0; i < predef_censor_count; i++, predef_censor++) { + if (!predef_censor->file) + continue; + puts( "[CENSOR]" ); + printf(" File = %s\n\n", predef_censor->file); + } + /* "Censor" section */ + return (config_valid ? 0 : 1); }
@@ -757,7 +772,7 @@ strlcat(Conf_HelpFile, HELP_FILE, sizeof(Conf_HelpFile)); strcpy(Conf_ServerPwd, ""); strlcpy(Conf_PidFile, PID_FILE, sizeof(Conf_PidFile)); - Conf_UID = Conf_GID = 0; + Conf_UID = Conf_GID = 703;
/* Limits */ Conf_ConnectRetry = 60; @@ -818,7 +833,6 @@ Conf_SyslogFacility = 0; #endif #endif - /* Initialize server configuration structures */ if (InitServers) { for (i = 0; i < MAX_SERVERS; @@ -901,6 +915,12 @@ int i, n; FILE *fd; DIR *dh; + size_t predef_censor_count; + struct Conf_Censor *predef_censor; + const char *error; + int line = 0; + char filters[LINE_LEN][LINE_LEN]; + int erroffset;
Log(LOG_INFO, "Using configuration file "%s" ...", NGIRCd_ConfFile);
@@ -1049,6 +1069,41 @@ Conf_SSLOptions.CipherList = strdup_warn(DEFAULT_CIPHERS); #endif
+ /* Load censor files */ + predef_censor_count = array_length(&Conf_Censors, sizeof(*predef_censor)); + predef_censor = array_start(&Conf_Censors); + + for (unsigned int i = 0; i < predef_censor_count; i++, predef_censor++) { +// LogDebug("\n\nCensor file: %s\n", predef_censor->file); + if (predef_censor->file) { + fd = fopen(predef_censor->file, "r"); + if (fd) { + while (fgets(filters[line], LINE_LEN, fd)) { + ngt_TrimLastChr(filters[line], '\n'); + line++; + } + fclose(fd); + pcre **re = calloc(sizeof(pcre *), line); + if (!re) { + Config_Error(LOG_ERR, "Can't allocate memory for censor rules"); + break; + } + for (int i = 0; i < line; i++) { + re[i] = pcre_compile(filters[i], PCRE_CASELESS, &error, &erroffset, NULL); + if (!re[i]) { + Config_Error(LOG_ERR, "Can't compile censor rules"); + break; + } + LogDebug("Line: %d, Filter: %s, Re: %p", i, filters[i], re[i]); + } + predef_censor->re = re; + predef_censor->len = line; + } else { + Config_Error(LOG_ERR, "Can't read %s", predef_censor->file); + } + } + } + /* Censor filters */ return true; }
@@ -1150,6 +1205,18 @@ continue; }
+ if (strcasecmp(section, "[CENSOR]") == 0) { + count = array_length(&Conf_Censors, + sizeof(struct Conf_Censor)); + if (!array_alloc(&Conf_Censors, + sizeof(struct Conf_Censor), count)) { + Config_Error(LOG_ERR, + "Could not allocate memory for new censor (line %d)", + line); + } + continue; + } + Config_Error(LOG_ERR, "%s, line %d: Unknown section "%s"!", File, line, section); @@ -1187,6 +1254,8 @@ Handle_SERVER(File, line, var, arg); else if (strcasecmp(section, "[CHANNEL]") == 0) Handle_CHANNEL(File, line, var, arg); + else if (strcasecmp(section, "[CENSOR]") == 0) + Handle_CENSOR(File, line, var, arg); else Config_Error(LOG_ERR, "%s, line %d: Variable "%s" outside section!", @@ -1646,7 +1715,7 @@ return; } if (strcasecmp(Var, "MaxPenaltyTime") == 0) { - Conf_MaxPenaltyTime = atol(Arg); + Conf_MaxPenaltyTime = atoll(Arg); if (Conf_MaxPenaltyTime < -1) Conf_MaxPenaltyTime = -1; /* "unlimited" */ return; @@ -2193,6 +2262,40 @@ }
/** + * Handle variable in [CENSOR] configuration section. + * + * @param Line Line number in configuration file. + * @param Var Variable name. + * @param Arg Variable argument. + */ +static void +Handle_CENSOR(const char *File, int Line, char *Var, char *Arg) +{ + size_t len; + struct Conf_Censor *censor; + + assert( File != NULL ); + assert( Line > 0 ); + assert( Var != NULL ); + assert( Arg != NULL ); + + censor = array_get(&Conf_Censors, sizeof(*censor), + array_length(&Conf_Censors, sizeof(*censor)) - 1); + if (!censor) + return; + + if (strcasecmp(Var, "File") == 0) { + /* Censor file */ + len = strlcpy(censor->file, Arg, sizeof(censor->file)); + if (len >= sizeof(censor->file)) + Config_Error_TooLong(File, Line, Var); + return; + } + + Config_Error_Section(File, Line, Var, "Censor"); +} + +/** * Validate server configuration. * * Please note that this function uses exit(1) on fatal errors and therefore @@ -2280,8 +2383,8 @@
if (Conf_MaxPenaltyTime != -1) Config_Error(LOG_WARNING, - "Maximum penalty increase ('MaxPenaltyTime') is set to %ld, this is not recommended!", - Conf_MaxPenaltyTime); + "Maximum penalty increase ('MaxPenaltyTime') is set to %lld, this is not recommended!", + (long long)Conf_MaxPenaltyTime);
#ifdef DEBUG servers = servers_once = 0; diff -ru ngircd-barton/src/ngircd/conf.h ngircd/src/ngircd/conf.h --- ngircd-barton/src/ngircd/conf.h Wed Sep 4 00:28:34 2019 +++ ngircd/src/ngircd/conf.h Mon Sep 16 07:57:43 2019 @@ -27,6 +27,8 @@ #include "proc.h" #include "conf-ssl.h"
+#include <pcre.h> + /** * Configured IRC operator. * Please note that the name of the IRC operaor and his nick have nothing to @@ -101,7 +103,7 @@ /** Server info text */ GLOBAL char Conf_ServerInfo[CLIENT_INFO_LEN];
-/** Global server passwort */ +/** Global server password */ GLOBAL char Conf_ServerPwd[CLIENT_PASS_LEN];
/** Administrative information */ @@ -283,8 +285,16 @@ GLOBAL void Conf_DebugDump PARAMS((void)); #endif
+/** Censorship rules, one per file */ +struct Conf_Censor { + char file[FNAME_LEN]; /** File path */ + pcre **re; /** Patterns to match against */ + size_t len; /** length of regex patterns array */ +};
-#endif +/** Array of censorship rules */ +GLOBAL array Conf_Censors;
+#endif
/* -eof- */ diff -ru ngircd-barton/src/ngircd/irc.c ngircd/src/ngircd/irc.c --- ngircd-barton/src/ngircd/irc.c Wed Sep 4 00:28:34 2019 +++ ngircd/src/ngircd/irc.c Mon Sep 16 11:27:55 2019 @@ -38,6 +38,7 @@ #include "op.h"
#include "irc.h" +#include "censor.c"
static char *Option_String PARAMS((CONN_ID Idx)); static bool Send_Message PARAMS((CLIENT *Client, REQUEST *Req, int ForceType, @@ -511,6 +512,7 @@ #endif } /* Option_String */
+ /** * Send a message to target(s). * @@ -534,6 +536,12 @@ char *message = NULL; char *targets[MAX_HNDL_TARGETS]; int i, target_nr = 0; + size_t predef_censor_count; + struct Conf_Censor *predef_censor; + char match[COMMAND_LEN]; + char msg[COMMAND_LEN]; + pcre **re; + int re_len;
assert(Client != NULL); assert(Req != NULL); @@ -572,6 +580,26 @@ #endif message = Req->argv[1];
+ /* Check censorship rules */ + + predef_censor_count = array_length(&Conf_Censors, sizeof(*predef_censor)); + predef_censor = array_start(&Conf_Censors); + + if (strlcpy(msg, message, COMMAND_LEN) >= COMMAND_LEN) { + LogDebug("Error copying message!"); + } else { + for (unsigned int i = 0; i < predef_censor_count; i++, predef_censor++) { + re = predef_censor->re; + re_len = predef_censor->len; + if (re && censor(msg, COMMAND_LEN, re, re_len, match)) { + LogDebug("Message: %s, match: %s", msg, match); + snprintf(msg, COMMAND_LEN, "Your message to %s was dropped because it contains profanity or spam: %s. If this is an error, please report it in #help.", currentTarget, match); + currentTarget = Client_ID(Client); + message = msg; + break; + } + } + } /* handle msgtarget = msgto *("," msgto) */ currentTarget = strtok_r(currentTarget, ",", &strtok_last); ngt_UpperStr(Req->command); diff -ru ngircd-barton/src/ngircd/ngircd.c ngircd/src/ngircd/ngircd.c --- ngircd-barton/src/ngircd/ngircd.c Wed Sep 4 00:28:34 2019 +++ ngircd/src/ngircd/ngircd.c Mon Sep 16 07:57:43 2019 @@ -560,7 +560,7 @@ #if !defined(SINGLE_USER_OS)
/** - * Get user and group ID of unprivileged "nobody" user. + * Get user and group ID of unprivileged "_ngircd" user. * * @param uid User ID * @param gid Group ID @@ -584,7 +584,7 @@ } #endif
- pwd = getpwnam("nobody"); + pwd = getpwnam("_ngircd"); if (!pwd) return false;
@@ -700,11 +700,11 @@ if (Conf_UID == 0) { pwd = getpwuid(0); Log(LOG_INFO, - "ServerUID must not be %s(0), using "nobody" instead.", + "ServerUID must not be %s(0), using "_ngircd" instead.", pwd ? pwd->pw_name : "?"); if (!NGIRCd_getNobodyID(&Conf_UID, &Conf_GID)) { Log(LOG_WARNING, - "Could not get user/group ID of user "nobody": %s", + "Could not get user/group ID of user "_ngircd": %s", errno ? strerror(errno) : "not found" ); goto out; } Only in /etc/ngircd: profanity \b(smart|fat|dumb|big)?(ass|arse)(hol|whol)?e?s?\b bastard \bbdsm\b bitch blow\s?job bollock \bboners?\b \bboo+b(ies|s)?\b \bbugger(ed|ing)?s?\b butt(w?hole|plug) \bcirclejerk \bclit(s)?\b \bcock(s|suck.*)?\b \bcrap(py|ped|ping|per|tastic)?\b \b[ck]um(mer|ming|shot)?s?\b [ck]un+il+ingus \b[c|k]unt(s|lick.*)?\b \b(god-?)?damn \bdick (tele)?dildo \bdykes?\b eja[ck]ulat.* \b(dumb|nazi)?fag+(ot)?(it+)?s?\b \bfap(ping|pening|ed)?s?\b fellat(e|io) \bfook(er)?s?\b \b(m[ou]ther|m[ou]tha|father|sister|goat|cyber|finger|fist)?fuc?k+ fuc?k+(a|er|in|ing|ed|wad|head|off|whit)?s?\b \bfcuk(er|ing)?\b fudge\s*packers? \bfux(or)?s?\b gang\s*bang gay\s*(slave|lords?|sex) g[eo]t(ting|ten)? laid (phone|hot|core|\banal|\boral)\s*sex \bhorn(y|iest) (jack|jerk)-*\s*off \bjizz jizz\b \bkock(suck.*)?s?\b \blady\s*boys? lmf?ao\b mast[eu]rbat \bmofos?\b \bnigg(er|a)(s|z)?\b nutsacks? \bphu(ck|q)(ed|ing|ked)?s?\b \bpiss(ed|ing|in|es|er|ers|off)?\b \bpuss(e|y|ys|ies)\b \bretard(ed)?s?\b \brim(ming|job)?s?\b \bscrewing\b \bshag(ging)?\b \bshe-*males? \bshit|shit\b \bskank(y|s)?\b \bslut(ty|ting|s)?\b \btit+(s|y|ies|wank|ywank)\b \btwat(head|ty)?\b \bturds?\b \bwank(y|er)?s?\b \b(cam)?whor(e|ing)?(house)?s?\b \bwtf\b shibboleth
/etc/ngircd/ngircd.conf:
[Censor] File = /etc/ngircd/profanity
jrmu