suricata
detect-engine-content-inspection.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 Anoop Saldanha <anoopsaldanha@gmail.com>
22 *
23 * Performs content inspection on any buffer supplied.
24 */
25
26#include "suricata-common.h"
27#include "suricata.h"
28#include "decode.h"
29
30#include "detect.h"
31#include "detect-engine.h"
32#include "detect-parse.h"
33
34#include "rust.h"
35
36#include "detect-asn1.h"
37#include "detect-content.h"
38#include "detect-pcre.h"
39#include "detect-isdataat.h"
40#include "detect-bytetest.h"
41#include "detect-bytemath.h"
42#include "detect-bytejump.h"
43#include "detect-byte-extract.h"
44#include "detect-entropy.h"
45#include "detect-replace.h"
47#include "detect-uricontent.h"
48#include "detect-urilen.h"
49#include "detect-engine-uint.h"
50#include "detect-bsize.h"
51#include "detect-lua.h"
53#include "detect-base64-data.h"
54#include "detect-dataset.h"
55#include "detect-datarep.h"
56
57#include "util-spm.h"
58#include "util-debug.h"
59#include "util-print.h"
60#include "util-validate.h"
61
62#include "util-unittest.h"
64#include "util-profiling.h"
65
66#include "util-lua.h"
67
68#ifdef UNITTESTS
69thread_local uint32_t ut_inspection_recursion_counter = 0;
70#endif
71
73 struct {
74 uint32_t count;
75 const uint32_t limit;
77};
78
79/**
80 * \brief Run the actual payload match functions
81 *
82 * All keywords are evaluated against the buffer with buffer_len.
83 *
84 * For accounting the last match in relative matching the
85 * det_ctx->buffer_offset int is used.
86 *
87 * \param det_ctx Detection engine thread context
88 * \param s Signature to inspect
89 * \param sm SigMatch to inspect
90 * \param p Packet. Can be NULL.
91 * \param f Flow (for pcre flowvar storage)
92 * \param buffer Ptr to the buffer to inspect
93 * \param buffer_len Length of the payload
94 * \param stream_start_offset Indicates the start of the current buffer in
95 * the whole buffer stream inspected. This
96 * applies if the current buffer is inspected
97 * in chunks.
98 * \param inspection_mode Refers to the engine inspection mode we are currently
99 * inspecting. Can be payload, stream, one of the http
100 * buffer inspection modes or dce inspection mode.
101 * \param flags DETECT_CI_FLAG_*
102 *
103 * \retval -1 no match and give up (discontinue matching)
104 * \retval 0 no match
105 * \retval 1 match
106 */
107static int DetectEngineContentInspectionInternal(DetectEngineThreadCtx *det_ctx,
108 struct DetectEngineContentInspectionCtx *ctx, const Signature *s, const SigMatchData *smd,
109 Packet *p, Flow *f, const uint8_t *buffer, const uint32_t buffer_len,
110 const uint64_t stream_start_offset, const uint8_t flags,
111 const enum DetectContentInspectionType inspection_mode)
112{
113 SCEnter();
115
116 ctx->recursion.count++;
117 if (unlikely(ctx->recursion.count == ctx->recursion.limit)) {
118 KEYWORD_PROFILING_END(det_ctx, smd->type, 0);
119 SCReturnInt(-1);
120 }
121
122 // we want the ability to match on bsize: 0
123 if (smd == NULL || buffer == NULL) {
124 KEYWORD_PROFILING_END(det_ctx, smd->type, 0);
125 SCReturnInt(0);
126 }
127
128 if (smd->type == DETECT_CONTENT) {
129 const DetectContentData *cd = (const DetectContentData *)smd->ctx;
130 SCLogDebug("inspecting content %"PRIu32" buffer_len %"PRIu32, cd->id, buffer_len);
131
132 /* we might have already have this content matched by the mpm.
133 * (if there is any other reason why we'd want to avoid checking
134 * it here, please fill it in) */
135 //if (cd->flags & DETECT_CONTENT_NO_DOUBLE_INSPECTION_REQUIRED) {
136 // goto match;
137 //}
138
139 /* rule parsers should take care of this */
140#ifdef DEBUG
141 BUG_ON(cd->depth != 0 && cd->depth <= cd->offset);
142#endif
143
144 /* search for our pattern, checking the matches recursively.
145 * if we match we look for the next SigMatch as well */
146 uint32_t prev_offset = 0; /**< used in recursive searching */
147 uint32_t prev_buffer_offset = det_ctx->buffer_offset;
148
149 do {
150 uint32_t depth = buffer_len;
151 uint32_t offset = 0;
152 if ((cd->flags & DETECT_CONTENT_DISTANCE) ||
153 (cd->flags & DETECT_CONTENT_WITHIN)) {
154 SCLogDebug("det_ctx->buffer_offset %" PRIu32, det_ctx->buffer_offset);
155 offset = prev_buffer_offset;
156
157 int distance = cd->distance;
158 if (cd->flags & DETECT_CONTENT_DISTANCE) {
159 if (cd->flags & DETECT_CONTENT_DISTANCE_VAR) {
160 // This cast is wrong if a 64-bit value was extracted
161 distance = (uint32_t)det_ctx->byte_values[cd->distance];
162 }
163 if (distance < 0 && (uint32_t)(abs(distance)) > offset)
164 offset = 0;
165 else
166 offset += distance;
167
168 SCLogDebug("cd->distance %"PRIi32", offset %"PRIu32", depth %"PRIu32,
169 distance, offset, depth);
170 }
171
172 if (cd->flags & DETECT_CONTENT_WITHIN) {
173 if (cd->flags & DETECT_CONTENT_WITHIN_VAR) {
174 // This cast is wrong if a 64-bit value was extracted for within
175 if ((int32_t)depth > (int32_t)(prev_buffer_offset + det_ctx->byte_values[cd->within] + distance)) {
176 depth = prev_buffer_offset +
177 (uint32_t)det_ctx->byte_values[cd->within] + distance;
178 }
179 } else {
180 if ((int32_t)depth > (int32_t)(prev_buffer_offset + cd->within + distance)) {
181 depth = prev_buffer_offset + cd->within + distance;
182 }
183
184 SCLogDebug("cd->within %"PRIi32", det_ctx->buffer_offset %"PRIu32", depth %"PRIu32,
185 cd->within, prev_buffer_offset, depth);
186 }
187
188 if (stream_start_offset != 0 && prev_buffer_offset == 0) {
189 if (depth <= stream_start_offset) {
190 goto no_match;
191 } else if (depth >= (stream_start_offset + buffer_len)) {
192 ;
193 } else {
194 depth = depth - (uint32_t)stream_start_offset;
195 }
196 }
197 }
198
199 if (cd->flags & DETECT_CONTENT_DEPTH_VAR) {
200 if ((det_ctx->byte_values[cd->depth] + prev_buffer_offset) < depth) {
201 // ok to cast as we checked the byte value fits in a u32
202 depth = prev_buffer_offset + (uint32_t)det_ctx->byte_values[cd->depth];
203 }
204 } else {
205 if (cd->depth != 0) {
206 if ((cd->depth + prev_buffer_offset) < depth) {
207 depth = prev_buffer_offset + cd->depth;
208 }
209
210 SCLogDebug("cd->depth %"PRIu32", depth %"PRIu32, cd->depth, depth);
211 }
212 }
213
214 if (cd->flags & DETECT_CONTENT_OFFSET_VAR) {
215 if (det_ctx->byte_values[cd->offset] > offset) {
216 // This cast is wrong if a 64-bit value was extracted
217 offset = (uint32_t)det_ctx->byte_values[cd->offset];
218 }
219 } else {
220 if (cd->offset > offset) {
221 offset = cd->offset;
222 SCLogDebug("setting offset %"PRIu32, offset);
223 }
224 }
225 } else { /* implied no relative matches */
226 /* set depth */
227 if (cd->flags & DETECT_CONTENT_DEPTH_VAR) {
228 // This cast is wrong if a 64-bit value was extracted
229 depth = (uint32_t)det_ctx->byte_values[cd->depth];
230 } else {
231 if (cd->depth != 0) {
232 depth = cd->depth;
233 }
234 }
235
236 if (stream_start_offset != 0 && cd->flags & DETECT_CONTENT_DEPTH) {
237 if (depth <= stream_start_offset) {
238 goto no_match;
239 } else if (depth >= (stream_start_offset + buffer_len)) {
240 ;
241 } else {
242 depth = (uint32_t)(depth - stream_start_offset);
243 }
244 }
245
246 /* set offset */
247 if (cd->flags & DETECT_CONTENT_OFFSET_VAR) {
248 // This cast is wrong if a 64-bit value was extracted
249 offset = (uint32_t)det_ctx->byte_values[cd->offset];
250 } else {
251 offset = cd->offset;
252 }
253 prev_buffer_offset = 0;
254 }
255
256 /* If the value came from a variable, make sure to adjust the depth so it's relative
257 * to the offset value.
258 */
260 depth += offset;
261 }
262
263 /* update offset with prev_offset if we're searching for
264 * matches after the first occurrence. */
265 SCLogDebug("offset %"PRIu32", prev_offset %"PRIu32, offset, prev_offset);
266 if (prev_offset != 0)
267 offset = prev_offset;
268
269 SCLogDebug("offset %"PRIu32", depth %"PRIu32, offset, depth);
270
271 if (depth > buffer_len)
272 depth = buffer_len;
273
274 /* if offset is bigger than depth we can never match on a pattern.
275 * We can however, "match" on a negated pattern. */
276 if (offset > depth || depth == 0) {
277 if (cd->flags & DETECT_CONTENT_NEGATED) {
278 goto match;
279 } else {
280 goto no_match;
281 }
282 }
283
284 const uint8_t *sbuffer = buffer + offset;
285 uint32_t sbuffer_len = depth - offset;
286 SCLogDebug("sbuffer_len %" PRIu32 " depth: %" PRIu32 ", buffer_len: %" PRIu32,
287 sbuffer_len, depth, buffer_len);
288#ifdef DEBUG
289 BUG_ON(sbuffer_len > buffer_len);
290#endif
291 const uint8_t *found;
292 if (cd->flags & DETECT_CONTENT_ENDS_WITH && depth < buffer_len) {
293 SCLogDebug("depth < buffer_len while DETECT_CONTENT_ENDS_WITH is set. Can't possibly match.");
294 found = NULL;
295 } else if (cd->content_len > sbuffer_len) {
296 found = NULL;
297 } else {
298 /* do the actual search */
299 found = SpmScan(cd->spm_ctx, det_ctx->spm_thread_ctx, sbuffer,
300 sbuffer_len);
301 }
302
303 /* next we evaluate the result in combination with the
304 * negation flag. */
305 SCLogDebug("found %p cd negated %s", found, cd->flags & DETECT_CONTENT_NEGATED ? "true" : "false");
306
307 if (found == NULL) {
308 if (!(cd->flags & DETECT_CONTENT_NEGATED)) {
309 if ((cd->flags & (DETECT_CONTENT_DISTANCE | DETECT_CONTENT_WITHIN)) == 0) {
310 /* independent match from previous matches, so failure is fatal */
311 goto no_match_discontinue;
312 }
313
314 goto no_match;
315 } else {
316 goto match;
317 }
318 }
319
320 uint32_t match_offset = (uint32_t)((found - buffer) + cd->content_len);
321 if (cd->flags & DETECT_CONTENT_NEGATED) {
322 SCLogDebug("content %" PRIu32 " matched at offset %" PRIu32
323 ", but negated so no match",
324 cd->id, match_offset);
325 /* don't bother carrying recursive matches now, for preceding
326 * relative keywords */
327
328 /* found a match but not at the end of the buffer */
329 if (cd->flags & DETECT_CONTENT_ENDS_WITH) {
330 if (sbuffer_len != match_offset) {
331 SCLogDebug("content \"%s\" %" PRIu32 " matched at offset %" PRIu32
332 ", but not at end of buffer so match",
333 cd->content, cd->id, match_offset);
334 goto match;
335 }
336 }
337 if (DETECT_CONTENT_IS_SINGLE(cd)) {
338 goto no_match_discontinue;
339 }
340 goto no_match;
341 }
342
343 SCLogDebug("content %" PRIu32 " matched at offset %" PRIu32 "", cd->id, match_offset);
344 det_ctx->buffer_offset = match_offset;
345
346 if ((cd->flags & DETECT_CONTENT_ENDS_WITH) == 0 || match_offset == buffer_len) {
347 /* Match branch, add replace to the list if needed */
348 if (unlikely(cd->flags & DETECT_CONTENT_REPLACE)) {
349 if (inspection_mode == DETECT_ENGINE_CONTENT_INSPECTION_MODE_PAYLOAD) {
350 /* we will need to replace content if match is confirmed
351 * cast to non-const as replace writes to it. */
352 det_ctx->replist =
353 DetectReplaceAddToList(det_ctx->replist, (uint8_t *)found, cd);
354 } else {
355 SCLogWarning("Can't modify payload without packet");
356 }
357 }
358
359 /* if this is the last match we're done */
360 if (smd->is_last) {
361 goto match;
362 }
363
364 SCLogDebug("content %" PRIu32, cd->id);
365 KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
366
367 /* see if the next buffer keywords match. If not, we will
368 * search for another occurrence of this content and see
369 * if the others match then until we run out of matches */
370 int r = DetectEngineContentInspectionInternal(det_ctx, ctx, s, smd + 1, p, f,
371 buffer, buffer_len, stream_start_offset, flags, inspection_mode);
372 if (r == 1) {
373 SCReturnInt(1);
374 } else if (r == -1) {
375 SCLogDebug("'next sm' said to discontinue this right now");
376 SCReturnInt(-1);
377 }
378 SCLogDebug("no match for 'next sm'");
379
380 /* no match and no reason to look for another instance */
381 if ((cd->flags & DETECT_CONTENT_WITHIN_NEXT) == 0) {
382 SCLogDebug("'next sm' does not depend on me, so we can give up");
383 SCReturnInt(-1);
384 }
385
386 SCLogDebug("'next sm' depends on me %p, lets see what we can do (flags %u)", cd,
387 cd->flags);
388 }
389 /* set the previous match offset to the start of this match + 1 */
390 prev_offset = (match_offset - (cd->content_len - 1));
391 SCLogDebug("trying to see if there is another match after prev_offset %" PRIu32,
392 prev_offset);
393 } while(1);
394
395 } else if (smd->type == DETECT_ABSENT) {
396 const DetectAbsentData *id = (DetectAbsentData *)smd->ctx;
397 if (!id->or_else) {
398 // we match only on absent buffer
399 goto no_match;
400 }
401 goto match;
402 } else if (smd->type == DETECT_ISDATAAT) {
403 SCLogDebug("inspecting isdataat");
404
405 const DetectIsdataatData *id = (DetectIsdataatData *)smd->ctx;
406 uint32_t dataat = id->dataat;
407 if (id->flags & ISDATAAT_OFFSET_VAR) {
408 uint64_t be_value = det_ctx->byte_values[dataat];
409 if (be_value >= 100000000) {
410 if ((id->flags & ISDATAAT_NEGATED) == 0) {
411 SCLogDebug("extracted value %"PRIu64" very big: no match", be_value);
412 goto no_match;
413 }
414 SCLogDebug("extracted value way %"PRIu64" very big: match", be_value);
415 goto match;
416 }
417 dataat = (uint32_t)be_value;
418 SCLogDebug("isdataat: using value %u from byte_extract local_id %u", dataat, id->dataat);
419 }
420
421 if (id->flags & ISDATAAT_RELATIVE) {
422 if (det_ctx->buffer_offset + dataat > buffer_len) {
423 SCLogDebug("det_ctx->buffer_offset + dataat %"PRIu32" > %"PRIu32, det_ctx->buffer_offset + dataat, buffer_len);
424 if (id->flags & ISDATAAT_NEGATED)
425 goto match;
426 if ((id->flags & ISDATAAT_OFFSET_VAR) == 0) {
427 goto no_match_discontinue;
428 }
429 goto no_match;
430 } else {
431 SCLogDebug("relative isdataat match");
432 if (id->flags & ISDATAAT_NEGATED) {
433 goto no_match;
434 }
435 goto match;
436 }
437 } else {
438 if (dataat < buffer_len) {
439 SCLogDebug("absolute isdataat match");
440 if (id->flags & ISDATAAT_NEGATED) {
441 if ((id->flags & ISDATAAT_OFFSET_VAR) == 0) {
442 goto no_match_discontinue;
443 }
444 goto no_match;
445 }
446 goto match;
447 } else {
448 SCLogDebug("absolute isdataat mismatch, id->isdataat %"PRIu32", buffer_len %"PRIu32"", dataat, buffer_len);
449 if (id->flags & ISDATAAT_NEGATED)
450 goto match;
451 if ((id->flags & ISDATAAT_OFFSET_VAR) == 0) {
452 goto no_match_discontinue;
453 }
454 goto no_match;
455 }
456 }
457
458 } else if (smd->type == DETECT_PCRE) {
459 SCLogDebug("inspecting pcre");
460 const DetectPcreData *pe = (const DetectPcreData *)smd->ctx;
461 uint32_t prev_buffer_offset = det_ctx->buffer_offset;
462 uint32_t prev_offset = 0;
463
464 det_ctx->pcre_match_start_offset = 0;
465 do {
466 int r = DetectPcrePayloadMatch(det_ctx, s, smd, p, f, buffer, buffer_len);
467 if (r == 0) {
468 goto no_match;
469 }
470 if (!(pe->flags & DETECT_PCRE_RELATIVE_NEXT)) {
471 SCLogDebug("no relative match coming up, so this is a match");
472 goto match;
473 }
474 KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
475
476 /* save it, in case we need to do a pcre match once again */
477 prev_offset = det_ctx->pcre_match_start_offset;
478
479 /* see if the next payload keywords match. If not, we will
480 * search for another occurrence of this pcre and see
481 * if the others match, until we run out of matches */
482 r = DetectEngineContentInspectionInternal(det_ctx, ctx, s, smd + 1, p, f, buffer,
483 buffer_len, stream_start_offset, flags, inspection_mode);
484 if (r == 1) {
485 SCReturnInt(1);
486 } else if (r == -1) {
487 SCReturnInt(-1);
488 }
489
490 if (prev_offset == 0) {
491 // This happens for negated PCRE
492 // We do not search for another occurrence of this pcre
493 SCReturnInt(0);
494 }
495 det_ctx->buffer_offset = prev_buffer_offset;
496 det_ctx->pcre_match_start_offset = prev_offset;
497 } while (1);
498
499 } else if (smd->type == DETECT_ENTROPY) {
500 if (!DetectEntropyDoMatch(det_ctx, s, smd->ctx, buffer, buffer_len)) {
501 goto no_match;
502 }
503 goto match;
504 } else if (smd->type == DETECT_BYTETEST) {
505 const DetectBytetestData *btd = (const DetectBytetestData *)smd->ctx;
506 uint16_t btflags = btd->flags;
507 int32_t offset = btd->offset;
508 uint64_t value = btd->value;
509 int32_t nbytes = btd->nbytes;
510 if (btflags & DETECT_BYTETEST_OFFSET_VAR) {
511 // This cast is wrong if a 64-bit value was extracted
512 offset = (int32_t)det_ctx->byte_values[offset];
513 }
514 if (btflags & DETECT_BYTETEST_VALUE_VAR) {
515 value = det_ctx->byte_values[value];
516 }
517 if (btflags & DETECT_BYTETEST_NBYTES_VAR) {
518 // This cast is wrong if a 64-bit value was extracted
519 nbytes = (int32_t)det_ctx->byte_values[nbytes];
520 }
521
522 /* if we have dce enabled we will have to use the endianness
523 * specified by the dce header */
524 if (btflags & DETECT_BYTETEST_DCE) {
525 /* enable the endianness flag temporarily. once we are done
526 * processing we reset the flags to the original value*/
527 btflags |= ((flags & DETECT_CI_FLAGS_DCE_LE) ?
529 }
530
531 if (DetectBytetestDoMatch(det_ctx, s, smd->ctx, buffer, buffer_len, btflags, offset, nbytes,
532 value) != 1) {
533 goto no_match;
534 }
535
536 goto match;
537
538 } else if (smd->type == DETECT_BYTEJUMP) {
539 const DetectBytejumpData *bjd = (const DetectBytejumpData *)smd->ctx;
540 uint16_t bjflags = bjd->flags;
541 int32_t offset = bjd->offset;
542 int32_t nbytes;
543
544 if (bjflags & DETECT_BYTEJUMP_OFFSET_VAR) {
545 // This cast is wrong if a 64-bit value was extracted
546 offset = (int32_t)det_ctx->byte_values[offset];
547 SCLogDebug("[BJ] using offset value %d", offset);
548 }
549
550 if (bjflags & DETECT_BYTEJUMP_NBYTES_VAR) {
551 // This cast is wrong if a 64-bit value was extracted
552 nbytes = (int32_t)det_ctx->byte_values[bjd->nbytes];
553 SCLogDebug("[BJ] using nbytes value %d [index %d]", nbytes, bjd->nbytes);
554 } else {
555 nbytes = bjd->nbytes;
556 SCLogDebug("[BJ] using nbytes value %d [index n/a]", nbytes);
557 }
558
559 /* if we have dce enabled we will have to use the endianness
560 * specified by the dce header */
561 if (bjflags & DETECT_BYTEJUMP_DCE) {
562 /* enable the endianness flag temporarily. once we are done
563 * processing we reset the flags to the original value*/
564 bjflags |= ((flags & DETECT_CI_FLAGS_DCE_LE) ?
566 }
567
569 det_ctx, s, smd->ctx, buffer, buffer_len, bjflags, nbytes, offset)) {
570 goto no_match;
571 }
572
573 goto match;
574
575 } else if (smd->type == DETECT_BYTE_EXTRACT) {
576
577 const SCDetectByteExtractData *bed = (const SCDetectByteExtractData *)smd->ctx;
578 uint8_t endian = bed->endian;
579
580 /* if we have dce enabled we will have to use the endianness
581 * specified by the dce header */
582 if ((bed->flags & DETECT_BYTE_EXTRACT_FLAG_ENDIAN) && endian == EndianDCE &&
584
585 /* enable the endianness flag temporarily. once we are done
586 * processing we reset the flags to the original value*/
587 endian |= ((flags & DETECT_CI_FLAGS_DCE_LE) ? LittleEndian : BigEndian);
588 }
589
590 if (DetectByteExtractDoMatch(det_ctx, smd, s, buffer, buffer_len,
591 &det_ctx->byte_values[bed->local_id], endian) != 1) {
592 goto no_match;
593 }
594
595 SCLogDebug("[BE] Fetched value for index %d: %"PRIu64,
596 bed->local_id, det_ctx->byte_values[bed->local_id]);
597 goto match;
598
599 } else if (smd->type == DETECT_BYTEMATH) {
600
601 const DetectByteMathData *bmd = (const DetectByteMathData *)smd->ctx;
602 uint8_t endian = bmd->endian;
603
604 /* if we have dce enabled we will have to use the endianness
605 * specified by the dce header */
606 if ((bmd->flags & DETECT_BYTEMATH_FLAG_ENDIAN) && endian == (int)EndianDCE &&
608 /* enable the endianness flag temporarily. once we are done
609 * processing we reset the flags to the original value*/
610 endian = (uint8_t)((flags & DETECT_CI_FLAGS_DCE_LE) ? LittleEndian : BigEndian);
611 }
612 uint64_t rvalue;
613 if (bmd->flags & DETECT_BYTEMATH_FLAG_RVALUE_VAR) {
614 rvalue = det_ctx->byte_values[bmd->rvalue];
615 } else {
616 rvalue = bmd->rvalue;
617 }
618
619 uint8_t nbytes;
620 if (bmd->flags & DETECT_BYTEMATH_FLAG_NBYTES_VAR) {
621 nbytes = (uint8_t)det_ctx->byte_values[bmd->nbytes];
622 } else {
623 nbytes = bmd->nbytes;
624 }
625
626 if (DetectByteMathDoMatch(det_ctx, bmd, s, buffer, buffer_len, nbytes, rvalue,
627 &det_ctx->byte_values[bmd->local_id], endian) != 1) {
628 goto no_match;
629 }
630
631 SCLogDebug("[BM] Fetched value for index %d: %"PRIu64,
632 bmd->local_id, det_ctx->byte_values[bmd->local_id]);
633 goto match;
634
635 } else if (smd->type == DETECT_BSIZE) {
636
637 const bool eof = (flags & DETECT_CI_FLAGS_END);
638 const uint64_t data_size = buffer_len + stream_start_offset;
639 int r = DetectBsizeMatch(smd->ctx, data_size, eof);
640 if (r < 0) {
641 goto no_match_discontinue;
642 } else if (r == 0) {
643 goto no_match;
644 }
645 goto match;
646
647 } else if (smd->type == DETECT_DATASET) {
648
649 //PrintRawDataFp(stdout, buffer, buffer_len);
650 const DetectDatasetData *sd = (const DetectDatasetData *) smd->ctx;
651 int r = DetectDatasetBufferMatch(det_ctx, sd, buffer, buffer_len); //TODO buffer offset?
652 if (r == 1) {
653 goto match;
654 }
655 goto no_match_discontinue;
656
657 } else if (smd->type == DETECT_DATAREP) {
658
659 //PrintRawDataFp(stdout, buffer, buffer_len);
660 const DetectDatarepData *sd = (const DetectDatarepData *) smd->ctx;
661 int r = DetectDatarepBufferMatch(det_ctx, sd, buffer, buffer_len); //TODO buffer offset?
662 if (r == 1) {
663 goto match;
664 }
665 goto no_match_discontinue;
666
667 } else if (smd->type == DETECT_URILEN) {
668 SCLogDebug("inspecting uri len");
669
670 int r;
671 const DetectUrilenData *urilend = (const DetectUrilenData *)smd->ctx;
672 if (buffer_len > UINT16_MAX) {
673 r = DetectU16Match(UINT16_MAX, &urilend->du16);
674 } else {
675 r = DetectU16Match((uint16_t)buffer_len, &urilend->du16);
676 }
677
678 if (r == 1) {
679 goto match;
680 }
681 goto no_match_discontinue;
682 } else if (smd->type == DETECT_LUA) {
683 SCLogDebug("lua starting");
684
685 if (DetectLuaMatchBuffer(det_ctx, s, smd, buffer, buffer_len,
686 det_ctx->buffer_offset, f) != 1)
687 {
688 SCLogDebug("lua no_match");
689 goto no_match;
690 }
691 SCLogDebug("lua match");
692 goto match;
693 } else if (smd->type == DETECT_BASE64_DECODE) {
694 if (DetectBase64DecodeDoMatch(det_ctx, s, smd, buffer, buffer_len)) {
695 if (s->sm_arrays[DETECT_SM_LIST_BASE64_DATA] != NULL) {
696 if (det_ctx->base64_decoded_len) {
697 KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
698 int r = DetectEngineContentInspectionInternal(det_ctx, ctx, s,
700 det_ctx->base64_decoded, det_ctx->base64_decoded_len, 0,
702 if (r == 1) {
703 /* Base64 is a terminal list. */
704 goto final_match;
705 }
706 }
707 }
708 }
709 } else if (smd->type == DETECT_ASN1) {
710 if (!DetectAsn1Match(smd, buffer, buffer_len, det_ctx->buffer_offset)) {
711 SCLogDebug("asn1 no_match");
712 goto no_match;
713 }
714 SCLogDebug("asn1 match");
715 goto match;
716 } else {
717 SCLogDebug("sm->type %u", smd->type);
718#ifdef DEBUG
719 BUG_ON(1);
720#endif
721 }
722
723no_match:
724 KEYWORD_PROFILING_END(det_ctx, smd->type, 0);
725 SCReturnInt(0);
726
727no_match_discontinue:
728 KEYWORD_PROFILING_END(det_ctx, smd->type, 0);
729 SCReturnInt(-1);
730
731match:
732 /* this sigmatch matched, inspect the next one. If it was the last,
733 * the buffer portion of the signature matched. */
734 if (!smd->is_last) {
735 KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
736 int r = DetectEngineContentInspectionInternal(det_ctx, ctx, s, smd + 1, p, f, buffer,
737 buffer_len, stream_start_offset, flags, inspection_mode);
738 SCReturnInt(r);
739 }
740final_match:
741 KEYWORD_PROFILING_END(det_ctx, smd->type, 1);
742 SCReturnInt(1);
743}
744
745/** \brief wrapper around DetectEngineContentInspectionInternal to return true/false only
746 *
747 * \param smd sigmatches to evaluate
748 */
750 const Signature *s, const SigMatchData *smd, Packet *p, Flow *f, const uint8_t *buffer,
751 const uint32_t buffer_len, const uint64_t stream_start_offset, const uint8_t flags,
752 const enum DetectContentInspectionType inspection_mode)
753{
754 struct DetectEngineContentInspectionCtx ctx = { .recursion.count = 0,
755 .recursion.limit = de_ctx->inspection_recursion_limit };
756 det_ctx->buffer_offset = 0;
757
758 int r = DetectEngineContentInspectionInternal(det_ctx, &ctx, s, smd, p, f, buffer, buffer_len,
759 stream_start_offset, flags, inspection_mode);
760#ifdef UNITTESTS
761 ut_inspection_recursion_counter = ctx.recursion.count;
762#endif
763 if (r == 1)
764 return true;
765 else
766 return false;
767}
768
769/** \brief wrapper around DetectEngineContentInspectionInternal to return true/false only
770 *
771 * \param smd sigmatches to evaluate
772 */
774 const Signature *s, const SigMatchData *smd, Packet *p, Flow *f, const InspectionBuffer *b,
775 const enum DetectContentInspectionType inspection_mode)
776{
777 struct DetectEngineContentInspectionCtx ctx = { .recursion.count = 0,
778 .recursion.limit = de_ctx->inspection_recursion_limit };
779
780 det_ctx->buffer_offset = 0;
781
782 int r = DetectEngineContentInspectionInternal(det_ctx, &ctx, s, smd, p, f, b->inspect,
783 b->inspect_len, b->inspect_offset, b->flags, inspection_mode);
784#ifdef UNITTESTS
785 ut_inspection_recursion_counter = ctx.recursion.count;
786#endif
787 if (r == 1)
788 return true;
789 else
790 return false;
791}
792
794{
795 // we will match on NULL buffers there is one absent
796 bool absent_data = false;
797 while (1) {
798 if (smd->type == DETECT_ABSENT) {
799 absent_data = true;
800 break;
801 }
802 if (smd->is_last) {
803 break;
804 }
805 // smd does not get reused after this loop
806 smd++;
807 }
808 return absent_data;
809}
810
811#ifdef UNITTESTS
813#endif
uint8_t flags
Definition decode-gre.h:0
bool DetectAsn1Match(const SigMatchData *smd, const uint8_t *buffer, const uint32_t buffer_len, const uint32_t offset)
Definition detect-asn1.c:58
int DetectBase64DecodeDoMatch(DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, const uint8_t *payload, uint32_t payload_len)
int DetectBsizeMatch(const SigMatchCtx *ctx, const uint64_t buffer_size, bool eof)
bsize match function
int DetectByteExtractDoMatch(DetectEngineThreadCtx *det_ctx, const SigMatchData *smd, const Signature *s, const uint8_t *payload, uint32_t payload_len, uint64_t *value, uint8_t endian)
bool DetectBytejumpDoMatch(DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchCtx *ctx, const uint8_t *payload, uint32_t payload_len, uint16_t flags, int32_t nbytes, int32_t offset)
Byte jump match function.
#define DETECT_BYTEJUMP_DCE
#define DETECT_BYTEJUMP_LITTLE
#define DETECT_BYTEJUMP_OFFSET_VAR
#define DETECT_BYTEJUMP_NBYTES_VAR
int DetectByteMathDoMatch(DetectEngineThreadCtx *det_ctx, const DetectByteMathData *data, const Signature *s, const uint8_t *payload, const uint32_t payload_len, uint8_t nbytes, uint64_t rvalue, uint64_t *value, uint8_t endian)
int DetectBytetestDoMatch(DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchCtx *ctx, const uint8_t *payload, uint32_t payload_len, uint16_t flags, int32_t offset, int32_t nbytes, uint64_t value)
Bytetest detection code.
#define DETECT_BYTETEST_DCE
#define DETECT_BYTETEST_OFFSET_VAR
#define DETECT_BYTETEST_VALUE_VAR
#define DETECT_BYTETEST_LITTLE
#define DETECT_BYTETEST_NBYTES_VAR
#define DETECT_CONTENT_DEPTH
#define DETECT_CONTENT_WITHIN_VAR
#define DETECT_CONTENT_WITHIN
#define DETECT_CONTENT_DEPTH_VAR
#define DETECT_CONTENT_ENDS_WITH
#define DETECT_CONTENT_OFFSET_VAR
#define DETECT_CONTENT_DISTANCE
#define DETECT_CONTENT_WITHIN_NEXT
#define DETECT_CONTENT_NEGATED
#define DETECT_CONTENT_DISTANCE_VAR
#define DETECT_CONTENT_REPLACE
#define DETECT_CONTENT_IS_SINGLE(c)
int DetectDatarepBufferMatch(DetectEngineThreadCtx *det_ctx, const DetectDatarepData *sd, const uint8_t *data, const uint32_t data_len)
int DetectDatasetBufferMatch(DetectEngineThreadCtx *det_ctx, const DetectDatasetData *sd, const uint8_t *data, const uint32_t data_len)
thread_local uint32_t ut_inspection_recursion_counter
bool DetectEngineContentInspection(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Packet *p, Flow *f, const uint8_t *buffer, const uint32_t buffer_len, const uint64_t stream_start_offset, const uint8_t flags, const enum DetectContentInspectionType inspection_mode)
wrapper around DetectEngineContentInspectionInternal to return true/false only
bool DetectContentInspectionMatchOnAbsentBuffer(const SigMatchData *smd)
tells if we should match on absent buffer, because there is an absent keyword being used
bool DetectEngineContentInspectionBuffer(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Packet *p, Flow *f, const InspectionBuffer *b, const enum DetectContentInspectionType inspection_mode)
wrapper around DetectEngineContentInspectionInternal to return true/false only
@ DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE
@ DETECT_ENGINE_CONTENT_INSPECTION_MODE_PAYLOAD
#define DETECT_CI_FLAGS_DCE_BE
#define DETECT_CI_FLAGS_SINGLE
#define DETECT_CI_FLAGS_END
#define DETECT_CI_FLAGS_DCE_LE
@ DETECT_BYTE_EXTRACT
@ DETECT_BYTEMATH
@ DETECT_BYTETEST
@ DETECT_BASE64_DECODE
@ DETECT_BYTEJUMP
@ DETECT_ISDATAAT
int DetectU16Match(const uint16_t parg, const DetectUintData_u16 *du16)
bool DetectEntropyDoMatch(DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchCtx *ctx, const uint8_t *buffer, const uint32_t buffer_len)
uint32_t id
#define ISDATAAT_RELATIVE
#define ISDATAAT_NEGATED
#define ISDATAAT_OFFSET_VAR
int DetectLuaMatchBuffer(DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, const uint8_t *buffer, uint32_t buffer_len, uint32_t offset, Flow *f)
Definition detect-lua.c:240
int DetectPcrePayloadMatch(DetectEngineThreadCtx *det_ctx, const Signature *s, const SigMatchData *smd, Packet *p, Flow *f, const uint8_t *payload, uint32_t payload_len)
Match a regex on a single payload.
#define DETECT_PCRE_RELATIVE_NEXT
Definition detect-pcre.h:34
DetectReplaceList * DetectReplaceAddToList(DetectReplaceList *replist, uint8_t *found, const DetectContentData *cd)
@ DETECT_SM_LIST_BASE64_DATA
Definition detect.h:124
DetectEngineCtx * de_ctx
struct Thresholds ctx
struct DetectEngineContentInspectionCtx::@59 recursion
main detection engine ctx
Definition detect.h:932
int inspection_recursion_limit
Definition detect.h:973
SpmThreadCtx * spm_thread_ctx
Definition detect.h:1276
uint32_t pcre_match_start_offset
Definition detect.h:1272
uint64_t * byte_values
Definition detect.h:1279
uint8_t * base64_decoded
Definition detect.h:1326
DetectReplaceList * replist
Definition detect.h:1352
Flow data structure.
Definition flow.h:356
Data needed for Match()
Definition detect.h:365
bool is_last
Definition detect.h:367
SigMatchCtx * ctx
Definition detect.h:368
uint16_t type
Definition detect.h:366
Signature container.
Definition detect.h:668
SigMatchData * sm_arrays[DETECT_SM_LIST_MAX]
Definition detect.h:731
#define BUG_ON(x)
#define SCEnter(...)
Definition util-debug.h:277
#define SCLogDebug(...)
Definition util-debug.h:275
#define SCReturnInt(x)
Definition util-debug.h:281
#define SCLogWarning(...)
Macro used to log WARNING messages.
Definition util-debug.h:255
#define unlikely(expr)
#define KEYWORD_PROFILING_END(ctx, type, m)
#define KEYWORD_PROFILING_START
uint8_t * SpmScan(const SpmCtx *ctx, SpmThreadCtx *thread_ctx, const uint8_t *haystack, uint32_t haystack_len)
Definition util-spm.c:193
uint64_t offset