src/OsecFS.py
author ft
Tue, 29 Oct 2013 15:13:44 +0100
changeset 0 e840b60f3ea3
child 1 1f61fe50ab10
permissions -rwxr-xr-x
initial commit
     1 #!/usr/bin/python
     2 
     3 from fuse import Fuse
     4 import fuse
     5 
     6 import ConfigParser
     7 
     8 import sys
     9 
    10 import logging
    11 import os
    12 import errno
    13 
    14 # ToDo replace with ikarus
    15 import pyclamav
    16 import subprocess
    17 
    18 MINOPTS = { "Main" : ["Logfile", "Mountpoint", "Rootpath"]}
    19 
    20 CONFIG_NOT_READABLE = "Configfile is not readable"
    21 CONFIG_WRONG = "Something is wrong with the config"
    22 CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
    23 LOG = None
    24 
    25 SYSTEM_FILE_COMMAND = "file"
    26 
    27 
    28 def checkMinimumOptions (config):
    29     for section, options in MINOPTS.iteritems ():
    30         for option in options:
    31             if (config.has_option(section, option) == False):
    32                 print (CONFIG_MISSING % (section, option))
    33                 exit (129)
    34 
    35 def printUsage ():
    36     print ("Usage:")
    37     print ("%s configfile" % (sys.argv[0]))
    38     exit (128)
    39 
    40 def loadConfig ():
    41     print ("load config")
    42 
    43     if (len (sys.argv) < 2):
    44         printUsage ()
    45 
    46     configfile = sys.argv[1]
    47     config = ConfigParser.SafeConfigParser ()
    48 
    49     if ((os.path.exists (configfile) == False) or (os.path.isfile (configfile) == False) or (os.access (configfile, os.R_OK) == False)):
    50         print (CONFIG_NOT_READABLE)
    51         printUsage ()
    52 
    53     try:
    54         config.read (sys.argv[1])
    55     except Exception, e:
    56         print (CONFIG_WRONG)
    57         print ("Error: %s" % (e))
    58 
    59     checkMinimumOptions (config)
    60 
    61     return config
    62 
    63 def initLog (config):
    64     print ("init log")
    65 
    66     global LOG
    67     logfile = config.get("Main", "Logfile")
    68 
    69     # ToDo move log level and maybe other things to config file
    70     logging.basicConfig(
    71                         level = logging.DEBUG,
    72                         format = "%(asctime)s %(name)-12s %(funcName)-15s %(levelname)-8s %(message)s",
    73                         datefmt = "%Y-%m-%d %H:%M:%S",
    74                         filename = logfile,
    75                         filemode = "a+",
    76     )
    77     LOG = logging.getLogger("fuse_main")
    78 
    79 
    80 def fixPath (path):
    81     return ".%s" % (path)
    82 
    83 def rootPath (rootpath, path):
    84     return "%s%s" % (rootpath, path)
    85 
    86 def flag2mode (flags):
    87     md = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'}
    88     m = md[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)]
    89 
    90     if flags | os.O_APPEND:
    91         m = m.replace('w', 'a', 1)
    92 
    93     return m
    94 
    95 
    96 
    97 def scanFile (path):
    98     infected = False
    99 
   100     LOG.debug ("Scan File: %s" % (path))
   101 
   102     # ToDo implement ikarus
   103     result = pyclamav.scanfile (path)
   104     LOG.debug ("Result of file \"%s\": %s" % (path, result))
   105     if (result[0] != 0):
   106         infected = True
   107 
   108     if (infected == True):
   109         LOG.error ("Virus found deny Access %s" % (result,))
   110 
   111     return infected
   112 
   113 def whitelistFile (path):
   114     whitelisted = False;
   115 
   116     LOG.debug ("Execute \"%s\" command on \"%s\"" %(SYSTEM_FILE_COMMAND, path))
   117     
   118     result = None
   119     try:
   120         result = subprocess.check_output ([SYSTEM_FILE_COMMAND, path]);
   121         # ToDo replace with real whitelist
   122         whitelisted = True
   123     except Exception as e:
   124         LOG.error ("Call returns with an error!")
   125         LOG.error (e)
   126 
   127     LOG.debug ("Type: %s" %(result))
   128 
   129     return whitelisted
   130 
   131 class OsecFS (Fuse):
   132 
   133     __rootpath = None
   134 
   135     # default fuse init
   136     def __init__(self, rootpath, *args, **kw):
   137         self.__rootpath = rootpath
   138         Fuse.__init__ (self, *args, **kw)
   139         LOG.debug ("Init complete.")
   140 
   141     # defines that our working directory will be the __rootpath
   142     def fsinit(self):
   143         os.chdir (self.__rootpath)
   144 
   145     def getattr(self, path):
   146         LOG.debug ("*** getattr (%s)" % (fixPath (path)))
   147         return os.lstat (fixPath (path));
   148 
   149     def getdir(self, path):
   150         LOG.debug ("*** getdir (%s)" % (path));
   151         return os.listdir (fixPath (path))
   152 
   153     def readdir(self, path, offset):
   154         LOG.debug ("*** readdir (%s %s)" % (path, offset));
   155         for e in os.listdir (fixPath (path)):
   156             yield fuse.Direntry(e)
   157 
   158     def chmod (self, path, mode):
   159         LOG.debug ("*** chmod %s %s" % (path, oct(mode)))
   160         os.chmod (fixPath (path), mode)
   161 
   162     def chown (self, path, uid, gid):
   163         LOG.debug ("*** chown %s %s %s" % (path, uid, gid))
   164         os.chown (fixPath (path), uid, gid)
   165 
   166     def link (self, targetPath, linkPath):
   167         LOG.debug ("*** link %s %s" % (targetPath, linkPath))
   168         os.link (fixPath (targetPath), fixPath (linkPath))
   169 
   170     def mkdir (self, path, mode):
   171         LOG.debug ("*** mkdir %s %s" % (path, oct(mode)))
   172         os.mkdir (fixPath (path), mode)
   173 
   174     def mknod (self, path, mode, dev):
   175         LOG.debug ("*** mknod %s %s %s" % (path, oct (mode), dev))
   176         os.mknod (fixPath (path), mode, dev)
   177 
   178     # to implement virus scan
   179     def open (self, path, flags):
   180         LOG.debug ("*** open %s %s" % (path, oct (flags)))
   181         self.file = os.fdopen (os.open (fixPath (path), flags), flag2mode (flags))
   182         self.fd = self.file.fileno ()
   183 
   184         infected = scanFile (rootPath(self.__rootpath, path))
   185         if (infected == True):
   186             self.file.close ()
   187             return -errno.EACCES
   188         
   189         whitelisted = whitelistFile (rootPath(self.__rootpath, path))
   190         if (whitelisted == False):
   191             self.file.close ()
   192             return -errno.EACCES
   193 
   194     def read (self, path, length, offset):
   195         LOG.debug ("*** read %s %s %s" % (path, length, offset))
   196         self.file.seek (offset)
   197         return self.file.read (length)
   198 
   199     def readlink (self, path):
   200         LOG.debug ("*** readlink %s" % (path))
   201         return os.readlink (fixPath (path))
   202 
   203     def release (self, path, flags):
   204         LOG.debug ("*** release %s %s" % (path, oct (flags)))
   205         self.file.close ()
   206 
   207     def rename (self, oldPath, newPath):
   208         LOG.debug ("*** rename %s %s" % (oldPath, newPath))
   209         os.rename (fixPath (oldPath), fixPath (newPath))
   210 
   211     def rmdir (self, path):
   212         LOG.debug ("*** rmdir %s" % (path))
   213         os.rmdir (fixPath (path))
   214 
   215     def statfs (self):
   216         LOG.debug ("*** statfs")
   217         return os.statvfs(".")
   218 
   219     def symlink (self, targetPath, linkPath):
   220         LOG.debug ("*** symlink %s %s" % (targetPath, linkPath))
   221         os.symlink (fixPath (targetPath), fixPath (linkPath))
   222 
   223     def truncate (self, path, length):
   224         LOG.debug ("*** truncate %s %s" % (path, length))
   225         f = open (fixPath (path), "a")
   226         f.truncate (length)
   227         f.close ()
   228 
   229     def unlink (self, path):
   230         LOG.debug ("*** unlink %s" % (path))
   231         os.unlink (fixPath (path))
   232 
   233     def utime (self, path, times):
   234         LOG.debug ("*** utime %s %s" % (path, times))
   235         os.utime (fixPath (path), times)
   236 
   237     def write (self, path, buf, offset):
   238         LOG.debug ("*** write %s %s %s" % (path, buf, offset))
   239         self.file.seek (offset)
   240         self.file.write (buf)
   241         return len (buf)
   242 
   243     def access (self, path, mode):
   244         LOG.debug ("*** access %s %s" % (path, oct (mode)))
   245         if not os.access (fixPath (path), mode):
   246             return -errno.EACCES
   247 
   248     def create (self, path, flags, mode):
   249         LOG.debug ("*** create %s %s %s %s" % (fixPath (path), oct (flags), oct (mode), flag2mode (flags)))
   250         self.file = os.fdopen (os.open (fixPath (path), flags, mode), flag2mode (flags))
   251         self.fd = self.file.fileno ()
   252 
   253 
   254 if __name__ == "__main__":
   255     # Set api version
   256     fuse.fuse_python_api = (0, 2)
   257     fuse.feature_assert ('stateful_files', 'has_init')
   258 
   259     config = loadConfig ()
   260     initLog (config)
   261 
   262     osecfs = OsecFS (config.get ("Main", "Rootpath"))
   263     osecfs.flags = 0
   264     osecfs.multithreaded = 0
   265 
   266     # osecfs.parser.add_option (mountopt=config.get("Main", "Mountpoint"),
   267     #                      metavar="PATH",
   268     #                      default=config.get("Main", "Rootpath"),
   269     #                      help="mirror filesystem from under PATH [default: %default]")
   270     # osecfs.parse(values=osecfs, errex=1)
   271 
   272     fuse_args = [sys.argv[0], config.get ("Main", "Mountpoint")];
   273     osecfs.main (fuse_args)