1.1 --- a/ikarusscanner/IkarusScanner.py Tue Nov 04 16:35:33 2014 +0100
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,185 +0,0 @@
1.4 -#!/usr/bin/python
1.5 -
1.6 -# ------------------------------------------------------------
1.7 -# opensecurity package file
1.8 -#
1.9 -# Autor: Karlberger Christoph <Karlberger.C@ikarus.at>
1.10 -# X-Net Services GmbH <office@x-net.at>
1.11 -#
1.12 -# Copyright 2013-2014 X-Net and AIT Austrian Institute of Technology
1.13 -#
1.14 -# IKARUS Security Software GmbH
1.15 -# Blechturmgasse 11
1.16 -# 1050 Wien
1.17 -# AUSTRIA
1.18 -# http://www.ikarussecurity.com
1.19 -#
1.20 -# X-Net Technologies GmbH
1.21 -# Elisabethstrasse 1
1.22 -# 4020 Linz
1.23 -# AUSTRIA
1.24 -# https://www.x-net.at
1.25 -#
1.26 -# AIT Austrian Institute of Technology
1.27 -# Donau City Strasse 1
1.28 -# 1220 Wien
1.29 -# AUSTRIA
1.30 -# http://www.ait.ac.at
1.31 -#
1.32 -#
1.33 -# Licensed under the Apache License, Version 2.0 (the "License");
1.34 -# you may not use this file except in compliance with the License.
1.35 -# You may obtain a copy of the License at
1.36 -#
1.37 -# http://www.apache.org/licenses/LICENSE-2.0
1.38 -#
1.39 -# Unless required by applicable law or agreed to in writing, software
1.40 -# distributed under the License is distributed on an "AS IS" BASIS,
1.41 -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1.42 -# See the License for the specific language governing permissions and
1.43 -# limitations under the License.
1.44 -# ------------------------------------------------------------
1.45 -
1.46 -import ConfigParser
1.47 -
1.48 -import sys
1.49 -
1.50 -import logging
1.51 -import os
1.52 -import errno
1.53 -import time
1.54 -
1.55 -import urllib3
1.56 -import xml.etree.ElementTree as ET
1.57 -
1.58 -class IkarusScanner:
1.59 -
1.60 - # User the existing logger instance
1.61 - __LOG = logging.getLogger("IkarusScanner")
1.62 -
1.63 - __MINOPTS = { "Main" : ["LocalScanserverURL", "RemoteScanserverURL", "MaxFileSize", "RetryTimeout"]}
1.64 - __CONFIG_NOT_READABLE = "Configfile is not readable"
1.65 - __CONFIG_WRONG = "Something is wrong with the config"
1.66 - __CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
1.67 - __LOCAL_SCANSERVER_URL = ""
1.68 - __REMOTE_SCANSERVER_URL = ""
1.69 - __STATUS_CODE_OK = 200
1.70 - __STATUS_CODE_INFECTED = 210
1.71 - __STATUS_CODE_NOT_FOUND = 404
1.72 - __MAX_SCAN_FILE_SIZE = 50 * 0x100000
1.73 - __SCANSERVER_RETRY_TIMEOUT = 60
1.74 -
1.75 - # Global http pool manager used to connect to the scan server
1.76 - __remoteScanserverReachable = True
1.77 - __scanserverTimestamp = 0
1.78 - __httpPool = urllib3.PoolManager(num_pools = 1, timeout = 3)
1.79 -
1.80 - def __init__ (self, scanner_config_path):
1.81 - config = self.loadConfig (scanner_config_path)
1.82 -
1.83 - self.__scanserverTimestamp = time.time()
1.84 -
1.85 - self.__LOCAL_SCANSERVER_URL = config.get("Main", "LocalScanserverURL")
1.86 - self.__REMOTE_SCANSERVER_URL = config.get("Main", "RemoteScanserverURL")
1.87 - self.__SCANSERVER_RETRY_TIMEOUT = int(config.get("Main", "RetryTimeout"))
1.88 -
1.89 - # Convert file size from MB to byte
1.90 - self.__MAX_SCAN_FILE_SIZE = int(config.get("Main", "MaxFileSize")) * 0x100000
1.91 -
1.92 -
1.93 - def checkMinimumOptions (self, config):
1.94 - for section, options in self.__MINOPTS.iteritems ():
1.95 - for option in options:
1.96 - if (config.has_option(section, option) == False):
1.97 - self.__LOG.error (self.__CONFIG_MISSING % (section, option))
1.98 - exit (129)
1.99 -
1.100 - def loadConfig (self, scanner_config_path):
1.101 -
1.102 - configfile = scanner_config_path
1.103 - config = ConfigParser.SafeConfigParser ()
1.104 -
1.105 - if ((os.path.exists (scanner_config_path) == False) or (os.path.isfile (scanner_config_path) == False) or (os.access (scanner_config_path, os.R_OK) == False)):
1.106 - self.__LOG.error(self.__CONFIG_NOT_READABLE);
1.107 - raise SystemError(self.__CONFIG_NOT_READABLE)
1.108 -
1.109 - try:
1.110 - config.read (scanner_config_path)
1.111 - except Exception, e:
1.112 - self.__LOG.error("Error: %s" % (e));
1.113 - raise SystemError("Error: %s" % (e))
1.114 -
1.115 - self.checkMinimumOptions (config)
1.116 -
1.117 - return config
1.118 -
1.119 - def contactScanserver(self, url, fields):
1.120 - self.__LOG.debug("Contacting server %s" % url)
1.121 - return self.__httpPool.request_encode_body('POST', url, fields = fields, retries = 0)
1.122 -
1.123 - def scanFile (self, path, fileobject):
1.124 - return self.scanFileIkarus (path, fileobject)
1.125 -
1.126 - def scanFileIkarus (self, path, fileobject):
1.127 - retval = { "infected" : False, "virusname" : "Unknown" }
1.128 - self.__LOG.debug ("Scan File: %s" % (path))
1.129 -
1.130 - if (os.fstat(fileobject.fileno()).st_size > self.__MAX_SCAN_FILE_SIZE):
1.131 - self.__LOG.info("File max size exceeded. The file is not scanned.")
1.132 - retval["infected"] = False
1.133 - retval["virusname"] = "File is to big to be scanned."
1.134 - return retval
1.135 -
1.136 - fields = { 'up_file' : fileobject.read() }
1.137 -
1.138 - if (self.__remoteScanserverReachable == False) and ((self.__scanserverTimestamp + self.__SCANSERVER_RETRY_TIMEOUT) < time.time()):
1.139 - self.__remoteScanserverReachable = True
1.140 -
1.141 - if self.__remoteScanserverReachable:
1.142 - try:
1.143 - response = self.contactScanserver(self.__REMOTE_SCANSERVER_URL, fields)
1.144 - # We should catch socket.error here, but this does not work. Needs checking.
1.145 - except:
1.146 - self.__LOG.info("Remote scan server unreachable, using local scan server.")
1.147 - self.__LOG.debug("Exception: %s: %s" % (sys.exc_info()[0], sys.exc_info()[1]))
1.148 - self.__LOG.info("Next check for remote server in %s seconds." % (self.__SCANSERVER_RETRY_TIMEOUT))
1.149 -
1.150 - self.__remoteScanserverReachable = False
1.151 - self.__scanserverTimestamp = time.time()
1.152 -
1.153 - try:
1.154 - response = self.contactScanserver(self.__LOCAL_SCANSERVER_URL, fields)
1.155 - except:
1.156 - self.__LOG.error ("Connection to local scan server could not be established.")
1.157 - self.__LOG.debug ("Exception: %s" % (sys.exc_info()[0]))
1.158 - return retval
1.159 - else:
1.160 - try:
1.161 - response = self.contactScanserver(self.__LOCAL_SCANSERVER_URL, fields)
1.162 - except:
1.163 - self.__LOG.error ("Connection to local scan server could not be established.")
1.164 - self.__LOG.error ("Exception: %s" %(sys.exc_info()[0]))
1.165 - return retval
1.166 -
1.167 -
1.168 - if response.status == self.__STATUS_CODE_OK:
1.169 - retval["infected"] = False
1.170 - elif response.status == self.__STATUS_CODE_INFECTED:
1.171 - # Parse xml for info
1.172 - root = ET.fromstring(response.data)
1.173 -
1.174 - # this should be done in a more generic way
1.175 - retval["virusname"] = root[1][3][0].text
1.176 - retval["infected"] = True
1.177 - else:
1.178 - self.__LOG.error ("Connection error to scan server.")
1.179 -
1.180 - if (retval["infected"] == True):
1.181 - self.__LOG.error ("Virus found, denying access.")
1.182 - else:
1.183 - self.__LOG.debug ("No virus found.")
1.184 -
1.185 - return retval
1.186 -
1.187 -
1.188 -