big drastic change: use SSLContext.wrap_socket()
[sfa.git] / sfa / server / threadedserver.py
index c966b9f..4be1aff 100644 (file)
@@ -12,16 +12,16 @@ import traceback
 import threading
 from queue import Queue
 import socketserver
 import threading
 from queue import Queue
 import socketserver
+import ssl
 import http.server
 import xmlrpc.server
 import http.server
 import xmlrpc.server
-from OpenSSL import SSL
+import xmlrpc.client
 
 from sfa.util.sfalogging import logger
 from sfa.util.config import Config
 from sfa.util.cache import Cache
 from sfa.trust.certificate import Certificate
 from sfa.trust.trustedroots import TrustedRoots
 
 from sfa.util.sfalogging import logger
 from sfa.util.config import Config
 from sfa.util.cache import Cache
 from sfa.trust.certificate import Certificate
 from sfa.trust.trustedroots import TrustedRoots
-import xmlrpc.client
 
 # don't hard code an api class anymore here
 from sfa.generic import Generic
 
 # don't hard code an api class anymore here
 from sfa.generic import Generic
@@ -96,14 +96,8 @@ class SecureXMLRpcRequestHandler(xmlrpc.server.SimpleXMLRPCRequestHandler):
     but it uses HTTPS for transporting XML data.
     """
 
     but it uses HTTPS for transporting XML data.
     """
 
-    def setup(self):
-        self.connection = self.request
-        self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
-        self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
-# porting to python3
-# xmlrpc.server.SimpleXMLRPCRequestHandler inherits
-# http.server.BaseHTTPRequestHandler, that already has
-# the rfile and wfile attributes
+    # porting to python3
+    # setup() no longer needed
 
     def do_POST(self):
         """
 
     def do_POST(self):
         """
@@ -115,7 +109,7 @@ class SecureXMLRpcRequestHandler(xmlrpc.server.SimpleXMLRPCRequestHandler):
         try:
             peer_cert = Certificate()
             peer_cert.load_from_pyopenssl_x509(
         try:
             peer_cert = Certificate()
             peer_cert.load_from_pyopenssl_x509(
-                self.connection.get_peer_certificate())
+                self.connection.getpeercert())
             generic = Generic.the_flavour()
             self.api = generic.make_api(peer_cert=peer_cert,
                                         interface=self.server.interface,
             generic = Generic.the_flavour()
             self.api = generic.make_api(peer_cert=peer_cert,
                                         interface=self.server.interface,
@@ -149,12 +143,12 @@ class SecureXMLRpcRequestHandler(xmlrpc.server.SimpleXMLRPCRequestHandler):
             self.send_header("Content-type", "text/xml")
             self.send_header("Content-length", str(len(response)))
             self.end_headers()
             self.send_header("Content-type", "text/xml")
             self.send_header("Content-length", str(len(response)))
             self.end_headers()
-            self.wfile.write(response)
+            self.wfile.write(response.encode())
             self.wfile.flush()
             # close db connection
             self.api.close_dbsession()
             # shut down the connection
             self.wfile.flush()
             # close db connection
             self.api.close_dbsession()
             # shut down the connection
-            self.connection.shutdown()  # Modified here!
+            self.connection.shutdown(socket.SHUT_RDWR)  # Modified here!
 
 ##
 # Taken from the web (XXX find reference). Implements an HTTPS xmlrpc server
 
 ##
 # Taken from the web (XXX find reference). Implements an HTTPS xmlrpc server
@@ -169,7 +163,7 @@ class SecureXMLRPCServer(http.server.HTTPServer,
         Secure XML-RPC server.
 
         It it very similar to SimpleXMLRPCServer
         Secure XML-RPC server.
 
         It it very similar to SimpleXMLRPCServer
-         but it uses HTTPS for transporting XML data.
+        but it uses HTTPS for transporting XML data.
         """
         logger.debug(
             f"SecureXMLRPCServer.__init__, server_address={server_address}, "
         """
         logger.debug(
             f"SecureXMLRPCServer.__init__, server_address={server_address}, "
@@ -181,30 +175,30 @@ class SecureXMLRPCServer(http.server.HTTPServer,
         self.method_map = {}
         # add cache to the request handler
         HandlerClass.cache = Cache()
         self.method_map = {}
         # add cache to the request handler
         HandlerClass.cache = Cache()
-        # for compatibility with python 2.4 (centos53)
-        if sys.version_info < (2, 5):
-            xmlrpc.server.SimpleXMLRPCDispatcher.__init__(self)
-        else:
-            xmlrpc.server.SimpleXMLRPCDispatcher.__init__(
-                self, True, None)
+        xmlrpc.server.SimpleXMLRPCDispatcher.__init__(self, True, None)
         socketserver.BaseServer.__init__(self, server_address, HandlerClass)
         socketserver.BaseServer.__init__(self, server_address, HandlerClass)
-        ctx = SSL.Context(SSL.SSLv23_METHOD)
-        ctx.use_privatekey_file(key_file)
-        ctx.use_certificate_file(cert_file)
+        ssl_context = ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH)
+        ssl_context.load_cert_chain(cert_file, key_file)
         # If you wanted to verify certs against known CAs..
         # this is how you would do it
         # If you wanted to verify certs against known CAs..
         # this is how you would do it
-        # ctx.load_verify_locations('/etc/sfa/trusted_roots/plc.gpo.gid')
+        # ssl_context.load_verify_locations('/etc/sfa/trusted_roots/plc.gpo.gid')
         config = Config()
         trusted_cert_files = TrustedRoots(
             config.get_trustedroots_dir()).get_file_list()
         config = Config()
         trusted_cert_files = TrustedRoots(
             config.get_trustedroots_dir()).get_file_list()
+        cadata = ""
         for cert_file in trusted_cert_files:
         for cert_file in trusted_cert_files:
-            ctx.load_verify_locations(cert_file)
-        ctx.set_verify(SSL.VERIFY_PEER |
-                       SSL.VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback)
-        ctx.set_verify_depth(5)
-        ctx.set_app_data(self)
-        self.socket = SSL.Connection(ctx, socket.socket(self.address_family,
-                                                        self.socket_type))
+            with open(cert_file) as cafile:
+                cadata += cafile.read()
+        ssl_context.load_verify_locations(cadata=cadata)
+#        ctx.set_verify(SSL.VERIFY_PEER |
+#                       SSL.VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback)
+#        ctx.set_verify_depth(5)
+#        ctx.set_app_data(self)
+        # with python3 we use standard library SSLContext.wrap_socket()
+        # instead of an OpenSSL.SSL.Connection object
+        self.socket = ssl_context.wrap_socket(
+            socket.socket(self.address_family, self.socket_type),
+            server_side=True)
         self.server_bind()
         self.server_activate()
 
         self.server_bind()
         self.server_activate()
 
@@ -225,44 +219,15 @@ class SecureXMLRPCServer(http.server.HTTPServer,
             raise xmlrpc.client.Fault(1, ''.join(
                 traceback.format_exception(type, value, tb)))
 
             raise xmlrpc.client.Fault(1, ''.join(
                 traceback.format_exception(type, value, tb)))
 
-    # override this one from the python 2.7 code
-    # originally defined in class TCPServer
-    def shutdown_request(self, request):
-        """
-        Called to shutdown and close an individual request.
-        """
-        # ----------
-        # the std python 2.7 code just attempts a
-        # request.shutdown(socket.SHUT_WR)
-        # this works fine with regular sockets
-        # However we are dealing with an instance of
-        # OpenSSL.SSL.Connection instead
-        # This one only supports shutdown(), and in addition this does not
-        # always perform as expected
-        # ---------- std python 2.7 code
-        try:
-            # explicitly shutdown.  socket.close() merely releases
-            # the socket and waits for GC to perform the actual close.
-            request.shutdown(socket.SHUT_WR)
-        except socket.error:
-            pass  # some platforms may raise ENOTCONN here
-        # ----------
-        except TypeError:
-            # we are dealing with an OpenSSL.Connection object,
-            # try to shut it down but never mind if that fails
-            try:
-                request.shutdown()
-            except:
-                pass
-        # ----------
-        self.close_request(request)
+    # porting to python3
+    # shutdown_request() no longer needed
+
 
 # From Active State code: http://code.activestate.com/recipes/574454/
 # This is intended as a drop-in replacement for the ThreadingMixIn class in
 # module SocketServer of the standard lib. Instead of spawning a new thread
 # for each request, requests are processed by of pool of reusable threads.
 
 
 # From Active State code: http://code.activestate.com/recipes/574454/
 # This is intended as a drop-in replacement for the ThreadingMixIn class in
 # module SocketServer of the standard lib. Instead of spawning a new thread
 # for each request, requests are processed by of pool of reusable threads.
 
-
 class ThreadPoolMixIn(socketserver.ThreadingMixIn):
     """
     use a thread pool instead of a new thread on every request
 class ThreadPoolMixIn(socketserver.ThreadingMixIn):
     """
     use a thread pool instead of a new thread on every request