src/OsecFS.py
changeset 22 23028352807f
parent 2 d27473cf6a01
child 23 5b9fdcafd0df
     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)