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