oliver@168: #!/usr/bin/env python oliver@167: # -*- coding: utf-8 -*- oliver@167: oliver@167: # ------------------------------------------------------------ oliver@167: # opensecurity_client_restful_server oliver@167: # oliver@167: # the OpenSecurity client RESTful server oliver@167: # oliver@167: # Autor: Oliver Maurhart, oliver@167: # oliver@167: # Copyright (C) 2013 AIT Austrian Institute of Technology oliver@167: # AIT Austrian Institute of Technology GmbH oliver@167: # Donau-City-Strasse 1 | 1220 Vienna | Austria oliver@167: # http://www.ait.ac.at oliver@167: # oliver@167: # This program is free software; you can redistribute it and/or oliver@167: # modify it under the terms of the GNU General Public License oliver@167: # as published by the Free Software Foundation version 2. oliver@167: # oliver@167: # This program is distributed in the hope that it will be useful, oliver@167: # but WITHOUT ANY WARRANTY; without even the implied warranty of oliver@167: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the oliver@167: # GNU General Public License for more details. oliver@167: # oliver@167: # You should have received a copy of the GNU General Public License oliver@167: # along with this program; if not, write to the Free Software oliver@167: # Foundation, Inc., 51 Franklin Street, Fifth Floor, oliver@167: # Boston, MA 02110-1301, USA. oliver@167: # ------------------------------------------------------------ oliver@167: oliver@167: oliver@167: # ------------------------------------------------------------ oliver@167: # imports oliver@167: oliver@167: import getpass oliver@167: import glob oliver@167: import json oliver@167: import os oliver@167: import os.path oliver@167: import pickle oliver@167: import platform oliver@167: import socket oliver@167: import subprocess oliver@167: import sys oliver@167: import threading oliver@167: import time oliver@167: import urllib oliver@167: import urllib2 oliver@167: import web oliver@167: import threading oliver@167: import time oliver@167: import string oliver@167: import win32api oliver@167: import win32con BarthaM@176: import win32wnet oliver@178: import win32netcon BarthaM@176: import itertools BarthaM@176: import ctypes oliver@167: oliver@193: from PyQt4 import QtGui oliver@193: oliver@167: from opensecurity_util import logger, setupLogger, OpenSecurityException oliver@167: if sys.platform == 'win32' or sys.platform == 'cygwin': oliver@167: from cygwin import Cygwin oliver@167: oliver@167: # local oliver@167: import __init__ as opensecurity oliver@167: from environment import Environment oliver@167: oliver@167: oliver@167: # ------------------------------------------------------------ oliver@167: # const oliver@167: oliver@167: oliver@167: """All the URLs we know mapping to class handler""" oliver@167: opensecurity_urls = ( oliver@167: '/credentials', 'os_credentials', oliver@167: '/keyfile', 'os_keyfile', oliver@167: '/log', 'os_log', oliver@193: '/message', 'os_message', oliver@167: '/notification', 'os_notification', oliver@167: '/password', 'os_password', oliver@167: '/netmount', 'os_netmount', oliver@167: '/netumount', 'os_netumount', BarthaM@176: '/netcleanup', 'os_netcleanup', oliver@167: '/', 'os_root' oliver@167: ) oliver@167: oliver@167: oliver@167: # ------------------------------------------------------------ oliver@167: # vars oliver@167: oliver@167: oliver@167: """lock for read/write log file""" oliver@167: log_file_lock = threading.Lock() oliver@167: oliver@167: """timer for the log file bouncer""" oliver@167: log_file_bouncer = None oliver@167: oliver@167: """The REST server object""" oliver@167: server = None oliver@167: oliver@167: oliver@193: """The System Tray Icon instance""" oliver@193: tray_icon = None oliver@193: oliver@167: # ------------------------------------------------------------ oliver@167: # code oliver@167: oliver@167: oliver@167: class os_credentials: oliver@167: oliver@167: """OpenSecurity '/credentials' handler. oliver@167: oliver@167: This is called on GET /credentials?text=TEXT. oliver@167: Ideally this should pop up a user dialog to insert his oliver@167: credentials based the given TEXT. oliver@167: """ oliver@167: oliver@167: def GET(self): oliver@167: oliver@167: # pick the arguments oliver@167: args = web.input() oliver@167: oliver@167: # we _need_ a text oliver@167: if not "text" in args: oliver@167: raise web.badrequest('no text given') oliver@167: oliver@167: # remember remote ip oliver@167: remote_ip = web.ctx.environ['REMOTE_ADDR'] oliver@167: oliver@167: # create the process which queries the user oliver@167: dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw') oliver@167: process_command = [sys.executable, dlg_image, 'credentials', args.text] oliver@167: oliver@167: # run process result handling in seprate thread (not to block main one) oliver@208: bouncer = ProcessResultBouncer(process_command, remote_ip, '/credentials') oliver@167: bouncer.start() oliver@167: oliver@167: return 'user queried for credentials' oliver@167: oliver@167: oliver@167: class os_keyfile: oliver@167: oliver@167: """OpenSecurity '/keyfile' handler. oliver@167: oliver@167: This is called on GET /keyfile?text=TEXT. oliver@167: Ideally this should pop up a user dialog to insert his oliver@167: password along with a keyfile. oliver@167: """ oliver@167: oliver@167: def GET(self): oliver@167: oliver@167: # pick the arguments oliver@167: args = web.input() oliver@167: oliver@167: # we _need_ a text oliver@167: if not "text" in args: oliver@167: raise web.badrequest('no text given') oliver@167: oliver@167: # remember remote ip oliver@167: remote_ip = web.ctx.environ['REMOTE_ADDR'] oliver@167: oliver@167: # create the process which queries the user oliver@167: dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw') oliver@167: process_command = [sys.executable, dlg_image, 'keyfile', args.text] oliver@167: oliver@167: # run process result handling in seprate thread (not to block main one) oliver@208: bouncer = ProcessResultBouncer(process_command, remote_ip, '/keyfile') oliver@167: bouncer.start() oliver@167: oliver@167: return 'user queried for password and keyfile' oliver@167: oliver@167: oliver@167: class os_log: oliver@167: oliver@167: """OpenSecurity '/log' handler. oliver@167: oliver@167: This is called on GET or POST on the log function /log oliver@167: """ oliver@167: oliver@167: def GET(self): oliver@167: oliver@167: # pick the arguments oliver@167: self.POST() oliver@167: oliver@167: oliver@167: def POST(self): oliver@167: oliver@167: # pick the arguments oliver@167: args = web.input() oliver@167: args['user'] = getpass.getuser() oliver@167: args['system'] = platform.node() + " " + platform.system() + " " + platform.release() oliver@167: oliver@167: # add these to new data to log oliver@167: global log_file_lock oliver@167: log_file_name = os.path.join(Environment('OpenSecurity').log_path, 'vm_new.log') oliver@167: log_file_lock.acquire() oliver@167: pickle.dump(args, open(log_file_name, 'ab')) oliver@167: log_file_lock.release() oliver@167: oliver@167: return "Ok" oliver@167: oliver@167: oliver@193: class os_message: oliver@193: oliver@193: """OpenSecurity '/message' handler. oliver@193: oliver@193: This is called on GET /message?text=TEXTi&timeout=TIMEOUT. oliver@193: This pops up the typical tray message (like a ballon on windows). oliver@193: """ oliver@193: oliver@193: def POST(self): oliver@193: return self.GET() oliver@193: oliver@193: def GET(self): oliver@193: oliver@193: # pick the arguments oliver@193: args = web.input() oliver@193: oliver@193: # we _need_ a text oliver@193: if not "text" in args: oliver@193: raise web.badrequest('no text given') oliver@193: oliver@193: timeout = 5000 oliver@193: if "timeout" in args: oliver@193: try: oliver@193: timeout=int(args.timeout) oliver@193: except: oliver@193: pass oliver@193: oliver@193: if tray_icon is None: oliver@193: raise web.badrequest('unable to access tray icon instance') oliver@193: oliver@193: tray_icon.showMessage('OpenSecurity', args.text, QtGui.QSystemTrayIcon.Information, timeout) oliver@193: return 'Shown: ' + args.text + ' timeout: ' + str(timeout) + ' ms' oliver@193: oliver@193: oliver@167: class os_notification: oliver@167: oliver@167: """OpenSecurity '/notification' handler. oliver@167: oliver@167: This is called on GET /notification?msgtype=TYPE&text=TEXT. oliver@167: This will pop up an OpenSecurity notifcation window oliver@167: """ oliver@178: oliver@178: def POST(self): oliver@178: return self.GET() oliver@167: oliver@167: def GET(self): oliver@167: oliver@167: # pick the arguments oliver@167: args = web.input() oliver@167: oliver@167: # we _need_ a type oliver@167: if not "msgtype" in args: oliver@167: raise web.badrequest('no msgtype given') oliver@167: oliver@167: if not args.msgtype in ['information', 'warning', 'critical']: oliver@167: raise web.badrequest('Unknown value for msgtype') oliver@167: oliver@167: # we _need_ a text oliver@167: if not "text" in args: oliver@167: raise web.badrequest('no text given') oliver@167: oliver@167: # invoke the user dialog as a subprocess oliver@208: dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw') oliver@167: process_command = [sys.executable, dlg_image, 'notification-' + args.msgtype, args.text] oliver@167: process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE) oliver@167: oliver@167: return "Ok" oliver@167: oliver@167: oliver@167: class os_password: oliver@167: oliver@167: """OpenSecurity '/password' handler. oliver@167: oliver@167: This is called on GET /password?text=TEXT. oliver@167: Ideally this should pop up a user dialog to insert his oliver@167: password based device name. oliver@167: """ oliver@167: oliver@167: def GET(self): oliver@167: oliver@167: # pick the arguments oliver@167: args = web.input() oliver@167: oliver@167: # we _need_ a text oliver@167: if not "text" in args: oliver@167: raise web.badrequest('no text given') oliver@167: oliver@167: # remember remote ip oliver@167: remote_ip = web.ctx.environ['REMOTE_ADDR'] oliver@167: oliver@167: # create the process which queries the user oliver@167: dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw') oliver@167: process_command = [sys.executable, dlg_image, 'password', args.text] oliver@167: oliver@167: # run process result handling in seprate thread (not to block main one) oliver@208: bouncer = ProcessResultBouncer(process_command, remote_ip, '/password') oliver@167: bouncer.start() oliver@167: oliver@167: return 'user queried for password' oliver@167: oliver@193: oliver@193: BarthaM@176: def genNetworkDrive(): BarthaM@176: logical_drives = getLogicalDrives() BarthaM@176: logger.info("Used logical drive letters: "+ str(logical_drives).strip('[]') ) BarthaM@176: drives = list(map(chr, range(68, 91))) BarthaM@176: for drive in drives: BarthaM@176: if drive not in logical_drives: BarthaM@176: return drive BarthaM@176: return None BarthaM@176: BarthaM@176: def getLogicalDrives(): BarthaM@176: drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives() BarthaM@176: drives = list(itertools.compress(string.ascii_uppercase, map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1]))) BarthaM@176: return drives BarthaM@176: BarthaM@176: def getNetworkPath(drive): BarthaM@176: return win32wnet.WNetGetConnection(drive+':') BarthaM@176: BarthaM@176: def getDriveType(drive): BarthaM@176: return ctypes.cdll.kernel32.GetDriveTypeW(u"%s:\\"%drive) BarthaM@176: BarthaM@176: def getNetworkDrive(path): BarthaM@176: for drive in getLogicalDrives(): BarthaM@176: #if is a network drive BarthaM@176: if getDriveType(drive) == 4: BarthaM@176: network_path = getNetworkPath(drive) BarthaM@176: if path in network_path: BarthaM@176: return drive BarthaM@176: return None oliver@178: oliver@178: def mapDrive(drive, networkPath, user, password): oliver@178: if (os.path.exists(networkPath)): BarthaM@182: logger.debug(networkPath + " is found...") BarthaM@182: logger.debug("Trying to map " + networkPath + " on to " + drive + " .....") oliver@178: try: oliver@178: win32wnet.WNetAddConnection2(win32netcon.RESOURCETYPE_DISK, drive, networkPath, None, user, password) oliver@178: except: BarthaM@182: logger.error("Unexpected error...") oliver@178: return 1 BarthaM@182: logger.info("Mapping successful") oliver@178: return 0 oliver@178: else: BarthaM@182: logger.error("Network path unreachable...") oliver@178: return 1 BarthaM@184: BarthaM@184: mount_lock = threading.Lock() oliver@178: oliver@167: # handles netumount request oliver@167: class MountNetworkDriveHandler(threading.Thread): BarthaM@176: networkPath = None BarthaM@176: def __init__(self, net_path): oliver@167: threading.Thread.__init__(self) oliver@167: self.networkPath = net_path oliver@167: oliver@167: def run(self): oliver@167: #Check for network resource availability oliver@178: retry = 20 oliver@167: while not os.path.exists(self.networkPath): oliver@178: if retry == 0: oliver@178: break oliver@178: logger.info("Path not accessible: " + self.networkPath + " retrying") oliver@167: time.sleep(1) oliver@167: retry-=1 BarthaM@184: with mount_lock: BarthaM@184: drive = genNetworkDrive() BarthaM@184: if not drive: BarthaM@184: logger.error("Failed to assign drive letter for: " + self.networkPath) BarthaM@184: return 1 BarthaM@184: else: BarthaM@184: logger.info("Assigned drive " + drive + " to " + self.networkPath) BarthaM@184: BarthaM@184: #Check for drive availability BarthaM@184: drive = drive+':' BarthaM@184: if os.path.exists(drive): BarthaM@184: logger.error("Drive letter is already in use: " + drive) BarthaM@184: return 1 BarthaM@184: BarthaM@184: return mapDrive(drive, self.networkPath, "", "") oliver@167: oliver@167: class os_netmount: oliver@167: oliver@167: """OpenSecurity '/netmount' handler""" oliver@167: oliver@167: def GET(self): oliver@167: # pick the arguments oliver@167: args = web.input() oliver@167: oliver@167: # we _need_ a net_resource oliver@167: if not "net_resource" in args: oliver@167: raise web.badrequest('no net_resource given') oliver@167: BarthaM@176: driveHandler = MountNetworkDriveHandler(args['net_resource']) oliver@167: driveHandler.start() oliver@167: driveHandler.join(None) oliver@167: return 'Ok' oliver@178: oliver@178: def unmapDrive(drive, force=0): BarthaM@182: logger.debug("drive in use, trying to unmap...") oliver@178: if force == 0: BarthaM@182: logger.debug("Executing un-forced call...") oliver@178: oliver@178: try: oliver@178: win32wnet.WNetCancelConnection2(drive, 1, force) BarthaM@182: logger,info(drive + "successfully unmapped...") oliver@178: return 0 oliver@178: except: BarthaM@182: logger.error("Unmap failed, try again...") oliver@178: return 1 oliver@178: oliver@167: # handles netumount request oliver@167: class UmountNetworkDriveHandler(threading.Thread): BarthaM@176: networkPath = None oliver@167: running = True oliver@167: BarthaM@176: def __init__(self, path): oliver@167: threading.Thread.__init__(self) BarthaM@176: self.networkPath = path oliver@167: oliver@167: def run(self): oliver@167: while self.running: BarthaM@176: drive = getNetworkDrive(self.networkPath) BarthaM@176: if not drive: BarthaM@176: logger.info("Failed to retrieve drive letter for: " + self.networkPath + ". Successfully deleted or missing.") oliver@167: self.running = False oliver@167: else: BarthaM@176: drive = drive+':' BarthaM@176: logger.info("Unmounting drive " + drive + " for " + self.networkPath) oliver@178: result = unmapDrive(drive, force=1) oliver@178: if result != 0: oliver@167: continue oliver@167: oliver@167: oliver@167: class os_netumount: oliver@167: oliver@167: """OpenSecurity '/netumount' handler""" oliver@167: oliver@167: def GET(self): oliver@167: # pick the arguments oliver@167: args = web.input() oliver@167: BarthaM@176: # we _need_ a net_resource BarthaM@176: if not "net_resource" in args: BarthaM@176: raise web.badrequest('no net_resource given') oliver@167: BarthaM@176: driveHandler = UmountNetworkDriveHandler(args['net_resource']) oliver@167: driveHandler.start() oliver@167: driveHandler.join(None) oliver@167: return 'Ok' BarthaM@176: BarthaM@176: class os_netcleanup: oliver@167: BarthaM@176: """OpenSecurity '/netcleanup' handler""" BarthaM@176: BarthaM@176: def GET(self): BarthaM@176: # pick the arguments BarthaM@176: args = web.input() BarthaM@176: BarthaM@176: # we _need_ a net_resource BarthaM@176: if not "hostonly_ip" in args: BarthaM@181: raise web.badrequest('no hostonly_ip given') BarthaM@176: BarthaM@176: ip = args['hostonly_ip'] BarthaM@176: ip = ip[:ip.rindex('.')] BarthaM@176: drives = getLogicalDrives() BarthaM@176: for drive in drives: BarthaM@176: # found network drive BarthaM@176: if getDriveType(drive) == 4: BarthaM@176: path = getNetworkPath(drive) BarthaM@176: if ip in path: BarthaM@176: driveHandler = UmountNetworkDriveHandler(path) BarthaM@176: driveHandler.start() BarthaM@176: driveHandler.join(None) oliver@167: oliver@167: class os_root: oliver@167: oliver@167: """OpenSecurity '/' handler""" oliver@167: oliver@167: def GET(self): oliver@167: oliver@167: res = "OpenSecurity-Client RESTFul Server { \"version\": \"%s\" }" % opensecurity.__version__ oliver@167: oliver@167: # add some sample links oliver@167: res = res + """ oliver@167: oliver@167: USAGE EXAMPLES: oliver@167: oliver@167: Request a password: oliver@167: (copy paste this into your browser's address field after the host:port) oliver@167: oliver@167: /password?text=Give+me+a+password+for+device+%22My+USB+Drive%22+(ID%3A+32090-AAA-X0) oliver@167: oliver@167: (eg.: http://127.0.0.1:8090/password?text=Give+me+a+password+for+device+%22My+USB+Drive%22+(ID%3A+32090-AAA-X0)) oliver@167: NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window. oliver@167: oliver@167: oliver@167: Request a combination of user and password: oliver@167: (copy paste this into your browser's address field after the host:port) oliver@167: oliver@167: /credentials?text=Tell+the+NSA+which+credentials+to+use+in+order+to+avoid+hacking+noise+on+wire. oliver@167: oliver@167: (eg.: http://127.0.0.1:8090/credentials?text=Tell+the+NSA+which+credentials+to+use+in+order+to+avoid+hacking+noise+on+wire.) oliver@167: NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window. oliver@167: oliver@167: oliver@167: Request a combination of password and keyfile: oliver@167: (copy paste this into your browser's address field after the host:port) oliver@167: oliver@167: /keyfile?text=Your%20private%20RSA%20Keyfile%3A oliver@167: oliver@167: (eg.: http://127.0.0.1:8090//keyfile?text=Your%20private%20RSA%20Keyfile%3A) oliver@167: NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window. oliver@167: oliver@167: oliver@167: Start a Browser: oliver@167: (copy paste this into your browser's address field after the host:port) oliver@167: oliver@167: /application?vm=Debian+7&app=Browser oliver@167: oliver@167: (e.g. http://127.0.0.1:8090/application?vm=Debian+7&app=Browser) oliver@167: """ oliver@167: oliver@167: return res oliver@167: oliver@167: oliver@167: class ProcessResultBouncer(threading.Thread): oliver@167: oliver@167: """A class to post the result of a given process - assuming it to be in JSON - to a REST Api.""" oliver@167: oliver@208: def __init__(self, process_command, remote_ip, resource): oliver@167: oliver@167: """ctor""" oliver@167: oliver@167: threading.Thread.__init__(self) oliver@208: self._process_command = process_command oliver@167: self._remote_ip = remote_ip oliver@167: self._resource = resource oliver@167: oliver@167: oliver@167: def stop(self): oliver@167: oliver@167: """stop thread""" oliver@167: self.running = False oliver@167: oliver@167: oliver@167: def run(self): oliver@167: oliver@167: """run the thread""" oliver@167: oliver@208: while True: oliver@208: oliver@208: # invoke the user dialog as a subprocess oliver@208: process = subprocess.Popen(self._process_command, shell = False, stdout = subprocess.PIPE) oliver@208: result = process.communicate()[0] oliver@208: if process.returncode != 0: oliver@208: print 'user request has been aborted.' oliver@208: return oliver@208: oliver@208: # all ok, tell send request back appropriate destination oliver@208: try: oliver@208: j = json.loads(result) oliver@208: except: oliver@208: print 'error in password parsing' oliver@208: return oliver@208: oliver@208: # by provided a 'data' we turn this into a POST statement oliver@208: url_addr = 'http://' + self._remote_ip + ':58080' + self._resource oliver@208: req = urllib2.Request(url_addr, urllib.urlencode(j)) oliver@208: try: oliver@208: res = urllib2.urlopen(req) oliver@208: if res.getcode() == 200: oliver@208: return oliver@208: oliver@208: except urllib2.HTTPError as e: oliver@208: oliver@208: # invoke the user dialog as a subprocess oliver@208: dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw') oliver@209: dlg_process_command = [sys.executable, dlg_image, 'notification-critical', 'Error is
Code: {0!s}
Reason: {1}
{2}'.format(e.code, e.reason, e.read())] oliver@208: dlg_process = subprocess.Popen(dlg_process_command, shell = False, stdout = subprocess.PIPE) oliver@208: dlg_process.communicate()[0] oliver@167: oliver@167: oliver@167: class RESTServerThread(threading.Thread): oliver@167: oliver@167: """Thread for serving the REST API.""" oliver@167: oliver@167: def __init__(self, port): oliver@167: oliver@167: """ctor""" oliver@167: threading.Thread.__init__(self) oliver@167: self._port = port oliver@167: oliver@167: def stop(self): oliver@167: oliver@167: """stop thread""" oliver@167: self.running = False oliver@167: oliver@167: oliver@167: def run(self): oliver@167: oliver@167: """run the thread""" oliver@167: _serve(self._port) oliver@167: oliver@167: oliver@167: oliver@167: def is_already_running(port = 8090): oliver@167: oliver@167: """check if this is started twice""" oliver@167: oliver@167: try: oliver@167: s = socket.create_connection(('127.0.0.1', port), 0.5) oliver@167: except: oliver@167: return False oliver@167: oliver@167: return True oliver@167: oliver@167: oliver@167: def _bounce_vm_logs(): oliver@167: oliver@167: """grab all logs from the VMs and push them to the log servers""" oliver@167: oliver@167: global log_file_lock oliver@167: oliver@167: # pick the highest current number oliver@167: cur = 0 oliver@167: for f in glob.iglob(os.path.join(Environment('OpenSecurity').log_path, 'vm_cur.log.*')): oliver@167: try: oliver@167: n = f.split('.')[-1:][0] oliver@167: if cur < int(n): oliver@167: cur = int(n) oliver@167: except: oliver@167: pass oliver@167: oliver@167: cur = cur + 1 oliver@167: oliver@167: # first add new vm logs to our existing one: rename the log file oliver@167: log_file_name_new = os.path.join(Environment('OpenSecurity').log_path, 'vm_new.log') oliver@167: log_file_name_cur = os.path.join(Environment('OpenSecurity').log_path, 'vm_cur.log.' + str(cur)) oliver@167: log_file_lock.acquire() oliver@167: try: oliver@167: os.rename(log_file_name_new, log_file_name_cur) oliver@167: print('new log file: ' + log_file_name_cur) oliver@167: except: oliver@167: pass oliver@167: log_file_lock.release() oliver@167: oliver@167: # now we have a list of next log files to dump oliver@167: log_files = glob.glob(os.path.join(Environment('OpenSecurity').log_path, 'vm_cur.log.*')) oliver@167: log_files.sort() oliver@167: for log_file in log_files: oliver@167: oliver@167: try: oliver@167: f = open(log_file, 'rb') oliver@167: while True: oliver@167: l = pickle.load(f) oliver@167: _push_log(l) oliver@167: oliver@167: except EOFError: oliver@167: try: BarthaM@185: f.close() oliver@167: os.remove(log_file) oliver@167: except: oliver@167: logger.warning('tried to delete log file (pushed to EOF) "' + log_file + '" but failed') oliver@167: oliver@167: except: oliver@167: logger.warning('encountered error while pushing log file "' + log_file + '"') oliver@167: break oliver@167: oliver@167: # start bouncer again ... oliver@167: global log_file_bouncer oliver@167: log_file_bouncer = threading.Timer(5.0, _bounce_vm_logs) oliver@167: log_file_bouncer.start() oliver@167: oliver@167: oliver@167: def _push_log(log): oliver@167: """POST a single log to log server oliver@167: oliver@167: @param log the log POST param oliver@167: """ oliver@167: oliver@168: log_server_url = "http://extern.x-net.at/opensecurity/log" oliver@167: try: oliver@167: key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\OpenSecurity') oliver@167: log_server_url = str(win32api.RegQueryValueEx(key, 'LogServerURL')[0]) oliver@167: win32api.RegCloseKey(key) oliver@167: except: oliver@168: logger.warning('Cannot open Registry HKEY_LOCAL_MACHINE\SOFTWARE\OpenSecurity and get "LogServerURL" value, using default instead') oliver@167: oliver@167: # by provided a 'data' we turn this into a POST statement oliver@167: d = urllib.urlencode(log) oliver@167: req = urllib2.Request(log_server_url, d) oliver@167: urllib2.urlopen(req) oliver@167: logger.debug('pushed log to server: ' + str(log_server_url)) oliver@167: oliver@167: oliver@167: def _serve(port): oliver@167: oliver@167: """Start the REST server""" oliver@167: oliver@167: global server oliver@167: oliver@167: # start the VM-log bouncer timer oliver@167: global log_file_bouncer oliver@167: log_file_bouncer = threading.Timer(5.0, _bounce_vm_logs) oliver@167: log_file_bouncer.start() oliver@167: oliver@167: # trick the web.py server oliver@167: sys.argv = [__file__, str(port)] oliver@167: server = web.application(opensecurity_urls, globals()) oliver@167: server.run() oliver@167: oliver@167: oliver@167: def serve(port = 8090, background = False): oliver@167: oliver@167: """Start serving the REST Api oliver@167: port ... port number to listen on oliver@167: background ... cease into background (spawn thread) and return immediately""" oliver@167: oliver@167: # start threaded or direct version oliver@167: if background == True: oliver@167: t = RESTServerThread(port) oliver@167: t.start() oliver@167: else: oliver@167: _serve(port) oliver@167: oliver@167: def stop(): oliver@167: oliver@167: """Stop serving the REST Api""" oliver@167: oliver@167: global server oliver@167: if server is None: oliver@167: return oliver@167: oliver@167: global log_file_bouncer oliver@167: if log_file_bouncer is not None: oliver@167: log_file_bouncer.cancel() oliver@167: oliver@167: server.stop() oliver@167: oliver@167: # start oliver@167: if __name__ == "__main__": oliver@167: serve() oliver@167: