suricata
detect-within.c
Go to the documentation of this file.
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
47static int DetectWithinSetup(DetectEngineCtx *, Signature *, const char *);
48#ifdef UNITTESTS
49static void DetectWithinRegisterTests(void);
50#endif
51
53{
55 sigmatch_table[DETECT_WITHIN].desc = "indicate that this content match has to be within a certain distance of the previous content keyword match";
56 sigmatch_table[DETECT_WITHIN].url = "/rules/payload-keywords.html#within";
58 sigmatch_table[DETECT_WITHIN].Setup = DetectWithinSetup;
59#ifdef UNITTESTS
60 sigmatch_table[DETECT_WITHIN].RegisterTests = DetectWithinRegisterTests;
61#endif
62}
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 */
71static int DetectWithinSetup(DetectEngineCtx *de_ctx, Signature *s, const char *withinstr)
72{
73 const char *str = withinstr;
74
75 /* retrieve the sm to apply the within against */
77 if (pm == NULL) {
78 SCLogError("within needs preceding content option");
79 return -1;
80 }
81
82 /* verify other conditions */
84 if (cd->flags & DETECT_CONTENT_WITHIN) {
85 SCLogError("can't use multiple withins for the same content.");
86 return -1;
87 }
88 if ((cd->flags & DETECT_CONTENT_DEPTH) || (cd->flags & DETECT_CONTENT_OFFSET)) {
89 SCLogError("can't use a relative "
90 "keyword like within/distance with a absolute "
91 "relative keyword like depth/offset for the same "
92 "content.");
93 return -1;
94 }
95 if (cd->flags & DETECT_CONTENT_NEGATED && cd->flags & DETECT_CONTENT_FAST_PATTERN) {
96 SCLogError("can't have a relative "
97 "negated keyword set along with a fast_pattern");
98 return -1;
99 }
100 if (cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) {
101 SCLogError("can't have a relative "
102 "keyword set along with a fast_pattern:only;");
103 return -1;
104 }
105 if (str[0] != '-' && isalpha((unsigned char)str[0])) {
107 if (!DetectByteRetrieveSMVar(str, s, -1, &index)) {
108 SCLogError("unknown byte_ keyword var "
109 "seen in within - %s",
110 str);
111 return -1;
112 }
113 cd->within = index;
114 cd->flags |= DETECT_CONTENT_WITHIN_VAR;
115 } else {
116 if ((StringParseI32RangeCheck(&cd->within, 0, 0, str, -DETECT_CONTENT_VALUE_MAX,
118 SCLogError("invalid value for within: %s", str);
119 return -1;
120 }
121
122 if (cd->within < (int32_t)cd->content_len) {
123 SCLogError("within argument \"%" PRIi32 "\" is "
124 "less than the content length \"%" PRIu32 "\" which is invalid, since "
125 "this will never match. Invalidating signature",
126 cd->within, cd->content_len);
127 return -1;
128 }
129 }
130 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 SigMatch *prev_pm = DetectGetLastSMByListPtr(s, pm->prev,
137 if (prev_pm == NULL) {
138 return 0;
139 }
140 if (prev_pm->type == DETECT_CONTENT) {
141 DetectContentData *prev_cd = (DetectContentData *)prev_pm->ctx;
142 if (prev_cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) {
143 SCLogError("previous keyword "
144 "has a fast_pattern:only; set. Can't "
145 "have relative keywords around a fast_pattern "
146 "only content");
147 return -1;
148 }
149 prev_cd->flags |= DETECT_CONTENT_WITHIN_NEXT;
150 } else if (prev_pm->type == DETECT_PCRE) {
151 DetectPcreData *pd = (DetectPcreData *)prev_pm->ctx;
152 pd->flags |= DETECT_PCRE_RELATIVE_NEXT;
153 }
154 return 0;
155}
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 */
165static 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
189static 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
207static 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
221
223 FAIL_IF_NULL(s);
224
226 PASS;
227}
228
229void DetectWithinRegisterTests(void)
230{
231 UtRegisterTest("DetectWithinTestPacket01", DetectWithinTestPacket01);
232 UtRegisterTest("DetectWithinTestPacket02", DetectWithinTestPacket02);
233 UtRegisterTest("DetectWithinTestVarSetup", DetectWithinTestVarSetup);
234}
235#endif /* UNITTESTS */
bool DetectByteRetrieveSMVar(const char *arg, const Signature *s, int sm_list, DetectByteIndexType *index)
Used to retrieve args from BM.
Definition detect-byte.c:41
uint8_t DetectByteIndexType
Definition detect-byte.h:28
#define DETECT_CONTENT_DEPTH
#define DETECT_CONTENT_VALUE_MAX
#define DETECT_CONTENT_FAST_PATTERN_ONLY
#define DETECT_CONTENT_WITHIN_VAR
#define DETECT_CONTENT_WITHIN
#define DETECT_CONTENT_FAST_PATTERN
#define DETECT_CONTENT_WITHIN_NEXT
#define DETECT_CONTENT_OFFSET
#define DETECT_CONTENT_NEGATED
DetectEngineCtx * DetectEngineCtxInit(void)
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Signature * DetectEngineAppendSig(DetectEngineCtx *, const char *)
Parse and append a Signature into the Detection Engine Context signature list.
SigMatch * DetectGetLastSMByListPtr(const Signature *s, SigMatch *sm_list,...)
Returns the sm with the largest index (added last) from the list passed to us as a pointer.
SigMatch * DetectGetLastSMFromLists(const Signature *s,...)
Returns the sm with the largest index (added latest) from the lists passed to us.
SigTableElmt * sigmatch_table
#define DETECT_PCRE_RELATIVE_NEXT
Definition detect-pcre.h:34
void DetectWithinRegister(void)
DetectEngineCtx * de_ctx
#define FAIL_IF_NULL(expr)
Fail a test if expression evaluates to NULL.
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define FAIL_IF_NOT(expr)
Fail a test if expression evaluates to false.
#define PASS
Pass the test.
main detection engine ctx
Definition detect.h:932
a single match condition for a signature
Definition detect.h:356
uint16_t type
Definition detect.h:357
struct SigMatch_ * prev
Definition detect.h:361
SigMatchCtx * ctx
Definition detect.h:359
const char * url
Definition detect.h:1462
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition detect.h:1441
const char * desc
Definition detect.h:1461
void(* RegisterTests)(void)
Definition detect.h:1448
int(* Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition detect.h:1421
const char * name
Definition detect.h:1459
Signature container.
Definition detect.h:668
#define str(s)
int StringParseI32RangeCheck(int32_t *res, int base, size_t len, const char *str, int32_t min, int32_t max)
Definition util-byte.c:716
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
int UTHPacketMatchSig(Packet *p, const char *sig)
Packet * UTHBuildPacket(uint8_t *payload, uint16_t payload_len, uint8_t ipproto)
UTHBuildPacket is a wrapper that build packets with default ip and port fields.
void UTHFreePacket(Packet *p)
UTHFreePacket: function to release the allocated data from UTHBuildPacket and the packet itself.