suricata
detect-modbus.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2014 ANSSI
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
19  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /**
29  * \file
30  *
31  * \author David DIALLO <diallo@et.esiea.fr>
32  *
33  * Implements the Modbus function and access keywords
34  * You can specify a:
35  * - concrete function like Modbus:
36  * function 8, subfunction 4 (diagnostic: Force Listen Only Mode)
37  * - data (in primary table) register access (r/w) like Modbus:
38  * access read coils, address 1000 (.i.e Read coils: at address 1000)
39  * - write data value at specific address Modbus:
40  * access write, address 1500<>2000, value >2000 (Write multiple coils/register:
41  * at address between 1500 and 2000 value greater than 2000)
42  */
43 
44 #include "suricata-common.h"
45 
46 #include "detect.h"
47 #include "detect-parse.h"
48 #include "detect-engine.h"
49 
50 #include "detect-modbus.h"
51 #include "detect-engine-modbus.h"
52 
53 #include "util-debug.h"
54 
55 #include "app-layer-modbus.h"
56 
57 #include "stream-tcp.h"
58 
59 /**
60  * \brief Regex for parsing the Modbus unit id string
61  */
62 #define PARSE_REGEX_UNIT_ID "^\\s*\"?\\s*unit\\s+([<>]?\\d+)(<>\\d+)?(,\\s*(.*))?\\s*\"?\\s*$"
63 static pcre *unit_id_parse_regex;
64 static pcre_extra *unit_id_parse_regex_study;
65 
66 /**
67  * \brief Regex for parsing the Modbus function string
68  */
69 #define PARSE_REGEX_FUNCTION "^\\s*\"?\\s*function\\s*(!?[A-z0-9]+)(,\\s*subfunction\\s+(\\d+))?\\s*\"?\\s*$"
70 static pcre *function_parse_regex;
71 static pcre_extra *function_parse_regex_study;
72 
73 /**
74  * \brief Regex for parsing the Modbus access string
75  */
76 #define PARSE_REGEX_ACCESS "^\\s*\"?\\s*access\\s*(read|write)\\s*(discretes|coils|input|holding)?(,\\s*address\\s+([<>]?\\d+)(<>\\d+)?(,\\s*value\\s+([<>]?\\d+)(<>\\d+)?)?)?\\s*\"?\\s*$"
77 static pcre *access_parse_regex;
78 static pcre_extra *access_parse_regex_study;
79 
80 static int g_modbus_buffer_id = 0;
81 
82 #define MAX_SUBSTRINGS 30
83 
84 void DetectModbusRegisterTests(void);
85 
86 /** \internal
87  *
88  * \brief this function will free memory associated with DetectModbus
89  *
90  * \param ptr pointer to DetectModbus
91  */
92 static void DetectModbusFree(void *ptr) {
93  SCEnter();
94  DetectModbus *modbus = (DetectModbus *) ptr;
95 
96  if(modbus) {
97  if (modbus->subfunction)
98  SCFree(modbus->subfunction);
99 
100  if (modbus->unit_id)
101  SCFree(modbus->unit_id);
102 
103  if (modbus->address)
104  SCFree(modbus->address);
105 
106  if (modbus->data)
107  SCFree(modbus->data);
108 
109  SCFree(modbus);
110  }
111 }
112 
113 /** \internal
114  *
115  * \brief This function is used to parse Modbus parameters in access mode
116  *
117  * \param str Pointer to the user provided id option
118  *
119  * \retval Pointer to DetectModbusData on success or NULL on failure
120  */
121 static DetectModbus *DetectModbusAccessParse(const char *str)
122 {
123  SCEnter();
124  DetectModbus *modbus = NULL;
125 
126  char arg[MAX_SUBSTRINGS];
127  int ov[MAX_SUBSTRINGS], ret, res;
128 
129  ret = pcre_exec(access_parse_regex, access_parse_regex_study, str, strlen(str), 0, 0, ov, MAX_SUBSTRINGS);
130 
131  if (ret < 1)
132  goto error;
133 
134  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 1, arg, MAX_SUBSTRINGS);
135  if (res < 0) {
136  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
137  goto error;
138  }
139 
140  /* We have a correct Modbus option */
141  modbus = (DetectModbus *) SCCalloc(1, sizeof(DetectModbus));
142  if (unlikely(modbus == NULL))
143  goto error;
144 
145  if (strcmp(arg, "read") == 0)
146  modbus->type = MODBUS_TYP_READ;
147  else if (strcmp(arg, "write") == 0)
148  modbus->type = MODBUS_TYP_WRITE;
149  else
150  goto error;
151 
152  if (ret > 2) {
153  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 2, arg, MAX_SUBSTRINGS);
154  if (res < 0) {
155  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
156  goto error;
157  }
158 
159  if (*arg != '\0') {
160  if (strcmp(arg, "discretes") == 0) {
161  if (modbus->type == MODBUS_TYP_WRITE)
162  /* Discrete access is only read access. */
163  goto error;
164 
165  modbus->type |= MODBUS_TYP_DISCRETES;
166  }
167  else if (strcmp(arg, "coils") == 0) {
168  modbus->type |= MODBUS_TYP_COILS;
169  }
170  else if (strcmp(arg, "input") == 0) {
171  if (modbus->type == MODBUS_TYP_WRITE) {
172  /* Input access is only read access. */
173  goto error;
174  }
175 
176  modbus->type |= MODBUS_TYP_INPUT;
177  }
178  else if (strcmp(arg, "holding") == 0) {
179  modbus->type |= MODBUS_TYP_HOLDING;
180  }
181  else
182  goto error;
183  }
184 
185  if (ret > 4) {
186  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 4, arg, MAX_SUBSTRINGS);
187  if (res < 0) {
188  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
189  goto error;
190  }
191 
192  /* We have a correct address option */
193  modbus->address = (DetectModbusValue *) SCCalloc(1, sizeof(DetectModbusValue));
194  if (unlikely(modbus->address == NULL))
195  goto error;
196 
197  if (arg[0] == '>') {
198  modbus->address->min = atoi((const char*) (arg+1));
199  modbus->address->mode = DETECT_MODBUS_GT;
200  } else if (arg[0] == '<') {
201  modbus->address->min = atoi((const char*) (arg+1));
202  modbus->address->mode = DETECT_MODBUS_LT;
203  } else {
204  modbus->address->min = atoi((const char*) arg);
205  }
206  SCLogDebug("and min/equal address %d", modbus->address->min);
207 
208  if (ret > 5) {
209  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 5, arg, MAX_SUBSTRINGS);
210  if (res < 0) {
211  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
212  goto error;
213  }
214 
215  if (*arg != '\0') {
216  modbus->address->max = atoi((const char*) (arg+2));
217  modbus->address->mode = DETECT_MODBUS_RA;
218  SCLogDebug("and max address %d", modbus->address->max);
219  }
220 
221  if (ret > 7) {
222  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 7, arg, MAX_SUBSTRINGS);
223  if (res < 0) {
224  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
225  goto error;
226  }
227 
228  if (modbus->address->mode != DETECT_MODBUS_EQ) {
229  SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords (address range and value).");
230  goto error;
231  }
232 
233  /* We have a correct address option */
234  if (modbus->type == MODBUS_TYP_READ)
235  /* Value access is only possible in write access. */
236  goto error;
237 
238  modbus->data = (DetectModbusValue *) SCCalloc(1, sizeof(DetectModbusValue));
239  if (unlikely(modbus->data == NULL))
240  goto error;
241 
242  if (arg[0] == '>') {
243  modbus->data->min = atoi((const char*) (arg+1));
244  modbus->data->mode = DETECT_MODBUS_GT;
245  } else if (arg[0] == '<') {
246  modbus->data->min = atoi((const char*) (arg+1));
247  modbus->data->mode = DETECT_MODBUS_LT;
248  } else {
249  modbus->data->min = atoi((const char*) arg);
250  }
251  SCLogDebug("and min/equal value %d", modbus->data->min);
252 
253  if (ret > 8) {
254  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 8, arg, MAX_SUBSTRINGS);
255  if (res < 0) {
256  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
257  goto error;
258  }
259 
260  if (*arg != '\0') {
261  modbus->data->max = atoi((const char*) (arg+2));
262  modbus->data->mode = DETECT_MODBUS_RA;
263  SCLogDebug("and max value %d", modbus->data->max);
264  }
265  }
266  }
267  }
268  }
269  }
270 
271  SCReturnPtr(modbus, "DetectModbusAccess");
272 
273 error:
274  if (modbus != NULL)
275  DetectModbusFree(modbus);
276 
277  SCReturnPtr(NULL, "DetectModbus");
278 }
279 
280 /** \internal
281  *
282  * \brief This function is used to parse Modbus parameters in function mode
283  *
284  * \param str Pointer to the user provided id option
285  *
286  * \retval id_d pointer to DetectModbusData on success
287  * \retval NULL on failure
288  */
289 static DetectModbus *DetectModbusFunctionParse(const char *str)
290 {
291  SCEnter();
292  DetectModbus *modbus = NULL;
293 
294  char arg[MAX_SUBSTRINGS], *ptr = arg;
295  int ov[MAX_SUBSTRINGS], res, ret;
296 
297  ret = pcre_exec(function_parse_regex, function_parse_regex_study, str, strlen(str), 0, 0, ov, MAX_SUBSTRINGS);
298 
299  if (ret < 1)
300  goto error;
301 
302  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 1, arg, MAX_SUBSTRINGS);
303  if (res < 0) {
304  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
305  goto error;
306  }
307 
308  /* We have a correct Modbus function option */
309  modbus = (DetectModbus *) SCCalloc(1, sizeof(DetectModbus));
310  if (unlikely(modbus == NULL))
311  goto error;
312 
313  if (isdigit((unsigned char)ptr[0])) {
314  modbus->function = atoi((const char*) ptr);
315  /* Function code 0 is managed by decoder_event INVALID_FUNCTION_CODE */
316  if (modbus->function == MODBUS_FUNC_NONE) {
318  "Invalid argument \"%d\" supplied to modbus function keyword.",
319  modbus->function);
320  goto error;
321  }
322 
323  SCLogDebug("will look for modbus function %d", modbus->function);
324 
325  if (ret > 2) {
326  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 3, arg, MAX_SUBSTRINGS);
327  if (res < 0) {
328  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
329  goto error;
330  }
331 
332  /* We have a correct address option */
333  modbus->subfunction =(uint16_t *) SCCalloc(1, sizeof(uint16_t));
334  if (modbus->subfunction == NULL)
335  goto error;
336 
337  *(modbus->subfunction) = atoi((const char*) arg);
338  SCLogDebug("and subfunction %d", *(modbus->subfunction));
339  }
340  } else {
341  uint8_t neg = 0;
342 
343  if (ptr[0] == '!') {
344  neg = 1;
345  ptr++;
346  }
347 
348  if (strcmp("assigned", ptr) == 0)
350  else if (strcmp("unassigned", ptr) == 0)
352  else if (strcmp("public", ptr) == 0)
354  else if (strcmp("user", ptr) == 0)
356  else if (strcmp("reserved", ptr) == 0)
357  modbus->category = MODBUS_CAT_RESERVED;
358  else if (strcmp("all", ptr) == 0)
359  modbus->category = MODBUS_CAT_ALL;
360 
361  if (neg)
362  modbus->category = ~modbus->category;
363  SCLogDebug("will look for modbus category function %d", modbus->category);
364  }
365 
366  SCReturnPtr(modbus, "DetectModbusFunction");
367 
368 error:
369  if (modbus != NULL)
370  DetectModbusFree(modbus);
371 
372  SCReturnPtr(NULL, "DetectModbus");
373 }
374 
375 /** \internal
376  *
377  * \brief This function is used to parse Modbus parameters in unit id mode
378  *
379  * \param str Pointer to the user provided id option
380  *
381  * \retval Pointer to DetectModbusUnit on success or NULL on failure
382  */
383 static DetectModbus *DetectModbusUnitIdParse(const char *str)
384 {
385  SCEnter();
386  DetectModbus *modbus = NULL;
387 
388  char arg[MAX_SUBSTRINGS];
389  int ov[MAX_SUBSTRINGS], ret, res;
390 
391  ret = pcre_exec(unit_id_parse_regex, unit_id_parse_regex_study, str, strlen(str), 0, 0, ov, MAX_SUBSTRINGS);
392 
393  if (ret < 1)
394  goto error;
395 
396  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 1, arg, MAX_SUBSTRINGS);
397  if (res < 0) {
398  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
399  goto error;
400  }
401 
402  if (ret > 3) {
403  /* We have more Modbus option */
404  const char *str_ptr;
405 
406  res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 4, &str_ptr);
407  if (res < 0) {
408  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
409  goto error;
410  }
411 
412  if ((modbus = DetectModbusFunctionParse(str_ptr)) == NULL) {
413  if ((modbus = DetectModbusAccessParse(str_ptr)) == NULL) {
414  SCLogError(SC_ERR_PCRE_MATCH, "invalid modbus option");
415  goto error;
416  }
417  }
418  } else {
419  /* We have only unit id Modbus option */
420  modbus = (DetectModbus *) SCCalloc(1, sizeof(DetectModbus));
421  if (unlikely(modbus == NULL))
422  goto error;
423  }
424 
425  /* We have a correct unit id option */
426  modbus->unit_id = (DetectModbusValue *) SCCalloc(1, sizeof(DetectModbusValue));
427  if (unlikely(modbus->unit_id == NULL))
428  goto error;
429 
430  if (arg[0] == '>') {
431  modbus->unit_id->min = atoi((const char*) (arg+1));
432  modbus->unit_id->mode = DETECT_MODBUS_GT;
433  } else if (arg[0] == '<') {
434  modbus->unit_id->min = atoi((const char*) (arg+1));
435  modbus->unit_id->mode = DETECT_MODBUS_LT;
436  } else {
437  modbus->unit_id->min = atoi((const char*) arg);
438  }
439  SCLogDebug("and min/equal unit id %d", modbus->unit_id->min);
440 
441  if (ret > 2) {
442  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 2, arg, MAX_SUBSTRINGS);
443  if (res < 0) {
444  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
445  goto error;
446  }
447 
448  if (*arg != '\0') {
449  modbus->unit_id->max = atoi((const char*) (arg+2));
450  modbus->unit_id->mode = DETECT_MODBUS_RA;
451  SCLogDebug("and max unit id %d", modbus->unit_id->max);
452  }
453  }
454 
455  SCReturnPtr(modbus, "DetectModbusUnitId");
456 
457 error:
458  if (modbus != NULL)
459  DetectModbusFree(modbus);
460 
461  SCReturnPtr(NULL, "DetectModbus");
462 }
463 
464 
465 /** \internal
466  *
467  * \brief this function is used to add the parsed "id" option into the current signature
468  *
469  * \param de_ctx Pointer to the Detection Engine Context
470  * \param s Pointer to the Current Signature
471  * \param str Pointer to the user provided "id" option
472  *
473  * \retval 0 on Success or -1 on Failure
474  */
475 static int DetectModbusSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
476 {
477  SCEnter();
478  DetectModbus *modbus = NULL;
479  SigMatch *sm = NULL;
480 
482  return -1;
483 
484  if ((modbus = DetectModbusUnitIdParse(str)) == NULL) {
485  if ((modbus = DetectModbusFunctionParse(str)) == NULL) {
486  if ((modbus = DetectModbusAccessParse(str)) == NULL) {
487  SCLogError(SC_ERR_PCRE_MATCH, "invalid modbus option");
488  goto error;
489  }
490  }
491  }
492 
493  /* Okay so far so good, lets get this into a SigMatch and put it in the Signature. */
494  sm = SigMatchAlloc();
495  if (sm == NULL)
496  goto error;
497 
498  sm->type = DETECT_AL_MODBUS;
499  sm->ctx = (void *) modbus;
500 
501  SigMatchAppendSMToList(s, sm, g_modbus_buffer_id);
502 
503  SCReturnInt(0);
504 
505 error:
506  if (modbus != NULL)
507  DetectModbusFree(modbus);
508  if (sm != NULL)
509  SCFree(sm);
510  SCReturnInt(-1);
511 }
512 
513 /**
514  * \brief Registration function for Modbus keyword
515  */
517 {
518  SCEnter();
521  sigmatch_table[DETECT_AL_MODBUS].Setup = DetectModbusSetup;
522  sigmatch_table[DETECT_AL_MODBUS].Free = DetectModbusFree;
524 
526  &unit_id_parse_regex, &unit_id_parse_regex_study);
528  &function_parse_regex, &function_parse_regex_study);
530  &access_parse_regex, &access_parse_regex_study);
531 
535 
536  g_modbus_buffer_id = DetectBufferTypeGetByName("modbus");
537 }
538 
539 #ifdef UNITTESTS /* UNITTESTS */
540 #include "util-unittest.h"
541 
542 /** \test Signature containing a function. */
543 static int DetectModbusTest01(void)
544 {
545  DetectEngineCtx *de_ctx = NULL;
546  DetectModbus *modbus = NULL;
547 
548  de_ctx = DetectEngineCtxInit();
549  FAIL_IF_NULL(de_ctx);
550 
551  de_ctx->flags |= DE_QUIET;
552 
553  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
554  "(msg:\"Testing modbus function\"; "
555  "modbus: function 1; sid:1;)");
556  FAIL_IF_NULL(de_ctx->sig_list);
557  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
558  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
559 
560  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
561 
562  FAIL_IF_NOT(modbus->function == 1);
563 
564  SigGroupCleanup(de_ctx);
565  SigCleanSignatures(de_ctx);
566  DetectEngineCtxFree(de_ctx);
567  PASS;
568 }
569 
570 /** \test Signature containing a function and a subfunction. */
571 static int DetectModbusTest02(void)
572 {
573  DetectEngineCtx *de_ctx = NULL;
574  DetectModbus *modbus = NULL;
575 
576  de_ctx = DetectEngineCtxInit();
577  FAIL_IF_NULL(de_ctx);
578 
579  de_ctx->flags |= DE_QUIET;
580 
581  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
582  "(msg:\"Testing modbus function and subfunction\"; "
583  "modbus: function 8, subfunction 4; sid:1;)");
584  FAIL_IF_NULL(de_ctx->sig_list);
585  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
586  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
587 
588  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
589 
590  FAIL_IF_NOT(modbus->function == 8);
591  FAIL_IF_NOT(*modbus->subfunction == 4);
592 
593  SigGroupCleanup(de_ctx);
594  SigCleanSignatures(de_ctx);
595  DetectEngineCtxFree(de_ctx);
596  PASS;
597 }
598 
599 /** \test Signature containing a function category. */
600 static int DetectModbusTest03(void)
601 {
602  DetectEngineCtx *de_ctx = NULL;
603  DetectModbus *modbus = NULL;
604 
605  de_ctx = DetectEngineCtxInit();
606  FAIL_IF_NULL(de_ctx);
607 
608  de_ctx->flags |= DE_QUIET;
609 
610  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
611  "(msg:\"Testing modbus.function\"; "
612  "modbus: function reserved; sid:1;)");
613  FAIL_IF_NULL(de_ctx->sig_list);
614  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
615  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
616 
617  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
618 
620 
621  SigGroupCleanup(de_ctx);
622  SigCleanSignatures(de_ctx);
623  DetectEngineCtxFree(de_ctx);
624  PASS;
625 }
626 
627 /** \test Signature containing a negative function category. */
628 static int DetectModbusTest04(void)
629 {
630  DetectEngineCtx *de_ctx = NULL;
631  DetectModbus *modbus = NULL;
632 
633  uint8_t category = ~MODBUS_CAT_PUBLIC_ASSIGNED;
634 
635  de_ctx = DetectEngineCtxInit();
636  FAIL_IF_NULL(de_ctx);
637 
638  de_ctx->flags |= DE_QUIET;
639 
640  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
641  "(msg:\"Testing modbus function\"; "
642  "modbus: function !assigned; sid:1;)");
643  FAIL_IF_NULL(de_ctx->sig_list);
644  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
645  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
646 
647  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
648 
649  FAIL_IF_NOT(modbus->category == category);
650 
651  SigGroupCleanup(de_ctx);
652  SigCleanSignatures(de_ctx);
653  DetectEngineCtxFree(de_ctx);
654  PASS;
655 }
656 
657 /** \test Signature containing a access type. */
658 static int DetectModbusTest05(void)
659 {
660  DetectEngineCtx *de_ctx = NULL;
661  DetectModbus *modbus = NULL;
662 
663  de_ctx = DetectEngineCtxInit();
664  FAIL_IF_NULL(de_ctx);
665 
666  de_ctx->flags |= DE_QUIET;
667 
668  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
669  "(msg:\"Testing modbus.access\"; "
670  "modbus: access read; sid:1;)");
671  FAIL_IF_NULL(de_ctx->sig_list);
672  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
673  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
674 
675  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
676 
677  FAIL_IF_NOT(modbus->type == MODBUS_TYP_READ);
678 
679  SigGroupCleanup(de_ctx);
680  SigCleanSignatures(de_ctx);
681  DetectEngineCtxFree(de_ctx);
682  PASS;
683 }
684 
685 /** \test Signature containing a access function. */
686 static int DetectModbusTest06(void)
687 {
688  DetectEngineCtx *de_ctx = NULL;
689  DetectModbus *modbus = NULL;
690 
692 
693  de_ctx = DetectEngineCtxInit();
694  FAIL_IF_NULL(de_ctx);
695 
696  de_ctx->flags |= DE_QUIET;
697 
698  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
699  "(msg:\"Testing modbus.access\"; "
700  "modbus: access read discretes; sid:1;)");
701  FAIL_IF_NULL(de_ctx->sig_list);
702  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
703  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
704 
705  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
706 
707  FAIL_IF_NOT(modbus->type == type);
708 
709  SigGroupCleanup(de_ctx);
710  SigCleanSignatures(de_ctx);
711  DetectEngineCtxFree(de_ctx);
712  PASS;
713 }
714 
715 /** \test Signature containing a read access at an address. */
716 static int DetectModbusTest07(void)
717 {
718  DetectEngineCtx *de_ctx = NULL;
719  DetectModbus *modbus = NULL;
721 
722  uint8_t type = MODBUS_TYP_READ;
723 
724  de_ctx = DetectEngineCtxInit();
725  FAIL_IF_NULL(de_ctx);
726 
727  de_ctx->flags |= DE_QUIET;
728 
729  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
730  "(msg:\"Testing modbus.access\"; "
731  "modbus: access read, address 1000; sid:1;)");
732  FAIL_IF_NULL(de_ctx->sig_list);
733  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
734  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
735 
736  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
737 
738  FAIL_IF_NOT(modbus->type == type);
739  FAIL_IF_NOT((*modbus->address).mode == mode);
740  FAIL_IF_NOT((*modbus->address).min == 1000);
741 
742  SigGroupCleanup(de_ctx);
743  SigCleanSignatures(de_ctx);
744  DetectEngineCtxFree(de_ctx);
745  PASS;
746 }
747 
748 /** \test Signature containing a write access at a range of address. */
749 static int DetectModbusTest08(void)
750 {
751  DetectEngineCtx *de_ctx = NULL;
752  DetectModbus *modbus = NULL;
754 
755  uint8_t type = (MODBUS_TYP_WRITE | MODBUS_TYP_COILS);
756 
757  de_ctx = DetectEngineCtxInit();
758  FAIL_IF_NULL(de_ctx);
759 
760  de_ctx->flags |= DE_QUIET;
761 
762  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
763  "(msg:\"Testing modbus.access\"; "
764  "modbus: access write coils, address >500; sid:1;)");
765  FAIL_IF_NULL(de_ctx->sig_list);
766  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
767  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
768 
769  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
770 
771  FAIL_IF_NOT(modbus->type == type);
772  FAIL_IF_NOT((*modbus->address).mode == mode);
773  FAIL_IF_NOT((*modbus->address).min == 500);
774 
775  SigGroupCleanup(de_ctx);
776  SigCleanSignatures(de_ctx);
777  DetectEngineCtxFree(de_ctx);
778  PASS;
779 }
780 
781 /** \test Signature containing a write access at a address a range of value. */
782 static int DetectModbusTest09(void)
783 {
784  DetectEngineCtx *de_ctx = NULL;
785  DetectModbus *modbus = NULL;
786  DetectModbusMode addressMode = DETECT_MODBUS_EQ;
788 
790 
791  de_ctx = DetectEngineCtxInit();
792  FAIL_IF_NULL(de_ctx);
793 
794  de_ctx->flags |= DE_QUIET;
795 
796  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
797  "(msg:\"Testing modbus.access\"; "
798  "modbus: access write holding, address 100, value 500<>1000; sid:1;)");
799  FAIL_IF_NULL(de_ctx->sig_list);
800  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
801  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
802 
803  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
804 
805  FAIL_IF_NOT(modbus->type == type);
806  FAIL_IF_NOT((*modbus->address).mode == addressMode);
807  FAIL_IF_NOT((*modbus->address).min == 100);
808  FAIL_IF_NOT((*modbus->data).mode == valueMode);
809  FAIL_IF_NOT((*modbus->data).min == 500);
810  FAIL_IF_NOT((*modbus->data).max == 1000);
811 
812  SigGroupCleanup(de_ctx);
813  SigCleanSignatures(de_ctx);
814  DetectEngineCtxFree(de_ctx);
815  PASS;
816 }
817 
818 /** \test Signature containing a unit_id. */
819 static int DetectModbusTest10(void)
820 {
821  DetectEngineCtx *de_ctx = NULL;
822  DetectModbus *modbus = NULL;
824 
825  de_ctx = DetectEngineCtxInit();
826  FAIL_IF_NULL(de_ctx);
827 
828  de_ctx->flags |= DE_QUIET;
829 
830  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
831  "(msg:\"Testing modbus unit_id\"; "
832  "modbus: unit 10; sid:1;)");
833  FAIL_IF_NULL(de_ctx->sig_list);
834  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
835  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
836 
837  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
838 
839  FAIL_IF_NOT((*modbus->unit_id).min == 10);
840  FAIL_IF_NOT((*modbus->unit_id).mode == mode);
841 
842  SigGroupCleanup(de_ctx);
843  SigCleanSignatures(de_ctx);
844  DetectEngineCtxFree(de_ctx);
845  PASS;
846 }
847 
848 /** \test Signature containing a unit_id, a function and a subfunction. */
849 static int DetectModbusTest11(void)
850 {
851  DetectEngineCtx *de_ctx = NULL;
852  DetectModbus *modbus = NULL;
854 
855  de_ctx = DetectEngineCtxInit();
856  FAIL_IF_NULL(de_ctx);
857 
858  de_ctx->flags |= DE_QUIET;
859 
860  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
861  "(msg:\"Testing modbus function and subfunction\"; "
862  "modbus: unit 10, function 8, subfunction 4; sid:1;)");
863  FAIL_IF_NULL(de_ctx->sig_list);
864  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
865  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
866 
867  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
868 
869  FAIL_IF_NOT((*modbus->unit_id).min == 10);
870  FAIL_IF_NOT((*modbus->unit_id).mode == mode);
871  FAIL_IF_NOT(modbus->function == 8);
872  FAIL_IF_NOT((*modbus->subfunction) == 4);
873 
874  SigGroupCleanup(de_ctx);
875  SigCleanSignatures(de_ctx);
876  DetectEngineCtxFree(de_ctx);
877  PASS;
878 }
879 
880 /** \test Signature containing an unit_id and a read access at an address. */
881 static int DetectModbusTest12(void)
882 {
883  DetectEngineCtx *de_ctx = NULL;
884  DetectModbus *modbus = NULL;
886 
887  uint8_t type = MODBUS_TYP_READ;
888 
889  de_ctx = DetectEngineCtxInit();
890  FAIL_IF_NULL(de_ctx);
891 
892  de_ctx->flags |= DE_QUIET;
893 
894  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
895  "(msg:\"Testing modbus.access\"; "
896  "modbus: unit 10, access read, address 1000; sid:1;)");
897  FAIL_IF_NULL(de_ctx->sig_list);
898  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
899  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
900 
901  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
902 
903  FAIL_IF_NOT((*modbus->unit_id).min == 10);
904  FAIL_IF_NOT((*modbus->unit_id).mode == mode);
905  FAIL_IF_NOT(modbus->type == type);
906  FAIL_IF_NOT((*modbus->address).mode == mode);
907  FAIL_IF_NOT((*modbus->address).min == 1000);
908 
909  SigGroupCleanup(de_ctx);
910  SigCleanSignatures(de_ctx);
911  DetectEngineCtxFree(de_ctx);
912  PASS;
913 }
914 
915 /** \test Signature containing a range of unit_id. */
916 static int DetectModbusTest13(void)
917 {
918  DetectEngineCtx *de_ctx = NULL;
919  DetectModbus *modbus = NULL;
921 
922  de_ctx = DetectEngineCtxInit();
923  FAIL_IF_NULL(de_ctx);
924 
925  de_ctx->flags |= DE_QUIET;
926 
927  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
928  "(msg:\"Testing modbus.access\"; "
929  "modbus: unit 10<>500; sid:1;)");
930  FAIL_IF_NULL(de_ctx->sig_list);
931  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
932  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
933 
934  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
935 
936  FAIL_IF_NOT((*modbus->unit_id).min == 10);
937  FAIL_IF_NOT((*modbus->unit_id).max == 500);
938  FAIL_IF_NOT((*modbus->unit_id).mode == mode);
939 
940  SigGroupCleanup(de_ctx);
941  SigCleanSignatures(de_ctx);
942  DetectEngineCtxFree(de_ctx);
943  PASS;
944 }
945 #endif /* UNITTESTS */
946 
947 /**
948  * \brief this function registers unit tests for DetectModbus
949  */
951 {
952 #ifdef UNITTESTS /* UNITTESTS */
953  UtRegisterTest("DetectModbusTest01 - Testing function",
954  DetectModbusTest01);
955  UtRegisterTest("DetectModbusTest02 - Testing function and subfunction",
956  DetectModbusTest02);
957  UtRegisterTest("DetectModbusTest03 - Testing category function",
958  DetectModbusTest03);
959  UtRegisterTest("DetectModbusTest04 - Testing category function in negative",
960  DetectModbusTest04);
961  UtRegisterTest("DetectModbusTest05 - Testing access type",
962  DetectModbusTest05);
963  UtRegisterTest("DetectModbusTest06 - Testing access function",
964  DetectModbusTest06);
965  UtRegisterTest("DetectModbusTest07 - Testing access at address",
966  DetectModbusTest07);
967  UtRegisterTest("DetectModbusTest08 - Testing a range of address",
968  DetectModbusTest08);
969  UtRegisterTest("DetectModbusTest09 - Testing write a range of value",
970  DetectModbusTest09);
971  UtRegisterTest("DetectModbusTest10 - Testing unit_id",
972  DetectModbusTest10);
973  UtRegisterTest("DetectModbusTest11 - Testing unit_id, function and subfunction",
974  DetectModbusTest11);
975  UtRegisterTest("DetectModbusTest12 - Testing unit_id and access at address",
976  DetectModbusTest12);
977  UtRegisterTest("DetectModbusTest13 - Testing a range of unit_id",
978  DetectModbusTest13);
979 #endif /* UNITTESTS */
980 }
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect.h:1403
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1146
#define SCLogDebug(...)
Definition: util-debug.h:335
int DetectSignatureSetAppProto(Signature *s, AppProto alproto)
#define MODBUS_CAT_USER_DEFINED
int DetectEngineInspectModbus(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id)
Do the content inspection & validation for a signature.
#define PASS
Pass the test.
#define unlikely(expr)
Definition: util-optimize.h:35
#define MODBUS_TYP_WRITE
void DetectModbusRegisterTests(void)
this function registers unit tests for DetectModbus
Signature * SigInit(DetectEngineCtx *, const char *)
Parses a signature and adds it to the Detection Engine Context.
Signature * sig_list
Definition: detect.h:726
void SigCleanSignatures(DetectEngineCtx *de_ctx)
#define PARSE_REGEX_ACCESS
Regex for parsing the Modbus access string.
Definition: detect-modbus.c:76
const char * name
Definition: detect.h:1160
Signature container.
Definition: detect.h:492
#define MODBUS_TYP_COILS
main detection engine ctx
Definition: detect.h:720
DetectModbusMode mode
Definition: detect-modbus.h:49
#define DE_QUIET
Definition: detect.h:298
int DetectBufferTypeGetByName(const char *name)
#define str(s)
#define SCCalloc(nm, a)
Definition: util-mem.h:205
#define MAX_SUBSTRINGS
Definition: detect-modbus.c:82
uint16_t type
uint8_t flags
Definition: detect.h:721
#define PARSE_REGEX_FUNCTION
Regex for parsing the Modbus function string.
Definition: detect-modbus.c:69
void(* Free)(void *)
Definition: detect.h:1151
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
#define MODBUS_TYP_DISCRETES
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define MODBUS_CAT_PUBLIC_UNASSIGNED
void DetectSetupParseRegexes(const char *parse_str, pcre **parse_regex, pcre_extra **parse_regex_study)
#define SIG_FLAG_TOSERVER
Definition: detect.h:243
#define SCEnter(...)
Definition: util-debug.h:337
DetectModbusValue * data
Definition: detect-modbus.h:59
int SigGroupCleanup(DetectEngineCtx *de_ctx)
uint8_t type
Definition: detect.h:325
#define SCReturnInt(x)
Definition: util-debug.h:341
uint16_t * subfunction
Definition: detect-modbus.h:55
#define MODBUS_TYP_HOLDING
void SigMatchAppendSMToList(Signature *s, SigMatch *new, int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:282
SigMatchCtx * ctx
Definition: detect.h:327
#define MODBUS_CAT_ALL
#define MODBUS_TYP_INPUT
#define SCFree(a)
Definition: util-mem.h:236
PoolThreadReserved res
uint8_t function
Definition: detect-modbus.h:54
int(* Match)(ThreadVars *, DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition: detect.h:1129
#define SCReturnPtr(x, type)
Definition: util-debug.h:353
void DetectModbusRegister(void)
Registration function for Modbus keyword.
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
DetectModbusValue * unit_id
Definition: detect-modbus.h:57
SigMatch * SigMatchAlloc(void)
Definition: detect-parse.c:226
#define MODBUS_CAT_PUBLIC_ASSIGNED
DetectModbusMode
Definition: detect-modbus.h:39
uint8_t category
Definition: detect-modbus.h:53
#define MODBUS_FUNC_NONE
#define MODBUS_CAT_RESERVED
DetectModbusValue * address
Definition: detect-modbus.h:58
#define PARSE_REGEX_UNIT_ID
Regex for parsing the Modbus unit id string.
Definition: detect-modbus.c:62
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
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:1152
a single match condition for a signature
Definition: detect.h:324
#define FAIL_IF_NOT(expr)
Fail a test if expression to true.
Definition: util-unittest.h:82
DetectEngineCtx * DetectEngineCtxInit(void)
#define MODBUS_TYP_READ