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