116 lines
3.5 KiB
Python
116 lines
3.5 KiB
Python
"""
|
|
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)
|