Commit 2149a9ad authored by stratakis's avatar stratakis Committed by Victor Stinner

[2.7] bpo-32947: Fixes for TLS 1.3 and OpenSSL 1.1.1 (GH-8761) (GH-11876)

Backport of TLS 1.3 related fixes from 3.7.

Misc fixes and workarounds for compatibility with OpenSSL 1.1.1 from git
master and TLS 1.3 support. With OpenSSL 1.1.1, Python negotiates TLS 1.3 by
default. Some test cases only apply to TLS 1.2.

OpenSSL 1.1.1 has added a new option OP_ENABLE_MIDDLEBOX_COMPAT for TLS
1.3. The feature is enabled by default for maximum compatibility with
broken middle boxes. Users should be able to disable the hack and CPython's test suite needs
it to verify default options
Signed-off-by: default avatarChristian Heimes <christian@python.org>
(cherry picked from commit 2a4ee8aa01d61b6a9c8e9c65c211e61bdb471826)
parent 28eb87f4
......@@ -742,6 +742,15 @@ Constants
.. versionadded:: 2.7.9
.. data:: OP_ENABLE_MIDDLEBOX_COMPAT
Send dummy Change Cipher Spec (CCS) messages in TLS 1.3 handshake to make
a TLS 1.3 connection look more like a TLS 1.2 connection.
This option is only available with OpenSSL 1.1.1 and later.
.. versionadded:: 2.7.16
.. data:: OP_NO_COMPRESSION
Disable compression on the SSL channel. This is useful if the application
......
......@@ -82,6 +82,7 @@ OP_NO_COMPRESSION = getattr(ssl, "OP_NO_COMPRESSION", 0)
OP_SINGLE_DH_USE = getattr(ssl, "OP_SINGLE_DH_USE", 0)
OP_SINGLE_ECDH_USE = getattr(ssl, "OP_SINGLE_ECDH_USE", 0)
OP_CIPHER_SERVER_PREFERENCE = getattr(ssl, "OP_CIPHER_SERVER_PREFERENCE", 0)
OP_ENABLE_MIDDLEBOX_COMPAT = getattr(ssl, "OP_ENABLE_MIDDLEBOX_COMPAT", 0)
def handle_error(prefix):
......@@ -806,7 +807,8 @@ class ContextTests(unittest.TestCase):
default = (ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3)
# SSLContext also enables these by default
default |= (OP_NO_COMPRESSION | OP_CIPHER_SERVER_PREFERENCE |
OP_SINGLE_DH_USE | OP_SINGLE_ECDH_USE)
OP_SINGLE_DH_USE | OP_SINGLE_ECDH_USE |
OP_ENABLE_MIDDLEBOX_COMPAT)
self.assertEqual(default, ctx.options)
ctx.options |= ssl.OP_NO_TLSv1
self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options)
......@@ -1697,23 +1699,43 @@ else:
self.sock, server_side=True)
self.server.selected_npn_protocols.append(self.sslconn.selected_npn_protocol())
self.server.selected_alpn_protocols.append(self.sslconn.selected_alpn_protocol())
except socket.error as e:
# We treat ConnectionResetError as though it were an
# SSLError - OpenSSL on Ubuntu abruptly closes the
# connection when asked to use an unsupported protocol.
#
# XXX Various errors can have happened here, for example
# a mismatching protocol version, an invalid certificate,
# or a low-level bug. This should be made more discriminating.
if not isinstance(e, ssl.SSLError) and e.errno != errno.ECONNRESET:
raise
self.server.conn_errors.append(e)
if self.server.chatty:
handle_error("\n server: bad connection attempt from " + repr(self.addr) + ":\n")
self.running = False
self.server.stop()
self.close()
return False
except (ssl.SSLError, socket.error, OSError) as e:
if e.errno in (errno.ECONNRESET, errno.EPIPE, errno.ESHUTDOWN):
# Mimick Python 3:
#
# except (ConnectionResetError, BrokenPipeError):
#
# We treat ConnectionResetError as though it were an
# SSLError - OpenSSL on Ubuntu abruptly closes the
# connection when asked to use an unsupported protocol.
#
# BrokenPipeError is raised in TLS 1.3 mode, when OpenSSL
# tries to send session tickets after handshake.
# https://github.com/openssl/openssl/issues/6342
self.server.conn_errors.append(str(e))
if self.server.chatty:
handle_error(
"\n server: bad connection attempt from "
+ repr(self.addr) + ":\n")
self.running = False
self.close()
return False
else:
# OSError may occur with wrong protocols, e.g. both
# sides use PROTOCOL_TLS_SERVER.
#
# XXX Various errors can have happened here, for example
# a mismatching protocol version, an invalid certificate,
# or a low-level bug. This should be made more discriminating.
if not isinstance(e, ssl.SSLError) and e.errno != errno.ECONNRESET:
raise
self.server.conn_errors.append(e)
if self.server.chatty:
handle_error("\n server: bad connection attempt from " + repr(self.addr) + ":\n")
self.running = False
self.server.stop()
self.close()
return False
else:
if self.server.context.verify_mode == ssl.CERT_REQUIRED:
cert = self.sslconn.getpeercert()
......@@ -2769,7 +2791,7 @@ else:
# Block on the accept and wait on the connection to close.
evt.set()
remote[0], peer[0] = server.accept()
remote[0].recv(1)
remote[0].send(remote[0].recv(4))
t = threading.Thread(target=serve)
t.start()
......@@ -2777,6 +2799,8 @@ else:
evt.wait()
client = context.wrap_socket(socket.socket())
client.connect((host, port))
client.send(b'data')
client.recv()
client_addr = client.getsockname()
client.close()
t.join()
......
Add OP_ENABLE_MIDDLEBOX_COMPAT and test workaround for TLSv1.3 for future
compatibility with OpenSSL 1.1.1.
......@@ -4411,6 +4411,10 @@ init_ssl(void)
PyModule_AddIntConstant(m, "OP_NO_COMPRESSION",
SSL_OP_NO_COMPRESSION);
#endif
#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
PyModule_AddIntConstant(m, "OP_ENABLE_MIDDLEBOX_COMPAT",
SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
#endif
#if HAVE_SNI
r = Py_True;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment