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