From 8aea47abaaf4c1d3f562dc0773850eff875fde67 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Wed, 28 Jun 2017 21:20:09 -0400 Subject: [PATCH] osd: primitive cephx osd profile cap support The two new example profiles are read-only and read-write Signed-off-by: Jason Dillaman --- src/osd/OSDCap.cc | 261 +++++++++++++++++++++++++++++------------ src/osd/OSDCap.h | 90 +++++++++++--- src/test/osd/osdcap.cc | 11 ++ 3 files changed, 272 insertions(+), 90 deletions(-) diff --git a/src/osd/OSDCap.cc b/src/osd/OSDCap.cc index 25da5e2b69a..c759ab76825 100644 --- a/src/osd/OSDCap.cc +++ b/src/osd/OSDCap.cc @@ -25,7 +25,7 @@ using std::ostream; using std::vector; ostream& operator<<(ostream& out, const osd_rwxa_t& p) -{ +{ if (p == OSD_CAP_ANY) return out << "*"; @@ -53,42 +53,79 @@ ostream& operator<<(ostream& out, const OSDCapSpec& s) return out; } +ostream& operator<<(ostream& out, const OSDCapPoolNamespace& pns) +{ + if (!pns.pool_name.empty()) { + out << "pool " << pns.pool_name << " "; + } + if (pns.nspace) { + out << "namespace "; + if (pns.nspace->empty()) { + out << "\"\""; + } else { + out << *pns.nspace; + } + out << " "; + } + return out; +} + ostream& operator<<(ostream& out, const OSDCapMatch& m) { if (m.auid != -1LL) { out << "auid " << m.auid << " "; + } else { + out << m.pool_namespace; } + if (m.object_prefix.length()) { out << "object_prefix " << m.object_prefix << " "; } - if (m.pool_name.length()) { - out << "pool " << m.pool_name << " "; - } - if (m.is_nspace) { - out << "namespace "; - if (m.nspace.length() == 0) - out << "\"\""; - else - out << m.nspace; - out << " "; - } return out; } -bool OSDCapMatch::is_match(const string& pn, const string& ns, int64_t pool_auid, const string& object) const +ostream& operator<<(ostream& out, const OSDCapProfile& m) { - if (auid >= 0) { - if (auid != pool_auid) + out << "profile " << m.name << " "; + out << m.pool_namespace; + return out; +} + +bool OSDCapPoolNamespace::is_match(const std::string& pn, + const std::string& ns) const +{ + if (!pool_name.empty()) { + if (pool_name != pn) { return false; + } } - if (pool_name.length()) { - if (pool_name != pn) + if (nspace) { + if (*nspace != ns) { return false; + } } - if (is_nspace) { - if (nspace != ns) + return true; +} + +bool OSDCapPoolNamespace::is_match_all() const +{ + if (!pool_name.empty()) + return false; + if (nspace) + return false; + return true; +} + +bool OSDCapMatch::is_match(const string& pn, const string& ns, + int64_t pool_auid, const string& object) const +{ + if (auid >= 0) { + if (auid != pool_auid) return false; + } else if (!pool_namespace.is_match(pn, ns)) { + return false; } + if (object_prefix.length()) { if (object.find(object_prefix) != 0) return false; @@ -98,77 +135,91 @@ bool OSDCapMatch::is_match(const string& pn, const string& ns, int64_t pool_auid bool OSDCapMatch::is_match_all() const { - if (auid >= 0) - return false; - if (pool_name.length()) + if (auid >= 0) { return false; - if (is_nspace) + } else if (!pool_namespace.is_match_all()) { return false; - if (object_prefix.length()) + } + + if (object_prefix.length()) { return false; + } return true; } ostream& operator<<(ostream& out, const OSDCapGrant& g) { - return out << "grant(" << g.match << g.spec << ")"; + out << "grant("; + if (g.profile.is_valid()) { + out << g.profile; + } else { + out << g.match << g.spec; + } + out << ")"; + return out; } - -bool OSDCap::allow_all() const +bool OSDCapGrant::allow_all() const { - for (vector::const_iterator p = grants.begin(); p != grants.end(); ++p) - if (p->match.is_match_all() && p->spec.allow_all()) - return true; - return false; -} + if (profile.is_valid()) { + expand_profile(); + return std::any_of(profile_grants.cbegin(), profile_grants.cend(), + [](const OSDCapGrant& grant) { + return grant.allow_all(); + }); + } -void OSDCap::set_allow_all() -{ - grants.clear(); - grants.push_back(OSDCapGrant(OSDCapMatch(), OSDCapSpec(OSD_CAP_ANY))); + return (match.is_match_all() && spec.allow_all()); } -bool OSDCap::is_capable(const string& pool_name, const string& ns, int64_t pool_auid, - const string& object, bool op_may_read, bool op_may_write, - const std::vector& classes) const +bool OSDCapGrant::is_capable(const string& pool_name, const string& ns, + int64_t pool_auid, const string& object, + bool op_may_read, bool op_may_write, + const std::vector& classes, + std::vector* class_allowed) const { - const size_t num_classes = classes.size(); - std::vector class_allowed(num_classes, false); osd_rwxa_t allow = 0; - for (vector::const_iterator p = grants.begin(); - p != grants.end(); ++p) { - if (p->match.is_match(pool_name, ns, pool_auid, object)) { - allow = allow | p->spec.allow; + if (profile.is_valid()) { + expand_profile(); + return std::any_of(profile_grants.cbegin(), profile_grants.cend(), + [&](const OSDCapGrant& grant) { + return grant.is_capable(pool_name, ns, pool_auid, object, op_may_read, + op_may_write, classes, class_allowed); + }); + } else { + if (match.is_match(pool_name, ns, pool_auid, object)) { + allow = allow | spec.allow; if ((op_may_read && !(allow & OSD_CAP_R)) || - (op_may_write && !(allow & OSD_CAP_W))) - continue; - if (num_classes) { + (op_may_write && !(allow & OSD_CAP_W))) { + return false; + } + if (!classes.empty()) { // check 'allow *' - if (p->spec.allow_all()) + if (spec.allow_all()) { return true; + } + // compare this grant to each class in the operation - for (size_t i = 0; i < num_classes; i++) { + for (size_t i = 0; i < classes.size(); ++i) { // check 'allow class foo' - if (classes[i].name == p->spec.class_name && - !p->spec.class_name.empty()) { - class_allowed[i] = true; + if (!spec.class_name.empty() && classes[i].name == spec.class_name) { + (*class_allowed)[i] = true; continue; } // check 'allow x | class-{rw}': must be on whitelist - if (!classes[i].whitelisted) + if (!classes[i].whitelisted) { continue; + } if ((classes[i].read && !(allow & OSD_CAP_CLS_R)) || (classes[i].write && !(allow & OSD_CAP_CLS_W))) { continue; } - class_allowed[i] = true; + (*class_allowed)[i] = true; + } + if (!std::all_of(class_allowed->cbegin(), class_allowed->cend(), + [](bool v) { return v; })) { + return false; } - if (std::all_of(class_allowed.cbegin(), class_allowed.cend(), - [](bool v) { return v; })) - return true; - else - continue; } return true; } @@ -176,6 +227,57 @@ bool OSDCap::is_capable(const string& pool_name, const string& ns, int64_t pool_ return false; } +void OSDCapGrant::expand_profile() const +{ + // only generate this list once + if (!profile_grants.empty()) { + return; + } + + if (profile.name == "read-only") { + // grants READ-ONLY caps to the OSD + profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace), + OSDCapSpec(osd_rwxa_t(OSD_CAP_R))); + return; + } + if (profile.name == "read-write") { + // grants READ-WRITE caps to the OSD + profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace), + OSDCapSpec(osd_rwxa_t(OSD_CAP_R | OSD_CAP_W))); + } +} + +bool OSDCap::allow_all() const +{ + for (auto &grant : grants) { + if (grant.allow_all()) { + return true; + } + } + return false; +} + +void OSDCap::set_allow_all() +{ + grants.clear(); + grants.push_back(OSDCapGrant(OSDCapMatch(), OSDCapSpec(OSD_CAP_ANY))); +} + +bool OSDCap::is_capable(const string& pool_name, const string& ns, + int64_t pool_auid, const string& object, + bool op_may_read, bool op_may_write, + const std::vector& classes) const +{ + std::vector class_allowed(classes.size(), false); + for (auto &grant : grants) { + if (grant.is_capable(pool_name, ns, pool_auid, object, op_may_read, + op_may_write, classes, &class_allowed)) { + return true; + } + } + return false; +} + // grammar namespace qi = boost::spirit::qi; @@ -210,16 +312,17 @@ struct OSDCapParser : qi::grammar spaces = +ascii::space; - - // match := [pool[=] [namespace[=]] | auid <123>] [object_prefix ] pool_name %= -(spaces >> lit("pool") >> (lit('=') | spaces) >> str); nspace %= (spaces >> lit("namespace") >> (lit('=') | spaces) >> estr); + + // match := [pool[=] [namespace[=]] | auid <123>] [object_prefix ] auid %= (spaces >> lit("auid") >> spaces >> int_); object_prefix %= -(spaces >> lit("object_prefix") >> spaces >> str); - match = ( (auid >> object_prefix) [_val = phoenix::construct(_1, _2)] | - (pool_name >> nspace >> object_prefix) [_val = phoenix::construct(_1, _2, _3)] | - (pool_name >> object_prefix) [_val = phoenix::construct(_1, _2)]); + match = ( + (auid >> object_prefix) [_val = phoenix::construct(_1, _2)] | + (pool_name >> nspace >> object_prefix) [_val = phoenix::construct(_1, _2, _3)] | + (pool_name >> object_prefix) [_val = phoenix::construct(_1, _2)]); // rwxa := * | [r][w][x] [class-read] [class-write] rwxa = @@ -234,19 +337,27 @@ struct OSDCapParser : qi::grammar (spaces >> lit("class-write")[_val |= OSD_CAP_CLS_W]) )); // capspec := * | rwx | class [classcap] - capspec = - rwxa [_val = phoenix::construct(_1)] | - ( spaces >> lit("class") >> spaces >> ((str >> spaces >> str) [_val = phoenix::construct(_1, _2)] | - str [_val = phoenix::construct(_1, string())] )); + class_name %= (spaces >> lit("class") >> spaces >> str); + class_cap %= -(spaces >> str); + capspec = ( + (rwxa) [_val = phoenix::construct(_1)] | + (class_name >> class_cap) [_val = phoenix::construct(_1, _2)]); + + // profile := profile [pool[=] [namespace[=]]] + profile_name %= (spaces >> lit("profile") >> spaces >> str); + profile = ( + (profile_name >> pool_name >> nspace) [_val = phoenix::construct(_1, _2, _3)] | + (profile_name >> pool_name) [_val = phoenix::construct(_1, _2)]); // grant := allow match capspec grant = (*ascii::blank >> lit("allow") >> - ((capspec >> match) [_val = phoenix::construct(_2, _1)] | - (match >> capspec) [_val = phoenix::construct(_1, _2)]) >> - *ascii::blank); + ((capspec >> match) [_val = phoenix::construct(_2, _1)] | + (match >> capspec) [_val = phoenix::construct(_1, _2)] | + (profile) [_val = phoenix::construct(_1)] + ) >> *ascii::blank); // osdcap := grant [grant ...] grants %= (grant % (lit(';') | lit(','))); - osdcap = grants [_val = phoenix::construct(_1)]; + osdcap = grants [_val = phoenix::construct(_1)]; } qi::rule spaces; qi::rule rwxa; @@ -254,11 +365,15 @@ struct OSDCapParser : qi::grammar qi::rule unquoted_word; qi::rule str, estr; qi::rule auid; + qi::rule class_name; + qi::rule class_cap; qi::rule capspec; qi::rule pool_name; qi::rule nspace; qi::rule object_prefix; qi::rule match; + qi::rule profile_name; + qi::rule profile; qi::rule grant; qi::rule()> grants; qi::rule osdcap; diff --git a/src/osd/OSDCap.h b/src/osd/OSDCap.h index 8ce808052eb..2c9ffa27c80 100644 --- a/src/osd/OSDCap.h +++ b/src/osd/OSDCap.h @@ -1,4 +1,4 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab /* * Ceph - scalable distributed file system @@ -7,10 +7,10 @@ * * 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 + * License version 2.1, as published by the Free Software * Foundation. See file COPYING. - * - * OSDCaps: Hold the capabilities associated with a single authenticated + * + * OSDCaps: Hold the capabilities associated with a single authenticated * user key. These are specified by text strings of the form * "allow r" (which allows reading anything on the OSD) * "allow rwx auid foo" (which allows full access to listed auids) @@ -33,6 +33,10 @@ using std::ostream; #include "include/types.h" #include "OpRequest.h" +#include +#include +#include + static const __u8 OSD_CAP_R = (1 << 1); // read static const __u8 OSD_CAP_W = (1 << 2); // write static const __u8 OSD_CAP_CLS_R = (1 << 3); // class read @@ -74,22 +78,39 @@ struct OSDCapSpec { ostream& operator<<(ostream& out, const OSDCapSpec& s); +struct OSDCapPoolNamespace { + std::string pool_name; + boost::optional nspace = boost::none; + + OSDCapPoolNamespace() { + } + OSDCapPoolNamespace(const std::string& pool_name, + const boost::optional& nspace = boost::none) + : pool_name(pool_name), nspace(nspace) { + } + + bool is_match(const std::string& pn, const std::string& ns) const; + bool is_match_all() const; +}; + +ostream& operator<<(ostream& out, const OSDCapPoolNamespace& pns); + struct OSDCapMatch { // auid and pool_name/nspace are mutually exclusive - int64_t auid; - std::string pool_name; - bool is_nspace; // true if nspace is defined; false if not constrained. - std::string nspace; - + int64_t auid = CEPH_AUTH_UID_DEFAULT; + OSDCapPoolNamespace pool_namespace; std::string object_prefix; - OSDCapMatch() : auid(CEPH_AUTH_UID_DEFAULT), is_nspace(false) {} - OSDCapMatch(std::string pl, std::string pre) : - auid(CEPH_AUTH_UID_DEFAULT), pool_name(pl), is_nspace(false), object_prefix(pre) {} - OSDCapMatch(std::string pl, std::string ns, std::string pre) : - auid(CEPH_AUTH_UID_DEFAULT), pool_name(pl), is_nspace(true), nspace(ns), object_prefix(pre) {} - OSDCapMatch(uint64_t auid, std::string pre) : auid(auid), is_nspace(false), object_prefix(pre) {} + OSDCapMatch() {} + OSDCapMatch(const OSDCapPoolNamespace& pns) : pool_namespace(pns) {} + OSDCapMatch(const std::string& pl, const std::string& pre) + : pool_namespace(pl), object_prefix(pre) {} + OSDCapMatch(const std::string& pl, const std::string& ns, + const std::string& pre) + : pool_namespace(pl, ns), object_prefix(pre) {} + OSDCapMatch(uint64_t auid, const std::string& pre) + : auid(auid), object_prefix(pre) {} /** * check if given request parameters match our constraints @@ -100,19 +121,54 @@ struct OSDCapMatch { * @param object object name * @return true if we match, false otherwise */ - bool is_match(const std::string& pool_name, const std::string& nspace_name, int64_t pool_auid, const std::string& object) const; + bool is_match(const std::string& pool_name, const std::string& nspace_name, + int64_t pool_auid, const std::string& object) const; bool is_match_all() const; }; ostream& operator<<(ostream& out, const OSDCapMatch& m); +struct OSDCapProfile { + std::string name; + OSDCapPoolNamespace pool_namespace; + + OSDCapProfile() { + } + OSDCapProfile(const std::string& name, + const std::string& pool_name, + const boost::optional& nspace = boost::none) + : name(name), pool_namespace(pool_name, nspace) { + } + + inline bool is_valid() const { + return !name.empty(); + } +}; + +ostream& operator<<(ostream& out, const OSDCapProfile& m); + struct OSDCapGrant { OSDCapMatch match; OSDCapSpec spec; + OSDCapProfile profile; + + // explicit grants that a profile grant expands to; populated as + // needed by expand_profile() and cached here. + mutable std::list profile_grants; OSDCapGrant() {} - OSDCapGrant(OSDCapMatch m, OSDCapSpec s) : match(m), spec(s) {} + OSDCapGrant(const OSDCapMatch& m, const OSDCapSpec& s) : match(m), spec(s) {} + OSDCapGrant(const OSDCapProfile& profile) : profile(profile) { + } + + bool allow_all() const; + bool is_capable(const string& pool_name, const string& ns, int64_t pool_auid, + const string& object, bool op_may_read, bool op_may_write, + const std::vector& classes, + std::vector* class_allowed) const; + + void expand_profile() const; }; ostream& operator<<(ostream& out, const OSDCapGrant& g); diff --git a/src/test/osd/osdcap.cc b/src/test/osd/osdcap.cc index 95dc40ba556..7080bdeff33 100644 --- a/src/test/osd/osdcap.cc +++ b/src/test/osd/osdcap.cc @@ -70,6 +70,7 @@ const char *parse_good[] = { "allow pool foo namespace=nfoo rwx; allow pool bar namespace nbar object_prefix rbd r", "allow pool foo namespace=\"\" rwx; allow pool bar namespace='' object_prefix rbd r", "allow pool foo namespace \"\" rwx; allow pool bar namespace '' object_prefix rbd r", + "allow profile abc, allow profile abc pool=bar, allow profile abc pool=bar namespace=foo", 0 }; @@ -1006,3 +1007,13 @@ TEST(OSDCap, AllowClassMultiRWX) { ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", false, true, false}})); ASSERT_FALSE(cap.is_capable("bar", "", 0, "foo", false, false, {{"foo", false, false, false}, {"bar", false, false, false}})); } + +TEST(OSDCap, AllowProfile) { + OSDCap cap; + ASSERT_TRUE(cap.parse("allow profile read-only, allow profile read-write pool abc", NULL)); + ASSERT_FALSE(cap.allow_all()); + ASSERT_FALSE(cap.is_capable("foo", "", 0, "asdf", true, true, {})); + ASSERT_TRUE(cap.is_capable("foo", "", 0, "asdf", true, false, {})); + ASSERT_TRUE(cap.is_capable("abc", "", 0, "asdf", false, true, {})); +} + -- 2.39.5