Coverage Report

Created: 2026-09-28 07:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/src/detect-xbits.c
Line
Count
Source
1
/* Copyright (C) 2007-2020 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 the xbits keyword
24
 */
25
26
#include "suricata-common.h"
27
#include "decode.h"
28
#include "action-globals.h"
29
#include "detect.h"
30
#include "threads.h"
31
#include "flow.h"
32
#include "flow-util.h"
33
#include "detect-xbits.h"
34
#include "detect-hostbits.h"
35
#include "util-spm.h"
36
#include "util-byte.h"
37
38
#include "detect-engine-sigorder.h"
39
40
#include "app-layer-parser.h"
41
42
#include "detect-parse.h"
43
#include "detect-engine.h"
44
#include "detect-engine-mpm.h"
45
#include "detect-engine-state.h"
46
#include "detect-engine-build.h"
47
48
#include "flow-bit.h"
49
#include "host-bit.h"
50
#include "ippair-bit.h"
51
#include "tx-bit.h"
52
53
#include "util-var-name.h"
54
#include "util-unittest.h"
55
#include "util-debug.h"
56
57
/*
58
    xbits:set,bitname,track ip_pair,expire 60
59
 */
60
61
static int DetectXbitTxMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state,
62
        void *txv, const Signature *s, const SigMatchCtx *ctx);
63
static int DetectXbitMatch (DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *);
64
static int DetectXbitSetup (DetectEngineCtx *, Signature *, const char *);
65
#ifdef UNITTESTS
66
static void XBitsRegisterTests(void);
67
#endif
68
static void DetectXbitFree (DetectEngineCtx *, void *);
69
70
void DetectXbitsRegister (void)
71
79
{
72
79
    sigmatch_table[DETECT_XBITS].name = "xbits";
73
79
    sigmatch_table[DETECT_XBITS].desc = "operate on bits";
74
79
    sigmatch_table[DETECT_XBITS].url = "/rules/xbits.html";
75
79
    sigmatch_table[DETECT_XBITS].AppLayerTxMatch = DetectXbitTxMatch;
76
79
    sigmatch_table[DETECT_XBITS].Match = DetectXbitMatch;
77
79
    sigmatch_table[DETECT_XBITS].Setup = DetectXbitSetup;
78
79
    sigmatch_table[DETECT_XBITS].Free  = DetectXbitFree;
79
#ifdef UNITTESTS
80
    sigmatch_table[DETECT_XBITS].RegisterTests = XBitsRegisterTests;
81
#endif
82
    /* this is compatible to ip-only signatures */
83
79
    sigmatch_table[DETECT_XBITS].flags |= (SIGMATCH_IPONLY_COMPAT | SIGMATCH_SUPPORT_FIREWALL);
84
79
}
85
86
static int DetectIPPairbitMatchToggle (Packet *p, const DetectXbitsData *fd)
87
0
{
88
0
    IPPair *pair = IPPairGetIPPairFromHash(&p->src, &p->dst);
89
0
    if (pair == NULL)
90
0
        return 0;
91
92
0
    IPPairBitToggle(pair, fd->idx, SCTIME_ADD_SECS(p->ts, fd->expire));
93
0
    IPPairRelease(pair);
94
0
    return 1;
95
0
}
96
97
/* return true even if bit not found */
98
static int DetectIPPairbitMatchUnset (Packet *p, const DetectXbitsData *fd)
99
0
{
100
0
    IPPair *pair = IPPairLookupIPPairFromHash(&p->src, &p->dst);
101
0
    if (pair == NULL)
102
0
        return 1;
103
104
0
    IPPairBitUnset(pair,fd->idx);
105
0
    IPPairRelease(pair);
106
0
    return 1;
107
0
}
108
109
static int DetectIPPairbitMatchSet (Packet *p, const DetectXbitsData *fd)
110
86
{
111
86
    IPPair *pair = IPPairGetIPPairFromHash(&p->src, &p->dst);
112
86
    if (pair == NULL)
113
0
        return 0;
114
115
86
    IPPairBitSet(pair, fd->idx, SCTIME_ADD_SECS(p->ts, fd->expire));
116
86
    IPPairRelease(pair);
117
86
    return 1;
118
86
}
119
120
static int DetectIPPairbitMatchIsset (Packet *p, const DetectXbitsData *fd)
121
138
{
122
138
    int r = 0;
123
138
    IPPair *pair = IPPairLookupIPPairFromHash(&p->src, &p->dst);
124
138
    if (pair == NULL)
125
76
        return 0;
126
127
62
    r = IPPairBitIsset(pair, fd->idx, p->ts);
128
62
    IPPairRelease(pair);
129
62
    return r;
130
138
}
131
132
static int DetectIPPairbitMatchIsnotset (Packet *p, const DetectXbitsData *fd)
133
0
{
134
0
    int r = 0;
135
0
    IPPair *pair = IPPairLookupIPPairFromHash(&p->src, &p->dst);
136
0
    if (pair == NULL)
137
0
        return 1;
138
139
0
    r = IPPairBitIsnotset(pair, fd->idx, p->ts);
140
0
    IPPairRelease(pair);
141
0
    return r;
142
0
}
143
144
static int DetectXbitMatchIPPair(Packet *p, const DetectXbitsData *xd)
145
224
{
146
224
    switch (xd->cmd) {
147
138
        case DETECT_XBITS_CMD_ISSET:
148
138
            return DetectIPPairbitMatchIsset(p,xd);
149
0
        case DETECT_XBITS_CMD_ISNOTSET:
150
0
            return DetectIPPairbitMatchIsnotset(p,xd);
151
86
        case DETECT_XBITS_CMD_SET:
152
86
            return DetectIPPairbitMatchSet(p,xd);
153
0
        case DETECT_XBITS_CMD_UNSET:
154
0
            return DetectIPPairbitMatchUnset(p,xd);
155
0
        case DETECT_XBITS_CMD_TOGGLE:
156
0
            return DetectIPPairbitMatchToggle(p,xd);
157
224
    }
158
0
    return 0;
159
224
}
160
161
static int DetectXbitPostMatchTx(
162
        DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const DetectXbitsData *xd)
163
0
{
164
0
    if (p->flow == NULL)
165
0
        return 0;
166
0
    if (!det_ctx->tx_id_set)
167
0
        return 0;
168
0
    Flow *f = p->flow;
169
0
    void *txv = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, det_ctx->tx_id);
170
0
    if (txv == NULL)
171
0
        return 0;
172
0
    AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, txv);
173
174
0
    if (xd->cmd != DETECT_XBITS_CMD_SET)
175
0
        return 0;
176
177
0
    SCLogDebug("sid %u: post-match SET for bit %u on tx:%" PRIu64 ", txd:%p", s->id, xd->idx,
178
0
            det_ctx->tx_id, txd);
179
180
0
    return TxBitSet(txd, xd->idx);
181
0
}
182
183
/*
184
 * returns 0: no match
185
 *         1: match
186
 *        -1: error
187
 */
188
189
static int DetectXbitMatch (DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx)
190
228
{
191
228
    const DetectXbitsData *fd = (const DetectXbitsData *)ctx;
192
228
    if (fd == NULL)
193
0
        return 0;
194
195
228
    switch (fd->type) {
196
0
        case VAR_TYPE_HOST_BIT:
197
0
            return DetectXbitMatchHost(p, (const DetectXbitsData *)fd);
198
0
            break;
199
224
        case VAR_TYPE_IPPAIR_BIT:
200
224
            return DetectXbitMatchIPPair(p, (const DetectXbitsData *)fd);
201
0
            break;
202
0
        case VAR_TYPE_TX_BIT:
203
            // TODO this is for PostMatch only. Can we validate somehow?
204
0
            return DetectXbitPostMatchTx(det_ctx, p, s, fd);
205
0
            break;
206
4
        default:
207
4
            break;
208
228
    }
209
4
    return 0;
210
228
}
211
212
static int DetectXbitTxMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state,
213
        void *txv, const Signature *s, const SigMatchCtx *ctx)
214
0
{
215
0
    const DetectXbitsData *xd = (const DetectXbitsData *)ctx;
216
0
    DEBUG_VALIDATE_BUG_ON(xd == NULL);
217
218
0
    AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, txv);
219
220
0
    SCLogDebug("sid:%u: tx:%" PRIu64 ", txd->txbits:%p", s->id, det_ctx->tx_id, txd->txbits);
221
0
    int r = TxBitIsset(txd, xd->idx);
222
0
    if (r == 1) {
223
0
        return DETECT_ENGINE_INSPECT_SIG_MATCH;
224
0
    }
225
0
    return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
226
0
}
227
228
/** \internal
229
 *  \brief parse xbits rule options
230
 *  \retval 0 ok
231
 *  \retval -1 bad
232
 *  \param[out] cdout return DetectXbitsData structure or NULL if noalert
233
 */
234
static int DetectXbitParse(DetectEngineCtx *de_ctx,
235
        const char *rawstr, DetectXbitsData **cdout)
236
16.5k
{
237
16.5k
    bool cmd_set = false;
238
16.5k
    bool name_set = false;
239
16.5k
    bool track_set = false;
240
16.5k
    bool expire_set = false;
241
16.5k
    uint8_t cmd = 0;
242
16.5k
    uint8_t track = 0;
243
16.5k
    enum VarTypes var_type = VAR_TYPE_NOT_SET;
244
16.5k
    uint32_t expire = DETECT_XBITS_EXPIRE_DEFAULT;
245
16.5k
    DetectXbitsData *cd = NULL;
246
16.5k
    char name[256] = "";
247
16.5k
    char copy[strlen(rawstr) + 1];
248
16.5k
    strlcpy(copy, rawstr, sizeof(copy));
249
16.5k
    char *context = NULL;
250
16.5k
    char *token = strtok_r(copy, ",", &context);
251
61.0k
    while (token != NULL) {
252
75.1k
        while (*token != '\0' && isblank(*token)) {
253
24.6k
            token++;
254
24.6k
        }
255
50.4k
        char *val = strchr(token, ' ');
256
50.4k
        if (val != NULL) {
257
19.0k
            *val++ = '\0';
258
23.7k
            while (*val != '\0' && isblank(*val)) {
259
4.74k
                val++;
260
4.74k
            }
261
31.4k
        } else {
262
31.4k
            SCLogDebug("val %s", token);
263
31.4k
        }
264
50.4k
        if (strlen(token) == 0) {
265
873
            goto next;
266
873
        }
267
49.5k
        if (strcmp(token, "noalert") == 0 && !cmd_set) {
268
42
            if (strtok_r(NULL, ",", &context) != NULL) {
269
3
                return -1;
270
3
            }
271
39
            if (val && strlen(val) != 0) {
272
1
                return -1;
273
1
            }
274
38
            *cdout = NULL;
275
38
            return 0;
276
39
        }
277
49.5k
        if (!cmd_set) {
278
16.5k
            if (val && strlen(val) != 0) {
279
185
                return -1;
280
185
            }
281
16.3k
            if (strcmp(token, "set") == 0) {
282
4.23k
                cmd = DETECT_XBITS_CMD_SET;
283
12.0k
            } else if (strcmp(token, "isset") == 0) {
284
8.21k
                cmd = DETECT_XBITS_CMD_ISSET;
285
8.21k
            } else if (strcmp(token, "unset") == 0) {
286
26
                cmd = DETECT_XBITS_CMD_UNSET;
287
3.83k
            } else if (strcmp(token, "isnotset") == 0) {
288
853
                cmd = DETECT_XBITS_CMD_ISNOTSET;
289
2.98k
            } else if (strcmp(token, "toggle") == 0) {
290
1.55k
                cmd = DETECT_XBITS_CMD_TOGGLE;
291
1.55k
            } else {
292
1.43k
                SCLogError("Invalid xbits cmd: %s", token);
293
1.43k
                return -1;
294
1.43k
            }
295
14.8k
            cmd_set = true;
296
33.0k
        } else if (!name_set) {
297
14.8k
            if (val && strlen(val) != 0) {
298
1.16k
                return -1;
299
1.16k
            }
300
13.7k
            strlcpy(name, token, sizeof(name));
301
13.7k
            name_set = true;
302
18.1k
        } else if (!track_set || !expire_set) {
303
18.1k
            if (val == NULL) {
304
1.52k
                return -1;
305
1.52k
            }
306
16.6k
            if (strcmp(token, "track") == 0) {
307
7.24k
                if (track_set) {
308
4
                    return -1;
309
4
                }
310
7.24k
                if (strcmp(val, "ip_src") == 0) {
311
72
                    track = DETECT_XBITS_TRACK_IPSRC;
312
72
                    var_type = VAR_TYPE_HOST_BIT;
313
7.17k
                } else if (strcmp(val, "ip_dst") == 0) {
314
111
                    track = DETECT_XBITS_TRACK_IPDST;
315
111
                    var_type = VAR_TYPE_HOST_BIT;
316
7.05k
                } else if (strcmp(val, "ip_pair") == 0) {
317
5.51k
                    track = DETECT_XBITS_TRACK_IPPAIR;
318
5.51k
                    var_type = VAR_TYPE_IPPAIR_BIT;
319
5.51k
                } else if (strcmp(val, "tx") == 0) {
320
0
                    track = DETECT_XBITS_TRACK_TX;
321
0
                    var_type = VAR_TYPE_TX_BIT;
322
1.54k
                } else {
323
1.54k
                    SCLogError("Invalid xbits tracker: %s", val);
324
1.54k
                    return -1;
325
1.54k
                }
326
5.69k
                track_set = true;
327
9.41k
            } else if (strcmp(token, "expire") == 0) {
328
1.11k
                if (expire_set) {
329
1
                    return -1;
330
1
                }
331
1.10k
                if ((StringParseUint32(&expire, 10, 0, val) < 0) || (expire == 0)) {
332
40
                    SCLogError("Invalid expire value: %s", val);
333
40
                    return -1;
334
40
                }
335
1.06k
                expire_set = true;
336
1.06k
            }
337
16.6k
        } else {
338
2
            SCLogError("Invalid xbits keyword: %s", token);
339
2
            return -1;
340
2
        }
341
44.5k
    next:
342
44.5k
        token = strtok_r(NULL, ",", &context);
343
44.5k
    }
344
345
10.5k
    if (track == DETECT_XBITS_TRACK_TX) {
346
0
        if (cmd != DETECT_XBITS_CMD_ISSET && cmd != DETECT_XBITS_CMD_SET) {
347
0
            SCLogError("tx xbits only support set and isset");
348
0
            return -1;
349
0
        }
350
0
    }
351
352
10.5k
    cd = SCMalloc(sizeof(DetectXbitsData));
353
10.5k
    if (unlikely(cd == NULL))
354
0
        return -1;
355
356
10.5k
    uint32_t varname_id = VarNameStoreRegister(name, var_type);
357
10.5k
    if (unlikely(varname_id == 0)) {
358
0
        SCFree(cd);
359
0
        return -1;
360
0
    }
361
10.5k
    cd->idx = varname_id;
362
10.5k
    cd->cmd = cmd;
363
10.5k
    cd->tracker = track;
364
10.5k
    cd->type = var_type;
365
10.5k
    cd->expire = expire;
366
367
10.5k
    SCLogDebug("idx %" PRIu32 ", cmd %d, name %s", cd->idx, cmd, strlen(name) ? name : "(none)");
368
369
10.5k
    *cdout = cd;
370
10.5k
    return 0;
371
10.5k
}
372
373
int DetectXbitSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
374
4.37k
{
375
4.37k
    DetectXbitsData *cd = NULL;
376
377
4.37k
    int result = DetectXbitParse(de_ctx, rawstr, &cd);
378
4.37k
    if (result < 0) {
379
1.97k
        return -1;
380
2.39k
    } else if (cd == NULL) {
381
        /* noalert doesn't use a cd/sm struct. It flags the sig. We're done. */
382
0
        s->action &= ~ACTION_ALERT;
383
0
        return 0;
384
0
    }
385
386
    /* Okay so far so good, lets get this into a SigMatch
387
     * and put it in the Signature. */
388
2.39k
    switch (cd->cmd) {
389
        /* case DETECT_XBITS_CMD_NOALERT can't happen here */
390
10
        case DETECT_XBITS_CMD_ISNOTSET:
391
1.56k
        case DETECT_XBITS_CMD_ISSET: {
392
1.56k
            int list = DETECT_SM_LIST_MATCH;
393
1.56k
            if (cd->tracker == DETECT_XBITS_TRACK_TX) {
394
0
                SCLogDebug("tx xbit isset");
395
0
                if (s->init_data->hook.type != SIGNATURE_HOOK_TYPE_APP) {
396
0
                    SCLogError("tx xbits require an explicit rule hook");
397
0
                    SCFree(cd);
398
0
                    return -1;
399
0
                }
400
0
                list = s->init_data->hook.sm_list;
401
0
                SCLogDebug("setting list %d", list);
402
403
0
                if (list == -1) {
404
0
                    SCLogError("tx xbits failed to set up"); // TODO how would we get here?
405
0
                    SCFree(cd);
406
0
                    return -1;
407
0
                }
408
0
            }
409
410
1.56k
            SCLogDebug("adding match/txmatch");
411
            /* checks, so packet list */
412
1.56k
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_XBITS, (SigMatchCtx *)cd, list) ==
413
1.56k
                    NULL) {
414
0
                SCFree(cd);
415
0
                return -1;
416
0
            }
417
1.56k
            break;
418
1.56k
        }
419
        // all other cases
420
        // DETECT_XBITS_CMD_SET, DETECT_XBITS_CMD_UNSET, DETECT_XBITS_CMD_TOGGLE:
421
1.56k
        default:
422
825
            SCLogDebug("adding post-match");
423
            /* modifiers, only run when entire sig has matched */
424
825
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_XBITS, (SigMatchCtx *)cd,
425
825
                        DETECT_SM_LIST_POSTMATCH) == NULL) {
426
0
                SCFree(cd);
427
0
                return -1;
428
0
            }
429
825
            break;
430
2.39k
    }
431
432
2.39k
    return 0;
433
2.39k
}
434
435
static void DetectXbitFree (DetectEngineCtx *de_ctx, void *ptr)
436
10.5k
{
437
10.5k
    DetectXbitsData *fd = (DetectXbitsData *)ptr;
438
439
10.5k
    if (fd == NULL)
440
0
        return;
441
10.5k
    VarNameStoreUnregister(fd->idx, fd->type);
442
443
10.5k
    SCFree(fd);
444
10.5k
}
445
446
#ifdef UNITTESTS
447
448
static void XBitsTestSetup(void)
449
{
450
    StorageInit();
451
    HostBitInitCtx();
452
    IPPairBitInitCtx();
453
    StorageFinalize();
454
    HostInitConfig(true);
455
    IPPairInitConfig(true);
456
}
457
458
static void XBitsTestShutdown(void)
459
{
460
    HostCleanup();
461
    IPPairCleanup();
462
    StorageCleanup();
463
}
464
465
466
static int XBitsTestParse01(void)
467
{
468
    DetectEngineCtx *de_ctx = NULL;
469
    de_ctx = DetectEngineCtxInit();
470
    FAIL_IF_NULL(de_ctx);
471
    de_ctx->flags |= DE_QUIET;
472
    DetectXbitsData *cd = NULL;
473
474
#define BAD_INPUT(str) \
475
    FAIL_IF_NOT(DetectXbitParse(de_ctx, (str), &cd) == -1);
476
477
    BAD_INPUT("alert");
478
    BAD_INPUT("n0alert");
479
    BAD_INPUT("nOalert");
480
    BAD_INPUT("set,abc,track nonsense, expire 3600");
481
    BAD_INPUT("set,abc,track ip_source, expire 3600");
482
    BAD_INPUT("set,abc,track ip_src, expire -1");
483
    BAD_INPUT("set,abc,track ip_src, expire 0");
484
485
#undef BAD_INPUT
486
487
#define GOOD_INPUT(str, command, trk, typ, exp)             \
488
    FAIL_IF_NOT(DetectXbitParse(de_ctx, (str), &cd) == 0);  \
489
    FAIL_IF_NULL(cd);                                       \
490
    FAIL_IF_NOT(cd->cmd == (command));                      \
491
    FAIL_IF_NOT(cd->tracker == (trk));                      \
492
    FAIL_IF_NOT(cd->type == (typ));                         \
493
    FAIL_IF_NOT(cd->expire == (exp));                       \
494
    DetectXbitFree(NULL, cd);                               \
495
    cd = NULL;
496
497
    GOOD_INPUT("set,abc,track ip_pair",
498
            DETECT_XBITS_CMD_SET,
499
            DETECT_XBITS_TRACK_IPPAIR, VAR_TYPE_IPPAIR_BIT,
500
            DETECT_XBITS_EXPIRE_DEFAULT);
501
    GOOD_INPUT("set,abc,track ip_pair, expire 3600",
502
            DETECT_XBITS_CMD_SET,
503
            DETECT_XBITS_TRACK_IPPAIR, VAR_TYPE_IPPAIR_BIT,
504
            3600);
505
    GOOD_INPUT("set,abc,track ip_src, expire 1234",
506
            DETECT_XBITS_CMD_SET,
507
            DETECT_XBITS_TRACK_IPSRC, VAR_TYPE_HOST_BIT,
508
            1234);
509
510
#undef GOOD_INPUT
511
512
    DetectEngineCtxFree(de_ctx);
513
    PASS;
514
}
515
516
/**
517
 * \test
518
 */
519
520
static int XBitsTestSig01(void)
521
{
522
    uint8_t *buf = (uint8_t *)
523
                    "GET /one/ HTTP/1.1\r\n"
524
                    "Host: one.example.org\r\n"
525
                    "\r\n";
526
    uint16_t buflen = strlen((char *)buf);
527
    Packet *p = PacketGetFromAlloc();
528
    FAIL_IF_NULL(p);
529
    Signature *s = NULL;
530
    ThreadVars th_v;
531
    DetectEngineThreadCtx *det_ctx = NULL;
532
    DetectEngineCtx *de_ctx = NULL;
533
534
    memset(&th_v, 0, sizeof(th_v));
535
    p->src.family = AF_INET;
536
    p->dst.family = AF_INET;
537
    p->payload = buf;
538
    p->payload_len = buflen;
539
    p->proto = IPPROTO_TCP;
540
541
    XBitsTestSetup();
542
543
    de_ctx = DetectEngineCtxInit();
544
    FAIL_IF_NULL(de_ctx);
545
    de_ctx->flags |= DE_QUIET;
546
547
    s = DetectEngineAppendSig(de_ctx,
548
            "alert ip any any -> any any (xbits:set,abc,track ip_pair; content:\"GET \"; sid:1;)");
549
    FAIL_IF_NULL(s);
550
551
    SigGroupBuild(de_ctx);
552
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
553
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
554
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
555
    DetectEngineCtxFree(de_ctx);
556
    XBitsTestShutdown();
557
    SCFree(p);
558
    StatsThreadCleanup(&th_v);
559
    StatsReleaseResources();
560
    PASS;
561
}
562
563
/**
564
 * \test various options
565
 *
566
 *  \retval 1 on success
567
 *  \retval 0 on failure
568
 */
569
570
static int XBitsTestSig02(void)
571
{
572
    Signature *s = NULL;
573
    DetectEngineCtx *de_ctx = NULL;
574
    de_ctx = DetectEngineCtxInit();
575
    FAIL_IF_NULL(de_ctx);
576
    de_ctx->flags |= DE_QUIET;
577
578
    s = DetectEngineAppendSig(de_ctx,
579
            "alert ip any any -> any any (xbits:isset,abc,track ip_src; content:\"GET \"; sid:1;)");
580
    FAIL_IF_NULL(s);
581
582
    s = DetectEngineAppendSig(de_ctx,
583
            "alert ip any any -> any any (xbits:isnotset,abc,track ip_dst; content:\"GET \"; sid:2;)");
584
    FAIL_IF_NULL(s);
585
586
    s = DetectEngineAppendSig(de_ctx,
587
            "alert ip any any -> any any (xbits:set,abc,track ip_pair; content:\"GET \"; sid:3;)");
588
    FAIL_IF_NULL(s);
589
590
    s = DetectEngineAppendSig(de_ctx,
591
            "alert ip any any -> any any (xbits:unset,abc,track ip_src; content:\"GET \"; sid:4;)");
592
    FAIL_IF_NULL(s);
593
594
    s = DetectEngineAppendSig(de_ctx,
595
            "alert ip any any -> any any (xbits:toggle,abc,track ip_dst; content:\"GET \"; sid:5;)");
596
    FAIL_IF_NULL(s);
597
598
    s = DetectEngineAppendSig(de_ctx,
599
            "alert ip any any -> any any (xbits:!set,abc,track ip_dst; content:\"GET \"; sid:6;)");
600
    FAIL_IF_NOT_NULL(s);
601
602
    DetectEngineCtxFree(de_ctx);
603
    PASS;
604
}
605
606
/* Test to demonstrate redmine bug 4820 */
607
static int XBitsTestSig03(void)
608
{
609
    DetectEngineCtx *de_ctx = NULL;
610
    XBitsTestSetup();
611
    de_ctx = DetectEngineCtxInit();
612
    FAIL_IF_NULL(de_ctx);
613
    de_ctx->flags |= DE_QUIET;
614
615
    Signature *s = DetectEngineAppendSig(
616
            de_ctx, "alert http any any -> any any (msg:\"TEST - No Error\")\";\
617
            flow:established,to_server; http.method; content:\"GET\"; \
618
            xbits:set,ET.2020_8260.1,track ip_src,expire 10; sid:1;)");
619
    FAIL_IF_NULL(s);
620
621
    DetectEngineCtxFree(de_ctx);
622
    XBitsTestShutdown();
623
    PASS;
624
}
625
626
/* Test to demonstrate redmine bug 4820 */
627
static int XBitsTestSig04(void)
628
{
629
    DetectEngineCtx *de_ctx = NULL;
630
    XBitsTestSetup();
631
    de_ctx = DetectEngineCtxInit();
632
    FAIL_IF_NULL(de_ctx);
633
    de_ctx->flags |= DE_QUIET;
634
635
    Signature *s =
636
            DetectEngineAppendSig(de_ctx, "alert http any any -> any any (msg:\"TEST - Error\")\"; \
637
            flow:established,to_server; http.method; content:\"GET\"; \
638
            xbits:set,ET.2020_8260.1,noalert,track ip_src,expire 10; sid:2;)");
639
    FAIL_IF_NOT_NULL(s);
640
641
    DetectEngineCtxFree(de_ctx);
642
    XBitsTestShutdown();
643
    PASS;
644
}
645
646
static int XBitsTestSig05(void)
647
{
648
    DetectEngineCtx *de_ctx = NULL;
649
    XBitsTestSetup();
650
    de_ctx = DetectEngineCtxInit();
651
    FAIL_IF_NULL(de_ctx);
652
    de_ctx->flags |= DE_QUIET;
653
654
    Signature *s = DetectEngineAppendSig(de_ctx,
655
            "alert http any any -> any any (msg:\"ET EXPLOIT Possible Pulse Secure VPN RCE "
656
            "Chain Stage 1 Inbound - Request Config Backup (CVE-2020-8260)\"; "
657
            "flow:established,to_server; http.method; content:\"GET\"; http.uri; "
658
            "content:\"/dana-admin/cached/config/config.cgi?type=system\"; fast_pattern; "
659
            "xbits:set,ET.2020_8260.1,track ip_src,expire 10; xbits:noalert; "
660
            "classtype:attempted-admin; sid:2033750; rev:1;");
661
    FAIL_IF_NULL(s);
662
663
    DetectEngineCtxFree(de_ctx);
664
    XBitsTestShutdown();
665
    PASS;
666
}
667
668
static int XBitsTestSig06(void)
669
{
670
    DetectEngineCtx *de_ctx = NULL;
671
    XBitsTestSetup();
672
    de_ctx = DetectEngineCtxInit();
673
    FAIL_IF_NULL(de_ctx);
674
    de_ctx->flags |= DE_QUIET;
675
676
    Signature *s = DetectEngineAppendSig(de_ctx,
677
            "alert http any any -> any any (msg:\"ET EXPLOIT Possible Pulse Secure VPN RCE "
678
            "Chain Stage 2 Inbound - Upload Malicious Config (CVE-2020-8260)\"; "
679
            "flow:established,to_server; http.method; content:\"POST\"; http.uri; "
680
            "content:\"/dana-admin/cached/config/import.cgi\"; "
681
            "xbits:isset,ET.2020_8260.1,track ip_src,expire 10;"
682
            "xbits:set,ET.2020_8260.2,track ip_src,expire 10; "
683
            "classtype:attempted-admin; sid:2033751; rev:1;");
684
    FAIL_IF_NULL(s);
685
686
    DetectEngineCtxFree(de_ctx);
687
    XBitsTestShutdown();
688
    PASS;
689
}
690
691
static int DetectXBitsTestBadRules(void)
692
{
693
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
694
    FAIL_IF_NULL(de_ctx);
695
696
    const char *sigs[] = {
697
        "alert http any any -> any any (content:\"abc\"; xbits:set,bit1,noalert,track "
698
        "ip_src;sid:1;)",
699
        "alert http any any -> any any (content:\"abc\"; xbits:noalert,set,bit1,noalert,track "
700
        "ip_src;sid:10;)",
701
        "alert http any any -> any any (content:\"abc\"; xbits:isset,bit2,track "
702
        "ip_dst,asdf;sid:2;)",
703
        "alert http any any -> any any (content:\"abc\"; xbits:isnotset,track ip_pair;sid:3;)",
704
        "alert http any any -> any any (content:\"abc\"; xbits:toggle,track ip_pair,bit4;sid:4;)",
705
        "alert http any any -> any any (content:\"abc\"; xbits:unset,bit5,track ipsrc;sid:5;)",
706
        "alert http any any -> any any (content:\"abc\"; xbits:bit6,set,track ip_src,expire "
707
        "10;sid:6;)",
708
        "alert http any any -> any any (content:\"abc\"; xbits:set,bit7,track "
709
        "ip_pair,expire;sid:7;)",
710
        "alert http any any -> any any (content:\"abc\"; xbits:set,bit7,trackk ip_pair,expire "
711
        "3600, noalert;sid:8;)",
712
        NULL,
713
    };
714
715
    const char **sig = sigs;
716
    while (*sig) {
717
        SCLogDebug("sig %s", *sig);
718
        Signature *s = DetectEngineAppendSig(de_ctx, *sig);
719
        FAIL_IF_NOT_NULL(s);
720
        sig++;
721
    }
722
723
    DetectEngineCtxFree(de_ctx);
724
    PASS;
725
}
726
727
static int DetectXBitsTestGoodRules(void)
728
{
729
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
730
    FAIL_IF_NULL(de_ctx);
731
732
    const char *sigs[] = {
733
        "alert http any any -> any any (content:\"abc\"; xbits:set,bit1,track ip_src;sid:1;)",
734
        "alert http any any -> any any (content:\"abc\"; xbits:isset,bit2,track ip_dst;sid:2;)",
735
        "alert http any any -> any any (content:\"abc\"; xbits:isnotset, bit3,  track "
736
        "ip_pair;sid:3;)",
737
        "alert http any any -> any any (content:\"abc\"; xbits:toggle,bit4, track   "
738
        "ip_pair;sid:4;)",
739
        "alert http any any -> any any (content:\"abc\"; xbits:  unset ,bit5,track ip_src;sid:5;)",
740
        "alert http any any -> any any (content:\"abc\"; xbits:set,bit6 ,track ip_src, expire "
741
        "10 ;sid:6;)",
742
        "alert http any any -> any any (content:\"abc\"; xbits:set, bit7, track ip_pair, expire "
743
        "3600;sid:7;)",
744
        "alert http any any -> any any (content:\"abc\"; xbits:set, bit7, track ip_pair, expire "
745
        "3600; xbits:noalert; sid:8;)",
746
        "alert http any any -> any any (content:\"abc\"; xbits:noalert; xbits:set, bit7, track "
747
        "ip_pair, expire "
748
        "3600;sid:9;)",
749
        NULL,
750
    };
751
752
    const char **sig = sigs;
753
    while (*sig) {
754
        SCLogDebug("sig %s", *sig);
755
        Signature *s = DetectEngineAppendSig(de_ctx, *sig);
756
        FAIL_IF_NULL(s);
757
        sig++;
758
    }
759
760
    DetectEngineCtxFree(de_ctx);
761
    PASS;
762
}
763
764
/**
765
 * \brief this function registers unit tests for XBits
766
 */
767
static void XBitsRegisterTests(void)
768
{
769
    UtRegisterTest("XBitsTestParse01", XBitsTestParse01);
770
    UtRegisterTest("XBitsTestSig01", XBitsTestSig01);
771
    UtRegisterTest("XBitsTestSig02", XBitsTestSig02);
772
    UtRegisterTest("XBitsTestSig03", XBitsTestSig03);
773
    UtRegisterTest("XBitsTestSig04", XBitsTestSig04);
774
    UtRegisterTest("XBitsTestSig05", XBitsTestSig05);
775
    UtRegisterTest("XBitsTestSig06", XBitsTestSig06);
776
    UtRegisterTest("DetectXBitsTestBadRules", DetectXBitsTestBadRules);
777
    UtRegisterTest("DetectXBitsTestGoodRules", DetectXBitsTestGoodRules);
778
}
779
#endif /* UNITTESTS */