Coverage Report

Created: 2025-11-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/detect-bytemath.c
Line
Count
Source
1
/* Copyright (C) 2020-2022 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 Jeff Lucovsky <jeff@lucovsky.org>
22
 */
23
24
/*
25
 * Refer to the Snort manual, section 3.5.34 for details.
26
 */
27
28
#include "suricata-common.h"
29
#include "threads.h"
30
#include "decode.h"
31
32
#include "detect.h"
33
#include "detect-parse.h"
34
#include "detect-engine.h"
35
#include "detect-engine-mpm.h"
36
#include "detect-engine-state.h"
37
#include "detect-engine-build.h"
38
#include "detect-content.h"
39
#include "detect-pcre.h"
40
#include "detect-byte.h"
41
#include "detect-bytemath.h"
42
43
#include "app-layer-parser.h"
44
#include "app-layer-protos.h"
45
#include "rust-bindings.h"
46
47
#include "flow.h"
48
#include "flow-var.h"
49
#include "flow-util.h"
50
51
#include "util-byte.h"
52
#include "util-debug.h"
53
#include "util-unittest.h"
54
#include "util-unittest-helper.h"
55
#include "util-spm.h"
56
57
static int DetectByteMathSetup(DetectEngineCtx *, Signature *, const char *);
58
#ifdef UNITTESTS
59
static void DetectByteMathRegisterTests(void);
60
#endif
61
static void DetectByteMathFree(DetectEngineCtx *, void *);
62
63
#define DETECT_BYTEMATH_ENDIAN_DEFAULT (uint8_t) BigEndian
64
#define DETECT_BYTEMATH_BASE_DEFAULT   (uint8_t) BaseDec
65
/**
66
 * \brief Registers the keyword handlers for the "byte_math" keyword.
67
 */
68
void DetectBytemathRegister(void)
69
73
{
70
73
    sigmatch_table[DETECT_BYTEMATH].name = "byte_math";
71
73
    sigmatch_table[DETECT_BYTEMATH].Match = NULL;
72
73
    sigmatch_table[DETECT_BYTEMATH].Setup = DetectByteMathSetup;
73
73
    sigmatch_table[DETECT_BYTEMATH].Free = DetectByteMathFree;
74
#ifdef UNITTESTS
75
    sigmatch_table[DETECT_BYTEMATH].RegisterTests = DetectByteMathRegisterTests;
76
#endif
77
73
}
78
79
static inline bool DetectByteMathValidateNbytesOnly(const DetectByteMathData *data, int32_t nbytes)
80
4.27k
{
81
4.27k
    return nbytes >= 1 &&
82
4.27k
           (((data->flags & DETECT_BYTEMATH_FLAG_STRING) && nbytes <= 10) || (nbytes <= 4));
83
4.27k
}
84
85
int DetectByteMathDoMatch(DetectEngineThreadCtx *det_ctx, const SigMatchData *smd,
86
        const Signature *s, const uint8_t *payload, uint16_t payload_len, uint8_t nbytes,
87
        uint64_t rvalue, uint64_t *value, uint8_t endian)
88
4.27k
{
89
4.27k
    const DetectByteMathData *data = (DetectByteMathData *)smd->ctx;
90
4.27k
    if (payload_len == 0) {
91
0
        return 0;
92
0
    }
93
94
4.27k
    if (!DetectByteMathValidateNbytesOnly(data, nbytes)) {
95
0
        return 0;
96
0
    }
97
98
4.27k
    const uint8_t *ptr;
99
4.27k
    int32_t len;
100
4.27k
    uint64_t val;
101
4.27k
    int extbytes;
102
103
    /* Calculate the ptr value for the byte-math op and length remaining in
104
     * the packet from that point.
105
     */
106
4.27k
    if (data->flags & DETECT_BYTEMATH_FLAG_RELATIVE) {
107
0
        SCLogDebug("relative, working with det_ctx->buffer_offset %" PRIu32 ", "
108
0
                   "data->offset %" PRIi32 "",
109
0
                det_ctx->buffer_offset, data->offset);
110
111
0
        ptr = payload + det_ctx->buffer_offset;
112
0
        len = payload_len - det_ctx->buffer_offset;
113
114
0
        ptr += data->offset;
115
0
        len -= data->offset;
116
117
        /* No match if there is no relative base */
118
0
        if (len <= 0) {
119
0
            return 0;
120
0
        }
121
4.27k
    } else {
122
4.27k
        SCLogDebug("absolute, data->offset %" PRIi32 "", data->offset);
123
124
4.27k
        ptr = payload + data->offset;
125
4.27k
        len = payload_len - data->offset;
126
4.27k
    }
127
128
    /* Validate that the to-be-extracted is within the packet */
129
4.27k
    if (ptr < payload || nbytes > len) {
130
0
        SCLogDebug("Data not within payload pkt=%p, ptr=%p, len=%" PRIu32 ", nbytes=%d", payload,
131
0
                ptr, len, nbytes);
132
0
        return 0;
133
0
    }
134
135
    /* Extract the byte data */
136
4.27k
    if (data->flags & DETECT_BYTEMATH_FLAG_STRING) {
137
0
        extbytes = ByteExtractStringUint64(&val, data->base, nbytes, (const char *)ptr);
138
0
        if (extbytes <= 0) {
139
0
            if (val == 0) {
140
0
                SCLogDebug("No Numeric value");
141
0
                return 0;
142
0
            } else {
143
0
                SCLogDebug("error extracting %d bytes of string data: %d", nbytes, extbytes);
144
0
                return -1;
145
0
            }
146
0
        }
147
4.27k
    } else {
148
4.27k
        ByteMathEndian bme = endian;
149
4.27k
        int endianness = (bme == BigEndian) ? BYTE_BIG_ENDIAN : BYTE_LITTLE_ENDIAN;
150
4.27k
        extbytes = ByteExtractUint64(&val, endianness, nbytes, ptr);
151
4.27k
        if (extbytes != nbytes) {
152
0
            SCLogDebug("error extracting %d bytes of numeric data: %d", nbytes, extbytes);
153
0
            return 0;
154
0
        }
155
4.27k
    }
156
157
4.27k
    BUG_ON(extbytes > len);
158
159
4.27k
    ptr += extbytes;
160
161
4.27k
    switch (data->oper) {
162
0
        case OperatorNone:
163
0
            break;
164
541
        case Addition:
165
541
            val += rvalue;
166
541
            break;
167
3.64k
        case Subtraction:
168
3.64k
            val -= rvalue;
169
3.64k
            break;
170
7
        case Division:
171
7
            if (rvalue == 0) {
172
6
                SCLogDebug("avoiding division by zero");
173
6
                return 0;
174
6
            }
175
1
            val /= rvalue;
176
1
            break;
177
0
        case Multiplication:
178
0
            val *= rvalue;
179
0
            break;
180
83
        case LeftShift:
181
83
            if (rvalue < 64) {
182
38
                val <<= rvalue;
183
45
            } else {
184
45
                val = 0;
185
45
            }
186
83
            break;
187
0
        case RightShift:
188
0
            val >>= rvalue;
189
0
            break;
190
4.27k
    }
191
192
4.27k
    det_ctx->buffer_offset = ptr - payload;
193
194
4.27k
    if (data->flags & DETECT_BYTEMATH_FLAG_BITMASK) {
195
0
        val &= data->bitmask_val;
196
0
        if (val && data->bitmask_shift_count) {
197
0
            val = val >> data->bitmask_shift_count;
198
0
        }
199
0
    }
200
201
4.27k
    *value = val;
202
4.27k
    return 1;
203
4.27k
}
204
205
/**
206
 * \internal
207
 * \brief Used to parse byte_math arg.
208
 *
209
 * \param arg The argument to parse.
210
 * \param rvalue May be NULL. When non-null, will contain the variable
211
 *              name of rvalue (iff rvalue is not a scalar value)
212
 *
213
 * \retval bmd On success an instance containing the parsed data.
214
 *            On failure, NULL.
215
 */
216
static DetectByteMathData *DetectByteMathParse(
217
        DetectEngineCtx *de_ctx, const char *arg, char **nbytes, char **rvalue)
218
26.0k
{
219
26.0k
    DetectByteMathData *bmd;
220
26.0k
    if ((bmd = ScByteMathParse(arg)) == NULL) {
221
11.2k
        SCLogError("invalid bytemath values");
222
11.2k
        return NULL;
223
11.2k
    }
224
225
14.7k
    if (bmd->nbytes_str) {
226
751
        if (nbytes == NULL) {
227
0
            SCLogError("byte_math supplied with "
228
0
                       "var name for nbytes. \"nbytes\" argument supplied to "
229
0
                       "this function must be non-NULL");
230
0
            goto error;
231
0
        }
232
751
        *nbytes = SCStrdup(bmd->nbytes_str);
233
751
        if (*nbytes == NULL) {
234
0
            goto error;
235
0
        }
236
751
    }
237
238
14.7k
    if (bmd->rvalue_str) {
239
9.19k
        if (rvalue == NULL) {
240
0
            SCLogError("byte_math supplied with "
241
0
                       "var name for rvalue. \"rvalue\" argument supplied to "
242
0
                       "this function must be non-NULL");
243
0
            goto error;
244
0
        }
245
9.19k
        *rvalue = SCStrdup(bmd->rvalue_str);
246
9.19k
        if (*rvalue == NULL) {
247
0
            goto error;
248
0
        }
249
9.19k
    }
250
251
14.7k
    if (bmd->flags & DETECT_BYTEMATH_FLAG_BITMASK) {
252
407
        if (bmd->bitmask_val) {
253
224
            uint32_t bmask = bmd->bitmask_val;
254
1.47k
            while (!(bmask & 0x1)){
255
1.25k
                bmask = bmask >> 1;
256
1.25k
                bmd->bitmask_shift_count++;
257
1.25k
            }
258
224
        }
259
407
    }
260
261
14.7k
    return bmd;
262
263
0
 error:
264
0
    if (bmd != NULL)
265
0
        DetectByteMathFree(de_ctx, bmd);
266
0
    return NULL;
267
14.7k
}
268
269
/**
270
 * \brief The setup function for the byte_math keyword for a signature.
271
 *
272
 * \param de_ctx Pointer to the detection engine context.
273
 * \param s      Pointer to signature for the current Signature being parsed
274
 *               from the rules.
275
 * \param arg    Pointer to the string holding the keyword value.
276
 *
277
 * \retval  0 On success.
278
 * \retval -1 On failure.
279
 */
280
static int DetectByteMathSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
281
7.74k
{
282
7.74k
    SigMatch *sm = NULL;
283
7.74k
    SigMatch *prev_pm = NULL;
284
7.74k
    DetectByteMathData *data;
285
7.74k
    char *rvalue = NULL;
286
7.74k
    char *nbytes = NULL;
287
7.74k
    int ret = -1;
288
289
7.74k
    data = DetectByteMathParse(de_ctx, arg, &nbytes, &rvalue);
290
7.74k
    if (data == NULL)
291
3.12k
        goto error;
292
293
4.61k
    int sm_list;
294
4.61k
    if (s->init_data->list != DETECT_SM_LIST_NOTSET) {
295
1.60k
        if (DetectBufferGetActiveList(de_ctx, s) == -1)
296
1
            goto error;
297
298
1.60k
        sm_list = s->init_data->list;
299
300
1.60k
        if (data->flags & DETECT_BYTEMATH_FLAG_RELATIVE) {
301
260
            prev_pm = DetectGetLastSMFromLists(s, DETECT_CONTENT, DETECT_PCRE, -1);
302
260
            if (!prev_pm) {
303
2
                SCLogError("relative specified without "
304
2
                           "previous pattern match");
305
2
                goto error;
306
2
            }
307
260
        }
308
3.01k
    } else if (data->endian == EndianDCE) {
309
3
        if (data->flags & DETECT_BYTEMATH_FLAG_RELATIVE) {
310
2
            prev_pm = DetectGetLastSMFromLists(s, DETECT_CONTENT, DETECT_PCRE,
311
2
                                               DETECT_BYTETEST, DETECT_BYTEJUMP,
312
2
                                               DETECT_BYTE_EXTRACT,
313
2
                                               DETECT_BYTEMATH,
314
2
                                               DETECT_ISDATAAT, -1);
315
2
            if (prev_pm == NULL) {
316
1
                sm_list = DETECT_SM_LIST_PMATCH;
317
1
            } else {
318
1
                sm_list = SigMatchListSMBelongsTo(s, prev_pm);
319
1
                if (sm_list < 0)
320
0
                    goto error;
321
1
            }
322
2
        } else {
323
1
            sm_list = DETECT_SM_LIST_PMATCH;
324
1
        }
325
326
3
        if (DetectSignatureSetAppProto(s, ALPROTO_DCERPC) < 0)
327
3
            goto error;
328
0
        s->flags |= SIG_FLAG_APPLAYER;
329
330
3.01k
    } else if (data->flags & DETECT_BYTEMATH_FLAG_RELATIVE) {
331
554
        prev_pm = DetectGetLastSMFromLists(s, DETECT_CONTENT, DETECT_PCRE,
332
554
                                           DETECT_BYTETEST, DETECT_BYTEJUMP,
333
554
                                           DETECT_BYTE_EXTRACT, DETECT_BYTEMATH,
334
554
                                           DETECT_ISDATAAT, -1);
335
554
        if (prev_pm == NULL) {
336
63
            sm_list = DETECT_SM_LIST_PMATCH;
337
491
        } else {
338
491
            sm_list = SigMatchListSMBelongsTo(s, prev_pm);
339
491
            if (sm_list < 0)
340
0
                goto error;
341
491
            if (sm_list != DETECT_SM_LIST_PMATCH)
342
139
                s->flags |= SIG_FLAG_APPLAYER;
343
491
        }
344
345
2.46k
    } else {
346
2.46k
        sm_list = DETECT_SM_LIST_PMATCH;
347
2.46k
    }
348
349
4.61k
    if (data->endian == EndianDCE) {
350
1
        if (DetectSignatureSetAppProto(s, ALPROTO_DCERPC) != 0)
351
1
            goto error;
352
353
0
        if ((data->flags & DETECT_BYTEMATH_FLAG_STRING) || (data->base == BaseDec) ||
354
0
                (data->base == BaseHex) || (data->base == BaseOct)) {
355
0
            SCLogError("Invalid option. "
356
0
                       "A bytemath keyword with dce holds other invalid modifiers.");
357
0
            goto error;
358
0
        }
359
0
    }
360
361
4.61k
    if (nbytes != NULL) {
362
328
        DetectByteIndexType index;
363
328
        if (!DetectByteRetrieveSMVar(nbytes, s, &index)) {
364
85
            SCLogError("unknown byte_ keyword var seen in byte_math - %s", nbytes);
365
85
            goto error;
366
85
        }
367
243
        data->nbytes = index;
368
243
        data->flags |= DETECT_BYTEMATH_FLAG_NBYTES_VAR;
369
243
        SCFree(nbytes);
370
243
        nbytes = NULL;
371
243
    }
372
373
4.52k
    if (rvalue != NULL) {
374
2.29k
        DetectByteIndexType index;
375
2.29k
        if (!DetectByteRetrieveSMVar(rvalue, s, &index)) {
376
471
            SCLogError("unknown byte_ keyword var seen in byte_math - %s", rvalue);
377
471
            goto error;
378
471
        }
379
1.82k
        data->rvalue = index;
380
1.82k
        data->flags |= DETECT_BYTEMATH_FLAG_RVALUE_VAR;
381
1.82k
        SCFree(rvalue);
382
1.82k
        rvalue = NULL;
383
1.82k
    }
384
385
4.05k
    SigMatch *prev_bmd_sm = DetectGetLastSMByListId(s, sm_list,
386
4.05k
            DETECT_BYTEMATH, -1);
387
4.05k
    if (prev_bmd_sm == NULL) {
388
1.22k
        data->local_id = 0;
389
2.83k
    } else {
390
2.83k
        data->local_id = ((DetectByteMathData *)prev_bmd_sm->ctx)->local_id + 1;
391
2.83k
    }
392
4.05k
    if (data->local_id > de_ctx->byte_extract_max_local_id) {
393
132
        de_ctx->byte_extract_max_local_id = data->local_id;
394
132
    }
395
396
4.05k
    sm = SigMatchAlloc();
397
4.05k
    if (sm == NULL)
398
0
        goto error;
399
4.05k
    sm->type = DETECT_BYTEMATH;
400
4.05k
    sm->ctx = (void *)data;
401
4.05k
    SigMatchAppendSMToList(s, sm, sm_list);
402
403
4.05k
    if (!(data->flags & DETECT_BYTEMATH_FLAG_RELATIVE))
404
3.27k
        goto okay;
405
406
785
    if (prev_pm == NULL)
407
63
        goto okay;
408
409
722
    if (prev_pm->type == DETECT_CONTENT) {
410
136
        DetectContentData *cd = (DetectContentData *)prev_pm->ctx;
411
136
        cd->flags |= DETECT_CONTENT_RELATIVE_NEXT;
412
586
    } else if (prev_pm->type == DETECT_PCRE) {
413
171
        DetectPcreData *pd = (DetectPcreData *)prev_pm->ctx;
414
171
        pd->flags |= DETECT_PCRE_RELATIVE_NEXT;
415
171
    }
416
417
4.05k
 okay:
418
4.05k
    return 0;
419
420
3.69k
 error:
421
3.69k
    if (rvalue)
422
550
        SCFree(rvalue);
423
3.69k
    if (nbytes)
424
89
        SCFree(nbytes);
425
3.69k
    DetectByteMathFree(de_ctx, data);
426
3.69k
    return ret;
427
722
}
428
429
/**
430
 * \brief Used to free instances of DetectByteMathractData.
431
 *
432
 * \param ptr Instance of DetectByteMathData to be freed.
433
 */
434
static void DetectByteMathFree(DetectEngineCtx *de_ctx, void *ptr)
435
26.0k
{
436
26.0k
    ScByteMathFree(ptr);
437
26.0k
}
438
439
/**
440
 * \brief Lookup the SigMatch for a named byte_math variable.
441
 *
442
 * \param arg The name of the byte_math variable to lookup.
443
 * \param s Pointer the signature to look in.
444
 *
445
 * \retval A pointer to the SigMatch if found, otherwise NULL.
446
 */
447
SigMatch *DetectByteMathRetrieveSMVar(const char *arg, const Signature *s)
448
6.05k
{
449
9.54k
    for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
450
4.19k
        SigMatch *sm = s->init_data->buffers[x].head;
451
16.2k
        while (sm != NULL) {
452
12.7k
            if (sm->type == DETECT_BYTEMATH) {
453
1.12k
                const DetectByteMathData *bmd = (const DetectByteMathData *)sm->ctx;
454
1.12k
                if (strcmp(bmd->result, arg) == 0) {
455
702
                    SCLogDebug("Retrieved SM for \"%s\"", arg);
456
702
                    return sm;
457
702
                }
458
1.12k
            }
459
12.0k
            sm = sm->next;
460
12.0k
        }
461
4.19k
    }
462
463
32.5k
    for (int list = 0; list < DETECT_SM_LIST_MAX; list++) {
464
28.9k
        SigMatch *sm = s->init_data->smlists[list];
465
39.8k
        while (sm != NULL) {
466
12.6k
            if (sm->type == DETECT_BYTEMATH) {
467
2.12k
                const DetectByteMathData *bmd = (const DetectByteMathData *)sm->ctx;
468
2.12k
                if (strcmp(bmd->result, arg) == 0) {
469
1.70k
                    SCLogDebug("Retrieved SM for \"%s\"", arg);
470
1.70k
                    return sm;
471
1.70k
                }
472
2.12k
            }
473
10.8k
            sm = sm->next;
474
10.8k
        }
475
28.9k
    }
476
477
3.64k
    return NULL;
478
5.34k
}
479
480
/*************************************Unittests********************************/
481
#ifdef UNITTESTS
482
#include "detect-engine-alert.h"
483
484
static int DetectByteMathParseTest01(void)
485
{
486
487
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
488
            "bytes 4, offset 2, oper +,"
489
            "rvalue 10, result bar",
490
            NULL, NULL);
491
    FAIL_IF(bmd == NULL);
492
493
    FAIL_IF_NOT(bmd->nbytes == 4);
494
    FAIL_IF_NOT(bmd->offset == 2);
495
    FAIL_IF_NOT(bmd->oper == Addition);
496
    FAIL_IF_NOT(bmd->rvalue == 10);
497
    FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
498
    FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_DEFAULT);
499
    FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEFAULT);
500
501
    DetectByteMathFree(NULL, bmd);
502
503
    PASS;
504
}
505
506
static int DetectByteMathParseTest02(void)
507
{
508
    /* bytes value invalid */
509
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
510
            "bytes 257, offset 2, oper +, "
511
            "rvalue 39, result bar",
512
            NULL, NULL);
513
514
    FAIL_IF_NOT(bmd == NULL);
515
516
    PASS;
517
}
518
519
static int DetectByteMathParseTest03(void)
520
{
521
    /* bytes value invalid */
522
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
523
            "bytes 11, offset 2, oper +, "
524
            "rvalue 39, result bar",
525
            NULL, NULL);
526
    FAIL_IF_NOT(bmd == NULL);
527
528
    PASS;
529
}
530
531
static int DetectByteMathParseTest04(void)
532
{
533
    /* offset value invalid */
534
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
535
            "bytes 4, offset 70000, oper +,"
536
            " rvalue 39, result bar",
537
            NULL, NULL);
538
539
    FAIL_IF_NOT(bmd == NULL);
540
541
    PASS;
542
}
543
544
static int DetectByteMathParseTest05(void)
545
{
546
    /* oper value invalid */
547
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
548
            "bytes 11, offset 16, oper &,"
549
            "rvalue 39, result bar",
550
            NULL, NULL);
551
    FAIL_IF_NOT(bmd == NULL);
552
553
    PASS;
554
}
555
556
static int DetectByteMathParseTest06(void)
557
{
558
    uint8_t flags = DETECT_BYTEMATH_FLAG_RELATIVE;
559
    char *rvalue = NULL;
560
561
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
562
            "bytes 4, offset 0, oper +,"
563
            "rvalue 248, result var, relative",
564
            NULL, &rvalue);
565
566
    FAIL_IF(bmd == NULL);
567
    FAIL_IF_NOT(bmd->nbytes == 4);
568
    FAIL_IF_NOT(bmd->offset == 0);
569
    FAIL_IF_NOT(bmd->oper == Addition);
570
    FAIL_IF_NOT(bmd->rvalue == 248);
571
    FAIL_IF_NOT(strcmp(bmd->result, "var") == 0);
572
    FAIL_IF_NOT(bmd->flags == flags);
573
    FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_DEFAULT);
574
    FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEFAULT);
575
576
    DetectByteMathFree(NULL, bmd);
577
578
    PASS;
579
}
580
581
static int DetectByteMathParseTest07(void)
582
{
583
    char *rvalue = NULL;
584
585
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
586
            "bytes 4, offset 2, oper +,"
587
            "rvalue foo, result bar",
588
            NULL, &rvalue);
589
    FAIL_IF_NOT(rvalue);
590
    FAIL_IF_NOT(bmd->nbytes == 4);
591
    FAIL_IF_NOT(bmd->offset == 2);
592
    FAIL_IF_NOT(bmd->oper == Addition);
593
    FAIL_IF_NOT(strcmp(rvalue, "foo") == 0);
594
    FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
595
    FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_DEFAULT);
596
    FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEFAULT);
597
598
    DetectByteMathFree(NULL, bmd);
599
600
    SCFree(rvalue);
601
602
    PASS;
603
}
604
605
static int DetectByteMathParseTest08(void)
606
{
607
    /* ensure Parse checks the pointer value when rvalue is a var */
608
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
609
            "bytes 4, offset 2, oper +,"
610
            "rvalue foo, result bar",
611
            NULL, NULL);
612
    FAIL_IF_NOT(bmd == NULL);
613
614
    PASS;
615
}
616
617
static int DetectByteMathParseTest09(void)
618
{
619
    uint8_t flags = DETECT_BYTEMATH_FLAG_RELATIVE;
620
621
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
622
            "bytes 4, offset 2, oper +,"
623
            "rvalue 39, result bar, relative",
624
            NULL, NULL);
625
    FAIL_IF(bmd == NULL);
626
627
    FAIL_IF_NOT(bmd->nbytes == 4);
628
    FAIL_IF_NOT(bmd->offset == 2);
629
    FAIL_IF_NOT(bmd->oper == Addition);
630
    FAIL_IF_NOT(bmd->rvalue == 39);
631
    FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
632
    FAIL_IF_NOT(bmd->flags == flags);
633
    FAIL_IF_NOT(bmd->endian == DETECT_BYTEMATH_ENDIAN_DEFAULT);
634
    FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEFAULT);
635
636
    DetectByteMathFree(NULL, bmd);
637
638
    PASS;
639
}
640
641
static int DetectByteMathParseTest10(void)
642
{
643
    uint8_t flags = DETECT_BYTEMATH_FLAG_ENDIAN;
644
645
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
646
            "bytes 4, offset 2, oper +,"
647
            "rvalue 39, result bar, endian"
648
            " big",
649
            NULL, NULL);
650
651
    FAIL_IF(bmd == NULL);
652
    FAIL_IF_NOT(bmd->nbytes == 4);
653
    FAIL_IF_NOT(bmd->offset == 2);
654
    FAIL_IF_NOT(bmd->oper == Addition);
655
    FAIL_IF_NOT(bmd->rvalue == 39);
656
    FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
657
    FAIL_IF_NOT(bmd->flags == flags);
658
    FAIL_IF_NOT(bmd->endian == BigEndian);
659
    FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEFAULT);
660
661
    DetectByteMathFree(NULL, bmd);
662
663
    PASS;
664
}
665
666
static int DetectByteMathParseTest11(void)
667
{
668
    uint8_t flags = DETECT_BYTEMATH_FLAG_ENDIAN;
669
670
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
671
            "bytes 4, offset 2, oper +, "
672
            "rvalue 39, result bar, dce",
673
            NULL, NULL);
674
675
    FAIL_IF(bmd == NULL);
676
    FAIL_IF_NOT(bmd->nbytes == 4);
677
    FAIL_IF_NOT(bmd->offset == 2);
678
    FAIL_IF_NOT(bmd->oper == Addition);
679
    FAIL_IF_NOT(bmd->rvalue == 39);
680
    FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
681
    FAIL_IF_NOT(bmd->flags == flags);
682
    FAIL_IF_NOT(bmd->endian == EndianDCE);
683
    FAIL_IF_NOT(bmd->base == DETECT_BYTEMATH_BASE_DEFAULT);
684
685
    DetectByteMathFree(NULL, bmd);
686
687
    PASS;
688
}
689
690
static int DetectByteMathParseTest12(void)
691
{
692
    uint8_t flags = DETECT_BYTEMATH_FLAG_RELATIVE | DETECT_BYTEMATH_FLAG_STRING;
693
694
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
695
            "bytes 4, offset 2, oper +,"
696
            "rvalue 39, result bar, "
697
            "relative, string dec",
698
            NULL, NULL);
699
700
    FAIL_IF(bmd == NULL);
701
    FAIL_IF_NOT(bmd->nbytes == 4);
702
    FAIL_IF_NOT(bmd->offset == 2);
703
    FAIL_IF_NOT(bmd->oper == Addition);
704
    FAIL_IF_NOT(bmd->rvalue == 39);
705
    FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
706
    FAIL_IF_NOT(bmd->flags == flags);
707
    FAIL_IF_NOT(bmd->endian == BigEndian);
708
    FAIL_IF_NOT(bmd->base == BaseDec);
709
710
    DetectByteMathFree(NULL, bmd);
711
712
    PASS;
713
}
714
715
static int DetectByteMathParseTest13(void)
716
{
717
    uint8_t flags = DETECT_BYTEMATH_FLAG_STRING |
718
                    DETECT_BYTEMATH_FLAG_RELATIVE |
719
                    DETECT_BYTEMATH_FLAG_BITMASK;
720
721
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
722
            "bytes 4, offset 2, oper +, "
723
            "rvalue 39, result bar, "
724
            "relative,  string dec, bitmask "
725
            "0x8f40",
726
            NULL, NULL);
727
728
    FAIL_IF(bmd == NULL);
729
    FAIL_IF_NOT(bmd->nbytes == 4);
730
    FAIL_IF_NOT(bmd->offset == 2);
731
    FAIL_IF_NOT(bmd->oper == Addition);
732
    FAIL_IF_NOT(bmd->rvalue == 39);
733
    FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
734
    FAIL_IF_NOT(bmd->bitmask_val == 0x8f40);
735
    FAIL_IF_NOT(bmd->bitmask_shift_count == 6);
736
    FAIL_IF_NOT(bmd->flags == flags);
737
    FAIL_IF_NOT(bmd->endian == BigEndian);
738
    FAIL_IF_NOT(bmd->base == BaseDec);
739
740
    DetectByteMathFree(NULL, bmd);
741
742
    PASS;
743
}
744
745
746
static int DetectByteMathParseTest14(void)
747
{
748
    /* incomplete */
749
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
750
            "bytes 4, offset 2, oper +,"
751
            "rvalue foo",
752
            NULL, NULL);
753
754
    FAIL_IF_NOT(bmd == NULL);
755
756
    PASS;
757
}
758
759
static int DetectByteMathParseTest15(void)
760
{
761
762
    /* incomplete */
763
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
764
            "bytes 4, offset 2, oper +, "
765
            "result bar",
766
            NULL, NULL);
767
768
    FAIL_IF_NOT(bmd == NULL);
769
770
    PASS;
771
}
772
773
static int DetectByteMathParseTest16(void)
774
{
775
    uint8_t flags = DETECT_BYTEMATH_FLAG_STRING | DETECT_BYTEMATH_FLAG_RELATIVE |
776
                    DETECT_BYTEMATH_FLAG_BITMASK;
777
778
    DetectByteMathData *bmd = DetectByteMathParse(NULL,
779
            "bytes 4, offset -2, oper +, "
780
            "rvalue 39, result bar, "
781
            "relative,  string dec, bitmask "
782
            "0x8f40",
783
            NULL, NULL);
784
785
    FAIL_IF(bmd == NULL);
786
    FAIL_IF_NOT(bmd->nbytes == 4);
787
    FAIL_IF_NOT(bmd->offset == -2);
788
    FAIL_IF_NOT(bmd->oper == Addition);
789
    FAIL_IF_NOT(bmd->rvalue == 39);
790
    FAIL_IF_NOT(strcmp(bmd->result, "bar") == 0);
791
    FAIL_IF_NOT(bmd->bitmask_val == 0x8f40);
792
    FAIL_IF_NOT(bmd->bitmask_shift_count == 6);
793
    FAIL_IF_NOT(bmd->flags == flags);
794
    FAIL_IF_NOT(bmd->endian == BigEndian);
795
    FAIL_IF_NOT(bmd->base == BaseDec);
796
797
    DetectByteMathFree(NULL, bmd);
798
799
    PASS;
800
}
801
802
static int DetectByteMathPacket01(void)
803
{
804
    uint8_t buf[] = { 0x38, 0x35, 0x6d, 0x00, 0x00, 0x01,
805
                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
806
                      0x00, 0x00, 0x6d, 0x00, 0x01, 0x00 };
807
    Flow f;
808
    void *dns_state = NULL;
809
    Packet *p = NULL;
810
    Signature *s = NULL;
811
    ThreadVars tv;
812
    DetectEngineThreadCtx *det_ctx = NULL;
813
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
814
815
    memset(&tv, 0, sizeof(ThreadVars));
816
    memset(&f, 0, sizeof(Flow));
817
818
    p = UTHBuildPacketReal(buf, sizeof(buf), IPPROTO_UDP,
819
                           "192.168.1.5", "192.168.1.1",
820
                           41424, 53);
821
    FAIL_IF_NULL(p);
822
823
    FLOW_INITIALIZE(&f);
824
    f.flags |= FLOW_IPV4;
825
    f.proto = IPPROTO_UDP;
826
    f.protomap = FlowGetProtoMapping(f.proto);
827
828
    p->flow = &f;
829
    p->flags |= PKT_HAS_FLOW;
830
    p->flowflags |= FLOW_PKT_TOSERVER;
831
    f.alproto = ALPROTO_DNS;
832
833
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
834
    FAIL_IF_NULL(de_ctx);
835
836
    de_ctx->mpm_matcher = mpm_default_matcher;
837
    de_ctx->flags |= DE_QUIET;
838
839
    /*
840
     * byte_extract: Extract 1 byte from offset 0 --> 0x0038
841
     * byte_math: Extract 1 byte from offset 2 (0x35)
842
     *            Add 0x35 + 0x38 = 109 (0x6d)
843
     * byte_test: Compare 2 bytes at offset 13 bytes from last
844
     *            match and compare with 0x6d
845
     */
846
    s = DetectEngineAppendSig(de_ctx, "alert udp any any -> any any "
847
                              "(byte_extract: 1, 0, extracted_val, relative;"
848
                              "byte_math: bytes 1, offset 1, oper +, rvalue extracted_val, result var;"
849
                              "byte_test: 2, =, var, 13;"
850
                              "msg:\"Byte extract and byte math with byte test verification\";"
851
                              "sid:1;)");
852
    FAIL_IF_NULL(s);
853
854
    /* this rule should not alert */
855
    s = DetectEngineAppendSig(de_ctx, "alert udp any any -> any any "
856
                              "(byte_extract: 1, 0, extracted_val, relative;"
857
                              "byte_math: bytes 1, offset 1, oper +, rvalue extracted_val, result var;"
858
                              "byte_test: 2, !=, var, 13;"
859
                              "msg:\"Byte extract and byte math with byte test verification\";"
860
                              "sid:2;)");
861
    FAIL_IF_NULL(s);
862
863
    /*
864
     * this rule should alert:
865
     * compares offset 15 with var ... 1 (offset 15) < 0x6d (var)
866
     */
867
    s = DetectEngineAppendSig(de_ctx, "alert udp any any -> any any "
868
                              "(byte_extract: 1, 0, extracted_val, relative;"
869
                              "byte_math: bytes 1, offset 1, oper +, rvalue extracted_val, result var;"
870
                              "byte_test: 2, <, var, 15;"
871
                              "msg:\"Byte extract and byte math with byte test verification\";"
872
                              "sid:3;)");
873
    FAIL_IF_NULL(s);
874
875
    SigGroupBuild(de_ctx);
876
    DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
877
    FAIL_IF_NULL(det_ctx);
878
879
    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNS,
880
                                STREAM_TOSERVER, buf, sizeof(buf));
881
    FAIL_IF_NOT(r == 0);
882
883
    dns_state = f.alstate;
884
    FAIL_IF_NULL(dns_state);
885
886
    /* do detect */
887
    SigMatchSignatures(&tv, de_ctx, det_ctx, p);
888
889
    /* ensure sids 1 & 3 alerted */
890
    FAIL_IF_NOT(PacketAlertCheck(p, 1));
891
    FAIL_IF(PacketAlertCheck(p, 2));
892
    FAIL_IF_NOT(PacketAlertCheck(p, 3));
893
894
    AppLayerParserThreadCtxFree(alp_tctx);
895
    DetectEngineThreadCtxDeinit(&tv, det_ctx);
896
    DetectEngineCtxFree(de_ctx);
897
898
    FLOW_DESTROY(&f);
899
    UTHFreePacket(p);
900
901
    PASS;
902
}
903
904
static int DetectByteMathPacket02(void)
905
{
906
    uint8_t buf[] = { 0x38, 0x35, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
907
        0x00, 0x70, 0x00, 0x01, 0x00 };
908
    Flow f;
909
    void *dns_state = NULL;
910
    Packet *p = NULL;
911
    Signature *s = NULL;
912
    ThreadVars tv;
913
    DetectEngineThreadCtx *det_ctx = NULL;
914
    AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
915
916
    memset(&tv, 0, sizeof(ThreadVars));
917
    memset(&f, 0, sizeof(Flow));
918
919
    p = UTHBuildPacketReal(buf, sizeof(buf), IPPROTO_UDP, "192.168.1.5", "192.168.1.1", 41424, 53);
920
    FAIL_IF_NULL(p);
921
922
    FLOW_INITIALIZE(&f);
923
    f.flags |= FLOW_IPV4;
924
    f.proto = IPPROTO_UDP;
925
    f.protomap = FlowGetProtoMapping(f.proto);
926
927
    p->flow = &f;
928
    p->flags |= PKT_HAS_FLOW;
929
    p->flowflags |= FLOW_PKT_TOSERVER;
930
    f.alproto = ALPROTO_DNS;
931
932
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
933
    FAIL_IF_NULL(de_ctx);
934
935
    de_ctx->mpm_matcher = mpm_default_matcher;
936
    de_ctx->flags |= DE_QUIET;
937
938
    /*
939
     * byte_extract: Extract 1 byte from offset 0 --> 0x38
940
     * byte_math: Extract 1 byte from offset -1 (0x38)
941
     *            Add 0x38 + 0x38 = 112 (0x70)
942
     * byte_test: Compare 2 bytes at offset 13 bytes from last
943
     *            match and compare with 0x70
944
     */
945
    s = DetectEngineAppendSig(de_ctx,
946
            "alert udp any any -> any any "
947
            "(byte_extract: 1, 0, extracted_val, relative;"
948
            "byte_math: bytes 1, offset -1, oper +, rvalue extracted_val, result var, relative;"
949
            "byte_test: 2, =, var, 13;"
950
            "msg:\"Byte extract and byte math with byte test verification\";"
951
            "sid:1;)");
952
    FAIL_IF_NULL(s);
953
954
    /* this rule should not alert */
955
    s = DetectEngineAppendSig(de_ctx,
956
            "alert udp any any -> any any "
957
            "(byte_extract: 1, 0, extracted_val, relative;"
958
            "byte_math: bytes 1, offset -1, oper +,  rvalue extracted_val, result var, relative;"
959
            "byte_test: 2, !=, var, 13;"
960
            "msg:\"Byte extract and byte math with byte test verification\";"
961
            "sid:2;)");
962
    FAIL_IF_NULL(s);
963
964
    /*
965
     * this rule should alert:
966
     * compares offset 15 with var ... 1 (offset 15) < 0x70 (var)
967
     */
968
    s = DetectEngineAppendSig(de_ctx,
969
            "alert udp any any -> any any "
970
            "(byte_extract: 1, 0, extracted_val, relative;"
971
            "byte_math: bytes 1, offset -1, oper +, rvalue extracted_val, result var, relative;"
972
            "byte_test: 2, <, var, 15;"
973
            "msg:\"Byte extract and byte math with byte test verification\";"
974
            "sid:3;)");
975
    FAIL_IF_NULL(s);
976
977
    SigGroupBuild(de_ctx);
978
    DetectEngineThreadCtxInit(&tv, (void *)de_ctx, (void *)&det_ctx);
979
    FAIL_IF_NULL(det_ctx);
980
981
    int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_DNS, STREAM_TOSERVER, buf, sizeof(buf));
982
    FAIL_IF_NOT(r == 0);
983
984
    dns_state = f.alstate;
985
    FAIL_IF_NULL(dns_state);
986
987
    /* do detect */
988
    SigMatchSignatures(&tv, de_ctx, det_ctx, p);
989
990
    /* ensure sids 1 & 3 alerted */
991
    FAIL_IF_NOT(PacketAlertCheck(p, 1));
992
    FAIL_IF(PacketAlertCheck(p, 2));
993
    FAIL_IF_NOT(PacketAlertCheck(p, 3));
994
995
    AppLayerParserThreadCtxFree(alp_tctx);
996
    DetectEngineThreadCtxDeinit(&tv, det_ctx);
997
    DetectEngineCtxFree(de_ctx);
998
999
    FLOW_DESTROY(&f);
1000
    UTHFreePacket(p);
1001
1002
    PASS;
1003
}
1004
1005
static int DetectByteMathContext01(void)
1006
{
1007
    DetectEngineCtx *de_ctx = NULL;
1008
    Signature *s = NULL;
1009
    SigMatch *sm = NULL;
1010
    DetectContentData *cd = NULL;
1011
    DetectByteMathData *bmd = NULL;
1012
1013
    de_ctx = DetectEngineCtxInit();
1014
    FAIL_IF(de_ctx == NULL);
1015
1016
    de_ctx->flags |= DE_QUIET;
1017
    s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
1018
                                           "(msg:\"Testing bytemath_body\"; "
1019
                                           "content:\"|00 04 93 F3|\"; "
1020
                                           "content:\"|00 00 00 07|\"; distance:4; within:4;"
1021
                                           "byte_math:bytes 4, offset 0, oper +, rvalue "
1022
                                           "248, result var, relative; sid:1;)");
1023
1024
    FAIL_IF(de_ctx->sig_list == NULL);
1025
1026
    FAIL_IF(s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH] == NULL);
1027
1028
    sm = s->init_data->smlists[DETECT_SM_LIST_PMATCH];
1029
    FAIL_IF(sm->type != DETECT_CONTENT);
1030
    cd = (DetectContentData *)sm->ctx;
1031
    FAIL_IF(cd->flags & DETECT_CONTENT_WITHIN);
1032
    FAIL_IF(cd->flags & DETECT_CONTENT_DISTANCE);
1033
    FAIL_IF(cd->content_len != 4);
1034
1035
    sm = sm->next;
1036
    FAIL_IF(sm->type != DETECT_CONTENT);
1037
    sm = sm->next;
1038
    FAIL_IF(sm->type != DETECT_BYTEMATH);
1039
1040
    FAIL_IF(sm->ctx == NULL);
1041
1042
    bmd = (DetectByteMathData *)sm->ctx;
1043
    FAIL_IF_NOT(bmd->nbytes == 4);
1044
    FAIL_IF_NOT(bmd->offset == 0);
1045
    FAIL_IF_NOT(bmd->rvalue == 248);
1046
    FAIL_IF_NOT(strcmp(bmd->result, "var") == 0);
1047
    FAIL_IF_NOT(bmd->flags == DETECT_BYTEMATH_FLAG_RELATIVE);
1048
    FAIL_IF_NOT(bmd->endian == BigEndian);
1049
    FAIL_IF_NOT(bmd->oper == Addition);
1050
    FAIL_IF_NOT(bmd->base == BaseDec);
1051
1052
    DetectEngineCtxFree(de_ctx);
1053
1054
    PASS;
1055
}
1056
1057
static void DetectByteMathRegisterTests(void)
1058
{
1059
    UtRegisterTest("DetectByteMathParseTest01", DetectByteMathParseTest01);
1060
    UtRegisterTest("DetectByteMathParseTest02", DetectByteMathParseTest02);
1061
    UtRegisterTest("DetectByteMathParseTest03", DetectByteMathParseTest03);
1062
    UtRegisterTest("DetectByteMathParseTest04", DetectByteMathParseTest04);
1063
    UtRegisterTest("DetectByteMathParseTest05", DetectByteMathParseTest05);
1064
    UtRegisterTest("DetectByteMathParseTest06", DetectByteMathParseTest06);
1065
    UtRegisterTest("DetectByteMathParseTest07", DetectByteMathParseTest07);
1066
    UtRegisterTest("DetectByteMathParseTest08", DetectByteMathParseTest08);
1067
    UtRegisterTest("DetectByteMathParseTest09", DetectByteMathParseTest09);
1068
    UtRegisterTest("DetectByteMathParseTest10", DetectByteMathParseTest10);
1069
    UtRegisterTest("DetectByteMathParseTest11", DetectByteMathParseTest11);
1070
    UtRegisterTest("DetectByteMathParseTest12", DetectByteMathParseTest12);
1071
    UtRegisterTest("DetectByteMathParseTest13", DetectByteMathParseTest13);
1072
    UtRegisterTest("DetectByteMathParseTest14", DetectByteMathParseTest14);
1073
    UtRegisterTest("DetectByteMathParseTest15", DetectByteMathParseTest15);
1074
    UtRegisterTest("DetectByteMathParseTest16", DetectByteMathParseTest16);
1075
    UtRegisterTest("DetectByteMathPacket01", DetectByteMathPacket01);
1076
    UtRegisterTest("DetectByteMathPacket02", DetectByteMathPacket02);
1077
    UtRegisterTest("DetectByteMathContext01", DetectByteMathContext01);
1078
}
1079
#endif /* UNITTESTS */