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