]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
mime: retain some stateful data for quoted-printable 13976/head
authorPhilippe Antoine <pantoine@oisf.net>
Mon, 29 Sep 2025 13:11:41 +0000 (15:11 +0200)
committerVictor Julien <victor@inliniac.net>
Tue, 7 Oct 2025 01:31:01 +0000 (03:31 +0200)
In case a sequence like =3D is split over 2 calls to SCSmtpMimeParseLine

Ticket: 7950
(cherry picked from commit 56e08c9134236ed851ccab3430219ca60354b455)

rust/src/mime/smtp.rs

index d53ff92dd09fc223b600d8d5a3c20a0fea129ffc..10e98d2ff86a2c63e66e1b4bf097cfbd56e4dca7 100644 (file)
@@ -477,6 +477,7 @@ fn mime_smtp_parse_line(
                     ctx.filename.clear();
                     ctx.headers.truncate(ctx.main_headers_nb);
                     ctx.encoding = MimeSmtpEncoding::Plain;
+                    ctx.bufeolen = 0;
                     if i.len() >= b.len() + 2 && i[b.len()] == b'-' && i[b.len() + 1] == b'-' {
                         ctx.boundaries.pop();
                     }
@@ -571,17 +572,49 @@ fn mime_smtp_parse_line(
                         if i.len() > MAX_ENC_LINE_LEN {
                             warnings |= MIME_ANOM_LONG_ENC_LINE;
                         }
-                        let mut c = 0;
+                        let mut c = 0usize;
                         let mut eol_equal = false;
                         let mut quoted_buffer = Vec::with_capacity(i.len());
+                        if ctx.bufeolen > 0 && ctx.bufeol[0] == b'=' {
+                            // we were escaping
+                            if i.len() >= 2 {
+                                let (h1, h2) = if ctx.bufeolen == 1 {
+                                    (i[0], i[1])
+                                } else {
+                                    (ctx.bufeol[1], i[0])
+                                };
+                                if let Some(v) = hex(h1) {
+                                    if let Some(v2) = hex(h2) {
+                                        quoted_buffer.push((v << 4) | v2);
+                                    }
+                                }
+                            }
+                            if quoted_buffer.is_empty() {
+                                warnings |= MIME_ANOM_INVALID_QP;
+                            }
+                            c = 3 - ctx.bufeolen as usize;
+                            ctx.bufeolen = 0;
+                        } else if ctx.bufeolen > 0 {
+                            quoted_buffer.extend_from_slice(&ctx.bufeol[..ctx.bufeolen as usize]);
+                            ctx.bufeolen = 0;
+                        }
                         while c < i.len() {
                             if i[c] == b'=' {
-                                if c == i.len() - 1 {
+                                if c == i.len() - 1 && full.len() > i.len() {
                                     eol_equal = true;
                                     break;
                                 } else if c + 2 >= i.len() {
-                                    // log event ?
-                                    warnings |= MIME_ANOM_INVALID_QP;
+                                    if full.len() > i.len() {
+                                        // log event ?
+                                        warnings |= MIME_ANOM_INVALID_QP;
+                                    } else {
+                                        // keep in state the bytes to unescape
+                                        ctx.bufeolen = (i.len() - c) as u8;
+                                        ctx.bufeol[0] = b'=';
+                                        if ctx.bufeolen == 2 {
+                                            ctx.bufeol[1] = i[c + 1];
+                                        }
+                                    }
                                     break;
                                 }
                                 if let Some(v) = hex(i[c + 1]) {
@@ -599,7 +632,13 @@ fn mime_smtp_parse_line(
                                 c += 1;
                             }
                         }
-                        if !eol_equal {
+                        if i.is_empty() {
+                            ctx.bufeolen = (full.len() - i.len()) as u8;
+                            if ctx.bufeolen > 0 {
+                                ctx.bufeol[..ctx.bufeolen as usize]
+                                    .copy_from_slice(&full[i.len()..]);
+                            }
+                        } else if !eol_equal {
                             quoted_buffer.extend_from_slice(&full[i.len()..]);
                         }
                         mime_smtp_find_url_strings(ctx, &quoted_buffer);
@@ -679,7 +718,11 @@ pub unsafe extern "C" fn SCMimeSmtpGetHeader(
     let name: &CStr = CStr::from_ptr(str); //unsafe
 
     // Convert to lowercase, mime::slice_equals_lowercase expects it.
-    let name: Vec<u8> = name.to_bytes().iter().map(|b| b.to_ascii_lowercase()).collect();
+    let name: Vec<u8> = name
+        .to_bytes()
+        .iter()
+        .map(|b| b.to_ascii_lowercase())
+        .collect();
     for h in &ctx.headers[ctx.main_headers_nb..] {
         if mime::slice_equals_lowercase(&h.name, &name) {
             *buffer = h.value.as_ptr();