IMAP protocol parser, logger and sticky buffers v4#15064
Conversation
This introduces a parser for IMAP protocol. Ticket OISF#8276
This introduces a logger for IMAP protocol. Ticket OISF#8276
This implement the following sticky buffers for IMAP protocol: - imap.request - imap.response - imap.pdu The following email sticky buffers have been updated to work with IMAP: - email.from - email.subject - email.to - email.cc - email.date - email.message_id - email.x_mailer The following email sticky buffers have been added and are supported only for IMAP: - email.command - email.body - email.header - email.header.name - email.header.value Ticket OISF#8276
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #15064 +/- ##
==========================================
- Coverage 82.59% 82.59% -0.01%
==========================================
Files 990 993 +3
Lines 271761 274180 +2419
==========================================
+ Hits 224465 226450 +1985
- Misses 47296 47730 +434
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
|
ignore that qa run, previous run corrupted things |
|
WARNING:
Pipeline = 30397 |
|
Do we keywords file file.data that should work for attachments as is the case for SMTP ? see #14995 (comment) |
If they aren't, they should + SV test :) |
|
More generally speaking, what is the list of features that we want here ? So, we can check for completeness or create later tickets for the rest. For example, what about lua ? |
My idea was to add support for IMAP in file.data after the parser is merged, but I can implement it right away. |
I prefer a more incremental process, so we can track it in a ticket. |
I’ll open a ticket to track adding IMAP support to file.data and we can handle it in a follow-up PR. |
|
And what about lua support ? |
We can track this in another ticket. Lets focus on review of what is in the PR, so we can get this functionality merged. We can add more features in follow up work. |
catenacyber
left a comment
There was a problem hiding this comment.
Thanks for the work, some stuff to finish
CI : ✅
Git ID set : looks fine for me
CLA : you already contributed
Doc update : nice
Redmine ticket : ok, should you first fix https://redmine.openinfosecfoundation.org/issues/8265 (in a backport-compatible way)
Rustfmt : yes :-)
Tests : some stuff on SV PR like OISF/suricata-verify#2908 (comment)
Dependencies added: none
Code : putting commens inline
Commits segmentation : I would squash parser and logger but ok
Commit messages : nice, typo This implement vs implements with an s
Also imap.pdu is a frame, not a sticky buffer
|
|
||
| Syntax:: | ||
|
|
||
| email.command; content:"<command>"; |
There was a problem hiding this comment.
Do we plan to have this for SMTP ?
| Email Header Normalization | ||
| -------------------------- | ||
|
|
||
| Email headers are normalized before matching: |
There was a problem hiding this comment.
Are these imap email headers mime ?
There was a problem hiding this comment.
No, these are standard email message headers.
| let mut message_email: Option<&EmailData> = None; | ||
|
|
||
| let command_bytes = extract_command_from_requests(&tx.requests); | ||
| let command = String::from_utf8_lossy(&command_bytes).into_owned(); |
There was a problem hiding this comment.
Can this be moved only in the condition if let Some(email_data) = message_email { below where it gets used
And I do not think you need to_owned just use the ref
| .iter() | ||
| .map(|a| String::from_utf8_lossy(a)) | ||
| .collect(); | ||
| js.append_string(&format!("{} {} {}", tag, cmd, args.join(" ")))?; |
There was a problem hiding this comment.
Looks painful to reconstruct, can we not keep the raw bytes as this is what we output ?
| { | ||
| for part in &fetch.body_parts { | ||
| if let Some(email_data) = &part.email { | ||
| message_email = Some(email_data); |
There was a problem hiding this comment.
Could there be multiple emails in one tx ?
There was a problem hiding this comment.
So, this code seems strange as there is a loop...
There was a problem hiding this comment.
Why? I need to look for the part that has parsed email data.
There was a problem hiding this comment.
How are you sure that only one part has parsed email data ?
| if let Some(email_data) = message_email { | ||
| js.open_object("email")?; | ||
| js.set_string("command", &command)?; | ||
| log_email_header_string(js, &email_data.headers, "from", "from")?; |
There was a problem hiding this comment.
should we not reuse code from rust/src/mime/smtp_log.rs ? Like I see log_field_comma(js, ctx, "cc", "cc")?; there
There was a problem hiding this comment.
I wouldn't reuse it. log_field_comma is tightly coupled to SMTP's MIME layer whereas log_email_header_string operates on a HashMap<String, Vec<String>> where headers are already parsed, normalized and unfolded.
There was a problem hiding this comment.
So, we cannot reuse it because one takes input as some C structures, and the other takes input as rust HashMap...
Hmmm... That is a shame... Hope we will not have problems by updating one function and not the other in the future...
| name: PARSER_NAME.as_ptr() as *const c_char, | ||
| default_port: default_port.as_ptr(), | ||
| ipproto: IPPROTO_TCP, | ||
| probe_ts: Some(imap_probing_parser), |
There was a problem hiding this comment.
We no longer have port-independent detection !
| } | ||
| } | ||
|
|
||
| tx.requests.push(literal_msg); |
There was a problem hiding this comment.
This push does not seem bounded, is it ?
| kw.flags = SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER; | ||
| SCDetectHelperKeywordRegister(&kw); | ||
| g_mime_email_body_buffer_id = SCDetectHelperBufferMpmRegister("email.body", "IMAP EMAIL BODY", | ||
| ALPROTO_IMAP, STREAM_TOSERVER | STREAM_TOCLIENT, GetImapEmailBodyData); |
There was a problem hiding this comment.
Do we wait the end of the email to detect it ?
| tx.parsed_email = Some(parsed_email); | ||
| } | ||
| } | ||
| tx.responses.push(response); |
There was a problem hiding this comment.
Should be bounded by tx.response_lines.len() < IMAP_MAX_LINES ?
| if self.request_tls { | ||
| if let ImapMessageType::Response { status, .. } = &response.message { | ||
| if matches!(status, ImapResponseStatus::Ok) { | ||
| SCLogDebug!("IMAP: STARTTLS detected"); |
There was a problem hiding this comment.
you should call SCAppLayerRequestProtocolTLSUpgrade right ?
There was a problem hiding this comment.
it's called above, line 363.
There was a problem hiding this comment.
I think it should be rather called here at this place, than on next request
|
Replaced with #15209 |
Changes:
imap-event.rulesaddedheadersandbodyfields boundedimap.email.body->email.bodyimap.email.header->email.headerimap.email.header.name->email.header.nameimap.email.header.value->email.header.valueimap.email.directionreplaced withemail.commandLink to ticket: https://redmine.openinfosecfoundation.org/issues/8276
Previous PR: #14995
SV_BRANCH=OISF/suricata-verify#2908