|
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 |