diff --git a/CVE-2024-27305.patch b/CVE-2024-27305.patch new file mode 100644 index 0000000..c4adcec --- /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 90cc4a2..704b4a5 100644 --- a/python-aiosmtpd.spec +++ b/python-aiosmtpd.spec @@ -1,11 +1,12 @@ %global _empty_manifest_terminate_build 0 Name: python-aiosmtpd Version: 1.4.4 -Release: 1 +Release: 2 Summary: aiosmtpd - asyncio based SMTP server License: Apache 2.0 URL: https://github.com/aio-libs/aiosmtpd Source0: https://github.com/aio-libs/aiosmtpd/archive/%{version}.tar.gz#/aiosmtpd-%{version}.tar.gz +Patch0: CVE-2024-27305.patch BuildArch: noarch @@ -31,7 +32,7 @@ Provides: python3-aiosmtpd-doc Development documents and examples for aiosmtpd. %prep -%autosetup -n aiosmtpd-%{version} +%autosetup -n aiosmtpd-%{version} -p1 %build %py3_build @@ -71,6 +72,9 @@ mv %{buildroot}/doclist.lst . %{_pkgdocdir} %changelog +* Wed Mar 13 2024 wangkai <13474090681@163.com> - 1.4.4-2 +- Fix CVE-2024-27305 + * Mon Mar 20 2023 jiangxinyu - 1.4.4-1 - Update package to version 1.4.4