OpenSecurity/bin/vmmanager.pyw
author mb
Tue, 18 Mar 2014 16:31:47 +0100
changeset 96 630b62946c9e
parent 95 cdebb7e0ba10
parent 91 a26757850ea9
child 97 f274426bdbb4
permissions -rwxr-xr-x
merge
     1 <<<<<<< local
     2 '''
     3 Created on Nov 19, 2013
     4 
     5 @author: BarthaM
     6 '''
     7 import os
     8 import os.path
     9 from subprocess import Popen, PIPE, call, STARTUPINFO, _subprocess
    10 import sys
    11 import re
    12 
    13 from cygwin import Cygwin
    14 from environment import Environment
    15 import threading
    16 import time
    17 import string
    18 
    19 import shutil
    20 import stat
    21 import tempfile
    22 from opensecurity_util import logger, setupLogger, OpenSecurityException
    23 import ctypes
    24 import itertools
    25 import _winreg
    26 DEBUG = True
    27 
    28 class VMManagerException(Exception):
    29     def __init__(self, value):
    30         self.value = value
    31     def __str__(self):
    32         return repr(self.value)
    33 
    34 class USBFilter:
    35     vendorid = ""
    36     productid = ""
    37     revision = ""
    38     
    39     def __init__(self, vendorid, productid, revision):
    40         self.vendorid = vendorid.lower()
    41         self.productid = productid.lower()
    42         self.revision = revision.lower()
    43         return
    44     
    45     def __eq__(self, other):
    46         return self.vendorid == other.vendorid and self.productid == other.productid and self.revision == other.revision
    47     
    48     def __hash__(self):
    49         return hash(self.vendorid) ^ hash(self.productid) ^ hash(self.revision)
    50     
    51     def __repr__(self):
    52         return "VendorId = \'" + str(self.vendorid) + "\' ProductId = \'" + str(self.productid) + "\' Revision = \'" + str(self.revision) + "\'"
    53     
    54     #def __getitem__(self, item):
    55     #    return self.coords[item]
    56  
    57 class VMManager(object):
    58     vmRootName = "SecurityDVM"
    59     systemProperties = None
    60     _instance = None
    61     machineFolder = ''
    62     rsdHandler = None
    63     
    64     def __init__(self):
    65         self.systemProperties = self.getSystemProperties()
    66         self.machineFolder = self.systemProperties["Default machine folder"]
    67         self.cleanup()
    68         self.rsdHandler = DeviceHandler(self)
    69         self.rsdHandler.start()
    70         return
    71     
    72     @staticmethod
    73     def getInstance():
    74         if VMManager._instance == None:
    75             VMManager._instance = VMManager()
    76         return VMManager._instance
    77     
    78     def cleanup(self):
    79         if self.rsdHandler != None:
    80             self.rsdHandler.stop()
    81             self.rsdHandler.join()
    82         drives = self.getNetworkDrives()
    83         for drive in drives.keys():
    84             self.unmapNetworkDrive(drive)
    85         for vm in self.listSDVM():
    86             self.poweroffVM(vm)
    87             self.removeVM(vm)
    88         
    89     # return hosty system properties
    90     def getSystemProperties(self):
    91         result = checkResult(Cygwin.vboxExecute('list systemproperties'))
    92         if result[1]=='':
    93             return None
    94         props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result[1].strip().splitlines()))
    95         return props
    96     
    97     # return the folder containing the guest VMs     
    98     def getMachineFolder(self):
    99         return self.machineFolder
   100 
   101     # list all existing VMs registered with VBox
   102     def listVM(self):
   103         result = checkResult(Cygwin.vboxExecute('list vms'))[1]
   104         vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   105         return vms
   106     
   107     # list running VMs
   108     def listRunningVMS(self):
   109         result = checkResult(Cygwin.vboxExecute('list runningvms'))[1]
   110         vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   111         return vms
   112     
   113     # list existing SDVMs
   114     def listSDVM(self):
   115         vms = self.listVM()
   116         svdms = []
   117         for vm in vms:
   118             if vm.startswith(self.vmRootName) and vm != self.vmRootName:
   119                 svdms.append(vm)
   120         return svdms
   121     
   122     # generate valid (not already existing SDVM name). necessary for creating a new VM
   123     def generateSDVMName(self):
   124         vms = self.listVM()
   125         for i in range(0,999):
   126             if(not self.vmRootName+str(i) in vms):
   127                 return self.vmRootName+str(i)
   128         return ''
   129     
   130     # check if the device is mass storage type
   131     @staticmethod
   132     def isMassStorageDevice(device):
   133         keyname = 'SYSTEM\CurrentControlSet\Enum\USB' + '\VID_' + device.vendorid+'&'+'PID_'+ device.productid
   134         key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keyname)
   135         #subkeys = _winreg.QueryInfoKey(key)[0]
   136         #for i in range(0, subkeys):
   137         #    print _winreg.EnumKey(key, i)     
   138         devinfokeyname = _winreg.EnumKey(key, 0)
   139         _winreg.CloseKey(key)
   140 
   141         devinfokey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keyname+'\\'+devinfokeyname)
   142         value = _winreg.QueryValueEx(devinfokey, 'SERVICE')[0]
   143         _winreg.CloseKey(devinfokey)
   144         
   145         return 'USBSTOR' in value
   146     
   147     # return the RSDs connected to the host
   148     @staticmethod
   149     def getConnectedRSDS():
   150         results = checkResult(Cygwin.vboxExecute('list usbhost'))[1]
   151         results = results.split('Host USB Devices:')[1].strip()
   152         
   153         items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   154         rsds = dict()   
   155         for item in items:
   156             props = dict()
   157             for line in item.splitlines():
   158                 if line != "":         
   159                     k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   160                     props[k] = v
   161             
   162             #if 'Product' in props.keys() and props['Product'] == 'Mass Storage':
   163             
   164             usb_filter = USBFilter( re.search(r"\((?P<vid>[0-9A-Fa-f]+)\)", props['VendorId']).groupdict()['vid'], 
   165                                     re.search(r"\((?P<pid>[0-9A-Fa-f]+)\)", props['ProductId']).groupdict()['pid'],
   166                                     re.search(r"\((?P<rev>[0-9A-Fa-f]+)\)", props['Revision']).groupdict()['rev'] )
   167             if VMManager.isMassStorageDevice(usb_filter):
   168                 rsds[props['UUID']] = usb_filter;
   169                 logger.debug(usb_filter)
   170         return rsds
   171     
   172     # return the RSDs attached to all existing SDVMs
   173     def getAttachedRSDs(self):
   174         vms = self.listSDVM()
   175         attached_devices = dict()
   176         for vm in vms:
   177             rsd_filter = self.getUSBFilter(vm)
   178             if rsd_filter != None:
   179                 attached_devices[vm] = rsd_filter
   180         return attached_devices
   181     
   182     # configures hostonly networking and DHCP server. requires admin rights
   183     def configureHostNetworking(self):
   184         #cmd = 'vboxmanage list hostonlyifs'
   185         #Cygwin.vboxExecute(cmd)
   186         #cmd = 'vboxmanage hostonlyif remove \"VirtualBox Host-Only Ethernet Adapter\"'
   187         #Cygwin.vboxExecute(cmd)
   188         #cmd = 'vboxmanage hostonlyif create'
   189         #Cygwin.vboxExecute(cmd)
   190         checkResult(Cygwin.vboxExecute('hostonlyif ipconfig \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.1 --netmask 255.255.255.0'))
   191         #cmd = 'vboxmanage dhcpserver add'
   192         #Cygwin.vboxExecute(cmd)
   193         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'))
   194     
   195     #create new virtual machine instance based on template vm named SecurityDVM (\SecurityDVM\SecurityDVM.vmdk)
   196     def createVM(self, vm_name):
   197         hostonly_if = self.getHostOnlyIFs()
   198         checkResult(Cygwin.vboxExecute('createvm --name ' + vm_name + ' --ostype Debian --register'))
   199         checkResult(Cygwin.vboxExecute('modifyvm ' + vm_name + ' --memory 512 --vram 10 --cpus 1 --usb on --usbehci on --nic1 hostonly --hostonlyadapter1 \"' + hostonly_if['Name'] + '\" --nic2 nat'))
   200         checkResult(Cygwin.vboxExecute('storagectl ' + vm_name + ' --name SATA --add sata --portcount 2'))
   201         return
   202     
   203     # attach storage image to controller
   204     def storageAttach(self, vm_name):
   205         if self.isStorageAttached(vm_name):
   206             self.storageDetach(vm_name)
   207         checkResult(Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium \"'+ self.machineFolder + '\SecurityDVM\SecurityDVM.vmdk\"'))
   208     
   209     # return true if storage is attached 
   210     def isStorageAttached(self, vm_name):
   211         info = self.getVMInfo(vm_name)
   212         return (info['SATA-0-0']!='none')
   213     
   214     # detach storage from controller
   215     def storageDetach(self, vm_name):
   216         if self.isStorageAttached(vm_name):
   217             checkResult(Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium none'))
   218     
   219     def changeStorageType(self, filename, storage_type):
   220         checkResult(Cygwin.vboxExecute('modifyhd \"' + filename + '\" --type ' + storage_type))
   221     
   222     # list storage snaphots for VM
   223     def updateTemplate(self):
   224 
   225         self.cleanup()
   226         self.poweroffVM('SecurityDVM')
   227         self.waitShutdown('SecurityDVM')
   228         
   229         # check for updates
   230         self.genCertificateISO('SecurityDVM')
   231         self.attachCertificateISO('SecurityDVM')
   232         
   233         self.storageDetach('SecurityDVM')
   234         results = checkResult(Cygwin.vboxExecute('list hdds'))[1]
   235         results = results.replace('Parent UUID', 'Parent')
   236         items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   237         
   238         snaps = dict()   
   239         for item in items:
   240             props = dict()
   241             for line in item.splitlines():
   242                 if line != "":         
   243                     k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   244                     props[k] = v;
   245             snaps[props['UUID']] = props
   246         
   247         
   248         template_storage = self.machineFolder + '\SecurityDVM\SecurityDVM.vmdk'
   249         
   250         # find template uuid
   251         template_uuid = ''
   252         for hdd in snaps.values():
   253             if hdd['Location'] == template_storage:
   254                 template_uuid = hdd['UUID']
   255         logger.debug('found parent uuid ' + template_uuid)
   256         
   257         # remove snapshots 
   258         for hdd in snaps.values():
   259             if hdd['Parent'] == template_uuid:
   260                 #template_uuid = hdd['UUID']
   261                 logger.debug('removing snapshot ' + hdd['UUID'])
   262                 checkResult(Cygwin.vboxExecute('closemedium disk {' + hdd['UUID'] + '} --delete'))#[1]
   263                 # parse result 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
   264         
   265         self.changeStorageType(template_storage,'normal')
   266         self.storageAttach('SecurityDVM')
   267         self.startVM('SecurityDVM')
   268         self.waitStartup('SecurityDVM')
   269         checkResult(Cygwin.sshExecute('"sudo apt-get -y update"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key'))
   270         checkResult(Cygwin.sshExecute('"sudo apt-get -y upgrade"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key'))
   271         #self.stopVM('SecurityDVM')
   272         self.hibernateVM('SecurityDVM')
   273         self.waitShutdown('SecurityDVM')
   274         self.storageDetach('SecurityDVM')
   275         self.changeStorageType(template_storage,'immutable')
   276         self.storageAttach('SecurityDVM')
   277         self.rsdHandler = DeviceHandler(self)
   278         self.rsdHandler.start()
   279     
   280     #remove VM from the system. should be used on VMs returned by listSDVMs    
   281     def removeVM(self, vm_name):
   282         logger.info('Removing ' + vm_name)
   283         checkResult(Cygwin.vboxExecute('unregistervm ' + vm_name + ' --delete'))
   284         machineFolder = Cygwin.cygPath(self.machineFolder)
   285         checkResult(Cygwin.bashExecute('"/usr/bin/rm -rf ' + machineFolder + '/' + vm_name + '"'))
   286     
   287     # start VM
   288     def startVM(self, vm_name):
   289         logger.info('Starting ' +  vm_name)
   290         result = checkResult(Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless' ))
   291         while 'successfully started' not in result[1]:
   292             logger.error("Failed to start SDVM: " + vm_name + " retrying")
   293             time.sleep(1)
   294             result = checkResult(Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless'))
   295         return result[0]
   296     
   297     # return wether VM is running or not
   298     def isVMRunning(self, vm_name):
   299         return vm_name in self.listRunningVMS()    
   300     
   301     # stop VM
   302     def stopVM(self, vm_name):
   303         logger.info('Sending shutdown signal to ' + vm_name)
   304         checkResult(Cygwin.sshExecute( '"sudo shutdown -h now"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key' ))
   305     
   306     # stop VM
   307     def hibernateVM(self, vm_name):
   308         logger.info('Sending hibernate-disk signal to ' + vm_name)
   309         checkResult(Cygwin.sshExecute( '"sudo hibernate-disk&"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key', wait_return=False))
   310             
   311     # poweroff VM
   312     def poweroffVM(self, vm_name):
   313         if not self.isVMRunning(vm_name):
   314             return
   315         logger.info('Powering off ' + vm_name)
   316         return checkResult(Cygwin.vboxExecute('controlvm ' + vm_name + ' poweroff'))
   317     
   318     #list the hostonly IFs exposed by the VBox host
   319     @staticmethod    
   320     def getHostOnlyIFs():
   321         result = Cygwin.vboxExecute('list hostonlyifs')[1]
   322         if result=='':
   323             return None
   324         props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result.strip().splitlines()))
   325         return props
   326     
   327     # return the hostOnly IP for a running guest or the host
   328     @staticmethod    
   329     def getHostOnlyIP(vm_name):
   330         if vm_name == None:
   331             logger.info('Gettting hostOnly IP address for Host')
   332             return VMManager.getHostOnlyIFs()['IPAddress']
   333         else:
   334             logger.info('Gettting hostOnly IP address ' + vm_name)
   335             result = checkResult(Cygwin.vboxExecute('guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP'))
   336             if result=='':
   337                 return None
   338             result = result[1]
   339             if result.startswith('No value set!'):
   340                 return None
   341             return result[result.index(':')+1:].strip()
   342             
   343     # attach removable storage device to VM by provision of filter
   344     def attachRSD(self, vm_name, rsd_filter):
   345         return checkResult(Cygwin.vboxExecute('usbfilter add 0 --target ' + vm_name + ' --name OpenSecurityRSD --vendorid ' + rsd_filter.vendorid + ' --productid ' + rsd_filter.productid + ' --revision ' + rsd_filter.revision))
   346     
   347     # detach removable storage from VM by 
   348     def detachRSD(self, vm_name):
   349         return checkResult(Cygwin.vboxExecute('usbfilter remove 0 --target ' + vm_name))
   350         
   351     # return the description set for an existing VM
   352     def getVMInfo(self, vm_name):
   353         results = checkResult(Cygwin.vboxExecute('showvminfo ' + vm_name + ' --machinereadable'))[1]
   354         props = dict((k.strip().strip('"'),v.strip().strip('"')) for k,v in (line.split('=', 1) for line in results.splitlines()))
   355         return props
   356     
   357     # return the configured USB filter for an existing VM 
   358     def getUSBFilter(self, vm_name):
   359         props = self.getVMInfo(vm_name)
   360         keys = set(['USBFilterVendorId1', 'USBFilterProductId1', 'USBFilterRevision1'])
   361         keyset = set(props.keys())
   362         usb_filter = None
   363         if keyset.issuperset(keys):
   364             usb_filter = USBFilter(props['USBFilterVendorId1'], props['USBFilterProductId1'], props['USBFilterRevision1'])
   365         return usb_filter
   366     
   367     #generates ISO containing authorized_keys for use with guest VM
   368     def genCertificateISO(self, vm_name):
   369         machineFolder = Cygwin.cygPath(self.machineFolder)
   370         # remove .ssh folder if exists
   371         checkResult(Cygwin.bashExecute('\"/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"'))
   372         # remove .ssh folder if exists
   373         checkResult(Cygwin.bashExecute('\"/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"\"'))
   374         # create .ssh folder in vm_name
   375         checkResult(Cygwin.bashExecute('\"/usr/bin/mkdir -p \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"'))
   376         # generate dvm_key pair in vm_name / .ssh     
   377         checkResult(Cygwin.bashExecute('\"/usr/bin/ssh-keygen -q -t rsa -N \\"\\" -C \\\"' + vm_name + '\\\" -f \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\"\"'))
   378         # move out private key
   379         checkResult(Cygwin.bashExecute('\"/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\" \\\"' + machineFolder + '/' + vm_name + '\\\"'))
   380         # set permissions for private key
   381         checkResult(Cygwin.bashExecute('\"/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"\"'))
   382         # rename public key to authorized_keys
   383         checkResult(Cygwin.bashExecute('\"/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key.pub\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"'))
   384         # set permissions for authorized_keys
   385         checkResult(Cygwin.bashExecute('\"/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"\"'))
   386         # generate iso image with .ssh/authorized keys
   387         checkResult(Cygwin.bashExecute('\"/usr/bin/genisoimage -J -R -o \\\"' + machineFolder + '/' + vm_name + '/'+ vm_name + '.iso\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"'))
   388     
   389     # attaches generated ssh public cert to guest vm
   390     def attachCertificateISO(self, vm_name):
   391         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\"'))
   392         return result
   393     
   394     # wait for machine to come up
   395     def waitStartup(self, vm_name, timeout_ms = 30000):
   396         checkResult(Cygwin.vboxExecute('guestproperty wait ' + vm_name + ' SDVMStarted --timeout ' + str(timeout_ms) + ' --fail-on-timeout'))
   397         return VMManager.getHostOnlyIP(vm_name)
   398     
   399     # wait for machine to shutdown
   400     def waitShutdown(self, vm_name):
   401         while vm_name in self.listRunningVMS():
   402             time.sleep(1)
   403         return
   404         
   405     # handles browsing request    
   406     def handleBrowsingRequest(self):
   407         handler = BrowsingHandler(self)
   408         handler.start()
   409         return 'ok'
   410     
   411     #Small function to check the availability of network resource.
   412     #def isAvailable(self, path):
   413         #return os.path.exists(path)
   414         #result = Cygwin.cmdExecute('IF EXIST "' + path + '" echo YES')
   415         #return string.find(result[1], 'YES',)
   416     
   417     #Small function to check if the mention location is a directory
   418     def isDirectory(self, path):
   419         result = checkResult(Cygwin.cmdExecute('dir ' + path + ' | FIND ".."'))
   420         return string.find(result[1], 'DIR',)
   421 
   422     def mapNetworkDrive(self, drive, networkPath, user, password):
   423         self.unmapNetworkDrive(drive)
   424         #Check for drive availability
   425         if os.path.exists(drive):
   426             logger.error("Drive letter is already in use: " + drive)
   427             return -1
   428         #Check for network resource availability
   429         retry = 5
   430         while not os.path.exists(networkPath):
   431             time.sleep(1)
   432             if retry == 0:
   433                 return -1
   434             logger.info("Path not accessible: " + networkPath + " retrying")
   435             retry-=1
   436             #return -1
   437     
   438         command = 'USE ' + drive + ' ' + networkPath + ' /PERSISTENT:NO'
   439         if user != None:
   440             command += ' ' + password + ' /User' + user
   441     
   442         #TODO: Execute 'NET USE' command with authentication
   443         result = checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', command))
   444         if string.find(result[1], 'successfully',) == -1:
   445             logger.error("Failed: NET " + command)
   446             return -1
   447         return 1
   448     
   449     def unmapNetworkDrive(self, drive):
   450         drives = self.getNetworkDrives()
   451         if drive not in drives.keys():
   452             return 1 
   453         result = checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE ' + drive + ' /DELETE /YES'))
   454         if string.find(str(result[1]), 'successfully',) == -1:
   455             logger.error(result[2])
   456             return -1
   457         return 1
   458     
   459     def getNetworkDrives(self):
   460         ip = VMManager.getHostOnlyIP(None)
   461         ip = ip[:ip.rindex('.')]
   462         drives = dict()    
   463         result = checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE'))
   464         for line in result[1].splitlines():
   465             if ip in line:
   466                 parts = line.split()
   467                 drives[parts[1]] = parts[2]
   468         return drives
   469             
   470     def genNetworkDrive(self):
   471         network_drives = self.getNetworkDrives()
   472         logical_drives = VMManager.getLogicalDrives()
   473         drives = list(map(chr, range(68, 91)))  
   474         for drive in drives:
   475             if drive+':' not in network_drives and drive not in logical_drives:
   476                 return drive+':'
   477 
   478     def getNetworkDrive(self, vm_name):
   479         ip = self.getHostOnlyIP(vm_name)
   480         result = checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE'))
   481         for line in result[1].splitlines():
   482             if line != None and ip in line:
   483                 parts = line.split()
   484                 return parts[0]
   485     @staticmethod
   486     def getLogicalDrives():
   487         drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
   488         return list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
   489     
   490     @staticmethod
   491     def getDriveType(drive):
   492         return ctypes.cdll.kernel32.GetDriveTypeW(u"%s:\\"%drive)
   493     
   494     @staticmethod
   495     def getVolumeInfo(drive):
   496         volumeNameBuffer = ctypes.create_unicode_buffer(1024)
   497         fileSystemNameBuffer = ctypes.create_unicode_buffer(1024)
   498         serial_number = None
   499         max_component_length = None
   500         file_system_flags = None
   501         
   502         rc = ctypes.cdll.kernel32.GetVolumeInformationW(
   503             #ctypes.c_wchar_p("F:\\"),
   504             u"%s:\\"%drive,
   505             volumeNameBuffer,
   506             ctypes.sizeof(volumeNameBuffer),
   507             serial_number,
   508             max_component_length,
   509             file_system_flags,
   510             fileSystemNameBuffer,
   511             ctypes.sizeof(fileSystemNameBuffer)
   512         )
   513         
   514         return volumeNameBuffer.value, fileSystemNameBuffer.value
   515 
   516 def checkResult(result):
   517     if result[0] != 0:
   518         logger.error('Command failed:' + ''.join(result[2]))
   519         raise OpenSecurityException('Command failed:' + ''.join(result[2]))
   520     return result
   521 
   522 # handles browsing request                    
   523 class BrowsingHandler(threading.Thread):   
   524     vmm = None
   525     def __init__(self, vmmanager):
   526         threading.Thread.__init__(self)
   527         self.vmm = vmmanager
   528      
   529     def run(self):
   530         try:
   531             Cygwin.start_X11()
   532             new_sdvm = self.vmm.generateSDVMName()
   533             self.vmm.createVM(new_sdvm)
   534             self.vmm.storageAttach(new_sdvm)
   535             self.vmm.genCertificateISO(new_sdvm)
   536             self.vmm.attachCertificateISO(new_sdvm)
   537             self.vmm.startVM(new_sdvm)
   538             new_ip = self.vmm.waitStartup(new_sdvm)
   539             drive = self.vmm.genNetworkDrive()
   540             if new_ip != None:
   541                 self.vmm.mapNetworkDrive(drive, '\\\\' + new_ip + '\\Download', None, None)
   542             #browser = '/usr/bin/iceweasel'
   543             #browser = '/usr/bin/midori'
   544             browser = '/usr/bin/chromium'
   545             result = checkResult(Cygwin.sshExecuteX11(browser, new_ip, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + new_sdvm + '/dvm_key'))
   546         except:
   547             logger.error("BrowsingHandler failed. Cleaning up")
   548             
   549         self.vmm.unmapNetworkDrive(drive)
   550         self.vmm.poweroffVM(new_sdvm)
   551         self.vmm.removeVM(new_sdvm)
   552                 
   553 class DeviceHandler(threading.Thread): 
   554     vmm = None
   555     attachedRSDs = None  
   556     connectedRSDs = None
   557     running = True
   558     def __init__(self, vmmanger): 
   559         threading.Thread.__init__(self)
   560         self.vmm = vmmanger
   561  
   562     def stop(self):
   563         self.running = False
   564         
   565     def run(self):
   566         self.connectedRSDs = dict()
   567         while self.running:
   568             tmp_rsds = self.vmm.getConnectedRSDS()
   569             
   570             self.attachedRSDs = self.vmm.getAttachedRSDs()
   571             for vm_name in self.attachedRSDs.keys():
   572                 if self.attachedRSDs[vm_name] not in tmp_rsds.values():
   573                     drive = self.vmm.getNetworkDrive(vm_name)
   574                     self.vmm.unmapNetworkDrive(drive)
   575                     #self.stopVM(vm_name)
   576                     self.vmm.detachRSD(vm_name)
   577                     self.vmm.poweroffVM(vm_name)
   578                     self.vmm.removeVM(vm_name)
   579                     break
   580                     
   581                     
   582             if tmp_rsds.keys() == self.connectedRSDs.keys():
   583                 logger.debug("Nothing's changed. sleep(3)")
   584                 time.sleep(3)
   585                 continue
   586             
   587             logger.info("Something's changed")          
   588             self.connectedRSDs = tmp_rsds
   589             
   590             
   591             
   592             #create new vm for attached device if any
   593             self.attachedRSDs = self.vmm.getAttachedRSDs()
   594             self.connectedRSDs = self.vmm.getConnectedRSDS()
   595             
   596             new_ip = None
   597             for connected_device in self.connectedRSDs.values():
   598                 if (self.attachedRSDs and False) or (connected_device not in self.attachedRSDs.values()):
   599                     new_sdvm = self.vmm.generateSDVMName()
   600                     self.vmm.createVM(new_sdvm)
   601                     self.vmm.storageAttach(new_sdvm)
   602                     self.vmm.attachRSD(new_sdvm, connected_device)
   603                     self.vmm.startVM(new_sdvm)
   604                     new_ip = self.vmm.waitStartup(new_sdvm)
   605                     drive = self.vmm.genNetworkDrive()
   606                     if new_ip != None:
   607                         self.vmm.mapNetworkDrive(drive, '\\\\' + new_ip + '\\USB', None, None)
   608 
   609 if __name__ == '__main__':
   610     #man = VMManager.getInstance()
   611     #man.listVM()
   612     #print man.getConnectedRSDs()
   613     #print man.getNetworkDrives()
   614     #man.genNetworkDrive()
   615     #drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
   616     #print list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
   617     #print list(map(chr, range(68, 91))) 
   618     #print Cygwin.getRegEntry('SYSTEM\CurrentControlSet\Enum\USB', 'VID_1058&PID_0704')[0]
   619     #devices = VMManager.getConnectedRSDS()
   620     #print devices
   621     
   622     drives = VMManager.getLogicalDrives()
   623     print drives
   624     print VMManager.getDriveType("E")
   625     print VMManager.getVolumeInfo("E")
   626     #for device in devices.values():
   627     #    #print device
   628     #    if VMManager.isMassStorageDevice(device):
   629     #        print device
   630         
   631     
   632     
   633     #time.sleep(-1)
   634     #man.listVM()
   635     #man.listVM()
   636     #man.listVM()
   637     #man.listVM()
   638     #man.genCertificateISO('SecurityDVM0')
   639     #man.guestExecute('SecurityDVM0', '/bin/ls -la')
   640     #logger = setupLogger('VMManager')
   641     #c = Cygwin()
   642     
   643     #man.sshExecute('/bin/ls -la', 'SecurityDVM0')
   644     #man.sshExecuteX11('/usr/bin/iceweasel', 'SecurityDVM0')
   645     #man.removeVM('SecurityDVM0')
   646     #man.netUse('192.168.56.134', 'USB\\')
   647     #ip = '192.168.56.139'
   648     
   649     #man.cygwin_path = 'c:\\cygwin64\\bin\\'
   650     #man.handleDeviceChange()
   651     #print man.listSDVM()
   652     #man.configureHostNetworking()
   653     #new_vm = man.generateSDVMName()
   654     #man.createVM(new_vm)
   655     
   656     #print Cygwin.cmd()
   657     #man.isAvailable('c:')
   658     #ip = man.getHostOnlyIP('SecurityDVM0')
   659     #man.mapNetworkDrive('h:', '\\\\' + ip + '\Download', None, None)
   660     
   661     #man.genCertificateISO(new_vm)
   662     #man.attachCertificateISO(new_vm)
   663     
   664     #man.attachCertificateISO(vm_name)
   665     #man.guestExecute(vm_name, "ls")
   666     #man.sshGuestX11Execute('SecurityDVM1', '/usr/bin/iceweasel')
   667     #time.sleep(60)
   668     #print man.cygwinPath("C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\.ssh\*")
   669     #man.genCertificateISO('SecurityDVM')
   670     #man.attachCertificateISO('SecurityDVM')
   671     #man.isStorageAttached('SecurityDVM')
   672     #man.guestExecute('SecurityDVM', 'sudo apt-get -y update')
   673     #man.guestExecute('SecurityDVM', 'sudo apt-get -y upgrade' )
   674     
   675     #man.stopVM('SecurityDVM')
   676     #man.storageDetach('SecurityDVM')
   677     #man.changeStorageType('C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\SecurityDVM.vmdk','immutable')
   678     #man.storageAttach('SecurityDVM')
   679     
   680     
   681     #cmd = "c:\\cygwin64\\bin\\bash.exe --login -c \"/bin/ls\""
   682     #man.execute(cmd)
   683 =======
   684 '''
   685 Created on Nov 19, 2013
   686 
   687 @author: BarthaM
   688 '''
   689 import os
   690 import os.path
   691 from subprocess import Popen, PIPE, call, STARTUPINFO, _subprocess
   692 import sys
   693 import re
   694 
   695 from cygwin import Cygwin
   696 from environment import Environment
   697 import threading
   698 import time
   699 import string
   700 
   701 import shutil
   702 import stat
   703 import tempfile
   704 from opensecurity_util import logger, setupLogger, OpenSecurityException
   705 import ctypes
   706 import itertools
   707 import _winreg
   708 DEBUG = True
   709 
   710 class VMManagerException(Exception):
   711     def __init__(self, value):
   712         self.value = value
   713     def __str__(self):
   714         return repr(self.value)
   715 
   716 class USBFilter:
   717     vendorid = ""
   718     productid = ""
   719     revision = ""
   720     
   721     def __init__(self, vendorid, productid, revision):
   722         self.vendorid = vendorid.lower()
   723         self.productid = productid.lower()
   724         self.revision = revision.lower()
   725         return
   726     
   727     def __eq__(self, other):
   728         return self.vendorid == other.vendorid and self.productid == other.productid and self.revision == other.revision
   729     
   730     def __hash__(self):
   731         return hash(self.vendorid) ^ hash(self.productid) ^ hash(self.revision)
   732     
   733     def __repr__(self):
   734         return "VendorId = \'" + str(self.vendorid) + "\' ProductId = \'" + str(self.productid) + "\' Revision = \'" + str(self.revision) + "\'"
   735     
   736     #def __getitem__(self, item):
   737     #    return self.coords[item]
   738  
   739 class VMManager(object):
   740     vmRootName = "SecurityDVM"
   741     systemProperties = None
   742     _instance = None
   743     machineFolder = ''
   744     rsdHandler = None
   745     _running = True
   746     
   747     def __init__(self):
   748         self._running = True
   749         self.systemProperties = self.getSystemProperties()
   750         self.machineFolder = self.systemProperties["Default machine folder"]
   751         self.cleanup()
   752         self.rsdHandler = DeviceHandler(self)
   753         self.rsdHandler.start()
   754         return
   755     
   756     @staticmethod
   757     def getInstance():
   758         if VMManager._instance == None:
   759             VMManager._instance = VMManager()
   760         return VMManager._instance
   761     
   762     def cleanup(self):
   763         if self.rsdHandler != None:
   764             self.rsdHandler.stop()
   765             self.rsdHandler.join()
   766         drives = self.getNetworkDrives()
   767         for drive in drives.keys():
   768             self.unmapNetworkDrive(drive)
   769         for vm in self.listSDVM():
   770             self.poweroffVM(vm)
   771             self.removeVM(vm)
   772         
   773     # return hosty system properties
   774     def getSystemProperties(self):
   775         result = checkResult(Cygwin.vboxExecute('list systemproperties'))
   776         if result[1]=='':
   777             return None
   778         props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result[1].strip().splitlines()))
   779         return props
   780     
   781     # return the folder containing the guest VMs     
   782     def getMachineFolder(self):
   783         return self.machineFolder
   784 
   785     # list all existing VMs registered with VBox
   786     def listVM(self):
   787         result = checkResult(Cygwin.vboxExecute('list vms'))[1]
   788         vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   789         return vms
   790     
   791     # list running VMs
   792     def listRunningVMS(self):
   793         result = checkResult(Cygwin.vboxExecute('list runningvms'))[1]
   794         vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
   795         return vms
   796     
   797     # list existing SDVMs
   798     def listSDVM(self):
   799         vms = self.listVM()
   800         svdms = []
   801         for vm in vms:
   802             if vm.startswith(self.vmRootName) and vm != self.vmRootName:
   803                 svdms.append(vm)
   804         return svdms
   805     
   806     # generate valid (not already existing SDVM name). necessary for creating a new VM
   807     def generateSDVMName(self):
   808         vms = self.listVM()
   809         for i in range(0,999):
   810             if(not self.vmRootName+str(i) in vms):
   811                 return self.vmRootName+str(i)
   812         return ''
   813     
   814     # check if the device is mass storage type
   815     @staticmethod
   816     def isMassStorageDevice(device):
   817         keyname = 'SYSTEM\CurrentControlSet\Enum\USB' + '\VID_' + device.vendorid+'&'+'PID_'+ device.productid
   818         key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keyname)
   819         #subkeys = _winreg.QueryInfoKey(key)[0]
   820         #for i in range(0, subkeys):
   821         #    print _winreg.EnumKey(key, i)     
   822         devinfokeyname = _winreg.EnumKey(key, 0)
   823         _winreg.CloseKey(key)
   824 
   825         devinfokey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keyname+'\\'+devinfokeyname)
   826         value = _winreg.QueryValueEx(devinfokey, 'SERVICE')[0]
   827         _winreg.CloseKey(devinfokey)
   828         
   829         return 'USBSTOR' in value
   830     
   831     # return the RSDs connected to the host
   832     @staticmethod
   833     def getConnectedRSDS():
   834         results = checkResult(Cygwin.vboxExecute('list usbhost'))[1]
   835         results = results.split('Host USB Devices:')[1].strip()
   836         
   837         items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   838         rsds = dict()   
   839         for item in items:
   840             props = dict()
   841             for line in item.splitlines():
   842                 if line != "":         
   843                     k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   844                     props[k] = v
   845             
   846             #if 'Product' in props.keys() and props['Product'] == 'Mass Storage':
   847             
   848             usb_filter = USBFilter( re.search(r"\((?P<vid>[0-9A-Fa-f]+)\)", props['VendorId']).groupdict()['vid'], 
   849                                     re.search(r"\((?P<pid>[0-9A-Fa-f]+)\)", props['ProductId']).groupdict()['pid'],
   850                                     re.search(r"\((?P<rev>[0-9A-Fa-f]+)\)", props['Revision']).groupdict()['rev'] )
   851             if VMManager.isMassStorageDevice(usb_filter):
   852                 rsds[props['UUID']] = usb_filter;
   853                 logger.debug(usb_filter)
   854         return rsds
   855     
   856     # return the RSDs attached to all existing SDVMs
   857     def getAttachedRSDs(self):
   858         vms = self.listSDVM()
   859         attached_devices = dict()
   860         for vm in vms:
   861             rsd_filter = self.getUSBFilter(vm)
   862             if rsd_filter != None:
   863                 attached_devices[vm] = rsd_filter
   864         return attached_devices
   865     
   866     # configures hostonly networking and DHCP server. requires admin rights
   867     def configureHostNetworking(self):
   868         #cmd = 'vboxmanage list hostonlyifs'
   869         #Cygwin.vboxExecute(cmd)
   870         #cmd = 'vboxmanage hostonlyif remove \"VirtualBox Host-Only Ethernet Adapter\"'
   871         #Cygwin.vboxExecute(cmd)
   872         #cmd = 'vboxmanage hostonlyif create'
   873         #Cygwin.vboxExecute(cmd)
   874         checkResult(Cygwin.vboxExecute('hostonlyif ipconfig \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.1 --netmask 255.255.255.0'))
   875         #cmd = 'vboxmanage dhcpserver add'
   876         #Cygwin.vboxExecute(cmd)
   877         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'))
   878     
   879     #create new virtual machine instance based on template vm named SecurityDVM (\SecurityDVM\SecurityDVM.vmdk)
   880     def createVM(self, vm_name):
   881         hostonly_if = self.getHostOnlyIFs()
   882         checkResult(Cygwin.vboxExecute('createvm --name ' + vm_name + ' --ostype Debian --register'))
   883         checkResult(Cygwin.vboxExecute('modifyvm ' + vm_name + ' --memory 512 --vram 10 --cpus 1 --usb on --usbehci on --nic1 hostonly --hostonlyadapter1 \"' + hostonly_if['Name'] + '\" --nic2 nat'))
   884         checkResult(Cygwin.vboxExecute('storagectl ' + vm_name + ' --name SATA --add sata --portcount 2'))
   885         return
   886     
   887     # attach storage image to controller
   888     def storageAttach(self, vm_name):
   889         if self.isStorageAttached(vm_name):
   890             self.storageDetach(vm_name)
   891         checkResult(Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium \"'+ self.machineFolder + '\SecurityDVM\SecurityDVM.vmdk\"'))
   892     
   893     # return true if storage is attached 
   894     def isStorageAttached(self, vm_name):
   895         info = self.getVMInfo(vm_name)
   896         return (info['SATA-0-0']!='none')
   897     
   898     # detach storage from controller
   899     def storageDetach(self, vm_name):
   900         if self.isStorageAttached(vm_name):
   901             checkResult(Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium none'))
   902     
   903     def changeStorageType(self, filename, storage_type):
   904         checkResult(Cygwin.vboxExecute('modifyhd \"' + filename + '\" --type ' + storage_type))
   905     
   906     # list storage snaphots for VM
   907     def updateTemplate(self):
   908         self.cleanup()
   909         self.poweroffVM('SecurityDVM')
   910         self.waitShutdown('SecurityDVM')
   911         
   912         # check for updates
   913         self.genCertificateISO('SecurityDVM')
   914         self.attachCertificateISO('SecurityDVM')
   915         
   916         self.storageDetach('SecurityDVM')
   917         results = checkResult(Cygwin.vboxExecute('list hdds'))[1]
   918         results = results.replace('Parent UUID', 'Parent')
   919         items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
   920         
   921         snaps = dict()   
   922         for item in items:
   923             props = dict()
   924             for line in item.splitlines():
   925                 if line != "":         
   926                     k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
   927                     props[k] = v;
   928             snaps[props['UUID']] = props
   929         
   930         
   931         template_storage = self.machineFolder + '\SecurityDVM\SecurityDVM.vmdk'
   932         
   933         # find template uuid
   934         template_uuid = ''
   935         for hdd in snaps.values():
   936             if hdd['Location'] == template_storage:
   937                 template_uuid = hdd['UUID']
   938         logger.debug('found parent uuid ' + template_uuid)
   939         
   940         # remove snapshots 
   941         for hdd in snaps.values():
   942             if hdd['Parent'] == template_uuid:
   943                 #template_uuid = hdd['UUID']
   944                 logger.debug('removing snapshot ' + hdd['UUID'])
   945                 checkResult(Cygwin.vboxExecute('closemedium disk {' + hdd['UUID'] + '} --delete'))#[1]
   946                 # parse result 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
   947         
   948         self.changeStorageType(template_storage,'normal')
   949         self.storageAttach('SecurityDVM')
   950         self.startVM('SecurityDVM')
   951         self.waitStartup('SecurityDVM')
   952         checkResult(Cygwin.sshExecute('"sudo apt-get -y update"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key'))
   953         checkResult(Cygwin.sshExecute('"sudo apt-get -y upgrade"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key'))
   954         #self.stopVM('SecurityDVM')
   955         self.hibernateVM('SecurityDVM')
   956         self.waitShutdown('SecurityDVM')
   957         self.storageDetach('SecurityDVM')
   958         self.changeStorageType(template_storage,'immutable')
   959         self.storageAttach('SecurityDVM')
   960         self.rsdHandler = DeviceHandler(self)
   961         self.rsdHandler.start()
   962     
   963     #remove VM from the system. should be used on VMs returned by listSDVMs    
   964     def removeVM(self, vm_name):
   965         logger.info('Removing ' + vm_name)
   966         checkResult(Cygwin.vboxExecute('unregistervm ' + vm_name + ' --delete'))
   967         vm_file = Cygwin.cygPath(self.machineFolder + '\\' + vm_name)
   968         checkResult(Cygwin.bashExecute('rm -rf \'' + vm_file + '\''))
   969     
   970     # start VM
   971     def startVM(self, vm_name):
   972         logger.info('Starting ' +  vm_name)
   973         result = checkResult(Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless' ))
   974         while 'successfully started' not in result[1] and _running:
   975             logger.error("Failed to start SDVM: " + vm_name + " retrying")
   976             time.sleep(1)
   977             result = checkResult(Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless'))
   978         return result[0]
   979     
   980     # return wether VM is running or not
   981     def isVMRunning(self, vm_name):
   982         return vm_name in self.listRunningVMS()    
   983     
   984     # stop VM
   985     def stopVM(self, vm_name):
   986         logger.info('Sending shutdown signal to ' + vm_name)
   987         checkResult(Cygwin.sshExecute( '"sudo shutdown -h now"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key' ))
   988     
   989     # stop VM
   990     def hibernateVM(self, vm_name):
   991         logger.info('Sending shutdown signal to ' + vm_name)
   992         checkResult(Cygwin.sshExecute( '"sudo hibernate-disk&"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key', wait_return=False))
   993             
   994     # poweroff VM
   995     def poweroffVM(self, vm_name):
   996         if not self.isVMRunning(vm_name):
   997             return
   998         logger.info('Powering off ' + vm_name)
   999         return checkResult(Cygwin.vboxExecute('controlvm ' + vm_name + ' poweroff'))
  1000     
  1001     #list the hostonly IFs exposed by the VBox host
  1002     @staticmethod    
  1003     def getHostOnlyIFs():
  1004         result = Cygwin.vboxExecute('list hostonlyifs')[1]
  1005         if result=='':
  1006             return None
  1007         props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result.strip().splitlines()))
  1008         return props
  1009     
  1010     # return the hostOnly IP for a running guest or the host
  1011     @staticmethod    
  1012     def getHostOnlyIP(vm_name):
  1013         if vm_name == None:
  1014             logger.info('Gettting hostOnly IP address for Host')
  1015             return VMManager.getHostOnlyIFs()['IPAddress']
  1016         else:
  1017             logger.info('Gettting hostOnly IP address ' + vm_name)
  1018             result = checkResult(Cygwin.vboxExecute('guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP'))
  1019             if result=='':
  1020                 return None
  1021             result = result[1]
  1022             if result.startswith('No value set!'):
  1023                 return None
  1024             return result[result.index(':')+1:].strip()
  1025             
  1026     # attach removable storage device to VM by provision of filter
  1027     def attachRSD(self, vm_name, rsd_filter):
  1028         return checkResult(Cygwin.vboxExecute('usbfilter add 0 --target ' + vm_name + ' --name OpenSecurityRSD --vendorid ' + rsd_filter.vendorid + ' --productid ' + rsd_filter.productid + ' --revision ' + rsd_filter.revision))
  1029     
  1030     # detach removable storage from VM by 
  1031     def detachRSD(self, vm_name):
  1032         return checkResult(Cygwin.vboxExecute('usbfilter remove 0 --target ' + vm_name))
  1033         
  1034     # return the description set for an existing VM
  1035     def getVMInfo(self, vm_name):
  1036         results = checkResult(Cygwin.vboxExecute('showvminfo ' + vm_name + ' --machinereadable'))[1]
  1037         props = dict((k.strip().strip('"'),v.strip().strip('"')) for k,v in (line.split('=', 1) for line in results.splitlines()))
  1038         return props
  1039     
  1040     # return the configured USB filter for an existing VM 
  1041     def getUSBFilter(self, vm_name):
  1042         props = self.getVMInfo(vm_name)
  1043         keys = set(['USBFilterVendorId1', 'USBFilterProductId1', 'USBFilterRevision1'])
  1044         keyset = set(props.keys())
  1045         usb_filter = None
  1046         if keyset.issuperset(keys):
  1047             usb_filter = USBFilter(props['USBFilterVendorId1'], props['USBFilterProductId1'], props['USBFilterRevision1'])
  1048         return usb_filter
  1049     
  1050     #generates ISO containing authorized_keys for use with guest VM
  1051     def genCertificateISO(self, vm_name):
  1052         machineFolder = Cygwin.cygPath(self.machineFolder)
  1053         # remove .ssh folder if exists
  1054         checkResult(Cygwin.bashExecute('\"/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"'))
  1055         # remove .ssh folder if exists
  1056         checkResult(Cygwin.bashExecute('\"/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"\"'))
  1057         # create .ssh folder in vm_name
  1058         checkResult(Cygwin.bashExecute('\"/usr/bin/mkdir -p \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"'))
  1059         # generate dvm_key pair in vm_name / .ssh     
  1060         checkResult(Cygwin.bashExecute('\"/usr/bin/ssh-keygen -q -t rsa -N \\"\\" -C \\\"' + vm_name + '\\\" -f \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\"\"'))
  1061         # move out private key
  1062         checkResult(Cygwin.bashExecute('\"/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\" \\\"' + machineFolder + '/' + vm_name + '\\\"'))
  1063         # set permissions for private key
  1064         checkResult(Cygwin.bashExecute('\"/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"\"'))
  1065         # rename public key to authorized_keys
  1066         checkResult(Cygwin.bashExecute('\"/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key.pub\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"'))
  1067         # set permissions for authorized_keys
  1068         checkResult(Cygwin.bashExecute('\"/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"\"'))
  1069         # generate iso image with .ssh/authorized keys
  1070         checkResult(Cygwin.bashExecute('\"/usr/bin/genisoimage -J -R -o \\\"' + machineFolder + '/' + vm_name + '/'+ vm_name + '.iso\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"'))
  1071     
  1072     # attaches generated ssh public cert to guest vm
  1073     def attachCertificateISO(self, vm_name):
  1074         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\"'))
  1075         return result
  1076     
  1077     # wait for machine to come up
  1078     def waitStartup(self, vm_name, timeout_ms = 30000):
  1079         checkResult(Cygwin.vboxExecute('guestproperty wait ' + vm_name + ' SDVMStarted --timeout ' + str(timeout_ms) + ' --fail-on-timeout'))
  1080         return VMManager.getHostOnlyIP(vm_name)
  1081     
  1082     # wait for machine to shutdown
  1083     def waitShutdown(self, vm_name):
  1084         while vm_name in self.listRunningVMS() and _running:
  1085             time.sleep(1)
  1086         return
  1087         
  1088     # handles browsing request    
  1089     def handleBrowsingRequest(self):
  1090         handler = BrowsingHandler(self)
  1091         handler.start()
  1092         return 'ok'
  1093     
  1094     #Small function to check the availability of network resource.
  1095     #def isAvailable(self, path):
  1096         #return os.path.exists(path)
  1097         #result = Cygwin.cmdExecute('IF EXIST "' + path + '" echo YES')
  1098         #return string.find(result[1], 'YES',)
  1099     
  1100     #Small function to check if the mention location is a directory
  1101     def isDirectory(self, path):
  1102         result = checkResult(Cygwin.cmdExecute('dir ' + path + ' | FIND ".."'))
  1103         return string.find(result[1], 'DIR',)
  1104 
  1105     def mapNetworkDrive(self, drive, networkPath, user, password):
  1106         self.unmapNetworkDrive(drive)
  1107         #Check for drive availability
  1108         if os.path.exists(drive):
  1109             logger.error("Drive letter is already in use: " + drive)
  1110             return -1
  1111         #Check for network resource availability
  1112         retry = 5
  1113         while not os.path.exists(networkPath):
  1114             time.sleep(1)
  1115             if retry == 0:
  1116                 return -1
  1117             logger.info("Path not accessible: " + networkPath + " retrying")
  1118             retry-=1
  1119             #return -1
  1120     
  1121         command = 'USE ' + drive + ' ' + networkPath + ' /PERSISTENT:NO'
  1122         if user != None:
  1123             command += ' ' + password + ' /User' + user
  1124     
  1125         #TODO: Execute 'NET USE' command with authentication
  1126         result = checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', command))
  1127         if string.find(result[1], 'successfully',) == -1:
  1128             logger.error("Failed: NET " + command)
  1129             return -1
  1130         return 1
  1131     
  1132     def unmapNetworkDrive(self, drive):
  1133         drives = self.getNetworkDrives()
  1134         if drive not in drives.keys():
  1135             return 1 
  1136         result = checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE ' + drive + ' /DELETE /YES'))
  1137         if string.find(str(result[1]), 'successfully',) == -1:
  1138             logger.error(result[2])
  1139             return -1
  1140         return 1
  1141     
  1142     def getNetworkDrives(self):
  1143         ip = VMManager.getHostOnlyIP(None)
  1144         ip = ip[:ip.rindex('.')]
  1145         drives = dict()    
  1146         result = checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE'))
  1147         for line in result[1].splitlines():
  1148             if ip in line:
  1149                 parts = line.split()
  1150                 drives[parts[1]] = parts[2]
  1151         return drives
  1152             
  1153     def genNetworkDrive(self):
  1154         network_drives = self.getNetworkDrives()
  1155         logical_drives = VMManager.getLogicalDrives()
  1156         drives = list(map(chr, range(68, 91)))  
  1157         for drive in drives:
  1158             if drive+':' not in network_drives and drive not in logical_drives:
  1159                 return drive+':'
  1160 
  1161     def getNetworkDrive(self, vm_name):
  1162         ip = self.getHostOnlyIP(vm_name)
  1163         result = checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE'))
  1164         for line in result[1].splitlines():
  1165             if line != None and ip in line:
  1166                 parts = line.split()
  1167                 return parts[0]
  1168     @staticmethod
  1169     def getLogicalDrives():
  1170         drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
  1171         return list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
  1172     
  1173     @staticmethod
  1174     def getDriveType(drive):
  1175         return ctypes.cdll.kernel32.GetDriveTypeW(u"%s:\\"%drive)
  1176     
  1177     @staticmethod
  1178     def getVolumeInfo(drive):
  1179         volumeNameBuffer = ctypes.create_unicode_buffer(1024)
  1180         fileSystemNameBuffer = ctypes.create_unicode_buffer(1024)
  1181         serial_number = None
  1182         max_component_length = None
  1183         file_system_flags = None
  1184         
  1185         rc = ctypes.cdll.kernel32.GetVolumeInformationW(
  1186             #ctypes.c_wchar_p("F:\\"),
  1187             u"%s:\\"%drive,
  1188             volumeNameBuffer,
  1189             ctypes.sizeof(volumeNameBuffer),
  1190             serial_number,
  1191             max_component_length,
  1192             file_system_flags,
  1193             fileSystemNameBuffer,
  1194             ctypes.sizeof(fileSystemNameBuffer)
  1195         )
  1196         
  1197         return volumeNameBuffer.value, fileSystemNameBuffer.value
  1198 
  1199     @staticmethod
  1200     def stop():
  1201         """stop all running infinite loops now --> needed for gracefull shutdown"""
  1202         _running = False
  1203 
  1204 
  1205 
  1206 def checkResult(result):
  1207     if result[0] != 0:
  1208         logger.error('Command failed:' + ''.join(result[2]))
  1209         raise OpenSecurityException('Command failed:' + ''.join(result[2]))
  1210     return result
  1211 
  1212 # handles browsing request                    
  1213 class BrowsingHandler(threading.Thread):
  1214     vmm = None
  1215     def __init__(self, vmmanager):
  1216         threading.Thread.__init__(self)
  1217         self.vmm = vmmanager
  1218      
  1219     def run(self):
  1220         try:
  1221             new_sdvm = self.vmm.generateSDVMName()
  1222             self.vmm.createVM(new_sdvm)
  1223             self.vmm.storageAttach(new_sdvm)
  1224             self.vmm.genCertificateISO(new_sdvm)
  1225             self.vmm.attachCertificateISO(new_sdvm)
  1226             self.vmm.startVM(new_sdvm)
  1227             new_ip = self.vmm.waitStartup(new_sdvm)
  1228             drive = self.vmm.genNetworkDrive()
  1229             if new_ip != None:
  1230                 self.vmm.mapNetworkDrive(drive, '\\\\' + new_ip + '\\Download', None, None)
  1231             result = checkResult(Cygwin.sshExecuteX11('/usr/bin/iceweasel', new_ip, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + new_sdvm + '/dvm_key'))
  1232         except:
  1233             logger.error("BrowsingHandler failed. Cleaning up")
  1234             
  1235         self.vmm.unmapNetworkDrive(drive)
  1236         self.vmm.poweroffVM(new_sdvm)
  1237         self.vmm.removeVM(new_sdvm)
  1238                 
  1239 class DeviceHandler(threading.Thread): 
  1240     vmm = None
  1241     #handleDeviceChangeLock = threading.Lock()
  1242     attachedRSDs = None  
  1243     connectedRSDs = None
  1244     running = True
  1245     def __init__(self, vmmanger): 
  1246         threading.Thread.__init__(self)
  1247         self.vmm = vmmanger
  1248  
  1249     def stop(self):
  1250         self.running = False
  1251         
  1252     def run(self):
  1253         self.connectedRSDs = dict()
  1254         self.attachedRSDs = self.vmm.getAttachedRSDs()
  1255         while self.running:
  1256             tmp_rsds = self.vmm.getConnectedRSDS()
  1257             if tmp_rsds.keys() == self.connectedRSDs.keys():
  1258                 logger.debug("Nothing's changed. sleep(3)")
  1259                 time.sleep(3)
  1260                 continue
  1261             
  1262             logger.info("Something's changed")          
  1263             self.connectedRSDs = tmp_rsds
  1264             self.attachedRSDs = self.vmm.getAttachedRSDs()
  1265             
  1266             for vm_name in self.attachedRSDs.keys():
  1267                 if self.attachedRSDs[vm_name] not in self.connectedRSDs.values():
  1268                     drive = self.vmm.getNetworkDrive(vm_name)
  1269                     self.vmm.unmapNetworkDrive(drive)
  1270                     #self.stopVM(vm_name)
  1271                     self.vmm.detachRSD(vm_name)
  1272                     self.vmm.poweroffVM(vm_name)
  1273                     self.vmm.removeVM(vm_name)
  1274             #create new vm for attached device if any
  1275             self.attachedRSDs = self.vmm.getAttachedRSDs()
  1276             self.connectedRSDs = self.vmm.getConnectedRSDS()
  1277             
  1278             new_ip = None
  1279             for connected_device in self.connectedRSDs.values():
  1280                 if (self.attachedRSDs and False) or (connected_device not in self.attachedRSDs.values()):
  1281                     new_sdvm = self.vmm.generateSDVMName()
  1282                     self.vmm.createVM(new_sdvm)
  1283                     self.vmm.storageAttach(new_sdvm)
  1284                     self.vmm.attachRSD(new_sdvm, connected_device)
  1285                     self.vmm.startVM(new_sdvm)
  1286                     new_ip = self.vmm.waitStartup(new_sdvm)
  1287                     drive = self.vmm.genNetworkDrive()
  1288                     if new_ip != None:
  1289                         self.vmm.mapNetworkDrive(drive, '\\\\' + new_ip + '\\USB', None, None)
  1290 
  1291 if __name__ == '__main__':
  1292     #man = VMManager.getInstance()
  1293     #man.listVM()
  1294     #print man.getConnectedRSDs()
  1295     #print man.getNetworkDrives()
  1296     #man.genNetworkDrive()
  1297     #drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
  1298     #print list(itertools.compress(string.ascii_uppercase,  map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
  1299     #print list(map(chr, range(68, 91))) 
  1300     #print Cygwin.getRegEntry('SYSTEM\CurrentControlSet\Enum\USB', 'VID_1058&PID_0704')[0]
  1301     #devices = VMManager.getConnectedRSDS()
  1302     #print devices
  1303     
  1304     drives = VMManager.getLogicalDrives()
  1305     print drives
  1306     print VMManager.getDriveType("E")
  1307     print VMManager.getVolumeInfo("E")
  1308     #for device in devices.values():
  1309     #    #print device
  1310     #    if VMManager.isMassStorageDevice(device):
  1311     #        print device
  1312         
  1313     
  1314     
  1315     #time.sleep(-1)
  1316     #man.listVM()
  1317     #man.listVM()
  1318     #man.listVM()
  1319     #man.listVM()
  1320     #man.genCertificateISO('SecurityDVM0')
  1321     #man.guestExecute('SecurityDVM0', '/bin/ls -la')
  1322     #logger = setupLogger('VMManager')
  1323     #c = Cygwin()
  1324     
  1325     #man.sshExecute('/bin/ls -la', 'SecurityDVM0')
  1326     #man.sshExecuteX11('/usr/bin/iceweasel', 'SecurityDVM0')
  1327     #man.removeVM('SecurityDVM0')
  1328     #man.netUse('192.168.56.134', 'USB\\')
  1329     #ip = '192.168.56.139'
  1330     
  1331     #man.cygwin_path = 'c:\\cygwin64\\bin\\'
  1332     #man.handleDeviceChange()
  1333     #print man.listSDVM()
  1334     #man.configureHostNetworking()
  1335     #new_vm = man.generateSDVMName()
  1336     #man.createVM(new_vm)
  1337     
  1338     #print Cygwin.cmd()
  1339     #man.isAvailable('c:')
  1340     #ip = man.getHostOnlyIP('SecurityDVM0')
  1341     #man.mapNetworkDrive('h:', '\\\\' + ip + '\Download', None, None)
  1342     
  1343     #man.genCertificateISO(new_vm)
  1344     #man.attachCertificateISO(new_vm)
  1345     
  1346     #man.attachCertificateISO(vm_name)
  1347     #man.guestExecute(vm_name, "ls")
  1348     #man.sshGuestX11Execute('SecurityDVM1', '/usr/bin/iceweasel')
  1349     #time.sleep(60)
  1350     #print man.cygwinPath("C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\.ssh\*")
  1351     #man.genCertificateISO('SecurityDVM')
  1352     #man.attachCertificateISO('SecurityDVM')
  1353     #man.isStorageAttached('SecurityDVM')
  1354     #man.guestExecute('SecurityDVM', 'sudo apt-get -y update')
  1355     #man.guestExecute('SecurityDVM', 'sudo apt-get -y upgrade' )
  1356     
  1357     #man.stopVM('SecurityDVM')
  1358     #man.storageDetach('SecurityDVM')
  1359     #man.changeStorageType('C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\SecurityDVM.vmdk','immutable')
  1360     #man.storageAttach('SecurityDVM')
  1361     
  1362     
  1363     #cmd = "c:\\cygwin64\\bin\\bash.exe --login -c \"/bin/ls\""
  1364     #man.execute(cmd)
  1365 >>>>>>> other