1.1 --- a/OpenSecurity/bin/vmmanager.pyw Tue Mar 04 16:54:51 2014 +0100
1.2 +++ b/OpenSecurity/bin/vmmanager.pyw Fri Mar 07 14:32:12 2014 +0100
1.3 @@ -1,598 +1,672 @@
1.4 -'''
1.5 -Created on Nov 19, 2013
1.6 -
1.7 -@author: BarthaM
1.8 -'''
1.9 -import os
1.10 -import os.path
1.11 -from subprocess import Popen, PIPE, call, STARTUPINFO, _subprocess
1.12 -import sys
1.13 -import re
1.14 -
1.15 -from cygwin import Cygwin
1.16 -from environment import Environment
1.17 -import threading
1.18 -import time
1.19 -import string
1.20 -
1.21 -import shutil
1.22 -import stat
1.23 -import tempfile
1.24 -from opensecurity_util import logger, setupLogger, OpenSecurityException
1.25 -import ctypes
1.26 -import itertools
1.27 -DEBUG = True
1.28 -
1.29 -class VMManagerException(Exception):
1.30 - def __init__(self, value):
1.31 - self.value = value
1.32 - def __str__(self):
1.33 - return repr(self.value)
1.34 -
1.35 -class USBFilter:
1.36 - vendorid = ""
1.37 - productid = ""
1.38 - revision = ""
1.39 -
1.40 - def __init__(self, vendorid, productid, revision):
1.41 - self.vendorid = vendorid.lower()
1.42 - self.productid = productid.lower()
1.43 - self.revision = revision.lower()
1.44 - return
1.45 -
1.46 - def __eq__(self, other):
1.47 - return self.vendorid == other.vendorid and self.productid == other.productid and self.revision == other.revision
1.48 -
1.49 - def __hash__(self):
1.50 - return hash(self.vendorid) ^ hash(self.productid) ^ hash(self.revision)
1.51 -
1.52 - def __repr__(self):
1.53 - return "VendorId = \'" + str(self.vendorid) + "\' ProductId = \'" + str(self.productid) + "\' Revision = \'" + str(self.revision) + "\'"
1.54 -
1.55 -class VMManager(object):
1.56 - vmRootName = "SecurityDVM"
1.57 - systemProperties = None
1.58 - _instance = None
1.59 - machineFolder = ''
1.60 - rsdHandler = None
1.61 -
1.62 - def __init__(self):
1.63 - self.systemProperties = self.getSystemProperties()
1.64 - self.machineFolder = self.systemProperties["Default machine folder"]
1.65 - self.cleanup()
1.66 - self.rsdHandler = DeviceHandler(self)
1.67 - self.rsdHandler.start()
1.68 - return
1.69 -
1.70 - @staticmethod
1.71 - def getInstance():
1.72 - if VMManager._instance == None:
1.73 - VMManager._instance = VMManager()
1.74 - return VMManager._instance
1.75 -
1.76 - def cleanup(self):
1.77 - if self.rsdHandler != None:
1.78 - self.rsdHandler.stop()
1.79 - self.rsdHandler.join()
1.80 - drives = self.getNetworkDrives()
1.81 - for drive in drives.keys():
1.82 - self.unmapNetworkDrive(drive)
1.83 - for vm in self.listSDVM():
1.84 - self.poweroffVM(vm)
1.85 - self.removeVM(vm)
1.86 -
1.87 - # return hosty system properties
1.88 - def getSystemProperties(self):
1.89 - result = Cygwin.vboxExecute('list systemproperties')
1.90 - if result[1]=='':
1.91 - return None
1.92 - props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result[1].strip().splitlines()))
1.93 - #logger.debug(props)
1.94 - return props
1.95 -
1.96 - # return the folder containing the guest VMs
1.97 - def getMachineFolder(self):
1.98 - return self.machineFolder
1.99 -
1.100 - # list all existing VMs registered with VBox
1.101 - def listVM(self):
1.102 - result = Cygwin.vboxExecute('list vms')[1]
1.103 - vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
1.104 - return vms
1.105 -
1.106 - # list running VMs
1.107 - def listRunningVMS(self):
1.108 - result = Cygwin.vboxExecute('list runningvms')[1]
1.109 - vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
1.110 - return vms
1.111 -
1.112 - # list existing SDVMs
1.113 - def listSDVM(self):
1.114 - vms = self.listVM()
1.115 - svdms = []
1.116 - for vm in vms:
1.117 - if vm.startswith(self.vmRootName) and vm != self.vmRootName:
1.118 - svdms.append(vm)
1.119 - return svdms
1.120 -
1.121 - # generate valid (not already existing SDVM name). necessary for creating a new VM
1.122 - def generateSDVMName(self):
1.123 - vms = self.listVM()
1.124 - for i in range(0,999):
1.125 - if(not self.vmRootName+str(i) in vms):
1.126 - return self.vmRootName+str(i)
1.127 - return ''
1.128 -
1.129 - # return the RSDs connected to the host
1.130 - def getConnectedRSDS(self):
1.131 - results = Cygwin.vboxExecute('list usbhost')[1]
1.132 - results = results.split('Host USB Devices:')[1].strip()
1.133 -
1.134 - items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
1.135 - rsds = dict()
1.136 - for item in items:
1.137 - props = dict()
1.138 - for line in item.splitlines():
1.139 - if line != "":
1.140 - k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
1.141 - props[k] = v
1.142 -
1.143 - if 'Product' in props.keys() and props['Product'] == 'Mass Storage':
1.144 - usb_filter = USBFilter( re.search(r"\((?P<vid>[0-9A-Fa-f]+)\)", props['VendorId']).groupdict()['vid'],
1.145 - re.search(r"\((?P<pid>[0-9A-Fa-f]+)\)", props['ProductId']).groupdict()['pid'],
1.146 - re.search(r"\((?P<rev>[0-9A-Fa-f]+)\)", props['Revision']).groupdict()['rev'] )
1.147 - rsds[props['UUID']] = usb_filter;
1.148 - logger.debug(usb_filter)
1.149 - return rsds
1.150 -
1.151 - # return the RSDs attached to all existing SDVMs
1.152 - def getAttachedRSDs(self):
1.153 - vms = self.listSDVM()
1.154 - attached_devices = dict()
1.155 - for vm in vms:
1.156 - rsd_filter = self.getUSBFilter(vm)
1.157 - if rsd_filter != None:
1.158 - attached_devices[vm] = rsd_filter
1.159 - return attached_devices
1.160 -
1.161 - # configures hostonly networking and DHCP server. requires admin rights
1.162 - def configureHostNetworking(self):
1.163 - #cmd = 'vboxmanage list hostonlyifs'
1.164 - #Cygwin.vboxExecute(cmd)
1.165 - #cmd = 'vboxmanage hostonlyif remove \"VirtualBox Host-Only Ethernet Adapter\"'
1.166 - #Cygwin.vboxExecute(cmd)
1.167 - #cmd = 'vboxmanage hostonlyif create'
1.168 - #Cygwin.vboxExecute(cmd)
1.169 - Cygwin.vboxExecute('hostonlyif ipconfig \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.1 --netmask 255.255.255.0')
1.170 - #cmd = 'vboxmanage dhcpserver add'
1.171 - #Cygwin.vboxExecute(cmd)
1.172 - 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')
1.173 -
1.174 - #create new virtual machine instance based on template vm named SecurityDVM (\SecurityDVM\SecurityDVM.vmdk)
1.175 - def createVM(self, vm_name):
1.176 - hostonly_if = self.getHostOnlyIFs()
1.177 - Cygwin.vboxExecute('createvm --name ' + vm_name + ' --ostype Debian --register')
1.178 - Cygwin.vboxExecute('modifyvm ' + vm_name + ' --memory 512 --vram 10 --cpus 1 --usb on --usbehci on --nic1 hostonly --hostonlyadapter1 \"' + hostonly_if['Name'] + '\" --nic2 nat')
1.179 - Cygwin.vboxExecute('storagectl ' + vm_name + ' --name SATA --add sata --portcount 2')
1.180 - return
1.181 -
1.182 - # attach storage image to controller
1.183 - def storageAttach(self, vm_name):
1.184 - if self.isStorageAttached(vm_name):
1.185 - self.storageDetach(vm_name)
1.186 - Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium \"'+ self.machineFolder + '\SecurityDVM\SecurityDVM.vmdk\"')
1.187 - return
1.188 -
1.189 - # return true if storage is attached
1.190 - def isStorageAttached(self, vm_name):
1.191 - info = self.getVMInfo(vm_name)
1.192 - return (info['SATA-0-0']!='none')
1.193 -
1.194 - # detach storage from controller
1.195 - def storageDetach(self, vm_name):
1.196 - if self.isStorageAttached(vm_name):
1.197 - Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium none')
1.198 - return
1.199 -
1.200 - def changeStorageType(self, filename, storage_type):
1.201 - Cygwin.vboxExecute('modifyhd \"' + filename + '\" --type ' + storage_type)
1.202 - return
1.203 -
1.204 - # list storage snaphots for VM
1.205 - def updateTemplate(self):
1.206 - self.cleanup()
1.207 - self.poweroffVM('SecurityDVM')
1.208 - self.waitShutdown('SecurityDVM')
1.209 -
1.210 - # check for updates
1.211 - self.genCertificateISO('SecurityDVM')
1.212 - self.attachCertificateISO('SecurityDVM')
1.213 -
1.214 - self.storageDetach('SecurityDVM')
1.215 - results = Cygwin.vboxExecute('list hdds')[1]
1.216 - results = results.replace('Parent UUID', 'Parent')
1.217 - items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
1.218 -
1.219 - snaps = dict()
1.220 - for item in items:
1.221 - props = dict()
1.222 - for line in item.splitlines():
1.223 - if line != "":
1.224 - k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
1.225 - props[k] = v;
1.226 - snaps[props['UUID']] = props
1.227 -
1.228 -
1.229 - template_storage = self.machineFolder + '\SecurityDVM\SecurityDVM.vmdk'
1.230 -
1.231 - # find template uuid
1.232 - template_uuid = ''
1.233 - for hdd in snaps.values():
1.234 - if hdd['Location'] == template_storage:
1.235 - template_uuid = hdd['UUID']
1.236 - logger.debug('found parent uuid ' + template_uuid)
1.237 -
1.238 - # remove snapshots
1.239 - for hdd in snaps.values():
1.240 - if hdd['Parent'] == template_uuid:
1.241 - #template_uuid = hdd['UUID']
1.242 - logger.debug('removing snapshot ' + hdd['UUID'])
1.243 - results = Cygwin.vboxExecute('closemedium disk {' + hdd['UUID'] + '} --delete')[1]
1.244 - # parse result 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
1.245 -
1.246 - self.changeStorageType(template_storage,'normal')
1.247 - self.storageAttach('SecurityDVM')
1.248 - self.startVM('SecurityDVM')
1.249 - self.waitStartup('SecurityDVM')
1.250 - Cygwin.sshExecute('"sudo apt-get -y update"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key' )
1.251 - Cygwin.sshExecute('"sudo apt-get -y upgrade"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key' )
1.252 - #self.stopVM('SecurityDVM')
1.253 - self.hibernateVM('SecurityDVM')
1.254 - self.waitShutdown('SecurityDVM')
1.255 - self.storageDetach('SecurityDVM')
1.256 - self.changeStorageType(template_storage,'immutable')
1.257 - self.storageAttach('SecurityDVM')
1.258 - self.rsdHandler = DeviceHandler(self)
1.259 - self.rsdHandler.start()
1.260 -
1.261 - #remove VM from the system. should be used on VMs returned by listSDVMs
1.262 - def removeVM(self, vm_name):
1.263 - logger.info('Removing ' + vm_name)
1.264 - Cygwin.vboxExecute('unregistervm ' + vm_name + ' --delete')
1.265 - machineFolder = Cygwin.cygPath(self.machineFolder)
1.266 - Cygwin.bashExecute('"/usr/bin/rm -rf ' + machineFolder + '/' + vm_name + '"')
1.267 -
1.268 - # start VM
1.269 - def startVM(self, vm_name):
1.270 - logger.info('Starting ' + vm_name)
1.271 - result = Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless' )
1.272 - while not string.find(str(result), 'successfully started',):
1.273 - logger.error("Failed to start SDVM: " + vm_name + " retrying")
1.274 - time.sleep(1)
1.275 - result = Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless')
1.276 - return result[0]
1.277 -
1.278 - # return wether VM is running or not
1.279 - def isVMRunning(self, vm_name):
1.280 - return vm_name in self.listRunningVMS()
1.281 -
1.282 - # stop VM
1.283 - def stopVM(self, vm_name):
1.284 - logger.info('Sending shutdown signal to ' + vm_name)
1.285 - Cygwin.sshExecute( '"sudo shutdown -h now"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key' )
1.286 -
1.287 - # stop VM
1.288 - def hibernateVM(self, vm_name):
1.289 - logger.info('Sending shutdown signal to ' + vm_name)
1.290 - Cygwin.sshExecute( '"sudo hibernate-disk&"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key', wait_return=False )
1.291 -
1.292 - # poweroff VM
1.293 - def poweroffVM(self, vm_name):
1.294 - if not self.isVMRunning(vm_name):
1.295 - return
1.296 - logger.info('Powering off ' + vm_name)
1.297 - return Cygwin.vboxExecute('controlvm ' + vm_name + ' poweroff')
1.298 -
1.299 - #list the hostonly IFs exposed by the VBox host
1.300 - @staticmethod
1.301 - def getHostOnlyIFs():
1.302 - result = Cygwin.vboxExecute('list hostonlyifs')[1]
1.303 - if result=='':
1.304 - return None
1.305 - props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result.strip().splitlines()))
1.306 - return props
1.307 -
1.308 - # return the hostOnly IP for a running guest or the host
1.309 - @staticmethod
1.310 - def getHostOnlyIP(vm_name):
1.311 - if vm_name == None:
1.312 - logger.info('Gettting hostOnly IP address for Host')
1.313 - return VMManager.getHostOnlyIFs()['IPAddress']
1.314 - else:
1.315 - logger.info('Gettting hostOnly IP address ' + vm_name)
1.316 - result = Cygwin.vboxExecute('guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP')
1.317 - if result=='':
1.318 - return None
1.319 - result = result[1]
1.320 - if result.startswith('No value set!'):
1.321 - return None
1.322 - return result[result.index(':')+1:].strip()
1.323 -
1.324 - # attach removable storage device to VM by provision of filter
1.325 - def attachRSD(self, vm_name, rsd_filter):
1.326 - return Cygwin.vboxExecute('usbfilter add 0 --target ' + vm_name + ' --name OpenSecurityRSD --vendorid ' + rsd_filter.vendorid + ' --productid ' + rsd_filter.productid + ' --revision ' + rsd_filter.revision)
1.327 -
1.328 - # detach removable storage from VM by
1.329 - def detachRSD(self, vm_name):
1.330 - return Cygwin.vboxExecute('usbfilter remove 0 --target ' + vm_name )
1.331 -
1.332 -
1.333 - # return the description set for an existing VM
1.334 - def getVMInfo(self, vm_name):
1.335 - results = Cygwin.vboxExecute('showvminfo ' + vm_name + ' --machinereadable')[1]
1.336 - props = dict((k.strip().strip('"'),v.strip().strip('"')) for k,v in (line.split('=', 1) for line in results.splitlines()))
1.337 - #logger.debug(props)
1.338 - return props
1.339 -
1.340 - # return the configured USB filter for an existing VM
1.341 - def getUSBFilter(self, vm_name):
1.342 - props = self.getVMInfo(vm_name)
1.343 - keys = set(['USBFilterVendorId1', 'USBFilterProductId1', 'USBFilterRevision1'])
1.344 - keyset = set(props.keys())
1.345 - usb_filter = None
1.346 - if keyset.issuperset(keys):
1.347 - usb_filter = USBFilter(props['USBFilterVendorId1'], props['USBFilterProductId1'], props['USBFilterRevision1'])
1.348 - return usb_filter
1.349 -
1.350 - #generates ISO containing authorized_keys for use with guest VM
1.351 - def genCertificateISO(self, vm_name):
1.352 - machineFolder = Cygwin.cygPath(self.machineFolder)
1.353 - # remove .ssh folder if exists
1.354 - cmd = '\"/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"'
1.355 - Cygwin.bashExecute(cmd)
1.356 - # remove .ssh folder if exists
1.357 - Cygwin.bashExecute('\"/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"\"')
1.358 - # create .ssh folder in vm_name
1.359 - Cygwin.bashExecute('\"/usr/bin/mkdir -p \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"')
1.360 - # generate dvm_key pair in vm_name / .ssh
1.361 - Cygwin.bashExecute('\"/usr/bin/ssh-keygen -q -t rsa -N \\"\\" -C \\\"' + vm_name + '\\\" -f \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\"\"')
1.362 - # move out private key
1.363 - Cygwin.bashExecute('\"/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\" \\\"' + machineFolder + '/' + vm_name + '\\\"')
1.364 - # set permissions for private key
1.365 - Cygwin.bashExecute('\"/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"\"')
1.366 - # rename public key to authorized_keys
1.367 - Cygwin.bashExecute('\"/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key.pub\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"')
1.368 - # set permissions for authorized_keys
1.369 - Cygwin.bashExecute('\"/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"\"')
1.370 - # generate iso image with .ssh/authorized keys
1.371 - Cygwin.bashExecute('\"/usr/bin/genisoimage -J -R -o \\\"' + machineFolder + '/' + vm_name + '/'+ vm_name + '.iso\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"')
1.372 -
1.373 - # attaches generated ssh public cert to guest vm
1.374 - def attachCertificateISO(self, vm_name):
1.375 - result = Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 1 --device 0 --type dvddrive --mtype readonly --medium \"' + self.machineFolder + '\\' + vm_name + '\\'+ vm_name + '.iso\"')
1.376 - return result
1.377 -
1.378 - # wait for machine to come up
1.379 - def waitStartup(self, vm_name, timeout_ms = 30000):
1.380 - result = Cygwin.vboxExecute('guestproperty wait ' + vm_name + ' SDVMStarted --timeout ' + str(timeout_ms) + ' --fail-on-timeout')
1.381 - return VMManager.getHostOnlyIP(vm_name)
1.382 -
1.383 - # wait for machine to shutdown
1.384 - def waitShutdown(self, vm_name):
1.385 - while vm_name in self.listRunningVMS():
1.386 - time.sleep(1)
1.387 - return
1.388 -
1.389 - # handles browsing request
1.390 - def handleBrowsingRequest(self):
1.391 - if VMManager.handleDeviceChangeLock.acquire(True):
1.392 - new_sdvm = self.generateSDVMName()
1.393 - self.createVM(new_sdvm)
1.394 - self.storageAttach(new_sdvm)
1.395 - self.genCertificateISO(new_sdvm)
1.396 - self.attachCertificateISO(new_sdvm)
1.397 - self.startVM(new_sdvm)
1.398 - new_ip = self.waitStartup(new_sdvm)
1.399 - if new_ip != None:
1.400 - self.mapNetworkDrive('g:', '\\\\' + new_ip + '\\Download', None, None)
1.401 - #TODO: cleanup notifications somwhere else (eg. machine shutdown)
1.402 - VMManager.handleDeviceChangeLock.release()
1.403 - return new_sdvm
1.404 -
1.405 - #Small function to check the availability of network resource.
1.406 - #def isAvailable(self, path):
1.407 - #return os.path.exists(path)
1.408 - #result = Cygwin.cmdExecute('IF EXIST "' + path + '" echo YES')
1.409 - #return string.find(result[1], 'YES',)
1.410 -
1.411 - #Small function to check if the mention location is a directory
1.412 - def isDirectory(self, path):
1.413 - result = Cygwin.cmdExecute('dir ' + path + ' | FIND ".."')
1.414 - return string.find(result[1], 'DIR',)
1.415 -
1.416 - def mapNetworkDrive(self, drive, networkPath, user, password):
1.417 - self.unmapNetworkDrive(drive)
1.418 - #Check for drive availability
1.419 - if os.path.exists(drive):
1.420 - logger.error("Drive letter is already in use: " + drive)
1.421 - return -1
1.422 - #Check for network resource availability
1.423 - while not os.path.exists(networkPath):
1.424 - time.sleep(1)
1.425 - logger.info("Path not accessible: " + networkPath + " retrying")
1.426 - #return -1
1.427 -
1.428 - command = 'USE ' + drive + ' ' + networkPath + ' /PERSISTENT:NO'
1.429 - if user != None:
1.430 - command += ' ' + password + ' /User' + user
1.431 -
1.432 - #TODO: Execute 'NET USE' command with authentication
1.433 - result = Cygwin.execute('C:\\Windows\\system32\\net.exe', command)
1.434 - if string.find(result[1], 'successfully',) == -1:
1.435 - logger.error("Failed: NET " + command)
1.436 - return -1
1.437 - return 1
1.438 -
1.439 - def unmapNetworkDrive(self, drive):
1.440 - drives = self.getNetworkDrives()
1.441 - if drive not in drives.keys():
1.442 - return 1
1.443 - result = Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE ' + drive + ' /DELETE /YES')
1.444 - if string.find(str(result[1]), 'successfully',) == -1:
1.445 - logger.error(result[2])
1.446 - return -1
1.447 - return 1
1.448 -
1.449 - def getNetworkDrives(self):
1.450 - ip = VMManager.getHostOnlyIP(None)
1.451 - ip = ip[:ip.rindex('.')]
1.452 - drives = dict()
1.453 - result = Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE')
1.454 - for line in result[1].splitlines():
1.455 - if ip in line:
1.456 - parts = line.split()
1.457 - drives[parts[1]] = parts[2]
1.458 - return drives
1.459 -
1.460 - def genNetworkDrive(self):
1.461 - network_drives = self.getNetworkDrives()
1.462 - logical_drives = self.getLogicalDrives()
1.463 - drives = list(map(chr, range(68, 91)))
1.464 - for drive in drives:
1.465 - if drive+':' not in network_drives and drive not in logical_drives:
1.466 - return drive+':'
1.467 -
1.468 - def getNetworkDrive(self, vm_name):
1.469 - ip = self.getHostOnlyIP(vm_name)
1.470 - result = Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE')
1.471 - for line in result[1].splitlines():
1.472 - if ip in line:
1.473 - parts = line.split()
1.474 - return parts[0]
1.475 -
1.476 - def getLogicalDrives(self):
1.477 - drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
1.478 - return list(itertools.compress(string.ascii_uppercase, map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
1.479 -
1.480 - #vms = self.listSDVM()
1.481 - #for vm in vms:
1.482 - # ip = self.getHostOnlyIP(vm)
1.483 -
1.484 -class DeviceHandler(threading.Thread):
1.485 - vmm = None
1.486 - #handleDeviceChangeLock = threading.Lock()
1.487 - attachedRSDs = None
1.488 - connectedRSDs = None
1.489 - running = True
1.490 - def __init__(self, vmmanger):
1.491 - threading.Thread.__init__(self)
1.492 - self.vmm = vmmanger
1.493 -
1.494 - def stop(self):
1.495 - self.running = False
1.496 -
1.497 - def run(self):
1.498 - self.connectedRSDs = dict()#self.vmm.getConnectedRSDS()
1.499 - self.attachedRSDs = self.vmm.getAttachedRSDs()
1.500 - while self.running:
1.501 - tmp_rsds = self.vmm.getConnectedRSDS()
1.502 - if tmp_rsds.keys() == self.connectedRSDs.keys():
1.503 - logger.debug("Nothing's changed. sleep(3)")
1.504 - time.sleep(3)
1.505 - continue
1.506 -
1.507 - logger.info("Something's changed")
1.508 -
1.509 - self.connectedRSDs = tmp_rsds
1.510 - self.attachedRSDs = self.vmm.getAttachedRSDs()
1.511 -
1.512 -
1.513 - for vm_name in self.attachedRSDs.keys():
1.514 - if self.attachedRSDs[vm_name] not in self.connectedRSDs.values():
1.515 - drive = self.vmm.getNetworkDrive(vm_name)
1.516 - self.vmm.unmapNetworkDrive(drive)
1.517 - #self.stopVM(vm_name)
1.518 - self.vmm.detachRSD(vm_name)
1.519 - self.vmm.poweroffVM(vm_name)
1.520 - self.vmm.removeVM(vm_name)
1.521 - #create new vm for attached device if any
1.522 - self.attachedRSDs = self.vmm.getAttachedRSDs()
1.523 - self.connectedRSDs = self.vmm.getConnectedRSDS()
1.524 -
1.525 - new_ip = None
1.526 - for connected_device in self.connectedRSDs.values():
1.527 - if (self.attachedRSDs and False) or (connected_device not in self.attachedRSDs.values()):
1.528 - new_sdvm = self.vmm.generateSDVMName()
1.529 - self.vmm.createVM(new_sdvm)
1.530 - self.vmm.storageAttach(new_sdvm)
1.531 - self.vmm.attachRSD(new_sdvm, connected_device)
1.532 - self.vmm.startVM(new_sdvm)
1.533 - new_ip = self.vmm.waitStartup(new_sdvm)
1.534 - drive = self.vmm.genNetworkDrive()
1.535 - if new_ip != None:
1.536 - self.vmm.mapNetworkDrive(drive, '\\\\' + new_ip + '\\USB', None, None)
1.537 - #TODO: cleanup notifications somwhere else (eg. machine shutdown)
1.538 - #self.handleDeviceChangeLock.release()
1.539 -
1.540 -
1.541 -if __name__ == '__main__':
1.542 - #man = VMManager.getInstance()
1.543 - #man.listVM()
1.544 - #print man.getConnectedRSDs()
1.545 - #print man.getNetworkDrives()
1.546 - #man.genNetworkDrive()
1.547 - drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
1.548 - print list(itertools.compress(string.ascii_uppercase, map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
1.549 - #print list(map(chr, range(68, 91)))
1.550 -
1.551 - #time.sleep(-1)
1.552 - #man.listVM()
1.553 - #man.listVM()
1.554 - #man.listVM()
1.555 - #man.listVM()
1.556 - #man.genCertificateISO('SecurityDVM0')
1.557 - #man.guestExecute('SecurityDVM0', '/bin/ls -la')
1.558 - #logger = setupLogger('VMManager')
1.559 - #c = Cygwin()
1.560 -
1.561 - #man.sshExecute('/bin/ls -la', 'SecurityDVM0')
1.562 - #man.sshExecuteX11('/usr/bin/iceweasel', 'SecurityDVM0')
1.563 - #man.removeVM('SecurityDVM0')
1.564 - #man.netUse('192.168.56.134', 'USB\\')
1.565 - #ip = '192.168.56.139'
1.566 -
1.567 - #man.cygwin_path = 'c:\\cygwin64\\bin\\'
1.568 - #man.handleDeviceChange()
1.569 - #print man.listSDVM()
1.570 - #man.configureHostNetworking()
1.571 - #new_vm = man.generateSDVMName()
1.572 - #man.createVM(new_vm)
1.573 -
1.574 - #print Cygwin.cmd()
1.575 - #man.isAvailable('c:')
1.576 - #ip = man.getHostOnlyIP('SecurityDVM0')
1.577 - #man.mapNetworkDrive('h:', '\\\\' + ip + '\Download', None, None)
1.578 -
1.579 - #man.genCertificateISO(new_vm)
1.580 - #man.attachCertificateISO(new_vm)
1.581 -
1.582 - #man.attachCertificateISO(vm_name)
1.583 - #man.guestExecute(vm_name, "ls")
1.584 - #man.sshGuestX11Execute('SecurityDVM1', '/usr/bin/iceweasel')
1.585 - #time.sleep(60)
1.586 - #print man.cygwinPath("C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\.ssh\*")
1.587 - #man.genCertificateISO('SecurityDVM')
1.588 - #man.attachCertificateISO('SecurityDVM')
1.589 - #man.isStorageAttached('SecurityDVM')
1.590 - #man.guestExecute('SecurityDVM', 'sudo apt-get -y update')
1.591 - #man.guestExecute('SecurityDVM', 'sudo apt-get -y upgrade' )
1.592 -
1.593 - #man.stopVM('SecurityDVM')
1.594 - #man.storageDetach('SecurityDVM')
1.595 - #man.changeStorageType('C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\SecurityDVM.vmdk','immutable')
1.596 - #man.storageAttach('SecurityDVM')
1.597 -
1.598 -
1.599 - #cmd = "c:\\cygwin64\\bin\\bash.exe --login -c \"/bin/ls\""
1.600 - #man.execute(cmd)
1.601 -
1.602 +'''
1.603 +Created on Nov 19, 2013
1.604 +
1.605 +@author: BarthaM
1.606 +'''
1.607 +import os
1.608 +import os.path
1.609 +from subprocess import Popen, PIPE, call, STARTUPINFO, _subprocess
1.610 +import sys
1.611 +import re
1.612 +
1.613 +from cygwin import Cygwin
1.614 +from environment import Environment
1.615 +import threading
1.616 +import time
1.617 +import string
1.618 +
1.619 +import shutil
1.620 +import stat
1.621 +import tempfile
1.622 +from opensecurity_util import logger, setupLogger, OpenSecurityException
1.623 +import ctypes
1.624 +import itertools
1.625 +import _winreg
1.626 +DEBUG = True
1.627 +
1.628 +class VMManagerException(Exception):
1.629 + def __init__(self, value):
1.630 + self.value = value
1.631 + def __str__(self):
1.632 + return repr(self.value)
1.633 +
1.634 +class USBFilter:
1.635 + vendorid = ""
1.636 + productid = ""
1.637 + revision = ""
1.638 +
1.639 + def __init__(self, vendorid, productid, revision):
1.640 + self.vendorid = vendorid.lower()
1.641 + self.productid = productid.lower()
1.642 + self.revision = revision.lower()
1.643 + return
1.644 +
1.645 + def __eq__(self, other):
1.646 + return self.vendorid == other.vendorid and self.productid == other.productid and self.revision == other.revision
1.647 +
1.648 + def __hash__(self):
1.649 + return hash(self.vendorid) ^ hash(self.productid) ^ hash(self.revision)
1.650 +
1.651 + def __repr__(self):
1.652 + return "VendorId = \'" + str(self.vendorid) + "\' ProductId = \'" + str(self.productid) + "\' Revision = \'" + str(self.revision) + "\'"
1.653 +
1.654 + #def __getitem__(self, item):
1.655 + # return self.coords[item]
1.656 +
1.657 +class VMManager(object):
1.658 + vmRootName = "SecurityDVM"
1.659 + systemProperties = None
1.660 + _instance = None
1.661 + machineFolder = ''
1.662 + rsdHandler = None
1.663 +
1.664 + def __init__(self):
1.665 + self.systemProperties = self.getSystemProperties()
1.666 + self.machineFolder = self.systemProperties["Default machine folder"]
1.667 + self.cleanup()
1.668 + self.rsdHandler = DeviceHandler(self)
1.669 + self.rsdHandler.start()
1.670 + return
1.671 +
1.672 + @staticmethod
1.673 + def getInstance():
1.674 + if VMManager._instance == None:
1.675 + VMManager._instance = VMManager()
1.676 + return VMManager._instance
1.677 +
1.678 + def cleanup(self):
1.679 + if self.rsdHandler != None:
1.680 + self.rsdHandler.stop()
1.681 + self.rsdHandler.join()
1.682 + drives = self.getNetworkDrives()
1.683 + for drive in drives.keys():
1.684 + self.unmapNetworkDrive(drive)
1.685 + for vm in self.listSDVM():
1.686 + self.poweroffVM(vm)
1.687 + self.removeVM(vm)
1.688 +
1.689 + # return hosty system properties
1.690 + def getSystemProperties(self):
1.691 + result = checkResult(Cygwin.vboxExecute('list systemproperties'))
1.692 + if result[1]=='':
1.693 + return None
1.694 + props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result[1].strip().splitlines()))
1.695 + return props
1.696 +
1.697 + # return the folder containing the guest VMs
1.698 + def getMachineFolder(self):
1.699 + return self.machineFolder
1.700 +
1.701 + # list all existing VMs registered with VBox
1.702 + def listVM(self):
1.703 + result = checkResult(Cygwin.vboxExecute('list vms'))[1]
1.704 + vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
1.705 + return vms
1.706 +
1.707 + # list running VMs
1.708 + def listRunningVMS(self):
1.709 + result = checkResult(Cygwin.vboxExecute('list runningvms'))[1]
1.710 + vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
1.711 + return vms
1.712 +
1.713 + # list existing SDVMs
1.714 + def listSDVM(self):
1.715 + vms = self.listVM()
1.716 + svdms = []
1.717 + for vm in vms:
1.718 + if vm.startswith(self.vmRootName) and vm != self.vmRootName:
1.719 + svdms.append(vm)
1.720 + return svdms
1.721 +
1.722 + # generate valid (not already existing SDVM name). necessary for creating a new VM
1.723 + def generateSDVMName(self):
1.724 + vms = self.listVM()
1.725 + for i in range(0,999):
1.726 + if(not self.vmRootName+str(i) in vms):
1.727 + return self.vmRootName+str(i)
1.728 + return ''
1.729 +
1.730 + # check if the device is mass storage type
1.731 + @staticmethod
1.732 + def isMassStorageDevice(device):
1.733 + keyname = 'SYSTEM\CurrentControlSet\Enum\USB' + '\VID_' + device.vendorid+'&'+'PID_'+ device.productid
1.734 + key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keyname)
1.735 + #subkeys = _winreg.QueryInfoKey(key)[0]
1.736 + #for i in range(0, subkeys):
1.737 + # print _winreg.EnumKey(key, i)
1.738 + devinfokeyname = _winreg.EnumKey(key, 0)
1.739 + _winreg.CloseKey(key)
1.740 +
1.741 + devinfokey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keyname+'\\'+devinfokeyname)
1.742 + value = _winreg.QueryValueEx(devinfokey, 'SERVICE')[0]
1.743 + _winreg.CloseKey(devinfokey)
1.744 +
1.745 + return 'USBSTOR' in value
1.746 +
1.747 + # return the RSDs connected to the host
1.748 + @staticmethod
1.749 + def getConnectedRSDS():
1.750 + results = checkResult(Cygwin.vboxExecute('list usbhost'))[1]
1.751 + results = results.split('Host USB Devices:')[1].strip()
1.752 +
1.753 + items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
1.754 + rsds = dict()
1.755 + for item in items:
1.756 + props = dict()
1.757 + for line in item.splitlines():
1.758 + if line != "":
1.759 + k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
1.760 + props[k] = v
1.761 +
1.762 + #if 'Product' in props.keys() and props['Product'] == 'Mass Storage':
1.763 +
1.764 + usb_filter = USBFilter( re.search(r"\((?P<vid>[0-9A-Fa-f]+)\)", props['VendorId']).groupdict()['vid'],
1.765 + re.search(r"\((?P<pid>[0-9A-Fa-f]+)\)", props['ProductId']).groupdict()['pid'],
1.766 + re.search(r"\((?P<rev>[0-9A-Fa-f]+)\)", props['Revision']).groupdict()['rev'] )
1.767 + if VMManager.isMassStorageDevice(usb_filter):
1.768 + rsds[props['UUID']] = usb_filter;
1.769 + logger.debug(usb_filter)
1.770 + return rsds
1.771 +
1.772 + # return the RSDs attached to all existing SDVMs
1.773 + def getAttachedRSDs(self):
1.774 + vms = self.listSDVM()
1.775 + attached_devices = dict()
1.776 + for vm in vms:
1.777 + rsd_filter = self.getUSBFilter(vm)
1.778 + if rsd_filter != None:
1.779 + attached_devices[vm] = rsd_filter
1.780 + return attached_devices
1.781 +
1.782 + # configures hostonly networking and DHCP server. requires admin rights
1.783 + def configureHostNetworking(self):
1.784 + #cmd = 'vboxmanage list hostonlyifs'
1.785 + #Cygwin.vboxExecute(cmd)
1.786 + #cmd = 'vboxmanage hostonlyif remove \"VirtualBox Host-Only Ethernet Adapter\"'
1.787 + #Cygwin.vboxExecute(cmd)
1.788 + #cmd = 'vboxmanage hostonlyif create'
1.789 + #Cygwin.vboxExecute(cmd)
1.790 + checkResult(Cygwin.vboxExecute('hostonlyif ipconfig \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.1 --netmask 255.255.255.0'))
1.791 + #cmd = 'vboxmanage dhcpserver add'
1.792 + #Cygwin.vboxExecute(cmd)
1.793 + 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'))
1.794 +
1.795 + #create new virtual machine instance based on template vm named SecurityDVM (\SecurityDVM\SecurityDVM.vmdk)
1.796 + def createVM(self, vm_name):
1.797 + hostonly_if = self.getHostOnlyIFs()
1.798 + checkResult(Cygwin.vboxExecute('createvm --name ' + vm_name + ' --ostype Debian --register'))
1.799 + checkResult(Cygwin.vboxExecute('modifyvm ' + vm_name + ' --memory 512 --vram 10 --cpus 1 --usb on --usbehci on --nic1 hostonly --hostonlyadapter1 \"' + hostonly_if['Name'] + '\" --nic2 nat'))
1.800 + checkResult(Cygwin.vboxExecute('storagectl ' + vm_name + ' --name SATA --add sata --portcount 2'))
1.801 + return
1.802 +
1.803 + # attach storage image to controller
1.804 + def storageAttach(self, vm_name):
1.805 + if self.isStorageAttached(vm_name):
1.806 + self.storageDetach(vm_name)
1.807 + checkResult(Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium \"'+ self.machineFolder + '\SecurityDVM\SecurityDVM.vmdk\"'))
1.808 +
1.809 + # return true if storage is attached
1.810 + def isStorageAttached(self, vm_name):
1.811 + info = self.getVMInfo(vm_name)
1.812 + return (info['SATA-0-0']!='none')
1.813 +
1.814 + # detach storage from controller
1.815 + def storageDetach(self, vm_name):
1.816 + if self.isStorageAttached(vm_name):
1.817 + checkResult(Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 0 --device 0 --type hdd --medium none'))
1.818 +
1.819 + def changeStorageType(self, filename, storage_type):
1.820 + checkResult(Cygwin.vboxExecute('modifyhd \"' + filename + '\" --type ' + storage_type))
1.821 +
1.822 + # list storage snaphots for VM
1.823 + def updateTemplate(self):
1.824 + self.cleanup()
1.825 + self.poweroffVM('SecurityDVM')
1.826 + self.waitShutdown('SecurityDVM')
1.827 +
1.828 + # check for updates
1.829 + self.genCertificateISO('SecurityDVM')
1.830 + self.attachCertificateISO('SecurityDVM')
1.831 +
1.832 + self.storageDetach('SecurityDVM')
1.833 + results = checkResult(Cygwin.vboxExecute('list hdds'))[1]
1.834 + results = results.replace('Parent UUID', 'Parent')
1.835 + items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
1.836 +
1.837 + snaps = dict()
1.838 + for item in items:
1.839 + props = dict()
1.840 + for line in item.splitlines():
1.841 + if line != "":
1.842 + k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
1.843 + props[k] = v;
1.844 + snaps[props['UUID']] = props
1.845 +
1.846 +
1.847 + template_storage = self.machineFolder + '\SecurityDVM\SecurityDVM.vmdk'
1.848 +
1.849 + # find template uuid
1.850 + template_uuid = ''
1.851 + for hdd in snaps.values():
1.852 + if hdd['Location'] == template_storage:
1.853 + template_uuid = hdd['UUID']
1.854 + logger.debug('found parent uuid ' + template_uuid)
1.855 +
1.856 + # remove snapshots
1.857 + for hdd in snaps.values():
1.858 + if hdd['Parent'] == template_uuid:
1.859 + #template_uuid = hdd['UUID']
1.860 + logger.debug('removing snapshot ' + hdd['UUID'])
1.861 + checkResult(Cygwin.vboxExecute('closemedium disk {' + hdd['UUID'] + '} --delete'))#[1]
1.862 + # parse result 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100%
1.863 +
1.864 + self.changeStorageType(template_storage,'normal')
1.865 + self.storageAttach('SecurityDVM')
1.866 + self.startVM('SecurityDVM')
1.867 + self.waitStartup('SecurityDVM')
1.868 + checkResult(Cygwin.sshExecute('"sudo apt-get -y update"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key'))
1.869 + checkResult(Cygwin.sshExecute('"sudo apt-get -y upgrade"', VMManager.getHostOnlyIP('SecurityDVM'), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + 'SecurityDVM' + '/dvm_key'))
1.870 + #self.stopVM('SecurityDVM')
1.871 + self.hibernateVM('SecurityDVM')
1.872 + self.waitShutdown('SecurityDVM')
1.873 + self.storageDetach('SecurityDVM')
1.874 + self.changeStorageType(template_storage,'immutable')
1.875 + self.storageAttach('SecurityDVM')
1.876 + self.rsdHandler = DeviceHandler(self)
1.877 + self.rsdHandler.start()
1.878 +
1.879 + #remove VM from the system. should be used on VMs returned by listSDVMs
1.880 + def removeVM(self, vm_name):
1.881 + logger.info('Removing ' + vm_name)
1.882 + checkResult(Cygwin.vboxExecute('unregistervm ' + vm_name + ' --delete'))
1.883 + machineFolder = Cygwin.cygPath(self.machineFolder)
1.884 + checkResult(Cygwin.bashExecute('"/usr/bin/rm -rf ' + machineFolder + '/' + vm_name + '"'))
1.885 +
1.886 + # start VM
1.887 + def startVM(self, vm_name):
1.888 + logger.info('Starting ' + vm_name)
1.889 + result = checkResult(Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless' ))
1.890 + while 'successfully started' not in result[1]:
1.891 + logger.error("Failed to start SDVM: " + vm_name + " retrying")
1.892 + time.sleep(1)
1.893 + result = checkResult(Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless'))
1.894 + return result[0]
1.895 +
1.896 + # return wether VM is running or not
1.897 + def isVMRunning(self, vm_name):
1.898 + return vm_name in self.listRunningVMS()
1.899 +
1.900 + # stop VM
1.901 + def stopVM(self, vm_name):
1.902 + logger.info('Sending shutdown signal to ' + vm_name)
1.903 + checkResult(Cygwin.sshExecute( '"sudo shutdown -h now"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key' ))
1.904 +
1.905 + # stop VM
1.906 + def hibernateVM(self, vm_name):
1.907 + logger.info('Sending shutdown signal to ' + vm_name)
1.908 + checkResult(Cygwin.sshExecute( '"sudo hibernate-disk&"', VMManager.getHostOnlyIP(vm_name), 'osecuser', Cygwin.cygPath(self.machineFolder) + '/' + vm_name + '/dvm_key', wait_return=False))
1.909 +
1.910 + # poweroff VM
1.911 + def poweroffVM(self, vm_name):
1.912 + if not self.isVMRunning(vm_name):
1.913 + return
1.914 + logger.info('Powering off ' + vm_name)
1.915 + return checkResult(Cygwin.vboxExecute('controlvm ' + vm_name + ' poweroff'))
1.916 +
1.917 + #list the hostonly IFs exposed by the VBox host
1.918 + @staticmethod
1.919 + def getHostOnlyIFs():
1.920 + result = Cygwin.vboxExecute('list hostonlyifs')[1]
1.921 + if result=='':
1.922 + return None
1.923 + props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result.strip().splitlines()))
1.924 + return props
1.925 +
1.926 + # return the hostOnly IP for a running guest or the host
1.927 + @staticmethod
1.928 + def getHostOnlyIP(vm_name):
1.929 + if vm_name == None:
1.930 + logger.info('Gettting hostOnly IP address for Host')
1.931 + return VMManager.getHostOnlyIFs()['IPAddress']
1.932 + else:
1.933 + logger.info('Gettting hostOnly IP address ' + vm_name)
1.934 + result = checkResult(Cygwin.vboxExecute('guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP'))
1.935 + if result=='':
1.936 + return None
1.937 + result = result[1]
1.938 + if result.startswith('No value set!'):
1.939 + return None
1.940 + return result[result.index(':')+1:].strip()
1.941 +
1.942 + # attach removable storage device to VM by provision of filter
1.943 + def attachRSD(self, vm_name, rsd_filter):
1.944 + return checkResult(Cygwin.vboxExecute('usbfilter add 0 --target ' + vm_name + ' --name OpenSecurityRSD --vendorid ' + rsd_filter.vendorid + ' --productid ' + rsd_filter.productid + ' --revision ' + rsd_filter.revision))
1.945 +
1.946 + # detach removable storage from VM by
1.947 + def detachRSD(self, vm_name):
1.948 + return checkResult(Cygwin.vboxExecute('usbfilter remove 0 --target ' + vm_name))
1.949 +
1.950 + # return the description set for an existing VM
1.951 + def getVMInfo(self, vm_name):
1.952 + results = checkResult(Cygwin.vboxExecute('showvminfo ' + vm_name + ' --machinereadable'))[1]
1.953 + props = dict((k.strip().strip('"'),v.strip().strip('"')) for k,v in (line.split('=', 1) for line in results.splitlines()))
1.954 + return props
1.955 +
1.956 + # return the configured USB filter for an existing VM
1.957 + def getUSBFilter(self, vm_name):
1.958 + props = self.getVMInfo(vm_name)
1.959 + keys = set(['USBFilterVendorId1', 'USBFilterProductId1', 'USBFilterRevision1'])
1.960 + keyset = set(props.keys())
1.961 + usb_filter = None
1.962 + if keyset.issuperset(keys):
1.963 + usb_filter = USBFilter(props['USBFilterVendorId1'], props['USBFilterProductId1'], props['USBFilterRevision1'])
1.964 + return usb_filter
1.965 +
1.966 + #generates ISO containing authorized_keys for use with guest VM
1.967 + def genCertificateISO(self, vm_name):
1.968 + machineFolder = Cygwin.cygPath(self.machineFolder)
1.969 + # remove .ssh folder if exists
1.970 + checkResult(Cygwin.bashExecute('\"/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"'))
1.971 + # remove .ssh folder if exists
1.972 + checkResult(Cygwin.bashExecute('\"/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"\"'))
1.973 + # create .ssh folder in vm_name
1.974 + checkResult(Cygwin.bashExecute('\"/usr/bin/mkdir -p \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"'))
1.975 + # generate dvm_key pair in vm_name / .ssh
1.976 + checkResult(Cygwin.bashExecute('\"/usr/bin/ssh-keygen -q -t rsa -N \\"\\" -C \\\"' + vm_name + '\\\" -f \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\"\"'))
1.977 + # move out private key
1.978 + checkResult(Cygwin.bashExecute('\"/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\" \\\"' + machineFolder + '/' + vm_name + '\\\"'))
1.979 + # set permissions for private key
1.980 + checkResult(Cygwin.bashExecute('\"/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"\"'))
1.981 + # rename public key to authorized_keys
1.982 + checkResult(Cygwin.bashExecute('\"/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key.pub\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"'))
1.983 + # set permissions for authorized_keys
1.984 + checkResult(Cygwin.bashExecute('\"/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"\"'))
1.985 + # generate iso image with .ssh/authorized keys
1.986 + checkResult(Cygwin.bashExecute('\"/usr/bin/genisoimage -J -R -o \\\"' + machineFolder + '/' + vm_name + '/'+ vm_name + '.iso\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"\"'))
1.987 +
1.988 + # attaches generated ssh public cert to guest vm
1.989 + def attachCertificateISO(self, vm_name):
1.990 + 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\"'))
1.991 + return result
1.992 +
1.993 + # wait for machine to come up
1.994 + def waitStartup(self, vm_name, timeout_ms = 30000):
1.995 + checkResult(Cygwin.vboxExecute('guestproperty wait ' + vm_name + ' SDVMStarted --timeout ' + str(timeout_ms) + ' --fail-on-timeout'))
1.996 + return VMManager.getHostOnlyIP(vm_name)
1.997 +
1.998 + # wait for machine to shutdown
1.999 + def waitShutdown(self, vm_name):
1.1000 + while vm_name in self.listRunningVMS():
1.1001 + time.sleep(1)
1.1002 + return
1.1003 +
1.1004 + # handles browsing request
1.1005 + def handleBrowsingRequest(self):
1.1006 + handler = BrowsingHandler(self)
1.1007 + handler.start()
1.1008 + return 'ok'
1.1009 +
1.1010 + #Small function to check the availability of network resource.
1.1011 + #def isAvailable(self, path):
1.1012 + #return os.path.exists(path)
1.1013 + #result = Cygwin.cmdExecute('IF EXIST "' + path + '" echo YES')
1.1014 + #return string.find(result[1], 'YES',)
1.1015 +
1.1016 + #Small function to check if the mention location is a directory
1.1017 + def isDirectory(self, path):
1.1018 + result = checkResult(Cygwin.cmdExecute('dir ' + path + ' | FIND ".."'))
1.1019 + return string.find(result[1], 'DIR',)
1.1020 +
1.1021 + def mapNetworkDrive(self, drive, networkPath, user, password):
1.1022 + self.unmapNetworkDrive(drive)
1.1023 + #Check for drive availability
1.1024 + if os.path.exists(drive):
1.1025 + logger.error("Drive letter is already in use: " + drive)
1.1026 + return -1
1.1027 + #Check for network resource availability
1.1028 + retry = 5
1.1029 + while not os.path.exists(networkPath):
1.1030 + time.sleep(1)
1.1031 + if retry == 0:
1.1032 + return -1
1.1033 + logger.info("Path not accessible: " + networkPath + " retrying")
1.1034 + retry-=1
1.1035 + #return -1
1.1036 +
1.1037 + command = 'USE ' + drive + ' ' + networkPath + ' /PERSISTENT:NO'
1.1038 + if user != None:
1.1039 + command += ' ' + password + ' /User' + user
1.1040 +
1.1041 + #TODO: Execute 'NET USE' command with authentication
1.1042 + result = checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', command))
1.1043 + if string.find(result[1], 'successfully',) == -1:
1.1044 + logger.error("Failed: NET " + command)
1.1045 + return -1
1.1046 + return 1
1.1047 +
1.1048 + def unmapNetworkDrive(self, drive):
1.1049 + drives = self.getNetworkDrives()
1.1050 + if drive not in drives.keys():
1.1051 + return 1
1.1052 + result = checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE ' + drive + ' /DELETE /YES'))
1.1053 + if string.find(str(result[1]), 'successfully',) == -1:
1.1054 + logger.error(result[2])
1.1055 + return -1
1.1056 + return 1
1.1057 +
1.1058 + def getNetworkDrives(self):
1.1059 + ip = VMManager.getHostOnlyIP(None)
1.1060 + ip = ip[:ip.rindex('.')]
1.1061 + drives = dict()
1.1062 + result = checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE'))
1.1063 + for line in result[1].splitlines():
1.1064 + if ip in line:
1.1065 + parts = line.split()
1.1066 + drives[parts[1]] = parts[2]
1.1067 + return drives
1.1068 +
1.1069 + def genNetworkDrive(self):
1.1070 + network_drives = self.getNetworkDrives()
1.1071 + logical_drives = VMManager.getLogicalDrives()
1.1072 + drives = list(map(chr, range(68, 91)))
1.1073 + for drive in drives:
1.1074 + if drive+':' not in network_drives and drive not in logical_drives:
1.1075 + return drive+':'
1.1076 +
1.1077 + def getNetworkDrive(self, vm_name):
1.1078 + ip = self.getHostOnlyIP(vm_name)
1.1079 + result = checkResult(Cygwin.execute('C:\\Windows\\system32\\net.exe', 'USE'))
1.1080 + for line in result[1].splitlines():
1.1081 + if line != None and ip in line:
1.1082 + parts = line.split()
1.1083 + return parts[0]
1.1084 + @staticmethod
1.1085 + def getLogicalDrives():
1.1086 + drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
1.1087 + return list(itertools.compress(string.ascii_uppercase, map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
1.1088 +
1.1089 + @staticmethod
1.1090 + def getDriveType(drive):
1.1091 + return ctypes.cdll.kernel32.GetDriveTypeW(u"%s:\\"%drive)
1.1092 +
1.1093 + @staticmethod
1.1094 + def getVolumeInfo(drive):
1.1095 + volumeNameBuffer = ctypes.create_unicode_buffer(1024)
1.1096 + fileSystemNameBuffer = ctypes.create_unicode_buffer(1024)
1.1097 + serial_number = None
1.1098 + max_component_length = None
1.1099 + file_system_flags = None
1.1100 +
1.1101 + rc = ctypes.cdll.kernel32.GetVolumeInformationW(
1.1102 + #ctypes.c_wchar_p("F:\\"),
1.1103 + u"%s:\\"%drive,
1.1104 + volumeNameBuffer,
1.1105 + ctypes.sizeof(volumeNameBuffer),
1.1106 + serial_number,
1.1107 + max_component_length,
1.1108 + file_system_flags,
1.1109 + fileSystemNameBuffer,
1.1110 + ctypes.sizeof(fileSystemNameBuffer)
1.1111 + )
1.1112 +
1.1113 + return volumeNameBuffer.value, fileSystemNameBuffer.value
1.1114 +
1.1115 +def checkResult(result):
1.1116 + if result[0] != 0:
1.1117 + logger.error('Command failed:' + ''.join(result[2]))
1.1118 + raise OpenSecurityException('Command failed:' + ''.join(result[2]))
1.1119 + return result
1.1120 +
1.1121 +# handles browsing request
1.1122 +class BrowsingHandler(threading.Thread):
1.1123 + vmm = None
1.1124 + def __init__(self, vmmanager):
1.1125 + threading.Thread.__init__(self)
1.1126 + self.vmm = vmmanager
1.1127 +
1.1128 + def run(self):
1.1129 + try:
1.1130 + new_sdvm = self.vmm.generateSDVMName()
1.1131 + self.vmm.createVM(new_sdvm)
1.1132 + self.vmm.storageAttach(new_sdvm)
1.1133 + self.vmm.genCertificateISO(new_sdvm)
1.1134 + self.vmm.attachCertificateISO(new_sdvm)
1.1135 + self.vmm.startVM(new_sdvm)
1.1136 + new_ip = self.vmm.waitStartup(new_sdvm)
1.1137 + drive = self.vmm.genNetworkDrive()
1.1138 + if new_ip != None:
1.1139 + self.vmm.mapNetworkDrive(drive, '\\\\' + new_ip + '\\Download', None, None)
1.1140 + result = checkResult(Cygwin.sshExecuteX11('/usr/bin/iceweasel', new_ip, 'osecuser', Cygwin.cygPath(self.vmm.getMachineFolder()) + '/' + new_sdvm + '/dvm_key'))
1.1141 + except:
1.1142 + logger.error("BrowsingHandler failed. Cleaning up")
1.1143 +
1.1144 + self.vmm.unmapNetworkDrive(drive)
1.1145 + self.vmm.poweroffVM(new_sdvm)
1.1146 + self.vmm.removeVM(new_sdvm)
1.1147 +
1.1148 +class DeviceHandler(threading.Thread):
1.1149 + vmm = None
1.1150 + #handleDeviceChangeLock = threading.Lock()
1.1151 + attachedRSDs = None
1.1152 + connectedRSDs = None
1.1153 + running = True
1.1154 + def __init__(self, vmmanger):
1.1155 + threading.Thread.__init__(self)
1.1156 + self.vmm = vmmanger
1.1157 +
1.1158 + def stop(self):
1.1159 + self.running = False
1.1160 +
1.1161 + def run(self):
1.1162 + self.connectedRSDs = dict()
1.1163 + self.attachedRSDs = self.vmm.getAttachedRSDs()
1.1164 + while self.running:
1.1165 + tmp_rsds = self.vmm.getConnectedRSDS()
1.1166 + if tmp_rsds.keys() == self.connectedRSDs.keys():
1.1167 + logger.debug("Nothing's changed. sleep(3)")
1.1168 + time.sleep(3)
1.1169 + continue
1.1170 +
1.1171 + logger.info("Something's changed")
1.1172 + self.connectedRSDs = tmp_rsds
1.1173 + self.attachedRSDs = self.vmm.getAttachedRSDs()
1.1174 +
1.1175 + for vm_name in self.attachedRSDs.keys():
1.1176 + if self.attachedRSDs[vm_name] not in self.connectedRSDs.values():
1.1177 + drive = self.vmm.getNetworkDrive(vm_name)
1.1178 + self.vmm.unmapNetworkDrive(drive)
1.1179 + #self.stopVM(vm_name)
1.1180 + self.vmm.detachRSD(vm_name)
1.1181 + self.vmm.poweroffVM(vm_name)
1.1182 + self.vmm.removeVM(vm_name)
1.1183 + #create new vm for attached device if any
1.1184 + self.attachedRSDs = self.vmm.getAttachedRSDs()
1.1185 + self.connectedRSDs = self.vmm.getConnectedRSDS()
1.1186 +
1.1187 + new_ip = None
1.1188 + for connected_device in self.connectedRSDs.values():
1.1189 + if (self.attachedRSDs and False) or (connected_device not in self.attachedRSDs.values()):
1.1190 + new_sdvm = self.vmm.generateSDVMName()
1.1191 + self.vmm.createVM(new_sdvm)
1.1192 + self.vmm.storageAttach(new_sdvm)
1.1193 + self.vmm.attachRSD(new_sdvm, connected_device)
1.1194 + self.vmm.startVM(new_sdvm)
1.1195 + new_ip = self.vmm.waitStartup(new_sdvm)
1.1196 + drive = self.vmm.genNetworkDrive()
1.1197 + if new_ip != None:
1.1198 + self.vmm.mapNetworkDrive(drive, '\\\\' + new_ip + '\\USB', None, None)
1.1199 +
1.1200 +if __name__ == '__main__':
1.1201 + #man = VMManager.getInstance()
1.1202 + #man.listVM()
1.1203 + #print man.getConnectedRSDs()
1.1204 + #print man.getNetworkDrives()
1.1205 + #man.genNetworkDrive()
1.1206 + #drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives()
1.1207 + #print list(itertools.compress(string.ascii_uppercase, map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1])))
1.1208 + #print list(map(chr, range(68, 91)))
1.1209 + #print Cygwin.getRegEntry('SYSTEM\CurrentControlSet\Enum\USB', 'VID_1058&PID_0704')[0]
1.1210 + #devices = VMManager.getConnectedRSDS()
1.1211 + #print devices
1.1212 +
1.1213 + drives = VMManager.getLogicalDrives()
1.1214 + print drives
1.1215 + print VMManager.getDriveType("E")
1.1216 + print VMManager.getVolumeInfo("E")
1.1217 + #for device in devices.values():
1.1218 + # #print device
1.1219 + # if VMManager.isMassStorageDevice(device):
1.1220 + # print device
1.1221 +
1.1222 +
1.1223 +
1.1224 + #time.sleep(-1)
1.1225 + #man.listVM()
1.1226 + #man.listVM()
1.1227 + #man.listVM()
1.1228 + #man.listVM()
1.1229 + #man.genCertificateISO('SecurityDVM0')
1.1230 + #man.guestExecute('SecurityDVM0', '/bin/ls -la')
1.1231 + #logger = setupLogger('VMManager')
1.1232 + #c = Cygwin()
1.1233 +
1.1234 + #man.sshExecute('/bin/ls -la', 'SecurityDVM0')
1.1235 + #man.sshExecuteX11('/usr/bin/iceweasel', 'SecurityDVM0')
1.1236 + #man.removeVM('SecurityDVM0')
1.1237 + #man.netUse('192.168.56.134', 'USB\\')
1.1238 + #ip = '192.168.56.139'
1.1239 +
1.1240 + #man.cygwin_path = 'c:\\cygwin64\\bin\\'
1.1241 + #man.handleDeviceChange()
1.1242 + #print man.listSDVM()
1.1243 + #man.configureHostNetworking()
1.1244 + #new_vm = man.generateSDVMName()
1.1245 + #man.createVM(new_vm)
1.1246 +
1.1247 + #print Cygwin.cmd()
1.1248 + #man.isAvailable('c:')
1.1249 + #ip = man.getHostOnlyIP('SecurityDVM0')
1.1250 + #man.mapNetworkDrive('h:', '\\\\' + ip + '\Download', None, None)
1.1251 +
1.1252 + #man.genCertificateISO(new_vm)
1.1253 + #man.attachCertificateISO(new_vm)
1.1254 +
1.1255 + #man.attachCertificateISO(vm_name)
1.1256 + #man.guestExecute(vm_name, "ls")
1.1257 + #man.sshGuestX11Execute('SecurityDVM1', '/usr/bin/iceweasel')
1.1258 + #time.sleep(60)
1.1259 + #print man.cygwinPath("C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\.ssh\*")
1.1260 + #man.genCertificateISO('SecurityDVM')
1.1261 + #man.attachCertificateISO('SecurityDVM')
1.1262 + #man.isStorageAttached('SecurityDVM')
1.1263 + #man.guestExecute('SecurityDVM', 'sudo apt-get -y update')
1.1264 + #man.guestExecute('SecurityDVM', 'sudo apt-get -y upgrade' )
1.1265 +
1.1266 + #man.stopVM('SecurityDVM')
1.1267 + #man.storageDetach('SecurityDVM')
1.1268 + #man.changeStorageType('C:\Users\BarthaM\VirtualBox VMs\SecurityDVM\SecurityDVM.vmdk','immutable')
1.1269 + #man.storageAttach('SecurityDVM')
1.1270 +
1.1271 +
1.1272 + #cmd = "c:\\cygwin64\\bin\\bash.exe --login -c \"/bin/ls\""
1.1273 + #man.execute(cmd)