diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 25ecfaec256..e12cfc6af0e 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -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 diff --git a/Lib/test/test_asyncio/test_events.py b/Lib/test/test_asyncio/test_events.py index 548a4610f77..bd62897b6a5 100644 --- a/Lib/test/test_asyncio/test_events.py +++ b/Lib/test/test_asyncio/test_events.py @@ -1187,7 +1187,11 @@ class EventLoopTestsMixin: 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): diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 9785a59a7e4..6b3cd5b6e37 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -89,6 +89,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): @@ -181,8 +182,8 @@ class BasicSocketTests(unittest.TestCase): 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 @@ -899,12 +900,13 @@ class ContextTests(unittest.TestCase): @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) @@ -1857,11 +1859,23 @@ if _have_threads: 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. # @@ -3042,7 +3056,7 @@ if _have_threads: # 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() @@ -3050,6 +3064,8 @@ if _have_threads: 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() diff --git a/Misc/NEWS.d/next/Library/2018-08-14-08-57-01.bpo-32947.mqStVW.rst b/Misc/NEWS.d/next/Library/2018-08-14-08-57-01.bpo-32947.mqStVW.rst new file mode 100644 index 00000000000..28de360c367 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-08-14-08-57-01.bpo-32947.mqStVW.rst @@ -0,0 +1,2 @@ +Add OP_ENABLE_MIDDLEBOX_COMPAT and test workaround for TLSv1.3 for future +compatibility with OpenSSL 1.1.1. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 5e007da858b..e0c7dfbdaac 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -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;