Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 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 | """ |
Hynek Schlawack | 8b7e455 | 2016-03-13 07:51:09 +0100 | [diff] [blame] | 17 | |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 18 | import SimpleXMLRPCServer |
Hynek Schlawack | 8b7e455 | 2016-03-13 07:51:09 +0100 | [diff] [blame] | 19 | import SocketServer |
| 20 | import os |
| 21 | import socket |
| 22 | |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 23 | from OpenSSL import SSL |
| 24 | |
Hynek Schlawack | 8b7e455 | 2016-03-13 07:51:09 +0100 | [diff] [blame] | 25 | |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 26 | class SSLWrapper: |
| 27 | """ |
| 28 | This whole class exists just to filter out a parameter |
| 29 | passed in to the shutdown() method in SimpleXMLRPC.doPOST() |
| 30 | """ |
| 31 | def __init__(self, conn): |
| 32 | """ |
| 33 | Connection is not yet a new-style class, |
| 34 | so I'm making a proxy instead of subclassing. |
| 35 | """ |
| 36 | self.__dict__["conn"] = conn |
Hynek Schlawack | 8b7e455 | 2016-03-13 07:51:09 +0100 | [diff] [blame] | 37 | |
| 38 | def __getattr__(self, name): |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 39 | return getattr(self.__dict__["conn"], name) |
Hynek Schlawack | 8b7e455 | 2016-03-13 07:51:09 +0100 | [diff] [blame] | 40 | |
| 41 | def __setattr__(self, name, value): |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 42 | setattr(self.__dict__["conn"], name, value) |
Hynek Schlawack | 8b7e455 | 2016-03-13 07:51:09 +0100 | [diff] [blame] | 43 | |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 44 | def shutdown(self, how=1): |
| 45 | """ |
| 46 | SimpleXMLRpcServer.doPOST calls shutdown(1), |
| 47 | and Connection.shutdown() doesn't take |
| 48 | an argument. So we just discard the argument. |
| 49 | """ |
| 50 | self.__dict__["conn"].shutdown() |
Hynek Schlawack | 8b7e455 | 2016-03-13 07:51:09 +0100 | [diff] [blame] | 51 | |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 52 | def accept(self): |
| 53 | """ |
| 54 | This is the other part of the shutdown() workaround. |
| 55 | Since servers create new sockets, we have to infect |
| 56 | them with our magic. :) |
| 57 | """ |
| 58 | c, a = self.__dict__["conn"].accept() |
| 59 | return (SSLWrapper(c), a) |
| 60 | |
| 61 | |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 62 | class SecureTCPServer(SocketServer.TCPServer): |
| 63 | """ |
| 64 | Just like TCPServer, but use a socket. |
| 65 | This really ought to let you specify the key and certificate files. |
| 66 | """ |
| 67 | def __init__(self, server_address, RequestHandlerClass): |
Hynek Schlawack | 8b7e455 | 2016-03-13 07:51:09 +0100 | [diff] [blame] | 68 | SocketServer.BaseServer.__init__( |
| 69 | self, server_address, RequestHandlerClass |
| 70 | ) |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 71 | |
Hynek Schlawack | 8b7e455 | 2016-03-13 07:51:09 +0100 | [diff] [blame] | 72 | # Same as normal, but make it secure: |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 73 | ctx = SSL.Context(SSL.SSLv23_METHOD) |
| 74 | ctx.set_options(SSL.OP_NO_SSLv2) |
| 75 | |
| 76 | dir = os.curdir |
Hynek Schlawack | 8b7e455 | 2016-03-13 07:51:09 +0100 | [diff] [blame] | 77 | ctx.use_privatekey_file(os.path.join(dir, 'server.pkey')) |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 78 | ctx.use_certificate_file(os.path.join(dir, 'server.cert')) |
| 79 | |
Hynek Schlawack | 8b7e455 | 2016-03-13 07:51:09 +0100 | [diff] [blame] | 80 | self.socket = SSLWrapper( |
| 81 | SSL.Connection( |
| 82 | ctx, socket.socket(self.address_family, self.socket_type) |
| 83 | ) |
| 84 | ) |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 85 | self.server_bind() |
| 86 | self.server_activate() |
| 87 | |
| 88 | |
Hynek Schlawack | 8b7e455 | 2016-03-13 07:51:09 +0100 | [diff] [blame] | 89 | class SecureXMLRPCRequestHandler( |
| 90 | SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 91 | def setup(self): |
| 92 | """ |
| 93 | We need to use socket._fileobject Because SSL.Connection |
| 94 | doesn't have a 'dup'. Not exactly sure WHY this is, but |
| 95 | this is backed up by comments in socket.py and SSL/connection.c |
| 96 | """ |
Hynek Schlawack | 8b7e455 | 2016-03-13 07:51:09 +0100 | [diff] [blame] | 97 | self.connection = self.request # for doPOST |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 98 | self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) |
| 99 | self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 100 | |
Hynek Schlawack | 8b7e455 | 2016-03-13 07:51:09 +0100 | [diff] [blame] | 101 | |
| 102 | class SecureXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer, |
| 103 | SecureTCPServer): |
Jean-Paul Calderone | 897bc25 | 2008-02-18 20:50:23 -0500 | [diff] [blame] | 104 | def __init__(self, addr, |
| 105 | requestHandler=SecureXMLRPCRequestHandler, |
| 106 | logRequests=1): |
| 107 | """ |
| 108 | This is the exact same code as SimpleXMLRPCServer.__init__ |
| 109 | except it calls SecureTCPServer.__init__ instead of plain |
| 110 | old TCPServer.__init__ |
| 111 | """ |
| 112 | self.funcs = {} |
| 113 | self.logRequests = logRequests |
| 114 | self.instance = None |
| 115 | SecureTCPServer.__init__(self, addr, requestHandler) |