OpenSecurity/bin/vmmanager.pyw
changeset 87 d5b04809faca
parent 81 84663db906d7
child 90 bfd41c38d156
     1.1 --- a/OpenSecurity/bin/vmmanager.pyw	Thu Feb 27 11:39:37 2014 +0100
     1.2 +++ b/OpenSecurity/bin/vmmanager.pyw	Tue Mar 04 16:54:51 2014 +0100
     1.3 @@ -1,598 +1,598 @@
     1.4 -'''
     1.5 -Created on Nov 19, 2013
     1.6 -
     1.7 -@author: BarthaM
     1.8 -'''
     1.9 -import os
    1.10 -import os.path
    1.11 -from subprocess import Popen, PIPE, call, STARTUPINFO, _subprocess
    1.12 -import sys
    1.13 -import re
    1.14 -
    1.15 -from cygwin import Cygwin
    1.16 -from environment import Environment
    1.17 -import threading
    1.18 -import time
    1.19 -import string
    1.20 -
    1.21 -import shutil
    1.22 -import stat
    1.23 -import tempfile
    1.24 -from opensecurity_util import logger, setupLogger, OpenSecurityException
    1.25 -import ctypes
    1.26 -import itertools
    1.27 -DEBUG = True
    1.28 -
    1.29 -class VMManagerException(Exception):
    1.30 -    def __init__(self, value):
    1.31 -        self.value = value
    1.32 -    def __str__(self):
    1.33 -        return repr(self.value)
    1.34 -
    1.35 -class USBFilter:
    1.36 -    vendorid = ""
    1.37 -    productid = ""
    1.38 -    revision = ""
    1.39 -    
    1.40 -    def __init__(self, vendorid, productid, revision):
    1.41 -        self.vendorid = vendorid.lower()
    1.42 -        self.productid = productid.lower()
    1.43 -        self.revision = revision.lower()
    1.44 -        return
    1.45 -    
    1.46 -    def __eq__(self, other):
    1.47 -        return self.vendorid == other.vendorid and self.productid == other.productid and self.revision == other.revision
    1.48 -    
    1.49 -    def __hash__(self):
    1.50 -        return hash(self.vendorid) ^ hash(self.productid) ^ hash(self.revision)
    1.51 -    
    1.52 -    def __repr__(self):
    1.53 -        return "VendorId = \'" + str(self.vendorid) + "\' ProductId = \'" + str(self.productid) + "\' Revision = \'" + str(self.revision) + "\'"
    1.54 - 
    1.55 -class VMManager(object):
    1.56 -    vmRootName = "SecurityDVM"
    1.57 -    systemProperties = None
    1.58 -    _instance = None
    1.59 -    machineFolder = ''
    1.60 -    rsdHandler = None
    1.61 -    
    1.62 -    def __init__(self):
    1.63 -        self.systemProperties = self.getSystemProperties()
    1.64 -        self.machineFolder = self.systemProperties["Default machine folder"]
    1.65 -        self.cleanup()
    1.66 -        self.rsdHandler = DeviceHandler(self)
    1.67 -        self.rsdHandler.start()
    1.68 -        return
    1.69 -    
    1.70 -    @staticmethod
    1.71 -    def getInstance():
    1.72 -        if VMManager._instance == None:
    1.73 -            VMManager._instance = VMManager()
    1.74 -        return VMManager._instance
    1.75 -    
    1.76 -    def cleanup(self):
    1.77 -        if self.rsdHandler != None:
    1.78 -            self.rsdHandler.stop()
    1.79 -            self.rsdHandler.join()
    1.80 -        drives = self.getNetworkDrives()
    1.81 -        for drive in drives.keys():
    1.82 -            self.unmapNetworkDrive(drive)
    1.83 -        for vm in self.listSDVM():
    1.84 -            self.poweroffVM(vm)
    1.85 -            self.removeVM(vm)
    1.86 -        
    1.87 -    # return hosty system properties
    1.88 -    def getSystemProperties(self):
    1.89 -        result = Cygwin.vboxExecute('list systemproperties')
    1.90 -        if result[1]=='':
    1.91 -            return None
    1.92 -        props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result[1].strip().splitlines()))
    1.93 -        #logger.debug(props)
    1.94 -        return props
    1.95 -    
    1.96 -    # return the folder containing the guest VMs     
    1.97 -    def getMachineFolder(self):
    1.98 -        return self.machineFolder
    1.99 -
   1.100 -    # list all existing VMs registered with VBox
   1.101 -    def listVM(self):
   1.102 -        result = Cygwin.vboxExecute('list vms')[1]
   1.103 -        vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   1.104 -        return vms
   1.105 -    
   1.106 -    # list running VMs
   1.107 -    def listRunningVMS(self):
   1.108 -        result = Cygwin.vboxExecute('list runningvms')[1]
   1.109 -        vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   1.110 -        return vms
   1.111 -    
   1.112 -    # list existing SDVMs
   1.113 -    def listSDVM(self):
   1.114 -        vms = self.listVM()
   1.115 -        svdms = []
   1.116 -        for vm in vms:
   1.117 -            if vm.startswith(self.vmRootName) and vm != self.vmRootName:
   1.118 -                svdms.append(vm)
   1.119 -        return svdms
   1.120 -    
   1.121 -    # generate valid (not already existing SDVM name). necessary for creating a new VM
   1.122 -    def generateSDVMName(self):
   1.123 -        vms = self.listVM()
   1.124 -        for i in range(0,999):
   1.125 -            if(not self.vmRootName+str(i) in vms):
   1.126 -                return self.vmRootName+str(i)
   1.127 -        return ''
   1.128 -    
   1.129 -    # return the RSDs connected to the host
   1.130 -    def getConnectedRSDS(self):
   1.131 -        results = Cygwin.vboxExecute('list usbhost')[1]
   1.132 -        results = results.split('Host USB Devices:')[1].strip()
   1.133 -        
   1.134 -        items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   1.135 -        rsds = dict()   
   1.136 -        for item in items:
   1.137 -            props = dict()
   1.138 -            for line in item.splitlines():
   1.139 -                if line != "":         
   1.140 -                    k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   1.141 -                    props[k] = v
   1.142 -            
   1.143 -            if 'Product' in props.keys() and props['Product'] == 'Mass Storage':
   1.144 -                usb_filter = USBFilter( re.search(r"\((?P<vid>[0-9A-Fa-f]+)\)", props['VendorId']).groupdict()['vid'], 
   1.145 -                                        re.search(r"\((?P<pid>[0-9A-Fa-f]+)\)", props['ProductId']).groupdict()['pid'],
   1.146 -                                        re.search(r"\((?P<rev>[0-9A-Fa-f]+)\)", props['Revision']).groupdict()['rev'] )
   1.147 -                rsds[props['UUID']] = usb_filter;
   1.148 -                logger.debug(usb_filter)
   1.149 -        return rsds
   1.150 -    
   1.151 -    # return the RSDs attached to all existing SDVMs
   1.152 -    def getAttachedRSDs(self):
   1.153 -        vms = self.listSDVM()
   1.154 -        attached_devices = dict()
   1.155 -        for vm in vms:
   1.156 -            rsd_filter = self.getUSBFilter(vm)
   1.157 -            if rsd_filter != None:
   1.158 -                attached_devices[vm] = rsd_filter
   1.159 -        return attached_devices
   1.160 -    
   1.161 -    # configures hostonly networking and DHCP server. requires admin rights
   1.162 -    def configureHostNetworking(self):
   1.163 -        #cmd = 'vboxmanage list hostonlyifs'
   1.164 -        #Cygwin.vboxExecute(cmd)
   1.165 -        #cmd = 'vboxmanage hostonlyif remove \"VirtualBox Host-Only Ethernet Adapter\"'
   1.166 -        #Cygwin.vboxExecute(cmd)
   1.167 -        #cmd = 'vboxmanage hostonlyif create'
   1.168 -        #Cygwin.vboxExecute(cmd)
   1.169 -        Cygwin.vboxExecute('hostonlyif ipconfig \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.1 --netmask 255.255.255.0')
   1.170 -        #cmd = 'vboxmanage dhcpserver add'
   1.171 -        #Cygwin.vboxExecute(cmd)
   1.172 -        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')
   1.173 -    
   1.174 -    #create new virtual machine instance based on template vm named SecurityDVM (\SecurityDVM\SecurityDVM.vmdk)
   1.175 -    def createVM(self, vm_name):
   1.176 -        hostonly_if = self.getHostOnlyIFs()
   1.177 -        Cygwin.vboxExecute('createvm --name ' + vm_name + ' --ostype Debian --register')
   1.178 -        Cygwin.vboxExecute('modifyvm ' + vm_name + ' --memory 512 --vram 10 --cpus 1 --usb on --usbehci on --nic1 hostonly --hostonlyadapter1 \"' + hostonly_if['Name'] + '\" --nic2 nat')
   1.179 -        Cygwin.vboxExecute('storagectl ' + vm_name + ' --name SATA --add sata --portcount 2')
   1.180 -        return
   1.181 -    
   1.182 -    # attach storage image to controller
   1.183 -    def storageAttach(self, vm_name):
   1.184 -        if self.isStorageAttached(vm_name):
   1.185 -            self.storageDetach(vm_name)
   1.186 -        Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium \"'+ self.machineFolder + '\SecurityDVM\SecurityDVM.vmdk\"')
   1.187 -        return
   1.188 -    
   1.189 -    # return true if storage is attached 
   1.190 -    def isStorageAttached(self, vm_name):
   1.191 -        info = self.getVMInfo(vm_name)
   1.192 -        return (info['SATA-0-0']!='none')
   1.193 -    
   1.194 -    # detach storage from controller
   1.195 -    def storageDetach(self, vm_name):
   1.196 -        if self.isStorageAttached(vm_name):
   1.197 -            Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium none')
   1.198 -        return
   1.199 -    
   1.200 -    def changeStorageType(self, filename, storage_type):
   1.201 -        Cygwin.vboxExecute('modifyhd \"' + filename + '\" --type ' + storage_type)
   1.202 -        return
   1.203 -    
   1.204 -    # list storage snaphots for VM
   1.205 -    def updateTemplate(self):
   1.206 -        self.cleanup()
   1.207 -        self.poweroffVM('SecurityDVM')
   1.208 -        self.waitShutdown('SecurityDVM')
   1.209 -        
   1.210 -        # check for updates
   1.211 -        self.genCertificateISO('SecurityDVM')
   1.212 -        self.attachCertificateISO('SecurityDVM')
   1.213 -        
   1.214 -        self.storageDetach('SecurityDVM')
   1.215 -        results = Cygwin.vboxExecute('list hdds')[1]
   1.216 -        results = results.replace('Parent UUID', 'Parent')
   1.217 -        items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   1.218 -        
   1.219 -        snaps = dict()   
   1.220 -        for item in items:
   1.221 -            props = dict()
   1.222 -            for line in item.splitlines():
   1.223 -                if line != "":         
   1.224 -                    k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   1.225 -                    props[k] = v;
   1.226 -            snaps[props['UUID']] = props
   1.227 -        
   1.228 -        
   1.229 -        template_storage = self.machineFolder + '\SecurityDVM\SecurityDVM.vmdk'
   1.230 -        
   1.231 -        # find template uuid
   1.232 -        template_uuid = ''
   1.233 -        for hdd in snaps.values():
   1.234 -            if hdd['Location'] == template_storage:
   1.235 -                template_uuid = hdd['UUID']
   1.236 -        logger.debug('found parent uuid ' + template_uuid)
   1.237 -        
   1.238 -        # remove snapshots 
   1.239 -        for hdd in snaps.values():
   1.240 -            if hdd['Parent'] == template_uuid:
   1.241 -                #template_uuid = hdd['UUID']
   1.242 -                logger.debug('removing snapshot ' + hdd['UUID'])
   1.243 -                results = Cygwin.vboxExecute('closemedium disk {' + hdd['UUID'] + '} --delete')[1]
   1.244 -                # parse result 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
   1.245 -        
   1.246 -        self.changeStorageType(template_storage,'normal')
   1.247 -        self.storageAttach('SecurityDVM')
   1.248 -        self.startVM('SecurityDVM')
   1.249 -        self.waitStartup('SecurityDVM')
   1.250 -        Cygwin.sshExecute('"sudo apt-get -y update"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key'  )
   1.251 -        Cygwin.sshExecute('"sudo apt-get -y upgrade"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key'  )
   1.252 -        #self.stopVM('SecurityDVM')
   1.253 -        self.hibernateVM('SecurityDVM')
   1.254 -        self.waitShutdown('SecurityDVM')
   1.255 -        self.storageDetach('SecurityDVM')
   1.256 -        self.changeStorageType(template_storage,'immutable')
   1.257 -        self.storageAttach('SecurityDVM')
   1.258 -        self.rsdHandler = DeviceHandler(self)
   1.259 -        self.rsdHandler.start()
   1.260 -    
   1.261 -    #remove VM from the system. should be used on VMs returned by listSDVMs    
   1.262 -    def removeVM(self, vm_name):
   1.263 -        logger.info('Removing ' + vm_name)
   1.264 -        Cygwin.vboxExecute('unregistervm ' + vm_name + ' --delete')
   1.265 -        machineFolder = Cygwin.cygPath(self.machineFolder)
   1.266 -        Cygwin.bashExecute('"/usr/bin/rm -rf ' + machineFolder + '/' + vm_name + '"')
   1.267 -    
   1.268 -    # start VM
   1.269 -    def startVM(self, vm_name):
   1.270 -        logger.info('Starting ' +  vm_name)
   1.271 -        result = Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless' )
   1.272 -        while not string.find(str(result), 'successfully started',):
   1.273 -            logger.error("Failed to start SDVM: " + vm_name + " retrying")
   1.274 -            time.sleep(1)
   1.275 -            result = Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless')
   1.276 -        return result[0]
   1.277 -    
   1.278 -    # return wether VM is running or not
   1.279 -    def isVMRunning(self, vm_name):
   1.280 -        return vm_name in self.listRunningVMS()    
   1.281 -    
   1.282 -    # stop VM
   1.283 -    def stopVM(self, vm_name):
   1.284 -        logger.info('Sending shutdown signal to ' + vm_name)
   1.285 -        Cygwin.sshExecute( '"sudo shutdown -h now"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key' )
   1.286 -    
   1.287 -    # stop VM
   1.288 -    def hibernateVM(self, vm_name):
   1.289 -        logger.info('Sending shutdown signal to ' + vm_name)
   1.290 -        Cygwin.sshExecute( '"sudo hibernate-disk&"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key', wait_return=False )
   1.291 -            
   1.292 -    # poweroff VM
   1.293 -    def poweroffVM(self, vm_name):
   1.294 -        if not self.isVMRunning(vm_name):
   1.295 -            return
   1.296 -        logger.info('Powering off ' + vm_name)
   1.297 -        return Cygwin.vboxExecute('controlvm ' + vm_name + ' poweroff')
   1.298 -    
   1.299 -    #list the hostonly IFs exposed by the VBox host
   1.300 -    @staticmethod    
   1.301 -    def getHostOnlyIFs():
   1.302 -        result = Cygwin.vboxExecute('list hostonlyifs')[1]
   1.303 -        if result=='':
   1.304 -            return None
   1.305 -        props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result.strip().splitlines()))
   1.306 -        return props
   1.307 -    
   1.308 -    # return the hostOnly IP for a running guest or the host
   1.309 -    @staticmethod    
   1.310 -    def getHostOnlyIP(vm_name):
   1.311 -        if vm_name == None:
   1.312 -            logger.info('Gettting hostOnly IP address for Host')
   1.313 -            return VMManager.getHostOnlyIFs()['IPAddress']
   1.314 -        else:
   1.315 -            logger.info('Gettting hostOnly IP address ' + vm_name)
   1.316 -            result = Cygwin.vboxExecute('guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP')
   1.317 -            if result=='':
   1.318 -                return None
   1.319 -            result = result[1]
   1.320 -            if result.startswith('No value set!'):
   1.321 -                return None
   1.322 -            return result[result.index(':')+1:].strip()
   1.323 -            
   1.324 -    # attach removable storage device to VM by provision of filter
   1.325 -    def attachRSD(self, vm_name, rsd_filter):
   1.326 -        return Cygwin.vboxExecute('usbfilter add 0 --target ' + vm_name + ' --name OpenSecurityRSD --vendorid ' + rsd_filter.vendorid + ' --productid ' + rsd_filter.productid + ' --revision ' + rsd_filter.revision)
   1.327 -    
   1.328 -    # detach removable storage from VM by 
   1.329 -    def detachRSD(self, vm_name):
   1.330 -        return Cygwin.vboxExecute('usbfilter remove 0 --target ' + vm_name )
   1.331 -        
   1.332 -    
   1.333 -    # return the description set for an existing VM
   1.334 -    def getVMInfo(self, vm_name):
   1.335 -        results = Cygwin.vboxExecute('showvminfo ' + vm_name + ' --machinereadable')[1]
   1.336 -        props = dict((k.strip().strip('"'),v.strip().strip('"')) for k,v in (line.split('=', 1) for line in results.splitlines()))
   1.337 -        #logger.debug(props)
   1.338 -        return props
   1.339 -    
   1.340 -    # return the configured USB filter for an existing VM 
   1.341 -    def getUSBFilter(self, vm_name):
   1.342 -        props = self.getVMInfo(vm_name)
   1.343 -        keys = set(['USBFilterVendorId1', 'USBFilterProductId1', 'USBFilterRevision1'])
   1.344 -        keyset = set(props.keys())
   1.345 -        usb_filter = None
   1.346 -        if keyset.issuperset(keys):
   1.347 -            usb_filter = USBFilter(props['USBFilterVendorId1'], props['USBFilterProductId1'], props['USBFilterRevision1'])
   1.348 -        return usb_filter
   1.349 -    
   1.350 -    #generates ISO containing authorized_keys for use with guest VM
   1.351 -    def genCertificateISO(self, vm_name):
   1.352 -        machineFolder = Cygwin.cygPath(self.machineFolder)
   1.353 -        # remove .ssh folder if exists
   1.354 -        cmd = '\"/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"'
   1.355 -        Cygwin.bashExecute(cmd)
   1.356 -        # remove .ssh folder if exists
   1.357 -        Cygwin.bashExecute('\"/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"\"')
   1.358 -        # create .ssh folder in vm_name
   1.359 -        Cygwin.bashExecute('\"/usr/bin/mkdir -p \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"')
   1.360 -        # generate dvm_key pair in vm_name / .ssh     
   1.361 -        Cygwin.bashExecute('\"/usr/bin/ssh-keygen -q -t rsa -N \\"\\" -C \\\"' + vm_name + '\\\" -f \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\"\"')
   1.362 -        # move out private key
   1.363 -        Cygwin.bashExecute('\"/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\" \\\"' + machineFolder + '/' + vm_name + '\\\"')
   1.364 -        # set permissions for private key
   1.365 -        Cygwin.bashExecute('\"/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"\"')
   1.366 -        # rename public key to authorized_keys
   1.367 -        Cygwin.bashExecute('\"/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key.pub\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"')
   1.368 -        # set permissions for authorized_keys
   1.369 -        Cygwin.bashExecute('\"/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"\"')
   1.370 -        # generate iso image with .ssh/authorized keys
   1.371 -        Cygwin.bashExecute('\"/usr/bin/genisoimage -J -R -o \\\"' + machineFolder + '/' + vm_name + '/'+ vm_name + '.iso\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"')
   1.372 -    
   1.373 -    # attaches generated ssh public cert to guest vm
   1.374 -    def attachCertificateISO(self, vm_name):
   1.375 -        result = Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 1 --device 0 --type dvddrive --mtype readonly --medium \"' + self.machineFolder + '\\' + vm_name + '\\'+ vm_name + '.iso\"')
   1.376 -        return result
   1.377 -    
   1.378 -    # wait for machine to come up
   1.379 -    def waitStartup(self, vm_name, timeout_ms = 30000):
   1.380 -        result = Cygwin.vboxExecute('guestproperty wait ' + vm_name + ' SDVMStarted --timeout ' + str(timeout_ms) + ' --fail-on-timeout')
   1.381 -        return VMManager.getHostOnlyIP(vm_name)
   1.382 -    
   1.383 -    # wait for machine to shutdown
   1.384 -    def waitShutdown(self, vm_name):
   1.385 -        while vm_name in self.listRunningVMS():
   1.386 -            time.sleep(1)
   1.387 -        return
   1.388 -        
   1.389 -    # handles browsing request    
   1.390 -    def handleBrowsingRequest(self):
   1.391 -        if VMManager.handleDeviceChangeLock.acquire(True):
   1.392 -            new_sdvm = self.generateSDVMName()
   1.393 -            self.createVM(new_sdvm)
   1.394 -            self.storageAttach(new_sdvm)
   1.395 -            self.genCertificateISO(new_sdvm)
   1.396 -            self.attachCertificateISO(new_sdvm)
   1.397 -            self.startVM(new_sdvm)
   1.398 -            new_ip = self.waitStartup(new_sdvm)
   1.399 -            if new_ip != None:
   1.400 -                self.mapNetworkDrive('g:', '\\\\' + new_ip + '\\Download', None, None)
   1.401 -            #TODO: cleanup notifications somwhere else (eg. machine shutdown)
   1.402 -            VMManager.handleDeviceChangeLock.release()
   1.403 -        return new_sdvm
   1.404 -    
   1.405 -    #Small function to check the availability of network resource.
   1.406 -    #def isAvailable(self, path):
   1.407 -        #return os.path.exists(path)
   1.408 -        #result = Cygwin.cmdExecute('IF EXIST "' + path + '" echo YES')
   1.409 -        #return string.find(result[1], 'YES',)
   1.410 -    
   1.411 -    #Small function to check if the mention location is a directory
   1.412 -    def isDirectory(self, path):
   1.413 -        result = Cygwin.cmdExecute('dir ' + path + ' | FIND ".."')
   1.414 -        return string.find(result[1], 'DIR',)
   1.415 -
   1.416 -    def mapNetworkDrive(self, drive, networkPath, user, password):
   1.417 -        self.unmapNetworkDrive(drive)
   1.418 -        #Check for drive availability
   1.419 -        if os.path.exists(drive):
   1.420 -            logger.error("Drive letter is already in use: " + drive)
   1.421 -            return -1
   1.422 -        #Check for network resource availability
   1.423 -        while not os.path.exists(networkPath):
   1.424 -            time.sleep(1)
   1.425 -            logger.info("Path not accessible: " + networkPath + " retrying")
   1.426 -            #return -1
   1.427 -    
   1.428 -        command = 'USE ' + drive + ' ' + networkPath + ' /PERSISTENT:NO'
   1.429 -        if user != None:
   1.430 -            command += ' ' + password + ' /User' + user
   1.431 -    
   1.432 -        #TODO: Execute 'NET USE' command with authentication
   1.433 -        result = Cygwin.execute('C:\\Windows\\system32\\net.exe', command)
   1.434 -        if string.find(result[1], 'successfully',) == -1:
   1.435 -            logger.error("Failed: NET " + command)
   1.436 -            return -1
   1.437 -        return 1
   1.438 -    
   1.439 -    def unmapNetworkDrive(self, drive):
   1.440 -        drives = self.getNetworkDrives()
   1.441 -        if drive not in drives.keys():
   1.442 -            return 1 
   1.443 -        result = Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE ' + drive + ' /DELETE /YES')
   1.444 -        if string.find(str(result[1]), 'successfully',) == -1:
   1.445 -            logger.error(result[2])
   1.446 -            return -1
   1.447 -        return 1
   1.448 -    
   1.449 -    def getNetworkDrives(self):
   1.450 -        ip = VMManager.getHostOnlyIP(None)
   1.451 -        ip = ip[:ip.rindex('.')]
   1.452 -        drives = dict()    
   1.453 -        result = Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE')
   1.454 -        for line in result[1].splitlines():
   1.455 -            if ip in line:
   1.456 -                parts = line.split()
   1.457 -                drives[parts[1]] = parts[2]
   1.458 -        return drives
   1.459 -            
   1.460 -    def genNetworkDrive(self):
   1.461 -        network_drives = self.getNetworkDrives()
   1.462 -        logical_drives = self.getLogicalDrives()
   1.463 -        drives = list(map(chr, range(68, 91)))  
   1.464 -        for drive in drives:
   1.465 -            if drive+':' not in network_drives and drive not in logical_drives:
   1.466 -                return drive+':'
   1.467 -
   1.468 -    def getNetworkDrive(self, vm_name):
   1.469 -        ip = self.getHostOnlyIP(vm_name)
   1.470 -        result = Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE')
   1.471 -        for line in result[1].splitlines():
   1.472 -            if ip in line:
   1.473 -                parts = line.split()
   1.474 -                return parts[0]
   1.475 -    
   1.476 -    def getLogicalDrives(self):
   1.477 -        drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
   1.478 -        return list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
   1.479 -                    
   1.480 -        #vms = self.listSDVM()
   1.481 -        #for vm in vms:
   1.482 -        #    ip = self.getHostOnlyIP(vm)
   1.483 -        
   1.484 -class DeviceHandler(threading.Thread): 
   1.485 -    vmm = None
   1.486 -    #handleDeviceChangeLock = threading.Lock()
   1.487 -    attachedRSDs = None  
   1.488 -    connectedRSDs = None
   1.489 -    running = True
   1.490 -    def __init__(self, vmmanger): 
   1.491 -        threading.Thread.__init__(self)
   1.492 -        self.vmm = vmmanger
   1.493 - 
   1.494 -    def stop(self):
   1.495 -        self.running = False
   1.496 -        
   1.497 -    def run(self):
   1.498 -        self.connectedRSDs = dict()#self.vmm.getConnectedRSDS()
   1.499 -        self.attachedRSDs = self.vmm.getAttachedRSDs()
   1.500 -        while self.running:
   1.501 -            tmp_rsds = self.vmm.getConnectedRSDS()
   1.502 -            if tmp_rsds.keys() == self.connectedRSDs.keys():
   1.503 -                logger.debug("Nothing's changed. sleep(3)")
   1.504 -                time.sleep(3)
   1.505 -                continue
   1.506 -            
   1.507 -            logger.info("Something's changed")          
   1.508 -            
   1.509 -            self.connectedRSDs = tmp_rsds
   1.510 -            self.attachedRSDs = self.vmm.getAttachedRSDs()
   1.511 -            
   1.512 -            
   1.513 -            for vm_name in self.attachedRSDs.keys():
   1.514 -                if self.attachedRSDs[vm_name] not in self.connectedRSDs.values():
   1.515 -                    drive = self.vmm.getNetworkDrive(vm_name)
   1.516 -                    self.vmm.unmapNetworkDrive(drive)
   1.517 -                    #self.stopVM(vm_name)
   1.518 -                    self.vmm.detachRSD(vm_name)
   1.519 -                    self.vmm.poweroffVM(vm_name)
   1.520 -                    self.vmm.removeVM(vm_name)
   1.521 -            #create new vm for attached device if any
   1.522 -            self.attachedRSDs = self.vmm.getAttachedRSDs()
   1.523 -            self.connectedRSDs = self.vmm.getConnectedRSDS()
   1.524 -            
   1.525 -            new_ip = None
   1.526 -            for connected_device in self.connectedRSDs.values():
   1.527 -                if (self.attachedRSDs and False) or (connected_device not in self.attachedRSDs.values()):
   1.528 -                    new_sdvm = self.vmm.generateSDVMName()
   1.529 -                    self.vmm.createVM(new_sdvm)
   1.530 -                    self.vmm.storageAttach(new_sdvm)
   1.531 -                    self.vmm.attachRSD(new_sdvm, connected_device)
   1.532 -                    self.vmm.startVM(new_sdvm)
   1.533 -                    new_ip = self.vmm.waitStartup(new_sdvm)
   1.534 -                    drive = self.vmm.genNetworkDrive()
   1.535 -                    if new_ip != None:
   1.536 -                        self.vmm.mapNetworkDrive(drive, '\\\\' + new_ip + '\\USB', None, None)
   1.537 -                    #TODO: cleanup notifications somwhere else (eg. machine shutdown)
   1.538 -            #self.handleDeviceChangeLock.release()
   1.539 -                
   1.540 -
   1.541 -if __name__ == '__main__':
   1.542 -    #man = VMManager.getInstance()
   1.543 -    #man.listVM()
   1.544 -    #print man.getConnectedRSDs()
   1.545 -    #print man.getNetworkDrives()
   1.546 -    #man.genNetworkDrive()
   1.547 -    drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
   1.548 -    print list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
   1.549 -    #print list(map(chr, range(68, 91))) 
   1.550 -    
   1.551 -    #time.sleep(-1)
   1.552 -    #man.listVM()
   1.553 -    #man.listVM()
   1.554 -    #man.listVM()
   1.555 -    #man.listVM()
   1.556 -    #man.genCertificateISO('SecurityDVM0')
   1.557 -    #man.guestExecute('SecurityDVM0', '/bin/ls -la')
   1.558 -    #logger = setupLogger('VMManager')
   1.559 -    #c = Cygwin()
   1.560 -    
   1.561 -    #man.sshExecute('/bin/ls -la', 'SecurityDVM0')
   1.562 -    #man.sshExecuteX11('/usr/bin/iceweasel', 'SecurityDVM0')
   1.563 -    #man.removeVM('SecurityDVM0')
   1.564 -    #man.netUse('192.168.56.134', 'USB\\')
   1.565 -    #ip = '192.168.56.139'
   1.566 -    
   1.567 -    #man.cygwin_path = 'c:\\cygwin64\\bin\\'
   1.568 -    #man.handleDeviceChange()
   1.569 -    #print man.listSDVM()
   1.570 -    #man.configureHostNetworking()
   1.571 -    #new_vm = man.generateSDVMName()
   1.572 -    #man.createVM(new_vm)
   1.573 -    
   1.574 -    #print Cygwin.cmd()
   1.575 -    #man.isAvailable('c:')
   1.576 -    #ip = man.getHostOnlyIP('SecurityDVM0')
   1.577 -    #man.mapNetworkDrive('h:', '\\\\' + ip + '\Download', None, None)
   1.578 -    
   1.579 -    #man.genCertificateISO(new_vm)
   1.580 -    #man.attachCertificateISO(new_vm)
   1.581 -    
   1.582 -    #man.attachCertificateISO(vm_name)
   1.583 -    #man.guestExecute(vm_name, "ls")
   1.584 -    #man.sshGuestX11Execute('SecurityDVM1', '/usr/bin/iceweasel')
   1.585 -    #time.sleep(60)
   1.586 -    #print man.cygwinPath("C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\.ssh\*")
   1.587 -    #man.genCertificateISO('SecurityDVM')
   1.588 -    #man.attachCertificateISO('SecurityDVM')
   1.589 -    #man.isStorageAttached('SecurityDVM')
   1.590 -    #man.guestExecute('SecurityDVM', 'sudo apt-get -y update')
   1.591 -    #man.guestExecute('SecurityDVM', 'sudo apt-get -y upgrade' )
   1.592 -    
   1.593 -    #man.stopVM('SecurityDVM')
   1.594 -    #man.storageDetach('SecurityDVM')
   1.595 -    #man.changeStorageType('C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\SecurityDVM.vmdk','immutable')
   1.596 -    #man.storageAttach('SecurityDVM')
   1.597 -    
   1.598 -    
   1.599 -    #cmd = "c:\\cygwin64\\bin\\bash.exe --login -c \"/bin/ls\""
   1.600 -    #man.execute(cmd)
   1.601 -    
   1.602 +'''
   1.603 +Created on Nov 19, 2013
   1.604 +
   1.605 +@author: BarthaM
   1.606 +'''
   1.607 +import os
   1.608 +import os.path
   1.609 +from subprocess import Popen, PIPE, call, STARTUPINFO, _subprocess
   1.610 +import sys
   1.611 +import re
   1.612 +
   1.613 +from cygwin import Cygwin
   1.614 +from environment import Environment
   1.615 +import threading
   1.616 +import time
   1.617 +import string
   1.618 +
   1.619 +import shutil
   1.620 +import stat
   1.621 +import tempfile
   1.622 +from opensecurity_util import logger, setupLogger, OpenSecurityException
   1.623 +import ctypes
   1.624 +import itertools
   1.625 +DEBUG = True
   1.626 +
   1.627 +class VMManagerException(Exception):
   1.628 +    def __init__(self, value):
   1.629 +        self.value = value
   1.630 +    def __str__(self):
   1.631 +        return repr(self.value)
   1.632 +
   1.633 +class USBFilter:
   1.634 +    vendorid = ""
   1.635 +    productid = ""
   1.636 +    revision = ""
   1.637 +    
   1.638 +    def __init__(self, vendorid, productid, revision):
   1.639 +        self.vendorid = vendorid.lower()
   1.640 +        self.productid = productid.lower()
   1.641 +        self.revision = revision.lower()
   1.642 +        return
   1.643 +    
   1.644 +    def __eq__(self, other):
   1.645 +        return self.vendorid == other.vendorid and self.productid == other.productid and self.revision == other.revision
   1.646 +    
   1.647 +    def __hash__(self):
   1.648 +        return hash(self.vendorid) ^ hash(self.productid) ^ hash(self.revision)
   1.649 +    
   1.650 +    def __repr__(self):
   1.651 +        return "VendorId = \'" + str(self.vendorid) + "\' ProductId = \'" + str(self.productid) + "\' Revision = \'" + str(self.revision) + "\'"
   1.652 + 
   1.653 +class VMManager(object):
   1.654 +    vmRootName = "SecurityDVM"
   1.655 +    systemProperties = None
   1.656 +    _instance = None
   1.657 +    machineFolder = ''
   1.658 +    rsdHandler = None
   1.659 +    
   1.660 +    def __init__(self):
   1.661 +        self.systemProperties = self.getSystemProperties()
   1.662 +        self.machineFolder = self.systemProperties["Default machine folder"]
   1.663 +        self.cleanup()
   1.664 +        self.rsdHandler = DeviceHandler(self)
   1.665 +        self.rsdHandler.start()
   1.666 +        return
   1.667 +    
   1.668 +    @staticmethod
   1.669 +    def getInstance():
   1.670 +        if VMManager._instance == None:
   1.671 +            VMManager._instance = VMManager()
   1.672 +        return VMManager._instance
   1.673 +    
   1.674 +    def cleanup(self):
   1.675 +        if self.rsdHandler != None:
   1.676 +            self.rsdHandler.stop()
   1.677 +            self.rsdHandler.join()
   1.678 +        drives = self.getNetworkDrives()
   1.679 +        for drive in drives.keys():
   1.680 +            self.unmapNetworkDrive(drive)
   1.681 +        for vm in self.listSDVM():
   1.682 +            self.poweroffVM(vm)
   1.683 +            self.removeVM(vm)
   1.684 +        
   1.685 +    # return hosty system properties
   1.686 +    def getSystemProperties(self):
   1.687 +        result = Cygwin.vboxExecute('list systemproperties')
   1.688 +        if result[1]=='':
   1.689 +            return None
   1.690 +        props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result[1].strip().splitlines()))
   1.691 +        #logger.debug(props)
   1.692 +        return props
   1.693 +    
   1.694 +    # return the folder containing the guest VMs     
   1.695 +    def getMachineFolder(self):
   1.696 +        return self.machineFolder
   1.697 +
   1.698 +    # list all existing VMs registered with VBox
   1.699 +    def listVM(self):
   1.700 +        result = Cygwin.vboxExecute('list vms')[1]
   1.701 +        vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   1.702 +        return vms
   1.703 +    
   1.704 +    # list running VMs
   1.705 +    def listRunningVMS(self):
   1.706 +        result = Cygwin.vboxExecute('list runningvms')[1]
   1.707 +        vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   1.708 +        return vms
   1.709 +    
   1.710 +    # list existing SDVMs
   1.711 +    def listSDVM(self):
   1.712 +        vms = self.listVM()
   1.713 +        svdms = []
   1.714 +        for vm in vms:
   1.715 +            if vm.startswith(self.vmRootName) and vm != self.vmRootName:
   1.716 +                svdms.append(vm)
   1.717 +        return svdms
   1.718 +    
   1.719 +    # generate valid (not already existing SDVM name). necessary for creating a new VM
   1.720 +    def generateSDVMName(self):
   1.721 +        vms = self.listVM()
   1.722 +        for i in range(0,999):
   1.723 +            if(not self.vmRootName+str(i) in vms):
   1.724 +                return self.vmRootName+str(i)
   1.725 +        return ''
   1.726 +    
   1.727 +    # return the RSDs connected to the host
   1.728 +    def getConnectedRSDS(self):
   1.729 +        results = Cygwin.vboxExecute('list usbhost')[1]
   1.730 +        results = results.split('Host USB Devices:')[1].strip()
   1.731 +        
   1.732 +        items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   1.733 +        rsds = dict()   
   1.734 +        for item in items:
   1.735 +            props = dict()
   1.736 +            for line in item.splitlines():
   1.737 +                if line != "":         
   1.738 +                    k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   1.739 +                    props[k] = v
   1.740 +            
   1.741 +            if 'Product' in props.keys() and props['Product'] == 'Mass Storage':
   1.742 +                usb_filter = USBFilter( re.search(r"\((?P<vid>[0-9A-Fa-f]+)\)", props['VendorId']).groupdict()['vid'], 
   1.743 +                                        re.search(r"\((?P<pid>[0-9A-Fa-f]+)\)", props['ProductId']).groupdict()['pid'],
   1.744 +                                        re.search(r"\((?P<rev>[0-9A-Fa-f]+)\)", props['Revision']).groupdict()['rev'] )
   1.745 +                rsds[props['UUID']] = usb_filter;
   1.746 +                logger.debug(usb_filter)
   1.747 +        return rsds
   1.748 +    
   1.749 +    # return the RSDs attached to all existing SDVMs
   1.750 +    def getAttachedRSDs(self):
   1.751 +        vms = self.listSDVM()
   1.752 +        attached_devices = dict()
   1.753 +        for vm in vms:
   1.754 +            rsd_filter = self.getUSBFilter(vm)
   1.755 +            if rsd_filter != None:
   1.756 +                attached_devices[vm] = rsd_filter
   1.757 +        return attached_devices
   1.758 +    
   1.759 +    # configures hostonly networking and DHCP server. requires admin rights
   1.760 +    def configureHostNetworking(self):
   1.761 +        #cmd = 'vboxmanage list hostonlyifs'
   1.762 +        #Cygwin.vboxExecute(cmd)
   1.763 +        #cmd = 'vboxmanage hostonlyif remove \"VirtualBox Host-Only Ethernet Adapter\"'
   1.764 +        #Cygwin.vboxExecute(cmd)
   1.765 +        #cmd = 'vboxmanage hostonlyif create'
   1.766 +        #Cygwin.vboxExecute(cmd)
   1.767 +        Cygwin.vboxExecute('hostonlyif ipconfig \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.1 --netmask 255.255.255.0')
   1.768 +        #cmd = 'vboxmanage dhcpserver add'
   1.769 +        #Cygwin.vboxExecute(cmd)
   1.770 +        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')
   1.771 +    
   1.772 +    #create new virtual machine instance based on template vm named SecurityDVM (\SecurityDVM\SecurityDVM.vmdk)
   1.773 +    def createVM(self, vm_name):
   1.774 +        hostonly_if = self.getHostOnlyIFs()
   1.775 +        Cygwin.vboxExecute('createvm --name ' + vm_name + ' --ostype Debian --register')
   1.776 +        Cygwin.vboxExecute('modifyvm ' + vm_name + ' --memory 512 --vram 10 --cpus 1 --usb on --usbehci on --nic1 hostonly --hostonlyadapter1 \"' + hostonly_if['Name'] + '\" --nic2 nat')
   1.777 +        Cygwin.vboxExecute('storagectl ' + vm_name + ' --name SATA --add sata --portcount 2')
   1.778 +        return
   1.779 +    
   1.780 +    # attach storage image to controller
   1.781 +    def storageAttach(self, vm_name):
   1.782 +        if self.isStorageAttached(vm_name):
   1.783 +            self.storageDetach(vm_name)
   1.784 +        Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium \"'+ self.machineFolder + '\SecurityDVM\SecurityDVM.vmdk\"')
   1.785 +        return
   1.786 +    
   1.787 +    # return true if storage is attached 
   1.788 +    def isStorageAttached(self, vm_name):
   1.789 +        info = self.getVMInfo(vm_name)
   1.790 +        return (info['SATA-0-0']!='none')
   1.791 +    
   1.792 +    # detach storage from controller
   1.793 +    def storageDetach(self, vm_name):
   1.794 +        if self.isStorageAttached(vm_name):
   1.795 +            Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium none')
   1.796 +        return
   1.797 +    
   1.798 +    def changeStorageType(self, filename, storage_type):
   1.799 +        Cygwin.vboxExecute('modifyhd \"' + filename + '\" --type ' + storage_type)
   1.800 +        return
   1.801 +    
   1.802 +    # list storage snaphots for VM
   1.803 +    def updateTemplate(self):
   1.804 +        self.cleanup()
   1.805 +        self.poweroffVM('SecurityDVM')
   1.806 +        self.waitShutdown('SecurityDVM')
   1.807 +        
   1.808 +        # check for updates
   1.809 +        self.genCertificateISO('SecurityDVM')
   1.810 +        self.attachCertificateISO('SecurityDVM')
   1.811 +        
   1.812 +        self.storageDetach('SecurityDVM')
   1.813 +        results = Cygwin.vboxExecute('list hdds')[1]
   1.814 +        results = results.replace('Parent UUID', 'Parent')
   1.815 +        items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   1.816 +        
   1.817 +        snaps = dict()   
   1.818 +        for item in items:
   1.819 +            props = dict()
   1.820 +            for line in item.splitlines():
   1.821 +                if line != "":         
   1.822 +                    k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   1.823 +                    props[k] = v;
   1.824 +            snaps[props['UUID']] = props
   1.825 +        
   1.826 +        
   1.827 +        template_storage = self.machineFolder + '\SecurityDVM\SecurityDVM.vmdk'
   1.828 +        
   1.829 +        # find template uuid
   1.830 +        template_uuid = ''
   1.831 +        for hdd in snaps.values():
   1.832 +            if hdd['Location'] == template_storage:
   1.833 +                template_uuid = hdd['UUID']
   1.834 +        logger.debug('found parent uuid ' + template_uuid)
   1.835 +        
   1.836 +        # remove snapshots 
   1.837 +        for hdd in snaps.values():
   1.838 +            if hdd['Parent'] == template_uuid:
   1.839 +                #template_uuid = hdd['UUID']
   1.840 +                logger.debug('removing snapshot ' + hdd['UUID'])
   1.841 +                results = Cygwin.vboxExecute('closemedium disk {' + hdd['UUID'] + '} --delete')[1]
   1.842 +                # parse result 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
   1.843 +        
   1.844 +        self.changeStorageType(template_storage,'normal')
   1.845 +        self.storageAttach('SecurityDVM')
   1.846 +        self.startVM('SecurityDVM')
   1.847 +        self.waitStartup('SecurityDVM')
   1.848 +        Cygwin.sshExecute('"sudo apt-get -y update"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key'  )
   1.849 +        Cygwin.sshExecute('"sudo apt-get -y upgrade"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key'  )
   1.850 +        #self.stopVM('SecurityDVM')
   1.851 +        self.hibernateVM('SecurityDVM')
   1.852 +        self.waitShutdown('SecurityDVM')
   1.853 +        self.storageDetach('SecurityDVM')
   1.854 +        self.changeStorageType(template_storage,'immutable')
   1.855 +        self.storageAttach('SecurityDVM')
   1.856 +        self.rsdHandler = DeviceHandler(self)
   1.857 +        self.rsdHandler.start()
   1.858 +    
   1.859 +    #remove VM from the system. should be used on VMs returned by listSDVMs    
   1.860 +    def removeVM(self, vm_name):
   1.861 +        logger.info('Removing ' + vm_name)
   1.862 +        Cygwin.vboxExecute('unregistervm ' + vm_name + ' --delete')
   1.863 +        machineFolder = Cygwin.cygPath(self.machineFolder)
   1.864 +        Cygwin.bashExecute('"/usr/bin/rm -rf ' + machineFolder + '/' + vm_name + '"')
   1.865 +    
   1.866 +    # start VM
   1.867 +    def startVM(self, vm_name):
   1.868 +        logger.info('Starting ' +  vm_name)
   1.869 +        result = Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless' )
   1.870 +        while not string.find(str(result), 'successfully started',):
   1.871 +            logger.error("Failed to start SDVM: " + vm_name + " retrying")
   1.872 +            time.sleep(1)
   1.873 +            result = Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless')
   1.874 +        return result[0]
   1.875 +    
   1.876 +    # return wether VM is running or not
   1.877 +    def isVMRunning(self, vm_name):
   1.878 +        return vm_name in self.listRunningVMS()    
   1.879 +    
   1.880 +    # stop VM
   1.881 +    def stopVM(self, vm_name):
   1.882 +        logger.info('Sending shutdown signal to ' + vm_name)
   1.883 +        Cygwin.sshExecute( '"sudo shutdown -h now"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key' )
   1.884 +    
   1.885 +    # stop VM
   1.886 +    def hibernateVM(self, vm_name):
   1.887 +        logger.info('Sending shutdown signal to ' + vm_name)
   1.888 +        Cygwin.sshExecute( '"sudo hibernate-disk&"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key', wait_return=False )
   1.889 +            
   1.890 +    # poweroff VM
   1.891 +    def poweroffVM(self, vm_name):
   1.892 +        if not self.isVMRunning(vm_name):
   1.893 +            return
   1.894 +        logger.info('Powering off ' + vm_name)
   1.895 +        return Cygwin.vboxExecute('controlvm ' + vm_name + ' poweroff')
   1.896 +    
   1.897 +    #list the hostonly IFs exposed by the VBox host
   1.898 +    @staticmethod    
   1.899 +    def getHostOnlyIFs():
   1.900 +        result = Cygwin.vboxExecute('list hostonlyifs')[1]
   1.901 +        if result=='':
   1.902 +            return None
   1.903 +        props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result.strip().splitlines()))
   1.904 +        return props
   1.905 +    
   1.906 +    # return the hostOnly IP for a running guest or the host
   1.907 +    @staticmethod    
   1.908 +    def getHostOnlyIP(vm_name):
   1.909 +        if vm_name == None:
   1.910 +            logger.info('Gettting hostOnly IP address for Host')
   1.911 +            return VMManager.getHostOnlyIFs()['IPAddress']
   1.912 +        else:
   1.913 +            logger.info('Gettting hostOnly IP address ' + vm_name)
   1.914 +            result = Cygwin.vboxExecute('guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP')
   1.915 +            if result=='':
   1.916 +                return None
   1.917 +            result = result[1]
   1.918 +            if result.startswith('No value set!'):
   1.919 +                return None
   1.920 +            return result[result.index(':')+1:].strip()
   1.921 +            
   1.922 +    # attach removable storage device to VM by provision of filter
   1.923 +    def attachRSD(self, vm_name, rsd_filter):
   1.924 +        return Cygwin.vboxExecute('usbfilter add 0 --target ' + vm_name + ' --name OpenSecurityRSD --vendorid ' + rsd_filter.vendorid + ' --productid ' + rsd_filter.productid + ' --revision ' + rsd_filter.revision)
   1.925 +    
   1.926 +    # detach removable storage from VM by 
   1.927 +    def detachRSD(self, vm_name):
   1.928 +        return Cygwin.vboxExecute('usbfilter remove 0 --target ' + vm_name )
   1.929 +        
   1.930 +    
   1.931 +    # return the description set for an existing VM
   1.932 +    def getVMInfo(self, vm_name):
   1.933 +        results = Cygwin.vboxExecute('showvminfo ' + vm_name + ' --machinereadable')[1]
   1.934 +        props = dict((k.strip().strip('"'),v.strip().strip('"')) for k,v in (line.split('=', 1) for line in results.splitlines()))
   1.935 +        #logger.debug(props)
   1.936 +        return props
   1.937 +    
   1.938 +    # return the configured USB filter for an existing VM 
   1.939 +    def getUSBFilter(self, vm_name):
   1.940 +        props = self.getVMInfo(vm_name)
   1.941 +        keys = set(['USBFilterVendorId1', 'USBFilterProductId1', 'USBFilterRevision1'])
   1.942 +        keyset = set(props.keys())
   1.943 +        usb_filter = None
   1.944 +        if keyset.issuperset(keys):
   1.945 +            usb_filter = USBFilter(props['USBFilterVendorId1'], props['USBFilterProductId1'], props['USBFilterRevision1'])
   1.946 +        return usb_filter
   1.947 +    
   1.948 +    #generates ISO containing authorized_keys for use with guest VM
   1.949 +    def genCertificateISO(self, vm_name):
   1.950 +        machineFolder = Cygwin.cygPath(self.machineFolder)
   1.951 +        # remove .ssh folder if exists
   1.952 +        cmd = '\"/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"'
   1.953 +        Cygwin.bashExecute(cmd)
   1.954 +        # remove .ssh folder if exists
   1.955 +        Cygwin.bashExecute('\"/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"\"')
   1.956 +        # create .ssh folder in vm_name
   1.957 +        Cygwin.bashExecute('\"/usr/bin/mkdir -p \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"')
   1.958 +        # generate dvm_key pair in vm_name / .ssh     
   1.959 +        Cygwin.bashExecute('\"/usr/bin/ssh-keygen -q -t rsa -N \\"\\" -C \\\"' + vm_name + '\\\" -f \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\"\"')
   1.960 +        # move out private key
   1.961 +        Cygwin.bashExecute('\"/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\" \\\"' + machineFolder + '/' + vm_name + '\\\"')
   1.962 +        # set permissions for private key
   1.963 +        Cygwin.bashExecute('\"/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"\"')
   1.964 +        # rename public key to authorized_keys
   1.965 +        Cygwin.bashExecute('\"/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key.pub\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"')
   1.966 +        # set permissions for authorized_keys
   1.967 +        Cygwin.bashExecute('\"/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"\"')
   1.968 +        # generate iso image with .ssh/authorized keys
   1.969 +        Cygwin.bashExecute('\"/usr/bin/genisoimage -J -R -o \\\"' + machineFolder + '/' + vm_name + '/'+ vm_name + '.iso\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"')
   1.970 +    
   1.971 +    # attaches generated ssh public cert to guest vm
   1.972 +    def attachCertificateISO(self, vm_name):
   1.973 +        result = Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 1 --device 0 --type dvddrive --mtype readonly --medium \"' + self.machineFolder + '\\' + vm_name + '\\'+ vm_name + '.iso\"')
   1.974 +        return result
   1.975 +    
   1.976 +    # wait for machine to come up
   1.977 +    def waitStartup(self, vm_name, timeout_ms = 30000):
   1.978 +        result = Cygwin.vboxExecute('guestproperty wait ' + vm_name + ' SDVMStarted --timeout ' + str(timeout_ms) + ' --fail-on-timeout')
   1.979 +        return VMManager.getHostOnlyIP(vm_name)
   1.980 +    
   1.981 +    # wait for machine to shutdown
   1.982 +    def waitShutdown(self, vm_name):
   1.983 +        while vm_name in self.listRunningVMS():
   1.984 +            time.sleep(1)
   1.985 +        return
   1.986 +        
   1.987 +    # handles browsing request    
   1.988 +    def handleBrowsingRequest(self):
   1.989 +        if VMManager.handleDeviceChangeLock.acquire(True):
   1.990 +            new_sdvm = self.generateSDVMName()
   1.991 +            self.createVM(new_sdvm)
   1.992 +            self.storageAttach(new_sdvm)
   1.993 +            self.genCertificateISO(new_sdvm)
   1.994 +            self.attachCertificateISO(new_sdvm)
   1.995 +            self.startVM(new_sdvm)
   1.996 +            new_ip = self.waitStartup(new_sdvm)
   1.997 +            if new_ip != None:
   1.998 +                self.mapNetworkDrive('g:', '\\\\' + new_ip + '\\Download', None, None)
   1.999 +            #TODO: cleanup notifications somwhere else (eg. machine shutdown)
  1.1000 +            VMManager.handleDeviceChangeLock.release()
  1.1001 +        return new_sdvm
  1.1002 +    
  1.1003 +    #Small function to check the availability of network resource.
  1.1004 +    #def isAvailable(self, path):
  1.1005 +        #return os.path.exists(path)
  1.1006 +        #result = Cygwin.cmdExecute('IF EXIST "' + path + '" echo YES')
  1.1007 +        #return string.find(result[1], 'YES',)
  1.1008 +    
  1.1009 +    #Small function to check if the mention location is a directory
  1.1010 +    def isDirectory(self, path):
  1.1011 +        result = Cygwin.cmdExecute('dir ' + path + ' | FIND ".."')
  1.1012 +        return string.find(result[1], 'DIR',)
  1.1013 +
  1.1014 +    def mapNetworkDrive(self, drive, networkPath, user, password):
  1.1015 +        self.unmapNetworkDrive(drive)
  1.1016 +        #Check for drive availability
  1.1017 +        if os.path.exists(drive):
  1.1018 +            logger.error("Drive letter is already in use: " + drive)
  1.1019 +            return -1
  1.1020 +        #Check for network resource availability
  1.1021 +        while not os.path.exists(networkPath):
  1.1022 +            time.sleep(1)
  1.1023 +            logger.info("Path not accessible: " + networkPath + " retrying")
  1.1024 +            #return -1
  1.1025 +    
  1.1026 +        command = 'USE ' + drive + ' ' + networkPath + ' /PERSISTENT:NO'
  1.1027 +        if user != None:
  1.1028 +            command += ' ' + password + ' /User' + user
  1.1029 +    
  1.1030 +        #TODO: Execute 'NET USE' command with authentication
  1.1031 +        result = Cygwin.execute('C:\\Windows\\system32\\net.exe', command)
  1.1032 +        if string.find(result[1], 'successfully',) == -1:
  1.1033 +            logger.error("Failed: NET " + command)
  1.1034 +            return -1
  1.1035 +        return 1
  1.1036 +    
  1.1037 +    def unmapNetworkDrive(self, drive):
  1.1038 +        drives = self.getNetworkDrives()
  1.1039 +        if drive not in drives.keys():
  1.1040 +            return 1 
  1.1041 +        result = Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE ' + drive + ' /DELETE /YES')
  1.1042 +        if string.find(str(result[1]), 'successfully',) == -1:
  1.1043 +            logger.error(result[2])
  1.1044 +            return -1
  1.1045 +        return 1
  1.1046 +    
  1.1047 +    def getNetworkDrives(self):
  1.1048 +        ip = VMManager.getHostOnlyIP(None)
  1.1049 +        ip = ip[:ip.rindex('.')]
  1.1050 +        drives = dict()    
  1.1051 +        result = Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE')
  1.1052 +        for line in result[1].splitlines():
  1.1053 +            if ip in line:
  1.1054 +                parts = line.split()
  1.1055 +                drives[parts[1]] = parts[2]
  1.1056 +        return drives
  1.1057 +            
  1.1058 +    def genNetworkDrive(self):
  1.1059 +        network_drives = self.getNetworkDrives()
  1.1060 +        logical_drives = self.getLogicalDrives()
  1.1061 +        drives = list(map(chr, range(68, 91)))  
  1.1062 +        for drive in drives:
  1.1063 +            if drive+':' not in network_drives and drive not in logical_drives:
  1.1064 +                return drive+':'
  1.1065 +
  1.1066 +    def getNetworkDrive(self, vm_name):
  1.1067 +        ip = self.getHostOnlyIP(vm_name)
  1.1068 +        result = Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE')
  1.1069 +        for line in result[1].splitlines():
  1.1070 +            if ip in line:
  1.1071 +                parts = line.split()
  1.1072 +                return parts[0]
  1.1073 +    
  1.1074 +    def getLogicalDrives(self):
  1.1075 +        drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
  1.1076 +        return list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
  1.1077 +                    
  1.1078 +        #vms = self.listSDVM()
  1.1079 +        #for vm in vms:
  1.1080 +        #    ip = self.getHostOnlyIP(vm)
  1.1081 +        
  1.1082 +class DeviceHandler(threading.Thread): 
  1.1083 +    vmm = None
  1.1084 +    #handleDeviceChangeLock = threading.Lock()
  1.1085 +    attachedRSDs = None  
  1.1086 +    connectedRSDs = None
  1.1087 +    running = True
  1.1088 +    def __init__(self, vmmanger): 
  1.1089 +        threading.Thread.__init__(self)
  1.1090 +        self.vmm = vmmanger
  1.1091 + 
  1.1092 +    def stop(self):
  1.1093 +        self.running = False
  1.1094 +        
  1.1095 +    def run(self):
  1.1096 +        self.connectedRSDs = dict()#self.vmm.getConnectedRSDS()
  1.1097 +        self.attachedRSDs = self.vmm.getAttachedRSDs()
  1.1098 +        while self.running:
  1.1099 +            tmp_rsds = self.vmm.getConnectedRSDS()
  1.1100 +            if tmp_rsds.keys() == self.connectedRSDs.keys():
  1.1101 +                logger.debug("Nothing's changed. sleep(3)")
  1.1102 +                time.sleep(3)
  1.1103 +                continue
  1.1104 +            
  1.1105 +            logger.info("Something's changed")          
  1.1106 +            
  1.1107 +            self.connectedRSDs = tmp_rsds
  1.1108 +            self.attachedRSDs = self.vmm.getAttachedRSDs()
  1.1109 +            
  1.1110 +            
  1.1111 +            for vm_name in self.attachedRSDs.keys():
  1.1112 +                if self.attachedRSDs[vm_name] not in self.connectedRSDs.values():
  1.1113 +                    drive = self.vmm.getNetworkDrive(vm_name)
  1.1114 +                    self.vmm.unmapNetworkDrive(drive)
  1.1115 +                    #self.stopVM(vm_name)
  1.1116 +                    self.vmm.detachRSD(vm_name)
  1.1117 +                    self.vmm.poweroffVM(vm_name)
  1.1118 +                    self.vmm.removeVM(vm_name)
  1.1119 +            #create new vm for attached device if any
  1.1120 +            self.attachedRSDs = self.vmm.getAttachedRSDs()
  1.1121 +            self.connectedRSDs = self.vmm.getConnectedRSDS()
  1.1122 +            
  1.1123 +            new_ip = None
  1.1124 +            for connected_device in self.connectedRSDs.values():
  1.1125 +                if (self.attachedRSDs and False) or (connected_device not in self.attachedRSDs.values()):
  1.1126 +                    new_sdvm = self.vmm.generateSDVMName()
  1.1127 +                    self.vmm.createVM(new_sdvm)
  1.1128 +                    self.vmm.storageAttach(new_sdvm)
  1.1129 +                    self.vmm.attachRSD(new_sdvm, connected_device)
  1.1130 +                    self.vmm.startVM(new_sdvm)
  1.1131 +                    new_ip = self.vmm.waitStartup(new_sdvm)
  1.1132 +                    drive = self.vmm.genNetworkDrive()
  1.1133 +                    if new_ip != None:
  1.1134 +                        self.vmm.mapNetworkDrive(drive, '\\\\' + new_ip + '\\USB', None, None)
  1.1135 +                    #TODO: cleanup notifications somwhere else (eg. machine shutdown)
  1.1136 +            #self.handleDeviceChangeLock.release()
  1.1137 +                
  1.1138 +
  1.1139 +if __name__ == '__main__':
  1.1140 +    #man = VMManager.getInstance()
  1.1141 +    #man.listVM()
  1.1142 +    #print man.getConnectedRSDs()
  1.1143 +    #print man.getNetworkDrives()
  1.1144 +    #man.genNetworkDrive()
  1.1145 +    drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
  1.1146 +    print list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
  1.1147 +    #print list(map(chr, range(68, 91))) 
  1.1148 +    
  1.1149 +    #time.sleep(-1)
  1.1150 +    #man.listVM()
  1.1151 +    #man.listVM()
  1.1152 +    #man.listVM()
  1.1153 +    #man.listVM()
  1.1154 +    #man.genCertificateISO('SecurityDVM0')
  1.1155 +    #man.guestExecute('SecurityDVM0', '/bin/ls -la')
  1.1156 +    #logger = setupLogger('VMManager')
  1.1157 +    #c = Cygwin()
  1.1158 +    
  1.1159 +    #man.sshExecute('/bin/ls -la', 'SecurityDVM0')
  1.1160 +    #man.sshExecuteX11('/usr/bin/iceweasel', 'SecurityDVM0')
  1.1161 +    #man.removeVM('SecurityDVM0')
  1.1162 +    #man.netUse('192.168.56.134', 'USB\\')
  1.1163 +    #ip = '192.168.56.139'
  1.1164 +    
  1.1165 +    #man.cygwin_path = 'c:\\cygwin64\\bin\\'
  1.1166 +    #man.handleDeviceChange()
  1.1167 +    #print man.listSDVM()
  1.1168 +    #man.configureHostNetworking()
  1.1169 +    #new_vm = man.generateSDVMName()
  1.1170 +    #man.createVM(new_vm)
  1.1171 +    
  1.1172 +    #print Cygwin.cmd()
  1.1173 +    #man.isAvailable('c:')
  1.1174 +    #ip = man.getHostOnlyIP('SecurityDVM0')
  1.1175 +    #man.mapNetworkDrive('h:', '\\\\' + ip + '\Download', None, None)
  1.1176 +    
  1.1177 +    #man.genCertificateISO(new_vm)
  1.1178 +    #man.attachCertificateISO(new_vm)
  1.1179 +    
  1.1180 +    #man.attachCertificateISO(vm_name)
  1.1181 +    #man.guestExecute(vm_name, "ls")
  1.1182 +    #man.sshGuestX11Execute('SecurityDVM1', '/usr/bin/iceweasel')
  1.1183 +    #time.sleep(60)
  1.1184 +    #print man.cygwinPath("C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\.ssh\*")
  1.1185 +    #man.genCertificateISO('SecurityDVM')
  1.1186 +    #man.attachCertificateISO('SecurityDVM')
  1.1187 +    #man.isStorageAttached('SecurityDVM')
  1.1188 +    #man.guestExecute('SecurityDVM', 'sudo apt-get -y update')
  1.1189 +    #man.guestExecute('SecurityDVM', 'sudo apt-get -y upgrade' )
  1.1190 +    
  1.1191 +    #man.stopVM('SecurityDVM')
  1.1192 +    #man.storageDetach('SecurityDVM')
  1.1193 +    #man.changeStorageType('C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\SecurityDVM.vmdk','immutable')
  1.1194 +    #man.storageAttach('SecurityDVM')
  1.1195 +    
  1.1196 +    
  1.1197 +    #cmd = "c:\\cygwin64\\bin\\bash.exe --login -c \"/bin/ls\""
  1.1198 +    #man.execute(cmd)
  1.1199 +