diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 5c6ddf12ec356d..85a561f6b74919 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -154,7 +154,6 @@ def data_file(*name): 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) -OP_IGNORE_UNEXPECTED_EOF = getattr(ssl, "OP_IGNORE_UNEXPECTED_EOF", 0) # Ubuntu has patched OpenSSL and changed behavior of security level 2 # see https://bugs.python.org/issue41561#msg389003 @@ -1200,8 +1199,7 @@ def test_options(self): # SSLContext also enables these by default default |= (OP_NO_COMPRESSION | OP_CIPHER_SERVER_PREFERENCE | OP_SINGLE_DH_USE | OP_SINGLE_ECDH_USE | - OP_ENABLE_MIDDLEBOX_COMPAT | - OP_IGNORE_UNEXPECTED_EOF) + OP_ENABLE_MIDDLEBOX_COMPAT) self.assertEqual(default, ctx.options) with warnings_helper.check_warnings(): ctx.options |= ssl.OP_NO_TLSv1 @@ -2362,6 +2360,20 @@ def test_bio_read_write_data(self): self.assertEqual(buf, b'foo\n') self.ssl_io_loop(sock, incoming, outgoing, sslobj.unwrap) + def test_transport_eof(self): + client_context, server_context, hostname = testing_context() + with socket.socket(socket.AF_INET) as sock: + sock.connect(self.server_addr) + incoming = ssl.MemoryBIO() + outgoing = ssl.MemoryBIO() + sslobj = client_context.wrap_bio(incoming, outgoing, + server_hostname=hostname) + self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake) + + # Simulate EOF from the transport. + incoming.write_eof() + self.assertRaises(ssl.SSLEOFError, sslobj.read) + @support.requires_resource('network') class NetworkedTests(unittest.TestCase): diff --git a/Misc/NEWS.d/next/Library/2022-07-30-23-01-43.gh-issue-95495.RA-q1d.rst b/Misc/NEWS.d/next/Library/2022-07-30-23-01-43.gh-issue-95495.RA-q1d.rst new file mode 100644 index 00000000000000..d0f4ccbdd3e39f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-07-30-23-01-43.gh-issue-95495.RA-q1d.rst @@ -0,0 +1,7 @@ +When built against OpenSSL 3.0, the :mod:`ssl` module had a bug where it +reported unauthenticated EOFs (i.e. without close_notify) as a clean TLS-level +EOF. It now raises :exc:`~ssl.SSLEOFError`, matching the behavior in previous +versions of OpenSSL. The :attr:`~ssl.SSLContext.options` attribute on +:class:`~ssl.SSLContext` also no longer includes +:data:`~ssl.OP_IGNORE_UNEXPECTED_EOF` by default. This option may be set to +specify the previous OpenSSL 3.0 behavior. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 79f78007b68dcd..61490783e9ba95 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -665,6 +665,16 @@ PySSL_SetError(PySSLSocket *sslsock, int ret, const char *filename, int lineno) ERR_GET_REASON(e) == SSL_R_CERTIFICATE_VERIFY_FAILED) { type = state->PySSLCertVerificationErrorObject; } +#if defined(SSL_R_UNEXPECTED_EOF_WHILE_READING) + /* OpenSSL 3.0 changed transport EOF from SSL_ERROR_SYSCALL with + * zero return value to SSL_ERROR_SSL with a special error code. */ + if (ERR_GET_LIB(e) == ERR_LIB_SSL && + ERR_GET_REASON(e) == SSL_R_UNEXPECTED_EOF_WHILE_READING) { + p = PY_SSL_ERROR_EOF; + type = state->PySSLEOFErrorObject; + errstr = "EOF occurred in violation of protocol"; + } +#endif break; } default: @@ -3133,10 +3143,6 @@ _ssl__SSLContext_impl(PyTypeObject *type, int proto_version) #endif #ifdef SSL_OP_SINGLE_ECDH_USE options |= SSL_OP_SINGLE_ECDH_USE; -#endif -#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF - /* Make OpenSSL 3.0.0 behave like 1.1.1 */ - options |= SSL_OP_IGNORE_UNEXPECTED_EOF; #endif SSL_CTX_set_options(self->ctx, options);