# # logactio - simple framework for doing configured action on certain # log file events # # Copyright 2012 - 2015 David Sommerseth # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation, version 2 # of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # For the avoidance of doubt the "preferred form" of this code is one which # is in an open unpatent encumbered format. Where cryptographic key signing # forms part of the process of creating an executable the information # including keys needed to generate an equivalently functional executable # are deemed to be part of the source code. # import hashlib, time class ThresholdType_Rule(object): def __init__(self, params): if "threshold" not in params: raise ValueError("Missing required 'threshold' parameter") self.__threshold = int(params["threshold"]) self.__timeframe = params["timeframe"] and int(params["timeframe"]) or None self.__ratelimit = params["ratelimit"] and int(params["ratelimit"]) or None self.__lastseen = 0 self.__lastsent = 0 self.__currentcount = 0 def CheckThreshold(self, alert, notused_regexmatch): now = int(time.time()) self.__currentcount += 1 ret = (self.__threshold == 0 or ((self.__currentcount % self.__threshold == 0) and (self.__timeframe is None or now <= (self.__lastseen + self.__timeframe))) and (self.__ratelimit is None or now > (self.__lastsent + self.__ratelimit))) if (self.__timeframe and (self.__lastseen > 0) and (now >= (self.__lastseen + self.__timeframe))): # If the time-frame have timed out, reset it self.__lastseen = 0 else: self.__lastseen = now return ret def ClearTimeTrackers(self, notused_regexmatch): self.__lastseen = 0 self.__lastsent = 0 def GetThreshold(self): return self.__threshold def GetCurrentCount(self, rgmatch): return self.__currentcount class ThresholdType_Exact(object): def __init__(self, params): self.__matchedgroups = {} self.__threshold = int(params["threshold"]) self.__timeframe = params["timeframe"] and int(params["timeframe"]) or None self.__ratelimit = params["ratelimit"] and int(params["ratelimit"]) or None def __gen_hash(self, regexmatch): v = "|".join(regexmatch) return hashlib.sha384(v.encode('utf-8')).hexdigest() def CheckThreshold(self, alert, regexmatch): now = int(time.time()) ret = False # If threshold is 0 or 1, then we process all records if self.__threshold < 2: return True # Check if we have a regexmatch on this from earlier checks rghash = self.__gen_hash(regexmatch) if rghash in self.__matchedgroups: lastevent = self.__matchedgroups[rghash] lastevent["count"] += 1 ret = ((lastevent["count"] % self.__threshold == 0) and (self.__timeframe is None or now <= (lastevent["lastseen"] + self.__timeframe)) and (self.__ratelimit is None or now > (lastevent["lastsent"] + self.__ratelimit))) try: if (self.__timeframe and 'lastseen' in lastevent and (lastevent["lastseen"] > 0) and (now >= (lastevent["lastseen"] + self.__timeframe))): # If the time-frame have timed out, reset it self.__lastseen = 0 else: self.__lastseen = now except KeyError as e: self.__lastseen = now else: # Not seen before, register it as a new one self.__matchedgroups[rghash] = { "count": 1, "lastseen": now, "lastsent": 0 } ret = (1 % self.__threshold == 0) return ret def ClearTimeTrackers(self, regexmatch): rghash = self.__gen_hash(regexmatch) if rghash in self.__matchedgroups: self.__matchedgroups[rghash]["lasteen"] = 0 self.__matchedgroups[rghash]["lastsent"] = 0 def GetThreshold(self): return self.__threshold def GetCurrentCount(self, regexmatch): rghash = self.__gen_hash(regexmatch) if rghash in self.__matchedgroups: return self.__matchedgroups[rghash]["count"] else: return 0 class ThresholdWatch(object): WATCHTYPE_RULE = 1 WATCHTYPE_EXACT = 2 def __init__(self, watchtype, params): self.__watchtype = watchtype if watchtype == self.WATCHTYPE_RULE: self.__thresholdtype = ThresholdType_Rule(params) elif watchtype == self.WATCHTYPE_EXACT: self.__thresholdtype = ThresholdType_Exact(params) else: raise ValueError("Invalid watchtype parameter") def CheckThreshold(self, alert, regexmatch): return self.__thresholdtype.CheckThreshold(alert, regexmatch) def ClearTimeTrackers(self, regexmatch): self.__thresholdtype.ClearTimeTrackers(regexmatch) def GetThreshold(self): return self.__thresholdtype.GetThreshold() def GetCurrentCount(self, regexmatch): return self.__thresholdtype.GetCurrentCount(regexmatch)