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