From: Philippe Antoine Date: Fri, 4 Jun 2021 13:06:10 +0000 (+0200) Subject: mime: handles multiple sections for a parameter X-Git-Tag: suricata-6.0.9~25 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0ca12493e3888f77ccbf3c9e8c2e652608d76af4;p=thirdparty%2Fsuricata.git mime: handles multiple sections for a parameter Ticket: 4386 as per RFC2231. For instance filename can be split between filename*0, filename*1, etc... (cherry picked from commit 784558df2e27455e0baf79d92253d21887ba3f49) --- diff --git a/rust/src/mime/mod.rs b/rust/src/mime/mod.rs index 256094cf43..b864659232 100644 --- a/rust/src/mime/mod.rs +++ b/rust/src/mime/mod.rs @@ -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, +) -> 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(¤t_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(¤t_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 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 { @@ -127,21 +162,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())); @@ -155,13 +194,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(); } } diff --git a/src/util-decode-mime.c b/src/util-decode-mime.c index 75f572d6ce..4394cfc6c9 100644 --- a/src/util-decode-mime.c +++ b/src/util-decode-mime.c @@ -64,7 +64,7 @@ #define CTNT_TYPE_STR "content-type" #define CTNT_DISP_STR "content-disposition" #define CTNT_TRAN_STR "content-transfer-encoding" -#define MSG_ID_STR "message-id" +#define MSG_ID_STR "message-id" #define MSG_STR "message/" #define MULTIPART_STR "multipart/" #define QP_STR "quoted-printable"