1 /* Copyright (C) 2007-2017 Open Information Security Foundation
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
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.
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
21 * \author Victor Julien <victor@inliniac.net>
23 * Implements metadata keyword support
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.
29 #include "suricata-common.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"
37 #include "util-validate.h"
39 static int DetectMetadataSetup (DetectEngineCtx
*, Signature
*, const char *);
41 static void DetectMetadataRegisterTests(void);
44 void DetectMetadataRegister (void)
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
;
53 sigmatch_table
[DETECT_METADATA
].RegisterTests
= DetectMetadataRegisterTests
;
58 * \brief Free a Metadata object
60 void DetectMetadataFree(DetectMetadata
*mdata
)
69 int DetectMetadataHashInit(DetectEngineCtx
*de_ctx
)
71 if (! DetectEngineMustParseMetadata())
74 de_ctx
->metadata_table
= HashTableInit(4096, StringHashFunc
, StringHashCompareFunc
, StringHashFreeFunc
);
75 if (de_ctx
->metadata_table
== NULL
)
80 void DetectMetadataHashFree(DetectEngineCtx
*de_ctx
)
82 if (de_ctx
->metadata_table
)
83 HashTableFree(de_ctx
->metadata_table
);
86 static const char *DetectMedatataHashAdd(DetectEngineCtx
*de_ctx
, const char *string
)
88 const char * hstring
= (char *)HashTableLookup(de_ctx
->metadata_table
, (void *)string
, strlen(string
));
93 const char *astring
= SCStrdup(string
);
94 if (astring
== NULL
) {
98 if (HashTableAdd(de_ctx
->metadata_table
, (void *)astring
, strlen(astring
)) == 0) {
99 return (char *)HashTableLookup(de_ctx
->metadata_table
, (void *)astring
, strlen(astring
));
101 SCFree((void *)astring
);
106 static int SortHelper(const void *a
, const void *b
)
108 const DetectMetadata
*ma
= *(const DetectMetadata
**)a
;
109 const DetectMetadata
*mb
= *(const DetectMetadata
**)b
;
110 return strcasecmp(ma
->key
, mb
->key
);
113 static char *CraftPreformattedJSON(const DetectMetadata
*head
)
116 for (const DetectMetadata
*m
= head
; m
!= NULL
; m
= m
->next
) {
122 const DetectMetadata
*array
[cnt
];
124 for (const DetectMetadata
*m
= head
; m
!= NULL
; m
= m
->next
) {
128 qsort(array
, cnt
, sizeof(DetectMetadata
*), SortHelper
);
130 JsonBuilder
*js
= jb_new_object();
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
142 if (nm
&& strcasecmp(m
->key
, nm
->key
) == 0) {
144 jb_open_array(js
, m
->key
);
147 jb_append_string(js
, m
->value
);
150 jb_open_array(js
, m
->key
);
152 jb_append_string(js
, m
->value
);
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);
168 memcpy(ptr
, MD_STR
, MD_STR_LEN
);
170 memcpy(ptr
, jb_ptr(js
), len
);
179 static int DetectMetadataParse(DetectEngineCtx
*de_ctx
, Signature
*s
, const char *metadatastr
)
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
)) {
190 char *val
= strchr(key
, ' ');
193 while (*val
!= '\0' && isblank(*val
)) {
197 /* Skip metadata without a value. */
201 /* Also skip metadata if the key or value is empty. */
202 if (strlen(key
) == 0 || strlen(val
) == 0) {
206 const char *hkey
= DetectMedatataHashAdd(de_ctx
, key
);
208 SCLogError(SC_ERR_MEM_ALLOC
, "can't create metadata key");
212 const char *hval
= DetectMedatataHashAdd(de_ctx
, val
);
214 SCLogError(SC_ERR_MEM_ALLOC
, "can't create metadata value");
218 SCLogDebug("key: %s, value: %s", hkey
, hval
);
220 DetectMetadata
*dkv
= SCMalloc(sizeof(DetectMetadata
));
230 key
= strtok_r(NULL
, ",", &xsaveptr
);
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
);
244 s
->metadata
->list
= head
;
245 SCFree(s
->metadata
->json_str
);
246 s
->metadata
->json_str
= CraftPreformattedJSON(head
);
251 static int DetectMetadataSetup(DetectEngineCtx
*de_ctx
, Signature
*s
, const char *rawstr
)
253 if (DetectEngineMustParseMetadata()) {
254 DetectMetadataParse(de_ctx
, s
, rawstr
);
262 static int DetectMetadataParseTest01(void)
264 DetectEngineUnsetParseMetadata();
265 DetectEngineCtx
*de_ctx
= DetectEngineCtxInit();
266 FAIL_IF_NULL(de_ctx
);
268 Signature
*sig
= DetectEngineAppendSig(de_ctx
,
269 "alert tcp any any -> any any "
270 "(metadata: toto 1; sid:1; rev:1;)");
272 FAIL_IF(sig
->metadata
);
274 DetectEngineCtxFree(de_ctx
);
278 static int DetectMetadataParseTest02(void)
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;"
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
));
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
);
307 * \brief this function registers unit tests for DetectCipService
309 static void DetectMetadataRegisterTests(void)
311 UtRegisterTest("DetectMetadataParseTest01", DetectMetadataParseTest01
);
312 UtRegisterTest("DetectMetadataParseTest02", DetectMetadataParseTest02
);
314 #endif /* UNITTESTS */