|
|
|
/* Copyright (C) 2013-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
|
|
|
|
* Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* version 2 along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
|
|
* 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \file
|
|
|
|
*
|
|
|
|
* \author Tom DeCanio <td@npulsetech.com>
|
|
|
|
*
|
|
|
|
* Logs alerts in JSON format.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "suricata-common.h"
|
|
|
|
#include "packet.h"
|
|
|
|
#include "detect.h"
|
|
|
|
#include "flow.h"
|
|
|
|
#include "conf.h"
|
|
|
|
|
|
|
|
#include "stream.h"
|
|
|
|
#include "threadvars.h"
|
|
|
|
#include "util-debug.h"
|
|
|
|
#include "stream-tcp.h"
|
|
|
|
|
|
|
|
#include "util-logopenfile.h"
|
|
|
|
#include "util-misc.h"
|
|
|
|
#include "util-time.h"
|
|
|
|
|
|
|
|
#include "detect-engine.h"
|
|
|
|
#include "detect-metadata.h"
|
|
|
|
#include "app-layer-parser.h"
|
|
|
|
#include "app-layer-htp-xff.h"
|
|
|
|
#include "app-layer-ftp.h"
|
|
|
|
#include "app-layer-frames.h"
|
|
|
|
#include "log-pcap.h"
|
|
|
|
|
|
|
|
#include "output.h"
|
|
|
|
#include "output-json.h"
|
|
|
|
#include "output-json-alert.h"
|
|
|
|
#include "output-json-http.h"
|
|
|
|
#include "rust.h"
|
|
|
|
#include "output-json-smtp.h"
|
|
|
|
#include "output-json-email-common.h"
|
|
|
|
#include "output-json-nfs.h"
|
|
|
|
#include "output-json-smb.h"
|
|
|
|
#include "output-json-flow.h"
|
|
|
|
#include "output-json-ike.h"
|
|
|
|
#include "output-json-frame.h"
|
|
|
|
|
|
|
|
#include "util-print.h"
|
|
|
|
#include "util-optimize.h"
|
|
|
|
#include "util-buffer.h"
|
|
|
|
#include "util-reference-config.h"
|
|
|
|
#include "util-validate.h"
|
|
|
|
|
|
|
|
#include "action-globals.h"
|
|
|
|
|
|
|
|
#define MODULE_NAME "JsonAlertLog"
|
|
|
|
|
|
|
|
#define LOG_JSON_PAYLOAD BIT_U16(0)
|
|
|
|
#define LOG_JSON_PACKET BIT_U16(1)
|
|
|
|
#define LOG_JSON_PAYLOAD_BASE64 BIT_U16(2)
|
|
|
|
#define LOG_JSON_TAGGED_PACKETS BIT_U16(3)
|
|
|
|
#define LOG_JSON_APP_LAYER BIT_U16(4)
|
|
|
|
#define LOG_JSON_FLOW BIT_U16(5)
|
|
|
|
#define LOG_JSON_HTTP_BODY BIT_U16(6)
|
|
|
|
#define LOG_JSON_HTTP_BODY_BASE64 BIT_U16(7)
|
|
|
|
#define LOG_JSON_RULE_METADATA BIT_U16(8)
|
eve/alert: include rule text in alert output
For SIEM analysis it is often useful to refer to the actual rules to
find out why a specific alert has been triggered when the signature
message does not convey enough information.
Turn on the new rule flag to include the rule text in eve alert output.
The feature is turned off by default.
With a rule like this:
alert dns $HOME_NET any -> 8.8.8.8 any (msg:"Google DNS server contacted"; sid:42;)
The eve alert output might look something like this (pretty-printed for
readability):
{
"timestamp": "2017-08-14T12:35:05.830812+0200",
"flow_id": 1919856770919772,
"in_iface": "eth0",
"event_type": "alert",
"src_ip": "10.20.30.40",
"src_port": 50968,
"dest_ip": "8.8.8.8",
"dest_port": 53,
"proto": "UDP",
"alert": {
"action": "allowed",
"gid": 1,
"signature_id": 42,
"rev": 0,
"signature": "Google DNS server contacted",
"category": "",
"severity": 3,
"rule": "alert dns $HOME_NET any -> 8.8.8.8 any (msg:\"Google DNS server contacted\"; sid:43;)"
},
"app_proto": "dns",
"flow": {
"pkts_toserver": 1,
"pkts_toclient": 0,
"bytes_toserver": 81,
"bytes_toclient": 0,
"start": "2017-08-14T12:35:05.830812+0200"
}
}
Feature #2020
8 years ago
|
|
|
#define LOG_JSON_RULE BIT_U16(9)
|
|
|
|
#define LOG_JSON_VERDICT BIT_U16(10)
|
|
|
|
#define LOG_JSON_WEBSOCKET_PAYLOAD BIT_U16(11)
|
|
|
|
#define LOG_JSON_WEBSOCKET_PAYLOAD_BASE64 BIT_U16(12)
|
|
|
|
#define LOG_JSON_PAYLOAD_LENGTH BIT_U16(13)
|
|
|
|
#define LOG_JSON_REFERENCE BIT_U16(14)
|
|
|
|
|
|
|
|
#define METADATA_DEFAULTS ( LOG_JSON_FLOW | \
|
|
|
|
LOG_JSON_APP_LAYER | \
|
|
|
|
LOG_JSON_RULE_METADATA)
|
|
|
|
|
|
|
|
#define JSON_BODY_LOGGING \
|
|
|
|
(LOG_JSON_HTTP_BODY | LOG_JSON_HTTP_BODY_BASE64 | LOG_JSON_WEBSOCKET_PAYLOAD | \
|
|
|
|
LOG_JSON_WEBSOCKET_PAYLOAD_BASE64)
|
|
|
|
|
|
|
|
#define JSON_STREAM_BUFFER_SIZE 4096
|
|
|
|
|
|
|
|
typedef struct AlertJsonOutputCtx_ {
|
|
|
|
LogFileCtx* file_ctx;
|
|
|
|
uint16_t flags;
|
|
|
|
uint32_t payload_buffer_size;
|
|
|
|
HttpXFFCfg *xff_cfg;
|
|
|
|
HttpXFFCfg *parent_xff_cfg;
|
|
|
|
OutputJsonCtx *eve_ctx;
|
|
|
|
} AlertJsonOutputCtx;
|
|
|
|
|
|
|
|
typedef struct JsonAlertLogThread_ {
|
|
|
|
MemBuffer *payload_buffer;
|
|
|
|
AlertJsonOutputCtx* json_output_ctx;
|
|
|
|
OutputJsonThreadCtx *ctx;
|
|
|
|
} JsonAlertLogThread;
|
|
|
|
|
|
|
|
static void AlertJsonSourceTarget(
|
|
|
|
const Packet *p, const PacketAlert *pa, SCJsonBuilder *js, JsonAddrInfo *addr)
|
output-json-alert: output source and target
Use metadata provided information to output the Source and Target
in the definition of IDMEF.
The output is now the following:
"alert": {
"action": "allowed",
"gid": 1,
"signature_id": 1,
"rev": 1,
"signature": "connection to home",
"category": "",
"severity": 3,
"source": {
"ip": "2001:31d0:000a:f68a:0000:0000:0000:0001",
"port": 80
},
"target": {
"ip": "2a01:0e34:ee97:b130:c685:08ff:dab3:c9c8",
"port": 48390
}
9 years ago
|
|
|
{
|
|
|
|
SCJbOpenObject(js, "source");
|
|
|
|
if (pa->s->flags & SIG_FLAG_DEST_IS_TARGET) {
|
|
|
|
SCJbSetString(js, "ip", addr->src_ip);
|
|
|
|
switch (p->proto) {
|
|
|
|
case IPPROTO_ICMP:
|
|
|
|
case IPPROTO_ICMPV6:
|
|
|
|
break;
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
case IPPROTO_TCP:
|
|
|
|
case IPPROTO_SCTP:
|
|
|
|
SCJbSetUint(js, "port", addr->sp);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (pa->s->flags & SIG_FLAG_SRC_IS_TARGET) {
|
|
|
|
SCJbSetString(js, "ip", addr->dst_ip);
|
|
|
|
switch (p->proto) {
|
|
|
|
case IPPROTO_ICMP:
|
|
|
|
case IPPROTO_ICMPV6:
|
|
|
|
break;
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
case IPPROTO_TCP:
|
|
|
|
case IPPROTO_SCTP:
|
|
|
|
SCJbSetUint(js, "port", addr->dp);
|
|
|
|
break;
|
|
|
|
}
|
output-json-alert: output source and target
Use metadata provided information to output the Source and Target
in the definition of IDMEF.
The output is now the following:
"alert": {
"action": "allowed",
"gid": 1,
"signature_id": 1,
"rev": 1,
"signature": "connection to home",
"category": "",
"severity": 3,
"source": {
"ip": "2001:31d0:000a:f68a:0000:0000:0000:0001",
"port": 80
},
"target": {
"ip": "2a01:0e34:ee97:b130:c685:08ff:dab3:c9c8",
"port": 48390
}
9 years ago
|
|
|
}
|
|
|
|
SCJbClose(js);
|
output-json-alert: output source and target
Use metadata provided information to output the Source and Target
in the definition of IDMEF.
The output is now the following:
"alert": {
"action": "allowed",
"gid": 1,
"signature_id": 1,
"rev": 1,
"signature": "connection to home",
"category": "",
"severity": 3,
"source": {
"ip": "2001:31d0:000a:f68a:0000:0000:0000:0001",
"port": 80
},
"target": {
"ip": "2a01:0e34:ee97:b130:c685:08ff:dab3:c9c8",
"port": 48390
}
9 years ago
|
|
|
|
|
|
|
SCJbOpenObject(js, "target");
|
output-json-alert: output source and target
Use metadata provided information to output the Source and Target
in the definition of IDMEF.
The output is now the following:
"alert": {
"action": "allowed",
"gid": 1,
"signature_id": 1,
"rev": 1,
"signature": "connection to home",
"category": "",
"severity": 3,
"source": {
"ip": "2001:31d0:000a:f68a:0000:0000:0000:0001",
"port": 80
},
"target": {
"ip": "2a01:0e34:ee97:b130:c685:08ff:dab3:c9c8",
"port": 48390
}
9 years ago
|
|
|
if (pa->s->flags & SIG_FLAG_DEST_IS_TARGET) {
|
|
|
|
SCJbSetString(js, "ip", addr->dst_ip);
|
output-json-alert: output source and target
Use metadata provided information to output the Source and Target
in the definition of IDMEF.
The output is now the following:
"alert": {
"action": "allowed",
"gid": 1,
"signature_id": 1,
"rev": 1,
"signature": "connection to home",
"category": "",
"severity": 3,
"source": {
"ip": "2001:31d0:000a:f68a:0000:0000:0000:0001",
"port": 80
},
"target": {
"ip": "2a01:0e34:ee97:b130:c685:08ff:dab3:c9c8",
"port": 48390
}
9 years ago
|
|
|
switch (p->proto) {
|
|
|
|
case IPPROTO_ICMP:
|
|
|
|
case IPPROTO_ICMPV6:
|
|
|
|
break;
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
case IPPROTO_TCP:
|
|
|
|
case IPPROTO_SCTP:
|
|
|
|
SCJbSetUint(js, "port", addr->dp);
|
output-json-alert: output source and target
Use metadata provided information to output the Source and Target
in the definition of IDMEF.
The output is now the following:
"alert": {
"action": "allowed",
"gid": 1,
"signature_id": 1,
"rev": 1,
"signature": "connection to home",
"category": "",
"severity": 3,
"source": {
"ip": "2001:31d0:000a:f68a:0000:0000:0000:0001",
"port": 80
},
"target": {
"ip": "2a01:0e34:ee97:b130:c685:08ff:dab3:c9c8",
"port": 48390
}
9 years ago
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (pa->s->flags & SIG_FLAG_SRC_IS_TARGET) {
|
|
|
|
SCJbSetString(js, "ip", addr->src_ip);
|
output-json-alert: output source and target
Use metadata provided information to output the Source and Target
in the definition of IDMEF.
The output is now the following:
"alert": {
"action": "allowed",
"gid": 1,
"signature_id": 1,
"rev": 1,
"signature": "connection to home",
"category": "",
"severity": 3,
"source": {
"ip": "2001:31d0:000a:f68a:0000:0000:0000:0001",
"port": 80
},
"target": {
"ip": "2a01:0e34:ee97:b130:c685:08ff:dab3:c9c8",
"port": 48390
}
9 years ago
|
|
|
switch (p->proto) {
|
|
|
|
case IPPROTO_ICMP:
|
|
|
|
case IPPROTO_ICMPV6:
|
|
|
|
break;
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
case IPPROTO_TCP:
|
|
|
|
case IPPROTO_SCTP:
|
|
|
|
SCJbSetUint(js, "port", addr->sp);
|
output-json-alert: output source and target
Use metadata provided information to output the Source and Target
in the definition of IDMEF.
The output is now the following:
"alert": {
"action": "allowed",
"gid": 1,
"signature_id": 1,
"rev": 1,
"signature": "connection to home",
"category": "",
"severity": 3,
"source": {
"ip": "2001:31d0:000a:f68a:0000:0000:0000:0001",
"port": 80
},
"target": {
"ip": "2a01:0e34:ee97:b130:c685:08ff:dab3:c9c8",
"port": 48390
}
9 years ago
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SCJbClose(js);
|
output-json-alert: output source and target
Use metadata provided information to output the Source and Target
in the definition of IDMEF.
The output is now the following:
"alert": {
"action": "allowed",
"gid": 1,
"signature_id": 1,
"rev": 1,
"signature": "connection to home",
"category": "",
"severity": 3,
"source": {
"ip": "2001:31d0:000a:f68a:0000:0000:0000:0001",
"port": 80
},
"target": {
"ip": "2a01:0e34:ee97:b130:c685:08ff:dab3:c9c8",
"port": 48390
}
9 years ago
|
|
|
}
|
|
|
|
|
|
|
|
static void AlertJsonReference(const PacketAlert *pa, SCJsonBuilder *jb)
|
|
|
|
{
|
|
|
|
if (!pa->s->references) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const DetectReference *kv = pa->s->references;
|
|
|
|
SCJbOpenArray(jb, "references");
|
|
|
|
while (kv) {
|
|
|
|
/* Note that the key and reference sizes have been bound
|
|
|
|
* checked during parsing
|
|
|
|
*/
|
|
|
|
const size_t size_needed = kv->key_len + kv->reference_len + 1;
|
|
|
|
char kv_store[size_needed];
|
|
|
|
snprintf(kv_store, size_needed, "%s%s", kv->key, kv->reference);
|
|
|
|
SCJbAppendString(jb, kv_store);
|
|
|
|
kv = kv->next;
|
|
|
|
}
|
|
|
|
SCJbClose(jb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AlertJsonMetadata(const PacketAlert *pa, SCJsonBuilder *js)
|
|
|
|
{
|
|
|
|
if (pa->s->metadata && pa->s->metadata->json_str) {
|
|
|
|
SCJbSetFormatted(js, pa->s->metadata->json_str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AlertJsonHeader(const Packet *p, const PacketAlert *pa, SCJsonBuilder *js, uint16_t flags,
|
|
|
|
JsonAddrInfo *addr, char *xff_buffer)
|
|
|
|
{
|
|
|
|
const char *action = "allowed";
|
|
|
|
/* use packet action if rate_filter modified the action */
|
|
|
|
if (unlikely(pa->flags & PACKET_ALERT_FLAG_RATE_FILTER_MODIFIED)) {
|
|
|
|
if (PacketCheckAction(p, ACTION_DROP_REJECT)) {
|
|
|
|
action = "blocked";
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (pa->action & ACTION_REJECT_ANY) {
|
|
|
|
action = "blocked";
|
|
|
|
} else if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) {
|
|
|
|
action = "blocked";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add tx_id to root element for correlation with other events. */
|
|
|
|
/* json_object_del(js, "tx_id"); */
|
|
|
|
if (pa->flags & PACKET_ALERT_FLAG_TX) {
|
|
|
|
SCJbSetUint(js, "tx_id", pa->tx_id);
|
|
|
|
}
|
|
|
|
if (pa->flags & PACKET_ALERT_FLAG_TX_GUESSED) {
|
|
|
|
SCJbSetBool(js, "tx_guessed", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
SCJbOpenObject(js, "alert");
|
|
|
|
|
|
|
|
SCJbSetString(js, "action", action);
|
|
|
|
SCJbSetUint(js, "gid", pa->s->gid);
|
|
|
|
SCJbSetUint(js, "signature_id", pa->s->id);
|
|
|
|
SCJbSetUint(js, "rev", pa->s->rev);
|
|
|
|
/* TODO: SCJsonBuilder should handle unprintable characters like
|
|
|
|
* SCJsonString. */
|
|
|
|
SCJbSetString(js, "signature", pa->s->msg ? pa->s->msg : "");
|
|
|
|
SCJbSetString(js, "category", pa->s->class_msg ? pa->s->class_msg : "");
|
|
|
|
SCJbSetUint(js, "severity", pa->s->prio);
|
|
|
|
|
|
|
|
if (p->tenant_id > 0) {
|
|
|
|
SCJbSetUint(js, "tenant_id", p->tenant_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (addr && pa->s->flags & SIG_FLAG_HAS_TARGET) {
|
|
|
|
AlertJsonSourceTarget(p, pa, js, addr);
|
output-json-alert: output source and target
Use metadata provided information to output the Source and Target
in the definition of IDMEF.
The output is now the following:
"alert": {
"action": "allowed",
"gid": 1,
"signature_id": 1,
"rev": 1,
"signature": "connection to home",
"category": "",
"severity": 3,
"source": {
"ip": "2001:31d0:000a:f68a:0000:0000:0000:0001",
"port": 80
},
"target": {
"ip": "2a01:0e34:ee97:b130:c685:08ff:dab3:c9c8",
"port": 48390
}
9 years ago
|
|
|
}
|
|
|
|
|
|
|
|
if ((flags & LOG_JSON_REFERENCE)) {
|
|
|
|
AlertJsonReference(pa, js);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & LOG_JSON_RULE_METADATA) {
|
|
|
|
AlertJsonMetadata(pa, js);
|
|
|
|
}
|
|
|
|
|
datajson: introduce feature
This patch introduces new option to dataset keyword.
Where regular dataset allows match from sets, dataset with json
format allows the same but also adds JSON data to the alert
event. This data is coming from the set definition it self.
For example, an ipv4 set will look like:
[{"ip": "10.16.1.11", "test": "success","context":3}]
The syntax is a JSON array but it can also be a JSON object
with an array inside. The idea is to directly used data coming
from the API of a threat intel management software.
The syntax of the keyword is the following:
dataset:isset,src_ip,type ip,load src.lst,format json, \
enrichment_key src_ip, value_key ip;
Compare to dataset, it just have a supplementary option key
that is used to indicate in which subobject the JSON value
should be added.
The information is added in the even under the alert.extra
subobject:
"alert": {
"extra": {
"src_ip": {
"ip": "10.6.1.11",
"test": "success",
"context": 3
},
The main interest of the feature is to be able to contextualize
a match. For example, if you have an IOC source, you can do
[
{"buffer": "value1", "actor":"APT28","Country":"FR"},
{"buffer": "value2", "actor":"APT32","Country":"NL"}
]
This way, a single dataset is able to produce context to the
event where it was not possible before and multiple signatures
had to be used.
The format introduced in datajson is an evolution of the
historical datarep format. This has some limitations. For example,
if a user fetch IOCs from a threat intel server there is a large
change that the format will be JSON or XML. Suricata has no support
for the second but can support the first one.
Keeping the key value may seem redundant but it is useful to have it
directly accessible in the extra data to be able to query it
independantly of the signature (where it can be multiple metadata
or even be a transformed metadata).
In some case, when interacting with data (mostly coming from
threat intel servers), the JSON array containing the data
to use is not at the root of the object and it is ncessary
to access a subobject.
This patch implements this with support of key in level1.level2.
This is done via the `array_key` option that contains the path
to the data.
Ticket: #7372
5 months ago
|
|
|
if (pa->json_info.json_string != NULL) {
|
|
|
|
SCJbOpenObject(js, "extra");
|
|
|
|
const struct ExtraDataJsonList *json_info = &pa->json_info;
|
|
|
|
while (json_info) {
|
|
|
|
SCJbSetFormatted(js, json_info->json_string);
|
|
|
|
json_info = json_info->next;
|
|
|
|
}
|
|
|
|
SCJbClose(js);
|
|
|
|
}
|
|
|
|
if (flags & LOG_JSON_RULE) {
|
|
|
|
SCJbSetString(js, "rule", pa->s->sig_str);
|
|
|
|
}
|
|
|
|
if (xff_buffer && xff_buffer[0]) {
|
|
|
|
SCJbSetString(js, "xff", xff_buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
SCJbClose(js);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AlertJsonTunnel(const Packet *p, SCJsonBuilder *js)
|
|
|
|
{
|
|
|
|
if (p->root == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SCJbOpenObject(js, "tunnel");
|
|
|
|
|
|
|
|
enum PktSrcEnum pkt_src;
|
|
|
|
uint64_t pcap_cnt;
|
|
|
|
JsonAddrInfo addr = json_addr_info_zero;
|
|
|
|
JsonAddrInfoInit(p->root, 0, &addr);
|
|
|
|
pcap_cnt = p->root->pcap_cnt;
|
|
|
|
pkt_src = p->root->pkt_src;
|
|
|
|
|
|
|
|
SCJbSetString(js, "src_ip", addr.src_ip);
|
|
|
|
SCJbSetUint(js, "src_port", addr.sp);
|
|
|
|
SCJbSetString(js, "dest_ip", addr.dst_ip);
|
|
|
|
SCJbSetUint(js, "dest_port", addr.dp);
|
|
|
|
SCJbSetString(js, "proto", addr.proto);
|
|
|
|
|
|
|
|
SCJbSetUint(js, "depth", p->recursion_level);
|
|
|
|
if (pcap_cnt != 0) {
|
|
|
|
SCJbSetUint(js, "pcap_cnt", pcap_cnt);
|
|
|
|
}
|
|
|
|
SCJbSetString(js, "pkt_src", PktSrcToString(pkt_src));
|
|
|
|
SCJbClose(js);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AlertAddPayload(AlertJsonOutputCtx *json_output_ctx, SCJsonBuilder *js, const Packet *p)
|
|
|
|
{
|
|
|
|
if (json_output_ctx->flags & LOG_JSON_PAYLOAD_BASE64) {
|
|
|
|
SCJbSetBase64(js, "payload", p->payload, p->payload_len);
|
|
|
|
}
|
|
|
|
if (json_output_ctx->flags & LOG_JSON_PAYLOAD_LENGTH) {
|
|
|
|
SCJbSetUint(js, "payload_length", p->payload_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (json_output_ctx->flags & LOG_JSON_PAYLOAD) {
|
|
|
|
uint8_t printable_buf[p->payload_len + 1];
|
|
|
|
uint32_t offset = 0;
|
|
|
|
PrintStringsToBuffer(printable_buf, &offset,
|
|
|
|
p->payload_len + 1,
|
|
|
|
p->payload, p->payload_len);
|
|
|
|
printable_buf[p->payload_len] = '\0';
|
|
|
|
SCJbSetString(js, "payload_printable", (char *)printable_buf);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AlertAddAppLayer(
|
|
|
|
const Packet *p, SCJsonBuilder *jb, const uint64_t tx_id, const uint16_t option_flags)
|
|
|
|
{
|
|
|
|
const AppProto proto = FlowGetAppProtocol(p->flow);
|
|
|
|
EveJsonSimpleAppLayerLogger *al = SCEveJsonSimpleGetLogger(proto);
|
|
|
|
SCJsonBuilderMark mark = { 0, 0, 0 };
|
|
|
|
if (al && al->LogTx) {
|
|
|
|
void *state = FlowGetAppState(p->flow);
|
|
|
|
if (state) {
|
|
|
|
void *tx = AppLayerParserGetTx(p->flow->proto, proto, state, tx_id);
|
|
|
|
if (tx) {
|
|
|
|
const int ts =
|
|
|
|
AppLayerParserGetStateProgress(p->flow->proto, proto, tx, STREAM_TOSERVER);
|
|
|
|
const int tc =
|
|
|
|
AppLayerParserGetStateProgress(p->flow->proto, proto, tx, STREAM_TOCLIENT);
|
|
|
|
SCJbSetString(jb, "ts_progress",
|
|
|
|
AppLayerParserGetStateNameById(p->flow->proto, proto, ts, STREAM_TOSERVER));
|
|
|
|
SCJbSetString(jb, "tc_progress",
|
|
|
|
AppLayerParserGetStateNameById(p->flow->proto, proto, tc, STREAM_TOCLIENT));
|
|
|
|
SCJbGetMark(jb, &mark);
|
|
|
|
switch (proto) {
|
|
|
|
// first check some protocols need special options for alerts logging
|
|
|
|
case ALPROTO_WEBSOCKET:
|
|
|
|
if (option_flags &
|
|
|
|
(LOG_JSON_WEBSOCKET_PAYLOAD | LOG_JSON_WEBSOCKET_PAYLOAD_BASE64)) {
|
|
|
|
bool pp = (option_flags & LOG_JSON_WEBSOCKET_PAYLOAD) != 0;
|
|
|
|
bool pb64 = (option_flags & LOG_JSON_WEBSOCKET_PAYLOAD_BASE64) != 0;
|
|
|
|
if (!SCWebSocketLogDetails(tx, jb, pp, pb64)) {
|
|
|
|
SCJbRestoreMark(jb, &mark);
|
|
|
|
}
|
|
|
|
// nothing more to log or do
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!al->LogTx(tx, jb)) {
|
|
|
|
SCJbRestoreMark(jb, &mark);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
void *state = FlowGetAppState(p->flow);
|
|
|
|
if (state) {
|
|
|
|
void *tx = AppLayerParserGetTx(p->flow->proto, proto, state, tx_id);
|
|
|
|
if (tx) {
|
|
|
|
const int ts =
|
|
|
|
AppLayerParserGetStateProgress(p->flow->proto, proto, tx, STREAM_TOSERVER);
|
|
|
|
const int tc =
|
|
|
|
AppLayerParserGetStateProgress(p->flow->proto, proto, tx, STREAM_TOCLIENT);
|
|
|
|
SCJbSetString(jb, "ts_progress",
|
|
|
|
AppLayerParserGetStateNameById(p->flow->proto, proto, ts, STREAM_TOSERVER));
|
|
|
|
SCJbSetString(jb, "tc_progress",
|
|
|
|
AppLayerParserGetStateNameById(p->flow->proto, proto, tc, STREAM_TOCLIENT));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch (proto) {
|
|
|
|
case ALPROTO_HTTP1:
|
|
|
|
// TODO: Could result in an empty http object being logged.
|
|
|
|
SCJbOpenObject(jb, "http");
|
|
|
|
if (EveHttpAddMetadata(p->flow, tx_id, jb)) {
|
|
|
|
if (option_flags & LOG_JSON_HTTP_BODY) {
|
|
|
|
EveHttpLogJSONBodyPrintable(jb, p->flow, tx_id);
|
|
|
|
}
|
|
|
|
if (option_flags & LOG_JSON_HTTP_BODY_BASE64) {
|
|
|
|
EveHttpLogJSONBodyBase64(jb, p->flow, tx_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SCJbClose(jb);
|
|
|
|
break;
|
|
|
|
case ALPROTO_SMTP:
|
|
|
|
SCJbGetMark(jb, &mark);
|
|
|
|
SCJbOpenObject(jb, "smtp");
|
|
|
|
if (EveSMTPAddMetadata(p->flow, tx_id, jb)) {
|
|
|
|
SCJbClose(jb);
|
|
|
|
} else {
|
|
|
|
SCJbRestoreMark(jb, &mark);
|
|
|
|
}
|
|
|
|
SCJbGetMark(jb, &mark);
|
|
|
|
SCJbOpenObject(jb, "email");
|
|
|
|
if (EveEmailAddMetadata(p->flow, tx_id, jb)) {
|
|
|
|
SCJbClose(jb);
|
|
|
|
} else {
|
|
|
|
SCJbRestoreMark(jb, &mark);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ALPROTO_NFS:
|
|
|
|
/* rpc */
|
|
|
|
SCJbGetMark(jb, &mark);
|
|
|
|
SCJbOpenObject(jb, "rpc");
|
|
|
|
if (EveNFSAddMetadataRPC(p->flow, tx_id, jb)) {
|
|
|
|
SCJbClose(jb);
|
|
|
|
} else {
|
|
|
|
SCJbRestoreMark(jb, &mark);
|
|
|
|
}
|
|
|
|
/* nfs */
|
|
|
|
SCJbGetMark(jb, &mark);
|
|
|
|
SCJbOpenObject(jb, "nfs");
|
|
|
|
if (EveNFSAddMetadata(p->flow, tx_id, jb)) {
|
|
|
|
SCJbClose(jb);
|
|
|
|
} else {
|
|
|
|
SCJbRestoreMark(jb, &mark);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ALPROTO_SMB:
|
|
|
|
SCJbGetMark(jb, &mark);
|
|
|
|
SCJbOpenObject(jb, "smb");
|
|
|
|
if (EveSMBAddMetadata(p->flow, tx_id, jb)) {
|
|
|
|
SCJbClose(jb);
|
|
|
|
} else {
|
|
|
|
SCJbRestoreMark(jb, &mark);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ALPROTO_IKE:
|
|
|
|
SCJbGetMark(jb, &mark);
|
|
|
|
if (!EveIKEAddMetadata(p->flow, tx_id, jb)) {
|
|
|
|
SCJbRestoreMark(jb, &mark);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ALPROTO_DCERPC: {
|
|
|
|
if (state) {
|
|
|
|
void *tx = AppLayerParserGetTx(p->flow->proto, proto, state, tx_id);
|
|
|
|
if (tx) {
|
|
|
|
SCJbGetMark(jb, &mark);
|
|
|
|
SCJbOpenObject(jb, "dcerpc");
|
|
|
|
if (p->proto == IPPROTO_TCP) {
|
|
|
|
if (!SCDcerpcLogJsonRecordTcp(state, tx, jb)) {
|
|
|
|
SCJbRestoreMark(jb, &mark);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!SCDcerpcLogJsonRecordUdp(state, tx, jb)) {
|
|
|
|
SCJbRestoreMark(jb, &mark);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SCJbClose(jb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AlertAddFiles(const Packet *p, SCJsonBuilder *jb, const uint64_t tx_id)
|
|
|
|
{
|
|
|
|
const uint8_t direction =
|
|
|
|
(p->flowflags & FLOW_PKT_TOSERVER) ? STREAM_TOSERVER : STREAM_TOCLIENT;
|
|
|
|
FileContainer *ffc = NULL;
|
|
|
|
if (p->flow->alstate != NULL) {
|
|
|
|
void *tx = AppLayerParserGetTx(p->flow->proto, p->flow->alproto, p->flow->alstate, tx_id);
|
|
|
|
if (tx) {
|
|
|
|
AppLayerGetFileState files = AppLayerParserGetTxFiles(p->flow, tx, direction);
|
|
|
|
ffc = files.fc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ffc != NULL) {
|
|
|
|
File *file = ffc->head;
|
|
|
|
bool isopen = false;
|
|
|
|
while (file) {
|
|
|
|
if (!isopen) {
|
|
|
|
isopen = true;
|
|
|
|
SCJbOpenArray(jb, "files");
|
|
|
|
}
|
|
|
|
SCJbStartObject(jb);
|
|
|
|
EveFileInfo(jb, file, tx_id, file->flags);
|
|
|
|
SCJbClose(jb);
|
|
|
|
file = file->next;
|
|
|
|
}
|
|
|
|
if (isopen) {
|
|
|
|
SCJbClose(jb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AlertAddFrame(
|
|
|
|
const Packet *p, const int64_t frame_id, SCJsonBuilder *jb, MemBuffer *buffer)
|
|
|
|
{
|
|
|
|
if (p->flow == NULL || (p->proto == IPPROTO_TCP && p->flow->protoctx == NULL))
|
|
|
|
return;
|
|
|
|
|
|
|
|
FramesContainer *frames_container = AppLayerFramesGetContainer(p->flow);
|
|
|
|
if (frames_container == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Frames *frames = NULL;
|
|
|
|
TcpStream *stream = NULL;
|
|
|
|
if (p->proto == IPPROTO_TCP) {
|
|
|
|
TcpSession *ssn = p->flow->protoctx;
|
|
|
|
if (PKT_IS_TOSERVER(p)) {
|
|
|
|
stream = &ssn->client;
|
|
|
|
frames = &frames_container->toserver;
|
|
|
|
} else {
|
|
|
|
stream = &ssn->server;
|
|
|
|
frames = &frames_container->toclient;
|
|
|
|
}
|
|
|
|
Frame *frame = FrameGetById(frames, frame_id);
|
|
|
|
if (frame != NULL) {
|
|
|
|
FrameJsonLogOneFrame(IPPROTO_TCP, frame, p->flow, stream, p, jb, buffer);
|
|
|
|
}
|
|
|
|
} else if (p->proto == IPPROTO_UDP) {
|
|
|
|
if (PKT_IS_TOSERVER(p)) {
|
|
|
|
frames = &frames_container->toserver;
|
|
|
|
} else {
|
|
|
|
frames = &frames_container->toclient;
|
|
|
|
}
|
|
|
|
Frame *frame = FrameGetById(frames, frame_id);
|
|
|
|
if (frame != NULL) {
|
|
|
|
FrameJsonLogOneFrame(IPPROTO_UDP, frame, p->flow, NULL, p, jb, buffer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Build verdict object
|
|
|
|
*
|
|
|
|
* \param p Pointer to Packet current being logged
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
void EveAddVerdict(SCJsonBuilder *jb, const Packet *p)
|
|
|
|
{
|
|
|
|
SCJbOpenObject(jb, "verdict");
|
|
|
|
|
|
|
|
/* add verdict info */
|
|
|
|
if (PacketCheckAction(p, ACTION_REJECT_ANY)) {
|
|
|
|
// check rule to define type of reject packet sent
|
|
|
|
if (EngineModeIsIPS()) {
|
|
|
|
JB_SET_STRING(jb, "action", "drop");
|
|
|
|
} else {
|
|
|
|
JB_SET_STRING(jb, "action", "alert");
|
|
|
|
}
|
|
|
|
if (PacketCheckAction(p, ACTION_REJECT)) {
|
|
|
|
JB_SET_STRING(jb, "reject-target", "to_client");
|
|
|
|
} else if (PacketCheckAction(p, ACTION_REJECT_DST)) {
|
|
|
|
JB_SET_STRING(jb, "reject-target", "to_server");
|
|
|
|
} else if (PacketCheckAction(p, ACTION_REJECT_BOTH)) {
|
|
|
|
JB_SET_STRING(jb, "reject-target", "both");
|
|
|
|
}
|
|
|
|
SCJbOpenArray(jb, "reject");
|
|
|
|
switch (p->proto) {
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
case IPPROTO_ICMP:
|
|
|
|
case IPPROTO_ICMPV6:
|
|
|
|
SCJbAppendString(jb, "icmp-prohib");
|
|
|
|
break;
|
|
|
|
case IPPROTO_TCP:
|
|
|
|
SCJbAppendString(jb, "tcp-reset");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
SCJbClose(jb);
|
|
|
|
|
|
|
|
} else if (PacketCheckAction(p, ACTION_DROP) && EngineModeIsIPS()) {
|
|
|
|
JB_SET_STRING(jb, "action", "drop");
|
|
|
|
} else if (PacketCheckAction(p, ACTION_ACCEPT)) {
|
|
|
|
JB_SET_STRING(jb, "action", "accept");
|
|
|
|
} else if (p->alerts.alerts[p->alerts.cnt].action & ACTION_PASS) {
|
|
|
|
JB_SET_STRING(jb, "action", "pass");
|
|
|
|
} else {
|
|
|
|
// TODO make sure we don't have a situation where this wouldn't work
|
|
|
|
JB_SET_STRING(jb, "action", "alert");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Close verdict */
|
|
|
|
SCJbClose(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, SCJsonBuilder *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) {
|
|
|
|
SCJbSetBase64(jb, "payload", cbd.payload->buffer, cbd.payload->offset);
|
|
|
|
}
|
|
|
|
if (json_output_ctx->flags & LOG_JSON_PAYLOAD_LENGTH) {
|
|
|
|
SCJbSetUint(jb, "payload_length", 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, cbd.payload->offset + 1,
|
|
|
|
cbd.payload->buffer, cbd.payload->offset);
|
|
|
|
SCJbSetString(jb, "payload_printable", (char *)printable_buf);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int AlertJson(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p)
|
|
|
|
{
|
|
|
|
AlertJsonOutputCtx *json_output_ctx = aft->json_output_ctx;
|
|
|
|
|
|
|
|
if (p->alerts.cnt == 0 && !(p->flags & PKT_HAS_TAG))
|
|
|
|
return TM_ECODE_OK;
|
|
|
|
|
|
|
|
for (int i = 0; i < p->alerts.cnt; i++) {
|
|
|
|
const PacketAlert *pa = &p->alerts.alerts[i];
|
|
|
|
if (unlikely(pa->s == NULL)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* First initialize the address info (5-tuple). */
|
|
|
|
JsonAddrInfo addr = json_addr_info_zero;
|
|
|
|
JsonAddrInfoInit(p, LOG_DIR_PACKET, &addr);
|
|
|
|
|
|
|
|
/* Check for XFF, overwriting address info if needed. */
|
|
|
|
HttpXFFCfg *xff_cfg = json_output_ctx->xff_cfg != NULL ? json_output_ctx->xff_cfg
|
|
|
|
: json_output_ctx->parent_xff_cfg;
|
|
|
|
int have_xff_ip = 0;
|
|
|
|
char xff_buffer[XFF_MAXLEN];
|
|
|
|
xff_buffer[0] = 0;
|
|
|
|
if ((xff_cfg != NULL) && !(xff_cfg->flags & XFF_DISABLED) && p->flow != NULL) {
|
|
|
|
if (FlowGetAppProtocol(p->flow) == ALPROTO_HTTP1) {
|
|
|
|
if (pa->flags & PACKET_ALERT_FLAG_TX) {
|
|
|
|
have_xff_ip = HttpXFFGetIPFromTx(p->flow, pa->tx_id, xff_cfg,
|
|
|
|
xff_buffer, XFF_MAXLEN);
|
|
|
|
} else {
|
|
|
|
have_xff_ip = HttpXFFGetIP(p->flow, xff_cfg, xff_buffer,
|
|
|
|
XFF_MAXLEN);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (have_xff_ip && xff_cfg->flags & XFF_OVERWRITE) {
|
|
|
|
if (p->flowflags & FLOW_PKT_TOCLIENT) {
|
|
|
|
strlcpy(addr.dst_ip, xff_buffer, JSON_ADDR_LEN);
|
|
|
|
} else {
|
|
|
|
strlcpy(addr.src_ip, xff_buffer, JSON_ADDR_LEN);
|
|
|
|
}
|
|
|
|
/* Clear have_xff_ip so the xff field does not get
|
|
|
|
* logged below. */
|
|
|
|
have_xff_ip = false;
|
|
|
|
}
|
|
|
|
if (have_xff_ip && !(xff_cfg->flags & XFF_EXTRADATA)) {
|
|
|
|
// reset xff_buffer so as not to log it
|
|
|
|
xff_buffer[0] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SCJsonBuilder *jb =
|
|
|
|
CreateEveHeader(p, LOG_DIR_PACKET, "alert", &addr, json_output_ctx->eve_ctx);
|
|
|
|
if (unlikely(jb == NULL))
|
|
|
|
return TM_ECODE_OK;
|
|
|
|
|
|
|
|
|
|
|
|
/* alert */
|
|
|
|
AlertJsonHeader(p, pa, jb, json_output_ctx->flags, &addr, xff_buffer);
|
|
|
|
|
|
|
|
if (PacketIsTunnel(p)) {
|
|
|
|
AlertJsonTunnel(p, jb);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p->flow != NULL) {
|
|
|
|
if (pa->flags & PACKET_ALERT_FLAG_TX) {
|
|
|
|
if (json_output_ctx->flags & LOG_JSON_APP_LAYER) {
|
|
|
|
AlertAddAppLayer(p, jb, pa->tx_id, json_output_ctx->flags);
|
|
|
|
}
|
|
|
|
/* including fileinfo data is configured by the metadata setting */
|
|
|
|
if (json_output_ctx->flags & LOG_JSON_RULE_METADATA) {
|
|
|
|
AlertAddFiles(p, jb, pa->tx_id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
EveAddAppProto(p->flow, jb);
|
|
|
|
|
|
|
|
if (p->flowflags & FLOW_PKT_TOSERVER) {
|
|
|
|
SCJbSetString(jb, "direction", "to_server");
|
|
|
|
} else {
|
|
|
|
SCJbSetString(jb, "direction", "to_client");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (json_output_ctx->flags & LOG_JSON_FLOW) {
|
|
|
|
SCJbOpenObject(jb, "flow");
|
|
|
|
EveAddFlow(p->flow, jb);
|
eve/alert: add src and dest info to flow in alert
When looking at an alert event, it was impossible to determine which
side from src or dest IP in the alert was the client and wich side
was the server with regards to the underlying flow. This was a problem
when you try to known who belongs a metadata property such as a HTTP
hostname or a TLS JA3.
This patch updates the code to add src and dest IP in the flow
subobject as well as src and dst port. This way, we can now which
side is the client and which side is the server.
The result is looking like:
{
"event_type": "alert",
"src_ip": "22.47.184.196",
"src_port": 81,
"dest_ip": "192.168.1.47",
"dest_port": 1063,
"proto": "TCP",
"tx_id": 0,
"alert": {
"signature_id": 2018959,
"rev": 3,
},
"app_proto": "http",
"flow": {
"pkts_toserver": 22,
"pkts_toclient": 35,
"bytes_toserver": 1370,
"bytes_toclient": 48852,
"start": "2009-10-28T10:01:46.755232+0100",
"src_ip": "192.168.1.47",
"dest_ip": "22.47.184.196",
"src_port": 1063,
"dest_port": 81
}
}
3 years ago
|
|
|
if (p->flowflags & FLOW_PKT_TOCLIENT) {
|
|
|
|
SCJbSetString(jb, "src_ip", addr.dst_ip);
|
|
|
|
SCJbSetString(jb, "dest_ip", addr.src_ip);
|
eve/alert: add src and dest info to flow in alert
When looking at an alert event, it was impossible to determine which
side from src or dest IP in the alert was the client and wich side
was the server with regards to the underlying flow. This was a problem
when you try to known who belongs a metadata property such as a HTTP
hostname or a TLS JA3.
This patch updates the code to add src and dest IP in the flow
subobject as well as src and dst port. This way, we can now which
side is the client and which side is the server.
The result is looking like:
{
"event_type": "alert",
"src_ip": "22.47.184.196",
"src_port": 81,
"dest_ip": "192.168.1.47",
"dest_port": 1063,
"proto": "TCP",
"tx_id": 0,
"alert": {
"signature_id": 2018959,
"rev": 3,
},
"app_proto": "http",
"flow": {
"pkts_toserver": 22,
"pkts_toclient": 35,
"bytes_toserver": 1370,
"bytes_toclient": 48852,
"start": "2009-10-28T10:01:46.755232+0100",
"src_ip": "192.168.1.47",
"dest_ip": "22.47.184.196",
"src_port": 1063,
"dest_port": 81
}
}
3 years ago
|
|
|
if (addr.sp > 0) {
|
|
|
|
SCJbSetUint(jb, "src_port", addr.dp);
|
|
|
|
SCJbSetUint(jb, "dest_port", addr.sp);
|
eve/alert: add src and dest info to flow in alert
When looking at an alert event, it was impossible to determine which
side from src or dest IP in the alert was the client and wich side
was the server with regards to the underlying flow. This was a problem
when you try to known who belongs a metadata property such as a HTTP
hostname or a TLS JA3.
This patch updates the code to add src and dest IP in the flow
subobject as well as src and dst port. This way, we can now which
side is the client and which side is the server.
The result is looking like:
{
"event_type": "alert",
"src_ip": "22.47.184.196",
"src_port": 81,
"dest_ip": "192.168.1.47",
"dest_port": 1063,
"proto": "TCP",
"tx_id": 0,
"alert": {
"signature_id": 2018959,
"rev": 3,
},
"app_proto": "http",
"flow": {
"pkts_toserver": 22,
"pkts_toclient": 35,
"bytes_toserver": 1370,
"bytes_toclient": 48852,
"start": "2009-10-28T10:01:46.755232+0100",
"src_ip": "192.168.1.47",
"dest_ip": "22.47.184.196",
"src_port": 1063,
"dest_port": 81
}
}
3 years ago
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SCJbSetString(jb, "src_ip", addr.src_ip);
|
|
|
|
SCJbSetString(jb, "dest_ip", addr.dst_ip);
|
eve/alert: add src and dest info to flow in alert
When looking at an alert event, it was impossible to determine which
side from src or dest IP in the alert was the client and wich side
was the server with regards to the underlying flow. This was a problem
when you try to known who belongs a metadata property such as a HTTP
hostname or a TLS JA3.
This patch updates the code to add src and dest IP in the flow
subobject as well as src and dst port. This way, we can now which
side is the client and which side is the server.
The result is looking like:
{
"event_type": "alert",
"src_ip": "22.47.184.196",
"src_port": 81,
"dest_ip": "192.168.1.47",
"dest_port": 1063,
"proto": "TCP",
"tx_id": 0,
"alert": {
"signature_id": 2018959,
"rev": 3,
},
"app_proto": "http",
"flow": {
"pkts_toserver": 22,
"pkts_toclient": 35,
"bytes_toserver": 1370,
"bytes_toclient": 48852,
"start": "2009-10-28T10:01:46.755232+0100",
"src_ip": "192.168.1.47",
"dest_ip": "22.47.184.196",
"src_port": 1063,
"dest_port": 81
}
}
3 years ago
|
|
|
if (addr.sp > 0) {
|
|
|
|
SCJbSetUint(jb, "src_port", addr.sp);
|
|
|
|
SCJbSetUint(jb, "dest_port", addr.dp);
|
eve/alert: add src and dest info to flow in alert
When looking at an alert event, it was impossible to determine which
side from src or dest IP in the alert was the client and wich side
was the server with regards to the underlying flow. This was a problem
when you try to known who belongs a metadata property such as a HTTP
hostname or a TLS JA3.
This patch updates the code to add src and dest IP in the flow
subobject as well as src and dst port. This way, we can now which
side is the client and which side is the server.
The result is looking like:
{
"event_type": "alert",
"src_ip": "22.47.184.196",
"src_port": 81,
"dest_ip": "192.168.1.47",
"dest_port": 1063,
"proto": "TCP",
"tx_id": 0,
"alert": {
"signature_id": 2018959,
"rev": 3,
},
"app_proto": "http",
"flow": {
"pkts_toserver": 22,
"pkts_toclient": 35,
"bytes_toserver": 1370,
"bytes_toclient": 48852,
"start": "2009-10-28T10:01:46.755232+0100",
"src_ip": "192.168.1.47",
"dest_ip": "22.47.184.196",
"src_port": 1063,
"dest_port": 81
}
}
3 years ago
|
|
|
}
|
|
|
|
}
|
|
|
|
SCJbClose(jb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* payload */
|
|
|
|
if (json_output_ctx->flags &
|
|
|
|
(LOG_JSON_PAYLOAD | LOG_JSON_PAYLOAD_BASE64 | LOG_JSON_PAYLOAD_LENGTH)) {
|
|
|
|
int stream = (p->proto == IPPROTO_TCP) ?
|
|
|
|
(pa->flags & (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_STREAM_MATCH) ?
|
|
|
|
1 : 0) : 0;
|
|
|
|
// should be impossible, as stream implies flow
|
|
|
|
DEBUG_VALIDATE_BUG_ON(stream && p->flow == NULL);
|
|
|
|
|
|
|
|
/* Is this a stream? If so, pack part of it into the payload field */
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* This is a single packet and not a stream */
|
|
|
|
AlertAddPayload(json_output_ctx, jb, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
SCJbSetUint(jb, "stream", stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pa->flags & PACKET_ALERT_FLAG_FRAME) {
|
|
|
|
AlertAddFrame(p, pa->frame_id, jb, aft->payload_buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* base64-encoded full packet */
|
|
|
|
if (json_output_ctx->flags & LOG_JSON_PACKET) {
|
|
|
|
EvePacket(p, jb, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *pcap_filename = PcapLogGetFilename();
|
|
|
|
if (pcap_filename != NULL) {
|
|
|
|
SCJbSetString(jb, "capture_file", pcap_filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (json_output_ctx->flags & LOG_JSON_VERDICT) {
|
|
|
|
EveAddVerdict(jb, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
OutputJsonBuilderBuffer(tv, p, p->flow, jb, aft->ctx);
|
|
|
|
SCJbFree(jb);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((p->flags & PKT_HAS_TAG) && (json_output_ctx->flags &
|
|
|
|
LOG_JSON_TAGGED_PACKETS)) {
|
|
|
|
SCJsonBuilder *packetjs =
|
|
|
|
CreateEveHeader(p, LOG_DIR_PACKET, "packet", NULL, json_output_ctx->eve_ctx);
|
|
|
|
if (unlikely(packetjs != NULL)) {
|
|
|
|
EvePacket(p, packetjs, 0);
|
|
|
|
OutputJsonBuilderBuffer(tv, p, p->flow, packetjs, aft->ctx);
|
|
|
|
SCJbFree(packetjs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return TM_ECODE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int AlertJsonDecoderEvent(ThreadVars *tv, JsonAlertLogThread *aft, const Packet *p)
|
|
|
|
{
|
|
|
|
AlertJsonOutputCtx *json_output_ctx = aft->json_output_ctx;
|
|
|
|
|
|
|
|
if (p->alerts.cnt == 0)
|
|
|
|
return TM_ECODE_OK;
|
|
|
|
|
|
|
|
for (int i = 0; i < p->alerts.cnt; i++) {
|
|
|
|
const PacketAlert *pa = &p->alerts.alerts[i];
|
|
|
|
if (unlikely(pa->s == NULL)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
SCJsonBuilder *jb =
|
|
|
|
CreateEveHeader(p, LOG_DIR_PACKET, "alert", NULL, json_output_ctx->eve_ctx);
|
|
|
|
if (unlikely(jb == NULL))
|
|
|
|
return TM_ECODE_OK;
|
|
|
|
|
|
|
|
AlertJsonHeader(p, pa, jb, json_output_ctx->flags, NULL, NULL);
|
|
|
|
|
|
|
|
if (PacketIsTunnel(p)) {
|
|
|
|
AlertJsonTunnel(p, jb);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* base64-encoded full packet */
|
|
|
|
if (json_output_ctx->flags & LOG_JSON_PACKET) {
|
|
|
|
EvePacket(p, jb, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *pcap_filename = PcapLogGetFilename();
|
|
|
|
if (pcap_filename != NULL) {
|
|
|
|
SCJbSetString(jb, "capture_file", pcap_filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (json_output_ctx->flags & LOG_JSON_VERDICT) {
|
|
|
|
EveAddVerdict(jb, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
OutputJsonBuilderBuffer(tv, p, p->flow, jb, aft->ctx);
|
|
|
|
SCJbFree(jb);
|
|
|
|
}
|
|
|
|
|
|
|
|
return TM_ECODE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int JsonAlertFlush(ThreadVars *tv, void *thread_data, const Packet *p)
|
|
|
|
{
|
|
|
|
JsonAlertLogThread *aft = thread_data;
|
|
|
|
SCLogDebug("%s flushing %s", tv->name, ((LogFileCtx *)(aft->ctx->file_ctx))->filename);
|
|
|
|
OutputJsonFlush(aft->ctx);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int JsonAlertLogger(ThreadVars *tv, void *thread_data, const Packet *p)
|
|
|
|
{
|
|
|
|
JsonAlertLogThread *aft = thread_data;
|
|
|
|
|
|
|
|
if (PacketIsIPv4(p) || PacketIsIPv6(p)) {
|
|
|
|
return AlertJson(tv, aft, p);
|
|
|
|
} else if (p->alerts.cnt > 0) {
|
|
|
|
return AlertJsonDecoderEvent(tv, aft, p);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool JsonAlertLogCondition(ThreadVars *tv, void *thread_data, const Packet *p)
|
|
|
|
{
|
|
|
|
return (p->alerts.cnt || (p->flags & PKT_HAS_TAG));
|
|
|
|
}
|
|
|
|
|
|
|
|
static TmEcode JsonAlertLogThreadInit(ThreadVars *t, const void *initdata, void **data)
|
|
|
|
{
|
|
|
|
JsonAlertLogThread *aft = SCCalloc(1, sizeof(JsonAlertLogThread));
|
|
|
|
if (unlikely(aft == NULL))
|
|
|
|
return TM_ECODE_FAILED;
|
|
|
|
|
|
|
|
if (initdata == NULL)
|
|
|
|
{
|
|
|
|
SCLogDebug("Error getting context for EveLogAlert. \"initdata\" argument NULL");
|
|
|
|
goto error_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Use the Output Context (file pointer and mutex) */
|
|
|
|
AlertJsonOutputCtx *json_output_ctx = ((OutputCtx *)initdata)->data;
|
|
|
|
|
|
|
|
aft->payload_buffer = MemBufferCreateNew(json_output_ctx->payload_buffer_size);
|
|
|
|
if (aft->payload_buffer == NULL) {
|
|
|
|
goto error_exit;
|
|
|
|
}
|
|
|
|
aft->ctx = CreateEveThreadCtx(t, json_output_ctx->eve_ctx);
|
|
|
|
if (!aft->ctx) {
|
|
|
|
goto error_exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
aft->json_output_ctx = json_output_ctx;
|
|
|
|
|
|
|
|
*data = (void *)aft;
|
|
|
|
return TM_ECODE_OK;
|
|
|
|
|
|
|
|
error_exit:
|
|
|
|
if (aft->payload_buffer != NULL) {
|
|
|
|
MemBufferFree(aft->payload_buffer);
|
|
|
|
}
|
|
|
|
SCFree(aft);
|
|
|
|
return TM_ECODE_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static TmEcode JsonAlertLogThreadDeinit(ThreadVars *t, void *data)
|
|
|
|
{
|
|
|
|
JsonAlertLogThread *aft = (JsonAlertLogThread *)data;
|
|
|
|
if (aft == NULL) {
|
|
|
|
return TM_ECODE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
MemBufferFree(aft->payload_buffer);
|
|
|
|
FreeEveThreadCtx(aft->ctx);
|
|
|
|
|
|
|
|
/* clear memory */
|
|
|
|
memset(aft, 0, sizeof(JsonAlertLogThread));
|
|
|
|
|
|
|
|
SCFree(aft);
|
|
|
|
return TM_ECODE_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void JsonAlertLogDeInitCtxSub(OutputCtx *output_ctx)
|
|
|
|
{
|
|
|
|
SCLogDebug("cleaning up sub output_ctx %p", output_ctx);
|
|
|
|
|
|
|
|
AlertJsonOutputCtx *json_output_ctx = (AlertJsonOutputCtx *) output_ctx->data;
|
|
|
|
|
|
|
|
if (json_output_ctx != NULL) {
|
|
|
|
HttpXFFCfg *xff_cfg = json_output_ctx->xff_cfg;
|
|
|
|
if (xff_cfg != NULL) {
|
|
|
|
SCFree(xff_cfg);
|
|
|
|
}
|
|
|
|
SCFree(json_output_ctx);
|
|
|
|
}
|
|
|
|
SCFree(output_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SetFlag(const SCConfNode *conf, const char *name, uint16_t flag, uint16_t *out_flags)
|
|
|
|
{
|
|
|
|
DEBUG_VALIDATE_BUG_ON(conf == NULL);
|
|
|
|
const char *setting = SCConfNodeLookupChildValue(conf, name);
|
|
|
|
if (setting != NULL) {
|
|
|
|
if (SCConfValIsTrue(setting)) {
|
|
|
|
*out_flags |= flag;
|
|
|
|
} else {
|
|
|
|
*out_flags &= ~flag;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void JsonAlertLogSetupMetadata(AlertJsonOutputCtx *json_output_ctx, SCConfNode *conf)
|
|
|
|
{
|
|
|
|
static bool warn_no_meta = false;
|
|
|
|
uint32_t payload_buffer_size = JSON_STREAM_BUFFER_SIZE;
|
|
|
|
uint16_t flags = METADATA_DEFAULTS;
|
|
|
|
|
|
|
|
if (conf != NULL) {
|
|
|
|
/* Check for metadata to enable/disable. */
|
|
|
|
SCConfNode *metadata = SCConfNodeLookupChild(conf, "metadata");
|
|
|
|
if (metadata != NULL) {
|
|
|
|
if (metadata->val != NULL && SCConfValIsFalse(metadata->val)) {
|
|
|
|
flags &= ~METADATA_DEFAULTS;
|
|
|
|
} else if (SCConfNodeHasChildren(metadata)) {
|
|
|
|
SCConfNode *rule_metadata = SCConfNodeLookupChild(metadata, "rule");
|
|
|
|
if (rule_metadata) {
|
|
|
|
SetFlag(rule_metadata, "raw", LOG_JSON_RULE, &flags);
|
|
|
|
SetFlag(rule_metadata, "metadata", LOG_JSON_RULE_METADATA,
|
|
|
|
&flags);
|
|
|
|
SetFlag(rule_metadata, "reference", LOG_JSON_REFERENCE, &flags);
|
|
|
|
}
|
|
|
|
SetFlag(metadata, "flow", LOG_JSON_FLOW, &flags);
|
|
|
|
SetFlag(metadata, "app-layer", LOG_JSON_APP_LAYER, &flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Non-metadata toggles. */
|
|
|
|
SetFlag(conf, "payload", LOG_JSON_PAYLOAD_BASE64, &flags);
|
|
|
|
SetFlag(conf, "packet", LOG_JSON_PACKET, &flags);
|
|
|
|
SetFlag(conf, "tagged-packets", LOG_JSON_TAGGED_PACKETS, &flags);
|
|
|
|
SetFlag(conf, "payload-printable", LOG_JSON_PAYLOAD, &flags);
|
|
|
|
SetFlag(conf, "http-body-printable", LOG_JSON_HTTP_BODY, &flags);
|
|
|
|
SetFlag(conf, "http-body", LOG_JSON_HTTP_BODY_BASE64, &flags);
|
|
|
|
SetFlag(conf, "websocket-payload-printable", LOG_JSON_WEBSOCKET_PAYLOAD, &flags);
|
|
|
|
SetFlag(conf, "websocket-payload", LOG_JSON_WEBSOCKET_PAYLOAD_BASE64, &flags);
|
|
|
|
SetFlag(conf, "verdict", LOG_JSON_VERDICT, &flags);
|
|
|
|
SetFlag(conf, "payload-length", LOG_JSON_PAYLOAD_LENGTH, &flags);
|
|
|
|
|
|
|
|
/* Check for obsolete flags and warn that they have no effect. */
|
|
|
|
static const char *deprecated_flags[] = { "http", "tls", "ssh", "smtp", "dnp3", "app-layer",
|
|
|
|
"flow", NULL };
|
|
|
|
for (int i = 0; deprecated_flags[i] != NULL; i++) {
|
|
|
|
if (SCConfNodeLookupChildValue(conf, deprecated_flags[i]) != NULL) {
|
|
|
|
SCLogWarning("Found deprecated eve-log.alert flag \"%s\", this flag has no effect",
|
|
|
|
deprecated_flags[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *payload_buffer_value = SCConfNodeLookupChildValue(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. Killing engine",
|
|
|
|
payload_buffer_value);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
} else if (value == 0) {
|
|
|
|
// you should not ask for payload if you want 0 of it
|
|
|
|
SCLogError("Error payload-buffer-size should not be 0");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
} else {
|
|
|
|
payload_buffer_size = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!warn_no_meta && flags & JSON_BODY_LOGGING) {
|
|
|
|
if (((flags & LOG_JSON_APP_LAYER) == 0)) {
|
|
|
|
SCLogWarning("HTTP body logging has been configured, however, "
|
|
|
|
"metadata logging has not been enabled. HTTP body logging will be "
|
|
|
|
"disabled.");
|
|
|
|
flags &= ~JSON_BODY_LOGGING;
|
|
|
|
warn_no_meta = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flags & LOG_JSON_RULE_METADATA) {
|
|
|
|
DetectEngineSetParseMetadata();
|
|
|
|
}
|
|
|
|
|
|
|
|
json_output_ctx->payload_buffer_size = payload_buffer_size;
|
|
|
|
json_output_ctx->flags |= flags;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HttpXFFCfg *JsonAlertLogGetXffCfg(SCConfNode *conf)
|
|
|
|
{
|
|
|
|
HttpXFFCfg *xff_cfg = NULL;
|
|
|
|
if (conf != NULL && SCConfNodeLookupChild(conf, "xff") != NULL) {
|
|
|
|
xff_cfg = SCCalloc(1, sizeof(HttpXFFCfg));
|
|
|
|
if (likely(xff_cfg != NULL)) {
|
|
|
|
HttpXFFGetCfg(conf, xff_cfg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return xff_cfg;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Create a new LogFileCtx for "fast" output style.
|
|
|
|
* \param conf The configuration node for this output.
|
|
|
|
* \return A LogFileCtx pointer on success, NULL on failure.
|
|
|
|
*/
|
|
|
|
static OutputInitResult JsonAlertLogInitCtxSub(SCConfNode *conf, OutputCtx *parent_ctx)
|
|
|
|
{
|
|
|
|
OutputInitResult result = { NULL, false };
|
|
|
|
OutputJsonCtx *ajt = parent_ctx->data;
|
|
|
|
AlertJsonOutputCtx *json_output_ctx = NULL;
|
|
|
|
|
|
|
|
OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
|
|
|
|
if (unlikely(output_ctx == NULL))
|
|
|
|
return result;
|
|
|
|
|
|
|
|
json_output_ctx = SCCalloc(1, sizeof(AlertJsonOutputCtx));
|
|
|
|
if (unlikely(json_output_ctx == NULL)) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
json_output_ctx->file_ctx = ajt->file_ctx;
|
|
|
|
json_output_ctx->eve_ctx = ajt;
|
|
|
|
|
|
|
|
JsonAlertLogSetupMetadata(json_output_ctx, conf);
|
|
|
|
json_output_ctx->xff_cfg = JsonAlertLogGetXffCfg(conf);
|
|
|
|
if (json_output_ctx->xff_cfg == NULL) {
|
|
|
|
json_output_ctx->parent_xff_cfg = ajt->xff_cfg;
|
|
|
|
}
|
|
|
|
|
|
|
|
output_ctx->data = json_output_ctx;
|
|
|
|
output_ctx->DeInit = JsonAlertLogDeInitCtxSub;
|
|
|
|
|
|
|
|
result.ctx = output_ctx;
|
|
|
|
result.ok = true;
|
|
|
|
return result;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (json_output_ctx != NULL) {
|
|
|
|
SCFree(json_output_ctx);
|
|
|
|
}
|
|
|
|
if (output_ctx != NULL) {
|
|
|
|
SCFree(output_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void JsonAlertLogRegister (void)
|
|
|
|
{
|
|
|
|
OutputPacketLoggerFunctions output_logger_functions = {
|
|
|
|
.LogFunc = JsonAlertLogger,
|
|
|
|
.FlushFunc = JsonAlertFlush,
|
|
|
|
.ConditionFunc = JsonAlertLogCondition,
|
|
|
|
.ThreadInitFunc = JsonAlertLogThreadInit,
|
|
|
|
.ThreadDeinitFunc = JsonAlertLogThreadDeinit,
|
|
|
|
.ThreadExitPrintStatsFunc = NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
OutputRegisterPacketSubModule(LOGGER_JSON_ALERT, "eve-log", MODULE_NAME, "eve-log.alert",
|
|
|
|
JsonAlertLogInitCtxSub, &output_logger_functions);
|
|
|
|
}
|