Coverage Report

Created: 2025-12-31 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata/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
#include "detect-engine-uint.h"
36
37
#include "detect-tcp-seq.h"
38
39
#include "util-byte.h"
40
#include "util-unittest.h"
41
#include "util-unittest-helper.h"
42
#include "util-debug.h"
43
44
static int DetectSeqSetup(DetectEngineCtx *, Signature *, const char *);
45
static int DetectSeqMatch(DetectEngineThreadCtx *,
46
                          Packet *, const Signature *, const SigMatchCtx *);
47
#ifdef UNITTESTS
48
static void DetectSeqRegisterTests(void);
49
#endif
50
static void DetectSeqFree(DetectEngineCtx *, void *);
51
static int PrefilterSetupTcpSeq(DetectEngineCtx *de_ctx, SigGroupHead *sgh);
52
static bool PrefilterTcpSeqIsPrefilterable(const Signature *s);
53
54
void DetectSeqRegister(void)
55
73
{
56
73
    sigmatch_table[DETECT_SEQ].name = "tcp.seq";
57
73
    sigmatch_table[DETECT_SEQ].alias = "seq";
58
73
    sigmatch_table[DETECT_SEQ].desc = "check for a specific TCP sequence number";
59
73
    sigmatch_table[DETECT_SEQ].url = "/rules/header-keywords.html#seq";
60
73
    sigmatch_table[DETECT_SEQ].Match = DetectSeqMatch;
61
73
    sigmatch_table[DETECT_SEQ].Setup = DetectSeqSetup;
62
73
    sigmatch_table[DETECT_SEQ].Free = DetectSeqFree;
63
73
    sigmatch_table[DETECT_SEQ].flags = SIGMATCH_INFO_UINT32;
64
#ifdef UNITTESTS
65
    sigmatch_table[DETECT_SEQ].RegisterTests = DetectSeqRegisterTests;
66
#endif
67
73
    sigmatch_table[DETECT_SEQ].SupportsPrefilter = PrefilterTcpSeqIsPrefilterable;
68
73
    sigmatch_table[DETECT_SEQ].SetupPrefilter = PrefilterSetupTcpSeq;
69
73
}
70
71
/**
72
 * \internal
73
 * \brief This function is used to match packets with a given Seq number
74
 *
75
 * \param t pointer to thread vars
76
 * \param det_ctx pointer to the pattern matcher thread
77
 * \param p pointer to the current packet
78
 * \param m pointer to the sigmatch that we will cast into DetectSeqData
79
 *
80
 * \retval 0 no match
81
 * \retval 1 match
82
 */
83
static int DetectSeqMatch(DetectEngineThreadCtx *det_ctx,
84
                          Packet *p, const Signature *s, const SigMatchCtx *ctx)
85
7.58k
{
86
7.58k
    const DetectU32Data *data = (const DetectU32Data *)ctx;
87
88
7.58k
    DEBUG_VALIDATE_BUG_ON(PKT_IS_PSEUDOPKT(p));
89
    /* This is only needed on TCP packets */
90
7.58k
    if (!(PacketIsTCP(p))) {
91
1.52k
        return 0;
92
1.52k
    }
93
94
6.06k
    return DetectU32Match(TCP_GET_RAW_SEQ(PacketGetTCP(p)), data);
95
7.58k
}
96
97
/**
98
 * \internal
99
 * \brief this function is used to add the seq option into the signature
100
 *
101
 * \param de_ctx pointer to the Detection Engine Context
102
 * \param s pointer to the Current Signature
103
 * \param optstr pointer to the user provided options
104
 *
105
 * \retval 0 on Success
106
 * \retval -1 on Failure
107
 */
108
static int DetectSeqSetup (DetectEngineCtx *de_ctx, Signature *s, const char *optstr)
109
19.9k
{
110
19.9k
    DetectU32Data *data = SCDetectU32Parse(optstr);
111
19.9k
    if (data == NULL)
112
395
        return -1;
113
114
19.6k
    if (SCSigMatchAppendSMToList(
115
19.6k
                de_ctx, s, DETECT_SEQ, (SigMatchCtx *)data, DETECT_SM_LIST_MATCH) == NULL) {
116
0
        DetectSeqFree(de_ctx, data);
117
0
        return -1;
118
0
    }
119
19.6k
    s->flags |= SIG_FLAG_REQUIRE_PACKET;
120
19.6k
    return 0;
121
19.6k
}
122
123
/**
124
 * \internal
125
 * \brief this function will free memory associated with seq option
126
 *
127
 * \param data pointer to seq configuration data
128
 */
129
static void DetectSeqFree(DetectEngineCtx *de_ctx, void *ptr)
130
24.9k
{
131
24.9k
    SCDetectU32Free(ptr);
132
24.9k
}
133
134
/* prefilter code */
135
136
static void
137
PrefilterPacketSeqMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
138
5.08k
{
139
5.08k
    const PrefilterPacketHeaderCtx *ctx = pectx;
140
141
5.08k
    DEBUG_VALIDATE_BUG_ON(PKT_IS_PSEUDOPKT(p));
142
5.08k
    if (!PrefilterPacketHeaderExtraMatch(ctx, p))
143
4.74k
        return;
144
145
343
    if (p->proto == IPPROTO_TCP && PacketIsTCP(p)) {
146
282
        DetectU32Data du32;
147
282
        du32.mode = ctx->v1.u8[0];
148
282
        du32.arg1 = ctx->v1.u32[1];
149
282
        du32.arg2 = ctx->v1.u32[2];
150
282
        if (DetectU32Match(TCP_GET_RAW_SEQ(PacketGetTCP(p)), &du32)) {
151
3
            SCLogDebug("packet matches TCP seq %u", ctx->v1.u32[0]);
152
3
            PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
153
3
        }
154
282
    }
155
343
}
156
157
static int PrefilterSetupTcpSeq(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
158
6.94k
{
159
6.94k
    return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_SEQ, SIG_MASK_REQUIRE_REAL_PKT,
160
6.94k
            PrefilterPacketU32Set, PrefilterPacketU32Compare, PrefilterPacketSeqMatch);
161
6.94k
}
162
163
static bool PrefilterTcpSeqIsPrefilterable(const Signature *s)
164
0
{
165
0
    return PrefilterIsPrefilterableById(s, DETECT_SEQ);
166
0
}
167
168
169
#ifdef UNITTESTS
170
171
/**
172
 * \test DetectSeqSigTest01 tests parses
173
 */
174
static int DetectSeqSigTest01(void)
175
{
176
    int result = 0;
177
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
178
    if (de_ctx == NULL)
179
        goto end;
180
181
    /* These three are crammed in here as there is no Parse */
182
    if (SigInit(de_ctx,
183
                "alert tcp any any -> any any "
184
                "(msg:\"Testing seq\";seq:foo;sid:1;)") != NULL)
185
    {
186
        printf("invalid seq accepted: ");
187
        goto cleanup;
188
    }
189
    if (SigInit(de_ctx,
190
                "alert tcp any any -> any any "
191
                "(msg:\"Testing seq\";seq:9999999999;sid:1;)") != NULL)
192
    {
193
        printf("overflowing seq accepted: ");
194
        goto cleanup;
195
    }
196
    if (SigInit(de_ctx,
197
                "alert tcp any any -> any any "
198
                "(msg:\"Testing seq\";seq:-100;sid:1;)") != NULL)
199
    {
200
        printf("negative seq accepted: ");
201
        goto cleanup;
202
    }
203
    result = 1;
204
205
cleanup:
206
    if (de_ctx) {
207
        SigGroupCleanup(de_ctx);
208
        SigCleanSignatures(de_ctx);
209
        DetectEngineCtxFree(de_ctx);
210
    }
211
end:
212
    return result;
213
}
214
215
/**
216
 * \test DetectSeqSigTest02 tests seq keyword
217
 */
218
static int DetectSeqSigTest02(void)
219
{
220
    int result = 0;
221
    uint8_t *buf = (uint8_t *)"Hi all!";
222
    uint16_t buflen = strlen((char *)buf);
223
    Packet *p[3];
224
    p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
225
    p[1] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
226
    p[2] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_ICMP);
227
    if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
228
        goto end;
229
230
    /* TCP w/seq=42 */
231
    p[0]->l4.hdrs.tcph->th_seq = htonl(42);
232
233
    /* TCP w/seq=100 */
234
    p[1]->l4.hdrs.tcph->th_seq = htonl(100);
235
236
    const char *sigs[2];
237
    sigs[0]= "alert tcp any any -> any any (msg:\"Testing seq\"; seq:41; sid:1;)";
238
    sigs[1]= "alert tcp any any -> any any (msg:\"Testing seq\"; seq:42; sid:2;)";
239
240
    uint32_t sid[2] = {1, 2};
241
242
    uint32_t results[3][2] = {
243
                              /* packet 0 match sid 1 but should not match sid 2 */
244
                              {0, 1},
245
                              /* packet 1 should not match */
246
                              {0, 0},
247
                              /* packet 2 should not match */
248
                              {0, 0} };
249
250
    result = UTHGenericTest(p, 3, sigs, sid, (uint32_t *) results, 2);
251
    UTHFreePackets(p, 3);
252
end:
253
    return result;
254
}
255
256
/**
257
 * \internal
258
 * \brief This function registers unit tests for DetectSeq
259
 */
260
static void DetectSeqRegisterTests(void)
261
{
262
    UtRegisterTest("DetectSeqSigTest01", DetectSeqSigTest01);
263
    UtRegisterTest("DetectSeqSigTest02", DetectSeqSigTest02);
264
}
265
#endif /* UNITTESTS */