From bf130dc793bcb52d65000e714d1e98b8ab243786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Gr=C3=B6nholm?= Date: Sun, 13 Oct 2024 14:11:32 +0300 Subject: [PATCH] Fixed TypeError when TLS handshake fails with truststore SSLContext (#801) Fixes #795. (cherry picked from commit ede20291bb040838d36fd6f67f4ff6dcbe6ca815) --- docs/versionhistory.rst | 3 +++ pyproject.toml | 1 + src/anyio/streams/tls.py | 5 ++--- tests/streams/test_tls.py | 28 ++++++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/docs/versionhistory.rst b/docs/versionhistory.rst index 3a0d2e0d..29ac38d9 100644 --- a/docs/versionhistory.rst +++ b/docs/versionhistory.rst @@ -10,6 +10,9 @@ This library adheres to `Semantic Versioning 2.0 `_. - Fixed an async fixture's ``self`` being different than the test's ``self`` in class-based tests (`#633 `_) (PR by @agronholm and @graingert) +- Fixed ``TypeError`` with ``TLSStream`` on Windows when a certificate verification + error occurs when using a `truststore `_ + SSL certificate (`#795 `_) **4.6.0** diff --git a/pyproject.toml b/pyproject.toml index 8f244c42..641ed3bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,6 +52,7 @@ test = [ "pytest >= 7.0", "pytest-mock >= 3.6.1", "trustme", + "truststore >= 0.9.1; python_version >= '3.10'", """\ uvloop >= 0.21.0b1; platform_python_implementation == 'CPython' \ and platform_system != 'Windows'\ diff --git a/src/anyio/streams/tls.py b/src/anyio/streams/tls.py index e913eedb..d01c8e6f 100644 --- a/src/anyio/streams/tls.py +++ b/src/anyio/streams/tls.py @@ -162,9 +162,8 @@ async def _call_sslobject_method( except ssl.SSLError as exc: self._read_bio.write_eof() self._write_bio.write_eof() - if ( - isinstance(exc, ssl.SSLEOFError) - or "UNEXPECTED_EOF_WHILE_READING" in exc.strerror + if isinstance(exc, ssl.SSLEOFError) or ( + exc.strerror and "UNEXPECTED_EOF_WHILE_READING" in exc.strerror ): if self.standard_compatible: raise BrokenResourceError from exc diff --git a/tests/streams/test_tls.py b/tests/streams/test_tls.py index 9846e0c1..0c02d6be 100644 --- a/tests/streams/test_tls.py +++ b/tests/streams/test_tls.py @@ -388,6 +388,34 @@ async def test_default_context_ignore_unexpected_eof_flag_off( send1.close() receive1.close() + async def test_truststore_ssl( + self, request: pytest.FixtureRequest, server_context: ssl.SSLContext + ) -> None: + # This test is only expected to fail on Windows without the associated patch + def serve_sync() -> None: + with server_sock, pytest.raises(ssl.SSLEOFError): + server_sock.accept() + + # We deliberately skip making the client context trust the server context + truststore = pytest.importorskip("truststore") + client_context = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + + server_sock = server_context.wrap_socket( + socket.socket(), server_side=True, suppress_ragged_eofs=True + ) + server_sock.settimeout(1) + server_sock.bind(("127.0.0.1", 0)) + server_sock.listen() + server_thread = Thread(target=serve_sync, daemon=True) + server_thread.start() + request.addfinalizer(server_thread.join) + + async with await connect_tcp(*server_sock.getsockname()) as stream: + with pytest.raises(ssl.SSLCertVerificationError): + await TLSStream.wrap( + stream, hostname="localhost", ssl_context=client_context + ) + class TestTLSListener: async def test_handshake_fail(