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