X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Fmds%2FMDSAuthCaps.cc;fp=src%2Fceph%2Fsrc%2Fmds%2FMDSAuthCaps.cc;h=e22e449cd42218a5a1e8946064f5b0cd30abc19f;hb=812ff6ca9fcd3e629e49d4328905f33eee8ca3f5;hp=0000000000000000000000000000000000000000;hpb=15280273faafb77777eab341909a3f495cf248d9;p=stor4nfv.git diff --git a/src/ceph/src/mds/MDSAuthCaps.cc b/src/ceph/src/mds/MDSAuthCaps.cc new file mode 100644 index 0000000..e22e449 --- /dev/null +++ b/src/ceph/src/mds/MDSAuthCaps.cc @@ -0,0 +1,391 @@ +// -*- 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 +#include + +#include +#include +#include + +#include "common/debug.h" +#include "MDSAuthCaps.h" + +#define dout_subsys ceph_subsys_mds + +#undef dout_prefix +#define dout_prefix *_dout << "MDSAuthCap " + +using std::ostream; +using std::string; +namespace qi = boost::spirit::qi; +namespace ascii = boost::spirit::ascii; +namespace phoenix = boost::phoenix; + +template +struct MDSCapParser : qi::grammar +{ + MDSCapParser() : MDSCapParser::base_type(mdscaps) + { + using qi::char_; + using qi::int_; + using qi::uint_; + 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=] [uid= [gids=[,...]] + path %= (spaces >> lit("path") >> lit('=') >> (quoted_path | unquoted_path)); + uid %= (spaces >> lit("uid") >> lit('=') >> uint_); + uintlist %= (uint_ % lit(',')); + gidlist %= -(spaces >> lit("gids") >> lit('=') >> uintlist); + match = -( + (uid >> gidlist)[_val = phoenix::construct(_1, _2)] | + (path >> uid >> gidlist)[_val = phoenix::construct(_1, _2, _3)] | + (path)[_val = phoenix::construct(_1)]); + + // capspec = * | r[w] + capspec = spaces >> ( + lit("*")[_val = MDSCapSpec(true, true, true, true)] + | + (lit("rwp"))[_val = MDSCapSpec(true, true, false, true)] + | + (lit("rw"))[_val = MDSCapSpec(true, true, false, false)] + | + (lit("r"))[_val = MDSCapSpec(true, false, false, false)] + ); + + grant = lit("allow") >> (capspec >> match)[_val = phoenix::construct(_1, _2)]; + grants %= (grant % (*lit(' ') >> (lit(';') | lit(',')) >> *lit(' '))); + mdscaps = grants [_val = phoenix::construct(_1)]; + } + qi::rule spaces; + qi::rule quoted_path, unquoted_path; + qi::rule capspec; + qi::rule path; + qi::rule uid; + qi::rule() > uintlist; + qi::rule() > gidlist; + qi::rule match; + qi::rule grant; + qi::rule()> grants; + qi::rule mdscaps; +}; + +void MDSCapMatch::normalize_path() +{ + // drop any leading / + while (path.length() && path[0] == '/') { + path = path.substr(1); + } + + // drop dup // + // drop . + // drop .. +} + +bool MDSCapMatch::match(const std::string &target_path, + const int caller_uid, + const int caller_gid, + const vector *caller_gid_list) const +{ + if (uid != MDS_AUTH_UID_ANY) { + if (uid != caller_uid) + return false; + bool gid_matched = false; + if (std::find(gids.begin(), gids.end(), caller_gid) != gids.end()) + gid_matched = true; + if (caller_gid_list) { + for (auto i = caller_gid_list->begin(); i != caller_gid_list->end(); ++i) { + if (std::find(gids.begin(), gids.end(), *i) != gids.end()) { + gid_matched = true; + break; + } + } + } + if (!gid_matched) + return false; + } + + if (!match_path(target_path)) { + return false; + } + + return true; +} + +bool MDSCapMatch::match_path(const std::string &target_path) const +{ + if (path.length()) { + if (target_path.find(path) != 0) + return false; + // if path doesn't already have a trailing /, make sure the target + // does so that path=/foo doesn't match target_path=/food + if (target_path.length() > path.length() && + path[path.length()-1] != '/' && + target_path[path.length()] != '/') + return false; + } + + return true; +} + +/** + * Is the client *potentially* able to access this path? Actual + * permission will depend on uids/modes in the full is_capable. + */ +bool MDSAuthCaps::path_capable(const std::string &inode_path) const +{ + for (const auto &i : grants) { + if (i.match.match_path(inode_path)) { + return true; + } + } + + return false; +} + +/** + * 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 &inode_path, + uid_t inode_uid, gid_t inode_gid, + unsigned inode_mode, + uid_t caller_uid, gid_t caller_gid, + const vector *caller_gid_list, + unsigned mask, + uid_t new_uid, gid_t new_gid) const +{ + if (cct) + ldout(cct, 10) << __func__ << " inode(path /" << inode_path + << " owner " << inode_uid << ":" << inode_gid + << " mode 0" << std::oct << inode_mode << std::dec + << ") by caller " << caller_uid << ":" << caller_gid +// << "[" << caller_gid_list << "]"; + << " mask " << mask + << " new " << new_uid << ":" << new_gid + << " cap: " << *this << dendl; + + for (std::vector::const_iterator i = grants.begin(); + i != grants.end(); + ++i) { + + if (i->match.match(inode_path, caller_uid, caller_gid, caller_gid_list) && + i->spec.allows(mask & (MAY_READ|MAY_EXECUTE), mask & MAY_WRITE)) { + // we have a match; narrow down GIDs to those specifically allowed here + vector gids; + if (std::find(i->match.gids.begin(), i->match.gids.end(), caller_gid) != + i->match.gids.end()) { + gids.push_back(caller_gid); + } + if (caller_gid_list) { + std::set_intersection(i->match.gids.begin(), i->match.gids.end(), + caller_gid_list->begin(), caller_gid_list->end(), + std::back_inserter(gids)); + std::sort(gids.begin(), gids.end()); + } + + + // Spec is non-allowing if caller asked for set pool but spec forbids it + if (mask & MAY_SET_VXATTR) { + if (!i->spec.allows_set_vxattr()) { + continue; + } + } + + // check unix permissions? + if (i->match.uid == MDSCapMatch::MDS_AUTH_UID_ANY) { + return true; + } + + // chown/chgrp + if (mask & MAY_CHOWN) { + if (new_uid != caller_uid || // you can't chown to someone else + inode_uid != caller_uid) { // you can't chown from someone else + continue; + } + } + if (mask & MAY_CHGRP) { + // you can only chgrp *to* one of your groups... if you own the file. + if (inode_uid != caller_uid || + std::find(gids.begin(), gids.end(), new_gid) == + gids.end()) { + continue; + } + } + + if (inode_uid == caller_uid) { + if ((!(mask & MAY_READ) || (inode_mode & S_IRUSR)) && + (!(mask & MAY_WRITE) || (inode_mode & S_IWUSR)) && + (!(mask & MAY_EXECUTE) || (inode_mode & S_IXUSR))) { + return true; + } + } else if (std::find(gids.begin(), gids.end(), + inode_gid) != gids.end()) { + if ((!(mask & MAY_READ) || (inode_mode & S_IRGRP)) && + (!(mask & MAY_WRITE) || (inode_mode & S_IWGRP)) && + (!(mask & MAY_EXECUTE) || (inode_mode & S_IXGRP))) { + return true; + } + } else { + if ((!(mask & MAY_READ) || (inode_mode & S_IROTH)) && + (!(mask & MAY_WRITE) || (inode_mode & S_IWOTH)) && + (!(mask & MAY_EXECUTE) || (inode_mode & S_IXOTH))) { + return true; + } + } + } + } + + return false; +} + +void MDSAuthCaps::set_allow_all() +{ + grants.clear(); + grants.push_back(MDSCapGrant( + MDSCapSpec(true, true, true, true), + MDSCapMatch())); +} + +bool MDSAuthCaps::parse(CephContext *c, const std::string& str, ostream *err) +{ + // Special case for legacy caps + if (str == "allow") { + grants.clear(); + grants.push_back(MDSCapGrant(MDSCapSpec(true, true, false, true), MDSCapMatch())); + return true; + } + + MDSCapParser 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); + cct = c; // set after parser self-assignment + if (r && iter == end) { + for (auto& grant : grants) { + std::sort(grant.match.gids.begin(), grant.match.gids.end()); + } + return true; + } else { + // Make sure no grants are kept after parsing failed! + grants.clear(); + + if (err) + *err << "MDSAuthCaps parse failed, stopped at '" << std::string(iter, end) + << "' of '" << str << "'\n"; + return false; + } +} + + +bool MDSAuthCaps::allow_all() const +{ + for (std::vector::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.length()) { + out << "path=\"/" << match.path << "\""; + if (match.uid != MDSCapMatch::MDS_AUTH_UID_ANY) { + out << " "; + } + } + if (match.uid != MDSCapMatch::MDS_AUTH_UID_ANY) { + out << "uid=" << match.uid; + if (!match.gids.empty()) { + out << " gids="; + for (std::vector::const_iterator p = match.gids.begin(); + p != match.gids.end(); + ++p) { + if (p != match.gids.begin()) + out << ','; + out << *p; + } + } + } + + 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; +} +