Coverage Report

Created: 2025-07-23 07:29

/src/suricata7/src/detect-engine-proto.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2007-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 Victor Julien <victor@inliniac.net>
22
 *
23
 * Proto part of the detection engine.
24
 *
25
 * \todo move this out of the detection plugin structure
26
 */
27
28
#include "suricata-common.h"
29
30
#include "decode.h"
31
#include "detect.h"
32
33
#include "app-layer-parser.h"
34
35
#include "flow-util.h"
36
#include "flow-var.h"
37
38
#include "detect-engine-siggroup.h"
39
#include "detect-engine-state.h"
40
41
#include "util-cidr.h"
42
#include "util-byte.h"
43
#include "util-unittest.h"
44
#include "util-unittest-helper.h"
45
#include "util-debug.h"
46
47
/**
48
 * \brief Parses a protocol sent as a string.
49
 *
50
 * \param dp  Pointer to the DetectProto instance which will be updated with the
51
 *            incoming protocol information.
52
 * \param str Pointer to the string containing the protocol name.
53
 *
54
 * \retval >=0 If proto is detected, -1 otherwise.
55
 */
56
int DetectProtoParse(DetectProto *dp, const char *str)
57
2.87M
{
58
2.87M
    if (strcasecmp(str, "tcp") == 0) {
59
796k
        dp->proto[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8);
60
796k
        SCLogDebug("TCP protocol detected");
61
2.07M
    } else if (strcasecmp(str, "tcp-pkt") == 0) {
62
169k
        dp->proto[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8);
63
169k
        SCLogDebug("TCP protocol detected, packets only");
64
169k
        dp->flags |= DETECT_PROTO_ONLY_PKT;
65
1.90M
    } else if (strcasecmp(str, "tcp-stream") == 0) {
66
20.7k
        dp->proto[IPPROTO_TCP / 8] |= 1 << (IPPROTO_TCP % 8);
67
20.7k
        SCLogDebug("TCP protocol detected, stream only");
68
20.7k
        dp->flags |= DETECT_PROTO_ONLY_STREAM;
69
1.88M
    } else if (strcasecmp(str, "udp") == 0) {
70
69.7k
        dp->proto[IPPROTO_UDP / 8] |= 1 << (IPPROTO_UDP % 8);
71
69.7k
        SCLogDebug("UDP protocol detected");
72
1.81M
    } else if (strcasecmp(str, "icmpv4") == 0) {
73
567
        dp->proto[IPPROTO_ICMP / 8] |= 1 << (IPPROTO_ICMP % 8);
74
567
        SCLogDebug("ICMPv4 protocol detected");
75
1.81M
    } else if (strcasecmp(str, "icmpv6") == 0) {
76
281
        dp->proto[IPPROTO_ICMPV6 / 8] |= 1 << (IPPROTO_ICMPV6 % 8);
77
281
        SCLogDebug("ICMPv6 protocol detected");
78
1.81M
    } else if (strcasecmp(str, "icmp") == 0) {
79
17.2k
        dp->proto[IPPROTO_ICMP / 8] |= 1 << (IPPROTO_ICMP % 8);
80
17.2k
        dp->proto[IPPROTO_ICMPV6 / 8] |= 1 << (IPPROTO_ICMPV6 % 8);
81
17.2k
        SCLogDebug("ICMP protocol detected, sig applies both to ICMPv4 and ICMPv6");
82
1.79M
    } else if (strcasecmp(str, "sctp") == 0) {
83
24
        dp->proto[IPPROTO_SCTP / 8] |= 1 << (IPPROTO_SCTP % 8);
84
24
        SCLogDebug("SCTP protocol detected");
85
1.79M
    } else if (strcasecmp(str,"ipv4") == 0 ||
86
1.79M
               strcasecmp(str,"ip4") == 0 ) {
87
21.9k
        dp->flags |= (DETECT_PROTO_IPV4 | DETECT_PROTO_ANY);
88
21.9k
        memset(dp->proto, 0xff, sizeof(dp->proto));
89
21.9k
        SCLogDebug("IPv4 protocol detected");
90
1.77M
    } else if (strcasecmp(str,"ipv6") == 0 ||
91
1.77M
               strcasecmp(str,"ip6") == 0 ) {
92
11.1k
        dp->flags |= (DETECT_PROTO_IPV6 | DETECT_PROTO_ANY);
93
11.1k
        memset(dp->proto, 0xff, sizeof(dp->proto));
94
11.1k
        SCLogDebug("IPv6 protocol detected");
95
1.76M
    } else if (strcasecmp(str,"ip") == 0 ||
96
1.76M
               strcasecmp(str,"pkthdr") == 0) {
97
        /* Proto "ip" is treated as an "any" */
98
618k
        dp->flags |= DETECT_PROTO_ANY;
99
618k
        memset(dp->proto, 0xff, sizeof(dp->proto));
100
618k
        SCLogDebug("IP protocol detected");
101
1.14M
    } else {
102
1.14M
        goto error;
103
104
        /** \todo are numeric protocols even valid? */
105
#if 0
106
        uint8_t proto_u8; /* Used to avoid sign extension */
107
108
        /* Extract out a 0-256 value with validation checks */
109
        if (ByteExtractStringUint8(&proto_u8, 10, 0, str) == -1) {
110
            // XXX
111
            SCLogDebug("DetectProtoParse: Error in extracting byte string");
112
            goto error;
113
        }
114
        proto = (int)proto_u8;
115
116
        /* Proto 0 is the same as "ip" above */
117
        if (proto == IPPROTO_IP) {
118
            dp->flags |= DETECT_PROTO_ANY;
119
        } else {
120
            dp->proto[proto / 8] |= 1<<(proto % 8);
121
        }
122
#endif
123
1.14M
    }
124
125
1.72M
    return 0;
126
1.14M
error:
127
1.14M
    return -1;
128
2.87M
}
129
130
/** \brief see if a DetectProto contains a certain proto
131
 *  \param dp detect proto to inspect
132
 *  \param proto protocol (such as IPPROTO_TCP) to look for
133
 *  \retval 0 protocol not in the set
134
 *  \retval 1 protocol is in the set */
135
int DetectProtoContainsProto(const DetectProto *dp, int proto)
136
4.76M
{
137
4.76M
    if (dp->flags & DETECT_PROTO_ANY)
138
2.33M
        return 1;
139
140
2.42M
    if (dp->proto[proto / 8] & (1<<(proto % 8)))
141
2.36M
        return 1;
142
143
65.2k
    return 0;
144
2.42M
}
145
146
/* TESTS */
147
148
#ifdef UNITTESTS
149
#include "detect-engine.h"
150
#include "detect-parse.h"
151
#include "detect-engine-mpm.h"
152
/**
153
 * \brief this function is used to initialize the detection engine context and
154
 *        setup the signature with passed values.
155
 */
156
static int DetectProtoInitTest(DetectEngineCtx **de_ctx, Signature **sig,
157
                               DetectProto *dp, const char *str)
158
{
159
    char fullstr[1024];
160
    int result = 0;
161
162
    *de_ctx = NULL;
163
    *sig = NULL;
164
165
    if (snprintf(fullstr, 1024, "alert %s any any -> any any (msg:\"DetectProto"
166
            " test\"; sid:1;)", str) >= 1024)
167
    {
168
        goto end;
169
    }
170
171
    *de_ctx = DetectEngineCtxInit();
172
    if (*de_ctx == NULL) {
173
        goto end;
174
    }
175
176
    (*de_ctx)->flags |= DE_QUIET;
177
178
    (*de_ctx)->sig_list = SigInit(*de_ctx, fullstr);
179
    if ((*de_ctx)->sig_list == NULL) {
180
        goto end;
181
    }
182
183
    *sig = (*de_ctx)->sig_list;
184
185
    if (DetectProtoParse(dp, str) < 0)
186
        goto end;
187
188
    result = 1;
189
190
end:
191
    return result;
192
}
193
194
/**
195
 * \test ProtoTestParse01 is a test to make sure that we parse the
196
 *  protocol correctly, when given valid proto option.
197
 */
198
static int ProtoTestParse01 (void)
199
{
200
    DetectProto dp;
201
    memset(&dp,0,sizeof(DetectProto));
202
203
    int r = DetectProtoParse(&dp, "6");
204
205
    FAIL_IF_NOT(r < 0);
206
207
    PASS;
208
}
209
/**
210
 * \test ProtoTestParse02 is a test to make sure that we parse the
211
 *  protocol correctly, when given "tcp" as proto option.
212
 */
213
static int ProtoTestParse02 (void)
214
{
215
    DetectProto dp;
216
    memset(&dp,0,sizeof(DetectProto));
217
218
    int r = DetectProtoParse(&dp, "tcp");
219
220
    FAIL_IF_NOT(r >= 0);
221
    FAIL_IF_NOT(dp.proto[(IPPROTO_TCP / 8)] & (1 << (IPPROTO_TCP % 8)));
222
223
    PASS;
224
}
225
/**
226
 * \test ProtoTestParse03 is a test to make sure that we parse the
227
 *  protocol correctly, when given "ip" as proto option.
228
 */
229
static int ProtoTestParse03 (void)
230
{
231
    DetectProto dp;
232
    memset(&dp,0,sizeof(DetectProto));
233
234
    int r = DetectProtoParse(&dp, "ip");
235
236
    FAIL_IF_NOT(r >= 0);
237
    FAIL_IF_NOT(dp.flags & DETECT_PROTO_ANY);
238
239
    PASS;
240
}
241
242
/**
243
 * \test ProtoTestParse04 is a test to make sure that we do not parse the
244
 *  protocol, when given an invalid proto option.
245
 */
246
static int ProtoTestParse04 (void)
247
{
248
    DetectProto dp;
249
    memset(&dp,0,sizeof(DetectProto));
250
251
    /* Check for a bad number */
252
    int r = DetectProtoParse(&dp, "4242");
253
254
    FAIL_IF_NOT(r < 0);
255
256
    PASS;
257
}
258
259
/**
260
 * \test ProtoTestParse05 is a test to make sure that we do not parse the
261
 *  protocol, when given an invalid proto option.
262
 */
263
static int ProtoTestParse05 (void)
264
{
265
    DetectProto dp;
266
    memset(&dp,0,sizeof(DetectProto));
267
268
    /* Check for a bad string */
269
    int r = DetectProtoParse(&dp, "tcp/udp");
270
271
    FAIL_IF_NOT(r < 0);
272
273
    PASS;
274
}
275
276
/**
277
 * \test make sure that we properly parse tcp-pkt
278
 */
279
static int ProtoTestParse06 (void)
280
{
281
    DetectProto dp;
282
    memset(&dp,0,sizeof(DetectProto));
283
284
    /* Check for a bad string */
285
    int r = DetectProtoParse(&dp, "tcp-pkt");
286
287
    FAIL_IF(r < 0);
288
    FAIL_IF_NOT(dp.flags & DETECT_PROTO_ONLY_PKT);
289
290
    PASS;
291
}
292
293
/**
294
 * \test make sure that we properly parse tcp-stream
295
 */
296
static int ProtoTestParse07 (void)
297
{
298
    DetectProto dp;
299
    memset(&dp,0,sizeof(DetectProto));
300
301
    /* Check for a bad string */
302
    int r = DetectProtoParse(&dp, "tcp-stream");
303
304
    FAIL_IF(r < 0);
305
    FAIL_IF_NOT(dp.flags & DETECT_PROTO_ONLY_STREAM);
306
307
    PASS;
308
}
309
310
/**
311
 * \test DetectIPProtoTestSetup01 is a test for a protocol setting up in
312
 *       signature.
313
 */
314
static int DetectProtoTestSetup01(void)
315
{
316
    DetectProto dp;
317
    Signature *sig = NULL;
318
    DetectEngineCtx *de_ctx = NULL;
319
    int i;
320
321
    memset(&dp, 0, sizeof(dp));
322
323
    FAIL_IF_NOT(DetectProtoInitTest(&de_ctx, &sig, &dp, "tcp"));
324
325
    /* The signature proto should be TCP */
326
    FAIL_IF_NOT(sig->proto.proto[(IPPROTO_TCP / 8)] & (1 << (IPPROTO_TCP % 8)));
327
328
    for (i = 2; i < 256 / 8; i++) {
329
        FAIL_IF(sig->proto.proto[i] != 0);
330
    }
331
332
    DetectEngineCtxFree(de_ctx);
333
334
    PASS;
335
}
336
337
/**
338
 * \test DetectProtoTestSetup02 is a test for a icmpv4 and icmpv6
339
 *       protocol setting up in signature.
340
 */
341
static int DetectProtoTestSetup02(void)
342
{
343
    DetectProto dp;
344
    Signature *sig_icmpv4 = NULL;
345
    Signature *sig_icmpv6 = NULL;
346
    Signature *sig_icmp = NULL;
347
    DetectEngineCtx *de_ctx = NULL;
348
349
    memset(&dp, 0, sizeof(dp));
350
351
    FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmpv4, &dp, "icmpv4") == 0);
352
    FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmpv6, &dp, "icmpv6") == 0);
353
    FAIL_IF(DetectProtoInitTest(&de_ctx, &sig_icmp, &dp, "icmp") == 0);
354
355
    FAIL_IF_NOT(sig_icmpv4->proto.proto[IPPROTO_ICMP / 8] & (1 << (IPPROTO_ICMP % 8)));
356
    FAIL_IF_NOT(sig_icmpv6->proto.proto[IPPROTO_ICMPV6 / 8] & (1 << (IPPROTO_ICMPV6 % 8)));
357
358
    FAIL_IF_NOT(sig_icmp->proto.proto[IPPROTO_ICMP / 8] & (1 << (IPPROTO_ICMP % 8)));
359
    FAIL_IF_NOT(sig_icmp->proto.proto[IPPROTO_ICMPV6 / 8] & (1 << (IPPROTO_ICMPV6 % 8)));
360
361
    DetectEngineCtxFree(de_ctx);
362
363
    PASS;
364
}
365
366
/**
367
 * \test signature parsing with tcp-pkt and tcp-stream
368
 */
369
370
static int DetectProtoTestSig01(void)
371
{
372
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
373
    FAIL_IF_NULL(de_ctx);
374
375
    de_ctx->flags |= DE_QUIET;
376
377
    Signature *s = DetectEngineAppendSig(
378
            de_ctx, "alert tcp-pkt any any -> any any (msg:\"tcp-pkt\"; content:\"blah\"; sid:1;)");
379
    FAIL_IF_NULL(s);
380
381
    s = DetectEngineAppendSig(de_ctx,
382
            "alert tcp-stream any any -> any any (msg:\"tcp-stream\"; content:\"blah\"; sid:2;)");
383
    FAIL_IF_NULL(s);
384
385
    DetectEngineCtxFree(de_ctx);
386
387
    PASS;
388
}
389
#endif /* UNITTESTS */
390
391
/**
392
 * \brief this function registers unit tests for DetectProto
393
 */
394
void DetectProtoTests(void)
395
0
{
396
#ifdef UNITTESTS
397
    UtRegisterTest("ProtoTestParse01", ProtoTestParse01);
398
    UtRegisterTest("ProtoTestParse02", ProtoTestParse02);
399
    UtRegisterTest("ProtoTestParse03", ProtoTestParse03);
400
    UtRegisterTest("ProtoTestParse04", ProtoTestParse04);
401
    UtRegisterTest("ProtoTestParse05", ProtoTestParse05);
402
    UtRegisterTest("ProtoTestParse06", ProtoTestParse06);
403
    UtRegisterTest("ProtoTestParse07", ProtoTestParse07);
404
405
    UtRegisterTest("DetectProtoTestSetup01", DetectProtoTestSetup01);
406
    UtRegisterTest("DetectProtoTestSetup02", DetectProtoTestSetup02);
407
408
    UtRegisterTest("DetectProtoTestSig01", DetectProtoTestSig01);
409
#endif /* UNITTESTS */
410
0
}
411