]> git.proxmox.com Git - ceph.git/blame - ceph/src/mon/MonCap.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / mon / MonCap.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2013 Inktank
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15#include <boost/config/warning_disable.hpp>
16#include <boost/spirit/include/qi_uint.hpp>
17#include <boost/spirit/include/qi.hpp>
18#include <boost/fusion/include/std_pair.hpp>
19#include <boost/spirit/include/phoenix.hpp>
20#include <boost/fusion/adapted/struct/adapt_struct.hpp>
21#include <boost/fusion/include/adapt_struct.hpp>
22
23#include "MonCap.h"
24#include "include/stringify.h"
11fdf7f2 25#include "include/ipaddr.h"
7c673cae
FG
26#include "common/debug.h"
27#include "common/Formatter.h"
28
29#include <algorithm>
11fdf7f2 30#include <regex>
7c673cae 31
11fdf7f2 32#include "include/ceph_assert.h"
c07f9fc5 33
7c673cae
FG
34static inline bool is_not_alnum_space(char c)
35{
36 return !(isalpha(c) || isdigit(c) || (c == '-') || (c == '_'));
37}
38
39static string maybe_quote_string(const std::string& str)
40{
41 if (find_if(str.begin(), str.end(), is_not_alnum_space) == str.end())
42 return str;
43 return string("\"") + str + string("\"");
44}
45
46using std::ostream;
47using std::vector;
48
49#define dout_subsys ceph_subsys_mon
50
31f18b77 51ostream& operator<<(ostream& out, const mon_rwxa_t& p)
7c673cae
FG
52{
53 if (p == MON_CAP_ANY)
54 return out << "*";
55
56 if (p & MON_CAP_R)
57 out << "r";
58 if (p & MON_CAP_W)
59 out << "w";
60 if (p & MON_CAP_X)
61 out << "x";
62 return out;
63}
64
65ostream& operator<<(ostream& out, const StringConstraint& c)
66{
c07f9fc5
FG
67 switch (c.match_type) {
68 case StringConstraint::MATCH_TYPE_EQUAL:
7c673cae 69 return out << "value " << c.value;
c07f9fc5
FG
70 case StringConstraint::MATCH_TYPE_PREFIX:
71 return out << "prefix " << c.value;
72 case StringConstraint::MATCH_TYPE_REGEX:
73 return out << "regex " << c.value;
74 default:
75 break;
76 }
77 return out;
7c673cae
FG
78}
79
80ostream& operator<<(ostream& out, const MonCapGrant& m)
81{
82 out << "allow";
83 if (m.service.length()) {
84 out << " service " << maybe_quote_string(m.service);
85 }
86 if (m.command.length()) {
87 out << " command " << maybe_quote_string(m.command);
88 if (!m.command_args.empty()) {
89 out << " with";
90 for (map<string,StringConstraint>::const_iterator p = m.command_args.begin();
91 p != m.command_args.end();
92 ++p) {
c07f9fc5
FG
93 switch (p->second.match_type) {
94 case StringConstraint::MATCH_TYPE_EQUAL:
95 out << " " << maybe_quote_string(p->first) << "="
96 << maybe_quote_string(p->second.value);
97 break;
98 case StringConstraint::MATCH_TYPE_PREFIX:
99 out << " " << maybe_quote_string(p->first) << " prefix "
100 << maybe_quote_string(p->second.value);
101 break;
102 case StringConstraint::MATCH_TYPE_REGEX:
103 out << " " << maybe_quote_string(p->first) << " regex "
104 << maybe_quote_string(p->second.value);
105 break;
106 default:
107 break;
108 }
7c673cae
FG
109 }
110 }
111 }
112 if (m.profile.length()) {
113 out << " profile " << maybe_quote_string(m.profile);
114 }
115 if (m.allow != 0)
116 out << " " << m.allow;
11fdf7f2
TL
117 if (m.network.size())
118 out << " network " << m.network;
7c673cae
FG
119 return out;
120}
121
122
123// <magic>
124// fusion lets us easily populate structs via the qi parser.
125
126typedef map<string,StringConstraint> kvmap;
127
128BOOST_FUSION_ADAPT_STRUCT(MonCapGrant,
129 (std::string, service)
130 (std::string, profile)
131 (std::string, command)
132 (kvmap, command_args)
11fdf7f2
TL
133 (mon_rwxa_t, allow)
134 (std::string, network))
7c673cae
FG
135
136BOOST_FUSION_ADAPT_STRUCT(StringConstraint,
c07f9fc5
FG
137 (StringConstraint::MatchType, match_type)
138 (std::string, value))
7c673cae
FG
139
140// </magic>
141
11fdf7f2
TL
142void MonCapGrant::parse_network()
143{
144 network_valid = ::parse_network(network.c_str(), &network_parsed,
145 &network_prefix);
146}
147
7c673cae
FG
148void MonCapGrant::expand_profile(int daemon_type, const EntityName& name) const
149{
150 // only generate this list once
151 if (!profile_grants.empty())
152 return;
153
154 if (profile == "read-only") {
155 // grants READ-ONLY caps monitor-wide
156 // 'auth' requires MON_CAP_X even for RO, which we do not grant here.
157 profile_grants.push_back(mon_rwxa_t(MON_CAP_R));
158 return;
159 }
160
161 if (profile == "read-write") {
162 // grants READ-WRITE caps monitor-wide
163 // 'auth' requires MON_CAP_X for all operations, which we do not grant.
164 profile_grants.push_back(mon_rwxa_t(MON_CAP_R | MON_CAP_W));
165 return;
166 }
167
168 switch (daemon_type) {
169 case CEPH_ENTITY_TYPE_MON:
170 expand_profile_mon(name);
171 return;
172 case CEPH_ENTITY_TYPE_MGR:
173 expand_profile_mgr(name);
174 return;
175 }
176}
177
178void MonCapGrant::expand_profile_mgr(const EntityName& name) const
179{
180}
181
182void MonCapGrant::expand_profile_mon(const EntityName& name) const
183{
184 if (profile == "mon") {
185 profile_grants.push_back(MonCapGrant("mon", MON_CAP_ALL));
186 profile_grants.push_back(MonCapGrant("log", MON_CAP_ALL));
187 }
188 if (profile == "osd") {
189 profile_grants.push_back(MonCapGrant("osd", MON_CAP_ALL));
190 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
191 profile_grants.push_back(MonCapGrant("pg", MON_CAP_R | MON_CAP_W));
192 profile_grants.push_back(MonCapGrant("log", MON_CAP_W));
193 }
194 if (profile == "mds") {
195 profile_grants.push_back(MonCapGrant("mds", MON_CAP_ALL));
196 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
197 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
198 // This command grant is checked explicitly in MRemoveSnaps handling
199 profile_grants.push_back(MonCapGrant("osd pool rmsnap"));
31f18b77 200 profile_grants.push_back(MonCapGrant("osd blacklist"));
7c673cae
FG
201 profile_grants.push_back(MonCapGrant("log", MON_CAP_W));
202 }
203 if (profile == "mgr") {
204 profile_grants.push_back(MonCapGrant("mgr", MON_CAP_ALL));
31f18b77
FG
205 profile_grants.push_back(MonCapGrant("log", MON_CAP_R | MON_CAP_W));
206 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R | MON_CAP_W));
207 profile_grants.push_back(MonCapGrant("mds", MON_CAP_R | MON_CAP_W));
11fdf7f2 208 profile_grants.push_back(MonCapGrant("fs", MON_CAP_R | MON_CAP_W));
7c673cae 209 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R | MON_CAP_W));
31f18b77
FG
210 profile_grants.push_back(MonCapGrant("auth", MON_CAP_R | MON_CAP_X));
211 profile_grants.push_back(MonCapGrant("config-key", MON_CAP_R | MON_CAP_W));
11fdf7f2 212 profile_grants.push_back(MonCapGrant("config", MON_CAP_R | MON_CAP_W));
7c673cae
FG
213 }
214 if (profile == "osd" || profile == "mds" || profile == "mon" ||
215 profile == "mgr") {
c07f9fc5
FG
216 StringConstraint constraint(StringConstraint::MATCH_TYPE_PREFIX,
217 string("daemon-private/") + stringify(name) +
218 string("/"));
7c673cae 219 string prefix = string("daemon-private/") + stringify(name) + string("/");
c07f9fc5
FG
220 profile_grants.push_back(MonCapGrant("config-key get", "key", constraint));
221 profile_grants.push_back(MonCapGrant("config-key put", "key", constraint));
222 profile_grants.push_back(MonCapGrant("config-key set", "key", constraint));
223 profile_grants.push_back(MonCapGrant("config-key exists", "key", constraint));
224 profile_grants.push_back(MonCapGrant("config-key delete", "key", constraint));
7c673cae
FG
225 }
226 if (profile == "bootstrap-osd") {
7c673cae
FG
227 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
228 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
229 profile_grants.push_back(MonCapGrant("mon getmap"));
c07f9fc5 230 profile_grants.push_back(MonCapGrant("osd new"));
11fdf7f2 231 profile_grants.push_back(MonCapGrant("osd purge-new"));
7c673cae
FG
232 }
233 if (profile == "bootstrap-mds") {
234 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
235 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
236 profile_grants.push_back(MonCapGrant("mon getmap"));
237 profile_grants.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mds keys
c07f9fc5
FG
238 profile_grants.back().command_args["entity"] = StringConstraint(
239 StringConstraint::MATCH_TYPE_PREFIX, "mds.");
240 profile_grants.back().command_args["caps_mon"] = StringConstraint(
241 StringConstraint::MATCH_TYPE_EQUAL, "allow profile mds");
242 profile_grants.back().command_args["caps_osd"] = StringConstraint(
243 StringConstraint::MATCH_TYPE_EQUAL, "allow rwx");
244 profile_grants.back().command_args["caps_mds"] = StringConstraint(
245 StringConstraint::MATCH_TYPE_EQUAL, "allow");
7c673cae
FG
246 }
247 if (profile == "bootstrap-mgr") {
248 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
249 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
250 profile_grants.push_back(MonCapGrant("mon getmap"));
251 profile_grants.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mgr keys
c07f9fc5
FG
252 profile_grants.back().command_args["entity"] = StringConstraint(
253 StringConstraint::MATCH_TYPE_PREFIX, "mgr.");
254 profile_grants.back().command_args["caps_mon"] = StringConstraint(
255 StringConstraint::MATCH_TYPE_EQUAL, "allow profile mgr");
7c673cae
FG
256 }
257 if (profile == "bootstrap-rgw") {
258 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
259 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R)); // read osdmap
260 profile_grants.push_back(MonCapGrant("mon getmap"));
261 profile_grants.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other mds keys
c07f9fc5
FG
262 profile_grants.back().command_args["entity"] = StringConstraint(
263 StringConstraint::MATCH_TYPE_PREFIX, "client.rgw.");
264 profile_grants.back().command_args["caps_mon"] = StringConstraint(
265 StringConstraint::MATCH_TYPE_EQUAL, "allow rw");
266 profile_grants.back().command_args["caps_osd"] = StringConstraint(
267 StringConstraint::MATCH_TYPE_EQUAL, "allow rwx");
7c673cae 268 }
11fdf7f2 269 if (profile == "bootstrap-rbd" || profile == "bootstrap-rbd-mirror") {
d2e6a577 270 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R)); // read monmap
11fdf7f2 271 profile_grants.push_back(MonCapGrant("auth get-or-create")); // FIXME: this can expose other rbd keys
d2e6a577
FG
272 profile_grants.back().command_args["entity"] = StringConstraint(
273 StringConstraint::MATCH_TYPE_PREFIX, "client.");
274 profile_grants.back().command_args["caps_mon"] = StringConstraint(
11fdf7f2
TL
275 StringConstraint::MATCH_TYPE_EQUAL,
276 (profile == "bootstrap-rbd-mirror" ? "profile rbd-mirror" :
277 "profile rbd"));
d2e6a577
FG
278 profile_grants.back().command_args["caps_osd"] = StringConstraint(
279 StringConstraint::MATCH_TYPE_REGEX,
280 "^([ ,]*profile(=|[ ]+)['\"]?rbd[^ ,'\"]*['\"]?([ ]+pool(=|[ ]+)['\"]?[^,'\"]+['\"]?)?)+$");
281 }
7c673cae
FG
282 if (profile == "fs-client") {
283 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
284 profile_grants.push_back(MonCapGrant("mds", MON_CAP_R));
285 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
286 profile_grants.push_back(MonCapGrant("pg", MON_CAP_R));
287 }
288 if (profile == "simple-rados-client") {
289 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
290 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
291 profile_grants.push_back(MonCapGrant("pg", MON_CAP_R));
292 }
11fdf7f2 293 if (profile == "rbd" || profile == "rbd-mirror") {
c07f9fc5
FG
294 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
295 profile_grants.push_back(MonCapGrant("osd", MON_CAP_R));
296 profile_grants.push_back(MonCapGrant("pg", MON_CAP_R));
297
298 // exclusive lock dead-client blacklisting (IP+nonce required)
299 profile_grants.push_back(MonCapGrant("osd blacklist"));
300 profile_grants.back().command_args["blacklistop"] = StringConstraint(
301 StringConstraint::MATCH_TYPE_EQUAL, "add");
302 profile_grants.back().command_args["addr"] = StringConstraint(
d2e6a577 303 StringConstraint::MATCH_TYPE_REGEX, "^[^/]+/[0-9]+$");
11fdf7f2
TL
304
305 }
306 if (profile == "rbd-mirror") {
307 StringConstraint constraint(StringConstraint::MATCH_TYPE_PREFIX,
308 "rbd/mirror/");
309 profile_grants.push_back(MonCapGrant("config-key get", "key", constraint));
c07f9fc5 310 }
7c673cae
FG
311
312 if (profile == "role-definer") {
313 // grants ALL caps to the auth subsystem, read-only on the
314 // monitor subsystem and nothing else.
315 profile_grants.push_back(MonCapGrant("mon", MON_CAP_R));
316 profile_grants.push_back(MonCapGrant("auth", MON_CAP_ALL));
317 }
318}
319
320mon_rwxa_t MonCapGrant::get_allowed(CephContext *cct,
321 int daemon_type,
322 EntityName name,
323 const std::string& s, const std::string& c,
324 const map<string,string>& c_args) const
325{
326 if (profile.length()) {
327 expand_profile(daemon_type, name);
328 mon_rwxa_t a;
329 for (list<MonCapGrant>::const_iterator p = profile_grants.begin();
330 p != profile_grants.end(); ++p)
331 a = a | p->get_allowed(cct, daemon_type, name, s, c, c_args);
332 return a;
333 }
334 if (service.length()) {
335 if (service != s)
336 return 0;
337 return allow;
338 }
339 if (command.length()) {
340 if (command != c)
341 return 0;
342 for (map<string,StringConstraint>::const_iterator p = command_args.begin(); p != command_args.end(); ++p) {
343 map<string,string>::const_iterator q = c_args.find(p->first);
344 // argument must be present if a constraint exists
345 if (q == c_args.end())
346 return 0;
c07f9fc5
FG
347 switch (p->second.match_type) {
348 case StringConstraint::MATCH_TYPE_EQUAL:
7c673cae
FG
349 if (p->second.value != q->second)
350 return 0;
c07f9fc5
FG
351 break;
352 case StringConstraint::MATCH_TYPE_PREFIX:
353 if (q->second.find(p->second.value) != 0)
7c673cae 354 return 0;
c07f9fc5
FG
355 break;
356 case StringConstraint::MATCH_TYPE_REGEX:
11fdf7f2
TL
357 try {
358 std::regex pattern(
359 p->second.value, std::regex::extended);
360 if (!std::regex_match(q->second, pattern))
c07f9fc5 361 return 0;
11fdf7f2
TL
362 } catch(const std::regex_error&) {
363 return 0;
364 }
c07f9fc5
FG
365 break;
366 default:
367 break;
7c673cae
FG
368 }
369 }
370 return MON_CAP_ALL;
371 }
f64942e4
AA
372 // we don't allow config-key service to be accessed with blanket caps other
373 // than '*' (i.e., 'any'), and that should have been checked by the caller
374 // via 'is_allow_all()'.
375 if (s == "config-key") {
376 return 0;
377 }
7c673cae
FG
378 return allow;
379}
380
381ostream& operator<<(ostream&out, const MonCap& m)
382{
383 for (vector<MonCapGrant>::const_iterator p = m.grants.begin(); p != m.grants.end(); ++p) {
384 if (p != m.grants.begin())
385 out << ", ";
386 out << *p;
387 }
388 return out;
389}
390
391bool MonCap::is_allow_all() const
392{
393 for (vector<MonCapGrant>::const_iterator p = grants.begin(); p != grants.end(); ++p)
394 if (p->is_allow_all())
395 return true;
396 return false;
397}
398
399void MonCap::set_allow_all()
400{
401 grants.clear();
402 grants.push_back(MonCapGrant(MON_CAP_ANY));
403 text = "allow *";
404}
405
11fdf7f2
TL
406bool MonCap::is_capable(
407 CephContext *cct,
408 int daemon_type,
409 EntityName name,
410 const string& service,
411 const string& command, const map<string,string>& command_args,
412 bool op_may_read, bool op_may_write, bool op_may_exec,
413 const entity_addr_t& addr) const
7c673cae
FG
414{
415 if (cct)
416 ldout(cct, 20) << "is_capable service=" << service << " command=" << command
417 << (op_may_read ? " read":"")
418 << (op_may_write ? " write":"")
419 << (op_may_exec ? " exec":"")
11fdf7f2 420 << " addr " << addr
7c673cae
FG
421 << " on cap " << *this
422 << dendl;
11fdf7f2 423
7c673cae
FG
424 mon_rwxa_t allow = 0;
425 for (vector<MonCapGrant>::const_iterator p = grants.begin();
426 p != grants.end(); ++p) {
427 if (cct)
11fdf7f2
TL
428 ldout(cct, 20) << " allow so far " << allow << ", doing grant " << *p
429 << dendl;
430
431 if (p->network.size() &&
432 (!p->network_valid ||
433 !network_contains(p->network_parsed,
434 p->network_prefix,
435 addr))) {
436 continue;
437 }
7c673cae
FG
438
439 if (p->is_allow_all()) {
440 if (cct)
441 ldout(cct, 20) << " allow all" << dendl;
442 return true;
443 }
444
445 // check enumerated caps
446 allow = allow | p->get_allowed(cct, daemon_type, name, service, command,
447 command_args);
448 if ((!op_may_read || (allow & MON_CAP_R)) &&
449 (!op_may_write || (allow & MON_CAP_W)) &&
450 (!op_may_exec || (allow & MON_CAP_X))) {
451 if (cct)
452 ldout(cct, 20) << " match" << dendl;
453 return true;
454 }
455 }
456 return false;
457}
458
459void MonCap::encode(bufferlist& bl) const
460{
461 ENCODE_START(4, 4, bl); // legacy MonCaps was 3, 3
11fdf7f2 462 encode(text, bl);
7c673cae
FG
463 ENCODE_FINISH(bl);
464}
465
11fdf7f2 466void MonCap::decode(bufferlist::const_iterator& bl)
7c673cae
FG
467{
468 string s;
469 DECODE_START(4, bl);
11fdf7f2 470 decode(s, bl);
7c673cae
FG
471 DECODE_FINISH(bl);
472 parse(s, NULL);
473}
474
475void MonCap::dump(Formatter *f) const
476{
477 f->dump_string("text", text);
478}
479
480void MonCap::generate_test_instances(list<MonCap*>& ls)
481{
482 ls.push_back(new MonCap);
483 ls.push_back(new MonCap);
484 ls.back()->parse("allow *");
485 ls.push_back(new MonCap);
486 ls.back()->parse("allow rwx");
487 ls.push_back(new MonCap);
488 ls.back()->parse("allow service foo x");
489 ls.push_back(new MonCap);
490 ls.back()->parse("allow command bar x");
491 ls.push_back(new MonCap);
492 ls.back()->parse("allow service foo r, allow command bar x");
493 ls.push_back(new MonCap);
494 ls.back()->parse("allow command bar with k1=v1 x");
495 ls.push_back(new MonCap);
496 ls.back()->parse("allow command bar with k1=v1 k2=v2 x");
497}
498
499// grammar
500namespace qi = boost::spirit::qi;
501namespace ascii = boost::spirit::ascii;
502namespace phoenix = boost::phoenix;
503
504
505template <typename Iterator>
506struct MonCapParser : qi::grammar<Iterator, MonCap()>
507{
508 MonCapParser() : MonCapParser::base_type(moncap)
509 {
510 using qi::char_;
511 using qi::int_;
512 using qi::ulong_long;
513 using qi::lexeme;
514 using qi::alnum;
515 using qi::_val;
516 using qi::_1;
517 using qi::_2;
518 using qi::_3;
519 using qi::eps;
520 using qi::lit;
521
522 quoted_string %=
523 lexeme['"' >> +(char_ - '"') >> '"'] |
524 lexeme['\'' >> +(char_ - '\'') >> '\''];
f64942e4 525 unquoted_word %= +char_("a-zA-Z0-9_./-");
7c673cae 526 str %= quoted_string | unquoted_word;
11fdf7f2 527 network_str %= +char_("/.:a-fA-F0-9][");
7c673cae
FG
528
529 spaces = +(lit(' ') | lit('\n') | lit('\t'));
530
531 // command := command[=]cmd [k1=v1 k2=v2 ...]
c07f9fc5
FG
532 str_match = '=' >> qi::attr(StringConstraint::MATCH_TYPE_EQUAL) >> str;
533 str_prefix = spaces >> lit("prefix") >> spaces >>
534 qi::attr(StringConstraint::MATCH_TYPE_PREFIX) >> str;
535 str_regex = spaces >> lit("regex") >> spaces >>
536 qi::attr(StringConstraint::MATCH_TYPE_REGEX) >> str;
537 kv_pair = str >> (str_match | str_prefix | str_regex);
7c673cae
FG
538 kv_map %= kv_pair >> *(spaces >> kv_pair);
539 command_match = -spaces >> lit("allow") >> spaces >> lit("command") >> (lit('=') | spaces)
540 >> qi::attr(string()) >> qi::attr(string())
541 >> str
542 >> -(spaces >> lit("with") >> spaces >> kv_map)
11fdf7f2
TL
543 >> qi::attr(0)
544 >> -(spaces >> lit("network") >> spaces >> network_str);
7c673cae
FG
545
546 // service foo rwxa
547 service_match %= -spaces >> lit("allow") >> spaces >> lit("service") >> (lit('=') | spaces)
548 >> str >> qi::attr(string()) >> qi::attr(string())
549 >> qi::attr(map<string,StringConstraint>())
11fdf7f2
TL
550 >> spaces >> rwxa
551 >> -(spaces >> lit("network") >> spaces >> network_str);
7c673cae
FG
552
553 // profile foo
c07f9fc5
FG
554 profile_match %= -spaces >> -(lit("allow") >> spaces)
555 >> lit("profile") >> (lit('=') | spaces)
7c673cae
FG
556 >> qi::attr(string())
557 >> str
558 >> qi::attr(string())
559 >> qi::attr(map<string,StringConstraint>())
11fdf7f2
TL
560 >> qi::attr(0)
561 >> -(spaces >> lit("network") >> spaces >> network_str);
7c673cae
FG
562
563 // rwxa
564 rwxa_match %= -spaces >> lit("allow") >> spaces
565 >> qi::attr(string()) >> qi::attr(string()) >> qi::attr(string())
566 >> qi::attr(map<string,StringConstraint>())
11fdf7f2
TL
567 >> rwxa
568 >> -(spaces >> lit("network") >> spaces >> network_str);
7c673cae
FG
569
570 // rwxa := * | [r][w][x]
571 rwxa =
572 (lit("*")[_val = MON_CAP_ANY]) |
11fdf7f2 573 (lit("all")[_val = MON_CAP_ANY]) |
7c673cae
FG
574 ( eps[_val = 0] >>
575 ( lit('r')[_val |= MON_CAP_R] ||
576 lit('w')[_val |= MON_CAP_W] ||
577 lit('x')[_val |= MON_CAP_X]
578 )
579 );
580
581 // grant := allow ...
582 grant = -spaces >> (rwxa_match | profile_match | service_match | command_match) >> -spaces;
583
584 // moncap := grant [grant ...]
585 grants %= (grant % (*lit(' ') >> (lit(';') | lit(',')) >> *lit(' ')));
586 moncap = grants [_val = phoenix::construct<MonCap>(_1)];
587
588 }
589 qi::rule<Iterator> spaces;
590 qi::rule<Iterator, unsigned()> rwxa;
591 qi::rule<Iterator, string()> quoted_string;
592 qi::rule<Iterator, string()> unquoted_word;
11fdf7f2 593 qi::rule<Iterator, string()> str, network_str;
7c673cae 594
c07f9fc5 595 qi::rule<Iterator, StringConstraint()> str_match, str_prefix, str_regex;
7c673cae
FG
596 qi::rule<Iterator, pair<string, StringConstraint>()> kv_pair;
597 qi::rule<Iterator, map<string, StringConstraint>()> kv_map;
598
599 qi::rule<Iterator, MonCapGrant()> rwxa_match;
600 qi::rule<Iterator, MonCapGrant()> command_match;
601 qi::rule<Iterator, MonCapGrant()> service_match;
602 qi::rule<Iterator, MonCapGrant()> profile_match;
603 qi::rule<Iterator, MonCapGrant()> grant;
604 qi::rule<Iterator, std::vector<MonCapGrant>()> grants;
605 qi::rule<Iterator, MonCap()> moncap;
606};
607
608bool MonCap::parse(const string& str, ostream *err)
609{
11fdf7f2
TL
610 auto iter = str.begin();
611 auto end = str.end();
612
613 MonCapParser<string::const_iterator> exp;
614 bool r = qi::parse(iter, end, exp, *this);
7c673cae
FG
615 if (r && iter == end) {
616 text = str;
11fdf7f2
TL
617 for (auto& g : grants) {
618 g.parse_network();
619 }
7c673cae
FG
620 return true;
621 }
622
623 // Make sure no grants are kept after parsing failed!
624 grants.clear();
625
626 if (err) {
627 if (iter != end)
11fdf7f2
TL
628 *err << "mon capability parse failed, stopped at '"
629 << std::string(iter, end)
630 << "' of '" << str << "'";
7c673cae 631 else
11fdf7f2 632 *err << "mon capability parse failed, stopped at end of '" << str << "'";
7c673cae
FG
633 }
634
635 return false;
636}
637