!26 [sync] PR-24: Fix CVE-2023-5625 (Patch for round CVE-2021-21419)
From: @openeuler-sync-bot Reviewed-by: @cherry530 Signed-off-by: @cherry530
This commit is contained in:
commit
01aa36d346
207
CVE-2021-21419.patch
Normal file
207
CVE-2021-21419.patch
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
From 1412f5e4125b4313f815778a1acb4d3336efcd07 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Onno Kortmann <onno@gmx.net>
|
||||||
|
Date: Thu, 1 Apr 2021 16:15:47 +0200
|
||||||
|
Subject: [PATCH] websocket: Limit maximum uncompressed frame length to 8MiB
|
||||||
|
|
||||||
|
This fixes a memory exhaustion DOS attack vector.
|
||||||
|
|
||||||
|
References: GHSA-9p9m-jm8w-94p2
|
||||||
|
https://github.com/eventlet/eventlet/security/advisories/GHSA-9p9m-jm8w-94p2
|
||||||
|
---
|
||||||
|
eventlet/websocket.py | 34 +++++++++++++++++----
|
||||||
|
tests/websocket_new_test.py | 59 ++++++++++++++++++++++++++++++++++++-
|
||||||
|
2 files changed, 86 insertions(+), 7 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/eventlet/websocket.py b/eventlet/websocket.py
|
||||||
|
index 2222b4ba65..245993d55b 100644
|
||||||
|
--- a/eventlet/websocket.py
|
||||||
|
+++ b/eventlet/websocket.py
|
||||||
|
@@ -38,6 +38,7 @@
|
||||||
|
break
|
||||||
|
|
||||||
|
ACCEPTABLE_CLIENT_ERRORS = set((errno.ECONNRESET, errno.EPIPE))
|
||||||
|
+DEFAULT_MAX_FRAME_LENGTH = 8 << 20
|
||||||
|
|
||||||
|
__all__ = ["WebSocketWSGI", "WebSocket"]
|
||||||
|
PROTOCOL_GUID = b'258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
|
||||||
|
@@ -75,14 +76,20 @@ def my_handler(ws):
|
||||||
|
:class:`WebSocket`. To close the socket, simply return from the
|
||||||
|
function. Note that the server will log the websocket request at
|
||||||
|
the time of closure.
|
||||||
|
+
|
||||||
|
+ An optional argument max_frame_length can be given, which will set the
|
||||||
|
+ maximum incoming *uncompressed* payload length of a frame. By default, this
|
||||||
|
+ is set to 8MiB. Note that excessive values here might create a DOS attack
|
||||||
|
+ vector.
|
||||||
|
"""
|
||||||
|
|
||||||
|
- def __init__(self, handler):
|
||||||
|
+ def __init__(self, handler, max_frame_length=DEFAULT_MAX_FRAME_LENGTH):
|
||||||
|
self.handler = handler
|
||||||
|
self.protocol_version = None
|
||||||
|
self.support_legacy_versions = True
|
||||||
|
self.supported_protocols = []
|
||||||
|
self.origin_checker = None
|
||||||
|
+ self.max_frame_length = max_frame_length
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def configured(cls,
|
||||||
|
@@ -324,7 +331,8 @@ def _handle_hybi_request(self, environ):
|
||||||
|
sock.sendall(b'\r\n'.join(handshake_reply) + b'\r\n\r\n')
|
||||||
|
return RFC6455WebSocket(sock, environ, self.protocol_version,
|
||||||
|
protocol=negotiated_protocol,
|
||||||
|
- extensions=parsed_extensions)
|
||||||
|
+ extensions=parsed_extensions,
|
||||||
|
+ max_frame_length=self.max_frame_length)
|
||||||
|
|
||||||
|
def _extract_number(self, value):
|
||||||
|
"""
|
||||||
|
@@ -503,7 +511,8 @@ class ProtocolError(ValueError):
|
||||||
|
|
||||||
|
|
||||||
|
class RFC6455WebSocket(WebSocket):
|
||||||
|
- def __init__(self, sock, environ, version=13, protocol=None, client=False, extensions=None):
|
||||||
|
+ def __init__(self, sock, environ, version=13, protocol=None, client=False, extensions=None,
|
||||||
|
+ max_frame_length=DEFAULT_MAX_FRAME_LENGTH):
|
||||||
|
super(RFC6455WebSocket, self).__init__(sock, environ, version)
|
||||||
|
self.iterator = self._iter_frames()
|
||||||
|
self.client = client
|
||||||
|
@@ -512,6 +521,8 @@ def __init__(self, sock, environ, version=13, protocol=None, client=False, exten
|
||||||
|
|
||||||
|
self._deflate_enc = None
|
||||||
|
self._deflate_dec = None
|
||||||
|
+ self.max_frame_length = max_frame_length
|
||||||
|
+ self._remote_close_data = None
|
||||||
|
|
||||||
|
class UTF8Decoder(object):
|
||||||
|
def __init__(self):
|
||||||
|
@@ -583,12 +594,13 @@ def _get_bytes(self, numbytes):
|
||||||
|
return data
|
||||||
|
|
||||||
|
class Message(object):
|
||||||
|
- def __init__(self, opcode, decoder=None, decompressor=None):
|
||||||
|
+ def __init__(self, opcode, max_frame_length, decoder=None, decompressor=None):
|
||||||
|
self.decoder = decoder
|
||||||
|
self.data = []
|
||||||
|
self.finished = False
|
||||||
|
self.opcode = opcode
|
||||||
|
self.decompressor = decompressor
|
||||||
|
+ self.max_frame_length = max_frame_length
|
||||||
|
|
||||||
|
def push(self, data, final=False):
|
||||||
|
self.finished = final
|
||||||
|
@@ -597,7 +609,12 @@ def push(self, data, final=False):
|
||||||
|
def getvalue(self):
|
||||||
|
data = b"".join(self.data)
|
||||||
|
if not self.opcode & 8 and self.decompressor:
|
||||||
|
- data = self.decompressor.decompress(data + b'\x00\x00\xff\xff')
|
||||||
|
+ data = self.decompressor.decompress(data + b"\x00\x00\xff\xff", self.max_frame_length)
|
||||||
|
+ if self.decompressor.unconsumed_tail:
|
||||||
|
+ raise FailedConnectionError(
|
||||||
|
+ 1009,
|
||||||
|
+ "Incoming compressed frame exceeds length limit of {} bytes.".format(self.max_frame_length))
|
||||||
|
+
|
||||||
|
if self.decoder:
|
||||||
|
data = self.decoder.decode(data, self.finished)
|
||||||
|
return data
|
||||||
|
@@ -611,6 +628,7 @@ def _apply_mask(data, mask, length=None, offset=0):
|
||||||
|
|
||||||
|
def _handle_control_frame(self, opcode, data):
|
||||||
|
if opcode == 8: # connection close
|
||||||
|
+ self._remote_close_data = data
|
||||||
|
if not data:
|
||||||
|
status = 1000
|
||||||
|
elif len(data) > 1:
|
||||||
|
@@ -710,13 +728,17 @@ def _recv_frame(self, message=None):
|
||||||
|
length = struct.unpack('!H', recv(2))[0]
|
||||||
|
elif length == 127:
|
||||||
|
length = struct.unpack('!Q', recv(8))[0]
|
||||||
|
+
|
||||||
|
+ if length > self.max_frame_length:
|
||||||
|
+ raise FailedConnectionError(1009, "Incoming frame of {} bytes is above length limit of {} bytes.".format(
|
||||||
|
+ length, self.max_frame_length))
|
||||||
|
if masked:
|
||||||
|
mask = struct.unpack('!BBBB', recv(4))
|
||||||
|
received = 0
|
||||||
|
if not message or opcode & 8:
|
||||||
|
decoder = self.UTF8Decoder() if opcode == 1 else None
|
||||||
|
decompressor = self._get_permessage_deflate_dec(rsv1)
|
||||||
|
- message = self.Message(opcode, decoder=decoder, decompressor=decompressor)
|
||||||
|
+ message = self.Message(opcode, self.max_frame_length, decoder=decoder, decompressor=decompressor)
|
||||||
|
if not length:
|
||||||
|
message.push(b'', final=finished)
|
||||||
|
else:
|
||||||
|
diff --git a/tests/websocket_new_test.py b/tests/websocket_new_test.py
|
||||||
|
index 5f98025ec7..cc857924fe 100644
|
||||||
|
--- a/tests/websocket_new_test.py
|
||||||
|
+++ b/tests/websocket_new_test.py
|
||||||
|
@@ -30,7 +30,12 @@ def handle(ws):
|
||||||
|
else:
|
||||||
|
ws.close()
|
||||||
|
|
||||||
|
-wsapp = websocket.WebSocketWSGI(handle)
|
||||||
|
+
|
||||||
|
+# Set a lower limit of DEFAULT_MAX_FRAME_LENGTH for testing, as
|
||||||
|
+# sending an 8MiB frame over the loopback interface can trigger a
|
||||||
|
+# timeout.
|
||||||
|
+TEST_MAX_FRAME_LENGTH = 50000
|
||||||
|
+wsapp = websocket.WebSocketWSGI(handle, max_frame_length=TEST_MAX_FRAME_LENGTH)
|
||||||
|
|
||||||
|
|
||||||
|
class TestWebSocket(tests.wsgi_test._TestBase):
|
||||||
|
@@ -534,3 +539,55 @@ def test_compressed_send_recv_both_no_context_13(self):
|
||||||
|
|
||||||
|
ws.close()
|
||||||
|
eventlet.sleep(0.01)
|
||||||
|
+
|
||||||
|
+ def test_large_frame_size_compressed_13(self):
|
||||||
|
+ # Test fix for GHSA-9p9m-jm8w-94p2
|
||||||
|
+ extensions_string = 'permessage-deflate'
|
||||||
|
+ extensions = {'permessage-deflate': {
|
||||||
|
+ 'client_no_context_takeover': False,
|
||||||
|
+ 'server_no_context_takeover': False}}
|
||||||
|
+
|
||||||
|
+ sock = eventlet.connect(self.server_addr)
|
||||||
|
+ sock.sendall(six.b(self.connect % extensions_string))
|
||||||
|
+ sock.recv(1024)
|
||||||
|
+ ws = websocket.RFC6455WebSocket(sock, {}, client=True, extensions=extensions)
|
||||||
|
+
|
||||||
|
+ should_still_fit = b"x" * TEST_MAX_FRAME_LENGTH
|
||||||
|
+ one_too_much = should_still_fit + b"x"
|
||||||
|
+
|
||||||
|
+ # send just fitting frame twice to make sure they are fine independently
|
||||||
|
+ ws.send(should_still_fit)
|
||||||
|
+ assert ws.wait() == should_still_fit
|
||||||
|
+ ws.send(should_still_fit)
|
||||||
|
+ assert ws.wait() == should_still_fit
|
||||||
|
+ ws.send(one_too_much)
|
||||||
|
+
|
||||||
|
+ res = ws.wait()
|
||||||
|
+ assert res is None # socket closed
|
||||||
|
+ # TODO: The websocket currently sents compressed control frames, which contradicts RFC7692.
|
||||||
|
+ # Renable the following assert after that has been fixed.
|
||||||
|
+ # assert ws._remote_close_data == b"\x03\xf1Incoming compressed frame is above length limit."
|
||||||
|
+ eventlet.sleep(0.01)
|
||||||
|
+
|
||||||
|
+ def test_large_frame_size_uncompressed_13(self):
|
||||||
|
+ # Test fix for GHSA-9p9m-jm8w-94p2
|
||||||
|
+ sock = eventlet.connect(self.server_addr)
|
||||||
|
+ sock.sendall(six.b(self.connect))
|
||||||
|
+ sock.recv(1024)
|
||||||
|
+ ws = websocket.RFC6455WebSocket(sock, {}, client=True)
|
||||||
|
+
|
||||||
|
+ should_still_fit = b"x" * TEST_MAX_FRAME_LENGTH
|
||||||
|
+ one_too_much = should_still_fit + b"x"
|
||||||
|
+
|
||||||
|
+ # send just fitting frame twice to make sure they are fine independently
|
||||||
|
+ ws.send(should_still_fit)
|
||||||
|
+ assert ws.wait() == should_still_fit
|
||||||
|
+ ws.send(should_still_fit)
|
||||||
|
+ assert ws.wait() == should_still_fit
|
||||||
|
+ ws.send(one_too_much)
|
||||||
|
+
|
||||||
|
+ res = ws.wait()
|
||||||
|
+ assert res is None # socket closed
|
||||||
|
+ # close code should be available now
|
||||||
|
+ assert ws._remote_close_data == b"\x03\xf1Incoming frame of 50001 bytes is above length limit of 50000 bytes."
|
||||||
|
+ eventlet.sleep(0.01)
|
||||||
@ -1,11 +1,13 @@
|
|||||||
%global _empty_manifest_terminate_build 0
|
%global _empty_manifest_terminate_build 0
|
||||||
Name: python-eventlet
|
Name: python-eventlet
|
||||||
Version: 0.30.2
|
Version: 0.30.2
|
||||||
Release: 1
|
Release: 2
|
||||||
Summary: Highly concurrent networking library
|
Summary: Highly concurrent networking library
|
||||||
License: MIT License
|
License: MIT License
|
||||||
URL: http://eventlet.net
|
URL: http://eventlet.net
|
||||||
Source0: https://files.pythonhosted.org/packages/23/db/8ff5a9dec5ff016d5836254b676d507c2180d8838d7e545277d938896913/eventlet-0.30.2.tar.gz
|
Source0: https://files.pythonhosted.org/packages/23/db/8ff5a9dec5ff016d5836254b676d507c2180d8838d7e545277d938896913/eventlet-0.30.2.tar.gz
|
||||||
|
# https://github.com/eventlet/eventlet/commit/1412f5e4125b4313f815778a1acb4d3336efcd07
|
||||||
|
Patch0: CVE-2021-21419.patch
|
||||||
BuildArch: noarch
|
BuildArch: noarch
|
||||||
|
|
||||||
%description
|
%description
|
||||||
@ -40,7 +42,7 @@ Provides: python3-eventlet-doc
|
|||||||
Eventlet is a concurrent networking library for Python that allows you to change how you run your code, not how you write it.
|
Eventlet is a concurrent networking library for Python that allows you to change how you run your code, not how you write it.
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
%autosetup -n eventlet-0.30.2 -S git
|
%autosetup -n eventlet-0.30.2 -p1
|
||||||
|
|
||||||
%build
|
%build
|
||||||
%py3_build
|
%py3_build
|
||||||
@ -83,6 +85,9 @@ mv %{buildroot}/doclist.lst .
|
|||||||
%{_docdir}/*
|
%{_docdir}/*
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Mon Oct 23 2023 yaoxin <yao_xin001@hoperun.com> - 0.30.2-2
|
||||||
|
- Fix CVE-2023-5625 (Patch for round CVE-2021-21419)
|
||||||
|
|
||||||
* Mon Jul 26 2021 OpenStack_SIG <openstack@openeuler.org> - 0.30.2-1
|
* Mon Jul 26 2021 OpenStack_SIG <openstack@openeuler.org> - 0.30.2-1
|
||||||
- update to 0.30.2
|
- update to 0.30.2
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user