From: Bhagya Tholpady (bbantwal) Date: Wed, 19 Aug 2020 20:27:13 +0000 (+0000) Subject: Merge pull request #2382 in SNORT/snort3 from ~SVLASIUK/snort3:dump_config_json to... X-Git-Tag: 3.0.2-6~49 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8be856bb5d8ed535e30b35aadae3c841a169f19e;p=thirdparty%2Fsnort3.git Merge pull request #2382 in SNORT/snort3 from ~SVLASIUK/snort3:dump_config_json to master Squashed commit of the following: commit bcba018627626465fbb8f55dacab8a6856690da3 Author: Serhii Vlasiuk Date: Thu Aug 6 14:34:18 2020 +0300 dump_config: dump config in JSON format to stdout add new command-line option --dump-config=all to dump the config in JSON format commit 67b5defe6f7a132ff62d2bd278364476cefe372c Author: Oleksii Khomiakovskyi Date: Wed Aug 5 17:13:33 2020 +0300 helpers: add unit tests for special characters escaping --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c5dc29dc4..cc640a868 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -89,6 +89,7 @@ add_subdirectory(actions) add_subdirectory(codecs) add_subdirectory(control) add_subdirectory(detection) +add_subdirectory(dump_config) add_subdirectory(events) add_subdirectory(file_api) add_subdirectory(filters) @@ -139,6 +140,7 @@ add_executable( snort $ $ $ + $ $ $ $ diff --git a/src/detection/fp_create.cc b/src/detection/fp_create.cc index 21a0cd084..cbc8405a2 100644 --- a/src/detection/fp_create.cc +++ b/src/detection/fp_create.cc @@ -1613,7 +1613,7 @@ int fpCreateFastPacketDetection(SnortConfig* sc) if ( log_rule_group_details ) LogMessage("Service Based Rule Maps Done....\n"); - if ( !sc->validation_mode() or sc->mem_check() ) + if ( !sc->test_mode() or sc->mem_check() ) { unsigned c = compile_mpses(sc, can_build_mt(fp)); unsigned expected = mpse_count + offload_mpse_count; diff --git a/src/dump_config/CMakeLists.txt b/src/dump_config/CMakeLists.txt new file mode 100644 index 000000000..35e0c523d --- /dev/null +++ b/src/dump_config/CMakeLists.txt @@ -0,0 +1,12 @@ + +add_library ( dump_config OBJECT + config_data.cc + config_data.h + config_output.cc + config_output.h + json_config_output.cc + json_config_output.h + text_config_output.cc + text_config_output.h +) + diff --git a/src/dump_config/config_data.cc b/src/dump_config/config_data.cc new file mode 100644 index 000000000..d2ffb3d54 --- /dev/null +++ b/src/dump_config/config_data.cc @@ -0,0 +1,334 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// config_data.cc author Serhii Vlasiuk + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "config_data.h" + +#include + +using namespace snort; + +BaseConfigNode::BaseConfigNode(BaseConfigNode* p) : + parent(p) +{} + +void BaseConfigNode::add_child_node(BaseConfigNode* node) +{ + assert(node); + children.push_back(node); +} + +void BaseConfigNode::clear_nodes(BaseConfigNode* node) +{ + for ( auto& config_node : node->children ) + clear_nodes(config_node); + + delete node; +} + +TreeConfigNode::TreeConfigNode(BaseConfigNode* parent_node, + const std::string& node_name, const Parameter::Type node_type) : + BaseConfigNode(parent_node), name(node_name), type(node_type) +{} + +BaseConfigNode* TreeConfigNode::get_node(const std::string& name) +{ + for ( auto node : children ) + { + if ( node->get_name() == name ) + return node; + } + return nullptr; +} + +ValueConfigNode::ValueConfigNode(BaseConfigNode* parent_node, const Value& val, + const std::string& name) : + BaseConfigNode(parent_node), value(val), custom_name(name) +{} + +void ValueConfigNode::set_value(const snort::Value& v) +{ + if ( value.get_param_type() == Parameter::PT_MULTI ) + { + std::string origin = value.get_origin_string(); + if ( !multi_value && value.has_default() ) + origin.clear(); + + origin += " "; + origin += v.get_origin_string(); + value.set_origin(origin.c_str()); + multi_value = true; + } + else + value = v; +} + +BaseConfigNode* ValueConfigNode::get_node(const std::string& name) +{ + if ( !custom_name.empty() ) + return ((custom_name == name) and value.has_default()) ? this : nullptr; + else + return value.is(name.c_str()) and value.has_default() ? this : nullptr; +} + +ConfigData::ConfigData(const char* file) +{ + if ( file ) + file_name = file; +} + +void ConfigData::sort() +{ + config_trees.sort([](const BaseConfigNode* l, const BaseConfigNode* r) + { return l->get_name() < r->get_name(); }); +} + +void ConfigData::clear() +{ + for ( auto config_tree: config_trees ) + BaseConfigNode::clear_nodes(config_tree); + + config_trees.clear(); +} + +#ifdef UNIT_TEST +#include + +TEST_CASE("add_nodes", "[ConfigData]") +{ + ConfigData config_data("test_file"); + + auto node_test1 = new TreeConfigNode(nullptr, "test1", Parameter::Type::PT_TABLE); + auto node_test2 = new TreeConfigNode(nullptr, "test2", Parameter::Type::PT_TABLE); + + config_data.add_config_tree(node_test1); + config_data.add_config_tree(node_test2); + + CHECK(config_data.config_trees.size() == 2); + + config_data.clear(); +} + +TEST_CASE("clear_nodes", "[ConfigData]") +{ + ConfigData config_data("test_file"); + + auto node_test1 = new TreeConfigNode(nullptr, "test1", Parameter::Type::PT_TABLE); + auto node_test2 = new TreeConfigNode(nullptr, "test2", Parameter::Type::PT_TABLE); + + config_data.add_config_tree(node_test1); + config_data.add_config_tree(node_test2); + + config_data.clear(); + + CHECK(config_data.config_trees.size() == 0); +} + +TEST_CASE("sort_nodes", "[ConfigData]") +{ + ConfigData config_data("test_file"); + + auto node_test1 = new TreeConfigNode(nullptr, "test1", Parameter::Type::PT_TABLE); + auto node_test2 = new TreeConfigNode(nullptr, "test2", Parameter::Type::PT_TABLE); + auto node_test3 = new TreeConfigNode(nullptr, "test3", Parameter::Type::PT_TABLE); + auto node_test4 = new TreeConfigNode(nullptr, "test4", Parameter::Type::PT_TABLE); + + config_data.add_config_tree(node_test2); + config_data.add_config_tree(node_test3); + config_data.add_config_tree(node_test4); + config_data.add_config_tree(node_test1); + + CHECK(config_data.config_trees.front()->get_name() == "test2"); + CHECK(config_data.config_trees.back()->get_name() == "test1"); + + config_data.sort(); + + CHECK(config_data.config_trees.front()->get_name() == "test1"); + CHECK(config_data.config_trees.back()->get_name() == "test4"); + + config_data.clear(); +} + +TEST_CASE("tree_config_node", "[TreeConfigNode]") +{ + BaseConfigNode* parent_node = new TreeConfigNode(nullptr, "parent_node", + Parameter::Type::PT_TABLE); + BaseConfigNode* child_node = new TreeConfigNode(parent_node, "child_node", + Parameter::Type::PT_LIST); + + parent_node->add_child_node(child_node); + + SECTION("get_name") + { + CHECK(parent_node->get_name() == "parent_node"); + CHECK(child_node->get_name() == "child_node"); + } + SECTION("get_type") + { + CHECK(parent_node->get_type() == Parameter::Type::PT_TABLE); + CHECK(child_node->get_type() == Parameter::Type::PT_LIST); + } + SECTION("get_node") + { + CHECK(parent_node->get_node("child_node") == child_node); + CHECK(parent_node->get_node("other_node") == nullptr); + CHECK(child_node->get_node("child_node") == nullptr); + } + SECTION("get_parent_node") + { + CHECK(child_node->get_parent_node() == parent_node); + CHECK(parent_node->get_parent_node() == nullptr); + } + SECTION("get_children") + { + CHECK(parent_node->get_children().size() == 1); + CHECK(child_node->get_children().size() == 0); + } + SECTION("get_value") + { + CHECK(parent_node->get_value() == nullptr); + CHECK(child_node->get_value() == nullptr); + } + + BaseConfigNode::clear_nodes(parent_node); +} + +TEST_CASE("value_config_node", "[ValueConfigNode]") +{ + BaseConfigNode* parent_node = new TreeConfigNode(nullptr, "parent_node", + Parameter::Type::PT_TABLE); + + const Parameter p_string("param_str", Parameter::PT_STRING, nullptr, nullptr, + "test param PT_STRING type"); + + const Parameter p_string_custom("param_str_custom", Parameter::PT_STRING, nullptr, + "custom_default", "test param PT_STRING type with custom name"); + + const Parameter p_bool_w_default("param_bool", Parameter::PT_BOOL, nullptr, "false", + "test param PT_BOOL type with default"); + + const Parameter p_multi_w_default("param_multi", Parameter::PT_MULTI, + "test1 | test2 | test3 | test4", "test2 test3", "test param PT_MULTI type with default"); + + Value val_str("test_str"); + val_str.set(&p_string); + + Value val_str_custom("test_str_custom"); + val_str_custom.set(&p_string_custom); + + Value val_bool(true); + val_bool.set(&p_bool_w_default); + + Value val_multi("test2 test3"); + val_multi.set(&p_multi_w_default); + + BaseConfigNode* value_node_str = new ValueConfigNode(parent_node, val_str); + BaseConfigNode* value_node_bool = new ValueConfigNode(parent_node, val_bool); + BaseConfigNode* value_node_multi = new ValueConfigNode(parent_node, val_multi); + BaseConfigNode* value_node_custom_name = new ValueConfigNode(parent_node, val_str_custom, + "custom_name"); + + parent_node->add_child_node(value_node_str); + parent_node->add_child_node(value_node_bool); + parent_node->add_child_node(value_node_multi); + parent_node->add_child_node(value_node_custom_name); + + SECTION("get_name") + { + CHECK(value_node_str->get_name() == "param_str"); + CHECK(value_node_bool->get_name() == "param_bool"); + CHECK(value_node_multi->get_name() == "param_multi"); + CHECK(value_node_custom_name->get_name() == "custom_name"); + } + SECTION("get_type") + { + CHECK(value_node_str->get_type() == Parameter::PT_STRING); + CHECK(value_node_bool->get_type() == Parameter::PT_BOOL); + CHECK(value_node_multi->get_type() == Parameter::PT_MULTI); + CHECK(value_node_custom_name->get_type() == Parameter::PT_STRING); + } + SECTION("get_node") + { + CHECK(parent_node->get_node("param_str") == value_node_str); + CHECK(parent_node->get_node("param_bool") == value_node_bool); + CHECK(parent_node->get_node("param_multi") == value_node_multi); + CHECK(parent_node->get_node("custom_name") == value_node_custom_name); + + CHECK(value_node_str->get_node("param_str") == nullptr); + CHECK(value_node_bool->get_node("param_bool") == value_node_bool); + CHECK(value_node_multi->get_node("param_multi") == value_node_multi); + CHECK(value_node_custom_name->get_node("custom_name") == value_node_custom_name); + } + SECTION("get_parent_node") + { + CHECK(value_node_str->get_parent_node() == parent_node); + CHECK(value_node_bool->get_parent_node() == parent_node); + CHECK(value_node_multi->get_parent_node() == parent_node); + CHECK(value_node_custom_name->get_parent_node() == parent_node); + } + SECTION("get_children") + { + CHECK(parent_node->get_children().size() == 4); + CHECK(value_node_multi->get_children().size() == 0); + CHECK(value_node_str->get_children().size() == 0); + CHECK(value_node_bool->get_children().size() == 0); + CHECK(value_node_custom_name->get_children().size() == 0); + } + SECTION("get_value") + { + CHECK(value_node_str->get_value()->get_origin_string() == "test_str"); + CHECK(value_node_bool->get_value()->get_bool() == true); + CHECK(value_node_multi->get_value()->get_origin_string() == "test2 test3"); + CHECK(value_node_custom_name->get_value()->get_origin_string() == "test_str_custom"); + } + + Value new_val_str("new_value"); + value_node_str->set_value(new_val_str); + + Value new_custom_val_str("new_custom_value"); + value_node_custom_name->set_value(new_custom_val_str); + + Value new_val_bool(false); + value_node_bool->set_value(new_val_bool); + + Value new_val1_multi("test1"); + value_node_multi->set_value(new_val1_multi); + + Value new_val2_multi("test2"); + value_node_multi->set_value(new_val2_multi); + + Value new_val3_multi("test3"); + value_node_multi->set_value(new_val3_multi); + + SECTION("get_value_after_update") + { + CHECK(value_node_str->get_value()->get_origin_string() == "new_value"); + CHECK(value_node_bool->get_value()->get_bool() == false); + CHECK(value_node_multi->get_value()->get_origin_string() == "test1 test2 test3"); + CHECK(value_node_custom_name->get_value()->get_origin_string() == "new_custom_value"); + } + + BaseConfigNode::clear_nodes(parent_node); +} + +#endif + diff --git a/src/main/config_tree.h b/src/dump_config/config_data.h similarity index 74% rename from src/main/config_tree.h rename to src/dump_config/config_data.h index 598dbb12e..b6ad0603d 100644 --- a/src/main/config_tree.h +++ b/src/dump_config/config_data.h @@ -15,10 +15,10 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- -// config_tree.h author Serhii Vlasiuk +// config_data.h author Serhii Vlasiuk -#ifndef CONFIG_TREE_H -#define CONFIG_TREE_H +#ifndef CONFIG_DATA_H +#define CONFIG_DATA_H #include @@ -26,13 +26,7 @@ class BaseConfigNode; -using ChildrenNodes = std::list; - -class ConfigTextFormat -{ -public: - static void print(const BaseConfigNode* parent, const std::string& config_name); -}; +using ConfigTrees = std::list; class BaseConfigNode { @@ -43,10 +37,10 @@ public: virtual std::string get_name() const = 0; virtual snort::Parameter::Type get_type() const = 0; virtual BaseConfigNode* get_node(const std::string& name) = 0; - virtual std::string data() const { return ""; } virtual void set_value(const snort::Value&) {} + virtual const snort::Value* get_value() const { return nullptr; } - const ChildrenNodes& get_children() const + const ConfigTrees& get_children() const { return children; } BaseConfigNode* get_parent_node() const @@ -57,7 +51,7 @@ public: static void clear_nodes(BaseConfigNode* root); protected: - ChildrenNodes children; + ConfigTrees children; BaseConfigNode* parent = nullptr; }; @@ -84,26 +78,43 @@ private: class ValueConfigNode : public BaseConfigNode { public: - ValueConfigNode(BaseConfigNode* parent, const snort::Value& value); + ValueConfigNode(BaseConfigNode* parent, const snort::Value& value, + const std::string& name = ""); private: virtual std::string get_name() const override - { return value.get_name(); } + { return !custom_name.empty() ? custom_name : value.get_name(); } virtual snort::Parameter::Type get_type() const override { return value.get_param_type(); } - virtual std::string data() const override - { return value.get_origin_string(); } - - virtual void set_value(const snort::Value& v) override - { value = v; } + virtual const snort::Value* get_value() const override + { return &value; } + virtual void set_value(const snort::Value& v) override; virtual BaseConfigNode* get_node(const std::string& name) override; private: snort::Value value; + bool multi_value = false; + std::string custom_name; +}; + +class ConfigData +{ +public: + ConfigData(const char* file_name); + + void add_config_tree(BaseConfigNode* root) + { config_trees.push_back(root); } + + void sort(); + void clear(); + +public: + std::string file_name; + ConfigTrees config_trees; }; -#endif // CONFIG_TREE_H +#endif // CONFIG_DATA_H diff --git a/src/dump_config/config_output.cc b/src/dump_config/config_output.cc new file mode 100644 index 000000000..27e308487 --- /dev/null +++ b/src/dump_config/config_output.cc @@ -0,0 +1,34 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// config_output.cc author Serhii Vlasiuk + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "config_output.h" + +#include "config_data.h" + +void ConfigOutput::dump_config(ConfigData& config_data) +{ + config_data.sort(); + dump(config_data); + config_data.clear(); +} + diff --git a/src/dump_config/config_output.h b/src/dump_config/config_output.h new file mode 100644 index 000000000..065caf773 --- /dev/null +++ b/src/dump_config/config_output.h @@ -0,0 +1,38 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// config_output.h author Serhii Vlasiuk + +#ifndef CONFIG_OUTPUT_H +#define CONFIG_OUTPUT_H + +class ConfigData; + +class ConfigOutput +{ +public: + ConfigOutput() = default; + virtual ~ConfigOutput() = default; + + void dump_config(ConfigData&); + +private: + virtual void dump(const ConfigData&) = 0; +}; + +#endif // CONFIG_OUTPUT_H + diff --git a/src/dump_config/dev_notes.txt b/src/dump_config/dev_notes.txt new file mode 100644 index 000000000..b9bb6e3a9 --- /dev/null +++ b/src/dump_config/dev_notes.txt @@ -0,0 +1,29 @@ +This directory contains classes related to Snort configuration dump. + +Snort supports dumping config into a file for JSON and text formats. + +* ConfigData + + The ConfigData structure represents the internal config data of a particular Lua file. + A unique shell is associated with every base and targeted policy file. + Every module in a Lua file is represented by the General Tree. + The Tree node can be one of the following types: TreeConfigNode, ValueConfigNode. + The TreeConfigNode represents a table or list. + The ValueConfigNode represents a config value itself. + +* ConfigOutput + + The ConfigOutput class is a base class that encapsulates dumping config into a file. + Pure virtual function dump() should be overridden by derived classes for particular + output format. + +* JsonAllConfigOutput + + The JsonAllConfigOutput class inherits from ConfigOutput and dumps base and targeted + policy file in JSON format. + +* TextConfigOutput + + The TextConfigOutput class inherits from ConfigOutput and dumps base and targeted + policy file in text format. + diff --git a/src/dump_config/json_config_output.cc b/src/dump_config/json_config_output.cc new file mode 100644 index 000000000..a8f9bae1f --- /dev/null +++ b/src/dump_config/json_config_output.cc @@ -0,0 +1,104 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// json_config_output.cc author Serhii Vlasiuk + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "json_config_output.h" + +#include "config_data.h" + +using namespace snort; + +JsonAllConfigOutput::JsonAllConfigOutput() : + ConfigOutput(), json(std::cout) +{ json.open_array(); } + +JsonAllConfigOutput::~JsonAllConfigOutput() +{ json.close_array(); } + +void JsonAllConfigOutput::dump(const ConfigData& config_data) +{ + json.open(); + json.put("filename", config_data.file_name); + json.open("config"); + + for ( const auto config_tree: config_data.config_trees ) + dump_modules(config_tree); + + json.close(); + json.close(); +} + +void JsonAllConfigOutput::dump_modules(const BaseConfigNode* node) +{ + Parameter::Type type = node->get_type(); + if ( type == Parameter::PT_LIST ) + json.open_array(node->get_name().c_str()); + else if ( type == Parameter::PT_TABLE ) + { + std::string name = node->get_name(); + name.empty() ? json.open() : json.open(name.c_str()); + } + else + dump_value(node); + + for ( const auto n : node->get_children() ) + dump_modules(n); + + if ( type == Parameter::PT_LIST ) + json.close_array(); + else if ( type == Parameter::PT_TABLE ) + json.close(); +} + +void JsonAllConfigOutput::dump_value(const BaseConfigNode* node) +{ + const Value* value = node->get_value(); + if ( !value ) + return; + + switch ( node->get_type() ) + { + case Parameter::PT_BOOL: + case Parameter::PT_IMPLIED: + value->get_bool() ? json.put_true(node->get_name().c_str()) : + json.put_false(node->get_name().c_str()); + break; + case Parameter::PT_INT: + json.put(node->get_name().c_str(), value->get_long()); + break; + case Parameter::PT_REAL: + { + std::string value_str = value->get_as_string(); + auto pos = value_str.find("."); + int precision = 0; + if ( pos != std::string::npos ) + precision = value_str.size() - pos - 1; + + json.put(node->get_name().c_str(), value->get_real(), precision); + break; + } + default: + json.put(node->get_name().c_str(), value->get_origin_string()); + break; + } +} + diff --git a/src/dump_config/json_config_output.h b/src/dump_config/json_config_output.h new file mode 100644 index 000000000..c0c8c901d --- /dev/null +++ b/src/dump_config/json_config_output.h @@ -0,0 +1,45 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// json_config_output.h author Serhii Vlasiuk + +#ifndef JSON_CONFIG_OUTPUT_H +#define JSON_CONFIG_OUTPUT_H + +#include "config_output.h" +#include "helpers/json_stream.h" + +class BaseConfigNode; + +class JsonAllConfigOutput : public ConfigOutput +{ +public: + JsonAllConfigOutput(); + ~JsonAllConfigOutput() override; + +private: + void dump(const ConfigData&) override; + + void dump_modules(const BaseConfigNode* node); + void dump_value(const BaseConfigNode* node); + +private: + JsonStream json; +}; + +#endif // JSON_CONFIG_OUTPUT_H + diff --git a/src/main/config_tree.cc b/src/dump_config/text_config_output.cc similarity index 51% rename from src/main/config_tree.cc rename to src/dump_config/text_config_output.cc index a68986923..f8607c5f5 100644 --- a/src/main/config_tree.cc +++ b/src/dump_config/text_config_output.cc @@ -15,21 +15,48 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- -// config_tree.cc author Serhii Vlasiuk +// text_config_output.cc author Serhii Vlasiuk -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif +#include "text_config_output.h" -#include "config_tree.h" +#include +#include -#include - -#include "log/messages.h" +#include "config_data.h" using namespace snort; -void ConfigTextFormat::print(const BaseConfigNode* parent, const std::string& config_name) +void TextConfigOutput::dump_value(const BaseConfigNode* node, const std::string& config_name) +{ + const Value* value = node->get_value(); + if ( !value ) + return; + + if ( value->get_as_string().empty() ) + return; + + switch ( node->get_type() ) + { + case Parameter::PT_BOOL: + case Parameter::PT_IMPLIED: + { + std::string value_str = value->get_bool() ? "true" : "false"; + std::cout << config_name << "=" << value_str << std::endl; + break; + } + case Parameter::PT_INT: + std::cout << config_name << "=" << value->get_long() << std::endl; + break; + case Parameter::PT_REAL: + std::cout << config_name << "=" << value->get_real() << std::endl; + break; + default: + std::cout << config_name << "=" << std::quoted(value->get_origin_string()) << std::endl; + break; + } +} + +void TextConfigOutput::dump_modules(const BaseConfigNode* parent, const std::string& config_name) { static char buf[16]; int list_index = 0; @@ -51,53 +78,19 @@ void ConfigTextFormat::print(const BaseConfigNode* parent, const std::string& co full_config_name += "]"; } - print(node, full_config_name); + dump_modules(node, full_config_name); } - std::string config_data = parent->data(); - if ( !config_data.empty() ) - LogConfig("%s=%s\n", config_name.c_str(), config_data.c_str()); -} - -BaseConfigNode::BaseConfigNode(BaseConfigNode* p) : - parent(p) -{} - -void BaseConfigNode::add_child_node(BaseConfigNode* node) -{ - assert(node); - children.push_back(node); -} - -void BaseConfigNode::clear_nodes(BaseConfigNode* node) -{ - for ( auto& config_node : node->children ) - clear_nodes(config_node); - - delete node; + dump_value(parent, config_name); } -TreeConfigNode::TreeConfigNode(BaseConfigNode* parent_node, - const std::string& node_name, const Parameter::Type node_type) : - BaseConfigNode(parent_node), name(node_name), type(node_type) -{} - -BaseConfigNode* TreeConfigNode::get_node(const std::string& name) +void TextConfigOutput::dump(const ConfigData& config_data) { - for ( auto node : children ) - { - if ( node->get_name() == name ) - return node; - } - return nullptr; -} - -ValueConfigNode::ValueConfigNode(BaseConfigNode* parent_node, const Value& val) : - BaseConfigNode(parent_node), value(val) -{} + std::string output("consolidated config for "); + output += config_data.file_name; + std::cout << output << std::endl; -BaseConfigNode* ValueConfigNode::get_node(const std::string& name) -{ - return value.is(name.c_str()) and value.has_default() ? this : nullptr; + for ( const auto config_tree: config_data.config_trees ) + dump_modules(config_tree, config_tree->get_name()); } diff --git a/src/dump_config/text_config_output.h b/src/dump_config/text_config_output.h new file mode 100644 index 000000000..92affbbf3 --- /dev/null +++ b/src/dump_config/text_config_output.h @@ -0,0 +1,42 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// 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 along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// text_config_output.h author Serhii Vlasiuk + +#ifndef TEXT_CONFIG_OUTPUT_H +#define TEXT_CONFIG_OUTPUT_H + +#include + +#include "config_output.h" + +class BaseConfigNode; + +class TextConfigOutput : public ConfigOutput +{ +public: + TextConfigOutput() = default; + +private: + void dump(const ConfigData&) override; + + void dump_modules(const BaseConfigNode* parent, const std::string& config_name); + void dump_value(const BaseConfigNode* parent, const std::string& config_name); +}; + +#endif // TEXT_CONFIG_OUTPUT_H + diff --git a/src/framework/value.cc b/src/framework/value.cc index 82bdfec89..9f1d9746e 100644 --- a/src/framework/value.cc +++ b/src/framework/value.cc @@ -199,26 +199,39 @@ bool Value::strtol(long& n, const std::string& tok) const return true; } -const char* Value::get_as_string() +std::string Value::get_as_string() const { + std::string value_str = str; switch ( type ) { case VT_BOOL: - str = num ? "true" : "false"; + value_str = num ? "true" : "false"; break; case VT_NUM: - ss = new stringstream; - *ss << num; - str = ss->str(); + { + stringstream tmp; + tmp << std::fixed; + tmp << num; + value_str = tmp.str(); + auto dot_pos = value_str.find('.'); + auto pos = value_str.find_last_not_of("0"); + if ( pos == dot_pos ) + --pos; + + value_str = value_str.substr(0, pos + 1); break; + } default: break; } - return str.c_str(); + return value_str; } std::string Value::get_origin_string() const { + if ( origin_str.empty() ) + return ""; + std::string value; std::string token; @@ -230,15 +243,6 @@ std::string Value::get_origin_string() const } value.erase(value.size() - 1); - if ( param && param->type != Parameter::PT_BOOL - && param->type != Parameter::PT_REAL - && param->type != Parameter::PT_INT - && param->type != Parameter::PT_IMPLIED ) - { - value.insert(0, "'"); - value.insert(value.length(), "'"); - } - return value; } @@ -418,19 +422,23 @@ TEST_CASE("token test", "[Value]") TEST_CASE("get as string", "[Value]") { - const char* str_val; bool bool_val = true; double num_val = 6; Value test_val(bool_val); - str_val = (const char *)test_val.get_as_string(); - REQUIRE(str_val != nullptr); - CHECK((strcmp(str_val,"true")==0)); + CHECK((strcmp(test_val.get_as_string().c_str(),"true") == 0)); test_val.set(num_val); - str_val = (const char *)test_val.get_as_string(); - REQUIRE(str_val != nullptr); - CHECK((strcmp(str_val,"6")==0)); + CHECK((strcmp(test_val.get_as_string().c_str(),"6") == 0)); + + test_val.set(1234.2); + CHECK((strcmp(test_val.get_as_string().c_str(),"1234.2") == 0)); + + test_val.set(123456.0893); + CHECK((strcmp(test_val.get_as_string().c_str(),"123456.0893") == 0)); + + test_val.set(0.0803); + CHECK((strcmp(test_val.get_as_string().c_str(),"0.0803") == 0)); } diff --git a/src/framework/value.h b/src/framework/value.h index df1760857..aa93044f0 100644 --- a/src/framework/value.h +++ b/src/framework/value.h @@ -42,10 +42,10 @@ public: enum ValueType { VT_BOOL, VT_NUM, VT_STR }; Value(bool b) - { set(b); set_origin(b); } + { set(b); } Value(double d) - { set(d); set_origin(d); } + { set(d); } Value(const char* s) { set(s); set_origin(s); } @@ -85,20 +85,9 @@ public: void set(bool b) { type = VT_BOOL; num = b ? 1 : 0; str.clear(); } - void set_origin(bool val) - { origin_str = val ? "true" : "false"; } - void set(double d) { type = VT_NUM; num = d; str.clear(); } - void set_origin(double val) - { - auto temp = new std::stringstream; - *temp << val; - origin_str = temp->str(); - delete temp; - } - void set(long n) { set((double)n); } @@ -168,7 +157,7 @@ public: const char* get_string() const { return str.c_str(); } - const char* get_as_string(); + std::string get_as_string() const; Parameter::Type get_param_type() const; std::string get_origin_string() const; diff --git a/src/helpers/test/json_stream_test.cc b/src/helpers/test/json_stream_test.cc index e59c278ae..06c8550ad 100644 --- a/src/helpers/test/json_stream_test.cc +++ b/src/helpers/test/json_stream_test.cc @@ -153,6 +153,14 @@ TEST_CASE("basic", "[json_stream]") CHECK(ss.str() == x); } + SECTION("backslash") + { + const char* s = R"-(content:\test\;)-"; + const char* x = R"-("content:\\test\\;")-"; + js.put(nullptr, s); + CHECK(ss.str() == x); + } + SECTION("embedded quotes") { const char* s = R"-(content:"foo";)-"; @@ -161,6 +169,14 @@ TEST_CASE("basic", "[json_stream]") CHECK(ss.str() == x); } + SECTION("special characters") + { + const char* s = R"-(content: / " \ $ # ! @ % ^ & * ' \b\f\t\r\n . )-"; + const char* x = R"-("content: / \" \\ $ # ! @ % ^ & * ' \\b\\f\\t\\r\\n . ")-"; + js.put(nullptr, s); + CHECK(ss.str() == x); + } + SECTION("null list") { js.put("i"); diff --git a/src/log/messages.cc b/src/log/messages.cc index 7c308b38b..e805d987f 100644 --- a/src/log/messages.cc +++ b/src/log/messages.cc @@ -235,16 +235,6 @@ void LogMessage(FILE* fh, const char* format,...) va_end(ap); } -void LogConfig(const char* format, ...) -{ - va_list ap; - va_start(ap, format); - - WriteLogMessage(stdout, false, format, ap); - - va_end(ap); -} - /* * Function: WarningMessage(const char *, ...) * diff --git a/src/log/messages.h b/src/log/messages.h index d61e2e870..a2ab4d282 100644 --- a/src/log/messages.h +++ b/src/log/messages.h @@ -61,7 +61,6 @@ SO_PUBLIC void ParseError(const char*, ...) __attribute__((format (printf, 1, 2) SO_PUBLIC void ReloadError(const char*, ...) __attribute__((format (printf, 1, 2))); [[noreturn]] SO_PUBLIC void ParseAbort(const char*, ...) __attribute__((format (printf, 1, 2))); -SO_PUBLIC void LogConfig(const char*, ...) __attribute__((format (printf, 1, 2))); SO_PUBLIC void LogMessage(const char*, ...) __attribute__((format (printf, 1, 2))); SO_PUBLIC void LogMessage(FILE*, const char*, ...) __attribute__((format (printf, 2, 3))); SO_PUBLIC void WarningMessage(const char*, ...) __attribute__((format (printf, 1, 2))); diff --git a/src/main.cc b/src/main.cc index 42db72182..fcb8696b6 100644 --- a/src/main.cc +++ b/src/main.cc @@ -822,9 +822,6 @@ static bool set_mode() return false; } - if ( sc->dump_config() ) - return false; - if ( just_validate(sc) ) { LogMessage("\nSnort successfully validated the configuration (with %u warnings).\n", diff --git a/src/main/CMakeLists.txt b/src/main/CMakeLists.txt index 954e543a9..502e5710d 100644 --- a/src/main/CMakeLists.txt +++ b/src/main/CMakeLists.txt @@ -21,8 +21,6 @@ add_library (main OBJECT analyzer.h analyzer_command.cc build.h - config_tree.cc - config_tree.h help.cc help.h modules.cc diff --git a/src/main/dev_notes.txt b/src/main/dev_notes.txt index c4cae9e04..c4a535fd2 100644 --- a/src/main/dev_notes.txt +++ b/src/main/dev_notes.txt @@ -63,11 +63,3 @@ information and management. Currently it is being used as a cross-platform mechanism for managing CPU affinity of threads, but it will be used in the future for NUMA (non-uniform memory access) awareness among other things. -Consolidated Config output - -The snort config parsed is stored as an internal tree structure within the Shell. -A unique shell is associated with every base and targeted policy file. -Every module in a lua file is represented by the General Tree. -The Tree node can be one of the following types: TreeConfigNode, ValueConfigNode. -The TreeConfigNode represents a table or list. -The ValueConfigNode represents a config value itself. diff --git a/src/main/shell.cc b/src/main/shell.cc index c8b1c9bcd..e757361f1 100644 --- a/src/main/shell.cc +++ b/src/main/shell.cc @@ -28,7 +28,7 @@ #include #include -#include "config_tree.h" +#include "dump_config/config_output.h" #include "log/messages.h" #include "lua/lua.h" #include "main/policy.h" @@ -47,6 +47,9 @@ using namespace std; string Shell::fatal; std::stack Shell::current_shells; +ConfigOutput* Shell::s_config_output = nullptr; +BaseConfigNode* Shell::s_current_node = nullptr; +bool Shell::s_close_table = true; // FIXIT-M Shell::panic() works on Linux but on OSX we can't throw from lua // to C++. unprotected lua calls could be wrapped in a pcall to ensure lua @@ -65,6 +68,15 @@ Shell* Shell::get_current_shell() return nullptr; } +void Shell::set_config_output(ConfigOutput* output) +{ s_config_output = output; } + +void Shell::clear_config_output() +{ + s_config_output = nullptr; + s_current_node = nullptr; +} + bool Shell::is_whitelisted(const std::string& key) { Shell* sh = Shell::get_current_shell(); @@ -112,6 +124,12 @@ void Shell::config_open_table(bool is_root_node, bool is_list, int idx, if ( p ) node_type = p->type; + if ( node_type == Parameter::PT_MULTI ) + { + s_close_table = false; + return; + } + if ( node_type == Parameter::PT_TABLE ) update_current_config_node(table_name); else @@ -126,18 +144,16 @@ void Shell::config_open_table(bool is_root_node, bool is_list, int idx, void Shell::add_config_child_node(const std::string& node_name, snort::Parameter::Type type) { - Shell* sh = Shell::get_current_shell(); - - if ( !sh ) + if ( !s_current_node ) return; std::string name; - if ( sh->s_current_node->get_name() != node_name ) + if ( s_current_node->get_name() != node_name ) name = node_name; - auto new_node = new TreeConfigNode(sh->s_current_node, name, type); - sh->s_current_node->add_child_node(new_node); - sh->s_current_node = new_node; + auto new_node = new TreeConfigNode(s_current_node, name, type); + s_current_node->add_child_node(new_node); + s_current_node = new_node; } void Shell::add_config_root_node(const std::string& root_name, snort::Parameter::Type node_type) @@ -148,74 +164,86 @@ void Shell::add_config_root_node(const std::string& root_name, snort::Parameter: return; sh->s_current_node = new TreeConfigNode(nullptr, root_name, node_type); - sh->config_trees.push_back(sh->s_current_node); + sh->config_data.add_config_tree(sh->s_current_node); } void Shell::update_current_config_node(const std::string& node_name) { - Shell* sh = Shell::get_current_shell(); - - if ( !sh ) - return; - - if ( !sh->s_current_node ) + if ( !s_current_node ) return; // node has been added during setting default options if ( !node_name.empty() ) - sh->s_current_node = sh->s_current_node->get_node(node_name); - else if ( sh->s_current_node->get_parent_node() and - sh->s_current_node->get_type() == Parameter::PT_TABLE and - !sh->s_current_node->get_name().empty() ) - sh->s_current_node = sh->s_current_node->get_parent_node(); + s_current_node = s_current_node->get_node(node_name); + else if ( s_current_node->get_parent_node() and + s_current_node->get_type() == Parameter::PT_TABLE and + !s_current_node->get_name().empty() ) + s_current_node = s_current_node->get_parent_node(); - assert(sh->s_current_node); + assert(s_current_node); } void Shell::config_close_table() { - Shell* sh = Shell::get_current_shell(); - - if ( !sh ) + if ( !s_close_table ) + { + s_close_table = true; return; + } - if ( !sh->s_current_node ) + if ( !s_current_node ) return; - sh->s_current_node = sh->s_current_node->get_parent_node(); + s_current_node = s_current_node->get_parent_node(); } -void Shell::set_config_value(const snort::Value& value) +void Shell::set_config_value(const std::string& fqn, const snort::Value& value) { - Shell* sh = Shell::get_current_shell(); - - if ( !sh ) - return; - - if ( !sh->s_current_node ) + if ( !s_current_node ) return; - // lua interpreter does not call open_table for simple list items like (string) - // we have to add tree node for this item too - if ( sh->s_current_node->get_type() == Parameter::PT_LIST ) + // lua interpreter does not call open_table for simple list items like (string) or + // special rule_state list items + // We have to add tree node for this item too + if ( s_current_node->get_type() == Parameter::PT_LIST ) { - add_config_child_node("", Parameter::PT_TABLE); - sh->s_current_node->add_child_node(new ValueConfigNode(sh->s_current_node, value)); - sh->s_current_node = sh->s_current_node->get_parent_node(); + auto node = s_current_node->get_node(""); + if ( !node || s_current_node->get_name().find(":") == std::string::npos ) + { + node = new TreeConfigNode(s_current_node, "", Parameter::PT_TABLE); + s_current_node->add_child_node(node); + } + + node->add_child_node(new ValueConfigNode(node, value)); return; } - + BaseConfigNode* child_node = nullptr; - for ( auto node : sh->s_current_node->get_children() ) + + std::string custom_name; + if ( strchr(value.get_name(), '$') ) + custom_name = fqn.substr(fqn.find_last_of(".") + 1); + + for ( auto node : s_current_node->get_children() ) { - child_node = node->get_node(value.get_name()); + if ( (node->get_type() == Parameter::PT_MULTI) and (node->get_name() == value.get_name()) ) + { + child_node = node; + break; + } + + if ( !custom_name.empty() ) + child_node = node->get_node(custom_name); + else + child_node = node->get_node(value.get_name()); + if ( child_node ) break; } if ( !child_node ) - sh->s_current_node->add_child_node(new ValueConfigNode(sh->s_current_node, value)); + s_current_node->add_child_node(new ValueConfigNode(s_current_node, value, custom_name)); else child_node->set_value(value); } @@ -278,7 +306,8 @@ static void load_string(lua_State* L, const char* s) // public methods //------------------------------------------------------------------------- -Shell::Shell(const char* s, bool load_defaults) +Shell::Shell(const char* s, bool load_defaults) : + config_data(s) { // FIXIT-M should wrap in Lua::State lua = luaL_newstate(); @@ -314,6 +343,7 @@ Shell::~Shell() void Shell::set_file(const char* s) { file = s; + config_data.file_name = file; } void Shell::set_overrides(const char* s) @@ -383,12 +413,9 @@ bool Shell::configure(SnortConfig* sc, bool is_fatal, bool is_root) clear_whitelist(); - if ( SnortConfig::get_conf()->dump_config() ) - { - sort_config(); - print_config_text(); - clear_config_tree(); - } + auto config_output = Shell::get_current_shell()->s_config_output; + if ( config_output ) + config_output->dump_config(config_data); current_shells.pop(); @@ -459,29 +486,6 @@ static void print_list(const Shell::Whitelist& wlist, const std::string& msg) //------------------------------------------------------------------------- // private methods //------------------------------------------------------------------------- -void Shell::sort_config() -{ - config_trees.sort([](const BaseConfigNode* l, const BaseConfigNode* r) - { return l->get_name() < r->get_name(); }); -} - -void Shell::print_config_text() const -{ - std::string output("consolidated config for "); - output += file; - LogConfig("%s\n", output.c_str()); - - for ( const auto config_tree: config_trees ) - ConfigTextFormat::print(config_tree, config_tree->get_name()); -} - -void Shell::clear_config_tree() -{ - for ( auto config_tree: config_trees ) - BaseConfigNode::clear_nodes(config_tree); - - config_trees.clear(); -} void Shell::print_whitelist() const { diff --git a/src/main/shell.h b/src/main/shell.h index ef6883d98..e72e056a9 100644 --- a/src/main/shell.h +++ b/src/main/shell.h @@ -27,11 +27,13 @@ #include #include +#include "dump_config/config_data.h" #include "framework/parameter.h" struct lua_State; class BaseConfigNode; +class ConfigOutput; namespace snort { @@ -70,10 +72,12 @@ public: static void config_open_table(bool is_root_node, bool is_list, int idx, const std::string& table_name, const snort::Parameter* p); - static void set_config_value(const snort::Value& value); + static void set_config_value(const std::string& fqn, const snort::Value& value); static void add_config_child_node(const std::string& node_name, snort::Parameter::Type type); static void update_current_config_node(const std::string& node_name = ""); static void config_close_table(); + static void set_config_output(ConfigOutput* config_output); + static void clear_config_output(); private: static void add_config_root_node(const std::string& root_name, snort::Parameter::Type type); @@ -85,6 +89,9 @@ private: private: static std::string fatal; static std::stack current_shells; + static ConfigOutput* s_config_output; + static BaseConfigNode* s_current_node; + static bool s_close_table; private: void clear_whitelist() @@ -106,10 +113,6 @@ private: void print_whitelist() const; void whitelist_update(const char* keyword, bool is_prefix); - void sort_config(); - void print_config_text() const; - void clear_config_tree(); - private: bool loaded; bool bootstrapped = false; @@ -120,8 +123,7 @@ private: Whitelist whitelist; Whitelist internal_whitelist; Whitelist whitelist_prefixes; - std::list config_trees; - BaseConfigNode* s_current_node = nullptr; + ConfigData config_data; }; #endif diff --git a/src/main/snort.cc b/src/main/snort.cc index 28e665b43..432cbf5c5 100644 --- a/src/main/snort.cc +++ b/src/main/snort.cc @@ -426,7 +426,7 @@ void Snort::cleanup() SFDAQ::term(); FileService::close(); - if ( !SnortConfig::get_conf()->validation_mode() ) // FIXIT-M ideally the check is in one place + if ( !SnortConfig::get_conf()->test_mode() ) // FIXIT-M ideally the check is in one place PrintStatistics(); CloseLogger(); diff --git a/src/main/snort_config.cc b/src/main/snort_config.cc index 46e9abccb..b22540e59 100644 --- a/src/main/snort_config.cc +++ b/src/main/snort_config.cc @@ -31,6 +31,8 @@ #include "detection/detection_engine.h" #include "detection/fp_config.h" #include "detection/fp_create.h" +#include "dump_config/json_config_output.h" +#include "dump_config/text_config_output.h" #include "filters/detection_filter.h" #include "filters/rate_filter.h" #include "filters/sfrf.h" @@ -946,6 +948,25 @@ bool SnortConfig::get_default_rule_state() const return true; } +ConfigOutput* SnortConfig::create_config_output() const +{ + ConfigOutput* output = nullptr; + + switch (dump_config_type) + { + case DUMP_CONFIG_JSON_ALL: + output = new JsonAllConfigOutput(); + break; + case DUMP_CONFIG_TEXT: + output = new TextConfigOutput(); + break; + default: + break; + } + + return output; +} + bool SnortConfig::tunnel_bypass_enabled(uint16_t proto) const { return !((tunnel_mask & proto) or SFDAQ::get_tunnel_bypass(proto)); diff --git a/src/main/snort_config.h b/src/main/snort_config.h index 30a7fbbec..f7d89d5b7 100644 --- a/src/main/snort_config.h +++ b/src/main/snort_config.h @@ -81,7 +81,6 @@ enum RunFlag RUN_FLAG__IP_FRAGS_ONLY = 0x08000000, RUN_FLAG__DUMP_RULE_STATE = 0x10000000, - RUN_FLAG__DUMP_CONFIG = 0x20000000, }; enum OutputFlag @@ -126,6 +125,14 @@ enum TunnelFlags TUNNEL_VXLAN = 0x100 }; +enum DumpConfigType +{ + DUMP_CONFIG_NONE = 0, + DUMP_CONFIG_JSON_ALL, + DUMP_CONFIG_TEXT +}; + +class ConfigOutput; class FastPatternConfig; class RuleStateMap; class TraceConfig; @@ -424,6 +431,8 @@ public: bool cloned = false; Plugins* plugins = nullptr; SoRules* so_rules = nullptr; + + DumpConfigType dump_config_type = DUMP_CONFIG_NONE; private: bool active_enabled = false; std::list reload_tuners; @@ -501,8 +510,8 @@ public: { return address_anomaly_check_enabled; } // mode related - bool dump_config() const - { return run_flags & RUN_FLAG__DUMP_CONFIG; } + bool dump_config_mode() const + { return dump_config_type > DUMP_CONFIG_NONE; } bool dump_msg_map() const { return run_flags & RUN_FLAG__DUMP_MSG_MAP; } @@ -522,14 +531,11 @@ public: bool test_mode() const { return run_flags & RUN_FLAG__TEST; } - bool validation_mode() const - { return test_mode() or dump_config(); } - bool mem_check() const - { return (run_flags & RUN_FLAG__MEM_CHECK) and !dump_config(); } + { return run_flags & RUN_FLAG__MEM_CHECK; } bool daemon_mode() const - { return (run_flags & RUN_FLAG__DAEMON) and !dump_config(); } + { return run_flags & RUN_FLAG__DAEMON; } bool read_mode() const { return run_flags & RUN_FLAG__READ; } @@ -701,6 +707,8 @@ public: bool get_default_rule_state() const; + ConfigOutput* create_config_output() const; + SO_PUBLIC bool tunnel_bypass_enabled(uint16_t proto) const; // FIXIT-L snort_conf needed for static hash before initialized diff --git a/src/main/snort_module.cc b/src/main/snort_module.cc index dcbff4e20..aa3adb786 100644 --- a/src/main/snort_module.cc +++ b/src/main/snort_module.cc @@ -347,6 +347,9 @@ static const Parameter s_params[] = { "--dump-builtin-rules", Parameter::PT_STRING, "(optional)", nullptr, "[] output stub rules for selected modules" }, + { "--dump-config", Parameter::PT_SELECT, "all", nullptr, + "dump config in json format" }, + { "--dump-config-text", Parameter::PT_IMPLIED, nullptr, nullptr, "dump config in text format" }, @@ -871,10 +874,19 @@ bool SnortModule::set(const char*, Value& v, SnortConfig* sc) else if ( v.is("--dump-builtin-rules") ) dump_builtin_rules(sc, v.get_string()); + else if ( v.is("--dump-config") ) + { + sc->set_quiet(true); + sc->run_flags |= RUN_FLAG__TEST; + if ( v.get_as_string() == "all" ) + sc->dump_config_type = DUMP_CONFIG_JSON_ALL; + } + else if ( v.is("--dump-config-text") ) { - sc->run_flags |= RUN_FLAG__DUMP_CONFIG; - sc->set_quiet(true); + sc->set_quiet(true); + sc->run_flags |= RUN_FLAG__TEST; + sc->dump_config_type = DUMP_CONFIG_TEXT; } else if ( v.is("--dump-dynamic-rules") ) diff --git a/src/managers/module_manager.cc b/src/managers/module_manager.cc index 531844c18..5276b1b08 100644 --- a/src/managers/module_manager.cc +++ b/src/managers/module_manager.cc @@ -455,8 +455,8 @@ static bool set_var(const char* fqn, Value& v) static bool set_param(Module* mod, const char* fqn, Value& val) { - if ( s_config->dump_config() ) - Shell::set_config_value(val); + if ( s_config->dump_config_mode() ) + Shell::set_config_value(fqn, val); if ( !mod->verified_set(fqn, val, s_config) ) { @@ -595,7 +595,7 @@ static bool begin(Module* m, const Parameter* p, const char* s, int idx, int dep { const Parameter* table_item_params = reinterpret_cast(p->range); - if ( s_config->dump_config() ) + if ( s_config->dump_config_mode() ) Shell::add_config_child_node(get_sub_table(fqn), p->type); if ( !begin(m, table_item_params, fqn.c_str(), idx, depth+1) ) @@ -634,7 +634,7 @@ static bool begin(Module* m, const Parameter* p, const char* s, int idx, int dep } ++p; } - if ( s_config->dump_config() ) + if ( s_config->dump_config_mode() ) Shell::update_current_config_node(); return true; } @@ -764,7 +764,7 @@ SO_PUBLIC bool open_table(const char* s, int idx) s_current = unique_key; } - if ( s_config->dump_config() ) + if ( s_config->dump_config_mode() ) { std::string table_name = get_sub_table(s); bool is_top_level = false; @@ -817,7 +817,7 @@ SO_PUBLIC void close_table(const char* s, int idx) s_type.clear(); } - if ( s_config->dump_config() ) + if ( s_config->dump_config_mode() ) Shell::config_close_table(); } diff --git a/src/parser/parser.cc b/src/parser/parser.cc index 5c1d4e5bf..ce857a4f9 100644 --- a/src/parser/parser.cc +++ b/src/parser/parser.cc @@ -33,6 +33,7 @@ #include "detection/fp_config.h" #include "detection/rules.h" #include "detection/sfrim.h" +#include "dump_config/config_output.h" #include "filters/detection_filter.h" #include "filters/rate_filter.h" #include "filters/sfthreshold.h" @@ -320,6 +321,7 @@ SnortConfig* ParseSnortConf(const SnortConfig* boot_conf, const char* fname, boo sc->output_flags = boot_conf->output_flags; sc->logging_flags = boot_conf->logging_flags; sc->tweaks = boot_conf->tweaks; + sc->dump_config_type = boot_conf->dump_config_type; VarNode* tmp = boot_conf->var_list; @@ -357,6 +359,9 @@ SnortConfig* ParseSnortConf(const SnortConfig* boot_conf, const char* fname, boo sh->set_file(fname); } + bool parse_file_failed = false; + auto output = SnortConfig::get_conf()->create_config_output(); + Shell::set_config_output(output); for ( unsigned i = 0; true; i++ ) { sh = sc->policy_map->get_shell(i); @@ -367,10 +372,18 @@ SnortConfig* ParseSnortConf(const SnortConfig* boot_conf, const char* fname, boo set_policies(sc, sh); if (!parse_file(sc, sh, is_fatal, (i == 0))) - return sc; + { + parse_file_failed = true; + break; + } } - set_default_policy(sc); + delete output; + Shell::clear_config_output(); + + if ( !parse_file_failed ) + set_default_policy(sc); + return sc; } diff --git a/src/trace/trace_module.cc b/src/trace/trace_module.cc index 6d4a0353a..c99f112a5 100644 --- a/src/trace/trace_module.cc +++ b/src/trace/trace_module.cc @@ -178,20 +178,20 @@ bool TraceModule::end(const char* fqn, int, SnortConfig* sc) { assert(trace_parser); - if ( sc->dump_config() ) + if ( sc->dump_config_mode() ) trace_parser->clear_traces(); else { switch ( log_output_type ) { - case OUTPUT_TYPE_STDOUT: - trace_parser->get_trace_config()->logger_factory = new StdoutLoggerFactory(); - break; - case OUTPUT_TYPE_SYSLOG: - trace_parser->get_trace_config()->logger_factory = new SyslogLoggerFactory(); - break; - default: - break; + case OUTPUT_TYPE_STDOUT: + trace_parser->get_trace_config()->logger_factory = new StdoutLoggerFactory(); + break; + case OUTPUT_TYPE_SYSLOG: + trace_parser->get_trace_config()->logger_factory = new SyslogLoggerFactory(); + break; + default: + break; } // "output=syslog" config override case diff --git a/src/trace/trace_swap.cc b/src/trace/trace_swap.cc index cc9341485..5b1879c88 100644 --- a/src/trace/trace_swap.cc +++ b/src/trace/trace_swap.cc @@ -220,7 +220,7 @@ static int set(lua_State* L) !trace_parser.set_traces(module_name, val) ) { LogMessage("== invalid trace value is provided: %s.%s.%s = %s\n", - root_element_key, module_name, val_name, val.get_as_string()); + root_element_key, module_name, val_name, val.get_as_string().c_str()); parse_err = true; } @@ -259,7 +259,7 @@ static int set(lua_State* L) !trace_parser.set_constraints(val) ) { LogMessage("== invalid constraints value is provided: %s.%s = %s\n", - root_element_key, val_name, val.get_as_string()); + root_element_key, val_name, val.get_as_string().c_str()); parse_err = true; }