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();
520  sigmatch_table[DETECT_AL_MODBUS].desc = "match on various properties of Modbus requests";
521  sigmatch_table[DETECT_AL_MODBUS].url = DOC_URL DOC_VERSION "/rules/modbus-keyword.html#modbus-keyword";
523  sigmatch_table[DETECT_AL_MODBUS].Setup = DetectModbusSetup;
524  sigmatch_table[DETECT_AL_MODBUS].Free = DetectModbusFree;
526 
528  &unit_id_parse_regex, &unit_id_parse_regex_study);
530  &function_parse_regex, &function_parse_regex_study);
532  &access_parse_regex, &access_parse_regex_study);
533 
537 
538  g_modbus_buffer_id = DetectBufferTypeGetByName("modbus");
539 }
540 
541 #ifdef UNITTESTS /* UNITTESTS */
542 #include "util-unittest.h"
543 
544 /** \test Signature containing a function. */
545 static int DetectModbusTest01(void)
546 {
547  DetectEngineCtx *de_ctx = NULL;
548  DetectModbus *modbus = NULL;
549 
550  de_ctx = DetectEngineCtxInit();
551  FAIL_IF_NULL(de_ctx);
552 
553  de_ctx->flags |= DE_QUIET;
554 
555  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
556  "(msg:\"Testing modbus function\"; "
557  "modbus: function 1; sid:1;)");
558  FAIL_IF_NULL(de_ctx->sig_list);
559  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
560  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
561 
562  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
563 
564  FAIL_IF_NOT(modbus->function == 1);
565 
566  SigGroupCleanup(de_ctx);
567  SigCleanSignatures(de_ctx);
568  DetectEngineCtxFree(de_ctx);
569  PASS;
570 }
571 
572 /** \test Signature containing a function and a subfunction. */
573 static int DetectModbusTest02(void)
574 {
575  DetectEngineCtx *de_ctx = NULL;
576  DetectModbus *modbus = NULL;
577 
578  de_ctx = DetectEngineCtxInit();
579  FAIL_IF_NULL(de_ctx);
580 
581  de_ctx->flags |= DE_QUIET;
582 
583  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
584  "(msg:\"Testing modbus function and subfunction\"; "
585  "modbus: function 8, subfunction 4; sid:1;)");
586  FAIL_IF_NULL(de_ctx->sig_list);
587  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
588  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
589 
590  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
591 
592  FAIL_IF_NOT(modbus->function == 8);
593  FAIL_IF_NOT(*modbus->subfunction == 4);
594 
595  SigGroupCleanup(de_ctx);
596  SigCleanSignatures(de_ctx);
597  DetectEngineCtxFree(de_ctx);
598  PASS;
599 }
600 
601 /** \test Signature containing a function category. */
602 static int DetectModbusTest03(void)
603 {
604  DetectEngineCtx *de_ctx = NULL;
605  DetectModbus *modbus = NULL;
606 
607  de_ctx = DetectEngineCtxInit();
608  FAIL_IF_NULL(de_ctx);
609 
610  de_ctx->flags |= DE_QUIET;
611 
612  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
613  "(msg:\"Testing modbus.function\"; "
614  "modbus: function reserved; sid:1;)");
615  FAIL_IF_NULL(de_ctx->sig_list);
616  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
617  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
618 
619  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
620 
622 
623  SigGroupCleanup(de_ctx);
624  SigCleanSignatures(de_ctx);
625  DetectEngineCtxFree(de_ctx);
626  PASS;
627 }
628 
629 /** \test Signature containing a negative function category. */
630 static int DetectModbusTest04(void)
631 {
632  DetectEngineCtx *de_ctx = NULL;
633  DetectModbus *modbus = NULL;
634 
635  uint8_t category = ~MODBUS_CAT_PUBLIC_ASSIGNED;
636 
637  de_ctx = DetectEngineCtxInit();
638  FAIL_IF_NULL(de_ctx);
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 !assigned; sid:1;)");
645  FAIL_IF_NULL(de_ctx->sig_list);
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 
651  FAIL_IF_NOT(modbus->category == category);
652 
653  SigGroupCleanup(de_ctx);
654  SigCleanSignatures(de_ctx);
655  DetectEngineCtxFree(de_ctx);
656  PASS;
657 }
658 
659 /** \test Signature containing a access type. */
660 static int DetectModbusTest05(void)
661 {
662  DetectEngineCtx *de_ctx = NULL;
663  DetectModbus *modbus = NULL;
664 
665  de_ctx = DetectEngineCtxInit();
666  FAIL_IF_NULL(de_ctx);
667 
668  de_ctx->flags |= DE_QUIET;
669 
670  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
671  "(msg:\"Testing modbus.access\"; "
672  "modbus: access read; sid:1;)");
673  FAIL_IF_NULL(de_ctx->sig_list);
674  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
675  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
676 
677  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
678 
679  FAIL_IF_NOT(modbus->type == MODBUS_TYP_READ);
680 
681  SigGroupCleanup(de_ctx);
682  SigCleanSignatures(de_ctx);
683  DetectEngineCtxFree(de_ctx);
684  PASS;
685 }
686 
687 /** \test Signature containing a access function. */
688 static int DetectModbusTest06(void)
689 {
690  DetectEngineCtx *de_ctx = NULL;
691  DetectModbus *modbus = NULL;
692 
694 
695  de_ctx = DetectEngineCtxInit();
696  FAIL_IF_NULL(de_ctx);
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 discretes; sid:1;)");
703  FAIL_IF_NULL(de_ctx->sig_list);
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 == type);
710 
711  SigGroupCleanup(de_ctx);
712  SigCleanSignatures(de_ctx);
713  DetectEngineCtxFree(de_ctx);
714  PASS;
715 }
716 
717 /** \test Signature containing a read access at an address. */
718 static int DetectModbusTest07(void)
719 {
720  DetectEngineCtx *de_ctx = NULL;
721  DetectModbus *modbus = NULL;
723 
724  uint8_t type = MODBUS_TYP_READ;
725 
726  de_ctx = DetectEngineCtxInit();
727  FAIL_IF_NULL(de_ctx);
728 
729  de_ctx->flags |= DE_QUIET;
730 
731  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
732  "(msg:\"Testing modbus.access\"; "
733  "modbus: access read, address 1000; sid:1;)");
734  FAIL_IF_NULL(de_ctx->sig_list);
735  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
736  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
737 
738  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
739 
740  FAIL_IF_NOT(modbus->type == type);
741  FAIL_IF_NOT((*modbus->address).mode == mode);
742  FAIL_IF_NOT((*modbus->address).min == 1000);
743 
744  SigGroupCleanup(de_ctx);
745  SigCleanSignatures(de_ctx);
746  DetectEngineCtxFree(de_ctx);
747  PASS;
748 }
749 
750 /** \test Signature containing a write access at a range of address. */
751 static int DetectModbusTest08(void)
752 {
753  DetectEngineCtx *de_ctx = NULL;
754  DetectModbus *modbus = NULL;
756 
757  uint8_t type = (MODBUS_TYP_WRITE | MODBUS_TYP_COILS);
758 
759  de_ctx = DetectEngineCtxInit();
760  FAIL_IF_NULL(de_ctx);
761 
762  de_ctx->flags |= DE_QUIET;
763 
764  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
765  "(msg:\"Testing modbus.access\"; "
766  "modbus: access write coils, address >500; sid:1;)");
767  FAIL_IF_NULL(de_ctx->sig_list);
768  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
769  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
770 
771  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
772 
773  FAIL_IF_NOT(modbus->type == type);
774  FAIL_IF_NOT((*modbus->address).mode == mode);
775  FAIL_IF_NOT((*modbus->address).min == 500);
776 
777  SigGroupCleanup(de_ctx);
778  SigCleanSignatures(de_ctx);
779  DetectEngineCtxFree(de_ctx);
780  PASS;
781 }
782 
783 /** \test Signature containing a write access at a address a range of value. */
784 static int DetectModbusTest09(void)
785 {
786  DetectEngineCtx *de_ctx = NULL;
787  DetectModbus *modbus = NULL;
788  DetectModbusMode addressMode = DETECT_MODBUS_EQ;
790 
792 
793  de_ctx = DetectEngineCtxInit();
794  FAIL_IF_NULL(de_ctx);
795 
796  de_ctx->flags |= DE_QUIET;
797 
798  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
799  "(msg:\"Testing modbus.access\"; "
800  "modbus: access write holding, address 100, value 500<>1000; sid:1;)");
801  FAIL_IF_NULL(de_ctx->sig_list);
802  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
803  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
804 
805  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
806 
807  FAIL_IF_NOT(modbus->type == type);
808  FAIL_IF_NOT((*modbus->address).mode == addressMode);
809  FAIL_IF_NOT((*modbus->address).min == 100);
810  FAIL_IF_NOT((*modbus->data).mode == valueMode);
811  FAIL_IF_NOT((*modbus->data).min == 500);
812  FAIL_IF_NOT((*modbus->data).max == 1000);
813 
814  SigGroupCleanup(de_ctx);
815  SigCleanSignatures(de_ctx);
816  DetectEngineCtxFree(de_ctx);
817  PASS;
818 }
819 
820 /** \test Signature containing a unit_id. */
821 static int DetectModbusTest10(void)
822 {
823  DetectEngineCtx *de_ctx = NULL;
824  DetectModbus *modbus = NULL;
826 
827  de_ctx = DetectEngineCtxInit();
828  FAIL_IF_NULL(de_ctx);
829 
830  de_ctx->flags |= DE_QUIET;
831 
832  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
833  "(msg:\"Testing modbus unit_id\"; "
834  "modbus: unit 10; sid:1;)");
835  FAIL_IF_NULL(de_ctx->sig_list);
836  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
837  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
838 
839  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
840 
841  FAIL_IF_NOT((*modbus->unit_id).min == 10);
842  FAIL_IF_NOT((*modbus->unit_id).mode == mode);
843 
844  SigGroupCleanup(de_ctx);
845  SigCleanSignatures(de_ctx);
846  DetectEngineCtxFree(de_ctx);
847  PASS;
848 }
849 
850 /** \test Signature containing a unit_id, a function and a subfunction. */
851 static int DetectModbusTest11(void)
852 {
853  DetectEngineCtx *de_ctx = NULL;
854  DetectModbus *modbus = NULL;
856 
857  de_ctx = DetectEngineCtxInit();
858  FAIL_IF_NULL(de_ctx);
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 function and subfunction\"; "
864  "modbus: unit 10, function 8, subfunction 4; sid:1;)");
865  FAIL_IF_NULL(de_ctx->sig_list);
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  FAIL_IF_NOT(modbus->function == 8);
874  FAIL_IF_NOT((*modbus->subfunction) == 4);
875 
876  SigGroupCleanup(de_ctx);
877  SigCleanSignatures(de_ctx);
878  DetectEngineCtxFree(de_ctx);
879  PASS;
880 }
881 
882 /** \test Signature containing an unit_id and a read access at an address. */
883 static int DetectModbusTest12(void)
884 {
885  DetectEngineCtx *de_ctx = NULL;
886  DetectModbus *modbus = NULL;
888 
889  uint8_t type = MODBUS_TYP_READ;
890 
891  de_ctx = DetectEngineCtxInit();
892  FAIL_IF_NULL(de_ctx);
893 
894  de_ctx->flags |= DE_QUIET;
895 
896  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
897  "(msg:\"Testing modbus.access\"; "
898  "modbus: unit 10, access read, address 1000; sid:1;)");
899  FAIL_IF_NULL(de_ctx->sig_list);
900  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
901  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
902 
903  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
904 
905  FAIL_IF_NOT((*modbus->unit_id).min == 10);
906  FAIL_IF_NOT((*modbus->unit_id).mode == mode);
907  FAIL_IF_NOT(modbus->type == type);
908  FAIL_IF_NOT((*modbus->address).mode == mode);
909  FAIL_IF_NOT((*modbus->address).min == 1000);
910 
911  SigGroupCleanup(de_ctx);
912  SigCleanSignatures(de_ctx);
913  DetectEngineCtxFree(de_ctx);
914  PASS;
915 }
916 
917 /** \test Signature containing a range of unit_id. */
918 static int DetectModbusTest13(void)
919 {
920  DetectEngineCtx *de_ctx = NULL;
921  DetectModbus *modbus = NULL;
923 
924  de_ctx = DetectEngineCtxInit();
925  FAIL_IF_NULL(de_ctx);
926 
927  de_ctx->flags |= DE_QUIET;
928 
929  de_ctx->sig_list = SigInit(de_ctx, "alert modbus any any -> any any "
930  "(msg:\"Testing modbus.access\"; "
931  "modbus: unit 10<>500; sid:1;)");
932  FAIL_IF_NULL(de_ctx->sig_list);
933  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]);
934  FAIL_IF_NULL(de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx);
935 
936  modbus = (DetectModbus *) de_ctx->sig_list->sm_lists_tail[g_modbus_buffer_id]->ctx;
937 
938  FAIL_IF_NOT((*modbus->unit_id).min == 10);
939  FAIL_IF_NOT((*modbus->unit_id).max == 500);
940  FAIL_IF_NOT((*modbus->unit_id).mode == mode);
941 
942  SigGroupCleanup(de_ctx);
943  SigCleanSignatures(de_ctx);
944  DetectEngineCtxFree(de_ctx);
945  PASS;
946 }
947 #endif /* UNITTESTS */
948 
949 /**
950  * \brief this function registers unit tests for DetectModbus
951  */
953 {
954 #ifdef UNITTESTS /* UNITTESTS */
955  UtRegisterTest("DetectModbusTest01 - Testing function",
956  DetectModbusTest01);
957  UtRegisterTest("DetectModbusTest02 - Testing function and subfunction",
958  DetectModbusTest02);
959  UtRegisterTest("DetectModbusTest03 - Testing category function",
960  DetectModbusTest03);
961  UtRegisterTest("DetectModbusTest04 - Testing category function in negative",
962  DetectModbusTest04);
963  UtRegisterTest("DetectModbusTest05 - Testing access type",
964  DetectModbusTest05);
965  UtRegisterTest("DetectModbusTest06 - Testing access function",
966  DetectModbusTest06);
967  UtRegisterTest("DetectModbusTest07 - Testing access at address",
968  DetectModbusTest07);
969  UtRegisterTest("DetectModbusTest08 - Testing a range of address",
970  DetectModbusTest08);
971  UtRegisterTest("DetectModbusTest09 - Testing write a range of value",
972  DetectModbusTest09);
973  UtRegisterTest("DetectModbusTest10 - Testing unit_id",
974  DetectModbusTest10);
975  UtRegisterTest("DetectModbusTest11 - Testing unit_id, function and subfunction",
976  DetectModbusTest11);
977  UtRegisterTest("DetectModbusTest12 - Testing unit_id and access at address",
978  DetectModbusTest12);
979  UtRegisterTest("DetectModbusTest13 - Testing a range of unit_id",
980  DetectModbusTest13);
981 #endif /* UNITTESTS */
982 }
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect.h:1448
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1186
#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:767
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:1200
Signature container.
Definition: detect.h:522
#define MODBUS_TYP_COILS
main detection engine ctx
Definition: detect.h:761
DetectModbusMode mode
Definition: detect-modbus.h:49
#define DE_QUIET
Definition: detect.h:292
int DetectBufferTypeGetByName(const char *name)
#define str(s)
#define SCCalloc(nm, a)
Definition: util-mem.h:253
#define MAX_SUBSTRINGS
Definition: detect-modbus.c:82
uint8_t flags
Definition: detect.h:762
#define PARSE_REGEX_FUNCTION
Regex for parsing the Modbus function string.
Definition: detect-modbus.c:69
void(* Free)(void *)
Definition: detect.h:1191
uint8_t type
#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:236
#define SCEnter(...)
Definition: util-debug.h:337
DetectModbusValue * data
Definition: detect-modbus.h:59
int(* Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition: detect.h:1170
int SigGroupCleanup(DetectEngineCtx *de_ctx)
uint8_t type
Definition: detect.h:319
#define SCReturnInt(x)
Definition: util-debug.h:341
uint16_t * subfunction
Definition: detect-modbus.h:55
#define MODBUS_TYP_HOLDING
const char * desc
Definition: detect.h:1202
void SigMatchAppendSMToList(Signature *s, SigMatch *new, int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:346
SigMatchCtx * ctx
Definition: detect.h:321
#define MODBUS_CAT_ALL
#define MODBUS_TYP_INPUT
#define SCFree(a)
Definition: util-mem.h:322
PoolThreadReserved res
uint8_t function
Definition: detect-modbus.h:54
const char * url
Definition: detect.h:1203
#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
#define DOC_URL
Definition: suricata.h:86
DetectModbusValue * unit_id
Definition: detect-modbus.h:57
SigMatch * SigMatchAlloc(void)
Definition: detect-parse.c:232
#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 DOC_VERSION
Definition: suricata.h:91
#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:1192
a single match condition for a signature
Definition: detect.h:318
#define FAIL_IF_NOT(expr)
Fail a test if expression to true.
Definition: util-unittest.h:82
DetectEngineCtx * DetectEngineCtxInit(void)
#define MODBUS_TYP_READ