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