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