suricata
detect-engine-uint.c
Go to the documentation of this file.
1 /* Copyright (C) 2020 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Philippe Antoine <p.antoine@catenacyber.fr>
22  *
23  */
24 
25 #include "suricata-common.h"
26 
27 #include "util-byte.h"
28 #include "detect-parse.h"
29 #include "detect-engine-uint.h"
30 
31 /**
32  * \brief Regex for parsing our options
33  */
34 #define PARSE_REGEX "^\\s*([0-9]*)?\\s*([<>=-]+)?\\s*([0-9]+)?\\s*$"
35 
36 static DetectParseRegex uint_pcre;
37 
38 
39 int DetectU32Match(const uint32_t parg, const DetectU32Data *du32)
40 {
41  switch (du32->mode) {
42  case DETECT_UINT_EQ:
43  if (parg == du32->arg1) {
44  return 1;
45  }
46  return 0;
47  case DETECT_UINT_LT:
48  if (parg < du32->arg1) {
49  return 1;
50  }
51  return 0;
52  case DETECT_UINT_LTE:
53  if (parg <= du32->arg1) {
54  return 1;
55  }
56  return 0;
57  case DETECT_UINT_GT:
58  if (parg > du32->arg1) {
59  return 1;
60  }
61  return 0;
62  case DETECT_UINT_GTE:
63  if (parg >= du32->arg1) {
64  return 1;
65  }
66  return 0;
67  case DETECT_UINT_RA:
68  if (parg > du32->arg1 && parg < du32->arg2) {
69  return 1;
70  }
71  return 0;
72  default:
73  BUG_ON("unknown mode");
74  }
75  return 0;
76 }
77 
78 static int DetectU32Validate(DetectU32Data *du32)
79 {
80  switch (du32->mode) {
81  case DETECT_UINT_LT:
82  if (du32->arg1 == 0) {
83  return 1;
84  }
85  break;
86  case DETECT_UINT_GT:
87  if (du32->arg1 == UINT32_MAX) {
88  return 1;
89  }
90  break;
91  case DETECT_UINT_RA:
92  if (du32->arg1 >= du32->arg2) {
93  return 1;
94  }
95  // we need at least one value that can match parg > du32->arg1 && parg < du32->arg2
96  if (du32->arg1 + 1 >= du32->arg2) {
97  return 1;
98  }
99  break;
100  default:
101  break;
102  }
103  return 0;
104 }
105 
106 /**
107  * \brief This function is used to parse u32 options passed via some u32 keyword
108  *
109  * \param u32str Pointer to the user provided u32 options
110  *
111  * \retval DetectU32Data pointer to DetectU32Data on success
112  * \retval NULL on failure
113  */
114 
115 DetectU32Data *DetectU32Parse (const char *u32str)
116 {
117  /* We initialize these to please static checkers, these values will
118  either be updated or not used later on */
119  DetectU32Data u32da = {0, 0, 0};
120  DetectU32Data *u32d = NULL;
121  char arg1[16] = "";
122  char arg2[16] = "";
123  char arg3[16] = "";
124 
125  int ret = 0, res = 0;
126  size_t pcre2len;
127 
128  ret = DetectParsePcreExec(&uint_pcre, u32str, 0, 0);
129  if (ret < 2 || ret > 4) {
130  SCLogError(SC_ERR_PCRE_MATCH, "parse error, ret %" PRId32 "", ret);
131  return NULL;
132  }
133 
134  pcre2len = sizeof(arg1);
135  res = pcre2_substring_copy_bynumber(uint_pcre.match, 1, (PCRE2_UCHAR8 *)arg1, &pcre2len);
136  if (res < 0) {
137  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
138  return NULL;
139  }
140  SCLogDebug("Arg1 \"%s\"", arg1);
141 
142  if (ret >= 3) {
143  pcre2len = sizeof(arg2);
144  res = pcre2_substring_copy_bynumber(uint_pcre.match, 2, (PCRE2_UCHAR8 *)arg2, &pcre2len);
145  if (res < 0) {
146  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
147  return NULL;
148  }
149  SCLogDebug("Arg2 \"%s\"", arg2);
150 
151  if (ret >= 4) {
152  pcre2len = sizeof(arg3);
153  res = pcre2_substring_copy_bynumber(
154  uint_pcre.match, 3, (PCRE2_UCHAR8 *)arg3, &pcre2len);
155  if (res < 0) {
156  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
157  return NULL;
158  }
159  SCLogDebug("Arg3 \"%s\"", arg3);
160  }
161  }
162 
163  if (strlen(arg2) > 0) {
164  /*set the values*/
165  switch(arg2[0]) {
166  case '<':
167  case '>':
168  if (strlen(arg2) == 1) {
169  if (strlen(arg3) == 0)
170  return NULL;
171 
172  if (ByteExtractStringUint32(&u32da.arg1, 10, strlen(arg3), arg3) < 0) {
173  SCLogError(SC_ERR_BYTE_EXTRACT_FAILED, "ByteExtractStringUint32 failed");
174  return NULL;
175  }
176 
177  SCLogDebug("u32 is %" PRIu32 "", u32da.arg1);
178  if (strlen(arg1) > 0)
179  return NULL;
180 
181  if (arg2[0] == '<') {
182  u32da.mode = DETECT_UINT_LT;
183  } else { // arg2[0] == '>'
184  u32da.mode = DETECT_UINT_GT;
185  }
186  break;
187  } else if (strlen(arg2) == 2) {
188  if (arg2[0] == '<' && arg2[1] == '=') {
189  u32da.mode = DETECT_UINT_LTE;
190  break;
191  } else if (arg2[0] == '>' || arg2[1] == '=') {
192  u32da.mode = DETECT_UINT_GTE;
193  break;
194  } else if (arg2[0] != '<' || arg2[1] != '>') {
195  return NULL;
196  }
197  } else {
198  return NULL;
199  }
200  // fall through
201  case '-':
202  if (strlen(arg1)== 0)
203  return NULL;
204  if (strlen(arg3)== 0)
205  return NULL;
206 
207  u32da.mode = DETECT_UINT_RA;
208  if (ByteExtractStringUint32(&u32da.arg1, 10, strlen(arg1), arg1) < 0) {
209  SCLogError(SC_ERR_BYTE_EXTRACT_FAILED, "ByteExtractStringUint32 failed");
210  return NULL;
211  }
212  if (ByteExtractStringUint32(&u32da.arg2, 10, strlen(arg3), arg3) < 0) {
213  SCLogError(SC_ERR_BYTE_EXTRACT_FAILED, "ByteExtractStringUint32 failed");
214  return NULL;
215  }
216 
217  SCLogDebug("u32 is %"PRIu32" to %"PRIu32"", u32da.arg1, u32da.arg2);
218  if (u32da.arg1 >= u32da.arg2) {
219  SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid u32 range. ");
220  return NULL;
221  }
222  break;
223  default:
224  u32da.mode = DETECT_UINT_EQ;
225 
226  if (strlen(arg2) > 0 ||
227  strlen(arg3) > 0 ||
228  strlen(arg1) == 0)
229  return NULL;
230 
231  if (ByteExtractStringUint32(&u32da.arg1, 10, strlen(arg1), arg1) < 0) {
232  SCLogError(SC_ERR_BYTE_EXTRACT_FAILED, "ByteExtractStringUint32 failed");
233  return NULL;
234  }
235  }
236  } else {
237  u32da.mode = DETECT_UINT_EQ;
238 
239  if (strlen(arg3) > 0 ||
240  strlen(arg1) == 0)
241  return NULL;
242 
243  if (ByteExtractStringUint32(&u32da.arg1, 10, strlen(arg1), arg1) < 0) {
244  SCLogError(SC_ERR_BYTE_EXTRACT_FAILED, "ByteExtractStringUint32 failed");
245  return NULL;
246  }
247  }
248  if (DetectU32Validate(&u32da)) {
249  SCLogError(SC_ERR_INVALID_VALUE, "Impossible value for uint32 condition : %s", u32str);
250  return NULL;
251  }
252  u32d = SCCalloc(1, sizeof (DetectU32Data));
253  if (unlikely(u32d == NULL))
254  return NULL;
255  u32d->arg1 = u32da.arg1;
256  u32d->arg2 = u32da.arg2;
257  u32d->mode = u32da.mode;
258 
259  return u32d;
260 }
261 
262 void
264 {
265  const DetectU32Data *a = smctx;
266  v->u8[0] = a->mode;
267  v->u32[1] = a->arg1;
268  v->u32[2] = a->arg2;
269 }
270 
271 bool
273 {
274  const DetectU32Data *a = smctx;
275  if (v.u8[0] == a->mode &&
276  v.u32[1] == a->arg1 &&
277  v.u32[2] == a->arg2)
278  return true;
279  return false;
280 }
281 
282 static bool g_detect_uint_registered = false;
283 
285 {
286  if (g_detect_uint_registered == false) {
287  // register only once
289  g_detect_uint_registered = true;
290  }
291 }
292 
293 //same as u32 but with u8
294 int DetectU8Match(const uint8_t parg, const DetectU8Data *du8)
295 {
296  switch (du8->mode) {
297  case DETECT_UINT_EQ:
298  if (parg == du8->arg1) {
299  return 1;
300  }
301  return 0;
302  case DETECT_UINT_LT:
303  if (parg < du8->arg1) {
304  return 1;
305  }
306  return 0;
307  case DETECT_UINT_LTE:
308  if (parg <= du8->arg1) {
309  return 1;
310  }
311  return 0;
312  case DETECT_UINT_GT:
313  if (parg > du8->arg1) {
314  return 1;
315  }
316  return 0;
317  case DETECT_UINT_GTE:
318  if (parg >= du8->arg1) {
319  return 1;
320  }
321  return 0;
322  case DETECT_UINT_RA:
323  if (parg > du8->arg1 && parg < du8->arg2) {
324  return 1;
325  }
326  return 0;
327  default:
328  BUG_ON("unknown mode");
329  }
330  return 0;
331 }
332 
333 static int DetectU8Validate(DetectU8Data *du8)
334 {
335  switch (du8->mode) {
336  case DETECT_UINT_LT:
337  if (du8->arg1 == 0) {
338  return 1;
339  }
340  break;
341  case DETECT_UINT_GT:
342  if (du8->arg1 == UINT8_MAX) {
343  return 1;
344  }
345  break;
346  case DETECT_UINT_RA:
347  if (du8->arg1 >= du8->arg2) {
348  return 1;
349  }
350  // we need at least one value that can match parg > du8->arg1 && parg < du8->arg2
351  if (du8->arg1 + 1 >= du8->arg2) {
352  return 1;
353  }
354  break;
355  default:
356  break;
357  }
358  return 0;
359 }
360 
361 /**
362  * \brief This function is used to parse u8 options passed via some u8 keyword
363  *
364  * \param u8str Pointer to the user provided u8 options
365  *
366  * \retval DetectU8Data pointer to DetectU8Data on success
367  * \retval NULL on failure
368  */
369 
370 DetectU8Data *DetectU8Parse (const char *u8str)
371 {
372  /* We initialize these to please static checkers, these values will
373  either be updated or not used later on */
374  DetectU8Data u8da = {0, 0, 0};
375  DetectU8Data *u8d = NULL;
376  char arg1[16] = "";
377  char arg2[16] = "";
378  char arg3[16] = "";
379 
380  int ret = 0, res = 0;
381  size_t pcre2len;
382 
383  ret = DetectParsePcreExec(&uint_pcre, u8str, 0, 0);
384  if (ret < 2 || ret > 4) {
385  SCLogError(SC_ERR_PCRE_MATCH, "parse error, ret %" PRId32 "", ret);
386  return NULL;
387  }
388 
389  pcre2len = sizeof(arg1);
390  res = pcre2_substring_copy_bynumber(uint_pcre.match, 1, (PCRE2_UCHAR8 *)arg1, &pcre2len);
391  if (res < 0) {
392  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
393  return NULL;
394  }
395  SCLogDebug("Arg1 \"%s\"", arg1);
396 
397  if (ret >= 3) {
398  pcre2len = sizeof(arg2);
399  res = pcre2_substring_copy_bynumber(uint_pcre.match, 2, (PCRE2_UCHAR8 *)arg2, &pcre2len);
400  if (res < 0) {
401  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
402  return NULL;
403  }
404  SCLogDebug("Arg2 \"%s\"", arg2);
405 
406  if (ret >= 4) {
407  pcre2len = sizeof(arg3);
408  res = pcre2_substring_copy_bynumber(
409  uint_pcre.match, 3, (PCRE2_UCHAR8 *)arg3, &pcre2len);
410  if (res < 0) {
411  SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed");
412  return NULL;
413  }
414  SCLogDebug("Arg3 \"%s\"", arg3);
415  }
416  }
417 
418  if (strlen(arg2) > 0) {
419  /*set the values*/
420  switch(arg2[0]) {
421  case '<':
422  case '>':
423  if (strlen(arg2) == 1) {
424  if (StringParseUint8(&u8da.arg1, 10, strlen(arg3), arg3) < 0) {
425  SCLogError(SC_ERR_BYTE_EXTRACT_FAILED, "ByteExtractStringUint8 failed");
426  return NULL;
427  }
428 
429  SCLogDebug("u8 is %" PRIu8 "", u8da.arg1);
430  if (strlen(arg1) > 0)
431  return NULL;
432 
433  if (arg2[0] == '<') {
434  u8da.mode = DETECT_UINT_LT;
435  } else { // arg2[0] == '>'
436  u8da.mode = DETECT_UINT_GT;
437  }
438  break;
439  } else if (strlen(arg2) == 2) {
440  if (arg2[0] == '<' && arg2[1] == '=') {
441  u8da.mode = DETECT_UINT_LTE;
442  break;
443  } else if (arg2[0] == '>' || arg2[1] == '=') {
444  u8da.mode = DETECT_UINT_GTE;
445  break;
446  } else if (arg2[0] != '<' || arg2[1] != '>') {
447  return NULL;
448  }
449  } else {
450  return NULL;
451  }
452  // fall through
453  case '-':
454  u8da.mode = DETECT_UINT_RA;
455  if (StringParseUint8(&u8da.arg1, 10, strlen(arg1), arg1) < 0) {
456  SCLogError(SC_ERR_BYTE_EXTRACT_FAILED, "ByteExtractStringUint8 failed");
457  return NULL;
458  }
459  if (StringParseUint8(&u8da.arg2, 10, strlen(arg3), arg3) < 0) {
460  SCLogError(SC_ERR_BYTE_EXTRACT_FAILED, "ByteExtractStringUint8 failed");
461  return NULL;
462  }
463 
464  SCLogDebug("u8 is %"PRIu8" to %"PRIu8"", u8da.arg1, u8da.arg2);
465  if (u8da.arg1 >= u8da.arg2) {
466  SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid u8 range. ");
467  return NULL;
468  }
469  break;
470  default:
471  u8da.mode = DETECT_UINT_EQ;
472 
473  if (strlen(arg2) > 0 ||
474  strlen(arg3) > 0)
475  return NULL;
476 
477  if (StringParseUint8(&u8da.arg1, 10, strlen(arg1), arg1) < 0) {
478  SCLogError(SC_ERR_BYTE_EXTRACT_FAILED, "ByteExtractStringUint8 failed");
479  return NULL;
480  }
481  }
482  } else {
483  u8da.mode = DETECT_UINT_EQ;
484 
485  if (strlen(arg3) > 0)
486  return NULL;
487 
488  if (StringParseUint8(&u8da.arg1, 10, strlen(arg1), arg1) < 0) {
489  SCLogError(SC_ERR_BYTE_EXTRACT_FAILED, "ByteExtractStringUint8 failed");
490  return NULL;
491  }
492  }
493  if (DetectU8Validate(&u8da)) {
494  SCLogError(SC_ERR_INVALID_VALUE, "Impossible value for uint8 condition : %s", u8str);
495  return NULL;
496  }
497  u8d = SCCalloc(1, sizeof (DetectU8Data));
498  if (unlikely(u8d == NULL))
499  return NULL;
500  u8d->arg1 = u8da.arg1;
501  u8d->arg2 = u8da.arg2;
502  u8d->mode = u8da.mode;
503 
504  return u8d;
505 }
util-byte.h
detect-engine-uint.h
DetectParseRegex::match
pcre2_match_data * match
Definition: detect-parse.h:45
DetectU8Data_::arg1
uint8_t arg1
Definition: detect-engine-uint.h:53
DetectParsePcreExec
int DetectParsePcreExec(DetectParseRegex *parse_regex, const char *str, int start_offset, int options)
Definition: detect-parse.c:2467
SC_ERR_INVALID_VALUE
@ SC_ERR_INVALID_VALUE
Definition: util-error.h:160
DetectParseRegex
Definition: detect-parse.h:42
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:298
PrefilterPacketU32Set
void PrefilterPacketU32Set(PrefilterPacketHeaderValue *v, void *smctx)
Definition: detect-engine-uint.c:263
DETECT_UINT_LTE
@ DETECT_UINT_LTE
Definition: detect-engine-uint.h:34
PARSE_REGEX
#define PARSE_REGEX
Regex for parsing our options.
Definition: detect-engine-uint.c:34
SC_ERR_INVALID_SIGNATURE
@ SC_ERR_INVALID_SIGNATURE
Definition: util-error.h:69
DetectU8Data_::mode
DetectUintMode mode
Definition: detect-engine-uint.h:56
PrefilterPacketHeaderValue::u8
uint8_t u8[16]
Definition: detect-engine-prefilter-common.h:22
DetectU32Parse
DetectU32Data * DetectU32Parse(const char *u32str)
This function is used to parse u32 options passed via some u32 keyword.
Definition: detect-engine-uint.c:115
DetectU8Match
int DetectU8Match(const uint8_t parg, const DetectU8Data *du8)
Definition: detect-engine-uint.c:294
DetectU8Data_
Definition: detect-engine-uint.h:52
DetectU32Match
int DetectU32Match(const uint32_t parg, const DetectU32Data *du32)
Definition: detect-engine-uint.c:39
SC_ERR_PCRE_MATCH
@ SC_ERR_PCRE_MATCH
Definition: util-error.h:32
res
PoolThreadReserved res
Definition: stream-tcp-private.h:0
DetectSetupParseRegexes
void DetectSetupParseRegexes(const char *parse_str, DetectParseRegex *detect_parse)
Definition: detect-parse.c:2590
SC_ERR_PCRE_COPY_SUBSTRING
@ SC_ERR_PCRE_COPY_SUBSTRING
Definition: util-error.h:358
PrefilterPacketHeaderValue::u32
uint32_t u32[4]
Definition: detect-engine-prefilter-common.h:24
DETECT_UINT_GTE
@ DETECT_UINT_GTE
Definition: detect-engine-uint.h:35
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:281
SC_ERR_BYTE_EXTRACT_FAILED
@ SC_ERR_BYTE_EXTRACT_FAILED
Definition: util-error.h:158
DETECT_UINT_EQ
@ DETECT_UINT_EQ
Definition: detect-engine-uint.h:31
ByteExtractStringUint32
int ByteExtractStringUint32(uint32_t *res, int base, uint16_t len, const char *str)
Definition: util-byte.c:239
PrefilterPacketU32Compare
bool PrefilterPacketU32Compare(PrefilterPacketHeaderValue v, void *smctx)
Definition: detect-engine-uint.c:272
DETECT_UINT_LT
@ DETECT_UINT_LT
Definition: detect-engine-uint.h:30
DetectU32Data_::arg2
uint32_t arg2
Definition: detect-engine-uint.h:40
suricata-common.h
DetectU32Data_::mode
DetectUintMode mode
Definition: detect-engine-uint.h:42
SCLogError
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:257
DetectU8Data_::arg2
uint8_t arg2
Definition: detect-engine-uint.h:54
DetectU32Data_
Definition: detect-engine-uint.h:38
DETECT_UINT_GT
@ DETECT_UINT_GT
Definition: detect-engine-uint.h:32
DetectU8Parse
DetectU8Data * DetectU8Parse(const char *u8str)
This function is used to parse u8 options passed via some u8 keyword.
Definition: detect-engine-uint.c:370
detect-parse.h
StringParseUint8
int StringParseUint8(uint8_t *res, int base, uint16_t len, const char *str)
Definition: util-byte.c:359
DETECT_UINT_RA
@ DETECT_UINT_RA
Definition: detect-engine-uint.h:33
PrefilterPacketHeaderValue
Definition: detect-engine-prefilter-common.h:21
DetectU32Data_::arg1
uint32_t arg1
Definition: detect-engine-uint.h:39
DetectUintRegister
void DetectUintRegister(void)
Definition: detect-engine-uint.c:284
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53