[PATCH] https: support tls sni (server name indication) for https urls (issue3090)

Alex Orange crazycasta at gmail.com
Tue Jan 13 01:11:05 UTC 2015


# HG changeset patch
# User Alex Orange <crazycasta at gmail.com>
# Date 1421110880 25200
#      Mon Jan 12 18:01:20 2015 -0700
# Node ID b683de04cfd910f8f9c1c9400c748a11ef853c35
# Parent  678f53865c6860a950392691814766957ee89316
https: support tls sni (server name indication) for https urls (issue3090)

SNI is a common way of sharing servers across multiple domains using separate
SSL certificates. As of Python 2.7.9 SSLContext has been backported from
Python 3. This patch changes sslutil's ssl_wrap_socket to use SSLContext and
take a server hostname as and argument. It also changes the url module to make
use of this argument.

diff -r 678f53865c68 -r b683de04cfd9 mercurial/sslutil.py
--- a/mercurial/sslutil.py	Thu Jan 08 00:01:03 2015 +0100
+++ b/mercurial/sslutil.py	Mon Jan 12 18:01:20 2015 -0700
@@ -15,16 +15,39 @@
     import ssl
     CERT_REQUIRED = ssl.CERT_REQUIRED
     PROTOCOL_TLSv1 = ssl.PROTOCOL_TLSv1
-    def ssl_wrap_socket(sock, keyfile, certfile, ssl_version=PROTOCOL_TLSv1,
-                cert_reqs=ssl.CERT_NONE, ca_certs=None):
-        sslsocket = ssl.wrap_socket(sock, keyfile, certfile,
-                                    cert_reqs=cert_reqs, ca_certs=ca_certs,
-                                    ssl_version=ssl_version)
-        # check if wrap_socket failed silently because socket had been closed
-        # - see http://bugs.python.org/issue13721
-        if not sslsocket.cipher():
-            raise util.Abort(_('ssl connection failed'))
-        return sslsocket
+    try:
+        ssl_context = ssl.SSLContext
+
+        def ssl_wrap_socket(sock, keyfile, certfile, ssl_version=PROTOCOL_TLSv1,
+                            cert_reqs=ssl.CERT_NONE, ca_certs=None,
+                            server_hostname=None):
+            sslcontext = ssl.SSLContext(ssl_version)
+            if keyfile is not None:
+                sslcontext.load_cert_chain(certfile, keyfile)
+            sslcontext.verify_mode = cert_reqs
+            sslcontext.load_verify_locations(cafile=ca_certs)
+
+            sslsocket = sslcontext.wrap_socket(sock,
+                                               server_hostname=server_hostname)
+            # check if wrap_socket failed silently because socket had been
+            # closed
+            # - see http://bugs.python.org/issue13721
+            if not sslsocket.cipher():
+                raise util.Abort(_('ssl connection failed'))
+            return sslsocket
+    except AttributeError:
+        def ssl_wrap_socket(sock, keyfile, certfile, ssl_version=PROTOCOL_TLSv1,
+                            cert_reqs=ssl.CERT_NONE, ca_certs=None,
+                            server_hostname=None):
+            sslsocket = ssl.wrap_socket(sock, keyfile, certfile,
+                                        cert_reqs=cert_reqs, ca_certs=ca_certs,
+                                        ssl_version=ssl_version)
+            # check if wrap_socket failed silently because socket had been
+            # closed
+            # - see http://bugs.python.org/issue13721
+            if not sslsocket.cipher():
+                raise util.Abort(_('ssl connection failed'))
+            return sslsocket
 except ImportError:
     CERT_REQUIRED = 2
 
@@ -33,7 +56,8 @@
     import socket, httplib
 
     def ssl_wrap_socket(sock, keyfile, certfile, ssl_version=PROTOCOL_TLSv1,
-                        cert_reqs=CERT_REQUIRED, ca_certs=None):
+                        cert_reqs=CERT_REQUIRED, ca_certs=None,
+                        server_hostname=None):
         if not util.safehasattr(socket, 'ssl'):
             raise util.Abort(_('Python SSL support not found'))
         if ca_certs:
diff -r 678f53865c68 -r b683de04cfd9 mercurial/url.py
--- a/mercurial/url.py	Thu Jan 08 00:01:03 2015 +0100
+++ b/mercurial/url.py	Mon Jan 12 18:01:20 2015 -0700
@@ -185,7 +185,8 @@
             self.sock.connect((self.host, self.port))
             if _generic_proxytunnel(self):
                 # we do not support client X.509 certificates
-                self.sock = sslutil.ssl_wrap_socket(self.sock, None, None)
+                self.sock = sslutil.ssl_wrap_socket(self.sock, None, None,
+                                                    server_hostname=self.host)
         else:
             keepalive.HTTPConnection.connect(self)
 
@@ -341,7 +342,7 @@
                 _generic_proxytunnel(self)
                 host = self.realhostport.rsplit(':', 1)[0]
             self.sock = sslutil.ssl_wrap_socket(
-                self.sock, self.key_file, self.cert_file,
+                self.sock, self.key_file, self.cert_file, server_hostname=host,
                 **sslutil.sslkwargs(self.ui, host))
             sslutil.validator(self.ui, host)(self.sock)
 


More information about the Mercurial-devel mailing list