summaryrefslogtreecommitdiffstats
path: root/src/osd/OSDCap.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/osd/OSDCap.cc')
-rw-r--r--src/osd/OSDCap.cc531
1 files changed, 531 insertions, 0 deletions
diff --git a/src/osd/OSDCap.cc b/src/osd/OSDCap.cc
new file mode 100644
index 00000000..bd8d0b89
--- /dev/null
+++ b/src/osd/OSDCap.cc
@@ -0,0 +1,531 @@
+// -*- 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) 2009-2011 New Dream Network
+ *
+ * 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 <boost/config/warning_disable.hpp>
+#include <boost/spirit/include/qi.hpp>
+#include <boost/spirit/include/phoenix_operator.hpp>
+#include <boost/spirit/include/phoenix.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+
+#include "OSDCap.h"
+#include "common/config.h"
+#include "common/debug.h"
+#include "include/ipaddr.h"
+
+using std::ostream;
+using std::vector;
+
+ostream& operator<<(ostream& out, const osd_rwxa_t& p)
+{
+ if (p == OSD_CAP_ANY)
+ return out << "*";
+
+ if (p & OSD_CAP_R)
+ out << "r";
+ if (p & OSD_CAP_W)
+ out << "w";
+ if ((p & OSD_CAP_X) == OSD_CAP_X) {
+ out << "x";
+ } else {
+ if (p & OSD_CAP_CLS_R)
+ out << " class-read";
+ if (p & OSD_CAP_CLS_W)
+ out << " class-write";
+ }
+ return out;
+}
+
+ostream& operator<<(ostream& out, const OSDCapSpec& s)
+{
+ if (s.allow)
+ return out << s.allow;
+ if (s.class_name.length()) {
+ out << "class '" << s.class_name << "'";
+ if (!s.method_name.empty()) {
+ out << " '" << s.method_name << "'";
+ }
+ }
+ 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 OSDCapPoolTag &pt)
+{
+ out << "app " << pt.application << " key " << pt.key << " val " << pt.value
+ << " ";
+ return out;
+}
+
+ostream& operator<<(ostream& out, const OSDCapMatch& m)
+{
+ if (!m.pool_namespace.pool_name.empty() || m.pool_namespace.nspace) {
+ out << m.pool_namespace;
+ }
+
+ if (!m.pool_tag.application.empty()) {
+ out << m.pool_tag;
+ }
+
+ if (m.object_prefix.length()) {
+ out << "object_prefix " << m.object_prefix << " ";
+ }
+ return out;
+}
+
+ostream& operator<<(ostream& out, const OSDCapProfile& m)
+{
+ 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 (nspace) {
+ if (!nspace->empty() && nspace->back() == '*' &&
+ boost::starts_with(ns, nspace->substr(0, nspace->length() - 1))) {
+ return true;
+ }
+
+ if (*nspace != ns) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool OSDCapPoolNamespace::is_match_all() const
+{
+ if (!pool_name.empty())
+ return false;
+ if (nspace)
+ return false;
+ return true;
+}
+
+bool OSDCapPoolTag::is_match(const app_map_t& app_map) const
+{
+ if (application.empty()) {
+ return true;
+ }
+ auto kv_map = app_map.find(application);
+ if (kv_map == app_map.end()) {
+ return false;
+ }
+ if (!key.compare("*") && !value.compare("*")) {
+ return true;
+ }
+ if (!key.compare("*")) {
+ for (auto it : kv_map->second) {
+ if (it.second == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+ auto kv_val = kv_map->second.find(key);
+ if (kv_val == kv_map->second.end()) {
+ return false;
+ }
+ if (!value.compare("*")) {
+ return true;
+ }
+ return kv_val->second == value;
+}
+
+bool OSDCapPoolTag::is_match_all() const {
+ return application.empty();
+}
+
+bool OSDCapMatch::is_match(const string& pn, const string& ns,
+ const OSDCapPoolTag::app_map_t& app_map,
+ const string& object) const
+{
+ if (!pool_namespace.is_match(pn, ns)) {
+ return false;
+ } else if (!pool_tag.is_match(app_map)) {
+ return false;
+ }
+
+ if (object_prefix.length()) {
+ if (object.find(object_prefix) != 0)
+ return false;
+ }
+ return true;
+}
+
+bool OSDCapMatch::is_match_all() const
+{
+if (!pool_namespace.is_match_all()) {
+ return false;
+ } else if (!pool_tag.is_match_all()) {
+ return false;
+ }
+
+ if (object_prefix.length()) {
+ return false;
+ }
+ return true;
+}
+
+ostream& operator<<(ostream& out, const OSDCapGrant& g)
+{
+ out << "grant(";
+ if (g.profile.is_valid()) {
+ out << g.profile << " [";
+ for (auto it = g.profile_grants.cbegin();
+ it != g.profile_grants.cend(); ++it) {
+ if (it != g.profile_grants.cbegin()) {
+ out << ",";
+ }
+ out << *it;
+ }
+ out << "]";
+ } else {
+ out << g.match << g.spec;
+ }
+ if (g.network.size()) {
+ out << " network " << g.network;
+ }
+ out << ")";
+ return out;
+}
+
+void OSDCapGrant::set_network(const string& n)
+{
+ network = n;
+ network_valid = ::parse_network(n.c_str(), &network_parsed, &network_prefix);
+}
+
+bool OSDCapGrant::allow_all() const
+{
+ if (profile.is_valid()) {
+ return std::any_of(profile_grants.cbegin(), profile_grants.cend(),
+ [](const OSDCapGrant& grant) {
+ return grant.allow_all();
+ });
+ }
+
+ return (match.is_match_all() && spec.allow_all());
+}
+
+bool OSDCapGrant::is_capable(
+ const string& pool_name,
+ const string& ns,
+ const OSDCapPoolTag::app_map_t& application_metadata,
+ const string& object,
+ bool op_may_read,
+ bool op_may_write,
+ const std::vector<OpRequest::ClassInfo>& classes,
+ const entity_addr_t& addr,
+ std::vector<bool>* class_allowed) const
+{
+ osd_rwxa_t allow = 0;
+
+ if (network.size() &&
+ (!network_valid ||
+ !network_contains(network_parsed,
+ network_prefix,
+ addr))) {
+ return false;
+ }
+
+ if (profile.is_valid()) {
+ return std::any_of(profile_grants.cbegin(), profile_grants.cend(),
+ [&](const OSDCapGrant& grant) {
+ return grant.is_capable(pool_name, ns,
+ application_metadata,
+ object, op_may_read,
+ op_may_write, classes, addr,
+ class_allowed);
+ });
+ } else {
+ if (match.is_match(pool_name, ns, application_metadata, object)) {
+ allow = allow | spec.allow;
+ if ((op_may_read && !(allow & OSD_CAP_R)) ||
+ (op_may_write && !(allow & OSD_CAP_W))) {
+ return false;
+ }
+ if (!classes.empty()) {
+ // check 'allow *'
+ if (spec.allow_all()) {
+ return true;
+ }
+
+ // compare this grant to each class in the operation
+ for (size_t i = 0; i < classes.size(); ++i) {
+ // check 'allow class foo [method_name]'
+ if (!spec.class_name.empty() &&
+ classes[i].class_name == spec.class_name &&
+ (spec.method_name.empty() ||
+ classes[i].method_name == spec.method_name)) {
+ (*class_allowed)[i] = true;
+ continue;
+ }
+ // check 'allow x | class-{rw}': must be on whitelist
+ 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;
+ }
+ if (!std::all_of(class_allowed->cbegin(), class_allowed->cend(),
+ [](bool v) { return v; })) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+void OSDCapGrant::expand_profile()
+{
+ 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)));
+ }
+
+ if (profile.name == "rbd") {
+ // RBD read-write grant
+ profile_grants.emplace_back(OSDCapMatch(string(), "rbd_info"),
+ OSDCapSpec(osd_rwxa_t(OSD_CAP_R)));
+ profile_grants.emplace_back(OSDCapMatch(string(), "rbd_children"),
+ OSDCapSpec(osd_rwxa_t(OSD_CAP_CLS_R)));
+ profile_grants.emplace_back(OSDCapMatch(string(), "rbd_mirroring"),
+ OSDCapSpec(osd_rwxa_t(OSD_CAP_CLS_R)));
+ profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace.pool_name),
+ OSDCapSpec("rbd", "metadata_list"));
+ profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
+ OSDCapSpec(osd_rwxa_t(OSD_CAP_R |
+ OSD_CAP_W |
+ OSD_CAP_X)));
+ }
+ if (profile.name == "rbd-read-only") {
+ // RBD read-only grant
+ profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
+ OSDCapSpec(osd_rwxa_t(OSD_CAP_R |
+ OSD_CAP_CLS_R)));
+ profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace,
+ "rbd_header."),
+ OSDCapSpec("rbd", "child_attach"));
+ profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace,
+ "rbd_header."),
+ OSDCapSpec("rbd", "child_detach"));
+ }
+}
+
+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,
+ const OSDCapPoolTag::app_map_t& application_metadata,
+ const string& object,
+ bool op_may_read, bool op_may_write,
+ const std::vector<OpRequest::ClassInfo>& classes,
+ const entity_addr_t& addr) const
+{
+ std::vector<bool> class_allowed(classes.size(), false);
+ for (auto &grant : grants) {
+ if (grant.is_capable(pool_name, ns, application_metadata,
+ object, op_may_read, op_may_write, classes, addr,
+ &class_allowed)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+// grammar
+namespace qi = boost::spirit::qi;
+namespace ascii = boost::spirit::ascii;
+namespace phoenix = boost::phoenix;
+
+template <typename Iterator>
+struct OSDCapParser : qi::grammar<Iterator, OSDCap()>
+{
+ OSDCapParser() : OSDCapParser::base_type(osdcap)
+ {
+ 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;
+
+ quoted_string %=
+ lexeme['"' >> +(char_ - '"') >> '"'] |
+ lexeme['\'' >> +(char_ - '\'') >> '\''];
+ equoted_string %=
+ lexeme['"' >> *(char_ - '"') >> '"'] |
+ lexeme['\'' >> *(char_ - '\'') >> '\''];
+ unquoted_word %= +char_("a-zA-Z0-9_./-");
+ str %= quoted_string | unquoted_word;
+ estr %= equoted_string | unquoted_word;
+ network_str %= +char_("/.:a-fA-F0-9][");
+
+ spaces = +ascii::space;
+
+ wildcard = (lit('*') | lit("all")) [_val = "*"];
+
+ pool_name %= -(spaces >> lit("pool") >> (lit('=') | spaces) >> str);
+ nspace %= (spaces >> lit("namespace")
+ >> (lit('=') | spaces)
+ >> estr >> -char_('*'));
+
+ // match := [pool[=]<poolname> [namespace[=]<namespace>]] [object_prefix <prefix>]
+ object_prefix %= -(spaces >> lit("object_prefix") >> spaces >> str);
+ pooltag %= (spaces >> lit("tag")
+ >> spaces >> str // application
+ >> spaces >> (wildcard | str) // key
+ >> -spaces >> lit('=') >> -spaces >> (wildcard | str)); // value
+
+ match = (
+ pooltag [_val = phoenix::construct<OSDCapMatch>(_1)] |
+ (nspace >> pooltag) [_val = phoenix::construct<OSDCapMatch>(_1, _2)] |
+ (pool_name >> nspace >> object_prefix) [_val = phoenix::construct<OSDCapMatch>(_1, _2, _3)] |
+ (pool_name >> object_prefix) [_val = phoenix::construct<OSDCapMatch>(_1, _2)]
+ );
+
+ // rwxa := * | [r][w][x] [class-read] [class-write]
+ rwxa =
+ (spaces >> wildcard[_val = OSD_CAP_ANY]) |
+ ( eps[_val = 0] >>
+ (
+ spaces >>
+ ( lit('r')[_val |= OSD_CAP_R] ||
+ lit('w')[_val |= OSD_CAP_W] ||
+ lit('x')[_val |= OSD_CAP_X] )) ||
+ ( (spaces >> lit("class-read")[_val |= OSD_CAP_CLS_R]) ||
+ (spaces >> lit("class-write")[_val |= OSD_CAP_CLS_W]) ));
+
+ // capspec := * | rwx | class <name> [<method name>]
+ class_name %= (spaces >> lit("class") >> spaces >> str);
+ method_name %= -(spaces >> str);
+ capspec = (
+ (rwxa) [_val = phoenix::construct<OSDCapSpec>(_1)] |
+ (class_name >> method_name) [_val = phoenix::construct<OSDCapSpec>(_1, _2)]);
+
+ // profile := profile <name> [pool[=]<pool> [namespace[=]<namespace>]]
+ profile_name %= (lit("profile") >> (lit('=') | spaces) >> str);
+ profile = (
+ (profile_name >> pool_name >> nspace) [_val = phoenix::construct<OSDCapProfile>(_1, _2, _3)] |
+ (profile_name >> pool_name) [_val = phoenix::construct<OSDCapProfile>(_1, _2)]);
+
+ // grant := allow match capspec
+ grant = (*ascii::blank >>
+ ((lit("allow") >> capspec >> match >>
+ -(spaces >> lit("network") >> spaces >> network_str))
+ [_val = phoenix::construct<OSDCapGrant>(_2, _1, _3)] |
+ (lit("allow") >> match >> capspec >>
+ -(spaces >> lit("network") >> spaces >> network_str))
+ [_val = phoenix::construct<OSDCapGrant>(_1, _2, _3)] |
+ (profile >> -(spaces >> lit("network") >> spaces >> network_str))
+ [_val = phoenix::construct<OSDCapGrant>(_1, _2)]
+ ) >> *ascii::blank);
+ // osdcap := grant [grant ...]
+ grants %= (grant % (lit(';') | lit(',')));
+ osdcap = grants [_val = phoenix::construct<OSDCap>(_1)];
+ }
+ qi::rule<Iterator> spaces;
+ qi::rule<Iterator, unsigned()> rwxa;
+ qi::rule<Iterator, string()> quoted_string, equoted_string;
+ qi::rule<Iterator, string()> unquoted_word;
+ qi::rule<Iterator, string()> str, estr, network_str;
+ qi::rule<Iterator, string()> wildcard;
+ qi::rule<Iterator, string()> class_name;
+ qi::rule<Iterator, string()> method_name;
+ qi::rule<Iterator, OSDCapSpec()> capspec;
+ qi::rule<Iterator, string()> pool_name;
+ qi::rule<Iterator, string()> nspace;
+ qi::rule<Iterator, string()> object_prefix;
+ qi::rule<Iterator, OSDCapPoolTag()> pooltag;
+ qi::rule<Iterator, OSDCapMatch()> match;
+ qi::rule<Iterator, string()> profile_name;
+ qi::rule<Iterator, OSDCapProfile()> profile;
+ qi::rule<Iterator, OSDCapGrant()> grant;
+ qi::rule<Iterator, std::vector<OSDCapGrant>()> grants;
+ qi::rule<Iterator, OSDCap()> osdcap;
+};
+
+bool OSDCap::parse(const string& str, ostream *err)
+{
+ OSDCapParser<string::const_iterator> g;
+ string::const_iterator iter = str.begin();
+ string::const_iterator end = str.end();
+
+ bool r = qi::phrase_parse(iter, end, g, ascii::space, *this);
+ if (r && iter == end)
+ return true;
+
+ // Make sure no grants are kept after parsing failed!
+ grants.clear();
+
+ if (err)
+ *err << "osd capability parse failed, stopped at '" << std::string(iter, end)
+ << "' of '" << str << "'";
+
+ return false;
+}