return e ? iter->second : string();
}
+namespace {
+Effect eval_or_pass(const boost::optional<Policy>& policy,
+ const rgw::IAM::Environment& env,
+ boost::optional<const rgw::auth::Identity&> id,
+ const uint64_t op,
+ const ARN& arn) {
+ if (!policy)
+ return Effect::Pass;
+ else
+ return policy->eval(env, id, op, arn);
+}
+
+}
+
+Effect eval_user_policies(const vector<Policy>& user_policies,
+ const rgw::IAM::Environment& env,
+ boost::optional<const rgw::auth::Identity&> 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)
+ prev_res = Effect::Allow;
+ else if (usr_policy_res == Effect::Pass && prev_res == Effect::Allow)
+ usr_policy_res = Effect::Allow;
+ }
+ return usr_policy_res;
+}
+
bool verify_user_permission(struct req_state * const s,
+ RGWAccessControlPolicy * const user_acl,
+ const vector<rgw::IAM::Policy>& user_policies,
+ const rgw::IAM::ARN& res,
+ const uint64_t op)
+{
+ auto usr_policy_res = eval_user_policies(user_policies, s->env, boost::none, op, res);
+ if (usr_policy_res == Effect::Deny)
+ return false;
+
+ auto perm = op_to_perm(op);
+
+ return verify_user_permission_no_policy(s, user_acl, perm);
+}
+
+bool verify_user_permission_no_policy(struct req_state * const s,
RGWAccessControlPolicy * const user_acl,
const int perm)
{
}
bool verify_user_permission(struct req_state * const s,
- const int perm)
+ const rgw::IAM::ARN& res,
+ const uint64_t op)
+{
+ return verify_user_permission(s, s->user_acl.get(), s->iam_user_policies, res, op);
+}
+
+bool verify_user_permission_no_policy(struct req_state * const s,
+ const int perm)
{
- return verify_user_permission(s, s->user_acl.get(), perm);
+ return verify_user_permission_no_policy(s, s->user_acl.get(), perm);
}
bool verify_requester_payer_permission(struct req_state *s)
return false;
}
-namespace {
-Effect eval_or_pass(const boost::optional<Policy>& policy,
- const rgw::IAM::Environment& env,
- const rgw::auth::Identity& id,
- const uint64_t op,
- const ARN& arn) {
- if (!policy)
- return Effect::Pass;
- else
- return policy->eval(env, id, op, arn);
-}
-}
-
bool verify_bucket_permission(struct req_state * const s,
const rgw_bucket& bucket,
RGWAccessControlPolicy * const user_acl,
RGWAccessControlPolicy * const bucket_acl,
const boost::optional<Policy>& bucket_policy,
+ const vector<Policy>& user_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)
+ return false;
+
auto r = eval_or_pass(bucket_policy, s->env, *s->auth.identity,
op, ARN(bucket));
if (r == Effect::Allow)
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);
s->user_acl.get(),
s->bucket_acl.get(),
s->iam_policy,
+ s->iam_user_policies,
op);
}
RGWAccessControlPolicy * const user_acl,
RGWAccessControlPolicy * const bucket_acl,
const boost::optional<Policy>& bucket_policy,
+ const vector<Policy>& user_policies,
const uint8_t deferred_check,
const uint64_t op)
{
return (s->defer_to_bucket_acls == deferred_check \
- && verify_bucket_permission(s, bucket, user_acl, bucket_acl, bucket_policy, op));
+ && verify_bucket_permission(s, bucket, user_acl, bucket_acl, bucket_policy, user_policies,op));
}
static inline bool check_deferred_bucket_only_acl(struct req_state * const s,
RGWAccessControlPolicy * const bucket_acl,
RGWAccessControlPolicy * const object_acl,
const boost::optional<Policy>& bucket_policy,
+ const vector<Policy>& user_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)
+ return false;
auto r = eval_or_pass(bucket_policy, s->env, *s->auth.identity, op, ARN(obj));
if (r == Effect::Allow)
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(s, obj.bucket, user_acl, bucket_acl, bucket_policy,
- RGW_DEFER_TO_BUCKET_ACLS_RECURSE, op) ||
+ user_policies, RGW_DEFER_TO_BUCKET_ACLS_RECURSE, op) ||
check_deferred_bucket_perms(s, obj.bucket, user_acl, bucket_acl, bucket_policy,
- RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, rgw::IAM::s3All)) {
+ user_policies, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, rgw::IAM::s3All)) {
return true;
}
s->bucket_acl.get(),
s->object_acl.get(),
s->iam_policy,
+ s->iam_user_policies,
op);
}
}
}
+static vector<Policy> get_iam_user_policy_from_attr(CephContext* cct,
+ RGWRados* store,
+ map<string, bufferlist>& attrs,
+ const string& tenant) {
+ vector<Policy> policies;
+ if (auto it = attrs.find(RGW_ATTR_USER_POLICY); it != attrs.end()) {
+ bufferlist out_bl = attrs[RGW_ATTR_USER_POLICY];
+ map<string, string> policy_map;
+ decode(policy_map, out_bl);
+ for (auto& it : policy_map) {
+ bufferlist bl = bufferlist::static_from_string(it.second);
+ Policy p(cct, tenant, bl);
+ policies.push_back(std::move(p));
+ }
+ }
+ return policies;
+}
+
static int get_obj_attrs(RGWRados *store, struct req_state *s, rgw_obj& obj, map<string, bufferlist>& attrs)
{
RGWRados::Object op_target(store, s->bucket_info, *static_cast<RGWObjectCtx *>(s->obj_ctx), obj);
}
}
+
/* handle user ACL only for those APIs which support it */
if (s->user_acl) {
map<string, bufferlist> uattrs;
-
ret = rgw_get_user_attrs_by_uid(store, acct_acl_user.uid, uattrs);
if (!ret) {
ret = get_user_policy_from_attr(s->cct, store, uattrs, *s->user_acl);
}
}
+ try {
+ map<string, bufferlist> uattrs;
+ if (ret = rgw_get_user_attrs_by_uid(store, s->user->user_id, uattrs); ! ret) {
+ s->iam_user_policies = get_iam_user_policy_from_attr(s->cct, store, uattrs, s->user->user_id.tenant);
+ }
+ } catch (const std::exception& e) {
+ lderr(s->cct) << "Error reading IAM User Policy: " << e.what() << dendl;
+ ret = -EACCES;
+ }
+
try {
s->iam_policy = get_iam_policy_from_attr(s->cct, store, s->bucket_attrs,
s->bucket_tenant);
}
if (s->iam_policy && s->iam_policy->has_partial_conditional(S3_EXISTING_OBJTAG))
rgw_iam_add_existing_objtags(store, s, obj, action);
+ if (! s->iam_user_policies.empty()) {
+ for (auto& user_policy : s->iam_user_policies) {
+ if (user_policy.has_partial_conditional(S3_EXISTING_OBJTAG))
+ rgw_iam_add_existing_objtags(store, s, obj, action);
+ }
+ }
}
if (!verify_object_permission(s, action)) {
rgw_obj obj = rgw_obj(s->bucket, s->object);
rgw_iam_add_existing_objtags(store, s, obj, iam_action);
}
-
+ if (! s->iam_user_policies.empty()) {
+ for (auto& user_policy : s->iam_user_policies) {
+ if (user_policy.has_partial_conditional(S3_EXISTING_OBJTAG)) {
+ rgw_obj obj = rgw_obj(s->bucket, s->object);
+ rgw_iam_add_existing_objtags(store, s, obj, iam_action);
+ }
+ }
+ }
if (!verify_object_permission(s,iam_action))
return -EACCES;
auto obj = rgw_obj(s->bucket, s->object);
rgw_iam_add_existing_objtags(store, s, obj, iam_action);
}
-
+ if (! s->iam_user_policies.empty()) {
+ for (auto& user_policy : s->iam_user_policies) {
+ if (user_policy.has_partial_conditional(S3_EXISTING_OBJTAG)) {
+ auto obj = rgw_obj(s->bucket, s->object);
+ rgw_iam_add_existing_objtags(store, s, obj, iam_action);
+ }
+ }
+ }
if (!verify_object_permission(s, iam_action))
return -EACCES;
}
} else if (s->auth.identity->is_admin_of(s->user->user_id)) {
ldpp_dout(this, 2) << "overriding permissions due to admin operation" << dendl;
} else if (!verify_object_permission(s, part, s->user_acl.get(), bucket_acl,
- &obj_policy, bucket_policy, action)) {
+ &obj_policy, bucket_policy, s->iam_user_policies, action)) {
return -EPERM;
}
-
if (ent.meta.size == 0) {
return 0;
}
int RGWListBuckets::verify_permission()
{
- if (!verify_user_permission(s, RGW_PERM_READ)) {
+ if (!verify_user_permission(s, ARN(), rgw::IAM::s3ListAllMyBuckets)) {
return -EACCES;
}
int RGWStatAccount::verify_permission()
{
- if (!verify_user_permission(s, RGW_PERM_READ)) {
+ if (!verify_user_permission_no_policy(s, RGW_PERM_READ)) {
return -EACCES;
}
return -EACCES;
}
- if (!verify_user_permission(s, RGW_PERM_WRITE)) {
+ if (!verify_user_permission(s, ARN(s->bucket), rgw::IAM::s3CreateBucket)) {
return -EACCES;
}
/* admin request overrides permission checks */
if (! s->auth.identity->is_admin_of(cs_acl.get_owner().get_id())) {
- if (policy) {
+ if (policy || ! s->iam_user_policies.empty()) {
+ auto usr_policy_res = Effect::Pass;
+ for (auto& user_policy : s->iam_user_policies) {
+ if (usr_policy_res = user_policy.eval(s->env, *s->auth.identity,
+ cs_object.instance.empty() ?
+ rgw::IAM::s3GetObject :
+ rgw::IAM::s3GetObjectVersion,
+ rgw::IAM::ARN(obj)); usr_policy_res == Effect::Deny)
+ return -EACCES;
+ else if (usr_policy_res == Effect::Allow)
+ break;
+ }
auto e = policy->eval(s->env, *s->auth.identity,
cs_object.instance.empty() ?
rgw::IAM::s3GetObject :
rgw::IAM::ARN(obj));
if (e == Effect::Deny) {
return -EACCES;
- } else if (e == Effect::Pass &&
+ } else if (usr_policy_res == Effect::Pass && e == Effect::Pass &&
!cs_acl.verify_permission(*s->auth.identity, s->perm_mask,
RGW_PERM_READ)) {
return -EACCES;
return op_ret;
}
- if (s->iam_policy) {
+ if (s->iam_policy || ! s->iam_user_policies.empty()) {
rgw_add_grant_to_iam_environment(s->env, s);
rgw_add_to_iam_environment(s->env, "s3:x-amz-acl", s->canned_acl);
rgw_add_to_iam_environment(s->env, s3_kms_attr, kms_header->second);
}
+ auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+ *s->auth.identity,
+ rgw::IAM::s3PutObject,
+ rgw_obj(s->bucket, s->object));
+ if (usr_policy_res == Effect::Deny)
+ return -EACCES;
+
auto e = s->iam_policy->eval(s->env, *s->auth.identity,
rgw::IAM::s3PutObject,
rgw_obj(s->bucket, s->object));
return 0;
} else if (e == Effect::Deny) {
return -EACCES;
+ } else if (usr_policy_res == Effect::Allow) {
+ return 0;
}
}
return;
}
- if (s->iam_policy) {
+ if (s->iam_policy || ! s->iam_user_policies.empty()) {
+ auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+ *s->auth.identity,
+ rgw::IAM::s3PutObject,
+ rgw_obj(s->bucket, s->object));
+ if (usr_policy_res == Effect::Deny) {
+ op_ret = -EACCES;
+ return;
+ }
auto e = s->iam_policy->eval(s->env, *s->auth.identity,
rgw::IAM::s3PutObject,
rgw_obj(s->bucket, s->object));
if (e == Effect::Deny) {
op_ret = -EACCES;
return;
- } else if (e == Effect::Pass && !verify_bucket_permission_no_policy(s, RGW_PERM_WRITE)) {
+ } else if (usr_policy_res == Effect::Pass && e == Effect::Pass && !verify_bucket_permission_no_policy(s, RGW_PERM_WRITE)) {
op_ret = -EACCES;
return;
}
return -EACCES;
}
- if (!verify_user_permission(s, RGW_PERM_WRITE)) {
+ if (!verify_user_permission_no_policy(s, RGW_PERM_WRITE)) {
return -EACCES;
}
int RGWDeleteObj::verify_permission()
{
- if (s->iam_policy) {
+ if (s->iam_policy || ! s->iam_user_policies.empty()) {
+ auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+ *s->auth.identity,
+ s->object.instance.empty() ?
+ rgw::IAM::s3DeleteObject :
+ rgw::IAM::s3DeleteObjectVersion,
+ ARN(s->bucket, s->object.name));
+ if (usr_policy_res == Effect::Deny) {
+ return false;
+ }
auto r = s->iam_policy->eval(s->env, *s->auth.identity,
s->object.instance.empty() ?
rgw::IAM::s3DeleteObject :
return true;
else if (r == Effect::Deny)
return false;
+ else if (usr_policy_res == Effect::Allow)
+ return true;
}
if (!verify_bucket_permission_no_policy(s, RGW_PERM_WRITE)) {
rgw_obj obj = rgw_obj(s->bucket, s->object);
rgw_iam_add_existing_objtags(store, s, obj, iam_action);
}
-
+ if (! s->iam_user_policies.empty()) {
+ for (auto& user_policy : s->iam_user_policies) {
+ if (user_policy.has_partial_conditional(S3_EXISTING_OBJTAG)) {
+ rgw_obj obj = rgw_obj(s->bucket, s->object);
+ rgw_iam_add_existing_objtags(store, s, obj, iam_action);
+ }
+ }
+ }
perm = verify_object_permission(s, iam_action);
} else {
perm = verify_bucket_permission(s, rgw::IAM::s3GetBucketAcl);
int RGWInitMultipart::verify_permission()
{
- if (s->iam_policy) {
+ if (s->iam_policy || ! s->iam_user_policies.empty()) {
+ auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+ *s->auth.identity,
+ rgw::IAM::s3PutObject,
+ rgw_obj(s->bucket, s->object));
+ if (usr_policy_res == Effect::Deny) {
+ return -EACCES;
+ }
auto e = s->iam_policy->eval(s->env, *s->auth.identity,
rgw::IAM::s3PutObject,
rgw_obj(s->bucket, s->object));
return 0;
} else if (e == Effect::Deny) {
return -EACCES;
+ } else if (usr_policy_res == Effect::Allow) {
+ return 0;
}
}
int RGWCompleteMultipart::verify_permission()
{
- if (s->iam_policy) {
+ if (s->iam_policy || ! s->iam_user_policies.empty()) {
+ auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+ *s->auth.identity,
+ rgw::IAM::s3PutObject,
+ rgw_obj(s->bucket, s->object));
+ if (usr_policy_res == Effect::Deny) {
+ return -EACCES;
+ }
auto e = s->iam_policy->eval(s->env, *s->auth.identity,
rgw::IAM::s3PutObject,
rgw_obj(s->bucket, s->object));
return 0;
} else if (e == Effect::Deny) {
return -EACCES;
+ } else if (usr_policy_res == Effect::Allow) {
+ return 0;
}
}
int RGWAbortMultipart::verify_permission()
{
- if (s->iam_policy) {
+ if (s->iam_policy || ! s->iam_user_policies.empty()) {
+ auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+ *s->auth.identity,
+ rgw::IAM::s3AbortMultipartUpload,
+ rgw_obj(s->bucket, s->object));
+ if (usr_policy_res == Effect::Deny) {
+ return -EACCES;
+ }
+
auto e = s->iam_policy->eval(s->env, *s->auth.identity,
rgw::IAM::s3AbortMultipartUpload,
rgw_obj(s->bucket, s->object));
return 0;
} else if (e == Effect::Deny) {
return -EACCES;
- }
+ } else if (usr_policy_res == Effect::Allow)
+ return 0;
}
if (!verify_bucket_permission_no_policy(s, RGW_PERM_WRITE)) {
int RGWDeleteMultiObj::verify_permission()
{
acl_allowed = verify_bucket_permission_no_policy(s, RGW_PERM_WRITE);
- if (!acl_allowed && !s->iam_policy)
+ if (!acl_allowed && !s->iam_policy && s->iam_user_policies.empty())
return -EACCES;
return 0;
iter != multi_delete->objects.end() && num_processed < max_to_delete;
++iter, num_processed++) {
rgw_obj obj(bucket, *iter);
- if (s->iam_policy) {
+ if (s->iam_policy || ! s->iam_user_policies.empty()) {
+ auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+ *s->auth.identity,
+ iter->instance.empty() ?
+ rgw::IAM::s3DeleteObject :
+ rgw::IAM::s3DeleteObjectVersion,
+ obj);
+ if (usr_policy_res == Effect::Deny) {
+ send_partial_response(*iter, false, "", -EACCES);
+ continue;
+ }
auto e = s->iam_policy->eval(s->env,
*s->auth.identity,
iter->instance.empty() ?
rgw::IAM::s3DeleteObjectVersion,
obj);
if ((e == Effect::Deny) ||
- (e == Effect::Pass && !acl_allowed)) {
+ (usr_policy_res == Effect::Pass && e == Effect::Pass && !acl_allowed)) {
send_partial_response(*iter, false, "", -EACCES);
continue;
}
/* 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(s, binfo.bucket, s->user_acl.get(),
- &bacl, policy, rgw::IAM::s3DeleteBucket);
+ &bacl, policy, s->iam_user_policies, rgw::IAM::s3DeleteBucket);
}
bool RGWBulkDelete::Deleter::delete_single(const acct_path_t& path)
return -EACCES;
}
- if (! verify_user_permission(s, RGW_PERM_WRITE)) {
+ if (! verify_user_permission_no_policy(s, RGW_PERM_WRITE)) {
return -EACCES;
}
auto policy = get_iam_policy_from_attr(s->cct, store, battrs, binfo.bucket.tenant);
bucket_owner = bacl.get_owner();
- if (policy) {
+ if (policy || ! s->iam_user_policies.empty()) {
+ auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
+ *s->auth.identity,
+ rgw::IAM::s3PutObject, obj);
+ if (usr_policy_res == Effect::Deny) {
+ return false;
+ }
auto e = policy->eval(s->env, *s->auth.identity,
rgw::IAM::s3PutObject, obj);
if (e == Effect::Allow) {
return true;
} else if (e == Effect::Deny) {
return false;
+ } else if (usr_policy_res == Effect::Allow) {
+ return true;
}
}