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 #include "util-byte.h"
55 
56 #include "app-layer-modbus.h"
57 
58 #include "stream-tcp.h"
59 
60 /**
61  * \brief Regex for parsing the Modbus unit id string
62  */
63 #define PARSE_REGEX_UNIT_ID "^\\s*\"?\\s*unit\\s+([<>]?\\d+)(<>\\d+)?(,\\s*(.*))?\\s*\"?\\s*$"
64 static DetectParseRegex unit_id_parse_regex;
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 DetectParseRegex function_parse_regex;
71 
72 /**
73  * \brief Regex for parsing the Modbus access string
74  */
75 #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*$"
76 static DetectParseRegex access_parse_regex;
77 
78 static int g_modbus_buffer_id = 0;
79 
80 
81 void DetectModbusRegisterTests(void);
82 
83 /** \internal
84  *
85  * \brief this function will free memory associated with DetectModbus
86  *
87  * \param ptr pointer to DetectModbus
88  */
89 static void DetectModbusFree(DetectEngineCtx *de_ctx, void *ptr) {
90  SCEnter();
91  DetectModbus *modbus = (DetectModbus *) ptr;
92 
93  if(modbus) {
94  if (modbus->unit_id)
95  SCFree(modbus->unit_id);
96 
97  if (modbus->address)
98  SCFree(modbus->address);
99 
100  if (modbus->data)
101  SCFree(modbus->data);
102 
103  SCFree(modbus);
104  }
105 }
106 
107 /** \internal
108  *
109  * \brief This function is used to parse Modbus parameters in access mode
110  *
111  * \param de_ctx Pointer to the detection engine context
112  * \param str Pointer to the user provided id option
113  *
114  * \retval Pointer to DetectModbusData on success or NULL on failure
115  */
116 static DetectModbus *DetectModbusAccessParse(DetectEngineCtx *de_ctx, const char *str)
117 {
118  SCEnter();
119  DetectModbus *modbus = NULL;
120 
121  char arg[MAX_SUBSTRINGS];
122  int ov[MAX_SUBSTRINGS], ret, res;
123 
124  ret = DetectParsePcreExec(&access_parse_regex, str, 0, 0, ov, MAX_SUBSTRINGS);
125  if (ret < 1)
126  goto error;
127 
128  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 1, arg, MAX_SUBSTRINGS);
129  if (res < 0) {
130  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
131  goto error;
132  }
133 
134  /* We have a correct Modbus option */
135  modbus = (DetectModbus *) SCCalloc(1, sizeof(DetectModbus));
136  if (unlikely(modbus == NULL))
137  goto error;
138 
139  if (strcmp(arg, "read") == 0)
140  modbus->type = MODBUS_TYP_READ;
141  else if (strcmp(arg, "write") == 0)
142  modbus->type = MODBUS_TYP_WRITE;
143  else
144  goto error;
145 
146  if (ret > 2) {
147  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 2, arg, MAX_SUBSTRINGS);
148  if (res < 0) {
149  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
150  goto error;
151  }
152 
153  if (*arg != '\0') {
154  if (strcmp(arg, "discretes") == 0) {
155  if (modbus->type == MODBUS_TYP_WRITE)
156  /* Discrete access is only read access. */
157  goto error;
158 
159  modbus->type |= MODBUS_TYP_DISCRETES;
160  }
161  else if (strcmp(arg, "coils") == 0) {
162  modbus->type |= MODBUS_TYP_COILS;
163  }
164  else if (strcmp(arg, "input") == 0) {
165  if (modbus->type == MODBUS_TYP_WRITE) {
166  /* Input access is only read access. */
167  goto error;
168  }
169 
170  modbus->type |= MODBUS_TYP_INPUT;
171  }
172  else if (strcmp(arg, "holding") == 0) {
173  modbus->type |= MODBUS_TYP_HOLDING;
174  }
175  else
176  goto error;
177  }
178 
179  if (ret > 4) {
180  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 4, arg, MAX_SUBSTRINGS);
181  if (res < 0) {
182  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
183  goto error;
184  }
185 
186  /* We have a correct address option */
187  modbus->address = (DetectModbusValue *) SCCalloc(1, sizeof(DetectModbusValue));
188  if (unlikely(modbus->address == NULL))
189  goto error;
190 
191  uint8_t idx;
192  if (arg[0] == '>') {
193  idx = 1;
194  modbus->address->mode = DETECT_MODBUS_GT;
195  } else if (arg[0] == '<') {
196  idx = 1;
197  modbus->address->mode = DETECT_MODBUS_LT;
198  } else {
199  idx = 0;
200  }
201  if (StringParseUint16(&modbus->address->min, 10, 0,
202  (const char *) (arg + idx)) < 0) {
203  SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for min "
204  "address: %s", (const char *)(arg + idx));
205  goto error;
206  }
207  SCLogDebug("and min/equal address %d", modbus->address->min);
208 
209  if (ret > 5) {
210  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 5, arg, MAX_SUBSTRINGS);
211  if (res < 0) {
212  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
213  goto error;
214  }
215 
216  if (*arg != '\0') {
217  if (StringParseUint16(&modbus->address->max, 10, 0,
218  (const char*) (arg + 2)) < 0) {
219  SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for max "
220  "address: %s", (const char*)(arg + 2));
221  goto error;
222  }
223  modbus->address->mode = DETECT_MODBUS_RA;
224  SCLogDebug("and max address %d", modbus->address->max);
225  }
226 
227  if (ret > 7) {
228  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 7, arg, MAX_SUBSTRINGS);
229  if (res < 0) {
230  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
231  goto error;
232  }
233 
234  if (modbus->address->mode != DETECT_MODBUS_EQ) {
235  SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords (address range and value).");
236  goto error;
237  }
238 
239  /* We have a correct address option */
240  if (modbus->type == MODBUS_TYP_READ)
241  /* Value access is only possible in write access. */
242  goto error;
243 
244  modbus->data = (DetectModbusValue *) SCCalloc(1, sizeof(DetectModbusValue));
245  if (unlikely(modbus->data == NULL))
246  goto error;
247 
248 
249  uint8_t idx_mode;
250  if (arg[0] == '>') {
251  idx_mode = 1;
252  modbus->data->mode = DETECT_MODBUS_GT;
253  } else if (arg[0] == '<') {
254  idx_mode = 1;
255  modbus->data->mode = DETECT_MODBUS_LT;
256  } else {
257  idx_mode = 0;
258  }
259  if (StringParseUint16(&modbus->data->min, 10, 0,
260  (const char*) (arg + idx_mode)) < 0) {
261  SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for "
262  "min data: %s", (const char*)(arg + idx_mode));
263  goto error;
264  }
265  SCLogDebug("and min/equal value %d", modbus->data->min);
266 
267  if (ret > 8) {
268  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 8, arg, MAX_SUBSTRINGS);
269  if (res < 0) {
270  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
271  goto error;
272  }
273 
274  if (*arg != '\0') {
275  if (StringParseUint16(&modbus->data->max,
276  10, 0, (const char*) (arg + 2)) < 0) {
278  "Invalid value for max data: %s",
279  (const char*)(arg + 2));
280  goto error;
281  }
282  modbus->data->mode = DETECT_MODBUS_RA;
283  SCLogDebug("and max value %d", modbus->data->max);
284  }
285  }
286  }
287  }
288  }
289  }
290 
291  SCReturnPtr(modbus, "DetectModbusAccess");
292 
293 error:
294  if (modbus != NULL)
295  DetectModbusFree(de_ctx, modbus);
296 
297  SCReturnPtr(NULL, "DetectModbus");
298 }
299 
300 /** \internal
301  *
302  * \brief This function is used to parse Modbus parameters in function mode
303  *
304  * \param str Pointer to the user provided id option
305  *
306  * \retval id_d pointer to DetectModbusData on success
307  * \retval NULL on failure
308  */
309 static DetectModbus *DetectModbusFunctionParse(DetectEngineCtx *de_ctx, const char *str)
310 {
311  SCEnter();
312  DetectModbus *modbus = NULL;
313 
314  char arg[MAX_SUBSTRINGS], *ptr = arg;
315  int ov[MAX_SUBSTRINGS], res, ret;
316 
317  ret = DetectParsePcreExec(&function_parse_regex, str, 0, 0, ov, MAX_SUBSTRINGS);
318  if (ret < 1)
319  goto error;
320 
321  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 1, arg, MAX_SUBSTRINGS);
322  if (res < 0) {
323  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
324  goto error;
325  }
326 
327  /* We have a correct Modbus function option */
328  modbus = (DetectModbus *) SCCalloc(1, sizeof(DetectModbus));
329  if (unlikely(modbus == NULL))
330  goto error;
331 
332  if (isdigit((unsigned char)ptr[0])) {
333  if (StringParseUint8(&modbus->function, 10, 0, (const char *)ptr) < 0) {
334  SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for "
335  "modbus function: %s", (const char *)ptr);
336  goto error;
337  }
338  /* Function code 0 is managed by decoder_event INVALID_FUNCTION_CODE */
339  if (modbus->function == MODBUS_FUNC_NONE) {
341  "Invalid argument \"%d\" supplied to modbus function keyword.",
342  modbus->function);
343  goto error;
344  }
345 
346  SCLogDebug("will look for modbus function %d", modbus->function);
347 
348  if (ret > 2) {
349  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 3, arg, MAX_SUBSTRINGS);
350  if (res < 0) {
351  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
352  goto error;
353  }
354 
355  if (StringParseUint16(&modbus->subfunction, 10, 0, (const char *)arg) < 0) {
356  SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for "
357  "modbus subfunction: %s", (const char*)arg);
358  goto error;
359  }
360  modbus->has_subfunction = true;
361 
362  SCLogDebug("and subfunction %d", modbus->subfunction);
363  }
364  } else {
365  uint8_t neg = 0;
366 
367  if (ptr[0] == '!') {
368  neg = 1;
369  ptr++;
370  }
371 
372  if (strcmp("assigned", ptr) == 0)
374  else if (strcmp("unassigned", ptr) == 0)
376  else if (strcmp("public", ptr) == 0)
378  else if (strcmp("user", ptr) == 0)
380  else if (strcmp("reserved", ptr) == 0)
381  modbus->category = MODBUS_CAT_RESERVED;
382  else if (strcmp("all", ptr) == 0)
383  modbus->category = MODBUS_CAT_ALL;
384 
385  if (neg)
386  modbus->category = ~modbus->category;
387  SCLogDebug("will look for modbus category function %d", modbus->category);
388  }
389 
390  SCReturnPtr(modbus, "DetectModbusFunction");
391 
392 error:
393  if (modbus != NULL)
394  DetectModbusFree(de_ctx, modbus);
395 
396  SCReturnPtr(NULL, "DetectModbus");
397 }
398 
399 /** \internal
400  *
401  * \brief This function is used to parse Modbus parameters in unit id mode
402  *
403  * \param str Pointer to the user provided id option
404  *
405  * \retval Pointer to DetectModbusUnit on success or NULL on failure
406  */
407 static DetectModbus *DetectModbusUnitIdParse(DetectEngineCtx *de_ctx, const char *str)
408 {
409  SCEnter();
410  DetectModbus *modbus = NULL;
411 
412  char arg[MAX_SUBSTRINGS];
413  int ov[MAX_SUBSTRINGS], ret, res;
414 
415  ret = DetectParsePcreExec(&unit_id_parse_regex, str, 0, 0, ov, MAX_SUBSTRINGS);
416  if (ret < 1)
417  goto error;
418 
419  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 1, arg, MAX_SUBSTRINGS);
420  if (res < 0) {
421  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
422  goto error;
423  }
424 
425  if (ret > 3) {
426  /* We have more Modbus option */
427  const char *str_ptr;
428 
429  res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 4, &str_ptr);
430  if (res < 0) {
431  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
432  goto error;
433  }
434 
435  if ((modbus = DetectModbusFunctionParse(de_ctx, str_ptr)) == NULL) {
436  if ((modbus = DetectModbusAccessParse(de_ctx, str_ptr)) == NULL) {
437  SCLogError(SC_ERR_PCRE_MATCH, "invalid modbus option");
438  goto error;
439  }
440  }
441  } else {
442  /* We have only unit id Modbus option */
443  modbus = (DetectModbus *) SCCalloc(1, sizeof(DetectModbus));
444  if (unlikely(modbus == NULL))
445  goto error;
446  }
447 
448  /* We have a correct unit id option */
449  modbus->unit_id = (DetectModbusValue *) SCCalloc(1, sizeof(DetectModbusValue));
450  if (unlikely(modbus->unit_id == NULL))
451  goto error;
452 
453  uint8_t idx;
454  if (arg[0] == '>') {
455  idx = 1;
456  modbus->unit_id->mode = DETECT_MODBUS_GT;
457  } else if (arg[0] == '<') {
458  idx = 1;
459  modbus->unit_id->mode = DETECT_MODBUS_LT;
460  } else {
461  idx = 0;
462  }
463  if (StringParseUint16(&modbus->unit_id->min, 10, 0, (const char *) (arg + idx)) < 0) {
464  SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for "
465  "modbus min unit id: %s", (const char*)(arg + idx));
466  goto error;
467  }
468  SCLogDebug("and min/equal unit id %d", modbus->unit_id->min);
469 
470  if (ret > 2) {
471  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 2, arg, MAX_SUBSTRINGS);
472  if (res < 0) {
473  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
474  goto error;
475  }
476 
477  if (*arg != '\0') {
478  if (StringParseUint16(&modbus->unit_id->max, 10, 0, (const char *) (arg + 2)) < 0) {
479  SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for "
480  "modbus max unit id: %s", (const char*)(arg + 2));
481  goto error;
482  }
483  modbus->unit_id->mode = DETECT_MODBUS_RA;
484  SCLogDebug("and max unit id %d", modbus->unit_id->max);
485  }
486  }
487 
488  SCReturnPtr(modbus, "DetectModbusUnitId");
489 
490 error:
491  if (modbus != NULL)
492  DetectModbusFree(de_ctx, modbus);
493 
494  SCReturnPtr(NULL, "DetectModbus");
495 }
496 
497 
498 /** \internal
499  *
500  * \brief this function is used to add the parsed "id" option into the current signature
501  *
502  * \param de_ctx Pointer to the Detection Engine Context
503  * \param s Pointer to the Current Signature
504  * \param str Pointer to the user provided "id" option
505  *
506  * \retval 0 on Success or -1 on Failure
507  */
508 static int DetectModbusSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
509 {
510  SCEnter();
511  DetectModbus *modbus = NULL;
512  SigMatch *sm = NULL;
513 
515  return -1;
516 
517  if ((modbus = DetectModbusUnitIdParse(de_ctx, str)) == NULL) {
518  if ((modbus = DetectModbusFunctionParse(de_ctx, str)) == NULL) {
519  if ((modbus = DetectModbusAccessParse(de_ctx, str)) == NULL) {
520  SCLogError(SC_ERR_PCRE_MATCH, "invalid modbus option");
521  goto error;
522  }
523  }
524  }
525 
526  /* Okay so far so good, lets get this into a SigMatch and put it in the Signature. */
527  sm = SigMatchAlloc();
528  if (sm == NULL)
529  goto error;
530 
531  sm->type = DETECT_AL_MODBUS;
532  sm->ctx = (void *) modbus;
533 
534  SigMatchAppendSMToList(s, sm, g_modbus_buffer_id);
535 
536  SCReturnInt(0);
537 
538 error:
539  if (modbus != NULL)
540  DetectModbusFree(de_ctx, modbus);
541  if (sm != NULL)
542  SCFree(sm);
543  SCReturnInt(-1);
544 }
545 
546 /**
547  * \brief Registration function for Modbus keyword
548  */
550 {
551  SCEnter();
553  sigmatch_table[DETECT_AL_MODBUS].desc = "match on various properties of Modbus requests";
554  sigmatch_table[DETECT_AL_MODBUS].url = "/rules/modbus-keyword.html#modbus-keyword";
556  sigmatch_table[DETECT_AL_MODBUS].Setup = DetectModbusSetup;
557  sigmatch_table[DETECT_AL_MODBUS].Free = DetectModbusFree;
559 
560  DetectSetupParseRegexes(PARSE_REGEX_UNIT_ID, &unit_id_parse_regex);
561  DetectSetupParseRegexes(PARSE_REGEX_FUNCTION, &function_parse_regex);
562  DetectSetupParseRegexes(PARSE_REGEX_ACCESS, &access_parse_regex);
563 
567 
568  g_modbus_buffer_id = DetectBufferTypeGetByName("modbus");
569 }
570 
571 #ifdef UNITTESTS /* UNITTESTS */
572 #include "util-unittest.h"
573 
574 /** \test Signature containing a function. */
575 static int DetectModbusTest01(void)
576 {
577  DetectEngineCtx *de_ctx = NULL;
578  DetectModbus *modbus = NULL;
579 
582 
583  de_ctx->flags |= DE_QUIET;
584 
585  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
586  "(msg:\"Testing modbus function\"; "
587  "modbus: function 1; sid:1;)");
589  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
590  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
591 
592  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
593 
594  FAIL_IF_NOT(modbus->function == 1);
595 
599  PASS;
600 }
601 
602 /** \test Signature containing a function and a subfunction. */
603 static int DetectModbusTest02(void)
604 {
605  DetectEngineCtx *de_ctx = NULL;
606  DetectModbus *modbus = NULL;
607 
610 
611  de_ctx->flags |= DE_QUIET;
612 
613  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
614  "(msg:\"Testing modbus function and subfunction\"; "
615  "modbus: function 8, subfunction 4; sid:1;)");
617  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
618  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
619 
620  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
621 
622  FAIL_IF_NOT(modbus->function == 8);
623  FAIL_IF_NOT(modbus->subfunction == 4);
624 
628  PASS;
629 }
630 
631 /** \test Signature containing a function category. */
632 static int DetectModbusTest03(void)
633 {
634  DetectEngineCtx *de_ctx = NULL;
635  DetectModbus *modbus = NULL;
636 
639 
640  de_ctx->flags |= DE_QUIET;
641 
642  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
643  "(msg:\"Testing modbus.function\"; "
644  "modbus: function reserved; sid:1;)");
646  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
647  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
648 
649  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
650 
652 
656  PASS;
657 }
658 
659 /** \test Signature containing a negative function category. */
660 static int DetectModbusTest04(void)
661 {
662  DetectEngineCtx *de_ctx = NULL;
663  DetectModbus *modbus = NULL;
664 
665  uint8_t category = ~MODBUS_CAT_PUBLIC_ASSIGNED;
666 
669 
670  de_ctx->flags |= DE_QUIET;
671 
672  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
673  "(msg:\"Testing modbus function\"; "
674  "modbus: function !assigned; sid:1;)");
676  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
677  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
678 
679  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
680 
681  FAIL_IF_NOT(modbus->category == category);
682 
686  PASS;
687 }
688 
689 /** \test Signature containing a access type. */
690 static int DetectModbusTest05(void)
691 {
692  DetectEngineCtx *de_ctx = NULL;
693  DetectModbus *modbus = NULL;
694 
697 
698  de_ctx->flags |= DE_QUIET;
699 
700  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
701  "(msg:\"Testing modbus.access\"; "
702  "modbus: access read; sid:1;)");
704  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
705  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
706 
707  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
708 
709  FAIL_IF_NOT(modbus->type == MODBUS_TYP_READ);
710 
714  PASS;
715 }
716 
717 /** \test Signature containing a access function. */
718 static int DetectModbusTest06(void)
719 {
720  DetectEngineCtx *de_ctx = NULL;
721  DetectModbus *modbus = NULL;
722 
724 
727 
728  de_ctx->flags |= DE_QUIET;
729 
730  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
731  "(msg:\"Testing modbus.access\"; "
732  "modbus: access read discretes; sid:1;)");
734  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
735  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
736 
737  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
738 
739  FAIL_IF_NOT(modbus->type == type);
740 
744  PASS;
745 }
746 
747 /** \test Signature containing a read access at an address. */
748 static int DetectModbusTest07(void)
749 {
750  DetectEngineCtx *de_ctx = NULL;
751  DetectModbus *modbus = NULL;
753 
754  uint8_t type = MODBUS_TYP_READ;
755 
758 
759  de_ctx->flags |= DE_QUIET;
760 
761  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
762  "(msg:\"Testing modbus.access\"; "
763  "modbus: access read, address 1000; sid:1;)");
765  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
766  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
767 
768  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
769 
770  FAIL_IF_NOT(modbus->type == type);
771  FAIL_IF_NOT((*modbus->address).mode == mode);
772  FAIL_IF_NOT((*modbus->address).min == 1000);
773 
777  PASS;
778 }
779 
780 /** \test Signature containing a write access at a range of address. */
781 static int DetectModbusTest08(void)
782 {
783  DetectEngineCtx *de_ctx = NULL;
784  DetectModbus *modbus = NULL;
786 
787  uint8_t type = (MODBUS_TYP_WRITE | MODBUS_TYP_COILS);
788 
791 
792  de_ctx->flags |= DE_QUIET;
793 
794  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
795  "(msg:\"Testing modbus.access\"; "
796  "modbus: access write coils, address >500; sid:1;)");
798  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
799  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
800 
801  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
802 
803  FAIL_IF_NOT(modbus->type == type);
804  FAIL_IF_NOT((*modbus->address).mode == mode);
805  FAIL_IF_NOT((*modbus->address).min == 500);
806 
810  PASS;
811 }
812 
813 /** \test Signature containing a write access at a address a range of value. */
814 static int DetectModbusTest09(void)
815 {
816  DetectEngineCtx *de_ctx = NULL;
817  DetectModbus *modbus = NULL;
818  DetectModbusMode addressMode = DETECT_MODBUS_EQ;
820 
822 
825 
826  de_ctx->flags |= DE_QUIET;
827 
828  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
829  "(msg:\"Testing modbus.access\"; "
830  "modbus: access write holding, address 100, value 500<>1000; sid:1;)");
832  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
833  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
834 
835  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
836 
837  FAIL_IF_NOT(modbus->type == type);
838  FAIL_IF_NOT((*modbus->address).mode == addressMode);
839  FAIL_IF_NOT((*modbus->address).min == 100);
840  FAIL_IF_NOT((*modbus->data).mode == valueMode);
841  FAIL_IF_NOT((*modbus->data).min == 500);
842  FAIL_IF_NOT((*modbus->data).max == 1000);
843 
847  PASS;
848 }
849 
850 /** \test Signature containing a unit_id. */
851 static int DetectModbusTest10(void)
852 {
853  DetectEngineCtx *de_ctx = NULL;
854  DetectModbus *modbus = NULL;
856 
859 
860  de_ctx->flags |= DE_QUIET;
861 
862  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
863  "(msg:\"Testing modbus unit_id\"; "
864  "modbus: unit 10; sid:1;)");
866  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
867  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
868 
869  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
870 
871  FAIL_IF_NOT((*modbus->unit_id).min == 10);
872  FAIL_IF_NOT((*modbus->unit_id).mode == mode);
873 
877  PASS;
878 }
879 
880 /** \test Signature containing a unit_id, a function and a subfunction. */
881 static int DetectModbusTest11(void)
882 {
883  DetectEngineCtx *de_ctx = NULL;
884  DetectModbus *modbus = NULL;
886 
889 
890  de_ctx->flags |= DE_QUIET;
891 
892  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
893  "(msg:\"Testing modbus function and subfunction\"; "
894  "modbus: unit 10, function 8, subfunction 4; sid:1;)");
896  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
897  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
898 
899  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
900 
901  FAIL_IF_NOT((*modbus->unit_id).min == 10);
902  FAIL_IF_NOT((*modbus->unit_id).mode == mode);
903  FAIL_IF_NOT(modbus->function == 8);
904  FAIL_IF_NOT(modbus->subfunction == 4);
905 
909  PASS;
910 }
911 
912 /** \test Signature containing an unit_id and a read access at an address. */
913 static int DetectModbusTest12(void)
914 {
915  DetectEngineCtx *de_ctx = NULL;
916  DetectModbus *modbus = NULL;
918 
919  uint8_t type = MODBUS_TYP_READ;
920 
923 
924  de_ctx->flags |= DE_QUIET;
925 
926  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
927  "(msg:\"Testing modbus.access\"; "
928  "modbus: unit 10, access read, address 1000; sid:1;)");
930  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
931  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
932 
933  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
934 
935  FAIL_IF_NOT((*modbus->unit_id).min == 10);
936  FAIL_IF_NOT((*modbus->unit_id).mode == mode);
937  FAIL_IF_NOT(modbus->type == type);
938  FAIL_IF_NOT((*modbus->address).mode == mode);
939  FAIL_IF_NOT((*modbus->address).min == 1000);
940 
944  PASS;
945 }
946 
947 /** \test Signature containing a range of unit_id. */
948 static int DetectModbusTest13(void)
949 {
950  DetectEngineCtx *de_ctx = NULL;
951  DetectModbus *modbus = NULL;
953 
956 
957  de_ctx->flags |= DE_QUIET;
958 
959  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
960  "(msg:\"Testing modbus.access\"; "
961  "modbus: unit 10<>500; sid:1;)");
963  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
964  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
965 
966  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
967 
968  FAIL_IF_NOT((*modbus->unit_id).min == 10);
969  FAIL_IF_NOT((*modbus->unit_id).max == 500);
970  FAIL_IF_NOT((*modbus->unit_id).mode == mode);
971 
975  PASS;
976 }
977 #endif /* UNITTESTS */
978 
979 /**
980  * \brief this function registers unit tests for DetectModbus
981  */
983 {
984 #ifdef UNITTESTS /* UNITTESTS */
985  UtRegisterTest("DetectModbusTest01 - Testing function",
986  DetectModbusTest01);
987  UtRegisterTest("DetectModbusTest02 - Testing function and subfunction",
988  DetectModbusTest02);
989  UtRegisterTest("DetectModbusTest03 - Testing category function",
990  DetectModbusTest03);
991  UtRegisterTest("DetectModbusTest04 - Testing category function in negative",
992  DetectModbusTest04);
993  UtRegisterTest("DetectModbusTest05 - Testing access type",
994  DetectModbusTest05);
995  UtRegisterTest("DetectModbusTest06 - Testing access function",
996  DetectModbusTest06);
997  UtRegisterTest("DetectModbusTest07 - Testing access at address",
998  DetectModbusTest07);
999  UtRegisterTest("DetectModbusTest08 - Testing a range of address",
1000  DetectModbusTest08);
1001  UtRegisterTest("DetectModbusTest09 - Testing write a range of value",
1002  DetectModbusTest09);
1003  UtRegisterTest("DetectModbusTest10 - Testing unit_id",
1004  DetectModbusTest10);
1005  UtRegisterTest("DetectModbusTest11 - Testing unit_id, function and subfunction",
1006  DetectModbusTest11);
1007  UtRegisterTest("DetectModbusTest12 - Testing unit_id and access at address",
1008  DetectModbusTest12);
1009  UtRegisterTest("DetectModbusTest13 - Testing a range of unit_id",
1010  DetectModbusTest13);
1011 #endif /* UNITTESTS */
1012 }
DetectModbus_::subfunction
uint16_t subfunction
Definition: detect-modbus.h:55
util-byte.h
DetectModbus_::address
DetectModbusValue * address
Definition: detect-modbus.h:59
SigTableElmt_::url
const char * url
Definition: detect.h:1212
DetectSignatureSetAppProto
int DetectSignatureSetAppProto(Signature *s, AppProto alproto)
Definition: detect-parse.c:1468
detect-engine.h
StringParseUint16
int StringParseUint16(uint16_t *res, int base, uint16_t len, const char *str)
Definition: util-byte.c:336
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
SigTableElmt_::desc
const char * desc
Definition: detect.h:1211
PARSE_REGEX_ACCESS
#define PARSE_REGEX_ACCESS
Regex for parsing the Modbus access string.
Definition: detect-modbus.c:75
SC_ERR_INVALID_VALUE
@ SC_ERR_INVALID_VALUE
Definition: util-error.h:160
SigTableElmt_::Free
void(* Free)(DetectEngineCtx *, void *)
Definition: detect.h:1200
SigTableElmt_::name
const char * name
Definition: detect.h:1209
MODBUS_CAT_PUBLIC_UNASSIGNED
#define MODBUS_CAT_PUBLIC_UNASSIGNED
Definition: app-layer-modbus.h:58
stream-tcp.h
detect-engine-modbus.h
MODBUS_TYP_HOLDING
#define MODBUS_TYP_HOLDING
Definition: app-layer-modbus.h:74
DetectModbusRegisterTests
void DetectModbusRegisterTests(void)
this function registers unit tests for DetectModbus
Definition: detect-modbus.c:982
MAX_SUBSTRINGS
#define MAX_SUBSTRINGS
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
DetectModbus_
Definition: detect-modbus.h:52
DetectModbusValue_::mode
DetectModbusMode mode
Definition: detect-modbus.h:49
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:103
DetectModbus_::has_subfunction
bool has_subfunction
Definition: detect-modbus.h:56
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:298
ALPROTO_MODBUS
@ ALPROTO_MODBUS
Definition: app-layer-protos.h:42
SigInit
Signature * SigInit(DetectEngineCtx *, const char *)
Parses a signature and adds it to the Detection Engine Context.
Definition: detect-parse.c:2033
MODBUS_TYP_READ
#define MODBUS_TYP_READ
Definition: app-layer-modbus.h:66
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:766
SC_ERR_INVALID_SIGNATURE
@ SC_ERR_INVALID_SIGNATURE
Definition: util-error.h:69
DetectEngineCtxFree
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Definition: detect-engine.c:2089
DE_QUIET
#define DE_QUIET
Definition: detect.h:293
DETECT_MODBUS_EQ
@ DETECT_MODBUS_EQ
Definition: detect-modbus.h:40
SC_ERR_PCRE_GET_SUBSTRING
@ SC_ERR_PCRE_GET_SUBSTRING
Definition: util-error.h:34
SigCleanSignatures
void SigCleanSignatures(DetectEngineCtx *de_ctx)
Definition: detect-engine-build.c:39
SigTableElmt_::Setup
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1195
util-unittest.h
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression to true.
Definition: util-unittest.h:82
DetectModbusValue_::max
uint16_t max
Definition: detect-modbus.h:48
DetectBufferTypeGetByName
int DetectBufferTypeGetByName(const char *name)
Definition: detect-engine.c:876
MODBUS_CAT_USER_DEFINED
#define MODBUS_CAT_USER_DEFINED
Definition: app-layer-modbus.h:59
SIG_FLAG_TOSERVER
#define SIG_FLAG_TOSERVER
Definition: detect.h:236
MODBUS_CAT_ALL
#define MODBUS_CAT_ALL
Definition: app-layer-modbus.h:61
DetectModbus_::category
uint8_t category
Definition: detect-modbus.h:53
util-debug.h
SC_ERR_PCRE_MATCH
@ SC_ERR_PCRE_MATCH
Definition: util-error.h:32
type
uint8_t type
Definition: decode-icmpv4.h:0
DetectEngineInspectModbus
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.
Definition: detect-engine-modbus.c:199
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:17
MODBUS_CAT_RESERVED
#define MODBUS_CAT_RESERVED
Definition: app-layer-modbus.h:60
DetectModbusValue_
Definition: detect-modbus.h:46
res
PoolThreadReserved res
Definition: stream-tcp-private.h:0
DetectSetupParseRegexes
void DetectSetupParseRegexes(const char *parse_str, DetectParseRegex *detect_parse)
Definition: detect-parse.c:2470
SCEnter
#define SCEnter(...)
Definition: util-debug.h:300
detect.h
MODBUS_TYP_DISCRETES
#define MODBUS_TYP_DISCRETES
Definition: app-layer-modbus.h:70
PARSE_REGEX_FUNCTION
#define PARSE_REGEX_FUNCTION
Regex for parsing the Modbus function string.
Definition: detect-modbus.c:69
SigMatch_::ctx
SigMatchCtx * ctx
Definition: detect.h:322
SigGroupCleanup
int SigGroupCleanup(DetectEngineCtx *de_ctx)
Definition: detect-engine-build.c:1949
DetectParsePcreExec
int DetectParsePcreExec(DetectParseRegex *parse_regex, const char *str, int start_offset, int options, int *ovector, int ovector_size)
Definition: detect-parse.c:2400
detect-modbus.h
SCReturnPtr
#define SCReturnPtr(x, type)
Definition: util-debug.h:316
SigTableElmt_::Match
int(* Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition: detect.h:1178
DetectModbus_::type
uint8_t type
Definition: detect-modbus.h:57
DetectModbus_::function
uint8_t function
Definition: detect-modbus.h:54
DETECT_MODBUS_LT
@ DETECT_MODBUS_LT
Definition: detect-modbus.h:41
SigMatchAlloc
SigMatch * SigMatchAlloc(void)
Definition: detect-parse.c:235
SigMatch_::type
uint8_t type
Definition: detect.h:320
MODBUS_TYP_INPUT
#define MODBUS_TYP_INPUT
Definition: app-layer-modbus.h:73
app-layer-modbus.h
suricata-common.h
MODBUS_CAT_PUBLIC_ASSIGNED
#define MODBUS_CAT_PUBLIC_ASSIGNED
Definition: app-layer-modbus.h:57
sigmatch_table
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect-parse.c:73
DetectParseRegex_
Definition: detect-parse.h:42
SCLogError
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:257
DetectEngineCtx_::sig_list
Signature * sig_list
Definition: detect.h:772
DetectModbusMode
DetectModbusMode
Definition: detect-modbus.h:39
DETECT_AL_MODBUS
@ DETECT_AL_MODBUS
Definition: detect-engine-register.h:215
MODBUS_FUNC_NONE
#define MODBUS_FUNC_NONE
Definition: app-layer-modbus.h:82
str
#define str(s)
Definition: suricata-common.h:273
SCFree
#define SCFree(p)
Definition: util-mem.h:61
detect-parse.h
Signature_
Signature container.
Definition: detect.h:527
SigMatch_
a single match condition for a signature
Definition: detect.h:319
DetectModbusValue_::min
uint16_t min
Definition: detect-modbus.h:47
StringParseUint8
int StringParseUint8(uint8_t *res, int base, uint16_t len, const char *str)
Definition: util-byte.c:359
DetectEngineCtxInit
DetectEngineCtx * DetectEngineCtxInit(void)
Definition: detect-engine.c:2044
DetectModbus_::data
DetectModbusValue * data
Definition: detect-modbus.h:60
DETECT_MODBUS_GT
@ DETECT_MODBUS_GT
Definition: detect-modbus.h:42
DetectModbusRegister
void DetectModbusRegister(void)
Registration function for Modbus keyword.
Definition: detect-modbus.c:549
DetectEngineCtx_::flags
uint8_t flags
Definition: detect.h:767
MODBUS_TYP_COILS
#define MODBUS_TYP_COILS
Definition: app-layer-modbus.h:71
DetectAppLayerInspectEngineRegister
void DetectAppLayerInspectEngineRegister(const char *name, AppProto alproto, uint32_t dir, int progress, InspectEngineFuncPtr Callback)
register inspect engine at start up time
Definition: detect-engine.c:170
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:304
MODBUS_TYP_WRITE
#define MODBUS_TYP_WRITE
Definition: app-layer-modbus.h:67
SigMatchAppendSMToList
void SigMatchAppendSMToList(Signature *s, SigMatch *new, int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:349
SigTableElmt_::RegisterTests
void(* RegisterTests)(void)
Definition: detect.h:1201
PARSE_REGEX_UNIT_ID
#define PARSE_REGEX_UNIT_ID
Regex for parsing the Modbus unit id string.
Definition: detect-modbus.c:63
DETECT_MODBUS_RA
@ DETECT_MODBUS_RA
Definition: detect-modbus.h:43
DetectModbus_::unit_id
DetectModbusValue * unit_id
Definition: detect-modbus.h:58
SC_ERR_CONFLICTING_RULE_KEYWORDS
@ SC_ERR_CONFLICTING_RULE_KEYWORDS
Definition: util-error.h:171