Coverage Report

Created: 2025-12-31 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/detect-within.c
Line
Count
Source
1
/* Copyright (C) 2007-2023 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
 * \author Anoop Saldanha <anoopsaldanha@gmail.com>
23
 *
24
 * Implements the within keyword
25
 */
26
27
#include "suricata-common.h"
28
29
#include "decode.h"
30
31
#include "detect.h"
32
#include "detect-engine.h"
33
#include "detect-parse.h"
34
#include "detect-content.h"
35
#include "detect-uricontent.h"
36
#include "detect-byte.h"
37
#include "app-layer.h"
38
39
#include "flow-var.h"
40
41
#include "util-byte.h"
42
#include "util-debug.h"
43
#include "detect-pcre.h"
44
#include "detect-within.h"
45
#include "util-unittest.h"
46
47
static int DetectWithinSetup(DetectEngineCtx *, Signature *, const char *);
48
#ifdef UNITTESTS
49
static void DetectWithinRegisterTests(void);
50
#endif
51
52
void DetectWithinRegister(void)
53
73
{
54
73
    sigmatch_table[DETECT_WITHIN].name = "within";
55
73
    sigmatch_table[DETECT_WITHIN].desc = "indicate that this content match has to be within a certain distance of the previous content keyword match";
56
73
    sigmatch_table[DETECT_WITHIN].url = "/rules/payload-keywords.html#within";
57
73
    sigmatch_table[DETECT_WITHIN].Match = NULL;
58
73
    sigmatch_table[DETECT_WITHIN].Setup = DetectWithinSetup;
59
#ifdef UNITTESTS
60
    sigmatch_table[DETECT_WITHIN].RegisterTests = DetectWithinRegisterTests;
61
#endif
62
73
}
63
64
/** \brief Setup within pattern (content/uricontent) modifier.
65
 *
66
 *  \todo apply to uricontent
67
 *
68
 *  \retval 0 ok
69
 *  \retval -1 error, sig needs to be invalidated
70
 */
71
static int DetectWithinSetup(DetectEngineCtx *de_ctx, Signature *s, const char *withinstr)
72
71.5k
{
73
71.5k
    const char *str = withinstr;
74
75
    /* retrieve the sm to apply the within against */
76
71.5k
    SigMatch *pm = DetectGetLastSMFromLists(s, DETECT_CONTENT, -1);
77
71.5k
    if (pm == NULL) {
78
2.91k
        SCLogError("within needs preceding content option");
79
2.91k
        return -1;
80
2.91k
    }
81
82
    /* verify other conditions */
83
68.6k
    DetectContentData *cd = (DetectContentData *)pm->ctx;
84
68.6k
    if (cd->flags & DETECT_CONTENT_WITHIN) {
85
1.00k
        SCLogError("can't use multiple withins for the same content.");
86
1.00k
        return -1;
87
1.00k
    }
88
67.6k
    if ((cd->flags & DETECT_CONTENT_DEPTH) || (cd->flags & DETECT_CONTENT_OFFSET)) {
89
702
        SCLogError("can't use a relative "
90
702
                   "keyword like within/distance with a absolute "
91
702
                   "relative keyword like depth/offset for the same "
92
702
                   "content.");
93
702
        return -1;
94
702
    }
95
66.9k
    if (cd->flags & DETECT_CONTENT_NEGATED && cd->flags & DETECT_CONTENT_FAST_PATTERN) {
96
12
        SCLogError("can't have a relative "
97
12
                   "negated keyword set along with a fast_pattern");
98
12
        return -1;
99
12
    }
100
66.9k
    if (cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) {
101
36
        SCLogError("can't have a relative "
102
36
                   "keyword set along with a fast_pattern:only;");
103
36
        return -1;
104
36
    }
105
66.8k
    if (str[0] != '-' && isalpha((unsigned char)str[0])) {
106
2.11k
        DetectByteIndexType index;
107
2.11k
        if (!DetectByteRetrieveSMVar(str, s, &index)) {
108
854
            SCLogError("unknown byte_ keyword var "
109
854
                       "seen in within - %s",
110
854
                    str);
111
854
            return -1;
112
854
        }
113
1.26k
        cd->within = index;
114
1.26k
        cd->flags |= DETECT_CONTENT_WITHIN_VAR;
115
64.7k
    } else {
116
64.7k
        if ((StringParseI32RangeCheck(&cd->within, 0, 0, str, -DETECT_CONTENT_VALUE_MAX,
117
64.7k
                     DETECT_CONTENT_VALUE_MAX) < 0)) {
118
586
            SCLogError("invalid value for within: %s", str);
119
586
            return -1;
120
586
        }
121
122
64.1k
        if (cd->within < (int32_t)cd->content_len) {
123
623
            SCLogError("within argument \"%" PRIi32 "\" is "
124
623
                       "less than the content length \"%" PRIu32 "\" which is invalid, since "
125
623
                       "this will never match.  Invalidating signature",
126
623
                    cd->within, cd->content_len);
127
623
            return -1;
128
623
        }
129
64.1k
    }
130
64.8k
    cd->flags |= DETECT_CONTENT_WITHIN;
131
132
    /* these are the only ones against which we set a flag.  We have other
133
     * relative keywords like byttest, isdataat, bytejump, but we don't
134
     * set a flag against them */
135
64.8k
    SigMatch *prev_pm = DetectGetLastSMByListPtr(s, pm->prev,
136
64.8k
            DETECT_CONTENT, DETECT_PCRE, -1);
137
64.8k
    if (prev_pm == NULL) {
138
23.6k
        return 0;
139
23.6k
    }
140
41.2k
    if (prev_pm->type == DETECT_CONTENT) {
141
28.9k
        DetectContentData *prev_cd = (DetectContentData *)prev_pm->ctx;
142
28.9k
        if (prev_cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) {
143
2
            SCLogError("previous keyword "
144
2
                       "has a fast_pattern:only; set. Can't "
145
2
                       "have relative keywords around a fast_pattern "
146
2
                       "only content");
147
2
            return -1;
148
2
        }
149
28.9k
        prev_cd->flags |= DETECT_CONTENT_WITHIN_NEXT;
150
28.9k
    } else if (prev_pm->type == DETECT_PCRE) {
151
12.2k
        DetectPcreData *pd = (DetectPcreData *)prev_pm->ctx;
152
12.2k
        pd->flags |= DETECT_PCRE_RELATIVE_NEXT;
153
12.2k
    }
154
41.2k
    return 0;
155
41.2k
}
156
157
/***********************************Unittests**********************************/
158
159
#ifdef UNITTESTS
160
#include "util-unittest-helper.h"
161
 /**
162
 * \test DetectWithinTestPacket01 is a test to check matches of
163
 * within, if the previous keyword is pcre (bug 145)
164
 */
165
static int DetectWithinTestPacket01 (void)
166
{
167
    uint8_t *buf = (uint8_t *)"GET /AllWorkAndNoPlayMakesWillADullBoy HTTP/1.0"
168
                    "User-Agent: Wget/1.11.4"
169
                    "Accept: */*"
170
                    "Host: www.google.com"
171
                    "Connection: Keep-Alive"
172
                    "Date: Mon, 04 Jan 2010 17:29:39 GMT";
173
    uint16_t buflen = strlen((char *)buf);
174
175
    Packet *p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
176
    FAIL_IF_NULL(p);
177
178
    char sig[] = "alert tcp any any -> any any (msg:\"pcre with within "
179
                 "modifier\"; pcre:\"/AllWorkAndNoPlayMakesWillADullBoy/\";"
180
                 " content:\"HTTP\"; within:5; sid:49; rev:1;)";
181
    int result = UTHPacketMatchSig(p, sig);
182
    FAIL_IF_NOT(result == 1);
183
184
    UTHFreePacket(p);
185
    PASS;
186
}
187
188
189
static int DetectWithinTestPacket02 (void)
190
{
191
    uint8_t *buf = (uint8_t *)"Zero Five Ten Fourteen";
192
    uint16_t buflen = strlen((char *)buf);
193
194
    Packet *p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
195
    FAIL_IF_NULL(p);
196
197
    char sig[] = "alert tcp any any -> any any (msg:\"pcre with within "
198
                 "modifier\"; content:\"Five\"; content:\"Ten\"; within:3; distance:1; sid:1;)";
199
200
    int result = UTHPacketMatchSig(p, sig);
201
    FAIL_IF_NOT(result == 1);
202
203
    UTHFreePacket(p);
204
    PASS;
205
}
206
207
static int DetectWithinTestVarSetup(void)
208
{
209
    char sig[] = "alert tcp any any -> any any ( "
210
        "msg:\"test rule\"; "
211
        "content:\"abc\"; "
212
        "http_client_body; "
213
        "byte_extract:2,0,somevar,relative; "
214
        "content:\"def\"; "
215
        "within:somevar; "
216
        "http_client_body; "
217
        "sid:4; rev:1;)";
218
219
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
220
    FAIL_IF_NULL(de_ctx);
221
222
    Signature *s = DetectEngineAppendSig(de_ctx, sig);
223
    FAIL_IF_NULL(s);
224
225
    DetectEngineCtxFree(de_ctx);
226
    PASS;
227
}
228
229
void DetectWithinRegisterTests(void)
230
{
231
    UtRegisterTest("DetectWithinTestPacket01", DetectWithinTestPacket01);
232
    UtRegisterTest("DetectWithinTestPacket02", DetectWithinTestPacket02);
233
    UtRegisterTest("DetectWithinTestVarSetup", DetectWithinTestVarSetup);
234
}
235
#endif /* UNITTESTS */