[PATCH 1 of 3] sslutil: move protocol determination to _hostsettings

Gregory Szorc gregory.szorc at gmail.com
Thu Jul 7 06:31:35 UTC 2016


# HG changeset patch
# User Gregory Szorc <gregory.szorc at gmail.com>
# Date 1467870444 25200
#      Wed Jul 06 22:47:24 2016 -0700
# Node ID f72b68901929352cd54e5d0c3b7e47b0d01c9313
# Parent  54ad81b0665f8b7191e6f049594db06b06d34e50
sslutil: move protocol determination to _hostsettings

Most of the logic for configuring TLS is now in this function.
Let's move protocol determination code there as well.

diff --git a/mercurial/sslutil.py b/mercurial/sslutil.py
--- a/mercurial/sslutil.py
+++ b/mercurial/sslutil.py
@@ -121,20 +121,38 @@ def _hostsettings(ui, hostname):
         'certfingerprints': [],
         # Path to file containing concatenated CA certs. Used by
         # SSLContext.load_verify_locations().
         'cafile': None,
         # Whether certificate verification should be disabled.
         'disablecertverification': False,
         # Whether the legacy [hostfingerprints] section has data for this host.
         'legacyfingerprint': False,
+        # PROTOCOL_* constant to use for SSLContext.__init__.
+        'protocol': None,
         # ssl.CERT_* constant used by SSLContext.verify_mode.
         'verifymode': None,
     }
 
+    # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
+    # that both ends support, including TLS protocols. On legacy stacks,
+    # the highest it likely goes in TLS 1.0. On modern stacks, it can
+    # support TLS 1.2.
+    #
+    # The PROTOCOL_TLSv* constants select a specific TLS version
+    # only (as opposed to multiple versions). So the method for
+    # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and
+    # disable protocols via SSLContext.options and OP_NO_* constants.
+    # However, SSLContext.options doesn't work unless we have the
+    # full/real SSLContext available to us.
+    if modernssl:
+        s['protocol'] = ssl.PROTOCOL_SSLv23
+    else:
+        s['protocol'] = ssl.PROTOCOL_TLSv1
+
     # Look for fingerprints in [hostsecurity] section. Value is a list
     # of <alg>:<fingerprint> strings.
     fingerprints = ui.configlist('hostsecurity', '%s:fingerprints' % hostname,
                                  [])
     for fingerprint in fingerprints:
         if not (fingerprint.startswith(('sha1:', 'sha256:', 'sha512:'))):
             raise error.Abort(_('invalid fingerprint for %s: %s') % (
                                 hostname, fingerprint),
@@ -210,16 +228,17 @@ def _hostsettings(ui, hostname):
         else:
             # At this point we don't have a fingerprint, aren't being
             # explicitly insecure, and can't load CA certs. Connecting
             # is insecure. We allow the connection and abort during
             # validation (once we have the fingerprint to print to the
             # user).
             s['verifymode'] = ssl.CERT_NONE
 
+    assert s['protocol'] is not None
     assert s['verifymode'] is not None
 
     return s
 
 def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None):
     """Add SSL/TLS to a socket.
 
     This is a glorified wrapper for ``ssl.wrap_socket()``. It makes sane
@@ -232,37 +251,20 @@ def wrapsocket(sock, keyfile, certfile, 
       server (and client) support SNI, this tells the server which certificate
       to use.
     """
     if not serverhostname:
         raise error.Abort(_('serverhostname argument is required'))
 
     settings = _hostsettings(ui, serverhostname)
 
-    # Despite its name, PROTOCOL_SSLv23 selects the highest protocol
-    # that both ends support, including TLS protocols. On legacy stacks,
-    # the highest it likely goes in TLS 1.0. On modern stacks, it can
-    # support TLS 1.2.
-    #
-    # The PROTOCOL_TLSv* constants select a specific TLS version
-    # only (as opposed to multiple versions). So the method for
-    # supporting multiple TLS versions is to use PROTOCOL_SSLv23 and
-    # disable protocols via SSLContext.options and OP_NO_* constants.
-    # However, SSLContext.options doesn't work unless we have the
-    # full/real SSLContext available to us.
-    #
+    # TODO use ssl.create_default_context() on modernssl.
+    sslcontext = SSLContext(settings['protocol'])
+
     # SSLv2 and SSLv3 are broken. We ban them outright.
-    if modernssl:
-        protocol = ssl.PROTOCOL_SSLv23
-    else:
-        protocol = ssl.PROTOCOL_TLSv1
-
-    # TODO use ssl.create_default_context() on modernssl.
-    sslcontext = SSLContext(protocol)
-
     # This is a no-op on old Python.
     sslcontext.options |= OP_NO_SSLv2 | OP_NO_SSLv3
 
     # This still works on our fake SSLContext.
     sslcontext.verify_mode = settings['verifymode']
 
     if certfile is not None:
         def password():


More information about the Mercurial-devel mailing list