]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: verify perm on delete replication
authorSeena Fallah <seenafallah@gmail.com>
Thu, 20 Feb 2025 21:15:31 +0000 (22:15 +0100)
committerCasey Bodley <cbodley@redhat.com>
Tue, 29 Apr 2025 14:22:46 +0000 (10:22 -0400)
Check for s3:ReplicateDelete for replicating object deletes and
delete markers when pipe is set to user mode.

Signed-off-by: Seena Fallah <seenafallah@gmail.com>
(cherry picked from commit d7fe7915b452c5639b415d6457e272fe0d235ef5)

src/rgw/driver/rados/rgw_cr_rados.cc
src/rgw/driver/rados/rgw_cr_rados.h
src/rgw/driver/rados/rgw_data_sync.cc
src/rgw/rgw_iam_policy.cc
src/rgw/rgw_iam_policy.h

index 9e58aad4216db86994a43e2953b3623767a5eb99..80dc958157ba447b0125a7c59e58fb9a65094b0d 100644 (file)
@@ -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(),
+                                              &params)) {
+    return -ERR_PRECONDITION_FAILED;
+  }
+
+  if (params.mode == rgw_sync_pipe_params::MODE_USER) {
+    std::optional<RGWUserPermHandler> 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();
index 412de94dd933223c147ab7ae8ff7d43a58ff4885..470849b16801459bf0c59a8326c6e8edf077f16b 100644 (file)
@@ -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<rgw::sal::Bucket> bucket;
   std::unique_ptr<rgw::sal::Object> 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);
index 5bad9932fd7b6cf132ff5f04be7ddea2e0c2547b..af7518c36c9003a88b54540b8c10e51f3e6ecda3 100644 (file)
@@ -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);
 }
 
index caf9150edf5f314ba41bd1039aef0fd44d8f49d5..ea85ab49618118aaec8ed327e8916036eef06f54 100644 (file)
@@ -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";
 
index fbee04e2acf4fc4aa2c278fd3f8c1c77d0374697..e51250041fd9546f8aecf723527ebe2782d6deaa 100644 (file)
@@ -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: