suricata
output-json-anomaly.c
Go to the documentation of this file.
1 /* Copyright (C) 2019 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Jeff Lucovsky <jeff@lucovsky.org>
22  *
23  * Logs anomalies in JSON format.
24  *
25  */
26 
27 #include "suricata-common.h"
28 #include "debug.h"
29 #include "detect.h"
30 #include "flow.h"
31 #include "conf.h"
32 
33 #include "threads.h"
34 #include "tm-threads.h"
35 #include "threadvars.h"
36 #include "util-debug.h"
37 
38 #include "util-misc.h"
39 
40 #include "detect-parse.h"
41 #include "detect-engine.h"
42 #include "util-logopenfile.h"
43 
44 #include "output.h"
45 #include "output-json.h"
46 #include "output-json-anomaly.h"
47 
48 #include "util-byte.h"
49 #include "util-byte.h"
50 #include "util-privs.h"
51 #include "util-print.h"
52 #include "util-proto-name.h"
53 #include "util-optimize.h"
54 #include "util-buffer.h"
55 #include "util-crypt.h"
56 #include "util-validate.h"
57 
58 #define MODULE_NAME "JsonAnomalyLog"
59 
60 #ifdef HAVE_LIBJANSSON
61 
62 #define LOG_JSON_PACKETHDR BIT_U16(0)
63 
64 typedef struct AnomalyJsonOutputCtx_ {
65  LogFileCtx* file_ctx;
66  uint16_t flags;
67  OutputJsonCommonSettings cfg;
68 } AnomalyJsonOutputCtx;
69 
70 typedef struct JsonAnomalyLogThread_ {
71  /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */
72  LogFileCtx* file_ctx;
73  MemBuffer *json_buffer;
74  AnomalyJsonOutputCtx* json_output_ctx;
75 } JsonAnomalyLogThread;
76 
77 static int AnomalyJson(ThreadVars *tv, JsonAnomalyLogThread *aft, const Packet *p)
78 {
79  bool is_ip_pkt = PKT_IS_IPV4(p) || PKT_IS_IPV6(p);
80 
81  char timebuf[64];
82  CreateIsoTimeString(&p->ts, timebuf, sizeof(timebuf));
83 
84  for (int i = 0; i < p->events.cnt; i++) {
85  MemBufferReset(aft->json_buffer);
86 
87  json_t *js = CreateJSONHeader(p, LOG_DIR_PACKET, "anomaly");
88 
89  if (unlikely(js == NULL)) {
90  return TM_ECODE_OK;
91  }
92 
93  json_t *ajs = json_object();
94  if (unlikely(ajs == NULL)) {
95  json_decref(js);
96  return TM_ECODE_OK;
97  }
98 
99  if (!is_ip_pkt) {
100  json_object_set_new(js, "timestamp", json_string(timebuf));
101  } else {
102  JsonFiveTuple((const Packet *)p, LOG_DIR_PACKET, js);
103  JsonAddCommonOptions(&aft->json_output_ctx->cfg, p, p->flow, js);
104  }
105 
106  if (aft->json_output_ctx->flags & LOG_JSON_PACKETHDR) {
107  JsonPacket(p, js, GET_PKT_LEN(p) < 32 ? GET_PKT_LEN(p) : 32);
108  }
109 
110  uint8_t event_code = p->events.events[i];
111  if (event_code < DECODE_EVENT_MAX) {
112  const char *event = DEvents[event_code].event_name;
113  json_object_set_new(ajs, "type",
114  EVENT_IS_DECODER_PACKET_ERROR(event_code) ?
115  json_string("packet") : json_string("stream"));
116  json_object_set_new(ajs, "event", json_string(event));
117  } else {
118  /* include event code with unrecognized events */
119  uint32_t offset = 0;
120  char unknown_event_buf[8];
121  json_object_set_new(ajs, "type", json_string("unknown"));
122  PrintBufferData(unknown_event_buf, &offset, 8, "%d", event_code);
123  json_object_set_new(ajs, "code", json_string(unknown_event_buf));
124  }
125 
126  /* anomaly */
127  json_object_set_new(js, "anomaly", ajs);
128  OutputJSONBuffer(js, aft->file_ctx, &aft->json_buffer);
129 
130  json_object_clear(js);
131  json_decref(js);
132  }
133 
134  return TM_ECODE_OK;
135 }
136 
137 
138 static int JsonAnomalyLogger(ThreadVars *tv, void *thread_data, const Packet *p)
139 {
140  JsonAnomalyLogThread *aft = thread_data;
141  return AnomalyJson(tv, aft, p);
142 }
143 
144 static int JsonAnomalyLogCondition(ThreadVars *tv, const Packet *p)
145 {
146  return p->events.cnt > 0 ? TRUE : FALSE;
147 }
148 
149 #define OUTPUT_BUFFER_SIZE 65535
150 static TmEcode JsonAnomalyLogThreadInit(ThreadVars *t, const void *initdata, void **data)
151 {
152  JsonAnomalyLogThread *aft = SCCalloc(1, sizeof(JsonAnomalyLogThread));
153  if (unlikely(aft == NULL)) {
154  return TM_ECODE_FAILED;
155  }
156 
157  if(initdata == NULL) {
158  SCLogDebug("Error getting context for EveLogAnomaly. \"initdata\" argument NULL");
159  SCFree(aft);
160  return TM_ECODE_FAILED;
161  }
162 
163  aft->json_buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
164  if (aft->json_buffer == NULL) {
165  SCFree(aft);
166  return TM_ECODE_FAILED;
167  }
168 
169  /** Use the Output Context (file pointer and mutex) */
170  AnomalyJsonOutputCtx *json_output_ctx = ((OutputCtx *)initdata)->data;
171  aft->file_ctx = json_output_ctx->file_ctx;
172  aft->json_output_ctx = json_output_ctx;
173 
174  *data = (void *)aft;
175  return TM_ECODE_OK;
176 }
177 
178 static TmEcode JsonAnomalyLogThreadDeinit(ThreadVars *t, void *data)
179 {
180  JsonAnomalyLogThread *aft = (JsonAnomalyLogThread *)data;
181  if (aft == NULL) {
182  return TM_ECODE_OK;
183  }
184 
185  MemBufferFree(aft->json_buffer);
186 
187  /* clear memory */
188  memset(aft, 0, sizeof(JsonAnomalyLogThread));
189 
190  SCFree(aft);
191  return TM_ECODE_OK;
192 }
193 
194 static void JsonAnomalyLogDeInitCtx(OutputCtx *output_ctx)
195 {
196  AnomalyJsonOutputCtx *json_output_ctx = (AnomalyJsonOutputCtx *) output_ctx->data;
197  if (json_output_ctx != NULL) {
198  LogFileFreeCtx(json_output_ctx->file_ctx);
199  SCFree(json_output_ctx);
200  }
201  SCFree(output_ctx);
202 }
203 
204 static void JsonAnomalyLogDeInitCtxSub(OutputCtx *output_ctx)
205 {
206  SCLogDebug("cleaning up sub output_ctx %p", output_ctx);
207 
208  AnomalyJsonOutputCtx *json_output_ctx = (AnomalyJsonOutputCtx *) output_ctx->data;
209 
210  if (json_output_ctx != NULL) {
211  SCFree(json_output_ctx);
212  }
213  SCFree(output_ctx);
214 }
215 
216 #define DEFAULT_LOG_FILENAME "anomaly.json"
217 static void SetFlag(const ConfNode *conf, const char *name, uint16_t flag, uint16_t *out_flags)
218 {
219  DEBUG_VALIDATE_BUG_ON(conf == NULL);
220  const char *setting = ConfNodeLookupChildValue(conf, name);
221  if (setting != NULL) {
222  if (ConfValIsTrue(setting)) {
223  *out_flags |= flag;
224  } else {
225  *out_flags &= ~flag;
226  }
227  }
228 }
229 
230 static void JsonAnomalyLogConf(AnomalyJsonOutputCtx *json_output_ctx,
231  ConfNode *conf)
232 {
233  uint16_t flags = 0;
234  if (conf != NULL) {
235  SetFlag(conf, "packethdr", LOG_JSON_PACKETHDR, &flags);
236  }
237  json_output_ctx->flags |= flags;
238 }
239 
240 /**
241  * \brief Create a new LogFileCtx for "fast" output style.
242  * \param conf The configuration node for this output.
243  * \return A LogFileCtx pointer on success, NULL on failure.
244  */
245 static OutputInitResult JsonAnomalyLogInitCtx(ConfNode *conf)
246 {
247  OutputInitResult result = { NULL, false };
248  AnomalyJsonOutputCtx *json_output_ctx = NULL;
249  LogFileCtx *logfile_ctx = LogFileNewCtx();
250  if (logfile_ctx == NULL) {
251  SCLogDebug("JsonAnomalyLogInitCtx: Could not create new LogFileCtx");
252  return result;
253  }
254 
255  if (SCConfLogOpenGeneric(conf, logfile_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
256  LogFileFreeCtx(logfile_ctx);
257  return result;
258  }
259 
260  OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
261  if (unlikely(output_ctx == NULL)) {
262  LogFileFreeCtx(logfile_ctx);
263  return result;
264  }
265 
266  json_output_ctx = SCCalloc(1, sizeof(AnomalyJsonOutputCtx));
267  if (unlikely(json_output_ctx == NULL)) {
268  LogFileFreeCtx(logfile_ctx);
269  SCFree(output_ctx);
270  return result;
271  }
272 
273  json_output_ctx->file_ctx = logfile_ctx;
274  JsonAnomalyLogConf(json_output_ctx, conf);
275 
276  output_ctx->data = json_output_ctx;
277  output_ctx->DeInit = JsonAnomalyLogDeInitCtx;
278 
279  result.ctx = output_ctx;
280  result.ok = true;
281  return result;
282 }
283 
284 /**
285  * \brief Create a new LogFileCtx for "fast" output style.
286  * \param conf The configuration node for this output.
287  * \return A LogFileCtx pointer on success, NULL on failure.
288  */
289 static OutputInitResult JsonAnomalyLogInitCtxSub(ConfNode *conf, OutputCtx *parent_ctx)
290 {
291  OutputInitResult result = { NULL, false };
292  OutputJsonCtx *ajt = parent_ctx->data;
293  AnomalyJsonOutputCtx *json_output_ctx = NULL;
294 
295  OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
296  if (unlikely(output_ctx == NULL))
297  return result;
298 
299  json_output_ctx = SCCalloc(1, sizeof(AnomalyJsonOutputCtx));
300  if (unlikely(json_output_ctx == NULL)) {
301  goto error;
302  }
303 
304  json_output_ctx->file_ctx = ajt->file_ctx;
305  JsonAnomalyLogConf(json_output_ctx, conf);
306  json_output_ctx->cfg = ajt->cfg;
307 
308  output_ctx->data = json_output_ctx;
309  output_ctx->DeInit = JsonAnomalyLogDeInitCtxSub;
310 
311  result.ctx = output_ctx;
312  result.ok = true;
313  return result;
314 
315 error:
316  SCFree(output_ctx);
317 
318  return result;
319 }
320 
321 void JsonAnomalyLogRegister (void)
322 {
324  JsonAnomalyLogInitCtx, JsonAnomalyLogger, JsonAnomalyLogCondition,
325  JsonAnomalyLogThreadInit, JsonAnomalyLogThreadDeinit, NULL);
326 
328  "eve-log.anomaly", JsonAnomalyLogInitCtxSub, JsonAnomalyLogger,
329  JsonAnomalyLogCondition, JsonAnomalyLogThreadInit, JsonAnomalyLogThreadDeinit,
330  NULL);
331 }
332 
333 #else
334 
336 {
337 }
338 
339 #endif
MemBuffer * MemBufferCreateNew(uint32_t size)
Definition: util-buffer.c:32
uint16_t flags
#define SCLogDebug(...)
Definition: util-debug.h:335
struct Flow_ * flow
Definition: decode.h:443
#define FALSE
#define unlikely(expr)
Definition: util-optimize.h:35
#define MemBufferReset(mem_buffer)
Reset the mem buffer.
Definition: util-buffer.h:42
uint64_t offset
#define PKT_IS_IPV6(p)
Definition: decode.h:250
void(* DeInit)(struct OutputCtx_ *)
Definition: tm-modules.h:84
uint8_t events[PACKET_ENGINE_EVENT_MAX]
Definition: decode.h:305
#define TRUE
#define PKT_IS_IPV4(p)
Definition: decode.h:249
#define SCCalloc(nm, a)
Definition: util-mem.h:197
const char * ConfNodeLookupChildValue(const ConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:843
void OutputRegisterPacketModule(LoggerId id, const char *name, const char *conf_name, OutputInitFunc InitFunc, PacketLogger PacketLogFunc, PacketLogCondition PacketConditionFunc, ThreadInitFunc ThreadInit, ThreadDeinitFunc ThreadDeinit, ThreadExitPrintStatsFunc ThreadExitPrintStats)
Register a packet output module.
Definition: output.c:159
void CreateIsoTimeString(const struct timeval *ts, char *str, size_t size)
Definition: util-time.c:179
void OutputRegisterPacketSubModule(LoggerId id, const char *parent_name, const char *name, const char *conf_name, OutputInitSubFunc InitFunc, PacketLogger PacketLogFunc, PacketLogCondition PacketConditionFunc, ThreadInitFunc ThreadInit, ThreadDeinitFunc ThreadDeinit, ThreadExitPrintStatsFunc ThreadExitPrintStats)
Register a packet output sub-module.
Definition: output.c:200
void JsonAnomalyLogRegister(void)
#define PrintBufferData(buf, buf_offset_ptr, buf_size,...)
Definition: util-print.h:29
PacketEngineEvents events
Definition: decode.h:565
LogFileCtx * LogFileNewCtx(void)
LogFileNewCtx() Get a new LogFileCtx.
int SCConfLogOpenGeneric(ConfNode *conf, LogFileCtx *log_ctx, const char *default_filename, int rotate)
open a generic output "log file", which may be a regular file or a socket
const struct DecodeEvents_ DEvents[]
Definition: decode-events.c:29
#define MODULE_NAME
int ConfValIsTrue(const char *val)
Check if a value is true.
Definition: conf.c:566
Definition: conf.h:32
OutputCtx * ctx
Definition: output.h:42
#define DEFAULT_LOG_FILENAME
int LogFileFreeCtx(LogFileCtx *lf_ctx)
LogFileFreeCtx() Destroy a LogFileCtx (Close the file and free memory)
const char * event_name
#define SCFree(a)
Definition: util-mem.h:228
void * data
Definition: tm-modules.h:81
#define EVENT_IS_DECODER_PACKET_ERROR(e)
Per thread variable structure.
Definition: threadvars.h:57
struct timeval ts
Definition: decode.h:449
#define GET_PKT_LEN(p)
Definition: decode.h:222
#define OUTPUT_BUFFER_SIZE
Definition: log-httplog.c:58
void MemBufferFree(MemBuffer *buffer)
Definition: util-buffer.c:82
#define DEBUG_VALIDATE_BUG_ON(exp)