Changed requests to urllib3.
Added maximimum file size for scanned files.
14 # ToDo replace with ikarus
21 MINOPTS = { "Main" : ["Logfile", "Mountpoint", "Rootpath", "LocalScanserverURL", "RemoteScanserverURL", "ReadOnly"]}
23 CONFIG_NOT_READABLE = "Configfile is not readable"
24 CONFIG_WRONG = "Something is wrong with the config"
25 CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
27 LOCAL_SCANSERVER_URL = ""
28 REMOTE_SCANSERVER_URL = ""
30 STATUS_CODE_INFECTED = 210
31 STATUS_CODE_NOT_FOUND = 404
33 MAX_SCAN_FILE_SIZE = 50 * 0x100000
35 SYSTEM_FILE_COMMAND = "file"
37 # Global http pool manager used to connect to the scan server
38 httpPool = urllib3.PoolManager()
40 def checkMinimumOptions (config):
41 for section, options in MINOPTS.iteritems ():
42 for option in options:
43 if (config.has_option(section, option) == False):
44 print (CONFIG_MISSING % (section, option))
49 print ("%s configfile mountpath ro/rw" % (sys.argv[0]))
55 if (len (sys.argv) < 4):
58 configfile = sys.argv[1]
59 config = ConfigParser.SafeConfigParser ()
61 if ((os.path.exists (configfile) == False) or (os.path.isfile (configfile) == False) or (os.access (configfile, os.R_OK) == False)):
62 print (CONFIG_NOT_READABLE)
66 config.read (sys.argv[1])
69 print ("Error: %s" % (e))
72 config.set("Main", "Mountpoint", sys.argv[2])
73 if (sys.argv[3] == "rw"):
74 config.set("Main", "ReadOnly", "false")
76 config.set("Main", "ReadOnly", "true")
78 checkMinimumOptions (config)
86 logfile = config.get("Main", "Logfile")
88 # ToDo move log level and maybe other things to config file
90 level = logging.DEBUG,
91 format = "%(asctime)s %(name)-12s %(funcName)-15s %(levelname)-8s %(message)s",
92 datefmt = "%Y-%m-%d %H:%M:%S",
96 LOG = logging.getLogger("fuse_main")
100 return ".%s" % (path)
102 def rootPath (rootpath, path):
103 return "%s%s" % (rootpath, path)
105 def flag2mode (flags):
106 md = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'}
107 m = md[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)]
109 if flags | os.O_APPEND:
110 m = m.replace('w', 'a', 1)
114 def scanFileIkarus (path, fileobject):
116 LOG.debug ("Scan File: %s" % (path))
118 if (os.fstat(fileobject.fileno()).st_size > MAX_SCAN_FILE_SIZE):
119 LOG.info("File max size exceeded. The file is not scanned.")
122 fields = { 'up_file' : (path, fileobject.read()) }
125 response = httpPool.request_encode_body('POST', REMOTE_SCANSERVER_URL, fields = fields)
126 # We should catch socket.error here, but this does not work. Needs checking.
128 LOG.info("Remote scan server unreachable, using local scan server.")
131 response = httpPool.request_encode_body('POST', LOCAL_SCANSERVER_URL, fields = fields)
133 LOG.error ("Connection to local scan server could not be established.")
134 LOG.error ("Exception: %s" %(sys.exc_info()[0]))
137 if response.status == STATUS_CODE_OK:
139 elif response.status == STATUS_CODE_INFECTED:
140 # Parse xml for info if desired
141 #contentXML = r.content
142 #root = ET.fromstring(contentXML)
143 #status = root[1][2].text
146 LOG.error ("Connection error to scan server.")
148 if (infected == True):
149 LOG.error ("Virus found, denying access.")
151 LOG.debug ("No virus found.")
155 def scanFileClamAV (path):
158 LOG.debug ("Scan File: %s" % (path))
160 # ToDo implement ikarus
161 result = pyclamav.scanfile (path)
162 LOG.debug ("Result of file \"%s\": %s" % (path, result))
166 if (infected == True):
167 LOG.error ("Virus found, deny Access %s" % (result,))
171 def whitelistFile (path):
174 LOG.debug ("Execute \"%s\" command on \"%s\"" %(SYSTEM_FILE_COMMAND, path))
178 result = subprocess.check_output ([SYSTEM_FILE_COMMAND, path]);
179 # ToDo replace with real whitelist
181 except Exception as e:
182 LOG.error ("Call returns with an error!")
185 LOG.debug ("Type: %s" %(result))
194 def __init__(self, rootpath, *args, **kw):
195 self.__rootpath = rootpath
196 Fuse.__init__ (self, *args, **kw)
197 LOG.debug ("Init complete.")
199 # defines that our working directory will be the __rootpath
201 os.chdir (self.__rootpath)
203 def getattr(self, path):
204 LOG.debug ("*** getattr (%s)" % (fixPath (path)))
205 return os.lstat (fixPath (path));
207 def getdir(self, path):
208 LOG.debug ("*** getdir (%s)" % (path));
209 return os.listdir (fixPath (path))
211 def readdir(self, path, offset):
212 LOG.debug ("*** readdir (%s %s)" % (path, offset));
213 for e in os.listdir (fixPath (path)):
214 yield fuse.Direntry(e)
216 def chmod (self, path, mode):
217 LOG.debug ("*** chmod %s %s" % (path, oct(mode)))
218 if (config.get("Main", "ReadOnly") == "true"):
220 os.chmod (fixPath (path), mode)
222 def chown (self, path, uid, gid):
223 LOG.debug ("*** chown %s %s %s" % (path, uid, gid))
224 if (config.get("Main", "ReadOnly") == "true"):
226 os.chown (fixPath (path), uid, gid)
228 def link (self, targetPath, linkPath):
229 LOG.debug ("*** link %s %s" % (targetPath, linkPath))
230 if (config.get("Main", "ReadOnly") == "true"):
232 os.link (fixPath (targetPath), fixPath (linkPath))
234 def mkdir (self, path, mode):
235 LOG.debug ("*** mkdir %s %s" % (path, oct(mode)))
236 if (config.get("Main", "ReadOnly") == "true"):
238 os.mkdir (fixPath (path), mode)
240 def mknod (self, path, mode, dev):
241 LOG.debug ("*** mknod %s %s %s" % (path, oct (mode), dev))
242 if (config.get("Main", "ReadOnly") == "true"):
244 os.mknod (fixPath (path), mode, dev)
246 # to implement virus scan
247 def open (self, path, flags):
248 LOG.debug ("*** open %s %s" % (path, oct (flags)))
249 self.file = os.fdopen (os.open (fixPath (path), flags), flag2mode (flags))
250 self.fd = self.file.fileno ()
252 infected = scanFileIkarus (rootPath(self.__rootpath, path), self.file)
253 #infected = scanFileClamAV (rootPath(self.__rootpath, path))
254 if (infected == True):
258 whitelisted = whitelistFile (rootPath(self.__rootpath, path))
259 if (whitelisted == False):
263 def read (self, path, length, offset):
264 LOG.debug ("*** read %s %s %s" % (path, length, offset))
265 self.file.seek (offset)
266 return self.file.read (length)
268 def readlink (self, path):
269 LOG.debug ("*** readlink %s" % (path))
270 return os.readlink (fixPath (path))
272 def release (self, path, flags):
273 LOG.debug ("*** release %s %s" % (path, oct (flags)))
276 def rename (self, oldPath, newPath):
277 LOG.debug ("*** rename %s %s %s" % (oldPath, newPath, config.get("Main", "ReadOnly")))
278 if (config.get("Main", "ReadOnly") == "true"):
280 os.rename (fixPath (oldPath), fixPath (newPath))
282 def rmdir (self, path):
283 LOG.debug ("*** rmdir %s %s" % (path, config.get("Main", "ReadOnly")))
284 if (config.get("Main", "ReadOnly") == "true"):
286 os.rmdir (fixPath (path))
289 LOG.debug ("*** statfs")
290 return os.statvfs(".")
292 def symlink (self, targetPath, linkPath):
293 LOG.debug ("*** symlink %s %s %s" % (targetPath, linkPath, config.get("Main", "ReadOnly")))
294 if (config.get("Main", "ReadOnly") == "true"):
296 os.symlink (fixPath (targetPath), fixPath (linkPath))
298 def truncate (self, path, length):
299 LOG.debug ("*** truncate %s %s %s" % (path, length, config.get("Main", "ReadOnly")))
300 if (config.get("Main", "ReadOnly") == "true"):
302 f = open (fixPath (path), "a")
306 def unlink (self, path):
307 LOG.debug ("*** unlink %s %s" % (path, config.get("Main", "ReadOnly")))
308 if (config.get("Main", "ReadOnly") == "true"):
310 os.unlink (fixPath (path))
312 def utime (self, path, times):
313 LOG.debug ("*** utime %s %s" % (path, times))
314 os.utime (fixPath (path), times)
316 def write (self, path, buf, offset):
317 LOG.debug ("*** write %s %s %s %s" % (path, buf, offset, config.get("Main", "ReadOnly")))
318 if (config.get("Main", "ReadOnly") == "true"):
321 self.file.seek (offset)
322 self.file.write (buf)
325 def access (self, path, mode):
326 LOG.debug ("*** access %s %s" % (path, oct (mode)))
327 if not os.access (fixPath (path), mode):
330 def create (self, path, flags, mode):
331 LOG.debug ("*** create %s %s %s %s %s" % (fixPath (path), oct (flags), oct (mode), flag2mode (flags), config.get("Main", "ReadOnly")))
332 if (config.get("Main", "ReadOnly") == "true"):
334 self.file = os.fdopen (os.open (fixPath (path), flags, mode), flag2mode (flags))
335 self.fd = self.file.fileno ()
338 if __name__ == "__main__":
340 fuse.fuse_python_api = (0, 2)
341 fuse.feature_assert ('stateful_files', 'has_init')
343 config = loadConfig ()
346 LOCAL_SCANSERVER_URL = config.get("Main", "LocalScanserverURL")
347 REMOTE_SCANSERVER_URL = config.get("Main", "RemoteScanserverURL")
349 # Convert file size from MB to byte
350 MAX_SCAN_FILE_SIZE = int(config.get("Main", "MaxFileSize")) * 0x100000
352 osecfs = OsecFS (config.get ("Main", "Rootpath"))
354 osecfs.multithreaded = 0
356 # osecfs.parser.add_option (mountopt=config.get("Main", "Mountpoint"),
358 # default=config.get("Main", "Rootpath"),
359 # help="mirror filesystem from under PATH [default: %default]")
360 # osecfs.parse(values=osecfs, errex=1)
362 fuse_args = [sys.argv[0], config.get ("Main", "Mountpoint")];
363 osecfs.main (fuse_args)