suricata
detect-dataset.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 dataset 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-dataset.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-debug.h"
39 #include "util-print.h"
40 
41 #define PARSE_REGEX "([a-z]+)(?:,\\s*([\\-_A-z0-9\\s\\.]+)){1,4}"
42 static pcre *parse_regex;
43 static pcre_extra *parse_regex_study;
44 
46  const Signature *, const SigMatchCtx *);
47 static int DetectDatasetSetup (DetectEngineCtx *, Signature *, const char *);
48 void DetectDatasetFree (void *);
49 
51 {
52  sigmatch_table[DETECT_DATASET].name = "dataset";
53  sigmatch_table[DETECT_DATASET].desc = "match sticky buffer against datasets";
54  sigmatch_table[DETECT_DATASET].url = DOC_URL DOC_VERSION "/rules/dataset-keywords.html#dataset";
55  sigmatch_table[DETECT_DATASET].Setup = DetectDatasetSetup;
57 
58  DetectSetupParseRegexes(PARSE_REGEX, &parse_regex, &parse_regex_study);
59 }
60 
61 /*
62  1 match
63  0 no match
64  -1 can't match
65  */
67  const DetectDatasetData *sd,
68  const uint8_t *data, const uint32_t data_len)
69 {
70  if (data == NULL || data_len == 0)
71  return 0;
72 
73  switch (sd->cmd) {
75  //PrintRawDataFp(stdout, data, data_len);
76  int r = DatasetLookup(sd->set, data, data_len);
77  SCLogDebug("r %d", r);
78  if (r == 1)
79  return 1;
80  break;
81  }
83  //PrintRawDataFp(stdout, data, data_len);
84  int r = DatasetLookup(sd->set, data, data_len);
85  SCLogDebug("r %d", r);
86  if (r == 0)
87  return 1;
88  break;
89  }
91  //PrintRawDataFp(stdout, data, data_len);
92  int r = DatasetAdd(sd->set, data, data_len);
93  if (r == 1)
94  return 1;
95  break;
96  }
97  default:
98  abort();
99  }
100  return 0;
101 }
102 
103 static int DetectDatasetParse(const char *str,
104  char *cmd, int cmd_len,
105  char *name, int name_len,
106  enum DatasetTypes *type,
107  char *load, size_t load_size,
108  char *save, size_t save_size)
109 {
110  bool cmd_set = false;
111  bool name_set = false;
112  bool load_set = false;
113  bool save_set = false;
114  bool state_set = false;
115 
116  char copy[strlen(str)+1];
117  strlcpy(copy, str, sizeof(copy));
118  char *xsaveptr = NULL;
119  char *key = strtok_r(copy, ",", &xsaveptr);
120  while (key != NULL) {
121  while (*key != '\0' && isblank(*key)) {
122  key++;
123  }
124  char *val = strchr(key, ' ');
125  if (val != NULL) {
126  *val++ = '\0';
127  while (*val != '\0' && isblank(*val)) {
128  val++;
129  SCLogDebug("cmd %s val %s", key, val);
130  }
131  } else {
132  SCLogDebug("cmd %s", key);
133  }
134 
135  if (strlen(key) == 0) {
136  goto next;
137  }
138 
139  if (!cmd_set) {
140  if (val) {
141  return -1;
142  }
143  strlcpy(cmd, key, cmd_len);
144  cmd_set = true;
145  } else if (!name_set) {
146  if (val) {
147  return -1;
148  }
149  strlcpy(name, key, name_len);
150  name_set = true;
151  } else {
152  if (val == NULL) {
153  return -1;
154  }
155 
156  if (strcmp(key, "type") == 0) {
157  SCLogDebug("type %s", val);
158 
159  if (strcmp(val, "md5") == 0) {
161  } else if (strcmp(val, "sha256") == 0) {
163  } else if (strcmp(val, "string") == 0) {
165  } else {
166  SCLogError(SC_ERR_INVALID_SIGNATURE, "bad type %s", val);
167  return -1;
168  }
169 
170  } else if (strcmp(key, "save") == 0) {
171  if (save_set) {
173  "'save' can only appear once");
174  return -1;
175  }
176  SCLogDebug("save %s", val);
177  strlcpy(save, val, save_size);
178  save_set = true;
179  } else if (strcmp(key, "load") == 0) {
180  if (load_set) {
182  "'load' can only appear once");
183  return -1;
184  }
185  SCLogDebug("load %s", val);
186  strlcpy(load, val, load_size);
187  load_set = true;
188  } else if (strcmp(key, "state") == 0) {
189  if (state_set) {
191  "'state' can only appear once");
192  return -1;
193  }
194  SCLogDebug("state %s", val);
195  strlcpy(load, val, load_size);
196  strlcpy(save, val, save_size);
197  state_set = true;
198  }
199  }
200 
201  SCLogDebug("key: %s, value: %s", key, val);
202 
203  next:
204  key = strtok_r(NULL, ",", &xsaveptr);
205  }
206 
207  if ((load_set || save_set) && state_set) {
209  "'state' can not be mixed with 'load' and 'save'");
210  return -1;
211  }
212 
213  /* Trim trailing whitespace. */
214  while (strlen(name) > 0 && isblank(name[strlen(name) - 1])) {
215  name[strlen(name) - 1] = '\0';
216  }
217 
218  /* Validate name, spaces are not allowed. */
219  for (size_t i = 0; i < strlen(name); i++) {
220  if (isblank(name[i])) {
222  "spaces not allowed in dataset names");
223  return 0;
224  }
225  }
226 
227  return 1;
228 }
229 
230 /** \brief wrapper around dirname that does leave input untouched */
231 static void GetDirName(const char *in, char *out, size_t outs)
232 {
233  if (strlen(in) == 0) {
234  return;
235  }
236 
237  size_t size = strlen(in) + 1;
238  char tmp[size];
239  strlcpy(tmp, in, size);
240 
241  char *dir = dirname(tmp);
242  BUG_ON(dir == NULL);
243  strlcpy(out, dir, outs);
244  return;
245 }
246 
247 static int SetupLoadPath(const DetectEngineCtx *de_ctx,
248  char *load, size_t load_size)
249 {
250  SCLogDebug("load %s", load);
251 
252  if (PathIsAbsolute(load)) {
253  return 0;
254  }
255 
256  bool done = false;
257 #ifdef HAVE_LIBGEN_H
258  BUG_ON(de_ctx->rule_file == NULL);
259 
260  char dir[PATH_MAX] = "";
261  GetDirName(de_ctx->rule_file, dir, sizeof(dir));
262 
263  SCLogDebug("rule_file %s dir %s", de_ctx->rule_file, dir);
264  char path[PATH_MAX];
265  if (snprintf(path, sizeof(path), "%s/%s", dir, load) >= (int)sizeof(path)) // TODO windows path
266  return -1;
267 
268  if (SCPathExists(load)) {
269  done = true;
270  strlcpy(load, path, load_size);
271  SCLogDebug("using path '%s' (HAVE_LIBGEN_H)", load);
272  }
273 #endif
274  if (!done) {
275  char *loadp = DetectLoadCompleteSigPath(de_ctx, load);
276  if (loadp == NULL) {
277  return -1;
278  }
279  SCLogDebug("loadp %s", loadp);
280 
281  if (SCPathExists(loadp)) {
282  strlcpy(load, loadp, load_size);
283  SCLogDebug("using path '%s' (non-HAVE_LIBGEN_H)", load);
284  }
285  SCFree(loadp);
286  }
287  return 0;
288 }
289 
290 static int SetupSavePath(const DetectEngineCtx *de_ctx,
291  char *save, size_t save_size)
292 {
293  SCLogDebug("save %s", save);
294 
295  if (PathIsAbsolute(save)) {
296  return 0;
297  }
298 
299  // data dir
300  const char *dir = ConfigGetDataDirectory();
301  BUG_ON(dir == NULL); // should not be able to fail
302  char path[PATH_MAX];
303  if (snprintf(path, sizeof(path), "%s/%s", dir, save) >= (int)sizeof(path)) // TODO windows path
304  return -1;
305 
306  /* TODO check if location exists and is writable */
307 
308  strlcpy(save, path, save_size);
309 
310  return 0;
311 }
312 
313 int DetectDatasetSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
314 {
315  DetectDatasetData *cd = NULL;
316  SigMatch *sm = NULL;
317  uint8_t cmd = 0;
318  char cmd_str[16] = "", name[64] = "";
320  char load[PATH_MAX] = "";
321  char save[PATH_MAX] = "";
322 
323  if (DetectBufferGetActiveList(de_ctx, s) == -1) {
325  "datasets are only supported for sticky buffers");
326  SCReturnInt(-1);
327  }
328 
329  int list = s->init_data->list;
330  if (list == DETECT_SM_LIST_NOTSET) {
332  "datasets are only supported for sticky buffers");
333  SCReturnInt(-1);
334  }
335 
336  if (!DetectDatasetParse(rawstr, cmd_str, sizeof(cmd_str), name,
337  sizeof(name), &type, load, sizeof(load), save, sizeof(save))) {
338  return -1;
339  }
340 
341  if (strcmp(cmd_str,"isset") == 0) {
343  } else if (strcmp(cmd_str,"isnotset") == 0) {
345  } else if (strcmp(cmd_str,"set") == 0) {
347  } else if (strcmp(cmd_str,"unset") == 0) {
349  } else {
351  "dataset action \"%s\" is not supported.", cmd_str);
352  return -1;
353  }
354 
355  /* if just 'load' is set, we load data from the same dir as the
356  * rule file. If load+save is used, we use data dir */
357  if (strlen(save) == 0 && strlen(load) != 0) {
358  if (SetupLoadPath(de_ctx, load, sizeof(load)) != 0)
359  return -1;
360  /* if just 'save' is set, we use either full path or the
361  * data-dir */
362  } else if (strlen(save) != 0 && strlen(load) == 0) {
363  if (SetupSavePath(de_ctx, save, sizeof(save)) != 0)
364  return -1;
365  /* use 'save' logic for 'state', but put the resulting
366  * path into 'load' as well. */
367  } else if (strlen(save) != 0 && strlen(load) != 0 &&
368  strcmp(save, load) == 0) {
369  if (SetupSavePath(de_ctx, save, sizeof(save)) != 0)
370  return -1;
371  strlcpy(load, save, sizeof(load));
372  }
373 
374  SCLogDebug("name '%s' load '%s' save '%s'", name, load, save);
375  Dataset *set = DatasetGet(name, type, save, load);
376  if (set == NULL) {
378  "failed to set up dataset '%s'.", name);
379  return -1;
380  }
381 
382  cd = SCCalloc(1, sizeof(DetectDatasetData));
383  if (unlikely(cd == NULL))
384  goto error;
385 
386  cd->set = set;
387  cd->cmd = cmd;
388 
389  SCLogDebug("cmd %s, name %s",
390  cmd_str, strlen(name) ? name : "(none)");
391 
392  /* Okay so far so good, lets get this into a SigMatch
393  * and put it in the Signature. */
394  sm = SigMatchAlloc();
395  if (sm == NULL)
396  goto error;
397 
398  sm->type = DETECT_DATASET;
399  sm->ctx = (SigMatchCtx *)cd;
400  SigMatchAppendSMToList(s, sm, list);
401  return 0;
402 
403 error:
404  if (cd != NULL)
405  SCFree(cd);
406  if (sm != NULL)
407  SCFree(sm);
408  return -1;
409 }
410 
411 void DetectDatasetFree (void *ptr)
412 {
414  if (fd == NULL)
415  return;
416 
417  SCFree(fd);
418 }
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect.h:1439
SignatureInitData * init_data
Definition: detect.h:586
#define DETECT_DATASET_CMD_SET
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
#define unlikely(expr)
Definition: util-optimize.h:35
const char * name
Definition: detect.h:1193
int DatasetAdd(Dataset *set, const uint8_t *data, const uint32_t data_len)
Definition: datasets.c:952
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
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 DetectBufferGetActiveList(DetectEngineCtx *de_ctx, Signature *s)
#define str(s)
#define SCCalloc(nm, a)
Definition: util-mem.h:253
char * rule_file
Definition: detect.h:860
int DetectDatasetMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
const char * ConfigGetDataDirectory()
Definition: util-conf.c:83
Data structures and function prototypes for keeping state for the detection engine.
void(* Free)(void *)
Definition: detect.h:1184
uint8_t type
#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)
void DetectDatasetFree(void *)
#define DETECT_SM_LIST_NOTSET
Definition: detect.h:111
int DetectDatasetBufferMatch(DetectEngineThreadCtx *det_ctx, const DetectDatasetData *sd, const uint8_t *data, const uint32_t data_len)
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
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:281
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
int DatasetLookup(Dataset *set, const uint8_t *data, const uint32_t data_len)
Definition: datasets.c:803
#define SCFree(a)
Definition: util-mem.h:322
const char * url
Definition: detect.h:1196
#define DETECT_DATASET_CMD_ISSET
void DetectDatasetRegister(void)
#define DETECT_DATASET_CMD_ISNOTSET
#define DOC_URL
Definition: suricata.h:86
SigMatch * SigMatchAlloc(void)
Definition: detect-parse.c:232
Per thread variable structure.
Definition: threadvars.h:57
#define DETECT_DATASET_CMD_UNSET
#define DOC_VERSION
Definition: suricata.h:91
a single match condition for a signature
Definition: detect.h:313
#define PARSE_REGEX