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