blob: 757b49c040dec7bedc79c323e6a53c8ea71acdd6 [file] [log] [blame]
Jean-Paul Calderone897bc252008-02-18 20:50:23 -05001"""
2SecureXMLRPCServer module using pyOpenSSL 0.5
3Written 0907.2002
4by Michal Wallace
5http://www.sabren.net/
6
7This acts exactly like SimpleXMLRPCServer
8from the standard python library, but
9uses secure connections. The technique
10and classes should work for any SocketServer
11style server. However, the code has not
12been extensively tested.
13
14This code is in the public domain.
15It is provided AS-IS WITH NO WARRANTY WHATSOEVER.
16"""
17import SocketServer
18import os, socket
19import SimpleXMLRPCServer
20from OpenSSL import SSL
21
22class 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
55class 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):
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 dir = os.curdir
68 ctx.use_privatekey_file (os.path.join(dir, 'server.pkey'))
69 ctx.use_certificate_file(os.path.join(dir, 'server.cert'))
70
71 self.socket = SSLWrapper(SSL.Connection(ctx, socket.socket(self.address_family,
72 self.socket_type)))
73 self.server_bind()
74 self.server_activate()
75
76
77class SecureXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
78 def setup(self):
79 """
80 We need to use socket._fileobject Because SSL.Connection
81 doesn't have a 'dup'. Not exactly sure WHY this is, but
82 this is backed up by comments in socket.py and SSL/connection.c
83 """
84 self.connection = self.request # for doPOST
85 self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
86 self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
87
88
89class SecureXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer, SecureTCPServer):
90 def __init__(self, addr,
91 requestHandler=SecureXMLRPCRequestHandler,
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 SecureTCPServer.__init__(self, addr, requestHandler)
102