]> granicus.if.org Git - python/commitdiff
bpo-31431: SSLContext.check_hostname auto-sets CERT_REQUIRED (#3531)
authorChristian Heimes <christian@python.org>
Fri, 15 Sep 2017 18:29:57 +0000 (20:29 +0200)
committerGitHub <noreply@github.com>
Fri, 15 Sep 2017 18:29:57 +0000 (20:29 +0200)
Signed-off-by: Christian Heimes <christian@python.org>
Doc/library/ssl.rst
Lib/test/test_ssl.py
Misc/NEWS.d/next/Library/2017-09-13-07-37-20.bpo-31431.dj994R.rst [new file with mode: 0644]
Modules/_ssl.c

index eb4d8ace3defce40418299c076ec14d017605eea..1f3e8d5f7813a9bac5e87db48fdddda95cf64b89 100644 (file)
@@ -1674,7 +1674,10 @@ to speed up repeated connections from the same clients.
    :meth:`SSLSocket.do_handshake`. The context's
    :attr:`~SSLContext.verify_mode` must be set to :data:`CERT_OPTIONAL` or
    :data:`CERT_REQUIRED`, and you must pass *server_hostname* to
-   :meth:`~SSLContext.wrap_socket` in order to match the hostname.
+   :meth:`~SSLContext.wrap_socket` in order to match the hostname.  Enabling
+   hostname checking automatically sets :attr:`~SSLContext.verify_mode` from
+   :data:`CERT_NONE` to :data:`CERT_REQUIRED`.  It cannot be set back to
+   :data:`CERT_NONE` as long as hostname checking is enabled.
 
    Example::
 
@@ -1691,6 +1694,13 @@ to speed up repeated connections from the same clients.
 
    .. versionadded:: 3.4
 
+   .. versionchanged:: 3.7
+
+      :attr:`~SSLContext.verify_mode` is now automatically changed
+      to :data:`CERT_REQUIRED`  when hostname checking is enabled and
+      :attr:`~SSLContext.verify_mode` is :data:`CERT_NONE`. Previously
+      the same operation would have failed with a :exc:`ValueError`.
+
    .. note::
 
      This features requires OpenSSL 0.9.8f or newer.
index 2978b8b3ebd7372cd50baefdacf531e850c4c345..aa2429ac9827aef42b5f22738a341974609dd270 100644 (file)
@@ -1363,24 +1363,45 @@ class ContextTests(unittest.TestCase):
     def test_check_hostname(self):
         ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)
         self.assertFalse(ctx.check_hostname)
+        self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
 
-        # Requires CERT_REQUIRED or CERT_OPTIONAL
-        with self.assertRaises(ValueError):
-            ctx.check_hostname = True
+        # Auto set CERT_REQUIRED
+        ctx.check_hostname = True
+        self.assertTrue(ctx.check_hostname)
+        self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
+        ctx.check_hostname = False
         ctx.verify_mode = ssl.CERT_REQUIRED
         self.assertFalse(ctx.check_hostname)
+        self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
+
+        # Changing verify_mode does not affect check_hostname
+        ctx.check_hostname = False
+        ctx.verify_mode = ssl.CERT_NONE
+        ctx.check_hostname = False
+        self.assertFalse(ctx.check_hostname)
+        self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
+        # Auto set
         ctx.check_hostname = True
         self.assertTrue(ctx.check_hostname)
+        self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
 
+        ctx.check_hostname = False
         ctx.verify_mode = ssl.CERT_OPTIONAL
+        ctx.check_hostname = False
+        self.assertFalse(ctx.check_hostname)
+        self.assertEqual(ctx.verify_mode, ssl.CERT_OPTIONAL)
+        # keep CERT_OPTIONAL
         ctx.check_hostname = True
         self.assertTrue(ctx.check_hostname)
+        self.assertEqual(ctx.verify_mode, ssl.CERT_OPTIONAL)
 
         # Cannot set CERT_NONE with check_hostname enabled
         with self.assertRaises(ValueError):
             ctx.verify_mode = ssl.CERT_NONE
         ctx.check_hostname = False
         self.assertFalse(ctx.check_hostname)
+        ctx.verify_mode = ssl.CERT_NONE
+        self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
 
     def test_context_client_server(self):
         # PROTOCOL_TLS_CLIENT has sane defaults
diff --git a/Misc/NEWS.d/next/Library/2017-09-13-07-37-20.bpo-31431.dj994R.rst b/Misc/NEWS.d/next/Library/2017-09-13-07-37-20.bpo-31431.dj994R.rst
new file mode 100644 (file)
index 0000000..6083fd6
--- /dev/null
@@ -0,0 +1,2 @@
+SSLContext.check_hostname now automatically sets SSLContext.verify_mode to
+ssl.CERT_REQUIRED instead of failing with a ValueError.
index 8aaaa3212c17580df8894bba8eb4285ec2612c94..73abad3dcf1c7c452ba688b39edee0ca5e4dcb46 100644 (file)
@@ -3227,10 +3227,10 @@ set_check_hostname(PySSLContext *self, PyObject *arg, void *c)
         return -1;
     if (check_hostname &&
             SSL_CTX_get_verify_mode(self->ctx) == SSL_VERIFY_NONE) {
-        PyErr_SetString(PyExc_ValueError,
-                        "check_hostname needs a SSL context with either "
-                        "CERT_OPTIONAL or CERT_REQUIRED");
-        return -1;
+        /* check_hostname = True sets verify_mode = CERT_REQUIRED */
+        if (_set_verify_mode(self->ctx, PY_SSL_CERT_REQUIRED) == -1) {
+            return -1;
+        }
     }
     self->check_hostname = check_hostname;
     return 0;