8000 [3.6] bpo-32947: Fixes for TLS 1.3 and OpenSSL 1.1.1 (GH-5663) by tiran · Pull Request #8761 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

[3.6] bpo-32947: Fixes for TLS 1.3 and OpenSSL 1.1.1 (GH-5663) #8761

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Doc/library/ssl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,15 @@ Constants

.. versionadded:: 3.3

.. 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:: 3.6.7

.. data:: OP_NO_COMPRESSION

Disable compression on the SSL channel. This is useful if the application
Expand Down
6 changes: 5 additions & 1 deletion Lib/test/test_asyncio/test_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -1187,7 +1187,11 @@ def test_create_server_ssl_match_failed(self):
self.loop.run_until_complete(f_c)

# close connection
proto.transport.close()
# transport may be None with TLS 1.3, because connection is
# interrupted, server is unable to send session tickets, and
# transport is closed.
if proto.transport is not None:
proto.transport.close()
server.close()

def test_legacy_create_server_ssl_match_failed(self):
Expand Down
28 changes: 22 additions & 6 deletions Lib/test/test_ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ def data_file(*name):
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):
Expand Down Expand Up @@ -181,8 +182,8 @@ def test_constants(self):
ssl.OP_NO_TLSv1
ssl.OP_NO_TLSv1_3
if ssl.OPENSSL_VERSION_INFO >= (1, 0, 1):
ssl.OP_NO_TLSv1_1
ssl.OP_NO_TLSv1_2
ssl.OP_NO_TLSv1_1
ssl.OP_NO_TLSv1_2

def test_str_for_enums(self):
# Make sure that the PROTOCOL_* constants have enum-like string
Expand Down Expand Up @@ -899,12 +900,13 @@ def test_get_ciphers(self):

@skip_if_broken_ubuntu_ssl
def test_options(self):
ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1)
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
# OP_ALL | OP_NO_SSLv2 | OP_NO_SSLv3 is the default value
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)
Expand Down Expand Up @@ -1857,11 +1859,23 @@ def wrap_conn(self):
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 (ssl.SSLError, ConnectionResetError, OSError) as e:
except (ConnectionResetError, BrokenPipeError) 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.
#
# 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
except (ssl.SSLError, OSError) as e:
# OSError may occur with wrong protocols, e.g. both
# sides use PROTOCOL_TLS_SERVER.
#
Expand Down Expand Up @@ -3042,14 +3056,16 @@ def serve():
# Block on the accept and wait on the connection to close.
evt.set()
remote, peer = server.accept()
remote.recv(1)
remote.send(remote.recv(4))

t = threading.Thread(target=serve)
t.start()
# Client wait until server setup and perform a connect.
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()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add OP_ENABLE_MIDDLEBOX_COMPAT and test workaround for TLSv1.3 for future
compatibility with OpenSSL 1.1.1.
4 changes: 4 additions & 0 deletions Modules/_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -5486,6 +5486,10 @@ PyInit__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;
Expand Down
0