]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_op.cc
import quincy beta 17.1.0
[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 = s->bucket->remove_bucket(this, false, false, nullptr, y);
3431 if (op_ret < 0 && op_ret == -ECANCELED) {
3432 // lost a race, either with mdlog sync or another delete bucket operation.
3433 // in either case, we've already called ctl.bucket->unlink_bucket()
3434 op_ret = 0;
3435 }
3436
3437 return;
3438 }
3439
3440 int RGWPutObj::init_processing(optional_yield y) {
3441 copy_source = url_decode(s->info.env->get("HTTP_X_AMZ_COPY_SOURCE", ""));
3442 copy_source_range = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE_RANGE");
3443 size_t pos;
3444 int ret;
3445
3446 /* handle x-amz-copy-source */
3447 std::string_view cs_view(copy_source);
3448 if (! cs_view.empty()) {
3449 if (cs_view[0] == '/')
3450 cs_view.remove_prefix(1);
3451 copy_source_bucket_name = std::string(cs_view);
3452 pos = copy_source_bucket_name.find("/");
3453 if (pos == std::string::npos) {
3454 ret = -EINVAL;
3455 ldpp_dout(this, 5) << "x-amz-copy-source bad format" << dendl;
3456 return ret;
3457 }
3458 copy_source_object_name =
3459 copy_source_bucket_name.substr(pos + 1, copy_source_bucket_name.size());
3460 copy_source_bucket_name = copy_source_bucket_name.substr(0, pos);
3461 #define VERSION_ID_STR "?versionId="
3462 pos = copy_source_object_name.find(VERSION_ID_STR);
3463 if (pos == std::string::npos) {
3464 copy_source_object_name = url_decode(copy_source_object_name);
3465 } else {
3466 copy_source_version_id =
3467 copy_source_object_name.substr(pos + sizeof(VERSION_ID_STR) - 1);
3468 copy_source_object_name =
3469 url_decode(copy_source_object_name.substr(0, pos));
3470 }
3471 pos = copy_source_bucket_name.find(":");
3472 if (pos == std::string::npos) {
3473 // if tenant is not specified in x-amz-copy-source, use tenant of the requester
3474 copy_source_tenant_name = s->user->get_tenant();
3475 } else {
3476 copy_source_tenant_name = copy_source_bucket_name.substr(0, pos);
3477 copy_source_bucket_name = copy_source_bucket_name.substr(pos + 1, copy_source_bucket_name.size());
3478 if (copy_source_bucket_name.empty()) {
3479 ret = -EINVAL;
3480 ldpp_dout(this, 5) << "source bucket name is empty" << dendl;
3481 return ret;
3482 }
3483 }
3484 std::unique_ptr<rgw::sal::Bucket> bucket;
3485 ret = store->get_bucket(this, s->user.get(), copy_source_tenant_name, copy_source_bucket_name,
3486 &bucket, y);
3487 if (ret < 0) {
3488 ldpp_dout(this, 5) << __func__ << "(): get_bucket() returned ret=" << ret << dendl;
3489 if (ret == -ENOENT) {
3490 ret = -ERR_NO_SUCH_BUCKET;
3491 }
3492 return ret;
3493 }
3494
3495 ret = bucket->load_bucket(this, y);
3496 if (ret < 0) {
3497 ldpp_dout(this, 5) << __func__ << "(): load_bucket() returned ret=" << ret << dendl;
3498 return ret;
3499 }
3500 copy_source_bucket_info = bucket->get_info();
3501
3502 /* handle x-amz-copy-source-range */
3503 if (copy_source_range) {
3504 string range = copy_source_range;
3505 pos = range.find("bytes=");
3506 if (pos == std::string::npos || pos != 0) {
3507 ret = -EINVAL;
3508 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format" << dendl;
3509 return ret;
3510 }
3511 /* 6 is the length of "bytes=" */
3512 range = range.substr(pos + 6);
3513 pos = range.find("-");
3514 if (pos == std::string::npos) {
3515 ret = -EINVAL;
3516 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format" << dendl;
3517 return ret;
3518 }
3519 string first = range.substr(0, pos);
3520 string last = range.substr(pos + 1);
3521 if (first.find_first_not_of("0123456789") != std::string::npos ||
3522 last.find_first_not_of("0123456789") != std::string::npos) {
3523 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format not an integer" << dendl;
3524 ret = -EINVAL;
3525 return ret;
3526 }
3527 copy_source_range_fst = strtoull(first.c_str(), NULL, 10);
3528 copy_source_range_lst = strtoull(last.c_str(), NULL, 10);
3529 if (copy_source_range_fst > copy_source_range_lst) {
3530 ret = -ERANGE;
3531 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format first number bigger than second" << dendl;
3532 return ret;
3533 }
3534 }
3535
3536 } /* copy_source */
3537 return RGWOp::init_processing(y);
3538 }
3539
3540 int RGWPutObj::verify_permission(optional_yield y)
3541 {
3542 if (! copy_source.empty()) {
3543
3544 RGWAccessControlPolicy cs_acl(s->cct);
3545 boost::optional<Policy> policy;
3546 map<string, bufferlist> cs_attrs;
3547 std::unique_ptr<rgw::sal::Bucket> cs_bucket;
3548 int ret = store->get_bucket(NULL, copy_source_bucket_info, &cs_bucket);
3549 if (ret < 0)
3550 return ret;
3551
3552 std::unique_ptr<rgw::sal::Object> cs_object =
3553 cs_bucket->get_object(rgw_obj_key(copy_source_object_name, copy_source_version_id));
3554
3555 cs_object->set_atomic(s->obj_ctx);
3556 cs_object->set_prefetch_data(s->obj_ctx);
3557
3558 /* check source object permissions */
3559 if (ret = read_obj_policy(this, store, s, copy_source_bucket_info, cs_attrs, &cs_acl, nullptr,
3560 policy, cs_bucket.get(), cs_object.get(), y, true); ret < 0) {
3561 return ret;
3562 }
3563
3564 /* admin request overrides permission checks */
3565 if (! s->auth.identity->is_admin_of(cs_acl.get_owner().get_id())) {
3566 if (policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
3567 //add source object tags for permission evaluation
3568 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, policy, s->iam_user_policies, s->session_policies);
3569 if (has_s3_existing_tag || has_s3_resource_tag)
3570 rgw_iam_add_objtags(this, s, cs_object.get(), has_s3_existing_tag, has_s3_resource_tag);
3571 auto usr_policy_res = Effect::Pass;
3572 rgw::ARN obj_arn(cs_object->get_obj());
3573 for (auto& user_policy : s->iam_user_policies) {
3574 if (usr_policy_res = user_policy.eval(s->env, *s->auth.identity,
3575 cs_object->get_instance().empty() ?
3576 rgw::IAM::s3GetObject :
3577 rgw::IAM::s3GetObjectVersion,
3578 obj_arn); usr_policy_res == Effect::Deny)
3579 return -EACCES;
3580 else if (usr_policy_res == Effect::Allow)
3581 break;
3582 }
3583 rgw::IAM::Effect e = Effect::Pass;
3584 if (policy) {
3585 rgw::ARN obj_arn(cs_object->get_obj());
3586 e = policy->eval(s->env, *s->auth.identity,
3587 cs_object->get_instance().empty() ?
3588 rgw::IAM::s3GetObject :
3589 rgw::IAM::s3GetObjectVersion,
3590 obj_arn);
3591 }
3592 if (e == Effect::Deny) {
3593 return -EACCES;
3594 } else if (usr_policy_res == Effect::Pass && e == Effect::Pass &&
3595 !cs_acl.verify_permission(this, *s->auth.identity, s->perm_mask,
3596 RGW_PERM_READ)) {
3597 return -EACCES;
3598 }
3599 rgw_iam_remove_objtags(this, s, cs_object.get(), has_s3_existing_tag, has_s3_resource_tag);
3600 } else if (!cs_acl.verify_permission(this, *s->auth.identity, s->perm_mask,
3601 RGW_PERM_READ)) {
3602 return -EACCES;
3603 }
3604 }
3605 }
3606
3607 if (s->bucket_access_conf && s->bucket_access_conf->block_public_acls()) {
3608 if (s->canned_acl.compare("public-read") ||
3609 s->canned_acl.compare("public-read-write") ||
3610 s->canned_acl.compare("authenticated-read"))
3611 return -EACCES;
3612 }
3613
3614 auto op_ret = get_params(y);
3615 if (op_ret < 0) {
3616 ldpp_dout(this, 20) << "get_params() returned ret=" << op_ret << dendl;
3617 return op_ret;
3618 }
3619
3620 if (s->iam_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
3621 rgw_add_grant_to_iam_environment(s->env, s);
3622
3623 rgw_add_to_iam_environment(s->env, "s3:x-amz-acl", s->canned_acl);
3624
3625 if (obj_tags != nullptr && obj_tags->count() > 0){
3626 auto tags = obj_tags->get_tags();
3627 for (const auto& kv: tags){
3628 rgw_add_to_iam_environment(s->env, "s3:RequestObjectTag/"+kv.first, kv.second);
3629 }
3630 }
3631
3632 constexpr auto encrypt_attr = "x-amz-server-side-encryption";
3633 constexpr auto s3_encrypt_attr = "s3:x-amz-server-side-encryption";
3634 auto enc_header = s->info.x_meta_map.find(encrypt_attr);
3635 if (enc_header != s->info.x_meta_map.end()){
3636 rgw_add_to_iam_environment(s->env, s3_encrypt_attr, enc_header->second);
3637 }
3638
3639 constexpr auto kms_attr = "x-amz-server-side-encryption-aws-kms-key-id";
3640 constexpr auto s3_kms_attr = "s3:x-amz-server-side-encryption-aws-kms-key-id";
3641 auto kms_header = s->info.x_meta_map.find(kms_attr);
3642 if (kms_header != s->info.x_meta_map.end()){
3643 rgw_add_to_iam_environment(s->env, s3_kms_attr, kms_header->second);
3644 }
3645
3646 // Add bucket tags for authorization
3647 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
3648 if (has_s3_resource_tag)
3649 rgw_iam_add_buckettags(this, s);
3650
3651 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
3652 rgw::IAM::s3PutObject,
3653 s->object->get_obj());
3654 if (identity_policy_res == Effect::Deny)
3655 return -EACCES;
3656
3657 rgw::IAM::Effect e = Effect::Pass;
3658 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
3659 if (s->iam_policy) {
3660 ARN obj_arn(s->object->get_obj());
3661 e = s->iam_policy->eval(s->env, *s->auth.identity,
3662 rgw::IAM::s3PutObject,
3663 obj_arn,
3664 princ_type);
3665 }
3666 if (e == Effect::Deny) {
3667 return -EACCES;
3668 }
3669
3670 if (!s->session_policies.empty()) {
3671 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
3672 rgw::IAM::s3PutObject,
3673 s->object->get_obj());
3674 if (session_policy_res == Effect::Deny) {
3675 return -EACCES;
3676 }
3677 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
3678 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
3679 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
3680 (session_policy_res == Effect::Allow && e == Effect::Allow))
3681 return 0;
3682 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
3683 //Intersection of session policy and identity policy plus bucket policy
3684 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow)
3685 return 0;
3686 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
3687 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow)
3688 return 0;
3689 }
3690 return -EACCES;
3691 }
3692 if (e == Effect::Allow || identity_policy_res == Effect::Allow) {
3693 return 0;
3694 }
3695 }
3696
3697 if (!verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
3698 return -EACCES;
3699 }
3700
3701 return 0;
3702 }
3703
3704
3705 void RGWPutObj::pre_exec()
3706 {
3707 rgw_bucket_object_pre_exec(s);
3708 }
3709
3710 class RGWPutObj_CB : public RGWGetObj_Filter
3711 {
3712 RGWPutObj *op;
3713 public:
3714 explicit RGWPutObj_CB(RGWPutObj *_op) : op(_op) {}
3715 ~RGWPutObj_CB() override {}
3716
3717 int handle_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) override {
3718 return op->get_data_cb(bl, bl_ofs, bl_len);
3719 }
3720 };
3721
3722 int RGWPutObj::get_data_cb(bufferlist& bl, off_t bl_ofs, off_t bl_len)
3723 {
3724 bufferlist bl_tmp;
3725 bl.begin(bl_ofs).copy(bl_len, bl_tmp);
3726
3727 bl_aux.append(bl_tmp);
3728
3729 return bl_len;
3730 }
3731
3732 int RGWPutObj::get_data(const off_t fst, const off_t lst, bufferlist& bl)
3733 {
3734 RGWPutObj_CB cb(this);
3735 RGWGetObj_Filter* filter = &cb;
3736 boost::optional<RGWGetObj_Decompress> decompress;
3737 std::unique_ptr<RGWGetObj_Filter> decrypt;
3738 RGWCompressionInfo cs_info;
3739 map<string, bufferlist> attrs;
3740 int ret = 0;
3741
3742 uint64_t obj_size;
3743 int64_t new_ofs, new_end;
3744
3745 new_ofs = fst;
3746 new_end = lst;
3747
3748 std::unique_ptr<rgw::sal::Bucket> bucket;
3749 ret = store->get_bucket(nullptr, copy_source_bucket_info, &bucket);
3750 if (ret < 0)
3751 return ret;
3752
3753 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(rgw_obj_key(copy_source_object_name, copy_source_version_id));
3754 std::unique_ptr<rgw::sal::Object::ReadOp> read_op(obj->get_read_op(s->obj_ctx));
3755
3756 ret = read_op->prepare(s->yield, this);
3757 if (ret < 0)
3758 return ret;
3759
3760 obj_size = obj->get_obj_size();
3761
3762 bool need_decompress;
3763 op_ret = rgw_compression_info_from_attrset(obj->get_attrs(), need_decompress, cs_info);
3764 if (op_ret < 0) {
3765 ldpp_dout(this, 0) << "ERROR: failed to decode compression info" << dendl;
3766 return -EIO;
3767 }
3768
3769 bool partial_content = true;
3770 if (need_decompress)
3771 {
3772 obj_size = cs_info.orig_size;
3773 decompress.emplace(s->cct, &cs_info, partial_content, filter);
3774 filter = &*decompress;
3775 }
3776
3777 auto attr_iter = obj->get_attrs().find(RGW_ATTR_MANIFEST);
3778 op_ret = this->get_decrypt_filter(&decrypt,
3779 filter,
3780 obj->get_attrs(),
3781 attr_iter != obj->get_attrs().end() ? &(attr_iter->second) : nullptr);
3782 if (decrypt != nullptr) {
3783 filter = decrypt.get();
3784 }
3785 if (op_ret < 0) {
3786 return op_ret;
3787 }
3788
3789 ret = obj->range_to_ofs(obj_size, new_ofs, new_end);
3790 if (ret < 0)
3791 return ret;
3792
3793 filter->fixup_range(new_ofs, new_end);
3794 ret = read_op->iterate(this, new_ofs, new_end, filter, s->yield);
3795
3796 if (ret >= 0)
3797 ret = filter->flush();
3798
3799 bl.claim_append(bl_aux);
3800
3801 return ret;
3802 }
3803
3804 // special handling for compression type = "random" with multipart uploads
3805 static CompressorRef get_compressor_plugin(const req_state *s,
3806 const std::string& compression_type)
3807 {
3808 if (compression_type != "random") {
3809 return Compressor::create(s->cct, compression_type);
3810 }
3811
3812 bool is_multipart{false};
3813 const auto& upload_id = s->info.args.get("uploadId", &is_multipart);
3814
3815 if (!is_multipart) {
3816 return Compressor::create(s->cct, compression_type);
3817 }
3818
3819 // use a hash of the multipart upload id so all parts use the same plugin
3820 const auto alg = std::hash<std::string>{}(upload_id) % Compressor::COMP_ALG_LAST;
3821 if (alg == Compressor::COMP_ALG_NONE) {
3822 return nullptr;
3823 }
3824 return Compressor::create(s->cct, alg);
3825 }
3826
3827 void RGWPutObj::execute(optional_yield y)
3828 {
3829 char supplied_md5_bin[CEPH_CRYPTO_MD5_DIGESTSIZE + 1];
3830 char supplied_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
3831 char calc_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
3832 unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE];
3833 MD5 hash;
3834 // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
3835 hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
3836 bufferlist bl, aclbl, bs;
3837 int len;
3838
3839 off_t fst;
3840 off_t lst;
3841
3842 bool need_calc_md5 = (dlo_manifest == NULL) && (slo_info == NULL);
3843 perfcounter->inc(l_rgw_put);
3844 // report latency on return
3845 auto put_lat = make_scope_guard([&] {
3846 perfcounter->tinc(l_rgw_put_lat, s->time_elapsed());
3847 });
3848
3849 op_ret = -EINVAL;
3850 if (rgw::sal::Object::empty(s->object.get())) {
3851 return;
3852 }
3853
3854 if (!s->bucket_exists) {
3855 op_ret = -ERR_NO_SUCH_BUCKET;
3856 return;
3857 }
3858
3859 op_ret = get_system_versioning_params(s, &olh_epoch, &version_id);
3860 if (op_ret < 0) {
3861 ldpp_dout(this, 20) << "get_system_versioning_params() returned ret="
3862 << op_ret << dendl;
3863 return;
3864 }
3865
3866 if (supplied_md5_b64) {
3867 need_calc_md5 = true;
3868
3869 ldpp_dout(this, 15) << "supplied_md5_b64=" << supplied_md5_b64 << dendl;
3870 op_ret = ceph_unarmor(supplied_md5_bin, &supplied_md5_bin[CEPH_CRYPTO_MD5_DIGESTSIZE + 1],
3871 supplied_md5_b64, supplied_md5_b64 + strlen(supplied_md5_b64));
3872 ldpp_dout(this, 15) << "ceph_armor ret=" << op_ret << dendl;
3873 if (op_ret != CEPH_CRYPTO_MD5_DIGESTSIZE) {
3874 op_ret = -ERR_INVALID_DIGEST;
3875 return;
3876 }
3877
3878 buf_to_hex((const unsigned char *)supplied_md5_bin, CEPH_CRYPTO_MD5_DIGESTSIZE, supplied_md5);
3879 ldpp_dout(this, 15) << "supplied_md5=" << supplied_md5 << dendl;
3880 }
3881
3882 if (!chunked_upload) { /* with chunked upload we don't know how big is the upload.
3883 we also check sizes at the end anyway */
3884 op_ret = s->bucket->check_quota(this, user_quota, bucket_quota, s->content_length, y);
3885 if (op_ret < 0) {
3886 ldpp_dout(this, 20) << "check_quota() returned ret=" << op_ret << dendl;
3887 return;
3888 }
3889 }
3890
3891 if (supplied_etag) {
3892 strncpy(supplied_md5, supplied_etag, sizeof(supplied_md5) - 1);
3893 supplied_md5[sizeof(supplied_md5) - 1] = '\0';
3894 }
3895
3896 const bool multipart = !multipart_upload_id.empty();
3897 auto& obj_ctx = *static_cast<RGWObjectCtx*>(s->obj_ctx);
3898
3899 /* Handle object versioning of Swift API. */
3900 if (! multipart) {
3901 op_ret = s->object->swift_versioning_copy(s->obj_ctx, this, s->yield);
3902 if (op_ret < 0) {
3903 return;
3904 }
3905 }
3906
3907 // make reservation for notification if needed
3908 std::unique_ptr<rgw::sal::Notification> res
3909 = store->get_notification(
3910 s->object.get(), s->src_object.get(), s,
3911 rgw::notify::ObjectCreatedPut);
3912 if(!multipart) {
3913 op_ret = res->publish_reserve(this, obj_tags.get());
3914 if (op_ret < 0) {
3915 return;
3916 }
3917 }
3918
3919 // create the object processor
3920 auto aio = rgw::make_throttle(s->cct->_conf->rgw_put_obj_min_window_size,
3921 s->yield);
3922 std::unique_ptr<rgw::sal::Writer> processor;
3923
3924 rgw_placement_rule *pdest_placement = &s->dest_placement;
3925
3926 if (multipart) {
3927 std::unique_ptr<rgw::sal::MultipartUpload> upload;
3928 upload = s->bucket->get_multipart_upload(s->object->get_name(),
3929 multipart_upload_id);
3930 op_ret = upload->get_info(this, s->yield, s->obj_ctx, &pdest_placement);
3931
3932 s->trace->SetAttribute(tracing::rgw::UPLOAD_ID, multipart_upload_id);
3933 multipart_trace = tracing::rgw::tracer.add_span(name(), upload->get_trace());
3934
3935 if (op_ret < 0) {
3936 if (op_ret != -ENOENT) {
3937 ldpp_dout(this, 0) << "ERROR: get_multipart_info returned " << op_ret << ": " << cpp_strerror(-op_ret) << dendl;
3938 } else {// -ENOENT: raced with upload complete/cancel, no need to spam log
3939 ldpp_dout(this, 20) << "failed to get multipart info (returned " << op_ret << ": " << cpp_strerror(-op_ret) << "): probably raced with upload complete / cancel" << dendl;
3940 }
3941 return;
3942 }
3943 /* upload will go out of scope, so copy the dest placement for later use */
3944 s->dest_placement = *pdest_placement;
3945 pdest_placement = &s->dest_placement;
3946 ldpp_dout(this, 20) << "dest_placement for part=" << *pdest_placement << dendl;
3947 processor = upload->get_writer(this, s->yield, s->object->clone(),
3948 s->user->get_id(), obj_ctx, pdest_placement,
3949 multipart_part_num, multipart_part_str);
3950 } else if(append) {
3951 if (s->bucket->versioned()) {
3952 op_ret = -ERR_INVALID_BUCKET_STATE;
3953 return;
3954 }
3955 processor = store->get_append_writer(this, s->yield, s->object->clone(),
3956 s->bucket_owner.get_id(), obj_ctx,
3957 pdest_placement, s->req_id, position,
3958 &cur_accounted_size);
3959 } else {
3960 if (s->bucket->versioning_enabled()) {
3961 if (!version_id.empty()) {
3962 s->object->set_instance(version_id);
3963 } else {
3964 s->object->gen_rand_obj_instance_name();
3965 version_id = s->object->get_instance();
3966 }
3967 }
3968 processor = store->get_atomic_writer(this, s->yield, s->object->clone(),
3969 s->bucket_owner.get_id(), obj_ctx,
3970 pdest_placement, olh_epoch, s->req_id);
3971 }
3972
3973 op_ret = processor->prepare(s->yield);
3974 if (op_ret < 0) {
3975 ldpp_dout(this, 20) << "processor->prepare() returned ret=" << op_ret
3976 << dendl;
3977 return;
3978 }
3979 if ((! copy_source.empty()) && !copy_source_range) {
3980 std::unique_ptr<rgw::sal::Bucket> bucket;
3981 op_ret = store->get_bucket(nullptr, copy_source_bucket_info, &bucket);
3982 if (op_ret < 0) {
3983 ldpp_dout(this, 0) << "ERROR: failed to get bucket with error" << op_ret << dendl;
3984 return;
3985 }
3986 std::unique_ptr<rgw::sal::Object> obj =
3987 bucket->get_object(rgw_obj_key(copy_source_object_name, copy_source_version_id));
3988
3989 RGWObjState *astate;
3990 op_ret = obj->get_obj_state(this, &obj_ctx, &astate, s->yield);
3991 if (op_ret < 0) {
3992 ldpp_dout(this, 0) << "ERROR: get copy source obj state returned with error" << op_ret << dendl;
3993 return;
3994 }
3995 bufferlist bl;
3996 if (astate->get_attr(RGW_ATTR_MANIFEST, bl)) {
3997 RGWObjManifest m;
3998 decode(m, bl);
3999 if (m.get_tier_type() == "cloud-s3") {
4000 op_ret = -ERR_INVALID_OBJECT_STATE;
4001 ldpp_dout(this, 0) << "ERROR: Cannot copy cloud tiered object. Failing with "
4002 << op_ret << dendl;
4003 return;
4004 }
4005 }
4006
4007 if (!astate->exists){
4008 op_ret = -ENOENT;
4009 return;
4010 }
4011 lst = astate->accounted_size - 1;
4012 } else {
4013 lst = copy_source_range_lst;
4014 }
4015 fst = copy_source_range_fst;
4016
4017 // no filters by default
4018 rgw::sal::DataProcessor *filter = processor.get();
4019
4020 const auto& compression_type = store->get_zone()->get_params().get_compression_type(*pdest_placement);
4021 CompressorRef plugin;
4022 boost::optional<RGWPutObj_Compress> compressor;
4023
4024 std::unique_ptr<rgw::sal::DataProcessor> encrypt;
4025
4026 if (!append) { // compression and encryption only apply to full object uploads
4027 op_ret = get_encrypt_filter(&encrypt, filter);
4028 if (op_ret < 0) {
4029 return;
4030 }
4031 if (encrypt != nullptr) {
4032 filter = &*encrypt;
4033 } else if (compression_type != "none") {
4034 plugin = get_compressor_plugin(s, compression_type);
4035 if (!plugin) {
4036 ldpp_dout(this, 1) << "Cannot load plugin for compression type "
4037 << compression_type << dendl;
4038 } else {
4039 compressor.emplace(s->cct, plugin, filter);
4040 filter = &*compressor;
4041 // always send incompressible hint when rgw is itself doing compression
4042 s->object->set_compressed(s->obj_ctx);
4043 }
4044 }
4045 }
4046 tracepoint(rgw_op, before_data_transfer, s->req_id.c_str());
4047 do {
4048 bufferlist data;
4049 if (fst > lst)
4050 break;
4051 if (copy_source.empty()) {
4052 len = get_data(data);
4053 } else {
4054 off_t cur_lst = min<off_t>(fst + s->cct->_conf->rgw_max_chunk_size - 1, lst);
4055 op_ret = get_data(fst, cur_lst, data);
4056 if (op_ret < 0)
4057 return;
4058 len = data.length();
4059 s->content_length += len;
4060 fst += len;
4061 }
4062 if (len < 0) {
4063 op_ret = len;
4064 ldpp_dout(this, 20) << "get_data() returned ret=" << op_ret << dendl;
4065 return;
4066 } else if (len == 0) {
4067 break;
4068 }
4069
4070 if (need_calc_md5) {
4071 hash.Update((const unsigned char *)data.c_str(), data.length());
4072 }
4073
4074 /* update torrrent */
4075 torrent.update(data);
4076
4077 op_ret = filter->process(std::move(data), ofs);
4078 if (op_ret < 0) {
4079 ldpp_dout(this, 20) << "processor->process() returned ret="
4080 << op_ret << dendl;
4081 return;
4082 }
4083
4084 ofs += len;
4085 } while (len > 0);
4086 tracepoint(rgw_op, after_data_transfer, s->req_id.c_str(), ofs);
4087
4088 // flush any data in filters
4089 op_ret = filter->process({}, ofs);
4090 if (op_ret < 0) {
4091 return;
4092 }
4093
4094 if (!chunked_upload && ofs != s->content_length) {
4095 op_ret = -ERR_REQUEST_TIMEOUT;
4096 return;
4097 }
4098 s->obj_size = ofs;
4099 s->object->set_obj_size(ofs);
4100
4101 perfcounter->inc(l_rgw_put_b, s->obj_size);
4102
4103 op_ret = do_aws4_auth_completion();
4104 if (op_ret < 0) {
4105 return;
4106 }
4107
4108 op_ret = s->bucket->check_quota(this, user_quota, bucket_quota, s->obj_size, y);
4109 if (op_ret < 0) {
4110 ldpp_dout(this, 20) << "second check_quota() returned op_ret=" << op_ret << dendl;
4111 return;
4112 }
4113
4114 hash.Final(m);
4115
4116 if (compressor && compressor->is_compressed()) {
4117 bufferlist tmp;
4118 RGWCompressionInfo cs_info;
4119 cs_info.compression_type = plugin->get_type_name();
4120 cs_info.orig_size = s->obj_size;
4121 cs_info.compressor_message = compressor->get_compressor_message();
4122 cs_info.blocks = move(compressor->get_compression_blocks());
4123 encode(cs_info, tmp);
4124 attrs[RGW_ATTR_COMPRESSION] = tmp;
4125 ldpp_dout(this, 20) << "storing " << RGW_ATTR_COMPRESSION
4126 << " with type=" << cs_info.compression_type
4127 << ", orig_size=" << cs_info.orig_size
4128 << ", blocks=" << cs_info.blocks.size() << dendl;
4129 }
4130
4131 buf_to_hex(m, CEPH_CRYPTO_MD5_DIGESTSIZE, calc_md5);
4132
4133 etag = calc_md5;
4134
4135 if (supplied_md5_b64 && strcmp(calc_md5, supplied_md5)) {
4136 op_ret = -ERR_BAD_DIGEST;
4137 return;
4138 }
4139
4140 policy.encode(aclbl);
4141 emplace_attr(RGW_ATTR_ACL, std::move(aclbl));
4142
4143 if (dlo_manifest) {
4144 op_ret = encode_dlo_manifest_attr(dlo_manifest, attrs);
4145 if (op_ret < 0) {
4146 ldpp_dout(this, 0) << "bad user manifest: " << dlo_manifest << dendl;
4147 return;
4148 }
4149 }
4150
4151 if (slo_info) {
4152 bufferlist manifest_bl;
4153 encode(*slo_info, manifest_bl);
4154 emplace_attr(RGW_ATTR_SLO_MANIFEST, std::move(manifest_bl));
4155 }
4156
4157 if (supplied_etag && etag.compare(supplied_etag) != 0) {
4158 op_ret = -ERR_UNPROCESSABLE_ENTITY;
4159 return;
4160 }
4161 bl.append(etag.c_str(), etag.size());
4162 emplace_attr(RGW_ATTR_ETAG, std::move(bl));
4163
4164 populate_with_generic_attrs(s, attrs);
4165 op_ret = rgw_get_request_metadata(this, s->cct, s->info, attrs);
4166 if (op_ret < 0) {
4167 return;
4168 }
4169 encode_delete_at_attr(delete_at, attrs);
4170 encode_obj_tags_attr(obj_tags.get(), attrs);
4171 rgw_cond_decode_objtags(s, attrs);
4172
4173 /* Add a custom metadata to expose the information whether an object
4174 * is an SLO or not. Appending the attribute must be performed AFTER
4175 * processing any input from user in order to prohibit overwriting. */
4176 if (slo_info) {
4177 bufferlist slo_userindicator_bl;
4178 slo_userindicator_bl.append("True", 4);
4179 emplace_attr(RGW_ATTR_SLO_UINDICATOR, std::move(slo_userindicator_bl));
4180 }
4181 if (obj_legal_hold) {
4182 bufferlist obj_legal_hold_bl;
4183 obj_legal_hold->encode(obj_legal_hold_bl);
4184 emplace_attr(RGW_ATTR_OBJECT_LEGAL_HOLD, std::move(obj_legal_hold_bl));
4185 }
4186 if (obj_retention) {
4187 bufferlist obj_retention_bl;
4188 obj_retention->encode(obj_retention_bl);
4189 emplace_attr(RGW_ATTR_OBJECT_RETENTION, std::move(obj_retention_bl));
4190 }
4191
4192 tracepoint(rgw_op, processor_complete_enter, s->req_id.c_str());
4193 op_ret = processor->complete(s->obj_size, etag, &mtime, real_time(), attrs,
4194 (delete_at ? *delete_at : real_time()), if_match, if_nomatch,
4195 (user_data.empty() ? nullptr : &user_data), nullptr, nullptr,
4196 s->yield);
4197 tracepoint(rgw_op, processor_complete_exit, s->req_id.c_str());
4198
4199 /* produce torrent */
4200 if (s->cct->_conf->rgw_torrent_flag && (ofs == torrent.get_data_len()))
4201 {
4202 torrent.init(s, store);
4203 torrent.set_create_date(mtime);
4204 op_ret = torrent.complete(y);
4205 if (0 != op_ret)
4206 {
4207 ldpp_dout(this, 0) << "ERROR: torrent.handle_data() returned " << op_ret << dendl;
4208 return;
4209 }
4210 }
4211
4212 // send request to notification manager
4213 int ret = res->publish_commit(this, s->obj_size, mtime, etag, s->object->get_instance());
4214 if (ret < 0) {
4215 ldpp_dout(this, 1) << "ERROR: publishing notification failed, with error: " << ret << dendl;
4216 // too late to rollback operation, hence op_ret is not set here
4217 }
4218 }
4219
4220 int RGWPostObj::verify_permission(optional_yield y)
4221 {
4222 return 0;
4223 }
4224
4225 void RGWPostObj::pre_exec()
4226 {
4227 rgw_bucket_object_pre_exec(s);
4228 }
4229
4230 void RGWPostObj::execute(optional_yield y)
4231 {
4232 boost::optional<RGWPutObj_Compress> compressor;
4233 CompressorRef plugin;
4234 char supplied_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
4235
4236 /* Read in the data from the POST form. */
4237 op_ret = get_params(y);
4238 if (op_ret < 0) {
4239 return;
4240 }
4241
4242 op_ret = verify_params();
4243 if (op_ret < 0) {
4244 return;
4245 }
4246
4247 if (s->iam_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
4248 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
4249 rgw::IAM::s3PutObject,
4250 s->object->get_obj());
4251 if (identity_policy_res == Effect::Deny) {
4252 op_ret = -EACCES;
4253 return;
4254 }
4255
4256 rgw::IAM::Effect e = Effect::Pass;
4257 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
4258 if (s->iam_policy) {
4259 ARN obj_arn(s->object->get_obj());
4260 e = s->iam_policy->eval(s->env, *s->auth.identity,
4261 rgw::IAM::s3PutObject,
4262 obj_arn,
4263 princ_type);
4264 }
4265 if (e == Effect::Deny) {
4266 op_ret = -EACCES;
4267 return;
4268 }
4269
4270 if (!s->session_policies.empty()) {
4271 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
4272 rgw::IAM::s3PutObject,
4273 s->object->get_obj());
4274 if (session_policy_res == Effect::Deny) {
4275 op_ret = -EACCES;
4276 return;
4277 }
4278 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
4279 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
4280 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
4281 (session_policy_res == Effect::Allow && e == Effect::Allow)) {
4282 op_ret = 0;
4283 return;
4284 }
4285 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
4286 //Intersection of session policy and identity policy plus bucket policy
4287 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) {
4288 op_ret = 0;
4289 return;
4290 }
4291 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
4292 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) {
4293 op_ret = 0;
4294 return;
4295 }
4296 }
4297 op_ret = -EACCES;
4298 return;
4299 }
4300 if (identity_policy_res == Effect::Pass && e == Effect::Pass && !verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
4301 op_ret = -EACCES;
4302 return;
4303 }
4304 } else if (!verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
4305 op_ret = -EACCES;
4306 return;
4307 }
4308
4309 // make reservation for notification if needed
4310 std::unique_ptr<rgw::sal::Notification> res
4311 = store->get_notification(s->object.get(), s->src_object.get(), s, rgw::notify::ObjectCreatedPost);
4312 op_ret = res->publish_reserve(this);
4313 if (op_ret < 0) {
4314 return;
4315 }
4316
4317 /* Start iteration over data fields. It's necessary as Swift's FormPost
4318 * is capable to handle multiple files in single form. */
4319 do {
4320 char calc_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
4321 unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE];
4322 MD5 hash;
4323 // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
4324 hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
4325 ceph::buffer::list bl, aclbl;
4326 int len = 0;
4327
4328 op_ret = s->bucket->check_quota(this, user_quota, bucket_quota, s->content_length, y);
4329 if (op_ret < 0) {
4330 return;
4331 }
4332
4333 if (supplied_md5_b64) {
4334 char supplied_md5_bin[CEPH_CRYPTO_MD5_DIGESTSIZE + 1];
4335 ldpp_dout(this, 15) << "supplied_md5_b64=" << supplied_md5_b64 << dendl;
4336 op_ret = ceph_unarmor(supplied_md5_bin, &supplied_md5_bin[CEPH_CRYPTO_MD5_DIGESTSIZE + 1],
4337 supplied_md5_b64, supplied_md5_b64 + strlen(supplied_md5_b64));
4338 ldpp_dout(this, 15) << "ceph_armor ret=" << op_ret << dendl;
4339 if (op_ret != CEPH_CRYPTO_MD5_DIGESTSIZE) {
4340 op_ret = -ERR_INVALID_DIGEST;
4341 return;
4342 }
4343
4344 buf_to_hex((const unsigned char *)supplied_md5_bin, CEPH_CRYPTO_MD5_DIGESTSIZE, supplied_md5);
4345 ldpp_dout(this, 15) << "supplied_md5=" << supplied_md5 << dendl;
4346 }
4347
4348 std::unique_ptr<rgw::sal::Object> obj =
4349 s->bucket->get_object(rgw_obj_key(get_current_filename()));
4350 if (s->bucket->versioning_enabled()) {
4351 obj->gen_rand_obj_instance_name();
4352 }
4353
4354 auto aio = rgw::make_throttle(s->cct->_conf->rgw_put_obj_min_window_size,
4355 s->yield);
4356
4357 std::unique_ptr<rgw::sal::Writer> processor;
4358 processor = store->get_atomic_writer(this, s->yield, std::move(obj),
4359 s->bucket_owner.get_id(), *s->obj_ctx,
4360 &s->dest_placement, 0, s->req_id);
4361 op_ret = processor->prepare(s->yield);
4362 if (op_ret < 0) {
4363 return;
4364 }
4365
4366 /* No filters by default. */
4367 rgw::sal::DataProcessor *filter = processor.get();
4368
4369 std::unique_ptr<rgw::sal::DataProcessor> encrypt;
4370 op_ret = get_encrypt_filter(&encrypt, filter);
4371 if (op_ret < 0) {
4372 return;
4373 }
4374 if (encrypt != nullptr) {
4375 filter = encrypt.get();
4376 } else {
4377 const auto& compression_type = store->get_zone()->get_params().get_compression_type(
4378 s->dest_placement);
4379 if (compression_type != "none") {
4380 plugin = Compressor::create(s->cct, compression_type);
4381 if (!plugin) {
4382 ldpp_dout(this, 1) << "Cannot load plugin for compression type "
4383 << compression_type << dendl;
4384 } else {
4385 compressor.emplace(s->cct, plugin, filter);
4386 filter = &*compressor;
4387 }
4388 }
4389 }
4390
4391 bool again;
4392 do {
4393 ceph::bufferlist data;
4394 len = get_data(data, again);
4395
4396 if (len < 0) {
4397 op_ret = len;
4398 return;
4399 }
4400
4401 if (!len) {
4402 break;
4403 }
4404
4405 hash.Update((const unsigned char *)data.c_str(), data.length());
4406 op_ret = filter->process(std::move(data), ofs);
4407
4408 ofs += len;
4409
4410 if (ofs > max_len) {
4411 op_ret = -ERR_TOO_LARGE;
4412 return;
4413 }
4414 } while (again);
4415
4416 // flush
4417 op_ret = filter->process({}, ofs);
4418 if (op_ret < 0) {
4419 return;
4420 }
4421
4422 if (len < min_len) {
4423 op_ret = -ERR_TOO_SMALL;
4424 return;
4425 }
4426
4427 s->obj_size = ofs;
4428 s->object->set_obj_size(ofs);
4429
4430
4431 op_ret = s->bucket->check_quota(this, user_quota, bucket_quota, s->obj_size, y);
4432 if (op_ret < 0) {
4433 return;
4434 }
4435
4436 hash.Final(m);
4437 buf_to_hex(m, CEPH_CRYPTO_MD5_DIGESTSIZE, calc_md5);
4438
4439 etag = calc_md5;
4440
4441 if (supplied_md5_b64 && strcmp(calc_md5, supplied_md5)) {
4442 op_ret = -ERR_BAD_DIGEST;
4443 return;
4444 }
4445
4446 bl.append(etag.c_str(), etag.size());
4447 emplace_attr(RGW_ATTR_ETAG, std::move(bl));
4448
4449 policy.encode(aclbl);
4450 emplace_attr(RGW_ATTR_ACL, std::move(aclbl));
4451
4452 const std::string content_type = get_current_content_type();
4453 if (! content_type.empty()) {
4454 ceph::bufferlist ct_bl;
4455 ct_bl.append(content_type.c_str(), content_type.size() + 1);
4456 emplace_attr(RGW_ATTR_CONTENT_TYPE, std::move(ct_bl));
4457 }
4458
4459 if (compressor && compressor->is_compressed()) {
4460 ceph::bufferlist tmp;
4461 RGWCompressionInfo cs_info;
4462 cs_info.compression_type = plugin->get_type_name();
4463 cs_info.orig_size = s->obj_size;
4464 cs_info.compressor_message = compressor->get_compressor_message();
4465 cs_info.blocks = move(compressor->get_compression_blocks());
4466 encode(cs_info, tmp);
4467 emplace_attr(RGW_ATTR_COMPRESSION, std::move(tmp));
4468 }
4469
4470 op_ret = processor->complete(s->obj_size, etag, nullptr, real_time(), attrs,
4471 (delete_at ? *delete_at : real_time()),
4472 nullptr, nullptr, nullptr, nullptr, nullptr,
4473 s->yield);
4474 if (op_ret < 0) {
4475 return;
4476 }
4477 } while (is_next_file_to_upload());
4478
4479 // send request to notification manager
4480 int ret = res->publish_commit(this, ofs, s->object->get_mtime(), etag, s->object->get_instance());
4481 if (ret < 0) {
4482 ldpp_dout(this, 1) << "ERROR: publishing notification failed, with error: " << ret << dendl;
4483 // too late to rollback operation, hence op_ret is not set here
4484 }
4485 }
4486
4487
4488 void RGWPutMetadataAccount::filter_out_temp_url(map<string, bufferlist>& add_attrs,
4489 const set<string>& rmattr_names,
4490 map<int, string>& temp_url_keys)
4491 {
4492 map<string, bufferlist>::iterator iter;
4493
4494 iter = add_attrs.find(RGW_ATTR_TEMPURL_KEY1);
4495 if (iter != add_attrs.end()) {
4496 temp_url_keys[0] = iter->second.c_str();
4497 add_attrs.erase(iter);
4498 }
4499
4500 iter = add_attrs.find(RGW_ATTR_TEMPURL_KEY2);
4501 if (iter != add_attrs.end()) {
4502 temp_url_keys[1] = iter->second.c_str();
4503 add_attrs.erase(iter);
4504 }
4505
4506 for (const string& name : rmattr_names) {
4507 if (name.compare(RGW_ATTR_TEMPURL_KEY1) == 0) {
4508 temp_url_keys[0] = string();
4509 }
4510 if (name.compare(RGW_ATTR_TEMPURL_KEY2) == 0) {
4511 temp_url_keys[1] = string();
4512 }
4513 }
4514 }
4515
4516 int RGWPutMetadataAccount::init_processing(optional_yield y)
4517 {
4518 /* First, go to the base class. At the time of writing the method was
4519 * responsible only for initializing the quota. This isn't necessary
4520 * here as we are touching metadata only. I'm putting this call only
4521 * for the future. */
4522 op_ret = RGWOp::init_processing(y);
4523 if (op_ret < 0) {
4524 return op_ret;
4525 }
4526
4527 op_ret = get_params(y);
4528 if (op_ret < 0) {
4529 return op_ret;
4530 }
4531
4532 op_ret = s->user->read_attrs(this, y);
4533 if (op_ret < 0) {
4534 return op_ret;
4535 }
4536 orig_attrs = s->user->get_attrs();
4537
4538 if (has_policy) {
4539 bufferlist acl_bl;
4540 policy.encode(acl_bl);
4541 attrs.emplace(RGW_ATTR_ACL, std::move(acl_bl));
4542 }
4543
4544 op_ret = rgw_get_request_metadata(this, s->cct, s->info, attrs, false);
4545 if (op_ret < 0) {
4546 return op_ret;
4547 }
4548 prepare_add_del_attrs(orig_attrs, rmattr_names, attrs);
4549 populate_with_generic_attrs(s, attrs);
4550
4551 /* Try extract the TempURL-related stuff now to allow verify_permission
4552 * evaluate whether we need FULL_CONTROL or not. */
4553 filter_out_temp_url(attrs, rmattr_names, temp_url_keys);
4554
4555 /* The same with quota except a client needs to be reseller admin. */
4556 op_ret = filter_out_quota_info(attrs, rmattr_names, new_quota,
4557 &new_quota_extracted);
4558 if (op_ret < 0) {
4559 return op_ret;
4560 }
4561
4562 return 0;
4563 }
4564
4565 int RGWPutMetadataAccount::verify_permission(optional_yield y)
4566 {
4567 if (s->auth.identity->is_anonymous()) {
4568 return -EACCES;
4569 }
4570
4571 if (!verify_user_permission_no_policy(this, s, RGW_PERM_WRITE)) {
4572 return -EACCES;
4573 }
4574
4575 /* Altering TempURL keys requires FULL_CONTROL. */
4576 if (!temp_url_keys.empty() && s->perm_mask != RGW_PERM_FULL_CONTROL) {
4577 return -EPERM;
4578 }
4579
4580 /* We are failing this intensionally to allow system user/reseller admin
4581 * override in rgw_process.cc. This is the way to specify a given RGWOp
4582 * expect extra privileges. */
4583 if (new_quota_extracted) {
4584 return -EACCES;
4585 }
4586
4587 return 0;
4588 }
4589
4590 void RGWPutMetadataAccount::execute(optional_yield y)
4591 {
4592 /* Params have been extracted earlier. See init_processing(). */
4593 op_ret = s->user->load_user(this, y);
4594 if (op_ret < 0) {
4595 return;
4596 }
4597
4598 /* Handle the TempURL-related stuff. */
4599 if (!temp_url_keys.empty()) {
4600 for (auto& pair : temp_url_keys) {
4601 s->user->get_info().temp_url_keys[pair.first] = std::move(pair.second);
4602 }
4603 }
4604
4605 /* Handle the quota extracted at the verify_permission step. */
4606 if (new_quota_extracted) {
4607 s->user->get_info().user_quota = std::move(new_quota);
4608 }
4609
4610 /* We are passing here the current (old) user info to allow the function
4611 * optimize-out some operations. */
4612 s->user->set_attrs(attrs);
4613 op_ret = s->user->store_user(this, y, false, &s->user->get_info());
4614 }
4615
4616 int RGWPutMetadataBucket::verify_permission(optional_yield y)
4617 {
4618 if (!verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
4619 return -EACCES;
4620 }
4621
4622 return 0;
4623 }
4624
4625 void RGWPutMetadataBucket::pre_exec()
4626 {
4627 rgw_bucket_object_pre_exec(s);
4628 }
4629
4630 void RGWPutMetadataBucket::execute(optional_yield y)
4631 {
4632 op_ret = get_params(y);
4633 if (op_ret < 0) {
4634 return;
4635 }
4636
4637 op_ret = rgw_get_request_metadata(this, s->cct, s->info, attrs, false);
4638 if (op_ret < 0) {
4639 return;
4640 }
4641
4642 if (!placement_rule.empty() &&
4643 placement_rule != s->bucket->get_placement_rule()) {
4644 op_ret = -EEXIST;
4645 return;
4646 }
4647
4648 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
4649 /* Encode special metadata first as we're using std::map::emplace under
4650 * the hood. This method will add the new items only if the map doesn't
4651 * contain such keys yet. */
4652 if (has_policy) {
4653 if (s->dialect.compare("swift") == 0) {
4654 auto old_policy = \
4655 static_cast<RGWAccessControlPolicy_SWIFT*>(s->bucket_acl.get());
4656 auto new_policy = static_cast<RGWAccessControlPolicy_SWIFT*>(&policy);
4657 new_policy->filter_merge(policy_rw_mask, old_policy);
4658 policy = *new_policy;
4659 }
4660 buffer::list bl;
4661 policy.encode(bl);
4662 emplace_attr(RGW_ATTR_ACL, std::move(bl));
4663 }
4664
4665 if (has_cors) {
4666 buffer::list bl;
4667 cors_config.encode(bl);
4668 emplace_attr(RGW_ATTR_CORS, std::move(bl));
4669 }
4670
4671 /* It's supposed that following functions WILL NOT change any
4672 * special attributes (like RGW_ATTR_ACL) if they are already
4673 * present in attrs. */
4674 prepare_add_del_attrs(s->bucket_attrs, rmattr_names, attrs);
4675 populate_with_generic_attrs(s, attrs);
4676
4677 /* According to the Swift's behaviour and its container_quota
4678 * WSGI middleware implementation: anyone with write permissions
4679 * is able to set the bucket quota. This stays in contrast to
4680 * account quotas that can be set only by clients holding
4681 * reseller admin privileges. */
4682 op_ret = filter_out_quota_info(attrs, rmattr_names, s->bucket->get_info().quota);
4683 if (op_ret < 0) {
4684 return op_ret;
4685 }
4686
4687 if (swift_ver_location) {
4688 s->bucket->get_info().swift_ver_location = *swift_ver_location;
4689 s->bucket->get_info().swift_versioning = (!swift_ver_location->empty());
4690 }
4691
4692 /* Web site of Swift API. */
4693 filter_out_website(attrs, rmattr_names, s->bucket->get_info().website_conf);
4694 s->bucket->get_info().has_website = !s->bucket->get_info().website_conf.is_empty();
4695
4696 /* Setting attributes also stores the provided bucket info. Due
4697 * to this fact, the new quota settings can be serialized with
4698 * the same call. */
4699 op_ret = s->bucket->merge_and_store_attrs(this, attrs, s->yield);
4700 return op_ret;
4701 });
4702 }
4703
4704 int RGWPutMetadataObject::verify_permission(optional_yield y)
4705 {
4706 // This looks to be something specific to Swift. We could add
4707 // operations like swift:PutMetadataObject to the Policy Engine.
4708 if (!verify_object_permission_no_policy(this, s, RGW_PERM_WRITE)) {
4709 return -EACCES;
4710 }
4711
4712 return 0;
4713 }
4714
4715 void RGWPutMetadataObject::pre_exec()
4716 {
4717 rgw_bucket_object_pre_exec(s);
4718 }
4719
4720 void RGWPutMetadataObject::execute(optional_yield y)
4721 {
4722 rgw_obj target_obj;
4723 rgw::sal::Attrs attrs, rmattrs;
4724
4725 s->object->set_atomic(s->obj_ctx);
4726
4727 op_ret = get_params(y);
4728 if (op_ret < 0) {
4729 return;
4730 }
4731
4732 op_ret = rgw_get_request_metadata(this, s->cct, s->info, attrs);
4733 if (op_ret < 0) {
4734 return;
4735 }
4736
4737 /* check if obj exists, read orig attrs */
4738 op_ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, s, &target_obj);
4739 if (op_ret < 0) {
4740 return;
4741 }
4742
4743 /* Check whether the object has expired. Swift API documentation
4744 * stands that we should return 404 Not Found in such case. */
4745 if (need_object_expiration() && s->object->is_expired()) {
4746 op_ret = -ENOENT;
4747 return;
4748 }
4749
4750 /* Filter currently existing attributes. */
4751 prepare_add_del_attrs(s->object->get_attrs(), attrs, rmattrs);
4752 populate_with_generic_attrs(s, attrs);
4753 encode_delete_at_attr(delete_at, attrs);
4754
4755 if (dlo_manifest) {
4756 op_ret = encode_dlo_manifest_attr(dlo_manifest, attrs);
4757 if (op_ret < 0) {
4758 ldpp_dout(this, 0) << "bad user manifest: " << dlo_manifest << dendl;
4759 return;
4760 }
4761 }
4762
4763 op_ret = s->object->set_obj_attrs(this, s->obj_ctx, &attrs, &rmattrs, s->yield, &target_obj);
4764 }
4765
4766 int RGWDeleteObj::handle_slo_manifest(bufferlist& bl, optional_yield y)
4767 {
4768 RGWSLOInfo slo_info;
4769 auto bliter = bl.cbegin();
4770 try {
4771 decode(slo_info, bliter);
4772 } catch (buffer::error& err) {
4773 ldpp_dout(this, 0) << "ERROR: failed to decode slo manifest" << dendl;
4774 return -EIO;
4775 }
4776
4777 try {
4778 deleter = std::unique_ptr<RGWBulkDelete::Deleter>(\
4779 new RGWBulkDelete::Deleter(this, store, s));
4780 } catch (const std::bad_alloc&) {
4781 return -ENOMEM;
4782 }
4783
4784 list<RGWBulkDelete::acct_path_t> items;
4785 for (const auto& iter : slo_info.entries) {
4786 const string& path_str = iter.path;
4787
4788 const size_t sep_pos = path_str.find('/', 1 /* skip first slash */);
4789 if (std::string_view::npos == sep_pos) {
4790 return -EINVAL;
4791 }
4792
4793 RGWBulkDelete::acct_path_t path;
4794
4795 path.bucket_name = url_decode(path_str.substr(1, sep_pos - 1));
4796 path.obj_key = url_decode(path_str.substr(sep_pos + 1));
4797
4798 items.push_back(path);
4799 }
4800
4801 /* Request removal of the manifest object itself. */
4802 RGWBulkDelete::acct_path_t path;
4803 path.bucket_name = s->bucket_name;
4804 path.obj_key = s->object->get_key();
4805 items.push_back(path);
4806
4807 int ret = deleter->delete_chunk(items, y);
4808 if (ret < 0) {
4809 return ret;
4810 }
4811
4812 return 0;
4813 }
4814
4815 int RGWDeleteObj::verify_permission(optional_yield y)
4816 {
4817 int op_ret = get_params(y);
4818 if (op_ret) {
4819 return op_ret;
4820 }
4821
4822 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
4823 if (has_s3_existing_tag || has_s3_resource_tag)
4824 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
4825
4826 if (s->iam_policy || ! s->iam_user_policies.empty() || ! s->session_policies.empty()) {
4827 if (s->bucket->get_info().obj_lock_enabled() && bypass_governance_mode) {
4828 auto r = eval_identity_or_session_policies(s->iam_user_policies, s->env,
4829 rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket->get_key(), s->object->get_name()));
4830 if (r == Effect::Deny) {
4831 bypass_perm = false;
4832 } else if (r == Effect::Pass && s->iam_policy) {
4833 ARN obj_arn(ARN(s->bucket->get_key(), s->object->get_name()));
4834 r = s->iam_policy->eval(s->env, *s->auth.identity, rgw::IAM::s3BypassGovernanceRetention, obj_arn);
4835 if (r == Effect::Deny) {
4836 bypass_perm = false;
4837 }
4838 } else if (r == Effect::Pass && !s->session_policies.empty()) {
4839 r = eval_identity_or_session_policies(s->session_policies, s->env,
4840 rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket->get_key(), s->object->get_name()));
4841 if (r == Effect::Deny) {
4842 bypass_perm = false;
4843 }
4844 }
4845 }
4846 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
4847 s->object->get_instance().empty() ?
4848 rgw::IAM::s3DeleteObject :
4849 rgw::IAM::s3DeleteObjectVersion,
4850 ARN(s->bucket->get_key(), s->object->get_name()));
4851 if (identity_policy_res == Effect::Deny) {
4852 return -EACCES;
4853 }
4854
4855 rgw::IAM::Effect r = Effect::Pass;
4856 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
4857 ARN obj_arn(ARN(s->bucket->get_key(), s->object->get_name()));
4858 if (s->iam_policy) {
4859 r = s->iam_policy->eval(s->env, *s->auth.identity,
4860 s->object->get_instance().empty() ?
4861 rgw::IAM::s3DeleteObject :
4862 rgw::IAM::s3DeleteObjectVersion,
4863 obj_arn,
4864 princ_type);
4865 }
4866 if (r == Effect::Deny)
4867 return -EACCES;
4868
4869 if (!s->session_policies.empty()) {
4870 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
4871 s->object->get_instance().empty() ?
4872 rgw::IAM::s3DeleteObject :
4873 rgw::IAM::s3DeleteObjectVersion,
4874 obj_arn);
4875 if (session_policy_res == Effect::Deny) {
4876 return -EACCES;
4877 }
4878 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
4879 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
4880 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
4881 (session_policy_res == Effect::Allow && r == Effect::Allow)) {
4882 return 0;
4883 }
4884 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
4885 //Intersection of session policy and identity policy plus bucket policy
4886 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || r == Effect::Allow) {
4887 return 0;
4888 }
4889 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
4890 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) {
4891 return 0;
4892 }
4893 }
4894 return -EACCES;
4895 }
4896 if (r == Effect::Allow || identity_policy_res == Effect::Allow)
4897 return 0;
4898 }
4899
4900 if (!verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
4901 return -EACCES;
4902 }
4903
4904 if (s->bucket->get_info().mfa_enabled() &&
4905 !s->object->get_instance().empty() &&
4906 !s->mfa_verified) {
4907 ldpp_dout(this, 5) << "NOTICE: object delete request with a versioned object, mfa auth not provided" << dendl;
4908 return -ERR_MFA_REQUIRED;
4909 }
4910
4911 return 0;
4912 }
4913
4914 void RGWDeleteObj::pre_exec()
4915 {
4916 rgw_bucket_object_pre_exec(s);
4917 }
4918
4919 void RGWDeleteObj::execute(optional_yield y)
4920 {
4921 if (!s->bucket_exists) {
4922 op_ret = -ERR_NO_SUCH_BUCKET;
4923 return;
4924 }
4925
4926 if (!rgw::sal::Object::empty(s->object.get())) {
4927 uint64_t obj_size = 0;
4928 std::string etag;
4929 RGWObjectCtx* obj_ctx = static_cast<RGWObjectCtx *>(s->obj_ctx);
4930 {
4931 RGWObjState* astate = nullptr;
4932 bool check_obj_lock = s->object->have_instance() && s->bucket->get_info().obj_lock_enabled();
4933
4934 op_ret = s->object->get_obj_state(this, obj_ctx, &astate, s->yield, true);
4935 if (op_ret < 0) {
4936 if (need_object_expiration() || multipart_delete) {
4937 return;
4938 }
4939
4940 if (check_obj_lock) {
4941 /* check if obj exists, read orig attrs */
4942 if (op_ret == -ENOENT) {
4943 /* object maybe delete_marker, skip check_obj_lock*/
4944 check_obj_lock = false;
4945 } else {
4946 return;
4947 }
4948 }
4949 } else {
4950 obj_size = astate->size;
4951 etag = astate->attrset[RGW_ATTR_ETAG].to_str();
4952 }
4953
4954 // ignore return value from get_obj_attrs in all other cases
4955 op_ret = 0;
4956
4957 if (check_obj_lock) {
4958 ceph_assert(astate);
4959 int object_lock_response = verify_object_lock(this, astate->attrset, bypass_perm, bypass_governance_mode);
4960 if (object_lock_response != 0) {
4961 op_ret = object_lock_response;
4962 if (op_ret == -EACCES) {
4963 s->err.message = "forbidden by object lock";
4964 }
4965 return;
4966 }
4967 }
4968
4969 if (multipart_delete) {
4970 if (!astate) {
4971 op_ret = -ERR_NOT_SLO_MANIFEST;
4972 return;
4973 }
4974
4975 const auto slo_attr = astate->attrset.find(RGW_ATTR_SLO_MANIFEST);
4976
4977 if (slo_attr != astate->attrset.end()) {
4978 op_ret = handle_slo_manifest(slo_attr->second, y);
4979 if (op_ret < 0) {
4980 ldpp_dout(this, 0) << "ERROR: failed to handle slo manifest ret=" << op_ret << dendl;
4981 }
4982 } else {
4983 op_ret = -ERR_NOT_SLO_MANIFEST;
4984 }
4985
4986 return;
4987 }
4988 }
4989
4990 // make reservation for notification if needed
4991 const auto versioned_object = s->bucket->versioning_enabled();
4992 const auto event_type = versioned_object &&
4993 s->object->get_instance().empty() ?
4994 rgw::notify::ObjectRemovedDeleteMarkerCreated :
4995 rgw::notify::ObjectRemovedDelete;
4996 std::unique_ptr<rgw::sal::Notification> res
4997 = store->get_notification(s->object.get(), s->src_object.get(), s,
4998 event_type);
4999 op_ret = res->publish_reserve(this);
5000 if (op_ret < 0) {
5001 return;
5002 }
5003
5004 s->object->set_atomic(s->obj_ctx);
5005
5006 bool ver_restored = false;
5007 op_ret = s->object->swift_versioning_restore(s->obj_ctx, ver_restored, this);
5008 if (op_ret < 0) {
5009 return;
5010 }
5011
5012 if (!ver_restored) {
5013 uint64_t epoch = 0;
5014
5015 /* Swift's versioning mechanism hasn't found any previous version of
5016 * the object that could be restored. This means we should proceed
5017 * with the regular delete path. */
5018 op_ret = get_system_versioning_params(s, &epoch, &version_id);
5019 if (op_ret < 0) {
5020 return;
5021 }
5022
5023 std::unique_ptr<rgw::sal::Object::DeleteOp> del_op = s->object->get_delete_op(obj_ctx);
5024 del_op->params.obj_owner = s->owner;
5025 del_op->params.bucket_owner = s->bucket_owner;
5026 del_op->params.versioning_status = s->bucket->get_info().versioning_status();
5027 del_op->params.unmod_since = unmod_since;
5028 del_op->params.high_precision_time = s->system_request;
5029 del_op->params.olh_epoch = epoch;
5030 del_op->params.marker_version_id = version_id;
5031
5032 op_ret = del_op->delete_obj(this, y);
5033 if (op_ret >= 0) {
5034 delete_marker = del_op->result.delete_marker;
5035 version_id = del_op->result.version_id;
5036 }
5037
5038 /* Check whether the object has expired. Swift API documentation
5039 * stands that we should return 404 Not Found in such case. */
5040 if (need_object_expiration() && s->object->is_expired()) {
5041 op_ret = -ENOENT;
5042 return;
5043 }
5044 }
5045
5046 if (op_ret == -ECANCELED) {
5047 op_ret = 0;
5048 }
5049 if (op_ret == -ERR_PRECONDITION_FAILED && no_precondition_error) {
5050 op_ret = 0;
5051 }
5052
5053 // send request to notification manager
5054 int ret = res->publish_commit(this, obj_size, ceph::real_clock::now(), etag, version_id);
5055 if (ret < 0) {
5056 ldpp_dout(this, 1) << "ERROR: publishing notification failed, with error: " << ret << dendl;
5057 // too late to rollback operation, hence op_ret is not set here
5058 }
5059 } else {
5060 op_ret = -EINVAL;
5061 }
5062 }
5063
5064 bool RGWCopyObj::parse_copy_location(const std::string_view& url_src,
5065 string& bucket_name,
5066 rgw_obj_key& key,
5067 req_state* s)
5068 {
5069 std::string_view name_str;
5070 std::string_view params_str;
5071
5072 // search for ? before url-decoding so we don't accidentally match %3F
5073 size_t pos = url_src.find('?');
5074 if (pos == string::npos) {
5075 name_str = url_src;
5076 } else {
5077 name_str = url_src.substr(0, pos);
5078 params_str = url_src.substr(pos + 1);
5079 }
5080
5081 if (name_str[0] == '/') // trim leading slash
5082 name_str.remove_prefix(1);
5083
5084 std::string dec_src = url_decode(name_str);
5085
5086 pos = dec_src.find('/');
5087 if (pos == string::npos)
5088 return false;
5089
5090 bucket_name = dec_src.substr(0, pos);
5091 key.name = dec_src.substr(pos + 1);
5092
5093 if (key.name.empty()) {
5094 return false;
5095 }
5096
5097 if (! params_str.empty()) {
5098 RGWHTTPArgs args;
5099 args.set(std::string(params_str));
5100 args.parse(s);
5101
5102 key.instance = args.get("versionId", NULL);
5103 }
5104
5105 return true;
5106 }
5107
5108 int RGWCopyObj::verify_permission(optional_yield y)
5109 {
5110 RGWAccessControlPolicy src_acl(s->cct);
5111 boost::optional<Policy> src_policy;
5112 op_ret = get_params(y);
5113 if (op_ret < 0)
5114 return op_ret;
5115
5116 op_ret = get_system_versioning_params(s, &olh_epoch, &version_id);
5117 if (op_ret < 0) {
5118 return op_ret;
5119 }
5120
5121 op_ret = store->get_bucket(this, s->user.get(),
5122 rgw_bucket(src_tenant_name,
5123 src_bucket_name,
5124 s->bucket_instance_id),
5125 &src_bucket, y);
5126 if (op_ret < 0) {
5127 if (op_ret == -ENOENT) {
5128 op_ret = -ERR_NO_SUCH_BUCKET;
5129 }
5130 return op_ret;
5131 }
5132
5133 /* This is the only place the bucket is set on src_object */
5134 s->src_object->set_bucket(src_bucket.get());
5135 /* get buckets info (source and dest) */
5136 if (s->local_source && source_zone.empty()) {
5137 s->src_object->set_atomic(s->obj_ctx);
5138 s->src_object->set_prefetch_data(s->obj_ctx);
5139
5140 rgw_placement_rule src_placement;
5141
5142 /* check source object permissions */
5143 op_ret = read_obj_policy(this, store, s, src_bucket->get_info(), src_bucket->get_attrs(), &src_acl, &src_placement.storage_class,
5144 src_policy, src_bucket.get(), s->src_object.get(), y);
5145 if (op_ret < 0) {
5146 return op_ret;
5147 }
5148
5149 /* follow up on previous checks that required reading source object head */
5150 if (need_to_check_storage_class) {
5151 src_placement.inherit_from(src_bucket->get_placement_rule());
5152
5153 op_ret = check_storage_class(src_placement);
5154 if (op_ret < 0) {
5155 return op_ret;
5156 }
5157 }
5158
5159 /* admin request overrides permission checks */
5160 if (!s->auth.identity->is_admin_of(src_acl.get_owner().get_id())) {
5161 if (src_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
5162 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, src_policy, s->iam_user_policies, s->session_policies);
5163 if (has_s3_existing_tag || has_s3_resource_tag)
5164 rgw_iam_add_objtags(this, s, s->src_object.get(), has_s3_existing_tag, has_s3_resource_tag);
5165
5166 ARN obj_arn(s->src_object->get_obj());
5167 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
5168 s->src_object->get_instance().empty() ?
5169 rgw::IAM::s3GetObject :
5170 rgw::IAM::s3GetObjectVersion,
5171 obj_arn);
5172 if (identity_policy_res == Effect::Deny) {
5173 return -EACCES;
5174 }
5175 auto e = Effect::Pass;
5176 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
5177 if (src_policy) {
5178 e = src_policy->eval(s->env, *s->auth.identity,
5179 s->src_object->get_instance().empty() ?
5180 rgw::IAM::s3GetObject :
5181 rgw::IAM::s3GetObjectVersion,
5182 obj_arn,
5183 princ_type);
5184 }
5185 if (e == Effect::Deny) {
5186 return -EACCES;
5187 }
5188 if (!s->session_policies.empty()) {
5189 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
5190 s->src_object->get_instance().empty() ?
5191 rgw::IAM::s3GetObject :
5192 rgw::IAM::s3GetObjectVersion,
5193 obj_arn);
5194 if (session_policy_res == Effect::Deny) {
5195 return -EACCES;
5196 }
5197 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
5198 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
5199 if ((session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) &&
5200 (session_policy_res != Effect::Allow || e != Effect::Allow)) {
5201 return -EACCES;
5202 }
5203 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
5204 //Intersection of session policy and identity policy plus bucket policy
5205 if ((session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) && e != Effect::Allow) {
5206 return -EACCES;
5207 }
5208 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
5209 if (session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) {
5210 return -EACCES;
5211 }
5212 }
5213 }
5214 if (identity_policy_res == Effect::Pass && e == Effect::Pass &&
5215 !src_acl.verify_permission(this, *s->auth.identity, s->perm_mask,
5216 RGW_PERM_READ)) {
5217 return -EACCES;
5218 }
5219 //remove src object tags as it may interfere with policy evaluation of destination obj
5220 if (has_s3_existing_tag || has_s3_resource_tag)
5221 rgw_iam_remove_objtags(this, s, s->src_object.get(), has_s3_existing_tag, has_s3_resource_tag);
5222
5223 } else if (!src_acl.verify_permission(this, *s->auth.identity,
5224 s->perm_mask,
5225 RGW_PERM_READ)) {
5226 return -EACCES;
5227 }
5228 }
5229 }
5230
5231 RGWAccessControlPolicy dest_bucket_policy(s->cct);
5232
5233 if (src_bucket_name.compare(dest_bucket_name) == 0) { /* will only happen if s->local_source
5234 or intra region sync */
5235 dest_bucket = src_bucket->clone();
5236 } else {
5237 op_ret = store->get_bucket(this, s->user.get(), dest_tenant_name, dest_bucket_name, &dest_bucket, y);
5238 if (op_ret < 0) {
5239 if (op_ret == -ENOENT) {
5240 ldpp_dout(this, 0) << "ERROR: Destination Bucket not found for user: " << s->user->get_id().to_str() << dendl;
5241 op_ret = -ERR_NO_SUCH_BUCKET;
5242 }
5243 return op_ret;
5244 }
5245 }
5246
5247 dest_object = dest_bucket->get_object(rgw_obj_key(dest_obj_name));
5248 dest_object->set_atomic(s->obj_ctx);
5249
5250 /* check dest bucket permissions */
5251 op_ret = read_bucket_policy(this, store, s, dest_bucket->get_info(),
5252 dest_bucket->get_attrs(),
5253 &dest_bucket_policy, dest_bucket->get_key(), y);
5254 if (op_ret < 0) {
5255 return op_ret;
5256 }
5257 auto dest_iam_policy = get_iam_policy_from_attr(s->cct, dest_bucket->get_attrs(), dest_bucket->get_tenant());
5258 /* admin request overrides permission checks */
5259 if (! s->auth.identity->is_admin_of(dest_policy.get_owner().get_id())){
5260 if (dest_iam_policy != boost::none || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
5261 //Add destination bucket tags for authorization
5262 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, dest_iam_policy, s->iam_user_policies, s->session_policies);
5263 if (has_s3_resource_tag)
5264 rgw_iam_add_buckettags(this, s, dest_bucket.get());
5265
5266 rgw_add_to_iam_environment(s->env, "s3:x-amz-copy-source", copy_source);
5267 if (md_directive)
5268 rgw_add_to_iam_environment(s->env, "s3:x-amz-metadata-directive",
5269 *md_directive);
5270
5271 ARN obj_arn(dest_object->get_obj());
5272 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies,
5273 s->env,
5274 rgw::IAM::s3PutObject,
5275 obj_arn);
5276 if (identity_policy_res == Effect::Deny) {
5277 return -EACCES;
5278 }
5279 auto e = Effect::Pass;
5280 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
5281 if (dest_iam_policy) {
5282 e = dest_iam_policy->eval(s->env, *s->auth.identity,
5283 rgw::IAM::s3PutObject,
5284 obj_arn,
5285 princ_type);
5286 }
5287 if (e == Effect::Deny) {
5288 return -EACCES;
5289 }
5290 if (!s->session_policies.empty()) {
5291 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env, rgw::IAM::s3PutObject, obj_arn);
5292 if (session_policy_res == Effect::Deny) {
5293 return false;
5294 }
5295 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
5296 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
5297 if ((session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) &&
5298 (session_policy_res != Effect::Allow || e == Effect::Allow)) {
5299 return -EACCES;
5300 }
5301 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
5302 //Intersection of session policy and identity policy plus bucket policy
5303 if ((session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) && e != Effect::Allow) {
5304 return -EACCES;
5305 }
5306 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
5307 if (session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) {
5308 return -EACCES;
5309 }
5310 }
5311 }
5312 if (identity_policy_res == Effect::Pass && e == Effect::Pass &&
5313 ! dest_bucket_policy.verify_permission(this,
5314 *s->auth.identity,
5315 s->perm_mask,
5316 RGW_PERM_WRITE)){
5317 return -EACCES;
5318 }
5319 } else if (! dest_bucket_policy.verify_permission(this, *s->auth.identity, s->perm_mask,
5320 RGW_PERM_WRITE)) {
5321 return -EACCES;
5322 }
5323
5324 }
5325
5326 op_ret = init_dest_policy();
5327 if (op_ret < 0) {
5328 return op_ret;
5329 }
5330
5331 return 0;
5332 }
5333
5334
5335 int RGWCopyObj::init_common()
5336 {
5337 if (if_mod) {
5338 if (parse_time(if_mod, &mod_time) < 0) {
5339 op_ret = -EINVAL;
5340 return op_ret;
5341 }
5342 mod_ptr = &mod_time;
5343 }
5344
5345 if (if_unmod) {
5346 if (parse_time(if_unmod, &unmod_time) < 0) {
5347 op_ret = -EINVAL;
5348 return op_ret;
5349 }
5350 unmod_ptr = &unmod_time;
5351 }
5352
5353 bufferlist aclbl;
5354 dest_policy.encode(aclbl);
5355 emplace_attr(RGW_ATTR_ACL, std::move(aclbl));
5356
5357 op_ret = rgw_get_request_metadata(this, s->cct, s->info, attrs);
5358 if (op_ret < 0) {
5359 return op_ret;
5360 }
5361 populate_with_generic_attrs(s, attrs);
5362
5363 return 0;
5364 }
5365
5366 static void copy_obj_progress_cb(off_t ofs, void *param)
5367 {
5368 RGWCopyObj *op = static_cast<RGWCopyObj *>(param);
5369 op->progress_cb(ofs);
5370 }
5371
5372 void RGWCopyObj::progress_cb(off_t ofs)
5373 {
5374 if (!s->cct->_conf->rgw_copy_obj_progress)
5375 return;
5376
5377 if (ofs - last_ofs <
5378 static_cast<off_t>(s->cct->_conf->rgw_copy_obj_progress_every_bytes)) {
5379 return;
5380 }
5381
5382 send_partial_response(ofs);
5383
5384 last_ofs = ofs;
5385 }
5386
5387 void RGWCopyObj::pre_exec()
5388 {
5389 rgw_bucket_object_pre_exec(s);
5390 }
5391
5392 void RGWCopyObj::execute(optional_yield y)
5393 {
5394 if (init_common() < 0)
5395 return;
5396
5397 // make reservation for notification if needed
5398 std::unique_ptr<rgw::sal::Notification> res
5399 = store->get_notification(
5400 s->object.get(), s->src_object.get(),
5401 s, rgw::notify::ObjectCreatedCopy);
5402 op_ret = res->publish_reserve(this);
5403 if (op_ret < 0) {
5404 return;
5405 }
5406
5407 if ( ! version_id.empty()) {
5408 dest_object->set_instance(version_id);
5409 } else if (dest_bucket->versioning_enabled()) {
5410 dest_object->gen_rand_obj_instance_name();
5411 }
5412
5413 s->src_object->set_atomic(s->obj_ctx);
5414 dest_object->set_atomic(s->obj_ctx);
5415
5416 encode_delete_at_attr(delete_at, attrs);
5417
5418 if (obj_retention) {
5419 bufferlist obj_retention_bl;
5420 obj_retention->encode(obj_retention_bl);
5421 emplace_attr(RGW_ATTR_OBJECT_RETENTION, std::move(obj_retention_bl));
5422 }
5423 if (obj_legal_hold) {
5424 bufferlist obj_legal_hold_bl;
5425 obj_legal_hold->encode(obj_legal_hold_bl);
5426 emplace_attr(RGW_ATTR_OBJECT_LEGAL_HOLD, std::move(obj_legal_hold_bl));
5427 }
5428
5429 uint64_t obj_size = 0;
5430 {
5431 // get src object size (cached in obj_ctx from verify_permission())
5432 RGWObjState* astate = nullptr;
5433 op_ret = s->src_object->get_obj_state(this, s->obj_ctx, &astate, s->yield, true);
5434 if (op_ret < 0) {
5435 return;
5436 }
5437
5438 /* Check if the src object is cloud-tiered */
5439 bufferlist bl;
5440 if (astate->get_attr(RGW_ATTR_MANIFEST, bl)) {
5441 RGWObjManifest m;
5442 decode(m, bl);
5443 if (m.get_tier_type() == "cloud-s3") {
5444 op_ret = -ERR_INVALID_OBJECT_STATE;
5445 ldpp_dout(this, 0) << "ERROR: Cannot copy cloud tiered object. Failing with "
5446 << op_ret << dendl;
5447 return;
5448 }
5449 }
5450
5451 obj_size = astate->size;
5452
5453 if (!s->system_request) { // no quota enforcement for system requests
5454 if (astate->accounted_size > static_cast<size_t>(s->cct->_conf->rgw_max_put_size)) {
5455 op_ret = -ERR_TOO_LARGE;
5456 return;
5457 }
5458 // enforce quota against the destination bucket owner
5459 op_ret = dest_bucket->check_quota(this, user_quota, bucket_quota,
5460 astate->accounted_size, y);
5461 if (op_ret < 0) {
5462 return;
5463 }
5464 }
5465 }
5466
5467 bool high_precision_time = (s->system_request);
5468
5469 /* Handle object versioning of Swift API. In case of copying to remote this
5470 * should fail gently (op_ret == 0) as the dst_obj will not exist here. */
5471 op_ret = dest_object->swift_versioning_copy(s->obj_ctx, this, s->yield);
5472 if (op_ret < 0) {
5473 return;
5474 }
5475
5476 RGWObjectCtx& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
5477 op_ret = s->src_object->copy_object(obj_ctx,
5478 s->user.get(),
5479 &s->info,
5480 source_zone,
5481 dest_object.get(),
5482 dest_bucket.get(),
5483 src_bucket.get(),
5484 s->dest_placement,
5485 &src_mtime,
5486 &mtime,
5487 mod_ptr,
5488 unmod_ptr,
5489 high_precision_time,
5490 if_match,
5491 if_nomatch,
5492 attrs_mod,
5493 copy_if_newer,
5494 attrs,
5495 RGWObjCategory::Main,
5496 olh_epoch,
5497 delete_at,
5498 (version_id.empty() ? NULL : &version_id),
5499 &s->req_id, /* use req_id as tag */
5500 &etag,
5501 copy_obj_progress_cb, (void *)this,
5502 this,
5503 s->yield);
5504
5505 // send request to notification manager
5506 int ret = res->publish_commit(this, obj_size, mtime, etag, dest_object->get_instance());
5507 if (ret < 0) {
5508 ldpp_dout(this, 1) << "ERROR: publishing notification failed, with error: " << ret << dendl;
5509 // too late to rollback operation, hence op_ret is not set here
5510 }
5511 }
5512
5513 int RGWGetACLs::verify_permission(optional_yield y)
5514 {
5515 bool perm;
5516 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
5517 if (!rgw::sal::Object::empty(s->object.get())) {
5518 auto iam_action = s->object->get_instance().empty() ?
5519 rgw::IAM::s3GetObjectAcl :
5520 rgw::IAM::s3GetObjectVersionAcl;
5521 if (has_s3_existing_tag || has_s3_resource_tag)
5522 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
5523 perm = verify_object_permission(this, s, iam_action);
5524 } else {
5525 if (!s->bucket_exists) {
5526 return -ERR_NO_SUCH_BUCKET;
5527 }
5528 if (has_s3_resource_tag)
5529 rgw_iam_add_buckettags(this, s);
5530 perm = verify_bucket_permission(this, s, rgw::IAM::s3GetBucketAcl);
5531 }
5532 if (!perm)
5533 return -EACCES;
5534
5535 return 0;
5536 }
5537
5538 void RGWGetACLs::pre_exec()
5539 {
5540 rgw_bucket_object_pre_exec(s);
5541 }
5542
5543 void RGWGetACLs::execute(optional_yield y)
5544 {
5545 stringstream ss;
5546 RGWAccessControlPolicy* const acl = \
5547 (!rgw::sal::Object::empty(s->object.get()) ? s->object_acl.get() : s->bucket_acl.get());
5548 RGWAccessControlPolicy_S3* const s3policy = \
5549 static_cast<RGWAccessControlPolicy_S3*>(acl);
5550 s3policy->to_xml(ss);
5551 acls = ss.str();
5552 }
5553
5554
5555
5556 int RGWPutACLs::verify_permission(optional_yield y)
5557 {
5558 bool perm;
5559
5560 rgw_add_to_iam_environment(s->env, "s3:x-amz-acl", s->canned_acl);
5561
5562 rgw_add_grant_to_iam_environment(s->env, s);
5563 if (!rgw::sal::Object::empty(s->object.get())) {
5564 auto iam_action = s->object->get_instance().empty() ? rgw::IAM::s3PutObjectAcl : rgw::IAM::s3PutObjectVersionAcl;
5565 op_ret = rgw_iam_add_objtags(this, s, true, true);
5566 perm = verify_object_permission(this, s, iam_action);
5567 } else {
5568 op_ret = rgw_iam_add_buckettags(this, s);
5569 perm = verify_bucket_permission(this, s, rgw::IAM::s3PutBucketAcl);
5570 }
5571 if (!perm)
5572 return -EACCES;
5573
5574 return 0;
5575 }
5576
5577 int RGWGetLC::verify_permission(optional_yield y)
5578 {
5579 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
5580 if (has_s3_resource_tag)
5581 rgw_iam_add_buckettags(this, s);
5582
5583 bool perm;
5584 perm = verify_bucket_permission(this, s, rgw::IAM::s3GetLifecycleConfiguration);
5585 if (!perm)
5586 return -EACCES;
5587
5588 return 0;
5589 }
5590
5591 int RGWPutLC::verify_permission(optional_yield y)
5592 {
5593 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
5594 if (has_s3_resource_tag)
5595 rgw_iam_add_buckettags(this, s);
5596
5597 bool perm;
5598 perm = verify_bucket_permission(this, s, rgw::IAM::s3PutLifecycleConfiguration);
5599 if (!perm)
5600 return -EACCES;
5601
5602 return 0;
5603 }
5604
5605 int RGWDeleteLC::verify_permission(optional_yield y)
5606 {
5607 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
5608 if (has_s3_resource_tag)
5609 rgw_iam_add_buckettags(this, s);
5610
5611 bool perm;
5612 perm = verify_bucket_permission(this, s, rgw::IAM::s3PutLifecycleConfiguration);
5613 if (!perm)
5614 return -EACCES;
5615
5616 return 0;
5617 }
5618
5619 void RGWPutACLs::pre_exec()
5620 {
5621 rgw_bucket_object_pre_exec(s);
5622 }
5623
5624 void RGWGetLC::pre_exec()
5625 {
5626 rgw_bucket_object_pre_exec(s);
5627 }
5628
5629 void RGWPutLC::pre_exec()
5630 {
5631 rgw_bucket_object_pre_exec(s);
5632 }
5633
5634 void RGWDeleteLC::pre_exec()
5635 {
5636 rgw_bucket_object_pre_exec(s);
5637 }
5638
5639 void RGWPutACLs::execute(optional_yield y)
5640 {
5641 bufferlist bl;
5642
5643 RGWAccessControlPolicy_S3 *policy = NULL;
5644 RGWACLXMLParser_S3 parser(s->cct);
5645 RGWAccessControlPolicy_S3 new_policy(s->cct);
5646 stringstream ss;
5647
5648 op_ret = 0; /* XXX redundant? */
5649
5650 if (!parser.init()) {
5651 op_ret = -EINVAL;
5652 return;
5653 }
5654
5655
5656 RGWAccessControlPolicy* const existing_policy = \
5657 (rgw::sal::Object::empty(s->object.get()) ? s->bucket_acl.get() : s->object_acl.get());
5658
5659 owner = existing_policy->get_owner();
5660
5661 op_ret = get_params(y);
5662 if (op_ret < 0) {
5663 if (op_ret == -ERANGE) {
5664 ldpp_dout(this, 4) << "The size of request xml data is larger than the max limitation, data size = "
5665 << s->length << dendl;
5666 op_ret = -ERR_MALFORMED_XML;
5667 s->err.message = "The XML you provided was larger than the maximum " +
5668 std::to_string(s->cct->_conf->rgw_max_put_param_size) +
5669 " bytes allowed.";
5670 }
5671 return;
5672 }
5673
5674 char* buf = data.c_str();
5675 ldpp_dout(this, 15) << "read len=" << data.length() << " data=" << (buf ? buf : "") << dendl;
5676
5677 if (!s->canned_acl.empty() && data.length() > 0) {
5678 op_ret = -EINVAL;
5679 return;
5680 }
5681
5682 if (!s->canned_acl.empty() || s->has_acl_header) {
5683 op_ret = get_policy_from_state(store, s, ss);
5684 if (op_ret < 0)
5685 return;
5686
5687 data.clear();
5688 data.append(ss.str());
5689 }
5690
5691 if (!parser.parse(data.c_str(), data.length(), 1)) {
5692 op_ret = -EINVAL;
5693 return;
5694 }
5695 policy = static_cast<RGWAccessControlPolicy_S3 *>(parser.find_first("AccessControlPolicy"));
5696 if (!policy) {
5697 op_ret = -EINVAL;
5698 return;
5699 }
5700
5701 const RGWAccessControlList& req_acl = policy->get_acl();
5702 const multimap<string, ACLGrant>& req_grant_map = req_acl.get_grant_map();
5703 #define ACL_GRANTS_MAX_NUM 100
5704 int max_num = s->cct->_conf->rgw_acl_grants_max_num;
5705 if (max_num < 0) {
5706 max_num = ACL_GRANTS_MAX_NUM;
5707 }
5708
5709 int grants_num = req_grant_map.size();
5710 if (grants_num > max_num) {
5711 ldpp_dout(this, 4) << "An acl can have up to " << max_num
5712 << " grants, request acl grants num: " << grants_num << dendl;
5713 op_ret = -ERR_LIMIT_EXCEEDED;
5714 s->err.message = "The request is rejected, because the acl grants number you requested is larger than the maximum "
5715 + std::to_string(max_num)
5716 + " grants allowed in an acl.";
5717 return;
5718 }
5719
5720 // forward bucket acl requests to meta master zone
5721 if ((rgw::sal::Object::empty(s->object.get()))) {
5722 bufferlist in_data;
5723 // include acl data unless it was generated from a canned_acl
5724 if (s->canned_acl.empty()) {
5725 in_data.append(data);
5726 }
5727 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, in_data, nullptr, s->info, y);
5728 if (op_ret < 0) {
5729 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
5730 return;
5731 }
5732 }
5733
5734 if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 15>()) {
5735 ldpp_dout(this, 15) << "Old AccessControlPolicy";
5736 policy->to_xml(*_dout);
5737 *_dout << dendl;
5738 }
5739
5740 op_ret = policy->rebuild(this, store, &owner, new_policy, s->err.message);
5741 if (op_ret < 0)
5742 return;
5743
5744 if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 15>()) {
5745 ldpp_dout(this, 15) << "New AccessControlPolicy:";
5746 new_policy.to_xml(*_dout);
5747 *_dout << dendl;
5748 }
5749
5750 if (s->bucket_access_conf &&
5751 s->bucket_access_conf->block_public_acls() &&
5752 new_policy.is_public(this)) {
5753 op_ret = -EACCES;
5754 return;
5755 }
5756 new_policy.encode(bl);
5757 map<string, bufferlist> attrs;
5758
5759 if (!rgw::sal::Object::empty(s->object.get())) {
5760 s->object->set_atomic(s->obj_ctx);
5761 //if instance is empty, we should modify the latest object
5762 op_ret = s->object->modify_obj_attrs(s->obj_ctx, RGW_ATTR_ACL, bl, s->yield, this);
5763 } else {
5764 map<string,bufferlist> attrs = s->bucket_attrs;
5765 attrs[RGW_ATTR_ACL] = bl;
5766 op_ret = s->bucket->merge_and_store_attrs(this, attrs, y);
5767 }
5768 if (op_ret == -ECANCELED) {
5769 op_ret = 0; /* lost a race, but it's ok because acls are immutable */
5770 }
5771 }
5772
5773 void RGWPutLC::execute(optional_yield y)
5774 {
5775 bufferlist bl;
5776
5777 RGWLifecycleConfiguration_S3 config(s->cct);
5778 RGWXMLParser parser;
5779 RGWLifecycleConfiguration_S3 new_config(s->cct);
5780
5781 content_md5 = s->info.env->get("HTTP_CONTENT_MD5");
5782 if (content_md5 == nullptr) {
5783 op_ret = -ERR_INVALID_REQUEST;
5784 s->err.message = "Missing required header for this request: Content-MD5";
5785 ldpp_dout(this, 5) << s->err.message << dendl;
5786 return;
5787 }
5788
5789 std::string content_md5_bin;
5790 try {
5791 content_md5_bin = rgw::from_base64(std::string_view(content_md5));
5792 } catch (...) {
5793 s->err.message = "Request header Content-MD5 contains character "
5794 "that is not base64 encoded.";
5795 ldpp_dout(this, 5) << s->err.message << dendl;
5796 op_ret = -ERR_BAD_DIGEST;
5797 return;
5798 }
5799
5800 if (!parser.init()) {
5801 op_ret = -EINVAL;
5802 return;
5803 }
5804
5805 op_ret = get_params(y);
5806 if (op_ret < 0)
5807 return;
5808
5809 char* buf = data.c_str();
5810 ldpp_dout(this, 15) << "read len=" << data.length() << " data=" << (buf ? buf : "") << dendl;
5811
5812 MD5 data_hash;
5813 // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
5814 data_hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
5815 unsigned char data_hash_res[CEPH_CRYPTO_MD5_DIGESTSIZE];
5816 data_hash.Update(reinterpret_cast<const unsigned char*>(buf), data.length());
5817 data_hash.Final(data_hash_res);
5818
5819 if (memcmp(data_hash_res, content_md5_bin.c_str(), CEPH_CRYPTO_MD5_DIGESTSIZE) != 0) {
5820 op_ret = -ERR_BAD_DIGEST;
5821 s->err.message = "The Content-MD5 you specified did not match what we received.";
5822 ldpp_dout(this, 5) << s->err.message
5823 << " Specified content md5: " << content_md5
5824 << ", calculated content md5: " << data_hash_res
5825 << dendl;
5826 return;
5827 }
5828
5829 if (!parser.parse(buf, data.length(), 1)) {
5830 op_ret = -ERR_MALFORMED_XML;
5831 return;
5832 }
5833
5834 try {
5835 RGWXMLDecoder::decode_xml("LifecycleConfiguration", config, &parser);
5836 } catch (RGWXMLDecoder::err& err) {
5837 ldpp_dout(this, 5) << "Bad lifecycle configuration: " << err << dendl;
5838 op_ret = -ERR_MALFORMED_XML;
5839 return;
5840 }
5841
5842 op_ret = config.rebuild(new_config);
5843 if (op_ret < 0)
5844 return;
5845
5846 if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 15>()) {
5847 XMLFormatter xf;
5848 new_config.dump_xml(&xf);
5849 stringstream ss;
5850 xf.flush(ss);
5851 ldpp_dout(this, 15) << "New LifecycleConfiguration:" << ss.str() << dendl;
5852 }
5853
5854 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
5855 if (op_ret < 0) {
5856 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
5857 return;
5858 }
5859
5860 op_ret = store->get_rgwlc()->set_bucket_config(s->bucket.get(), s->bucket_attrs, &new_config);
5861 if (op_ret < 0) {
5862 return;
5863 }
5864 return;
5865 }
5866
5867 void RGWDeleteLC::execute(optional_yield y)
5868 {
5869 bufferlist data;
5870 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
5871 if (op_ret < 0) {
5872 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
5873 return;
5874 }
5875
5876 op_ret = store->get_rgwlc()->remove_bucket_config(s->bucket.get(), s->bucket_attrs);
5877 if (op_ret < 0) {
5878 return;
5879 }
5880 return;
5881 }
5882
5883 int RGWGetCORS::verify_permission(optional_yield y)
5884 {
5885 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
5886 if (has_s3_resource_tag)
5887 rgw_iam_add_buckettags(this, s);
5888
5889 return verify_bucket_owner_or_policy(s, rgw::IAM::s3GetBucketCORS);
5890 }
5891
5892 void RGWGetCORS::execute(optional_yield y)
5893 {
5894 op_ret = read_bucket_cors();
5895 if (op_ret < 0)
5896 return ;
5897
5898 if (!cors_exist) {
5899 ldpp_dout(this, 2) << "No CORS configuration set yet for this bucket" << dendl;
5900 op_ret = -ERR_NO_CORS_FOUND;
5901 return;
5902 }
5903 }
5904
5905 int RGWPutCORS::verify_permission(optional_yield y)
5906 {
5907 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
5908 if (has_s3_resource_tag)
5909 rgw_iam_add_buckettags(this, s);
5910
5911 return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketCORS);
5912 }
5913
5914 void RGWPutCORS::execute(optional_yield y)
5915 {
5916 rgw_raw_obj obj;
5917
5918 op_ret = get_params(y);
5919 if (op_ret < 0)
5920 return;
5921
5922 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, in_data, nullptr, s->info, y);
5923 if (op_ret < 0) {
5924 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
5925 return;
5926 }
5927
5928 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
5929 rgw::sal::Attrs attrs(s->bucket_attrs);
5930 attrs[RGW_ATTR_CORS] = cors_bl;
5931 return s->bucket->merge_and_store_attrs(this, attrs, s->yield);
5932 });
5933 }
5934
5935 int RGWDeleteCORS::verify_permission(optional_yield y)
5936 {
5937 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
5938 if (has_s3_resource_tag)
5939 rgw_iam_add_buckettags(this, s);
5940
5941 // No separate delete permission
5942 return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketCORS);
5943 }
5944
5945 void RGWDeleteCORS::execute(optional_yield y)
5946 {
5947 bufferlist data;
5948 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
5949 if (op_ret < 0) {
5950 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
5951 return;
5952 }
5953
5954 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
5955 op_ret = read_bucket_cors();
5956 if (op_ret < 0)
5957 return op_ret;
5958
5959 if (!cors_exist) {
5960 ldpp_dout(this, 2) << "No CORS configuration set yet for this bucket" << dendl;
5961 op_ret = -ENOENT;
5962 return op_ret;
5963 }
5964
5965 rgw::sal::Attrs attrs(s->bucket_attrs);
5966 attrs.erase(RGW_ATTR_CORS);
5967 op_ret = s->bucket->merge_and_store_attrs(this, attrs, s->yield);
5968 if (op_ret < 0) {
5969 ldpp_dout(this, 0) << "RGWLC::RGWDeleteCORS() failed to set attrs on bucket=" << s->bucket->get_name()
5970 << " returned err=" << op_ret << dendl;
5971 }
5972 return op_ret;
5973 });
5974 }
5975
5976 void RGWOptionsCORS::get_response_params(string& hdrs, string& exp_hdrs, unsigned *max_age) {
5977 get_cors_response_headers(this, rule, req_hdrs, hdrs, exp_hdrs, max_age);
5978 }
5979
5980 int RGWOptionsCORS::validate_cors_request(RGWCORSConfiguration *cc) {
5981 rule = cc->host_name_rule(origin);
5982 if (!rule) {
5983 ldpp_dout(this, 10) << "There is no cors rule present for " << origin << dendl;
5984 return -ENOENT;
5985 }
5986
5987 if (!validate_cors_rule_method(this, rule, req_meth)) {
5988 return -ENOENT;
5989 }
5990
5991 if (!validate_cors_rule_header(this, rule, req_hdrs)) {
5992 return -ENOENT;
5993 }
5994
5995 return 0;
5996 }
5997
5998 void RGWOptionsCORS::execute(optional_yield y)
5999 {
6000 op_ret = read_bucket_cors();
6001 if (op_ret < 0)
6002 return;
6003
6004 origin = s->info.env->get("HTTP_ORIGIN");
6005 if (!origin) {
6006 ldpp_dout(this, 0) << "Missing mandatory Origin header" << dendl;
6007 op_ret = -EINVAL;
6008 return;
6009 }
6010 req_meth = s->info.env->get("HTTP_ACCESS_CONTROL_REQUEST_METHOD");
6011 if (!req_meth) {
6012 ldpp_dout(this, 0) << "Missing mandatory Access-control-request-method header" << dendl;
6013 op_ret = -EINVAL;
6014 return;
6015 }
6016 if (!cors_exist) {
6017 ldpp_dout(this, 2) << "No CORS configuration set yet for this bucket" << dendl;
6018 op_ret = -ENOENT;
6019 return;
6020 }
6021 req_hdrs = s->info.env->get("HTTP_ACCESS_CONTROL_REQUEST_HEADERS");
6022 op_ret = validate_cors_request(&bucket_cors);
6023 if (!rule) {
6024 origin = req_meth = NULL;
6025 return;
6026 }
6027 return;
6028 }
6029
6030 int RGWGetRequestPayment::verify_permission(optional_yield y)
6031 {
6032 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
6033 if (has_s3_resource_tag)
6034 rgw_iam_add_buckettags(this, s);
6035
6036 return verify_bucket_owner_or_policy(s, rgw::IAM::s3GetBucketRequestPayment);
6037 }
6038
6039 void RGWGetRequestPayment::pre_exec()
6040 {
6041 rgw_bucket_object_pre_exec(s);
6042 }
6043
6044 void RGWGetRequestPayment::execute(optional_yield y)
6045 {
6046 requester_pays = s->bucket->get_info().requester_pays;
6047 }
6048
6049 int RGWSetRequestPayment::verify_permission(optional_yield y)
6050 {
6051 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
6052 if (has_s3_resource_tag)
6053 rgw_iam_add_buckettags(this, s);
6054
6055 return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketRequestPayment);
6056 }
6057
6058 void RGWSetRequestPayment::pre_exec()
6059 {
6060 rgw_bucket_object_pre_exec(s);
6061 }
6062
6063 void RGWSetRequestPayment::execute(optional_yield y)
6064 {
6065
6066 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, in_data, nullptr, s->info, y);
6067 if (op_ret < 0) {
6068 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
6069 return;
6070 }
6071
6072 op_ret = get_params(y);
6073
6074 if (op_ret < 0)
6075 return;
6076
6077 s->bucket->get_info().requester_pays = requester_pays;
6078 op_ret = s->bucket->put_info(this, false, real_time());
6079 if (op_ret < 0) {
6080 ldpp_dout(this, 0) << "NOTICE: put_bucket_info on bucket=" << s->bucket->get_name()
6081 << " returned err=" << op_ret << dendl;
6082 return;
6083 }
6084 s->bucket_attrs = s->bucket->get_attrs();
6085 }
6086
6087 int RGWInitMultipart::verify_permission(optional_yield y)
6088 {
6089 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
6090 if (has_s3_existing_tag || has_s3_resource_tag)
6091 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
6092
6093 if (s->iam_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
6094 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
6095 rgw::IAM::s3PutObject,
6096 s->object->get_obj());
6097 if (identity_policy_res == Effect::Deny) {
6098 return -EACCES;
6099 }
6100
6101 rgw::IAM::Effect e = Effect::Pass;
6102 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
6103 ARN obj_arn(s->object->get_obj());
6104 if (s->iam_policy) {
6105 e = s->iam_policy->eval(s->env, *s->auth.identity,
6106 rgw::IAM::s3PutObject,
6107 obj_arn,
6108 princ_type);
6109 }
6110 if (e == Effect::Deny) {
6111 return -EACCES;
6112 }
6113
6114 if (!s->session_policies.empty()) {
6115 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
6116 rgw::IAM::s3PutObject,
6117 s->object->get_obj());
6118 if (session_policy_res == Effect::Deny) {
6119 return -EACCES;
6120 }
6121 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
6122 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
6123 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
6124 (session_policy_res == Effect::Allow && e == Effect::Allow)) {
6125 return 0;
6126 }
6127 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
6128 //Intersection of session policy and identity policy plus bucket policy
6129 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) {
6130 return 0;
6131 }
6132 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
6133 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) {
6134 return 0;
6135 }
6136 }
6137 return -EACCES;
6138 }
6139 if (e == Effect::Allow || identity_policy_res == Effect::Allow) {
6140 return 0;
6141 }
6142 }
6143
6144 if (!verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
6145 return -EACCES;
6146 }
6147
6148 return 0;
6149 }
6150
6151 void RGWInitMultipart::pre_exec()
6152 {
6153 rgw_bucket_object_pre_exec(s);
6154 }
6155
6156 void RGWInitMultipart::execute(optional_yield y)
6157 {
6158 bufferlist aclbl, tracebl;
6159 rgw::sal::Attrs attrs;
6160
6161 if (get_params(y) < 0)
6162 return;
6163
6164 if (rgw::sal::Object::empty(s->object.get()))
6165 return;
6166
6167 if (multipart_trace) {
6168 tracing::encode(multipart_trace->GetContext(), tracebl);
6169 attrs[RGW_ATTR_TRACE] = tracebl;
6170 }
6171
6172 policy.encode(aclbl);
6173 attrs[RGW_ATTR_ACL] = aclbl;
6174
6175 populate_with_generic_attrs(s, attrs);
6176
6177 /* select encryption mode */
6178 op_ret = prepare_encryption(attrs);
6179 if (op_ret != 0)
6180 return;
6181
6182 op_ret = rgw_get_request_metadata(this, s->cct, s->info, attrs);
6183 if (op_ret < 0) {
6184 return;
6185 }
6186
6187 std::unique_ptr<rgw::sal::MultipartUpload> upload;
6188 upload = s->bucket->get_multipart_upload(s->object->get_name(),
6189 upload_id);
6190 op_ret = upload->init(this, s->yield, s->obj_ctx, s->owner, s->dest_placement, attrs);
6191
6192 if (op_ret == 0) {
6193 upload_id = upload->get_upload_id();
6194 }
6195 s->trace->SetAttribute(tracing::rgw::UPLOAD_ID, upload_id);
6196 multipart_trace->UpdateName(tracing::rgw::MULTIPART + upload_id);
6197
6198 }
6199
6200 int RGWCompleteMultipart::verify_permission(optional_yield y)
6201 {
6202 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
6203 if (has_s3_existing_tag || has_s3_resource_tag)
6204 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
6205
6206 if (s->iam_policy || ! s->iam_user_policies.empty() || ! s->session_policies.empty()) {
6207 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
6208 rgw::IAM::s3PutObject,
6209 s->object->get_obj());
6210 if (identity_policy_res == Effect::Deny) {
6211 return -EACCES;
6212 }
6213
6214 rgw::IAM::Effect e = Effect::Pass;
6215 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
6216 rgw::ARN obj_arn(s->object->get_obj());
6217 if (s->iam_policy) {
6218 e = s->iam_policy->eval(s->env, *s->auth.identity,
6219 rgw::IAM::s3PutObject,
6220 obj_arn,
6221 princ_type);
6222 }
6223 if (e == Effect::Deny) {
6224 return -EACCES;
6225 }
6226
6227 if (!s->session_policies.empty()) {
6228 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
6229 rgw::IAM::s3PutObject,
6230 s->object->get_obj());
6231 if (session_policy_res == Effect::Deny) {
6232 return -EACCES;
6233 }
6234 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
6235 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
6236 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
6237 (session_policy_res == Effect::Allow && e == Effect::Allow)) {
6238 return 0;
6239 }
6240 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
6241 //Intersection of session policy and identity policy plus bucket policy
6242 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) {
6243 return 0;
6244 }
6245 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
6246 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) {
6247 return 0;
6248 }
6249 }
6250 return -EACCES;
6251 }
6252 if (e == Effect::Allow || identity_policy_res == Effect::Allow) {
6253 return 0;
6254 }
6255 }
6256
6257 if (!verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
6258 return -EACCES;
6259 }
6260
6261 return 0;
6262 }
6263
6264 void RGWCompleteMultipart::pre_exec()
6265 {
6266 rgw_bucket_object_pre_exec(s);
6267 }
6268
6269 void RGWCompleteMultipart::execute(optional_yield y)
6270 {
6271 RGWMultiCompleteUpload *parts;
6272 RGWMultiXMLParser parser;
6273 std::unique_ptr<rgw::sal::MultipartUpload> upload;
6274 off_t ofs = 0;
6275 std::unique_ptr<rgw::sal::Object> meta_obj;
6276 std::unique_ptr<rgw::sal::Object> target_obj;
6277 uint64_t olh_epoch = 0;
6278
6279 op_ret = get_params(y);
6280 if (op_ret < 0)
6281 return;
6282 op_ret = get_system_versioning_params(s, &olh_epoch, &version_id);
6283 if (op_ret < 0) {
6284 return;
6285 }
6286
6287 if (!data.length()) {
6288 op_ret = -ERR_MALFORMED_XML;
6289 return;
6290 }
6291
6292 if (!parser.init()) {
6293 op_ret = -EIO;
6294 return;
6295 }
6296
6297 if (!parser.parse(data.c_str(), data.length(), 1)) {
6298 op_ret = -ERR_MALFORMED_XML;
6299 return;
6300 }
6301
6302 parts = static_cast<RGWMultiCompleteUpload *>(parser.find_first("CompleteMultipartUpload"));
6303 if (!parts || parts->parts.empty()) {
6304 // CompletedMultipartUpload is incorrect but some versions of some libraries use it, see PR #41700
6305 parts = static_cast<RGWMultiCompleteUpload *>(parser.find_first("CompletedMultipartUpload"));
6306 }
6307
6308 if (!parts || parts->parts.empty()) {
6309 op_ret = -ERR_MALFORMED_XML;
6310 return;
6311 }
6312
6313
6314 if ((int)parts->parts.size() >
6315 s->cct->_conf->rgw_multipart_part_upload_limit) {
6316 op_ret = -ERANGE;
6317 return;
6318 }
6319
6320 upload = s->bucket->get_multipart_upload(s->object->get_name(), upload_id);
6321
6322 RGWCompressionInfo cs_info;
6323 bool compressed = false;
6324 uint64_t accounted_size = 0;
6325
6326 list<rgw_obj_index_key> remove_objs; /* objects to be removed from index listing */
6327
6328 meta_obj = upload->get_meta_obj();
6329 meta_obj->set_in_extra_data(true);
6330 meta_obj->set_hash_source(s->object->get_name());
6331
6332 /*take a cls lock on meta_obj to prevent racing completions (or retries)
6333 from deleting the parts*/
6334 int max_lock_secs_mp =
6335 s->cct->_conf.get_val<int64_t>("rgw_mp_lock_max_time");
6336 utime_t dur(max_lock_secs_mp, 0);
6337
6338 serializer = meta_obj->get_serializer(this, "RGWCompleteMultipart");
6339 op_ret = serializer->try_lock(this, dur, y);
6340 if (op_ret < 0) {
6341 ldpp_dout(this, 0) << "failed to acquire lock" << dendl;
6342 if (op_ret == -ENOENT && check_previously_completed(parts)) {
6343 ldpp_dout(this, 1) << "NOTICE: This multipart completion is already completed" << dendl;
6344 op_ret = 0;
6345 return;
6346 }
6347 op_ret = -ERR_INTERNAL_ERROR;
6348 s->err.message = "This multipart completion is already in progress";
6349 return;
6350 }
6351
6352 op_ret = meta_obj->get_obj_attrs(s->obj_ctx, s->yield, this);
6353 if (op_ret < 0) {
6354 ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << meta_obj
6355 << " ret=" << op_ret << dendl;
6356 return;
6357 }
6358 s->trace->SetAttribute(tracing::rgw::UPLOAD_ID, upload_id);
6359 jspan_context trace_ctx(false, false);
6360 extract_span_context(meta_obj->get_attrs(), trace_ctx);
6361 multipart_trace = tracing::rgw::tracer.add_span(name(), trace_ctx);
6362
6363
6364 // make reservation for notification if needed
6365 std::unique_ptr<rgw::sal::Notification> res
6366 = store->get_notification(meta_obj.get(), nullptr, s, rgw::notify::ObjectCreatedCompleteMultipartUpload, &s->object->get_name());
6367 op_ret = res->publish_reserve(this);
6368 if (op_ret < 0) {
6369 return;
6370 }
6371
6372 target_obj = s->bucket->get_object(rgw_obj_key(s->object->get_name()));
6373 if (s->bucket->versioning_enabled()) {
6374 if (!version_id.empty()) {
6375 target_obj->set_instance(version_id);
6376 } else {
6377 target_obj->gen_rand_obj_instance_name();
6378 version_id = target_obj->get_instance();
6379 }
6380 }
6381 target_obj->set_attrs(meta_obj->get_attrs());
6382
6383 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);
6384 if (op_ret < 0) {
6385 ldpp_dout(this, 0) << "ERROR: upload complete failed ret=" << op_ret << dendl;
6386 return;
6387 }
6388
6389 // remove the upload meta object ; the meta object is not versioned
6390 // when the bucket is, as that would add an unneeded delete marker
6391 int r = meta_obj->delete_object(this, s->obj_ctx, y, true /* prevent versioning */);
6392 if (r >= 0) {
6393 /* serializer's exclusive lock is released */
6394 serializer->clear_locked();
6395 } else {
6396 ldpp_dout(this, 0) << "WARNING: failed to remove object " << meta_obj << dendl;
6397 }
6398
6399 // send request to notification manager
6400 int ret = res->publish_commit(this, ofs, target_obj->get_mtime(), etag, target_obj->get_instance());
6401 if (ret < 0) {
6402 ldpp_dout(this, 1) << "ERROR: publishing notification failed, with error: " << ret << dendl;
6403 // too late to rollback operation, hence op_ret is not set here
6404 }
6405 } // RGWCompleteMultipart::execute
6406
6407 bool RGWCompleteMultipart::check_previously_completed(const RGWMultiCompleteUpload* parts)
6408 {
6409 // re-calculate the etag from the parts and compare to the existing object
6410 int ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, this);
6411 if (ret < 0) {
6412 ldpp_dout(this, 0) << __func__ << "() ERROR: get_obj_attrs() returned ret=" << ret << dendl;
6413 return false;
6414 }
6415 rgw::sal::Attrs sattrs = s->object->get_attrs();
6416 string oetag = sattrs[RGW_ATTR_ETAG].to_str();
6417
6418 MD5 hash;
6419 // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
6420 hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
6421 for (const auto& [index, part] : parts->parts) {
6422 std::string partetag = rgw_string_unquote(part);
6423 char petag[CEPH_CRYPTO_MD5_DIGESTSIZE];
6424 hex_to_buf(partetag.c_str(), petag, CEPH_CRYPTO_MD5_DIGESTSIZE);
6425 hash.Update((const unsigned char *)petag, sizeof(petag));
6426 ldpp_dout(this, 20) << __func__ << "() re-calculating multipart etag: part: "
6427 << index << ", etag: " << partetag << dendl;
6428 }
6429
6430 unsigned char final_etag[CEPH_CRYPTO_MD5_DIGESTSIZE];
6431 char final_etag_str[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 16];
6432 hash.Final(final_etag);
6433 buf_to_hex(final_etag, CEPH_CRYPTO_MD5_DIGESTSIZE, final_etag_str);
6434 snprintf(&final_etag_str[CEPH_CRYPTO_MD5_DIGESTSIZE * 2], sizeof(final_etag_str) - CEPH_CRYPTO_MD5_DIGESTSIZE * 2,
6435 "-%lld", (long long)parts->parts.size());
6436
6437 if (oetag.compare(final_etag_str) != 0) {
6438 ldpp_dout(this, 1) << __func__ << "() NOTICE: etag mismatch: object etag:"
6439 << oetag << ", re-calculated etag:" << final_etag_str << dendl;
6440 return false;
6441 }
6442 ldpp_dout(this, 5) << __func__ << "() object etag and re-calculated etag match, etag: " << oetag << dendl;
6443 return true;
6444 }
6445
6446 void RGWCompleteMultipart::complete()
6447 {
6448 /* release exclusive lock iff not already */
6449 if (unlikely(serializer && serializer->locked)) {
6450 int r = serializer->unlock();
6451 if (r < 0) {
6452 ldpp_dout(this, 0) << "WARNING: failed to unlock " << serializer->oid << dendl;
6453 }
6454 }
6455 send_response();
6456 }
6457
6458 int RGWAbortMultipart::verify_permission(optional_yield y)
6459 {
6460 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
6461 if (has_s3_existing_tag || has_s3_resource_tag)
6462 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
6463
6464 if (s->iam_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
6465 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
6466 rgw::IAM::s3AbortMultipartUpload,
6467 s->object->get_obj());
6468 if (identity_policy_res == Effect::Deny) {
6469 return -EACCES;
6470 }
6471
6472 rgw::IAM::Effect e = Effect::Pass;
6473 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
6474 ARN obj_arn(s->object->get_obj());
6475 if (s->iam_policy) {
6476 e = s->iam_policy->eval(s->env, *s->auth.identity,
6477 rgw::IAM::s3AbortMultipartUpload,
6478 obj_arn, princ_type);
6479 }
6480
6481 if (e == Effect::Deny) {
6482 return -EACCES;
6483 }
6484
6485 if (!s->session_policies.empty()) {
6486 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
6487 rgw::IAM::s3PutObject,
6488 s->object->get_obj());
6489 if (session_policy_res == Effect::Deny) {
6490 return -EACCES;
6491 }
6492 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
6493 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
6494 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
6495 (session_policy_res == Effect::Allow && e == Effect::Allow)) {
6496 return 0;
6497 }
6498 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
6499 //Intersection of session policy and identity policy plus bucket policy
6500 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) {
6501 return 0;
6502 }
6503 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
6504 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) {
6505 return 0;
6506 }
6507 }
6508 return -EACCES;
6509 }
6510 if (e == Effect::Allow || identity_policy_res == Effect::Allow) {
6511 return 0;
6512 }
6513 }
6514
6515 if (!verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE)) {
6516 return -EACCES;
6517 }
6518
6519 return 0;
6520 }
6521
6522 void RGWAbortMultipart::pre_exec()
6523 {
6524 rgw_bucket_object_pre_exec(s);
6525 }
6526
6527 void RGWAbortMultipart::execute(optional_yield y)
6528 {
6529 op_ret = -EINVAL;
6530 string upload_id;
6531 upload_id = s->info.args.get("uploadId");
6532 std::unique_ptr<rgw::sal::Object> meta_obj;
6533 std::unique_ptr<rgw::sal::MultipartUpload> upload;
6534
6535 if (upload_id.empty() || rgw::sal::Object::empty(s->object.get()))
6536 return;
6537
6538 upload = s->bucket->get_multipart_upload(s->object->get_name(), upload_id);
6539 RGWObjectCtx *obj_ctx = static_cast<RGWObjectCtx *>(s->obj_ctx);
6540
6541 jspan_context trace_ctx(false, false);
6542 if (tracing::rgw::tracer.is_enabled()) {
6543 // read meta object attributes for trace info
6544 meta_obj = upload->get_meta_obj();
6545 meta_obj->set_in_extra_data(true);
6546 meta_obj->get_obj_attrs(obj_ctx, s->yield, this);
6547 extract_span_context(meta_obj->get_attrs(), trace_ctx);
6548 }
6549 multipart_trace = tracing::rgw::tracer.add_span(name(), trace_ctx);
6550
6551 op_ret = upload->abort(this, s->cct, obj_ctx);
6552 }
6553
6554 int RGWListMultipart::verify_permission(optional_yield y)
6555 {
6556 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
6557 if (has_s3_existing_tag || has_s3_resource_tag)
6558 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
6559
6560 if (!verify_object_permission(this, s, rgw::IAM::s3ListMultipartUploadParts))
6561 return -EACCES;
6562
6563 return 0;
6564 }
6565
6566 void RGWListMultipart::pre_exec()
6567 {
6568 rgw_bucket_object_pre_exec(s);
6569 }
6570
6571 void RGWListMultipart::execute(optional_yield y)
6572 {
6573 op_ret = get_params(y);
6574 if (op_ret < 0)
6575 return;
6576
6577 upload = s->bucket->get_multipart_upload(s->object->get_name(), upload_id);
6578
6579 rgw::sal::Attrs attrs;
6580 op_ret = upload->get_info(this, s->yield, s->obj_ctx, &placement, &attrs);
6581 /* decode policy */
6582 map<string, bufferlist>::iterator iter = attrs.find(RGW_ATTR_ACL);
6583 if (iter != attrs.end()) {
6584 auto bliter = iter->second.cbegin();
6585 try {
6586 policy.decode(bliter);
6587 } catch (buffer::error& err) {
6588 ldpp_dout(this, 0) << "ERROR: could not decode policy, caught buffer::error" << dendl;
6589 op_ret = -EIO;
6590 }
6591 }
6592 if (op_ret < 0)
6593 return;
6594
6595 op_ret = upload->list_parts(this, s->cct, max_parts, marker, NULL, &truncated);
6596 }
6597
6598 int RGWListBucketMultiparts::verify_permission(optional_yield y)
6599 {
6600 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
6601 if (has_s3_resource_tag)
6602 rgw_iam_add_buckettags(this, s);
6603
6604 if (!verify_bucket_permission(this,
6605 s,
6606 rgw::IAM::s3ListBucketMultipartUploads))
6607 return -EACCES;
6608
6609 return 0;
6610 }
6611
6612 void RGWListBucketMultiparts::pre_exec()
6613 {
6614 rgw_bucket_object_pre_exec(s);
6615 }
6616
6617 void RGWListBucketMultiparts::execute(optional_yield y)
6618 {
6619 op_ret = get_params(y);
6620 if (op_ret < 0)
6621 return;
6622
6623 if (s->prot_flags & RGW_REST_SWIFT) {
6624 string path_args;
6625 path_args = s->info.args.get("path");
6626 if (!path_args.empty()) {
6627 if (!delimiter.empty() || !prefix.empty()) {
6628 op_ret = -EINVAL;
6629 return;
6630 }
6631 prefix = path_args;
6632 delimiter="/";
6633 }
6634 }
6635
6636 op_ret = s->bucket->list_multiparts(this, prefix, marker_meta,
6637 delimiter, max_uploads, uploads,
6638 &common_prefixes, &is_truncated);
6639 if (op_ret < 0) {
6640 return;
6641 }
6642
6643 if (!uploads.empty()) {
6644 next_marker_key = uploads.back()->get_key();
6645 next_marker_upload_id = uploads.back()->get_upload_id();
6646 }
6647 }
6648
6649 void RGWGetHealthCheck::execute(optional_yield y)
6650 {
6651 if (!g_conf()->rgw_healthcheck_disabling_path.empty() &&
6652 (::access(g_conf()->rgw_healthcheck_disabling_path.c_str(), F_OK) == 0)) {
6653 /* Disabling path specified & existent in the filesystem. */
6654 op_ret = -ERR_SERVICE_UNAVAILABLE; /* 503 */
6655 } else {
6656 op_ret = 0; /* 200 OK */
6657 }
6658 }
6659
6660 int RGWDeleteMultiObj::verify_permission(optional_yield y)
6661 {
6662 int op_ret = get_params(y);
6663 if (op_ret) {
6664 return op_ret;
6665 }
6666
6667 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
6668 if (has_s3_existing_tag || has_s3_resource_tag)
6669 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
6670
6671 if (s->iam_policy || ! s->iam_user_policies.empty() || ! s->session_policies.empty()) {
6672 if (s->bucket->get_info().obj_lock_enabled() && bypass_governance_mode) {
6673 ARN bucket_arn(s->bucket->get_key());
6674 auto r = eval_identity_or_session_policies(s->iam_user_policies, s->env,
6675 rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket->get_key()));
6676 if (r == Effect::Deny) {
6677 bypass_perm = false;
6678 } else if (r == Effect::Pass && s->iam_policy) {
6679 r = s->iam_policy->eval(s->env, *s->auth.identity, rgw::IAM::s3BypassGovernanceRetention,
6680 bucket_arn);
6681 if (r == Effect::Deny) {
6682 bypass_perm = false;
6683 }
6684 } else if (r == Effect::Pass && !s->session_policies.empty()) {
6685 r = eval_identity_or_session_policies(s->session_policies, s->env,
6686 rgw::IAM::s3BypassGovernanceRetention, ARN(s->bucket->get_key()));
6687 if (r == Effect::Deny) {
6688 bypass_perm = false;
6689 }
6690 }
6691 }
6692
6693 bool not_versioned = rgw::sal::Object::empty(s->object.get()) || s->object->get_instance().empty();
6694
6695 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
6696 not_versioned ?
6697 rgw::IAM::s3DeleteObject :
6698 rgw::IAM::s3DeleteObjectVersion,
6699 ARN(s->bucket->get_key()));
6700 if (identity_policy_res == Effect::Deny) {
6701 return -EACCES;
6702 }
6703
6704 rgw::IAM::Effect r = Effect::Pass;
6705 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
6706 rgw::ARN bucket_arn(s->bucket->get_key());
6707 if (s->iam_policy) {
6708 r = s->iam_policy->eval(s->env, *s->auth.identity,
6709 not_versioned ?
6710 rgw::IAM::s3DeleteObject :
6711 rgw::IAM::s3DeleteObjectVersion,
6712 bucket_arn,
6713 princ_type);
6714 }
6715 if (r == Effect::Deny)
6716 return -EACCES;
6717
6718 if (!s->session_policies.empty()) {
6719 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
6720 not_versioned ?
6721 rgw::IAM::s3DeleteObject :
6722 rgw::IAM::s3DeleteObjectVersion,
6723 ARN(s->bucket->get_key()));
6724 if (session_policy_res == Effect::Deny) {
6725 return -EACCES;
6726 }
6727 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
6728 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
6729 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
6730 (session_policy_res == Effect::Allow && r == Effect::Allow)) {
6731 return 0;
6732 }
6733 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
6734 //Intersection of session policy and identity policy plus bucket policy
6735 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || r == Effect::Allow) {
6736 return 0;
6737 }
6738 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
6739 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) {
6740 return 0;
6741 }
6742 }
6743 return -EACCES;
6744 }
6745 if (r == Effect::Allow || identity_policy_res == Effect::Allow)
6746 return 0;
6747 }
6748
6749 acl_allowed = verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE);
6750 if (!acl_allowed)
6751 return -EACCES;
6752
6753 return 0;
6754 }
6755
6756 void RGWDeleteMultiObj::pre_exec()
6757 {
6758 rgw_bucket_object_pre_exec(s);
6759 }
6760
6761 void RGWDeleteMultiObj::execute(optional_yield y)
6762 {
6763 RGWMultiDelDelete *multi_delete;
6764 vector<rgw_obj_key>::iterator iter;
6765 RGWMultiDelXMLParser parser;
6766 RGWObjectCtx *obj_ctx = static_cast<RGWObjectCtx *>(s->obj_ctx);
6767 char* buf;
6768
6769 buf = data.c_str();
6770 if (!buf) {
6771 op_ret = -EINVAL;
6772 goto error;
6773 }
6774
6775 if (!parser.init()) {
6776 op_ret = -EINVAL;
6777 goto error;
6778 }
6779
6780 if (!parser.parse(buf, data.length(), 1)) {
6781 op_ret = -EINVAL;
6782 goto error;
6783 }
6784
6785 multi_delete = static_cast<RGWMultiDelDelete *>(parser.find_first("Delete"));
6786 if (!multi_delete) {
6787 op_ret = -EINVAL;
6788 goto error;
6789 } else {
6790 #define DELETE_MULTI_OBJ_MAX_NUM 1000
6791 int max_num = s->cct->_conf->rgw_delete_multi_obj_max_num;
6792 if (max_num < 0) {
6793 max_num = DELETE_MULTI_OBJ_MAX_NUM;
6794 }
6795 int multi_delete_object_num = multi_delete->objects.size();
6796 if (multi_delete_object_num > max_num) {
6797 op_ret = -ERR_MALFORMED_XML;
6798 goto error;
6799 }
6800 }
6801
6802 if (multi_delete->is_quiet())
6803 quiet = true;
6804
6805 if (s->bucket->get_info().mfa_enabled()) {
6806 bool has_versioned = false;
6807 for (auto i : multi_delete->objects) {
6808 if (!i.instance.empty()) {
6809 has_versioned = true;
6810 break;
6811 }
6812 }
6813 if (has_versioned && !s->mfa_verified) {
6814 ldpp_dout(this, 5) << "NOTICE: multi-object delete request with a versioned object, mfa auth not provided" << dendl;
6815 op_ret = -ERR_MFA_REQUIRED;
6816 goto error;
6817 }
6818 }
6819
6820 begin_response();
6821 if (multi_delete->objects.empty()) {
6822 goto done;
6823 }
6824
6825 for (iter = multi_delete->objects.begin();
6826 iter != multi_delete->objects.end();
6827 ++iter) {
6828 std::string version_id;
6829 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(*iter);
6830 if (s->iam_policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
6831 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
6832 iter->instance.empty() ?
6833 rgw::IAM::s3DeleteObject :
6834 rgw::IAM::s3DeleteObjectVersion,
6835 ARN(obj->get_obj()));
6836 if (identity_policy_res == Effect::Deny) {
6837 send_partial_response(*iter, false, "", -EACCES);
6838 continue;
6839 }
6840
6841 rgw::IAM::Effect e = Effect::Pass;
6842 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
6843 if (s->iam_policy) {
6844 ARN obj_arn(obj->get_obj());
6845 e = s->iam_policy->eval(s->env,
6846 *s->auth.identity,
6847 iter->instance.empty() ?
6848 rgw::IAM::s3DeleteObject :
6849 rgw::IAM::s3DeleteObjectVersion,
6850 obj_arn,
6851 princ_type);
6852 }
6853 if (e == Effect::Deny) {
6854 send_partial_response(*iter, false, "", -EACCES);
6855 continue;
6856 }
6857
6858 if (!s->session_policies.empty()) {
6859 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
6860 iter->instance.empty() ?
6861 rgw::IAM::s3DeleteObject :
6862 rgw::IAM::s3DeleteObjectVersion,
6863 ARN(obj->get_obj()));
6864 if (session_policy_res == Effect::Deny) {
6865 send_partial_response(*iter, false, "", -EACCES);
6866 continue;
6867 }
6868 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
6869 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
6870 if ((session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) &&
6871 (session_policy_res != Effect::Allow || e != Effect::Allow)) {
6872 send_partial_response(*iter, false, "", -EACCES);
6873 continue;
6874 }
6875 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
6876 //Intersection of session policy and identity policy plus bucket policy
6877 if ((session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) && e != Effect::Allow) {
6878 send_partial_response(*iter, false, "", -EACCES);
6879 continue;
6880 }
6881 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
6882 if (session_policy_res != Effect::Allow || identity_policy_res != Effect::Allow) {
6883 send_partial_response(*iter, false, "", -EACCES);
6884 continue;
6885 }
6886 }
6887 send_partial_response(*iter, false, "", -EACCES);
6888 continue;
6889 }
6890
6891 if ((identity_policy_res == Effect::Pass && e == Effect::Pass && !acl_allowed)) {
6892 send_partial_response(*iter, false, "", -EACCES);
6893 continue;
6894 }
6895 }
6896
6897 uint64_t obj_size = 0;
6898 std::string etag;
6899
6900 if (!rgw::sal::Object::empty(obj.get())) {
6901 RGWObjState* astate = nullptr;
6902 bool check_obj_lock = obj->have_instance() && bucket->get_info().obj_lock_enabled();
6903 const auto ret = obj->get_obj_state(this, obj_ctx, &astate, s->yield, true);
6904
6905 if (ret < 0) {
6906 if (ret == -ENOENT) {
6907 // object maybe delete_marker, skip check_obj_lock
6908 check_obj_lock = false;
6909 } else {
6910 // Something went wrong.
6911 send_partial_response(*iter, false, "", ret);
6912 continue;
6913 }
6914 } else {
6915 obj_size = astate->size;
6916 etag = astate->attrset[RGW_ATTR_ETAG].to_str();
6917 }
6918
6919 if (check_obj_lock) {
6920 ceph_assert(astate);
6921 int object_lock_response = verify_object_lock(this, astate->attrset, bypass_perm, bypass_governance_mode);
6922 if (object_lock_response != 0) {
6923 send_partial_response(*iter, false, "", object_lock_response);
6924 continue;
6925 }
6926 }
6927 }
6928
6929 // make reservation for notification if needed
6930 const auto versioned_object = s->bucket->versioning_enabled();
6931 const auto event_type = versioned_object && obj->get_instance().empty() ?
6932 rgw::notify::ObjectRemovedDeleteMarkerCreated :
6933 rgw::notify::ObjectRemovedDelete;
6934 std::unique_ptr<rgw::sal::Notification> res
6935 = store->get_notification(obj.get(), s->src_object.get(), s, event_type);
6936 op_ret = res->publish_reserve(this);
6937 if (op_ret < 0) {
6938 send_partial_response(*iter, false, "", op_ret);
6939 continue;
6940 }
6941
6942 obj->set_atomic(obj_ctx);
6943
6944 std::unique_ptr<rgw::sal::Object::DeleteOp> del_op = obj->get_delete_op(obj_ctx);
6945 del_op->params.versioning_status = obj->get_bucket()->get_info().versioning_status();
6946 del_op->params.obj_owner = s->owner;
6947 del_op->params.bucket_owner = s->bucket_owner;
6948 del_op->params.marker_version_id = version_id;
6949
6950 op_ret = del_op->delete_obj(this, y);
6951 if (op_ret == -ENOENT) {
6952 op_ret = 0;
6953 }
6954
6955 send_partial_response(*iter, obj->get_delete_marker(), del_op->result.version_id, op_ret);
6956
6957 // send request to notification manager
6958 int ret = res->publish_commit(this, obj_size, ceph::real_clock::now(), etag, version_id);
6959 if (ret < 0) {
6960 ldpp_dout(this, 1) << "ERROR: publishing notification failed, with error: " << ret << dendl;
6961 // too late to rollback operation, hence op_ret is not set here
6962 }
6963 }
6964
6965 /* set the return code to zero, errors at this point will be
6966 dumped to the response */
6967 op_ret = 0;
6968
6969 done:
6970 // will likely segfault if begin_response() has not been called
6971 end_response();
6972 return;
6973
6974 error:
6975 send_status();
6976 return;
6977
6978 }
6979
6980 bool RGWBulkDelete::Deleter::verify_permission(RGWBucketInfo& binfo,
6981 map<string, bufferlist>& battrs,
6982 ACLOwner& bucket_owner /* out */,
6983 optional_yield y)
6984 {
6985 RGWAccessControlPolicy bacl(store->ctx());
6986 int ret = read_bucket_policy(dpp, store, s, binfo, battrs, &bacl, binfo.bucket, y);
6987 if (ret < 0) {
6988 return false;
6989 }
6990
6991 auto policy = get_iam_policy_from_attr(s->cct, battrs, binfo.bucket.tenant);
6992
6993 bucket_owner = bacl.get_owner();
6994
6995 /* We can use global user_acl because each BulkDelete request is allowed
6996 * to work on entities from a single account only. */
6997 return verify_bucket_permission(dpp, s, binfo.bucket, s->user_acl.get(),
6998 &bacl, policy, s->iam_user_policies, s->session_policies, rgw::IAM::s3DeleteBucket);
6999 }
7000
7001 bool RGWBulkDelete::Deleter::delete_single(const acct_path_t& path, optional_yield y)
7002 {
7003 std::unique_ptr<rgw::sal::Bucket> bucket;
7004 ACLOwner bowner;
7005 RGWObjVersionTracker ot;
7006
7007 int ret = store->get_bucket(dpp, s->user.get(), s->user->get_tenant(), path.bucket_name, &bucket, y);
7008 if (ret < 0) {
7009 goto binfo_fail;
7010 }
7011
7012 ret = bucket->load_bucket(dpp, s->yield);
7013 if (ret < 0) {
7014 goto binfo_fail;
7015 }
7016
7017 if (!verify_permission(bucket->get_info(), bucket->get_attrs(), bowner, y)) {
7018 ret = -EACCES;
7019 goto auth_fail;
7020 }
7021
7022 if (!path.obj_key.empty()) {
7023 ACLOwner bucket_owner;
7024
7025 bucket_owner.set_id(bucket->get_info().owner);
7026 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(path.obj_key);
7027 obj->set_atomic(s->obj_ctx);
7028
7029 std::unique_ptr<rgw::sal::Object::DeleteOp> del_op = obj->get_delete_op(s->obj_ctx);
7030 del_op->params.versioning_status = obj->get_bucket()->get_info().versioning_status();
7031 del_op->params.obj_owner = bowner;
7032 del_op->params.bucket_owner = bucket_owner;
7033
7034 ret = del_op->delete_obj(dpp, y);
7035 if (ret < 0) {
7036 goto delop_fail;
7037 }
7038 } else {
7039 ret = bucket->remove_bucket(dpp, false, true, &s->info, s->yield);
7040 if (ret < 0) {
7041 goto delop_fail;
7042 }
7043 }
7044
7045 num_deleted++;
7046 return true;
7047
7048 binfo_fail:
7049 if (-ENOENT == ret) {
7050 ldpp_dout(dpp, 20) << "cannot find bucket = " << path.bucket_name << dendl;
7051 num_unfound++;
7052 } else {
7053 ldpp_dout(dpp, 20) << "cannot get bucket info, ret = " << ret << dendl;
7054
7055 fail_desc_t failed_item = {
7056 .err = ret,
7057 .path = path
7058 };
7059 failures.push_back(failed_item);
7060 }
7061 return false;
7062
7063 auth_fail:
7064 ldpp_dout(dpp, 20) << "wrong auth for " << path << dendl;
7065 {
7066 fail_desc_t failed_item = {
7067 .err = ret,
7068 .path = path
7069 };
7070 failures.push_back(failed_item);
7071 }
7072 return false;
7073
7074 delop_fail:
7075 if (-ENOENT == ret) {
7076 ldpp_dout(dpp, 20) << "cannot find entry " << path << dendl;
7077 num_unfound++;
7078 } else {
7079 fail_desc_t failed_item = {
7080 .err = ret,
7081 .path = path
7082 };
7083 failures.push_back(failed_item);
7084 }
7085 return false;
7086 }
7087
7088 bool RGWBulkDelete::Deleter::delete_chunk(const std::list<acct_path_t>& paths, optional_yield y)
7089 {
7090 ldpp_dout(dpp, 20) << "in delete_chunk" << dendl;
7091 for (auto path : paths) {
7092 ldpp_dout(dpp, 20) << "bulk deleting path: " << path << dendl;
7093 delete_single(path, y);
7094 }
7095
7096 return true;
7097 }
7098
7099 int RGWBulkDelete::verify_permission(optional_yield y)
7100 {
7101 return 0;
7102 }
7103
7104 void RGWBulkDelete::pre_exec()
7105 {
7106 rgw_bucket_object_pre_exec(s);
7107 }
7108
7109 void RGWBulkDelete::execute(optional_yield y)
7110 {
7111 deleter = std::unique_ptr<Deleter>(new Deleter(this, store, s));
7112
7113 bool is_truncated = false;
7114 do {
7115 list<RGWBulkDelete::acct_path_t> items;
7116
7117 int ret = get_data(items, &is_truncated);
7118 if (ret < 0) {
7119 return;
7120 }
7121
7122 ret = deleter->delete_chunk(items, y);
7123 } while (!op_ret && is_truncated);
7124
7125 return;
7126 }
7127
7128
7129 constexpr std::array<int, 2> RGWBulkUploadOp::terminal_errors;
7130
7131 int RGWBulkUploadOp::verify_permission(optional_yield y)
7132 {
7133 if (s->auth.identity->is_anonymous()) {
7134 return -EACCES;
7135 }
7136
7137 if (! verify_user_permission_no_policy(this, s, RGW_PERM_WRITE)) {
7138 return -EACCES;
7139 }
7140
7141 if (s->user->get_tenant() != s->bucket_tenant) {
7142 ldpp_dout(this, 10) << "user cannot create a bucket in a different tenant"
7143 << " (user_id.tenant=" << s->user->get_tenant()
7144 << " requested=" << s->bucket_tenant << ")" << dendl;
7145 return -EACCES;
7146 }
7147
7148 if (s->user->get_max_buckets() < 0) {
7149 return -EPERM;
7150 }
7151
7152 return 0;
7153 }
7154
7155 void RGWBulkUploadOp::pre_exec()
7156 {
7157 rgw_bucket_object_pre_exec(s);
7158 }
7159
7160 boost::optional<std::pair<std::string, rgw_obj_key>>
7161 RGWBulkUploadOp::parse_path(const std::string_view& path)
7162 {
7163 /* We need to skip all slashes at the beginning in order to preserve
7164 * compliance with Swift. */
7165 const size_t start_pos = path.find_first_not_of('/');
7166
7167 if (std::string_view::npos != start_pos) {
7168 /* Seperator is the first slash after the leading ones. */
7169 const size_t sep_pos = path.substr(start_pos).find('/');
7170
7171 if (std::string_view::npos != sep_pos) {
7172 const auto bucket_name = path.substr(start_pos, sep_pos - start_pos);
7173 const auto obj_name = path.substr(sep_pos + 1);
7174
7175 return std::make_pair(std::string(bucket_name),
7176 rgw_obj_key(std::string(obj_name)));
7177 } else {
7178 /* It's guaranteed here that bucket name is at least one character
7179 * long and is different than slash. */
7180 return std::make_pair(std::string(path.substr(start_pos)),
7181 rgw_obj_key());
7182 }
7183 }
7184
7185 return none;
7186 }
7187
7188 std::pair<std::string, std::string>
7189 RGWBulkUploadOp::handle_upload_path(struct req_state *s)
7190 {
7191 std::string bucket_path, file_prefix;
7192 if (! s->init_state.url_bucket.empty()) {
7193 file_prefix = bucket_path = s->init_state.url_bucket + "/";
7194 if (!rgw::sal::Object::empty(s->object.get())) {
7195 const std::string& object_name = s->object->get_name();
7196
7197 /* As rgw_obj_key::empty() already verified emptiness of s->object->get_name(),
7198 * we can safely examine its last element. */
7199 if (object_name.back() == '/') {
7200 file_prefix.append(object_name);
7201 } else {
7202 file_prefix.append(object_name).append("/");
7203 }
7204 }
7205 }
7206 return std::make_pair(bucket_path, file_prefix);
7207 }
7208
7209 int RGWBulkUploadOp::handle_dir_verify_permission(optional_yield y)
7210 {
7211 if (s->user->get_max_buckets() > 0) {
7212 rgw::sal::BucketList buckets;
7213 std::string marker;
7214 op_ret = s->user->list_buckets(this, marker, std::string(), s->user->get_max_buckets(),
7215 false, buckets, y);
7216 if (op_ret < 0) {
7217 return op_ret;
7218 }
7219
7220 if (buckets.count() >= static_cast<size_t>(s->user->get_max_buckets())) {
7221 return -ERR_TOO_MANY_BUCKETS;
7222 }
7223 }
7224
7225 return 0;
7226 }
7227
7228 static void forward_req_info(const DoutPrefixProvider *dpp, CephContext *cct, req_info& info, const std::string& bucket_name)
7229 {
7230 /* the request of container or object level will contain bucket name.
7231 * only at account level need to append the bucket name */
7232 if (info.script_uri.find(bucket_name) != std::string::npos) {
7233 return;
7234 }
7235
7236 ldpp_dout(dpp, 20) << "append the bucket: "<< bucket_name << " to req_info" << dendl;
7237 info.script_uri.append("/").append(bucket_name);
7238 info.request_uri_aws4 = info.request_uri = info.script_uri;
7239 info.effective_uri = "/" + bucket_name;
7240 }
7241
7242 void RGWBulkUploadOp::init(rgw::sal::Store* const store,
7243 struct req_state* const s,
7244 RGWHandler* const h)
7245 {
7246 RGWOp::init(store, s, h);
7247 }
7248
7249 int RGWBulkUploadOp::handle_dir(const std::string_view path, optional_yield y)
7250 {
7251 ldpp_dout(this, 20) << "got directory=" << path << dendl;
7252
7253 op_ret = handle_dir_verify_permission(y);
7254 if (op_ret < 0) {
7255 return op_ret;
7256 }
7257
7258 std::string bucket_name;
7259 rgw_obj_key object_junk;
7260 std::tie(bucket_name, object_junk) = *parse_path(path);
7261
7262 rgw_raw_obj obj(store->get_zone()->get_params().domain_root,
7263 rgw_make_bucket_entry_name(s->bucket_tenant, bucket_name));
7264
7265 /* we need to make sure we read bucket info, it's not read before for this
7266 * specific request */
7267 std::unique_ptr<rgw::sal::Bucket> bucket;
7268
7269 /* Create metadata: ACLs. */
7270 std::map<std::string, ceph::bufferlist> attrs;
7271 RGWAccessControlPolicy policy;
7272 policy.create_default(s->user->get_id(), s->user->get_display_name());
7273 ceph::bufferlist aclbl;
7274 policy.encode(aclbl);
7275 attrs.emplace(RGW_ATTR_ACL, std::move(aclbl));
7276
7277 obj_version objv, ep_objv;
7278 bool bucket_exists;
7279 RGWQuotaInfo quota_info;
7280 const RGWQuotaInfo* pquota_info = nullptr;
7281 RGWBucketInfo out_info;
7282 string swift_ver_location;
7283 rgw_bucket new_bucket;
7284 req_info info = s->info;
7285 new_bucket.tenant = s->bucket_tenant; /* ignored if bucket exists */
7286 new_bucket.name = bucket_name;
7287 rgw_placement_rule placement_rule;
7288 placement_rule.storage_class = s->info.storage_class;
7289 forward_req_info(this, s->cct, info, bucket_name);
7290
7291 op_ret = s->user->create_bucket(this, new_bucket,
7292 store->get_zone()->get_zonegroup().get_id(),
7293 placement_rule, swift_ver_location,
7294 pquota_info, policy, attrs,
7295 out_info, ep_objv,
7296 true, false, &bucket_exists,
7297 info, &bucket, y);
7298 /* continue if EEXIST and create_bucket will fail below. this way we can
7299 * recover from a partial create by retrying it. */
7300 ldpp_dout(this, 20) << "rgw_create_bucket returned ret=" << op_ret
7301 << ", bucket=" << bucket << dendl;
7302
7303 return op_ret;
7304 }
7305
7306
7307 bool RGWBulkUploadOp::handle_file_verify_permission(RGWBucketInfo& binfo,
7308 const rgw_obj& obj,
7309 std::map<std::string, ceph::bufferlist>& battrs,
7310 ACLOwner& bucket_owner /* out */,
7311 optional_yield y)
7312 {
7313 RGWAccessControlPolicy bacl(store->ctx());
7314 op_ret = read_bucket_policy(this, store, s, binfo, battrs, &bacl, binfo.bucket, y);
7315 if (op_ret < 0) {
7316 ldpp_dout(this, 20) << "cannot read_policy() for bucket" << dendl;
7317 return false;
7318 }
7319
7320 auto policy = get_iam_policy_from_attr(s->cct, battrs, binfo.bucket.tenant);
7321
7322 bucket_owner = bacl.get_owner();
7323 if (policy || ! s->iam_user_policies.empty() || !s->session_policies.empty()) {
7324 auto identity_policy_res = eval_identity_or_session_policies(s->iam_user_policies, s->env,
7325 rgw::IAM::s3PutObject, obj);
7326 if (identity_policy_res == Effect::Deny) {
7327 return false;
7328 }
7329
7330 rgw::IAM::PolicyPrincipal princ_type = rgw::IAM::PolicyPrincipal::Other;
7331 ARN obj_arn(obj);
7332 auto e = policy->eval(s->env, *s->auth.identity,
7333 rgw::IAM::s3PutObject, obj_arn, princ_type);
7334 if (e == Effect::Deny) {
7335 return false;
7336 }
7337
7338 if (!s->session_policies.empty()) {
7339 auto session_policy_res = eval_identity_or_session_policies(s->session_policies, s->env,
7340 rgw::IAM::s3PutObject, obj);
7341 if (session_policy_res == Effect::Deny) {
7342 return false;
7343 }
7344 if (princ_type == rgw::IAM::PolicyPrincipal::Role) {
7345 //Intersection of session policy and identity policy plus intersection of session policy and bucket policy
7346 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) ||
7347 (session_policy_res == Effect::Allow && e == Effect::Allow)) {
7348 return true;
7349 }
7350 } else if (princ_type == rgw::IAM::PolicyPrincipal::Session) {
7351 //Intersection of session policy and identity policy plus bucket policy
7352 if ((session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) || e == Effect::Allow) {
7353 return true;
7354 }
7355 } else if (princ_type == rgw::IAM::PolicyPrincipal::Other) {// there was no match in the bucket policy
7356 if (session_policy_res == Effect::Allow && identity_policy_res == Effect::Allow) {
7357 return true;
7358 }
7359 }
7360 return false;
7361 }
7362 if (e == Effect::Allow || identity_policy_res == Effect::Allow) {
7363 return true;
7364 }
7365 }
7366
7367 return verify_bucket_permission_no_policy(this, s, s->user_acl.get(),
7368 &bacl, RGW_PERM_WRITE);
7369 }
7370
7371 int RGWBulkUploadOp::handle_file(const std::string_view path,
7372 const size_t size,
7373 AlignedStreamGetter& body, optional_yield y)
7374 {
7375
7376 ldpp_dout(this, 20) << "got file=" << path << ", size=" << size << dendl;
7377
7378 if (size > static_cast<size_t>(s->cct->_conf->rgw_max_put_size)) {
7379 op_ret = -ERR_TOO_LARGE;
7380 return op_ret;
7381 }
7382
7383 std::string bucket_name;
7384 rgw_obj_key object;
7385 std::tie(bucket_name, object) = *parse_path(path);
7386
7387 auto& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
7388 std::unique_ptr<rgw::sal::Bucket> bucket;
7389 ACLOwner bowner;
7390
7391 op_ret = store->get_bucket(this, s->user.get(), rgw_bucket(rgw_bucket_key(s->user->get_tenant(), bucket_name)), &bucket, y);
7392 if (op_ret < 0) {
7393 if (op_ret == -ENOENT) {
7394 ldpp_dout(this, 20) << "non existent directory=" << bucket_name << dendl;
7395 }
7396 return op_ret;
7397 }
7398
7399 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(object);
7400
7401 if (! handle_file_verify_permission(bucket->get_info(),
7402 obj->get_obj(),
7403 bucket->get_attrs(), bowner, y)) {
7404 ldpp_dout(this, 20) << "object creation unauthorized" << dendl;
7405 op_ret = -EACCES;
7406 return op_ret;
7407 }
7408
7409 op_ret = bucket->check_quota(this, user_quota, bucket_quota, size, y);
7410 if (op_ret < 0) {
7411 return op_ret;
7412 }
7413
7414 if (bucket->versioning_enabled()) {
7415 obj->gen_rand_obj_instance_name();
7416 }
7417
7418 rgw_placement_rule dest_placement = s->dest_placement;
7419 dest_placement.inherit_from(bucket->get_placement_rule());
7420
7421 std::unique_ptr<rgw::sal::Writer> processor;
7422 processor = store->get_atomic_writer(this, s->yield, std::move(obj),
7423 bowner.get_id(), obj_ctx,
7424 &s->dest_placement, 0, s->req_id);
7425 op_ret = processor->prepare(s->yield);
7426 if (op_ret < 0) {
7427 ldpp_dout(this, 20) << "cannot prepare processor due to ret=" << op_ret << dendl;
7428 return op_ret;
7429 }
7430
7431 /* No filters by default. */
7432 rgw::sal::DataProcessor *filter = processor.get();
7433
7434 const auto& compression_type = store->get_zone()->get_params().get_compression_type(
7435 dest_placement);
7436 CompressorRef plugin;
7437 boost::optional<RGWPutObj_Compress> compressor;
7438 if (compression_type != "none") {
7439 plugin = Compressor::create(s->cct, compression_type);
7440 if (! plugin) {
7441 ldpp_dout(this, 1) << "Cannot load plugin for rgw_compression_type "
7442 << compression_type << dendl;
7443 } else {
7444 compressor.emplace(s->cct, plugin, filter);
7445 filter = &*compressor;
7446 }
7447 }
7448
7449 /* Upload file content. */
7450 ssize_t len = 0;
7451 size_t ofs = 0;
7452 MD5 hash;
7453 // Allow use of MD5 digest in FIPS mode for non-cryptographic purposes
7454 hash.SetFlags(EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
7455 do {
7456 ceph::bufferlist data;
7457 len = body.get_at_most(s->cct->_conf->rgw_max_chunk_size, data);
7458
7459 ldpp_dout(this, 20) << "body=" << data.c_str() << dendl;
7460 if (len < 0) {
7461 op_ret = len;
7462 return op_ret;
7463 } else if (len > 0) {
7464 hash.Update((const unsigned char *)data.c_str(), data.length());
7465 op_ret = filter->process(std::move(data), ofs);
7466 if (op_ret < 0) {
7467 ldpp_dout(this, 20) << "filter->process() returned ret=" << op_ret << dendl;
7468 return op_ret;
7469 }
7470
7471 ofs += len;
7472 }
7473
7474 } while (len > 0);
7475
7476 // flush
7477 op_ret = filter->process({}, ofs);
7478 if (op_ret < 0) {
7479 return op_ret;
7480 }
7481
7482 if (ofs != size) {
7483 ldpp_dout(this, 10) << "real file size different from declared" << dendl;
7484 op_ret = -EINVAL;
7485 return op_ret;
7486 }
7487
7488 op_ret = bucket->check_quota(this, user_quota, bucket_quota, size, y);
7489 if (op_ret < 0) {
7490 ldpp_dout(this, 20) << "quota exceeded for path=" << path << dendl;
7491 return op_ret;
7492 }
7493
7494 char calc_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
7495 unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE];
7496 hash.Final(m);
7497 buf_to_hex(m, CEPH_CRYPTO_MD5_DIGESTSIZE, calc_md5);
7498
7499 /* Create metadata: ETAG. */
7500 std::map<std::string, ceph::bufferlist> attrs;
7501 std::string etag = calc_md5;
7502 ceph::bufferlist etag_bl;
7503 etag_bl.append(etag.c_str(), etag.size() + 1);
7504 attrs.emplace(RGW_ATTR_ETAG, std::move(etag_bl));
7505
7506 /* Create metadata: ACLs. */
7507 RGWAccessControlPolicy policy;
7508 policy.create_default(s->user->get_id(), s->user->get_display_name());
7509 ceph::bufferlist aclbl;
7510 policy.encode(aclbl);
7511 attrs.emplace(RGW_ATTR_ACL, std::move(aclbl));
7512
7513 /* Create metadata: compression info. */
7514 if (compressor && compressor->is_compressed()) {
7515 ceph::bufferlist tmp;
7516 RGWCompressionInfo cs_info;
7517 cs_info.compression_type = plugin->get_type_name();
7518 cs_info.orig_size = size;
7519 cs_info.compressor_message = compressor->get_compressor_message();
7520 cs_info.blocks = std::move(compressor->get_compression_blocks());
7521 encode(cs_info, tmp);
7522 attrs.emplace(RGW_ATTR_COMPRESSION, std::move(tmp));
7523 }
7524
7525 /* Complete the transaction. */
7526 op_ret = processor->complete(size, etag, nullptr, ceph::real_time(),
7527 attrs, ceph::real_time() /* delete_at */,
7528 nullptr, nullptr, nullptr, nullptr, nullptr,
7529 s->yield);
7530 if (op_ret < 0) {
7531 ldpp_dout(this, 20) << "processor::complete returned op_ret=" << op_ret << dendl;
7532 }
7533
7534 return op_ret;
7535 }
7536
7537 void RGWBulkUploadOp::execute(optional_yield y)
7538 {
7539 ceph::bufferlist buffer(64 * 1024);
7540
7541 ldpp_dout(this, 20) << "start" << dendl;
7542
7543 /* Create an instance of stream-abstracting class. Having this indirection
7544 * allows for easy introduction of decompressors like gzip and bzip2. */
7545 auto stream = create_stream();
7546 if (! stream) {
7547 return;
7548 }
7549
7550 /* Handling the $UPLOAD_PATH accordingly to the Swift's Bulk middleware. See:
7551 * https://github.com/openstack/swift/blob/2.13.0/swift/common/middleware/bulk.py#L31-L41 */
7552 std::string bucket_path, file_prefix;
7553 std::tie(bucket_path, file_prefix) = handle_upload_path(s);
7554
7555 auto status = rgw::tar::StatusIndicator::create();
7556 do {
7557 op_ret = stream->get_exactly(rgw::tar::BLOCK_SIZE, buffer);
7558 if (op_ret < 0) {
7559 ldpp_dout(this, 2) << "cannot read header" << dendl;
7560 return;
7561 }
7562
7563 /* We need to re-interpret the buffer as a TAR block. Exactly two blocks
7564 * must be tracked to detect out end-of-archive. It occurs when both of
7565 * them are empty (zeroed). Tracing this particular inter-block dependency
7566 * is responsibility of the rgw::tar::StatusIndicator class. */
7567 boost::optional<rgw::tar::HeaderView> header;
7568 std::tie(status, header) = rgw::tar::interpret_block(status, buffer);
7569
7570 if (! status.empty() && header) {
7571 /* This specific block isn't empty (entirely zeroed), so we can parse
7572 * it as a TAR header and dispatch. At the moment we do support only
7573 * regular files and directories. Everything else (symlinks, devices)
7574 * will be ignored but won't cease the whole upload. */
7575 switch (header->get_filetype()) {
7576 case rgw::tar::FileType::NORMAL_FILE: {
7577 ldpp_dout(this, 2) << "handling regular file" << dendl;
7578
7579 std::string_view filename;
7580 if (bucket_path.empty())
7581 filename = header->get_filename();
7582 else
7583 filename = file_prefix + std::string(header->get_filename());
7584 auto body = AlignedStreamGetter(0, header->get_filesize(),
7585 rgw::tar::BLOCK_SIZE, *stream);
7586 op_ret = handle_file(filename,
7587 header->get_filesize(),
7588 body, y);
7589 if (! op_ret) {
7590 /* Only regular files counts. */
7591 num_created++;
7592 } else {
7593 failures.emplace_back(op_ret, std::string(filename));
7594 }
7595 break;
7596 }
7597 case rgw::tar::FileType::DIRECTORY: {
7598 ldpp_dout(this, 2) << "handling regular directory" << dendl;
7599
7600 std::string_view dirname = bucket_path.empty() ? header->get_filename() : bucket_path;
7601 op_ret = handle_dir(dirname, y);
7602 if (op_ret < 0 && op_ret != -ERR_BUCKET_EXISTS) {
7603 failures.emplace_back(op_ret, std::string(dirname));
7604 }
7605 break;
7606 }
7607 default: {
7608 /* Not recognized. Skip. */
7609 op_ret = 0;
7610 break;
7611 }
7612 }
7613
7614 /* In case of any problems with sub-request authorization Swift simply
7615 * terminates whole upload immediately. */
7616 if (boost::algorithm::contains(std::initializer_list<int>{ op_ret },
7617 terminal_errors)) {
7618 ldpp_dout(this, 2) << "terminating due to ret=" << op_ret << dendl;
7619 break;
7620 }
7621 } else {
7622 ldpp_dout(this, 2) << "an empty block" << dendl;
7623 op_ret = 0;
7624 }
7625
7626 buffer.clear();
7627 } while (! status.eof());
7628
7629 return;
7630 }
7631
7632 RGWBulkUploadOp::AlignedStreamGetter::~AlignedStreamGetter()
7633 {
7634 const size_t aligned_legnth = length + (-length % alignment);
7635 ceph::bufferlist junk;
7636
7637 DecoratedStreamGetter::get_exactly(aligned_legnth - position, junk);
7638 }
7639
7640 ssize_t RGWBulkUploadOp::AlignedStreamGetter::get_at_most(const size_t want,
7641 ceph::bufferlist& dst)
7642 {
7643 const size_t max_to_read = std::min(want, length - position);
7644 const auto len = DecoratedStreamGetter::get_at_most(max_to_read, dst);
7645 if (len > 0) {
7646 position += len;
7647 }
7648 return len;
7649 }
7650
7651 ssize_t RGWBulkUploadOp::AlignedStreamGetter::get_exactly(const size_t want,
7652 ceph::bufferlist& dst)
7653 {
7654 const auto len = DecoratedStreamGetter::get_exactly(want, dst);
7655 if (len > 0) {
7656 position += len;
7657 }
7658 return len;
7659 }
7660
7661 int RGWGetAttrs::verify_permission(optional_yield y)
7662 {
7663 s->object->set_atomic(s->obj_ctx);
7664
7665 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
7666 if (has_s3_existing_tag || has_s3_resource_tag)
7667 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
7668
7669 auto iam_action = s->object->get_instance().empty() ?
7670 rgw::IAM::s3GetObject :
7671 rgw::IAM::s3GetObjectVersion;
7672
7673 if (!verify_object_permission(this, s, iam_action)) {
7674 return -EACCES;
7675 }
7676
7677 return 0;
7678 }
7679
7680 void RGWGetAttrs::pre_exec()
7681 {
7682 rgw_bucket_object_pre_exec(s);
7683 }
7684
7685 void RGWGetAttrs::execute(optional_yield y)
7686 {
7687 op_ret = get_params();
7688 if (op_ret < 0)
7689 return;
7690
7691 s->object->set_atomic(s->obj_ctx);
7692
7693 op_ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, this);
7694 if (op_ret < 0) {
7695 ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << s->object
7696 << " ret=" << op_ret << dendl;
7697 return;
7698 }
7699
7700 /* XXX RGWObject::get_obj_attrs() does not support filtering (yet) */
7701 auto& obj_attrs = s->object->get_attrs();
7702 if (attrs.size() != 0) {
7703 /* return only attrs requested */
7704 for (auto& att : attrs) {
7705 auto iter = obj_attrs.find(att.first);
7706 if (iter != obj_attrs.end()) {
7707 att.second = iter->second;
7708 }
7709 }
7710 } else {
7711 /* return all attrs */
7712 for (auto& att : obj_attrs) {
7713 attrs.insert(get_attrs_t::value_type(att.first, att.second));;
7714 }
7715 }
7716
7717 return;
7718 }
7719
7720 int RGWRMAttrs::verify_permission(optional_yield y)
7721 {
7722 // This looks to be part of the RGW-NFS machinery and has no S3 or
7723 // Swift equivalent.
7724 bool perm;
7725 if (!rgw::sal::Object::empty(s->object.get())) {
7726 perm = verify_object_permission_no_policy(this, s, RGW_PERM_WRITE);
7727 } else {
7728 perm = verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE);
7729 }
7730 if (!perm)
7731 return -EACCES;
7732
7733 return 0;
7734 }
7735
7736 void RGWRMAttrs::pre_exec()
7737 {
7738 rgw_bucket_object_pre_exec(s);
7739 }
7740
7741 void RGWRMAttrs::execute(optional_yield y)
7742 {
7743 op_ret = get_params();
7744 if (op_ret < 0)
7745 return;
7746
7747 s->object->set_atomic(s->obj_ctx);
7748
7749 op_ret = s->object->set_obj_attrs(this, s->obj_ctx, nullptr, &attrs, y);
7750 if (op_ret < 0) {
7751 ldpp_dout(this, 0) << "ERROR: failed to delete obj attrs, obj=" << s->object
7752 << " ret=" << op_ret << dendl;
7753 }
7754 return;
7755 }
7756
7757 int RGWSetAttrs::verify_permission(optional_yield y)
7758 {
7759 // This looks to be part of the RGW-NFS machinery and has no S3 or
7760 // Swift equivalent.
7761 bool perm;
7762 if (!rgw::sal::Object::empty(s->object.get())) {
7763 perm = verify_object_permission_no_policy(this, s, RGW_PERM_WRITE);
7764 } else {
7765 perm = verify_bucket_permission_no_policy(this, s, RGW_PERM_WRITE);
7766 }
7767 if (!perm)
7768 return -EACCES;
7769
7770 return 0;
7771 }
7772
7773 void RGWSetAttrs::pre_exec()
7774 {
7775 rgw_bucket_object_pre_exec(s);
7776 }
7777
7778 void RGWSetAttrs::execute(optional_yield y)
7779 {
7780 op_ret = get_params(y);
7781 if (op_ret < 0)
7782 return;
7783
7784 if (!rgw::sal::Object::empty(s->object.get())) {
7785 rgw::sal::Attrs a(attrs);
7786 op_ret = s->object->set_obj_attrs(this, s->obj_ctx, &a, nullptr, y);
7787 } else {
7788 op_ret = s->bucket->merge_and_store_attrs(this, attrs, y);
7789 }
7790
7791 } /* RGWSetAttrs::execute() */
7792
7793 void RGWGetObjLayout::pre_exec()
7794 {
7795 rgw_bucket_object_pre_exec(s);
7796 }
7797
7798 void RGWGetObjLayout::execute(optional_yield y)
7799 {
7800 }
7801
7802
7803 int RGWConfigBucketMetaSearch::verify_permission(optional_yield y)
7804 {
7805 if (!s->auth.identity->is_owner_of(s->bucket_owner.get_id())) {
7806 return -EACCES;
7807 }
7808
7809 return 0;
7810 }
7811
7812 void RGWConfigBucketMetaSearch::pre_exec()
7813 {
7814 rgw_bucket_object_pre_exec(s);
7815 }
7816
7817 void RGWConfigBucketMetaSearch::execute(optional_yield y)
7818 {
7819 op_ret = get_params(y);
7820 if (op_ret < 0) {
7821 ldpp_dout(this, 20) << "NOTICE: get_params() returned ret=" << op_ret << dendl;
7822 return;
7823 }
7824
7825 s->bucket->get_info().mdsearch_config = mdsearch_config;
7826
7827 op_ret = s->bucket->put_info(this, false, real_time());
7828 if (op_ret < 0) {
7829 ldpp_dout(this, 0) << "NOTICE: put_bucket_info on bucket=" << s->bucket->get_name()
7830 << " returned err=" << op_ret << dendl;
7831 return;
7832 }
7833 s->bucket_attrs = s->bucket->get_attrs();
7834 }
7835
7836 int RGWGetBucketMetaSearch::verify_permission(optional_yield y)
7837 {
7838 if (!s->auth.identity->is_owner_of(s->bucket_owner.get_id())) {
7839 return -EACCES;
7840 }
7841
7842 return 0;
7843 }
7844
7845 void RGWGetBucketMetaSearch::pre_exec()
7846 {
7847 rgw_bucket_object_pre_exec(s);
7848 }
7849
7850 int RGWDelBucketMetaSearch::verify_permission(optional_yield y)
7851 {
7852 if (!s->auth.identity->is_owner_of(s->bucket_owner.get_id())) {
7853 return -EACCES;
7854 }
7855
7856 return 0;
7857 }
7858
7859 void RGWDelBucketMetaSearch::pre_exec()
7860 {
7861 rgw_bucket_object_pre_exec(s);
7862 }
7863
7864 void RGWDelBucketMetaSearch::execute(optional_yield y)
7865 {
7866 s->bucket->get_info().mdsearch_config.clear();
7867
7868 op_ret = s->bucket->put_info(this, false, real_time());
7869 if (op_ret < 0) {
7870 ldpp_dout(this, 0) << "NOTICE: put_bucket_info on bucket=" << s->bucket->get_name()
7871 << " returned err=" << op_ret << dendl;
7872 return;
7873 }
7874 s->bucket_attrs = s->bucket->get_attrs();
7875 }
7876
7877
7878 RGWHandler::~RGWHandler()
7879 {
7880 }
7881
7882 int RGWHandler::init(rgw::sal::Store* _store,
7883 struct req_state *_s,
7884 rgw::io::BasicClient *cio)
7885 {
7886 store = _store;
7887 s = _s;
7888
7889 return 0;
7890 }
7891
7892 int RGWHandler::do_init_permissions(const DoutPrefixProvider *dpp, optional_yield y)
7893 {
7894 int ret = rgw_build_bucket_policies(dpp, store, s, y);
7895 if (ret < 0) {
7896 ldpp_dout(dpp, 10) << "init_permissions on " << s->bucket
7897 << " failed, ret=" << ret << dendl;
7898 return ret==-ENODATA ? -EACCES : ret;
7899 }
7900
7901 rgw_build_iam_environment(store, s);
7902 return ret;
7903 }
7904
7905 int RGWHandler::do_read_permissions(RGWOp *op, bool only_bucket, optional_yield y)
7906 {
7907 if (only_bucket) {
7908 /* already read bucket info */
7909 return 0;
7910 }
7911 int ret = rgw_build_object_policies(op, store, s, op->prefetch_data(), y);
7912
7913 if (ret < 0) {
7914 ldpp_dout(op, 10) << "read_permissions on " << s->bucket << ":"
7915 << s->object << " only_bucket=" << only_bucket
7916 << " ret=" << ret << dendl;
7917 if (ret == -ENODATA)
7918 ret = -EACCES;
7919 if (s->auth.identity->is_anonymous() && ret == -EACCES)
7920 ret = -EPERM;
7921 }
7922
7923 return ret;
7924 }
7925
7926 int RGWOp::error_handler(int err_no, string *error_content, optional_yield y) {
7927 return dialect_handler->error_handler(err_no, error_content, y);
7928 }
7929
7930 int RGWHandler::error_handler(int err_no, string *error_content, optional_yield) {
7931 // This is the do-nothing error handler
7932 return err_no;
7933 }
7934
7935 std::ostream& RGWOp::gen_prefix(std::ostream& out) const
7936 {
7937 // append <dialect>:<op name> to the prefix
7938 return s->gen_prefix(out) << s->dialect << ':' << name() << ' ';
7939 }
7940
7941 void RGWDefaultResponseOp::send_response() {
7942 if (op_ret) {
7943 set_req_state_err(s, op_ret);
7944 }
7945 dump_errno(s);
7946 end_header(s);
7947 }
7948
7949 void RGWPutBucketPolicy::send_response()
7950 {
7951 if (!op_ret) {
7952 /* A successful Put Bucket Policy should return a 204 on success */
7953 op_ret = STATUS_NO_CONTENT;
7954 }
7955 if (op_ret) {
7956 set_req_state_err(s, op_ret);
7957 }
7958 dump_errno(s);
7959 end_header(s);
7960 }
7961
7962 int RGWPutBucketPolicy::verify_permission(optional_yield y)
7963 {
7964 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
7965 if (has_s3_resource_tag)
7966 rgw_iam_add_buckettags(this, s);
7967
7968 if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketPolicy)) {
7969 return -EACCES;
7970 }
7971
7972 return 0;
7973 }
7974
7975 int RGWPutBucketPolicy::get_params(optional_yield y)
7976 {
7977 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
7978 // At some point when I have more time I want to make a version of
7979 // rgw_rest_read_all_input that doesn't use malloc.
7980 std::tie(op_ret, data) = read_all_input(s, max_size, false);
7981
7982 // And throws exceptions.
7983 return op_ret;
7984 }
7985
7986 void RGWPutBucketPolicy::execute(optional_yield y)
7987 {
7988 op_ret = get_params(y);
7989 if (op_ret < 0) {
7990 return;
7991 }
7992
7993 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
7994 if (op_ret < 0) {
7995 ldpp_dout(this, 20) << "forward_request_to_master returned ret=" << op_ret << dendl;
7996 return;
7997 }
7998
7999 try {
8000 const Policy p(s->cct, s->bucket_tenant, data);
8001 rgw::sal::Attrs attrs(s->bucket_attrs);
8002 if (s->bucket_access_conf &&
8003 s->bucket_access_conf->block_public_policy() &&
8004 rgw::IAM::is_public(p)) {
8005 op_ret = -EACCES;
8006 return;
8007 }
8008
8009 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [&p, this, &attrs] {
8010 attrs[RGW_ATTR_IAM_POLICY].clear();
8011 attrs[RGW_ATTR_IAM_POLICY].append(p.text);
8012 op_ret = s->bucket->merge_and_store_attrs(this, attrs, s->yield);
8013 return op_ret;
8014 });
8015 } catch (rgw::IAM::PolicyParseException& e) {
8016 ldpp_dout(this, 20) << "failed to parse policy: " << e.what() << dendl;
8017 op_ret = -EINVAL;
8018 }
8019 }
8020
8021 void RGWGetBucketPolicy::send_response()
8022 {
8023 if (op_ret) {
8024 set_req_state_err(s, op_ret);
8025 }
8026 dump_errno(s);
8027 end_header(s, this, "application/json");
8028 dump_body(s, policy);
8029 }
8030
8031 int RGWGetBucketPolicy::verify_permission(optional_yield y)
8032 {
8033 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8034 if (has_s3_resource_tag)
8035 rgw_iam_add_buckettags(this, s);
8036
8037 if (!verify_bucket_permission(this, s, rgw::IAM::s3GetBucketPolicy)) {
8038 return -EACCES;
8039 }
8040
8041 return 0;
8042 }
8043
8044 void RGWGetBucketPolicy::execute(optional_yield y)
8045 {
8046 rgw::sal::Attrs attrs(s->bucket_attrs);
8047 auto aiter = attrs.find(RGW_ATTR_IAM_POLICY);
8048 if (aiter == attrs.end()) {
8049 ldpp_dout(this, 0) << "can't find bucket IAM POLICY attr bucket_name = "
8050 << s->bucket_name << dendl;
8051 op_ret = -ERR_NO_SUCH_BUCKET_POLICY;
8052 s->err.message = "The bucket policy does not exist";
8053 return;
8054 } else {
8055 policy = attrs[RGW_ATTR_IAM_POLICY];
8056
8057 if (policy.length() == 0) {
8058 ldpp_dout(this, 10) << "The bucket policy does not exist, bucket: "
8059 << s->bucket_name << dendl;
8060 op_ret = -ERR_NO_SUCH_BUCKET_POLICY;
8061 s->err.message = "The bucket policy does not exist";
8062 return;
8063 }
8064 }
8065 }
8066
8067 void RGWDeleteBucketPolicy::send_response()
8068 {
8069 if (op_ret) {
8070 set_req_state_err(s, op_ret);
8071 }
8072 dump_errno(s);
8073 end_header(s);
8074 }
8075
8076 int RGWDeleteBucketPolicy::verify_permission(optional_yield y)
8077 {
8078 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8079 if (has_s3_resource_tag)
8080 rgw_iam_add_buckettags(this, s);
8081
8082 if (!verify_bucket_permission(this, s, rgw::IAM::s3DeleteBucketPolicy)) {
8083 return -EACCES;
8084 }
8085
8086 return 0;
8087 }
8088
8089 void RGWDeleteBucketPolicy::execute(optional_yield y)
8090 {
8091 bufferlist data;
8092 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
8093 if (op_ret < 0) {
8094 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
8095 return;
8096 }
8097
8098 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
8099 rgw::sal::Attrs attrs(s->bucket_attrs);
8100 attrs.erase(RGW_ATTR_IAM_POLICY);
8101 op_ret = s->bucket->merge_and_store_attrs(this, attrs, s->yield);
8102 return op_ret;
8103 });
8104 }
8105
8106 void RGWPutBucketObjectLock::pre_exec()
8107 {
8108 rgw_bucket_object_pre_exec(s);
8109 }
8110
8111 int RGWPutBucketObjectLock::verify_permission(optional_yield y)
8112 {
8113 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8114 if (has_s3_resource_tag)
8115 rgw_iam_add_buckettags(this, s);
8116
8117 return verify_bucket_owner_or_policy(s, rgw::IAM::s3PutBucketObjectLockConfiguration);
8118 }
8119
8120 void RGWPutBucketObjectLock::execute(optional_yield y)
8121 {
8122 if (!s->bucket->get_info().obj_lock_enabled()) {
8123 s->err.message = "object lock configuration can't be set if bucket object lock not enabled";
8124 ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
8125 op_ret = -ERR_INVALID_BUCKET_STATE;
8126 return;
8127 }
8128
8129 RGWXMLDecoder::XMLParser parser;
8130 if (!parser.init()) {
8131 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
8132 op_ret = -EINVAL;
8133 return;
8134 }
8135 op_ret = get_params(y);
8136 if (op_ret < 0) {
8137 return;
8138 }
8139 if (!parser.parse(data.c_str(), data.length(), 1)) {
8140 op_ret = -ERR_MALFORMED_XML;
8141 return;
8142 }
8143
8144 try {
8145 RGWXMLDecoder::decode_xml("ObjectLockConfiguration", obj_lock, &parser, true);
8146 } catch (RGWXMLDecoder::err& err) {
8147 ldpp_dout(this, 5) << "unexpected xml:" << err << dendl;
8148 op_ret = -ERR_MALFORMED_XML;
8149 return;
8150 }
8151 if (obj_lock.has_rule() && !obj_lock.retention_period_valid()) {
8152 s->err.message = "retention period must be a positive integer value";
8153 ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
8154 op_ret = -ERR_INVALID_RETENTION_PERIOD;
8155 return;
8156 }
8157
8158 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
8159 if (op_ret < 0) {
8160 ldpp_dout(this, 20) << __func__ << "forward_request_to_master returned ret=" << op_ret << dendl;
8161 return;
8162 }
8163
8164 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
8165 s->bucket->get_info().obj_lock = obj_lock;
8166 op_ret = s->bucket->put_info(this, false, real_time());
8167 return op_ret;
8168 });
8169 return;
8170 }
8171
8172 void RGWGetBucketObjectLock::pre_exec()
8173 {
8174 rgw_bucket_object_pre_exec(s);
8175 }
8176
8177 int RGWGetBucketObjectLock::verify_permission(optional_yield y)
8178 {
8179 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8180 if (has_s3_resource_tag)
8181 rgw_iam_add_buckettags(this, s);
8182
8183 return verify_bucket_owner_or_policy(s, rgw::IAM::s3GetBucketObjectLockConfiguration);
8184 }
8185
8186 void RGWGetBucketObjectLock::execute(optional_yield y)
8187 {
8188 if (!s->bucket->get_info().obj_lock_enabled()) {
8189 op_ret = -ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION;
8190 return;
8191 }
8192 }
8193
8194 int RGWPutObjRetention::verify_permission(optional_yield y)
8195 {
8196 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
8197 if (has_s3_existing_tag || has_s3_resource_tag)
8198 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
8199
8200 if (!verify_object_permission(this, s, rgw::IAM::s3PutObjectRetention)) {
8201 return -EACCES;
8202 }
8203 op_ret = get_params(y);
8204 if (op_ret) {
8205 return op_ret;
8206 }
8207 if (bypass_governance_mode) {
8208 bypass_perm = verify_object_permission(this, s, rgw::IAM::s3BypassGovernanceRetention);
8209 }
8210 return 0;
8211 }
8212
8213 void RGWPutObjRetention::pre_exec()
8214 {
8215 rgw_bucket_object_pre_exec(s);
8216 }
8217
8218 void RGWPutObjRetention::execute(optional_yield y)
8219 {
8220 if (!s->bucket->get_info().obj_lock_enabled()) {
8221 s->err.message = "object retention can't be set if bucket object lock not configured";
8222 ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
8223 op_ret = -ERR_INVALID_REQUEST;
8224 return;
8225 }
8226
8227 RGWXMLDecoder::XMLParser parser;
8228 if (!parser.init()) {
8229 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
8230 op_ret = -EINVAL;
8231 return;
8232 }
8233
8234 if (!parser.parse(data.c_str(), data.length(), 1)) {
8235 op_ret = -ERR_MALFORMED_XML;
8236 return;
8237 }
8238
8239 try {
8240 RGWXMLDecoder::decode_xml("Retention", obj_retention, &parser, true);
8241 } catch (RGWXMLDecoder::err& err) {
8242 ldpp_dout(this, 5) << "unexpected xml:" << err << dendl;
8243 op_ret = -ERR_MALFORMED_XML;
8244 return;
8245 }
8246
8247 if (ceph::real_clock::to_time_t(obj_retention.get_retain_until_date()) < ceph_clock_now()) {
8248 s->err.message = "the retain-until date must be in the future";
8249 ldpp_dout(this, 0) << "ERROR: " << s->err.message << dendl;
8250 op_ret = -EINVAL;
8251 return;
8252 }
8253 bufferlist bl;
8254 obj_retention.encode(bl);
8255
8256 //check old retention
8257 op_ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, this);
8258 if (op_ret < 0) {
8259 ldpp_dout(this, 0) << "ERROR: get obj attr error"<< dendl;
8260 return;
8261 }
8262 rgw::sal::Attrs attrs = s->object->get_attrs();
8263 auto aiter = attrs.find(RGW_ATTR_OBJECT_RETENTION);
8264 if (aiter != attrs.end()) {
8265 RGWObjectRetention old_obj_retention;
8266 try {
8267 decode(old_obj_retention, aiter->second);
8268 } catch (buffer::error& err) {
8269 ldpp_dout(this, 0) << "ERROR: failed to decode RGWObjectRetention" << dendl;
8270 op_ret = -EIO;
8271 return;
8272 }
8273 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())) {
8274 if (old_obj_retention.get_mode().compare("GOVERNANCE") != 0 || !bypass_perm || !bypass_governance_mode) {
8275 s->err.message = "proposed retain-until date shortens an existing retention period and governance bypass check failed";
8276 op_ret = -EACCES;
8277 return;
8278 }
8279 } else if (old_obj_retention.get_mode() == obj_retention.get_mode()) {
8280 // ok if retention mode doesn't change
8281 } else if (obj_retention.get_mode() == "GOVERNANCE") {
8282 s->err.message = "can't change retention mode from COMPLIANCE to GOVERNANCE";
8283 op_ret = -EACCES;
8284 return;
8285 } else if (!bypass_perm || !bypass_governance_mode) {
8286 s->err.message = "can't change retention mode from GOVERNANCE without governance bypass";
8287 op_ret = -EACCES;
8288 return;
8289 }
8290 }
8291
8292 op_ret = s->object->modify_obj_attrs(s->obj_ctx, RGW_ATTR_OBJECT_RETENTION, bl, s->yield, this);
8293
8294 return;
8295 }
8296
8297 int RGWGetObjRetention::verify_permission(optional_yield y)
8298 {
8299 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
8300 if (has_s3_existing_tag || has_s3_resource_tag)
8301 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
8302
8303 if (!verify_object_permission(this, s, rgw::IAM::s3GetObjectRetention)) {
8304 return -EACCES;
8305 }
8306 return 0;
8307 }
8308
8309 void RGWGetObjRetention::pre_exec()
8310 {
8311 rgw_bucket_object_pre_exec(s);
8312 }
8313
8314 void RGWGetObjRetention::execute(optional_yield y)
8315 {
8316 if (!s->bucket->get_info().obj_lock_enabled()) {
8317 s->err.message = "bucket object lock not configured";
8318 ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
8319 op_ret = -ERR_INVALID_REQUEST;
8320 return;
8321 }
8322 op_ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, this);
8323 if (op_ret < 0) {
8324 ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << s->object
8325 << " ret=" << op_ret << dendl;
8326 return;
8327 }
8328 rgw::sal::Attrs attrs = s->object->get_attrs();
8329 auto aiter = attrs.find(RGW_ATTR_OBJECT_RETENTION);
8330 if (aiter == attrs.end()) {
8331 op_ret = -ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION;
8332 return;
8333 }
8334
8335 bufferlist::const_iterator iter{&aiter->second};
8336 try {
8337 obj_retention.decode(iter);
8338 } catch (const buffer::error& e) {
8339 ldpp_dout(this, 0) << __func__ << "decode object retention config failed" << dendl;
8340 op_ret = -EIO;
8341 return;
8342 }
8343 return;
8344 }
8345
8346 int RGWPutObjLegalHold::verify_permission(optional_yield y)
8347 {
8348 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
8349 if (has_s3_existing_tag || has_s3_resource_tag)
8350 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
8351
8352 if (!verify_object_permission(this, s, rgw::IAM::s3PutObjectLegalHold)) {
8353 return -EACCES;
8354 }
8355 return 0;
8356 }
8357
8358 void RGWPutObjLegalHold::pre_exec()
8359 {
8360 rgw_bucket_object_pre_exec(s);
8361 }
8362
8363 void RGWPutObjLegalHold::execute(optional_yield y) {
8364 if (!s->bucket->get_info().obj_lock_enabled()) {
8365 s->err.message = "object legal hold can't be set if bucket object lock not enabled";
8366 ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
8367 op_ret = -ERR_INVALID_REQUEST;
8368 return;
8369 }
8370
8371 RGWXMLDecoder::XMLParser parser;
8372 if (!parser.init()) {
8373 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
8374 op_ret = -EINVAL;
8375 return;
8376 }
8377
8378 op_ret = get_params(y);
8379 if (op_ret < 0)
8380 return;
8381
8382 if (!parser.parse(data.c_str(), data.length(), 1)) {
8383 op_ret = -ERR_MALFORMED_XML;
8384 return;
8385 }
8386
8387 try {
8388 RGWXMLDecoder::decode_xml("LegalHold", obj_legal_hold, &parser, true);
8389 } catch (RGWXMLDecoder::err &err) {
8390 ldpp_dout(this, 5) << "unexpected xml:" << err << dendl;
8391 op_ret = -ERR_MALFORMED_XML;
8392 return;
8393 }
8394 bufferlist bl;
8395 obj_legal_hold.encode(bl);
8396 //if instance is empty, we should modify the latest object
8397 op_ret = s->object->modify_obj_attrs(s->obj_ctx, RGW_ATTR_OBJECT_LEGAL_HOLD, bl, s->yield, this);
8398 return;
8399 }
8400
8401 int RGWGetObjLegalHold::verify_permission(optional_yield y)
8402 {
8403 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s);
8404 if (has_s3_existing_tag || has_s3_resource_tag)
8405 rgw_iam_add_objtags(this, s, has_s3_existing_tag, has_s3_resource_tag);
8406
8407 if (!verify_object_permission(this, s, rgw::IAM::s3GetObjectLegalHold)) {
8408 return -EACCES;
8409 }
8410 return 0;
8411 }
8412
8413 void RGWGetObjLegalHold::pre_exec()
8414 {
8415 rgw_bucket_object_pre_exec(s);
8416 }
8417
8418 void RGWGetObjLegalHold::execute(optional_yield y)
8419 {
8420 if (!s->bucket->get_info().obj_lock_enabled()) {
8421 s->err.message = "bucket object lock not configured";
8422 ldpp_dout(this, 4) << "ERROR: " << s->err.message << dendl;
8423 op_ret = -ERR_INVALID_REQUEST;
8424 return;
8425 }
8426 map<string, bufferlist> attrs;
8427 op_ret = s->object->get_obj_attrs(s->obj_ctx, s->yield, this);
8428 if (op_ret < 0) {
8429 ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << s->object
8430 << " ret=" << op_ret << dendl;
8431 return;
8432 }
8433 auto aiter = s->object->get_attrs().find(RGW_ATTR_OBJECT_LEGAL_HOLD);
8434 if (aiter == s->object->get_attrs().end()) {
8435 op_ret = -ERR_NO_SUCH_OBJECT_LOCK_CONFIGURATION;
8436 return;
8437 }
8438
8439 bufferlist::const_iterator iter{&aiter->second};
8440 try {
8441 obj_legal_hold.decode(iter);
8442 } catch (const buffer::error& e) {
8443 ldpp_dout(this, 0) << __func__ << "decode object legal hold config failed" << dendl;
8444 op_ret = -EIO;
8445 return;
8446 }
8447 return;
8448 }
8449
8450 void RGWGetClusterStat::execute(optional_yield y)
8451 {
8452 op_ret = store->cluster_stat(stats_op);
8453 }
8454
8455 int RGWGetBucketPolicyStatus::verify_permission(optional_yield y)
8456 {
8457 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8458 if (has_s3_resource_tag)
8459 rgw_iam_add_buckettags(this, s);
8460
8461 if (!verify_bucket_permission(this, s, rgw::IAM::s3GetBucketPolicyStatus)) {
8462 return -EACCES;
8463 }
8464
8465 return 0;
8466 }
8467
8468 void RGWGetBucketPolicyStatus::execute(optional_yield y)
8469 {
8470 isPublic = (s->iam_policy && rgw::IAM::is_public(*s->iam_policy)) || s->bucket_acl->is_public(this);
8471 }
8472
8473 int RGWPutBucketPublicAccessBlock::verify_permission(optional_yield y)
8474 {
8475 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8476 if (has_s3_resource_tag)
8477 rgw_iam_add_buckettags(this, s);
8478
8479 if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketPublicAccessBlock)) {
8480 return -EACCES;
8481 }
8482
8483 return 0;
8484 }
8485
8486 int RGWPutBucketPublicAccessBlock::get_params(optional_yield y)
8487 {
8488 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
8489 std::tie(op_ret, data) = read_all_input(s, max_size, false);
8490 return op_ret;
8491 }
8492
8493 void RGWPutBucketPublicAccessBlock::execute(optional_yield y)
8494 {
8495 RGWXMLDecoder::XMLParser parser;
8496 if (!parser.init()) {
8497 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
8498 op_ret = -EINVAL;
8499 return;
8500 }
8501
8502 op_ret = get_params(y);
8503 if (op_ret < 0)
8504 return;
8505
8506 if (!parser.parse(data.c_str(), data.length(), 1)) {
8507 ldpp_dout(this, 0) << "ERROR: malformed XML" << dendl;
8508 op_ret = -ERR_MALFORMED_XML;
8509 return;
8510 }
8511
8512 try {
8513 RGWXMLDecoder::decode_xml("PublicAccessBlockConfiguration", access_conf, &parser, true);
8514 } catch (RGWXMLDecoder::err &err) {
8515 ldpp_dout(this, 5) << "unexpected xml:" << err << dendl;
8516 op_ret = -ERR_MALFORMED_XML;
8517 return;
8518 }
8519
8520 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
8521 if (op_ret < 0) {
8522 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
8523 return;
8524 }
8525
8526 bufferlist bl;
8527 access_conf.encode(bl);
8528 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this, &bl] {
8529 rgw::sal::Attrs attrs(s->bucket_attrs);
8530 attrs[RGW_ATTR_PUBLIC_ACCESS] = bl;
8531 return s->bucket->merge_and_store_attrs(this, attrs, s->yield);
8532 });
8533
8534 }
8535
8536 int RGWGetBucketPublicAccessBlock::verify_permission(optional_yield y)
8537 {
8538 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8539 if (has_s3_resource_tag)
8540 rgw_iam_add_buckettags(this, s);
8541
8542 if (!verify_bucket_permission(this, s, rgw::IAM::s3GetBucketPolicy)) {
8543 return -EACCES;
8544 }
8545
8546 return 0;
8547 }
8548
8549 void RGWGetBucketPublicAccessBlock::execute(optional_yield y)
8550 {
8551 auto attrs = s->bucket_attrs;
8552 if (auto aiter = attrs.find(RGW_ATTR_PUBLIC_ACCESS);
8553 aiter == attrs.end()) {
8554 ldpp_dout(this, 0) << "can't find bucket IAM POLICY attr bucket_name = "
8555 << s->bucket_name << dendl;
8556 // return the default;
8557 return;
8558 } else {
8559 bufferlist::const_iterator iter{&aiter->second};
8560 try {
8561 access_conf.decode(iter);
8562 } catch (const buffer::error& e) {
8563 ldpp_dout(this, 0) << __func__ << "decode access_conf failed" << dendl;
8564 op_ret = -EIO;
8565 return;
8566 }
8567 }
8568 }
8569
8570
8571 void RGWDeleteBucketPublicAccessBlock::send_response()
8572 {
8573 if (op_ret) {
8574 set_req_state_err(s, op_ret);
8575 }
8576 dump_errno(s);
8577 end_header(s);
8578 }
8579
8580 int RGWDeleteBucketPublicAccessBlock::verify_permission(optional_yield y)
8581 {
8582 auto [has_s3_existing_tag, has_s3_resource_tag] = rgw_check_policy_condition(this, s, false);
8583 if (has_s3_resource_tag)
8584 rgw_iam_add_buckettags(this, s);
8585
8586 if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketPublicAccessBlock)) {
8587 return -EACCES;
8588 }
8589
8590 return 0;
8591 }
8592
8593 void RGWDeleteBucketPublicAccessBlock::execute(optional_yield y)
8594 {
8595 bufferlist data;
8596 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
8597 if (op_ret < 0) {
8598 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
8599 return;
8600 }
8601
8602 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this] {
8603 rgw::sal::Attrs attrs(s->bucket_attrs);
8604 attrs.erase(RGW_ATTR_PUBLIC_ACCESS);
8605 op_ret = s->bucket->merge_and_store_attrs(this, attrs, s->yield);
8606 return op_ret;
8607 });
8608 }
8609
8610 int RGWPutBucketEncryption::get_params(optional_yield y)
8611 {
8612 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
8613 std::tie(op_ret, data) = read_all_input(s, max_size, false);
8614 return op_ret;
8615 }
8616
8617 int RGWPutBucketEncryption::verify_permission(optional_yield y)
8618 {
8619 if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketEncryption)) {
8620 return -EACCES;
8621 }
8622 return 0;
8623 }
8624
8625 void RGWPutBucketEncryption::execute(optional_yield y)
8626 {
8627 RGWXMLDecoder::XMLParser parser;
8628 if (!parser.init()) {
8629 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
8630 op_ret = -EINVAL;
8631 return;
8632 }
8633 op_ret = get_params(y);
8634 if (op_ret < 0) {
8635 return;
8636 }
8637 if (!parser.parse(data.c_str(), data.length(), 1)) {
8638 ldpp_dout(this, 0) << "ERROR: malformed XML" << dendl;
8639 op_ret = -ERR_MALFORMED_XML;
8640 return;
8641 }
8642
8643 try {
8644 RGWXMLDecoder::decode_xml("ServerSideEncryptionConfiguration", bucket_encryption_conf, &parser, true);
8645 } catch (RGWXMLDecoder::err& err) {
8646 ldpp_dout(this, 5) << "unexpected xml:" << err << dendl;
8647 op_ret = -ERR_MALFORMED_XML;
8648 return;
8649 }
8650
8651 if(bucket_encryption_conf.kms_master_key_id().compare("") != 0) {
8652 ldpp_dout(this, 5) << "encryption not supported with sse-kms" << dendl;
8653 op_ret = -ERR_NOT_IMPLEMENTED;
8654 s->err.message = "SSE-KMS support is not provided";
8655 return;
8656 }
8657
8658 if(bucket_encryption_conf.sse_algorithm().compare("AES256") != 0) {
8659 ldpp_dout(this, 5) << "only aes256 algorithm is supported for encryption" << dendl;
8660 op_ret = -ERR_NOT_IMPLEMENTED;
8661 s->err.message = "Encryption is supported only with AES256 algorithm";
8662 return;
8663 }
8664
8665 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
8666 if (op_ret < 0) {
8667 ldpp_dout(this, 20) << "forward_request_to_master returned ret=" << op_ret << dendl;
8668 return;
8669 }
8670
8671 bufferlist key_id_bl;
8672 string bucket_owner_id = s->bucket->get_info().owner.id;
8673 key_id_bl.append(bucket_owner_id.c_str(), bucket_owner_id.size() + 1);
8674
8675 bufferlist conf_bl;
8676 bucket_encryption_conf.encode(conf_bl);
8677 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this, y, &conf_bl, &key_id_bl] {
8678 rgw::sal::Attrs attrs = s->bucket->get_attrs();
8679 attrs[RGW_ATTR_BUCKET_ENCRYPTION_POLICY] = conf_bl;
8680 attrs[RGW_ATTR_BUCKET_ENCRYPTION_KEY_ID] = key_id_bl;
8681 return s->bucket->merge_and_store_attrs(this, attrs, y);
8682 });
8683 }
8684
8685 int RGWGetBucketEncryption::verify_permission(optional_yield y)
8686 {
8687 if (!verify_bucket_permission(this, s, rgw::IAM::s3GetBucketEncryption)) {
8688 return -EACCES;
8689 }
8690 return 0;
8691 }
8692
8693 void RGWGetBucketEncryption::execute(optional_yield y)
8694 {
8695 const auto& attrs = s->bucket_attrs;
8696 if (auto aiter = attrs.find(RGW_ATTR_BUCKET_ENCRYPTION_POLICY);
8697 aiter == attrs.end()) {
8698 ldpp_dout(this, 0) << "can't find BUCKET ENCRYPTION attr for bucket_name = " << s->bucket_name << dendl;
8699 op_ret = -ENOENT;
8700 s->err.message = "The server side encryption configuration was not found";
8701 return;
8702 } else {
8703 bufferlist::const_iterator iter{&aiter->second};
8704 try {
8705 bucket_encryption_conf.decode(iter);
8706 } catch (const buffer::error& e) {
8707 ldpp_dout(this, 0) << __func__ << "decode bucket_encryption_conf failed" << dendl;
8708 op_ret = -EIO;
8709 return;
8710 }
8711 }
8712 }
8713
8714 int RGWDeleteBucketEncryption::verify_permission(optional_yield y)
8715 {
8716 if (!verify_bucket_permission(this, s, rgw::IAM::s3PutBucketEncryption)) {
8717 return -EACCES;
8718 }
8719 return 0;
8720 }
8721
8722 void RGWDeleteBucketEncryption::execute(optional_yield y)
8723 {
8724 bufferlist data;
8725 op_ret = store->forward_request_to_master(this, s->user.get(), nullptr, data, nullptr, s->info, y);
8726 if (op_ret < 0) {
8727 ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
8728 return;
8729 }
8730
8731 op_ret = retry_raced_bucket_write(this, s->bucket.get(), [this, y] {
8732 rgw::sal::Attrs attrs = s->bucket->get_attrs();
8733 attrs.erase(RGW_ATTR_BUCKET_ENCRYPTION_POLICY);
8734 attrs.erase(RGW_ATTR_BUCKET_ENCRYPTION_KEY_ID);
8735 op_ret = s->bucket->merge_and_store_attrs(this, attrs, y);
8736 return op_ret;
8737 });
8738 }
8739
8740 void rgw_slo_entry::decode_json(JSONObj *obj)
8741 {
8742 JSONDecoder::decode_json("path", path, obj);
8743 JSONDecoder::decode_json("etag", etag, obj);
8744 JSONDecoder::decode_json("size_bytes", size_bytes, obj);
8745 };
8746