suricata
detect-metadata.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2017 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 metadata keyword support
24  *
25  * \todo Do we need to do anything more this is used in snort host attribute table
26  * It is also used for rule management.
27  */
28 
29 #include "suricata-common.h"
30 #include "detect.h"
31 #include "detect-parse.h"
32 #include "detect-engine.h"
33 #include "detect-metadata.h"
34 #include "util-hash-string.h"
35 #include "rust.h"
36 #include "util-validate.h"
37 
38 static int DetectMetadataSetup (DetectEngineCtx *, Signature *, const char *);
39 #ifdef UNITTESTS
40 static void DetectMetadataRegisterTests(void);
41 #endif
42 
44 {
45  sigmatch_table[DETECT_METADATA].name = "metadata";
46  sigmatch_table[DETECT_METADATA].desc = "used for logging";
47  sigmatch_table[DETECT_METADATA].url = "/rules/meta.html#metadata";
49  sigmatch_table[DETECT_METADATA].Setup = DetectMetadataSetup;
51 #ifdef UNITTESTS
52  sigmatch_table[DETECT_METADATA].RegisterTests = DetectMetadataRegisterTests;
53 #endif
54 }
55 
56 /**
57  * \brief Free a Metadata object
58  */
60 {
61  SCEnter();
62 
63  SCFree(mdata);
64 
65  SCReturn;
66 }
67 
69 {
71  return 0;
72 
74  if (de_ctx->metadata_table == NULL)
75  return -1;
76  return 0;
77 }
78 
80 {
83 }
84 
85 static const char *DetectMetadataHashAdd(DetectEngineCtx *de_ctx, const char *string)
86 {
87  const char *hstring = (char *)HashTableLookup(
88  de_ctx->metadata_table, (void *)string, (uint16_t)strlen(string));
89  if (hstring) {
90  return hstring;
91  }
92 
93  const char *astring = SCStrdup(string);
94  if (astring == NULL) {
95  return NULL;
96  }
97 
98  if (HashTableAdd(de_ctx->metadata_table, (void *)astring, (uint16_t)strlen(astring)) == 0) {
99  return (char *)HashTableLookup(
100  de_ctx->metadata_table, (void *)astring, (uint16_t)strlen(astring));
101  } else {
102  SCFree((void *)astring);
103  }
104  return NULL;
105 }
106 
107 static int SortHelper(const void *a, const void *b)
108 {
109  const DetectMetadata *ma = *(const DetectMetadata **)a;
110  const DetectMetadata *mb = *(const DetectMetadata **)b;
111  return strcasecmp(ma->key, mb->key);
112 }
113 
114 static char *CraftPreformattedJSON(const DetectMetadata *head)
115 {
116  int cnt = 0;
117  for (const DetectMetadata *m = head; m != NULL; m = m->next) {
118  cnt++;
119  }
120  if (cnt == 0)
121  return NULL;
122 
123  // a rule can not have that many metadata
125  const DetectMetadata *array[cnt];
126  int i = 0;
127  for (const DetectMetadata *m = head; m != NULL; m = m->next) {
128  array[i++] = m;
129  }
130  BUG_ON(i != cnt);
131  qsort(array, cnt, sizeof(DetectMetadata *), SortHelper);
132 
133  SCJsonBuilder *js = SCJbNewObject();
134  if (js == NULL)
135  return NULL;
136 
137  /* array is sorted by key, so we can create a jsonbuilder object
138  * with each key appearing just once with one or more values */
139  bool array_open = false;
140  for (int j = 0; j < cnt; j++) {
141  const DetectMetadata *m = array[j];
142  const DetectMetadata *nm = j + 1 < cnt ? array[j + 1] : NULL;
143  DEBUG_VALIDATE_BUG_ON(m == NULL); // for scan-build
144 
145  if (nm && strcasecmp(m->key, nm->key) == 0) {
146  if (!array_open) {
147  SCJbOpenArray(js, m->key);
148  array_open = true;
149  }
150  SCJbAppendString(js, m->value);
151  } else {
152  if (!array_open) {
153  SCJbOpenArray(js, m->key);
154  }
155  SCJbAppendString(js, m->value);
156  SCJbClose(js);
157  array_open = false;
158  }
159  }
160  SCJbClose(js);
161  /* we have a complete json builder. Now store it as a C string */
162  const size_t len = SCJbLen(js);
163 #define MD_STR "\"metadata\":"
164 #define MD_STR_LEN (sizeof(MD_STR) - 1)
165  char *str = SCMalloc(len + MD_STR_LEN + 1);
166  if (str == NULL) {
167  SCJbFree(js);
168  return NULL;
169  }
170  char *ptr = str;
171  memcpy(ptr, MD_STR, MD_STR_LEN);
172  ptr += MD_STR_LEN;
173  memcpy(ptr, SCJbPtr(js), len);
174  ptr += len;
175  *ptr = '\0';
176 #undef MD_STR
177 #undef MD_STR_LEN
178  SCJbFree(js);
179  return str;
180 }
181 
182 static int DetectMetadataParse(DetectEngineCtx *de_ctx, Signature *s, const char *metadatastr)
183 {
184  DetectMetadata *head = s->metadata ? s->metadata->list : NULL;
185  char copy[strlen(metadatastr)+1];
186  strlcpy(copy, metadatastr, sizeof(copy));
187  char *xsaveptr = NULL;
188  char *key = strtok_r(copy, ",", &xsaveptr);
189  while (key != NULL) {
190  while (*key != '\0' && isblank(*key)) {
191  key++;
192  }
193  char *val = strchr(key, ' ');
194  if (val != NULL) {
195  *val++ = '\0';
196  while (*val != '\0' && isblank(*val)) {
197  val++;
198  }
199  } else {
200  /* Skip metadata without a value. */
201  goto next;
202  }
203 
204  /* Also skip metadata if the key or value is empty. */
205  if (strlen(key) == 0 || strlen(val) == 0) {
206  goto next;
207  }
208 
209  const char *hkey = DetectMetadataHashAdd(de_ctx, key);
210  if (hkey == NULL) {
211  SCLogError("can't create metadata key");
212  continue;
213  }
214 
215  const char *hval = DetectMetadataHashAdd(de_ctx, val);
216  if (hval == NULL) {
217  SCLogError("can't create metadata value");
218  goto next;
219  }
220 
221  SCLogDebug("key: %s, value: %s", hkey, hval);
222 
223  DetectMetadata *dkv = SCMalloc(sizeof(DetectMetadata));
224  if (dkv == NULL) {
225  goto next;
226  }
227  dkv->key = hkey;
228  dkv->value = hval;
229  dkv->next = head;
230  head = dkv;
231 
232  next:
233  key = strtok_r(NULL, ",", &xsaveptr);
234  }
235  if (head != NULL) {
236  if (s->metadata == NULL) {
237  s->metadata = SCCalloc(1, sizeof(*s->metadata));
238  if (s->metadata == NULL) {
239  for (DetectMetadata *m = head; m != NULL; ) {
240  DetectMetadata *next_m = m->next;
242  m = next_m;
243  }
244  return -1;
245  }
246  }
247  s->metadata->list = head;
248  SCFree(s->metadata->json_str);
249  s->metadata->json_str = CraftPreformattedJSON(head);
250  }
251  return 0;
252 }
253 
254 static int DetectMetadataSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
255 {
257  DetectMetadataParse(de_ctx, s, rawstr);
258  }
259 
260  return 0;
261 }
262 
263 #ifdef UNITTESTS
264 
265 static int DetectMetadataParseTest01(void)
266 {
270 
272  "alert tcp any any -> any any "
273  "(metadata: toto 1; sid:1; rev:1;)");
274  FAIL_IF_NULL(sig);
275  FAIL_IF(sig->metadata);
276 
278  PASS;
279 }
280 
281 static int DetectMetadataParseTest02(void)
282 {
287  "alert tcp any any -> any any "
288  "(metadata: toto 1; "
289  "metadata: titi 2, jaivu gros_minet;"
290  "sid:1; rev:1;)");
291  FAIL_IF_NULL(sig);
292  FAIL_IF_NULL(sig->metadata);
293  FAIL_IF_NULL(sig->metadata->list);
294  FAIL_IF_NULL(sig->metadata->list->key);
295  FAIL_IF(strcmp("jaivu", sig->metadata->list->key));
296  FAIL_IF(strcmp("gros_minet", sig->metadata->list->value));
297  FAIL_IF_NULL(sig->metadata->list->next);
298  DetectMetadata *dm = sig->metadata->list->next;
299  FAIL_IF(strcmp("titi", dm->key));
300  dm = dm->next;
301  FAIL_IF_NULL(dm);
302  FAIL_IF(strcmp("toto", dm->key));
303  FAIL_IF_NOT(strcmp(sig->metadata->json_str,
304  "\"metadata\":{\"jaivu\":[\"gros_minet\"],\"titi\":[\"2\"],\"toto\":[\"1\"]}") == 0);
306  PASS;
307 }
308 
309 /**
310  * \brief this function registers unit tests for DetectCipService
311  */
312 static void DetectMetadataRegisterTests(void)
313 {
314  UtRegisterTest("DetectMetadataParseTest01", DetectMetadataParseTest01);
315  UtRegisterTest("DetectMetadataParseTest02", DetectMetadataParseTest02);
316 }
317 #endif /* UNITTESTS */
SCJsonBuilder
struct SCJsonBuilder SCJsonBuilder
Definition: detect-engine-helper.h:83
SigTableElmt_::url
const char * url
Definition: detect.h:1545
util-hash-string.h
len
uint8_t len
Definition: app-layer-dnp3.h:2
DetectMetadataHead::json_str
char * json_str
Definition: detect-metadata.h:40
DetectMetadataFree
void DetectMetadataFree(DetectMetadata *mdata)
Free a Metadata object.
Definition: detect-metadata.c:59
detect-engine.h
FAIL_IF_NULL
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
Definition: util-unittest.h:89
SigTableElmt_::desc
const char * desc
Definition: detect.h:1544
sigmatch_table
SigTableElmt * sigmatch_table
Definition: detect-parse.c:78
DetectMetadata_::value
const char * value
Definition: detect-metadata.h:34
SigTableElmt_::Free
void(* Free)(DetectEngineCtx *, void *)
Definition: detect.h:1529
DetectMetadata_::key
const char * key
Definition: detect-metadata.h:32
DetectEngineMustParseMetadata
int DetectEngineMustParseMetadata(void)
Definition: detect-engine.c:5293
SigTableElmt_::name
const char * name
Definition: detect.h:1542
UtRegisterTest
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Definition: util-unittest.c:101
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:282
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:995
DetectMetadataHashInit
int DetectMetadataHashInit(DetectEngineCtx *de_ctx)
Definition: detect-metadata.c:68
DetectEngineCtxFree
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Definition: detect-engine.c:2912
rust.h
m
SCMutex m
Definition: flow-hash.h:6
DetectEngineAppendSig
Signature * DetectEngineAppendSig(DetectEngineCtx *, const char *)
Parse and append a Signature into the Detection Engine Context signature list.
Definition: detect-parse.c:3971
DetectMetadataRegister
void DetectMetadataRegister(void)
Definition: detect-metadata.c:43
StringHashCompareFunc
char StringHashCompareFunc(void *data1, uint16_t datalen1, void *data2, uint16_t datalen2)
Definition: util-hash-string.c:38
SigTableElmt_::Setup
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1524
StringHashFunc
uint32_t StringHashFunc(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash-string.c:33
FAIL_IF_NOT
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
Definition: util-unittest.h:82
HashTableFree
void HashTableFree(HashTable *ht)
Free a HashTable and all its contents.
Definition: util-hash.c:111
strlcpy
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
PASS
#define PASS
Pass the test.
Definition: util-unittest.h:105
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:22
SCEnter
#define SCEnter(...)
Definition: util-debug.h:284
HashTableLookup
void * HashTableLookup(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash.c:193
DetectMetadataHashFree
void DetectMetadataHashFree(DetectEngineCtx *de_ctx)
Definition: detect-metadata.c:79
DETECT_METADATA
@ DETECT_METADATA
Definition: detect-engine-register.h:108
detect.h
HashTableAdd
int HashTableAdd(HashTable *ht, void *data, uint16_t datalen)
Definition: util-hash.c:131
StringHashFreeFunc
void StringHashFreeFunc(void *data)
Definition: util-hash-string.c:44
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:322
DetectMetadataHead::list
DetectMetadata * list
Definition: detect-metadata.h:41
SCReturn
#define SCReturn
Definition: util-debug.h:286
DETECT_MAX_RULE_SIZE
#define DETECT_MAX_RULE_SIZE
Definition: detect.h:47
MD_STR_LEN
#define MD_STR_LEN
SigTableElmt_::Match
int(* Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition: detect.h:1504
DetectMetadata_
Signature metadata list.
Definition: detect-metadata.h:30
MD_STR
#define MD_STR
cnt
uint32_t cnt
Definition: tmqh-packetpool.h:7
FAIL_IF
#define FAIL_IF(expr)
Fail a test if expression evaluates to true.
Definition: util-unittest.h:71
suricata-common.h
detect-metadata.h
SCStrdup
#define SCStrdup(s)
Definition: util-mem.h:56
util-validate.h
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
str
#define str(s)
Definition: suricata-common.h:313
Signature_::metadata
DetectMetadataHead * metadata
Definition: detect.h:771
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:274
head
Flow * head
Definition: flow-hash.h:1
DetectEngineUnsetParseMetadata
void DetectEngineUnsetParseMetadata(void)
Definition: detect-engine.c:5288
SCFree
#define SCFree(p)
Definition: util-mem.h:61
DetectMetadata_::next
struct DetectMetadata_ * next
Definition: detect-metadata.h:36
detect-parse.h
Signature_
Signature container.
Definition: detect.h:692
HashTableInit
HashTable * HashTableInit(uint32_t size, uint32_t(*Hash)(struct HashTable_ *, void *, uint16_t), char(*Compare)(void *, uint16_t, void *, uint16_t), void(*Free)(void *))
Definition: util-hash.c:34
DetectEngineCtxInit
DetectEngineCtx * DetectEngineCtxInit(void)
Definition: detect-engine.c:2873
DetectEngineSetParseMetadata
void DetectEngineSetParseMetadata(void)
Definition: detect-engine.c:5283
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
DetectEngineCtx_::metadata_table
HashTable * metadata_table
Definition: detect.h:1145
DEBUG_VALIDATE_BUG_ON
#define DEBUG_VALIDATE_BUG_ON(exp)
Definition: util-validate.h:109
SigTableElmt_::RegisterTests
void(* RegisterTests)(void)
Definition: detect.h:1531