#include <algorithm>
+#include <boost/regex.hpp>
+#include "include/assert.h"
+
static inline bool is_not_alnum_space(char c)
{
return !(isalpha(c) || isdigit(c) || (c == '-') || (c == '_'));
ostream& operator<<(ostream& out, const StringConstraint& c)
{
- if (c.prefix.length())
- return out << "prefix " << c.prefix;
- else
+ switch (c.match_type) {
+ case StringConstraint::MATCH_TYPE_EQUAL:
return out << "value " << c.value;
+ case StringConstraint::MATCH_TYPE_PREFIX:
+ return out << "prefix " << c.value;
+ case StringConstraint::MATCH_TYPE_REGEX:
+ return out << "regex " << c.value;
+ default:
+ break;
+ }
+ return out;
}
ostream& operator<<(ostream& out, const MonCapGrant& m)
for (map<string,StringConstraint>::const_iterator p = m.command_args.begin();
p != m.command_args.end();
++p) {
- if (p->second.value.length())
- out << " " << maybe_quote_string(p->first) << "=" << maybe_quote_string(p->second.value);
- else
- out << " " << maybe_quote_string(p->first) << " prefix " << maybe_quote_string(p->second.prefix);
+ switch (p->second.match_type) {
+ case StringConstraint::MATCH_TYPE_EQUAL:
+ out << " " << maybe_quote_string(p->first) << "="
+ << maybe_quote_string(p->second.value);
+ break;
+ case StringConstraint::MATCH_TYPE_PREFIX:
+ out << " " << maybe_quote_string(p->first) << " prefix "
+ << maybe_quote_string(p->second.value);
+ break;
+ case StringConstraint::MATCH_TYPE_REGEX:
+ out << " " << maybe_quote_string(p->first) << " regex "
+ << maybe_quote_string(p->second.value);
+ break;
+ default:
+ break;
+ }
}
}
}
(mon_rwxa_t, allow))
BOOST_FUSION_ADAPT_STRUCT(StringConstraint,
- (std::string, value)
- (std::string, prefix))
+ (StringConstraint::MatchType, match_type)
+ (std::string, value))
// </magic>
profile_grants.push_back(MonCapGrant("osd", MON_CAP_R | MON_CAP_W));
profile_grants.push_back(MonCapGrant("auth", MON_CAP_R | MON_CAP_X));
profile_grants.push_back(MonCapGrant("config-key", MON_CAP_R | MON_CAP_W));
- string prefix = string("daemon-private/mgr/");
- profile_grants.push_back(MonCapGrant("config-key get", "key",
- StringConstraint("", prefix)));
- profile_grants.push_back(MonCapGrant("config-key put", "key",
- StringConstraint("", prefix)));
- profile_grants.push_back(MonCapGrant("config-key exists", "key",
- StringConstraint("", prefix)));
- profile_grants.push_back(MonCapGrant("config-key delete", "key",
- StringConstraint("", prefix)));
+ StringConstraint constraint(StringConstraint::MATCH_TYPE_PREFIX,
+ "daemon-private/mgr/");
+ profile_grants.push_back(MonCapGrant("config-key get", "key", constraint));
+ profile_grants.push_back(MonCapGrant("config-key put", "key", constraint));
+ profile_grants.push_back(MonCapGrant("config-key exists", "key", constraint));
+ profile_grants.push_back(MonCapGrant("config-key delete", "key", constraint));
}
if (profile == "osd" || profile == "mds" || profile == "mon" ||
profile == "mgr") {
+ StringConstraint constraint(StringConstraint::MATCH_TYPE_PREFIX,
+ string("daemon-private/") + stringify(name) +
+ string("/"));
string prefix = string("daemon-private/") + stringify(name) + string("/");
- profile_grants.push_back(MonCapGrant("config-key get", "key", StringConstraint("", prefix)));
- profile_grants.push_back(MonCapGrant("config-key put", "key", StringConstraint("", prefix)));
- profile_grants.push_back(MonCapGrant("config-key exists", "key", StringConstraint("", prefix)));
- profile_grants.push_back(MonCapGrant("config-key delete", "key", StringConstraint("", prefix)));
+ profile_grants.push_back(MonCapGrant("config-key get", "key", constraint));
+ profile_grants.push_back(MonCapGrant("config-key put", "key", constraint));
+ profile_grants.push_back(MonCapGrant("config-key exists", "key", constraint));
+ profile_grants.push_back(MonCapGrant("config-key delete", "key", constraint));
}
if (profile == "bootstrap-osd") {
- string prefix = "dm-crypt/osd";
profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
profile_grants.push_back(MonCapGrant("mon getmap"));
profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
profile_grants.push_back(MonCapGrant("mon getmap"));
profile_grants.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mds keys
- profile_grants.back().command_args["entity"] = StringConstraint("", "mds.");
- profile_grants.back().command_args["caps_mon"] = StringConstraint("allow profile mds", "");
- profile_grants.back().command_args["caps_osd"] = StringConstraint("allow rwx", "");
- profile_grants.back().command_args["caps_mds"] = StringConstraint("allow", "");
+ profile_grants.back().command_args["entity"] = StringConstraint(
+ StringConstraint::MATCH_TYPE_PREFIX, "mds.");
+ profile_grants.back().command_args["caps_mon"] = StringConstraint(
+ StringConstraint::MATCH_TYPE_EQUAL, "allow profile mds");
+ profile_grants.back().command_args["caps_osd"] = StringConstraint(
+ StringConstraint::MATCH_TYPE_EQUAL, "allow rwx");
+ profile_grants.back().command_args["caps_mds"] = StringConstraint(
+ StringConstraint::MATCH_TYPE_EQUAL, "allow");
}
if (profile == "bootstrap-mgr") {
profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
profile_grants.push_back(MonCapGrant("mon getmap"));
profile_grants.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mgr keys
- profile_grants.back().command_args["entity"] = StringConstraint("", "mgr.");
- profile_grants.back().command_args["caps_mon"] = StringConstraint("allow profile mgr", "");
+ profile_grants.back().command_args["entity"] = StringConstraint(
+ StringConstraint::MATCH_TYPE_PREFIX, "mgr.");
+ profile_grants.back().command_args["caps_mon"] = StringConstraint(
+ StringConstraint::MATCH_TYPE_EQUAL, "allow profile mgr");
}
if (profile == "bootstrap-rgw") {
profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
profile_grants.push_back(MonCapGrant("mon getmap"));
profile_grants.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mds keys
- profile_grants.back().command_args["entity"] = StringConstraint("", "client.rgw.");
- profile_grants.back().command_args["caps_mon"] = StringConstraint("allow rw", "");
- profile_grants.back().command_args["caps_osd"] = StringConstraint("allow rwx", "");
+ profile_grants.back().command_args["entity"] = StringConstraint(
+ StringConstraint::MATCH_TYPE_PREFIX, "client.rgw.");
+ profile_grants.back().command_args["caps_mon"] = StringConstraint(
+ StringConstraint::MATCH_TYPE_EQUAL, "allow rw");
+ profile_grants.back().command_args["caps_osd"] = StringConstraint(
+ StringConstraint::MATCH_TYPE_EQUAL, "allow rwx");
}
if (profile == "fs-client") {
profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
// argument must be present if a constraint exists
if (q == c_args.end())
return 0;
- if (p->second.value.length()) {
- // match value
+ switch (p->second.match_type) {
+ case StringConstraint::MATCH_TYPE_EQUAL:
if (p->second.value != q->second)
return 0;
- } else {
- // match prefix
- if (q->second.find(p->second.prefix) != 0)
+ break;
+ case StringConstraint::MATCH_TYPE_PREFIX:
+ if (q->second.find(p->second.value) != 0)
return 0;
+ break;
+ case StringConstraint::MATCH_TYPE_REGEX:
+ {
+ boost::regex pattern(p->second.value,
+ boost::regex::basic | boost::regex::no_except);
+ if (pattern.empty() || !boost::regex_match(q->second, pattern))
+ return 0;
+ }
+ break;
+ default:
+ break;
}
}
return MON_CAP_ALL;
spaces = +(lit(' ') | lit('\n') | lit('\t'));
// command := command[=]cmd [k1=v1 k2=v2 ...]
- str_match = '=' >> str >> qi::attr(string());
- str_prefix = spaces >> lit("prefix") >> spaces >> qi::attr(string()) >> str;
- kv_pair = str >> (str_match | str_prefix);
+ str_match = '=' >> qi::attr(StringConstraint::MATCH_TYPE_EQUAL) >> str;
+ str_prefix = spaces >> lit("prefix") >> spaces >>
+ qi::attr(StringConstraint::MATCH_TYPE_PREFIX) >> str;
+ str_regex = spaces >> lit("regex") >> spaces >>
+ qi::attr(StringConstraint::MATCH_TYPE_REGEX) >> str;
+ kv_pair = str >> (str_match | str_prefix | str_regex);
kv_map %= kv_pair >> *(spaces >> kv_pair);
command_match = -spaces >> lit("allow") >> spaces >> lit("command") >> (lit('=') | spaces)
>> qi::attr(string()) >> qi::attr(string())
qi::rule<Iterator, string()> unquoted_word;
qi::rule<Iterator, string()> str;
- qi::rule<Iterator, StringConstraint()> str_match, str_prefix;
+ qi::rule<Iterator, StringConstraint()> str_match, str_prefix, str_regex;
qi::rule<Iterator, pair<string, StringConstraint>()> kv_pair;
qi::rule<Iterator, map<string, StringConstraint>()> kv_map;
"allow command abc with arg=foo arg2=bar",
"allow command abc with arg=foo arg2 prefix bar arg3 prefix baz",
"allow command abc with arg=foo arg2 prefix \"bar bingo\" arg3 prefix baz",
+ "allow command abc with arg regex \"^[0-9a-z.]*$\"",
+ "allow command abc with arg regex \"\(invaluid regex\"",
"allow service foo x",
"allow service foo x; allow service bar x",
"allow service foo w ;allow service bar x",
name, "", "config-key delete", ca, true, true, true));
}
+TEST(MonCap, CommandRegEx) {
+ MonCap cap;
+ ASSERT_FALSE(cap.is_allow_all());
+ ASSERT_TRUE(cap.parse("allow command abc with arg regex \"^[0-9a-z.]*$\"", NULL));
+
+ EntityName name;
+ name.from_str("osd.123");
+ ASSERT_TRUE(cap.is_capable(nullptr, CEPH_ENTITY_TYPE_OSD, name, "",
+ "abc", {{"arg", "12345abcde"}}, true, true, true));
+ ASSERT_FALSE(cap.is_capable(nullptr, CEPH_ENTITY_TYPE_OSD, name, "",
+ "abc", {{"arg", "~!@#$"}}, true, true, true));
+
+ ASSERT_TRUE(cap.parse("allow command abc with arg regex \"[*\"", NULL));
+ ASSERT_FALSE(cap.is_capable(nullptr, CEPH_ENTITY_TYPE_OSD, name, "",
+ "abc", {{"arg", ""}}, true, true, true));
+}