_sslutil__avoid_deprecation_warnings_from_python_3_10_s_ssl_module
authorDebian Python Team <team+python@tracker.debian.org>
Tue, 7 Jun 2022 18:53:46 +0000 (19:53 +0100)
committerJulien Cristau <jcristau@debian.org>
Tue, 7 Jun 2022 18:53:46 +0000 (19:53 +0100)
# HG changeset patch
# User Julien Cristau <jcristau@debian.org>
# Date 1649506532 -7200
#      Sat Apr 09 14:15:32 2022 +0200
# Node ID 410916fc5935c7855c2cf4876d8311d34aabc29a
# Parent  96cc0ab5e1e68e4e163e8933842f908dcf42897a
sslutil: avoid deprecation warnings from python 3.10's ssl module

Use ssl.PROTOCOL_TLS_{CLIENT,SERVER} and
SSLContext.{min,max}imum_version when supported (3.7+).

And, catch deprecation warnings when the user asks for deprecated TLS
versions (1.0 and 1.1).

Differential Revision: https://phab.mercurial-scm.org/D12488

Gbp-Pq: Topic py310
Gbp-Pq: Name 3_sslutil__avoid_deprecation_warnings_from_python_3_10_s_ssl_module.patch

mercurial/sslutil.py

index 2b1e0d715a4c73c459c0ea07dfecd2ee280bdc14..38b670ecfbb801f608a9ccc1eff6fc7f3106368a 100644 (file)
@@ -13,6 +13,7 @@ import hashlib
 import os
 import re
 import ssl
+import warnings
 
 from .i18n import _
 from .pycompat import getattr
@@ -309,14 +310,38 @@ def wrapsocket(sock, keyfile, certfile, ui, serverhostname=None):
     # bundle with a specific CA cert removed. If the system/default CA bundle
     # is loaded and contains that removed CA, you've just undone the user's
     # choice.
-    #
-    # Despite its name, PROTOCOL_SSLv23 selects the highest protocol that both
-    # ends support, including TLS protocols. commonssloptions() restricts the
-    # set of allowed protocols.
-    sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
-    sslcontext.options |= commonssloptions(settings[b'minimumprotocol'])
+
+    if util.safehasattr(ssl, 'PROTOCOL_TLS_CLIENT'):
+        # python 3.7+
+        sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
+        minimumprotocol = settings[b'minimumprotocol']
+        if minimumprotocol == b'tls1.0':
+            with warnings.catch_warnings():
+                warnings.filterwarnings('ignore', 'ssl.TLSVersion.TLSv1 is deprecated', DeprecationWarning)
+                sslcontext.minimum_version = ssl.TLSVersion.TLSv1
+        elif minimumprotocol == b'tls1.1':
+            with warnings.catch_warnings():
+                warnings.filterwarnings('ignore', 'ssl.TLSVersion.TLSv1_1 is deprecated', DeprecationWarning)
+                sslcontext.minimum_version = ssl.TLSVersion.TLSv1_1
+        elif minimumprotocol == b'tls1.2':
+            sslcontext.minimum_version = ssl.TLSVersion.TLSv1_2
+        else:
+            raise error.Abort(_(b'this should not happen'))
+        # Prevent CRIME.
+        # There is no guarantee this attribute is defined on the module.
+        sslcontext.options |= getattr(ssl, 'OP_NO_COMPRESSION', 0)
+    else:
+        # Despite its name, PROTOCOL_SSLv23 selects the highest protocol that both
+        # ends support, including TLS protocols. commonssloptions() restricts the
+        # set of allowed protocols.
+        sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
+        sslcontext.options |= commonssloptions(settings[b'minimumprotocol'])
+
+    # We check the hostname ourselves in _verifycert
+    sslcontext.check_hostname = False
     sslcontext.verify_mode = settings[b'verifymode']
 
+
     if settings[b'ciphers']:
         try:
             sslcontext.set_ciphers(pycompat.sysstr(settings[b'ciphers']))
@@ -510,37 +535,69 @@ def wrapserversocket(
                 _(b'referenced certificate file (%s) does not exist') % f
             )
 
-    # Despite its name, PROTOCOL_SSLv23 selects the highest protocol that both
-    # ends support, including TLS protocols. commonssloptions() restricts the
-    # set of allowed protocols.
-    protocol = ssl.PROTOCOL_SSLv23
-    options = commonssloptions(b'tls1.0')
-
-    # This config option is intended for use in tests only. It is a giant
-    # footgun to kill security. Don't define it.
-    exactprotocol = ui.config(b'devel', b'serverexactprotocol')
-    if exactprotocol == b'tls1.0':
-        if b'tls1.0' not in supportedprotocols:
-            raise error.Abort(_(b'TLS 1.0 not supported by this Python'))
-        protocol = ssl.PROTOCOL_TLSv1
-    elif exactprotocol == b'tls1.1':
-        if b'tls1.1' not in supportedprotocols:
-            raise error.Abort(_(b'TLS 1.1 not supported by this Python'))
-        protocol = ssl.PROTOCOL_TLSv1_1
-    elif exactprotocol == b'tls1.2':
-        if b'tls1.2' not in supportedprotocols:
-            raise error.Abort(_(b'TLS 1.2 not supported by this Python'))
-        protocol = ssl.PROTOCOL_TLSv1_2
-    elif exactprotocol:
-        raise error.Abort(
-            _(b'invalid value for serverexactprotocol: %s') % exactprotocol
-        )
+    if util.safehasattr(ssl, 'PROTOCOL_TLS_SERVER'):
+        # python 3.7+
+        sslcontext = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
+        sslcontext.options |= getattr(ssl, 'OP_NO_COMPRESSION', 0)
+
+        # This config option is intended for use in tests only. It is a giant
+        # footgun to kill security. Don't define it.
+        exactprotocol = ui.config(b'devel', b'serverexactprotocol')
+        if exactprotocol == b'tls1.0':
+            if b'tls1.0' not in supportedprotocols:
+                raise error.Abort(_(b'TLS 1.0 not supported by this Python'))
+            with warnings.catch_warnings():
+                warnings.filterwarnings('ignore', 'ssl.TLSVersion.TLSv1 is deprecated', DeprecationWarning)
+                sslcontext.minimum_version = ssl.TLSVersion.TLSv1
+                sslcontext.maximum_version = ssl.TLSVersion.TLSv1
+        elif exactprotocol == b'tls1.1':
+            if b'tls1.1' not in supportedprotocols:
+                raise error.Abort(_(b'TLS 1.1 not supported by this Python'))
+            with warnings.catch_warnings():
+                warnings.filterwarnings('ignore', 'ssl.TLSVersion.TLSv1_1 is deprecated', DeprecationWarning)
+                sslcontext.minimum_version = ssl.TLSVersion.TLSv1_1
+                sslcontext.maximum_version = ssl.TLSVersion.TLSv1_1
+        elif exactprotocol == b'tls1.2':
+            if b'tls1.2' not in supportedprotocols:
+                raise error.Abort(_(b'TLS 1.2 not supported by this Python'))
+            sslcontext.minimum_version = ssl.TLSVersion.TLSv1_2
+            sslcontext.maximum_version = ssl.TLSVersion.TLSv1_2
+        elif exactprotocol:
+            raise error.Abort(
+                _(b'invalid value for serverexactprotocol: %s') % exactprotocol
+            )
+    else:
+        # Despite its name, PROTOCOL_SSLv23 selects the highest protocol that both
+        # ends support, including TLS protocols. commonssloptions() restricts the
+        # set of allowed protocols.
+        protocol = ssl.PROTOCOL_SSLv23
+        options = commonssloptions(b'tls1.0')
+
+        # This config option is intended for use in tests only. It is a giant
+        # footgun to kill security. Don't define it.
+        exactprotocol = ui.config(b'devel', b'serverexactprotocol')
+        if exactprotocol == b'tls1.0':
+            if b'tls1.0' not in supportedprotocols:
+                raise error.Abort(_(b'TLS 1.0 not supported by this Python'))
+            protocol = ssl.PROTOCOL_TLSv1
+        elif exactprotocol == b'tls1.1':
+            if b'tls1.1' not in supportedprotocols:
+                raise error.Abort(_(b'TLS 1.1 not supported by this Python'))
+            protocol = ssl.PROTOCOL_TLSv1_1
+        elif exactprotocol == b'tls1.2':
+            if b'tls1.2' not in supportedprotocols:
+                raise error.Abort(_(b'TLS 1.2 not supported by this Python'))
+            protocol = ssl.PROTOCOL_TLSv1_2
+        elif exactprotocol:
+            raise error.Abort(
+                _(b'invalid value for serverexactprotocol: %s') % exactprotocol
+            )
 
-    # We /could/ use create_default_context() here since it doesn't load
-    # CAs when configured for client auth. However, it is hard-coded to
-    # use ssl.PROTOCOL_SSLv23 which may not be appropriate here.
-    sslcontext = ssl.SSLContext(protocol)
-    sslcontext.options |= options
+        # We /could/ use create_default_context() here since it doesn't load
+        # CAs when configured for client auth. However, it is hard-coded to
+        # use ssl.PROTOCOL_SSLv23 which may not be appropriate here.
+        sslcontext = ssl.SSLContext(protocol)
+        sslcontext.options |= options
 
     # Improve forward secrecy.
     sslcontext.options |= getattr(ssl, 'OP_SINGLE_DH_USE', 0)