]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_iam_policy.cc
Import ceph 15.2.8
[ceph.git] / ceph / src / rgw / rgw_iam_policy.cc
CommitLineData
31f18b77 1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
9f95a23c 2// vim: ts=8 sw=2 smarttab ft=cpp
31f18b77
FG
3
4
5#include <cstring>
11fdf7f2
TL
6#include <iostream>
7#include <regex>
31f18b77
FG
8#include <sstream>
9#include <stack>
10#include <utility>
11
11fdf7f2
TL
12#include <experimental/iterator>
13
31f18b77
FG
14#include "rapidjson/reader.h"
15
16#include "rgw_auth.h"
224ce89b 17#include <arpa/inet.h>
31f18b77
FG
18#include "rgw_iam_policy.h"
19
20namespace {
21constexpr int dout_subsys = ceph_subsys_rgw;
22}
23
24using std::bitset;
25using std::find;
26using std::int64_t;
27using std::move;
28using std::pair;
29using std::size_t;
30using std::string;
31using std::stringstream;
32using std::ostream;
33using std::uint16_t;
34using std::uint64_t;
35using std::unordered_map;
36
37using boost::container::flat_set;
11fdf7f2
TL
38using std::regex;
39using std::regex_constants::ECMAScript;
40using std::regex_constants::optimize;
41using std::regex_match;
42using std::smatch;
31f18b77
FG
43
44using rapidjson::BaseReaderHandler;
45using rapidjson::UTF8;
46using rapidjson::SizeType;
47using rapidjson::Reader;
48using rapidjson::kParseCommentsFlag;
49using rapidjson::kParseNumbersAsStringsFlag;
50using rapidjson::StringStream;
51using rapidjson::ParseResult;
52
53using rgw::auth::Principal;
54
55namespace rgw {
56namespace IAM {
57#include "rgw_iam_policy_keywords.frag.cc"
58
59struct actpair {
60 const char* name;
61 const uint64_t bit;
62};
63
31f18b77 64
31f18b77
FG
65
66static const actpair actpairs[] =
67{{ "s3:AbortMultipartUpload", s3AbortMultipartUpload },
68 { "s3:CreateBucket", s3CreateBucket },
69 { "s3:DeleteBucketPolicy", s3DeleteBucketPolicy },
70 { "s3:DeleteBucket", s3DeleteBucket },
71 { "s3:DeleteBucketWebsite", s3DeleteBucketWebsite },
72 { "s3:DeleteObject", s3DeleteObject },
73 { "s3:DeleteObjectVersion", s3DeleteObjectVersion },
224ce89b
WB
74 { "s3:DeleteObjectTagging", s3DeleteObjectTagging },
75 { "s3:DeleteObjectVersionTagging", s3DeleteObjectVersionTagging },
9f95a23c
TL
76 { "s3:DeleteBucketPublicAccessBlock", s3DeleteBucketPublicAccessBlock},
77 { "s3:DeletePublicAccessBlock", s3DeletePublicAccessBlock},
31f18b77
FG
78 { "s3:DeleteReplicationConfiguration", s3DeleteReplicationConfiguration },
79 { "s3:GetAccelerateConfiguration", s3GetAccelerateConfiguration },
80 { "s3:GetBucketAcl", s3GetBucketAcl },
81 { "s3:GetBucketCORS", s3GetBucketCORS },
82 { "s3:GetBucketLocation", s3GetBucketLocation },
83 { "s3:GetBucketLogging", s3GetBucketLogging },
84 { "s3:GetBucketNotification", s3GetBucketNotification },
85 { "s3:GetBucketPolicy", s3GetBucketPolicy },
9f95a23c
TL
86 { "s3:GetBucketPolicyStatus", s3GetBucketPolicyStatus },
87 { "s3:GetBucketPublicAccessBlock", s3GetBucketPublicAccessBlock },
31f18b77
FG
88 { "s3:GetBucketRequestPayment", s3GetBucketRequestPayment },
89 { "s3:GetBucketTagging", s3GetBucketTagging },
90 { "s3:GetBucketVersioning", s3GetBucketVersioning },
91 { "s3:GetBucketWebsite", s3GetBucketWebsite },
92 { "s3:GetLifecycleConfiguration", s3GetLifecycleConfiguration },
eafe8130 93 { "s3:GetBucketObjectLockConfiguration", s3GetBucketObjectLockConfiguration },
9f95a23c 94 { "s3:GetPublicAccessBlock", s3GetPublicAccessBlock },
31f18b77
FG
95 { "s3:GetObjectAcl", s3GetObjectAcl },
96 { "s3:GetObject", s3GetObject },
97 { "s3:GetObjectTorrent", s3GetObjectTorrent },
98 { "s3:GetObjectVersionAcl", s3GetObjectVersionAcl },
99 { "s3:GetObjectVersion", s3GetObjectVersion },
100 { "s3:GetObjectVersionTorrent", s3GetObjectVersionTorrent },
224ce89b
WB
101 { "s3:GetObjectTagging", s3GetObjectTagging },
102 { "s3:GetObjectVersionTagging", s3GetObjectVersionTagging},
eafe8130
TL
103 { "s3:GetObjectRetention", s3GetObjectRetention},
104 { "s3:GetObjectLegalHold", s3GetObjectLegalHold},
31f18b77
FG
105 { "s3:GetReplicationConfiguration", s3GetReplicationConfiguration },
106 { "s3:ListAllMyBuckets", s3ListAllMyBuckets },
28e407b8 107 { "s3:ListBucketMultipartUploads", s3ListBucketMultipartUploads },
31f18b77
FG
108 { "s3:ListBucket", s3ListBucket },
109 { "s3:ListBucketVersions", s3ListBucketVersions },
110 { "s3:ListMultipartUploadParts", s3ListMultipartUploadParts },
111 { "s3:PutAccelerateConfiguration", s3PutAccelerateConfiguration },
112 { "s3:PutBucketAcl", s3PutBucketAcl },
113 { "s3:PutBucketCORS", s3PutBucketCORS },
114 { "s3:PutBucketLogging", s3PutBucketLogging },
115 { "s3:PutBucketNotification", s3PutBucketNotification },
116 { "s3:PutBucketPolicy", s3PutBucketPolicy },
117 { "s3:PutBucketRequestPayment", s3PutBucketRequestPayment },
118 { "s3:PutBucketTagging", s3PutBucketTagging },
119 { "s3:PutBucketVersioning", s3PutBucketVersioning },
120 { "s3:PutBucketWebsite", s3PutBucketWebsite },
121 { "s3:PutLifecycleConfiguration", s3PutLifecycleConfiguration },
eafe8130 122 { "s3:PutBucketObjectLockConfiguration", s3PutBucketObjectLockConfiguration },
31f18b77
FG
123 { "s3:PutObjectAcl", s3PutObjectAcl },
124 { "s3:PutObject", s3PutObject },
125 { "s3:PutObjectVersionAcl", s3PutObjectVersionAcl },
224ce89b
WB
126 { "s3:PutObjectTagging", s3PutObjectTagging },
127 { "s3:PutObjectVersionTagging", s3PutObjectVersionTagging },
eafe8130
TL
128 { "s3:PutObjectRetention", s3PutObjectRetention },
129 { "s3:PutObjectLegalHold", s3PutObjectLegalHold },
130 { "s3:BypassGovernanceRetention", s3BypassGovernanceRetention },
9f95a23c
TL
131 { "s3:PutBucketPublicAccessBlock", s3PutBucketPublicAccessBlock },
132 { "s3:PutPublicAccessBlock", s3PutPublicAccessBlock },
31f18b77 133 { "s3:PutReplicationConfiguration", s3PutReplicationConfiguration },
11fdf7f2
TL
134 { "s3:RestoreObject", s3RestoreObject },
135 { "iam:PutUserPolicy", iamPutUserPolicy },
136 { "iam:GetUserPolicy", iamGetUserPolicy },
137 { "iam:DeleteUserPolicy", iamDeleteUserPolicy },
138 { "iam:ListUserPolicies", iamListUserPolicies },
139 { "iam:CreateRole", iamCreateRole},
140 { "iam:DeleteRole", iamDeleteRole},
141 { "iam:GetRole", iamGetRole},
142 { "iam:ModifyRole", iamModifyRole},
143 { "iam:ListRoles", iamListRoles},
144 { "iam:PutRolePolicy", iamPutRolePolicy},
145 { "iam:GetRolePolicy", iamGetRolePolicy},
146 { "iam:ListRolePolicies", iamListRolePolicies},
147 { "iam:DeleteRolePolicy", iamDeleteRolePolicy},
f91f0fd5
TL
148 { "iam:CreateOIDCProvider", iamCreateOIDCProvider},
149 { "iam:DeleteOIDCProvider", iamDeleteOIDCProvider},
150 { "iam:GetOIDCProvider", iamGetOIDCProvider},
151 { "iam:ListOIDCProviders", iamListOIDCProviders},
11fdf7f2
TL
152 { "sts:AssumeRole", stsAssumeRole},
153 { "sts:AssumeRoleWithWebIdentity", stsAssumeRoleWithWebIdentity},
154 { "sts:GetSessionToken", stsGetSessionToken},
155};
31f18b77
FG
156
157struct PolicyParser;
158
159const Keyword top[1]{"<Top>", TokenKind::pseudo, TokenID::Top, 0, false,
160 false};
161const Keyword cond_key[1]{"<Condition Key>", TokenKind::cond_key,
162 TokenID::CondKey, 0, true, false};
163
164struct ParseState {
165 PolicyParser* pp;
166 const Keyword* w;
167
168 bool arraying = false;
169 bool objecting = false;
c07f9fc5 170 bool cond_ifexists = false;
31f18b77
FG
171
172 void reset();
173
174 ParseState(PolicyParser* pp, const Keyword* w)
175 : pp(pp), w(w) {}
176
177 bool obj_start();
178
179 bool obj_end();
180
181 bool array_start() {
182 if (w->arrayable && !arraying) {
183 arraying = true;
184 return true;
185 }
186 return false;
187 }
188
189 bool array_end();
190
191 bool key(const char* s, size_t l);
192 bool do_string(CephContext* cct, const char* s, size_t l);
193 bool number(const char* str, size_t l);
194};
195
196// If this confuses you, look up the Curiously Recurring Template Pattern
197struct PolicyParser : public BaseReaderHandler<UTF8<>, PolicyParser> {
198 keyword_hash tokens;
199 std::vector<ParseState> s;
200 CephContext* cct;
201 const string& tenant;
202 Policy& policy;
d2e6a577 203 uint32_t v = 0;
31f18b77
FG
204
205 uint32_t seen = 0;
206
207 uint32_t dex(TokenID in) const {
208 switch (in) {
209 case TokenID::Version:
210 return 0x1;
211 case TokenID::Id:
212 return 0x2;
213 case TokenID::Statement:
214 return 0x4;
215 case TokenID::Sid:
216 return 0x8;
217 case TokenID::Effect:
218 return 0x10;
219 case TokenID::Principal:
220 return 0x20;
221 case TokenID::NotPrincipal:
222 return 0x40;
223 case TokenID::Action:
224 return 0x80;
225 case TokenID::NotAction:
226 return 0x100;
227 case TokenID::Resource:
228 return 0x200;
229 case TokenID::NotResource:
230 return 0x400;
231 case TokenID::Condition:
232 return 0x800;
233 case TokenID::AWS:
234 return 0x1000;
235 case TokenID::Federated:
236 return 0x2000;
237 case TokenID::Service:
238 return 0x4000;
239 case TokenID::CanonicalUser:
240 return 0x8000;
241 default:
242 ceph_abort();
243 }
244 }
245 bool test(TokenID in) {
246 return seen & dex(in);
247 }
248 void set(TokenID in) {
249 seen |= dex(in);
d2e6a577
FG
250 if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) |
251 dex(TokenID::Principal) | dex(TokenID::NotPrincipal) |
252 dex(TokenID::Action) | dex(TokenID::NotAction) |
253 dex(TokenID::Resource) | dex(TokenID::NotResource) |
254 dex(TokenID::Condition) | dex(TokenID::AWS) |
255 dex(TokenID::Federated) | dex(TokenID::Service) |
256 dex(TokenID::CanonicalUser))) {
257 v |= dex(in);
c07f9fc5 258 }
31f18b77
FG
259 }
260 void set(std::initializer_list<TokenID> l) {
261 for (auto in : l) {
262 seen |= dex(in);
d2e6a577
FG
263 if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) |
264 dex(TokenID::Principal) | dex(TokenID::NotPrincipal) |
265 dex(TokenID::Action) | dex(TokenID::NotAction) |
266 dex(TokenID::Resource) | dex(TokenID::NotResource) |
267 dex(TokenID::Condition) | dex(TokenID::AWS) |
268 dex(TokenID::Federated) | dex(TokenID::Service) |
269 dex(TokenID::CanonicalUser))) {
270 v |= dex(in);
c07f9fc5 271 }
31f18b77
FG
272 }
273 }
274 void reset(TokenID in) {
275 seen &= ~dex(in);
d2e6a577
FG
276 if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) |
277 dex(TokenID::Principal) | dex(TokenID::NotPrincipal) |
278 dex(TokenID::Action) | dex(TokenID::NotAction) |
279 dex(TokenID::Resource) | dex(TokenID::NotResource) |
280 dex(TokenID::Condition) | dex(TokenID::AWS) |
281 dex(TokenID::Federated) | dex(TokenID::Service) |
282 dex(TokenID::CanonicalUser))) {
283 v &= ~dex(in);
c07f9fc5 284 }
31f18b77
FG
285 }
286 void reset(std::initializer_list<TokenID> l) {
287 for (auto in : l) {
288 seen &= ~dex(in);
d2e6a577
FG
289 if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) |
290 dex(TokenID::Principal) | dex(TokenID::NotPrincipal) |
291 dex(TokenID::Action) | dex(TokenID::NotAction) |
292 dex(TokenID::Resource) | dex(TokenID::NotResource) |
293 dex(TokenID::Condition) | dex(TokenID::AWS) |
294 dex(TokenID::Federated) | dex(TokenID::Service) |
295 dex(TokenID::CanonicalUser))) {
296 v &= ~dex(in);
c07f9fc5
FG
297 }
298 }
299 }
d2e6a577
FG
300 void reset(uint32_t& v) {
301 seen &= ~v;
302 v = 0;
31f18b77
FG
303 }
304
305 PolicyParser(CephContext* cct, const string& tenant, Policy& policy)
306 : cct(cct), tenant(tenant), policy(policy) {}
307 PolicyParser(const PolicyParser& policy) = delete;
308
309 bool StartObject() {
310 if (s.empty()) {
311 s.push_back({this, top});
312 s.back().objecting = true;
313 return true;
314 }
315
316 return s.back().obj_start();
317 }
318 bool EndObject(SizeType memberCount) {
319 if (s.empty()) {
320 return false;
321 }
31f18b77
FG
322 return s.back().obj_end();
323 }
324 bool Key(const char* str, SizeType length, bool copy) {
325 if (s.empty()) {
326 return false;
327 }
31f18b77
FG
328 return s.back().key(str, length);
329 }
330
331 bool String(const char* str, SizeType length, bool copy) {
332 if (s.empty()) {
333 return false;
334 }
31f18b77
FG
335 return s.back().do_string(cct, str, length);
336 }
337 bool RawNumber(const char* str, SizeType length, bool copy) {
338 if (s.empty()) {
339 return false;
340 }
341
342 return s.back().number(str, length);
343 }
344 bool StartArray() {
345 if (s.empty()) {
346 return false;
347 }
348
349 return s.back().array_start();
350 }
351 bool EndArray(SizeType) {
352 if (s.empty()) {
353 return false;
354 }
355
356 return s.back().array_end();
357 }
358
359 bool Default() {
360 return false;
361 }
362};
363
364
365// I really despise this misfeature of C++.
366//
367bool ParseState::obj_end() {
368 if (objecting) {
369 objecting = false;
370 if (!arraying) {
371 pp->s.pop_back();
372 } else {
373 reset();
374 }
375 return true;
376 }
377 return false;
378}
379
380bool ParseState::key(const char* s, size_t l) {
c07f9fc5
FG
381 auto token_len = l;
382 bool ifexists = false;
383 if (w->id == TokenID::Condition && w->kind == TokenKind::statement) {
384 static constexpr char IfExists[] = "IfExists";
385 if (boost::algorithm::ends_with(boost::string_view{s, l}, IfExists)) {
386 ifexists = true;
387 token_len -= sizeof(IfExists)-1;
388 }
389 }
390 auto k = pp->tokens.lookup(s, token_len);
31f18b77
FG
391
392 if (!k) {
393 if (w->kind == TokenKind::cond_op) {
d2e6a577 394 auto id = w->id;
31f18b77 395 auto& t = pp->policy.statements.back();
d2e6a577 396 auto c_ife = cond_ifexists;
31f18b77 397 pp->s.emplace_back(pp, cond_key);
d2e6a577 398 t.conditions.emplace_back(id, s, l, c_ife);
31f18b77
FG
399 return true;
400 } else {
401 return false;
402 }
403 }
404
405 // If the token we're going with belongs within the condition at the
406 // top of the stack and we haven't already encountered it, push it
407 // on the stack
31f18b77
FG
408 // Top
409 if ((((w->id == TokenID::Top) && (k->kind == TokenKind::top)) ||
410 // Statement
411 ((w->id == TokenID::Statement) && (k->kind == TokenKind::statement)) ||
412
413 /// Principal
414 ((w->id == TokenID::Principal || w->id == TokenID::NotPrincipal) &&
415 (k->kind == TokenKind::princ_type))) &&
416
417 // Check that it hasn't been encountered. Note that this
418 // conjoins with the run of disjunctions above.
419 !pp->test(k->id)) {
420 pp->set(k->id);
421 pp->s.emplace_back(pp, k);
422 return true;
423 } else if ((w->id == TokenID::Condition) &&
424 (k->kind == TokenKind::cond_op)) {
425 pp->s.emplace_back(pp, k);
c07f9fc5 426 pp->s.back().cond_ifexists = ifexists;
31f18b77
FG
427 return true;
428 }
429 return false;
430}
431
432// I should just rewrite a few helper functions to use iterators,
433// which will make all of this ever so much nicer.
11fdf7f2
TL
434static boost::optional<Principal> parse_principal(CephContext* cct, TokenID t,
435 string&& s) {
31f18b77
FG
436 // Wildcard!
437 if ((t == TokenID::AWS) && (s == "*")) {
438 return Principal::wildcard();
439
440 // Do nothing for now.
441 } else if (t == TokenID::CanonicalUser) {
442
11fdf7f2
TL
443 } // AWS and Federated ARNs
444 else if (t == TokenID::AWS || t == TokenID::Federated) {
b32b8144
FG
445 if (auto a = ARN::parse(s)) {
446 if (a->resource == "root") {
447 return Principal::tenant(std::move(a->account));
448 }
449
450 static const char rx_str[] = "([^/]*)/(.*)";
451 static const regex rx(rx_str, sizeof(rx_str) - 1,
11fdf7f2
TL
452 std::regex_constants::ECMAScript |
453 std::regex_constants::optimize);
b32b8144
FG
454 smatch match;
455 if (regex_match(a->resource, match, rx) && match.size() == 3) {
456 if (match[1] == "user") {
457 return Principal::user(std::move(a->account),
458 match[2]);
459 }
460
461 if (match[1] == "role") {
462 return Principal::role(std::move(a->account),
463 match[2]);
464 }
11fdf7f2
TL
465
466 if (match[1] == "oidc-provider") {
467 return Principal::oidc_provider(std::move(match[2]));
468 }
f91f0fd5
TL
469 if (match[1] == "assumed-role") {
470 return Principal::assumed_role(std::move(a->account), match[2]);
471 }
b32b8144
FG
472 }
473 } else {
31f18b77
FG
474 if (std::none_of(s.begin(), s.end(),
475 [](const char& c) {
476 return (c == ':') || (c == '/');
477 })) {
478 // Since tenants are simply prefixes, there's no really good
479 // way to see if one exists or not. So we return the thing and
480 // let them try to match against it.
481 return Principal::tenant(std::move(s));
482 }
483 }
31f18b77
FG
484 }
485
486 ldout(cct, 0) << "Supplied principal is discarded: " << s << dendl;
487 return boost::none;
488}
489
490bool ParseState::do_string(CephContext* cct, const char* s, size_t l) {
491 auto k = pp->tokens.lookup(s, l);
492 Policy& p = pp->policy;
11fdf7f2
TL
493 bool is_action = false;
494 bool is_validaction = false;
31f18b77
FG
495 Statement* t = p.statements.empty() ? nullptr : &(p.statements.back());
496
497 // Top level!
498 if ((w->id == TokenID::Version) && k &&
499 k->kind == TokenKind::version_key) {
500 p.version = static_cast<Version>(k->specific);
501 } else if (w->id == TokenID::Id) {
502 p.id = string(s, l);
503
504 // Statement
505
506 } else if (w->id == TokenID::Sid) {
507 t->sid.emplace(s, l);
b32b8144 508 } else if ((w->id == TokenID::Effect) && k &&
31f18b77
FG
509 k->kind == TokenKind::effect_key) {
510 t->effect = static_cast<Effect>(k->specific);
511 } else if (w->id == TokenID::Principal && s && *s == '*') {
512 t->princ.emplace(Principal::wildcard());
513 } else if (w->id == TokenID::NotPrincipal && s && *s == '*') {
514 t->noprinc.emplace(Principal::wildcard());
515 } else if ((w->id == TokenID::Action) ||
516 (w->id == TokenID::NotAction)) {
11fdf7f2
TL
517 is_action = true;
518 if (*s == '*') {
519 is_validaction = true;
520 (w->id == TokenID::Action ?
521 t->action = allValue : t->notaction = allValue);
522 } else {
523 for (auto& p : actpairs) {
524 if (match_policy({s, l}, p.name, MATCH_POLICY_ACTION)) {
525 is_validaction = true;
526 (w->id == TokenID::Action ? t->action[p.bit] = 1 : t->notaction[p.bit] = 1);
527 }
528 if ((t->action & s3AllValue) == s3AllValue) {
529 t->action[s3All] = 1;
530 }
531 if ((t->notaction & s3AllValue) == s3AllValue) {
532 t->notaction[s3All] = 1;
533 }
534 if ((t->action & iamAllValue) == iamAllValue) {
535 t->action[iamAll] = 1;
536 }
537 if ((t->notaction & iamAllValue) == iamAllValue) {
538 t->notaction[iamAll] = 1;
539 }
540 if ((t->action & stsAllValue) == stsAllValue) {
541 t->action[stsAll] = 1;
542 }
543 if ((t->notaction & stsAllValue) == stsAllValue) {
544 t->notaction[stsAll] = 1;
545 }
31f18b77
FG
546 }
547 }
548 } else if (w->id == TokenID::Resource || w->id == TokenID::NotResource) {
549 auto a = ARN::parse({s, l}, true);
550 // You can't specify resources for someone ELSE'S account.
551 if (a && (a->account.empty() || a->account == pp->tenant ||
552 a->account == "*")) {
553 if (a->account.empty() || a->account == "*")
554 a->account = pp->tenant;
555 (w->id == TokenID::Resource ? t->resource : t->notresource)
556 .emplace(std::move(*a));
557 }
558 else
559 ldout(cct, 0) << "Supplied resource is discarded: " << string(s, l)
560 << dendl;
561 } else if (w->kind == TokenKind::cond_key) {
562 auto& t = pp->policy.statements.back();
563 t.conditions.back().vals.emplace_back(s, l);
564
565 // Principals
566
567 } else if (w->kind == TokenKind::princ_type) {
3efd9988
FG
568 if (pp->s.size() <= 1) {
569 return false;
570 }
31f18b77
FG
571 auto& pri = pp->s[pp->s.size() - 2].w->id == TokenID::Principal ?
572 t->princ : t->noprinc;
573
b32b8144
FG
574
575 if (auto o = parse_principal(pp->cct, w->id, string(s, l))) {
31f18b77 576 pri.emplace(std::move(*o));
b32b8144 577 }
31f18b77
FG
578
579 // Failure
580
581 } else {
582 return false;
583 }
584
585 if (!arraying) {
586 pp->s.pop_back();
587 }
588
11fdf7f2
TL
589 if (is_action && !is_validaction){
590 return false;
591 }
592
31f18b77
FG
593 return true;
594}
595
596bool ParseState::number(const char* s, size_t l) {
597 // Top level!
598 if (w->kind == TokenKind::cond_key) {
599 auto& t = pp->policy.statements.back();
600 t.conditions.back().vals.emplace_back(s, l);
601
602 // Failure
603
604 } else {
605 return false;
606 }
607
608 if (!arraying) {
609 pp->s.pop_back();
610 }
611
612 return true;
613}
614
615void ParseState::reset() {
c07f9fc5 616 pp->reset(pp->v);
31f18b77
FG
617}
618
619bool ParseState::obj_start() {
620 if (w->objectable && !objecting) {
621 objecting = true;
622 if (w->id == TokenID::Statement) {
11fdf7f2 623 pp->policy.statements.emplace_back();
31f18b77
FG
624 }
625
626 return true;
627 }
628
629 return false;
630}
631
632
633bool ParseState::array_end() {
634 if (arraying && !objecting) {
635 pp->s.pop_back();
636 return true;
637 }
638
639 return false;
640}
641
642ostream& operator <<(ostream& m, const MaskedIP& ip) {
643 // I have a theory about why std::bitset is the way it is.
644 if (ip.v6) {
b32b8144
FG
645 for (int i = 7; i >= 0; --i) {
646 uint16_t hextet = 0;
647 for (int j = 15; j >= 0; --j) {
648 hextet |= (ip.addr[(i * 16) + j] << j);
31f18b77 649 }
b32b8144 650 m << hex << (unsigned int) hextet;
31f18b77 651 if (i != 0) {
b32b8144 652 m << ":";
31f18b77
FG
653 }
654 }
655 } else {
656 // It involves Satan.
657 for (int i = 3; i >= 0; --i) {
658 uint8_t b = 0;
659 for (int j = 7; j >= 0; --j) {
660 b |= (ip.addr[(i * 8) + j] << j);
661 }
b32b8144 662 m << (unsigned int) b;
31f18b77
FG
663 if (i != 0) {
664 m << ".";
665 }
666 }
667 }
b32b8144 668 m << "/" << dec << ip.prefix;
31f18b77
FG
669 // It would explain a lot
670 return m;
671}
672
31f18b77
FG
673bool Condition::eval(const Environment& env) const {
674 auto i = env.find(key);
675 if (op == TokenID::Null) {
676 return i == env.end() ? true : false;
677 }
678
679 if (i == env.end()) {
c07f9fc5 680 return ifexists;
31f18b77
FG
681 }
682 const auto& s = i->second;
683
684 switch (op) {
685 // String!
686 case TokenID::StringEquals:
687 return orrible(std::equal_to<std::string>(), s, vals);
688
689 case TokenID::StringNotEquals:
11fdf7f2 690 return orrible(std::not_fn(std::equal_to<std::string>()),
31f18b77
FG
691 s, vals);
692
693 case TokenID::StringEqualsIgnoreCase:
694 return orrible(ci_equal_to(), s, vals);
695
696 case TokenID::StringNotEqualsIgnoreCase:
11fdf7f2 697 return orrible(std::not_fn(ci_equal_to()), s, vals);
31f18b77 698
31f18b77 699 case TokenID::StringLike:
d2e6a577
FG
700 return orrible(string_like(), s, vals);
701
31f18b77 702 case TokenID::StringNotLike:
11fdf7f2 703 return orrible(std::not_fn(string_like()), s, vals);
31f18b77
FG
704
705 // Numeric
706 case TokenID::NumericEquals:
707 return shortible(std::equal_to<double>(), as_number, s, vals);
708
709 case TokenID::NumericNotEquals:
11fdf7f2 710 return shortible(std::not_fn(std::equal_to<double>()),
31f18b77
FG
711 as_number, s, vals);
712
713
714 case TokenID::NumericLessThan:
715 return shortible(std::less<double>(), as_number, s, vals);
716
717
718 case TokenID::NumericLessThanEquals:
719 return shortible(std::less_equal<double>(), as_number, s, vals);
720
721 case TokenID::NumericGreaterThan:
722 return shortible(std::greater<double>(), as_number, s, vals);
723
724 case TokenID::NumericGreaterThanEquals:
725 return shortible(std::greater_equal<double>(), as_number, s, vals);
726
727 // Date!
728 case TokenID::DateEquals:
729 return shortible(std::equal_to<ceph::real_time>(), as_date, s, vals);
730
731 case TokenID::DateNotEquals:
11fdf7f2 732 return shortible(std::not_fn(std::equal_to<ceph::real_time>()),
31f18b77
FG
733 as_date, s, vals);
734
735 case TokenID::DateLessThan:
736 return shortible(std::less<ceph::real_time>(), as_date, s, vals);
737
738
739 case TokenID::DateLessThanEquals:
740 return shortible(std::less_equal<ceph::real_time>(), as_date, s, vals);
741
742 case TokenID::DateGreaterThan:
743 return shortible(std::greater<ceph::real_time>(), as_date, s, vals);
744
745 case TokenID::DateGreaterThanEquals:
746 return shortible(std::greater_equal<ceph::real_time>(), as_date, s,
747 vals);
748
749 // Bool!
750 case TokenID::Bool:
751 return shortible(std::equal_to<bool>(), as_bool, s, vals);
752
753 // Binary!
754 case TokenID::BinaryEquals:
755 return shortible(std::equal_to<ceph::bufferlist>(), as_binary, s,
756 vals);
757
758 // IP Address!
759 case TokenID::IpAddress:
760 return shortible(std::equal_to<MaskedIP>(), as_network, s, vals);
761
762 case TokenID::NotIpAddress:
b32b8144
FG
763 {
764 auto xc = as_network(s);
765 if (!xc) {
766 return false;
767 }
768
769 for (const string& d : vals) {
770 auto xd = as_network(d);
771 if (!xd) {
772 continue;
773 }
774
775 if (xc == xd) {
776 return false;
777 }
778 }
779 return true;
780 }
31f18b77
FG
781
782#if 0
783 // Amazon Resource Names! (Does S3 need this?)
784 TokenID::ArnEquals, TokenID::ArnNotEquals, TokenID::ArnLike,
785 TokenID::ArnNotLike,
786#endif
787
788 default:
789 return false;
790 }
791}
792
11fdf7f2 793boost::optional<MaskedIP> Condition::as_network(const string& s) {
31f18b77
FG
794 MaskedIP m;
795 if (s.empty()) {
11fdf7f2 796 return boost::none;
31f18b77
FG
797 }
798
b32b8144 799 m.v6 = (s.find(':') == string::npos) ? false : true;
11fdf7f2 800
31f18b77
FG
801 auto slash = s.find('/');
802 if (slash == string::npos) {
803 m.prefix = m.v6 ? 128 : 32;
804 } else {
805 char* end = 0;
806 m.prefix = strtoul(s.data() + slash + 1, &end, 10);
807 if (*end != 0 || (m.v6 && m.prefix > 128) ||
808 (!m.v6 && m.prefix > 32)) {
11fdf7f2 809 return boost::none;
31f18b77
FG
810 }
811 }
812
813 string t;
814 auto p = &s;
815
816 if (slash != string::npos) {
817 t.assign(s, 0, slash);
818 p = &t;
819 }
820
821 if (m.v6) {
b32b8144 822 struct in6_addr a;
31f18b77 823 if (inet_pton(AF_INET6, p->c_str(), static_cast<void*>(&a)) != 1) {
11fdf7f2 824 return boost::none;
31f18b77
FG
825 }
826
b32b8144
FG
827 m.addr |= Address(a.s6_addr[15]) << 0;
828 m.addr |= Address(a.s6_addr[14]) << 8;
829 m.addr |= Address(a.s6_addr[13]) << 16;
830 m.addr |= Address(a.s6_addr[12]) << 24;
831 m.addr |= Address(a.s6_addr[11]) << 32;
832 m.addr |= Address(a.s6_addr[10]) << 40;
833 m.addr |= Address(a.s6_addr[9]) << 48;
834 m.addr |= Address(a.s6_addr[8]) << 56;
835 m.addr |= Address(a.s6_addr[7]) << 64;
836 m.addr |= Address(a.s6_addr[6]) << 72;
837 m.addr |= Address(a.s6_addr[5]) << 80;
838 m.addr |= Address(a.s6_addr[4]) << 88;
839 m.addr |= Address(a.s6_addr[3]) << 96;
840 m.addr |= Address(a.s6_addr[2]) << 104;
841 m.addr |= Address(a.s6_addr[1]) << 112;
842 m.addr |= Address(a.s6_addr[0]) << 120;
31f18b77 843 } else {
b32b8144 844 struct in_addr a;
31f18b77 845 if (inet_pton(AF_INET, p->c_str(), static_cast<void*>(&a)) != 1) {
11fdf7f2 846 return boost::none;
31f18b77 847 }
b32b8144
FG
848
849 m.addr = ntohl(a.s_addr);
31f18b77
FG
850 }
851
b32b8144 852 return m;
31f18b77
FG
853}
854
855namespace {
856const char* condop_string(const TokenID t) {
857 switch (t) {
858 case TokenID::StringEquals:
859 return "StringEquals";
860
861 case TokenID::StringNotEquals:
862 return "StringNotEquals";
863
864 case TokenID::StringEqualsIgnoreCase:
865 return "StringEqualsIgnoreCase";
866
867 case TokenID::StringNotEqualsIgnoreCase:
868 return "StringNotEqualsIgnoreCase";
869
870 case TokenID::StringLike:
871 return "StringLike";
872
873 case TokenID::StringNotLike:
874 return "StringNotLike";
875
876 // Numeric!
877 case TokenID::NumericEquals:
878 return "NumericEquals";
879
880 case TokenID::NumericNotEquals:
881 return "NumericNotEquals";
882
883 case TokenID::NumericLessThan:
884 return "NumericLessThan";
885
886 case TokenID::NumericLessThanEquals:
887 return "NumericLessThanEquals";
888
889 case TokenID::NumericGreaterThan:
890 return "NumericGreaterThan";
891
892 case TokenID::NumericGreaterThanEquals:
893 return "NumericGreaterThanEquals";
894
895 case TokenID::DateEquals:
896 return "DateEquals";
897
898 case TokenID::DateNotEquals:
899 return "DateNotEquals";
900
901 case TokenID::DateLessThan:
902 return "DateLessThan";
903
904 case TokenID::DateLessThanEquals:
905 return "DateLessThanEquals";
906
907 case TokenID::DateGreaterThan:
908 return "DateGreaterThan";
909
910 case TokenID::DateGreaterThanEquals:
911 return "DateGreaterThanEquals";
912
913 case TokenID::Bool:
914 return "Bool";
915
916 case TokenID::BinaryEquals:
917 return "BinaryEquals";
918
919 case TokenID::IpAddress:
920 return "case TokenID::IpAddress";
921
922 case TokenID::NotIpAddress:
923 return "NotIpAddress";
924
925 case TokenID::ArnEquals:
926 return "ArnEquals";
927
928 case TokenID::ArnNotEquals:
929 return "ArnNotEquals";
930
931 case TokenID::ArnLike:
932 return "ArnLike";
933
934 case TokenID::ArnNotLike:
935 return "ArnNotLike";
936
937 case TokenID::Null:
938 return "Null";
939
940 default:
941 return "InvalidConditionOperator";
942 }
943}
944
945template<typename Iterator>
946ostream& print_array(ostream& m, Iterator begin, Iterator end) {
947 if (begin == end) {
11fdf7f2 948 m << "[]";
31f18b77 949 } else {
31f18b77 950 m << "[ ";
11fdf7f2
TL
951 std::copy(begin, end, std::experimental::make_ostream_joiner(m, ", "));
952 m << " ]";
31f18b77 953 }
31f18b77
FG
954 return m;
955}
11fdf7f2
TL
956
957template<typename Iterator>
958ostream& print_dict(ostream& m, Iterator begin, Iterator end) {
959 m << "{ ";
960 std::copy(begin, end, std::experimental::make_ostream_joiner(m, ", "));
961 m << " }";
962 return m;
963}
964
31f18b77
FG
965}
966
967ostream& operator <<(ostream& m, const Condition& c) {
11fdf7f2 968 m << condop_string(c.op);
31f18b77
FG
969 if (c.ifexists) {
970 m << "IfExists";
971 }
972 m << ": { " << c.key;
973 print_array(m, c.vals.cbegin(), c.vals.cend());
11fdf7f2 974 return m << " }";
31f18b77
FG
975}
976
977Effect Statement::eval(const Environment& e,
11fdf7f2 978 boost::optional<const rgw::auth::Identity&> ida,
31f18b77 979 uint64_t act, const ARN& res) const {
f6b5b4d7
TL
980
981 if (eval_principal(e, ida) == Effect::Deny) {
982 return Effect::Pass;
31f18b77
FG
983 }
984
11fdf7f2
TL
985 if (!resource.empty()) {
986 if (!std::any_of(resource.begin(), resource.end(),
987 [&res](const ARN& pattern) {
988 return pattern.match(res);
989 })) {
990 return Effect::Pass;
991 }
992 } else if (!notresource.empty()) {
993 if (std::any_of(notresource.begin(), notresource.end(),
994 [&res](const ARN& pattern) {
995 return pattern.match(res);
996 })) {
997 return Effect::Pass;
998 }
31f18b77
FG
999 }
1000
11fdf7f2 1001 if (!(action[act] == 1) || (notaction[act] == 1)) {
31f18b77
FG
1002 return Effect::Pass;
1003 }
1004
1005 if (std::all_of(conditions.begin(),
1006 conditions.end(),
1007 [&e](const Condition& c) { return c.eval(e);})) {
1008 return effect;
1009 }
1010
1011 return Effect::Pass;
1012}
1013
11fdf7f2
TL
1014Effect Statement::eval_principal(const Environment& e,
1015 boost::optional<const rgw::auth::Identity&> ida) const {
1016 if (ida) {
1017 if (princ.empty() && noprinc.empty()) {
1018 return Effect::Deny;
1019 }
1020 if (!princ.empty() && !ida->is_identity(princ)) {
1021 return Effect::Deny;
1022 } else if (!noprinc.empty() && ida->is_identity(noprinc)) {
1023 return Effect::Deny;
1024 }
1025 }
1026 return Effect::Allow;
1027}
1028
1029Effect Statement::eval_conditions(const Environment& e) const {
1030 if (std::all_of(conditions.begin(),
1031 conditions.end(),
1032 [&e](const Condition& c) { return c.eval(e);})) {
1033 return Effect::Allow;
1034 }
1035 return Effect::Deny;
1036}
1037
31f18b77
FG
1038namespace {
1039const char* action_bit_string(uint64_t action) {
1040 switch (action) {
1041 case s3GetObject:
1042 return "s3:GetObject";
1043
1044 case s3GetObjectVersion:
1045 return "s3:GetObjectVersion";
1046
1047 case s3PutObject:
1048 return "s3:PutObject";
1049
1050 case s3GetObjectAcl:
1051 return "s3:GetObjectAcl";
1052
1053 case s3GetObjectVersionAcl:
1054 return "s3:GetObjectVersionAcl";
1055
1056 case s3PutObjectAcl:
1057 return "s3:PutObjectAcl";
1058
1059 case s3PutObjectVersionAcl:
1060 return "s3:PutObjectVersionAcl";
1061
1062 case s3DeleteObject:
1063 return "s3:DeleteObject";
1064
1065 case s3DeleteObjectVersion:
1066 return "s3:DeleteObjectVersion";
1067
1068 case s3ListMultipartUploadParts:
1069 return "s3:ListMultipartUploadParts";
1070
1071 case s3AbortMultipartUpload:
1072 return "s3:AbortMultipartUpload";
1073
1074 case s3GetObjectTorrent:
1075 return "s3:GetObjectTorrent";
1076
1077 case s3GetObjectVersionTorrent:
1078 return "s3:GetObjectVersionTorrent";
1079
1080 case s3RestoreObject:
1081 return "s3:RestoreObject";
1082
1083 case s3CreateBucket:
1084 return "s3:CreateBucket";
1085
1086 case s3DeleteBucket:
1087 return "s3:DeleteBucket";
1088
1089 case s3ListBucket:
1090 return "s3:ListBucket";
1091
1092 case s3ListBucketVersions:
1093 return "s3:ListBucketVersions";
1094 case s3ListAllMyBuckets:
1095 return "s3:ListAllMyBuckets";
1096
28e407b8
AA
1097 case s3ListBucketMultipartUploads:
1098 return "s3:ListBucketMultipartUploads";
31f18b77
FG
1099
1100 case s3GetAccelerateConfiguration:
1101 return "s3:GetAccelerateConfiguration";
1102
1103 case s3PutAccelerateConfiguration:
1104 return "s3:PutAccelerateConfiguration";
1105
1106 case s3GetBucketAcl:
1107 return "s3:GetBucketAcl";
1108
1109 case s3PutBucketAcl:
1110 return "s3:PutBucketAcl";
1111
1112 case s3GetBucketCORS:
1113 return "s3:GetBucketCORS";
1114
1115 case s3PutBucketCORS:
1116 return "s3:PutBucketCORS";
1117
1118 case s3GetBucketVersioning:
1119 return "s3:GetBucketVersioning";
1120
1121 case s3PutBucketVersioning:
1122 return "s3:PutBucketVersioning";
1123
1124 case s3GetBucketRequestPayment:
1125 return "s3:GetBucketRequestPayment";
1126
1127 case s3PutBucketRequestPayment:
1128 return "s3:PutBucketRequestPayment";
1129
1130 case s3GetBucketLocation:
1131 return "s3:GetBucketLocation";
1132
1133 case s3GetBucketPolicy:
1134 return "s3:GetBucketPolicy";
1135
1136 case s3DeleteBucketPolicy:
1137 return "s3:DeleteBucketPolicy";
1138
1139 case s3PutBucketPolicy:
1140 return "s3:PutBucketPolicy";
1141
1142 case s3GetBucketNotification:
1143 return "s3:GetBucketNotification";
1144
1145 case s3PutBucketNotification:
1146 return "s3:PutBucketNotification";
1147
1148 case s3GetBucketLogging:
1149 return "s3:GetBucketLogging";
1150
1151 case s3PutBucketLogging:
1152 return "s3:PutBucketLogging";
1153
1154 case s3GetBucketTagging:
1155 return "s3:GetBucketTagging";
1156
1157 case s3PutBucketTagging:
1158 return "s3:PutBucketTagging";
1159
1160 case s3GetBucketWebsite:
1161 return "s3:GetBucketWebsite";
1162
1163 case s3PutBucketWebsite:
1164 return "s3:PutBucketWebsite";
1165
1166 case s3DeleteBucketWebsite:
1167 return "s3:DeleteBucketWebsite";
1168
1169 case s3GetLifecycleConfiguration:
1170 return "s3:GetLifecycleConfiguration";
1171
1172 case s3PutLifecycleConfiguration:
1173 return "s3:PutLifecycleConfiguration";
1174
1175 case s3PutReplicationConfiguration:
1176 return "s3:PutReplicationConfiguration";
1177
1178 case s3GetReplicationConfiguration:
1179 return "s3:GetReplicationConfiguration";
1180
1181 case s3DeleteReplicationConfiguration:
1182 return "s3:DeleteReplicationConfiguration";
224ce89b
WB
1183
1184 case s3PutObjectTagging:
1185 return "s3:PutObjectTagging";
1186
1187 case s3PutObjectVersionTagging:
1188 return "s3:PutObjectVersionTagging";
1189
1190 case s3GetObjectTagging:
1191 return "s3:GetObjectTagging";
1192
1193 case s3GetObjectVersionTagging:
1194 return "s3:GetObjectVersionTagging";
1195
1196 case s3DeleteObjectTagging:
1197 return "s3:DeleteObjectTagging";
1198
1199 case s3DeleteObjectVersionTagging:
1200 return "s3:DeleteObjectVersionTagging";
11fdf7f2 1201
eafe8130
TL
1202 case s3PutBucketObjectLockConfiguration:
1203 return "s3:PutBucketObjectLockConfiguration";
1204
1205 case s3GetBucketObjectLockConfiguration:
1206 return "s3:GetBucketObjectLockConfiguration";
1207
1208 case s3PutObjectRetention:
1209 return "s3:PutObjectRetention";
1210
1211 case s3GetObjectRetention:
1212 return "s3:GetObjectRetention";
1213
1214 case s3PutObjectLegalHold:
1215 return "s3:PutObjectLegalHold";
1216
1217 case s3GetObjectLegalHold:
1218 return "s3:GetObjectLegalHold";
1219
1220 case s3BypassGovernanceRetention:
1221 return "s3:BypassGovernanceRetention";
1222
11fdf7f2
TL
1223 case iamPutUserPolicy:
1224 return "iam:PutUserPolicy";
1225
1226 case iamGetUserPolicy:
1227 return "iam:GetUserPolicy";
1228
1229 case iamListUserPolicies:
1230 return "iam:ListUserPolicies";
1231
1232 case iamDeleteUserPolicy:
1233 return "iam:DeleteUserPolicy";
1234
1235 case iamCreateRole:
1236 return "iam:CreateRole";
1237
1238 case iamDeleteRole:
1239 return "iam:DeleteRole";
1240
1241 case iamGetRole:
1242 return "iam:GetRole";
1243
1244 case iamModifyRole:
1245 return "iam:ModifyRole";
1246
1247 case iamListRoles:
1248 return "iam:ListRoles";
1249
1250 case iamPutRolePolicy:
1251 return "iam:PutRolePolicy";
1252
1253 case iamGetRolePolicy:
1254 return "iam:GetRolePolicy";
1255
1256 case iamListRolePolicies:
1257 return "iam:ListRolePolicies";
1258
1259 case iamDeleteRolePolicy:
1260 return "iam:DeleteRolePolicy";
1261
f91f0fd5
TL
1262 case iamCreateOIDCProvider:
1263 return "iam:CreateOIDCProvider";
1264
1265 case iamDeleteOIDCProvider:
1266 return "iam:DeleteOIDCProvider";
1267
1268 case iamGetOIDCProvider:
1269 return "iam:GetOIDCProvider";
1270
1271 case iamListOIDCProviders:
1272 return "iam:ListOIDCProviders";
1273
11fdf7f2
TL
1274 case stsAssumeRole:
1275 return "sts:AssumeRole";
1276
1277 case stsAssumeRoleWithWebIdentity:
1278 return "sts:AssumeRoleWithWebIdentity";
1279
1280 case stsGetSessionToken:
1281 return "sts:GetSessionToken";
31f18b77
FG
1282 }
1283 return "s3Invalid";
1284}
1285
11fdf7f2 1286ostream& print_actions(ostream& m, const Action_t a) {
31f18b77
FG
1287 bool begun = false;
1288 m << "[ ";
11fdf7f2
TL
1289 for (auto i = 0U; i < allCount; ++i) {
1290 if (a[i] == 1) {
31f18b77 1291 if (begun) {
11fdf7f2 1292 m << ", ";
31f18b77 1293 } else {
11fdf7f2 1294 begun = true;
31f18b77 1295 }
11fdf7f2 1296 m << action_bit_string(i);
31f18b77
FG
1297 }
1298 }
1299 if (begun) {
1300 m << " ]";
1301 } else {
1302 m << "]";
1303 }
1304 return m;
1305}
1306}
1307
1308ostream& operator <<(ostream& m, const Statement& s) {
1309 m << "{ ";
1310 if (s.sid) {
1311 m << "Sid: " << *s.sid << ", ";
1312 }
1313 if (!s.princ.empty()) {
1314 m << "Principal: ";
11fdf7f2 1315 print_dict(m, s.princ.cbegin(), s.princ.cend());
31f18b77
FG
1316 m << ", ";
1317 }
1318 if (!s.noprinc.empty()) {
1319 m << "NotPrincipal: ";
11fdf7f2 1320 print_dict(m, s.noprinc.cbegin(), s.noprinc.cend());
31f18b77
FG
1321 m << ", ";
1322 }
1323
1324 m << "Effect: " <<
1325 (s.effect == Effect::Allow ?
1326 (const char*) "Allow" :
1327 (const char*) "Deny");
1328
11fdf7f2 1329 if (s.action.any() || s.notaction.any() || !s.resource.empty() ||
31f18b77
FG
1330 !s.notresource.empty() || !s.conditions.empty()) {
1331 m << ", ";
1332 }
1333
11fdf7f2 1334 if (s.action.any()) {
31f18b77
FG
1335 m << "Action: ";
1336 print_actions(m, s.action);
1337
11fdf7f2 1338 if (s.notaction.any() || !s.resource.empty() ||
31f18b77
FG
1339 !s.notresource.empty() || !s.conditions.empty()) {
1340 m << ", ";
1341 }
1342 }
1343
11fdf7f2 1344 if (s.notaction.any()) {
31f18b77
FG
1345 m << "NotAction: ";
1346 print_actions(m, s.notaction);
1347
1348 if (!s.resource.empty() || !s.notresource.empty() ||
1349 !s.conditions.empty()) {
1350 m << ", ";
1351 }
1352 }
1353
1354 if (!s.resource.empty()) {
1355 m << "Resource: ";
1356 print_array(m, s.resource.cbegin(), s.resource.cend());
1357
1358 if (!s.notresource.empty() || !s.conditions.empty()) {
1359 m << ", ";
1360 }
1361 }
1362
1363 if (!s.notresource.empty()) {
1364 m << "NotResource: ";
1365 print_array(m, s.notresource.cbegin(), s.notresource.cend());
1366
1367 if (!s.conditions.empty()) {
1368 m << ", ";
1369 }
1370 }
1371
1372 if (!s.conditions.empty()) {
1373 m << "Condition: ";
11fdf7f2 1374 print_dict(m, s.conditions.cbegin(), s.conditions.cend());
31f18b77
FG
1375 }
1376
1377 return m << " }";
1378}
1379
31f18b77
FG
1380Policy::Policy(CephContext* cct, const string& tenant,
1381 const bufferlist& _text)
1382 : text(_text.to_str()) {
1383 StringStream ss(text.data());
1384 PolicyParser pp(cct, tenant, *this);
1385 auto pr = Reader{}.Parse<kParseNumbersAsStringsFlag |
1386 kParseCommentsFlag>(ss, pp);
1387 if (!pr) {
1388 throw PolicyParseException(std::move(pr));
1389 }
1390}
1391
1392Effect Policy::eval(const Environment& e,
11fdf7f2 1393 boost::optional<const rgw::auth::Identity&> ida,
31f18b77
FG
1394 std::uint64_t action, const ARN& resource) const {
1395 auto allowed = false;
1396 for (auto& s : statements) {
1397 auto g = s.eval(e, ida, action, resource);
1398 if (g == Effect::Deny) {
1399 return g;
1400 } else if (g == Effect::Allow) {
1401 allowed = true;
1402 }
1403 }
1404 return allowed ? Effect::Allow : Effect::Pass;
1405}
1406
11fdf7f2
TL
1407Effect Policy::eval_principal(const Environment& e,
1408 boost::optional<const rgw::auth::Identity&> ida) const {
1409 auto allowed = false;
1410 for (auto& s : statements) {
1411 auto g = s.eval_principal(e, ida);
1412 if (g == Effect::Deny) {
1413 return g;
1414 } else if (g == Effect::Allow) {
1415 allowed = true;
1416 }
1417 }
1418 return allowed ? Effect::Allow : Effect::Deny;
1419}
1420
1421Effect Policy::eval_conditions(const Environment& e) const {
1422 auto allowed = false;
1423 for (auto& s : statements) {
1424 auto g = s.eval_conditions(e);
1425 if (g == Effect::Deny) {
1426 return g;
1427 } else if (g == Effect::Allow) {
1428 allowed = true;
1429 }
1430 }
1431 return allowed ? Effect::Allow : Effect::Deny;
1432}
1433
31f18b77
FG
1434ostream& operator <<(ostream& m, const Policy& p) {
1435 m << "{ Version: "
1436 << (p.version == Version::v2008_10_17 ? "2008-10-17" : "2012-10-17");
1437
1438 if (p.id || !p.statements.empty()) {
1439 m << ", ";
1440 }
1441
1442 if (p.id) {
1443 m << "Id: " << *p.id;
1444 if (!p.statements.empty()) {
1445 m << ", ";
1446 }
1447 }
1448
1449 if (!p.statements.empty()) {
1450 m << "Statements: ";
1451 print_array(m, p.statements.cbegin(), p.statements.cend());
1452 m << ", ";
1453 }
1454 return m << " }";
1455}
1456
9f95a23c
TL
1457static const Environment iam_all_env = {
1458 {"aws:SourceIp","1.1.1.1"},
1459 {"aws:UserId","anonymous"},
1460 {"s3:x-amz-server-side-encryption-aws-kms-key-id","secret"}
1461};
1462
1463struct IsPublicStatement
1464{
1465 bool operator() (const Statement &s) const {
1466 if (s.effect == Effect::Allow) {
1467 for (const auto& p : s.princ) {
1468 if (p.is_wildcard()) {
1469 return s.eval_conditions(iam_all_env) == Effect::Allow;
1470 }
1471 }
1472 // no princ should not contain fixed values
1473 return std::none_of(s.noprinc.begin(), s.noprinc.end(), [](const rgw::auth::Principal& p) {
1474 return p.is_wildcard();
1475 });
1476 }
1477 return false;
1478 }
1479};
1480
1481
1482bool is_public(const Policy& p)
1483{
1484 return std::any_of(p.statements.begin(), p.statements.end(), IsPublicStatement());
31f18b77 1485}
9f95a23c
TL
1486
1487} // namespace IAM
1488} // namespace rgw