diff --git a/include/NebulaUtil.h b/include/NebulaUtil.h index 4c91c03454..118bb7c27e 100644 --- a/include/NebulaUtil.h +++ b/include/NebulaUtil.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -141,12 +142,8 @@ namespace one_util * * @param st string to split * @param delim delimiter character - * @param clean_empty true to clean empty split parts. - * Example for st "a::b:c" - * clean_empty true will return ["a", "b", "c"] - * clean_empty fase will return ["a", "", "b", "c"] + * @param parts where the result will be saved * - * @return a vector containing the resulting substrings */ template void split(const std::string &st, char delim, std::vector &parts) @@ -176,6 +173,19 @@ namespace one_util } } + /** + * Splits a string, using the given delimiter + * + * @param st string to split + * @param delim delimiter character + * @param clean_empty true to clean empty split parts. + * Example for st "a::b:c" + * clean_empty true will return ["a", "b", "c"] + * clean_empty fase will return ["a", "", "b", "c"] + * + * @return a vector containing the resulting substrings + */ + std::vector split(const std::string& st, char delim, bool clean_empty = true); @@ -364,6 +374,34 @@ namespace one_util template <> bool str_cast(const std::string& str, std::string& value); + /** + * Converts string into unsigned integer type + * @param str Input string + * + * @return Unsigned integer value on success, 0 on failure. + * @note If value in string is greater than typename T can hold, + * maximum possible value will be returned + */ + template + T string_to_unsigned(const std::string& str) + { + T value; + + if (std::regex_search(str, std::regex("[^0-9]"))) + { + return 0; + } + + std::istringstream iss(str); + iss >> value; + + if (iss.fail() || !iss.eof()) + { + value = std::numeric_limits::max(); + } + + return value; + } } // namespace one_util #endif /* _NEBULA_UTIL_H_ */ diff --git a/src/secgroup/SecurityGroup.cc b/src/secgroup/SecurityGroup.cc index b2c8bdf2f3..7bb0c4217c 100644 --- a/src/secgroup/SecurityGroup.cc +++ b/src/secgroup/SecurityGroup.cc @@ -63,7 +63,8 @@ int SecurityGroup::insert(SqlDB *db, string& error_str) if (name.empty()) { - goto error_name; + error_str = "No NAME in template for Security Group."; + return -1; } get_template_attribute("RULE", rules); @@ -72,7 +73,7 @@ int SecurityGroup::insert(SqlDB *db, string& error_str) { if (!is_valid(rule, error_str)) { - goto error_valid; + return -1; } } @@ -80,19 +81,10 @@ int SecurityGroup::insert(SqlDB *db, string& error_str) if ( insert_replace(db, false, error_str) != 0 ) { - goto error_db; + return -1; } return 0; - -error_name: - error_str = "No NAME in template for Security Group."; - goto error_common; - -error_valid: -error_db: -error_common: - return -1; } /* -------------------------------------------------------------------------- */ @@ -302,18 +294,12 @@ void SecurityGroup::get_rules(vector& result) const bool SecurityGroup::is_valid(const VectorAttribute * rule, string& error) const { - string value, ip, proto; - - unsigned int ivalue; - - int id; - // ------------------------------------------------------------------------- // Check PROTOCOL and extensions // - RANGE for TCP and UDP // - ICMP_TYPE for ICMP // ------------------------------------------------------------------------- - proto = rule->vector_value("PROTOCOL"); + auto proto = rule->vector_value("PROTOCOL"); one_util::toupper(proto); @@ -325,7 +311,7 @@ bool SecurityGroup::is_valid(const VectorAttribute * rule, string& error) const return false; } - value = rule->vector_value("RANGE"); + auto value = rule->vector_value("RANGE"); if (!value.empty() && proto != "TCP" && proto != "UDP") { @@ -335,24 +321,64 @@ bool SecurityGroup::is_valid(const VectorAttribute * rule, string& error) const if (!value.empty()) { - const char *range_pattern = "^(([[:digit:]]+|[[:digit:]]+:[[:digit:]]+),)*([[:digit:]]+|[[:digit:]]+:[[:digit:]]+)$"; - if (one_util::regex_match(range_pattern, value.c_str()) != 0) + constexpr auto ports_list_pattern = + "^(([[:digit:]]+|[[:digit:]]+:[[:digit:]]+),)*([[:digit:]]+|[[:digit:]]+:[[:digit:]]+)$"; + + if (one_util::regex_match(ports_list_pattern, value.c_str()) != 0) { error = "Invalid RANGE specification."; return false; } - // Check all port numbers are between 0-65535 - const char *big_port_pattern = "([1-9][[:digit:]]{5,}|" - "[7-9][[:digit:]]{4,}|" - "6[6-9][[:digit:]]{3,}|" - "65[6-9][[:digit:]]{2,}|" - "655[4-9][[:digit:]]|" - "6553[6-9])"; - if (one_util::regex_match(big_port_pattern, value.c_str()) == 0) + std::size_t port_counter = 0; + + const auto port_rules = one_util::split(value, ','); + + for (const auto& port_rule : port_rules) { - error = "RANGE out of bounds 0-65536."; - return false; + constexpr auto range_pattern = "([[:digit:]]+:[[:digit:]]+)"; + + if (one_util::regex_match(range_pattern, port_rule.c_str()) == 0) + { + const auto port_values = one_util::split(port_rule, ':'); + if (port_values.size() != 2) + { + error = "Invalid RANGE specification."; + return false; + } + + const auto end_value = one_util::string_to_unsigned(port_values[1]); + + if (end_value > 65535) + { + error = "RANGE out of bounds 0-65536."; + return false; + } + + if (end_value < one_util::string_to_unsigned(port_values[0])) + { + error = "RANGE BEGIN value is greater than RANGE END value"; + return false; + } + + port_counter += 2; + } + else + { + if (one_util::string_to_unsigned(port_rule) > 65535) + { + error = "RANGE out of bounds 0-65536."; + return false; + } + + ++port_counter; + } + + if (port_counter > 15) + { + error = "Not more than 15 ports can be specified in a RULE"; + return false; + } } } @@ -366,6 +392,7 @@ bool SecurityGroup::is_valid(const VectorAttribute * rule, string& error) const return false; } + unsigned int ivalue = 0; if (rule->vector_value("ICMP_TYPE", ivalue) != 0) { error = "Wrong ICMP_TYPE, it must be integer"; @@ -383,6 +410,7 @@ bool SecurityGroup::is_valid(const VectorAttribute * rule, string& error) const return false; } + unsigned int ivalue = 0; if (rule->vector_value("ICMPV6_TYPE", ivalue) != 0) { error = "Wrong ICMPV6_TYPE, it must be integer"; @@ -408,12 +436,13 @@ bool SecurityGroup::is_valid(const VectorAttribute * rule, string& error) const // Check IP, SIZE and NETWORK_ID // ------------------------------------------------------------------------- - ip = rule->vector_value("IP"); + const auto ip = rule->vector_value("IP"); if (!ip.empty()) //Target as IP & SIZE { struct in6_addr ip_addr; + unsigned int ivalue = 0; if (rule->vector_value("SIZE", ivalue) != 0) { error = "Wrong or empty SIZE."; @@ -431,6 +460,7 @@ bool SecurityGroup::is_valid(const VectorAttribute * rule, string& error) const } else //Target is ANY or NETWORK_ID { + int id = 0; if (rule->vector_value("NETWORK_ID", value) == 0 && rule->vector_value("NETWORK_ID", id) != 0) { diff --git a/src/vmm/LibVirtDriverKVM.cc b/src/vmm/LibVirtDriverKVM.cc index 7b93439ec4..a3bee0b94d 100644 --- a/src/vmm/LibVirtDriverKVM.cc +++ b/src/vmm/LibVirtDriverKVM.cc @@ -23,6 +23,7 @@ #include "Nebula.h" #include "Image.h" #include "DatastorePool.h" +#include "NebulaUtil.h" #include #include @@ -105,27 +106,6 @@ static int to_int(const string& s) return val; } -template -T to_unsigned(const std::string& str) -{ - T value; - - if (std::regex_search(str, std::regex("[^0-9]"))) - { - return 0; - } - - std::istringstream iss(str); - iss >> value; - - if (iss.fail() || !iss.eof()) - { - value = std::numeric_limits::max(); - } - - return value; -} - template static void insert_sec(ofstream& file, const string& base, const string& s, const string& sm, const string& sml) @@ -134,7 +114,7 @@ static void insert_sec(ofstream& file, const string& base, const string& s, if (!s.empty()) { - s_i = to_unsigned(s); + s_i = one_util::string_to_unsigned(s); file << "\t\t\t\t<" << base << "_sec>" << one_util::escape_xml(std::to_string(s_i)) << "\n"; @@ -142,7 +122,7 @@ static void insert_sec(ofstream& file, const string& base, const string& s, if (!sm.empty()) { - const auto sm_i = to_unsigned(sm); + const auto sm_i = one_util::string_to_unsigned(sm); if ( sm_i > s_i) { @@ -152,7 +132,7 @@ static void insert_sec(ofstream& file, const string& base, const string& s, if (!sml.empty()) { - const auto sml_i = to_unsigned(sml); + const auto sml_i = one_util::string_to_unsigned(sml); file << "\t\t\t\t<" << base << "_sec_max_length>" << one_util::escape_xml(std::to_string(sml_i))