OpenSecurity/bin/vmmanager.pyw
author Bartha Mihai <mihai.bartha@ait.ac.at>
Tue, 28 Oct 2014 15:20:42 +0100
changeset 238 d33edf5c2717
parent 237 63f6861d0ad9
child 240 d7ef04254e9c
permissions -rwxr-xr-x
modified start_browsing script to use the proxy settings
     1 #!/bin/env python
     2 # -*- coding: utf-8 -*-
     3 
     4 # ------------------------------------------------------------
     5 # opensecurityd
     6 #   
     7 # the opensecurityd as RESTful server
     8 #
     9 # Autor: Mihai Bartha, <mihai.bartha@ait.ac.at>
    10 #
    11 # Copyright (C) 2013 AIT Austrian Institute of Technology
    12 # AIT Austrian Institute of Technology GmbH
    13 # Donau-City-Strasse 1 | 1220 Vienna | Austria
    14 # http://www.ait.ac.at
    15 #
    16 # This program is free software; you can redistribute it and/or
    17 # modify it under the terms of the GNU General Public License
    18 # as published by the Free Software Foundation version 2.
    19 # 
    20 # This program is distributed in the hope that it will be useful,
    21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
    22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    23 # GNU General Public License for more details.
    24 # 
    25 # You should have received a copy of the GNU General Public License
    26 # along with this program; if not, write to the Free Software
    27 # Foundation, Inc., 51 Franklin Street, Fifth Floor, 
    28 # Boston, MA  02110-1301, USA.
    29 # ------------------------------------------------------------
    30 
    31 
    32 # ------------------------------------------------------------
    33 # imports
    34 
    35 import os
    36 import os.path
    37 from subprocess import Popen, PIPE, call, STARTUPINFO, _subprocess
    38 import sys
    39 import re
    40 
    41 from cygwin import Cygwin
    42 from environment import Environment
    43 import threading
    44 import time
    45 import string
    46 
    47 import shutil
    48 import stat
    49 import tempfile
    50 from opensecurity_util import logger, import_logger, setupLogger, OpenSecurityException, showTrayMessage
    51 import ctypes
    52 import itertools
    53 import win32api
    54 import win32con
    55 import win32security
    56 import win32wnet
    57 import urllib
    58 import urllib2
    59 import unittest
    60 DEBUG = True
    61 
    62 
    63 new_sdvm_lock = threading.Lock()
    64 backup_lock = threading.Lock()
    65 
    66 class VMManagerException(Exception):
    67     def __init__(self, value):
    68         self.value = value
    69     def __str__(self):
    70         return repr(self.value)
    71 
    72 class USBFilter:
    73     uuid = ""
    74     vendorid = ""
    75     productid = ""
    76     revision = ""
    77     serial = ""
    78     
    79     def __init__(self, uuid, vendorid, productid, revision, serial):
    80         self.uuid = uuid
    81         self.vendorid = vendorid.lower()
    82         self.productid = productid.lower()
    83         self.revision = revision.lower()
    84         self.serial = serial
    85         return
    86     
    87     def __eq__(self, other):
    88         return self.uuid == other.uuid #self.vendorid == other.vendorid and self.productid == other.productid and self.revision == other.revision
    89     
    90     def __hash__(self):
    91         return hash(self.uuid) ^ hash(self.vendorid) ^ hash(self.productid) ^ hash(self.revision) ^ hash(self.serial)
    92     
    93     def __repr__(self):
    94         return "UUID:" + str(self.uuid) + " VendorId = \'" + str(self.vendorid) + "\' ProductId = \'" + str(self.productid) + "\'" + "\' Revision = \'" + str(self.revision) + "\' SerialNumber = \'" + str(self.serial)
    95     
    96     #def __getitem__(self, item):
    97     #    return self.coords[item]
    98 def once(theClass):
    99     theClass.systemProperties = theClass.getSystemProperties()
   100     theClass.machineFolder =    theClass.systemProperties["Default machine folder"]
   101     #theClass.hostonlyIF =       theClass.getHostOnlyIFs()["VirtualBox Host-Only Ethernet Adapter"]
   102     theClass.blacklistedRSD =   theClass.loadRSDBlacklist()
   103     theClass.templateImage =    theClass.machineFolder + '\\' + theClass.vmRootName + '\\' + theClass.vmRootName + '.vmdk'
   104     return theClass
   105     
   106 @once
   107 class VMManager(object):
   108     vmRootName = "SecurityDVM"
   109     systemProperties = None
   110     _instance = None
   111     machineFolder = ''
   112     hostonlyIF = None
   113     
   114     blacklistedRSD = None
   115     status_message = 'Starting up...'
   116     templateImage = None
   117     importHandler = None
   118     updateHandler = None
   119     deviceHandler = None
   120     sdvmFactory = None
   121     vms = dict()
   122     
   123  
   124     def __init__(self):
   125         # only proceed if we have a working background environment
   126         if self.backend_ok():
   127             VMManager.hostonlyIF = self.getHostOnlyIFs()["VirtualBox Host-Only Ethernet Adapter"]
   128             self.cleanup()
   129         else:
   130             logger.critical(self.status_message)
   131     
   132 
   133     @staticmethod
   134     def getInstance():
   135         if VMManager._instance == None:
   136             VMManager._instance = VMManager()
   137         return VMManager._instance
   138     
   139     #list the hostonly IFs exposed by the VBox host
   140     @staticmethod    
   141     def getHostOnlyIFs():
   142         results = Cygwin.vboxExecute('list hostonlyifs')[1]
   143         ifs = dict()
   144         if results=='':
   145             return ifs
   146         items = list( "Name:    " + result for result in results.split('Name:    ') if result != '')
   147         for item in items:
   148             if item != "":
   149                 props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in item.strip().splitlines()))
   150                 ifs[props["Name"]] = props
   151         return ifs
   152     
   153     #list the hostonly IFs exposed by the VBox host
   154     @staticmethod    
   155     def getDHCPServers():
   156         results = Cygwin.vboxExecute('list dhcpservers')[1]
   157         if results=='':
   158             return dict()
   159         items = list( "NetworkName:    " + result for result in results.split('NetworkName:    ') if result != '')
   160         dhcps = dict()   
   161         for item in items:
   162             if item != "":
   163                 props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in item.strip().splitlines()))
   164                 dhcps[props["NetworkName"]] = props
   165         return dhcps 
   166         
   167     # return hosty system properties
   168     @staticmethod
   169     def getSystemProperties():
   170         result = Cygwin.vboxExecute('list systemproperties')
   171         if result[1]=='':
   172             return None
   173         props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result[1].strip().splitlines()))
   174         return props
   175     
   176     # return the folder containing the guest VMs     
   177     def getMachineFolder(self):
   178         return VMManager.machineFolder
   179     
   180     # verifies the hostonly interface and DHCP server settings
   181     def verifyHostOnlySettings(self):
   182         interfaceName = "VirtualBox Host-Only Ethernet Adapter"
   183         networkName = "HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter"
   184         
   185         hostonlyifs = self.getHostOnlyIFs()
   186         if not interfaceName in hostonlyifs.keys():
   187             Cygwin.vboxExecute('hostonlyif create')
   188             hostonlyifs = self.getHostOnlyIFs()
   189             if not interfaceName in hostonlyifs.keys():
   190                 return False
   191         
   192         interface = hostonlyifs[interfaceName]
   193         if interface['VBoxNetworkName'] != networkName or interface['DHCP'] != 'Disabled' or interface['IPAddress'] != '192.168.56.1':
   194             Cygwin.vboxExecute('hostonlyif ipconfig "' + interfaceName + '" --ip 192.168.56.1 --netmask 255.255.255.0')
   195             
   196         dhcpservers = self.getDHCPServers()
   197         if not networkName in dhcpservers.keys():
   198             Cygwin.vboxExecute('dhcpserver add --ifname "' + interfaceName + '" --ip 192.168.56.100 --netmask 255.255.255.0 --lowerip 192.168.56.101 --upperip 192.168.56.254 --enable')
   199             dhcpservers = self.getDHCPServers()
   200             if not networkName in dhcpservers.keys():
   201                 return False
   202             
   203         server = dhcpservers[networkName]
   204         if server['IP'] != '192.168.56.100' or server['NetworkMask'] != '255.255.255.0' or server['lowerIPAddress'] != '192.168.56.101' or server['upperIPAddress'] != '192.168.56.254' or server['Enabled'] != 'Yes':
   205             Cygwin.vboxExecute('VBoxManage dhcpserver modify --netname "' + networkName + '" --ip 192.168.56.100 --netmask 255.255.255.0 --lowerip 192.168.56.101 --upperip 192.168.56.254 --enable')
   206         
   207         return True
   208 
   209     def template_installed(self):
   210         """ check if we do have our root VMs installed """
   211         vms = self.listVMS()
   212         if not self.vmRootName in vms:
   213             self.status_message = 'Unable to locate root SecurityDVM. Please download and setup the initial image.'
   214             return False
   215         return True
   216         
   217     def backend_ok(self):
   218         """check if the backend (VirtualBox) is sufficient for our task"""
   219 
   220         # ensure we have our system props
   221         if VMManager.systemProperties == None:
   222             VMManager.systemProperties = self.getSystemProperties()
   223         if VMManager.systemProperties == None:
   224             self.status_message = 'Failed to get backend system properties. Is Backend (VirtualBox?) installed?'
   225             return False
   226 
   227         # check for existing Extension pack
   228         if not 'Remote desktop ExtPack' in VMManager.systemProperties:
   229             self.status_message = 'No remote desktop extension pack found. Please install the "Oracle VM VirtualBox Extension Pack" from https://www.virtualbox.org/wiki/Downloads.'
   230             return False
   231         if VMManager.systemProperties['Remote desktop ExtPack'] == 'Oracle VM VirtualBox Extension Pack ':
   232             self.status_message = 'Unsure if suitable extension pack is installed. Please install the "Oracle VM VirtualBox Extension Pack" from https://www.virtualbox.org/wiki/Downloads.'
   233             return False
   234 
   235         # check the existing hostOnly network settings and try to reconfigure if faulty
   236         if not self.verifyHostOnlySettings():
   237             return False
   238         
   239         # basically all seems nice and ready to rumble
   240         self.status_message = 'All is ok.'
   241         return True
   242     
   243     def start(self, force = False):
   244         if not force:
   245             if self.importHandler and self.importHandler.isAlive():
   246                 logger.info("Initial update running canceling start.")
   247                 return
   248         
   249             if self.updateHandler and self.updateHandler.isAlive():
   250                 logger.info("Update running canceling start.")
   251                 return
   252         
   253         self.stop()
   254         Cygwin.allowExec()
   255         if self.backend_ok() and self.template_installed():
   256             self.sdvmFactory = SDVMFactory(self)
   257             self.sdvmFactory.start()
   258             self.deviceHandler = DeviceHandler(self)
   259             self.deviceHandler.start()
   260     
   261     def stop(self):
   262         Cygwin.denyExec()
   263         if self.sdvmFactory != None:
   264             self.sdvmFactory.stop()
   265             self.sdvmFactory.join()
   266             self.sdvmFactory = None
   267         if self.deviceHandler != None:
   268             self.deviceHandler.stop()
   269             self.deviceHandler.join()
   270             self.deviceHandler = None
   271         
   272         Cygwin.allowExec()
   273 
   274     def cleanup(self):
   275         self.stop()
   276         Cygwin.allowExec()
   277         ip = self.getHostOnlyIP(None)
   278         try:
   279             result = urllib2.urlopen('http://127.0.0.1:8090/netcleanup?'+'hostonly_ip='+ip).readline()
   280         except urllib2.URLError:
   281             logger.info("Network drive cleanup all skipped. OpenSecurity Tray client not started yet.")
   282             
   283         for vm in self.listSDVM():
   284             self.poweroffVM(vm)
   285             self.removeVM(vm)
   286         self.vms = dict()
   287         
   288     # list all existing VMs registered with VBox
   289     def listVMS(self):
   290         result = Cygwin.vboxExecute('list vms')[1]
   291         vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   292         return vms
   293     
   294     # list running VMs
   295     def listRunningVMS(self):
   296         result = Cygwin.vboxExecute('list runningvms')[1]
   297         vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   298         return vms
   299     
   300     # list existing SDVMs
   301     def listSDVM(self):
   302         vms = self.listVMS()
   303         svdms = []
   304         for vm in vms:
   305             if vm.startswith(self.vmRootName) and vm != self.vmRootName:
   306                 svdms.append(vm)
   307         return svdms
   308     
   309     # generate valid (not already existing SDVM name). necessary for creating a new VM
   310     def genSDVMName(self):
   311         vms = self.listVMS()
   312         for i in range(0,999):
   313             if(not self.vmRootName+str(i) in vms):
   314                 return self.vmRootName+str(i)
   315         return ''
   316     
   317     @staticmethod
   318     def loadRSDBlacklist():
   319         blacklist = dict()
   320         try:
   321             fo = open(Environment('OpenSecurity').prefix_path +"\\bin\\blacklist.usb", "r")
   322         except IOError:
   323             logger.error("Could not open RSD blacklist file.")
   324             return blacklist
   325         
   326         lines = fo.readlines()
   327         for line in lines:
   328             if line != "":  
   329                 parts = line.strip().split(' ')
   330                 blacklist[parts[0].lower()] = parts[1].lower()
   331         return blacklist
   332          
   333     @staticmethod
   334     def isBlacklisted(device):
   335         if VMManager.blacklistedRSD:
   336             blacklisted = device.vendorid.lower() in VMManager.blacklistedRSD.keys() and device.productid.lower() == VMManager.blacklistedRSD[device.vendorid]
   337             return blacklisted
   338         return False 
   339     
   340     # check if the device is mass storage type
   341     @staticmethod
   342     def isMassStorageDevice(device):
   343         vidkey = None
   344         devinfokey = None
   345         value = ""
   346         try:
   347             keyname = 'SYSTEM\CurrentControlSet\Enum\USB' + '\VID_' + device.vendorid+'&'+'PID_'+ device.productid
   348             vidkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, keyname)
   349             devinfokeyname = win32api.RegEnumKey(vidkey, 0)
   350             win32api.RegCloseKey(vidkey)
   351     
   352             devinfokey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, keyname+'\\'+devinfokeyname)
   353             value = win32api.RegQueryValueEx(devinfokey, 'SERVICE')[0]
   354             win32api.RegCloseKey(devinfokey)
   355         except Exception as ex:
   356             logger.error('Error reading registry.Exception details: %s' %ex)
   357         finally:
   358             if vidkey is not None:
   359                 win32api.RegCloseKey(vidkey)
   360             if devinfokey is not None:
   361                 win32api.RegCloseKey(devinfokey)
   362         
   363         return 'USBSTOR' in value
   364     
   365     # return the RSDs connected to the host
   366     @staticmethod
   367     def getExistingRSDs():
   368         results = Cygwin.vboxExecute('list usbhost')[1]
   369         results = results.split('Host USB Devices:')[1].strip()
   370         
   371         items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   372         rsds = dict()   
   373         for item in items:
   374             props = dict()
   375             for line in item.splitlines():     
   376                 if line != "":         
   377                     k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   378                     props[k] = v
   379             
   380             uuid = re.search(r"(?P<uuid>[0-9A-Fa-f\-]+)", props['UUID']).groupdict()['uuid']
   381             vid = re.search(r"\((?P<vid>[0-9A-Fa-f]+)\)", props['VendorId']).groupdict()['vid']
   382             pid = re.search(r"\((?P<pid>[0-9A-Fa-f]+)\)", props['ProductId']).groupdict()['pid']
   383             rev = re.search(r"\((?P<rev>[0-9A-Fa-f]+)\)", props['Revision']).groupdict()['rev']
   384             serial = None
   385             if 'SerialNumber' in props.keys():
   386                 serial = re.search(r"(?P<ser>[0-9A-Fa-f]+)", props['SerialNumber']).groupdict()['ser']
   387             usb_filter = USBFilter( uuid, vid, pid, rev, serial)
   388              
   389             if VMManager.isMassStorageDevice(usb_filter) and not VMManager.isBlacklisted(usb_filter):
   390                 rsds[uuid] = usb_filter
   391                 logger.debug(usb_filter)
   392         return rsds
   393     
   394    
   395     # return the attached USB device as usb descriptor for an existing VM 
   396     def getAttachedRSD(self, vm_name):
   397         props = self.getVMInfo(vm_name)
   398         keys = set(['USBAttachedUUID1', 'USBAttachedVendorId1', 'USBAttachedProductId1', 'USBAttachedRevision1', 'USBAttachedSerialNumber1'])
   399         keyset = set(props.keys())
   400         usb_filter = None
   401         if keyset.issuperset(keys):
   402             usb_filter = USBFilter(props['USBAttachedUUID1'], props['USBAttachedVendorId1'], props['USBAttachedProductId1'], props['USBAttachedRevision1'], props['USBAttachedSerialNumber1'])
   403         return usb_filter
   404         
   405     # return the RSDs attached to all existing SDVMs
   406     def getAttachedRSDs(self):
   407         vms = self.listSDVM()
   408         attached_devices = dict()
   409         for vm in vms:
   410             rsd_filter = self.getAttachedRSD(vm)
   411             if rsd_filter != None:
   412                 attached_devices[vm] = rsd_filter
   413         return attached_devices
   414     
   415     # attach removable storage device to VM by provision of filter
   416     def attachRSD(self, vm_name, rsd_filter):
   417         #return Cygwin.vboxExecute('usbfilter add 0 --target ' + vm_name + ' --name OpenSecurityRSD --vendorid ' + rsd_filter.vendorid + ' --productid ' + rsd_filter.productid + ' --revision ' + rsd_filter.revision + ' --serialnumber ' + rsd_filter.serial)
   418         return Cygwin.vboxExecute('controlvm ' + vm_name + ' usbattach ' + rsd_filter.uuid )
   419     
   420     # detach removable storage from VM by 
   421     def detachRSD(self, vm_name, rsd_filter):
   422         #return Cygwin.vboxExecute('usbfilter remove 0 --target ' + vm_name)
   423         return Cygwin.vboxExecute('controlvm ' + vm_name + ' usbdetach ' + rsd_filter.uuid )
   424         
   425     # configures hostonly networking and DHCP server. requires admin rights
   426     def configureHostNetworking(self):
   427         #cmd = 'vboxmanage list hostonlyifs'
   428         #Cygwin.vboxExecute(cmd)
   429         #cmd = 'vboxmanage hostonlyif remove \"VirtualBox Host-Only Ethernet Adapter\"'
   430         #Cygwin.vboxExecute(cmd)
   431         #cmd = 'vboxmanage hostonlyif create'
   432         #Cygwin.vboxExecute(cmd)
   433         Cygwin.vboxExecute('hostonlyif ipconfig \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.1 --netmask 255.255.255.0')
   434         #cmd = 'vboxmanage dhcpserver add'
   435         #Cygwin.vboxExecute(cmd)
   436         Cygwin.vboxExecute('dhcpserver modify --ifname \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.100 --netmask 255.255.255.0 --lowerip 192.168.56.101 --upperip 192.168.56.200')
   437     
   438     def isSDVMExisting(self, vm_name):
   439         sdvms = self.listSDVM()
   440         return vm_name in sdvms
   441         
   442     #create new virtual machine instance based on template vm named SecurityDVM (\SecurityDVM\SecurityDVM.vmdk)
   443     def createVM(self, vm_name):
   444         if self.isSDVMExisting(vm_name):
   445             return
   446         #remove eventually existing SDVM folder
   447         machineFolder = Cygwin.cygPath(VMManager.machineFolder)
   448         Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '\\\"')
   449         Cygwin.vboxExecute('createvm --name ' + vm_name + ' --ostype Debian --register')
   450         Cygwin.vboxExecute('modifyvm ' + vm_name + ' --memory 768 --vram 10 --cpus 1 --usb on --usbehci on --nic1 hostonly --hostonlyadapter1 \"' + self.hostonlyIF['Name'] + '\" --nic2 nat')
   451         Cygwin.vboxExecute('storagectl ' + vm_name + ' --name SATA --add sata --portcount 2')
   452 
   453     #create new SecurityDVM with automatically generated name from template (thread safe)        
   454     def newSDVM(self):
   455         with new_sdvm_lock:
   456             vm_name = self.genSDVMName()
   457             self.createVM(vm_name)
   458         return vm_name
   459     
   460     #VMManager.machineFolder + '\SecurityDVM\SecurityDVM.vmdk
   461     # attach storage image to controller
   462     def attachVDisk(self, vm_name, vdisk_controller, vdisk_port, vdisk_device, vdisk_image):
   463         if self.isVDiskAttached(vm_name, vdisk_controller, vdisk_port, vdisk_device):
   464             self.detachVDisk(vm_name, vdisk_controller, vdisk_port, vdisk_device)
   465         Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl '+ vdisk_controller + ' --port ' + vdisk_port + ' --device ' + vdisk_device + ' --type hdd --medium "'+ vdisk_image + '"')
   466     
   467     # return true if storage is attached 
   468     def isVDiskAttached(self, vm_name, vdisk_controller, vdisk_port, vdisk_device):
   469         info = self.getVMInfo(vm_name)
   470         return (info[vdisk_controller+'-'+vdisk_port+'-'+vdisk_device] != 'none')
   471     
   472     # detach storage from controller
   473     def detachVDisk(self, vm_name, vdisk_controller, vdisk_port, vdisk_device):
   474         if self.isVDiskAttached(vm_name, vdisk_controller, vdisk_port, vdisk_device):
   475             Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl ' + vdisk_controller + ' --port ' + vdisk_port + ' --device ' + vdisk_device + ' --medium none')
   476     
   477     # modify type of the vdisk_image
   478     def changeVDiskType(self, vdisk_image, storage_type):
   479         Cygwin.vboxExecute('modifyhd "' + vdisk_image + '" --type ' + storage_type)
   480         
   481     # grab VM storage controller, port and device for vdisk image name
   482     def getVDiskController(self, vm_name, image_name = '.vmdk'):
   483         vm_description = self.getVMInfo(vm_name)
   484         vdisk_controller = None
   485         for key, value in vm_description.iteritems():
   486             if image_name in value:
   487                 vdisk_controller = key
   488                 break
   489         return vdisk_controller
   490     
   491     # return attached vmdk image name containing image_name 
   492     def getVDiskImage(self, vm_name, image_name = '.vmdk'):
   493         vmInfo = self.getVMInfo(vm_name)
   494         vdisk_image = None
   495         for value in vmInfo.values():
   496             if image_name in value:
   497                 break
   498         return vdisk_image 
   499         
   500     @staticmethod    
   501     def getVDiskImages():
   502         results = Cygwin.vboxExecute('list hdds')[1]
   503         results = results.replace('Parent UUID', 'Parent')
   504         items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   505         
   506         snaps = dict()   
   507         for item in items:
   508             props = dict()
   509             for line in item.splitlines():
   510                 if line != "":         
   511                     k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   512                     props[k] = v;
   513             snaps[props['UUID']] = props
   514         return snaps
   515     
   516     @staticmethod 
   517     def getVDiskUUID(vdisk_image):
   518         images = VMManager.getVDiskImages()
   519         # find template uuid
   520         template_uuid = None
   521         for hdd in images.values():
   522             if hdd['Location'] == vdisk_image:
   523                 template_uuid = hdd['UUID']
   524                 break
   525         return template_uuid
   526     
   527     def removeSnapshots(self, imageUUID):
   528         snaps = self.getVDiskImages()
   529         # remove snapshots 
   530         for hdd in snaps.values():
   531             if hdd['Parent'] == imageUUID:
   532                 snapshotUUID = hdd['UUID']
   533                 self.removeImage(snapshotUUID)
   534                 
   535     def removeImage(self, imageUUID):
   536         logger.debug('removing snapshot ' + imageUUID)
   537         Cygwin.vboxExecute('closemedium disk {' + imageUUID + '} --delete')
   538         # parse result 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
   539     
   540     #remove VM from the system. should be used on VMs returned by listSDVMs    
   541     def removeVM(self, vm_name):
   542         logger.info('Removing ' + vm_name)
   543         Cygwin.vboxExecute('unregistervm ' + vm_name + ' --delete')
   544         #try to close medium if still existing
   545         #Cygwin.vboxExecute('closemedium disk {' + hdd['UUID'] + '} --delete')
   546         self.removeVMFolder(vm_name)
   547     
   548     def removeVMFolder(self, vm_name):
   549         machineFolder = Cygwin.cygPath(VMManager.machineFolder)
   550         Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '\\' + vm_name + '\\\"')
   551     
   552     # start VM
   553     def startVM(self, vm_name):
   554         logger.info('Starting ' +  vm_name)
   555         Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False')
   556         result = Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless' )
   557         while 'successfully started' not in result[1]:
   558             logger.error("Failed to start SDVM: " + vm_name + " retrying")
   559             logger.error("Command returned:\n" + result[2])
   560             time.sleep(1)
   561             result = Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless')
   562         return result[0]
   563     
   564     # return wether VM is running or not
   565     def isVMRunning(self, vm_name):
   566         return vm_name in self.listRunningVMS()    
   567     
   568     # stop VM
   569     def stopVM(self, vm_name):
   570         logger.info('Sending shutdown signal to ' + vm_name)
   571         Cygwin.sshExecute( '"sudo shutdown -h now"', self.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(VMManager.machineFolder) + '/' + vm_name + '/dvm_key' )
   572         Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False')
   573     
   574     # stop VM
   575     def hibernateVM(self, vm_name):
   576         logger.info('Sending hibernate-disk signal to ' + vm_name)
   577         Cygwin.sshBackgroundExecute( '"sudo hibernate-disk"', self.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(VMManager.machineFolder) + '/' + vm_name + '/dvm_key', wait_return=False)
   578         Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False')
   579             
   580     # poweroff VM
   581     def poweroffVM(self, vm_name):
   582         if not self.isVMRunning(vm_name):
   583             return
   584         logger.info('Powering off ' + vm_name)
   585         Cygwin.vboxExecute('controlvm ' + vm_name + ' poweroff')
   586         Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False')
   587     
   588     
   589     # return the hostOnly IP for a running guest or the host    
   590     def getHostOnlyIP(self, vm_name):
   591         if vm_name == None:
   592             logger.info('Getting hostOnly IP address for Host')
   593             return VMManager.hostonlyIF['IPAddress']
   594         else:
   595             logger.info('Getting hostOnly IP address ' + vm_name)
   596             result = Cygwin.vboxExecute('guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP')
   597             if result=='':
   598                 return None
   599             result = result[1]
   600             if result.startswith('No value set!'):
   601                 return None
   602             return result[result.index(':')+1:].strip()
   603         
   604     # return the description set for an existing VM
   605     def getVMInfo(self, vm_name):
   606         results = Cygwin.vboxExecute('showvminfo ' + vm_name + ' --machinereadable')[1]
   607         props = dict((k.strip().strip('"'),v.strip().strip('"')) for k,v in (line.split('=', 1) for line in results.splitlines()))
   608         return props
   609     
   610     #generates ISO containing authorized_keys for use with guest VM
   611     def genCertificate(self, vm_name):
   612         machineFolder = Cygwin.cygPath(VMManager.machineFolder)
   613         # remove .ssh folder if exists
   614         Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"')
   615         # remove .ssh folder if exists
   616         Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"')
   617         # create .ssh folder in vm_name
   618         Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"')
   619         # generate dvm_key pair in vm_name / .ssh     
   620         Cygwin.bashExecute('/usr/bin/ssh-keygen -q -t rsa -N \\\"\\\" -C \\\"' + vm_name + '\\\" -f \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\"')
   621         # move out private key
   622         Cygwin.bashExecute('/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\" \\\"' + machineFolder + '/' + vm_name + '\\\"')
   623         # set permissions for private key
   624         Cygwin.bashExecute('/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"')
   625         # rename public key to authorized_keys
   626         Cygwin.bashExecute('/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key.pub\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"')
   627         # set permissions for authorized_keys
   628         Cygwin.bashExecute('/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"')
   629         # generate iso image with .ssh/authorized keys
   630         Cygwin.bashExecute('/usr/bin/genisoimage -J -R -o \\\"' + machineFolder + '/' + vm_name + '/'+ vm_name + '.iso\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"')
   631     
   632     # attaches generated ssh public cert to guest vm
   633     def attachCertificate(self, vm_name):
   634         if self.isCertificateAttached(vm_name):
   635             self.detachCertificate(vm_name)
   636         Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 1 --device 0 --type dvddrive --mtype readonly --medium \"' + VMManager.machineFolder + '\\' + vm_name + '\\'+ vm_name + '.iso\"')
   637     
   638     # return true if storage is attached 
   639     def isCertificateAttached(self, vm_name):
   640         info = self.getVMInfo(vm_name)
   641         return (info['SATA-1-0']!='none')
   642     
   643     # detach storage from controller
   644     def detachCertificate(self, vm_name):
   645         if self.isCertificateAttached(vm_name):
   646             Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 1 --device 0 --type hdd --medium none')
   647             
   648     # wait for machine to come up
   649     def waitStartup(self, vm_name):
   650         #Cygwin.vboxExecute('guestproperty wait ' + vm_name + ' SDVMStarted --timeout ' + str(timeout_ms) + ' --fail-on-timeout', try_count = 60)
   651         started = False
   652         while not started:
   653             result = Cygwin.vboxExecute('guestproperty get ' + vm_name + ' SDVMStarted')[1]
   654             if "Value: True" in result:
   655                 started = True
   656             else:
   657                 time.sleep(3) 
   658         return self.getHostOnlyIP(vm_name)
   659     
   660     # wait for machine to shutdown
   661     def waitShutdown(self, vm_name):
   662         while vm_name in self.listRunningVMS():
   663             time.sleep(1)
   664         return
   665     
   666     #Small function to check if the mentioned location is a directory
   667     def isDirectory(self, path):
   668         result = Cygwin.cmdExecute('dir ' + path + ' | FIND ".."')
   669         return string.find(result[1], 'DIR',)
   670     
   671     def genNetworkDrive(self):
   672         logical_drives = VMManager.getLogicalDrives()
   673         logger.info("Used logical drive letters: "+ str(logical_drives).strip('[]') )
   674         drives = list(map(chr, range(68, 91)))  
   675         for drive in drives:
   676             if drive not in logical_drives:
   677                 return drive
   678             
   679     @staticmethod
   680     def getLogicalDrives():
   681         drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
   682         drives = list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
   683         return drives
   684     
   685     @staticmethod
   686     def getDriveType(drive):
   687         return ctypes.cdll.kernel32.GetDriveTypeW(u"%s:\\"%drive)
   688     
   689     @staticmethod
   690     def getNetworkPath(drive):
   691         return win32wnet.WNetGetConnection(drive+':')
   692     
   693     @staticmethod
   694     def getVolumeInfo(drive):
   695         volumeNameBuffer = ctypes.create_unicode_buffer(1024)
   696         fileSystemNameBuffer = ctypes.create_unicode_buffer(1024)
   697         serial_number = None
   698         max_component_length = None
   699         file_system_flags = None
   700         
   701         rc = ctypes.cdll.kernel32.GetVolumeInformationW(
   702             u"%s:\\"%drive,
   703             volumeNameBuffer,
   704             ctypes.sizeof(volumeNameBuffer),
   705             serial_number,
   706             max_component_length,
   707             file_system_flags,
   708             fileSystemNameBuffer,
   709             ctypes.sizeof(fileSystemNameBuffer)
   710         )
   711         return volumeNameBuffer.value, fileSystemNameBuffer.value
   712     
   713     def getNetworkDrive(self, vm_name):
   714         ip = self.getHostOnlyIP(vm_name)
   715         if ip == None:
   716             logger.error("Failed getting hostonly IP for " + vm_name)
   717             return None
   718         logger.info("Got IP address for " + vm_name + ': ' + ip)
   719         for drive in VMManager.getLogicalDrives():
   720             #if is a network drive
   721             if VMManager.getDriveType(drive) == 4:
   722                 network_path = VMManager.getNetworkPath(drive)
   723                 if ip in network_path:
   724                     return drive
   725         return None
   726     
   727     def getNetworkDrives(self):
   728         ip = self.getHostOnlyIP(None)
   729         if ip == None:
   730             logger.error("Failed getting hostonly IP for system")
   731             return None
   732         logger.info("Got IP address for system: " + ip)
   733         ip = ip[:ip.rindex('.')]
   734         network_drives = dict()
   735         for drive in VMManager.getLogicalDrives():
   736             #if is a network drive
   737             if VMManager.getDriveType(drive) == 4:
   738                 network_path = VMManager.getNetworkPath(drive)
   739                 if ip in network_path:
   740                     network_drives[drive] = network_path  
   741         return network_drives
   742     
   743     # handles browsing request    
   744     def handleBrowsingRequest(self, proxy = None, wpad = None):
   745         showTrayMessage('Starting Secure Browsing...', 7000)
   746         handler = BrowsingHandler(self, proxy, wpad)
   747         handler.start()
   748         return 'ok'
   749     
   750     def getActiveUserName(self):
   751         key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI')
   752         v = str(win32api.RegQueryValueEx(key, 'LastLoggedOnUser')[0])
   753         win32api.RegCloseKey(key)
   754         user_name = win32api.ExpandEnvironmentStrings(v)
   755         return user_name
   756         
   757     def getUserSID(self, user_name):
   758         domain, user = user_name.split("\\")
   759         account_name = win32security.LookupAccountName(domain, user)
   760         if account_name == None:
   761             logger.error("Failed lookup account name for user " + user_name)
   762             return None
   763         sid = win32security.ConvertSidToStringSid(account_name[0])
   764         if sid == None:
   765             logger.error("Failed converting SID for account " + account_name[0])
   766             return None
   767         return sid    
   768     
   769     def getAppDataDirReg(self, sid):    
   770         key = win32api.RegOpenKey(win32con.HKEY_USERS, sid + '\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders')
   771         value, _ = win32api.RegQueryValueEx(key, "AppData")
   772         win32api.RegCloseKey(key)
   773         return value
   774                    
   775     def getAppDataDir(self):
   776         user = self.getActiveUserName()
   777         if user == None:
   778             logger.error("Cannot get active user name")
   779             raise OpenSecurityException("Cannot get active user name")
   780         else:
   781             logger.info('Got active user name ' + user)
   782         sid = self.getUserSID(user)
   783         if sid == None:
   784             logger.error("Cannot get SID for active user")
   785             raise OpenSecurityException("Cannot get SID for active user")
   786         else:
   787             logger.info("Got active user SID " + sid + " for user " + user)
   788             
   789         path = self.getAppDataDirReg(sid)
   790         if path == None:
   791             logger.error("Cannot get AppDataDir for active user")
   792             raise OpenSecurityException("Cannot get AppDataDir for active user")
   793         else:
   794             logger.info("Got AppData dir for user " + user + ': ' + path)
   795         
   796         return Cygwin.cygPath(path)
   797     
   798     #import initial template
   799     def importTemplate(self, image_path):
   800         import_logger.info('Stopping Opensecurity...')
   801         self.stop()
   802         
   803         import_logger.info('Cleaning up system in preparation for import...')
   804         self.cleanup()
   805         
   806         import_logger.info('Removing template SDVM...')
   807         # if template exists
   808         if self.vmRootName in self.listVMS():
   809             # shutdown template if running
   810             self.poweroffVM(self.vmRootName)
   811             # detach and remove VDisk
   812             tmplateUUID = self.getVDiskUUID(self.templateImage)
   813             if tmplateUUID != None:
   814                 logger.debug('Found template VDisk uuid ' + tmplateUUID)
   815                 controller = self.getVDiskController(self.vmRootName)
   816                 if controller:
   817                     controller = controller.split('-')
   818                     self.detachVDisk(self.vmRootName, controller[0], controller[1], controller[2])
   819                 self.removeSnapshots(tmplateUUID)
   820                 self.removeImage(tmplateUUID)
   821             else:
   822                 logger.info('Template uuid not found')
   823             # remove VM    
   824             self.removeVM(self.vmRootName)
   825         # remove template VM folder 
   826         self.removeVMFolder(self.vmRootName)
   827         import_logger.info('Cleanup finished...')
   828         
   829         import_logger.info('Checking privileges...')
   830         result = Cygwin.bashExecute('id -G')
   831         if '544' not in result[1]:
   832             import_logger.debug('Insufficient privileges.')
   833             import_logger.debug("Trying to continue...")
   834         
   835         # check OpenSecurity Initial VM Image
   836         import_logger.debug('Looking for VM image: ' + image_path)
   837         result = os.path.isfile(image_path)
   838       
   839         if not result:
   840             import_logger.debug('Warning: no OpenSecurity Initial Image found.')
   841             import_logger.debug('Please download using the OpenSecurity download tool.')
   842             raise OpenSecurityException('OpenSecurity Initial Image not found.')
   843         logger.debug('Initial VM image: ' + image_path + ' found')
   844         
   845         if not self.template_installed():
   846             import_logger.info('Importing SDVm template: ' + image_path)
   847             Cygwin.vboxExecute('import "' + image_path + '" --vsys 0 --vmname ' + VMManager.vmRootName + ' --unit 12 --disk "' + self.templateImage + '"')
   848         else:
   849             import_logger.info('Found ' + VMManager.vmRootName + ' already present in VBox reusing it.')
   850             import_logger.info('if you want a complete new import please remove the VM first.')
   851             import_logger.info('starting OpenSecurity service...')
   852             return
   853 
   854         # remove unnecessary IDE controller
   855         Cygwin.vboxExecute('storagectl ' + VMManager.vmRootName + ' --name IDE --remove')
   856 
   857         info = self.getVDiskController(VMManager.vmRootName, self.templateImage)
   858         if info:
   859             info = info.split('-')
   860             self.detachVDisk(VMManager.vmRootName, info[0], info[1], info[2])
   861         
   862         self.changeVDiskType(self.templateImage, 'immutable')
   863         self.attachVDisk(VMManager.vmRootName, info[0], info[1], info[2], self.templateImage)
   864         import_logger.info('Initial import finished.')    
   865         
   866     # update template 
   867     def updateTemplate(self):
   868         import_logger.debug('Stopping Opensecurity...')
   869         self.stop()
   870         
   871         import_logger.debug('Cleaning up system in preparation for update...')
   872         self.cleanup()
   873         
   874         import_logger.info('Cleanup finished...')
   875         
   876         # shutdown template if running
   877         self.poweroffVM(self.vmRootName)
   878         
   879         import_logger.info('Starting template VM...')
   880         # check for updates
   881         self.genCertificate(self.vmRootName)
   882         self.attachCertificate(self.vmRootName)
   883 
   884         import_logger.info('Removing snapshots...')        
   885         
   886         self.detachVDisk(self.vmRootName, 'SATA', '0', '0')
   887         templateUUID = self.getVDiskUUID(self.templateImage)
   888         self.removeSnapshots(templateUUID)
   889         
   890         import_logger.info('Setting VDisk image to normal...')
   891         self.changeVDiskType(self.templateImage, 'normal')
   892         self.attachVDisk(self.vmRootName, 'SATA', '0', '0', self.templateImage)
   893         
   894         import_logger.info('Starting VM...')
   895         self.startVM(self.vmRootName)
   896         self.waitStartup(self.vmRootName)
   897         
   898         import_logger.info('Updating components...')
   899         tmp_ip = self.getHostOnlyIP(self.vmRootName)
   900         tmp_machine_folder = Cygwin.cygPath(VMManager.machineFolder)
   901         Cygwin.sshExecute('"sudo apt-get -y update"', tmp_ip, 'osecuser', tmp_machine_folder + '/' + self.vmRootName + '/dvm_key')
   902         Cygwin.sshExecute('"sudo apt-get -y upgrade"', tmp_ip, 'osecuser', tmp_machine_folder + '/' + self.vmRootName + '/dvm_key')
   903         
   904         import_logger.info('Restarting template VM...')
   905         #check if reboot is required
   906         result = Cygwin.sshExecute('"if [ -f /var/run/reboot-required ]; then echo \\\"Yes\\\"; fi"', tmp_ip, 'osecuser', tmp_machine_folder + '/' + self.vmRootName + '/dvm_key')
   907         if "Yes" in result[1]:
   908             self.stopVM(self.vmRootName)
   909             self.waitShutdown(self.vmRootName)
   910             self.startVM(self.vmRootName)
   911             self.waitStartup(self.vmRootName)
   912         
   913         import_logger.info('Stopping template VM...')
   914         self.stopVM(self.vmRootName)
   915         self.waitShutdown(self.vmRootName)
   916         
   917         import_logger.info('Setting VDisk image to immutable...')
   918         self.detachVDisk(self.vmRootName, 'SATA', '0', '0') 
   919         self.changeVDiskType(self.templateImage, 'immutable')
   920         self.attachVDisk(self.vmRootName,  'SATA', '0', '0', self.templateImage)
   921         
   922         import_logger.info('Update template finished...')
   923     
   924     def startInitialImport(self):
   925         if self.importHandler and self.importHandler.isAlive():
   926             import_logger.info("Initial import already running.")
   927             return
   928         self.importHandler = InitialImportHandler(self)
   929         self.importHandler.start()
   930         import_logger.info("Initial import started.")
   931         
   932     def startUpdateTemplate(self):
   933         if self.updateHandler and self.updateHandler.isAlive():
   934             import_logger.info("Template update already running.")
   935             return
   936         self.updateHandler = UpdateHandler(self)
   937         self.updateHandler.start()
   938         import_logger.info("Template update started.")
   939         
   940     def createSession(self, browsing=False):
   941         new_sdvm = self.newSDVM()
   942         self.attachVDisk(new_sdvm, 'SATA', '0', '0', self.templateImage)
   943         self.genCertificate(new_sdvm)
   944         self.attachCertificate(new_sdvm)
   945         self.startVM(new_sdvm)
   946         new_ip = self.waitStartup(new_sdvm)
   947         if new_ip == None:
   948             logger.error("Error getting IP address of SDVM. Cleaning up.")
   949             self.poweroffVM(new_sdvm)
   950             self.removeVM(new_sdvm)
   951             return None
   952         else:
   953             logger.info("Got IP address for " + new_sdvm + ' ' + new_ip)
   954             self.vms[new_sdvm] = {'vm_name' : new_sdvm, 'ip_addr' : new_ip, 'used' : False, 'running' : True, 'browsing' : browsing }
   955             if browsing:
   956                 # restore browser settings
   957                 appDataDir = self.getAppDataDir()
   958                 logger.info("Restoring browser settings in AppData dir " + appDataDir)
   959                 # create OpenSecurity settings dir on local machine user home /AppData/Roaming 
   960                 Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + appDataDir + '/OpenSecurity\\\"')
   961                 # create chromium settings dir on local machine if not existing
   962                 Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + appDataDir + '/OpenSecurity/chromium\\\"')
   963                 # create chromium settings dir on remote machine if not existing
   964                 Cygwin.sshExecute('"mkdir -p \\\"/home/osecuser/.config\\\""', new_ip, 'osecuser', Cygwin.cygPath(self.getMachineFolder()) + '/' + new_sdvm + '/dvm_key')
   965                 #restore settings on vm
   966                 self.restoreFile(new_sdvm, new_ip, appDataDir + '/OpenSecurity/chromium', '/home/osecuser/.config/')
   967             return self.vms[new_sdvm]
   968             
   969     def releaseSession(self, vm_name):
   970         del self.vms[vm_name]
   971         self.poweroffVM(vm_name)
   972         self.removeVM(vm_name)
   973         self.sdvmFactory.trigger()
   974         
   975     def getSession(self, browsing = False):
   976         # return first found unused SDVM
   977         for vm in self.vms.values():
   978             if vm['used'] == False and vm['browsing'] == browsing:
   979                 vm['used'] = True
   980                 self.sdvmFactory.trigger()
   981                 return vm
   982         return self.createSession(browsing)
   983         
   984             
   985     def backupFile(self, vm_name, ip_addr, src, dest):
   986         global backup_lock
   987         with backup_lock:
   988             certificate = Cygwin.cygPath(self.getMachineFolder()) + '/' + vm_name + '/dvm_key'
   989             command = '-r -o StrictHostKeyChecking=no -i "' + certificate + '" "osecuser@' + ip_addr + ':' + src + '" "' + dest + '"'
   990             return Cygwin.execute(Cygwin.cygwin_scp, command, wait_return=True, window=False)
   991     
   992     def restoreFile(self, vm_name, ip_addr, src, dest):
   993         certificate = Cygwin.cygPath(self.getMachineFolder()) + '/' + vm_name + '/dvm_key'
   994         command = '-r -o StrictHostKeyChecking=no -i "' + certificate + '" "' + src + '" "osecuser@' + ip_addr + ':' + dest + '"'
   995         return Cygwin.execute(Cygwin.cygwin_scp, command, wait_return=True, window=False)
   996 
   997     
   998         
   999 class SDVMFactory(threading.Thread):
  1000     vmm = None
  1001     running = True
  1002     triggerEv = None
  1003     
  1004     def __init__(self, vmmanager):
  1005         threading.Thread.__init__(self)
  1006         self.vmm = vmmanager
  1007         self.triggerEv = threading.Event()
  1008         
  1009     def run(self):
  1010         while self.running:
  1011             self.triggerEv.clear()            
  1012 
  1013             # find existance of free device and browsing sessions 
  1014             freeDeviceSession = False
  1015             freeBrowsingSession = False
  1016             for vm in self.vmm.vms.values():
  1017                 if vm['used'] == False and vm['browsing'] == False:
  1018                     freeDeviceSession = True
  1019                 if vm['used'] == False and vm['browsing'] == True:
  1020                     freeBrowsingSession = True
  1021             
  1022             #prepare new sessions if none
  1023             if not freeDeviceSession:
  1024                 self.vmm.createSession(False)
  1025             if not freeBrowsingSession:
  1026                 self.vmm.createSession(True)
  1027             self.triggerEv.wait()
  1028     
  1029     def trigger(self, ):
  1030         self.triggerEv.set()
  1031         
  1032     def stop(self):
  1033         self.running = False
  1034         self.triggerEv.set()
  1035         
  1036 #handles browsing session creation 
  1037 class BrowsingHandler(threading.Thread):
  1038     vmm = None
  1039     proxy = None
  1040     wpad = None
  1041     net_resource = None
  1042     ip_addr = None
  1043     vm_name = None
  1044     
  1045     def __init__(self, vmmanager, proxy, wpad):
  1046         threading.Thread.__init__(self)
  1047         self.vmm = vmmanager
  1048         self.proxy = proxy
  1049         self.wpad = wpad
  1050         
  1051     def run(self):
  1052         session = None
  1053         try:
  1054             session = self.vmm.getSession()
  1055             if not session:
  1056                 raise OpenSecurityException("Could not get new SDVM session.")
  1057             
  1058             self.ip_addr = session['ip_addr']
  1059             self.vm_name = session['vm_name']
  1060             
  1061             self.net_resource = '\\\\' + self.ip_addr + '\\Download'
  1062             urllib2.urlopen('http://127.0.0.1:8090/netmount?'+'net_resource='+self.net_resource)#.readline()
  1063             
  1064             if self.wpad:
  1065                 browser = '\\\"/usr/bin/chromium --proxy-pac-url=\\\"'+self.wpad+'\\\"\\\"'
  1066             elif self.proxy:
  1067                 browser = '\\\"export http_proxy='+self.proxy+'; /usr/bin/chromium\\\"'
  1068             else:
  1069                 browser = '\\\"/usr/bin/chromium\\\"'
  1070                 
  1071             Cygwin.sshExecuteX11(browser, self.ip_addr, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + self.vm_name + '/dvm_key')
  1072             appDataDir = self.vmm.getAppDataDir()
  1073             self.vmm.backupFile(self.vm_name, self.ip_addr, '/home/osecuser/.config/chromium', appDataDir + '/OpenSecurity/')
  1074         
  1075         except urllib2.URLError:
  1076             logger.error("Network drive connect failed. OpenSecurity Tray client not running.")
  1077             self.net_resource = None
  1078 
  1079         except:
  1080             logger.info("BrowsingHandler failed. See log for details")
  1081         
  1082         if session:
  1083             if self.net_resource == None:
  1084                 logger.info("Missing browsing SDVM's network share. Skipping disconnect")
  1085             else:
  1086                 try:
  1087                     urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'net_resource='+self.net_resource).readline()
  1088                     self.net_resource = None
  1089                 except urllib2.URLError:
  1090                     logger.error("Network share disconnect failed. OpenSecurity Tray client not running.")
  1091         if self.vm_name:
  1092             self.vmm.releaseSession(self.vm_name)
  1093         
  1094         self.vmm.sdvmFactory.trigger()
  1095 
  1096     
  1097             
  1098                 
  1099 class DeviceHandler(threading.Thread): 
  1100     vmm = None
  1101     existingRSDs = None
  1102     attachedRSDs = None  
  1103     running = True
  1104     def __init__(self, vmmanger): 
  1105         threading.Thread.__init__(self)
  1106         self.vmm = vmmanger
  1107  
  1108     def stop(self):
  1109         self.running = False
  1110         
  1111     def run(self):
  1112         self.existingRSDs = dict()
  1113         self.attachedRSDs = self.vmm.getAttachedRSDs()
  1114         
  1115         while self.running:
  1116             tmp_rsds = self.vmm.getExistingRSDs()
  1117             if tmp_rsds.keys() == self.existingRSDs.keys():
  1118                 logger.debug("Nothing's changed. sleep(3)")
  1119                 time.sleep(3)
  1120                 continue
  1121             
  1122             showTrayMessage('System changed.\nEvaluating...', 7000)
  1123             logger.info("Something's changed")
  1124             
  1125             tmp_attached = self.attachedRSDs     
  1126             for vm_name in tmp_attached.keys():
  1127                 if tmp_attached[vm_name] not in tmp_rsds.values():
  1128                     ip = self.vmm.getHostOnlyIP(vm_name)
  1129                     if ip == None:
  1130                         logger.error("Failed getting hostonly IP for " + vm_name)
  1131                         continue
  1132                     
  1133                     try:
  1134                         net_resource = '\\\\' + ip + '\\USB'
  1135                         result = urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'net_resource='+net_resource).readline()
  1136                     except urllib2.URLError:
  1137                         logger.error("Network drive disconnect failed. OpenSecurity Tray client not running.")
  1138                         continue
  1139                     
  1140                     # detach not necessary as already removed from vm description upon disconnect
  1141                     del self.attachedRSDs[vm_name]
  1142                     self.vmm.releaseSession(vm_name)
  1143                     
  1144             #create new vms for new devices if any
  1145             new_ip = None
  1146             for new_device in tmp_rsds.values():
  1147                 showTrayMessage('Mounting device...', 7000)
  1148                 if (self.attachedRSDs and False) or (new_device not in self.attachedRSDs.values()):
  1149                    
  1150                     session = self.vmm.getSession()
  1151                     if not session:
  1152                         logger.info("Could not get new SDVM session.")
  1153                         continue
  1154                         #raise OpenSecurityException("Could not get new SDVM session.")
  1155                     new_sdvm = session['vm_name']
  1156                     new_ip = session['ip_addr']
  1157                     try:
  1158                         self.vmm.attachRSD(new_sdvm, new_device)
  1159                         self.attachedRSDs[new_sdvm] = new_device
  1160                     except:
  1161                         logger.info("RSD prematurely removed. Cleaning up.")
  1162                         self.vmm.releaseSession(new_sdvm)
  1163                         continue
  1164                     
  1165                     try:
  1166                         net_resource = '\\\\' + new_ip + '\\USB'
  1167                         result = urllib2.urlopen('http://127.0.0.1:8090/netmount?'+'net_resource='+net_resource).readline()
  1168                     except urllib2.URLError:
  1169                         logger.error("Network drive connect failed (tray client not accessible). Cleaning up.")
  1170                         self.vmm.releaseSession(new_sdvm)
  1171                         continue
  1172                     
  1173             self.existingRSDs = tmp_rsds
  1174 
  1175 class UpdateHandler(threading.Thread):
  1176     vmm = None    
  1177     def __init__(self, vmmanager):
  1178         threading.Thread.__init__(self)
  1179         self.vmm = vmmanager
  1180     
  1181     def run(self):
  1182         try:
  1183             self.vmm.updateTemplate()
  1184         except:
  1185             import_logger.info("Update template failed. Refer to service log for details.")
  1186         self.vmm.start(force=True)
  1187     
  1188 class InitialImportHandler(threading.Thread):
  1189     vmm = None    
  1190     def __init__(self, vmmanager):
  1191         threading.Thread.__init__(self)
  1192         self.vmm = vmmanager
  1193     
  1194     def run(self):
  1195         try:
  1196             self.vmm.importTemplate(self.vmm.getMachineFolder() + '\\OsecVM.ova')
  1197             self.vmm.updateTemplate()
  1198         except:
  1199             import_logger.info("Initial import failed. Refer to service log for details.")
  1200         self.vmm.start(force=True)