Coverage Report

Created: 2025-11-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/detect-tcp-ack.c
Line
Count
Source
1
/* Copyright (C) 2007-2016 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
 * \author Victor Julien <victor@inliniac.net>
23
 *
24
 * Implements the "ack" keyword.
25
 */
26
27
#include "suricata-common.h"
28
#include "decode.h"
29
#include "detect.h"
30
31
#include "detect-parse.h"
32
#include "detect-engine.h"
33
#include "detect-engine-mpm.h"
34
#include "detect-engine-prefilter.h"
35
#include "detect-engine-prefilter-common.h"
36
#include "detect-engine-build.h"
37
38
#include "detect-tcp-ack.h"
39
40
#include "util-byte.h"
41
#include "util-unittest.h"
42
#include "util-unittest-helper.h"
43
#include "util-debug.h"
44
45
/* prototypes */
46
static int DetectAckSetup(DetectEngineCtx *, Signature *, const char *);
47
static int DetectAckMatch(DetectEngineThreadCtx *,
48
                          Packet *, const Signature *, const SigMatchCtx *);
49
#ifdef UNITTESTS
50
static void DetectAckRegisterTests(void);
51
#endif
52
static void DetectAckFree(DetectEngineCtx *, void *);
53
static int PrefilterSetupTcpAck(DetectEngineCtx *de_ctx, SigGroupHead *sgh);
54
static bool PrefilterTcpAckIsPrefilterable(const Signature *s);
55
56
void DetectAckRegister(void)
57
73
{
58
73
    sigmatch_table[DETECT_ACK].name = "tcp.ack";
59
73
    sigmatch_table[DETECT_ACK].alias = "ack";
60
73
    sigmatch_table[DETECT_ACK].desc = "check for a specific TCP acknowledgement number";
61
73
    sigmatch_table[DETECT_ACK].url = "/rules/header-keywords.html#ack";
62
73
    sigmatch_table[DETECT_ACK].Match = DetectAckMatch;
63
73
    sigmatch_table[DETECT_ACK].Setup = DetectAckSetup;
64
73
    sigmatch_table[DETECT_ACK].Free = DetectAckFree;
65
66
73
    sigmatch_table[DETECT_ACK].SupportsPrefilter = PrefilterTcpAckIsPrefilterable;
67
73
    sigmatch_table[DETECT_ACK].SetupPrefilter = PrefilterSetupTcpAck;
68
#ifdef UNITTESTS
69
    sigmatch_table[DETECT_ACK].RegisterTests = DetectAckRegisterTests;
70
#endif
71
73
}
72
73
/**
74
 * \internal
75
 * \brief This function is used to match packets with a given Ack number
76
 *
77
 * \param t pointer to thread vars
78
 * \param det_ctx pointer to the pattern matcher thread
79
 * \param p pointer to the current packet
80
 * \param m pointer to the sigmatch that we will cast into DetectAckData
81
 *
82
 * \retval 0 no match
83
 * \retval 1 match
84
 */
85
static int DetectAckMatch(DetectEngineThreadCtx *det_ctx,
86
                          Packet *p, const Signature *s, const SigMatchCtx *ctx)
87
2.60k
{
88
2.60k
    const DetectAckData *data = (const DetectAckData *)ctx;
89
90
    /* This is only needed on TCP packets */
91
2.60k
    if (!(PKT_IS_TCP(p)) || PKT_IS_PSEUDOPKT(p)) {
92
831
        return 0;
93
831
    }
94
95
1.76k
    return (data->ack == TCP_GET_ACK(p)) ? 1 : 0;
96
2.60k
}
97
98
/**
99
 * \internal
100
 * \brief this function is used to add the ack option into the signature
101
 *
102
 * \param de_ctx pointer to the Detection Engine Context
103
 * \param s pointer to the Current Signature
104
 * \param m pointer to the Current SigMatch
105
 * \param optstr pointer to the user provided options
106
 *
107
 * \retval 0 on Success
108
 * \retval -1 on Failure
109
 */
110
static int DetectAckSetup(DetectEngineCtx *de_ctx, Signature *s, const char *optstr)
111
1.55k
{
112
1.55k
    DetectAckData *data = NULL;
113
1.55k
    SigMatch *sm = NULL;
114
115
1.55k
    data = SCMalloc(sizeof(DetectAckData));
116
1.55k
    if (unlikely(data == NULL))
117
0
        goto error;
118
119
1.55k
    sm = SigMatchAlloc();
120
1.55k
    if (sm == NULL)
121
0
        goto error;
122
123
1.55k
    sm->type = DETECT_ACK;
124
125
1.55k
    if (StringParseUint32(&data->ack, 10, 0, optstr) < 0) {
126
129
        goto error;
127
129
    }
128
1.43k
    sm->ctx = (SigMatchCtx*)data;
129
130
1.43k
    SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
131
1.43k
    s->flags |= SIG_FLAG_REQUIRE_PACKET;
132
133
1.43k
    return 0;
134
135
129
error:
136
129
    if (data)
137
129
        SCFree(data);
138
129
    if (sm)
139
129
        SigMatchFree(de_ctx, sm);
140
129
    return -1;
141
142
1.55k
}
143
144
/**
145
 * \internal
146
 * \brief this function will free memory associated with ack option
147
 *
148
 * \param data pointer to ack configuration data
149
 */
150
static void DetectAckFree(DetectEngineCtx *de_ctx, void *ptr)
151
2.18k
{
152
2.18k
    DetectAckData *data = (DetectAckData *)ptr;
153
2.18k
    SCFree(data);
154
2.18k
}
155
156
/* prefilter code */
157
158
static void
159
PrefilterPacketAckMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
160
0
{
161
0
    const PrefilterPacketHeaderCtx *ctx = pectx;
162
163
0
    if (!PrefilterPacketHeaderExtraMatch(ctx, p))
164
0
        return;
165
166
0
    if ((p->proto) == IPPROTO_TCP && !(PKT_IS_PSEUDOPKT(p)) &&
167
0
        (p->tcph != NULL) && (TCP_GET_ACK(p) == ctx->v1.u32[0]))
168
0
    {
169
0
        SCLogDebug("packet matches TCP ack %u", ctx->v1.u32[0]);
170
0
        PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
171
0
    }
172
0
}
173
174
static void
175
PrefilterPacketAckSet(PrefilterPacketHeaderValue *v, void *smctx)
176
0
{
177
0
    const DetectAckData *a = smctx;
178
0
    v->u32[0] = a->ack;
179
0
}
180
181
static bool
182
PrefilterPacketAckCompare(PrefilterPacketHeaderValue v, void *smctx)
183
0
{
184
0
    const DetectAckData *a = smctx;
185
0
    if (v.u32[0] == a->ack)
186
0
        return true;
187
0
    return false;
188
0
}
189
190
static int PrefilterSetupTcpAck(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
191
0
{
192
0
    return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_ACK,
193
0
        PrefilterPacketAckSet,
194
0
        PrefilterPacketAckCompare,
195
0
        PrefilterPacketAckMatch);
196
0
}
197
198
static bool PrefilterTcpAckIsPrefilterable(const Signature *s)
199
0
{
200
0
    const SigMatch *sm;
201
0
    for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH] ; sm != NULL; sm = sm->next) {
202
0
        switch (sm->type) {
203
0
            case DETECT_ACK:
204
0
                return true;
205
0
        }
206
0
    }
207
0
    return false;
208
0
}
209
210
#ifdef UNITTESTS
211
#include "detect-engine-alert.h"
212
/**
213
 * \internal
214
 * \brief This test tests sameip success and failure.
215
 */
216
static int DetectAckSigTest01(void)
217
{
218
    Packet *p1 = NULL;
219
    Packet *p2 = NULL;
220
    Packet *p3 = NULL;
221
    ThreadVars th_v;
222
    DetectEngineThreadCtx *det_ctx;
223
    int result = 0;
224
225
    memset(&th_v, 0, sizeof(th_v));
226
227
    /* TCP w/ack=42 */
228
    p1 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
229
    p1->tcph->th_ack = htonl(42);
230
231
    /* TCP w/ack=100 */
232
    p2 = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
233
    p2->tcph->th_ack = htonl(100);
234
235
    /* ICMP */
236
    p3 = UTHBuildPacket(NULL, 0, IPPROTO_ICMP);
237
238
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
239
    if (de_ctx == NULL) {
240
        goto end;
241
    }
242
243
    de_ctx->flags |= DE_QUIET;
244
245
    /* These three are crammed in here as there is no Parse */
246
    if (SigInit(de_ctx,
247
                "alert tcp any any -> any any "
248
                "(msg:\"Testing ack\";ack:foo;sid:1;)") != NULL)
249
    {
250
        printf("invalid ack accepted: ");
251
        goto cleanup_engine;
252
    }
253
    if (SigInit(de_ctx,
254
                "alert tcp any any -> any any "
255
                "(msg:\"Testing ack\";ack:9999999999;sid:1;)") != NULL)
256
    {
257
        printf("overflowing ack accepted: ");
258
        goto cleanup_engine;
259
    }
260
    if (SigInit(de_ctx,
261
                "alert tcp any any -> any any "
262
                "(msg:\"Testing ack\";ack:-100;sid:1;)") != NULL)
263
    {
264
        printf("negative ack accepted: ");
265
        goto cleanup_engine;
266
    }
267
268
    de_ctx->sig_list = SigInit(de_ctx,
269
                               "alert tcp any any -> any any "
270
                               "(msg:\"Testing ack\";ack:41;sid:1;)");
271
    if (de_ctx->sig_list == NULL) {
272
        goto cleanup_engine;
273
    }
274
275
    de_ctx->sig_list->next = SigInit(de_ctx,
276
                                     "alert tcp any any -> any any "
277
                                     "(msg:\"Testing ack\";ack:42;sid:2;)");
278
    if (de_ctx->sig_list->next == NULL) {
279
        goto cleanup_engine;
280
    }
281
282
    SigGroupBuild(de_ctx);
283
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
284
285
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p1);
286
    if (PacketAlertCheck(p1, 1) != 0) {
287
        printf("sid 1 alerted, but should not have: ");
288
        goto cleanup;
289
    }
290
    if (PacketAlertCheck(p1, 2) == 0) {
291
        printf("sid 2 did not alert, but should have: ");
292
        goto cleanup;
293
    }
294
295
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p2);
296
    if (PacketAlertCheck(p2, 1) != 0) {
297
        printf("sid 1 alerted, but should not have: ");
298
        goto cleanup;
299
    }
300
    if (PacketAlertCheck(p2, 2) != 0) {
301
        printf("sid 2 alerted, but should not have: ");
302
        goto cleanup;
303
    }
304
305
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p3);
306
    if (PacketAlertCheck(p3, 1) != 0) {
307
        printf("sid 1 alerted, but should not have: ");
308
        goto cleanup;
309
    }
310
    if (PacketAlertCheck(p3, 2) != 0) {
311
        printf("sid 2 alerted, but should not have: ");
312
        goto cleanup;
313
    }
314
315
    result = 1;
316
317
cleanup:
318
    SigGroupCleanup(de_ctx);
319
    SigCleanSignatures(de_ctx);
320
321
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
322
323
cleanup_engine:
324
    DetectEngineCtxFree(de_ctx);
325
326
end:
327
    return result;
328
}
329
330
/**
331
 * \internal
332
 * \brief This function registers unit tests for DetectAck
333
 */
334
static void DetectAckRegisterTests(void)
335
{
336
    UtRegisterTest("DetectAckSigTest01", DetectAckSigTest01);
337
}
338
#endif /* UNITTESTS */