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