diff --git a/server/jail.py b/server/jail.py index 31d2a419..00d0b96c 100644 --- a/server/jail.py +++ b/server/jail.py @@ -27,7 +27,6 @@ __license__ = "GPL" import Queue, logging from actions import Actions -from threading import Lock # Gets the instance of the logger. logSys = logging.getLogger("fail2ban.jail") @@ -35,7 +34,6 @@ logSys = logging.getLogger("fail2ban.jail") class Jail: def __init__(self, name, backend = "auto"): - self.__lock = Lock() self.__name = name self.__queue = Queue.Queue() self.__filter = None @@ -61,89 +59,51 @@ class Jail: self.__filter = FilterGamin(self) def setName(self, name): - self.__lock.acquire() self.__name = name - self.__lock.release() def getName(self): - try: - self.__lock.acquire() - return self.__name - finally: - self.__lock.release() + return self.__name def getFilter(self): - try: - self.__lock.acquire() - return self.__filter - finally: - self.__lock.release() + return self.__filter def getAction(self): - try: - self.__lock.acquire() - return self.__action - finally: - self.__lock.release() + return self.__action def putFailTicket(self, ticket): - self.__lock.acquire() self.__queue.put(ticket) - self.__lock.release() def getFailTicket(self): try: - self.__lock.acquire() - try: - return self.__queue.get(False) - except Queue.Empty: - return False - finally: - self.__lock.release() + return self.__queue.get(False) + except Queue.Empty: + return False def start(self): - self.__lock.acquire() self.__filter.start() self.__action.start() - self.__lock.release() def stop(self): - self.__lock.acquire() self.__filter.stop() self.__action.stop() - self.__lock.release() self.__filter.join() self.__action.join() def isActive(self): - try: - self.__lock.acquire() - isActive0 = self.__filter.isActive() - isActive1 = self.__action.isActive() - return isActive0 or isActive1 - finally: - self.__lock.release() + isActive0 = self.__filter.isActive() + isActive1 = self.__action.isActive() + return isActive0 or isActive1 def setIdle(self, value): - self.__lock.acquire() self.__filter.setIdle(value) self.__action.setIdle(value) - self.__lock.release() def getIdle(self): - try: - self.__lock.acquire() - return self.__filter.getIdle() or self.__action.getIdle() - finally: - self.__lock.release() + return self.__filter.getIdle() or self.__action.getIdle() def getStatus(self): - try: - self.__lock.acquire() - fStatus = self.__filter.status() - aStatus = self.__action.status() - ret = [("filter", fStatus), - ("action", aStatus)] - return ret - finally: - self.__lock.release() + fStatus = self.__filter.status() + aStatus = self.__action.status() + ret = [("filter", fStatus), + ("action", aStatus)] + return ret diff --git a/server/jails.py b/server/jails.py index 8ae2ab67..6a327e1c 100644 --- a/server/jails.py +++ b/server/jails.py @@ -28,29 +28,63 @@ __license__ = "GPL" from jail import Jail from threading import Lock +## +# Handles the jails. +# +# This class handles the jails. Creation, deletion or access to a jail must be +# done through this class. This class is thread-safe which is not the case of +# the jail itself, including filter and actions. + class Jails: + ## + # Constructor. + def __init__(self): self.__lock = Lock() self.__jails = dict() + ## + # Adds a jail. + # + # Adds a new jail which should use the given backend. Raises a + # DuplicateJailException if the jail is already defined. + # @param name The name of the jail + # @param backend The backend to use + def add(self, name, backend): - self.__lock.acquire() - if self.__jails.has_key(name): - self.__lock.release() - raise DuplicateJailException(name) - else: - self.__jails[name] = Jail(name, backend) + try: + self.__lock.acquire() + if self.__jails.has_key(name): + raise DuplicateJailException(name) + else: + self.__jails[name] = Jail(name, backend) + finally: self.__lock.release() + ## + # Removes a jail. + # + # Removes the jail name. Raise an UnknownJailException + # if the jail does not exist. + # @param name The name of the jail + def remove(self, name): - self.__lock.acquire() - if self.__jails.has_key(name): - del self.__jails[name] + try: + self.__lock.acquire() + if self.__jails.has_key(name): + del self.__jails[name] + else: + raise UnknownJailException(name) + finally: self.__lock.release() - else: - self.__lock.release() - raise UnknownJailException(name) + + ## + # Returns a jail. + # + # Returns the jail name. Raise an UnknownJailException + # if the jail does not exist. + # @param name The name of the jail def get(self, name): try: @@ -63,6 +97,13 @@ class Jails: finally: self.__lock.release() + ## + # Returns an action class instance. + # + # Returns the action object of the jail name. Raise an + # UnknownJailException if the jail does not exist. + # @param name The name of the jail + def getAction(self, name): try: self.__lock.acquire() @@ -74,6 +115,13 @@ class Jails: finally: self.__lock.release() + ## + # Returns a filter class instance. + # + # Returns the filter object of the jail name. Raise an + # UnknownJailException if the jail does not exist. + # @param name The name of the jail + def getFilter(self, name): try: self.__lock.acquire() @@ -85,6 +133,11 @@ class Jails: finally: self.__lock.release() + ## + # Returns the jails. + # + # Returns a copy of the jails list. + def getAll(self): try: self.__lock.acquire() @@ -92,6 +145,11 @@ class Jails: finally: self.__lock.release() + ## + # Returns the size of the jails. + # + # Returns the number of jails. + def size(self): try: self.__lock.acquire() diff --git a/server/server.py b/server/server.py index af995f54..88099335 100644 --- a/server/server.py +++ b/server/server.py @@ -24,6 +24,7 @@ __date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" +from threading import Lock, RLock from jails import Jails from transmitter import Transmitter from ssocket import SSocket @@ -36,6 +37,8 @@ logSys = logging.getLogger("fail2ban.server") class Server: def __init__(self, daemon = False): + self.__loggingLock = Lock() + self.__lock = RLock() self.__jails = Jails() self.__daemon = daemon self.__transm = Transmitter(self) @@ -91,17 +94,29 @@ class Server: self.__jails.remove(name) def startJail(self, name): - if not self.isActive(name): - self.__jails.get(name).start() + try: + self.__lock.acquire() + if not self.isActive(name): + self.__jails.get(name).start() + finally: + self.__lock.release() def stopJail(self, name): - if self.isActive(name): - self.__jails.get(name).stop() - self.delJail(name) + try: + self.__lock.acquire() + if self.isActive(name): + self.__jails.get(name).stop() + self.delJail(name) + finally: + self.__lock.release() def stopAllJail(self): - for jail in self.__jails.getAll(): - self.stopJail(jail) + try: + self.__lock.acquire() + for jail in self.__jails.getAll(): + self.stopJail(jail) + finally: + self.__lock.release() def isActive(self, name): return self.__jails.get(name).isActive() @@ -225,15 +240,19 @@ class Server: # Status def status(self): - jailList = '' - for jail in self.__jails.getAll(): - jailList += jail + ', ' - length = len(jailList) - if not length == 0: - jailList = jailList[:length-2] - ret = [("Number of jail", self.__jails.size()), - ("Jail list", jailList)] - return ret + try: + self.__lock.acquire() + jailList = '' + for jail in self.__jails.getAll(): + jailList += jail + ', ' + length = len(jailList) + if not length == 0: + jailList = jailList[:length-2] + ret = [("Number of jail", self.__jails.size()), + ("Jail list", jailList)] + return ret + finally: + self.__lock.release() def statusJail(self, name): return self.__jails.get(name).getStatus() @@ -252,17 +271,21 @@ class Server: # @param value the level def setLogLevel(self, value): - self.__logLevel = value - logLevel = logging.DEBUG - if value == 0: - logLevel = logging.FATAL - elif value == 1: - logLevel = logging.ERROR - elif value == 2: - logLevel = logging.WARNING - elif value == 3: - logLevel = logging.INFO - logging.getLogger("fail2ban").setLevel(logLevel) + try: + self.__loggingLock.acquire() + self.__logLevel = value + logLevel = logging.DEBUG + if value == 0: + logLevel = logging.FATAL + elif value == 1: + logLevel = logging.ERROR + elif value == 2: + logLevel = logging.WARNING + elif value == 3: + logLevel = logging.INFO + logging.getLogger("fail2ban").setLevel(logLevel) + finally: + self.__loggingLock.release() ## # Get the logging level. @@ -271,35 +294,47 @@ class Server: # @return the log level def getLogLevel(self): - return self.__logLevel + try: + self.__loggingLock.acquire() + return self.__logLevel + finally: + self.__loggingLock.release() def setLogTarget(self, target): - # Remove previous handler - logging.getLogger("fail2ban").handlers = [] - self.__logTarget = target - if target == "SYSLOG": - hdlr = logging.handlers.SysLogHandler() - elif target == "STDOUT": - hdlr = logging.StreamHandler(sys.stdout) - elif target == "STDERR": - hdlr = logging.StreamHandler(sys.stderr) - else: - # Target should be a file - try: - open(target, "a") - hdlr = logging.FileHandler(target) - except IOError: - logSys.error("Unable to log to " + target) - return False - # set a format which is simpler for console use - formatter = logging.Formatter("%(asctime)s %(name)-16s: %(levelname)-6s %(message)s") - # tell the handler to use this format - hdlr.setFormatter(formatter) - logging.getLogger("fail2ban").addHandler(hdlr) - return True + try: + self.__loggingLock.acquire() + # Remove previous handler + logging.getLogger("fail2ban").handlers = [] + if target == "SYSLOG": + hdlr = logging.handlers.SysLogHandler() + elif target == "STDOUT": + hdlr = logging.StreamHandler(sys.stdout) + elif target == "STDERR": + hdlr = logging.StreamHandler(sys.stderr) + else: + # Target should be a file + try: + open(target, "a") + hdlr = logging.FileHandler(target) + except IOError: + logSys.error("Unable to log to " + target) + return False + self.__logTarget = target + # set a format which is simpler for console use + formatter = logging.Formatter("%(asctime)s %(name)-16s: %(levelname)-6s %(message)s") + # tell the handler to use this format + hdlr.setFormatter(formatter) + logging.getLogger("fail2ban").addHandler(hdlr) + return True + finally: + self.__loggingLock.release() def getLogTarget(self): - return self.__logTarget + try: + self.__loggingLock.acquire() + return self.__logTarget + finally: + self.__loggingLock.release() def __createDaemon(self): """ Detach a process from the controlling terminal and run it in the diff --git a/server/transmitter.py b/server/transmitter.py index 64ad3919..f6051597 100644 --- a/server/transmitter.py +++ b/server/transmitter.py @@ -24,7 +24,6 @@ __date__ = "$Date$" __copyright__ = "Copyright (c) 2004 Cyril Jaquier" __license__ = "GPL" -from threading import Lock import logging, time # Gets the instance of the logger. @@ -32,229 +31,235 @@ logSys = logging.getLogger("fail2ban.comm") class Transmitter: + ## + # Constructor. + # + # @param The server reference + def __init__(self, server): - self.__lock = Lock() self.__server = server - def proceed(self, action): + ## + # Proceeds a command. + # + # Proceeds an incoming command. + # @param command The incoming command + + def proceed(self, command): # Deserialize object + logSys.debug("Command: " + `command`) try: - self.__lock.acquire() - logSys.debug("Action: " + `action`) - try: - ret = self.__actionHandler(action) - ack = 0, ret - except Exception, e: - logSys.warn("Invalid command: " + `action`) - ack = 1, e - return ack - finally: - self.__lock.release() + ret = self.__commandHandler(command) + ack = 0, ret + except Exception, e: + logSys.warn("Invalid command: " + `command`) + ack = 1, e + return ack ## - # Handle an action. + # Handle an command. # # - def __actionHandler(self, action): - if action[0] == "ping": + def __commandHandler(self, command): + if command[0] == "ping": return "pong" - elif action[0] == "add": - name = action[1] + elif command[0] == "add": + name = command[1] if name == "all": raise Exception("Reserved name") try: - backend = action[2] + backend = command[2] except IndexError: backend = "auto" self.__server.addJail(name, backend) return name - elif action[0] == "start": - name = action[1] + elif command[0] == "start": + name = command[1] self.__server.startJail(name) return None - elif action[0] == "stop": - if len(action) == 1: + elif command[0] == "stop": + if len(command) == 1: self.__server.quit() - elif action[1] == "all": + elif command[1] == "all": self.__server.stopAllJail() else: - name = action[1] + name = command[1] self.__server.stopJail(name) return None - elif action[0] == "sleep": - value = action[1] + elif command[0] == "sleep": + value = command[1] time.sleep(int(value)) return None - elif action[0] == "set": - return self.__actionSet(action[1:]) - elif action[0] == "get": - return self.__actionGet(action[1:]) - elif action[0] == "status": - return self.status(action[1:]) + elif command[0] == "set": + return self.__commandSet(command[1:]) + elif command[0] == "get": + return self.__commandGet(command[1:]) + elif command[0] == "status": + return self.status(command[1:]) raise Exception("Invalid command") - def __actionSet(self, action): - name = action[0] + def __commandSet(self, command): + name = command[0] # Logging if name == "loglevel": - value = int(action[1]) + value = int(command[1]) self.__server.setLogLevel(value) return self.__server.getLogLevel() elif name == "logtarget": - value = action[1] + value = command[1] self.__server.setLogTarget(value) return self.__server.getLogTarget() # Jail - elif action[1] == "idle": - if action[2] == "on": + elif command[1] == "idle": + if command[2] == "on": self.__server.setIdleJail(name, True) - elif action[2] == "off": + elif command[2] == "off": self.__server.setIdleJail(name, False) return self.__server.getIdleJail(name) # Filter - elif action[1] == "addignoreip": - value = action[2] + elif command[1] == "addignoreip": + value = command[2] self.__server.addIgnoreIP(name, value) return self.__server.getIgnoreIP(name) - elif action[1] == "delignoreip": - value = action[2] + elif command[1] == "delignoreip": + value = command[2] self.__server.delIgnoreIP(name, value) return self.__server.getIgnoreIP(name) - elif action[1] == "addlogpath": - value = action[2:] + elif command[1] == "addlogpath": + value = command[2:] for path in value: self.__server.addLogPath(name, path) return self.__server.getLogPath(name) - elif action[1] == "dellogpath": - value = action[2] + elif command[1] == "dellogpath": + value = command[2] self.__server.delLogPath(name, value) return self.__server.getLogPath(name) - elif action[1] == "timeregex": - value = action[2] + elif command[1] == "timeregex": + value = command[2] self.__server.setTimeRegex(name, value) return self.__server.getTimeRegex(name) - elif action[1] == "timepattern": - value = action[2] + elif command[1] == "timepattern": + value = command[2] self.__server.setTimePattern(name, value) return self.__server.getTimePattern(name) - elif action[1] == "failregex": - value = action[2] + elif command[1] == "failregex": + value = command[2] self.__server.setFailRegex(name, value) return self.__server.getFailRegex(name) - elif action[1] == "maxtime": - value = action[2] + elif command[1] == "maxtime": + value = command[2] self.__server.setMaxTime(name, int(value)) return self.__server.getMaxTime(name) - elif action[1] == "findtime": - value = action[2] + elif command[1] == "findtime": + value = command[2] self.__server.setFindTime(name, int(value)) return self.__server.getFindTime(name) - elif action[1] == "maxretry": - value = action[2] + elif command[1] == "maxretry": + value = command[2] self.__server.setMaxRetry(name, int(value)) return self.__server.getMaxRetry(name) - # Action - elif action[1] == "bantime": - value = action[2] + # command + elif command[1] == "bantime": + value = command[2] self.__server.setBanTime(name, int(value)) return self.__server.getBanTime(name) - elif action[1] == "addaction": - value = action[2] + elif command[1] == "addaction": + value = command[2] self.__server.addAction(name, value) - return self.__server.getLastAction(name).getName() - elif action[1] == "delaction": + return self.__server.getLastcommand(name).getName() + elif command[1] == "delaction": self.__server.delAction(name, value) return None - elif action[1] == "setcinfo": - act = action[2] - key = action[3] - value = action[4] + elif command[1] == "setcinfo": + act = command[2] + key = command[3] + value = command[4] self.__server.setCInfo(name, act, key, value) return self.__server.getCInfo(name, act, key) - elif action[1] == "delcinfo": - act = action[2] - key = action[3] + elif command[1] == "delcinfo": + act = command[2] + key = command[3] self.__server.delCInfo(name, act, key) return None - elif action[1] == "actionstart": - act = action[2] - value = action[3] + elif command[1] == "actionstart": + act = command[2] + value = command[3] self.__server.setActionStart(name, act, value) return self.__server.getActionStart(name, act) - elif action[1] == "actionstop": - act = action[2] - value = action[3] + elif command[1] == "actionstop": + act = command[2] + value = command[3] self.__server.setActionStop(name, act, value) return self.__server.getActionStop(name, act) - elif action[1] == "actioncheck": - act = action[2] - value = action[3] + elif command[1] == "actioncheck": + act = command[2] + value = command[3] self.__server.setActionCheck(name, act, value) return self.__server.getActionCheck(name, act) - elif action[1] == "actionban": - act = action[2] - value = action[3] + elif command[1] == "actionban": + act = command[2] + value = command[3] self.__server.setActionBan(name, act, value) return self.__server.getActionBan(name, act) - elif action[1] == "actionunban": - act = action[2] - value = action[3] + elif command[1] == "actionunban": + act = command[2] + value = command[3] self.__server.setActionUnban(name, act, value) return self.__server.getActionUnban(name, act) raise Exception("Invalid command (no set action or not yet implemented)") - def __actionGet(self, action): - name = action[0] + def __commandGet(self, command): + name = command[0] # Logging if name == "loglevel": return self.__server.getLogLevel() elif name == "logtarget": return self.__server.getLogTarget() # Filter - elif action[1] == "logpath": + elif command[1] == "logpath": return self.__server.getLogPath(name) - elif action[1] == "ignoreip": + elif command[1] == "ignoreip": return self.__server.getIgnoreIP(name) - elif action[1] == "timeregex": + elif command[1] == "timeregex": return self.__server.getTimeRegex(name) - elif action[1] == "timepattern": + elif command[1] == "timepattern": return self.__server.getTimePattern(name) - elif action[1] == "failregex": + elif command[1] == "failregex": return self.__server.getFailRegex(name) - elif action[1] == "maxtime": + elif command[1] == "maxtime": return self.__server.getMaxTime(name) - elif action[1] == "findtime": + elif command[1] == "findtime": return self.__server.getFindTime(name) - elif action[1] == "maxretry": + elif command[1] == "maxretry": return self.__server.getMaxRetry(name) # Action - elif action[1] == "bantime": + elif command[1] == "bantime": return self.__server.getBanTime(name) - elif action[1] == "addaction": + elif command[1] == "addaction": return self.__server.getLastAction(name).getName() - elif action[1] == "actionstart": - act = action[2] + elif command[1] == "actionstart": + act = command[2] return self.__server.getActionStart(name, act) - elif action[1] == "actionstop": - act = action[2] + elif command[1] == "actionstop": + act = command[2] return self.__server.getActionStop(name, act) - elif action[1] == "actioncheck": - act = action[2] + elif command[1] == "actioncheck": + act = command[2] return self.__server.getActionCheck(name, act) - elif action[1] == "actionban": - act = action[2] + elif command[1] == "actionban": + act = command[2] return self.__server.getActionBan(name, act) - elif action[1] == "actionunban": - act = action[2] + elif command[1] == "actionunban": + act = command[2] return self.__server.getActionUnban(name, act) raise Exception("Invalid command (no get action or not yet implemented)") - def status(self, action): - if len(action) == 0: + def status(self, command): + if len(command) == 0: return self.__server.status() else: - name = action[0] + name = command[0] return self.__server.statusJail(name) raise Exception("Invalid command (no status)") \ No newline at end of file