https://github.com/python/cpython/commit/427dfa6bb436fe267f74aac55a8ecd3d861ab107
commit: 427dfa6bb436fe267f74aac55a8ecd3d861ab107
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-13T10:06:19Z
summary:

[3.13] gh-114905: Test that ssl._create_stdlib_context() rejects check_hostname 
with CERT_NONE (GH-155509) (GH-155670)

With PROTOCOL_TLS_CLIENT, which became the default protocol in 3.10, this is
an error.  With an explicitly specified legacy protocol it used to succeed,
silently raising verify_mode to CERT_REQUIRED and ignoring the requested
CERT_NONE.

No caller of ssl._create_stdlib_context() in the standard library passes
check_hostname, so no public API reaches it.
(cherry picked from commit 726e48565670e56affd3e6d461871ec20ee12694)

Co-authored-by: Serhiy Storchaka <[email protected]>

files:
M Lib/ssl.py
M Lib/test/test_ssl.py

diff --git a/Lib/ssl.py b/Lib/ssl.py
index 7508e4f2a4bd70..cc5f7ee7f453f1 100644
--- a/Lib/ssl.py
+++ b/Lib/ssl.py
@@ -755,6 +755,8 @@ def _create_unverified_context(protocol=None, *, 
cert_reqs=CERT_NONE,
         raise ValueError(purpose)
 
     context = SSLContext(protocol)
+    # Setting verify_mode to CERT_NONE fails while check_hostname is
+    # enabled, so assign check_hostname first (gh-114905).
     context.check_hostname = check_hostname
     if cert_reqs is not None:
         context.verify_mode = cert_reqs
diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index d57664286cb695..db67090930b013 100644
--- a/Lib/test/test_ssl.py
+++ b/Lib/test/test_ssl.py
@@ -1715,6 +1715,35 @@ def test__create_stdlib_context(self):
         self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
         self._assert_context_options(ctx)
 
+    def test__create_stdlib_context_check_hostname(self):
+        # gh-114905: check_hostname cannot be combined with CERT_NONE,
+        # the default for cert_reqs.
+        msg = "Cannot set verify_mode to CERT_NONE when check_hostname"
+        with self.assertRaisesRegex(ValueError, msg):
+            ssl._create_stdlib_context(check_hostname=True)
+        with self.assertRaisesRegex(ValueError, msg):
+            ssl._create_stdlib_context(cert_reqs=ssl.CERT_NONE,
+                                       check_hostname=True)
+
+        # Accepted before 3.10 with a legacy protocol.
+        if has_tls_protocol('PROTOCOL_TLSv1_2'):
+            with warnings_helper.check_warnings():
+                with self.assertRaisesRegex(ValueError, msg):
+                    ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1_2,
+                                               cert_reqs=ssl.CERT_NONE,
+                                               check_hostname=True)
+
+        # cert_reqs=None leaves PROTOCOL_TLS_CLIENT's CERT_REQUIRED.
+        ctx = ssl._create_stdlib_context(cert_reqs=None, check_hostname=True)
+        self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
+        self.assertTrue(ctx.check_hostname)
+
+        # CERT_REQUIRED is covered by test__create_stdlib_context().
+        ctx = ssl._create_stdlib_context(cert_reqs=ssl.CERT_OPTIONAL,
+                                         check_hostname=True)
+        self.assertEqual(ctx.verify_mode, ssl.CERT_OPTIONAL)
+        self.assertTrue(ctx.check_hostname)
+
     def test_check_hostname(self):
         with warnings_helper.check_warnings():
             ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to