OpenSecurity/bin/vmmanager.pyw
author Oliver Maurhart <oliver.maurhart@ait.ac.at>
Thu, 22 May 2014 12:17:57 +0200
changeset 169 a133c8d03ef8
parent 167 1e1811fa44bc
child 170 81d5f845d966
permissions -rwxr-xr-x
yet another eXtreme programming 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         browser = '\\\"/usr/bin/chromium\\\"'
   603         #if not Cygwin.is_X11_running():
   604         #    self.vmm.browsingManager.restart.set()
   605         #    return
   606         
   607         try:
   608             self.vmm.browsingManager.started.wait() 
   609             result = Cygwin.checkResult(Cygwin.sshExecuteX11(browser, self.vmm.browsingManager.ip_addr, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + self.vmm.browsingManager.vm_name + '/dvm_key'))
   610             self.vmm.backupFile('/home/osecuser/.config/chromium', self.vmm.browsingManager.appDataDir + '/OpenSecurity/')
   611         except:
   612             logger.info("BrowsingHandler closing. Restarting browsing SDVM.")
   613 
   614         self.vmm.browsingManager.restart.set()
   615         
   616             
   617 # handles browsing Vm creation and destruction                    
   618 class BrowsingManager(threading.Thread):   
   619     vmm = None
   620     running = True
   621     restart = None
   622     ip_addr = None
   623     vm_name = None
   624     drive = None
   625     appDataDir = None
   626     
   627     def __init__(self, vmmanager):
   628         threading.Thread.__init__(self)
   629         self.vmm = vmmanager
   630         self.restart = threading.Event()
   631         self.started = threading.Event()
   632         
   633      
   634     def run(self):
   635         while self.running:
   636             self.restart.clear()
   637             self.started.clear()
   638             
   639             if self.drive == None:
   640                 logger.info("Missing browsing SDVM's network drive letter. Skipping disconnect")
   641             else:
   642                 try:
   643                     browsing_vm = urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'drive_letter='+self.drive).readline()
   644                     self.drive = None
   645                 except urllib2.URLError:
   646                     logger.error("Network drive disconnect failed. OpenSecurity Tray client not running.")
   647                     continue
   648             
   649             self.ip_addr = None
   650 
   651             if self.vm_name != None:
   652                 self.vmm.poweroffVM(self.vm_name)
   653                 self.vmm.removeVM(self.vm_name)
   654             
   655             try:
   656                 self.vm_name = self.vmm.newSDVM()
   657                 self.vmm.storageAttach(self.vm_name)
   658                 self.vmm.genCertificateISO(self.vm_name)
   659                 self.vmm.attachCertificateISO(self.vm_name)
   660                 self.vmm.startVM(self.vm_name)
   661                 self.ip_addr = self.vmm.waitStartup(self.vm_name)
   662                 if self.ip_addr == None:
   663                     logger.error("Failed to get ip address")
   664                     continue
   665                 else:
   666                     logger.info("Got IP address for " + self.vm_name + ' ' + self.ip_addr)
   667                 
   668                 self.drive = self.vmm.genNetworkDrive()
   669                 if self.drive == None:
   670                     logger.error("Failed to assign Network drive letter")
   671                     continue
   672                 else:
   673                     logger.info("Assigned drive " + self.drive + " to " + self.vm_name)
   674                 
   675                 try:
   676                     net_resource = '\\\\' + self.ip_addr + '\\Download'
   677                     result = urllib2.urlopen('http://127.0.0.1:8090/netmount?'+'drive_letter='+self.drive+'&net_resource='+net_resource).readline()
   678                 except urllib2.URLError:
   679                     logger.error("Network drive connect failed. OpenSecurity Tray client not running.")
   680                     self.drive = None
   681                     continue
   682                 
   683                 user = self.vmm.getActiveUserName()
   684                 if user == None:
   685                     logger.error("Cannot get active user name")
   686                     continue
   687                 else:
   688                     logger.info('Got active user name ' + user)
   689                 sid = self.vmm.getUserSID(user)
   690                 if sid == None:
   691                     logger.error("Cannot get SID for active user")
   692                     continue
   693                 else:
   694                     logger.info("Got active user SID " + sid + " for user " + user)
   695                     
   696                 path = self.vmm.getAppDataDir(sid)
   697                 if path == None:
   698                     logger.error("Cannot get AppDataDir for active user")
   699                     continue
   700                 else:
   701                     logger.info("Got AppData dir for user " + user + ': ' + path)
   702                 
   703                 self.appDataDir = Cygwin.cygPath(path)
   704                 logger.info("Restoring browser settings in AppData dir " + self.appDataDir)
   705                 # create OpenSecurity settings dir on local machine user home /AppData/Roaming 
   706                 Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + self.appDataDir + '/OpenSecurity\\\"'))
   707                 # create chromium settings dir on local machine if not existing
   708                 Cygwin.checkResult(Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + self.appDataDir + '/OpenSecurity/chromium\\\"'))
   709                 # create chromium settings dir on remote machine if not existing
   710                 Cygwin.checkResult(Cygwin.sshExecute('"mkdir -p \\\"/home/osecuser/.config\\\""', self.ip_addr, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + self.vm_name + '/dvm_key'))
   711                 #restore settings on vm
   712                 self.vmm.restoreFile(self.appDataDir + '/OpenSecurity/chromium', '/home/osecuser/.config/')
   713                 self.started.set()
   714                 logger.error("Browsing SDVM running.")
   715                 self.restart.wait()
   716             except:
   717                 logger.error("BrowsingHandler failed. Cleaning up")
   718                 
   719 class DeviceHandler(threading.Thread): 
   720     vmm = None
   721     attachedRSDs = None  
   722     connectedRSDs = None
   723     running = True
   724     def __init__(self, vmmanger): 
   725         threading.Thread.__init__(self)
   726         self.vmm = vmmanger
   727  
   728     def stop(self):
   729         self.running = False
   730         
   731     def run(self):
   732         self.connectedRSDs = dict()
   733         
   734         while self.running:
   735             tmp_rsds = self.vmm.getConnectedRSDS()
   736             
   737             self.attachedRSDs = self.vmm.getAttachedRSDs()
   738             for vm_name in self.attachedRSDs.keys():
   739                 if self.attachedRSDs[vm_name] not in tmp_rsds.values():
   740                     drive = self.vmm.getNetworkDrive(vm_name)
   741                     if drive == None:
   742                         logger.error("Error getting SDVM's network drive letter.")
   743                         continue
   744                     try:
   745                         result = urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'drive_letter='+drive).readline()
   746                     except urllib2.URLError:
   747                         logger.error("Network drive disconnect failed. OpenSecurity Tray client not running.")
   748                         continue
   749                     self.vmm.detachRSD(vm_name)
   750                     self.vmm.poweroffVM(vm_name)
   751                     self.vmm.removeVM(vm_name)
   752                     break
   753                     
   754                     
   755             if tmp_rsds.keys() == self.connectedRSDs.keys():
   756                 logger.debug("Nothing's changed. sleep(3)")
   757                 time.sleep(3)
   758                 continue
   759             
   760             logger.info("Something's changed")          
   761             self.connectedRSDs = tmp_rsds
   762            
   763             #create new vm for attached device if any
   764             self.attachedRSDs = self.vmm.getAttachedRSDs()
   765             self.connectedRSDs = self.vmm.getConnectedRSDS()
   766             
   767             new_ip = None
   768             for connected_device in self.connectedRSDs.values():
   769                 if (self.attachedRSDs and False) or (connected_device not in self.attachedRSDs.values()):
   770                     new_sdvm = self.vmm.newSDVM()
   771                     self.vmm.storageAttach(new_sdvm)
   772                     self.vmm.attachRSD(new_sdvm, connected_device)
   773                     self.vmm.startVM(new_sdvm)
   774                     new_ip = self.vmm.waitStartup(new_sdvm)
   775                     if new_ip == None:
   776                         logger.error("Error getting IP address of SDVM.")
   777                         continue
   778                     drive = self.vmm.genNetworkDrive()
   779                     if drive == None:
   780                         logger.error("Error getting drive letter for network drive.")
   781                         continue
   782                     try:
   783                         net_resource = '\\\\' + new_ip + '\\USB'
   784                         result = urllib2.urlopen('http://127.0.0.1:8090/netmount?'+'drive_letter='+drive+'&net_resource='+net_resource).readline()
   785                     except urllib2.URLError:
   786                         logger.error("Network drive connect failed. OpenSecurity Tray client not running.")
   787                         continue
   788                         
   789 
   790 if __name__ == '__main__':
   791     #man = VMManager.getInstance()
   792     #man.listVM()
   793     #print man.getConnectedRSDs()
   794     #print man.getNetworkDrives()
   795     #man.genNetworkDrive()
   796     #drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
   797     #print list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
   798     #print list(map(chr, range(68, 91))) 
   799     #print Cygwin.getRegEntry('SYSTEM\CurrentControlSet\Enum\USB', 'VID_1058&PID_0704')[0]
   800     #devices = VMManager.getConnectedRSDS()
   801     #print devices
   802     
   803     drives = VMManager.getLogicalDrives()
   804     print drives
   805     print VMManager.getDriveType("E")
   806     print VMManager.getVolumeInfo("E")
   807     
   808     #vmm.backupFile()
   809     #for device in devices.values():
   810     #    #print device
   811     #    if VMManager.isMassStorageDevice(device):
   812     #        print device
   813         
   814     
   815     
   816     #time.sleep(-1)
   817     #man.listVM()
   818     #man.listVM()
   819     #man.listVM()
   820     #man.listVM()
   821     #man.genCertificateISO('SecurityDVM0')
   822     #man.guestExecute('SecurityDVM0', '/bin/ls -la')
   823     #logger = setupLogger('VMManager')
   824     #c = Cygwin()
   825     
   826     #man.sshExecute('/bin/ls -la', 'SecurityDVM0')
   827     #man.sshExecuteX11('/usr/bin/iceweasel', 'SecurityDVM0')
   828     #man.removeVM('SecurityDVM0')
   829     #man.netUse('192.168.56.134', 'USB\\')
   830     #ip = '192.168.56.139'
   831     
   832     #man.cygwin_path = 'c:\\cygwin64\\bin\\'
   833     #man.handleDeviceChange()
   834     #print man.listSDVM()
   835     #man.configureHostNetworking()
   836     #new_vm = man.generateSDVMName()
   837     #man.createVM(new_vm)
   838     
   839     #print Cygwin.cmd()
   840     #man.isAvailable('c:')
   841     #ip = man.getHostOnlyIP('SecurityDVM0')
   842     #man.mapNetworkDrive('h:', '\\\\' + ip + '\Download', None, None)
   843     
   844     #man.genCertificateISO(new_vm)
   845     #man.attachCertificateISO(new_vm)
   846     
   847     #man.attachCertificateISO(vm_name)
   848     #man.guestExecute(vm_name, "ls")
   849     #man.sshGuestX11Execute('SecurityDVM1', '/usr/bin/iceweasel')
   850     #time.sleep(60)
   851     #print man.cygwinPath("C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\.ssh\*")
   852     #man.genCertificateISO('SecurityDVM')
   853     #man.attachCertificateISO('SecurityDVM')
   854     #man.isStorageAttached('SecurityDVM')
   855     #man.guestExecute('SecurityDVM', 'sudo apt-get -y update')
   856     #man.guestExecute('SecurityDVM', 'sudo apt-get -y upgrade' )
   857     
   858     #man.stopVM('SecurityDVM')
   859     #man.storageDetach('SecurityDVM')
   860     #man.changeStorageType('C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\SecurityDVM.vmdk','immutable')
   861     #man.storageAttach('SecurityDVM')
   862     
   863     
   864     #cmd = "c:\\cygwin64\\bin\\bash.exe --login -c \"/bin/ls\""
   865     #man.execute(cmd)