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";
496  sigmatch_table[DETECT_AL_DNP3FUNC].AppLayerTxMatch = DetectDNP3FuncMatch;
497  sigmatch_table[DETECT_AL_DNP3FUNC].Setup = DetectDNP3FuncSetup;
498  sigmatch_table[DETECT_AL_DNP3FUNC].Free = DetectDNP3Free;
500  DetectDNP3FuncRegisterTests;
501 
502  SCReturn;
503 }
504 
505 static void DetectDNP3IndRegister(void)
506 {
507  SCEnter();
508 
509  sigmatch_table[DETECT_AL_DNP3IND].name = "dnp3_ind";
510  sigmatch_table[DETECT_AL_DNP3IND].alias = "dnp3.ind";
512  sigmatch_table[DETECT_AL_DNP3IND].AppLayerTxMatch = DetectDNP3IndMatch;
513  sigmatch_table[DETECT_AL_DNP3IND].Setup = DetectDNP3IndSetup;
514  sigmatch_table[DETECT_AL_DNP3IND].Free = DetectDNP3Free;
516  DetectDNP3IndRegisterTests;
517 
518  SCReturn;
519 }
520 
521 static void DetectDNP3ObjRegister(void)
522 {
523  SCEnter();
524 
525  sigmatch_table[DETECT_AL_DNP3OBJ].name = "dnp3_obj";
526  sigmatch_table[DETECT_AL_DNP3OBJ].alias = "dnp3.obj";
528  sigmatch_table[DETECT_AL_DNP3OBJ].AppLayerTxMatch = DetectDNP3ObjMatch;
529  sigmatch_table[DETECT_AL_DNP3OBJ].Setup = DetectDNP3ObjSetup;
530  sigmatch_table[DETECT_AL_DNP3OBJ].Free = DetectDNP3Free;
532  DetectDNP3ObjRegisterTests;
533 
534  SCReturn;
535 }
536 
537 static int DetectDNP3DataSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
538 {
539  SCEnter();
541  return -1;
542 
543  if (DetectBufferSetActiveList(s, g_dnp3_data_buffer_id) != 0)
544  return -1;
545 
546  SCReturnInt(0);
547 }
548 
549 static void DetectDNP3DataRegister(void)
550 {
551  SCEnter();
552 
553  sigmatch_table[DETECT_AL_DNP3DATA].name = "dnp3.data";
554  sigmatch_table[DETECT_AL_DNP3DATA].alias = "dnp3_data";
555  sigmatch_table[DETECT_AL_DNP3DATA].Setup = DetectDNP3DataSetup;
557  DetectDNP3DataRegisterTests;
558 
560 
564  GetDNP3Data);
566  PrefilterGenericMpmRegister, GetDNP3Data,
567  ALPROTO_DNP3, 0);
568 
572  GetDNP3Data);
574  PrefilterGenericMpmRegister, GetDNP3Data,
575  ALPROTO_DNP3, 0);
576 
577  g_dnp3_data_buffer_id = DetectBufferTypeGetByName("dnp3_data");
578  SCReturn;
579 }
580 
582 {
583  DetectDNP3DataRegister();
584 
585  DetectDNP3FuncRegister();
586  DetectDNP3IndRegister();
587  DetectDNP3ObjRegister();
588 
589  /* Register the list of func, ind and obj. */
592  DetectEngineInspectDNP3);
595  DetectEngineInspectDNP3);
596 
597  g_dnp3_match_buffer_id = DetectBufferTypeRegister("dnp3");
598 
599 }
600 
601 #ifdef UNITTESTS
602 
603 #include "util-unittest.h"
604 #include "util-unittest-helper.h"
605 #include "app-layer-parser.h"
606 #include "detect-engine.h"
607 #include "flow-util.h"
608 #include "stream-tcp.h"
609 
610 static int DetectDNP3FuncParseFunctionCodeTest(void)
611 {
612  uint8_t fc;
613 
614  /* Valid. */
615  FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("0", &fc));
616  FAIL_IF(fc != 0);
617 
618  FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("1", &fc));
619  FAIL_IF(fc != 1);
620 
621  FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("254", &fc));
622  FAIL_IF(fc != 254);
623 
624  FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("255", &fc));
625  FAIL_IF(fc != 255);
626 
627  FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("confirm", &fc));
628  FAIL_IF(fc != 0);
629 
630  FAIL_IF_NOT(DetectDNP3FuncParseFunctionCode("CONFIRM", &fc));
631  FAIL_IF(fc != 0);
632 
633  /* Invalid. */
634  FAIL_IF(DetectDNP3FuncParseFunctionCode("", &fc));
635  FAIL_IF(DetectDNP3FuncParseFunctionCode("-1", &fc));
636  FAIL_IF(DetectDNP3FuncParseFunctionCode("-2", &fc));
637  FAIL_IF(DetectDNP3FuncParseFunctionCode("256", &fc));
638  FAIL_IF(DetectDNP3FuncParseFunctionCode("unknown_function_code", &fc));
639 
640  PASS;
641 }
642 
643 static int DetectDNP3FuncTest01(void)
644 {
645  DetectEngineCtx *de_ctx = NULL;
646  DetectDNP3 *dnp3func = NULL;
647 
648  de_ctx = DetectEngineCtxInit();
649  FAIL_IF_NULL(de_ctx);
650 
651  de_ctx->sig_list = SigInit(de_ctx,
652  "alert dnp3 any any -> any any "
653  "(msg:\"SURICATA DNP3 Write request\"; "
654  "dnp3_func:2; sid:5000009; rev:1;)");
655  FAIL_IF_NULL(de_ctx->sig_list);
656 
657  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_dnp3_match_buffer_id]);
658  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_dnp3_match_buffer_id]->ctx);
659 
660  dnp3func = (DetectDNP3 *)de_ctx->sig_list->sm_lists_tail[g_dnp3_match_buffer_id]->ctx;
661  FAIL_IF(dnp3func->function_code != 2);
662 
663  if (de_ctx != NULL) {
664  DetectEngineCtxFree(de_ctx);
665  }
666  PASS;
667 }
668 
669 static int DetectDNP3IndTestParseAsInteger(void)
670 {
671  uint16_t flags = 0;
672 
673  FAIL_IF(!DetectDNP3IndParse("0", &flags));
674  FAIL_IF(flags != 0);
675  FAIL_IF(!DetectDNP3IndParse("1", &flags));
676  FAIL_IF(flags != 0x0001);
677 
678  FAIL_IF(!DetectDNP3IndParse("0x0", &flags));
679  FAIL_IF(flags != 0);
680  FAIL_IF(!DetectDNP3IndParse("0x0000", &flags));
681  FAIL_IF(flags != 0);
682  FAIL_IF(!DetectDNP3IndParse("0x0001", &flags));
683  FAIL_IF(flags != 0x0001);
684 
685  FAIL_IF(!DetectDNP3IndParse("0x8421", &flags));
686  FAIL_IF(flags != 0x8421);
687 
688  FAIL_IF(DetectDNP3IndParse("a", &flags));
689 
690  PASS;
691 }
692 
693 static int DetectDNP3IndTestParseByName(void)
694 {
695  uint16_t flags = 0;
696 
697  FAIL_IF(!DetectDNP3IndParse("all_stations", &flags));
698  FAIL_IF(!(flags & 0x0100));
699  FAIL_IF(!DetectDNP3IndParse("class_1_events , class_2_events", &flags));
700  FAIL_IF(!(flags & 0x0200));
701  FAIL_IF(!(flags & 0x0400));
702  FAIL_IF((flags & 0xf9ff));
703 
704  FAIL_IF(DetectDNP3IndParse("something", &flags));
705 
706  PASS;
707 }
708 
709 static int DetectDNP3ObjSetupTest(void)
710 {
711  DetectEngineCtx *de_ctx = NULL;
712  DetectDNP3 *detect = NULL;
713 
714  de_ctx = DetectEngineCtxInit();
715  FAIL_IF(de_ctx == NULL);
716 
717  de_ctx->sig_list = SigInit(de_ctx,
718  "alert dnp3 any any -> any any "
719  "(msg:\"SURICATA DNP3 Object Test\"; "
720  "dnp3_obj:99,99; sid:1; rev:1;)");
721  FAIL_IF(de_ctx->sig_list == NULL);
722 
723  FAIL_IF(de_ctx->sig_list->sm_lists_tail[g_dnp3_match_buffer_id] == NULL);
724  FAIL_IF(de_ctx->sig_list->sm_lists_tail[g_dnp3_match_buffer_id]->ctx == NULL);
725 
726  detect = (DetectDNP3 *)de_ctx->sig_list->sm_lists_tail[g_dnp3_match_buffer_id]->ctx;
727  FAIL_IF(detect->obj_group != 99);
728  FAIL_IF(detect->obj_variation != 99);
729 
730  if (de_ctx != NULL) {
731  DetectEngineCtxFree(de_ctx);
732  }
733  PASS;
734 }
735 
736 static int DetectDNP3ObjParseTest(void)
737 {
738  uint8_t group, var;
739 
740  FAIL_IF(!DetectDNP3ObjParse("0,0", &group, &var));
741  FAIL_IF(group != 0 || var != 0);
742 
743  FAIL_IF(!DetectDNP3ObjParse("255,255", &group, &var));
744  FAIL_IF(group != 255 || var != 255);
745 
746  FAIL_IF(DetectDNP3ObjParse("-1,-1", &group, &var));
747  FAIL_IF(DetectDNP3ObjParse("256,256", &group, &var));
748  FAIL_IF(DetectDNP3ObjParse("a,1", &group, &var));
749  FAIL_IF(DetectDNP3ObjParse("1,a", &group, &var));
750 
751  PASS;
752 }
753 
754 /**
755  * Test request (to server) content match.
756  */
757 static int DetectDNP3DataTest01(void)
758 {
760  DetectEngineThreadCtx *det_ctx = NULL;
761  DetectEngineCtx *de_ctx = NULL;
762  Flow f;
763  Packet *p;
764  TcpSession tcp;
765  ThreadVars tv;
766 
767  uint8_t request[] = {
768  0x05, 0x64, 0x1a, 0xc4, 0x02, 0x00, 0x01, 0x00,
769  0xa5, 0xe9,
770 
771  0xff, 0xc9, 0x05, 0x0c, 0x01, 0x28, 0x01, 0x00,
772  0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
773 
774  /* CRC. */
775  0x72, 0xef,
776 
777  0x00, 0x00, 0x00, 0x00, 0x00,
778 
779  /* CRC. */
780  0xff, 0xff,
781  };
782 
783  /* Setup flow. */
784  memset(&f, 0, sizeof(Flow));
785  memset(&tcp, 0, sizeof(TcpSession));
786  memset(&tv, 0, sizeof(ThreadVars));
787  p = UTHBuildPacket(request, sizeof(request), IPPROTO_TCP);
788  FLOW_INITIALIZE(&f);
789  f.alproto = ALPROTO_DNP3;
790  f.protoctx = (void *)&tcp;
791  f.proto = IPPROTO_TCP;
792  f.flags |= FLOW_IPV4;
793  p->flow = &f;
797 
798  de_ctx = DetectEngineCtxInit();
799  FAIL_IF(de_ctx == NULL);
800 
801  /* Either direction - should match. */
802  Signature *s = DetectEngineAppendSig(de_ctx,
803  "alert dnp3 any any -> any any ("
804  "msg:\"DetectDNP3DataTest01\"; "
805  "dnp3_data; "
806  "content:\"|01 01 01 00 00 00 00 00 00 00|\"; "
807  "sid:1; rev:1;)");
808  FAIL_IF(s == NULL);
809 
810  /* To server - should match. */
811  s = DetectEngineAppendSig(de_ctx,
812  "alert dnp3 any any -> any any ("
813  "msg:\"DetectDNP3DataTest01\"; "
814  "flow:established,to_server; "
815  "dnp3_data; "
816  "content:\"|01 01 01 00 00 00 00 00 00 00|\"; "
817  "sid:2; rev:1;)");
818  FAIL_IF(s == NULL);
819 
820  /* To client - should not match. */
821  s = DetectEngineAppendSig(de_ctx,
822  "alert dnp3 any any -> any any ("
823  "msg:\"DetectDNP3DataTest01\"; "
824  "flow:established,to_client; "
825  "dnp3_data; "
826  "content:\"|01 01 01 00 00 00 00 00 00 00|\"; "
827  "sid:3; rev:1;)");
828  FAIL_IF(s == NULL);
829 
830  /* The content of a CRC - should not match. */
831  s = DetectEngineAppendSig(de_ctx,
832  "alert dnp3 any any -> any any ("
833  "msg:\"DetectDNP3DataTest01\"; "
834  "dnp3_data; "
835  "content:\"|72 ef|\"; "
836  "sid:4; rev:1;)");
837  FAIL_IF(s == NULL);
838 
839  SigGroupBuild(de_ctx);
840  DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
841 
842  SCMutexLock(&f.m);
843  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNP3,
844  STREAM_TOSERVER, request, sizeof(request));
845  SCMutexUnlock(&f.m);
846  FAIL_IF(r);
847 
848  FAIL_IF(f.alstate == NULL);
849 
850  SigMatchSignatures(&tv, de_ctx, det_ctx, p);
851  FAIL_IF(!PacketAlertCheck(p, 1));
852  FAIL_IF(!PacketAlertCheck(p, 2));
853  FAIL_IF(PacketAlertCheck(p, 3));
854  FAIL_IF(PacketAlertCheck(p, 4));
855 
856  if (alp_tctx != NULL)
857  AppLayerParserThreadCtxFree(alp_tctx);
858  if (det_ctx != NULL)
859  DetectEngineThreadCtxDeinit(&tv, det_ctx);
860  if (de_ctx != NULL)
861  SigGroupCleanup(de_ctx);
862  if (de_ctx != NULL)
863  DetectEngineCtxFree(de_ctx);
865  FLOW_DESTROY(&f);
866  UTHFreePacket(p);
867  PASS;
868 }
869 
870 /**
871  * Test response (to client) content match.
872  */
873 static int DetectDNP3DataTest02(void)
874 {
876  DetectEngineThreadCtx *det_ctx = NULL;
877  DetectEngineCtx *de_ctx = NULL;
878  Flow f;
879  Packet *p;
880  TcpSession tcp;
881  ThreadVars tv;
882 
883  uint8_t request[] = {
884  /* Link header. */
885  0x05, 0x64, 0x1a, 0xc4, 0x02, 0x00, 0x01, 0x00,
886 
887  /* CRC. */
888  0xa5, 0xe9,
889 
890  /* Transport header. */
891  0xff,
892 
893  /* Application layer. */
894  0xc9, 0x05, 0x0c, 0x01, 0x28, 0x01, 0x00, 0x00,
895  0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
896 
897  /* CRC. */
898  0x72, 0xef,
899 
900  /* Application layer. */
901  0x00, 0x00, 0x00, 0x00, 0x00,
902 
903  /* CRC. */
904  0xff, 0xff,
905  };
906 
907  uint8_t response[] = {
908  /* Link header. */
909  0x05, 0x64, 0x1c, 0x44, 0x01, 0x00, 0x02, 0x00,
910 
911  /* CRC. */
912  0xe2, 0x59,
913 
914  /* Transport header. */
915  0xc3,
916 
917  /* Application layyer. */
918  0xc9, 0x81, 0x00, 0x00, 0x0c, 0x01, 0x28, 0x01,
919  0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00,
920 
921  /* CRC. */
922  0x7a, 0x65,
923 
924  /* Application layer. */
925  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
926 
927  /* CRC. */
928  0xff, 0xff
929  };
930 
931  /* Setup flow. */
932  memset(&f, 0, sizeof(Flow));
933  memset(&tcp, 0, sizeof(TcpSession));
934  memset(&tv, 0, sizeof(ThreadVars));
935  p = UTHBuildPacket(response, sizeof(response), IPPROTO_TCP);
936  FLOW_INITIALIZE(&f);
937  f.alproto = ALPROTO_DNP3;
938  f.protoctx = (void *)&tcp;
939  f.proto = IPPROTO_TCP;
940  f.flags |= FLOW_IPV4;
941  p->flow = &f;
945 
946  de_ctx = DetectEngineCtxInit();
947  FAIL_IF(de_ctx == NULL);
948 
949  /* Either direction - should match. */
950  Signature *s = DetectEngineAppendSig(de_ctx,
951  "alert dnp3 any any -> any any ("
952  "msg:\"DetectDNP3DataTest01\"; "
953  "dnp3_data; "
954  "content:\"|01 01 01 00 00 00 00|\"; "
955  "sid:1; rev:1;)");
956  FAIL_IF(s == NULL);
957 
958  /* To server - should not match. */
959  s = DetectEngineAppendSig(de_ctx,
960  "alert dnp3 any any -> any any ("
961  "msg:\"DetectDNP3DataTest01\"; "
962  "flow:established,to_server; "
963  "dnp3_data; "
964  "content:\"|01 01 01 00 00 00 00|\"; "
965  "sid:2; rev:1;)");
966  FAIL_IF(s == NULL);
967 
968  /* To client - should match. */
969  s = DetectEngineAppendSig(de_ctx,
970  "alert dnp3 any any -> any any ("
971  "msg:\"DetectDNP3DataTest01\"; "
972  "flow:established,to_client; "
973  "dnp3_data; "
974  "content:\"|01 01 01 00 00 00 00|\"; "
975  "sid:3; rev:1;)");
976  FAIL_IF(s == NULL);
977 
978  /* The content of a CRC - should not match. */
979  s = DetectEngineAppendSig(de_ctx,
980  "alert dnp3 any any -> any any ("
981  "msg:\"DetectDNP3DataTest01\"; "
982  "dnp3_data; "
983  "content:\"|7a 65|\"; "
984  "sid:4; rev:1;)");
985  FAIL_IF(s == NULL);
986 
987  SigGroupBuild(de_ctx);
988  DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
989 
990  /* Send through the request, then response. */
991  SCMutexLock(&f.m);
992  int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNP3,
993  STREAM_TOSERVER, request, sizeof(request));
994  SCMutexUnlock(&f.m);
995  FAIL_IF(r);
996  FAIL_IF(f.alstate == NULL);
997 
998  SCMutexLock(&f.m);
999  r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNP3, STREAM_TOCLIENT,
1000  response, sizeof(response));
1001  SCMutexUnlock(&f.m);
1002  FAIL_IF(r);
1003 
1004  SigMatchSignatures(&tv, de_ctx, det_ctx, p);
1005  FAIL_IF(!PacketAlertCheck(p, 1));
1006  FAIL_IF(PacketAlertCheck(p, 2));
1007  FAIL_IF(!PacketAlertCheck(p, 3));
1008  FAIL_IF(PacketAlertCheck(p, 4));
1009 
1010  if (alp_tctx != NULL)
1011  AppLayerParserThreadCtxFree(alp_tctx);
1012  if (det_ctx != NULL)
1013  DetectEngineThreadCtxDeinit(&tv, det_ctx);
1014  if (de_ctx != NULL)
1015  SigGroupCleanup(de_ctx);
1016  if (de_ctx != NULL)
1017  DetectEngineCtxFree(de_ctx);
1019  FLOW_DESTROY(&f);
1020  UTHFreePacket(p);
1021  PASS;
1022 }
1023 
1024 #endif
1025 
1026 static void DetectDNP3FuncRegisterTests(void)
1027 {
1028 #ifdef UNITTESTS
1029  UtRegisterTest("DetectDNP3FuncParseFunctionCodeTest",
1030  DetectDNP3FuncParseFunctionCodeTest);
1031  UtRegisterTest("DetectDNP3FuncTest01", DetectDNP3FuncTest01);
1032 #endif
1033 }
1034 
1035 static void DetectDNP3IndRegisterTests(void)
1036 {
1037 #ifdef UNITTESTS
1038  UtRegisterTest("DetectDNP3IndTestParseAsInteger",
1039  DetectDNP3IndTestParseAsInteger);
1040  UtRegisterTest("DetectDNP3IndTestParseByName",
1041  DetectDNP3IndTestParseByName);
1042 #endif
1043 }
1044 
1045 static void DetectDNP3ObjRegisterTests(void)
1046 {
1047 #ifdef UNITTESTS
1048  UtRegisterTest("DetectDNP3ObjParseTest", DetectDNP3ObjParseTest);
1049  UtRegisterTest("DetectDNP3ObjSetupTest", DetectDNP3ObjSetupTest);
1050 #endif
1051 }
1052 
1053 void DetectDNP3DataRegisterTests(void)
1054 {
1055 #ifdef UNITTESTS
1056  UtRegisterTest("DetectDNP3DataTest01", DetectDNP3DataTest01);
1057  UtRegisterTest("DetectDNP3DataTest02", DetectDNP3DataTest02);
1058 #endif
1059 }
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:1439
uint16_t flags
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1179
#define SCLogDebug(...)
Definition: util-debug.h:335
int DetectSignatureSetAppProto(Signature *s, AppProto alproto)
struct Flow_ * flow
Definition: decode.h:445
void DetectDNP3Register(void)
Definition: detect-dnp3.c:581
#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:762
#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:322
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
InspectionBuffer * InspectionBufferGet(DetectEngineThreadCtx *det_ctx, const int list_id)
const char * name
Definition: detect.h:1193
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:517
#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:308
void * protoctx
Definition: flow.h:400
main detection engine ctx
Definition: detect.h:756
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:233
#define SIGMATCH_INFO_STICKY_BUFFER
Definition: detect.h:1386
void(* Free)(void *)
Definition: detect.h:1184
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
#define FLOW_DESTROY(f)
Definition: flow-util.h:119
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:232
#define SCEnter(...)
Definition: util-debug.h:337
void StreamTcpInitConfig(char)
To initialize the stream global configuration data.
Definition: stream-tcp.c:365
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:439
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:1163
int SigGroupCleanup(DetectEngineCtx *de_ctx)
uint8_t type
Definition: detect.h:314
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
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)
int PrefilterGenericMpmRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, const DetectBufferMpmRegistery *mpm_reg, int list_id)
SigMatchCtx * ctx
Definition: detect.h:316
const char * alias
Definition: detect.h:1194
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)
#define SIGMATCH_NOOPT
Definition: detect.h:1362
int(* AppLayerTxMatch)(DetectEngineThreadCtx *, Flow *, uint8_t flags, void *alstate, void *txv, const Signature *, const SigMatchCtx *)
Definition: detect.h:1166
#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:1090
#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:338
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:443
uint16_t flags
Definition: detect.h:1187
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:1088
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:1185
a single match condition for a signature
Definition: detect.h:313
#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