Coverage Report

Created: 2026-03-31 07:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata/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
74.2k
{
73
74.2k
    const char *str = withinstr;
74
75
    /* retrieve the sm to apply the within against */
76
74.2k
    SigMatch *pm = SCDetectGetLastSMFromLists(s, DETECT_CONTENT, -1);
77
74.2k
    if (pm == NULL) {
78
1.84k
        SCLogError("within needs preceding content option");
79
1.84k
        return -1;
80
1.84k
    }
81
82
    /* verify other conditions */
83
72.4k
    DetectContentData *cd = (DetectContentData *)pm->ctx;
84
72.4k
    if (cd->flags & DETECT_CONTENT_WITHIN) {
85
786
        SCLogError("can't use multiple withins for the same content.");
86
786
        return -1;
87
786
    }
88
71.6k
    if ((cd->flags & DETECT_CONTENT_DEPTH) || (cd->flags & DETECT_CONTENT_OFFSET)) {
89
715
        SCLogError("can't use a relative "
90
715
                   "keyword like within/distance with a absolute "
91
715
                   "relative keyword like depth/offset for the same "
92
715
                   "content.");
93
715
        return -1;
94
715
    }
95
70.9k
    if (cd->flags & DETECT_CONTENT_NEGATED && cd->flags & DETECT_CONTENT_FAST_PATTERN) {
96
29
        SCLogError("can't have a relative "
97
29
                   "negated keyword set along with a fast_pattern");
98
29
        return -1;
99
29
    }
100
70.8k
    if (cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) {
101
41
        SCLogError("can't have a relative "
102
41
                   "keyword set along with a fast_pattern:only;");
103
41
        return -1;
104
41
    }
105
70.8k
    if (str[0] != '-' && isalpha((unsigned char)str[0])) {
106
2.37k
        DetectByteIndexType index;
107
2.37k
        if (!DetectByteRetrieveSMVar(str, s, -1, &index)) {
108
642
            SCLogError("unknown byte_ keyword var "
109
642
                       "seen in within - %s",
110
642
                    str);
111
642
            return -1;
112
642
        }
113
1.73k
        cd->within = index;
114
1.73k
        cd->flags |= DETECT_CONTENT_WITHIN_VAR;
115
68.4k
    } else {
116
68.4k
        if ((StringParseI32RangeCheck(&cd->within, 0, 0, str, -DETECT_CONTENT_VALUE_MAX,
117
68.4k
                     DETECT_CONTENT_VALUE_MAX) < 0)) {
118
624
            SCLogError("invalid value for within: %s", str);
119
624
            return -1;
120
624
        }
121
122
67.8k
        if (cd->within < (int32_t)cd->content_len) {
123
747
            SCLogError("within argument \"%" PRIi32 "\" is "
124
747
                       "less than the content length \"%" PRIu32 "\" which is invalid, since "
125
747
                       "this will never match.  Invalidating signature",
126
747
                    cd->within, cd->content_len);
127
747
            return -1;
128
747
        }
129
67.8k
    }
130
68.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
68.8k
    SigMatch *prev_pm = DetectGetLastSMByListPtr(s, pm->prev,
136
68.8k
            DETECT_CONTENT, DETECT_PCRE, -1);
137
68.8k
    if (prev_pm == NULL) {
138
22.5k
        return 0;
139
22.5k
    }
140
46.2k
    if (prev_pm->type == DETECT_CONTENT) {
141
32.4k
        DetectContentData *prev_cd = (DetectContentData *)prev_pm->ctx;
142
32.4k
        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
32.4k
        prev_cd->flags |= DETECT_CONTENT_WITHIN_NEXT;
150
32.4k
    } else if (prev_pm->type == DETECT_PCRE) {
151
13.7k
        DetectPcreData *pd = (DetectPcreData *)prev_pm->ctx;
152
13.7k
        pd->flags |= DETECT_PCRE_RELATIVE_NEXT;
153
13.7k
    }
154
46.2k
    return 0;
155
46.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 */