Skip to content

Commit

Permalink
Merge pull request #37933 from markhoughton-microfocus/issue_47586_fix
Browse files Browse the repository at this point in the history
rgw: check object locks in multi-object delete

Reviewed-by: Matt Benjamin <mbenjamin@redhat.com>
Reviewed-by: Abhishek Lekshmanan <abhishek@suse.com>
  • Loading branch information
ivancich committed Dec 1, 2020
2 parents c0f1c56 + d8c2026 commit bd075b7
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 46 deletions.
35 changes: 35 additions & 0 deletions src/rgw/rgw_common.cc
Expand Up @@ -1490,6 +1490,41 @@ bool verify_object_permission(const DoutPrefixProvider* dpp, struct req_state *s
op);
}


int verify_object_lock(const DoutPrefixProvider* dpp, const rgw::sal::RGWAttrs& 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];
Expand Down
7 changes: 7 additions & 0 deletions src/rgw/rgw_common.h
Expand Up @@ -48,6 +48,7 @@ namespace rgw::sal {
class RGWUser;
class RGWBucket;
class RGWObject;
using RGWAttrs = std::map<std::string, ceph::buffer::list>;
}

using ceph::crypto::MD5;
Expand Down Expand Up @@ -2139,6 +2140,12 @@ 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);
extern int verify_object_lock(
const DoutPrefixProvider* dpp,
const rgw::sal::RGWAttrs& 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);
Expand Down
104 changes: 58 additions & 46 deletions src/rgw/rgw_op.cc
Expand Up @@ -4739,13 +4739,13 @@ void RGWDeleteObj::execute(optional_yield y)
}

if (check_obj_lock) {
/* check if obj exists, read orig attrs */
if (op_ret == -ENOENT) {
/* object maybe delete_marker, skip check_obj_lock*/
check_obj_lock = false;
} else {
return;
}
/* check if obj exists, read orig attrs */
if (op_ret == -ENOENT) {
/* object maybe delete_marker, skip check_obj_lock*/
check_obj_lock = false;
} else {
return;
}
}
} else {
attrs = s->object->get_attrs();
Expand All @@ -4755,37 +4755,10 @@ void RGWDeleteObj::execute(optional_yield y)
op_ret = 0;

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;
}
}

Expand Down Expand Up @@ -6378,10 +6351,31 @@ void RGWGetHealthCheck::execute(optional_yield y)

int RGWDeleteMultiObj::verify_permission(optional_yield y)
{
int op_ret = get_params(y);
if (op_ret) {
return op_ret;
}

if (s->iam_policy || ! s->iam_user_policies.empty()) {
if (s->bucket->get_info().obj_lock_enabled() && bypass_governance_mode) {
auto r = eval_user_policies(s->iam_user_policies, s->env, boost::none,
rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket->get_key()));
if (r == Effect::Deny) {
bypass_perm = false;
} else if (r == Effect::Pass && s->iam_policy) {
r = s->iam_policy->eval(s->env, *s->auth.identity, rgw::IAM::s3BypassGovernanceRetention,
ARN(s->bucket->get_key()));
if (r == Effect::Deny) {
bypass_perm = false;
}
}
}

bool not_versioned = rgw::sal::RGWObject::empty(s->object.get()) || s->object->get_instance().empty();

auto usr_policy_res = eval_user_policies(s->iam_user_policies, s->env,
boost::none,
s->object->get_instance().empty() ?
not_versioned ?
rgw::IAM::s3DeleteObject :
rgw::IAM::s3DeleteObjectVersion,
ARN(s->bucket->get_key()));
Expand All @@ -6392,7 +6386,7 @@ int RGWDeleteMultiObj::verify_permission(optional_yield y)
rgw::IAM::Effect r = Effect::Pass;
if (s->iam_policy) {
r = s->iam_policy->eval(s->env, *s->auth.identity,
s->object->get_instance().empty() ?
not_versioned ?
rgw::IAM::s3DeleteObject :
rgw::IAM::s3DeleteObjectVersion,
ARN(s->bucket->get_key()));
Expand Down Expand Up @@ -6425,11 +6419,6 @@ void RGWDeleteMultiObj::execute(optional_yield y)
RGWObjectCtx *obj_ctx = static_cast<RGWObjectCtx *>(s->obj_ctx);
char* buf;

op_ret = get_params(y);
if (op_ret < 0) {
goto error;
}

buf = data.c_str();
if (!buf) {
op_ret = -EINVAL;
Expand Down Expand Up @@ -6518,7 +6507,30 @@ void RGWDeleteMultiObj::execute(optional_yield y)
continue;
}
}


// verify_object_lock
bool check_obj_lock = obj->have_instance() && bucket->get_info().obj_lock_enabled();
if (check_obj_lock) {
int get_attrs_response = obj->get_obj_attrs(s->obj_ctx, s->yield);
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, obj->get_attrs(), bypass_perm, bypass_governance_mode);
if (object_lock_response != 0) {
send_partial_response(*iter, false, "", object_lock_response);
continue;
}
}
// make reservation for notification if needed
const auto versioned_object = s->bucket->versioning_enabled();
rgw::notify::reservation_t res(store, s, obj.get());
Expand Down
5 changes: 5 additions & 0 deletions src/rgw/rgw_op.h
Expand Up @@ -1907,11 +1907,16 @@ class RGWDeleteMultiObj : public RGWOp {
bool quiet;
bool status_dumped;
bool acl_allowed = false;
bool bypass_perm;
bool bypass_governance_mode;


public:
RGWDeleteMultiObj() {
quiet = false;
status_dumped = false;
bypass_perm = true;
bypass_governance_mode = false;
}
int verify_permission(optional_yield y) override;
void pre_exec() override;
Expand Down
6 changes: 6 additions & 0 deletions src/rgw/rgw_rest_s3.cc
Expand Up @@ -3870,6 +3870,12 @@ int RGWDeleteMultiObj_ObjStore_S3::get_params(optional_yield y)
return ret;
}

const char *bypass_gov_header = s->info.env->get("HTTP_X_AMZ_BYPASS_GOVERNANCE_RETENTION");
if (bypass_gov_header) {
std::string bypass_gov_decoded = url_decode(bypass_gov_header);
bypass_governance_mode = boost::algorithm::iequals(bypass_gov_decoded, "true");
}

return do_aws4_auth_completion();
}

Expand Down

0 comments on commit bd075b7

Please sign in to comment.