diff --git a/0001-Implement-Unthreaded-Controller-256.patch b/0001-Implement-Unthreaded-Controller-256.patch index 78d36288c56cb649318ca0e2060206901a34c0a1..f3dc2b0914150460b29690d32f12eb386c8e0102 100644 --- a/0001-Implement-Unthreaded-Controller-256.patch +++ b/0001-Implement-Unthreaded-Controller-256.patch @@ -158,16 +158,6 @@ index 35dcb88..2c1bab7 100644 .. .. If you edit the above badges, don't forget to edit setup.cfg .. .. The |Discourse| badge MUST NOT be included in setup.cfg .. |Discourse| image:: https://img.shields.io/discourse/status?server=https%3A%2F%2Faio-libs.discourse.group%2F&style=social -diff --git a/aiosmtpd/__init__.py b/aiosmtpd/__init__.py -index 7d459d8..9c7b938 100644 ---- a/aiosmtpd/__init__.py -+++ b/aiosmtpd/__init__.py -@@ -1,4 +1,4 @@ - # Copyright 2014-2021 The aiosmtpd Developers - # SPDX-License-Identifier: Apache-2.0 - --__version__ = "1.4.2" -+__version__ = "1.5.0a1" diff --git a/aiosmtpd/controller.py b/aiosmtpd/controller.py index 2258c54..d3345b8 100644 --- a/aiosmtpd/controller.py diff --git a/0002-Code-Hygiene-259.patch b/0002-Code-Hygiene-259.patch index 5fddb26063316466c9b361906dd34c373ae73c0a..add742c64ab7a20842d10cff5aa7385059e3e576 100644 --- a/0002-Code-Hygiene-259.patch +++ b/0002-Code-Hygiene-259.patch @@ -66,16 +66,6 @@ index f7b0e32..ebc2248 100644 python -m flake8 aiosmtpd setup.py housekeep.py release.py - name: "Docs Checking" # language=bash -diff --git a/aiosmtpd/__init__.py b/aiosmtpd/__init__.py -index 9c7b938..e96d0ee 100644 ---- a/aiosmtpd/__init__.py -+++ b/aiosmtpd/__init__.py -@@ -1,4 +1,4 @@ - # Copyright 2014-2021 The aiosmtpd Developers - # SPDX-License-Identifier: Apache-2.0 - --__version__ = "1.5.0a1" -+__version__ = "1.5.0a2" diff --git a/aiosmtpd/controller.py b/aiosmtpd/controller.py index d3345b8..79bdbd0 100644 --- a/aiosmtpd/controller.py diff --git a/CVE-2024-27305.patch b/CVE-2024-27305.patch new file mode 100644 index 0000000000000000000000000000000000000000..c4adcec1800edf71b77d9c9c4c4fdcef0c663372 --- /dev/null +++ b/CVE-2024-27305.patch @@ -0,0 +1,160 @@ +From 24b6c79c8921cf1800e27ca144f4f37023982bbb Mon Sep 17 00:00:00 2001 +From: Login <84237895+The-Login@users.noreply.github.com> +Date: Sat, 2 Mar 2024 15:55:13 +0100 +Subject: [PATCH] Merge pull request from GHSA-pr2m-px7j-xg65 +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Origin: https://github.com/aio-libs/aiosmtpd/commit/24b6c79c8921cf1800e27ca144f4f37023982bbb + +* SMTP Smuggling Fix + +Adapted adherence to RFC 5321 ยง 2.3.8 to fix SMTP smuggling issues (https://www.rfc-editor.org/rfc/rfc5321#section-2.3.8) + +* Apply suggestions from code review + +Co-authored-by: Sam Bull + +* Add files via upload + +* Update test_smtpsmuggling.py + +--------- + +Co-authored-by: Sam Bull +--- + aiosmtpd/smtp.py | 11 ++-- + aiosmtpd/tests/test_smtpsmuggling.py | 79 ++++++++++++++++++++++++++++ + 2 files changed, 85 insertions(+), 5 deletions(-) + create mode 100644 aiosmtpd/tests/test_smtpsmuggling.py + +diff --git a/aiosmtpd/smtp.py b/aiosmtpd/smtp.py +index 39e70d8b..00902c1e 100644 +--- a/aiosmtpd/smtp.py ++++ b/aiosmtpd/smtp.py +@@ -87,7 +87,7 @@ class _DataState(enum.Enum): + EMPTY_BARR = bytearray() + EMPTYBYTES = b'' + MISSING = _Missing() +-NEWLINE = '\n' ++NEWLINE = '\r\n' + VALID_AUTHMECH = re.compile(r"[A-Z0-9_-]+\Z") + + # https://tools.ietf.org/html/rfc3207.html#page-3 +@@ -1427,9 +1427,10 @@ async def smtp_DATA(self, arg: str) -> None: + # Since eof_received cancels this coroutine, + # readuntil() can never raise asyncio.IncompleteReadError. + try: +- line: bytes = await self._reader.readuntil() ++ # https://datatracker.ietf.org/doc/html/rfc5321#section-2.3.8 ++ line: bytes = await self._reader.readuntil(b'\r\n') + log.debug('DATA readline: %s', line) +- assert line.endswith(b'\n') ++ assert line.endswith(b'\r\n') + except asyncio.CancelledError: + # The connection got reset during the DATA command. + log.info('Connection lost during DATA') +@@ -1446,7 +1447,7 @@ async def smtp_DATA(self, arg: str) -> None: + data *= 0 + # Drain the stream anyways + line = await self._reader.read(e.consumed) +- assert not line.endswith(b'\n') ++ assert not line.endswith(b'\r\n') + # A lone dot in a line signals the end of DATA. + if not line_fragments and line == b'.\r\n': + break +@@ -1458,7 +1459,7 @@ async def smtp_DATA(self, arg: str) -> None: + # Discard data immediately to prevent memory pressure + data *= 0 + line_fragments.append(line) +- if line.endswith(b'\n'): ++ if line.endswith(b'\r\n'): + # Record data only if state is "NOMINAL" + if state == _DataState.NOMINAL: + line = EMPTY_BARR.join(line_fragments) +diff --git a/aiosmtpd/tests/test_smtpsmuggling.py b/aiosmtpd/tests/test_smtpsmuggling.py +new file mode 100644 +index 00000000..b5d37851 +--- /dev/null ++++ b/aiosmtpd/tests/test_smtpsmuggling.py +@@ -0,0 +1,79 @@ ++# Copyright 2014-2021 The aiosmtpd Developers ++# SPDX-License-Identifier: Apache-2.0 ++ ++"""Test SMTP smuggling.""" ++ ++from email.mime.text import MIMEText ++from smtplib import SMTP, SMTP_SSL ++from typing import Generator, Union ++ ++import pytest ++import smtplib ++ ++from aiosmtpd.controller import Controller ++from aiosmtpd.testing.helpers import ReceivingHandler ++from aiosmtpd.testing.statuscodes import SMTP_STATUS_CODES as S ++ ++from aiosmtpd.smtp import SMTP as Server ++from aiosmtpd.smtp import Session as ServerSession ++from aiosmtpd.smtp import Envelope ++ ++from .conftest import Global, controller_data, handler_data ++ ++from aiosmtpd.testing.helpers import ( ++ ReceivingHandler ++) ++ ++def new_data(self, msg): ++ self.putcmd("data") ++ ++ (code, repl) = self.getreply() ++ if self.debuglevel > 0: ++ self._print_debug('data:', (code, repl)) ++ if code != 354: ++ raise SMTPDataError(code, repl) ++ else: ++ ##### Patching input encoding so we can send raw messages ++ #if isinstance(msg, str): ++ # msg = smtplib._fix_eols(msg).encode('ascii') ++ #q = smtplib._quote_periods(msg) ++ #if q[-2:] != smtplib.bCRLF: ++ # q = q + smtplib.bCRLF ++ #q = q + b"." + smtplib.bCRLF ++ q = msg ++ self.send(q) ++ (code, msg) = self.getreply() ++ if self.debuglevel > 0: ++ self._print_debug('data:', (code, msg)) ++ return (code, msg) ++ ++def return_unchanged(data): ++ return data ++ ++class TestSmuggling: ++ @handler_data(class_=ReceivingHandler) ++ def test_smtp_smuggling(self, plain_controller, client): ++ smtplib._fix_eols = return_unchanged ++ smtplib._quote_periods = return_unchanged ++ smtplib.SMTP.data = new_data ++ ++ handler = plain_controller.handler ++ sender = "sender@example.com" ++ recipients = ["rcpt1@example.com"] ++ resp = client.helo("example.com") ++ assert resp == S.S250_FQDN ++ # Trying SMTP smuggling with a fake \n.\r\n end-of-data sequence. ++ message_data = b"""\ ++From: Anne Person \r\n\ ++To: Bart Person \r\n\ ++Subject: A test\r\n\ ++Message-ID: \r\n\ ++\r\n\ ++Testing\ ++\n.\r\n\ ++NO SMUGGLING ++\r\n.\r\n\ ++""" ++ results = client.sendmail(sender, recipients, message_data) ++ client.quit() ++ assert b"NO SMUGGLING" in handler.box[0].content diff --git a/python-aiosmtpd.spec b/python-aiosmtpd.spec index a664e4da818cbc3b3f094826029ecc97dd9a86f5..1bd6b964bb2c9e723f4fcab5aca57526531afe70 100644 --- a/python-aiosmtpd.spec +++ b/python-aiosmtpd.spec @@ -1,7 +1,7 @@ %global _empty_manifest_terminate_build 0 Name: python-aiosmtpd Version: 1.4.2 -Release: 1 +Release: 2 Summary: aiosmtpd - asyncio based SMTP server License: Apache 2.0 URL: https://github.com/aio-libs/aiosmtpd @@ -11,6 +11,7 @@ Patch0002: 0002-Code-Hygiene-259.patch Patch0003: 0003-URGENT-Fix-RTD-docs-gen.patch Patch0004: 0004-Make-Sphinx-RTD-deps-SSOT.patch Patch0005: %{url}/pull/284.patch +Patch0006: CVE-2024-27305.patch BuildArch: noarch @@ -78,6 +79,9 @@ mv %{buildroot}/doclist.lst . %{_pkgdocdir} %changelog +* Wed Mar 13 2024 wangkai <13474090681@163.com> - 1.4.2-2 +- Fix CVE-2024-27305 + * Wed Oct 12 2022 Ge Wang - 1.4.2-1 - Upgrade to version 1.4.2