suricata
detect-fast-pattern.c
Go to the documentation of this file.
1/* Copyright (C) 2007-2021 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 Anoop Saldanha <anoopsaldanha@gmail.com>
22 *
23 * Implements the fast_pattern keyword
24 */
25
26#include "suricata-common.h"
27#include "detect.h"
28#include "flow.h"
29#include "detect-content.h"
30#include "detect-parse.h"
31#include "detect-engine.h"
32#include "detect-engine-mpm.h"
33#include "detect-engine-build.h"
34#include "detect-fast-pattern.h"
35
36#include "util-error.h"
37#include "util-byte.h"
38#include "util-debug.h"
39#include "util-unittest.h"
41
42#define PARSE_REGEX "^(\\s*only\\s*)|\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*$"
43
44static DetectParseRegex parse_regex;
45
46static int DetectFastPatternSetup(DetectEngineCtx *, Signature *, const char *);
47#ifdef UNITTESTS
48static void DetectFastPatternRegisterTests(void);
49#endif
50
51/* holds the list of sm match lists that need to be searched for a keyword
52 * that has fp support */
53static SCFPSupportSMList *g_fp_support_smlist_list = NULL;
54
55/**
56 * \brief Checks if a particular buffer is in the list
57 * of lists that need to be searched for a keyword that has fp support.
58 *
59 * \param list_id The list id.
60 *
61 * \retval 1 If supported.
62 * \retval 0 If not.
63 */
65 const int list_id)
66{
67 if (de_ctx->fp_support_smlist_list == NULL) {
68 return 0;
69 }
70
71 if (list_id == DETECT_SM_LIST_PMATCH)
72 return 1;
73
75}
76
77static void Add(SCFPSupportSMList **list, const int list_id, const int priority)
78{
79 SCFPSupportSMList *ip = NULL;
80 /* insertion point - ip */
81 for (SCFPSupportSMList *tmp = *list; tmp != NULL; tmp = tmp->next) {
82 if (list_id == tmp->list_id) {
83 SCLogDebug("SM list already registered.");
84 return;
85 }
86
87 /* We need a strict check to be sure that the current list
88 * was not already registered
89 * and other lists with the same priority hide it.
90 */
91 if (priority < tmp->priority)
92 break;
93
94 ip = tmp;
95 }
96
97 if (*list == NULL) {
99 if (unlikely(new == NULL))
100 exit(EXIT_FAILURE);
101 new->list_id = list_id;
102 new->priority = priority;
103
104 *list = new;
105 return;
106 }
107
109 if (unlikely(new == NULL))
110 exit(EXIT_FAILURE);
111 new->list_id = list_id;
112 new->priority = priority;
113 if (ip == NULL) {
114 new->next = *list;
115 *list = new;
116 } else {
117 new->next = ip->next;
118 ip->next = new;
119 }
120}
121
122/**
123 * \brief Lets one add a sm list id to be searched for potential fp supported
124 * keywords later.
125 *
126 * \param list_id SM list id.
127 * \param priority Priority for this list.
128 */
129void SupportFastPatternForSigMatchList(int list_id, int priority)
130{
131 Add(&g_fp_support_smlist_list, list_id, priority);
132}
133
135{
136 Add(&de_ctx->fp_support_smlist_list, list_id, priority);
137}
138
139/**
140 * \brief Registers the keywords(SMs) that should be given fp support.
141 */
143{
145
146 /* other types are handled by DetectMpmAppLayerRegister() */
147}
148
150{
151 SCFPSupportSMList *last = NULL;
152 for (SCFPSupportSMList *tmp = g_fp_support_smlist_list; tmp != NULL; tmp = tmp->next) {
153 SCFPSupportSMList *n = SCCalloc(1, sizeof(*n));
154 if (n == NULL) {
155 FatalError("out of memory: %s", strerror(errno));
156 }
157 n->list_id = tmp->list_id;
158 n->priority = tmp->priority;
159
160 // append
161 if (de_ctx->fp_support_smlist_list == NULL) {
162 last = de_ctx->fp_support_smlist_list = n;
163 } else {
164 BUG_ON(last == NULL);
165 last->next = n;
166 last = n;
167 }
168 }
169}
170
172{
173 for (SCFPSupportSMList *tmp = de_ctx->fp_support_smlist_list; tmp != NULL;) {
175 SCFree(tmp);
176 tmp = next;
177 }
179}
180
181/**
182 * \brief Registration function for fast_pattern keyword
183 */
185{
186 sigmatch_table[DETECT_FAST_PATTERN].name = "fast_pattern";
187 sigmatch_table[DETECT_FAST_PATTERN].desc = "force using preceding content in the multi pattern matcher";
188 sigmatch_table[DETECT_FAST_PATTERN].url = "/rules/prefilter-keywords.html#fast-pattern";
190 sigmatch_table[DETECT_FAST_PATTERN].Setup = DetectFastPatternSetup;
192#ifdef UNITTESTS
193 sigmatch_table[DETECT_FAST_PATTERN].RegisterTests = DetectFastPatternRegisterTests;
194#endif
196
198}
199
200/**
201 * \brief Configures the previous content context for a fast_pattern modifier
202 * keyword used in the rule.
203 *
204 * \param de_ctx Pointer to the Detection Engine Context.
205 * \param s Pointer to the Signature to which the current keyword belongs.
206 * \param arg May hold an argument
207 *
208 * \retval 0 On success.
209 * \retval -1 On failure.
210 */
211static int DetectFastPatternSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
212{
213 int res = 0;
214 size_t pcre2len;
215 char arg_substr[128] = "";
216 DetectContentData *cd = NULL;
217 pcre2_match_data *match = NULL;
218
221 if (pm1 == NULL && pm2 == NULL) {
222 SCLogError("fast_pattern found inside "
223 "the rule, without a content context. Please use a "
224 "content based keyword before using fast_pattern");
225 return -1;
226 }
227
228 SigMatch *pm = NULL;
229 if (pm1 && pm2) {
230 if (pm1->idx > pm2->idx)
231 pm = pm1;
232 else
233 pm = pm2;
234 } else if (pm1 && !pm2) {
235 pm = pm1;
236 } else {
237 pm = pm2;
238 }
239
240 if (s->flags & SIG_FLAG_TXBOTHDIR && s->init_data->curbuf != NULL) {
243 SCLogError("fast_pattern cannot be used on to_client keyword for "
244 "transactional rule with a streaming buffer to server %u",
245 s->id);
246 goto error;
247 }
249 }
250 }
251
252 cd = (DetectContentData *)pm->ctx;
253 if ((cd->flags & DETECT_CONTENT_NEGATED) &&
257 (cd->flags & DETECT_CONTENT_DEPTH))) {
258
259 /* we can't have any of these if we are having "only" */
260 SCLogError("fast_pattern; cannot be "
261 "used with negated content, along with relative modifiers");
262 goto error;
263 }
264
265 if (arg == NULL|| strcmp(arg, "") == 0) {
267 SCLogError("can't use multiple fast_pattern "
268 "options for the same content");
269 goto error;
270 }
271 else { /*allow only one content to have fast_pattern modifier*/
272 for (uint32_t list_id = 0; list_id < DETECT_SM_LIST_MAX; list_id++) {
273 SigMatch *sm = NULL;
274 for (sm = s->init_data->smlists[list_id]; sm != NULL; sm = sm->next) {
275 if (sm->type == DETECT_CONTENT) {
276 DetectContentData *tmp_cd = (DetectContentData *)sm->ctx;
277 if (tmp_cd->flags & DETECT_CONTENT_FAST_PATTERN) {
278 SCLogError("fast_pattern "
279 "can be used on only one content in a rule");
280 goto error;
281 }
282 }
283 }
284 }
285 }
287 SCLogError("fast_pattern cannot be used with base64_data");
288 goto error;
289 }
291 return 0;
292 }
293
294 /* Execute the regex and populate args with captures. */
295 int ret = DetectParsePcreExec(&parse_regex, &match, arg, 0, 0);
296 /* fast pattern only */
297 if (ret == 2) {
298 if ((cd->flags & DETECT_CONTENT_NEGATED) ||
302 (cd->flags & DETECT_CONTENT_DEPTH)) {
303
304 /* we can't have any of these if we are having "only" */
305 SCLogError("fast_pattern: only; cannot be "
306 "used with negated content or with any of the relative "
307 "modifiers like distance, within, offset, depth");
308 goto error;
309 }
311
312 /* fast pattern chop */
313 } else if (ret == 4) {
314 pcre2len = sizeof(arg_substr);
315 res = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)arg_substr, &pcre2len);
316 if (res < 0) {
317 SCLogError("pcre2_substring_copy_bynumber failed "
318 "for fast_pattern offset");
319 goto error;
320 }
321 uint16_t offset;
322 if (StringParseUint16(&offset, 10, 0,
323 (const char *)arg_substr) < 0) {
324 SCLogError("Invalid fast pattern offset:"
325 " \"%s\"",
326 arg_substr);
327 goto error;
328 }
329
330 pcre2len = sizeof(arg_substr);
331 res = pcre2_substring_copy_bynumber(match, 3, (PCRE2_UCHAR8 *)arg_substr, &pcre2len);
332 if (res < 0) {
333 SCLogError("pcre2_substring_copy_bynumber failed "
334 "for fast_pattern offset");
335 goto error;
336 }
337 uint16_t length;
338 if (StringParseUint16(&length, 10, 0,
339 (const char *)arg_substr) < 0) {
340 SCLogError("Invalid value for fast "
341 "pattern: \"%s\"",
342 arg_substr);
343 goto error;
344 }
345
346 // Avoiding integer overflow
347 if (offset > (65535 - length)) {
348 SCLogError("Fast pattern (length + offset) "
349 "exceeds limit pattern length limit");
350 goto error;
351 }
352
353 if (offset + length > cd->content_len) {
354 SCLogError("Fast pattern (length + "
355 "offset (%u)) exceeds pattern length (%u)",
356 offset + length, cd->content_len);
357 goto error;
358 }
359
361 cd->fp_chop_len = length;
363
364 } else {
365 SCLogError("parse error, ret %" PRId32 ", string %s", ret, arg);
366 goto error;
367 }
368
370
371 pcre2_match_data_free(match);
372 return 0;
373
374 error:
375 if (match) {
376 pcre2_match_data_free(match);
377 }
378 return -1;
379}
380
381/*----------------------------------Unittests---------------------------------*/
382
383#ifdef UNITTESTS
384#include "detect-engine-alert.h"
385#include "detect-engine-buffer.h"
386static SigMatch *GetMatches(Signature *s, const int list)
387{
389 if (sm == NULL && list < DETECT_SM_LIST_MAX) {
390 sm = s->init_data->smlists[list];
391 }
392 return sm;
393}
394
395static int DetectFastPatternStickySingle(const char *sticky, const int list)
396{
399 char string[1024];
400 snprintf(string, sizeof(string),
401 "alert tcp any any -> any any "
402 "(%s%scontent:\"one\"; fast_pattern; sid:1;)",
403 sticky ? sticky : "", sticky ? "; " : " ");
405 FAIL_IF_NULL(s);
406 SigMatch *sm = GetMatches(s, list);
407 FAIL_IF_NULL(sm);
413 PASS;
414}
415
416static int DetectFastPatternModifierSingle(const char *sticky, const int list)
417{
420 char string[1024];
421 snprintf(string, sizeof(string),
422 "alert tcp any any -> any any "
423 "(content:\"one\"; %s%sfast_pattern; sid:1;)",
424 sticky ? sticky : "", sticky ? "; " : " ");
426 FAIL_IF_NULL(s);
427 SigMatch *sm = GetMatches(s, list);
428 FAIL_IF_NULL(sm);
434 PASS;
435}
436
437static int DetectFastPatternStickySingleNoFP(const char *sticky, const int list)
438{
441 char string[1024];
442 snprintf(string, sizeof(string),
443 "alert tcp any any -> any any "
444 "(%s%scontent:\"one\"; sid:1;)",
445 sticky ? sticky : "", sticky ? "; " : " ");
447 FAIL_IF_NULL(s);
448 SigMatch *sm = GetMatches(s, list);
449 FAIL_IF_NULL(sm);
455 PASS;
456}
457
458static int DetectFastPatternModifierSingleNoFP(const char *sticky, const int list)
459{
462 char string[1024];
463 snprintf(string, sizeof(string),
464 "alert tcp any any -> any any "
465 "(content:\"one\"; %s%ssid:1;)",
466 sticky ? sticky : "", sticky ? "; " : " ");
468 FAIL_IF_NULL(s);
469 SigMatch *sm = GetMatches(s, list);
470 FAIL_IF_NULL(sm);
476 PASS;
477}
478
479static int DetectFastPatternStickySingleBadArg(const char *sticky)
480{
483 char string[1024];
484 /* bogus argument to fast_pattern */
485 snprintf(string, sizeof(string),
486 "alert tcp any any -> any any "
487 "(%s%scontent:\"one\"; fast_pattern:boo; sid:1;)",
488 sticky ? sticky : "", sticky ? "; " : " ");
491 /* fast_pattern only with distance */
492 snprintf(string, sizeof(string),
493 "alert tcp any any -> any any "
494 "(%s%scontent:\"one\"; fast_pattern:only; content:\"two\"; distance:10; sid:1;)",
495 sticky ? sticky : "", sticky ? "; " : " ");
496 s = DetectEngineAppendSig(de_ctx, string);
498 /* fast_pattern only with distance */
499 snprintf(string, sizeof(string),
500 "alert tcp any any -> any any "
501 "(%s%scontent:\"one\"; content:\"two\"; fast_pattern:only; distance:10; sid:1;)",
502 sticky ? sticky : "", sticky ? "; " : " ");
503 s = DetectEngineAppendSig(de_ctx, string);
505 /* fast_pattern only with distance */
506 snprintf(string, sizeof(string),
507 "alert tcp any any -> any any "
508 "(%s%scontent:\"one\"; content:\"two\"; distance:10; fast_pattern:only; sid:1;)",
509 sticky ? sticky : "", sticky ? "; " : " ");
510 s = DetectEngineAppendSig(de_ctx, string);
512 /* fast_pattern chop with invalid values */
513 snprintf(string, sizeof(string),
514 "alert tcp any any -> any any "
515 "(%s%scontent:\"one\"; fast_pattern:5,6; sid:1;)",
516 sticky ? sticky : "", sticky ? "; " : " ");
517 s = DetectEngineAppendSig(de_ctx, string);
520 PASS;
521}
522
523static int DetectFastPatternModifierBadRules(const char *sticky)
524{
527 char string[1024];
528 /* bogus argument to fast_pattern */
529 snprintf(string, sizeof(string),
530 "alert tcp any any -> any any "
531 "(content:\"one\"; %s%sfast_pattern:boo; sid:1;)",
532 sticky ? sticky : "", sticky ? "; " : " ");
535 /* fast_pattern only with distance */
536 snprintf(string, sizeof(string),
537 "alert tcp any any -> any any "
538 "(content:\"one\"; %s%sfast_pattern:only; content:\"two\"; %s%sdistance:10; sid:1;)",
539 sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
540 s = DetectEngineAppendSig(de_ctx, string);
542#if 0 // TODO bug?
543 /* fast_pattern only with distance */
544 snprintf(string, sizeof(string), "alert tcp any any -> any any "
545 "(content:\"one\"; %s%s content:\"two\"; %s%sdistance:10; fast_pattern:only; sid:1;)",
546 sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
547 s = DetectEngineAppendSig(de_ctx, string);
549#endif
550 /* fast_pattern only with within */
551 snprintf(string, sizeof(string),
552 "alert tcp any any -> any any "
553 "(content:\"one\"; %s%sfast_pattern:only; content:\"two\"; %s%swithin:10; sid:1;)",
554 sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
555 s = DetectEngineAppendSig(de_ctx, string);
557 /* fast_pattern only with within */
558 snprintf(string, sizeof(string),
559 "alert tcp any any -> any any "
560 "(content:\"one\"; %s%s content:\"two\"; %s%swithin:10; fast_pattern:only; sid:1;)",
561 sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
562 s = DetectEngineAppendSig(de_ctx, string);
564 /* fast_pattern only with offset */
565 snprintf(string, sizeof(string),
566 "alert tcp any any -> any any "
567 "(content:\"one\"; %s%sfast_pattern:only; offset:10; sid:1;)",
568 sticky ? sticky : "", sticky ? "; " : " ");
569 s = DetectEngineAppendSig(de_ctx, string);
571 /* fast_pattern only with offset */
572 snprintf(string, sizeof(string),
573 "alert tcp any any -> any any "
574 "(content:\"one\"; %s%s offset:10; fast_pattern:only; sid:1;)",
575 sticky ? sticky : "", sticky ? "; " : " ");
576 s = DetectEngineAppendSig(de_ctx, string);
578 /* fast_pattern only with depth */
579 snprintf(string, sizeof(string),
580 "alert tcp any any -> any any "
581 "(content:\"one\"; %s%sfast_pattern:only; depth:10; sid:1;)",
582 sticky ? sticky : "", sticky ? "; " : " ");
583 s = DetectEngineAppendSig(de_ctx, string);
585 /* fast_pattern only with depth */
586 snprintf(string, sizeof(string),
587 "alert tcp any any -> any any "
588 "(content:\"one\"; %s%s depth:10; fast_pattern:only; sid:1;)",
589 sticky ? sticky : "", sticky ? "; " : " ");
590 s = DetectEngineAppendSig(de_ctx, string);
592 /* fast_pattern only negate */
593 snprintf(string, sizeof(string),
594 "alert tcp any any -> any any "
595 "(content:\"one\"; %s%s content:!\"two\"; %s%sfast_pattern:only; sid:1;)",
596 sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
597 s = DetectEngineAppendSig(de_ctx, string);
599 /* fast_pattern chop with invalid values */
600 snprintf(string, sizeof(string),
601 "alert tcp any any -> any any "
602 "(content:\"one\"; %s%sfast_pattern:5,6; sid:1;)",
603 sticky ? sticky : "", sticky ? "; " : " ");
604 s = DetectEngineAppendSig(de_ctx, string);
606 /* fast_pattern chop with invalid values */
607 snprintf(string, sizeof(string),
608 "alert tcp any any -> any any "
609 "(content:\"one\"; %s%sfast_pattern:65977,2; sid:1;)",
610 sticky ? sticky : "", sticky ? "; " : " ");
611 s = DetectEngineAppendSig(de_ctx, string);
613 /* fast_pattern chop with invalid values */
614 snprintf(string, sizeof(string),
615 "alert tcp any any -> any any "
616 "(content:\"one\"; %s%sfast_pattern:2,65977; sid:1;)",
617 sticky ? sticky : "", sticky ? "; " : " ");
618 s = DetectEngineAppendSig(de_ctx, string);
620 /* fast_pattern chop with invalid values */
621 snprintf(string, sizeof(string),
622 "alert tcp any any -> any any "
623 "(content:\"one\"; %s%sfast_pattern:2,65534; sid:1;)",
624 sticky ? sticky : "", sticky ? "; " : " ");
625 s = DetectEngineAppendSig(de_ctx, string);
627 /* fast_pattern chop with invalid values */
628 snprintf(string, sizeof(string),
629 "alert tcp any any -> any any "
630 "(content:\"one\"; %s%sfast_pattern:65534,2; sid:1;)",
631 sticky ? sticky : "", sticky ? "; " : " ");
632 s = DetectEngineAppendSig(de_ctx, string);
634 /* negated fast_pattern with distance */
635 snprintf(string, sizeof(string),
636 "alert tcp any any -> any any "
637 "(content:\"one\"; %s%scontent:!\"two\"; fast_pattern:1,2; %s%sdistance:10; sid:1;)",
638 sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
639 s = DetectEngineAppendSig(de_ctx, string);
641 /* negated fast_pattern with within */
642 snprintf(string, sizeof(string),
643 "alert tcp any any -> any any "
644 "(content:\"one\"; %s%scontent:!\"two\"; fast_pattern:1,2; %s%swithin:10; sid:1;)",
645 sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
646 s = DetectEngineAppendSig(de_ctx, string);
648 /* negated fast_pattern with depth */
649 snprintf(string, sizeof(string),
650 "alert tcp any any -> any any "
651 "(content:\"one\"; %s%scontent:!\"two\"; fast_pattern:1,2; %s%sdepth:10; sid:1;)",
652 sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
653 s = DetectEngineAppendSig(de_ctx, string);
655 /* negated fast_pattern with offset */
656 snprintf(string, sizeof(string),
657 "alert tcp any any -> any any "
658 "(content:\"one\"; %s%scontent:!\"two\"; fast_pattern:1,2; %s%soffset:10; sid:1;)",
659 sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
660 s = DetectEngineAppendSig(de_ctx, string);
663 PASS;
664}
665
666static int DetectFastPatternStickySingleFPOnly(const char *sticky, const int list)
667{
670 char string[1024];
671 snprintf(string, sizeof(string),
672 "alert tcp any any -> any any "
673 "(%s%scontent:\"one\"; fast_pattern:only; sid:1;)",
674 sticky ? sticky : "", sticky ? "; " : " ");
676 FAIL_IF_NULL(s);
677 SigMatch *sm = GetMatches(s, list);
678 FAIL_IF_NULL(sm);
685 PASS;
686}
687
688static int DetectFastPatternModifierFPOnly(const char *sticky, const int list)
689{
692 char string[1024];
693 snprintf(string, sizeof(string),
694 "alert tcp any any -> any any "
695 "(content:\"one\"; %s%sfast_pattern:only; sid:1;)",
696 sticky ? sticky : "", sticky ? "; " : " ");
698 FAIL_IF_NULL(s);
699 SigMatch *sm = GetMatches(s, list);
700 FAIL_IF_NULL(sm);
706
707 snprintf(string, sizeof(string),
708 "alert tcp any any -> any any "
709 "(content:\"one\"; %s%scontent:\"two\"; %s%sfast_pattern:only; sid:2;)",
710 sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
711 s = DetectEngineAppendSig(de_ctx, string);
712 FAIL_IF_NULL(s);
713 sm = GetMatches(s, list);
714 FAIL_IF_NULL(sm);
715 FAIL_IF_NULL(sm->next);
717 cd = (DetectContentData *)sm->ctx;
721 sm = sm->next;
723 cd = (DetectContentData *)sm->ctx;
727
728 snprintf(string, sizeof(string),
729 "alert tcp any any -> any any "
730 "(content:\"one\"; %s%scontent:\"two\"; distance:10; %s%scontent:\"three\"; "
731 "%s%sfast_pattern:only; sid:3;)",
732 sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ",
733 sticky ? sticky : "", sticky ? "; " : " ");
734 s = DetectEngineAppendSig(de_ctx, string);
735 FAIL_IF_NULL(s);
736 sm = GetMatches(s, list);
737 FAIL_IF_NULL(sm);
738 FAIL_IF_NULL(sm->next);
740 cd = (DetectContentData *)sm->ctx;
744 sm = sm->next;
745 FAIL_IF_NULL(sm->next);
747 cd = (DetectContentData *)sm->ctx;
751 sm = sm->next;
754 cd = (DetectContentData *)sm->ctx;
758
759 snprintf(string, sizeof(string),
760 "alert tcp any any -> any any "
761 "(content:\"one\"; %s%scontent:\"two\"; within:10; %s%scontent:\"three\"; "
762 "%s%sfast_pattern:only; sid:4;)",
763 sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ",
764 sticky ? sticky : "", sticky ? "; " : " ");
765 s = DetectEngineAppendSig(de_ctx, string);
766 FAIL_IF_NULL(s);
767 sm = GetMatches(s, list);
768 FAIL_IF_NULL(sm);
769 FAIL_IF_NULL(sm->next);
771 cd = (DetectContentData *)sm->ctx;
775 sm = sm->next;
776 FAIL_IF_NULL(sm->next);
778 cd = (DetectContentData *)sm->ctx;
782 sm = sm->next;
785 cd = (DetectContentData *)sm->ctx;
789
790 snprintf(string, sizeof(string),
791 "alert tcp any any -> any any "
792 "(content:\"one\"; %s%scontent:\"two\"; offset:10; %s%scontent:\"three\"; "
793 "%s%sfast_pattern:only; sid:5;)",
794 sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ",
795 sticky ? sticky : "", sticky ? "; " : " ");
796 s = DetectEngineAppendSig(de_ctx, string);
797 FAIL_IF_NULL(s);
798 sm = GetMatches(s, list);
799 FAIL_IF_NULL(sm);
800 FAIL_IF_NULL(sm->next);
802 cd = (DetectContentData *)sm->ctx;
806 sm = sm->next;
807 FAIL_IF_NULL(sm->next);
809 cd = (DetectContentData *)sm->ctx;
813 sm = sm->next;
816 cd = (DetectContentData *)sm->ctx;
820
821 snprintf(string, sizeof(string),
822 "alert tcp any any -> any any "
823 "(content:\"one\"; %s%scontent:\"two\"; depth:10; %s%scontent:\"three\"; "
824 "%s%sfast_pattern:only; sid:6;)",
825 sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ",
826 sticky ? sticky : "", sticky ? "; " : " ");
827 s = DetectEngineAppendSig(de_ctx, string);
828 FAIL_IF_NULL(s);
829 sm = GetMatches(s, list);
830 FAIL_IF_NULL(sm);
831 FAIL_IF_NULL(sm->next);
833 cd = (DetectContentData *)sm->ctx;
837 sm = sm->next;
838 FAIL_IF_NULL(sm->next);
840 cd = (DetectContentData *)sm->ctx;
844 sm = sm->next;
847 cd = (DetectContentData *)sm->ctx;
851
852 snprintf(string, sizeof(string),
853 "alert tcp any any -> any any "
854 "(content:!\"one\"; %s%sfast_pattern; content:\"two\"; depth:10; %s%ssid:7;)",
855 sticky ? sticky : "", sticky ? "; " : " ", sticky ? sticky : "", sticky ? "; " : " ");
856 s = DetectEngineAppendSig(de_ctx, string);
857 FAIL_IF_NULL(s);
858 sm = GetMatches(s, list);
859 FAIL_IF_NULL(sm);
860 FAIL_IF_NULL(sm->next);
862 cd = (DetectContentData *)sm->ctx;
867 sm = sm->next;
870 cd = (DetectContentData *)sm->ctx;
874
876 PASS;
877}
878
879static int DetectFastPatternStickyFPChop(const char *sticky, const int list)
880{
883 char string[1024];
884 snprintf(string, sizeof(string),
885 "alert tcp any any -> any any "
886 "(%s%scontent:\"onetwothree\"; fast_pattern:3,4; sid:1;)",
887 sticky ? sticky : "", sticky ? "; " : " ");
889 FAIL_IF_NULL(s);
890 SigMatch *sm = GetMatches(s, list);
891 FAIL_IF_NULL(sm);
898 FAIL_IF_NOT(cd->fp_chop_offset == 3);
899 FAIL_IF_NOT(cd->fp_chop_len == 4);
900
901 snprintf(string, sizeof(string),
902 "alert tcp any any -> any any "
903 "(%s%scontent:\"onetwothree\"; fast_pattern:3,4; content:\"xyz\"; distance:10; sid:2;)",
904 sticky ? sticky : "", sticky ? "; " : " ");
905 s = DetectEngineAppendSig(de_ctx, string);
906 FAIL_IF_NULL(s);
907 sm = GetMatches(s, list);
908 FAIL_IF_NULL(sm);
910 cd = (DetectContentData *)sm->ctx;
915 FAIL_IF_NOT(cd->fp_chop_offset == 3);
916 FAIL_IF_NOT(cd->fp_chop_len == 4);
917
919 PASS;
920}
921
922static int DetectFastPatternModifierFPChop(const char *sticky, const int list)
923{
926 char string[1024];
927 snprintf(string, sizeof(string),
928 "alert tcp any any -> any any "
929 "(content:\"onetwothree\"; %s%sfast_pattern:3,4; sid:1;)",
930 sticky ? sticky : "", sticky ? "; " : " ");
932 FAIL_IF_NULL(s);
933 SigMatch *sm = GetMatches(s, list);
934 FAIL_IF_NULL(sm);
941 FAIL_IF_NOT(cd->fp_chop_offset == 3);
942 FAIL_IF_NOT(cd->fp_chop_len == 4);
943
944 snprintf(string, sizeof(string),
945 "alert tcp any any -> any any "
946 "(content:!\"onetwothree\"; %s%sfast_pattern:3,4; sid:2;)",
947 sticky ? sticky : "", sticky ? "; " : " ");
948 s = DetectEngineAppendSig(de_ctx, string);
949 FAIL_IF_NULL(s);
950 sm = GetMatches(s, list);
951 FAIL_IF_NULL(sm);
953 cd = (DetectContentData *)sm->ctx;
960 FAIL_IF_NOT(cd->fp_chop_offset == 3);
961 FAIL_IF_NOT(cd->fp_chop_len == 4);
962
964 PASS;
965}
966
967/**
968 * \test Checks if a fast_pattern is registered in a Signature
969 */
970static int DetectFastPatternTest01(void)
971{
972 FAIL_IF_NOT(DetectFastPatternStickySingle(NULL, DETECT_SM_LIST_PMATCH));
973 FAIL_IF_NOT(DetectFastPatternModifierSingle(NULL, DETECT_SM_LIST_PMATCH));
974 FAIL_IF_NOT(DetectFastPatternStickySingleNoFP(NULL, DETECT_SM_LIST_PMATCH));
975 FAIL_IF_NOT(DetectFastPatternModifierSingleNoFP(NULL, DETECT_SM_LIST_PMATCH));
976 FAIL_IF_NOT(DetectFastPatternStickySingleBadArg(NULL));
977 FAIL_IF_NOT(DetectFastPatternModifierBadRules(NULL));
978 FAIL_IF_NOT(DetectFastPatternStickySingleFPOnly(NULL, DETECT_SM_LIST_PMATCH));
979 FAIL_IF_NOT(DetectFastPatternModifierFPOnly(NULL, DETECT_SM_LIST_PMATCH));
980 FAIL_IF_NOT(DetectFastPatternStickyFPChop(NULL, DETECT_SM_LIST_PMATCH));
981 FAIL_IF_NOT(DetectFastPatternModifierFPChop(NULL, DETECT_SM_LIST_PMATCH));
982
983 struct {
984 const char *buffer_name;
985 const char *sb_name;
986 const char *mod_name;
987 } keywords[] = {
988 { "file_data", "file.data", NULL },
989 { "http_uri", "http.uri", "http_uri" },
990 { "http_raw_uri", "http.uri.raw", "http_raw_uri" },
991 { "http_user_agent", "http.user_agent", "http_user_agent" },
992 { "http_header", "http.header", "http_header" },
993 // http_raw_header requires sigs to have a direction
994 //{ "http_raw_header", "http.header.raw", "http_raw_header" },
995 { "http_method", "http.method", "http_method" },
996 { "http_cookie", "http.cookie", "http_cookie" },
997 { "http_host", "http.host", "http_host" },
998 { "http_raw_host", "http.host.raw", "http_raw_host" },
999 { "http_stat_code", "http.stat_code", "http_stat_code" },
1000 { "http_stat_msg", "http.stat_msg", "http_stat_msg" },
1001 { "http_client_body", "http.request_body", "http_client_body" },
1002 { NULL, NULL, NULL },
1003 };
1004
1005 for (int i = 0; keywords[i].buffer_name != NULL; i++) {
1006 const int list_id = DetectBufferTypeGetByName(keywords[i].buffer_name);
1007 FAIL_IF(list_id == -1);
1008
1009 const char *k = keywords[i].sb_name;
1010 if (k) {
1011 FAIL_IF_NOT(DetectFastPatternStickySingle(k, list_id));
1012 FAIL_IF_NOT(DetectFastPatternStickySingleNoFP(k, list_id));
1013 FAIL_IF_NOT(DetectFastPatternStickySingleBadArg(k));
1014 FAIL_IF_NOT(DetectFastPatternStickySingleFPOnly(k, list_id));
1015 FAIL_IF_NOT(DetectFastPatternStickyFPChop(k, list_id));
1016 }
1017 k = keywords[i].mod_name;
1018 if (k) {
1019 FAIL_IF_NOT(DetectFastPatternModifierSingle(k, list_id));
1020 FAIL_IF_NOT(DetectFastPatternModifierSingleNoFP(k, list_id));
1021 FAIL_IF_NOT(DetectFastPatternModifierBadRules(k));
1022 FAIL_IF_NOT(DetectFastPatternModifierFPOnly(k, list_id));
1023 FAIL_IF_NOT(DetectFastPatternModifierFPChop(k, list_id));
1024 }
1025 }
1026
1027 PASS;
1028}
1029
1030/**
1031 * \test Checks to make sure that other sigs work that should when fast_pattern is inspecting on the
1032 * same payload
1033 *
1034 */
1035static int DetectFastPatternTest14(void)
1036{
1037 uint8_t *buf = (uint8_t *)"Dummy is our name. Oh yes. From right here "
1038 "right now, all the way to hangover. right. strings5_imp now here "
1039 "comes our dark knight strings_string5. Yes here is our dark knight";
1040 uint16_t buflen = strlen((char *)buf);
1041 ThreadVars th_v;
1042 DetectEngineThreadCtx *det_ctx = NULL;
1043
1044 memset(&th_v, 0, sizeof(th_v));
1045 Packet *p = UTHBuildPacket(buf, buflen, IPPROTO_TCP);
1046 FAIL_IF_NULL(p);
1047
1050 de_ctx->flags |= DE_QUIET;
1051
1053
1055 "alert tcp any any -> any any "
1056 "(msg:\"fast_pattern test\"; content:\"strings_string5\"; content:\"knight\"; "
1057 "fast_pattern; sid:1;)");
1058 FAIL_IF_NULL(s);
1059
1061 "alert tcp any any -> any any "
1062 "(msg:\"test different content\"; content:\"Dummy is our name\"; sid:2;)");
1063 FAIL_IF_NULL(s);
1064
1066 DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
1067
1068 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
1071
1072 UTHFreePackets(&p, 1);
1073 DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
1075 FlowShutdown();
1076 PASS;
1077}
1078
1079/**
1080 * Unittest to check
1081 * - if we assign different content_ids to duplicate patterns, but one of the
1082 * patterns has a fast_pattern chop set.
1083 * - if 2 unique patterns get unique ids.
1084 * - if 2 duplicate patterns, with no chop set get unique ids.
1085 */
1086static int DetectFastPatternTest671(void)
1087{
1090 de_ctx->flags |= DE_QUIET;
1091
1092 Signature *s[6];
1093 s[0] = DetectEngineAppendSig(
1094 de_ctx, "alert tcp any any -> any any (content:\"onetwothreefour\"; sid:1;)");
1095 FAIL_IF_NULL(s[0]);
1096 s[1] = DetectEngineAppendSig(
1097 de_ctx, "alert tcp any any -> any any (content:\"onetwothreefour\"; sid:2;)");
1098 FAIL_IF_NULL(s[1]);
1099 s[2] = DetectEngineAppendSig(
1100 de_ctx, "alert tcp any any -> any any (content:\"uniquepattern\"; sid:3;)");
1101 FAIL_IF_NULL(s[2]);
1103 "alert tcp any any -> any any (content:\"onetwothreefour\"; fast_pattern:3,5; sid:4;)");
1104 FAIL_IF_NULL(s[3]);
1105 s[4] = DetectEngineAppendSig(
1106 de_ctx, "alert tcp any any -> any any (content:\"twoth\"; sid:5;)");
1107 FAIL_IF_NULL(s[4]);
1109 "alert tcp any any -> any any (content:\"onetwothreefour\"; fast_pattern:0,15; "
1110 "sid:6;)");
1111 FAIL_IF_NULL(s[5]);
1112
1114
1117 FAIL_IF(cd->id != 0);
1118
1119 smd = s[1]->sm_arrays[DETECT_SM_LIST_PMATCH];
1120 cd = (DetectContentData *)smd->ctx;
1121 FAIL_IF(cd->id != 0);
1122
1123 smd = s[2]->sm_arrays[DETECT_SM_LIST_PMATCH];
1124 cd = (DetectContentData *)smd->ctx;
1125 FAIL_IF(cd->id != 2);
1126
1127 smd = s[3]->sm_arrays[DETECT_SM_LIST_PMATCH];
1128 cd = (DetectContentData *)smd->ctx;
1129 FAIL_IF(cd->id != 1);
1130
1131 smd = s[4]->sm_arrays[DETECT_SM_LIST_PMATCH];
1132 cd = (DetectContentData *)smd->ctx;
1133 FAIL_IF(cd->id != 1);
1134
1135 smd = s[5]->sm_arrays[DETECT_SM_LIST_PMATCH];
1136 cd = (DetectContentData *)smd->ctx;
1137 FAIL_IF(cd->id != 0);
1138
1140 PASS;
1141}
1142
1143static int DetectFastPatternPrefilter(void)
1144{
1147 const char *string = "alert tcp any any -> any any "
1148 "(content:\"one\"; prefilter; sid:1;)";
1150 FAIL_IF_NULL(s);
1152 FAIL_IF_NULL(sm);
1158 PASS;
1159}
1160
1161static void DetectFastPatternRegisterTests(void)
1162{
1163 UtRegisterTest("DetectFastPatternTest01", DetectFastPatternTest01);
1164 UtRegisterTest("DetectFastPatternTest14", DetectFastPatternTest14);
1165 /* Unittest to check
1166 * - if we assign different content_ids to duplicate patterns, but one of the
1167 * patterns has a fast_pattern chop set.
1168 * - if 2 unique patterns get unique ids.
1169 * - if 2 duplicate patterns, with no chop set get unique ids.
1170 */
1171 UtRegisterTest("DetectFastPatternTest671", DetectFastPatternTest671);
1172
1173 UtRegisterTest("DetectFastPatternPrefilter", DetectFastPatternPrefilter);
1174}
1175#endif
struct HtpBodyChunk_ * next
#define DETECT_CONTENT_FAST_PATTERN_CHOP
#define DETECT_CONTENT_DEPTH
#define DETECT_CONTENT_FAST_PATTERN_ONLY
#define DETECT_CONTENT_WITHIN
#define DETECT_CONTENT_FAST_PATTERN
#define DETECT_CONTENT_DISTANCE
#define DETECT_CONTENT_OFFSET
#define DETECT_CONTENT_NEGATED
#define DETECT_CONTENT_IS_SINGLE(c)
int PacketAlertCheck(Packet *p, uint32_t sid)
Check if a certain sid alerted, this is used in the test functions.
SigMatch * DetectBufferGetFirstSigMatch(const Signature *s, const uint32_t buf_id)
int SigGroupBuild(DetectEngineCtx *de_ctx)
Convert the signature list into the runtime match structure.
bool DetectBufferToClient(const DetectEngineCtx *de_ctx, int buf_id, AppProto alproto)
@ DETECT_FAST_PATTERN
DetectEngineCtx * DetectEngineCtxInit(void)
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Signature * DetectEngineAppendSig(DetectEngineCtx *, const char *)
Parse and append a Signature into the Detection Engine Context signature list.
TmEcode DetectEngineThreadCtxInit(ThreadVars *tv, void *initdata, void **data)
initialize thread specific detection engine context
bool DetectEngineBufferTypeSupportsMpmGetById(const DetectEngineCtx *de_ctx, const int id)
int DetectBufferTypeGetByName(const char *name)
TmEcode DetectEngineThreadCtxDeinit(ThreadVars *tv, void *data)
void SupportFastPatternForSigMatchList(int list_id, int priority)
Lets one add a sm list id to be searched for potential fp supported keywords later.
void DetectEngineFreeFastPatternList(DetectEngineCtx *de_ctx)
int FastPatternSupportEnabledForSigMatchList(const DetectEngineCtx *de_ctx, const int list_id)
Checks if a particular buffer is in the list of lists that need to be searched for a keyword that has...
void DetectEngineInitializeFastPatternList(DetectEngineCtx *de_ctx)
void DetectEngineRegisterFastPatternForId(DetectEngineCtx *de_ctx, int list_id, int priority)
void SupportFastPatternForSigMatchTypes(void)
Registers the keywords(SMs) that should be given fp support.
#define PARSE_REGEX
void DetectFastPatternRegister(void)
Registration function for fast_pattern keyword.
SigMatch * DetectGetLastSMFromMpmLists(const DetectEngineCtx *de_ctx, const Signature *s)
get the last SigMatch from lists that support MPM.
void DetectSetupParseRegexes(const char *parse_str, DetectParseRegex *detect_parse)
int DetectParsePcreExec(DetectParseRegex *parse_regex, pcre2_match_data **match, const char *str, int start_offset, int options)
SigMatch * DetectGetLastSMFromLists(const Signature *s,...)
Returns the sm with the largest index (added latest) from the lists passed to us.
int SigMatchListSMBelongsTo(const Signature *s, const SigMatch *key_sm)
SigTableElmt * sigmatch_table
void SigMatchSignatures(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
wrapper for old tests
Definition detect.c:2420
#define DE_QUIET
Definition detect.h:330
#define SIG_FLAG_INIT_TXDIR_STREAMING_TOSERVER
Definition detect.h:304
#define SIG_FLAG_INIT_TXDIR_FAST_TOCLIENT
Definition detect.h:306
#define SIGMATCH_OPTIONAL_OPT
Definition detect.h:1661
@ DETECT_SM_LIST_PMATCH
Definition detect.h:119
@ DETECT_SM_LIST_BASE64_DATA
Definition detect.h:124
@ DETECT_SM_LIST_MAX
Definition detect.h:135
#define SIG_FLAG_TXBOTHDIR
Definition detect.h:250
void FlowInitConfig(bool quiet)
initialize the configuration
Definition flow.c:547
void FlowShutdown(void)
shutdown the flow engine
Definition flow.c:691
#define FLOW_QUIET
Definition flow.h:43
DetectEngineCtx * de_ctx
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
#define PASS
Pass the test.
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
#define FAIL_IF_NOT_NULL(expr)
Fail a test if expression evaluates to non-NULL.
main detection engine ctx
Definition detect.h:932
uint8_t flags
Definition detect.h:934
SCFPSupportSMList * fp_support_smlist_list
Definition detect.h:1106
struct HtpBodyChunk_ * next
struct SCFPSupportSMList_ * next
Definition detect.h:842
Data needed for Match()
Definition detect.h:365
SigMatchCtx * ctx
Definition detect.h:368
a single match condition for a signature
Definition detect.h:356
uint16_t type
Definition detect.h:357
struct SigMatch_ * next
Definition detect.h:360
SigMatchCtx * ctx
Definition detect.h:359
uint16_t idx
Definition detect.h:358
const char * url
Definition detect.h:1462
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition detect.h:1441
void(* Free)(DetectEngineCtx *, void *)
Definition detect.h:1446
uint16_t flags
Definition detect.h:1450
const char * desc
Definition detect.h:1461
void(* RegisterTests)(void)
Definition detect.h:1448
int(* Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition detect.h:1421
const char * name
Definition detect.h:1459
uint32_t init_flags
Definition detect.h:608
struct SigMatch_ * smlists[DETECT_SM_LIST_MAX]
Definition detect.h:642
SignatureInitDataBuffer * curbuf
Definition detect.h:650
Signature container.
Definition detect.h:668
uint32_t flags
Definition detect.h:669
SignatureInitData * init_data
Definition detect.h:747
AppProto alproto
Definition detect.h:673
uint32_t id
Definition detect.h:713
SigMatchData * sm_arrays[DETECT_SM_LIST_MAX]
Definition detect.h:731
Per thread variable structure.
Definition threadvars.h:58
#define BUG_ON(x)
int StringParseUint16(uint16_t *res, int base, size_t len, const char *str)
Definition util-byte.c:337
#define FatalError(...)
Definition util-debug.h:510
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define unlikely(expr)
uint64_t offset
void UTHFreePackets(Packet **p, int numpkts)
UTHFreePackets: function to release the allocated data from UTHBuildPacket and the packet itself.
Packet * UTHBuildPacket(uint8_t *payload, uint16_t payload_len, uint8_t ipproto)
UTHBuildPacket is a wrapper that build packets with default ip and port fields.