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