]> git.proxmox.com Git - ceph.git/blob - ceph/src/mds/MDSAuthCaps.cc
bump version to 18.2.4-pve3
[ceph.git] / ceph / src / mds / MDSAuthCaps.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) 2014 Red Hat
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 <string_view>
16
17 #include <errno.h>
18
19 #include <boost/spirit/include/qi.hpp>
20 #include <boost/phoenix/operator.hpp>
21 #include <boost/phoenix.hpp>
22
23 #include "common/debug.h"
24 #include "MDSAuthCaps.h"
25 #include "mdstypes.h"
26 #include "include/ipaddr.h"
27
28 #define dout_subsys ceph_subsys_mds
29
30 #undef dout_prefix
31 #define dout_prefix *_dout << "MDSAuthCap "
32
33 using std::ostream;
34 using std::string;
35 using std::vector;
36 using std::string_view;
37 namespace qi = boost::spirit::qi;
38 namespace ascii = boost::spirit::ascii;
39 namespace phoenix = boost::phoenix;
40
41 template <typename Iterator>
42 struct MDSCapParser : qi::grammar<Iterator, MDSAuthCaps()>
43 {
44 MDSCapParser() : MDSCapParser::base_type(mdscaps)
45 {
46 using qi::attr;
47 using qi::bool_;
48 using qi::char_;
49 using qi::int_;
50 using qi::uint_;
51 using qi::lexeme;
52 using qi::alnum;
53 using qi::_val;
54 using qi::_1;
55 using qi::_2;
56 using qi::_3;
57 using qi::_4;
58 using qi::_5;
59 using qi::eps;
60 using qi::lit;
61
62 spaces = +(lit(' ') | lit('\n') | lit('\t'));
63
64 quoted_path %=
65 lexeme[lit("\"") >> *(char_ - '"') >> '"'] |
66 lexeme[lit("'") >> *(char_ - '\'') >> '\''];
67 unquoted_path %= +char_("a-zA-Z0-9_./-");
68 network_str %= +char_("/.:a-fA-F0-9][");
69 fs_name_str %= +char_("a-zA-Z0-9_.-");
70
71 path %= -(spaces >> lit("path") >> lit('=') >> (quoted_path | unquoted_path));
72 uid %= -(spaces >> lit("uid") >> lit('=') >> uint_);
73 uintlist %= (uint_ % lit(','));
74 gidlist %= -(spaces >> lit("gids") >> lit('=') >> uintlist);
75 fs_name %= -(spaces >> lit("fsname") >> lit('=') >> fs_name_str);
76 root_squash %= -(spaces >> lit("root_squash") >> attr(true));
77 match = (fs_name >> path >> root_squash >> uid >> gidlist)[_val = phoenix::construct<MDSCapMatch>(_1, _2, _3, _4, _5)];
78
79 // capspec = * | r[w][f][p][s]
80 capspec = spaces >> (
81 lit("*")[_val = MDSCapSpec(MDSCapSpec::ALL)]
82 |
83 lit("all")[_val = MDSCapSpec(MDSCapSpec::ALL)]
84 |
85 (lit("rwfps"))[_val = MDSCapSpec(MDSCapSpec::RWFPS)]
86 |
87 (lit("rwps"))[_val = MDSCapSpec(MDSCapSpec::RWPS)]
88 |
89 (lit("rwfp"))[_val = MDSCapSpec(MDSCapSpec::RWFP)]
90 |
91 (lit("rwfs"))[_val = MDSCapSpec(MDSCapSpec::RWFS)]
92 |
93 (lit("rwp"))[_val = MDSCapSpec(MDSCapSpec::RWP)]
94 |
95 (lit("rws"))[_val = MDSCapSpec(MDSCapSpec::RWS)]
96 |
97 (lit("rwf"))[_val = MDSCapSpec(MDSCapSpec::RWF)]
98 |
99 (lit("rw"))[_val = MDSCapSpec(MDSCapSpec::RW)]
100 |
101 (lit("r"))[_val = MDSCapSpec(MDSCapSpec::READ)]
102 );
103
104 grant = lit("allow") >> (capspec >> match >>
105 -(spaces >> lit("network") >> spaces >> network_str))
106 [_val = phoenix::construct<MDSCapGrant>(_1, _2, _3)];
107 grants %= (grant % (*lit(' ') >> (lit(';') | lit(',')) >> *lit(' ')));
108 mdscaps = grants [_val = phoenix::construct<MDSAuthCaps>(_1)];
109 }
110 qi::rule<Iterator> spaces;
111 qi::rule<Iterator, string()> quoted_path, unquoted_path, network_str;
112 qi::rule<Iterator, string()> fs_name_str, fs_name, path;
113 qi::rule<Iterator, bool()> root_squash;
114 qi::rule<Iterator, MDSCapSpec()> capspec;
115 qi::rule<Iterator, uint32_t()> uid;
116 qi::rule<Iterator, vector<uint32_t>() > uintlist;
117 qi::rule<Iterator, vector<uint32_t>() > gidlist;
118 qi::rule<Iterator, MDSCapMatch()> match;
119 qi::rule<Iterator, MDSCapGrant()> grant;
120 qi::rule<Iterator, vector<MDSCapGrant>()> grants;
121 qi::rule<Iterator, MDSAuthCaps()> mdscaps;
122 };
123
124 void MDSCapMatch::normalize_path()
125 {
126 // drop any leading /
127 while (path.length() && path[0] == '/') {
128 path = path.substr(1);
129 }
130
131 // drop dup //
132 // drop .
133 // drop ..
134 }
135
136 bool MDSCapMatch::match(string_view target_path,
137 const int caller_uid,
138 const int caller_gid,
139 const vector<uint64_t> *caller_gid_list) const
140 {
141 if (uid != MDS_AUTH_UID_ANY) {
142 if (uid != caller_uid)
143 return false;
144 if (!gids.empty()) {
145 bool gid_matched = false;
146 if (std::find(gids.begin(), gids.end(), caller_gid) != gids.end())
147 gid_matched = true;
148 if (caller_gid_list) {
149 for (auto i = caller_gid_list->begin(); i != caller_gid_list->end(); ++i) {
150 if (std::find(gids.begin(), gids.end(), *i) != gids.end()) {
151 gid_matched = true;
152 break;
153 }
154 }
155 }
156 if (!gid_matched)
157 return false;
158 }
159 }
160
161 if (!match_path(target_path)) {
162 return false;
163 }
164
165 return true;
166 }
167
168 bool MDSCapMatch::match_path(string_view target_path) const
169 {
170 if (path.length()) {
171 if (target_path.find(path) != 0)
172 return false;
173 // if path doesn't already have a trailing /, make sure the target
174 // does so that path=/foo doesn't match target_path=/food
175 if (target_path.length() > path.length() &&
176 path[path.length()-1] != '/' &&
177 target_path[path.length()] != '/')
178 return false;
179 }
180
181 return true;
182 }
183
184 void MDSCapGrant::parse_network()
185 {
186 network_valid = ::parse_network(network.c_str(), &network_parsed,
187 &network_prefix);
188 }
189
190 /**
191 * Is the client *potentially* able to access this path? Actual
192 * permission will depend on uids/modes in the full is_capable.
193 */
194 bool MDSAuthCaps::path_capable(string_view inode_path) const
195 {
196 for (const auto &i : grants) {
197 if (i.match.match_path(inode_path)) {
198 return true;
199 }
200 }
201
202 return false;
203 }
204
205 /**
206 * For a given filesystem path, query whether this capability carries`
207 * authorization to read or write.
208 *
209 * This is true if any of the 'grant' clauses in the capability match the
210 * requested path + op.
211 */
212 bool MDSAuthCaps::is_capable(string_view inode_path,
213 uid_t inode_uid, gid_t inode_gid,
214 unsigned inode_mode,
215 uid_t caller_uid, gid_t caller_gid,
216 const vector<uint64_t> *caller_gid_list,
217 unsigned mask,
218 uid_t new_uid, gid_t new_gid,
219 const entity_addr_t& addr) const
220 {
221 ldout(g_ceph_context, 10) << __func__ << " inode(path /" << inode_path
222 << " owner " << inode_uid << ":" << inode_gid
223 << " mode 0" << std::oct << inode_mode << std::dec
224 << ") by caller " << caller_uid << ":" << caller_gid
225 // << "[" << caller_gid_list << "]";
226 << " mask " << mask
227 << " new " << new_uid << ":" << new_gid
228 << " cap: " << *this << dendl;
229
230 for (const auto& grant : grants) {
231 if (grant.network.size() &&
232 (!grant.network_valid ||
233 !network_contains(grant.network_parsed,
234 grant.network_prefix,
235 addr))) {
236 continue;
237 }
238
239 if (grant.match.match(inode_path, caller_uid, caller_gid, caller_gid_list) &&
240 grant.spec.allows(mask & (MAY_READ|MAY_EXECUTE), mask & MAY_WRITE)) {
241 if (grant.match.root_squash && ((caller_uid == 0) || (caller_gid == 0)) &&
242 (mask & MAY_WRITE)) {
243 continue;
244 }
245 // we have a match; narrow down GIDs to those specifically allowed here
246 vector<uint64_t> gids;
247 if (std::find(grant.match.gids.begin(), grant.match.gids.end(), caller_gid) !=
248 grant.match.gids.end()) {
249 gids.push_back(caller_gid);
250 }
251 if (caller_gid_list) {
252 std::set_intersection(grant.match.gids.begin(), grant.match.gids.end(),
253 caller_gid_list->begin(), caller_gid_list->end(),
254 std::back_inserter(gids));
255 std::sort(gids.begin(), gids.end());
256 }
257
258
259 // Spec is non-allowing if caller asked for set pool but spec forbids it
260 if (mask & MAY_SET_VXATTR) {
261 if (!grant.spec.allow_set_vxattr()) {
262 continue;
263 }
264 }
265
266 if (mask & MAY_SNAPSHOT) {
267 if (!grant.spec.allow_snapshot()) {
268 continue;
269 }
270 }
271
272 if (mask & MAY_FULL) {
273 if (!grant.spec.allow_full()) {
274 continue;
275 }
276 }
277
278 // check unix permissions?
279 if (grant.match.uid == MDSCapMatch::MDS_AUTH_UID_ANY) {
280 return true;
281 }
282
283 // chown/chgrp
284 if (mask & MAY_CHOWN) {
285 if (new_uid != caller_uid || // you can't chown to someone else
286 inode_uid != caller_uid) { // you can't chown from someone else
287 continue;
288 }
289 }
290 if (mask & MAY_CHGRP) {
291 // you can only chgrp *to* one of your groups... if you own the file.
292 if (inode_uid != caller_uid ||
293 std::find(gids.begin(), gids.end(), new_gid) ==
294 gids.end()) {
295 continue;
296 }
297 }
298
299 if (inode_uid == caller_uid) {
300 if ((!(mask & MAY_READ) || (inode_mode & S_IRUSR)) &&
301 (!(mask & MAY_WRITE) || (inode_mode & S_IWUSR)) &&
302 (!(mask & MAY_EXECUTE) || (inode_mode & S_IXUSR))) {
303 return true;
304 }
305 } else if (std::find(gids.begin(), gids.end(),
306 inode_gid) != gids.end()) {
307 if ((!(mask & MAY_READ) || (inode_mode & S_IRGRP)) &&
308 (!(mask & MAY_WRITE) || (inode_mode & S_IWGRP)) &&
309 (!(mask & MAY_EXECUTE) || (inode_mode & S_IXGRP))) {
310 return true;
311 }
312 } else {
313 if ((!(mask & MAY_READ) || (inode_mode & S_IROTH)) &&
314 (!(mask & MAY_WRITE) || (inode_mode & S_IWOTH)) &&
315 (!(mask & MAY_EXECUTE) || (inode_mode & S_IXOTH))) {
316 return true;
317 }
318 }
319 }
320 }
321
322 return false;
323 }
324
325 void MDSAuthCaps::set_allow_all()
326 {
327 grants.clear();
328 grants.push_back(MDSCapGrant(MDSCapSpec(MDSCapSpec::ALL), MDSCapMatch(),
329 {}));
330 }
331
332 bool MDSAuthCaps::parse(string_view str, ostream *err)
333 {
334 // Special case for legacy caps
335 if (str == "allow") {
336 grants.clear();
337 grants.push_back(MDSCapGrant(MDSCapSpec(MDSCapSpec::RWPS), MDSCapMatch(),
338 {}));
339 return true;
340 }
341
342 auto iter = str.begin();
343 auto end = str.end();
344 MDSCapParser<decltype(iter)> g;
345
346 bool r = qi::phrase_parse(iter, end, g, ascii::space, *this);
347 if (r && iter == end) {
348 for (auto& grant : grants) {
349 std::sort(grant.match.gids.begin(), grant.match.gids.end());
350 grant.parse_network();
351 }
352 return true;
353 } else {
354 // Make sure no grants are kept after parsing failed!
355 grants.clear();
356
357 if (err) {
358 if (string(iter, end).find("allow") != string::npos) {
359 *err << "Permission flags in MDS caps must start with 'r' or " <<
360 "'rw' or be '*' or 'all'";
361 } else {
362 *err << "mds capability parse failed, stopped at '"
363 << string(iter, end) << "' of '" << str << "'";
364 }
365 }
366 return false;
367 }
368 }
369
370
371 bool MDSAuthCaps::allow_all() const
372 {
373 for (const auto& grant : grants) {
374 if (grant.match.is_match_all() && grant.spec.allow_all()) {
375 return true;
376 }
377 }
378
379 return false;
380 }
381
382
383 ostream &operator<<(ostream &out, const MDSCapMatch &match)
384 {
385 if (!match.fs_name.empty()) {
386 out << " fsname=" << match.fs_name;
387 }
388 if (match.path.length()) {
389 out << " path=\"/" << match.path << "\"";
390 }
391 if (match.root_squash) {
392 out << " root_squash";
393 }
394 if (match.uid != MDSCapMatch::MDS_AUTH_UID_ANY) {
395 out << " uid=" << match.uid;
396 if (!match.gids.empty()) {
397 out << " gids=";
398 bool first = true;
399 for (const auto& gid : match.gids) {
400 if (!first)
401 out << ',';
402 out << gid;
403 first = false;
404 }
405 }
406 }
407
408 return out;
409 }
410
411
412 ostream &operator<<(ostream &out, const MDSCapSpec &spec)
413 {
414 if (spec.allow_all()) {
415 out << "*";
416 } else {
417 if (spec.allow_read()) {
418 out << "r";
419 }
420 if (spec.allow_write()) {
421 out << "w";
422 }
423 if (spec.allow_full()) {
424 out << "f";
425 }
426 if (spec.allow_set_vxattr()) {
427 out << "p";
428 }
429 if (spec.allow_snapshot()) {
430 out << "s";
431 }
432 }
433
434 return out;
435 }
436
437
438 ostream &operator<<(ostream &out, const MDSCapGrant &grant)
439 {
440 out << "allow ";
441 out << grant.spec;
442 out << grant.match;
443 if (grant.network.size()) {
444 out << " network " << grant.network;
445 }
446 return out;
447 }
448
449
450 ostream &operator<<(ostream &out, const MDSAuthCaps &cap)
451 {
452 out << "MDSAuthCaps[";
453 for (size_t i = 0; i < cap.grants.size(); ++i) {
454 out << cap.grants[i];
455 if (i < cap.grants.size() - 1) {
456 out << ", ";
457 }
458 }
459 out << "]";
460
461 return out;
462 }
463
464 ostream &operator<<(ostream &out, const MDSCapAuth &auth)
465 {
466 out << "MDSCapAuth(" << auth.match << "readable="
467 << auth.readable << ", writeable=" << auth.writeable << ")";
468 return out;
469 }