]>
git.proxmox.com Git - ceph.git/blob - 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
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2014 Red Hat
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.
19 #include <boost/spirit/include/qi.hpp>
20 #include <boost/spirit/include/phoenix_operator.hpp>
21 #include <boost/spirit/include/phoenix.hpp>
23 #include "common/debug.h"
24 #include "MDSAuthCaps.h"
26 #define dout_subsys ceph_subsys_mds
29 #define dout_prefix *_dout << "MDSAuthCap "
33 namespace qi
= boost::spirit::qi
;
34 namespace ascii
= boost::spirit::ascii
;
35 namespace phoenix
= boost::phoenix
;
37 template <typename Iterator
>
38 struct MDSCapParser
: qi::grammar
<Iterator
, MDSAuthCaps()>
40 MDSCapParser() : MDSCapParser::base_type(mdscaps
)
54 spaces
= +(lit(' ') | lit('\n') | lit('\t'));
57 lexeme
[lit("\"") >> *(char_
- '"') >> '"'] |
58 lexeme
[lit("'") >> *(char_
- '\'') >> '\''];
59 unquoted_path
%= +char_("a-zA-Z0-9_./-");
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
);
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
)]);
73 lit("*")[_val
= MDSCapSpec(true, true, true, true)]
75 (lit("rwp"))[_val
= MDSCapSpec(true, true, false, true)]
77 (lit("rw"))[_val
= MDSCapSpec(true, true, false, false)]
79 (lit("r"))[_val
= MDSCapSpec(true, false, false, false)]
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
)];
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
;
99 void MDSCapMatch::normalize_path()
101 // drop any leading /
102 while (path
.length() && path
[0] == '/') {
103 path
= path
.substr(1);
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
116 if (uid
!= MDS_AUTH_UID_ANY
) {
117 if (uid
!= caller_uid
)
120 bool gid_matched
= false;
121 if (std::find(gids
.begin(), gids
.end(), caller_gid
) != gids
.end())
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()) {
136 if (!match_path(target_path
)) {
143 bool MDSCapMatch::match_path(const std::string
&target_path
) const
146 if (target_path
.find(path
) != 0)
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()] != '/')
160 * Is the client *potentially* able to access this path? Actual
161 * permission will depend on uids/modes in the full is_capable.
163 bool MDSAuthCaps::path_capable(const std::string
&inode_path
) const
165 for (const auto &i
: grants
) {
166 if (i
.match
.match_path(inode_path
)) {
175 * For a given filesystem path, query whether this capability carries`
176 * authorization to read or write.
178 * This is true if any of the 'grant' clauses in the capability match the
179 * requested path + op.
181 bool MDSAuthCaps::is_capable(const std::string
&inode_path
,
182 uid_t inode_uid
, gid_t inode_gid
,
184 uid_t caller_uid
, gid_t caller_gid
,
185 const vector
<uint64_t> *caller_gid_list
,
187 uid_t new_uid
, gid_t new_gid
) const
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 << "]";
196 << " new " << new_uid
<< ":" << new_gid
197 << " cap: " << *this << dendl
;
199 for (std::vector
<MDSCapGrant
>::const_iterator i
= grants
.begin();
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
);
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());
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()) {
226 // check unix permissions?
227 if (i
->match
.uid
== MDSCapMatch::MDS_AUTH_UID_ANY
) {
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
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
) ==
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
))) {
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
))) {
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
))) {
273 void MDSAuthCaps::set_allow_all()
276 grants
.push_back(MDSCapGrant(
277 MDSCapSpec(true, true, true, true),
281 bool MDSAuthCaps::parse(CephContext
*c
, const std::string
& str
, ostream
*err
)
283 // Special case for legacy caps
284 if (str
== "allow") {
286 grants
.push_back(MDSCapGrant(MDSCapSpec(true, true, false, true), MDSCapMatch()));
290 MDSCapParser
<std::string::const_iterator
> g
;
291 std::string::const_iterator iter
= str
.begin();
292 std::string::const_iterator end
= str
.end();
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());
302 // Make sure no grants are kept after parsing failed!
306 *err
<< "MDSAuthCaps parse failed, stopped at '" << std::string(iter
, end
)
307 << "' of '" << str
<< "'\n";
313 bool MDSAuthCaps::allow_all() const
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()) {
325 ostream
&operator<<(ostream
&out
, const MDSCapMatch
&match
)
327 if (match
.path
.length()) {
328 out
<< "path=\"/" << match
.path
<< "\"";
329 if (match
.uid
!= MDSCapMatch::MDS_AUTH_UID_ANY
) {
333 if (match
.uid
!= MDSCapMatch::MDS_AUTH_UID_ANY
) {
334 out
<< "uid=" << match
.uid
;
335 if (!match
.gids
.empty()) {
337 for (std::vector
<gid_t
>::const_iterator p
= match
.gids
.begin();
338 p
!= match
.gids
.end();
340 if (p
!= match
.gids
.begin())
351 ostream
&operator<<(ostream
&out
, const MDSCapSpec
&spec
)
368 ostream
&operator<<(ostream
&out
, const MDSCapGrant
&grant
)
372 if (!grant
.match
.is_match_all()) {
373 out
<< " " << grant
.match
;
380 ostream
&operator<<(ostream
&out
, const MDSAuthCaps
&cap
)
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) {