/src/suricata7/src/detect-stream_size.c
Line | Count | Source |
1 | | /* Copyright (C) 2007-2020 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 Gurvinder Singh <gurvindersinghdahiya@gmail.com> |
22 | | * |
23 | | * Stream size for the engine. |
24 | | */ |
25 | | |
26 | | #include "suricata-common.h" |
27 | | #include "stream-tcp.h" |
28 | | #include "util-unittest.h" |
29 | | |
30 | | #include "detect.h" |
31 | | #include "detect-parse.h" |
32 | | |
33 | | #include "flow.h" |
34 | | #include "detect-stream_size.h" |
35 | | #include "stream-tcp-private.h" |
36 | | #include "detect-engine-prefilter-common.h" |
37 | | #include "detect-engine-uint.h" |
38 | | #include "util-debug.h" |
39 | | #include "util-byte.h" |
40 | | |
41 | | |
42 | | /*prototypes*/ |
43 | | static int DetectStreamSizeMatch (DetectEngineThreadCtx *, Packet *, |
44 | | const Signature *, const SigMatchCtx *); |
45 | | static int DetectStreamSizeSetup (DetectEngineCtx *, Signature *, const char *); |
46 | | void DetectStreamSizeFree(DetectEngineCtx *de_ctx, void *); |
47 | | #ifdef UNITTESTS |
48 | | static void DetectStreamSizeRegisterTests(void); |
49 | | #endif |
50 | | static int PrefilterSetupStreamSize(DetectEngineCtx *de_ctx, SigGroupHead *sgh); |
51 | | static bool PrefilterStreamSizeIsPrefilterable(const Signature *s); |
52 | | |
53 | | /** |
54 | | * \brief Registration function for stream_size: keyword |
55 | | */ |
56 | | |
57 | | void DetectStreamSizeRegister(void) |
58 | 73 | { |
59 | 73 | sigmatch_table[DETECT_STREAM_SIZE].name = "stream_size"; |
60 | 73 | sigmatch_table[DETECT_STREAM_SIZE].desc = "match on amount of bytes of a stream"; |
61 | 73 | sigmatch_table[DETECT_STREAM_SIZE].url = "/rules/flow-keywords.html#stream-size"; |
62 | 73 | sigmatch_table[DETECT_STREAM_SIZE].Match = DetectStreamSizeMatch; |
63 | 73 | sigmatch_table[DETECT_STREAM_SIZE].Setup = DetectStreamSizeSetup; |
64 | 73 | sigmatch_table[DETECT_STREAM_SIZE].Free = DetectStreamSizeFree; |
65 | | #ifdef UNITTESTS |
66 | | sigmatch_table[DETECT_STREAM_SIZE].RegisterTests = DetectStreamSizeRegisterTests; |
67 | | #endif |
68 | 73 | sigmatch_table[DETECT_STREAM_SIZE].SupportsPrefilter = PrefilterStreamSizeIsPrefilterable; |
69 | 73 | sigmatch_table[DETECT_STREAM_SIZE].SetupPrefilter = PrefilterSetupStreamSize; |
70 | 73 | } |
71 | | |
72 | | static int DetectStreamSizeMatchAux(const DetectStreamSizeData *sd, const TcpSession *ssn) |
73 | 1.47k | { |
74 | 1.47k | int ret = 0; |
75 | 1.47k | uint32_t csdiff = 0; |
76 | 1.47k | uint32_t ssdiff = 0; |
77 | | |
78 | 1.47k | if (sd->flags == StreamSizeServer) { |
79 | | /* get the server stream size */ |
80 | 1.47k | ssdiff = ssn->server.next_seq - ssn->server.isn; |
81 | 1.47k | ret = DetectU32Match(ssdiff, &sd->du32); |
82 | | |
83 | 1.47k | } else if (sd->flags == StreamSizeClient) { |
84 | | /* get the client stream size */ |
85 | 0 | csdiff = ssn->client.next_seq - ssn->client.isn; |
86 | 0 | ret = DetectU32Match(csdiff, &sd->du32); |
87 | |
|
88 | 0 | } else if (sd->flags == StreamSizeBoth) { |
89 | 0 | ssdiff = ssn->server.next_seq - ssn->server.isn; |
90 | 0 | csdiff = ssn->client.next_seq - ssn->client.isn; |
91 | |
|
92 | 0 | if (DetectU32Match(ssdiff, &sd->du32) && DetectU32Match(csdiff, &sd->du32)) |
93 | 0 | ret = 1; |
94 | |
|
95 | 0 | } else if (sd->flags == StreamSizeEither) { |
96 | 0 | ssdiff = ssn->server.next_seq - ssn->server.isn; |
97 | 0 | csdiff = ssn->client.next_seq - ssn->client.isn; |
98 | |
|
99 | 0 | if (DetectU32Match(ssdiff, &sd->du32) || DetectU32Match(csdiff, &sd->du32)) |
100 | 0 | ret = 1; |
101 | 0 | } |
102 | 1.47k | return ret; |
103 | 1.47k | } |
104 | | |
105 | | /** |
106 | | * \brief This function is used to match Stream size rule option on a packet with those passed via |
107 | | * stream_size: |
108 | | * |
109 | | * \param t pointer to thread vars |
110 | | * \param det_ctx pointer to the pattern matcher thread |
111 | | * \param p pointer to the current packet |
112 | | * \param m pointer to the sigmatch that we will cast into DetectStreamSizeData |
113 | | * |
114 | | * \retval 0 no match |
115 | | * \retval 1 match |
116 | | */ |
117 | | static int DetectStreamSizeMatch( |
118 | | DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) |
119 | 20 | { |
120 | | |
121 | 20 | const DetectStreamSizeData *sd = (const DetectStreamSizeData *)ctx; |
122 | | |
123 | 20 | if (!(PKT_IS_TCP(p))) |
124 | 0 | return 0; |
125 | 20 | if (p->flow == NULL || p->flow->protoctx == NULL) |
126 | 0 | return 0; |
127 | | |
128 | 20 | const TcpSession *ssn = (TcpSession *)p->flow->protoctx; |
129 | | |
130 | 20 | SCReturnInt(DetectStreamSizeMatchAux(sd, ssn)); |
131 | 20 | } |
132 | | |
133 | | /** |
134 | | * \brief this function is used to add the parsed stream size data into the current signature |
135 | | * |
136 | | * \param de_ctx pointer to the Detection Engine Context |
137 | | * \param s pointer to the Current Signature |
138 | | * \param streamstr pointer to the user provided stream size options |
139 | | * |
140 | | * \retval 0 on Success |
141 | | * \retval -1 on Failure |
142 | | */ |
143 | | static int DetectStreamSizeSetup (DetectEngineCtx *de_ctx, Signature *s, const char *streamstr) |
144 | 13.3k | { |
145 | 13.3k | DetectStreamSizeData *sd = rs_detect_stream_size_parse(streamstr); |
146 | 13.3k | if (sd == NULL) |
147 | 5.70k | return -1; |
148 | | |
149 | 7.65k | SigMatch *sm = SigMatchAlloc(); |
150 | 7.65k | if (sm == NULL) { |
151 | 0 | DetectStreamSizeFree(de_ctx, sd); |
152 | 0 | return -1; |
153 | 0 | } |
154 | | |
155 | 7.65k | sm->type = DETECT_STREAM_SIZE; |
156 | 7.65k | sm->ctx = (SigMatchCtx *)sd; |
157 | | |
158 | 7.65k | SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH); |
159 | 7.65k | return 0; |
160 | 7.65k | } |
161 | | |
162 | | /** |
163 | | * \brief this function will free memory associated with DetectStreamSizeData |
164 | | * |
165 | | * \param ptr pointer to DetectStreamSizeData |
166 | | */ |
167 | | void DetectStreamSizeFree(DetectEngineCtx *de_ctx, void *ptr) |
168 | 7.65k | { |
169 | 7.65k | rs_detect_stream_size_free(ptr); |
170 | 7.65k | } |
171 | | |
172 | | /* prefilter code */ |
173 | | |
174 | | static void PrefilterPacketStreamsizeMatch( |
175 | | DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx) |
176 | 1.55k | { |
177 | 1.55k | if (!(PKT_IS_TCP(p)) || PKT_IS_PSEUDOPKT(p)) |
178 | 57 | return; |
179 | | |
180 | 1.49k | if (p->flow == NULL || p->flow->protoctx == NULL) |
181 | 42 | return; |
182 | | |
183 | | /* during setup Suricata will automatically see if there is another |
184 | | * check that can be added: alproto, sport or dport */ |
185 | 1.45k | const PrefilterPacketHeaderCtx *ctx = pectx; |
186 | 1.45k | if (!PrefilterPacketHeaderExtraMatch(ctx, p)) |
187 | 93 | return; |
188 | | |
189 | 1.36k | DetectStreamSizeData dsd; |
190 | 1.36k | dsd.du32.mode = ctx->v1.u8[0]; |
191 | 1.36k | dsd.flags = ctx->v1.u8[1]; |
192 | 1.36k | dsd.du32.arg1 = ctx->v1.u32[2]; |
193 | 1.36k | const TcpSession *ssn = (TcpSession *)p->flow->protoctx; |
194 | | /* if we match, add all the sigs that use this prefilter. This means |
195 | | * that these will be inspected further */ |
196 | 1.36k | if (DetectStreamSizeMatchAux(&dsd, ssn)) { |
197 | 1.36k | PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt); |
198 | 1.36k | } |
199 | 1.36k | } |
200 | | |
201 | | static void PrefilterPacketStreamSizeSet(PrefilterPacketHeaderValue *v, void *smctx) |
202 | 184 | { |
203 | 184 | const DetectStreamSizeData *a = smctx; |
204 | 184 | v->u8[0] = a->du32.mode; |
205 | 184 | v->u8[1] = a->flags; |
206 | 184 | v->u32[2] = a->du32.arg1; |
207 | 184 | } |
208 | | |
209 | | static bool PrefilterPacketStreamSizeCompare(PrefilterPacketHeaderValue v, void *smctx) |
210 | 33 | { |
211 | 33 | const DetectStreamSizeData *a = smctx; |
212 | 33 | if (v.u8[0] == a->du32.mode && v.u8[1] == a->flags && v.u32[2] == a->du32.arg1) |
213 | 33 | return true; |
214 | 0 | return false; |
215 | 33 | } |
216 | | |
217 | | static int PrefilterSetupStreamSize(DetectEngineCtx *de_ctx, SigGroupHead *sgh) |
218 | 226 | { |
219 | 226 | return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_STREAM_SIZE, PrefilterPacketStreamSizeSet, |
220 | 226 | PrefilterPacketStreamSizeCompare, PrefilterPacketStreamsizeMatch); |
221 | 226 | } |
222 | | |
223 | | static bool PrefilterStreamSizeIsPrefilterable(const Signature *s) |
224 | 0 | { |
225 | 0 | const SigMatch *sm; |
226 | 0 | for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL; sm = sm->next) { |
227 | 0 | switch (sm->type) { |
228 | 0 | case DETECT_STREAM_SIZE: |
229 | 0 | return true; |
230 | 0 | } |
231 | 0 | } |
232 | 0 | return false; |
233 | 0 | } |
234 | | |
235 | | #ifdef UNITTESTS |
236 | | /** |
237 | | * \test DetectStreamSizeParseTest01 is a test to make sure that we parse the |
238 | | * user options correctly, when given valid stream_size options. |
239 | | */ |
240 | | |
241 | | static int DetectStreamSizeParseTest01 (void) |
242 | | { |
243 | | int result = 0; |
244 | | DetectStreamSizeData *sd = NULL; |
245 | | sd = rs_detect_stream_size_parse("server,<,6"); |
246 | | if (sd != NULL) { |
247 | | if (sd->flags & StreamSizeServer && sd->du32.mode == DETECT_UINT_LT && sd->du32.arg1 == 6) |
248 | | result = 1; |
249 | | DetectStreamSizeFree(NULL, sd); |
250 | | } |
251 | | |
252 | | return result; |
253 | | } |
254 | | |
255 | | /** |
256 | | * \test DetectStreamSizeParseTest02 is a test to make sure that we detect the |
257 | | * invalid stream_size options. |
258 | | */ |
259 | | |
260 | | static int DetectStreamSizeParseTest02 (void) |
261 | | { |
262 | | int result = 1; |
263 | | DetectStreamSizeData *sd = NULL; |
264 | | sd = rs_detect_stream_size_parse("invalidoption,<,6"); |
265 | | if (sd != NULL) { |
266 | | printf("expected: NULL got 0x%02X %" PRIu32 ": ", sd->flags, sd->du32.arg1); |
267 | | result = 0; |
268 | | DetectStreamSizeFree(NULL, sd); |
269 | | } |
270 | | |
271 | | return result; |
272 | | } |
273 | | |
274 | | /** |
275 | | * \test DetectStreamSizeParseTest03 is a test to make sure that we match the |
276 | | * packet correctly provided valid stream size. |
277 | | */ |
278 | | |
279 | | static int DetectStreamSizeParseTest03 (void) |
280 | | { |
281 | | |
282 | | int result = 0; |
283 | | DetectStreamSizeData *sd = NULL; |
284 | | TcpSession ssn; |
285 | | ThreadVars tv; |
286 | | DetectEngineThreadCtx dtx; |
287 | | Packet *p = PacketGetFromAlloc(); |
288 | | if (unlikely(p == NULL)) |
289 | | return 0; |
290 | | Signature s; |
291 | | SigMatch sm; |
292 | | TcpStream client; |
293 | | Flow f; |
294 | | TCPHdr tcph; |
295 | | |
296 | | memset(&ssn, 0, sizeof(TcpSession)); |
297 | | memset(&tv, 0, sizeof(ThreadVars)); |
298 | | memset(&dtx, 0, sizeof(DetectEngineThreadCtx)); |
299 | | memset(&s, 0, sizeof(Signature)); |
300 | | memset(&sm, 0, sizeof(SigMatch)); |
301 | | memset(&client, 0, sizeof(TcpStream)); |
302 | | memset(&f, 0, sizeof(Flow)); |
303 | | memset(&tcph, 0, sizeof(TCPHdr)); |
304 | | |
305 | | sd = rs_detect_stream_size_parse("client,>,8"); |
306 | | if (sd != NULL) { |
307 | | if (!(sd->flags & StreamSizeClient)) { |
308 | | printf("sd->flags not STREAM_SIZE_CLIENT: "); |
309 | | DetectStreamSizeFree(NULL, sd); |
310 | | SCFree(p); |
311 | | return 0; |
312 | | } |
313 | | |
314 | | if (sd->du32.mode != DETECT_UINT_GT) { |
315 | | printf("sd->mode not DETECTSSIZE_GT: "); |
316 | | DetectStreamSizeFree(NULL, sd); |
317 | | SCFree(p); |
318 | | return 0; |
319 | | } |
320 | | |
321 | | if (sd->du32.arg1 != 8) { |
322 | | printf("sd->ssize is %" PRIu32 ", not 8: ", sd->du32.arg1); |
323 | | DetectStreamSizeFree(NULL, sd); |
324 | | SCFree(p); |
325 | | return 0; |
326 | | } |
327 | | } else { |
328 | | printf("sd == NULL: "); |
329 | | SCFree(p); |
330 | | return 0; |
331 | | } |
332 | | |
333 | | client.next_seq = 20; |
334 | | client.isn = 10; |
335 | | ssn.client = client; |
336 | | f.protoctx = &ssn; |
337 | | p->flow = &f; |
338 | | p->tcph = &tcph; |
339 | | sm.ctx = (SigMatchCtx*)sd; |
340 | | |
341 | | result = DetectStreamSizeMatch(&dtx, p, &s, sm.ctx); |
342 | | if (result == 0) { |
343 | | printf("result 0 != 1: "); |
344 | | } |
345 | | DetectStreamSizeFree(NULL, sd); |
346 | | SCFree(p); |
347 | | return result; |
348 | | } |
349 | | |
350 | | /** |
351 | | * \test DetectStreamSizeParseTest04 is a test to make sure that we match the |
352 | | * stream_size against invalid packet parameters. |
353 | | */ |
354 | | |
355 | | static int DetectStreamSizeParseTest04 (void) |
356 | | { |
357 | | |
358 | | int result = 0; |
359 | | DetectStreamSizeData *sd = NULL; |
360 | | TcpSession ssn; |
361 | | ThreadVars tv; |
362 | | DetectEngineThreadCtx dtx; |
363 | | Packet *p = PacketGetFromAlloc(); |
364 | | if (unlikely(p == NULL)) |
365 | | return 0; |
366 | | Signature s; |
367 | | SigMatch sm; |
368 | | TcpStream client; |
369 | | Flow f; |
370 | | IPV4Hdr ip4h; |
371 | | |
372 | | memset(&ssn, 0, sizeof(TcpSession)); |
373 | | memset(&tv, 0, sizeof(ThreadVars)); |
374 | | memset(&dtx, 0, sizeof(DetectEngineThreadCtx)); |
375 | | memset(&s, 0, sizeof(Signature)); |
376 | | memset(&sm, 0, sizeof(SigMatch)); |
377 | | memset(&client, 0, sizeof(TcpStream)); |
378 | | memset(&f, 0, sizeof(Flow)); |
379 | | memset(&ip4h, 0, sizeof(IPV4Hdr)); |
380 | | |
381 | | sd = rs_detect_stream_size_parse(" client , > , 8 "); |
382 | | if (sd != NULL) { |
383 | | if (!(sd->flags & StreamSizeClient) && sd->du32.mode != DETECT_UINT_GT && |
384 | | sd->du32.arg1 != 8) { |
385 | | SCFree(p); |
386 | | return 0; |
387 | | } |
388 | | } else |
389 | | { |
390 | | SCFree(p); |
391 | | return 0; |
392 | | } |
393 | | |
394 | | client.next_seq = 20; |
395 | | client.isn = 12; |
396 | | ssn.client = client; |
397 | | f.protoctx = &ssn; |
398 | | p->flow = &f; |
399 | | p->ip4h = &ip4h; |
400 | | sm.ctx = (SigMatchCtx*)sd; |
401 | | |
402 | | if (!DetectStreamSizeMatch(&dtx, p, &s, sm.ctx)) |
403 | | result = 1; |
404 | | |
405 | | SCFree(p); |
406 | | return result; |
407 | | } |
408 | | |
409 | | /** |
410 | | * \brief this function registers unit tests for DetectStreamSize |
411 | | */ |
412 | | void DetectStreamSizeRegisterTests(void) |
413 | | { |
414 | | UtRegisterTest("DetectStreamSizeParseTest01", DetectStreamSizeParseTest01); |
415 | | UtRegisterTest("DetectStreamSizeParseTest02", DetectStreamSizeParseTest02); |
416 | | UtRegisterTest("DetectStreamSizeParseTest03", DetectStreamSizeParseTest03); |
417 | | UtRegisterTest("DetectStreamSizeParseTest04", DetectStreamSizeParseTest04); |
418 | | } |
419 | | #endif /* UNITTESTS */ |