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