1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/Apache License, Version 2.0.txt Tue Nov 04 17:40:17 2014 +0100
1.3 @@ -0,0 +1,202 @@
1.4 +
1.5 + Apache License
1.6 + Version 2.0, January 2004
1.7 + http://www.apache.org/licenses/
1.8 +
1.9 + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1.10 +
1.11 + 1. Definitions.
1.12 +
1.13 + "License" shall mean the terms and conditions for use, reproduction,
1.14 + and distribution as defined by Sections 1 through 9 of this document.
1.15 +
1.16 + "Licensor" shall mean the copyright owner or entity authorized by
1.17 + the copyright owner that is granting the License.
1.18 +
1.19 + "Legal Entity" shall mean the union of the acting entity and all
1.20 + other entities that control, are controlled by, or are under common
1.21 + control with that entity. For the purposes of this definition,
1.22 + "control" means (i) the power, direct or indirect, to cause the
1.23 + direction or management of such entity, whether by contract or
1.24 + otherwise, or (ii) ownership of fifty percent (50%) or more of the
1.25 + outstanding shares, or (iii) beneficial ownership of such entity.
1.26 +
1.27 + "You" (or "Your") shall mean an individual or Legal Entity
1.28 + exercising permissions granted by this License.
1.29 +
1.30 + "Source" form shall mean the preferred form for making modifications,
1.31 + including but not limited to software source code, documentation
1.32 + source, and configuration files.
1.33 +
1.34 + "Object" form shall mean any form resulting from mechanical
1.35 + transformation or translation of a Source form, including but
1.36 + not limited to compiled object code, generated documentation,
1.37 + and conversions to other media types.
1.38 +
1.39 + "Work" shall mean the work of authorship, whether in Source or
1.40 + Object form, made available under the License, as indicated by a
1.41 + copyright notice that is included in or attached to the work
1.42 + (an example is provided in the Appendix below).
1.43 +
1.44 + "Derivative Works" shall mean any work, whether in Source or Object
1.45 + form, that is based on (or derived from) the Work and for which the
1.46 + editorial revisions, annotations, elaborations, or other modifications
1.47 + represent, as a whole, an original work of authorship. For the purposes
1.48 + of this License, Derivative Works shall not include works that remain
1.49 + separable from, or merely link (or bind by name) to the interfaces of,
1.50 + the Work and Derivative Works thereof.
1.51 +
1.52 + "Contribution" shall mean any work of authorship, including
1.53 + the original version of the Work and any modifications or additions
1.54 + to that Work or Derivative Works thereof, that is intentionally
1.55 + submitted to Licensor for inclusion in the Work by the copyright owner
1.56 + or by an individual or Legal Entity authorized to submit on behalf of
1.57 + the copyright owner. For the purposes of this definition, "submitted"
1.58 + means any form of electronic, verbal, or written communication sent
1.59 + to the Licensor or its representatives, including but not limited to
1.60 + communication on electronic mailing lists, source code control systems,
1.61 + and issue tracking systems that are managed by, or on behalf of, the
1.62 + Licensor for the purpose of discussing and improving the Work, but
1.63 + excluding communication that is conspicuously marked or otherwise
1.64 + designated in writing by the copyright owner as "Not a Contribution."
1.65 +
1.66 + "Contributor" shall mean Licensor and any individual or Legal Entity
1.67 + on behalf of whom a Contribution has been received by Licensor and
1.68 + subsequently incorporated within the Work.
1.69 +
1.70 + 2. Grant of Copyright License. Subject to the terms and conditions of
1.71 + this License, each Contributor hereby grants to You a perpetual,
1.72 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
1.73 + copyright license to reproduce, prepare Derivative Works of,
1.74 + publicly display, publicly perform, sublicense, and distribute the
1.75 + Work and such Derivative Works in Source or Object form.
1.76 +
1.77 + 3. Grant of Patent License. Subject to the terms and conditions of
1.78 + this License, each Contributor hereby grants to You a perpetual,
1.79 + worldwide, non-exclusive, no-charge, royalty-free, irrevocable
1.80 + (except as stated in this section) patent license to make, have made,
1.81 + use, offer to sell, sell, import, and otherwise transfer the Work,
1.82 + where such license applies only to those patent claims licensable
1.83 + by such Contributor that are necessarily infringed by their
1.84 + Contribution(s) alone or by combination of their Contribution(s)
1.85 + with the Work to which such Contribution(s) was submitted. If You
1.86 + institute patent litigation against any entity (including a
1.87 + cross-claim or counterclaim in a lawsuit) alleging that the Work
1.88 + or a Contribution incorporated within the Work constitutes direct
1.89 + or contributory patent infringement, then any patent licenses
1.90 + granted to You under this License for that Work shall terminate
1.91 + as of the date such litigation is filed.
1.92 +
1.93 + 4. Redistribution. You may reproduce and distribute copies of the
1.94 + Work or Derivative Works thereof in any medium, with or without
1.95 + modifications, and in Source or Object form, provided that You
1.96 + meet the following conditions:
1.97 +
1.98 + (a) You must give any other recipients of the Work or
1.99 + Derivative Works a copy of this License; and
1.100 +
1.101 + (b) You must cause any modified files to carry prominent notices
1.102 + stating that You changed the files; and
1.103 +
1.104 + (c) You must retain, in the Source form of any Derivative Works
1.105 + that You distribute, all copyright, patent, trademark, and
1.106 + attribution notices from the Source form of the Work,
1.107 + excluding those notices that do not pertain to any part of
1.108 + the Derivative Works; and
1.109 +
1.110 + (d) If the Work includes a "NOTICE" text file as part of its
1.111 + distribution, then any Derivative Works that You distribute must
1.112 + include a readable copy of the attribution notices contained
1.113 + within such NOTICE file, excluding those notices that do not
1.114 + pertain to any part of the Derivative Works, in at least one
1.115 + of the following places: within a NOTICE text file distributed
1.116 + as part of the Derivative Works; within the Source form or
1.117 + documentation, if provided along with the Derivative Works; or,
1.118 + within a display generated by the Derivative Works, if and
1.119 + wherever such third-party notices normally appear. The contents
1.120 + of the NOTICE file are for informational purposes only and
1.121 + do not modify the License. You may add Your own attribution
1.122 + notices within Derivative Works that You distribute, alongside
1.123 + or as an addendum to the NOTICE text from the Work, provided
1.124 + that such additional attribution notices cannot be construed
1.125 + as modifying the License.
1.126 +
1.127 + You may add Your own copyright statement to Your modifications and
1.128 + may provide additional or different license terms and conditions
1.129 + for use, reproduction, or distribution of Your modifications, or
1.130 + for any such Derivative Works as a whole, provided Your use,
1.131 + reproduction, and distribution of the Work otherwise complies with
1.132 + the conditions stated in this License.
1.133 +
1.134 + 5. Submission of Contributions. Unless You explicitly state otherwise,
1.135 + any Contribution intentionally submitted for inclusion in the Work
1.136 + by You to the Licensor shall be under the terms and conditions of
1.137 + this License, without any additional terms or conditions.
1.138 + Notwithstanding the above, nothing herein shall supersede or modify
1.139 + the terms of any separate license agreement you may have executed
1.140 + with Licensor regarding such Contributions.
1.141 +
1.142 + 6. Trademarks. This License does not grant permission to use the trade
1.143 + names, trademarks, service marks, or product names of the Licensor,
1.144 + except as required for reasonable and customary use in describing the
1.145 + origin of the Work and reproducing the content of the NOTICE file.
1.146 +
1.147 + 7. Disclaimer of Warranty. Unless required by applicable law or
1.148 + agreed to in writing, Licensor provides the Work (and each
1.149 + Contributor provides its Contributions) on an "AS IS" BASIS,
1.150 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
1.151 + implied, including, without limitation, any warranties or conditions
1.152 + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
1.153 + PARTICULAR PURPOSE. You are solely responsible for determining the
1.154 + appropriateness of using or redistributing the Work and assume any
1.155 + risks associated with Your exercise of permissions under this License.
1.156 +
1.157 + 8. Limitation of Liability. In no event and under no legal theory,
1.158 + whether in tort (including negligence), contract, or otherwise,
1.159 + unless required by applicable law (such as deliberate and grossly
1.160 + negligent acts) or agreed to in writing, shall any Contributor be
1.161 + liable to You for damages, including any direct, indirect, special,
1.162 + incidental, or consequential damages of any character arising as a
1.163 + result of this License or out of the use or inability to use the
1.164 + Work (including but not limited to damages for loss of goodwill,
1.165 + work stoppage, computer failure or malfunction, or any and all
1.166 + other commercial damages or losses), even if such Contributor
1.167 + has been advised of the possibility of such damages.
1.168 +
1.169 + 9. Accepting Warranty or Additional Liability. While redistributing
1.170 + the Work or Derivative Works thereof, You may choose to offer,
1.171 + and charge a fee for, acceptance of support, warranty, indemnity,
1.172 + or other liability obligations and/or rights consistent with this
1.173 + License. However, in accepting such obligations, You may act only
1.174 + on Your own behalf and on Your sole responsibility, not on behalf
1.175 + of any other Contributor, and only if You agree to indemnify,
1.176 + defend, and hold each Contributor harmless for any liability
1.177 + incurred by, or claims asserted against, such Contributor by reason
1.178 + of your accepting any such warranty or additional liability.
1.179 +
1.180 + END OF TERMS AND CONDITIONS
1.181 +
1.182 + APPENDIX: How to apply the Apache License to your work.
1.183 +
1.184 + To apply the Apache License to your work, attach the following
1.185 + boilerplate notice, with the fields enclosed by brackets "[]"
1.186 + replaced with your own identifying information. (Don't include
1.187 + the brackets!) The text should be enclosed in the appropriate
1.188 + comment syntax for the file format. We also recommend that a
1.189 + file or class name and description of purpose be included on the
1.190 + same "printed page" as the copyright notice for easier
1.191 + identification within third-party archives.
1.192 +
1.193 + Copyright [yyyy] [name of copyright owner]
1.194 +
1.195 + Licensed under the Apache License, Version 2.0 (the "License");
1.196 + you may not use this file except in compliance with the License.
1.197 + You may obtain a copy of the License at
1.198 +
1.199 + http://www.apache.org/licenses/LICENSE-2.0
1.200 +
1.201 + Unless required by applicable law or agreed to in writing, software
1.202 + distributed under the License is distributed on an "AS IS" BASIS,
1.203 + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1.204 + See the License for the specific language governing permissions and
1.205 + limitations under the License.
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/config/OsecFS.cfg Tue Nov 04 17:40:17 2014 +0100
2.3 @@ -0,0 +1,36 @@
2.4 +[Main]
2.5 +# make sure this file is writeable
2.6 +Logfile: /var/log/fuse_test.log
2.7 +
2.8 +# DEBUG, INFO, WARNING, ERROR, CRITICAL
2.9 +LogLevel: debug
2.10 +
2.11 +# where the files really are on the filesystem
2.12 +Rootpath: /tmp/root_fuse
2.13 +
2.14 +
2.15 +
2.16 +
2.17 +# path to scanner class
2.18 +#ScannerPath: /path/to/ikarusscanner/src/
2.19 +ScannerPath: /home/spawn/workspace_python/ikarusscanner/src/
2.20 +
2.21 +# scanner module name
2.22 +ScannerModuleName: IkarusScanner
2.23 +ScannerClassName: IkarusScanner
2.24 +
2.25 +# config file for scanner (path will be in the constructor)
2.26 +#ScannerConfig: /path/to/IkarusScanner.cfg
2.27 +ScannerConfig: /home/spawn/workspace_python/ikarusscanner/config/IkarusScanner.cfg
2.28 +
2.29 +
2.30 +
2.31 +# path to scanner class
2.32 +#ScannerPath: /path/to/clamavscanner/src
2.33 +
2.34 +# scanner module name
2.35 +#ScannerModuleName: ClamAVScanner
2.36 +#ScannerClassName: ClamAVScanner
2.37 +
2.38 +# config file for scanner (path will be in the constructor)
2.39 +#ScannerConfig: /path/to/ClamAVScanner.cfg
2.40 \ No newline at end of file
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/src/OsecFS.py Tue Nov 04 17:40:17 2014 +0100
3.3 @@ -0,0 +1,471 @@
3.4 +#!/usr/bin/python
3.5 +
3.6 +# ------------------------------------------------------------
3.7 +# opensecurity package file
3.8 +#
3.9 +# Autor: X-Net Services GmbH <office@x-net.at>
3.10 +#
3.11 +# Copyright 2013-2014 X-Net and AIT Austrian Institute of Technology
3.12 +#
3.13 +#
3.14 +# X-Net Technologies GmbH
3.15 +# Elisabethstrasse 1
3.16 +# 4020 Linz
3.17 +# AUSTRIA
3.18 +# https://www.x-net.at
3.19 +#
3.20 +# AIT Austrian Institute of Technology
3.21 +# Donau City Strasse 1
3.22 +# 1220 Wien
3.23 +# AUSTRIA
3.24 +# http://www.ait.ac.at
3.25 +#
3.26 +#
3.27 +# Licensed under the Apache License, Version 2.0 (the "License");
3.28 +# you may not use this file except in compliance with the License.
3.29 +# You may obtain a copy of the License at
3.30 +#
3.31 +# http://www.apache.org/licenses/LICENSE-2.0
3.32 +#
3.33 +# Unless required by applicable law or agreed to in writing, software
3.34 +# distributed under the License is distributed on an "AS IS" BASIS,
3.35 +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3.36 +# See the License for the specific language governing permissions and
3.37 +# limitations under the License.
3.38 +# ------------------------------------------------------------
3.39 +
3.40 +
3.41 +from fuse import Fuse
3.42 +import fuse
3.43 +
3.44 +import ConfigParser
3.45 +
3.46 +import sys
3.47 +
3.48 +import logging
3.49 +import os
3.50 +import errno
3.51 +import time
3.52 +
3.53 +from importlib import import_module
3.54 +
3.55 +
3.56 +import subprocess
3.57 +
3.58 +import urllib3
3.59 +import netifaces
3.60 +import netaddr
3.61 +import hashlib
3.62 +
3.63 +
3.64 +sys.stderr = open('/var/log/osecfs_error.log', 'a+')
3.65 +
3.66 +
3.67 +MINOPTS = { "Main" : ["Logfile", "LogLevel", "Mountpoint", "Rootpath", "ScannerPath", "ScannerModuleName", "ScannerClassName", "ScannerConfig", "ReadOnly"]}
3.68 +
3.69 +CONFIG_NOT_READABLE = "Configfile is not readable"
3.70 +CONFIG_WRONG = "Something is wrong with the config"
3.71 +CONFIG_MISSING = "Section: \"%s\" Option: \"%s\" in configfile is missing"
3.72 +SCAN_WRONG_RETURN_VALUE = "The return Value of the malware scanner is wrong. Has to be an dictionary"
3.73 +SCAN_RETURN_VALUE_KEY_MISSING = "The dictionary has to include key \"infected\" (True, False) and \"virusname\" (String)"
3.74 +VIRUS_FOUND = "Virus found. Access denied"
3.75 +NOTIFICATION_CRITICAL = "critical"
3.76 +NOTIFICATION_INFO = "info"
3.77 +LOG = None
3.78 +MalwareScanner = None
3.79 +STATUS_CODE_OK = 200
3.80 +
3.81 +SYSTEM_FILE_COMMAND = "file"
3.82 +httpPool = urllib3.PoolManager(num_pools = 1, timeout = 3)
3.83 +
3.84 +def checkMinimumOptions (config):
3.85 + for section, options in MINOPTS.iteritems ():
3.86 + for option in options:
3.87 + if (config.has_option(section, option) == False):
3.88 + print (CONFIG_MISSING % (section, option))
3.89 + exit (129)
3.90 +
3.91 +def printUsage ():
3.92 + print ("Usage:")
3.93 + print ("%s configfile mountpath ro/rw" % (sys.argv[0]))
3.94 + exit (128)
3.95 +
3.96 +def loadConfig ():
3.97 + print ("load config")
3.98 +
3.99 + if (len (sys.argv) < 4):
3.100 + printUsage ()
3.101 +
3.102 + configfile = sys.argv[1]
3.103 + config = ConfigParser.SafeConfigParser ()
3.104 +
3.105 + if ((os.path.exists (configfile) == False) or (os.path.isfile (configfile) == False) or (os.access (configfile, os.R_OK) == False)):
3.106 + print (CONFIG_NOT_READABLE)
3.107 + printUsage ()
3.108 +
3.109 + try:
3.110 + config.read (sys.argv[1])
3.111 + except Exception, e:
3.112 + print (CONFIG_WRONG)
3.113 + print ("Error: %s" % (e))
3.114 +
3.115 +
3.116 + config.set("Main", "Mountpoint", sys.argv[2])
3.117 + if (sys.argv[3] == "rw"):
3.118 + config.set("Main", "ReadOnly", "false")
3.119 + else:
3.120 + config.set("Main", "ReadOnly", "true")
3.121 +
3.122 + checkMinimumOptions (config)
3.123 +
3.124 + return config
3.125 +
3.126 +def initLog (config):
3.127 + print ("init log")
3.128 +
3.129 + global LOG
3.130 + logfile = config.get("Main", "Logfile")
3.131 +
3.132 + numeric_level = getattr(logging, config.get("Main", "LogLevel").upper(), None)
3.133 + if not isinstance(numeric_level, int):
3.134 + raise ValueError('Invalid log level: %s' % loglevel)
3.135 +
3.136 + # ToDo move log level and maybe other things to config file
3.137 + logging.basicConfig(
3.138 + level = numeric_level,
3.139 + format = "%(asctime)s %(name)-12s %(funcName)-15s %(levelname)-8s %(message)s",
3.140 + datefmt = "%Y-%m-%d %H:%M:%S",
3.141 + filename = logfile,
3.142 + filemode = "a+",
3.143 + )
3.144 + LOG = logging.getLogger("fuse_main")
3.145 +
3.146 +
3.147 +def fixPath (path):
3.148 + return ".%s" % (path)
3.149 +
3.150 +def rootPath (rootpath, path):
3.151 + return "%s%s" % (rootpath, path)
3.152 +
3.153 +def flag2mode (flags):
3.154 + md = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'}
3.155 + m = md[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)]
3.156 +
3.157 + # windows sets append even if it would overwrite the whole file (seek 0)
3.158 + # so ignore append option
3.159 + #if flags | os.O_APPEND:
3.160 + # m = m.replace('w', 'a', 1)
3.161 +
3.162 + return m
3.163 +
3.164 +def scanFile (path, fileobject):
3.165 + LOG.debug ("Scan File \"%s\" with malware Scanner" %(path,) )
3.166 + return MalwareScanner.scanFile (path, fileobject)
3.167 +
3.168 +
3.169 +def scanFileClamAV (path):
3.170 + infected = False
3.171 +
3.172 + LOG.debug ("Scan File: %s" % (path))
3.173 +
3.174 + result = pyclamav.scanfile (path)
3.175 + LOG.debug ("Result of file \"%s\": %s" % (path, result))
3.176 + if (result[0] != 0):
3.177 + infected = True
3.178 +
3.179 + if (infected == True):
3.180 + LOG.error ("Virus found, deny Access %s" % (result,))
3.181 +
3.182 + return infected
3.183 +
3.184 +def whitelistFile (path):
3.185 + whitelisted = False;
3.186 +
3.187 + LOG.debug ("Execute \"%s\" command on \"%s\"" %(SYSTEM_FILE_COMMAND, path))
3.188 +
3.189 + result = None
3.190 + try:
3.191 + result = subprocess.check_output ([SYSTEM_FILE_COMMAND, path]);
3.192 + # ToDo replace with real whitelist
3.193 + whitelisted = True
3.194 + except Exception as e:
3.195 + LOG.error ("Call returns with an error!")
3.196 + LOG.error (e)
3.197 +
3.198 + LOG.debug ("Type: %s" %(result))
3.199 +
3.200 + return whitelisted
3.201 +
3.202 +def sendDataToRest (urlpath, data):
3.203 + netifaces.ifaddresses("eth0")[2][0]["addr"]
3.204 +
3.205 + # Get first address in network (0 = network ip -> 192.168.0.0)
3.206 + remote_ip = netaddr.IPNetwork("%s/%s" %(netifaces.ifaddresses("eth0")[2][0]["addr"], netifaces.ifaddresses("eth0")[2][0]["netmask"]))[1]
3.207 +
3.208 + url = ("http://%s:8090//%s" %(remote_ip, urlpath))
3.209 +
3.210 + LOG.debug ("Send data to \"%s\"" %(url, ))
3.211 + LOG.debug ("Data: %s" %(data, ))
3.212 +
3.213 + try:
3.214 + response = httpPool.request_encode_body("POST", url, fields=data, retries=0)
3.215 + except Exception, e:
3.216 + LOG.error("Remote host not reachable")
3.217 + LOG.error ("Exception: %s" %(e,))
3.218 + return
3.219 +
3.220 + if response.status == STATUS_CODE_OK:
3.221 + LOG.info("Data sent successfully to rest server")
3.222 + return True
3.223 + else:
3.224 + LOG.error("Server returned errorcode: %s" %(response.status,))
3.225 + return False
3.226 +
3.227 +
3.228 +def sendNotification (type, message):
3.229 + data = {"msgtype" : type, "text" : message}
3.230 +
3.231 + if (type == "information"):
3.232 + sendDataToRest ("message", data)
3.233 + else:
3.234 + sendDataToRest ("notification", data)
3.235 +
3.236 +def sendReadOnlyNotification():
3.237 + sendNotification("critical", "Filesystem is in read only mode. If you want to export files please initialize an encrypted filesystem.")
3.238 +
3.239 +def sendLogNotPossibleNotification():
3.240 + sendNotification("critical", "Send log entry to opensecurity rest server failed.")
3.241 +
3.242 +def sendFileLog(filename, filesize, filehash, hashtype):
3.243 + data = {"filename" : filename, "filesize" : "%s" %(filesize,), "filehash" : filehash, "hashtype" : hashtype}
3.244 + retval = sendDataToRest ("log", data)
3.245 + if (retval == False):
3.246 + sendLogNotPossibleNotification()
3.247 +
3.248 +def calcMD5 (path, block_size=256*128, hr=True):
3.249 + md5 = hashlib.md5()
3.250 + with open(path,'rb') as f:
3.251 + for chunk in iter(lambda: f.read(block_size), b''):
3.252 + md5.update(chunk)
3.253 + if hr:
3.254 + return md5.hexdigest()
3.255 + return md5.digest()
3.256 +
3.257 +class OsecFS (Fuse):
3.258 +
3.259 + __rootpath = None
3.260 +
3.261 + # default fuse init
3.262 + def __init__(self, rootpath, *args, **kw):
3.263 + self.__rootpath = rootpath
3.264 + Fuse.__init__ (self, *args, **kw)
3.265 + LOG.debug ("Init complete.")
3.266 + sendNotification("information", "Filesystem successfully mounted.")
3.267 +
3.268 + # defines that our working directory will be the __rootpath
3.269 + def fsinit(self):
3.270 + os.chdir (self.__rootpath)
3.271 +
3.272 + def getattr(self, path):
3.273 + LOG.debug ("*** getattr (%s)" % (fixPath (path)))
3.274 + return os.lstat (fixPath (path));
3.275 +
3.276 + def getdir(self, path):
3.277 + LOG.debug ("*** getdir (%s)" % (path));
3.278 + return os.listdir (fixPath (path))
3.279 +
3.280 + def readdir(self, path, offset):
3.281 + LOG.debug ("*** readdir (%s %s)" % (path, offset));
3.282 + for e in os.listdir (fixPath (path)):
3.283 + yield fuse.Direntry(e)
3.284 +
3.285 + def chmod (self, path, mode):
3.286 + LOG.debug ("*** chmod %s %s" % (path, oct(mode)))
3.287 + if (config.get("Main", "ReadOnly") == "true"):
3.288 + sendReadOnlyNotification()
3.289 + return -errno.EACCES
3.290 + os.chmod (fixPath (path), mode)
3.291 +
3.292 + def chown (self, path, uid, gid):
3.293 + LOG.debug ("*** chown %s %s %s" % (path, uid, gid))
3.294 + if (config.get("Main", "ReadOnly") == "true"):
3.295 + sendReadOnlyNotification()
3.296 + return -errno.EACCES
3.297 + os.chown (fixPath (path), uid, gid)
3.298 +
3.299 + def link (self, targetPath, linkPath):
3.300 + LOG.debug ("*** link %s %s" % (targetPath, linkPath))
3.301 + if (config.get("Main", "ReadOnly") == "true"):
3.302 + sendReadOnlyNotification()
3.303 + return -errno.EACCES
3.304 + os.link (fixPath (targetPath), fixPath (linkPath))
3.305 +
3.306 + def mkdir (self, path, mode):
3.307 + LOG.debug ("*** mkdir %s %s" % (path, oct(mode)))
3.308 + if (config.get("Main", "ReadOnly") == "true"):
3.309 + sendReadOnlyNotification()
3.310 + return -errno.EACCES
3.311 + os.mkdir (fixPath (path), mode)
3.312 +
3.313 + def mknod (self, path, mode, dev):
3.314 + LOG.debug ("*** mknod %s %s %s" % (path, oct (mode), dev))
3.315 + if (config.get("Main", "ReadOnly") == "true"):
3.316 + sendReadOnlyNotification()
3.317 + return -errno.EACCES
3.318 + os.mknod (fixPath (path), mode, dev)
3.319 +
3.320 + # to implement virus scan
3.321 + def open (self, path, flags):
3.322 + LOG.debug ("*** open %s %s" % (path, oct (flags)))
3.323 + self.file = os.fdopen (os.open (fixPath (path), flags), flag2mode (flags))
3.324 + self.written = False
3.325 + self.fd = self.file.fileno ()
3.326 +
3.327 + LOG.debug(self.__rootpath)
3.328 + LOG.debug(path)
3.329 +
3.330 + retval = scanFile (rootPath(self.__rootpath, path), self.file)
3.331 +
3.332 + #if type(retval) is not dict:
3.333 + if (isinstance(retval, dict) == False):
3.334 + LOG.error(SCAN_WRONG_RETURN_VALUE)
3.335 + self.file.close ()
3.336 + return -errno.EACCES
3.337 +
3.338 + if ((retval.has_key("infected") == False) or (retval.has_key("virusname") == False)):
3.339 + LOG.error(SCAN_RETURN_VALUE_KEY_MISSING)
3.340 + self.file.close ()
3.341 + return -errno.EACCES
3.342 +
3.343 +
3.344 + if (retval.get("infected") == True):
3.345 + self.file.close ()
3.346 + sendNotification(NOTIFICATION_CRITICAL, "%s\nFile: %s\nVirus: %s" %(VIRUS_FOUND, path, retval.get("virusname")))
3.347 + LOG.error("%s" %(VIRUS_FOUND,))
3.348 + LOG.error("Virus: %s" %(retval.get("virusname"),))
3.349 + return -errno.EACCES
3.350 +
3.351 + whitelisted = whitelistFile (rootPath(self.__rootpath, path))
3.352 + if (whitelisted == False):
3.353 + self.file.close ()
3.354 + sendNotification(NOTIFICATION_CRITICAL, "File not in whitelist. Access denied.")
3.355 + return -errno.EACCES
3.356 +
3.357 + def read (self, path, length, offset):
3.358 + LOG.debug ("*** read %s %s %s" % (path, length, offset))
3.359 + self.file.seek (offset)
3.360 + return self.file.read (length)
3.361 +
3.362 + def readlink (self, path):
3.363 + LOG.debug ("*** readlink %s" % (path))
3.364 + return os.readlink (fixPath (path))
3.365 +
3.366 + def release (self, path, flags):
3.367 + LOG.debug ("*** release %s %s" % (path, oct (flags)))
3.368 + self.file.flush()
3.369 + os.fsync(self.file.fileno())
3.370 + self.file.close ()
3.371 +
3.372 + if (self.written == True):
3.373 + hashsum = calcMD5(fixPath(path))
3.374 + filesize = os.path.getsize(fixPath(path))
3.375 + sendFileLog(path, filesize, hashsum, "md5")
3.376 +
3.377 +
3.378 + def rename (self, oldPath, newPath):
3.379 + LOG.debug ("*** rename %s %s %s" % (oldPath, newPath, config.get("Main", "ReadOnly")))
3.380 + if (config.get("Main", "ReadOnly") == "true"):
3.381 + sendReadOnlyNotification()
3.382 + return -errno.EACCES
3.383 + os.rename (fixPath (oldPath), fixPath (newPath))
3.384 +
3.385 + def rmdir (self, path):
3.386 + LOG.debug ("*** rmdir %s %s" % (path, config.get("Main", "ReadOnly")))
3.387 + if (config.get("Main", "ReadOnly") == "true"):
3.388 + sendReadOnlyNotification()
3.389 + return -errno.EACCES
3.390 + os.rmdir (fixPath (path))
3.391 +
3.392 + def statfs (self):
3.393 + LOG.debug ("*** statfs")
3.394 + return os.statvfs(".")
3.395 +
3.396 + def symlink (self, targetPath, linkPath):
3.397 + LOG.debug ("*** symlink %s %s %s" % (targetPath, linkPath, config.get("Main", "ReadOnly")))
3.398 + if (config.get("Main", "ReadOnly") == "true"):
3.399 + sendReadOnlyNotification()
3.400 + return -errno.EACCES
3.401 + os.symlink (fixPath (targetPath), fixPath (linkPath))
3.402 +
3.403 + def truncate (self, path, length):
3.404 + LOG.debug ("*** truncate %s %s %s" % (path, length, config.get("Main", "ReadOnly")))
3.405 + if (config.get("Main", "ReadOnly") == "true"):
3.406 + sendReadOnlyNotification()
3.407 + return -errno.EACCES
3.408 + f = open (fixPath (path), "w+")
3.409 + f.truncate (length)
3.410 + f.close ()
3.411 +
3.412 + def unlink (self, path):
3.413 + LOG.debug ("*** unlink %s %s" % (path, config.get("Main", "ReadOnly")))
3.414 + if (config.get("Main", "ReadOnly") == "true"):
3.415 + sendReadOnlyNotification()
3.416 + return -errno.EACCES
3.417 + os.unlink (fixPath (path))
3.418 +
3.419 + def utime (self, path, times):
3.420 + LOG.debug ("*** utime %s %s" % (path, times))
3.421 + os.utime (fixPath (path), times)
3.422 +
3.423 + def write (self, path, buf, offset):
3.424 + #LOG.debug ("*** write %s %s %s %s" % (path, buf, offset, config.get("Main", "ReadOnly")))
3.425 + LOG.debug ("*** write %s %s %s %s" % (path, "filecontent", offset, config.get("Main", "ReadOnly")))
3.426 + if (config.get("Main", "ReadOnly") == "true"):
3.427 + self.file.close()
3.428 + sendReadOnlyNotification()
3.429 + return -errno.EACCES
3.430 + self.file.seek (offset)
3.431 + self.file.write (buf)
3.432 + self.written = True
3.433 + return len (buf)
3.434 +
3.435 + def access (self, path, mode):
3.436 + LOG.debug ("*** access %s %s" % (path, oct (mode)))
3.437 + if not os.access (fixPath (path), mode):
3.438 + return -errno.EACCES
3.439 +
3.440 + def create (self, path, flags, mode):
3.441 + LOG.debug ("*** create %s %s %s %s %s" % (fixPath (path), oct (flags), oct (mode), flag2mode (flags), config.get("Main", "ReadOnly")))
3.442 + if (config.get("Main", "ReadOnly") == "true"):
3.443 + sendReadOnlyNotification()
3.444 + return -errno.EACCES
3.445 +
3.446 + self.file = os.fdopen (os.open (fixPath (path), flags), flag2mode(flags))
3.447 + self.written = True
3.448 + self.fd = self.file.fileno ()
3.449 +
3.450 +
3.451 +if __name__ == "__main__":
3.452 + # Set api version
3.453 + fuse.fuse_python_api = (0, 2)
3.454 + fuse.feature_assert ('stateful_files', 'has_init')
3.455 +
3.456 + config = loadConfig ()
3.457 + initLog (config)
3.458 +
3.459 + #sendNotification("Info", "OsecFS started")
3.460 +
3.461 + # Import the Malware Scanner
3.462 + sys.path.append(config.get("Main", "ScannerPath"))
3.463 +
3.464 + MalwareModule = import_module(config.get("Main", "ScannerModuleName"))
3.465 + MalwareClass = getattr(MalwareModule, config.get("Main", "ScannerClassName"))
3.466 +
3.467 + MalwareScanner = MalwareClass (config.get("Main", "ScannerConfig"));
3.468 +
3.469 + osecfs = OsecFS (config.get ("Main", "Rootpath"))
3.470 + osecfs.flags = 0
3.471 + osecfs.multithreaded = 0
3.472 +
3.473 + fuse_args = [sys.argv[0], config.get ("Main", "Mountpoint")];
3.474 + osecfs.main (fuse_args)