suricata
detect-detection-filter.c
Go to the documentation of this file.
1/* Copyright (C) 2007-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 Gerardo Iglesias <iglesiasg@gmail.com>
22 *
23 * Implements the detection_filter keyword
24 */
25
26#include "suricata-common.h"
27#include "suricata.h"
28#include "decode.h"
29#include "detect.h"
30
31#include "host.h"
32
34#include "detect-threshold.h"
35#include "detect-parse.h"
36
37#include "util-byte.h"
38#include "util-unittest.h"
40#include "util-debug.h"
41#include "detect-engine-build.h"
42
43#define TRACK_DST 1
44#define TRACK_SRC 2
45
46/**
47 *\brief Regex for parsing our detection_filter options
48 */
49#define PARSE_REGEX \
50 "^\\s*(track|count|seconds)\\s+(by_src|by_dst|by_flow|\\d+)\\s*,\\s*(track|count|seconds)\\s+" \
51 "(by_src|" \
52 "by_dst|by_flow|\\d+)\\s*,\\s*(track|count|seconds)\\s+(by_src|by_dst|by_flow|\\d+)\\s*$"
53
54static DetectParseRegex parse_regex;
55
56static int DetectDetectionFilterMatch(
57 DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *);
58static int DetectDetectionFilterSetup(DetectEngineCtx *, Signature *, const char *);
59#ifdef UNITTESTS
60static void DetectDetectionFilterRegisterTests(void);
61#endif
62static void DetectDetectionFilterFree(DetectEngineCtx *, void *);
63
64/**
65 * \brief Registration function for detection_filter: keyword
66 */
68{
69 sigmatch_table[DETECT_DETECTION_FILTER].name = "detection_filter";
71 "alert on every match after a threshold has been reached";
72 sigmatch_table[DETECT_DETECTION_FILTER].url = "/rules/thresholding.html#detection-filter";
73 sigmatch_table[DETECT_DETECTION_FILTER].Match = DetectDetectionFilterMatch;
74 sigmatch_table[DETECT_DETECTION_FILTER].Setup = DetectDetectionFilterSetup;
75 sigmatch_table[DETECT_DETECTION_FILTER].Free = DetectDetectionFilterFree;
76#ifdef UNITTESTS
77 sigmatch_table[DETECT_DETECTION_FILTER].RegisterTests = DetectDetectionFilterRegisterTests;
78#endif
79 /* this is compatible to ip-only signatures */
81
83}
84
85static int DetectDetectionFilterMatch(
86 DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx)
87{
88 return 1;
89}
90
91/**
92 * \internal
93 * \brief This function is used to parse detection_filter options passed via detection_filter:
94 * keyword
95 *
96 * \param rawstr Pointer to the user provided detection_filter options
97 *
98 * \retval df pointer to DetectThresholdData on success
99 * \retval NULL on failure
100 */
101static DetectThresholdData *DetectDetectionFilterParse(const char *rawstr)
102{
103 DetectThresholdData *df = NULL;
104 int res = 0;
105 size_t pcre2_len;
106 const char *str_ptr = NULL;
107 char *args[6] = { NULL, NULL, NULL, NULL, NULL, NULL };
108 char *copy_str = NULL, *df_opt = NULL;
109 int seconds_found = 0, count_found = 0, track_found = 0;
110 int seconds_pos = 0, count_pos = 0;
111 size_t pos = 0;
112 int i = 0;
113 char *saveptr = NULL;
114 pcre2_match_data *match = NULL;
115
116 copy_str = SCStrdup(rawstr);
117 if (unlikely(copy_str == NULL)) {
118 goto error;
119 }
120
121 for (pos = 0, df_opt = strtok_r(copy_str, ",", &saveptr);
122 pos < strlen(copy_str) && df_opt != NULL;
123 pos++, df_opt = strtok_r(NULL, ",", &saveptr)) {
124 if (strstr(df_opt, "count"))
125 count_found++;
126 if (strstr(df_opt, "second"))
127 seconds_found++;
128 if (strstr(df_opt, "track"))
129 track_found++;
130 }
131 SCFree(copy_str);
132 copy_str = NULL;
133
134 if (count_found != 1 || seconds_found != 1 || track_found != 1)
135 goto error;
136
137 int ret = DetectParsePcreExec(&parse_regex, &match, rawstr, 0, 0);
138 if (ret < 5) {
139 SCLogError("pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr);
140 goto error;
141 }
142
143 df = SCCalloc(1, sizeof(DetectThresholdData));
144 if (unlikely(df == NULL))
145 goto error;
146
147 df->type = TYPE_DETECTION;
148
149 for (i = 0; i < (ret - 1); i++) {
150 res = pcre2_substring_get_bynumber(match, i + 1, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
151 if (res < 0) {
152 SCLogError("pcre2_substring_get_bynumber failed");
153 goto error;
154 }
155
156 args[i] = (char *)str_ptr;
157
158 if (strncasecmp(args[i], "by_dst", strlen("by_dst")) == 0)
159 df->track = TRACK_DST;
160 if (strncasecmp(args[i], "by_src", strlen("by_src")) == 0)
161 df->track = TRACK_SRC;
162 if (strncasecmp(args[i], "by_flow", strlen("by_flow")) == 0)
163 df->track = TRACK_FLOW;
164 if (strncasecmp(args[i], "count", strlen("count")) == 0)
165 count_pos = i + 1;
166 if (strncasecmp(args[i], "seconds", strlen("seconds")) == 0)
167 seconds_pos = i + 1;
168 }
169
170 if (args[count_pos] == NULL || args[seconds_pos] == NULL) {
171 goto error;
172 }
173
174 if (StringParseUint32(&df->count, 10, strlen(args[count_pos]), args[count_pos]) <= 0) {
175 goto error;
176 }
177
178 if (StringParseUint32(&df->seconds, 10, strlen(args[seconds_pos]), args[seconds_pos]) <= 0) {
179 goto error;
180 }
181
182 if (df->count == 0 || df->seconds == 0) {
183 SCLogError("found an invalid value");
184 goto error;
185 }
186
187 for (i = 0; i < 6; i++) {
188 if (args[i] != NULL)
189 pcre2_substring_free((PCRE2_UCHAR *)args[i]);
190 }
191
192 pcre2_match_data_free(match);
193 return df;
194
195error:
196 for (i = 0; i < 6; i++) {
197 if (args[i] != NULL)
198 pcre2_substring_free((PCRE2_UCHAR *)args[i]);
199 }
200 if (df != NULL)
201 SCFree(df);
202 if (match) {
203 pcre2_match_data_free(match);
204 }
205 return NULL;
206}
207
208/**
209 * \internal
210 * \brief this function is used to add the parsed detection_filter into the current signature
211 *
212 * \param de_ctx pointer to the Detection Engine Context
213 * \param s pointer to the Current Signature
214 * \param m pointer to the Current SigMatch
215 * \param rawstr pointer to the user provided detection_filter options
216 *
217 * \retval 0 on Success
218 * \retval -1 on Failure
219 */
220static int DetectDetectionFilterSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
221{
222 SCEnter();
223 DetectThresholdData *df = NULL;
224 SigMatch *tmpm = NULL;
225
226 /* checks if there's a previous instance of threshold */
228 if (tmpm != NULL) {
229 SCLogError("\"detection_filter\" and \"threshold\" are not allowed in the same rule");
230 SCReturnInt(-1);
231 }
232 /* checks there's no previous instance of detection_filter */
234 if (tmpm != NULL) {
235 SCLogError("At most one \"detection_filter\" is allowed per rule");
236 SCReturnInt(-1);
237 }
238
239 df = DetectDetectionFilterParse(rawstr);
240 if (df == NULL)
241 goto error;
242
244 DETECT_SM_LIST_THRESHOLD) == NULL) {
245 goto error;
246 }
247
248 return 0;
249
250error:
251 if (df)
252 SCFree(df);
253 return -1;
254}
255
256/**
257 * \internal
258 * \brief this function will free memory associated with DetectThresholdData
259 *
260 * \param df_ptr pointer to DetectDetectionFilterData
261 */
262static void DetectDetectionFilterFree(DetectEngineCtx *de_ctx, void *df_ptr)
263{
265 if (df)
266 SCFree(df);
267}
268
269/*
270 * ONLY TESTS BELOW THIS COMMENT
271 */
272#ifdef UNITTESTS
273#include "detect-engine.h"
274#include "detect-engine-mpm.h"
276#include "detect-engine-alert.h"
277#include "util-time.h"
278#include "util-hashlist.h"
279#include "action-globals.h"
280#include "packet.h"
281
282/**
283 * \test DetectDetectionFilterTestParse01 is a test for a valid detection_filter options
284 *
285 */
286static int DetectDetectionFilterTestParse01(void)
287{
288 DetectThresholdData *df = DetectDetectionFilterParse("track by_dst,count 10,seconds 60");
289 FAIL_IF_NULL(df);
291 FAIL_IF_NOT(df->count == 10);
292 FAIL_IF_NOT(df->seconds == 60);
293 DetectDetectionFilterFree(NULL, df);
294
295 PASS;
296}
297
298/**
299 * \test DetectDetectionFilterTestParse02 is a test for a invalid detection_filter options
300 *
301 */
302static int DetectDetectionFilterTestParse02(void)
303{
304 DetectThresholdData *df = DetectDetectionFilterParse("track both,count 10,seconds 60");
306
307 PASS;
308}
309
310/**
311 * \test DetectDetectionfilterTestParse03 is a test for a valid detection_filter options in any
312 * order
313 *
314 */
315static int DetectDetectionFilterTestParse03(void)
316{
317 DetectThresholdData *df = DetectDetectionFilterParse("track by_dst, seconds 60, count 10");
318 FAIL_IF_NULL(df);
320 FAIL_IF_NOT(df->count == 10);
321 FAIL_IF_NOT(df->seconds == 60);
322 DetectDetectionFilterFree(NULL, df);
323
324 PASS;
325}
326
327/**
328 * \test DetectDetectionFilterTestParse04 is a test for an invalid detection_filter options in any
329 * order
330 *
331 */
332static int DetectDetectionFilterTestParse04(void)
333{
335 DetectDetectionFilterParse("count 10, track by_dst, seconds 60, count 10");
337
338 PASS;
339}
340
341/**
342 * \test DetectDetectionFilterTestParse05 is a test for a valid detection_filter options in any
343 * order
344 *
345 */
346static int DetectDetectionFilterTestParse05(void)
347{
348 DetectThresholdData *df = DetectDetectionFilterParse("count 10, track by_dst, seconds 60");
349 FAIL_IF_NULL(df);
351 FAIL_IF_NOT(df->count == 10);
352 FAIL_IF_NOT(df->seconds == 60);
353 DetectDetectionFilterFree(NULL, df);
354
355 PASS;
356}
357
358/**
359 * \test DetectDetectionFilterTestParse06 is a test for an invalid value in detection_filter
360 *
361 */
362static int DetectDetectionFilterTestParse06(void)
363{
364 DetectThresholdData *df = DetectDetectionFilterParse("count 10, track by_dst, seconds 0");
366
367 PASS;
368}
369
370/**
371 * \test DetectDetectionFilterTestSig1 is a test for checking the working of detection_filter
372 * keyword by setting up the signature and later testing its working by matching the received packet
373 * against the sig.
374 *
375 */
376static int DetectDetectionFilterTestSig1(void)
377{
378 ThreadVars th_v;
379 DetectEngineThreadCtx *det_ctx;
380
382
383 memset(&th_v, 0, sizeof(th_v));
384
385 Packet *p = UTHBuildPacketReal(NULL, 0, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
386
389
391
393 "alert tcp any any -> any 80 (msg:\"detection_filter Test\"; detection_filter: "
394 "track by_dst, count 4, seconds 60; sid:1;)");
395 FAIL_IF_NULL(s);
396
398 DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
399
400 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
402 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
404 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
406 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
408 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
410 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
412 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
414 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
416
417 DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
419
420 UTHFreePackets(&p, 1);
422
423 PASS;
424}
425
426/**
427 * \test DetectDetectionFilterTestSig2 is a test for checking the working of detection_filter
428 * keyword by setting up the signature and later testing its working by matching the received packet
429 * against the sig.
430 *
431 */
432
433static int DetectDetectionFilterTestSig2(void)
434{
435 ThreadVars th_v;
436 DetectEngineThreadCtx *det_ctx;
437
439
440 memset(&th_v, 0, sizeof(th_v));
441
442 Packet *p = UTHBuildPacketReal(NULL, 0, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
443
445
447
449
451 "alert tcp any any -> any 80 (msg:\"detection_filter Test 2\"; "
452 "detection_filter: track by_dst, count 4, seconds 60; sid:10;)");
453 FAIL_IF_NULL(s);
454
456 DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
457
458 p->ts = TimeGet();
459
460 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
462 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
464 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
466
468 p->ts = TimeGet();
469
470 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
472 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
474 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
476 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
478
479 DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
481
482 UTHFreePackets(&p, 1);
484
485 PASS;
486}
487
488/**
489 * \test drops
490 */
491static int DetectDetectionFilterTestSig3(void)
492{
493 ThreadVars th_v;
494 DetectEngineThreadCtx *det_ctx;
495
497
498 memset(&th_v, 0, sizeof(th_v));
499
500 Packet *p = UTHBuildPacketReal(NULL, 0, IPPROTO_TCP, "1.1.1.1", "2.2.2.2", 1024, 80);
501
504
506
508 "drop tcp any any -> any 80 (msg:\"detection_filter Test 2\"; "
509 "detection_filter: track by_dst, count 2, seconds 60; sid:10;)");
510 FAIL_IF_NULL(s);
511
513 DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
514
515 p->ts = TimeGet();
516
517 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
519 FAIL_IF(PacketTestAction(p, ACTION_DROP));
520 p->action = 0;
521
522 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
524 FAIL_IF(PacketTestAction(p, ACTION_DROP));
525 p->action = 0;
526
527 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
529 FAIL_IF_NOT(PacketTestAction(p, ACTION_DROP));
530 p->action = 0;
531
533 p->ts = TimeGet();
534
535 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
537 FAIL_IF(PacketTestAction(p, ACTION_DROP));
538 p->action = 0;
539
540 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
542 FAIL_IF(PacketTestAction(p, ACTION_DROP));
543 p->action = 0;
544
545 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
547 FAIL_IF_NOT(PacketTestAction(p, ACTION_DROP));
548 p->action = 0;
549
550 SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
552 FAIL_IF_NOT(PacketTestAction(p, ACTION_DROP));
553 p->action = 0;
554
555 DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
557
558 UTHFreePackets(&p, 1);
560
561 PASS;
562}
563
564static void DetectDetectionFilterRegisterTests(void)
565{
566 UtRegisterTest("DetectDetectionFilterTestParse01", DetectDetectionFilterTestParse01);
567 UtRegisterTest("DetectDetectionFilterTestParse02", DetectDetectionFilterTestParse02);
568 UtRegisterTest("DetectDetectionFilterTestParse03", DetectDetectionFilterTestParse03);
569 UtRegisterTest("DetectDetectionFilterTestParse04", DetectDetectionFilterTestParse04);
570 UtRegisterTest("DetectDetectionFilterTestParse05", DetectDetectionFilterTestParse05);
571 UtRegisterTest("DetectDetectionFilterTestParse06", DetectDetectionFilterTestParse06);
572 UtRegisterTest("DetectDetectionFilterTestSig1", DetectDetectionFilterTestSig1);
573 UtRegisterTest("DetectDetectionFilterTestSig2", DetectDetectionFilterTestSig2);
574 UtRegisterTest("DetectDetectionFilterTestSig3", DetectDetectionFilterTestSig3);
575}
576#endif /* UNITTESTS */
#define ACTION_DROP
#define TRACK_SRC
#define TRACK_DST
void DetectDetectionFilterRegister(void)
Registration function for detection_filter: keyword.
#define PARSE_REGEX
Regex for parsing our detection_filter options.
int PacketAlertCheck(Packet *p, uint32_t sid)
Check if a certain sid alerted, this is used in the test functions.
int SigGroupBuild(DetectEngineCtx *de_ctx)
Convert the signature list into the runtime match structure.
@ DETECT_THRESHOLD
@ DETECT_DETECTION_FILTER
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
TmEcode DetectEngineThreadCtxDeinit(ThreadVars *tv, void *data)
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 * SCSigMatchAppendSMToList(DetectEngineCtx *de_ctx, Signature *s, uint16_t type, SigMatchCtx *ctx, const int list)
Append a SigMatch to the list type.
SigMatch * DetectGetLastSMFromLists(const Signature *s,...)
Returns the sm with the largest index (added latest) from the lists passed to us.
SigTableElmt * sigmatch_table
#define TYPE_DETECTION
#define TRACK_FLOW
void SigMatchSignatures(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p)
wrapper for old tests
Definition detect.c:2420
#define SIGMATCH_IPONLY_COMPAT
Definition detect.h:1653
#define DE_QUIET
Definition detect.h:330
@ DETECT_SM_LIST_THRESHOLD
Definition detect.h:133
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.
void ThresholdInit(void)
void ThresholdDestroy(void)
struct Thresholds ctx
main detection engine ctx
Definition detect.h:932
uint8_t flags
Definition detect.h:934
SCTime_t ts
Definition decode.h:555
uint8_t action
Definition decode.h:609
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition detect.h:351
a single match condition for a signature
Definition detect.h:356
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
Signature container.
Definition detect.h:668
Per thread variable structure.
Definition threadvars.h:58
int StringParseUint32(uint32_t *res, int base, size_t len, const char *str)
Definition util-byte.c:313
#define SCEnter(...)
Definition util-debug.h:277
#define SCReturnInt(x)
Definition util-debug.h:281
#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 SCStrdup(s)
Definition util-mem.h:56
#define unlikely(expr)
SCTime_t TimeGet(void)
Definition util-time.c:152
void TimeSetIncrementTime(uint32_t tv_sec)
increment the time in the engine
Definition util-time.c:180
void UTHFreePackets(Packet **p, int numpkts)
UTHFreePackets: function to release the allocated data from UTHBuildPacket and the packet itself.
Packet * UTHBuildPacketReal(uint8_t *payload, uint16_t payload_len, uint8_t ipproto, const char *src, const char *dst, uint16_t sport, uint16_t dport)
UTHBuildPacketReal is a function that create tcp/udp packets for unittests specifying ip and port sou...