[NEW PATCH] Replace m2crypto with python's standard ssl (via gerrit-bot)

Dan Kenigsberg danken at redhat.com
Wed Sep 14 20:01:01 UTC 2011


New patch submitted by Dan Kenigsberg (danken at redhat.com)

You can review this change at: http://gerrit.usersys.redhat.com/797

commit 77f5eccd6b427e94bfe72452b51fd44365470c0d
Author: Dan Kenigsberg <danken at redhat.com>
Date:   Fri Oct 1 15:43:43 2010 +0200

    Replace m2crypto with python's standard ssl
    
    Change-Id: Ie2162f72c914dc6c7832ca2951bbf151ed058859

diff --git a/vdsm.spec.in b/vdsm.spec.in
index f1a3473..e076cd8 100644
--- a/vdsm.spec.in
+++ b/vdsm.spec.in
@@ -18,7 +18,7 @@ Requires: python which
 Requires: sudo >= 1.7.3
 Requires: qemu-kvm
 Requires: qemu-img >= 2:0.12.1.2-2.172
-Requires: m2crypto ethtool logrotate
+Requires: ethtool logrotate
 Requires: iscsi-initiator-utils >= 6.2.0.872-15
 Requires: nfs-utils dmidecode
 Requires: lvm2 >= 2.02.72-8.el6_0.4
@@ -181,7 +181,6 @@ Used by the trained monkeys at Red Hat to insert chaos and mayhem in to VDSM
 %package cli
 Summary: VDSM command line interface
 Group: Applications/System
-Requires: m2crypto
 
 %description cli
 Call VDSM commands from the command line. Used for testing and debugging.
@@ -372,7 +371,8 @@ machines without running real guests.
 %{_sysconfdir}/bash_completion.d/vdsClient
 %{_datadir}/%{vdsm_name}/vdscli.py*
 %{_datadir}/%{vdsm_name}/dumpStorageTable.py*
-%{_bindir}/vdsClient
+%{_datadir}/%{vdsm_name}/SecureXMLRPCServer.py*
+%attr (755,root,root) %{_bindir}/vdsClient
 %{_mandir}/man1/vdsClient.1*
 
 %files bootstrap
diff --git a/vdsm/SecureXMLRPCServer.py b/vdsm/SecureXMLRPCServer.py
index c092296..3fbecc8 100644
--- a/vdsm/SecureXMLRPCServer.py
+++ b/vdsm/SecureXMLRPCServer.py
@@ -1,5 +1,4 @@
 # Copyright 2008 Red Hat, Inc. and/or its affiliates.
-# Copyright 2001 Brian Quinlan.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -7,7 +6,7 @@
 #
 # * Redistributions of source code must retain the above copyright
 #   notice, this list of conditions and the following disclaimer.
-#    
+#
 # * Redistributions in binary form must reproduce the above copyright
 #   notice, this list of conditions and the following disclaimer in
 #   the documentation and/or other materials provided with the
@@ -28,106 +27,133 @@
 
 
 """SecureXMLRPCServer.py - simple XML RPC server supporting SSL.
-
-Based on this article: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81549
-
-For windows users: http://webcleaner.sourceforge.net/pyOpenSSL-0.6.win32-py2.4.exe
 """
 
-import SocketServer
-import BaseHTTPServer
 import SimpleXMLRPCServer
-
+import xmlrpclib
+import ssl
+import httplib
 import socket
-from M2Crypto import SSL
-
-class PoliteSSLConnection(SSL.Connection):
-
-    def gettimeout(self):
-         return self.socket.gettimeout()
-
-    def accept(self):
-        """Do a simple tcp accept only"""
-        sock, addr = self.socket.accept()
-        return PoliteSSLConnection(self.ctx, sock), addr
-
-    def _finish_accept_ssl(self, sock, addr):
-        """Finish ssl accept (possibly on a new thread)
-        Code based on accept() of M2Crypto/SSL/Connection.py 0.18-2"""
-        sock.addr = addr
-        sock.setup_ssl()
-        sock.set_accept_state()
-        # Here is the change from M2Crpyto's code.
-        # make sure sock.accept_ssl() does not block if self in nonblocking.
-        sock.settimeout(self.socket.gettimeout())
-        # On exception, I'm being nice to my peer (possible attacker?) and
-        # close his socket.
-        try:
-            sock.accept_ssl()
-            check = getattr(self, 'postConnectionCheck', self.serverPostConnectionCheck)
-            if check is not None:
-                if not check(self.get_peer_cert(), sock.addr[0]):
-                    raise SSL.Checker.SSLVerificationError, 'post connection check failed'
-        except Exception, e:
-            e.addr = addr
-            sock.close()
-            raise
-        return sock, addr
-
-class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
-    """Secure XML-RPC request handler class.
-
-    It it very similar to SimpleXMLRPCRequestHandler but it uses HTTPS for transporting XML data.
-    """
-    wbufsize = -1
-
-    def do_POST(self):
-        """Handles the HTTPS POST request."""
-        SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.do_POST(self)
-        # I dont understand why the inherited
-        # self.connection.shutdown(SSL_SENT_SHUTDOWN) without .close()
-        # makes me hang
-        self.connection.shutdown(0)
-        self.connection.close()
-
-class SecureXMLRPCServer(BaseHTTPServer.HTTPServer, SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
-    def __init__(self, server_address, keyFile, certFile, caCert,
-                 logRequests=False, timeout=None, requestHandler=SecureXMLRpcRequestHandler):
-        """Secure XML-RPC server.
-
-        It it very similar to SimpleXMLRPCServer but it uses HTTPS for transporting XML data.
-        """
-        self.logRequests = logRequests
-
-        try:
-            SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self, False, None)
-        except TypeError:
-            # older versions of SimpleXMLRPCServer had a different API
-            SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self)
-
-        SocketServer.BaseServer.__init__(self, server_address, requestHandler)
-        ctx = SSL.Context()
-
-        ctx.load_cert_chain(certFile, keyFile)
-
-        ctx.set_client_CA_list_from_file(caCert)
-        ctx.load_verify_info(caCert)
-
-        ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, 10)
-        ctx.set_session_id_ctx ('vdsm-ssl')
-        self.socket = PoliteSSLConnection(ctx, socket.socket(
-                                self.address_family, self.socket_type))
-        self.socket.settimeout(timeout)
-        self.server_bind()
-        self.server_activate()
+import SocketServer
+
+class VerificationError(RuntimeError): pass
+
+def verify_commonName(sock, name):
+    """Verify that commonName field in peer's certiface matches peer's address"""
+
+    cert = sock.getpeercert()
+    if not cert:
+        raise VerificationError('peer gave no certificate')
+
+    cn = dict([ rdn[0] for rdn in cert['subject'] ])['commonName']
+    try:
+        hosts = socket.gethostbyname_ex(cn)[2]
+    except:
+        hosts = []
+    if name not in hosts + [cn]:
+        raise VerificationError('peer %s does not match its commonName %s'
+                                % (name, cn))
+
+SecureXMLRPCRequestHandler = SimpleXMLRPCServer.SimpleXMLRPCRequestHandler
+
+class SecureXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):
+    def __init__(self, addr,
+                 requestHandler=SimpleXMLRPCServer.SimpleXMLRPCRequestHandler,
+                 logRequests=True, allow_none=False, encoding=None,
+                 bind_and_activate=True,
+                 keyfile=None, certfile=None, ca_certs=None):
+        """Initialize a SimpleXMLRPCServer instance but wrap its .socket member with ssl."""
+
+        SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, addr,
+                 requestHandler,
+                 logRequests, allow_none, encoding,
+                 bind_and_activate=False)
+        self.socket = ssl.wrap_socket(self.socket,
+                 keyfile=keyfile, certfile=certfile,
+                 ca_certs=ca_certs, server_side=True,
+                 cert_reqs=ssl.CERT_REQUIRED,
+                 do_handshake_on_connect=False)
+        if bind_and_activate:
+            self.server_bind()
+            self.server_activate()
 
     def finish_request(self, request, client_address):
-        """Finish one request by doing ssl handshake and instantiating RequestHandlerClass."""
-        request, client_address = self.socket._finish_accept_ssl(request, client_address)
-        self.RequestHandlerClass(request, client_address, self)
+        request.do_handshake()
+
+        if self._verifyCommonName:
+            verify_commonName(request, client_address[0])
+
+        return SimpleXMLRPCServer.SimpleXMLRPCServer.finish_request(self, request,
+                                                             client_address)
+
+    def handle_error(self, request, client_address):
+        import logging
+        logging.error('client %s', client_address, exc_info=True)
+
+
+class SecureThreadedXMLRPCServer(SocketServer.ThreadingMixIn,
+                                 SecureXMLRPCServer): pass
+
+class VerifyingHTTPSConnection(httplib.HTTPSConnection):
+    def __init__(self, host, port=None, key_file=None, cert_file=None,
+                 strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
+                 ca_certs=None, cert_reqs=ssl.CERT_REQUIRED):
+        httplib.HTTPSConnection.__init__(self, host, port, key_file, cert_file,
+                      strict, timeout)
+        self.ca_certs = ca_certs
+        self.cert_reqs = cert_reqs
+
+    def connect(self):
+        "Connect to a host on a given (SSL) port."
+
+        sock = socket.create_connection((self.host, self.port), self.timeout)
+        if self._tunnel_host:
+            self.sock = sock
+            self._tunnel()
+        # DK added: pass ca_cert to sslsocket
+        self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file,
+                                    ca_certs=self.ca_certs, server_side=False,
+                                    cert_reqs=self.cert_reqs)
+        verify_commonName(self.sock, self.host)
+
+class VerifyingSafeTransport(xmlrpclib.SafeTransport):
+    def __init__(self, use_datetime=0, key_file=None, cert_file=None,
+                 ca_certs=None, cert_reqs=ssl.CERT_REQUIRED):
+        xmlrpclib.SafeTransport.__init__(self, use_datetime)
+        self.key_file = key_file
+        self.cert_file = cert_file
+        self.ca_certs = ca_certs
+        self.cert_reqs = cert_reqs
+
+    def make_connection(self, host):
+        """Return VerifyingHTTPS object that is aware of ca_certs, and will create VerifyingHTTPSConnection"""
+        chost, self._extra_headers, x509 = self.get_host_info(host)
+        return VerifyingHTTPS(
+                        chost, None, key_file=self.key_file,
+                        cert_file=self.cert_file, ca_certs=self.ca_certs,
+                        cert_reqs=self.cert_reqs)
+
+
+class VerifyingHTTPS(httplib.HTTPS):
+    _connection_class = VerifyingHTTPSConnection
+
+    def __init__(self, host='', port=None, key_file=None, cert_file=None,
+                 strict=None, ca_certs=None, cert_reqs=ssl.CERT_REQUIRED):
+        """A ca_cert-aware HTTPS object, that creates a VerifyingHTTPSConnection"""
+        # provide a default host, pass the X509 cert info
+
+        # urf. compensate for bad input.
+        if port == 0:
+            port = None
+        self._setup(self._connection_class(host, port, key_file,
+                                           cert_file, strict, ca_certs=ca_certs,
+                                           cert_reqs=cert_reqs))
+
+        # we never actually use these for anything, but we keep them
+        # here for compatibility with post-1.5.2 CVS.
+        self.key_file = key_file
+        self.cert_file = cert_file
 
-class SecureThreadedXMLRPCServer(SocketServer.ThreadingMixIn, SecureXMLRPCServer):
-    pass
 
 class __Test(object):
     """Self-signed key, generated with
@@ -141,40 +167,25 @@ class __Test(object):
     def server(self):
         """Test xml rpc over https server"""
         class xmlrpc_registers:
-            def __init__(self):
-                import string
-                self.python_string = string
-
             def add(self, x, y):
                 return x + y
 
-            def mult(self,x,y):
-                return x*y
-
-            def div(self,x,y):
-                return x//y
-
             def wait(self):
                 import time
                 time.sleep(10)
                 return 1
 
-        server = SecureThreadedXMLRPCServer((self.host, self.port), self.KEYFILE, self.CERTFILE, self.CACERT)
+        server = SecureXMLRPCServer((self.host, self.port),
+                     keyfile=self.KEYFILE, certfile=self.CERTFILE, ca_certs=self.CACERT)
         server.register_instance(xmlrpc_registers())
         print "Serving HTTPS on", self.host, "port", self.port
         server.serve_forever()
 
     def client(self):
-        from M2Crypto.m2xmlrpclib import Server, SSL_Transport
-        from M2Crypto import SSL
-
-        ctx = SSL.Context()
-
-        ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, 16)
-        ctx.load_verify_locations(self.CACERT)
-        ctx.load_cert(self.CERTFILE, self.KEYFILE)
-
-        s = Server('https://%s:%s' % (self.host, self.port), SSL_Transport(ctx))
+        vtransport=VerifyingSafeTransport(key_file=self.KEYFILE,
+                        cert_file=self.CERTFILE, ca_certs=self.CACERT)
+        s = xmlrpclib.ServerProxy('https://%s:%s' % (self.host, self.port),
+                                  transport=vtransport)
         print s.add(2, 3)
 
 if __name__ == '__main__':
diff --git a/vdsm/clientIF.py b/vdsm/clientIF.py
index 241a86a..e6ef62f 100644
--- a/vdsm/clientIF.py
+++ b/vdsm/clientIF.py
@@ -32,8 +32,6 @@ import SimpleXMLRPCServer
 from xml.dom import minidom
 import uuid
 
-import M2Crypto.threading
-
 from storage.dispatcher import StorageDispatcher
 import storage.misc
 import storage.hba
@@ -219,15 +217,14 @@ class clientIF:
 
         server_address = (self.serverIP, int(self.serverPort))
         if config.getboolean('vars', 'ssl'):
-            class LoggingHandler(LoggingMixIn, SecureXMLRPCServer.SecureXMLRpcRequestHandler):
+            class LoggingHandler(LoggingMixIn, SecureXMLRPCServer.SecureXMLRPCRequestHandler):
                 def setup(self):
                     cif.threadLocal.client = self.client_address[0]
-                    return SecureXMLRPCServer.SecureXMLRpcRequestHandler.setup(self)
-            M2Crypto.threading.init()
+                    return SecureXMLRPCServer.SecureXMLRPCRequestHandler.setup(self)
             KEYFILE, CERTFILE, CACERT = self.getKeyCertFilenames()
             s = SecureXMLRPCServer.SecureThreadedXMLRPCServer(server_address,
                         KEYFILE, CERTFILE, CACERT,
-                        timeout=config.getint('vars', 'vds_responsiveness_timeout'),
+#                        timeout=config.getint('vars', 'vds_responsiveness_timeout'),
                         requestHandler=LoggingHandler)
             utils.closeOnExec(s.socket.socket.fileno())
         else:
diff --git a/vdsm/vm.py b/vdsm/vm.py
index 8a87db6..f270e34 100644
--- a/vdsm/vm.py
+++ b/vdsm/vm.py
@@ -117,17 +117,9 @@ class MigrationSourceThread(threading.Thread):
         except:
             pass
         if config.getboolean('vars', 'ssl'):
-            from M2Crypto import SSL
-
-            KEYFILE, CERTFILE, CACERT = self._vm.cif.getKeyCertFilenames()
-
-            ctx = SSL.Context()
-            ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, 16)
-            ctx.load_verify_locations(CACERT)
-            ctx.load_cert(CERTFILE, KEYFILE)
-
+            import vdscli
             serverAddress = 'https://' + self.remoteHost + ':' + self.remotePort
-            self.destServer = kaxmlrpclib.SslServer(serverAddress, ctx)
+            self.destServer = vdscli.connect(serverAddress, useSSL=True)
         else:
             serverAddress = 'http://' + self.remoteHost + ':' + self.remotePort
             self.destServer = kaxmlrpclib.Server(serverAddress)
diff --git a/vdsm_cli/vdscli.py.in b/vdsm_cli/vdscli.py.in
index 5644a50..2a6ebd9 100644
--- a/vdsm_cli/vdscli.py.in
+++ b/vdsm_cli/vdscli.py.in
@@ -22,6 +22,7 @@
 import xmlrpclib
 import subprocess
 import os
+import SecureXMLRPCServer
 
 d_useSSL = False
 d_tsPath = '@TRUSTSTORE@'
@@ -86,9 +87,6 @@ def connect(addrport=None, useSSL=None, tsPath=None):
     if useSSL is None: useSSL = d_useSSL
     if tsPath is None: tsPath = d_tsPath
     if useSSL:
-        from M2Crypto.m2xmlrpclib import SSL_Transport
-        from M2Crypto import SSL
-
         if os.name == 'nt':
             KEYFILE = tsPath + '\\keys\\rhevm.pem'
             CERTFILE = tsPath + '\\certs\\rhevm.cer'
@@ -98,14 +96,10 @@ def connect(addrport=None, useSSL=None, tsPath=None):
             CERTFILE = tsPath + '/certs/vdsmcert.pem'
             CACERT = tsPath + '/certs/cacert.pem'
 
-        ctx = SSL.Context ('sslv3')
-
-        ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, 16)
-        ctx.load_verify_locations(CACERT)
-        ctx.load_cert(CERTFILE, KEYFILE, lambda v: "mypass")
-
-        server = xmlrpclib.Server('https://%s' % addrport,
-                                SSL_Transport(ctx))
+        transport=SecureXMLRPCServer.VerifyingSafeTransport(key_file=KEYFILE,
+                        cert_file=CERTFILE, ca_certs=CACERT)
+        server = xmlrpclib.ServerProxy('https://%s' % addrport,
+                                       transport=transport)
     else:
         server = xmlrpclib.Server('http://%s' % addrport)
     return server




More information about the vdsm-patches mailing list