iro/xmlrpc/SecureXMLRPCServer.py
branchdevel
changeset 240 3406d3bf05d4
parent 239 4cf5e664c847
child 241 546316b0b09c
equal deleted inserted replaced
239:4cf5e664c847 240:3406d3bf05d4
     1 """
       
     2 SecureXMLRPCServer module using pyOpenSSL 0.5
       
     3 Written 0907.2002
       
     4 by Michal Wallace
       
     5 http://www.sabren.net/
       
     6 
       
     7 This acts exactly like SimpleXMLRPCServer
       
     8 from the standard python library, but
       
     9 uses secure connections. The technique
       
    10 and classes should work for any SocketServer
       
    11 style server. However, the code has not
       
    12 been extensively tested.
       
    13 
       
    14 This code is in the public domain.
       
    15 It is provided AS-IS WITH NO WARRANTY WHATSOEVER.
       
    16 """
       
    17 import SocketServer
       
    18 import os, socket
       
    19 import SimpleXMLRPCServer
       
    20 from OpenSSL import SSL
       
    21 
       
    22 class SSLWrapper:
       
    23     """
       
    24     This whole class exists just to filter out a parameter
       
    25     passed in to the shutdown() method in SimpleXMLRPC.doPOST()
       
    26     """
       
    27     def __init__(self, conn):
       
    28         """
       
    29         Connection is not yet a new-style class,
       
    30         so I'm making a proxy instead of subclassing.
       
    31         """
       
    32         self.__dict__["conn"] = conn
       
    33     def __getattr__(self,name):
       
    34         return getattr(self.__dict__["conn"], name)
       
    35     def __setattr__(self,name, value):
       
    36         setattr(self.__dict__["conn"], name, value)
       
    37     def shutdown(self, how=1):
       
    38         """
       
    39         SimpleXMLRpcServer.doPOST calls shutdown(1),
       
    40         and Connection.shutdown() doesn't take
       
    41         an argument. So we just discard the argument.
       
    42         """
       
    43         self.__dict__["conn"].shutdown()
       
    44     def accept(self):
       
    45         """
       
    46         This is the other part of the shutdown() workaround.
       
    47         Since servers create new sockets, we have to infect
       
    48         them with our magic. :)
       
    49         """
       
    50         c, a = self.__dict__["conn"].accept()
       
    51         return (SSLWrapper(c), a)
       
    52 
       
    53 
       
    54 
       
    55 class SecureTCPServer(SocketServer.TCPServer):
       
    56     """
       
    57     Just like TCPServer, but use a socket.
       
    58     This really ought to let you specify the key and certificate files.
       
    59     """
       
    60     def __init__(self, server_address, RequestHandlerClass,certificate,privatekey):
       
    61         SocketServer.BaseServer.__init__(self, server_address, RequestHandlerClass)
       
    62 
       
    63         ## Same as normal, but make it secure:
       
    64         ctx = SSL.Context(SSL.SSLv23_METHOD)
       
    65         ctx.set_options(SSL.OP_NO_SSLv2)
       
    66 
       
    67         ctx.use_privatekey_file (privatekey)
       
    68         ctx.use_certificate_file(certificate)
       
    69 
       
    70         self.socket = SSLWrapper(SSL.Connection(ctx, socket.socket(self.address_family,
       
    71                                                                   self.socket_type)))
       
    72         self.server_bind()
       
    73         self.server_activate()
       
    74 
       
    75 
       
    76 class SecureXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
       
    77     def setup(self):
       
    78         """
       
    79         We need to use socket._fileobject Because SSL.Connection
       
    80         doesn't have a 'dup'. Not exactly sure WHY this is, but
       
    81         this is backed up by comments in socket.py and SSL/connection.c
       
    82         """
       
    83         self.connection = self.request # for doPOST
       
    84         self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
       
    85         self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
       
    86     
       
    87 
       
    88 class SecureXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer, SecureTCPServer):
       
    89     def __init__(self, addr,
       
    90                  requestHandler=SecureXMLRPCRequestHandler,
       
    91                  certificate="server.cert",privatekey="server.pem",
       
    92                  logRequests=1):
       
    93         """
       
    94         This is the exact same code as SimpleXMLRPCServer.__init__
       
    95         except it calls SecureTCPServer.__init__ instead of plain
       
    96         old TCPServer.__init__
       
    97         """
       
    98         self.funcs = {}
       
    99         self.logRequests = logRequests
       
   100         self.instance = None
       
   101         SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self,False,None)
       
   102         SecureTCPServer.__init__(self, addr, requestHandler,certificate=certificate,privatekey=privatekey)
       
   103 
       
   104 
       
   105 def test():
       
   106     server = SecureXMLRPCServer.SecureXMLRPCServer(("localhost", 8000),requestHandler=SecureXMLRPCRequestHandler,certificate="./certs/test.cert.pem",privatekey="./certs/test.key.pem")
       
   107     server.register_introspection_functions()
       
   108     server.register_function(lambda x: x*x, 'square')
       
   109     server.serve_forever()
       
   110 
       
   111 if __name__ == '__main__':
       
   112     test()
       
   113