suricata
detect-datarep.c
Go to the documentation of this file.
1 /* Copyright (C) 2018-2019 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
20  *
21  * \author Victor Julien <victor@inliniac.net>
22  *
23  * Implements the datarep keyword
24  */
25 
26 #include "suricata-common.h"
27 #include "decode.h"
28 #include "detect.h"
29 #include "threads.h"
30 #include "datasets.h"
31 #include "detect-datarep.h"
32 
33 #include "detect-parse.h"
34 #include "detect-engine.h"
35 #include "detect-engine-mpm.h"
36 #include "detect-engine-state.h"
37 
38 #include "util-byte.h"
39 #include "util-debug.h"
40 #include "util-print.h"
41 
42 #define PARSE_REGEX "([a-z]+)(?:,\\s*([\\-_A-z0-9\\s\\.]+)){1,4}"
43 static pcre *parse_regex;
44 static pcre_extra *parse_regex_study;
45 
47  const Signature *, const SigMatchCtx *);
48 static int DetectDatarepSetup (DetectEngineCtx *, Signature *, const char *);
49 void DetectDatarepFree (void *);
50 
52 {
53  sigmatch_table[DETECT_DATAREP].name = "datarep";
54  sigmatch_table[DETECT_DATAREP].desc = "operate on datasets";
55  sigmatch_table[DETECT_DATAREP].url = DOC_URL DOC_VERSION "/rules/dataset-keywords.html#datarep";
56  sigmatch_table[DETECT_DATAREP].Setup = DetectDatarepSetup;
58 
59  DetectSetupParseRegexes(PARSE_REGEX, &parse_regex, &parse_regex_study);
60 }
61 
62 /*
63  1 match
64  0 no match
65  -1 can't match
66  */
68  const DetectDatarepData *sd,
69  const uint8_t *data, const uint32_t data_len)
70 {
71  if (data == NULL || data_len == 0)
72  return 0;
73 
74  DataRepResultType r = DatasetLookupwRep(sd->set, data, data_len, &sd->rep);
75  if (!r.found)
76  return 0;
77 
78  switch (sd->op) {
79  case DATAREP_OP_GT:
80  if (r.rep.value > sd->rep.value)
81  return 1;
82  break;
83  case DATAREP_OP_LT:
84  if (r.rep.value < sd->rep.value)
85  return 1;
86  break;
87  case DATAREP_OP_EQ:
88  if (r.rep.value == sd->rep.value)
89  return 1;
90  break;
91  }
92  return 0;
93 }
94 
95 static int DetectDatarepParse(const char *str,
96  char *cmd, int cmd_len,
97  char *name, int name_len,
98  enum DatasetTypes *type,
99  char *load, size_t load_size,
100  uint16_t *rep_value)
101 {
102  bool cmd_set = false;
103  bool name_set = false;
104  bool value_set = false;
105 
106  char copy[strlen(str)+1];
107  strlcpy(copy, str, sizeof(copy));
108  char *xsaveptr = NULL;
109  char *key = strtok_r(copy, ",", &xsaveptr);
110  while (key != NULL) {
111  while (*key != '\0' && isblank(*key)) {
112  key++;
113  }
114  char *val = strchr(key, ' ');
115  if (val != NULL) {
116  *val++ = '\0';
117  while (*val != '\0' && isblank(*val)) {
118  val++;
119  SCLogDebug("cmd %s val %s", key, val);
120  }
121  } else {
122  SCLogDebug("cmd %s", key);
123  }
124 
125  if (strlen(key) == 0) {
126  goto next;
127  }
128 
129  if (!name_set) {
130  if (val) {
131  return -1;
132  }
133  strlcpy(name, key, name_len);
134  name_set = true;
135  } else if (!cmd_set) {
136  if (val) {
137  return -1;
138  }
139  strlcpy(cmd, key, cmd_len);
140  cmd_set = true;
141  } else if (!value_set) {
142  if (val) {
143  return -1;
144  }
145 
146  if (ByteExtractStringUint16(rep_value, 10, 0, key) != (int)strlen(key))
147  return -1;
148 
149  value_set = true;
150  } else {
151  if (val == NULL) {
152  return -1;
153  }
154 
155  if (strcmp(key, "type") == 0) {
156  SCLogDebug("type %s", val);
157 
158  if (strcmp(val, "md5") == 0) {
160  } else if (strcmp(val, "sha256") == 0) {
162  } else if (strcmp(val, "string") == 0) {
164  } else {
165  SCLogDebug("bad type %s", val);
166  return -1;
167  }
168 
169  } else if (strcmp(key, "load") == 0) {
170  SCLogDebug("load %s", val);
171  strlcpy(load, val, load_size);
172  }
173  }
174 
175  SCLogDebug("key: %s, value: %s", key, val);
176 
177  next:
178  key = strtok_r(NULL, ",", &xsaveptr);
179  }
180 
181  if (strlen(load) > 0 && *type == DATASET_TYPE_NOTSET) {
183  "if load is used type must be set as well");
184  return 0;
185  }
186 
187  if (!name_set || !cmd_set || !value_set) {
189  "missing values");
190  return 0;
191  }
192 
193  /* Trim trailing whitespace. */
194  while (strlen(name) > 0 && isblank(name[strlen(name) - 1])) {
195  name[strlen(name) - 1] = '\0';
196  }
197 
198  /* Validate name, spaces are not allowed. */
199  for (size_t i = 0; i < strlen(name); i++) {
200  if (isblank(name[i])) {
202  "spaces not allowed in dataset names");
203  return 0;
204  }
205  }
206 
207  return 1;
208 }
209 
210 /** \brief wrapper around dirname that does leave input untouched */
211 static void GetDirName(const char *in, char *out, size_t outs)
212 {
213  if (strlen(in) == 0) {
214  return;
215  }
216 
217  size_t size = strlen(in) + 1;
218  char tmp[size];
219  strlcpy(tmp, in, size);
220 
221  char *dir = dirname(tmp);
222  BUG_ON(dir == NULL);
223  strlcpy(out, dir, outs);
224  return;
225 }
226 
227 static int SetupLoadPath(const DetectEngineCtx *de_ctx,
228  char *load, size_t load_size)
229 {
230  SCLogDebug("load %s", load);
231 
232  if (PathIsAbsolute(load)) {
233  return 0;
234  }
235 
236  bool done = false;
237 #ifdef HAVE_LIBGEN_H
238  BUG_ON(de_ctx->rule_file == NULL);
239 
240  char dir[PATH_MAX] = "";
241  GetDirName(de_ctx->rule_file, dir, sizeof(dir));
242 
243  SCLogDebug("rule_file %s dir %s", de_ctx->rule_file, dir);
244  char path[PATH_MAX];
245  if (snprintf(path, sizeof(path), "%s/%s", dir, load) >= (int)sizeof(path)) // TODO windows path
246  return -1;
247 
248  if (SCPathExists(path)) {
249  done = true;
250  strlcpy(load, path, load_size);
251  SCLogDebug("using path '%s' (HAVE_LIBGEN_H)", load);
252  } else {
253  SCLogDebug("path '%s' does not exist (HAVE_LIBGEN_H)", path);
254  }
255 #endif
256  if (!done) {
257  char *loadp = DetectLoadCompleteSigPath(de_ctx, load);
258  if (loadp == NULL) {
259  return -1;
260  }
261  SCLogDebug("loadp %s", loadp);
262 
263  if (SCPathExists(loadp)) {
264  strlcpy(load, loadp, load_size);
265  SCLogDebug("using path '%s' (non-HAVE_LIBGEN_H)", load);
266  } else {
267  SCLogDebug("path '%s' does not exist (non-HAVE_LIBGEN_H)", loadp);
268  }
269  SCFree(loadp);
270 
271  // TODO try data-dir as well?
272  }
273  return 0;
274 }
275 
276 static int DetectDatarepSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
277 {
278  SigMatch *sm = NULL;
279  char cmd_str[16] = "", name[64] = "";
281  char load[PATH_MAX];
282  uint16_t value = 0;
283 
284  if (DetectBufferGetActiveList(de_ctx, s) == -1) {
286  "datarep is only supported for sticky buffers");
287  SCReturnInt(-1);
288  }
289 
290  int list = s->init_data->list;
291  if (list == DETECT_SM_LIST_NOTSET) {
293  "datarep is only supported for sticky buffers");
294  SCReturnInt(-1);
295  }
296 
297  if (!DetectDatarepParse(rawstr, cmd_str, sizeof(cmd_str), name,
298  sizeof(name), &type, load, sizeof(load), &value)) {
299  return -1;
300  }
301 
302  if (strlen(load) != 0) {
303  if (SetupLoadPath(de_ctx, load, sizeof(load)) != 0)
304  return -1;
305  }
306 
307  enum DetectDatarepOp op;
308  if (strcmp(cmd_str,">") == 0) {
309  op = DATAREP_OP_GT;
310  } else if (strcmp(cmd_str,"<") == 0) {
311  op = DATAREP_OP_LT;
312  } else if (strcmp(cmd_str,"==") == 0) {
313  op = DATAREP_OP_EQ;
314  } else {
316  "datarep operation \"%s\" is not supported.", cmd_str);
317  return -1;
318  }
319 
320  Dataset *set = DatasetGet(name, type, /* no save */ NULL, load);
321  if (set == NULL) {
323  "failed to set up datarep set '%s'.", name);
324  return -1;
325  }
326 
327  DetectDatarepData *cd = SCCalloc(1, sizeof(DetectDatarepData));
328  if (unlikely(cd == NULL))
329  goto error;
330 
331  cd->set = set;
332  cd->op = op;
333  cd->rep.value = value;
334 
335  SCLogDebug("cmd %s, name %s",
336  cmd_str, strlen(name) ? name : "(none)");
337 
338  /* Okay so far so good, lets get this into a SigMatch
339  * and put it in the Signature. */
340  sm = SigMatchAlloc();
341  if (sm == NULL)
342  goto error;
343 
344  sm->type = DETECT_DATAREP;
345  sm->ctx = (SigMatchCtx *)cd;
346  SigMatchAppendSMToList(s, sm, list);
347  return 0;
348 
349 error:
350  if (cd != NULL)
351  SCFree(cd);
352  if (sm != NULL)
353  SCFree(sm);
354  return -1;
355 }
356 
357 void DetectDatarepFree (void *ptr)
358 {
360 
361  if (fd == NULL)
362  return;
363 
364  SCFree(fd);
365 }
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect.h:1439
SignatureInitData * init_data
Definition: detect.h:586
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1179
#define SCLogDebug(...)
Definition: util-debug.h:335
struct HtpBodyChunk_ * next
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
#define BUG_ON(x)
DatasetTypes
Definition: datasets.h:28
void DetectDatarepRegister(void)
#define unlikely(expr)
Definition: util-optimize.h:35
const char * name
Definition: detect.h:1193
DetectDatarepOp
Signature container.
Definition: detect.h:517
#define DATASET_TYPE_NOTSET
Definition: datasets.h:29
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition: detect.h:308
enum DetectDatarepOp op
bool SCPathExists(const char *path)
Check if a path exists.
Definition: util-path.c:132
main detection engine ctx
Definition: detect.h:756
Dataset * DatasetGet(const char *name, enum DatasetTypes type, const char *save, const char *load)
Definition: datasets.c:407
int ByteExtractStringUint16(uint16_t *res, int base, uint16_t len, const char *str)
Definition: util-byte.c:264
int DetectBufferGetActiveList(DetectEngineCtx *de_ctx, Signature *s)
#define str(s)
#define SCCalloc(nm, a)
Definition: util-mem.h:253
int DetectDatarepBufferMatch(DetectEngineThreadCtx *det_ctx, const DetectDatarepData *sd, const uint8_t *data, const uint32_t data_len)
char * rule_file
Definition: detect.h:860
Data structures and function prototypes for keeping state for the detection engine.
void(* Free)(void *)
Definition: detect.h:1184
uint8_t type
int DetectDatarepMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
void DetectSetupParseRegexes(const char *parse_str, pcre **parse_regex, pcre_extra **parse_regex_study)
#define DETECT_SM_LIST_NOTSET
Definition: detect.h:111
int PathIsAbsolute(const char *path)
Check if a path is absolute.
Definition: util-path.c:39
uint8_t type
Definition: detect.h:314
#define SCReturnInt(x)
Definition: util-debug.h:341
const char * desc
Definition: detect.h:1195
void SigMatchAppendSMToList(Signature *s, SigMatch *new, int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:288
char * DetectLoadCompleteSigPath(const DetectEngineCtx *de_ctx, const char *sig_file)
Create the path if default-rule-path was specified.
SigMatchCtx * ctx
Definition: detect.h:316
#define SCFree(a)
Definition: util-mem.h:322
void DetectDatarepFree(void *)
#define PARSE_REGEX
const char * url
Definition: detect.h:1196
#define DOC_URL
Definition: suricata.h:86
SigMatch * SigMatchAlloc(void)
Definition: detect-parse.c:232
DataRepResultType DatasetLookupwRep(Dataset *set, const uint8_t *data, const uint32_t data_len, const DataRepType *rep)
Definition: datasets.c:819
Per thread variable structure.
Definition: threadvars.h:57
#define DOC_VERSION
Definition: suricata.h:91
a single match condition for a signature
Definition: detect.h:313