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/351/20240316/v1 #10654

Merged
merged 14 commits into from
Mar 18, 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
2 changes: 1 addition & 1 deletion src/conf-yaml-loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ static int ConfYamlParse(yaml_parser_t *parser, ConfNode *parent, int inseq, int

while (!done) {
if (!yaml_parser_parse(parser, &event)) {
SCLogError("Failed to parse configuration file at line %" PRIuMAX ": %s\n",
SCLogError("Failed to parse configuration file at line %" PRIuMAX ": %s",
(uintmax_t)parser->problem_mark.line, parser->problem);
retval = -1;
break;
Expand Down
115 changes: 69 additions & 46 deletions src/output-json-alert.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "stream.h"
#include "threadvars.h"
#include "util-debug.h"
#include "stream-tcp.h"

#include "util-logopenfile.h"
#include "util-misc.h"
Expand Down Expand Up @@ -112,17 +113,6 @@ typedef struct JsonAlertLogThread_ {
OutputJsonThreadCtx *ctx;
} JsonAlertLogThread;

/* Callback function to pack payload contents from a stream into a buffer
* so we can report them in JSON output. */
static int AlertJsonDumpStreamSegmentCallback(
const Packet *p, TcpSegment *seg, void *data, const uint8_t *buf, uint32_t buflen)
{
MemBuffer *payload = (MemBuffer *)data;
MemBufferWriteRaw(payload, buf, buflen);

return 1;
}

static void AlertJsonSourceTarget(const Packet *p, const PacketAlert *pa,
JsonBuilder *js, JsonAddrInfo *addr)
{
Expand Down Expand Up @@ -414,7 +404,8 @@ static void AlertAddFiles(const Packet *p, JsonBuilder *jb, const uint64_t tx_id
}
}

static void AlertAddFrame(const Packet *p, JsonBuilder *jb, const int64_t frame_id)
static void AlertAddFrame(
const Packet *p, const int64_t frame_id, JsonBuilder *jb, MemBuffer *buffer)
{
if (p->flow == NULL || (p->proto == IPPROTO_TCP && p->flow->protoctx == NULL))
return;
Expand All @@ -436,7 +427,7 @@ static void AlertAddFrame(const Packet *p, JsonBuilder *jb, const int64_t frame_
}
Frame *frame = FrameGetById(frames, frame_id);
if (frame != NULL) {
FrameJsonLogOneFrame(IPPROTO_TCP, frame, p->flow, stream, p, jb);
FrameJsonLogOneFrame(IPPROTO_TCP, frame, p->flow, stream, p, jb, buffer);
}
} else if (p->proto == IPPROTO_UDP) {
if (PKT_IS_TOSERVER(p)) {
Expand All @@ -446,7 +437,7 @@ static void AlertAddFrame(const Packet *p, JsonBuilder *jb, const int64_t frame_
}
Frame *frame = FrameGetById(frames, frame_id);
if (frame != NULL) {
FrameJsonLogOneFrame(IPPROTO_UDP, frame, p->flow, NULL, p, jb);
FrameJsonLogOneFrame(IPPROTO_UDP, frame, p->flow, NULL, p, jb, buffer);
}
}
}
Expand Down Expand Up @@ -502,9 +493,64 @@ void EveAddVerdict(JsonBuilder *jb, const Packet *p)
jb_close(jb);
}

struct AlertJsonStreamDataCallbackData {
MemBuffer *payload;
uint64_t last_re;
};

static int AlertJsonStreamDataCallback(
void *cb_data, const uint8_t *input, const uint32_t input_len, const uint64_t input_offset)
{
struct AlertJsonStreamDataCallbackData *cbd = cb_data;
if (input_offset > cbd->last_re) {
MemBufferWriteString(
cbd->payload, "[%" PRIu64 " bytes missing]", input_offset - cbd->last_re);
}

int done = 0;
uint32_t written = MemBufferWriteRaw(cbd->payload, input, input_len);
if (written < input_len)
done = 1;
cbd->last_re = input_offset + input_len;
return done;
}

/** \internal
* \brief try to log stream data into payload/payload_printable
* \retval true stream data logged
* \retval false stream data not logged
*/
static bool AlertJsonStreamData(const AlertJsonOutputCtx *json_output_ctx, JsonAlertLogThread *aft,
Flow *f, const Packet *p, JsonBuilder *jb)
{
TcpSession *ssn = f->protoctx;
TcpStream *stream = (PKT_IS_TOSERVER(p)) ? &ssn->client : &ssn->server;

MemBufferReset(aft->payload_buffer);
struct AlertJsonStreamDataCallbackData cbd = { .payload = aft->payload_buffer,
.last_re = STREAM_BASE_OFFSET(stream) };
uint64_t unused = 0;
StreamReassembleLog(ssn, stream, AlertJsonStreamDataCallback, &cbd, STREAM_BASE_OFFSET(stream),
&unused, false);
if (cbd.payload->offset) {
if (json_output_ctx->flags & LOG_JSON_PAYLOAD_BASE64) {
jb_set_base64(jb, "payload", cbd.payload->buffer, cbd.payload->offset);
}

if (json_output_ctx->flags & LOG_JSON_PAYLOAD) {
uint8_t printable_buf[cbd.payload->offset + 1];
uint32_t offset = 0;
PrintStringsToBuffer(printable_buf, &offset, sizeof(printable_buf), cbd.payload->buffer,
cbd.payload->offset);
jb_set_string(jb, "payload_printable", (char *)printable_buf);
}
return true;
}
return false;
}

static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p)
{
MemBuffer *payload = aft->payload_buffer;
AlertJsonOutputCtx *json_output_ctx = aft->json_output_ctx;

if (p->alerts.cnt == 0 && !(p->flags & PKT_HAS_TAG))
Expand Down Expand Up @@ -610,36 +656,14 @@ static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p)
int stream = (p->proto == IPPROTO_TCP) ?
(pa->flags & (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_STREAM_MATCH) ?
1 : 0) : 0;
DEBUG_VALIDATE_BUG_ON(
p->flow == NULL); // should be impossible, but scan-build got confused

/* Is this a stream? If so, pack part of it into the payload field */
if (stream) {
uint8_t flag;

MemBufferReset(payload);

if (p->flowflags & FLOW_PKT_TOSERVER) {
flag = STREAM_DUMP_TOCLIENT;
} else {
flag = STREAM_DUMP_TOSERVER;
}

StreamSegmentForEach((const Packet *)p, flag,
AlertJsonDumpStreamSegmentCallback,
(void *)payload);
if (payload->offset) {
if (json_output_ctx->flags & LOG_JSON_PAYLOAD_BASE64) {
jb_set_base64(jb, "payload", payload->buffer, payload->offset);
}

if (json_output_ctx->flags & LOG_JSON_PAYLOAD) {
uint8_t printable_buf[payload->offset + 1];
uint32_t offset = 0;
PrintStringsToBuffer(printable_buf, &offset,
sizeof(printable_buf),
payload->buffer, payload->offset);
jb_set_string(jb, "payload_printable", (char *)printable_buf);
}
} else if (p->payload_len) {
if (stream && p->flow != NULL) {
const bool stream_data_logged =
AlertJsonStreamData(json_output_ctx, aft, p->flow, p, jb);
if (!stream_data_logged && p->payload_len) {
/* Fallback on packet payload */
AlertAddPayload(json_output_ctx, jb, p);
}
Expand All @@ -652,7 +676,7 @@ static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p)
}

if (pa->flags & PACKET_ALERT_FLAG_FRAME) {
AlertAddFrame(p, jb, pa->frame_id);
AlertAddFrame(p, pa->frame_id, jb, aft->payload_buffer);
}

/* base64-encoded full packet */
Expand Down Expand Up @@ -889,14 +913,13 @@ static void JsonAlertLogSetupMetadata(AlertJsonOutputCtx *json_output_ctx,
warn_no_meta = true;
}
}

json_output_ctx->payload_buffer_size = payload_buffer_size;
}

if (flags & LOG_JSON_RULE_METADATA) {
DetectEngineSetParseMetadata();
}

json_output_ctx->payload_buffer_size = payload_buffer_size;
json_output_ctx->flags |= flags;
}

Expand Down
135 changes: 89 additions & 46 deletions src/output-json-frame.c
Original file line number Diff line number Diff line change
Expand Up @@ -125,61 +125,90 @@ static void PayloadAsHex(const uint8_t *data, uint32_t data_len, char *str, size
}
#endif

static void FrameAddPayloadTCP(JsonBuilder *js, const TcpStream *stream, const Frame *frame)
struct FrameJsonStreamDataCallbackData {
MemBuffer *payload;
const Frame *frame;
uint64_t last_re; /**< used to detect gaps */
};

static int FrameJsonStreamDataCallback(
void *cb_data, const uint8_t *input, const uint32_t input_len, const uint64_t input_offset)
{
uint32_t sb_data_len = 0;
const uint8_t *data = NULL;
uint64_t data_offset = 0;
struct FrameJsonStreamDataCallbackData *cbd = cb_data;
const Frame *frame = cbd->frame;

// TODO consider ACK'd
uint32_t write_size = input_len;
int done = 0;

if (frame->offset < STREAM_BASE_OFFSET(stream)) {
if (StreamingBufferGetData(&stream->sb, &data, &sb_data_len, &data_offset) == 0) {
SCLogDebug("NO DATA1");
return;
if (frame->len >= 0) {
const uint64_t data_re = input_offset + input_len;
const uint64_t frame_re = frame->offset + (uint64_t)frame->len;

/* data entirely after frame, we're done */
if (input_offset >= frame_re) {
return 1;
}
} else {
data_offset = (uint64_t)frame->offset;
SCLogDebug("data_offset %" PRIu64, data_offset);
if (StreamingBufferGetDataAtOffset(
&stream->sb, &data, &sb_data_len, (uint64_t)data_offset) == 0) {
SCLogDebug("NO DATA1");
return;
/* make sure to only log data belonging to the frame */
if (data_re >= frame_re) {
const uint64_t to_write = frame_re - input_offset;
if (to_write < (uint64_t)write_size) {
write_size = (uint32_t)to_write;
}
done = 1;
}
}
if (data == NULL || sb_data_len == 0) {
SCLogDebug("NO DATA2");
return;
if (input_offset > cbd->last_re) {
MemBufferWriteString(
cbd->payload, "[%" PRIu64 " bytes missing]", input_offset - cbd->last_re);
}

if (frame->len >= 0) {
sb_data_len = MIN(frame->len, (int32_t)sb_data_len);
if (write_size > 0) {
uint32_t written = MemBufferWriteRaw(cbd->payload, input, write_size);
if (written < write_size)
done = 1;
}
SCLogDebug("frame data_offset %" PRIu64 ", data_len %u frame len %" PRIi64, data_offset,
sb_data_len, frame->len);
cbd->last_re = input_offset + write_size;
return done;
}

/** \internal
* \brief try to log frame's stream data into payload/payload_printable
*/
static void FrameAddPayloadTCP(Flow *f, const TcpSession *ssn, const TcpStream *stream,
const Frame *frame, JsonBuilder *jb, MemBuffer *buffer)
{
MemBufferReset(buffer);

/* consider all data, ACK'd and non-ACK'd */
const uint64_t stream_data_re = StreamDataRightEdge(stream, true);
bool complete = false;
if (frame->len > 0) {
const uint64_t frame_re = frame->offset + (uint64_t)frame->len;
const uint64_t data_re = data_offset + sb_data_len;
complete = frame_re <= data_re;
if (frame->len >= 0 && frame->offset + (uint64_t)frame->len <= stream_data_re) {
complete = true;
}
jb_set_bool(js, "complete", complete);

uint32_t data_len = MIN(sb_data_len, 256);
jb_set_base64(js, "payload", data, data_len);
struct FrameJsonStreamDataCallbackData cbd = {
.payload = buffer, .frame = frame, .last_re = frame->offset
};
uint64_t unused = 0;
StreamReassembleLog(
ssn, stream, FrameJsonStreamDataCallback, &cbd, frame->offset, &unused, false);
/* if we have all data, but didn't log until the end of the frame, we have a gap at the
* end of the frame
* TODO what about not logging due to buffer full? */
if (complete && frame->len >= 0 && cbd.last_re < frame->offset + (uint64_t)frame->len) {
MemBufferWriteString(cbd.payload, "[%" PRIu64 " bytes missing]",
(frame->offset + (uint64_t)frame->len) - cbd.last_re);
}

uint8_t printable_buf[data_len + 1];
uint32_t o = 0;
PrintStringsToBuffer(printable_buf, &o, data_len + 1, data, data_len);
printable_buf[data_len] = '\0';
jb_set_string(js, "payload_printable", (char *)printable_buf);
#if 0
char pretty_buf[data_len * 4 + 1];
pretty_buf[0] = '\0';
PayloadAsHex(data, data_len, pretty_buf, data_len * 4 + 1);
jb_set_string(js, "payload_hex", pretty_buf);
#endif
if (cbd.payload->offset) {
jb_set_base64(jb, "payload", cbd.payload->buffer, cbd.payload->offset);
uint8_t printable_buf[cbd.payload->offset + 1];
uint32_t offset = 0;
PrintStringsToBuffer(printable_buf, &offset, sizeof(printable_buf), cbd.payload->buffer,
cbd.payload->offset);
jb_set_string(jb, "payload_printable", (char *)printable_buf);
jb_set_bool(jb, "complete", complete);
}
}

static void FrameAddPayloadUDP(JsonBuilder *js, const Packet *p, const Frame *frame)
Expand Down Expand Up @@ -223,8 +252,8 @@ static void FrameAddPayloadUDP(JsonBuilder *js, const Packet *p, const Frame *fr
/** \brief log a single frame
* \note ipproto argument is passed to assist static code analyzers
*/
void FrameJsonLogOneFrame(const uint8_t ipproto, const Frame *frame, const Flow *f,
const TcpStream *stream, const Packet *p, JsonBuilder *jb)
void FrameJsonLogOneFrame(const uint8_t ipproto, const Frame *frame, Flow *f,
const TcpStream *stream, const Packet *p, JsonBuilder *jb, MemBuffer *buffer)
{
DEBUG_VALIDATE_BUG_ON(ipproto != p->proto);
DEBUG_VALIDATE_BUG_ON(ipproto != f->proto);
Expand All @@ -249,7 +278,7 @@ void FrameJsonLogOneFrame(const uint8_t ipproto, const Frame *frame, const Flow
} else {
jb_set_uint(jb, "length", frame->len);
}
FrameAddPayloadTCP(jb, stream, frame);
FrameAddPayloadTCP(f, f->protoctx, stream, frame, jb, buffer);
} else {
jb_set_uint(jb, "length", frame->len);
FrameAddPayloadUDP(jb, p, frame);
Expand Down Expand Up @@ -287,7 +316,7 @@ static int FrameJsonUdp(
return TM_ECODE_OK;

jb_set_string(jb, "app_proto", AppProtoToString(f->alproto));
FrameJsonLogOneFrame(IPPROTO_UDP, frame, p->flow, NULL, p, jb);
FrameJsonLogOneFrame(IPPROTO_UDP, frame, p->flow, NULL, p, jb, aft->payload_buffer);
OutputJsonBuilderBuffer(jb, aft->ctx);
jb_free(jb);
frame->flags |= FRAME_FLAG_LOGGED;
Expand Down Expand Up @@ -359,7 +388,7 @@ static int FrameJson(ThreadVars *tv, JsonFrameLogThread *aft, const Packet *p)
return TM_ECODE_OK;

jb_set_string(jb, "app_proto", AppProtoToString(p->flow->alproto));
FrameJsonLogOneFrame(IPPROTO_TCP, frame, p->flow, stream, p, jb);
FrameJsonLogOneFrame(IPPROTO_TCP, frame, p->flow, stream, p, jb, aft->payload_buffer);
OutputJsonBuilderBuffer(jb, aft->ctx);
jb_free(jb);
frame->flags |= FRAME_FLAG_LOGGED;
Expand Down Expand Up @@ -482,8 +511,22 @@ static OutputInitResult JsonFrameLogInitCtxSub(ConfNode *conf, OutputCtx *parent
goto error;
}

uint32_t payload_buffer_size = 4096;
if (conf != NULL) {
const char *payload_buffer_value = ConfNodeLookupChildValue(conf, "payload-buffer-size");
if (payload_buffer_value != NULL) {
uint32_t value;
if (ParseSizeStringU32(payload_buffer_value, &value) < 0) {
SCLogError("Error parsing payload-buffer-size \"%s\"", payload_buffer_value);
goto error;
}
payload_buffer_size = value;
}
}

json_output_ctx->file_ctx = ajt->file_ctx;
json_output_ctx->eve_ctx = ajt;
json_output_ctx->payload_buffer_size = payload_buffer_size;

output_ctx->data = json_output_ctx;
output_ctx->DeInit = JsonFrameLogDeInitCtxSub;
Expand Down
Loading
Loading