/* * Based on timedban.c. Created by k4be. * (C) Copyright 2009-2017 Bram Matthys (Syzop) and the UnrealIRCd team. * License: GPLv2 * * This module adds an extended ban ~I:mask * Ban prefixed with ~I will match only users that are not identified * to services. * * Note that this extended ban is rather special in the sense that * it permits (crazy) triple-extbans to be set, such as: * +b ~I:~q:~c:#channel * (=mute a user that is also on #channel, unless he's idenitifed to services) * * The triple-extbans / double-stacking requires special routines that * are based on parts of the core and special recursion checks. * If you are looking for inspiration of coding your own extended ban * then look at another extended ban * module as this module is not a * good starting point ;) */ #include "unrealircd.h" /* Maximum length of a ban */ #define MAX_LENGTH 128 ModuleHeader MOD_HEADER(m_unauthban) = { "m_unauthban", "$Id: v1.00 2018/11/05 k4be$", "ExtBan ~I: bans that match only users that are not logged in", "3.2-b8-1", NULL }; /* Forward declarations */ char *unauthban_extban_conv_param(char *para_in); int unauthban_extban_is_ok(aClient* sptr, aChannel* chptr, char* para_in, int checkt, int what, int what2); int unauthban_is_banned(aClient *sptr, aChannel *chptr, char *ban, int chktype); void add_send_mode_param(aChannel *chptr, aClient *from, char what, char mode, char *param); char *unauthban_chanmsg(aClient *, aClient *, aChannel *, char *, int); MOD_TEST(m_unauthban) { return MOD_SUCCESS; } MOD_INIT(m_unauthban) { ExtbanInfo extban; memset(&extban, 0, sizeof(ExtbanInfo)); extban.flag = 'I'; extban.options |= EXTBOPT_ACTMODIFIER; /* not really, but ours shouldn't be stacked from group 1 */ extban.options |= EXTBOPT_CHSVSMODE; /* so "SVSMODE -nick" will unset affected ~I extbans */ //extban.options |= EXTBOPT_INVEX; /* invex probably would be useless */ extban.conv_param = unauthban_extban_conv_param; extban.is_ok = unauthban_extban_is_ok; extban.is_banned = unauthban_is_banned; if (!ExtbanAdd(modinfo->handle, extban)) { config_error("m_unauthban: unable to register 'I' extban type!!"); return MOD_FAILED; } return MOD_SUCCESS; } MOD_LOAD(m_unauthban) { return MOD_SUCCESS; } MOD_UNLOAD(m_unauthban) { return MOD_SUCCESS; } /** Generic helper for our conv_param extban function. * Mostly copied from clean_ban_mask() */ char *generic_clean_ban_mask(char *mask) { char *cp, *x; char *user; char *host; Extban *p; static char maskbuf[512]; /* Work on a copy */ strlcpy(maskbuf, mask, sizeof(maskbuf)); mask = maskbuf; cp = index(mask, ' '); if (cp) *cp = '\0'; /* Strip any ':' at beginning since that would cause a desynch */ for (; (*mask && (*mask == ':')); mask++); if (!*mask) return NULL; /* Forbid ASCII <= 32 in all bans */ for (x = mask; *x; x++) if (*x <= ' ') return NULL; /* Extended ban? */ if ((*mask == '~') && mask[1] && (mask[2] == ':')) { p = findmod_by_bantype(mask[1]); if (!p) return NULL; /* reject unknown extban */ if (p->conv_param) return p->conv_param(mask); /* else, do some basic sanity checks and cut it off at 80 bytes */ if ((mask[1] != ':') || (mask[2] == '\0')) return NULL; /* require a ":" after extban type */ if (strlen(mask) > 80) mask[80] = '\0'; return mask; } if ((*mask == '~') && !strchr(mask, '@')) return NULL; /* not an extended ban and not a ~user@host ban either. */ if ((user = index((cp = mask), '!'))) *user++ = '\0'; if ((host = rindex(user ? user : cp, '@'))) { *host++ = '\0'; if (!user) return make_nick_user_host(NULL, trim_str(cp,USERLEN), trim_str(host,HOSTLEN)); } else if (!user && index(cp, '.')) return make_nick_user_host(NULL, NULL, trim_str(cp,HOSTLEN)); return make_nick_user_host(trim_str(cp,NICKLEN), trim_str(user,USERLEN), trim_str(host,HOSTLEN)); } /** Convert ban to an acceptable format (or return NULL to fully reject it) */ char *unauthban_extban_conv_param(char *para_in) { static char retbuf[MAX_LENGTH+1]; char para[MAX_LENGTH+1]; char tmpmask[MAX_LENGTH+1]; char *newmask; /**< Cleaned matching method, such as 'n!u@h' */ static int unauthban_extban_conv_param_recursion = 0; if (unauthban_extban_conv_param_recursion) return NULL; /* reject: recursion detected! */ strlcpy(para, para_in+3, sizeof(para)); /* work on a copy (and truncate it) */ /* ~I:n!u@h for direct matching * ~I:~x:.... when calling another bantype */ strlcpy(tmpmask, para, sizeof(tmpmask)); unauthban_extban_conv_param_recursion++; //newmask = extban_conv_param_nuh_or_extban(tmpmask); newmask = generic_clean_ban_mask(tmpmask); unauthban_extban_conv_param_recursion--; if (!newmask || (strlen(newmask) <= 1)) return NULL; snprintf(retbuf, sizeof(retbuf), "~I:%s", newmask); return retbuf; } int unauthban_extban_syntax(aClient *sptr, int checkt, char *reason) { if (MyClient(sptr) && (checkt == EXBCHK_PARAM)) { sendnotice(sptr, "Error when setting unauth ban: %s", reason); sendnotice(sptr, " Syntax: +b ~I:mask"); sendnotice(sptr, "Example: +b ~I:nick!user@host"); sendnotice(sptr, "Valid masks are: nick!user@host or another extban type such as ~c, ~S, .."); } return 0; /* FAIL: ban rejected */ } /** Generic helper for sub-bans, used by our "is this ban ok?" function */ int generic_ban_is_ok(aClient *sptr, aChannel *chptr, char *mask, int checkt, int what, int what2) { if ((mask[0] == '~') && MyClient(sptr)) { Extban *p; /* This portion is copied from clean_ban_mask() */ if (mask[1] && (mask[2] == ':') && RESTRICT_EXTENDEDBANS && MyClient(sptr) && !ValidatePermissionsForPath("channel:extbans",sptr,NULL,NULL,NULL)) { if (!strcmp(RESTRICT_EXTENDEDBANS, "*")) { if (checkt == EXBCHK_ACCESS_ERR) sendnotice(sptr, "Setting/removing of extended bans has been disabled"); return 0; /* REJECT */ } if (strchr(RESTRICT_EXTENDEDBANS, mask[1])) { if (checkt == EXBCHK_ACCESS_ERR) sendnotice(sptr, "Setting/removing of extended bantypes '%s' has been disabled", RESTRICT_EXTENDEDBANS); return 0; /* REJECT */ } } /* End of portion */ /* This portion is inspired by m_mode */ p = findmod_by_bantype(mask[1]); if (checkt == EXBCHK_ACCESS) { if (p && p->is_ok && !p->is_ok(sptr, chptr, mask, EXBCHK_ACCESS, what, what2) && !ValidatePermissionsForPath("override:extban",sptr,NULL,chptr,NULL)) { return 0; /* REJECT */ } } else if (checkt == EXBCHK_ACCESS_ERR) { if (p && p->is_ok && !p->is_ok(sptr, chptr, mask, EXBCHK_ACCESS, what, what2) && !ValidatePermissionsForPath("override:extban",sptr,NULL,chptr,NULL)) { p->is_ok(sptr, chptr, mask, EXBCHK_ACCESS_ERR, what, what2); return 0; /* REJECT */ } } else if (checkt == EXBCHK_PARAM) { if (p && p->is_ok && !p->is_ok(sptr, chptr, mask, EXBCHK_PARAM, what, what2)) { return 0; /* REJECT */ } } /* End of portion */ } /* ACCEPT: * - not an extban; OR * - extban with NULL is_ok; OR * - non-existing extban character (handled by conv_param?) */ return 1; } /** Validate ban ("is this ban ok?") */ int unauthban_extban_is_ok(aClient* sptr, aChannel* chptr, char* para_in, int checkt, int what, int what2) { char para[MAX_LENGTH+1]; char tmpmask[MAX_LENGTH+1]; char *newmask; /**< Cleaned matching method, such as 'n!u@h' */ static int unauthban_extban_is_ok_recursion = 0; int res; /* Always permit deletion */ if (what == MODE_DEL) return 1; if (unauthban_extban_is_ok_recursion) return 0; /* Recursion detected (~I:~I:....) */ strlcpy(para, para_in+3, sizeof(para)); /* work on a copy (and truncate it) */ /* ~I:n!u@h for direct matching * ~I:~x:.... when calling another bantype */ strlcpy(tmpmask, para, sizeof(tmpmask)); unauthban_extban_is_ok_recursion++; //res = extban_is_ok_nuh_extban(sptr, chptr, tmpmask, checkt, what, what2); res = generic_ban_is_ok(sptr, chptr, tmpmask, checkt, what, what2); unauthban_extban_is_ok_recursion--; if (res == 0) { /* This could be anything ranging from: * invalid n!u@h syntax, unknown (sub)extbantype, * disabled extban type in conf, too much recursion, etc. */ return unauthban_extban_syntax(sptr, checkt, "Invalid matcher"); } return 1; /* OK */ } /** Check if the user is currently banned */ int unauthban_is_banned(aClient *sptr, aChannel *chptr, char *ban, int chktype) { if (strncmp(ban, "~I:", 3)) return 0; /* not for us */ if(IsLoggedIn(sptr)) return 0; // this is the magic ban += 3; // skip extban prefix return ban_check_mask(sptr, chptr, ban, chktype, 0); // if not logged in, process as normal ban } static char mbuf[512]; static char pbuf[512]; #if MODEBUFLEN > 512 #error "add_send_mode_param() is not made for MODEBUFLEN > 512" #endif void add_send_mode_param(aChannel *chptr, aClient *from, char what, char mode, char *param) { static char *modes = NULL, lastwhat; static short count = 0; short send = 0; if (!modes) modes = mbuf; if (!mbuf[0]) { modes = mbuf; *modes++ = what; *modes = 0; lastwhat = what; *pbuf = 0; count = 0; } if (lastwhat != what) { *modes++ = what; *modes = 0; lastwhat = what; } if (strlen(pbuf) + strlen(param) + 11 < MODEBUFLEN) { if (*pbuf) strcat(pbuf, " "); strcat(pbuf, param); *modes++ = mode; *modes = 0; count++; } else if (*pbuf) send = 1; if (count == MAXMODEPARAMS) send = 1; if (send) { sendto_channel_butserv(chptr, &me, ":%s MODE %s %s %s", me.name, chptr->chname, mbuf, pbuf); sendto_server(NULL, 0, 0, ":%s MODE %s %s %s 0", me.name, chptr->chname, mbuf, pbuf); send = 0; *pbuf = 0; modes = mbuf; *modes++ = what; lastwhat = what; if (count != MAXMODEPARAMS) { strlcpy(pbuf, param, sizeof(pbuf)); *modes++ = mode; count = 1; } else count = 0; *modes = 0; } }