bpo-31431: SSLContext.check_hostname auto-sets CERT_REQUIRED (#3531)

Signed-off-by: Christian Heimes <christian@python.org>
This commit is contained in:
Christian Heimes 2017-09-15 20:29:57 +02:00 committed by GitHub
parent a170fa162d
commit e82c034496
4 changed files with 44 additions and 11 deletions

View 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.

View 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):
# 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)
ctx.check_hostname = True
self.assertTrue(ctx.check_hostname)
self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
ctx.verify_mode = ssl.CERT_OPTIONAL
# 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

View File

@ -0,0 +1,2 @@
SSLContext.check_hostname now automatically sets SSLContext.verify_mode to
ssl.CERT_REQUIRED instead of failing with a ValueError.

View File

@ -3227,11 +3227,11 @@ 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");
/* 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;
}