commit original files (rev 21) default tip
authorft
Tue, 04 Nov 2014 17:40:17 +0100
changeset 25a600a9b39dd7
parent 24 2422a3542d94
commit original files (rev 21)
Apache License, Version 2.0.txt
config/OsecFS.cfg
src/OsecFS.py
     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)