suricata
detect-tag.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 detect-tag.c
20 *
21 * \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
22 * \author Victor Julien <victor@inliniac.net>
23 *
24 * Implements the tag keyword
25 *
26 */
27
28#include "suricata-common.h"
29#include "detect.h"
30#include "detect-parse.h"
31#include "detect-tag.h"
32#include "detect-engine-tag.h"
33#include "detect-engine.h"
34#include "detect-engine-state.h"
35#include "app-layer-parser.h"
36
37#include "decode.h"
38
39#include "flow.h"
40#include "flow-var.h"
41#include "flow-util.h"
42#include "stream-tcp-private.h"
43
44#include "util-time.h"
45#include "util-byte.h"
46#include "util-unittest.h"
48#include "util-debug.h"
49#include "threads.h"
50
51SC_ATOMIC_EXTERN(unsigned int, num_tags);
52
53/* format: tag: <type>, <count>, <metric>, [direction]; */
54#define PARSE_REGEX "^\\s*(host|session)\\s*(,\\s*(\\d+)\\s*,\\s*(packets|bytes|seconds)\\s*(,\\s*(src|dst))?\\s*)?$"
55
56static DetectParseRegex parse_regex;
57
58static int DetectTagMatch(DetectEngineThreadCtx *, Packet *,
59 const Signature *, const SigMatchCtx *);
60static int DetectTagSetup(DetectEngineCtx *, Signature *, const char *);
61#ifdef UNITTESTS
62static void DetectTagRegisterTests(void);
63#endif
65
66/**
67 * \brief Registration function for keyword tag
68 */
70{
72 sigmatch_table[DETECT_TAG].Match = DetectTagMatch;
73 sigmatch_table[DETECT_TAG].Setup = DetectTagSetup;
75#ifdef UNITTESTS
76 sigmatch_table[DETECT_TAG].RegisterTests = DetectTagRegisterTests;
77#endif
79
81}
82
83/**
84 * \brief This function is used to setup a tag for session/host
85 *
86 * \param t pointer to thread vars
87 * \param det_ctx pointer to the pattern matcher thread
88 * \param p pointer to the current packet
89 * \param m pointer to the sigmatch that we will cast into DetectTagData
90 *
91 * \retval 0 no match
92 * \retval 1 match
93 */
94static int DetectTagMatch(DetectEngineThreadCtx *det_ctx, Packet *p,
95 const Signature *s, const SigMatchCtx *ctx)
96{
97 const DetectTagData *td = (const DetectTagData *)ctx;
99 memset(&tde, 0, sizeof(DetectTagDataEntry));
100
101 switch (td->type) {
103#ifdef DEBUG
105#endif
106
107 tde.sid = s->id;
108 tde.gid = s->gid;
109 tde.last_ts = tde.first_ts = p->ts;
110 tde.metric = td->metric;
111 tde.count = td->count;
112 if (td->direction == DETECT_TAG_DIR_SRC)
114 else if (td->direction == DETECT_TAG_DIR_DST)
116
117 SCLogDebug("Tagging Host with sid %"PRIu32":%"PRIu32"", s->id, s->gid);
118 TagHashAddTag(&tde, p);
119 break;
121 if (p->flow != NULL) {
122 SCLogDebug("Setting up tag for flow");
123 /* If it already exists it will be updated */
124 tde.sid = s->id;
125 tde.gid = s->gid;
126 tde.last_ts = tde.first_ts = p->ts;
127 tde.metric = td->metric;
128 tde.count = td->count;
129
130 SCLogDebug("Adding to or updating flow; first_ts %" PRIu64 " count %u",
131 (uint64_t)SCTIME_SECS(tde.first_ts), tde.count);
132 TagFlowAdd(p, &tde);
133 } else {
134 SCLogDebug("No flow to append the session tag");
135 }
136 break;
137#ifdef DEBUG
138 default:
139 SCLogDebug("unknown type of a tag keyword (not session nor host)");
140 BUG_ON(1);
141 break;
142#endif
143 }
144
145 return 1;
146}
147
148/**
149 * \brief This function is used to parse tag options passed to tag keyword
150 *
151 * \param tagstr Pointer to the user provided tag options
152 *
153 * \retval td pointer to DetectTagData on success
154 * \retval NULL on failure
155 */
156static DetectTagData *DetectTagParse(const char *tagstr)
157{
158 DetectTagData td;
159 size_t pcre2_len;
160 const char *str_ptr = NULL;
161
162 pcre2_match_data *match = NULL;
163 int ret = DetectParsePcreExec(&parse_regex, &match, tagstr, 0, 0);
164 if (ret < 1) {
165 SCLogError("parse error, ret %" PRId32 ", string %s", ret, tagstr);
166 goto error;
167 }
168
169 int res = pcre2_substring_get_bynumber(match, 1, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
170 if (res < 0 || str_ptr == NULL) {
171 SCLogError("pcre2_substring_get_bynumber failed");
172 goto error;
173 }
174
175 /* Type */
176 if (strcasecmp("session", str_ptr) == 0) {
178 } else if (strcasecmp("host", str_ptr) == 0) {
180 } else {
181 SCLogError("Invalid argument type. Must be session or host (%s)", tagstr);
182 goto error;
183 }
184 pcre2_substring_free((PCRE2_UCHAR *)str_ptr);
185 str_ptr = NULL;
186
187 /* default tag is 256 packets from session or dst host */
191
192 if (ret > 4) {
193 res = pcre2_substring_get_bynumber(match, 3, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
194 if (res < 0 || str_ptr == NULL) {
195 SCLogError("pcre2_substring_get_bynumber failed");
196 goto error;
197 }
198
199 /* count */
200 if (StringParseUint32(&td.count, 10, strlen(str_ptr),
201 str_ptr) <= 0) {
202 SCLogError("Invalid argument for count. Must be a value in the range of 0 to %" PRIu32
203 " (%s)",
204 UINT32_MAX, tagstr);
205 goto error;
206 }
207
208 pcre2_substring_free((PCRE2_UCHAR *)str_ptr);
209 str_ptr = NULL;
210
211 res = pcre2_substring_get_bynumber(match, 4, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
212 if (res < 0 || str_ptr == NULL) {
213 SCLogError("pcre2_substring_get_bynumber failed");
214 goto error;
215 }
216
217 /* metric */
218 if (strcasecmp("packets", str_ptr) == 0) {
222 /* TODO: load DETECT_TAG_MAX_PKTS from config */
223 } else if (strcasecmp("seconds", str_ptr) == 0) {
225 } else if (strcasecmp("bytes", str_ptr) == 0) {
227 } else {
229 "Invalid argument metric. Must be one of \"seconds\", \"packets\" or \"bytes\" "
230 "(%s)",
231 tagstr);
232 goto error;
233 }
234
235 pcre2_substring_free((PCRE2_UCHAR *)str_ptr);
236 str_ptr = NULL;
237
238 /* if specified, overwrite it */
239 if (ret == 7) {
240 res = pcre2_substring_get_bynumber(match, 6, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
241 if (res < 0 || str_ptr == NULL) {
242 SCLogError("pcre2_substring_get_bynumber failed");
243 goto error;
244 }
245
246 /* metric */
247 if (strcasecmp("src", str_ptr) == 0) {
249 } else if (strcasecmp("dst", str_ptr) == 0) {
251 } else {
253 "Invalid argument direction. Must be one of \"src\" or \"dst\" (only valid "
254 "for tag host type, not sessions) (%s)",
255 tagstr);
256 goto error;
257 }
258
259 if (td.type != DETECT_TAG_TYPE_HOST) {
261 "Argument direction doesn't make sense for type \"session\" (%s [%" PRIu8
262 "])",
263 tagstr, td.type);
264 }
265
266 pcre2_substring_free((PCRE2_UCHAR *)str_ptr);
267 str_ptr = NULL;
268 }
269 }
270
271 DetectTagData *real_td = SCMalloc(sizeof(DetectTagData));
272 if (unlikely(real_td == NULL)) {
273 SCLogError("Error allocating memory");
274 goto error;
275 }
276
277 memcpy(real_td, &td, sizeof(DetectTagData));
278 pcre2_match_data_free(match);
279 return real_td;
280
281error:
282 if (match) {
283 pcre2_match_data_free(match);
284 }
285 if (str_ptr != NULL)
286 pcre2_substring_free((PCRE2_UCHAR *)str_ptr);
287 return NULL;
288}
289
290/**
291 * \brief this function is used to add the parsed tag data into the current signature
292 *
293 * \param de_ctx pointer to the Detection Engine Context
294 * \param s pointer to the Current Signature
295 * \param tagstr pointer to the user provided tag options
296 *
297 * \retval 0 on Success
298 * \retval -1 on Failure
299 */
300int DetectTagSetup(DetectEngineCtx *de_ctx, Signature *s, const char *tagstr)
301{
302 DetectTagData *td = DetectTagParse(tagstr);
303 if (td == NULL)
304 return -1;
305
306 /* Append it to the list of tags */
308 NULL) {
310 return -1;
311 }
312 return 0;
313}
314
315/** \internal
316 * \brief this function will free memory associated with
317 * DetectTagDataEntry
318 *
319 * \param td pointer to DetectTagDataEntry
320 */
321static void DetectTagDataEntryFree(void *ptr)
322{
323 if (ptr != NULL) {
325 SCFree(dte);
326 }
327}
328
329
330/**
331 * \brief this function will free all the entries of a list
332 * DetectTagDataEntry
333 *
334 * \param td pointer to DetectTagDataEntryList
335 */
337{
338 if (ptr != NULL) {
339 DetectTagDataEntry *entry = ptr;
340
341 while (entry != NULL) {
342 DetectTagDataEntry *next_entry = entry->next;
343 DetectTagDataEntryFree(entry);
344 (void) SC_ATOMIC_SUB(num_tags, 1);
345 entry = next_entry;
346 }
347 }
348}
349
350/**
351 * \brief this function will free memory associated with DetectTagData
352 *
353 * \param td pointer to DetectTagData
354 */
356{
357 DetectTagData *td = (DetectTagData *)ptr;
358 SCFree(td);
359}
360
361#ifdef UNITTESTS
362
363/**
364 * \test DetectTagTestParse01 is a test to make sure that we return "something"
365 * when given valid tag opt
366 */
367static int DetectTagTestParse01(void)
368{
369 int result = 0;
370 DetectTagData *td = NULL;
371 td = DetectTagParse("session, 123, packets");
372 if (td != NULL && td->type == DETECT_TAG_TYPE_SESSION
373 && td->count == 123
375 DetectTagDataFree(NULL, td);
376 result = 1;
377 }
378
379 return result;
380}
381
382/**
383 * \test DetectTagTestParse02 is a test to check that we parse tag correctly
384 */
385static int DetectTagTestParse02(void)
386{
387 int result = 0;
388 DetectTagData *td = NULL;
389 td = DetectTagParse("host, 200, bytes, src");
390 if (td != NULL && td->type == DETECT_TAG_TYPE_HOST
391 && td->count == 200
393 && td->direction == DETECT_TAG_DIR_SRC) {
394 result = 1;
395 DetectTagDataFree(NULL, td);
396 }
397
398 return result;
399}
400
401/**
402 * \test DetectTagTestParse03 is a test for setting the stateless tag opt
403 */
404static int DetectTagTestParse03(void)
405{
406 int result = 0;
407 DetectTagData *td = NULL;
408 td = DetectTagParse("host, 200, bytes, dst");
409 if (td != NULL && td->type == DETECT_TAG_TYPE_HOST
410 && td->count == 200
412 && td->direction == DETECT_TAG_DIR_DST) {
413 result = 1;
414 DetectTagDataFree(NULL, td);
415 }
416
417 return result;
418}
419
420/**
421 * \test DetectTagTestParse04 is a test for default opts
422 */
423static int DetectTagTestParse04(void)
424{
425 int result = 0;
426 DetectTagData *td = NULL;
427 td = DetectTagParse("session");
428 if (td != NULL && td->type == DETECT_TAG_TYPE_SESSION
429 && td->count == DETECT_TAG_MAX_PKTS
431 result = 1;
432 DetectTagDataFree(NULL, td);
433 }
434
435 return result;
436}
437
438/**
439 * \test DetectTagTestParse05 is a test for default opts
440 */
441static int DetectTagTestParse05(void)
442{
443 int result = 0;
444 DetectTagData *td = NULL;
445 td = DetectTagParse("host");
446 if (td != NULL && td->type == DETECT_TAG_TYPE_HOST
447 && td->count == DETECT_TAG_MAX_PKTS
449 && td->direction == DETECT_TAG_DIR_DST) {
450 result = 1;
451 DetectTagDataFree(NULL, td);
452 }
453
454 return result;
455}
456
457/**
458 * \brief this function registers unit tests for DetectTag
459 */
460void DetectTagRegisterTests(void)
461{
462 UtRegisterTest("DetectTagTestParse01", DetectTagTestParse01);
463 UtRegisterTest("DetectTagTestParse02", DetectTagTestParse02);
464 UtRegisterTest("DetectTagTestParse03", DetectTagTestParse03);
465 UtRegisterTest("DetectTagTestParse04", DetectTagTestParse04);
466 UtRegisterTest("DetectTagTestParse05", DetectTagTestParse05);
467
469}
470#endif /* UNITTESTS */
Data structures and function prototypes for keeping state for the detection engine.
int TagHashAddTag(DetectTagDataEntry *tde, Packet *p)
Add a tag entry for a host. If it already exist, update it.
int TagFlowAdd(Packet *p, DetectTagDataEntry *tde)
This function is used to add a tag to a session (type session) or update it if it's already installed...
void DetectEngineTagRegisterTests(void)
this function registers unit tests for DetectTag
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.
SigTableElmt * sigmatch_table
void DetectTagDataFree(DetectEngineCtx *, void *)
this function will free memory associated with DetectTagData
Definition detect-tag.c:355
void DetectTagDataListFree(void *ptr)
this function will free all the entries of a list DetectTagDataEntry
Definition detect-tag.c:336
void DetectTagRegister(void)
Registration function for keyword tag.
Definition detect-tag.c:69
#define PARSE_REGEX
Definition detect-tag.c:54
#define TAG_ENTRY_FLAG_DIR_DST
Definition detect-tag.h:89
@ DETECT_TAG_METRIC_PACKET
Definition detect-tag.h:55
@ DETECT_TAG_METRIC_BYTES
Definition detect-tag.h:57
@ DETECT_TAG_METRIC_SECONDS
Definition detect-tag.h:56
@ DETECT_TAG_TYPE_HOST
Definition detect-tag.h:45
@ DETECT_TAG_TYPE_SESSION
Definition detect-tag.h:44
#define DETECT_TAG_MAX_PKTS
Definition detect-tag.h:40
#define TAG_ENTRY_FLAG_DIR_SRC
Definition detect-tag.h:88
@ DETECT_TAG_DIR_SRC
Definition detect-tag.h:50
@ DETECT_TAG_DIR_DST
Definition detect-tag.h:51
#define SIGMATCH_IPONLY_COMPAT
Definition detect.h:1653
@ DETECT_SM_LIST_TMATCH
Definition detect.h:129
DetectEngineCtx * de_ctx
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
struct Thresholds ctx
main detection engine ctx
Definition detect.h:932
struct DetectTagDataEntry_ * next
Definition detect-tag.h:84
uint8_t direction
Definition detect-tag.h:63
uint32_t count
Definition detect-tag.h:64
uint8_t metric
Definition detect-tag.h:65
SCTime_t ts
Definition decode.h:555
struct Flow_ * flow
Definition decode.h:546
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition detect.h:351
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
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
uint32_t id
Definition detect.h:713
uint32_t gid
Definition detect.h:714
#define BUG_ON(x)
#define SC_ATOMIC_EXTERN(type, name)
wrapper for referencing an atomic variable declared on another file.
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
int StringParseUint32(uint32_t *res, int base, size_t len, const char *str)
Definition util-byte.c:313
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition util-debug.h:255
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
#define SCMalloc(sz)
Definition util-mem.h:47
#define SCFree(p)
Definition util-mem.h:61
#define unlikely(expr)
#define SCTIME_SECS(t)
Definition util-time.h:57