suricata
detect-engine-analyzer.c
Go to the documentation of this file.
1/* Copyright (C) 2007-2025 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 Eileen Donlon <emdonlo@gmail.com>
22 * \author Victor Julien <victor@inliniac.net>
23 *
24 * Rule analyzers for the detection engine
25 */
26
27#include "suricata-common.h"
28#include "suricata.h"
29#include "rust.h"
30#include "action-globals.h"
31#include "detect.h"
32#include "detect-parse.h"
33#include "detect-engine.h"
35#include "detect-engine-mpm.h"
36#include "detect-engine-uint.h"
37#include "conf.h"
38#include "detect-content.h"
39#include "detect-pcre.h"
40#include "detect-bytejump.h"
41#include "detect-bytetest.h"
42#include "detect-isdataat.h"
43#include "detect-flow.h"
44#include "detect-tcp-flags.h"
45#include "detect-tcp-ack.h"
46#include "detect-ipopts.h"
47#include "detect-tcp-seq.h"
48#include "feature.h"
49#include "util-print.h"
50#include "util-time.h"
51#include "util-validate.h"
52#include "util-conf.h"
53#include "detect-flowbits.h"
54#include "util-var-name.h"
55#include "detect-icmp-id.h"
56#include "detect-tcp-window.h"
57
58static int rule_warnings_only = 0;
59
60/* Details for each buffer being tracked */
69
70typedef struct FpPatternStats_ {
71 uint16_t min;
72 uint16_t max;
73 uint32_t cnt;
74 uint64_t tot;
76
77/* Track which items require the item_seen value to be exposed */
79 const char *bufname;
81};
82
83typedef struct EngineAnalysisCtx_ {
84
87
90 pcre2_code *percent_re;
91
92 /*
93 * This array contains the map between the `analyzer_items` array listed above and
94 * the item ids returned by DetectBufferTypeGetByName. Iterating signature's sigmatch
95 * array provides list_ids. The map converts those ids into elements of the
96 * analyzer items array.
97 *
98 * Ultimately, the g_buffer_type_hash is searched for each buffer name. The size of that
99 * hashlist is 256, so that's the value we use here.
100 */
101 int16_t analyzer_item_map[256];
103 /*
104 * Certain values must be directly accessible. This array contains items that are directly
105 * accessed when checking if they've been seen or not.
106 */
108
111
113 /* request keywords */
114 { 0, false, false, true, "http_uri", "http uri" },
115 { 0, false, false, false, "http_raw_uri", "http raw uri" },
116 { 0, false, true, false, "http_method", "http method" },
117 { 0, false, false, false, "http_request_line", "http request line" },
118 { 0, false, false, false, "http_client_body", "http client body" },
119 { 0, false, false, true, "http_header", "http header" },
120 { 0, false, false, false, "http_raw_header", "http raw header" },
121 { 0, false, false, true, "http_cookie", "http cookie" },
122 { 0, false, false, false, "http_user_agent", "http user agent" },
123 { 0, false, false, false, "http_host", "http host" },
124 { 0, false, false, false, "http_raw_host", "http raw host" },
125 { 0, false, false, false, "http_accept_enc", "http accept enc" },
126 { 0, false, false, false, "http_referer", "http referer" },
127 { 0, false, false, false, "http_content_type", "http content type" },
128 { 0, false, false, false, "http_header_names", "http header names" },
129
130 /* response keywords not listed above */
131 { 0, false, false, false, "http_stat_msg", "http stat msg" },
132 { 0, false, false, false, "http_stat_code", "http stat code" },
133 { 0, false, true, false, "file_data", "http server body" },
134
135 /* missing request keywords */
136 { 0, false, false, false, "http_request_line", "http request line" },
137 { 0, false, false, false, "http_accept", "http accept" },
138 { 0, false, false, false, "http_accept_lang", "http accept lang" },
139 { 0, false, false, false, "http_connection", "http connection" },
140 { 0, false, false, false, "http_content_len", "http content len" },
141 { 0, false, false, false, "http_protocol", "http protocol" },
142 { 0, false, false, false, "http_start", "http start" },
143
144 /* missing response keywords; some of the missing are listed above*/
145 { 0, false, false, false, "http_response_line", "http response line" },
146 { 0, false, false, false, "http.server", "http server" },
147 { 0, false, false, false, "http.location", "http location" },
148};
149
150static void FpPatternStatsAdd(FpPatternStats *fp, int list, uint16_t patlen)
151{
152 if (list < 0 || list >= DETECT_SM_LIST_MAX)
153 return;
154
155 FpPatternStats *f = &fp[list];
156
157 if (f->min == 0)
158 f->min = patlen;
159 else if (patlen < f->min)
160 f->min = patlen;
161
162 if (patlen > f->max)
163 f->max = patlen;
164
165 f->cnt++;
166 f->tot += patlen;
167}
168
169void EngineAnalysisFP(const DetectEngineCtx *de_ctx, const Signature *s, const char *line)
170{
171 int fast_pattern_set = 0;
172 int fast_pattern_only_set = 0;
173 int fast_pattern_chop_set = 0;
174 const DetectContentData *fp_cd = NULL;
175 const SigMatch *mpm_sm = s->init_data->mpm_sm;
176 const int mpm_sm_list = s->init_data->mpm_sm_list;
177
178 if (mpm_sm != NULL) {
179 fp_cd = (DetectContentData *)mpm_sm->ctx;
180 if (fp_cd->flags & DETECT_CONTENT_FAST_PATTERN) {
181 fast_pattern_set = 1;
183 fast_pattern_only_set = 1;
184 } else if (fp_cd->flags & DETECT_CONTENT_FAST_PATTERN_CHOP) {
185 fast_pattern_chop_set = 1;
186 }
187 }
188 }
189
190 FILE *fp = de_ctx->ea->rule_engine_analysis_fp;
191 fprintf(fp, "== Sid: %u ==\n", s->id);
192 fprintf(fp, "%s\n", line);
193
194 fprintf(fp, " Fast Pattern analysis:\n");
195 if (s->init_data->prefilter_sm != NULL) {
196 fprintf(fp, " Prefilter on: %s\n",
198 fprintf(fp, "\n");
199 return;
200 }
201
202 if (fp_cd == NULL) {
203 fprintf(fp, " No content present\n");
204 fprintf(fp, "\n");
205 return;
206 }
207
208 fprintf(fp, " Fast pattern matcher: ");
209 int list_type = mpm_sm_list;
210 if (list_type == DETECT_SM_LIST_PMATCH)
211 fprintf(fp, "content\n");
212 else {
213 const char *desc = DetectEngineBufferTypeGetDescriptionById(de_ctx, list_type);
214 const char *name = DetectEngineBufferTypeGetNameById(de_ctx, list_type);
215 if (desc && name) {
216 fprintf(fp, "%s (%s)\n", desc, name);
217 }
218 }
219
220 int flags_set = 0;
221 fprintf(fp, " Flags:");
222 if (fp_cd->flags & DETECT_CONTENT_OFFSET) {
223 fprintf(fp, " Offset");
224 flags_set = 1;
225 } if (fp_cd->flags & DETECT_CONTENT_DEPTH) {
226 fprintf(fp, " Depth");
227 flags_set = 1;
228 }
229 if (fp_cd->flags & DETECT_CONTENT_WITHIN) {
230 fprintf(fp, " Within");
231 flags_set = 1;
232 }
233 if (fp_cd->flags & DETECT_CONTENT_DISTANCE) {
234 fprintf(fp, " Distance");
235 flags_set = 1;
236 }
237 if (fp_cd->flags & DETECT_CONTENT_NOCASE) {
238 fprintf(fp, " Nocase");
239 flags_set = 1;
240 }
241 if (fp_cd->flags & DETECT_CONTENT_NEGATED) {
242 fprintf(fp, " Negated");
243 flags_set = 1;
244 }
245 if (flags_set == 0)
246 fprintf(fp, " None");
247 fprintf(fp, "\n");
248
249 fprintf(fp, " Fast pattern set: %s\n", fast_pattern_set ? "yes" : "no");
250 fprintf(fp, " Fast pattern only set: %s\n", fast_pattern_only_set ? "yes" : "no");
251 fprintf(fp, " Fast pattern chop set: %s\n", fast_pattern_chop_set ? "yes" : "no");
252 if (fast_pattern_chop_set) {
253 fprintf(fp, " Fast pattern offset, length: %u, %u\n", fp_cd->fp_chop_offset,
254 fp_cd->fp_chop_len);
255 }
256
257 uint16_t patlen = fp_cd->content_len;
258 uint8_t *pat = SCMalloc(fp_cd->content_len + 1);
259 if (unlikely(pat == NULL)) {
260 FatalError("Error allocating memory");
261 }
262 memcpy(pat, fp_cd->content, fp_cd->content_len);
263 pat[fp_cd->content_len] = '\0';
264 fprintf(fp, " Original content: ");
265 PrintRawUriFp(fp, pat, patlen);
266 fprintf(fp, "\n");
267
268 if (fast_pattern_chop_set) {
269 SCFree(pat);
270 patlen = fp_cd->fp_chop_len;
271 pat = SCMalloc(fp_cd->fp_chop_len + 1);
272 if (unlikely(pat == NULL)) {
273 exit(EXIT_FAILURE);
274 }
275 memcpy(pat, fp_cd->content + fp_cd->fp_chop_offset, fp_cd->fp_chop_len);
276 pat[fp_cd->fp_chop_len] = '\0';
277 fprintf(fp, " Final content: ");
278 PrintRawUriFp(fp, pat, patlen);
279 fprintf(fp, "\n");
280
281 FpPatternStatsAdd(&de_ctx->ea->fp_pattern_stats[0], list_type, patlen);
282 } else {
283 fprintf(fp, " Final content: ");
284 PrintRawUriFp(fp, pat, patlen);
285 fprintf(fp, "\n");
286
287 FpPatternStatsAdd(&de_ctx->ea->fp_pattern_stats[0], list_type, patlen);
288 }
289 SCFree(pat);
290
291 fprintf(fp, "\n");
292}
293
294/**
295 * \brief Sets up the fast pattern analyzer according to the config.
296 *
297 * \retval 1 If rule analyzer successfully enabled.
298 * \retval 0 If not enabled.
299 */
300static int SetupFPAnalyzer(DetectEngineCtx *de_ctx)
301{
302 int fp_engine_analysis_set = 0;
303
304 if ((SCConfGetBool("engine-analysis.rules-fast-pattern", &fp_engine_analysis_set)) == 0) {
305 return false;
306 }
307
308 if (fp_engine_analysis_set == 0)
309 return false;
310
311 const char *log_dir = SCConfigGetLogDirectory();
312 char *log_path = SCMalloc(PATH_MAX);
313 if (log_path == NULL) {
314 FatalError("Unable to allocate scratch memory for rule filename");
315 }
316 snprintf(log_path, PATH_MAX, "%s/%s%s", log_dir,
317 de_ctx->ea->file_prefix ? de_ctx->ea->file_prefix : "", "rules_fast_pattern.txt");
318
319 FILE *fp = fopen(log_path, "w");
320 if (fp == NULL) {
321 SCLogError("failed to open %s: %s", log_path, strerror(errno));
322 SCFree(log_path);
323 return false;
324 }
325
327
328 SCLogInfo("Engine-Analysis for fast_pattern printed to file - %s",
329 log_path);
330 SCFree(log_path);
331
332 struct timeval tval;
333 gettimeofday(&tval, NULL);
334 struct tm local_tm;
335 struct tm *tms = SCLocalTime(tval.tv_sec, &local_tm);
336 fprintf(fp, "----------------------------------------------"
337 "---------------------\n");
338 fprintf(fp,
339 "Date: %" PRId32 "/%" PRId32 "/%04d -- "
340 "%02d:%02d:%02d\n",
341 tms->tm_mday, tms->tm_mon + 1, tms->tm_year + 1900, tms->tm_hour, tms->tm_min,
342 tms->tm_sec);
343 fprintf(fp, "----------------------------------------------"
344 "---------------------\n");
345
346 memset(&de_ctx->ea->fp_pattern_stats[0], 0, sizeof(de_ctx->ea->fp_pattern_stats));
347 return true;
348}
349
350/**
351 * \brief Compiles regex for rule analysis
352 * \retval 1 if successful
353 * \retval 0 if on error
354 */
355static bool PerCentEncodingSetup(EngineAnalysisCtx *ea_ctx)
356{
357#define DETECT_PERCENT_ENCODING_REGEX "%[0-9|a-f|A-F]{2}"
358 int en;
359 PCRE2_SIZE eo = 0;
360 int opts = 0; // PCRE2_NEWLINE_ANY??
361
362 ea_ctx->percent_re = pcre2_compile((PCRE2_SPTR8)DETECT_PERCENT_ENCODING_REGEX,
363 PCRE2_ZERO_TERMINATED, opts, &en, &eo, NULL);
364 if (ea_ctx->percent_re == NULL) {
365 PCRE2_UCHAR errbuffer[256];
366 pcre2_get_error_message(en, errbuffer, sizeof(errbuffer));
367 SCLogError("Compile of \"%s\" failed at offset %d: %s", DETECT_PERCENT_ENCODING_REGEX,
368 (int)eo, errbuffer);
369 return false;
370 }
371
372 return true;
373}
374/**
375 * \brief Sets up the rule analyzer according to the config
376 * \retval 1 if rule analyzer successfully enabled
377 * \retval 0 if not enabled
378 */
379static int SetupRuleAnalyzer(DetectEngineCtx *de_ctx)
380{
381 SCConfNode *conf = SCConfGetNode("engine-analysis");
382 int enabled = 0;
383 if (conf != NULL) {
384 const char *value = SCConfNodeLookupChildValue(conf, "rules");
385 if (value && SCConfValIsTrue(value)) {
386 enabled = 1;
387 } else if (value && strcasecmp(value, "warnings-only") == 0) {
388 enabled = 1;
389 rule_warnings_only = 1;
390 }
391 if (enabled) {
392 const char *log_dir;
393 log_dir = SCConfigGetLogDirectory();
394 char log_path[PATH_MAX];
395 snprintf(log_path, sizeof(log_path), "%s/%s%s", log_dir,
396 de_ctx->ea->file_prefix ? de_ctx->ea->file_prefix : "", "rules_analysis.txt");
397 de_ctx->ea->rule_engine_analysis_fp = fopen(log_path, "w");
398 if (de_ctx->ea->rule_engine_analysis_fp == NULL) {
399 SCLogError("failed to open %s: %s", log_path, strerror(errno));
400 return 0;
401 }
402
403 SCLogInfo("Engine-Analysis for rules printed to file - %s",
404 log_path);
405
406 struct timeval tval;
407 gettimeofday(&tval, NULL);
408 struct tm local_tm;
409 struct tm *tms = SCLocalTime(tval.tv_sec, &local_tm);
411 "----------------------------------------------"
412 "---------------------\n");
414 "Date: %" PRId32 "/%" PRId32 "/%04d -- "
415 "%02d:%02d:%02d\n",
416 tms->tm_mday, tms->tm_mon + 1, tms->tm_year + 1900, tms->tm_hour, tms->tm_min,
417 tms->tm_sec);
419 "----------------------------------------------"
420 "---------------------\n");
421
422 /*compile regex's for rule analysis*/
423 if (!PerCentEncodingSetup(de_ctx->ea)) {
425 "Error compiling regex; can't check for percent encoding in normalized "
426 "http content.\n");
427 }
428 }
429 }
430 else {
431 SCLogInfo("Conf parameter \"engine-analysis.rules\" not found. "
432 "Defaulting to not printing the rules analysis report.");
433 }
434 if (!enabled) {
435 SCLogInfo("Engine-Analysis for rules disabled in conf file.");
436 return 0;
437 }
438 return 1;
439}
440
441static void CleanupFPAnalyzer(DetectEngineCtx *de_ctx)
442{
443 FILE *fp = de_ctx->ea->rule_engine_analysis_fp;
444 fprintf(fp, "============\n"
445 "Summary:\n============\n");
446
447 for (int i = 0; i < DETECT_SM_LIST_MAX; i++) {
449 if (f->cnt == 0)
450 continue;
451
452 fprintf(fp,
453 "%s, smallest pattern %u byte(s), longest pattern %u byte(s), number of patterns "
454 "%u, avg pattern len %.2f byte(s)\n",
456 (float)((double)f->tot / (float)f->cnt));
457 }
458
461}
462
463static void CleanupRuleAnalyzer(DetectEngineCtx *de_ctx)
464{
465 if (de_ctx->ea->fp_engine_analysis_fp != NULL) {
468 }
469 if (de_ctx->ea->percent_re != NULL) {
470 pcre2_code_free(de_ctx->ea->percent_re);
471 }
472}
473
474void SetupEngineAnalysis(DetectEngineCtx *de_ctx, bool *fp_analysis, bool *rule_analysis)
475{
476 *fp_analysis = false;
477 *rule_analysis = false;
478
480 if (ea == NULL) {
481 FatalError("Unable to allocate per-engine analysis context");
482 }
483
484 ea->file_prefix = NULL;
485 size_t cfg_prefix_len = strlen(de_ctx->config_prefix);
486 if (cfg_prefix_len > 0) {
487 /* length of prefix + NULL + "." */
488 ea->file_prefix = SCCalloc(1, cfg_prefix_len + 1 + 1);
489 if (ea->file_prefix == NULL) {
490 FatalError("Unable to allocate per-engine analysis context name buffer");
491 }
492
493 snprintf(ea->file_prefix, cfg_prefix_len + 1 + 1, "%s.", de_ctx->config_prefix);
494 }
495
496 de_ctx->ea = ea;
497
498 *fp_analysis = SetupFPAnalyzer(de_ctx);
499 *rule_analysis = SetupRuleAnalyzer(de_ctx);
500
501 if (!(*fp_analysis || *rule_analysis)) {
502 if (ea->file_prefix)
503 SCFree(ea->file_prefix);
504 if (ea->analyzer_items)
506 SCFree(ea);
507 }
508}
509
511{
512 if (de_ctx->ea) {
513 CleanupRuleAnalyzer(de_ctx);
514 CleanupFPAnalyzer(de_ctx);
515 if (de_ctx->ea->file_prefix)
519 SCFree(de_ctx->ea);
520 de_ctx->ea = NULL;
521 }
522}
523
524/**
525 * \brief Checks for % encoding in content.
526 * \param Pointer to content
527 * \retval number of matches if content has % encoding
528 * \retval 0 if it doesn't have % encoding
529 * \retval -1 on error
530 */
531static int PerCentEncodingMatch(EngineAnalysisCtx *ea_ctx, uint8_t *content, uint16_t content_len)
532{
533 int ret = 0;
534
535 pcre2_match_data *match = pcre2_match_data_create_from_pattern(ea_ctx->percent_re, NULL);
536 ret = pcre2_match(ea_ctx->percent_re, (PCRE2_SPTR8)content, content_len, 0, 0, match, NULL);
537 if (ret == -1) {
538 return 0;
539 } else if (ret < -1) {
540 SCLogError("Error parsing content - %s; error code is %d", content, ret);
541 ret = -1;
542 }
543 pcre2_match_data_free(match);
544 return ret;
545}
546
547static void EngineAnalysisRulesPrintFP(const DetectEngineCtx *de_ctx, const Signature *s)
548{
549 const DetectContentData *fp_cd = NULL;
550 const SigMatch *mpm_sm = s->init_data->mpm_sm;
551 const int mpm_sm_list = s->init_data->mpm_sm_list;
552
553 if (mpm_sm != NULL) {
554 fp_cd = (DetectContentData *)mpm_sm->ctx;
555 }
556
557 if (fp_cd == NULL) {
558 return;
559 }
560
561 uint16_t patlen = fp_cd->content_len;
562 uint8_t *pat = SCMalloc(fp_cd->content_len + 1);
563 if (unlikely(pat == NULL)) {
564 FatalError("Error allocating memory");
565 }
566
567 EngineAnalysisCtx *ea_ctx = de_ctx->ea;
568
569 memcpy(pat, fp_cd->content, fp_cd->content_len);
570 pat[fp_cd->content_len] = '\0';
571
573 SCFree(pat);
574 patlen = fp_cd->fp_chop_len;
575 pat = SCMalloc(fp_cd->fp_chop_len + 1);
576 if (unlikely(pat == NULL)) {
577 exit(EXIT_FAILURE);
578 }
579 memcpy(pat, fp_cd->content + fp_cd->fp_chop_offset, fp_cd->fp_chop_len);
580 pat[fp_cd->fp_chop_len] = '\0';
581 fprintf(ea_ctx->rule_engine_analysis_fp, " Fast Pattern \"");
582 PrintRawUriFp(ea_ctx->rule_engine_analysis_fp, pat, patlen);
583 } else {
584 fprintf(ea_ctx->rule_engine_analysis_fp, " Fast Pattern \"");
585 PrintRawUriFp(ea_ctx->rule_engine_analysis_fp, pat, patlen);
586 }
587 SCFree(pat);
588
589 fprintf(ea_ctx->rule_engine_analysis_fp, "\" on \"");
590
591 const int list_type = mpm_sm_list;
592 if (list_type == DETECT_SM_LIST_PMATCH) {
593 int payload = 0;
594 int stream = 0;
596 payload = 1;
598 stream = 1;
599 fprintf(ea_ctx->rule_engine_analysis_fp, "%s",
600 payload ? (stream ? "payload and reassembled stream" : "payload")
601 : "reassembled stream");
602 }
603 else {
604 const char *desc = DetectEngineBufferTypeGetDescriptionById(de_ctx, list_type);
605 const char *name = DetectEngineBufferTypeGetNameById(de_ctx, list_type);
606 if (desc && name) {
607 fprintf(ea_ctx->rule_engine_analysis_fp, "%s (%s)", desc, name);
608 } else if (desc || name) {
609 fprintf(ea_ctx->rule_engine_analysis_fp, "%s", desc ? desc : name);
610 }
611
612 }
613
614 fprintf(ea_ctx->rule_engine_analysis_fp, "\" ");
616 if (bt && bt->transforms.cnt) {
617 fprintf(ea_ctx->rule_engine_analysis_fp, "(with %d transform(s)) ", bt->transforms.cnt);
618 }
619 fprintf(ea_ctx->rule_engine_analysis_fp, "buffer.\n");
620}
621
623 const DetectEngineCtx *de_ctx, const char *line, const char *file, int lineno)
624{
625 fprintf(de_ctx->ea->fp_engine_analysis_fp, "== Sid: UNKNOWN ==\n");
626 fprintf(de_ctx->ea->fp_engine_analysis_fp, "%s\n", line);
627 fprintf(de_ctx->ea->fp_engine_analysis_fp, " FAILURE: invalid rule.\n");
628 fprintf(de_ctx->ea->fp_engine_analysis_fp, " File: %s.\n", file);
629 fprintf(de_ctx->ea->fp_engine_analysis_fp, " Line: %d.\n", lineno);
630 fprintf(de_ctx->ea->fp_engine_analysis_fp, "\n");
631}
632
633typedef struct RuleAnalyzer {
634 SCJsonBuilder *js; /* document root */
635
636 SCJsonBuilder *js_warnings;
637 SCJsonBuilder *js_notes;
639
640static void ATTR_FMT_PRINTF(2, 3) AnalyzerNote(RuleAnalyzer *ctx, char *fmt, ...)
641{
642 va_list ap;
643 char str[1024];
644
645 va_start(ap, fmt);
646 vsnprintf(str, sizeof(str), fmt, ap);
647 va_end(ap);
648
649 if (!ctx->js_notes)
650 ctx->js_notes = SCJbNewArray();
651 if (ctx->js_notes)
652 SCJbAppendString(ctx->js_notes, str);
653}
654
655static void ATTR_FMT_PRINTF(2, 3) AnalyzerWarning(RuleAnalyzer *ctx, char *fmt, ...)
656{
657 va_list ap;
658 char str[1024];
659
660 va_start(ap, fmt);
661 vsnprintf(str, sizeof(str), fmt, ap);
662 va_end(ap);
663
664 if (!ctx->js_warnings)
665 ctx->js_warnings = SCJbNewArray();
666 if (ctx->js_warnings)
667 SCJbAppendString(ctx->js_warnings, str);
668}
669
670#define CHECK(pat) if (strlen((pat)) <= len && memcmp((pat), buf, MIN(len, strlen((pat)))) == 0) return true;
671
672static bool LooksLikeHTTPMethod(const uint8_t *buf, uint16_t len)
673{
674 CHECK("GET /");
675 CHECK("POST /");
676 CHECK("HEAD /");
677 CHECK("PUT /");
678 return false;
679}
680
681static bool LooksLikeHTTPUA(const uint8_t *buf, uint16_t len)
682{
683 CHECK("User-Agent: ");
684 CHECK("\nUser-Agent: ");
685 return false;
686}
687
688static void DumpContent(SCJsonBuilder *js, const DetectContentData *cd)
689{
690 char pattern_str[1024] = "";
691 DetectContentPatternPrettyPrint(cd, pattern_str, sizeof(pattern_str));
692
693 SCJbSetString(js, "pattern", pattern_str);
694 SCJbSetUint(js, "length", cd->content_len);
695 SCJbSetBool(js, "nocase", cd->flags & DETECT_CONTENT_NOCASE);
696 SCJbSetBool(js, "negated", cd->flags & DETECT_CONTENT_NEGATED);
697 SCJbSetBool(js, "starts_with", cd->flags & DETECT_CONTENT_STARTS_WITH);
698 SCJbSetBool(js, "ends_with", cd->flags & DETECT_CONTENT_ENDS_WITH);
699 SCJbSetBool(js, "is_mpm", cd->flags & DETECT_CONTENT_MPM);
700 SCJbSetBool(js, "no_double_inspect", cd->flags & DETECT_CONTENT_NO_DOUBLE_INSPECTION_REQUIRED);
701 if (cd->flags & DETECT_CONTENT_OFFSET) {
702 SCJbSetUint(js, "offset", cd->offset);
703 }
704 if (cd->flags & DETECT_CONTENT_DEPTH) {
705 SCJbSetUint(js, "depth", cd->depth);
706 }
707 if (cd->flags & DETECT_CONTENT_DISTANCE) {
708 SCJbSetInt(js, "distance", cd->distance);
709 }
710 if (cd->flags & DETECT_CONTENT_WITHIN) {
711 SCJbSetInt(js, "within", cd->within);
712 }
713 SCJbSetBool(js, "fast_pattern", cd->flags & DETECT_CONTENT_FAST_PATTERN);
714 SCJbSetBool(js, "relative_next", cd->flags & DETECT_CONTENT_RELATIVE_NEXT);
715}
716
717static void DumpPcre(SCJsonBuilder *js, const DetectPcreData *cd)
718{
719 SCJbSetBool(js, "relative", cd->flags & DETECT_PCRE_RELATIVE);
720 SCJbSetBool(js, "relative_next", cd->flags & DETECT_PCRE_RELATIVE_NEXT);
721 SCJbSetBool(js, "nocase", cd->flags & DETECT_PCRE_CASELESS);
722 SCJbSetBool(js, "negated", cd->flags & DETECT_PCRE_NEGATE);
723}
724
725static void DumpMatches(RuleAnalyzer *ctx, SCJsonBuilder *js, const SigMatchData *smd)
726{
727 if (smd == NULL)
728 return;
729
730 SCJbOpenArray(js, "matches");
731 do {
732 SCJbStartObject(js);
733 const char *mname = sigmatch_table[smd->type].name;
734 SCJbSetString(js, "name", mname);
735
736 switch (smd->type) {
737 case DETECT_CONTENT: {
738 const DetectContentData *cd = (const DetectContentData *)smd->ctx;
739
740 SCJbOpenObject(js, "content");
741 DumpContent(js, cd);
743 AnalyzerNote(ctx, (char *)"'fast_pattern:only' option is silently ignored and "
744 "is interpreted as regular 'fast_pattern'");
745 }
746 if (LooksLikeHTTPMethod(cd->content, cd->content_len)) {
747 AnalyzerNote(ctx,
748 (char *)"pattern looks like it inspects HTTP, use http.request_line or "
749 "http.method and http.uri instead for improved performance");
750 }
751 if (LooksLikeHTTPUA(cd->content, cd->content_len)) {
752 AnalyzerNote(ctx,
753 (char *)"pattern looks like it inspects HTTP, use http.user_agent "
754 "or http.header for improved performance");
755 }
757 AnalyzerNote(ctx, (char *)"'within' option for pattern w/o previous content "
758 "was converted to 'depth'");
759 }
761 AnalyzerNote(ctx, (char *)"'distance' option for pattern w/o previous content "
762 "was converted to 'offset'");
763 }
764 SCJbClose(js);
765 break;
766 }
767 case DETECT_PCRE: {
768 const DetectPcreData *cd = (const DetectPcreData *)smd->ctx;
769
770 SCJbOpenObject(js, "pcre");
771 DumpPcre(js, cd);
772 SCJbClose(js);
773 if (cd->flags & DETECT_PCRE_RAWBYTES) {
774 AnalyzerNote(ctx,
775 (char *)"'/B' (rawbytes) option is a no-op and is silently ignored");
776 }
777 break;
778 }
779 case DETECT_BYTEJUMP: {
780 const DetectBytejumpData *cd = (const DetectBytejumpData *)smd->ctx;
781
782 SCJbOpenObject(js, "byte_jump");
783 SCJbSetUint(js, "nbytes", cd->nbytes);
784 SCJbSetInt(js, "offset", cd->offset);
785 SCJbSetUint(js, "multiplier", cd->multiplier);
786 SCJbSetInt(js, "post_offset", cd->post_offset);
787 switch (cd->base) {
789 SCJbSetString(js, "base", "unset");
790 break;
792 SCJbSetString(js, "base", "oct");
793 break;
795 SCJbSetString(js, "base", "dec");
796 break;
798 SCJbSetString(js, "base", "hex");
799 break;
800 }
801 SCJbOpenArray(js, "flags");
803 SCJbAppendString(js, "from_beginning");
805 SCJbAppendString(js, "little_endian");
806 if (cd->flags & DETECT_BYTEJUMP_BIG)
807 SCJbAppendString(js, "big_endian");
809 SCJbAppendString(js, "string");
811 SCJbAppendString(js, "relative");
813 SCJbAppendString(js, "align");
814 if (cd->flags & DETECT_BYTEJUMP_DCE)
815 SCJbAppendString(js, "dce");
817 SCJbAppendString(js, "offset_be");
818 if (cd->flags & DETECT_BYTEJUMP_END)
819 SCJbAppendString(js, "from_end");
820 SCJbClose(js);
821 SCJbClose(js);
822 break;
823 }
824 case DETECT_BYTETEST: {
825 const DetectBytetestData *cd = (const DetectBytetestData *)smd->ctx;
826
827 SCJbOpenObject(js, "byte_test");
828 SCJbSetUint(js, "nbytes", cd->nbytes);
829 SCJbSetInt(js, "offset", cd->offset);
830 switch (cd->base) {
832 SCJbSetString(js, "base", "unset");
833 break;
835 SCJbSetString(js, "base", "oct");
836 break;
838 SCJbSetString(js, "base", "dec");
839 break;
841 SCJbSetString(js, "base", "hex");
842 break;
843 }
844 SCJbOpenArray(js, "flags");
846 SCJbAppendString(js, "little_endian");
847 if (cd->flags & DETECT_BYTETEST_BIG)
848 SCJbAppendString(js, "big_endian");
850 SCJbAppendString(js, "string");
852 SCJbAppendString(js, "relative");
853 if (cd->flags & DETECT_BYTETEST_DCE)
854 SCJbAppendString(js, "dce");
855 SCJbClose(js);
856 SCJbClose(js);
857 break;
858 }
859 case DETECT_ABSENT: {
860 const DetectAbsentData *dad = (const DetectAbsentData *)smd->ctx;
861 SCJbOpenObject(js, "absent");
862 SCJbSetBool(js, "or_else", dad->or_else);
863 SCJbClose(js);
864 break;
865 }
866
867 case DETECT_IPOPTS: {
868 const DetectIpOptsData *cd = (const DetectIpOptsData *)smd->ctx;
869
870 SCJbOpenObject(js, "ipopts");
871 const char *flag = IpOptsFlagToString(cd->ipopt);
872 SCJbSetString(js, "option", flag);
873 SCJbClose(js);
874 break;
875 }
876 case DETECT_FLOWBITS: {
877 const DetectFlowbitsData *cd = (const DetectFlowbitsData *)smd->ctx;
878
879 SCJbOpenObject(js, "flowbits");
880 switch (cd->cmd) {
882 SCJbSetString(js, "cmd", "isset");
883 break;
885 SCJbSetString(js, "cmd", "isnotset");
886 break;
888 SCJbSetString(js, "cmd", "set");
889 break;
891 SCJbSetString(js, "cmd", "unset");
892 break;
894 SCJbSetString(js, "cmd", "toggle");
895 break;
896 }
897 bool is_or = false;
898 SCJbOpenArray(js, "names");
899 if (cd->or_list_size == 0) {
900 SCJbAppendString(js, VarNameStoreSetupLookup(cd->idx, VAR_TYPE_FLOW_BIT));
901 } else if (cd->or_list_size > 0) {
902 is_or = true;
903 for (uint8_t i = 0; i < cd->or_list_size; i++) {
904 const char *varname =
906 SCJbAppendString(js, varname);
907 }
908 }
909 SCJbClose(js); // array
910 if (is_or) {
911 SCJbSetString(js, "operator", "or");
912 }
913 SCJbClose(js); // object
914 break;
915 }
916 case DETECT_ACK: {
917 const DetectAckData *cd = (const DetectAckData *)smd->ctx;
918
919 SCJbOpenObject(js, "ack");
920 SCJbSetUint(js, "number", cd->ack);
921 SCJbClose(js);
922 break;
923 }
924 case DETECT_SEQ: {
925 const DetectSeqData *cd = (const DetectSeqData *)smd->ctx;
926 SCJbOpenObject(js, "seq");
927 SCJbSetUint(js, "number", cd->seq);
928 SCJbClose(js);
929 break;
930 }
931 case DETECT_TCPMSS: {
932 const DetectU16Data *cd = (const DetectU16Data *)smd->ctx;
933 SCJbOpenObject(js, "tcp_mss");
934 SCDetectU16ToJson(js, cd);
935 SCJbClose(js);
936 break;
937 }
938 case DETECT_DSIZE: {
939 const DetectU16Data *cd = (const DetectU16Data *)smd->ctx;
940 SCJbOpenObject(js, "dsize");
941 SCDetectU16ToJson(js, cd);
942 SCJbClose(js);
943 break;
944 }
945 case DETECT_ICODE: {
946 const DetectU8Data *cd = (const DetectU8Data *)smd->ctx;
947 SCJbOpenObject(js, "code");
948 SCDetectU8ToJson(js, cd);
949 SCJbClose(js);
950 break;
951 }
952 case DETECT_ICMP_ID: {
953 const DetectIcmpIdData *cd = (const DetectIcmpIdData *)smd->ctx;
954 SCJbOpenObject(js, "id");
955 SCJbSetUint(js, "number", SCNtohs(cd->id));
956 SCJbClose(js);
957 break;
958 }
959 case DETECT_WINDOW: {
960 const DetectWindowData *wd = (const DetectWindowData *)smd->ctx;
961 SCJbOpenObject(js, "window");
962 SCJbSetUint(js, "size", wd->size);
963 SCJbSetBool(js, "negated", wd->negated);
964 SCJbClose(js);
965 break;
966 }
967 case DETECT_FLOW_AGE: {
968 const DetectU32Data *cd = (const DetectU32Data *)smd->ctx;
969 SCJbOpenObject(js, "flow_age");
970 SCDetectU32ToJson(js, cd);
971 SCJbClose(js);
972 break;
973 }
974 }
975 SCJbClose(js);
976
977 if (smd->is_last)
978 break;
979 smd++;
980 } while (1);
981 SCJbClose(js);
982}
983
986{
987 SCEnter();
988
989 RuleAnalyzer ctx = { NULL, NULL, NULL };
990
991 ctx.js = SCJbNewObject();
992 if (ctx.js == NULL)
993 SCReturn;
994
995 if (s->init_data->firewall_rule) {
996 JB_SET_STRING(ctx.js, "class", "firewall");
997 } else {
998 JB_SET_STRING(ctx.js, "class", "threat detection");
999 }
1000
1001 SCJbSetString(ctx.js, "raw", s->sig_str);
1002 SCJbSetUint(ctx.js, "id", s->id);
1003 SCJbSetUint(ctx.js, "gid", s->gid);
1004 SCJbSetUint(ctx.js, "rev", s->rev);
1005 SCJbSetString(ctx.js, "msg", s->msg);
1006
1007 const char *alproto = AppProtoToString(s->alproto);
1008 SCJbSetString(ctx.js, "app_proto", alproto);
1009
1010 SCJbOpenArray(ctx.js, "requirements");
1011 if (s->mask & SIG_MASK_REQUIRE_PAYLOAD) {
1012 SCJbAppendString(ctx.js, "payload");
1013 }
1015 SCJbAppendString(ctx.js, "no_payload");
1016 }
1017 if (s->mask & SIG_MASK_REQUIRE_FLOW) {
1018 SCJbAppendString(ctx.js, "flow");
1019 }
1021 SCJbAppendString(ctx.js, "tcp_flags_init_deinit");
1022 }
1024 SCJbAppendString(ctx.js, "tcp_flags_unusual");
1025 }
1027 SCJbAppendString(ctx.js, "engine_event");
1028 }
1030 SCJbAppendString(ctx.js, "real_pkt");
1031 }
1032 SCJbClose(ctx.js);
1033
1034 SCJbOpenObject(ctx.js, "match_policy");
1035 SCJbOpenArray(ctx.js, "actions");
1036 if (s->action & ACTION_ALERT) {
1037 SCJbAppendString(ctx.js, "alert");
1038 }
1039 if (s->action & ACTION_DROP) {
1040 SCJbAppendString(ctx.js, "drop");
1041 }
1042 if (s->action & ACTION_REJECT) {
1043 SCJbAppendString(ctx.js, "reject");
1044 }
1045 if (s->action & ACTION_REJECT_DST) {
1046 SCJbAppendString(ctx.js, "reject_dst");
1047 }
1048 if (s->action & ACTION_REJECT_BOTH) {
1049 SCJbAppendString(ctx.js, "reject_both");
1050 }
1051 if (s->action & ACTION_CONFIG) {
1052 SCJbAppendString(ctx.js, "config");
1053 }
1054 if (s->action & ACTION_PASS) {
1055 SCJbAppendString(ctx.js, "pass");
1056 }
1057 if (s->action & ACTION_ACCEPT) {
1058 SCJbAppendString(ctx.js, "accept");
1059 }
1060 SCJbClose(ctx.js);
1061
1062 if (s->action_scope == ACTION_SCOPE_AUTO) {
1064 switch (flow_action) {
1066 SCJbSetString(ctx.js, "scope", "packet");
1067 break;
1069 SCJbSetString(ctx.js, "scope", "flow");
1070 break;
1072 SCJbSetString(ctx.js, "scope", "flow_if_stateful");
1073 break;
1074 }
1075 } else {
1076 enum ActionScope as = s->action_scope;
1077 switch (as) {
1079 SCJbSetString(ctx.js, "scope", "packet");
1080 break;
1081 case ACTION_SCOPE_FLOW:
1082 SCJbSetString(ctx.js, "scope", "flow");
1083 break;
1084 case ACTION_SCOPE_HOOK:
1085 SCJbSetString(ctx.js, "scope", "hook");
1086 break;
1087 case ACTION_SCOPE_TX:
1088 SCJbSetString(ctx.js, "scope", "tx");
1089 break;
1090 case ACTION_SCOPE_AUTO: /* should be unreachable */
1091 break;
1092 }
1093 }
1094 SCJbClose(ctx.js);
1095
1096 switch (s->type) {
1097 case SIG_TYPE_NOT_SET:
1098 SCJbSetString(ctx.js, "type", "unset");
1099 break;
1100 case SIG_TYPE_IPONLY:
1101 SCJbSetString(ctx.js, "type", "ip_only");
1102 break;
1104 SCJbSetString(ctx.js, "type", "like_ip_only");
1105 break;
1106 case SIG_TYPE_PDONLY:
1107 SCJbSetString(ctx.js, "type", "pd_only");
1108 break;
1109 case SIG_TYPE_DEONLY:
1110 SCJbSetString(ctx.js, "type", "de_only");
1111 break;
1112 case SIG_TYPE_PKT:
1113 SCJbSetString(ctx.js, "type", "pkt");
1114 break;
1116 SCJbSetString(ctx.js, "type", "pkt_stream");
1117 break;
1118 case SIG_TYPE_STREAM:
1119 SCJbSetString(ctx.js, "type", "stream");
1120 break;
1121 case SIG_TYPE_APPLAYER:
1122 SCJbSetString(ctx.js, "type", "app_layer");
1123 break;
1124 case SIG_TYPE_APP_TX:
1125 SCJbSetString(ctx.js, "type", "app_tx");
1126 break;
1127 case SIG_TYPE_MAX:
1128 SCJbSetString(ctx.js, "type", "error");
1129 break;
1130 }
1131
1132 // dependencies object and its subfields only logged if we have values
1134 SCJbOpenObject(ctx.js, "dependencies");
1135 SCJbOpenObject(ctx.js, "flowbits");
1136 SCJbOpenObject(ctx.js, "upstream");
1138 SCJbOpenObject(ctx.js, "state_modifying_rules");
1139 SCJbOpenArray(ctx.js, "sids");
1140 for (uint32_t i = 0; i < s->init_data->rule_state_dependant_sids_idx; i++) {
1141 SCJbAppendUint(ctx.js, s->init_data->rule_state_dependant_sids_array[i]);
1142 }
1143 SCJbClose(ctx.js); // sids
1144 SCJbOpenArray(ctx.js, "names");
1145 for (uint32_t i = 0; i < s->init_data->rule_state_flowbits_ids_size - 1; i++) {
1146 if (s->init_data->rule_state_flowbits_ids_array[i] != 0) {
1147 SCJbAppendString(ctx.js,
1150 }
1151 }
1152 SCJbClose(ctx.js); // names
1153 SCJbClose(ctx.js); // state_modifying_rules
1154 }
1155 SCJbClose(ctx.js); // upstream
1156 SCJbClose(ctx.js); // flowbits
1157 SCJbClose(ctx.js); // dependencies
1158 }
1159
1160 SCJbOpenArray(ctx.js, "flags");
1161 if (s->flags & SIG_FLAG_SRC_ANY) {
1162 SCJbAppendString(ctx.js, "src_any");
1163 }
1164 if (s->flags & SIG_FLAG_DST_ANY) {
1165 SCJbAppendString(ctx.js, "dst_any");
1166 }
1167 if (s->flags & SIG_FLAG_SP_ANY) {
1168 SCJbAppendString(ctx.js, "sp_any");
1169 }
1170 if (s->flags & SIG_FLAG_DP_ANY) {
1171 SCJbAppendString(ctx.js, "dp_any");
1172 }
1173 if ((s->action & ACTION_ALERT) == 0) {
1174 SCJbAppendString(ctx.js, "noalert");
1175 }
1176 if (s->flags & SIG_FLAG_DSIZE) {
1177 SCJbAppendString(ctx.js, "dsize");
1178 }
1179 if (s->flags & SIG_FLAG_APPLAYER) {
1180 SCJbAppendString(ctx.js, "applayer");
1181 }
1182 if (s->flags & SIG_FLAG_REQUIRE_PACKET) {
1183 SCJbAppendString(ctx.js, "need_packet");
1184 }
1185 if (s->flags & SIG_FLAG_REQUIRE_STREAM) {
1186 SCJbAppendString(ctx.js, "need_stream");
1187 }
1188 if (s->flags & SIG_FLAG_MPM_NEG) {
1189 SCJbAppendString(ctx.js, "negated_mpm");
1190 }
1191 if (s->flags & SIG_FLAG_FLUSH) {
1192 SCJbAppendString(ctx.js, "flush");
1193 }
1195 SCJbAppendString(ctx.js, "need_flowvar");
1196 }
1197 if (s->flags & SIG_FLAG_FILESTORE) {
1198 SCJbAppendString(ctx.js, "filestore");
1199 }
1200 if (s->flags & SIG_FLAG_TOSERVER) {
1201 SCJbAppendString(ctx.js, "toserver");
1202 }
1203 if (s->flags & SIG_FLAG_TOCLIENT) {
1204 SCJbAppendString(ctx.js, "toclient");
1205 }
1206 if (s->flags & SIG_FLAG_TLSSTORE) {
1207 SCJbAppendString(ctx.js, "tlsstore");
1208 }
1209 if (s->flags & SIG_FLAG_BYPASS) {
1210 SCJbAppendString(ctx.js, "bypass");
1211 }
1212 if (s->flags & SIG_FLAG_PREFILTER) {
1213 SCJbAppendString(ctx.js, "prefilter");
1214 }
1215 if (s->flags & SIG_FLAG_SRC_IS_TARGET) {
1216 SCJbAppendString(ctx.js, "src_is_target");
1217 }
1218 if (s->flags & SIG_FLAG_DEST_IS_TARGET) {
1219 SCJbAppendString(ctx.js, "dst_is_target");
1220 }
1221 SCJbClose(ctx.js);
1222
1223 const DetectEnginePktInspectionEngine *pkt_mpm = NULL;
1224 const DetectEngineAppInspectionEngine *app_mpm = NULL;
1225
1226 SCJbOpenArray(ctx.js, "pkt_engines");
1228 for ( ; pkt != NULL; pkt = pkt->next) {
1230 if (name == NULL) {
1231 switch (pkt->sm_list) {
1233 name = "payload";
1234 break;
1236 name = "packet";
1237 break;
1238 default:
1239 name = "unknown";
1240 break;
1241 }
1242 }
1243 SCJbStartObject(ctx.js);
1244 SCJbSetString(ctx.js, "name", name);
1245 SCJbSetBool(ctx.js, "is_mpm", pkt->mpm);
1246 if (pkt->v1.transforms != NULL) {
1247 SCJbOpenArray(ctx.js, "transforms");
1248 for (int t = 0; t < pkt->v1.transforms->cnt; t++) {
1249 SCJbStartObject(ctx.js);
1250 SCJbSetString(ctx.js, "name",
1252 SCJbClose(ctx.js);
1253 }
1254 SCJbClose(ctx.js);
1255 }
1256 DumpMatches(&ctx, ctx.js, pkt->smd);
1257 SCJbClose(ctx.js);
1258 if (pkt->mpm) {
1259 pkt_mpm = pkt;
1260 }
1261 }
1262 SCJbClose(ctx.js);
1263 SCJbOpenArray(ctx.js, "frame_engines");
1265 for (; frame != NULL; frame = frame->next) {
1267 SCJbStartObject(ctx.js);
1268 SCJbSetString(ctx.js, "name", name);
1269 SCJbSetBool(ctx.js, "is_mpm", frame->mpm);
1270 if (frame->v1.transforms != NULL) {
1271 SCJbOpenArray(ctx.js, "transforms");
1272 for (int t = 0; t < frame->v1.transforms->cnt; t++) {
1273 SCJbStartObject(ctx.js);
1274 SCJbSetString(ctx.js, "name",
1276 SCJbClose(ctx.js);
1277 }
1278 SCJbClose(ctx.js);
1279 }
1280 DumpMatches(&ctx, ctx.js, frame->smd);
1281 SCJbClose(ctx.js);
1282 }
1283 SCJbClose(ctx.js);
1284
1286 bool has_stream = false;
1287 bool has_client_body_mpm = false;
1288 bool has_file_data_mpm = false;
1289
1290 SCJbOpenArray(ctx.js, "engines");
1292 for ( ; app != NULL; app = app->next) {
1294 if (name == NULL) {
1295 switch (app->sm_list) {
1297 name = "stream";
1298 break;
1299 default:
1300 name = "unknown";
1301 break;
1302 }
1303 }
1304
1305 if (app->sm_list == DETECT_SM_LIST_PMATCH && !app->mpm) {
1306 has_stream = true;
1307 } else if (app->mpm && strcmp(name, "http_client_body") == 0) {
1308 has_client_body_mpm = true;
1309 } else if (app->mpm && strcmp(name, "file_data") == 0) {
1310 has_file_data_mpm = true;
1311 }
1312
1313 SCJbStartObject(ctx.js);
1314 SCJbSetString(ctx.js, "name", name);
1315 const char *direction = app->dir == 0 ? "toserver" : "toclient";
1316 SCJbSetString(ctx.js, "direction", direction);
1317 SCJbSetBool(ctx.js, "is_mpm", app->mpm);
1318 SCJbSetString(ctx.js, "app_proto", AppProtoToString(app->alproto));
1319 SCJbSetUint(ctx.js, "progress", app->progress);
1320
1321 if (app->v2.transforms != NULL) {
1322 SCJbOpenArray(ctx.js, "transforms");
1323 for (int t = 0; t < app->v2.transforms->cnt; t++) {
1324 SCJbStartObject(ctx.js);
1325 SCJbSetString(ctx.js, "name",
1327 SCJbClose(ctx.js);
1328 }
1329 SCJbClose(ctx.js);
1330 }
1331 DumpMatches(&ctx, ctx.js, app->smd);
1332 SCJbClose(ctx.js);
1333 if (app->mpm) {
1334 app_mpm = app;
1335 }
1336 }
1337 SCJbClose(ctx.js);
1338
1339 if (has_stream && has_client_body_mpm)
1340 AnalyzerNote(&ctx, (char *)"mpm in http_client_body combined with stream match leads to stream buffering");
1341 if (has_stream && has_file_data_mpm)
1342 AnalyzerNote(&ctx, (char *)"mpm in file_data combined with stream match leads to stream buffering");
1343 }
1344
1345 SCJbOpenObject(ctx.js, "lists");
1346 for (int i = 0; i < DETECT_SM_LIST_MAX; i++) {
1347 if (s->sm_arrays[i] != NULL) {
1348 SCJbOpenObject(ctx.js, DetectListToHumanString(i));
1349 DumpMatches(&ctx, ctx.js, s->sm_arrays[i]);
1350 SCJbClose(ctx.js);
1351 }
1352 }
1353 SCJbClose(ctx.js);
1354
1355 if (pkt_mpm || app_mpm) {
1356 SCJbOpenObject(ctx.js, "mpm");
1357
1358 int mpm_list = pkt_mpm ? DETECT_SM_LIST_PMATCH : app_mpm->sm_list;
1359 const char *name;
1360 if (mpm_list < DETECT_SM_LIST_DYNAMIC_START)
1361 name = DetectListToHumanString(mpm_list);
1362 else
1364 SCJbSetString(ctx.js, "buffer", name);
1365
1366 SigMatchData *smd = pkt_mpm ? pkt_mpm->smd : app_mpm->smd;
1367 if (smd == NULL && mpm_list == DETECT_SM_LIST_PMATCH) {
1368 smd = s->sm_arrays[mpm_list];
1369 }
1370 do {
1371 switch (smd->type) {
1372 case DETECT_CONTENT: {
1373 const DetectContentData *cd = (const DetectContentData *)smd->ctx;
1374 if (cd->flags & DETECT_CONTENT_MPM) {
1375 DumpContent(ctx.js, cd);
1376 }
1377 break;
1378 }
1379 }
1380
1381 if (smd->is_last)
1382 break;
1383 smd++;
1384 } while (1);
1385 SCJbClose(ctx.js);
1386 } else if (s->init_data->prefilter_sm) {
1387 SCJbOpenObject(ctx.js, "prefilter");
1388 int prefilter_list = SigMatchListSMBelongsTo(s, s->init_data->prefilter_sm);
1389 const char *name;
1390 if (prefilter_list < DETECT_SM_LIST_DYNAMIC_START)
1391 name = DetectListToHumanString(prefilter_list);
1392 else
1394 SCJbSetString(ctx.js, "buffer", name);
1395 const char *mname = sigmatch_table[s->init_data->prefilter_sm->type].name;
1396 SCJbSetString(ctx.js, "name", mname);
1397 SCJbClose(ctx.js);
1398 }
1399
1400 if (ctx.js_warnings) {
1401 SCJbClose(ctx.js_warnings);
1402 SCJbSetObject(ctx.js, "warnings", ctx.js_warnings);
1403 SCJbFree(ctx.js_warnings);
1404 ctx.js_warnings = NULL;
1405 }
1406 if (ctx.js_notes) {
1407 SCJbClose(ctx.js_notes);
1408 SCJbSetObject(ctx.js, "notes", ctx.js_notes);
1409 SCJbFree(ctx.js_notes);
1410 ctx.js_notes = NULL;
1411 }
1412 SCJbClose(ctx.js);
1413
1414 const char *filename = "rules.json";
1415 const char *log_dir = SCConfigGetLogDirectory();
1416 char json_path[PATH_MAX] = "";
1417 snprintf(json_path, sizeof(json_path), "%s/%s%s", log_dir,
1418 de_ctx->ea->file_prefix ? de_ctx->ea->file_prefix : "", filename);
1419
1421 FILE *fp = fopen(json_path, "a");
1422 if (fp != NULL) {
1423 fwrite(SCJbPtr(ctx.js), SCJbLen(ctx.js), 1, fp);
1424 fprintf(fp, "\n");
1425 fclose(fp);
1426 }
1428 SCJbFree(ctx.js);
1429 SCReturn;
1430}
1431
1433{
1434 if (de_ctx->pattern_hash_table == NULL)
1435 return;
1436
1437 SCJsonBuilder *root_jb = SCJbNewObject();
1438 SCJsonBuilder *arrays[de_ctx->buffer_type_id];
1439 memset(&arrays, 0, sizeof(SCJsonBuilder *) * de_ctx->buffer_type_id);
1440
1441 SCJbOpenArray(root_jb, "buffers");
1442
1444 htb != NULL; htb = HashListTableGetListNext(htb)) {
1445 char str[1024] = "";
1448
1449 SCJsonBuilder *jb = arrays[p->sm_list];
1450 if (arrays[p->sm_list] == NULL) {
1451 jb = arrays[p->sm_list] = SCJbNewObject();
1452 const char *name;
1455 else
1457 SCJbSetString(jb, "name", name);
1458 SCJbSetUint(jb, "list_id", p->sm_list);
1459
1460 SCJbOpenArray(jb, "patterns");
1461 }
1462
1463 SCJbStartObject(jb);
1464 SCJbSetString(jb, "pattern", str);
1465 SCJbSetUint(jb, "patlen", p->cd->content_len);
1466 SCJbSetUint(jb, "cnt", p->cnt);
1467 SCJbSetUint(jb, "mpm", p->mpm);
1468 SCJbOpenObject(jb, "flags");
1469 SCJbSetBool(jb, "nocase", p->cd->flags & DETECT_CONTENT_NOCASE);
1470 SCJbSetBool(jb, "negated", p->cd->flags & DETECT_CONTENT_NEGATED);
1471 SCJbSetBool(jb, "depth", p->cd->flags & DETECT_CONTENT_DEPTH);
1472 SCJbSetBool(jb, "offset", p->cd->flags & DETECT_CONTENT_OFFSET);
1473 SCJbSetBool(jb, "endswith", p->cd->flags & DETECT_CONTENT_ENDS_WITH);
1474 SCJbClose(jb);
1475 SCJbClose(jb);
1476 }
1477
1478 for (uint32_t i = 0; i < de_ctx->buffer_type_id; i++) {
1479 SCJsonBuilder *jb = arrays[i];
1480 if (jb == NULL)
1481 continue;
1482
1483 SCJbClose(jb); // array
1484 SCJbClose(jb); // object
1485
1486 SCJbAppendObject(root_jb, jb);
1487 SCJbFree(jb);
1488 }
1489 SCJbClose(root_jb);
1490 SCJbClose(root_jb);
1491
1492 const char *filename = "patterns.json";
1493 const char *log_dir = SCConfigGetLogDirectory();
1494 char json_path[PATH_MAX] = "";
1495 snprintf(json_path, sizeof(json_path), "%s/%s%s", log_dir,
1496 de_ctx->ea->file_prefix ? de_ctx->ea->file_prefix : "", filename);
1497
1499 FILE *fp = fopen(json_path, "a");
1500 if (fp != NULL) {
1501 fwrite(SCJbPtr(root_jb), SCJbLen(root_jb), 1, fp);
1502 fprintf(fp, "\n");
1503 fclose(fp);
1504 }
1506 SCJbFree(root_jb);
1507
1509 de_ctx->pattern_hash_table = NULL;
1510}
1511
1512static void EngineAnalysisItemsReset(EngineAnalysisCtx *ea_ctx)
1513{
1514 for (size_t i = 0; i < ARRAY_SIZE(analyzer_items); i++) {
1515 ea_ctx->analyzer_items[i].item_seen = false;
1516 }
1517}
1518
1519static void EngineAnalysisItemsInit(EngineAnalysisCtx *ea_ctx)
1520{
1521 if (ea_ctx->analyzer_initialized) {
1522 EngineAnalysisItemsReset(ea_ctx);
1523 return;
1524 }
1525
1526 ea_ctx->exposed_item_seen_list[0].bufname = "http_method";
1527 ea_ctx->exposed_item_seen_list[1].bufname = "file_data";
1528 ea_ctx->analyzer_items = SCCalloc(1, sizeof(analyzer_items));
1529 if (!ea_ctx->analyzer_items) {
1530 FatalError("Unable to allocate analysis scratch pad");
1531 }
1532 memset(ea_ctx->analyzer_item_map, -1, sizeof(ea_ctx->analyzer_item_map));
1533
1534 for (size_t i = 0; i < ARRAY_SIZE(analyzer_items); i++) {
1535 ea_ctx->analyzer_items[i] = analyzer_items[i];
1536 DetectEngineAnalyzerItems *analyzer_item = &ea_ctx->analyzer_items[i];
1537
1538 int item_id = DetectBufferTypeGetByName(analyzer_item->item_name);
1539 DEBUG_VALIDATE_BUG_ON(item_id < 0 || item_id > UINT16_MAX);
1540 analyzer_item->item_id = (uint16_t)item_id;
1541 if (analyzer_item->item_id == -1) {
1542 /* Mismatch between the analyzer_items array and what's supported */
1543 FatalError("unable to initialize engine-analysis table: detect buffer \"%s\" not "
1544 "recognized.",
1545 analyzer_item->item_name);
1546 }
1547 analyzer_item->item_seen = false;
1548
1549 if (analyzer_item->export_item_seen) {
1550 for (size_t k = 0; k < ARRAY_SIZE(ea_ctx->exposed_item_seen_list); k++) {
1551 if (0 ==
1552 strcmp(ea_ctx->exposed_item_seen_list[k].bufname, analyzer_item->item_name))
1553 ea_ctx->exposed_item_seen_list[k].item_seen_ptr = &analyzer_item->item_seen;
1554 }
1555 }
1556 ea_ctx->analyzer_item_map[analyzer_item->item_id] = (int16_t)i;
1557 }
1558
1559 ea_ctx->analyzer_initialized = true;
1560}
1561
1562/**
1563 * \brief Prints analysis of loaded rules.
1564 *
1565 * Warns if potential rule issues are detected. For example,
1566 * warns if a rule uses a construct that may perform poorly,
1567 * e.g. pcre without content or with http_method content only;
1568 * warns if a rule uses a construct that may not be consistent with intent,
1569 * e.g. client side ports only, http and content without any http_* modifiers, etc.
1570 *
1571 * \param s Pointer to the signature.
1572 */
1574 const Signature *s, const char *line)
1575{
1576 uint32_t rule_bidirectional = 0;
1577 uint32_t rule_pcre = 0;
1578 uint32_t rule_pcre_http = 0;
1579 uint32_t rule_content = 0;
1580 uint32_t rule_flow = 0;
1581 uint32_t rule_flags = 0;
1582 uint32_t rule_flow_toserver = 0;
1583 uint32_t rule_flow_toclient = 0;
1584 uint32_t rule_flow_nostream = 0;
1585 uint32_t rule_ipv4_only = 0;
1586 uint32_t rule_ipv6_only = 0;
1587 uint32_t rule_flowbits = 0;
1588 uint32_t rule_flowint = 0;
1589 uint32_t rule_content_http = 0;
1590 uint32_t rule_content_offset_depth = 0;
1591 int32_t list_id = 0;
1592 uint32_t rule_warning = 0;
1593 uint32_t stream_buf = 0;
1594 uint32_t packet_buf = 0;
1595 uint32_t file_store = 0;
1596 uint32_t warn_pcre_no_content = 0;
1597 uint32_t warn_pcre_http_content = 0;
1598 uint32_t warn_pcre_http = 0;
1599 uint32_t warn_content_http_content = 0;
1600 uint32_t warn_content_http = 0;
1601 uint32_t warn_tcp_no_flow = 0;
1602 uint32_t warn_client_ports = 0;
1603 uint32_t warn_direction = 0;
1604 uint32_t warn_method_toclient = 0;
1605 uint32_t warn_method_serverbody = 0;
1606 uint32_t warn_pcre_method = 0;
1607 uint32_t warn_encoding_norm_http_buf = 0;
1608 uint32_t warn_file_store_not_present = 0;
1609 uint32_t warn_offset_depth_pkt_stream = 0;
1610 uint32_t warn_offset_depth_alproto = 0;
1611 uint32_t warn_non_alproto_fp_for_alproto_sig = 0;
1612 uint32_t warn_no_direction = 0;
1613 uint32_t warn_both_direction = 0;
1614
1615 EngineAnalysisItemsInit(de_ctx->ea);
1616
1617 bool *http_method_item_seen_ptr = de_ctx->ea->exposed_item_seen_list[0].item_seen_ptr;
1618 bool *http_server_body_item_seen_ptr = de_ctx->ea->exposed_item_seen_list[1].item_seen_ptr;
1619
1621 rule_bidirectional = 1;
1622 }
1623
1624 if (s->flags & SIG_FLAG_REQUIRE_PACKET) {
1625 packet_buf += 1;
1626 }
1627 if (s->flags & SIG_FLAG_FILESTORE) {
1628 file_store += 1;
1629 }
1630 if (s->flags & SIG_FLAG_REQUIRE_STREAM) {
1631 stream_buf += 1;
1632 }
1633
1634 if (s->proto.flags & DETECT_PROTO_IPV4) {
1635 rule_ipv4_only += 1;
1636 }
1637 if (s->proto.flags & DETECT_PROTO_IPV6) {
1638 rule_ipv6_only += 1;
1639 }
1640
1641 for (list_id = 0; list_id < DETECT_SM_LIST_MAX; list_id++) {
1642 SigMatch *sm = NULL;
1643 for (sm = s->init_data->smlists[list_id]; sm != NULL; sm = sm->next) {
1644 int16_t item_slot = de_ctx->ea->analyzer_item_map[list_id];
1645 if (sm->type == DETECT_PCRE) {
1646 if (item_slot == -1) {
1647 rule_pcre++;
1648 continue;
1649 }
1650
1651 rule_pcre_http++;
1652 de_ctx->ea->analyzer_items[item_slot].item_seen = true;
1653 } else if (sm->type == DETECT_CONTENT) {
1654 if (item_slot == -1) {
1655 rule_content++;
1656 if (list_id == DETECT_SM_LIST_PMATCH) {
1659 rule_content_offset_depth++;
1660 }
1661 }
1662 continue;
1663 }
1664
1665 rule_content_http++;
1666 de_ctx->ea->analyzer_items[item_slot].item_seen = true;
1667
1668 if (de_ctx->ea->analyzer_items[item_slot].check_encoding_match) {
1670 if (cd != NULL &&
1671 PerCentEncodingMatch(de_ctx->ea, cd->content, cd->content_len) > 0) {
1672 warn_encoding_norm_http_buf += 1;
1673 }
1674 }
1675 }
1676 else if (sm->type == DETECT_FLOW) {
1677 rule_flow += 1;
1678 if ((s->flags & SIG_FLAG_TOSERVER) && !(s->flags & SIG_FLAG_TOCLIENT)) {
1679 rule_flow_toserver = 1;
1680 }
1681 else if ((s->flags & SIG_FLAG_TOCLIENT) && !(s->flags & SIG_FLAG_TOSERVER)) {
1682 rule_flow_toclient = 1;
1683 }
1684 DetectFlowData *fd = (DetectFlowData *)sm->ctx;
1685 if (fd != NULL) {
1687 rule_flow_nostream = 1;
1688 }
1689 }
1690 else if (sm->type == DETECT_FLOWBITS) {
1691 if (list_id == DETECT_SM_LIST_MATCH) {
1692 rule_flowbits += 1;
1693 }
1694 }
1695 else if (sm->type == DETECT_FLOWINT) {
1696 if (list_id == DETECT_SM_LIST_MATCH) {
1697 rule_flowint += 1;
1698 }
1699 }
1700 else if (sm->type == DETECT_FLAGS) {
1701 DetectFlagsData *fd = (DetectFlagsData *)sm->ctx;
1702 if (fd != NULL) {
1703 rule_flags = 1;
1704 }
1705 }
1706 } /* for (sm = s->init_data->smlists[list_id]; sm != NULL; sm = sm->next) */
1707
1708 } /* for ( ; list_id < DETECT_SM_LIST_MAX; list_id++) */
1709
1710 if (file_store && !RequiresFeature("output::file-store")) {
1711 rule_warning += 1;
1712 warn_file_store_not_present = 1;
1713 }
1714
1715 if (rule_pcre > 0 && rule_content == 0 && rule_content_http == 0) {
1716 rule_warning += 1;
1717 warn_pcre_no_content = 1;
1718 }
1719
1720 if (rule_content_http > 0 && rule_pcre > 0 && rule_pcre_http == 0) {
1721 rule_warning += 1;
1722 warn_pcre_http_content = 1;
1723 } else if (s->alproto == ALPROTO_HTTP1 && rule_pcre > 0 && rule_pcre_http == 0) {
1724 rule_warning += 1;
1725 warn_pcre_http = 1;
1726 }
1727
1728 if (rule_content > 0 && rule_content_http > 0) {
1729 rule_warning += 1;
1730 warn_content_http_content = 1;
1731 }
1732 if (s->alproto == ALPROTO_HTTP1 && rule_content > 0 && rule_content_http == 0) {
1733 rule_warning += 1;
1734 warn_content_http = 1;
1735 }
1736 if (rule_content == 1) {
1737 //todo: warning if content is weak, separate warning for pcre + weak content
1738 }
1739 if (rule_flow == 0 && rule_flags == 0 && !(s->proto.flags & DETECT_PROTO_ANY) &&
1740 DetectProtoContainsProto(&s->proto, IPPROTO_TCP) &&
1741 (rule_content || rule_content_http || rule_pcre || rule_pcre_http || rule_flowbits ||
1742 rule_flowint)) {
1743 rule_warning += 1;
1744 warn_tcp_no_flow = 1;
1745 }
1746 if (rule_flow && !rule_bidirectional && (rule_flow_toserver || rule_flow_toclient)
1747 && !((s->flags & SIG_FLAG_SP_ANY) && (s->flags & SIG_FLAG_DP_ANY))) {
1748 if (((s->flags & SIG_FLAG_TOSERVER) && !(s->flags & SIG_FLAG_SP_ANY) && (s->flags & SIG_FLAG_DP_ANY))
1749 || ((s->flags & SIG_FLAG_TOCLIENT) && !(s->flags & SIG_FLAG_DP_ANY) && (s->flags & SIG_FLAG_SP_ANY))) {
1750 rule_warning += 1;
1751 warn_client_ports = 1;
1752 }
1753 }
1754 if (rule_flow && rule_bidirectional && (rule_flow_toserver || rule_flow_toclient)) {
1755 rule_warning += 1;
1756 warn_direction = 1;
1757 }
1758
1759 if (*http_method_item_seen_ptr) {
1760 if (rule_flow && rule_flow_toclient) {
1761 rule_warning += 1;
1762 warn_method_toclient = 1;
1763 }
1764 if (*http_server_body_item_seen_ptr) {
1765 rule_warning += 1;
1766 warn_method_serverbody = 1;
1767 }
1768 if (rule_content == 0 && rule_content_http == 0 && (rule_pcre > 0 || rule_pcre_http > 0)) {
1769 rule_warning += 1;
1770 warn_pcre_method = 1;
1771 }
1772 }
1773 if (rule_content_offset_depth > 0 && stream_buf && packet_buf) {
1774 rule_warning += 1;
1775 warn_offset_depth_pkt_stream = 1;
1776 }
1777 if (rule_content_offset_depth > 0 && !stream_buf && packet_buf && s->alproto != ALPROTO_UNKNOWN) {
1778 rule_warning += 1;
1779 warn_offset_depth_alproto = 1;
1780 }
1781 if (s->init_data->mpm_sm != NULL && s->alproto == ALPROTO_HTTP1 &&
1783 rule_warning += 1;
1784 warn_non_alproto_fp_for_alproto_sig = 1;
1785 }
1786
1787 if ((s->flags & (SIG_FLAG_TOSERVER|SIG_FLAG_TOCLIENT)) == 0) {
1788 warn_no_direction += 1;
1789 rule_warning += 1;
1790 }
1791
1792 /* No warning about direction for ICMP protos */
1793 if (!(DetectProtoContainsProto(&s->proto, IPPROTO_ICMPV6) && DetectProtoContainsProto(&s->proto, IPPROTO_ICMP))) {
1795 warn_both_direction += 1;
1796 rule_warning += 1;
1797 }
1798 }
1799
1800 if (!rule_warnings_only || (rule_warnings_only && rule_warning > 0)) {
1801 FILE *fp = de_ctx->ea->rule_engine_analysis_fp;
1802 fprintf(fp, "== Sid: %u ==\n", s->id);
1803 fprintf(fp, "%s\n", line);
1804
1805 switch (s->type) {
1806 case SIG_TYPE_NOT_SET:
1807 break;
1808 case SIG_TYPE_IPONLY:
1809 fprintf(fp, " Rule is ip only.\n");
1810 break;
1812 fprintf(fp, " Rule is like ip only.\n");
1813 break;
1814 case SIG_TYPE_PDONLY:
1815 fprintf(fp, " Rule is PD only.\n");
1816 break;
1817 case SIG_TYPE_DEONLY:
1818 fprintf(fp, " Rule is DE only.\n");
1819 break;
1820 case SIG_TYPE_PKT:
1821 fprintf(fp, " Rule is packet inspecting.\n");
1822 break;
1824 fprintf(fp, " Rule is packet and stream inspecting.\n");
1825 break;
1826 case SIG_TYPE_STREAM:
1827 fprintf(fp, " Rule is stream inspecting.\n");
1828 break;
1829 case SIG_TYPE_APPLAYER:
1830 fprintf(fp, " Rule is app-layer inspecting.\n");
1831 break;
1832 case SIG_TYPE_APP_TX:
1833 fprintf(fp, " Rule is App-layer TX inspecting.\n");
1834 break;
1835 case SIG_TYPE_MAX:
1836 break;
1837 }
1838 if (rule_ipv6_only)
1839 fprintf(fp, " Rule is IPv6 only.\n");
1840 if (rule_ipv4_only)
1841 fprintf(fp, " Rule is IPv4 only.\n");
1842 if (packet_buf)
1843 fprintf(fp, " Rule matches on packets.\n");
1844 if (!rule_flow_nostream && stream_buf &&
1845 (rule_flow || rule_flowbits || rule_flowint || rule_content || rule_pcre)) {
1846 fprintf(fp, " Rule matches on reassembled stream.\n");
1847 }
1848 for(size_t i = 0; i < ARRAY_SIZE(analyzer_items); i++) {
1850 if (ai->item_seen) {
1851 fprintf(fp, " Rule matches on %s buffer.\n", ai->display_name);
1852 }
1853 }
1854 if (s->alproto != ALPROTO_UNKNOWN) {
1855 fprintf(fp, " App layer protocol is %s.\n", AppProtoToString(s->alproto));
1856 }
1857 if (rule_content || rule_content_http || rule_pcre || rule_pcre_http) {
1858 fprintf(fp,
1859 " Rule contains %u content options, %u http content options, %u pcre "
1860 "options, and %u pcre options with http modifiers.\n",
1861 rule_content, rule_content_http, rule_pcre, rule_pcre_http);
1862 }
1863
1864 /* print fast pattern info */
1865 if (s->init_data->prefilter_sm) {
1866 fprintf(fp, " Prefilter on: %s.\n",
1868 } else {
1869 EngineAnalysisRulesPrintFP(de_ctx, s);
1870 }
1871
1872 /* this is where the warnings start */
1873 if (warn_pcre_no_content /*rule_pcre > 0 && rule_content == 0 && rule_content_http == 0*/) {
1874 fprintf(fp, " Warning: Rule uses pcre without a content option present.\n"
1875 " -Consider adding a content to improve performance of this "
1876 "rule.\n");
1877 }
1878 if (warn_pcre_http_content /*rule_content_http > 0 && rule_pcre > 0 && rule_pcre_http == 0*/) {
1879 fprintf(fp, " Warning: Rule uses content options with http_* and pcre options "
1880 "without http modifiers.\n"
1881 " -Consider adding http pcre modifier.\n");
1882 }
1883 else if (warn_pcre_http /*s->alproto == ALPROTO_HTTP1 && rule_pcre > 0 && rule_pcre_http == 0*/) {
1884 fprintf(fp, " Warning: Rule app layer protocol is http, but pcre options do not "
1885 "have http modifiers.\n"
1886 " -Consider adding http pcre modifiers.\n");
1887 }
1888 if (warn_content_http_content /*rule_content > 0 && rule_content_http > 0*/) {
1889 fprintf(fp,
1890 " Warning: Rule contains content with http_* and content without http_*.\n"
1891 " -Consider adding http content modifiers.\n");
1892 }
1893 if (warn_content_http /*s->alproto == ALPROTO_HTTP1 && rule_content > 0 && rule_content_http == 0*/) {
1894 fprintf(fp, " Warning: Rule app layer protocol is http, but content options do not "
1895 "have http_* modifiers.\n"
1896 " -Consider adding http content modifiers.\n");
1897 }
1898 if (rule_content == 1) {
1899 //todo: warning if content is weak, separate warning for pcre + weak content
1900 }
1901 if (warn_encoding_norm_http_buf) {
1902 fprintf(fp, " Warning: Rule may contain percent encoded content for a normalized "
1903 "http buffer match.\n");
1904 }
1905 if (warn_tcp_no_flow /*rule_flow == 0 && rule_flags == 0
1906 && !(s->proto.flags & DETECT_PROTO_ANY) && DetectProtoContainsProto(&s->proto, IPPROTO_TCP)*/) {
1907 fprintf(fp, " Warning: TCP rule without a flow or flags option.\n"
1908 " -Consider adding flow or flags to improve performance of "
1909 "this rule.\n");
1910 }
1911 if (warn_client_ports /*rule_flow && !rule_bidirectional && (rule_flow_toserver || rule_flow_toclient)
1912 && !((s->flags & SIG_FLAG_SP_ANY) && (s->flags & SIG_FLAG_DP_ANY)))
1913 if (((s->flags & SIG_FLAG_TOSERVER) && !(s->flags & SIG_FLAG_SP_ANY) && (s->flags & SIG_FLAG_DP_ANY))
1914 || ((s->flags & SIG_FLAG_TOCLIENT) && !(s->flags & SIG_FLAG_DP_ANY) && (s->flags & SIG_FLAG_SP_ANY))*/) {
1915 fprintf(fp,
1916 " Warning: Rule contains ports or port variables only on the client side.\n"
1917 " -Flow direction possibly inconsistent with rule.\n");
1918 }
1919 if (warn_direction /*rule_flow && rule_bidirectional && (rule_flow_toserver || rule_flow_toclient)*/) {
1920 fprintf(fp, " Warning: Rule is bidirectional and has a flow option with a specific "
1921 "direction.\n");
1922 }
1923 if (warn_method_toclient /*http_method_buf && rule_flow && rule_flow_toclient*/) {
1924 fprintf(fp, " Warning: Rule uses content or pcre for http_method with "
1925 "flow:to_client or from_server\n");
1926 }
1927 if (warn_method_serverbody /*http_method_buf && http_server_body_buf*/) {
1928 fprintf(fp, " Warning: Rule uses content or pcre for http_method with content or "
1929 "pcre for http_server_body.\n");
1930 }
1931 if (warn_pcre_method /*http_method_buf && rule_content == 0 && rule_content_http == 0
1932 && (rule_pcre > 0 || rule_pcre_http > 0)*/) {
1933 fprintf(fp, " Warning: Rule uses pcre with only a http_method content; possible "
1934 "performance issue.\n");
1935 }
1936 if (warn_offset_depth_pkt_stream) {
1937 fprintf(fp, " Warning: Rule has depth"
1938 "/offset with raw content keywords. Please note the "
1939 "offset/depth will be checked against both packet "
1940 "payloads and stream. If you meant to have the offset/"
1941 "depth checked against just the payload, you can update "
1942 "the signature as \"alert tcp-pkt...\"\n");
1943 }
1944 if (warn_offset_depth_alproto) {
1945 fprintf(fp,
1946 " Warning: Rule has "
1947 "offset/depth set along with a match on a specific "
1948 "app layer protocol - %d. This can lead to FNs if we "
1949 "have a offset/depth content match on a packet payload "
1950 "before we can detect the app layer protocol for the "
1951 "flow.\n",
1952 s->alproto);
1953 }
1954 if (warn_non_alproto_fp_for_alproto_sig) {
1955 fprintf(fp, " Warning: Rule app layer "
1956 "protocol is http, but the fast_pattern is set on the raw "
1957 "stream. Consider adding fast_pattern over a http "
1958 "buffer for increased performance.");
1959 }
1960 if (warn_no_direction) {
1961 fprintf(fp, " Warning: Rule has no direction indicator.\n");
1962 }
1963 if (warn_both_direction) {
1964 fprintf(fp, " Warning: Rule is inspecting both the request and the response.\n");
1965 }
1966 if (warn_file_store_not_present) {
1967 fprintf(fp, " Warning: Rule requires file-store but the output file-store is not "
1968 "enabled.\n");
1969 }
1970 if (rule_warning == 0) {
1971 fprintf(fp, " No warnings for this rule.\n");
1972 }
1973 fprintf(fp, "\n");
1974 }
1975}
1976
1977#include "app-layer-parser.h"
1978
1979static void FirewallAddRulesForState(const DetectEngineCtx *de_ctx, const AppProto a,
1980 const uint8_t state, const uint8_t direction, RuleAnalyzer *ctx)
1981{
1982 uint32_t accept_rules = 0;
1983 SCJbSetString(ctx->js, "policy", "drop:flow");
1984 SCJbOpenArray(ctx->js, "rules");
1985 for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
1986 if ((s->flags & SIG_FLAG_FIREWALL) == 0)
1987 break;
1988 if (s->type != SIG_TYPE_APP_TX)
1989 continue;
1990 if (s->alproto != a)
1991 continue;
1992
1993 if (direction == STREAM_TOSERVER) {
1994 if (s->flags & SIG_FLAG_TOCLIENT) {
1995 continue;
1996 }
1997 } else {
1998 if (s->flags & SIG_FLAG_TOSERVER) {
1999 continue;
2000 }
2001 }
2002
2003 if (s->app_progress_hook == state) {
2004 SCJbAppendString(ctx->js, s->sig_str);
2005 accept_rules += ((s->action & ACTION_ACCEPT) != 0);
2006 }
2007 }
2008 SCJbClose(ctx->js);
2009
2010 if (accept_rules == 0) {
2011 AnalyzerWarning(ctx, (char *)"no accept rules for state, default policy will be applied");
2012 }
2013}
2014
2016{
2017 RuleAnalyzer ctx = { NULL, NULL, NULL };
2018 ctx.js = SCJbNewObject();
2019 if (ctx.js == NULL)
2020 return -1;
2021
2022 SCJbOpenObject(ctx.js, "tables");
2023 SCJbOpenObject(ctx.js, "packet:filter");
2024 SCJbSetString(ctx.js, "policy", "drop:packet");
2025 SCJbOpenArray(ctx.js, "rules");
2026 uint32_t accept_rules = 0;
2027 uint32_t last_sid = 0;
2028 for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
2029 if ((s->flags & SIG_FLAG_FIREWALL) == 0)
2030 break;
2031 if (s->type != SIG_TYPE_PKT)
2032 continue;
2033 /* don't double list <> sigs */
2034 if (last_sid == s->id)
2035 continue;
2036 last_sid = s->id;
2037 SCJbAppendString(ctx.js, s->sig_str);
2038 accept_rules += ((s->action & ACTION_ACCEPT) != 0);
2039 }
2040 SCJbClose(ctx.js);
2041 if (accept_rules == 0) {
2042 AnalyzerWarning(&ctx,
2043 (char *)"no accept rules for \'packet:filter\', default policy will be applied");
2044 }
2045 if (ctx.js_warnings) {
2046 SCJbClose(ctx.js_warnings);
2047 SCJbSetObject(ctx.js, "warnings", ctx.js_warnings);
2048 SCJbFree(ctx.js_warnings);
2049 ctx.js_warnings = NULL;
2050 }
2051 SCJbClose(ctx.js); // packet_filter
2052
2053 for (AppProto a = 0; a < g_alproto_max; a++) {
2054 if (!AppProtoIsValid(a))
2055 continue;
2056
2057 // HACK not all protocols have named states yet
2058 const char *hack = AppLayerParserGetStateNameById(IPPROTO_TCP, a, 0, STREAM_TOSERVER);
2059 if (!hack)
2060 continue;
2061
2062 SCJbOpenObject(ctx.js, AppProtoToString(a));
2063 const uint8_t complete_state_ts =
2064 (const uint8_t)AppLayerParserGetStateProgressCompletionStatus(a, STREAM_TOSERVER);
2065 for (uint8_t state = 0; state < complete_state_ts; state++) {
2066 const char *name =
2067 AppLayerParserGetStateNameById(IPPROTO_TCP, a, state, STREAM_TOSERVER);
2068 char table_name[128];
2069 snprintf(table_name, sizeof(table_name), "app:%s:%s", AppProtoToString(a), name);
2070 SCJbOpenObject(ctx.js, table_name);
2071 FirewallAddRulesForState(de_ctx, a, state, STREAM_TOSERVER, &ctx);
2072 if (ctx.js_warnings) {
2073 SCJbClose(ctx.js_warnings);
2074 SCJbSetObject(ctx.js, "warnings", ctx.js_warnings);
2075 SCJbFree(ctx.js_warnings);
2076 ctx.js_warnings = NULL;
2077 }
2078 SCJbClose(ctx.js);
2079 }
2080 const uint8_t complete_state_tc =
2081 (const uint8_t)AppLayerParserGetStateProgressCompletionStatus(a, STREAM_TOCLIENT);
2082 for (uint8_t state = 0; state < complete_state_tc; state++) {
2083 const char *name =
2084 AppLayerParserGetStateNameById(IPPROTO_TCP, a, state, STREAM_TOCLIENT);
2085 char table_name[128];
2086 snprintf(table_name, sizeof(table_name), "app:%s:%s", AppProtoToString(a), name);
2087 SCJbOpenObject(ctx.js, table_name);
2088 FirewallAddRulesForState(de_ctx, a, state, STREAM_TOCLIENT, &ctx);
2089 if (ctx.js_warnings) {
2090 SCJbClose(ctx.js_warnings);
2091 SCJbSetObject(ctx.js, "warnings", ctx.js_warnings);
2092 SCJbFree(ctx.js_warnings);
2093 ctx.js_warnings = NULL;
2094 }
2095 SCJbClose(ctx.js);
2096 }
2097 SCJbClose(ctx.js); // app layer
2098 }
2099 SCJbOpenObject(ctx.js, "packet:td");
2100 SCJbSetString(ctx.js, "policy", "accept:hook");
2101 last_sid = 0;
2102 SCJbOpenArray(ctx.js, "rules");
2103 for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
2104 if ((s->flags & SIG_FLAG_FIREWALL) != 0)
2105 continue;
2106 if (s->type == SIG_TYPE_APP_TX)
2107 continue;
2108 if (last_sid == s->id)
2109 continue;
2110 last_sid = s->id;
2111 SCJbAppendString(ctx.js, s->sig_str);
2112 }
2113 SCJbClose(ctx.js); // rules
2114 SCJbClose(ctx.js); // packet:td
2115 SCJbOpenObject(ctx.js, "app:td");
2116 SCJbSetString(ctx.js, "policy", "accept:hook");
2117 last_sid = 0;
2118 SCJbOpenArray(ctx.js, "rules");
2119 for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
2120 if ((s->flags & SIG_FLAG_FIREWALL) != 0)
2121 continue;
2122 if (s->type != SIG_TYPE_APP_TX)
2123 continue;
2124 if (last_sid == s->id)
2125 continue;
2126 last_sid = s->id;
2127 SCJbAppendString(ctx.js, s->sig_str);
2128 }
2129 SCJbClose(ctx.js); // rules
2130 SCJbClose(ctx.js); // app:td
2131 SCJbClose(ctx.js); // tables
2132
2133 SCJbOpenObject(ctx.js, "lists");
2134 SCJbOpenObject(ctx.js, "firewall");
2135 last_sid = 0;
2136 SCJbOpenArray(ctx.js, "rules");
2137 for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
2138 if ((s->flags & SIG_FLAG_FIREWALL) == 0)
2139 continue;
2140 if (last_sid == s->id)
2141 continue;
2142 last_sid = s->id;
2143 SCJbAppendString(ctx.js, s->sig_str);
2144 }
2145 SCJbClose(ctx.js); // rules
2146 SCJbClose(ctx.js); // firewall
2147
2148 SCJbOpenObject(ctx.js, "td");
2149 last_sid = 0;
2150 SCJbOpenArray(ctx.js, "rules");
2151 for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
2152 if ((s->flags & SIG_FLAG_FIREWALL) != 0)
2153 continue;
2154 if (last_sid == s->id)
2155 continue;
2156 last_sid = s->id;
2157 SCJbAppendString(ctx.js, s->sig_str);
2158 }
2159 SCJbClose(ctx.js); // rules
2160 SCJbClose(ctx.js); // td
2161
2162 SCJbOpenObject(ctx.js, "all");
2163 last_sid = 0;
2164 SCJbOpenArray(ctx.js, "rules");
2165 for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
2166 if (last_sid == s->id)
2167 continue;
2168 last_sid = s->id;
2169 SCJbAppendString(ctx.js, s->sig_str);
2170 }
2171 SCJbClose(ctx.js); // rules
2172 SCJbClose(ctx.js); // all
2173
2174 SCJbClose(ctx.js); // lists
2175
2176 SCJbClose(ctx.js); // top level object
2177
2178 const char *filename = "firewall.json";
2179 const char *log_dir = SCConfigGetLogDirectory();
2180 char json_path[PATH_MAX] = "";
2181 snprintf(json_path, sizeof(json_path), "%s/%s", log_dir, filename);
2182
2184 FILE *fp = fopen(json_path, "w");
2185 if (fp != NULL) {
2186 fwrite(SCJbPtr(ctx.js), SCJbLen(ctx.js), 1, fp);
2187 fprintf(fp, "\n");
2188 fclose(fp);
2189 }
2191 SCJbFree(ctx.js);
2192 return 0;
2193}
#define ACTION_REJECT
#define ACTION_PASS
#define ACTION_ACCEPT
#define ACTION_REJECT_BOTH
#define ACTION_REJECT_DST
ActionScope
@ ACTION_SCOPE_HOOK
@ ACTION_SCOPE_PACKET
@ ACTION_SCOPE_TX
@ ACTION_SCOPE_FLOW
@ ACTION_SCOPE_AUTO
#define ACTION_CONFIG
#define ACTION_ALERT
#define ACTION_DROP
uint8_t len
int AppLayerParserGetStateProgressCompletionStatus(AppProto alproto, uint8_t direction)
const char * AppLayerParserGetStateNameById(uint8_t ipproto, AppProto alproto, const int id, const uint8_t direction)
AppProto g_alproto_max
const char * AppProtoToString(AppProto alproto)
Maps the ALPROTO_*, to its string equivalent.
uint16_t AppProto
@ ALPROTO_UNKNOWN
@ ALPROTO_HTTP1
SCConfNode * SCConfGetNode(const char *name)
Get a SCConfNode by name.
Definition conf.c:181
int SCConfValIsTrue(const char *val)
Check if a value is true.
Definition conf.c:551
int SCConfGetBool(const char *name, int *val)
Retrieve a configuration value as a boolean.
Definition conf.c:497
const char * SCConfNodeLookupChildValue(const SCConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition conf.c:824
#define DETECT_BYTEJUMP_ALIGN
#define DETECT_BYTEJUMP_DCE
#define DETECT_BYTEJUMP_LITTLE
#define DETECT_BYTEJUMP_BASE_DEC
#define DETECT_BYTEJUMP_BASE_UNSET
#define DETECT_BYTEJUMP_END
#define DETECT_BYTEJUMP_RELATIVE
#define DETECT_BYTEJUMP_BIG
#define DETECT_BYTEJUMP_STRING
#define DETECT_BYTEJUMP_OFFSET_BE
#define DETECT_BYTEJUMP_BEGIN
#define DETECT_BYTEJUMP_BASE_OCT
#define DETECT_BYTEJUMP_BASE_HEX
#define DETECT_BYTETEST_DCE
#define DETECT_BYTETEST_RELATIVE
#define DETECT_BYTETEST_BIG
#define DETECT_BYTETEST_BASE_OCT
#define DETECT_BYTETEST_STRING
#define DETECT_BYTETEST_BASE_HEX
#define DETECT_BYTETEST_LITTLE
#define DETECT_BYTETEST_BASE_DEC
#define DETECT_BYTETEST_BASE_UNSET
void DetectContentPatternPrettyPrint(const DetectContentData *cd, char *str, size_t str_len)
#define DETECT_CONTENT_STARTS_WITH
#define DETECT_CONTENT_FAST_PATTERN_CHOP
#define DETECT_CONTENT_DEPTH
#define DETECT_CONTENT_NO_DOUBLE_INSPECTION_REQUIRED
#define DETECT_CONTENT_RELATIVE_NEXT
#define DETECT_CONTENT_DISTANCE2OFFSET
#define DETECT_CONTENT_FAST_PATTERN_ONLY
#define DETECT_CONTENT_WITHIN
#define DETECT_CONTENT_FAST_PATTERN
#define DETECT_CONTENT_ENDS_WITH
#define DETECT_CONTENT_WITHIN2DEPTH
#define DETECT_CONTENT_DISTANCE
#define DETECT_CONTENT_OFFSET
#define DETECT_CONTENT_NEGATED
#define DETECT_CONTENT_MPM
#define DETECT_CONTENT_NOCASE
int FirewallAnalyzer(const DetectEngineCtx *de_ctx)
void EngineAnalysisFP(const DetectEngineCtx *de_ctx, const Signature *s, const char *line)
struct EngineAnalysisCtx_ EngineAnalysisCtx
void DumpPatterns(DetectEngineCtx *de_ctx)
void EngineAnalysisRulesFailure(const DetectEngineCtx *de_ctx, const char *line, const char *file, int lineno)
#define CHECK(pat)
#define DETECT_PERCENT_ENCODING_REGEX
const DetectEngineAnalyzerItems analyzer_items[]
void EngineAnalysisRules2(const DetectEngineCtx *de_ctx, const Signature *s)
void SetupEngineAnalysis(DetectEngineCtx *de_ctx, bool *fp_analysis, bool *rule_analysis)
void CleanupEngineAnalysis(DetectEngineCtx *de_ctx)
SCMutex g_rules_analyzer_write_m
void EngineAnalysisRules(const DetectEngineCtx *de_ctx, const Signature *s, const char *line)
Prints analysis of loaded rules.
struct FpPatternStats_ FpPatternStats
int SignatureHasStreamContent(const Signature *s)
check if a signature has patterns that are to be inspected against the stream payload (as opposed to ...
int SignatureHasPacketContent(const Signature *s)
check if a signature has patterns that are to be inspected against a packets payload (as opposed to t...
int DetectProtoContainsProto(const DetectProto *dp, int proto)
see if a DetectProto contains a certain proto
#define DETECT_PROTO_IPV6
#define DETECT_PROTO_IPV4
#define DETECT_PROTO_ANY
@ DETECT_BYTETEST
@ DETECT_BYTEJUMP
@ DETECT_FLOWBITS
DetectUintData_u16 DetectU16Data
DetectUintData_u32 DetectU32Data
DetectUintData_u8 DetectU8Data
const char * DetectEngineBufferTypeGetDescriptionById(const DetectEngineCtx *de_ctx, const int id)
const char * DetectEngineBufferTypeGetNameById(const DetectEngineCtx *de_ctx, const int id)
const struct SignatureProperties signature_properties[SIG_TYPE_MAX]
const char * DetectSigmatchListEnumToString(enum DetectSigmatchListEnum type)
const DetectBufferType * DetectEngineBufferTypeGetById(const DetectEngineCtx *de_ctx, const int id)
int DetectBufferTypeGetByName(const char *name)
#define DETECT_FLOW_FLAG_NOSTREAM
Definition detect-flow.h:33
#define DETECT_FLOWBITS_CMD_ISNOTSET
#define DETECT_FLOWBITS_CMD_ISSET
#define DETECT_FLOWBITS_CMD_TOGGLE
#define DETECT_FLOWBITS_CMD_UNSET
#define DETECT_FLOWBITS_CMD_SET
const char * IpOptsFlagToString(uint16_t flag)
Return human readable value for ipopts flag.
const char * DetectListToHumanString(int list)
int SigMatchListSMBelongsTo(const Signature *s, const SigMatch *key_sm)
SigTableElmt * sigmatch_table
#define DETECT_PCRE_RELATIVE_NEXT
Definition detect-pcre.h:34
#define DETECT_PCRE_NEGATE
Definition detect-pcre.h:35
#define DETECT_PCRE_RAWBYTES
Definition detect-pcre.h:31
#define DETECT_PCRE_CASELESS
Definition detect-pcre.h:32
#define DETECT_PCRE_RELATIVE
Definition detect-pcre.h:29
#define SIG_FLAG_FILESTORE
Definition detect.h:269
#define SIG_MASK_REQUIRE_FLOW
Definition detect.h:312
#define SIG_FLAG_SP_ANY
Definition detect.h:243
#define SIG_FLAG_REQUIRE_PACKET
Definition detect.h:254
#define SIG_FLAG_TOCLIENT
Definition detect.h:272
#define SIG_FLAG_TLSSTORE
Definition detect.h:274
#define SIG_MASK_REQUIRE_ENGINE_EVENT
Definition detect.h:318
#define SIG_FLAG_SRC_ANY
Definition detect.h:241
#define SIG_MASK_REQUIRE_PAYLOAD
Definition detect.h:311
#define SIG_FLAG_INIT_BIDIREC
Definition detect.h:293
@ SIG_TYPE_PKT_STREAM
Definition detect.h:73
@ SIG_TYPE_APP_TX
Definition detect.h:77
@ SIG_TYPE_NOT_SET
Definition detect.h:65
@ SIG_TYPE_DEONLY
Definition detect.h:71
@ SIG_TYPE_IPONLY
Definition detect.h:66
@ SIG_TYPE_MAX
Definition detect.h:79
@ SIG_TYPE_PKT
Definition detect.h:72
@ SIG_TYPE_APPLAYER
Definition detect.h:76
@ SIG_TYPE_PDONLY
Definition detect.h:70
@ SIG_TYPE_STREAM
Definition detect.h:74
@ SIG_TYPE_LIKE_IPONLY
Definition detect.h:67
#define SIG_FLAG_TOSERVER
Definition detect.h:271
#define SIG_MASK_REQUIRE_REAL_PKT
Definition detect.h:316
#define SIG_FLAG_FLUSH
Definition detect.h:259
#define SIG_FLAG_DST_ANY
Definition detect.h:242
#define SIG_MASK_REQUIRE_FLAGS_UNUSUAL
Definition detect.h:314
#define SIG_MASK_REQUIRE_FLAGS_INITDEINIT
Definition detect.h:313
#define SIG_FLAG_SRC_IS_TARGET
Definition detect.h:283
#define SIG_FLAG_PREFILTER
Definition detect.h:278
#define SIG_MASK_REQUIRE_NO_PAYLOAD
Definition detect.h:315
#define SIG_FLAG_FIREWALL
Definition detect.h:246
#define SIG_FLAG_MPM_NEG
Definition detect.h:257
SignaturePropertyFlowAction
Definition detect.h:83
@ SIG_PROP_FLOW_ACTION_FLOW
Definition detect.h:85
@ SIG_PROP_FLOW_ACTION_PACKET
Definition detect.h:84
@ SIG_PROP_FLOW_ACTION_FLOW_IF_STATEFUL
Definition detect.h:86
#define SIG_FLAG_DEST_IS_TARGET
Definition detect.h:285
#define SIG_FLAG_DSIZE
Definition detect.h:248
#define SIG_FLAG_REQUIRE_FLOWVAR
Definition detect.h:267
#define SIG_FLAG_DP_ANY
Definition detect.h:244
@ DETECT_SM_LIST_MATCH
Definition detect.h:117
@ DETECT_SM_LIST_PMATCH
Definition detect.h:119
@ DETECT_SM_LIST_MAX
Definition detect.h:135
@ DETECT_SM_LIST_DYNAMIC_START
Definition detect.h:138
#define SIG_FLAG_APPLAYER
Definition detect.h:249
#define SIG_FLAG_REQUIRE_STREAM
Definition detect.h:255
#define SIG_FLAG_INIT_STATE_MATCH
Definition detect.h:296
#define SIG_FLAG_BYPASS
Definition detect.h:276
bool RequiresFeature(const char *feature_name)
Definition feature.c:126
DetectEngineCtx * de_ctx
struct Thresholds ctx
#define JB_SET_STRING(jb, key, val)
Definition rust.h:26
DetectEngineTransforms transforms
Definition detect.h:462
const DetectEngineTransforms * transforms
Definition detect.h:436
struct DetectEngineAppInspectionEngine_ * next
Definition detect.h:441
struct DetectEngineAppInspectionEngine_::@90 v2
main detection engine ctx
Definition detect.h:932
struct EngineAnalysisCtx_ * ea
Definition detect.h:1129
uint32_t buffer_type_id
Definition detect.h:1081
HashListTable * pattern_hash_table
Definition detect.h:965
Signature * sig_list
Definition detect.h:941
char config_prefix[64]
Definition detect.h:1051
struct DetectEngineFrameInspectionEngine * next
Definition detect.h:521
struct DetectEngineFrameInspectionEngine::@94 v1
const DetectEngineTransforms * transforms
Definition detect.h:518
struct DetectEnginePktInspectionEngine * next
Definition detect.h:494
const DetectEngineTransforms * transforms
Definition detect.h:492
struct DetectEnginePktInspectionEngine::@93 v1
TransformData transforms[DETECT_TRANSFORMS_MAX]
Definition detect.h:392
const struct DetectContentData_ * cd
Definition detect.h:809
struct ExposedItemSeen exposed_item_seen_list[2]
FpPatternStats fp_pattern_stats[DETECT_SM_LIST_MAX]
DetectEngineAnalyzerItems * analyzer_items
SCJsonBuilder * js_notes
SCJsonBuilder * js_warnings
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
a single match condition for a signature
Definition detect.h:356
uint16_t type
Definition detect.h:357
struct SigMatch_ * next
Definition detect.h:360
SigMatchCtx * ctx
Definition detect.h:359
const char * name
Definition detect.h:1459
uint32_t init_flags
Definition detect.h:608
SigMatch * mpm_sm
Definition detect.h:623
uint32_t * rule_state_dependant_sids_array
Definition detect.h:657
bool is_rule_state_dependant
Definition detect.h:656
uint32_t rule_state_flowbits_ids_size
Definition detect.h:661
SigMatch * prefilter_sm
Definition detect.h:625
struct SigMatch_ * smlists[DETECT_SM_LIST_MAX]
Definition detect.h:642
uint32_t * rule_state_flowbits_ids_array
Definition detect.h:660
uint32_t rule_state_dependant_sids_idx
Definition detect.h:659
uint32_t rule_state_dependant_sids_size
Definition detect.h:658
enum SignaturePropertyFlowAction flow_action
Definition detect.h:90
Signature container.
Definition detect.h:668
DetectEngineFrameInspectionEngine * frame_inspect
Definition detect.h:727
uint8_t action
Definition detect.h:683
enum SignatureType type
Definition detect.h:671
uint32_t flags
Definition detect.h:669
SignatureInitData * init_data
Definition detect.h:747
DetectEnginePktInspectionEngine * pkt_inspect
Definition detect.h:726
uint8_t action_scope
Definition detect.h:690
AppProto alproto
Definition detect.h:673
DetectEngineAppInspectionEngine * app_inspect
Definition detect.h:725
uint32_t rev
Definition detect.h:715
DetectProto proto
Definition detect.h:687
char * sig_str
Definition detect.h:745
SignatureMask mask
Definition detect.h:679
uint32_t id
Definition detect.h:713
struct Signature_ * next
Definition detect.h:750
char * msg
Definition detect.h:736
uint8_t app_progress_hook
Definition detect.h:705
SigMatchData * sm_arrays[DETECT_SM_LIST_MAX]
Definition detect.h:731
uint32_t gid
Definition detect.h:714
#define SCNtohs(x)
#define ARRAY_SIZE(arr)
#define ATTR_FMT_PRINTF(x, y)
#define str(s)
#define SCMUTEX_INITIALIZER
#define SCMutex
#define SCMutexUnlock(mut)
#define SCMutexLock(mut)
const char * name
const char * SCConfigGetLogDirectory(void)
Definition util-conf.c:38
#define SCEnter(...)
Definition util-debug.h:277
#define FatalError(...)
Definition util-debug.h:510
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition util-debug.h:225
#define SCLogError(...)
Macro used to log ERROR messages.
Definition util-debug.h:267
#define SCReturn
Definition util-debug.h:279
HashListTableBucket * HashListTableGetListHead(HashListTable *ht)
void HashListTableFree(HashListTable *ht)
#define HashListTableGetListData(hb)
#define HashListTableGetListNext(hb)
#define SCMalloc(sz)
Definition util-mem.h:47
#define SCFree(p)
Definition util-mem.h:61
#define SCCalloc(nm, sz)
Definition util-mem.h:53
#define unlikely(expr)
void PrintRawUriFp(FILE *fp, const uint8_t *buf, uint32_t buflen)
Definition util-print.c:69
struct tm * SCLocalTime(time_t timep, struct tm *result)
Definition util-time.c:267
#define DEBUG_VALIDATE_BUG_ON(exp)
const char * VarNameStoreSetupLookup(const uint32_t id, const enum VarTypes type)
@ VAR_TYPE_FLOW_BIT
Definition util-var.h:36