onos-1.4.0-rc1 tag
[onosfw.git] / framework / src / suricata / src / output-json-drop.c
1 /* Copyright (C) 2007-2013 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 Tom DeCanio <td@npulsetech.com>
22  *
23  * JSON Drop log module to log the dropped packet information
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 "decode-ipv4.h"
39 #include "detect.h"
40 #include "detect-parse.h"
41 #include "detect-engine.h"
42 #include "detect-engine-mpm.h"
43 #include "detect-reference.h"
44
45 #include "output.h"
46 #include "output-json.h"
47 #include "output-json-alert.h"
48
49 #include "util-unittest.h"
50 #include "util-unittest-helper.h"
51 #include "util-classification-config.h"
52 #include "util-privs.h"
53 #include "util-print.h"
54 #include "util-proto-name.h"
55 #include "util-logopenfile.h"
56 #include "util-time.h"
57 #include "util-buffer.h"
58
59 #define MODULE_NAME "JsonDropLog"
60
61 #ifdef HAVE_LIBJANSSON
62 #include <jansson.h>
63
64 #define LOG_DROP_ALERTS 1
65
66 typedef struct JsonDropOutputCtx_ {
67     LogFileCtx *file_ctx;
68     uint8_t flags;
69 } JsonDropOutputCtx;
70
71 typedef struct JsonDropLogThread_ {
72     JsonDropOutputCtx *drop_ctx;
73     MemBuffer *buffer;
74 } JsonDropLogThread;
75
76 /**
77  * \brief   Log the dropped packets in netfilter format when engine is running
78  *          in inline mode
79  *
80  * \param tv    Pointer the current thread variables
81  * \param p     Pointer the packet which is being logged
82  *
83  * \return return TM_EODE_OK on success
84  */
85 static int DropLogJSON (JsonDropLogThread *aft, const Packet *p)
86 {
87     uint16_t proto = 0;
88     MemBuffer *buffer = (MemBuffer *)aft->buffer;
89     json_t *js = CreateJSONHeader((Packet *)p, 0, "drop");//TODO const
90     if (unlikely(js == NULL))
91         return TM_ECODE_OK;
92
93     json_t *djs = json_object();
94     if (unlikely(djs == NULL)) {
95         json_decref(js);
96         return TM_ECODE_OK;
97     }
98
99     /* reset */
100     MemBufferReset(buffer);
101
102     if (PKT_IS_IPV4(p)) {
103         json_object_set_new(djs, "len", json_integer(IPV4_GET_IPLEN(p)));
104         json_object_set_new(djs, "tos", json_integer(IPV4_GET_IPTOS(p)));
105         json_object_set_new(djs, "ttl", json_integer(IPV4_GET_IPTTL(p)));
106         json_object_set_new(djs, "ipid", json_integer(IPV4_GET_IPID(p)));
107         proto = IPV4_GET_IPPROTO(p);
108     } else if (PKT_IS_IPV6(p)) {
109         json_object_set_new(djs, "len", json_integer(IPV6_GET_PLEN(p)));
110         json_object_set_new(djs, "tc", json_integer(IPV6_GET_CLASS(p)));
111         json_object_set_new(djs, "hoplimit", json_integer(IPV6_GET_HLIM(p)));
112         json_object_set_new(djs, "flowlbl", json_integer(IPV6_GET_FLOW(p)));
113         proto = IPV6_GET_L4PROTO(p);
114     }
115     switch (proto) {
116         case IPPROTO_TCP:
117             json_object_set_new(djs, "tcpseq", json_integer(TCP_GET_SEQ(p)));
118             json_object_set_new(djs, "tcpack", json_integer(TCP_GET_ACK(p)));
119             json_object_set_new(djs, "tcpwin", json_integer(TCP_GET_WINDOW(p)));
120             json_object_set_new(djs, "syn", TCP_ISSET_FLAG_SYN(p) ? json_true() : json_false());
121             json_object_set_new(djs, "ack", TCP_ISSET_FLAG_ACK(p) ? json_true() : json_false());
122             json_object_set_new(djs, "psh", TCP_ISSET_FLAG_PUSH(p) ? json_true() : json_false());
123             json_object_set_new(djs, "rst", TCP_ISSET_FLAG_RST(p) ? json_true() : json_false());
124             json_object_set_new(djs, "urg", TCP_ISSET_FLAG_URG(p) ? json_true() : json_false());
125             json_object_set_new(djs, "fin", TCP_ISSET_FLAG_FIN(p) ? json_true() : json_false());
126             json_object_set_new(djs, "tcpres", json_integer(TCP_GET_RAW_X2(p->tcph)));
127             json_object_set_new(djs, "tcpurgp", json_integer(TCP_GET_URG_POINTER(p)));
128             break;
129         case IPPROTO_UDP:
130             json_object_set_new(djs, "udplen", json_integer(UDP_GET_LEN(p)));
131             break;
132         case IPPROTO_ICMP:
133             if (PKT_IS_ICMPV4(p)) {
134                 json_object_set_new(djs, "icmp_id", json_integer(ICMPV4_GET_ID(p)));
135                 json_object_set_new(djs, "icmp_seq", json_integer(ICMPV4_GET_SEQ(p)));
136             } else if(PKT_IS_ICMPV6(p)) {
137                 json_object_set_new(djs, "icmp_id", json_integer(ICMPV6_GET_ID(p)));
138                 json_object_set_new(djs, "icmp_seq", json_integer(ICMPV6_GET_SEQ(p)));
139             }
140             break;
141     }
142     json_object_set_new(js, "drop", djs);
143
144     if (aft->drop_ctx->flags & LOG_DROP_ALERTS) {
145         int logged = 0;
146         int i;
147         for (i = 0; i < p->alerts.cnt; i++) {
148             const PacketAlert *pa = &p->alerts.alerts[i];
149             if (unlikely(pa->s == NULL)) {
150                 continue;
151             }
152             if ((pa->action & (ACTION_REJECT|ACTION_REJECT_DST|ACTION_REJECT_BOTH)) ||
153                ((pa->action & ACTION_DROP) && EngineModeIsIPS()))
154             {
155                 AlertJsonHeader(p, pa, js);
156                 logged = 1;
157             }
158         }
159         if (logged == 0) {
160             if (p->alerts.drop.action != 0) {
161                 const PacketAlert *pa = &p->alerts.drop;
162                 AlertJsonHeader(p, pa, js);
163             }
164         }
165     }
166
167     OutputJSONBuffer(js, aft->drop_ctx->file_ctx, buffer);
168     json_object_del(js, "drop");
169     json_object_clear(js);
170     json_decref(js);
171
172     return TM_ECODE_OK;
173 }
174
175 #define OUTPUT_BUFFER_SIZE 65535
176 static TmEcode JsonDropLogThreadInit(ThreadVars *t, void *initdata, void **data)
177 {
178     JsonDropLogThread *aft = SCMalloc(sizeof(JsonDropLogThread));
179     if (unlikely(aft == NULL))
180         return TM_ECODE_FAILED;
181     memset(aft, 0, sizeof(*aft));
182
183     if(initdata == NULL)
184     {
185         SCLogDebug("Error getting context for AlertFastLog.  \"initdata\" argument NULL");
186         SCFree(aft);
187         return TM_ECODE_FAILED;
188     }
189
190     aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
191     if (aft->buffer == NULL) {
192         SCFree(aft);
193         return TM_ECODE_FAILED;
194     }
195
196     /** Use the Ouptut Context (file pointer and mutex) */
197     aft->drop_ctx = ((OutputCtx *)initdata)->data;
198
199     *data = (void *)aft;
200     return TM_ECODE_OK;
201 }
202
203 static TmEcode JsonDropLogThreadDeinit(ThreadVars *t, void *data)
204 {
205     JsonDropLogThread *aft = (JsonDropLogThread *)data;
206     if (aft == NULL) {
207         return TM_ECODE_OK;
208     }
209
210     MemBufferFree(aft->buffer);
211
212     /* clear memory */
213     memset(aft, 0, sizeof(*aft));
214
215     SCFree(aft);
216     return TM_ECODE_OK;
217 }
218
219 static void JsonDropLogDeInitCtx(OutputCtx *output_ctx)
220 {
221     OutputDropLoggerDisable();
222
223     LogFileCtx *logfile_ctx = (LogFileCtx *)output_ctx->data;
224     LogFileFreeCtx(logfile_ctx);
225     SCFree(output_ctx);
226 }
227
228 static void JsonDropLogDeInitCtxSub(OutputCtx *output_ctx)
229 {
230     OutputDropLoggerDisable();
231
232     SCLogDebug("cleaning up sub output_ctx %p", output_ctx);
233     SCFree(output_ctx);
234 }
235
236 static void JsonDropOutputCtxFree(JsonDropOutputCtx *drop_ctx)
237 {
238     if (drop_ctx != NULL) {
239         if (drop_ctx->file_ctx != NULL)
240             LogFileFreeCtx(drop_ctx->file_ctx);
241         SCFree(drop_ctx);
242     }
243 }
244
245 #define DEFAULT_LOG_FILENAME "drop.json"
246 static OutputCtx *JsonDropLogInitCtx(ConfNode *conf)
247 {
248     if (OutputDropLoggerEnable() != 0) {
249         SCLogError(SC_ERR_CONF_YAML_ERROR, "only one 'drop' logger "
250             "can be enabled");
251         return NULL;
252     }
253
254     JsonDropOutputCtx *drop_ctx = SCCalloc(1, sizeof(*drop_ctx));
255     if (drop_ctx == NULL)
256         return NULL;
257
258     drop_ctx->file_ctx = LogFileNewCtx();
259     if (drop_ctx->file_ctx == NULL) {
260         JsonDropOutputCtxFree(drop_ctx);
261         return NULL;
262     }
263
264     if (SCConfLogOpenGeneric(conf, drop_ctx->file_ctx, DEFAULT_LOG_FILENAME, 1) < 0) {
265         JsonDropOutputCtxFree(drop_ctx);
266         return NULL;
267     }
268
269     OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
270     if (unlikely(output_ctx == NULL)) {
271         JsonDropOutputCtxFree(drop_ctx);
272         return NULL;
273     }
274
275     if (conf) {
276         const char *extended = ConfNodeLookupChildValue(conf, "alerts");
277         if (extended != NULL) {
278             if (ConfValIsTrue(extended)) {
279                 drop_ctx->flags = LOG_DROP_ALERTS;
280             }
281         }
282     }
283
284     output_ctx->data = drop_ctx;
285     output_ctx->DeInit = JsonDropLogDeInitCtx;
286     return output_ctx;
287 }
288
289 static OutputCtx *JsonDropLogInitCtxSub(ConfNode *conf, OutputCtx *parent_ctx)
290 {
291     if (OutputDropLoggerEnable() != 0) {
292         SCLogError(SC_ERR_CONF_YAML_ERROR, "only one 'drop' logger "
293             "can be enabled");
294         return NULL;
295     }
296
297     AlertJsonThread *ajt = parent_ctx->data;
298
299     JsonDropOutputCtx *drop_ctx = SCCalloc(1, sizeof(*drop_ctx));
300     if (drop_ctx == NULL)
301         return NULL;
302
303     OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
304     if (unlikely(output_ctx == NULL)) {
305         JsonDropOutputCtxFree(drop_ctx);
306         return NULL;
307     }
308
309     if (conf) {
310         const char *extended = ConfNodeLookupChildValue(conf, "alerts");
311         if (extended != NULL) {
312             if (ConfValIsTrue(extended)) {
313                 drop_ctx->flags = LOG_DROP_ALERTS;
314             }
315         }
316     }
317
318     drop_ctx->file_ctx = ajt->file_ctx;
319
320     output_ctx->data = drop_ctx;
321     output_ctx->DeInit = JsonDropLogDeInitCtxSub;
322     return output_ctx;
323 }
324
325 /**
326  * \brief   Log the dropped packets when engine is running in inline mode
327  *
328  * \param tv    Pointer the current thread variables
329  * \param data  Pointer to the droplog struct
330  * \param p     Pointer the packet which is being logged
331  *
332  * \retval 0 on succes
333  */
334 static int JsonDropLogger(ThreadVars *tv, void *thread_data, const Packet *p)
335 {
336     JsonDropLogThread *td = thread_data;
337     int r = DropLogJSON(td, p);
338     if (r < 0)
339         return -1;
340
341     if (p->flow) {
342         FLOWLOCK_RDLOCK(p->flow);
343         if (p->flow->flags & FLOW_ACTION_DROP) {
344             if (PKT_IS_TOSERVER(p) && !(p->flow->flags & FLOW_TOSERVER_DROP_LOGGED))
345                 p->flow->flags |= FLOW_TOSERVER_DROP_LOGGED;
346             else if (PKT_IS_TOCLIENT(p) && !(p->flow->flags & FLOW_TOCLIENT_DROP_LOGGED))
347                 p->flow->flags |= FLOW_TOCLIENT_DROP_LOGGED;
348         }
349         FLOWLOCK_UNLOCK(p->flow);
350     }
351     return 0;
352 }
353
354
355 /**
356  * \brief Check if we need to drop-log this packet
357  *
358  * \param tv    Pointer the current thread variables
359  * \param p     Pointer the packet which is tested
360  *
361  * \retval bool TRUE or FALSE
362  */
363 static int JsonDropLogCondition(ThreadVars *tv, const Packet *p)
364 {
365     if (!EngineModeIsIPS()) {
366         SCLogDebug("engine is not running in inline mode, so returning");
367         return FALSE;
368     }
369     if (PKT_IS_PSEUDOPKT(p)) {
370         SCLogDebug("drop log doesn't log pseudo packets");
371         return FALSE;
372     }
373
374     if (p->flow != NULL) {
375         int ret = FALSE;
376
377         /* for a flow that will be dropped fully, log just once per direction */
378         FLOWLOCK_RDLOCK(p->flow);
379         if (p->flow->flags & FLOW_ACTION_DROP) {
380             if (PKT_IS_TOSERVER(p) && !(p->flow->flags & FLOW_TOSERVER_DROP_LOGGED))
381                 ret = TRUE;
382             else if (PKT_IS_TOCLIENT(p) && !(p->flow->flags & FLOW_TOCLIENT_DROP_LOGGED))
383                 ret = TRUE;
384         }
385         FLOWLOCK_UNLOCK(p->flow);
386
387         /* if drop is caused by signature, log anyway */
388         if (p->alerts.drop.action != 0)
389             ret = TRUE;
390
391         return ret;
392     } else if (PACKET_TEST_ACTION(p, ACTION_DROP)) {
393         return TRUE;
394     }
395
396     return FALSE;
397 }
398
399 void TmModuleJsonDropLogRegister (void)
400 {
401     tmm_modules[TMM_JSONDROPLOG].name = MODULE_NAME;
402     tmm_modules[TMM_JSONDROPLOG].ThreadInit = JsonDropLogThreadInit;
403     tmm_modules[TMM_JSONDROPLOG].ThreadDeinit = JsonDropLogThreadDeinit;
404     tmm_modules[TMM_JSONDROPLOG].cap_flags = 0;
405     tmm_modules[TMM_JSONDROPLOG].flags = TM_FLAG_LOGAPI_TM;
406
407     OutputRegisterPacketModule(MODULE_NAME, "drop-json-log",
408             JsonDropLogInitCtx, JsonDropLogger, JsonDropLogCondition);
409     OutputRegisterPacketSubModule("eve-log", MODULE_NAME, "eve-log.drop",
410             JsonDropLogInitCtxSub, JsonDropLogger, JsonDropLogCondition);
411 }
412
413 #else
414
415 static TmEcode OutputJsonThreadInit(ThreadVars *t, void *initdata, void **data)
416 {
417     SCLogInfo("Can't init JSON output - JSON support was disabled during build.");
418     return TM_ECODE_FAILED;
419 }
420
421 void TmModuleJsonDropLogRegister (void)
422 {
423     tmm_modules[TMM_JSONDROPLOG].name = MODULE_NAME;
424     tmm_modules[TMM_JSONDROPLOG].ThreadInit = OutputJsonThreadInit;
425 }
426
427 #endif