]>
git.proxmox.com Git - ceph.git/blob - ceph/src/mds/MDSAuthCaps.cc
b78ebd6615b792f8ec9be0d99893e7d10f03df3c
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/spirit/include/phoenix_operator.hpp>
21 #include <boost/spirit/include/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 namespace qi
= boost::spirit::qi
;
37 namespace ascii
= boost::spirit::ascii
;
38 namespace phoenix
= boost::phoenix
;
40 template <typename Iterator
>
41 struct MDSCapParser
: qi::grammar
<Iterator
, MDSAuthCaps()>
43 MDSCapParser() : MDSCapParser::base_type(mdscaps
)
59 spaces
= +(lit(' ') | lit('\n') | lit('\t'));
62 lexeme
[lit("\"") >> *(char_
- '"') >> '"'] |
63 lexeme
[lit("'") >> *(char_
- '\'') >> '\''];
64 unquoted_path
%= +char_("a-zA-Z0-9_./-");
65 network_str
%= +char_("/.:a-fA-F0-9][");
66 fs_name_str
%= +char_("a-zA-Z0-9_.-");
68 // match := [path=<path>] [uid=<uid> [gids=<gid>[,<gid>...]]
69 // TODO: allow fsname, and root_squash to be specified with uid, and gidlist
70 path
%= (spaces
>> lit("path") >> lit('=') >> (quoted_path
| unquoted_path
));
71 uid
%= (spaces
>> lit("uid") >> lit('=') >> uint_
);
72 uintlist
%= (uint_
% lit(','));
73 gidlist
%= -(spaces
>> lit("gids") >> lit('=') >> uintlist
);
74 fs_name
%= -(spaces
>> lit("fsname") >> lit('=') >> fs_name_str
);
75 root_squash
%= (spaces
>> lit("root_squash") >> attr(true));
77 (fs_name
>> path
>> root_squash
)[_val
= phoenix::construct
<MDSCapMatch
>(_2
, _1
, _3
)] |
78 (uid
>> gidlist
)[_val
= phoenix::construct
<MDSCapMatch
>(_1
, _2
)] |
79 (path
>> uid
>> gidlist
)[_val
= phoenix::construct
<MDSCapMatch
>(_1
, _2
, _3
)] |
80 (fs_name
>> path
)[_val
= phoenix::construct
<MDSCapMatch
>(_2
, _1
)] |
81 (fs_name
>> root_squash
)[_val
= phoenix::construct
<MDSCapMatch
>(std::string(), _1
, _2
)] |
82 (path
>> root_squash
)[_val
= phoenix::construct
<MDSCapMatch
>(_1
, std::string(), _2
)] |
83 (path
)[_val
= phoenix::construct
<MDSCapMatch
>(_1
)] |
84 (root_squash
)[_val
= phoenix::construct
<MDSCapMatch
>(std::string(), std::string(), _1
)] |
85 (fs_name
)[_val
= phoenix::construct
<MDSCapMatch
>(std::string(),
88 // capspec = * | r[w][f][p][s]
90 lit("*")[_val
= MDSCapSpec(MDSCapSpec::ALL
)]
92 lit("all")[_val
= MDSCapSpec(MDSCapSpec::ALL
)]
94 (lit("rwfps"))[_val
= MDSCapSpec(MDSCapSpec::RWFPS
)]
96 (lit("rwps"))[_val
= MDSCapSpec(MDSCapSpec::RWPS
)]
98 (lit("rwfp"))[_val
= MDSCapSpec(MDSCapSpec::RWFP
)]
100 (lit("rwfs"))[_val
= MDSCapSpec(MDSCapSpec::RWFS
)]
102 (lit("rwp"))[_val
= MDSCapSpec(MDSCapSpec::RWP
)]
104 (lit("rws"))[_val
= MDSCapSpec(MDSCapSpec::RWS
)]
106 (lit("rwf"))[_val
= MDSCapSpec(MDSCapSpec::RWF
)]
108 (lit("rw"))[_val
= MDSCapSpec(MDSCapSpec::RW
)]
110 (lit("r"))[_val
= MDSCapSpec(MDSCapSpec::READ
)]
113 grant
= lit("allow") >> (capspec
>> match
>>
114 -(spaces
>> lit("network") >> spaces
>> network_str
))
115 [_val
= phoenix::construct
<MDSCapGrant
>(_1
, _2
, _3
)];
116 grants
%= (grant
% (*lit(' ') >> (lit(';') | lit(',')) >> *lit(' ')));
117 mdscaps
= grants
[_val
= phoenix::construct
<MDSAuthCaps
>(_1
)];
119 qi::rule
<Iterator
> spaces
;
120 qi::rule
<Iterator
, string()> quoted_path
, unquoted_path
, network_str
;
121 qi::rule
<Iterator
, string()> fs_name_str
, fs_name
, path
;
122 qi::rule
<Iterator
, bool()> root_squash
;
123 qi::rule
<Iterator
, MDSCapSpec()> capspec
;
124 qi::rule
<Iterator
, uint32_t()> uid
;
125 qi::rule
<Iterator
, std::vector
<uint32_t>() > uintlist
;
126 qi::rule
<Iterator
, std::vector
<uint32_t>() > gidlist
;
127 qi::rule
<Iterator
, MDSCapMatch()> match
;
128 qi::rule
<Iterator
, MDSCapGrant()> grant
;
129 qi::rule
<Iterator
, std::vector
<MDSCapGrant
>()> grants
;
130 qi::rule
<Iterator
, MDSAuthCaps()> mdscaps
;
133 void MDSCapMatch::normalize_path()
135 // drop any leading /
136 while (path
.length() && path
[0] == '/') {
137 path
= path
.substr(1);
145 bool MDSCapMatch::match(std::string_view target_path
,
146 const int caller_uid
,
147 const int caller_gid
,
148 const vector
<uint64_t> *caller_gid_list
) const
150 if (uid
!= MDS_AUTH_UID_ANY
) {
151 if (uid
!= caller_uid
)
154 bool gid_matched
= false;
155 if (std::find(gids
.begin(), gids
.end(), caller_gid
) != gids
.end())
157 if (caller_gid_list
) {
158 for (auto i
= caller_gid_list
->begin(); i
!= caller_gid_list
->end(); ++i
) {
159 if (std::find(gids
.begin(), gids
.end(), *i
) != gids
.end()) {
170 if (!match_path(target_path
)) {
177 bool MDSCapMatch::match_path(std::string_view target_path
) const
180 if (target_path
.find(path
) != 0)
182 // if path doesn't already have a trailing /, make sure the target
183 // does so that path=/foo doesn't match target_path=/food
184 if (target_path
.length() > path
.length() &&
185 path
[path
.length()-1] != '/' &&
186 target_path
[path
.length()] != '/')
193 void MDSCapGrant::parse_network()
195 network_valid
= ::parse_network(network
.c_str(), &network_parsed
,
200 * Is the client *potentially* able to access this path? Actual
201 * permission will depend on uids/modes in the full is_capable.
203 bool MDSAuthCaps::path_capable(std::string_view inode_path
) const
205 for (const auto &i
: grants
) {
206 if (i
.match
.match_path(inode_path
)) {
215 * For a given filesystem path, query whether this capability carries`
216 * authorization to read or write.
218 * This is true if any of the 'grant' clauses in the capability match the
219 * requested path + op.
221 bool MDSAuthCaps::is_capable(std::string_view inode_path
,
222 uid_t inode_uid
, gid_t inode_gid
,
224 uid_t caller_uid
, gid_t caller_gid
,
225 const vector
<uint64_t> *caller_gid_list
,
227 uid_t new_uid
, gid_t new_gid
,
228 const entity_addr_t
& addr
) const
231 ldout(cct
, 10) << __func__
<< " inode(path /" << inode_path
232 << " owner " << inode_uid
<< ":" << inode_gid
233 << " mode 0" << std::oct
<< inode_mode
<< std::dec
234 << ") by caller " << caller_uid
<< ":" << caller_gid
235 // << "[" << caller_gid_list << "]";
237 << " new " << new_uid
<< ":" << new_gid
238 << " cap: " << *this << dendl
;
240 for (const auto& grant
: grants
) {
241 if (grant
.network
.size() &&
242 (!grant
.network_valid
||
243 !network_contains(grant
.network_parsed
,
244 grant
.network_prefix
,
249 if (grant
.match
.match(inode_path
, caller_uid
, caller_gid
, caller_gid_list
) &&
250 grant
.spec
.allows(mask
& (MAY_READ
|MAY_EXECUTE
), mask
& MAY_WRITE
)) {
251 if (grant
.match
.root_squash
&& ((caller_uid
== 0) || (caller_gid
== 0)) &&
252 (mask
& MAY_WRITE
)) {
255 // we have a match; narrow down GIDs to those specifically allowed here
256 vector
<uint64_t> gids
;
257 if (std::find(grant
.match
.gids
.begin(), grant
.match
.gids
.end(), caller_gid
) !=
258 grant
.match
.gids
.end()) {
259 gids
.push_back(caller_gid
);
261 if (caller_gid_list
) {
262 std::set_intersection(grant
.match
.gids
.begin(), grant
.match
.gids
.end(),
263 caller_gid_list
->begin(), caller_gid_list
->end(),
264 std::back_inserter(gids
));
265 std::sort(gids
.begin(), gids
.end());
269 // Spec is non-allowing if caller asked for set pool but spec forbids it
270 if (mask
& MAY_SET_VXATTR
) {
271 if (!grant
.spec
.allow_set_vxattr()) {
276 if (mask
& MAY_SNAPSHOT
) {
277 if (!grant
.spec
.allow_snapshot()) {
282 if (mask
& MAY_FULL
) {
283 if (!grant
.spec
.allow_full()) {
288 // check unix permissions?
289 if (grant
.match
.uid
== MDSCapMatch::MDS_AUTH_UID_ANY
) {
294 if (mask
& MAY_CHOWN
) {
295 if (new_uid
!= caller_uid
|| // you can't chown to someone else
296 inode_uid
!= caller_uid
) { // you can't chown from someone else
300 if (mask
& MAY_CHGRP
) {
301 // you can only chgrp *to* one of your groups... if you own the file.
302 if (inode_uid
!= caller_uid
||
303 std::find(gids
.begin(), gids
.end(), new_gid
) ==
309 if (inode_uid
== caller_uid
) {
310 if ((!(mask
& MAY_READ
) || (inode_mode
& S_IRUSR
)) &&
311 (!(mask
& MAY_WRITE
) || (inode_mode
& S_IWUSR
)) &&
312 (!(mask
& MAY_EXECUTE
) || (inode_mode
& S_IXUSR
))) {
315 } else if (std::find(gids
.begin(), gids
.end(),
316 inode_gid
) != gids
.end()) {
317 if ((!(mask
& MAY_READ
) || (inode_mode
& S_IRGRP
)) &&
318 (!(mask
& MAY_WRITE
) || (inode_mode
& S_IWGRP
)) &&
319 (!(mask
& MAY_EXECUTE
) || (inode_mode
& S_IXGRP
))) {
323 if ((!(mask
& MAY_READ
) || (inode_mode
& S_IROTH
)) &&
324 (!(mask
& MAY_WRITE
) || (inode_mode
& S_IWOTH
)) &&
325 (!(mask
& MAY_EXECUTE
) || (inode_mode
& S_IXOTH
))) {
335 void MDSAuthCaps::set_allow_all()
338 grants
.push_back(MDSCapGrant(MDSCapSpec(MDSCapSpec::ALL
), MDSCapMatch(),
342 bool MDSAuthCaps::parse(CephContext
*c
, std::string_view str
, ostream
*err
)
344 // Special case for legacy caps
345 if (str
== "allow") {
347 grants
.push_back(MDSCapGrant(MDSCapSpec(MDSCapSpec::RWPS
), MDSCapMatch(),
352 auto iter
= str
.begin();
353 auto end
= str
.end();
354 MDSCapParser
<decltype(iter
)> g
;
356 bool r
= qi::phrase_parse(iter
, end
, g
, ascii::space
, *this);
357 cct
= c
; // set after parser self-assignment
358 if (r
&& iter
== end
) {
359 for (auto& grant
: grants
) {
360 std::sort(grant
.match
.gids
.begin(), grant
.match
.gids
.end());
361 grant
.parse_network();
365 // Make sure no grants are kept after parsing failed!
369 *err
<< "mds capability parse failed, stopped at '"
370 << std::string(iter
, end
)
371 << "' of '" << str
<< "'";
377 bool MDSAuthCaps::allow_all() const
379 for (const auto& grant
: grants
) {
380 if (grant
.match
.is_match_all() && grant
.spec
.allow_all()) {
389 ostream
&operator<<(ostream
&out
, const MDSCapMatch
&match
)
391 if (!match
.fs_name
.empty()) {
392 out
<< " fsname=" << match
.fs_name
;
394 if (match
.path
.length()) {
395 out
<< " path=\"/" << match
.path
<< "\"";
397 if (match
.root_squash
) {
398 out
<< " root_squash";
400 if (match
.uid
!= MDSCapMatch::MDS_AUTH_UID_ANY
) {
401 out
<< " uid=" << match
.uid
;
402 if (!match
.gids
.empty()) {
405 for (const auto& gid
: match
.gids
) {
418 ostream
&operator<<(ostream
&out
, const MDSCapSpec
&spec
)
420 if (spec
.allow_all()) {
423 if (spec
.allow_read()) {
426 if (spec
.allow_write()) {
429 if (spec
.allow_full()) {
432 if (spec
.allow_set_vxattr()) {
435 if (spec
.allow_snapshot()) {
444 ostream
&operator<<(ostream
&out
, const MDSCapGrant
&grant
)
449 if (grant
.network
.size()) {
450 out
<< " network " << grant
.network
;
456 ostream
&operator<<(ostream
&out
, const MDSAuthCaps
&cap
)
458 out
<< "MDSAuthCaps[";
459 for (size_t i
= 0; i
< cap
.grants
.size(); ++i
) {
460 out
<< cap
.grants
[i
];
461 if (i
< cap
.grants
.size() - 1) {