]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
mime: handles multiple sections for a parameter
authorPhilippe Antoine <contact@catenacyber.fr>
Fri, 4 Jun 2021 13:06:10 +0000 (15:06 +0200)
committerVictor Julien <vjulien@oisf.net>
Tue, 7 Dec 2021 06:56:36 +0000 (07:56 +0100)
Ticket: 4386

as per RFC2231.
For instance filename can be split between filename*0,
filename*1, etc...

rust/src/mime/mod.rs

index 7af3f77438a4cb4885b6e5de5d7d4d79de9e227c..045b542411e8f56b513409dde97dda6c34f65ee6 100644 (file)
@@ -29,42 +29,42 @@ pub struct MIMEHeaderTokens<'a> {
 }
 
 pub fn mime_parse_value_delimited(input: &[u8]) -> IResult<&[u8], &[u8]> {
-    let (i2, _) = tag!(input, "\"")?;
-    let (i3, value) = take_until!(i2, "\"")?;
-    let (i4, _) = tag!(i3, "\"")?;
-    return Ok((i4, value));
+    let (input, _) = tag!(input, "\"")?;
+    let (input, value) = take_until!(input, "\"")?;
+    let (input, _) = tag!(input, "\"")?;
+    return Ok((input, value));
 }
 
 pub fn mime_parse_header_token(input: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> {
     // from RFC2047 : like ch.is_ascii_whitespace but without 0x0c FORM-FEED
-    let (i1, _) = take_while!(input, |ch: u8| ch == 0x20
+    let (input, _) = take_while!(input, |ch: u8| ch == 0x20
         || ch == 0x09
         || ch == 0x0a
         || ch == 0x0d)?;
-    let (i2, name) = take_until!(i1, "=")?;
-    let (i3, _) = tag!(i2, "=")?;
-    let (i4, value) = alt!(
-        i3,
+    let (input, name) = take_until!(input, "=")?;
+    let (input, _) = tag!(input, "=")?;
+    let (input, value) = alt!(
+        input,
         mime_parse_value_delimited | complete!(take_until!(";")) | rest
     )?;
-    let (i5, _) = opt!(i4, complete!(tag!(";")))?;
-    return Ok((i5, (name, value)));
+    let (input, _) = opt!(input, complete!(tag!(";")))?;
+    return Ok((input, (name, value)));
 }
 
 fn mime_parse_header_tokens(input: &[u8]) -> IResult<&[u8], MIMEHeaderTokens> {
-    let (mut i2, _) = take_until_and_consume!(input, ";")?;
+    let (mut input, _) = take_until_and_consume!(input, ";")?;
     let mut tokens = HashMap::new();
-    while i2.len() > 0 {
-        match mime_parse_header_token(i2) {
+    while input.len() > 0 {
+        match mime_parse_header_token(input) {
             Ok((rem, t)) => {
                 tokens.insert(t.0, t.1);
                 // should never happen
-                debug_validate_bug_on!(i2.len() == rem.len());
-                if i2.len() == rem.len() {
+                debug_validate_bug_on!(input.len() == rem.len());
+                if input.len() == rem.len() {
                     //infinite loop
                     return Err(Err::Error((input, ErrorKind::Eof)));
                 }
-                i2 = rem;
+                input = rem;
             }
             Err(_) => {
                 // keep first tokens is error in remaining buffer
@@ -72,17 +72,51 @@ fn mime_parse_header_tokens(input: &[u8]) -> IResult<&[u8], MIMEHeaderTokens> {
             }
         }
     }
-    return Ok((i2, MIMEHeaderTokens { tokens }));
+    return Ok((input, MIMEHeaderTokens { tokens }));
 }
 
-fn mime_find_header_token<'a>(header: &'a [u8], token: &[u8]) -> Result<&'a [u8], ()> {
+fn mime_find_header_token<'a>(
+    header: &'a [u8], token: &[u8], sections_values: &'a mut Vec<u8>,
+) -> Result<&'a [u8], ()> {
     match mime_parse_header_tokens(header) {
         Ok((_rem, t)) => {
+            // in case of multiple sections for the parameter cf RFC2231
+            let mut current_section_slice = Vec::new();
+
             // look for the specific token
             match t.tokens.get(token) {
                 // easy nominal case
                 Some(value) => return Ok(value),
-                None => return Err(()),
+                None => {
+                    // check for initial section of a parameter
+                    current_section_slice.extend_from_slice(token);
+                    current_section_slice.extend_from_slice(b"*0");
+                    match t.tokens.get(&current_section_slice[..]) {
+                        Some(value) => {
+                            sections_values.extend_from_slice(value);
+                            let l = current_section_slice.len();
+                            current_section_slice[l - 1] = b'1';
+                        }
+                        None => return Err(()),
+                    }
+                }
+            }
+
+            let mut current_section_seen = 1;
+            // we have at least the initial section
+            // try looping until we do not find anymore a next section
+            loop {
+                match t.tokens.get(&current_section_slice[..]) {
+                    Some(value) => {
+                        sections_values.extend_from_slice(value);
+                        current_section_seen += 1;
+                        let nbdigits = current_section_slice.len() - token.len() - 1;
+                        current_section_slice.truncate(current_section_slice.len() - nbdigits);
+                        current_section_slice
+                            .extend_from_slice(current_section_seen.to_string().as_bytes());
+                    }
+                    None => return Ok(sections_values),
+                }
             }
         }
         Err(_) => {
@@ -103,7 +137,8 @@ pub unsafe extern "C" fn rs_mime_find_header_token(
 ) -> bool {
     let hbuf = build_slice!(hinput, hlen as usize);
     let tbuf = build_slice!(tinput, tlen as usize);
-    match mime_find_header_token(hbuf, tbuf) {
+    let mut sections_values = Vec::new();
+    match mime_find_header_token(hbuf, tbuf, &mut sections_values) {
         Ok(value) => {
             // limit the copy to the supplied buffer size
             if value.len() <= RS_MIME_MAX_TOKEN_LEN {
@@ -125,21 +160,25 @@ mod test {
 
     #[test]
     fn test_mime_find_header_token() {
+        let mut outvec = Vec::new();
         let undelimok = mime_find_header_token(
             "attachment; filename=test;".as_bytes(),
             "filename".as_bytes(),
+            &mut outvec,
         );
         assert_eq!(undelimok, Ok("test".as_bytes()));
 
         let delimok = mime_find_header_token(
             "attachment; filename=\"test2\";".as_bytes(),
             "filename".as_bytes(),
+            &mut outvec,
         );
         assert_eq!(delimok, Ok("test2".as_bytes()));
 
         let evasion_othertoken = mime_find_header_token(
             "attachment; dummy=\"filename=wrong\"; filename=real;".as_bytes(),
             "filename".as_bytes(),
+            &mut outvec,
         );
         assert_eq!(evasion_othertoken, Ok("real".as_bytes()));
 
@@ -153,13 +192,39 @@ mod test {
         let badending = mime_find_header_token(
             "attachment; filename=oksofar; badending".as_bytes(),
             "filename".as_bytes(),
+            &mut outvec,
         );
         assert_eq!(badending, Ok("oksofar".as_bytes()));
 
         let missend = mime_find_header_token(
             "attachment; filename=test".as_bytes(),
             "filename".as_bytes(),
+            &mut outvec,
         );
         assert_eq!(missend, Ok("test".as_bytes()));
+
+        let spaces = mime_find_header_token(
+            "attachment; filename=test me wrong".as_bytes(),
+            "filename".as_bytes(),
+            &mut outvec,
+        );
+        assert_eq!(spaces, Ok("test me wrong".as_bytes()));
+
+        assert_eq!(outvec.len(), 0);
+        let multi = mime_find_header_token(
+            "attachment; filename*0=abc; filename*1=\"def\";".as_bytes(),
+            "filename".as_bytes(),
+            &mut outvec,
+        );
+        assert_eq!(multi, Ok("abcdef".as_bytes()));
+        outvec.clear();
+
+        let multi = mime_find_header_token(
+            "attachment; filename*1=456; filename*0=\"123\"".as_bytes(),
+            "filename".as_bytes(),
+            &mut outvec,
+        );
+        assert_eq!(multi, Ok("123456".as_bytes()));
+        outvec.clear();
     }
 }