src/OsecFS.py
changeset 0 e840b60f3ea3
child 1 1f61fe50ab10
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/OsecFS.py	Tue Oct 29 15:13:44 2013 +0100
     1.3 @@ -0,0 +1,273 @@
     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 +MINOPTS = { "Main" : ["Logfile", "Mountpoint", "Rootpath"]}
    1.22 +
    1.23 +CONFIG_NOT_READABLE = "Configfile is not readable"
    1.24 +CONFIG_WRONG = "Something is wrong with the config"
    1.25 +CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
    1.26 +LOG = None
    1.27 +
    1.28 +SYSTEM_FILE_COMMAND = "file"
    1.29 +
    1.30 +
    1.31 +def checkMinimumOptions (config):
    1.32 +    for section, options in MINOPTS.iteritems ():
    1.33 +        for option in options:
    1.34 +            if (config.has_option(section, option) == False):
    1.35 +                print (CONFIG_MISSING % (section, option))
    1.36 +                exit (129)
    1.37 +
    1.38 +def printUsage ():
    1.39 +    print ("Usage:")
    1.40 +    print ("%s configfile" % (sys.argv[0]))
    1.41 +    exit (128)
    1.42 +
    1.43 +def loadConfig ():
    1.44 +    print ("load config")
    1.45 +
    1.46 +    if (len (sys.argv) < 2):
    1.47 +        printUsage ()
    1.48 +
    1.49 +    configfile = sys.argv[1]
    1.50 +    config = ConfigParser.SafeConfigParser ()
    1.51 +
    1.52 +    if ((os.path.exists (configfile) == False) or (os.path.isfile (configfile) == False) or (os.access (configfile, os.R_OK) == False)):
    1.53 +        print (CONFIG_NOT_READABLE)
    1.54 +        printUsage ()
    1.55 +
    1.56 +    try:
    1.57 +        config.read (sys.argv[1])
    1.58 +    except Exception, e:
    1.59 +        print (CONFIG_WRONG)
    1.60 +        print ("Error: %s" % (e))
    1.61 +
    1.62 +    checkMinimumOptions (config)
    1.63 +
    1.64 +    return config
    1.65 +
    1.66 +def initLog (config):
    1.67 +    print ("init log")
    1.68 +
    1.69 +    global LOG
    1.70 +    logfile = config.get("Main", "Logfile")
    1.71 +
    1.72 +    # ToDo move log level and maybe other things to config file
    1.73 +    logging.basicConfig(
    1.74 +                        level = logging.DEBUG,
    1.75 +                        format = "%(asctime)s %(name)-12s %(funcName)-15s %(levelname)-8s %(message)s",
    1.76 +                        datefmt = "%Y-%m-%d %H:%M:%S",
    1.77 +                        filename = logfile,
    1.78 +                        filemode = "a+",
    1.79 +    )
    1.80 +    LOG = logging.getLogger("fuse_main")
    1.81 +
    1.82 +
    1.83 +def fixPath (path):
    1.84 +    return ".%s" % (path)
    1.85 +
    1.86 +def rootPath (rootpath, path):
    1.87 +    return "%s%s" % (rootpath, path)
    1.88 +
    1.89 +def flag2mode (flags):
    1.90 +    md = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'}
    1.91 +    m = md[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)]
    1.92 +
    1.93 +    if flags | os.O_APPEND:
    1.94 +        m = m.replace('w', 'a', 1)
    1.95 +
    1.96 +    return m
    1.97 +
    1.98 +
    1.99 +
   1.100 +def scanFile (path):
   1.101 +    infected = False
   1.102 +
   1.103 +    LOG.debug ("Scan File: %s" % (path))
   1.104 +
   1.105 +    # ToDo implement ikarus
   1.106 +    result = pyclamav.scanfile (path)
   1.107 +    LOG.debug ("Result of file \"%s\": %s" % (path, result))
   1.108 +    if (result[0] != 0):
   1.109 +        infected = True
   1.110 +
   1.111 +    if (infected == True):
   1.112 +        LOG.error ("Virus found deny Access %s" % (result,))
   1.113 +
   1.114 +    return infected
   1.115 +
   1.116 +def whitelistFile (path):
   1.117 +    whitelisted = False;
   1.118 +
   1.119 +    LOG.debug ("Execute \"%s\" command on \"%s\"" %(SYSTEM_FILE_COMMAND, path))
   1.120 +    
   1.121 +    result = None
   1.122 +    try:
   1.123 +        result = subprocess.check_output ([SYSTEM_FILE_COMMAND, path]);
   1.124 +        # ToDo replace with real whitelist
   1.125 +        whitelisted = True
   1.126 +    except Exception as e:
   1.127 +        LOG.error ("Call returns with an error!")
   1.128 +        LOG.error (e)
   1.129 +
   1.130 +    LOG.debug ("Type: %s" %(result))
   1.131 +
   1.132 +    return whitelisted
   1.133 +
   1.134 +class OsecFS (Fuse):
   1.135 +
   1.136 +    __rootpath = None
   1.137 +
   1.138 +    # default fuse init
   1.139 +    def __init__(self, rootpath, *args, **kw):
   1.140 +        self.__rootpath = rootpath
   1.141 +        Fuse.__init__ (self, *args, **kw)
   1.142 +        LOG.debug ("Init complete.")
   1.143 +
   1.144 +    # defines that our working directory will be the __rootpath
   1.145 +    def fsinit(self):
   1.146 +        os.chdir (self.__rootpath)
   1.147 +
   1.148 +    def getattr(self, path):
   1.149 +        LOG.debug ("*** getattr (%s)" % (fixPath (path)))
   1.150 +        return os.lstat (fixPath (path));
   1.151 +
   1.152 +    def getdir(self, path):
   1.153 +        LOG.debug ("*** getdir (%s)" % (path));
   1.154 +        return os.listdir (fixPath (path))
   1.155 +
   1.156 +    def readdir(self, path, offset):
   1.157 +        LOG.debug ("*** readdir (%s %s)" % (path, offset));
   1.158 +        for e in os.listdir (fixPath (path)):
   1.159 +            yield fuse.Direntry(e)
   1.160 +
   1.161 +    def chmod (self, path, mode):
   1.162 +        LOG.debug ("*** chmod %s %s" % (path, oct(mode)))
   1.163 +        os.chmod (fixPath (path), mode)
   1.164 +
   1.165 +    def chown (self, path, uid, gid):
   1.166 +        LOG.debug ("*** chown %s %s %s" % (path, uid, gid))
   1.167 +        os.chown (fixPath (path), uid, gid)
   1.168 +
   1.169 +    def link (self, targetPath, linkPath):
   1.170 +        LOG.debug ("*** link %s %s" % (targetPath, linkPath))
   1.171 +        os.link (fixPath (targetPath), fixPath (linkPath))
   1.172 +
   1.173 +    def mkdir (self, path, mode):
   1.174 +        LOG.debug ("*** mkdir %s %s" % (path, oct(mode)))
   1.175 +        os.mkdir (fixPath (path), mode)
   1.176 +
   1.177 +    def mknod (self, path, mode, dev):
   1.178 +        LOG.debug ("*** mknod %s %s %s" % (path, oct (mode), dev))
   1.179 +        os.mknod (fixPath (path), mode, dev)
   1.180 +
   1.181 +    # to implement virus scan
   1.182 +    def open (self, path, flags):
   1.183 +        LOG.debug ("*** open %s %s" % (path, oct (flags)))
   1.184 +        self.file = os.fdopen (os.open (fixPath (path), flags), flag2mode (flags))
   1.185 +        self.fd = self.file.fileno ()
   1.186 +
   1.187 +        infected = scanFile (rootPath(self.__rootpath, path))
   1.188 +        if (infected == True):
   1.189 +            self.file.close ()
   1.190 +            return -errno.EACCES
   1.191 +        
   1.192 +        whitelisted = whitelistFile (rootPath(self.__rootpath, path))
   1.193 +        if (whitelisted == False):
   1.194 +            self.file.close ()
   1.195 +            return -errno.EACCES
   1.196 +
   1.197 +    def read (self, path, length, offset):
   1.198 +        LOG.debug ("*** read %s %s %s" % (path, length, offset))
   1.199 +        self.file.seek (offset)
   1.200 +        return self.file.read (length)
   1.201 +
   1.202 +    def readlink (self, path):
   1.203 +        LOG.debug ("*** readlink %s" % (path))
   1.204 +        return os.readlink (fixPath (path))
   1.205 +
   1.206 +    def release (self, path, flags):
   1.207 +        LOG.debug ("*** release %s %s" % (path, oct (flags)))
   1.208 +        self.file.close ()
   1.209 +
   1.210 +    def rename (self, oldPath, newPath):
   1.211 +        LOG.debug ("*** rename %s %s" % (oldPath, newPath))
   1.212 +        os.rename (fixPath (oldPath), fixPath (newPath))
   1.213 +
   1.214 +    def rmdir (self, path):
   1.215 +        LOG.debug ("*** rmdir %s" % (path))
   1.216 +        os.rmdir (fixPath (path))
   1.217 +
   1.218 +    def statfs (self):
   1.219 +        LOG.debug ("*** statfs")
   1.220 +        return os.statvfs(".")
   1.221 +
   1.222 +    def symlink (self, targetPath, linkPath):
   1.223 +        LOG.debug ("*** symlink %s %s" % (targetPath, linkPath))
   1.224 +        os.symlink (fixPath (targetPath), fixPath (linkPath))
   1.225 +
   1.226 +    def truncate (self, path, length):
   1.227 +        LOG.debug ("*** truncate %s %s" % (path, length))
   1.228 +        f = open (fixPath (path), "a")
   1.229 +        f.truncate (length)
   1.230 +        f.close ()
   1.231 +
   1.232 +    def unlink (self, path):
   1.233 +        LOG.debug ("*** unlink %s" % (path))
   1.234 +        os.unlink (fixPath (path))
   1.235 +
   1.236 +    def utime (self, path, times):
   1.237 +        LOG.debug ("*** utime %s %s" % (path, times))
   1.238 +        os.utime (fixPath (path), times)
   1.239 +
   1.240 +    def write (self, path, buf, offset):
   1.241 +        LOG.debug ("*** write %s %s %s" % (path, buf, offset))
   1.242 +        self.file.seek (offset)
   1.243 +        self.file.write (buf)
   1.244 +        return len (buf)
   1.245 +
   1.246 +    def access (self, path, mode):
   1.247 +        LOG.debug ("*** access %s %s" % (path, oct (mode)))
   1.248 +        if not os.access (fixPath (path), mode):
   1.249 +            return -errno.EACCES
   1.250 +
   1.251 +    def create (self, path, flags, mode):
   1.252 +        LOG.debug ("*** create %s %s %s %s" % (fixPath (path), oct (flags), oct (mode), flag2mode (flags)))
   1.253 +        self.file = os.fdopen (os.open (fixPath (path), flags, mode), flag2mode (flags))
   1.254 +        self.fd = self.file.fileno ()
   1.255 +
   1.256 +
   1.257 +if __name__ == "__main__":
   1.258 +    # Set api version
   1.259 +    fuse.fuse_python_api = (0, 2)
   1.260 +    fuse.feature_assert ('stateful_files', 'has_init')
   1.261 +
   1.262 +    config = loadConfig ()
   1.263 +    initLog (config)
   1.264 +
   1.265 +    osecfs = OsecFS (config.get ("Main", "Rootpath"))
   1.266 +    osecfs.flags = 0
   1.267 +    osecfs.multithreaded = 0
   1.268 +
   1.269 +    # osecfs.parser.add_option (mountopt=config.get("Main", "Mountpoint"),
   1.270 +    #                      metavar="PATH",
   1.271 +    #                      default=config.get("Main", "Rootpath"),
   1.272 +    #                      help="mirror filesystem from under PATH [default: %default]")
   1.273 +    # osecfs.parse(values=osecfs, errex=1)
   1.274 +
   1.275 +    fuse_args = [sys.argv[0], config.get ("Main", "Mountpoint")];
   1.276 +    osecfs.main (fuse_args)