1.1 --- a/src/OsecFS.py Wed Nov 27 15:37:26 2013 +0100
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,327 +0,0 @@
1.4 -#!/usr/bin/python
1.5 -
1.6 -from fuse import Fuse
1.7 -import fuse
1.8 -
1.9 -import ConfigParser
1.10 -
1.11 -import sys
1.12 -
1.13 -import logging
1.14 -import os
1.15 -import errno
1.16 -
1.17 -# ToDo replace with ikarus
1.18 -#import pyclamav
1.19 -import subprocess
1.20 -
1.21 -import requests
1.22 -
1.23 -
1.24 -MINOPTS = { "Main" : ["Logfile", "Mountpoint", "Rootpath", "LocalScanserverURL", "RemoteScanserverURL"]}
1.25 -
1.26 -CONFIG_NOT_READABLE = "Configfile is not readable"
1.27 -CONFIG_WRONG = "Something is wrong with the config"
1.28 -CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
1.29 -LOG = None
1.30 -LOCAL_SCANSERVER_URL = ""
1.31 -REMOTE_SCANSERVER_URL = ""
1.32 -STATUS_CODE_OK = 200
1.33 -STATUS_CODE_INFECTED = 210
1.34 -STATUS_CODE_NOT_FOUND = 404
1.35 -
1.36 -SYSTEM_FILE_COMMAND = "file"
1.37 -
1.38 -
1.39 -def checkMinimumOptions (config):
1.40 - for section, options in MINOPTS.iteritems ():
1.41 - for option in options:
1.42 - if (config.has_option(section, option) == False):
1.43 - print (CONFIG_MISSING % (section, option))
1.44 - exit (129)
1.45 -
1.46 -def printUsage ():
1.47 - print ("Usage:")
1.48 - print ("%s configfile" % (sys.argv[0]))
1.49 - exit (128)
1.50 -
1.51 -def loadConfig ():
1.52 - print ("load config")
1.53 -
1.54 - if (len (sys.argv) < 2):
1.55 - printUsage ()
1.56 -
1.57 - configfile = sys.argv[1]
1.58 - config = ConfigParser.SafeConfigParser ()
1.59 -
1.60 - if ((os.path.exists (configfile) == False) or (os.path.isfile (configfile) == False) or (os.access (configfile, os.R_OK) == False)):
1.61 - print (CONFIG_NOT_READABLE)
1.62 - printUsage ()
1.63 -
1.64 - try:
1.65 - config.read (sys.argv[1])
1.66 - except Exception, e:
1.67 - print (CONFIG_WRONG)
1.68 - print ("Error: %s" % (e))
1.69 -
1.70 - checkMinimumOptions (config)
1.71 -
1.72 - return config
1.73 -
1.74 -def initLog (config):
1.75 - print ("init log")
1.76 -
1.77 - global LOG
1.78 - logfile = config.get("Main", "Logfile")
1.79 -
1.80 - # ToDo move log level and maybe other things to config file
1.81 - logging.basicConfig(
1.82 - level = logging.DEBUG,
1.83 - format = "%(asctime)s %(name)-12s %(funcName)-15s %(levelname)-8s %(message)s",
1.84 - datefmt = "%Y-%m-%d %H:%M:%S",
1.85 - filename = logfile,
1.86 - filemode = "a+",
1.87 - )
1.88 - LOG = logging.getLogger("fuse_main")
1.89 -
1.90 -
1.91 -def fixPath (path):
1.92 - return ".%s" % (path)
1.93 -
1.94 -def rootPath (rootpath, path):
1.95 - return "%s%s" % (rootpath, path)
1.96 -
1.97 -def flag2mode (flags):
1.98 - md = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'}
1.99 - m = md[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)]
1.100 -
1.101 - if flags | os.O_APPEND:
1.102 - m = m.replace('w', 'a', 1)
1.103 -
1.104 - return m
1.105 -
1.106 -def scanFileIkarus (path, fileobject):
1.107 - infected = False
1.108 - LOG.debug ("Scan File: %s" % (path))
1.109 -
1.110 - files = {'up_file': (path, fileobject)}
1.111 -
1.112 - try:
1.113 - #TODO: change to remote server
1.114 - r = requests.post(LOCAL_SCANSERVER_URL, files=files)
1.115 - except requests.exceptions.ConnectionError:
1.116 - #LOG.info("Remote scan server unreachable, using local scan server.")
1.117 -
1.118 - # TODO:
1.119 - # Here the local scan server should be contacted.
1.120 - # The requests package does not upload content in the second post request,
1.121 - # so no fallback server can be used right now (bug?)
1.122 - # I did not a find a solution yet, maybe another http package has to be used.
1.123 - # Disabled for now.
1.124 -
1.125 - #try:
1.126 - # r = requests.post(LOCAL_SCANSERVER_URL, files=files)
1.127 - #except requests.exceptions.ConnectionError:
1.128 - # return 2
1.129 - LOG.error ("Connection to scan server could not be established.")
1.130 - return False
1.131 -
1.132 - if r.status_code == STATUS_CODE_OK:
1.133 - infected = False
1.134 - elif r.status_code == STATUS_CODE_INFECTED:
1.135 - # Parse xml for info if desired
1.136 - #contentXML = r.content
1.137 - #root = ET.fromstring(contentXML)
1.138 - #status = root[1][2].text
1.139 - infected = True
1.140 - else:
1.141 - LOG.error ("Connection error to scan server.")
1.142 -
1.143 - if (infected == True):
1.144 - LOG.error ("Virus found, denying access.")
1.145 - else:
1.146 - LOG.debug ("No virus found.")
1.147 -
1.148 - return infected
1.149 -
1.150 -def scanFileClamAV (path):
1.151 - infected = False
1.152 -
1.153 - LOG.debug ("Scan File: %s" % (path))
1.154 -
1.155 - # ToDo implement ikarus
1.156 - result = pyclamav.scanfile (path)
1.157 - LOG.debug ("Result of file \"%s\": %s" % (path, result))
1.158 - if (result[0] != 0):
1.159 - infected = True
1.160 -
1.161 - if (infected == True):
1.162 - LOG.error ("Virus found, deny Access %s" % (result,))
1.163 -
1.164 - return infected
1.165 -
1.166 -def whitelistFile (path):
1.167 - whitelisted = False;
1.168 -
1.169 - LOG.debug ("Execute \"%s\" command on \"%s\"" %(SYSTEM_FILE_COMMAND, path))
1.170 -
1.171 - result = None
1.172 - try:
1.173 - result = subprocess.check_output ([SYSTEM_FILE_COMMAND, path]);
1.174 - # ToDo replace with real whitelist
1.175 - whitelisted = True
1.176 - except Exception as e:
1.177 - LOG.error ("Call returns with an error!")
1.178 - LOG.error (e)
1.179 -
1.180 - LOG.debug ("Type: %s" %(result))
1.181 -
1.182 - return whitelisted
1.183 -
1.184 -class OsecFS (Fuse):
1.185 -
1.186 - __rootpath = None
1.187 -
1.188 - # default fuse init
1.189 - def __init__(self, rootpath, *args, **kw):
1.190 - self.__rootpath = rootpath
1.191 - Fuse.__init__ (self, *args, **kw)
1.192 - LOG.debug ("Init complete.")
1.193 -
1.194 - # defines that our working directory will be the __rootpath
1.195 - def fsinit(self):
1.196 - os.chdir (self.__rootpath)
1.197 -
1.198 - def getattr(self, path):
1.199 - LOG.debug ("*** getattr (%s)" % (fixPath (path)))
1.200 - return os.lstat (fixPath (path));
1.201 -
1.202 - def getdir(self, path):
1.203 - LOG.debug ("*** getdir (%s)" % (path));
1.204 - return os.listdir (fixPath (path))
1.205 -
1.206 - def readdir(self, path, offset):
1.207 - LOG.debug ("*** readdir (%s %s)" % (path, offset));
1.208 - for e in os.listdir (fixPath (path)):
1.209 - yield fuse.Direntry(e)
1.210 -
1.211 - def chmod (self, path, mode):
1.212 - LOG.debug ("*** chmod %s %s" % (path, oct(mode)))
1.213 - os.chmod (fixPath (path), mode)
1.214 -
1.215 - def chown (self, path, uid, gid):
1.216 - LOG.debug ("*** chown %s %s %s" % (path, uid, gid))
1.217 - os.chown (fixPath (path), uid, gid)
1.218 -
1.219 - def link (self, targetPath, linkPath):
1.220 - LOG.debug ("*** link %s %s" % (targetPath, linkPath))
1.221 - os.link (fixPath (targetPath), fixPath (linkPath))
1.222 -
1.223 - def mkdir (self, path, mode):
1.224 - LOG.debug ("*** mkdir %s %s" % (path, oct(mode)))
1.225 - os.mkdir (fixPath (path), mode)
1.226 -
1.227 - def mknod (self, path, mode, dev):
1.228 - LOG.debug ("*** mknod %s %s %s" % (path, oct (mode), dev))
1.229 - os.mknod (fixPath (path), mode, dev)
1.230 -
1.231 - # to implement virus scan
1.232 - def open (self, path, flags):
1.233 - LOG.debug ("*** open %s %s" % (path, oct (flags)))
1.234 - self.file = os.fdopen (os.open (fixPath (path), flags), flag2mode (flags))
1.235 - self.fd = self.file.fileno ()
1.236 -
1.237 - infected = scanFileIkarus (rootPath(self.__rootpath, path), self.file)
1.238 - #infected = scanFileClamAV (rootPath(self.__rootpath, path))
1.239 - if (infected == True):
1.240 - self.file.close ()
1.241 - return -errno.EACCES
1.242 -
1.243 - whitelisted = whitelistFile (rootPath(self.__rootpath, path))
1.244 - if (whitelisted == False):
1.245 - self.file.close ()
1.246 - return -errno.EACCES
1.247 -
1.248 - def read (self, path, length, offset):
1.249 - LOG.debug ("*** read %s %s %s" % (path, length, offset))
1.250 - self.file.seek (offset)
1.251 - return self.file.read (length)
1.252 -
1.253 - def readlink (self, path):
1.254 - LOG.debug ("*** readlink %s" % (path))
1.255 - return os.readlink (fixPath (path))
1.256 -
1.257 - def release (self, path, flags):
1.258 - LOG.debug ("*** release %s %s" % (path, oct (flags)))
1.259 - self.file.close ()
1.260 -
1.261 - def rename (self, oldPath, newPath):
1.262 - LOG.debug ("*** rename %s %s" % (oldPath, newPath))
1.263 - os.rename (fixPath (oldPath), fixPath (newPath))
1.264 -
1.265 - def rmdir (self, path):
1.266 - LOG.debug ("*** rmdir %s" % (path))
1.267 - os.rmdir (fixPath (path))
1.268 -
1.269 - def statfs (self):
1.270 - LOG.debug ("*** statfs")
1.271 - return os.statvfs(".")
1.272 -
1.273 - def symlink (self, targetPath, linkPath):
1.274 - LOG.debug ("*** symlink %s %s" % (targetPath, linkPath))
1.275 - os.symlink (fixPath (targetPath), fixPath (linkPath))
1.276 -
1.277 - def truncate (self, path, length):
1.278 - LOG.debug ("*** truncate %s %s" % (path, length))
1.279 - f = open (fixPath (path), "a")
1.280 - f.truncate (length)
1.281 - f.close ()
1.282 -
1.283 - def unlink (self, path):
1.284 - LOG.debug ("*** unlink %s" % (path))
1.285 - os.unlink (fixPath (path))
1.286 -
1.287 - def utime (self, path, times):
1.288 - LOG.debug ("*** utime %s %s" % (path, times))
1.289 - os.utime (fixPath (path), times)
1.290 -
1.291 - def write (self, path, buf, offset):
1.292 - LOG.debug ("*** write %s %s %s" % (path, buf, offset))
1.293 - self.file.seek (offset)
1.294 - self.file.write (buf)
1.295 - return len (buf)
1.296 -
1.297 - def access (self, path, mode):
1.298 - LOG.debug ("*** access %s %s" % (path, oct (mode)))
1.299 - if not os.access (fixPath (path), mode):
1.300 - return -errno.EACCES
1.301 -
1.302 - def create (self, path, flags, mode):
1.303 - LOG.debug ("*** create %s %s %s %s" % (fixPath (path), oct (flags), oct (mode), flag2mode (flags)))
1.304 - self.file = os.fdopen (os.open (fixPath (path), flags, mode), flag2mode (flags))
1.305 - self.fd = self.file.fileno ()
1.306 -
1.307 -
1.308 -if __name__ == "__main__":
1.309 - # Set api version
1.310 - fuse.fuse_python_api = (0, 2)
1.311 - fuse.feature_assert ('stateful_files', 'has_init')
1.312 -
1.313 - config = loadConfig ()
1.314 - initLog (config)
1.315 -
1.316 - LOCAL_SCANSERVER_URL = config.get("Main", "LocalScanserverURL")
1.317 - REMOTE_SCANSERVER_URL = config.get("Main", "RemoteScanserverURL")
1.318 -
1.319 - osecfs = OsecFS (config.get ("Main", "Rootpath"))
1.320 - osecfs.flags = 0
1.321 - osecfs.multithreaded = 0
1.322 -
1.323 - # osecfs.parser.add_option (mountopt=config.get("Main", "Mountpoint"),
1.324 - # metavar="PATH",
1.325 - # default=config.get("Main", "Rootpath"),
1.326 - # help="mirror filesystem from under PATH [default: %default]")
1.327 - # osecfs.parse(values=osecfs, errex=1)
1.328 -
1.329 - fuse_args = [sys.argv[0], config.get ("Main", "Mountpoint")];
1.330 - osecfs.main (fuse_args)