Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / 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
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
31 using std::ostream;
32 using std::string;
33 namespace qi = boost::spirit::qi;
34 namespace ascii = boost::spirit::ascii;
35 namespace phoenix = boost::phoenix;
36
37 template <typename Iterator>
38 struct 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
99 void 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
111 bool 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
141 bool 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  */
161 bool 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  */
179 bool 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
271 void MDSAuthCaps::set_allow_all()
272 {
273     grants.clear();
274     grants.push_back(MDSCapGrant(
275                        MDSCapSpec(true, true, true, true),
276                        MDSCapMatch()));
277 }
278
279 bool 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
311 bool 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
323 ostream &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
349 ostream &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
366 ostream &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
378 ostream &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