From: Adam C. Emerson Date: Thu, 6 Apr 2017 23:11:30 +0000 (-0400) Subject: rgw: Verify policies as WELL as ACLs X-Git-Tag: v12.1.0~10^2~88^2~2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=266103fa225378f0a76f758e46fc1a6d53e9d09a;p=ceph.git rgw: Verify policies as WELL as ACLs Only hook into object/bucket checks for now. Once we have STS (giving us Roles) or User/Group policies it will make sense to hook into those, too. Signed-off-by: Adam C. Emerson --- diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc index f0a34a5e22b..009918b76ff 100644 --- a/src/rgw/rgw_common.cc +++ b/src/rgw/rgw_common.cc @@ -34,6 +34,14 @@ #define dout_context g_ceph_context #define dout_subsys ceph_subsys_rgw +using boost::none; +using boost::optional; + +using rgw::IAM::ARN; +using rgw::IAM::Effect; +using rgw::IAM::op_to_perm; +using rgw::IAM::Policy; + PerfCounters *perfcounter = NULL; const uint32_t RGWBucketInfo::NUM_SHARDS_BLIND_BUCKET(UINT32_MAX); @@ -1069,17 +1077,39 @@ bool verify_requester_payer_permission(struct req_state *s) } bool verify_bucket_permission(struct req_state * const s, + const rgw_bucket& bucket, RGWAccessControlPolicy * const user_acl, RGWAccessControlPolicy * const bucket_acl, - const int perm) + const optional& bucket_policy, + const uint64_t op) { - if (!bucket_acl) + if (!verify_requester_payer_permission(s)) return false; - if ((perm & (int)s->perm_mask) != perm) + if (bucket_policy) { + auto r = bucket_policy->eval(s->env, *s->auth.identity, op, ARN(bucket)); + if (r == 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; + } + + const auto perm = op_to_perm(op); + + return verify_bucket_permission_no_policy(s, user_acl, bucket_acl, perm); +} + +bool verify_bucket_permission_no_policy(struct req_state * const s, + RGWAccessControlPolicy * const user_acl, + RGWAccessControlPolicy * const bucket_acl, + const int perm) +{ + if (!bucket_acl) return false; - if (!verify_requester_payer_permission(s)) + if ((perm & (int)s->perm_mask) != perm) return false; if (bucket_acl->verify_permission(*s->auth.identity, perm, perm, @@ -1092,35 +1122,123 @@ bool verify_bucket_permission(struct req_state * const s, return user_acl->verify_permission(*s->auth.identity, perm, perm); } -bool verify_bucket_permission(struct req_state * const s, const int perm) +bool verify_bucket_permission_no_policy(struct req_state * const s, const int perm) +{ + if (!verify_requester_payer_permission(s)) + return false; + + return verify_bucket_permission_no_policy(s, + s->user_acl.get(), + s->bucket_acl, + perm); +} + +bool verify_bucket_permission(struct req_state * const s, const uint64_t op) { return verify_bucket_permission(s, + s->bucket, s->user_acl.get(), s->bucket_acl, - perm); + s->iam_policy, + op); +} + +static inline bool check_deferred_bucket_perms(struct req_state * const s, + const rgw_bucket& bucket, + RGWAccessControlPolicy * const user_acl, + RGWAccessControlPolicy * const bucket_acl, + const optional& bucket_policy, + 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)); } -static inline bool check_deferred_bucket_acl(struct req_state * const s, - RGWAccessControlPolicy * const user_acl, - RGWAccessControlPolicy * const bucket_acl, - const uint8_t deferred_check, - const int perm) +static inline bool check_deferred_bucket_only_acl(struct req_state * const s, + RGWAccessControlPolicy * const user_acl, + RGWAccessControlPolicy * const bucket_acl, + const uint8_t deferred_check, + const int perm) { return (s->defer_to_bucket_acls == deferred_check \ - && verify_bucket_permission(s, user_acl, bucket_acl, perm)); + && verify_bucket_permission_no_policy(s, user_acl, bucket_acl, perm)); } bool verify_object_permission(struct req_state * const s, + const rgw_obj& obj, RGWAccessControlPolicy * const user_acl, RGWAccessControlPolicy * const bucket_acl, RGWAccessControlPolicy * const object_acl, - const int perm) + const optional& bucket_policy, + const uint64_t op) { if (!verify_requester_payer_permission(s)) return false; - if (check_deferred_bucket_acl(s, user_acl, bucket_acl, RGW_DEFER_TO_BUCKET_ACLS_RECURSE, perm) || - check_deferred_bucket_acl(s, user_acl, bucket_acl, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, RGW_PERM_FULL_CONTROL)) { + if (bucket_policy) { + auto r = bucket_policy->eval(s->env, *s->auth.identity, op, ARN(obj)); + if (r == 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; + } + + 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) || + check_deferred_bucket_perms(s, obj.bucket, user_acl, bucket_acl, bucket_policy, + RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, rgw::IAM::s3All)) { + return true; + } + + if (!object_acl) { + return false; + } + + bool ret = object_acl->verify_permission(*s->auth.identity, s->perm_mask, perm); + if (ret) { + return true; + } + + if (!s->cct->_conf->rgw_enforce_swift_acls) + return ret; + + if ((perm & (int)s->perm_mask) != perm) + return false; + + int swift_perm = 0; + if (perm & (RGW_PERM_READ | RGW_PERM_READ_ACP)) + swift_perm |= RGW_PERM_READ_OBJS; + if (perm & RGW_PERM_WRITE) + swift_perm |= RGW_PERM_WRITE_OBJS; + + if (!swift_perm) + return false; + + /* we already verified the user mask above, so we pass swift_perm as the mask here, + otherwise the mask might not cover the swift permissions bits */ + if (bucket_acl->verify_permission(*s->auth.identity, swift_perm, swift_perm, + s->info.env->get("HTTP_REFERER"))) + return true; + + if (!user_acl) + return false; + + return user_acl->verify_permission(*s->auth.identity, swift_perm, swift_perm); +} + +bool verify_object_permission_no_policy(struct req_state * const s, + RGWAccessControlPolicy * const user_acl, + RGWAccessControlPolicy * const bucket_acl, + RGWAccessControlPolicy * const object_acl, + const int perm) +{ + if (check_deferred_bucket_only_acl(s, user_acl, bucket_acl, RGW_DEFER_TO_BUCKET_ACLS_RECURSE, perm) || + check_deferred_bucket_only_acl(s, user_acl, bucket_acl, RGW_DEFER_TO_BUCKET_ACLS_FULL_CONTROL, RGW_PERM_FULL_CONTROL)) { return true; } @@ -1160,13 +1278,25 @@ bool verify_object_permission(struct req_state * const s, return user_acl->verify_permission(*s->auth.identity, swift_perm, swift_perm); } -bool verify_object_permission(struct req_state *s, int perm) +bool verify_object_permission_no_policy(struct req_state *s, int perm) +{ + if (!verify_requester_payer_permission(s)) + return false; + + return verify_object_permission_no_policy(s, s->user_acl.get(), + s->bucket_acl, s->object_acl, + perm); +} + +bool verify_object_permission(struct req_state *s, uint64_t op) { return verify_object_permission(s, - s->user_acl.get(), + rgw_obj(s->bucket, s->object), + s->user_acl.get(), s->bucket_acl, s->object_acl, - perm); + s->iam_policy, + op); } class HexTable diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h index ad1759f4c77..099f6df79cd 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -2150,17 +2150,38 @@ bool verify_user_permission(struct req_state * const s, const int perm); bool verify_user_permission(struct req_state * const s, const int perm); -extern bool verify_bucket_permission(struct req_state * s, - RGWAccessControlPolicy * user_acl, - RGWAccessControlPolicy * bucket_acl, - int perm); -extern bool verify_bucket_permission(struct req_state *s, int perm); -extern bool verify_object_permission(struct req_state *s, - RGWAccessControlPolicy * user_acl, - RGWAccessControlPolicy * bucket_acl, - RGWAccessControlPolicy * object_acl, - int perm); -extern bool verify_object_permission(struct req_state *s, int perm); +bool verify_bucket_permission( + struct req_state * const s, + const rgw_bucket& bucket, + RGWAccessControlPolicy * const user_acl, + RGWAccessControlPolicy * const bucket_acl, + const boost::optional& bucket_policy, + const uint64_t op); +bool verify_bucket_permission(struct req_state * const s, const uint64_t op); +bool verify_bucket_permission_no_policy( + struct req_state * const s, + RGWAccessControlPolicy * const user_acl, + RGWAccessControlPolicy * const bucket_acl, + const int perm); +bool verify_bucket_permission_no_policy(struct req_state * const s, + const int perm); +extern bool verify_object_permission( + struct req_state * const s, + const rgw_obj& obj, + RGWAccessControlPolicy * const user_acl, + RGWAccessControlPolicy * const bucket_acl, + RGWAccessControlPolicy * const object_acl, + const boost::optional& bucket_policy, + const uint64_t op); +extern bool verify_object_permission(struct req_state *s, uint64_t op); +extern bool verify_object_permission_no_policy( + struct req_state * const s, + RGWAccessControlPolicy * const user_acl, + RGWAccessControlPolicy * const bucket_acl, + RGWAccessControlPolicy * const object_acl, + int perm); +extern bool verify_object_permission_no_policy(struct req_state *s, + int perm); /** Convert an input URL into a sane object name * by converting %-escaped strings into characters, etc*/ extern void rgw_uri_escape_char(char c, string& dst); diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index b4f5ac68d76..49c910ec3e2 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -55,6 +55,11 @@ using namespace std; using namespace librados; using ceph::crypto::MD5; using boost::optional; +using boost::none; + +using rgw::IAM::ARN; +using rgw::IAM::Effect; +using rgw::IAM::Policy; using rgw::IAM::Policy; @@ -246,9 +251,9 @@ static optional get_iam_policy_from_attr(CephContext* cct, const string& tenant) { auto i = attrs.find(RGW_ATTR_IAM_POLICY); if (i != attrs.end()) { - return rgw::IAM::Policy(cct, tenant, i->second); + return Policy(cct, tenant, i->second); } else { - return boost::none; + return none; } } @@ -320,7 +325,8 @@ static int read_obj_policy(RGWRados *store, struct req_state *s, RGWBucketInfo& bucket_info, map& bucket_attrs, - RGWAccessControlPolicy *policy, + RGWAccessControlPolicy* acl, + optional& policy, rgw_bucket& bucket, rgw_obj_key& object) { @@ -342,9 +348,11 @@ static int read_obj_policy(RGWRados *store, } else { obj = rgw_obj(bucket, object); } + policy = get_iam_policy_from_attr(s->cct, store, bucket_attrs, bucket.tenant); + RGWObjectCtx *obj_ctx = static_cast(s->obj_ctx); int ret = get_obj_policy_from_attr(s->cct, store, *obj_ctx, - bucket_info, bucket_attrs, policy, obj); + bucket_info, bucket_attrs, acl, obj); if (ret == -ENOENT) { /* object does not exist checking the bucket's ACL to make sure that we send a proper error code */ @@ -557,7 +565,7 @@ int rgw_build_object_policies(RGWRados *store, struct req_state *s, if (prefetch_data) { store->set_prefetch_data(s->obj_ctx, obj); } - ret = read_obj_policy(store, s, s->bucket_info, s->bucket_attrs, s->object_acl, s->bucket, s->object); + ret = read_obj_policy(store, s, s->bucket_info, s->bucket_attrs, s->object_acl, s->iam_policy, s->bucket, s->object); } return ret; @@ -639,7 +647,21 @@ int RGWGetObj::verify_permission() store->set_prefetch_data(s->obj_ctx, obj); } - if (!verify_object_permission(s, RGW_PERM_READ)) { + if (torrent.get_flag()) { + if (obj.key.instance.empty()) { + action = rgw::IAM::s3GetObjectTorrent; + } else { + action = rgw::IAM::s3GetObjectVersionTorrent; + } + } else { + if (obj.key.instance.empty()) { + action = rgw::IAM::s3GetObject; + } else { + action = rgw::IAM::s3GetObjectVersion; + } + } + + if (!verify_object_permission(s, action)) { return -EACCES; } @@ -879,7 +901,8 @@ bool RGWOp::generate_cors_headers(string& origin, string& method, string& header int RGWGetObj::read_user_manifest_part(rgw_bucket& bucket, const rgw_bucket_dir_entry& ent, - RGWAccessControlPolicy * const bucket_policy, + RGWAccessControlPolicy * const bucket_acl, + const optional& bucket_policy, const off_t start_ofs, const off_t end_ofs) { @@ -954,8 +977,8 @@ int RGWGetObj::read_user_manifest_part(rgw_bucket& bucket, ldout(s->cct, 2) << "overriding permissions due to system operation" << dendl; } else if (s->auth.identity->is_admin_of(s->user->user_id)) { ldout(s->cct, 2) << "overriding permissions due to admin operation" << dendl; - } else if (!verify_object_permission(s, s->user_acl.get(), bucket_policy, - &obj_policy, RGW_PERM_READ)) { + } else if (!verify_object_permission(s, part, s->user_acl.get(), bucket_acl, + &obj_policy, bucket_policy, action)) { return -EPERM; } @@ -977,13 +1000,15 @@ static int iterate_user_manifest_parts(CephContext * const cct, const off_t end, RGWBucketInfo *pbucket_info, const string& obj_prefix, - RGWAccessControlPolicy * const bucket_policy, + RGWAccessControlPolicy * const bucket_acl, + const optional& bucket_policy, uint64_t * const ptotal_len, uint64_t * const pobj_size, string * const pobj_sum, int (*cb)(rgw_bucket& bucket, const rgw_bucket_dir_entry& ent, - RGWAccessControlPolicy * const bucket_policy, + RGWAccessControlPolicy * const bucket_acl, + const optional& bucket_policy, off_t start_ofs, off_t end_ofs, void *param), @@ -1039,7 +1064,7 @@ static int iterate_user_manifest_parts(CephContext * const cct, len_count += end_ofs - start_ofs; if (cb) { - r = cb(bucket, ent, bucket_policy, start_ofs, end_ofs, cb_param); + r = cb(bucket, ent, bucket_acl, bucket_policy, start_ofs, end_ofs, cb_param); if (r < 0) { return r; } @@ -1065,13 +1090,12 @@ static int iterate_user_manifest_parts(CephContext * const cct, } struct rgw_slo_part { - RGWAccessControlPolicy *bucket_policy; + RGWAccessControlPolicy *bucket_acl = nullptr; + Policy* bucket_policy = nullptr; rgw_bucket bucket; string obj_name; - uint64_t size; + uint64_t size = 0; string etag; - - rgw_slo_part() : bucket_policy(NULL), size(0) {} }; static int iterate_slo_parts(CephContext *cct, @@ -1081,7 +1105,8 @@ static int iterate_slo_parts(CephContext *cct, map& slo_parts, int (*cb)(rgw_bucket& bucket, const rgw_bucket_dir_entry& ent, - RGWAccessControlPolicy *bucket_policy, + RGWAccessControlPolicy *bucket_acl, + const optional& bucket_policy, off_t start_ofs, off_t end_ofs, void *param), @@ -1130,8 +1155,12 @@ static int iterate_slo_parts(CephContext *cct, if (found_start) { if (cb) { - int r = cb(part.bucket, ent, part.bucket_policy, start_ofs, end_ofs, cb_param); - if (r < 0) + // SLO is a Swift thing, and Swift has no knowledge of S3 Policies. + int r = cb(part.bucket, ent, part.bucket_acl, + (part.bucket_policy ? + optional(*part.bucket_policy) : none), + start_ofs, end_ofs, cb_param); + if (r < 0) return r; } } @@ -1144,13 +1173,14 @@ static int iterate_slo_parts(CephContext *cct, static int get_obj_user_manifest_iterate_cb(rgw_bucket& bucket, const rgw_bucket_dir_entry& ent, - RGWAccessControlPolicy * const bucket_policy, + RGWAccessControlPolicy * const bucket_acl, + const optional& bucket_policy, const off_t start_ofs, const off_t end_ofs, void * const param) { RGWGetObj *op = static_cast(param); - return op->read_user_manifest_part(bucket, ent, bucket_policy, start_ofs, end_ofs); + return op->read_user_manifest_part(bucket, ent, bucket_acl, bucket_policy, start_ofs, end_ofs); } int RGWGetObj::handle_user_manifest(const char *prefix) @@ -1172,8 +1202,10 @@ int RGWGetObj::handle_user_manifest(const char *prefix) rgw_bucket bucket; - RGWAccessControlPolicy _bucket_policy(s->cct); - RGWAccessControlPolicy *bucket_policy; + RGWAccessControlPolicy _bucket_acl(s->cct); + RGWAccessControlPolicy *bucket_acl; + optional _bucket_policy; + optional* bucket_policy; RGWBucketInfo bucket_info; RGWBucketInfo *pbucket_info; @@ -1190,16 +1222,20 @@ int RGWGetObj::handle_user_manifest(const char *prefix) } bucket = bucket_info.bucket; pbucket_info = &bucket_info; - bucket_policy = &_bucket_policy; - r = read_bucket_policy(store, s, bucket_info, bucket_attrs, bucket_policy, bucket); + bucket_acl = &_bucket_acl; + r = read_bucket_policy(store, s, bucket_info, bucket_attrs, bucket_acl, bucket); if (r < 0) { ldout(s->cct, 0) << "failed to read bucket policy" << dendl; return r; } + _bucket_policy = get_iam_policy_from_attr(s->cct, store, bucket_attrs, + bucket_info.bucket.tenant); + bucket_policy = &_bucket_policy; } else { bucket = s->bucket; pbucket_info = &s->bucket_info; - bucket_policy = s->bucket_acl; + bucket_acl = s->bucket_acl; + bucket_policy = &s->iam_policy; } /* dry run to find out: @@ -1207,7 +1243,7 @@ int RGWGetObj::handle_user_manifest(const char *prefix) * - overall DLO's content size, * - md5 sum of overall DLO's content (for etag of Swift API). */ int r = iterate_user_manifest_parts(s->cct, store, ofs, end, - pbucket_info, obj_prefix, bucket_policy, + pbucket_info, obj_prefix, bucket_acl, *bucket_policy, &total_len, &s->obj_size, &lo_etag, nullptr /* cb */, nullptr /* cb arg */); if (r < 0) { @@ -1221,7 +1257,7 @@ int RGWGetObj::handle_user_manifest(const char *prefix) } r = iterate_user_manifest_parts(s->cct, store, ofs, end, - pbucket_info, obj_prefix, bucket_policy, + pbucket_info, obj_prefix, bucket_acl, *bucket_policy, nullptr, nullptr, nullptr, get_obj_user_manifest_iterate_cb, (void *)this); if (r < 0) { @@ -1248,8 +1284,8 @@ int RGWGetObj::handle_slo_manifest(bufferlist& bl) } ldout(s->cct, 2) << "RGWGetObj::handle_slo_manifest()" << dendl; - list allocated_policies; - map policies; + vector allocated_acls; + map>> policies; map buckets; map slo_parts; @@ -1281,16 +1317,18 @@ int RGWGetObj::handle_slo_manifest(bufferlist& bl) string obj_name = path.substr(pos_sep + 1); rgw_bucket bucket; - RGWAccessControlPolicy *bucket_policy; + RGWAccessControlPolicy *bucket_acl; + Policy* bucket_policy; if (bucket_name.compare(s->bucket.name) != 0) { const auto& piter = policies.find(bucket_name); if (piter != policies.end()) { - bucket_policy = piter->second; - bucket = buckets[bucket_name]; + bucket_acl = piter->second.first; + bucket_policy = piter->second.second.get_ptr(); + bucket = buckets[bucket_name]; } else { - allocated_policies.push_back(RGWAccessControlPolicy(s->cct)); - RGWAccessControlPolicy& _bucket_policy = allocated_policies.back(); + allocated_acls.push_back(RGWAccessControlPolicy(s->cct)); + RGWAccessControlPolicy& _bucket_acl = allocated_acls.back(); RGWBucketInfo bucket_info; map bucket_attrs; @@ -1304,23 +1342,28 @@ int RGWGetObj::handle_slo_manifest(bufferlist& bl) return r; } bucket = bucket_info.bucket; - bucket_policy = &_bucket_policy; - r = read_bucket_policy(store, s, bucket_info, bucket_attrs, bucket_policy, + bucket_acl = &_bucket_acl; + r = read_bucket_policy(store, s, bucket_info, bucket_attrs, bucket_acl, bucket); if (r < 0) { - ldout(s->cct, 0) << "failed to read bucket policy for bucket " + ldout(s->cct, 0) << "failed to read bucket ACL for bucket " << bucket << dendl; return r; - } - buckets[bucket_name] = bucket; - policies[bucket_name] = bucket_policy; + } + auto _bucket_policy = get_iam_policy_from_attr( + s->cct, store, bucket_attrs, bucket_info.bucket.tenant); + bucket_policy = _bucket_policy.get_ptr(); + buckets[bucket_name] = bucket; + policies[bucket_name] = make_pair(bucket_acl, _bucket_policy); } } else { bucket = s->bucket; - bucket_policy = s->bucket_acl; + bucket_acl = s->bucket_acl; + bucket_policy = s->iam_policy.get_ptr(); } rgw_slo_part part; + part.bucket_acl = bucket_acl; part.bucket_policy = bucket_policy; part.bucket = bucket; part.obj_name = obj_name; @@ -1962,7 +2005,8 @@ void RGWDeleteBucketWebsite::execute() int RGWStatBucket::verify_permission() { - if (!verify_bucket_permission(s, RGW_PERM_READ)) { + // This (a HEAD request on a bucket) is governed by the s3:ListBucket permission. + if (!verify_bucket_permission(s, rgw::IAM::s3ListBucket)) { return -EACCES; } @@ -2001,7 +2045,15 @@ void RGWStatBucket::execute() int RGWListBucket::verify_permission() { - if (!verify_bucket_permission(s, RGW_PERM_READ)) { + op_ret = get_params(); + if (op_ret < 0) { + return op_ret; + } + + if (!verify_bucket_permission(s, + list_versions ? + rgw::IAM::s3ListBucketVersions : + rgw::IAM::s3ListBucket)) { return -EACCES; } @@ -2039,10 +2091,6 @@ void RGWListBucket::execute() return; } - op_ret = get_params(); - if (op_ret < 0) - return; - if (need_container_stats()) { map m; m[s->bucket.name] = RGWBucketEnt(); @@ -2565,7 +2613,7 @@ void RGWCreateBucket::execute() int RGWDeleteBucket::verify_permission() { - if (!verify_bucket_permission(s, RGW_PERM_WRITE)) { + if (!verify_bucket_permission(s, rgw::IAM::s3DeleteBucket)) { return -EACCES; } @@ -2662,7 +2710,8 @@ int RGWPutObj::verify_permission() { if (copy_source) { - RGWAccessControlPolicy cs_policy(s->cct); + RGWAccessControlPolicy cs_acl(s->cct); + optional policy; map cs_attrs; rgw_bucket cs_bucket(copy_source_bucket_info.bucket); rgw_obj_key cs_object(copy_source_object_name, copy_source_version_id); @@ -2672,19 +2721,45 @@ int RGWPutObj::verify_permission() store->set_prefetch_data(s->obj_ctx, obj); /* check source object permissions */ - if (read_obj_policy(store, s, copy_source_bucket_info, cs_attrs, &cs_policy, cs_bucket, cs_object) < 0) { + if (read_obj_policy(store, s, copy_source_bucket_info, cs_attrs, &cs_acl, policy, + cs_bucket, cs_object) < 0) { return -EACCES; } /* admin request overrides permission checks */ - if (! s->auth.identity->is_admin_of(cs_policy.get_owner().get_id()) && - ! cs_policy.verify_permission(*s->auth.identity, s->perm_mask, RGW_PERM_READ)) { - return -EACCES; + if (! s->auth.identity->is_admin_of(cs_acl.get_owner().get_id())) { + if (policy) { + auto e = policy->eval(s->env, *s->auth.identity, + cs_object.instance.empty() ? + rgw::IAM::s3GetObject : + rgw::IAM::s3GetObjectVersion, + rgw::IAM::ARN(obj)); + if (e == Effect::Deny) { + return -EACCES; + } else if (e == Effect::Pass && + !cs_acl.verify_permission(*s->auth.identity, s->perm_mask, + RGW_PERM_READ)) { + return -EACCES; + } + } else if (!cs_acl.verify_permission(*s->auth.identity, s->perm_mask, + RGW_PERM_READ)) { + return -EACCES; + } } + } + if (s->iam_policy) { + auto e = s->iam_policy->eval(s->env, *s->auth.identity, + rgw::IAM::s3PutObject, + rgw_obj(s->bucket, s->object)); + if (e == Effect::Allow) { + return 0; + } else if (e == Effect::Deny) { + return -EACCES; + } } - if (!verify_bucket_permission(s, RGW_PERM_WRITE)) { + if (!verify_bucket_permission_no_policy(s, RGW_PERM_WRITE)) { return -EACCES; } @@ -3376,7 +3451,18 @@ void RGWPostObj::execute() return; } - if (!verify_bucket_permission(s, RGW_PERM_WRITE)) { + if (s->iam_policy) { + 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)) { + op_ret = -EACCES; + return; + } + } else if (!verify_bucket_permission_no_policy(s, RGW_PERM_WRITE)) { op_ret = -EACCES; return; } @@ -3642,7 +3728,7 @@ void RGWPutMetadataAccount::execute() int RGWPutMetadataBucket::verify_permission() { - if (!verify_bucket_permission(s, RGW_PERM_WRITE)) { + if (!verify_bucket_permission_no_policy(s, RGW_PERM_WRITE)) { return -EACCES; } @@ -3721,7 +3807,9 @@ void RGWPutMetadataBucket::execute() int RGWPutMetadataObject::verify_permission() { - if (!verify_object_permission(s, RGW_PERM_WRITE)) { + // This looks to be something specific to Swift. We could add + // operations like swift:PutMetadataObject to the Policy Engine. + if (!verify_object_permission_no_policy(s, RGW_PERM_WRITE)) { return -EACCES; } @@ -3832,7 +3920,19 @@ int RGWDeleteObj::handle_slo_manifest(bufferlist& bl) int RGWDeleteObj::verify_permission() { - if (!verify_bucket_permission(s, RGW_PERM_WRITE)) { + if (s->iam_policy) { + auto r = s->iam_policy->eval(s->env, *s->auth.identity, + s->object.instance.empty() ? + rgw::IAM::s3DeleteObject : + rgw::IAM::s3DeleteObjectVersion, + ARN(s->bucket, s->object.name)); + if (r == Effect::Allow) + return true; + else if (r == Effect::Deny) + return false; + } + + if (!verify_bucket_permission_no_policy(s, RGW_PERM_WRITE)) { return -EACCES; } @@ -3982,7 +4082,8 @@ bool RGWCopyObj::parse_copy_location(const string& url_src, string& bucket_name, int RGWCopyObj::verify_permission() { - RGWAccessControlPolicy src_policy(s->cct); + RGWAccessControlPolicy src_acl(s->cct); + optional src_policy; op_ret = get_params(); if (op_ret < 0) return op_ret; @@ -4017,17 +4118,32 @@ int RGWCopyObj::verify_permission() store->set_prefetch_data(s->obj_ctx, src_obj); /* check source object permissions */ - op_ret = read_obj_policy(store, s, src_bucket_info, src_attrs, &src_policy, - src_bucket, src_object); + op_ret = read_obj_policy(store, s, src_bucket_info, src_attrs, &src_acl, + src_policy, src_bucket, src_object); if (op_ret < 0) { return op_ret; } /* admin request overrides permission checks */ - if (! s->auth.identity->is_admin_of(src_policy.get_owner().get_id()) && - ! src_policy.verify_permission(*s->auth.identity, s->perm_mask, - RGW_PERM_READ)) { - return -EACCES; + if (!s->auth.identity->is_admin_of(src_acl.get_owner().get_id())) { + if (src_policy) { + auto e = src_policy->eval(s->env, *s->auth.identity, + src_object.instance.empty() ? + rgw::IAM::s3GetObject : + rgw::IAM::s3GetObjectVersion, + ARN(src_obj)); + if (e == Effect::Deny) { + return -EACCES; + } else if (e == Effect::Pass && + !src_acl.verify_permission(*s->auth.identity, s->perm_mask, + RGW_PERM_READ)) { + return -EACCES; + } + } else if (!src_acl.verify_permission(*s->auth.identity, + s->perm_mask, + RGW_PERM_READ)) { + return -EACCES; + } } } @@ -4188,9 +4304,12 @@ int RGWGetACLs::verify_permission() { bool perm; if (!s->object.empty()) { - perm = verify_object_permission(s, RGW_PERM_READ_ACP); + perm = verify_object_permission(s, + s->object.instance.empty() ? + rgw::IAM::s3GetObjectAcl : + rgw::IAM::s3GetObjectVersionAcl); } else { - perm = verify_bucket_permission(s, RGW_PERM_READ_ACP); + perm = verify_bucket_permission(s, rgw::IAM::s3GetObjectAcl); } if (!perm) return -EACCES; @@ -4218,9 +4337,12 @@ int RGWPutACLs::verify_permission() { bool perm; if (!s->object.empty()) { - perm = verify_object_permission(s, RGW_PERM_WRITE_ACP); + perm = verify_object_permission(s, + s->object.instance.empty() ? + rgw::IAM::s3PutObjectAcl : + rgw::IAM::s3PutObjectVersionAcl); } else { - perm = verify_bucket_permission(s, RGW_PERM_WRITE_ACP); + perm = verify_bucket_permission(s, rgw::IAM::s3PutBucketAcl); } if (!perm) return -EACCES; @@ -4231,7 +4353,7 @@ int RGWPutACLs::verify_permission() int RGWGetLC::verify_permission() { bool perm; - perm = verify_bucket_permission(s, RGW_PERM_READ_ACP); + perm = verify_bucket_permission(s, rgw::IAM::s3GetLifecycleConfiguration); if (!perm) return -EACCES; @@ -4241,7 +4363,7 @@ int RGWGetLC::verify_permission() int RGWPutLC::verify_permission() { bool perm; - perm = verify_bucket_permission(s, RGW_PERM_WRITE_ACP); + perm = verify_bucket_permission(s, rgw::IAM::s3PutLifecycleConfiguration); if (!perm) return -EACCES; @@ -4251,7 +4373,7 @@ int RGWPutLC::verify_permission() int RGWDeleteLC::verify_permission() { bool perm; - perm = verify_bucket_permission(s, RGW_PERM_WRITE_ACP); + perm = verify_bucket_permission(s, rgw::IAM::s3PutLifecycleConfiguration); if (!perm) return -EACCES; @@ -4717,8 +4839,20 @@ void RGWSetRequestPayment::execute() int RGWInitMultipart::verify_permission() { - if (!verify_bucket_permission(s, RGW_PERM_WRITE)) + if (s->iam_policy) { + auto e = s->iam_policy->eval(s->env, *s->auth.identity, + rgw::IAM::s3PutObject, + rgw_obj(s->bucket, s->object)); + if (e == Effect::Allow) { + return 0; + } else if (e == Effect::Deny) { + return -EACCES; + } + } + + if (!verify_bucket_permission_no_policy(s, RGW_PERM_WRITE)) { return -EACCES; + } return 0; } @@ -4822,8 +4956,20 @@ static int get_multipart_info(RGWRados *store, struct req_state *s, int RGWCompleteMultipart::verify_permission() { - if (!verify_bucket_permission(s, RGW_PERM_WRITE)) + if (s->iam_policy) { + auto e = s->iam_policy->eval(s->env, *s->auth.identity, + rgw::IAM::s3PutObject, + rgw_obj(s->bucket, s->object)); + if (e == Effect::Allow) { + return 0; + } else if (e == Effect::Deny) { + return -EACCES; + } + } + + if (!verify_bucket_permission_no_policy(s, RGW_PERM_WRITE)) { return -EACCES; + } return 0; } @@ -5068,8 +5214,20 @@ void RGWCompleteMultipart::execute() int RGWAbortMultipart::verify_permission() { - if (!verify_bucket_permission(s, RGW_PERM_WRITE)) + if (s->iam_policy) { + auto e = s->iam_policy->eval(s->env, *s->auth.identity, + rgw::IAM::s3AbortMultipartUpload, + rgw_obj(s->bucket, s->object)); + if (e == Effect::Allow) { + return 0; + } else if (e == Effect::Deny) { + return -EACCES; + } + } + + if (!verify_bucket_permission_no_policy(s, RGW_PERM_WRITE)) { return -EACCES; + } return 0; } @@ -5105,7 +5263,7 @@ void RGWAbortMultipart::execute() int RGWListMultipart::verify_permission() { - if (!verify_object_permission(s, RGW_PERM_READ)) + if (!verify_object_permission(s, rgw::IAM::s3ListMultipartUploadParts)) return -EACCES; return 0; @@ -5139,7 +5297,8 @@ void RGWListMultipart::execute() int RGWListBucketMultiparts::verify_permission() { - if (!verify_bucket_permission(s, RGW_PERM_READ)) + if (!verify_bucket_permission(s, + rgw::IAM::s3ListBucketMultiPartUploads)) return -EACCES; return 0; @@ -5211,7 +5370,8 @@ void RGWGetHealthCheck::execute() int RGWDeleteMultiObj::verify_permission() { - if (!verify_bucket_permission(s, RGW_PERM_WRITE)) + acl_allowed = verify_bucket_permission_no_policy(s, RGW_PERM_WRITE); + if (!acl_allowed && !s->iam_policy) return -EACCES; return 0; @@ -5268,6 +5428,19 @@ void RGWDeleteMultiObj::execute() iter != multi_delete->objects.end() && num_processed < max_to_delete; ++iter, num_processed++) { rgw_obj obj(bucket, *iter); + if (s->iam_policy) { + auto e = s->iam_policy->eval(s->env, + *s->auth.identity, + iter->instance.empty() ? + rgw::IAM::s3DeleteObject : + rgw::IAM::s3DeleteObjectVersion, + obj); + if ((e == Effect::Deny) || + (e == Effect::Pass && !acl_allowed)) { + send_partial_response(*iter, false, "", -EACCES); + continue; + } + } obj_ctx->obj.set_atomic(obj); @@ -5314,11 +5487,14 @@ bool RGWBulkDelete::Deleter::verify_permission(RGWBucketInfo& binfo, return false; } + auto policy = get_iam_policy_from_attr(s->cct, store, battrs, binfo.bucket.tenant); + bucket_owner = bacl.get_owner(); /* 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, s->user_acl.get(), &bacl, RGW_PERM_WRITE); + return verify_bucket_permission(s, binfo.bucket, s->user_acl.get(), + &bacl, policy, rgw::IAM::s3DeleteBucket); } bool RGWBulkDelete::Deleter::delete_single(const acct_path_t& path) @@ -5531,7 +5707,7 @@ RGWBulkUploadOp::parse_path(const boost::string_ref& path) } } - return boost::none; + return none; } std::pair @@ -5752,6 +5928,7 @@ int RGWBulkUploadOp::handle_dir(const boost::string_ref path) bool RGWBulkUploadOp::handle_file_verify_permission(RGWBucketInfo& binfo, + const rgw_obj& obj, std::map& battrs, ACLOwner& bucket_owner /* out */) { @@ -5763,8 +5940,21 @@ bool RGWBulkUploadOp::handle_file_verify_permission(RGWBucketInfo& binfo, return false; } + auto policy = get_iam_policy_from_attr(s->cct, store, battrs, binfo.bucket.tenant); + bucket_owner = bacl.get_owner(); - return verify_bucket_permission(s, s->user_acl.get(), &bacl, RGW_PERM_WRITE); + if (policy) { + 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; + } + } + + return verify_bucket_permission_no_policy(s, s->user_acl.get(), + &bacl, RGW_PERM_WRITE); } int RGWBulkUploadOp::handle_file(const boost::string_ref path, @@ -5800,7 +5990,9 @@ int RGWBulkUploadOp::handle_file(const boost::string_ref path, return op_ret; } - if (! handle_file_verify_permission(binfo, battrs, bowner)) { + if (! handle_file_verify_permission(binfo, + rgw_obj(binfo.bucket, object), + battrs, bowner)) { ldout(s->cct, 20) << "bulk upload: object creation unauthorized" << dendl; op_ret = -EACCES; return op_ret; @@ -6050,11 +6242,13 @@ ssize_t RGWBulkUploadOp::AlignedStreamGetter::get_exactly(const size_t want, int RGWSetAttrs::verify_permission() { + // This looks to be part of the RGW-NFS machinery and has no S3 or + // Swift equivalent. bool perm; if (!s->object.empty()) { - perm = verify_object_permission(s, RGW_PERM_WRITE); + perm = verify_object_permission_no_policy(s, RGW_PERM_WRITE); } else { - perm = verify_bucket_permission(s, RGW_PERM_WRITE); + perm = verify_bucket_permission_no_policy(s, RGW_PERM_WRITE); } if (!perm) return -EACCES; diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h index 290a4263b34..e0d3aacb5e3 100644 --- a/src/rgw/rgw_op.h +++ b/src/rgw/rgw_op.h @@ -213,6 +213,7 @@ protected: bool first_data; uint64_t cur_ofs; bufferlist waiting; + uint64_t action = 0; int init_common(); public: @@ -251,11 +252,13 @@ public: int verify_permission() override; void pre_exec() override; void execute() override; - int read_user_manifest_part(rgw_bucket& bucket, - const rgw_bucket_dir_entry& ent, - RGWAccessControlPolicy *bucket_policy, - off_t start_ofs, - off_t end_ofs); + int read_user_manifest_part( + rgw_bucket& bucket, + const rgw_bucket_dir_entry& ent, + RGWAccessControlPolicy * const bucket_acl, + const boost::optional& bucket_policy, + const off_t start_ofs, + const off_t end_ofs); int handle_user_manifest(const char *prefix); int handle_slo_manifest(bufferlist& bl); @@ -434,7 +437,8 @@ protected: handle_upload_path(struct req_state *s); bool handle_file_verify_permission(RGWBucketInfo& binfo, - std::map& battrs, + const rgw_obj& obj, + std::map& battrs, ACLOwner& bucket_owner /* out */); int handle_file(boost::string_ref path, size_t size, @@ -1697,6 +1701,7 @@ protected: rgw_bucket bucket; bool quiet; bool status_dumped; + bool acl_allowed = false; public: RGWDeleteMultiObj() {