Coverage Report

Created: 2026-01-16 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/detect-tcp-seq.c
Line
Count
Source
1
/* Copyright (C) 2007-2010 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 Brian Rectanus <brectanu@gmail.com>
22
 *
23
 * Implements the seq keyword.
24
 */
25
26
#include "suricata-common.h"
27
#include "decode.h"
28
#include "detect.h"
29
30
#include "detect-parse.h"
31
#include "detect-engine.h"
32
#include "detect-engine-prefilter.h"
33
#include "detect-engine-prefilter-common.h"
34
#include "detect-engine-build.h"
35
36
#include "detect-tcp-seq.h"
37
38
#include "util-byte.h"
39
#include "util-unittest.h"
40
#include "util-unittest-helper.h"
41
#include "util-debug.h"
42
43
static int DetectSeqSetup(DetectEngineCtx *, Signature *, const char *);
44
static int DetectSeqMatch(DetectEngineThreadCtx *,
45
                          Packet *, const Signature *, const SigMatchCtx *);
46
#ifdef UNITTESTS
47
static void DetectSeqRegisterTests(void);
48
#endif
49
static void DetectSeqFree(DetectEngineCtx *, void *);
50
static int PrefilterSetupTcpSeq(DetectEngineCtx *de_ctx, SigGroupHead *sgh);
51
static bool PrefilterTcpSeqIsPrefilterable(const Signature *s);
52
53
void DetectSeqRegister(void)
54
73
{
55
73
    sigmatch_table[DETECT_SEQ].name = "tcp.seq";
56
73
    sigmatch_table[DETECT_SEQ].alias = "seq";
57
73
    sigmatch_table[DETECT_SEQ].desc = "check for a specific TCP sequence number";
58
73
    sigmatch_table[DETECT_SEQ].url = "/rules/header-keywords.html#seq";
59
73
    sigmatch_table[DETECT_SEQ].Match = DetectSeqMatch;
60
73
    sigmatch_table[DETECT_SEQ].Setup = DetectSeqSetup;
61
73
    sigmatch_table[DETECT_SEQ].Free = DetectSeqFree;
62
#ifdef UNITTESTS
63
    sigmatch_table[DETECT_SEQ].RegisterTests = DetectSeqRegisterTests;
64
#endif
65
73
    sigmatch_table[DETECT_SEQ].SupportsPrefilter = PrefilterTcpSeqIsPrefilterable;
66
73
    sigmatch_table[DETECT_SEQ].SetupPrefilter = PrefilterSetupTcpSeq;
67
73
}
68
69
/**
70
 * \internal
71
 * \brief This function is used to match packets with a given Seq number
72
 *
73
 * \param t pointer to thread vars
74
 * \param det_ctx pointer to the pattern matcher thread
75
 * \param p pointer to the current packet
76
 * \param m pointer to the sigmatch that we will cast into DetectSeqData
77
 *
78
 * \retval 0 no match
79
 * \retval 1 match
80
 */
81
static int DetectSeqMatch(DetectEngineThreadCtx *det_ctx,
82
                          Packet *p, const Signature *s, const SigMatchCtx *ctx)
83
5.85k
{
84
5.85k
    const DetectSeqData *data = (const DetectSeqData *)ctx;
85
86
    /* This is only needed on TCP packets */
87
5.85k
    if (!(PKT_IS_TCP(p)) || PKT_IS_PSEUDOPKT(p)) {
88
1.21k
        return 0;
89
1.21k
    }
90
91
4.64k
    return (data->seq == TCP_GET_SEQ(p)) ? 1 : 0;
92
5.85k
}
93
94
/**
95
 * \internal
96
 * \brief this function is used to add the seq option into the signature
97
 *
98
 * \param de_ctx pointer to the Detection Engine Context
99
 * \param s pointer to the Current Signature
100
 * \param optstr pointer to the user provided options
101
 *
102
 * \retval 0 on Success
103
 * \retval -1 on Failure
104
 */
105
static int DetectSeqSetup (DetectEngineCtx *de_ctx, Signature *s, const char *optstr)
106
5.50k
{
107
5.50k
    DetectSeqData *data = NULL;
108
5.50k
    SigMatch *sm = NULL;
109
110
5.50k
    data = SCMalloc(sizeof(DetectSeqData));
111
5.50k
    if (unlikely(data == NULL))
112
0
        goto error;
113
114
5.50k
    sm = SigMatchAlloc();
115
5.50k
    if (sm == NULL)
116
0
        goto error;
117
118
5.50k
    sm->type = DETECT_SEQ;
119
120
5.50k
    if (StringParseUint32(&data->seq, 10, 0, optstr) < 0) {
121
132
        goto error;
122
132
    }
123
5.37k
    sm->ctx = (SigMatchCtx*)data;
124
125
5.37k
    SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
126
5.37k
    s->flags |= SIG_FLAG_REQUIRE_PACKET;
127
128
5.37k
    return 0;
129
130
132
error:
131
132
    if (data)
132
132
        SCFree(data);
133
132
    if (sm)
134
132
        SigMatchFree(de_ctx, sm);
135
132
    return -1;
136
137
5.50k
}
138
139
/**
140
 * \internal
141
 * \brief this function will free memory associated with seq option
142
 *
143
 * \param data pointer to seq configuration data
144
 */
145
static void DetectSeqFree(DetectEngineCtx *de_ctx, void *ptr)
146
23.9k
{
147
23.9k
    DetectSeqData *data = (DetectSeqData *)ptr;
148
23.9k
    SCFree(data);
149
23.9k
}
150
151
/* prefilter code */
152
153
static void
154
PrefilterPacketSeqMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
155
1.13k
{
156
1.13k
    const PrefilterPacketHeaderCtx *ctx = pectx;
157
158
1.13k
    if (!PrefilterPacketHeaderExtraMatch(ctx, p))
159
1.11k
        return;
160
161
15
    if ((p->proto) == IPPROTO_TCP && !(PKT_IS_PSEUDOPKT(p)) &&
162
0
        (p->tcph != NULL) && (TCP_GET_SEQ(p) == ctx->v1.u32[0]))
163
0
    {
164
0
        SCLogDebug("packet matches TCP seq %u", ctx->v1.u32[0]);
165
0
        PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
166
0
    }
167
15
}
168
169
static void
170
PrefilterPacketSeqSet(PrefilterPacketHeaderValue *v, void *smctx)
171
2.70k
{
172
2.70k
    const DetectSeqData *a = smctx;
173
2.70k
    v->u32[0] = a->seq;
174
2.70k
}
175
176
static bool
177
PrefilterPacketSeqCompare(PrefilterPacketHeaderValue v, void *smctx)
178
1.90k
{
179
1.90k
    const DetectSeqData *a = smctx;
180
1.90k
    if (v.u32[0] == a->seq)
181
1.90k
        return true;
182
0
    return false;
183
1.90k
}
184
185
static int PrefilterSetupTcpSeq(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
186
6.91k
{
187
6.91k
    return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_SEQ,
188
6.91k
        PrefilterPacketSeqSet,
189
6.91k
        PrefilterPacketSeqCompare,
190
6.91k
        PrefilterPacketSeqMatch);
191
6.91k
}
192
193
static bool PrefilterTcpSeqIsPrefilterable(const Signature *s)
194
0
{
195
0
    const SigMatch *sm;
196
0
    for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH] ; sm != NULL; sm = sm->next) {
197
0
        switch (sm->type) {
198
0
            case DETECT_SEQ:
199
0
                return true;
200
0
        }
201
0
    }
202
0
    return false;
203
0
}
204
205
206
#ifdef UNITTESTS
207
208
/**
209
 * \test DetectSeqSigTest01 tests parses
210
 */
211
static int DetectSeqSigTest01(void)
212
{
213
    int result = 0;
214
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
215
    if (de_ctx == NULL)
216
        goto end;
217
218
    /* These three are crammed in here as there is no Parse */
219
    if (SigInit(de_ctx,
220
                "alert tcp any any -> any any "
221
                "(msg:\"Testing seq\";seq:foo;sid:1;)") != NULL)
222
    {
223
        printf("invalid seq accepted: ");
224
        goto cleanup;
225
    }
226
    if (SigInit(de_ctx,
227
                "alert tcp any any -> any any "
228
                "(msg:\"Testing seq\";seq:9999999999;sid:1;)") != NULL)
229
    {
230
        printf("overflowing seq accepted: ");
231
        goto cleanup;
232
    }
233
    if (SigInit(de_ctx,
234
                "alert tcp any any -> any any "
235
                "(msg:\"Testing seq\";seq:-100;sid:1;)") != NULL)
236
    {
237
        printf("negative seq accepted: ");
238
        goto cleanup;
239
    }
240
    result = 1;
241
242
cleanup:
243
    if (de_ctx) {
244
        SigGroupCleanup(de_ctx);
245
        SigCleanSignatures(de_ctx);
246
        DetectEngineCtxFree(de_ctx);
247
    }
248
end:
249
    return result;
250
}
251
252
/**
253
 * \test DetectSeqSigTest02 tests seq keyword
254
 */
255
static int DetectSeqSigTest02(void)
256
{
257
    int result = 0;
258
    uint8_t *buf = (uint8_t *)"Hi all!";
259
    uint16_t buflen = strlen((char *)buf);
260
    Packet *p[3];
261
    p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
262
    p[1] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
263
    p[2] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_ICMP);
264
    if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
265
        goto end;
266
267
    /* TCP w/seq=42 */
268
    p[0]->tcph->th_seq = htonl(42);
269
270
    /* TCP w/seq=100 */
271
    p[1]->tcph->th_seq = htonl(100);
272
273
    const char *sigs[2];
274
    sigs[0]= "alert tcp any any -> any any (msg:\"Testing seq\"; seq:41; sid:1;)";
275
    sigs[1]= "alert tcp any any -> any any (msg:\"Testing seq\"; seq:42; sid:2;)";
276
277
    uint32_t sid[2] = {1, 2};
278
279
    uint32_t results[3][2] = {
280
                              /* packet 0 match sid 1 but should not match sid 2 */
281
                              {0, 1},
282
                              /* packet 1 should not match */
283
                              {0, 0},
284
                              /* packet 2 should not match */
285
                              {0, 0} };
286
287
    result = UTHGenericTest(p, 3, sigs, sid, (uint32_t *) results, 2);
288
    UTHFreePackets(p, 3);
289
end:
290
    return result;
291
}
292
293
/**
294
 * \internal
295
 * \brief This function registers unit tests for DetectSeq
296
 */
297
static void DetectSeqRegisterTests(void)
298
{
299
    UtRegisterTest("DetectSeqSigTest01", DetectSeqSigTest01);
300
    UtRegisterTest("DetectSeqSigTest02", DetectSeqSigTest02);
301
}
302
#endif /* UNITTESTS */