SecureXMLRPCServer.py 3.5 KB

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