/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 */ |