2 Created on Nov 19, 2013
8 from subprocess import Popen, PIPE, call
20 def __init__(self, vendorid, productid, revision):
21 self.vendorid = vendorid.lower()
22 self.productid = productid.lower()
23 self.revision = revision.lower()
26 def __eq__(self, other):
27 return self.vendorid == other.vendorid and self.productid == other.productid and self.revision == other.revision
30 return hash(self.vendorid) ^ hash(self.productid) ^ hash(self.revision)
33 return "VendorId = \'" + str(self.vendorid) + "\' ProductId = \'" + str(self.productid) + "\' Revision = \'" + str(self.revision) + "\'"
36 class VMManager(object):
37 vmRootName = "SecurityDVM"
38 systemProperties = None
39 cygwin_path = 'c:\\cygwin64\\bin\\'
42 self.systemProperties = self.getSystemProperties()
43 #TODO: get cygwin path externally
46 def execute(self, cmd):
48 print('trying to launch: ' + cmd)
49 process = Popen(cmd, stdout=PIPE, stderr=PIPE)
51 print('launched: ' + cmd)
52 result = process.wait()
53 res_stdout = process.stdout.read();
54 res_stderr = process.stderr.read();
60 return result, res_stdout, res_stderr
62 def getSystemProperties(self):
63 cmd = 'VBoxManage list systemproperties'
64 result = self.execute(cmd)
67 props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result[1].strip().splitlines()))
70 def getDefaultMachineFolder(self):
71 return self.systemProperties["Default machine folder"]
73 def createVM(self, vm_name):
74 hostonly_if = self.getHostOnlyIFs()
75 cmd = 'VBoxManage createvm --name ' + vm_name, ' --ostype Debian --register'
77 cmd = 'VBoxManage modifyvm ' + vm_name + ' --memory 512 --vram 10 --cpus 1 --usb on --usbehci on --nic1 hostonly --hostonlyadapter1 ' + hostonly_if['Name'] + ' --nic2 nat'
79 cmd = 'VBoxManage storagectl ' + vm_name + ' --name contr1 --add sata --sataportcount 2'
81 cmd = 'VBoxManage storageattach ' + vm_name + ' --storagectl contr1 --port 0 --device 0 --type hdd --mtype normal --medium C:\Users\BarthaM\VirtualBox VMs\SecurityVM\SecurityVM.vdi'
85 def attachRSD(self, vm_name, filter):
86 cmd = 'VBoxManage usbfilter add 0 --target ' + vm_name + ' --name OpenSecurityRSD --vendorid ' + filter.vendorid + ' --productid ' + filter.productid + ' --revision ' + filter.revision
87 print self.execute(cmd)
89 def removeVM(self, vm_name):
90 print('removing ' + vm_name)
91 cmd = 'VBoxManage unregistervm', vm_name, '--delete'
92 print self.execute(cmd)
93 machineFolder = self.getDefaultMachineFolder()
94 cmd = self.cygwin_path+'bash.exe --login -c \"rm -rf ' + machineFolder + '\\' + vm_name + '*\"'
95 print self.execute(cmd)
97 def startVM(self, vm_name):
98 print('starting ' + vm_name)
99 cmd = 'VBoxManage startvm ' + vm_name + ' --type headless'
100 print self.execute(cmd)
102 def stopVM(self, vm_name):
103 print('stopping ' + vm_name)
104 cmd = 'VBoxManage controlvm ' + vm_name + ' poweroff'
105 print self.execute(cmd)
107 def getHostOnlyIFs(self):
108 cmd = ['VBoxManage list hostonlyifs']
109 result = self.execute(cmd)
112 props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result.strip().splitlines()))
115 def getHostOnlyIP(self, vm_name):
116 print('gettting hostOnly IP address ' + vm_name)
117 cmd = 'VBoxManage guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP'
118 result = self.execute(cmd)
122 return result[result.index(':')+1:].strip()
125 cmd = 'VBoxManage list usbhost'
126 results = self.execute(cmd)
127 results = results.split('Host USB Devices:')[1].strip()
129 items = list( "UUID:"+result for result in results.split('UUID:') if result != '')
133 for line in item.splitlines():
135 k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip()
138 if 'Product' in props.keys() and props['Product'] == 'Mass Storage':
139 usb_filter = USBFilter( re.search(r"\((?P<vid>[0-9A-Fa-f]+)\)", props['VendorId']).groupdict()['vid'],
140 re.search(r"\((?P<pid>[0-9A-Fa-f]+)\)", props['ProductId']).groupdict()['pid'],
141 re.search(r"\((?P<rev>[0-9A-Fa-f]+)\)", props['Revision']).groupdict()['rev'] )
142 rsds[props['UUID']] = usb_filter;
148 cmd = 'VBoxManage list vms'
149 result = self.execute(cmd)
150 vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines()))
157 if vm.startswith(self.vmRootName) and vm != self.vmRootName:
161 def generateSDVMName(self):
163 for i in range(0,999):
164 if(not self.vmRootName+str(i) in vms):
165 return self.vmRootName+str(i)
168 def getVMInfo(self, vm_name):
169 cmd = 'VBoxManage showvminfo ' + vm_name + ' --machinereadable'
170 results = self.execute(cmd)
171 props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split('=', 1) for line in results.splitlines()))
174 def getUSBFilter(self, vm_name):
175 props = self.getVMInfo(vm_name)
176 keys = set(['USBFilterVendorId1', 'USBFilterProductId1', 'USBFilterRevision1'])
177 keyset = set(props.keys())
179 if keyset.issuperset(keys):
180 usb_filter = USBFilter(props['USBFilterVendorId1'], props['USBFilterProductId1'], props['USBFilterRevision1'])
183 def getAttachedRSDs(self):
184 vms = self.listSDVM()
185 attached_devices = dict()
187 filter = self.getUSBFilter(vm)
189 attached_devices[vm] = filter
190 return attached_devices
192 #generates ISO containing authorized_keys for use with guest VM
193 def genCertificateISO(self, vm_name):
194 machineFolder = self.getDefaultMachineFolder()
195 # create .ssh folder in vm_name
196 cmd = self.cygwin_path+'bash.exe --login -c \"mkdir -p \\\"' + machineFolder + '\\' + vm_name + '\\.ssh\\\"\"'
197 result = self.execute(cmd)
198 # generate dvm_key pair in vm_name / .ssh
199 cmd = self.cygwin_path+'bash.exe --login -c \"ssh-keygen -q -t rsa -N \\"\\" -C \\\"' + vm_name + '\\\" -f \\\"' + machineFolder + '\\' + vm_name + '\\.ssh\\dvm_key\\\"\"' #'echo -e "y\\n" |',
200 result = self.execute(cmd)
201 # set permissions for keys
202 #TODO: test without chmod
203 cmd = self.cygwin_path+'bash.exe --login -c \"chmod 500 \\\"' + machineFolder + '\\' + vm_name + '\\.ssh\\*\\\"\"'
204 result = self.execute(cmd)
205 # move out private key
206 cmd = self.cygwin_path+'bash.exe --login -c \"mv \\\"' + machineFolder + '\\' + vm_name + '\\.ssh\\dvm_key\\\" \\\"' + machineFolder + '\\' + vm_name + '\\\"'
207 result = self.execute(cmd)
208 # rename public key to authorized_keys
209 cmd = self.cygwin_path+'bash.exe --login -c \"mv \\\"' + machineFolder + '\\' + vm_name + '\\.ssh\\dvm_key.pub\\\" \\\"' + machineFolder + '\\' + vm_name + '\\.ssh\\authorized_keys\\\"'
210 result = self.execute(cmd)
211 # generate iso image with .ssh/authorized keys
212 cmd = self.cygwin_path+'bash.exe --login -c \"/usr/bin/genisoimage -J -R -o \\\"' + machineFolder + '\\' + vm_name + '\\'+ vm_name + '.iso\\\" \\\"' + machineFolder + '\\' + vm_name + '\\.ssh\\\"\"'
213 result = self.execute(cmd)
215 # attaches generated ssh public cert to guest vm
216 def attachCertificateISO(self, vm_name):
217 machineFolder = self.getDefaultMachineFolder()
218 cmd = 'vboxmanage storageattach ' + vm_name + ' --storagectl contr1 --port 1 --device 0 --type dvddrive --mtype readonly --medium \"' + machineFolder + '\\' + vm_name + '\\'+ vm_name + '.iso\"'
219 result = self.execute(cmd)
222 # handles device change events
223 def handleDeviceChange(self):
224 attached_devices = self.getAttachedRSDs()
225 connected_devices = self.listRSDS()
226 for vm_name in attached_devices.keys():
227 if attached_devices[vm_name] not in connected_devices.values():
229 self.removeVM(vm_name)
231 attached_devices = self.getAttachedRSDs()
232 for connected_device in connected_devices.values():
233 if connected_device not in attached_devices.values():
234 new_sdvm = self.generateSDVMName()
235 self.createVM(new_sdvm)
236 self.genCertificateISO(new_sdvm)
237 self.attachCertificateISO(new_sdvm)
238 self.attachRSD(new_sdvm, connected_device)
239 self.startVM(new_sdvm)
241 # executes command over ssh on guest vm
242 def sshGuestExecute(self, vm_name, prog):
244 address = self.getHostOnlyIP(vm_name)
245 machineFolder = self.getDefaultMachineFolder()
247 cmd = self.cygwin_path+'bash.exe --login -c \"ssh -i \\\"' + machineFolder + '\\' + vm_name + '\\dvm_key\\\" bartham@' + address + ' ' + prog + '\"'
248 return self.execute(cmd)
250 # executes command over ssh on guest vm with X forwarding
251 def sshGuestX11Execute(self, vm_name, prog):
252 #TODO: verify if X server is running on user account
253 #TODO: set DISPLAY accordingly
254 address = self.getHostOnlyIP(vm_name)
255 machineFolder = self.getDefaultMachineFolder()
257 cmd = self.cygwin_path+'bash.exe --login -c \"DISPLAY=:0 ssh -Y -i \\\"' + machineFolder + '\\' + vm_name + '\\dvm_key\\\" bartham@' + address + ' ' + prog + '\"'
258 return self.execute(cmd)
260 # configures hostonly networking and DHCP server
261 # requires admin rights
262 def configureHostNetworking(self):
263 #cmd = 'vboxmanage list hostonlyifs'
265 #cmd = 'vboxmanage hostonlyif remove \"VirtualBox Host-Only Ethernet Adapter\"'
267 #cmd = 'vboxmanage hostonlyif create'
269 cmd = 'vboxmanage hostonlyif ipconfig \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.1 --netmask 255.255.255.0'
271 #cmd = 'vboxmanage dhcpserver add'
273 cmd = 'vboxmanage dhcpserver modify --ifname \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.1 --netmask 255.255.255.0 --lowerip 192.168.56.100 --upperip 192.168.56.255'
276 # executes NET USE and connects to samba share on guestos
277 def netUse(self, vm_name):
278 ip = self.getHostOnlyIP(vm_name)
279 cmd = 'net use H: \\' + ip + '\RSD_Device'
280 return self.execute(cmd)
283 if __name__ == '__main__':
285 man.cygwin_path = 'c:\\cygwin64\\bin\\'
286 #man.handleDeviceChange()
287 #print man.listSDVM()
288 man.configureHostNetworking()
289 vm_name = "SecurityDVM0"
290 man.genCertificateISO(vm_name)
291 #man.attachCertificateISO(vm_name)
292 #man.sshGuestExecute(vm_name, "ls")
293 #man.sshGuestX11Execute(vm_name, "iceweasel")
294 #cmd = "c:\\cygwin64\\bin\\bash.exe --login -c \"/bin/ls\""