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