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 = NULL;
287  SigMatch *sm = NULL;
288 
289  td = DetectTagParse(tagstr);
290  if (td == NULL) goto error;
291 
292  sm = SigMatchAlloc();
293  if (sm == NULL)
294  goto error;
295 
296  sm->type = DETECT_TAG;
297  sm->ctx = (SigMatchCtx *)td;
298 
299  /* Append it to the list of tags */
301 
302  return 0;
303 
304 error:
305  if (td != NULL) DetectTagDataFree(td);
306  if (sm != NULL) SCFree(sm);
307  return -1;
308 
309 }
310 
311 /** \internal
312  * \brief this function will free memory associated with
313  * DetectTagDataEntry
314  *
315  * \param td pointer to DetectTagDataEntry
316  */
317 static void DetectTagDataEntryFree(void *ptr)
318 {
319  if (ptr != NULL) {
321  SCFree(dte);
322  }
323 }
324 
325 
326 /**
327  * \brief this function will free all the entries of a list
328  * DetectTagDataEntry
329  *
330  * \param td pointer to DetectTagDataEntryList
331  */
332 void DetectTagDataListFree(void *ptr)
333 {
334  if (ptr != NULL) {
335  DetectTagDataEntry *entry = ptr;
336 
337  while (entry != NULL) {
338  DetectTagDataEntry *next_entry = entry->next;
339  DetectTagDataEntryFree(entry);
340  (void) SC_ATOMIC_SUB(num_tags, 1);
341  entry = next_entry;
342  }
343  }
344 }
345 
346 /**
347  * \brief this function will free memory associated with DetectTagData
348  *
349  * \param td pointer to DetectTagData
350  */
351 void DetectTagDataFree(void *ptr)
352 {
353  DetectTagData *td = (DetectTagData *)ptr;
354  SCFree(td);
355 }
356 
357 #ifdef UNITTESTS
358 
359 /**
360  * \test DetectTagTestParse01 is a test to make sure that we return "something"
361  * when given valid tag opt
362  */
363 static int DetectTagTestParse01(void)
364 {
365  int result = 0;
366  DetectTagData *td = NULL;
367  td = DetectTagParse("session, 123, packets");
368  if (td != NULL && td->type == DETECT_TAG_TYPE_SESSION
369  && td->count == 123
370  && td->metric == DETECT_TAG_METRIC_PACKET) {
371  DetectTagDataFree(td);
372  result = 1;
373  }
374 
375  return result;
376 }
377 
378 /**
379  * \test DetectTagTestParse02 is a test to check that we parse tag correctly
380  */
381 static int DetectTagTestParse02(void)
382 {
383  int result = 0;
384  DetectTagData *td = NULL;
385  td = DetectTagParse("host, 200, bytes, src");
386  if (td != NULL && td->type == DETECT_TAG_TYPE_HOST
387  && td->count == 200
389  && td->direction == DETECT_TAG_DIR_SRC) {
390  result = 1;
391  DetectTagDataFree(td);
392  }
393 
394  return result;
395 }
396 
397 /**
398  * \test DetectTagTestParse03 is a test for setting the stateless tag opt
399  */
400 static int DetectTagTestParse03(void)
401 {
402  int result = 0;
403  DetectTagData *td = NULL;
404  td = DetectTagParse("host, 200, bytes, dst");
405  if (td != NULL && td->type == DETECT_TAG_TYPE_HOST
406  && td->count == 200
408  && td->direction == DETECT_TAG_DIR_DST) {
409  result = 1;
410  DetectTagDataFree(td);
411  }
412 
413  return result;
414 }
415 
416 /**
417  * \test DetectTagTestParse04 is a test for default opts
418  */
419 static int DetectTagTestParse04(void)
420 {
421  int result = 0;
422  DetectTagData *td = NULL;
423  td = DetectTagParse("session");
424  if (td != NULL && td->type == DETECT_TAG_TYPE_SESSION
425  && td->count == DETECT_TAG_MAX_PKTS
426  && td->metric == DETECT_TAG_METRIC_PACKET) {
427  result = 1;
428  DetectTagDataFree(td);
429  }
430 
431  return result;
432 }
433 
434 /**
435  * \test DetectTagTestParse05 is a test for default opts
436  */
437 static int DetectTagTestParse05(void)
438 {
439  int result = 0;
440  DetectTagData *td = NULL;
441  td = DetectTagParse("host");
442  if (td != NULL && td->type == DETECT_TAG_TYPE_HOST
443  && td->count == DETECT_TAG_MAX_PKTS
445  && td->direction == DETECT_TAG_DIR_DST) {
446  result = 1;
447  DetectTagDataFree(td);
448  }
449 
450  return result;
451 }
452 
453 #endif /* UNITTESTS */
454 
455 /**
456  * \brief this function registers unit tests for DetectTag
457  */
459 {
460 #ifdef UNITTESTS
461  UtRegisterTest("DetectTagTestParse01", DetectTagTestParse01);
462  UtRegisterTest("DetectTagTestParse02", DetectTagTestParse02);
463  UtRegisterTest("DetectTagTestParse03", DetectTagTestParse03);
464  UtRegisterTest("DetectTagTestParse04", DetectTagTestParse04);
465  UtRegisterTest("DetectTagTestParse05", DetectTagTestParse05);
466 
468 #endif /* UNITTESTS */
469 }
uint8_t direction
Definition: detect-tag.h:67
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect.h:1403
uint32_t count
Definition: detect-tag.h:68
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1146
#define SCLogDebug(...)
Definition: util-debug.h:335
struct Flow_ * flow
Definition: decode.h:444
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:525
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:125
const char * name
Definition: detect.h:1160
Signature container.
Definition: detect.h:492
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition: detect.h:319
void DetectTagDataListFree(void *ptr)
this function will free all the entries of a list DetectTagDataEntry
Definition: detect-tag.c:332
void DetectTagDataFree(void *)
this function will free memory associated with DetectTagData
Definition: detect-tag.c:351
main detection engine ctx
Definition: detect.h:720
#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:1151
#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:526
uint8_t type
Definition: detect.h:325
#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:282
SigMatchCtx * ctx
Definition: detect.h:327
#define SCMalloc(a)
Definition: util-mem.h:174
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:236
PoolThreadReserved res
int(* Match)(ThreadVars *, DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition: detect.h:1129
#define PARSE_REGEX
Definition: detect-tag.c:55
#define SIGMATCH_IPONLY_COMPAT
Definition: detect.h:1330
void DetectTagRegisterTests(void)
this function registers unit tests for DetectTag
Definition: detect-tag.c:458
SigMatch * SigMatchAlloc(void)
Definition: detect-parse.c:226
Per thread variable structure.
Definition: threadvars.h:57
struct timeval ts
Definition: decode.h:450
uint8_t type
Definition: detect-tag.h:66
uint16_t flags
Definition: detect.h:1154
void(* RegisterTests)(void)
Definition: detect.h:1152
a single match condition for a signature
Definition: detect.h:324
struct DetectTagDataEntry_ * next
Definition: detect-tag.h:91