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 #ifdef UNITTESTS
81 void DetectModbusRegisterTests(void);
82 #endif
83 
84 /** \internal
85  *
86  * \brief this function will free memory associated with DetectModbus
87  *
88  * \param ptr pointer to DetectModbus
89  */
90 static void DetectModbusFree(DetectEngineCtx *de_ctx, void *ptr) {
91  SCEnter();
92  DetectModbus *modbus = (DetectModbus *) ptr;
93 
94  if(modbus) {
95  if (modbus->unit_id)
96  SCFree(modbus->unit_id);
97 
98  if (modbus->address)
99  SCFree(modbus->address);
100 
101  if (modbus->data)
102  SCFree(modbus->data);
103 
104  SCFree(modbus);
105  }
106 }
107 
108 /** \internal
109  *
110  * \brief This function is used to parse Modbus parameters in access mode
111  *
112  * \param de_ctx Pointer to the detection engine context
113  * \param str Pointer to the user provided id option
114  *
115  * \retval Pointer to DetectModbusData on success or NULL on failure
116  */
117 static DetectModbus *DetectModbusAccessParse(DetectEngineCtx *de_ctx, const char *str)
118 {
119  SCEnter();
120  DetectModbus *modbus = NULL;
121 
122  char arg[MAX_SUBSTRINGS];
123  int ov[MAX_SUBSTRINGS], ret, res;
124 
125  ret = DetectParsePcreExec(&access_parse_regex, str, 0, 0, ov, MAX_SUBSTRINGS);
126  if (ret < 1)
127  goto error;
128 
129  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 1, arg, MAX_SUBSTRINGS);
130  if (res < 0) {
131  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
132  goto error;
133  }
134 
135  /* We have a correct Modbus option */
136  modbus = (DetectModbus *) SCCalloc(1, sizeof(DetectModbus));
137  if (unlikely(modbus == NULL))
138  goto error;
139 
140  if (strcmp(arg, "read") == 0)
141  modbus->type = MODBUS_TYP_READ;
142  else if (strcmp(arg, "write") == 0)
143  modbus->type = MODBUS_TYP_WRITE;
144  else
145  goto error;
146 
147  if (ret > 2) {
148  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 2, arg, MAX_SUBSTRINGS);
149  if (res < 0) {
150  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
151  goto error;
152  }
153 
154  if (*arg != '\0') {
155  if (strcmp(arg, "discretes") == 0) {
156  if (modbus->type == MODBUS_TYP_WRITE)
157  /* Discrete access is only read access. */
158  goto error;
159 
160  modbus->type |= MODBUS_TYP_DISCRETES;
161  }
162  else if (strcmp(arg, "coils") == 0) {
163  modbus->type |= MODBUS_TYP_COILS;
164  }
165  else if (strcmp(arg, "input") == 0) {
166  if (modbus->type == MODBUS_TYP_WRITE) {
167  /* Input access is only read access. */
168  goto error;
169  }
170 
171  modbus->type |= MODBUS_TYP_INPUT;
172  }
173  else if (strcmp(arg, "holding") == 0) {
174  modbus->type |= MODBUS_TYP_HOLDING;
175  }
176  else
177  goto error;
178  }
179 
180  if (ret > 4) {
181  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 4, arg, MAX_SUBSTRINGS);
182  if (res < 0) {
183  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
184  goto error;
185  }
186 
187  /* We have a correct address option */
188  modbus->address = (DetectModbusValue *) SCCalloc(1, sizeof(DetectModbusValue));
189  if (unlikely(modbus->address == NULL))
190  goto error;
191 
192  uint8_t idx;
193  if (arg[0] == '>') {
194  idx = 1;
195  modbus->address->mode = DETECT_MODBUS_GT;
196  } else if (arg[0] == '<') {
197  idx = 1;
198  modbus->address->mode = DETECT_MODBUS_LT;
199  } else {
200  idx = 0;
201  }
202  if (StringParseUint16(&modbus->address->min, 10, 0,
203  (const char *) (arg + idx)) < 0) {
204  SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for min "
205  "address: %s", (const char *)(arg + idx));
206  goto error;
207  }
208  SCLogDebug("and min/equal address %d", modbus->address->min);
209 
210  if (ret > 5) {
211  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 5, arg, MAX_SUBSTRINGS);
212  if (res < 0) {
213  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
214  goto error;
215  }
216 
217  if (*arg != '\0') {
218  if (StringParseUint16(&modbus->address->max, 10, 0,
219  (const char*) (arg + 2)) < 0) {
220  SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for max "
221  "address: %s", (const char*)(arg + 2));
222  goto error;
223  }
224  modbus->address->mode = DETECT_MODBUS_RA;
225  SCLogDebug("and max address %d", modbus->address->max);
226  }
227 
228  if (ret > 7) {
229  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 7, arg, MAX_SUBSTRINGS);
230  if (res < 0) {
231  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
232  goto error;
233  }
234 
235  if (modbus->address->mode != DETECT_MODBUS_EQ) {
236  SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords (address range and value).");
237  goto error;
238  }
239 
240  /* We have a correct address option */
241  if (modbus->type == MODBUS_TYP_READ)
242  /* Value access is only possible in write access. */
243  goto error;
244 
245  modbus->data = (DetectModbusValue *) SCCalloc(1, sizeof(DetectModbusValue));
246  if (unlikely(modbus->data == NULL))
247  goto error;
248 
249 
250  uint8_t idx_mode;
251  if (arg[0] == '>') {
252  idx_mode = 1;
253  modbus->data->mode = DETECT_MODBUS_GT;
254  } else if (arg[0] == '<') {
255  idx_mode = 1;
256  modbus->data->mode = DETECT_MODBUS_LT;
257  } else {
258  idx_mode = 0;
259  }
260  if (StringParseUint16(&modbus->data->min, 10, 0,
261  (const char*) (arg + idx_mode)) < 0) {
262  SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for "
263  "min data: %s", (const char*)(arg + idx_mode));
264  goto error;
265  }
266  SCLogDebug("and min/equal value %d", modbus->data->min);
267 
268  if (ret > 8) {
269  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 8, arg, MAX_SUBSTRINGS);
270  if (res < 0) {
271  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
272  goto error;
273  }
274 
275  if (*arg != '\0') {
276  if (StringParseUint16(&modbus->data->max,
277  10, 0, (const char*) (arg + 2)) < 0) {
279  "Invalid value for max data: %s",
280  (const char*)(arg + 2));
281  goto error;
282  }
283  modbus->data->mode = DETECT_MODBUS_RA;
284  SCLogDebug("and max value %d", modbus->data->max);
285  }
286  }
287  }
288  }
289  }
290  }
291 
292  SCReturnPtr(modbus, "DetectModbusAccess");
293 
294 error:
295  if (modbus != NULL)
296  DetectModbusFree(de_ctx, modbus);
297 
298  SCReturnPtr(NULL, "DetectModbus");
299 }
300 
301 /** \internal
302  *
303  * \brief This function is used to parse Modbus parameters in function mode
304  *
305  * \param str Pointer to the user provided id option
306  *
307  * \retval id_d pointer to DetectModbusData on success
308  * \retval NULL on failure
309  */
310 static DetectModbus *DetectModbusFunctionParse(DetectEngineCtx *de_ctx, const char *str)
311 {
312  SCEnter();
313  DetectModbus *modbus = NULL;
314 
315  char arg[MAX_SUBSTRINGS], *ptr = arg;
316  int ov[MAX_SUBSTRINGS], res, ret;
317 
318  ret = DetectParsePcreExec(&function_parse_regex, str, 0, 0, ov, MAX_SUBSTRINGS);
319  if (ret < 1)
320  goto error;
321 
322  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 1, arg, MAX_SUBSTRINGS);
323  if (res < 0) {
324  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
325  goto error;
326  }
327 
328  /* We have a correct Modbus function option */
329  modbus = (DetectModbus *) SCCalloc(1, sizeof(DetectModbus));
330  if (unlikely(modbus == NULL))
331  goto error;
332 
333  if (isdigit((unsigned char)ptr[0])) {
334  if (StringParseUint8(&modbus->function, 10, 0, (const char *)ptr) < 0) {
335  SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for "
336  "modbus function: %s", (const char *)ptr);
337  goto error;
338  }
339  /* Function code 0 is managed by decoder_event INVALID_FUNCTION_CODE */
340  if (modbus->function == MODBUS_FUNC_NONE) {
342  "Invalid argument \"%d\" supplied to modbus function keyword.",
343  modbus->function);
344  goto error;
345  }
346 
347  SCLogDebug("will look for modbus function %d", modbus->function);
348 
349  if (ret > 2) {
350  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 3, arg, MAX_SUBSTRINGS);
351  if (res < 0) {
352  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
353  goto error;
354  }
355 
356  if (StringParseUint16(&modbus->subfunction, 10, 0, (const char *)arg) < 0) {
357  SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for "
358  "modbus subfunction: %s", (const char*)arg);
359  goto error;
360  }
361  modbus->has_subfunction = true;
362 
363  SCLogDebug("and subfunction %d", modbus->subfunction);
364  }
365  } else {
366  uint8_t neg = 0;
367 
368  if (ptr[0] == '!') {
369  neg = 1;
370  ptr++;
371  }
372 
373  if (strcmp("assigned", ptr) == 0)
375  else if (strcmp("unassigned", ptr) == 0)
377  else if (strcmp("public", ptr) == 0)
379  else if (strcmp("user", ptr) == 0)
381  else if (strcmp("reserved", ptr) == 0)
382  modbus->category = MODBUS_CAT_RESERVED;
383  else if (strcmp("all", ptr) == 0)
384  modbus->category = MODBUS_CAT_ALL;
385 
386  if (neg)
387  modbus->category = ~modbus->category;
388  SCLogDebug("will look for modbus category function %d", modbus->category);
389  }
390 
391  SCReturnPtr(modbus, "DetectModbusFunction");
392 
393 error:
394  if (modbus != NULL)
395  DetectModbusFree(de_ctx, modbus);
396 
397  SCReturnPtr(NULL, "DetectModbus");
398 }
399 
400 /** \internal
401  *
402  * \brief This function is used to parse Modbus parameters in unit id mode
403  *
404  * \param str Pointer to the user provided id option
405  *
406  * \retval Pointer to DetectModbusUnit on success or NULL on failure
407  */
408 static DetectModbus *DetectModbusUnitIdParse(DetectEngineCtx *de_ctx, const char *str)
409 {
410  SCEnter();
411  DetectModbus *modbus = NULL;
412 
413  char arg[MAX_SUBSTRINGS];
414  int ov[MAX_SUBSTRINGS], ret, res;
415 
416  ret = DetectParsePcreExec(&unit_id_parse_regex, str, 0, 0, ov, MAX_SUBSTRINGS);
417  if (ret < 1)
418  goto error;
419 
420  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 1, arg, MAX_SUBSTRINGS);
421  if (res < 0) {
422  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
423  goto error;
424  }
425 
426  if (ret > 3) {
427  /* We have more Modbus option */
428  const char *str_ptr;
429 
430  res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 4, &str_ptr);
431  if (res < 0) {
432  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
433  goto error;
434  }
435 
436  if ((modbus = DetectModbusFunctionParse(de_ctx, str_ptr)) == NULL) {
437  if ((modbus = DetectModbusAccessParse(de_ctx, str_ptr)) == NULL) {
438  SCLogError(SC_ERR_PCRE_MATCH, "invalid modbus option");
439  goto error;
440  }
441  }
442  } else {
443  /* We have only unit id Modbus option */
444  modbus = (DetectModbus *) SCCalloc(1, sizeof(DetectModbus));
445  if (unlikely(modbus == NULL))
446  goto error;
447  }
448 
449  /* We have a correct unit id option */
450  modbus->unit_id = (DetectModbusValue *) SCCalloc(1, sizeof(DetectModbusValue));
451  if (unlikely(modbus->unit_id == NULL))
452  goto error;
453 
454  uint8_t idx;
455  if (arg[0] == '>') {
456  idx = 1;
457  modbus->unit_id->mode = DETECT_MODBUS_GT;
458  } else if (arg[0] == '<') {
459  idx = 1;
460  modbus->unit_id->mode = DETECT_MODBUS_LT;
461  } else {
462  idx = 0;
463  }
464  if (StringParseUint16(&modbus->unit_id->min, 10, 0, (const char *) (arg + idx)) < 0) {
465  SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for "
466  "modbus min unit id: %s", (const char*)(arg + idx));
467  goto error;
468  }
469  SCLogDebug("and min/equal unit id %d", modbus->unit_id->min);
470 
471  if (ret > 2) {
472  res = pcre_copy_substring(str, ov, MAX_SUBSTRINGS, 2, arg, MAX_SUBSTRINGS);
473  if (res < 0) {
474  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
475  goto error;
476  }
477 
478  if (*arg != '\0') {
479  if (StringParseUint16(&modbus->unit_id->max, 10, 0, (const char *) (arg + 2)) < 0) {
480  SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for "
481  "modbus max unit id: %s", (const char*)(arg + 2));
482  goto error;
483  }
484  modbus->unit_id->mode = DETECT_MODBUS_RA;
485  SCLogDebug("and max unit id %d", modbus->unit_id->max);
486  }
487  }
488 
489  SCReturnPtr(modbus, "DetectModbusUnitId");
490 
491 error:
492  if (modbus != NULL)
493  DetectModbusFree(de_ctx, modbus);
494 
495  SCReturnPtr(NULL, "DetectModbus");
496 }
497 
498 
499 /** \internal
500  *
501  * \brief this function is used to add the parsed "id" option into the current signature
502  *
503  * \param de_ctx Pointer to the Detection Engine Context
504  * \param s Pointer to the Current Signature
505  * \param str Pointer to the user provided "id" option
506  *
507  * \retval 0 on Success or -1 on Failure
508  */
509 static int DetectModbusSetup(DetectEngineCtx *de_ctx, Signature *s, const char *str)
510 {
511  SCEnter();
512  DetectModbus *modbus = NULL;
513  SigMatch *sm = NULL;
514 
516  return -1;
517 
518  if ((modbus = DetectModbusUnitIdParse(de_ctx, str)) == NULL) {
519  if ((modbus = DetectModbusFunctionParse(de_ctx, str)) == NULL) {
520  if ((modbus = DetectModbusAccessParse(de_ctx, str)) == NULL) {
521  SCLogError(SC_ERR_PCRE_MATCH, "invalid modbus option");
522  goto error;
523  }
524  }
525  }
526 
527  /* Okay so far so good, lets get this into a SigMatch and put it in the Signature. */
528  sm = SigMatchAlloc();
529  if (sm == NULL)
530  goto error;
531 
532  sm->type = DETECT_AL_MODBUS;
533  sm->ctx = (void *) modbus;
534 
535  SigMatchAppendSMToList(s, sm, g_modbus_buffer_id);
536 
537  SCReturnInt(0);
538 
539 error:
540  if (modbus != NULL)
541  DetectModbusFree(de_ctx, modbus);
542  if (sm != NULL)
543  SCFree(sm);
544  SCReturnInt(-1);
545 }
546 
547 /**
548  * \brief Registration function for Modbus keyword
549  */
551 {
552  SCEnter();
554  sigmatch_table[DETECT_AL_MODBUS].desc = "match on various properties of Modbus requests";
555  sigmatch_table[DETECT_AL_MODBUS].url = "/rules/modbus-keyword.html#modbus-keyword";
557  sigmatch_table[DETECT_AL_MODBUS].Setup = DetectModbusSetup;
558  sigmatch_table[DETECT_AL_MODBUS].Free = DetectModbusFree;
559 #ifdef UNITTESTS
561 #endif
562  DetectSetupParseRegexes(PARSE_REGEX_UNIT_ID, &unit_id_parse_regex);
563  DetectSetupParseRegexes(PARSE_REGEX_FUNCTION, &function_parse_regex);
564  DetectSetupParseRegexes(PARSE_REGEX_ACCESS, &access_parse_regex);
565 
569 
570  g_modbus_buffer_id = DetectBufferTypeGetByName("modbus");
571 }
572 
573 #ifdef UNITTESTS /* UNITTESTS */
574 #include "util-unittest.h"
575 
576 /** \test Signature containing a function. */
577 static int DetectModbusTest01(void)
578 {
579  DetectEngineCtx *de_ctx = NULL;
580  DetectModbus *modbus = NULL;
581 
584 
585  de_ctx->flags |= DE_QUIET;
586 
587  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
588  "(msg:\"Testing modbus function\"; "
589  "modbus: function 1; sid:1;)");
591  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
592  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
593 
594  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
595 
596  FAIL_IF_NOT(modbus->function == 1);
597 
601  PASS;
602 }
603 
604 /** \test Signature containing a function and a subfunction. */
605 static int DetectModbusTest02(void)
606 {
607  DetectEngineCtx *de_ctx = NULL;
608  DetectModbus *modbus = NULL;
609 
612 
613  de_ctx->flags |= DE_QUIET;
614 
615  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
616  "(msg:\"Testing modbus function and subfunction\"; "
617  "modbus: function 8, subfunction 4; sid:1;)");
619  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
620  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
621 
622  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
623 
624  FAIL_IF_NOT(modbus->function == 8);
625  FAIL_IF_NOT(modbus->subfunction == 4);
626 
630  PASS;
631 }
632 
633 /** \test Signature containing a function category. */
634 static int DetectModbusTest03(void)
635 {
636  DetectEngineCtx *de_ctx = NULL;
637  DetectModbus *modbus = NULL;
638 
641 
642  de_ctx->flags |= DE_QUIET;
643 
644  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
645  "(msg:\"Testing modbus.function\"; "
646  "modbus: function reserved; sid:1;)");
648  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
649  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
650 
651  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
652 
654 
658  PASS;
659 }
660 
661 /** \test Signature containing a negative function category. */
662 static int DetectModbusTest04(void)
663 {
664  DetectEngineCtx *de_ctx = NULL;
665  DetectModbus *modbus = NULL;
666 
667  uint8_t category = ~MODBUS_CAT_PUBLIC_ASSIGNED;
668 
671 
672  de_ctx->flags |= DE_QUIET;
673 
674  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
675  "(msg:\"Testing modbus function\"; "
676  "modbus: function !assigned; sid:1;)");
678  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
679  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
680 
681  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
682 
683  FAIL_IF_NOT(modbus->category == category);
684 
688  PASS;
689 }
690 
691 /** \test Signature containing a access type. */
692 static int DetectModbusTest05(void)
693 {
694  DetectEngineCtx *de_ctx = NULL;
695  DetectModbus *modbus = NULL;
696 
699 
700  de_ctx->flags |= DE_QUIET;
701 
702  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
703  "(msg:\"Testing modbus.access\"; "
704  "modbus: access read; sid:1;)");
706  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
707  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
708 
709  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
710 
711  FAIL_IF_NOT(modbus->type == MODBUS_TYP_READ);
712 
716  PASS;
717 }
718 
719 /** \test Signature containing a access function. */
720 static int DetectModbusTest06(void)
721 {
722  DetectEngineCtx *de_ctx = NULL;
723  DetectModbus *modbus = NULL;
724 
726 
729 
730  de_ctx->flags |= DE_QUIET;
731 
732  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
733  "(msg:\"Testing modbus.access\"; "
734  "modbus: access read discretes; sid:1;)");
736  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
737  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
738 
739  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
740 
741  FAIL_IF_NOT(modbus->type == type);
742 
746  PASS;
747 }
748 
749 /** \test Signature containing a read access at an address. */
750 static int DetectModbusTest07(void)
751 {
752  DetectEngineCtx *de_ctx = NULL;
753  DetectModbus *modbus = NULL;
755 
756  uint8_t type = MODBUS_TYP_READ;
757 
760 
761  de_ctx->flags |= DE_QUIET;
762 
763  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
764  "(msg:\"Testing modbus.access\"; "
765  "modbus: access read, address 1000; sid:1;)");
767  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
768  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
769 
770  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
771 
772  FAIL_IF_NOT(modbus->type == type);
773  FAIL_IF_NOT((*modbus->address).mode == mode);
774  FAIL_IF_NOT((*modbus->address).min == 1000);
775 
779  PASS;
780 }
781 
782 /** \test Signature containing a write access at a range of address. */
783 static int DetectModbusTest08(void)
784 {
785  DetectEngineCtx *de_ctx = NULL;
786  DetectModbus *modbus = NULL;
788 
789  uint8_t type = (MODBUS_TYP_WRITE | MODBUS_TYP_COILS);
790 
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 coils, address >500; sid:1;)");
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 == mode);
807  FAIL_IF_NOT((*modbus->address).min == 500);
808 
812  PASS;
813 }
814 
815 /** \test Signature containing a write access at a address a range of value. */
816 static int DetectModbusTest09(void)
817 {
818  DetectEngineCtx *de_ctx = NULL;
819  DetectModbus *modbus = NULL;
820  DetectModbusMode addressMode = DETECT_MODBUS_EQ;
822 
824 
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.access\"; "
832  "modbus: access write holding, address 100, value 500<>1000; sid:1;)");
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->type == type);
840  FAIL_IF_NOT((*modbus->address).mode == addressMode);
841  FAIL_IF_NOT((*modbus->address).min == 100);
842  FAIL_IF_NOT((*modbus->data).mode == valueMode);
843  FAIL_IF_NOT((*modbus->data).min == 500);
844  FAIL_IF_NOT((*modbus->data).max == 1000);
845 
849  PASS;
850 }
851 
852 /** \test Signature containing a unit_id. */
853 static int DetectModbusTest10(void)
854 {
855  DetectEngineCtx *de_ctx = NULL;
856  DetectModbus *modbus = NULL;
858 
861 
862  de_ctx->flags |= DE_QUIET;
863 
864  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
865  "(msg:\"Testing modbus unit_id\"; "
866  "modbus: unit 10; sid:1;)");
868  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
869  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
870 
871  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
872 
873  FAIL_IF_NOT((*modbus->unit_id).min == 10);
874  FAIL_IF_NOT((*modbus->unit_id).mode == mode);
875 
879  PASS;
880 }
881 
882 /** \test Signature containing a unit_id, a function and a subfunction. */
883 static int DetectModbusTest11(void)
884 {
885  DetectEngineCtx *de_ctx = NULL;
886  DetectModbus *modbus = NULL;
888 
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 function and subfunction\"; "
896  "modbus: unit 10, function 8, subfunction 4; sid:1;)");
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->function == 8);
906  FAIL_IF_NOT(modbus->subfunction == 4);
907 
911  PASS;
912 }
913 
914 /** \test Signature containing an unit_id and a read access at an address. */
915 static int DetectModbusTest12(void)
916 {
917  DetectEngineCtx *de_ctx = NULL;
918  DetectModbus *modbus = NULL;
920 
921  uint8_t type = MODBUS_TYP_READ;
922 
925 
926  de_ctx->flags |= DE_QUIET;
927 
928  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
929  "(msg:\"Testing modbus.access\"; "
930  "modbus: unit 10, access read, address 1000; sid:1;)");
932  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
933  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
934 
935  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
936 
937  FAIL_IF_NOT((*modbus->unit_id).min == 10);
938  FAIL_IF_NOT((*modbus->unit_id).mode == mode);
939  FAIL_IF_NOT(modbus->type == type);
940  FAIL_IF_NOT((*modbus->address).mode == mode);
941  FAIL_IF_NOT((*modbus->address).min == 1000);
942 
946  PASS;
947 }
948 
949 /** \test Signature containing a range of unit_id. */
950 static int DetectModbusTest13(void)
951 {
952  DetectEngineCtx *de_ctx = NULL;
953  DetectModbus *modbus = NULL;
955 
958 
959  de_ctx->flags |= DE_QUIET;
960 
961  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
962  "(msg:\"Testing modbus.access\"; "
963  "modbus: unit 10<>500; sid:1;)");
965  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
966  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
967 
968  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
969 
970  FAIL_IF_NOT((*modbus->unit_id).min == 10);
971  FAIL_IF_NOT((*modbus->unit_id).max == 500);
972  FAIL_IF_NOT((*modbus->unit_id).mode == mode);
973 
977  PASS;
978 }
979 
980 /**
981  * \brief this function registers unit tests for DetectModbus
982  */
984 {
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 }
1012 #endif /* UNITTESTS */
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:1215
DetectSignatureSetAppProto
int DetectSignatureSetAppProto(Signature *s, AppProto alproto)
Definition: detect-parse.c:1480
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:1214
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:1202
SigTableElmt_::name
const char * name
Definition: detect.h:1212
MODBUS_CAT_PUBLIC_UNASSIGNED
#define MODBUS_CAT_PUBLIC_UNASSIGNED
Definition: app-layer-modbus.h:59
stream-tcp.h
detect-engine-modbus.h
MODBUS_TYP_HOLDING
#define MODBUS_TYP_HOLDING
Definition: app-layer-modbus.h:75
DetectModbusRegisterTests
void DetectModbusRegisterTests(void)
this function registers unit tests for DetectModbus
Definition: detect-modbus.c:983
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:2056
MODBUS_TYP_READ
#define MODBUS_TYP_READ
Definition: app-layer-modbus.h:67
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:767
SC_ERR_INVALID_SIGNATURE
@ SC_ERR_INVALID_SIGNATURE
Definition: util-error.h:69
DetectEngineCtxFree
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Definition: detect-engine.c:2093
DE_QUIET
#define DE_QUIET
Definition: detect.h:294
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:1197
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:880
MODBUS_CAT_USER_DEFINED
#define MODBUS_CAT_USER_DEFINED
Definition: app-layer-modbus.h:60
SIG_FLAG_TOSERVER
#define SIG_FLAG_TOSERVER
Definition: detect.h:236
MODBUS_CAT_ALL
#define MODBUS_CAT_ALL
Definition: app-layer-modbus.h:62
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:61
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:2493
SCEnter
#define SCEnter(...)
Definition: util-debug.h:300
detect.h
MODBUS_TYP_DISCRETES
#define MODBUS_TYP_DISCRETES
Definition: app-layer-modbus.h:71
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:323
SigGroupCleanup
int SigGroupCleanup(DetectEngineCtx *de_ctx)
Definition: detect-engine-build.c:1953
DetectParsePcreExec
int DetectParsePcreExec(DetectParseRegex *parse_regex, const char *str, int start_offset, int options, int *ovector, int ovector_size)
Definition: detect-parse.c:2423
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:1180
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:321
MODBUS_TYP_INPUT
#define MODBUS_TYP_INPUT
Definition: app-layer-modbus.h:74
app-layer-modbus.h
suricata-common.h
MODBUS_CAT_PUBLIC_ASSIGNED
#define MODBUS_CAT_PUBLIC_ASSIGNED
Definition: app-layer-modbus.h:58
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:773
DetectModbusMode
DetectModbusMode
Definition: detect-modbus.h:39
DETECT_AL_MODBUS
@ DETECT_AL_MODBUS
Definition: detect-engine-register.h:228
MODBUS_FUNC_NONE
#define MODBUS_FUNC_NONE
Definition: app-layer-modbus.h:83
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:528
SigMatch_
a single match condition for a signature
Definition: detect.h:320
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:2048
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:550
DetectEngineCtx_::flags
uint8_t flags
Definition: detect.h:768
MODBUS_TYP_COILS
#define MODBUS_TYP_COILS
Definition: app-layer-modbus.h:72
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:171
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:68
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:1204
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