From: Kefu Chai Date: Wed, 14 Feb 2018 11:45:51 +0000 (+0800) Subject: common/admin_socket: validate command json before feeding it to hook X-Git-Tag: v13.0.2~254^2~1 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=90ea4b91e4e04f5086f7d6435ba1ffec445a5e29;p=ceph.git common/admin_socket: validate command json before feeding it to hook Signed-off-by: Kefu Chai --- diff --git a/src/common/admin_socket.cc b/src/common/admin_socket.cc index 19397e578f5c..362a9971fcc5 100644 --- a/src/common/admin_socket.cc +++ b/src/common/admin_socket.cc @@ -409,7 +409,8 @@ bool AdminSocket::do_accept() in_hook = true; auto match_hook = p->second; m_lock.Unlock(); - bool success = match_hook->call(match, cmdmap, format, out); + bool success = (validate(match, cmdmap, out) && + match_hook->call(match, cmdmap, format, out)); m_lock.Lock(); in_hook = false; in_hook_cond.Signal(); @@ -439,6 +440,19 @@ bool AdminSocket::do_accept() return rval; } +bool AdminSocket::validate(const std::string& command, + const cmdmap_t& cmdmap, + bufferlist& out) const +{ + stringstream os; + if (validate_cmd(m_cct, m_descs.at(command), cmdmap, os)) { + return true; + } else { + out.append(os); + return false; + } +} + int AdminSocket::register_command(std::string command, std::string cmddesc, AdminSocketHook *hook, std::string help) { int ret; diff --git a/src/common/admin_socket.h b/src/common/admin_socket.h index e526ba37c919..e3066bf3cdb3 100644 --- a/src/common/admin_socket.h +++ b/src/common/admin_socket.h @@ -85,6 +85,9 @@ private: void *entry() override; bool do_accept(); + bool validate(const std::string& command, + const cmdmap_t& cmdmap, + bufferlist& out) const; CephContext *m_cct; std::string m_path; diff --git a/src/common/cmdparse.cc b/src/common/cmdparse.cc index 7047a25a26e7..f6c1a968fe63 100644 --- a/src/common/cmdparse.cc +++ b/src/common/cmdparse.cc @@ -391,3 +391,178 @@ int parse_osd_id(const char *s, std::ostream *pss) } return id; } + +namespace { +template +bool find_first_in(boost::string_view s, const char *delims, Func&& f) +{ + auto pos = s.find_first_not_of(delims); + while (pos != s.npos) { + s.remove_prefix(pos); + auto end = s.find_first_of(delims); + if (f(s.substr(0, end))) { + return true; + } + pos = s.find_first_not_of(delims, end); + } + return false; +} + +template +T str_to_num(const std::string& s) +{ + if constexpr (is_same_v) { + return std::stoi(s); + } else if constexpr (is_same_v) { + return std::stol(s); + } else if constexpr (is_same_v) { + return std::stoll(s); + } else if constexpr (is_same_v) { + return std::stod(s); + } +} + +using arg_desc_t = std::map; + +template +bool arg_in_range(T value, const arg_desc_t& desc, std::ostream& os) { + auto range = desc.find("range"); + if (range == desc.end()) { + return true; + } + auto min_max = get_str_list(string(range->second), "|"); + auto min = str_to_num(min_max.front()); + auto max = numeric_limits::max(); + if (min_max.size() > 1) { + max = str_to_num(min_max.back()); + } + if (value < min || value > max) { + os << "'" << value << "' out of range: " << min_max; + return false; + } + return true; +} + +bool validate_str_arg(boost::string_view value, + boost::string_view type, + const arg_desc_t& desc, + std::ostream& os) +{ + if (type == "CephIPAddr") { + entity_addr_t addr; + if (addr.parse(string(value).c_str())) { + return true; + } else { + os << "failed to parse addr '" << value << "', should be ip:[port]"; + return false; + } + } else if (type == "CephChoices") { + auto choices = desc.find("strings"); + assert(choices != end(desc)); + auto strings = choices->second; + if (find_first_in(strings, "|", [=](auto choice) { + return (value == choice); + })) { + return true; + } else { + os << "'" << value << "' not belong to '" << strings << "'"; + return false; + } + } else { + // CephString or other types like CephPgid + return true; + } +} + +template, + T>> +bool validate_arg(CephContext* cct, + const cmdmap_t& cmdmap, + const arg_desc_t& desc, + const boost::string_view name, + const boost::string_view type, + std::ostream& os) +{ + Value v; + if (!cmd_getval(cct, cmdmap, string(name), v)) { + if constexpr (is_vector) { + // an empty list is acceptable. + return true; + } else { + if (auto req = desc.find("req"); + req != end(desc) && req->second == "false") { + return true; + } else { + os << "missing required parameter: '" << name << "'"; + return false; + } + } + } + auto validate = [&](const T& value) { + if constexpr (is_same_v) { + return validate_str_arg(value, type, desc, os); + } else if constexpr (is_same_v || + is_same_v) { + return arg_in_range(value, desc, os); + } + }; + if constexpr(is_vector) { + return find_if_not(begin(v), end(v), validate) == end(v); + } else { + return validate(v); + } +} +} // anonymous namespace + +bool validate_cmd(CephContext* cct, + const std::string& desc, + const cmdmap_t& cmdmap, + std::ostream& os) +{ + return !find_first_in(desc, " ", [&](auto desc) { + arg_desc_t arg_desc; + for_each_substr(desc, ",", [&](auto kv) { + auto equal = kv.find('='); + if (equal == kv.npos) { + // it should be the command + return; + } + auto key = kv.substr(0, equal); + auto val = kv.substr(equal + 1); + arg_desc[key] = val; + }); + if (arg_desc.empty()) { + return false; + } + assert(arg_desc.count("name")); + assert(arg_desc.count("type")); + auto name = arg_desc["name"]; + auto type = arg_desc["type"]; + if (arg_desc.count("n")) { + if (type == "CephInt") { + return !validate_arg(cct, cmdmap, arg_desc, + name, type, os); + } else if (type == "CephFloat") { + return !validate_arg(cct, cmdmap, arg_desc, + name, type, os); + } else { + return !validate_arg(cct, cmdmap, arg_desc, + name, type, os); + } + } else { + if (type == "CephInt") { + return !validate_arg(cct, cmdmap, arg_desc, + name, type, os); + } else if (type == "CephFloat") { + return !validate_arg(cct, cmdmap, arg_desc, + name, type, os); + } else { + return !validate_arg(cct, cmdmap, arg_desc, + name, type, os); + } + } + }); +} diff --git a/src/common/cmdparse.h b/src/common/cmdparse.h index 41495f5551a2..814430418eb3 100644 --- a/src/common/cmdparse.h +++ b/src/common/cmdparse.h @@ -78,6 +78,10 @@ cmd_putval(CephContext *cct, cmdmap_t& cmdmap, const std::string& k, const T& va cmdmap[k] = val; } +bool validate_cmd(CephContext* cct, + const std::string& desc, + const cmdmap_t& cmdmap, + std::ostream& os); extern int parse_osd_id(const char *s, std::ostream *pss); extern long parse_pos_long(const char *s, std::ostream *pss = NULL);