bpo-31399: Let OpenSSL verify hostname and IP address (#3462)

bpo-31399: Let OpenSSL verify hostname and IP

The ssl module now uses OpenSSL's X509_VERIFY_PARAM_set1_host() and
X509_VERIFY_PARAM_set1_ip() API to verify hostname and IP addresses.

* Remove match_hostname calls
* Check for libssl with set1_host, libssl must provide X509_VERIFY_PARAM_set1_host()
* Add documentation for OpenSSL 1.0.2 requirement
* Don't support OpenSSL special mode with a leading dot, e.g. ".example.org" matches "www.example.org". It's not standard conform.
* Add hostname_checks_common_name

Signed-off-by: Christian Heimes <christian@python.org>
This commit is contained in:
Christian Heimes 2018-01-27 15:51:38 +01:00 committed by GitHub
parent 746cc75541
commit 61d478c71c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 302 additions and 73 deletions

View file

@ -148,7 +148,6 @@ _IntEnum._convert(
lambda name: name.startswith('CERT_'),
source=_ssl)
PROTOCOL_SSLv23 = _SSLMethod.PROTOCOL_SSLv23 = _SSLMethod.PROTOCOL_TLS
_PROTOCOL_NAMES = {value: name for name, value in _SSLMethod.__members__.items()}
@ -172,6 +171,8 @@ if _ssl.HAS_TLS_UNIQUE:
else:
CHANNEL_BINDING_TYPES = []
HAS_NEVER_CHECK_COMMON_NAME = hasattr(_ssl, 'HOSTFLAG_NEVER_CHECK_SUBJECT')
# Disable weak or insecure ciphers by default
# (OpenSSL's default setting is 'DEFAULT:!aNULL:!eNULL')
@ -216,9 +217,7 @@ _RESTRICTED_SERVER_CIPHERS = (
'!aNULL:!eNULL:!MD5:!DSS:!RC4:!3DES'
)
class CertificateError(ValueError):
pass
CertificateError = SSLCertVerificationError
def _dnsname_match(dn, hostname):
@ -473,6 +472,23 @@ class SSLContext(_SSLContext):
def options(self, value):
super(SSLContext, SSLContext).options.__set__(self, value)
if hasattr(_ssl, 'HOSTFLAG_NEVER_CHECK_SUBJECT'):
@property
def hostname_checks_common_name(self):
ncs = self._host_flags & _ssl.HOSTFLAG_NEVER_CHECK_SUBJECT
return ncs != _ssl.HOSTFLAG_NEVER_CHECK_SUBJECT
@hostname_checks_common_name.setter
def hostname_checks_common_name(self, value):
if value:
self._host_flags &= ~_ssl.HOSTFLAG_NEVER_CHECK_SUBJECT
else:
self._host_flags |= _ssl.HOSTFLAG_NEVER_CHECK_SUBJECT
else:
@property
def hostname_checks_common_name(self):
return True
@property
def verify_flags(self):
return VerifyFlags(super().verify_flags)
@ -699,11 +715,6 @@ class SSLObject:
def do_handshake(self):
"""Start the SSL/TLS handshake."""
self._sslobj.do_handshake()
if self.context.check_hostname:
if not self.server_hostname:
raise ValueError("check_hostname needs server_hostname "
"argument")
match_hostname(self.getpeercert(), self.server_hostname)
def unwrap(self):
"""Start the SSL shutdown handshake."""