From: Bhagya Tholpady (bbantwal) Date: Tue, 28 Jul 2020 15:43:37 +0000 (+0000) Subject: Merge pull request #2329 in SNORT/snort3 from ~OSHUMEIK/snort3:policy_custom_variable... X-Git-Tag: 3.0.2-3~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cfe3418ffe585750f8b29ced606805487e13f6ec;p=thirdparty%2Fsnort3.git Merge pull request #2329 in SNORT/snort3 from ~OSHUMEIK/snort3:policy_custom_variables to master Squashed commit of the following: commit a0f0b195faee442a2ee63fec1e8bdf20e1b29966 Author: Oleksii Khomiakovskyi Date: Mon Jul 6 17:23:23 2020 +0300 ips: add support for variables with each ips policy Custom variables are for rules only. Snort2lua creates custom variables for each ips entry. --- diff --git a/doc/upgrade/differences.txt b/doc/upgrade/differences.txt index c148e7f66..f267f771e 100644 --- a/doc/upgrade/differences.txt +++ b/doc/upgrade/differences.txt @@ -141,6 +141,8 @@ Some things Snort++ can do today that Snort can not do as well: * stream5_tcp: prune_log_max deleted; to be replaced with histogram * stream5_tcp: max_active_responses, min_response_seconds moved to active.max_responses, min_interval +* ips policies support snort variables (_PATH, _PORT, _NET, _SERVER), + ips = { variables = { var1 = expr1, var2 = expr2, ... } } === Rules diff --git a/lua/snort_defaults.lua b/lua/snort_defaults.lua index 5a41e12ac..f95698699 100644 --- a/lua/snort_defaults.lua +++ b/lua/snort_defaults.lua @@ -34,7 +34,7 @@ BLACK_LIST_PATH = '../lists' -- default networks --------------------------------------------------------------------------- --- List of DNS servers on your network +-- List of DNS servers on your network DNS_SERVERS = HOME_NET -- List of ftp servers on your network @@ -49,7 +49,7 @@ SIP_SERVERS = HOME_NET -- List of SMTP servers on your network SMTP_SERVERS = HOME_NET --- List of sql servers on your network +-- List of sql servers on your network SQL_SERVERS = HOME_NET -- List of ssh servers on your network @@ -72,7 +72,7 @@ HTTP_PORTS = 3702 4343 4848 5250 6988 7000 7001 7144 7145 7510 7777 7779 8000 8008 8014 8028 8080 8085 8088 8090 8118 8123 8180 8181 8243 8280 8300 8800 8888 8899 9000 9060 9080 9090 9091 9443 9999 11371 34443 34444 41080 - 50002 55555 + 50002 55555 ]] -- List of ports you run mail servers on @@ -90,6 +90,37 @@ SSH_PORTS = ' 22' -- List of ports for file inspection FILE_DATA_PORTS = HTTP_PORTS .. MAIL_PORTS +--------------------------------------------------------------------------- +-- default variables +--------------------------------------------------------------------------- + +default_variables = { + RULE_PATH = RULE_PATH, + BUILTIN_RULE_PATH = BUILTIN_RULE_PATH, + PLUGIN_RULE_PATH = PLUGIN_RULE_PATH, + WHITE_LIST_PATH = WHITE_LIST_PATH, + BLACK_LIST_PATH = BLACK_LIST_PATH, + + HOME_NET = HOME_NET, + EXTERNAL_NET = EXTERNAL_NET, + DNS_SERVERS = DNS_SERVERS, + FTP_SERVERS = FTP_SERVERS, + HTTP_SERVERS = HTTP_SERVERS, + SIP_SERVERS = SIP_SERVERS, + SMTP_SERVERS = SMTP_SERVERS, + SQL_SERVERS = SQL_SERVERS, + SSH_SERVERS = SSH_SERVERS, + TELNET_SERVERS = TELNET_SERVERS, + + FTP_PORTS = FTP_PORTS, + HTTP_PORTS = HTTP_PORTS, + MAIL_PORTS = MAIL_PORTS, + ORACLE_PORTS = ORACLE_PORTS, + SIP_PORTS = SIP_PORTS, + SSH_PORTS = SSH_PORTS, + FILE_DATA_PORTS = FILE_DATA_PORTS, +} + --------------------------------------------------------------------------- -- default ftp server --------------------------------------------------------------------------- @@ -104,7 +135,7 @@ ftp_default_cmds = XSEM XSEN XSHA1 XSHA256 ]] -ftp_default_data_chan_cmds = +ftp_default_data_chan_cmds = [[ PORT PASV LPRT LPSV EPRT EPSV ]] @@ -114,28 +145,28 @@ ftp_default_data_xfer_cmds = RETR STOR STOU APPE LIST NLST ]] -ftp_default_file_put_cmds = +ftp_default_file_put_cmds = [[ STOR STOU ]] -ftp_default_file_get_cmds = +ftp_default_file_get_cmds = [[ RETR ]] -ftp_default_login_cmds = +ftp_default_login_cmds = [[ USER PASS ]] -ftp_default_encr_cmds = +ftp_default_encr_cmds = [[ AUTH ]] -ftp_format_commands = -[[ +ftp_format_commands = +[[ ACCT ADAT ALLO APPE AUTH CEL CLNT CMD CONF CWD DELE ENC EPRT EPSV ESTP HELP LANG LIST LPRT MACB MAIL MDTM MIC MKD MLSD MLST MODE NLST OPTS PASS PBSZ PORT PROT REST RETR RMD RNFR RNTO SDUP SITE SIZE SMNT STAT @@ -358,7 +389,7 @@ default_wizard = }, hexes = { - { service = 'dnp3', proto = 'tcp', client_first = true, + { service = 'dnp3', proto = 'tcp', client_first = true, to_server = { '|05 64|' }, to_client = { '|05 64|' } }, { service = 'http2', proto = 'tcp', client_first = true, @@ -1157,6 +1188,7 @@ default_whitelist = ip_med_sweep ip_med_dist ip_hi_proto ip_hi_decoy ip_hi_sweep ip_hi_dist icmp_low_sweep icmp_med_sweep icmp_hi_sweep default_hi_port_scan default_med_port_scan default_low_port_scan + default_variables ]] snort_whitelist_append(default_whitelist) diff --git a/src/main/modules.cc b/src/main/modules.cc index b67322867..4a463d4e6 100644 --- a/src/main/modules.cc +++ b/src/main/modules.cc @@ -51,6 +51,7 @@ #include "parser/parse_conf.h" #include "parser/parse_ip.h" #include "parser/parser.h" +#include "parser/vars.h" #include "payload_injector/payload_injector_module.h" #include "profiler/profiler.h" #include "search_engines/pat_stats.h" @@ -1167,6 +1168,21 @@ bool InspectionModule::set(const char*, Value& v, SnortConfig* sc) // Ips policy module //------------------------------------------------------------------------- +static void set_var(SnortConfig* sc, const char* fqn, Value& v) +{ + const char* ptr = strrchr(fqn, '.'); + assert(ptr); + SetVar(sc, ptr + 1, v.get_string()); +} + +static const Parameter variable_params[] = +{ + { "$var" , Parameter::PT_STRING, nullptr, nullptr, + "IPS policy variable" }, + + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + static const Parameter ips_params[] = { { "default_rule_state", Parameter::PT_ENUM, "no | yes | inherit", "inherit", @@ -1202,6 +1218,9 @@ static const Parameter ips_params[] = "IPS policy uuid" }, #endif + { "variables", Parameter::PT_TABLE, variable_params, nullptr, + "defines IPS policy variables" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } }; @@ -1213,12 +1232,27 @@ class IpsModule : public Module public: IpsModule() : Module("ips", ips_help, ips_params) { } bool set(const char*, Value&, SnortConfig*) override; + bool matches(const char*, std::string&) override; Usage get_usage() const override { return DETECT; } }; -bool IpsModule::set(const char*, Value& v, SnortConfig* sc) +bool IpsModule::matches(const char* param, std::string& name) +{ + if ( strcmp(param, "$var") ) + return false; + + if ( name.find("_PATH") != string::npos or + name.find("_PORT") != string::npos or + name.find("_NET") != string::npos or + name.find("_SERVER") != string::npos ) + return true; + + return false; +} + +bool IpsModule::set(const char* fqn, Value& v, SnortConfig* sc) { IpsPolicy* p = get_ips_policy(); @@ -1263,6 +1297,9 @@ bool IpsModule::set(const char*, Value& v, SnortConfig* sc) } #endif + else if ( strstr(fqn, "variables") ) + set_var(sc, fqn, v); + else return false; diff --git a/src/managers/module_manager.cc b/src/managers/module_manager.cc index 93d63a800..fb9371269 100644 --- a/src/managers/module_manager.cc +++ b/src/managers/module_manager.cc @@ -188,19 +188,6 @@ static void set_top(string& fqn) fqn.erase(pos); } -static void trace(const char* s, const char* fqn, Value& v) -{ -#if 1 - if ( s ) - return; -#endif - - if ( v.get_type() == Value::VT_STR ) - printf("%s: %s = '%s'\n", s, fqn, v.get_string()); - else - printf("%s: %s = " STDi64 "\n", s, fqn, v.get_int64()); -} - static ModHook* get_hook(const char* s) { auto mh = s_modules.find(s); @@ -407,54 +394,6 @@ static const Parameter* get_params( return get_params(new_fqn, m, p, idx); } -// FIXIT-M vars may have been defined on command line. that mechanism will -// be replaced with pulling a Lua chunk from the command line and stuffing -// into L before setting configs; that will overwrite -// -// FIXIT-M should only need one table with dynamically typed vars -// -// -// FIXIT-M this is a hack to tell vars by naming convention; with one table -// this is obviated but if multiple tables are kept might want to change -// these to a module with parameters -// -// FIXIT-L presently no way to catch errors like EXTERNAL_NET = not HOME_NET -// which becomes a bool var and is ignored. -static bool set_var(const char* fqn, Value& val) -{ - if ( val.get_type() != Value::VT_STR ) - return false; - - if ( get_ips_policy() == nullptr ) - return true; - - trace("var", fqn, val); - const char* s = val.get_string(); - - if ( strstr(fqn, "PATH") ) - AddVarToTable(s_config, fqn, s); - - else if ( strstr(fqn, "PORT") ) - PortVarDefine(s_config, fqn, s); - - else if ( strstr(fqn, "NET") || strstr(fqn, "SERVER") ) - ParseIpVar(s_config, fqn, s); - - return true; -} - -static bool set_param(Module* mod, const char* fqn, Value& val) -{ - if ( !mod->verified_set(fqn, val, s_config) ) - { - ParseError("%s is invalid", fqn); - ++s_errors; - } - - trace("par", fqn, val); - return true; -} - static bool ignored(const char* fqn) { static const char* ignore = nullptr; @@ -481,6 +420,41 @@ static bool ignored(const char* fqn) return true; } +// FIXIT-M vars may have been defined on command line. that mechanism will +// be replaced with pulling a Lua chunk from the command line and stuffing +// into L before setting configs; that will overwrite +// +// FIXIT-L presently no way to catch errors like EXTERNAL_NET = not HOME_NET +// which becomes a bool var and is ignored. +static bool set_var(const char* fqn, Value& v) +{ + bool to_be_set = v.get_type() == Value::VT_STR; + + if ( to_be_set ) + { + if ( get_ips_policy() != nullptr ) + SetVar(s_config, fqn, v.get_string()); + } + else + { + if ( !ignored(fqn) ) + ParseWarning(WARN_SYMBOLS, "unknown symbol %s", fqn); + } + + return to_be_set; +} + +static bool set_param(Module* mod, const char* fqn, Value& val) +{ + if ( !mod->verified_set(fqn, val, s_config) ) + { + ParseError("%s is invalid", fqn); + ++s_errors; + } + + return true; +} + static bool set_value(const char* fqn, Value& v) { string t = fqn; @@ -493,13 +467,7 @@ static bool set_value(const char* fqn, Value& v) Module* mod = ModuleManager::get_module(key.c_str()); if ( !mod ) - { - bool found = set_var(fqn, v); - - if ( !found && !ignored(fqn) ) - ParseWarning(WARN_SYMBOLS, "unknown symbol %s", fqn); - return found; - } + return set_var(fqn, v); const Parameter* p; auto a = s_pmap.find(t); diff --git a/src/parser/parse_conf.cc b/src/parser/parse_conf.cc index 134619305..af7f2a041 100644 --- a/src/parser/parse_conf.cc +++ b/src/parser/parse_conf.cc @@ -201,6 +201,26 @@ void parse_include(SnortConfig* sc, const char* arg) pop_parse_location(); } +// FIXIT-M should only need one table with dynamically typed vars +// +// FIXIT-M this is a hack to tell vars by naming convention; with one table +// this is obviated but if multiple tables are kept might want to change +// these to a module with parameters +void SetVar(snort::SnortConfig* sc, const char* name, const char* value) +{ + if ( strstr(name, "_PATH") ) + AddVarToTable(sc, name, value); + + else if ( strstr(name, "_PORT") ) + PortVarDefine(sc, name, value); + + else if ( strstr(name, "_NET") ) + ParseIpVar(sc, name, value); + + else if ( strstr(name, "_SERVER") ) + ParseIpVar(sc, name, value); +} + void ParseIpVar(SnortConfig* sc, const char* var, const char* val) { int ret; diff --git a/src/parser/parse_conf.h b/src/parser/parse_conf.h index 9e7e10df2..065bab0c9 100644 --- a/src/parser/parse_conf.h +++ b/src/parser/parse_conf.h @@ -41,6 +41,7 @@ const char* get_config_file(const char* arg, std::string& file); void parse_rules_file(snort::SnortConfig*, const char* fname); void parse_rules_string(snort::SnortConfig*, const char* str); +void SetVar(snort::SnortConfig*, const char* name, const char* value); void ParseIpVar(snort::SnortConfig*, const char* name, const char* s); void parse_include(snort::SnortConfig*, const char*); diff --git a/src/parser/parse_ip.cc b/src/parser/parse_ip.cc index 674dc5c7d..73a92be8c 100644 --- a/src/parser/parse_ip.cc +++ b/src/parser/parse_ip.cc @@ -34,14 +34,10 @@ sfip_var_t* sfip_var_from_string(const char* addr, const char* caller) { sfip_var_t* ret; int ret_code; - vartable_t* ip_vartable = nullptr; - - if (get_ips_policy()) - ip_vartable = get_ips_policy()->ip_vartable; ret = (sfip_var_t*)snort_calloc(sizeof(sfip_var_t)); - if ((ret_code = sfvt_add_to_var(ip_vartable, ret, addr)) != SFIP_SUCCESS) + if ((ret_code = sfvt_add_to_var(nullptr, ret, addr)) != SFIP_SUCCESS) { if (ret_code == SFIP_LOOKUP_FAILURE) { diff --git a/src/parser/vars.cc b/src/parser/vars.cc index 5e7f67b6a..131a8f47a 100644 --- a/src/parser/vars.cc +++ b/src/parser/vars.cc @@ -159,18 +159,14 @@ int PortVarDefine(SnortConfig* sc, const char* name, const char* s) if ( rstat < 0 ) { ParseError("PortVarTableAdd failed with '%s', exiting.", po->name); + PortObjectFree(po); } else if ( rstat > 0 ) { ParseWarning(WARN_VARS, "PortVar '%s', already defined.", po->name); + PortObjectFree(po); } -#if 0 - LogMessage("PortVar '%s' defined : ",po->name); - PortObjectPrintPortsRaw(po); - LogMessage("\n"); -#endif - return 0; } @@ -589,30 +585,6 @@ VarEntry* VarDefine( else p->id = var_id; -#if 0 - vlen = strlen(value); - LogMessage("Var '%s' defined, value len = %d chars", p->name, vlen); - - if ( vlen < 64 ) - { - LogMessage(", value = %s\n", value); - } - else - { - LogMessage("\n"); - n = 128; - s = value; - while (vlen) - { - if ( n > vlen ) - n = vlen; - LogMessage(" %.*s\n", n, s); - s += n; - vlen -= n; - } - } -#endif - return p; } diff --git a/src/sfip/sf_ipvar.cc b/src/sfip/sf_ipvar.cc index 187bf62e2..c52b7fbb9 100644 --- a/src/sfip/sf_ipvar.cc +++ b/src/sfip/sf_ipvar.cc @@ -1340,6 +1340,55 @@ TEST_CASE("SfIpVarListMerge", "[SfIpVar]") sfvt_free_table(table); } + + SECTION("merge without table") + { + SfIp* ip; + + var2 = (sfip_var_t*)snort_calloc(sizeof(sfip_var_t)); + table = sfvt_alloc_table(); + + // 'foo' variable + CHECK(sfvt_add_str(table, "foo 1.0.0.1", &var1) == SFIP_SUCCESS); + + // no table used + CHECK(sfvt_add_to_var(nullptr, var2, "1.0.0.2") == SFIP_SUCCESS); + CHECK(sfvt_add_to_var(nullptr, var2, "$foo") == SFIP_LOOKUP_UNAVAILABLE); + CHECK(sfvt_add_to_var(nullptr, var2, "$moo") == SFIP_LOOKUP_UNAVAILABLE); + + print_var_list(var2->head); + CHECK(!strcmp("1.0.0.2", sfipvar_test_buff)); + + ip = (SfIp *)snort_alloc(sizeof(SfIp)); + CHECK(ip->set("1.0.0.1") == SFIP_SUCCESS); + CHECK((sfvar_ip_in(var2, ip) == false)); + snort_free(ip); + + ip = (SfIp *)snort_alloc(sizeof(SfIp)); + CHECK(ip->set("1.0.0.2") == SFIP_SUCCESS); + CHECK((sfvar_ip_in(var2, ip) == true)); + snort_free(ip); + + // using table + CHECK(sfvt_add_to_var(table, var2, "$foo") == SFIP_SUCCESS); + CHECK(sfvt_add_to_var(table, var2, "$moo") == SFIP_LOOKUP_FAILURE); + + print_var_list(var2->head); + CHECK(!strcmp("1.0.0.1,1.0.0.2", sfipvar_test_buff)); + + ip = (SfIp *)snort_alloc(sizeof(SfIp)); + CHECK(ip->set("1.0.0.1") == SFIP_SUCCESS); + CHECK((sfvar_ip_in(var2, ip) == true)); + snort_free(ip); + + ip = (SfIp *)snort_alloc(sizeof(SfIp)); + CHECK(ip->set("1.0.0.2") == SFIP_SUCCESS); + CHECK((sfvar_ip_in(var2, ip) == true)); + snort_free(ip); + + sfvar_free(var2); + sfvt_free_table(table); + } } TEST_CASE("SfIpVarCopyAddCompare", "[SfIpVar]") diff --git a/tools/snort2lua/data/dt_data.cc b/tools/snort2lua/data/dt_data.cc index 17673972c..6818eef65 100644 --- a/tools/snort2lua/data/dt_data.cc +++ b/tools/snort2lua/data/dt_data.cc @@ -263,8 +263,19 @@ void DataApi::failed_conversion(const std::istringstream& stream, const std::str errors->add_text("^^^^ unknown_syntax=" + unknown_option); } +static bool is_local_variable(const std::string& name) +{ + return name.find("_PATH") != std::string::npos + || name.find("_PORT") != std::string::npos + || name.find("_NET") != std::string::npos + || name.find("_SERVER") != std::string::npos; +} + void DataApi::set_variable(const std::string& name, const std::string& value, bool quoted) { + if (is_local_variable(name)) + local_vars.push_back(name); + Variable* var = new Variable(name); vars.push_back(var); var->set_value(value, quoted); @@ -276,6 +287,9 @@ bool DataApi::add_variable(const std::string& name, const std::string& value) if (name == v->get_name()) return v->add_value(value); + if (is_local_variable(name)) + local_vars.push_back(name); + Variable* var = new Variable(name); vars.push_back(var); return var->add_value(value); @@ -311,7 +325,7 @@ void DataApi::add_comment(const std::string& c) void DataApi::add_unsupported_comment(const std::string& c) { unsupported->add_text(c); } -void DataApi::print_errors(std::ostream& out) +void DataApi::print_errors(std::ostream& out) const { if (is_default_mode() && !errors->empty()) @@ -320,7 +334,7 @@ void DataApi::print_errors(std::ostream& out) } } -void DataApi::print_data(std::ostream& out) +void DataApi::print_data(std::ostream& out) const { for (Variable* v : vars) out << (*v) << "\n\n"; @@ -329,18 +343,29 @@ void DataApi::print_data(std::ostream& out) out << (*i) << "\n\n"; } -void DataApi::print_comments(std::ostream& out) +void DataApi::print_comments(std::ostream& out) const { if (is_default_mode() && !comments->empty()) out << (*comments) << "\n"; } -void DataApi::print_unsupported(std::ostream& out) +void DataApi::print_unsupported(std::ostream& out) const { if (is_default_mode() && !unsupported->empty()) out << (*unsupported) << "\n"; } +void DataApi::print_local_variables(std::ostream& out) const +{ + if (local_vars.empty()) + return; + + out << "local_variables =\n{\n"; + for (const auto& v : local_vars) + out << " " << v << " = " << v << ",\n"; + out << "}\n\n"; +} + void DataApi::swap_conf_data(std::vector& new_vars, std::vector& new_includes, Comments*& new_comments, Comments*& new_unsupported) diff --git a/tools/snort2lua/data/dt_data.h b/tools/snort2lua/data/dt_data.h index 3d774d65f..a6f3a2b0f 100644 --- a/tools/snort2lua/data/dt_data.h +++ b/tools/snort2lua/data/dt_data.h @@ -81,10 +81,11 @@ public: void reset_state(); // Output Functions - void print_errors(std::ostream&); - void print_data(std::ostream&); - void print_comments(std::ostream& out); - void print_unsupported(std::ostream& out); + void print_errors(std::ostream&) const; + void print_data(std::ostream&) const; + void print_comments(std::ostream& out) const; + void print_unsupported(std::ostream& out) const; + void print_local_variables(std::ostream&) const; // have there been any failed conversion? bool failed_conversions() const; @@ -93,6 +94,9 @@ public: bool empty() const { return vars.empty() && includes.empty(); } + bool has_local_vars() const + { return !local_vars.empty(); } + // functions specifically useful when parsing includes. // allows for easy swapping of data. These two functions // swap data which will be printed in 'print_rules()' and @@ -141,6 +145,7 @@ private: std::vector vars; std::vector includes; + std::vector local_vars; Comments* comments; Comments* errors; Comments* unsupported; diff --git a/tools/snort2lua/helpers/converter.cc b/tools/snort2lua/helpers/converter.cc index c0b05934b..c34b2132f 100644 --- a/tools/snort2lua/helpers/converter.cc +++ b/tools/snort2lua/helpers/converter.cc @@ -31,8 +31,10 @@ #include "helpers/converter.h" #include "conversion_state.h" #include "data/data_types/dt_comment.h" +#include "data/data_types/dt_include.h" #include "data/data_types/dt_rule.h" #include "data/data_types/dt_table.h" +#include "data/data_types/dt_var.h" #include "helpers/s2l_util.h" #include "helpers/util_binder.h" #include "init_state.h" @@ -173,12 +175,18 @@ int Converter::parse_include_file(const std::string& input_file) rule_api.include_rule_file(input_file + ".rules"); } + for (auto v : vars) + delete v; + for (auto r : rules) delete r; for (auto t : tables) delete t; + for (auto i : includes) + delete i; + return rc; } @@ -518,13 +526,16 @@ int Converter::convert( if (!rule_api.empty()) { + data_api.print_local_variables(out); + if (rule_file.empty() || rule_file == output_file) { rule_api.print_rules(out, false); - std::string s = std::string("$local_rules"); table_api.open_top_level_table("ips"); - table_api.add_option("rules", s); + table_api.add_option("rules", "$local_rules"); + if (data_api.has_local_vars()) + table_api.add_option("variables", "$local_variables"); table_api.close_table(); } else @@ -536,6 +547,8 @@ int Converter::convert( table_api.open_top_level_table("ips"); table_api.add_option("include", rule_file); + if (data_api.has_local_vars()) + table_api.add_option("variables", "$local_variables"); table_api.close_table(); } }