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