moved scanner engines to extra projects
authorft
Tue, 18 Feb 2014 15:37:10 +0100
changeset 11dc877520743b
parent 10 b97aad470500
child 12 e1961a1cbb61
moved scanner engines to extra projects
its now possible to configure the scanner engine with the config file
config/OsecFS.cfg
src/OsecFS.py
     1.1 --- a/config/OsecFS.cfg	Mon Dec 09 16:38:20 2013 +0100
     1.2 +++ b/config/OsecFS.cfg	Tue Feb 18 15:37:10 2014 +0100
     1.3 @@ -2,17 +2,32 @@
     1.4  # make sure this file is writeable
     1.5  Logfile: /var/log/fuse_test.log
     1.6  
     1.7 +# DEBUG, INFO, WARNING, ERROR, CRITICAL
     1.8 +LogLevel: debug
     1.9 +
    1.10  # where the files really are on the filesystem 
    1.11  Rootpath: /tmp/root_fuse
    1.12  
    1.13 -# the maximum file size in MB that is scanned
    1.14 -MaxFileSize: 50
    1.15  
    1.16 -# the URL of the local scan server
    1.17 -LocalScanserverURL: http://localhost/virusscan
    1.18 +# path to scanner class
    1.19 +ScannerPath: /path/to/ikarusscanner/src/
    1.20  
    1.21 -# the URL of the remote scan server
    1.22 -RemoteScanserverURL: http://192.168.63.129/virusscan
    1.23 +# scanner module name
    1.24 +ScannerModuleName: IkarusScanner
    1.25 +ScannerClassName: IkarusScanner
    1.26  
    1.27 -# wait time in seconds until a new connection attempt to remote server is made
    1.28 -RetryTimeout: 600
    1.29 +# config file for scanner (path will be in the constructor)
    1.30 +ScannerConfig: /path/to/IkarusScanner.cfg
    1.31 +
    1.32 +
    1.33 +
    1.34 +
    1.35 +# path to scanner class
    1.36 +#ScannerPath: /path/to/clamavscanner/src
    1.37 +
    1.38 +# scanner module name
    1.39 +#ScannerModuleName: ClamAVScanner
    1.40 +#ScannerClassName: ClamAVScanner
    1.41 +
    1.42 +# config file for scanner (path will be in the constructor)
    1.43 +#ScannerConfig: /path/to/ClamAVScanner.cfg
    1.44 \ No newline at end of file
     2.1 --- a/src/OsecFS.py	Mon Dec 09 16:38:20 2013 +0100
     2.2 +++ b/src/OsecFS.py	Tue Feb 18 15:37:10 2014 +0100
     2.3 @@ -12,8 +12,9 @@
     2.4  import errno
     2.5  import time
     2.6  
     2.7 -# ToDo replace with ikarus
     2.8 -#import pyclamav
     2.9 +from importlib import import_module
    2.10 +
    2.11 +
    2.12  import subprocess
    2.13  
    2.14  import urllib3
    2.15 @@ -22,26 +23,23 @@
    2.16  import netaddr
    2.17  
    2.18  
    2.19 -MINOPTS = { "Main" : ["Logfile", "Mountpoint", "Rootpath", "LocalScanserverURL", "RemoteScanserverURL", "ReadOnly"]}
    2.20 +sys.stderr = open('/var/log/osecfs_error.log', 'a+')
    2.21 +
    2.22 +
    2.23 +MINOPTS = { "Main" : ["Logfile", "LogLevel", "Mountpoint", "Rootpath", "ScannerPath", "ScannerModuleName", "ScannerClassName", "ScannerConfig", "ReadOnly"]}
    2.24  
    2.25  CONFIG_NOT_READABLE = "Configfile is not readable"
    2.26  CONFIG_WRONG = "Something is wrong with the config"
    2.27  CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
    2.28 +SCAN_WRONG_RETURN_VALUE = "The return Value of the malware scanner is wrong. Has to be an dictionary"
    2.29 +SCAN_RETURN_VALUE_KEY_MISSING = "The dictionary has to include key \"infected\" (True, False) and \"virusname\" (String)"
    2.30 +VIRUS_FOUND = "Virus found. Access denied"
    2.31 +NOTIFICATION_CRITICAL = "critical"
    2.32 +NOTIFICATION_INFO = "info"
    2.33  LOG = None
    2.34 -LOCAL_SCANSERVER_URL = ""
    2.35 -REMOTE_SCANSERVER_URL = ""
    2.36 -STATUS_CODE_OK = 200
    2.37 -STATUS_CODE_INFECTED = 210
    2.38 -STATUS_CODE_NOT_FOUND = 404
    2.39 +MalwareScanner = None
    2.40  
    2.41  SYSTEM_FILE_COMMAND = "file"
    2.42 -
    2.43 -MAX_SCAN_FILE_SIZE = 50 * 0x100000
    2.44 -SCANSERVER_RETRY_TIMEOUT = 60
    2.45 -
    2.46 -# Global http pool manager used to connect to the scan server
    2.47 -remoteScanserverReachable = True
    2.48 -scanserverTimestamp = 0
    2.49  httpPool = urllib3.PoolManager(num_pools = 1, timeout = 3)
    2.50  
    2.51  def checkMinimumOptions (config):
    2.52 @@ -91,10 +89,14 @@
    2.53  
    2.54      global LOG
    2.55      logfile = config.get("Main", "Logfile")
    2.56 +    
    2.57 +    numeric_level = getattr(logging, config.get("Main", "LogLevel").upper(), None)
    2.58 +    if not isinstance(numeric_level, int):
    2.59 +        raise ValueError('Invalid log level: %s' % loglevel)
    2.60  
    2.61      # ToDo move log level and maybe other things to config file
    2.62      logging.basicConfig(
    2.63 -                        level = logging.DEBUG,
    2.64 +                        level = numeric_level,
    2.65                          format = "%(asctime)s %(name)-12s %(funcName)-15s %(levelname)-8s %(message)s",
    2.66                          datefmt = "%Y-%m-%d %H:%M:%S",
    2.67                          filename = logfile,
    2.68 @@ -117,70 +119,11 @@
    2.69          m = m.replace('w', 'a', 1)
    2.70  
    2.71      return m
    2.72 -    
    2.73 -def contactScanserver(url, fields):
    2.74 -    return httpPool.request_encode_body('POST', url, fields = fields, retries = 0)
    2.75 -    
    2.76  
    2.77 -def scanFileIkarus (path, fileobject):
    2.78 -    global remoteScanserverReachable
    2.79 -    global scanserverTimestamp
    2.80 +def scanFile (path, fileobject):
    2.81 +    LOG.debug ("Scan File \"%s\" with malware Scanner" %(path,) )
    2.82 +    return MalwareScanner.scanFile (path, fileobject)
    2.83  
    2.84 -    infected = False
    2.85 -    LOG.debug ("Scan File: %s" % (path))
    2.86 -
    2.87 -    if (os.fstat(fileobject.fileno()).st_size > MAX_SCAN_FILE_SIZE):
    2.88 -        LOG.info("File max size exceeded. The file is not scanned.")
    2.89 -        return False
    2.90 -
    2.91 -    fields = { 'up_file' : fileobject.read() }
    2.92 -
    2.93 -    if (remoteScanserverReachable == False) and ((scanserverTimestamp + SCANSERVER_RETRY_TIMEOUT) < time.time()):
    2.94 -        remoteScanserverReachable = True
    2.95 -
    2.96 -    if remoteScanserverReachable:
    2.97 -        try:
    2.98 -            response = contactScanserver(REMOTE_SCANSERVER_URL, fields)
    2.99 -            # We should catch socket.error here, but this does not work. Needs checking.
   2.100 -        except:
   2.101 -            LOG.info("Remote scan server unreachable, using local scan server.")
   2.102 -            LOG.info("Next check for remote server in %s seconds." % (SCANSERVER_RETRY_TIMEOUT))
   2.103 -            
   2.104 -            remoteScanserverReachable = False
   2.105 -            scanserverTimestamp = time.time()
   2.106 -
   2.107 -            try:
   2.108 -                response = contactScanserver(LOCAL_SCANSERVER_URL, fields)
   2.109 -            except:
   2.110 -                LOG.error ("Connection to local scan server could not be established.")
   2.111 -                LOG.error ("Exception: %s" %(sys.exc_info()[0]))
   2.112 -                return False
   2.113 -    else:
   2.114 -        try:
   2.115 -            response = contactScanserver(LOCAL_SCANSERVER_URL, fields)
   2.116 -        except:
   2.117 -            LOG.error ("Connection to local scan server could not be established.")
   2.118 -            LOG.error ("Exception: %s" %(sys.exc_info()[0]))
   2.119 -            return False
   2.120 -    
   2.121 -
   2.122 -    if response.status == STATUS_CODE_OK:
   2.123 -        infected = False
   2.124 -    elif response.status == STATUS_CODE_INFECTED:
   2.125 -        # Parse xml for info if desired
   2.126 -        #contentXML = r.content
   2.127 -        #root = ET.fromstring(contentXML)
   2.128 -        #status = root[1][2].text
   2.129 -        infected = True
   2.130 -    else:
   2.131 -        LOG.error ("Connection error to scan server.")
   2.132 -
   2.133 -    if (infected == True):
   2.134 -        LOG.error ("Virus found, denying access.")
   2.135 -    else:
   2.136 -        LOG.debug ("No virus found.")
   2.137 -
   2.138 -    return infected
   2.139  
   2.140  def scanFileClamAV (path):
   2.141      infected = False
   2.142 @@ -312,18 +255,35 @@
   2.143          LOG.debug ("*** open %s %s" % (path, oct (flags)))
   2.144          self.file = os.fdopen (os.open (fixPath (path), flags), flag2mode (flags))
   2.145          self.fd = self.file.fileno ()
   2.146 -
   2.147 -        infected = scanFileIkarus (rootPath(self.__rootpath, path), self.file)
   2.148 -        #infected = scanFileClamAV (rootPath(self.__rootpath, path))
   2.149 -        if (infected == True):
   2.150 +        
   2.151 +        LOG.debug(self.__rootpath)
   2.152 +        LOG.debug(path)
   2.153 +        
   2.154 +        retval = scanFile (rootPath(self.__rootpath, path), self.file)
   2.155 +        
   2.156 +        #if type(retval) is not dict:
   2.157 +        if (isinstance(retval, dict) == False):
   2.158 +            LOG.error(SCAN_WRONG_RETURN_VALUE)
   2.159              self.file.close ()
   2.160 -            sendNotification("critical", "Virus found. Access denied.")
   2.161 +            return -errno.EACCES
   2.162 +        
   2.163 +        if ((retval.has_key("infected") == False) or (retval.has_key("virusname") == False)):
   2.164 +            LOG.error(SCAN_RETURN_VALUE_KEY_MISSING)
   2.165 +            self.file.close ()
   2.166 +            return -errno.EACCES
   2.167 +            
   2.168 +        
   2.169 +        if (retval.get("infected") == True):
   2.170 +            self.file.close ()
   2.171 +            sendNotification(NOTIFICATION_CRITICAL, "%s\nFile: %s\nVirus: %s" %(VIRUS_FOUND, path, retval.get("virusname")))
   2.172 +            LOG.error("%s" %(VIRUS_FOUND,))
   2.173 +            LOG.error("Virus: %s" %(retval.get("virusname"),))
   2.174              return -errno.EACCES
   2.175          
   2.176          whitelisted = whitelistFile (rootPath(self.__rootpath, path))
   2.177          if (whitelisted == False):
   2.178              self.file.close ()
   2.179 -            sendNotification("critical", "File not in whitelist. Access denied.")
   2.180 +            sendNotification(NOTIFICATION_CRITICAL, "File not in whitelist. Access denied.")
   2.181              return -errno.EACCES
   2.182  
   2.183      def read (self, path, length, offset):
   2.184 @@ -417,15 +377,14 @@
   2.185      initLog (config)
   2.186      
   2.187      #sendNotification("Info", "OsecFS started")
   2.188 -
   2.189 -    scanserverTimestamp = time.time()
   2.190 -
   2.191 -    LOCAL_SCANSERVER_URL = config.get("Main", "LocalScanserverURL")
   2.192 -    REMOTE_SCANSERVER_URL = config.get("Main", "RemoteScanserverURL")
   2.193 -    SCANSERVER_RETRY_TIMEOUT = int(config.get("Main", "RetryTimeout"))
   2.194 -
   2.195 -    # Convert file size from MB to byte
   2.196 -    MAX_SCAN_FILE_SIZE = int(config.get("Main", "MaxFileSize")) * 0x100000
   2.197 +    
   2.198 +    # Import the Malware Scanner
   2.199 +    sys.path.append(config.get("Main", "ScannerPath"))
   2.200 +    
   2.201 +    MalwareModule = import_module(config.get("Main", "ScannerModuleName"))
   2.202 +    MalwareClass = getattr(MalwareModule, config.get("Main", "ScannerClassName"))
   2.203 +    
   2.204 +    MalwareScanner = MalwareClass (config.get("Main", "ScannerConfig"));
   2.205      
   2.206      osecfs = OsecFS (config.get ("Main", "Rootpath"))
   2.207      osecfs.flags = 0