OpenSecurity/bin/vmmanager.pyw
author Oliver Maurhart <oliver.maurhart@ait.ac.at>
Thu, 22 May 2014 11:00:33 +0200
changeset 167 1e1811fa44bc
parent 166 6718e19352e6
child 169 a133c8d03ef8
permissions -rwxr-xr-x
eXtreme Programming session - changes by Mihai
     1 '''
     2 Created on Nov 19, 2013
     3 
     4 @author: BarthaM
     5 '''
     6 import os
     7 import os.path
     8 from subprocess import Popen, PIPE, call, STARTUPINFO, _subprocess
     9 import sys
    10 import re
    11 
    12 from cygwin import Cygwin
    13 from environment import Environment
    14 import threading
    15 import time
    16 import string
    17 
    18 import shutil
    19 import stat
    20 import tempfile
    21 from opensecurity_util import logger, setupLogger, OpenSecurityException
    22 import ctypes
    23 import itertools
    24 import win32api
    25 import win32con
    26 import win32security
    27 import urllib
    28 import urllib2
    29 DEBUG = True
    30 
    31 
    32 new_sdvm_lock = threading.Lock()
    33 
    34 class VMManagerException(Exception):
    35     def __init__(self, value):
    36         self.value = value
    37     def __str__(self):
    38         return repr(self.value)
    39 
    40 class USBFilter:
    41     vendorid = ""
    42     productid = ""
    43     revision = ""
    44     
    45     def __init__(self, vendorid, productid, revision):
    46         self.vendorid = vendorid.lower()
    47         self.productid = productid.lower()
    48         self.revision = revision.lower()
    49         return
    50     
    51     def __eq__(self, other):
    52         return self.vendorid == other.vendorid and self.productid == other.productid and self.revision == other.revision
    53     
    54     def __hash__(self):
    55         return hash(self.vendorid) ^ hash(self.productid) ^ hash(self.revision)
    56     
    57     def __repr__(self):
    58         return "VendorId = \'" + str(self.vendorid) + "\' ProductId = \'" + str(self.productid) + "\' Revision = \'" + str(self.revision) + "\'"
    59     
    60     #def __getitem__(self, item):
    61     #    return self.coords[item]
    62  
    63 class VMManager(object):
    64     vmRootName = "SecurityDVM"
    65     systemProperties = None
    66     _instance = None
    67     machineFolder = ''
    68     rsdHandler = None
    69     browsingManager = None
    70     status_message = 'Starting up...'
    71 
    72  
    73     def __init__(self):
    74 
    75         self.systemProperties = self.getSystemProperties()
    76         self.machineFolder = self.systemProperties["Default machine folder"]
    77 
    78         # only proceed if we have a working background environment
    79         if self.backend_ok():
    80             self.cleanup()
    81             self.browsingManager = BrowsingManager(self)
    82             self.browsingManager.start()
    83             self.rsdHandler = DeviceHandler(self)
    84             self.rsdHandler.start()
    85         else:
    86             logger.critical(self.status_message)
    87     
    88 
    89     @staticmethod
    90     def getInstance():
    91         if VMManager._instance == None:
    92             VMManager._instance = VMManager()
    93         return VMManager._instance
    94     
    95 
    96     def backend_ok(self):
    97 
    98         """check if the backend (VirtualBox) is sufficient for our task"""
    99 
   100         # ensure we have our system props
   101         if self.systemProperties == None:
   102             self.systemProperties = self.getSystemProperties()
   103         if self.systemProperties == None:
   104             self.status_message = 'Failed to get backend system properties. Is Backend (VirtualBox?) installed?'
   105             return False
   106 
   107         # check for existing Extension pack
   108         if not 'Remote desktop ExtPack' in self.systemProperties:
   109             self.status_message = 'No remote desktop extension pack found. Please install the "Oracle VM VirtualBox Extension Pack" from https://www.virtualbox.org/wiki/Downloads.'
   110             return False
   111         if self.systemProperties['Remote desktop ExtPack'] == 'Oracle VM VirtualBox Extension Pack ':
   112             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.'
   113             return False
   114 
   115         # check if we do have our root VMs installed
   116         vms = self.listVM()
   117         if not self.vmRootName in vms:
   118             self.status_message = 'Unable to locate root SecurityDVM. Please download and setup the initial image.'
   119             return False
   120 
   121         # basically all seems nice and ready to rumble
   122         self.status_message = 'All is ok.'
   123 
   124         return True
   125 
   126 
   127     def cleanup(self):
   128         if self.rsdHandler != None:
   129             self.rsdHandler.stop()
   130             self.rsdHandler.join()
   131         drives = self.getNetworkDrives()
   132         for drive in drives.keys():
   133             try:
   134                 result = urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'drive_letter='+drive).readline()
   135             except urllib2.URLError:
   136                 logger.error("Network drive disconnect failed. OpenSecurity Tray client not running.")
   137             
   138         for vm in self.listSDVM():
   139             self.poweroffVM(vm)
   140             self.removeVM(vm)
   141         
   142     # return hosty system properties
   143     def getSystemProperties(self):
   144         result = Cygwin.checkResult(Cygwin.vboxExecute('list systemproperties'))
   145         if result[1]=='':
   146             return None
   147         props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result[1].strip().splitlines()))
   148         return props
   149     
   150     # return the folder containing the guest VMs     
   151     def getMachineFolder(self):
   152         return self.machineFolder
   153 
   154     # list all existing VMs registered with VBox
   155     def listVM(self):
   156         result = Cygwin.checkResult(Cygwin.vboxExecute('list vms'))[1]
   157         vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   158         return vms
   159     
   160     # list running VMs
   161     def listRunningVMS(self):
   162         result = Cygwin.checkResult(Cygwin.vboxExecute('list runningvms'))[1]
   163         vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   164         return vms
   165     
   166     # list existing SDVMs
   167     def listSDVM(self):
   168         vms = self.listVM()
   169         svdms = []
   170         for vm in vms:
   171             if vm.startswith(self.vmRootName) and vm != self.vmRootName:
   172                 svdms.append(vm)
   173         return svdms
   174     
   175     # generate valid (not already existing SDVM name). necessary for creating a new VM
   176     def genSDVMName(self):
   177         vms = self.listVM()
   178         for i in range(0,999):
   179             if(not self.vmRootName+str(i) in vms):
   180                 return self.vmRootName+str(i)
   181         return ''
   182     
   183     # check if the device is mass storage type
   184     @staticmethod
   185     def isMassStorageDevice(device):
   186         #TODO: implement filtering for card readers (this is olivers) 
   187         #      alternatively implement handling of multiple drives on same USB client
   188         if device.vendorid == '058f' and  device.productid=='6362':
   189             return False
   190         keyname = 'SYSTEM\CurrentControlSet\Enum\USB' + '\VID_' + device.vendorid+'&'+'PID_'+ device.productid
   191         key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, keyname)
   192         #subkeys = _winreg.QueryInfoKey(key)[0]
   193         #for i in range(0, subkeys):
   194         #    print _winreg.EnumKey(key, i)     
   195         devinfokeyname = win32api.RegEnumKey(key, 0)
   196         win32api.RegCloseKey(key)
   197 
   198         devinfokey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, keyname+'\\'+devinfokeyname)
   199         value = win32api.RegQueryValueEx(devinfokey, 'SERVICE')[0]
   200         win32api.RegCloseKey(devinfokey)
   201         
   202         return 'USBSTOR' in value
   203     
   204     # return the RSDs connected to the host
   205     @staticmethod
   206     def getConnectedRSDS():
   207         results = Cygwin.checkResult(Cygwin.vboxExecute('list usbhost'))[1]
   208         results = results.split('Host USB Devices:')[1].strip()
   209         
   210         items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   211         rsds = dict()   
   212         for item in items:
   213             props = dict()
   214             for line in item.splitlines():
   215                 if line != "":         
   216                     k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   217                     props[k] = v
   218             
   219             #if 'Product' in props.keys() and props['Product'] == 'Mass Storage':
   220             
   221             usb_filter = USBFilter( re.search(r"\((?P<vid>[0-9A-Fa-f]+)\)", props['VendorId']).groupdict()['vid'], 
   222                                     re.search(r"\((?P<pid>[0-9A-Fa-f]+)\)", props['ProductId']).groupdict()['pid'],
   223                                     re.search(r"\((?P<rev>[0-9A-Fa-f]+)\)", props['Revision']).groupdict()['rev'] )
   224             if VMManager.isMassStorageDevice(usb_filter):
   225                 rsds[props['UUID']] = usb_filter;
   226                 logger.debug(usb_filter)
   227         return rsds
   228     
   229     # return the RSDs attached to all existing SDVMs
   230     def getAttachedRSDs(self):
   231         vms = self.listSDVM()
   232         attached_devices = dict()
   233         for vm in vms:
   234             rsd_filter = self.getUSBFilter(vm)
   235             if rsd_filter != None:
   236                 attached_devices[vm] = rsd_filter
   237         return attached_devices
   238     
   239     # configures hostonly networking and DHCP server. requires admin rights
   240     def configureHostNetworking(self):
   241         #cmd = 'vboxmanage list hostonlyifs'
   242         #Cygwin.vboxExecute(cmd)
   243         #cmd = 'vboxmanage hostonlyif remove \"VirtualBox Host-Only Ethernet Adapter\"'
   244         #Cygwin.vboxExecute(cmd)
   245         #cmd = 'vboxmanage hostonlyif create'
   246         #Cygwin.vboxExecute(cmd)
   247         Cygwin.checkResult(Cygwin.vboxExecute('hostonlyif ipconfig \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.1 --netmask 255.255.255.0'))
   248         #cmd = 'vboxmanage dhcpserver add'
   249         #Cygwin.vboxExecute(cmd)
   250         Cygwin.checkResult(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'))
   251     
   252     def isSDVMExisting(self, vm_name):
   253         sdvms = self.listSDVM()
   254         return vm_name in sdvms
   255         
   256     #create new virtual machine instance based on template vm named SecurityDVM (\SecurityDVM\SecurityDVM.vmdk)
   257     def createVM(self, vm_name):
   258         if self.isSDVMExisting(vm_name):
   259             return
   260         #remove eventually existing SDVM folder
   261         machineFolder = Cygwin.cygPath(self.machineFolder)
   262         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '\\\"'))
   263         hostonly_if = self.getHostOnlyIFs()
   264         Cygwin.checkResult(Cygwin.vboxExecute('createvm --name ' + vm_name + ' --ostype Debian --register'))
   265         Cygwin.checkResult(Cygwin.vboxExecute('modifyvm ' + vm_name + ' --memory 512 --vram 10 --cpus 1 --usb on --usbehci on --nic1 hostonly --hostonlyadapter1 \"' + hostonly_if['Name'] + '\" --nic2 nat'))
   266         Cygwin.checkResult(Cygwin.vboxExecute('storagectl ' + vm_name + ' --name SATA --add sata --portcount 2'))
   267 
   268     #create new SecurityDVM with automatically generated name from template (thread safe)        
   269     def newSDVM(self):
   270         with new_sdvm_lock:
   271             vm_name = self.genSDVMName()
   272             self.createVM(vm_name)
   273         return vm_name
   274     
   275     # attach storage image to controller
   276     def storageAttach(self, vm_name):
   277         if self.isStorageAttached(vm_name):
   278             self.storageDetach(vm_name)
   279         Cygwin.checkResult(Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium \"'+ self.machineFolder + '\SecurityDVM\SecurityDVM.vmdk\"'))
   280     
   281     # return true if storage is attached 
   282     def isStorageAttached(self, vm_name):
   283         info = self.getVMInfo(vm_name)
   284         return (info['SATA-0-0']!='none')
   285     
   286     # detach storage from controller
   287     def storageDetach(self, vm_name):
   288         if self.isStorageAttached(vm_name):
   289             Cygwin.checkResult(Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium none'))
   290     
   291     def changeStorageType(self, filename, storage_type):
   292         Cygwin.checkResult(Cygwin.vboxExecute('modifyhd \"' + filename + '\" --type ' + storage_type))
   293     
   294     # list storage snaphots for VM
   295     def updateTemplate(self):
   296 
   297         self.cleanup()
   298         self.poweroffVM('SecurityDVM')
   299         self.waitShutdown('SecurityDVM')
   300         
   301         # check for updates
   302         self.genCertificateISO('SecurityDVM')
   303         self.attachCertificateISO('SecurityDVM')
   304         
   305         self.storageDetach('SecurityDVM')
   306         results = Cygwin.checkResult(Cygwin.vboxExecute('list hdds'))[1]
   307         results = results.replace('Parent UUID', 'Parent')
   308         items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   309         
   310         snaps = dict()   
   311         for item in items:
   312             props = dict()
   313             for line in item.splitlines():
   314                 if line != "":         
   315                     k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   316                     props[k] = v;
   317             snaps[props['UUID']] = props
   318         
   319         
   320         template_storage = self.machineFolder + '\SecurityDVM\SecurityDVM.vmdk'
   321         
   322         # find template uuid
   323         template_uuid = ''
   324         for hdd in snaps.values():
   325             if hdd['Location'] == template_storage:
   326                 template_uuid = hdd['UUID']
   327         logger.debug('found parent uuid ' + template_uuid)
   328         
   329         # remove snapshots 
   330         for hdd in snaps.values():
   331             if hdd['Parent'] == template_uuid:
   332                 #template_uuid = hdd['UUID']
   333                 logger.debug('removing snapshot ' + hdd['UUID'])
   334                 Cygwin.checkResult(Cygwin.vboxExecute('closemedium disk {' + hdd['UUID'] + '} --delete'))#[1]
   335                 # parse result 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
   336         
   337         self.changeStorageType(template_storage,'normal')
   338         self.storageAttach('SecurityDVM')
   339         self.startVM('SecurityDVM')
   340         self.waitStartup('SecurityDVM')
   341         Cygwin.checkResult(Cygwin.sshExecute('"sudo apt-get -y update"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key'))
   342         Cygwin.checkResult(Cygwin.sshExecute('"sudo apt-get -y upgrade"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key'))
   343         #self.stopVM('SecurityDVM')
   344         self.hibernateVM('SecurityDVM')
   345         self.waitShutdown('SecurityDVM')
   346         self.storageDetach('SecurityDVM')
   347         self.changeStorageType(template_storage,'immutable')
   348         self.storageAttach('SecurityDVM')
   349         self.rsdHandler = DeviceHandler(self)
   350         self.rsdHandler.start()
   351     
   352     #remove VM from the system. should be used on VMs returned by listSDVMs    
   353     def removeVM(self, vm_name):
   354         logger.info('Removing ' + vm_name)
   355         Cygwin.checkResult(Cygwin.vboxExecute('unregistervm ' + vm_name + ' --delete'))
   356         machineFolder = Cygwin.cygPath(self.machineFolder)
   357         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '\\\"'))
   358     
   359     # start VM
   360     def startVM(self, vm_name):
   361         logger.info('Starting ' +  vm_name)
   362         result = Cygwin.checkResult(Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless' ))
   363         while 'successfully started' not in result[1]:
   364             logger.error("Failed to start SDVM: " + vm_name + " retrying")
   365             logger.error("Command returned:\n" + result[2])
   366             time.sleep(1)
   367             result = Cygwin.checkResult(Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless'))
   368         return result[0]
   369     
   370     # return wether VM is running or not
   371     def isVMRunning(self, vm_name):
   372         return vm_name in self.listRunningVMS()    
   373     
   374     # stop VM
   375     def stopVM(self, vm_name):
   376         logger.info('Sending shutdown signal to ' + vm_name)
   377         Cygwin.checkResult(Cygwin.sshExecute( '"sudo shutdown -h now"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key' ))
   378     
   379     # stop VM
   380     def hibernateVM(self, vm_name):
   381         logger.info('Sending hibernate-disk signal to ' + vm_name)
   382         Cygwin.checkResult(Cygwin.sshBackgroundExecute( '"sudo hibernate-disk"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key', wait_return=False))
   383             
   384     # poweroff VM
   385     def poweroffVM(self, vm_name):
   386         if not self.isVMRunning(vm_name):
   387             return
   388         logger.info('Powering off ' + vm_name)
   389         return Cygwin.checkResult(Cygwin.vboxExecute('controlvm ' + vm_name + ' poweroff'))
   390     
   391     #list the hostonly IFs exposed by the VBox host
   392     @staticmethod    
   393     def getHostOnlyIFs():
   394         result = Cygwin.vboxExecute('list hostonlyifs')[1]
   395         if result=='':
   396             return None
   397         props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result.strip().splitlines()))
   398         return props
   399     
   400     # return the hostOnly IP for a running guest or the host
   401     @staticmethod    
   402     def getHostOnlyIP(vm_name):
   403         if vm_name == None:
   404             logger.info('Getting hostOnly IP address for Host')
   405             return VMManager.getHostOnlyIFs()['IPAddress']
   406         else:
   407             logger.info('Getting hostOnly IP address ' + vm_name)
   408             result = Cygwin.checkResult(Cygwin.vboxExecute('guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP'))
   409             if result=='':
   410                 return None
   411             result = result[1]
   412             if result.startswith('No value set!'):
   413                 return None
   414             return result[result.index(':')+1:].strip()
   415             
   416     # attach removable storage device to VM by provision of filter
   417     def attachRSD(self, vm_name, rsd_filter):
   418         return Cygwin.checkResult(Cygwin.vboxExecute('usbfilter add 0 --target ' + vm_name + ' --name OpenSecurityRSD --vendorid ' + rsd_filter.vendorid + ' --productid ' + rsd_filter.productid + ' --revision ' + rsd_filter.revision))
   419     
   420     # detach removable storage from VM by 
   421     def detachRSD(self, vm_name):
   422         return Cygwin.checkResult(Cygwin.vboxExecute('usbfilter remove 0 --target ' + vm_name))
   423         
   424     # return the description set for an existing VM
   425     def getVMInfo(self, vm_name):
   426         results = Cygwin.checkResult(Cygwin.vboxExecute('showvminfo ' + vm_name + ' --machinereadable'))[1]
   427         props = dict((k.strip().strip('"'),v.strip().strip('"')) for k,v in (line.split('=', 1) for line in results.splitlines()))
   428         return props
   429     
   430     # return the configured USB filter for an existing VM 
   431     def getUSBFilter(self, vm_name):
   432         props = self.getVMInfo(vm_name)
   433         keys = set(['USBFilterVendorId1', 'USBFilterProductId1', 'USBFilterRevision1'])
   434         keyset = set(props.keys())
   435         usb_filter = None
   436         if keyset.issuperset(keys):
   437             usb_filter = USBFilter(props['USBFilterVendorId1'], props['USBFilterProductId1'], props['USBFilterRevision1'])
   438         return usb_filter
   439     
   440     #generates ISO containing authorized_keys for use with guest VM
   441     def genCertificateISO(self, vm_name):
   442         machineFolder = Cygwin.cygPath(self.machineFolder)
   443         # remove .ssh folder if exists
   444         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"'))
   445         # remove .ssh folder if exists
   446         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"'))
   447         # create .ssh folder in vm_name
   448         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"'))
   449         # generate dvm_key pair in vm_name / .ssh     
   450         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/ssh-keygen -q -t rsa -N \\\"\\\" -C \\\"' + vm_name + '\\\" -f \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\"'))
   451         # move out private key
   452         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\" \\\"' + machineFolder + '/' + vm_name + '\\\"'))
   453         # set permissions for private key
   454         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"'))
   455         # rename public key to authorized_keys
   456         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key.pub\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"'))
   457         # set permissions for authorized_keys
   458         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"'))
   459         # generate iso image with .ssh/authorized keys
   460         Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/genisoimage -J -R -o \\\"' + machineFolder + '/' + vm_name + '/'+ vm_name + '.iso\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"'))
   461     
   462     # attaches generated ssh public cert to guest vm
   463     def attachCertificateISO(self, vm_name):
   464         result = Cygwin.checkResult(Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 1 --device 0 --type dvddrive --mtype readonly --medium \"' + self.machineFolder + '\\' + vm_name + '\\'+ vm_name + '.iso\"'))
   465         return result
   466     
   467     # wait for machine to come up
   468     def waitStartup(self, vm_name, timeout_ms = 30000):
   469         Cygwin.checkResult(Cygwin.vboxExecute('guestproperty wait ' + vm_name + ' SDVMStarted --timeout ' + str(timeout_ms) + ' --fail-on-timeout'))
   470         return VMManager.getHostOnlyIP(vm_name)
   471     
   472     # wait for machine to shutdown
   473     def waitShutdown(self, vm_name):
   474         while vm_name in self.listRunningVMS():
   475             time.sleep(1)
   476         return
   477     
   478     #Small function to check if the mentioned location is a directory
   479     def isDirectory(self, path):
   480         result = Cygwin.checkResult(Cygwin.cmdExecute('dir ' + path + ' | FIND ".."'))
   481         return string.find(result[1], 'DIR',)
   482     
   483     def getNetworkDrives(self):
   484         ip = VMManager.getHostOnlyIP(None)
   485         ip = ip[:ip.rindex('.')]
   486         drives = dict()    
   487         result = Cygwin.checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE'))
   488 
   489         #logger.info( result[1] )
   490         for line in result[1].splitlines():
   491             if ip in line:
   492                 parts = line.split()
   493                 drives[parts[1]] = parts[2]
   494 
   495         return drives
   496 
   497     def getNetworkDrive(self, vm_name):
   498         ip = self.getHostOnlyIP(vm_name)
   499         result = Cygwin.checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE'))
   500         for line in result[1].splitlines():
   501             if line != None and ip in line:
   502                 parts = line.split()
   503                 return parts[1]
   504     
   505     def genNetworkDrive(self):
   506         network_drives = self.getNetworkDrives()
   507         logger.info("Used network drive letters: "+ str(network_drives.keys()).strip('[]') ) 
   508         logical_drives = VMManager.getLogicalDrives()
   509         logger.info("Used logical drive letters: "+ str(logical_drives).strip('[]') )
   510         drives = list(map(chr, range(68, 91)))  
   511         for drive in drives:
   512             if drive+':' not in network_drives and drive not in logical_drives:
   513                 return drive+':'
   514             
   515     @staticmethod
   516     def getLogicalDrives():
   517         drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
   518         return list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
   519     
   520     @staticmethod
   521     def getDriveType(drive):
   522         return ctypes.cdll.kernel32.GetDriveTypeW(u"%s:\\"%drive)
   523     
   524     @staticmethod
   525     def getVolumeInfo(drive):
   526         volumeNameBuffer = ctypes.create_unicode_buffer(1024)
   527         fileSystemNameBuffer = ctypes.create_unicode_buffer(1024)
   528         serial_number = None
   529         max_component_length = None
   530         file_system_flags = None
   531         
   532         rc = ctypes.cdll.kernel32.GetVolumeInformationW(
   533             #ctypes.c_wchar_p("F:\\"),
   534             u"%s:\\"%drive,
   535             volumeNameBuffer,
   536             ctypes.sizeof(volumeNameBuffer),
   537             serial_number,
   538             max_component_length,
   539             file_system_flags,
   540             fileSystemNameBuffer,
   541             ctypes.sizeof(fileSystemNameBuffer)
   542         )
   543         
   544         return volumeNameBuffer.value, fileSystemNameBuffer.value
   545     
   546     # handles browsing request    
   547     def handleBrowsingRequest(self):
   548         handler = BrowsingHandler(self)
   549         handler.start()
   550         return 'ok'
   551     
   552     def getActiveUserName(self):
   553         key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI')
   554         v = str(win32api.RegQueryValueEx(key, 'LastLoggedOnUser')[0])
   555         win32api.RegCloseKey(key)
   556         user_name = win32api.ExpandEnvironmentStrings(v)
   557         return user_name
   558         
   559     def getUserSID(self, user_name):
   560         domain, user = user_name.split("\\")
   561         account_name = win32security.LookupAccountName(domain, user)
   562         if account_name == None:
   563             logger.error("Failed lookup account name for user " + user_name)
   564             return None
   565         sid = win32security.ConvertSidToStringSid(account_name[0])
   566         if sid == None:
   567             logger.error("Failed converting SID for account " + account_name[0])
   568             return None
   569         return sid
   570         
   571     def getAppDataDir(self, sid):    
   572         key = win32api.RegOpenKey(win32con.HKEY_USERS, sid + '\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders')
   573         value, type = win32api.RegQueryValueEx(key, "AppData")
   574         win32api.RegCloseKey(key)
   575         return value
   576         
   577         #key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' + '\\' + sid)
   578         #value, type = win32api.RegQueryValueEx(key, "ProfileImagePath")
   579         #print value
   580     
   581     def backupFile(self, src, dest):
   582         certificate = Cygwin.cygPath(self.getMachineFolder()) + '/' + self.browsingManager.vm_name + '/dvm_key'
   583         command = '-r -o StrictHostKeyChecking=no -i "' + certificate + '" "osecuser@' + self.browsingManager.ip_addr + ':' + src + '" "' + dest + '"'
   584         return Cygwin.execute(Cygwin.cygwin_scp, command, wait_return=True, window=False)
   585     
   586     def restoreFile(self, src, dest):
   587         certificate = Cygwin.cygPath(self.getMachineFolder()) + '/' + self.browsingManager.vm_name + '/dvm_key'
   588         #command = '-r -v -o StrictHostKeyChecking=no -i \"' + certificate + '\" \"' + src + '\" \"osecuser@' + self.browsingManager.ip_addr + ':' + dest + '\"'
   589         command = '-r -o StrictHostKeyChecking=no -i "' + certificate + '" "' + src + '" "osecuser@' + self.browsingManager.ip_addr + ':' + dest + '"'
   590         return Cygwin.execute(Cygwin.cygwin_scp, command, wait_return=True, window=False)    
   591 
   592 
   593 #handles browsing session creation 
   594 class BrowsingHandler(threading.Thread):
   595     vmm = None
   596     def __init__(self, vmmanager):
   597          threading.Thread.__init__(self)
   598          self.vmm = vmmanager
   599         
   600     def run(self):
   601         browser = '\\\"/usr/bin/chromium; pidof dbus-launch | xargs kill\\\"'
   602         if not Cygwin.is_X11_running():
   603             self.vmm.browsingManager.restart.set()
   604             return
   605         
   606         try:
   607             self.vmm.browsingManager.started.wait() 
   608             result = Cygwin.checkResult(Cygwin.sshExecuteX11(browser, self.vmm.browsingManager.ip_addr, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + self.vmm.browsingManager.vm_name + '/dvm_key'))
   609             #backup settings on vm
   610         except:
   611             logger.error("BrowsingHandler closing. Cleaning up")    
   612         self.vmm.backupFile('/home/osecuser/.config/chromium', self.vmm.browsingManager.appDataDir + '/OpenSecurity/')
   613         self.vmm.browsingManager.restart.set()
   614         
   615             
   616 # handles browsing Vm creation and destruction                    
   617 class BrowsingManager(threading.Thread):   
   618     vmm = None
   619     running = True
   620     restart = None
   621     ip_addr = None
   622     vm_name = None
   623     drive = None
   624     appDataDir = None
   625     
   626     def __init__(self, vmmanager):
   627         threading.Thread.__init__(self)
   628         self.vmm = vmmanager
   629         self.restart = threading.Event()
   630         self.started = threading.Event()
   631         
   632      
   633     def run(self):
   634         while self.running:
   635             self.restart.clear()
   636             self.started.clear()
   637             
   638             if self.drive == None:
   639                 logger.info("Missing browsing SDVM's network drive letter. Skipping disconnect")
   640             else:
   641                 try:
   642                     browsing_vm = urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'drive_letter='+self.drive).readline()
   643                     self.drive = None
   644                 except urllib2.URLError:
   645                     logger.error("Network drive disconnect failed. OpenSecurity Tray client not running.")
   646                     continue
   647             
   648             self.ip_addr = None
   649 
   650             if self.vm_name != None:
   651                 self.vmm.poweroffVM(self.vm_name)
   652                 self.vmm.removeVM(self.vm_name)
   653             
   654             try:
   655                 self.vm_name = self.vmm.newSDVM()
   656                 self.vmm.storageAttach(self.vm_name)
   657                 self.vmm.genCertificateISO(self.vm_name)
   658                 self.vmm.attachCertificateISO(self.vm_name)
   659                 self.vmm.startVM(self.vm_name)
   660                 self.ip_addr = self.vmm.waitStartup(self.vm_name)
   661                 if self.ip_addr == None:
   662                     logger.error("Failed to get ip address")
   663                     continue
   664                 else:
   665                     logger.info("Got IP address for " + self.vm_name + ' ' + self.ip_addr)
   666                 
   667                 self.drive = self.vmm.genNetworkDrive()
   668                 if self.drive == None:
   669                     logger.error("Failed to assign Network drive letter")
   670                     continue
   671                 else:
   672                     logger.info("Assigned drive " + self.drive + " to " + self.vm_name)
   673                 
   674                 try:
   675                     net_resource = '\\\\' + self.ip_addr + '\\Download'
   676                     result = urllib2.urlopen('http://127.0.0.1:8090/netmount?'+'drive_letter='+self.drive+'&net_resource='+net_resource).readline()
   677                 except urllib2.URLError:
   678                     logger.error("Network drive connect failed. OpenSecurity Tray client not running.")
   679                     self.drive = None
   680                     continue
   681                 
   682                 user = self.vmm.getActiveUserName()
   683                 if user == None:
   684                     logger.error("Cannot get active user name")
   685                     continue
   686                 else:
   687                     logger.info('Got active user name ' + user)
   688                 sid = self.vmm.getUserSID(user)
   689                 if sid == None:
   690                     logger.error("Cannot get SID for active user")
   691                     continue
   692                 else:
   693                     logger.info("Got active user SID " + sid + " for user " + user)
   694                     
   695                 path = self.vmm.getAppDataDir(sid)
   696                 if path == None:
   697                     logger.error("Cannot get AppDataDir for active user")
   698                     continue
   699                 else:
   700                     logger.info("Got AppData dir for user " + user + ': ' + path)
   701                 
   702                 self.appDataDir = Cygwin.cygPath(path)
   703                 logger.info("Restoring browser settings in AppData dir " + self.appDataDir)
   704                 # create OpenSecurity settings dir on local machine user home /AppData/Roaming 
   705                 Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + self.appDataDir + '/OpenSecurity\\\"'))
   706                 # create chromium settings dir on local machine if not existing
   707                 Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + self.appDataDir + '/OpenSecurity/chromium\\\"'))
   708                 # create chromium settings dir on remote machine if not existing
   709                 Cygwin.checkResult(Cygwin.sshExecute('"mkdir -p \\\"/home/osecuser/.config\\\""', self.ip_addr, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + self.vm_name + '/dvm_key'))
   710                 #restore settings on vm
   711                 self.vmm.restoreFile(self.appDataDir + '/OpenSecurity/chromium', '/home/osecuser/.config/')
   712                 self.started.set()
   713                 logger.error("Browsing SDVM running.")
   714                 self.restart.wait()
   715             except:
   716                 logger.error("BrowsingHandler failed. Cleaning up")
   717                 
   718 class DeviceHandler(threading.Thread): 
   719     vmm = None
   720     attachedRSDs = None  
   721     connectedRSDs = None
   722     running = True
   723     def __init__(self, vmmanger): 
   724         threading.Thread.__init__(self)
   725         self.vmm = vmmanger
   726  
   727     def stop(self):
   728         self.running = False
   729         
   730     def run(self):
   731         self.connectedRSDs = dict()
   732         
   733         while self.running:
   734             tmp_rsds = self.vmm.getConnectedRSDS()
   735             
   736             self.attachedRSDs = self.vmm.getAttachedRSDs()
   737             for vm_name in self.attachedRSDs.keys():
   738                 if self.attachedRSDs[vm_name] not in tmp_rsds.values():
   739                     drive = self.vmm.getNetworkDrive(vm_name)
   740                     if drive == None:
   741                         logger.error("Error getting SDVM's network drive letter.")
   742                         continue
   743                     try:
   744                         result = urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'drive_letter='+drive).readline()
   745                     except urllib2.URLError:
   746                         logger.error("Network drive disconnect failed. OpenSecurity Tray client not running.")
   747                         continue
   748                     self.vmm.detachRSD(vm_name)
   749                     self.vmm.poweroffVM(vm_name)
   750                     self.vmm.removeVM(vm_name)
   751                     break
   752                     
   753                     
   754             if tmp_rsds.keys() == self.connectedRSDs.keys():
   755                 logger.debug("Nothing's changed. sleep(3)")
   756                 time.sleep(3)
   757                 continue
   758             
   759             logger.info("Something's changed")          
   760             self.connectedRSDs = tmp_rsds
   761            
   762             #create new vm for attached device if any
   763             self.attachedRSDs = self.vmm.getAttachedRSDs()
   764             self.connectedRSDs = self.vmm.getConnectedRSDS()
   765             
   766             new_ip = None
   767             for connected_device in self.connectedRSDs.values():
   768                 if (self.attachedRSDs and False) or (connected_device not in self.attachedRSDs.values()):
   769                     new_sdvm = self.vmm.newSDVM()
   770                     self.vmm.storageAttach(new_sdvm)
   771                     self.vmm.attachRSD(new_sdvm, connected_device)
   772                     self.vmm.startVM(new_sdvm)
   773                     new_ip = self.vmm.waitStartup(new_sdvm)
   774                     if new_ip == None:
   775                         logger.error("Error getting IP address of SDVM.")
   776                         continue
   777                     drive = self.vmm.genNetworkDrive()
   778                     if drive == None:
   779                         logger.error("Error getting drive letter for network drive.")
   780                         continue
   781                     try:
   782                         net_resource = '\\\\' + new_ip + '\\USB'
   783                         result = urllib2.urlopen('http://127.0.0.1:8090/netmount?'+'drive_letter='+drive+'&net_resource='+net_resource).readline()
   784                     except urllib2.URLError:
   785                         logger.error("Network drive connect failed. OpenSecurity Tray client not running.")
   786                         continue
   787                         
   788 
   789 if __name__ == '__main__':
   790     #man = VMManager.getInstance()
   791     #man.listVM()
   792     #print man.getConnectedRSDs()
   793     #print man.getNetworkDrives()
   794     #man.genNetworkDrive()
   795     #drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
   796     #print list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
   797     #print list(map(chr, range(68, 91))) 
   798     #print Cygwin.getRegEntry('SYSTEM\CurrentControlSet\Enum\USB', 'VID_1058&PID_0704')[0]
   799     #devices = VMManager.getConnectedRSDS()
   800     #print devices
   801     
   802     drives = VMManager.getLogicalDrives()
   803     print drives
   804     print VMManager.getDriveType("E")
   805     print VMManager.getVolumeInfo("E")
   806     
   807     #vmm.backupFile()
   808     #for device in devices.values():
   809     #    #print device
   810     #    if VMManager.isMassStorageDevice(device):
   811     #        print device
   812         
   813     
   814     
   815     #time.sleep(-1)
   816     #man.listVM()
   817     #man.listVM()
   818     #man.listVM()
   819     #man.listVM()
   820     #man.genCertificateISO('SecurityDVM0')
   821     #man.guestExecute('SecurityDVM0', '/bin/ls -la')
   822     #logger = setupLogger('VMManager')
   823     #c = Cygwin()
   824     
   825     #man.sshExecute('/bin/ls -la', 'SecurityDVM0')
   826     #man.sshExecuteX11('/usr/bin/iceweasel', 'SecurityDVM0')
   827     #man.removeVM('SecurityDVM0')
   828     #man.netUse('192.168.56.134', 'USB\\')
   829     #ip = '192.168.56.139'
   830     
   831     #man.cygwin_path = 'c:\\cygwin64\\bin\\'
   832     #man.handleDeviceChange()
   833     #print man.listSDVM()
   834     #man.configureHostNetworking()
   835     #new_vm = man.generateSDVMName()
   836     #man.createVM(new_vm)
   837     
   838     #print Cygwin.cmd()
   839     #man.isAvailable('c:')
   840     #ip = man.getHostOnlyIP('SecurityDVM0')
   841     #man.mapNetworkDrive('h:', '\\\\' + ip + '\Download', None, None)
   842     
   843     #man.genCertificateISO(new_vm)
   844     #man.attachCertificateISO(new_vm)
   845     
   846     #man.attachCertificateISO(vm_name)
   847     #man.guestExecute(vm_name, "ls")
   848     #man.sshGuestX11Execute('SecurityDVM1', '/usr/bin/iceweasel')
   849     #time.sleep(60)
   850     #print man.cygwinPath("C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\.ssh\*")
   851     #man.genCertificateISO('SecurityDVM')
   852     #man.attachCertificateISO('SecurityDVM')
   853     #man.isStorageAttached('SecurityDVM')
   854     #man.guestExecute('SecurityDVM', 'sudo apt-get -y update')
   855     #man.guestExecute('SecurityDVM', 'sudo apt-get -y upgrade' )
   856     
   857     #man.stopVM('SecurityDVM')
   858     #man.storageDetach('SecurityDVM')
   859     #man.changeStorageType('C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\SecurityDVM.vmdk','immutable')
   860     #man.storageAttach('SecurityDVM')
   861     
   862     
   863     #cmd = "c:\\cygwin64\\bin\\bash.exe --login -c \"/bin/ls\""
   864     #man.execute(cmd)