suricata
detect-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 
20 #include "stream.h"
21 
22 #include "detect.h"
23 #include "detect-parse.h"
24 #include "detect-dnp3.h"
25 #include "detect-engine.h"
26 #include "detect-engine-mpm.h"
29 
30 #include "app-layer-dnp3.h"
31 
32 static int g_dnp3_match_buffer_id = 0;
33 static int g_dnp3_data_buffer_id = 0;
34 
35 /**
36  * The detection struct.
37  */
38 typedef struct DetectDNP3_ {
39  union {
40  struct {
41  /* Function code for function code detection. */
42  uint8_t function_code;
43  };
44  struct {
45  /* Internal indicator flags for IIN detection. */
46  uint16_t ind_flags;
47  };
48  struct {
49  /* Object info for object detection. */
50  uint8_t obj_group;
51  uint8_t obj_variation;
52  };
53  };
54 } DetectDNP3;
55 
56 /**
57  * Indicator names to value mappings (Snort compatible).
58  */
60  {"device_restart", 0x8000},
61  {"device_trouble", 0x4000},
62  {"local_control", 0x2000},
63  {"need_time", 0x1000},
64  {"class_3_events", 0x0800},
65  {"class_2_events", 0x0400},
66  {"class_1_events", 0x0200},
67  {"all_stations", 0x0100},
68 
69  {"reserved_1", 0x0080},
70  {"reserved_2", 0x0040},
71  {"config_corrupt", 0x0020},
72  {"already_executing", 0x0010},
73  {"event_buffer_overflow", 0x0008},
74  {"parameter_error", 0x0004},
75  {"object_unknown", 0x0002},
76  {"no_func_code_support", 0x0001},
77 
78  {NULL, 0},
79 };
80 
81 /**
82  * Application function code name to code mappings (Snort compatible).
83  */
85  {"confirm", 0},
86  {"read", 1},
87  {"write", 2},
88  {"select", 3},
89  {"operate", 4},
90  {"direct_operate", 5},
91  {"direct_operate_nr", 6},
92  {"immed_freeze", 7},
93  {"immed_freeze_nr", 8},
94  {"freeze_clear", 9},
95  {"freeze_clear_nr", 10},
96  {"freeze_at_time", 11},
97  {"freeze_at_time_nr", 12},
98  {"cold_restart", 13},
99  {"warm_restart", 14},
100  {"initialize_data", 15},
101  {"initialize_appl", 16},
102  {"start_appl", 17},
103  {"stop_appl", 18},
104  {"save_config", 19},
105  {"enable_unsolicited", 20},
106  {"disable_unsolicited", 21},
107  {"assign_class", 22},
108  {"delay_measure", 23},
109  {"record_current_time", 24},
110  {"open_file", 25},
111  {"close_file", 26},
112  {"delete_file", 27},
113  {"get_file_info", 28},
114  {"authenticate_file", 29},
115  {"abort_file", 30},
116  {"activate_config", 31},
117  {"authenticate_req", 32},
118  {"authenticate_err", 33},
119  {"response", 129},
120  {"unsolicited_response", 130},
121  {"authenticate_resp", 131}
122 };
123 
124 static void DetectDNP3FuncRegisterTests(void);
125 static void DetectDNP3IndRegisterTests(void);
126 static void DetectDNP3ObjRegisterTests(void);
127 static void DetectDNP3DataRegisterTests(void);
128 
129 /**
130  * \brief Utility function to trim leading and trailing whitespace
131  * from a string.
132  */
133 static char *TrimString(char *str)
134 {
135  char *end = str + strlen(str) - 1;
136  while (isspace(*str)) {
137  str++;
138  }
139  while (end > str && isspace(*end)) {
140  end--;
141  }
142  *(end + 1) = '\0';
143  return str;
144 }
145 
146 static InspectionBuffer *GetDNP3Data(DetectEngineThreadCtx *det_ctx,
147  const DetectEngineTransforms *transforms,
148  Flow *_f, const uint8_t flow_flags,
149  void *txv, const int list_id)
150 {
151  SCLogDebug("list_id %d", list_id);
152  InspectionBuffer *buffer = InspectionBufferGet(det_ctx, list_id);
153  if (buffer->inspect == NULL) {
154  DNP3Transaction *tx = (DNP3Transaction *)txv;
155  SCLogDebug("tx %p", tx);
156 
157  const uint8_t *data = NULL;
158  uint32_t data_len = 0;
159 
160  if (flow_flags & STREAM_TOSERVER) {
161  data = tx->request_buffer;
162  data_len = tx->request_buffer_len;
163  } else if (flow_flags & STREAM_TOCLIENT) {
164  data = tx->response_buffer;
165  data_len = tx->response_buffer_len;
166  }
167  if (data == NULL || data_len == 0)
168  return NULL;
169 
170  SCLogDebug("tx %p data %p data_len %u", tx, data, data_len);
171  InspectionBufferSetup(buffer, data, data_len);
172  InspectionBufferApplyTransforms(buffer, transforms);
173  }
174  return buffer;
175 }
176 
177 static int DetectEngineInspectDNP3(ThreadVars *tv, DetectEngineCtx *de_ctx,
178  DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd,
179  Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
180 {
181  return DetectEngineInspectGenericList(tv, de_ctx, det_ctx, s, smd,
182  f, flags, alstate, txv, tx_id);
183 }
184 
185 /**
186  * \brief Parse the provided function name or code to its integer
187  * value.
188  *
189  * If the value passed is a number, it will be checked that it falls
190  * within the range of valid function codes. If function name is
191  * passed it will be resolved to its function code.
192  *
193  * \retval The function code as an integer if successul, -1 on
194  * failure.
195  */
196 static int DetectDNP3FuncParseFunctionCode(const char *str, uint8_t *fc)
197 {
198  if (ByteExtractStringUint8(fc, 10, strlen(str), str) >= 0) {
199  return 1;
200  }
201 
202  /* Lookup by name. */
203  for (size_t i = 0;
204  i < sizeof(DNP3FunctionNameMap) / sizeof(DNP3Mapping); i++) {
205  if (strcasecmp(str, DNP3FunctionNameMap[i].name) == 0) {
206  *fc = DNP3FunctionNameMap[i].value;
207  return 1;
208  }
209  }
210 
211  return 0;
212 }
213 
214 static int DetectDNP3FuncSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
215 {
216  SCEnter();
217  DetectDNP3 *dnp3 = NULL;
218  SigMatch *sm = NULL;
219  uint8_t function_code;
220 
222  return -1;
223 
224  if (!DetectDNP3FuncParseFunctionCode(str, &function_code)) {
226  "Invalid argument \"%s\" supplied to dnp3_func keyword.", str);
227  return -1;
228  }
229 
230  dnp3 = SCCalloc(1, sizeof(DetectDNP3));
231  if (unlikely(dnp3 == NULL)) {
232  goto error;
233  }
235 
236  sm = SigMatchAlloc();
237  if (sm == NULL) {
238  goto error;
239  }
240  sm->type = DETECT_AL_DNP3FUNC;
241  sm->ctx = (void *)dnp3;
242 
243  SigMatchAppendSMToList(s, sm, g_dnp3_match_buffer_id);
244 
245  SCReturnInt(0);
246 error:
247  if (dnp3 != NULL) {
248  SCFree(dnp3);
249  }
250  if (sm != NULL) {
251  SCFree(sm);
252  }
253  SCReturnInt(-1);
254 }
255 
256 static int DetectDNP3IndParseByName(const char *str, uint16_t *flags)
257 {
258  char tmp[strlen(str) + 1];
259  char *p, *last = NULL;
260 
261  strlcpy(tmp, str, sizeof(tmp));
262 
263  for ((p = strtok_r(tmp, ",", &last)); p; (p = strtok_r(NULL, ",", &last))) {
264  p = TrimString(p);
265  int found = 0;
266  int i = 0;
267  while (DNP3IndicatorsMap[i].name != NULL) {
268  if (strcasecmp(p, DNP3IndicatorsMap[i].name) == 0) {
269  *flags |= DNP3IndicatorsMap[i].value;
270  found = 1;
271  break;
272  }
273  i++;
274  }
275 
276  if (!found) {
278  "Bad argument \"%s\" supplied to dnp3.ind keyword.", p);
279  return 0;
280  }
281  }
282 
283  return 1;
284 }
285 
286 static int DetectDNP3IndParse(const char *str, uint16_t *flags)
287 {
288  *flags = 0;
289 
290  if (ByteExtractStringUint16(flags, 0, strlen(str), str) > 0) {
291  return 1;
292  }
293 
294  /* Parse by name - will log a more specific error message on error. */
295  if (DetectDNP3IndParseByName(str, flags)) {
296  return 1;
297  }
298 
299  return 0;
300 }
301 
302 static int DetectDNP3IndSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
303 {
304  SCEnter();
305  DetectDNP3 *detect = NULL;
306  SigMatch *sm = NULL;
307  uint16_t flags;
308 
310  return -1;
311 
312  if (!DetectDNP3IndParse(str, &flags)) {
314  "Invalid argument \"%s\" supplied to dnp3.ind keyword.", str);
315  return -1;
316  }
317 
318  detect = SCCalloc(1, sizeof(DetectDNP3));
319  if (unlikely(detect == NULL)) {
320  goto error;
321  }
322  detect->ind_flags = flags;
323 
324  sm = SigMatchAlloc();
325  if (sm == NULL) {
326  goto error;
327  }
328  sm->type = DETECT_AL_DNP3IND;
329  sm->ctx = (void *)detect;
330  SigMatchAppendSMToList(s, sm, g_dnp3_match_buffer_id);
331 
332  SCReturnInt(0);
333 error:
334  if (detect != NULL) {
335  SCFree(detect);
336  }
337  if (sm != NULL) {
338  SCFree(sm);
339  }
340  SCReturnInt(-1);
341 }
342 
343 /**
344  * \brief Parse the value of string of the dnp3_obj keyword.
345  *
346  * \param str the input string
347  * \param gout pointer to variable to store the parsed group integer
348  * \param vout pointer to variable to store the parsed variation integer
349  *
350  * \retval 1 if parsing successful otherwise 0.
351  */
352 static int DetectDNP3ObjParse(const char *str, uint8_t *group, uint8_t *var)
353 {
354  size_t size = strlen(str) + 1;
355  char groupstr[size], *varstr, *sep;
356  strlcpy(groupstr, str, size);
357 
358  sep = strchr(groupstr, ',');
359  if (sep == NULL) {
360  return 0;
361  }
362  *sep = '\0';
363  varstr = sep + 1;
364 
365  if (ByteExtractStringUint8(group, 0, strlen(groupstr), groupstr) < 0) {
366  return 0;
367  }
368 
369  if (ByteExtractStringUint8(var, 0, strlen(varstr), varstr) < 0) {
370  return 0;
371  }
372 
373  return 1;
374 }
375 
376 static int DetectDNP3ObjSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
377 {
378  SCEnter();
379  uint8_t group;
380  uint8_t variation;
381  DetectDNP3 *detect = NULL;
382  SigMatch *sm = NULL;
383 
385  return -1;
386 
387  if (!DetectDNP3ObjParse(str, &group, &variation)) {
388  goto fail;
389  }
390 
391  detect = SCCalloc(1, sizeof(*detect));
392  if (unlikely(detect == NULL)) {
393  goto fail;
394  }
395  detect->obj_group = group;
396  detect->obj_variation = variation;
397 
398  sm = SigMatchAlloc();
399  if (unlikely(sm == NULL)) {
400  goto fail;
401  }
402  sm->type = DETECT_AL_DNP3OBJ;
403  sm->ctx = (void *)detect;
404  SigMatchAppendSMToList(s, sm, g_dnp3_match_buffer_id);
405 
406  SCReturnInt(1);
407 fail:
408  if (detect != NULL) {
409  SCFree(detect);
410  }
411  if (sm != NULL) {
412  SCFree(sm);
413  }
414  SCReturnInt(0);
415 }
416 
417 static void DetectDNP3Free(void *ptr)
418 {
419  SCEnter();
420  if (ptr != NULL) {
421  SCFree(ptr);
422  }
423  SCReturn;
424 }
425 
426 static int DetectDNP3FuncMatch(DetectEngineThreadCtx *det_ctx,
427  Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
428  const SigMatchCtx *ctx)
429 {
430  DNP3Transaction *tx = (DNP3Transaction *)txv;
431  DetectDNP3 *detect = (DetectDNP3 *)ctx;
432  int match = 0;
433 
434  if (flags & STREAM_TOSERVER) {
435  match = detect->function_code == tx->request_ah.function_code;
436  }
437  else if (flags & STREAM_TOCLIENT) {
438  match = detect->function_code == tx->response_ah.function_code;
439  }
440 
441  return match;
442 }
443 
444 static int DetectDNP3ObjMatch(DetectEngineThreadCtx *det_ctx,
445  Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
446  const SigMatchCtx *ctx)
447 {
448  DNP3Transaction *tx = (DNP3Transaction *)txv;
449  DetectDNP3 *detect = (DetectDNP3 *)ctx;
450  DNP3ObjectList *objects = NULL;
451 
452  if (flags & STREAM_TOSERVER) {
453  objects = &tx->request_objects;
454  }
455  else if (flags & STREAM_TOCLIENT) {
456  objects = &tx->response_objects;
457  }
458 
459  if (objects != NULL) {
460  DNP3Object *object;
461  TAILQ_FOREACH(object, objects, next) {
462  if (object->group == detect->obj_group &&
463  object->variation == detect->obj_variation) {
464  return 1;
465  }
466  }
467  }
468 
469  return 0;
470 }
471 
472 static int DetectDNP3IndMatch(DetectEngineThreadCtx *det_ctx,
473  Flow *f, uint8_t flags, void *state, void *txv, const Signature *s,
474  const SigMatchCtx *ctx)
475 {
476  DNP3Transaction *tx = (DNP3Transaction *)txv;
477  DetectDNP3 *detect = (DetectDNP3 *)ctx;
478 
479  if (flags & STREAM_TOCLIENT) {
480  if ((tx->response_iin.iin1 & (detect->ind_flags >> 8)) ||
481  (tx->response_iin.iin2 & (detect->ind_flags & 0xf))) {
482  return 1;
483  }
484  }
485 
486  return 0;
487 }
488 
489 static void DetectDNP3FuncRegister(void)
490 {
491  SCEnter();
492 
493  sigmatch_table[DETECT_AL_DNP3FUNC].name = "dnp3_func";
494  sigmatch_table[DETECT_AL_DNP3FUNC].alias = "dnp3.func";
495  sigmatch_table[DETECT_AL_DNP3FUNC].desc = "match on the application function code found in DNP3 request and responses";
496  sigmatch_table[DETECT_AL_DNP3FUNC].url = DOC_URL DOC_VERSION "/rules/dnp3-keywords.html#dnp3-func";
498  sigmatch_table[DETECT_AL_DNP3FUNC].AppLayerTxMatch = DetectDNP3FuncMatch;
499  sigmatch_table[DETECT_AL_DNP3FUNC].Setup = DetectDNP3FuncSetup;
500  sigmatch_table[DETECT_AL_DNP3FUNC].Free = DetectDNP3Free;
502  DetectDNP3FuncRegisterTests;
503 
504  SCReturn;
505 }
506 
507 static void DetectDNP3IndRegister(void)
508 {
509  SCEnter();
510 
511  sigmatch_table[DETECT_AL_DNP3IND].name = "dnp3_ind";
512  sigmatch_table[DETECT_AL_DNP3IND].alias = "dnp3.ind";
513  sigmatch_table[DETECT_AL_DNP3IND].desc = "match on the DNP3 internal indicator flags in the response application header";
514  sigmatch_table[DETECT_AL_DNP3IND].url = DOC_URL DOC_VERSION "/rules/dnp3-keywords.html#dnp3-ind";
516  sigmatch_table[DETECT_AL_DNP3IND].AppLayerTxMatch = DetectDNP3IndMatch;
517  sigmatch_table[DETECT_AL_DNP3IND].Setup = DetectDNP3IndSetup;
518  sigmatch_table[DETECT_AL_DNP3IND].Free = DetectDNP3Free;
520  DetectDNP3IndRegisterTests;
521 
522  SCReturn;
523 }
524 
525 static void DetectDNP3ObjRegister(void)
526 {
527  SCEnter();
528 
529  sigmatch_table[DETECT_AL_DNP3OBJ].name = "dnp3_obj";
530  sigmatch_table[DETECT_AL_DNP3OBJ].alias = "dnp3.obj";
531  sigmatch_table[DETECT_AL_DNP3OBJ].desc = "match on the DNP3 application data objects";
532  sigmatch_table[DETECT_AL_DNP3OBJ].url = DOC_URL DOC_VERSION "/rules/dnp3-keywords.html#dnp3-obj";
534  sigmatch_table[DETECT_AL_DNP3OBJ].AppLayerTxMatch = DetectDNP3ObjMatch;
535  sigmatch_table[DETECT_AL_DNP3OBJ].Setup = DetectDNP3ObjSetup;
536  sigmatch_table[DETECT_AL_DNP3OBJ].Free = DetectDNP3Free;
538  DetectDNP3ObjRegisterTests;
539 
540  SCReturn;
541 }
542 
543 static int DetectDNP3DataSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
544 {
545  SCEnter();
547  return -1;
548 
549  if (DetectBufferSetActiveList(s, g_dnp3_data_buffer_id) != 0)
550  return -1;
551 
552  SCReturnInt(0);
553 }
554 
555 static void DetectDNP3DataRegister(void)
556 {
557  SCEnter();
558 
559  sigmatch_table[DETECT_AL_DNP3DATA].name = "dnp3.data";
560  sigmatch_table[DETECT_AL_DNP3DATA].alias = "dnp3_data";
561  sigmatch_table[DETECT_AL_DNP3DATA].desc = "make the following content options to match on the re-assembled application buffer";
562  sigmatch_table[DETECT_AL_DNP3DATA].url = DOC_URL DOC_VERSION "/rules/dnp3-keywords.html#dnp3-data";
563  sigmatch_table[DETECT_AL_DNP3DATA].Setup = DetectDNP3DataSetup;
565  DetectDNP3DataRegisterTests;
566 
568 
572  GetDNP3Data);
574  PrefilterGenericMpmRegister, GetDNP3Data,
575  ALPROTO_DNP3, 0);
576 
580  GetDNP3Data);
582  PrefilterGenericMpmRegister, GetDNP3Data,
583  ALPROTO_DNP3, 0);
584 
585  g_dnp3_data_buffer_id = DetectBufferTypeGetByName("dnp3_data");
586  SCReturn;
587 }
588 
590 {
591  DetectDNP3DataRegister();
592 
593  DetectDNP3FuncRegister();
594  DetectDNP3IndRegister();
595  DetectDNP3ObjRegister();
596 
597  /* Register the list of func, ind and obj. */
600  DetectEngineInspectDNP3);
603  DetectEngineInspectDNP3);
604 
605  g_dnp3_match_buffer_id = DetectBufferTypeRegister("dnp3");
606 
607 }
608 
609 #ifdef UNITTESTS
610 
611 #include "util-unittest.h"
612 #include "util-unittest-helper.h"
613 #include "app-layer-parser.h"
614 #include "detect-engine.h"
615 #include "flow-util.h"
616 #include "stream-tcp.h"
617 
618 static int DetectDNP3FuncParseFunctionCodeTest(void)
619 {
620  uint8_t fc;
621 
622  /* Valid. */
623  FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("0", &fc));
624  FAIL_IF(fc != 0);
625 
626  FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("1", &fc));
627  FAIL_IF(fc != 1);
628 
629  FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("254", &fc));
630  FAIL_IF(fc != 254);
631 
632  FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("255", &fc));
633  FAIL_IF(fc != 255);
634 
635  FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("confirm", &fc));
636  FAIL_IF(fc != 0);
637 
638  FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("CONFIRM", &fc));
639  FAIL_IF(fc != 0);
640 
641  /* Invalid. */
642  FAIL_IF(DetectDNP3FuncParseFunctionCode("", &fc));
643  FAIL_IF(DetectDNP3FuncParseFunctionCode("-1", &fc));
644  FAIL_IF(DetectDNP3FuncParseFunctionCode("-2", &fc));
645  FAIL_IF(DetectDNP3FuncParseFunctionCode("256", &fc));
646  FAIL_IF(DetectDNP3FuncParseFunctionCode("unknown_function_code", &fc));
647 
648  PASS;
649 }
650 
651 static int DetectDNP3FuncTest01(void)
652 {
653  DetectEngineCtx *de_ctx = NULL;
654  DetectDNP3 *dnp3func = NULL;
655 
656  de_ctx = DetectEngineCtxInit();
657  FAIL_IF_NULL(de_ctx);
658 
659  de_ctx->sig_list = SigInit(de_ctx,
660  "alert dnp3 any any -> any any "
661  "(msg:\"SURICATA DNP3 Write request\"; "
662  "dnp3_func:2; sid:5000009; rev:1;)");
663  FAIL_IF_NULL(de_ctx->sig_list);
664 
665  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_dnp3_match_buffer_id]);
666  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_dnp3_match_buffer_id]->ctx);
667 
668  dnp3func = (DetectDNP3 *)de_ctx->sig_list->sm_lists_tail[g_dnp3_match_buffer_id]->ctx;
669  FAIL_IF(dnp3func->function_code != 2);
670 
671  if (de_ctx != NULL) {
672  DetectEngineCtxFree(de_ctx);
673  }
674  PASS;
675 }
676 
677 static int DetectDNP3IndTestParseAsInteger(void)
678 {
679  uint16_t flags = 0;
680 
681  FAIL_IF(!DetectDNP3IndParse("0", &flags));
682  FAIL_IF(flags != 0);
683  FAIL_IF(!DetectDNP3IndParse("1", &flags));
684  FAIL_IF(flags != 0x0001);
685 
686  FAIL_IF(!DetectDNP3IndParse("0x0", &flags));
687  FAIL_IF(flags != 0);
688  FAIL_IF(!DetectDNP3IndParse("0x0000", &flags));
689  FAIL_IF(flags != 0);
690  FAIL_IF(!DetectDNP3IndParse("0x0001", &flags));
691  FAIL_IF(flags != 0x0001);
692 
693  FAIL_IF(!DetectDNP3IndParse("0x8421", &flags));
694  FAIL_IF(flags != 0x8421);
695 
696  FAIL_IF(DetectDNP3IndParse("a", &flags));
697 
698  PASS;
699 }
700 
701 static int DetectDNP3IndTestParseByName(void)
702 {
703  uint16_t flags = 0;
704 
705  FAIL_IF(!DetectDNP3IndParse("all_stations", &flags));
706  FAIL_IF(!(flags & 0x0100));
707  FAIL_IF(!DetectDNP3IndParse("class_1_events , class_2_events", &flags));
708  FAIL_IF(!(flags & 0x0200));
709  FAIL_IF(!(flags & 0x0400));
710  FAIL_IF((flags & 0xf9ff));
711 
712  FAIL_IF(DetectDNP3IndParse("something", &flags));
713 
714  PASS;
715 }
716 
717 static int DetectDNP3ObjSetupTest(void)
718 {
719  DetectEngineCtx *de_ctx = NULL;
720  DetectDNP3 *detect = NULL;
721 
722  de_ctx = DetectEngineCtxInit();
723  FAIL_IF(de_ctx == NULL);
724 
725  de_ctx->sig_list = SigInit(de_ctx,
726  "alert dnp3 any any -> any any "
727  "(msg:\"SURICATA DNP3 Object Test\"; "
728  "dnp3_obj:99,99; sid:1; rev:1;)");
729  FAIL_IF(de_ctx->sig_list == NULL);
730 
731  FAIL_IF(de_ctx->sig_list->sm_lists_tail[g_dnp3_match_buffer_id] == NULL);
732  FAIL_IF(de_ctx->sig_list->sm_lists_tail[g_dnp3_match_buffer_id]->ctx == NULL);
733 
734  detect = (DetectDNP3 *)de_ctx->sig_list->sm_lists_tail[g_dnp3_match_buffer_id]->ctx;
735  FAIL_IF(detect->obj_group != 99);
736  FAIL_IF(detect->obj_variation != 99);
737 
738  if (de_ctx != NULL) {
739  DetectEngineCtxFree(de_ctx);
740  }
741  PASS;
742 }
743 
744 static int DetectDNP3ObjParseTest(void)
745 {
746  uint8_t group, var;
747 
748  FAIL_IF(!DetectDNP3ObjParse("0,0", &group, &var));
749  FAIL_IF(group != 0 || var != 0);
750 
751  FAIL_IF(!DetectDNP3ObjParse("255,255", &group, &var));
752  FAIL_IF(group != 255 || var != 255);
753 
754  FAIL_IF(DetectDNP3ObjParse("-1,-1", &group, &var));
755  FAIL_IF(DetectDNP3ObjParse("256,256", &group, &var));
756  FAIL_IF(DetectDNP3ObjParse("a,1", &group, &var));
757  FAIL_IF(DetectDNP3ObjParse("1,a", &group, &var));
758 
759  PASS;
760 }
761 
762 /**
763  * Test request (to server) content match.
764  */
765 static int DetectDNP3DataTest01(void)
766 {
768  DetectEngineThreadCtx *det_ctx = NULL;
769  DetectEngineCtx *de_ctx = NULL;
770  Flow f;
771  Packet *p;
772  TcpSession tcp;
773  ThreadVars tv;
774 
775  uint8_t request[] = {
776  0x05, 0x64, 0x1a, 0xc4, 0x02, 0x00, 0x01, 0x00,
777  0xa5, 0xe9,
778 
779  0xff, 0xc9, 0x05, 0x0c, 0x01, 0x28, 0x01, 0x00,
780  0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
781 
782  /* CRC. */
783  0x72, 0xef,
784 
785  0x00, 0x00, 0x00, 0x00, 0x00,
786 
787  /* CRC. */
788  0xff, 0xff,
789  };
790 
791  /* Setup flow. */
792  memset(&f, 0, sizeof(Flow));
793  memset(&tcp, 0, sizeof(TcpSession));
794  memset(&tv, 0, sizeof(ThreadVars));
795  p = UTHBuildPacket(request, sizeof(request), IPPROTO_TCP);
796  FLOW_INITIALIZE(&f);
797  f.alproto = ALPROTO_DNP3;
798  f.protoctx = (void *)&tcp;
799  f.proto = IPPROTO_TCP;
800  f.flags |= FLOW_IPV4;
801  p->flow = &f;
805 
806  de_ctx = DetectEngineCtxInit();
807  FAIL_IF(de_ctx == NULL);
808 
809  /* Either direction - should match. */
810  Signature *s = DetectEngineAppendSig(de_ctx,
811  "alert dnp3 any any -> any any ("
812  "msg:\"DetectDNP3DataTest01\"; "
813  "dnp3_data; "
814  "content:\"|01 01 01 00 00 00 00 00 00 00|\"; "
815  "sid:1; rev:1;)");
816  FAIL_IF(s == NULL);
817 
818  /* To server - should match. */
819  s = DetectEngineAppendSig(de_ctx,
820  "alert dnp3 any any -> any any ("
821  "msg:\"DetectDNP3DataTest01\"; "
822  "flow:established,to_server; "
823  "dnp3_data; "
824  "content:\"|01 01 01 00 00 00 00 00 00 00|\"; "
825  "sid:2; rev:1;)");
826  FAIL_IF(s == NULL);
827 
828  /* To client - should not match. */
829  s = DetectEngineAppendSig(de_ctx,
830  "alert dnp3 any any -> any any ("
831  "msg:\"DetectDNP3DataTest01\"; "
832  "flow:established,to_client; "
833  "dnp3_data; "
834  "content:\"|01 01 01 00 00 00 00 00 00 00|\"; "
835  "sid:3; rev:1;)");
836  FAIL_IF(s == NULL);
837 
838  /* The content of a CRC - should not match. */
839  s = DetectEngineAppendSig(de_ctx,
840  "alert dnp3 any any -> any any ("
841  "msg:\"DetectDNP3DataTest01\"; "
842  "dnp3_data; "
843  "content:\"|72 ef|\"; "
844  "sid:4; rev:1;)");
845  FAIL_IF(s == NULL);
846 
847  SigGroupBuild(de_ctx);
848  DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
849 
850  SCMutexLock(&f.m);
851  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNP3,
852  STREAM_TOSERVER, request, sizeof(request));
853  SCMutexUnlock(&f.m);
854  FAIL_IF(r);
855 
856  FAIL_IF(f.alstate == NULL);
857 
858  SigMatchSignatures(&tv, de_ctx, det_ctx, p);
859  FAIL_IF(!PacketAlertCheck(p, 1));
860  FAIL_IF(!PacketAlertCheck(p, 2));
861  FAIL_IF(PacketAlertCheck(p, 3));
862  FAIL_IF(PacketAlertCheck(p, 4));
863 
864  if (alp_tctx != NULL)
865  AppLayerParserThreadCtxFree(alp_tctx);
866  if (det_ctx != NULL)
867  DetectEngineThreadCtxDeinit(&tv, det_ctx);
868  if (de_ctx != NULL)
869  SigGroupCleanup(de_ctx);
870  if (de_ctx != NULL)
871  DetectEngineCtxFree(de_ctx);
873  FLOW_DESTROY(&f);
874  UTHFreePacket(p);
875  PASS;
876 }
877 
878 /**
879  * Test response (to client) content match.
880  */
881 static int DetectDNP3DataTest02(void)
882 {
884  DetectEngineThreadCtx *det_ctx = NULL;
885  DetectEngineCtx *de_ctx = NULL;
886  Flow f;
887  Packet *p;
888  TcpSession tcp;
889  ThreadVars tv;
890 
891  uint8_t request[] = {
892  /* Link header. */
893  0x05, 0x64, 0x1a, 0xc4, 0x02, 0x00, 0x01, 0x00,
894 
895  /* CRC. */
896  0xa5, 0xe9,
897 
898  /* Transport header. */
899  0xff,
900 
901  /* Application layer. */
902  0xc9, 0x05, 0x0c, 0x01, 0x28, 0x01, 0x00, 0x00,
903  0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
904 
905  /* CRC. */
906  0x72, 0xef,
907 
908  /* Application layer. */
909  0x00, 0x00, 0x00, 0x00, 0x00,
910 
911  /* CRC. */
912  0xff, 0xff,
913  };
914 
915  uint8_t response[] = {
916  /* Link header. */
917  0x05, 0x64, 0x1c, 0x44, 0x01, 0x00, 0x02, 0x00,
918 
919  /* CRC. */
920  0xe2, 0x59,
921 
922  /* Transport header. */
923  0xc3,
924 
925  /* Application layyer. */
926  0xc9, 0x81, 0x00, 0x00, 0x0c, 0x01, 0x28, 0x01,
927  0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00,
928 
929  /* CRC. */
930  0x7a, 0x65,
931 
932  /* Application layer. */
933  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
934 
935  /* CRC. */
936  0xff, 0xff
937  };
938 
939  /* Setup flow. */
940  memset(&f, 0, sizeof(Flow));
941  memset(&tcp, 0, sizeof(TcpSession));
942  memset(&tv, 0, sizeof(ThreadVars));
943  p = UTHBuildPacket(response, sizeof(response), IPPROTO_TCP);
944  FLOW_INITIALIZE(&f);
945  f.alproto = ALPROTO_DNP3;
946  f.protoctx = (void *)&tcp;
947  f.proto = IPPROTO_TCP;
948  f.flags |= FLOW_IPV4;
949  p->flow = &f;
953 
954  de_ctx = DetectEngineCtxInit();
955  FAIL_IF(de_ctx == NULL);
956 
957  /* Either direction - should match. */
958  Signature *s = DetectEngineAppendSig(de_ctx,
959  "alert dnp3 any any -> any any ("
960  "msg:\"DetectDNP3DataTest01\"; "
961  "dnp3_data; "
962  "content:\"|01 01 01 00 00 00 00|\"; "
963  "sid:1; rev:1;)");
964  FAIL_IF(s == NULL);
965 
966  /* To server - should not match. */
967  s = DetectEngineAppendSig(de_ctx,
968  "alert dnp3 any any -> any any ("
969  "msg:\"DetectDNP3DataTest01\"; "
970  "flow:established,to_server; "
971  "dnp3_data; "
972  "content:\"|01 01 01 00 00 00 00|\"; "
973  "sid:2; rev:1;)");
974  FAIL_IF(s == NULL);
975 
976  /* To client - should match. */
977  s = DetectEngineAppendSig(de_ctx,
978  "alert dnp3 any any -> any any ("
979  "msg:\"DetectDNP3DataTest01\"; "
980  "flow:established,to_client; "
981  "dnp3_data; "
982  "content:\"|01 01 01 00 00 00 00|\"; "
983  "sid:3; rev:1;)");
984  FAIL_IF(s == NULL);
985 
986  /* The content of a CRC - should not match. */
987  s = DetectEngineAppendSig(de_ctx,
988  "alert dnp3 any any -> any any ("
989  "msg:\"DetectDNP3DataTest01\"; "
990  "dnp3_data; "
991  "content:\"|7a 65|\"; "
992  "sid:4; rev:1;)");
993  FAIL_IF(s == NULL);
994 
995  SigGroupBuild(de_ctx);
996  DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
997 
998  /* Send through the request, then response. */
999  SCMutexLock(&f.m);
1000  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNP3,
1001  STREAM_TOSERVER, request, sizeof(request));
1002  SCMutexUnlock(&f.m);
1003  FAIL_IF(r);
1004  FAIL_IF(f.alstate == NULL);
1005 
1006  SCMutexLock(&f.m);
1007  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNP3, STREAM_TOCLIENT,
1008  response, sizeof(response));
1009  SCMutexUnlock(&f.m);
1010  FAIL_IF(r);
1011 
1012  SigMatchSignatures(&tv, de_ctx, det_ctx, p);
1013  FAIL_IF(!PacketAlertCheck(p, 1));
1014  FAIL_IF(PacketAlertCheck(p, 2));
1015  FAIL_IF(!PacketAlertCheck(p, 3));
1016  FAIL_IF(PacketAlertCheck(p, 4));
1017 
1018  if (alp_tctx != NULL)
1019  AppLayerParserThreadCtxFree(alp_tctx);
1020  if (det_ctx != NULL)
1021  DetectEngineThreadCtxDeinit(&tv, det_ctx);
1022  if (de_ctx != NULL)
1023  SigGroupCleanup(de_ctx);
1024  if (de_ctx != NULL)
1025  DetectEngineCtxFree(de_ctx);
1027  FLOW_DESTROY(&f);
1028  UTHFreePacket(p);
1029  PASS;
1030 }
1031 
1032 #endif
1033 
1034 static void DetectDNP3FuncRegisterTests(void)
1035 {
1036 #ifdef UNITTESTS
1037  UtRegisterTest("DetectDNP3FuncParseFunctionCodeTest",
1038  DetectDNP3FuncParseFunctionCodeTest);
1039  UtRegisterTest("DetectDNP3FuncTest01", DetectDNP3FuncTest01);
1040 #endif
1041 }
1042 
1043 static void DetectDNP3IndRegisterTests(void)
1044 {
1045 #ifdef UNITTESTS
1046  UtRegisterTest("DetectDNP3IndTestParseAsInteger",
1047  DetectDNP3IndTestParseAsInteger);
1048  UtRegisterTest("DetectDNP3IndTestParseByName",
1049  DetectDNP3IndTestParseByName);
1050 #endif
1051 }
1052 
1053 static void DetectDNP3ObjRegisterTests(void)
1054 {
1055 #ifdef UNITTESTS
1056  UtRegisterTest("DetectDNP3ObjParseTest", DetectDNP3ObjParseTest);
1057  UtRegisterTest("DetectDNP3ObjSetupTest", DetectDNP3ObjSetupTest);
1058 #endif
1059 }
1060 
1061 void DetectDNP3DataRegisterTests(void)
1062 {
1063 #ifdef UNITTESTS
1064  UtRegisterTest("DetectDNP3DataTest01", DetectDNP3DataTest01);
1065  UtRegisterTest("DetectDNP3DataTest02", DetectDNP3DataTest02);
1066 #endif
1067 }
Signature * DetectEngineAppendSig(DetectEngineCtx *de_ctx, const char *sigstr)
Parse and append a Signature into the Detection Engine Context signature list.
DNP3ApplicationHeader response_ah
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect.h:1448
uint16_t flags
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1186
#define SCLogDebug(...)
Definition: util-debug.h:335
int DetectSignatureSetAppProto(Signature *s, AppProto alproto)
struct Flow_ * flow
Definition: decode.h:446
void DetectDNP3Register(void)
Definition: detect-dnp3.c:589
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:350
struct HtpBodyChunk_ * next
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
DNP3ObjectList request_objects
int PacketAlertCheck(Packet *p, uint32_t sid)
Check if a certain sid alerted, this is used in the test functions.
uint8_t proto
Definition: flow.h:344
uint16_t value
Definition: detect-dnp3.h:26
#define PASS
Pass the test.
#define unlikely(expr)
Definition: util-optimize.h:35
Signature * SigInit(DetectEngineCtx *, const char *)
Parses a signature and adds it to the Detection Engine Context.
Signature * sig_list
Definition: detect.h:767
#define FAIL_IF(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:71
void AppLayerParserThreadCtxFree(AppLayerParserThreadCtx *tctx)
Destroys the app layer parser thread context obtained using AppLayerParserThreadCtxAlloc().
#define FLOW_PKT_ESTABLISHED
Definition: flow.h:203
Data needed for Match()
Definition: detect.h:327
DNP3 transaction.
void StreamTcpFreeConfig(char quiet)
Definition: stream-tcp.c:668
int ByteExtractStringUint8(uint8_t *res, int base, uint16_t len, const char *str)
Definition: util-byte.c:284
TmEcode DetectEngineThreadCtxInit(ThreadVars *, void *, void **)
initialize thread specific detection engine context
InspectionBuffer * InspectionBufferGet(DetectEngineThreadCtx *det_ctx, const int list_id)
const char * name
Definition: detect.h:1200
Struct to hold the list of decoded objects.
int DetectEngineInspectGenericList(ThreadVars *tv, const DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Flow *f, const uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
Do the content inspection & validation for a signature.
Signature container.
Definition: detect.h:522
#define TRUE
void DetectAppLayerMpmRegister2(const char *name, int direction, int priority, int(*PrefilterRegister)(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, const DetectBufferMpmRegistery *mpm_reg, int list_id), InspectionBufferGetDataPtr GetData, AppProto alproto, int tx_min_progress)
register a MPM engine
#define SCMutexLock(mut)
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition: detect.h:313
void * protoctx
Definition: flow.h:400
main detection engine ctx
Definition: detect.h:761
int ByteExtractStringUint16(uint16_t *res, int base, uint16_t len, const char *str)
Definition: util-byte.c:264
TmEcode DetectEngineThreadCtxDeinit(ThreadVars *, void *)
DNP3ApplicationHeader request_ah
void * alstate
Definition: flow.h:438
int DetectBufferTypeGetByName(const char *name)
#define str(s)
#define SCCalloc(nm, a)
Definition: util-mem.h:253
uint16_t ind_flags
Definition: detect-dnp3.c:46
#define SCMutexUnlock(mut)
uint8_t variation
#define SIG_FLAG_TOCLIENT
Definition: detect.h:237
#define SIGMATCH_INFO_STICKY_BUFFER
Definition: detect.h:1393
void(* Free)(void *)
Definition: detect.h:1191
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
#define FLOW_DESTROY(f)
Definition: flow-util.h:121
int SigGroupBuild(DetectEngineCtx *de_ctx)
Convert the signature list into the runtime match structure.
uint8_t obj_group
Definition: detect-dnp3.c:50
uint8_t group
uint8_t obj_variation
Definition: detect-dnp3.c:51
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
void SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
wrapper for old tests
Definition: detect.c:1670
DNP3Mapping DNP3IndicatorsMap[]
Definition: detect-dnp3.c:59
struct DetectDNP3_ DetectDNP3
#define SIG_FLAG_TOSERVER
Definition: detect.h:236
#define SCEnter(...)
Definition: util-debug.h:337
void StreamTcpInitConfig(char)
To initialize the stream global configuration data.
Definition: stream-tcp.c:364
void DetectAppLayerInspectEngineRegister2(const char *name, AppProto alproto, uint32_t dir, int progress, InspectEngineFuncPtr2 Callback2, InspectionBufferGetDataPtr GetData)
register inspect engine at start up time
uint8_t flowflags
Definition: decode.h:440
Packet * UTHBuildPacket(uint8_t *payload, uint16_t payload_len, uint8_t ipproto)
UTHBuildPacket is a wrapper that build packets with default ip and port fields.
SCMutex m
Definition: flow.h:394
#define STREAM_TOCLIENT
Definition: stream.h:32
#define FLOW_PKT_TOSERVER
Definition: flow.h:201
AppLayerParserThreadCtx * AppLayerParserThreadCtxAlloc(void)
Gets a new app layer protocol&#39;s parser thread context.
int(* Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition: detect.h:1170
int SigGroupCleanup(DetectEngineCtx *de_ctx)
uint8_t type
Definition: detect.h:319
int DetectEngineInspectBufferGeneric(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const DetectEngineAppInspectionEngine *engine, const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
Do the content inspection & validation for a signature.
#define SCReturnInt(x)
Definition: util-debug.h:341
uint8_t variation
const char * desc
Definition: detect.h:1202
void SigMatchAppendSMToList(Signature *s, SigMatch *new, int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:346
int DetectBufferTypeRegister(const char *name)
int PrefilterGenericMpmRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, const DetectBufferMpmRegistery *mpm_reg, int list_id)
SigMatchCtx * ctx
Definition: detect.h:321
const char * alias
Definition: detect.h:1201
DNP3InternalInd response_iin
DNP3Mapping DNP3FunctionNameMap[]
Definition: detect-dnp3.c:84
#define SCFree(a)
Definition: util-mem.h:322
uint16_t tx_id
void InspectionBufferSetup(InspectionBuffer *buffer, const uint8_t *data, const uint32_t data_len)
setup the buffer with our initial data
uint8_t function_code
Definition: detect-dnp3.c:42
void InspectionBufferApplyTransforms(InspectionBuffer *buffer, const DetectEngineTransforms *transforms)
int AppLayerParserParse(ThreadVars *tv, AppLayerParserThreadCtx *alp_tctx, Flow *f, AppProto alproto, uint8_t flags, const uint8_t *input, uint32_t input_len)
#define SIGMATCH_NOOPT
Definition: detect.h:1369
const char * url
Definition: detect.h:1203
int(* AppLayerTxMatch)(DetectEngineThreadCtx *, Flow *, uint8_t flags, void *alstate, void *txv, const Signature *, const SigMatchCtx *)
Definition: detect.h:1173
#define FLOW_INITIALIZE(f)
Definition: flow-util.h:39
#define STREAM_TOSERVER
Definition: stream.h:31
void UTHFreePacket(Packet *p)
UTHFreePacket: function to release the allocated data from UTHBuildPacket and the packet itself...
#define PKT_HAS_FLOW
Definition: decode.h:1094
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
int DetectBufferSetActiveList(Signature *s, const int list)
const uint8_t * inspect
Definition: detect.h:343
#define DOC_URL
Definition: suricata.h:86
uint8_t * request_buffer
SigMatch * SigMatchAlloc(void)
Definition: detect-parse.c:232
uint32_t request_buffer_len
#define SCReturn
Definition: util-debug.h:339
Per thread variable structure.
Definition: threadvars.h:57
#define FLOW_PKT_TOCLIENT
Definition: flow.h:202
AppProto alproto
application level protocol
Definition: flow.h:409
uint32_t flags
Definition: decode.h:444
#define DOC_VERSION
Definition: suricata.h:91
uint16_t flags
Definition: detect.h:1194
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Flow data structure.
Definition: flow.h:325
#define FLOW_IPV4
Definition: flow.h:94
uint32_t flags
Definition: flow.h:379
uint8_t * response_buffer
#define PKT_STREAM_EST
Definition: decode.h:1092
void DetectAppLayerInspectEngineRegister(const char *name, AppProto alproto, uint32_t dir, int progress, InspectEngineFuncPtr Callback)
register inspect engine at start up time
void(* RegisterTests)(void)
Definition: detect.h:1192
a single match condition for a signature
Definition: detect.h:318
#define FAIL_IF_NOT(expr)
Fail a test if expression to true.
Definition: util-unittest.h:82
DNP3ObjectList response_objects
DetectEngineCtx * DetectEngineCtxInit(void)
uint32_t response_buffer_len