// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab ft=cpp #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common/ceph_time.h" #include "common/iso_8601.h" #include "rapidjson/error/error.h" #include "rapidjson/error/en.h" #include "rgw_acl.h" #include "rgw_basic_types.h" #include "rgw_iam_policy_keywords.h" #include "rgw_string.h" #include "rgw_arn.h" namespace rgw { namespace auth { class Identity; } } namespace rgw { namespace IAM { static constexpr std::uint64_t s3GetObject = 0; static constexpr std::uint64_t s3GetObjectVersion = 1; static constexpr std::uint64_t s3PutObject = 2; static constexpr std::uint64_t s3GetObjectAcl = 3; static constexpr std::uint64_t s3GetObjectVersionAcl = 4; static constexpr std::uint64_t s3PutObjectAcl = 5; static constexpr std::uint64_t s3PutObjectVersionAcl = 6; static constexpr std::uint64_t s3DeleteObject = 7; static constexpr std::uint64_t s3DeleteObjectVersion = 8; static constexpr std::uint64_t s3ListMultipartUploadParts = 9; static constexpr std::uint64_t s3AbortMultipartUpload = 10; static constexpr std::uint64_t s3GetObjectTorrent = 11; static constexpr std::uint64_t s3GetObjectVersionTorrent = 12; static constexpr std::uint64_t s3RestoreObject = 13; static constexpr std::uint64_t s3CreateBucket = 14; static constexpr std::uint64_t s3DeleteBucket = 15; static constexpr std::uint64_t s3ListBucket = 16; static constexpr std::uint64_t s3ListBucketVersions = 17; static constexpr std::uint64_t s3ListAllMyBuckets = 18; static constexpr std::uint64_t s3ListBucketMultipartUploads = 19; static constexpr std::uint64_t s3GetAccelerateConfiguration = 20; static constexpr std::uint64_t s3PutAccelerateConfiguration = 21; static constexpr std::uint64_t s3GetBucketAcl = 22; static constexpr std::uint64_t s3PutBucketAcl = 23; static constexpr std::uint64_t s3GetBucketCORS = 24; static constexpr std::uint64_t s3PutBucketCORS = 25; static constexpr std::uint64_t s3GetBucketVersioning = 26; static constexpr std::uint64_t s3PutBucketVersioning = 27; static constexpr std::uint64_t s3GetBucketRequestPayment = 28; static constexpr std::uint64_t s3PutBucketRequestPayment = 29; static constexpr std::uint64_t s3GetBucketLocation = 30; static constexpr std::uint64_t s3GetBucketPolicy = 31; static constexpr std::uint64_t s3DeleteBucketPolicy = 32; static constexpr std::uint64_t s3PutBucketPolicy = 33; static constexpr std::uint64_t s3GetBucketNotification = 34; static constexpr std::uint64_t s3PutBucketNotification = 35; static constexpr std::uint64_t s3GetBucketLogging = 36; static constexpr std::uint64_t s3PutBucketLogging = 37; static constexpr std::uint64_t s3GetBucketTagging = 38; static constexpr std::uint64_t s3PutBucketTagging = 39; static constexpr std::uint64_t s3GetBucketWebsite = 40; static constexpr std::uint64_t s3PutBucketWebsite = 41; static constexpr std::uint64_t s3DeleteBucketWebsite = 42; static constexpr std::uint64_t s3GetLifecycleConfiguration = 43; static constexpr std::uint64_t s3PutLifecycleConfiguration = 44; static constexpr std::uint64_t s3PutReplicationConfiguration = 45; static constexpr std::uint64_t s3GetReplicationConfiguration = 46; static constexpr std::uint64_t s3DeleteReplicationConfiguration = 47; static constexpr std::uint64_t s3GetObjectTagging = 48; static constexpr std::uint64_t s3PutObjectTagging = 49; static constexpr std::uint64_t s3DeleteObjectTagging = 50; static constexpr std::uint64_t s3GetObjectVersionTagging = 51; static constexpr std::uint64_t s3PutObjectVersionTagging = 52; static constexpr std::uint64_t s3DeleteObjectVersionTagging = 53; static constexpr std::uint64_t s3PutBucketObjectLockConfiguration = 54; static constexpr std::uint64_t s3GetBucketObjectLockConfiguration = 55; static constexpr std::uint64_t s3PutObjectRetention = 56; static constexpr std::uint64_t s3GetObjectRetention = 57; static constexpr std::uint64_t s3PutObjectLegalHold = 58; static constexpr std::uint64_t s3GetObjectLegalHold = 59; static constexpr std::uint64_t s3BypassGovernanceRetention = 60; static constexpr std::uint64_t s3GetBucketPolicyStatus = 61; static constexpr std::uint64_t s3PutPublicAccessBlock = 62; static constexpr std::uint64_t s3GetPublicAccessBlock = 63; static constexpr std::uint64_t s3DeletePublicAccessBlock = 64; static constexpr std::uint64_t s3GetBucketPublicAccessBlock = 65; static constexpr std::uint64_t s3PutBucketPublicAccessBlock = 66; static constexpr std::uint64_t s3DeleteBucketPublicAccessBlock = 67; static constexpr std::uint64_t s3GetBucketEncryption = 68; static constexpr std::uint64_t s3PutBucketEncryption = 69; static constexpr std::uint64_t s3All = 70; static constexpr std::uint64_t iamPutUserPolicy = s3All + 1; static constexpr std::uint64_t iamGetUserPolicy = s3All + 2; static constexpr std::uint64_t iamDeleteUserPolicy = s3All + 3; static constexpr std::uint64_t iamListUserPolicies = s3All + 4; static constexpr std::uint64_t iamCreateRole = s3All + 5; static constexpr std::uint64_t iamDeleteRole = s3All + 6; static constexpr std::uint64_t iamModifyRoleTrustPolicy = s3All + 7; static constexpr std::uint64_t iamGetRole = s3All + 8; static constexpr std::uint64_t iamListRoles = s3All + 9; static constexpr std::uint64_t iamPutRolePolicy = s3All + 10; static constexpr std::uint64_t iamGetRolePolicy = s3All + 11; static constexpr std::uint64_t iamListRolePolicies = s3All + 12; static constexpr std::uint64_t iamDeleteRolePolicy = s3All + 13; static constexpr std::uint64_t iamCreateOIDCProvider = s3All + 14; static constexpr std::uint64_t iamDeleteOIDCProvider = s3All + 15; static constexpr std::uint64_t iamGetOIDCProvider = s3All + 16; static constexpr std::uint64_t iamListOIDCProviders = s3All + 17; static constexpr std::uint64_t iamTagRole = s3All + 18; static constexpr std::uint64_t iamListRoleTags = s3All + 19; static constexpr std::uint64_t iamUntagRole = s3All + 20; static constexpr std::uint64_t iamUpdateRole = s3All + 21; static constexpr std::uint64_t iamAll = s3All + 22; static constexpr std::uint64_t stsAssumeRole = iamAll + 1; static constexpr std::uint64_t stsAssumeRoleWithWebIdentity = iamAll + 2; static constexpr std::uint64_t stsGetSessionToken = iamAll + 3; static constexpr std::uint64_t stsTagSession = iamAll + 4; static constexpr std::uint64_t stsAll = iamAll + 5; static constexpr std::uint64_t s3Count = s3All; static constexpr std::uint64_t allCount = stsAll + 1; using Action_t = std::bitset; using NotAction_t = Action_t; template constexpr std::bitset make_bitmask(size_t s) { // unfortunately none of the shift/logic operators of std::bitset have a constexpr variation return s < 64 ? std::bitset ((1ULL << s) - 1) : std::bitset((1ULL << 63) - 1) | make_bitmask (s - 63) << 63; } template constexpr std::bitset set_cont_bits(size_t start, size_t end) { return (make_bitmask(end - start)) << start; } static const Action_t None(0); static const Action_t s3AllValue = set_cont_bits(0,s3All); static const Action_t iamAllValue = set_cont_bits(s3All+1,iamAll); static const Action_t stsAllValue = set_cont_bits(iamAll+1,stsAll); static const Action_t allValue = set_cont_bits(0,allCount); namespace { // Please update the table in doc/radosgw/s3/authentication.rst if you // modify this function. inline int op_to_perm(std::uint64_t op) { switch (op) { case s3GetObject: case s3GetObjectTorrent: case s3GetObjectVersion: case s3GetObjectVersionTorrent: case s3GetObjectTagging: case s3GetObjectVersionTagging: case s3GetObjectRetention: case s3GetObjectLegalHold: case s3ListAllMyBuckets: case s3ListBucket: case s3ListBucketMultipartUploads: case s3ListBucketVersions: case s3ListMultipartUploadParts: return RGW_PERM_READ; case s3AbortMultipartUpload: case s3CreateBucket: case s3DeleteBucket: case s3DeleteObject: case s3DeleteObjectVersion: case s3PutObject: case s3PutObjectTagging: case s3PutObjectVersionTagging: case s3DeleteObjectTagging: case s3DeleteObjectVersionTagging: case s3RestoreObject: case s3PutObjectRetention: case s3PutObjectLegalHold: case s3BypassGovernanceRetention: return RGW_PERM_WRITE; case s3GetAccelerateConfiguration: case s3GetBucketAcl: case s3GetBucketCORS: case s3GetBucketEncryption: case s3GetBucketLocation: case s3GetBucketLogging: case s3GetBucketNotification: case s3GetBucketPolicy: case s3GetBucketPolicyStatus: case s3GetBucketRequestPayment: case s3GetBucketTagging: case s3GetBucketVersioning: case s3GetBucketWebsite: case s3GetLifecycleConfiguration: case s3GetObjectAcl: case s3GetObjectVersionAcl: case s3GetReplicationConfiguration: case s3GetBucketObjectLockConfiguration: case s3GetBucketPublicAccessBlock: return RGW_PERM_READ_ACP; case s3DeleteBucketPolicy: case s3DeleteBucketWebsite: case s3DeleteReplicationConfiguration: case s3PutAccelerateConfiguration: case s3PutBucketAcl: case s3PutBucketCORS: case s3PutBucketEncryption: case s3PutBucketLogging: case s3PutBucketNotification: case s3PutBucketPolicy: case s3PutBucketRequestPayment: case s3PutBucketTagging: case s3PutBucketVersioning: case s3PutBucketWebsite: case s3PutLifecycleConfiguration: case s3PutObjectAcl: case s3PutObjectVersionAcl: case s3PutReplicationConfiguration: case s3PutBucketObjectLockConfiguration: case s3PutBucketPublicAccessBlock: return RGW_PERM_WRITE_ACP; case s3All: return RGW_PERM_FULL_CONTROL; } return RGW_PERM_INVALID; } } enum class PolicyPrincipal { Role, Session, Other }; using Environment = std::unordered_multimap; using Address = std::bitset<128>; struct MaskedIP { bool v6; Address addr; // Since we're mapping IPv6 to IPv4 addresses, we may want to // consider making the prefix always be in terms of a v6 address // and just use the v6 bit to rewrite it as a v4 prefix for // output. unsigned int prefix; }; std::ostream& operator <<(std::ostream& m, const MaskedIP& ip); inline bool operator ==(const MaskedIP& l, const MaskedIP& r) { auto shift = std::max((l.v6 ? 128 : 32) - ((int) l.prefix), (r.v6 ? 128 : 32) - ((int) r.prefix)); ceph_assert(shift >= 0); return (l.addr >> shift) == (r.addr >> shift); } struct Condition { TokenID op; // Originally I was going to use a perfect hash table, but Marcus // says keys are to be added at run-time not compile time. // In future development, use symbol internment. std::string key; bool ifexists = false; bool isruntime = false; //Is evaluated during run-time // Much to my annoyance there is no actual way to do this in a // typed way that is compatible with AWS. I know this because I've // seen examples where the same value is used as a string in one // context and a date in another. std::vector vals; Condition() = default; Condition(TokenID op, const char* s, std::size_t len, bool ifexists) : op(op), key(s, len), ifexists(ifexists) {} bool eval(const Environment& e) const; static boost::optional as_number(const std::string& s) { std::size_t p = 0; try { double d = std::stod(s, &p); if (p < s.length()) { return boost::none; } return d; } catch (const std::logic_error& e) { return boost::none; } } static boost::optional as_date(const std::string& s) { std::size_t p = 0; try { double d = std::stod(s, &p); if (p == s.length()) { return ceph::real_time( std::chrono::seconds(static_cast(d)) + std::chrono::nanoseconds( static_cast((d - static_cast(d)) * 1000000000))); } return from_iso_8601(std::string_view(s), false); } catch (const std::logic_error& e) { return boost::none; } } static boost::optional as_bool(const std::string& s) { std::size_t p = 0; if (s.empty() || boost::iequals(s, "false")) { return false; } try { double d = std::stod(s, &p); if (p == s.length()) { return !((d == +0.0) || (d == -0.0) || std::isnan(d)); } } catch (const std::logic_error& e) { // Fallthrough } return true; } static boost::optional as_binary(const std::string& s) { // In a just world ceph::bufferlist base64; // I could populate a bufferlist base64.push_back(buffer::create_static( s.length(), const_cast(s.data()))); // Yuck // From a base64 encoded std::string. ceph::bufferlist bin; try { bin.decode_base64(base64); } catch (const ceph::buffer::malformed_input& e) { return boost::none; } return bin; } static boost::optional as_network(const std::string& s); struct ci_equal_to { bool operator ()(const std::string& s1, const std::string& s2) const { return boost::iequals(s1, s2); } }; struct string_like { bool operator ()(const std::string& input, const std::string& pattern) const { return match_wildcards(pattern, input, 0); } }; struct ci_starts_with { bool operator()(const std::string& s1, const std::string& s2) const { return boost::istarts_with(s1, s2); } }; using unordered_multimap_it_pair = std::pair ::const_iterator, std::unordered_multimap::const_iterator>; template static bool andible(F&& f, const unordered_multimap_it_pair& it, const std::vector& v) { for (auto itr = it.first; itr != it.second; itr++) { bool matched = false; for (const auto& d : v) { if (std::forward(f)(itr->second, d)) { matched = true; } } if (!matched) return false; } return true; } template static bool orrible(F&& f, const unordered_multimap_it_pair& it, const std::vector& v) { for (auto itr = it.first; itr != it.second; itr++) { for (const auto& d : v) { if (std::forward(f)(itr->second, d)) { return true; } } } return false; } template static bool shortible(F&& f, X& x, const std::string& c, const std::vector& v) { auto xc = std::forward(x)(c); if (!xc) { return false; } for (const auto& d : v) { auto xd = std::forward(x)(d); if (!xd) { continue; } if (std::forward(f)(*xc, *xd)) { return true; } } return false; } template bool has_key_p(const std::string& _key, F p) const { return p(key, _key); } template bool has_val_p(const std::string& _val, F p) const { for (auto val : vals) { if (p(val, _val)) return true; } return false; } }; std::ostream& operator <<(std::ostream& m, const Condition& c); struct Statement { boost::optional sid = boost::none; boost::container::flat_set princ; boost::container::flat_set noprinc; // Every statement MUST provide an effect. I just initialize it to // deny as defensive programming. Effect effect = Effect::Deny; Action_t action = 0; NotAction_t notaction = 0; boost::container::flat_set resource; boost::container::flat_set notresource; std::vector conditions; Effect eval(const Environment& e, boost::optional ida, std::uint64_t action, boost::optional resource, boost::optional princ_type=boost::none) const; Effect eval_principal(const Environment& e, boost::optional ida, boost::optional princ_type=boost::none) const; Effect eval_conditions(const Environment& e) const; }; std::ostream& operator <<(std::ostream& m, const Statement& s); struct PolicyParseException : public std::exception { rapidjson::ParseResult pr; std::string msg; explicit PolicyParseException(const rapidjson::ParseResult pr, const std::string& annotation) : pr(pr), msg(fmt::format("At character offset {}, {}", pr.Offset(), (pr.Code() == rapidjson::kParseErrorTermination ? annotation : rapidjson::GetParseError_En(pr.Code())))) {} const char* what() const noexcept override { return msg.c_str(); } }; struct Policy { std::string text; Version version = Version::v2008_10_17; boost::optional id = boost::none; std::vector statements; // reject_invalid_principals should be set to // `cct->_conf.get_val("rgw_policy_reject_invalid_principals")` // when executing operations that *set* a bucket policy, but should // be false when reading a stored bucket policy so as not to break // backwards configuration. Policy(CephContext* cct, const std::string& tenant, const bufferlist& text, bool reject_invalid_principals); Effect eval(const Environment& e, boost::optional ida, std::uint64_t action, boost::optional resource, boost::optional princ_type=boost::none) const; Effect eval_principal(const Environment& e, boost::optional ida, boost::optional princ_type=boost::none) const; Effect eval_conditions(const Environment& e) const; template bool has_conditional(const std::string& conditional, F p) const { for (const auto&s: statements){ if (std::any_of(s.conditions.begin(), s.conditions.end(), [&](const Condition& c) { return c.has_key_p(conditional, p);})) return true; } return false; } template bool has_conditional_value(const std::string& conditional, F p) const { for (const auto&s: statements){ if (std::any_of(s.conditions.begin(), s.conditions.end(), [&](const Condition& c) { return c.has_val_p(conditional, p);})) return true; } return false; } bool has_conditional(const std::string& c) const { return has_conditional(c, Condition::ci_equal_to()); } bool has_partial_conditional(const std::string& c) const { return has_conditional(c, Condition::ci_starts_with()); } // Example: ${s3:ResourceTag} bool has_partial_conditional_value(const std::string& c) const { return has_conditional_value(c, Condition::ci_starts_with()); } }; std::ostream& operator <<(std::ostream& m, const Policy& p); bool is_public(const Policy& p); } }