From f6d6a814589f3f6b88a21b2ff12790df1e79376f Mon Sep 17 00:00:00 2001 From: mcepl <> Date: Thu, 14 Sep 2023 14:25:03 +0000 Subject: [PATCH] Update python to version 2.7.18 / rev 187 via SR 1110909 https://build.opensuse.org/request/show/1110909 by user mcepl + anag+factory Forwarded request #1110536 from dgarcia - Add CVE-2023-40217-avoid-ssl-pre-close.patch fixing gh#python/cpython#108310, backport from upstream patch gh#python/cpython#108315 (bsc#1214692, CVE-2023-40217) --- packages/p/python/.files | Bin 4898 -> 4976 bytes packages/p/python/.rev | 13 + .../CVE-2023-40217-avoid-ssl-pre-close.patch | 330 ++++++++++++++++++ packages/p/python/python-base.changes | 8 + packages/p/python/python-base.spec | 3 + packages/p/python/python-doc.changes | 8 + packages/p/python/python-doc.spec | 3 + packages/p/python/python.changes | 8 + packages/p/python/python.spec | 3 + 9 files changed, 376 insertions(+) create mode 100644 packages/p/python/CVE-2023-40217-avoid-ssl-pre-close.patch diff --git a/packages/p/python/.files b/packages/p/python/.files index 805ffe9b82aeefc2e61f1791f4ab3a98e54efcc0..90640b87c3d166c3aee4bab25d48e2b65aaf2c23 100644 GIT binary patch delta 662 zcma)(J8aWH9LAI8S*Lk8j7W&7MHH92v*Y-96x2LQlk|Zqb!vsq=QAm)8iS*ju&}a5 z--?8UU_eaWK!OFq&WxZF8w?#QvEY;lb?o+EzQ^zTznngr&IMtgq_jjV7F!zrrnpjW zV{fX7{j!BEeF@)F%XpD%;ILfBr>u?r;^3*LEJbYJWP>mB$^^6w0kiM|xa( zj;kWKbHSkB?d=+_h-5O|6}$IUh?(>P-j+=?wGHGojt^1o$Jx&CO#=ssMSMv%@GifG z_k{&K*K8bTR*{YF&!uIw3iI|Ea?9gfuP5i^IQ4wIhF=r&Sj7$iHGEj4i= zSHp3(je4q%*Sdp;rFHx%Fg(fBu3E%&B^f=}cPRDkitS4!C1+AGpE)C(+_=>V$yaR! oKXXl!5z48ueOD02ViqSAUn#+va+U4c!i#jpQv;u9XA(F60&6SMg8%>k delta 531 zcmajbOKTHR00rPoCy&Wvl1ZzgD}#&dOlI!fdEB89cg~OMi)wx{-8WM?HIYbk@GEy4 zLG~)XCxck8<4fimnz>HJv;^fQu|nTxmdjn+i&#Xs7+GvYMC9RJ<~-hOHsbtC%0eLs zkEmnw5hbDPNoFz0ozM)W>#}fGWK0hBclO4YM|>)4(}@%~r#0a$ETOCSQBSR&-grg5 zfJcQ6zLt8pmkO|%U&L>t1*foq8_F7nY7^h|366`m@Khh*Pk9B0+7ce52N-G=o+W#@ zG)M8u=;A25NKTtLQx{LvHL=6TcjOfJOWU}ZWca8K(NDV=&-buvtl({~fhS6U*Yj;C zdM`d~V=LLN5JDp6SyED0a+3*L_zsg1GfiIz-}O;hIE$l#g`e6w-lW#?Iei^G)2LHo z5g8HU*dBF4$7GZ;CP-w@5~T3`87=>x_V3GVx`hvAg3aPh4CfB;yts_25#Vll17|XQ cJj|{^RaVg`d#IHL@p3o*MDbX)ajdld0;^)BRsaA1 diff --git a/packages/p/python/.rev b/packages/p/python/.rev index 87685a32845..3d4727b3223 100644 --- a/packages/p/python/.rev +++ b/packages/p/python/.rev @@ -4006,4 +4006,17 @@ However, upstream development now focuses on 3.x series. 1103620 + + c728d486614a6ba426b992578f5dd486 + 2.7.18 + + anag+factory + Forwarded request #1110536 from dgarcia + +- Add CVE-2023-40217-avoid-ssl-pre-close.patch fixing + gh#python/cpython#108310, backport from upstream patch + gh#python/cpython#108315 + (bsc#1214692, CVE-2023-40217) + 1110909 + diff --git a/packages/p/python/CVE-2023-40217-avoid-ssl-pre-close.patch b/packages/p/python/CVE-2023-40217-avoid-ssl-pre-close.patch new file mode 100644 index 00000000000..b601f889a62 --- /dev/null +++ b/packages/p/python/CVE-2023-40217-avoid-ssl-pre-close.patch @@ -0,0 +1,330 @@ +From f0c1e55dfd28970196768a6997a6dc0eab0f5259 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C5=81ukasz=20Langa?= +Date: Tue, 22 Aug 2023 17:39:17 +0200 +Subject: [PATCH] gh-108310: Fix CVE-2023-40217: Check for & avoid the ssl + pre-close flaw +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Instances of `ssl.SSLSocket` were vulnerable to a bypass of the TLS handshake +and included protections (like certificate verification) and treating sent +unencrypted data as if it were post-handshake TLS encrypted data. + +The vulnerability is caused when a socket is connected, data is sent by the +malicious peer and stored in a buffer, and then the malicious peer closes the +socket within a small timing window before the other peers’ TLS handshake can +begin. After this sequence of events the closed socket will not immediately +attempt a TLS handshake due to not being connected but will also allow the +buffered data to be read as if a successful TLS handshake had occurred. + +Co-Authored-By: Gregory P. Smith [Google LLC] +--- + Lib/ssl.py | 31 ++- + Lib/test/test_ssl.py | 215 ++++++++++++++++++ + ...-08-22-17-39-12.gh-issue-108310.fVM3sg.rst | 7 + + 3 files changed, 252 insertions(+), 1 deletion(-) + create mode 100644 Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst + +Index: Python-2.7.18/Lib/ssl.py +=================================================================== +--- Python-2.7.18.orig/Lib/ssl.py ++++ Python-2.7.18/Lib/ssl.py +@@ -576,10 +576,13 @@ class SSLSocket(socket): + "in client mode") + if self._context.check_hostname and not server_hostname: + raise ValueError("check_hostname requires server_hostname") ++ self._closed = False ++ self._sslobj = None + self.server_side = server_side + self.server_hostname = server_hostname + self.do_handshake_on_connect = do_handshake_on_connect + self.suppress_ragged_eofs = suppress_ragged_eofs ++ sock_timeout = sock.gettimeout() + + # See if we are connected + try: +@@ -588,11 +591,38 @@ class SSLSocket(socket): + if e.errno != errno.ENOTCONN: + raise + connected = False ++ blocking = self.gettimeout() == 0 ++ self.setblocking(False) ++ try: ++ # We are not connected so this is not supposed to block, but ++ # testing revealed otherwise on macOS and Windows so we do ++ # the non-blocking dance regardless. Our raise when any data ++ # is found means consuming the data is harmless. ++ notconn_pre_handshake_data = self.recv(1) ++ except socket_error as e: ++ # EINVAL occurs for recv(1) on non-connected on unix sockets. ++ if e.errno not in (errno.ENOTCONN, errno.EINVAL): ++ raise ++ notconn_pre_handshake_data = b'' ++ self.setblocking(blocking) ++ if notconn_pre_handshake_data: ++ # This prevents pending data sent to the socket before it was ++ # closed from escaping to the caller who could otherwise ++ # presume it came through a successful TLS connection. ++ reason = "Closed before TLS handshake with data in recv buffer." ++ notconn_pre_handshake_data_error = SSLError(e.errno, reason) ++ # Add the SSLError attributes that _ssl.c always adds. ++ notconn_pre_handshake_data_error.reason = reason ++ notconn_pre_handshake_data_error.library = None ++ try: ++ self.close() ++ except socket_error: ++ pass ++ raise notconn_pre_handshake_data_error + else: + connected = True + +- self._closed = False +- self._sslobj = None ++ self.settimeout(sock_timeout) # Must come after setblocking() calls. + self._connected = connected + if connected: + # create the SSL object +Index: Python-2.7.18/Lib/test/test_ssl.py +=================================================================== +--- Python-2.7.18.orig/Lib/test/test_ssl.py ++++ Python-2.7.18/Lib/test/test_ssl.py +@@ -20,6 +20,8 @@ import traceback + import weakref + import platform + import re ++import struct ++import httplib + import functools + from contextlib import closing + +@@ -3262,6 +3264,217 @@ else: + self.assertRaises(ValueError, s.write, b'hello') + + ++def set_socket_so_linger_on_with_zero_timeout(sock): ++ sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0)) ++ ++ ++class TestPreHandshakeClose(unittest.TestCase): ++ """Verify behavior of close sockets with received data before to the handshake. ++ """ ++ ++ class SingleConnectionTestServerThread(threading.Thread): ++ ++ def __init__(self, name=None, call_after_accept=None): ++ self.call_after_accept = call_after_accept ++ self.received_data = b'' # set by .run() ++ self.wrap_error = None # set by .run() ++ self.listener = None # set by .start() ++ self.port = None # set by .start() ++ super().__init__(name=name) ++ ++ def __enter__(self): ++ self.start() ++ return self ++ ++ def __exit__(self, *args): ++ try: ++ if self.listener: ++ self.listener.close() ++ except OSError: ++ pass ++ self.join() ++ self.wrap_error = None # avoid dangling references ++ ++ def start(self): ++ self.ssl_ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) ++ self.ssl_ctx.verify_mode = ssl.CERT_REQUIRED ++ self.ssl_ctx.load_verify_locations(cafile=ONLYCERT) ++ self.ssl_ctx.load_cert_chain(certfile=ONLYCERT, keyfile=ONLYKEY) ++ self.listener = socket.socket() ++ self.port = support.bind_port(self.listener) ++ self.listener.settimeout(2.0) ++ self.listener.listen(1) ++ super().start() ++ ++ def run(self): ++ conn, address = self.listener.accept() ++ self.listener.close() ++ with conn: ++ if self.call_after_accept(conn): ++ return ++ try: ++ tls_socket = self.ssl_ctx.wrap_socket(conn, server_side=True) ++ except OSError as err: # ssl.SSLError inherits from OSError ++ self.wrap_error = err ++ else: ++ try: ++ self.received_data = tls_socket.recv(400) ++ except OSError: ++ pass # closed, protocol error, etc. ++ ++ def non_linux_skip_if_other_okay_error(self, err): ++ if sys.platform == "linux": ++ return # Expect the full test setup to always work on Linux. ++ if (isinstance(err, ConnectionResetError) or ++ (isinstance(err, OSError) and err.errno == errno.EINVAL) or ++ re.search('wrong.version.number', getattr(err, "reason", ""), re.I)): ++ # On Windows the TCP RST leads to a ConnectionResetError ++ # (ECONNRESET) which Linux doesn't appear to surface to userspace. ++ # If wrap_socket() winds up on the "if connected:" path and doing ++ # the actual wrapping... we get an SSLError from OpenSSL. Typically ++ # WRONG_VERSION_NUMBER. While appropriate, neither is the scenario ++ # we're specifically trying to test. The way this test is written ++ # is known to work on Linux. We'll skip it anywhere else that it ++ # does not present as doing so. ++ self.skipTest("Could not recreate conditions on %s: %s" % (sys.platform, err)) ++ # If maintaining this conditional winds up being a problem. ++ # just turn this into an unconditional skip anything but Linux. ++ # The important thing is that our CI has the logic covered. ++ ++ def test_preauth_data_to_tls_server(self): ++ server_accept_called = threading.Event() ++ ready_for_server_wrap_socket = threading.Event() ++ ++ def call_after_accept(unused): ++ server_accept_called.set() ++ if not ready_for_server_wrap_socket.wait(2.0): ++ raise RuntimeError("wrap_socket event never set, test may fail.") ++ return False # Tell the server thread to continue. ++ ++ server = self.SingleConnectionTestServerThread( ++ call_after_accept=call_after_accept, ++ name="preauth_data_to_tls_server") ++ server.__enter__() # starts it ++ self.addCleanup(server.__exit__) # ... & unittest.TestCase stops it. ++ ++ with socket.socket() as client: ++ client.connect(server.listener.getsockname()) ++ # This forces an immediate connection close via RST on .close(). ++ set_socket_so_linger_on_with_zero_timeout(client) ++ client.setblocking(False) ++ ++ server_accept_called.wait() ++ client.send(b"DELETE /data HTTP/1.0\r\n\r\n") ++ client.close() # RST ++ ++ ready_for_server_wrap_socket.set() ++ server.join() ++ wrap_error = server.wrap_error ++ self.assertEqual(b"", server.received_data) ++ self.assertIsInstance(wrap_error, OSError) # All platforms. ++ self.non_linux_skip_if_other_okay_error(wrap_error) ++ self.assertIsInstance(wrap_error, ssl.SSLError) ++ self.assertIn("before TLS handshake with data", wrap_error.args[1]) ++ self.assertIn("before TLS handshake with data", wrap_error.reason) ++ self.assertNotEqual(0, wrap_error.args[0]) ++ self.assertIsNone(wrap_error.library, msg="attr must exist") ++ ++ def test_preauth_data_to_tls_client(self): ++ client_can_continue_with_wrap_socket = threading.Event() ++ ++ def call_after_accept(conn_to_client): ++ # This forces an immediate connection close via RST on .close(). ++ set_socket_so_linger_on_with_zero_timeout(conn_to_client) ++ conn_to_client.send( ++ b"HTTP/1.0 307 Temporary Redirect\r\n" ++ b"Location: https://example.com/someone-elses-server\r\n" ++ b"\r\n") ++ conn_to_client.close() # RST ++ client_can_continue_with_wrap_socket.set() ++ return True # Tell the server to stop. ++ ++ server = self.SingleConnectionTestServerThread( ++ call_after_accept=call_after_accept, ++ name="preauth_data_to_tls_client") ++ server.__enter__() # starts it ++ self.addCleanup(server.__exit__) # ... & unittest.TestCase stops it. ++ ++ # Redundant; call_after_accept sets SO_LINGER on the accepted conn. ++ set_socket_so_linger_on_with_zero_timeout(server.listener) ++ ++ with socket.socket() as client: ++ client.connect(server.listener.getsockname()) ++ if not client_can_continue_with_wrap_socket.wait(2.0): ++ self.fail("test server took too long.") ++ ssl_ctx = ssl.create_default_context() ++ try: ++ tls_client = ssl_ctx.wrap_socket( ++ client, server_hostname="localhost") ++ except OSError as err: # SSLError inherits from OSError ++ wrap_error = err ++ received_data = b"" ++ else: ++ wrap_error = None ++ received_data = tls_client.recv(400) ++ tls_client.close() ++ ++ server.join() ++ self.assertEqual(b"", received_data) ++ self.assertIsInstance(wrap_error, OSError) # All platforms. ++ self.non_linux_skip_if_other_okay_error(wrap_error) ++ self.assertIsInstance(wrap_error, ssl.SSLError) ++ self.assertIn("before TLS handshake with data", wrap_error.args[1]) ++ self.assertIn("before TLS handshake with data", wrap_error.reason) ++ self.assertNotEqual(0, wrap_error.args[0]) ++ self.assertIsNone(wrap_error.library, msg="attr must exist") ++ ++ def test_https_client_non_tls_response_ignored(self): ++ ++ server_responding = threading.Event() ++ ++ class SynchronizedHTTPSConnection(httplib.HTTPSConnection): ++ def connect(self): ++ httplib.HTTPConnection.connect(self) ++ # Wait for our fault injection server to have done its thing. ++ if not server_responding.wait(1.0) and support.verbose: ++ sys.stdout.write("server_responding event never set.") ++ self.sock = self._context.wrap_socket( ++ self.sock, server_hostname=self.host) ++ ++ def call_after_accept(conn_to_client): ++ # This forces an immediate connection close via RST on .close(). ++ set_socket_so_linger_on_with_zero_timeout(conn_to_client) ++ conn_to_client.send( ++ b"HTTP/1.0 402 Payment Required\r\n" ++ b"\r\n") ++ conn_to_client.close() # RST ++ server_responding.set() ++ return True # Tell the server to stop. ++ ++ server = self.SingleConnectionTestServerThread( ++ call_after_accept=call_after_accept, ++ name="non_tls_http_RST_responder") ++ server.__enter__() # starts it ++ self.addCleanup(server.__exit__) # ... & unittest.TestCase stops it. ++ # Redundant; call_after_accept sets SO_LINGER on the accepted conn. ++ set_socket_so_linger_on_with_zero_timeout(server.listener) ++ ++ connection = SynchronizedHTTPSConnection( ++ "localhost", ++ port=server.port, ++ context=ssl.create_default_context(), ++ timeout=2.0, ++ ) ++ # There are lots of reasons this raises as desired, long before this ++ # test was added. Sending the request requires a successful TLS wrapped ++ # socket; that fails if the connection is broken. It may seem pointless ++ # to test this. It serves as an illustration of something that we never ++ # want to happen... properly not happening. ++ with self.assertRaises(OSError) as err_ctx: ++ connection.request("HEAD", "/test", headers={"Host": "localhost"}) ++ response = connection.getresponse() ++ ++ + def test_main(verbose=False): + if support.verbose: + plats = { +Index: Python-2.7.18/Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst +=================================================================== +--- /dev/null ++++ Python-2.7.18/Misc/NEWS.d/next/Security/2023-08-22-17-39-12.gh-issue-108310.fVM3sg.rst +@@ -0,0 +1,7 @@ ++Fixed an issue where instances of :class:`ssl.SSLSocket` were vulnerable to ++a bypass of the TLS handshake and included protections (like certificate ++verification) and treating sent unencrypted data as if it were ++post-handshake TLS encrypted data. Security issue reported as ++`CVE-2023-40217 ++`_ by ++Aapo Oksman. Patch by Gregory P. Smith. diff --git a/packages/p/python/python-base.changes b/packages/p/python/python-base.changes index 1357f8818be..763709f6aeb 100644 --- a/packages/p/python/python-base.changes +++ b/packages/p/python/python-base.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Tue Sep 12 07:55:52 UTC 2023 - Daniel Garcia + +- Add CVE-2023-40217-avoid-ssl-pre-close.patch fixing + gh#python/cpython#108310, backport from upstream patch + gh#python/cpython#108315 + (bsc#1214692, CVE-2023-40217) + ------------------------------------------------------------------- Thu Aug 3 14:53:38 UTC 2023 - Matej Cepl diff --git a/packages/p/python/python-base.spec b/packages/p/python/python-base.spec index f82202c6df6..5d85f3389f5 100644 --- a/packages/p/python/python-base.spec +++ b/packages/p/python/python-base.spec @@ -156,6 +156,8 @@ Patch77: CVE-2023-27043-email-parsing-errors.patch # PATCH-FIX-UPSTREAM Revert-gh105127-left-tests.patch bsc#1210638 mcepl@suse.com # Partially revert previous patch Patch78: Revert-gh105127-left-tests.patch +# PATCH-FIX-UPSTREAM CVE-2023-40217-avoid-ssl-pre-close.patch gh#python/cpython#108315 +Patch79: CVE-2023-40217-avoid-ssl-pre-close.patch # COMMON-PATCH-END %define python_version %(echo %{tarversion} | head -c 3) BuildRequires: automake @@ -310,6 +312,7 @@ other applications. %patch76 -p1 %patch77 -p1 %patch78 -p1 +%patch79 -p1 # For patch 66 cp -v %{SOURCE66} Lib/test/recursion.tar diff --git a/packages/p/python/python-doc.changes b/packages/p/python/python-doc.changes index 1357f8818be..763709f6aeb 100644 --- a/packages/p/python/python-doc.changes +++ b/packages/p/python/python-doc.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Tue Sep 12 07:55:52 UTC 2023 - Daniel Garcia + +- Add CVE-2023-40217-avoid-ssl-pre-close.patch fixing + gh#python/cpython#108310, backport from upstream patch + gh#python/cpython#108315 + (bsc#1214692, CVE-2023-40217) + ------------------------------------------------------------------- Thu Aug 3 14:53:38 UTC 2023 - Matej Cepl diff --git a/packages/p/python/python-doc.spec b/packages/p/python/python-doc.spec index 8f46b2ee34f..b6454a029ae 100644 --- a/packages/p/python/python-doc.spec +++ b/packages/p/python/python-doc.spec @@ -155,6 +155,8 @@ Patch77: CVE-2023-27043-email-parsing-errors.patch # PATCH-FIX-UPSTREAM Revert-gh105127-left-tests.patch bsc#1210638 mcepl@suse.com # Partially revert previous patch Patch78: Revert-gh105127-left-tests.patch +# PATCH-FIX-UPSTREAM CVE-2023-40217-avoid-ssl-pre-close.patch gh#python/cpython#108315 +Patch79: CVE-2023-40217-avoid-ssl-pre-close.patch # COMMON-PATCH-END Provides: pyth_doc = %{version} Provides: pyth_ps = %{version} @@ -244,6 +246,7 @@ Python, and Macintosh Module Reference in PDF format. %patch76 -p1 %patch77 -p1 %patch78 -p1 +%patch79 -p1 # For patch 66 cp -v %{SOURCE66} Lib/test/recursion.tar diff --git a/packages/p/python/python.changes b/packages/p/python/python.changes index 1357f8818be..763709f6aeb 100644 --- a/packages/p/python/python.changes +++ b/packages/p/python/python.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Tue Sep 12 07:55:52 UTC 2023 - Daniel Garcia + +- Add CVE-2023-40217-avoid-ssl-pre-close.patch fixing + gh#python/cpython#108310, backport from upstream patch + gh#python/cpython#108315 + (bsc#1214692, CVE-2023-40217) + ------------------------------------------------------------------- Thu Aug 3 14:53:38 UTC 2023 - Matej Cepl diff --git a/packages/p/python/python.spec b/packages/p/python/python.spec index f7edf42ba59..8e73cd5082e 100644 --- a/packages/p/python/python.spec +++ b/packages/p/python/python.spec @@ -155,6 +155,8 @@ Patch77: CVE-2023-27043-email-parsing-errors.patch # PATCH-FIX-UPSTREAM Revert-gh105127-left-tests.patch bsc#1210638 mcepl@suse.com # Partially revert previous patch Patch78: Revert-gh105127-left-tests.patch +# PATCH-FIX-UPSTREAM CVE-2023-40217-avoid-ssl-pre-close.patch gh#python/cpython#108315 +Patch79: CVE-2023-40217-avoid-ssl-pre-close.patch # COMMON-PATCH-END BuildRequires: automake BuildRequires: db-devel @@ -364,6 +366,7 @@ that rely on earlier non-verification behavior. %patch76 -p1 %patch77 -p1 %patch78 -p1 +%patch79 -p1 # For patch 66 cp -v %{SOURCE66} Lib/test/recursion.tar