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