/* * ================================================================= * Filename: operpasswd.c * Description: Failed OPER attempts notifier * Documentation: operpasswd.txt (comes with the package) * Author: AngryWolf * ================================================================= */ #include "config.h" #include "struct.h" #include "common.h" #include "sys.h" #include "numeric.h" #include "msg.h" #include "channel.h" #include #include #include #include #include #ifdef _WIN32 #include #endif #include #include "h.h" #ifdef STRIPBADWORDS #include "badwords.h" #endif #ifdef _WIN32 #include "version.h" #endif typedef struct _fcount FCount; struct _fcount { FCount *prev, *next; aClient *cptr; u_int count; }; #ifndef MODVAR extern int SVSNOOP; #endif extern void sendto_realops(char *pattern, ...); extern void sendto_serv_butone_token(aClient *one, char *prefix, char *command, char *token, char *pattern, ...); #define FlagOperPass 'O' #define ircstrdup(x,y) if (x) MyFree(x); if (!y) x = NULL; else x = strdup(y) #define ircfree(x) if (x) MyFree(x); x = NULL #define DelHook(x) if (x) HookDel(x); x = NULL #define DelOverride(cmd, ovr) if (ovr && CommandExists(cmd)) CmdoverrideDel(ovr); ovr = NULL #define DelSnomask(x) if (x) SnomaskDel(x); x = NULL #define IsSkoAdmin(sptr) (IsAdmin(sptr) || IsNetAdmin(sptr) || IsSAdmin(sptr) || IsCoAdmin(sptr)) #define UMODE_DENY 0 #define UMODE_ALLOW 1 #define ENABLE_SNOMASK 0x0001 #define ENABLE_GLOBAL_NOTICES 0x0002 #define ENABLE_LOGGING 0x0004 static Cmdoverride *AddOverride(char *msg, iFP cb); static Snomask *AddSnomask(char flag, iFP allowed, long *mode); static int ovr_oper(Cmdoverride *, aClient *, aClient *, int, char *[]); static int cb_test(ConfigFile *, ConfigEntry *, int, int *); static int cb_conf(ConfigFile *, ConfigEntry *, int); static int cb_quit(aClient *, char *); static int cb_rehash(); static void del_failop_counts(); static int umode_allow_admins(aClient *sptr, int what); #ifdef HOOKTYPE_REHASH_COMPLETE static int cb_rehash_complete(); #endif static Cmdoverride *OvrOper = NULL; static Hook *HookConfTest, *HookConfRun, *HookConfRehash; static Hook *HookQuit; static Snomask *SnomaskOperPass = NULL; static long SNO_OPERPASS = 0; static FCount *FailedOperups = NULL; static char *textbuf = NULL; #ifdef HOOKTYPE_REHASH_COMPLETE Hook *HookRehashDone; static u_char module_loaded = 0; #else u_int event_added; #endif struct { unsigned char options; u_int max_failed_operups; char *failop_kill_reason; } Settings; #ifndef STATIC_LINKING static ModuleInfo *MyModInfo; #define MyMod MyModInfo->handle #define SAVE_MODINFO MyModInfo = modinfo; #else #define MyMod NULL #define SAVE_MODINFO #endif ModuleHeader MOD_HEADER(operpasswd) = { "operpasswd", "$Id: operpasswd.c,v 3.1 2004/08/06 13:52:47 angrywolf Exp $", "Failed OPER attempts notifier", "3.2-b8-1", NULL }; static void InitConf() { #ifndef HOOKTYPE_REHASH_COMPLETE event_added = 0; #endif memset(&Settings, 0, sizeof Settings); } static void FreeConf() { ircfree(Settings.failop_kill_reason); } static int cb_rehash() { module_loaded = 0; #ifndef STATIC_LINKING DelOverride("oper", OvrOper); #endif FreeConf(); InitConf(); return 1; } #ifdef HOOKTYPE_REHASH_COMPLETE static int cb_rehash_complete() #else static EVENT(LoadObjects) #endif { if (!module_loaded) { #ifndef STATIC_LINKING OvrOper = AddOverride("oper", ovr_oper); #endif if (!(Settings.options & ENABLE_SNOMASK)) Settings.options &= ~ENABLE_GLOBAL_NOTICES; if (Settings.options) { if (!textbuf) textbuf = (char *) MyMalloc(BUFSIZE); } else { if (textbuf) { MyFree(textbuf); textbuf = NULL; } } if (Settings.options & ENABLE_SNOMASK) { if (!SnomaskOperPass) SnomaskOperPass = AddSnomask(FlagOperPass, umode_allow_admins, &SNO_OPERPASS); } else { DelSnomask(SnomaskOperPass); } if (!Settings.max_failed_operups) del_failop_counts(); module_loaded = 1; } #ifdef HOOKTYPE_REHASH_COMPLETE return 0; #endif } DLLFUNC int MOD_TEST(operpasswd)(ModuleInfo *modinfo) { SAVE_MODINFO HookConfTest = HookAddEx(modinfo->handle, HOOKTYPE_CONFIGTEST, cb_test); return MOD_SUCCESS; } DLLFUNC int MOD_INIT(operpasswd)(ModuleInfo *modinfo) { SAVE_MODINFO #ifndef STATIC_LINKING ModuleSetOptions(modinfo->handle, MOD_OPT_PERM); #endif SnomaskOperPass = NULL; FailedOperups = NULL; textbuf = NULL; InitConf(); HookConfRun = HookAddEx(modinfo->handle, HOOKTYPE_CONFIGRUN, cb_conf); HookConfRehash = HookAddEx(modinfo->handle, HOOKTYPE_REHASH, cb_rehash); HookQuit = HookAddEx(modinfo->handle, HOOKTYPE_LOCAL_QUIT, cb_quit); #ifdef HOOKTYPE_REHASH_COMPLETE HookRehashDone = HookAddEx(modinfo->handle, HOOKTYPE_REHASH_COMPLETE, cb_rehash_complete); #endif return MOD_SUCCESS; } DLLFUNC int MOD_LOAD(operpasswd)(int module_load) { cb_rehash_complete(); return MOD_SUCCESS; } DLLFUNC int MOD_UNLOAD(operpasswd)(int module_unload) { FreeConf(); del_failop_counts(); DelOverride("oper", OvrOper); DelSnomask(SnomaskOperPass); #ifdef HOOKTYPE_REHASH_COMPLETE DelHook(HookRehashDone); #endif DelHook(HookQuit); DelHook(HookConfRehash); DelHook(HookConfRun); DelHook(HookConfTest); return MOD_SUCCESS; } static void del_failop_counts() { FCount *f; ListStruct *next; for (f = FailedOperups; f; f = (FCount *) next) { next = (ListStruct *) f->next; DelListItem(f, FailedOperups); MyFree(f); } } static FCount *find_failop_count(aClient *cptr) { FCount *f; for (f = FailedOperups; f; f = f->next) if (f->cptr == cptr) break; return f; } static int umode_allow_admins(aClient *sptr, int what) { /* don't check access remotely */ return (!MyClient(sptr) || IsSkoAdmin(sptr)) ? UMODE_ALLOW : UMODE_DENY; } static Cmdoverride *AddOverride(char *msg, iFP cb) { Cmdoverride *ovr = CmdoverrideAdd(MyMod, msg, cb); #ifndef STATIC_LINKING if (ModuleGetError(MyMod) != MODERR_NOERROR || !ovr) #else if (!ovr) #endif { #ifndef STATIC_LINKING config_error("Error replacing command %s when loading module %s: %s", msg, MOD_HEADER(operpasswd).name, ModuleGetErrorStr(MyMod)); #else config_error("Error replacing command %s when loading module %s", msg, MOD_HEADER(operpasswd).name); #endif return NULL; } return ovr; } static Snomask *AddSnomask(char flag, iFP allowed, long *mode) { Snomask *s; *mode = 0; s = SnomaskAdd(MyMod, flag, allowed, mode); #ifndef STATIC_LINKING if ((ModuleGetError(MyMod) != MODERR_NOERROR) || !s) #else if (!s) #endif { #ifndef STATIC_LINKING sendto_realops("Error adding snomask %c: %s", flag, ModuleGetErrorStr(MyMod)); #else sendto_realops("Error adding snomask %c", flag); #endif return NULL; } return s; } #define CHECK_EMPTY(ce, parent) \ if (!(ce)->ce_varname) \ { \ config_error("%s:%i: blank %s item", \ (ce)->ce_fileptr->cf_filename, \ (ce)->ce_varlinenum, (parent)->ce_varname); \ errors++; \ continue; \ } \ if (!(ce)->ce_vardata) \ { \ config_error("%s:%i: %s::%s without value", \ (ce)->ce_fileptr->cf_filename, \ (ce)->ce_varlinenum, \ (parent)->ce_varname, (ce)->ce_varname); \ errors++; \ continue; \ } static int cb_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) { ConfigEntry *cep; int errors = 0; if (type != CONFIG_MAIN) return 0; if (!strcmp(ce->ce_varname, "operpasswd")) { for (cep = ce->ce_entries; cep; cep = cep->ce_next) { CHECK_EMPTY(cep, ce) if (!strcmp(cep->ce_varname, "enable-snomask")) ; else if (!strcmp(cep->ce_varname, "enable-global-notices")) ; else if (!strcmp(cep->ce_varname, "enable-logging")) ; else if (!strcmp(cep->ce_varname, "max-failed-operups")) ; else if (!strcmp(cep->ce_varname, "failop-kill-reason")) ; else { config_error("%s:%i: unknown directive operpasswd::%s", cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname); errors++; } } *errs = errors; return errors ? -1 : 1; } else return 0; } static int cb_conf(ConfigFile *cf, ConfigEntry *ce, int type) { ConfigEntry *cep; if (type != CONFIG_MAIN) return 0; if (!strcmp(ce->ce_varname, "operpasswd")) { for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "enable-snomask")) { if (config_checkval(cep->ce_vardata, CFG_YESNO)) Settings.options |= ENABLE_SNOMASK; } else if (!strcmp(cep->ce_varname, "enable-global-notices")) { if (config_checkval(cep->ce_vardata, CFG_YESNO)) Settings.options |= ENABLE_GLOBAL_NOTICES; } else if (!strcmp(cep->ce_varname, "enable-logging")) { if (config_checkval(cep->ce_vardata, CFG_YESNO)) Settings.options |= ENABLE_LOGGING; } else if (!strcmp(cep->ce_varname, "max-failed-operups")) Settings.max_failed_operups = atoi(cep->ce_vardata); else if (!strcmp(cep->ce_varname, "failop-kill-reason")) { ircstrdup(Settings.failop_kill_reason, cep->ce_vardata); } } #ifndef HOOKTYPE_REHASH_COMPLETE if (!event_added) { /* Not the best solution, but it works. */ EventAdd("operpasswd_loadobjects", 0, 1, LoadObjects, NULL); event_added = 1; } #endif return 1; } return 0; } static int cb_quit(aClient *sptr, char *comment) { FCount *f; if ((f = find_failop_count(sptr))) { DelListItem(f, FailedOperups); MyFree(f); } return 0; } static int ovr_oper(Cmdoverride *ovr, aClient *cptr, aClient *sptr, int parc, char *parv[]) { FCount *f; int ret; /* No need to check if '!MyConnect(sptr)'. */ if (!IsPerson(sptr) || IsAnOper(sptr) || SVSNOOP || parc < 3) return CallCmdoverride(ovr, cptr, sptr, parc, parv); ret = CallCmdoverride(ovr, cptr, sptr, parc, parv); if (!IsAnOper(sptr)) /* Oper-up failed */ { if (Settings.options) { snprintf(textbuf, BUFSIZE, "[FAILOP] From: %s, " "login: %s, password: %s", sptr->name, parv[1], parv[2]); if (Settings.options & ENABLE_SNOMASK) { sendto_snomask(SNO_OPERPASS, "*** Notice -- %s", textbuf); if (Settings.options & ENABLE_GLOBAL_NOTICES) sendto_serv_butone_token(NULL, me.name, MSG_SENDSNO, TOK_SENDSNO, "O :*** Notice -- %s", textbuf); } if (Settings.options & ENABLE_LOGGING) ircd_log(LOG_OPER, "%s", textbuf); } if (Settings.max_failed_operups) { if (!(f = find_failop_count(sptr))) { f = (FCount *) MyMallocEx(sizeof(FCount)); f->cptr = sptr; f->count = 1; AddListItem(f, FailedOperups); } else { f->count++; if (f->count > Settings.max_failed_operups) return exit_client(cptr, sptr, &me, Settings.failop_kill_reason ? Settings.failop_kill_reason : "Too many failed oper-ups"); } } } return ret; }