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