# HG changeset patch # User om # Date 1386323475 -3600 # Node ID 4457d7071a2377addc6683120a881d656c0e317b # Parent 27aa3b16eebb9b0b8106123ac69dd3b95dbdc2ff adopted server code and merged client into "bin" diff -r 27aa3b16eebb -r 4457d7071a23 OpenSecurity/bin/about.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenSecurity/bin/about.py Fri Dec 06 10:51:15 2013 +0100 @@ -0,0 +1,126 @@ +#!/bin/env python +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------ +# about-dialog +# +# tell the user about the project +# +# Autor: Oliver Maurhart, +# +# Copyright (C) 2013 AIT Austrian Institute of Technology +# AIT Austrian Institute of Technology GmbH +# Donau-City-Strasse 1 | 1220 Vienna | Austria +# http://www.ait.ac.at +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# ------------------------------------------------------------ + + +# ------------------------------------------------------------ +# imports + +import os + +from PyQt4 import QtCore +from PyQt4 import QtGui + +# local +from environment import Environment + +# ------------------------------------------------------------ +# vars + + +ABOUT_TEXT = """ + + + +
+

+ +

+

OpenSecurity

+

+

+

+Blah ...
+ +

+Copyright (C) 2013, AIT Austrian Institute of Technology
+AIT Austrian Institute of Technology GmbH
+Donau-City-Strasse 1 | 1220 Vienna | Austria
+http://www.ait.ac.at +

+ + + + +"""; + + +# ------------------------------------------------------------ +# code + + +class About(QtGui.QDialog): + + """Show some about stuff.""" + + def __init__(self, parent = None, flags = QtCore.Qt.WindowFlags(0)): + + # super call and widget init + super(About, self).__init__(parent, flags) + + # setup image search path + QtCore.QDir.setSearchPaths("image", QtCore.QStringList(os.path.join(Environment('opensecurity').data_path, '..', 'gfx'))); + + self.setWindowTitle('About OpenSecuirty ...') + self.setup_ui() + + + def setup_ui(self): + + """Create the widgets.""" + + lyMain = QtGui.QVBoxLayout(self) + lyMain.setContentsMargins(8, 8, 8, 8) + + lbAbout = QtGui.QLabel() + lbAbout.setStyleSheet("QWidget { background: white; color: black; };") + lbAbout.setText(ABOUT_TEXT) + lbAbout.setContentsMargins(12, 12, 12, 12) + + scAbout = QtGui.QScrollArea() + scAbout.setWidget(lbAbout) + scAbout.viewport().setStyleSheet("QWidget { background: white; color: black; };") + lyMain.addWidget(scAbout) + + # buttons + lyButton = QtGui.QHBoxLayout() + lyMain.addLayout(lyButton) + + lyButton.addStretch(1) + btnOk = QtGui.QPushButton('&Ok', self) + btnOk.setMinimumWidth(100) + lyButton.addWidget(btnOk) + + # connectors + btnOk.clicked.connect(self.accept) + + # reduce to the max + self.setMinimumSize(400, 200) + self.resize(lyMain.minimumSize()) + diff -r 27aa3b16eebb -r 4457d7071a23 OpenSecurity/bin/credentials.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenSecurity/bin/credentials.py Fri Dec 06 10:51:15 2013 +0100 @@ -0,0 +1,160 @@ +#!/bin/env python +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------ +# credentials-dialog +# +# ask the user credentials +# +# Autor: Oliver Maurhart, +# +# Copyright (C) 2013 AIT Austrian Institute of Technology +# AIT Austrian Institute of Technology GmbH +# Donau-City-Strasse 1 | 1220 Vienna | Austria +# http://www.ait.ac.at +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# ------------------------------------------------------------ + + +# ------------------------------------------------------------ +# imports + +import sys + +from PyQt4 import QtCore +from PyQt4 import QtGui + +# local +from about import About + +# ------------------------------------------------------------ +# code + + +class Credentials(QtGui.QDialog): + + """Ask the user for credentials.""" + + def __init__(self, text, parent = None, flags = QtCore.Qt.WindowFlags(0)): + + super(Credentials, self).__init__(parent, flags) + self.setWindowTitle('OpenSecuirty Credentials Request') + self.setup_ui() + + # positionate ourself central + screen = QtGui.QDesktopWidget().screenGeometry() + self.resize(self.geometry().width() * 1.25, self.geometry().height()) + size = self.geometry() + self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2) + + # fix up text + self.lbText.setText(text) + + + def clicked_about(self): + """clicked the about button""" + dlgAbout = About() + dlgAbout.exec_() + + + def clicked_cancel(self): + """clicked the cancel button""" + self.reject() + + + def clicked_ok(self): + """clicked the ok button""" + sys.stdout.write('{ ') + sys.stdout.write('\'user\': \'') + sys.stdout.write(self.edUser.text()) + sys.stdout.write('\', ') + sys.stdout.write('\'password\': \'') + sys.stdout.write(self.edPassword.text()) + sys.stdout.write('\' ') + sys.stdout.write('}\n') + self.accept() + + + def setup_ui(self): + + """Create the widgets.""" + + lyMain = QtGui.QVBoxLayout(self) + lyMain.setContentsMargins(8, 8, 8, 8) + + # content area: left pixmap, right text + lyContent = QtGui.QHBoxLayout() + lyMain.addLayout(lyContent) + + # pixmap + lbPix = QtGui.QLabel() + lbPix.setPixmap(QtGui.QPixmapCache.find('opensecurity_icon_64')) + lyContent.addWidget(lbPix, 0, QtCore.Qt.Alignment(QtCore.Qt.AlignTop + QtCore.Qt.AlignHCenter)) + lyContent.addSpacing(16) + + # text ... + lyText = QtGui.QGridLayout() + lyContent.addLayout(lyText) + self.lbText = QtGui.QLabel() + lyText.addWidget(self.lbText, 0, 0, 1, 2) + + lbUser = QtGui.QLabel('&User:') + lyText.addWidget(lbUser, 1, 0) + self.edUser = QtGui.QLineEdit() + lyText.addWidget(self.edUser, 1, 1) + lbUser.setBuddy(self.edUser) + + lbPassword = QtGui.QLabel('&Password:') + lyText.addWidget(lbPassword, 2, 0) + self.edPassword = QtGui.QLineEdit() + self.edPassword.setEchoMode(QtGui.QLineEdit.Password) + lyText.addWidget(self.edPassword, 2, 1) + lbPassword.setBuddy(self.edPassword) + + lyText.addWidget(QtGui.QWidget(), 3, 0, 1, 2) + lyText.setColumnStretch(1, 1) + lyText.setRowStretch(3, 1) + + lyMain.addStretch(1) + + # buttons + lyButton = QtGui.QHBoxLayout() + lyMain.addLayout(lyButton) + + lyButton.addStretch(1) + btnOk = QtGui.QPushButton('&Ok', self) + btnOk.setDefault(True) + btnOk.setMinimumWidth(100) + lyButton.addWidget(btnOk) + btnCancel = QtGui.QPushButton('&Cancel', self) + btnCancel.setMinimumWidth(100) + lyButton.addWidget(btnCancel) + btnAbout = QtGui.QPushButton('&About', self) + btnAbout.setMinimumWidth(100) + lyButton.addWidget(btnAbout) + + button_width = max(btnOk.width(), btnCancel.width(), btnAbout.width()) + btnOk.setMinimumWidth(button_width) + btnCancel.setMinimumWidth(button_width) + btnAbout.setMinimumWidth(button_width) + + # reduce to the max + self.resize(lyMain.minimumSize()) + + # connectors + btnOk.clicked.connect(self.clicked_ok) + btnCancel.clicked.connect(self.clicked_cancel) + btnAbout.clicked.connect(self.clicked_about) diff -r 27aa3b16eebb -r 4457d7071a23 OpenSecurity/bin/cygwin.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenSecurity/bin/cygwin.py Fri Dec 06 10:51:15 2013 +0100 @@ -0,0 +1,105 @@ +#!/bin/env python +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------ +# cygwin command +# +# executes a cygwin command inside the opensecurity project +# +# Autor: Oliver Maurhart, +# +# Copyright (C) 2013 AIT Austrian Institute of Technology +# AIT Austrian Institute of Technology GmbH +# Donau-City-Strasse 1 | 1220 Vienna | Austria +# http://www.ait.ac.at +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# ------------------------------------------------------------ + + +# ------------------------------------------------------------ +# imports + +import os +import subprocess +import sys + +# local +from environment import Environment + + +# ------------------------------------------------------------ +# code + + +class Cygwin(object): + + """Some nifty methods working with Cygwin""" + + def __call__(self, command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE): + """make an instance of this object act as a function""" + return self.execute(command, stdin, stdout, stderr) + + + @staticmethod + def root(): + """get the path to our local cygwin installment""" + return os.path.join(Environment('OpenSecurity').prefix_path, '..', 'cygwin') + + + def execute(self, command, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE): + """execute a cygwin shell command + + command is list of arguments like ['/bin/ls', '-al', '-h'] + + a Popen object is returned""" + command_path = Cygwin.root() + os.sep.join(command[0].split('/')) + command = [command_path] + command[1:] + + return subprocess.Popen(command, shell = False, stdin = stdin, stdout = stdout, stderr = stderr) + + + @staticmethod + def is_X11_running(): + """check if we can connect to a X11 running instance""" + p = Cygwin()(['/bin/bash', '-c', 'DISPLAY=:0 /usr/bin/xset -q']) + stdout, stderr = p.communicate() + return p.returncode == 0 + + + @staticmethod + def start_X11(): + """start X11 in the background (if not already running) on DISPLAY=:0""" + + # do not start if already running + if Cygwin.is_X11_running(): + return + + # launch X11 (forget output and return immediately) + p = Cygwin()(['/bin/bash', '--login', '-i', '-c', ' X :0 -multiwindow'], stdin = None, stdout = None, stderr = None) + + +# start +if __name__ == "__main__": + + # execute what is given on the command line + c = Cygwin() + p = c(sys.argv[1:]) + + # wait until the process finished and grab the output + stdout, stderr = p.communicate() + print('=== call result on stdout: ===\n' + stdout) + print('=== call result on stderr: ===\n' + stderr) + diff -r 27aa3b16eebb -r 4457d7071a23 OpenSecurity/bin/environment.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenSecurity/bin/environment.py Fri Dec 06 10:51:15 2013 +0100 @@ -0,0 +1,103 @@ +#!/bin/env python +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------ +# environment.py +# +# pick some current environment infos +# +# Autor: Oliver Maurhart, +# +# Copyright (C) 2013 AIT Austrian Institute of Technology +# AIT Austrian Institute of Technology GmbH +# Donau-City-Strasse 1 | 1220 Vienna | Austria +# http://www.ait.ac.at +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# ------------------------------------------------------------ + + +# ------------------------------------------------------------ +# imports + +import os +import os.path +import sys + + +# ------------------------------------------------------------ +# code + + +class Environment(object): + + """Hold some nifty environment stuff in a dedicated class.""" + + def __init__(self, application = None): + + # if we ain't got a path to start from, all is valid/lost + if len(sys.path[0]) == 0: + self._prefix_path = '' + self._data_path = '' + return + + # the prefix path + # + # - on Linux: this is ../../ to the current executable + # e.g. "/usr/bin/myprogram" --> "/usr" + # + # - on Windows (inkl. Cygwin): this is the installation folder + # e.g. "C:/Program Files/MyProgram/myprogam" --> "C:/Program Files/MyProgram" + # + if sys.platform == 'linux2': + self._prefix_path = os.path.split(sys.path[0])[0] + elif sys.platform == 'win32' or sys.platform == 'cygwin': + self._prefix_path = sys.path[0] + + # the data path where all data files are stored + if sys.platform == 'linux2': + if not application is None: + self._data_path = os.path.join(self._prefix_path, os.path.join('share', application)) + else: + self._data_path = os.path.join(self._prefix_path, 'share') + elif sys.platform == 'win32' or sys.platform == 'cygwin': + self._data_path = self._prefix_path + + + def data_path_get(self): + """dat_path get""" + return self._data_path + + data_path = property(data_path_get) + + + def prefix_path_get(self): + """prefix_path get""" + return self._prefix_path + + prefix_path = property(prefix_path_get) + +# test method +def test(): + + """Test: class Environment""" + e = Environment('My Application') + print('prefix_path: "{0}"'.format(e.prefix_path)) + print(' data_path: "{0}"'.format(e.data_path)) + + +# test the module +if __name__ == '__main__': + test() diff -r 27aa3b16eebb -r 4457d7071a23 OpenSecurity/bin/launch.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenSecurity/bin/launch.py Fri Dec 06 10:51:15 2013 +0100 @@ -0,0 +1,287 @@ +#!/bin/env python +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------ +# opensecurity-launcher +# +# launches an application inside a VM +# +# Autor: Oliver Maurhart, +# +# Copyright (C) 2013 AIT Austrian Institute of Technology +# AIT Austrian Institute of Technology GmbH +# Donau-City-Strasse 1 | 1220 Vienna | Austria +# http://www.ait.ac.at +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# ------------------------------------------------------------ + + +# ------------------------------------------------------------ +# imports + +import argparse +import os +import subprocess +import sys + +from PyQt4 import QtCore +from PyQt4 import QtGui + +# local +from about import About +from cygwin import Cygwin +from environment import Environment +import opensecurity_server + + +# ------------------------------------------------------------ +# code + + +class Chooser(QtGui.QDialog, object): + + """Ask the user what to launch.""" + + def __init__(self, parent = None, flags = QtCore.Qt.WindowFlags(0)): + + super(Chooser, self).__init__(parent, flags) + self.setWindowTitle('OpenSecuirty Launch Application') + self.setup_ui() + + # known vms and applications + self._apps, self_vms = [], [] + + # positionate ourself central + screen = QtGui.QDesktopWidget().screenGeometry() + self.resize(self.geometry().width() * 1.25, self.geometry().height()) + size = self.geometry() + self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2) + + # refresh vm and command input + self.refresh() + + + def app_get(self): + """The application of the user.""" + a = str(self._cbApplication.currentText()) + for app in self._apps: + if a == app['name']: + return app['command'] + return a + + app = property(app_get) + + + def clicked_about(self): + """clicked the about button""" + dlgAbout = About() + dlgAbout.exec_() + + + def clicked_cancel(self): + """clicked the cancel button""" + self.reject() + + + def clicked_ok(self): + """clicked the ok button""" + self.accept() + + + def refresh(self): + """load the known vms and commands and adjust input fields""" + + self._apps = opensecurity_server.query_apps() + self._vms = opensecurity_server.query_vms() + + # add the VMs we know + self._cbApplication.clear() + for app in self._apps: + self._cbApplication.addItem(app['name']) + + # add the commands we know + self._cbVM.clear() + for vm in self._vms: + self._cbVM.addItem(vm['name']) + + + def setup_ui(self): + """Create the widgets.""" + + lyMain = QtGui.QVBoxLayout(self) + lyMain.setContentsMargins(8, 8, 8, 8) + + # content area: left pixmap, right text + lyContent = QtGui.QHBoxLayout() + lyMain.addLayout(lyContent) + + # pixmap + lbPix = QtGui.QLabel() + lbPix.setPixmap(QtGui.QPixmapCache.find('opensecurity_icon_64')) + lyContent.addWidget(lbPix, 0, QtCore.Qt.Alignment(QtCore.Qt.AlignTop + QtCore.Qt.AlignHCenter)) + lyContent.addSpacing(16) + + # launch ... + lyLaunch = QtGui.QGridLayout() + lyContent.addLayout(lyLaunch) + lbTitle = QtGui.QLabel('Specify details for application to launch.') + lyLaunch.addWidget(lbTitle, 0, 0, 1, 2) + + lbVM = QtGui.QLabel('&VM-ID:') + lyLaunch.addWidget(lbVM, 1, 0) + self._cbVM = QtGui.QComboBox() + self._cbVM.setEditable(True) + self._cbVM.setInsertPolicy(QtGui.QComboBox.InsertAlphabetically) + lyLaunch.addWidget(self._cbVM, 1, 1) + lbVM.setBuddy(self._cbVM) + + lbApplication = QtGui.QLabel('&Application:') + lyLaunch.addWidget(lbApplication, 2, 0) + self._cbApplication = QtGui.QComboBox() + self._cbApplication.setEditable(True) + self._cbApplication.setInsertPolicy(QtGui.QComboBox.InsertAlphabetically) + lyLaunch.addWidget(self._cbApplication, 2, 1) + lbApplication.setBuddy(self._cbApplication) + + lyLaunch.addWidget(QtGui.QWidget(), 3, 0, 1, 2) + lyLaunch.setColumnStretch(1, 1) + lyLaunch.setRowStretch(3, 1) + + lyMain.addStretch(1) + + # buttons + lyButton = QtGui.QHBoxLayout() + lyMain.addLayout(lyButton) + + lyButton.addStretch(1) + btnOk = QtGui.QPushButton('&Ok', self) + btnOk.setDefault(True) + btnOk.setMinimumWidth(100) + lyButton.addWidget(btnOk) + btnCancel = QtGui.QPushButton('&Cancel', self) + btnCancel.setMinimumWidth(100) + lyButton.addWidget(btnCancel) + btnAbout = QtGui.QPushButton('&About', self) + btnAbout.setMinimumWidth(100) + lyButton.addWidget(btnAbout) + + button_width = max(btnOk.width(), btnCancel.width(), btnAbout.width()) + btnOk.setMinimumWidth(button_width) + btnCancel.setMinimumWidth(button_width) + btnAbout.setMinimumWidth(button_width) + + # reduce to the max + self.resize(lyMain.minimumSize()) + + # connectors + btnOk.clicked.connect(self.clicked_ok) + btnCancel.clicked.connect(self.clicked_cancel) + btnAbout.clicked.connect(self.clicked_about) + + + def user_get(self): + """The user of the vm of choice.""" + v = str(self._cbVM.currentText()) + for vm in self._vms: + if v == vm['name']: + return vm['user'] + return v + + user = property(user_get) + + + def vm_get(self): + """The vm of choice.""" + v = str(self._cbVM.currentText()) + for vm in self._vms: + if v == vm['name']: + return vm['ip'] + return v + + vm = property(vm_get) + + +def ask_user(): + """ask the user for VM and app to start""" + + # launch Qt + app = QtGui.QApplication(sys.argv) + + # prebuild the pixmap cache: fetch all jpg, png and jpeg images and load them + image_path = os.path.join(Environment("OpenSecurity").data_path, '..', 'gfx') + for file in os.listdir(image_path): + if file.lower().rpartition('.')[2] in ('jpg', 'png', 'jpeg'): + QtGui.QPixmapCache.insert(file.lower().rpartition('.')[0], QtGui.QPixmap(os.path.join(image_path, file))) + + # we should have now our application icon + app.setWindowIcon(QtGui.QIcon(QtGui.QPixmapCache.find('opensecurity_icon_64'))) + + # pop up the dialog + dlg = Chooser() + dlg.show() + app.exec_() + + if dlg.result() == QtGui.QDialog.Accepted: + return dlg.user, dlg.vm, dlg.app + + return '', '', '' + + +def main(): + """entry point""" + + # parse command line + parser = argparse.ArgumentParser(description = 'OpenSecurity Launcher: run application in VM') + parser.add_argument('user', metavar='USER', help='USER on Virtual Machine', nargs='?', type=str, default='') + parser.add_argument('ip', metavar='IP', help='IP of Virtual Machine', nargs='?', type=str, default='') + parser.add_argument('command', metavar='COMMAND', help='Full path of command and arguments to start inside VM', nargs='?', type=str, default='') + args = parser.parse_args() + + # we must have at least all or none set + set_user = args.user != '' + set_ip = args.ip != '' + set_command = args.command != '' + set_ALL = set_user and set_ip and set_command + set_NONE = (not set_user) and (not set_ip) and (not set_command) + if (not set_ALL) and (not set_NONE): + sys.stderr.write("Please specify user, ip and command or none.\n") + sys.stderr.write("Type '--help' for help.\n") + sys.exit(1) + + # check if we need to ask the user + if set_NONE: + args.user, args.ip, args.command = ask_user() + + # still no IP? --> no chance, over and out! + if args.ip == '': + sys.exit(0) + + # ensure we have our X11 running + Cygwin.start_X11() + + # the SSH command + user_at_guest = args.user + '@' + args.ip + ssh = 'DISPLAY=:0 /usr/bin/ssh -Y ' + user_at_guest + ' ' + args.command + print(ssh) + + # off we go! + Cygwin()(['/bin/bash', '--login', '-i', '-c', ssh], None, None, None) + + +# start +if __name__ == "__main__": + main() + diff -r 27aa3b16eebb -r 4457d7071a23 OpenSecurity/bin/opensecurity_client_restful_server.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenSecurity/bin/opensecurity_client_restful_server.py Fri Dec 06 10:51:15 2013 +0100 @@ -0,0 +1,215 @@ +#!/bin/env python +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------ +# opensecurity_client_restful_server +# +# the OpenSecurity client RESTful server +# +# Autor: Oliver Maurhart, +# +# Copyright (C) 2013 AIT Austrian Institute of Technology +# AIT Austrian Institute of Technology GmbH +# Donau-City-Strasse 1 | 1220 Vienna | Austria +# http://www.ait.ac.at +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# ------------------------------------------------------------ + + +# ------------------------------------------------------------ +# imports + +import os +import os.path +import subprocess +import sys +import web + +# local +from environment import Environment +import opensecurity_server + + +# ------------------------------------------------------------ +# const + + +__version__ = "0.1" + + +"""All the URLs we know mapping to class handler""" +opensecurity_urls = ( + '/application', 'os_application', + '/credentials', 'os_credentials', + '/password', 'os_password', + '/', 'os_root' +) + + +# ------------------------------------------------------------ +# code + + +class os_application: + """OpenSecurity '/application' handler. + + This is called on GET /application?vm=VM-ID&app=APP-ID + This tries to access the vm identified with the label VM-ID + and launched the application identified APP-ID + """ + + def GET(self): + + # pick the arguments + args = web.input() + + # we _need_ a vm + if not "vm" in args: + raise web.badrequest() + + # we _need_ a app + if not "app" in args: + raise web.badrequest() + + apps = opensecurity_server.query_apps() + vms = opensecurity_server.query_vms() + + # check if we do have valid vm + v = [v for v in vms if v['name'] == args.vm] + if len(v) == 0: + raise web.notfound('vm not found') + v = v[0] + + # check if we do have a valid app + a = [a for a in apps if a['name'] == args.app] + if len(a) == 0: + raise web.notfound('app not found') + a = a[0] + + # invoke launch with + res = "starting: launch " + v['user'] + " " + v['ip'] + " " + a['command'] + + launch_image = os.path.join(sys.path[0], 'launch.py') + process_command = [sys.executable, launch_image, v['user'], v['ip'], a['command']] + process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE) + result = process.communicate()[0] + if process.returncode != 0: + return 'Launch of application aborted.' + + return result + + +class os_credentials: + """OpenSecurity '/credentials' handler. + + This is called on GET /credentials?text=TEXT. + Ideally this should pop up a user dialog to insert his + credentials based the given TEXT. + """ + + def GET(self): + + # pick the arguments + args = web.input() + + # we _need_ a device id + if not "text" in args: + raise web.badrequest() + + # invoke the user dialog as a subprocess + dlg_credentials_image = os.path.join(sys.path[0], 'opensecurity_dialog.py') + process_command = [sys.executable, dlg_credentials_image, 'credentials', args.text] + process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE) + result = process.communicate()[0] + if process.returncode != 0: + return 'Credentials request has been aborted.' + + return result + + +class os_password: + """OpenSecurity '/password' handler. + + This is called on GET /password?text=TEXT. + Ideally this should pop up a user dialog to insert his + password based device name. + """ + + def GET(self): + + # pick the arguments + args = web.input() + + # we _need_ a device id + if not "text" in args: + raise web.badrequest() + + # invoke the user dialog as a subprocess + dlg_credentials_image = os.path.join(sys.path[0], 'opensecurity_dialog.py') + process_command = [sys.executable, dlg_credentials_image, 'password', args.text] + process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE) + result = process.communicate()[0] + if process.returncode != 0: + return 'password request has been aborted.' + + return result + + +class os_root: + """OpenSecurity '/' handler""" + + def GET(self): + + res = "OpenSecurity-Client RESTFul Server { \"version\": \"%s\" }" % __version__ + + # add some sample links + res = res + """ + +USAGE EXAMPLES: + +Request a password: + (copy paste this into your browser's address field after the host:port) + + /password?text=Give+me+a+password+for+device+%22My+USB+Drive%22+(ID%3A+32090-AAA-X0) + + (eg.: http://127.0.0.1:8090/password?text=Give+me+a+password+for+device+%22My+USB+Drive%22+(ID%3A+32090-AAA-X0)) + NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window. + + +Request a combination of user and password: + (copy paste this into your browser's address field after the host:port) + + /credentials?text=Tell+the+NSA+which+credentials+to+use+in+order+to+avoid+hacking+noise+on+wire. + + (eg.: http://127.0.0.1:8090/credentials?text=Tell+the+NSA+which+credentials+to+use+in+order+to+avoid+hacking+noise+on+wire.) + NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window. + + +Start a Browser: + (copy paste this into your browser's address field after the host:port) + + /application?vm=Debian+7&app=Browser + + (e.g. http://127.0.0.1:8090/application?vm=Debian+7&app=Browser) + """ + + return res + + +# start +if __name__ == "__main__": + server = web.application(opensecurity_urls, globals()) + server.run() diff -r 27aa3b16eebb -r 4457d7071a23 OpenSecurity/bin/opensecurity_dialog.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenSecurity/bin/opensecurity_dialog.py Fri Dec 06 10:51:15 2013 +0100 @@ -0,0 +1,93 @@ +#!/bin/env python +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------ +# opensecurity-dialog +# +# an opensecurity dialog +# +# Autor: Oliver Maurhart, +# +# Copyright (C) 2013 AIT Austrian Institute of Technology +# AIT Austrian Institute of Technology GmbH +# Donau-City-Strasse 1 | 1220 Vienna | Austria +# http://www.ait.ac.at +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# ------------------------------------------------------------ + + +# ------------------------------------------------------------ +# imports + +import argparse +import os +import sys + +from PyQt4 import QtCore +from PyQt4 import QtGui + +# local +from credentials import Credentials +from environment import Environment +from password import Password + + +# ------------------------------------------------------------ +# code + + +def main(): + + # parse command line + parser = argparse.ArgumentParser(description = 'OpenSecurity Dialog.') + parser.add_argument('mode', metavar='MODE', help='dialog mode: \'password\' or \'credentials\'') + parser.add_argument('text', metavar='TEXT', help='text to show') + args = parser.parse_args() + + app = QtGui.QApplication(sys.argv) + + # prebuild the pixmap cache: fetch all jpg, png and jpeg images and load them + data_path = Environment("OpenSecurity").data_path + image_path = os.path.join(data_path, '..', 'gfx') + for file in os.listdir(image_path): + if file.lower().rpartition('.')[2] in ('jpg', 'png', 'jpeg'): + QtGui.QPixmapCache.insert(file.lower().rpartition('.')[0], QtGui.QPixmap(os.path.join(image_path, file))) + + # we should have now our application icon + app.setWindowIcon(QtGui.QIcon(QtGui.QPixmapCache.find('opensecurity_icon_64'))) + + if args.mode == 'password': + dlg = Password(args.text) + + if args.mode == 'credentials': + dlg = Credentials(args.text) + + # pop up the dialog + dlg.show() + app.exec_() + + # give proper result code + if dlg.result() == QtGui.QDialog.Accepted: + res = 0 + else: + res = 1 + sys.exit(res) + + +# start +if __name__ == "__main__": + main() + diff -r 27aa3b16eebb -r 4457d7071a23 OpenSecurity/bin/opensecurity_server.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenSecurity/bin/opensecurity_server.py Fri Dec 06 10:51:15 2013 +0100 @@ -0,0 +1,70 @@ +#!/bin/env python +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------ +# opensecurity-server +# +# talk to the opensecurity server +# +# Autor: Oliver Maurhart, +# +# Copyright (C) 2013 AIT Austrian Institute of Technology +# AIT Austrian Institute of Technology GmbH +# Donau-City-Strasse 1 | 1220 Vienna | Austria +# http://www.ait.ac.at +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# ------------------------------------------------------------ + +# ------------------------------------------------------------ +# import + +from pprint import PrettyPrinter + + +# ------------------------------------------------------------ +# code + +def query_apps(): + """get the list of known apps""" + + # TODO: REPLACE THIS HARDCODED STUFF WITH REAL CODE TO THE OS SERVER + apps = [ + { 'vm': '', 'name': 'Browser', 'command': '/usr/bin/iceweasel'}, + ] + + return apps + + +def query_vms(): + """get the list of registered vms, their ip and the prefered user""" + + # TODO: REPLACE THIS HARDCODED STUFF WITH REAL CODE TO THE OS SERVER + vms = [ + { 'user': 'opensec', 'name': 'Debian 7', 'ip': '192.168.56.101'}, + { 'user': 'opensec', 'name': 'Anit-Virus VM', 'ip': '192.168.56.101'} + ] + + return vms + + +# start +if __name__ == "__main__": + print("known apps: ") + PrettyPrinter().pprint(query_apps()) + print("known vms: ") + PrettyPrinter().pprint(query_vms()) + + diff -r 27aa3b16eebb -r 4457d7071a23 OpenSecurity/bin/opensecurity_tray.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenSecurity/bin/opensecurity_tray.py Fri Dec 06 10:51:15 2013 +0100 @@ -0,0 +1,131 @@ +#!/bin/env python +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------ +# opensecurity-dialog +# +# an opensecurity dialog +# +# Autor: Oliver Maurhart, +# +# Copyright (C) 2013 AIT Austrian Institute of Technology +# AIT Austrian Institute of Technology GmbH +# Donau-City-Strasse 1 | 1220 Vienna | Austria +# http://www.ait.ac.at +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# ------------------------------------------------------------ + + +# ------------------------------------------------------------ +# imports + +import argparse +import os +import subprocess +import sys + +from PyQt4 import QtCore +from PyQt4 import QtGui + +# local +from about import About +from environment import Environment + + +# ------------------------------------------------------------ +# code + + +class OpenSecurityTrayIcon(QtGui.QSystemTrayIcon): + + """This is the OpenSecuirty Tray Icon""" + + def __init__(self, icon, parent=None): + + super(OpenSecurityTrayIcon, self).__init__(icon, parent) + self.setup_ui() + + + def clicked_about(self): + """clicked about""" + dlgAbout = About() + dlgAbout.exec_() + + + def clicked_exit(self): + """clicked exit""" + sys.exit(0) + + + def clicked_launch_application(self): + """clicked the launch an application""" + dlg_launch_image = os.path.join(sys.path[0], 'launch.pyw') + process_command = [sys.executable, dlg_launch_image] + print(process_command) + process = subprocess.Popen(process_command, shell = False) + process.communicate() + + + def clicked_refresh(self): + """clicked refresh""" + self.setup_ui() + + + def setup_ui(self): + """create the user interface + As for the system tray this is 'just' the context menu. + """ + + # define the tray icon menu + menu = QtGui.QMenu(self.parent()) + self.setContextMenu(menu) + + # add known apps + + # add standard menu items + cAcLaunch = menu.addAction(QtGui.QIcon(QtGui.QPixmapCache.find('opensecurity_icon_64')), 'Lauch Application') + menu.addSeparator() + cAcRefresh = menu.addAction('Refresh') + cAcAbout = menu.addAction("About") + cAcExit = menu.addAction("Exit") + + cAcLaunch.triggered.connect(self.clicked_launch_application) + cAcRefresh.triggered.connect(self.clicked_refresh) + cAcAbout.triggered.connect(self.clicked_about) + cAcExit.triggered.connect(self.clicked_exit) + + +def main(): + + app = QtGui.QApplication(sys.argv) + + # prebuild the pixmap cache: fetch all jpg, png and jpeg images and load them + image_path = os.path.join(Environment("OpenSecurity").data_path, '..', 'gfx') + for file in os.listdir(image_path): + if file.lower().rpartition('.')[2] in ('jpg', 'png', 'jpeg'): + QtGui.QPixmapCache.insert(file.lower().rpartition('.')[0], QtGui.QPixmap(os.path.join(image_path, file))) + + w = QtGui.QWidget() + trayIcon = OpenSecurityTrayIcon(QtGui.QIcon(QtGui.QPixmapCache.find('opensecurity_icon_64')), w) + + trayIcon.show() + sys.exit(app.exec_()) + + +# start +if __name__ == "__main__": + main() + diff -r 27aa3b16eebb -r 4457d7071a23 OpenSecurity/bin/opensecurityd.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenSecurity/bin/opensecurityd.py Fri Dec 06 10:51:15 2013 +0100 @@ -0,0 +1,119 @@ +#!/bin/env python +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------ +# opensecurityd +# +# the opensecurityd as RESTful server +# +# Autor: Oliver Maurhart, +# +# Copyright (C) 2013 AIT Austrian Institute of Technology +# AIT Austrian Institute of Technology GmbH +# Donau-City-Strasse 1 | 1220 Vienna | Austria +# http://www.ait.ac.at +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# ------------------------------------------------------------ + + +# ------------------------------------------------------------ +# imports + +import os +import os.path +import subprocess +import sys +import web +from vmmanager.vmmanager import VMManager + +# local +from environment import Environment + + +# ------------------------------------------------------------ +# const + +__version__ = "0.1" + + +"""All the URLs we know mapping to class handler""" +opensecurity_urls = ( + '/device_change', 'os_device_change', + '/sdvms', 'os_sdvms', + '/vms', 'os_vms', + '/vms/(.*)', 'os_vm', + '/', 'os_root' +) + + +# ------------------------------------------------------------ +# vars + +# Global VMManager instance +gvm_mgr = VMManager() + + +# ------------------------------------------------------------ +# code + + +class os_device_change: + """OpenSecurity '/device_change' handler""" + + def GET(self): + #gvm_mgr.configureHostNetworking() + print 'received device_change' + return "os_device_change" + + +class os_sdvms: + """OpenSecurity '/sdvms' handler""" + + def GET(self): + return gvm_mgr.listSDVM() + + +class os_vm: + """OpenSecurity '/vms/VM' handler""" + + def GET(self, name): + return gvm_mgr.getVMInfo(name) + + +class os_vms: + """OpenSecurity '/vms' handler""" + + def GET(self): + return gvm_mgr.listVM() + + +class os_root: + """OpenSecurity '/' handler""" + + def GET(self): + res = "'os_server': { " + res += "'version': '" + __version__ + "', " + res += "'virtualbox_path': '" + gvm_mgr.vBoxPath + "', " + res += "'machine_folder': '" + gvm_mgr.getDefaultMachineFolder() + "' " + res += "}" + return res + + +# start +if __name__ == "__main__": + server = web.application(opensecurity_urls, globals()) + server.run() + diff -r 27aa3b16eebb -r 4457d7071a23 OpenSecurity/bin/password.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenSecurity/bin/password.py Fri Dec 06 10:51:15 2013 +0100 @@ -0,0 +1,150 @@ +#!/bin/env python +# -*- coding: utf-8 -*- + +# ------------------------------------------------------------ +# password-dialog +# +# ask the user a password +# +# Autor: Oliver Maurhart, +# +# Copyright (C) 2013 AIT Austrian Institute of Technology +# AIT Austrian Institute of Technology GmbH +# Donau-City-Strasse 1 | 1220 Vienna | Austria +# http://www.ait.ac.at +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation version 2. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, +# Boston, MA 02110-1301, USA. +# ------------------------------------------------------------ + + +# ------------------------------------------------------------ +# imports + +import sys + +from PyQt4 import QtCore +from PyQt4 import QtGui + +# local +from about import About + +# ------------------------------------------------------------ +# code + + +class Password(QtGui.QDialog): + + """Ask the user for a password.""" + + def __init__(self, text, parent = None, flags = QtCore.Qt.WindowFlags(0)): + + # super call and widget init + super(Password, self).__init__(parent, flags) + self.setWindowTitle('OpenSecuirty Password Request') + self.setup_ui() + + # positionate ourself central + screen = QtGui.QDesktopWidget().screenGeometry() + self.resize(self.geometry().width() * 1.25, self.geometry().height()) + size = self.geometry() + self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2) + + # fix up text + self.lbText.setText(text) + + + def clicked_about(self): + """clicked the about button""" + dlgAbout = About() + dlgAbout.exec_() + + + def clicked_cancel(self): + """clicked the cancel button""" + self.reject() + + + def clicked_ok(self): + """clicked the ok button""" + sys.stdout.write('{ ') + sys.stdout.write('\'password\': \'') + sys.stdout.write(self.edPassword.text()) + sys.stdout.write('\' ') + sys.stdout.write('}\n') + self.accept() + + + def setup_ui(self): + + """Create the widgets.""" + + lyMain = QtGui.QVBoxLayout(self) + lyMain.setContentsMargins(8, 8, 8, 8) + + # content area: left pixmap, right text + lyContent = QtGui.QHBoxLayout() + lyMain.addLayout(lyContent) + + # pixmap + lbPix = QtGui.QLabel() + lbPix.setPixmap(QtGui.QPixmapCache.find('opensecurity_icon_64')) + lyContent.addWidget(lbPix, 0, QtCore.Qt.Alignment(QtCore.Qt.AlignTop + QtCore.Qt.AlignHCenter)) + lyContent.addSpacing(16) + + # text ... + lyText = QtGui.QVBoxLayout() + lyContent.addLayout(lyText) + self.lbText = QtGui.QLabel() + lyText.addWidget(self.lbText) + lyPassword = QtGui.QHBoxLayout() + lyText.addLayout(lyPassword) + lbPassword = QtGui.QLabel('&Password:') + lyPassword.addWidget(lbPassword) + self.edPassword = QtGui.QLineEdit() + self.edPassword.setEchoMode(QtGui.QLineEdit.Password) + lyPassword.addWidget(self.edPassword) + lbPassword.setBuddy(self.edPassword) + lyText.addStretch(1) + + lyMain.addStretch(1) + + # buttons + lyButton = QtGui.QHBoxLayout() + lyMain.addLayout(lyButton) + + lyButton.addStretch(1) + btnOk = QtGui.QPushButton('&Ok', self) + btnOk.setDefault(True) + btnOk.setMinimumWidth(100) + lyButton.addWidget(btnOk) + btnCancel = QtGui.QPushButton('&Cancel', self) + btnCancel.setMinimumWidth(100) + lyButton.addWidget(btnCancel) + btnAbout = QtGui.QPushButton('&About', self) + btnAbout.setMinimumWidth(100) + lyButton.addWidget(btnAbout) + + button_width = max(btnOk.width(), btnCancel.width(), btnAbout.width()) + btnOk.setMinimumWidth(button_width) + btnCancel.setMinimumWidth(button_width) + btnAbout.setMinimumWidth(button_width) + + # reduce to the max + self.resize(lyMain.minimumSize()) + + # connectors + btnOk.clicked.connect(self.clicked_ok) + btnCancel.clicked.connect(self.clicked_cancel) + btnAbout.clicked.connect(self.clicked_about) diff -r 27aa3b16eebb -r 4457d7071a23 OpenSecurity/bin/vmmanager/PKG-INFO --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenSecurity/bin/vmmanager/PKG-INFO Fri Dec 06 10:51:15 2013 +0100 @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: vmmanager.py +Version: 0.1 +Summary: vmmanager.py: manages GustVM's +Home-page: http://webpy.org/ +Author: Mihai Bartha +Author-email: mihai.bartha@ait.ac.at +License: Public domain +Description: Module to manage virtualbox guests and host +Platform: any diff -r 27aa3b16eebb -r 4457d7071a23 OpenSecurity/bin/vmmanager/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenSecurity/bin/vmmanager/__init__.py Fri Dec 06 10:51:15 2013 +0100 @@ -0,0 +1,6 @@ +__version__ = "0.1" +__author__ = [ + "Mihai Bartha " +] +__license__ = "public domain" +__contributors__ = "OpenSecurity Consortium" \ No newline at end of file diff -r 27aa3b16eebb -r 4457d7071a23 OpenSecurity/bin/vmmanager/vmmanager.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OpenSecurity/bin/vmmanager/vmmanager.py Fri Dec 06 10:51:15 2013 +0100 @@ -0,0 +1,342 @@ +''' +Created on Nov 19, 2013 + +@author: BarthaM +''' +import os +import os.path +from subprocess import Popen, PIPE, call +import subprocess +import sys +import re +from cygwin import Cygwin +import _winreg + +DEBUG = True + +class USBFilter: + vendorid = "" + productid = "" + revision = "" + + def __init__(self, vendorid, productid, revision): + self.vendorid = vendorid.lower() + self.productid = productid.lower() + self.revision = revision.lower() + return + + def __eq__(self, other): + return self.vendorid == other.vendorid and self.productid == other.productid and self.revision == other.revision + + def __hash__(self): + return hash(self.vendorid) ^ hash(self.productid) ^ hash(self.revision) + + def __repr__(self): + return "VendorId = \'" + str(self.vendorid) + "\' ProductId = \'" + str(self.productid) + "\' Revision = \'" + str(self.revision) + "\'" + + +class VMManager(object): + """Manage Virtual Machines""" + + vmRootName = "SecurityDVM" + + def __init__(self): + self.vBoxPath = self.getVBoxManagePath() + self.vBoxManage = os.path.join(self.vBoxPath, 'VBoxManage.exe') + self.systemProperties = self.getSystemProperties() + self.cygwin_path = Cygwin.root() + return + + def execute(self, cmd): + """execute a command""" + + # we can handle strings and lists as input + c = cmd + if type(cmd) == list: + c = ' '.join(cmd) + + if DEBUG: + sys.stderr.write('trying to launch: ' + c + '\n') + process = Popen(cmd, stdout=PIPE, stderr=PIPE) + if DEBUG: + sys.stderr.write('launched: ' + c + '\n') + + result = process.wait() + res_stdout = process.stdout.read(); + res_stderr = process.stderr.read(); + if DEBUG: + if res_stdout != "": + print res_stdout + if res_stderr != "": + print res_stderr + return result, res_stdout, res_stderr + + + def getVBoxManagePath(self): + """get the path to the VirtualBox installation on this system""" + p = None + try: + k = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\Oracle\VirtualBox') + p = _winreg.QueryValueEx(k, 'InstallDir')[0] + _winreg.CloseKey(k) + except: + pass + return p + + # return host system properties + def getSystemProperties(self): + cmd = [self.vBoxManage, 'list', 'systemproperties'] + result = self.execute(cmd) + if result[1]=='': + return None + props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result[1].strip().splitlines())) + return props + + # return the folder containing the guest VMs + def getDefaultMachineFolder(self): + return self.systemProperties["Default machine folder"] + + #list the hostonly IFs exposed by the VBox host + def getHostOnlyIFs(self): + cmd = [self.vBoxManage, 'list', 'hostonlyifs'] + result = self.execute(cmd)[1] + if result=='': + return None + props = dict((k.strip(),v.strip().strip('"')) for k,v in (line.split(':', 1) for line in result.strip().splitlines())) + return props + + def listRSDS(self): + cmd = [self.vBoxManage, 'list', 'usbhost'] + results = self.execute(cmd) + results = results.split('Host USB Devices:')[1].strip() + + items = list( "UUID:" + result for result in results.split('UUID:') if result != '') + rsds = dict() + for item in items: + props = dict() + for line in item.splitlines(): + if line != "": + k,v = line[:line.index(':')].strip(), line[line.index(':')+1:].strip() + props[k] = v; + + if 'Product' in props.keys() and props['Product'] == 'Mass Storage': + usb_filter = USBFilter( re.search(r"\((?P[0-9A-Fa-f]+)\)", props['VendorId']).groupdict()['vid'], + re.search(r"\((?P[0-9A-Fa-f]+)\)", props['ProductId']).groupdict()['pid'], + re.search(r"\((?P[0-9A-Fa-f]+)\)", props['Revision']).groupdict()['rev'] ) + rsds[props['UUID']] = usb_filter; + if DEBUG: + print filter + return rsds + + # list all existing VMs registered with VBox + def listVM(self): + cmd = [self.vBoxManage, 'list', 'vms'] + result = self.execute(cmd)[1] + vms = list(k.strip().strip('"') for k,_ in (line.split(' ') for line in result.splitlines())) + return vms + + # list existing SDVMs + def listSDVM(self): + vms = self.listVM() + svdms = [] + for vm in vms: + if vm.startswith(self.vmRootName) and vm != self.vmRootName: + svdms.append(vm) + return svdms + + # generate valid (not already existing SDVM name). necessary for creating a new VM + def generateSDVMName(self): + vms = self.listVM() + for i in range(0,999): + if(not self.vmRootName+str(i) in vms): + return self.vmRootName+str(i) + return '' + + # return the RSDs attached to all existing SDVMs + def getAttachedRSDs(self): + vms = self.listSDVM() + attached_devices = dict() + for vm in vms: + rsd_filter = self.getUSBFilter(vm) + if filter != None: + attached_devices[vm] = rsd_filter + return attached_devices + + # configures hostonly networking and DHCP server. requires admin rights + def configureHostNetworking(self): + #cmd = 'vboxmanage list hostonlyifs' + #self.execute(cmd) + #cmd = 'vboxmanage hostonlyif remove \"VirtualBox Host-Only Ethernet Adapter\"' + #self.execute(cmd) + #cmd = 'vboxmanage hostonlyif create' + #self.execute(cmd) + cmd = self.vBoxManage + ' hostonlyif ipconfig \"VirtualBox Host-Only Ethernet Adapter\" --ip 192.168.56.1 --netmask 255.255.255.0' + self.execute(cmd) + #cmd = 'vboxmanage dhcpserver add' + #self.execute(cmd) + cmd = self.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' + self.execute(cmd) + + #create new virtual machine instance based on template vm named SecurityDVM (\SecurityDVM\SecurityDVM.vmdk) + def createVM(self, vm_name): + hostonly_if = self.getHostOnlyIFs() + machineFolder = self.getDefaultMachineFolder() + cmd = self.vBoxManage + ' createvm --name ' + vm_name + ' --ostype Debian --register' + self.execute(cmd) + cmd = self.vBoxManage + ' modifyvm ' + vm_name + ' --memory 512 --vram 10 --cpus 1 --usb on --usbehci on --nic1 hostonly --hostonlyadapter1 \"' + hostonly_if['Name'] + '\" --nic2 nat' + self.execute(cmd) + cmd = self.vBoxManage + ' storagectl ' + vm_name + ' --name contr1 --add sata --portcount 2' + self.execute(cmd) + cmd = self.vBoxManage + ' storageattach ' + vm_name + ' --storagectl contr1 --port 0 --device 0 --type hdd --mtype normal --medium \"'+ machineFolder + '\SecurityDVM\SecurityDVM.vmdk\"' + self.execute(cmd) + return + + #remove VM from the system. should be used on VMs returned by listSDVMs + def removeVM(self, vm_name): + print('removing ' + vm_name) + cmd = self.vBoxManage + ' unregistervm', vm_name, '--delete' + print self.execute(cmd) + machineFolder = self.getDefaultMachineFolder() + cmd = self.cygwin_path+'bash.exe --login -c \"rm -rf ' + machineFolder + '\\' + vm_name + '*\"' + print self.execute(cmd) + + # start VM + def startVM(self, vm_name): + print('starting ' + vm_name) + cmd = self.vBoxManage + ' startvm ' + vm_name + ' --type headless' + print self.execute(cmd) + + # stop VM + def stopVM(self, vm_name): + print('stopping ' + vm_name) + cmd = self.vBoxManage + ' controlvm ' + vm_name + ' poweroff' + print self.execute(cmd) + + # return the hostOnly IP for a running guest + def getHostOnlyIP(self, vm_name): + print('gettting hostOnly IP address ' + vm_name) + cmd = self.vBoxManage + ' guestproperty get ' + vm_name + ' /VirtualBox/GuestInfo/Net/0/V4/IP' + result = self.execute(cmd) + if result=='': + return None + result = result[1] + return result[result.index(':')+1:].strip() + + # attach removable storage device to VM by provision of filter + def attachRSD(self, vm_name, rsd_filter): + cmd = self.vBoxManage + ' usbfilter add 0 --target ' + vm_name + ' --name OpenSecurityRSD --vendorid ' + rsd_filter.vendorid + ' --productid ' + rsd_filter.productid + ' --revision ' + rsd_filter.revision + print self.execute(cmd) + + + # return the description set for an existing VM + def getVMInfo(self, vm_name): + cmd = self.vBoxManage + ' showvminfo ' + vm_name + ' --machinereadable' + r, o, e = self.execute(cmd) + props = dict((k.strip(),v.strip().strip('"')) for k, v in (line.split('=', 1) for line in o.splitlines())) + return props + + # return the configured USB filter for an existing VM + def getUSBFilter(self, vm_name): + props = self.getVMInfo(vm_name) + keys = set(['USBFilterVendorId1', 'USBFilterProductId1', 'USBFilterRevision1']) + keyset = set(props.keys()) + usb_filter = None + if keyset.issuperset(keys): + usb_filter = USBFilter(props['USBFilterVendorId1'], props['USBFilterProductId1'], props['USBFilterRevision1']) + return usb_filter + + #generates ISO containing authorized_keys for use with guest VM + def genCertificateISO(self, vm_name): + machineFolder = self.getDefaultMachineFolder() + # create .ssh folder in vm_name + cmd = self.cygwin_path+'bash.exe --login -c \"mkdir -p \\\"' + machineFolder + '\\' + vm_name + '\\.ssh\\\"\"' + result = self.execute(cmd) + # generate dvm_key pair in vm_name / .ssh + 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" |', + result = self.execute(cmd) + # set permissions for keys + #TODO: test without chmod + cmd = self.cygwin_path+'bash.exe --login -c \"chmod 500 \\\"' + machineFolder + '\\' + vm_name + '\\.ssh\\*\\\"\"' + result = self.execute(cmd) + # move out private key + cmd = self.cygwin_path+'bash.exe --login -c \"mv \\\"' + machineFolder + '\\' + vm_name + '\\.ssh\\dvm_key\\\" \\\"' + machineFolder + '\\' + vm_name + '\\\"' + result = self.execute(cmd) + # rename public key to authorized_keys + cmd = self.cygwin_path+'bash.exe --login -c \"mv \\\"' + machineFolder + '\\' + vm_name + '\\.ssh\\dvm_key.pub\\\" \\\"' + machineFolder + '\\' + vm_name + '\\.ssh\\authorized_keys\\\"' + result = self.execute(cmd) + # generate iso image with .ssh/authorized keys + cmd = self.cygwin_path+'bash.exe --login -c \"/usr/bin/genisoimage -J -R -o \\\"' + machineFolder + '\\' + vm_name + '\\'+ vm_name + '.iso\\\" \\\"' + machineFolder + '\\' + vm_name + '\\.ssh\\\"\"' + result = self.execute(cmd) + + # attaches generated ssh public cert to guest vm + def attachCertificateISO(self, vm_name): + machineFolder = self.getDefaultMachineFolder() + cmd = self.vBoxManage + ' storageattach ' + vm_name + ' --storagectl contr1 --port 1 --device 0 --type dvddrive --mtype readonly --medium \"' + machineFolder + '\\' + vm_name + '\\'+ vm_name + '.iso\"' + result = self.execute(cmd) + return result + + # handles device change events + def handleDeviceChange(self): + attached_devices = self.getAttachedRSDs() + connected_devices = self.listRSDS() + for vm_name in attached_devices.keys(): + if attached_devices[vm_name] not in connected_devices.values(): + self.stopVM(vm_name) + self.removeVM(vm_name) + + attached_devices = self.getAttachedRSDs() + for connected_device in connected_devices.values(): + if connected_device not in attached_devices.values(): + new_sdvm = self.generateSDVMName() + self.createVM(new_sdvm) + self.genCertificateISO(new_sdvm) + self.attachCertificateISO(new_sdvm) + self.attachRSD(new_sdvm, connected_device) + self.startVM(new_sdvm) + + # executes command over ssh on guest vm + def sshGuestExecute(self, vm_name, prog, user_name='opensec'): + # get vm ip + address = self.getHostOnlyIP(vm_name) + machineFolder = self.getDefaultMachineFolder() + # run command + cmd = self.cygwin_path+'bash.exe --login -c \"ssh -i \\\"' + machineFolder + '\\' + vm_name + '\\dvm_key\\\" ' + user_name + '@' + address + ' ' + prog + '\"' + return self.execute(cmd) + + # executes command over ssh on guest vm with X forwarding + def sshGuestX11Execute(self, vm_name, prog, user_name='opensec'): + #TODO: verify if X server is running on user account + #TODO: set DISPLAY accordingly + address = self.getHostOnlyIP(vm_name) + machineFolder = self.getDefaultMachineFolder() + # run command + cmd = self.cygwin_path+'bash.exe --login -c \"DISPLAY=:0 ssh -Y -i \\\"' + machineFolder + '\\' + vm_name + '\\dvm_key\\\" ' + user_name + '@' + address + ' ' + prog + '\"' + return self.execute(cmd) + + # executes NET USE and connects to samba share on guestos + def netUse(self, vm_name): + ip = self.getHostOnlyIP(vm_name) + cmd = 'net use H: \\' + ip + '\RSD_Device' + return self.execute(cmd) + + +if __name__ == '__main__': + man = VMManager() + man.cygwin_path = 'c:\\cygwin64\\bin\\' + #man.handleDeviceChange() + #print man.listSDVM() + #man.configureHostNetworking() + new_vm = man.generateSDVMName() + man.createVM(new_vm) + man.genCertificateISO(new_vm) + man.attachCertificateISO(new_vm) + + #man.attachCertificateISO(vm_name) + #man.sshGuestExecute(vm_name, "ls") + #man.sshGuestX11Execute(vm_name, "iceweasel") + #cmd = "c:\\cygwin64\\bin\\bash.exe --login -c \"/bin/ls\"" + #man.execute(cmd) + + + +