Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

next/466/20240604/v1 #11236

Merged
merged 14 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 56 additions & 1 deletion doc/userguide/rules/smtp-keywords.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,59 @@ Signature Example:
:example-rule-options:`file.name; content:"winmail.dat";` \
classtype:bad-unknown; sid:1; rev:1;)

For additional information on the ``file.name`` keyword, see :doc:`file-keywords`.
For additional information on the ``file.name`` keyword, see :doc:`file-keywords`.

Frames
------

The SMTP parser supports the following frames:

* smtp.command_line
* smtp.response_line
* smtp.data
* smtp.stream

smtp.command_line
~~~~~~~~~~~~~~~~~

A single line from the client to the server. Multi-line commands will have a frame per
line. Lines part of the SMTP DATA transfer are excluded.

Copy link
Member

Choose a reason for hiding this comment

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

There's a typo in the name of the CSS container. These rules may not render as intended. It should be example-rule.

.. container:: example fule

alert smtp any any -> any any ( \
:example-rule-options:`frame:smtp.command_line; content:"MAIL|20|FROM:"; startswith;` \
sid:1;)

smtp.response_line
~~~~~~~~~~~~~~~~~~

A single line from the server to the client. Multi-line commands will have a frame per line.

.. container:: example fule

alert smtp any any -> any any ( \
:example-rule-options:`frame:smtp.response_line; content:"354 go ahead"; startswith;` \
sid:1;)

smtp.data
~~~~~~~~~

A streaming buffer containing the DATA bytes sent from client to server.

.. container:: example fule

alert smtp any any -> any any ( \
:example-rule-options:`frame:smtp.data; content:"Reply-To:"; startswith; content:"Subject"; distance:0;` \
sid:1;)

smtp.stream
~~~~~~~~~~~

Streaming buffer of the entire TCP data for the SMTP session.

.. container:: example fule

alert smtp any any -> any any (flow:to_client; \
:example-rule-options:`frame:smtp.stream; content:"250 ok|0d 0a|354 go ahead";` \
sid:1;)
7 changes: 7 additions & 0 deletions etc/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1207,6 +1207,13 @@
"type": "string"
}
},
"NS": {
"type": "array",
"minItems": 1,
"items": {
"type": "string"
}
},
"NULL": {
"type": "array",
"minItems": 1,
Expand Down
8 changes: 4 additions & 4 deletions rust/src/dns/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ fn dns_log_json_answer(
}

fn dns_log_query(
tx: &mut DNSTransaction, i: u16, flags: u64, jb: &mut JsonBuilder,
tx: &DNSTransaction, i: u16, flags: u64, jb: &mut JsonBuilder,
) -> Result<bool, JsonError> {
let index = i as usize;
if let Some(request) = &tx.request {
Expand All @@ -637,7 +637,7 @@ fn dns_log_query(

#[no_mangle]
pub extern "C" fn SCDnsLogJsonQuery(
tx: &mut DNSTransaction, i: u16, flags: u64, jb: &mut JsonBuilder,
tx: &DNSTransaction, i: u16, flags: u64, jb: &mut JsonBuilder,
) -> bool {
match dns_log_query(tx, i, flags, jb) {
Ok(false) | Err(_) => {
Expand All @@ -651,7 +651,7 @@ pub extern "C" fn SCDnsLogJsonQuery(

#[no_mangle]
pub extern "C" fn SCDnsLogJsonAnswer(
tx: &mut DNSTransaction, flags: u64, js: &mut JsonBuilder,
tx: &DNSTransaction, flags: u64, js: &mut JsonBuilder,
) -> bool {
if let Some(response) = &tx.response {
for query in &response.queries {
Expand All @@ -664,7 +664,7 @@ pub extern "C" fn SCDnsLogJsonAnswer(
}

#[no_mangle]
pub extern "C" fn SCDnsLogAnswerEnabled(tx: &mut DNSTransaction, flags: u64) -> bool {
pub extern "C" fn SCDnsLogAnswerEnabled(tx: &DNSTransaction, flags: u64) -> bool {
if let Some(response) = &tx.response {
for query in &response.queries {
if dns_log_rrtype_enabled(query.rrtype, flags) {
Expand Down
51 changes: 49 additions & 2 deletions src/app-layer-frames.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* Copyright (C) 2007-2022 Open Information Security Foundation
/* Copyright (C) 2007-2024 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
Expand Down Expand Up @@ -83,6 +83,32 @@ static void FrameDebug(const char *prefix, const Frames *frames, const Frame *fr
#endif
}

/**
* \note "open" means a frame that has no length set (len == -1)
* \todo perhaps we can search backwards */
Frame *FrameGetLastOpenByType(Frames *frames, const uint8_t frame_type)
{
Frame *candidate = NULL;

SCLogDebug(
"frames %p cnt %u, looking for last of type %" PRIu8, frames, frames->cnt, frame_type);
for (uint16_t i = 0; i < frames->cnt; i++) {
if (i < FRAMES_STATIC_CNT) {
Frame *frame = &frames->sframes[i];
FrameDebug("get_by_id(static)", frames, frame);
if (frame->type == frame_type && frame->len == -1)
candidate = frame;
} else {
const uint16_t o = i - FRAMES_STATIC_CNT;
Frame *frame = &frames->dframes[o];
FrameDebug("get_by_id(dynamic)", frames, frame);
if (frame->type == frame_type && frame->len == -1)
candidate = frame;
}
}
return candidate;
}

Frame *FrameGetById(Frames *frames, const int64_t id)
{
SCLogDebug("frames %p cnt %u, looking for %" PRIi64, frames, frames->cnt, id);
Expand Down Expand Up @@ -427,7 +453,7 @@ Frame *AppLayerFrameNewByPointer(Flow *f, const StreamSlice *stream_slice,
if (f->proto == IPPROTO_TCP && f->protoctx == NULL)
return NULL;
if (frame_start < stream_slice->input ||
frame_start >= stream_slice->input + stream_slice->input_len)
frame_start > stream_slice->input + stream_slice->input_len)
return NULL;
#endif
BUG_ON(frame_start < stream_slice->input);
Expand Down Expand Up @@ -666,6 +692,27 @@ Frame *AppLayerFrameGetById(Flow *f, const int dir, const FrameId frame_id)
return FrameGetById(frames, frame_id);
}

Frame *AppLayerFrameGetLastOpenByType(Flow *f, const int dir, const uint8_t frame_type)
{
if (!(FrameConfigTypeIsEnabled(f->alproto, frame_type)))
return NULL;

FramesContainer *frames_container = AppLayerFramesGetContainer(f);
SCLogDebug("get frame_type %" PRIu8 " direction %u/%s frames_container %p", frame_type, dir,
dir == 0 ? "toserver" : "toclient", frames_container);
if (frames_container == NULL)
return NULL;

Frames *frames;
if (dir == 0) {
frames = &frames_container->toserver;
} else {
frames = &frames_container->toclient;
}
SCLogDebug("frames %p", frames);
return FrameGetLastOpenByType(frames, frame_type);
}

static inline bool FrameIsDone(const Frame *frame, const uint64_t abs_right_edge)
{
/* frame with negative length means we don't know the size yet. */
Expand Down
4 changes: 4 additions & 0 deletions src/app-layer-frames.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,13 @@ void AppLayerFrameDump(Flow *f);

Frame *FrameGetByIndex(Frames *frames, const uint32_t idx);
Frame *FrameGetById(Frames *frames, const int64_t id);
Frame *FrameGetLastOpenByType(Frames *frames, const uint8_t frame_type);

Frame *AppLayerFrameGetById(Flow *f, const int direction, const FrameId frame_id);
Frame *AppLayerFrameGetLastOpenByType(Flow *f, const int direction, const uint8_t frame_type);

FrameId AppLayerFrameGetId(Frame *r);

void AppLayerFrameAddEvent(Frame *frame, uint8_t e);
void AppLayerFrameAddEventById(Flow *f, const int dir, const FrameId id, uint8_t e);
void AppLayerFrameSetLength(Frame *frame, int64_t len);
Expand Down
Loading
Loading