/* 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;
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)) {
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);
<< ", 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
// 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);
--- /dev/null
+// -*- 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;
+}
+
--- /dev/null
+// -*- 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
mds/snap.cc \
mds/SessionMap.cc \
mds/MDSContext.cc \
+ mds/MDSAuthCaps.cc \
mds/MDLog.cc \
common/TrackedOp.cc
libmds_la_LIBADD = $(LIBOSDC)
mds/MDS.h \
mds/Beacon.h \
mds/MDSContext.h \
+ mds/MDSAuthCaps.h \
mds/MDSMap.h \
mds/MDSTable.h \
mds/MDSTableServer.h \
#include "include/elist.h"
#include "include/interval_set.h"
#include "mdstypes.h"
+#include "mds/MDSAuthCaps.h"
class CInode;
struct MDRequestImpl;
int importing_count;
friend class SessionMap;
+
// Human (friendly) name is soft state generated from client metadata
void _update_human_name();
std::string human_name;
session_info_t info; ///< durable bits
+ MDSAuthCaps auth_caps;
+
ConnectionRef connection;
xlist<Session*>::item item_session_list;