diff --git a/server/datedetector.py b/server/datedetector.py index 5b52326e..1513bd48 100644 --- a/server/datedetector.py +++ b/server/datedetector.py @@ -24,26 +24,45 @@ __date__ = "$Date: 2006-09-04 21:19:58 +0200 (Mon, 04 Sep 2006) $" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" -import time +import time, logging from datetemplate import DateTemplate +from datestrptime import DateStrptime +from datetai64n import DateTai64n +from dateepoch import DateEpoch +from threading import Lock + +# Gets the instance of the logger. +logSys = logging.getLogger("fail2ban.filter.datedetector") class DateDetector: def __init__(self): + self.lock = Lock() self.templates = list() self.defTemplate = DateTemplate() def addDefaultTemplate(self): - template = DateTemplate() - template.setRegex("\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}") + # standard + template = DateStrptime() + template.setName("Month Day Hour:Minute:Second") + template.setRegex("^\S{3}\s{1,2}\d{1,2} \d{2}:\d{2}:\d{2}") template.setPattern("%b %d %H:%M:%S") self.templates.append(template) - - template = DateTemplate() - template.setRegex("\S{3} \S{3} \d{2} \d{2}:\d{2}:\d{2} \d{4}") + # asctime + template = DateStrptime() + template.setName("Weekday Month Day Hour:Minute:Second Year") + template.setRegex("^\S{3} \S{3} \d{2} \d{2}:\d{2}:\d{2} \d{4}") template.setPattern("%a %b %d %H:%M:%S %Y") self.templates.append(template) + # TAI64N + template = DateTai64n() + template.setName("TAI64N") + self.templates.append(template) + # Epoch + template = DateEpoch() + template.setName("Epoch") + self.templates.append(template) def setDefaultRegex(self, value): self.defTemplate.setRegex(value) @@ -57,18 +76,17 @@ class DateDetector: def getDefaultPattern(self): return self.defTemplate.getPattern() - #def addTemplate(self, template): - # self.templates.append(template) - def matchTime(self, line): if self.defTemplate.isValid(): return self.defTemplate.matchDate(line) else: - # TODO Should be called from outside. Add locking + self.lock.acquire() for template in self.templates: match = template.matchDate(line) if match <> None: + self.lock.release() return match + self.lock.release() return None def getTime(self, line): @@ -79,15 +97,18 @@ class DateDetector: except ValueError: return None else: - # TODO Should be called from outside. Add locking - self.sortTemplate() + self.lock.acquire() for template in self.templates: try: date = template.getDate(line) + if date == None: + continue template.incHits() + self.lock.release() return date except ValueError: pass + self.lock.release() return None def getUnixTime(self, line): @@ -97,7 +118,13 @@ class DateDetector: else: return time.mktime(date) + ## + # Sort the template lists using the hits score. This method is not called + # in this object and thus should be called from time to time. + def sortTemplate(self): + self.lock.acquire() + logSys.debug("Sorting the template list") self.templates.sort(cmp = lambda x, y: cmp(x.getHits(), y.getHits()), reverse=True) - \ No newline at end of file + self.lock.release() diff --git a/server/dateepoch.py b/server/dateepoch.py new file mode 100644 index 00000000..ef5cb2ba --- /dev/null +++ b/server/dateepoch.py @@ -0,0 +1,44 @@ +# This file is part of Fail2Ban. +# +# Fail2Ban 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; either version 2 of the License, or +# (at your option) any later version. +# +# Fail2Ban 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 Fail2Ban; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# Author: Cyril Jaquier +# +# $Revision: 321 $ + +__author__ = "Cyril Jaquier" +__version__ = "$Revision: 321 $" +__date__ = "$Date: 2006-09-04 21:19:58 +0200 (Mon, 04 Sep 2006) $" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier" +__license__ = "GPL" + +import re, time + +from datetemplate import DateTemplate + +class DateEpoch(DateTemplate): + + def __init__(self): + DateTemplate.__init__(self) + # We already know the format for TAI64N + self.setRegex("^\d{10}(\.\d{6})?") + + def getDate(self, line): + date = None + dateMatch = self.matchDate(line) + if dateMatch: + # extract part of format which represents seconds since epoch + date = list(time.gmtime(float(dateMatch.group()))) + return date diff --git a/server/datestrptime.py b/server/datestrptime.py new file mode 100644 index 00000000..ef115722 --- /dev/null +++ b/server/datestrptime.py @@ -0,0 +1,49 @@ +# This file is part of Fail2Ban. +# +# Fail2Ban 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; either version 2 of the License, or +# (at your option) any later version. +# +# Fail2Ban 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 Fail2Ban; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# Author: Cyril Jaquier +# +# $Revision: 321 $ + +__author__ = "Cyril Jaquier" +__version__ = "$Revision: 321 $" +__date__ = "$Date: 2006-09-04 21:19:58 +0200 (Mon, 04 Sep 2006) $" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier" +__license__ = "GPL" + +import re, time + +from datetemplate import DateTemplate + +class DateStrptime(DateTemplate): + + def __init__(self): + DateTemplate.__init__(self) + + def getDate(self, line): + date = None + dateMatch = self.matchDate(line) + if dateMatch: + date = list(time.strptime(dateMatch.group(), self.pattern)) + if date[0] < 2000: + # There is probably no year field in the logs + date[0] = time.gmtime()[0] + # Bug fix for #1241756 + # If the date is greater than the current time, we suppose + # that the log is not from this year but from the year before + if time.mktime(date) > time.time(): + date[0] -= 1 + return date diff --git a/server/datetai64n.py b/server/datetai64n.py new file mode 100644 index 00000000..064b6643 --- /dev/null +++ b/server/datetai64n.py @@ -0,0 +1,46 @@ +# This file is part of Fail2Ban. +# +# Fail2Ban 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; either version 2 of the License, or +# (at your option) any later version. +# +# Fail2Ban 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 Fail2Ban; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +# Author: Cyril Jaquier +# +# $Revision: 321 $ + +__author__ = "Cyril Jaquier" +__version__ = "$Revision: 321 $" +__date__ = "$Date: 2006-09-04 21:19:58 +0200 (Mon, 04 Sep 2006) $" +__copyright__ = "Copyright (c) 2004 Cyril Jaquier" +__license__ = "GPL" + +import re, time + +from datetemplate import DateTemplate + +class DateTai64n(DateTemplate): + + def __init__(self): + DateTemplate.__init__(self) + # We already know the format for TAI64N + self.setRegex("@[0-9a-f]{24}") + + def getDate(self, line): + date = None + dateMatch = self.matchDate(line) + if dateMatch: + # extract part of format which represents seconds since epoch + value = dateMatch.group() + seconds_since_epoch = value[2:17] + date = list(time.gmtime(int(seconds_since_epoch, 16))) + return date diff --git a/server/datetemplate.py b/server/datetemplate.py index 5944adca..eae730ee 100644 --- a/server/datetemplate.py +++ b/server/datetemplate.py @@ -29,12 +29,21 @@ import re, time class DateTemplate: def __init__(self): + self.name = "" self.regex = "" + self.cRegex = None self.pattern = "" self.hits = 0 + def setName(self, name): + self.name = name + + def getName(self): + return self.name + def setRegex(self, regex): self.regex = regex + self.cRegex = re.compile(regex) def getRegex(self): return self.regex @@ -55,20 +64,8 @@ class DateTemplate: return self.hits def matchDate(self, line): - dateMatch = re.search(self.regex, line) + dateMatch = self.cRegex.search(line) return dateMatch def getDate(self, line): - date = None - dateMatch = self.matchDate(line) - if dateMatch: - date = list(time.strptime(dateMatch.group(), self.pattern)) - if date[0] < 2000: - # There is probably no year field in the logs - date[0] = time.gmtime()[0] - # Bug fix for #1241756 - # If the date is greater than the current time, we suppose - # that the log is not from this year but from the year before - if time.mktime(date) > time.time(): - date[0] -= 1 - return date + raise Exception("matchDate() is abstract") diff --git a/server/filter.py b/server/filter.py index 9f81bc8d..a4bbe3db 100644 --- a/server/filter.py +++ b/server/filter.py @@ -217,6 +217,7 @@ class Filter(JailThread): if not self.isIdle: if self.isModified(): self.getFailures() + self.dateDetector.sortTemplate() try: ticket = self.failManager.toBan() self.jail.putFailTicket(ticket)