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