suricata
output-json-dnp3.c
Go to the documentation of this file.
1 /* Copyright (C) 2015 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 #include "suricata-common.h"
19 #include "debug.h"
20 #include "detect.h"
21 #include "pkt-var.h"
22 #include "conf.h"
23 
24 #include "threads.h"
25 #include "threadvars.h"
26 #include "tm-threads.h"
27 
28 #include "util-print.h"
29 #include "util-unittest.h"
30 #include "util-buffer.h"
31 #include "util-crypt.h"
32 #include "util-debug.h"
33 
34 #include "app-layer.h"
35 #include "app-layer-parser.h"
36 #include "app-layer-dnp3.h"
37 #include "app-layer-dnp3-objects.h"
38 
39 #include "detect-dnp3.h"
40 
41 #include "output.h"
42 #include "output-json.h"
43 #include "output-json-dnp3.h"
45 
46 #ifdef HAVE_LIBJANSSON
47 #include <jansson.h>
48 
49 typedef struct LogDNP3FileCtx_ {
50  LogFileCtx *file_ctx;
51  uint32_t flags;
52  uint8_t include_object_data;
53  OutputJsonCommonSettings cfg;
54 } LogDNP3FileCtx;
55 
56 typedef struct LogDNP3LogThread_ {
57  LogDNP3FileCtx *dnp3log_ctx;
58  MemBuffer *buffer;
59 } LogDNP3LogThread;
60 
61 static json_t *JsonDNP3LogLinkControl(uint8_t lc)
62 {
63  json_t *lcjs = json_object();
64  if (unlikely(lcjs == NULL)) {
65  return NULL;
66  }
67 
68  json_object_set_new(lcjs, "dir", json_boolean(DNP3_LINK_DIR(lc)));
69  json_object_set_new(lcjs, "pri", json_boolean(DNP3_LINK_PRI(lc)));
70  json_object_set_new(lcjs, "fcb", json_boolean(DNP3_LINK_FCB(lc)));
71  json_object_set_new(lcjs, "fcv", json_boolean(DNP3_LINK_FCV(lc)));
72  json_object_set_new(lcjs, "function_code", json_integer(DNP3_LINK_FC(lc)));
73 
74  return lcjs;
75 }
76 
77 static json_t *JsonDNP3LogIin(uint16_t iin)
78 {
79  json_t *iinjs = json_object();
80  if (unlikely(iinjs == NULL)) {
81  return NULL;
82  }
83 
84  json_t *indicators = json_array();
85  if (unlikely(indicators == NULL)) {
86  json_decref(iinjs);
87  return NULL;
88  }
89 
90  if (iin) {
91  int mapping = 0;
92  do {
93  if (iin & DNP3IndicatorsMap[mapping].value) {
94  json_array_append_new(indicators,
95  json_string(DNP3IndicatorsMap[mapping].name));
96  }
97  mapping++;
98  } while (DNP3IndicatorsMap[mapping].name != NULL);
99  }
100  json_object_set_new(iinjs, "indicators", indicators);
101 
102  return iinjs;
103 }
104 
105 static json_t *JsonDNP3LogApplicationControl(uint8_t ac)
106 {
107  json_t *acjs = json_object();
108  if (unlikely(acjs == NULL)) {
109  return NULL;
110  }
111 
112  json_object_set_new(acjs, "fir", json_boolean(DNP3_APP_FIR(ac)));
113  json_object_set_new(acjs, "fin", json_boolean(DNP3_APP_FIN(ac)));
114  json_object_set_new(acjs, "con", json_boolean(DNP3_APP_CON(ac)));
115  json_object_set_new(acjs, "uns", json_boolean(DNP3_APP_UNS(ac)));
116  json_object_set_new(acjs, "sequence", json_integer(DNP3_APP_SEQ(ac)));
117 
118  return acjs;
119 }
120 
121 /**
122  * \brief Log the items (points) for an object.
123  *
124  * TODO: Autogenerate this function based on object definitions.
125  */
126 static json_t *JsonDNP3LogObjectItems(DNP3Object *object)
127 {
128  DNP3Point *item;
129  json_t *jsitems;
130 
131  if (unlikely((jsitems = json_array()) == NULL)) {
132  return NULL;
133  }
134 
135  TAILQ_FOREACH(item, object->points, next) {
136  json_t *js = json_object();
137  if (unlikely(js == NULL)) {
138  break;
139  }
140 
141  json_object_set_new(js, "prefix", json_integer(item->prefix));
142  json_object_set_new(js, "index", json_integer(item->index));
143  if (DNP3PrefixIsSize(object->prefix_code)) {
144  json_object_set_new(js, "size", json_integer(item->size));
145  }
146 
147  OutputJsonDNP3SetItem(js, object, item);
148  json_array_append_new(jsitems, js);
149  }
150 
151  return jsitems;
152 }
153 
154 /**
155  * \brief Log the application layer objects.
156  *
157  * \param objects A list of DNP3 objects.
158  *
159  * \retval a json_t pointer containing the logged DNP3 objects.
160  */
161 static json_t *JsonDNP3LogObjects(DNP3ObjectList *objects)
162 {
163  DNP3Object *object;
164  json_t *js = json_array();
165  if (unlikely(js == NULL)) {
166  return NULL;
167  }
168 
169  TAILQ_FOREACH(object, objects, next) {
170  json_t *objs = json_object();
171  if (unlikely(objs == NULL)) {
172  goto error;
173  }
174  json_object_set_new(objs, "group", json_integer(object->group));
175  json_object_set_new(objs, "variation",
176  json_integer(object->variation));
177  json_object_set_new(objs, "qualifier", json_integer(object->qualifier));
178  json_object_set_new(objs, "prefix_code",
179  json_integer(object->prefix_code));
180  json_object_set_new(objs, "range_code",
181  json_integer(object->range_code));
182  json_object_set_new(objs, "start", json_integer(object->start));
183  json_object_set_new(objs, "stop", json_integer(object->stop));
184  json_object_set_new(objs, "count", json_integer(object->count));
185 
186  if (object->points != NULL && !TAILQ_EMPTY(object->points)) {
187  json_t *points = JsonDNP3LogObjectItems(object);
188  if (points != NULL) {
189  json_object_set_new(objs, "points", points);
190  }
191  }
192 
193  json_array_append_new(js, objs);
194  }
195 
196  return js;
197 error:
198  json_decref(js);
199  return NULL;
200 }
201 
202 json_t *JsonDNP3LogRequest(DNP3Transaction *dnp3tx)
203 {
204  json_t *dnp3js = json_object();
205  if (dnp3js == NULL) {
206  return NULL;;
207  }
208  json_object_set_new(dnp3js, "type", json_string("request"));
209 
210  json_t *lcjs = JsonDNP3LogLinkControl(dnp3tx->request_lh.control);
211  if (lcjs != NULL) {
212  json_object_set_new(dnp3js, "control", lcjs);
213  }
214 
215  json_object_set_new(dnp3js, "src", json_integer(dnp3tx->request_lh.src));
216  json_object_set_new(dnp3js, "dst", json_integer(dnp3tx->request_lh.dst));
217 
218  /* DNP3 application layer. */
219  json_t *al = json_object();
220  if (al == NULL) {
221  goto error;
222  }
223  json_object_set_new(dnp3js, "application", al);
224 
225  json_t *acjs = JsonDNP3LogApplicationControl(dnp3tx->request_ah.control);
226  if (acjs != NULL) {
227  json_object_set_new(al, "control", acjs);
228  }
229 
230  json_object_set_new(al, "function_code",
231  json_integer(dnp3tx->request_ah.function_code));
232 
233  json_t *objects = JsonDNP3LogObjects(&dnp3tx->request_objects);
234  if (objects != NULL) {
235  json_object_set_new(al, "objects", objects);
236  }
237  json_object_set_new(al, "complete",
238  json_boolean(dnp3tx->request_complete));
239 
240  return dnp3js;
241 
242 error:
243  json_decref(dnp3js);
244  return NULL;
245 }
246 
247 json_t *JsonDNP3LogResponse(DNP3Transaction *dnp3tx)
248 {
249  json_t *dnp3js = json_object();
250  if (dnp3js == NULL) {
251  return NULL;
252  }
253  if (dnp3tx->response_ah.function_code == DNP3_APP_FC_UNSOLICITED_RESP) {
254  json_object_set_new(dnp3js, "type",
255  json_string("unsolicited_response"));
256  }
257  else {
258  json_object_set_new(dnp3js, "type", json_string("response"));
259  }
260 
261  json_t *lcjs = JsonDNP3LogLinkControl(dnp3tx->response_lh.control);
262  if (lcjs != NULL) {
263  json_object_set_new(dnp3js, "control", lcjs);
264  }
265 
266  json_object_set_new(dnp3js, "src", json_integer(dnp3tx->response_lh.src));
267  json_object_set_new(dnp3js, "dst", json_integer(dnp3tx->response_lh.dst));
268 
269  /* DNP3 application layer. */
270  json_t *al = json_object();
271  if (al == NULL) {
272  goto error;
273  }
274  json_object_set_new(dnp3js, "application", al);
275 
276  json_t *acjs = JsonDNP3LogApplicationControl(dnp3tx->response_ah.control);
277  if (acjs != NULL) {
278  json_object_set_new(al, "control", acjs);
279  }
280 
281  json_object_set_new(al, "function_code",
282  json_integer(dnp3tx->response_ah.function_code));
283 
284  json_t *iinjs = JsonDNP3LogIin(dnp3tx->response_iin.iin1 << 8 |
285  dnp3tx->response_iin.iin2);
286  if (iinjs != NULL) {
287  json_object_set_new(dnp3js, "iin", iinjs);
288  }
289 
290  json_t *objects = JsonDNP3LogObjects(&dnp3tx->response_objects);
291  if (objects != NULL) {
292  json_object_set_new(al, "objects", objects);
293  }
294  json_object_set_new(al, "complete",
295  json_boolean(dnp3tx->response_complete));
296 
297  return dnp3js;
298 
299 error:
300  json_decref(dnp3js);
301  return NULL;
302 }
303 
304 static int JsonDNP3LoggerToServer(ThreadVars *tv, void *thread_data,
305  const Packet *p, Flow *f, void *state, void *vtx, uint64_t tx_id)
306 {
307  SCEnter();
308  LogDNP3LogThread *thread = (LogDNP3LogThread *)thread_data;
309  DNP3Transaction *tx = vtx;
310 
311  MemBuffer *buffer = (MemBuffer *)thread->buffer;
312 
313  MemBufferReset(buffer);
314  if (tx->has_request && tx->request_done) {
315  json_t *js = CreateJSONHeader(p, LOG_DIR_FLOW, "dnp3");
316  if (unlikely(js == NULL)) {
317  return TM_ECODE_OK;
318  }
319 
320  JsonAddCommonOptions(&thread->dnp3log_ctx->cfg, p, f, js);
321 
322  json_t *dnp3js = JsonDNP3LogRequest(tx);
323  if (dnp3js != NULL) {
324  json_object_set_new(js, "dnp3", dnp3js);
325  OutputJSONBuffer(js, thread->dnp3log_ctx->file_ctx, &buffer);
326  }
327  json_decref(js);
328  }
329 
331 }
332 
333 static int JsonDNP3LoggerToClient(ThreadVars *tv, void *thread_data,
334  const Packet *p, Flow *f, void *state, void *vtx, uint64_t tx_id)
335 {
336  SCEnter();
337  LogDNP3LogThread *thread = (LogDNP3LogThread *)thread_data;
338  DNP3Transaction *tx = vtx;
339 
340  MemBuffer *buffer = (MemBuffer *)thread->buffer;
341 
342  MemBufferReset(buffer);
343  if (tx->has_response && tx->response_done) {
344  json_t *js = CreateJSONHeader(p, LOG_DIR_FLOW, "dnp3");
345  if (unlikely(js == NULL)) {
346  return TM_ECODE_OK;
347  }
348 
349  JsonAddCommonOptions(&thread->dnp3log_ctx->cfg, p, f, js);
350 
351  json_t *dnp3js = JsonDNP3LogResponse(tx);
352  if (dnp3js != NULL) {
353  json_object_set_new(js, "dnp3", dnp3js);
354  OutputJSONBuffer(js, thread->dnp3log_ctx->file_ctx, &buffer);
355  }
356  json_decref(js);
357  }
358 
360 }
361 
362 static void OutputDNP3LogDeInitCtxSub(OutputCtx *output_ctx)
363 {
364  SCLogDebug("cleaning up sub output_ctx %p", output_ctx);
365  LogDNP3FileCtx *dnp3log_ctx = (LogDNP3FileCtx *)output_ctx->data;
366  SCFree(dnp3log_ctx);
367  SCFree(output_ctx);
368 }
369 
370 #define DEFAULT_LOG_FILENAME "dnp3.json"
371 
372 static OutputInitResult OutputDNP3LogInitSub(ConfNode *conf, OutputCtx *parent_ctx)
373 {
374  OutputInitResult result = { NULL, false };
375  OutputJsonCtx *json_ctx = parent_ctx->data;
376 
377  LogDNP3FileCtx *dnp3log_ctx = SCCalloc(1, sizeof(*dnp3log_ctx));
378  if (unlikely(dnp3log_ctx == NULL)) {
379  return result;
380  }
381  dnp3log_ctx->file_ctx = json_ctx->file_ctx;
382  dnp3log_ctx->cfg = json_ctx->cfg;
383 
384  OutputCtx *output_ctx = SCCalloc(1, sizeof(*output_ctx));
385  if (unlikely(output_ctx == NULL)) {
386  SCFree(dnp3log_ctx);
387  return result;
388  }
389  output_ctx->data = dnp3log_ctx;
390  output_ctx->DeInit = OutputDNP3LogDeInitCtxSub;
391 
392  SCLogInfo("DNP3 log sub-module initialized.");
393 
395 
396  result.ctx = output_ctx;
397  result.ok = true;
398  return result;
399 }
400 
401 #define OUTPUT_BUFFER_SIZE 65535
402 
403 static TmEcode JsonDNP3LogThreadInit(ThreadVars *t, const void *initdata, void **data)
404 {
405  LogDNP3LogThread *thread = SCCalloc(1, sizeof(*thread));
406  if (unlikely(thread == NULL)) {
407  return TM_ECODE_FAILED;
408  }
409 
410  if (initdata == NULL) {
411  SCLogDebug("Error getting context for DNP3. \"initdata\" is NULL.");
412  SCFree(thread);
413  return TM_ECODE_FAILED;
414  }
415 
416  thread->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
417  if (unlikely(thread->buffer == NULL)) {
418  SCFree(thread);
419  return TM_ECODE_FAILED;
420  }
421 
422  thread->dnp3log_ctx = ((OutputCtx *)initdata)->data;
423  *data = (void *)thread;
424 
425  return TM_ECODE_OK;
426 }
427 
428 static TmEcode JsonDNP3LogThreadDeinit(ThreadVars *t, void *data)
429 {
430  LogDNP3LogThread *thread = (LogDNP3LogThread *)data;
431  if (thread == NULL) {
432  return TM_ECODE_OK;
433  }
434  if (thread->buffer != NULL) {
435  MemBufferFree(thread->buffer);
436  }
437  SCFree(thread);
438  return TM_ECODE_OK;
439 }
440 
441 void JsonDNP3LogRegister(void)
442 {
443  /* Register direction aware eve sub-modules. */
445  "JsonDNP3Log", "eve-log.dnp3", OutputDNP3LogInitSub, ALPROTO_DNP3,
446  JsonDNP3LoggerToServer, 0, 1, JsonDNP3LogThreadInit,
447  JsonDNP3LogThreadDeinit, NULL);
449  "JsonDNP3Log", "eve-log.dnp3", OutputDNP3LogInitSub, ALPROTO_DNP3,
450  JsonDNP3LoggerToClient, 1, 1, JsonDNP3LogThreadInit,
451  JsonDNP3LogThreadDeinit, NULL);
452 }
453 
454 #else
455 
457 {
458 }
459 
460 #endif
#define DNP3_APP_FIR(x)
DNP3ApplicationHeader response_ah
MemBuffer * MemBufferCreateNew(uint32_t size)
Definition: util-buffer.c:32
uint8_t prefix_code
uint32_t start
uint16_t flags
#define SCLogDebug(...)
Definition: util-debug.h:335
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:350
struct HtpBodyChunk_ * next
DNP3ObjectList request_objects
#define DNP3_LINK_DIR(control)
uint32_t prefix
#define unlikely(expr)
Definition: util-optimize.h:35
#define DNP3_APP_CON(x)
#define MemBufferReset(mem_buffer)
Reset the mem buffer.
Definition: util-buffer.h:42
#define DNP3_LINK_PRI(control)
uint32_t stop
DNP3 transaction.
#define DNP3_APP_UNS(x)
#define DNP3_LINK_FCV(control)
void(* DeInit)(struct OutputCtx_ *)
Definition: tm-modules.h:84
#define DNP3_APP_FIN(x)
Struct to hold the list of decoded objects.
uint8_t request_complete
DNP3LinkHeader request_lh
DNP3ApplicationHeader request_ah
#define DNP3_APP_SEQ(x)
#define SCCalloc(nm, a)
Definition: util-mem.h:197
uint32_t count
void AppLayerParserRegisterLogger(uint8_t ipproto, AppProto alproto)
DNP3Mapping DNP3IndicatorsMap[]
Definition: detect-dnp3.c:57
#define SCEnter(...)
Definition: util-debug.h:337
DNP3 object point.
uint8_t * buffer
Definition: util-buffer.h:28
void OutputRegisterTxSubModuleWithProgress(LoggerId id, const char *parent_name, const char *name, const char *conf_name, OutputInitSubFunc InitFunc, AppProto alproto, TxLogger TxLogFunc, int tc_log_progress, int ts_log_progress, ThreadInitFunc ThreadInit, ThreadDeinitFunc ThreadDeinit, ThreadExitPrintStatsFunc ThreadExitPrintStats)
Definition: output.c:366
#define SCReturnInt(x)
Definition: util-debug.h:341
uint8_t variation
uint8_t response_complete
Definition: conf.h:32
OutputCtx * ctx
Definition: output.h:42
DNP3LinkHeader response_lh
uint8_t range_code
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:254
DNP3InternalInd response_iin
#define SCFree(a)
Definition: util-mem.h:228
uint16_t tx_id
#define DNP3_LINK_FC(control)
#define DNP3_APP_FC_UNSOLICITED_RESP
#define DNP3_LINK_FCB(control)
void * data
Definition: tm-modules.h:81
uint32_t size
int DNP3PrefixIsSize(uint8_t prefix_code)
Check if the prefix code is a size prefix.
void JsonDNP3LogRegister(void)
DNP3PointList * points
#define TAILQ_EMPTY(head)
Definition: queue.h:347
Per thread variable structure.
Definition: threadvars.h:57
#define OUTPUT_BUFFER_SIZE
Definition: log-httplog.c:58
uint32_t index
Flow data structure.
Definition: flow.h:324
DNP3ObjectList response_objects
uint8_t qualifier
void MemBufferFree(MemBuffer *buffer)
Definition: util-buffer.c:82