]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
common/admin_socket: validate command json before feeding it to hook
authorKefu Chai <kchai@redhat.com>
Wed, 14 Feb 2018 11:45:51 +0000 (19:45 +0800)
committerKefu Chai <kchai@redhat.com>
Thu, 15 Feb 2018 19:51:12 +0000 (03:51 +0800)
Signed-off-by: Kefu Chai <kchai@redhat.com>
src/common/admin_socket.cc
src/common/admin_socket.h
src/common/cmdparse.cc
src/common/cmdparse.h

index 19397e578f5c2da8724fd62d65cc9450568c8cad..362a9971fcc57fffc15579c75af8b33af8e5cca9 100644 (file)
@@ -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;
index e526ba37c919a8bf8282bbc563908063746163df..e3066bf3cdb38d579d994ab0571e6d01cdb33193 100644 (file)
@@ -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;
index 7047a25a26e75a5478ee5edebfbe360d46b5e8c4..f6c1a968fe63b6d1453c87bd24eefbdc66341a6f 100644 (file)
@@ -391,3 +391,178 @@ int parse_osd_id(const char *s, std::ostream *pss)
   }
   return id;
 }
+
+namespace {
+template <typename Func>
+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<typename T>
+T str_to_num(const std::string& s)
+{
+  if constexpr (is_same_v<T, int>) {
+    return std::stoi(s);
+  } else if constexpr (is_same_v<T, long>) {
+    return std::stol(s);
+  } else if constexpr (is_same_v<T, long long>) {
+    return std::stoll(s);
+  } else if constexpr (is_same_v<T, double>) {
+    return std::stod(s);
+  }
+}
+
+using arg_desc_t = std::map<boost::string_view, boost::string_view>;
+
+template<typename T>
+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<T>(min_max.front());
+  auto max = numeric_limits<T>::max();
+  if (min_max.size() > 1) {
+    max = str_to_num<T>(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<bool is_vector,
+        typename T,
+        typename Value = conditional_t<is_vector,
+                                       vector<T>,
+                                       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<std::string, T>) {
+      return validate_str_arg(value, type, desc, os);
+    } else if constexpr (is_same_v<int64_t, T> ||
+                        is_same_v<double, T>) {
+      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<true, int64_t>(cct, cmdmap, arg_desc,
+                                           name, type, os);
+      } else if (type == "CephFloat") {
+       return !validate_arg<true, double>(cct, cmdmap, arg_desc,
+                                           name, type, os);
+      } else {
+       return !validate_arg<true, string>(cct, cmdmap, arg_desc,
+                                          name, type, os);
+      }
+    } else {
+      if (type == "CephInt") {
+       return !validate_arg<false, int64_t>(cct, cmdmap, arg_desc,
+                                           name, type, os);
+      } else if (type == "CephFloat") {
+       return !validate_arg<false, double>(cct, cmdmap, arg_desc,
+                                           name, type, os);
+      } else {
+       return !validate_arg<false, string>(cct, cmdmap, arg_desc,
+                                           name, type, os);
+      }
+    }
+  });
+}
index 41495f5551a283c3628c86b608fdf3e13216931c..814430418eb3144a570dff5581f40e5c959a5a37 100644 (file)
@@ -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);