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