/src/suricata/src/detect-engine-event.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2021 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 Breno Silva <breno.silva@gmail.com> |
22 | | * |
23 | | * Implements the decode-event keyword |
24 | | */ |
25 | | |
26 | | #include "suricata-common.h" |
27 | | #include "suricata.h" |
28 | | #include "decode.h" |
29 | | #include "detect.h" |
30 | | #include "detect-parse.h" |
31 | | #include "detect-engine-prefilter-common.h" |
32 | | #include "detect-engine-uint.h" |
33 | | |
34 | | #include "flow-var.h" |
35 | | #include "decode-events.h" |
36 | | |
37 | | #include "util-debug.h" |
38 | | |
39 | | #include "stream-tcp.h" |
40 | | |
41 | | |
42 | | /* Need to get the DEvents[] array */ |
43 | | |
44 | | #include "detect-engine-event.h" |
45 | | #include "util-unittest.h" |
46 | | |
47 | 79 | #define PARSE_REGEX "\\S[0-9A-z_]+[.][A-z0-9_+.]+$" |
48 | | |
49 | | static DetectParseRegex parse_regex; |
50 | | |
51 | | static int DetectEngineEventMatch (DetectEngineThreadCtx *, |
52 | | Packet *, const Signature *, const SigMatchCtx *); |
53 | | static int DetectEngineEventSetup (DetectEngineCtx *, Signature *, const char *); |
54 | | static int DetectDecodeEventSetup (DetectEngineCtx *, Signature *, const char *); |
55 | | static int DetectStreamEventSetup (DetectEngineCtx *, Signature *, const char *); |
56 | | static void DetectEngineEventFree (DetectEngineCtx *, void *); |
57 | | #ifdef UNITTESTS |
58 | | void EngineEventRegisterTests(void); |
59 | | #endif |
60 | | |
61 | | static bool PrefilterEventIsPrefilterable(const Signature *s, int smtype) |
62 | 0 | { |
63 | 0 | const SigMatch *sm; |
64 | 0 | for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL; sm = sm->next) { |
65 | 0 | if (sm->type == smtype) { |
66 | 0 | return true; |
67 | 0 | } |
68 | 0 | } |
69 | 0 | return false; |
70 | 0 | } |
71 | | static bool PrefilterStreamEventIsPrefilterable(const Signature *s) |
72 | 0 | { |
73 | 0 | return PrefilterEventIsPrefilterable(s, DETECT_STREAM_EVENT); |
74 | 0 | } |
75 | | |
76 | | static bool PrefilterDecodeEventIsPrefilterable(const Signature *s) |
77 | 0 | { |
78 | 0 | return PrefilterEventIsPrefilterable(s, DETECT_DECODE_EVENT); |
79 | 0 | } |
80 | | |
81 | | static void PrefilterPacketEventSet(PrefilterPacketHeaderValue *v, void *smctx) |
82 | 414 | { |
83 | 414 | const DetectEngineEventData *a = smctx; |
84 | 414 | v->u8[0] = PREFILTER_U8HASH_MODE_EQ; |
85 | 414 | v->u8[1] = a->event; // arg1 |
86 | 414 | v->u8[2] = 0; // arg2 |
87 | 414 | } |
88 | | |
89 | | static bool PrefilterPacketEventCompare(PrefilterPacketHeaderValue v, void *smctx) |
90 | 0 | { |
91 | 0 | const DetectEngineEventData *a = smctx; |
92 | 0 | DetectUintData_u8 du8; |
93 | 0 | du8.mode = DETECT_UINT_EQ; |
94 | 0 | du8.arg1 = a->event; |
95 | 0 | du8.arg2 = 0; |
96 | 0 | return PrefilterPacketU8Compare(v, &du8); |
97 | 0 | } |
98 | | |
99 | | static void PrefilterPacketEventMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx) |
100 | 46 | { |
101 | 46 | const PrefilterPacketU8HashCtx *h = pectx; |
102 | 100 | for (uint8_t u = 0; u < p->events.cnt; u++) { |
103 | 54 | const SigsArray *sa = h->array[p->events.events[u]]; |
104 | 54 | if (sa) { |
105 | 1 | PrefilterAddSids(&det_ctx->pmq, sa->sigs, sa->cnt); |
106 | 1 | } |
107 | 54 | } |
108 | 46 | } |
109 | | |
110 | | static int PrefilterSetupStreamEvent(DetectEngineCtx *de_ctx, SigGroupHead *sgh) |
111 | 45 | { |
112 | 45 | return PrefilterSetupPacketHeaderU8Hash(de_ctx, sgh, DETECT_STREAM_EVENT, |
113 | 45 | SIG_MASK_REQUIRE_ENGINE_EVENT, PrefilterPacketEventSet, PrefilterPacketEventCompare, |
114 | 45 | PrefilterPacketEventMatch); |
115 | 45 | } |
116 | | |
117 | | static int PrefilterSetupDecodeEvent(DetectEngineCtx *de_ctx, SigGroupHead *sgh) |
118 | 175 | { |
119 | 175 | return PrefilterSetupPacketHeaderU8Hash(de_ctx, sgh, DETECT_DECODE_EVENT, |
120 | 175 | SIG_MASK_REQUIRE_ENGINE_EVENT, PrefilterPacketEventSet, PrefilterPacketEventCompare, |
121 | 175 | PrefilterPacketEventMatch); |
122 | 175 | } |
123 | | |
124 | | /** |
125 | | * \brief Registration function for decode-event: keyword |
126 | | */ |
127 | | void DetectEngineEventRegister (void) |
128 | 79 | { |
129 | 79 | sigmatch_table[DETECT_ENGINE_EVENT].name = "engine-event"; |
130 | 79 | sigmatch_table[DETECT_ENGINE_EVENT].Match = DetectEngineEventMatch; |
131 | 79 | sigmatch_table[DETECT_ENGINE_EVENT].Setup = DetectEngineEventSetup; |
132 | 79 | sigmatch_table[DETECT_ENGINE_EVENT].Free = DetectEngineEventFree; |
133 | | #ifdef UNITTESTS |
134 | | sigmatch_table[DETECT_ENGINE_EVENT].RegisterTests = EngineEventRegisterTests; |
135 | | #endif |
136 | | |
137 | 79 | sigmatch_table[DETECT_DECODE_EVENT].name = "decode-event"; |
138 | 79 | sigmatch_table[DETECT_DECODE_EVENT].Match = DetectEngineEventMatch; |
139 | 79 | sigmatch_table[DETECT_DECODE_EVENT].Setup = DetectDecodeEventSetup; |
140 | 79 | sigmatch_table[DETECT_DECODE_EVENT].Free = DetectEngineEventFree; |
141 | 79 | sigmatch_table[DETECT_DECODE_EVENT].desc = |
142 | 79 | "match on events triggered by structural or invalid values during packet decoding"; |
143 | 79 | sigmatch_table[DETECT_DECODE_EVENT].url = "/rules/decode-layer.html#decode-event"; |
144 | 79 | sigmatch_table[DETECT_DECODE_EVENT].flags |= SIGMATCH_DEONLY_COMPAT; |
145 | 79 | sigmatch_table[DETECT_DECODE_EVENT].SupportsPrefilter = PrefilterDecodeEventIsPrefilterable; |
146 | 79 | sigmatch_table[DETECT_DECODE_EVENT].SetupPrefilter = PrefilterSetupDecodeEvent; |
147 | | |
148 | 79 | sigmatch_table[DETECT_STREAM_EVENT].name = "stream-event"; |
149 | 79 | sigmatch_table[DETECT_STREAM_EVENT].Match = DetectEngineEventMatch; |
150 | 79 | sigmatch_table[DETECT_STREAM_EVENT].Setup = DetectStreamEventSetup; |
151 | 79 | sigmatch_table[DETECT_STREAM_EVENT].Free = DetectEngineEventFree; |
152 | 79 | sigmatch_table[DETECT_STREAM_EVENT].desc = |
153 | 79 | "match on events triggered by anomalies during TCP streaming"; |
154 | 79 | sigmatch_table[DETECT_STREAM_EVENT].SupportsPrefilter = PrefilterStreamEventIsPrefilterable; |
155 | 79 | sigmatch_table[DETECT_STREAM_EVENT].SetupPrefilter = PrefilterSetupStreamEvent; |
156 | | |
157 | 79 | DetectSetupParseRegexes(PARSE_REGEX, &parse_regex); |
158 | 79 | } |
159 | | |
160 | | /** |
161 | | * \brief This function is used to match decoder event flags set on a packet with those passed via decode-event: |
162 | | * |
163 | | * \param t pointer to thread vars |
164 | | * \param det_ctx pointer to the pattern matcher thread |
165 | | * \param p pointer to the current packet |
166 | | * \param s pointer to the Signature |
167 | | * \param m pointer to the sigmatch |
168 | | * |
169 | | * \retval 0 no match |
170 | | * \retval 1 match |
171 | | */ |
172 | | static int DetectEngineEventMatch (DetectEngineThreadCtx *det_ctx, |
173 | | Packet *p, const Signature *s, const SigMatchCtx *ctx) |
174 | 320k | { |
175 | 320k | SCEnter(); |
176 | | |
177 | 320k | const DetectEngineEventData *de = (const DetectEngineEventData *)ctx; |
178 | | |
179 | 320k | if (ENGINE_ISSET_EVENT(p, de->event)) { |
180 | 32.2k | SCLogDebug("de->event matched %u", de->event); |
181 | 32.2k | SCReturnInt(1); |
182 | 32.2k | } |
183 | | |
184 | 320k | SCReturnInt(0); |
185 | 320k | } |
186 | | |
187 | | static bool OutdatedEvent(const char *raw) |
188 | 49.8k | { |
189 | 49.8k | return strcmp(raw, "decoder.udp.hlen_invalid") == 0; |
190 | 49.8k | } |
191 | | |
192 | | /** |
193 | | * \brief This function is used to parse decoder events options passed via decode-event: keyword |
194 | | * |
195 | | * \param rawstr Pointer to the user provided decode-event options |
196 | | * |
197 | | * \retval de pointer to DetectFlowData on success |
198 | | * \retval NULL on failure |
199 | | */ |
200 | | static DetectEngineEventData *DetectEngineEventParse (const char *rawstr) |
201 | 61.8k | { |
202 | 61.8k | int i; |
203 | 61.8k | DetectEngineEventData *de = NULL; |
204 | 61.8k | int res = 0, found = 0; |
205 | 61.8k | size_t pcre2len; |
206 | 61.8k | pcre2_match_data *match = NULL; |
207 | | |
208 | 61.8k | int ret = DetectParsePcreExec(&parse_regex, &match, rawstr, 0, 0); |
209 | 61.8k | if (ret < 1) { |
210 | 1.11k | SCLogError("pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr); |
211 | 1.11k | goto error; |
212 | 1.11k | } |
213 | | |
214 | 60.7k | char copy_str[128] = ""; |
215 | 60.7k | pcre2len = sizeof(copy_str); |
216 | 60.7k | res = pcre2_substring_copy_bynumber(match, 0, (PCRE2_UCHAR8 *)copy_str, &pcre2len); |
217 | | |
218 | 60.7k | if (res < 0) { |
219 | 2 | SCLogError("pcre2_substring_copy_bynumber failed"); |
220 | 2 | goto error; |
221 | 2 | } |
222 | | |
223 | 5.61M | for (i = 0; DEvents[i].event_name != NULL; i++) { |
224 | 5.61M | if (strcasecmp(DEvents[i].event_name,copy_str) == 0) { |
225 | 58.6k | found = 1; |
226 | 58.6k | break; |
227 | 58.6k | } |
228 | 5.61M | } |
229 | | |
230 | 60.7k | if (found == 0) { |
231 | 2.01k | SCLogError("unknown decode event \"%s\"", copy_str); |
232 | 2.01k | goto error; |
233 | 2.01k | } |
234 | | |
235 | 58.6k | de = SCMalloc(sizeof(DetectEngineEventData)); |
236 | 58.6k | if (unlikely(de == NULL)) |
237 | 0 | goto error; |
238 | | |
239 | 58.6k | de->event = DEvents[i].code; |
240 | | |
241 | 58.6k | if (de->event == STREAM_REASSEMBLY_OVERLAP_DIFFERENT_DATA) { |
242 | 145 | StreamTcpReassembleConfigEnableOverlapCheck(); |
243 | 145 | } |
244 | | |
245 | 58.6k | if (OutdatedEvent(rawstr)) { |
246 | 115 | if (SigMatchStrictEnabled(DETECT_DECODE_EVENT)) { |
247 | 0 | SCLogError("decode-event keyword no longer supports event \"%s\"", rawstr); |
248 | 0 | goto error; |
249 | 115 | } else { |
250 | 115 | SCLogWarning("decode-event keyword no longer supports event \"%s\"", rawstr); |
251 | 115 | } |
252 | 115 | } |
253 | | |
254 | 58.6k | pcre2_match_data_free(match); |
255 | 58.6k | return de; |
256 | | |
257 | 3.13k | error: |
258 | 3.13k | if (de) |
259 | 0 | SCFree(de); |
260 | 3.13k | if (match) { |
261 | 3.13k | pcre2_match_data_free(match); |
262 | 3.13k | } |
263 | 3.13k | return NULL; |
264 | 58.6k | } |
265 | | |
266 | | /** |
267 | | * \brief this function is used to add the parsed decode-event into the current signature |
268 | | * |
269 | | * \param de_ctx pointer to the Detection Engine Context |
270 | | * \param s pointer to the Current Signature |
271 | | * \param rawstr pointer to the user provided decode-event options |
272 | | * |
273 | | * \retval 0 on Success |
274 | | * \retval -1 on Failure |
275 | | */ |
276 | | static int DetectEngineEventSetupDo( |
277 | | DetectEngineCtx *de_ctx, Signature *s, const char *rawstr, uint16_t smtype) |
278 | 61.8k | { |
279 | 61.8k | DetectEngineEventData *de = DetectEngineEventParse(rawstr); |
280 | 61.8k | if (de == NULL) |
281 | 3.13k | return -1; |
282 | | |
283 | 58.6k | SCLogDebug("rawstr %s %u", rawstr, de->event); |
284 | | |
285 | 58.6k | if (SCSigMatchAppendSMToList(de_ctx, s, smtype, (SigMatchCtx *)de, DETECT_SM_LIST_MATCH) == |
286 | 58.6k | NULL) { |
287 | 0 | SCFree(de); |
288 | 0 | return -1; |
289 | 0 | } |
290 | 58.6k | return 0; |
291 | 58.6k | } |
292 | | |
293 | | |
294 | | static int DetectEngineEventSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) |
295 | 65 | { |
296 | 65 | return DetectEngineEventSetupDo (de_ctx, s, rawstr, DETECT_ENGINE_EVENT); |
297 | 65 | } |
298 | | |
299 | | /** |
300 | | * \brief this function will free memory associated with DetectEngineEventData |
301 | | * |
302 | | * \param de pointer to DetectEngineEventData |
303 | | */ |
304 | | static void DetectEngineEventFree(DetectEngineCtx *de_ctx, void *ptr) |
305 | 58.6k | { |
306 | 58.6k | DetectEngineEventData *de = (DetectEngineEventData *)ptr; |
307 | 58.6k | if (de) |
308 | 58.6k | SCFree(de); |
309 | 58.6k | } |
310 | | |
311 | | |
312 | | /** |
313 | | * \brief this function Setup the 'decode-event' keyword by setting the correct |
314 | | * signature type |
315 | | */ |
316 | | static int DetectDecodeEventSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) |
317 | 50.9k | { |
318 | 50.9k | char drawstr[64] = "decoder."; |
319 | | |
320 | | /* decoder:$EVENT alias command develop as decode-event:decoder.$EVENT */ |
321 | 50.9k | strlcat(drawstr, rawstr, sizeof(drawstr)); |
322 | | |
323 | 50.9k | return DetectEngineEventSetupDo(de_ctx, s, drawstr, DETECT_DECODE_EVENT); |
324 | 50.9k | } |
325 | | |
326 | | /** |
327 | | * \brief this function Setup the 'stream-event' keyword by resolving the alias |
328 | | */ |
329 | | static int DetectStreamEventSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) |
330 | 10.7k | { |
331 | 10.7k | char srawstr[64] = "stream."; |
332 | | |
333 | 10.7k | if (strcmp(rawstr, "est_synack_resend_with_different_ack") == 0) { |
334 | 287 | rawstr = "est_synack_resend_with_diff_ack"; |
335 | 10.5k | } else if (strcmp(rawstr, "3whs_synack_resend_with_different_ack") == 0) { |
336 | 1.23k | rawstr = "3whs_synack_resend_with_diff_ack"; |
337 | 1.23k | } |
338 | | |
339 | | /* stream:$EVENT alias command develop as decode-event:stream.$EVENT */ |
340 | 10.7k | strlcat(srawstr, rawstr, sizeof(srawstr)); |
341 | | |
342 | 10.7k | return DetectEngineEventSetupDo(de_ctx, s, srawstr, DETECT_STREAM_EVENT); |
343 | 10.7k | } |
344 | | |
345 | | /* |
346 | | * ONLY TESTS BELOW THIS COMMENT |
347 | | */ |
348 | | #ifdef UNITTESTS |
349 | | |
350 | | /** |
351 | | * \test EngineEventTestParse01 is a test for a valid decode-event value |
352 | | */ |
353 | | static int EngineEventTestParse01 (void) |
354 | | { |
355 | | DetectEngineEventData *de = DetectEngineEventParse("decoder.ipv4.pkt_too_small"); |
356 | | |
357 | | FAIL_IF_NULL(de); |
358 | | |
359 | | DetectEngineEventFree(NULL, de); |
360 | | |
361 | | PASS; |
362 | | } |
363 | | |
364 | | /** |
365 | | * \test EngineEventTestParse02 is a test for a valid upper + lower case decode-event value |
366 | | */ |
367 | | static int EngineEventTestParse02 (void) |
368 | | { |
369 | | DetectEngineEventData *de = DetectEngineEventParse("decoder.PPP.pkt_too_small"); |
370 | | |
371 | | FAIL_IF_NULL(de); |
372 | | |
373 | | DetectEngineEventFree(NULL, de); |
374 | | |
375 | | PASS; |
376 | | } |
377 | | |
378 | | /** |
379 | | * \test EngineEventTestParse03 is a test for a valid upper case decode-event value |
380 | | */ |
381 | | static int EngineEventTestParse03 (void) |
382 | | { |
383 | | DetectEngineEventData *de = DetectEngineEventParse("decoder.IPV6.PKT_TOO_SMALL"); |
384 | | |
385 | | FAIL_IF_NULL(de); |
386 | | |
387 | | DetectEngineEventFree(NULL, de); |
388 | | |
389 | | PASS; |
390 | | } |
391 | | |
392 | | /** |
393 | | * \test EngineEventTestParse04 is a test for an invalid upper case decode-event value |
394 | | */ |
395 | | static int EngineEventTestParse04 (void) |
396 | | { |
397 | | DetectEngineEventData *de = DetectEngineEventParse("decoder.IPV6.INVALID_EVENT"); |
398 | | |
399 | | FAIL_IF_NOT_NULL(de); |
400 | | |
401 | | DetectEngineEventFree(NULL, de); |
402 | | |
403 | | PASS; |
404 | | } |
405 | | |
406 | | /** |
407 | | * \test EngineEventTestParse05 is a test for an invalid char into the decode-event value |
408 | | */ |
409 | | static int EngineEventTestParse05 (void) |
410 | | { |
411 | | DetectEngineEventData *de = DetectEngineEventParse("decoder.IPV-6,INVALID_CHAR"); |
412 | | |
413 | | FAIL_IF_NOT_NULL(de); |
414 | | |
415 | | DetectEngineEventFree(NULL, de); |
416 | | |
417 | | PASS; |
418 | | } |
419 | | |
420 | | /** |
421 | | * \test EngineEventTestParse06 is a test for match function with valid decode-event value |
422 | | */ |
423 | | static int EngineEventTestParse06 (void) |
424 | | { |
425 | | Packet *p = PacketGetFromAlloc(); |
426 | | FAIL_IF_NULL(p); |
427 | | |
428 | | ThreadVars tv; |
429 | | |
430 | | memset(&tv, 0, sizeof(ThreadVars)); |
431 | | |
432 | | ENGINE_SET_EVENT(p,PPP_PKT_TOO_SMALL); |
433 | | |
434 | | DetectEngineEventData *de = DetectEngineEventParse("decoder.ppp.pkt_too_small"); |
435 | | FAIL_IF_NULL(de); |
436 | | |
437 | | de->event = PPP_PKT_TOO_SMALL; |
438 | | |
439 | | SigMatch *sm = SigMatchAlloc(); |
440 | | FAIL_IF_NULL(sm); |
441 | | |
442 | | sm->type = DETECT_DECODE_EVENT; |
443 | | sm->ctx = (SigMatchCtx *)de; |
444 | | |
445 | | FAIL_IF_NOT(DetectEngineEventMatch(NULL, p, NULL, sm->ctx)); |
446 | | |
447 | | PacketFree(p); |
448 | | SCFree(de); |
449 | | SCFree(sm); |
450 | | |
451 | | PASS; |
452 | | } |
453 | | |
454 | | /** |
455 | | * \brief this function registers unit tests for EngineEvent |
456 | | */ |
457 | | void EngineEventRegisterTests(void) |
458 | | { |
459 | | UtRegisterTest("EngineEventTestParse01", EngineEventTestParse01); |
460 | | UtRegisterTest("EngineEventTestParse02", EngineEventTestParse02); |
461 | | UtRegisterTest("EngineEventTestParse03", EngineEventTestParse03); |
462 | | UtRegisterTest("EngineEventTestParse04", EngineEventTestParse04); |
463 | | UtRegisterTest("EngineEventTestParse05", EngineEventTestParse05); |
464 | | UtRegisterTest("EngineEventTestParse06", EngineEventTestParse06); |
465 | | } |
466 | | #endif /* UNITTESTS */ |