Skip to content

Commit 845c149

Browse files
committed
Replace SSLContext.host_flags
Host flags are now in internal API. Public API is a new attribute hostname_checks_common_name. Signed-off-by: Christian Heimes <[email protected]>
1 parent 1e8b4ab commit 845c149

File tree

5 files changed

+69
-37
lines changed

5 files changed

+69
-37
lines changed

Doc/library/ssl.rst

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -593,26 +593,6 @@ Constants
593593
be passed, either to :meth:`SSLContext.load_verify_locations` or as a
594594
value of the ``ca_certs`` parameter to :func:`wrap_socket`.
595595

596-
.. class:: HostFlags
597-
598-
:class:`enum.IntFlag` collection of all hostname verification flags for
599-
:attr:`SSLContext.host_flags`
600-
601-
.. versionadded:: 3.7
602-
603-
.. attribute:: HostFlags.HOSTFLAG_NO_PARTIAL_WILDCARDS
604-
605-
Dont' support wildcard certificate with partial matches, e.g.
606-
``www*.example.org``.
607-
608-
.. attribute:: HostFlags.HOSTFLAG_NEVER_CHECK_SUBJECT
609-
610-
Ignore subject CN even if the certificate has no subject alternative
611-
name extension.
612-
613-
.. note:: The flag is not available when the ssl module is compiled
614-
with OpenSSL 1.0.2 or LibreSSL.
615-
616596
.. class:: VerifyMode
617597

618598
:class:`enum.IntEnum` collection of CERT_* constants.
@@ -877,6 +857,14 @@ Constants
877857

878858
.. versionadded:: 3.5
879859

860+
.. data:: HAS_NEVER_CHECK_COMMON_NAME
861+
862+
Whether the OpenSSL library has built-in support not checking subject
863+
common name and :attr:`SSLContext.hostname_checks_common_name` is
864+
writeable.
865+
866+
.. versionadded:: 3.7
867+
880868
.. data:: HAS_ECDH
881869

882870
Whether the OpenSSL library has built-in support for Elliptic Curve-based
@@ -1763,13 +1751,17 @@ to speed up repeated connections from the same clients.
17631751
The protocol version chosen when constructing the context. This attribute
17641752
is read-only.
17651753

1766-
.. attribute:: SSLContext.host_flags
1754+
.. attribute:: SSLContext.hostname_checks_common_name
17671755

1768-
The flags for validating host names. By default
1769-
:data:`HostFlags.HOSTFLAG_NO_PARTIAL_WILDCARDS` is set.
1756+
Whether :attr:`~SSLContext.check_hostname` falls back to verify the cert's
1757+
subject common name in the absence of a subject alternative name
1758+
extension (default: true).
17701759

17711760
.. versionadded:: 3.7
17721761

1762+
.. note::
1763+
Only writeable with OpenSSL 1.1.0 or higher.
1764+
17731765
.. attribute:: SSLContext.verify_flags
17741766

17751767
The flags for certificate verification operations. You can set flags like

Lib/ssl.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,6 @@
148148
lambda name: name.startswith('CERT_'),
149149
source=_ssl)
150150

151-
_IntFlag._convert(
152-
'HostFlags', __name__,
153-
lambda name: name.startswith('HOSTFLAG_'),
154-
source=_ssl)
155-
156151
PROTOCOL_SSLv23 = _SSLMethod.PROTOCOL_SSLv23 = _SSLMethod.PROTOCOL_TLS
157152
_PROTOCOL_NAMES = {value: name for name, value in _SSLMethod.__members__.items()}
158153

@@ -176,6 +171,8 @@
176171
else:
177172
CHANNEL_BINDING_TYPES = []
178173

174+
HAS_NEVER_CHECK_COMMON_NAME = hasattr(_ssl, 'HOSTFLAG_NEVER_CHECK_SUBJECT')
175+
179176

180177
# Disable weak or insecure ciphers by default
181178
# (OpenSSL's default setting is 'DEFAULT:!aNULL:!eNULL')
@@ -475,13 +472,22 @@ def options(self):
475472
def options(self, value):
476473
super(SSLContext, SSLContext).options.__set__(self, value)
477474

478-
@property
479-
def host_flags(self):
480-
return HostFlags(super().host_flags)
475+
if hasattr(_ssl, 'HOSTFLAG_NEVER_CHECK_SUBJECT'):
476+
@property
477+
def hostname_checks_common_name(self):
478+
ncs = self._host_flags & _ssl.HOSTFLAG_NEVER_CHECK_SUBJECT
479+
return ncs != _ssl.HOSTFLAG_NEVER_CHECK_SUBJECT
481480

482-
@host_flags.setter
483-
def host_flags(self, value):
484-
super(SSLContext, SSLContext).host_flags.__set__(self, value)
481+
@hostname_checks_common_name.setter
482+
def hostname_checks_common_name(self, value):
483+
if value:
484+
self._host_flags &= ~_ssl.HOSTFLAG_NEVER_CHECK_SUBJECT
485+
else:
486+
self._host_flags |= _ssl.HOSTFLAG_NEVER_CHECK_SUBJECT
487+
else:
488+
@property
489+
def hostname_checks_common_name(self):
490+
return True
485491

486492
@property
487493
def verify_flags(self):

Lib/test/test_ssl.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,19 @@ def test_verify_mode_protocol(self):
988988
self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
989989
self.assertTrue(ctx.check_hostname)
990990

991+
def test_hostname_checks_common_name(self):
992+
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
993+
self.assertTrue(ctx.hostname_checks_common_name)
994+
if ssl.HAS_NEVER_CHECK_COMMON_NAME:
995+
ctx.hostname_checks_common_name = True
996+
self.assertTrue(ctx.hostname_checks_common_name)
997+
ctx.hostname_checks_common_name = False
998+
self.assertFalse(ctx.hostname_checks_common_name)
999+
ctx.hostname_checks_common_name = True
1000+
self.assertTrue(ctx.hostname_checks_common_name)
1001+
else:
1002+
with self.assertRaises(AttributeError):
1003+
ctx.hostname_checks_common_name = True
9911004

9921005
@unittest.skipUnless(have_verify_flags(),
9931006
"verify_flags need OpenSSL > 0.9.8")
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
1-
Let OpenSSL verify hostname and IP addressw
1+
The ssl module now uses OpenSSL's X509_VERIFY_PARAM_set1_host() and
2+
X509_VERIFY_PARAM_set1_ip() API to verify hostname and IP addresses. Subject
3+
common name fallback can be disabled with
4+
SSLContext.hostname_checks_common_name.

Modules/_ssl.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4185,8 +4185,8 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form)
41854185
static PyGetSetDef context_getsetlist[] = {
41864186
{"check_hostname", (getter) get_check_hostname,
41874187
(setter) set_check_hostname, NULL},
4188-
{"host_flags", (getter) get_host_flags,
4189-
(setter) set_host_flags, NULL},
4188+
{"_host_flags", (getter) get_host_flags,
4189+
(setter) set_host_flags, NULL},
41904190
{"options", (getter) get_options,
41914191
(setter) set_options, NULL},
41924192
{"verify_flags", (getter) get_verify_flags,
@@ -5574,12 +5574,30 @@ PyInit__ssl(void)
55745574
SSL_OP_NO_COMPRESSION);
55755575
#endif
55765576

5577+
#ifdef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT
5578+
PyModule_AddIntConstant(m, "HOSTFLAG_ALWAYS_CHECK_SUBJECT",
5579+
X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT);
5580+
#endif
55775581
#ifdef X509_CHECK_FLAG_NEVER_CHECK_SUBJECT
55785582
PyModule_AddIntConstant(m, "HOSTFLAG_NEVER_CHECK_SUBJECT",
55795583
X509_CHECK_FLAG_NEVER_CHECK_SUBJECT);
55805584
#endif
5585+
#ifdef X509_CHECK_FLAG_NO_WILDCARDS
5586+
PyModule_AddIntConstant(m, "HOSTFLAG_NO_WILDCARDS",
5587+
X509_CHECK_FLAG_NO_WILDCARDS);
5588+
#endif
5589+
#ifdef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
55815590
PyModule_AddIntConstant(m, "HOSTFLAG_NO_PARTIAL_WILDCARDS",
55825591
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
5592+
#endif
5593+
#ifdef X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS
5594+
PyModule_AddIntConstant(m, "HOSTFLAG_MULTI_LABEL_WILDCARDS",
5595+
X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS);
5596+
#endif
5597+
#ifdef X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS
5598+
PyModule_AddIntConstant(m, "HOSTFLAG_SINGLE_LABEL_SUBDOMAINS",
5599+
X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS);
5600+
#endif
55835601

55845602
#if HAVE_SNI
55855603
r = Py_True;

0 commit comments

Comments
 (0)