# - Mike Warot's capabilities demo version 0.012 # Copyright (C) 2008 Michael A. Warot, all rights reserved. # 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, either version 3 of the License, or # (at your option) any later version. # # 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, see . # # # Based on webserver.py Copyright Jon Berg , turtlemeat.com # version - new capabilites # 001 - moved to port 81 to avoid built in IIS in XP, etc. # 002 - added code from Rogier Steehouder to allow for keyboard interrupt of the server when running from the command line # 003 - add code to display requested path # 004 - get a random number and display it if the request is /token # turns out that Mersenne Twister is an available random number generator... close enough to cryptographic for a while. 8) # 005 - keep a list of tokens we hand out, display on the main page # 006 - allow deletion of a token from the list # 007 - after deleting token, gets you back to the main page # 008 - added GPL language (version 3 of the gpl, don't want this to end up in a Tivo) and startup hint in the command window # 009 - Try to get more documentation working, and be a bit friendlier # 010 - get rid of /token handler # 011 - add code to update content # 012 - move admin to /admin # default page is now content only # import string,cgi,time, socket, random from os import curdir, sep from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer validtokens = [] # start with no tokens version = '0.012' versiondate = 'May 15, 2008' protectedcontent = 'The rain in Spain stays mainly in the plain.' # code from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/499376 which allows for keyboard interrupt of the server when running from command line+ class Server(HTTPServer): """HTTPServer class with timeout.""" def get_request(self): """Get the request and client address from the socket.""" # 10 second timeout self.socket.settimeout(10.0) result = None while result is None: try: result = self.socket.accept() except socket.timeout: pass # Reset timeout on the new socket result[0].settimeout(None) return result class MyHandler(BaseHTTPRequestHandler): def do_GET(self): global validtokens try: if self.path.endswith(".html"): f = open(curdir + sep + self.path) #self.path has /test.html #note that this potentially makes every file on your computer readable by the internet self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() self.wfile.write(f.read()) f.close() return # default out to a welcome page if self.path.endswith("admin"): self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() self.wfile.write("Welcome to Mike Warot's little capabilities demo, version "+version+"
") self.wfile.write('This is the administration page.
') self.wfile.write('
Here is the capabilities protected content: ['+protectedcontent+']
') self.wfile.write('
Here is a list of the valid access tokens:
') for i in validtokens: self.wfile.write('
'); self.wfile.write(i+'') self.wfile.write('') self.wfile.write('
') if (len(validtokens) > 0): self.wfile.write('
You can revoke any of the outstanding capabilities with the corresponding revoke button.
') self.wfile.write('
You can also grant permissions, which creates a new token, using the button below.
') self.wfile.write('
') self.wfile.write('') self.wfile.write('") self.wfile.write('This is the user page.
') self.wfile.write('') self.wfile.write('
') self.wfile.write('
') self.wfile.write('') self.wfile.write('
') return except IOError: self.send_error(404,'File Not Found: %s' % self.path) def do_POST(self): global validtokens global protectedcontent # Parse the form data posted form = cgi.FieldStorage( fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD':'POST', 'CONTENT_TYPE':self.headers['Content-Type'], }) if self.path.endswith("grant"): self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() x = "%032x" % (random.getrandbits(128)) validtokens = validtokens + [ x ] self.wfile.write(x) return if self.path.endswith("write"): cap = form["capability"].value if (cap in validtokens): protectedcontent = form["content"].value self.send_response(200) self.send_header('Refresh','2; url=/') # 2 seconds gives us time to debug this thing, and see the output self.send_header('Content-type', 'text/html') self.end_headers() self.wfile.write('Content has been successfully updated. Please return to the index page') return else: self.send_response(200) self.send_header('Refresh','20; url=/') # 20 seconds gives us time to debug this thing, and see the output self.send_header('Content-type', 'text/html') self.end_headers() self.wfile.write('Update failed, perhaps you wish to return to the index page') return cap = form["capability"].value if (cap in validtokens) and self.path.endswith("delete"): validtokens.remove(cap) self.send_response(200) self.send_header('Refresh','2; url=/') # 2 seconds gives us time to debug this thing, and see the output self.send_header('Content-type', 'text/html') self.end_headers() self.wfile.write('Please return to the index page') pass else: # Begin the response if we didn't have a valid delete token request self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() self.wfile.write('Client: %s\n' % str(self.client_address)) self.wfile.write('Path: %s\n' % self.path) self.wfile.write('Form data:\n') # Echo back information about what was posted in the form for field in form.keys(): field_item = form[field] if field_item.filename: # The field contains an uploaded file file_data = field_item.file.read() file_len = len(file_data) del file_data self.wfile.write('\tUploaded %s (%d bytes)\n' % (field,file_len)) else: # Regular form value self.wfile.write('\t%s=%s\n' % (field, form[field].value)) pass def main(): try: server = Server(('', 81), MyHandler) # port 81 to avoid IIS, Apache, etc. print "Welcome to Mike Warot's capability based security demo web server, version "+version print 'You can access it at http://127.0.0.1:81' print 'Use control-c to tell it to shut down, which may take up to 10 seconds' print 'started httpserver...' server.serve_forever() except KeyboardInterrupt: print '^C received, shutting down server' server.socket.close() if __name__ == '__main__': main()