Coverage Report

Created: 2026-09-06 07:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 */