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)
|