]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_op.cc
0fa2ffc85aef9d3d324787a08163af1a898add92
[ceph.git] / ceph / src / rgw / rgw_op.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
3
4 #include <errno.h>
5 #include <stdlib.h>
6 #include <system_error>
7 #include <unistd.h>
8
9 #include <sstream>
10 #include <string_view>
11
12 #include <boost/algorithm/string/predicate.hpp>
13 #include <boost/optional.hpp>
14 #include <boost/utility/in_place_factory.hpp>
15
16 #include "include/scope_guard.h"
17 #include "common/Clock.h"
18 #include "common/armor.h"
19 #include "common/errno.h"
20 #include "common/mime.h"
21 #include "common/utf8.h"
22 #include "common/ceph_json.h"
23 #include "common/static_ptr.h"
24 #include "rgw_tracer.h"
25
26 #include "rgw_rados.h"
27 #include "rgw_zone.h"
28 #include "rgw_op.h"
29 #include "rgw_rest.h"
30 #include "rgw_acl.h"
31 #include "rgw_acl_s3.h"
32 #include "rgw_acl_swift.h"
33 #include "rgw_aio_throttle.h"
34 #include "rgw_user.h"
35 #include "rgw_bucket.h"
36 #include "rgw_log.h"
37 #include "rgw_multi.h"
38 #include "rgw_multi_del.h"
39 #include "rgw_cors.h"
40 #include "rgw_cors_s3.h"
41 #include "rgw_rest_conn.h"
42 #include "rgw_rest_s3.h"
43 #include "rgw_tar.h"
44 #include "rgw_client_io.h"
45 #include "rgw_compression.h"
46 #include "rgw_role.h"
47 #include "rgw_tag_s3.h"
48 #include "rgw_putobj_processor.h"
49 #include "rgw_crypt.h"
50 #include "rgw_perf_counters.h"
51 #include "rgw_notify.h"
52 #include "rgw_notify_event_type.h"
53 #include "rgw_sal.h"
54 #include "rgw_sal_rados.h"
55
56 #include "services/svc_zone.h"
57 #include "services/svc_quota.h"
58 #include "services/svc_sys_obj.h"
59
60 #include "cls/lock/cls_lock_client.h"
61 #include "cls/rgw/cls_rgw_client.h"
62
63
64 #include "include/ceph_assert.h"
65
66 #include "compressor/Compressor.h"
67
68 #ifdef WITH_LTTNG
69 #define TRACEPOINT_DEFINE
70 #define TRACEPOINT_PROBE_DYNAMIC_LINKAGE
71 #include "tracing/rgw_op.h"
72 #undef TRACEPOINT_PROBE_DYNAMIC_LINKAGE
73 #undef TRACEPOINT_DEFINE
74 #else
75 #define tracepoint(...)
76 #endif
77
78 #define dout_context g_ceph_context
79 #define dout_subsys ceph_subsys_rgw
80
81 using namespace std;
82 using namespace librados;
83 using ceph::crypto::MD5;
84 using boost::optional;
85 using boost::none;
86
87 using rgw::ARN;
88 using rgw::IAM::Effect;
89 using rgw::IAM::Policy;
90
91 static string mp_ns = RGW_OBJ_NS_MULTIPART;
92 static string shadow_ns = RGW_OBJ_NS_SHADOW;
93
94 static void forward_req_info(const DoutPrefixProvider *dpp, CephContext *cct, req_info& info, const std::string& bucket_name);
95
96 static MultipartMetaFilter mp_filter;
97
98 // this probably should belong in the rgw_iam_policy_keywords, I'll get it to it
99 // at some point
100 static constexpr auto S3_EXISTING_OBJTAG = "s3:ExistingObjectTag";
101 static constexpr auto S3_RESOURCE_TAG = "s3:ResourceTag";
102 static constexpr auto S3_RUNTIME_RESOURCE_VAL = "${s3:ResourceTag";
103
104 int RGWGetObj::parse_range(void)
105 {
106 int r = -ERANGE;
107 string rs(range_str);
108 string ofs_str;
109 string end_str;
110
111 ignore_invalid_range = s->cct->_conf->rgw_ignore_get_invalid_range;
112 partial_content = false;
113
114 size_t pos = rs.find("bytes=");
115 if (pos == string::npos) {
116 pos = 0;
117 while (isspace(rs[pos]))
118 pos++;
119 int end = pos;
120 while (isalpha(rs[end]))
121 end++;
122 if (strncasecmp(rs.c_str(), "bytes", end - pos) != 0)
123 return 0;
124 while (isspace(rs[end]))
125 end++;
126 if (rs[end] != '=')
127 return 0;
128 rs = rs.substr(end + 1);
129 } else {
130 rs = rs.substr(pos + 6); /* size of("bytes=") */
131 }
132 pos = rs.find('-');
133 if (pos == string::npos)
134 goto done;
135
136 partial_content = true;
137
138 ofs_str = rs.substr(0, pos);
139 end_str = rs.substr(pos + 1);
140 if (end_str.length()) {
141 end = atoll(end_str.c_str());
142 if (end < 0)
143 goto done;
144 }
145
146 if (ofs_str.length()) {
147 ofs = atoll(ofs_str.c_str());
148 } else { // RFC2616 suffix-byte-range-spec
149 ofs = -end;
150 end = -1;
151 }
152
153 if (end >= 0 && end < ofs)
154 goto done;
155
156 range_parsed = true;
157 return 0;
158
159 done:
160 if (ignore_invalid_range) {
161 partial_content = false;
162 ofs = 0;
163 end = -1;
164 range_parsed = false; // allow retry
165 r = 0;
166 }
167
168 return r;
169 }
170
171 static int decode_policy(const DoutPrefixProvider *dpp,
172 CephContext *cct,
173 bufferlist& bl,
174 RGWAccessControlPolicy *policy)
175 {
176 auto iter = bl.cbegin();
177 try {
178 policy->decode(iter);
179 } catch (buffer::error& err) {
180 ldpp_dout(dpp, 0) << "ERROR: could not decode policy, caught buffer::error" << dendl;
181 return -EIO;
182 }
183 if (cct->_conf->subsys.should_gather<ceph_subsys_rgw, 15>()) {
184 ldpp_dout(dpp, 15) << __func__ << " Read AccessControlPolicy";
185 RGWAccessControlPolicy_S3 *s3policy = static_cast<RGWAccessControlPolicy_S3 *>(policy);
186 s3policy->to_xml(*_dout);
187 *_dout << dendl;
188 }
189 return 0;
190 }
191
192
193 static int get_user_policy_from_attr(const DoutPrefixProvider *dpp,
194 CephContext * const cct,
195 map<string, bufferlist>& attrs,
196 RGWAccessControlPolicy& policy /* out */)
197 {
198 auto aiter = attrs.find(RGW_ATTR_ACL);
199 if (aiter != attrs.end()) {
200 int ret = decode_policy(dpp, cct, aiter->second, &policy);
201 if (ret < 0) {
202 return ret;
203 }
204 } else {
205 return -ENOENT;
206 }
207
208 return 0;
209 }
210
211 /**
212 * Get the AccessControlPolicy for an object off of disk.
213 * policy: must point to a valid RGWACL, and will be filled upon return.
214 * bucket: name of the bucket containing the object.
215 * object: name of the object to get the ACL for.
216 * Returns: 0 on success, -ERR# otherwise.
217 */
218 int rgw_op_get_bucket_policy_from_attr(const DoutPrefixProvider *dpp,
219 CephContext *cct,
220 rgw::sal::Store* store,
221 RGWBucketInfo& bucket_info,
222 map<string, bufferlist>& bucket_attrs,
223 RGWAccessControlPolicy *policy,
224 optional_yield y)
225 {
226 map<string, bufferlist>::iterator aiter = bucket_attrs.find(RGW_ATTR_ACL);
227
228 if (aiter != bucket_attrs.end()) {
229 int ret = decode_policy(dpp, cct, aiter->second, policy);
230 if (ret < 0)
231 return ret;
232 } else {
233 ldpp_dout(dpp, 0) << "WARNING: couldn't find acl header for bucket, generating default" << dendl;
234 std::unique_ptr<rgw::sal::User> user = store->get_user(bucket_info.owner);
235 /* object exists, but policy is broken */
236 int r = user->load_user(dpp, y);
237 if (r < 0)
238 return r;
239
240 policy->create_default(bucket_info.owner, user->get_display_name());
241 }
242 return 0;
243 }
244
245 static int get_obj_policy_from_attr(const DoutPrefixProvider *dpp,
246 CephContext *cct,
247 rgw::sal::Store* store,
248 RGWObjectCtx& obj_ctx,
249 RGWBucketInfo& bucket_info,
250 map<string, bufferlist>& bucket_attrs,
251 RGWAccessControlPolicy *policy,
252 string *storage_class,
253 rgw::sal::Object* obj,
254 optional_yield y)
255 {
256 bufferlist bl;
257 int ret = 0;
258
259 std::unique_ptr<rgw::sal::Object::ReadOp> rop = obj->get_read_op(&obj_ctx);
260
261 ret = rop->get_attr(dpp, RGW_ATTR_ACL, bl, y);
262 if (ret >= 0) {
263 ret = decode_policy(dpp, cct, bl, policy);
264 if (ret < 0)
265 return ret;
266 } else if (ret == -ENODATA) {
267 /* object exists, but policy is broken */
268 ldpp_dout(dpp, 0) << "WARNING: couldn't find acl header for object, generating default" << dendl;
269 std::unique_ptr<rgw::sal::User> user = store->get_user(bucket_info.owner);
270 ret = user->load_user(dpp, y);
271 if (ret < 0)
272 return ret;
273
274 policy->create_default(bucket_info.owner, user->get_display_name());
275 }
276
277 if (storage_class) {
278 bufferlist scbl;
279 int r = rop->get_attr(dpp, RGW_ATTR_STORAGE_CLASS, scbl, y);
280 if (r >= 0) {
281 *storage_class = scbl.to_str();
282 } else {
283 storage_class->clear();
284 }
285 }
286
287 return ret;
288 }
289
290
291 static boost::optional<Policy> get_iam_policy_from_attr(CephContext* cct,
292 map<string, bufferlist>& attrs,
293 const string& tenant) {
294 auto i = attrs.find(RGW_ATTR_IAM_POLICY);
295 if (i != attrs.end()) {
296 return Policy(cct, tenant, i->second);
297 } else {
298 return none;
299 }
300 }
301
302 static boost::optional<PublicAccessBlockConfiguration>
303 get_public_access_conf_from_attr(const map<string, bufferlist>& attrs)
304 {
305 if (auto aiter = attrs.find(RGW_ATTR_PUBLIC_ACCESS);
306 aiter != attrs.end()) {
307 bufferlist::const_iterator iter{&aiter->second};
308 PublicAccessBlockConfiguration access_conf;
309 try {
310 access_conf.decode(iter);
311 } catch (const buffer::error& e) {
312 return boost::none;
313 }
314 return access_conf;
315 }
316 return boost::none;
317 }
318
319 vector<Policy> get_iam_user_policy_from_attr(CephContext* cct,
320 map<string, bufferlist>& attrs,
321 const string& tenant) {
322 vector<Policy> policies;
323 if (auto it = attrs.find(RGW_ATTR_USER_POLICY); it != attrs.end()) {
324 bufferlist out_bl = attrs[RGW_ATTR_USER_POLICY];
325 map<string, string> policy_map;
326 decode(policy_map, out_bl);
327 for (auto& it : policy_map) {
328 bufferlist bl = bufferlist::static_from_string(it.second);
329 Policy p(cct, tenant, bl);
330 policies.push_back(std::move(p));
331 }
332 }
333 return policies;
334 }
335
336 static int read_bucket_policy(const DoutPrefixProvider *dpp,
337 rgw::sal::Store* store,
338 struct req_state *s,
339 RGWBucketInfo& bucket_info,
340 map<string, bufferlist>& bucket_attrs,
341 RGWAccessControlPolicy *policy,
342 rgw_bucket& bucket,
343 optional_yield y)
344 {
345 if (!s->system_request && bucket_info.flags & BUCKET_SUSPENDED) {
346 ldpp_dout(dpp, 0) << "NOTICE: bucket " << bucket_info.bucket.name
347 << " is suspended" << dendl;
348 return -ERR_USER_SUSPENDED;
349 }
350
351 if (bucket.name.empty()) {
352 return 0;
353 }
354
355 int ret = rgw_op_get_bucket_policy_from_attr(dpp, s->cct, store, bucket_info, bucket_attrs, policy, y);
356 if (ret == -ENOENT) {
357 ret = -ERR_NO_SUCH_BUCKET;
358 }
359
360 return ret;
361 }
362
363 static int read_obj_policy(const DoutPrefixProvider *dpp,
364 rgw::sal::Store* store,
365 struct req_state *s,
366 RGWBucketInfo& bucket_info,
367 map<string, bufferlist>& bucket_attrs,
368 RGWAccessControlPolicy* acl,
369 string *storage_class,
370 boost::optional<Policy>& policy,
371 rgw::sal::Bucket* bucket,
372 rgw::sal::Object* object,
373 optional_yield y,
374 bool copy_src=false)
375 {
376 string upload_id;
377 upload_id = s->info.args.get("uploadId");
378 std::unique_ptr<rgw::sal::Object> mpobj;
379 rgw_obj obj;
380
381 if (!s->system_request && bucket_info.flags & BUCKET_SUSPENDED) {
382 ldpp_dout(dpp, 0) << "NOTICE: bucket " << bucket_info.bucket.name
383 << " is suspended" << dendl;
384 return -ERR_USER_SUSPENDED;
385 }
386
387 // when getting policy info for copy-source obj, upload_id makes no sense.
388 // 'copy_src' is used to make this function backward compatible.
389 if (!upload_id.empty() && !copy_src) {
390 /* multipart upload */
391 std::unique_ptr<rgw::sal::MultipartUpload> upload;
392 upload = bucket->get_multipart_upload(object->get_name(), upload_id);
393 mpobj = upload->get_meta_obj();
394 mpobj->set_in_extra_data(true);
395 object = mpobj.get();
396 }
397 policy = get_iam_policy_from_attr(s->cct, bucket_attrs, bucket->get_tenant());
398
399 RGWObjectCtx *obj_ctx = static_cast<RGWObjectCtx *>(s->obj_ctx);
400 int ret = get_obj_policy_from_attr(dpp, s->cct, store, *obj_ctx,
401 bucket_info, bucket_attrs, acl, storage_class, object, s->yield);
402 if (ret == -ENOENT) {
403 /* object does not exist checking the bucket's ACL to make sure
404 that we send a proper error code */
405 RGWAccessControlPolicy bucket_policy(s->cct);
406 ret = rgw_op_get_bucket_policy_from_attr(dpp, s->cct, store, bucket_info, bucket_attrs, &bucket_policy, y);
407 if (ret < 0) {
408 return ret;
409 }
410 const rgw_user& bucket_owner = bucket_policy.get_owner().get_id();
411 if (bucket_owner.compare(s->user->get_id()) != 0 &&
412 ! s->auth.identity->is_admin_of(bucket_owner)) {
413 auto r = eval_identity_or_session_policies(s->iam_user_policies, s->env,
414 rgw::IAM::s3ListBucket, ARN(bucket->get_key()));
415 if (r == Effect::Allow)
416 return -ENOENT;
417 if (r == Effect::Deny)
418 return -EACCES;
419 if (policy) {
420 ARN b_arn(bucket->get_key());
421 r = policy->eval(s->env, *s->auth.identity, rgw::IAM::s3ListBucket, b_arn);
422 if (r == Effect::Allow)
423 return -ENOENT;
424 if (r == Effect::Deny)
425 return -EACCES;
426 }
427 if (! s->session_policies.empty()) {
428 r = eval_identity_or_session_policies(s->session_policies, s->env,
429 rgw::IAM::s3ListBucket, ARN(bucket->get_key()));
430 if (r == Effect::Allow)
431 return -ENOENT;
432 if (r == Effect::Deny)
433 return -EACCES;
434 }
435 if (! bucket_policy.verify_permission(s, *s->auth.identity, s->perm_mask, RGW_PERM_READ))
436 ret = -EACCES;
437 else
438 ret = -ENOENT;
439 } else {
440 ret = -ENOENT;
441 }
442 }
443
444 return ret;
445 }
446
447 /**
448 * Get the AccessControlPolicy for an user, bucket or object off of disk.
449 * s: The req_state to draw information from.
450 * only_bucket: If true, reads the user and bucket ACLs rather than the object ACL.
451 * Returns: 0 on success, -ERR# otherwise.
452 */
453 int rgw_build_bucket_policies(const DoutPrefixProvider *dpp, rgw::sal::Store* store, struct req_state* s, optional_yield y)
454 {
455 int ret = 0;
456
457 string bi = s->info.args.get(RGW_SYS_PARAM_PREFIX "bucket-instance");
458 if (!bi.empty()) {
459 // note: overwrites s->bucket_name, may include a tenant/
460 ret = rgw_bucket_parse_bucket_instance(bi, &s->bucket_name, &s->bucket_instance_id, &s->bucket_instance_shard_id);
461 if (ret < 0) {
462 return ret;
463 }
464 }
465
466 if(s->dialect.compare("s3") == 0) {
467 s->bucket_acl = std::make_unique<RGWAccessControlPolicy_S3>(s->cct);
468 } else if(s->dialect.compare("swift") == 0) {
469 /* We aren't allocating the account policy for those operations using
470 * the Swift's infrastructure that don't really need req_state::user.
471 * Typical example here is the implementation of /info. */
472 if (!s->user->get_id().empty()) {
473 s->user_acl = std::make_unique<RGWAccessControlPolicy_SWIFTAcct>(s->cct);
474 }
475 s->bucket_acl = std::make_unique<RGWAccessControlPolicy_SWIFT>(s->cct);
476 } else {
477 s->bucket_acl = std::make_unique<RGWAccessControlPolicy>(s->cct);
478 }
479
480 /* check if copy source is within the current domain */
481 if (!s->src_bucket_name.empty()) {
482 std::unique_ptr<rgw::sal::Bucket> src_bucket;
483 ret = store->get_bucket(dpp, nullptr,
484 rgw_bucket(s->src_tenant_name,
485 s->src_bucket_name,
486 s->bucket_instance_id),
487 &src_bucket, y);
488 if (ret == 0) {
489 string& zonegroup = src_bucket->get_info().zonegroup;
490 s->local_source = store->get_zone()->get_zonegroup().equals(zonegroup);
491 }
492 }
493
494 struct {
495 rgw_user uid;
496 std::string display_name;
497 } acct_acl_user = {
498 s->user->get_id(),
499 s->user->get_display_name(),
500 };
501
502 if (!s->bucket_name.empty()) {
503 s->bucket_exists = true;
504
505 /* This is the only place that s->bucket is created. It should never be
506 * overwritten. */
507 ret = store->get_bucket(dpp, s->user.get(), rgw_bucket(rgw_bucket_key(s->bucket_tenant, s->bucket_name, s->bucket_instance_id)), &s->bucket, y);
508 if (ret < 0) {
509 if (ret != -ENOENT) {
510 string bucket_log;
511 bucket_log = rgw_make_bucket_entry_name(s->bucket_tenant, s->bucket_name);
512 ldpp_dout(dpp, 0) << "NOTICE: couldn't get bucket from bucket_name (name="
513 << bucket_log << ")" << dendl;
514 return ret;
515 }
516 s->bucket_exists = false;
517 return -ERR_NO_SUCH_BUCKET;
518 }
519 if (!rgw::sal::Object::empty(s->object.get())) {
520 s->object->set_bucket(s->bucket.get());
521 }
522
523 s->bucket_mtime = s->bucket->get_modification_time();
524 s->bucket_attrs = s->bucket->get_attrs();
525 ret = read_bucket_policy(dpp, store, s, s->bucket->get_info(),
526 s->bucket->get_attrs(),
527 s->bucket_acl.get(), s->bucket->get_key(), y);
528 acct_acl_user = {
529 s->bucket->get_info().owner,
530 s->bucket_acl->get_owner().get_display_name(),
531 };
532
533 s->bucket_owner = s->bucket_acl->get_owner();
534
535 RGWZoneGroup zonegroup;
536 int r = store->get_zone()->get_zonegroup(s->bucket->get_info().zonegroup, zonegroup);
537 if (!r) {
538 if (!zonegroup.endpoints.empty()) {
539 s->zonegroup_endpoint = zonegroup.endpoints.front();
540 } else {
541 // use zonegroup's master zone endpoints
542 auto z = zonegroup.zones.find(zonegroup.master_zone);
543 if (z != zonegroup.zones.end() && !z->second.endpoints.empty()) {
544 s->zonegroup_endpoint = z->second.endpoints.front();
545 }
546 }
547 s->zonegroup_name = zonegroup.get_name();
548 }
549 if (r < 0 && ret == 0) {
550 ret = r;
551 }
552
553 if (!store->get_zone()->get_zonegroup().equals(s->bucket->get_info().zonegroup)) {
554 ldpp_dout(dpp, 0) << "NOTICE: request for data in a different zonegroup ("
555 << s->bucket->get_info().zonegroup << " != "
556 << store->get_zone()->get_zonegroup().get_id() << ")" << dendl;
557 /* we now need to make sure that the operation actually requires copy source, that is
558 * it's a copy operation
559 */
560 if (store->get_zone()->get_zonegroup().is_master_zonegroup() && s->system_request) {
561 /*If this is the master, don't redirect*/
562 } else if (s->op_type == RGW_OP_GET_BUCKET_LOCATION ) {
563 /* If op is get bucket location, don't redirect */
564 } else if (!s->local_source ||
565 (s->op != OP_PUT && s->op != OP_COPY) ||
566 rgw::sal::Object::empty(s->object.get())) {
567 return -ERR_PERMANENT_REDIRECT;
568 }
569 }
570
571 /* init dest placement */
572 s->dest_placement.storage_class = s->info.storage_class;
573 s->dest_placement.inherit_from(s->bucket->get_placement_rule());
574
575 if (!store->get_zone()->get_params().valid_placement(s->dest_placement)) {
576 ldpp_dout(dpp, 0) << "NOTICE: invalid dest placement: " << s->dest_placement.to_str() << dendl;
577 return -EINVAL;
578 }
579
580 s->bucket_access_conf = get_public_access_conf_from_attr(s->bucket->get_attrs());
581 }
582
583 /* handle user ACL only for those APIs which support it */
584 if (s->user_acl) {
585 std::unique_ptr<rgw::sal::User> acl_user = store->get_user(acct_acl_user.uid);
586
587 ret = acl_user->read_attrs(dpp, y);
588 if (!ret) {
589 ret = get_user_policy_from_attr(dpp, s->cct, acl_user->get_attrs(), *s->user_acl);
590 }
591 if (-ENOENT == ret) {
592 /* In already existing clusters users won't have ACL. In such case
593 * assuming that only account owner has the rights seems to be
594 * reasonable. That allows to have only one verification logic.
595 * NOTE: there is small compatibility kludge for global, empty tenant:
596 * 1. if we try to reach an existing bucket, its owner is considered
597 * as account owner.
598 * 2. otherwise account owner is identity stored in s->user->user_id. */
599 s->user_acl->create_default(acct_acl_user.uid,
600 acct_acl_user.display_name);
601 ret = 0;
602 } else if (ret < 0) {
603 ldpp_dout(dpp, 0) << "NOTICE: couldn't get user attrs for handling ACL "
604 "(user_id=" << s->user->get_id() << ", ret=" << ret << ")" << dendl;
605 return ret;
606 }
607 }
608 // We don't need user policies in case of STS token returned by AssumeRole,
609 // hence the check for user type
610 if (! s->user->get_id().empty() && s->auth.identity->get_identity_type() != TYPE_ROLE) {
611 try {
612 ret = s->user->read_attrs(dpp, y);
613 if (ret == 0) {
614 auto user_policies = get_iam_user_policy_from_attr(s->cct,
615 s->user->get_attrs(),
616 s->user->get_tenant());
617 s->iam_user_policies.insert(s->iam_user_policies.end(),
618 std::make_move_iterator(user_policies.begin()),
619 std::make_move_iterator(user_policies.end()));
620 } else {
621 if (ret == -ENOENT)
622 ret = 0;
623 else ret = -EACCES;
624 }
625 } catch (const std::exception& e) {
626 ldpp_dout(dpp, -1) << "Error reading IAM User Policy: " << e.what() << dendl;
627 ret = -EACCES;
628 }
629 }
630
631 try {
632 s->iam_policy = get_iam_policy_from_attr(s->cct, s->bucket_attrs, s->bucket_tenant);
633 } catch (const std::exception& e) {
634 // Really this is a can't happen condition. We parse the policy
635 // when it's given to us, so perhaps we should abort or otherwise
636 // raise bloody murder.
637 ldpp_dout(dpp, 0) << "Error reading IAM Policy: " << e.what() << dendl;
638 ret = -EACCES;
639 }
640
641 bool success = store->get_zone()->get_redirect_endpoint(&s->redirect_zone_endpoint);
642 if (success) {
643 ldpp_dout(dpp, 20) << "redirect_zone_endpoint=" << s->redirect_zone_endpoint << dendl;
644 }
645
646 return ret;
647 }
648
649 /**
650 * Get the AccessControlPolicy for a bucket or object off of disk.
651 * s: The req_state to draw information from.
652 * only_bucket: If true, reads the bucket ACL rather than the object ACL.
653 * Returns: 0 on success, -ERR# otherwise.
654 */
655 int rgw_build_object_policies(const DoutPrefixProvider *dpp, rgw::sal::Store* store,
656 struct req_state *s, bool prefetch_data, optional_yield y)
657 {
658 int ret = 0;
659
660 if (!rgw::sal::Object::empty(s->object.get())) {
661 if (!s->bucket_exists) {
662 return -ERR_NO_SUCH_BUCKET;
663 }
664 s->object_acl = std::make_unique<RGWAccessControlPolicy>(s->cct);
665
666 s->object->set_atomic(s->obj_ctx);
667 if (prefetch_data) {
668 s->object->set_prefetch_data(s->obj_ctx);
669 }
670 ret = read_obj_policy(dpp, store, s, s->bucket->get_info(), s->bucket_attrs,
671 s->object_acl.get(), nullptr, s->iam_policy, s->bucket.get(),
672 s->object.get(), y);
673 }
674
675 return ret;
676 }
677
678 static int rgw_iam_remove_objtags(const DoutPrefixProvider *dpp, struct req_state* s, rgw::sal::Object* object, bool has_existing_obj_tag, bool has_resource_tag) {
679 object->set_atomic(s->obj_ctx);
680 int op_ret = object->get_obj_attrs(s->obj_ctx, s->yield, dpp);
681 if (op_ret < 0)
682 return op_ret;
683 rgw::sal::Attrs attrs = object->get_attrs();
684 auto tags = attrs.find(RGW_ATTR_TAGS);
685 if (tags != attrs.end()) {
686 RGWObjTags tagset;
687 try {
688 auto bliter = tags->second.cbegin();
689 tagset.decode(bliter);
690 } catch (buffer::error& err) {
691 ldpp_dout(s, 0) << "ERROR: caught buffer::error, couldn't decode TagSet" << dendl;
692 return -EIO;
693 }
694 for (auto& tag: tagset.get_tags()) {
695 if (has_existing_obj_tag) {
696 vector<std::unordered_multimap<string, string>::iterator> iters;
697 string key = "s3:ExistingObjectTag/" + tag.first;
698 auto result = s->env.equal_range(key);
699 for (auto& it = result.first; it != result.second; ++it)
700 {
701 if (tag.second == it->second) {
702 iters.emplace_back(it);
703 }
704 }
705 for (auto& it : iters) {
706 s->env.erase(it);
707 }
708 }//end if has_existing_obj_tag
709 if (has_resource_tag) {
710 vector<std::unordered_multimap<string, string>::iterator> iters;
711 string key = "s3:ResourceTag/" + tag.first;
712 auto result = s->env.equal_range(key);
713 for (auto& it = result.first; it != result.second; ++it)
714 {
715 if (tag.second == it->second) {
716 iters.emplace_back(it);
717 }
718 }
719 for (auto& it : iters) {
720 s->env.erase(it);
721 }
722 }//end if has_resource_tag
723 }
724 }
725 return 0;
726 }
727
728 void rgw_add_to_iam_environment(rgw::IAM::Environment& e, std::string_view key, std::string_view val){
729 // This variant just adds non empty key pairs to IAM env., values can be empty
730 // in certain cases like tagging
731 if (!key.empty())
732 e.emplace(key,val);
733 }
734
735 static int rgw_iam_add_tags_from_bl(struct req_state* s, bufferlist& bl, bool has_existing_obj_tag=false, bool has_resource_tag=false){
736 RGWObjTags& tagset = s->tagset;
737 try {
738 auto bliter = bl.cbegin();
739 tagset.decode(bliter);
740 } catch (buffer::error& err) {
741 ldpp_dout(s, 0) << "ERROR: caught buffer::error, couldn't decode TagSet" << dendl;
742 return -EIO;
743 }
744
745 for (const auto& tag: tagset.get_tags()){
746 if (has_existing_obj_tag)
747 rgw_add_to_iam_environment(s->env, "s3:ExistingObjectTag/" + tag.first, tag.second);
748 if (has_resource_tag)
749 rgw_add_to_iam_environment(s->env, "s3:ResourceTag/" + tag.first, tag.second);
750 }
751 return 0;
752 }
753
754 static int rgw_iam_add_objtags(const DoutPrefixProvider *dpp, struct req_state* s, rgw::sal::Object* object, bool has_existing_obj_tag, bool has_resource_tag) {
755 object->set_atomic(s->obj_ctx);
756 int op_ret = object->get_obj_attrs(s->obj_ctx, s->yield, dpp);
757 if (op_ret < 0)
758 return op_ret;
759 rgw::sal::Attrs attrs = object->get_attrs();
760 auto tags = attrs.find(RGW_ATTR_TAGS);
761 if (tags != attrs.end()){
762 return rgw_iam_add_tags_from_bl(s, tags->second, has_existing_obj_tag, has_resource_tag);
763 }
764 return 0;
765 }
766
767 static int rgw_iam_add_objtags(const DoutPrefixProvider *dpp, struct req_state* s, bool has_existing_obj_tag, bool has_resource_tag) {
768 if (!rgw::sal::Object::empty(s->object.get())) {
769 return rgw_iam_add_objtags(dpp, s, s->object.get(), has_existing_obj_tag, has_resource_tag);
770 }
771 return 0;
772 }
773
774 static int rgw_iam_add_buckettags(const DoutPrefixProvider *dpp, struct req_state* s, rgw::sal::Bucket* bucket) {
775 rgw::sal::Attrs attrs = bucket->get_attrs();
776 auto tags = attrs.find(RGW_ATTR_TAGS);
777 if (tags != attrs.end()) {
778 return rgw_iam_add_tags_from_bl(s, tags->second, false, true);
779 }
780 return 0;
781 }
782
783 static int rgw_iam_add_buckettags(const DoutPrefixProvider *dpp, struct req_state* s) {
784 return rgw_iam_add_buckettags(dpp, s, s->bucket.get());
785 }
786
787 static std::tuple<bool, bool> rgw_check_policy_condition(const DoutPrefixProvider *dpp,
788 boost::optional<rgw::IAM::Policy> iam_policy,
789 boost::optional<vector<rgw::IAM::Policy>> identity_policies,
790 boost::optional<vector<rgw::IAM::Policy>> session_policies,
791 bool check_obj_exist_tag=true) {
792 bool has_existing_obj_tag = false, has_resource_tag = false;
793 bool iam_policy_s3_exist_tag = false, iam_policy_s3_resource_tag = false;
794 if (iam_policy) {
795 if (check_obj_exist_tag) {
796 iam_policy_s3_exist_tag = iam_policy->has_partial_conditional(S3_EXISTING_OBJTAG);
797 }
798 iam_policy_s3_resource_tag = iam_policy->has_partial_conditional(S3_RESOURCE_TAG) || iam_policy->has_partial_conditional_value(S3_RUNTIME_RESOURCE_VAL);
799 }
800
801 bool identity_policy_s3_exist_tag = false, identity_policy_s3_resource_tag = false;
802 if (identity_policies) {
803 for (auto& identity_policy : identity_policies.get()) {
804 if (check_obj_exist_tag) {
805 if (identity_policy.has_partial_conditional(S3_EXISTING_OBJTAG))
806 identity_policy_s3_exist_tag = true;
807 }
808 if (identity_policy.has_partial_conditional(S3_RESOURCE_TAG) || identity_policy.has_partial_conditional_value(S3_RUNTIME_RESOURCE_VAL))
809 identity_policy_s3_resource_tag = true;
810 if (identity_policy_s3_exist_tag && identity_policy_s3_resource_tag) // check all policies till both are set to true
811 break;
812 }
813 }
814
815 bool session_policy_s3_exist_tag = false, session_policy_s3_resource_flag = false;
816 if (session_policies) {
817 for (auto& session_policy : session_policies.get()) {
818 if (check_obj_exist_tag) {
819 if (session_policy.has_partial_conditional(S3_EXISTING_OBJTAG))
820 session_policy_s3_exist_tag = true;
821 }
822 if (session_policy.has_partial_conditional(S3_RESOURCE_TAG) || session_policy.has_partial_conditional_value(S3_RUNTIME_RESOURCE_VAL))
823 session_policy_s3_resource_flag = true;
824 if (session_policy_s3_exist_tag && session_policy_s3_resource_flag)
825 break;
826 }
827 }
828
829 has_existing_obj_tag = iam_policy_s3_exist_tag || identity_policy_s3_exist_tag || session_policy_s3_exist_tag;
830 has_resource_tag = iam_policy_s3_resource_tag || identity_policy_s3_resource_tag || session_policy_s3_resource_flag;
831 return make_tuple(has_existing_obj_tag, has_resource_tag);
832 }
833
834 static std::tuple<bool, bool> rgw_check_policy_condition(const DoutPrefixProvider *dpp, struct req_state* s, bool check_obj_exist_tag=true) {
835 return rgw_check_policy_condition(dpp, s->iam_policy, s->iam_user_policies, s->session_policies, check_obj_exist_tag);
836 }
837
838 static void rgw_add_grant_to_iam_environment(rgw::IAM::Environment& e, struct req_state *s){
839
840 using header_pair_t = std::pair <const char*, const char*>;
841 static const std::initializer_list <header_pair_t> acl_header_conditionals {
842 {"HTTP_X_AMZ_GRANT_READ", "s3:x-amz-grant-read"},
843 {"HTTP_X_AMZ_GRANT_WRITE", "s3:x-amz-grant-write"},
844 {"HTTP_X_AMZ_GRANT_READ_ACP", "s3:x-amz-grant-read-acp"},
845 {"HTTP_X_AMZ_GRANT_WRITE_ACP", "s3:x-amz-grant-write-acp"},
846 {"HTTP_X_AMZ_GRANT_FULL_CONTROL", "s3:x-amz-grant-full-control"}
847 };
848
849 if (s->has_acl_header){
850 for (const auto& c: acl_header_conditionals){
851 auto hdr = s->info.env->get(c.first);
852 if(hdr) {
853 e.emplace(c.second, hdr);
854 }
855 }
856 }
857 }
858
859 void rgw_build_iam_environment(rgw::sal::Store* store,
860 struct req_state* s)
861 {
862 const auto& m = s->info.env->get_map();
863 auto t = ceph::real_clock::now();
864 s->env.emplace("aws:CurrentTime", std::to_string(ceph::real_clock::to_time_t(t)));
865 s->env.emplace("aws:EpochTime", ceph::to_iso_8601(t));
866 // TODO: This is fine for now, but once we have STS we'll need to
867 // look and see. Also this won't work with the IdentityApplier
868 // model, since we need to know the actual credential.
869 s->env.emplace("aws:PrincipalType", "User");
870
871 auto i = m.find("HTTP_REFERER");
872 if (i != m.end()) {
873 s->env.emplace("aws:Referer", i->second);
874 }
875
876 if (rgw_transport_is_secure(s->cct, *s->info.env)) {
877 s->env.emplace("aws:SecureTransport", "true");
878 }
879
880 const auto remote_addr_param = s->cct->_conf->rgw_remote_addr_param;
881 if (remote_addr_param.length()) {
882 i = m.find(remote_addr_param);
883 } else {
884 i = m.find("REMOTE_ADDR");
885 }
886 if (i != m.end()) {
887 const string* ip = &(i->second);
888 string temp;
889 if (remote_addr_param == "HTTP_X_FORWARDED_FOR") {
890 const auto comma = ip->find(',');
891 if (comma != string::npos) {
892 temp.assign(*ip, 0, comma);
893 ip = &temp;
894 }
895 }
896 s->env.emplace("aws:SourceIp", *ip);
897 }
898
899 i = m.find("HTTP_USER_AGENT"); {
900 if (i != m.end())
901 s->env.emplace("aws:UserAgent", i->second);
902 }
903
904 if (s->user) {
905 // What to do about aws::userid? One can have multiple access
906 // keys so that isn't really suitable. Do we have a durable
907 // identifier that can persist through name changes?
908 s->env.emplace("aws:username", s->user->get_id().id);
909 }
910
911 i = m.find("HTTP_X_AMZ_SECURITY_TOKEN");
912 if (i != m.end()) {
913 s->env.emplace("sts:authentication", "true");
914 } else {
915 s->env.emplace("sts:authentication", "false");
916 }
917 }
918
919 void rgw_bucket_object_pre_exec(struct req_state *s)
920 {
921 if (s->expect_cont)
922 dump_continue(s);
923
924 dump_bucket_from_state(s);
925 }
926
927 // So! Now and then when we try to update bucket information, the
928 // bucket has changed during the course of the operation. (Or we have
929 // a cache consistency problem that Watch/Notify isn't ruling out
930 // completely.)
931 //
932 // When this happens, we need to update the bucket info and try
933 // again. We have, however, to try the right *part* again. We can't
934 // simply re-send, since that will obliterate the previous update.
935 //
936 // Thus, callers of this function should include everything that
937 // merges information to be changed into the bucket information as
938 // well as the call to set it.
939 //
940 // The called function must return an integer, negative on error. In
941 // general, they should just return op_ret.
942 namespace {
943 template<typename F>
944 int retry_raced_bucket_write(const DoutPrefixProvider *dpp, rgw::sal::Bucket* b, const F& f) {
945 auto r = f();
946 for (auto i = 0u; i < 15u && r == -ECANCELED; ++i) {
947 r = b->try_refresh_info(dpp, nullptr);
948 if (r >= 0) {
949 r = f();
950 }
951 }
952 return r;
953 }
954 }
955
956
957 int RGWGetObj::verify_permission(optional_yield y)
958 {
959 s->object->set_atomic(s->obj_ctx);
960
961 if (prefetch_data()) {
962 s->object->set_prefetch_data(s->obj_ctx);
963 }
964
965 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
966 if (has_s3_existing_tag || has_s3_resource_tag)
967 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
968
969 if (torrent.get_flag()) {
970 if (s->object->get_instance().empty()) {
971 action = rgw::IAM::s3GetObjectTorrent;
972 } else {
973 action = rgw::IAM::s3GetObjectVersionTorrent;
974 }
975 } else {
976 if (s->object->get_instance().empty()) {
977 action = rgw::IAM::s3GetObject;
978 } else {
979 action = rgw::IAM::s3GetObjectVersion;
980 }
981 }
982
983 if (!verify_object_permission(this, s, action)) {
984 return -EACCES;
985 }
986
987 if (s->bucket->get_info().obj_lock_enabled()) {
988 get_retention = verify_object_permission(this, s, rgw::IAM::s3GetObjectRetention);
989 get_legal_hold = verify_object_permission(this, s, rgw::IAM::s3GetObjectLegalHold);
990 }
991
992 return 0;
993 }
994
995 RGWOp::~RGWOp(){};
996
997 int RGWOp::verify_op_mask()
998 {
999 uint32_t required_mask = op_mask();
1000
1001 ldpp_dout(this, 20) << "required_mask= " << required_mask
1002 << " user.op_mask=" << s->user->get_info().op_mask << dendl;
1003
1004 if ((s->user->get_info().op_mask & required_mask) != required_mask) {
1005 return -EPERM;
1006 }
1007
1008 if (!s->system_request && (required_mask & RGW_OP_TYPE_MODIFY) && !store->get_zone()->is_writeable()) {
1009 ldpp_dout(this, 5) << "NOTICE: modify request to a read-only zone by a "
1010 "non-system user, permission denied" << dendl;
1011 return -EPERM;
1012 }
1013
1014 return 0;
1015 }
1016
1017 int RGWGetObjTags::verify_permission(optional_yield y)
1018 {
1019 auto iam_action = s->object->get_instance().empty()?
1020 rgw::IAM::s3GetObjectTagging:
1021 rgw::IAM::s3GetObjectVersionTagging;
1022
1023 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
1024 if (has_s3_existing_tag || has_s3_resource_tag)
1025 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
1026 if (!verify_object_permission(this, s,iam_action))
1027 return -EACCES;
1028
1029 return 0;
1030 }
1031
1032 void RGWGetObjTags::pre_exec()
1033 {
1034 rgw_bucket_object_pre_exec(s);
1035 }
1036
1037 void RGWGetObjTags::execute(optional_yield y)
1038 {
1039 rgw::sal::Attrs attrs;
1040
1041 s->object->set_atomic(s->obj_ctx);
1042
1043 op_ret = s->object->get_obj_attrs(s->obj_ctx, y, this);
1044 if (op_ret < 0) {
1045 ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << s->object
1046 << " ret=" << op_ret << dendl;
1047 return;
1048 }
1049
1050 attrs = s->object->get_attrs();
1051 auto tags = attrs.find(RGW_ATTR_TAGS);
1052 if(tags != attrs.end()){
1053 has_tags = true;
1054 tags_bl.append(tags->second);
1055 }
1056 send_response_data(tags_bl);
1057 }
1058
1059 int RGWPutObjTags::verify_permission(optional_yield y)
1060 {
1061 auto iam_action = s->object->get_instance().empty() ?
1062 rgw::IAM::s3PutObjectTagging:
1063 rgw::IAM::s3PutObjectVersionTagging;
1064
1065 //Using buckets tags for authorization makes more sense.
1066 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, true);
1067 if (has_s3_existing_tag)
1068 rgw_iam_add_objtags(this, s, true, false);
1069 if (has_s3_resource_tag)
1070 rgw_iam_add_buckettags(this, s);
1071 if (!verify_object_permission(this, s,iam_action))
1072 return -EACCES;
1073 return 0;
1074 }
1075
1076 void RGWPutObjTags::execute(optional_yield y)
1077 {
1078 op_ret = get_params(y);
1079 if (op_ret < 0)
1080 return;
1081
1082 if (rgw::sal::Object::empty(s->object.get())){
1083 op_ret= -EINVAL; // we only support tagging on existing objects
1084 return;
1085 }
1086
1087 s->object->set_atomic(s->obj_ctx);
1088 op_ret = s->object->modify_obj_attrs(s->obj_ctx, RGW_ATTR_TAGS, tags_bl, y, this);
1089 if (op_ret == -ECANCELED){
1090 op_ret = -ERR_TAG_CONFLICT;
1091 }
1092 }
1093
1094 void RGWDeleteObjTags::pre_exec()
1095 {
1096 rgw_bucket_object_pre_exec(s);
1097 }
1098
1099
1100 int RGWDeleteObjTags::verify_permission(optional_yield y)
1101 {
1102 if (!rgw::sal::Object::empty(s->object.get())) {
1103 auto iam_action = s->object->get_instance().empty() ?
1104 rgw::IAM::s3DeleteObjectTagging:
1105 rgw::IAM::s3DeleteObjectVersionTagging;
1106
1107 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
1108 if (has_s3_existing_tag || has_s3_resource_tag)
1109 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
1110 if (!verify_object_permission(this, s, iam_action))
1111 return -EACCES;
1112 }
1113 return 0;
1114 }
1115
1116 void RGWDeleteObjTags::execute(optional_yield y)
1117 {
1118 if (rgw::sal::Object::empty(s->object.get()))
1119 return;
1120
1121 op_ret = s->object->delete_obj_attrs(this, s->obj_ctx, RGW_ATTR_TAGS, y);
1122 }
1123
1124 int RGWGetBucketTags::verify_permission(optional_yield y)
1125 {
1126 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
1127 if (has_s3_resource_tag)
1128 rgw_iam_add_buckettags(this, s);
1129
1130 if (!verify_bucket_permission(this, s, rgw::IAM::s3GetBucketTagging)) {
1131 return -EACCES;
1132 }
1133
1134 return 0;
1135 }
1136
1137 void RGWGetBucketTags::pre_exec()
1138 {
1139 rgw_bucket_object_pre_exec(s);
1140 }
1141
1142 void RGWGetBucketTags::execute(optional_yield y)
1143 {
1144 auto iter = s->bucket_attrs.find(RGW_ATTR_TAGS);
1145 if (iter != s->bucket_attrs.end()) {
1146 has_tags = true;
1147 tags_bl.append(iter->second);
1148 } else {
1149 op_ret = -ERR_NO_SUCH_TAG_SET;
1150 }
1151 send_response_data(tags_bl);
1152 }
1153
1154 int RGWPutBucketTags::verify_permission(optional_yield y) {
1155 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
1156 if (has_s3_resource_tag)
1157 rgw_iam_add_buckettags(this, s);
1158
1159 return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketTagging);
1160 }
1161
1162 void RGWPutBucketTags::execute(optional_yield y)
1163 {
1164
1165 op_ret = get_params(this, y);
1166 if (op_ret < 0)
1167 return;
1168
1169 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, in_data, nullptr, s->info, y);
1170 if (op_ret < 0) {
1171 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
1172 }
1173
1174 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this, y] {
1175 rgw::sal::Attrs attrs = s->bucket->get_attrs();
1176 attrs[RGW_ATTR_TAGS] = tags_bl;
1177 return s->bucket->merge_and_store_attrs(this, attrs, y);
1178 });
1179
1180 }
1181
1182 void RGWDeleteBucketTags::pre_exec()
1183 {
1184 rgw_bucket_object_pre_exec(s);
1185 }
1186
1187 int RGWDeleteBucketTags::verify_permission(optional_yield y)
1188 {
1189 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
1190 if (has_s3_resource_tag)
1191 rgw_iam_add_buckettags(this, s);
1192
1193 return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketTagging);
1194 }
1195
1196 void RGWDeleteBucketTags::execute(optional_yield y)
1197 {
1198 bufferlist in_data;
1199 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, in_data, nullptr, s->info, y);
1200 if (op_ret < 0) {
1201 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
1202 return;
1203 }
1204
1205 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this, y] {
1206 rgw::sal::Attrs attrs = s->bucket->get_attrs();
1207 attrs.erase(RGW_ATTR_TAGS);
1208 op_ret = s->bucket->merge_and_store_attrs(this, attrs, y);
1209 if (op_ret < 0) {
1210 ldpp_dout(this, 0) << "RGWDeleteBucketTags() failed to remove RGW_ATTR_TAGS on bucket="
1211 << s->bucket->get_name()
1212 << " returned err= " << op_ret << dendl;
1213 }
1214 return op_ret;
1215 });
1216 }
1217
1218 int RGWGetBucketReplication::verify_permission(optional_yield y)
1219 {
1220 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
1221 if (has_s3_resource_tag)
1222 rgw_iam_add_buckettags(this, s);
1223
1224 if (!verify_bucket_permission(this, s, rgw::IAM::s3GetReplicationConfiguration)) {
1225 return -EACCES;
1226 }
1227
1228 return 0;
1229 }
1230
1231 void RGWGetBucketReplication::pre_exec()
1232 {
1233 rgw_bucket_object_pre_exec(s);
1234 }
1235
1236 void RGWGetBucketReplication::execute(optional_yield y)
1237 {
1238 send_response_data();
1239 }
1240
1241 int RGWPutBucketReplication::verify_permission(optional_yield y) {
1242 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
1243 if (has_s3_resource_tag)
1244 rgw_iam_add_buckettags(this, s);
1245 return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutReplicationConfiguration);
1246 }
1247
1248 void RGWPutBucketReplication::execute(optional_yield y) {
1249
1250 op_ret = get_params(y);
1251 if (op_ret < 0)
1252 return;
1253
1254 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, in_data, nullptr, s->info, y);
1255 if (op_ret < 0) {
1256 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
1257 return;
1258 }
1259
1260 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
1261 auto sync_policy = (s->bucket->get_info().sync_policy ? *s->bucket->get_info().sync_policy : rgw_sync_policy_info());
1262
1263 for (auto& group : sync_policy_groups) {
1264 sync_policy.groups[group.id] = group;
1265 }
1266
1267 s->bucket->get_info().set_sync_policy(std::move(sync_policy));
1268
1269 int ret = s->bucket->put_info(this, false, real_time());
1270 if (ret < 0) {
1271 ldpp_dout(this, 0) << "ERROR: put_bucket_instance_info (bucket=" << s->bucket << ") returned ret=" << ret << dendl;
1272 return ret;
1273 }
1274
1275 return 0;
1276 });
1277 }
1278
1279 void RGWDeleteBucketReplication::pre_exec()
1280 {
1281 rgw_bucket_object_pre_exec(s);
1282 }
1283
1284 int RGWDeleteBucketReplication::verify_permission(optional_yield y)
1285 {
1286 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
1287 if (has_s3_resource_tag)
1288 rgw_iam_add_buckettags(this, s);
1289
1290 return verify_bucket_owner_or_policy(s, rgw::IAM::s3DeleteReplicationConfiguration);
1291 }
1292
1293 void RGWDeleteBucketReplication::execute(optional_yield y)
1294 {
1295 bufferlist in_data;
1296 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, in_data, nullptr, s->info, y);
1297 if (op_ret < 0) {
1298 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
1299 return;
1300 }
1301
1302 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
1303 if (!s->bucket->get_info().sync_policy) {
1304 return 0;
1305 }
1306
1307 rgw_sync_policy_info sync_policy = *s->bucket->get_info().sync_policy;
1308
1309 update_sync_policy(&sync_policy);
1310
1311 s->bucket->get_info().set_sync_policy(std::move(sync_policy));
1312
1313 int ret = s->bucket->put_info(this, false, real_time());
1314 if (ret < 0) {
1315 ldpp_dout(this, 0) << "ERROR: put_bucket_instance_info (bucket=" << s->bucket << ") returned ret=" << ret << dendl;
1316 return ret;
1317 }
1318
1319 return 0;
1320 });
1321 }
1322
1323 int RGWOp::do_aws4_auth_completion()
1324 {
1325 ldpp_dout(this, 5) << "NOTICE: call to do_aws4_auth_completion" << dendl;
1326 if (s->auth.completer) {
1327 if (!s->auth.completer->complete()) {
1328 return -ERR_AMZ_CONTENT_SHA256_MISMATCH;
1329 } else {
1330 ldpp_dout(this, 10) << "v4 auth ok -- do_aws4_auth_completion" << dendl;
1331 }
1332
1333 /* TODO(rzarzynski): yes, we're really called twice on PUTs. Only first
1334 * call passes, so we disable second one. This is old behaviour, sorry!
1335 * Plan for tomorrow: seek and destroy. */
1336 s->auth.completer = nullptr;
1337 }
1338
1339 return 0;
1340 }
1341
1342 int RGWOp::init_quota()
1343 {
1344 /* no quota enforcement for system requests */
1345 if (s->system_request)
1346 return 0;
1347
1348 /* init quota related stuff */
1349 if (!(s->user->get_info().op_mask & RGW_OP_TYPE_MODIFY)) {
1350 return 0;
1351 }
1352
1353 /* only interested in object related ops */
1354 if (rgw::sal::Bucket::empty(s->bucket.get())
1355 || rgw::sal::Object::empty(s->object.get())) {
1356 return 0;
1357 }
1358
1359 std::unique_ptr<rgw::sal::User> owner_user =
1360 store->get_user(s->bucket->get_info().owner);
1361 rgw::sal::User* user;
1362
1363 if (s->user->get_id() == s->bucket_owner.get_id()) {
1364 user = s->user.get();
1365 } else {
1366 int r = owner_user->load_user(this, s->yield);
1367 if (r < 0)
1368 return r;
1369 user = owner_user.get();
1370 }
1371
1372 store->get_quota(bucket_quota, user_quota);
1373
1374 if (s->bucket->get_info().quota.enabled) {
1375 bucket_quota = s->bucket->get_info().quota;
1376 } else if (user->get_info().bucket_quota.enabled) {
1377 bucket_quota = user->get_info().bucket_quota;
1378 }
1379
1380 if (user->get_info().user_quota.enabled) {
1381 user_quota = user->get_info().user_quota;
1382 }
1383
1384 return 0;
1385 }
1386
1387 static bool validate_cors_rule_method(const DoutPrefixProvider *dpp, RGWCORSRule *rule, const char *req_meth) {
1388 uint8_t flags = 0;
1389
1390 if (!req_meth) {
1391 ldpp_dout(dpp, 5) << "req_meth is null" << dendl;
1392 return false;
1393 }
1394
1395 if (strcmp(req_meth, "GET") == 0) flags = RGW_CORS_GET;
1396 else if (strcmp(req_meth, "POST") == 0) flags = RGW_CORS_POST;
1397 else if (strcmp(req_meth, "PUT") == 0) flags = RGW_CORS_PUT;
1398 else if (strcmp(req_meth, "DELETE") == 0) flags = RGW_CORS_DELETE;
1399 else if (strcmp(req_meth, "HEAD") == 0) flags = RGW_CORS_HEAD;
1400
1401 if (rule->get_allowed_methods() & flags) {
1402 ldpp_dout(dpp, 10) << "Method " << req_meth << " is supported" << dendl;
1403 } else {
1404 ldpp_dout(dpp, 5) << "Method " << req_meth << " is not supported" << dendl;
1405 return false;
1406 }
1407
1408 return true;
1409 }
1410
1411 static bool validate_cors_rule_header(const DoutPrefixProvider *dpp, RGWCORSRule *rule, const char *req_hdrs) {
1412 if (req_hdrs) {
1413 vector<string> hdrs;
1414 get_str_vec(req_hdrs, hdrs);
1415 for (const auto& hdr : hdrs) {
1416 if (!rule->is_header_allowed(hdr.c_str(), hdr.length())) {
1417 ldpp_dout(dpp, 5) << "Header " << hdr << " is not registered in this rule" << dendl;
1418 return false;
1419 }
1420 }
1421 }
1422 return true;
1423 }
1424
1425 int RGWOp::read_bucket_cors()
1426 {
1427 bufferlist bl;
1428
1429 map<string, bufferlist>::iterator aiter = s->bucket_attrs.find(RGW_ATTR_CORS);
1430 if (aiter == s->bucket_attrs.end()) {
1431 ldpp_dout(this, 20) << "no CORS configuration attr found" << dendl;
1432 cors_exist = false;
1433 return 0; /* no CORS configuration found */
1434 }
1435
1436 cors_exist = true;
1437
1438 bl = aiter->second;
1439
1440 auto iter = bl.cbegin();
1441 try {
1442 bucket_cors.decode(iter);
1443 } catch (buffer::error& err) {
1444 ldpp_dout(this, 0) << "ERROR: could not decode CORS, caught buffer::error" << dendl;
1445 return -EIO;
1446 }
1447 if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 15>()) {
1448 RGWCORSConfiguration_S3 *s3cors = static_cast<RGWCORSConfiguration_S3 *>(&bucket_cors);
1449 ldpp_dout(this, 15) << "Read RGWCORSConfiguration";
1450 s3cors->to_xml(*_dout);
1451 *_dout << dendl;
1452 }
1453 return 0;
1454 }
1455
1456 /** CORS 6.2.6.
1457 * If any of the header field-names is not a ASCII case-insensitive match for
1458 * any of the values in list of headers do not set any additional headers and
1459 * terminate this set of steps.
1460 * */
1461 static void get_cors_response_headers(const DoutPrefixProvider *dpp, RGWCORSRule *rule, const char *req_hdrs, string& hdrs, string& exp_hdrs, unsigned *max_age) {
1462 if (req_hdrs) {
1463 list<string> hl;
1464 get_str_list(req_hdrs, hl);
1465 for(list<string>::iterator it = hl.begin(); it != hl.end(); ++it) {
1466 if (!rule->is_header_allowed((*it).c_str(), (*it).length())) {
1467 ldpp_dout(dpp, 5) << "Header " << (*it) << " is not registered in this rule" << dendl;
1468 } else {
1469 if (hdrs.length() > 0) hdrs.append(",");
1470 hdrs.append((*it));
1471 }
1472 }
1473 }
1474 rule->format_exp_headers(exp_hdrs);
1475 *max_age = rule->get_max_age();
1476 }
1477
1478 /**
1479 * Generate the CORS header response
1480 *
1481 * This is described in the CORS standard, section 6.2.
1482 */
1483 bool RGWOp::generate_cors_headers(string& origin, string& method, string& headers, string& exp_headers, unsigned *max_age)
1484 {
1485 /* CORS 6.2.1. */
1486 const char *orig = s->info.env->get("HTTP_ORIGIN");
1487 if (!orig) {
1488 return false;
1489 }
1490
1491 /* Custom: */
1492 origin = orig;
1493 int temp_op_ret = read_bucket_cors();
1494 if (temp_op_ret < 0) {
1495 op_ret = temp_op_ret;
1496 return false;
1497 }
1498
1499 if (!cors_exist) {
1500 ldpp_dout(this, 2) << "No CORS configuration set yet for this bucket" << dendl;
1501 return false;
1502 }
1503
1504 /* CORS 6.2.2. */
1505 RGWCORSRule *rule = bucket_cors.host_name_rule(orig);
1506 if (!rule)
1507 return false;
1508
1509 /*
1510 * Set the Allowed-Origin header to a asterisk if this is allowed in the rule
1511 * and no Authorization was send by the client
1512 *
1513 * The origin parameter specifies a URI that may access the resource. The browser must enforce this.
1514 * For requests without credentials, the server may specify "*" as a wildcard,
1515 * thereby allowing any origin to access the resource.
1516 */
1517 const char *authorization = s->info.env->get("HTTP_AUTHORIZATION");
1518 if (!authorization && rule->has_wildcard_origin())
1519 origin = "*";
1520
1521 /* CORS 6.2.3. */
1522 const char *req_meth = s->info.env->get("HTTP_ACCESS_CONTROL_REQUEST_METHOD");
1523 if (!req_meth) {
1524 req_meth = s->info.method;
1525 }
1526
1527 if (req_meth) {
1528 method = req_meth;
1529 /* CORS 6.2.5. */
1530 if (!validate_cors_rule_method(this, rule, req_meth)) {
1531 return false;
1532 }
1533 }
1534
1535 /* CORS 6.2.4. */
1536 const char *req_hdrs = s->info.env->get("HTTP_ACCESS_CONTROL_REQUEST_HEADERS");
1537
1538 /* CORS 6.2.6. */
1539 get_cors_response_headers(this, rule, req_hdrs, headers, exp_headers, max_age);
1540
1541 return true;
1542 }
1543
1544 int RGWGetObj::read_user_manifest_part(rgw::sal::Bucket* bucket,
1545 const rgw_bucket_dir_entry& ent,
1546 RGWAccessControlPolicy * const bucket_acl,
1547 const boost::optional<Policy>& bucket_policy,
1548 const off_t start_ofs,
1549 const off_t end_ofs,
1550 bool swift_slo)
1551 {
1552 ldpp_dout(this, 20) << "user manifest obj=" << ent.key.name
1553 << "[" << ent.key.instance << "]" << dendl;
1554 RGWGetObj_CB cb(this);
1555 RGWGetObj_Filter* filter = &cb;
1556 boost::optional<RGWGetObj_Decompress> decompress;
1557
1558 int64_t cur_ofs = start_ofs;
1559 int64_t cur_end = end_ofs;
1560
1561 std::unique_ptr<rgw::sal::Object> part = bucket->get_object(ent.key);
1562
1563 RGWObjectCtx obj_ctx(store);
1564 RGWAccessControlPolicy obj_policy(s->cct);
1565
1566 ldpp_dout(this, 20) << "reading obj=" << part << " ofs=" << cur_ofs
1567 << " end=" << cur_end << dendl;
1568
1569 part->set_atomic(&obj_ctx);
1570 part->set_prefetch_data(&obj_ctx);
1571
1572 std::unique_ptr<rgw::sal::Object::ReadOp> read_op = part->get_read_op(&obj_ctx);
1573
1574 if (!swift_slo) {
1575 /* SLO etag is optional */
1576 read_op->params.if_match = ent.meta.etag.c_str();
1577 }
1578
1579 op_ret = read_op->prepare(s->yield, this);
1580 if (op_ret < 0)
1581 return op_ret;
1582 op_ret = part->range_to_ofs(ent.meta.accounted_size, cur_ofs, cur_end);
1583 if (op_ret < 0)
1584 return op_ret;
1585 bool need_decompress;
1586 op_ret = rgw_compression_info_from_attrset(part->get_attrs(), need_decompress, cs_info);
1587 if (op_ret < 0) {
1588 ldpp_dout(this, 0) << "ERROR: failed to decode compression info" << dendl;
1589 return -EIO;
1590 }
1591
1592 if (need_decompress)
1593 {
1594 if (cs_info.orig_size != ent.meta.accounted_size) {
1595 // hmm.. something wrong, object not as expected, abort!
1596 ldpp_dout(this, 0) << "ERROR: expected cs_info.orig_size=" << cs_info.orig_size
1597 << ", actual read size=" << ent.meta.size << dendl;
1598 return -EIO;
1599 }
1600 decompress.emplace(s->cct, &cs_info, partial_content, filter);
1601 filter = &*decompress;
1602 }
1603 else
1604 {
1605 if (part->get_obj_size() != ent.meta.size) {
1606 // hmm.. something wrong, object not as expected, abort!
1607 ldpp_dout(this, 0) << "ERROR: expected obj_size=" << part->get_obj_size()
1608 << ", actual read size=" << ent.meta.size << dendl;
1609 return -EIO;
1610 }
1611 }
1612
1613 op_ret = rgw_policy_from_attrset(s, s->cct, part->get_attrs(), &obj_policy);
1614 if (op_ret < 0)
1615 return op_ret;
1616
1617 /* We can use global user_acl because LOs cannot have segments
1618 * stored inside different accounts. */
1619 if (s->system_request) {
1620 ldpp_dout(this, 2) << "overriding permissions due to system operation" << dendl;
1621 } else if (s->auth.identity->is_admin_of(s->user->get_id())) {
1622 ldpp_dout(this, 2) << "overriding permissions due to admin operation" << dendl;
1623 } else if (!verify_object_permission(this, s, part->get_obj(), s->user_acl.get(),
1624 bucket_acl, &obj_policy, bucket_policy,
1625 s->iam_user_policies, s->session_policies, action)) {
1626 return -EPERM;
1627 }
1628 if (ent.meta.size == 0) {
1629 return 0;
1630 }
1631
1632 perfcounter->inc(l_rgw_get_b, cur_end - cur_ofs);
1633 filter->fixup_range(cur_ofs, cur_end);
1634 op_ret = read_op->iterate(this, cur_ofs, cur_end, filter, s->yield);
1635 if (op_ret >= 0)
1636 op_ret = filter->flush();
1637 return op_ret;
1638 }
1639
1640 static int iterate_user_manifest_parts(const DoutPrefixProvider *dpp,
1641 CephContext * const cct,
1642 rgw::sal::Store* const store,
1643 const off_t ofs,
1644 const off_t end,
1645 rgw::sal::Bucket* bucket,
1646 const string& obj_prefix,
1647 RGWAccessControlPolicy * const bucket_acl,
1648 const boost::optional<Policy>& bucket_policy,
1649 uint64_t * const ptotal_len,
1650 uint64_t * const pobj_size,
1651 string * const pobj_sum,
1652 int (*cb)(rgw::sal::Bucket* bucket,
1653 const rgw_bucket_dir_entry& ent,
1654 RGWAccessControlPolicy * const bucket_acl,
1655 const boost::optional<Policy>& bucket_policy,
1656 off_t start_ofs,
1657 off_t end_ofs,
1658 void *param,
1659 bool swift_slo),
1660 void * const cb_param,
1661 optional_yield y)
1662 {
1663 uint64_t obj_ofs = 0, len_count = 0;
1664 bool found_start = false, found_end = false, handled_end = false;
1665 string delim;
1666
1667 utime_t start_time = ceph_clock_now();
1668
1669 rgw::sal::Bucket::ListParams params;
1670 params.prefix = obj_prefix;
1671 params.delim = delim;
1672
1673 rgw::sal::Bucket::ListResults results;
1674 MD5 etag_sum;
1675 // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
1676 etag_sum.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
1677 do {
1678 static constexpr auto MAX_LIST_OBJS = 100u;
1679 int r = bucket->list(dpp, params, MAX_LIST_OBJS, results, y);
1680 if (r < 0) {
1681 return r;
1682 }
1683
1684 for (rgw_bucket_dir_entry& ent : results.objs) {
1685 const uint64_t cur_total_len = obj_ofs;
1686 const uint64_t obj_size = ent.meta.accounted_size;
1687 uint64_t start_ofs = 0, end_ofs = obj_size;
1688
1689 if ((ptotal_len || cb) && !found_start && cur_total_len + obj_size > (uint64_t)ofs) {
1690 start_ofs = ofs - obj_ofs;
1691 found_start = true;
1692 }
1693
1694 obj_ofs += obj_size;
1695 if (pobj_sum) {
1696 etag_sum.Update((const unsigned char *)ent.meta.etag.c_str(),
1697 ent.meta.etag.length());
1698 }
1699
1700 if ((ptotal_len || cb) && !found_end && obj_ofs > (uint64_t)end) {
1701 end_ofs = end - cur_total_len + 1;
1702 found_end = true;
1703 }
1704
1705 perfcounter->tinc(l_rgw_get_lat,
1706 (ceph_clock_now() - start_time));
1707
1708 if (found_start && !handled_end) {
1709 len_count += end_ofs - start_ofs;
1710
1711 if (cb) {
1712 r = cb(bucket, ent, bucket_acl, bucket_policy, start_ofs, end_ofs,
1713 cb_param, false /* swift_slo */);
1714 if (r < 0) {
1715 return r;
1716 }
1717 }
1718 }
1719
1720 handled_end = found_end;
1721 start_time = ceph_clock_now();
1722 }
1723 } while (results.is_truncated);
1724
1725 if (ptotal_len) {
1726 *ptotal_len = len_count;
1727 }
1728 if (pobj_size) {
1729 *pobj_size = obj_ofs;
1730 }
1731 if (pobj_sum) {
1732 complete_etag(etag_sum, pobj_sum);
1733 }
1734
1735 return 0;
1736 }
1737
1738 struct rgw_slo_part {
1739 RGWAccessControlPolicy *bucket_acl = nullptr;
1740 Policy* bucket_policy = nullptr;
1741 rgw::sal::Bucket* bucket;
1742 string obj_name;
1743 uint64_t size = 0;
1744 string etag;
1745 };
1746
1747 static int iterate_slo_parts(const DoutPrefixProvider *dpp,
1748 CephContext *cct,
1749 rgw::sal::Store*store,
1750 off_t ofs,
1751 off_t end,
1752 map<uint64_t, rgw_slo_part>& slo_parts,
1753 int (*cb)(rgw::sal::Bucket* bucket,
1754 const rgw_bucket_dir_entry& ent,
1755 RGWAccessControlPolicy *bucket_acl,
1756 const boost::optional<Policy>& bucket_policy,
1757 off_t start_ofs,
1758 off_t end_ofs,
1759 void *param,
1760 bool swift_slo),
1761 void *cb_param)
1762 {
1763 bool found_start = false, found_end = false;
1764
1765 if (slo_parts.empty()) {
1766 return 0;
1767 }
1768
1769 utime_t start_time = ceph_clock_now();
1770
1771 map<uint64_t, rgw_slo_part>::iterator iter = slo_parts.upper_bound(ofs);
1772 if (iter != slo_parts.begin()) {
1773 --iter;
1774 }
1775
1776 uint64_t obj_ofs = iter->first;
1777
1778 for (; iter != slo_parts.end() && !found_end; ++iter) {
1779 rgw_slo_part& part = iter->second;
1780 rgw_bucket_dir_entry ent;
1781
1782 ent.key.name = part.obj_name;
1783 ent.meta.accounted_size = ent.meta.size = part.size;
1784 ent.meta.etag = part.etag;
1785
1786 uint64_t cur_total_len = obj_ofs;
1787 uint64_t start_ofs = 0, end_ofs = ent.meta.size - 1;
1788
1789 if (!found_start && cur_total_len + ent.meta.size > (uint64_t)ofs) {
1790 start_ofs = ofs - obj_ofs;
1791 found_start = true;
1792 }
1793
1794 obj_ofs += ent.meta.size;
1795
1796 if (!found_end && obj_ofs > (uint64_t)end) {
1797 end_ofs = end - cur_total_len;
1798 found_end = true;
1799 }
1800
1801 perfcounter->tinc(l_rgw_get_lat,
1802 (ceph_clock_now() - start_time));
1803
1804 if (found_start) {
1805 if (cb) {
1806 ldpp_dout(dpp, 20) << "iterate_slo_parts()"
1807 << " obj=" << part.obj_name
1808 << " start_ofs=" << start_ofs
1809 << " end_ofs=" << end_ofs
1810 << dendl;
1811
1812 // SLO is a Swift thing, and Swift has no knowledge of S3 Policies.
1813 int r = cb(part.bucket, ent, part.bucket_acl,
1814 (part.bucket_policy ?
1815 boost::optional<Policy>(*part.bucket_policy) : none),
1816 start_ofs, end_ofs, cb_param, true /* swift_slo */);
1817 if (r < 0)
1818 return r;
1819 }
1820 }
1821
1822 start_time = ceph_clock_now();
1823 }
1824
1825 return 0;
1826 }
1827
1828 static int get_obj_user_manifest_iterate_cb(rgw::sal::Bucket* bucket,
1829 const rgw_bucket_dir_entry& ent,
1830 RGWAccessControlPolicy * const bucket_acl,
1831 const boost::optional<Policy>& bucket_policy,
1832 const off_t start_ofs,
1833 const off_t end_ofs,
1834 void * const param,
1835 bool swift_slo = false)
1836 {
1837 RGWGetObj *op = static_cast<RGWGetObj *>(param);
1838 return op->read_user_manifest_part(
1839 bucket, ent, bucket_acl, bucket_policy, start_ofs, end_ofs, swift_slo);
1840 }
1841
1842 int RGWGetObj::handle_user_manifest(const char *prefix, optional_yield y)
1843 {
1844 const std::string_view prefix_view(prefix);
1845 ldpp_dout(this, 2) << "RGWGetObj::handle_user_manifest() prefix="
1846 << prefix_view << dendl;
1847
1848 const size_t pos = prefix_view.find('/');
1849 if (pos == string::npos) {
1850 return -EINVAL;
1851 }
1852
1853 const std::string bucket_name = url_decode(prefix_view.substr(0, pos));
1854 const std::string obj_prefix = url_decode(prefix_view.substr(pos + 1));
1855
1856 RGWAccessControlPolicy _bucket_acl(s->cct);
1857 RGWAccessControlPolicy *bucket_acl;
1858 boost::optional<Policy> _bucket_policy;
1859 boost::optional<Policy>* bucket_policy;
1860 RGWBucketInfo bucket_info;
1861 std::unique_ptr<rgw::sal::Bucket> ubucket;
1862 rgw::sal::Bucket* pbucket = NULL;
1863 int r = 0;
1864
1865 if (bucket_name.compare(s->bucket->get_name()) != 0) {
1866 map<string, bufferlist> bucket_attrs;
1867 r = store->get_bucket(this, s->user.get(), s->user->get_tenant(), bucket_name, &ubucket, y);
1868 if (r < 0) {
1869 ldpp_dout(this, 0) << "could not get bucket info for bucket="
1870 << bucket_name << dendl;
1871 return r;
1872 }
1873 bucket_acl = &_bucket_acl;
1874 r = read_bucket_policy(this, store, s, ubucket->get_info(), bucket_attrs, bucket_acl, ubucket->get_key(), y);
1875 if (r < 0) {
1876 ldpp_dout(this, 0) << "failed to read bucket policy" << dendl;
1877 return r;
1878 }
1879 _bucket_policy = get_iam_policy_from_attr(s->cct, bucket_attrs, s->user->get_tenant());
1880 bucket_policy = &_bucket_policy;
1881 pbucket = ubucket.get();
1882 } else {
1883 pbucket = s->bucket.get();
1884 bucket_acl = s->bucket_acl.get();
1885 bucket_policy = &s->iam_policy;
1886 }
1887
1888 /* dry run to find out:
1889 * - total length (of the parts we are going to send to client),
1890 * - overall DLO's content size,
1891 * - md5 sum of overall DLO's content (for etag of Swift API). */
1892 r = iterate_user_manifest_parts(this, s->cct, store, ofs, end,
1893 pbucket, obj_prefix, bucket_acl, *bucket_policy,
1894 nullptr, &s->obj_size, &lo_etag,
1895 nullptr /* cb */, nullptr /* cb arg */, y);
1896 if (r < 0) {
1897 return r;
1898 }
1899 s->object->set_obj_size(s->obj_size);
1900
1901 r = s->object->range_to_ofs(s->obj_size, ofs, end);
1902 if (r < 0) {
1903 return r;
1904 }
1905
1906 r = iterate_user_manifest_parts(this, s->cct, store, ofs, end,
1907 pbucket, obj_prefix, bucket_acl, *bucket_policy,
1908 &total_len, nullptr, nullptr,
1909 nullptr, nullptr, y);
1910 if (r < 0) {
1911 return r;
1912 }
1913
1914 if (!get_data) {
1915 bufferlist bl;
1916 send_response_data(bl, 0, 0);
1917 return 0;
1918 }
1919
1920 r = iterate_user_manifest_parts(this, s->cct, store, ofs, end,
1921 pbucket, obj_prefix, bucket_acl, *bucket_policy,
1922 nullptr, nullptr, nullptr,
1923 get_obj_user_manifest_iterate_cb, (void *)this, y);
1924 if (r < 0) {
1925 return r;
1926 }
1927
1928 if (!total_len) {
1929 bufferlist bl;
1930 send_response_data(bl, 0, 0);
1931 }
1932
1933 return r;
1934 }
1935
1936 int RGWGetObj::handle_slo_manifest(bufferlist& bl, optional_yield y)
1937 {
1938 RGWSLOInfo slo_info;
1939 auto bliter = bl.cbegin();
1940 try {
1941 decode(slo_info, bliter);
1942 } catch (buffer::error& err) {
1943 ldpp_dout(this, 0) << "ERROR: failed to decode slo manifest" << dendl;
1944 return -EIO;
1945 }
1946 ldpp_dout(this, 2) << "RGWGetObj::handle_slo_manifest()" << dendl;
1947
1948 vector<RGWAccessControlPolicy> allocated_acls;
1949 map<string, pair<RGWAccessControlPolicy *, boost::optional<Policy>>> policies;
1950 map<string, std::unique_ptr<rgw::sal::Bucket>> buckets;
1951
1952 map<uint64_t, rgw_slo_part> slo_parts;
1953
1954 MD5 etag_sum;
1955 // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
1956 etag_sum.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
1957 total_len = 0;
1958
1959 for (const auto& entry : slo_info.entries) {
1960 const string& path = entry.path;
1961
1962 /* If the path starts with slashes, strip them all. */
1963 const size_t pos_init = path.find_first_not_of('/');
1964 /* According to the documentation of std::string::find following check
1965 * is not necessary as we should get the std::string::npos propagation
1966 * here. This might be true with the accuracy to implementation's bugs.
1967 * See following question on SO:
1968 * http://stackoverflow.com/questions/1011790/why-does-stdstring-findtext-stdstringnpos-not-return-npos
1969 */
1970 if (pos_init == string::npos) {
1971 return -EINVAL;
1972 }
1973
1974 const size_t pos_sep = path.find('/', pos_init);
1975 if (pos_sep == string::npos) {
1976 return -EINVAL;
1977 }
1978
1979 string bucket_name = path.substr(pos_init, pos_sep - pos_init);
1980 string obj_name = path.substr(pos_sep + 1);
1981
1982 rgw::sal::Bucket* bucket;
1983 RGWAccessControlPolicy *bucket_acl;
1984 Policy* bucket_policy;
1985
1986 if (bucket_name.compare(s->bucket->get_name()) != 0) {
1987 const auto& piter = policies.find(bucket_name);
1988 if (piter != policies.end()) {
1989 bucket_acl = piter->second.first;
1990 bucket_policy = piter->second.second.get_ptr();
1991 bucket = buckets[bucket_name].get();
1992 } else {
1993 allocated_acls.push_back(RGWAccessControlPolicy(s->cct));
1994 RGWAccessControlPolicy& _bucket_acl = allocated_acls.back();
1995
1996 std::unique_ptr<rgw::sal::Bucket> tmp_bucket;
1997 int r = store->get_bucket(this, s->user.get(), s->user->get_tenant(), bucket_name, &tmp_bucket, y);
1998 if (r < 0) {
1999 ldpp_dout(this, 0) << "could not get bucket info for bucket="
2000 << bucket_name << dendl;
2001 return r;
2002 }
2003 bucket = tmp_bucket.get();
2004 bucket_acl = &_bucket_acl;
2005 r = read_bucket_policy(this, store, s, tmp_bucket->get_info(), tmp_bucket->get_attrs(), bucket_acl,
2006 tmp_bucket->get_key(), y);
2007 if (r < 0) {
2008 ldpp_dout(this, 0) << "failed to read bucket ACL for bucket "
2009 << bucket << dendl;
2010 return r;
2011 }
2012 auto _bucket_policy = get_iam_policy_from_attr(
2013 s->cct, tmp_bucket->get_attrs(), tmp_bucket->get_tenant());
2014 bucket_policy = _bucket_policy.get_ptr();
2015 buckets[bucket_name].swap(tmp_bucket);
2016 policies[bucket_name] = make_pair(bucket_acl, _bucket_policy);
2017 }
2018 } else {
2019 bucket = s->bucket.get();
2020 bucket_acl = s->bucket_acl.get();
2021 bucket_policy = s->iam_policy.get_ptr();
2022 }
2023
2024 rgw_slo_part part;
2025 part.bucket_acl = bucket_acl;
2026 part.bucket_policy = bucket_policy;
2027 part.bucket = bucket;
2028 part.obj_name = obj_name;
2029 part.size = entry.size_bytes;
2030 part.etag = entry.etag;
2031 ldpp_dout(this, 20) << "slo_part: bucket=" << part.bucket
2032 << " obj=" << part.obj_name
2033 << " size=" << part.size
2034 << " etag=" << part.etag
2035 << dendl;
2036
2037 etag_sum.Update((const unsigned char *)entry.etag.c_str(),
2038 entry.etag.length());
2039
2040 slo_parts[total_len] = part;
2041 total_len += part.size;
2042 } /* foreach entry */
2043
2044 complete_etag(etag_sum, &lo_etag);
2045
2046 s->obj_size = slo_info.total_size;
2047 s->object->set_obj_size(slo_info.total_size);
2048 ldpp_dout(this, 20) << "s->obj_size=" << s->obj_size << dendl;
2049
2050 int r = s->object->range_to_ofs(total_len, ofs, end);
2051 if (r < 0) {
2052 return r;
2053 }
2054
2055 total_len = end - ofs + 1;
2056 ldpp_dout(this, 20) << "Requested: ofs=" << ofs
2057 << " end=" << end
2058 << " total=" << total_len
2059 << dendl;
2060
2061 r = iterate_slo_parts(this, s->cct, store, ofs, end, slo_parts,
2062 get_obj_user_manifest_iterate_cb, (void *)this);
2063 if (r < 0) {
2064 return r;
2065 }
2066
2067 return 0;
2068 }
2069
2070 int RGWGetObj::get_data_cb(bufferlist& bl, off_t bl_ofs, off_t bl_len)
2071 {
2072 /* garbage collection related handling:
2073 * defer_gc disabled for https://tracker.ceph.com/issues/47866 */
2074 return send_response_data(bl, bl_ofs, bl_len);
2075 }
2076
2077 bool RGWGetObj::prefetch_data()
2078 {
2079 /* HEAD request, stop prefetch*/
2080 if (!get_data || s->info.env->exists("HTTP_X_RGW_AUTH")) {
2081 return false;
2082 }
2083
2084 range_str = s->info.env->get("HTTP_RANGE");
2085 // TODO: add range prefetch
2086 if (range_str) {
2087 parse_range();
2088 return false;
2089 }
2090
2091 return get_data;
2092 }
2093
2094 void RGWGetObj::pre_exec()
2095 {
2096 rgw_bucket_object_pre_exec(s);
2097 }
2098
2099 static inline void rgw_cond_decode_objtags(
2100 struct req_state *s,
2101 const std::map<std::string, buffer::list> &attrs)
2102 {
2103 const auto& tags = attrs.find(RGW_ATTR_TAGS);
2104 if (tags != attrs.end()) {
2105 try {
2106 bufferlist::const_iterator iter{&tags->second};
2107 s->tagset.decode(iter);
2108 } catch (buffer::error& err) {
2109 ldpp_dout(s, 0)
2110 << "ERROR: caught buffer::error, couldn't decode TagSet" << dendl;
2111 }
2112 }
2113 }
2114
2115 void RGWGetObj::execute(optional_yield y)
2116 {
2117 bufferlist bl;
2118 gc_invalidate_time = ceph_clock_now();
2119 gc_invalidate_time += (s->cct->_conf->rgw_gc_obj_min_wait / 2);
2120
2121 bool need_decompress;
2122 int64_t ofs_x, end_x;
2123
2124 RGWGetObj_CB cb(this);
2125 RGWGetObj_Filter* filter = (RGWGetObj_Filter *)&cb;
2126 boost::optional<RGWGetObj_Decompress> decompress;
2127 std::unique_ptr<RGWGetObj_Filter> decrypt;
2128 map<string, bufferlist>::iterator attr_iter;
2129
2130 perfcounter->inc(l_rgw_get);
2131
2132 std::unique_ptr<rgw::sal::Object::ReadOp> read_op(s->object->get_read_op(s->obj_ctx));
2133
2134 op_ret = get_params(y);
2135 if (op_ret < 0)
2136 goto done_err;
2137
2138 op_ret = init_common();
2139 if (op_ret < 0)
2140 goto done_err;
2141
2142 read_op->params.mod_ptr = mod_ptr;
2143 read_op->params.unmod_ptr = unmod_ptr;
2144 read_op->params.high_precision_time = s->system_request; /* system request need to use high precision time */
2145 read_op->params.mod_zone_id = mod_zone_id;
2146 read_op->params.mod_pg_ver = mod_pg_ver;
2147 read_op->params.if_match = if_match;
2148 read_op->params.if_nomatch = if_nomatch;
2149 read_op->params.lastmod = &lastmod;
2150
2151 op_ret = read_op->prepare(s->yield, this);
2152 if (op_ret < 0)
2153 goto done_err;
2154 version_id = s->object->get_instance();
2155 s->obj_size = s->object->get_obj_size();
2156 attrs = s->object->get_attrs();
2157
2158 /* STAT ops don't need data, and do no i/o */
2159 if (get_type() == RGW_OP_STAT_OBJ) {
2160 return;
2161 }
2162 if (s->info.env->exists("HTTP_X_RGW_AUTH")) {
2163 op_ret = 0;
2164 goto done_err;
2165 }
2166 /* start gettorrent */
2167 if (torrent.get_flag())
2168 {
2169 attr_iter = attrs.find(RGW_ATTR_CRYPT_MODE);
2170 if (attr_iter != attrs.end() && attr_iter->second.to_str() == "SSE-C-AES256") {
2171 ldpp_dout(this, 0) << "ERROR: torrents are not supported for objects "
2172 "encrypted with SSE-C" << dendl;
2173 op_ret = -EINVAL;
2174 goto done_err;
2175 }
2176 torrent.init(s, store);
2177 rgw_obj obj = s->object->get_obj();
2178 op_ret = torrent.get_torrent_file(s->object.get(), total_len, bl, obj);
2179 if (op_ret < 0)
2180 {
2181 ldpp_dout(this, 0) << "ERROR: failed to get_torrent_file ret= " << op_ret
2182 << dendl;
2183 goto done_err;
2184 }
2185 op_ret = send_response_data(bl, 0, total_len);
2186 if (op_ret < 0)
2187 {
2188 ldpp_dout(this, 0) << "ERROR: failed to send_response_data ret= " << op_ret << dendl;
2189 goto done_err;
2190 }
2191 return;
2192 }
2193 /* end gettorrent */
2194
2195 op_ret = rgw_compression_info_from_attrset(attrs, need_decompress, cs_info);
2196 if (op_ret < 0) {
2197 ldpp_dout(this, 0) << "ERROR: failed to decode compression info, cannot decompress" << dendl;
2198 goto done_err;
2199 }
2200 if (need_decompress) {
2201 s->obj_size = cs_info.orig_size;
2202 s->object->set_obj_size(cs_info.orig_size);
2203 decompress.emplace(s->cct, &cs_info, partial_content, filter);
2204 filter = &*decompress;
2205 }
2206
2207 attr_iter = attrs.find(RGW_ATTR_MANIFEST);
2208 if (attr_iter != attrs.end() && get_type() == RGW_OP_GET_OBJ && get_data) {
2209 RGWObjManifest m;
2210 decode(m, attr_iter->second);
2211 if (m.get_tier_type() == "cloud-s3") {
2212 /* XXX: Instead send presigned redirect or read-through */
2213 op_ret = -ERR_INVALID_OBJECT_STATE;
2214 ldpp_dout(this, 0) << "ERROR: Cannot get cloud tiered object. Failing with "
2215 << op_ret << dendl;
2216 goto done_err;
2217 }
2218 }
2219
2220 attr_iter = attrs.find(RGW_ATTR_USER_MANIFEST);
2221 if (attr_iter != attrs.end() && !skip_manifest) {
2222 op_ret = handle_user_manifest(attr_iter->second.c_str(), y);
2223 if (op_ret < 0) {
2224 ldpp_dout(this, 0) << "ERROR: failed to handle user manifest ret="
2225 << op_ret << dendl;
2226 goto done_err;
2227 }
2228 return;
2229 }
2230
2231 attr_iter = attrs.find(RGW_ATTR_SLO_MANIFEST);
2232 if (attr_iter != attrs.end() && !skip_manifest) {
2233 is_slo = true;
2234 op_ret = handle_slo_manifest(attr_iter->second, y);
2235 if (op_ret < 0) {
2236 ldpp_dout(this, 0) << "ERROR: failed to handle slo manifest ret=" << op_ret
2237 << dendl;
2238 goto done_err;
2239 }
2240 return;
2241 }
2242
2243 // for range requests with obj size 0
2244 if (range_str && !(s->obj_size)) {
2245 total_len = 0;
2246 op_ret = -ERANGE;
2247 goto done_err;
2248 }
2249
2250 op_ret = s->object->range_to_ofs(s->obj_size, ofs, end);
2251 if (op_ret < 0)
2252 goto done_err;
2253 total_len = (ofs <= end ? end + 1 - ofs : 0);
2254
2255 /* Check whether the object has expired. Swift API documentation
2256 * stands that we should return 404 Not Found in such case. */
2257 if (need_object_expiration() && s->object->is_expired()) {
2258 op_ret = -ENOENT;
2259 goto done_err;
2260 }
2261
2262 /* Decode S3 objtags, if any */
2263 rgw_cond_decode_objtags(s, attrs);
2264
2265 start = ofs;
2266
2267 attr_iter = attrs.find(RGW_ATTR_MANIFEST);
2268 op_ret = this->get_decrypt_filter(&decrypt, filter,
2269 attr_iter != attrs.end() ? &(attr_iter->second) : nullptr);
2270 if (decrypt != nullptr) {
2271 filter = decrypt.get();
2272 }
2273 if (op_ret < 0) {
2274 goto done_err;
2275 }
2276
2277 if (!get_data || ofs > end) {
2278 send_response_data(bl, 0, 0);
2279 return;
2280 }
2281
2282 perfcounter->inc(l_rgw_get_b, end - ofs);
2283
2284 ofs_x = ofs;
2285 end_x = end;
2286 filter->fixup_range(ofs_x, end_x);
2287 op_ret = read_op->iterate(this, ofs_x, end_x, filter, s->yield);
2288
2289 if (op_ret >= 0)
2290 op_ret = filter->flush();
2291
2292 perfcounter->tinc(l_rgw_get_lat, s->time_elapsed());
2293 if (op_ret < 0) {
2294 goto done_err;
2295 }
2296
2297 op_ret = send_response_data(bl, 0, 0);
2298 if (op_ret < 0) {
2299 goto done_err;
2300 }
2301 return;
2302
2303 done_err:
2304 send_response_data_error(y);
2305 }
2306
2307 int RGWGetObj::init_common()
2308 {
2309 if (range_str) {
2310 /* range parsed error when prefetch */
2311 if (!range_parsed) {
2312 int r = parse_range();
2313 if (r < 0)
2314 return r;
2315 }
2316 }
2317 if (if_mod) {
2318 if (parse_time(if_mod, &mod_time) < 0)
2319 return -EINVAL;
2320 mod_ptr = &mod_time;
2321 }
2322
2323 if (if_unmod) {
2324 if (parse_time(if_unmod, &unmod_time) < 0)
2325 return -EINVAL;
2326 unmod_ptr = &unmod_time;
2327 }
2328
2329 return 0;
2330 }
2331
2332 int RGWListBuckets::verify_permission(optional_yield y)
2333 {
2334 rgw::Partition partition = rgw::Partition::aws;
2335 rgw::Service service = rgw::Service::s3;
2336
2337 string tenant;
2338 if (s->auth.identity->get_identity_type() == TYPE_ROLE) {
2339 tenant = s->auth.identity->get_role_tenant();
2340 } else {
2341 tenant = s->user->get_tenant();
2342 }
2343
2344 if (!verify_user_permission(this, s, ARN(partition, service, "", tenant, "*"), rgw::IAM::s3ListAllMyBuckets)) {
2345 return -EACCES;
2346 }
2347
2348 return 0;
2349 }
2350
2351 int RGWGetUsage::verify_permission(optional_yield y)
2352 {
2353 if (s->auth.identity->is_anonymous()) {
2354 return -EACCES;
2355 }
2356
2357 return 0;
2358 }
2359
2360 void RGWListBuckets::execute(optional_yield y)
2361 {
2362 bool done;
2363 bool started = false;
2364 uint64_t total_count = 0;
2365
2366 const uint64_t max_buckets = s->cct->_conf->rgw_list_buckets_max_chunk;
2367
2368 op_ret = get_params(y);
2369 if (op_ret < 0) {
2370 goto send_end;
2371 }
2372
2373 if (supports_account_metadata()) {
2374 op_ret = s->user->read_attrs(this, s->yield);
2375 if (op_ret < 0) {
2376 goto send_end;
2377 }
2378 }
2379
2380 is_truncated = false;
2381 do {
2382 rgw::sal::BucketList buckets;
2383 uint64_t read_count;
2384 if (limit >= 0) {
2385 read_count = min(limit - total_count, max_buckets);
2386 } else {
2387 read_count = max_buckets;
2388 }
2389
2390 op_ret = s->user->list_buckets(this, marker, end_marker, read_count, should_get_stats(), buckets, y);
2391
2392 if (op_ret < 0) {
2393 /* hmm.. something wrong here.. the user was authenticated, so it
2394 should exist */
2395 ldpp_dout(this, 10) << "WARNING: failed on rgw_get_user_buckets uid="
2396 << s->user->get_id() << dendl;
2397 break;
2398 }
2399
2400 /* We need to have stats for all our policies - even if a given policy
2401 * isn't actually used in a given account. In such situation its usage
2402 * stats would be simply full of zeros. */
2403 for (const auto& policy : store->get_zone()->get_zonegroup().placement_targets) {
2404 policies_stats.emplace(policy.second.name,
2405 decltype(policies_stats)::mapped_type());
2406 }
2407
2408 std::map<std::string, std::unique_ptr<rgw::sal::Bucket>>& m = buckets.get_buckets();
2409 for (const auto& kv : m) {
2410 const auto& bucket = kv.second;
2411
2412 global_stats.bytes_used += bucket->get_size();
2413 global_stats.bytes_used_rounded += bucket->get_size_rounded();
2414 global_stats.objects_count += bucket->get_count();
2415
2416 /* operator[] still can create a new entry for storage policy seen
2417 * for first time. */
2418 auto& policy_stats = policies_stats[bucket->get_placement_rule().to_str()];
2419 policy_stats.bytes_used += bucket->get_size();
2420 policy_stats.bytes_used_rounded += bucket->get_size_rounded();
2421 policy_stats.buckets_count++;
2422 policy_stats.objects_count += bucket->get_count();
2423 }
2424 global_stats.buckets_count += m.size();
2425 total_count += m.size();
2426
2427 done = (m.size() < read_count || (limit >= 0 && total_count >= (uint64_t)limit));
2428
2429 if (!started) {
2430 send_response_begin(buckets.count() > 0);
2431 started = true;
2432 }
2433
2434 if (read_count > 0 &&
2435 !m.empty()) {
2436 auto riter = m.rbegin();
2437 marker = riter->first;
2438
2439 handle_listing_chunk(std::move(buckets));
2440 }
2441 } while (is_truncated && !done);
2442
2443 send_end:
2444 if (!started) {
2445 send_response_begin(false);
2446 }
2447 send_response_end();
2448 }
2449
2450 void RGWGetUsage::execute(optional_yield y)
2451 {
2452 uint64_t start_epoch = 0;
2453 uint64_t end_epoch = (uint64_t)-1;
2454 op_ret = get_params(y);
2455 if (op_ret < 0)
2456 return;
2457
2458 if (!start_date.empty()) {
2459 op_ret = utime_t::parse_date(start_date, &start_epoch, NULL);
2460 if (op_ret < 0) {
2461 ldpp_dout(this, 0) << "ERROR: failed to parse start date" << dendl;
2462 return;
2463 }
2464 }
2465
2466 if (!end_date.empty()) {
2467 op_ret = utime_t::parse_date(end_date, &end_epoch, NULL);
2468 if (op_ret < 0) {
2469 ldpp_dout(this, 0) << "ERROR: failed to parse end date" << dendl;
2470 return;
2471 }
2472 }
2473
2474 uint32_t max_entries = 1000;
2475
2476 bool is_truncated = true;
2477
2478 RGWUsageIter usage_iter;
2479
2480 while (s->bucket && is_truncated) {
2481 op_ret = s->bucket->read_usage(this, start_epoch, end_epoch, max_entries, &is_truncated,
2482 usage_iter, usage);
2483 if (op_ret == -ENOENT) {
2484 op_ret = 0;
2485 is_truncated = false;
2486 }
2487
2488 if (op_ret < 0) {
2489 return;
2490 }
2491 }
2492
2493 op_ret = rgw_user_sync_all_stats(this, store, s->user.get(), y);
2494 if (op_ret < 0) {
2495 ldpp_dout(this, 0) << "ERROR: failed to sync user stats" << dendl;
2496 return;
2497 }
2498
2499 op_ret = rgw_user_get_all_buckets_stats(this, store, s->user.get(), buckets_usage, y);
2500 if (op_ret < 0) {
2501 ldpp_dout(this, 0) << "ERROR: failed to get user's buckets stats" << dendl;
2502 return;
2503 }
2504
2505 op_ret = s->user->read_stats(this, y, &stats);
2506 if (op_ret < 0) {
2507 ldpp_dout(this, 0) << "ERROR: can't read user header" << dendl;
2508 return;
2509 }
2510
2511 return;
2512 }
2513
2514 int RGWStatAccount::verify_permission(optional_yield y)
2515 {
2516 if (!verify_user_permission_no_policy(this, s, RGW_PERM_READ)) {
2517 return -EACCES;
2518 }
2519
2520 return 0;
2521 }
2522
2523 void RGWStatAccount::execute(optional_yield y)
2524 {
2525 string marker;
2526 rgw::sal::BucketList buckets;
2527 uint64_t max_buckets = s->cct->_conf->rgw_list_buckets_max_chunk;
2528 const string *lastmarker;
2529
2530 do {
2531
2532 lastmarker = nullptr;
2533 op_ret = s->user->list_buckets(this, marker, string(), max_buckets, true, buckets, y);
2534 if (op_ret < 0) {
2535 /* hmm.. something wrong here.. the user was authenticated, so it
2536 should exist */
2537 ldpp_dout(this, 10) << "WARNING: failed on list_buckets uid="
2538 << s->user->get_id() << " ret=" << op_ret << dendl;
2539 break;
2540 } else {
2541 /* We need to have stats for all our policies - even if a given policy
2542 * isn't actually used in a given account. In such situation its usage
2543 * stats would be simply full of zeros. */
2544 for (const auto& policy : store->get_zone()->get_zonegroup().placement_targets) {
2545 policies_stats.emplace(policy.second.name,
2546 decltype(policies_stats)::mapped_type());
2547 }
2548
2549 std::map<std::string, std::unique_ptr<rgw::sal::Bucket>>& m = buckets.get_buckets();
2550 for (const auto& kv : m) {
2551 const auto& bucket = kv.second;
2552 lastmarker = &kv.first;
2553
2554 global_stats.bytes_used += bucket->get_size();
2555 global_stats.bytes_used_rounded += bucket->get_size_rounded();
2556 global_stats.objects_count += bucket->get_count();
2557
2558 /* operator[] still can create a new entry for storage policy seen
2559 * for first time. */
2560 auto& policy_stats = policies_stats[bucket->get_placement_rule().to_str()];
2561 policy_stats.bytes_used += bucket->get_size();
2562 policy_stats.bytes_used_rounded += bucket->get_size_rounded();
2563 policy_stats.buckets_count++;
2564 policy_stats.objects_count += bucket->get_count();
2565 }
2566 global_stats.buckets_count += m.size();
2567
2568 }
2569 if (!lastmarker) {
2570 ldpp_dout(this, -1) << "ERROR: rgw_read_user_buckets, stasis at marker="
2571 << marker << " uid=" << s->user->get_id() << dendl;
2572 break;
2573 }
2574 marker = *lastmarker;
2575 } while (buckets.is_truncated());
2576 }
2577
2578 int RGWGetBucketVersioning::verify_permission(optional_yield y)
2579 {
2580 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
2581 if (has_s3_resource_tag)
2582 rgw_iam_add_buckettags(this, s);
2583
2584 return verify_bucket_owner_or_policy(s, rgw::IAM::s3GetBucketVersioning);
2585 }
2586
2587 void RGWGetBucketVersioning::pre_exec()
2588 {
2589 rgw_bucket_object_pre_exec(s);
2590 }
2591
2592 void RGWGetBucketVersioning::execute(optional_yield y)
2593 {
2594 if (! s->bucket_exists) {
2595 op_ret = -ERR_NO_SUCH_BUCKET;
2596 return;
2597 }
2598
2599 versioned = s->bucket->versioned();
2600 versioning_enabled = s->bucket->versioning_enabled();
2601 mfa_enabled = s->bucket->get_info().mfa_enabled();
2602 }
2603
2604 int RGWSetBucketVersioning::verify_permission(optional_yield y)
2605 {
2606 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
2607 if (has_s3_resource_tag)
2608 rgw_iam_add_buckettags(this, s);
2609
2610 return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketVersioning);
2611 }
2612
2613 void RGWSetBucketVersioning::pre_exec()
2614 {
2615 rgw_bucket_object_pre_exec(s);
2616 }
2617
2618 void RGWSetBucketVersioning::execute(optional_yield y)
2619 {
2620 op_ret = get_params(y);
2621 if (op_ret < 0)
2622 return;
2623
2624 if (! s->bucket_exists) {
2625 op_ret = -ERR_NO_SUCH_BUCKET;
2626 return;
2627 }
2628
2629 if (s->bucket->get_info().obj_lock_enabled() && versioning_status != VersioningEnabled) {
2630 s->err.message = "bucket versioning cannot be disabled on buckets with object lock enabled";
2631 ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
2632 op_ret = -ERR_INVALID_BUCKET_STATE;
2633 return;
2634 }
2635
2636 bool cur_mfa_status = s->bucket->get_info().mfa_enabled();
2637
2638 mfa_set_status &= (mfa_status != cur_mfa_status);
2639
2640 if (mfa_set_status &&
2641 !s->mfa_verified) {
2642 op_ret = -ERR_MFA_REQUIRED;
2643 return;
2644 }
2645 //if mfa is enabled for bucket, make sure mfa code is validated in case versioned status gets changed
2646 if (cur_mfa_status) {
2647 bool req_versioning_status = false;
2648 //if requested versioning status is not the same as the one set for the bucket, return error
2649 if (versioning_status == VersioningEnabled) {
2650 req_versioning_status = (s->bucket->get_info().flags & BUCKET_VERSIONS_SUSPENDED) != 0;
2651 } else if (versioning_status == VersioningSuspended) {
2652 req_versioning_status = (s->bucket->get_info().flags & BUCKET_VERSIONS_SUSPENDED) == 0;
2653 }
2654 if (req_versioning_status && !s->mfa_verified) {
2655 op_ret = -ERR_MFA_REQUIRED;
2656 return;
2657 }
2658 }
2659
2660 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, in_data, nullptr, s->info, y);
2661 if (op_ret < 0) {
2662 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
2663 return;
2664 }
2665
2666 bool modified = mfa_set_status;
2667
2668 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [&] {
2669 if (mfa_set_status) {
2670 if (mfa_status) {
2671 s->bucket->get_info().flags |= BUCKET_MFA_ENABLED;
2672 } else {
2673 s->bucket->get_info().flags &= ~BUCKET_MFA_ENABLED;
2674 }
2675 }
2676
2677 if (versioning_status == VersioningEnabled) {
2678 s->bucket->get_info().flags |= BUCKET_VERSIONED;
2679 s->bucket->get_info().flags &= ~BUCKET_VERSIONS_SUSPENDED;
2680 modified = true;
2681 } else if (versioning_status == VersioningSuspended) {
2682 s->bucket->get_info().flags |= (BUCKET_VERSIONED | BUCKET_VERSIONS_SUSPENDED);
2683 modified = true;
2684 } else {
2685 return op_ret;
2686 }
2687 s->bucket->set_attrs(rgw::sal::Attrs(s->bucket_attrs));
2688 return s->bucket->put_info(this, false, real_time());
2689 });
2690
2691 if (!modified) {
2692 return;
2693 }
2694
2695 if (op_ret < 0) {
2696 ldpp_dout(this, 0) << "NOTICE: put_bucket_info on bucket=" << s->bucket->get_name()
2697 << " returned err=" << op_ret << dendl;
2698 return;
2699 }
2700 }
2701
2702 int RGWGetBucketWebsite::verify_permission(optional_yield y)
2703 {
2704 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
2705 if (has_s3_resource_tag)
2706 rgw_iam_add_buckettags(this, s);
2707
2708 return verify_bucket_owner_or_policy(s, rgw::IAM::s3GetBucketWebsite);
2709 }
2710
2711 void RGWGetBucketWebsite::pre_exec()
2712 {
2713 rgw_bucket_object_pre_exec(s);
2714 }
2715
2716 void RGWGetBucketWebsite::execute(optional_yield y)
2717 {
2718 if (!s->bucket->get_info().has_website) {
2719 op_ret = -ERR_NO_SUCH_WEBSITE_CONFIGURATION;
2720 }
2721 }
2722
2723 int RGWSetBucketWebsite::verify_permission(optional_yield y)
2724 {
2725 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
2726 if (has_s3_resource_tag)
2727 rgw_iam_add_buckettags(this, s);
2728
2729 return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketWebsite);
2730 }
2731
2732 void RGWSetBucketWebsite::pre_exec()
2733 {
2734 rgw_bucket_object_pre_exec(s);
2735 }
2736
2737 void RGWSetBucketWebsite::execute(optional_yield y)
2738 {
2739 op_ret = get_params(y);
2740
2741 if (op_ret < 0)
2742 return;
2743
2744 if (!s->bucket_exists) {
2745 op_ret = -ERR_NO_SUCH_BUCKET;
2746 return;
2747 }
2748
2749 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, in_data, nullptr, s->info, y);
2750 if (op_ret < 0) {
2751 ldpp_dout(this, 0) << " forward_request_to_master returned ret=" << op_ret << dendl;
2752 return;
2753 }
2754
2755 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
2756 s->bucket->get_info().has_website = true;
2757 s->bucket->get_info().website_conf = website_conf;
2758 op_ret = s->bucket->put_info(this, false, real_time());
2759 return op_ret;
2760 });
2761
2762 if (op_ret < 0) {
2763 ldpp_dout(this, 0) << "NOTICE: put_bucket_info on bucket=" << s->bucket->get_name()
2764 << " returned err=" << op_ret << dendl;
2765 return;
2766 }
2767 }
2768
2769 int RGWDeleteBucketWebsite::verify_permission(optional_yield y)
2770 {
2771 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
2772 if (has_s3_resource_tag)
2773 rgw_iam_add_buckettags(this, s);
2774
2775 return verify_bucket_owner_or_policy(s, rgw::IAM::s3DeleteBucketWebsite);
2776 }
2777
2778 void RGWDeleteBucketWebsite::pre_exec()
2779 {
2780 rgw_bucket_object_pre_exec(s);
2781 }
2782
2783 void RGWDeleteBucketWebsite::execute(optional_yield y)
2784 {
2785 if (!s->bucket_exists) {
2786 op_ret = -ERR_NO_SUCH_BUCKET;
2787 return;
2788 }
2789
2790 bufferlist in_data;
2791
2792 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, in_data, nullptr, s->info, y);
2793 if (op_ret < 0) {
2794 ldpp_dout(this, 0) << "NOTICE: forward_to_master failed on bucket=" << s->bucket->get_name()
2795 << "returned err=" << op_ret << dendl;
2796 return;
2797 }
2798 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
2799 s->bucket->get_info().has_website = false;
2800 s->bucket->get_info().website_conf = RGWBucketWebsiteConf();
2801 op_ret = s->bucket->put_info(this, false, real_time());
2802 return op_ret;
2803 });
2804 if (op_ret < 0) {
2805 ldpp_dout(this, 0) << "NOTICE: put_bucket_info on bucket=" << s->bucket
2806 << " returned err=" << op_ret << dendl;
2807 return;
2808 }
2809 }
2810
2811 int RGWStatBucket::verify_permission(optional_yield y)
2812 {
2813 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
2814 if (has_s3_resource_tag)
2815 rgw_iam_add_buckettags(this, s);
2816
2817 // This (a HEAD request on a bucket) is governed by the s3:ListBucket permission.
2818 if (!verify_bucket_permission(this, s, rgw::IAM::s3ListBucket)) {
2819 return -EACCES;
2820 }
2821
2822 return 0;
2823 }
2824
2825 void RGWStatBucket::pre_exec()
2826 {
2827 rgw_bucket_object_pre_exec(s);
2828 }
2829
2830 void RGWStatBucket::execute(optional_yield y)
2831 {
2832 if (!s->bucket_exists) {
2833 op_ret = -ERR_NO_SUCH_BUCKET;
2834 return;
2835 }
2836
2837 op_ret = store->get_bucket(this, s->user.get(), s->bucket->get_key(), &bucket, y);
2838 if (op_ret) {
2839 return;
2840 }
2841 op_ret = bucket->update_container_stats(s);
2842 }
2843
2844 int RGWListBucket::verify_permission(optional_yield y)
2845 {
2846 op_ret = get_params(y);
2847 if (op_ret < 0) {
2848 return op_ret;
2849 }
2850 if (!prefix.empty())
2851 s->env.emplace("s3:prefix", prefix);
2852
2853 if (!delimiter.empty())
2854 s->env.emplace("s3:delimiter", delimiter);
2855
2856 s->env.emplace("s3:max-keys", std::to_string(max));
2857
2858 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
2859 if (has_s3_resource_tag)
2860 rgw_iam_add_buckettags(this, s);
2861
2862 if (!verify_bucket_permission(this,
2863 s,
2864 list_versions ?
2865 rgw::IAM::s3ListBucketVersions :
2866 rgw::IAM::s3ListBucket)) {
2867 return -EACCES;
2868 }
2869
2870 return 0;
2871 }
2872
2873 int RGWListBucket::parse_max_keys()
2874 {
2875 // Bound max value of max-keys to configured value for security
2876 // Bound min value of max-keys to '0'
2877 // Some S3 clients explicitly send max-keys=0 to detect if the bucket is
2878 // empty without listing any items.
2879 return parse_value_and_bound(max_keys, max, 0,
2880 g_conf().get_val<uint64_t>("rgw_max_listing_results"),
2881 default_max);
2882 }
2883
2884 void RGWListBucket::pre_exec()
2885 {
2886 rgw_bucket_object_pre_exec(s);
2887 }
2888
2889 void RGWListBucket::execute(optional_yield y)
2890 {
2891 if (!s->bucket_exists) {
2892 op_ret = -ERR_NO_SUCH_BUCKET;
2893 return;
2894 }
2895
2896 if (allow_unordered && !delimiter.empty()) {
2897 ldpp_dout(this, 0) <<
2898 "ERROR: unordered bucket listing requested with a delimiter" << dendl;
2899 op_ret = -EINVAL;
2900 return;
2901 }
2902
2903 if (need_container_stats()) {
2904 op_ret = s->bucket->update_container_stats(s);
2905 }
2906
2907 rgw::sal::Bucket::ListParams params;
2908 params.prefix = prefix;
2909 params.delim = delimiter;
2910 params.marker = marker;
2911 params.end_marker = end_marker;
2912 params.list_versions = list_versions;
2913 params.allow_unordered = allow_unordered;
2914 params.shard_id = shard_id;
2915
2916 rgw::sal::Bucket::ListResults results;
2917
2918 op_ret = s->bucket->list(this, params, max, results, y);
2919 if (op_ret >= 0) {
2920 next_marker = results.next_marker;
2921 is_truncated = results.is_truncated;
2922 objs = std::move(results.objs);
2923 common_prefixes = std::move(results.common_prefixes);
2924 }
2925 }
2926
2927 int RGWGetBucketLogging::verify_permission(optional_yield y)
2928 {
2929 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
2930 if (has_s3_resource_tag)
2931 rgw_iam_add_buckettags(this, s);
2932
2933 return verify_bucket_owner_or_policy(s, rgw::IAM::s3GetBucketLogging);
2934 }
2935
2936 int RGWGetBucketLocation::verify_permission(optional_yield y)
2937 {
2938 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
2939 if (has_s3_resource_tag)
2940 rgw_iam_add_buckettags(this, s);
2941
2942 return verify_bucket_owner_or_policy(s, rgw::IAM::s3GetBucketLocation);
2943 }
2944
2945 int RGWCreateBucket::verify_permission(optional_yield y)
2946 {
2947 /* This check is mostly needed for S3 that doesn't support account ACL.
2948 * Swift doesn't allow to delegate any permission to an anonymous user,
2949 * so it will become an early exit in such case. */
2950 if (s->auth.identity->is_anonymous()) {
2951 return -EACCES;
2952 }
2953
2954 rgw_bucket bucket;
2955 bucket.name = s->bucket_name;
2956 bucket.tenant = s->bucket_tenant;
2957 ARN arn = ARN(bucket);
2958 if (!verify_user_permission(this, s, arn, rgw::IAM::s3CreateBucket)) {
2959 return -EACCES;
2960 }
2961
2962 if (s->user->get_tenant() != s->bucket_tenant) {
2963 //AssumeRole is meant for cross account access
2964 if (s->auth.identity->get_identity_type() != TYPE_ROLE) {
2965 ldpp_dout(this, 10) << "user cannot create a bucket in a different tenant"
2966 << " (user_id.tenant=" << s->user->get_tenant()
2967 << " requested=" << s->bucket_tenant << ")"
2968 << dendl;
2969 return -EACCES;
2970 }
2971 }
2972
2973 if (s->user->get_max_buckets() < 0) {
2974 return -EPERM;
2975 }
2976
2977 if (s->user->get_max_buckets()) {
2978 rgw::sal::BucketList buckets;
2979 string marker;
2980 op_ret = s->user->list_buckets(this, marker, string(), s->user->get_max_buckets(),
2981 false, buckets, y);
2982 if (op_ret < 0) {
2983 return op_ret;
2984 }
2985
2986 if ((int)buckets.count() >= s->user->get_max_buckets()) {
2987 return -ERR_TOO_MANY_BUCKETS;
2988 }
2989 }
2990
2991 return 0;
2992 }
2993
2994 void RGWCreateBucket::pre_exec()
2995 {
2996 rgw_bucket_object_pre_exec(s);
2997 }
2998
2999 static void prepare_add_del_attrs(const map<string, bufferlist>& orig_attrs,
3000 map<string, bufferlist>& out_attrs,
3001 map<string, bufferlist>& out_rmattrs)
3002 {
3003 for (const auto& kv : orig_attrs) {
3004 const string& name = kv.first;
3005
3006 /* Check if the attr is user-defined metadata item. */
3007 if (name.compare(0, sizeof(RGW_ATTR_META_PREFIX) - 1,
3008 RGW_ATTR_META_PREFIX) == 0) {
3009 /* For the objects all existing meta attrs have to be removed. */
3010 out_rmattrs[name] = kv.second;
3011 } else if (out_attrs.find(name) == std::end(out_attrs)) {
3012 out_attrs[name] = kv.second;
3013 }
3014 }
3015 }
3016
3017 /* Fuse resource metadata basing on original attributes in @orig_attrs, set
3018 * of _custom_ attribute names to remove in @rmattr_names and attributes in
3019 * @out_attrs. Place results in @out_attrs.
3020 *
3021 * NOTE: it's supposed that all special attrs already present in @out_attrs
3022 * will be preserved without any change. Special attributes are those which
3023 * names start with RGW_ATTR_META_PREFIX. They're complement to custom ones
3024 * used for X-Account-Meta-*, X-Container-Meta-*, X-Amz-Meta and so on. */
3025 static void prepare_add_del_attrs(const map<string, bufferlist>& orig_attrs,
3026 const set<string>& rmattr_names,
3027 map<string, bufferlist>& out_attrs)
3028 {
3029 for (const auto& kv : orig_attrs) {
3030 const string& name = kv.first;
3031
3032 /* Check if the attr is user-defined metadata item. */
3033 if (name.compare(0, strlen(RGW_ATTR_META_PREFIX),
3034 RGW_ATTR_META_PREFIX) == 0) {
3035 /* For the buckets all existing meta attrs are preserved,
3036 except those that are listed in rmattr_names. */
3037 if (rmattr_names.find(name) != std::end(rmattr_names)) {
3038 const auto aiter = out_attrs.find(name);
3039
3040 if (aiter != std::end(out_attrs)) {
3041 out_attrs.erase(aiter);
3042 }
3043 } else {
3044 /* emplace() won't alter the map if the key is already present.
3045 * This behaviour is fully intensional here. */
3046 out_attrs.emplace(kv);
3047 }
3048 } else if (out_attrs.find(name) == std::end(out_attrs)) {
3049 out_attrs[name] = kv.second;
3050 }
3051 }
3052 }
3053
3054
3055 static void populate_with_generic_attrs(const req_state * const s,
3056 map<string, bufferlist>& out_attrs)
3057 {
3058 for (const auto& kv : s->generic_attrs) {
3059 bufferlist& attrbl = out_attrs[kv.first];
3060 const string& val = kv.second;
3061 attrbl.clear();
3062 attrbl.append(val.c_str(), val.size() + 1);
3063 }
3064 }
3065
3066
3067 static int filter_out_quota_info(std::map<std::string, bufferlist>& add_attrs,
3068 const std::set<std::string>& rmattr_names,
3069 RGWQuotaInfo& quota,
3070 bool * quota_extracted = nullptr)
3071 {
3072 bool extracted = false;
3073
3074 /* Put new limit on max objects. */
3075 auto iter = add_attrs.find(RGW_ATTR_QUOTA_NOBJS);
3076 std::string err;
3077 if (std::end(add_attrs) != iter) {
3078 quota.max_objects =
3079 static_cast<int64_t>(strict_strtoll(iter->second.c_str(), 10, &err));
3080 if (!err.empty()) {
3081 return -EINVAL;
3082 }
3083 add_attrs.erase(iter);
3084 extracted = true;
3085 }
3086
3087 /* Put new limit on bucket (container) size. */
3088 iter = add_attrs.find(RGW_ATTR_QUOTA_MSIZE);
3089 if (iter != add_attrs.end()) {
3090 quota.max_size =
3091 static_cast<int64_t>(strict_strtoll(iter->second.c_str(), 10, &err));
3092 if (!err.empty()) {
3093 return -EINVAL;
3094 }
3095 add_attrs.erase(iter);
3096 extracted = true;
3097 }
3098
3099 for (const auto& name : rmattr_names) {
3100 /* Remove limit on max objects. */
3101 if (name.compare(RGW_ATTR_QUOTA_NOBJS) == 0) {
3102 quota.max_objects = -1;
3103 extracted = true;
3104 }
3105
3106 /* Remove limit on max bucket size. */
3107 if (name.compare(RGW_ATTR_QUOTA_MSIZE) == 0) {
3108 quota.max_size = -1;
3109 extracted = true;
3110 }
3111 }
3112
3113 /* Swift requries checking on raw usage instead of the 4 KiB rounded one. */
3114 quota.check_on_raw = true;
3115 quota.enabled = quota.max_size > 0 || quota.max_objects > 0;
3116
3117 if (quota_extracted) {
3118 *quota_extracted = extracted;
3119 }
3120
3121 return 0;
3122 }
3123
3124
3125 static void filter_out_website(std::map<std::string, ceph::bufferlist>& add_attrs,
3126 const std::set<std::string>& rmattr_names,
3127 RGWBucketWebsiteConf& ws_conf)
3128 {
3129 std::string lstval;
3130
3131 /* Let's define a mapping between each custom attribute and the memory where
3132 * attribute's value should be stored. The memory location is expressed by
3133 * a non-const reference. */
3134 const auto mapping = {
3135 std::make_pair(RGW_ATTR_WEB_INDEX, std::ref(ws_conf.index_doc_suffix)),
3136 std::make_pair(RGW_ATTR_WEB_ERROR, std::ref(ws_conf.error_doc)),
3137 std::make_pair(RGW_ATTR_WEB_LISTINGS, std::ref(lstval)),
3138 std::make_pair(RGW_ATTR_WEB_LIST_CSS, std::ref(ws_conf.listing_css_doc)),
3139 std::make_pair(RGW_ATTR_SUBDIR_MARKER, std::ref(ws_conf.subdir_marker))
3140 };
3141
3142 for (const auto& kv : mapping) {
3143 const char * const key = kv.first;
3144 auto& target = kv.second;
3145
3146 auto iter = add_attrs.find(key);
3147
3148 if (std::end(add_attrs) != iter) {
3149 /* The "target" is a reference to ws_conf. */
3150 target = iter->second.c_str();
3151 add_attrs.erase(iter);
3152 }
3153
3154 if (rmattr_names.count(key)) {
3155 target = std::string();
3156 }
3157 }
3158
3159 if (! lstval.empty()) {
3160 ws_conf.listing_enabled = boost::algorithm::iequals(lstval, "true");
3161 }
3162 }
3163
3164
3165 void RGWCreateBucket::execute(optional_yield y)
3166 {
3167 buffer::list aclbl;
3168 buffer::list corsbl;
3169 string bucket_name = rgw_make_bucket_entry_name(s->bucket_tenant, s->bucket_name);
3170 rgw_raw_obj obj(store->get_zone()->get_params().domain_root, bucket_name);
3171
3172 op_ret = get_params(y);
3173 if (op_ret < 0)
3174 return;
3175
3176 if (!relaxed_region_enforcement &&
3177 !location_constraint.empty() &&
3178 !store->get_zone()->has_zonegroup_api(location_constraint)) {
3179 ldpp_dout(this, 0) << "location constraint (" << location_constraint << ")"
3180 << " can't be found." << dendl;
3181 op_ret = -ERR_INVALID_LOCATION_CONSTRAINT;
3182 s->err.message = "The specified location-constraint is not valid";
3183 return;
3184 }
3185
3186 if (!relaxed_region_enforcement && !store->get_zone()->get_zonegroup().is_master_zonegroup() && !location_constraint.empty() &&
3187 store->get_zone()->get_zonegroup().api_name != location_constraint) {
3188 ldpp_dout(this, 0) << "location constraint (" << location_constraint << ")"
3189 << " doesn't match zonegroup" << " (" << store->get_zone()->get_zonegroup().api_name << ")"
3190 << dendl;
3191 op_ret = -ERR_INVALID_LOCATION_CONSTRAINT;
3192 s->err.message = "The specified location-constraint is not valid";
3193 return;
3194 }
3195
3196 const auto& zonegroup = store->get_zone()->get_zonegroup();
3197 if (!placement_rule.name.empty() &&
3198 !zonegroup.placement_targets.count(placement_rule.name)) {
3199 ldpp_dout(this, 0) << "placement target (" << placement_rule.name << ")"
3200 << " doesn't exist in the placement targets of zonegroup"
3201 << " (" << store->get_zone()->get_zonegroup().api_name << ")" << dendl;
3202 op_ret = -ERR_INVALID_LOCATION_CONSTRAINT;
3203 s->err.message = "The specified placement target does not exist";
3204 return;
3205 }
3206
3207 /* we need to make sure we read bucket info, it's not read before for this
3208 * specific request */
3209 {
3210 std::unique_ptr<rgw::sal::Bucket> tmp_bucket;
3211 op_ret = store->get_bucket(this, s->user.get(), s->bucket_tenant,
3212 s->bucket_name, &tmp_bucket, y);
3213 if (op_ret < 0 && op_ret != -ENOENT)
3214 return;
3215 s->bucket_exists = (op_ret != -ENOENT);
3216
3217 if (s->bucket_exists) {
3218 if (!s->system_request &&
3219 store->get_zone()->get_zonegroup().get_id() !=
3220 tmp_bucket->get_info().zonegroup) {
3221 op_ret = -EEXIST;
3222 return;
3223 }
3224 /* Initialize info from req_state */
3225 info = tmp_bucket->get_info();
3226 }
3227 }
3228
3229 s->bucket_owner.set_id(s->user->get_id()); /* XXX dang use s->bucket->owner */
3230 s->bucket_owner.set_name(s->user->get_display_name());
3231
3232 string zonegroup_id;
3233
3234 if (s->system_request) {
3235 zonegroup_id = s->info.args.get(RGW_SYS_PARAM_PREFIX "zonegroup");
3236 if (zonegroup_id.empty()) {
3237 zonegroup_id = store->get_zone()->get_zonegroup().get_id();
3238 }
3239 } else {
3240 zonegroup_id = store->get_zone()->get_zonegroup().get_id();
3241 }
3242
3243 /* Encode special metadata first as we're using std::map::emplace under
3244 * the hood. This method will add the new items only if the map doesn't
3245 * contain such keys yet. */
3246 policy.encode(aclbl);
3247 emplace_attr(RGW_ATTR_ACL, std::move(aclbl));
3248
3249 if (has_cors) {
3250 cors_config.encode(corsbl);
3251 emplace_attr(RGW_ATTR_CORS, std::move(corsbl));
3252 }
3253
3254 RGWQuotaInfo quota_info;
3255 const RGWQuotaInfo * pquota_info = nullptr;
3256 if (need_metadata_upload()) {
3257 /* It's supposed that following functions WILL NOT change any special
3258 * attributes (like RGW_ATTR_ACL) if they are already present in attrs. */
3259 op_ret = rgw_get_request_metadata(this, s->cct, s->info, attrs, false);
3260 if (op_ret < 0) {
3261 return;
3262 }
3263 prepare_add_del_attrs(s->bucket_attrs, rmattr_names, attrs);
3264 populate_with_generic_attrs(s, attrs);
3265
3266 op_ret = filter_out_quota_info(attrs, rmattr_names, quota_info);
3267 if (op_ret < 0) {
3268 return;
3269 } else {
3270 pquota_info = &quota_info;
3271 }
3272
3273 /* Web site of Swift API. */
3274 filter_out_website(attrs, rmattr_names, info.website_conf);
3275 info.has_website = !info.website_conf.is_empty();
3276 }
3277
3278 rgw_bucket tmp_bucket;
3279 tmp_bucket.tenant = s->bucket_tenant; /* ignored if bucket exists */
3280 tmp_bucket.name = s->bucket_name;
3281
3282 /* Handle updates of the metadata for Swift's object versioning. */
3283 if (swift_ver_location) {
3284 info.swift_ver_location = *swift_ver_location;
3285 info.swift_versioning = (! swift_ver_location->empty());
3286 }
3287
3288 /* We're replacing bucket with the newly created one */
3289 ldpp_dout(this, 10) << "user=" << s->user << " bucket=" << tmp_bucket << dendl;
3290 op_ret = s->user->create_bucket(this, tmp_bucket, zonegroup_id,
3291 placement_rule,
3292 info.swift_ver_location,
3293 pquota_info, policy, attrs, info, ep_objv,
3294 true, obj_lock_enabled, &s->bucket_exists, s->info,
3295 &s->bucket, y);
3296
3297 /* continue if EEXIST and create_bucket will fail below. this way we can
3298 * recover from a partial create by retrying it. */
3299 ldpp_dout(this, 20) << "rgw_create_bucket returned ret=" << op_ret << " bucket=" << s->bucket.get() << dendl;
3300
3301 if (op_ret)
3302 return;
3303
3304 const bool existed = s->bucket_exists;
3305 if (need_metadata_upload() && existed) {
3306 /* OK, it looks we lost race with another request. As it's required to
3307 * handle metadata fusion and upload, the whole operation becomes very
3308 * similar in nature to PutMetadataBucket. However, as the attrs may
3309 * changed in the meantime, we have to refresh. */
3310 short tries = 0;
3311 do {
3312 map<string, bufferlist> battrs;
3313
3314 op_ret = s->bucket->load_bucket(this, y);
3315 if (op_ret < 0) {
3316 return;
3317 } else if (!s->bucket->is_owner(s->user.get())) {
3318 /* New bucket doesn't belong to the account we're operating on. */
3319 op_ret = -EEXIST;
3320 return;
3321 } else {
3322 s->bucket_attrs = s->bucket->get_attrs();
3323 }
3324
3325 attrs.clear();
3326
3327 op_ret = rgw_get_request_metadata(this, s->cct, s->info, attrs, false);
3328 if (op_ret < 0) {
3329 return;
3330 }
3331 prepare_add_del_attrs(s->bucket_attrs, rmattr_names, attrs);
3332 populate_with_generic_attrs(s, attrs);
3333 op_ret = filter_out_quota_info(attrs, rmattr_names, s->bucket->get_info().quota);
3334 if (op_ret < 0) {
3335 return;
3336 }
3337
3338 /* Handle updates of the metadata for Swift's object versioning. */
3339 if (swift_ver_location) {
3340 s->bucket->get_info().swift_ver_location = *swift_ver_location;
3341 s->bucket->get_info().swift_versioning = (! swift_ver_location->empty());
3342 }
3343
3344 /* Web site of Swift API. */
3345 filter_out_website(attrs, rmattr_names, s->bucket->get_info().website_conf);
3346 s->bucket->get_info().has_website = !s->bucket->get_info().website_conf.is_empty();
3347
3348 /* This will also set the quota on the bucket. */
3349 op_ret = s->bucket->merge_and_store_attrs(this, attrs, y);
3350 } while (op_ret == -ECANCELED && tries++ < 20);
3351
3352 /* Restore the proper return code. */
3353 if (op_ret >= 0) {
3354 op_ret = -ERR_BUCKET_EXISTS;
3355 }
3356 }
3357 }
3358
3359 int RGWDeleteBucket::verify_permission(optional_yield y)
3360 {
3361 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
3362 if (has_s3_resource_tag)
3363 rgw_iam_add_buckettags(this, s);
3364
3365 if (!verify_bucket_permission(this, s, rgw::IAM::s3DeleteBucket)) {
3366 return -EACCES;
3367 }
3368
3369 return 0;
3370 }
3371
3372 void RGWDeleteBucket::pre_exec()
3373 {
3374 rgw_bucket_object_pre_exec(s);
3375 }
3376
3377 void RGWDeleteBucket::execute(optional_yield y)
3378 {
3379 if (s->bucket_name.empty()) {
3380 op_ret = -EINVAL;
3381 return;
3382 }
3383
3384 if (!s->bucket_exists) {
3385 ldpp_dout(this, 0) << "ERROR: bucket " << s->bucket_name << " not found" << dendl;
3386 op_ret = -ERR_NO_SUCH_BUCKET;
3387 return;
3388 }
3389 RGWObjVersionTracker ot;
3390 ot.read_version = s->bucket->get_version();
3391
3392 if (s->system_request) {
3393 string tag = s->info.args.get(RGW_SYS_PARAM_PREFIX "tag");
3394 string ver_str = s->info.args.get(RGW_SYS_PARAM_PREFIX "ver");
3395 if (!tag.empty()) {
3396 ot.read_version.tag = tag;
3397 uint64_t ver;
3398 string err;
3399 ver = strict_strtol(ver_str.c_str(), 10, &err);
3400 if (!err.empty()) {
3401 ldpp_dout(this, 0) << "failed to parse ver param" << dendl;
3402 op_ret = -EINVAL;
3403 return;
3404 }
3405 ot.read_version.ver = ver;
3406 }
3407 }
3408
3409 op_ret = s->bucket->sync_user_stats(this, y);
3410 if ( op_ret < 0) {
3411 ldpp_dout(this, 1) << "WARNING: failed to sync user stats before bucket delete: op_ret= " << op_ret << dendl;
3412 }
3413
3414 op_ret = s->bucket->check_empty(this, y);
3415 if (op_ret < 0) {
3416 return;
3417 }
3418
3419 bufferlist in_data;
3420 op_ret = store->forward_request_to_master(this, s->user.get(), &ot.read_version, in_data, nullptr, s->info, y);
3421 if (op_ret < 0) {
3422 if (op_ret == -ENOENT) {
3423 /* adjust error, we want to return with NoSuchBucket and not
3424 * NoSuchKey */
3425 op_ret = -ERR_NO_SUCH_BUCKET;
3426 }
3427 return;
3428 }
3429
3430 op_ret = rgw_remove_sse_s3_bucket_key(s);
3431 if (op_ret != 0) {
3432 // do nothing; it will already have been logged
3433 }
3434
3435 op_ret = s->bucket->remove_bucket(this, false, false, nullptr, y);
3436 if (op_ret < 0 && op_ret == -ECANCELED) {
3437 // lost a race, either with mdlog sync or another delete bucket operation.
3438 // in either case, we've already called ctl.bucket->unlink_bucket()
3439 op_ret = 0;
3440 }
3441
3442 return;
3443 }
3444
3445 int RGWPutObj::init_processing(optional_yield y) {
3446 copy_source = url_decode(s->info.env->get("HTTP_X_AMZ_COPY_SOURCE", ""));
3447 copy_source_range = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE_RANGE");
3448 size_t pos;
3449 int ret;
3450
3451 /* handle x-amz-copy-source */
3452 std::string_view cs_view(copy_source);
3453 if (! cs_view.empty()) {
3454 if (cs_view[0] == '/')
3455 cs_view.remove_prefix(1);
3456 copy_source_bucket_name = std::string(cs_view);
3457 pos = copy_source_bucket_name.find("/");
3458 if (pos == std::string::npos) {
3459 ret = -EINVAL;
3460 ldpp_dout(this, 5) << "x-amz-copy-source bad format" << dendl;
3461 return ret;
3462 }
3463 copy_source_object_name =
3464 copy_source_bucket_name.substr(pos + 1, copy_source_bucket_name.size());
3465 copy_source_bucket_name = copy_source_bucket_name.substr(0, pos);
3466 #define VERSION_ID_STR "?versionId="
3467 pos = copy_source_object_name.find(VERSION_ID_STR);
3468 if (pos == std::string::npos) {
3469 copy_source_object_name = url_decode(copy_source_object_name);
3470 } else {
3471 copy_source_version_id =
3472 copy_source_object_name.substr(pos + sizeof(VERSION_ID_STR) - 1);
3473 copy_source_object_name =
3474 url_decode(copy_source_object_name.substr(0, pos));
3475 }
3476 pos = copy_source_bucket_name.find(":");
3477 if (pos == std::string::npos) {
3478 // if tenant is not specified in x-amz-copy-source, use tenant of the requester
3479 copy_source_tenant_name = s->user->get_tenant();
3480 } else {
3481 copy_source_tenant_name = copy_source_bucket_name.substr(0, pos);
3482 copy_source_bucket_name = copy_source_bucket_name.substr(pos + 1, copy_source_bucket_name.size());
3483 if (copy_source_bucket_name.empty()) {
3484 ret = -EINVAL;
3485 ldpp_dout(this, 5) << "source bucket name is empty" << dendl;
3486 return ret;
3487 }
3488 }
3489 std::unique_ptr<rgw::sal::Bucket> bucket;
3490 ret = store->get_bucket(this, s->user.get(), copy_source_tenant_name, copy_source_bucket_name,
3491 &bucket, y);
3492 if (ret < 0) {
3493 ldpp_dout(this, 5) << __func__ << "(): get_bucket() returned ret=" << ret << dendl;
3494 if (ret == -ENOENT) {
3495 ret = -ERR_NO_SUCH_BUCKET;
3496 }
3497 return ret;
3498 }
3499
3500 ret = bucket->load_bucket(this, y);
3501 if (ret < 0) {
3502 ldpp_dout(this, 5) << __func__ << "(): load_bucket() returned ret=" << ret << dendl;
3503 return ret;
3504 }
3505 copy_source_bucket_info = bucket->get_info();
3506
3507 /* handle x-amz-copy-source-range */
3508 if (copy_source_range) {
3509 string range = copy_source_range;
3510 pos = range.find("bytes=");
3511 if (pos == std::string::npos || pos != 0) {
3512 ret = -EINVAL;
3513 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format" << dendl;
3514 return ret;
3515 }
3516 /* 6 is the length of "bytes=" */
3517 range = range.substr(pos + 6);
3518 pos = range.find("-");
3519 if (pos == std::string::npos) {
3520 ret = -EINVAL;
3521 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format" << dendl;
3522 return ret;
3523 }
3524 string first = range.substr(0, pos);
3525 string last = range.substr(pos + 1);
3526 if (first.find_first_not_of("0123456789") != std::string::npos ||
3527 last.find_first_not_of("0123456789") != std::string::npos) {
3528 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format not an integer" << dendl;
3529 ret = -EINVAL;
3530 return ret;
3531 }
3532 copy_source_range_fst = strtoull(first.c_str(), NULL, 10);
3533 copy_source_range_lst = strtoull(last.c_str(), NULL, 10);
3534 if (copy_source_range_fst > copy_source_range_lst) {
3535 ret = -ERANGE;
3536 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format first number bigger than second" << dendl;
3537 return ret;
3538 }
3539 }
3540
3541 } /* copy_source */
3542 return RGWOp::init_processing(y);
3543 }
3544
3545 int RGWPutObj::verify_permission(optional_yield y)
3546 {
3547 if (! copy_source.empty()) {
3548
3549 RGWAccessControlPolicy cs_acl(s->cct);
3550 boost::optional<Policy> policy;
3551 map<string, bufferlist> cs_attrs;
3552 std::unique_ptr<rgw::sal::Bucket> cs_bucket;
3553 int ret = store->get_bucket(NULL, copy_source_bucket_info, &cs_bucket);
3554 if (ret < 0)
3555 return ret;
3556
3557 std::unique_ptr<rgw::sal::Object> cs_object =
3558 cs_bucket->get_object(rgw_obj_key(copy_source_object_name, copy_source_version_id));
3559
3560 cs_object->set_atomic(s->obj_ctx);
3561 cs_object->set_prefetch_data(s->obj_ctx);
3562
3563 /* check source object permissions */
3564 if (ret = read_obj_policy(this, store, s, copy_source_bucket_info, cs_attrs, &cs_acl, nullptr,
3565 policy, cs_bucket.get(), cs_object.get(), y, true); ret < 0) {
3566 return ret;
3567 }
3568
3569 /* admin request overrides permission checks */
3570 if (! s->auth.identity->is_admin_of(cs_acl.get_owner().get_id())) {
3571 if (policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
3572 //add source object tags for permission evaluation
3573 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, policy, s->iam_user_policies, s->session_policies);
3574 if (has_s3_existing_tag || has_s3_resource_tag)
3575 rgw_iam_add_objtags(this, s, cs_object.get(), has_s3_existing_tag, has_s3_resource_tag);
3576 auto usr_policy_res = Effect::Pass;
3577 rgw::ARN obj_arn(cs_object->get_obj());
3578 for (auto& user_policy : s->iam_user_policies) {
3579 if (usr_policy_res = user_policy.eval(s->env, *s->auth.identity,
3580 cs_object->get_instance().empty() ?
3581 rgw::IAM::s3GetObject :
3582 rgw::IAM::s3GetObjectVersion,
3583 obj_arn); usr_policy_res == Effect::Deny)
3584 return -EACCES;
3585 else if (usr_policy_res == Effect::Allow)
3586 break;
3587 }
3588 rgw::IAM::Effect e = Effect::Pass;
3589 if (policy) {
3590 rgw::ARN obj_arn(cs_object->get_obj());
3591 e = policy->eval(s->env, *s->auth.identity,
3592 cs_object->get_instance().empty() ?
3593 rgw::IAM::s3GetObject :
3594 rgw::IAM::s3GetObjectVersion,
3595 obj_arn);
3596 }
3597 if (e == Effect::Deny) {
3598 return -EACCES;
3599 } else if (usr_policy_res == Effect::Pass && e == Effect::Pass &&
3600 !cs_acl.verify_permission(this, *s->auth.identity, s->perm_mask,
3601 RGW_PERM_READ)) {
3602 return -EACCES;
3603 }
3604 rgw_iam_remove_objtags(this, s, cs_object.get(), has_s3_existing_tag, has_s3_resource_tag);
3605 } else if (!cs_acl.verify_permission(this, *s->auth.identity, s->perm_mask,
3606 RGW_PERM_READ)) {
3607 return -EACCES;
3608 }
3609 }
3610 }
3611
3612 if (s->bucket_access_conf && s->bucket_access_conf->block_public_acls()) {
3613 if (s->canned_acl.compare("public-read") ||
3614 s->canned_acl.compare("public-read-write") ||
3615 s->canned_acl.compare("authenticated-read"))
3616 return -EACCES;
3617 }
3618
3619 auto op_ret = get_params(y);
3620 if (op_ret < 0) {
3621 ldpp_dout(this, 20) << "get_params() returned ret=" << op_ret << dendl;
3622 return op_ret;
3623 }
3624
3625 if (s->iam_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
3626 rgw_add_grant_to_iam_environment(s->env, s);
3627
3628 rgw_add_to_iam_environment(s->env, "s3:x-amz-acl", s->canned_acl);
3629
3630 if (obj_tags != nullptr && obj_tags->count() > 0){
3631 auto tags = obj_tags->get_tags();
3632 for (const auto& kv: tags){
3633 rgw_add_to_iam_environment(s->env, "s3:RequestObjectTag/"+kv.first, kv.second);
3634 }
3635 }
3636
3637 constexpr auto encrypt_attr = "x-amz-server-side-encryption";
3638 constexpr auto s3_encrypt_attr = "s3:x-amz-server-side-encryption";
3639 auto enc_header = s->info.crypt_attribute_map.find(encrypt_attr);
3640 if (enc_header != s->info.crypt_attribute_map.end()){
3641 rgw_add_to_iam_environment(s->env, s3_encrypt_attr, enc_header->second);
3642 }
3643
3644 constexpr auto kms_attr = "x-amz-server-side-encryption-aws-kms-key-id";
3645 constexpr auto s3_kms_attr = "s3:x-amz-server-side-encryption-aws-kms-key-id";
3646 auto kms_header = s->info.crypt_attribute_map.find(kms_attr);
3647 if (kms_header != s->info.crypt_attribute_map.end()){
3648 rgw_add_to_iam_environment(s->env, s3_kms_attr, kms_header->second);
3649 }
3650
3651 // Add bucket tags for authorization
3652 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
3653 if (has_s3_resource_tag)
3654 rgw_iam_add_buckettags(this, s);
3655
3656 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
3657 rgw::IAM::s3PutObject,
3658 s->object->get_obj());
3659 if (identity_policy_res == Effect::Deny)
3660 return -EACCES;
3661
3662 rgw::IAM::Effect e = Effect::Pass;
3663 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
3664 if (s->iam_policy) {
3665 ARN obj_arn(s->object->get_obj());
3666 e = s->iam_policy->eval(s->env, *s->auth.identity,
3667 rgw::IAM::s3PutObject,
3668 obj_arn,
3669 princ_type);
3670 }
3671 if (e == Effect::Deny) {
3672 return -EACCES;
3673 }
3674
3675 if (!s->session_policies.empty()) {
3676 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
3677 rgw::IAM::s3PutObject,
3678 s->object->get_obj());
3679 if (session_policy_res == Effect::Deny) {
3680 return -EACCES;
3681 }
3682 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
3683 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
3684 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
3685 (session_policy_res == Effect::Allow && e == Effect::Allow))
3686 return 0;
3687 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
3688 //Intersection of session policy and identity policy plus bucket policy
3689 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow)
3690 return 0;
3691 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
3692 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow)
3693 return 0;
3694 }
3695 return -EACCES;
3696 }
3697 if (e == Effect::Allow || identity_policy_res == Effect::Allow) {
3698 return 0;
3699 }
3700 }
3701
3702 if (!verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
3703 return -EACCES;
3704 }
3705
3706 return 0;
3707 }
3708
3709
3710 void RGWPutObj::pre_exec()
3711 {
3712 rgw_bucket_object_pre_exec(s);
3713 }
3714
3715 class RGWPutObj_CB : public RGWGetObj_Filter
3716 {
3717 RGWPutObj *op;
3718 public:
3719 explicit RGWPutObj_CB(RGWPutObj *_op) : op(_op) {}
3720 ~RGWPutObj_CB() override {}
3721
3722 int handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) override {
3723 return op->get_data_cb(bl, bl_ofs, bl_len);
3724 }
3725 };
3726
3727 int RGWPutObj::get_data_cb(bufferlist& bl, off_t bl_ofs, off_t bl_len)
3728 {
3729 bufferlist bl_tmp;
3730 bl.begin(bl_ofs).copy(bl_len, bl_tmp);
3731
3732 bl_aux.append(bl_tmp);
3733
3734 return bl_len;
3735 }
3736
3737 int RGWPutObj::get_data(const off_t fst, const off_t lst, bufferlist& bl)
3738 {
3739 RGWPutObj_CB cb(this);
3740 RGWGetObj_Filter* filter = &cb;
3741 boost::optional<RGWGetObj_Decompress> decompress;
3742 std::unique_ptr<RGWGetObj_Filter> decrypt;
3743 RGWCompressionInfo cs_info;
3744 map<string, bufferlist> attrs;
3745 int ret = 0;
3746
3747 uint64_t obj_size;
3748 int64_t new_ofs, new_end;
3749
3750 new_ofs = fst;
3751 new_end = lst;
3752
3753 std::unique_ptr<rgw::sal::Bucket> bucket;
3754 ret = store->get_bucket(nullptr, copy_source_bucket_info, &bucket);
3755 if (ret < 0)
3756 return ret;
3757
3758 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(rgw_obj_key(copy_source_object_name, copy_source_version_id));
3759 std::unique_ptr<rgw::sal::Object::ReadOp> read_op(obj->get_read_op(s->obj_ctx));
3760
3761 ret = read_op->prepare(s->yield, this);
3762 if (ret < 0)
3763 return ret;
3764
3765 obj_size = obj->get_obj_size();
3766
3767 bool need_decompress;
3768 op_ret = rgw_compression_info_from_attrset(obj->get_attrs(), need_decompress, cs_info);
3769 if (op_ret < 0) {
3770 ldpp_dout(this, 0) << "ERROR: failed to decode compression info" << dendl;
3771 return -EIO;
3772 }
3773
3774 bool partial_content = true;
3775 if (need_decompress)
3776 {
3777 obj_size = cs_info.orig_size;
3778 decompress.emplace(s->cct, &cs_info, partial_content, filter);
3779 filter = &*decompress;
3780 }
3781
3782 auto attr_iter = obj->get_attrs().find(RGW_ATTR_MANIFEST);
3783 op_ret = this->get_decrypt_filter(&decrypt,
3784 filter,
3785 obj->get_attrs(),
3786 attr_iter != obj->get_attrs().end() ? &(attr_iter->second) : nullptr);
3787 if (decrypt != nullptr) {
3788 filter = decrypt.get();
3789 }
3790 if (op_ret < 0) {
3791 return op_ret;
3792 }
3793
3794 ret = obj->range_to_ofs(obj_size, new_ofs, new_end);
3795 if (ret < 0)
3796 return ret;
3797
3798 filter->fixup_range(new_ofs, new_end);
3799 ret = read_op->iterate(this, new_ofs, new_end, filter, s->yield);
3800
3801 if (ret >= 0)
3802 ret = filter->flush();
3803
3804 bl.claim_append(bl_aux);
3805
3806 return ret;
3807 }
3808
3809 // special handling for compression type = "random" with multipart uploads
3810 static CompressorRef get_compressor_plugin(const req_state *s,
3811 const std::string& compression_type)
3812 {
3813 if (compression_type != "random") {
3814 return Compressor::create(s->cct, compression_type);
3815 }
3816
3817 bool is_multipart{false};
3818 const auto& upload_id = s->info.args.get("uploadId", &is_multipart);
3819
3820 if (!is_multipart) {
3821 return Compressor::create(s->cct, compression_type);
3822 }
3823
3824 // use a hash of the multipart upload id so all parts use the same plugin
3825 const auto alg = std::hash<std::string>{}(upload_id) % Compressor::COMP_ALG_LAST;
3826 if (alg == Compressor::COMP_ALG_NONE) {
3827 return nullptr;
3828 }
3829 return Compressor::create(s->cct, alg);
3830 }
3831
3832 void RGWPutObj::execute(optional_yield y)
3833 {
3834 char supplied_md5_bin[CEPH_CRYPTO_MD5_DIGESTSIZE + 1];
3835 char supplied_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
3836 char calc_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
3837 unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE];
3838 MD5 hash;
3839 // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
3840 hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
3841 bufferlist bl, aclbl, bs;
3842 int len;
3843
3844 off_t fst;
3845 off_t lst;
3846
3847 bool need_calc_md5 = (dlo_manifest == NULL) && (slo_info == NULL);
3848 perfcounter->inc(l_rgw_put);
3849 // report latency on return
3850 auto put_lat = make_scope_guard([&] {
3851 perfcounter->tinc(l_rgw_put_lat, s->time_elapsed());
3852 });
3853
3854 op_ret = -EINVAL;
3855 if (rgw::sal::Object::empty(s->object.get())) {
3856 return;
3857 }
3858
3859 if (!s->bucket_exists) {
3860 op_ret = -ERR_NO_SUCH_BUCKET;
3861 return;
3862 }
3863
3864 op_ret = get_system_versioning_params(s, &olh_epoch, &version_id);
3865 if (op_ret < 0) {
3866 ldpp_dout(this, 20) << "get_system_versioning_params() returned ret="
3867 << op_ret << dendl;
3868 return;
3869 }
3870
3871 if (supplied_md5_b64) {
3872 need_calc_md5 = true;
3873
3874 ldpp_dout(this, 15) << "supplied_md5_b64=" << supplied_md5_b64 << dendl;
3875 op_ret = ceph_unarmor(supplied_md5_bin, &supplied_md5_bin[CEPH_CRYPTO_MD5_DIGESTSIZE + 1],
3876 supplied_md5_b64, supplied_md5_b64 + strlen(supplied_md5_b64));
3877 ldpp_dout(this, 15) << "ceph_armor ret=" << op_ret << dendl;
3878 if (op_ret != CEPH_CRYPTO_MD5_DIGESTSIZE) {
3879 op_ret = -ERR_INVALID_DIGEST;
3880 return;
3881 }
3882
3883 buf_to_hex((const unsigned char *)supplied_md5_bin, CEPH_CRYPTO_MD5_DIGESTSIZE, supplied_md5);
3884 ldpp_dout(this, 15) << "supplied_md5=" << supplied_md5 << dendl;
3885 }
3886
3887 if (!chunked_upload) { /* with chunked upload we don't know how big is the upload.
3888 we also check sizes at the end anyway */
3889 op_ret = s->bucket->check_quota(this, user_quota, bucket_quota, s->content_length, y);
3890 if (op_ret < 0) {
3891 ldpp_dout(this, 20) << "check_quota() returned ret=" << op_ret << dendl;
3892 return;
3893 }
3894 }
3895
3896 if (supplied_etag) {
3897 strncpy(supplied_md5, supplied_etag, sizeof(supplied_md5) - 1);
3898 supplied_md5[sizeof(supplied_md5) - 1] = '\0';
3899 }
3900
3901 const bool multipart = !multipart_upload_id.empty();
3902 auto& obj_ctx = *static_cast<RGWObjectCtx*>(s->obj_ctx);
3903
3904 /* Handle object versioning of Swift API. */
3905 if (! multipart) {
3906 op_ret = s->object->swift_versioning_copy(s->obj_ctx, this, s->yield);
3907 if (op_ret < 0) {
3908 return;
3909 }
3910 }
3911
3912 // make reservation for notification if needed
3913 std::unique_ptr<rgw::sal::Notification> res
3914 = store->get_notification(
3915 s->object.get(), s->src_object.get(), s,
3916 rgw::notify::ObjectCreatedPut);
3917 if(!multipart) {
3918 op_ret = res->publish_reserve(this, obj_tags.get());
3919 if (op_ret < 0) {
3920 return;
3921 }
3922 }
3923
3924 // create the object processor
3925 auto aio = rgw::make_throttle(s->cct->_conf->rgw_put_obj_min_window_size,
3926 s->yield);
3927 std::unique_ptr<rgw::sal::Writer> processor;
3928
3929 rgw_placement_rule *pdest_placement = &s->dest_placement;
3930
3931 if (multipart) {
3932 std::unique_ptr<rgw::sal::MultipartUpload> upload;
3933 upload = s->bucket->get_multipart_upload(s->object->get_name(),
3934 multipart_upload_id);
3935 op_ret = upload->get_info(this, s->yield, s->obj_ctx, &pdest_placement);
3936
3937 s->trace->SetAttribute(tracing::rgw::UPLOAD_ID, multipart_upload_id);
3938 multipart_trace = tracing::rgw::tracer.add_span(name(), upload->get_trace());
3939
3940 if (op_ret < 0) {
3941 if (op_ret != -ENOENT) {
3942 ldpp_dout(this, 0) << "ERROR: get_multipart_info returned " << op_ret << ": " << cpp_strerror(-op_ret) << dendl;
3943 } else {// -ENOENT: raced with upload complete/cancel, no need to spam log
3944 ldpp_dout(this, 20) << "failed to get multipart info (returned " << op_ret << ": " << cpp_strerror(-op_ret) << "): probably raced with upload complete / cancel" << dendl;
3945 }
3946 return;
3947 }
3948 /* upload will go out of scope, so copy the dest placement for later use */
3949 s->dest_placement = *pdest_placement;
3950 pdest_placement = &s->dest_placement;
3951 ldpp_dout(this, 20) << "dest_placement for part=" << *pdest_placement << dendl;
3952 processor = upload->get_writer(this, s->yield, s->object->clone(),
3953 s->user->get_id(), obj_ctx, pdest_placement,
3954 multipart_part_num, multipart_part_str);
3955 } else if(append) {
3956 if (s->bucket->versioned()) {
3957 op_ret = -ERR_INVALID_BUCKET_STATE;
3958 return;
3959 }
3960 processor = store->get_append_writer(this, s->yield, s->object->clone(),
3961 s->bucket_owner.get_id(), obj_ctx,
3962 pdest_placement, s->req_id, position,
3963 &cur_accounted_size);
3964 } else {
3965 if (s->bucket->versioning_enabled()) {
3966 if (!version_id.empty()) {
3967 s->object->set_instance(version_id);
3968 } else {
3969 s->object->gen_rand_obj_instance_name();
3970 version_id = s->object->get_instance();
3971 }
3972 }
3973 processor = store->get_atomic_writer(this, s->yield, s->object->clone(),
3974 s->bucket_owner.get_id(), obj_ctx,
3975 pdest_placement, olh_epoch, s->req_id);
3976 }
3977
3978 op_ret = processor->prepare(s->yield);
3979 if (op_ret < 0) {
3980 ldpp_dout(this, 20) << "processor->prepare() returned ret=" << op_ret
3981 << dendl;
3982 return;
3983 }
3984 if ((! copy_source.empty()) && !copy_source_range) {
3985 std::unique_ptr<rgw::sal::Bucket> bucket;
3986 op_ret = store->get_bucket(nullptr, copy_source_bucket_info, &bucket);
3987 if (op_ret < 0) {
3988 ldpp_dout(this, 0) << "ERROR: failed to get bucket with error" << op_ret << dendl;
3989 return;
3990 }
3991 std::unique_ptr<rgw::sal::Object> obj =
3992 bucket->get_object(rgw_obj_key(copy_source_object_name, copy_source_version_id));
3993
3994 RGWObjState *astate;
3995 op_ret = obj->get_obj_state(this, &obj_ctx, &astate, s->yield);
3996 if (op_ret < 0) {
3997 ldpp_dout(this, 0) << "ERROR: get copy source obj state returned with error" << op_ret << dendl;
3998 return;
3999 }
4000 bufferlist bl;
4001 if (astate->get_attr(RGW_ATTR_MANIFEST, bl)) {
4002 RGWObjManifest m;
4003 decode(m, bl);
4004 if (m.get_tier_type() == "cloud-s3") {
4005 op_ret = -ERR_INVALID_OBJECT_STATE;
4006 ldpp_dout(this, 0) << "ERROR: Cannot copy cloud tiered object. Failing with "
4007 << op_ret << dendl;
4008 return;
4009 }
4010 }
4011
4012 if (!astate->exists){
4013 op_ret = -ENOENT;
4014 return;
4015 }
4016 lst = astate->accounted_size - 1;
4017 } else {
4018 lst = copy_source_range_lst;
4019 }
4020 fst = copy_source_range_fst;
4021
4022 // no filters by default
4023 rgw::sal::DataProcessor *filter = processor.get();
4024
4025 const auto& compression_type = store->get_zone()->get_params().get_compression_type(*pdest_placement);
4026 CompressorRef plugin;
4027 boost::optional<RGWPutObj_Compress> compressor;
4028
4029 std::unique_ptr<rgw::sal::DataProcessor> encrypt;
4030
4031 if (!append) { // compression and encryption only apply to full object uploads
4032 op_ret = get_encrypt_filter(&encrypt, filter);
4033 if (op_ret < 0) {
4034 return;
4035 }
4036 if (encrypt != nullptr) {
4037 filter = &*encrypt;
4038 } else if (compression_type != "none") {
4039 plugin = get_compressor_plugin(s, compression_type);
4040 if (!plugin) {
4041 ldpp_dout(this, 1) << "Cannot load plugin for compression type "
4042 << compression_type << dendl;
4043 } else {
4044 compressor.emplace(s->cct, plugin, filter);
4045 filter = &*compressor;
4046 // always send incompressible hint when rgw is itself doing compression
4047 s->object->set_compressed(s->obj_ctx);
4048 }
4049 }
4050 }
4051 tracepoint(rgw_op, before_data_transfer, s->req_id.c_str());
4052 do {
4053 bufferlist data;
4054 if (fst > lst)
4055 break;
4056 if (copy_source.empty()) {
4057 len = get_data(data);
4058 } else {
4059 off_t cur_lst = min<off_t>(fst + s->cct->_conf->rgw_max_chunk_size - 1, lst);
4060 op_ret = get_data(fst, cur_lst, data);
4061 if (op_ret < 0)
4062 return;
4063 len = data.length();
4064 s->content_length += len;
4065 fst += len;
4066 }
4067 if (len < 0) {
4068 op_ret = len;
4069 ldpp_dout(this, 20) << "get_data() returned ret=" << op_ret << dendl;
4070 return;
4071 } else if (len == 0) {
4072 break;
4073 }
4074
4075 if (need_calc_md5) {
4076 hash.Update((const unsigned char *)data.c_str(), data.length());
4077 }
4078
4079 /* update torrrent */
4080 torrent.update(data);
4081
4082 op_ret = filter->process(std::move(data), ofs);
4083 if (op_ret < 0) {
4084 ldpp_dout(this, 20) << "processor->process() returned ret="
4085 << op_ret << dendl;
4086 return;
4087 }
4088
4089 ofs += len;
4090 } while (len > 0);
4091 tracepoint(rgw_op, after_data_transfer, s->req_id.c_str(), ofs);
4092
4093 // flush any data in filters
4094 op_ret = filter->process({}, ofs);
4095 if (op_ret < 0) {
4096 return;
4097 }
4098
4099 if (!chunked_upload && ofs != s->content_length) {
4100 op_ret = -ERR_REQUEST_TIMEOUT;
4101 return;
4102 }
4103 s->obj_size = ofs;
4104 s->object->set_obj_size(ofs);
4105
4106 perfcounter->inc(l_rgw_put_b, s->obj_size);
4107
4108 op_ret = do_aws4_auth_completion();
4109 if (op_ret < 0) {
4110 return;
4111 }
4112
4113 op_ret = s->bucket->check_quota(this, user_quota, bucket_quota, s->obj_size, y);
4114 if (op_ret < 0) {
4115 ldpp_dout(this, 20) << "second check_quota() returned op_ret=" << op_ret << dendl;
4116 return;
4117 }
4118
4119 hash.Final(m);
4120
4121 if (compressor && compressor->is_compressed()) {
4122 bufferlist tmp;
4123 RGWCompressionInfo cs_info;
4124 cs_info.compression_type = plugin->get_type_name();
4125 cs_info.orig_size = s->obj_size;
4126 cs_info.compressor_message = compressor->get_compressor_message();
4127 cs_info.blocks = move(compressor->get_compression_blocks());
4128 encode(cs_info, tmp);
4129 attrs[RGW_ATTR_COMPRESSION] = tmp;
4130 ldpp_dout(this, 20) << "storing " << RGW_ATTR_COMPRESSION
4131 << " with type=" << cs_info.compression_type
4132 << ", orig_size=" << cs_info.orig_size
4133 << ", blocks=" << cs_info.blocks.size() << dendl;
4134 }
4135
4136 buf_to_hex(m, CEPH_CRYPTO_MD5_DIGESTSIZE, calc_md5);
4137
4138 etag = calc_md5;
4139
4140 if (supplied_md5_b64 && strcmp(calc_md5, supplied_md5)) {
4141 op_ret = -ERR_BAD_DIGEST;
4142 return;
4143 }
4144
4145 policy.encode(aclbl);
4146 emplace_attr(RGW_ATTR_ACL, std::move(aclbl));
4147
4148 if (dlo_manifest) {
4149 op_ret = encode_dlo_manifest_attr(dlo_manifest, attrs);
4150 if (op_ret < 0) {
4151 ldpp_dout(this, 0) << "bad user manifest: " << dlo_manifest << dendl;
4152 return;
4153 }
4154 }
4155
4156 if (slo_info) {
4157 bufferlist manifest_bl;
4158 encode(*slo_info, manifest_bl);
4159 emplace_attr(RGW_ATTR_SLO_MANIFEST, std::move(manifest_bl));
4160 }
4161
4162 if (supplied_etag && etag.compare(supplied_etag) != 0) {
4163 op_ret = -ERR_UNPROCESSABLE_ENTITY;
4164 return;
4165 }
4166 bl.append(etag.c_str(), etag.size());
4167 emplace_attr(RGW_ATTR_ETAG, std::move(bl));
4168
4169 populate_with_generic_attrs(s, attrs);
4170 op_ret = rgw_get_request_metadata(this, s->cct, s->info, attrs);
4171 if (op_ret < 0) {
4172 return;
4173 }
4174 encode_delete_at_attr(delete_at, attrs);
4175 encode_obj_tags_attr(obj_tags.get(), attrs);
4176 rgw_cond_decode_objtags(s, attrs);
4177
4178 /* Add a custom metadata to expose the information whether an object
4179 * is an SLO or not. Appending the attribute must be performed AFTER
4180 * processing any input from user in order to prohibit overwriting. */
4181 if (slo_info) {
4182 bufferlist slo_userindicator_bl;
4183 slo_userindicator_bl.append("True", 4);
4184 emplace_attr(RGW_ATTR_SLO_UINDICATOR, std::move(slo_userindicator_bl));
4185 }
4186 if (obj_legal_hold) {
4187 bufferlist obj_legal_hold_bl;
4188 obj_legal_hold->encode(obj_legal_hold_bl);
4189 emplace_attr(RGW_ATTR_OBJECT_LEGAL_HOLD, std::move(obj_legal_hold_bl));
4190 }
4191 if (obj_retention) {
4192 bufferlist obj_retention_bl;
4193 obj_retention->encode(obj_retention_bl);
4194 emplace_attr(RGW_ATTR_OBJECT_RETENTION, std::move(obj_retention_bl));
4195 }
4196
4197 tracepoint(rgw_op, processor_complete_enter, s->req_id.c_str());
4198 op_ret = processor->complete(s->obj_size, etag, &mtime, real_time(), attrs,
4199 (delete_at ? *delete_at : real_time()), if_match, if_nomatch,
4200 (user_data.empty() ? nullptr : &user_data), nullptr, nullptr,
4201 s->yield);
4202 tracepoint(rgw_op, processor_complete_exit, s->req_id.c_str());
4203
4204 /* produce torrent */
4205 if (s->cct->_conf->rgw_torrent_flag && (ofs == torrent.get_data_len()))
4206 {
4207 torrent.init(s, store);
4208 torrent.set_create_date(mtime);
4209 op_ret = torrent.complete(y);
4210 if (0 != op_ret)
4211 {
4212 ldpp_dout(this, 0) << "ERROR: torrent.handle_data() returned " << op_ret << dendl;
4213 return;
4214 }
4215 }
4216
4217 // send request to notification manager
4218 int ret = res->publish_commit(this, s->obj_size, mtime, etag, s->object->get_instance());
4219 if (ret < 0) {
4220 ldpp_dout(this, 1) << "ERROR: publishing notification failed, with error: " << ret << dendl;
4221 // too late to rollback operation, hence op_ret is not set here
4222 }
4223 }
4224
4225 int RGWPostObj::verify_permission(optional_yield y)
4226 {
4227 return 0;
4228 }
4229
4230 void RGWPostObj::pre_exec()
4231 {
4232 rgw_bucket_object_pre_exec(s);
4233 }
4234
4235 void RGWPostObj::execute(optional_yield y)
4236 {
4237 boost::optional<RGWPutObj_Compress> compressor;
4238 CompressorRef plugin;
4239 char supplied_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
4240
4241 /* Read in the data from the POST form. */
4242 op_ret = get_params(y);
4243 if (op_ret < 0) {
4244 return;
4245 }
4246
4247 op_ret = verify_params();
4248 if (op_ret < 0) {
4249 return;
4250 }
4251
4252 if (s->iam_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
4253 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
4254 rgw::IAM::s3PutObject,
4255 s->object->get_obj());
4256 if (identity_policy_res == Effect::Deny) {
4257 op_ret = -EACCES;
4258 return;
4259 }
4260
4261 rgw::IAM::Effect e = Effect::Pass;
4262 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
4263 if (s->iam_policy) {
4264 ARN obj_arn(s->object->get_obj());
4265 e = s->iam_policy->eval(s->env, *s->auth.identity,
4266 rgw::IAM::s3PutObject,
4267 obj_arn,
4268 princ_type);
4269 }
4270 if (e == Effect::Deny) {
4271 op_ret = -EACCES;
4272 return;
4273 }
4274
4275 if (!s->session_policies.empty()) {
4276 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
4277 rgw::IAM::s3PutObject,
4278 s->object->get_obj());
4279 if (session_policy_res == Effect::Deny) {
4280 op_ret = -EACCES;
4281 return;
4282 }
4283 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
4284 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
4285 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
4286 (session_policy_res == Effect::Allow && e == Effect::Allow)) {
4287 op_ret = 0;
4288 return;
4289 }
4290 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
4291 //Intersection of session policy and identity policy plus bucket policy
4292 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) {
4293 op_ret = 0;
4294 return;
4295 }
4296 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
4297 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) {
4298 op_ret = 0;
4299 return;
4300 }
4301 }
4302 op_ret = -EACCES;
4303 return;
4304 }
4305 if (identity_policy_res == Effect::Pass && e == Effect::Pass && !verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
4306 op_ret = -EACCES;
4307 return;
4308 }
4309 } else if (!verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
4310 op_ret = -EACCES;
4311 return;
4312 }
4313
4314 // make reservation for notification if needed
4315 std::unique_ptr<rgw::sal::Notification> res
4316 = store->get_notification(s->object.get(), s->src_object.get(), s, rgw::notify::ObjectCreatedPost);
4317 op_ret = res->publish_reserve(this);
4318 if (op_ret < 0) {
4319 return;
4320 }
4321
4322 /* Start iteration over data fields. It's necessary as Swift's FormPost
4323 * is capable to handle multiple files in single form. */
4324 do {
4325 char calc_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
4326 unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE];
4327 MD5 hash;
4328 // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
4329 hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
4330 ceph::buffer::list bl, aclbl;
4331 int len = 0;
4332
4333 op_ret = s->bucket->check_quota(this, user_quota, bucket_quota, s->content_length, y);
4334 if (op_ret < 0) {
4335 return;
4336 }
4337
4338 if (supplied_md5_b64) {
4339 char supplied_md5_bin[CEPH_CRYPTO_MD5_DIGESTSIZE + 1];
4340 ldpp_dout(this, 15) << "supplied_md5_b64=" << supplied_md5_b64 << dendl;
4341 op_ret = ceph_unarmor(supplied_md5_bin, &supplied_md5_bin[CEPH_CRYPTO_MD5_DIGESTSIZE + 1],
4342 supplied_md5_b64, supplied_md5_b64 + strlen(supplied_md5_b64));
4343 ldpp_dout(this, 15) << "ceph_armor ret=" << op_ret << dendl;
4344 if (op_ret != CEPH_CRYPTO_MD5_DIGESTSIZE) {
4345 op_ret = -ERR_INVALID_DIGEST;
4346 return;
4347 }
4348
4349 buf_to_hex((const unsigned char *)supplied_md5_bin, CEPH_CRYPTO_MD5_DIGESTSIZE, supplied_md5);
4350 ldpp_dout(this, 15) << "supplied_md5=" << supplied_md5 << dendl;
4351 }
4352
4353 std::unique_ptr<rgw::sal::Object> obj =
4354 s->bucket->get_object(rgw_obj_key(get_current_filename()));
4355 if (s->bucket->versioning_enabled()) {
4356 obj->gen_rand_obj_instance_name();
4357 }
4358
4359 auto aio = rgw::make_throttle(s->cct->_conf->rgw_put_obj_min_window_size,
4360 s->yield);
4361
4362 std::unique_ptr<rgw::sal::Writer> processor;
4363 processor = store->get_atomic_writer(this, s->yield, std::move(obj),
4364 s->bucket_owner.get_id(), *s->obj_ctx,
4365 &s->dest_placement, 0, s->req_id);
4366 op_ret = processor->prepare(s->yield);
4367 if (op_ret < 0) {
4368 return;
4369 }
4370
4371 /* No filters by default. */
4372 rgw::sal::DataProcessor *filter = processor.get();
4373
4374 std::unique_ptr<rgw::sal::DataProcessor> encrypt;
4375 op_ret = get_encrypt_filter(&encrypt, filter);
4376 if (op_ret < 0) {
4377 return;
4378 }
4379 if (encrypt != nullptr) {
4380 filter = encrypt.get();
4381 } else {
4382 const auto& compression_type = store->get_zone()->get_params().get_compression_type(
4383 s->dest_placement);
4384 if (compression_type != "none") {
4385 plugin = Compressor::create(s->cct, compression_type);
4386 if (!plugin) {
4387 ldpp_dout(this, 1) << "Cannot load plugin for compression type "
4388 << compression_type << dendl;
4389 } else {
4390 compressor.emplace(s->cct, plugin, filter);
4391 filter = &*compressor;
4392 }
4393 }
4394 }
4395
4396 bool again;
4397 do {
4398 ceph::bufferlist data;
4399 len = get_data(data, again);
4400
4401 if (len < 0) {
4402 op_ret = len;
4403 return;
4404 }
4405
4406 if (!len) {
4407 break;
4408 }
4409
4410 hash.Update((const unsigned char *)data.c_str(), data.length());
4411 op_ret = filter->process(std::move(data), ofs);
4412 if (op_ret < 0) {
4413 return;
4414 }
4415
4416 ofs += len;
4417
4418 if (ofs > max_len) {
4419 op_ret = -ERR_TOO_LARGE;
4420 return;
4421 }
4422 } while (again);
4423
4424 // flush
4425 op_ret = filter->process({}, ofs);
4426 if (op_ret < 0) {
4427 return;
4428 }
4429
4430 if (len < min_len) {
4431 op_ret = -ERR_TOO_SMALL;
4432 return;
4433 }
4434
4435 s->obj_size = ofs;
4436 s->object->set_obj_size(ofs);
4437
4438
4439 op_ret = s->bucket->check_quota(this, user_quota, bucket_quota, s->obj_size, y);
4440 if (op_ret < 0) {
4441 return;
4442 }
4443
4444 hash.Final(m);
4445 buf_to_hex(m, CEPH_CRYPTO_MD5_DIGESTSIZE, calc_md5);
4446
4447 etag = calc_md5;
4448
4449 if (supplied_md5_b64 && strcmp(calc_md5, supplied_md5)) {
4450 op_ret = -ERR_BAD_DIGEST;
4451 return;
4452 }
4453
4454 bl.append(etag.c_str(), etag.size());
4455 emplace_attr(RGW_ATTR_ETAG, std::move(bl));
4456
4457 policy.encode(aclbl);
4458 emplace_attr(RGW_ATTR_ACL, std::move(aclbl));
4459
4460 const std::string content_type = get_current_content_type();
4461 if (! content_type.empty()) {
4462 ceph::bufferlist ct_bl;
4463 ct_bl.append(content_type.c_str(), content_type.size() + 1);
4464 emplace_attr(RGW_ATTR_CONTENT_TYPE, std::move(ct_bl));
4465 }
4466
4467 if (compressor && compressor->is_compressed()) {
4468 ceph::bufferlist tmp;
4469 RGWCompressionInfo cs_info;
4470 cs_info.compression_type = plugin->get_type_name();
4471 cs_info.orig_size = s->obj_size;
4472 cs_info.compressor_message = compressor->get_compressor_message();
4473 cs_info.blocks = move(compressor->get_compression_blocks());
4474 encode(cs_info, tmp);
4475 emplace_attr(RGW_ATTR_COMPRESSION, std::move(tmp));
4476 }
4477
4478 op_ret = processor->complete(s->obj_size, etag, nullptr, real_time(), attrs,
4479 (delete_at ? *delete_at : real_time()),
4480 nullptr, nullptr, nullptr, nullptr, nullptr,
4481 s->yield);
4482 if (op_ret < 0) {
4483 return;
4484 }
4485 } while (is_next_file_to_upload());
4486
4487 // send request to notification manager
4488 int ret = res->publish_commit(this, ofs, s->object->get_mtime(), etag, s->object->get_instance());
4489 if (ret < 0) {
4490 ldpp_dout(this, 1) << "ERROR: publishing notification failed, with error: " << ret << dendl;
4491 // too late to rollback operation, hence op_ret is not set here
4492 }
4493 }
4494
4495
4496 void RGWPutMetadataAccount::filter_out_temp_url(map<string, bufferlist>& add_attrs,
4497 const set<string>& rmattr_names,
4498 map<int, string>& temp_url_keys)
4499 {
4500 map<string, bufferlist>::iterator iter;
4501
4502 iter = add_attrs.find(RGW_ATTR_TEMPURL_KEY1);
4503 if (iter != add_attrs.end()) {
4504 temp_url_keys[0] = iter->second.c_str();
4505 add_attrs.erase(iter);
4506 }
4507
4508 iter = add_attrs.find(RGW_ATTR_TEMPURL_KEY2);
4509 if (iter != add_attrs.end()) {
4510 temp_url_keys[1] = iter->second.c_str();
4511 add_attrs.erase(iter);
4512 }
4513
4514 for (const string& name : rmattr_names) {
4515 if (name.compare(RGW_ATTR_TEMPURL_KEY1) == 0) {
4516 temp_url_keys[0] = string();
4517 }
4518 if (name.compare(RGW_ATTR_TEMPURL_KEY2) == 0) {
4519 temp_url_keys[1] = string();
4520 }
4521 }
4522 }
4523
4524 int RGWPutMetadataAccount::init_processing(optional_yield y)
4525 {
4526 /* First, go to the base class. At the time of writing the method was
4527 * responsible only for initializing the quota. This isn't necessary
4528 * here as we are touching metadata only. I'm putting this call only
4529 * for the future. */
4530 op_ret = RGWOp::init_processing(y);
4531 if (op_ret < 0) {
4532 return op_ret;
4533 }
4534
4535 op_ret = get_params(y);
4536 if (op_ret < 0) {
4537 return op_ret;
4538 }
4539
4540 op_ret = s->user->read_attrs(this, y);
4541 if (op_ret < 0) {
4542 return op_ret;
4543 }
4544 orig_attrs = s->user->get_attrs();
4545
4546 if (has_policy) {
4547 bufferlist acl_bl;
4548 policy.encode(acl_bl);
4549 attrs.emplace(RGW_ATTR_ACL, std::move(acl_bl));
4550 }
4551
4552 op_ret = rgw_get_request_metadata(this, s->cct, s->info, attrs, false);
4553 if (op_ret < 0) {
4554 return op_ret;
4555 }
4556 prepare_add_del_attrs(orig_attrs, rmattr_names, attrs);
4557 populate_with_generic_attrs(s, attrs);
4558
4559 /* Try extract the TempURL-related stuff now to allow verify_permission
4560 * evaluate whether we need FULL_CONTROL or not. */
4561 filter_out_temp_url(attrs, rmattr_names, temp_url_keys);
4562
4563 /* The same with quota except a client needs to be reseller admin. */
4564 op_ret = filter_out_quota_info(attrs, rmattr_names, new_quota,
4565 &new_quota_extracted);
4566 if (op_ret < 0) {
4567 return op_ret;
4568 }
4569
4570 return 0;
4571 }
4572
4573 int RGWPutMetadataAccount::verify_permission(optional_yield y)
4574 {
4575 if (s->auth.identity->is_anonymous()) {
4576 return -EACCES;
4577 }
4578
4579 if (!verify_user_permission_no_policy(this, s, RGW_PERM_WRITE)) {
4580 return -EACCES;
4581 }
4582
4583 /* Altering TempURL keys requires FULL_CONTROL. */
4584 if (!temp_url_keys.empty() && s->perm_mask != RGW_PERM_FULL_CONTROL) {
4585 return -EPERM;
4586 }
4587
4588 /* We are failing this intensionally to allow system user/reseller admin
4589 * override in rgw_process.cc. This is the way to specify a given RGWOp
4590 * expect extra privileges. */
4591 if (new_quota_extracted) {
4592 return -EACCES;
4593 }
4594
4595 return 0;
4596 }
4597
4598 void RGWPutMetadataAccount::execute(optional_yield y)
4599 {
4600 /* Params have been extracted earlier. See init_processing(). */
4601 op_ret = s->user->load_user(this, y);
4602 if (op_ret < 0) {
4603 return;
4604 }
4605
4606 /* Handle the TempURL-related stuff. */
4607 if (!temp_url_keys.empty()) {
4608 for (auto& pair : temp_url_keys) {
4609 s->user->get_info().temp_url_keys[pair.first] = std::move(pair.second);
4610 }
4611 }
4612
4613 /* Handle the quota extracted at the verify_permission step. */
4614 if (new_quota_extracted) {
4615 s->user->get_info().user_quota = std::move(new_quota);
4616 }
4617
4618 /* We are passing here the current (old) user info to allow the function
4619 * optimize-out some operations. */
4620 s->user->set_attrs(attrs);
4621 op_ret = s->user->store_user(this, y, false, &s->user->get_info());
4622 }
4623
4624 int RGWPutMetadataBucket::verify_permission(optional_yield y)
4625 {
4626 if (!verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
4627 return -EACCES;
4628 }
4629
4630 return 0;
4631 }
4632
4633 void RGWPutMetadataBucket::pre_exec()
4634 {
4635 rgw_bucket_object_pre_exec(s);
4636 }
4637
4638 void RGWPutMetadataBucket::execute(optional_yield y)
4639 {
4640 op_ret = get_params(y);
4641 if (op_ret < 0) {
4642 return;
4643 }
4644
4645 op_ret = rgw_get_request_metadata(this, s->cct, s->info, attrs, false);
4646 if (op_ret < 0) {
4647 return;
4648 }
4649
4650 if (!placement_rule.empty() &&
4651 placement_rule != s->bucket->get_placement_rule()) {
4652 op_ret = -EEXIST;
4653 return;
4654 }
4655
4656 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
4657 /* Encode special metadata first as we're using std::map::emplace under
4658 * the hood. This method will add the new items only if the map doesn't
4659 * contain such keys yet. */
4660 if (has_policy) {
4661 if (s->dialect.compare("swift") == 0) {
4662 auto old_policy = \
4663 static_cast<RGWAccessControlPolicy_SWIFT*>(s->bucket_acl.get());
4664 auto new_policy = static_cast<RGWAccessControlPolicy_SWIFT*>(&policy);
4665 new_policy->filter_merge(policy_rw_mask, old_policy);
4666 policy = *new_policy;
4667 }
4668 buffer::list bl;
4669 policy.encode(bl);
4670 emplace_attr(RGW_ATTR_ACL, std::move(bl));
4671 }
4672
4673 if (has_cors) {
4674 buffer::list bl;
4675 cors_config.encode(bl);
4676 emplace_attr(RGW_ATTR_CORS, std::move(bl));
4677 }
4678
4679 /* It's supposed that following functions WILL NOT change any
4680 * special attributes (like RGW_ATTR_ACL) if they are already
4681 * present in attrs. */
4682 prepare_add_del_attrs(s->bucket_attrs, rmattr_names, attrs);
4683 populate_with_generic_attrs(s, attrs);
4684
4685 /* According to the Swift's behaviour and its container_quota
4686 * WSGI middleware implementation: anyone with write permissions
4687 * is able to set the bucket quota. This stays in contrast to
4688 * account quotas that can be set only by clients holding
4689 * reseller admin privileges. */
4690 op_ret = filter_out_quota_info(attrs, rmattr_names, s->bucket->get_info().quota);
4691 if (op_ret < 0) {
4692 return op_ret;
4693 }
4694
4695 if (swift_ver_location) {
4696 s->bucket->get_info().swift_ver_location = *swift_ver_location;
4697 s->bucket->get_info().swift_versioning = (!swift_ver_location->empty());
4698 }
4699
4700 /* Web site of Swift API. */
4701 filter_out_website(attrs, rmattr_names, s->bucket->get_info().website_conf);
4702 s->bucket->get_info().has_website = !s->bucket->get_info().website_conf.is_empty();
4703
4704 /* Setting attributes also stores the provided bucket info. Due
4705 * to this fact, the new quota settings can be serialized with
4706 * the same call. */
4707 op_ret = s->bucket->merge_and_store_attrs(this, attrs, s->yield);
4708 return op_ret;
4709 });
4710 }
4711
4712 int RGWPutMetadataObject::verify_permission(optional_yield y)
4713 {
4714 // This looks to be something specific to Swift. We could add
4715 // operations like swift:PutMetadataObject to the Policy Engine.
4716 if (!verify_object_permission_no_policy(this, s, RGW_PERM_WRITE)) {
4717 return -EACCES;
4718 }
4719
4720 return 0;
4721 }
4722
4723 void RGWPutMetadataObject::pre_exec()
4724 {
4725 rgw_bucket_object_pre_exec(s);
4726 }
4727
4728 void RGWPutMetadataObject::execute(optional_yield y)
4729 {
4730 rgw_obj target_obj;
4731 rgw::sal::Attrs attrs, rmattrs;
4732
4733 s->object->set_atomic(s->obj_ctx);
4734
4735 op_ret = get_params(y);
4736 if (op_ret < 0) {
4737 return;
4738 }
4739
4740 op_ret = rgw_get_request_metadata(this, s->cct, s->info, attrs);
4741 if (op_ret < 0) {
4742 return;
4743 }
4744
4745 /* check if obj exists, read orig attrs */
4746 op_ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, s, &target_obj);
4747 if (op_ret < 0) {
4748 return;
4749 }
4750
4751 /* Check whether the object has expired. Swift API documentation
4752 * stands that we should return 404 Not Found in such case. */
4753 if (need_object_expiration() && s->object->is_expired()) {
4754 op_ret = -ENOENT;
4755 return;
4756 }
4757
4758 /* Filter currently existing attributes. */
4759 prepare_add_del_attrs(s->object->get_attrs(), attrs, rmattrs);
4760 populate_with_generic_attrs(s, attrs);
4761 encode_delete_at_attr(delete_at, attrs);
4762
4763 if (dlo_manifest) {
4764 op_ret = encode_dlo_manifest_attr(dlo_manifest, attrs);
4765 if (op_ret < 0) {
4766 ldpp_dout(this, 0) << "bad user manifest: " << dlo_manifest << dendl;
4767 return;
4768 }
4769 }
4770
4771 op_ret = s->object->set_obj_attrs(this, s->obj_ctx, &attrs, &rmattrs, s->yield, &target_obj);
4772 }
4773
4774 int RGWDeleteObj::handle_slo_manifest(bufferlist& bl, optional_yield y)
4775 {
4776 RGWSLOInfo slo_info;
4777 auto bliter = bl.cbegin();
4778 try {
4779 decode(slo_info, bliter);
4780 } catch (buffer::error& err) {
4781 ldpp_dout(this, 0) << "ERROR: failed to decode slo manifest" << dendl;
4782 return -EIO;
4783 }
4784
4785 try {
4786 deleter = std::unique_ptr<RGWBulkDelete::Deleter>(\
4787 new RGWBulkDelete::Deleter(this, store, s));
4788 } catch (const std::bad_alloc&) {
4789 return -ENOMEM;
4790 }
4791
4792 list<RGWBulkDelete::acct_path_t> items;
4793 for (const auto& iter : slo_info.entries) {
4794 const string& path_str = iter.path;
4795
4796 const size_t sep_pos = path_str.find('/', 1 /* skip first slash */);
4797 if (std::string_view::npos == sep_pos) {
4798 return -EINVAL;
4799 }
4800
4801 RGWBulkDelete::acct_path_t path;
4802
4803 path.bucket_name = url_decode(path_str.substr(1, sep_pos - 1));
4804 path.obj_key = url_decode(path_str.substr(sep_pos + 1));
4805
4806 items.push_back(path);
4807 }
4808
4809 /* Request removal of the manifest object itself. */
4810 RGWBulkDelete::acct_path_t path;
4811 path.bucket_name = s->bucket_name;
4812 path.obj_key = s->object->get_key();
4813 items.push_back(path);
4814
4815 int ret = deleter->delete_chunk(items, y);
4816 if (ret < 0) {
4817 return ret;
4818 }
4819
4820 return 0;
4821 }
4822
4823 int RGWDeleteObj::verify_permission(optional_yield y)
4824 {
4825 int op_ret = get_params(y);
4826 if (op_ret) {
4827 return op_ret;
4828 }
4829
4830 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
4831 if (has_s3_existing_tag || has_s3_resource_tag)
4832 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
4833
4834 if (s->iam_policy || ! s->iam_user_policies.empty() || ! s->session_policies.empty()) {
4835 if (s->bucket->get_info().obj_lock_enabled() && bypass_governance_mode) {
4836 auto r = eval_identity_or_session_policies(s->iam_user_policies, s->env,
4837 rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket->get_key(), s->object->get_name()));
4838 if (r == Effect::Deny) {
4839 bypass_perm = false;
4840 } else if (r == Effect::Pass && s->iam_policy) {
4841 ARN obj_arn(ARN(s->bucket->get_key(), s->object->get_name()));
4842 r = s->iam_policy->eval(s->env, *s->auth.identity, rgw::IAM::s3BypassGovernanceRetention, obj_arn);
4843 if (r == Effect::Deny) {
4844 bypass_perm = false;
4845 }
4846 } else if (r == Effect::Pass && !s->session_policies.empty()) {
4847 r = eval_identity_or_session_policies(s->session_policies, s->env,
4848 rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket->get_key(), s->object->get_name()));
4849 if (r == Effect::Deny) {
4850 bypass_perm = false;
4851 }
4852 }
4853 }
4854 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
4855 s->object->get_instance().empty() ?
4856 rgw::IAM::s3DeleteObject :
4857 rgw::IAM::s3DeleteObjectVersion,
4858 ARN(s->bucket->get_key(), s->object->get_name()));
4859 if (identity_policy_res == Effect::Deny) {
4860 return -EACCES;
4861 }
4862
4863 rgw::IAM::Effect r = Effect::Pass;
4864 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
4865 ARN obj_arn(ARN(s->bucket->get_key(), s->object->get_name()));
4866 if (s->iam_policy) {
4867 r = s->iam_policy->eval(s->env, *s->auth.identity,
4868 s->object->get_instance().empty() ?
4869 rgw::IAM::s3DeleteObject :
4870 rgw::IAM::s3DeleteObjectVersion,
4871 obj_arn,
4872 princ_type);
4873 }
4874 if (r == Effect::Deny)
4875 return -EACCES;
4876
4877 if (!s->session_policies.empty()) {
4878 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
4879 s->object->get_instance().empty() ?
4880 rgw::IAM::s3DeleteObject :
4881 rgw::IAM::s3DeleteObjectVersion,
4882 obj_arn);
4883 if (session_policy_res == Effect::Deny) {
4884 return -EACCES;
4885 }
4886 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
4887 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
4888 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
4889 (session_policy_res == Effect::Allow && r == Effect::Allow)) {
4890 return 0;
4891 }
4892 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
4893 //Intersection of session policy and identity policy plus bucket policy
4894 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || r == Effect::Allow) {
4895 return 0;
4896 }
4897 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
4898 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) {
4899 return 0;
4900 }
4901 }
4902 return -EACCES;
4903 }
4904 if (r == Effect::Allow || identity_policy_res == Effect::Allow)
4905 return 0;
4906 }
4907
4908 if (!verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
4909 return -EACCES;
4910 }
4911
4912 if (s->bucket->get_info().mfa_enabled() &&
4913 !s->object->get_instance().empty() &&
4914 !s->mfa_verified) {
4915 ldpp_dout(this, 5) << "NOTICE: object delete request with a versioned object, mfa auth not provided" << dendl;
4916 return -ERR_MFA_REQUIRED;
4917 }
4918
4919 return 0;
4920 }
4921
4922 void RGWDeleteObj::pre_exec()
4923 {
4924 rgw_bucket_object_pre_exec(s);
4925 }
4926
4927 void RGWDeleteObj::execute(optional_yield y)
4928 {
4929 if (!s->bucket_exists) {
4930 op_ret = -ERR_NO_SUCH_BUCKET;
4931 return;
4932 }
4933
4934 if (!rgw::sal::Object::empty(s->object.get())) {
4935 uint64_t obj_size = 0;
4936 std::string etag;
4937 RGWObjectCtx* obj_ctx = static_cast<RGWObjectCtx *>(s->obj_ctx);
4938 {
4939 RGWObjState* astate = nullptr;
4940 bool check_obj_lock = s->object->have_instance() && s->bucket->get_info().obj_lock_enabled();
4941
4942 op_ret = s->object->get_obj_state(this, obj_ctx, &astate, s->yield, true);
4943 if (op_ret < 0) {
4944 if (need_object_expiration() || multipart_delete) {
4945 return;
4946 }
4947
4948 if (check_obj_lock) {
4949 /* check if obj exists, read orig attrs */
4950 if (op_ret == -ENOENT) {
4951 /* object maybe delete_marker, skip check_obj_lock*/
4952 check_obj_lock = false;
4953 } else {
4954 return;
4955 }
4956 }
4957 } else {
4958 obj_size = astate->size;
4959 etag = astate->attrset[RGW_ATTR_ETAG].to_str();
4960 }
4961
4962 // ignore return value from get_obj_attrs in all other cases
4963 op_ret = 0;
4964
4965 if (check_obj_lock) {
4966 ceph_assert(astate);
4967 int object_lock_response = verify_object_lock(this, astate->attrset, bypass_perm, bypass_governance_mode);
4968 if (object_lock_response != 0) {
4969 op_ret = object_lock_response;
4970 if (op_ret == -EACCES) {
4971 s->err.message = "forbidden by object lock";
4972 }
4973 return;
4974 }
4975 }
4976
4977 if (multipart_delete) {
4978 if (!astate) {
4979 op_ret = -ERR_NOT_SLO_MANIFEST;
4980 return;
4981 }
4982
4983 const auto slo_attr = astate->attrset.find(RGW_ATTR_SLO_MANIFEST);
4984
4985 if (slo_attr != astate->attrset.end()) {
4986 op_ret = handle_slo_manifest(slo_attr->second, y);
4987 if (op_ret < 0) {
4988 ldpp_dout(this, 0) << "ERROR: failed to handle slo manifest ret=" << op_ret << dendl;
4989 }
4990 } else {
4991 op_ret = -ERR_NOT_SLO_MANIFEST;
4992 }
4993
4994 return;
4995 }
4996 }
4997
4998 // make reservation for notification if needed
4999 const auto versioned_object = s->bucket->versioning_enabled();
5000 const auto event_type = versioned_object &&
5001 s->object->get_instance().empty() ?
5002 rgw::notify::ObjectRemovedDeleteMarkerCreated :
5003 rgw::notify::ObjectRemovedDelete;
5004 std::unique_ptr<rgw::sal::Notification> res
5005 = store->get_notification(s->object.get(), s->src_object.get(), s,
5006 event_type);
5007 op_ret = res->publish_reserve(this);
5008 if (op_ret < 0) {
5009 return;
5010 }
5011
5012 s->object->set_atomic(s->obj_ctx);
5013
5014 bool ver_restored = false;
5015 op_ret = s->object->swift_versioning_restore(s->obj_ctx, ver_restored, this);
5016 if (op_ret < 0) {
5017 return;
5018 }
5019
5020 if (!ver_restored) {
5021 uint64_t epoch = 0;
5022
5023 /* Swift's versioning mechanism hasn't found any previous version of
5024 * the object that could be restored. This means we should proceed
5025 * with the regular delete path. */
5026 op_ret = get_system_versioning_params(s, &epoch, &version_id);
5027 if (op_ret < 0) {
5028 return;
5029 }
5030
5031 std::unique_ptr<rgw::sal::Object::DeleteOp> del_op = s->object->get_delete_op(obj_ctx);
5032 del_op->params.obj_owner = s->owner;
5033 del_op->params.bucket_owner = s->bucket_owner;
5034 del_op->params.versioning_status = s->bucket->get_info().versioning_status();
5035 del_op->params.unmod_since = unmod_since;
5036 del_op->params.high_precision_time = s->system_request;
5037 del_op->params.olh_epoch = epoch;
5038 del_op->params.marker_version_id = version_id;
5039
5040 op_ret = del_op->delete_obj(this, y);
5041 if (op_ret >= 0) {
5042 delete_marker = del_op->result.delete_marker;
5043 version_id = del_op->result.version_id;
5044 }
5045
5046 /* Check whether the object has expired. Swift API documentation
5047 * stands that we should return 404 Not Found in such case. */
5048 if (need_object_expiration() && s->object->is_expired()) {
5049 op_ret = -ENOENT;
5050 return;
5051 }
5052 }
5053
5054 if (op_ret == -ECANCELED) {
5055 op_ret = 0;
5056 }
5057 if (op_ret == -ERR_PRECONDITION_FAILED && no_precondition_error) {
5058 op_ret = 0;
5059 }
5060
5061 // send request to notification manager
5062 int ret = res->publish_commit(this, obj_size, ceph::real_clock::now(), etag, version_id);
5063 if (ret < 0) {
5064 ldpp_dout(this, 1) << "ERROR: publishing notification failed, with error: " << ret << dendl;
5065 // too late to rollback operation, hence op_ret is not set here
5066 }
5067 } else {
5068 op_ret = -EINVAL;
5069 }
5070 }
5071
5072 bool RGWCopyObj::parse_copy_location(const std::string_view& url_src,
5073 string& bucket_name,
5074 rgw_obj_key& key,
5075 req_state* s)
5076 {
5077 std::string_view name_str;
5078 std::string_view params_str;
5079
5080 // search for ? before url-decoding so we don't accidentally match %3F
5081 size_t pos = url_src.find('?');
5082 if (pos == string::npos) {
5083 name_str = url_src;
5084 } else {
5085 name_str = url_src.substr(0, pos);
5086 params_str = url_src.substr(pos + 1);
5087 }
5088
5089 if (name_str[0] == '/') // trim leading slash
5090 name_str.remove_prefix(1);
5091
5092 std::string dec_src = url_decode(name_str);
5093
5094 pos = dec_src.find('/');
5095 if (pos == string::npos)
5096 return false;
5097
5098 bucket_name = dec_src.substr(0, pos);
5099 key.name = dec_src.substr(pos + 1);
5100
5101 if (key.name.empty()) {
5102 return false;
5103 }
5104
5105 if (! params_str.empty()) {
5106 RGWHTTPArgs args;
5107 args.set(std::string(params_str));
5108 args.parse(s);
5109
5110 key.instance = args.get("versionId", NULL);
5111 }
5112
5113 return true;
5114 }
5115
5116 int RGWCopyObj::verify_permission(optional_yield y)
5117 {
5118 RGWAccessControlPolicy src_acl(s->cct);
5119 boost::optional<Policy> src_policy;
5120 op_ret = get_params(y);
5121 if (op_ret < 0)
5122 return op_ret;
5123
5124 op_ret = get_system_versioning_params(s, &olh_epoch, &version_id);
5125 if (op_ret < 0) {
5126 return op_ret;
5127 }
5128
5129 op_ret = store->get_bucket(this, s->user.get(),
5130 rgw_bucket(src_tenant_name,
5131 src_bucket_name,
5132 s->bucket_instance_id),
5133 &src_bucket, y);
5134 if (op_ret < 0) {
5135 if (op_ret == -ENOENT) {
5136 op_ret = -ERR_NO_SUCH_BUCKET;
5137 }
5138 return op_ret;
5139 }
5140
5141 /* This is the only place the bucket is set on src_object */
5142 s->src_object->set_bucket(src_bucket.get());
5143 /* get buckets info (source and dest) */
5144 if (s->local_source && source_zone.empty()) {
5145 s->src_object->set_atomic(s->obj_ctx);
5146 s->src_object->set_prefetch_data(s->obj_ctx);
5147
5148 rgw_placement_rule src_placement;
5149
5150 /* check source object permissions */
5151 op_ret = read_obj_policy(this, store, s, src_bucket->get_info(), src_bucket->get_attrs(), &src_acl, &src_placement.storage_class,
5152 src_policy, src_bucket.get(), s->src_object.get(), y);
5153 if (op_ret < 0) {
5154 return op_ret;
5155 }
5156
5157 /* follow up on previous checks that required reading source object head */
5158 if (need_to_check_storage_class) {
5159 src_placement.inherit_from(src_bucket->get_placement_rule());
5160
5161 op_ret = check_storage_class(src_placement);
5162 if (op_ret < 0) {
5163 return op_ret;
5164 }
5165 }
5166
5167 /* admin request overrides permission checks */
5168 if (!s->auth.identity->is_admin_of(src_acl.get_owner().get_id())) {
5169 if (src_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
5170 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, src_policy, s->iam_user_policies, s->session_policies);
5171 if (has_s3_existing_tag || has_s3_resource_tag)
5172 rgw_iam_add_objtags(this, s, s->src_object.get(), has_s3_existing_tag, has_s3_resource_tag);
5173
5174 ARN obj_arn(s->src_object->get_obj());
5175 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
5176 s->src_object->get_instance().empty() ?
5177 rgw::IAM::s3GetObject :
5178 rgw::IAM::s3GetObjectVersion,
5179 obj_arn);
5180 if (identity_policy_res == Effect::Deny) {
5181 return -EACCES;
5182 }
5183 auto e = Effect::Pass;
5184 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
5185 if (src_policy) {
5186 e = src_policy->eval(s->env, *s->auth.identity,
5187 s->src_object->get_instance().empty() ?
5188 rgw::IAM::s3GetObject :
5189 rgw::IAM::s3GetObjectVersion,
5190 obj_arn,
5191 princ_type);
5192 }
5193 if (e == Effect::Deny) {
5194 return -EACCES;
5195 }
5196 if (!s->session_policies.empty()) {
5197 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
5198 s->src_object->get_instance().empty() ?
5199 rgw::IAM::s3GetObject :
5200 rgw::IAM::s3GetObjectVersion,
5201 obj_arn);
5202 if (session_policy_res == Effect::Deny) {
5203 return -EACCES;
5204 }
5205 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
5206 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
5207 if ((session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) &&
5208 (session_policy_res != Effect::Allow || e != Effect::Allow)) {
5209 return -EACCES;
5210 }
5211 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
5212 //Intersection of session policy and identity policy plus bucket policy
5213 if ((session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) && e != Effect::Allow) {
5214 return -EACCES;
5215 }
5216 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
5217 if (session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) {
5218 return -EACCES;
5219 }
5220 }
5221 }
5222 if (identity_policy_res == Effect::Pass && e == Effect::Pass &&
5223 !src_acl.verify_permission(this, *s->auth.identity, s->perm_mask,
5224 RGW_PERM_READ)) {
5225 return -EACCES;
5226 }
5227 //remove src object tags as it may interfere with policy evaluation of destination obj
5228 if (has_s3_existing_tag || has_s3_resource_tag)
5229 rgw_iam_remove_objtags(this, s, s->src_object.get(), has_s3_existing_tag, has_s3_resource_tag);
5230
5231 } else if (!src_acl.verify_permission(this, *s->auth.identity,
5232 s->perm_mask,
5233 RGW_PERM_READ)) {
5234 return -EACCES;
5235 }
5236 }
5237 }
5238
5239 RGWAccessControlPolicy dest_bucket_policy(s->cct);
5240
5241 if (src_bucket_name.compare(dest_bucket_name) == 0) { /* will only happen if s->local_source
5242 or intra region sync */
5243 dest_bucket = src_bucket->clone();
5244 } else {
5245 op_ret = store->get_bucket(this, s->user.get(), dest_tenant_name, dest_bucket_name, &dest_bucket, y);
5246 if (op_ret < 0) {
5247 if (op_ret == -ENOENT) {
5248 ldpp_dout(this, 0) << "ERROR: Destination Bucket not found for user: " << s->user->get_id().to_str() << dendl;
5249 op_ret = -ERR_NO_SUCH_BUCKET;
5250 }
5251 return op_ret;
5252 }
5253 }
5254
5255 dest_object = dest_bucket->get_object(rgw_obj_key(dest_obj_name));
5256 dest_object->set_atomic(s->obj_ctx);
5257
5258 /* check dest bucket permissions */
5259 op_ret = read_bucket_policy(this, store, s, dest_bucket->get_info(),
5260 dest_bucket->get_attrs(),
5261 &dest_bucket_policy, dest_bucket->get_key(), y);
5262 if (op_ret < 0) {
5263 return op_ret;
5264 }
5265 auto dest_iam_policy = get_iam_policy_from_attr(s->cct, dest_bucket->get_attrs(), dest_bucket->get_tenant());
5266 /* admin request overrides permission checks */
5267 if (! s->auth.identity->is_admin_of(dest_policy.get_owner().get_id())){
5268 if (dest_iam_policy != boost::none || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
5269 //Add destination bucket tags for authorization
5270 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, dest_iam_policy, s->iam_user_policies, s->session_policies);
5271 if (has_s3_resource_tag)
5272 rgw_iam_add_buckettags(this, s, dest_bucket.get());
5273
5274 rgw_add_to_iam_environment(s->env, "s3:x-amz-copy-source", copy_source);
5275 if (md_directive)
5276 rgw_add_to_iam_environment(s->env, "s3:x-amz-metadata-directive",
5277 *md_directive);
5278
5279 ARN obj_arn(dest_object->get_obj());
5280 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies,
5281 s->env,
5282 rgw::IAM::s3PutObject,
5283 obj_arn);
5284 if (identity_policy_res == Effect::Deny) {
5285 return -EACCES;
5286 }
5287 auto e = Effect::Pass;
5288 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
5289 if (dest_iam_policy) {
5290 e = dest_iam_policy->eval(s->env, *s->auth.identity,
5291 rgw::IAM::s3PutObject,
5292 obj_arn,
5293 princ_type);
5294 }
5295 if (e == Effect::Deny) {
5296 return -EACCES;
5297 }
5298 if (!s->session_policies.empty()) {
5299 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env, rgw::IAM::s3PutObject, obj_arn);
5300 if (session_policy_res == Effect::Deny) {
5301 return false;
5302 }
5303 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
5304 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
5305 if ((session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) &&
5306 (session_policy_res != Effect::Allow || e == Effect::Allow)) {
5307 return -EACCES;
5308 }
5309 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
5310 //Intersection of session policy and identity policy plus bucket policy
5311 if ((session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) && e != Effect::Allow) {
5312 return -EACCES;
5313 }
5314 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
5315 if (session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) {
5316 return -EACCES;
5317 }
5318 }
5319 }
5320 if (identity_policy_res == Effect::Pass && e == Effect::Pass &&
5321 ! dest_bucket_policy.verify_permission(this,
5322 *s->auth.identity,
5323 s->perm_mask,
5324 RGW_PERM_WRITE)){
5325 return -EACCES;
5326 }
5327 } else if (! dest_bucket_policy.verify_permission(this, *s->auth.identity, s->perm_mask,
5328 RGW_PERM_WRITE)) {
5329 return -EACCES;
5330 }
5331
5332 }
5333
5334 op_ret = init_dest_policy();
5335 if (op_ret < 0) {
5336 return op_ret;
5337 }
5338
5339 return 0;
5340 }
5341
5342
5343 int RGWCopyObj::init_common()
5344 {
5345 if (if_mod) {
5346 if (parse_time(if_mod, &mod_time) < 0) {
5347 op_ret = -EINVAL;
5348 return op_ret;
5349 }
5350 mod_ptr = &mod_time;
5351 }
5352
5353 if (if_unmod) {
5354 if (parse_time(if_unmod, &unmod_time) < 0) {
5355 op_ret = -EINVAL;
5356 return op_ret;
5357 }
5358 unmod_ptr = &unmod_time;
5359 }
5360
5361 bufferlist aclbl;
5362 dest_policy.encode(aclbl);
5363 emplace_attr(RGW_ATTR_ACL, std::move(aclbl));
5364
5365 op_ret = rgw_get_request_metadata(this, s->cct, s->info, attrs);
5366 if (op_ret < 0) {
5367 return op_ret;
5368 }
5369 populate_with_generic_attrs(s, attrs);
5370
5371 return 0;
5372 }
5373
5374 static void copy_obj_progress_cb(off_t ofs, void *param)
5375 {
5376 RGWCopyObj *op = static_cast<RGWCopyObj *>(param);
5377 op->progress_cb(ofs);
5378 }
5379
5380 void RGWCopyObj::progress_cb(off_t ofs)
5381 {
5382 if (!s->cct->_conf->rgw_copy_obj_progress)
5383 return;
5384
5385 if (ofs - last_ofs <
5386 static_cast<off_t>(s->cct->_conf->rgw_copy_obj_progress_every_bytes)) {
5387 return;
5388 }
5389
5390 send_partial_response(ofs);
5391
5392 last_ofs = ofs;
5393 }
5394
5395 void RGWCopyObj::pre_exec()
5396 {
5397 rgw_bucket_object_pre_exec(s);
5398 }
5399
5400 void RGWCopyObj::execute(optional_yield y)
5401 {
5402 if (init_common() < 0)
5403 return;
5404
5405 // make reservation for notification if needed
5406 std::unique_ptr<rgw::sal::Notification> res
5407 = store->get_notification(
5408 s->object.get(), s->src_object.get(),
5409 s, rgw::notify::ObjectCreatedCopy);
5410 op_ret = res->publish_reserve(this);
5411 if (op_ret < 0) {
5412 return;
5413 }
5414
5415 if ( ! version_id.empty()) {
5416 dest_object->set_instance(version_id);
5417 } else if (dest_bucket->versioning_enabled()) {
5418 dest_object->gen_rand_obj_instance_name();
5419 }
5420
5421 s->src_object->set_atomic(s->obj_ctx);
5422 dest_object->set_atomic(s->obj_ctx);
5423
5424 encode_delete_at_attr(delete_at, attrs);
5425
5426 if (obj_retention) {
5427 bufferlist obj_retention_bl;
5428 obj_retention->encode(obj_retention_bl);
5429 emplace_attr(RGW_ATTR_OBJECT_RETENTION, std::move(obj_retention_bl));
5430 }
5431 if (obj_legal_hold) {
5432 bufferlist obj_legal_hold_bl;
5433 obj_legal_hold->encode(obj_legal_hold_bl);
5434 emplace_attr(RGW_ATTR_OBJECT_LEGAL_HOLD, std::move(obj_legal_hold_bl));
5435 }
5436
5437 uint64_t obj_size = 0;
5438 {
5439 // get src object size (cached in obj_ctx from verify_permission())
5440 RGWObjState* astate = nullptr;
5441 op_ret = s->src_object->get_obj_state(this, s->obj_ctx, &astate, s->yield, true);
5442 if (op_ret < 0) {
5443 return;
5444 }
5445
5446 /* Check if the src object is cloud-tiered */
5447 bufferlist bl;
5448 if (astate->get_attr(RGW_ATTR_MANIFEST, bl)) {
5449 RGWObjManifest m;
5450 decode(m, bl);
5451 if (m.get_tier_type() == "cloud-s3") {
5452 op_ret = -ERR_INVALID_OBJECT_STATE;
5453 ldpp_dout(this, 0) << "ERROR: Cannot copy cloud tiered object. Failing with "
5454 << op_ret << dendl;
5455 return;
5456 }
5457 }
5458
5459 obj_size = astate->size;
5460
5461 if (!s->system_request) { // no quota enforcement for system requests
5462 if (astate->accounted_size > static_cast<size_t>(s->cct->_conf->rgw_max_put_size)) {
5463 op_ret = -ERR_TOO_LARGE;
5464 return;
5465 }
5466 // enforce quota against the destination bucket owner
5467 op_ret = dest_bucket->check_quota(this, user_quota, bucket_quota,
5468 astate->accounted_size, y);
5469 if (op_ret < 0) {
5470 return;
5471 }
5472 }
5473 }
5474
5475 bool high_precision_time = (s->system_request);
5476
5477 /* Handle object versioning of Swift API. In case of copying to remote this
5478 * should fail gently (op_ret == 0) as the dst_obj will not exist here. */
5479 op_ret = dest_object->swift_versioning_copy(s->obj_ctx, this, s->yield);
5480 if (op_ret < 0) {
5481 return;
5482 }
5483
5484 RGWObjectCtx& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
5485 op_ret = s->src_object->copy_object(obj_ctx,
5486 s->user.get(),
5487 &s->info,
5488 source_zone,
5489 dest_object.get(),
5490 dest_bucket.get(),
5491 src_bucket.get(),
5492 s->dest_placement,
5493 &src_mtime,
5494 &mtime,
5495 mod_ptr,
5496 unmod_ptr,
5497 high_precision_time,
5498 if_match,
5499 if_nomatch,
5500 attrs_mod,
5501 copy_if_newer,
5502 attrs,
5503 RGWObjCategory::Main,
5504 olh_epoch,
5505 delete_at,
5506 (version_id.empty() ? NULL : &version_id),
5507 &s->req_id, /* use req_id as tag */
5508 &etag,
5509 copy_obj_progress_cb, (void *)this,
5510 this,
5511 s->yield);
5512
5513 // send request to notification manager
5514 int ret = res->publish_commit(this, obj_size, mtime, etag, dest_object->get_instance());
5515 if (ret < 0) {
5516 ldpp_dout(this, 1) << "ERROR: publishing notification failed, with error: " << ret << dendl;
5517 // too late to rollback operation, hence op_ret is not set here
5518 }
5519 }
5520
5521 int RGWGetACLs::verify_permission(optional_yield y)
5522 {
5523 bool perm;
5524 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
5525 if (!rgw::sal::Object::empty(s->object.get())) {
5526 auto iam_action = s->object->get_instance().empty() ?
5527 rgw::IAM::s3GetObjectAcl :
5528 rgw::IAM::s3GetObjectVersionAcl;
5529 if (has_s3_existing_tag || has_s3_resource_tag)
5530 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
5531 perm = verify_object_permission(this, s, iam_action);
5532 } else {
5533 if (!s->bucket_exists) {
5534 return -ERR_NO_SUCH_BUCKET;
5535 }
5536 if (has_s3_resource_tag)
5537 rgw_iam_add_buckettags(this, s);
5538 perm = verify_bucket_permission(this, s, rgw::IAM::s3GetBucketAcl);
5539 }
5540 if (!perm)
5541 return -EACCES;
5542
5543 return 0;
5544 }
5545
5546 void RGWGetACLs::pre_exec()
5547 {
5548 rgw_bucket_object_pre_exec(s);
5549 }
5550
5551 void RGWGetACLs::execute(optional_yield y)
5552 {
5553 stringstream ss;
5554 RGWAccessControlPolicy* const acl = \
5555 (!rgw::sal::Object::empty(s->object.get()) ? s->object_acl.get() : s->bucket_acl.get());
5556 RGWAccessControlPolicy_S3* const s3policy = \
5557 static_cast<RGWAccessControlPolicy_S3*>(acl);
5558 s3policy->to_xml(ss);
5559 acls = ss.str();
5560 }
5561
5562
5563
5564 int RGWPutACLs::verify_permission(optional_yield y)
5565 {
5566 bool perm;
5567
5568 rgw_add_to_iam_environment(s->env, "s3:x-amz-acl", s->canned_acl);
5569
5570 rgw_add_grant_to_iam_environment(s->env, s);
5571 if (!rgw::sal::Object::empty(s->object.get())) {
5572 auto iam_action = s->object->get_instance().empty() ? rgw::IAM::s3PutObjectAcl : rgw::IAM::s3PutObjectVersionAcl;
5573 op_ret = rgw_iam_add_objtags(this, s, true, true);
5574 perm = verify_object_permission(this, s, iam_action);
5575 } else {
5576 op_ret = rgw_iam_add_buckettags(this, s);
5577 perm = verify_bucket_permission(this, s, rgw::IAM::s3PutBucketAcl);
5578 }
5579 if (!perm)
5580 return -EACCES;
5581
5582 return 0;
5583 }
5584
5585 int RGWGetLC::verify_permission(optional_yield y)
5586 {
5587 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
5588 if (has_s3_resource_tag)
5589 rgw_iam_add_buckettags(this, s);
5590
5591 bool perm;
5592 perm = verify_bucket_permission(this, s, rgw::IAM::s3GetLifecycleConfiguration);
5593 if (!perm)
5594 return -EACCES;
5595
5596 return 0;
5597 }
5598
5599 int RGWPutLC::verify_permission(optional_yield y)
5600 {
5601 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
5602 if (has_s3_resource_tag)
5603 rgw_iam_add_buckettags(this, s);
5604
5605 bool perm;
5606 perm = verify_bucket_permission(this, s, rgw::IAM::s3PutLifecycleConfiguration);
5607 if (!perm)
5608 return -EACCES;
5609
5610 return 0;
5611 }
5612
5613 int RGWDeleteLC::verify_permission(optional_yield y)
5614 {
5615 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
5616 if (has_s3_resource_tag)
5617 rgw_iam_add_buckettags(this, s);
5618
5619 bool perm;
5620 perm = verify_bucket_permission(this, s, rgw::IAM::s3PutLifecycleConfiguration);
5621 if (!perm)
5622 return -EACCES;
5623
5624 return 0;
5625 }
5626
5627 void RGWPutACLs::pre_exec()
5628 {
5629 rgw_bucket_object_pre_exec(s);
5630 }
5631
5632 void RGWGetLC::pre_exec()
5633 {
5634 rgw_bucket_object_pre_exec(s);
5635 }
5636
5637 void RGWPutLC::pre_exec()
5638 {
5639 rgw_bucket_object_pre_exec(s);
5640 }
5641
5642 void RGWDeleteLC::pre_exec()
5643 {
5644 rgw_bucket_object_pre_exec(s);
5645 }
5646
5647 void RGWPutACLs::execute(optional_yield y)
5648 {
5649 bufferlist bl;
5650
5651 RGWAccessControlPolicy_S3 *policy = NULL;
5652 RGWACLXMLParser_S3 parser(s->cct);
5653 RGWAccessControlPolicy_S3 new_policy(s->cct);
5654 stringstream ss;
5655
5656 op_ret = 0; /* XXX redundant? */
5657
5658 if (!parser.init()) {
5659 op_ret = -EINVAL;
5660 return;
5661 }
5662
5663
5664 RGWAccessControlPolicy* const existing_policy = \
5665 (rgw::sal::Object::empty(s->object.get()) ? s->bucket_acl.get() : s->object_acl.get());
5666
5667 owner = existing_policy->get_owner();
5668
5669 op_ret = get_params(y);
5670 if (op_ret < 0) {
5671 if (op_ret == -ERANGE) {
5672 ldpp_dout(this, 4) << "The size of request xml data is larger than the max limitation, data size = "
5673 << s->length << dendl;
5674 op_ret = -ERR_MALFORMED_XML;
5675 s->err.message = "The XML you provided was larger than the maximum " +
5676 std::to_string(s->cct->_conf->rgw_max_put_param_size) +
5677 " bytes allowed.";
5678 }
5679 return;
5680 }
5681
5682 char* buf = data.c_str();
5683 ldpp_dout(this, 15) << "read len=" << data.length() << " data=" << (buf ? buf : "") << dendl;
5684
5685 if (!s->canned_acl.empty() && data.length() > 0) {
5686 op_ret = -EINVAL;
5687 return;
5688 }
5689
5690 if (!s->canned_acl.empty() || s->has_acl_header) {
5691 op_ret = get_policy_from_state(store, s, ss);
5692 if (op_ret < 0)
5693 return;
5694
5695 data.clear();
5696 data.append(ss.str());
5697 }
5698
5699 if (!parser.parse(data.c_str(), data.length(), 1)) {
5700 op_ret = -EINVAL;
5701 return;
5702 }
5703 policy = static_cast<RGWAccessControlPolicy_S3 *>(parser.find_first("AccessControlPolicy"));
5704 if (!policy) {
5705 op_ret = -EINVAL;
5706 return;
5707 }
5708
5709 const RGWAccessControlList& req_acl = policy->get_acl();
5710 const multimap<string, ACLGrant>& req_grant_map = req_acl.get_grant_map();
5711 #define ACL_GRANTS_MAX_NUM 100
5712 int max_num = s->cct->_conf->rgw_acl_grants_max_num;
5713 if (max_num < 0) {
5714 max_num = ACL_GRANTS_MAX_NUM;
5715 }
5716
5717 int grants_num = req_grant_map.size();
5718 if (grants_num > max_num) {
5719 ldpp_dout(this, 4) << "An acl can have up to " << max_num
5720 << " grants, request acl grants num: " << grants_num << dendl;
5721 op_ret = -ERR_LIMIT_EXCEEDED;
5722 s->err.message = "The request is rejected, because the acl grants number you requested is larger than the maximum "
5723 + std::to_string(max_num)
5724 + " grants allowed in an acl.";
5725 return;
5726 }
5727
5728 // forward bucket acl requests to meta master zone
5729 if ((rgw::sal::Object::empty(s->object.get()))) {
5730 bufferlist in_data;
5731 // include acl data unless it was generated from a canned_acl
5732 if (s->canned_acl.empty()) {
5733 in_data.append(data);
5734 }
5735 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, in_data, nullptr, s->info, y);
5736 if (op_ret < 0) {
5737 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
5738 return;
5739 }
5740 }
5741
5742 if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 15>()) {
5743 ldpp_dout(this, 15) << "Old AccessControlPolicy";
5744 policy->to_xml(*_dout);
5745 *_dout << dendl;
5746 }
5747
5748 op_ret = policy->rebuild(this, store, &owner, new_policy, s->err.message);
5749 if (op_ret < 0)
5750 return;
5751
5752 if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 15>()) {
5753 ldpp_dout(this, 15) << "New AccessControlPolicy:";
5754 new_policy.to_xml(*_dout);
5755 *_dout << dendl;
5756 }
5757
5758 if (s->bucket_access_conf &&
5759 s->bucket_access_conf->block_public_acls() &&
5760 new_policy.is_public(this)) {
5761 op_ret = -EACCES;
5762 return;
5763 }
5764 new_policy.encode(bl);
5765 map<string, bufferlist> attrs;
5766
5767 if (!rgw::sal::Object::empty(s->object.get())) {
5768 s->object->set_atomic(s->obj_ctx);
5769 //if instance is empty, we should modify the latest object
5770 op_ret = s->object->modify_obj_attrs(s->obj_ctx, RGW_ATTR_ACL, bl, s->yield, this);
5771 } else {
5772 map<string,bufferlist> attrs = s->bucket_attrs;
5773 attrs[RGW_ATTR_ACL] = bl;
5774 op_ret = s->bucket->merge_and_store_attrs(this, attrs, y);
5775 }
5776 if (op_ret == -ECANCELED) {
5777 op_ret = 0; /* lost a race, but it's ok because acls are immutable */
5778 }
5779 }
5780
5781 void RGWPutLC::execute(optional_yield y)
5782 {
5783 bufferlist bl;
5784
5785 RGWLifecycleConfiguration_S3 config(s->cct);
5786 RGWXMLParser parser;
5787 RGWLifecycleConfiguration_S3 new_config(s->cct);
5788
5789 content_md5 = s->info.env->get("HTTP_CONTENT_MD5");
5790 if (content_md5 == nullptr) {
5791 op_ret = -ERR_INVALID_REQUEST;
5792 s->err.message = "Missing required header for this request: Content-MD5";
5793 ldpp_dout(this, 5) << s->err.message << dendl;
5794 return;
5795 }
5796
5797 std::string content_md5_bin;
5798 try {
5799 content_md5_bin = rgw::from_base64(std::string_view(content_md5));
5800 } catch (...) {
5801 s->err.message = "Request header Content-MD5 contains character "
5802 "that is not base64 encoded.";
5803 ldpp_dout(this, 5) << s->err.message << dendl;
5804 op_ret = -ERR_BAD_DIGEST;
5805 return;
5806 }
5807
5808 if (!parser.init()) {
5809 op_ret = -EINVAL;
5810 return;
5811 }
5812
5813 op_ret = get_params(y);
5814 if (op_ret < 0)
5815 return;
5816
5817 char* buf = data.c_str();
5818 ldpp_dout(this, 15) << "read len=" << data.length() << " data=" << (buf ? buf : "") << dendl;
5819
5820 MD5 data_hash;
5821 // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
5822 data_hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
5823 unsigned char data_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE];
5824 data_hash.Update(reinterpret_cast<const unsigned char*>(buf), data.length());
5825 data_hash.Final(data_hash_res);
5826
5827 if (memcmp(data_hash_res, content_md5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) {
5828 op_ret = -ERR_BAD_DIGEST;
5829 s->err.message = "The Content-MD5 you specified did not match what we received.";
5830 ldpp_dout(this, 5) << s->err.message
5831 << " Specified content md5: " << content_md5
5832 << ", calculated content md5: " << data_hash_res
5833 << dendl;
5834 return;
5835 }
5836
5837 if (!parser.parse(buf, data.length(), 1)) {
5838 op_ret = -ERR_MALFORMED_XML;
5839 return;
5840 }
5841
5842 try {
5843 RGWXMLDecoder::decode_xml("LifecycleConfiguration", config, &parser);
5844 } catch (RGWXMLDecoder::err& err) {
5845 ldpp_dout(this, 5) << "Bad lifecycle configuration: " << err << dendl;
5846 op_ret = -ERR_MALFORMED_XML;
5847 return;
5848 }
5849
5850 op_ret = config.rebuild(new_config);
5851 if (op_ret < 0)
5852 return;
5853
5854 if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 15>()) {
5855 XMLFormatter xf;
5856 new_config.dump_xml(&xf);
5857 stringstream ss;
5858 xf.flush(ss);
5859 ldpp_dout(this, 15) << "New LifecycleConfiguration:" << ss.str() << dendl;
5860 }
5861
5862 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
5863 if (op_ret < 0) {
5864 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
5865 return;
5866 }
5867
5868 op_ret = store->get_rgwlc()->set_bucket_config(s->bucket.get(), s->bucket_attrs, &new_config);
5869 if (op_ret < 0) {
5870 return;
5871 }
5872 return;
5873 }
5874
5875 void RGWDeleteLC::execute(optional_yield y)
5876 {
5877 bufferlist data;
5878 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
5879 if (op_ret < 0) {
5880 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
5881 return;
5882 }
5883
5884 op_ret = store->get_rgwlc()->remove_bucket_config(s->bucket.get(), s->bucket_attrs);
5885 if (op_ret < 0) {
5886 return;
5887 }
5888 return;
5889 }
5890
5891 int RGWGetCORS::verify_permission(optional_yield y)
5892 {
5893 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
5894 if (has_s3_resource_tag)
5895 rgw_iam_add_buckettags(this, s);
5896
5897 return verify_bucket_owner_or_policy(s, rgw::IAM::s3GetBucketCORS);
5898 }
5899
5900 void RGWGetCORS::execute(optional_yield y)
5901 {
5902 op_ret = read_bucket_cors();
5903 if (op_ret < 0)
5904 return ;
5905
5906 if (!cors_exist) {
5907 ldpp_dout(this, 2) << "No CORS configuration set yet for this bucket" << dendl;
5908 op_ret = -ERR_NO_CORS_FOUND;
5909 return;
5910 }
5911 }
5912
5913 int RGWPutCORS::verify_permission(optional_yield y)
5914 {
5915 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
5916 if (has_s3_resource_tag)
5917 rgw_iam_add_buckettags(this, s);
5918
5919 return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketCORS);
5920 }
5921
5922 void RGWPutCORS::execute(optional_yield y)
5923 {
5924 rgw_raw_obj obj;
5925
5926 op_ret = get_params(y);
5927 if (op_ret < 0)
5928 return;
5929
5930 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, in_data, nullptr, s->info, y);
5931 if (op_ret < 0) {
5932 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
5933 return;
5934 }
5935
5936 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
5937 rgw::sal::Attrs attrs(s->bucket_attrs);
5938 attrs[RGW_ATTR_CORS] = cors_bl;
5939 return s->bucket->merge_and_store_attrs(this, attrs, s->yield);
5940 });
5941 }
5942
5943 int RGWDeleteCORS::verify_permission(optional_yield y)
5944 {
5945 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
5946 if (has_s3_resource_tag)
5947 rgw_iam_add_buckettags(this, s);
5948
5949 // No separate delete permission
5950 return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketCORS);
5951 }
5952
5953 void RGWDeleteCORS::execute(optional_yield y)
5954 {
5955 bufferlist data;
5956 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
5957 if (op_ret < 0) {
5958 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
5959 return;
5960 }
5961
5962 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
5963 op_ret = read_bucket_cors();
5964 if (op_ret < 0)
5965 return op_ret;
5966
5967 if (!cors_exist) {
5968 ldpp_dout(this, 2) << "No CORS configuration set yet for this bucket" << dendl;
5969 op_ret = -ENOENT;
5970 return op_ret;
5971 }
5972
5973 rgw::sal::Attrs attrs(s->bucket_attrs);
5974 attrs.erase(RGW_ATTR_CORS);
5975 op_ret = s->bucket->merge_and_store_attrs(this, attrs, s->yield);
5976 if (op_ret < 0) {
5977 ldpp_dout(this, 0) << "RGWLC::RGWDeleteCORS() failed to set attrs on bucket=" << s->bucket->get_name()
5978 << " returned err=" << op_ret << dendl;
5979 }
5980 return op_ret;
5981 });
5982 }
5983
5984 void RGWOptionsCORS::get_response_params(string& hdrs, string& exp_hdrs, unsigned *max_age) {
5985 get_cors_response_headers(this, rule, req_hdrs, hdrs, exp_hdrs, max_age);
5986 }
5987
5988 int RGWOptionsCORS::validate_cors_request(RGWCORSConfiguration *cc) {
5989 rule = cc->host_name_rule(origin);
5990 if (!rule) {
5991 ldpp_dout(this, 10) << "There is no cors rule present for " << origin << dendl;
5992 return -ENOENT;
5993 }
5994
5995 if (!validate_cors_rule_method(this, rule, req_meth)) {
5996 return -ENOENT;
5997 }
5998
5999 if (!validate_cors_rule_header(this, rule, req_hdrs)) {
6000 return -ENOENT;
6001 }
6002
6003 return 0;
6004 }
6005
6006 void RGWOptionsCORS::execute(optional_yield y)
6007 {
6008 op_ret = read_bucket_cors();
6009 if (op_ret < 0)
6010 return;
6011
6012 origin = s->info.env->get("HTTP_ORIGIN");
6013 if (!origin) {
6014 ldpp_dout(this, 0) << "Missing mandatory Origin header" << dendl;
6015 op_ret = -EINVAL;
6016 return;
6017 }
6018 req_meth = s->info.env->get("HTTP_ACCESS_CONTROL_REQUEST_METHOD");
6019 if (!req_meth) {
6020 ldpp_dout(this, 0) << "Missing mandatory Access-control-request-method header" << dendl;
6021 op_ret = -EINVAL;
6022 return;
6023 }
6024 if (!cors_exist) {
6025 ldpp_dout(this, 2) << "No CORS configuration set yet for this bucket" << dendl;
6026 op_ret = -ENOENT;
6027 return;
6028 }
6029 req_hdrs = s->info.env->get("HTTP_ACCESS_CONTROL_REQUEST_HEADERS");
6030 op_ret = validate_cors_request(&bucket_cors);
6031 if (!rule) {
6032 origin = req_meth = NULL;
6033 return;
6034 }
6035 return;
6036 }
6037
6038 int RGWGetRequestPayment::verify_permission(optional_yield y)
6039 {
6040 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
6041 if (has_s3_resource_tag)
6042 rgw_iam_add_buckettags(this, s);
6043
6044 return verify_bucket_owner_or_policy(s, rgw::IAM::s3GetBucketRequestPayment);
6045 }
6046
6047 void RGWGetRequestPayment::pre_exec()
6048 {
6049 rgw_bucket_object_pre_exec(s);
6050 }
6051
6052 void RGWGetRequestPayment::execute(optional_yield y)
6053 {
6054 requester_pays = s->bucket->get_info().requester_pays;
6055 }
6056
6057 int RGWSetRequestPayment::verify_permission(optional_yield y)
6058 {
6059 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
6060 if (has_s3_resource_tag)
6061 rgw_iam_add_buckettags(this, s);
6062
6063 return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketRequestPayment);
6064 }
6065
6066 void RGWSetRequestPayment::pre_exec()
6067 {
6068 rgw_bucket_object_pre_exec(s);
6069 }
6070
6071 void RGWSetRequestPayment::execute(optional_yield y)
6072 {
6073
6074 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, in_data, nullptr, s->info, y);
6075 if (op_ret < 0) {
6076 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
6077 return;
6078 }
6079
6080 op_ret = get_params(y);
6081
6082 if (op_ret < 0)
6083 return;
6084
6085 s->bucket->get_info().requester_pays = requester_pays;
6086 op_ret = s->bucket->put_info(this, false, real_time());
6087 if (op_ret < 0) {
6088 ldpp_dout(this, 0) << "NOTICE: put_bucket_info on bucket=" << s->bucket->get_name()
6089 << " returned err=" << op_ret << dendl;
6090 return;
6091 }
6092 s->bucket_attrs = s->bucket->get_attrs();
6093 }
6094
6095 int RGWInitMultipart::verify_permission(optional_yield y)
6096 {
6097 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
6098 if (has_s3_existing_tag || has_s3_resource_tag)
6099 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
6100
6101 if (s->iam_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
6102 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
6103 rgw::IAM::s3PutObject,
6104 s->object->get_obj());
6105 if (identity_policy_res == Effect::Deny) {
6106 return -EACCES;
6107 }
6108
6109 rgw::IAM::Effect e = Effect::Pass;
6110 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
6111 ARN obj_arn(s->object->get_obj());
6112 if (s->iam_policy) {
6113 e = s->iam_policy->eval(s->env, *s->auth.identity,
6114 rgw::IAM::s3PutObject,
6115 obj_arn,
6116 princ_type);
6117 }
6118 if (e == Effect::Deny) {
6119 return -EACCES;
6120 }
6121
6122 if (!s->session_policies.empty()) {
6123 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
6124 rgw::IAM::s3PutObject,
6125 s->object->get_obj());
6126 if (session_policy_res == Effect::Deny) {
6127 return -EACCES;
6128 }
6129 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
6130 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
6131 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
6132 (session_policy_res == Effect::Allow && e == Effect::Allow)) {
6133 return 0;
6134 }
6135 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
6136 //Intersection of session policy and identity policy plus bucket policy
6137 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) {
6138 return 0;
6139 }
6140 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
6141 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) {
6142 return 0;
6143 }
6144 }
6145 return -EACCES;
6146 }
6147 if (e == Effect::Allow || identity_policy_res == Effect::Allow) {
6148 return 0;
6149 }
6150 }
6151
6152 if (!verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
6153 return -EACCES;
6154 }
6155
6156 return 0;
6157 }
6158
6159 void RGWInitMultipart::pre_exec()
6160 {
6161 rgw_bucket_object_pre_exec(s);
6162 }
6163
6164 void RGWInitMultipart::execute(optional_yield y)
6165 {
6166 bufferlist aclbl, tracebl;
6167 rgw::sal::Attrs attrs;
6168
6169 if (get_params(y) < 0)
6170 return;
6171
6172 if (rgw::sal::Object::empty(s->object.get()))
6173 return;
6174
6175 if (multipart_trace) {
6176 tracing::encode(multipart_trace->GetContext(), tracebl);
6177 attrs[RGW_ATTR_TRACE] = tracebl;
6178 }
6179
6180 policy.encode(aclbl);
6181 attrs[RGW_ATTR_ACL] = aclbl;
6182
6183 populate_with_generic_attrs(s, attrs);
6184
6185 /* select encryption mode */
6186 op_ret = prepare_encryption(attrs);
6187 if (op_ret != 0)
6188 return;
6189
6190 op_ret = rgw_get_request_metadata(this, s->cct, s->info, attrs);
6191 if (op_ret < 0) {
6192 return;
6193 }
6194
6195 std::unique_ptr<rgw::sal::MultipartUpload> upload;
6196 upload = s->bucket->get_multipart_upload(s->object->get_name(),
6197 upload_id);
6198 op_ret = upload->init(this, s->yield, s->obj_ctx, s->owner, s->dest_placement, attrs);
6199
6200 if (op_ret == 0) {
6201 upload_id = upload->get_upload_id();
6202 }
6203 s->trace->SetAttribute(tracing::rgw::UPLOAD_ID, upload_id);
6204 multipart_trace->UpdateName(tracing::rgw::MULTIPART + upload_id);
6205
6206 }
6207
6208 int RGWCompleteMultipart::verify_permission(optional_yield y)
6209 {
6210 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
6211 if (has_s3_existing_tag || has_s3_resource_tag)
6212 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
6213
6214 if (s->iam_policy || ! s->iam_user_policies.empty() || ! s->session_policies.empty()) {
6215 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
6216 rgw::IAM::s3PutObject,
6217 s->object->get_obj());
6218 if (identity_policy_res == Effect::Deny) {
6219 return -EACCES;
6220 }
6221
6222 rgw::IAM::Effect e = Effect::Pass;
6223 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
6224 rgw::ARN obj_arn(s->object->get_obj());
6225 if (s->iam_policy) {
6226 e = s->iam_policy->eval(s->env, *s->auth.identity,
6227 rgw::IAM::s3PutObject,
6228 obj_arn,
6229 princ_type);
6230 }
6231 if (e == Effect::Deny) {
6232 return -EACCES;
6233 }
6234
6235 if (!s->session_policies.empty()) {
6236 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
6237 rgw::IAM::s3PutObject,
6238 s->object->get_obj());
6239 if (session_policy_res == Effect::Deny) {
6240 return -EACCES;
6241 }
6242 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
6243 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
6244 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
6245 (session_policy_res == Effect::Allow && e == Effect::Allow)) {
6246 return 0;
6247 }
6248 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
6249 //Intersection of session policy and identity policy plus bucket policy
6250 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) {
6251 return 0;
6252 }
6253 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
6254 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) {
6255 return 0;
6256 }
6257 }
6258 return -EACCES;
6259 }
6260 if (e == Effect::Allow || identity_policy_res == Effect::Allow) {
6261 return 0;
6262 }
6263 }
6264
6265 if (!verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
6266 return -EACCES;
6267 }
6268
6269 return 0;
6270 }
6271
6272 void RGWCompleteMultipart::pre_exec()
6273 {
6274 rgw_bucket_object_pre_exec(s);
6275 }
6276
6277 void RGWCompleteMultipart::execute(optional_yield y)
6278 {
6279 RGWMultiCompleteUpload *parts;
6280 RGWMultiXMLParser parser;
6281 std::unique_ptr<rgw::sal::MultipartUpload> upload;
6282 off_t ofs = 0;
6283 std::unique_ptr<rgw::sal::Object> meta_obj;
6284 std::unique_ptr<rgw::sal::Object> target_obj;
6285 uint64_t olh_epoch = 0;
6286
6287 op_ret = get_params(y);
6288 if (op_ret < 0)
6289 return;
6290 op_ret = get_system_versioning_params(s, &olh_epoch, &version_id);
6291 if (op_ret < 0) {
6292 return;
6293 }
6294
6295 if (!data.length()) {
6296 op_ret = -ERR_MALFORMED_XML;
6297 return;
6298 }
6299
6300 if (!parser.init()) {
6301 op_ret = -EIO;
6302 return;
6303 }
6304
6305 if (!parser.parse(data.c_str(), data.length(), 1)) {
6306 op_ret = -ERR_MALFORMED_XML;
6307 return;
6308 }
6309
6310 parts = static_cast<RGWMultiCompleteUpload *>(parser.find_first("CompleteMultipartUpload"));
6311 if (!parts || parts->parts.empty()) {
6312 // CompletedMultipartUpload is incorrect but some versions of some libraries use it, see PR #41700
6313 parts = static_cast<RGWMultiCompleteUpload *>(parser.find_first("CompletedMultipartUpload"));
6314 }
6315
6316 if (!parts || parts->parts.empty()) {
6317 op_ret = -ERR_MALFORMED_XML;
6318 return;
6319 }
6320
6321
6322 if ((int)parts->parts.size() >
6323 s->cct->_conf->rgw_multipart_part_upload_limit) {
6324 op_ret = -ERANGE;
6325 return;
6326 }
6327
6328 upload = s->bucket->get_multipart_upload(s->object->get_name(), upload_id);
6329
6330 RGWCompressionInfo cs_info;
6331 bool compressed = false;
6332 uint64_t accounted_size = 0;
6333
6334 list<rgw_obj_index_key> remove_objs; /* objects to be removed from index listing */
6335
6336 meta_obj = upload->get_meta_obj();
6337 meta_obj->set_in_extra_data(true);
6338 meta_obj->set_hash_source(s->object->get_name());
6339
6340 /*take a cls lock on meta_obj to prevent racing completions (or retries)
6341 from deleting the parts*/
6342 int max_lock_secs_mp =
6343 s->cct->_conf.get_val<int64_t>("rgw_mp_lock_max_time");
6344 utime_t dur(max_lock_secs_mp, 0);
6345
6346 serializer = meta_obj->get_serializer(this, "RGWCompleteMultipart");
6347 op_ret = serializer->try_lock(this, dur, y);
6348 if (op_ret < 0) {
6349 ldpp_dout(this, 0) << "failed to acquire lock" << dendl;
6350 if (op_ret == -ENOENT && check_previously_completed(parts)) {
6351 ldpp_dout(this, 1) << "NOTICE: This multipart completion is already completed" << dendl;
6352 op_ret = 0;
6353 return;
6354 }
6355 op_ret = -ERR_INTERNAL_ERROR;
6356 s->err.message = "This multipart completion is already in progress";
6357 return;
6358 }
6359
6360 op_ret = meta_obj->get_obj_attrs(s->obj_ctx, s->yield, this);
6361 if (op_ret < 0) {
6362 ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << meta_obj
6363 << " ret=" << op_ret << dendl;
6364 return;
6365 }
6366 s->trace->SetAttribute(tracing::rgw::UPLOAD_ID, upload_id);
6367 jspan_context trace_ctx(false, false);
6368 extract_span_context(meta_obj->get_attrs(), trace_ctx);
6369 multipart_trace = tracing::rgw::tracer.add_span(name(), trace_ctx);
6370
6371
6372 // make reservation for notification if needed
6373 std::unique_ptr<rgw::sal::Notification> res
6374 = store->get_notification(meta_obj.get(), nullptr, s, rgw::notify::ObjectCreatedCompleteMultipartUpload, &s->object->get_name());
6375 op_ret = res->publish_reserve(this);
6376 if (op_ret < 0) {
6377 return;
6378 }
6379
6380 target_obj = s->bucket->get_object(rgw_obj_key(s->object->get_name()));
6381 if (s->bucket->versioning_enabled()) {
6382 if (!version_id.empty()) {
6383 target_obj->set_instance(version_id);
6384 } else {
6385 target_obj->gen_rand_obj_instance_name();
6386 version_id = target_obj->get_instance();
6387 }
6388 }
6389 target_obj->set_attrs(meta_obj->get_attrs());
6390
6391 op_ret = upload->complete(this, y, s->cct, parts->parts, remove_objs, accounted_size, compressed, cs_info, ofs, s->req_id, s->owner, olh_epoch, target_obj.get(), s->obj_ctx);
6392 if (op_ret < 0) {
6393 ldpp_dout(this, 0) << "ERROR: upload complete failed ret=" << op_ret << dendl;
6394 return;
6395 }
6396
6397 // remove the upload meta object ; the meta object is not versioned
6398 // when the bucket is, as that would add an unneeded delete marker
6399 int r = meta_obj->delete_object(this, s->obj_ctx, y, true /* prevent versioning */);
6400 if (r >= 0) {
6401 /* serializer's exclusive lock is released */
6402 serializer->clear_locked();
6403 } else {
6404 ldpp_dout(this, 0) << "WARNING: failed to remove object " << meta_obj << dendl;
6405 }
6406
6407 // send request to notification manager
6408 int ret = res->publish_commit(this, ofs, target_obj->get_mtime(), etag, target_obj->get_instance());
6409 if (ret < 0) {
6410 ldpp_dout(this, 1) << "ERROR: publishing notification failed, with error: " << ret << dendl;
6411 // too late to rollback operation, hence op_ret is not set here
6412 }
6413 } // RGWCompleteMultipart::execute
6414
6415 bool RGWCompleteMultipart::check_previously_completed(const RGWMultiCompleteUpload* parts)
6416 {
6417 // re-calculate the etag from the parts and compare to the existing object
6418 int ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, this);
6419 if (ret < 0) {
6420 ldpp_dout(this, 0) << __func__ << "() ERROR: get_obj_attrs() returned ret=" << ret << dendl;
6421 return false;
6422 }
6423 rgw::sal::Attrs sattrs = s->object->get_attrs();
6424 string oetag = sattrs[RGW_ATTR_ETAG].to_str();
6425
6426 MD5 hash;
6427 // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
6428 hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
6429 for (const auto& [index, part] : parts->parts) {
6430 std::string partetag = rgw_string_unquote(part);
6431 char petag[CEPH_CRYPTO_MD5_DIGESTSIZE];
6432 hex_to_buf(partetag.c_str(), petag, CEPH_CRYPTO_MD5_DIGESTSIZE);
6433 hash.Update((const unsigned char *)petag, sizeof(petag));
6434 ldpp_dout(this, 20) << __func__ << "() re-calculating multipart etag: part: "
6435 << index << ", etag: " << partetag << dendl;
6436 }
6437
6438 unsigned char final_etag[CEPH_CRYPTO_MD5_DIGESTSIZE];
6439 char final_etag_str[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 16];
6440 hash.Final(final_etag);
6441 buf_to_hex(final_etag, CEPH_CRYPTO_MD5_DIGESTSIZE, final_etag_str);
6442 snprintf(&final_etag_str[CEPH_CRYPTO_MD5_DIGESTSIZE * 2], sizeof(final_etag_str) - CEPH_CRYPTO_MD5_DIGESTSIZE * 2,
6443 "-%lld", (long long)parts->parts.size());
6444
6445 if (oetag.compare(final_etag_str) != 0) {
6446 ldpp_dout(this, 1) << __func__ << "() NOTICE: etag mismatch: object etag:"
6447 << oetag << ", re-calculated etag:" << final_etag_str << dendl;
6448 return false;
6449 }
6450 ldpp_dout(this, 5) << __func__ << "() object etag and re-calculated etag match, etag: " << oetag << dendl;
6451 return true;
6452 }
6453
6454 void RGWCompleteMultipart::complete()
6455 {
6456 /* release exclusive lock iff not already */
6457 if (unlikely(serializer && serializer->locked)) {
6458 int r = serializer->unlock();
6459 if (r < 0) {
6460 ldpp_dout(this, 0) << "WARNING: failed to unlock " << serializer->oid << dendl;
6461 }
6462 }
6463 send_response();
6464 }
6465
6466 int RGWAbortMultipart::verify_permission(optional_yield y)
6467 {
6468 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
6469 if (has_s3_existing_tag || has_s3_resource_tag)
6470 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
6471
6472 if (s->iam_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
6473 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
6474 rgw::IAM::s3AbortMultipartUpload,
6475 s->object->get_obj());
6476 if (identity_policy_res == Effect::Deny) {
6477 return -EACCES;
6478 }
6479
6480 rgw::IAM::Effect e = Effect::Pass;
6481 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
6482 ARN obj_arn(s->object->get_obj());
6483 if (s->iam_policy) {
6484 e = s->iam_policy->eval(s->env, *s->auth.identity,
6485 rgw::IAM::s3AbortMultipartUpload,
6486 obj_arn, princ_type);
6487 }
6488
6489 if (e == Effect::Deny) {
6490 return -EACCES;
6491 }
6492
6493 if (!s->session_policies.empty()) {
6494 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
6495 rgw::IAM::s3PutObject,
6496 s->object->get_obj());
6497 if (session_policy_res == Effect::Deny) {
6498 return -EACCES;
6499 }
6500 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
6501 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
6502 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
6503 (session_policy_res == Effect::Allow && e == Effect::Allow)) {
6504 return 0;
6505 }
6506 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
6507 //Intersection of session policy and identity policy plus bucket policy
6508 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) {
6509 return 0;
6510 }
6511 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
6512 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) {
6513 return 0;
6514 }
6515 }
6516 return -EACCES;
6517 }
6518 if (e == Effect::Allow || identity_policy_res == Effect::Allow) {
6519 return 0;
6520 }
6521 }
6522
6523 if (!verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
6524 return -EACCES;
6525 }
6526
6527 return 0;
6528 }
6529
6530 void RGWAbortMultipart::pre_exec()
6531 {
6532 rgw_bucket_object_pre_exec(s);
6533 }
6534
6535 void RGWAbortMultipart::execute(optional_yield y)
6536 {
6537 op_ret = -EINVAL;
6538 string upload_id;
6539 upload_id = s->info.args.get("uploadId");
6540 std::unique_ptr<rgw::sal::Object> meta_obj;
6541 std::unique_ptr<rgw::sal::MultipartUpload> upload;
6542
6543 if (upload_id.empty() || rgw::sal::Object::empty(s->object.get()))
6544 return;
6545
6546 upload = s->bucket->get_multipart_upload(s->object->get_name(), upload_id);
6547 RGWObjectCtx *obj_ctx = static_cast<RGWObjectCtx *>(s->obj_ctx);
6548
6549 jspan_context trace_ctx(false, false);
6550 if (tracing::rgw::tracer.is_enabled()) {
6551 // read meta object attributes for trace info
6552 meta_obj = upload->get_meta_obj();
6553 meta_obj->set_in_extra_data(true);
6554 meta_obj->get_obj_attrs(obj_ctx, s->yield, this);
6555 extract_span_context(meta_obj->get_attrs(), trace_ctx);
6556 }
6557 multipart_trace = tracing::rgw::tracer.add_span(name(), trace_ctx);
6558
6559 op_ret = upload->abort(this, s->cct, obj_ctx);
6560 }
6561
6562 int RGWListMultipart::verify_permission(optional_yield y)
6563 {
6564 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
6565 if (has_s3_existing_tag || has_s3_resource_tag)
6566 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
6567
6568 if (!verify_object_permission(this, s, rgw::IAM::s3ListMultipartUploadParts))
6569 return -EACCES;
6570
6571 return 0;
6572 }
6573
6574 void RGWListMultipart::pre_exec()
6575 {
6576 rgw_bucket_object_pre_exec(s);
6577 }
6578
6579 void RGWListMultipart::execute(optional_yield y)
6580 {
6581 op_ret = get_params(y);
6582 if (op_ret < 0)
6583 return;
6584
6585 upload = s->bucket->get_multipart_upload(s->object->get_name(), upload_id);
6586
6587 rgw::sal::Attrs attrs;
6588 op_ret = upload->get_info(this, s->yield, s->obj_ctx, &placement, &attrs);
6589 /* decode policy */
6590 map<string, bufferlist>::iterator iter = attrs.find(RGW_ATTR_ACL);
6591 if (iter != attrs.end()) {
6592 auto bliter = iter->second.cbegin();
6593 try {
6594 policy.decode(bliter);
6595 } catch (buffer::error& err) {
6596 ldpp_dout(this, 0) << "ERROR: could not decode policy, caught buffer::error" << dendl;
6597 op_ret = -EIO;
6598 }
6599 }
6600 if (op_ret < 0)
6601 return;
6602
6603 op_ret = upload->list_parts(this, s->cct, max_parts, marker, NULL, &truncated);
6604 }
6605
6606 int RGWListBucketMultiparts::verify_permission(optional_yield y)
6607 {
6608 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
6609 if (has_s3_resource_tag)
6610 rgw_iam_add_buckettags(this, s);
6611
6612 if (!verify_bucket_permission(this,
6613 s,
6614 rgw::IAM::s3ListBucketMultipartUploads))
6615 return -EACCES;
6616
6617 return 0;
6618 }
6619
6620 void RGWListBucketMultiparts::pre_exec()
6621 {
6622 rgw_bucket_object_pre_exec(s);
6623 }
6624
6625 void RGWListBucketMultiparts::execute(optional_yield y)
6626 {
6627 op_ret = get_params(y);
6628 if (op_ret < 0)
6629 return;
6630
6631 if (s->prot_flags & RGW_REST_SWIFT) {
6632 string path_args;
6633 path_args = s->info.args.get("path");
6634 if (!path_args.empty()) {
6635 if (!delimiter.empty() || !prefix.empty()) {
6636 op_ret = -EINVAL;
6637 return;
6638 }
6639 prefix = path_args;
6640 delimiter="/";
6641 }
6642 }
6643
6644 op_ret = s->bucket->list_multiparts(this, prefix, marker_meta,
6645 delimiter, max_uploads, uploads,
6646 &common_prefixes, &is_truncated);
6647 if (op_ret < 0) {
6648 return;
6649 }
6650
6651 if (!uploads.empty()) {
6652 next_marker_key = uploads.back()->get_key();
6653 next_marker_upload_id = uploads.back()->get_upload_id();
6654 }
6655 }
6656
6657 void RGWGetHealthCheck::execute(optional_yield y)
6658 {
6659 if (!g_conf()->rgw_healthcheck_disabling_path.empty() &&
6660 (::access(g_conf()->rgw_healthcheck_disabling_path.c_str(), F_OK) == 0)) {
6661 /* Disabling path specified & existent in the filesystem. */
6662 op_ret = -ERR_SERVICE_UNAVAILABLE; /* 503 */
6663 } else {
6664 op_ret = 0; /* 200 OK */
6665 }
6666 }
6667
6668 int RGWDeleteMultiObj::verify_permission(optional_yield y)
6669 {
6670 int op_ret = get_params(y);
6671 if (op_ret) {
6672 return op_ret;
6673 }
6674
6675 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
6676 if (has_s3_existing_tag || has_s3_resource_tag)
6677 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
6678
6679 if (s->iam_policy || ! s->iam_user_policies.empty() || ! s->session_policies.empty()) {
6680 if (s->bucket->get_info().obj_lock_enabled() && bypass_governance_mode) {
6681 ARN bucket_arn(s->bucket->get_key());
6682 auto r = eval_identity_or_session_policies(s->iam_user_policies, s->env,
6683 rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket->get_key()));
6684 if (r == Effect::Deny) {
6685 bypass_perm = false;
6686 } else if (r == Effect::Pass && s->iam_policy) {
6687 r = s->iam_policy->eval(s->env, *s->auth.identity, rgw::IAM::s3BypassGovernanceRetention,
6688 bucket_arn);
6689 if (r == Effect::Deny) {
6690 bypass_perm = false;
6691 }
6692 } else if (r == Effect::Pass && !s->session_policies.empty()) {
6693 r = eval_identity_or_session_policies(s->session_policies, s->env,
6694 rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket->get_key()));
6695 if (r == Effect::Deny) {
6696 bypass_perm = false;
6697 }
6698 }
6699 }
6700
6701 bool not_versioned = rgw::sal::Object::empty(s->object.get()) || s->object->get_instance().empty();
6702
6703 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
6704 not_versioned ?
6705 rgw::IAM::s3DeleteObject :
6706 rgw::IAM::s3DeleteObjectVersion,
6707 ARN(s->bucket->get_key()));
6708 if (identity_policy_res == Effect::Deny) {
6709 return -EACCES;
6710 }
6711
6712 rgw::IAM::Effect r = Effect::Pass;
6713 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
6714 rgw::ARN bucket_arn(s->bucket->get_key());
6715 if (s->iam_policy) {
6716 r = s->iam_policy->eval(s->env, *s->auth.identity,
6717 not_versioned ?
6718 rgw::IAM::s3DeleteObject :
6719 rgw::IAM::s3DeleteObjectVersion,
6720 bucket_arn,
6721 princ_type);
6722 }
6723 if (r == Effect::Deny)
6724 return -EACCES;
6725
6726 if (!s->session_policies.empty()) {
6727 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
6728 not_versioned ?
6729 rgw::IAM::s3DeleteObject :
6730 rgw::IAM::s3DeleteObjectVersion,
6731 ARN(s->bucket->get_key()));
6732 if (session_policy_res == Effect::Deny) {
6733 return -EACCES;
6734 }
6735 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
6736 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
6737 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
6738 (session_policy_res == Effect::Allow && r == Effect::Allow)) {
6739 return 0;
6740 }
6741 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
6742 //Intersection of session policy and identity policy plus bucket policy
6743 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || r == Effect::Allow) {
6744 return 0;
6745 }
6746 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
6747 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) {
6748 return 0;
6749 }
6750 }
6751 return -EACCES;
6752 }
6753 if (r == Effect::Allow || identity_policy_res == Effect::Allow)
6754 return 0;
6755 }
6756
6757 acl_allowed = verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE);
6758 if (!acl_allowed)
6759 return -EACCES;
6760
6761 return 0;
6762 }
6763
6764 void RGWDeleteMultiObj::pre_exec()
6765 {
6766 rgw_bucket_object_pre_exec(s);
6767 }
6768
6769 void RGWDeleteMultiObj::execute(optional_yield y)
6770 {
6771 RGWMultiDelDelete *multi_delete;
6772 vector<rgw_obj_key>::iterator iter;
6773 RGWMultiDelXMLParser parser;
6774 RGWObjectCtx *obj_ctx = static_cast<RGWObjectCtx *>(s->obj_ctx);
6775 char* buf;
6776
6777 buf = data.c_str();
6778 if (!buf) {
6779 op_ret = -EINVAL;
6780 goto error;
6781 }
6782
6783 if (!parser.init()) {
6784 op_ret = -EINVAL;
6785 goto error;
6786 }
6787
6788 if (!parser.parse(buf, data.length(), 1)) {
6789 op_ret = -EINVAL;
6790 goto error;
6791 }
6792
6793 multi_delete = static_cast<RGWMultiDelDelete *>(parser.find_first("Delete"));
6794 if (!multi_delete) {
6795 op_ret = -EINVAL;
6796 goto error;
6797 } else {
6798 #define DELETE_MULTI_OBJ_MAX_NUM 1000
6799 int max_num = s->cct->_conf->rgw_delete_multi_obj_max_num;
6800 if (max_num < 0) {
6801 max_num = DELETE_MULTI_OBJ_MAX_NUM;
6802 }
6803 int multi_delete_object_num = multi_delete->objects.size();
6804 if (multi_delete_object_num > max_num) {
6805 op_ret = -ERR_MALFORMED_XML;
6806 goto error;
6807 }
6808 }
6809
6810 if (multi_delete->is_quiet())
6811 quiet = true;
6812
6813 if (s->bucket->get_info().mfa_enabled()) {
6814 bool has_versioned = false;
6815 for (auto i : multi_delete->objects) {
6816 if (!i.instance.empty()) {
6817 has_versioned = true;
6818 break;
6819 }
6820 }
6821 if (has_versioned && !s->mfa_verified) {
6822 ldpp_dout(this, 5) << "NOTICE: multi-object delete request with a versioned object, mfa auth not provided" << dendl;
6823 op_ret = -ERR_MFA_REQUIRED;
6824 goto error;
6825 }
6826 }
6827
6828 begin_response();
6829 if (multi_delete->objects.empty()) {
6830 goto done;
6831 }
6832
6833 for (iter = multi_delete->objects.begin();
6834 iter != multi_delete->objects.end();
6835 ++iter) {
6836 std::string version_id;
6837 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(*iter);
6838 if (s->iam_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
6839 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
6840 iter->instance.empty() ?
6841 rgw::IAM::s3DeleteObject :
6842 rgw::IAM::s3DeleteObjectVersion,
6843 ARN(obj->get_obj()));
6844 if (identity_policy_res == Effect::Deny) {
6845 send_partial_response(*iter, false, "", -EACCES);
6846 continue;
6847 }
6848
6849 rgw::IAM::Effect e = Effect::Pass;
6850 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
6851 if (s->iam_policy) {
6852 ARN obj_arn(obj->get_obj());
6853 e = s->iam_policy->eval(s->env,
6854 *s->auth.identity,
6855 iter->instance.empty() ?
6856 rgw::IAM::s3DeleteObject :
6857 rgw::IAM::s3DeleteObjectVersion,
6858 obj_arn,
6859 princ_type);
6860 }
6861 if (e == Effect::Deny) {
6862 send_partial_response(*iter, false, "", -EACCES);
6863 continue;
6864 }
6865
6866 if (!s->session_policies.empty()) {
6867 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
6868 iter->instance.empty() ?
6869 rgw::IAM::s3DeleteObject :
6870 rgw::IAM::s3DeleteObjectVersion,
6871 ARN(obj->get_obj()));
6872 if (session_policy_res == Effect::Deny) {
6873 send_partial_response(*iter, false, "", -EACCES);
6874 continue;
6875 }
6876 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
6877 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
6878 if ((session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) &&
6879 (session_policy_res != Effect::Allow || e != Effect::Allow)) {
6880 send_partial_response(*iter, false, "", -EACCES);
6881 continue;
6882 }
6883 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
6884 //Intersection of session policy and identity policy plus bucket policy
6885 if ((session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) && e != Effect::Allow) {
6886 send_partial_response(*iter, false, "", -EACCES);
6887 continue;
6888 }
6889 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
6890 if (session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) {
6891 send_partial_response(*iter, false, "", -EACCES);
6892 continue;
6893 }
6894 }
6895 send_partial_response(*iter, false, "", -EACCES);
6896 continue;
6897 }
6898
6899 if ((identity_policy_res == Effect::Pass && e == Effect::Pass && !acl_allowed)) {
6900 send_partial_response(*iter, false, "", -EACCES);
6901 continue;
6902 }
6903 }
6904
6905 uint64_t obj_size = 0;
6906 std::string etag;
6907
6908 if (!rgw::sal::Object::empty(obj.get())) {
6909 RGWObjState* astate = nullptr;
6910 bool check_obj_lock = obj->have_instance() && bucket->get_info().obj_lock_enabled();
6911 const auto ret = obj->get_obj_state(this, obj_ctx, &astate, s->yield, true);
6912
6913 if (ret < 0) {
6914 if (ret == -ENOENT) {
6915 // object maybe delete_marker, skip check_obj_lock
6916 check_obj_lock = false;
6917 } else {
6918 // Something went wrong.
6919 send_partial_response(*iter, false, "", ret);
6920 continue;
6921 }
6922 } else {
6923 obj_size = astate->size;
6924 etag = astate->attrset[RGW_ATTR_ETAG].to_str();
6925 }
6926
6927 if (check_obj_lock) {
6928 ceph_assert(astate);
6929 int object_lock_response = verify_object_lock(this, astate->attrset, bypass_perm, bypass_governance_mode);
6930 if (object_lock_response != 0) {
6931 send_partial_response(*iter, false, "", object_lock_response);
6932 continue;
6933 }
6934 }
6935 }
6936
6937 // make reservation for notification if needed
6938 const auto versioned_object = s->bucket->versioning_enabled();
6939 const auto event_type = versioned_object && obj->get_instance().empty() ?
6940 rgw::notify::ObjectRemovedDeleteMarkerCreated :
6941 rgw::notify::ObjectRemovedDelete;
6942 std::unique_ptr<rgw::sal::Notification> res
6943 = store->get_notification(obj.get(), s->src_object.get(), s, event_type);
6944 op_ret = res->publish_reserve(this);
6945 if (op_ret < 0) {
6946 send_partial_response(*iter, false, "", op_ret);
6947 continue;
6948 }
6949
6950 obj->set_atomic(obj_ctx);
6951
6952 std::unique_ptr<rgw::sal::Object::DeleteOp> del_op = obj->get_delete_op(obj_ctx);
6953 del_op->params.versioning_status = obj->get_bucket()->get_info().versioning_status();
6954 del_op->params.obj_owner = s->owner;
6955 del_op->params.bucket_owner = s->bucket_owner;
6956 del_op->params.marker_version_id = version_id;
6957
6958 op_ret = del_op->delete_obj(this, y);
6959 if (op_ret == -ENOENT) {
6960 op_ret = 0;
6961 }
6962
6963 send_partial_response(*iter, obj->get_delete_marker(), del_op->result.version_id, op_ret);
6964
6965 // send request to notification manager
6966 int ret = res->publish_commit(this, obj_size, ceph::real_clock::now(), etag, version_id);
6967 if (ret < 0) {
6968 ldpp_dout(this, 1) << "ERROR: publishing notification failed, with error: " << ret << dendl;
6969 // too late to rollback operation, hence op_ret is not set here
6970 }
6971 }
6972
6973 /* set the return code to zero, errors at this point will be
6974 dumped to the response */
6975 op_ret = 0;
6976
6977 done:
6978 // will likely segfault if begin_response() has not been called
6979 end_response();
6980 return;
6981
6982 error:
6983 send_status();
6984 return;
6985
6986 }
6987
6988 bool RGWBulkDelete::Deleter::verify_permission(RGWBucketInfo& binfo,
6989 map<string, bufferlist>& battrs,
6990 ACLOwner& bucket_owner /* out */,
6991 optional_yield y)
6992 {
6993 RGWAccessControlPolicy bacl(store->ctx());
6994 int ret = read_bucket_policy(dpp, store, s, binfo, battrs, &bacl, binfo.bucket, y);
6995 if (ret < 0) {
6996 return false;
6997 }
6998
6999 auto policy = get_iam_policy_from_attr(s->cct, battrs, binfo.bucket.tenant);
7000
7001 bucket_owner = bacl.get_owner();
7002
7003 /* We can use global user_acl because each BulkDelete request is allowed
7004 * to work on entities from a single account only. */
7005 return verify_bucket_permission(dpp, s, binfo.bucket, s->user_acl.get(),
7006 &bacl, policy, s->iam_user_policies, s->session_policies, rgw::IAM::s3DeleteBucket);
7007 }
7008
7009 bool RGWBulkDelete::Deleter::delete_single(const acct_path_t& path, optional_yield y)
7010 {
7011 std::unique_ptr<rgw::sal::Bucket> bucket;
7012 ACLOwner bowner;
7013 RGWObjVersionTracker ot;
7014
7015 int ret = store->get_bucket(dpp, s->user.get(), s->user->get_tenant(), path.bucket_name, &bucket, y);
7016 if (ret < 0) {
7017 goto binfo_fail;
7018 }
7019
7020 ret = bucket->load_bucket(dpp, s->yield);
7021 if (ret < 0) {
7022 goto binfo_fail;
7023 }
7024
7025 if (!verify_permission(bucket->get_info(), bucket->get_attrs(), bowner, y)) {
7026 ret = -EACCES;
7027 goto auth_fail;
7028 }
7029
7030 if (!path.obj_key.empty()) {
7031 ACLOwner bucket_owner;
7032
7033 bucket_owner.set_id(bucket->get_info().owner);
7034 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(path.obj_key);
7035 obj->set_atomic(s->obj_ctx);
7036
7037 std::unique_ptr<rgw::sal::Object::DeleteOp> del_op = obj->get_delete_op(s->obj_ctx);
7038 del_op->params.versioning_status = obj->get_bucket()->get_info().versioning_status();
7039 del_op->params.obj_owner = bowner;
7040 del_op->params.bucket_owner = bucket_owner;
7041
7042 ret = del_op->delete_obj(dpp, y);
7043 if (ret < 0) {
7044 goto delop_fail;
7045 }
7046 } else {
7047 ret = bucket->remove_bucket(dpp, false, true, &s->info, s->yield);
7048 if (ret < 0) {
7049 goto delop_fail;
7050 }
7051 }
7052
7053 num_deleted++;
7054 return true;
7055
7056 binfo_fail:
7057 if (-ENOENT == ret) {
7058 ldpp_dout(dpp, 20) << "cannot find bucket = " << path.bucket_name << dendl;
7059 num_unfound++;
7060 } else {
7061 ldpp_dout(dpp, 20) << "cannot get bucket info, ret = " << ret << dendl;
7062
7063 fail_desc_t failed_item = {
7064 .err = ret,
7065 .path = path
7066 };
7067 failures.push_back(failed_item);
7068 }
7069 return false;
7070
7071 auth_fail:
7072 ldpp_dout(dpp, 20) << "wrong auth for " << path << dendl;
7073 {
7074 fail_desc_t failed_item = {
7075 .err = ret,
7076 .path = path
7077 };
7078 failures.push_back(failed_item);
7079 }
7080 return false;
7081
7082 delop_fail:
7083 if (-ENOENT == ret) {
7084 ldpp_dout(dpp, 20) << "cannot find entry " << path << dendl;
7085 num_unfound++;
7086 } else {
7087 fail_desc_t failed_item = {
7088 .err = ret,
7089 .path = path
7090 };
7091 failures.push_back(failed_item);
7092 }
7093 return false;
7094 }
7095
7096 bool RGWBulkDelete::Deleter::delete_chunk(const std::list<acct_path_t>& paths, optional_yield y)
7097 {
7098 ldpp_dout(dpp, 20) << "in delete_chunk" << dendl;
7099 for (auto path : paths) {
7100 ldpp_dout(dpp, 20) << "bulk deleting path: " << path << dendl;
7101 delete_single(path, y);
7102 }
7103
7104 return true;
7105 }
7106
7107 int RGWBulkDelete::verify_permission(optional_yield y)
7108 {
7109 return 0;
7110 }
7111
7112 void RGWBulkDelete::pre_exec()
7113 {
7114 rgw_bucket_object_pre_exec(s);
7115 }
7116
7117 void RGWBulkDelete::execute(optional_yield y)
7118 {
7119 deleter = std::unique_ptr<Deleter>(new Deleter(this, store, s));
7120
7121 bool is_truncated = false;
7122 do {
7123 list<RGWBulkDelete::acct_path_t> items;
7124
7125 int ret = get_data(items, &is_truncated);
7126 if (ret < 0) {
7127 return;
7128 }
7129
7130 ret = deleter->delete_chunk(items, y);
7131 } while (!op_ret && is_truncated);
7132
7133 return;
7134 }
7135
7136
7137 constexpr std::array<int, 2> RGWBulkUploadOp::terminal_errors;
7138
7139 int RGWBulkUploadOp::verify_permission(optional_yield y)
7140 {
7141 if (s->auth.identity->is_anonymous()) {
7142 return -EACCES;
7143 }
7144
7145 if (! verify_user_permission_no_policy(this, s, RGW_PERM_WRITE)) {
7146 return -EACCES;
7147 }
7148
7149 if (s->user->get_tenant() != s->bucket_tenant) {
7150 ldpp_dout(this, 10) << "user cannot create a bucket in a different tenant"
7151 << " (user_id.tenant=" << s->user->get_tenant()
7152 << " requested=" << s->bucket_tenant << ")" << dendl;
7153 return -EACCES;
7154 }
7155
7156 if (s->user->get_max_buckets() < 0) {
7157 return -EPERM;
7158 }
7159
7160 return 0;
7161 }
7162
7163 void RGWBulkUploadOp::pre_exec()
7164 {
7165 rgw_bucket_object_pre_exec(s);
7166 }
7167
7168 boost::optional<std::pair<std::string, rgw_obj_key>>
7169 RGWBulkUploadOp::parse_path(const std::string_view& path)
7170 {
7171 /* We need to skip all slashes at the beginning in order to preserve
7172 * compliance with Swift. */
7173 const size_t start_pos = path.find_first_not_of('/');
7174
7175 if (std::string_view::npos != start_pos) {
7176 /* Seperator is the first slash after the leading ones. */
7177 const size_t sep_pos = path.substr(start_pos).find('/');
7178
7179 if (std::string_view::npos != sep_pos) {
7180 const auto bucket_name = path.substr(start_pos, sep_pos - start_pos);
7181 const auto obj_name = path.substr(sep_pos + 1);
7182
7183 return std::make_pair(std::string(bucket_name),
7184 rgw_obj_key(std::string(obj_name)));
7185 } else {
7186 /* It's guaranteed here that bucket name is at least one character
7187 * long and is different than slash. */
7188 return std::make_pair(std::string(path.substr(start_pos)),
7189 rgw_obj_key());
7190 }
7191 }
7192
7193 return none;
7194 }
7195
7196 std::pair<std::string, std::string>
7197 RGWBulkUploadOp::handle_upload_path(struct req_state *s)
7198 {
7199 std::string bucket_path, file_prefix;
7200 if (! s->init_state.url_bucket.empty()) {
7201 file_prefix = bucket_path = s->init_state.url_bucket + "/";
7202 if (!rgw::sal::Object::empty(s->object.get())) {
7203 const std::string& object_name = s->object->get_name();
7204
7205 /* As rgw_obj_key::empty() already verified emptiness of s->object->get_name(),
7206 * we can safely examine its last element. */
7207 if (object_name.back() == '/') {
7208 file_prefix.append(object_name);
7209 } else {
7210 file_prefix.append(object_name).append("/");
7211 }
7212 }
7213 }
7214 return std::make_pair(bucket_path, file_prefix);
7215 }
7216
7217 int RGWBulkUploadOp::handle_dir_verify_permission(optional_yield y)
7218 {
7219 if (s->user->get_max_buckets() > 0) {
7220 rgw::sal::BucketList buckets;
7221 std::string marker;
7222 op_ret = s->user->list_buckets(this, marker, std::string(), s->user->get_max_buckets(),
7223 false, buckets, y);
7224 if (op_ret < 0) {
7225 return op_ret;
7226 }
7227
7228 if (buckets.count() >= static_cast<size_t>(s->user->get_max_buckets())) {
7229 return -ERR_TOO_MANY_BUCKETS;
7230 }
7231 }
7232
7233 return 0;
7234 }
7235
7236 static void forward_req_info(const DoutPrefixProvider *dpp, CephContext *cct, req_info& info, const std::string& bucket_name)
7237 {
7238 /* the request of container or object level will contain bucket name.
7239 * only at account level need to append the bucket name */
7240 if (info.script_uri.find(bucket_name) != std::string::npos) {
7241 return;
7242 }
7243
7244 ldpp_dout(dpp, 20) << "append the bucket: "<< bucket_name << " to req_info" << dendl;
7245 info.script_uri.append("/").append(bucket_name);
7246 info.request_uri_aws4 = info.request_uri = info.script_uri;
7247 info.effective_uri = "/" + bucket_name;
7248 }
7249
7250 void RGWBulkUploadOp::init(rgw::sal::Store* const store,
7251 struct req_state* const s,
7252 RGWHandler* const h)
7253 {
7254 RGWOp::init(store, s, h);
7255 }
7256
7257 int RGWBulkUploadOp::handle_dir(const std::string_view path, optional_yield y)
7258 {
7259 ldpp_dout(this, 20) << "got directory=" << path << dendl;
7260
7261 op_ret = handle_dir_verify_permission(y);
7262 if (op_ret < 0) {
7263 return op_ret;
7264 }
7265
7266 std::string bucket_name;
7267 rgw_obj_key object_junk;
7268 std::tie(bucket_name, object_junk) = *parse_path(path);
7269
7270 rgw_raw_obj obj(store->get_zone()->get_params().domain_root,
7271 rgw_make_bucket_entry_name(s->bucket_tenant, bucket_name));
7272
7273 /* we need to make sure we read bucket info, it's not read before for this
7274 * specific request */
7275 std::unique_ptr<rgw::sal::Bucket> bucket;
7276
7277 /* Create metadata: ACLs. */
7278 std::map<std::string, ceph::bufferlist> attrs;
7279 RGWAccessControlPolicy policy;
7280 policy.create_default(s->user->get_id(), s->user->get_display_name());
7281 ceph::bufferlist aclbl;
7282 policy.encode(aclbl);
7283 attrs.emplace(RGW_ATTR_ACL, std::move(aclbl));
7284
7285 obj_version objv, ep_objv;
7286 bool bucket_exists;
7287 RGWQuotaInfo quota_info;
7288 const RGWQuotaInfo* pquota_info = nullptr;
7289 RGWBucketInfo out_info;
7290 string swift_ver_location;
7291 rgw_bucket new_bucket;
7292 req_info info = s->info;
7293 new_bucket.tenant = s->bucket_tenant; /* ignored if bucket exists */
7294 new_bucket.name = bucket_name;
7295 rgw_placement_rule placement_rule;
7296 placement_rule.storage_class = s->info.storage_class;
7297 forward_req_info(this, s->cct, info, bucket_name);
7298
7299 op_ret = s->user->create_bucket(this, new_bucket,
7300 store->get_zone()->get_zonegroup().get_id(),
7301 placement_rule, swift_ver_location,
7302 pquota_info, policy, attrs,
7303 out_info, ep_objv,
7304 true, false, &bucket_exists,
7305 info, &bucket, y);
7306 /* continue if EEXIST and create_bucket will fail below. this way we can
7307 * recover from a partial create by retrying it. */
7308 ldpp_dout(this, 20) << "rgw_create_bucket returned ret=" << op_ret
7309 << ", bucket=" << bucket << dendl;
7310
7311 return op_ret;
7312 }
7313
7314
7315 bool RGWBulkUploadOp::handle_file_verify_permission(RGWBucketInfo& binfo,
7316 const rgw_obj& obj,
7317 std::map<std::string, ceph::bufferlist>& battrs,
7318 ACLOwner& bucket_owner /* out */,
7319 optional_yield y)
7320 {
7321 RGWAccessControlPolicy bacl(store->ctx());
7322 op_ret = read_bucket_policy(this, store, s, binfo, battrs, &bacl, binfo.bucket, y);
7323 if (op_ret < 0) {
7324 ldpp_dout(this, 20) << "cannot read_policy() for bucket" << dendl;
7325 return false;
7326 }
7327
7328 auto policy = get_iam_policy_from_attr(s->cct, battrs, binfo.bucket.tenant);
7329
7330 bucket_owner = bacl.get_owner();
7331 if (policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
7332 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
7333 rgw::IAM::s3PutObject, obj);
7334 if (identity_policy_res == Effect::Deny) {
7335 return false;
7336 }
7337
7338 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
7339 ARN obj_arn(obj);
7340 auto e = policy->eval(s->env, *s->auth.identity,
7341 rgw::IAM::s3PutObject, obj_arn, princ_type);
7342 if (e == Effect::Deny) {
7343 return false;
7344 }
7345
7346 if (!s->session_policies.empty()) {
7347 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
7348 rgw::IAM::s3PutObject, obj);
7349 if (session_policy_res == Effect::Deny) {
7350 return false;
7351 }
7352 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
7353 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
7354 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
7355 (session_policy_res == Effect::Allow && e == Effect::Allow)) {
7356 return true;
7357 }
7358 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
7359 //Intersection of session policy and identity policy plus bucket policy
7360 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) {
7361 return true;
7362 }
7363 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
7364 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) {
7365 return true;
7366 }
7367 }
7368 return false;
7369 }
7370 if (e == Effect::Allow || identity_policy_res == Effect::Allow) {
7371 return true;
7372 }
7373 }
7374
7375 return verify_bucket_permission_no_policy(this, s, s->user_acl.get(),
7376 &bacl, RGW_PERM_WRITE);
7377 }
7378
7379 int RGWBulkUploadOp::handle_file(const std::string_view path,
7380 const size_t size,
7381 AlignedStreamGetter& body, optional_yield y)
7382 {
7383
7384 ldpp_dout(this, 20) << "got file=" << path << ", size=" << size << dendl;
7385
7386 if (size > static_cast<size_t>(s->cct->_conf->rgw_max_put_size)) {
7387 op_ret = -ERR_TOO_LARGE;
7388 return op_ret;
7389 }
7390
7391 std::string bucket_name;
7392 rgw_obj_key object;
7393 std::tie(bucket_name, object) = *parse_path(path);
7394
7395 auto& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
7396 std::unique_ptr<rgw::sal::Bucket> bucket;
7397 ACLOwner bowner;
7398
7399 op_ret = store->get_bucket(this, s->user.get(), rgw_bucket(rgw_bucket_key(s->user->get_tenant(), bucket_name)), &bucket, y);
7400 if (op_ret < 0) {
7401 if (op_ret == -ENOENT) {
7402 ldpp_dout(this, 20) << "non existent directory=" << bucket_name << dendl;
7403 }
7404 return op_ret;
7405 }
7406
7407 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(object);
7408
7409 if (! handle_file_verify_permission(bucket->get_info(),
7410 obj->get_obj(),
7411 bucket->get_attrs(), bowner, y)) {
7412 ldpp_dout(this, 20) << "object creation unauthorized" << dendl;
7413 op_ret = -EACCES;
7414 return op_ret;
7415 }
7416
7417 op_ret = bucket->check_quota(this, user_quota, bucket_quota, size, y);
7418 if (op_ret < 0) {
7419 return op_ret;
7420 }
7421
7422 if (bucket->versioning_enabled()) {
7423 obj->gen_rand_obj_instance_name();
7424 }
7425
7426 rgw_placement_rule dest_placement = s->dest_placement;
7427 dest_placement.inherit_from(bucket->get_placement_rule());
7428
7429 std::unique_ptr<rgw::sal::Writer> processor;
7430 processor = store->get_atomic_writer(this, s->yield, std::move(obj),
7431 bowner.get_id(), obj_ctx,
7432 &s->dest_placement, 0, s->req_id);
7433 op_ret = processor->prepare(s->yield);
7434 if (op_ret < 0) {
7435 ldpp_dout(this, 20) << "cannot prepare processor due to ret=" << op_ret << dendl;
7436 return op_ret;
7437 }
7438
7439 /* No filters by default. */
7440 rgw::sal::DataProcessor *filter = processor.get();
7441
7442 const auto& compression_type = store->get_zone()->get_params().get_compression_type(
7443 dest_placement);
7444 CompressorRef plugin;
7445 boost::optional<RGWPutObj_Compress> compressor;
7446 if (compression_type != "none") {
7447 plugin = Compressor::create(s->cct, compression_type);
7448 if (! plugin) {
7449 ldpp_dout(this, 1) << "Cannot load plugin for rgw_compression_type "
7450 << compression_type << dendl;
7451 } else {
7452 compressor.emplace(s->cct, plugin, filter);
7453 filter = &*compressor;
7454 }
7455 }
7456
7457 /* Upload file content. */
7458 ssize_t len = 0;
7459 size_t ofs = 0;
7460 MD5 hash;
7461 // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
7462 hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
7463 do {
7464 ceph::bufferlist data;
7465 len = body.get_at_most(s->cct->_conf->rgw_max_chunk_size, data);
7466
7467 ldpp_dout(this, 20) << "body=" << data.c_str() << dendl;
7468 if (len < 0) {
7469 op_ret = len;
7470 return op_ret;
7471 } else if (len > 0) {
7472 hash.Update((const unsigned char *)data.c_str(), data.length());
7473 op_ret = filter->process(std::move(data), ofs);
7474 if (op_ret < 0) {
7475 ldpp_dout(this, 20) << "filter->process() returned ret=" << op_ret << dendl;
7476 return op_ret;
7477 }
7478
7479 ofs += len;
7480 }
7481
7482 } while (len > 0);
7483
7484 // flush
7485 op_ret = filter->process({}, ofs);
7486 if (op_ret < 0) {
7487 return op_ret;
7488 }
7489
7490 if (ofs != size) {
7491 ldpp_dout(this, 10) << "real file size different from declared" << dendl;
7492 op_ret = -EINVAL;
7493 return op_ret;
7494 }
7495
7496 op_ret = bucket->check_quota(this, user_quota, bucket_quota, size, y);
7497 if (op_ret < 0) {
7498 ldpp_dout(this, 20) << "quota exceeded for path=" << path << dendl;
7499 return op_ret;
7500 }
7501
7502 char calc_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
7503 unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE];
7504 hash.Final(m);
7505 buf_to_hex(m, CEPH_CRYPTO_MD5_DIGESTSIZE, calc_md5);
7506
7507 /* Create metadata: ETAG. */
7508 std::map<std::string, ceph::bufferlist> attrs;
7509 std::string etag = calc_md5;
7510 ceph::bufferlist etag_bl;
7511 etag_bl.append(etag.c_str(), etag.size() + 1);
7512 attrs.emplace(RGW_ATTR_ETAG, std::move(etag_bl));
7513
7514 /* Create metadata: ACLs. */
7515 RGWAccessControlPolicy policy;
7516 policy.create_default(s->user->get_id(), s->user->get_display_name());
7517 ceph::bufferlist aclbl;
7518 policy.encode(aclbl);
7519 attrs.emplace(RGW_ATTR_ACL, std::move(aclbl));
7520
7521 /* Create metadata: compression info. */
7522 if (compressor && compressor->is_compressed()) {
7523 ceph::bufferlist tmp;
7524 RGWCompressionInfo cs_info;
7525 cs_info.compression_type = plugin->get_type_name();
7526 cs_info.orig_size = size;
7527 cs_info.compressor_message = compressor->get_compressor_message();
7528 cs_info.blocks = std::move(compressor->get_compression_blocks());
7529 encode(cs_info, tmp);
7530 attrs.emplace(RGW_ATTR_COMPRESSION, std::move(tmp));
7531 }
7532
7533 /* Complete the transaction. */
7534 op_ret = processor->complete(size, etag, nullptr, ceph::real_time(),
7535 attrs, ceph::real_time() /* delete_at */,
7536 nullptr, nullptr, nullptr, nullptr, nullptr,
7537 s->yield);
7538 if (op_ret < 0) {
7539 ldpp_dout(this, 20) << "processor::complete returned op_ret=" << op_ret << dendl;
7540 }
7541
7542 return op_ret;
7543 }
7544
7545 void RGWBulkUploadOp::execute(optional_yield y)
7546 {
7547 ceph::bufferlist buffer(64 * 1024);
7548
7549 ldpp_dout(this, 20) << "start" << dendl;
7550
7551 /* Create an instance of stream-abstracting class. Having this indirection
7552 * allows for easy introduction of decompressors like gzip and bzip2. */
7553 auto stream = create_stream();
7554 if (! stream) {
7555 return;
7556 }
7557
7558 /* Handling the $UPLOAD_PATH accordingly to the Swift's Bulk middleware. See:
7559 * https://github.com/openstack/swift/blob/2.13.0/swift/common/middleware/bulk.py#L31-L41 */
7560 std::string bucket_path, file_prefix;
7561 std::tie(bucket_path, file_prefix) = handle_upload_path(s);
7562
7563 auto status = rgw::tar::StatusIndicator::create();
7564 do {
7565 op_ret = stream->get_exactly(rgw::tar::BLOCK_SIZE, buffer);
7566 if (op_ret < 0) {
7567 ldpp_dout(this, 2) << "cannot read header" << dendl;
7568 return;
7569 }
7570
7571 /* We need to re-interpret the buffer as a TAR block. Exactly two blocks
7572 * must be tracked to detect out end-of-archive. It occurs when both of
7573 * them are empty (zeroed). Tracing this particular inter-block dependency
7574 * is responsibility of the rgw::tar::StatusIndicator class. */
7575 boost::optional<rgw::tar::HeaderView> header;
7576 std::tie(status, header) = rgw::tar::interpret_block(status, buffer);
7577
7578 if (! status.empty() && header) {
7579 /* This specific block isn't empty (entirely zeroed), so we can parse
7580 * it as a TAR header and dispatch. At the moment we do support only
7581 * regular files and directories. Everything else (symlinks, devices)
7582 * will be ignored but won't cease the whole upload. */
7583 switch (header->get_filetype()) {
7584 case rgw::tar::FileType::NORMAL_FILE: {
7585 ldpp_dout(this, 2) << "handling regular file" << dendl;
7586
7587 std::string_view filename;
7588 if (bucket_path.empty())
7589 filename = header->get_filename();
7590 else
7591 filename = file_prefix + std::string(header->get_filename());
7592 auto body = AlignedStreamGetter(0, header->get_filesize(),
7593 rgw::tar::BLOCK_SIZE, *stream);
7594 op_ret = handle_file(filename,
7595 header->get_filesize(),
7596 body, y);
7597 if (! op_ret) {
7598 /* Only regular files counts. */
7599 num_created++;
7600 } else {
7601 failures.emplace_back(op_ret, std::string(filename));
7602 }
7603 break;
7604 }
7605 case rgw::tar::FileType::DIRECTORY: {
7606 ldpp_dout(this, 2) << "handling regular directory" << dendl;
7607
7608 std::string_view dirname = bucket_path.empty() ? header->get_filename() : bucket_path;
7609 op_ret = handle_dir(dirname, y);
7610 if (op_ret < 0 && op_ret != -ERR_BUCKET_EXISTS) {
7611 failures.emplace_back(op_ret, std::string(dirname));
7612 }
7613 break;
7614 }
7615 default: {
7616 /* Not recognized. Skip. */
7617 op_ret = 0;
7618 break;
7619 }
7620 }
7621
7622 /* In case of any problems with sub-request authorization Swift simply
7623 * terminates whole upload immediately. */
7624 if (boost::algorithm::contains(std::initializer_list<int>{ op_ret },
7625 terminal_errors)) {
7626 ldpp_dout(this, 2) << "terminating due to ret=" << op_ret << dendl;
7627 break;
7628 }
7629 } else {
7630 ldpp_dout(this, 2) << "an empty block" << dendl;
7631 op_ret = 0;
7632 }
7633
7634 buffer.clear();
7635 } while (! status.eof());
7636
7637 return;
7638 }
7639
7640 RGWBulkUploadOp::AlignedStreamGetter::~AlignedStreamGetter()
7641 {
7642 const size_t aligned_legnth = length + (-length % alignment);
7643 ceph::bufferlist junk;
7644
7645 DecoratedStreamGetter::get_exactly(aligned_legnth - position, junk);
7646 }
7647
7648 ssize_t RGWBulkUploadOp::AlignedStreamGetter::get_at_most(const size_t want,
7649 ceph::bufferlist& dst)
7650 {
7651 const size_t max_to_read = std::min(want, length - position);
7652 const auto len = DecoratedStreamGetter::get_at_most(max_to_read, dst);
7653 if (len > 0) {
7654 position += len;
7655 }
7656 return len;
7657 }
7658
7659 ssize_t RGWBulkUploadOp::AlignedStreamGetter::get_exactly(const size_t want,
7660 ceph::bufferlist& dst)
7661 {
7662 const auto len = DecoratedStreamGetter::get_exactly(want, dst);
7663 if (len > 0) {
7664 position += len;
7665 }
7666 return len;
7667 }
7668
7669 int RGWGetAttrs::verify_permission(optional_yield y)
7670 {
7671 s->object->set_atomic(s->obj_ctx);
7672
7673 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
7674 if (has_s3_existing_tag || has_s3_resource_tag)
7675 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
7676
7677 auto iam_action = s->object->get_instance().empty() ?
7678 rgw::IAM::s3GetObject :
7679 rgw::IAM::s3GetObjectVersion;
7680
7681 if (!verify_object_permission(this, s, iam_action)) {
7682 return -EACCES;
7683 }
7684
7685 return 0;
7686 }
7687
7688 void RGWGetAttrs::pre_exec()
7689 {
7690 rgw_bucket_object_pre_exec(s);
7691 }
7692
7693 void RGWGetAttrs::execute(optional_yield y)
7694 {
7695 op_ret = get_params();
7696 if (op_ret < 0)
7697 return;
7698
7699 s->object->set_atomic(s->obj_ctx);
7700
7701 op_ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, this);
7702 if (op_ret < 0) {
7703 ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << s->object
7704 << " ret=" << op_ret << dendl;
7705 return;
7706 }
7707
7708 /* XXX RGWObject::get_obj_attrs() does not support filtering (yet) */
7709 auto& obj_attrs = s->object->get_attrs();
7710 if (attrs.size() != 0) {
7711 /* return only attrs requested */
7712 for (auto& att : attrs) {
7713 auto iter = obj_attrs.find(att.first);
7714 if (iter != obj_attrs.end()) {
7715 att.second = iter->second;
7716 }
7717 }
7718 } else {
7719 /* return all attrs */
7720 for (auto& att : obj_attrs) {
7721 attrs.insert(get_attrs_t::value_type(att.first, att.second));;
7722 }
7723 }
7724
7725 return;
7726 }
7727
7728 int RGWRMAttrs::verify_permission(optional_yield y)
7729 {
7730 // This looks to be part of the RGW-NFS machinery and has no S3 or
7731 // Swift equivalent.
7732 bool perm;
7733 if (!rgw::sal::Object::empty(s->object.get())) {
7734 perm = verify_object_permission_no_policy(this, s, RGW_PERM_WRITE);
7735 } else {
7736 perm = verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE);
7737 }
7738 if (!perm)
7739 return -EACCES;
7740
7741 return 0;
7742 }
7743
7744 void RGWRMAttrs::pre_exec()
7745 {
7746 rgw_bucket_object_pre_exec(s);
7747 }
7748
7749 void RGWRMAttrs::execute(optional_yield y)
7750 {
7751 op_ret = get_params();
7752 if (op_ret < 0)
7753 return;
7754
7755 s->object->set_atomic(s->obj_ctx);
7756
7757 op_ret = s->object->set_obj_attrs(this, s->obj_ctx, nullptr, &attrs, y);
7758 if (op_ret < 0) {
7759 ldpp_dout(this, 0) << "ERROR: failed to delete obj attrs, obj=" << s->object
7760 << " ret=" << op_ret << dendl;
7761 }
7762 return;
7763 }
7764
7765 int RGWSetAttrs::verify_permission(optional_yield y)
7766 {
7767 // This looks to be part of the RGW-NFS machinery and has no S3 or
7768 // Swift equivalent.
7769 bool perm;
7770 if (!rgw::sal::Object::empty(s->object.get())) {
7771 perm = verify_object_permission_no_policy(this, s, RGW_PERM_WRITE);
7772 } else {
7773 perm = verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE);
7774 }
7775 if (!perm)
7776 return -EACCES;
7777
7778 return 0;
7779 }
7780
7781 void RGWSetAttrs::pre_exec()
7782 {
7783 rgw_bucket_object_pre_exec(s);
7784 }
7785
7786 void RGWSetAttrs::execute(optional_yield y)
7787 {
7788 op_ret = get_params(y);
7789 if (op_ret < 0)
7790 return;
7791
7792 if (!rgw::sal::Object::empty(s->object.get())) {
7793 rgw::sal::Attrs a(attrs);
7794 op_ret = s->object->set_obj_attrs(this, s->obj_ctx, &a, nullptr, y);
7795 } else {
7796 op_ret = s->bucket->merge_and_store_attrs(this, attrs, y);
7797 }
7798
7799 } /* RGWSetAttrs::execute() */
7800
7801 void RGWGetObjLayout::pre_exec()
7802 {
7803 rgw_bucket_object_pre_exec(s);
7804 }
7805
7806 void RGWGetObjLayout::execute(optional_yield y)
7807 {
7808 }
7809
7810
7811 int RGWConfigBucketMetaSearch::verify_permission(optional_yield y)
7812 {
7813 if (!s->auth.identity->is_owner_of(s->bucket_owner.get_id())) {
7814 return -EACCES;
7815 }
7816
7817 return 0;
7818 }
7819
7820 void RGWConfigBucketMetaSearch::pre_exec()
7821 {
7822 rgw_bucket_object_pre_exec(s);
7823 }
7824
7825 void RGWConfigBucketMetaSearch::execute(optional_yield y)
7826 {
7827 op_ret = get_params(y);
7828 if (op_ret < 0) {
7829 ldpp_dout(this, 20) << "NOTICE: get_params() returned ret=" << op_ret << dendl;
7830 return;
7831 }
7832
7833 s->bucket->get_info().mdsearch_config = mdsearch_config;
7834
7835 op_ret = s->bucket->put_info(this, false, real_time());
7836 if (op_ret < 0) {
7837 ldpp_dout(this, 0) << "NOTICE: put_bucket_info on bucket=" << s->bucket->get_name()
7838 << " returned err=" << op_ret << dendl;
7839 return;
7840 }
7841 s->bucket_attrs = s->bucket->get_attrs();
7842 }
7843
7844 int RGWGetBucketMetaSearch::verify_permission(optional_yield y)
7845 {
7846 if (!s->auth.identity->is_owner_of(s->bucket_owner.get_id())) {
7847 return -EACCES;
7848 }
7849
7850 return 0;
7851 }
7852
7853 void RGWGetBucketMetaSearch::pre_exec()
7854 {
7855 rgw_bucket_object_pre_exec(s);
7856 }
7857
7858 int RGWDelBucketMetaSearch::verify_permission(optional_yield y)
7859 {
7860 if (!s->auth.identity->is_owner_of(s->bucket_owner.get_id())) {
7861 return -EACCES;
7862 }
7863
7864 return 0;
7865 }
7866
7867 void RGWDelBucketMetaSearch::pre_exec()
7868 {
7869 rgw_bucket_object_pre_exec(s);
7870 }
7871
7872 void RGWDelBucketMetaSearch::execute(optional_yield y)
7873 {
7874 s->bucket->get_info().mdsearch_config.clear();
7875
7876 op_ret = s->bucket->put_info(this, false, real_time());
7877 if (op_ret < 0) {
7878 ldpp_dout(this, 0) << "NOTICE: put_bucket_info on bucket=" << s->bucket->get_name()
7879 << " returned err=" << op_ret << dendl;
7880 return;
7881 }
7882 s->bucket_attrs = s->bucket->get_attrs();
7883 }
7884
7885
7886 RGWHandler::~RGWHandler()
7887 {
7888 }
7889
7890 int RGWHandler::init(rgw::sal::Store* _store,
7891 struct req_state *_s,
7892 rgw::io::BasicClient *cio)
7893 {
7894 store = _store;
7895 s = _s;
7896
7897 return 0;
7898 }
7899
7900 int RGWHandler::do_init_permissions(const DoutPrefixProvider *dpp, optional_yield y)
7901 {
7902 int ret = rgw_build_bucket_policies(dpp, store, s, y);
7903 if (ret < 0) {
7904 ldpp_dout(dpp, 10) << "init_permissions on " << s->bucket
7905 << " failed, ret=" << ret << dendl;
7906 return ret==-ENODATA ? -EACCES : ret;
7907 }
7908
7909 rgw_build_iam_environment(store, s);
7910 return ret;
7911 }
7912
7913 int RGWHandler::do_read_permissions(RGWOp *op, bool only_bucket, optional_yield y)
7914 {
7915 if (only_bucket) {
7916 /* already read bucket info */
7917 return 0;
7918 }
7919 int ret = rgw_build_object_policies(op, store, s, op->prefetch_data(), y);
7920
7921 if (ret < 0) {
7922 ldpp_dout(op, 10) << "read_permissions on " << s->bucket << ":"
7923 << s->object << " only_bucket=" << only_bucket
7924 << " ret=" << ret << dendl;
7925 if (ret == -ENODATA)
7926 ret = -EACCES;
7927 if (s->auth.identity->is_anonymous() && ret == -EACCES)
7928 ret = -EPERM;
7929 }
7930
7931 return ret;
7932 }
7933
7934 int RGWOp::error_handler(int err_no, string *error_content, optional_yield y) {
7935 return dialect_handler->error_handler(err_no, error_content, y);
7936 }
7937
7938 int RGWHandler::error_handler(int err_no, string *error_content, optional_yield) {
7939 // This is the do-nothing error handler
7940 return err_no;
7941 }
7942
7943 std::ostream& RGWOp::gen_prefix(std::ostream& out) const
7944 {
7945 // append <dialect>:<op name> to the prefix
7946 return s->gen_prefix(out) << s->dialect << ':' << name() << ' ';
7947 }
7948
7949 void RGWDefaultResponseOp::send_response() {
7950 if (op_ret) {
7951 set_req_state_err(s, op_ret);
7952 }
7953 dump_errno(s);
7954 end_header(s);
7955 }
7956
7957 void RGWPutBucketPolicy::send_response()
7958 {
7959 if (!op_ret) {
7960 /* A successful Put Bucket Policy should return a 204 on success */
7961 op_ret = STATUS_NO_CONTENT;
7962 }
7963 if (op_ret) {
7964 set_req_state_err(s, op_ret);
7965 }
7966 dump_errno(s);
7967 end_header(s);
7968 }
7969
7970 int RGWPutBucketPolicy::verify_permission(optional_yield y)
7971 {
7972 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
7973 if (has_s3_resource_tag)
7974 rgw_iam_add_buckettags(this, s);
7975
7976 if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketPolicy)) {
7977 return -EACCES;
7978 }
7979
7980 return 0;
7981 }
7982
7983 int RGWPutBucketPolicy::get_params(optional_yield y)
7984 {
7985 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
7986 // At some point when I have more time I want to make a version of
7987 // rgw_rest_read_all_input that doesn't use malloc.
7988 std::tie(op_ret, data) = read_all_input(s, max_size, false);
7989
7990 // And throws exceptions.
7991 return op_ret;
7992 }
7993
7994 void RGWPutBucketPolicy::execute(optional_yield y)
7995 {
7996 op_ret = get_params(y);
7997 if (op_ret < 0) {
7998 return;
7999 }
8000
8001 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
8002 if (op_ret < 0) {
8003 ldpp_dout(this, 20) << "forward_request_to_master returned ret=" << op_ret << dendl;
8004 return;
8005 }
8006
8007 try {
8008 const Policy p(s->cct, s->bucket_tenant, data);
8009 rgw::sal::Attrs attrs(s->bucket_attrs);
8010 if (s->bucket_access_conf &&
8011 s->bucket_access_conf->block_public_policy() &&
8012 rgw::IAM::is_public(p)) {
8013 op_ret = -EACCES;
8014 return;
8015 }
8016
8017 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [&p, this, &attrs] {
8018 attrs[RGW_ATTR_IAM_POLICY].clear();
8019 attrs[RGW_ATTR_IAM_POLICY].append(p.text);
8020 op_ret = s->bucket->merge_and_store_attrs(this, attrs, s->yield);
8021 return op_ret;
8022 });
8023 } catch (rgw::IAM::PolicyParseException& e) {
8024 ldpp_dout(this, 20) << "failed to parse policy: " << e.what() << dendl;
8025 op_ret = -EINVAL;
8026 }
8027 }
8028
8029 void RGWGetBucketPolicy::send_response()
8030 {
8031 if (op_ret) {
8032 set_req_state_err(s, op_ret);
8033 }
8034 dump_errno(s);
8035 end_header(s, this, "application/json");
8036 dump_body(s, policy);
8037 }
8038
8039 int RGWGetBucketPolicy::verify_permission(optional_yield y)
8040 {
8041 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8042 if (has_s3_resource_tag)
8043 rgw_iam_add_buckettags(this, s);
8044
8045 if (!verify_bucket_permission(this, s, rgw::IAM::s3GetBucketPolicy)) {
8046 return -EACCES;
8047 }
8048
8049 return 0;
8050 }
8051
8052 void RGWGetBucketPolicy::execute(optional_yield y)
8053 {
8054 rgw::sal::Attrs attrs(s->bucket_attrs);
8055 auto aiter = attrs.find(RGW_ATTR_IAM_POLICY);
8056 if (aiter == attrs.end()) {
8057 ldpp_dout(this, 0) << "can't find bucket IAM POLICY attr bucket_name = "
8058 << s->bucket_name << dendl;
8059 op_ret = -ERR_NO_SUCH_BUCKET_POLICY;
8060 s->err.message = "The bucket policy does not exist";
8061 return;
8062 } else {
8063 policy = attrs[RGW_ATTR_IAM_POLICY];
8064
8065 if (policy.length() == 0) {
8066 ldpp_dout(this, 10) << "The bucket policy does not exist, bucket: "
8067 << s->bucket_name << dendl;
8068 op_ret = -ERR_NO_SUCH_BUCKET_POLICY;
8069 s->err.message = "The bucket policy does not exist";
8070 return;
8071 }
8072 }
8073 }
8074
8075 void RGWDeleteBucketPolicy::send_response()
8076 {
8077 if (op_ret) {
8078 set_req_state_err(s, op_ret);
8079 }
8080 dump_errno(s);
8081 end_header(s);
8082 }
8083
8084 int RGWDeleteBucketPolicy::verify_permission(optional_yield y)
8085 {
8086 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8087 if (has_s3_resource_tag)
8088 rgw_iam_add_buckettags(this, s);
8089
8090 if (!verify_bucket_permission(this, s, rgw::IAM::s3DeleteBucketPolicy)) {
8091 return -EACCES;
8092 }
8093
8094 return 0;
8095 }
8096
8097 void RGWDeleteBucketPolicy::execute(optional_yield y)
8098 {
8099 bufferlist data;
8100 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
8101 if (op_ret < 0) {
8102 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
8103 return;
8104 }
8105
8106 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
8107 rgw::sal::Attrs attrs(s->bucket_attrs);
8108 attrs.erase(RGW_ATTR_IAM_POLICY);
8109 op_ret = s->bucket->merge_and_store_attrs(this, attrs, s->yield);
8110 return op_ret;
8111 });
8112 }
8113
8114 void RGWPutBucketObjectLock::pre_exec()
8115 {
8116 rgw_bucket_object_pre_exec(s);
8117 }
8118
8119 int RGWPutBucketObjectLock::verify_permission(optional_yield y)
8120 {
8121 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8122 if (has_s3_resource_tag)
8123 rgw_iam_add_buckettags(this, s);
8124
8125 return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketObjectLockConfiguration);
8126 }
8127
8128 void RGWPutBucketObjectLock::execute(optional_yield y)
8129 {
8130 if (!s->bucket->get_info().obj_lock_enabled()) {
8131 s->err.message = "object lock configuration can't be set if bucket object lock not enabled";
8132 ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
8133 op_ret = -ERR_INVALID_BUCKET_STATE;
8134 return;
8135 }
8136
8137 RGWXMLDecoder::XMLParser parser;
8138 if (!parser.init()) {
8139 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
8140 op_ret = -EINVAL;
8141 return;
8142 }
8143 op_ret = get_params(y);
8144 if (op_ret < 0) {
8145 return;
8146 }
8147 if (!parser.parse(data.c_str(), data.length(), 1)) {
8148 op_ret = -ERR_MALFORMED_XML;
8149 return;
8150 }
8151
8152 try {
8153 RGWXMLDecoder::decode_xml("ObjectLockConfiguration", obj_lock, &parser, true);
8154 } catch (RGWXMLDecoder::err& err) {
8155 ldpp_dout(this, 5) << "unexpected xml:" << err << dendl;
8156 op_ret = -ERR_MALFORMED_XML;
8157 return;
8158 }
8159 if (obj_lock.has_rule() && !obj_lock.retention_period_valid()) {
8160 s->err.message = "retention period must be a positive integer value";
8161 ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
8162 op_ret = -ERR_INVALID_RETENTION_PERIOD;
8163 return;
8164 }
8165
8166 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
8167 if (op_ret < 0) {
8168 ldpp_dout(this, 20) << __func__ << "forward_request_to_master returned ret=" << op_ret << dendl;
8169 return;
8170 }
8171
8172 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
8173 s->bucket->get_info().obj_lock = obj_lock;
8174 op_ret = s->bucket->put_info(this, false, real_time());
8175 return op_ret;
8176 });
8177 return;
8178 }
8179
8180 void RGWGetBucketObjectLock::pre_exec()
8181 {
8182 rgw_bucket_object_pre_exec(s);
8183 }
8184
8185 int RGWGetBucketObjectLock::verify_permission(optional_yield y)
8186 {
8187 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8188 if (has_s3_resource_tag)
8189 rgw_iam_add_buckettags(this, s);
8190
8191 return verify_bucket_owner_or_policy(s, rgw::IAM::s3GetBucketObjectLockConfiguration);
8192 }
8193
8194 void RGWGetBucketObjectLock::execute(optional_yield y)
8195 {
8196 if (!s->bucket->get_info().obj_lock_enabled()) {
8197 op_ret = -ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION;
8198 return;
8199 }
8200 }
8201
8202 int RGWPutObjRetention::verify_permission(optional_yield y)
8203 {
8204 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
8205 if (has_s3_existing_tag || has_s3_resource_tag)
8206 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
8207
8208 if (!verify_object_permission(this, s, rgw::IAM::s3PutObjectRetention)) {
8209 return -EACCES;
8210 }
8211 op_ret = get_params(y);
8212 if (op_ret) {
8213 return op_ret;
8214 }
8215 if (bypass_governance_mode) {
8216 bypass_perm = verify_object_permission(this, s, rgw::IAM::s3BypassGovernanceRetention);
8217 }
8218 return 0;
8219 }
8220
8221 void RGWPutObjRetention::pre_exec()
8222 {
8223 rgw_bucket_object_pre_exec(s);
8224 }
8225
8226 void RGWPutObjRetention::execute(optional_yield y)
8227 {
8228 if (!s->bucket->get_info().obj_lock_enabled()) {
8229 s->err.message = "object retention can't be set if bucket object lock not configured";
8230 ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
8231 op_ret = -ERR_INVALID_REQUEST;
8232 return;
8233 }
8234
8235 RGWXMLDecoder::XMLParser parser;
8236 if (!parser.init()) {
8237 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
8238 op_ret = -EINVAL;
8239 return;
8240 }
8241
8242 if (!parser.parse(data.c_str(), data.length(), 1)) {
8243 op_ret = -ERR_MALFORMED_XML;
8244 return;
8245 }
8246
8247 try {
8248 RGWXMLDecoder::decode_xml("Retention", obj_retention, &parser, true);
8249 } catch (RGWXMLDecoder::err& err) {
8250 ldpp_dout(this, 5) << "unexpected xml:" << err << dendl;
8251 op_ret = -ERR_MALFORMED_XML;
8252 return;
8253 }
8254
8255 if (ceph::real_clock::to_time_t(obj_retention.get_retain_until_date()) < ceph_clock_now()) {
8256 s->err.message = "the retain-until date must be in the future";
8257 ldpp_dout(this, 0) << "ERROR: " << s->err.message << dendl;
8258 op_ret = -EINVAL;
8259 return;
8260 }
8261 bufferlist bl;
8262 obj_retention.encode(bl);
8263
8264 //check old retention
8265 op_ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, this);
8266 if (op_ret < 0) {
8267 ldpp_dout(this, 0) << "ERROR: get obj attr error"<< dendl;
8268 return;
8269 }
8270 rgw::sal::Attrs attrs = s->object->get_attrs();
8271 auto aiter = attrs.find(RGW_ATTR_OBJECT_RETENTION);
8272 if (aiter != attrs.end()) {
8273 RGWObjectRetention old_obj_retention;
8274 try {
8275 decode(old_obj_retention, aiter->second);
8276 } catch (buffer::error& err) {
8277 ldpp_dout(this, 0) << "ERROR: failed to decode RGWObjectRetention" << dendl;
8278 op_ret = -EIO;
8279 return;
8280 }
8281 if (ceph::real_clock::to_time_t(obj_retention.get_retain_until_date()) < ceph::real_clock::to_time_t(old_obj_retention.get_retain_until_date())) {
8282 if (old_obj_retention.get_mode().compare("GOVERNANCE") != 0 || !bypass_perm || !bypass_governance_mode) {
8283 s->err.message = "proposed retain-until date shortens an existing retention period and governance bypass check failed";
8284 op_ret = -EACCES;
8285 return;
8286 }
8287 } else if (old_obj_retention.get_mode() == obj_retention.get_mode()) {
8288 // ok if retention mode doesn't change
8289 } else if (obj_retention.get_mode() == "GOVERNANCE") {
8290 s->err.message = "can't change retention mode from COMPLIANCE to GOVERNANCE";
8291 op_ret = -EACCES;
8292 return;
8293 } else if (!bypass_perm || !bypass_governance_mode) {
8294 s->err.message = "can't change retention mode from GOVERNANCE without governance bypass";
8295 op_ret = -EACCES;
8296 return;
8297 }
8298 }
8299
8300 op_ret = s->object->modify_obj_attrs(s->obj_ctx, RGW_ATTR_OBJECT_RETENTION, bl, s->yield, this);
8301
8302 return;
8303 }
8304
8305 int RGWGetObjRetention::verify_permission(optional_yield y)
8306 {
8307 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
8308 if (has_s3_existing_tag || has_s3_resource_tag)
8309 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
8310
8311 if (!verify_object_permission(this, s, rgw::IAM::s3GetObjectRetention)) {
8312 return -EACCES;
8313 }
8314 return 0;
8315 }
8316
8317 void RGWGetObjRetention::pre_exec()
8318 {
8319 rgw_bucket_object_pre_exec(s);
8320 }
8321
8322 void RGWGetObjRetention::execute(optional_yield y)
8323 {
8324 if (!s->bucket->get_info().obj_lock_enabled()) {
8325 s->err.message = "bucket object lock not configured";
8326 ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
8327 op_ret = -ERR_INVALID_REQUEST;
8328 return;
8329 }
8330 op_ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, this);
8331 if (op_ret < 0) {
8332 ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << s->object
8333 << " ret=" << op_ret << dendl;
8334 return;
8335 }
8336 rgw::sal::Attrs attrs = s->object->get_attrs();
8337 auto aiter = attrs.find(RGW_ATTR_OBJECT_RETENTION);
8338 if (aiter == attrs.end()) {
8339 op_ret = -ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION;
8340 return;
8341 }
8342
8343 bufferlist::const_iterator iter{&aiter->second};
8344 try {
8345 obj_retention.decode(iter);
8346 } catch (const buffer::error& e) {
8347 ldpp_dout(this, 0) << __func__ << "decode object retention config failed" << dendl;
8348 op_ret = -EIO;
8349 return;
8350 }
8351 return;
8352 }
8353
8354 int RGWPutObjLegalHold::verify_permission(optional_yield y)
8355 {
8356 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
8357 if (has_s3_existing_tag || has_s3_resource_tag)
8358 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
8359
8360 if (!verify_object_permission(this, s, rgw::IAM::s3PutObjectLegalHold)) {
8361 return -EACCES;
8362 }
8363 return 0;
8364 }
8365
8366 void RGWPutObjLegalHold::pre_exec()
8367 {
8368 rgw_bucket_object_pre_exec(s);
8369 }
8370
8371 void RGWPutObjLegalHold::execute(optional_yield y) {
8372 if (!s->bucket->get_info().obj_lock_enabled()) {
8373 s->err.message = "object legal hold can't be set if bucket object lock not enabled";
8374 ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
8375 op_ret = -ERR_INVALID_REQUEST;
8376 return;
8377 }
8378
8379 RGWXMLDecoder::XMLParser parser;
8380 if (!parser.init()) {
8381 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
8382 op_ret = -EINVAL;
8383 return;
8384 }
8385
8386 op_ret = get_params(y);
8387 if (op_ret < 0)
8388 return;
8389
8390 if (!parser.parse(data.c_str(), data.length(), 1)) {
8391 op_ret = -ERR_MALFORMED_XML;
8392 return;
8393 }
8394
8395 try {
8396 RGWXMLDecoder::decode_xml("LegalHold", obj_legal_hold, &parser, true);
8397 } catch (RGWXMLDecoder::err &err) {
8398 ldpp_dout(this, 5) << "unexpected xml:" << err << dendl;
8399 op_ret = -ERR_MALFORMED_XML;
8400 return;
8401 }
8402 bufferlist bl;
8403 obj_legal_hold.encode(bl);
8404 //if instance is empty, we should modify the latest object
8405 op_ret = s->object->modify_obj_attrs(s->obj_ctx, RGW_ATTR_OBJECT_LEGAL_HOLD, bl, s->yield, this);
8406 return;
8407 }
8408
8409 int RGWGetObjLegalHold::verify_permission(optional_yield y)
8410 {
8411 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
8412 if (has_s3_existing_tag || has_s3_resource_tag)
8413 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
8414
8415 if (!verify_object_permission(this, s, rgw::IAM::s3GetObjectLegalHold)) {
8416 return -EACCES;
8417 }
8418 return 0;
8419 }
8420
8421 void RGWGetObjLegalHold::pre_exec()
8422 {
8423 rgw_bucket_object_pre_exec(s);
8424 }
8425
8426 void RGWGetObjLegalHold::execute(optional_yield y)
8427 {
8428 if (!s->bucket->get_info().obj_lock_enabled()) {
8429 s->err.message = "bucket object lock not configured";
8430 ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
8431 op_ret = -ERR_INVALID_REQUEST;
8432 return;
8433 }
8434 map<string, bufferlist> attrs;
8435 op_ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, this);
8436 if (op_ret < 0) {
8437 ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << s->object
8438 << " ret=" << op_ret << dendl;
8439 return;
8440 }
8441 auto aiter = s->object->get_attrs().find(RGW_ATTR_OBJECT_LEGAL_HOLD);
8442 if (aiter == s->object->get_attrs().end()) {
8443 op_ret = -ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION;
8444 return;
8445 }
8446
8447 bufferlist::const_iterator iter{&aiter->second};
8448 try {
8449 obj_legal_hold.decode(iter);
8450 } catch (const buffer::error& e) {
8451 ldpp_dout(this, 0) << __func__ << "decode object legal hold config failed" << dendl;
8452 op_ret = -EIO;
8453 return;
8454 }
8455 return;
8456 }
8457
8458 void RGWGetClusterStat::execute(optional_yield y)
8459 {
8460 op_ret = store->cluster_stat(stats_op);
8461 }
8462
8463 int RGWGetBucketPolicyStatus::verify_permission(optional_yield y)
8464 {
8465 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8466 if (has_s3_resource_tag)
8467 rgw_iam_add_buckettags(this, s);
8468
8469 if (!verify_bucket_permission(this, s, rgw::IAM::s3GetBucketPolicyStatus)) {
8470 return -EACCES;
8471 }
8472
8473 return 0;
8474 }
8475
8476 void RGWGetBucketPolicyStatus::execute(optional_yield y)
8477 {
8478 isPublic = (s->iam_policy && rgw::IAM::is_public(*s->iam_policy)) || s->bucket_acl->is_public(this);
8479 }
8480
8481 int RGWPutBucketPublicAccessBlock::verify_permission(optional_yield y)
8482 {
8483 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8484 if (has_s3_resource_tag)
8485 rgw_iam_add_buckettags(this, s);
8486
8487 if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketPublicAccessBlock)) {
8488 return -EACCES;
8489 }
8490
8491 return 0;
8492 }
8493
8494 int RGWPutBucketPublicAccessBlock::get_params(optional_yield y)
8495 {
8496 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
8497 std::tie(op_ret, data) = read_all_input(s, max_size, false);
8498 return op_ret;
8499 }
8500
8501 void RGWPutBucketPublicAccessBlock::execute(optional_yield y)
8502 {
8503 RGWXMLDecoder::XMLParser parser;
8504 if (!parser.init()) {
8505 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
8506 op_ret = -EINVAL;
8507 return;
8508 }
8509
8510 op_ret = get_params(y);
8511 if (op_ret < 0)
8512 return;
8513
8514 if (!parser.parse(data.c_str(), data.length(), 1)) {
8515 ldpp_dout(this, 0) << "ERROR: malformed XML" << dendl;
8516 op_ret = -ERR_MALFORMED_XML;
8517 return;
8518 }
8519
8520 try {
8521 RGWXMLDecoder::decode_xml("PublicAccessBlockConfiguration", access_conf, &parser, true);
8522 } catch (RGWXMLDecoder::err &err) {
8523 ldpp_dout(this, 5) << "unexpected xml:" << err << dendl;
8524 op_ret = -ERR_MALFORMED_XML;
8525 return;
8526 }
8527
8528 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
8529 if (op_ret < 0) {
8530 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
8531 return;
8532 }
8533
8534 bufferlist bl;
8535 access_conf.encode(bl);
8536 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this, &bl] {
8537 rgw::sal::Attrs attrs(s->bucket_attrs);
8538 attrs[RGW_ATTR_PUBLIC_ACCESS] = bl;
8539 return s->bucket->merge_and_store_attrs(this, attrs, s->yield);
8540 });
8541
8542 }
8543
8544 int RGWGetBucketPublicAccessBlock::verify_permission(optional_yield y)
8545 {
8546 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8547 if (has_s3_resource_tag)
8548 rgw_iam_add_buckettags(this, s);
8549
8550 if (!verify_bucket_permission(this, s, rgw::IAM::s3GetBucketPolicy)) {
8551 return -EACCES;
8552 }
8553
8554 return 0;
8555 }
8556
8557 void RGWGetBucketPublicAccessBlock::execute(optional_yield y)
8558 {
8559 auto attrs = s->bucket_attrs;
8560 if (auto aiter = attrs.find(RGW_ATTR_PUBLIC_ACCESS);
8561 aiter == attrs.end()) {
8562 ldpp_dout(this, 0) << "can't find bucket IAM POLICY attr bucket_name = "
8563 << s->bucket_name << dendl;
8564 // return the default;
8565 return;
8566 } else {
8567 bufferlist::const_iterator iter{&aiter->second};
8568 try {
8569 access_conf.decode(iter);
8570 } catch (const buffer::error& e) {
8571 ldpp_dout(this, 0) << __func__ << "decode access_conf failed" << dendl;
8572 op_ret = -EIO;
8573 return;
8574 }
8575 }
8576 }
8577
8578
8579 void RGWDeleteBucketPublicAccessBlock::send_response()
8580 {
8581 if (op_ret) {
8582 set_req_state_err(s, op_ret);
8583 }
8584 dump_errno(s);
8585 end_header(s);
8586 }
8587
8588 int RGWDeleteBucketPublicAccessBlock::verify_permission(optional_yield y)
8589 {
8590 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8591 if (has_s3_resource_tag)
8592 rgw_iam_add_buckettags(this, s);
8593
8594 if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketPublicAccessBlock)) {
8595 return -EACCES;
8596 }
8597
8598 return 0;
8599 }
8600
8601 void RGWDeleteBucketPublicAccessBlock::execute(optional_yield y)
8602 {
8603 bufferlist data;
8604 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
8605 if (op_ret < 0) {
8606 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
8607 return;
8608 }
8609
8610 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
8611 rgw::sal::Attrs attrs(s->bucket_attrs);
8612 attrs.erase(RGW_ATTR_PUBLIC_ACCESS);
8613 op_ret = s->bucket->merge_and_store_attrs(this, attrs, s->yield);
8614 return op_ret;
8615 });
8616 }
8617
8618 int RGWPutBucketEncryption::get_params(optional_yield y)
8619 {
8620 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
8621 std::tie(op_ret, data) = read_all_input(s, max_size, false);
8622 return op_ret;
8623 }
8624
8625 int RGWPutBucketEncryption::verify_permission(optional_yield y)
8626 {
8627 if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketEncryption)) {
8628 return -EACCES;
8629 }
8630 return 0;
8631 }
8632
8633 void RGWPutBucketEncryption::execute(optional_yield y)
8634 {
8635 RGWXMLDecoder::XMLParser parser;
8636 if (!parser.init()) {
8637 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
8638 op_ret = -EINVAL;
8639 return;
8640 }
8641 op_ret = get_params(y);
8642 if (op_ret < 0) {
8643 return;
8644 }
8645 if (!parser.parse(data.c_str(), data.length(), 1)) {
8646 ldpp_dout(this, 0) << "ERROR: malformed XML" << dendl;
8647 op_ret = -ERR_MALFORMED_XML;
8648 return;
8649 }
8650
8651 try {
8652 RGWXMLDecoder::decode_xml("ServerSideEncryptionConfiguration", bucket_encryption_conf, &parser, true);
8653 } catch (RGWXMLDecoder::err& err) {
8654 ldpp_dout(this, 5) << "unexpected xml:" << err << dendl;
8655 op_ret = -ERR_MALFORMED_XML;
8656 return;
8657 }
8658
8659 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
8660 if (op_ret < 0) {
8661 ldpp_dout(this, 20) << "forward_request_to_master returned ret=" << op_ret << dendl;
8662 return;
8663 }
8664
8665 bufferlist conf_bl;
8666 bucket_encryption_conf.encode(conf_bl);
8667 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this, y, &conf_bl] {
8668 rgw::sal::Attrs attrs = s->bucket->get_attrs();
8669 attrs[RGW_ATTR_BUCKET_ENCRYPTION_POLICY] = conf_bl;
8670 return s->bucket->merge_and_store_attrs(this, attrs, y);
8671 });
8672 }
8673
8674 int RGWGetBucketEncryption::verify_permission(optional_yield y)
8675 {
8676 if (!verify_bucket_permission(this, s, rgw::IAM::s3GetBucketEncryption)) {
8677 return -EACCES;
8678 }
8679 return 0;
8680 }
8681
8682 void RGWGetBucketEncryption::execute(optional_yield y)
8683 {
8684 const auto& attrs = s->bucket_attrs;
8685 if (auto aiter = attrs.find(RGW_ATTR_BUCKET_ENCRYPTION_POLICY);
8686 aiter == attrs.end()) {
8687 ldpp_dout(this, 0) << "can't find BUCKET ENCRYPTION attr for bucket_name = " << s->bucket_name << dendl;
8688 op_ret = -ENOENT;
8689 s->err.message = "The server side encryption configuration was not found";
8690 return;
8691 } else {
8692 bufferlist::const_iterator iter{&aiter->second};
8693 try {
8694 bucket_encryption_conf.decode(iter);
8695 } catch (const buffer::error& e) {
8696 ldpp_dout(this, 0) << __func__ << "decode bucket_encryption_conf failed" << dendl;
8697 op_ret = -EIO;
8698 return;
8699 }
8700 }
8701 }
8702
8703 int RGWDeleteBucketEncryption::verify_permission(optional_yield y)
8704 {
8705 if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketEncryption)) {
8706 return -EACCES;
8707 }
8708 return 0;
8709 }
8710
8711 void RGWDeleteBucketEncryption::execute(optional_yield y)
8712 {
8713 bufferlist data;
8714 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
8715 if (op_ret < 0) {
8716 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
8717 return;
8718 }
8719
8720 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this, y] {
8721 rgw::sal::Attrs attrs = s->bucket->get_attrs();
8722 attrs.erase(RGW_ATTR_BUCKET_ENCRYPTION_POLICY);
8723 attrs.erase(RGW_ATTR_BUCKET_ENCRYPTION_KEY_ID);
8724 op_ret = s->bucket->merge_and_store_attrs(this, attrs, y);
8725 return op_ret;
8726 });
8727 }
8728
8729 void rgw_slo_entry::decode_json(JSONObj *obj)
8730 {
8731 JSONDecoder::decode_json("path", path, obj);
8732 JSONDecoder::decode_json("etag", etag, obj);
8733 JSONDecoder::decode_json("size_bytes", size_bytes, obj);
8734 };
8735