suricata
detect-dataset.c
Go to the documentation of this file.
1 /* Copyright (C) 2018-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
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 #include "util-misc.h"
41 #include "util-path.h"
42 #include "util-conf.h"
43 #include "util-validate.h"
44 
45 #define DETECT_DATASET_CMD_SET 0
46 #define DETECT_DATASET_CMD_UNSET 1
47 #define DETECT_DATASET_CMD_ISNOTSET 2
48 #define DETECT_DATASET_CMD_ISSET 3
49 
51  const Signature *, const SigMatchCtx *);
52 static int DetectDatasetSetup (DetectEngineCtx *, Signature *, const char *);
53 void DetectDatasetFree (DetectEngineCtx *, void *);
54 
56 {
57  sigmatch_table[DETECT_DATASET].name = "dataset";
58  sigmatch_table[DETECT_DATASET].desc = "match sticky buffer against datasets (experimental)";
59  sigmatch_table[DETECT_DATASET].url = "/rules/dataset-keywords.html#dataset";
60  sigmatch_table[DETECT_DATASET].Setup = DetectDatasetSetup;
62 }
63 
64 /*
65  1 match
66  0 no match
67  -1 can't match
68  */
70  const DetectDatasetData *sd,
71  const uint8_t *data, const uint32_t data_len)
72 {
73  if (data == NULL || data_len == 0)
74  return 0;
75 
76  switch (sd->cmd) {
78  //PrintRawDataFp(stdout, data, data_len);
79  int r = DatasetLookup(sd->set, data, data_len);
80  SCLogDebug("r %d", r);
81  if (r == 1)
82  return 1;
83  break;
84  }
86  //PrintRawDataFp(stdout, data, data_len);
87  int r = DatasetLookup(sd->set, data, data_len);
88  SCLogDebug("r %d", r);
89  if (r < 1)
90  return 1;
91  break;
92  }
94  //PrintRawDataFp(stdout, data, data_len);
95  int r = DatasetAdd(sd->set, data, data_len);
96  if (r == 1)
97  return 1;
98  break;
99  }
101  int r = DatasetRemove(sd->set, data, data_len);
102  if (r == 1)
103  return 1;
104  break;
105  }
106  default:
107  DEBUG_VALIDATE_BUG_ON("unknown dataset command");
108  }
109  return 0;
110 }
111 
112 static int DetectDatasetParse(const char *str, char *cmd, int cmd_len, char *name, int name_len,
113  enum DatasetTypes *type, char *load, size_t load_size, char *save, size_t save_size,
114  uint64_t *memcap, uint32_t *hashsize)
115 {
116  bool cmd_set = false;
117  bool name_set = false;
118  bool load_set = false;
119  bool save_set = false;
120  bool state_set = false;
121 
122  char copy[strlen(str)+1];
123  strlcpy(copy, str, sizeof(copy));
124  char *xsaveptr = NULL;
125  char *key = strtok_r(copy, ",", &xsaveptr);
126  while (key != NULL) {
127  while (*key != '\0' && isblank(*key)) {
128  key++;
129  }
130  char *val = strchr(key, ' ');
131  if (val != NULL) {
132  *val++ = '\0';
133  while (*val != '\0' && isblank(*val)) {
134  val++;
135  SCLogDebug("cmd %s val %s", key, val);
136  }
137  } else {
138  SCLogDebug("cmd %s", key);
139  }
140 
141  if (strlen(key) == 0) {
142  goto next;
143  }
144 
145  if (!cmd_set) {
146  if (val && strlen(val) != 0) {
147  return -1;
148  }
149  strlcpy(cmd, key, cmd_len);
150  cmd_set = true;
151  } else if (!name_set) {
152  if (val && strlen(val) != 0) {
153  return -1;
154  }
155  strlcpy(name, key, name_len);
156  name_set = true;
157  } else {
158  if (val == NULL) {
159  return -1;
160  }
161 
162  if (strcmp(key, "type") == 0) {
163  SCLogDebug("type %s", val);
164 
165  if (strcmp(val, "md5") == 0) {
167  } else if (strcmp(val, "sha256") == 0) {
169  } else if (strcmp(val, "string") == 0) {
171  } else if (strcmp(val, "ipv4") == 0) {
173  } else if (strcmp(val, "ipv6") == 0) {
175  } else if (strcmp(val, "ip") == 0) {
177  } else {
178  SCLogError("bad type %s", val);
179  return -1;
180  }
181 
182  } else if (strcmp(key, "save") == 0) {
183  if (save_set) {
184  SCLogWarning("'save' can only appear once");
185  return -1;
186  }
187  SCLogDebug("save %s", val);
188  strlcpy(save, val, save_size);
189  save_set = true;
190  } else if (strcmp(key, "load") == 0) {
191  if (load_set) {
192  SCLogWarning("'load' can only appear once");
193  return -1;
194  }
195  SCLogDebug("load %s", val);
196  strlcpy(load, val, load_size);
197  load_set = true;
198  } else if (strcmp(key, "state") == 0) {
199  if (state_set) {
200  SCLogWarning("'state' can only appear once");
201  return -1;
202  }
203  SCLogDebug("state %s", val);
204  strlcpy(load, val, load_size);
205  strlcpy(save, val, save_size);
206  state_set = true;
207  }
208  if (strcmp(key, "memcap") == 0) {
209  if (ParseSizeStringU64(val, memcap) < 0) {
210  SCLogWarning("invalid value for memcap: %s,"
211  " resetting to default",
212  val);
213  *memcap = 0;
214  }
215  }
216  if (strcmp(key, "hashsize") == 0) {
217  if (ParseSizeStringU32(val, hashsize) < 0) {
218  SCLogWarning("invalid value for hashsize: %s,"
219  " resetting to default",
220  val);
221  *hashsize = 0;
222  }
223  }
224  }
225 
226  SCLogDebug("key: %s, value: %s", key, val);
227 
228  next:
229  key = strtok_r(NULL, ",", &xsaveptr);
230  }
231 
232  if ((load_set || save_set) && state_set) {
233  SCLogWarning("'state' can not be mixed with 'load' and 'save'");
234  return -1;
235  }
236 
237  /* Trim trailing whitespace. */
238  while (strlen(name) > 0 && isblank(name[strlen(name) - 1])) {
239  name[strlen(name) - 1] = '\0';
240  }
241 
242  /* Validate name, spaces are not allowed. */
243  for (size_t i = 0; i < strlen(name); i++) {
244  if (isblank(name[i])) {
245  SCLogError("spaces not allowed in dataset names");
246  return 0;
247  }
248  }
249 
250  return 1;
251 }
252 
253 /** \brief wrapper around dirname that does leave input untouched */
254 static void GetDirName(const char *in, char *out, size_t outs)
255 {
256  if (strlen(in) == 0) {
257  return;
258  }
259 
260  size_t size = strlen(in) + 1;
261  char tmp[size];
262  strlcpy(tmp, in, size);
263 
264  char *dir = dirname(tmp);
265  BUG_ON(dir == NULL);
266  strlcpy(out, dir, outs);
267 }
268 
269 static int SetupLoadPath(const DetectEngineCtx *de_ctx,
270  char *load, size_t load_size)
271 {
272  SCLogDebug("load %s", load);
273 
274  if (PathIsAbsolute(load)) {
275  return 0;
276  }
277 
278  bool done = false;
279 #ifdef HAVE_LIBGEN_H
280  BUG_ON(de_ctx->rule_file == NULL);
281 
282  char dir[PATH_MAX] = "";
283  GetDirName(de_ctx->rule_file, dir, sizeof(dir));
284 
285  SCLogDebug("rule_file %s dir %s", de_ctx->rule_file, dir);
286  char path[PATH_MAX];
287  if (snprintf(path, sizeof(path), "%s/%s", dir, load) >= (int)sizeof(path)) // TODO windows path
288  return -1;
289 
290  if (SCPathExists(path)) {
291  done = true;
292  strlcpy(load, path, load_size);
293  SCLogDebug("using path '%s' (HAVE_LIBGEN_H)", load);
294  }
295 #endif
296  if (!done) {
297  char *loadp = DetectLoadCompleteSigPath(de_ctx, load);
298  if (loadp == NULL) {
299  return -1;
300  }
301  SCLogDebug("loadp %s", loadp);
302 
303  if (SCPathExists(loadp)) {
304  strlcpy(load, loadp, load_size);
305  SCLogDebug("using path '%s' (non-HAVE_LIBGEN_H)", load);
306  }
307  SCFree(loadp);
308  }
309  return 0;
310 }
311 
312 static int SetupSavePath(const DetectEngineCtx *de_ctx,
313  char *save, size_t save_size)
314 {
315  SCLogDebug("save %s", save);
316 
317  int allow_save = 1;
318  if (ConfGetBool("datasets.rules.allow-write", &allow_save)) {
319  if (!allow_save) {
320  SCLogError("Rules containing save/state datasets have been disabled");
321  return -1;
322  }
323  }
324 
325  int allow_absolute = 0;
326  (void)ConfGetBool("datasets.rules.allow-absolute-filenames", &allow_absolute);
327  if (allow_absolute) {
328  SCLogNotice("Allowing absolute filename for dataset rule: %s", save);
329  } else {
330  if (PathIsAbsolute(save)) {
331  SCLogError("Absolute paths not allowed: %s", save);
332  return -1;
333  }
334 
335  if (SCPathContainsTraversal(save)) {
336  SCLogError("Directory traversals not allowed: %s", save);
337  return -1;
338  }
339  }
340 
341  // data dir
342  const char *dir = ConfigGetDataDirectory();
343  BUG_ON(dir == NULL); // should not be able to fail
344  char path[PATH_MAX];
345  if (snprintf(path, sizeof(path), "%s/%s", dir, save) >= (int)sizeof(path)) // TODO windows path
346  return -1;
347 
348  /* TODO check if location exists and is writable */
349 
350  strlcpy(save, path, save_size);
351 
352  return 0;
353 }
354 
355 int DetectDatasetSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
356 {
357  DetectDatasetData *cd = NULL;
358  uint8_t cmd = 0;
359  uint64_t memcap = 0;
360  uint32_t hashsize = 0;
361  char cmd_str[16] = "", name[DATASET_NAME_MAX_LEN + 1] = "";
363  char load[PATH_MAX] = "";
364  char save[PATH_MAX] = "";
365 
366  if (DetectBufferGetActiveList(de_ctx, s) == -1) {
367  SCLogError("datasets are only supported for sticky buffers");
368  SCReturnInt(-1);
369  }
370 
371  int list = s->init_data->list;
372  if (list == DETECT_SM_LIST_NOTSET) {
373  SCLogError("datasets are only supported for sticky buffers");
374  SCReturnInt(-1);
375  }
376 
377  if (!DetectDatasetParse(rawstr, cmd_str, sizeof(cmd_str), name, sizeof(name), &type, load,
378  sizeof(load), save, sizeof(save), &memcap, &hashsize)) {
379  return -1;
380  }
381 
382  if (strcmp(cmd_str,"isset") == 0) {
384  } else if (strcmp(cmd_str,"isnotset") == 0) {
386  } else if (strcmp(cmd_str,"set") == 0) {
388  } else if (strcmp(cmd_str,"unset") == 0) {
390  } else {
391  SCLogError("dataset action \"%s\" is not supported.", cmd_str);
392  return -1;
393  }
394 
395  /* if just 'load' is set, we load data from the same dir as the
396  * rule file. If load+save is used, we use data dir */
397  if (strlen(save) == 0 && strlen(load) != 0) {
398  if (SetupLoadPath(de_ctx, load, sizeof(load)) != 0)
399  return -1;
400  /* if just 'save' is set, we use either full path or the
401  * data-dir */
402  } else if (strlen(save) != 0 && strlen(load) == 0) {
403  if (SetupSavePath(de_ctx, save, sizeof(save)) != 0)
404  return -1;
405  /* use 'save' logic for 'state', but put the resulting
406  * path into 'load' as well. */
407  } else if (strlen(save) != 0 && strlen(load) != 0 &&
408  strcmp(save, load) == 0) {
409  if (SetupSavePath(de_ctx, save, sizeof(save)) != 0)
410  return -1;
411  strlcpy(load, save, sizeof(load));
412  }
413 
414  SCLogDebug("name '%s' load '%s' save '%s'", name, load, save);
415  Dataset *set = DatasetGet(name, type, save, load, memcap, hashsize);
416  if (set == NULL) {
417  SCLogError("failed to set up dataset '%s'.", name);
418  return -1;
419  }
420 
421  cd = SCCalloc(1, sizeof(DetectDatasetData));
422  if (unlikely(cd == NULL))
423  goto error;
424 
425  cd->set = set;
426  cd->cmd = cmd;
427 
428  SCLogDebug("cmd %s, name %s",
429  cmd_str, strlen(name) ? name : "(none)");
430 
431  /* Okay so far so good, lets get this into a SigMatch
432  * and put it in the Signature. */
433 
434  if (SigMatchAppendSMToList(de_ctx, s, DETECT_DATASET, (SigMatchCtx *)cd, list) == NULL) {
435  goto error;
436  }
437  return 0;
438 
439 error:
440  if (cd != NULL)
441  SCFree(cd);
442  return -1;
443 }
444 
446 {
448  if (fd == NULL)
449  return;
450 
451  SCFree(fd);
452 }
DetectDatasetData_
Definition: detect-dataset.h:29
SigTableElmt_::url
const char * url
Definition: detect.h:1312
detect-engine.h
DetectDatasetData_::cmd
uint8_t cmd
Definition: detect-dataset.h:31
SigTableElmt_::desc
const char * desc
Definition: detect.h:1311
sigmatch_table
SigTableElmt * sigmatch_table
Definition: detect-parse.c:128
SigTableElmt_::Free
void(* Free)(DetectEngineCtx *, void *)
Definition: detect.h:1299
SigTableElmt_::name
const char * name
Definition: detect.h:1309
ConfGetBool
int ConfGetBool(const char *name, int *val)
Retrieve a configuration value as a boolean.
Definition: conf.c:482
DetectEngineCtx_::rule_file
char * rule_file
Definition: detect.h:938
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
DETECT_DATASET_CMD_SET
#define DETECT_DATASET_CMD_SET
Definition: detect-dataset.c:45
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
ParseSizeStringU64
int ParseSizeStringU64(const char *size, uint64_t *res)
Definition: util-misc.c:190
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
threads.h
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:843
DATASET_TYPE_SHA256
@ DATASET_TYPE_SHA256
Definition: datasets.h:34
DetectDatasetData_::set
Dataset * set
Definition: detect-dataset.h:30
ConfigGetDataDirectory
const char * ConfigGetDataDirectory(void)
Definition: util-conf.c:80
DetectBufferGetActiveList
int DetectBufferGetActiveList(DetectEngineCtx *de_ctx, Signature *s)
Definition: detect-engine.c:1425
SigTableElmt_::Setup
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1294
DATASET_TYPE_IPV6
@ DATASET_TYPE_IPV6
Definition: datasets.h:36
DETECT_DATASET_CMD_ISSET
#define DETECT_DATASET_CMD_ISSET
Definition: detect-dataset.c:48
strlcpy
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
DatasetGet
Dataset * DatasetGet(const char *name, enum DatasetTypes type, const char *save, const char *load, uint64_t memcap, uint32_t hashsize)
Definition: datasets.c:630
SCPathContainsTraversal
bool SCPathContainsTraversal(const char *path)
Check for directory traversal.
Definition: util-path.c:307
DatasetRemove
int DatasetRemove(Dataset *set, const uint8_t *data, const uint32_t data_len)
Definition: datasets.c:1755
datasets.h
decode.h
util-debug.h
DatasetAdd
int DatasetAdd(Dataset *set, const uint8_t *data, const uint32_t data_len)
Definition: datasets.c:1555
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:17
DetectEngineThreadCtx_
Definition: detect.h:1098
DetectDatasetFree
void DetectDatasetFree(DetectEngineCtx *, void *)
Definition: detect-dataset.c:445
DATASET_TYPE_NOTSET
#define DATASET_TYPE_NOTSET
Definition: datasets.h:31
SignatureInitData_::list
int list
Definition: detect.h:572
util-print.h
detect-engine-mpm.h
detect.h
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:58
SCLogWarning
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition: util-debug.h:249
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:300
DetectDatasetRegister
void DetectDatasetRegister(void)
Definition: detect-dataset.c:55
Packet_
Definition: decode.h:476
type
uint16_t type
Definition: decode-vlan.c:107
Signature_::init_data
SignatureInitData * init_data
Definition: detect.h:672
DatasetLookup
int DatasetLookup(Dataset *set, const uint8_t *data, const uint32_t data_len)
see if data is part of the set
Definition: datasets.c:1320
DATASET_TYPE_IPV4
@ DATASET_TYPE_IPV4
Definition: datasets.h:35
detect-engine-state.h
Data structures and function prototypes for keeping state for the detection engine.
SigMatchCtx_
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition: detect.h:344
DETECT_SM_LIST_NOTSET
#define DETECT_SM_LIST_NOTSET
Definition: detect.h:141
SCPathExists
bool SCPathExists(const char *path)
Check if a path exists.
Definition: util-path.c:219
DetectDatasetBufferMatch
int DetectDatasetBufferMatch(DetectEngineThreadCtx *det_ctx, const DetectDatasetData *sd, const uint8_t *data, const uint32_t data_len)
Definition: detect-dataset.c:69
DatasetTypes
DatasetTypes
Definition: datasets.h:30
util-conf.h
suricata-common.h
util-path.h
DATASET_NAME_MAX_LEN
#define DATASET_NAME_MAX_LEN
Definition: datasets.h:39
DETECT_DATASET_CMD_UNSET
#define DETECT_DATASET_CMD_UNSET
Definition: detect-dataset.c:46
PathIsAbsolute
int PathIsAbsolute(const char *path)
Check if a path is absolute.
Definition: util-path.c:44
detect-dataset.h
hashsize
#define hashsize(n)
Definition: util-hash-lookup3.h:40
ParseSizeStringU32
int ParseSizeStringU32(const char *size, uint32_t *res)
Definition: util-misc.c:173
util-validate.h
str
#define str(s)
Definition: suricata-common.h:291
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:261
SCFree
#define SCFree(p)
Definition: util-mem.h:61
detect-parse.h
Signature_
Signature container.
Definition: detect.h:603
DETECT_DATASET_CMD_ISNOTSET
#define DETECT_DATASET_CMD_ISNOTSET
Definition: detect-dataset.c:47
DATASET_TYPE_MD5
@ DATASET_TYPE_MD5
Definition: datasets.h:33
DATASET_TYPE_STRING
@ DATASET_TYPE_STRING
Definition: datasets.h:32
DetectLoadCompleteSigPath
char * DetectLoadCompleteSigPath(const DetectEngineCtx *de_ctx, const char *sig_file)
Create the path if default-rule-path was specified.
Definition: detect-engine-loader.c:62
SigMatchAppendSMToList
SigMatch * SigMatchAppendSMToList(DetectEngineCtx *de_ctx, Signature *s, uint16_t type, SigMatchCtx *ctx, const int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:437
DetectDatasetMatch
int DetectDatasetMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Dataset
Definition: datasets.h:40
util-misc.h
SCLogNotice
#define SCLogNotice(...)
Macro used to log NOTICE messages.
Definition: util-debug.h:237
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCReturnInt
#define SCReturnInt(x)
Definition: util-debug.h:275
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:102
DETECT_DATASET
@ DETECT_DATASET
Definition: detect-engine-register.h:89