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