]>
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>
20 #include <boost/spirit/include/qi.hpp>
21 #include <boost/spirit/include/phoenix_operator.hpp>
22 #include <boost/spirit/include/phoenix.hpp>
24 #include "common/debug.h"
25 #include "MDSAuthCaps.h"
26 #include "include/ipaddr.h"
28 #define dout_subsys ceph_subsys_mds
31 #define dout_prefix *_dout << "MDSAuthCap "
35 namespace qi
= boost::spirit::qi
;
36 namespace ascii
= boost::spirit::ascii
;
37 namespace phoenix
= boost::phoenix
;
39 template <typename Iterator
>
40 struct MDSCapParser
: qi::grammar
<Iterator
, MDSAuthCaps()>
42 MDSCapParser() : MDSCapParser::base_type(mdscaps
)
56 spaces
= +(lit(' ') | lit('\n') | lit('\t'));
59 lexeme
[lit("\"") >> *(char_
- '"') >> '"'] |
60 lexeme
[lit("'") >> *(char_
- '\'') >> '\''];
61 unquoted_path
%= +char_("a-zA-Z0-9_./-");
62 network_str
%= +char_("/.:a-fA-F0-9][");
64 // match := [path=<path>] [uid=<uid> [gids=<gid>[,<gid>...]]
65 path
%= (spaces
>> lit("path") >> lit('=') >> (quoted_path
| unquoted_path
));
66 uid
%= (spaces
>> lit("uid") >> lit('=') >> uint_
);
67 uintlist
%= (uint_
% lit(','));
68 gidlist
%= -(spaces
>> lit("gids") >> lit('=') >> uintlist
);
70 (uid
>> gidlist
)[_val
= phoenix::construct
<MDSCapMatch
>(_1
, _2
)] |
71 (path
>> uid
>> gidlist
)[_val
= phoenix::construct
<MDSCapMatch
>(_1
, _2
, _3
)] |
72 (path
)[_val
= phoenix::construct
<MDSCapMatch
>(_1
)]);
74 // capspec = * | r[w][p][s]
76 lit("*")[_val
= MDSCapSpec(MDSCapSpec::ALL
)]
78 lit("all")[_val
= MDSCapSpec(MDSCapSpec::ALL
)]
80 (lit("rwps"))[_val
= MDSCapSpec(MDSCapSpec::RWPS
)]
82 (lit("rwp"))[_val
= MDSCapSpec(MDSCapSpec::RWP
)]
84 (lit("rws"))[_val
= MDSCapSpec(MDSCapSpec::RWS
)]
86 (lit("rw"))[_val
= MDSCapSpec(MDSCapSpec::RW
)]
88 (lit("r"))[_val
= MDSCapSpec(MDSCapSpec::READ
)]
91 grant
= lit("allow") >> (capspec
>> match
>>
92 -(spaces
>> lit("network") >> spaces
>> network_str
))
93 [_val
= phoenix::construct
<MDSCapGrant
>(_1
, _2
, _3
)];
94 grants
%= (grant
% (*lit(' ') >> (lit(';') | lit(',')) >> *lit(' ')));
95 mdscaps
= grants
[_val
= phoenix::construct
<MDSAuthCaps
>(_1
)];
97 qi::rule
<Iterator
> spaces
;
98 qi::rule
<Iterator
, string()> quoted_path
, unquoted_path
, network_str
;
99 qi::rule
<Iterator
, MDSCapSpec()> capspec
;
100 qi::rule
<Iterator
, string()> path
;
101 qi::rule
<Iterator
, uint32_t()> uid
;
102 qi::rule
<Iterator
, std::vector
<uint32_t>() > uintlist
;
103 qi::rule
<Iterator
, std::vector
<uint32_t>() > gidlist
;
104 qi::rule
<Iterator
, MDSCapMatch()> match
;
105 qi::rule
<Iterator
, MDSCapGrant()> grant
;
106 qi::rule
<Iterator
, std::vector
<MDSCapGrant
>()> grants
;
107 qi::rule
<Iterator
, MDSAuthCaps()> mdscaps
;
110 void MDSCapMatch::normalize_path()
112 // drop any leading /
113 while (path
.length() && path
[0] == '/') {
114 path
= path
.substr(1);
122 bool MDSCapMatch::match(std::string_view target_path
,
123 const int caller_uid
,
124 const int caller_gid
,
125 const vector
<uint64_t> *caller_gid_list
) const
127 if (uid
!= MDS_AUTH_UID_ANY
) {
128 if (uid
!= caller_uid
)
131 bool gid_matched
= false;
132 if (std::find(gids
.begin(), gids
.end(), caller_gid
) != gids
.end())
134 if (caller_gid_list
) {
135 for (auto i
= caller_gid_list
->begin(); i
!= caller_gid_list
->end(); ++i
) {
136 if (std::find(gids
.begin(), gids
.end(), *i
) != gids
.end()) {
147 if (!match_path(target_path
)) {
154 bool MDSCapMatch::match_path(std::string_view target_path
) const
157 if (target_path
.find(path
) != 0)
159 // if path doesn't already have a trailing /, make sure the target
160 // does so that path=/foo doesn't match target_path=/food
161 if (target_path
.length() > path
.length() &&
162 path
[path
.length()-1] != '/' &&
163 target_path
[path
.length()] != '/')
170 void MDSCapGrant::parse_network()
172 network_valid
= ::parse_network(network
.c_str(), &network_parsed
,
177 * Is the client *potentially* able to access this path? Actual
178 * permission will depend on uids/modes in the full is_capable.
180 bool MDSAuthCaps::path_capable(std::string_view inode_path
) const
182 for (const auto &i
: grants
) {
183 if (i
.match
.match_path(inode_path
)) {
192 * For a given filesystem path, query whether this capability carries`
193 * authorization to read or write.
195 * This is true if any of the 'grant' clauses in the capability match the
196 * requested path + op.
198 bool MDSAuthCaps::is_capable(std::string_view inode_path
,
199 uid_t inode_uid
, gid_t inode_gid
,
201 uid_t caller_uid
, gid_t caller_gid
,
202 const vector
<uint64_t> *caller_gid_list
,
204 uid_t new_uid
, gid_t new_gid
,
205 const entity_addr_t
& addr
) const
208 ldout(cct
, 10) << __func__
<< " inode(path /" << inode_path
209 << " owner " << inode_uid
<< ":" << inode_gid
210 << " mode 0" << std::oct
<< inode_mode
<< std::dec
211 << ") by caller " << caller_uid
<< ":" << caller_gid
212 // << "[" << caller_gid_list << "]";
214 << " new " << new_uid
<< ":" << new_gid
215 << " cap: " << *this << dendl
;
217 for (std::vector
<MDSCapGrant
>::const_iterator i
= grants
.begin();
220 if (i
->network
.size() &&
221 (!i
->network_valid
||
222 !network_contains(i
->network_parsed
,
228 if (i
->match
.match(inode_path
, caller_uid
, caller_gid
, caller_gid_list
) &&
229 i
->spec
.allows(mask
& (MAY_READ
|MAY_EXECUTE
), mask
& MAY_WRITE
)) {
230 // we have a match; narrow down GIDs to those specifically allowed here
231 vector
<uint64_t> gids
;
232 if (std::find(i
->match
.gids
.begin(), i
->match
.gids
.end(), caller_gid
) !=
233 i
->match
.gids
.end()) {
234 gids
.push_back(caller_gid
);
236 if (caller_gid_list
) {
237 std::set_intersection(i
->match
.gids
.begin(), i
->match
.gids
.end(),
238 caller_gid_list
->begin(), caller_gid_list
->end(),
239 std::back_inserter(gids
));
240 std::sort(gids
.begin(), gids
.end());
244 // Spec is non-allowing if caller asked for set pool but spec forbids it
245 if (mask
& MAY_SET_VXATTR
) {
246 if (!i
->spec
.allow_set_vxattr()) {
251 if (mask
& MAY_SNAPSHOT
) {
252 if (!i
->spec
.allow_snapshot()) {
257 // check unix permissions?
258 if (i
->match
.uid
== MDSCapMatch::MDS_AUTH_UID_ANY
) {
263 if (mask
& MAY_CHOWN
) {
264 if (new_uid
!= caller_uid
|| // you can't chown to someone else
265 inode_uid
!= caller_uid
) { // you can't chown from someone else
269 if (mask
& MAY_CHGRP
) {
270 // you can only chgrp *to* one of your groups... if you own the file.
271 if (inode_uid
!= caller_uid
||
272 std::find(gids
.begin(), gids
.end(), new_gid
) ==
278 if (inode_uid
== caller_uid
) {
279 if ((!(mask
& MAY_READ
) || (inode_mode
& S_IRUSR
)) &&
280 (!(mask
& MAY_WRITE
) || (inode_mode
& S_IWUSR
)) &&
281 (!(mask
& MAY_EXECUTE
) || (inode_mode
& S_IXUSR
))) {
284 } else if (std::find(gids
.begin(), gids
.end(),
285 inode_gid
) != gids
.end()) {
286 if ((!(mask
& MAY_READ
) || (inode_mode
& S_IRGRP
)) &&
287 (!(mask
& MAY_WRITE
) || (inode_mode
& S_IWGRP
)) &&
288 (!(mask
& MAY_EXECUTE
) || (inode_mode
& S_IXGRP
))) {
292 if ((!(mask
& MAY_READ
) || (inode_mode
& S_IROTH
)) &&
293 (!(mask
& MAY_WRITE
) || (inode_mode
& S_IWOTH
)) &&
294 (!(mask
& MAY_EXECUTE
) || (inode_mode
& S_IXOTH
))) {
304 void MDSAuthCaps::set_allow_all()
307 grants
.push_back(MDSCapGrant(MDSCapSpec(MDSCapSpec::ALL
), MDSCapMatch(),
311 bool MDSAuthCaps::parse(CephContext
*c
, std::string_view str
, ostream
*err
)
313 // Special case for legacy caps
314 if (str
== "allow") {
316 grants
.push_back(MDSCapGrant(MDSCapSpec(MDSCapSpec::RWPS
), MDSCapMatch(),
321 auto iter
= str
.begin();
322 auto end
= str
.end();
323 MDSCapParser
<decltype(iter
)> g
;
325 bool r
= qi::phrase_parse(iter
, end
, g
, ascii::space
, *this);
326 cct
= c
; // set after parser self-assignment
327 if (r
&& iter
== end
) {
328 for (auto& grant
: grants
) {
329 std::sort(grant
.match
.gids
.begin(), grant
.match
.gids
.end());
330 grant
.parse_network();
334 // Make sure no grants are kept after parsing failed!
338 *err
<< "mds capability parse failed, stopped at '"
339 << std::string(iter
, end
)
340 << "' of '" << str
<< "'";
346 bool MDSAuthCaps::allow_all() const
348 for (std::vector
<MDSCapGrant
>::const_iterator i
= grants
.begin(); i
!= grants
.end(); ++i
) {
349 if (i
->match
.is_match_all() && i
->spec
.allow_all()) {
358 ostream
&operator<<(ostream
&out
, const MDSCapMatch
&match
)
360 if (match
.path
.length()) {
361 out
<< "path=\"/" << match
.path
<< "\"";
362 if (match
.uid
!= MDSCapMatch::MDS_AUTH_UID_ANY
) {
366 if (match
.uid
!= MDSCapMatch::MDS_AUTH_UID_ANY
) {
367 out
<< "uid=" << match
.uid
;
368 if (!match
.gids
.empty()) {
370 for (std::vector
<gid_t
>::const_iterator p
= match
.gids
.begin();
371 p
!= match
.gids
.end();
373 if (p
!= match
.gids
.begin())
384 ostream
&operator<<(ostream
&out
, const MDSCapSpec
&spec
)
386 if (spec
.allow_all()) {
389 if (spec
.allow_read()) {
392 if (spec
.allow_write()) {
395 if (spec
.allow_set_vxattr()) {
398 if (spec
.allow_snapshot()) {
407 ostream
&operator<<(ostream
&out
, const MDSCapGrant
&grant
)
411 if (!grant
.match
.is_match_all()) {
412 out
<< " " << grant
.match
;
414 if (grant
.network
.size()) {
415 out
<< " network " << grant
.network
;
421 ostream
&operator<<(ostream
&out
, const MDSAuthCaps
&cap
)
423 out
<< "MDSAuthCaps[";
424 for (size_t i
= 0; i
< cap
.grants
.size(); ++i
) {
425 out
<< cap
.grants
[i
];
426 if (i
< cap
.grants
.size() - 1) {