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