From: Pritha Srivastava Date: Thu, 27 May 2021 13:36:44 +0000 (+0530) Subject: rgw/sts: correcting the evaluation of session policies X-Git-Tag: v16.2.6~50^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=e9d541cb459e40697928c5ca7b65793d277298ec;p=ceph.git rgw/sts: correcting the evaluation of session policies passed in with AssumeRoleWithWebIdentity. Session Policies are used to restrict the permissions granted by identity-based (Role's permission policy and resource-policy (bucket policy) in some cases. Fixes: https://tracker.ceph.com/issues/51019 Signed-off-by: Pritha Srivastava (cherry picked from commit bd611d451aaaba65c6c1b7a91ec4486142c90522) Conflicts: src/rgw/rgw_op.cc Cherry-pick notes: - conflict due to rgw::sal::RGWObject renaming to rgw::sal::Object after Pacific --- diff --git a/src/rgw/rgw_auth.cc b/src/rgw/rgw_auth.cc index 9d0d3ea42b33..ce613f40d1be 100644 --- a/src/rgw/rgw_auth.cc +++ b/src/rgw/rgw_auth.cc @@ -804,15 +804,17 @@ void rgw::auth::RoleApplier::modify_request_state(const DoutPrefixProvider *dpp, } } - try { - string policy = this->token_policy; - bufferlist bl = bufferlist::static_from_string(policy); - const rgw::IAM::Policy p(s->cct, role.tenant, bl); - s->iam_user_policies.push_back(std::move(p)); - } catch (rgw::IAM::PolicyParseException& e) { - //Control shouldn't reach here as the policy has already been - //verified earlier - ldpp_dout(dpp, 20) << "failed to parse token policy: " << e.what() << dendl; + if (!this->token_policy.empty()) { + try { + string policy = this->token_policy; + bufferlist bl = bufferlist::static_from_string(policy); + const rgw::IAM::Policy p(s->cct, role.tenant, bl); + s->session_policies.push_back(std::move(p)); + } catch (rgw::IAM::PolicyParseException& e) { + //Control shouldn't reach here as the policy has already been + //verified earlier + ldpp_dout(dpp, 20) << "failed to parse token policy: " << e.what() << dendl; + } } string condition = "aws:userid"; diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc index 57765bbfe922..bbd8ab6f70b9 100644 --- a/src/rgw/rgw_common.cc +++ b/src/rgw/rgw_common.cc @@ -1048,30 +1048,31 @@ Effect eval_or_pass(const boost::optional& policy, const rgw::IAM::Environment& env, boost::optional id, const uint64_t op, - const ARN& arn) { + const ARN& arn, + boost::optional princ_type=boost::none) { if (!policy) return Effect::Pass; else - return policy->eval(env, id, op, arn); + return policy->eval(env, id, op, arn, princ_type); } } -Effect eval_user_policies(const vector& user_policies, +Effect eval_identity_or_session_policies(const vector& policies, const rgw::IAM::Environment& env, boost::optional id, const uint64_t op, const ARN& arn) { - auto usr_policy_res = Effect::Pass, prev_res = Effect::Pass; - for (auto& user_policy : user_policies) { - if (usr_policy_res = eval_or_pass(user_policy, env, id, op, arn); usr_policy_res == Effect::Deny) - return usr_policy_res; - else if (usr_policy_res == Effect::Allow) + auto policy_res = Effect::Pass, prev_res = Effect::Pass; + for (auto& policy : policies) { + if (policy_res = eval_or_pass(policy, env, id, op, arn); policy_res == Effect::Deny) + return policy_res; + else if (policy_res == Effect::Allow) prev_res = Effect::Allow; - else if (usr_policy_res == Effect::Pass && prev_res == Effect::Allow) - usr_policy_res = Effect::Allow; + else if (policy_res == Effect::Pass && prev_res == Effect::Allow) + policy_res = Effect::Allow; } - return usr_policy_res; + return policy_res; } bool verify_user_permission(const DoutPrefixProvider* dpp, @@ -1081,7 +1082,7 @@ bool verify_user_permission(const DoutPrefixProvider* dpp, const rgw::ARN& res, const uint64_t op) { - auto usr_policy_res = eval_user_policies(user_policies, s->env, boost::none, op, res); + auto usr_policy_res = eval_identity_or_session_policies(user_policies, s->env, boost::none, op, res); if (usr_policy_res == Effect::Deny) { return false; } @@ -1160,26 +1161,49 @@ bool verify_bucket_permission(const DoutPrefixProvider* dpp, RGWAccessControlPolicy * const user_acl, RGWAccessControlPolicy * const bucket_acl, const boost::optional& bucket_policy, - const vector& user_policies, + const vector& identity_policies, + const vector& session_policies, const uint64_t op) { if (!verify_requester_payer_permission(s)) return false; - auto usr_policy_res = eval_user_policies(user_policies, s->env, boost::none, op, ARN(bucket)); - if (usr_policy_res == Effect::Deny) + auto identity_policy_res = eval_identity_or_session_policies(identity_policies, s->env, boost::none, op, ARN(bucket)); + if (identity_policy_res == Effect::Deny) return false; + rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other; auto r = eval_or_pass(bucket_policy, s->env, *s->identity, - op, ARN(bucket)); - if (r == Effect::Allow) + op, ARN(bucket), princ_type); + if (r == Effect::Deny) + return false; + + //Take into account session policies, if the identity making a request is a role + if (!session_policies.empty()) { + auto session_policy_res = eval_identity_or_session_policies(session_policies, s->env, boost::none, op, ARN(bucket)); + if (session_policy_res == Effect::Deny) { + return false; + } + if (princ_type == rgw::IAM::PolicyPrincipal::Role) { + //Intersection of session policy and identity policy plus intersection of session policy and bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || + (session_policy_res == Effect::Allow && r == Effect::Allow)) + return true; + } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) { + //Intersection of session policy and identity policy plus bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || r == Effect::Allow) + return true; + } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy + if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) + return true; + } + return false; + } + + if (r == Effect::Allow || identity_policy_res == Effect::Allow) // It looks like S3 ACLs only GRANT permissions rather than // denying them, so this should be safe. return true; - else if (r == Effect::Deny) - return false; - else if (usr_policy_res == Effect::Allow) // r is Effect::Pass at this point - return true; const auto perm = op_to_perm(op); @@ -1193,13 +1217,14 @@ bool verify_bucket_permission(const DoutPrefixProvider* dpp, RGWAccessControlPolicy * const bucket_acl, const boost::optional& bucket_policy, const vector& user_policies, + const vector& session_policies, const uint64_t op) { perm_state_from_req_state ps(s); return verify_bucket_permission(dpp, &ps, bucket, user_acl, bucket_acl, bucket_policy, user_policies, - op); + session_policies, op); } bool verify_bucket_permission_no_policy(const DoutPrefixProvider* dpp, struct perm_state_base * const s, @@ -1263,31 +1288,54 @@ bool verify_bucket_permission(const DoutPrefixProvider* dpp, struct req_state * s->bucket_acl.get(), s->iam_policy, s->iam_user_policies, + s->session_policies, op); } -// Authorize anyone permitted by the policy and the bucket owner +// Authorize anyone permitted by the bucket policy, identity policies, session policies and the bucket owner // unless explicitly denied by the policy. int verify_bucket_owner_or_policy(struct req_state* const s, const uint64_t op) { - auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env, boost::none, op, ARN(s->bucket->get_key())); - if (usr_policy_res == Effect::Deny) { + auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env, boost::none, op, ARN(s->bucket->get_key())); + if (identity_policy_res == Effect::Deny) { return -EACCES; } + rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other; auto e = eval_or_pass(s->iam_policy, s->env, *s->auth.identity, - op, ARN(s->bucket->get_key())); + op, ARN(s->bucket->get_key()), princ_type); if (e == Effect::Deny) { return -EACCES; } + if (!s->session_policies.empty()) { + auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env, boost::none, op, ARN(s->bucket->get_key())); + if (session_policy_res == Effect::Deny) { + return -EACCES; + } + if (princ_type == rgw::IAM::PolicyPrincipal::Role) { + //Intersection of session policy and identity policy plus intersection of session policy and bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || + (session_policy_res == Effect::Allow && e == Effect::Allow)) + return 0; + } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) { + //Intersection of session policy and identity policy plus bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) + return 0; + } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy + if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) + return 0; + } + return -EACCES; + } + if (e == Effect::Allow || - usr_policy_res == Effect::Allow || + identity_policy_res == Effect::Allow || (e == Effect::Pass && - usr_policy_res == Effect::Pass && + identity_policy_res == Effect::Pass && s->auth.identity->is_owner_of(s->bucket_owner.get_id()))) { return 0; } else { @@ -1302,12 +1350,13 @@ static inline bool check_deferred_bucket_perms(const DoutPrefixProvider* dpp, RGWAccessControlPolicy * const user_acl, RGWAccessControlPolicy * const bucket_acl, const boost::optional& bucket_policy, - const vector& user_policies, + const vector& identity_policies, + const vector& session_policies, const uint8_t deferred_check, const uint64_t op) { return (s->defer_to_bucket_acls == deferred_check \ - && verify_bucket_permission(dpp, s, bucket, user_acl, bucket_acl, bucket_policy, user_policies,op)); + && verify_bucket_permission(dpp, s, bucket, user_acl, bucket_acl, bucket_policy, identity_policies, session_policies,op)); } static inline bool check_deferred_bucket_only_acl(const DoutPrefixProvider* dpp, @@ -1327,32 +1376,54 @@ bool verify_object_permission(const DoutPrefixProvider* dpp, struct perm_state_b RGWAccessControlPolicy * const bucket_acl, RGWAccessControlPolicy * const object_acl, const boost::optional& bucket_policy, - const vector& user_policies, + const vector& identity_policies, + const vector& session_policies, const uint64_t op) { if (!verify_requester_payer_permission(s)) return false; - auto usr_policy_res = eval_user_policies(user_policies, s->env, boost::none, op, ARN(obj)); - if (usr_policy_res == Effect::Deny) + auto identity_policy_res = eval_identity_or_session_policies(identity_policies, s->env, boost::none, op, ARN(obj)); + if (identity_policy_res == Effect::Deny) return false; - auto r = eval_or_pass(bucket_policy, s->env, *s->identity, op, ARN(obj)); - if (r == Effect::Allow) + rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other; + auto r = eval_or_pass(bucket_policy, s->env, *s->identity, op, ARN(obj), princ_type); + if (r == Effect::Deny) + return false; + + if (!session_policies.empty()) { + auto session_policy_res = eval_identity_or_session_policies(session_policies, s->env, boost::none, op, ARN(obj)); + if (session_policy_res == Effect::Deny) { + return false; + } + if (princ_type == rgw::IAM::PolicyPrincipal::Role) { + //Intersection of session policy and identity policy plus intersection of session policy and bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || + (session_policy_res == Effect::Allow && r == Effect::Allow)) + return true; + } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) { + //Intersection of session policy and identity policy plus bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || r == Effect::Allow) + return true; + } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy + if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) + return true; + } + return false; + } + + if (r == Effect::Allow || identity_policy_res == Effect::Allow) // It looks like S3 ACLs only GRANT permissions rather than // denying them, so this should be safe. return true; - else if (r == Effect::Deny) - return false; - else if (usr_policy_res == Effect::Allow) - return true; const auto perm = op_to_perm(op); if (check_deferred_bucket_perms(dpp, s, obj.bucket, user_acl, bucket_acl, bucket_policy, - user_policies, RGW_DEFER_TO_BUCKET_ACLS_RECURSE, op) || + identity_policies, session_policies, RGW_DEFER_TO_BUCKET_ACLS_RECURSE, op) || check_deferred_bucket_perms(dpp, s, obj.bucket, user_acl, bucket_acl, bucket_policy, - user_policies, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, rgw::IAM::s3All)) { + identity_policies, session_policies, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, rgw::IAM::s3All)) { return true; } @@ -1401,14 +1472,15 @@ bool verify_object_permission(const DoutPrefixProvider* dpp, struct req_state * RGWAccessControlPolicy * const bucket_acl, RGWAccessControlPolicy * const object_acl, const boost::optional& bucket_policy, - const vector& user_policies, + const vector& identity_policies, + const vector& session_policies, const uint64_t op) { perm_state_from_req_state ps(s); return verify_object_permission(dpp, &ps, obj, user_acl, bucket_acl, object_acl, bucket_policy, - user_policies, op); + identity_policies, session_policies, op); } bool verify_object_permission_no_policy(const DoutPrefixProvider* dpp, @@ -1489,6 +1561,7 @@ bool verify_object_permission(const DoutPrefixProvider* dpp, struct req_state *s s->object_acl.get(), s->iam_policy, s->iam_user_policies, + s->session_policies, op); } diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h index 7b20466775d2..41413b077bb3 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -1620,6 +1620,8 @@ struct req_state : DoutPrefixProvider { //token claims from STS token for ops log (can be used for Keystone token also) std::vector token_claims; + vector session_policies; + req_state(CephContext* _cct, RGWEnv* e, uint64_t id); ~req_state(); @@ -2085,7 +2087,7 @@ bool verify_object_permission_no_policy(const DoutPrefixProvider* dpp, /** Check if the req_state's user has the necessary permissions * to do the requested action */ -rgw::IAM::Effect eval_user_policies(const vector& user_policies, +rgw::IAM::Effect eval_identity_or_session_policies(const vector& user_policies, const rgw::IAM::Environment& env, boost::optional id, const uint64_t op, @@ -2114,7 +2116,8 @@ bool verify_bucket_permission( RGWAccessControlPolicy * const user_acl, RGWAccessControlPolicy * const bucket_acl, const boost::optional& bucket_policy, - const vector& user_policies, + const vector& identity_policies, + const vector& session_policies, const uint64_t op); bool verify_bucket_permission(const DoutPrefixProvider* dpp, struct req_state * const s, const uint64_t op); bool verify_bucket_permission_no_policy( @@ -2136,7 +2139,8 @@ extern bool verify_object_permission( RGWAccessControlPolicy * const bucket_acl, RGWAccessControlPolicy * const object_acl, const boost::optional& bucket_policy, - const vector& user_policies, + const vector& identity_policies, + const vector& session_policies, const uint64_t op); extern bool verify_object_permission(const DoutPrefixProvider* dpp, struct req_state *s, uint64_t op); extern bool verify_object_permission_no_policy( diff --git a/src/rgw/rgw_iam_policy.cc b/src/rgw/rgw_iam_policy.cc index 2265334a76f4..48780c2efbb0 100644 --- a/src/rgw/rgw_iam_policy.cc +++ b/src/rgw/rgw_iam_policy.cc @@ -976,9 +976,9 @@ ostream& operator <<(ostream& m, const Condition& c) { Effect Statement::eval(const Environment& e, boost::optional ida, - uint64_t act, const ARN& res) const { + uint64_t act, const ARN& res, boost::optional princ_type) const { - if (eval_principal(e, ida) == Effect::Deny) { + if (eval_principal(e, ida, princ_type) == Effect::Deny) { return Effect::Pass; } @@ -1012,13 +1012,34 @@ Effect Statement::eval(const Environment& e, } Effect Statement::eval_principal(const Environment& e, - boost::optional ida) const { + boost::optional ida, boost::optional princ_type) const { + if (princ_type) { + *princ_type = PolicyPrincipal::Other; + } if (ida) { if (princ.empty() && noprinc.empty()) { return Effect::Deny; } - if (!princ.empty() && !ida->is_identity(princ)) { + if (ida->get_identity_type() != TYPE_ROLE && !princ.empty() && !ida->is_identity(princ)) { return Effect::Deny; + } + if (ida->get_identity_type() == TYPE_ROLE && !princ.empty()) { + bool princ_matched = false; + for (auto p : princ) { // Check each principal to determine the type of the one that has matched + boost::container::flat_set id; + id.insert(p); + if (ida->is_identity(id)) { + if (p.is_assumed_role() || p.is_user()) { + if (princ_type) *princ_type = PolicyPrincipal::Session; + } else { + if (princ_type) *princ_type = PolicyPrincipal::Role; + } + princ_matched = true; + } + } + if (!princ_matched) { + return Effect::Deny; + } } else if (!noprinc.empty() && ida->is_identity(noprinc)) { return Effect::Deny; } @@ -1391,10 +1412,11 @@ Policy::Policy(CephContext* cct, const string& tenant, Effect Policy::eval(const Environment& e, boost::optional ida, - std::uint64_t action, const ARN& resource) const { + std::uint64_t action, const ARN& resource, + boost::optional princ_type) const { auto allowed = false; for (auto& s : statements) { - auto g = s.eval(e, ida, action, resource); + auto g = s.eval(e, ida, action, resource, princ_type); if (g == Effect::Deny) { return g; } else if (g == Effect::Allow) { @@ -1405,10 +1427,10 @@ Effect Policy::eval(const Environment& e, } Effect Policy::eval_principal(const Environment& e, - boost::optional ida) const { + boost::optional ida, boost::optional princ_type) const { auto allowed = false; for (auto& s : statements) { - auto g = s.eval_principal(e, ida); + auto g = s.eval_principal(e, ida, princ_type); if (g == Effect::Deny) { return g; } else if (g == Effect::Allow) { diff --git a/src/rgw/rgw_iam_policy.h b/src/rgw/rgw_iam_policy.h index 4f2c144f29f4..808c2296c941 100644 --- a/src/rgw/rgw_iam_policy.h +++ b/src/rgw/rgw_iam_policy.h @@ -242,6 +242,12 @@ inline int op_to_perm(std::uint64_t op) { } } +enum class PolicyPrincipal { + Role, + Session, + Other +}; + using Environment = boost::container::flat_map; using Address = std::bitset<128>; @@ -439,10 +445,10 @@ struct Statement { Effect eval(const Environment& e, boost::optional ida, - std::uint64_t action, const ARN& resource) const; + std::uint64_t action, const ARN& resource, boost::optional princ_type=boost::none) const; Effect eval_principal(const Environment& e, - boost::optional ida) const; + boost::optional ida, boost::optional princ_type=boost::none) const; Effect eval_conditions(const Environment& e) const; }; @@ -471,10 +477,10 @@ struct Policy { Effect eval(const Environment& e, boost::optional ida, - std::uint64_t action, const ARN& resource) const; + std::uint64_t action, const ARN& resource, boost::optional princ_type=boost::none) const; Effect eval_principal(const Environment& e, - boost::optional ida) const; + boost::optional ida, boost::optional princ_type=boost::none) const; Effect eval_conditions(const Environment& e) const; diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index 4a371e7a2796..cd05f8ab8dbe 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -491,7 +491,7 @@ static int read_obj_policy(const DoutPrefixProvider *dpp, const rgw_user& bucket_owner = bucket_policy.get_owner().get_id(); if (bucket_owner.compare(s->user->get_id()) != 0 && ! s->auth.identity->is_admin_of(bucket_owner)) { - auto r = eval_user_policies(s->iam_user_policies, s->env, + auto r = eval_identity_or_session_policies(s->iam_user_policies, s->env, *s->auth.identity, rgw::IAM::s3ListBucket, ARN(bucket->get_key())); if (r == Effect::Allow) @@ -505,6 +505,15 @@ static int read_obj_policy(const DoutPrefixProvider *dpp, if (r == Effect::Deny) return -EACCES; } + if (! s->session_policies.empty()) { + r = eval_identity_or_session_policies(s->session_policies, s->env, + *s->auth.identity, rgw::IAM::s3ListBucket, + ARN(bucket->get_key())); + if (r == Effect::Allow) + return -ENOENT; + if (r == Effect::Deny) + return -EACCES; + } if (! bucket_policy.verify_permission(s, *s->auth.identity, s->perm_mask, RGW_PERM_READ)) ret = -EACCES; else @@ -1569,7 +1578,7 @@ int RGWGetObj::read_user_manifest_part(rgw::sal::RGWBucket* bucket, ldpp_dout(this, 2) << "overriding permissions due to admin operation" << dendl; } else if (!verify_object_permission(this, s, part->get_obj(), s->user_acl.get(), bucket_acl, &obj_policy, bucket_policy, - s->iam_user_policies, action)) { + s->iam_user_policies, s->session_policies, action)) { return -EPERM; } if (ent.meta.size == 0) { @@ -3565,24 +3574,49 @@ int RGWPutObj::verify_permission(optional_yield y) /* Object needs a bucket from this point */ s->object->set_bucket(s->bucket.get()); - auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env, + auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env, boost::none, rgw::IAM::s3PutObject, s->object->get_obj()); - if (usr_policy_res == Effect::Deny) + if (identity_policy_res == Effect::Deny) return -EACCES; rgw::IAM::Effect e = Effect::Pass; + rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other; if (s->iam_policy) { e = s->iam_policy->eval(s->env, *s->auth.identity, rgw::IAM::s3PutObject, - s->object->get_obj()); + s->object->get_obj(), + princ_type); } - if (e == Effect::Allow) { - return 0; - } else if (e == Effect::Deny) { + if (e == Effect::Deny) { return -EACCES; - } else if (usr_policy_res == Effect::Allow) { + } + + if (!s->session_policies.empty()) { + auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env, + boost::none, + rgw::IAM::s3PutObject, + s->object->get_obj()); + if (session_policy_res == Effect::Deny) { + return -EACCES; + } + if (princ_type == rgw::IAM::PolicyPrincipal::Role) { + //Intersection of session policy and identity policy plus intersection of session policy and bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || + (session_policy_res == Effect::Allow && e == Effect::Allow)) + return 0; + } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) { + //Intersection of session policy and identity policy plus bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) + return 0; + } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy + if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) + return 0; + } + return -EACCES; + } + if (e == Effect::Allow || identity_policy_res == Effect::Allow) { return 0; } } @@ -4119,26 +4153,61 @@ void RGWPostObj::execute(optional_yield y) return; } - if (s->iam_policy || ! s->iam_user_policies.empty()) { - auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env, + if (s->iam_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) { + auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env, boost::none, rgw::IAM::s3PutObject, s->object->get_obj()); - if (usr_policy_res == Effect::Deny) { + if (identity_policy_res == Effect::Deny) { op_ret = -EACCES; return; } rgw::IAM::Effect e = Effect::Pass; + rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other; if (s->iam_policy) { e = s->iam_policy->eval(s->env, *s->auth.identity, rgw::IAM::s3PutObject, - s->object->get_obj()); + s->object->get_obj(), + princ_type); } if (e == Effect::Deny) { op_ret = -EACCES; return; - } else if (usr_policy_res == Effect::Pass && e == Effect::Pass && !verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) { + } + + if (!s->session_policies.empty()) { + auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env, + boost::none, + rgw::IAM::s3PutObject, + s->object->get_obj()); + if (session_policy_res == Effect::Deny) { + op_ret = -EACCES; + return; + } + if (princ_type == rgw::IAM::PolicyPrincipal::Role) { + //Intersection of session policy and identity policy plus intersection of session policy and bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || + (session_policy_res == Effect::Allow && e == Effect::Allow)) { + op_ret = 0; + return; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) { + //Intersection of session policy and identity policy plus bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) { + op_ret = 0; + return; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy + if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) { + op_ret = 0; + return; + } + } + op_ret = -EACCES; + return; + } + if (identity_policy_res == Effect::Pass && e == Effect::Pass && !verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) { op_ret = -EACCES; return; } @@ -4666,9 +4735,9 @@ int RGWDeleteObj::verify_permission(optional_yield y) if (op_ret) { return op_ret; } - if (s->iam_policy || ! s->iam_user_policies.empty()) { + if (s->iam_policy || ! s->iam_user_policies.empty() || ! s->session_policies.empty()) { if (s->bucket->get_info().obj_lock_enabled() && bypass_governance_mode) { - auto r = eval_user_policies(s->iam_user_policies, s->env, boost::none, + auto r = eval_identity_or_session_policies(s->iam_user_policies, s->env, boost::none, rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket->get_key(), s->object->get_name())); if (r == Effect::Deny) { bypass_perm = false; @@ -4678,31 +4747,66 @@ int RGWDeleteObj::verify_permission(optional_yield y) if (r == Effect::Deny) { bypass_perm = false; } + } else if (r == Effect::Pass && !s->session_policies.empty()) { + r = eval_identity_or_session_policies(s->session_policies, s->env, boost::none, + rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket->get_key(), s->object->get_name())); + if (r == Effect::Deny) { + bypass_perm = false; + } } } - auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env, + auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env, boost::none, s->object->get_instance().empty() ? rgw::IAM::s3DeleteObject : rgw::IAM::s3DeleteObjectVersion, ARN(s->bucket->get_key(), s->object->get_name())); - if (usr_policy_res == Effect::Deny) { + if (identity_policy_res == Effect::Deny) { return -EACCES; } rgw::IAM::Effect r = Effect::Pass; + rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other; if (s->iam_policy) { r = s->iam_policy->eval(s->env, *s->auth.identity, s->object->get_instance().empty() ? rgw::IAM::s3DeleteObject : rgw::IAM::s3DeleteObjectVersion, - ARN(s->bucket->get_key(), s->object->get_name())); + ARN(s->bucket->get_key(), s->object->get_name()), + princ_type); } - if (r == Effect::Allow) - return 0; - else if (r == Effect::Deny) + if (r == Effect::Deny) + return -EACCES; + + if (!s->session_policies.empty()) { + auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env, + boost::none, + s->object->get_instance().empty() ? + rgw::IAM::s3DeleteObject : + rgw::IAM::s3DeleteObjectVersion, + ARN(s->bucket->get_key(), s->object->get_name())); + if (session_policy_res == Effect::Deny) { + return -EACCES; + } + if (princ_type == rgw::IAM::PolicyPrincipal::Role) { + //Intersection of session policy and identity policy plus intersection of session policy and bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || + (session_policy_res == Effect::Allow && r == Effect::Allow)) { + return 0; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) { + //Intersection of session policy and identity policy plus bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || r == Effect::Allow) { + return 0; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy + if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) { + return 0; + } + } return -EACCES; - else if (usr_policy_res == Effect::Allow) + } + if (r == Effect::Allow || identity_policy_res == Effect::Allow) return 0; } @@ -5738,26 +5842,54 @@ void RGWSetRequestPayment::execute(optional_yield y) int RGWInitMultipart::verify_permission(optional_yield y) { - if (s->iam_policy || ! s->iam_user_policies.empty()) { - auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env, + if (s->iam_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) { + auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env, boost::none, rgw::IAM::s3PutObject, s->object->get_obj()); - if (usr_policy_res == Effect::Deny) { + if (identity_policy_res == Effect::Deny) { return -EACCES; } rgw::IAM::Effect e = Effect::Pass; + rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other; if (s->iam_policy) { e = s->iam_policy->eval(s->env, *s->auth.identity, rgw::IAM::s3PutObject, - s->object->get_obj()); + s->object->get_obj(), + princ_type); } - if (e == Effect::Allow) { - return 0; - } else if (e == Effect::Deny) { + if (e == Effect::Deny) { + return -EACCES; + } + + if (!s->session_policies.empty()) { + auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env, + boost::none, + rgw::IAM::s3PutObject, + s->object->get_obj()); + if (session_policy_res == Effect::Deny) { + return -EACCES; + } + if (princ_type == rgw::IAM::PolicyPrincipal::Role) { + //Intersection of session policy and identity policy plus intersection of session policy and bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || + (session_policy_res == Effect::Allow && e == Effect::Allow)) { + return 0; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) { + //Intersection of session policy and identity policy plus bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) { + return 0; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy + if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) { + return 0; + } + } return -EACCES; - } else if (usr_policy_res == Effect::Allow) { + } + if (e == Effect::Allow || identity_policy_res == Effect::Allow) { return 0; } } @@ -5855,26 +5987,54 @@ void RGWInitMultipart::execute(optional_yield y) int RGWCompleteMultipart::verify_permission(optional_yield y) { - if (s->iam_policy || ! s->iam_user_policies.empty()) { - auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env, + if (s->iam_policy || ! s->iam_user_policies.empty() || ! s->session_policies.empty()) { + auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env, boost::none, rgw::IAM::s3PutObject, s->object->get_obj()); - if (usr_policy_res == Effect::Deny) { + if (identity_policy_res == Effect::Deny) { return -EACCES; } rgw::IAM::Effect e = Effect::Pass; + rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other; if (s->iam_policy) { e = s->iam_policy->eval(s->env, *s->auth.identity, rgw::IAM::s3PutObject, - s->object->get_obj()); + s->object->get_obj(), + princ_type); } - if (e == Effect::Allow) { - return 0; - } else if (e == Effect::Deny) { + if (e == Effect::Deny) { + return -EACCES; + } + + if (!s->session_policies.empty()) { + auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env, + boost::none, + rgw::IAM::s3PutObject, + s->object->get_obj()); + if (session_policy_res == Effect::Deny) { + return -EACCES; + } + if (princ_type == rgw::IAM::PolicyPrincipal::Role) { + //Intersection of session policy and identity policy plus intersection of session policy and bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || + (session_policy_res == Effect::Allow && e == Effect::Allow)) { + return 0; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) { + //Intersection of session policy and identity policy plus bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) { + return 0; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy + if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) { + return 0; + } + } return -EACCES; - } else if (usr_policy_res == Effect::Allow) { + } + if (e == Effect::Allow || identity_policy_res == Effect::Allow) { return 0; } } @@ -6195,27 +6355,56 @@ void RGWCompleteMultipart::complete() int RGWAbortMultipart::verify_permission(optional_yield y) { - if (s->iam_policy || ! s->iam_user_policies.empty()) { - auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env, + if (s->iam_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) { + auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env, boost::none, rgw::IAM::s3AbortMultipartUpload, s->object->get_obj()); - if (usr_policy_res == Effect::Deny) { + if (identity_policy_res == Effect::Deny) { return -EACCES; } rgw::IAM::Effect e = Effect::Pass; + rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other; if (s->iam_policy) { e = s->iam_policy->eval(s->env, *s->auth.identity, rgw::IAM::s3AbortMultipartUpload, - s->object->get_obj()); + s->object->get_obj(), princ_type); } - if (e == Effect::Allow) { - return 0; - } else if (e == Effect::Deny) { + + if (e == Effect::Deny) { return -EACCES; - } else if (usr_policy_res == Effect::Allow) + } + + if (!s->session_policies.empty()) { + auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env, + boost::none, + rgw::IAM::s3PutObject, + s->object->get_obj()); + if (session_policy_res == Effect::Deny) { + return -EACCES; + } + if (princ_type == rgw::IAM::PolicyPrincipal::Role) { + //Intersection of session policy and identity policy plus intersection of session policy and bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || + (session_policy_res == Effect::Allow && e == Effect::Allow)) { + return 0; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) { + //Intersection of session policy and identity policy plus bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) { + return 0; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy + if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) { + return 0; + } + } + return -EACCES; + } + if (e == Effect::Allow || identity_policy_res == Effect::Allow) { return 0; + } } if (!verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) { @@ -6362,9 +6551,9 @@ int RGWDeleteMultiObj::verify_permission(optional_yield y) return op_ret; } - if (s->iam_policy || ! s->iam_user_policies.empty()) { + if (s->iam_policy || ! s->iam_user_policies.empty() || ! s->session_policies.empty()) { if (s->bucket->get_info().obj_lock_enabled() && bypass_governance_mode) { - auto r = eval_user_policies(s->iam_user_policies, s->env, boost::none, + auto r = eval_identity_or_session_policies(s->iam_user_policies, s->env, boost::none, rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket->get_key())); if (r == Effect::Deny) { bypass_perm = false; @@ -6374,34 +6563,69 @@ int RGWDeleteMultiObj::verify_permission(optional_yield y) if (r == Effect::Deny) { bypass_perm = false; } + } else if (r == Effect::Pass && !s->session_policies.empty()) { + r = eval_identity_or_session_policies(s->session_policies, s->env, boost::none, + rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket->get_key())); + if (r == Effect::Deny) { + bypass_perm = false; + } } } bool not_versioned = rgw::sal::RGWObject::empty(s->object.get()) || s->object->get_instance().empty(); - auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env, + auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env, boost::none, not_versioned ? rgw::IAM::s3DeleteObject : rgw::IAM::s3DeleteObjectVersion, ARN(s->bucket->get_key())); - if (usr_policy_res == Effect::Deny) { + if (identity_policy_res == Effect::Deny) { return -EACCES; } rgw::IAM::Effect r = Effect::Pass; + rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other; if (s->iam_policy) { r = s->iam_policy->eval(s->env, *s->auth.identity, not_versioned ? rgw::IAM::s3DeleteObject : rgw::IAM::s3DeleteObjectVersion, - ARN(s->bucket->get_key())); + ARN(s->bucket->get_key()), + princ_type); } - if (r == Effect::Allow) - return 0; - else if (r == Effect::Deny) + if (r == Effect::Deny) return -EACCES; - else if (usr_policy_res == Effect::Allow) + + if (!s->session_policies.empty()) { + auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env, + boost::none, + not_versioned ? + rgw::IAM::s3DeleteObject : + rgw::IAM::s3DeleteObjectVersion, + ARN(s->bucket->get_key())); + if (session_policy_res == Effect::Deny) { + return -EACCES; + } + if (princ_type == rgw::IAM::PolicyPrincipal::Role) { + //Intersection of session policy and identity policy plus intersection of session policy and bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || + (session_policy_res == Effect::Allow && r == Effect::Allow)) { + return 0; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) { + //Intersection of session policy and identity policy plus bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || r == Effect::Allow) { + return 0; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy + if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) { + return 0; + } + } + return -EACCES; + } + if (r == Effect::Allow || identity_policy_res == Effect::Allow) return 0; } @@ -6485,30 +6709,70 @@ void RGWDeleteMultiObj::execute(optional_yield y) iter != multi_delete->objects.end(); ++iter) { std::string version_id; - std::unique_ptr obj = bucket->get_object(*iter); - if (s->iam_policy || ! s->iam_user_policies.empty()) { - auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env, + std::unique_ptr obj = bucket->get_object(*iter); + if (s->iam_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) { + auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env, boost::none, iter->instance.empty() ? rgw::IAM::s3DeleteObject : rgw::IAM::s3DeleteObjectVersion, ARN(obj->get_obj())); - if (usr_policy_res == Effect::Deny) { + if (identity_policy_res == Effect::Deny) { send_partial_response(*iter, false, "", -EACCES); continue; } rgw::IAM::Effect e = Effect::Pass; + rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other; if (s->iam_policy) { e = s->iam_policy->eval(s->env, *s->auth.identity, iter->instance.empty() ? rgw::IAM::s3DeleteObject : rgw::IAM::s3DeleteObjectVersion, - ARN(obj->get_obj())); + ARN(obj->get_obj()), + princ_type); + } + if (e == Effect::Deny) { + send_partial_response(*iter, false, "", -EACCES); + continue; + } + + if (!s->session_policies.empty()) { + auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env, + boost::none, + iter->instance.empty() ? + rgw::IAM::s3DeleteObject : + rgw::IAM::s3DeleteObjectVersion, + ARN(obj->get_obj())); + if (session_policy_res == Effect::Deny) { + send_partial_response(*iter, false, "", -EACCES); + continue; + } + if (princ_type == rgw::IAM::PolicyPrincipal::Role) { + //Intersection of session policy and identity policy plus intersection of session policy and bucket policy + if ((session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) && + (session_policy_res != Effect::Allow || e != Effect::Allow)) { + send_partial_response(*iter, false, "", -EACCES); + continue; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) { + //Intersection of session policy and identity policy plus bucket policy + if ((session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) && e != Effect::Allow) { + send_partial_response(*iter, false, "", -EACCES); + continue; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy + if (session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) { + send_partial_response(*iter, false, "", -EACCES); + continue; + } + } + send_partial_response(*iter, false, "", -EACCES); + continue; } - if ((e == Effect::Deny) || - (usr_policy_res == Effect::Pass && e == Effect::Pass && !acl_allowed)) { + + if ((identity_policy_res == Effect::Pass && e == Effect::Pass && !acl_allowed)) { send_partial_response(*iter, false, "", -EACCES); continue; } @@ -6603,7 +6867,7 @@ bool RGWBulkDelete::Deleter::verify_permission(RGWBucketInfo& binfo, /* We can use global user_acl because each BulkDelete request is allowed * to work on entities from a single account only. */ return verify_bucket_permission(dpp, s, binfo.bucket, s->user_acl.get(), - &bacl, policy, s->iam_user_policies, rgw::IAM::s3DeleteBucket); + &bacl, policy, s->iam_user_policies, s->session_policies, rgw::IAM::s3DeleteBucket); } bool RGWBulkDelete::Deleter::delete_single(const acct_path_t& path, optional_yield y) @@ -6962,20 +7226,47 @@ bool RGWBulkUploadOp::handle_file_verify_permission(RGWBucketInfo& binfo, auto policy = get_iam_policy_from_attr(s->cct, battrs, binfo.bucket.tenant); bucket_owner = bacl.get_owner(); - if (policy || ! s->iam_user_policies.empty()) { - auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env, + if (policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) { + auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env, boost::none, rgw::IAM::s3PutObject, obj); - if (usr_policy_res == Effect::Deny) { + if (identity_policy_res == Effect::Deny) { return false; } + + rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other; auto e = policy->eval(s->env, *s->auth.identity, - rgw::IAM::s3PutObject, obj); - if (e == Effect::Allow) { - return true; - } else if (e == Effect::Deny) { + rgw::IAM::s3PutObject, obj, princ_type); + if (e == Effect::Deny) { + return false; + } + + if (!s->session_policies.empty()) { + auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env, + boost::none, + rgw::IAM::s3PutObject, obj); + if (session_policy_res == Effect::Deny) { + return false; + } + if (princ_type == rgw::IAM::PolicyPrincipal::Role) { + //Intersection of session policy and identity policy plus intersection of session policy and bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || + (session_policy_res == Effect::Allow && e == Effect::Allow)) { + return true; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) { + //Intersection of session policy and identity policy plus bucket policy + if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) { + return true; + } + } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy + if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) { + return true; + } + } return false; - } else if (usr_policy_res == Effect::Allow) { + } + if (e == Effect::Allow || identity_policy_res == Effect::Allow) { return true; } } diff --git a/src/test/rgw/test_rgw_iam_policy.cc b/src/test/rgw/test_rgw_iam_policy.cc index 0970540eb197..46fd9a2ac4cd 100644 --- a/src/test/rgw/test_rgw_iam_policy.cc +++ b/src/test/rgw/test_rgw_iam_policy.cc @@ -119,11 +119,6 @@ public: return 0; } - uint32_t get_identity_type() const override { - abort(); - return 0; - } - string get_acct_name() const override { abort(); return 0; @@ -144,6 +139,10 @@ public: } return ids.find(id) != ids.end() || ids.find(Principal::wildcard()) != ids.end(); } + + uint32_t get_identity_type() const override { + return TYPE_RGW; + } }; class PolicyTest : public ::testing::Test {