BarthaM@212: #!/bin/env python BarthaM@212: # -*- coding: utf-8 -*- mb@90: BarthaM@212: # ------------------------------------------------------------ oliver@240: # the VMManager BarthaM@212: # oliver@240: # management of the Virtual Machines BarthaM@212: # BarthaM@212: # Autor: Mihai Bartha, BarthaM@212: # oliver@240: # Copyright 2013-2014 X-Net and AIT Austrian Institute of Technology BarthaM@212: # BarthaM@212: # oliver@240: # X-Net Services GmbH oliver@240: # Elisabethstrasse 1 oliver@240: # 4020 Linz oliver@240: # AUSTRIA oliver@240: # https://www.x-net.at oliver@240: # oliver@240: # AIT Austrian Institute of Technology oliver@240: # Donau City Strasse 1 oliver@240: # 1220 Wien oliver@240: # AUSTRIA oliver@240: # http://www.ait.ac.at oliver@240: # oliver@240: # oliver@240: # Licensed under the Apache License, Version 2.0 (the "License"); oliver@240: # you may not use this file except in compliance with the License. oliver@240: # You may obtain a copy of the License at oliver@240: # oliver@240: # http://www.apache.org/licenses/LICENSE-2.0 oliver@240: # oliver@240: # Unless required by applicable law or agreed to in writing, software oliver@240: # distributed under the License is distributed on an "AS IS" BASIS, oliver@240: # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. oliver@240: # See the License for the specific language governing permissions and oliver@240: # limitations under the License. BarthaM@212: # ------------------------------------------------------------ BarthaM@212: BarthaM@212: BarthaM@212: # ------------------------------------------------------------ BarthaM@212: # imports BarthaM@212: mb@90: import os mb@90: import os.path mb@90: from subprocess import Popen, PIPE, call, STARTUPINFO, _subprocess mb@90: import sys mb@90: import re mb@90: mb@90: from cygwin import Cygwin mb@90: from environment import Environment mb@90: import threading mb@90: import time mb@90: import string mb@90: mb@90: import shutil mb@90: import stat mb@90: import tempfile BarthaM@221: from opensecurity_util import logger, import_logger, setupLogger, OpenSecurityException, showTrayMessage mb@90: import ctypes mb@90: import itertools BarthaM@143: import win32api BarthaM@143: import win32con BarthaM@143: import win32security BarthaM@176: import win32wnet BarthaM@151: import urllib BarthaM@151: import urllib2 BarthaM@212: import unittest mb@90: DEBUG = True mb@90: BarthaM@159: BarthaM@159: new_sdvm_lock = threading.Lock() BarthaM@235: backup_lock = threading.Lock() BarthaM@159: mb@90: class VMManagerException(Exception): mb@90: def __init__(self, value): mb@90: self.value = value mb@90: def __str__(self): mb@90: return repr(self.value) mb@90: mb@90: class USBFilter: BarthaM@172: uuid = "" mb@90: vendorid = "" mb@90: productid = "" mb@90: revision = "" BarthaM@172: serial = "" mb@90: BarthaM@172: def __init__(self, uuid, vendorid, productid, revision, serial): BarthaM@172: self.uuid = uuid mb@90: self.vendorid = vendorid.lower() mb@90: self.productid = productid.lower() mb@90: self.revision = revision.lower() BarthaM@172: self.serial = serial mb@90: return mb@90: mb@90: def __eq__(self, other): BarthaM@172: return self.uuid == other.uuid #self.vendorid == other.vendorid and self.productid == other.productid and self.revision == other.revision mb@90: mb@90: def __hash__(self): BarthaM@172: return hash(self.uuid) ^ hash(self.vendorid) ^ hash(self.productid) ^ hash(self.revision) ^ hash(self.serial) mb@90: mb@90: def __repr__(self): BarthaM@172: return "UUID:" + str(self.uuid) + " VendorId = \'" + str(self.vendorid) + "\' ProductId = \'" + str(self.productid) + "\'" + "\' Revision = \'" + str(self.revision) + "\' SerialNumber = \'" + str(self.serial) mb@90: mb@90: #def __getitem__(self, item): mb@90: # return self.coords[item] BarthaM@212: def once(theClass): BarthaM@212: theClass.systemProperties = theClass.getSystemProperties() BarthaM@212: theClass.machineFolder = theClass.systemProperties["Default machine folder"] BarthaM@217: #theClass.hostonlyIF = theClass.getHostOnlyIFs()["VirtualBox Host-Only Ethernet Adapter"] BarthaM@212: theClass.blacklistedRSD = theClass.loadRSDBlacklist() BarthaM@221: theClass.templateImage = theClass.machineFolder + '\\' + theClass.vmRootName + '\\' + theClass.vmRootName + '.vmdk' BarthaM@212: return theClass BarthaM@212: BarthaM@212: @once mb@90: class VMManager(object): mb@90: vmRootName = "SecurityDVM" mb@90: systemProperties = None mb@90: _instance = None mb@90: machineFolder = '' BarthaM@217: hostonlyIF = None BarthaM@234: BarthaM@183: blacklistedRSD = None oliver@131: status_message = 'Starting up...' BarthaM@221: templateImage = None BarthaM@221: importHandler = None BarthaM@221: updateHandler = None BarthaM@234: deviceHandler = None BarthaM@234: sdvmFactory = None BarthaM@234: vms = dict() BarthaM@234: oliver@131: oliver@131: def __init__(self): oliver@131: # only proceed if we have a working background environment oliver@131: if self.backend_ok(): BarthaM@217: VMManager.hostonlyIF = self.getHostOnlyIFs()["VirtualBox Host-Only Ethernet Adapter"] oliver@131: self.cleanup() oliver@131: else: oliver@131: logger.critical(self.status_message) mb@90: oliver@131: mb@90: @staticmethod mb@90: def getInstance(): mb@90: if VMManager._instance == None: mb@90: VMManager._instance = VMManager() mb@90: return VMManager._instance mb@90: BarthaM@176: #list the hostonly IFs exposed by the VBox host BarthaM@176: @staticmethod BarthaM@176: def getHostOnlyIFs(): BarthaM@217: results = Cygwin.vboxExecute('list hostonlyifs')[1] BarthaM@217: ifs = dict() BarthaM@217: if results=='': BarthaM@217: return ifs BarthaM@217: items = list( "Name: " + result for result in results.split('Name: ') if result != '') BarthaM@217: for item in items: BarthaM@217: if item != "": BarthaM@217: props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in item.strip().splitlines())) BarthaM@217: ifs[props["Name"]] = props BarthaM@217: return ifs BarthaM@217: BarthaM@217: #list the hostonly IFs exposed by the VBox host BarthaM@217: @staticmethod BarthaM@217: def getDHCPServers(): BarthaM@217: results = Cygwin.vboxExecute('list dhcpservers')[1] BarthaM@217: if results=='': BarthaM@217: return dict() BarthaM@217: items = list( "NetworkName: " + result for result in results.split('NetworkName: ') if result != '') BarthaM@217: dhcps = dict() BarthaM@217: for item in items: BarthaM@217: if item != "": BarthaM@217: props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in item.strip().splitlines())) BarthaM@217: dhcps[props["NetworkName"]] = props BarthaM@217: return dhcps BarthaM@176: BarthaM@176: # return hosty system properties BarthaM@176: @staticmethod BarthaM@176: def getSystemProperties(): BarthaM@218: result = Cygwin.vboxExecute('list systemproperties') BarthaM@176: if result[1]=='': BarthaM@176: return None BarthaM@176: props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result[1].strip().splitlines())) BarthaM@176: return props BarthaM@176: BarthaM@176: # return the folder containing the guest VMs BarthaM@176: def getMachineFolder(self): BarthaM@212: return VMManager.machineFolder BarthaM@217: BarthaM@217: # verifies the hostonly interface and DHCP server settings BarthaM@217: def verifyHostOnlySettings(self): BarthaM@217: interfaceName = "VirtualBox Host-Only Ethernet Adapter" BarthaM@217: networkName = "HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter" BarthaM@217: BarthaM@217: hostonlyifs = self.getHostOnlyIFs() BarthaM@217: if not interfaceName in hostonlyifs.keys(): BarthaM@217: Cygwin.vboxExecute('hostonlyif create') BarthaM@217: hostonlyifs = self.getHostOnlyIFs() BarthaM@217: if not interfaceName in hostonlyifs.keys(): BarthaM@217: return False BarthaM@217: BarthaM@217: interface = hostonlyifs[interfaceName] BarthaM@217: if interface['VBoxNetworkName'] != networkName or interface['DHCP'] != 'Disabled' or interface['IPAddress'] != '192.168.56.1': BarthaM@217: Cygwin.vboxExecute('hostonlyif ipconfig "' + interfaceName + '" --ip 192.168.56.1 --netmask 255.255.255.0') BarthaM@217: BarthaM@217: dhcpservers = self.getDHCPServers() BarthaM@217: if not networkName in dhcpservers.keys(): BarthaM@217: Cygwin.vboxExecute('dhcpserver add --ifname "' + interfaceName + '" --ip 192.168.56.100 --netmask 255.255.255.0 --lowerip 192.168.56.101 --upperip 192.168.56.254 --enable') BarthaM@217: dhcpservers = self.getDHCPServers() BarthaM@217: if not networkName in dhcpservers.keys(): BarthaM@217: return False BarthaM@217: BarthaM@217: server = dhcpservers[networkName] BarthaM@217: if server['IP'] != '192.168.56.100' or server['NetworkMask'] != '255.255.255.0' or server['lowerIPAddress'] != '192.168.56.101' or server['upperIPAddress'] != '192.168.56.254' or server['Enabled'] != 'Yes': BarthaM@217: Cygwin.vboxExecute('VBoxManage dhcpserver modify --netname "' + networkName + '" --ip 192.168.56.100 --netmask 255.255.255.0 --lowerip 192.168.56.101 --upperip 192.168.56.254 --enable') BarthaM@217: BarthaM@217: return True oliver@131: BarthaM@219: def template_installed(self): BarthaM@219: """ check if we do have our root VMs installed """ BarthaM@221: vms = self.listVMS() BarthaM@219: if not self.vmRootName in vms: BarthaM@219: self.status_message = 'Unable to locate root SecurityDVM. Please download and setup the initial image.' BarthaM@219: return False BarthaM@219: return True BarthaM@219: oliver@131: def backend_ok(self): oliver@131: """check if the backend (VirtualBox) is sufficient for our task""" oliver@131: oliver@131: # ensure we have our system props BarthaM@212: if VMManager.systemProperties == None: BarthaM@212: VMManager.systemProperties = self.getSystemProperties() BarthaM@212: if VMManager.systemProperties == None: oliver@131: self.status_message = 'Failed to get backend system properties. Is Backend (VirtualBox?) installed?' oliver@131: return False oliver@131: oliver@131: # check for existing Extension pack BarthaM@212: if not 'Remote desktop ExtPack' in VMManager.systemProperties: oliver@131: self.status_message = 'No remote desktop extension pack found. Please install the "Oracle VM VirtualBox Extension Pack" from https://www.virtualbox.org/wiki/Downloads.' oliver@131: return False BarthaM@212: if VMManager.systemProperties['Remote desktop ExtPack'] == 'Oracle VM VirtualBox Extension Pack ': oliver@131: 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.' oliver@131: return False oliver@131: BarthaM@219: # check the existing hostOnly network settings and try to reconfigure if faulty BarthaM@219: if not self.verifyHostOnlySettings(): oliver@131: return False BarthaM@219: oliver@131: # basically all seems nice and ready to rumble oliver@131: self.status_message = 'All is ok.' oliver@131: return True BarthaM@170: BarthaM@221: def start(self, force = False): BarthaM@221: if not force: BarthaM@221: if self.importHandler and self.importHandler.isAlive(): BarthaM@221: logger.info("Initial update running canceling start.") BarthaM@221: return BarthaM@221: BarthaM@221: if self.updateHandler and self.updateHandler.isAlive(): BarthaM@221: logger.info("Update running canceling start.") BarthaM@221: return BarthaM@221: BarthaM@170: self.stop() BarthaM@219: Cygwin.allowExec() BarthaM@219: if self.backend_ok() and self.template_installed(): BarthaM@234: self.sdvmFactory = SDVMFactory(self) BarthaM@234: self.sdvmFactory.start() BarthaM@234: self.deviceHandler = DeviceHandler(self) BarthaM@234: self.deviceHandler.start() BarthaM@234: BarthaM@234: def stop(self): BarthaM@234: Cygwin.denyExec() BarthaM@234: if self.sdvmFactory != None: BarthaM@234: self.sdvmFactory.stop() BarthaM@234: self.sdvmFactory.join() BarthaM@234: self.sdvmFactory = None BarthaM@234: if self.deviceHandler != None: BarthaM@234: self.deviceHandler.stop() BarthaM@234: self.deviceHandler.join() BarthaM@234: self.deviceHandler = None BarthaM@170: BarthaM@234: Cygwin.allowExec() BarthaM@170: BarthaM@170: def cleanup(self): BarthaM@170: self.stop() BarthaM@219: Cygwin.allowExec() BarthaM@176: ip = self.getHostOnlyIP(None) BarthaM@176: try: BarthaM@176: result = urllib2.urlopen('http://127.0.0.1:8090/netcleanup?'+'hostonly_ip='+ip).readline() BarthaM@176: except urllib2.URLError: BarthaM@176: logger.info("Network drive cleanup all skipped. OpenSecurity Tray client not started yet.") BarthaM@151: mb@90: for vm in self.listSDVM(): mb@90: self.poweroffVM(vm) mb@90: self.removeVM(vm) mihai@237: self.vms = dict() mihai@237: mb@90: # list all existing VMs registered with VBox BarthaM@221: def listVMS(self): BarthaM@218: result = Cygwin.vboxExecute('list vms')[1] mb@90: vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines())) mb@90: return vms mb@90: mb@90: # list running VMs mb@90: def listRunningVMS(self): BarthaM@218: result = Cygwin.vboxExecute('list runningvms')[1] mb@90: vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines())) mb@90: return vms mb@90: mb@90: # list existing SDVMs mb@90: def listSDVM(self): BarthaM@221: vms = self.listVMS() mb@90: svdms = [] mb@90: for vm in vms: mb@90: if vm.startswith(self.vmRootName) and vm != self.vmRootName: mb@90: svdms.append(vm) mb@90: return svdms mb@90: mb@90: # generate valid (not already existing SDVM name). necessary for creating a new VM BarthaM@159: def genSDVMName(self): BarthaM@221: vms = self.listVMS() mb@90: for i in range(0,999): mb@90: if(not self.vmRootName+str(i) in vms): mb@90: return self.vmRootName+str(i) mb@90: return '' mb@90: BarthaM@183: @staticmethod BarthaM@183: def loadRSDBlacklist(): BarthaM@183: blacklist = dict() BarthaM@183: try: BarthaM@183: fo = open(Environment('OpenSecurity').prefix_path +"\\bin\\blacklist.usb", "r") BarthaM@183: except IOError: BarthaM@183: logger.error("Could not open RSD blacklist file.") BarthaM@183: return blacklist BarthaM@183: BarthaM@183: lines = fo.readlines() BarthaM@183: for line in lines: BarthaM@183: if line != "": BarthaM@183: parts = line.strip().split(' ') BarthaM@183: blacklist[parts[0].lower()] = parts[1].lower() BarthaM@183: return blacklist BarthaM@183: BarthaM@183: @staticmethod BarthaM@183: def isBlacklisted(device): BarthaM@183: if VMManager.blacklistedRSD: BarthaM@183: blacklisted = device.vendorid.lower() in VMManager.blacklistedRSD.keys() and device.productid.lower() == VMManager.blacklistedRSD[device.vendorid] BarthaM@183: return blacklisted BarthaM@183: return False BarthaM@183: mb@90: # check if the device is mass storage type mb@90: @staticmethod mb@90: def isMassStorageDevice(device): BarthaM@219: vidkey = None BarthaM@219: devinfokey = None BarthaM@219: value = "" BarthaM@219: try: BarthaM@219: keyname = 'SYSTEM\CurrentControlSet\Enum\USB' + '\VID_' + device.vendorid+'&'+'PID_'+ device.productid BarthaM@219: vidkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, keyname) BarthaM@219: devinfokeyname = win32api.RegEnumKey(vidkey, 0) BarthaM@219: win32api.RegCloseKey(vidkey) BarthaM@219: BarthaM@219: devinfokey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, keyname+'\\'+devinfokeyname) BarthaM@219: value = win32api.RegQueryValueEx(devinfokey, 'SERVICE')[0] BarthaM@219: win32api.RegCloseKey(devinfokey) BarthaM@219: except Exception as ex: BarthaM@219: logger.error('Error reading registry.Exception details: %s' %ex) BarthaM@219: finally: BarthaM@219: if vidkey is not None: BarthaM@219: win32api.RegCloseKey(vidkey) BarthaM@219: if devinfokey is not None: BarthaM@219: win32api.RegCloseKey(devinfokey) mb@90: mb@90: return 'USBSTOR' in value mb@90: mb@90: # return the RSDs connected to the host mb@90: @staticmethod BarthaM@172: def getExistingRSDs(): BarthaM@218: results = Cygwin.vboxExecute('list usbhost')[1] mb@90: results = results.split('Host USB Devices:')[1].strip() mb@90: mb@90: items = list( "UUID:"+result for result in results.split('UUID:') if result != '') mb@90: rsds = dict() mb@90: for item in items: mb@90: props = dict() BarthaM@172: for line in item.splitlines(): mb@90: if line != "": mb@90: k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip() mb@90: props[k] = v mb@90: BarthaM@172: uuid = re.search(r"(?P[0-9A-Fa-f\-]+)", props['UUID']).groupdict()['uuid'] BarthaM@172: vid = re.search(r"\((?P[0-9A-Fa-f]+)\)", props['VendorId']).groupdict()['vid'] BarthaM@172: pid = re.search(r"\((?P[0-9A-Fa-f]+)\)", props['ProductId']).groupdict()['pid'] BarthaM@172: rev = re.search(r"\((?P[0-9A-Fa-f]+)\)", props['Revision']).groupdict()['rev'] BarthaM@172: serial = None BarthaM@172: if 'SerialNumber' in props.keys(): BarthaM@172: serial = re.search(r"(?P[0-9A-Fa-f]+)", props['SerialNumber']).groupdict()['ser'] BarthaM@183: usb_filter = USBFilter( uuid, vid, pid, rev, serial) BarthaM@183: BarthaM@183: if VMManager.isMassStorageDevice(usb_filter) and not VMManager.isBlacklisted(usb_filter): BarthaM@172: rsds[uuid] = usb_filter mb@90: logger.debug(usb_filter) mb@90: return rsds mb@90: BarthaM@172: BarthaM@172: # return the attached USB device as usb descriptor for an existing VM BarthaM@172: def getAttachedRSD(self, vm_name): BarthaM@172: props = self.getVMInfo(vm_name) BarthaM@172: keys = set(['USBAttachedUUID1', 'USBAttachedVendorId1', 'USBAttachedProductId1', 'USBAttachedRevision1', 'USBAttachedSerialNumber1']) BarthaM@172: keyset = set(props.keys()) BarthaM@172: usb_filter = None BarthaM@172: if keyset.issuperset(keys): BarthaM@172: usb_filter = USBFilter(props['USBAttachedUUID1'], props['USBAttachedVendorId1'], props['USBAttachedProductId1'], props['USBAttachedRevision1'], props['USBAttachedSerialNumber1']) BarthaM@172: return usb_filter BarthaM@172: mb@90: # return the RSDs attached to all existing SDVMs mb@90: def getAttachedRSDs(self): mb@90: vms = self.listSDVM() mb@90: attached_devices = dict() mb@90: for vm in vms: BarthaM@172: rsd_filter = self.getAttachedRSD(vm) mb@90: if rsd_filter != None: mb@90: attached_devices[vm] = rsd_filter mb@90: return attached_devices mb@90: BarthaM@172: # attach removable storage device to VM by provision of filter BarthaM@172: def attachRSD(self, vm_name, rsd_filter): BarthaM@218: #return Cygwin.vboxExecute('usbfilter add 0 --target ' + vm_name + ' --name OpenSecurityRSD --vendorid ' + rsd_filter.vendorid + ' --productid ' + rsd_filter.productid + ' --revision ' + rsd_filter.revision + ' --serialnumber ' + rsd_filter.serial) BarthaM@218: return Cygwin.vboxExecute('controlvm ' + vm_name + ' usbattach ' + rsd_filter.uuid ) BarthaM@172: BarthaM@172: # detach removable storage from VM by BarthaM@172: def detachRSD(self, vm_name, rsd_filter): BarthaM@218: #return Cygwin.vboxExecute('usbfilter remove 0 --target ' + vm_name) BarthaM@218: return Cygwin.vboxExecute('controlvm ' + vm_name + ' usbdetach ' + rsd_filter.uuid ) BarthaM@172: mb@90: # configures hostonly networking and DHCP server. requires admin rights mb@90: def configureHostNetworking(self): mb@90: #cmd = 'vboxmanage list hostonlyifs' mb@90: #Cygwin.vboxExecute(cmd) mb@90: #cmd = 'vboxmanage hostonlyif remove \"VirtualBox Host-Only Ethernet Adapter\"' mb@90: #Cygwin.vboxExecute(cmd) mb@90: #cmd = 'vboxmanage hostonlyif create' mb@90: #Cygwin.vboxExecute(cmd) BarthaM@218: Cygwin.vboxExecute('hostonlyif ipconfig \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.1 --netmask 255.255.255.0') mb@90: #cmd = 'vboxmanage dhcpserver add' mb@90: #Cygwin.vboxExecute(cmd) BarthaM@218: 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') mb@90: BarthaM@125: def isSDVMExisting(self, vm_name): BarthaM@125: sdvms = self.listSDVM() BarthaM@125: return vm_name in sdvms BarthaM@125: mb@90: #create new virtual machine instance based on template vm named SecurityDVM (\SecurityDVM\SecurityDVM.vmdk) mb@90: def createVM(self, vm_name): BarthaM@125: if self.isSDVMExisting(vm_name): BarthaM@125: return BarthaM@125: #remove eventually existing SDVM folder BarthaM@212: machineFolder = Cygwin.cygPath(VMManager.machineFolder) BarthaM@218: Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '\\\"') BarthaM@218: Cygwin.vboxExecute('createvm --name ' + vm_name + ' --ostype Debian --register') BarthaM@218: Cygwin.vboxExecute('modifyvm ' + vm_name + ' --memory 768 --vram 10 --cpus 1 --usb on --usbehci on --nic1 hostonly --hostonlyadapter1 \"' + self.hostonlyIF['Name'] + '\" --nic2 nat') BarthaM@218: Cygwin.vboxExecute('storagectl ' + vm_name + ' --name SATA --add sata --portcount 2') BarthaM@159: BarthaM@159: #create new SecurityDVM with automatically generated name from template (thread safe) BarthaM@159: def newSDVM(self): BarthaM@159: with new_sdvm_lock: BarthaM@159: vm_name = self.genSDVMName() BarthaM@159: self.createVM(vm_name) BarthaM@159: return vm_name mb@90: BarthaM@221: #VMManager.machineFolder + '\SecurityDVM\SecurityDVM.vmdk mb@90: # attach storage image to controller BarthaM@221: def attachVDisk(self, vm_name, vdisk_controller, vdisk_port, vdisk_device, vdisk_image): BarthaM@221: if self.isVDiskAttached(vm_name, vdisk_controller, vdisk_port, vdisk_device): BarthaM@221: self.detachVDisk(vm_name, vdisk_controller, vdisk_port, vdisk_device) BarthaM@221: Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl '+ vdisk_controller + ' --port ' + vdisk_port + ' --device ' + vdisk_device + ' --type hdd --medium "'+ vdisk_image + '"') mb@90: mb@90: # return true if storage is attached BarthaM@221: def isVDiskAttached(self, vm_name, vdisk_controller, vdisk_port, vdisk_device): mb@90: info = self.getVMInfo(vm_name) BarthaM@221: return (info[vdisk_controller+'-'+vdisk_port+'-'+vdisk_device] != 'none') mb@90: mb@90: # detach storage from controller BarthaM@221: def detachVDisk(self, vm_name, vdisk_controller, vdisk_port, vdisk_device): BarthaM@221: if self.isVDiskAttached(vm_name, vdisk_controller, vdisk_port, vdisk_device): BarthaM@221: Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl ' + vdisk_controller + ' --port ' + vdisk_port + ' --device ' + vdisk_device + ' --medium none') mb@90: BarthaM@221: # modify type of the vdisk_image BarthaM@221: def changeVDiskType(self, vdisk_image, storage_type): BarthaM@221: Cygwin.vboxExecute('modifyhd "' + vdisk_image + '" --type ' + storage_type) BarthaM@171: BarthaM@221: # grab VM storage controller, port and device for vdisk image name BarthaM@221: def getVDiskController(self, vm_name, image_name = '.vmdk'): BarthaM@221: vm_description = self.getVMInfo(vm_name) BarthaM@221: vdisk_controller = None BarthaM@221: for key, value in vm_description.iteritems(): BarthaM@221: if image_name in value: BarthaM@221: vdisk_controller = key BarthaM@221: break BarthaM@221: return vdisk_controller BarthaM@221: BarthaM@221: # return attached vmdk image name containing image_name BarthaM@221: def getVDiskImage(self, vm_name, image_name = '.vmdk'): BarthaM@221: vmInfo = self.getVMInfo(vm_name) BarthaM@221: vdisk_image = None BarthaM@221: for value in vmInfo.values(): BarthaM@221: if image_name in value: BarthaM@221: break BarthaM@221: return vdisk_image BarthaM@212: BarthaM@212: @staticmethod BarthaM@221: def getVDiskImages(): BarthaM@218: results = Cygwin.vboxExecute('list hdds')[1] mb@90: results = results.replace('Parent UUID', 'Parent') mb@90: items = list( "UUID:"+result for result in results.split('UUID:') if result != '') mb@90: mb@90: snaps = dict() mb@90: for item in items: mb@90: props = dict() mb@90: for line in item.splitlines(): mb@90: if line != "": mb@90: k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip() mb@90: props[k] = v; mb@90: snaps[props['UUID']] = props BarthaM@171: return snaps BarthaM@171: BarthaM@212: @staticmethod BarthaM@221: def getVDiskUUID(vdisk_image): BarthaM@221: images = VMManager.getVDiskImages() mb@90: # find template uuid BarthaM@171: template_uuid = None BarthaM@171: for hdd in images.values(): BarthaM@221: if hdd['Location'] == vdisk_image: mb@90: template_uuid = hdd['UUID'] BarthaM@171: break BarthaM@171: return template_uuid BarthaM@221: BarthaM@171: def removeSnapshots(self, imageUUID): BarthaM@221: snaps = self.getVDiskImages() mb@90: # remove snapshots mb@90: for hdd in snaps.values(): BarthaM@171: if hdd['Parent'] == imageUUID: BarthaM@171: snapshotUUID = hdd['UUID'] BarthaM@171: self.removeImage(snapshotUUID) BarthaM@170: BarthaM@171: def removeImage(self, imageUUID): BarthaM@171: logger.debug('removing snapshot ' + imageUUID) BarthaM@218: Cygwin.vboxExecute('closemedium disk {' + imageUUID + '} --delete') BarthaM@171: # parse result 0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100% mb@90: mb@90: #remove VM from the system. should be used on VMs returned by listSDVMs mb@90: def removeVM(self, vm_name): mb@90: logger.info('Removing ' + vm_name) BarthaM@218: Cygwin.vboxExecute('unregistervm ' + vm_name + ' --delete') BarthaM@221: #try to close medium if still existing BarthaM@218: #Cygwin.vboxExecute('closemedium disk {' + hdd['UUID'] + '} --delete') BarthaM@170: self.removeVMFolder(vm_name) BarthaM@170: BarthaM@170: def removeVMFolder(self, vm_name): BarthaM@212: machineFolder = Cygwin.cygPath(VMManager.machineFolder) mihai@252: Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/\"') mb@90: mb@90: # start VM mb@90: def startVM(self, vm_name): mb@90: logger.info('Starting ' + vm_name) BarthaM@218: Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False') BarthaM@212: result = Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless' ) mb@90: while 'successfully started' not in result[1]: mb@90: logger.error("Failed to start SDVM: " + vm_name + " retrying") oliver@129: logger.error("Command returned:\n" + result[2]) mb@90: time.sleep(1) BarthaM@212: result = Cygwin.vboxExecute('startvm ' + vm_name + ' --type headless') mb@90: return result[0] mb@90: mb@90: # return wether VM is running or not mb@90: def isVMRunning(self, vm_name): mb@90: return vm_name in self.listRunningVMS() mb@90: mb@90: # stop VM mb@90: def stopVM(self, vm_name): mb@90: logger.info('Sending shutdown signal to ' + vm_name) mihai@252: Cygwin.sshExecute( '"sudo shutdown -h now"', self.getHostOnlyIP(vm_name), 'osecuser', self.getCertificatePath(vm_name) ) BarthaM@218: Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False') mb@90: mb@90: # stop VM mb@90: def hibernateVM(self, vm_name): mb@95: logger.info('Sending hibernate-disk signal to ' + vm_name) mihai@252: Cygwin.sshBackgroundExecute( '"sudo hibernate-disk"', self.getHostOnlyIP(vm_name), 'osecuser', self.getCertificatePath(vm_name), wait_return=False) BarthaM@218: Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False') mb@90: mb@90: # poweroff VM mb@90: def poweroffVM(self, vm_name): mb@90: if not self.isVMRunning(vm_name): mb@90: return mb@90: logger.info('Powering off ' + vm_name) BarthaM@218: Cygwin.vboxExecute('controlvm ' + vm_name + ' poweroff') BarthaM@218: Cygwin.vboxExecute('guestproperty set ' + vm_name + ' SDVMStarted False') BarthaM@218: mb@90: BarthaM@176: # return the hostOnly IP for a running guest or the host BarthaM@176: def getHostOnlyIP(self, vm_name): mb@90: if vm_name == None: oliver@129: logger.info('Getting hostOnly IP address for Host') BarthaM@217: return VMManager.hostonlyIF['IPAddress'] mb@90: else: oliver@129: logger.info('Getting hostOnly IP address ' + vm_name) BarthaM@218: result = Cygwin.vboxExecute('guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP') mb@90: if result=='': mb@90: return None mb@90: result = result[1] mb@90: if result.startswith('No value set!'): mb@90: return None mb@90: return result[result.index(':')+1:].strip() mb@90: mb@90: # return the description set for an existing VM mb@90: def getVMInfo(self, vm_name): BarthaM@218: results = Cygwin.vboxExecute('showvminfo ' + vm_name + ' --machinereadable')[1] mb@90: props = dict((k.strip().strip('"'),v.strip().strip('"')) for k,v in (line.split('=', 1) for line in results.splitlines())) mb@90: return props mb@90: mb@90: #generates ISO containing authorized_keys for use with guest VM BarthaM@218: def genCertificate(self, vm_name): BarthaM@212: machineFolder = Cygwin.cygPath(VMManager.machineFolder) mb@90: # remove .ssh folder if exists BarthaM@218: Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"') mb@90: # remove .ssh folder if exists BarthaM@218: Cygwin.bashExecute('/usr/bin/rm -rf \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"') mb@90: # create .ssh folder in vm_name BarthaM@218: Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"') mb@90: # generate dvm_key pair in vm_name / .ssh BarthaM@218: Cygwin.bashExecute('/usr/bin/ssh-keygen -q -t rsa -N \\\"\\\" -C \\\"' + vm_name + '\\\" -f \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\"') mb@90: # move out private key BarthaM@218: Cygwin.bashExecute('/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key\\\" \\\"' + machineFolder + '/' + vm_name + '\\\"') mb@90: # set permissions for private key BarthaM@218: Cygwin.bashExecute('/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/dvm_key\\\"') mb@90: # rename public key to authorized_keys BarthaM@218: Cygwin.bashExecute('/usr/bin/mv \\\"' + machineFolder + '/' + vm_name + '/.ssh/dvm_key.pub\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"') mb@90: # set permissions for authorized_keys BarthaM@218: Cygwin.bashExecute('/usr/bin/chmod 500 \\\"' + machineFolder + '/' + vm_name + '/.ssh/authorized_keys\\\"') mb@90: # generate iso image with .ssh/authorized keys BarthaM@218: Cygwin.bashExecute('/usr/bin/genisoimage -J -R -o \\\"' + machineFolder + '/' + vm_name + '/'+ vm_name + '.iso\\\" \\\"' + machineFolder + '/' + vm_name + '/.ssh\\\"') mb@90: mb@90: # attaches generated ssh public cert to guest vm BarthaM@218: def attachCertificate(self, vm_name): BarthaM@218: if self.isCertificateAttached(vm_name): BarthaM@218: self.detachCertificate(vm_name) BarthaM@218: Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 1 --device 0 --type dvddrive --mtype readonly --medium \"' + VMManager.machineFolder + '\\' + vm_name + '\\'+ vm_name + '.iso\"') mb@90: BarthaM@218: # return true if storage is attached BarthaM@218: def isCertificateAttached(self, vm_name): BarthaM@218: info = self.getVMInfo(vm_name) BarthaM@218: return (info['SATA-1-0']!='none') BarthaM@218: BarthaM@218: # detach storage from controller BarthaM@218: def detachCertificate(self, vm_name): BarthaM@218: if self.isCertificateAttached(vm_name): BarthaM@218: Cygwin.vboxExecute('storageattach ' + vm_name + ' --storagectl SATA --port 1 --device 0 --type hdd --medium none') mihai@252: mihai@252: # return path for the certificate of a specific vm mihai@252: def getCertificatePath(self, vm_name): mihai@252: return Cygwin.cygPath(VMManager.machineFolder) + '/' + vm_name + '/dvm_key' mihai@252: mb@90: # wait for machine to come up BarthaM@218: def waitStartup(self, vm_name): BarthaM@218: #Cygwin.vboxExecute('guestproperty wait ' + vm_name + ' SDVMStarted --timeout ' + str(timeout_ms) + ' --fail-on-timeout', try_count = 60) BarthaM@217: started = False BarthaM@217: while not started: BarthaM@218: result = Cygwin.vboxExecute('guestproperty get ' + vm_name + ' SDVMStarted')[1] BarthaM@217: if "Value: True" in result: BarthaM@217: started = True BarthaM@217: else: BarthaM@217: time.sleep(3) BarthaM@176: return self.getHostOnlyIP(vm_name) mb@90: mb@90: # wait for machine to shutdown mb@90: def waitShutdown(self, vm_name): mb@90: while vm_name in self.listRunningVMS(): mb@90: time.sleep(1) mb@90: return mb@90: BarthaM@135: #Small function to check if the mentioned location is a directory mb@90: def isDirectory(self, path): BarthaM@218: result = Cygwin.cmdExecute('dir ' + path + ' | FIND ".."') mb@90: return string.find(result[1], 'DIR',) mb@90: oliver@167: def genNetworkDrive(self): oliver@167: logical_drives = VMManager.getLogicalDrives() oliver@167: logger.info("Used logical drive letters: "+ str(logical_drives).strip('[]') ) oliver@167: drives = list(map(chr, range(68, 91))) oliver@167: for drive in drives: BarthaM@176: if drive not in logical_drives: BarthaM@176: return drive BarthaM@151: mb@90: @staticmethod mb@90: def getLogicalDrives(): mb@90: drive_bitmask = ctypes.cdll.kernel32.GetLogicalDrives() BarthaM@176: drives = list(itertools.compress(string.ascii_uppercase, map(lambda x:ord(x) - ord('0'), bin(drive_bitmask)[:1:-1]))) BarthaM@176: return drives mb@90: mb@90: @staticmethod mb@90: def getDriveType(drive): mb@90: return ctypes.cdll.kernel32.GetDriveTypeW(u"%s:\\"%drive) mb@90: mb@90: @staticmethod BarthaM@176: def getNetworkPath(drive): BarthaM@176: return win32wnet.WNetGetConnection(drive+':') BarthaM@176: BarthaM@176: @staticmethod mb@90: def getVolumeInfo(drive): mb@90: volumeNameBuffer = ctypes.create_unicode_buffer(1024) mb@90: fileSystemNameBuffer = ctypes.create_unicode_buffer(1024) mb@90: serial_number = None mb@90: max_component_length = None mb@90: file_system_flags = None mb@90: mb@90: rc = ctypes.cdll.kernel32.GetVolumeInformationW( mb@90: u"%s:\\"%drive, mb@90: volumeNameBuffer, mb@90: ctypes.sizeof(volumeNameBuffer), mb@90: serial_number, mb@90: max_component_length, mb@90: file_system_flags, mb@90: fileSystemNameBuffer, mb@90: ctypes.sizeof(fileSystemNameBuffer) mb@90: ) mb@90: return volumeNameBuffer.value, fileSystemNameBuffer.value BarthaM@141: BarthaM@176: def getNetworkDrive(self, vm_name): BarthaM@176: ip = self.getHostOnlyIP(vm_name) BarthaM@176: if ip == None: BarthaM@176: logger.error("Failed getting hostonly IP for " + vm_name) BarthaM@176: return None BarthaM@176: logger.info("Got IP address for " + vm_name + ': ' + ip) BarthaM@176: for drive in VMManager.getLogicalDrives(): BarthaM@176: #if is a network drive BarthaM@176: if VMManager.getDriveType(drive) == 4: BarthaM@176: network_path = VMManager.getNetworkPath(drive) BarthaM@176: if ip in network_path: BarthaM@176: return drive BarthaM@176: return None BarthaM@176: BarthaM@176: def getNetworkDrives(self): BarthaM@176: ip = self.getHostOnlyIP(None) BarthaM@176: if ip == None: BarthaM@176: logger.error("Failed getting hostonly IP for system") BarthaM@176: return None BarthaM@176: logger.info("Got IP address for system: " + ip) BarthaM@176: ip = ip[:ip.rindex('.')] BarthaM@176: network_drives = dict() BarthaM@176: for drive in VMManager.getLogicalDrives(): BarthaM@176: #if is a network drive BarthaM@176: if VMManager.getDriveType(drive) == 4: BarthaM@176: network_path = VMManager.getNetworkPath(drive) BarthaM@176: if ip in network_path: BarthaM@176: network_drives[drive] = network_path BarthaM@176: return network_drives BarthaM@176: BarthaM@141: # handles browsing request BarthaM@234: def handleBrowsingRequest(self, proxy = None, wpad = None): oliver@193: showTrayMessage('Starting Secure Browsing...', 7000) BarthaM@223: handler = BrowsingHandler(self, proxy, wpad) BarthaM@141: handler.start() BarthaM@141: return 'ok' BarthaM@143: BarthaM@143: def getActiveUserName(self): BarthaM@143: key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI') BarthaM@143: v = str(win32api.RegQueryValueEx(key, 'LastLoggedOnUser')[0]) BarthaM@143: win32api.RegCloseKey(key) BarthaM@143: user_name = win32api.ExpandEnvironmentStrings(v) BarthaM@143: return user_name BarthaM@143: BarthaM@143: def getUserSID(self, user_name): oliver@167: domain, user = user_name.split("\\") oliver@167: account_name = win32security.LookupAccountName(domain, user) oliver@167: if account_name == None: oliver@167: logger.error("Failed lookup account name for user " + user_name) oliver@167: return None BarthaM@143: sid = win32security.ConvertSidToStringSid(account_name[0]) oliver@167: if sid == None: oliver@167: logger.error("Failed converting SID for account " + account_name[0]) oliver@167: return None mihai@237: return sid mihai@237: mihai@237: def getAppDataDirReg(self, sid): BarthaM@143: key = win32api.RegOpenKey(win32con.HKEY_USERS, sid + '\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders') BarthaM@221: value, _ = win32api.RegQueryValueEx(key, "AppData") BarthaM@143: win32api.RegCloseKey(key) BarthaM@143: return value mihai@237: mihai@237: def getAppDataDir(self): mihai@237: user = self.getActiveUserName() mihai@237: if user == None: mihai@237: logger.error("Cannot get active user name") mihai@237: raise OpenSecurityException("Cannot get active user name") mihai@237: else: mihai@237: logger.info('Got active user name ' + user) mihai@237: sid = self.getUserSID(user) mihai@237: if sid == None: mihai@237: logger.error("Cannot get SID for active user") mihai@237: raise OpenSecurityException("Cannot get SID for active user") mihai@237: else: mihai@237: logger.info("Got active user SID " + sid + " for user " + user) mihai@237: mihai@237: path = self.getAppDataDirReg(sid) mihai@237: if path == None: mihai@237: logger.error("Cannot get AppDataDir for active user") mihai@237: raise OpenSecurityException("Cannot get AppDataDir for active user") mihai@237: else: mihai@237: logger.info("Got AppData dir for user " + user + ': ' + path) mihai@237: mihai@237: return Cygwin.cygPath(path) BarthaM@143: BarthaM@221: #import initial template BarthaM@221: def importTemplate(self, image_path): BarthaM@221: import_logger.info('Stopping Opensecurity...') BarthaM@221: self.stop() BarthaM@221: BarthaM@221: import_logger.info('Cleaning up system in preparation for import...') BarthaM@221: self.cleanup() BarthaM@221: BarthaM@221: import_logger.info('Removing template SDVM...') BarthaM@221: # if template exists BarthaM@221: if self.vmRootName in self.listVMS(): BarthaM@221: # shutdown template if running BarthaM@221: self.poweroffVM(self.vmRootName) BarthaM@221: # detach and remove VDisk BarthaM@221: tmplateUUID = self.getVDiskUUID(self.templateImage) BarthaM@221: if tmplateUUID != None: BarthaM@221: logger.debug('Found template VDisk uuid ' + tmplateUUID) BarthaM@221: controller = self.getVDiskController(self.vmRootName) BarthaM@221: if controller: BarthaM@221: controller = controller.split('-') BarthaM@221: self.detachVDisk(self.vmRootName, controller[0], controller[1], controller[2]) BarthaM@221: self.removeSnapshots(tmplateUUID) BarthaM@221: self.removeImage(tmplateUUID) BarthaM@221: else: BarthaM@221: logger.info('Template uuid not found') BarthaM@221: # remove VM BarthaM@221: self.removeVM(self.vmRootName) BarthaM@221: # remove template VM folder BarthaM@221: self.removeVMFolder(self.vmRootName) BarthaM@221: import_logger.info('Cleanup finished...') BarthaM@221: BarthaM@221: import_logger.info('Checking privileges...') BarthaM@221: result = Cygwin.bashExecute('id -G') BarthaM@221: if '544' not in result[1]: BarthaM@221: import_logger.debug('Insufficient privileges.') BarthaM@221: import_logger.debug("Trying to continue...") BarthaM@221: BarthaM@221: # check OpenSecurity Initial VM Image BarthaM@221: import_logger.debug('Looking for VM image: ' + image_path) BarthaM@221: result = os.path.isfile(image_path) BarthaM@221: BarthaM@221: if not result: BarthaM@221: import_logger.debug('Warning: no OpenSecurity Initial Image found.') BarthaM@221: import_logger.debug('Please download using the OpenSecurity download tool.') BarthaM@221: raise OpenSecurityException('OpenSecurity Initial Image not found.') BarthaM@221: logger.debug('Initial VM image: ' + image_path + ' found') BarthaM@221: BarthaM@221: if not self.template_installed(): BarthaM@221: import_logger.info('Importing SDVm template: ' + image_path) BarthaM@221: Cygwin.vboxExecute('import "' + image_path + '" --vsys 0 --vmname ' + VMManager.vmRootName + ' --unit 12 --disk "' + self.templateImage + '"') BarthaM@221: else: BarthaM@221: import_logger.info('Found ' + VMManager.vmRootName + ' already present in VBox reusing it.') BarthaM@221: import_logger.info('if you want a complete new import please remove the VM first.') BarthaM@221: import_logger.info('starting OpenSecurity service...') BarthaM@221: return mb@90: BarthaM@221: # remove unnecessary IDE controller BarthaM@221: Cygwin.vboxExecute('storagectl ' + VMManager.vmRootName + ' --name IDE --remove') BarthaM@221: BarthaM@221: info = self.getVDiskController(VMManager.vmRootName, self.templateImage) BarthaM@221: if info: BarthaM@221: info = info.split('-') BarthaM@221: self.detachVDisk(VMManager.vmRootName, info[0], info[1], info[2]) BarthaM@221: BarthaM@221: self.changeVDiskType(self.templateImage, 'immutable') BarthaM@221: self.attachVDisk(VMManager.vmRootName, info[0], info[1], info[2], self.templateImage) BarthaM@221: import_logger.info('Initial import finished.') BarthaM@221: BarthaM@221: # update template BarthaM@221: def updateTemplate(self): BarthaM@221: import_logger.debug('Stopping Opensecurity...') BarthaM@221: self.stop() BarthaM@221: BarthaM@221: import_logger.debug('Cleaning up system in preparation for update...') BarthaM@221: self.cleanup() BarthaM@221: BarthaM@221: import_logger.info('Cleanup finished...') BarthaM@221: BarthaM@221: # shutdown template if running BarthaM@221: self.poweroffVM(self.vmRootName) BarthaM@221: BarthaM@221: import_logger.info('Starting template VM...') BarthaM@221: # check for updates BarthaM@221: self.genCertificate(self.vmRootName) BarthaM@221: self.attachCertificate(self.vmRootName) BarthaM@221: BarthaM@221: import_logger.info('Removing snapshots...') BarthaM@221: BarthaM@221: self.detachVDisk(self.vmRootName, 'SATA', '0', '0') BarthaM@221: templateUUID = self.getVDiskUUID(self.templateImage) BarthaM@221: self.removeSnapshots(templateUUID) BarthaM@221: BarthaM@221: import_logger.info('Setting VDisk image to normal...') BarthaM@221: self.changeVDiskType(self.templateImage, 'normal') BarthaM@221: self.attachVDisk(self.vmRootName, 'SATA', '0', '0', self.templateImage) BarthaM@221: BarthaM@221: import_logger.info('Starting VM...') BarthaM@221: self.startVM(self.vmRootName) BarthaM@221: self.waitStartup(self.vmRootName) BarthaM@221: BarthaM@221: import_logger.info('Updating components...') BarthaM@221: tmp_ip = self.getHostOnlyIP(self.vmRootName) mihai@252: mihai@252: Cygwin.sshExecute('"sudo apt-get -y update"', tmp_ip, 'osecuser', self.getCertificatePath(self.vmRootName) ) mihai@252: Cygwin.sshExecute('"sudo apt-get -y dist-upgrade"', tmp_ip, 'osecuser', self.getCertificatePath(self.vmRootName) ) BarthaM@221: BarthaM@221: import_logger.info('Restarting template VM...') BarthaM@221: #check if reboot is required mihai@252: result = Cygwin.sshExecute('"if [ -f /var/run/reboot-required ]; then echo \\\"Yes\\\"; fi"', tmp_ip, 'osecuser', self.getCertificatePath(self.vmRootName) ) BarthaM@221: if "Yes" in result[1]: BarthaM@221: self.stopVM(self.vmRootName) BarthaM@221: self.waitShutdown(self.vmRootName) BarthaM@221: self.startVM(self.vmRootName) BarthaM@221: self.waitStartup(self.vmRootName) BarthaM@221: BarthaM@221: import_logger.info('Stopping template VM...') BarthaM@221: self.stopVM(self.vmRootName) BarthaM@221: self.waitShutdown(self.vmRootName) BarthaM@221: BarthaM@221: import_logger.info('Setting VDisk image to immutable...') BarthaM@221: self.detachVDisk(self.vmRootName, 'SATA', '0', '0') BarthaM@221: self.changeVDiskType(self.templateImage, 'immutable') BarthaM@221: self.attachVDisk(self.vmRootName, 'SATA', '0', '0', self.templateImage) BarthaM@221: BarthaM@221: import_logger.info('Update template finished...') BarthaM@221: BarthaM@221: def startInitialImport(self): BarthaM@221: if self.importHandler and self.importHandler.isAlive(): BarthaM@221: import_logger.info("Initial import already running.") BarthaM@221: return BarthaM@221: self.importHandler = InitialImportHandler(self) BarthaM@221: self.importHandler.start() BarthaM@221: import_logger.info("Initial import started.") BarthaM@221: BarthaM@221: def startUpdateTemplate(self): BarthaM@221: if self.updateHandler and self.updateHandler.isAlive(): mihai@237: import_logger.info("Template update already running.") BarthaM@221: return BarthaM@221: self.updateHandler = UpdateHandler(self) BarthaM@221: self.updateHandler.start() mihai@237: import_logger.info("Template update started.") BarthaM@234: mihai@237: def createSession(self, browsing=False): BarthaM@234: new_sdvm = self.newSDVM() BarthaM@234: self.attachVDisk(new_sdvm, 'SATA', '0', '0', self.templateImage) BarthaM@234: self.genCertificate(new_sdvm) BarthaM@234: self.attachCertificate(new_sdvm) BarthaM@234: self.startVM(new_sdvm) BarthaM@234: new_ip = self.waitStartup(new_sdvm) BarthaM@234: if new_ip == None: BarthaM@234: logger.error("Error getting IP address of SDVM. Cleaning up.") BarthaM@234: self.poweroffVM(new_sdvm) BarthaM@234: self.removeVM(new_sdvm) BarthaM@234: return None BarthaM@234: else: BarthaM@234: logger.info("Got IP address for " + new_sdvm + ' ' + new_ip) mihai@237: self.vms[new_sdvm] = {'vm_name' : new_sdvm, 'ip_addr' : new_ip, 'used' : False, 'running' : True, 'browsing' : browsing } mihai@237: if browsing: mihai@237: # restore browser settings mihai@237: appDataDir = self.getAppDataDir() mihai@237: logger.info("Restoring browser settings in AppData dir " + appDataDir) mihai@237: # create OpenSecurity settings dir on local machine user home /AppData/Roaming mihai@237: Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + appDataDir + '/OpenSecurity\\\"') mihai@237: # create chromium settings dir on local machine if not existing mihai@237: Cygwin.bashExecute('/usr/bin/mkdir -p \\\"' + appDataDir + '/OpenSecurity/chromium\\\"') mihai@237: # create chromium settings dir on remote machine if not existing mihai@252: Cygwin.sshExecute('"mkdir -p \\\"/home/osecuser/.config\\\""', new_ip, 'osecuser', self.getCertificatePath(new_sdvm)) mihai@252: # restore settings to svm from active user settings dir mihai@252: # self.restoreFile(new_sdvm, new_ip, appDataDir + '/OpenSecurity/chromium', '/home/osecuser/.config/') mihai@252: self.syncRemoteFile(new_sdvm, new_ip, appDataDir + '/OpenSecurity/chromium', '/home/osecuser/.config/') BarthaM@234: return self.vms[new_sdvm] BarthaM@234: BarthaM@234: def releaseSession(self, vm_name): BarthaM@234: del self.vms[vm_name] BarthaM@234: self.poweroffVM(vm_name) BarthaM@234: self.removeVM(vm_name) BarthaM@234: self.sdvmFactory.trigger() BarthaM@234: mihai@237: def getSession(self, browsing = False): BarthaM@234: # return first found unused SDVM BarthaM@234: for vm in self.vms.values(): mihai@237: if vm['used'] == False and vm['browsing'] == browsing: BarthaM@234: vm['used'] = True BarthaM@234: self.sdvmFactory.trigger() BarthaM@234: return vm mihai@252: return self.createSession(browsing) mihai@237: mihai@237: def backupFile(self, vm_name, ip_addr, src, dest): mihai@237: global backup_lock mihai@237: with backup_lock: mihai@252: certificate = self.getCertificatePath(vm_name) mihai@237: command = '-r -o StrictHostKeyChecking=no -i "' + certificate + '" "osecuser@' + ip_addr + ':' + src + '" "' + dest + '"' mihai@237: return Cygwin.execute(Cygwin.cygwin_scp, command, wait_return=True, window=False) mihai@237: mihai@237: def restoreFile(self, vm_name, ip_addr, src, dest): mihai@252: certificate = self.getCertificatePath(vm_name) mihai@237: command = '-r -o StrictHostKeyChecking=no -i "' + certificate + '" "' + src + '" "osecuser@' + ip_addr + ':' + dest + '"' mihai@237: return Cygwin.execute(Cygwin.cygwin_scp, command, wait_return=True, window=False) mihai@237: mihai@252: def syncRemoteFile(self, vm_name, ip_addr, src, dest): mihai@252: certificate = self.getCertificatePath(vm_name) mihai@252: command = '-av -e "\\"' + Cygwin.cygwin_ssh + '\\" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i \\"' + certificate + '\\"" "' + src + '" "osecuser@' + ip_addr + ':' + dest + '"' mihai@252: return Cygwin.execute(Cygwin.cygwin_rsync, command, wait_return=True, window=False) mihai@252: mihai@252: def syncLocalFile(self, vm_name, ip_addr, src, dest): mihai@252: global backup_lock mihai@252: with backup_lock: mihai@252: certificate = self.getCertificatePath(vm_name) mihai@252: command = '-av -e "\\"' + Cygwin.cygwin_ssh + '\\" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i \\"' + certificate + '\\"" "osecuser@' + ip_addr + ':' + src + '" "' + dest + '"' mihai@252: return Cygwin.execute(Cygwin.cygwin_rsync, command, wait_return=True, window=False) BarthaM@234: BarthaM@234: class SDVMFactory(threading.Thread): BarthaM@234: vmm = None BarthaM@234: running = True BarthaM@234: triggerEv = None BarthaM@234: BarthaM@234: def __init__(self, vmmanager): BarthaM@234: threading.Thread.__init__(self) BarthaM@234: self.vmm = vmmanager BarthaM@234: self.triggerEv = threading.Event() BarthaM@234: BarthaM@234: def run(self): BarthaM@234: while self.running: BarthaM@234: self.triggerEv.clear() BarthaM@234: mihai@237: # find existance of free device and browsing sessions mihai@237: freeDeviceSession = False mihai@237: freeBrowsingSession = False BarthaM@234: for vm in self.vmm.vms.values(): mihai@237: if vm['used'] == False and vm['browsing'] == False: mihai@237: freeDeviceSession = True mihai@237: if vm['used'] == False and vm['browsing'] == True: mihai@237: freeBrowsingSession = True mihai@237: mihai@237: #prepare new sessions if none mihai@237: if not freeDeviceSession: mihai@237: self.vmm.createSession(False) mihai@237: if not freeBrowsingSession: mihai@237: self.vmm.createSession(True) BarthaM@234: self.triggerEv.wait() BarthaM@234: mihai@237: def trigger(self, ): BarthaM@234: self.triggerEv.set() BarthaM@234: BarthaM@234: def stop(self): BarthaM@234: self.running = False BarthaM@234: self.triggerEv.set() BarthaM@234: BarthaM@234: #handles browsing session creation BarthaM@234: class BrowsingHandler(threading.Thread): BarthaM@234: vmm = None BarthaM@234: proxy = None BarthaM@234: wpad = None BarthaM@234: net_resource = None BarthaM@234: ip_addr = None BarthaM@234: vm_name = None BarthaM@234: BarthaM@234: def __init__(self, vmmanager, proxy, wpad): BarthaM@234: threading.Thread.__init__(self) BarthaM@234: self.vmm = vmmanager BarthaM@234: self.proxy = proxy BarthaM@234: self.wpad = wpad BarthaM@234: BarthaM@234: def run(self): BarthaM@234: session = None BarthaM@234: try: BarthaM@234: session = self.vmm.getSession() BarthaM@234: if not session: BarthaM@234: raise OpenSecurityException("Could not get new SDVM session.") BarthaM@234: BarthaM@234: self.ip_addr = session['ip_addr'] BarthaM@234: self.vm_name = session['vm_name'] BarthaM@234: BarthaM@234: self.net_resource = '\\\\' + self.ip_addr + '\\Download' mihai@237: urllib2.urlopen('http://127.0.0.1:8090/netmount?'+'net_resource='+self.net_resource)#.readline() BarthaM@234: mihai@252: # synchronize browser settings mihai@252: appDataDir = self.vmm.getAppDataDir() mihai@252: logger.info("Syncing browser settings in AppData dir " + appDataDir) mihai@252: # sync settings on vm mihai@252: self.vmm.syncRemoteFile(self.vm_name, self.ip_addr, appDataDir + '/OpenSecurity/chromium', '/home/osecuser/.config/') mihai@252: BarthaM@234: if self.wpad: BarthaM@234: browser = '\\\"/usr/bin/chromium --proxy-pac-url=\\\"'+self.wpad+'\\\"\\\"' BarthaM@234: elif self.proxy: BarthaM@234: browser = '\\\"export http_proxy='+self.proxy+'; /usr/bin/chromium\\\"' BarthaM@234: else: BarthaM@234: browser = '\\\"/usr/bin/chromium\\\"' BarthaM@234: mihai@252: Cygwin.sshExecuteX11(browser, self.ip_addr, 'osecuser', self.vmm.getCertificatePath(self.vm_name)) mihai@237: appDataDir = self.vmm.getAppDataDir() mihai@252: # self.vmm.backupFile(self.vm_name, self.ip_addr, '/home/osecuser/.config/chromium', appDataDir + '/OpenSecurity/') mihai@252: self.vmm.syncLocalFile(self.vm_name, self.ip_addr, '/home/osecuser/.config/chromium', appDataDir + '/OpenSecurity/') BarthaM@234: BarthaM@234: except urllib2.URLError: BarthaM@234: logger.error("Network drive connect failed. OpenSecurity Tray client not running.") BarthaM@234: self.net_resource = None BarthaM@234: BarthaM@234: except: BarthaM@234: logger.info("BrowsingHandler failed. See log for details") BarthaM@234: BarthaM@234: if session: BarthaM@234: if self.net_resource == None: BarthaM@234: logger.info("Missing browsing SDVM's network share. Skipping disconnect") BarthaM@234: else: BarthaM@234: try: BarthaM@234: urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'net_resource='+self.net_resource).readline() BarthaM@234: self.net_resource = None BarthaM@234: except urllib2.URLError: BarthaM@234: logger.error("Network share disconnect failed. OpenSecurity Tray client not running.") BarthaM@234: if self.vm_name: BarthaM@234: self.vmm.releaseSession(self.vm_name) BarthaM@234: BarthaM@234: self.vmm.sdvmFactory.trigger() BarthaM@234: BarthaM@234: BarthaM@234: class DeviceHandler(threading.Thread): BarthaM@234: vmm = None BarthaM@234: existingRSDs = None BarthaM@234: attachedRSDs = None BarthaM@234: running = True BarthaM@234: def __init__(self, vmmanger): BarthaM@234: threading.Thread.__init__(self) BarthaM@234: self.vmm = vmmanger BarthaM@234: BarthaM@234: def stop(self): BarthaM@234: self.running = False BarthaM@234: BarthaM@234: def run(self): BarthaM@234: self.existingRSDs = dict() BarthaM@234: self.attachedRSDs = self.vmm.getAttachedRSDs() BarthaM@234: BarthaM@234: while self.running: BarthaM@234: tmp_rsds = self.vmm.getExistingRSDs() BarthaM@234: if tmp_rsds.keys() == self.existingRSDs.keys(): BarthaM@234: logger.debug("Nothing's changed. sleep(3)") BarthaM@234: time.sleep(3) BarthaM@234: continue BarthaM@234: BarthaM@234: showTrayMessage('System changed.\nEvaluating...', 7000) BarthaM@234: logger.info("Something's changed") BarthaM@234: BarthaM@234: tmp_attached = self.attachedRSDs BarthaM@234: for vm_name in tmp_attached.keys(): BarthaM@234: if tmp_attached[vm_name] not in tmp_rsds.values(): BarthaM@234: ip = self.vmm.getHostOnlyIP(vm_name) BarthaM@234: if ip == None: BarthaM@234: logger.error("Failed getting hostonly IP for " + vm_name) BarthaM@234: continue BarthaM@234: BarthaM@234: try: BarthaM@234: net_resource = '\\\\' + ip + '\\USB' BarthaM@234: result = urllib2.urlopen('http://127.0.0.1:8090/netumount?'+'net_resource='+net_resource).readline() BarthaM@234: except urllib2.URLError: BarthaM@234: logger.error("Network drive disconnect failed. OpenSecurity Tray client not running.") BarthaM@234: continue BarthaM@234: BarthaM@234: # detach not necessary as already removed from vm description upon disconnect BarthaM@234: del self.attachedRSDs[vm_name] BarthaM@234: self.vmm.releaseSession(vm_name) BarthaM@234: BarthaM@234: #create new vms for new devices if any BarthaM@234: new_ip = None BarthaM@234: for new_device in tmp_rsds.values(): BarthaM@234: showTrayMessage('Mounting device...', 7000) BarthaM@234: if (self.attachedRSDs and False) or (new_device not in self.attachedRSDs.values()): BarthaM@234: BarthaM@234: session = self.vmm.getSession() BarthaM@234: if not session: BarthaM@234: logger.info("Could not get new SDVM session.") BarthaM@234: continue BarthaM@234: #raise OpenSecurityException("Could not get new SDVM session.") BarthaM@234: new_sdvm = session['vm_name'] BarthaM@234: new_ip = session['ip_addr'] BarthaM@234: try: BarthaM@234: self.vmm.attachRSD(new_sdvm, new_device) BarthaM@234: self.attachedRSDs[new_sdvm] = new_device BarthaM@234: except: BarthaM@234: logger.info("RSD prematurely removed. Cleaning up.") BarthaM@234: self.vmm.releaseSession(new_sdvm) BarthaM@234: continue BarthaM@234: BarthaM@234: try: BarthaM@234: net_resource = '\\\\' + new_ip + '\\USB' BarthaM@234: result = urllib2.urlopen('http://127.0.0.1:8090/netmount?'+'net_resource='+net_resource).readline() BarthaM@234: except urllib2.URLError: BarthaM@234: logger.error("Network drive connect failed (tray client not accessible). Cleaning up.") BarthaM@234: self.vmm.releaseSession(new_sdvm) BarthaM@234: continue BarthaM@234: BarthaM@234: self.existingRSDs = tmp_rsds BarthaM@221: BarthaM@221: class UpdateHandler(threading.Thread): BarthaM@221: vmm = None BarthaM@221: def __init__(self, vmmanager): BarthaM@221: threading.Thread.__init__(self) BarthaM@221: self.vmm = vmmanager BarthaM@221: BarthaM@221: def run(self): BarthaM@221: try: BarthaM@221: self.vmm.updateTemplate() BarthaM@221: except: BarthaM@221: import_logger.info("Update template failed. Refer to service log for details.") BarthaM@221: self.vmm.start(force=True) BarthaM@221: BarthaM@221: class InitialImportHandler(threading.Thread): BarthaM@221: vmm = None BarthaM@221: def __init__(self, vmmanager): BarthaM@221: threading.Thread.__init__(self) BarthaM@221: self.vmm = vmmanager BarthaM@221: BarthaM@221: def run(self): BarthaM@221: try: BarthaM@221: self.vmm.importTemplate(self.vmm.getMachineFolder() + '\\OsecVM.ova') BarthaM@221: self.vmm.updateTemplate() BarthaM@221: except: BarthaM@221: import_logger.info("Initial import failed. Refer to service log for details.") oliver@240: self.vmm.start(force=True)