8000 [2.7] bpo-32947: Fixes for TLS 1.3 and OpenSSL 1.1.1 (GH-8761) (GH-11… · python/cpython@2149a9a · GitHub
[go: up one dir, main page]

Skip to content

Commit 2149a9a

Browse files
stratakisvstinner
authored andcommitted
[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: Christian Heimes <christian@python.org> (cherry picked from commit 2a4ee8a)
1 parent 28eb87f commit 2149a9a

File tree

4 files changed

+58
-19
lines changed

4 files changed

+58
-19
lines changed

Doc/library/ssl.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,15 @@ Constants
742742

743743
.. versionadded:: 2.7.9
744744

745+
.. data:: OP_ENABLE_MIDDLEBOX_COMPAT
746+
747+
Send dummy Change Cipher Spec (CCS) messages in TLS 1.3 handshake to make
748+
a TLS 1.3 connection look more like a TLS 1.2 connection.
749+
750+
This option is only available with OpenSSL 1.1.1 and later.
751+
752+
.. versionadded:: 2.7.16
753+
745754
.. data:: OP_NO_COMPRESSION
746755

747756
Disable compression on the SSL channel. This is useful if the application

Lib/test/test_ssl.py

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ def data_file(*name):
8282
OP_SINGLE_DH_USE = getattr(ssl, "OP_SINGLE_DH_USE", 0)
8383
OP_SINGLE_ECDH_USE = getattr(ssl, "OP_SINGLE_ECDH_USE", 0)
8484
OP_CIPHER_SERVER_PREFERENCE = getattr(ssl, "OP_CIPHER_SERVER_PREFERENCE", 0)
85+
OP_ENABLE_MIDDLEBOX_COMPAT = getattr(ssl, "OP_ENABLE_MIDDLEBOX_COMPAT", 0)
8586

8687

8788
def handle_error(prefix):
@@ -806,7 +807,8 @@ def test_options(self):
806807
default = (ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3)
807808
# SSLContext also enables these by default
808809
default |= (OP_NO_COMPRESSION | OP_CIPHER_SERVER_PREFERENCE |
809-
OP_SINGLE_DH_USE | OP_SINGLE_ECDH_USE)
810+
OP_SINGLE_DH_USE | OP_SINGLE_ECDH_USE |
811+
OP_ENABLE_MIDDLEBOX_COMPAT)
810812
self.assertEqual(default, ctx.options)
811813
ctx.options |= ssl.OP_NO_TLSv1
812814
self.assertEqual(default | ssl.OP_NO_TLSv1, ctx.options)
@@ -1697,23 +1699,43 @@ def wrap_conn(self):
16971699
self.sock, server_side=True)
16981700
self.server.selected_npn_protocols.append(self.sslconn.selected_npn_protocol())
16991701
self.server.selected_alpn_protocols.append(self.sslconn.selected_alpn_protocol())
1700-
except socket.error as e:
1701-
# We treat ConnectionResetError as though it were an
1702-
# SSLError - OpenSSL on Ubuntu abruptly closes the
1703-
# connection when asked to use an unsupported protocol.
1704-
#
1705-
# XXX Various errors can have happened here, for example
1706-
# a mismatching protocol version, an invalid certificate,
1707-
# or a low-level bug. This should be made more discriminating.
1708-
if not isinstance(e, ssl.SSLError) and e.errno != errno.ECONNRESET:
1709-
raise
1710-
self.server.conn_errors.append(e)
1711-
if self.server.chatty:
1712-
handle_error("\n server: bad connection attempt from " + repr(self.addr) + ":\n")
1713-
self.running = False
1714-
self.server.stop()
1715-
self.close()
1716-
return False
1702+
except (ssl.SSLError, socket.error, OSError) as e:
1703+
if e.errno in (errno.ECONNRESET, errno.EPIPE, errno.ESHUTDOWN):
1704+
# Mimick Python 3:
1705+
#
1706+
# except (ConnectionResetError, BrokenPipeError):
1707+
#
1708+
# We treat ConnectionResetError as though it were an
1709+
# SSLError - OpenSSL on Ubuntu abruptly closes the
1710+
# connection when asked to use an unsupported protocol.
1711+
#
1712+
# BrokenPipeError is raised in TLS 1.3 mode, when OpenSSL
1713+
# tries to send session tickets after handshake.
1714+
# https://github.com/openssl/openssl/issues/6342
1715+
self.server.conn_errors.append(str(e))
1716+
if self.server.chatty:
1717+
handle_error(
1718+
"\n server: bad connection attempt from "
1719+
+ repr(self.addr) + ":\n")
1720+
self.running = False
1721+
self.close()
1722+
return False
1723+
else:
1724+
# OSError may occur with wrong protocols, e.g. both
1725+
# sides use PROTOCOL_TLS_SERVER.
1726+
#
1727+
# XXX Various errors can have happened here, for example
1728+
# a mismatching protocol version, an invalid certificate,
1729+
# or a low-level bug. This should be made more discriminating.
1730+
if not isinstance(e, ssl.SSLError) and e.errno != errno.ECONNRESET:
1731+
raise
1732+
self.server.conn_errors.append(e)
1733+
if self.server.chatty:
1734+
handle_error("\n server: bad connection attempt from " + repr(self.addr) + ":\n")
1735+
self.running = False
1736+
self.server.stop()
1737+
self.close()
1738+
return False
17171739
else:
17181740
if self.server.context.verify_mode == ssl.CERT_REQUIRED:
17191741
cert = self.sslconn.getpeercert()
@@ -2769,14 +2791,16 @@ def serve():
27692791
# Block on the accept and wait on the connection to close.
27702792
evt.set()
27712793
remote[0], peer[0] = server.accept()
2772-
remote[0].recv(1)
2794+
remote[0].send(remote[0].recv(4))
27732795

27742796
t = threading.Thread(target=serve)
27752797
t.start()
27762798
# Client wait until server setup and perform a connect.
27772799
evt.wait()
27782800
client = context.wrap_socket(socket.socket())
27792801
client.connect((host, port))
2802+
client.send(b'data')
2803+
client.recv()
27802804
client_addr = client.getsockname()
27812805
client.close()
27822806
t.join()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add OP_ENABLE_MIDDLEBOX_COMPAT and test workaround for TLSv1.3 for future
2+
compatibility with OpenSSL 1.1.1.

Modules/_ssl.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4411,6 +4411,10 @@ init_ssl(void)
44114411
PyModule_AddIntConstant(m, "OP_NO_COMPRESSION",
44124412
SSL_OP_NO_COMPRESSION);
44134413
#endif
4414+
#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
4415+
PyModule_AddIntConstant(m, "OP_ENABLE_MIDDLEBOX_COMPAT",
4416+
SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
4417+
#endif
44144418

44154419
#if HAVE_SNI
44164420
r = Py_True;

0 commit comments

Comments
 (0)
0