[Python-checkins] bpo-44362: ssl: improve deprecation warnings and docs (GH-26646)

tiran webhook-mailer at python.org
Fri Jun 11 03:15:58 EDT 2021


https://github.com/python/cpython/commit/e26014f1c47d26d6097ff7a0f25384bfbde714a9
commit: e26014f1c47d26d6097ff7a0f25384bfbde714a9
branch: main
author: Christian Heimes <christian at python.org>
committer: tiran <christian at python.org>
date: 2021-06-11T09:15:48+02:00
summary:

bpo-44362: ssl: improve deprecation warnings and docs (GH-26646)

Signed-off-by: Christian Heimes <christian at python.org>

files:
A Misc/NEWS.d/next/Library/2021-06-10-20-07-32.bpo-44362.oVOMfd.rst
M Doc/library/ssl.rst
M Doc/whatsnew/3.10.rst
M Lib/ssl.py
M Lib/test/test_ssl.py
M Modules/_ssl.c

diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst
index afa3d87f5767a..4902d34888ebc 100644
--- a/Doc/library/ssl.rst
+++ b/Doc/library/ssl.rst
@@ -681,19 +681,23 @@ Constants
 
    .. deprecated:: 3.10
 
+      TLS clients and servers require different default settings for secure
+      communication. The generic TLS protocol constant is deprecated in
+      favor of :data:`PROTOCOL_TLS_CLIENT` and :data:`PROTOCOL_TLS_SERVER`.
+
 .. data:: PROTOCOL_TLS_CLIENT
 
-   Auto-negotiate the highest protocol version like :data:`PROTOCOL_TLS`,
-   but only support client-side :class:`SSLSocket` connections. The protocol
-   enables :data:`CERT_REQUIRED` and :attr:`~SSLContext.check_hostname` by
-   default.
+   Auto-negotiate the highest protocol version that both the client and
+   server support, and configure the context client-side connections. The
+   protocol enables :data:`CERT_REQUIRED` and
+   :attr:`~SSLContext.check_hostname` by default.
 
    .. versionadded:: 3.6
 
 .. data:: PROTOCOL_TLS_SERVER
 
-   Auto-negotiate the highest protocol version like :data:`PROTOCOL_TLS`,
-   but only support server-side :class:`SSLSocket` connections.
+   Auto-negotiate the highest protocol version that both the client and
+   server support, and configure the context server-side connections.
 
    .. versionadded:: 3.6
 
diff --git a/Doc/whatsnew/3.10.rst b/Doc/whatsnew/3.10.rst
index df9806a211833..249eb733a88bf 100644
--- a/Doc/whatsnew/3.10.rst
+++ b/Doc/whatsnew/3.10.rst
@@ -1214,18 +1214,11 @@ The ssl module has preliminary support for OpenSSL 3.0.0 and new option
 :issue:`43789`, and :issue:`43811`.)
 
 Deprecated function and use of deprecated constants now result in
-a :exc:`DeprecationWarning`. The following features have been deprecated
-since Python 3.6, Python 3.7, or OpenSSL 1.1.0:
-:data:`~ssl.OP_NO_SSLv2`, :data:`~ssl.OP_NO_SSLv3`, :data:`~ssl.OP_NO_TLSv1`,
-:data:`~ssl.OP_NO_TLSv1_1`, :data:`~ssl.OP_NO_TLSv1_2`,
-:data:`~ssl.OP_NO_TLSv1_3`, :data:`~ssl.PROTOCOL_SSLv2`,
-:data:`~ssl.PROTOCOL_SSLv3`, :data:`~ssl.PROTOCOL_SSLv23`,
-:data:`~ssl.PROTOCOL_TLSv1`, :data:`~ssl.PROTOCOL_TLSv1_1`,
-:data:`~ssl.PROTOCOL_TLSv1_2`, :data:`~ssl.PROTOCOL_TLS`,
-:func:`~ssl.wrap_socket`, :func:`~ssl.match_hostname`,
-:func:`~ssl.RAND_pseudo_bytes`, :func:`~ssl.RAND_egd`,
-:meth:`ssl.SSLSocket.selected_npn_protocol`,
-:meth:`ssl.SSLContext.set_npn_protocols`.
+a :exc:`DeprecationWarning`. :attr:`ssl.SSLContext.options` has
+:data:`~ssl.OP_NO_SSLv2` and :data:`~ssl.OP_NO_SSLv3` set by default and
+therefore cannot warn about setting the flag again. The
+:ref:`deprecation section <whatsnew310-deprecated>` has a list of deprecated
+features.
 (Contributed by Christian Heimes in :issue:`43880`.)
 
 The ssl module now has more secure default settings. Ciphers without forward
@@ -1441,6 +1434,8 @@ Optimizations
   readers or writers, just like its equivalent classes in :mod:`gzip` and
   :mod:`lzma` have always been.  (Contributed by Inada Naoki in :issue:`43785`).
 
+.. _whatsnew310-deprecated:
+
 Deprecated
 ==========
 
@@ -1609,6 +1604,30 @@ Deprecated
 * ``cgi.log()`` is deprecated and slated for removal in Python 3.12.
   (Contributed by Inada Naoki in :issue:`41139`.)
 
+* The following :mod:`ssl` features have been deprecated since Python 3.6,
+  Python 3.7, or OpenSSL 1.1.0 and will be removed in 3.11:
+
+  * :data:`~ssl.OP_NO_SSLv2`, :data:`~ssl.OP_NO_SSLv3`, :data:`~ssl.OP_NO_TLSv1`,
+    :data:`~ssl.OP_NO_TLSv1_1`, :data:`~ssl.OP_NO_TLSv1_2`, and
+    :data:`~ssl.OP_NO_TLSv1_3` are replaced by
+    :attr:`sslSSLContext.minimum_version` and
+    :attr:`sslSSLContext.maximum_version`.
+
+  * :data:`~ssl.PROTOCOL_SSLv2`, :data:`~ssl.PROTOCOL_SSLv3`,
+    :data:`~ssl.PROTOCOL_SSLv23`, :data:`~ssl.PROTOCOL_TLSv1`,
+    :data:`~ssl.PROTOCOL_TLSv1_1`, :data:`~ssl.PROTOCOL_TLSv1_2`, and
+    :data:`~ssl.PROTOCOL_TLS` are deprecated in favor of
+    :data:`~ssl.PROTOCOL_TLS_CLIENT` and :data:`~ssl.PROTOCOL_TLS_SERVER`
+
+  * :func:`~ssl.wrap_socket` is replaced by :meth:`ssl.SSLContext.wrap_socket`
+
+  * :func:`~ssl.match_hostname`
+
+  * :func:`~ssl.RAND_pseudo_bytes`, :func:`~ssl.RAND_egd`
+
+  * NPN features like :meth:`ssl.SSLSocket.selected_npn_protocol` and
+    :meth:`ssl.SSLContext.set_npn_protocols` are replaced by ALPN.
+
 .. _whatsnew310-removed:
 
 Removed
diff --git a/Lib/ssl.py b/Lib/ssl.py
index aeb2958da24be..e667e9658d511 100644
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -94,7 +94,7 @@
 import os
 from collections import namedtuple
 from enum import Enum as _Enum, IntEnum as _IntEnum, IntFlag as _IntFlag
-from enum import _simple_enum, _test_simple_enum
+from enum import _simple_enum
 
 import _ssl             # if we can't import it, let the error propagate
 
@@ -387,7 +387,7 @@ def match_hostname(cert, hostname):
     returns nothing.
     """
     warnings.warn(
-        "ssl module: match_hostname() is deprecated",
+        "ssl.match_hostname() is deprecated",
         category=DeprecationWarning,
         stacklevel=2
     )
@@ -492,8 +492,7 @@ class SSLContext(_SSLContext):
     def __new__(cls, protocol=None, *args, **kwargs):
         if protocol is None:
             warnings.warn(
-                "ssl module: "
-                "SSLContext() without protocol argument is deprecated.",
+                "ssl.SSLContext() without protocol argument is deprecated.",
                 category=DeprecationWarning,
                 stacklevel=2
             )
@@ -536,7 +535,11 @@ def wrap_bio(self, incoming, outgoing, server_side=False,
         )
 
     def set_npn_protocols(self, npn_protocols):
-        warnings.warn("NPN is deprecated, use ALPN instead", stacklevel=2)
+        warnings.warn(
+            "ssl NPN is deprecated, use ALPN instead",
+            DeprecationWarning,
+            stacklevel=2
+        )
         protos = bytearray()
         for protocol in npn_protocols:
             b = bytes(protocol, 'ascii')
@@ -940,7 +943,9 @@ def selected_npn_protocol(self):
         if a next protocol was not negotiated or if NPN is not supported by one
         of the peers."""
         warnings.warn(
-            "ssl module: NPN is deprecated, use ALPN instead", stacklevel=2
+            "ssl NPN is deprecated, use ALPN instead",
+            DeprecationWarning,
+            stacklevel=2
         )
 
     def selected_alpn_protocol(self):
@@ -1157,7 +1162,9 @@ def getpeercert(self, binary_form=False):
     def selected_npn_protocol(self):
         self._checkClosed()
         warnings.warn(
-            "ssl module: NPN is deprecated, use ALPN instead", stacklevel=2
+            "ssl NPN is deprecated, use ALPN instead",
+            DeprecationWarning,
+            stacklevel=2
         )
         return None
 
@@ -1419,7 +1426,7 @@ def wrap_socket(sock, keyfile=None, certfile=None,
                 suppress_ragged_eofs=True,
                 ciphers=None):
     warnings.warn(
-        "ssl module: wrap_socket is deprecated, use SSLContext.wrap_socket()",
+        "ssl.wrap_socket() is deprecated, use SSLContext.wrap_socket()",
         category=DeprecationWarning,
         stacklevel=2
     )
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index fdf5f19d8d4c5..31bc199e930a6 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -1750,7 +1750,7 @@ class MySSLObject(ssl.SSLObject):
 
         with ctx.wrap_socket(socket.socket(), server_side=True) as sock:
             self.assertIsInstance(sock, MySSLSocket)
-        obj = ctx.wrap_bio(ssl.MemoryBIO(), ssl.MemoryBIO())
+        obj = ctx.wrap_bio(ssl.MemoryBIO(), ssl.MemoryBIO(), server_side=True)
         self.assertIsInstance(obj, MySSLObject)
 
     def test_num_tickest(self):
@@ -2884,24 +2884,29 @@ def test_echo(self):
                                    server_context=client_context,
                                    chatty=True, connectionchatty=True,
                                    sni_name=hostname)
-            self.assertIn('called a function you should not call',
-                          str(e.exception))
+            self.assertIn(
+                'Cannot create a client socket with a PROTOCOL_TLS_SERVER context',
+                str(e.exception)
+            )
 
         with self.subTest(client=ssl.PROTOCOL_TLS_SERVER, server=ssl.PROTOCOL_TLS_SERVER):
             with self.assertRaises(ssl.SSLError) as e:
                 server_params_test(client_context=server_context,
                                    server_context=server_context,
                                    chatty=True, connectionchatty=True)
-            self.assertIn('called a function you should not call',
-                          str(e.exception))
+            self.assertIn(
+                'Cannot create a client socket with a PROTOCOL_TLS_SERVER context',
+                str(e.exception)
+            )
 
         with self.subTest(client=ssl.PROTOCOL_TLS_CLIENT, server=ssl.PROTOCOL_TLS_CLIENT):
             with self.assertRaises(ssl.SSLError) as e:
                 server_params_test(client_context=server_context,
                                    server_context=client_context,
                                    chatty=True, connectionchatty=True)
-            self.assertIn('called a function you should not call',
-                          str(e.exception))
+            self.assertIn(
+                'Cannot create a client socket with a PROTOCOL_TLS_SERVER context',
+                str(e.exception))
 
     def test_getpeercert(self):
         if support.verbose:
diff --git a/Misc/NEWS.d/next/Library/2021-06-10-20-07-32.bpo-44362.oVOMfd.rst b/Misc/NEWS.d/next/Library/2021-06-10-20-07-32.bpo-44362.oVOMfd.rst
new file mode 100644
index 0000000000000..0e6aef3c90e6f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2021-06-10-20-07-32.bpo-44362.oVOMfd.rst
@@ -0,0 +1,2 @@
+Improve :mod:`ssl` module's deprecation messages, error reporting, and
+documentation for deprecations.
diff --git a/Modules/_ssl.c b/Modules/_ssl.c
index 73544bb12d1d8..8daf04dd08bbb 100644
--- a/Modules/_ssl.c
+++ b/Modules/_ssl.c
@@ -697,10 +697,9 @@ _setSSLError (_sslmodulestate *state, const char *errstr, int errcode, const cha
 }
 
 static int
-_ssl_deprecated(const char* name, int stacklevel) {
-    return PyErr_WarnFormat(
-        PyExc_DeprecationWarning, stacklevel,
-        "ssl module: %s is deprecated", name
+_ssl_deprecated(const char* msg, int stacklevel) {
+    return PyErr_WarnEx(
+        PyExc_DeprecationWarning, msg, stacklevel
     );
 }
 
@@ -788,6 +787,21 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
     SSL_CTX *ctx = sslctx->ctx;
     _PySSLError err = { 0 };
 
+    if ((socket_type == PY_SSL_SERVER) &&
+        (sslctx->protocol == PY_SSL_VERSION_TLS_CLIENT)) {
+        _setSSLError(get_state_ctx(sslctx),
+                     "Cannot create a server socket with a "
+                     "PROTOCOL_TLS_CLIENT context", 0, __FILE__, __LINE__);
+        return NULL;
+    }
+    if ((socket_type == PY_SSL_CLIENT) &&
+        (sslctx->protocol == PY_SSL_VERSION_TLS_SERVER)) {
+        _setSSLError(get_state_ctx(sslctx),
+                     "Cannot create a client socket with a "
+                     "PROTOCOL_TLS_SERVER context", 0, __FILE__, __LINE__);
+        return NULL;
+    }
+
     self = PyObject_GC_New(PySSLSocket,
                            get_state_ctx(sslctx)->PySSLSocket_Type);
     if (self == NULL)
@@ -2980,7 +2994,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
     switch(proto_version) {
 #if defined(SSL3_VERSION) && !defined(OPENSSL_NO_SSL3)
     case PY_SSL_VERSION_SSL3:
-        PY_SSL_DEPRECATED("PROTOCOL_SSLv3", 2, NULL);
+        PY_SSL_DEPRECATED("ssl.PROTOCOL_SSLv3 is deprecated", 2, NULL);
         method = SSLv3_method();
         break;
 #endif
@@ -2988,7 +3002,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
         !defined(OPENSSL_NO_TLS1) && \
         !defined(OPENSSL_NO_TLS1_METHOD))
     case PY_SSL_VERSION_TLS1:
-        PY_SSL_DEPRECATED("PROTOCOL_TLSv1", 2, NULL);
+        PY_SSL_DEPRECATED("ssl.PROTOCOL_TLSv1 is deprecated", 2, NULL);
         method = TLSv1_method();
         break;
 #endif
@@ -2996,7 +3010,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
         !defined(OPENSSL_NO_TLS1_1) && \
         !defined(OPENSSL_NO_TLS1_1_METHOD))
     case PY_SSL_VERSION_TLS1_1:
-        PY_SSL_DEPRECATED("PROTOCOL_TLSv1_1", 2, NULL);
+        PY_SSL_DEPRECATED("ssl.PROTOCOL_TLSv1_1 is deprecated", 2, NULL);
         method = TLSv1_1_method();
         break;
 #endif
@@ -3004,12 +3018,12 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
         !defined(OPENSSL_NO_TLS1_2) && \
         !defined(OPENSSL_NO_TLS1_2_METHOD))
     case PY_SSL_VERSION_TLS1_2:
-        PY_SSL_DEPRECATED("PROTOCOL_TLSv1_2", 2, NULL);
+        PY_SSL_DEPRECATED("ssl.PROTOCOL_TLSv1_2 is deprecated", 2, NULL);
         method = TLSv1_2_method();
         break;
 #endif
     case PY_SSL_VERSION_TLS:
-        PY_SSL_DEPRECATED("PROTOCOL_TLS", 2, NULL);
+        PY_SSL_DEPRECATED("ssl.PROTOCOL_TLS is deprecated", 2, NULL);
         method = TLS_method();
         break;
     case PY_SSL_VERSION_TLS_CLIENT:
@@ -3433,13 +3447,13 @@ set_min_max_proto_version(PySSLContext *self, PyObject *arg, int what)
     /* check for deprecations and supported values */
     switch(v) {
         case PY_PROTO_SSLv3:
-            PY_SSL_DEPRECATED("TLSVersion.SSLv3", 2, -1);
+            PY_SSL_DEPRECATED("ssl.TLSVersion.SSLv3 is deprecated", 2, -1);
             break;
         case PY_PROTO_TLSv1:
-            PY_SSL_DEPRECATED("TLSVersion.TLSv1", 2, -1);
+            PY_SSL_DEPRECATED("ssl.TLSVersion.TLSv1 is deprecated", 2, -1);
             break;
         case PY_PROTO_TLSv1_1:
-            PY_SSL_DEPRECATED("TLSVersion.TLSv1_1", 2, -1);
+            PY_SSL_DEPRECATED("ssl.TLSVersion.TLSv1_1 is deprecated", 2, -1);
             break;
         case PY_PROTO_MINIMUM_SUPPORTED:
         case PY_PROTO_MAXIMUM_SUPPORTED:
@@ -3583,7 +3597,7 @@ set_options(PySSLContext *self, PyObject *arg, void *c)
     set = ~opts & new_opts;
 
     if ((set & opt_no) != 0) {
-        if (_ssl_deprecated("Setting OP_NO_SSL* or SSL_NO_TLS* options is "
+        if (_ssl_deprecated("ssl.OP_NO_SSL*/ssl.SSL_NO_TLS* options are "
                             "deprecated", 2) < 0) {
             return -1;
         }
@@ -5146,7 +5160,7 @@ static PyObject *
 _ssl_RAND_pseudo_bytes_impl(PyObject *module, int n)
 /*[clinic end generated code: output=b1509e937000e52d input=58312bd53f9bbdd0]*/
 {
-    PY_SSL_DEPRECATED("RAND_pseudo_bytes", 1, NULL);
+    PY_SSL_DEPRECATED("ssl.RAND_pseudo_bytes() is deprecated", 1, NULL);
     return PySSL_RAND(module, n, 1);
 }
 



More information about the Python-checkins mailing list