IMAP protocol parser, logger and sticky buffers v6#15400
Conversation
This introduces a parser for IMAP protocol. Ticket OISF#8276
This introduces a logger for IMAP protocol. Ticket OISF#8276
This implements the following sticky buffers for IMAP protocol: - imap.request - imap.response The following frames have been added: - imap.body - imap.headers - 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 #15400 +/- ##
==========================================
- Coverage 82.65% 82.61% -0.04%
==========================================
Files 996 999 +3
Lines 271109 273624 +2515
==========================================
+ Hits 224076 226057 +1981
- Misses 47033 47567 +534
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
|
Information: QA ran without warnings. Pipeline = 31514 |
|
Needs a rebase now |
catenacyber
left a comment
There was a problem hiding this comment.
Thanks for the work, needs a rebase
CI/QA : ✅
Git ID set : looks fine for me
CLA : you already contributed
Doc update : nice :-)
Redmine ticket : ok, should https://redmine.openinfosecfoundation.org/issues/3244 be closed as duplicate ?
Rustfmt : 🟡 please add the check in CI see .github/workflows/builds.yml: - run: rustfmt --check rust/src/dns/*.rs
Tests : some unanswered questions on SV PR like OISF/suricata-verify#2908 (comment)
Dependencies added: none
Code : looking
Commits segmentation : I would squash parser and logger but ok
Commit messages : cool
|
In the new PR, please add a explanation of the transaction state machine(s) and life cycle, including how transactions and other data structures are bounded. |
| pub direction: u8, | ||
| } | ||
|
|
||
| pub fn extract_command_from_requests(requests: &[ImapMessage]) -> Vec<u8> { |
There was a problem hiding this comment.
Does this need to be pub ? (I do not think so)
| pub fn extract_command_from_requests(requests: &[ImapMessage]) -> Vec<u8> { | ||
| for req in requests { | ||
| if let ImapMessageType::Command { command, arguments } = &req.message { | ||
| return if matches!(command, ImapCommand::Uid) { |
There was a problem hiding this comment.
Why is uid different ? please comment in the code ;-)
| return if matches!(command, ImapCommand::Uid) { | ||
| arguments | ||
| .first() | ||
| .map(|a| String::from_utf8_lossy(a).to_lowercase().into_bytes()) |
There was a problem hiding this comment.
Why converting to string and then back to Vec with loss on the way ?
There was a problem hiding this comment.
Ops, forgot to remove the string part.
| .map(|a| String::from_utf8_lossy(a).to_lowercase().into_bytes()) | ||
| .unwrap_or_default() | ||
| } else { | ||
| command.to_string().to_lowercase().into_bytes() |
There was a problem hiding this comment.
Because in that way the sticky buffer can match with a single case.
There was a problem hiding this comment.
I think we should preserve casing, and let rule writers use to_lowercase transform if they want
| } | ||
|
|
||
| impl ImapTransaction { | ||
| pub fn new() -> ImapTransaction { |
There was a problem hiding this comment.
Can we not just derive Default ?
| .iter() | ||
| .map(|a| String::from_utf8_lossy(a)) | ||
| .collect(); | ||
| js.append_string(&format!("{} {} {}", tag, cmd, args.join(" ")))?; |
There was a problem hiding this comment.
Still feels strange to recompute what we have seen on the wire...
There was a problem hiding this comment.
I think I can just log the raw_line directly.
| } = &response.message | ||
| { | ||
| if let Some(merged) = fetch.get_email() { | ||
| let _message_email = Some(merged); |
There was a problem hiding this comment.
Why this unused code ?
| } | ||
| let hname = CStr::from_ptr(hname); | ||
| if let Ok(hname_str) = hname.to_str() { | ||
| let normalized = hname_str.to_lowercase().replace('-', "_"); |
There was a problem hiding this comment.
Why this normalization from message-id to message_id ?
There was a problem hiding this comment.
Also, I do not see SV tests for this normlaization for x-mailer for example
There was a problem hiding this comment.
Following the same convention used by SMTP to log email events.
There was a problem hiding this comment.
But this is not logging, this is detection
Is IMAP using x_mailer when SMTP is using x-mailer in the network traffic ?
| } | ||
| let hname = CStr::from_ptr(hname); | ||
| if let Ok(hname_str) = hname.to_str() { | ||
| let normalized = hname_str.to_lowercase().replace('-', "_"); |
There was a problem hiding this comment.
I mean you can use hname_str straight away as the only caller of SCDetectImapEmailGetDataArray passes a string that is already "normalized"
|
|
||
| email.body_md5 | ||
| -------------- | ||
|
|
There was a problem hiding this comment.
Works only for SMTP, right ?
There was a problem hiding this comment.
So, please add it as you did for the other keywords ;-)
| email.body | ||
| ---------- | ||
|
|
||
| Matches on the body content of an email transferred over IMAP. |
There was a problem hiding this comment.
There is a limit of the size of the body we inspect, right ?
So, I think it should be mentioned in the docs
There was a problem hiding this comment.
Yes, the limit is applied when an email is parsed.
Changes:
emailobject insideimapevent is no longer logged. Existingemailcode will be refactored after this PR is merged in order to introduce a dedicatedemailevent.Link to ticket: https://redmine.openinfosecfoundation.org/issues/8276
Previous PR: #15209
SV_BRANCH=OISF/suricata-verify#2908