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

@ -146,9 +146,10 @@ Functions, Constants, and Exceptions
.. exception:: CertificateError
Raised to signal an error with a certificate (such as mismatching
hostname). Certificate errors detected by OpenSSL, though, raise
an :exc:`SSLCertVerificationError`.
An alias for :exc:`SSLCertVerificationError`.
.. versionchanged:: 3.7
The exception is now an alias for :exc:`SSLCertVerificationError`.
Socket creation
@ -430,8 +431,14 @@ Certificate handling
of the certificate, is now supported.
.. versionchanged:: 3.7
The function is no longer used to TLS connections. Hostname matching
is now performed by OpenSSL.
Allow wildcard when it is the leftmost and the only character
in that segment.
in that segment. Partial wildcards like ``www*.example.com`` are no
longer supported.
.. deprecated:: 3.7
.. function:: cert_time_to_seconds(cert_time)
@ -850,6 +857,14 @@ Constants
.. versionadded:: 3.5
.. data:: HAS_NEVER_CHECK_COMMON_NAME
Whether the OpenSSL library has built-in support not checking subject
common name and :attr:`SSLContext.hostname_checks_common_name` is
writeable.
.. versionadded:: 3.7
.. data:: HAS_ECDH
Whether the OpenSSL library has built-in support for Elliptic Curve-based
@ -1075,6 +1090,12 @@ SSL sockets also have the following additional methods and attributes:
The socket timeout is no more reset each time bytes are received or sent.
The socket timeout is now to maximum total duration of the handshake.
.. versionchanged:: 3.7
Hostname or IP address is matched by OpenSSL during handshake. The
function :func:`match_hostname` is no longer used. In case OpenSSL
refuses a hostname or IP address, the handshake is aborted early and
a TLS alert message is send to the peer.
.. method:: SSLSocket.getpeercert(binary_form=False)
If there is no certificate for the peer on the other end of the connection,
@ -1730,6 +1751,17 @@ to speed up repeated connections from the same clients.
The protocol version chosen when constructing the context. This attribute
is read-only.
.. attribute:: SSLContext.hostname_checks_common_name
Whether :attr:`~SSLContext.check_hostname` falls back to verify the cert's
subject common name in the absence of a subject alternative name
extension (default: true).
.. versionadded:: 3.7
.. note::
Only writeable with OpenSSL 1.1.0 or higher.
.. attribute:: SSLContext.verify_flags
The flags for certificate verification operations. You can set flags like
@ -2324,6 +2356,10 @@ in this case, the :func:`match_hostname` function can be used. This common
check is automatically performed when :attr:`SSLContext.check_hostname` is
enabled.
.. versionchanged:: 3.7
Hostname matchings is now performed by OpenSSL. Python no longer uses
:func:`match_hostname`.
In server mode, if you want to authenticate your clients using the SSL layer
(rather than using a higher-level authentication mechanism), you'll also have
to specify :const:`CERT_REQUIRED` and similarly check the client certificate.

View File

@ -568,6 +568,32 @@ can be set within the scope of a group.
``'^$'`` or ``(?=-)`` that matches an empty string.
(Contributed by Serhiy Storchaka in :issue:`25054`.)
ssl
---
The ssl module now uses OpenSSL's builtin API instead of
:func:`~ssl.match_hostname` to check host name or IP address. Values
are validated during TLS handshake. Any cert validation error including
a failing host name match now raises :exc:`~ssl.SSLCertVerificationError` and
aborts the handshake with a proper TLS Alert message. The new exception
contains additional information. Host name validation can be customized
with :attr:`~ssl.SSLContext.host_flags`.
(Contributed by Christian Heimes in :issue:`31399`.)
.. note::
The improved host name check requires an OpenSSL 1.0.2 or 1.1 compatible
libssl. OpenSSL 0.9.8 and 1.0.1 are no longer supported. LibreSSL is
temporarily not supported until it gains the necessary OpenSSL 1.0.2 APIs.
The ssl module no longer sends IP addresses in SNI TLS extension.
(Contributed by Christian Heimes in :issue:`32185`.)
:func:`~ssl.match_hostname` no longer supports partial wildcards like
``www*.example.org``. :attr:`~ssl.SSLContext.host_flags` has partial
wildcard matching disabled by default.
(Contributed by Mandeep Singh in :issue:`23033` and Christian Heimes in
:issue:`31399`.)
string
------
@ -1120,6 +1146,12 @@ Other CPython implementation changes
emitted in the first place), and an explicit ``error::BytesWarning``
warnings filter added to convert them to exceptions.
* CPython' :mod:`ssl` module requires OpenSSL 1.0.2 or 1.1 compatible libssl.
OpenSSL 1.0.1 has reached end of lifetime on 2016-12-31 and is no longer
supported. LibreSSL is temporarily not supported as well. LibreSSL releases
up to version 2.6.4 are missing required OpenSSL 1.0.2 APIs.
Documentation
=============

View File

@ -590,12 +590,6 @@ class SSLProtocol(protocols.Protocol):
raise handshake_exc
peercert = sslobj.getpeercert()
if not hasattr(self._sslcontext, 'check_hostname'):
# Verify hostname if requested, Python 3.4+ uses check_hostname
# and checks the hostname in do_handshake()
if (self._server_hostname and
self._sslcontext.verify_mode != ssl.CERT_NONE):
ssl.match_hostname(peercert, self._server_hostname)
except BaseException as exc:
if self._loop.get_debug():
if isinstance(exc, ssl.CertificateError):

View File

@ -1375,7 +1375,8 @@ else:
if key_file or cert_file:
context.load_cert_chain(cert_file, key_file)
self._context = context
self._check_hostname = check_hostname
if check_hostname is not None:
self._context.check_hostname = check_hostname
def connect(self):
"Connect to a host on a given (SSL) port."
@ -1389,13 +1390,6 @@ else:
self.sock = self._context.wrap_socket(self.sock,
server_hostname=server_hostname)
if not self._context.check_hostname and self._check_hostname:
try:
ssl.match_hostname(self.sock.getpeercert(), server_hostname)
except Exception:
self.sock.shutdown(socket.SHUT_RDWR)
self.sock.close()
raise
__all__.append("HTTPSConnection")

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

View File

@ -1148,11 +1148,13 @@ class EventLoopTestsMixin:
with test_utils.disable_logger():
with self.assertRaisesRegex(
ssl.CertificateError,
"hostname '127.0.0.1' doesn't match 'localhost'"):
"IP address mismatch, certificate is not valid for "
"'127.0.0.1'"):
self.loop.run_until_complete(f_c)
# close connection
proto.transport.close()
# transport is None because TLS ALERT aborted the handshake
self.assertIsNone(proto.transport)
server.close()
@support.skip_unless_bind_unix_socket

View File

@ -330,6 +330,9 @@ if ssl is not None:
return
elif err.args[0] == ssl.SSL_ERROR_EOF:
return self.handle_close()
# TODO: SSLError does not expose alert information
elif "SSLV3_ALERT_BAD_CERTIFICATE" in err.args[1]:
return self.handle_close()
raise
except OSError as err:
if err.args[0] == errno.ECONNABORTED:

View File

@ -485,7 +485,8 @@ class NewIMAPSSLTests(NewIMAPTestsMixin, unittest.TestCase):
ssl_context.load_verify_locations(CAFILE)
with self.assertRaisesRegex(ssl.CertificateError,
"hostname '127.0.0.1' doesn't match 'localhost'"):
"IP address mismatch, certificate is not valid for "
"'127.0.0.1'"):
_, server = self._setup(SimpleIMAPHandler)
client = self.imap_class(*server.server_address,
ssl_context=ssl_context)
@ -874,7 +875,8 @@ class ThreadedNetworkedTestsSSL(ThreadedNetworkedTests):
with self.assertRaisesRegex(
ssl.CertificateError,
"hostname '127.0.0.1' doesn't match 'localhost'"):
"IP address mismatch, certificate is not valid for "
"'127.0.0.1'"):
with self.reaped_server(SimpleIMAPHandler) as server:
client = self.imap_class(*server.server_address,
ssl_context=ssl_context)

View File

@ -176,6 +176,9 @@ class DummyPOP3Handler(asynchat.async_chat):
return
elif err.args[0] == ssl.SSL_ERROR_EOF:
return self.handle_close()
# TODO: SSLError does not expose alert information
elif "SSLV3_ALERT_BAD_CERTIFICATE" in err.args[1]:
return self.handle_close()
raise
except OSError as err:
if err.args[0] == errno.ECONNABORTED:

View File

@ -988,6 +988,19 @@ class ContextTests(unittest.TestCase):
self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED)
self.assertTrue(ctx.check_hostname)
def test_hostname_checks_common_name(self):
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
self.assertTrue(ctx.hostname_checks_common_name)
if ssl.HAS_NEVER_CHECK_COMMON_NAME:
ctx.hostname_checks_common_name = True
self.assertTrue(ctx.hostname_checks_common_name)
ctx.hostname_checks_common_name = False
self.assertFalse(ctx.hostname_checks_common_name)
ctx.hostname_checks_common_name = True
self.assertTrue(ctx.hostname_checks_common_name)
else:
with self.assertRaises(AttributeError):
ctx.hostname_checks_common_name = True
@unittest.skipUnless(have_verify_flags(),
"verify_flags need OpenSSL > 0.9.8")
@ -1511,6 +1524,16 @@ class SSLErrorTests(unittest.TestCase):
ctx.wrap_bio(ssl.MemoryBIO(), ssl.MemoryBIO(),
server_hostname="xn--.com")
def test_bad_server_hostname(self):
ctx = ssl.create_default_context()
with self.assertRaises(ValueError):
ctx.wrap_bio(ssl.MemoryBIO(), ssl.MemoryBIO(),
server_hostname="")
with self.assertRaises(ValueError):
ctx.wrap_bio(ssl.MemoryBIO(), ssl.MemoryBIO(),
server_hostname=".example.org")
class MemoryBIOTests(unittest.TestCase):
def test_read_write(self):
@ -2536,8 +2559,9 @@ class ThreadedTests(unittest.TestCase):
with server:
with client_context.wrap_socket(socket.socket(),
server_hostname="invalid") as s:
with self.assertRaisesRegex(ssl.CertificateError,
"hostname 'invalid' doesn't match 'localhost'"):
with self.assertRaisesRegex(
ssl.CertificateError,
"Hostname mismatch, certificate is not valid for 'invalid'."):
s.connect((HOST, server.port))
# missing server_hostname arg should cause an exception, too

View File

@ -573,7 +573,7 @@ class TestUrlopen(unittest.TestCase):
cafile=CERT_fakehostname)
# Good cert, but mismatching hostname
handler = self.start_https_server(certfile=CERT_fakehostname)
with self.assertRaises(ssl.CertificateError) as cm:
with self.assertRaises(urllib.error.URLError) as cm:
self.urlopen("https://localhost:%s/bizarre" % handler.port,
cafile=CERT_fakehostname)

View File

@ -0,0 +1,4 @@
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. Subject
common name fallback can be disabled with
SSLContext.hostname_checks_common_name.

View File

@ -64,10 +64,13 @@ static PySocketModule_APIObject PySocketModule;
#include "openssl/rand.h"
#include "openssl/bio.h"
/* Set HAVE_X509_VERIFY_PARAM_SET1_HOST for non-autoconf builds */
#ifndef HAVE_X509_VERIFY_PARAM_SET1_HOST
# if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER > 0x1000200fL
# ifdef LIBRESSL_VERSION_NUMBER
# error "LibreSSL is missing X509_VERIFY_PARAM_set1_host(), see https://github.com/libressl-portable/portable/issues/381"
# elif OPENSSL_VERSION_NUMBER > 0x1000200fL
# define HAVE_X509_VERIFY_PARAM_SET1_HOST
# else
# error "libssl is too old and does not support X509_VERIFY_PARAM_set1_host()"
# endif
#endif
@ -217,11 +220,6 @@ static STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *store) {
return store->objs;
}
static X509_VERIFY_PARAM *X509_STORE_get0_param(X509_STORE *store)
{
return store->param;
}
static int
SSL_SESSION_has_ticket(const SSL_SESSION *s)
{
@ -317,6 +315,10 @@ typedef struct {
PyObject *set_hostname;
#endif
int check_hostname;
/* OpenSSL has no API to get hostflags from X509_VERIFY_PARAM* struct.
* We have to maintain our own copy. OpenSSL's hostflags default to 0.
*/
unsigned int hostflags;
} PySSLContext;
typedef struct {
@ -701,6 +703,74 @@ _setSSLError (const char *errstr, int errcode, const char *filename, int lineno)
return NULL;
}
/*
* SSL objects
*/
static int
_ssl_configure_hostname(PySSLSocket *self, const char* server_hostname)
{
int retval = -1;
ASN1_OCTET_STRING *ip;
PyObject *hostname;
size_t len;
assert(server_hostname);
/* Disable OpenSSL's special mode with leading dot in hostname:
* When name starts with a dot (e.g ".example.com"), it will be
* matched by a certificate valid for any sub-domain of name.
*/
len = strlen(server_hostname);
if (len == 0 || *server_hostname == '.') {
PyErr_SetString(
PyExc_ValueError,
"server_hostname cannot be an empty string or start with a "
"leading dot.");
return retval;
}
/* inet_pton is not available on all platforms. */
ip = a2i_IPADDRESS(server_hostname);
if (ip == NULL) {
ERR_clear_error();
}
hostname = PyUnicode_Decode(server_hostname, len, "idna", "strict");
if (hostname == NULL) {
goto error;
}
self->server_hostname = hostname;
/* Only send SNI extension for non-IP hostnames */
if (ip == NULL) {
if (!SSL_set_tlsext_host_name(self->ssl, server_hostname)) {
_setSSLError(NULL, 0, __FILE__, __LINE__);
}
}
if (self->ctx->check_hostname) {
X509_VERIFY_PARAM *param = SSL_get0_param(self->ssl);
if (ip == NULL) {
if (!X509_VERIFY_PARAM_set1_host(param, server_hostname, 0)) {
_setSSLError(NULL, 0, __FILE__, __LINE__);
goto error;
}
} else {
if (!X509_VERIFY_PARAM_set1_ip(param, ASN1_STRING_data(ip),
ASN1_STRING_length(ip))) {
_setSSLError(NULL, 0, __FILE__, __LINE__);
goto error;
}
}
}
retval = 0;
error:
if (ip != NULL) {
ASN1_OCTET_STRING_free(ip);
}
return retval;
}
static PySSLSocket *
newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
enum py_ssl_server_or_client socket_type,
@ -722,15 +792,6 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
self->shutdown_seen_zero = 0;
self->owner = NULL;
self->server_hostname = NULL;
if (server_hostname != NULL) {
PyObject *hostname = PyUnicode_Decode(server_hostname, strlen(server_hostname),
"idna", "strict");
if (hostname == NULL) {
Py_DECREF(self);
return NULL;
}
self->server_hostname = hostname;
}
self->ssl_errno = 0;
self->c_errno = 0;
#ifdef MS_WINDOWS
@ -761,10 +822,12 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
#endif
SSL_set_mode(self->ssl, mode);
#if HAVE_SNI
if (server_hostname != NULL)
SSL_set_tlsext_host_name(self->ssl, server_hostname);
#endif
if (server_hostname != NULL) {
if (_ssl_configure_hostname(self, server_hostname) < 0) {
Py_DECREF(self);
return NULL;
}
}
/* If the socket is in non-blocking mode or timeout mode, set the BIO
* to non-blocking mode (blocking is the default)
*/
@ -2711,6 +2774,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
PySSLContext *self;
long options;
SSL_CTX *ctx = NULL;
X509_VERIFY_PARAM *params;
int result;
#if defined(SSL_MODE_RELEASE_BUFFERS)
unsigned long libver;
@ -2760,6 +2824,7 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
return NULL;
}
self->ctx = ctx;
self->hostflags = X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS;
#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
self->npn_protocols = NULL;
#endif
@ -2858,14 +2923,13 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version)
sizeof(SID_CTX));
#undef SID_CTX
params = SSL_CTX_get0_param(self->ctx);
#ifdef X509_V_FLAG_TRUSTED_FIRST
{
/* Improve trust chain building when cross-signed intermediate
certificates are present. See https://bugs.python.org/issue23476. */
X509_STORE *store = SSL_CTX_get_cert_store(self->ctx);
X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST);
}
/* Improve trust chain building when cross-signed intermediate
certificates are present. See https://bugs.python.org/issue23476. */
X509_VERIFY_PARAM_set_flags(params, X509_V_FLAG_TRUSTED_FIRST);
#endif
X509_VERIFY_PARAM_set_hostflags(params, self->hostflags);
return (PyObject *)self;
}
@ -3152,12 +3216,10 @@ set_verify_mode(PySSLContext *self, PyObject *arg, void *c)
static PyObject *
get_verify_flags(PySSLContext *self, void *c)
{
X509_STORE *store;
X509_VERIFY_PARAM *param;
unsigned long flags;
store = SSL_CTX_get_cert_store(self->ctx);
param = X509_STORE_get0_param(store);
param = SSL_CTX_get0_param(self->ctx);
flags = X509_VERIFY_PARAM_get_flags(param);
return PyLong_FromUnsignedLong(flags);
}
@ -3165,14 +3227,12 @@ get_verify_flags(PySSLContext *self, void *c)
static int
set_verify_flags(PySSLContext *self, PyObject *arg, void *c)
{
X509_STORE *store;
X509_VERIFY_PARAM *param;
unsigned long new_flags, flags, set, clear;
if (!PyArg_Parse(arg, "k", &new_flags))
return -1;
store = SSL_CTX_get_cert_store(self->ctx);
param = X509_STORE_get0_param(store);
param = SSL_CTX_get0_param(self->ctx);
flags = X509_VERIFY_PARAM_get_flags(param);
clear = flags & ~new_flags;
set = ~flags & new_flags;
@ -3220,6 +3280,27 @@ set_options(PySSLContext *self, PyObject *arg, void *c)
return 0;
}
static PyObject *
get_host_flags(PySSLContext *self, void *c)
{
return PyLong_FromUnsignedLong(self->hostflags);
}
static int
set_host_flags(PySSLContext *self, PyObject *arg, void *c)
{
X509_VERIFY_PARAM *param;
unsigned int new_flags = 0;
if (!PyArg_Parse(arg, "I", &new_flags))
return -1;
param = SSL_CTX_get0_param(self->ctx);
self->hostflags = new_flags;
X509_VERIFY_PARAM_set_hostflags(param, new_flags);
return 0;
}
static PyObject *
get_check_hostname(PySSLContext *self, void *c)
{
@ -4104,6 +4185,8 @@ _ssl__SSLContext_get_ca_certs_impl(PySSLContext *self, int binary_form)
static PyGetSetDef context_getsetlist[] = {
{"check_hostname", (getter) get_check_hostname,
(setter) set_check_hostname, NULL},
{"_host_flags", (getter) get_host_flags,
(setter) set_host_flags, NULL},
{"options", (getter) get_options,
(setter) set_options, NULL},
{"verify_flags", (getter) get_verify_flags,
@ -5491,6 +5574,31 @@ PyInit__ssl(void)
SSL_OP_NO_COMPRESSION);
#endif
#ifdef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT
PyModule_AddIntConstant(m, "HOSTFLAG_ALWAYS_CHECK_SUBJECT",
X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT);
#endif
#ifdef X509_CHECK_FLAG_NEVER_CHECK_SUBJECT
PyModule_AddIntConstant(m, "HOSTFLAG_NEVER_CHECK_SUBJECT",
X509_CHECK_FLAG_NEVER_CHECK_SUBJECT);
#endif
#ifdef X509_CHECK_FLAG_NO_WILDCARDS
PyModule_AddIntConstant(m, "HOSTFLAG_NO_WILDCARDS",
X509_CHECK_FLAG_NO_WILDCARDS);
#endif
#ifdef X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS
PyModule_AddIntConstant(m, "HOSTFLAG_NO_PARTIAL_WILDCARDS",
X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
#endif
#ifdef X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS
PyModule_AddIntConstant(m, "HOSTFLAG_MULTI_LABEL_WILDCARDS",
X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS);
#endif
#ifdef X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS
PyModule_AddIntConstant(m, "HOSTFLAG_SINGLE_LABEL_SUBDOMAINS",
X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS);
#endif
#if HAVE_SNI
r = Py_True;
#else

View File

@ -687,4 +687,7 @@ Py_NO_ENABLE_SHARED to find out. Also support MS_NO_COREDLL for b/w compat */
/* framework name */
#define _PYTHONFRAMEWORK ""
/* Define if libssl has X509_VERIFY_PARAM_set1_host and related function */
#define HAVE_X509_VERIFY_PARAM_SET1_HOST 1
#endif /* !Py_CONFIG_H */

View File

@ -363,6 +363,16 @@ class PyBuildExt(build_ext):
print_three_column(failed)
print()
if any('_ssl' in l
for l in (missing, self.failed, self.failed_on_import)):
print()
print("Could not build the ssl module!")
print("Python requires an OpenSSL 1.0.2 or 1.1 compatible "
"libssl with X509_VERIFY_PARAM_set1_host().")
print("LibreSSL 2.6.4 and earlier do not provide the necessary "
"APIs, https://github.com/libressl-portable/portable/issues/381")
print()
def build_extension(self, ext):
if ext.name == '_ctypes':
@ -2144,13 +2154,16 @@ class PyBuildExt(build_ext):
if krb5_h:
ssl_incs.extend(krb5_h)
ssl_ext = Extension(
'_ssl', ['_ssl.c'],
include_dirs=openssl_includes,
library_dirs=openssl_libdirs,
libraries=openssl_libs,
depends=['socketmodule.h']
)
if config_vars.get("HAVE_X509_VERIFY_PARAM_SET1_HOST"):
ssl_ext = Extension(
'_ssl', ['_ssl.c'],
include_dirs=openssl_includes,
library_dirs=openssl_libdirs,
libraries=openssl_libs,
depends=['socketmodule.h']
)
else:
ssl_ext = None
hashlib_ext = Extension(
'_hashlib', ['_hashopenssl.c'],