]> git.ipfire.org Git - people/ms/suricata.git/blob - src/detect-metadata.c
core: Remove unneeded consts
[people/ms/suricata.git] / src / detect-metadata.c
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 managment.
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
39 static int DetectMetadataSetup (DetectEngineCtx *, Signature *, const char *);
40 #ifdef UNITTESTS
41 static void DetectMetadataRegisterTests(void);
42 #endif
43
44 void DetectMetadataRegister (void)
45 {
46 sigmatch_table[DETECT_METADATA].name = "metadata";
47 sigmatch_table[DETECT_METADATA].desc = "used for logging";
48 sigmatch_table[DETECT_METADATA].url = "/rules/meta.html#metadata";
49 sigmatch_table[DETECT_METADATA].Match = NULL;
50 sigmatch_table[DETECT_METADATA].Setup = DetectMetadataSetup;
51 sigmatch_table[DETECT_METADATA].Free = NULL;
52 #ifdef UNITTESTS
53 sigmatch_table[DETECT_METADATA].RegisterTests = DetectMetadataRegisterTests;
54 #endif
55 }
56
57 /**
58 * \brief Free a Metadata object
59 */
60 void DetectMetadataFree(DetectMetadata *mdata)
61 {
62 SCEnter();
63
64 SCFree(mdata);
65
66 SCReturn;
67 }
68
69 int DetectMetadataHashInit(DetectEngineCtx *de_ctx)
70 {
71 if (! DetectEngineMustParseMetadata())
72 return 0;
73
74 de_ctx->metadata_table = HashTableInit(4096, StringHashFunc, StringHashCompareFunc, StringHashFreeFunc);
75 if (de_ctx->metadata_table == NULL)
76 return -1;
77 return 0;
78 }
79
80 void DetectMetadataHashFree(DetectEngineCtx *de_ctx)
81 {
82 if (de_ctx->metadata_table)
83 HashTableFree(de_ctx->metadata_table);
84 }
85
86 static const char *DetectMedatataHashAdd(DetectEngineCtx *de_ctx, const char *string)
87 {
88 const char * hstring = (char *)HashTableLookup(de_ctx->metadata_table, (void *)string, 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, strlen(astring)) == 0) {
99 return (char *)HashTableLookup(de_ctx->metadata_table, (void *)astring, strlen(astring));
100 } else {
101 SCFree((void *)astring);
102 }
103 return NULL;
104 }
105
106 static int SortHelper(const void *a, const void *b)
107 {
108 const DetectMetadata *ma = *(const DetectMetadata **)a;
109 const DetectMetadata *mb = *(const DetectMetadata **)b;
110 return strcasecmp(ma->key, mb->key);
111 }
112
113 static char *CraftPreformattedJSON(const DetectMetadata *head)
114 {
115 int cnt = 0;
116 for (const DetectMetadata *m = head; m != NULL; m = m->next) {
117 cnt++;
118 }
119 if (cnt == 0)
120 return NULL;
121
122 const DetectMetadata *array[cnt];
123 int i = 0;
124 for (const DetectMetadata *m = head; m != NULL; m = m->next) {
125 array[i++] = m;
126 }
127 BUG_ON(i != cnt);
128 qsort(array, cnt, sizeof(DetectMetadata *), SortHelper);
129
130 JsonBuilder *js = jb_new_object();
131 if (js == NULL)
132 return NULL;
133
134 /* array is sorted by key, so we can create a jsonbuilder object
135 * with each key appearing just once with one or more values */
136 bool array_open = false;
137 for (int j = 0; j < cnt; j++) {
138 const DetectMetadata *m = array[j];
139 const DetectMetadata *nm = j + 1 < cnt ? array[j + 1] : NULL;
140 DEBUG_VALIDATE_BUG_ON(m == NULL); // for scan-build
141
142 if (nm && strcasecmp(m->key, nm->key) == 0) {
143 if (!array_open) {
144 jb_open_array(js, m->key);
145 array_open = true;
146 }
147 jb_append_string(js, m->value);
148 } else {
149 if (!array_open) {
150 jb_open_array(js, m->key);
151 }
152 jb_append_string(js, m->value);
153 jb_close(js);
154 array_open = false;
155 }
156 }
157 jb_close(js);
158 /* we have a complete json builder. Now store it as a C string */
159 const size_t len = jb_len(js);
160 #define MD_STR "\"metadata\":"
161 #define MD_STR_LEN (sizeof(MD_STR) - 1)
162 char *str = SCMalloc(len + MD_STR_LEN + 1);
163 if (str == NULL) {
164 jb_free(js);
165 return NULL;
166 }
167 char *ptr = str;
168 memcpy(ptr, MD_STR, MD_STR_LEN);
169 ptr += MD_STR_LEN;
170 memcpy(ptr, jb_ptr(js), len);
171 ptr += len;
172 *ptr = '\0';
173 #undef MD_STR
174 #undef MD_STR_LEN
175 jb_free(js);
176 return str;
177 }
178
179 static int DetectMetadataParse(DetectEngineCtx *de_ctx, Signature *s, const char *metadatastr)
180 {
181 DetectMetadata *head = s->metadata ? s->metadata->list : NULL;
182 char copy[strlen(metadatastr)+1];
183 strlcpy(copy, metadatastr, sizeof(copy));
184 char *xsaveptr = NULL;
185 char *key = strtok_r(copy, ",", &xsaveptr);
186 while (key != NULL) {
187 while (*key != '\0' && isblank(*key)) {
188 key++;
189 }
190 char *val = strchr(key, ' ');
191 if (val != NULL) {
192 *val++ = '\0';
193 while (*val != '\0' && isblank(*val)) {
194 val++;
195 }
196 } else {
197 /* Skip metadata without a value. */
198 goto next;
199 }
200
201 /* Also skip metadata if the key or value is empty. */
202 if (strlen(key) == 0 || strlen(val) == 0) {
203 goto next;
204 }
205
206 const char *hkey = DetectMedatataHashAdd(de_ctx, key);
207 if (hkey == NULL) {
208 SCLogError(SC_ERR_MEM_ALLOC, "can't create metadata key");
209 continue;
210 }
211
212 const char *hval = DetectMedatataHashAdd(de_ctx, val);
213 if (hval == NULL) {
214 SCLogError(SC_ERR_MEM_ALLOC, "can't create metadata value");
215 goto next;
216 }
217
218 SCLogDebug("key: %s, value: %s", hkey, hval);
219
220 DetectMetadata *dkv = SCMalloc(sizeof(DetectMetadata));
221 if (dkv == NULL) {
222 goto next;
223 }
224 dkv->key = hkey;
225 dkv->value = hval;
226 dkv->next = head;
227 head = dkv;
228
229 next:
230 key = strtok_r(NULL, ",", &xsaveptr);
231 }
232 if (head != NULL) {
233 if (s->metadata == NULL) {
234 s->metadata = SCCalloc(1, sizeof(*s->metadata));
235 if (s->metadata == NULL) {
236 for (DetectMetadata *m = head; m != NULL; ) {
237 DetectMetadata *next_m = m->next;
238 DetectMetadataFree(m);
239 m = next_m;
240 }
241 return -1;
242 }
243 }
244 s->metadata->list = head;
245 SCFree(s->metadata->json_str);
246 s->metadata->json_str = CraftPreformattedJSON(head);
247 }
248 return 0;
249 }
250
251 static int DetectMetadataSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
252 {
253 if (DetectEngineMustParseMetadata()) {
254 DetectMetadataParse(de_ctx, s, rawstr);
255 }
256
257 return 0;
258 }
259
260 #ifdef UNITTESTS
261
262 static int DetectMetadataParseTest01(void)
263 {
264 DetectEngineUnsetParseMetadata();
265 DetectEngineCtx *de_ctx = DetectEngineCtxInit();
266 FAIL_IF_NULL(de_ctx);
267
268 Signature *sig = DetectEngineAppendSig(de_ctx,
269 "alert tcp any any -> any any "
270 "(metadata: toto 1; sid:1; rev:1;)");
271 FAIL_IF_NULL(sig);
272 FAIL_IF(sig->metadata);
273
274 DetectEngineCtxFree(de_ctx);
275 PASS;
276 }
277
278 static int DetectMetadataParseTest02(void)
279 {
280 DetectEngineSetParseMetadata();
281 DetectEngineCtx *de_ctx = DetectEngineCtxInit();
282 FAIL_IF_NULL(de_ctx);
283 Signature *sig = DetectEngineAppendSig(de_ctx,
284 "alert tcp any any -> any any "
285 "(metadata: toto 1; "
286 "metadata: titi 2, jaivu gros_minet;"
287 "sid:1; rev:1;)");
288 FAIL_IF_NULL(sig);
289 FAIL_IF_NULL(sig->metadata);
290 FAIL_IF_NULL(sig->metadata->list);
291 FAIL_IF_NULL(sig->metadata->list->key);
292 FAIL_IF(strcmp("jaivu", sig->metadata->list->key));
293 FAIL_IF(strcmp("gros_minet", sig->metadata->list->value));
294 FAIL_IF_NULL(sig->metadata->list->next);
295 DetectMetadata *dm = sig->metadata->list->next;
296 FAIL_IF(strcmp("titi", dm->key));
297 dm = dm->next;
298 FAIL_IF_NULL(dm);
299 FAIL_IF(strcmp("toto", dm->key));
300 FAIL_IF_NOT(strcmp(sig->metadata->json_str,
301 "\"metadata\":{\"jaivu\":[\"gros_minet\"],\"titi\":[\"2\"],\"toto\":[\"1\"]}") == 0);
302 DetectEngineCtxFree(de_ctx);
303 PASS;
304 }
305
306 /**
307 * \brief this function registers unit tests for DetectCipService
308 */
309 static void DetectMetadataRegisterTests(void)
310 {
311 UtRegisterTest("DetectMetadataParseTest01", DetectMetadataParseTest01);
312 UtRegisterTest("DetectMetadataParseTest02", DetectMetadataParseTest02);
313 }
314 #endif /* UNITTESTS */