]> git.proxmox.com Git - ceph.git/blame - ceph/src/mds/MDSAuthCaps.cc
add subtree-ish sources for 12.0.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;
119 bool gid_matched = false;
120 if (std::find(gids.begin(), gids.end(), caller_gid) != gids.end())
121 gid_matched = true;
122 if (caller_gid_list) {
123 for (auto i = caller_gid_list->begin(); i != caller_gid_list->end(); ++i) {
124 if (std::find(gids.begin(), gids.end(), *i) != gids.end()) {
125 gid_matched = true;
126 break;
127 }
128 }
129 }
130 if (!gid_matched)
131 return false;
132 }
133
134 if (!match_path(target_path)) {
135 return false;
136 }
137
138 return true;
139}
140
141bool MDSCapMatch::match_path(const std::string &target_path) const
142{
143 if (path.length()) {
144 if (target_path.find(path) != 0)
145 return false;
146 // if path doesn't already have a trailing /, make sure the target
147 // does so that path=/foo doesn't match target_path=/food
148 if (target_path.length() > path.length() &&
149 path[path.length()-1] != '/' &&
150 target_path[path.length()] != '/')
151 return false;
152 }
153
154 return true;
155}
156
157/**
158 * Is the client *potentially* able to access this path? Actual
159 * permission will depend on uids/modes in the full is_capable.
160 */
161bool MDSAuthCaps::path_capable(const std::string &inode_path) const
162{
163 for (const auto &i : grants) {
164 if (i.match.match_path(inode_path)) {
165 return true;
166 }
167 }
168
169 return false;
170}
171
172/**
173 * For a given filesystem path, query whether this capability carries`
174 * authorization to read or write.
175 *
176 * This is true if any of the 'grant' clauses in the capability match the
177 * requested path + op.
178 */
179bool MDSAuthCaps::is_capable(const std::string &inode_path,
180 uid_t inode_uid, gid_t inode_gid,
181 unsigned inode_mode,
182 uid_t caller_uid, gid_t caller_gid,
183 const vector<uint64_t> *caller_gid_list,
184 unsigned mask,
185 uid_t new_uid, gid_t new_gid) const
186{
187 if (cct)
188 ldout(cct, 10) << __func__ << " inode(path /" << inode_path
189 << " owner " << inode_uid << ":" << inode_gid
190 << " mode 0" << std::oct << inode_mode << std::dec
191 << ") by caller " << caller_uid << ":" << caller_gid
192// << "[" << caller_gid_list << "]";
193 << " mask " << mask
194 << " new " << new_uid << ":" << new_gid
195 << " cap: " << *this << dendl;
196
197 for (std::vector<MDSCapGrant>::const_iterator i = grants.begin();
198 i != grants.end();
199 ++i) {
200
201 if (i->match.match(inode_path, caller_uid, caller_gid, caller_gid_list) &&
202 i->spec.allows(mask & (MAY_READ|MAY_EXECUTE), mask & MAY_WRITE)) {
203 // we have a match; narrow down GIDs to those specifically allowed here
204 vector<uint64_t> gids;
205 if (std::find(i->match.gids.begin(), i->match.gids.end(), caller_gid) !=
206 i->match.gids.end()) {
207 gids.push_back(caller_gid);
208 }
209 if (caller_gid_list) {
210 std::set_intersection(i->match.gids.begin(), i->match.gids.end(),
211 caller_gid_list->begin(), caller_gid_list->end(),
212 std::back_inserter(gids));
213 std::sort(gids.begin(), gids.end());
214 }
215
216
217 // Spec is non-allowing if caller asked for set pool but spec forbids it
218 if (mask & MAY_SET_VXATTR) {
219 if (!i->spec.allows_set_vxattr()) {
220 continue;
221 }
222 }
223
224 // check unix permissions?
225 if (i->match.uid == MDSCapMatch::MDS_AUTH_UID_ANY) {
226 return true;
227 }
228
229 // chown/chgrp
230 if (mask & MAY_CHOWN) {
231 if (new_uid != caller_uid || // you can't chown to someone else
232 inode_uid != caller_uid) { // you can't chown from someone else
233 continue;
234 }
235 }
236 if (mask & MAY_CHGRP) {
237 // you can only chgrp *to* one of your groups... if you own the file.
238 if (inode_uid != caller_uid ||
239 std::find(gids.begin(), gids.end(), new_gid) ==
240 gids.end()) {
241 continue;
242 }
243 }
244
245 if (inode_uid == caller_uid) {
246 if ((!(mask & MAY_READ) || (inode_mode & S_IRUSR)) &&
247 (!(mask & MAY_WRITE) || (inode_mode & S_IWUSR)) &&
248 (!(mask & MAY_EXECUTE) || (inode_mode & S_IXUSR))) {
249 return true;
250 }
251 } else if (std::find(gids.begin(), gids.end(),
252 inode_gid) != gids.end()) {
253 if ((!(mask & MAY_READ) || (inode_mode & S_IRGRP)) &&
254 (!(mask & MAY_WRITE) || (inode_mode & S_IWGRP)) &&
255 (!(mask & MAY_EXECUTE) || (inode_mode & S_IXGRP))) {
256 return true;
257 }
258 } else {
259 if ((!(mask & MAY_READ) || (inode_mode & S_IROTH)) &&
260 (!(mask & MAY_WRITE) || (inode_mode & S_IWOTH)) &&
261 (!(mask & MAY_EXECUTE) || (inode_mode & S_IXOTH))) {
262 return true;
263 }
264 }
265 }
266 }
267
268 return false;
269}
270
271void MDSAuthCaps::set_allow_all()
272{
273 grants.clear();
274 grants.push_back(MDSCapGrant(
275 MDSCapSpec(true, true, true, true),
276 MDSCapMatch()));
277}
278
279bool MDSAuthCaps::parse(CephContext *c, const std::string& str, ostream *err)
280{
281 // Special case for legacy caps
282 if (str == "allow") {
283 grants.clear();
284 grants.push_back(MDSCapGrant(MDSCapSpec(true, true, false, true), MDSCapMatch()));
285 return true;
286 }
287
288 MDSCapParser<std::string::const_iterator> g;
289 std::string::const_iterator iter = str.begin();
290 std::string::const_iterator end = str.end();
291
292 bool r = qi::phrase_parse(iter, end, g, ascii::space, *this);
293 cct = c; // set after parser self-assignment
294 if (r && iter == end) {
295 for (auto& grant : grants) {
296 std::sort(grant.match.gids.begin(), grant.match.gids.end());
297 }
298 return true;
299 } else {
300 // Make sure no grants are kept after parsing failed!
301 grants.clear();
302
303 if (err)
304 *err << "MDSAuthCaps parse failed, stopped at '" << std::string(iter, end)
305 << "' of '" << str << "'\n";
306 return false;
307 }
308}
309
310
311bool MDSAuthCaps::allow_all() const
312{
313 for (std::vector<MDSCapGrant>::const_iterator i = grants.begin(); i != grants.end(); ++i) {
314 if (i->match.is_match_all() && i->spec.allow_all()) {
315 return true;
316 }
317 }
318
319 return false;
320}
321
322
323ostream &operator<<(ostream &out, const MDSCapMatch &match)
324{
325 if (match.path.length()) {
326 out << "path=\"/" << match.path << "\"";
327 if (match.uid != MDSCapMatch::MDS_AUTH_UID_ANY) {
328 out << " ";
329 }
330 }
331 if (match.uid != MDSCapMatch::MDS_AUTH_UID_ANY) {
332 out << "uid=" << match.uid;
333 if (!match.gids.empty()) {
334 out << " gids=";
335 for (std::vector<gid_t>::const_iterator p = match.gids.begin();
336 p != match.gids.end();
337 ++p) {
338 if (p != match.gids.begin())
339 out << ',';
340 out << *p;
341 }
342 }
343 }
344
345 return out;
346}
347
348
349ostream &operator<<(ostream &out, const MDSCapSpec &spec)
350{
351 if (spec.any) {
352 out << "*";
353 } else {
354 if (spec.read) {
355 out << "r";
356 }
357 if (spec.write) {
358 out << "w";
359 }
360 }
361
362 return out;
363}
364
365
366ostream &operator<<(ostream &out, const MDSCapGrant &grant)
367{
368 out << "allow ";
369 out << grant.spec;
370 if (!grant.match.is_match_all()) {
371 out << " " << grant.match;
372 }
373
374 return out;
375}
376
377
378ostream &operator<<(ostream &out, const MDSAuthCaps &cap)
379{
380 out << "MDSAuthCaps[";
381 for (size_t i = 0; i < cap.grants.size(); ++i) {
382 out << cap.grants[i];
383 if (i < cap.grants.size() - 1) {
384 out << ", ";
385 }
386 }
387 out << "]";
388
389 return out;
390}
391