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