diff --git a/CVE-2023-46137.patch b/CVE-2023-46137.patch new file mode 100644 index 0000000000000000000000000000000000000000..8c37b9b4a867f02b49621bd8b5aea754aa696d25 --- /dev/null +++ b/CVE-2023-46137.patch @@ -0,0 +1,192 @@ +From 8d500550fdee4c55e3158f8d8c293c2dc1587869 Mon Sep 17 00:00:00 2001 +From: starlet-dx <15929766099@163.com> +Date: Fri, 29 Dec 2023 15:36:52 +0800 +Subject: [PATCH 1/1] 11976 stop processing pipelined HTTP/1.1 requests that are received together #11979 + +Origin: +https://github.com/twisted/twisted/commit/1e6e9d23cac59689760558dcb6634285e694b04c +--- + src/twisted/web/http.py | 32 +++++++-- + src/twisted/web/newsfragments/11976.bugfix | 7 ++ + src/twisted/web/test/test_web.py | 81 +++++++++++++++++++++- + 3 files changed, 114 insertions(+), 6 deletions(-) + create mode 100644 src/twisted/web/newsfragments/11976.bugfix + +diff --git a/src/twisted/web/http.py b/src/twisted/web/http.py +index b80a55a..23f8817 100644 +--- a/src/twisted/web/http.py ++++ b/src/twisted/web/http.py +@@ -2443,14 +2443,38 @@ class HTTPChannel(basic.LineReceiver, policies.TimeoutMixin): + + self._handlingRequest = True + ++ # We go into raw mode here even though we will be receiving lines next ++ # in the protocol; however, this data will be buffered and then passed ++ # back to line mode in the setLineMode call in requestDone. ++ self.setRawMode() ++ + req = self.requests[-1] + req.requestReceived(command, path, version) + +- def dataReceived(self, data): ++ def rawDataReceived(self, data: bytes) -> None: + """ +- Data was received from the network. Process it. ++ This is called when this HTTP/1.1 parser is in raw mode rather than ++ line mode. ++ ++ It may be in raw mode for one of two reasons: ++ ++ 1. All the headers of a request have been received and this ++ L{HTTPChannel} is currently receiving its body. ++ ++ 2. The full content of a request has been received and is currently ++ being processed asynchronously, and this L{HTTPChannel} is ++ buffering the data of all subsequent requests to be parsed ++ later. ++ ++ In the second state, the data will be played back later. ++ ++ @note: This isn't really a public API, and should be invoked only by ++ L{LineReceiver}'s line parsing logic. If you wish to drive an ++ L{HTTPChannel} from a custom data source, call C{dataReceived} on ++ it directly. ++ ++ @see: L{LineReceive.rawDataReceived} + """ +- # If we're currently handling a request, buffer this data. + if self._handlingRequest: + self._dataBuffer.append(data) + if ( +@@ -2462,9 +2486,7 @@ class HTTPChannel(basic.LineReceiver, policies.TimeoutMixin): + # ready. See docstring for _optimisticEagerReadSize above. + self._networkProducer.pauseProducing() + return +- return basic.LineReceiver.dataReceived(self, data) + +- def rawDataReceived(self, data): + self.resetTimeout() + + try: +diff --git a/src/twisted/web/newsfragments/11976.bugfix b/src/twisted/web/newsfragments/11976.bugfix +new file mode 100644 +index 0000000..8ac292b +--- /dev/null ++++ b/src/twisted/web/newsfragments/11976.bugfix +@@ -0,0 +1,7 @@ ++In Twisted 16.3.0, we changed twisted.web to stop dispatching HTTP/1.1 ++pipelined requests to application code. There was a bug in this change which ++still allowed clients which could send multiple full HTTP requests in a single ++TCP segment to trigger asynchronous processing of later requests, which could ++lead to out-of-order responses. This has now been corrected and twisted.web ++should never process a pipelined request over HTTP/1.1 until the previous ++request has fully completed. +diff --git a/src/twisted/web/test/test_web.py b/src/twisted/web/test/test_web.py +index 3eb35a9..b2b2ad7 100644 +--- a/src/twisted/web/test/test_web.py ++++ b/src/twisted/web/test/test_web.py +@@ -8,6 +8,7 @@ Tests for various parts of L{twisted.web}. + import os + import zlib + from io import BytesIO ++from typing import List + + from zope.interface import implementer + from zope.interface.verify import verifyObject +@@ -17,10 +18,13 @@ from twisted.internet.address import IPv4Address, IPv6Address + from twisted.internet.task import Clock + from twisted.logger import LogLevel, globalLogPublisher + from twisted.python import failure, reflect ++from twisted.python.compat import iterbytes + from twisted.python.filepath import FilePath +-from twisted.test.proto_helpers import EventLoggingObserver ++from twisted.test.proto_helpers import EventLoggingObserver, StringTransport + from twisted.trial import unittest + from twisted.web import error, http, iweb, resource, server ++from twisted.web.resource import Resource ++from twisted.web.server import NOT_DONE_YET, Request, Site + from twisted.web.static import Data + from twisted.web.test.requesthelper import DummyChannel, DummyRequest + from ._util import assertIsFilesystemTemporary +@@ -1849,3 +1853,78 @@ class ExplicitHTTPFactoryReactor(unittest.TestCase): + + factory = http.HTTPFactory() + self.assertIs(factory.reactor, reactor) ++ ++ ++class QueueResource(Resource): ++ """ ++ Add all requests to an internal queue, ++ without responding to the requests. ++ You can access the requests from the queue and handle their response. ++ """ ++ ++ isLeaf = True ++ ++ def __init__(self) -> None: ++ super().__init__() ++ self.dispatchedRequests: List[Request] = [] ++ ++ def render_GET(self, request: Request) -> int: ++ self.dispatchedRequests.append(request) ++ return NOT_DONE_YET ++ ++ ++class TestRFC9112Section932(unittest.TestCase): ++ """ ++ Verify that HTTP/1.1 request ordering is preserved. ++ """ ++ ++ def test_multipleRequestsInOneSegment(self) -> None: ++ """ ++ Twisted MUST NOT respond to a second HTTP/1.1 request while the first ++ is still pending. ++ """ ++ qr = QueueResource() ++ site = Site(qr) ++ proto = site.buildProtocol(None) ++ serverTransport = StringTransport() ++ proto.makeConnection(serverTransport) ++ proto.dataReceived( ++ b"GET /first HTTP/1.1\r\nHost: a\r\n\r\n" ++ b"GET /second HTTP/1.1\r\nHost: a\r\n\r\n" ++ ) ++ # The TCP data contains 2 requests, ++ # but only 1 request was dispatched, ++ # as the first request was not yet finalized. ++ self.assertEqual(len(qr.dispatchedRequests), 1) ++ # The first request is finalized and the ++ # second request is dispatched right away. ++ qr.dispatchedRequests[0].finish() ++ self.assertEqual(len(qr.dispatchedRequests), 2) ++ ++ def test_multipleRequestsInDifferentSegments(self) -> None: ++ """ ++ Twisted MUST NOT respond to a second HTTP/1.1 request while the first ++ is still pending, even if the second request is received in a separate ++ TCP package. ++ """ ++ qr = QueueResource() ++ site = Site(qr) ++ proto = site.buildProtocol(None) ++ serverTransport = StringTransport() ++ proto.makeConnection(serverTransport) ++ raw_data = ( ++ b"GET /first HTTP/1.1\r\nHost: a\r\n\r\n" ++ b"GET /second HTTP/1.1\r\nHost: a\r\n\r\n" ++ ) ++ # Just go byte by byte for the extreme case in which each byte is ++ # received in a separate TCP package. ++ for chunk in iterbytes(raw_data): ++ proto.dataReceived(chunk) ++ # The TCP data contains 2 requests, ++ # but only 1 request was dispatched, ++ # as the first request was not yet finalized. ++ self.assertEqual(len(qr.dispatchedRequests), 1) ++ # The first request is finalized and the ++ # second request is dispatched right away. ++ qr.dispatchedRequests[0].finish() ++ self.assertEqual(len(qr.dispatchedRequests), 2) +-- +2.30.0 + diff --git a/python-twisted.spec b/python-twisted.spec index a833af2e7f8b78874736e1fc640ad39268e0c2db..c0a1711b6d6f706a8e3685b2d7e71e793482c8b8 100644 --- a/python-twisted.spec +++ b/python-twisted.spec @@ -1,13 +1,15 @@ %define debug_package %{nil} Name: python-twisted Version: 22.4.0 -Release: 1 +Release: 2 Summary: An event-driven networking engine written in Python License: MIT URL: http://twistedmatrix.com/ Source0: https://files.pythonhosted.org/packages/source/T/Twisted/Twisted-%{version}.tar.gz # https://github.com/twisted/twisted/commit/f2f5e81c03f14e253e85fe457e646130780db40b Patch0: CVE-2022-39348.patch +# https://github.com/twisted/twisted/commit/1e6e9d23cac59689760558dcb6634285e694b04c +Patch1: CVE-2023-46137.patch %description Twisted is an event-based framework for internet applications, @@ -110,6 +112,9 @@ PATH=%{buildroot}%{_bindir}:$PATH PYTHONPATH=%{buildroot}%{python3_sitelib} %{bu %{_mandir}/man1/{cftp.1*,ckeygen.1*,conch.1*,mailmail.1*,pyhtmlizer.1*,tkconch.1*,trial.1*,twistd.1*} %changelog +* Fri Dec 29 2023 yaoxin - 22.4.0-2 +- Fix CVE-2023-46137 + * Fri Dec 08 2023 yaoxin - 22.4.0-1 - Upgrade to 22.4.0 for fix CVE-2022-21712,CVE-2022-21716 and CVE-2022-24801 - Fix CVE-2022-39348