]>
Commit | Line | Data |
---|---|---|
75ec5283 PA |
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 | ||
4b0085b0 | 36 | static DetectParseRegex uint_pcre; |
75ec5283 PA |
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; | |
b80cdae1 | 52 | case DETECT_UINT_LTE: |
53 | if (parg <= du32->arg1) { | |
54 | return 1; | |
55 | } | |
56 | return 0; | |
75ec5283 PA |
57 | case DETECT_UINT_GT: |
58 | if (parg > du32->arg1) { | |
59 | return 1; | |
60 | } | |
61 | return 0; | |
b80cdae1 | 62 | case DETECT_UINT_GTE: |
63 | if (parg >= du32->arg1) { | |
64 | return 1; | |
65 | } | |
66 | return 0; | |
75ec5283 PA |
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 | ||
3f15b249 PA |
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 | } | |
75ec5283 PA |
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 | { | |
5dc21b0e SS |
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}; | |
75ec5283 PA |
120 | DetectU32Data *u32d = NULL; |
121 | char arg1[16] = ""; | |
122 | char arg2[16] = ""; | |
123 | char arg3[16] = ""; | |
124 | ||
75ec5283 | 125 | int ret = 0, res = 0; |
3de99a21 | 126 | size_t pcre2len; |
75ec5283 | 127 | |
3de99a21 | 128 | ret = DetectParsePcreExec(&uint_pcre, u32str, 0, 0); |
75ec5283 PA |
129 | if (ret < 2 || ret > 4) { |
130 | SCLogError(SC_ERR_PCRE_MATCH, "parse error, ret %" PRId32 "", ret); | |
131 | return NULL; | |
132 | } | |
133 | ||
3de99a21 PA |
134 | pcre2len = sizeof(arg1); |
135 | res = pcre2_substring_copy_bynumber(uint_pcre.match, 1, (PCRE2_UCHAR8 *)arg1, &pcre2len); | |
75ec5283 | 136 | if (res < 0) { |
3de99a21 | 137 | SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed"); |
75ec5283 PA |
138 | return NULL; |
139 | } | |
140 | SCLogDebug("Arg1 \"%s\"", arg1); | |
141 | ||
142 | if (ret >= 3) { | |
3de99a21 PA |
143 | pcre2len = sizeof(arg2); |
144 | res = pcre2_substring_copy_bynumber(uint_pcre.match, 2, (PCRE2_UCHAR8 *)arg2, &pcre2len); | |
75ec5283 | 145 | if (res < 0) { |
3de99a21 | 146 | SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed"); |
75ec5283 PA |
147 | return NULL; |
148 | } | |
149 | SCLogDebug("Arg2 \"%s\"", arg2); | |
150 | ||
151 | if (ret >= 4) { | |
3de99a21 PA |
152 | pcre2len = sizeof(arg3); |
153 | res = pcre2_substring_copy_bynumber( | |
154 | uint_pcre.match, 3, (PCRE2_UCHAR8 *)arg3, &pcre2len); | |
75ec5283 | 155 | if (res < 0) { |
3de99a21 | 156 | SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed"); |
75ec5283 PA |
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 '>': | |
c9d222a4 PA |
168 | if (strlen(arg2) == 1) { |
169 | if (strlen(arg3) == 0) | |
170 | return NULL; | |
75ec5283 | 171 | |
c9d222a4 PA |
172 | if (ByteExtractStringUint32(&u32da.arg1, 10, strlen(arg3), arg3) < 0) { |
173 | SCLogError(SC_ERR_BYTE_EXTRACT_FAILED, "ByteExtractStringUint32 failed"); | |
174 | return NULL; | |
175 | } | |
75ec5283 | 176 | |
c9d222a4 PA |
177 | SCLogDebug("u32 is %" PRIu32 "", u32da.arg1); |
178 | if (strlen(arg1) > 0) | |
179 | return NULL; | |
75ec5283 | 180 | |
c9d222a4 | 181 | if (arg2[0] == '<') { |
b80cdae1 | 182 | u32da.mode = DETECT_UINT_LT; |
c9d222a4 PA |
183 | } else { // arg2[0] == '>' |
184 | u32da.mode = DETECT_UINT_GT; | |
b80cdae1 | 185 | } |
c9d222a4 PA |
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] == '=') { | |
b80cdae1 | 192 | u32da.mode = DETECT_UINT_GTE; |
c9d222a4 PA |
193 | break; |
194 | } else if (arg2[0] != '<' || arg2[1] != '>') { | |
195 | return NULL; | |
b80cdae1 | 196 | } |
c9d222a4 PA |
197 | } else { |
198 | return NULL; | |
75ec5283 | 199 | } |
c9d222a4 | 200 | // fall through |
75ec5283 PA |
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 | } | |
3f15b249 PA |
248 | if (DetectU32Validate(&u32da)) { |
249 | SCLogError(SC_ERR_INVALID_VALUE, "Impossible value for uint32 condition : %s", u32str); | |
250 | return NULL; | |
251 | } | |
75ec5283 PA |
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 | |
263 | PrefilterPacketU32Set(PrefilterPacketHeaderValue *v, void *smctx) | |
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 | |
272 | PrefilterPacketU32Compare(PrefilterPacketHeaderValue v, void *smctx) | |
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 | ||
a5572890 | 282 | static bool g_detect_uint_registered = false; |
75ec5283 | 283 | |
a5572890 | 284 | void DetectUintRegister(void) |
75ec5283 | 285 | { |
a5572890 | 286 | if (g_detect_uint_registered == false) { |
75ec5283 | 287 | // register only once |
4b0085b0 | 288 | DetectSetupParseRegexes(PARSE_REGEX, &uint_pcre); |
a5572890 | 289 | g_detect_uint_registered = true; |
75ec5283 PA |
290 | } |
291 | } | |
a5572890 PA |
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; | |
b80cdae1 | 307 | case DETECT_UINT_LTE: |
308 | if (parg <= du8->arg1) { | |
309 | return 1; | |
310 | } | |
311 | return 0; | |
a5572890 PA |
312 | case DETECT_UINT_GT: |
313 | if (parg > du8->arg1) { | |
314 | return 1; | |
315 | } | |
316 | return 0; | |
b80cdae1 | 317 | case DETECT_UINT_GTE: |
318 | if (parg >= du8->arg1) { | |
319 | return 1; | |
320 | } | |
321 | return 0; | |
a5572890 PA |
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 | ||
3f15b249 PA |
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 | ||
a5572890 PA |
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 | { | |
5dc21b0e SS |
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}; | |
a5572890 PA |
375 | DetectU8Data *u8d = NULL; |
376 | char arg1[16] = ""; | |
377 | char arg2[16] = ""; | |
378 | char arg3[16] = ""; | |
379 | ||
380 | int ret = 0, res = 0; | |
3de99a21 | 381 | size_t pcre2len; |
a5572890 | 382 | |
3de99a21 | 383 | ret = DetectParsePcreExec(&uint_pcre, u8str, 0, 0); |
a5572890 PA |
384 | if (ret < 2 || ret > 4) { |
385 | SCLogError(SC_ERR_PCRE_MATCH, "parse error, ret %" PRId32 "", ret); | |
386 | return NULL; | |
387 | } | |
388 | ||
3de99a21 PA |
389 | pcre2len = sizeof(arg1); |
390 | res = pcre2_substring_copy_bynumber(uint_pcre.match, 1, (PCRE2_UCHAR8 *)arg1, &pcre2len); | |
a5572890 | 391 | if (res < 0) { |
3de99a21 | 392 | SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed"); |
a5572890 PA |
393 | return NULL; |
394 | } | |
395 | SCLogDebug("Arg1 \"%s\"", arg1); | |
396 | ||
397 | if (ret >= 3) { | |
3de99a21 PA |
398 | pcre2len = sizeof(arg2); |
399 | res = pcre2_substring_copy_bynumber(uint_pcre.match, 2, (PCRE2_UCHAR8 *)arg2, &pcre2len); | |
a5572890 | 400 | if (res < 0) { |
3de99a21 | 401 | SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed"); |
a5572890 PA |
402 | return NULL; |
403 | } | |
404 | SCLogDebug("Arg2 \"%s\"", arg2); | |
405 | ||
406 | if (ret >= 4) { | |
3de99a21 PA |
407 | pcre2len = sizeof(arg3); |
408 | res = pcre2_substring_copy_bynumber( | |
409 | uint_pcre.match, 3, (PCRE2_UCHAR8 *)arg3, &pcre2len); | |
a5572890 | 410 | if (res < 0) { |
3de99a21 | 411 | SCLogError(SC_ERR_PCRE_COPY_SUBSTRING, "pcre2_substring_copy_bynumber failed"); |
a5572890 PA |
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 '>': | |
c9d222a4 PA |
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 | } | |
a5572890 | 428 | |
c9d222a4 PA |
429 | SCLogDebug("u8 is %" PRIu8 "", u8da.arg1); |
430 | if (strlen(arg1) > 0) | |
431 | return NULL; | |
a5572890 | 432 | |
c9d222a4 | 433 | if (arg2[0] == '<') { |
b80cdae1 | 434 | u8da.mode = DETECT_UINT_LT; |
c9d222a4 PA |
435 | } else { // arg2[0] == '>' |
436 | u8da.mode = DETECT_UINT_GT; | |
b80cdae1 | 437 | } |
c9d222a4 PA |
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] == '=') { | |
b80cdae1 | 444 | u8da.mode = DETECT_UINT_GTE; |
c9d222a4 PA |
445 | break; |
446 | } else if (arg2[0] != '<' || arg2[1] != '>') { | |
447 | return NULL; | |
b80cdae1 | 448 | } |
c9d222a4 PA |
449 | } else { |
450 | return NULL; | |
a5572890 | 451 | } |
c9d222a4 | 452 | // fall through |
a5572890 PA |
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 | } | |
3f15b249 PA |
493 | if (DetectU8Validate(&u8da)) { |
494 | SCLogError(SC_ERR_INVALID_VALUE, "Impossible value for uint8 condition : %s", u8str); | |
495 | return NULL; | |
496 | } | |
a5572890 PA |
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 | } |