Skip to content

IMAP protocol parser, logger and sticky buffers v2#14822

Closed
glongo wants to merge 4 commits into
OISF:mainfrom
glongo:dev-imap-proto-v2
Closed

IMAP protocol parser, logger and sticky buffers v2#14822
glongo wants to merge 4 commits into
OISF:mainfrom
glongo:dev-imap-proto-v2

Conversation

@glongo
Copy link
Copy Markdown
Contributor

@glongo glongo commented Feb 16, 2026

Link to ticket: https://redmine.openinfosecfoundation.org/issues/8276

Previous PR: #14792

Describe changes:

  • Fix body parsing for the Content-Disposition header
  • Fix body parsing when the TCP payload is reassembled

SV_BRANCH=OISF/suricata-verify#2908

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.email.direction
- imap.email.header
- imap.email.header.name
- imap.email.header.value
- imap.email.body

Ticket OISF#8276
@github-actions
Copy link
Copy Markdown

NOTE: This PR may contain new authors.

Email headers are normalized before matching:

- **Header names** are converted to lowercase with hyphens replaced by underscores
(e.g. ``Content-Type`` becomes ``content_type``).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why so ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we do that for smtp.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How so ? Where do you see this in the code ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See output-json-email-common.c:

struct {
    const char *config_field;
    const char *email_field;
    uint32_t flags;
} email_fields[] =  {
    { "reply_to", "reply-to", LOG_EMAIL_DEFAULT },
    { "bcc", "bcc", LOG_EMAIL_COMMA|LOG_EMAIL_EXTENDED },
    { "message_id", "message-id", LOG_EMAIL_EXTENDED },
    { "subject", "subject", LOG_EMAIL_EXTENDED },
    { "x_mailer", "x-mailer", LOG_EMAIL_EXTENDED },
    { "user_agent", "user-agent", LOG_EMAIL_EXTENDED },
    { "received", "received", LOG_EMAIL_ARRAY },
    { "x_originating_ip", "x-originating-ip", LOG_EMAIL_DEFAULT },
    { "in_reply_to",  "in-reply-to", LOG_EMAIL_DEFAULT },
    { "references",  "references", LOG_EMAIL_DEFAULT },
    { "importance",  "importance", LOG_EMAIL_DEFAULT },
    { "priority",  "priority", LOG_EMAIL_DEFAULT },
    { "sensitivity",  "sensitivity", LOG_EMAIL_DEFAULT },
    { "organization",  "organization", LOG_EMAIL_DEFAULT },
    { "content_md5",  "content-md5", LOG_EMAIL_DEFAULT },
    { "date", "date", LOG_EMAIL_DEFAULT },
    { NULL, NULL, LOG_EMAIL_DEFAULT},
};

Also, @jasonish asked me to do this for compatibility with elasticsearch.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for output, not for detection, right ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Philippe is correct in this interpretation.

quic-keywords
nfs-keywords
smtp-keywords
imap-keywords
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is more doc than keywords, there is a section where we show an example json output of an imap event

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, the idea would be to also add an output examplo to the https://docs.suricata.io/en/latest/output/eve/index.html chapter.

Comment thread src/output.c
JsonArpLogRegister();
/* IMAP JSON logger. */
OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonImapLog", "eve-log.imap",
OutputJsonLogInitSub, ALPROTO_IMAP, JsonGenericDirFlowLogger, JsonLogThreadInit,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if JsonGenericDirFlowLogger is the best for the cases where we see files/emails in both directions...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a direction field to the email object that indicates whether it's to_server or to_client.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I am not sure I like it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on what you don't like, exactly?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread rust/src/imap/parser.rs
}

fn parse_command_keyword(i: &[u8]) -> IResult<&[u8], ImapCommand> {
alt((
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why alt of alt ?

This looks inefficient compared to a switch over strings with rust being clever

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bcause alt accepts a maximum of 21 parsers.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that means alt is not the right way to do this

Comment thread rust/src/imap/parser.rs
fn parse_quoted_string(i: &[u8]) -> IResult<&[u8], &[u8]> {
delimited(
char('"'),
take_while(|c| c != b'"' && c != b'\r' && c != b'\n'),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not escape quotes in a quoted string ?

Comment thread etc/schema.json
"type": "array",
"description": "Array of IMAP response lines in this transaction",
"items": {
"type": "string"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can add the suricata section with the keywords ;-)

Comment thread rust/src/imap/imap.rs
pub requests: Vec<ImapMessage>,
pub responses: Vec<ImapMessage>,
pub request_lines: Vec<Vec<u8>>,
pub response_lines: Vec<Vec<u8>>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you bound the number of response lines somehow ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread rust/src/imap/imap.rs
if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
let _ = AppLayerRegisterParser(&parser, alproto);
}
if let Some(val) = conf_get("app-layer.protocols.imap.max-tx") {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have pipelined transactions or such ?

Comment thread rust/src/imap/imap.rs
if SCAppLayerProtoDetectConfProtoDetectionEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
let alproto = applayer_register_protocol_detection(&parser, 1);
ALPROTO_IMAP = alproto;
if SCAppLayerParserConfParserEnabled(ip_proto_str.as_ptr(), parser.name) != 0 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not have port-indpendent detection with CAPABILITY pattern anymore ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not yet.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes we do now and you are removing it... Was this intended ?

Copy link
Copy Markdown
Contributor

@catenacyber catenacyber left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome big work

I think there are quite some things to complete

My main remarks would be

  • how much verbose/configurable do we want the logs to be ?
  • can we have the existing keywords like email.from work also with IMAP ?

@suricata-qa
Copy link
Copy Markdown

Information: QA ran without warnings.

Pipeline = 29614

@jufajardini jufajardini added the needs rebase Needs rebase to main label Feb 24, 2026
Copy link
Copy Markdown
Contributor

@jufajardini jufajardini left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left some inline comments, sharing review now to allow for the new iteration to happen!

Comment thread suricata.yaml.in
content-inspect-window: 4096
imap:
enabled: detection-only
enabled: yes
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor, but: is the plan to have it enabled by default? If so, then I agree that output verbosity is something to consider carefully.

Comment thread doc/userguide/upgrade.rst
- Encrypted quic traffic bypass is now independently controlled through
``app-layer.protocols.quic.encryption-handling`` setting. The setting can either
be ``bypass``, ``track-only`` or ``full``.
- IMAP parser, logger and sticky buffers have been introduced.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it will be enabled by default, I think it is worth it mentioning that here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general this is not a changelog, so it should only mention breaking changes or other things ppl should really be aware of.

Comment thread src/output.c
JsonArpLogRegister();
/* IMAP JSON logger. */
OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonImapLog", "eve-log.imap",
OutputJsonLogInitSub, ALPROTO_IMAP, JsonGenericDirFlowLogger, JsonLogThreadInit,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on what you don't like, exactly?

Comment on lines +66 to +74
.. table:: **Direction values for imap.email.direction**

===== ===========
Value Name
===== ===========
0 to_server
1 to_client
===== ===========

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want users to use to_server and to_client, wouldn't it be simpler to remove this table?

Email headers are normalized before matching:

- **Header names** are converted to lowercase with hyphens replaced by underscores
(e.g. ``Content-Type`` becomes ``content_type``).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think Philippe is correct in this interpretation.

Comment thread src/output.c
JsonArpLogRegister();
/* IMAP JSON logger. */
OutputRegisterTxSubModule(LOGGER_JSON_TX, "eve-log", "JsonImapLog", "eve-log.imap",
OutputJsonLogInitSub, ALPROTO_IMAP, JsonGenericDirFlowLogger, JsonLogThreadInit,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quic-keywords
nfs-keywords
smtp-keywords
imap-keywords
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, the idea would be to also add an output examplo to the https://docs.suricata.io/en/latest/output/eve/index.html chapter.

@glongo
Copy link
Copy Markdown
Contributor Author

glongo commented Mar 9, 2026

Replaced with #14995

@glongo glongo closed this Mar 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs rebase Needs rebase to main

Development

Successfully merging this pull request may close these issues.

5 participants