]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
feature 349 rule analyzer v1
authorEileen Donlon <emdonlo@gmail.com>
Mon, 30 Apr 2012 23:23:23 +0000 (19:23 -0400)
committerVictor Julien <victor@inliniac.net>
Mon, 11 Jun 2012 16:01:12 +0000 (18:01 +0200)
src/Makefile.am
src/detect-engine-analyzer.c [new file with mode: 0644]
src/detect-engine-analyzer.h [new file with mode: 0644]
src/detect.c

index 363f0b95396974e9ecb3210e36dc9b6c179a4a5e..7cc62503ee5dff9b84619f133e91700dfb001e09 100644 (file)
@@ -94,6 +94,7 @@ detect-engine-hsmd.c detect-engine-hsmd.h \
 detect-engine-hscd.c detect-engine-hscd.h \
 detect-engine-state.c detect-engine-state.h \
 detect-engine-file.c detect-engine-file.h \
+detect-engine-analyzer.c detect-engine-analyzer.h \
 detect-parse.c detect-parse.h \
 detect-ack.c detect-ack.h \
 detect-seq.c detect-seq.h \
diff --git a/src/detect-engine-analyzer.c b/src/detect-engine-analyzer.c
new file mode 100644 (file)
index 0000000..9486e87
--- /dev/null
@@ -0,0 +1,513 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eileen Donlon <emdonlo@gmail.com>
+ *
+ * Rule analyzer for the detection engine
+ */
+
+#include "suricata-common.h"
+#include "suricata.h"
+#include "detect.h"
+#include "detect-engine-analyzer.h"
+#include "conf.h"
+#include "detect-content.h"
+#include "detect-flow.h"
+#include "detect-flags.h"
+
+static int rule_warnings_only = 0;
+static FILE *rule_engine_analysis_FD = NULL;
+static pcre *percent_re = NULL;
+static pcre_extra *percent_re_study = NULL;
+
+/**
+ * \brief Sets up the rule analyzer according to the config
+ * \retval 1 if rule analyzer successfully enabled
+ * \retval 0 if not enabled
+ */
+int SetupRuleAnalyzer(char *log_path)
+{
+    ConfNode *conf = ConfGetNode("engine-analysis.rules");
+    int enabled = 0;
+    if (conf != NULL) {
+        ConfGetChildValueBool(conf, "enabled", &enabled);
+        if (enabled) {
+            //rule_engine_analysis_set = 1;
+            ConfGetChildValueBool(conf, "warnings-only", &rule_warnings_only);
+            char *log_dir;
+            if (ConfGet("default-log-dir", &log_dir) != 1)
+                log_dir = DEFAULT_LOG_DIR;
+            snprintf(log_path, 256, "%s/%s", log_dir, "rules_analysis.txt");
+            rule_engine_analysis_FD = fopen(log_path, "w");
+            if (rule_engine_analysis_FD == NULL) {
+                SCLogError(SC_ERR_FOPEN, "ERROR: failed to open %s: %s", log_path, strerror(errno));
+                return 0;
+            }
+
+            struct timeval tval;
+            struct tm *tms;
+            gettimeofday(&tval, NULL);
+            struct tm local_tm;
+            tms = (struct tm *)localtime_r(&tval.tv_sec, &local_tm);
+            fprintf(rule_engine_analysis_FD, "----------------------------------------------"
+                    "---------------------\n");
+            fprintf(rule_engine_analysis_FD, "Date: %" PRId32 "/%" PRId32 "/%04d -- "
+                    "%02d:%02d:%02d\n",
+                    tms->tm_mday, tms->tm_mon + 1, tms->tm_year + 1900, tms->tm_hour,
+                    tms->tm_min, tms->tm_sec);
+            fprintf(rule_engine_analysis_FD, "----------------------------------------------"
+                    "---------------------\n");
+
+            /*compile regex's for rule analysis*/
+            if (PerCentEncodingSetup()== 0) {
+                fprintf(rule_engine_analysis_FD, "Error compiling regex; can't check for percent encoding in normalized http content.\n");
+            }
+        }
+    }
+    else {
+        SCLogInfo("Conf parameter \"engine-analysis.rules\" not found. "
+                                      "Defaulting to not printing the rules analysis report.");
+    }
+    if (!enabled) {
+        SCLogInfo("Engine-Analysis for rules disabled in conf file.");
+        return 0;
+    }
+    else return 1;
+}
+
+void CleanupRuleAnalyzer(char *log_path) {
+    if (rule_engine_analysis_FD != NULL) {
+         SCLogInfo("Engine-Analyis for rules printed to file - %s", log_path);
+        fclose(rule_engine_analysis_FD);
+        rule_engine_analysis_FD = NULL;
+    }
+}
+
+/**
+ * \brief Compiles regex for rule analysis
+ * \retval 1 if successful
+ * \retval 0 if on error
+ */
+int PerCentEncodingSetup ()
+{
+#define DETECT_PERCENT_ENCODING_REGEX "%[0-9|a-f|A-F]{2}"
+    const char *eb = NULL;
+    int eo = 0;
+    int opts = 0;    //PCRE_NEWLINE_ANY??
+
+    percent_re = pcre_compile(DETECT_PERCENT_ENCODING_REGEX, opts, &eb, &eo, NULL);
+    if (percent_re == NULL) {
+        SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
+                   DETECT_PERCENT_ENCODING_REGEX, eo, eb);
+        return 0;
+    }
+
+    percent_re_study = pcre_study(percent_re, 0, &eb);
+    if (eb != NULL) {
+        SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
+        return 0;
+    }
+    return 1;
+}
+
+/**
+ * \brief Checks for % encoding in content.
+ * \param Pointer to content
+ * \retval number of matches if content has % encoding
+ * \retval 0 if it doesn't have % encoding
+ * \retval -1 on error
+ */
+int PerCentEncodingMatch (uint8_t *content, uint8_t content_len)
+{
+#define MAX_ENCODED_CHARS 240
+    int ret = 0;
+    int ov[MAX_ENCODED_CHARS];
+
+    ret = pcre_exec(percent_re, percent_re_study, (char *)content, content_len, 0, 0, ov, MAX_ENCODED_CHARS);
+    if (ret == -1) {
+        return 0;
+    }
+    else if (ret < -1) {
+        SCLogError(SC_ERR_PCRE_MATCH, "Error parsing content - %s; error code is %d", content, ret);
+        return -1;
+    }
+    return ret;
+}
+
+/**
+ * \brief Prints analysis of loaded rules.
+ *
+ *        Warns if potential rule issues are detected. For example,
+ *        warns if a rule uses a construct that may perform poorly,
+ *        e.g. pcre without content or with http_method content only;
+ *        warns if a rule uses a construct that may not be consistent with intent,
+ *        e.g. client side ports only, http and content without any http_* modifiers, etc.
+ *
+ * \param s Pointer to the signature.
+ */
+void EngineAnalysisRules(Signature *s, char *line)
+{
+    uint32_t rule_bidirectional = 0;
+    uint32_t rule_pcre = 0;
+    uint32_t rule_pcre_http = 0;
+    uint32_t rule_content = 0;
+    uint32_t rule_flow = 0;
+    uint32_t rule_flags = 0;
+    uint32_t rule_flow_toserver = 0;
+    uint32_t rule_flow_toclient = 0;
+    uint32_t rule_flow_nostream = 0;
+    uint32_t rule_flowbits = 0;
+    uint32_t rule_flowint = 0;
+    uint32_t rule_flowvar = 0;
+    uint32_t rule_content_http = 0;
+    uint32_t list_id = 0;
+    uint32_t rule_warning = 0;
+    uint32_t raw_http_buf = 0;
+    uint32_t norm_http_buf = 0;
+    uint32_t stream_buf = 0;
+    uint32_t packet_buf = 0;
+    uint32_t http_header_buf = 0;
+    uint32_t http_uri_buf = 0;
+    uint32_t http_method_buf = 0;
+    uint32_t http_cookie_buf = 0;
+    uint32_t http_client_body_buf = 0;
+    uint32_t http_server_body_buf = 0;
+    uint32_t http_stat_code_buf = 0;
+    uint32_t http_stat_msg_buf = 0;
+    uint32_t http_raw_header_buf = 0;
+    uint32_t http_raw_uri_buf = 0;
+    uint32_t warn_pcre_no_content = 0;
+    uint32_t warn_pcre_http_content = 0;
+    uint32_t warn_pcre_http = 0;
+    uint32_t warn_content_http_content = 0;
+    uint32_t warn_content_http = 0;
+    uint32_t warn_tcp_no_flow = 0;
+    uint32_t warn_client_ports = 0;
+    uint32_t warn_direction = 0;
+    uint32_t warn_method_toclient = 0;
+    uint32_t warn_method_serverbody = 0;
+    uint32_t warn_pcre_method = 0;
+    uint32_t warn_encoding_norm_http_buf = 0;
+
+    if (s->init_flags & SIG_FLAG_INIT_BIDIREC) {
+        rule_bidirectional = 1;
+    }
+
+    if (s->flags & SIG_FLAG_REQUIRE_PACKET) {
+        packet_buf += 1;
+    }
+    else {
+        stream_buf += 1;
+    }
+    for (list_id = 0; list_id < DETECT_SM_LIST_MAX; list_id++) {
+
+        SigMatch *sm = NULL;
+        for (sm = s->sm_lists[list_id]; sm != NULL; sm = sm->next) {
+            if (sm->type == DETECT_PCRE) {
+                if (list_id == DETECT_SM_LIST_HCBDMATCH) {
+                    rule_pcre_http += 1;
+                    http_client_body_buf += 1;
+                    raw_http_buf += 1;
+                }
+                else if (list_id == DETECT_SM_LIST_UMATCH) {
+                    rule_pcre_http += 1;
+                    norm_http_buf += 1;
+                    http_uri_buf += 1;
+                }
+                else if (list_id == DETECT_SM_LIST_HHDMATCH) {
+                    rule_pcre_http += 1;
+                    norm_http_buf += 1;
+                    http_header_buf += 1;
+                }
+                else if (list_id == DETECT_SM_LIST_HCDMATCH) {
+                    rule_pcre_http += 1;
+                    norm_http_buf += 1;
+                    http_cookie_buf += 1;
+                }
+                else if (list_id == DETECT_SM_LIST_HSBDMATCH) {
+                    rule_pcre_http += 1;
+                    http_server_body_buf += 1;
+                    raw_http_buf += 1;
+                }
+                else if (list_id == DETECT_SM_LIST_HRHDMATCH) {
+                    rule_pcre_http += 1;
+                    raw_http_buf += 1;
+                    http_raw_header_buf += 1;
+                }
+                else if (list_id == DETECT_SM_LIST_HMDMATCH) {
+                    rule_pcre_http += 1;
+                    raw_http_buf += 1;
+                    http_method_buf += 1;
+                }
+                else if (list_id == DETECT_SM_LIST_HRUDMATCH) {
+                    rule_pcre_http += 1;
+                    raw_http_buf += 1;
+                    http_raw_uri_buf += 1;
+                }
+                else if (list_id == DETECT_SM_LIST_HSMDMATCH) {
+                    rule_pcre_http += 1;
+                    raw_http_buf += 1;
+                    http_stat_msg_buf += 1;
+                }
+                else if (list_id == DETECT_SM_LIST_HSCDMATCH) {
+                    rule_pcre_http += 1;
+                    raw_http_buf += 1;
+                    http_stat_code_buf += 1;
+                }
+                else {
+                    rule_pcre += 1;
+                }
+            }
+            else if (sm->type == DETECT_CONTENT) {
+
+                if (list_id == DETECT_SM_LIST_UMATCH
+                          || list_id == DETECT_SM_LIST_HHDMATCH
+                          || list_id == DETECT_SM_LIST_HCDMATCH) {
+                    rule_content_http += 1;
+                    norm_http_buf += 1;
+                    DetectContentData *cd = (DetectContentData *)sm->ctx;
+                    if (cd != NULL && PerCentEncodingMatch(cd->content, cd->content_len) > 0) {
+                        warn_encoding_norm_http_buf += 1;
+                        rule_warning += 1;
+                    }
+                    if (list_id == DETECT_SM_LIST_UMATCH) {
+                        http_uri_buf += 1;
+                    }
+                    else if (list_id == DETECT_SM_LIST_HHDMATCH) {
+                        http_header_buf += 1;
+                    }
+                    else if (list_id == DETECT_SM_LIST_HCDMATCH) {
+                        http_cookie_buf += 1;
+                    }
+                }
+                else if (list_id == DETECT_SM_LIST_HCBDMATCH) {
+                    rule_content_http += 1;
+                    http_client_body_buf += 1;
+                    raw_http_buf += 1;
+                }
+                else if (list_id == DETECT_SM_LIST_HSBDMATCH) {
+                    rule_content_http += 1;
+                    http_server_body_buf += 1;
+                    raw_http_buf += 1;
+                }
+                else if (list_id == DETECT_SM_LIST_HRHDMATCH) {
+                    rule_content_http += 1;
+                    raw_http_buf += 1;
+                    http_raw_header_buf += 1;
+                }
+                else if (list_id == DETECT_SM_LIST_HRUDMATCH) {
+                    rule_content_http += 1;
+                    raw_http_buf += 1;
+                    http_raw_uri_buf += 1;
+                }
+                else if (list_id == DETECT_SM_LIST_HSMDMATCH) {
+                    rule_content_http += 1;
+                    raw_http_buf += 1;
+                    http_stat_msg_buf += 1;
+                }
+                else if (list_id == DETECT_SM_LIST_HSCDMATCH) {
+                    rule_content_http += 1;
+                    raw_http_buf += 1;
+                    http_stat_code_buf += 1;
+                }
+                else if (list_id == DETECT_SM_LIST_HMDMATCH) {
+                    rule_content_http += 1;
+                    http_method_buf += 1;
+                    raw_http_buf += 1;
+                }
+                else {
+                    rule_content += 1;
+                }
+            }
+            else if (sm->type == DETECT_FLOW) {
+                rule_flow += 1;
+                if (s->flags & SIG_FLAG_TOSERVER && !(s->flags & SIG_FLAG_TOCLIENT)) {
+                    rule_flow_toserver = 1;
+                }
+                else if (s->flags & SIG_FLAG_TOCLIENT && !(s->flags & SIG_FLAG_TOSERVER)) {
+                    rule_flow_toclient = 1;
+                }
+                DetectFlowData *fd = (DetectFlowData *)sm->ctx;
+                if (fd != NULL) {
+                    if (fd->flags & FLOW_PKT_NOSTREAM) rule_flow_nostream = 1;
+                }
+            }
+            else if (sm->type == DETECT_FLOWBITS) {
+                if (list_id == DETECT_SM_LIST_MATCH) {
+                    rule_flowbits += 1;
+                }
+            }
+            else if (sm->type == DETECT_FLOWINT) {
+                if (list_id == DETECT_SM_LIST_MATCH) {
+                    rule_flowint += 1;
+                }
+            }
+            else if (sm->type == DETECT_FLAGS) {
+                DetectFlagsData *fd = (DetectFlagsData *)sm->ctx;
+                if (fd != NULL) {
+                        rule_flags = 1;
+                }
+            }
+        } /* for (sm = s->sm_lists[list_id]; sm != NULL; sm = sm->next) */
+
+    } /* for ( ; list_id < DETECT_SM_LIST_MAX; list_id++) */
+
+
+    if (rule_pcre > 0 && rule_content == 0 && rule_content_http == 0) {
+        rule_warning += 1;
+        warn_pcre_no_content = 1;
+    }
+
+    if (rule_content_http > 0 && rule_pcre > 0 && rule_pcre_http == 0) {
+        rule_warning += 1;
+        warn_pcre_http_content = 1;
+    }
+    else if (s->alproto == ALPROTO_HTTP && rule_pcre > 0 && rule_pcre_http == 0) {
+        rule_warning += 1;
+        warn_pcre_http = 1;
+    }
+
+    if (rule_content > 0 && rule_content_http > 0) {
+        rule_warning += 1;
+        warn_content_http_content = 1;
+    }
+    if (s->alproto == ALPROTO_HTTP && rule_content > 0 && rule_content_http == 0) {
+        rule_warning += 1;
+        warn_content_http = 1;
+    }
+    if (rule_content == 1) {
+         //todo: warning if content is weak, separate warning for pcre + weak content
+    }
+    if (rule_flow == 0 && rule_flags == 0
+        && !(s->proto.flags & DETECT_PROTO_ANY) && DetectProtoContainsProto(&s->proto, IPPROTO_TCP)
+        && (rule_content || rule_content_http || rule_pcre || rule_pcre_http || rule_flowbits)) {
+        rule_warning += 1;
+        warn_tcp_no_flow = 1;
+    }
+    if (rule_flow && !rule_bidirectional && (rule_flow_toserver || rule_flow_toclient)
+                  && !((s->flags & SIG_FLAG_SP_ANY) && (s->flags & SIG_FLAG_DP_ANY))) {
+        if (((s->flags & SIG_FLAG_TOSERVER) && !(s->flags & SIG_FLAG_SP_ANY) && (s->flags & SIG_FLAG_DP_ANY))
+          || ((s->flags & SIG_FLAG_TOCLIENT) && !(s->flags & SIG_FLAG_DP_ANY) && (s->flags & SIG_FLAG_SP_ANY))) {
+            rule_warning += 1;
+            warn_client_ports = 1;
+        }
+    }
+    if (rule_flow && rule_bidirectional && (rule_flow_toserver || rule_flow_toclient)) {
+        rule_warning += 1;
+        warn_direction = 1;
+    }
+    if (http_method_buf) {
+        if (rule_flow && rule_flow_toclient) {
+            rule_warning += 1;
+            warn_method_toclient = 1;
+        }
+        if (http_server_body_buf) {
+            rule_warning += 1;
+            warn_method_serverbody = 1;
+        }
+        if (rule_content == 0 && rule_content_http == 0 && (rule_pcre > 0 || rule_pcre_http > 0)) {
+            rule_warning += 1;
+            warn_pcre_method = 1;
+        }
+    }
+
+    if (!rule_warnings_only || (rule_warnings_only && rule_warning > 0)) {
+        fprintf(rule_engine_analysis_FD, "== Sid: %u ==\n", s->id);
+        fprintf(rule_engine_analysis_FD, "%s\n", line);
+
+        if (s->flags & SIG_FLAG_IPONLY) fprintf(rule_engine_analysis_FD, "   Rule is ip only.\n");
+        if (packet_buf) fprintf(rule_engine_analysis_FD, "    Rule matches on packets.\n");
+        if (!rule_flow_nostream && stream_buf && (rule_flow || rule_flowbits || rule_content || rule_pcre)) {
+            fprintf(rule_engine_analysis_FD, "    Rule matches on reassembled stream.\n");
+        }
+        if (http_uri_buf) fprintf(rule_engine_analysis_FD, "    Rule matches on http uri buffer.\n");
+        if (http_header_buf) fprintf(rule_engine_analysis_FD, "    Rule matches on http header buffer.\n");
+        if (http_cookie_buf) fprintf(rule_engine_analysis_FD, "    Rule matches on http cookie buffer.\n");
+        if (http_raw_uri_buf) fprintf(rule_engine_analysis_FD, "    Rule matches on http raw uri buffer.\n");
+        if (http_raw_header_buf) fprintf(rule_engine_analysis_FD, "    Rule matches on http raw header buffer.\n");
+        if (http_method_buf) fprintf(rule_engine_analysis_FD, "    Rule matches on http method buffer.\n");
+        if (http_server_body_buf) fprintf(rule_engine_analysis_FD, "    Rule matches on http server body buffer.\n");
+        if (http_client_body_buf) fprintf(rule_engine_analysis_FD, "    Rule matches on http client body buffer.\n");
+        if (http_stat_msg_buf) fprintf(rule_engine_analysis_FD, "    Rule matches on http stat msg buffer.\n");
+        if (http_stat_code_buf) fprintf(rule_engine_analysis_FD, "    Rule matches on http stat code buffer.\n");
+        if (s->alproto != ALPROTO_UNKNOWN) {
+            fprintf(rule_engine_analysis_FD, "    App layer protocol is %s.\n", TmModuleAlprotoToString(s->alproto));
+        }
+        if (rule_content || rule_content_http || rule_pcre || rule_pcre_http) {
+            fprintf(rule_engine_analysis_FD, "    Rule contains %d content options, %d http content options, %d pcre options, and %d pcre options with http modifiers.\n", rule_content, rule_content_http, rule_pcre, rule_pcre_http);
+        }
+        if (warn_pcre_no_content /*rule_pcre > 0 && rule_content == 0 && rule_content_http == 0*/) {
+            fprintf(rule_engine_analysis_FD, "    Warning: Rule uses pcre without a content option present.\n"
+                                             "             -Consider adding a content to improve performance of this rule.\n");
+        }
+        if (warn_pcre_http_content /*rule_content_http > 0 && rule_pcre > 0 && rule_pcre_http == 0*/) {
+            fprintf(rule_engine_analysis_FD, "    Warning: Rule uses content options with http_* and pcre options without http modifiers.\n"
+                                             "             -Consider adding http pcre modifier.\n");
+        }
+        else if (warn_pcre_http /*s->alproto == ALPROTO_HTTP && rule_pcre > 0 && rule_pcre_http == 0*/) {
+            fprintf(rule_engine_analysis_FD, "    Warning: Rule app layer protocol is http, but pcre options do not have http modifiers.\n"
+                                             "             -Consider adding http pcre modifiers.\n");
+        }
+        if (warn_content_http_content /*rule_content > 0 && rule_content_http > 0*/) {
+            fprintf(rule_engine_analysis_FD, "    Warning: Rule contains content with http_* and content without http_*.\n"
+                                         "             -Consider adding http content modifiers.\n");
+        }
+        if (warn_content_http /*s->alproto == ALPROTO_HTTP && rule_content > 0 && rule_content_http == 0*/) {
+            fprintf(rule_engine_analysis_FD, "    Warning: Rule app layer protocol is http, but content options do not have http_* modifiers.\n"
+                                             "             -Consider adding http content modifiers.\n");
+        }
+        if (rule_content == 1) {
+             //todo: warning if content is weak, separate warning for pcre + weak content
+        }
+        if (warn_encoding_norm_http_buf) {
+            fprintf(rule_engine_analysis_FD, "    Warning: Rule may contain percent encoded content for a normalized http buffer match.\n");
+        }
+        if (warn_tcp_no_flow /*rule_flow == 0 && rule_flow == 0
+                && !(s->proto.flags & DETECT_PROTO_ANY) && DetectProtoContainsProto(&s->proto, IPPROTO_TCP)*/) {
+            fprintf(rule_engine_analysis_FD, "    Warning: TCP rule without a flow or flags option.\n"
+                                             "             -Consider adding flow or flags to improve performance of this rule.\n");
+        }
+        if (warn_client_ports /*rule_flow && !rule_bidirectional && (rule_flow_toserver || rule_flow_toclient)
+                      && !((s->flags & SIG_FLAG_SP_ANY) && (s->flags & SIG_FLAG_DP_ANY)))
+            if (((s->flags & SIG_FLAG_TOSERVER) && !(s->flags & SIG_FLAG_SP_ANY) && (s->flags & SIG_FLAG_DP_ANY))
+                || ((s->flags & SIG_FLAG_TOCLIENT) && !(s->flags & SIG_FLAG_DP_ANY) && (s->flags & SIG_FLAG_SP_ANY))*/) {
+                fprintf(rule_engine_analysis_FD, "    Warning: Rule contains ports or port variables only on the client side.\n"
+                                                 "             -Flow direction possibly inconsistent with rule.\n");
+        }
+        if (warn_direction /*rule_flow && rule_bidirectional && (rule_flow_toserver || rule_flow_toclient)*/) {
+            fprintf(rule_engine_analysis_FD, "    Warning: Rule is bidirectional and has a flow option with a specific direction.\n");
+        }
+        if (warn_method_toclient /*http_method_buf && rule_flow && rule_flow_toclient*/) {
+            fprintf(rule_engine_analysis_FD, "    Warning: Rule uses content or pcre for http_method with flow:to_client or from_server\n");
+        }
+        if (warn_method_serverbody /*http_method_buf && http_server_body_buf*/) {
+            fprintf(rule_engine_analysis_FD, "    Warning: Rule uses content or pcre for http_method with content or pcre for http_server_body.\n");
+        }
+        if (warn_pcre_method /*http_method_buf && rule_content == 0 && rule_content_http == 0
+                               && (rule_pcre > 0 || rule_pcre_http > 0)*/) {
+            fprintf(rule_engine_analysis_FD, "    Warning: Rule uses pcre with only a http_method content; possible performance issue.\n");
+        }
+        if (rule_warning == 0) {
+            fprintf(rule_engine_analysis_FD, "    No warnings for this rule.\n");
+        }
+        fprintf(rule_engine_analysis_FD, "\n");
+    }
+    return;
+}
diff --git a/src/detect-engine-analyzer.h b/src/detect-engine-analyzer.h
new file mode 100644 (file)
index 0000000..f394176
--- /dev/null
@@ -0,0 +1,35 @@
+/* Copyright (C) 2007-2012 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Eileen Donlon <emdonlo@gmail.com>
+ */
+
+#ifndef __DETECT_ENGINE_ANALYZER_H__
+#define __DETECT_ENGINE_ANALYZER_H__
+
+#include <stdint.h>
+
+int SetupRuleAnalyzer(char *log_path);
+void CleanupRuleAnalyzer (char *log_path);
+int PerCentEncodingSetup ();
+int PerCentEncodingMatch (uint8_t *content, uint8_t content_len);
+void EngineAnalysisRules(Signature *s, char *line);
+
+#endif /* __DETECT_ENGINE_ANALYZER_H__ */
index 64ecc3d6b1f136895bfafbcbea3a76afe9bf621e..00aac168e65825d9a9bab36a2fbf92622b86b452 100644 (file)
@@ -46,6 +46,7 @@
 #include "detect-engine-dcepayload.h"
 #include "detect-engine-uri.h"
 #include "detect-engine-state.h"
+#include "detect-engine-analyzer.h"
 
 #include "detect-http-cookie.h"
 #include "detect-http-method.h"
@@ -182,6 +183,7 @@ extern uint8_t engine_mode;
 
 extern int engine_analysis;
 static int fp_engine_analysis_set = 0;
+static int rule_engine_analysis_set = 0;
 static FILE *fp_engine_analysis_FD = NULL;
 
 SigMatch *SigMatchAlloc(void);
@@ -523,6 +525,9 @@ int DetectLoadSigFile(DetectEngineCtx *de_ctx, char *sig_file, int *sigs_tot) {
             if (fp_engine_analysis_set) {
                 EngineAnalysisFastPattern(sig);
             }
+            if (rule_engine_analysis_set) {
+                EngineAnalysisRules(sig, line);
+            }
             SCLogDebug("signature %"PRIu32" loaded", sig->id);
             good++;
         } else {
@@ -571,7 +576,6 @@ int SigLoadSignatures(DetectEngineCtx *de_ctx, char *sig_file, int sig_file_excl
                       "report.");
             fp_engine_analysis_set = 0;
         }
-
         if (fp_engine_analysis_set) {
             char *log_dir;
             if (ConfGet("default-log-dir", &log_dir) != 1)
@@ -597,9 +601,11 @@ int SigLoadSignatures(DetectEngineCtx *de_ctx, char *sig_file, int sig_file_excl
                     tms->tm_min, tms->tm_sec);
             fprintf(fp_engine_analysis_FD, "----------------------------------------------"
                     "---------------------\n");
-        } else {
+        }
+        else {
             SCLogInfo("Engine-Analysis for fast_pattern disabled in conf file.");
         }
+        rule_engine_analysis_set = SetupRuleAnalyzer(log_path);
     }
 
     /* ok, let's load signature files from the general config */
@@ -698,6 +704,9 @@ int SigLoadSignatures(DetectEngineCtx *de_ctx, char *sig_file, int sig_file_excl
                 fp_engine_analysis_FD = NULL;
             }
         }
+        if (rule_engine_analysis_set) {
+            CleanupRuleAnalyzer(log_path);
+        }
     }
 
     DetectParseDupSigHashFree(de_ctx);
@@ -2061,7 +2070,7 @@ static int SignatureIsDEOnly(DetectEngineCtx *de_ctx, Signature *s) {
             SCReturnInt(0);
     }
 
-    /* need at least one decode event keyword to be condered decode event. */
+    /* need at least one decode event keyword to be considered decode event. */
     sm = s->sm_lists[DETECT_SM_LIST_MATCH];
     for ( ;sm != NULL; sm = sm->next) {
         if (sm->type == DETECT_DECODE_EVENT)