From b5c9d9d83ee11bf1d6783a935da2c73ed5d16199 Mon Sep 17 00:00:00 2001 From: Mark Houghton Date: Thu, 15 Oct 2020 12:13:50 +0100 Subject: [PATCH] rgw: Check S3 object lock date in multi-object delete Multi-object delete (via the S3 API) will now check each object's retention date in the same way as single object delet does. Fixes: http://tracker.ceph.com/issues/47586 Signed-off-by: Mark Houghton (cherry picked from commit 1a3f08550813e719b34a8133b83eefa97dd43d3a) Signed-off-by: Matt Benjamin Conflicts: src/rgw/rgw_common.h src/rgw/rgw_common.cc src/rgw/rgw_op.cc Signed-off-by: Matt Benjamin --- src/rgw/rgw_common.cc | 33 +++++++++++++++++++++++ src/rgw/rgw_common.h | 3 +++ src/rgw/rgw_op.cc | 62 +++++++++++++++++++++---------------------- 3 files changed, 66 insertions(+), 32 deletions(-) diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc index 567b80ca6c8ec..0abd7e0c26f96 100644 --- a/src/rgw/rgw_common.cc +++ b/src/rgw/rgw_common.cc @@ -1352,6 +1352,39 @@ bool verify_object_permission(const DoutPrefixProvider* dpp, struct req_state *s op); } +int verify_object_lock(const DoutPrefixProvider* dpp, const map& attrs, const bool bypass_perm, const bool bypass_governance_mode) { + auto aiter = attrs.find(RGW_ATTR_OBJECT_RETENTION); + if (aiter != attrs.end()) { + RGWObjectRetention obj_retention; + try { + decode(obj_retention, aiter->second); + } catch (buffer::error& err) { + ldpp_dout(dpp, 0) << "ERROR: failed to decode RGWObjectRetention" << dendl; + return -EIO; + } + if (ceph::real_clock::to_time_t(obj_retention.get_retain_until_date()) > ceph_clock_now()) { + if (obj_retention.get_mode().compare("GOVERNANCE") != 0 || !bypass_perm || !bypass_governance_mode) { + return -EACCES; + } + } + } + aiter = attrs.find(RGW_ATTR_OBJECT_LEGAL_HOLD); + if (aiter != attrs.end()) { + RGWObjectLegalHold obj_legal_hold; + try { + decode(obj_legal_hold, aiter->second); + } catch (buffer::error& err) { + ldpp_dout(dpp, 0) << "ERROR: failed to decode RGWObjectLegalHold" << dendl; + return -EIO; + } + if (obj_legal_hold.is_enabled()) { + return -EACCES; + } + } + + return 0; +} + class HexTable { char table[256]; diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h index 3911ab186481a..18f69e7950dd2 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -2569,6 +2569,9 @@ extern bool verify_object_permission_no_policy( int perm); extern bool verify_object_permission_no_policy(const DoutPrefixProvider* dpp, struct req_state *s, int perm); + +int verify_object_lock(const DoutPrefixProvider* dpp, const map& attrs, const bool bypass_perm, const bool bypass_governance_mode); + /** 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 80d29ede6edce..01e2a7929a1b6 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -4691,38 +4691,11 @@ void RGWDeleteObj::execute() } if (check_obj_lock) { - auto aiter = attrs.find(RGW_ATTR_OBJECT_RETENTION); - if (aiter != attrs.end()) { - RGWObjectRetention obj_retention; - try { - decode(obj_retention, aiter->second); - } catch (buffer::error& err) { - ldpp_dout(this, 0) << "ERROR: failed to decode RGWObjectRetention" << dendl; - op_ret = -EIO; - return; - } - if (ceph::real_clock::to_time_t(obj_retention.get_retain_until_date()) > ceph_clock_now()) { - if (obj_retention.get_mode().compare("GOVERNANCE") != 0 || !bypass_perm || !bypass_governance_mode) { - op_ret = -EACCES; - return; - } - } - } - aiter = attrs.find(RGW_ATTR_OBJECT_LEGAL_HOLD); - if (aiter != attrs.end()) { - RGWObjectLegalHold obj_legal_hold; - try { - decode(obj_legal_hold, aiter->second); - } catch (buffer::error& err) { - ldpp_dout(this, 0) << "ERROR: failed to decode RGWObjectLegalHold" << dendl; - op_ret = -EIO; - return; - } - if (obj_legal_hold.is_enabled()) { - op_ret = -EACCES; - return; - } - } + int object_lock_response = verify_object_lock(this, attrs, bypass_perm, bypass_governance_mode); + if (object_lock_response != 0) { + op_ret = object_lock_response; + return; + } } if (multipart_delete) { @@ -6419,6 +6392,7 @@ void RGWDeleteMultiObj::execute() iter != multi_delete->objects.end(); ++iter) { rgw_obj obj(bucket, *iter); + map attrs; if (s->iam_policy || ! s->iam_user_policies.empty()) { auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env, boost::none, @@ -6447,6 +6421,30 @@ void RGWDeleteMultiObj::execute() } } + // verify_object_lock + bool check_obj_lock = obj.key.have_instance() && s->bucket_info.obj_lock_enabled(); + if (check_obj_lock) { + int get_attrs_response = get_obj_attrs(store, s, obj, attrs); + if (get_attrs_response < 0) { + if (get_attrs_response == -ENOENT) { + // object maybe delete_marker, skip check_obj_lock + check_obj_lock = false; + } else { + // Something went wrong. + send_partial_response(*iter, false, "", get_attrs_response); + continue; + } + } + } + + if (check_obj_lock) { + int object_lock_response = verify_object_lock(this, attrs, bypass_perm, bypass_governance_mode); + if (object_lock_response != 0) { + send_partial_response(*iter, false, "", object_lock_response); + continue; + } + } + obj_ctx->set_atomic(obj); RGWRados::Object del_target(store, s->bucket_info, *obj_ctx, obj); -- 2.39.5