| """ | |
| 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 SimpleXMLRPCServer | |
| import SocketServer | |
| import os | |
| import socket | |
| 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): | |
| 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) | |
| dir = os.curdir | |
| ctx.use_privatekey_file(os.path.join(dir, 'server.pkey')) | |
| ctx.use_certificate_file(os.path.join(dir, 'server.cert')) | |
| 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, | |
| 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 | |
| SecureTCPServer.__init__(self, addr, requestHandler) |