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