OpenSecurity/bin/vmmanager.pyw
author BarthaM@N3SIM1218.D03.arc.local
Thu, 02 Oct 2014 18:29:40 +0100
changeset 235 8fd7b197735c
parent 234 216da9017f8f
child 237 63f6861d0ad9
permissions -rwxr-xr-x
added critical session around backupSettings func
     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 
   287     # list all existing VMs registered with VBox
   288     def listVMS(self):
   289         result = Cygwin.vboxExecute('list vms')[1]
   290         vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   291         return vms
   292     
   293     # list running VMs
   294     def listRunningVMS(self):
   295         result = Cygwin.vboxExecute('list runningvms')[1]
   296         vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   297         return vms
   298     
   299     # list existing SDVMs
   300     def listSDVM(self):
   301         vms = self.listVMS()
   302         svdms = []
   303         for vm in vms:
   304             if vm.startswith(self.vmRootName) and vm != self.vmRootName:
   305                 svdms.append(vm)
   306         return svdms
   307     
   308     # generate valid (not already existing SDVM name). necessary for creating a new VM
   309     def genSDVMName(self):
   310         vms = self.listVMS()
   311         for i in range(0,999):
   312             if(not self.vmRootName+str(i) in vms):
   313                 return self.vmRootName+str(i)
   314         return ''
   315     
   316     @staticmethod
   317     def loadRSDBlacklist():
   318         blacklist = dict()
   319         try:
   320             fo = open(Environment('OpenSecurity').prefix_path +"\\bin\\blacklist.usb", "r")
   321         except IOError:
   322             logger.error("Could not open RSD blacklist file.")
   323             return blacklist
   324         
   325         lines = fo.readlines()
   326         for line in lines:
   327             if line != "":  
   328                 parts = line.strip().split(' ')
   329                 blacklist[parts[0].lower()] = parts[1].lower()
   330         return blacklist
   331          
   332     @staticmethod
   333     def isBlacklisted(device):
   334         if VMManager.blacklistedRSD:
   335             blacklisted = device.vendorid.lower() in VMManager.blacklistedRSD.keys() and device.productid.lower() == VMManager.blacklistedRSD[device.vendorid]
   336             return blacklisted
   337         return False 
   338     
   339     # check if the device is mass storage type
   340     @staticmethod
   341     def isMassStorageDevice(device):
   342         vidkey = None
   343         devinfokey = None
   344         value = ""
   345         try:
   346             keyname = 'SYSTEM\CurrentControlSet\Enum\USB' + '\VID_' + device.vendorid+'&'+'PID_'+ device.productid
   347             vidkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, keyname)
   348             devinfokeyname = win32api.RegEnumKey(vidkey, 0)
   349             win32api.RegCloseKey(vidkey)
   350     
   351             devinfokey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, keyname+'\\'+devinfokeyname)
   352             value = win32api.RegQueryValueEx(devinfokey, 'SERVICE')[0]
   353             win32api.RegCloseKey(devinfokey)
   354         except Exception as ex:
   355             logger.error('Error reading registry.Exception details: %s' %ex)
   356         finally:
   357             if vidkey is not None:
   358                 win32api.RegCloseKey(vidkey)
   359             if devinfokey is not None:
   360                 win32api.RegCloseKey(devinfokey)
   361         
   362         return 'USBSTOR' in value
   363     
   364     # return the RSDs connected to the host
   365     @staticmethod
   366     def getExistingRSDs():
   367         results = Cygwin.vboxExecute('list usbhost')[1]
   368         results = results.split('Host USB Devices:')[1].strip()
   369         
   370         items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   371         rsds = dict()   
   372         for item in items:
   373             props = dict()
   374             for line in item.splitlines():     
   375                 if line != "":         
   376                     k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   377                     props[k] = v
   378             
   379             uuid = re.search(r"(?P<uuid>[0-9A-Fa-f\-]+)", props['UUID']).groupdict()['uuid']
   380             vid = re.search(r"\((?P<vid>[0-9A-Fa-f]+)\)", props['VendorId']).groupdict()['vid']
   381             pid = re.search(r"\((?P<pid>[0-9A-Fa-f]+)\)", props['ProductId']).groupdict()['pid']
   382             rev = re.search(r"\((?P<rev>[0-9A-Fa-f]+)\)", props['Revision']).groupdict()['rev']
   383             serial = None
   384             if 'SerialNumber' in props.keys():
   385                 serial = re.search(r"(?P<ser>[0-9A-Fa-f]+)", props['SerialNumber']).groupdict()['ser']
   386             usb_filter = USBFilter( uuid, vid, pid, rev, serial)
   387              
   388             if VMManager.isMassStorageDevice(usb_filter) and not VMManager.isBlacklisted(usb_filter):
   389                 rsds[uuid] = usb_filter
   390                 logger.debug(usb_filter)
   391         return rsds
   392     
   393    
   394     # return the attached USB device as usb descriptor for an existing VM 
   395     def getAttachedRSD(self, vm_name):
   396         props = self.getVMInfo(vm_name)
   397         keys = set(['USBAttachedUUID1', 'USBAttachedVendorId1', 'USBAttachedProductId1', 'USBAttachedRevision1', 'USBAttachedSerialNumber1'])
   398         keyset = set(props.keys())
   399         usb_filter = None
   400         if keyset.issuperset(keys):
   401             usb_filter = USBFilter(props['USBAttachedUUID1'], props['USBAttachedVendorId1'], props['USBAttachedProductId1'], props['USBAttachedRevision1'], props['USBAttachedSerialNumber1'])
   402         return usb_filter
   403         
   404     # return the RSDs attached to all existing SDVMs
   405     def getAttachedRSDs(self):
   406         vms = self.listSDVM()
   407         attached_devices = dict()
   408         for vm in vms:
   409             rsd_filter = self.getAttachedRSD(vm)
   410             if rsd_filter != None:
   411                 attached_devices[vm] = rsd_filter
   412         return attached_devices
   413     
   414     # attach removable storage device to VM by provision of filter
   415     def attachRSD(self, vm_name, rsd_filter):
   416         #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)
   417         return Cygwin.vboxExecute('controlvm ' + vm_name + ' usbattach ' + rsd_filter.uuid )
   418     
   419     # detach removable storage from VM by 
   420     def detachRSD(self, vm_name, rsd_filter):
   421         #return Cygwin.vboxExecute('usbfilter remove 0 --target ' + vm_name)
   422         return Cygwin.vboxExecute('controlvm ' + vm_name + ' usbdetach ' + rsd_filter.uuid )
   423         
   424     # configures hostonly networking and DHCP server. requires admin rights
   425     def configureHostNetworking(self):
   426         #cmd = 'vboxmanage list hostonlyifs'
   427         #Cygwin.vboxExecute(cmd)
   428         #cmd = 'vboxmanage hostonlyif remove \"VirtualBox Host-Only Ethernet Adapter\"'
   429         #Cygwin.vboxExecute(cmd)
   430         #cmd = 'vboxmanage hostonlyif create'
   431         #Cygwin.vboxExecute(cmd)
   432         Cygwin.vboxExecute('hostonlyif ipconfig \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.1 --netmask 255.255.255.0')
   433         #cmd = 'vboxmanage dhcpserver add'
   434         #Cygwin.vboxExecute(cmd)
   435         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')
   436     
   437     def isSDVMExisting(self, vm_name):
   438         sdvms = self.listSDVM()
   439         return vm_name in sdvms
   440         
   441     #create new virtual machine instance based on template vm named SecurityDVM (\SecurityDVM\SecurityDVM.vmdk)
   442     def createVM(self, vm_name):
   443         if self.isSDVMExisting(vm_name):
   444             return
   445         #remove eventually existing SDVM folder
   446         machineFolder = Cygwin.cygPath(VMManager.machineFolder)
   447         Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '\\\"')
   448         Cygwin.vboxExecute('createvm --name ' + vm_name + ' --ostype Debian --register')
   449         Cygwin.vboxExecute('modifyvm ' + vm_name + ' --memory 768 --vram 10 --cpus 1 --usb on --usbehci on --nic1 hostonly --hostonlyadapter1 \"' + self.hostonlyIF['Name'] + '\" --nic2 nat')
   450         Cygwin.vboxExecute('storagectl ' + vm_name + ' --name SATA --add sata --portcount 2')
   451 
   452     #create new SecurityDVM with automatically generated name from template (thread safe)        
   453     def newSDVM(self):
   454         with new_sdvm_lock:
   455             vm_name = self.genSDVMName()
   456             self.createVM(vm_name)
   457         return vm_name
   458     
   459     #VMManager.machineFolder + '\SecurityDVM\SecurityDVM.vmdk
   460     # attach storage image to controller
   461     def attachVDisk(self, vm_name, vdisk_controller, vdisk_port, vdisk_device, vdisk_image):
   462         if self.isVDiskAttached(vm_name, vdisk_controller, vdisk_port, vdisk_device):
   463             self.detachVDisk(vm_name, vdisk_controller, vdisk_port, vdisk_device)
   464         Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl '+ vdisk_controller + ' --port ' + vdisk_port + ' --device ' + vdisk_device + ' --type hdd --medium "'+ vdisk_image + '"')
   465     
   466     # return true if storage is attached 
   467     def isVDiskAttached(self, vm_name, vdisk_controller, vdisk_port, vdisk_device):
   468         info = self.getVMInfo(vm_name)
   469         return (info[vdisk_controller+'-'+vdisk_port+'-'+vdisk_device] != 'none')
   470     
   471     # detach storage from controller
   472     def detachVDisk(self, vm_name, vdisk_controller, vdisk_port, vdisk_device):
   473         if self.isVDiskAttached(vm_name, vdisk_controller, vdisk_port, vdisk_device):
   474             Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl ' + vdisk_controller + ' --port ' + vdisk_port + ' --device ' + vdisk_device + ' --medium none')
   475     
   476     # modify type of the vdisk_image
   477     def changeVDiskType(self, vdisk_image, storage_type):
   478         Cygwin.vboxExecute('modifyhd "' + vdisk_image + '" --type ' + storage_type)
   479         
   480     # grab VM storage controller, port and device for vdisk image name
   481     def getVDiskController(self, vm_name, image_name = '.vmdk'):
   482         vm_description = self.getVMInfo(vm_name)
   483         vdisk_controller = None
   484         for key, value in vm_description.iteritems():
   485             if image_name in value:
   486                 vdisk_controller = key
   487                 break
   488         return vdisk_controller
   489     
   490     # return attached vmdk image name containing image_name 
   491     def getVDiskImage(self, vm_name, image_name = '.vmdk'):
   492         vmInfo = self.getVMInfo(vm_name)
   493         vdisk_image = None
   494         for value in vmInfo.values():
   495             if image_name in value:
   496                 break
   497         return vdisk_image 
   498         
   499     @staticmethod    
   500     def getVDiskImages():
   501         results = Cygwin.vboxExecute('list hdds')[1]
   502         results = results.replace('Parent UUID', 'Parent')
   503         items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   504         
   505         snaps = dict()   
   506         for item in items:
   507             props = dict()
   508             for line in item.splitlines():
   509                 if line != "":         
   510                     k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   511                     props[k] = v;
   512             snaps[props['UUID']] = props
   513         return snaps
   514     
   515     @staticmethod 
   516     def getVDiskUUID(vdisk_image):
   517         images = VMManager.getVDiskImages()
   518         # find template uuid
   519         template_uuid = None
   520         for hdd in images.values():
   521             if hdd['Location'] == vdisk_image:
   522                 template_uuid = hdd['UUID']
   523                 break
   524         return template_uuid
   525     
   526     def removeSnapshots(self, imageUUID):
   527         snaps = self.getVDiskImages()
   528         # remove snapshots 
   529         for hdd in snaps.values():
   530             if hdd['Parent'] == imageUUID:
   531                 snapshotUUID = hdd['UUID']
   532                 self.removeImage(snapshotUUID)
   533                 
   534     def removeImage(self, imageUUID):
   535         logger.debug('removing snapshot ' + imageUUID)
   536         Cygwin.vboxExecute('closemedium disk {' + imageUUID + '} --delete')
   537         # parse result 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
   538     
   539     #remove VM from the system. should be used on VMs returned by listSDVMs    
   540     def removeVM(self, vm_name):
   541         logger.info('Removing ' + vm_name)
   542         Cygwin.vboxExecute('unregistervm ' + vm_name + ' --delete')
   543         #try to close medium if still existing
   544         #Cygwin.vboxExecute('closemedium disk {' + hdd['UUID'] + '} --delete')
   545         self.removeVMFolder(vm_name)
   546     
   547     def removeVMFolder(self, vm_name):
   548         machineFolder = Cygwin.cygPath(VMManager.machineFolder)
   549         Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '\\' + vm_name + '\\\"')
   550     
   551     # start VM
   552     def startVM(self, vm_name):
   553         logger.info('Starting ' +  vm_name)
   554         Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False')
   555         result = Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless' )
   556         while 'successfully started' not in result[1]:
   557             logger.error("Failed to start SDVM: " + vm_name + " retrying")
   558             logger.error("Command returned:\n" + result[2])
   559             time.sleep(1)
   560             result = Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless')
   561         return result[0]
   562     
   563     # return wether VM is running or not
   564     def isVMRunning(self, vm_name):
   565         return vm_name in self.listRunningVMS()    
   566     
   567     # stop VM
   568     def stopVM(self, vm_name):
   569         logger.info('Sending shutdown signal to ' + vm_name)
   570         Cygwin.sshExecute( '"sudo shutdown -h now"', self.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(VMManager.machineFolder) + '/' + vm_name + '/dvm_key' )
   571         Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False')
   572     
   573     # stop VM
   574     def hibernateVM(self, vm_name):
   575         logger.info('Sending hibernate-disk signal to ' + vm_name)
   576         Cygwin.sshBackgroundExecute( '"sudo hibernate-disk"', self.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(VMManager.machineFolder) + '/' + vm_name + '/dvm_key', wait_return=False)
   577         Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False')
   578             
   579     # poweroff VM
   580     def poweroffVM(self, vm_name):
   581         if not self.isVMRunning(vm_name):
   582             return
   583         logger.info('Powering off ' + vm_name)
   584         Cygwin.vboxExecute('controlvm ' + vm_name + ' poweroff')
   585         Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False')
   586     
   587     
   588     # return the hostOnly IP for a running guest or the host    
   589     def getHostOnlyIP(self, vm_name):
   590         if vm_name == None:
   591             logger.info('Getting hostOnly IP address for Host')
   592             return VMManager.hostonlyIF['IPAddress']
   593         else:
   594             logger.info('Getting hostOnly IP address ' + vm_name)
   595             result = Cygwin.vboxExecute('guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP')
   596             if result=='':
   597                 return None
   598             result = result[1]
   599             if result.startswith('No value set!'):
   600                 return None
   601             return result[result.index(':')+1:].strip()
   602         
   603     # return the description set for an existing VM
   604     def getVMInfo(self, vm_name):
   605         results = Cygwin.vboxExecute('showvminfo ' + vm_name + ' --machinereadable')[1]
   606         props = dict((k.strip().strip('"'),v.strip().strip('"')) for k,v in (line.split('=', 1) for line in results.splitlines()))
   607         return props
   608     
   609     #generates ISO containing authorized_keys for use with guest VM
   610     def genCertificate(self, vm_name):
   611         machineFolder = Cygwin.cygPath(VMManager.machineFolder)
   612         # remove .ssh folder if exists
   613         Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"')
   614         # remove .ssh folder if exists
   615         Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"')
   616         # create .ssh folder in vm_name
   617         Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"')
   618         # generate dvm_key pair in vm_name / .ssh     
   619         Cygwin.bashExecute('/usr/bin/ssh-keygen -q -t rsa -N \\\"\\\" -C \\\"' + vm_name + '\\\" -f \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\"')
   620         # move out private key
   621         Cygwin.bashExecute('/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\" \\\"' + machineFolder + '/' + vm_name + '\\\"')
   622         # set permissions for private key
   623         Cygwin.bashExecute('/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"')
   624         # rename public key to authorized_keys
   625         Cygwin.bashExecute('/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key.pub\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"')
   626         # set permissions for authorized_keys
   627         Cygwin.bashExecute('/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"')
   628         # generate iso image with .ssh/authorized keys
   629         Cygwin.bashExecute('/usr/bin/genisoimage -J -R -o \\\"' + machineFolder + '/' + vm_name + '/'+ vm_name + '.iso\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"')
   630     
   631     # attaches generated ssh public cert to guest vm
   632     def attachCertificate(self, vm_name):
   633         if self.isCertificateAttached(vm_name):
   634             self.detachCertificate(vm_name)
   635         Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 1 --device 0 --type dvddrive --mtype readonly --medium \"' + VMManager.machineFolder + '\\' + vm_name + '\\'+ vm_name + '.iso\"')
   636     
   637     # return true if storage is attached 
   638     def isCertificateAttached(self, vm_name):
   639         info = self.getVMInfo(vm_name)
   640         return (info['SATA-1-0']!='none')
   641     
   642     # detach storage from controller
   643     def detachCertificate(self, vm_name):
   644         if self.isCertificateAttached(vm_name):
   645             Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 1 --device 0 --type hdd --medium none')
   646             
   647     # wait for machine to come up
   648     def waitStartup(self, vm_name):
   649         #Cygwin.vboxExecute('guestproperty wait ' + vm_name + ' SDVMStarted --timeout ' + str(timeout_ms) + ' --fail-on-timeout', try_count = 60)
   650         started = False
   651         while not started:
   652             result = Cygwin.vboxExecute('guestproperty get ' + vm_name + ' SDVMStarted')[1]
   653             if "Value: True" in result:
   654                 started = True
   655             else:
   656                 time.sleep(3) 
   657         return self.getHostOnlyIP(vm_name)
   658     
   659     # wait for machine to shutdown
   660     def waitShutdown(self, vm_name):
   661         while vm_name in self.listRunningVMS():
   662             time.sleep(1)
   663         return
   664     
   665     #Small function to check if the mentioned location is a directory
   666     def isDirectory(self, path):
   667         result = Cygwin.cmdExecute('dir ' + path + ' | FIND ".."')
   668         return string.find(result[1], 'DIR',)
   669     
   670     def genNetworkDrive(self):
   671         logical_drives = VMManager.getLogicalDrives()
   672         logger.info("Used logical drive letters: "+ str(logical_drives).strip('[]') )
   673         drives = list(map(chr, range(68, 91)))  
   674         for drive in drives:
   675             if drive not in logical_drives:
   676                 return drive
   677             
   678     @staticmethod
   679     def getLogicalDrives():
   680         drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
   681         drives = list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
   682         return drives
   683     
   684     @staticmethod
   685     def getDriveType(drive):
   686         return ctypes.cdll.kernel32.GetDriveTypeW(u"%s:\\"%drive)
   687     
   688     @staticmethod
   689     def getNetworkPath(drive):
   690         return win32wnet.WNetGetConnection(drive+':')
   691     
   692     @staticmethod
   693     def getVolumeInfo(drive):
   694         volumeNameBuffer = ctypes.create_unicode_buffer(1024)
   695         fileSystemNameBuffer = ctypes.create_unicode_buffer(1024)
   696         serial_number = None
   697         max_component_length = None
   698         file_system_flags = None
   699         
   700         rc = ctypes.cdll.kernel32.GetVolumeInformationW(
   701             u"%s:\\"%drive,
   702             volumeNameBuffer,
   703             ctypes.sizeof(volumeNameBuffer),
   704             serial_number,
   705             max_component_length,
   706             file_system_flags,
   707             fileSystemNameBuffer,
   708             ctypes.sizeof(fileSystemNameBuffer)
   709         )
   710         return volumeNameBuffer.value, fileSystemNameBuffer.value
   711     
   712     def getNetworkDrive(self, vm_name):
   713         ip = self.getHostOnlyIP(vm_name)
   714         if ip == None:
   715             logger.error("Failed getting hostonly IP for " + vm_name)
   716             return None
   717         logger.info("Got IP address for " + vm_name + ': ' + ip)
   718         for drive in VMManager.getLogicalDrives():
   719             #if is a network drive
   720             if VMManager.getDriveType(drive) == 4:
   721                 network_path = VMManager.getNetworkPath(drive)
   722                 if ip in network_path:
   723                     return drive
   724         return None
   725     
   726     def getNetworkDrives(self):
   727         ip = self.getHostOnlyIP(None)
   728         if ip == None:
   729             logger.error("Failed getting hostonly IP for system")
   730             return None
   731         logger.info("Got IP address for system: " + ip)
   732         ip = ip[:ip.rindex('.')]
   733         network_drives = dict()
   734         for drive in VMManager.getLogicalDrives():
   735             #if is a network drive
   736             if VMManager.getDriveType(drive) == 4:
   737                 network_path = VMManager.getNetworkPath(drive)
   738                 if ip in network_path:
   739                     network_drives[drive] = network_path  
   740         return network_drives
   741     
   742     # handles browsing request    
   743     def handleBrowsingRequest(self, proxy = None, wpad = None):
   744         showTrayMessage('Starting Secure Browsing...', 7000)
   745         handler = BrowsingHandler(self, proxy, wpad)
   746         handler.start()
   747         return 'ok'
   748     
   749     def getActiveUserName(self):
   750         key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI')
   751         v = str(win32api.RegQueryValueEx(key, 'LastLoggedOnUser')[0])
   752         win32api.RegCloseKey(key)
   753         user_name = win32api.ExpandEnvironmentStrings(v)
   754         return user_name
   755         
   756     def getUserSID(self, user_name):
   757         domain, user = user_name.split("\\")
   758         account_name = win32security.LookupAccountName(domain, user)
   759         if account_name == None:
   760             logger.error("Failed lookup account name for user " + user_name)
   761             return None
   762         sid = win32security.ConvertSidToStringSid(account_name[0])
   763         if sid == None:
   764             logger.error("Failed converting SID for account " + account_name[0])
   765             return None
   766         return sid
   767         
   768     def getAppDataDir(self, sid):    
   769         key = win32api.RegOpenKey(win32con.HKEY_USERS, sid + '\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders')
   770         value, _ = win32api.RegQueryValueEx(key, "AppData")
   771         win32api.RegCloseKey(key)
   772         return value
   773         #key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' + '\\' + sid)
   774         #value, type = win32api.RegQueryValueEx(key, "ProfileImagePath")
   775         #print value
   776     
   777     #import initial template
   778     def importTemplate(self, image_path):
   779         import_logger.info('Stopping Opensecurity...')
   780         self.stop()
   781         
   782         import_logger.info('Cleaning up system in preparation for import...')
   783         self.cleanup()
   784         
   785         import_logger.info('Removing template SDVM...')
   786         # if template exists
   787         if self.vmRootName in self.listVMS():
   788             # shutdown template if running
   789             self.poweroffVM(self.vmRootName)
   790             # detach and remove VDisk
   791             tmplateUUID = self.getVDiskUUID(self.templateImage)
   792             if tmplateUUID != None:
   793                 logger.debug('Found template VDisk uuid ' + tmplateUUID)
   794                 controller = self.getVDiskController(self.vmRootName)
   795                 if controller:
   796                     controller = controller.split('-')
   797                     self.detachVDisk(self.vmRootName, controller[0], controller[1], controller[2])
   798                 self.removeSnapshots(tmplateUUID)
   799                 self.removeImage(tmplateUUID)
   800             else:
   801                 logger.info('Template uuid not found')
   802             # remove VM    
   803             self.removeVM(self.vmRootName)
   804         # remove template VM folder 
   805         self.removeVMFolder(self.vmRootName)
   806         import_logger.info('Cleanup finished...')
   807         
   808         import_logger.info('Checking privileges...')
   809         result = Cygwin.bashExecute('id -G')
   810         if '544' not in result[1]:
   811             import_logger.debug('Insufficient privileges.')
   812             import_logger.debug("Trying to continue...")
   813         
   814         # check OpenSecurity Initial VM Image
   815         import_logger.debug('Looking for VM image: ' + image_path)
   816         result = os.path.isfile(image_path)
   817       
   818         if not result:
   819             import_logger.debug('Warning: no OpenSecurity Initial Image found.')
   820             import_logger.debug('Please download using the OpenSecurity download tool.')
   821             raise OpenSecurityException('OpenSecurity Initial Image not found.')
   822         logger.debug('Initial VM image: ' + image_path + ' found')
   823         
   824         if not self.template_installed():
   825             import_logger.info('Importing SDVm template: ' + image_path)
   826             Cygwin.vboxExecute('import "' + image_path + '" --vsys 0 --vmname ' + VMManager.vmRootName + ' --unit 12 --disk "' + self.templateImage + '"')
   827         else:
   828             import_logger.info('Found ' + VMManager.vmRootName + ' already present in VBox reusing it.')
   829             import_logger.info('if you want a complete new import please remove the VM first.')
   830             import_logger.info('starting OpenSecurity service...')
   831             return
   832 
   833         # remove unnecessary IDE controller
   834         Cygwin.vboxExecute('storagectl ' + VMManager.vmRootName + ' --name IDE --remove')
   835 
   836         info = self.getVDiskController(VMManager.vmRootName, self.templateImage)
   837         if info:
   838             info = info.split('-')
   839             self.detachVDisk(VMManager.vmRootName, info[0], info[1], info[2])
   840         
   841         self.changeVDiskType(self.templateImage, 'immutable')
   842         self.attachVDisk(VMManager.vmRootName, info[0], info[1], info[2], self.templateImage)
   843         import_logger.info('Initial import finished.')    
   844         
   845     # update template 
   846     def updateTemplate(self):
   847         import_logger.debug('Stopping Opensecurity...')
   848         self.stop()
   849         
   850         import_logger.debug('Cleaning up system in preparation for update...')
   851         self.cleanup()
   852         
   853         import_logger.info('Cleanup finished...')
   854         
   855         # shutdown template if running
   856         self.poweroffVM(self.vmRootName)
   857         
   858         import_logger.info('Starting template VM...')
   859         # check for updates
   860         self.genCertificate(self.vmRootName)
   861         self.attachCertificate(self.vmRootName)
   862 
   863         import_logger.info('Removing snapshots...')        
   864         
   865         self.detachVDisk(self.vmRootName, 'SATA', '0', '0')
   866         templateUUID = self.getVDiskUUID(self.templateImage)
   867         self.removeSnapshots(templateUUID)
   868         
   869         import_logger.info('Setting VDisk image to normal...')
   870         self.changeVDiskType(self.templateImage, 'normal')
   871         self.attachVDisk(self.vmRootName, 'SATA', '0', '0', self.templateImage)
   872         
   873         import_logger.info('Starting VM...')
   874         self.startVM(self.vmRootName)
   875         self.waitStartup(self.vmRootName)
   876         
   877         import_logger.info('Updating components...')
   878         tmp_ip = self.getHostOnlyIP(self.vmRootName)
   879         tmp_machine_folder = Cygwin.cygPath(VMManager.machineFolder)
   880         Cygwin.sshExecute('"sudo apt-get -y update"', tmp_ip, 'osecuser', tmp_machine_folder + '/' + self.vmRootName + '/dvm_key')
   881         Cygwin.sshExecute('"sudo apt-get -y upgrade"', tmp_ip, 'osecuser', tmp_machine_folder + '/' + self.vmRootName + '/dvm_key')
   882         
   883         import_logger.info('Restarting template VM...')
   884         #check if reboot is required
   885         result = Cygwin.sshExecute('"if [ -f /var/run/reboot-required ]; then echo \\\"Yes\\\"; fi"', tmp_ip, 'osecuser', tmp_machine_folder + '/' + self.vmRootName + '/dvm_key')
   886         if "Yes" in result[1]:
   887             self.stopVM(self.vmRootName)
   888             self.waitShutdown(self.vmRootName)
   889             self.startVM(self.vmRootName)
   890             self.waitStartup(self.vmRootName)
   891         
   892         import_logger.info('Stopping template VM...')
   893         self.stopVM(self.vmRootName)
   894         self.waitShutdown(self.vmRootName)
   895         
   896         import_logger.info('Setting VDisk image to immutable...')
   897         self.detachVDisk(self.vmRootName, 'SATA', '0', '0') 
   898         self.changeVDiskType(self.templateImage, 'immutable')
   899         self.attachVDisk(self.vmRootName,  'SATA', '0', '0', self.templateImage)
   900         
   901         import_logger.info('Update template finished...')
   902     
   903     def startInitialImport(self):
   904         if self.importHandler and self.importHandler.isAlive():
   905             import_logger.info("Initial import already running.")
   906             return
   907         self.importHandler = InitialImportHandler(self)
   908         self.importHandler.start()
   909         import_logger.info("Initial import started.")
   910         
   911     def startUpdateTemplate(self):
   912         if self.updateHandler and self.updateHandler.isAlive():
   913             import_logger.info("Initial import already running.")
   914             return
   915         self.updateHandler = UpdateHandler(self)
   916         self.updateHandler.start()
   917         import_logger.info("Initial import started.")
   918         
   919     def createSession(self):
   920         new_sdvm = self.newSDVM()
   921         self.attachVDisk(new_sdvm, 'SATA', '0', '0', self.templateImage)
   922         self.genCertificate(new_sdvm)
   923         self.attachCertificate(new_sdvm)
   924         self.startVM(new_sdvm)
   925         new_ip = self.waitStartup(new_sdvm)
   926         if new_ip == None:
   927             logger.error("Error getting IP address of SDVM. Cleaning up.")
   928             self.poweroffVM(new_sdvm)
   929             self.removeVM(new_sdvm)
   930             return None
   931         else:
   932             logger.info("Got IP address for " + new_sdvm + ' ' + new_ip)
   933             self.vms[new_sdvm] = {'vm_name' : new_sdvm, 'ip_addr' : new_ip, 'used' : False, 'running' : True}
   934             return self.vms[new_sdvm]
   935             
   936     def releaseSession(self, vm_name):
   937         del self.vms[vm_name]
   938         self.poweroffVM(vm_name)
   939         self.removeVM(vm_name)
   940         self.sdvmFactory.trigger()
   941         
   942     def getSession(self):
   943         # return first found unused SDVM
   944         for vm in self.vms.values():
   945             if vm['used'] == False:
   946                 vm['used'] = True
   947                 self.sdvmFactory.trigger()
   948                 return vm
   949         return self.createSession()
   950         
   951 class SDVMFactory(threading.Thread):
   952     vmm = None
   953     running = True
   954     triggerEv = None
   955     
   956     def __init__(self, vmmanager):
   957         threading.Thread.__init__(self)
   958         self.vmm = vmmanager
   959         self.triggerEv = threading.Event()
   960         
   961     def run(self):
   962         while self.running:
   963             self.triggerEv.clear()            
   964 
   965             if len(self.vmm.vms) < 2:
   966                 self.vmm.createSession()
   967                 continue
   968             unused = 0
   969             for vm in self.vmm.vms.values():
   970                 if vm['used'] == False:
   971                     unused+=1
   972             if unused == 0:
   973                 self.vmm.createSession()
   974             self.triggerEv.wait()
   975     
   976     def trigger(self):
   977         self.triggerEv.set()
   978         
   979     def stop(self):
   980         self.running = False
   981         self.triggerEv.set()
   982         
   983 #handles browsing session creation 
   984 class BrowsingHandler(threading.Thread):
   985     vmm = None
   986     proxy = None
   987     wpad = None
   988     net_resource = None
   989     ip_addr = None
   990     vm_name = None
   991     
   992     def __init__(self, vmmanager, proxy, wpad):
   993         threading.Thread.__init__(self)
   994         self.vmm = vmmanager
   995         self.proxy = proxy
   996         self.wpad = wpad
   997         
   998     def run(self):
   999         session = None
  1000         try:
  1001             appDataDir = self.getAppDataDir()
  1002 
  1003             session = self.vmm.getSession()
  1004             if not session:
  1005                 raise OpenSecurityException("Could not get new SDVM session.")
  1006             
  1007             self.ip_addr = session['ip_addr']
  1008             self.vm_name = session['vm_name']
  1009             
  1010             self.net_resource = '\\\\' + self.ip_addr + '\\Download'
  1011             urllib2.urlopen('http://127.0.0.1:8090/netmount?'+'net_resource='+self.net_resource).readline()
  1012             
  1013             logger.info("Restoring browser settings in AppData dir " + appDataDir)
  1014             # create OpenSecurity settings dir on local machine user home /AppData/Roaming 
  1015             Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + appDataDir + '/OpenSecurity\\\"')
  1016             # create chromium settings dir on local machine if not existing
  1017             Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + appDataDir + '/OpenSecurity/chromium\\\"')
  1018             # create chromium settings dir on remote machine if not existing
  1019             Cygwin.sshExecute('"mkdir -p \\\"/home/osecuser/.config\\\""', self.ip_addr, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + self.vm_name + '/dvm_key')
  1020             #restore settings on vm
  1021             self.restoreFile(appDataDir + '/OpenSecurity/chromium', '/home/osecuser/.config/')
  1022                     
  1023             if self.wpad:
  1024                 browser = '\\\"/usr/bin/chromium --proxy-pac-url=\\\"'+self.wpad+'\\\"\\\"'
  1025             elif self.proxy:
  1026                 browser = '\\\"export http_proxy='+self.proxy+'; /usr/bin/chromium\\\"'
  1027             else:
  1028                 browser = '\\\"/usr/bin/chromium\\\"'
  1029                 
  1030             Cygwin.sshExecuteX11(browser, self.ip_addr, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + self.vm_name + '/dvm_key')
  1031             self.backupFile('/home/osecuser/.config/chromium', appDataDir + '/OpenSecurity/')
  1032         
  1033         except urllib2.URLError:
  1034             logger.error("Network drive connect failed. OpenSecurity Tray client not running.")
  1035             self.net_resource = None
  1036 
  1037         except:
  1038             logger.info("BrowsingHandler failed. See log for details")
  1039         
  1040         if session:
  1041             if self.net_resource == None:
  1042                 logger.info("Missing browsing SDVM's network share. Skipping disconnect")
  1043             else:
  1044                 try:
  1045                     urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'net_resource='+self.net_resource).readline()
  1046                     self.net_resource = None
  1047                 except urllib2.URLError:
  1048                     logger.error("Network share disconnect failed. OpenSecurity Tray client not running.")
  1049         if self.vm_name:
  1050             self.vmm.releaseSession(self.vm_name)
  1051         
  1052         self.vmm.sdvmFactory.trigger()
  1053 
  1054     def backupFile(self, src, dest):
  1055         global backup_lock
  1056         with backup_lock:
  1057             certificate = Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + self.vm_name + '/dvm_key'
  1058             command = '-r -o StrictHostKeyChecking=no -i "' + certificate + '" "osecuser@' + self.ip_addr + ':' + src + '" "' + dest + '"'
  1059             return Cygwin.execute(Cygwin.cygwin_scp, command, wait_return=True, window=False)
  1060     
  1061     def restoreFile(self, src, dest):
  1062         certificate = Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + self.vm_name + '/dvm_key'
  1063         command = '-r -o StrictHostKeyChecking=no -i "' + certificate + '" "' + src + '" "osecuser@' + self.ip_addr + ':' + dest + '"'
  1064         return Cygwin.execute(Cygwin.cygwin_scp, command, wait_return=True, window=False)
  1065                 
  1066     def getAppDataDir(self):
  1067         user = self.vmm.getActiveUserName()
  1068         if user == None:
  1069             logger.error("Cannot get active user name")
  1070             raise OpenSecurityException("Cannot get active user name")
  1071         else:
  1072             logger.info('Got active user name ' + user)
  1073         sid = self.vmm.getUserSID(user)
  1074         if sid == None:
  1075             logger.error("Cannot get SID for active user")
  1076             raise OpenSecurityException("Cannot get SID for active user")
  1077         else:
  1078             logger.info("Got active user SID " + sid + " for user " + user)
  1079             
  1080         path = self.vmm.getAppDataDir(sid)
  1081         if path == None:
  1082             logger.error("Cannot get AppDataDir for active user")
  1083             raise OpenSecurityException("Cannot get AppDataDir for active user")
  1084         else:
  1085             logger.info("Got AppData dir for user " + user + ': ' + path)
  1086         
  1087         return Cygwin.cygPath(path)
  1088             
  1089                 
  1090 class DeviceHandler(threading.Thread): 
  1091     vmm = None
  1092     existingRSDs = None
  1093     attachedRSDs = None  
  1094     running = True
  1095     def __init__(self, vmmanger): 
  1096         threading.Thread.__init__(self)
  1097         self.vmm = vmmanger
  1098  
  1099     def stop(self):
  1100         self.running = False
  1101         
  1102     def run(self):
  1103         self.existingRSDs = dict()
  1104         self.attachedRSDs = self.vmm.getAttachedRSDs()
  1105         
  1106         while self.running:
  1107             tmp_rsds = self.vmm.getExistingRSDs()
  1108             if tmp_rsds.keys() == self.existingRSDs.keys():
  1109                 logger.debug("Nothing's changed. sleep(3)")
  1110                 time.sleep(3)
  1111                 continue
  1112             
  1113             showTrayMessage('System changed.\nEvaluating...', 7000)
  1114             logger.info("Something's changed")
  1115             
  1116             tmp_attached = self.attachedRSDs     
  1117             for vm_name in tmp_attached.keys():
  1118                 if tmp_attached[vm_name] not in tmp_rsds.values():
  1119                     ip = self.vmm.getHostOnlyIP(vm_name)
  1120                     if ip == None:
  1121                         logger.error("Failed getting hostonly IP for " + vm_name)
  1122                         continue
  1123                     
  1124                     try:
  1125                         net_resource = '\\\\' + ip + '\\USB'
  1126                         result = urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'net_resource='+net_resource).readline()
  1127                     except urllib2.URLError:
  1128                         logger.error("Network drive disconnect failed. OpenSecurity Tray client not running.")
  1129                         continue
  1130                     
  1131                     # detach not necessary as already removed from vm description upon disconnect
  1132                     del self.attachedRSDs[vm_name]
  1133                     self.vmm.releaseSession(vm_name)
  1134                     
  1135             #create new vms for new devices if any
  1136             new_ip = None
  1137             for new_device in tmp_rsds.values():
  1138                 showTrayMessage('Mounting device...', 7000)
  1139                 if (self.attachedRSDs and False) or (new_device not in self.attachedRSDs.values()):
  1140                    
  1141                     session = self.vmm.getSession()
  1142                     if not session:
  1143                         logger.info("Could not get new SDVM session.")
  1144                         continue
  1145                         #raise OpenSecurityException("Could not get new SDVM session.")
  1146                     new_sdvm = session['vm_name']
  1147                     new_ip = session['ip_addr']
  1148                     try:
  1149                         self.vmm.attachRSD(new_sdvm, new_device)
  1150                         self.attachedRSDs[new_sdvm] = new_device
  1151                     except:
  1152                         logger.info("RSD prematurely removed. Cleaning up.")
  1153                         self.vmm.releaseSession(new_sdvm)
  1154                         continue
  1155                     
  1156                     try:
  1157                         net_resource = '\\\\' + new_ip + '\\USB'
  1158                         result = urllib2.urlopen('http://127.0.0.1:8090/netmount?'+'net_resource='+net_resource).readline()
  1159                     except urllib2.URLError:
  1160                         logger.error("Network drive connect failed (tray client not accessible). Cleaning up.")
  1161                         self.vmm.releaseSession(new_sdvm)
  1162                         continue
  1163                     
  1164             self.existingRSDs = tmp_rsds
  1165 
  1166 class UpdateHandler(threading.Thread):
  1167     vmm = None    
  1168     def __init__(self, vmmanager):
  1169         threading.Thread.__init__(self)
  1170         self.vmm = vmmanager
  1171     
  1172     def run(self):
  1173         try:
  1174             self.vmm.updateTemplate()
  1175         except:
  1176             import_logger.info("Update template failed. Refer to service log for details.")
  1177         self.vmm.start(force=True)
  1178     
  1179 class InitialImportHandler(threading.Thread):
  1180     vmm = None    
  1181     def __init__(self, vmmanager):
  1182         threading.Thread.__init__(self)
  1183         self.vmm = vmmanager
  1184     
  1185     def run(self):
  1186         try:
  1187             self.vmm.importTemplate(self.vmm.getMachineFolder() + '\\OsecVM.ova')
  1188             self.vmm.updateTemplate()
  1189         except:
  1190             import_logger.info("Initial import failed. Refer to service log for details.")
  1191         self.vmm.start(force=True)