From d7fe7915b452c5639b415d6457e272fe0d235ef5 Mon Sep 17 00:00:00 2001 From: Seena Fallah Date: Thu, 20 Feb 2025 22:15:31 +0100 Subject: [PATCH] rgw: verify perm on delete replication Check for s3:ReplicateDelete for replicating object deletes and delete markers when pipe is set to user mode. Signed-off-by: Seena Fallah --- src/rgw/driver/rados/rgw_cr_rados.cc | 48 ++++++++++++++++++++++++- src/rgw/driver/rados/rgw_cr_rados.h | 52 ++++++++++++++------------- src/rgw/driver/rados/rgw_data_sync.cc | 6 ++-- src/rgw/rgw_iam_policy.cc | 4 +++ src/rgw/rgw_iam_policy.h | 2 ++ 5 files changed, 84 insertions(+), 28 deletions(-) diff --git a/src/rgw/driver/rados/rgw_cr_rados.cc b/src/rgw/driver/rados/rgw_cr_rados.cc index 9e58aad4216db..80dc958157ba4 100644 --- a/src/rgw/driver/rados/rgw_cr_rados.cc +++ b/src/rgw/driver/rados/rgw_cr_rados.cc @@ -925,9 +925,55 @@ int RGWAsyncRemoveObj::_send_request(const DoutPrefixProvider *dpp) return 0; } - RGWAccessControlPolicy policy; + RGWObjTags obj_tags; + bufferlist bl_tag; + if (obj->get_attr(RGW_ATTR_TAGS, bl_tag)) { + auto bliter = bl_tag.cbegin(); + try { + obj_tags.decode(bliter); + } catch (buffer::error &err) { + ldpp_dout(dpp, 0) << "ERROR: " << __func__ << ": caught buffer::error couldn't decode TagSet " << dendl; + return -EIO; + } + } + + rgw_sync_pipe_params params; + if (!sync_pipe.info.handler.find_obj_params(obj->get_key(), + obj_tags.get_tags(), + ¶ms)) { + return -ERR_PRECONDITION_FAILED; + } + + if (params.mode == rgw_sync_pipe_params::MODE_USER) { + std::optional user_perms; + RGWUserPermHandler::Bucket dest_bucket_perms; + + if (params.user.empty()) { + ldpp_dout(dpp, 20) << "ERROR: " << __func__ << ": user level sync but user param not set" << dendl; + return -EPERM; + } + user_perms.emplace(dpp, store, dpp->get_cct(), params.user); + + ret = user_perms->init(); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: " << __func__ << ": failed to init user perms for uid=" << params.user << " ret=" << ret << dendl; + return ret; + } + + ret = user_perms->init_bucket(sync_pipe.dest_bucket_info, sync_pipe.dest_bucket_attrs, &dest_bucket_perms); + if (ret < 0) { + ldpp_dout(dpp, 0) << "ERROR: " << __func__ << ": failed to init bucket perms for uid=" << params.user << " bucket=" << bucket->get_key() << " ret=" << ret << dendl; + return ret; + } + + if (!dest_bucket_perms.verify_bucket_permission(obj->get_key(), rgw::IAM::s3ReplicateDelete)) { + ldpp_dout(dpp, 20) << "ERROR: " << __func__ << ": user does not have permission to delete object" << dendl; + return -EPERM; + } + } /* decode policy */ + RGWAccessControlPolicy policy; bufferlist bl; if (obj->get_attr(RGW_ATTR_ACL, bl)) { auto bliter = bl.cbegin(); diff --git a/src/rgw/driver/rados/rgw_cr_rados.h b/src/rgw/driver/rados/rgw_cr_rados.h index 412de94dd9332..470849b168014 100644 --- a/src/rgw/driver/rados/rgw_cr_rados.h +++ b/src/rgw/driver/rados/rgw_cr_rados.h @@ -1360,7 +1360,9 @@ public: class RGWAsyncRemoveObj : public RGWAsyncRadosRequest { const DoutPrefixProvider *dpp; rgw::sal::RadosStore* store; + CephContext *cct; rgw_zone_id source_zone; + rgw_bucket_sync_pipe& sync_pipe; std::unique_ptr bucket; std::unique_ptr obj; @@ -1379,25 +1381,27 @@ protected: int _send_request(const DoutPrefixProvider *dpp) override; public: RGWAsyncRemoveObj(const DoutPrefixProvider *_dpp, RGWCoroutine *caller, RGWAioCompletionNotifier *cn, - rgw::sal::RadosStore* _store, - const rgw_zone_id& _source_zone, - RGWBucketInfo& _bucket_info, - const rgw_obj_key& _key, - const std::string& _owner, - const std::string& _owner_display_name, - bool _versioned, - uint64_t _versioned_epoch, - bool _delete_marker, - bool _if_older, - real_time& _timestamp, - rgw_zone_set* _zones_trace) : RGWAsyncRadosRequest(caller, cn), dpp(_dpp), store(_store), - source_zone(_source_zone), - owner(_owner), - owner_display_name(_owner_display_name), - versioned(_versioned), - versioned_epoch(_versioned_epoch), - del_if_older(_if_older), - timestamp(_timestamp) { + rgw::sal::RadosStore* _store, + CephContext *_cct, + const rgw_zone_id& _source_zone, + rgw_bucket_sync_pipe& _sync_pipe, + const rgw_obj_key& _key, + const std::string& _owner, + const std::string& _owner_display_name, + bool _versioned, + uint64_t _versioned_epoch, + bool _delete_marker, + bool _if_older, + real_time& _timestamp, + rgw_zone_set* _zones_trace) : RGWAsyncRadosRequest(caller, cn), dpp(_dpp), store(_store), cct(_cct), + source_zone(_source_zone), + sync_pipe(_sync_pipe), + owner(_owner), + owner_display_name(_owner_display_name), + versioned(_versioned), + versioned_epoch(_versioned_epoch), + del_if_older(_if_older), + timestamp(_timestamp) { if (_delete_marker) { marker_version_id = _key.instance; } @@ -1405,7 +1409,7 @@ public: if (_zones_trace) { zones_trace = *_zones_trace; } - bucket = store->get_bucket(_bucket_info); + bucket = store->get_bucket(sync_pipe.dest_bucket_info); obj = bucket->get_object(_key); } }; @@ -1417,7 +1421,7 @@ class RGWRemoveObjCR : public RGWSimpleCoroutine { rgw::sal::RadosStore* store; rgw_zone_id source_zone; - RGWBucketInfo bucket_info; + rgw_bucket_sync_pipe& sync_pipe; rgw_obj_key key; bool versioned; @@ -1436,7 +1440,7 @@ class RGWRemoveObjCR : public RGWSimpleCoroutine { public: RGWRemoveObjCR(const DoutPrefixProvider *_dpp, RGWAsyncRadosProcessor *_async_rados, rgw::sal::RadosStore* _store, const rgw_zone_id& _source_zone, - RGWBucketInfo& _bucket_info, + rgw_bucket_sync_pipe& _sync_pipe, const rgw_obj_key& _key, bool _versioned, uint64_t _versioned_epoch, @@ -1447,7 +1451,7 @@ public: rgw_zone_set *_zones_trace) : RGWSimpleCoroutine(_store->ctx()), dpp(_dpp), cct(_store->ctx()), async_rados(_async_rados), store(_store), source_zone(_source_zone), - bucket_info(_bucket_info), + sync_pipe(_sync_pipe), key(_key), versioned(_versioned), versioned_epoch(_versioned_epoch), @@ -1477,7 +1481,7 @@ public: } int send_request(const DoutPrefixProvider *dpp) override { - req = new RGWAsyncRemoveObj(dpp, this, stack->create_completion_notifier(), store, source_zone, bucket_info, + req = new RGWAsyncRemoveObj(dpp, this, stack->create_completion_notifier(), store, cct, source_zone, sync_pipe, key, owner, owner_display_name, versioned, versioned_epoch, delete_marker, del_if_older, timestamp, zones_trace); async_rados->queue(req); diff --git a/src/rgw/driver/rados/rgw_data_sync.cc b/src/rgw/driver/rados/rgw_data_sync.cc index 5bad9932fd7b6..af7518c36c900 100644 --- a/src/rgw/driver/rados/rgw_data_sync.cc +++ b/src/rgw/driver/rados/rgw_data_sync.cc @@ -3045,7 +3045,7 @@ RGWCoroutine *RGWDefaultDataSyncModule::remove_object(const DoutPrefixProvider * { auto sync_env = sc->env; return new RGWRemoveObjCR(sync_env->dpp, sync_env->async_rados, sync_env->driver, sc->source_zone, - sync_pipe.dest_bucket_info, key, versioned, versioned_epoch, + sync_pipe, key, versioned, versioned_epoch, NULL, NULL, false, &mtime, zones_trace); } @@ -3054,7 +3054,7 @@ RGWCoroutine *RGWDefaultDataSyncModule::create_delete_marker(const DoutPrefixPro { auto sync_env = sc->env; return new RGWRemoveObjCR(sync_env->dpp, sync_env->async_rados, sync_env->driver, sc->source_zone, - sync_pipe.dest_bucket_info, key, versioned, versioned_epoch, + sync_pipe, key, versioned, versioned_epoch, &owner.id, &owner.display_name, true, &mtime, zones_trace); } @@ -3147,7 +3147,7 @@ RGWCoroutine *RGWArchiveDataSyncModule::create_delete_marker(const DoutPrefixPro << " versioned=" << versioned << " versioned_epoch=" << versioned_epoch << dendl; auto sync_env = sc->env; return new RGWRemoveObjCR(sync_env->dpp, sync_env->async_rados, sync_env->driver, sc->source_zone, - sync_pipe.dest_bucket_info, key, versioned, versioned_epoch, + sync_pipe, key, versioned, versioned_epoch, &owner.id, &owner.display_name, true, &mtime, zones_trace); } diff --git a/src/rgw/rgw_iam_policy.cc b/src/rgw/rgw_iam_policy.cc index caf9150edf5f3..ea85ab4961811 100644 --- a/src/rgw/rgw_iam_policy.cc +++ b/src/rgw/rgw_iam_policy.cc @@ -138,6 +138,7 @@ static const actpair actpairs[] = { "s3:PutReplicationConfiguration", s3PutReplicationConfiguration }, { "s3:RestoreObject", s3RestoreObject }, { "s3:DescribeJob", s3DescribeJob }, + { "s3:ReplicateDelete", s3ReplicateDelete }, { "s3-object-lambda:GetObject", s3objectlambdaGetObject }, { "s3-object-lambda:ListBucket", s3objectlambdaListBucket }, { "iam:PutUserPolicy", iamPutUserPolicy }, @@ -1509,6 +1510,9 @@ const char* action_bit_string(uint64_t action) { case s3DescribeJob: return "s3:DescribeJob"; + case s3ReplicateDelete: + return "s3:ReplicateDelete"; + case s3objectlambdaGetObject: return "s3-object-lambda:GetObject"; diff --git a/src/rgw/rgw_iam_policy.h b/src/rgw/rgw_iam_policy.h index fbee04e2acf4f..e51250041fd95 100644 --- a/src/rgw/rgw_iam_policy.h +++ b/src/rgw/rgw_iam_policy.h @@ -117,6 +117,7 @@ enum { s3DescribeJob, s3GetObjectAttributes, s3GetObjectVersionAttributes, + s3ReplicateDelete, s3All, s3objectlambdaGetObject, @@ -273,6 +274,7 @@ inline int op_to_perm(std::uint64_t op) { case s3PutObjectRetention: case s3PutObjectLegalHold: case s3BypassGovernanceRetention: + case s3ReplicateDelete: return RGW_PERM_WRITE; case s3GetAccelerateConfiguration: -- 2.39.5