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