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 #ifdef UNITTESTS
76  sigmatch_table[DETECT_TAG].RegisterTests = DetectTagRegisterTests;
77 #endif
79 
80  DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
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  */
94 static 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.tv_sec;
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.tv_sec;
127  tde.metric = td->metric;
128  tde.count = td->count;
129 
130  SCLogDebug("Adding to or updating flow; first_ts %u count %u",
131  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  */
156 static DetectTagData *DetectTagParse(const char *tagstr)
157 {
158  DetectTagData td;
159  int ret = 0, res = 0;
160  size_t pcre2_len;
161  const char *str_ptr = NULL;
162 
163  ret = DetectParsePcreExec(&parse_regex, tagstr, 0, 0);
164  if (ret < 1) {
165  SCLogError(SC_ERR_PCRE_MATCH, "parse error, ret %" PRId32 ", string %s", ret, tagstr);
166  goto error;
167  }
168 
169  res = pcre2_substring_get_bynumber(parse_regex.match, 1, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
170  if (res < 0 || str_ptr == NULL) {
171  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "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(SC_ERR_INVALID_VALUE, "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(
194  parse_regex.match, 3, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
195  if (res < 0 || str_ptr == NULL) {
196  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_get_bynumber failed");
197  goto error;
198  }
199 
200  /* count */
201  if (StringParseUint32(&td.count, 10, strlen(str_ptr),
202  str_ptr) <= 0) {
203  SCLogError(SC_ERR_INVALID_VALUE, "Invalid argument for count. Must be a value in the range of 0 to %"PRIu32" (%s)", UINT32_MAX, tagstr);
204  goto error;
205  }
206 
207  pcre2_substring_free((PCRE2_UCHAR *)str_ptr);
208  str_ptr = NULL;
209 
210  res = pcre2_substring_get_bynumber(
211  parse_regex.match, 4, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
212  if (res < 0 || str_ptr == NULL) {
213  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "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 {
228  SCLogError(SC_ERR_INVALID_VALUE, "Invalid argument metric. Must be one of \"seconds\", \"packets\" or \"bytes\" (%s)", tagstr);
229  goto error;
230  }
231 
232  pcre2_substring_free((PCRE2_UCHAR *)str_ptr);
233  str_ptr = NULL;
234 
235  /* if specified, overwrite it */
236  if (ret == 7) {
237  res = pcre2_substring_get_bynumber(
238  parse_regex.match, 6, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
239  if (res < 0 || str_ptr == NULL) {
240  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_get_bynumber failed");
241  goto error;
242  }
243 
244  /* metric */
245  if (strcasecmp("src", str_ptr) == 0) {
247  } else if (strcasecmp("dst", str_ptr) == 0) {
249  } else {
250  SCLogError(SC_ERR_INVALID_VALUE, "Invalid argument direction. Must be one of \"src\" or \"dst\" (only valid for tag host type, not sessions) (%s)", tagstr);
251  goto error;
252  }
253 
254  if (td.type != DETECT_TAG_TYPE_HOST) {
255  SCLogWarning(SC_ERR_INVALID_VALUE, "Argument direction doesn't make sense for type \"session\" (%s [%"PRIu8"])", tagstr, td.type);
256  }
257 
258  pcre2_substring_free((PCRE2_UCHAR *)str_ptr);
259  str_ptr = NULL;
260  }
261  }
262 
263  DetectTagData *real_td = SCMalloc(sizeof(DetectTagData));
264  if (unlikely(real_td == NULL)) {
265  SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
266  goto error;
267  }
268 
269  memcpy(real_td, &td, sizeof(DetectTagData));
270  return real_td;
271 
272 error:
273  if (str_ptr != NULL)
274  pcre2_substring_free((PCRE2_UCHAR *)str_ptr);
275  return NULL;
276 }
277 
278 /**
279  * \brief this function is used to add the parsed tag data into the current signature
280  *
281  * \param de_ctx pointer to the Detection Engine Context
282  * \param s pointer to the Current Signature
283  * \param tagstr pointer to the user provided tag options
284  *
285  * \retval 0 on Success
286  * \retval -1 on Failure
287  */
288 int DetectTagSetup(DetectEngineCtx *de_ctx, Signature *s, const char *tagstr)
289 {
290  DetectTagData *td = DetectTagParse(tagstr);
291  if (td == NULL)
292  return -1;
293 
294  SigMatch *sm = SigMatchAlloc();
295  if (sm == NULL) {
297  return -1;
298  }
299 
300  sm->type = DETECT_TAG;
301  sm->ctx = (SigMatchCtx *)td;
302 
303  /* Append it to the list of tags */
305  return 0;
306 }
307 
308 /** \internal
309  * \brief this function will free memory associated with
310  * DetectTagDataEntry
311  *
312  * \param td pointer to DetectTagDataEntry
313  */
314 static void DetectTagDataEntryFree(void *ptr)
315 {
316  if (ptr != NULL) {
318  SCFree(dte);
319  }
320 }
321 
322 
323 /**
324  * \brief this function will free all the entries of a list
325  * DetectTagDataEntry
326  *
327  * \param td pointer to DetectTagDataEntryList
328  */
329 void DetectTagDataListFree(void *ptr)
330 {
331  if (ptr != NULL) {
332  DetectTagDataEntry *entry = ptr;
333 
334  while (entry != NULL) {
335  DetectTagDataEntry *next_entry = entry->next;
336  DetectTagDataEntryFree(entry);
337  (void) SC_ATOMIC_SUB(num_tags, 1);
338  entry = next_entry;
339  }
340  }
341 }
342 
343 /**
344  * \brief this function will free memory associated with DetectTagData
345  *
346  * \param td pointer to DetectTagData
347  */
349 {
350  DetectTagData *td = (DetectTagData *)ptr;
351  SCFree(td);
352 }
353 
354 #ifdef UNITTESTS
355 
356 /**
357  * \test DetectTagTestParse01 is a test to make sure that we return "something"
358  * when given valid tag opt
359  */
360 static int DetectTagTestParse01(void)
361 {
362  int result = 0;
363  DetectTagData *td = NULL;
364  td = DetectTagParse("session, 123, packets");
365  if (td != NULL && td->type == DETECT_TAG_TYPE_SESSION
366  && td->count == 123
367  && td->metric == DETECT_TAG_METRIC_PACKET) {
368  DetectTagDataFree(NULL, td);
369  result = 1;
370  }
371 
372  return result;
373 }
374 
375 /**
376  * \test DetectTagTestParse02 is a test to check that we parse tag correctly
377  */
378 static int DetectTagTestParse02(void)
379 {
380  int result = 0;
381  DetectTagData *td = NULL;
382  td = DetectTagParse("host, 200, bytes, src");
383  if (td != NULL && td->type == DETECT_TAG_TYPE_HOST
384  && td->count == 200
386  && td->direction == DETECT_TAG_DIR_SRC) {
387  result = 1;
388  DetectTagDataFree(NULL, td);
389  }
390 
391  return result;
392 }
393 
394 /**
395  * \test DetectTagTestParse03 is a test for setting the stateless tag opt
396  */
397 static int DetectTagTestParse03(void)
398 {
399  int result = 0;
400  DetectTagData *td = NULL;
401  td = DetectTagParse("host, 200, bytes, dst");
402  if (td != NULL && td->type == DETECT_TAG_TYPE_HOST
403  && td->count == 200
405  && td->direction == DETECT_TAG_DIR_DST) {
406  result = 1;
407  DetectTagDataFree(NULL, td);
408  }
409 
410  return result;
411 }
412 
413 /**
414  * \test DetectTagTestParse04 is a test for default opts
415  */
416 static int DetectTagTestParse04(void)
417 {
418  int result = 0;
419  DetectTagData *td = NULL;
420  td = DetectTagParse("session");
421  if (td != NULL && td->type == DETECT_TAG_TYPE_SESSION
422  && td->count == DETECT_TAG_MAX_PKTS
423  && td->metric == DETECT_TAG_METRIC_PACKET) {
424  result = 1;
425  DetectTagDataFree(NULL, td);
426  }
427 
428  return result;
429 }
430 
431 /**
432  * \test DetectTagTestParse05 is a test for default opts
433  */
434 static int DetectTagTestParse05(void)
435 {
436  int result = 0;
437  DetectTagData *td = NULL;
438  td = DetectTagParse("host");
439  if (td != NULL && td->type == DETECT_TAG_TYPE_HOST
440  && td->count == DETECT_TAG_MAX_PKTS
442  && td->direction == DETECT_TAG_DIR_DST) {
443  result = 1;
444  DetectTagDataFree(NULL, td);
445  }
446 
447  return result;
448 }
449 
450 /**
451  * \brief this function registers unit tests for DetectTag
452  */
453 void DetectTagRegisterTests(void)
454 {
455  UtRegisterTest("DetectTagTestParse01", DetectTagTestParse01);
456  UtRegisterTest("DetectTagTestParse02", DetectTagTestParse02);
457  UtRegisterTest("DetectTagTestParse03", DetectTagTestParse03);
458  UtRegisterTest("DetectTagTestParse04", DetectTagTestParse04);
459  UtRegisterTest("DetectTagTestParse05", DetectTagTestParse05);
460 
462 }
463 #endif /* UNITTESTS */
util-byte.h
DetectParseRegex::match
pcre2_match_data * match
Definition: detect-parse.h:47
DETECT_TAG_METRIC_PACKET
@ DETECT_TAG_METRIC_PACKET
Definition: detect-tag.h:56
detect-engine.h
DetectParsePcreExec
int DetectParsePcreExec(DetectParseRegex *parse_regex, const char *str, int start_offset, int options)
Definition: detect-parse.c:2488
SC_ERR_INVALID_VALUE
@ SC_ERR_INVALID_VALUE
Definition: util-error.h:160
SigTableElmt_::Free
void(* Free)(DetectEngineCtx *, void *)
Definition: detect.h:1225
flow-util.h
DetectParseRegex
Definition: detect-parse.h:44
DetectTagDataEntry_::last_ts
uint32_t last_ts
Definition: detect-tag.h:85
SigTableElmt_::name
const char * name
Definition: detect.h:1235
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:296
DetectTagDataEntry_
Definition: detect-tag.h:71
DETECT_TAG_TYPE_SESSION
@ DETECT_TAG_TYPE_SESSION
Definition: detect-tag.h:44
threads.h
DETECT_TAG_METRIC_BYTES
@ DETECT_TAG_METRIC_BYTES
Definition: detect-tag.h:58
DetectTagDataEntry_::sid
uint32_t sid
Definition: detect-tag.h:78
SigTableElmt_::flags
uint16_t flags
Definition: detect.h:1229
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:784
DetectTagDataListFree
void DetectTagDataListFree(void *ptr)
this function will free all the entries of a list DetectTagDataEntry
Definition: detect-tag.c:329
DETECT_TAG_METRIC_SECONDS
@ DETECT_TAG_METRIC_SECONDS
Definition: detect-tag.h:57
DETECT_TAG_TYPE_HOST
@ DETECT_TAG_TYPE_HOST
Definition: detect-tag.h:45
TAG_ENTRY_FLAG_DIR_SRC
#define TAG_ENTRY_FLAG_DIR_SRC
Definition: detect-tag.h:93
SC_ERR_PCRE_GET_SUBSTRING
@ SC_ERR_PCRE_GET_SUBSTRING
Definition: util-error.h:34
DetectTagDataEntry_::metric
uint8_t metric
Definition: detect-tag.h:73
detect-tag.h
DetectTagData_::direction
uint8_t direction
Definition: detect-tag.h:65
SigTableElmt_::Setup
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1220
util-unittest.h
DetectTagDataEntry_::flags
uint8_t flags
Definition: detect-tag.h:72
util-unittest-helper.h
DetectTagDataEntry_::gid
uint32_t gid
Definition: detect-tag.h:79
Signature_::gid
uint32_t gid
Definition: detect.h:574
DetectTagDataEntry_::count
uint32_t count
Definition: detect-tag.h:77
decode.h
util-debug.h
SC_ERR_PCRE_MATCH
@ SC_ERR_PCRE_MATCH
Definition: util-error.h:32
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:17
TAG_ENTRY_FLAG_DIR_DST
#define TAG_ENTRY_FLAG_DIR_DST
Definition: detect-tag.h:94
DetectEngineThreadCtx_
Definition: detect.h:1024
DetectTagDataEntry_::first_ts
uint32_t first_ts
Definition: detect-tag.h:84
DetectSetupParseRegexes
void DetectSetupParseRegexes(const char *parse_str, DetectParseRegex *detect_parse)
Definition: detect-parse.c:2611
detect.h
DETECT_TAG_DIR_DST
@ DETECT_TAG_DIR_DST
Definition: detect-tag.h:51
StringParseUint32
int StringParseUint32(uint32_t *res, int base, size_t len, const char *str)
Definition: util-byte.c:313
util-time.h
app-layer-parser.h
SigMatch_::ctx
SigMatchCtx * ctx
Definition: detect.h:316
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:289
detect-engine-tag.h
SC_ATOMIC_SUB
#define SC_ATOMIC_SUB(name, val)
sub a value from our atomic variable
Definition: util-atomic.h:342
SC_ATOMIC_EXTERN
SC_ATOMIC_EXTERN(unsigned int, num_tags)
Packet_
Definition: decode.h:428
DETECT_TAG_DIR_SRC
@ DETECT_TAG_DIR_SRC
Definition: detect-tag.h:50
stream-tcp-private.h
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:1203
SigMatchAlloc
SigMatch * SigMatchAlloc(void)
Definition: detect-parse.c:238
SigMatchCtx_
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition: detect.h:308
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:465
Packet_::ts
struct timeval ts
Definition: decode.h:471
suricata-common.h
SigMatch_::type
uint16_t type
Definition: detect.h:314
sigmatch_table
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect-parse.c:76
SCLogError
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:255
DETECT_TAG
@ DETECT_TAG
Definition: detect-engine-register.h:60
DETECT_SM_LIST_TMATCH
@ DETECT_SM_LIST_TMATCH
Definition: detect.h:89
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
DetectTagData_::count
uint32_t count
Definition: detect-tag.h:66
DetectEngineTagRegisterTests
void DetectEngineTagRegisterTests(void)
this function registers unit tests for DetectTag
Definition: detect-engine-tag.c:1378
SCLogWarning
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:242
SCFree
#define SCFree(p)
Definition: util-mem.h:61
Signature_::id
uint32_t id
Definition: detect.h:573
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:124
detect-parse.h
Signature_
Signature container.
Definition: detect.h:539
SigMatch_
a single match condition for a signature
Definition: detect.h:313
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:181
DetectTagDataEntry_::next
struct DetectTagDataEntry_ * next
Definition: detect-tag.h:89
SC_ERR_MEM_ALLOC
@ SC_ERR_MEM_ALLOC
Definition: util-error.h:31
DetectTagData_
Definition: detect-tag.h:63
DetectTagData_::type
uint8_t type
Definition: detect-tag.h:64
PARSE_REGEX
#define PARSE_REGEX
Definition: detect-tag.c:54
DetectTagData_::metric
uint8_t metric
Definition: detect-tag.h:67
flow.h
flow-var.h
SIGMATCH_IPONLY_COMPAT
#define SIGMATCH_IPONLY_COMPAT
Definition: detect.h:1422
SigMatchAppendSMToList
void SigMatchAppendSMToList(Signature *s, SigMatch *new, int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:352
DetectTagDataFree
void DetectTagDataFree(DetectEngineCtx *, void *)
this function will free memory associated with DetectTagData
Definition: detect-tag.c:348
SigTableElmt_::RegisterTests
void(* RegisterTests)(void)
Definition: detect.h:1227