Your message dated Tue, 11 May 2021 19:04:08 +0000
with message-id <e1lgxfs-0000bt...@respighi.debian.org>
and subject line unblock python-eventlet
has caused the Debian Bug report #988357,
regarding unblock: python-eventlet/0.26.1-7 CVE-2021-21419
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact ow...@bugs.debian.org
immediately.)


-- 
988357: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=988357
Debian Bug Tracking System
Contact ow...@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian....@packages.debian.org
Usertags: unblock

Please unblock package python-eventlet

[ Reason ]
CVE-2021-21419

[ Impact ]
Malicious peer may exhaust memory on Eventlet side by sending
highly compressed data frame.

[ Tests ]
The Eventlet package contains its own test suite.

[ Risks ]
Regression? Hopefully not. The affected code is only in the
websocket.py file.

[ Checklist ]
  [x] all changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in testing

Please unblock python-eventlet/0.26.1-7

Cheers,

Thomas Goirand (zigo)
diff -Nru python-eventlet-0.26.1/debian/changelog 
python-eventlet-0.26.1/debian/changelog
--- python-eventlet-0.26.1/debian/changelog     2021-02-18 17:07:30.000000000 
+0100
+++ python-eventlet-0.26.1/debian/changelog     2021-05-11 08:03:43.000000000 
+0200
@@ -1,3 +1,11 @@
+python-eventlet (0.26.1-7) unstable; urgency=medium
+
+  * CVE-2021-21419: Malicious peer may exhaust memory on Eventlet side
+    by sending highly compressed data frame. Appled upstream patch: websocket:
+    Limit maximum uncompressed frame length to 8MiB (Closes: #988342).
+
+ -- Thomas Goirand <z...@debian.org>  Tue, 11 May 2021 08:03:43 +0200
+
 python-eventlet (0.26.1-6) unstable; urgency=medium
 
   * Hack a modified debian/greendns.py with filename=None instead of
diff -Nru 
python-eventlet-0.26.1/debian/patches/CVE-2021-21419_websocket-Limit-maximum-uncompressed-frame-length-to-8MiB.patch
 
python-eventlet-0.26.1/debian/patches/CVE-2021-21419_websocket-Limit-maximum-uncompressed-frame-length-to-8MiB.patch
--- 
python-eventlet-0.26.1/debian/patches/CVE-2021-21419_websocket-Limit-maximum-uncompressed-frame-length-to-8MiB.patch
        1970-01-01 01:00:00.000000000 +0100
+++ 
python-eventlet-0.26.1/debian/patches/CVE-2021-21419_websocket-Limit-maximum-uncompressed-frame-length-to-8MiB.patch
        2021-05-11 08:03:43.000000000 +0200
@@ -0,0 +1,203 @@
+Description: CVE-2021-21419: 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
+Author: Onno Kortmann <o...@gmx.net>
+Date: Thu, 1 Apr 2021 16:15:47 +0200
+Origin: 
https://github.com/eventlet/eventlet/commit/1412f5e4125b4313f815778a1acb4d3336efcd07.patch
+Bug-Debian: https://bugs.debian.org/988342
+Last-Update: 2021-05-11
+
+Index: python-eventlet/eventlet/websocket.py
+===================================================================
+--- python-eventlet.orig/eventlet/websocket.py
++++ python-eventlet/eventlet/websocket.py
+@@ -38,6 +38,7 @@ for _mod in ('wsaccel.utf8validator', 'a
+         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 @@ class WebSocketWSGI(object):
+     :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,
+@@ -323,7 +330,8 @@ class WebSocketWSGI(object):
+         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):
+         """
+@@ -502,7 +510,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
+@@ -511,6 +520,8 @@ class RFC6455WebSocket(WebSocket):
+ 
+         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):
+@@ -582,12 +593,13 @@ class RFC6455WebSocket(WebSocket):
+         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
+@@ -596,7 +608,12 @@ class RFC6455WebSocket(WebSocket):
+         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
+@@ -610,6 +627,7 @@ class RFC6455WebSocket(WebSocket):
+ 
+     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:
+@@ -709,13 +727,17 @@ class RFC6455WebSocket(WebSocket):
+             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:
+Index: python-eventlet/tests/websocket_new_test.py
+===================================================================
+--- python-eventlet.orig/tests/websocket_new_test.py
++++ python-eventlet/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 @@ class TestWebSocketWithCompression(tests
+ 
+         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)
diff -Nru python-eventlet-0.26.1/debian/patches/series 
python-eventlet-0.26.1/debian/patches/series
--- python-eventlet-0.26.1/debian/patches/series        2021-02-18 
17:07:30.000000000 +0100
+++ python-eventlet-0.26.1/debian/patches/series        2021-05-11 
08:03:43.000000000 +0200
@@ -13,3 +13,4 @@
 0017-py39-Add-_at_fork_reinit-method-to-Semaphores.patch
 0018-pyopenssl-tsafe-module-was-deprecated-and-removed-in.patch
 fix-infinte-recursion-in-ssl.patch
+CVE-2021-21419_websocket-Limit-maximum-uncompressed-frame-length-to-8MiB.patch

--- End Message ---
--- Begin Message ---
Unblocked.

--- End Message ---

Reply via email to