diff -r 000000000000 -r a3b6e531f0d2 iro/xmlrpc/SecureXMLRPCServer.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iro/xmlrpc/SecureXMLRPCServer.py Thu Oct 22 10:00:01 2009 +0200 @@ -0,0 +1,113 @@ +""" +SecureXMLRPCServer module using pyOpenSSL 0.5 +Written 0907.2002 +by Michal Wallace +http://www.sabren.net/ + +This acts exactly like SimpleXMLRPCServer +from the standard python library, but +uses secure connections. The technique +and classes should work for any SocketServer +style server. However, the code has not +been extensively tested. + +This code is in the public domain. +It is provided AS-IS WITH NO WARRANTY WHATSOEVER. +""" +import SocketServer +import os, socket +import SimpleXMLRPCServer +from OpenSSL import SSL + +class SSLWrapper: + """ + This whole class exists just to filter out a parameter + passed in to the shutdown() method in SimpleXMLRPC.doPOST() + """ + def __init__(self, conn): + """ + Connection is not yet a new-style class, + so I'm making a proxy instead of subclassing. + """ + self.__dict__["conn"] = conn + def __getattr__(self,name): + return getattr(self.__dict__["conn"], name) + def __setattr__(self,name, value): + setattr(self.__dict__["conn"], name, value) + def shutdown(self, how=1): + """ + SimpleXMLRpcServer.doPOST calls shutdown(1), + and Connection.shutdown() doesn't take + an argument. So we just discard the argument. + """ + self.__dict__["conn"].shutdown() + def accept(self): + """ + This is the other part of the shutdown() workaround. + Since servers create new sockets, we have to infect + them with our magic. :) + """ + c, a = self.__dict__["conn"].accept() + return (SSLWrapper(c), a) + + + +class SecureTCPServer(SocketServer.TCPServer): + """ + Just like TCPServer, but use a socket. + This really ought to let you specify the key and certificate files. + """ + def __init__(self, server_address, RequestHandlerClass,certificate,privatekey): + SocketServer.BaseServer.__init__(self, server_address, RequestHandlerClass) + + ## Same as normal, but make it secure: + ctx = SSL.Context(SSL.SSLv23_METHOD) + ctx.set_options(SSL.OP_NO_SSLv2) + + ctx.use_privatekey_file (privatekey) + ctx.use_certificate_file(certificate) + + self.socket = SSLWrapper(SSL.Connection(ctx, socket.socket(self.address_family, + self.socket_type))) + self.server_bind() + self.server_activate() + + +class SecureXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): + def setup(self): + """ + We need to use socket._fileobject Because SSL.Connection + doesn't have a 'dup'. Not exactly sure WHY this is, but + this is backed up by comments in socket.py and SSL/connection.c + """ + self.connection = self.request # for doPOST + self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) + self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) + + +class SecureXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer, SecureTCPServer): + def __init__(self, addr, + requestHandler=SecureXMLRPCRequestHandler, + certificate="server.cert",privatekey="server.pem", + logRequests=1): + """ + This is the exact same code as SimpleXMLRPCServer.__init__ + except it calls SecureTCPServer.__init__ instead of plain + old TCPServer.__init__ + """ + self.funcs = {} + self.logRequests = logRequests + self.instance = None + SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self,False,None) + SecureTCPServer.__init__(self, addr, requestHandler,certificate=certificate,privatekey=privatekey) + + +def test(): + server = SecureXMLRPCServer.SecureXMLRPCServer(("localhost", 8000),requestHandler=SecureXMLRPCRequestHandler,certificate="./certs/test.cert.pem",privatekey="./certs/test.key.pem") + server.register_introspection_functions() + server.register_function(lambda x: x*x, 'square') + server.serve_forever() + +if __name__ == '__main__': + test() +