mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
[3.11] GH-95494: Fix transport EOF handling in OpenSSL 3.0 (GH-95495) (#103006)
GH-25309 enabled SSL_OP_IGNORE_UNEXPECTED_EOF by default, with a comment
that it restores OpenSSL 1.1.1 behavior, but this wasn't quite right.
That option causes OpenSSL to treat transport EOF as the same as
close_notify (i.e. SSL_ERROR_ZERO_RETURN), whereas Python actually has
distinct SSLEOFError and SSLZeroReturnError exceptions. (The latter is
usually mapped to a zero return from read.) In OpenSSL 1.1.1, the ssl
module would raise them for transport EOF and close_notify,
respectively. In OpenSSL 3.0, both act like close_notify.
Fix this by, instead, just detecting SSL_R_UNEXPECTED_EOF_WHILE_READING
and mapping that to the other exception type.
There doesn't seem to have been any unit test of this error, so fill in
the missing one. This had to be done with the BIO path because it's
actually slightly tricky to simulate a transport EOF with Python's fd
based APIs. (If you instruct the server to close the socket, it gets
confused, probably because the server's SSL object is still referencing
the now dead fd?)
(cherry picked from commit 420bbb783b
)
Co-authored-by: David Benjamin <davidben@google.com>
This commit is contained in:
parent
b28f919007
commit
13df5d3497
3 changed files with 32 additions and 7 deletions
|
@ -154,7 +154,6 @@ 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)
|
||||
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 @@ class ContextTests(unittest.TestCase):
|
|||
# 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 @@ class SimpleBackgroundTests(unittest.TestCase):
|
|||
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):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue