IMAP: Check buf size before pushing escape sequences within quoted literals

This commit is contained in:
mdecimus
2025-09-13 20:13:25 +02:00
parent 058208992a
commit a8e631e881
3 changed files with 14 additions and 11 deletions

View File

@@ -26,9 +26,9 @@ If you are upgrading from v0.11.x or v0.12.x, this version includes **breaking c
- IMAP: Include `administer` permission in ACL responses.
- IMAP: Add owner rights to ACL get responses.
- IMAP: Do not auto-train Bayes when moving messages from Junk to Trash.
- IMAP/ManageSieve: Increase maximum quoted argument size (fixes #2039).
- CalDAV: Limit recurrence expansions in calendar reports.
- WebDAV: Do not fix percent encoding on WebDAV FS (closes #2036).
- IMAP/ManageSieve: Increase maximum quoted argument size (#2039).
- CalDAV: Limit recurrence expansions in calendar reports ([CVE-2025-59045](https://github.com/stalwartlabs/stalwart/security/advisories/GHSA-xv4r-q6gr-6pfg)).
- WebDAV: Do not fix percent encoding on WebDAV FS (#2036).
## [0.13.2] - 2025-07-28

View File

@@ -4,11 +4,11 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use super::{ResponseCode, ResponseType};
use compact_str::{CompactString, format_compact};
use std::fmt::Display;
use compact_str::{CompactString, format_compact};
use super::{ResponseCode, ResponseType};
const QUOTED_ARG_MAX_LEN: usize = 4096;
#[derive(Debug, Clone)]
pub enum Error {
@@ -277,7 +277,7 @@ impl<T: CommandParser> Receiver<T> {
if !escaped {
self.push_argument(true)?;
self.state = State::Argument { last_ch: b' ' };
} else if self.buf.len() < 4096 {
} else if self.buf.len() < QUOTED_ARG_MAX_LEN {
self.buf.push(ch);
self.state = State::ArgumentQuoted { escaped: false };
} else {
@@ -286,7 +286,11 @@ impl<T: CommandParser> Receiver<T> {
}
b'\\' => {
if escaped {
self.buf.push(ch);
if self.buf.len() < QUOTED_ARG_MAX_LEN {
self.buf.push(ch);
} else {
return Err(self.error_reset("Quoted argument too long."));
}
}
self.state = State::ArgumentQuoted { escaped: !escaped };
}
@@ -294,7 +298,7 @@ impl<T: CommandParser> Receiver<T> {
return Err(self.error_reset("Unterminated quoted argument."));
}
_ => {
if self.buf.len() < 4096 {
if self.buf.len() < QUOTED_ARG_MAX_LEN {
if escaped {
self.buf.push(b'\\');
}
@@ -340,7 +344,6 @@ impl<T: CommandParser> Receiver<T> {
self.buf.push(ch);
} else {
// Digit found after non-sync '+' flag
return Err(self.error_reset("Invalid literal."));
}
}

View File

@@ -40,7 +40,7 @@ async fn limits() {
// Exceed max line length
let mut session = Session::test_with_shutdown(TestSMTP::from_core(core).server, rx);
session.data.remote_ip_str = "10.0.0.1".into();
let mut buf = vec![b'A'; 2049];
let mut buf = vec![b'A'; 4097];
session.ingest(&buf).await.unwrap();
session.ingest(b"\r\n").await.unwrap();
session.response().assert_code("554 5.3.4");