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