]>
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.
15 #include <string_view>
19 #include <boost/spirit/include/qi.hpp>
20 #include <boost/phoenix/operator.hpp>
21 #include <boost/phoenix.hpp>
23 #include "common/debug.h"
24 #include "MDSAuthCaps.h"
26 #include "include/ipaddr.h"
28 #define dout_subsys ceph_subsys_mds
31 #define dout_prefix *_dout << "MDSAuthCap "
36 using std::string_view
;
37 namespace qi
= boost::spirit::qi
;
38 namespace ascii
= boost::spirit::ascii
;
39 namespace phoenix
= boost::phoenix
;
41 template <typename Iterator
>
42 struct MDSCapParser
: qi::grammar
<Iterator
, MDSAuthCaps()>
44 MDSCapParser() : MDSCapParser::base_type(mdscaps
)
62 spaces
= +(lit(' ') | lit('\n') | lit('\t'));
65 lexeme
[lit("\"") >> *(char_
- '"') >> '"'] |
66 lexeme
[lit("'") >> *(char_
- '\'') >> '\''];
67 unquoted_path
%= +char_("a-zA-Z0-9_./-");
68 network_str
%= +char_("/.:a-fA-F0-9][");
69 fs_name_str
%= +char_("a-zA-Z0-9_.-");
71 path
%= -(spaces
>> lit("path") >> lit('=') >> (quoted_path
| unquoted_path
));
72 uid
%= -(spaces
>> lit("uid") >> lit('=') >> uint_
);
73 uintlist
%= (uint_
% lit(','));
74 gidlist
%= -(spaces
>> lit("gids") >> lit('=') >> uintlist
);
75 fs_name
%= -(spaces
>> lit("fsname") >> lit('=') >> fs_name_str
);
76 root_squash
%= -(spaces
>> lit("root_squash") >> attr(true));
77 match
= (fs_name
>> path
>> root_squash
>> uid
>> gidlist
)[_val
= phoenix::construct
<MDSCapMatch
>(_1
, _2
, _3
, _4
, _5
)];
79 // capspec = * | r[w][f][p][s]
81 lit("*")[_val
= MDSCapSpec(MDSCapSpec::ALL
)]
83 lit("all")[_val
= MDSCapSpec(MDSCapSpec::ALL
)]
85 (lit("rwfps"))[_val
= MDSCapSpec(MDSCapSpec::RWFPS
)]
87 (lit("rwps"))[_val
= MDSCapSpec(MDSCapSpec::RWPS
)]
89 (lit("rwfp"))[_val
= MDSCapSpec(MDSCapSpec::RWFP
)]
91 (lit("rwfs"))[_val
= MDSCapSpec(MDSCapSpec::RWFS
)]
93 (lit("rwp"))[_val
= MDSCapSpec(MDSCapSpec::RWP
)]
95 (lit("rws"))[_val
= MDSCapSpec(MDSCapSpec::RWS
)]
97 (lit("rwf"))[_val
= MDSCapSpec(MDSCapSpec::RWF
)]
99 (lit("rw"))[_val
= MDSCapSpec(MDSCapSpec::RW
)]
101 (lit("r"))[_val
= MDSCapSpec(MDSCapSpec::READ
)]
104 grant
= lit("allow") >> (capspec
>> match
>>
105 -(spaces
>> lit("network") >> spaces
>> network_str
))
106 [_val
= phoenix::construct
<MDSCapGrant
>(_1
, _2
, _3
)];
107 grants
%= (grant
% (*lit(' ') >> (lit(';') | lit(',')) >> *lit(' ')));
108 mdscaps
= grants
[_val
= phoenix::construct
<MDSAuthCaps
>(_1
)];
110 qi::rule
<Iterator
> spaces
;
111 qi::rule
<Iterator
, string()> quoted_path
, unquoted_path
, network_str
;
112 qi::rule
<Iterator
, string()> fs_name_str
, fs_name
, path
;
113 qi::rule
<Iterator
, bool()> root_squash
;
114 qi::rule
<Iterator
, MDSCapSpec()> capspec
;
115 qi::rule
<Iterator
, uint32_t()> uid
;
116 qi::rule
<Iterator
, vector
<uint32_t>() > uintlist
;
117 qi::rule
<Iterator
, vector
<uint32_t>() > gidlist
;
118 qi::rule
<Iterator
, MDSCapMatch()> match
;
119 qi::rule
<Iterator
, MDSCapGrant()> grant
;
120 qi::rule
<Iterator
, vector
<MDSCapGrant
>()> grants
;
121 qi::rule
<Iterator
, MDSAuthCaps()> mdscaps
;
124 void MDSCapMatch::normalize_path()
126 // drop any leading /
127 while (path
.length() && path
[0] == '/') {
128 path
= path
.substr(1);
136 bool MDSCapMatch::match(string_view target_path
,
137 const int caller_uid
,
138 const int caller_gid
,
139 const vector
<uint64_t> *caller_gid_list
) const
141 if (uid
!= MDS_AUTH_UID_ANY
) {
142 if (uid
!= caller_uid
)
145 bool gid_matched
= false;
146 if (std::find(gids
.begin(), gids
.end(), caller_gid
) != gids
.end())
148 if (caller_gid_list
) {
149 for (auto i
= caller_gid_list
->begin(); i
!= caller_gid_list
->end(); ++i
) {
150 if (std::find(gids
.begin(), gids
.end(), *i
) != gids
.end()) {
161 if (!match_path(target_path
)) {
168 bool MDSCapMatch::match_path(string_view target_path
) const
171 if (target_path
.find(path
) != 0)
173 // if path doesn't already have a trailing /, make sure the target
174 // does so that path=/foo doesn't match target_path=/food
175 if (target_path
.length() > path
.length() &&
176 path
[path
.length()-1] != '/' &&
177 target_path
[path
.length()] != '/')
184 void MDSCapGrant::parse_network()
186 network_valid
= ::parse_network(network
.c_str(), &network_parsed
,
191 * Is the client *potentially* able to access this path? Actual
192 * permission will depend on uids/modes in the full is_capable.
194 bool MDSAuthCaps::path_capable(string_view inode_path
) const
196 for (const auto &i
: grants
) {
197 if (i
.match
.match_path(inode_path
)) {
206 * For a given filesystem path, query whether this capability carries`
207 * authorization to read or write.
209 * This is true if any of the 'grant' clauses in the capability match the
210 * requested path + op.
212 bool MDSAuthCaps::is_capable(string_view inode_path
,
213 uid_t inode_uid
, gid_t inode_gid
,
215 uid_t caller_uid
, gid_t caller_gid
,
216 const vector
<uint64_t> *caller_gid_list
,
218 uid_t new_uid
, gid_t new_gid
,
219 const entity_addr_t
& addr
) const
221 ldout(g_ceph_context
, 10) << __func__
<< " inode(path /" << inode_path
222 << " owner " << inode_uid
<< ":" << inode_gid
223 << " mode 0" << std::oct
<< inode_mode
<< std::dec
224 << ") by caller " << caller_uid
<< ":" << caller_gid
225 // << "[" << caller_gid_list << "]";
227 << " new " << new_uid
<< ":" << new_gid
228 << " cap: " << *this << dendl
;
230 for (const auto& grant
: grants
) {
231 if (grant
.network
.size() &&
232 (!grant
.network_valid
||
233 !network_contains(grant
.network_parsed
,
234 grant
.network_prefix
,
239 if (grant
.match
.match(inode_path
, caller_uid
, caller_gid
, caller_gid_list
) &&
240 grant
.spec
.allows(mask
& (MAY_READ
|MAY_EXECUTE
), mask
& MAY_WRITE
)) {
241 if (grant
.match
.root_squash
&& ((caller_uid
== 0) || (caller_gid
== 0)) &&
242 (mask
& MAY_WRITE
)) {
245 // we have a match; narrow down GIDs to those specifically allowed here
246 vector
<uint64_t> gids
;
247 if (std::find(grant
.match
.gids
.begin(), grant
.match
.gids
.end(), caller_gid
) !=
248 grant
.match
.gids
.end()) {
249 gids
.push_back(caller_gid
);
251 if (caller_gid_list
) {
252 std::set_intersection(grant
.match
.gids
.begin(), grant
.match
.gids
.end(),
253 caller_gid_list
->begin(), caller_gid_list
->end(),
254 std::back_inserter(gids
));
255 std::sort(gids
.begin(), gids
.end());
259 // Spec is non-allowing if caller asked for set pool but spec forbids it
260 if (mask
& MAY_SET_VXATTR
) {
261 if (!grant
.spec
.allow_set_vxattr()) {
266 if (mask
& MAY_SNAPSHOT
) {
267 if (!grant
.spec
.allow_snapshot()) {
272 if (mask
& MAY_FULL
) {
273 if (!grant
.spec
.allow_full()) {
278 // check unix permissions?
279 if (grant
.match
.uid
== MDSCapMatch::MDS_AUTH_UID_ANY
) {
284 if (mask
& MAY_CHOWN
) {
285 if (new_uid
!= caller_uid
|| // you can't chown to someone else
286 inode_uid
!= caller_uid
) { // you can't chown from someone else
290 if (mask
& MAY_CHGRP
) {
291 // you can only chgrp *to* one of your groups... if you own the file.
292 if (inode_uid
!= caller_uid
||
293 std::find(gids
.begin(), gids
.end(), new_gid
) ==
299 if (inode_uid
== caller_uid
) {
300 if ((!(mask
& MAY_READ
) || (inode_mode
& S_IRUSR
)) &&
301 (!(mask
& MAY_WRITE
) || (inode_mode
& S_IWUSR
)) &&
302 (!(mask
& MAY_EXECUTE
) || (inode_mode
& S_IXUSR
))) {
305 } else if (std::find(gids
.begin(), gids
.end(),
306 inode_gid
) != gids
.end()) {
307 if ((!(mask
& MAY_READ
) || (inode_mode
& S_IRGRP
)) &&
308 (!(mask
& MAY_WRITE
) || (inode_mode
& S_IWGRP
)) &&
309 (!(mask
& MAY_EXECUTE
) || (inode_mode
& S_IXGRP
))) {
313 if ((!(mask
& MAY_READ
) || (inode_mode
& S_IROTH
)) &&
314 (!(mask
& MAY_WRITE
) || (inode_mode
& S_IWOTH
)) &&
315 (!(mask
& MAY_EXECUTE
) || (inode_mode
& S_IXOTH
))) {
325 void MDSAuthCaps::set_allow_all()
328 grants
.push_back(MDSCapGrant(MDSCapSpec(MDSCapSpec::ALL
), MDSCapMatch(),
332 bool MDSAuthCaps::parse(string_view str
, ostream
*err
)
334 // Special case for legacy caps
335 if (str
== "allow") {
337 grants
.push_back(MDSCapGrant(MDSCapSpec(MDSCapSpec::RWPS
), MDSCapMatch(),
342 auto iter
= str
.begin();
343 auto end
= str
.end();
344 MDSCapParser
<decltype(iter
)> g
;
346 bool r
= qi::phrase_parse(iter
, end
, g
, ascii::space
, *this);
347 if (r
&& iter
== end
) {
348 for (auto& grant
: grants
) {
349 std::sort(grant
.match
.gids
.begin(), grant
.match
.gids
.end());
350 grant
.parse_network();
354 // Make sure no grants are kept after parsing failed!
358 if (string(iter
, end
).find("allow") != string::npos
) {
359 *err
<< "Permission flags in MDS caps must start with 'r' or " <<
360 "'rw' or be '*' or 'all'";
362 *err
<< "mds capability parse failed, stopped at '"
363 << string(iter
, end
) << "' of '" << str
<< "'";
371 bool MDSAuthCaps::allow_all() const
373 for (const auto& grant
: grants
) {
374 if (grant
.match
.is_match_all() && grant
.spec
.allow_all()) {
383 ostream
&operator<<(ostream
&out
, const MDSCapMatch
&match
)
385 if (!match
.fs_name
.empty()) {
386 out
<< " fsname=" << match
.fs_name
;
388 if (match
.path
.length()) {
389 out
<< " path=\"/" << match
.path
<< "\"";
391 if (match
.root_squash
) {
392 out
<< " root_squash";
394 if (match
.uid
!= MDSCapMatch::MDS_AUTH_UID_ANY
) {
395 out
<< " uid=" << match
.uid
;
396 if (!match
.gids
.empty()) {
399 for (const auto& gid
: match
.gids
) {
412 ostream
&operator<<(ostream
&out
, const MDSCapSpec
&spec
)
414 if (spec
.allow_all()) {
417 if (spec
.allow_read()) {
420 if (spec
.allow_write()) {
423 if (spec
.allow_full()) {
426 if (spec
.allow_set_vxattr()) {
429 if (spec
.allow_snapshot()) {
438 ostream
&operator<<(ostream
&out
, const MDSCapGrant
&grant
)
443 if (grant
.network
.size()) {
444 out
<< " network " << grant
.network
;
450 ostream
&operator<<(ostream
&out
, const MDSAuthCaps
&cap
)
452 out
<< "MDSAuthCaps[";
453 for (size_t i
= 0; i
< cap
.grants
.size(); ++i
) {
454 out
<< cap
.grants
[i
];
455 if (i
< cap
.grants
.size() - 1) {
464 ostream
&operator<<(ostream
&out
, const MDSCapAuth
&auth
)
466 out
<< "MDSCapAuth(" << auth
.match
<< "readable="
467 << auth
.readable
<< ", writeable=" << auth
.writeable
<< ")";