2 # -*- coding: utf-8 -*-
4 # ------------------------------------------------------------
5 # opensecurity_client_restful_server
7 # the OpenSecurity client RESTful server
9 # Autor: Oliver Maurhart, <oliver.maurhart@ait.ac.at>
11 # Copyright (C) 2013 AIT Austrian Institute of Technology
12 # AIT Austrian Institute of Technology GmbH
13 # Donau-City-Strasse 1 | 1220 Vienna | Austria
14 # http://www.ait.ac.at
16 # This program is free software; you can redistribute it and/or
17 # modify it under the terms of the GNU General Public License
18 # as published by the Free Software Foundation version 2.
20 # This program is distributed in the hope that it will be useful,
21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # GNU General Public License for more details.
25 # You should have received a copy of the GNU General Public License
26 # along with this program; if not, write to the Free Software
27 # Foundation, Inc., 51 Franklin Street, Fifth Floor,
28 # Boston, MA 02110-1301, USA.
29 # ------------------------------------------------------------
32 # ------------------------------------------------------------
50 import __init__ as opensecurity
53 # ------------------------------------------------------------
57 """All the URLs we know mapping to class handler"""
59 '/credentials', 'os_credentials',
60 '/keyfile', 'os_keyfile',
62 '/notification', 'os_notification',
63 '/password', 'os_password',
68 # ------------------------------------------------------------
72 """The REST server object"""
76 # ------------------------------------------------------------
82 """OpenSecurity '/credentials' handler.
84 This is called on GET /credentials?text=TEXT.
85 Ideally this should pop up a user dialog to insert his
86 credentials based the given TEXT.
95 if not "text" in args:
96 raise web.badrequest('no text given')
99 remote_ip = web.ctx.environ['REMOTE_ADDR']
101 # create the process which queries the user
102 dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw')
103 process_command = [sys.executable, dlg_image, 'credentials', args.text]
104 process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)
106 # run process result handling in seprate thread (not to block main one)
107 bouncer = ProcessResultBouncer(process, remote_ip, '/credentials')
110 return 'user queried for credentials'
115 """OpenSecurity '/keyfile' handler.
117 This is called on GET /keyfile?text=TEXT.
118 Ideally this should pop up a user dialog to insert his
119 password along with a keyfile.
128 if not "text" in args:
129 raise web.badrequest('no text given')
132 remote_ip = web.ctx.environ['REMOTE_ADDR']
134 # create the process which queries the user
135 dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw')
136 process_command = [sys.executable, dlg_image, 'keyfile', args.text]
137 process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)
139 # run process result handling in seprate thread (not to block main one)
140 bouncer = ProcessResultBouncer(process, remote_ip, '/keyfile')
143 return 'user queried for password and keyfile'
148 """OpenSecurity '/log' handler.
150 This is called on GET or POST on the log function /log
163 args['user'] = getpass.getuser()
164 args['system'] = platform.node() + " " + platform.system() + " " + platform.release()
167 url_addr = 'http://GIMME-SERVER-TO-LOG-TO/log'
169 # by provided a 'data' we turn this into a POST statement
170 d = urllib.urlencode(args)
171 req = urllib2.Request(url_addr, d)
173 res = urllib2.urlopen(req)
175 print('failed to contact: ' + url_addr)
176 print('log data: ' + d)
182 class os_notification:
184 """OpenSecurity '/notification' handler.
186 This is called on GET /notification?msgtype=TYPE&text=TEXT.
187 This will pop up an OpenSecurity notifcation window
196 if not "msgtype" in args:
197 raise web.badrequest('no msgtype given')
199 if not args.msgtype in ['information', 'warning', 'critical']:
200 raise web.badrequest('Unknown value for msgtype')
203 if not "text" in args:
204 raise web.badrequest('no text given')
206 # invoke the user dialog as a subprocess
207 dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.py')
208 process_command = [sys.executable, dlg_image, 'notification-' + args.msgtype, args.text]
209 process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)
216 """OpenSecurity '/password' handler.
218 This is called on GET /password?text=TEXT.
219 Ideally this should pop up a user dialog to insert his
220 password based device name.
229 if not "text" in args:
230 raise web.badrequest('no text given')
233 remote_ip = web.ctx.environ['REMOTE_ADDR']
235 # create the process which queries the user
236 dlg_image = os.path.join(sys.path[0], 'opensecurity_dialog.pyw')
237 process_command = [sys.executable, dlg_image, 'password', args.text]
238 process = subprocess.Popen(process_command, shell = False, stdout = subprocess.PIPE)
240 # run process result handling in seprate thread (not to block main one)
241 bouncer = ProcessResultBouncer(process, remote_ip, '/password')
244 return 'user queried for password'
249 """OpenSecurity '/' handler"""
253 res = "OpenSecurity-Client RESTFul Server { \"version\": \"%s\" }" % opensecurity.__version__
255 # add some sample links
261 (copy paste this into your browser's address field after the host:port)
263 /password?text=Give+me+a+password+for+device+%22My+USB+Drive%22+(ID%3A+32090-AAA-X0)
265 (eg.: http://127.0.0.1:8090/password?text=Give+me+a+password+for+device+%22My+USB+Drive%22+(ID%3A+32090-AAA-X0))
266 NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window.
269 Request a combination of user and password:
270 (copy paste this into your browser's address field after the host:port)
272 /credentials?text=Tell+the+NSA+which+credentials+to+use+in+order+to+avoid+hacking+noise+on+wire.
274 (eg.: http://127.0.0.1:8090/credentials?text=Tell+the+NSA+which+credentials+to+use+in+order+to+avoid+hacking+noise+on+wire.)
275 NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window.
278 Request a combination of password and keyfile:
279 (copy paste this into your browser's address field after the host:port)
281 /keyfile?text=Your%20private%20RSA%20Keyfile%3A
283 (eg.: http://127.0.0.1:8090//keyfile?text=Your%20private%20RSA%20Keyfile%3A)
284 NOTE: check yout taskbar, the dialog window may not pop up in front of your browser window.
288 (copy paste this into your browser's address field after the host:port)
290 /application?vm=Debian+7&app=Browser
292 (e.g. http://127.0.0.1:8090/application?vm=Debian+7&app=Browser)
298 class ProcessResultBouncer(threading.Thread):
300 """A class to post the result of a given process - assuming it to be in JSON - to a REST Api."""
302 def __init__(self, process, remote_ip, resource):
306 threading.Thread.__init__(self)
307 self._process = process
308 self._remote_ip = remote_ip
309 self._resource = resource
322 # invoke the user dialog as a subprocess
323 result = self._process.communicate()[0]
324 if self._process.returncode != 0:
325 print 'user request has been aborted.'
328 # all ok, tell send request back appropriate destination
330 j = json.loads(result)
332 print 'error in password parsing'
335 # TODO: it would be WAY easier and secure if we just
336 # add the result json to a HTTP-POST here.
337 url_addr = 'http://' + self._remote_ip + ':58080' + self._resource
339 # by provided a 'data' we turn this into a POST statement
340 req = urllib2.Request(url_addr, urllib.urlencode(j))
342 res = urllib2.urlopen(req)
344 print 'failed to contact: ' + url_addr
348 class RESTServerThread(threading.Thread):
350 """Thread for serving the REST API."""
352 def __init__(self, port):
355 threading.Thread.__init__(self)
371 def is_already_running(port = 8090):
373 """check if this is started twice"""
376 s = socket.create_connection(('127.0.0.1', port), 0.5)
385 """Start the REST server"""
389 # trick the web.py server
390 sys.argv = [__file__, str(port)]
391 server = web.application(opensecurity_urls, globals())
395 def serve(port = 8090, background = False):
397 """Start serving the REST Api
398 port ... port number to listen on
399 background ... cease into background (spawn thread) and return immediately"""
401 # start threaded or direct version
402 if background == True:
403 t = RESTServerThread(port)
410 """Stop serving the REST Api"""
420 if __name__ == "__main__":