mirror of
https://github.com/python/cpython.git
synced 2025-10-18 04:38:07 +00:00
bpo-31431: SSLContext.check_hostname auto-sets CERT_REQUIRED (#3531)
Signed-off-by: Christian Heimes <christian@python.org>
This commit is contained in:
parent
a170fa162d
commit
e82c034496
4 changed files with 44 additions and 11 deletions
|
@ -1674,7 +1674,10 @@ to speed up repeated connections from the same clients.
|
||||||
:meth:`SSLSocket.do_handshake`. The context's
|
:meth:`SSLSocket.do_handshake`. The context's
|
||||||
:attr:`~SSLContext.verify_mode` must be set to :data:`CERT_OPTIONAL` or
|
:attr:`~SSLContext.verify_mode` must be set to :data:`CERT_OPTIONAL` or
|
||||||
:data:`CERT_REQUIRED`, and you must pass *server_hostname* to
|
: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::
|
Example::
|
||||||
|
|
||||||
|
@ -1691,6 +1694,13 @@ to speed up repeated connections from the same clients.
|
||||||
|
|
||||||
.. versionadded:: 3.4
|
.. 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::
|
.. note::
|
||||||
|
|
||||||
This features requires OpenSSL 0.9.8f or newer.
|
This features requires OpenSSL 0.9.8f or newer.
|
||||||
|
|
|
@ -1363,24 +1363,45 @@ class ContextTests(unittest.TestCase):
|
||||||
def test_check_hostname(self):
|
def test_check_hostname(self):
|
||||||
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
||||||
self.assertFalse(ctx.check_hostname)
|
self.assertFalse(ctx.check_hostname)
|
||||||
|
self.assertEqual(ctx.verify_mode, ssl.CERT_NONE)
|
||||||
|
|
||||||
# Requires CERT_REQUIRED or CERT_OPTIONAL
|
# Auto set CERT_REQUIRED
|
||||||
with self.assertRaises(ValueError):
|
ctx.check_hostname = True
|
||||||
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
|
ctx.verify_mode = ssl.CERT_REQUIRED
|
||||||
self.assertFalse(ctx.check_hostname)
|
self.assertFalse(ctx.check_hostname)
|
||||||
ctx.check_hostname = True
|
self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
|
||||||
self.assertTrue(ctx.check_hostname)
|
|
||||||
|
|
||||||
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
|
ctx.check_hostname = True
|
||||||
self.assertTrue(ctx.check_hostname)
|
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
|
# Cannot set CERT_NONE with check_hostname enabled
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
ctx.verify_mode = ssl.CERT_NONE
|
ctx.verify_mode = ssl.CERT_NONE
|
||||||
ctx.check_hostname = False
|
ctx.check_hostname = False
|
||||||
self.assertFalse(ctx.check_hostname)
|
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):
|
def test_context_client_server(self):
|
||||||
# PROTOCOL_TLS_CLIENT has sane defaults
|
# PROTOCOL_TLS_CLIENT has sane defaults
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
SSLContext.check_hostname now automatically sets SSLContext.verify_mode to
|
||||||
|
ssl.CERT_REQUIRED instead of failing with a ValueError.
|
|
@ -3227,10 +3227,10 @@ set_check_hostname(PySSLContext *self, PyObject *arg, void *c)
|
||||||
return -1;
|
return -1;
|
||||||
if (check_hostname &&
|
if (check_hostname &&
|
||||||
SSL_CTX_get_verify_mode(self->ctx) == SSL_VERIFY_NONE) {
|
SSL_CTX_get_verify_mode(self->ctx) == SSL_VERIFY_NONE) {
|
||||||
PyErr_SetString(PyExc_ValueError,
|
/* check_hostname = True sets verify_mode = CERT_REQUIRED */
|
||||||
"check_hostname needs a SSL context with either "
|
if (_set_verify_mode(self->ctx, PY_SSL_CERT_REQUIRED) == -1) {
|
||||||
"CERT_OPTIONAL or CERT_REQUIRED");
|
return -1;
|
||||||
return -1;
|
}
|
||||||
}
|
}
|
||||||
self->check_hostname = check_hostname;
|
self->check_hostname = check_hostname;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue