X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Frgw%2Frgw_iam_policy.cc;fp=src%2Fceph%2Fsrc%2Frgw%2Frgw_iam_policy.cc;h=0000000000000000000000000000000000000000;hb=7da45d65be36d36b880cc55c5036e96c24b53f00;hp=cff4fa493351373f482e5a40d8d22e1a82499929;hpb=691462d09d0987b47e112d6ee8740375df3c51b2;p=stor4nfv.git diff --git a/src/ceph/src/rgw/rgw_iam_policy.cc b/src/ceph/src/rgw/rgw_iam_policy.cc deleted file mode 100644 index cff4fa4..0000000 --- a/src/ceph/src/rgw/rgw_iam_policy.cc +++ /dev/null @@ -1,1599 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - - -#include -#include -#include -#include - -#include -#include -#include "rapidjson/reader.h" - -#include "common/backport14.h" -#include "rgw_auth.h" -#include -#include "rgw_iam_policy.h" - -namespace { -constexpr int dout_subsys = ceph_subsys_rgw; -} - -using std::bitset; -using std::find; -using std::int64_t; -using std::move; -using std::pair; -using std::size_t; -using std::string; -using std::stringstream; -using std::ostream; -using std::uint16_t; -using std::uint64_t; -using std::unordered_map; - -using boost::container::flat_set; -using boost::none; -using boost::optional; -using boost::regex; -using boost::regex_constants::ECMAScript; -using boost::regex_constants::optimize; -using boost::regex_match; -using boost::smatch; - -using rapidjson::BaseReaderHandler; -using rapidjson::UTF8; -using rapidjson::SizeType; -using rapidjson::Reader; -using rapidjson::kParseCommentsFlag; -using rapidjson::kParseNumbersAsStringsFlag; -using rapidjson::StringStream; -using rapidjson::ParseResult; - -using rgw::auth::Principal; - -namespace rgw { -namespace IAM { -#include "rgw_iam_policy_keywords.frag.cc" - -struct actpair { - const char* name; - const uint64_t bit; -}; - -namespace { -optional to_partition(const smatch::value_type& p, - bool wildcards) { - if (p == "aws") { - return Partition::aws; - } else if (p == "aws-cn") { - return Partition::aws_cn; - } else if (p == "aws-us-gov") { - return Partition::aws_us_gov; - } else if (p == "*" && wildcards) { - return Partition::wildcard; - } else { - return none; - } - - ceph_abort(); -} - -optional to_service(const smatch::value_type& s, - bool wildcards) { - static const unordered_map services = { - { "acm", Service::acm }, - { "apigateway", Service::apigateway }, - { "appstream", Service::appstream }, - { "artifact", Service::artifact }, - { "autoscaling", Service::autoscaling }, - { "aws-marketplace", Service::aws_marketplace }, - { "aws-marketplace-management", - Service::aws_marketplace_management }, - { "aws-portal", Service::aws_portal }, - { "cloudformation", Service::cloudformation }, - { "cloudfront", Service::cloudfront }, - { "cloudhsm", Service::cloudhsm }, - { "cloudsearch", Service::cloudsearch }, - { "cloudtrail", Service::cloudtrail }, - { "cloudwatch", Service::cloudwatch }, - { "codebuild", Service::codebuild }, - { "codecommit", Service::codecommit }, - { "codedeploy", Service::codedeploy }, - { "codepipeline", Service::codepipeline }, - { "cognito-identity", Service::cognito_identity }, - { "cognito-idp", Service::cognito_idp }, - { "cognito-sync", Service::cognito_sync }, - { "config", Service::config }, - { "datapipeline", Service::datapipeline }, - { "devicefarm", Service::devicefarm }, - { "directconnect", Service::directconnect }, - { "dms", Service::dms }, - { "ds", Service::ds }, - { "dynamodb", Service::dynamodb }, - { "ec2", Service::ec2 }, - { "ecr", Service::ecr }, - { "ecs", Service::ecs }, - { "elasticache", Service::elasticache }, - { "elasticbeanstalk", Service::elasticbeanstalk }, - { "elasticfilesystem", Service::elasticfilesystem }, - { "elasticloadbalancing", Service::elasticloadbalancing }, - { "elasticmapreduce", Service::elasticmapreduce }, - { "elastictranscoder", Service::elastictranscoder }, - { "es", Service::es }, - { "events", Service::events }, - { "firehose", Service::firehose }, - { "gamelift", Service::gamelift }, - { "glacier", Service::glacier }, - { "health", Service::health }, - { "iam", Service::iam }, - { "importexport", Service::importexport }, - { "inspector", Service::inspector }, - { "iot", Service::iot }, - { "kinesis", Service::kinesis }, - { "kinesisanalytics", Service::kinesisanalytics }, - { "kms", Service::kms }, - { "lambda", Service::lambda }, - { "lightsail", Service::lightsail }, - { "logs", Service::logs }, - { "machinelearning", Service::machinelearning }, - { "mobileanalytics", Service::mobileanalytics }, - { "mobilehub", Service::mobilehub }, - { "opsworks", Service::opsworks }, - { "opsworks-cm", Service::opsworks_cm }, - { "polly", Service::polly }, - { "rds", Service::rds }, - { "redshift", Service::redshift }, - { "route53", Service::route53 }, - { "route53domains", Service::route53domains }, - { "s3", Service::s3 }, - { "sdb", Service::sdb }, - { "servicecatalog", Service::servicecatalog }, - { "ses", Service::ses }, - { "sns", Service::sns }, - { "sqs", Service::sqs }, - { "ssm", Service::ssm }, - { "states", Service::states }, - { "storagegateway", Service::storagegateway }, - { "sts", Service::sts }, - { "support", Service::support }, - { "swf", Service::swf }, - { "trustedadvisor", Service::trustedadvisor }, - { "waf", Service::waf }, - { "workmail", Service::workmail }, - { "workspaces", Service::workspaces }}; - - if (wildcards && s == "*") { - return Service::wildcard; - } - - auto i = services.find(s); - if (i == services.end()) { - return none; - } else { - return i->second; - } -} -} - -ARN::ARN(const rgw_obj& o) - : partition(Partition::aws), - service(Service::s3), - region(), - account(o.bucket.tenant), - resource(o.bucket.name) -{ - resource.push_back('/'); - resource.append(o.key.name); -} - -ARN::ARN(const rgw_bucket& b) - : partition(Partition::aws), - service(Service::s3), - region(), - account(b.tenant), - resource(b.name) { } - -ARN::ARN(const rgw_bucket& b, const string& o) - : partition(Partition::aws), - service(Service::s3), - region(), - account(b.tenant), - resource(b.name) { - resource.push_back('/'); - resource.append(o); -} - -optional ARN::parse(const string& s, bool wildcards) { - static const char str_wild[] = "arn:([^:]*):([^:]*):([^:]*):([^:]*):([^:]*)"; - static const regex rx_wild(str_wild, - sizeof(str_wild) - 1, - ECMAScript | optimize); - static const char str_no_wild[] - = "arn:([^:*]*):([^:*]*):([^:*]*):([^:*]*):([^:*]*)"; - static const regex rx_no_wild(str_no_wild, - sizeof(str_no_wild) - 1, - ECMAScript | optimize); - - smatch match; - - if ((s == "*") && wildcards) { - return ARN(Partition::wildcard, Service::wildcard, "*", "*", "*"); - } else if (regex_match(s, match, wildcards ? rx_wild : rx_no_wild)) { - if (match.size() != 6) { - return boost::none; - } - - ARN a; - { - auto p = to_partition(match[1], wildcards); - if (!p) - return none; - - a.partition = *p; - } - { - auto s = to_service(match[2], wildcards); - if (!s) { - return none; - } - a.service = *s; - } - - a.region = match[3]; - a.account = match[4]; - a.resource = match[5]; - - return a; - } - return none; -} - -string ARN::to_string() const { - string s; - - if (partition == Partition::aws) { - s.append("aws:"); - } else if (partition == Partition::aws_cn) { - s.append("aws-cn:"); - } else if (partition == Partition::aws_us_gov) { - s.append("aws-us-gov:"); - } else { - s.append("*:"); - } - - static const unordered_map services = { - { Service::acm, "acm" }, - { Service::apigateway, "apigateway" }, - { Service::appstream, "appstream" }, - { Service::artifact, "artifact" }, - { Service::autoscaling, "autoscaling" }, - { Service::aws_marketplace, "aws-marketplace" }, - { Service::aws_marketplace_management, "aws-marketplace-management" }, - { Service::aws_portal, "aws-portal" }, - { Service::cloudformation, "cloudformation" }, - { Service::cloudfront, "cloudfront" }, - { Service::cloudhsm, "cloudhsm" }, - { Service::cloudsearch, "cloudsearch" }, - { Service::cloudtrail, "cloudtrail" }, - { Service::cloudwatch, "cloudwatch" }, - { Service::codebuild, "codebuild" }, - { Service::codecommit, "codecommit" }, - { Service::codedeploy, "codedeploy" }, - { Service::codepipeline, "codepipeline" }, - { Service::cognito_identity, "cognito-identity" }, - { Service::cognito_idp, "cognito-idp" }, - { Service::cognito_sync, "cognito-sync" }, - { Service::config, "config" }, - { Service::datapipeline, "datapipeline" }, - { Service::devicefarm, "devicefarm" }, - { Service::directconnect, "directconnect" }, - { Service::dms, "dms" }, - { Service::ds, "ds" }, - { Service::dynamodb, "dynamodb" }, - { Service::ec2, "ec2" }, - { Service::ecr, "ecr" }, - { Service::ecs, "ecs" }, - { Service::elasticache, "elasticache" }, - { Service::elasticbeanstalk, "elasticbeanstalk" }, - { Service::elasticfilesystem, "elasticfilesystem" }, - { Service::elasticloadbalancing, "elasticloadbalancing" }, - { Service::elasticmapreduce, "elasticmapreduce" }, - { Service::elastictranscoder, "elastictranscoder" }, - { Service::es, "es" }, - { Service::events, "events" }, - { Service::firehose, "firehose" }, - { Service::gamelift, "gamelift" }, - { Service::glacier, "glacier" }, - { Service::health, "health" }, - { Service::iam, "iam" }, - { Service::importexport, "importexport" }, - { Service::inspector, "inspector" }, - { Service::iot, "iot" }, - { Service::kinesis, "kinesis" }, - { Service::kinesisanalytics, "kinesisanalytics" }, - { Service::kms, "kms" }, - { Service::lambda, "lambda" }, - { Service::lightsail, "lightsail" }, - { Service::logs, "logs" }, - { Service::machinelearning, "machinelearning" }, - { Service::mobileanalytics, "mobileanalytics" }, - { Service::mobilehub, "mobilehub" }, - { Service::opsworks, "opsworks" }, - { Service::opsworks_cm, "opsworks-cm" }, - { Service::polly, "polly" }, - { Service::rds, "rds" }, - { Service::redshift, "redshift" }, - { Service::route53, "route53" }, - { Service::route53domains, "route53domains" }, - { Service::s3, "s3" }, - { Service::sdb, "sdb" }, - { Service::servicecatalog, "servicecatalog" }, - { Service::ses, "ses" }, - { Service::sns, "sns" }, - { Service::sqs, "sqs" }, - { Service::ssm, "ssm" }, - { Service::states, "states" }, - { Service::storagegateway, "storagegateway" }, - { Service::sts, "sts" }, - { Service::support, "support" }, - { Service::swf, "swf" }, - { Service::trustedadvisor, "trustedadvisor" }, - { Service::waf, "waf" }, - { Service::workmail, "workmail" }, - { Service::workspaces, "workspaces" }}; - - auto i = services.find(service); - if (i != services.end()) { - s.append(i->second); - } else { - s.push_back('*'); - } - s.push_back(':'); - - s.append(region); - s.push_back(':'); - - s.append(account); - s.push_back(':'); - - s.append(resource); - - return s; -} - -bool operator ==(const ARN& l, const ARN& r) { - return ((l.partition == r.partition) && - (l.service == r.service) && - (l.region == r.region) && - (l.account == r.account) && - (l.resource == r.resource)); -} -bool operator <(const ARN& l, const ARN& r) { - return ((l.partition < r.partition) || - (l.service < r.service) || - (l.region < r.region) || - (l.account < r.account) || - (l.resource < r.resource)); -} - -// The candidate is not allowed to have wildcards. The only way to -// do that sanely would be to use unification rather than matching. -bool ARN::match(const ARN& candidate) const { - if ((candidate.partition == Partition::wildcard) || - (partition != candidate.partition && partition - != Partition::wildcard)) { - return false; - } - - if ((candidate.service == Service::wildcard) || - (service != candidate.service && service != Service::wildcard)) { - return false; - } - - if (!match_policy(region, candidate.region, MATCH_POLICY_ARN)) { - return false; - } - - if (!match_policy(account, candidate.account, MATCH_POLICY_ARN)) { - return false; - } - - if (!match_policy(resource, candidate.resource, MATCH_POLICY_ARN)) { - return false; - } - - return true; -} - -static const actpair actpairs[] = -{{ "s3:AbortMultipartUpload", s3AbortMultipartUpload }, - { "s3:CreateBucket", s3CreateBucket }, - { "s3:DeleteBucketPolicy", s3DeleteBucketPolicy }, - { "s3:DeleteBucket", s3DeleteBucket }, - { "s3:DeleteBucketWebsite", s3DeleteBucketWebsite }, - { "s3:DeleteObject", s3DeleteObject }, - { "s3:DeleteObjectVersion", s3DeleteObjectVersion }, - { "s3:DeleteObjectTagging", s3DeleteObjectTagging }, - { "s3:DeleteObjectVersionTagging", s3DeleteObjectVersionTagging }, - { "s3:DeleteReplicationConfiguration", s3DeleteReplicationConfiguration }, - { "s3:GetAccelerateConfiguration", s3GetAccelerateConfiguration }, - { "s3:GetBucketAcl", s3GetBucketAcl }, - { "s3:GetBucketCORS", s3GetBucketCORS }, - { "s3:GetBucketLocation", s3GetBucketLocation }, - { "s3:GetBucketLogging", s3GetBucketLogging }, - { "s3:GetBucketNotification", s3GetBucketNotification }, - { "s3:GetBucketPolicy", s3GetBucketPolicy }, - { "s3:GetBucketRequestPayment", s3GetBucketRequestPayment }, - { "s3:GetBucketTagging", s3GetBucketTagging }, - { "s3:GetBucketVersioning", s3GetBucketVersioning }, - { "s3:GetBucketWebsite", s3GetBucketWebsite }, - { "s3:GetLifecycleConfiguration", s3GetLifecycleConfiguration }, - { "s3:GetObjectAcl", s3GetObjectAcl }, - { "s3:GetObject", s3GetObject }, - { "s3:GetObjectTorrent", s3GetObjectTorrent }, - { "s3:GetObjectVersionAcl", s3GetObjectVersionAcl }, - { "s3:GetObjectVersion", s3GetObjectVersion }, - { "s3:GetObjectVersionTorrent", s3GetObjectVersionTorrent }, - { "s3:GetObjectTagging", s3GetObjectTagging }, - { "s3:GetObjectVersionTagging", s3GetObjectVersionTagging}, - { "s3:GetReplicationConfiguration", s3GetReplicationConfiguration }, - { "s3:ListAllMyBuckets", s3ListAllMyBuckets }, - { "s3:ListBucketMultiPartUploads", s3ListBucketMultiPartUploads }, - { "s3:ListBucket", s3ListBucket }, - { "s3:ListBucketVersions", s3ListBucketVersions }, - { "s3:ListMultipartUploadParts", s3ListMultipartUploadParts }, - { "s3:PutAccelerateConfiguration", s3PutAccelerateConfiguration }, - { "s3:PutBucketAcl", s3PutBucketAcl }, - { "s3:PutBucketCORS", s3PutBucketCORS }, - { "s3:PutBucketLogging", s3PutBucketLogging }, - { "s3:PutBucketNotification", s3PutBucketNotification }, - { "s3:PutBucketPolicy", s3PutBucketPolicy }, - { "s3:PutBucketRequestPayment", s3PutBucketRequestPayment }, - { "s3:PutBucketTagging", s3PutBucketTagging }, - { "s3:PutBucketVersioning", s3PutBucketVersioning }, - { "s3:PutBucketWebsite", s3PutBucketWebsite }, - { "s3:PutLifecycleConfiguration", s3PutLifecycleConfiguration }, - { "s3:PutObjectAcl", s3PutObjectAcl }, - { "s3:PutObject", s3PutObject }, - { "s3:PutObjectVersionAcl", s3PutObjectVersionAcl }, - { "s3:PutObjectTagging", s3PutObjectTagging }, - { "s3:PutObjectVersionTagging", s3PutObjectVersionTagging }, - { "s3:PutReplicationConfiguration", s3PutReplicationConfiguration }, - { "s3:RestoreObject", s3RestoreObject }}; - -struct PolicyParser; - -const Keyword top[1]{"", TokenKind::pseudo, TokenID::Top, 0, false, - false}; -const Keyword cond_key[1]{"", TokenKind::cond_key, - TokenID::CondKey, 0, true, false}; - -struct ParseState { - PolicyParser* pp; - const Keyword* w; - - bool arraying = false; - bool objecting = false; - bool cond_ifexists = false; - - void reset(); - - ParseState(PolicyParser* pp, const Keyword* w) - : pp(pp), w(w) {} - - bool obj_start(); - - bool obj_end(); - - bool array_start() { - if (w->arrayable && !arraying) { - arraying = true; - return true; - } - return false; - } - - bool array_end(); - - bool key(const char* s, size_t l); - bool do_string(CephContext* cct, const char* s, size_t l); - bool number(const char* str, size_t l); -}; - -// If this confuses you, look up the Curiously Recurring Template Pattern -struct PolicyParser : public BaseReaderHandler, PolicyParser> { - keyword_hash tokens; - std::vector s; - CephContext* cct; - const string& tenant; - Policy& policy; - uint32_t v = 0; - - uint32_t seen = 0; - - uint32_t dex(TokenID in) const { - switch (in) { - case TokenID::Version: - return 0x1; - case TokenID::Id: - return 0x2; - case TokenID::Statement: - return 0x4; - case TokenID::Sid: - return 0x8; - case TokenID::Effect: - return 0x10; - case TokenID::Principal: - return 0x20; - case TokenID::NotPrincipal: - return 0x40; - case TokenID::Action: - return 0x80; - case TokenID::NotAction: - return 0x100; - case TokenID::Resource: - return 0x200; - case TokenID::NotResource: - return 0x400; - case TokenID::Condition: - return 0x800; - case TokenID::AWS: - return 0x1000; - case TokenID::Federated: - return 0x2000; - case TokenID::Service: - return 0x4000; - case TokenID::CanonicalUser: - return 0x8000; - default: - ceph_abort(); - } - } - bool test(TokenID in) { - return seen & dex(in); - } - void set(TokenID in) { - seen |= dex(in); - if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) | - dex(TokenID::Principal) | dex(TokenID::NotPrincipal) | - dex(TokenID::Action) | dex(TokenID::NotAction) | - dex(TokenID::Resource) | dex(TokenID::NotResource) | - dex(TokenID::Condition) | dex(TokenID::AWS) | - dex(TokenID::Federated) | dex(TokenID::Service) | - dex(TokenID::CanonicalUser))) { - v |= dex(in); - } - } - void set(std::initializer_list l) { - for (auto in : l) { - seen |= dex(in); - if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) | - dex(TokenID::Principal) | dex(TokenID::NotPrincipal) | - dex(TokenID::Action) | dex(TokenID::NotAction) | - dex(TokenID::Resource) | dex(TokenID::NotResource) | - dex(TokenID::Condition) | dex(TokenID::AWS) | - dex(TokenID::Federated) | dex(TokenID::Service) | - dex(TokenID::CanonicalUser))) { - v |= dex(in); - } - } - } - void reset(TokenID in) { - seen &= ~dex(in); - if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) | - dex(TokenID::Principal) | dex(TokenID::NotPrincipal) | - dex(TokenID::Action) | dex(TokenID::NotAction) | - dex(TokenID::Resource) | dex(TokenID::NotResource) | - dex(TokenID::Condition) | dex(TokenID::AWS) | - dex(TokenID::Federated) | dex(TokenID::Service) | - dex(TokenID::CanonicalUser))) { - v &= ~dex(in); - } - } - void reset(std::initializer_list l) { - for (auto in : l) { - seen &= ~dex(in); - if (dex(in) & (dex(TokenID::Sid) | dex(TokenID::Effect) | - dex(TokenID::Principal) | dex(TokenID::NotPrincipal) | - dex(TokenID::Action) | dex(TokenID::NotAction) | - dex(TokenID::Resource) | dex(TokenID::NotResource) | - dex(TokenID::Condition) | dex(TokenID::AWS) | - dex(TokenID::Federated) | dex(TokenID::Service) | - dex(TokenID::CanonicalUser))) { - v &= ~dex(in); - } - } - } - void reset(uint32_t& v) { - seen &= ~v; - v = 0; - } - - PolicyParser(CephContext* cct, const string& tenant, Policy& policy) - : cct(cct), tenant(tenant), policy(policy) {} - PolicyParser(const PolicyParser& policy) = delete; - - bool StartObject() { - if (s.empty()) { - s.push_back({this, top}); - s.back().objecting = true; - return true; - } - - return s.back().obj_start(); - } - bool EndObject(SizeType memberCount) { - if (s.empty()) { - return false; - } - return s.back().obj_end(); - } - bool Key(const char* str, SizeType length, bool copy) { - if (s.empty()) { - return false; - } - return s.back().key(str, length); - } - - bool String(const char* str, SizeType length, bool copy) { - if (s.empty()) { - return false; - } - return s.back().do_string(cct, str, length); - } - bool RawNumber(const char* str, SizeType length, bool copy) { - if (s.empty()) { - return false; - } - - return s.back().number(str, length); - } - bool StartArray() { - if (s.empty()) { - return false; - } - - return s.back().array_start(); - } - bool EndArray(SizeType) { - if (s.empty()) { - return false; - } - - return s.back().array_end(); - } - - bool Default() { - return false; - } -}; - - -// I really despise this misfeature of C++. -// -bool ParseState::obj_end() { - if (objecting) { - objecting = false; - if (!arraying) { - pp->s.pop_back(); - } else { - reset(); - } - return true; - } - return false; -} - -bool ParseState::key(const char* s, size_t l) { - auto token_len = l; - bool ifexists = false; - if (w->id == TokenID::Condition && w->kind == TokenKind::statement) { - static constexpr char IfExists[] = "IfExists"; - if (boost::algorithm::ends_with(boost::string_view{s, l}, IfExists)) { - ifexists = true; - token_len -= sizeof(IfExists)-1; - } - } - auto k = pp->tokens.lookup(s, token_len); - - if (!k) { - if (w->kind == TokenKind::cond_op) { - auto id = w->id; - auto& t = pp->policy.statements.back(); - auto c_ife = cond_ifexists; - pp->s.emplace_back(pp, cond_key); - t.conditions.emplace_back(id, s, l, c_ife); - return true; - } else { - return false; - } - } - - // If the token we're going with belongs within the condition at the - // top of the stack and we haven't already encountered it, push it - // on the stack - // Top - if ((((w->id == TokenID::Top) && (k->kind == TokenKind::top)) || - // Statement - ((w->id == TokenID::Statement) && (k->kind == TokenKind::statement)) || - - /// Principal - ((w->id == TokenID::Principal || w->id == TokenID::NotPrincipal) && - (k->kind == TokenKind::princ_type))) && - - // Check that it hasn't been encountered. Note that this - // conjoins with the run of disjunctions above. - !pp->test(k->id)) { - pp->set(k->id); - pp->s.emplace_back(pp, k); - return true; - } else if ((w->id == TokenID::Condition) && - (k->kind == TokenKind::cond_op)) { - pp->s.emplace_back(pp, k); - pp->s.back().cond_ifexists = ifexists; - return true; - } - return false; -} - -// I should just rewrite a few helper functions to use iterators, -// which will make all of this ever so much nicer. -static optional parse_principal(CephContext* cct, TokenID t, - string&& s) { - // Wildcard! - if ((t == TokenID::AWS) && (s == "*")) { - return Principal::wildcard(); - - // Do nothing for now. - } else if (t == TokenID::CanonicalUser) { - - // AWS ARNs - } else if (t == TokenID::AWS) { - auto a = ARN::parse(s); - if (!a) { - if (std::none_of(s.begin(), s.end(), - [](const char& c) { - return (c == ':') || (c == '/'); - })) { - // Since tenants are simply prefixes, there's no really good - // way to see if one exists or not. So we return the thing and - // let them try to match against it. - return Principal::tenant(std::move(s)); - } - } - - if (a->resource == "root") { - return Principal::tenant(std::move(a->account)); - } - - static const char rx_str[] = "([^/]*)/(.*)"; - static const regex rx(rx_str, sizeof(rx_str) - 1, - ECMAScript | optimize); - smatch match; - if (regex_match(a->resource, match, rx)) { - if (match.size() != 3) { - return boost::none; - } - - if (match[1] == "user") { - return Principal::user(std::move(a->account), - match[2]); - } - - if (match[1] == "role") { - return Principal::role(std::move(a->account), - match[2]); - } - } - } - - ldout(cct, 0) << "Supplied principal is discarded: " << s << dendl; - return boost::none; -} - -bool ParseState::do_string(CephContext* cct, const char* s, size_t l) { - auto k = pp->tokens.lookup(s, l); - Policy& p = pp->policy; - Statement* t = p.statements.empty() ? nullptr : &(p.statements.back()); - - // Top level! - if ((w->id == TokenID::Version) && k && - k->kind == TokenKind::version_key) { - p.version = static_cast(k->specific); - } else if (w->id == TokenID::Id) { - p.id = string(s, l); - - // Statement - - } else if (w->id == TokenID::Sid) { - t->sid.emplace(s, l); - } else if ((w->id == TokenID::Effect) && - k->kind == TokenKind::effect_key) { - t->effect = static_cast(k->specific); - } else if (w->id == TokenID::Principal && s && *s == '*') { - t->princ.emplace(Principal::wildcard()); - } else if (w->id == TokenID::NotPrincipal && s && *s == '*') { - t->noprinc.emplace(Principal::wildcard()); - } else if ((w->id == TokenID::Action) || - (w->id == TokenID::NotAction)) { - for (auto& p : actpairs) { - if (match_policy({s, l}, p.name, MATCH_POLICY_ACTION)) { - (w->id == TokenID::Action ? t->action : t->notaction) |= p.bit; - } - } - } else if (w->id == TokenID::Resource || w->id == TokenID::NotResource) { - auto a = ARN::parse({s, l}, true); - // You can't specify resources for someone ELSE'S account. - if (a && (a->account.empty() || a->account == pp->tenant || - a->account == "*")) { - if (a->account.empty() || a->account == "*") - a->account = pp->tenant; - (w->id == TokenID::Resource ? t->resource : t->notresource) - .emplace(std::move(*a)); - } - else - ldout(cct, 0) << "Supplied resource is discarded: " << string(s, l) - << dendl; - } else if (w->kind == TokenKind::cond_key) { - auto& t = pp->policy.statements.back(); - t.conditions.back().vals.emplace_back(s, l); - - // Principals - - } else if (w->kind == TokenKind::princ_type) { - if (pp->s.size() <= 1) { - return false; - } - auto& pri = pp->s[pp->s.size() - 2].w->id == TokenID::Principal ? - t->princ : t->noprinc; - - auto o = parse_principal(pp->cct, w->id, string(s, l)); - if (o) - pri.emplace(std::move(*o)); - - // Failure - - } else { - return false; - } - - if (!arraying) { - pp->s.pop_back(); - } - - return true; -} - -bool ParseState::number(const char* s, size_t l) { - // Top level! - if (w->kind == TokenKind::cond_key) { - auto& t = pp->policy.statements.back(); - t.conditions.back().vals.emplace_back(s, l); - - // Failure - - } else { - return false; - } - - if (!arraying) { - pp->s.pop_back(); - } - - return true; -} - -void ParseState::reset() { - pp->reset(pp->v); -} - -bool ParseState::obj_start() { - if (w->objectable && !objecting) { - objecting = true; - if (w->id == TokenID::Statement) { - pp->policy.statements.push_back({}); - } - - return true; - } - - return false; -} - - -bool ParseState::array_end() { - if (arraying && !objecting) { - pp->s.pop_back(); - return true; - } - - return false; -} - -ostream& operator <<(ostream& m, const MaskedIP& ip) { - // I have a theory about why std::bitset is the way it is. - if (ip.v6) { - for (int i = 15; i >= 0; --i) { - uint8_t b = 0; - for (int j = 7; j >= 0; --j) { - b |= (ip.addr[(i * 8) + j] << j); - } - m << hex << b; - if (i != 0) { - m << "::"; - } - } - } else { - // It involves Satan. - for (int i = 3; i >= 0; --i) { - uint8_t b = 0; - for (int j = 7; j >= 0; --j) { - b |= (ip.addr[(i * 8) + j] << j); - } - m << b; - if (i != 0) { - m << "."; - } - } - } - m << "/" << ip.prefix; - // It would explain a lot - return m; -} - -string to_string(const MaskedIP& m) { - stringstream ss; - ss << m; - return ss.str(); -} - -bool Condition::eval(const Environment& env) const { - auto i = env.find(key); - if (op == TokenID::Null) { - return i == env.end() ? true : false; - } - - if (i == env.end()) { - return ifexists; - } - const auto& s = i->second; - - switch (op) { - // String! - case TokenID::StringEquals: - return orrible(std::equal_to(), s, vals); - - case TokenID::StringNotEquals: - return orrible(ceph::not_fn(std::equal_to()), - s, vals); - - case TokenID::StringEqualsIgnoreCase: - return orrible(ci_equal_to(), s, vals); - - case TokenID::StringNotEqualsIgnoreCase: - return orrible(ceph::not_fn(ci_equal_to()), s, vals); - - case TokenID::StringLike: - return orrible(string_like(), s, vals); - - case TokenID::StringNotLike: - return orrible(ceph::not_fn(string_like()), s, vals); - - // Numeric - case TokenID::NumericEquals: - return shortible(std::equal_to(), as_number, s, vals); - - case TokenID::NumericNotEquals: - return shortible(ceph::not_fn(std::equal_to()), - as_number, s, vals); - - - case TokenID::NumericLessThan: - return shortible(std::less(), as_number, s, vals); - - - case TokenID::NumericLessThanEquals: - return shortible(std::less_equal(), as_number, s, vals); - - case TokenID::NumericGreaterThan: - return shortible(std::greater(), as_number, s, vals); - - case TokenID::NumericGreaterThanEquals: - return shortible(std::greater_equal(), as_number, s, vals); - - // Date! - case TokenID::DateEquals: - return shortible(std::equal_to(), as_date, s, vals); - - case TokenID::DateNotEquals: - return shortible(ceph::not_fn(std::equal_to()), - as_date, s, vals); - - case TokenID::DateLessThan: - return shortible(std::less(), as_date, s, vals); - - - case TokenID::DateLessThanEquals: - return shortible(std::less_equal(), as_date, s, vals); - - case TokenID::DateGreaterThan: - return shortible(std::greater(), as_date, s, vals); - - case TokenID::DateGreaterThanEquals: - return shortible(std::greater_equal(), as_date, s, - vals); - - // Bool! - case TokenID::Bool: - return shortible(std::equal_to(), as_bool, s, vals); - - // Binary! - case TokenID::BinaryEquals: - return shortible(std::equal_to(), as_binary, s, - vals); - - // IP Address! - case TokenID::IpAddress: - return shortible(std::equal_to(), as_network, s, vals); - - case TokenID::NotIpAddress: - return shortible(ceph::not_fn(std::equal_to()), as_network, s, - vals); - -#if 0 - // Amazon Resource Names! (Does S3 need this?) - TokenID::ArnEquals, TokenID::ArnNotEquals, TokenID::ArnLike, - TokenID::ArnNotLike, -#endif - - default: - return false; - } -} - -optional Condition::as_network(const string& s) { - MaskedIP m; - if (s.empty()) { - return none; - } - - m.v6 = s.find(':'); - auto slash = s.find('/'); - if (slash == string::npos) { - m.prefix = m.v6 ? 128 : 32; - } else { - char* end = 0; - m.prefix = strtoul(s.data() + slash + 1, &end, 10); - if (*end != 0 || (m.v6 && m.prefix > 128) || - (!m.v6 && m.prefix > 32)) { - return none; - } - } - - string t; - auto p = &s; - - if (slash != string::npos) { - t.assign(s, 0, slash); - p = &t; - } - - if (m.v6) { - struct sockaddr_in6 a; - if (inet_pton(AF_INET6, p->c_str(), static_cast(&a)) != 1) { - return none; - } - - m.addr |= Address(a.sin6_addr.s6_addr[0]) << 0; - m.addr |= Address(a.sin6_addr.s6_addr[1]) << 8; - m.addr |= Address(a.sin6_addr.s6_addr[2]) << 16; - m.addr |= Address(a.sin6_addr.s6_addr[3]) << 24; - m.addr |= Address(a.sin6_addr.s6_addr[4]) << 32; - m.addr |= Address(a.sin6_addr.s6_addr[5]) << 40; - m.addr |= Address(a.sin6_addr.s6_addr[6]) << 48; - m.addr |= Address(a.sin6_addr.s6_addr[7]) << 56; - m.addr |= Address(a.sin6_addr.s6_addr[8]) << 64; - m.addr |= Address(a.sin6_addr.s6_addr[9]) << 72; - m.addr |= Address(a.sin6_addr.s6_addr[10]) << 80; - m.addr |= Address(a.sin6_addr.s6_addr[11]) << 88; - m.addr |= Address(a.sin6_addr.s6_addr[12]) << 96; - m.addr |= Address(a.sin6_addr.s6_addr[13]) << 104; - m.addr |= Address(a.sin6_addr.s6_addr[14]) << 112; - m.addr |= Address(a.sin6_addr.s6_addr[15]) << 120; - } else { - struct sockaddr_in a; - if (inet_pton(AF_INET, p->c_str(), static_cast(&a)) != 1) { - return none; - } - m.addr = ntohl(a.sin_addr.s_addr); - } - - return none; -} - -namespace { -const char* condop_string(const TokenID t) { - switch (t) { - case TokenID::StringEquals: - return "StringEquals"; - - case TokenID::StringNotEquals: - return "StringNotEquals"; - - case TokenID::StringEqualsIgnoreCase: - return "StringEqualsIgnoreCase"; - - case TokenID::StringNotEqualsIgnoreCase: - return "StringNotEqualsIgnoreCase"; - - case TokenID::StringLike: - return "StringLike"; - - case TokenID::StringNotLike: - return "StringNotLike"; - - // Numeric! - case TokenID::NumericEquals: - return "NumericEquals"; - - case TokenID::NumericNotEquals: - return "NumericNotEquals"; - - case TokenID::NumericLessThan: - return "NumericLessThan"; - - case TokenID::NumericLessThanEquals: - return "NumericLessThanEquals"; - - case TokenID::NumericGreaterThan: - return "NumericGreaterThan"; - - case TokenID::NumericGreaterThanEquals: - return "NumericGreaterThanEquals"; - - case TokenID::DateEquals: - return "DateEquals"; - - case TokenID::DateNotEquals: - return "DateNotEquals"; - - case TokenID::DateLessThan: - return "DateLessThan"; - - case TokenID::DateLessThanEquals: - return "DateLessThanEquals"; - - case TokenID::DateGreaterThan: - return "DateGreaterThan"; - - case TokenID::DateGreaterThanEquals: - return "DateGreaterThanEquals"; - - case TokenID::Bool: - return "Bool"; - - case TokenID::BinaryEquals: - return "BinaryEquals"; - - case TokenID::IpAddress: - return "case TokenID::IpAddress"; - - case TokenID::NotIpAddress: - return "NotIpAddress"; - - case TokenID::ArnEquals: - return "ArnEquals"; - - case TokenID::ArnNotEquals: - return "ArnNotEquals"; - - case TokenID::ArnLike: - return "ArnLike"; - - case TokenID::ArnNotLike: - return "ArnNotLike"; - - case TokenID::Null: - return "Null"; - - default: - return "InvalidConditionOperator"; - } -} - -template -ostream& print_array(ostream& m, Iterator begin, Iterator end) { - if (begin == end) { - m << "["; - } else { - auto beforelast = end - 1; - m << "[ "; - for (auto i = begin; i != end; ++i) { - m << *i; - if (i != beforelast) { - m << ", "; - } else { - m << " "; - } - } - } - m << "]"; - return m; -} -} - -ostream& operator <<(ostream& m, const Condition& c) { - m << "{ " << condop_string(c.op); - if (c.ifexists) { - m << "IfExists"; - } - m << ": { " << c.key; - print_array(m, c.vals.cbegin(), c.vals.cend()); - return m << "}"; -} - -string to_string(const Condition& c) { - stringstream ss; - ss << c; - return ss.str(); -} - -Effect Statement::eval(const Environment& e, - optional ida, - uint64_t act, const ARN& res) const { - if (ida && (!ida->is_identity(princ) || ida->is_identity(noprinc))) { - return Effect::Pass; - } - - - if (!std::any_of(resource.begin(), resource.end(), - [&res](const ARN& pattern) { - return pattern.match(res); - }) || - (std::any_of(notresource.begin(), notresource.end(), - [&res](const ARN& pattern) { - return pattern.match(res); - }))) { - return Effect::Pass; - } - - if (!(action & act) || (notaction & act)) { - return Effect::Pass; - } - - if (std::all_of(conditions.begin(), - conditions.end(), - [&e](const Condition& c) { return c.eval(e);})) { - return effect; - } - - return Effect::Pass; -} - -namespace { -const char* action_bit_string(uint64_t action) { - switch (action) { - case s3GetObject: - return "s3:GetObject"; - - case s3GetObjectVersion: - return "s3:GetObjectVersion"; - - case s3PutObject: - return "s3:PutObject"; - - case s3GetObjectAcl: - return "s3:GetObjectAcl"; - - case s3GetObjectVersionAcl: - return "s3:GetObjectVersionAcl"; - - case s3PutObjectAcl: - return "s3:PutObjectAcl"; - - case s3PutObjectVersionAcl: - return "s3:PutObjectVersionAcl"; - - case s3DeleteObject: - return "s3:DeleteObject"; - - case s3DeleteObjectVersion: - return "s3:DeleteObjectVersion"; - - case s3ListMultipartUploadParts: - return "s3:ListMultipartUploadParts"; - - case s3AbortMultipartUpload: - return "s3:AbortMultipartUpload"; - - case s3GetObjectTorrent: - return "s3:GetObjectTorrent"; - - case s3GetObjectVersionTorrent: - return "s3:GetObjectVersionTorrent"; - - case s3RestoreObject: - return "s3:RestoreObject"; - - case s3CreateBucket: - return "s3:CreateBucket"; - - case s3DeleteBucket: - return "s3:DeleteBucket"; - - case s3ListBucket: - return "s3:ListBucket"; - - case s3ListBucketVersions: - return "s3:ListBucketVersions"; - case s3ListAllMyBuckets: - return "s3:ListAllMyBuckets"; - - case s3ListBucketMultiPartUploads: - return "s3:ListBucketMultiPartUploads"; - - case s3GetAccelerateConfiguration: - return "s3:GetAccelerateConfiguration"; - - case s3PutAccelerateConfiguration: - return "s3:PutAccelerateConfiguration"; - - case s3GetBucketAcl: - return "s3:GetBucketAcl"; - - case s3PutBucketAcl: - return "s3:PutBucketAcl"; - - case s3GetBucketCORS: - return "s3:GetBucketCORS"; - - case s3PutBucketCORS: - return "s3:PutBucketCORS"; - - case s3GetBucketVersioning: - return "s3:GetBucketVersioning"; - - case s3PutBucketVersioning: - return "s3:PutBucketVersioning"; - - case s3GetBucketRequestPayment: - return "s3:GetBucketRequestPayment"; - - case s3PutBucketRequestPayment: - return "s3:PutBucketRequestPayment"; - - case s3GetBucketLocation: - return "s3:GetBucketLocation"; - - case s3GetBucketPolicy: - return "s3:GetBucketPolicy"; - - case s3DeleteBucketPolicy: - return "s3:DeleteBucketPolicy"; - - case s3PutBucketPolicy: - return "s3:PutBucketPolicy"; - - case s3GetBucketNotification: - return "s3:GetBucketNotification"; - - case s3PutBucketNotification: - return "s3:PutBucketNotification"; - - case s3GetBucketLogging: - return "s3:GetBucketLogging"; - - case s3PutBucketLogging: - return "s3:PutBucketLogging"; - - case s3GetBucketTagging: - return "s3:GetBucketTagging"; - - case s3PutBucketTagging: - return "s3:PutBucketTagging"; - - case s3GetBucketWebsite: - return "s3:GetBucketWebsite"; - - case s3PutBucketWebsite: - return "s3:PutBucketWebsite"; - - case s3DeleteBucketWebsite: - return "s3:DeleteBucketWebsite"; - - case s3GetLifecycleConfiguration: - return "s3:GetLifecycleConfiguration"; - - case s3PutLifecycleConfiguration: - return "s3:PutLifecycleConfiguration"; - - case s3PutReplicationConfiguration: - return "s3:PutReplicationConfiguration"; - - case s3GetReplicationConfiguration: - return "s3:GetReplicationConfiguration"; - - case s3DeleteReplicationConfiguration: - return "s3:DeleteReplicationConfiguration"; - - case s3PutObjectTagging: - return "s3:PutObjectTagging"; - - case s3PutObjectVersionTagging: - return "s3:PutObjectVersionTagging"; - - case s3GetObjectTagging: - return "s3:GetObjectTagging"; - - case s3GetObjectVersionTagging: - return "s3:GetObjectVersionTagging"; - - case s3DeleteObjectTagging: - return "s3:DeleteObjectTagging"; - - case s3DeleteObjectVersionTagging: - return "s3:DeleteObjectVersionTagging"; - } - return "s3Invalid"; -} - -ostream& print_actions(ostream& m, const uint64_t a) { - bool begun = false; - m << "[ "; - for (auto i = 0U; i < s3Count; ++i) { - if (a & (1 << i)) { - if (begun) { - m << ", "; - } else { - begun = true; - } - m << action_bit_string(1 << i); - } - } - if (begun) { - m << " ]"; - } else { - m << "]"; - } - return m; -} -} - -ostream& operator <<(ostream& m, const Statement& s) { - m << "{ "; - if (s.sid) { - m << "Sid: " << *s.sid << ", "; - } - if (!s.princ.empty()) { - m << "Principal: "; - print_array(m, s.princ.cbegin(), s.princ.cend()); - m << ", "; - } - if (!s.noprinc.empty()) { - m << "NotPrincipal: "; - print_array(m, s.noprinc.cbegin(), s.noprinc.cend()); - m << ", "; - } - - m << "Effect: " << - (s.effect == Effect::Allow ? - (const char*) "Allow" : - (const char*) "Deny"); - - if (s.action || s.notaction || !s.resource.empty() || - !s.notresource.empty() || !s.conditions.empty()) { - m << ", "; - } - - if (s.action) { - m << "Action: "; - print_actions(m, s.action); - - if (s.notaction || !s.resource.empty() || - !s.notresource.empty() || !s.conditions.empty()) { - m << ", "; - } - } - - if (s.notaction) { - m << "NotAction: "; - print_actions(m, s.notaction); - - if (!s.resource.empty() || !s.notresource.empty() || - !s.conditions.empty()) { - m << ", "; - } - } - - if (!s.resource.empty()) { - m << "Resource: "; - print_array(m, s.resource.cbegin(), s.resource.cend()); - - if (!s.notresource.empty() || !s.conditions.empty()) { - m << ", "; - } - } - - if (!s.notresource.empty()) { - m << "NotResource: "; - print_array(m, s.notresource.cbegin(), s.notresource.cend()); - - if (!s.conditions.empty()) { - m << ", "; - } - } - - if (!s.conditions.empty()) { - m << "Condition: "; - print_array(m, s.conditions.cbegin(), s.conditions.cend()); - } - - return m << " }"; -} - -string to_string(const Statement& s) { - stringstream m; - m << s; - return m.str(); -} - -Policy::Policy(CephContext* cct, const string& tenant, - const bufferlist& _text) - : text(_text.to_str()) { - StringStream ss(text.data()); - PolicyParser pp(cct, tenant, *this); - auto pr = Reader{}.Parse(ss, pp); - if (!pr) { - throw PolicyParseException(std::move(pr)); - } -} - -Effect Policy::eval(const Environment& e, - optional ida, - std::uint64_t action, const ARN& resource) const { - auto allowed = false; - for (auto& s : statements) { - auto g = s.eval(e, ida, action, resource); - if (g == Effect::Deny) { - return g; - } else if (g == Effect::Allow) { - allowed = true; - } - } - return allowed ? Effect::Allow : Effect::Pass; -} - -ostream& operator <<(ostream& m, const Policy& p) { - m << "{ Version: " - << (p.version == Version::v2008_10_17 ? "2008-10-17" : "2012-10-17"); - - if (p.id || !p.statements.empty()) { - m << ", "; - } - - if (p.id) { - m << "Id: " << *p.id; - if (!p.statements.empty()) { - m << ", "; - } - } - - if (!p.statements.empty()) { - m << "Statements: "; - print_array(m, p.statements.cbegin(), p.statements.cend()); - m << ", "; - } - return m << " }"; -} - -string to_string(const Policy& p) { - stringstream s; - s << p; - return s.str(); -} - -} -}