# HG changeset patch # User ft # Date 1383056024 -3600 # Node ID e840b60f3ea36e54b4eb1d7fea96e44d7bb00ba0 initial commit diff -r 000000000000 -r e840b60f3ea3 config/OsecFS.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config/OsecFS.cfg Tue Oct 29 15:13:44 2013 +0100 @@ -0,0 +1,9 @@ +[Main] +# make sure this file is writeable +Logfile: /var/log/fuse_test.log + +# the place that the user will see (you can't access virusfiles here) +Mountpoint: /tmp/virtual_fuse + +# where the files really are on the filesystem +Rootpath: /tmp/root_fuse \ No newline at end of file diff -r 000000000000 -r e840b60f3ea3 src/OsecFS.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/OsecFS.py Tue Oct 29 15:13:44 2013 +0100 @@ -0,0 +1,273 @@ +#!/usr/bin/python + +from fuse import Fuse +import fuse + +import ConfigParser + +import sys + +import logging +import os +import errno + +# ToDo replace with ikarus +import pyclamav +import subprocess + +MINOPTS = { "Main" : ["Logfile", "Mountpoint", "Rootpath"]} + +CONFIG_NOT_READABLE = "Configfile is not readable" +CONFIG_WRONG = "Something is wrong with the config" +CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing" +LOG = None + +SYSTEM_FILE_COMMAND = "file" + + +def checkMinimumOptions (config): + for section, options in MINOPTS.iteritems (): + for option in options: + if (config.has_option(section, option) == False): + print (CONFIG_MISSING % (section, option)) + exit (129) + +def printUsage (): + print ("Usage:") + print ("%s configfile" % (sys.argv[0])) + exit (128) + +def loadConfig (): + print ("load config") + + if (len (sys.argv) < 2): + printUsage () + + configfile = sys.argv[1] + config = ConfigParser.SafeConfigParser () + + if ((os.path.exists (configfile) == False) or (os.path.isfile (configfile) == False) or (os.access (configfile, os.R_OK) == False)): + print (CONFIG_NOT_READABLE) + printUsage () + + try: + config.read (sys.argv[1]) + except Exception, e: + print (CONFIG_WRONG) + print ("Error: %s" % (e)) + + checkMinimumOptions (config) + + return config + +def initLog (config): + print ("init log") + + global LOG + logfile = config.get("Main", "Logfile") + + # ToDo move log level and maybe other things to config file + logging.basicConfig( + level = logging.DEBUG, + format = "%(asctime)s %(name)-12s %(funcName)-15s %(levelname)-8s %(message)s", + datefmt = "%Y-%m-%d %H:%M:%S", + filename = logfile, + filemode = "a+", + ) + LOG = logging.getLogger("fuse_main") + + +def fixPath (path): + return ".%s" % (path) + +def rootPath (rootpath, path): + return "%s%s" % (rootpath, path) + +def flag2mode (flags): + md = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'} + m = md[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)] + + if flags | os.O_APPEND: + m = m.replace('w', 'a', 1) + + return m + + + +def scanFile (path): + infected = False + + LOG.debug ("Scan File: %s" % (path)) + + # ToDo implement ikarus + result = pyclamav.scanfile (path) + LOG.debug ("Result of file \"%s\": %s" % (path, result)) + if (result[0] != 0): + infected = True + + if (infected == True): + LOG.error ("Virus found deny Access %s" % (result,)) + + return infected + +def whitelistFile (path): + whitelisted = False; + + LOG.debug ("Execute \"%s\" command on \"%s\"" %(SYSTEM_FILE_COMMAND, path)) + + result = None + try: + result = subprocess.check_output ([SYSTEM_FILE_COMMAND, path]); + # ToDo replace with real whitelist + whitelisted = True + except Exception as e: + LOG.error ("Call returns with an error!") + LOG.error (e) + + LOG.debug ("Type: %s" %(result)) + + return whitelisted + +class OsecFS (Fuse): + + __rootpath = None + + # default fuse init + def __init__(self, rootpath, *args, **kw): + self.__rootpath = rootpath + Fuse.__init__ (self, *args, **kw) + LOG.debug ("Init complete.") + + # defines that our working directory will be the __rootpath + def fsinit(self): + os.chdir (self.__rootpath) + + def getattr(self, path): + LOG.debug ("*** getattr (%s)" % (fixPath (path))) + return os.lstat (fixPath (path)); + + def getdir(self, path): + LOG.debug ("*** getdir (%s)" % (path)); + return os.listdir (fixPath (path)) + + def readdir(self, path, offset): + LOG.debug ("*** readdir (%s %s)" % (path, offset)); + for e in os.listdir (fixPath (path)): + yield fuse.Direntry(e) + + def chmod (self, path, mode): + LOG.debug ("*** chmod %s %s" % (path, oct(mode))) + os.chmod (fixPath (path), mode) + + def chown (self, path, uid, gid): + LOG.debug ("*** chown %s %s %s" % (path, uid, gid)) + os.chown (fixPath (path), uid, gid) + + def link (self, targetPath, linkPath): + LOG.debug ("*** link %s %s" % (targetPath, linkPath)) + os.link (fixPath (targetPath), fixPath (linkPath)) + + def mkdir (self, path, mode): + LOG.debug ("*** mkdir %s %s" % (path, oct(mode))) + os.mkdir (fixPath (path), mode) + + def mknod (self, path, mode, dev): + LOG.debug ("*** mknod %s %s %s" % (path, oct (mode), dev)) + os.mknod (fixPath (path), mode, dev) + + # to implement virus scan + def open (self, path, flags): + LOG.debug ("*** open %s %s" % (path, oct (flags))) + self.file = os.fdopen (os.open (fixPath (path), flags), flag2mode (flags)) + self.fd = self.file.fileno () + + infected = scanFile (rootPath(self.__rootpath, path)) + if (infected == True): + self.file.close () + return -errno.EACCES + + whitelisted = whitelistFile (rootPath(self.__rootpath, path)) + if (whitelisted == False): + self.file.close () + return -errno.EACCES + + def read (self, path, length, offset): + LOG.debug ("*** read %s %s %s" % (path, length, offset)) + self.file.seek (offset) + return self.file.read (length) + + def readlink (self, path): + LOG.debug ("*** readlink %s" % (path)) + return os.readlink (fixPath (path)) + + def release (self, path, flags): + LOG.debug ("*** release %s %s" % (path, oct (flags))) + self.file.close () + + def rename (self, oldPath, newPath): + LOG.debug ("*** rename %s %s" % (oldPath, newPath)) + os.rename (fixPath (oldPath), fixPath (newPath)) + + def rmdir (self, path): + LOG.debug ("*** rmdir %s" % (path)) + os.rmdir (fixPath (path)) + + def statfs (self): + LOG.debug ("*** statfs") + return os.statvfs(".") + + def symlink (self, targetPath, linkPath): + LOG.debug ("*** symlink %s %s" % (targetPath, linkPath)) + os.symlink (fixPath (targetPath), fixPath (linkPath)) + + def truncate (self, path, length): + LOG.debug ("*** truncate %s %s" % (path, length)) + f = open (fixPath (path), "a") + f.truncate (length) + f.close () + + def unlink (self, path): + LOG.debug ("*** unlink %s" % (path)) + os.unlink (fixPath (path)) + + def utime (self, path, times): + LOG.debug ("*** utime %s %s" % (path, times)) + os.utime (fixPath (path), times) + + def write (self, path, buf, offset): + LOG.debug ("*** write %s %s %s" % (path, buf, offset)) + self.file.seek (offset) + self.file.write (buf) + return len (buf) + + def access (self, path, mode): + LOG.debug ("*** access %s %s" % (path, oct (mode))) + if not os.access (fixPath (path), mode): + return -errno.EACCES + + def create (self, path, flags, mode): + LOG.debug ("*** create %s %s %s %s" % (fixPath (path), oct (flags), oct (mode), flag2mode (flags))) + self.file = os.fdopen (os.open (fixPath (path), flags, mode), flag2mode (flags)) + self.fd = self.file.fileno () + + +if __name__ == "__main__": + # Set api version + fuse.fuse_python_api = (0, 2) + fuse.feature_assert ('stateful_files', 'has_init') + + config = loadConfig () + initLog (config) + + osecfs = OsecFS (config.get ("Main", "Rootpath")) + osecfs.flags = 0 + osecfs.multithreaded = 0 + + # osecfs.parser.add_option (mountopt=config.get("Main", "Mountpoint"), + # metavar="PATH", + # default=config.get("Main", "Rootpath"), + # help="mirror filesystem from under PATH [default: %default]") + # osecfs.parse(values=osecfs, errex=1) + + fuse_args = [sys.argv[0], config.get ("Main", "Mountpoint")]; + osecfs.main (fuse_args)