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