src/OsecFS.py
changeset 11 dc877520743b
parent 10 b97aad470500
child 12 e1961a1cbb61
     1.1 --- a/src/OsecFS.py	Mon Dec 09 16:38:20 2013 +0100
     1.2 +++ b/src/OsecFS.py	Tue Feb 18 15:37:10 2014 +0100
     1.3 @@ -12,8 +12,9 @@
     1.4  import errno
     1.5  import time
     1.6  
     1.7 -# ToDo replace with ikarus
     1.8 -#import pyclamav
     1.9 +from importlib import import_module
    1.10 +
    1.11 +
    1.12  import subprocess
    1.13  
    1.14  import urllib3
    1.15 @@ -22,26 +23,23 @@
    1.16  import netaddr
    1.17  
    1.18  
    1.19 -MINOPTS = { "Main" : ["Logfile", "Mountpoint", "Rootpath", "LocalScanserverURL", "RemoteScanserverURL", "ReadOnly"]}
    1.20 +sys.stderr = open('/var/log/osecfs_error.log', 'a+')
    1.21 +
    1.22 +
    1.23 +MINOPTS = { "Main" : ["Logfile", "LogLevel", "Mountpoint", "Rootpath", "ScannerPath", "ScannerModuleName", "ScannerClassName", "ScannerConfig", "ReadOnly"]}
    1.24  
    1.25  CONFIG_NOT_READABLE = "Configfile is not readable"
    1.26  CONFIG_WRONG = "Something is wrong with the config"
    1.27  CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
    1.28 +SCAN_WRONG_RETURN_VALUE = "The return Value of the malware scanner is wrong. Has to be an dictionary"
    1.29 +SCAN_RETURN_VALUE_KEY_MISSING = "The dictionary has to include key \"infected\" (True, False) and \"virusname\" (String)"
    1.30 +VIRUS_FOUND = "Virus found. Access denied"
    1.31 +NOTIFICATION_CRITICAL = "critical"
    1.32 +NOTIFICATION_INFO = "info"
    1.33  LOG = None
    1.34 -LOCAL_SCANSERVER_URL = ""
    1.35 -REMOTE_SCANSERVER_URL = ""
    1.36 -STATUS_CODE_OK = 200
    1.37 -STATUS_CODE_INFECTED = 210
    1.38 -STATUS_CODE_NOT_FOUND = 404
    1.39 +MalwareScanner = None
    1.40  
    1.41  SYSTEM_FILE_COMMAND = "file"
    1.42 -
    1.43 -MAX_SCAN_FILE_SIZE = 50 * 0x100000
    1.44 -SCANSERVER_RETRY_TIMEOUT = 60
    1.45 -
    1.46 -# Global http pool manager used to connect to the scan server
    1.47 -remoteScanserverReachable = True
    1.48 -scanserverTimestamp = 0
    1.49  httpPool = urllib3.PoolManager(num_pools = 1, timeout = 3)
    1.50  
    1.51  def checkMinimumOptions (config):
    1.52 @@ -91,10 +89,14 @@
    1.53  
    1.54      global LOG
    1.55      logfile = config.get("Main", "Logfile")
    1.56 +    
    1.57 +    numeric_level = getattr(logging, config.get("Main", "LogLevel").upper(), None)
    1.58 +    if not isinstance(numeric_level, int):
    1.59 +        raise ValueError('Invalid log level: %s' % loglevel)
    1.60  
    1.61      # ToDo move log level and maybe other things to config file
    1.62      logging.basicConfig(
    1.63 -                        level = logging.DEBUG,
    1.64 +                        level = numeric_level,
    1.65                          format = "%(asctime)s %(name)-12s %(funcName)-15s %(levelname)-8s %(message)s",
    1.66                          datefmt = "%Y-%m-%d %H:%M:%S",
    1.67                          filename = logfile,
    1.68 @@ -117,70 +119,11 @@
    1.69          m = m.replace('w', 'a', 1)
    1.70  
    1.71      return m
    1.72 -    
    1.73 -def contactScanserver(url, fields):
    1.74 -    return httpPool.request_encode_body('POST', url, fields = fields, retries = 0)
    1.75 -    
    1.76  
    1.77 -def scanFileIkarus (path, fileobject):
    1.78 -    global remoteScanserverReachable
    1.79 -    global scanserverTimestamp
    1.80 +def scanFile (path, fileobject):
    1.81 +    LOG.debug ("Scan File \"%s\" with malware Scanner" %(path,) )
    1.82 +    return MalwareScanner.scanFile (path, fileobject)
    1.83  
    1.84 -    infected = False
    1.85 -    LOG.debug ("Scan File: %s" % (path))
    1.86 -
    1.87 -    if (os.fstat(fileobject.fileno()).st_size > MAX_SCAN_FILE_SIZE):
    1.88 -        LOG.info("File max size exceeded. The file is not scanned.")
    1.89 -        return False
    1.90 -
    1.91 -    fields = { 'up_file' : fileobject.read() }
    1.92 -
    1.93 -    if (remoteScanserverReachable == False) and ((scanserverTimestamp + SCANSERVER_RETRY_TIMEOUT) < time.time()):
    1.94 -        remoteScanserverReachable = True
    1.95 -
    1.96 -    if remoteScanserverReachable:
    1.97 -        try:
    1.98 -            response = contactScanserver(REMOTE_SCANSERVER_URL, fields)
    1.99 -            # We should catch socket.error here, but this does not work. Needs checking.
   1.100 -        except:
   1.101 -            LOG.info("Remote scan server unreachable, using local scan server.")
   1.102 -            LOG.info("Next check for remote server in %s seconds." % (SCANSERVER_RETRY_TIMEOUT))
   1.103 -            
   1.104 -            remoteScanserverReachable = False
   1.105 -            scanserverTimestamp = time.time()
   1.106 -
   1.107 -            try:
   1.108 -                response = contactScanserver(LOCAL_SCANSERVER_URL, fields)
   1.109 -            except:
   1.110 -                LOG.error ("Connection to local scan server could not be established.")
   1.111 -                LOG.error ("Exception: %s" %(sys.exc_info()[0]))
   1.112 -                return False
   1.113 -    else:
   1.114 -        try:
   1.115 -            response = contactScanserver(LOCAL_SCANSERVER_URL, fields)
   1.116 -        except:
   1.117 -            LOG.error ("Connection to local scan server could not be established.")
   1.118 -            LOG.error ("Exception: %s" %(sys.exc_info()[0]))
   1.119 -            return False
   1.120 -    
   1.121 -
   1.122 -    if response.status == STATUS_CODE_OK:
   1.123 -        infected = False
   1.124 -    elif response.status == STATUS_CODE_INFECTED:
   1.125 -        # Parse xml for info if desired
   1.126 -        #contentXML = r.content
   1.127 -        #root = ET.fromstring(contentXML)
   1.128 -        #status = root[1][2].text
   1.129 -        infected = True
   1.130 -    else:
   1.131 -        LOG.error ("Connection error to scan server.")
   1.132 -
   1.133 -    if (infected == True):
   1.134 -        LOG.error ("Virus found, denying access.")
   1.135 -    else:
   1.136 -        LOG.debug ("No virus found.")
   1.137 -
   1.138 -    return infected
   1.139  
   1.140  def scanFileClamAV (path):
   1.141      infected = False
   1.142 @@ -312,18 +255,35 @@
   1.143          LOG.debug ("*** open %s %s" % (path, oct (flags)))
   1.144          self.file = os.fdopen (os.open (fixPath (path), flags), flag2mode (flags))
   1.145          self.fd = self.file.fileno ()
   1.146 -
   1.147 -        infected = scanFileIkarus (rootPath(self.__rootpath, path), self.file)
   1.148 -        #infected = scanFileClamAV (rootPath(self.__rootpath, path))
   1.149 -        if (infected == True):
   1.150 +        
   1.151 +        LOG.debug(self.__rootpath)
   1.152 +        LOG.debug(path)
   1.153 +        
   1.154 +        retval = scanFile (rootPath(self.__rootpath, path), self.file)
   1.155 +        
   1.156 +        #if type(retval) is not dict:
   1.157 +        if (isinstance(retval, dict) == False):
   1.158 +            LOG.error(SCAN_WRONG_RETURN_VALUE)
   1.159              self.file.close ()
   1.160 -            sendNotification("critical", "Virus found. Access denied.")
   1.161 +            return -errno.EACCES
   1.162 +        
   1.163 +        if ((retval.has_key("infected") == False) or (retval.has_key("virusname") == False)):
   1.164 +            LOG.error(SCAN_RETURN_VALUE_KEY_MISSING)
   1.165 +            self.file.close ()
   1.166 +            return -errno.EACCES
   1.167 +            
   1.168 +        
   1.169 +        if (retval.get("infected") == True):
   1.170 +            self.file.close ()
   1.171 +            sendNotification(NOTIFICATION_CRITICAL, "%s\nFile: %s\nVirus: %s" %(VIRUS_FOUND, path, retval.get("virusname")))
   1.172 +            LOG.error("%s" %(VIRUS_FOUND,))
   1.173 +            LOG.error("Virus: %s" %(retval.get("virusname"),))
   1.174              return -errno.EACCES
   1.175          
   1.176          whitelisted = whitelistFile (rootPath(self.__rootpath, path))
   1.177          if (whitelisted == False):
   1.178              self.file.close ()
   1.179 -            sendNotification("critical", "File not in whitelist. Access denied.")
   1.180 +            sendNotification(NOTIFICATION_CRITICAL, "File not in whitelist. Access denied.")
   1.181              return -errno.EACCES
   1.182  
   1.183      def read (self, path, length, offset):
   1.184 @@ -417,15 +377,14 @@
   1.185      initLog (config)
   1.186      
   1.187      #sendNotification("Info", "OsecFS started")
   1.188 -
   1.189 -    scanserverTimestamp = time.time()
   1.190 -
   1.191 -    LOCAL_SCANSERVER_URL = config.get("Main", "LocalScanserverURL")
   1.192 -    REMOTE_SCANSERVER_URL = config.get("Main", "RemoteScanserverURL")
   1.193 -    SCANSERVER_RETRY_TIMEOUT = int(config.get("Main", "RetryTimeout"))
   1.194 -
   1.195 -    # Convert file size from MB to byte
   1.196 -    MAX_SCAN_FILE_SIZE = int(config.get("Main", "MaxFileSize")) * 0x100000
   1.197 +    
   1.198 +    # Import the Malware Scanner
   1.199 +    sys.path.append(config.get("Main", "ScannerPath"))
   1.200 +    
   1.201 +    MalwareModule = import_module(config.get("Main", "ScannerModuleName"))
   1.202 +    MalwareClass = getattr(MalwareModule, config.get("Main", "ScannerClassName"))
   1.203 +    
   1.204 +    MalwareScanner = MalwareClass (config.get("Main", "ScannerConfig"));
   1.205      
   1.206      osecfs = OsecFS (config.get ("Main", "Rootpath"))
   1.207      osecfs.flags = 0