]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mds: introduce MDS auth caps
authorJohn Spray <john.spray@redhat.com>
Wed, 24 Sep 2014 10:37:34 +0000 (11:37 +0100)
committerJohn Spray <john.spray@redhat.com>
Wed, 8 Oct 2014 10:58:19 +0000 (11:58 +0100)
Signed-off-by: John Spray <john.spray@redhat.com>
src/mds/MDS.cc
src/mds/MDSAuthCaps.cc [new file with mode: 0644]
src/mds/MDSAuthCaps.h [new file with mode: 0644]
src/mds/Makefile.am
src/mds/SessionMap.h

index d981433234c1a176712d488bfdb451449898471c..1c803a811b2c5d05a03e5851f38cc6915d67d996 100644 (file)
@@ -805,8 +805,9 @@ void MDS::check_ops_in_flight()
 /* This function DOES put the passed message before returning*/
 void MDS::handle_command(MCommand *m)
 {
-  // FIXME authorize based on m->get_connection()
-  
+  Session *session = static_cast<Session *>(m->get_connection()->get_priv());
+  assert(session != NULL);
+
   int r = 0;
   cmdmap_t cmdmap;
   std::stringstream ss;
@@ -815,7 +816,15 @@ void MDS::handle_command(MCommand *m)
   Context *run_after = NULL;
 
 
-  if (m->cmd.empty()) {
+  if (!session->auth_caps.allow_all()) {
+    // TODO: enforce allow_all check only for 'w' commands
+    dout(1) << __func__
+      << ": received command from client without `tell` capability: "
+      << m->get_connection()->peer_addr << dendl;
+
+    ss << "permission denied";
+    r = -EPERM;
+  } else if (m->cmd.empty()) {
     ss << "no command given";
     outs = ss.str();
   } else if (!cmdmap_from_json(m->cmd, &cmdmap, ss)) {
@@ -825,12 +834,10 @@ void MDS::handle_command(MCommand *m)
     r = _handle_command(cmdmap, m->get_data(), &outbl, &outs, &run_after);
   }
 
-  if (m->get_connection()) {
-    MCommandReply *reply = new MCommandReply(r, outs);
-    reply->set_tid(m->get_tid());
-    reply->set_data(outbl);
-    m->get_connection()->send_message(reply);
-  }
+  MCommandReply *reply = new MCommandReply(r, outs);
+  reply->set_tid(m->get_tid());
+  reply->set_data(outbl);
+  m->get_connection()->send_message(reply);
 
   if (run_after) {
     run_after->complete(0);
@@ -2539,6 +2546,8 @@ bool MDS::ms_verify_authorizer(Connection *con, int peer_type,
               << ", new/authorizing con " << con << dendl;
       con->set_priv(s->get());
 
+
+
       // Wait until we fully accept the connection before setting
       // s->connection.  In particular, if there are multiple incoming
       // connection attempts, they will all get their authorizer
@@ -2552,6 +2561,25 @@ bool MDS::ms_verify_authorizer(Connection *con, int peer_type,
       // messenger.)
     }
 
+    bufferlist::iterator p = caps_info.caps.begin();
+    string auth_cap_str;
+    try {
+      ::decode(auth_cap_str, p);
+
+      dout(10) << __func__ << ": parsing auth_cap_str='" << auth_cap_str << "'" << dendl;
+      std::ostringstream errstr;
+      int parse_success = s->auth_caps.parse(auth_cap_str, &errstr);
+      if (parse_success == false) {
+        dout(1) << __func__ << ": auth cap parse error: " << errstr.str()
+          << " parsing '" << auth_cap_str << "'" << dendl;
+      }
+    } catch (buffer::error& e) {
+      // Assume legacy auth, defaults to:
+      //  * permit all filesystem ops
+      //  * permit no `tell` ops
+      dout(1) << __func__ << ": cannot decode auth caps bl of length " << caps_info.caps.length() << dendl;
+    }
+
     /*
     s->caps.set_allow_all(caps_info.allow_all);
  
diff --git a/src/mds/MDSAuthCaps.cc b/src/mds/MDSAuthCaps.cc
new file mode 100644 (file)
index 0000000..de97a0c
--- /dev/null
@@ -0,0 +1,210 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software 
+ * Foundation.  See file COPYING.
+ * 
+ */
+
+
+#include <errno.h>
+
+#include <boost/spirit/include/qi.hpp>
+#include <boost/spirit/include/phoenix_operator.hpp>
+#include <boost/spirit/include/phoenix.hpp>
+
+#include "MDSAuthCaps.h"
+
+using std::ostream;
+using std::string;
+namespace qi = boost::spirit::qi;
+namespace ascii = boost::spirit::ascii;
+namespace phoenix = boost::phoenix;
+
+const std::string MDSCapMatch::MDS_AUTH_PATH_ROOT = "/";
+
+template <typename Iterator>
+struct MDSCapParser : qi::grammar<Iterator, MDSAuthCaps()>
+{
+  MDSCapParser() : MDSCapParser::base_type(mdscaps)
+  {
+    using qi::char_;
+    using qi::int_;
+    using qi::lexeme;
+    using qi::alnum;
+    using qi::_val;
+    using qi::_1;
+    using qi::_2;
+    using qi::_3;
+    using qi::eps;
+    using qi::lit;
+
+    spaces = +(lit(' ') | lit('\n') | lit('\t'));
+
+    quoted_path %=
+      lexeme[lit("\"") >> *(char_ - '"') >> '"'] | 
+      lexeme[lit("'") >> *(char_ - '\'') >> '\''];
+    unquoted_path %= +char_("a-zA-Z0-9_.-/");
+
+    // match := [path=<path>] [uid=<uid>]
+    uid %= (spaces >> lit("uid") >> lit('=') >> int_);
+    path %= (spaces >> lit("path") >> lit('=') >> (quoted_path | unquoted_path));
+    match = -(
+             (uid)[_val = phoenix::construct<MDSCapMatch>(_1)] |
+             (path >> uid)[_val = phoenix::construct<MDSCapMatch>(_1, _2)] | 
+             (path)[_val = phoenix::construct<MDSCapMatch>(_1)]);
+
+    // capspec = * | r[w]
+    capspec = spaces >> (
+        lit("*")[_val = MDSCapSpec(true, true, true)]
+        |
+        (lit("rw"))[_val = MDSCapSpec(true, true, false)]
+        |
+        (lit("r"))[_val = MDSCapSpec(true, false, false)]
+        );
+
+    grant = lit("allow") >> (capspec >> match)[_val = phoenix::construct<MDSCapGrant>(_1, _2)];
+    grants %= (grant % (*lit(' ') >> (lit(';') | lit(',')) >> *lit(' ')));
+    mdscaps = grants  [_val = phoenix::construct<MDSAuthCaps>(_1)]; 
+  }
+  qi::rule<Iterator> spaces;
+  qi::rule<Iterator, string()> quoted_path, unquoted_path;
+  qi::rule<Iterator, MDSCapSpec()> capspec;
+  qi::rule<Iterator, string()> path;
+  qi::rule<Iterator, int()> uid;
+  qi::rule<Iterator, MDSCapMatch()> match;
+  qi::rule<Iterator, MDSCapGrant()> grant;
+  qi::rule<Iterator, std::vector<MDSCapGrant>()> grants;
+  qi::rule<Iterator, MDSAuthCaps()> mdscaps;
+};
+
+
+/**
+ * For a given filesystem path, query whether this capability carries`
+ * authorization to read or write.
+ *
+ * This is true if any of the 'grant' clauses in the capability match the
+ * requested path + op.
+ *
+ */
+bool MDSAuthCaps::is_capable(const std::string &path, int uid, bool may_read, bool may_write) const
+{
+  for (std::vector<MDSCapGrant>::const_iterator i = grants.begin(); i != grants.end(); ++i) {
+    if (i->match.match(path, uid)) {
+      if ((may_read && !i->spec.read) ||
+          (may_write && !i->spec.write)) {
+        continue;
+      } else {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+bool MDSAuthCaps::parse(const std::string& str, ostream *err)
+{
+  // Special case for legacy caps
+  if (str == "allow") {
+    grants.clear();
+    grants.push_back(MDSCapGrant(MDSCapSpec(true, true, false), MDSCapMatch()));
+    return true;
+  }
+
+  MDSCapParser<std::string::const_iterator> g;
+  std::string::const_iterator iter = str.begin();
+  std::string::const_iterator end = str.end();
+
+  bool r = qi::phrase_parse(iter, end, g, ascii::space, *this);
+  if (r && iter == end) {
+    return true;
+  } else {
+    // Make sure no grants are kept after parsing failed!
+    grants.clear();
+
+    if (err)
+      *err << "osdcap parse failed, stopped at '" << std::string(iter, end)
+           << "' of '" << str << "'\n";
+    return false; 
+  }
+}
+
+
+bool MDSAuthCaps::allow_all() const
+{
+  for (std::vector<MDSCapGrant>::const_iterator i = grants.begin(); i != grants.end(); ++i) {
+    if (i->match.is_match_all() && i->spec.allow_all()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+
+ostream &operator<<(ostream &out, const MDSCapMatch &match)
+{
+  if (match.path != MDSCapMatch::MDS_AUTH_PATH_ROOT) {
+    out << "path=\"" << match.path << "\"";
+  }
+  if (match.path != MDSCapMatch::MDS_AUTH_PATH_ROOT && match.uid != MDSCapMatch::MDS_AUTH_UID_ANY) {
+    out << " ";
+  }
+  if (match.uid != MDSCapMatch::MDS_AUTH_UID_ANY) {
+    out << "uid=" << match.uid;
+  }
+
+  return out;
+}
+
+
+ostream &operator<<(ostream &out, const MDSCapSpec &spec)
+{
+  if (spec.any) {
+    out << "*";
+  } else {
+    if (spec.read) {
+      out << "r";
+    }
+    if (spec.write) {
+      out << "w";
+    }
+  }
+
+  return out;
+}
+
+
+ostream &operator<<(ostream &out, const MDSCapGrant &grant)
+{
+  out << "allow ";
+  out << grant.spec;
+  if (!grant.match.is_match_all()) {
+    out << " " << grant.match;
+  }
+
+  return out;
+}
+
+
+ostream &operator<<(ostream &out, const MDSAuthCaps &cap)
+{
+  out << "MDSAuthCaps[";
+  for (size_t i = 0; i < cap.grants.size(); ++i) {
+    out << cap.grants[i];
+    if (i < cap.grants.size() - 1) {
+      out << ", ";
+    }
+  }
+  out << "]";
+
+  return out;
+}
+
diff --git a/src/mds/MDSAuthCaps.h b/src/mds/MDSAuthCaps.h
new file mode 100644 (file)
index 0000000..2e54d07
--- /dev/null
@@ -0,0 +1,86 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2014 Red Hat
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software 
+ * Foundation.  See file COPYING.
+ * 
+ */
+
+
+#ifndef MDS_AUTH_CAPS_H
+#define MDS_AUTH_CAPS_H
+
+#include <vector>
+#include <string>
+#include <sstream>
+
+
+struct MDSCapSpec {
+  bool read;
+  bool write;
+  bool any;
+
+  MDSCapSpec() : write(false), any(false) {}
+  MDSCapSpec(bool r_, bool w_, bool a_) : read(r_), write(w_), any(a_) {}
+
+  bool allow_all() const {return any;}
+};
+
+struct MDSCapMatch {
+  static const int MDS_AUTH_UID_ANY = -1;
+  static const std::string MDS_AUTH_PATH_ROOT;
+
+  int uid;  // Require UID to be equal to this, if !=MDS_AUTH_UID_ANY
+  std::string path;  // Require path to be child of this (may be "/" for any)
+
+  MDSCapMatch() : uid(MDS_AUTH_UID_ANY), path(MDS_AUTH_PATH_ROOT) {}
+  MDSCapMatch(int uid_) : uid(uid_), path(MDS_AUTH_PATH_ROOT) {}
+  MDSCapMatch(std::string path_) : uid(MDS_AUTH_UID_ANY), path(path_) {}
+  MDSCapMatch(std::string path_, int uid_) : uid(uid_), path(path_) {}
+  
+  bool is_match_all() const
+  {
+    return uid == MDS_AUTH_UID_ANY && path == "/";
+  }
+
+  bool match(const std::string &target_path, const int target_uid) const {
+    return (target_path.find(path) == 0 && (target_uid == uid || uid == MDS_AUTH_UID_ANY)); 
+  }
+};
+
+struct MDSCapGrant {
+  MDSCapSpec spec;
+  MDSCapMatch match;
+
+  MDSCapGrant(const MDSCapSpec &spec_, const MDSCapMatch &match_) : spec(spec_), match(match_) {}
+  MDSCapGrant() {}
+};
+
+class MDSAuthCaps
+{
+    protected:
+    std::vector<MDSCapGrant> grants;
+
+    public:
+    bool parse(const std::string &str, std::ostream *err);
+    MDSAuthCaps() {}
+    MDSAuthCaps(const std::vector<MDSCapGrant> &grants_) : grants(grants_) {}
+
+    bool allow_all() const;
+    bool is_capable(const std::string &path, int uid, bool may_read, bool may_write) const;
+    friend std::ostream &operator<<(std::ostream &out, const MDSAuthCaps &cap);
+};
+
+
+std::ostream &operator<<(std::ostream &out, const MDSCapMatch &match);
+std::ostream &operator<<(std::ostream &out, const MDSCapSpec &spec);
+std::ostream &operator<<(std::ostream &out, const MDSCapGrant &grant);
+std::ostream &operator<<(std::ostream &out, const MDSAuthCaps &cap);
+
+#endif // MDS_AUTH_CAPS_H
index 71f76d5c010a91349734316585e7e5fbf746414e..8bed0e15b11ffcb00057172becc178a2c402e7b0 100644 (file)
@@ -26,6 +26,7 @@ libmds_la_SOURCES = \
        mds/snap.cc \
        mds/SessionMap.cc \
        mds/MDSContext.cc \
+       mds/MDSAuthCaps.cc \
        mds/MDLog.cc \
        common/TrackedOp.cc
 libmds_la_LIBADD = $(LIBOSDC)
@@ -53,6 +54,7 @@ noinst_HEADERS += \
        mds/MDS.h \
        mds/Beacon.h \
        mds/MDSContext.h \
+       mds/MDSAuthCaps.h \
        mds/MDSMap.h \
        mds/MDSTable.h \
        mds/MDSTableServer.h \
index b000c5ec227cd4b4034a29fa8ad8884dfe4aea0c..4c38d3c10826d6d4775179a41725989a9e45582c 100644 (file)
@@ -25,6 +25,7 @@ using std::set;
 #include "include/elist.h"
 #include "include/interval_set.h"
 #include "mdstypes.h"
+#include "mds/MDSAuthCaps.h"
 
 class CInode;
 struct MDRequestImpl;
@@ -81,6 +82,7 @@ private:
   int importing_count;
   friend class SessionMap;
 
+
   // Human (friendly) name is soft state generated from client metadata
   void _update_human_name();
   std::string human_name;
@@ -98,6 +100,8 @@ public:
 
   session_info_t info;                         ///< durable bits
 
+  MDSAuthCaps auth_caps;
+
   ConnectionRef connection;
   xlist<Session*>::item item_session_list;