]> git.ipfire.org Git - people/ms/suricata.git/blame - src/detect-engine-uint.c
detect: allows <> syntax for uint ranges
[people/ms/suricata.git] / src / detect-engine-uint.c
CommitLineData
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 36static DetectParseRegex uint_pcre;
75ec5283
PA
37
38
39int 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
78static 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
115DetectU32Data *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
262void
263PrefilterPacketU32Set(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
271bool
272PrefilterPacketU32Compare(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 282static bool g_detect_uint_registered = false;
75ec5283 283
a5572890 284void 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
294int 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
333static 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
370DetectU8Data *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}