1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
11 #include <string_view>
13 #include <boost/algorithm/string/predicate.hpp>
14 #include <boost/container/flat_map.hpp>
15 #include <boost/container/flat_set.hpp>
16 #include <boost/optional.hpp>
17 #include <boost/thread/shared_mutex.hpp>
18 #include <boost/variant.hpp>
20 #include <fmt/format.h>
22 #include "common/ceph_time.h"
23 #include "common/iso_8601.h"
25 #include "rapidjson/error/error.h"
26 #include "rapidjson/error/en.h"
29 #include "rgw_basic_types.h"
30 #include "rgw_iam_policy_keywords.h"
31 #include "rgw_string.h"
43 static constexpr std::uint64_t s3GetObject
= 0;
44 static constexpr std::uint64_t s3GetObjectVersion
= 1;
45 static constexpr std::uint64_t s3PutObject
= 2;
46 static constexpr std::uint64_t s3GetObjectAcl
= 3;
47 static constexpr std::uint64_t s3GetObjectVersionAcl
= 4;
48 static constexpr std::uint64_t s3PutObjectAcl
= 5;
49 static constexpr std::uint64_t s3PutObjectVersionAcl
= 6;
50 static constexpr std::uint64_t s3DeleteObject
= 7;
51 static constexpr std::uint64_t s3DeleteObjectVersion
= 8;
52 static constexpr std::uint64_t s3ListMultipartUploadParts
= 9;
53 static constexpr std::uint64_t s3AbortMultipartUpload
= 10;
54 static constexpr std::uint64_t s3GetObjectTorrent
= 11;
55 static constexpr std::uint64_t s3GetObjectVersionTorrent
= 12;
56 static constexpr std::uint64_t s3RestoreObject
= 13;
57 static constexpr std::uint64_t s3CreateBucket
= 14;
58 static constexpr std::uint64_t s3DeleteBucket
= 15;
59 static constexpr std::uint64_t s3ListBucket
= 16;
60 static constexpr std::uint64_t s3ListBucketVersions
= 17;
61 static constexpr std::uint64_t s3ListAllMyBuckets
= 18;
62 static constexpr std::uint64_t s3ListBucketMultipartUploads
= 19;
63 static constexpr std::uint64_t s3GetAccelerateConfiguration
= 20;
64 static constexpr std::uint64_t s3PutAccelerateConfiguration
= 21;
65 static constexpr std::uint64_t s3GetBucketAcl
= 22;
66 static constexpr std::uint64_t s3PutBucketAcl
= 23;
67 static constexpr std::uint64_t s3GetBucketCORS
= 24;
68 static constexpr std::uint64_t s3PutBucketCORS
= 25;
69 static constexpr std::uint64_t s3GetBucketVersioning
= 26;
70 static constexpr std::uint64_t s3PutBucketVersioning
= 27;
71 static constexpr std::uint64_t s3GetBucketRequestPayment
= 28;
72 static constexpr std::uint64_t s3PutBucketRequestPayment
= 29;
73 static constexpr std::uint64_t s3GetBucketLocation
= 30;
74 static constexpr std::uint64_t s3GetBucketPolicy
= 31;
75 static constexpr std::uint64_t s3DeleteBucketPolicy
= 32;
76 static constexpr std::uint64_t s3PutBucketPolicy
= 33;
77 static constexpr std::uint64_t s3GetBucketNotification
= 34;
78 static constexpr std::uint64_t s3PutBucketNotification
= 35;
79 static constexpr std::uint64_t s3GetBucketLogging
= 36;
80 static constexpr std::uint64_t s3PutBucketLogging
= 37;
81 static constexpr std::uint64_t s3GetBucketTagging
= 38;
82 static constexpr std::uint64_t s3PutBucketTagging
= 39;
83 static constexpr std::uint64_t s3GetBucketWebsite
= 40;
84 static constexpr std::uint64_t s3PutBucketWebsite
= 41;
85 static constexpr std::uint64_t s3DeleteBucketWebsite
= 42;
86 static constexpr std::uint64_t s3GetLifecycleConfiguration
= 43;
87 static constexpr std::uint64_t s3PutLifecycleConfiguration
= 44;
88 static constexpr std::uint64_t s3PutReplicationConfiguration
= 45;
89 static constexpr std::uint64_t s3GetReplicationConfiguration
= 46;
90 static constexpr std::uint64_t s3DeleteReplicationConfiguration
= 47;
91 static constexpr std::uint64_t s3GetObjectTagging
= 48;
92 static constexpr std::uint64_t s3PutObjectTagging
= 49;
93 static constexpr std::uint64_t s3DeleteObjectTagging
= 50;
94 static constexpr std::uint64_t s3GetObjectVersionTagging
= 51;
95 static constexpr std::uint64_t s3PutObjectVersionTagging
= 52;
96 static constexpr std::uint64_t s3DeleteObjectVersionTagging
= 53;
97 static constexpr std::uint64_t s3PutBucketObjectLockConfiguration
= 54;
98 static constexpr std::uint64_t s3GetBucketObjectLockConfiguration
= 55;
99 static constexpr std::uint64_t s3PutObjectRetention
= 56;
100 static constexpr std::uint64_t s3GetObjectRetention
= 57;
101 static constexpr std::uint64_t s3PutObjectLegalHold
= 58;
102 static constexpr std::uint64_t s3GetObjectLegalHold
= 59;
103 static constexpr std::uint64_t s3BypassGovernanceRetention
= 60;
104 static constexpr std::uint64_t s3GetBucketPolicyStatus
= 61;
105 static constexpr std::uint64_t s3PutPublicAccessBlock
= 62;
106 static constexpr std::uint64_t s3GetPublicAccessBlock
= 63;
107 static constexpr std::uint64_t s3DeletePublicAccessBlock
= 64;
108 static constexpr std::uint64_t s3GetBucketPublicAccessBlock
= 65;
109 static constexpr std::uint64_t s3PutBucketPublicAccessBlock
= 66;
110 static constexpr std::uint64_t s3DeleteBucketPublicAccessBlock
= 67;
111 static constexpr std::uint64_t s3GetBucketEncryption
= 68;
112 static constexpr std::uint64_t s3PutBucketEncryption
= 69;
113 static constexpr std::uint64_t s3All
= 70;
115 static constexpr std::uint64_t iamPutUserPolicy
= s3All
+ 1;
116 static constexpr std::uint64_t iamGetUserPolicy
= s3All
+ 2;
117 static constexpr std::uint64_t iamDeleteUserPolicy
= s3All
+ 3;
118 static constexpr std::uint64_t iamListUserPolicies
= s3All
+ 4;
119 static constexpr std::uint64_t iamCreateRole
= s3All
+ 5;
120 static constexpr std::uint64_t iamDeleteRole
= s3All
+ 6;
121 static constexpr std::uint64_t iamModifyRoleTrustPolicy
= s3All
+ 7;
122 static constexpr std::uint64_t iamGetRole
= s3All
+ 8;
123 static constexpr std::uint64_t iamListRoles
= s3All
+ 9;
124 static constexpr std::uint64_t iamPutRolePolicy
= s3All
+ 10;
125 static constexpr std::uint64_t iamGetRolePolicy
= s3All
+ 11;
126 static constexpr std::uint64_t iamListRolePolicies
= s3All
+ 12;
127 static constexpr std::uint64_t iamDeleteRolePolicy
= s3All
+ 13;
128 static constexpr std::uint64_t iamCreateOIDCProvider
= s3All
+ 14;
129 static constexpr std::uint64_t iamDeleteOIDCProvider
= s3All
+ 15;
130 static constexpr std::uint64_t iamGetOIDCProvider
= s3All
+ 16;
131 static constexpr std::uint64_t iamListOIDCProviders
= s3All
+ 17;
132 static constexpr std::uint64_t iamTagRole
= s3All
+ 18;
133 static constexpr std::uint64_t iamListRoleTags
= s3All
+ 19;
134 static constexpr std::uint64_t iamUntagRole
= s3All
+ 20;
135 static constexpr std::uint64_t iamUpdateRole
= s3All
+ 21;
136 static constexpr std::uint64_t iamAll
= s3All
+ 22;
138 static constexpr std::uint64_t stsAssumeRole
= iamAll
+ 1;
139 static constexpr std::uint64_t stsAssumeRoleWithWebIdentity
= iamAll
+ 2;
140 static constexpr std::uint64_t stsGetSessionToken
= iamAll
+ 3;
141 static constexpr std::uint64_t stsTagSession
= iamAll
+ 4;
142 static constexpr std::uint64_t stsAll
= iamAll
+ 5;
144 static constexpr std::uint64_t s3Count
= s3All
;
145 static constexpr std::uint64_t allCount
= stsAll
+ 1;
147 using Action_t
= std::bitset
<allCount
>;
148 using NotAction_t
= Action_t
;
151 constexpr std::bitset
<N
> make_bitmask(size_t s
) {
152 // unfortunately none of the shift/logic operators of std::bitset have a constexpr variation
153 return s
< 64 ? std::bitset
<N
> ((1ULL << s
) - 1) :
154 std::bitset
<N
>((1ULL << 63) - 1) | make_bitmask
<N
> (s
- 63) << 63;
158 constexpr std::bitset
<N
> set_cont_bits(size_t start
, size_t end
)
160 return (make_bitmask
<N
>(end
- start
)) << start
;
163 static const Action_t
None(0);
164 static const Action_t s3AllValue
= set_cont_bits
<allCount
>(0,s3All
);
165 static const Action_t iamAllValue
= set_cont_bits
<allCount
>(s3All
+1,iamAll
);
166 static const Action_t stsAllValue
= set_cont_bits
<allCount
>(iamAll
+1,stsAll
);
167 static const Action_t allValue
= set_cont_bits
<allCount
>(0,allCount
);
170 // Please update the table in doc/radosgw/s3/authentication.rst if you
171 // modify this function.
172 inline int op_to_perm(std::uint64_t op
) {
175 case s3GetObjectTorrent
:
176 case s3GetObjectVersion
:
177 case s3GetObjectVersionTorrent
:
178 case s3GetObjectTagging
:
179 case s3GetObjectVersionTagging
:
180 case s3GetObjectRetention
:
181 case s3GetObjectLegalHold
:
182 case s3ListAllMyBuckets
:
184 case s3ListBucketMultipartUploads
:
185 case s3ListBucketVersions
:
186 case s3ListMultipartUploadParts
:
187 return RGW_PERM_READ
;
189 case s3AbortMultipartUpload
:
193 case s3DeleteObjectVersion
:
195 case s3PutObjectTagging
:
196 case s3PutObjectVersionTagging
:
197 case s3DeleteObjectTagging
:
198 case s3DeleteObjectVersionTagging
:
199 case s3RestoreObject
:
200 case s3PutObjectRetention
:
201 case s3PutObjectLegalHold
:
202 case s3BypassGovernanceRetention
:
203 return RGW_PERM_WRITE
;
205 case s3GetAccelerateConfiguration
:
207 case s3GetBucketCORS
:
208 case s3GetBucketEncryption
:
209 case s3GetBucketLocation
:
210 case s3GetBucketLogging
:
211 case s3GetBucketNotification
:
212 case s3GetBucketPolicy
:
213 case s3GetBucketPolicyStatus
:
214 case s3GetBucketRequestPayment
:
215 case s3GetBucketTagging
:
216 case s3GetBucketVersioning
:
217 case s3GetBucketWebsite
:
218 case s3GetLifecycleConfiguration
:
220 case s3GetObjectVersionAcl
:
221 case s3GetReplicationConfiguration
:
222 case s3GetBucketObjectLockConfiguration
:
223 case s3GetBucketPublicAccessBlock
:
224 return RGW_PERM_READ_ACP
;
226 case s3DeleteBucketPolicy
:
227 case s3DeleteBucketWebsite
:
228 case s3DeleteReplicationConfiguration
:
229 case s3PutAccelerateConfiguration
:
231 case s3PutBucketCORS
:
232 case s3PutBucketEncryption
:
233 case s3PutBucketLogging
:
234 case s3PutBucketNotification
:
235 case s3PutBucketPolicy
:
236 case s3PutBucketRequestPayment
:
237 case s3PutBucketTagging
:
238 case s3PutBucketVersioning
:
239 case s3PutBucketWebsite
:
240 case s3PutLifecycleConfiguration
:
242 case s3PutObjectVersionAcl
:
243 case s3PutReplicationConfiguration
:
244 case s3PutBucketObjectLockConfiguration
:
245 case s3PutBucketPublicAccessBlock
:
246 return RGW_PERM_WRITE_ACP
;
249 return RGW_PERM_FULL_CONTROL
;
251 return RGW_PERM_INVALID
;
255 enum class PolicyPrincipal
{
261 using Environment
= std::unordered_multimap
<std::string
, std::string
>;
263 using Address
= std::bitset
<128>;
267 // Since we're mapping IPv6 to IPv4 addresses, we may want to
268 // consider making the prefix always be in terms of a v6 address
269 // and just use the v6 bit to rewrite it as a v4 prefix for
274 std::ostream
& operator <<(std::ostream
& m
, const MaskedIP
& ip
);
276 inline bool operator ==(const MaskedIP
& l
, const MaskedIP
& r
) {
277 auto shift
= std::max((l
.v6
? 128 : 32) - ((int) l
.prefix
),
278 (r
.v6
? 128 : 32) - ((int) r
.prefix
));
279 ceph_assert(shift
>= 0);
280 return (l
.addr
>> shift
) == (r
.addr
>> shift
);
285 // Originally I was going to use a perfect hash table, but Marcus
286 // says keys are to be added at run-time not compile time.
288 // In future development, use symbol internment.
290 bool ifexists
= false;
291 bool isruntime
= false; //Is evaluated during run-time
292 // Much to my annoyance there is no actual way to do this in a
293 // typed way that is compatible with AWS. I know this because I've
294 // seen examples where the same value is used as a string in one
295 // context and a date in another.
296 std::vector
<std::string
> vals
;
298 Condition() = default;
299 Condition(TokenID op
, const char* s
, std::size_t len
, bool ifexists
)
300 : op(op
), key(s
, len
), ifexists(ifexists
) {}
302 bool eval(const Environment
& e
) const;
304 static boost::optional
<double> as_number(const std::string
& s
) {
308 double d
= std::stod(s
, &p
);
309 if (p
< s
.length()) {
314 } catch (const std::logic_error
& e
) {
319 static boost::optional
<ceph::real_time
> as_date(const std::string
& s
) {
323 double d
= std::stod(s
, &p
);
324 if (p
== s
.length()) {
325 return ceph::real_time(
326 std::chrono::seconds(static_cast<uint64_t>(d
)) +
327 std::chrono::nanoseconds(
328 static_cast<uint64_t>((d
- static_cast<uint64_t>(d
))
332 return from_iso_8601(std::string_view(s
), false);
333 } catch (const std::logic_error
& e
) {
338 static boost::optional
<bool> as_bool(const std::string
& s
) {
341 if (s
.empty() || boost::iequals(s
, "false")) {
346 double d
= std::stod(s
, &p
);
347 if (p
== s
.length()) {
348 return !((d
== +0.0) || (d
== -0.0) || std::isnan(d
));
350 } catch (const std::logic_error
& e
) {
357 static boost::optional
<ceph::bufferlist
> as_binary(const std::string
& s
) {
359 ceph::bufferlist base64
;
360 // I could populate a bufferlist
361 base64
.push_back(buffer::create_static(
363 const_cast<char*>(s
.data()))); // Yuck
364 // From a base64 encoded std::string.
365 ceph::bufferlist bin
;
368 bin
.decode_base64(base64
);
369 } catch (const ceph::buffer::malformed_input
& e
) {
375 static boost::optional
<MaskedIP
> as_network(const std::string
& s
);
379 bool operator ()(const std::string
& s1
,
380 const std::string
& s2
) const {
381 return boost::iequals(s1
, s2
);
386 bool operator ()(const std::string
& input
,
387 const std::string
& pattern
) const {
388 return match_wildcards(pattern
, input
, 0);
392 struct ci_starts_with
{
393 bool operator()(const std::string
& s1
,
394 const std::string
& s2
) const {
395 return boost::istarts_with(s1
, s2
);
399 using unordered_multimap_it_pair
= std::pair
<std::unordered_multimap
<std::string
,std::string
>::const_iterator
, std::unordered_multimap
<std::string
,std::string
>::const_iterator
>;
402 static bool andible(F
&& f
, const unordered_multimap_it_pair
& it
,
403 const std::vector
<std::string
>& v
) {
404 for (auto itr
= it
.first
; itr
!= it
.second
; itr
++) {
405 bool matched
= false;
406 for (const auto& d
: v
) {
407 if (std::forward
<F
>(f
)(itr
->second
, d
)) {
418 static bool orrible(F
&& f
, const unordered_multimap_it_pair
& it
,
419 const std::vector
<std::string
>& v
) {
420 for (auto itr
= it
.first
; itr
!= it
.second
; itr
++) {
421 for (const auto& d
: v
) {
422 if (std::forward
<F
>(f
)(itr
->second
, d
)) {
430 template<typename F
, typename X
>
431 static bool shortible(F
&& f
, X
& x
, const std::string
& c
,
432 const std::vector
<std::string
>& v
) {
433 auto xc
= std::forward
<X
>(x
)(c
);
438 for (const auto& d
: v
) {
439 auto xd
= std::forward
<X
>(x
)(d
);
444 if (std::forward
<F
>(f
)(*xc
, *xd
)) {
451 template <typename F
>
452 bool has_key_p(const std::string
& _key
, F p
) const {
456 template <typename F
>
457 bool has_val_p(const std::string
& _val
, F p
) const {
458 for (auto val
: vals
) {
466 std::ostream
& operator <<(std::ostream
& m
, const Condition
& c
);
469 boost::optional
<std::string
> sid
= boost::none
;
471 boost::container::flat_set
<rgw::auth::Principal
> princ
;
472 boost::container::flat_set
<rgw::auth::Principal
> noprinc
;
474 // Every statement MUST provide an effect. I just initialize it to
475 // deny as defensive programming.
476 Effect effect
= Effect::Deny
;
479 NotAction_t notaction
= 0;
481 boost::container::flat_set
<ARN
> resource
;
482 boost::container::flat_set
<ARN
> notresource
;
484 std::vector
<Condition
> conditions
;
486 Effect
eval(const Environment
& e
,
487 boost::optional
<const rgw::auth::Identity
&> ida
,
488 std::uint64_t action
, boost::optional
<const ARN
&> resource
, boost::optional
<PolicyPrincipal
&> princ_type
=boost::none
) const;
490 Effect
eval_principal(const Environment
& e
,
491 boost::optional
<const rgw::auth::Identity
&> ida
, boost::optional
<PolicyPrincipal
&> princ_type
=boost::none
) const;
493 Effect
eval_conditions(const Environment
& e
) const;
496 std::ostream
& operator <<(std::ostream
& m
, const Statement
& s
);
498 struct PolicyParseException
: public std::exception
{
499 rapidjson::ParseResult pr
;
502 explicit PolicyParseException(const rapidjson::ParseResult pr
,
503 const std::string
& annotation
)
505 msg(fmt::format("At character offset {}, {}",
507 (pr
.Code() == rapidjson::kParseErrorTermination
?
509 rapidjson::GetParseError_En(pr
.Code())))) {}
511 const char* what() const noexcept override
{
518 Version version
= Version::v2008_10_17
;
519 boost::optional
<std::string
> id
= boost::none
;
521 std::vector
<Statement
> statements
;
523 // reject_invalid_principals should be set to
524 // `cct->_conf.get_val<bool>("rgw_policy_reject_invalid_principals")`
525 // when executing operations that *set* a bucket policy, but should
526 // be false when reading a stored bucket policy so as not to break
527 // backwards configuration.
528 Policy(CephContext
* cct
, const std::string
& tenant
,
529 const bufferlist
& text
,
530 bool reject_invalid_principals
);
532 Effect
eval(const Environment
& e
,
533 boost::optional
<const rgw::auth::Identity
&> ida
,
534 std::uint64_t action
, boost::optional
<const ARN
&> resource
, boost::optional
<PolicyPrincipal
&> princ_type
=boost::none
) const;
536 Effect
eval_principal(const Environment
& e
,
537 boost::optional
<const rgw::auth::Identity
&> ida
, boost::optional
<PolicyPrincipal
&> princ_type
=boost::none
) const;
539 Effect
eval_conditions(const Environment
& e
) const;
541 template <typename F
>
542 bool has_conditional(const std::string
& conditional
, F p
) const {
543 for (const auto&s
: statements
){
544 if (std::any_of(s
.conditions
.begin(), s
.conditions
.end(),
545 [&](const Condition
& c
) { return c
.has_key_p(conditional
, p
);}))
551 template <typename F
>
552 bool has_conditional_value(const std::string
& conditional
, F p
) const {
553 for (const auto&s
: statements
){
554 if (std::any_of(s
.conditions
.begin(), s
.conditions
.end(),
555 [&](const Condition
& c
) { return c
.has_val_p(conditional
, p
);}))
561 bool has_conditional(const std::string
& c
) const {
562 return has_conditional(c
, Condition::ci_equal_to());
565 bool has_partial_conditional(const std::string
& c
) const {
566 return has_conditional(c
, Condition::ci_starts_with());
569 // Example: ${s3:ResourceTag}
570 bool has_partial_conditional_value(const std::string
& c
) const {
571 return has_conditional_value(c
, Condition::ci_starts_with());
575 std::ostream
& operator <<(std::ostream
& m
, const Policy
& p
);
576 bool is_public(const Policy
& p
);