OpenSecurity/bin/vmmanager.pyw
author BarthaM@N3SIM1218.D03.arc.local
Fri, 05 Sep 2014 12:28:30 +0100
changeset 221 853af9cfab6a
parent 219 9480e5ba1a82
child 223 a4fb6694e6fe
permissions -rwxr-xr-x
Integrated import script (rewritten in python) into opensecurity/vmmanager.py
Improoved user feedback upon import and update as well as logging.
Reduced system shutdown times and ui response times
Improoved the decoupling between UI and OSec subsystem.
Various other fixes
     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     rsdHandler = None
   112     hostonlyIF = None
   113     browsingManager = None
   114     blacklistedRSD = None
   115     status_message = 'Starting up...'
   116     templateImage = None
   117     importHandler = None
   118     updateHandler = None
   119  
   120     def __init__(self):
   121         # only proceed if we have a working background environment
   122         if self.backend_ok():
   123             VMManager.hostonlyIF = self.getHostOnlyIFs()["VirtualBox Host-Only Ethernet Adapter"]
   124             self.cleanup()
   125         else:
   126             logger.critical(self.status_message)
   127     
   128 
   129     @staticmethod
   130     def getInstance():
   131         if VMManager._instance == None:
   132             VMManager._instance = VMManager()
   133         return VMManager._instance
   134     
   135     #list the hostonly IFs exposed by the VBox host
   136     @staticmethod    
   137     def getHostOnlyIFs():
   138         results = Cygwin.vboxExecute('list hostonlyifs')[1]
   139         ifs = dict()
   140         if results=='':
   141             return ifs
   142         items = list( "Name:    " + result for result in results.split('Name:    ') if result != '')
   143         for item in items:
   144             if item != "":
   145                 props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in item.strip().splitlines()))
   146                 ifs[props["Name"]] = props
   147         return ifs
   148                     
   149         #props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result.strip().splitlines()))
   150         #return props
   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 stop(self):
   243         Cygwin.denyExec()
   244         if self.rsdHandler != None:
   245             self.rsdHandler.stop()
   246             self.rsdHandler.join()
   247             self.rsdHandler = None
   248             
   249         if self.browsingManager != None:
   250             self.browsingManager.stop()
   251             self.browsingManager.join()
   252             self.browsingManager = None
   253         Cygwin.allowExec()
   254     
   255     def start(self, force = False):
   256         if not force:
   257             if self.importHandler and self.importHandler.isAlive():
   258                 logger.info("Initial update running canceling start.")
   259                 return
   260         
   261             if self.updateHandler and self.updateHandler.isAlive():
   262                 logger.info("Update running canceling start.")
   263                 return
   264         
   265         self.stop()
   266         Cygwin.allowExec()
   267         if self.backend_ok() and self.template_installed():
   268             self.browsingManager = BrowsingManager(self)
   269             self.browsingManager.start()
   270             self.rsdHandler = DeviceHandler(self)
   271             self.rsdHandler.start()
   272         
   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):
   744         showTrayMessage('Starting Secure Browsing...', 7000)
   745         handler = BrowsingHandler(self, proxy)
   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     def backupFile(self, src, dest):
   778         certificate = Cygwin.cygPath(self.getMachineFolder()) + '/' + self.browsingManager.vm_name + '/dvm_key'
   779         command = '-r -o StrictHostKeyChecking=no -i "' + certificate + '" "osecuser@' + self.browsingManager.ip_addr + ':' + src + '" "' + dest + '"'
   780         return Cygwin.execute(Cygwin.cygwin_scp, command, wait_return=True, window=False)
   781     
   782     def restoreFile(self, src, dest):
   783         certificate = Cygwin.cygPath(self.getMachineFolder()) + '/' + self.browsingManager.vm_name + '/dvm_key'
   784         #command = '-r -v -o StrictHostKeyChecking=no -i \"' + certificate + '\" \"' + src + '\" \"osecuser@' + self.browsingManager.ip_addr + ':' + dest + '\"'
   785         command = '-r -o StrictHostKeyChecking=no -i "' + certificate + '" "' + src + '" "osecuser@' + self.browsingManager.ip_addr + ':' + dest + '"'
   786         return Cygwin.execute(Cygwin.cygwin_scp, command, wait_return=True, window=False)
   787     
   788     #import initial template
   789     def importTemplate(self, image_path):
   790         import_logger.info('Stopping Opensecurity...')
   791         self.stop()
   792         
   793         import_logger.info('Cleaning up system in preparation for import...')
   794         self.cleanup()
   795         
   796         import_logger.info('Removing template SDVM...')
   797         # if template exists
   798         if self.vmRootName in self.listVMS():
   799             # shutdown template if running
   800             self.poweroffVM(self.vmRootName)
   801             # detach and remove VDisk
   802             tmplateUUID = self.getVDiskUUID(self.templateImage)
   803             if tmplateUUID != None:
   804                 logger.debug('Found template VDisk uuid ' + tmplateUUID)
   805                 controller = self.getVDiskController(self.vmRootName)
   806                 if controller:
   807                     controller = controller.split('-')
   808                     self.detachVDisk(self.vmRootName, controller[0], controller[1], controller[2])
   809                 self.removeSnapshots(tmplateUUID)
   810                 self.removeImage(tmplateUUID)
   811             else:
   812                 logger.info('Template uuid not found')
   813             # remove VM    
   814             self.removeVM(self.vmRootName)
   815         # remove template VM folder 
   816         self.removeVMFolder(self.vmRootName)
   817         import_logger.info('Cleanup finished...')
   818         
   819         import_logger.info('Checking privileges...')
   820         result = Cygwin.bashExecute('id -G')
   821         if '544' not in result[1]:
   822             import_logger.debug('Insufficient privileges.')
   823             import_logger.debug("Trying to continue...")
   824         
   825         # check OpenSecurity Initial VM Image
   826         import_logger.debug('Looking for VM image: ' + image_path)
   827         result = os.path.isfile(image_path)
   828       
   829         if not result:
   830             import_logger.debug('Warning: no OpenSecurity Initial Image found.')
   831             import_logger.debug('Please download using the OpenSecurity download tool.')
   832             raise OpenSecurityException('OpenSecurity Initial Image not found.')
   833         logger.debug('Initial VM image: ' + image_path + ' found')
   834         
   835         if not self.template_installed():
   836             import_logger.info('Importing SDVm template: ' + image_path)
   837             Cygwin.vboxExecute('import "' + image_path + '" --vsys 0 --vmname ' + VMManager.vmRootName + ' --unit 12 --disk "' + self.templateImage + '"')
   838         else:
   839             import_logger.info('Found ' + VMManager.vmRootName + ' already present in VBox reusing it.')
   840             import_logger.info('if you want a complete new import please remove the VM first.')
   841             import_logger.info('starting OpenSecurity service...')
   842             return
   843 
   844         # remove unnecessary IDE controller
   845         Cygwin.vboxExecute('storagectl ' + VMManager.vmRootName + ' --name IDE --remove')
   846 
   847         info = self.getVDiskController(VMManager.vmRootName, self.templateImage)
   848         if info:
   849             info = info.split('-')
   850             self.detachVDisk(VMManager.vmRootName, info[0], info[1], info[2])
   851         
   852         self.changeVDiskType(self.templateImage, 'immutable')
   853         self.attachVDisk(VMManager.vmRootName, info[0], info[1], info[2], self.templateImage)
   854         import_logger.info('Initial import finished.')    
   855         
   856     # update template 
   857     def updateTemplate(self):
   858         import_logger.debug('Stopping Opensecurity...')
   859         self.stop()
   860         
   861         import_logger.debug('Cleaning up system in preparation for update...')
   862         self.cleanup()
   863         
   864         import_logger.info('Cleanup finished...')
   865         
   866         # shutdown template if running
   867         self.poweroffVM(self.vmRootName)
   868         
   869         import_logger.info('Starting template VM...')
   870         # check for updates
   871         self.genCertificate(self.vmRootName)
   872         self.attachCertificate(self.vmRootName)
   873 
   874         import_logger.info('Removing snapshots...')        
   875         
   876         self.detachVDisk(self.vmRootName, 'SATA', '0', '0')
   877         templateUUID = self.getVDiskUUID(self.templateImage)
   878         self.removeSnapshots(templateUUID)
   879         
   880         import_logger.info('Setting VDisk image to normal...')
   881         self.changeVDiskType(self.templateImage, 'normal')
   882         self.attachVDisk(self.vmRootName, 'SATA', '0', '0', self.templateImage)
   883         
   884         import_logger.info('Starting VM...')
   885         self.startVM(self.vmRootName)
   886         self.waitStartup(self.vmRootName)
   887         
   888         import_logger.info('Updating components...')
   889         tmp_ip = self.getHostOnlyIP(self.vmRootName)
   890         tmp_machine_folder = Cygwin.cygPath(VMManager.machineFolder)
   891         Cygwin.sshExecute('"sudo apt-get -y update"', tmp_ip, 'osecuser', tmp_machine_folder + '/' + self.vmRootName + '/dvm_key')
   892         Cygwin.sshExecute('"sudo apt-get -y upgrade"', tmp_ip, 'osecuser', tmp_machine_folder + '/' + self.vmRootName + '/dvm_key')
   893         
   894         import_logger.info('Restarting template VM...')
   895         #check if reboot is required
   896         result = Cygwin.sshExecute('"if [ -f /var/run/reboot-required ]; then echo \\\"Yes\\\"; fi"', tmp_ip, 'osecuser', tmp_machine_folder + '/' + self.vmRootName + '/dvm_key')
   897         if "Yes" in result[1]:
   898             self.stopVM(self.vmRootName)
   899             self.waitShutdown(self.vmRootName)
   900             self.startVM(self.vmRootName)
   901             self.waitStartup(self.vmRootName)
   902         
   903         import_logger.info('Stopping template VM...')
   904         self.stopVM(self.vmRootName)
   905         self.waitShutdown(self.vmRootName)
   906         
   907         import_logger.info('Setting VDisk image to immutable...')
   908         self.detachVDisk(self.vmRootName, 'SATA', '0', '0') 
   909         self.changeVDiskType(self.templateImage, 'immutable')
   910         self.attachVDisk(self.vmRootName,  'SATA', '0', '0', self.templateImage)
   911         
   912         import_logger.info('Update template finished...')
   913     
   914     def startInitialImport(self):
   915         if self.importHandler and self.importHandler.isAlive():
   916             import_logger.info("Initial import already running.")
   917             return
   918         self.importHandler = InitialImportHandler(self)
   919         self.importHandler.start()
   920         import_logger.info("Initial import started.")
   921         
   922     def startUpdateTemplate(self):
   923         if self.updateHandler and self.updateHandler.isAlive():
   924             import_logger.info("Initial import already running.")
   925             return
   926         self.updateHandler = UpdateHandler(self)
   927         self.updateHandler.start()
   928         import_logger.info("Initial import started.")
   929 
   930 class UpdateHandler(threading.Thread):
   931     vmm = None    
   932     def __init__(self, vmmanager):
   933         threading.Thread.__init__(self)
   934         self.vmm = vmmanager
   935     
   936     def run(self):
   937         try:
   938             self.vmm.updateTemplate()
   939         except:
   940             import_logger.info("Update template failed. Refer to service log for details.")
   941         self.vmm.start(force=True)
   942     
   943 class InitialImportHandler(threading.Thread):
   944     vmm = None    
   945     def __init__(self, vmmanager):
   946         threading.Thread.__init__(self)
   947         self.vmm = vmmanager
   948     
   949     def run(self):
   950         try:
   951             self.vmm.importTemplate(self.vmm.getMachineFolder() + '\\OsecVM.ova')
   952             self.vmm.updateTemplate()
   953         except:
   954             import_logger.info("Initial import failed. Refer to service log for details.")
   955         self.vmm.start(force=True)
   956             
   957 #handles browsing session creation 
   958 class BrowsingHandler(threading.Thread):
   959     vmm = None
   960     proxy = None
   961     def __init__(self, vmmanager, proxy):
   962         threading.Thread.__init__(self)
   963         self.vmm = vmmanager
   964         self.proxy = proxy
   965         
   966     def run(self):
   967         #browser = '\\\"/usr/bin/chromium; pidof dbus-launch | xargs kill\\\"'
   968         #browser = '\\\"/usr/bin/chromium\\\"'
   969         
   970         try:
   971             if self.proxy:
   972                 browser = '\\\"export http_proxy='+self.proxy+'; /usr/bin/chromium\\\"'
   973             else:
   974                 browser = '\\\"/usr/bin/chromium\\\"'
   975             self.vmm.browsingManager.started.wait() 
   976             result = Cygwin.sshExecuteX11(browser, self.vmm.browsingManager.ip_addr, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + self.vmm.browsingManager.vm_name + '/dvm_key')
   977             self.vmm.backupFile('/home/osecuser/.config/chromium', self.vmm.browsingManager.appDataDir + '/OpenSecurity/')
   978         except:
   979             logger.info("BrowsingHandler closing. Restarting browsing SDVM.")
   980 
   981         self.vmm.browsingManager.restart.set()
   982         
   983             
   984 # handles browsing Vm creation and destruction                    
   985 class BrowsingManager(threading.Thread):   
   986     vmm = None
   987     running = True
   988     restart = None
   989     ip_addr = None
   990     vm_name = None
   991     net_resource = None
   992     appDataDir = None
   993     
   994     def __init__(self, vmmanager):
   995         threading.Thread.__init__(self)
   996         self.vmm = vmmanager
   997         self.restart = threading.Event()
   998         self.started = threading.Event()
   999     
  1000     def stop(self):
  1001         self.running = False
  1002         self.restart.set()   
  1003      
  1004     def run(self):
  1005         while self.running:
  1006             self.restart.clear()
  1007             self.started.clear()
  1008             
  1009             if self.net_resource == None:
  1010                 logger.info("Missing browsing SDVM's network share. Skipping disconnect")
  1011             else:
  1012                 try:
  1013                     browsing_vm = urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'net_resource='+self.net_resource).readline()
  1014                     self.net_resource = None
  1015                 except urllib2.URLError:
  1016                     logger.error("Network share disconnect failed. OpenSecurity Tray client not running.")
  1017                     continue
  1018             
  1019             self.ip_addr = None
  1020 
  1021             if self.vm_name != None:
  1022                 self.vmm.poweroffVM(self.vm_name)
  1023                 self.vmm.removeVM(self.vm_name)
  1024             
  1025             try:
  1026                 self.vm_name = self.vmm.newSDVM()
  1027                 self.vmm.attachVDisk(self.vm_name, 'SATA', '0', '0', self.vmm.templateImage)
  1028                 self.vmm.genCertificate(self.vm_name)
  1029                 self.vmm.attachCertificate(self.vm_name)
  1030                 
  1031                 self.vmm.startVM(self.vm_name)
  1032                 
  1033                 self.ip_addr = self.vmm.waitStartup(self.vm_name)
  1034                 if self.ip_addr == None:
  1035                     logger.error("Failed to get ip address")
  1036                     continue
  1037                 else:
  1038                     logger.info("Got IP address for " + self.vm_name + ' ' + self.ip_addr)
  1039                 
  1040                 try:
  1041                     self.net_resource = '\\\\' + self.ip_addr + '\\Download'
  1042                     result = urllib2.urlopen('http://127.0.0.1:8090/netmount?'+'net_resource='+self.net_resource).readline()
  1043                 except urllib2.URLError:
  1044                     logger.error("Network drive connect failed. OpenSecurity Tray client not running.")
  1045                     self.net_resource = None
  1046                     continue
  1047                 
  1048                 user = self.vmm.getActiveUserName()
  1049                 if user == None:
  1050                     logger.error("Cannot get active user name")
  1051                     continue
  1052                 else:
  1053                     logger.info('Got active user name ' + user)
  1054                 sid = self.vmm.getUserSID(user)
  1055                 if sid == None:
  1056                     logger.error("Cannot get SID for active user")
  1057                     continue
  1058                 else:
  1059                     logger.info("Got active user SID " + sid + " for user " + user)
  1060                     
  1061                 path = self.vmm.getAppDataDir(sid)
  1062                 if path == None:
  1063                     logger.error("Cannot get AppDataDir for active user")
  1064                     continue
  1065                 else:
  1066                     logger.info("Got AppData dir for user " + user + ': ' + path)
  1067                 
  1068                 self.appDataDir = Cygwin.cygPath(path)
  1069                 logger.info("Restoring browser settings in AppData dir " + self.appDataDir)
  1070                 # create OpenSecurity settings dir on local machine user home /AppData/Roaming 
  1071                 Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + self.appDataDir + '/OpenSecurity\\\"')
  1072                 # create chromium settings dir on local machine if not existing
  1073                 Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + self.appDataDir + '/OpenSecurity/chromium\\\"')
  1074                 # create chromium settings dir on remote machine if not existing
  1075                 Cygwin.sshExecute('"mkdir -p \\\"/home/osecuser/.config\\\""', self.ip_addr, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + self.vm_name + '/dvm_key')
  1076                 #restore settings on vm
  1077                 self.vmm.restoreFile(self.appDataDir + '/OpenSecurity/chromium', '/home/osecuser/.config/')
  1078                 self.started.set()
  1079                 logger.info("Browsing SDVM running.")
  1080                 self.restart.wait()
  1081             except Exception as e:
  1082                 logger.error("Unexpected error: ".join(e))
  1083                 logger.error("BrowsingHandler failed. Cleaning up")
  1084                 #self.running= False
  1085                 
  1086 class DeviceHandler(threading.Thread): 
  1087     vmm = None
  1088     existingRSDs = None
  1089     attachedRSDs = None  
  1090     running = True
  1091     def __init__(self, vmmanger): 
  1092         threading.Thread.__init__(self)
  1093         self.vmm = vmmanger
  1094  
  1095     def stop(self):
  1096         self.running = False
  1097         
  1098     def run(self):
  1099         self.existingRSDs = dict()
  1100         self.attachedRSDs = self.vmm.getAttachedRSDs()
  1101         
  1102         while self.running:
  1103             tmp_rsds = self.vmm.getExistingRSDs()
  1104             if tmp_rsds.keys() == self.existingRSDs.keys():
  1105                 logger.debug("Nothing's changed. sleep(3)")
  1106                 time.sleep(3)
  1107                 continue
  1108             
  1109             showTrayMessage('System changed.\nEvaluating...', 7000)
  1110             logger.info("Something's changed")
  1111             tmp_attached = self.attachedRSDs     
  1112             for vm_name in tmp_attached.keys():
  1113                 if tmp_attached[vm_name] not in tmp_rsds.values():
  1114                     ip = self.vmm.getHostOnlyIP(vm_name)
  1115                     if ip == None:
  1116                         logger.error("Failed getting hostonly IP for " + vm_name)
  1117                         continue
  1118                     try:
  1119                         net_resource = '\\\\' + ip + '\\USB'
  1120                         result = urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'net_resource='+net_resource).readline()
  1121                     except urllib2.URLError:
  1122                         logger.error("Network drive disconnect failed. OpenSecurity Tray client not running.")
  1123                         continue
  1124                     
  1125                     # detach not necessary as already removed from vm description upon disconnect
  1126                     #self.vmm.detachRSD(vm_name, self.attachedRSDs[vm_name])
  1127                     del self.attachedRSDs[vm_name]
  1128                     self.vmm.poweroffVM(vm_name)
  1129                     self.vmm.removeVM(vm_name)
  1130                     #break
  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                     new_sdvm = self.vmm.newSDVM()
  1138                     self.vmm.attachVDisk(new_sdvm, 'SATA', '0', '0', self.vmm.templateImage)
  1139                     self.vmm.startVM(new_sdvm)
  1140                     new_ip = self.vmm.waitStartup(new_sdvm)
  1141                     if new_ip == None:
  1142                         logger.error("Error getting IP address of SDVM. Cleaning up.")
  1143                         self.vmm.poweroffVM(new_sdvm)
  1144                         self.vmm.removeVM(new_sdvm)
  1145                         continue
  1146                     else:
  1147                         logger.info("Got IP address for " + new_sdvm + ' ' + new_ip)
  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.poweroffVM(new_sdvm)
  1154                         self.vmm.removeVM(new_sdvm)
  1155                         continue
  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.poweroffVM(new_sdvm)
  1162                         self.vmm.removeVM(new_sdvm)
  1163                         continue
  1164                     
  1165             self.existingRSDs = tmp_rsds