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