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