]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_rest_s3.cc
0de040968c7c85d2ce4f9b2683766aaa21c0a3d4
[ceph.git] / ceph / src / rgw / rgw_rest_s3.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 <array>
6 #include <string.h>
7
8 #include "common/ceph_crypto.h"
9 #include "common/Formatter.h"
10 #include "common/utf8.h"
11 #include "common/ceph_json.h"
12 #include "common/safe_io.h"
13 #include "common/errno.h"
14 #include "auth/Crypto.h"
15 #include <boost/algorithm/string.hpp>
16 #include <boost/algorithm/string/replace.hpp>
17 #include <boost/utility/string_view.hpp>
18 #include <boost/tokenizer.hpp>
19
20 #include <liboath/oath.h>
21
22 #include "rgw_rest.h"
23 #include "rgw_rest_s3.h"
24 #include "rgw_rest_s3website.h"
25 #include "rgw_rest_pubsub.h"
26 #include "rgw_auth_s3.h"
27 #include "rgw_acl.h"
28 #include "rgw_policy_s3.h"
29 #include "rgw_user.h"
30 #include "rgw_cors.h"
31 #include "rgw_cors_s3.h"
32 #include "rgw_tag_s3.h"
33
34 #include "rgw_client_io.h"
35
36 #include "rgw_keystone.h"
37 #include "rgw_auth_keystone.h"
38 #include "rgw_auth_registry.h"
39
40 #include "rgw_es_query.h"
41
42 #include <typeinfo> // for 'typeid'
43
44 #include "rgw_ldap.h"
45 #include "rgw_token.h"
46 #include "rgw_rest_role.h"
47 #include "rgw_crypt.h"
48 #include "rgw_crypt_sanitize.h"
49 #include "rgw_rest_user_policy.h"
50 #include "rgw_zone.h"
51 #include "rgw_bucket_sync.h"
52
53 #include "services/svc_zone.h"
54 #include "services/svc_cls.h"
55
56 #include "include/ceph_assert.h"
57 #include "rgw_role.h"
58 #include "rgw_rest_sts.h"
59 #include "rgw_rest_iam.h"
60 #include "rgw_sts.h"
61
62 #define dout_context g_ceph_context
63 #define dout_subsys ceph_subsys_rgw
64
65 using namespace rgw;
66 using namespace ceph::crypto;
67
68 using std::get;
69
70 void list_all_buckets_start(struct req_state *s)
71 {
72 s->formatter->open_array_section_in_ns("ListAllMyBucketsResult", XMLNS_AWS_S3);
73 }
74
75 void list_all_buckets_end(struct req_state *s)
76 {
77 s->formatter->close_section();
78 }
79
80 void dump_bucket(struct req_state *s, rgw::sal::RGWBucket& obj)
81 {
82 s->formatter->open_object_section("Bucket");
83 s->formatter->dump_string("Name", obj.get_name());
84 dump_time(s, "CreationDate", &obj.get_creation_time());
85 s->formatter->close_section();
86 }
87
88 void rgw_get_errno_s3(rgw_http_error *e , int err_no)
89 {
90 rgw_http_errors::const_iterator r = rgw_http_s3_errors.find(err_no);
91
92 if (r != rgw_http_s3_errors.end()) {
93 e->http_ret = r->second.first;
94 e->s3_code = r->second.second;
95 } else {
96 e->http_ret = 500;
97 e->s3_code = "UnknownError";
98 }
99 }
100
101 static inline std::string get_s3_expiration_header(
102 struct req_state* s,
103 const ceph::real_time& mtime)
104 {
105 return rgw::lc::s3_expiration_header(
106 s, s->object, s->tagset, mtime, s->bucket_attrs);
107 }
108
109 struct response_attr_param {
110 const char *param;
111 const char *http_attr;
112 };
113
114 static struct response_attr_param resp_attr_params[] = {
115 {"response-content-type", "Content-Type"},
116 {"response-content-language", "Content-Language"},
117 {"response-expires", "Expires"},
118 {"response-cache-control", "Cache-Control"},
119 {"response-content-disposition", "Content-Disposition"},
120 {"response-content-encoding", "Content-Encoding"},
121 {NULL, NULL},
122 };
123
124 int RGWGetObj_ObjStore_S3Website::send_response_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) {
125 map<string, bufferlist>::iterator iter;
126 iter = attrs.find(RGW_ATTR_AMZ_WEBSITE_REDIRECT_LOCATION);
127 if (iter != attrs.end()) {
128 bufferlist &bl = iter->second;
129 s->redirect = bl.c_str();
130 s->err.http_ret = 301;
131 ldpp_dout(this, 20) << __CEPH_ASSERT_FUNCTION << " redirecting per x-amz-website-redirect-location=" << s->redirect << dendl;
132 op_ret = -ERR_WEBSITE_REDIRECT;
133 set_req_state_err(s, op_ret);
134 dump_errno(s);
135 dump_content_length(s, 0);
136 dump_redirect(s, s->redirect);
137 end_header(s, this);
138 return op_ret;
139 } else {
140 return RGWGetObj_ObjStore_S3::send_response_data(bl, bl_ofs, bl_len);
141 }
142 }
143
144 int RGWGetObj_ObjStore_S3Website::send_response_data_error()
145 {
146 return RGWGetObj_ObjStore_S3::send_response_data_error();
147 }
148
149 int RGWGetObj_ObjStore_S3::get_params()
150 {
151 // for multisite sync requests, only read the slo manifest itself, rather than
152 // all of the data from its parts. the parts will sync as separate objects
153 skip_manifest = s->info.args.exists(RGW_SYS_PARAM_PREFIX "sync-manifest");
154
155 // multisite sync requests should fetch encrypted data, along with the
156 // attributes needed to support decryption on the other zone
157 if (s->system_request) {
158 skip_decrypt = s->info.args.exists(RGW_SYS_PARAM_PREFIX "skip-decrypt");
159 }
160
161 return RGWGetObj_ObjStore::get_params();
162 }
163
164 int RGWGetObj_ObjStore_S3::send_response_data_error()
165 {
166 bufferlist bl;
167 return send_response_data(bl, 0 , 0);
168 }
169
170 template <class T>
171 int decode_attr_bl_single_value(map<string, bufferlist>& attrs, const char *attr_name, T *result, T def_val)
172 {
173 map<string, bufferlist>::iterator iter = attrs.find(attr_name);
174 if (iter == attrs.end()) {
175 *result = def_val;
176 return 0;
177 }
178 bufferlist& bl = iter->second;
179 if (bl.length() == 0) {
180 *result = def_val;
181 return 0;
182 }
183 auto bliter = bl.cbegin();
184 try {
185 decode(*result, bliter);
186 } catch (buffer::error& err) {
187 return -EIO;
188 }
189 return 0;
190 }
191
192 inline bool str_has_cntrl(const std::string s) {
193 return std::any_of(s.begin(), s.end(), ::iscntrl);
194 }
195
196 inline bool str_has_cntrl(const char* s) {
197 std::string _s(s);
198 return str_has_cntrl(_s);
199 }
200
201 int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs,
202 off_t bl_len)
203 {
204 const char *content_type = NULL;
205 string content_type_str;
206 map<string, string> response_attrs;
207 map<string, string>::iterator riter;
208 bufferlist metadata_bl;
209
210 string expires = get_s3_expiration_header(s, lastmod);
211
212 if (sent_header)
213 goto send_data;
214
215 if (custom_http_ret) {
216 set_req_state_err(s, 0);
217 dump_errno(s, custom_http_ret);
218 } else {
219 set_req_state_err(s, (partial_content && !op_ret) ? STATUS_PARTIAL_CONTENT
220 : op_ret);
221 dump_errno(s);
222 }
223
224 if (op_ret)
225 goto done;
226
227 if (range_str)
228 dump_range(s, start, end, s->obj_size);
229
230 if (s->system_request &&
231 s->info.args.exists(RGW_SYS_PARAM_PREFIX "prepend-metadata")) {
232
233 dump_header(s, "Rgwx-Object-Size", (long long)total_len);
234
235 if (rgwx_stat) {
236 /*
237 * in this case, we're not returning the object's content, only the prepended
238 * extra metadata
239 */
240 total_len = 0;
241 }
242
243 /* JSON encode object metadata */
244 JSONFormatter jf;
245 jf.open_object_section("obj_metadata");
246 encode_json("attrs", attrs, &jf);
247 utime_t ut(lastmod);
248 encode_json("mtime", ut, &jf);
249 jf.close_section();
250 stringstream ss;
251 jf.flush(ss);
252 metadata_bl.append(ss.str());
253 dump_header(s, "Rgwx-Embedded-Metadata-Len", metadata_bl.length());
254 total_len += metadata_bl.length();
255 }
256
257 if (s->system_request && !real_clock::is_zero(lastmod)) {
258 /* we end up dumping mtime in two different methods, a bit redundant */
259 dump_epoch_header(s, "Rgwx-Mtime", lastmod);
260 uint64_t pg_ver = 0;
261 int r = decode_attr_bl_single_value(attrs, RGW_ATTR_PG_VER, &pg_ver, (uint64_t)0);
262 if (r < 0) {
263 ldpp_dout(this, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl;
264 }
265 dump_header(s, "Rgwx-Obj-PG-Ver", pg_ver);
266
267 uint32_t source_zone_short_id = 0;
268 r = decode_attr_bl_single_value(attrs, RGW_ATTR_SOURCE_ZONE, &source_zone_short_id, (uint32_t)0);
269 if (r < 0) {
270 ldpp_dout(this, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl;
271 }
272 if (source_zone_short_id != 0) {
273 dump_header(s, "Rgwx-Source-Zone-Short-Id", source_zone_short_id);
274 }
275 }
276
277 for (auto &it : crypt_http_responses)
278 dump_header(s, it.first, it.second);
279
280 dump_content_length(s, total_len);
281 dump_last_modified(s, lastmod);
282 dump_header_if_nonempty(s, "x-amz-version-id", version_id);
283 dump_header_if_nonempty(s, "x-amz-expiration", expires);
284
285 if (attrs.find(RGW_ATTR_APPEND_PART_NUM) != attrs.end()) {
286 dump_header(s, "x-rgw-object-type", "Appendable");
287 dump_header(s, "x-rgw-next-append-position", s->obj_size);
288 } else {
289 dump_header(s, "x-rgw-object-type", "Normal");
290 }
291
292 if (! op_ret) {
293 if (! lo_etag.empty()) {
294 /* Handle etag of Swift API's large objects (DLO/SLO). It's entirerly
295 * legit to perform GET on them through S3 API. In such situation,
296 * a client should receive the composited content with corresponding
297 * etag value. */
298 dump_etag(s, lo_etag);
299 } else {
300 auto iter = attrs.find(RGW_ATTR_ETAG);
301 if (iter != attrs.end()) {
302 dump_etag(s, iter->second.to_str());
303 }
304 }
305
306 for (struct response_attr_param *p = resp_attr_params; p->param; p++) {
307 bool exists;
308 string val = s->info.args.get(p->param, &exists);
309 if (exists) {
310 /* reject unauthenticated response header manipulation, see
311 * https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html */
312 if (s->auth.identity->is_anonymous()) {
313 return -ERR_INVALID_REQUEST;
314 }
315 /* HTTP specification says no control characters should be present in
316 * header values: https://tools.ietf.org/html/rfc7230#section-3.2
317 * field-vchar = VCHAR / obs-text
318 *
319 * Failure to validate this permits a CRLF injection in HTTP headers,
320 * whereas S3 GetObject only permits specific headers.
321 */
322 if(str_has_cntrl(val)) {
323 /* TODO: return a more distinct error in future;
324 * stating what the problem is */
325 return -ERR_INVALID_REQUEST;
326 }
327
328 if (strcmp(p->param, "response-content-type") != 0) {
329 response_attrs[p->http_attr] = val;
330 } else {
331 content_type_str = val;
332 content_type = content_type_str.c_str();
333 }
334 }
335 }
336
337 for (auto iter = attrs.begin(); iter != attrs.end(); ++iter) {
338 const char *name = iter->first.c_str();
339 map<string, string>::iterator aiter = rgw_to_http_attrs.find(name);
340 if (aiter != rgw_to_http_attrs.end()) {
341 if (response_attrs.count(aiter->second) == 0) {
342 /* Was not already overridden by a response param. */
343
344 size_t len = iter->second.length();
345 string s(iter->second.c_str(), len);
346 while (len && !s[len - 1]) {
347 --len;
348 s.resize(len);
349 }
350 response_attrs[aiter->second] = s;
351 }
352 } else if (iter->first.compare(RGW_ATTR_CONTENT_TYPE) == 0) {
353 /* Special handling for content_type. */
354 if (!content_type) {
355 content_type_str = rgw_bl_str(iter->second);
356 content_type = content_type_str.c_str();
357 }
358 } else if (strcmp(name, RGW_ATTR_SLO_UINDICATOR) == 0) {
359 // this attr has an extra length prefix from encode() in prior versions
360 dump_header(s, "X-Object-Meta-Static-Large-Object", "True");
361 } else if (strncmp(name, RGW_ATTR_META_PREFIX,
362 sizeof(RGW_ATTR_META_PREFIX)-1) == 0) {
363 /* User custom metadata. */
364 name += sizeof(RGW_ATTR_PREFIX) - 1;
365 dump_header(s, name, iter->second);
366 } else if (iter->first.compare(RGW_ATTR_TAGS) == 0) {
367 RGWObjTags obj_tags;
368 try{
369 auto it = iter->second.cbegin();
370 obj_tags.decode(it);
371 } catch (buffer::error &err) {
372 ldpp_dout(this,0) << "Error caught buffer::error couldn't decode TagSet " << dendl;
373 }
374 dump_header(s, RGW_AMZ_TAG_COUNT, obj_tags.count());
375 } else if (iter->first.compare(RGW_ATTR_OBJECT_RETENTION) == 0 && get_retention){
376 RGWObjectRetention retention;
377 try {
378 decode(retention, iter->second);
379 dump_header(s, "x-amz-object-lock-mode", retention.get_mode());
380 dump_time_header(s, "x-amz-object-lock-retain-until-date", retention.get_retain_until_date());
381 } catch (buffer::error& err) {
382 ldpp_dout(this, 0) << "ERROR: failed to decode RGWObjectRetention" << dendl;
383 }
384 } else if (iter->first.compare(RGW_ATTR_OBJECT_LEGAL_HOLD) == 0 && get_legal_hold) {
385 RGWObjectLegalHold legal_hold;
386 try {
387 decode(legal_hold, iter->second);
388 dump_header(s, "x-amz-object-lock-legal-hold",legal_hold.get_status());
389 } catch (buffer::error& err) {
390 ldpp_dout(this, 0) << "ERROR: failed to decode RGWObjectLegalHold" << dendl;
391 }
392 }
393 }
394 }
395
396 done:
397 for (riter = response_attrs.begin(); riter != response_attrs.end();
398 ++riter) {
399 dump_header(s, riter->first, riter->second);
400 }
401
402 if (op_ret == -ERR_NOT_MODIFIED) {
403 end_header(s, this);
404 } else {
405 if (!content_type)
406 content_type = "binary/octet-stream";
407
408 end_header(s, this, content_type);
409 }
410
411 if (metadata_bl.length()) {
412 dump_body(s, metadata_bl);
413 }
414 sent_header = true;
415
416 send_data:
417 if (get_data && !op_ret) {
418 int r = dump_body(s, bl.c_str() + bl_ofs, bl_len);
419 if (r < 0)
420 return r;
421 }
422
423 return 0;
424 }
425
426 int RGWGetObj_ObjStore_S3::get_decrypt_filter(std::unique_ptr<RGWGetObj_Filter> *filter, RGWGetObj_Filter* cb, bufferlist* manifest_bl)
427 {
428 if (skip_decrypt) { // bypass decryption for multisite sync requests
429 return 0;
430 }
431
432 int res = 0;
433 std::unique_ptr<BlockCrypt> block_crypt;
434 res = rgw_s3_prepare_decrypt(s, attrs, &block_crypt, crypt_http_responses);
435 if (res == 0) {
436 if (block_crypt != nullptr) {
437 auto f = std::make_unique<RGWGetObj_BlockDecrypt>(s->cct, cb, std::move(block_crypt));
438 if (manifest_bl != nullptr) {
439 res = f->read_manifest(*manifest_bl);
440 if (res == 0) {
441 *filter = std::move(f);
442 }
443 }
444 }
445 }
446 return res;
447 }
448
449 void RGWGetObjTags_ObjStore_S3::send_response_data(bufferlist& bl)
450 {
451 dump_errno(s);
452 end_header(s, this, "application/xml");
453 dump_start(s);
454
455 s->formatter->open_object_section_in_ns("Tagging", XMLNS_AWS_S3);
456 s->formatter->open_object_section("TagSet");
457 if (has_tags){
458 RGWObjTagSet_S3 tagset;
459 auto iter = bl.cbegin();
460 try {
461 tagset.decode(iter);
462 } catch (buffer::error& err) {
463 ldpp_dout(this,0) << "ERROR: caught buffer::error, couldn't decode TagSet" << dendl;
464 op_ret= -EIO;
465 return;
466 }
467 tagset.dump_xml(s->formatter);
468 }
469 s->formatter->close_section();
470 s->formatter->close_section();
471 rgw_flush_formatter_and_reset(s, s->formatter);
472 }
473
474
475 int RGWPutObjTags_ObjStore_S3::get_params()
476 {
477 RGWXMLParser parser;
478
479 if (!parser.init()){
480 return -EINVAL;
481 }
482
483 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
484
485 int r = 0;
486 bufferlist data;
487 std::tie(r, data) = rgw_rest_read_all_input(s, max_size, false);
488
489 if (r < 0)
490 return r;
491
492 if (!parser.parse(data.c_str(), data.length(), 1)) {
493 return -ERR_MALFORMED_XML;
494 }
495
496 RGWObjTagging_S3 tagging;
497
498 try {
499 RGWXMLDecoder::decode_xml("Tagging", tagging, &parser);
500 } catch (RGWXMLDecoder::err& err) {
501 ldpp_dout(this, 5) << "Malformed tagging request: " << err << dendl;
502 return -ERR_MALFORMED_XML;
503 }
504
505 RGWObjTags obj_tags;
506 r = tagging.rebuild(obj_tags);
507 if (r < 0)
508 return r;
509
510 obj_tags.encode(tags_bl);
511 ldpp_dout(this, 20) << "Read " << obj_tags.count() << "tags" << dendl;
512
513 return 0;
514 }
515
516 void RGWPutObjTags_ObjStore_S3::send_response()
517 {
518 if (op_ret)
519 set_req_state_err(s, op_ret);
520 dump_errno(s);
521 end_header(s, this, "application/xml");
522 dump_start(s);
523
524 }
525
526 void RGWDeleteObjTags_ObjStore_S3::send_response()
527 {
528 int r = op_ret;
529 if (r == -ENOENT)
530 r = 0;
531 if (!r)
532 r = STATUS_NO_CONTENT;
533
534 set_req_state_err(s, r);
535 dump_errno(s);
536 end_header(s, this);
537 }
538
539 void RGWGetBucketTags_ObjStore_S3::send_response_data(bufferlist& bl)
540 {
541 if (op_ret)
542 set_req_state_err(s, op_ret);
543 dump_errno(s);
544 end_header(s, this, "application/xml");
545 dump_start(s);
546
547 if (!op_ret) {
548 s->formatter->open_object_section_in_ns("Tagging", XMLNS_AWS_S3);
549 s->formatter->open_object_section("TagSet");
550 if (has_tags){
551 RGWObjTagSet_S3 tagset;
552 auto iter = bl.cbegin();
553 try {
554 tagset.decode(iter);
555 } catch (buffer::error& err) {
556 ldout(s->cct,0) << "ERROR: caught buffer::error, couldn't decode TagSet" << dendl;
557 op_ret= -EIO;
558 return;
559 }
560 tagset.dump_xml(s->formatter);
561 }
562 s->formatter->close_section();
563 s->formatter->close_section();
564 rgw_flush_formatter_and_reset(s, s->formatter);
565 }
566 }
567
568 int RGWPutBucketTags_ObjStore_S3::get_params()
569 {
570 RGWXMLParser parser;
571
572 if (!parser.init()){
573 return -EINVAL;
574 }
575
576 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
577 int r = 0;
578 bufferlist data;
579
580 std::tie(r, data) = rgw_rest_read_all_input(s, max_size, false);
581
582 if (r < 0)
583 return r;
584
585 if (!parser.parse(data.c_str(), data.length(), 1)) {
586 return -ERR_MALFORMED_XML;
587 }
588
589 RGWObjTagging_S3 tagging;
590 try {
591 RGWXMLDecoder::decode_xml("Tagging", tagging, &parser);
592 } catch (RGWXMLDecoder::err& err) {
593
594 ldout(s->cct, 5) << "Malformed tagging request: " << err << dendl;
595 return -ERR_MALFORMED_XML;
596 }
597
598 RGWObjTags obj_tags(50); // A tag set can contain as many as 50 tags, or it can be empty.
599 r = tagging.rebuild(obj_tags);
600 if (r < 0)
601 return r;
602
603 obj_tags.encode(tags_bl);
604 ldout(s->cct, 20) << "Read " << obj_tags.count() << "tags" << dendl;
605
606 // forward bucket tags requests to meta master zone
607 if (!store->svc()->zone->is_meta_master()) {
608 /* only need to keep this data around if we're not meta master */
609 in_data = std::move(data);
610 }
611
612 return 0;
613 }
614
615 void RGWPutBucketTags_ObjStore_S3::send_response()
616 {
617 if (op_ret)
618 set_req_state_err(s, op_ret);
619 dump_errno(s);
620 end_header(s, this, "application/xml");
621 dump_start(s);
622 }
623
624 void RGWDeleteBucketTags_ObjStore_S3::send_response()
625 {
626 if (op_ret)
627 set_req_state_err(s, op_ret);
628 dump_errno(s);
629 end_header(s, this, "application/xml");
630 dump_start(s);
631 }
632
633 namespace {
634
635 bool is_valid_status(const string& s) {
636 return (s == "Enabled" ||
637 s == "Disabled");
638 }
639
640 static string enabled_group_id = "s3-bucket-replication:enabled";
641 static string disabled_group_id = "s3-bucket-replication:disabled";
642
643 struct ReplicationConfiguration {
644 string role;
645
646 struct Rule {
647 struct DeleteMarkerReplication {
648 string status;
649
650 void decode_xml(XMLObj *obj) {
651 RGWXMLDecoder::decode_xml("Status", status, obj);
652 }
653
654 void dump_xml(Formatter *f) const {
655 encode_xml("Status", status, f);
656 }
657
658 bool is_valid(CephContext *cct) const {
659 bool result = is_valid_status(status);
660 if (!result) {
661 ldout(cct, 5) << "NOTICE: bad status provided in DeleteMarkerReplication element (status=" << status << ")" << dendl;
662 }
663 return result;
664 }
665 };
666
667 struct Source { /* rgw extension */
668 std::vector<string> zone_names;
669
670 void decode_xml(XMLObj *obj) {
671 RGWXMLDecoder::decode_xml("Zone", zone_names, obj);
672 }
673
674 void dump_xml(Formatter *f) const {
675 encode_xml("Zone", zone_names, f);
676 }
677 };
678
679 struct Destination {
680 struct AccessControlTranslation {
681 string owner;
682
683 void decode_xml(XMLObj *obj) {
684 RGWXMLDecoder::decode_xml("Owner", owner, obj);
685 }
686 void dump_xml(Formatter *f) const {
687 encode_xml("Owner", owner, f);
688 }
689 };
690
691 std::optional<AccessControlTranslation> acl_translation;
692 std::optional<string> account;
693 string bucket;
694 std::optional<string> storage_class;
695 std::vector<string> zone_names;
696
697 void decode_xml(XMLObj *obj) {
698 RGWXMLDecoder::decode_xml("AccessControlTranslation", acl_translation, obj);
699 RGWXMLDecoder::decode_xml("Account", account, obj);
700 if (account && account->empty()) {
701 account.reset();
702 }
703 RGWXMLDecoder::decode_xml("Bucket", bucket, obj);
704 RGWXMLDecoder::decode_xml("StorageClass", storage_class, obj);
705 if (storage_class && storage_class->empty()) {
706 storage_class.reset();
707 }
708 RGWXMLDecoder::decode_xml("Zone", zone_names, obj); /* rgw extension */
709 }
710
711 void dump_xml(Formatter *f) const {
712 encode_xml("AccessControlTranslation", acl_translation, f);
713 encode_xml("Account", account, f);
714 encode_xml("Bucket", bucket, f);
715 encode_xml("StorageClass", storage_class, f);
716 encode_xml("Zone", zone_names, f);
717 }
718 };
719
720 struct Filter {
721 struct Tag {
722 string key;
723 string value;
724
725 bool empty() const {
726 return key.empty() && value.empty();
727 }
728
729 void decode_xml(XMLObj *obj) {
730 RGWXMLDecoder::decode_xml("Key", key, obj);
731 RGWXMLDecoder::decode_xml("Value", value, obj);
732 };
733
734 void dump_xml(Formatter *f) const {
735 encode_xml("Key", key, f);
736 encode_xml("Value", value, f);
737 }
738 };
739
740 struct AndElements {
741 std::optional<string> prefix;
742 std::vector<Tag> tags;
743
744 bool empty() const {
745 return !prefix &&
746 (tags.size() == 0);
747 }
748
749 void decode_xml(XMLObj *obj) {
750 std::vector<Tag> _tags;
751 RGWXMLDecoder::decode_xml("Prefix", prefix, obj);
752 if (prefix && prefix->empty()) {
753 prefix.reset();
754 }
755 RGWXMLDecoder::decode_xml("Tag", _tags, obj);
756 for (auto& t : _tags) {
757 if (!t.empty()) {
758 tags.push_back(std::move(t));
759 }
760 }
761 };
762
763 void dump_xml(Formatter *f) const {
764 encode_xml("Prefix", prefix, f);
765 encode_xml("Tag", tags, f);
766 }
767 };
768
769 std::optional<string> prefix;
770 std::optional<Tag> tag;
771 std::optional<AndElements> and_elements;
772
773 bool empty() const {
774 return (!prefix && !tag && !and_elements);
775 }
776
777 void decode_xml(XMLObj *obj) {
778 RGWXMLDecoder::decode_xml("Prefix", prefix, obj);
779 if (prefix && prefix->empty()) {
780 prefix.reset();
781 }
782 RGWXMLDecoder::decode_xml("Tag", tag, obj);
783 if (tag && tag->empty()) {
784 tag.reset();
785 }
786 RGWXMLDecoder::decode_xml("And", and_elements, obj);
787 if (and_elements && and_elements->empty()) {
788 and_elements.reset();
789 }
790 };
791
792 void dump_xml(Formatter *f) const {
793 encode_xml("Prefix", prefix, f);
794 encode_xml("Tag", tag, f);
795 encode_xml("And", and_elements, f);
796 }
797
798 bool is_valid(CephContext *cct) const {
799 if (tag && prefix) {
800 ldout(cct, 5) << "NOTICE: both tag and prefix were provided in replication filter rule" << dendl;
801 return false;
802 }
803
804 if (and_elements) {
805 if (prefix && and_elements->prefix) {
806 ldout(cct, 5) << "NOTICE: too many prefixes were provided in re" << dendl;
807 return false;
808 }
809 }
810 return true;
811 };
812
813 int to_sync_pipe_filter(CephContext *cct,
814 rgw_sync_pipe_filter *f) const {
815 if (!is_valid(cct)) {
816 return -EINVAL;
817 }
818 if (prefix) {
819 f->prefix = *prefix;
820 }
821 if (tag) {
822 f->tags.insert(rgw_sync_pipe_filter_tag(tag->key, tag->value));
823 }
824
825 if (and_elements) {
826 if (and_elements->prefix) {
827 f->prefix = *and_elements->prefix;
828 }
829 for (auto& t : and_elements->tags) {
830 f->tags.insert(rgw_sync_pipe_filter_tag(t.key, t.value));
831 }
832 }
833 return 0;
834 }
835
836 void from_sync_pipe_filter(const rgw_sync_pipe_filter& f) {
837 if (f.prefix && f.tags.empty()) {
838 prefix = f.prefix;
839 return;
840 }
841 if (f.prefix) {
842 and_elements.emplace();
843 and_elements->prefix = f.prefix;
844 } else if (f.tags.size() == 1) {
845 auto iter = f.tags.begin();
846 if (iter == f.tags.end()) {
847 /* should never happen */
848 return;
849 }
850 auto& t = *iter;
851 tag.emplace();
852 tag->key = t.key;
853 tag->value = t.value;
854 return;
855 }
856
857 if (f.tags.empty()) {
858 return;
859 }
860
861 if (!and_elements) {
862 and_elements.emplace();
863 }
864
865 for (auto& t : f.tags) {
866 auto& tag = and_elements->tags.emplace_back();
867 tag.key = t.key;
868 tag.value = t.value;
869 }
870 }
871 };
872
873 set<rgw_zone_id> get_zone_ids_from_names(rgw::sal::RGWRadosStore *store,
874 const vector<string>& zone_names) const {
875 set<rgw_zone_id> ids;
876
877 for (auto& name : zone_names) {
878 rgw_zone_id id;
879 if (store->svc()->zone->find_zone_id_by_name(name, &id)) {
880 ids.insert(std::move(id));
881 }
882 }
883
884 return ids;
885 }
886
887 vector<string> get_zone_names_from_ids(rgw::sal::RGWRadosStore *store,
888 const set<rgw_zone_id>& zone_ids) const {
889 vector<string> names;
890
891 for (auto& id : zone_ids) {
892 RGWZone *zone;
893 if (store->svc()->zone->find_zone(id, &zone)) {
894 names.emplace_back(zone->name);
895 }
896 }
897
898 return names;
899 }
900
901 std::optional<DeleteMarkerReplication> delete_marker_replication;
902 std::optional<Source> source;
903 Destination destination;
904 std::optional<Filter> filter;
905 string id;
906 int32_t priority;
907 string status;
908
909 void decode_xml(XMLObj *obj) {
910 RGWXMLDecoder::decode_xml("DeleteMarkerReplication", delete_marker_replication, obj);
911 RGWXMLDecoder::decode_xml("Source", source, obj);
912 RGWXMLDecoder::decode_xml("Destination", destination, obj);
913 RGWXMLDecoder::decode_xml("ID", id, obj);
914
915 std::optional<string> prefix;
916 RGWXMLDecoder::decode_xml("Prefix", prefix, obj);
917 if (prefix) {
918 filter.emplace();
919 filter->prefix = prefix;
920 }
921
922 if (!filter) {
923 RGWXMLDecoder::decode_xml("Filter", filter, obj);
924 } else {
925 /* don't want to have filter reset because it might have been initialized
926 * when decoding prefix
927 */
928 RGWXMLDecoder::decode_xml("Filter", *filter, obj);
929 }
930
931 RGWXMLDecoder::decode_xml("Priority", priority, obj);
932 RGWXMLDecoder::decode_xml("Status", status, obj);
933 }
934
935 void dump_xml(Formatter *f) const {
936 encode_xml("DeleteMarkerReplication", delete_marker_replication, f);
937 encode_xml("Source", source, f);
938 encode_xml("Destination", destination, f);
939 encode_xml("Filter", filter, f);
940 encode_xml("ID", id, f);
941 encode_xml("Priority", priority, f);
942 encode_xml("Status", status, f);
943 }
944
945 bool is_valid(CephContext *cct) const {
946 if (!is_valid_status(status)) {
947 ldout(cct, 5) << "NOTICE: bad status provided in rule (status=" << status << ")" << dendl;
948 return false;
949 }
950 if ((filter && !filter->is_valid(cct)) ||
951 (delete_marker_replication && !delete_marker_replication->is_valid(cct))) {
952 return false;
953 }
954 return true;
955 }
956
957 int to_sync_policy_pipe(req_state *s, rgw::sal::RGWRadosStore *store,
958 rgw_sync_bucket_pipes *pipe,
959 bool *enabled) const {
960 if (!is_valid(s->cct)) {
961 return -EINVAL;
962 }
963
964 pipe->id = id;
965 pipe->params.priority = priority;
966
967 const auto& user_id = s->user->get_id();
968
969 rgw_bucket_key dest_bk(user_id.tenant,
970 destination.bucket);
971
972 if (source && !source->zone_names.empty()) {
973 pipe->source.zones = get_zone_ids_from_names(store, source->zone_names);
974 } else {
975 pipe->source.set_all_zones(true);
976 }
977 if (!destination.zone_names.empty()) {
978 pipe->dest.zones = get_zone_ids_from_names(store, destination.zone_names);
979 } else {
980 pipe->dest.set_all_zones(true);
981 }
982 pipe->dest.bucket.emplace(dest_bk);
983
984 if (filter) {
985 int r = filter->to_sync_pipe_filter(s->cct, &pipe->params.source.filter);
986 if (r < 0) {
987 return r;
988 }
989 }
990 if (destination.acl_translation) {
991 rgw_user u;
992 u.tenant = user_id.tenant;
993 u.from_str(destination.acl_translation->owner); /* explicit tenant will override tenant,
994 otherwise will inherit it from s->user */
995 pipe->params.dest.acl_translation.emplace();
996 pipe->params.dest.acl_translation->owner = u;
997 }
998 pipe->params.dest.storage_class = destination.storage_class;
999
1000 *enabled = (status == "Enabled");
1001
1002 pipe->params.mode = rgw_sync_pipe_params::Mode::MODE_USER;
1003 pipe->params.user = user_id.to_str();
1004
1005 return 0;
1006 }
1007
1008 void from_sync_policy_pipe(rgw::sal::RGWRadosStore *store,
1009 const rgw_sync_bucket_pipes& pipe,
1010 bool enabled) {
1011 id = pipe.id;
1012 status = (enabled ? "Enabled" : "Disabled");
1013 priority = pipe.params.priority;
1014
1015 if (pipe.source.all_zones) {
1016 source.reset();
1017 } else if (pipe.source.zones) {
1018 source.emplace();
1019 source->zone_names = get_zone_names_from_ids(store, *pipe.source.zones);
1020 }
1021
1022 if (!pipe.dest.all_zones &&
1023 pipe.dest.zones) {
1024 destination.zone_names = get_zone_names_from_ids(store, *pipe.dest.zones);
1025 }
1026
1027 if (pipe.params.dest.acl_translation) {
1028 destination.acl_translation.emplace();
1029 destination.acl_translation->owner = pipe.params.dest.acl_translation->owner.to_str();
1030 }
1031
1032 if (pipe.params.dest.storage_class) {
1033 destination.storage_class = *pipe.params.dest.storage_class;
1034 }
1035
1036 if (pipe.dest.bucket) {
1037 destination.bucket = pipe.dest.bucket->get_key();
1038 }
1039
1040 filter.emplace();
1041 filter->from_sync_pipe_filter(pipe.params.source.filter);
1042
1043 if (filter->empty()) {
1044 filter.reset();
1045 }
1046 }
1047 };
1048
1049 std::vector<Rule> rules;
1050
1051 void decode_xml(XMLObj *obj) {
1052 RGWXMLDecoder::decode_xml("Role", role, obj);
1053 RGWXMLDecoder::decode_xml("Rule", rules, obj);
1054 }
1055
1056 void dump_xml(Formatter *f) const {
1057 encode_xml("Role", role, f);
1058 encode_xml("Rule", rules, f);
1059 }
1060
1061 int to_sync_policy_groups(req_state *s, rgw::sal::RGWRadosStore *store,
1062 vector<rgw_sync_policy_group> *result) const {
1063 result->resize(2);
1064
1065 rgw_sync_policy_group& enabled_group = (*result)[0];
1066 rgw_sync_policy_group& disabled_group = (*result)[1];
1067
1068 enabled_group.id = enabled_group_id;
1069 enabled_group.status = rgw_sync_policy_group::Status::ENABLED;
1070 disabled_group.id = disabled_group_id;
1071 disabled_group.status = rgw_sync_policy_group::Status::ALLOWED; /* not enabled, not forbidden */
1072
1073 for (auto& rule : rules) {
1074 rgw_sync_bucket_pipes pipe;
1075 bool enabled;
1076 int r = rule.to_sync_policy_pipe(s, store, &pipe, &enabled);
1077 if (r < 0) {
1078 ldout(s->cct, 5) << "NOTICE: failed to convert replication configuration into sync policy pipe (rule.id=" << rule.id << "): " << cpp_strerror(-r) << dendl;
1079 return r;
1080 }
1081
1082 if (enabled) {
1083 enabled_group.pipes.emplace_back(std::move(pipe));
1084 } else {
1085 disabled_group.pipes.emplace_back(std::move(pipe));
1086 }
1087 }
1088 return 0;
1089 }
1090
1091 void from_sync_policy_group(rgw::sal::RGWRadosStore *store,
1092 const rgw_sync_policy_group& group) {
1093
1094 bool enabled = (group.status == rgw_sync_policy_group::Status::ENABLED);
1095
1096 for (auto& pipe : group.pipes) {
1097 auto& rule = rules.emplace_back();
1098 rule.from_sync_policy_pipe(store, pipe, enabled);
1099 }
1100 }
1101 };
1102
1103 }
1104
1105 void RGWGetBucketReplication_ObjStore_S3::send_response_data()
1106 {
1107 if (op_ret)
1108 set_req_state_err(s, op_ret);
1109 dump_errno(s);
1110 end_header(s, this, "application/xml");
1111 dump_start(s);
1112
1113 ReplicationConfiguration conf;
1114
1115 if (s->bucket_info.sync_policy) {
1116 auto policy = s->bucket_info.sync_policy;
1117
1118 auto iter = policy->groups.find(enabled_group_id);
1119 if (iter != policy->groups.end()) {
1120 conf.from_sync_policy_group(store, iter->second);
1121 }
1122 iter = policy->groups.find(disabled_group_id);
1123 if (iter != policy->groups.end()) {
1124 conf.from_sync_policy_group(store, iter->second);
1125 }
1126 }
1127
1128 if (!op_ret) {
1129 s->formatter->open_object_section_in_ns("ReplicationConfiguration", XMLNS_AWS_S3);
1130 conf.dump_xml(s->formatter);
1131 s->formatter->close_section();
1132 rgw_flush_formatter_and_reset(s, s->formatter);
1133 }
1134 }
1135
1136 int RGWPutBucketReplication_ObjStore_S3::get_params()
1137 {
1138 RGWXMLParser parser;
1139
1140 if (!parser.init()){
1141 return -EINVAL;
1142 }
1143
1144 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
1145 int r = 0;
1146 bufferlist data;
1147
1148 std::tie(r, data) = rgw_rest_read_all_input(s, max_size, false);
1149
1150 if (r < 0)
1151 return r;
1152
1153 if (!parser.parse(data.c_str(), data.length(), 1)) {
1154 return -ERR_MALFORMED_XML;
1155 }
1156
1157 ReplicationConfiguration conf;
1158 try {
1159 RGWXMLDecoder::decode_xml("ReplicationConfiguration", conf, &parser);
1160 } catch (RGWXMLDecoder::err& err) {
1161
1162 ldout(s->cct, 5) << "Malformed tagging request: " << err << dendl;
1163 return -ERR_MALFORMED_XML;
1164 }
1165
1166 r = conf.to_sync_policy_groups(s, store, &sync_policy_groups);
1167 if (r < 0) {
1168 return r;
1169 }
1170
1171 // forward requests to meta master zone
1172 if (!store->svc()->zone->is_meta_master()) {
1173 /* only need to keep this data around if we're not meta master */
1174 in_data = std::move(data);
1175 }
1176
1177 return 0;
1178 }
1179
1180 void RGWPutBucketReplication_ObjStore_S3::send_response()
1181 {
1182 if (op_ret)
1183 set_req_state_err(s, op_ret);
1184 dump_errno(s);
1185 end_header(s, this, "application/xml");
1186 dump_start(s);
1187 }
1188
1189 void RGWDeleteBucketReplication_ObjStore_S3::update_sync_policy(rgw_sync_policy_info *policy)
1190 {
1191 policy->groups.erase(enabled_group_id);
1192 policy->groups.erase(disabled_group_id);
1193 }
1194
1195 void RGWDeleteBucketReplication_ObjStore_S3::send_response()
1196 {
1197 if (op_ret)
1198 set_req_state_err(s, op_ret);
1199 dump_errno(s);
1200 end_header(s, this, "application/xml");
1201 dump_start(s);
1202 }
1203
1204 void RGWListBuckets_ObjStore_S3::send_response_begin(bool has_buckets)
1205 {
1206 if (op_ret)
1207 set_req_state_err(s, op_ret);
1208 dump_errno(s);
1209 dump_start(s);
1210 // Explicitly use chunked transfer encoding so that we can stream the result
1211 // to the user without having to wait for the full length of it.
1212 end_header(s, NULL, "application/xml", CHUNKED_TRANSFER_ENCODING);
1213
1214 if (! op_ret) {
1215 list_all_buckets_start(s);
1216 dump_owner(s, s->user->get_id(), s->user->get_display_name());
1217 s->formatter->open_array_section("Buckets");
1218 sent_data = true;
1219 }
1220 }
1221
1222 void RGWListBuckets_ObjStore_S3::send_response_data(rgw::sal::RGWBucketList& buckets)
1223 {
1224 if (!sent_data)
1225 return;
1226
1227 map<string, rgw::sal::RGWBucket*>& m = buckets.get_buckets();
1228 map<string, rgw::sal::RGWBucket*>::iterator iter;
1229
1230 for (iter = m.begin(); iter != m.end(); ++iter) {
1231 rgw::sal::RGWBucket* obj = iter->second;
1232 dump_bucket(s, *obj);
1233 }
1234 rgw_flush_formatter(s, s->formatter);
1235 }
1236
1237 void RGWListBuckets_ObjStore_S3::send_response_end()
1238 {
1239 if (sent_data) {
1240 s->formatter->close_section();
1241 list_all_buckets_end(s);
1242 rgw_flush_formatter_and_reset(s, s->formatter);
1243 }
1244 }
1245
1246 int RGWGetUsage_ObjStore_S3::get_params()
1247 {
1248 start_date = s->info.args.get("start-date");
1249 end_date = s->info.args.get("end-date");
1250 return 0;
1251 }
1252
1253 static void dump_usage_categories_info(Formatter *formatter, const rgw_usage_log_entry& entry, map<string, bool> *categories)
1254 {
1255 formatter->open_array_section("categories");
1256 map<string, rgw_usage_data>::const_iterator uiter;
1257 for (uiter = entry.usage_map.begin(); uiter != entry.usage_map.end(); ++uiter) {
1258 if (categories && !categories->empty() && !categories->count(uiter->first))
1259 continue;
1260 const rgw_usage_data& usage = uiter->second;
1261 formatter->open_object_section("Entry");
1262 encode_json("Category", uiter->first, formatter);
1263 encode_json("BytesSent", usage.bytes_sent, formatter);
1264 encode_json("BytesReceived", usage.bytes_received, formatter);
1265 encode_json("Ops", usage.ops, formatter);
1266 encode_json("SuccessfulOps", usage.successful_ops, formatter);
1267 formatter->close_section(); // Entry
1268 }
1269 formatter->close_section(); // Category
1270 }
1271
1272 static void dump_usage_bucket_info(Formatter *formatter, const std::string& name, const cls_user_bucket_entry& entry)
1273 {
1274 formatter->open_object_section("Entry");
1275 encode_json("Bucket", name, formatter);
1276 encode_json("Bytes", entry.size, formatter);
1277 encode_json("Bytes_Rounded", entry.size_rounded, formatter);
1278 formatter->close_section(); // entry
1279 }
1280
1281 void RGWGetUsage_ObjStore_S3::send_response()
1282 {
1283 if (op_ret < 0)
1284 set_req_state_err(s, op_ret);
1285 dump_errno(s);
1286
1287 // Explicitly use chunked transfer encoding so that we can stream the result
1288 // to the user without having to wait for the full length of it.
1289 end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
1290 dump_start(s);
1291 if (op_ret < 0)
1292 return;
1293
1294 Formatter *formatter = s->formatter;
1295 string last_owner;
1296 bool user_section_open = false;
1297
1298 formatter->open_object_section("Usage");
1299 if (show_log_entries) {
1300 formatter->open_array_section("Entries");
1301 }
1302 map<rgw_user_bucket, rgw_usage_log_entry>::iterator iter;
1303 for (iter = usage.begin(); iter != usage.end(); ++iter) {
1304 const rgw_user_bucket& ub = iter->first;
1305 const rgw_usage_log_entry& entry = iter->second;
1306
1307 if (show_log_entries) {
1308 if (ub.user.compare(last_owner) != 0) {
1309 if (user_section_open) {
1310 formatter->close_section();
1311 formatter->close_section();
1312 }
1313 formatter->open_object_section("User");
1314 formatter->dump_string("Owner", ub.user);
1315 formatter->open_array_section("Buckets");
1316 user_section_open = true;
1317 last_owner = ub.user;
1318 }
1319 formatter->open_object_section("Bucket");
1320 formatter->dump_string("Bucket", ub.bucket);
1321 utime_t ut(entry.epoch, 0);
1322 ut.gmtime(formatter->dump_stream("Time"));
1323 formatter->dump_int("Epoch", entry.epoch);
1324 dump_usage_categories_info(formatter, entry, &categories);
1325 formatter->close_section(); // bucket
1326 }
1327
1328 summary_map[ub.user].aggregate(entry, &categories);
1329 }
1330
1331 if (show_log_entries) {
1332 if (user_section_open) {
1333 formatter->close_section(); // buckets
1334 formatter->close_section(); //user
1335 }
1336 formatter->close_section(); // entries
1337 }
1338
1339 if (show_log_sum) {
1340 formatter->open_array_section("Summary");
1341 map<string, rgw_usage_log_entry>::iterator siter;
1342 for (siter = summary_map.begin(); siter != summary_map.end(); ++siter) {
1343 const rgw_usage_log_entry& entry = siter->second;
1344 formatter->open_object_section("User");
1345 formatter->dump_string("User", siter->first);
1346 dump_usage_categories_info(formatter, entry, &categories);
1347 rgw_usage_data total_usage;
1348 entry.sum(total_usage, categories);
1349 formatter->open_object_section("Total");
1350 encode_json("BytesSent", total_usage.bytes_sent, formatter);
1351 encode_json("BytesReceived", total_usage.bytes_received, formatter);
1352 encode_json("Ops", total_usage.ops, formatter);
1353 encode_json("SuccessfulOps", total_usage.successful_ops, formatter);
1354 formatter->close_section(); // total
1355 formatter->close_section(); // user
1356 }
1357
1358 if (s->cct->_conf->rgw_rest_getusage_op_compat) {
1359 formatter->open_object_section("Stats");
1360 }
1361
1362 encode_json("TotalBytes", stats.size, formatter);
1363 encode_json("TotalBytesRounded", stats.size_rounded, formatter);
1364 encode_json("TotalEntries", stats.num_objects, formatter);
1365
1366 if (s->cct->_conf->rgw_rest_getusage_op_compat) {
1367 formatter->close_section(); //Stats
1368 }
1369
1370 formatter->close_section(); // summary
1371 }
1372
1373 formatter->open_array_section("CapacityUsed");
1374 formatter->open_object_section("User");
1375 formatter->open_array_section("Buckets");
1376 for (const auto& biter : buckets_usage) {
1377 const cls_user_bucket_entry& entry = biter.second;
1378 dump_usage_bucket_info(formatter, biter.first, entry);
1379 }
1380 formatter->close_section(); // Buckets
1381 formatter->close_section(); // User
1382 formatter->close_section(); // CapacityUsed
1383
1384 formatter->close_section(); // usage
1385 rgw_flush_formatter_and_reset(s, s->formatter);
1386 }
1387
1388 int RGWListBucket_ObjStore_S3::get_common_params()
1389 {
1390 list_versions = s->info.args.exists("versions");
1391 prefix = s->info.args.get("prefix");
1392
1393 // non-standard
1394 s->info.args.get_bool("allow-unordered", &allow_unordered, false);
1395 delimiter = s->info.args.get("delimiter");
1396 max_keys = s->info.args.get("max-keys");
1397 op_ret = parse_max_keys();
1398 if (op_ret < 0) {
1399 return op_ret;
1400 }
1401 encoding_type = s->info.args.get("encoding-type");
1402 if (s->system_request) {
1403 s->info.args.get_bool("objs-container", &objs_container, false);
1404 const char *shard_id_str = s->info.env->get("HTTP_RGWX_SHARD_ID");
1405 if (shard_id_str) {
1406 string err;
1407 shard_id = strict_strtol(shard_id_str, 10, &err);
1408 if (!err.empty()) {
1409 ldout(s->cct, 5) << "bad shard id specified: " << shard_id_str << dendl;
1410 return -EINVAL;
1411 }
1412 } else {
1413 shard_id = s->bucket_instance_shard_id;
1414 }
1415 }
1416 return 0;
1417 }
1418
1419 int RGWListBucket_ObjStore_S3::get_params()
1420 {
1421 int ret = get_common_params();
1422 if (ret < 0) {
1423 return ret;
1424 }
1425 if (!list_versions) {
1426 marker = s->info.args.get("marker");
1427 } else {
1428 marker.name = s->info.args.get("key-marker");
1429 marker.instance = s->info.args.get("version-id-marker");
1430 }
1431 return 0;
1432 }
1433
1434 int RGWListBucket_ObjStore_S3v2::get_params()
1435 {
1436 int ret = get_common_params();
1437 if (ret < 0) {
1438 return ret;
1439 }
1440 s->info.args.get_bool("fetch-owner", &fetchOwner, false);
1441 startAfter = s->info.args.get("start-after", &start_after_exist);
1442 continuation_token = s->info.args.get("continuation-token", &continuation_token_exist);
1443 if(!continuation_token_exist) {
1444 marker = startAfter;
1445 } else {
1446 marker = continuation_token;
1447 }
1448 return 0;
1449 }
1450
1451 void RGWListBucket_ObjStore_S3::send_common_versioned_response()
1452 {
1453 if (!s->bucket_tenant.empty()) {
1454 s->formatter->dump_string("Tenant", s->bucket_tenant);
1455 }
1456 s->formatter->dump_string("Name", s->bucket_name);
1457 s->formatter->dump_string("Prefix", prefix);
1458 s->formatter->dump_int("MaxKeys", max);
1459 if (!delimiter.empty()) {
1460 s->formatter->dump_string("Delimiter", delimiter);
1461 }
1462 s->formatter->dump_string("IsTruncated", (max && is_truncated ? "true"
1463 : "false"));
1464
1465 if (!common_prefixes.empty()) {
1466 map<string, bool>::iterator pref_iter;
1467 for (pref_iter = common_prefixes.begin();
1468 pref_iter != common_prefixes.end(); ++pref_iter) {
1469 s->formatter->open_array_section("CommonPrefixes");
1470 if (encode_key) {
1471 s->formatter->dump_string("Prefix", url_encode(pref_iter->first, false));
1472 } else {
1473 s->formatter->dump_string("Prefix", pref_iter->first);
1474 }
1475
1476 s->formatter->close_section();
1477 }
1478 }
1479 }
1480
1481 void RGWListBucket_ObjStore_S3::send_versioned_response()
1482 {
1483 s->formatter->open_object_section_in_ns("ListVersionsResult", XMLNS_AWS_S3);
1484 if (strcasecmp(encoding_type.c_str(), "url") == 0) {
1485 s->formatter->dump_string("EncodingType", "url");
1486 encode_key = true;
1487 }
1488 RGWListBucket_ObjStore_S3::send_common_versioned_response();
1489 s->formatter->dump_string("KeyMarker", marker.name);
1490 s->formatter->dump_string("VersionIdMarker", marker.instance);
1491 if (is_truncated && !next_marker.empty()) {
1492 s->formatter->dump_string("NextKeyMarker", next_marker.name);
1493 if (next_marker.instance.empty()) {
1494 s->formatter->dump_string("NextVersionIdMarker", "null");
1495 }
1496 else {
1497 s->formatter->dump_string("NextVersionIdMarker", next_marker.instance);
1498 }
1499 }
1500
1501 if (op_ret >= 0) {
1502 if (objs_container) {
1503 s->formatter->open_array_section("Entries");
1504 }
1505
1506 vector<rgw_bucket_dir_entry>::iterator iter;
1507 for (iter = objs.begin(); iter != objs.end(); ++iter) {
1508 const char *section_name = (iter->is_delete_marker() ? "DeleteMarker"
1509 : "Version");
1510 s->formatter->open_object_section(section_name);
1511 if (objs_container) {
1512 s->formatter->dump_bool("IsDeleteMarker", iter->is_delete_marker());
1513 }
1514 rgw_obj_key key(iter->key);
1515 if (encode_key) {
1516 string key_name;
1517 url_encode(key.name, key_name);
1518 s->formatter->dump_string("Key", key_name);
1519 }
1520 else {
1521 s->formatter->dump_string("Key", key.name);
1522 }
1523 string version_id = key.instance;
1524 if (version_id.empty()) {
1525 version_id = "null";
1526 }
1527 if (s->system_request) {
1528 if (iter->versioned_epoch > 0) {
1529 s->formatter->dump_int("VersionedEpoch", iter->versioned_epoch);
1530 }
1531 s->formatter->dump_string("RgwxTag", iter->tag);
1532 utime_t ut(iter->meta.mtime);
1533 ut.gmtime_nsec(s->formatter->dump_stream("RgwxMtime"));
1534 }
1535 s->formatter->dump_string("VersionId", version_id);
1536 s->formatter->dump_bool("IsLatest", iter->is_current());
1537 dump_time(s, "LastModified", &iter->meta.mtime);
1538 if (!iter->is_delete_marker()) {
1539 s->formatter->dump_format("ETag", "\"%s\"", iter->meta.etag.c_str());
1540 s->formatter->dump_int("Size", iter->meta.accounted_size);
1541 auto& storage_class = rgw_placement_rule::get_canonical_storage_class(iter->meta.storage_class);
1542 s->formatter->dump_string("StorageClass", storage_class.c_str());
1543 }
1544 dump_owner(s, rgw_user(iter->meta.owner), iter->meta.owner_display_name);
1545 if (iter->meta.appendable) {
1546 s->formatter->dump_string("Type", "Appendable");
1547 } else {
1548 s->formatter->dump_string("Type", "Normal");
1549 }
1550 s->formatter->close_section(); // Version/DeleteMarker
1551 }
1552 if (objs_container) {
1553 s->formatter->close_section(); // Entries
1554 }
1555 s->formatter->close_section(); // ListVersionsResult
1556 }
1557 rgw_flush_formatter_and_reset(s, s->formatter);
1558 }
1559
1560
1561 void RGWListBucket_ObjStore_S3::send_common_response()
1562 {
1563 if (!s->bucket_tenant.empty()) {
1564 s->formatter->dump_string("Tenant", s->bucket_tenant);
1565 }
1566 s->formatter->dump_string("Name", s->bucket_name);
1567 s->formatter->dump_string("Prefix", prefix);
1568 s->formatter->dump_int("MaxKeys", max);
1569 if (!delimiter.empty()) {
1570 s->formatter->dump_string("Delimiter", delimiter);
1571 }
1572 s->formatter->dump_string("IsTruncated", (max && is_truncated ? "true"
1573 : "false"));
1574
1575 if (!common_prefixes.empty()) {
1576 map<string, bool>::iterator pref_iter;
1577 for (pref_iter = common_prefixes.begin();
1578 pref_iter != common_prefixes.end(); ++pref_iter) {
1579 s->formatter->open_array_section("CommonPrefixes");
1580 if (encode_key) {
1581 s->formatter->dump_string("Prefix", url_encode(pref_iter->first, false));
1582 } else {
1583 s->formatter->dump_string("Prefix", pref_iter->first);
1584 }
1585 s->formatter->close_section();
1586 }
1587 }
1588 }
1589
1590 void RGWListBucket_ObjStore_S3::send_response()
1591 {
1592 if (op_ret < 0) {
1593 set_req_state_err(s, op_ret);
1594 }
1595 dump_errno(s);
1596
1597 // Explicitly use chunked transfer encoding so that we can stream the result
1598 // to the user without having to wait for the full length of it.
1599 end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
1600 dump_start(s);
1601 if (op_ret < 0) {
1602 return;
1603 }
1604 if (list_versions) {
1605 send_versioned_response();
1606 return;
1607 }
1608
1609 s->formatter->open_object_section_in_ns("ListBucketResult", XMLNS_AWS_S3);
1610 if (strcasecmp(encoding_type.c_str(), "url") == 0) {
1611 s->formatter->dump_string("EncodingType", "url");
1612 encode_key = true;
1613 }
1614 RGWListBucket_ObjStore_S3::send_common_response();
1615 if (op_ret >= 0) {
1616 vector<rgw_bucket_dir_entry>::iterator iter;
1617 for (iter = objs.begin(); iter != objs.end(); ++iter) {
1618 rgw_obj_key key(iter->key);
1619 s->formatter->open_array_section("Contents");
1620 if (encode_key) {
1621 string key_name;
1622 url_encode(key.name, key_name);
1623 s->formatter->dump_string("Key", key_name);
1624 } else {
1625 s->formatter->dump_string("Key", key.name);
1626 }
1627 dump_time(s, "LastModified", &iter->meta.mtime);
1628 s->formatter->dump_format("ETag", "\"%s\"", iter->meta.etag.c_str());
1629 s->formatter->dump_int("Size", iter->meta.accounted_size);
1630 auto& storage_class = rgw_placement_rule::get_canonical_storage_class(iter->meta.storage_class);
1631 s->formatter->dump_string("StorageClass", storage_class.c_str());
1632 dump_owner(s, rgw_user(iter->meta.owner), iter->meta.owner_display_name);
1633 if (s->system_request) {
1634 s->formatter->dump_string("RgwxTag", iter->tag);
1635 }
1636 if (iter->meta.appendable) {
1637 s->formatter->dump_string("Type", "Appendable");
1638 } else {
1639 s->formatter->dump_string("Type", "Normal");
1640 }
1641 s->formatter->close_section();
1642 }
1643 }
1644 s->formatter->dump_string("Marker", marker.name);
1645 if (is_truncated && !next_marker.empty()) {
1646 s->formatter->dump_string("NextMarker", next_marker.name);
1647 }
1648 s->formatter->close_section();
1649 rgw_flush_formatter_and_reset(s, s->formatter);
1650 }
1651
1652 void RGWListBucket_ObjStore_S3v2::send_versioned_response()
1653 {
1654 s->formatter->open_object_section_in_ns("ListVersionsResult", XMLNS_AWS_S3);
1655 RGWListBucket_ObjStore_S3v2::send_common_versioned_response();
1656 s->formatter->dump_string("KeyContinuationToken", marker.name);
1657 s->formatter->dump_string("VersionIdContinuationToken", marker.instance);
1658 if (is_truncated && !next_marker.empty()) {
1659 s->formatter->dump_string("NextKeyContinuationToken", next_marker.name);
1660 s->formatter->dump_string("NextVersionIdContinuationToken", next_marker.instance);
1661 }
1662
1663 if (strcasecmp(encoding_type.c_str(), "url") == 0) {
1664 s->formatter->dump_string("EncodingType", "url");
1665 encode_key = true;
1666 }
1667
1668 if (op_ret >= 0) {
1669 if (objs_container) {
1670 s->formatter->open_array_section("Entries");
1671 }
1672
1673 vector<rgw_bucket_dir_entry>::iterator iter;
1674 for (iter = objs.begin(); iter != objs.end(); ++iter) {
1675 const char *section_name = (iter->is_delete_marker() ? "DeleteContinuationToken"
1676 : "Version");
1677 s->formatter->open_object_section(section_name);
1678 if (objs_container) {
1679 s->formatter->dump_bool("IsDeleteContinuationToken", iter->is_delete_marker());
1680 }
1681 rgw_obj_key key(iter->key);
1682 if (encode_key) {
1683 string key_name;
1684 url_encode(key.name, key_name);
1685 s->formatter->dump_string("Key", key_name);
1686 }
1687 else {
1688 s->formatter->dump_string("Key", key.name);
1689 }
1690 string version_id = key.instance;
1691 if (version_id.empty()) {
1692 version_id = "null";
1693 }
1694 if (s->system_request) {
1695 if (iter->versioned_epoch > 0) {
1696 s->formatter->dump_int("VersionedEpoch", iter->versioned_epoch);
1697 }
1698 s->formatter->dump_string("RgwxTag", iter->tag);
1699 utime_t ut(iter->meta.mtime);
1700 ut.gmtime_nsec(s->formatter->dump_stream("RgwxMtime"));
1701 }
1702 s->formatter->dump_string("VersionId", version_id);
1703 s->formatter->dump_bool("IsLatest", iter->is_current());
1704 dump_time(s, "LastModified", &iter->meta.mtime);
1705 if (!iter->is_delete_marker()) {
1706 s->formatter->dump_format("ETag", "\"%s\"", iter->meta.etag.c_str());
1707 s->formatter->dump_int("Size", iter->meta.accounted_size);
1708 auto& storage_class = rgw_placement_rule::get_canonical_storage_class(iter->meta.storage_class);
1709 s->formatter->dump_string("StorageClass", storage_class.c_str());
1710 }
1711 if (fetchOwner == true) {
1712 dump_owner(s, s->user->get_id(), s->user->get_display_name());
1713 }
1714 s->formatter->close_section();
1715 }
1716
1717
1718 if (objs_container) {
1719 s->formatter->close_section();
1720 }
1721
1722 if (!common_prefixes.empty()) {
1723 map<string, bool>::iterator pref_iter;
1724 for (pref_iter = common_prefixes.begin();
1725 pref_iter != common_prefixes.end(); ++pref_iter) {
1726 s->formatter->open_array_section("CommonPrefixes");
1727 if (encode_key) {
1728 s->formatter->dump_string("Prefix", url_encode(pref_iter->first, false));
1729 } else {
1730 s->formatter->dump_string("Prefix", pref_iter->first);
1731 }
1732
1733 s->formatter->dump_int("KeyCount",objs.size());
1734 if (start_after_exist) {
1735 s->formatter->dump_string("StartAfter", startAfter);
1736 }
1737 s->formatter->close_section();
1738 }
1739 }
1740
1741 s->formatter->close_section();
1742 rgw_flush_formatter_and_reset(s, s->formatter);
1743 }
1744 }
1745
1746 void RGWListBucket_ObjStore_S3v2::send_response()
1747 {
1748 if (op_ret < 0) {
1749 set_req_state_err(s, op_ret);
1750 }
1751 dump_errno(s);
1752
1753 // Explicitly use chunked transfer encoding so that we can stream the result
1754 // to the user without having to wait for the full length of it.
1755 end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
1756 dump_start(s);
1757 if (op_ret < 0) {
1758 return;
1759 }
1760 if (list_versions) {
1761 send_versioned_response();
1762 return;
1763 }
1764
1765 s->formatter->open_object_section_in_ns("ListBucketResult", XMLNS_AWS_S3);
1766 if (strcasecmp(encoding_type.c_str(), "url") == 0) {
1767 s->formatter->dump_string("EncodingType", "url");
1768 encode_key = true;
1769 }
1770
1771 RGWListBucket_ObjStore_S3::send_common_response();
1772 if (op_ret >= 0) {
1773 vector<rgw_bucket_dir_entry>::iterator iter;
1774 for (iter = objs.begin(); iter != objs.end(); ++iter) {
1775 rgw_obj_key key(iter->key);
1776 s->formatter->open_array_section("Contents");
1777 if (encode_key) {
1778 string key_name;
1779 url_encode(key.name, key_name);
1780 s->formatter->dump_string("Key", key_name);
1781 }
1782 else {
1783 s->formatter->dump_string("Key", key.name);
1784 }
1785 dump_time(s, "LastModified", &iter->meta.mtime);
1786 s->formatter->dump_format("ETag", "\"%s\"", iter->meta.etag.c_str());
1787 s->formatter->dump_int("Size", iter->meta.accounted_size);
1788 auto& storage_class = rgw_placement_rule::get_canonical_storage_class(iter->meta.storage_class);
1789 s->formatter->dump_string("StorageClass", storage_class.c_str());
1790 if (fetchOwner == true) {
1791 dump_owner(s, s->user->get_id(), s->user->get_display_name());
1792 }
1793 if (s->system_request) {
1794 s->formatter->dump_string("RgwxTag", iter->tag);
1795 }
1796 if (iter->meta.appendable) {
1797 s->formatter->dump_string("Type", "Appendable");
1798 } else {
1799 s->formatter->dump_string("Type", "Normal");
1800 }
1801 s->formatter->close_section();
1802 }
1803 }
1804 if (continuation_token_exist) {
1805 s->formatter->dump_string("ContinuationToken", continuation_token);
1806 }
1807 if (is_truncated && !next_marker.empty()) {
1808 s->formatter->dump_string("NextContinuationToken", next_marker.name);
1809 }
1810 s->formatter->dump_int("KeyCount",objs.size());
1811 if (start_after_exist) {
1812 s->formatter->dump_string("StartAfter", startAfter);
1813 }
1814 s->formatter->close_section();
1815 rgw_flush_formatter_and_reset(s, s->formatter);
1816 }
1817
1818 void RGWGetBucketLogging_ObjStore_S3::send_response()
1819 {
1820 dump_errno(s);
1821 end_header(s, this, "application/xml");
1822 dump_start(s);
1823
1824 s->formatter->open_object_section_in_ns("BucketLoggingStatus", XMLNS_AWS_S3);
1825 s->formatter->close_section();
1826 rgw_flush_formatter_and_reset(s, s->formatter);
1827 }
1828
1829 void RGWGetBucketLocation_ObjStore_S3::send_response()
1830 {
1831 dump_errno(s);
1832 end_header(s, this);
1833 dump_start(s);
1834
1835 RGWZoneGroup zonegroup;
1836 string api_name;
1837
1838 int ret = store->svc()->zone->get_zonegroup(s->bucket_info.zonegroup, zonegroup);
1839 if (ret >= 0) {
1840 api_name = zonegroup.api_name;
1841 } else {
1842 if (s->bucket_info.zonegroup != "default") {
1843 api_name = s->bucket_info.zonegroup;
1844 }
1845 }
1846
1847 s->formatter->dump_format_ns("LocationConstraint", XMLNS_AWS_S3,
1848 "%s", api_name.c_str());
1849 rgw_flush_formatter_and_reset(s, s->formatter);
1850 }
1851
1852 void RGWGetBucketVersioning_ObjStore_S3::send_response()
1853 {
1854 dump_errno(s);
1855 end_header(s, this, "application/xml");
1856 dump_start(s);
1857
1858 s->formatter->open_object_section_in_ns("VersioningConfiguration", XMLNS_AWS_S3);
1859 if (versioned) {
1860 const char *status = (versioning_enabled ? "Enabled" : "Suspended");
1861 s->formatter->dump_string("Status", status);
1862 const char *mfa_status = (mfa_enabled ? "Enabled" : "Disabled");
1863 s->formatter->dump_string("MfaDelete", mfa_status);
1864 }
1865 s->formatter->close_section();
1866 rgw_flush_formatter_and_reset(s, s->formatter);
1867 }
1868
1869 struct ver_config_status {
1870 int status{VersioningSuspended};
1871
1872 enum MFAStatus {
1873 MFA_UNKNOWN,
1874 MFA_DISABLED,
1875 MFA_ENABLED,
1876 } mfa_status{MFA_UNKNOWN};
1877 int retcode{0};
1878
1879 void decode_xml(XMLObj *obj) {
1880 string status_str;
1881 string mfa_str;
1882 RGWXMLDecoder::decode_xml("Status", status_str, obj);
1883 if (status_str == "Enabled") {
1884 status = VersioningEnabled;
1885 } else if (status_str != "Suspended") {
1886 status = VersioningStatusInvalid;
1887 }
1888
1889
1890 if (RGWXMLDecoder::decode_xml("MfaDelete", mfa_str, obj)) {
1891 if (mfa_str == "Enabled") {
1892 mfa_status = MFA_ENABLED;
1893 } else if (mfa_str == "Disabled") {
1894 mfa_status = MFA_DISABLED;
1895 } else {
1896 retcode = -EINVAL;
1897 }
1898 }
1899 }
1900 };
1901
1902 int RGWSetBucketVersioning_ObjStore_S3::get_params()
1903 {
1904 int r = 0;
1905 bufferlist data;
1906 std::tie(r, data) =
1907 rgw_rest_read_all_input(s, s->cct->_conf->rgw_max_put_param_size, false);
1908 if (r < 0) {
1909 return r;
1910 }
1911
1912 r = do_aws4_auth_completion();
1913 if (r < 0) {
1914 return r;
1915 }
1916
1917 RGWXMLDecoder::XMLParser parser;
1918 if (!parser.init()) {
1919 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
1920 return -EIO;
1921 }
1922
1923 char* buf = data.c_str();
1924 if (!parser.parse(buf, data.length(), 1)) {
1925 ldpp_dout(this, 10) << "NOTICE: failed to parse data: " << buf << dendl;
1926 r = -EINVAL;
1927 return r;
1928 }
1929
1930 ver_config_status status_conf;
1931
1932 if (!RGWXMLDecoder::decode_xml("VersioningConfiguration", status_conf, &parser)) {
1933 ldpp_dout(this, 10) << "NOTICE: bad versioning config input" << dendl;
1934 return -EINVAL;
1935 }
1936
1937 if (!store->svc()->zone->is_meta_master()) {
1938 /* only need to keep this data around if we're not meta master */
1939 in_data.append(data);
1940 }
1941
1942 versioning_status = status_conf.status;
1943 if (versioning_status == VersioningStatusInvalid) {
1944 r = -EINVAL;
1945 }
1946
1947 if (status_conf.mfa_status != ver_config_status::MFA_UNKNOWN) {
1948 mfa_set_status = true;
1949 switch (status_conf.mfa_status) {
1950 case ver_config_status::MFA_DISABLED:
1951 mfa_status = false;
1952 break;
1953 case ver_config_status::MFA_ENABLED:
1954 mfa_status = true;
1955 break;
1956 default:
1957 ldpp_dout(this, 0) << "ERROR: RGWSetBucketVersioning_ObjStore_S3::get_params(): unexpected switch case mfa_status=" << status_conf.mfa_status << dendl;
1958 r = -EIO;
1959 }
1960 } else if (status_conf.retcode < 0) {
1961 r = status_conf.retcode;
1962 }
1963 return r;
1964 }
1965
1966 void RGWSetBucketVersioning_ObjStore_S3::send_response()
1967 {
1968 if (op_ret)
1969 set_req_state_err(s, op_ret);
1970 dump_errno(s);
1971 end_header(s, this, "application/xml");
1972 }
1973
1974 int RGWSetBucketWebsite_ObjStore_S3::get_params()
1975 {
1976 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
1977
1978 int r = 0;
1979 bufferlist data;
1980 std::tie(r, data) = rgw_rest_read_all_input(s, max_size, false);
1981
1982 if (r < 0) {
1983 return r;
1984 }
1985
1986 r = do_aws4_auth_completion();
1987 if (r < 0) {
1988 return r;
1989 }
1990
1991 in_data.append(data);
1992
1993 RGWXMLDecoder::XMLParser parser;
1994 if (!parser.init()) {
1995 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
1996 return -EIO;
1997 }
1998
1999 char* buf = data.c_str();
2000 if (!parser.parse(buf, data.length(), 1)) {
2001 ldpp_dout(this, 5) << "failed to parse xml: " << buf << dendl;
2002 return -EINVAL;
2003 }
2004
2005 try {
2006 RGWXMLDecoder::decode_xml("WebsiteConfiguration", website_conf, &parser, true);
2007 } catch (RGWXMLDecoder::err& err) {
2008 ldpp_dout(this, 5) << "unexpected xml: " << buf << dendl;
2009 return -EINVAL;
2010 }
2011
2012 if (website_conf.is_redirect_all && website_conf.redirect_all.hostname.empty()) {
2013 s->err.message = "A host name must be provided to redirect all requests (e.g. \"example.com\").";
2014 ldout(s->cct, 5) << s->err.message << dendl;
2015 return -EINVAL;
2016 } else if (!website_conf.is_redirect_all && !website_conf.is_set_index_doc) {
2017 s->err.message = "A value for IndexDocument Suffix must be provided if RedirectAllRequestsTo is empty";
2018 ldout(s->cct, 5) << s->err.message << dendl;
2019 return -EINVAL;
2020 } else if (!website_conf.is_redirect_all && website_conf.is_set_index_doc &&
2021 website_conf.index_doc_suffix.empty()) {
2022 s->err.message = "The IndexDocument Suffix is not well formed";
2023 ldout(s->cct, 5) << s->err.message << dendl;
2024 return -EINVAL;
2025 }
2026
2027 #define WEBSITE_ROUTING_RULES_MAX_NUM 50
2028 int max_num = s->cct->_conf->rgw_website_routing_rules_max_num;
2029 if (max_num < 0) {
2030 max_num = WEBSITE_ROUTING_RULES_MAX_NUM;
2031 }
2032 int routing_rules_num = website_conf.routing_rules.rules.size();
2033 if (routing_rules_num > max_num) {
2034 ldpp_dout(this, 4) << "An website routing config can have up to "
2035 << max_num
2036 << " rules, request website routing rules num: "
2037 << routing_rules_num << dendl;
2038 op_ret = -ERR_INVALID_WEBSITE_ROUTING_RULES_ERROR;
2039 s->err.message = std::to_string(routing_rules_num) +" routing rules provided, the number of routing rules in a website configuration is limited to "
2040 + std::to_string(max_num)
2041 + ".";
2042 return -ERR_INVALID_REQUEST;
2043 }
2044
2045 return 0;
2046 }
2047
2048 void RGWSetBucketWebsite_ObjStore_S3::send_response()
2049 {
2050 if (op_ret < 0)
2051 set_req_state_err(s, op_ret);
2052 dump_errno(s);
2053 end_header(s, this, "application/xml");
2054 }
2055
2056 void RGWDeleteBucketWebsite_ObjStore_S3::send_response()
2057 {
2058 if (op_ret == 0) {
2059 op_ret = STATUS_NO_CONTENT;
2060 }
2061 set_req_state_err(s, op_ret);
2062 dump_errno(s);
2063 end_header(s, this, "application/xml");
2064 }
2065
2066 void RGWGetBucketWebsite_ObjStore_S3::send_response()
2067 {
2068 if (op_ret)
2069 set_req_state_err(s, op_ret);
2070 dump_errno(s);
2071 end_header(s, this, "application/xml");
2072 dump_start(s);
2073
2074 if (op_ret < 0) {
2075 return;
2076 }
2077
2078 RGWBucketWebsiteConf& conf = s->bucket_info.website_conf;
2079
2080 s->formatter->open_object_section_in_ns("WebsiteConfiguration", XMLNS_AWS_S3);
2081 conf.dump_xml(s->formatter);
2082 s->formatter->close_section(); // WebsiteConfiguration
2083 rgw_flush_formatter_and_reset(s, s->formatter);
2084 }
2085
2086 static void dump_bucket_metadata(struct req_state *s, rgw::sal::RGWBucket* bucket)
2087 {
2088 dump_header(s, "X-RGW-Object-Count", static_cast<long long>(bucket->get_count()));
2089 dump_header(s, "X-RGW-Bytes-Used", static_cast<long long>(bucket->get_size()));
2090 }
2091
2092 void RGWStatBucket_ObjStore_S3::send_response()
2093 {
2094 if (op_ret >= 0) {
2095 dump_bucket_metadata(s, bucket);
2096 }
2097
2098 set_req_state_err(s, op_ret);
2099 dump_errno(s);
2100
2101 end_header(s, this);
2102 dump_start(s);
2103 }
2104
2105 static int create_s3_policy(struct req_state *s, rgw::sal::RGWRadosStore *store,
2106 RGWAccessControlPolicy_S3& s3policy,
2107 ACLOwner& owner)
2108 {
2109 if (s->has_acl_header) {
2110 if (!s->canned_acl.empty())
2111 return -ERR_INVALID_REQUEST;
2112
2113 return s3policy.create_from_headers(store->ctl()->user, s->info.env, owner);
2114 }
2115
2116 return s3policy.create_canned(owner, s->bucket_owner, s->canned_acl);
2117 }
2118
2119 class RGWLocationConstraint : public XMLObj
2120 {
2121 public:
2122 RGWLocationConstraint() {}
2123 ~RGWLocationConstraint() override {}
2124 bool xml_end(const char *el) override {
2125 if (!el)
2126 return false;
2127
2128 location_constraint = get_data();
2129
2130 return true;
2131 }
2132
2133 string location_constraint;
2134 };
2135
2136 class RGWCreateBucketConfig : public XMLObj
2137 {
2138 public:
2139 RGWCreateBucketConfig() {}
2140 ~RGWCreateBucketConfig() override {}
2141 };
2142
2143 class RGWCreateBucketParser : public RGWXMLParser
2144 {
2145 XMLObj *alloc_obj(const char *el) override {
2146 return new XMLObj;
2147 }
2148
2149 public:
2150 RGWCreateBucketParser() {}
2151 ~RGWCreateBucketParser() override {}
2152
2153 bool get_location_constraint(string& zone_group) {
2154 XMLObj *config = find_first("CreateBucketConfiguration");
2155 if (!config)
2156 return false;
2157
2158 XMLObj *constraint = config->find_first("LocationConstraint");
2159 if (!constraint)
2160 return false;
2161
2162 zone_group = constraint->get_data();
2163
2164 return true;
2165 }
2166 };
2167
2168 int RGWCreateBucket_ObjStore_S3::get_params()
2169 {
2170 RGWAccessControlPolicy_S3 s3policy(s->cct);
2171 bool relaxed_names = s->cct->_conf->rgw_relaxed_s3_bucket_names;
2172
2173 int r = valid_s3_bucket_name(s->bucket_name, relaxed_names);
2174 if (r)
2175 return r;
2176
2177 r = create_s3_policy(s, store, s3policy, s->owner);
2178 if (r < 0)
2179 return r;
2180
2181 policy = s3policy;
2182
2183 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
2184
2185 int op_ret = 0;
2186 bufferlist data;
2187 std::tie(op_ret, data) = rgw_rest_read_all_input(s, max_size, false);
2188
2189 if ((op_ret < 0) && (op_ret != -ERR_LENGTH_REQUIRED))
2190 return op_ret;
2191
2192 const int auth_ret = do_aws4_auth_completion();
2193 if (auth_ret < 0) {
2194 return auth_ret;
2195 }
2196
2197 in_data.append(data);
2198
2199 if (data.length()) {
2200 RGWCreateBucketParser parser;
2201
2202 if (!parser.init()) {
2203 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
2204 return -EIO;
2205 }
2206
2207 char* buf = data.c_str();
2208 bool success = parser.parse(buf, data.length(), 1);
2209 ldpp_dout(this, 20) << "create bucket input data=" << buf << dendl;
2210
2211 if (!success) {
2212 ldpp_dout(this, 0) << "failed to parse input: " << buf << dendl;
2213 return -EINVAL;
2214 }
2215
2216 if (!parser.get_location_constraint(location_constraint)) {
2217 ldpp_dout(this, 0) << "provided input did not specify location constraint correctly" << dendl;
2218 return -EINVAL;
2219 }
2220
2221 ldpp_dout(this, 10) << "create bucket location constraint: "
2222 << location_constraint << dendl;
2223 }
2224
2225 size_t pos = location_constraint.find(':');
2226 if (pos != string::npos) {
2227 placement_rule.init(location_constraint.substr(pos + 1), s->info.storage_class);
2228 location_constraint = location_constraint.substr(0, pos);
2229 } else {
2230 placement_rule.storage_class = s->info.storage_class;
2231 }
2232 auto iter = s->info.x_meta_map.find("x-amz-bucket-object-lock-enabled");
2233 if (iter != s->info.x_meta_map.end()) {
2234 if (!boost::algorithm::iequals(iter->second, "true") && !boost::algorithm::iequals(iter->second, "false")) {
2235 return -EINVAL;
2236 }
2237 obj_lock_enabled = boost::algorithm::iequals(iter->second, "true");
2238 }
2239 return 0;
2240 }
2241
2242 void RGWCreateBucket_ObjStore_S3::send_response()
2243 {
2244 if (op_ret == -ERR_BUCKET_EXISTS)
2245 op_ret = 0;
2246 if (op_ret)
2247 set_req_state_err(s, op_ret);
2248 dump_errno(s);
2249 end_header(s);
2250
2251 if (op_ret < 0)
2252 return;
2253
2254 if (s->system_request) {
2255 JSONFormatter f; /* use json formatter for system requests output */
2256
2257 f.open_object_section("info");
2258 encode_json("entry_point_object_ver", ep_objv, &f);
2259 encode_json("object_ver", info.objv_tracker.read_version, &f);
2260 encode_json("bucket_info", info, &f);
2261 f.close_section();
2262 rgw_flush_formatter_and_reset(s, &f);
2263 }
2264 }
2265
2266 void RGWDeleteBucket_ObjStore_S3::send_response()
2267 {
2268 int r = op_ret;
2269 if (!r)
2270 r = STATUS_NO_CONTENT;
2271
2272 set_req_state_err(s, r);
2273 dump_errno(s);
2274 end_header(s, this);
2275 }
2276
2277 static inline void map_qs_metadata(struct req_state* s)
2278 {
2279 /* merge S3 valid user metadata from the query-string into
2280 * x_meta_map, which maps them to attributes */
2281 const auto& params = const_cast<RGWHTTPArgs&>(s->info.args).get_params();
2282 for (const auto& elt : params) {
2283 std::string k = boost::algorithm::to_lower_copy(elt.first);
2284 if (k.find("x-amz-meta-") == /* offset */ 0) {
2285 rgw_add_amz_meta_header(s->info.x_meta_map, k, elt.second);
2286 }
2287 }
2288 }
2289
2290 int RGWPutObj_ObjStore_S3::get_params()
2291 {
2292 if (!s->length)
2293 return -ERR_LENGTH_REQUIRED;
2294
2295 map<string, bufferlist> src_attrs;
2296 size_t pos;
2297 int ret;
2298
2299 map_qs_metadata(s);
2300
2301 RGWAccessControlPolicy_S3 s3policy(s->cct);
2302 ret = create_s3_policy(s, store, s3policy, s->owner);
2303 if (ret < 0)
2304 return ret;
2305
2306 policy = s3policy;
2307
2308 if_match = s->info.env->get("HTTP_IF_MATCH");
2309 if_nomatch = s->info.env->get("HTTP_IF_NONE_MATCH");
2310 copy_source = url_decode(s->info.env->get("HTTP_X_AMZ_COPY_SOURCE", ""));
2311 copy_source_range = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE_RANGE");
2312
2313 /* handle x-amz-copy-source */
2314 boost::string_view cs_view(copy_source);
2315 if (! cs_view.empty()) {
2316 if (cs_view[0] == '/')
2317 cs_view.remove_prefix(1);
2318 copy_source_bucket_name = cs_view.to_string();
2319 pos = copy_source_bucket_name.find("/");
2320 if (pos == std::string::npos) {
2321 ret = -EINVAL;
2322 ldpp_dout(this, 5) << "x-amz-copy-source bad format" << dendl;
2323 return ret;
2324 }
2325 copy_source_object_name =
2326 copy_source_bucket_name.substr(pos + 1, copy_source_bucket_name.size());
2327 copy_source_bucket_name = copy_source_bucket_name.substr(0, pos);
2328 #define VERSION_ID_STR "?versionId="
2329 pos = copy_source_object_name.find(VERSION_ID_STR);
2330 if (pos == std::string::npos) {
2331 copy_source_object_name = url_decode(copy_source_object_name);
2332 } else {
2333 copy_source_version_id =
2334 copy_source_object_name.substr(pos + sizeof(VERSION_ID_STR) - 1);
2335 copy_source_object_name =
2336 url_decode(copy_source_object_name.substr(0, pos));
2337 }
2338 pos = copy_source_bucket_name.find(":");
2339 if (pos == std::string::npos) {
2340 copy_source_tenant_name = s->src_tenant_name;
2341 } else {
2342 copy_source_tenant_name = copy_source_bucket_name.substr(0, pos);
2343 copy_source_bucket_name = copy_source_bucket_name.substr(pos + 1, copy_source_bucket_name.size());
2344 if (copy_source_bucket_name.empty()) {
2345 ret = -EINVAL;
2346 ldpp_dout(this, 5) << "source bucket name is empty" << dendl;
2347 return ret;
2348 }
2349 }
2350 ret = store->getRados()->get_bucket_info(store->svc(),
2351 copy_source_tenant_name,
2352 copy_source_bucket_name,
2353 copy_source_bucket_info,
2354 NULL, s->yield, &src_attrs);
2355 if (ret < 0) {
2356 ldpp_dout(this, 5) << __func__ << "(): get_bucket_info() returned ret=" << ret << dendl;
2357 return ret;
2358 }
2359
2360 /* handle x-amz-copy-source-range */
2361
2362 if (copy_source_range) {
2363 string range = copy_source_range;
2364 pos = range.find("bytes=");
2365 if (pos == std::string::npos || pos != 0) {
2366 ret = -EINVAL;
2367 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format" << dendl;
2368 return ret;
2369 }
2370 /* 6 is the length of "bytes=" */
2371 range = range.substr(pos + 6);
2372 pos = range.find("-");
2373 if (pos == std::string::npos) {
2374 ret = -EINVAL;
2375 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format" << dendl;
2376 return ret;
2377 }
2378 string first = range.substr(0, pos);
2379 string last = range.substr(pos + 1);
2380 if (first.find_first_not_of("0123456789") != std::string::npos || last.find_first_not_of("0123456789") != std::string::npos)
2381 {
2382 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format not an integer" << dendl;
2383 ret = -EINVAL;
2384 return ret;
2385 }
2386 copy_source_range_fst = strtoull(first.c_str(), NULL, 10);
2387 copy_source_range_lst = strtoull(last.c_str(), NULL, 10);
2388 if (copy_source_range_fst > copy_source_range_lst)
2389 {
2390 ret = -ERANGE;
2391 ldpp_dout(this, 5) << "x-amz-copy-source-range bad format first number bigger than second" << dendl;
2392 return ret;
2393 }
2394 }
2395
2396 } /* copy_source */
2397
2398 /* handle object tagging */
2399 auto tag_str = s->info.env->get("HTTP_X_AMZ_TAGGING");
2400 if (tag_str){
2401 obj_tags = std::make_unique<RGWObjTags>();
2402 ret = obj_tags->set_from_string(tag_str);
2403 if (ret < 0){
2404 ldpp_dout(this,0) << "setting obj tags failed with " << ret << dendl;
2405 if (ret == -ERR_INVALID_TAG){
2406 ret = -EINVAL; //s3 returns only -EINVAL for PUT requests
2407 }
2408
2409 return ret;
2410 }
2411 }
2412
2413 //handle object lock
2414 auto obj_lock_mode_str = s->info.env->get("HTTP_X_AMZ_OBJECT_LOCK_MODE");
2415 auto obj_lock_date_str = s->info.env->get("HTTP_X_AMZ_OBJECT_LOCK_RETAIN_UNTIL_DATE");
2416 auto obj_legal_hold_str = s->info.env->get("HTTP_X_AMZ_OBJECT_LOCK_LEGAL_HOLD");
2417 if (obj_lock_mode_str && obj_lock_date_str) {
2418 boost::optional<ceph::real_time> date = ceph::from_iso_8601(obj_lock_date_str);
2419 if (boost::none == date || ceph::real_clock::to_time_t(*date) <= ceph_clock_now()) {
2420 ret = -EINVAL;
2421 ldpp_dout(this,0) << "invalid x-amz-object-lock-retain-until-date value" << dendl;
2422 return ret;
2423 }
2424 if (strcmp(obj_lock_mode_str, "GOVERNANCE") != 0 && strcmp(obj_lock_mode_str, "COMPLIANCE") != 0) {
2425 ret = -EINVAL;
2426 ldpp_dout(this,0) << "invalid x-amz-object-lock-mode value" << dendl;
2427 return ret;
2428 }
2429 obj_retention = new RGWObjectRetention(obj_lock_mode_str, *date);
2430 } else if ((obj_lock_mode_str && !obj_lock_date_str) || (!obj_lock_mode_str && obj_lock_date_str)) {
2431 ret = -EINVAL;
2432 ldpp_dout(this,0) << "need both x-amz-object-lock-mode and x-amz-object-lock-retain-until-date " << dendl;
2433 return ret;
2434 }
2435 if (obj_legal_hold_str) {
2436 if (strcmp(obj_legal_hold_str, "ON") != 0 && strcmp(obj_legal_hold_str, "OFF") != 0) {
2437 ret = -EINVAL;
2438 ldpp_dout(this,0) << "invalid x-amz-object-lock-legal-hold value" << dendl;
2439 return ret;
2440 }
2441 obj_legal_hold = new RGWObjectLegalHold(obj_legal_hold_str);
2442 }
2443 if (!s->bucket_info.obj_lock_enabled() && (obj_retention || obj_legal_hold)) {
2444 ldpp_dout(this, 0) << "ERROR: object retention or legal hold can't be set if bucket object lock not configured" << dendl;
2445 ret = -ERR_INVALID_REQUEST;
2446 return ret;
2447 }
2448 multipart_upload_id = s->info.args.get("uploadId");
2449 multipart_part_str = s->info.args.get("partNumber");
2450 if (!multipart_part_str.empty()) {
2451 string err;
2452 multipart_part_num = strict_strtol(multipart_part_str.c_str(), 10, &err);
2453 if (!err.empty()) {
2454 ldpp_dout(s, 10) << "bad part number: " << multipart_part_str << ": " << err << dendl;
2455 return -EINVAL;
2456 }
2457 } else if (!multipart_upload_id.empty()) {
2458 ldpp_dout(s, 10) << "part number with no multipart upload id" << dendl;
2459 return -EINVAL;
2460 }
2461
2462 append = s->info.args.exists("append");
2463 if (append) {
2464 string pos_str = s->info.args.get("position");
2465 if (pos_str.empty()) {
2466 return -EINVAL;
2467 } else {
2468 position = strtoull(pos_str.c_str(), NULL, 10);
2469 }
2470 }
2471
2472 return RGWPutObj_ObjStore::get_params();
2473 }
2474
2475 int RGWPutObj_ObjStore_S3::get_data(bufferlist& bl)
2476 {
2477 const int ret = RGWPutObj_ObjStore::get_data(bl);
2478 if (ret == 0) {
2479 const int ret_auth = do_aws4_auth_completion();
2480 if (ret_auth < 0) {
2481 return ret_auth;
2482 }
2483 }
2484
2485 return ret;
2486 }
2487
2488 static int get_success_retcode(int code)
2489 {
2490 switch (code) {
2491 case 201:
2492 return STATUS_CREATED;
2493 case 204:
2494 return STATUS_NO_CONTENT;
2495 }
2496 return 0;
2497 }
2498
2499 void RGWPutObj_ObjStore_S3::send_response()
2500 {
2501 if (op_ret) {
2502 set_req_state_err(s, op_ret);
2503 dump_errno(s);
2504 } else {
2505 if (s->cct->_conf->rgw_s3_success_create_obj_status) {
2506 op_ret = get_success_retcode(
2507 s->cct->_conf->rgw_s3_success_create_obj_status);
2508 set_req_state_err(s, op_ret);
2509 }
2510
2511 string expires = get_s3_expiration_header(s, mtime);
2512
2513 if (copy_source.empty()) {
2514 dump_errno(s);
2515 dump_etag(s, etag);
2516 dump_content_length(s, 0);
2517 dump_header_if_nonempty(s, "x-amz-version-id", version_id);
2518 dump_header_if_nonempty(s, "x-amz-expiration", expires);
2519 for (auto &it : crypt_http_responses)
2520 dump_header(s, it.first, it.second);
2521 } else {
2522 dump_errno(s);
2523 dump_header_if_nonempty(s, "x-amz-version-id", version_id);
2524 dump_header_if_nonempty(s, "x-amz-expiration", expires);
2525 end_header(s, this, "application/xml");
2526 dump_start(s);
2527 struct tm tmp;
2528 utime_t ut(mtime);
2529 time_t secs = (time_t)ut.sec();
2530 gmtime_r(&secs, &tmp);
2531 char buf[TIME_BUF_SIZE];
2532 s->formatter->open_object_section_in_ns("CopyPartResult",
2533 "http://s3.amazonaws.com/doc/2006-03-01/");
2534 if (strftime(buf, sizeof(buf), "%Y-%m-%dT%T.000Z", &tmp) > 0) {
2535 s->formatter->dump_string("LastModified", buf);
2536 }
2537 s->formatter->dump_string("ETag", etag);
2538 s->formatter->close_section();
2539 rgw_flush_formatter_and_reset(s, s->formatter);
2540 return;
2541 }
2542 }
2543 if (append) {
2544 if (op_ret == 0 || op_ret == -ERR_POSITION_NOT_EQUAL_TO_LENGTH) {
2545 dump_header(s, "x-rgw-next-append-position", cur_accounted_size);
2546 }
2547 }
2548 if (s->system_request && !real_clock::is_zero(mtime)) {
2549 dump_epoch_header(s, "Rgwx-Mtime", mtime);
2550 }
2551 end_header(s, this);
2552 }
2553
2554 static inline int get_obj_attrs(rgw::sal::RGWRadosStore *store, struct req_state *s, rgw_obj& obj, map<string, bufferlist>& attrs)
2555 {
2556 RGWRados::Object op_target(store->getRados(), s->bucket_info, *static_cast<RGWObjectCtx *>(s->obj_ctx), obj);
2557 RGWRados::Object::Read read_op(&op_target);
2558
2559 read_op.params.attrs = &attrs;
2560
2561 return read_op.prepare(s->yield);
2562 }
2563
2564 static inline void set_attr(map<string, bufferlist>& attrs, const char* key, const std::string& value)
2565 {
2566 bufferlist bl;
2567 encode(value,bl);
2568 attrs.emplace(key, std::move(bl));
2569 }
2570
2571 static inline void set_attr(map<string, bufferlist>& attrs, const char* key, const char* value)
2572 {
2573 bufferlist bl;
2574 encode(value,bl);
2575 attrs.emplace(key, std::move(bl));
2576 }
2577
2578 int RGWPutObj_ObjStore_S3::get_decrypt_filter(
2579 std::unique_ptr<RGWGetObj_Filter>* filter,
2580 RGWGetObj_Filter* cb,
2581 map<string, bufferlist>& attrs,
2582 bufferlist* manifest_bl)
2583 {
2584 std::map<std::string, std::string> crypt_http_responses_unused;
2585
2586 int res = 0;
2587 std::unique_ptr<BlockCrypt> block_crypt;
2588 res = rgw_s3_prepare_decrypt(s, attrs, &block_crypt, crypt_http_responses_unused);
2589 if (res == 0) {
2590 if (block_crypt != nullptr) {
2591 auto f = std::unique_ptr<RGWGetObj_BlockDecrypt>(new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt)));
2592 //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
2593 if (f != nullptr) {
2594 if (manifest_bl != nullptr) {
2595 res = f->read_manifest(*manifest_bl);
2596 if (res == 0) {
2597 *filter = std::move(f);
2598 }
2599 }
2600 }
2601 }
2602 }
2603 return res;
2604 }
2605
2606 int RGWPutObj_ObjStore_S3::get_encrypt_filter(
2607 std::unique_ptr<rgw::putobj::DataProcessor> *filter,
2608 rgw::putobj::DataProcessor *cb)
2609 {
2610 int res = 0;
2611 if (!multipart_upload_id.empty()) {
2612 RGWMPObj mp(s->object.name, multipart_upload_id);
2613 rgw_obj obj;
2614 obj.init_ns(s->bucket, mp.get_meta(), RGW_OBJ_NS_MULTIPART);
2615 obj.set_in_extra_data(true);
2616 map<string, bufferlist> xattrs;
2617 res = get_obj_attrs(store, s, obj, xattrs);
2618 if (res == 0) {
2619 std::unique_ptr<BlockCrypt> block_crypt;
2620 /* We are adding to existing object.
2621 * We use crypto mode that configured as if we were decrypting. */
2622 res = rgw_s3_prepare_decrypt(s, xattrs, &block_crypt, crypt_http_responses);
2623 if (res == 0 && block_crypt != nullptr)
2624 filter->reset(new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
2625 }
2626 /* it is ok, to not have encryption at all */
2627 }
2628 else
2629 {
2630 std::unique_ptr<BlockCrypt> block_crypt;
2631 res = rgw_s3_prepare_encrypt(s, attrs, nullptr, &block_crypt, crypt_http_responses);
2632 if (res == 0 && block_crypt != nullptr) {
2633 filter->reset(new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
2634 }
2635 }
2636 return res;
2637 }
2638
2639 void RGWPostObj_ObjStore_S3::rebuild_key(string& key)
2640 {
2641 static string var = "${filename}";
2642 int pos = key.find(var);
2643 if (pos < 0)
2644 return;
2645
2646 string new_key = key.substr(0, pos);
2647 new_key.append(filename);
2648 new_key.append(key.substr(pos + var.size()));
2649
2650 key = new_key;
2651 }
2652
2653 std::string RGWPostObj_ObjStore_S3::get_current_filename() const
2654 {
2655 return s->object.name;
2656 }
2657
2658 std::string RGWPostObj_ObjStore_S3::get_current_content_type() const
2659 {
2660 return content_type;
2661 }
2662
2663 int RGWPostObj_ObjStore_S3::get_params()
2664 {
2665 op_ret = RGWPostObj_ObjStore::get_params();
2666 if (op_ret < 0) {
2667 return op_ret;
2668 }
2669
2670 map_qs_metadata(s);
2671
2672 ldpp_dout(this, 20) << "adding bucket to policy env: " << s->bucket.name
2673 << dendl;
2674 env.add_var("bucket", s->bucket.name);
2675
2676 bool done;
2677 do {
2678 struct post_form_part part;
2679 int r = read_form_part_header(&part, done);
2680 if (r < 0)
2681 return r;
2682
2683 if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 20>()) {
2684 ldpp_dout(this, 20) << "read part header -- part.name="
2685 << part.name << dendl;
2686
2687 for (const auto& pair : part.fields) {
2688 ldpp_dout(this, 20) << "field.name=" << pair.first << dendl;
2689 ldpp_dout(this, 20) << "field.val=" << pair.second.val << dendl;
2690 ldpp_dout(this, 20) << "field.params:" << dendl;
2691
2692 for (const auto& param_pair : pair.second.params) {
2693 ldpp_dout(this, 20) << " " << param_pair.first
2694 << " -> " << param_pair.second << dendl;
2695 }
2696 }
2697 }
2698
2699 if (done) { /* unexpected here */
2700 err_msg = "Malformed request";
2701 return -EINVAL;
2702 }
2703
2704 if (stringcasecmp(part.name, "file") == 0) { /* beginning of data transfer */
2705 struct post_part_field& field = part.fields["Content-Disposition"];
2706 map<string, string>::iterator iter = field.params.find("filename");
2707 if (iter != field.params.end()) {
2708 filename = iter->second;
2709 }
2710 parts[part.name] = part;
2711 break;
2712 }
2713
2714 bool boundary;
2715 uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
2716 r = read_data(part.data, chunk_size, boundary, done);
2717 if (r < 0 || !boundary) {
2718 err_msg = "Couldn't find boundary";
2719 return -EINVAL;
2720 }
2721 parts[part.name] = part;
2722 string part_str(part.data.c_str(), part.data.length());
2723 env.add_var(part.name, part_str);
2724 } while (!done);
2725
2726 string object_str;
2727 if (!part_str(parts, "key", &object_str)) {
2728 err_msg = "Key not specified";
2729 return -EINVAL;
2730 }
2731
2732 s->object = rgw_obj_key(object_str);
2733
2734 rebuild_key(s->object.name);
2735
2736 if (s->object.empty()) {
2737 err_msg = "Empty object name";
2738 return -EINVAL;
2739 }
2740
2741 env.add_var("key", s->object.name);
2742
2743 part_str(parts, "Content-Type", &content_type);
2744
2745 /* AWS permits POST without Content-Type: http://tracker.ceph.com/issues/20201 */
2746 if (! content_type.empty()) {
2747 env.add_var("Content-Type", content_type);
2748 }
2749
2750 std::string storage_class;
2751 part_str(parts, "x-amz-storage-class", &storage_class);
2752
2753 if (! storage_class.empty()) {
2754 s->dest_placement.storage_class = storage_class;
2755 if (!store->svc()->zone->get_zone_params().valid_placement(s->dest_placement)) {
2756 ldpp_dout(this, 0) << "NOTICE: invalid dest placement: " << s->dest_placement.to_str() << dendl;
2757 err_msg = "The storage class you specified is not valid";
2758 return -EINVAL;
2759 }
2760 }
2761
2762 map<string, struct post_form_part, ltstr_nocase>::iterator piter =
2763 parts.upper_bound(RGW_AMZ_META_PREFIX);
2764 for (; piter != parts.end(); ++piter) {
2765 string n = piter->first;
2766 if (strncasecmp(n.c_str(), RGW_AMZ_META_PREFIX,
2767 sizeof(RGW_AMZ_META_PREFIX) - 1) != 0)
2768 break;
2769
2770 string attr_name = RGW_ATTR_PREFIX;
2771 attr_name.append(n);
2772
2773 /* need to null terminate it */
2774 bufferlist& data = piter->second.data;
2775 string str = string(data.c_str(), data.length());
2776
2777 bufferlist attr_bl;
2778 attr_bl.append(str.c_str(), str.size() + 1);
2779
2780 attrs[attr_name] = attr_bl;
2781 }
2782 // TODO: refactor this and the above loop to share code
2783 piter = parts.find(RGW_AMZ_WEBSITE_REDIRECT_LOCATION);
2784 if (piter != parts.end()) {
2785 string n = piter->first;
2786 string attr_name = RGW_ATTR_PREFIX;
2787 attr_name.append(n);
2788 /* need to null terminate it */
2789 bufferlist& data = piter->second.data;
2790 string str = string(data.c_str(), data.length());
2791
2792 bufferlist attr_bl;
2793 attr_bl.append(str.c_str(), str.size() + 1);
2794
2795 attrs[attr_name] = attr_bl;
2796 }
2797
2798 int r = get_policy();
2799 if (r < 0)
2800 return r;
2801
2802 r = get_tags();
2803 if (r < 0)
2804 return r;
2805
2806
2807 min_len = post_policy.min_length;
2808 max_len = post_policy.max_length;
2809
2810
2811
2812 return 0;
2813 }
2814
2815 int RGWPostObj_ObjStore_S3::get_tags()
2816 {
2817 string tags_str;
2818 if (part_str(parts, "tagging", &tags_str)) {
2819 RGWXMLParser parser;
2820 if (!parser.init()){
2821 ldpp_dout(this, 0) << "Couldn't init RGWObjTags XML parser" << dendl;
2822 err_msg = "Server couldn't process the request";
2823 return -EINVAL; // TODO: This class of errors in rgw code should be a 5XX error
2824 }
2825 if (!parser.parse(tags_str.c_str(), tags_str.size(), 1)) {
2826 ldpp_dout(this,0 ) << "Invalid Tagging XML" << dendl;
2827 err_msg = "Invalid Tagging XML";
2828 return -EINVAL;
2829 }
2830
2831 RGWObjTagging_S3 tagging;
2832
2833 try {
2834 RGWXMLDecoder::decode_xml("Tagging", tagging, &parser);
2835 } catch (RGWXMLDecoder::err& err) {
2836 ldpp_dout(this, 5) << "Malformed tagging request: " << err << dendl;
2837 return -EINVAL;
2838 }
2839
2840 RGWObjTags obj_tags;
2841 int r = tagging.rebuild(obj_tags);
2842 if (r < 0)
2843 return r;
2844
2845 bufferlist tags_bl;
2846 obj_tags.encode(tags_bl);
2847 ldpp_dout(this, 20) << "Read " << obj_tags.count() << "tags" << dendl;
2848 attrs[RGW_ATTR_TAGS] = tags_bl;
2849 }
2850
2851
2852 return 0;
2853 }
2854
2855 int RGWPostObj_ObjStore_S3::get_policy()
2856 {
2857 if (part_bl(parts, "policy", &s->auth.s3_postobj_creds.encoded_policy)) {
2858 bool aws4_auth = false;
2859
2860 /* x-amz-algorithm handling */
2861 using rgw::auth::s3::AWS4_HMAC_SHA256_STR;
2862 if ((part_str(parts, "x-amz-algorithm", &s->auth.s3_postobj_creds.x_amz_algorithm)) &&
2863 (s->auth.s3_postobj_creds.x_amz_algorithm == AWS4_HMAC_SHA256_STR)) {
2864 ldpp_dout(this, 0) << "Signature verification algorithm AWS v4 (AWS4-HMAC-SHA256)" << dendl;
2865 aws4_auth = true;
2866 } else {
2867 ldpp_dout(this, 0) << "Signature verification algorithm AWS v2" << dendl;
2868 }
2869
2870 // check that the signature matches the encoded policy
2871 if (aws4_auth) {
2872 /* AWS4 */
2873
2874 /* x-amz-credential handling */
2875 if (!part_str(parts, "x-amz-credential",
2876 &s->auth.s3_postobj_creds.x_amz_credential)) {
2877 ldpp_dout(this, 0) << "No S3 aws4 credential found!" << dendl;
2878 err_msg = "Missing aws4 credential";
2879 return -EINVAL;
2880 }
2881
2882 /* x-amz-signature handling */
2883 if (!part_str(parts, "x-amz-signature",
2884 &s->auth.s3_postobj_creds.signature)) {
2885 ldpp_dout(this, 0) << "No aws4 signature found!" << dendl;
2886 err_msg = "Missing aws4 signature";
2887 return -EINVAL;
2888 }
2889
2890 /* x-amz-date handling */
2891 std::string received_date_str;
2892 if (!part_str(parts, "x-amz-date", &received_date_str)) {
2893 ldpp_dout(this, 0) << "No aws4 date found!" << dendl;
2894 err_msg = "Missing aws4 date";
2895 return -EINVAL;
2896 }
2897 } else {
2898 /* AWS2 */
2899
2900 // check that the signature matches the encoded policy
2901 if (!part_str(parts, "AWSAccessKeyId",
2902 &s->auth.s3_postobj_creds.access_key)) {
2903 ldpp_dout(this, 0) << "No S3 aws2 access key found!" << dendl;
2904 err_msg = "Missing aws2 access key";
2905 return -EINVAL;
2906 }
2907
2908 if (!part_str(parts, "signature", &s->auth.s3_postobj_creds.signature)) {
2909 ldpp_dout(this, 0) << "No aws2 signature found!" << dendl;
2910 err_msg = "Missing aws2 signature";
2911 return -EINVAL;
2912 }
2913 }
2914
2915 if (part_str(parts, "x-amz-security-token", &s->auth.s3_postobj_creds.x_amz_security_token)) {
2916 if (s->auth.s3_postobj_creds.x_amz_security_token.size() == 0) {
2917 err_msg = "Invalid token";
2918 return -EINVAL;
2919 }
2920 }
2921
2922 /* FIXME: this is a makeshift solution. The browser upload authentication will be
2923 * handled by an instance of rgw::auth::Completer spawned in Handler's authorize()
2924 * method. */
2925 const int ret = rgw::auth::Strategy::apply(this, auth_registry_ptr->get_s3_post(), s);
2926 if (ret != 0) {
2927 return -EACCES;
2928 } else {
2929 /* Populate the owner info. */
2930 s->owner.set_id(s->user->get_id());
2931 s->owner.set_name(s->user->get_display_name());
2932 ldpp_dout(this, 20) << "Successful Signature Verification!" << dendl;
2933 }
2934
2935 ceph::bufferlist decoded_policy;
2936 try {
2937 decoded_policy.decode_base64(s->auth.s3_postobj_creds.encoded_policy);
2938 } catch (buffer::error& err) {
2939 ldpp_dout(this, 0) << "failed to decode_base64 policy" << dendl;
2940 err_msg = "Could not decode policy";
2941 return -EINVAL;
2942 }
2943
2944 decoded_policy.append('\0'); // NULL terminate
2945 ldpp_dout(this, 20) << "POST policy: " << decoded_policy.c_str() << dendl;
2946
2947
2948 int r = post_policy.from_json(decoded_policy, err_msg);
2949 if (r < 0) {
2950 if (err_msg.empty()) {
2951 err_msg = "Failed to parse policy";
2952 }
2953 ldpp_dout(this, 0) << "failed to parse policy" << dendl;
2954 return -EINVAL;
2955 }
2956
2957 if (aws4_auth) {
2958 /* AWS4 */
2959 post_policy.set_var_checked("x-amz-signature");
2960 } else {
2961 /* AWS2 */
2962 post_policy.set_var_checked("AWSAccessKeyId");
2963 post_policy.set_var_checked("signature");
2964 }
2965 post_policy.set_var_checked("policy");
2966
2967 r = post_policy.check(&env, err_msg);
2968 if (r < 0) {
2969 if (err_msg.empty()) {
2970 err_msg = "Policy check failed";
2971 }
2972 ldpp_dout(this, 0) << "policy check failed" << dendl;
2973 return r;
2974 }
2975
2976 } else {
2977 ldpp_dout(this, 0) << "No attached policy found!" << dendl;
2978 }
2979
2980 string canned_acl;
2981 part_str(parts, "acl", &canned_acl);
2982
2983 RGWAccessControlPolicy_S3 s3policy(s->cct);
2984 ldpp_dout(this, 20) << "canned_acl=" << canned_acl << dendl;
2985 if (s3policy.create_canned(s->owner, s->bucket_owner, canned_acl) < 0) {
2986 err_msg = "Bad canned ACLs";
2987 return -EINVAL;
2988 }
2989
2990 policy = s3policy;
2991
2992 return 0;
2993 }
2994
2995 int RGWPostObj_ObjStore_S3::complete_get_params()
2996 {
2997 bool done;
2998 do {
2999 struct post_form_part part;
3000 int r = read_form_part_header(&part, done);
3001 if (r < 0) {
3002 return r;
3003 }
3004
3005 ceph::bufferlist part_data;
3006 bool boundary;
3007 uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
3008 r = read_data(part.data, chunk_size, boundary, done);
3009 if (r < 0 || !boundary) {
3010 return -EINVAL;
3011 }
3012
3013 /* Just reading the data but not storing any results of that. */
3014 } while (!done);
3015
3016 return 0;
3017 }
3018
3019 int RGWPostObj_ObjStore_S3::get_data(ceph::bufferlist& bl, bool& again)
3020 {
3021 bool boundary;
3022 bool done;
3023
3024 const uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
3025 int r = read_data(bl, chunk_size, boundary, done);
3026 if (r < 0) {
3027 return r;
3028 }
3029
3030 if (boundary) {
3031 if (!done) {
3032 /* Reached end of data, let's drain the rest of the params */
3033 r = complete_get_params();
3034 if (r < 0) {
3035 return r;
3036 }
3037 }
3038 }
3039
3040 again = !boundary;
3041 return bl.length();
3042 }
3043
3044 void RGWPostObj_ObjStore_S3::send_response()
3045 {
3046 if (op_ret == 0 && parts.count("success_action_redirect")) {
3047 string redirect;
3048
3049 part_str(parts, "success_action_redirect", &redirect);
3050
3051 string tenant;
3052 string bucket;
3053 string key;
3054 string etag_str = "\"";
3055
3056 etag_str.append(etag);
3057 etag_str.append("\"");
3058
3059 string etag_url;
3060
3061 url_encode(s->bucket_tenant, tenant); /* surely overkill, but cheap */
3062 url_encode(s->bucket_name, bucket);
3063 url_encode(s->object.name, key);
3064 url_encode(etag_str, etag_url);
3065
3066 if (!s->bucket_tenant.empty()) {
3067 /*
3068 * What we really would like is to quaily the bucket name, so
3069 * that the client could simply copy it and paste into next request.
3070 * Unfortunately, in S3 we cannot know if the client will decide
3071 * to come through DNS, with "bucket.tenant" sytanx, or through
3072 * URL with "tenant\bucket" syntax. Therefore, we provide the
3073 * tenant separately.
3074 */
3075 redirect.append("?tenant=");
3076 redirect.append(tenant);
3077 redirect.append("&bucket=");
3078 redirect.append(bucket);
3079 } else {
3080 redirect.append("?bucket=");
3081 redirect.append(bucket);
3082 }
3083 redirect.append("&key=");
3084 redirect.append(key);
3085 redirect.append("&etag=");
3086 redirect.append(etag_url);
3087
3088 int r = check_utf8(redirect.c_str(), redirect.size());
3089 if (r < 0) {
3090 op_ret = r;
3091 goto done;
3092 }
3093 dump_redirect(s, redirect);
3094 op_ret = STATUS_REDIRECT;
3095 } else if (op_ret == 0 && parts.count("success_action_status")) {
3096 string status_string;
3097 uint32_t status_int;
3098
3099 part_str(parts, "success_action_status", &status_string);
3100
3101 int r = stringtoul(status_string, &status_int);
3102 if (r < 0) {
3103 op_ret = r;
3104 goto done;
3105 }
3106
3107 switch (status_int) {
3108 case 200:
3109 break;
3110 case 201:
3111 op_ret = STATUS_CREATED;
3112 break;
3113 default:
3114 op_ret = STATUS_NO_CONTENT;
3115 break;
3116 }
3117 } else if (! op_ret) {
3118 op_ret = STATUS_NO_CONTENT;
3119 }
3120
3121 done:
3122 if (op_ret == STATUS_CREATED) {
3123 for (auto &it : crypt_http_responses)
3124 dump_header(s, it.first, it.second);
3125 s->formatter->open_object_section("PostResponse");
3126 std::string base_uri = compute_domain_uri(s);
3127 if (!s->bucket_tenant.empty()){
3128 s->formatter->dump_format("Location", "%s/%s:%s/%s",
3129 base_uri.c_str(),
3130 url_encode(s->bucket_tenant).c_str(),
3131 url_encode(s->bucket_name).c_str(),
3132 url_encode(s->object.name).c_str());
3133 s->formatter->dump_string("Tenant", s->bucket_tenant);
3134 } else {
3135 s->formatter->dump_format("Location", "%s/%s/%s",
3136 base_uri.c_str(),
3137 url_encode(s->bucket_name).c_str(),
3138 url_encode(s->object.name).c_str());
3139 }
3140 s->formatter->dump_string("Bucket", s->bucket_name);
3141 s->formatter->dump_string("Key", s->object.name);
3142 s->formatter->dump_string("ETag", etag);
3143 s->formatter->close_section();
3144 }
3145 s->err.message = err_msg;
3146 set_req_state_err(s, op_ret);
3147 dump_errno(s);
3148 if (op_ret >= 0) {
3149 dump_content_length(s, s->formatter->get_len());
3150 }
3151 end_header(s, this);
3152 if (op_ret != STATUS_CREATED)
3153 return;
3154
3155 rgw_flush_formatter_and_reset(s, s->formatter);
3156 }
3157
3158 int RGWPostObj_ObjStore_S3::get_encrypt_filter(
3159 std::unique_ptr<rgw::putobj::DataProcessor> *filter,
3160 rgw::putobj::DataProcessor *cb)
3161 {
3162 std::unique_ptr<BlockCrypt> block_crypt;
3163 int res = rgw_s3_prepare_encrypt(s, attrs, &parts, &block_crypt,
3164 crypt_http_responses);
3165 if (res == 0 && block_crypt != nullptr) {
3166 filter->reset(new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
3167 }
3168 return res;
3169 }
3170
3171 int RGWDeleteObj_ObjStore_S3::get_params()
3172 {
3173 const char *if_unmod = s->info.env->get("HTTP_X_AMZ_DELETE_IF_UNMODIFIED_SINCE");
3174
3175 if (s->system_request) {
3176 s->info.args.get_bool(RGW_SYS_PARAM_PREFIX "no-precondition-error", &no_precondition_error, false);
3177 }
3178
3179 if (if_unmod) {
3180 std::string if_unmod_decoded = url_decode(if_unmod);
3181 uint64_t epoch;
3182 uint64_t nsec;
3183 if (utime_t::parse_date(if_unmod_decoded, &epoch, &nsec) < 0) {
3184 ldpp_dout(this, 10) << "failed to parse time: " << if_unmod_decoded << dendl;
3185 return -EINVAL;
3186 }
3187 unmod_since = utime_t(epoch, nsec).to_real_time();
3188 }
3189
3190 const char *bypass_gov_header = s->info.env->get("HTTP_X_AMZ_BYPASS_GOVERNANCE_RETENTION");
3191 if (bypass_gov_header) {
3192 std::string bypass_gov_decoded = url_decode(bypass_gov_header);
3193 bypass_governance_mode = boost::algorithm::iequals(bypass_gov_decoded, "true");
3194 }
3195
3196 return 0;
3197 }
3198
3199 void RGWDeleteObj_ObjStore_S3::send_response()
3200 {
3201 int r = op_ret;
3202 if (r == -ENOENT)
3203 r = 0;
3204 if (!r)
3205 r = STATUS_NO_CONTENT;
3206
3207 set_req_state_err(s, r);
3208 dump_errno(s);
3209 dump_header_if_nonempty(s, "x-amz-version-id", version_id);
3210 if (delete_marker) {
3211 dump_header(s, "x-amz-delete-marker", "true");
3212 }
3213 end_header(s, this);
3214 }
3215
3216 int RGWCopyObj_ObjStore_S3::init_dest_policy()
3217 {
3218 RGWAccessControlPolicy_S3 s3policy(s->cct);
3219
3220 /* build a policy for the target object */
3221 int r = create_s3_policy(s, store, s3policy, s->owner);
3222 if (r < 0)
3223 return r;
3224
3225 dest_policy = s3policy;
3226
3227 return 0;
3228 }
3229
3230 int RGWCopyObj_ObjStore_S3::get_params()
3231 {
3232 if_mod = s->info.env->get("HTTP_X_AMZ_COPY_IF_MODIFIED_SINCE");
3233 if_unmod = s->info.env->get("HTTP_X_AMZ_COPY_IF_UNMODIFIED_SINCE");
3234 if_match = s->info.env->get("HTTP_X_AMZ_COPY_IF_MATCH");
3235 if_nomatch = s->info.env->get("HTTP_X_AMZ_COPY_IF_NONE_MATCH");
3236
3237 src_tenant_name = s->src_tenant_name;
3238 src_bucket_name = s->src_bucket_name;
3239 src_object = s->src_object;
3240 dest_tenant_name = s->bucket.tenant;
3241 dest_bucket_name = s->bucket.name;
3242 dest_object = s->object.name;
3243
3244 if (s->system_request) {
3245 source_zone = s->info.args.get(RGW_SYS_PARAM_PREFIX "source-zone");
3246 s->info.args.get_bool(RGW_SYS_PARAM_PREFIX "copy-if-newer", &copy_if_newer, false);
3247 }
3248
3249 copy_source = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE");
3250 auto tmp_md_d = s->info.env->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
3251 if (tmp_md_d) {
3252 if (strcasecmp(tmp_md_d, "COPY") == 0) {
3253 attrs_mod = RGWRados::ATTRSMOD_NONE;
3254 } else if (strcasecmp(tmp_md_d, "REPLACE") == 0) {
3255 attrs_mod = RGWRados::ATTRSMOD_REPLACE;
3256 } else if (!source_zone.empty()) {
3257 attrs_mod = RGWRados::ATTRSMOD_NONE; // default for intra-zone_group copy
3258 } else {
3259 s->err.message = "Unknown metadata directive.";
3260 ldpp_dout(this, 0) << s->err.message << dendl;
3261 return -EINVAL;
3262 }
3263 md_directive = tmp_md_d;
3264 }
3265
3266 if (source_zone.empty() &&
3267 (dest_tenant_name.compare(src_tenant_name) == 0) &&
3268 (dest_bucket_name.compare(src_bucket_name) == 0) &&
3269 (dest_object.compare(src_object.name) == 0) &&
3270 src_object.instance.empty() &&
3271 (attrs_mod != RGWRados::ATTRSMOD_REPLACE)) {
3272 need_to_check_storage_class = true;
3273 }
3274
3275 return 0;
3276 }
3277
3278 int RGWCopyObj_ObjStore_S3::check_storage_class(const rgw_placement_rule& src_placement)
3279 {
3280 if (src_placement == s->dest_placement) {
3281 /* can only copy object into itself if replacing attrs */
3282 s->err.message = "This copy request is illegal because it is trying to copy "
3283 "an object to itself without changing the object's metadata, "
3284 "storage class, website redirect location or encryption attributes.";
3285 ldpp_dout(this, 0) << s->err.message << dendl;
3286 return -ERR_INVALID_REQUEST;
3287 }
3288 return 0;
3289 }
3290
3291 void RGWCopyObj_ObjStore_S3::send_partial_response(off_t ofs)
3292 {
3293 if (! sent_header) {
3294 if (op_ret)
3295 set_req_state_err(s, op_ret);
3296 dump_errno(s);
3297
3298 // Explicitly use chunked transfer encoding so that we can stream the result
3299 // to the user without having to wait for the full length of it.
3300 end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
3301 dump_start(s);
3302 if (op_ret == 0) {
3303 s->formatter->open_object_section_in_ns("CopyObjectResult", XMLNS_AWS_S3);
3304 }
3305 sent_header = true;
3306 } else {
3307 /* Send progress field. Note that this diverge from the original S3
3308 * spec. We do this in order to keep connection alive.
3309 */
3310 s->formatter->dump_int("Progress", (uint64_t)ofs);
3311 }
3312 rgw_flush_formatter(s, s->formatter);
3313 }
3314
3315 void RGWCopyObj_ObjStore_S3::send_response()
3316 {
3317 if (!sent_header)
3318 send_partial_response(0);
3319
3320 if (op_ret == 0) {
3321 dump_time(s, "LastModified", &mtime);
3322 if (!etag.empty()) {
3323 s->formatter->dump_string("ETag", std::move(etag));
3324 }
3325 s->formatter->close_section();
3326 rgw_flush_formatter_and_reset(s, s->formatter);
3327 }
3328 }
3329
3330 void RGWGetACLs_ObjStore_S3::send_response()
3331 {
3332 if (op_ret)
3333 set_req_state_err(s, op_ret);
3334 dump_errno(s);
3335 end_header(s, this, "application/xml");
3336 dump_start(s);
3337 rgw_flush_formatter(s, s->formatter);
3338 dump_body(s, acls);
3339 }
3340
3341 int RGWPutACLs_ObjStore_S3::get_params()
3342 {
3343 int ret = RGWPutACLs_ObjStore::get_params();
3344 if (ret >= 0) {
3345 const int ret_auth = do_aws4_auth_completion();
3346 if (ret_auth < 0) {
3347 return ret_auth;
3348 }
3349 } else {
3350 /* a request body is not required an S3 PutACLs request--n.b.,
3351 * s->length is non-null iff a content length was parsed (the
3352 * ACP or canned ACL could be in any of 3 headers, don't worry
3353 * about that here) */
3354 if ((ret == -ERR_LENGTH_REQUIRED) &&
3355 !!(s->length)) {
3356 return 0;
3357 }
3358 }
3359 return ret;
3360 }
3361
3362 int RGWPutACLs_ObjStore_S3::get_policy_from_state(rgw::sal::RGWRadosStore *store,
3363 struct req_state *s,
3364 stringstream& ss)
3365 {
3366 RGWAccessControlPolicy_S3 s3policy(s->cct);
3367
3368 // bucket-* canned acls do not apply to bucket
3369 if (s->object.empty()) {
3370 if (s->canned_acl.find("bucket") != string::npos)
3371 s->canned_acl.clear();
3372 }
3373
3374 int r = create_s3_policy(s, store, s3policy, owner);
3375 if (r < 0)
3376 return r;
3377
3378 s3policy.to_xml(ss);
3379
3380 return 0;
3381 }
3382
3383 void RGWPutACLs_ObjStore_S3::send_response()
3384 {
3385 if (op_ret)
3386 set_req_state_err(s, op_ret);
3387 dump_errno(s);
3388 end_header(s, this, "application/xml");
3389 dump_start(s);
3390 }
3391
3392 void RGWGetLC_ObjStore_S3::execute()
3393 {
3394 config.set_ctx(s->cct);
3395
3396 map<string, bufferlist>::iterator aiter = s->bucket_attrs.find(RGW_ATTR_LC);
3397 if (aiter == s->bucket_attrs.end()) {
3398 op_ret = -ENOENT;
3399 return;
3400 }
3401
3402 bufferlist::const_iterator iter{&aiter->second};
3403 try {
3404 config.decode(iter);
3405 } catch (const buffer::error& e) {
3406 ldpp_dout(this, 0) << __func__ << "decode life cycle config failed" << dendl;
3407 op_ret = -EIO;
3408 return;
3409 }
3410 }
3411
3412 void RGWGetLC_ObjStore_S3::send_response()
3413 {
3414 if (op_ret) {
3415 if (op_ret == -ENOENT) {
3416 set_req_state_err(s, ERR_NO_SUCH_LC);
3417 } else {
3418 set_req_state_err(s, op_ret);
3419 }
3420 }
3421 dump_errno(s);
3422 end_header(s, this, "application/xml");
3423 dump_start(s);
3424
3425 if (op_ret < 0)
3426 return;
3427
3428 encode_xml("LifecycleConfiguration", XMLNS_AWS_S3, config, s->formatter);
3429 rgw_flush_formatter_and_reset(s, s->formatter);
3430 }
3431
3432 void RGWPutLC_ObjStore_S3::send_response()
3433 {
3434 if (op_ret)
3435 set_req_state_err(s, op_ret);
3436 dump_errno(s);
3437 end_header(s, this, "application/xml");
3438 dump_start(s);
3439 }
3440
3441 void RGWDeleteLC_ObjStore_S3::send_response()
3442 {
3443 if (op_ret == 0)
3444 op_ret = STATUS_NO_CONTENT;
3445 if (op_ret) {
3446 set_req_state_err(s, op_ret);
3447 }
3448 dump_errno(s);
3449 end_header(s, this, "application/xml");
3450 dump_start(s);
3451 }
3452
3453 void RGWGetCORS_ObjStore_S3::send_response()
3454 {
3455 if (op_ret) {
3456 if (op_ret == -ENOENT)
3457 set_req_state_err(s, ERR_NO_SUCH_CORS_CONFIGURATION);
3458 else
3459 set_req_state_err(s, op_ret);
3460 }
3461 dump_errno(s);
3462 end_header(s, NULL, "application/xml");
3463 dump_start(s);
3464 if (! op_ret) {
3465 string cors;
3466 RGWCORSConfiguration_S3 *s3cors =
3467 static_cast<RGWCORSConfiguration_S3 *>(&bucket_cors);
3468 stringstream ss;
3469
3470 s3cors->to_xml(ss);
3471 cors = ss.str();
3472 dump_body(s, cors);
3473 }
3474 }
3475
3476 int RGWPutCORS_ObjStore_S3::get_params()
3477 {
3478 RGWCORSXMLParser_S3 parser(s->cct);
3479 RGWCORSConfiguration_S3 *cors_config;
3480
3481 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
3482
3483 int r = 0;
3484 bufferlist data;
3485 std::tie(r, data) = rgw_rest_read_all_input(s, max_size, false);
3486 if (r < 0) {
3487 return r;
3488 }
3489
3490 r = do_aws4_auth_completion();
3491 if (r < 0) {
3492 return r;
3493 }
3494
3495 if (!parser.init()) {
3496 return -EINVAL;
3497 }
3498
3499 char* buf = data.c_str();
3500 if (!buf || !parser.parse(buf, data.length(), 1)) {
3501 return -ERR_MALFORMED_XML;
3502 }
3503 cors_config =
3504 static_cast<RGWCORSConfiguration_S3 *>(parser.find_first(
3505 "CORSConfiguration"));
3506 if (!cors_config) {
3507 return -ERR_MALFORMED_XML;
3508 }
3509
3510 #define CORS_RULES_MAX_NUM 100
3511 int max_num = s->cct->_conf->rgw_cors_rules_max_num;
3512 if (max_num < 0) {
3513 max_num = CORS_RULES_MAX_NUM;
3514 }
3515 int cors_rules_num = cors_config->get_rules().size();
3516 if (cors_rules_num > max_num) {
3517 ldpp_dout(this, 4) << "An cors config can have up to "
3518 << max_num
3519 << " rules, request cors rules num: "
3520 << cors_rules_num << dendl;
3521 op_ret = -ERR_INVALID_CORS_RULES_ERROR;
3522 s->err.message = "The number of CORS rules should not exceed allowed limit of "
3523 + std::to_string(max_num) + " rules.";
3524 return -ERR_INVALID_REQUEST;
3525 }
3526
3527 // forward bucket cors requests to meta master zone
3528 if (!store->svc()->zone->is_meta_master()) {
3529 /* only need to keep this data around if we're not meta master */
3530 in_data.append(data);
3531 }
3532
3533 if (s->cct->_conf->subsys.should_gather<ceph_subsys_rgw, 15>()) {
3534 ldpp_dout(this, 15) << "CORSConfiguration";
3535 cors_config->to_xml(*_dout);
3536 *_dout << dendl;
3537 }
3538
3539 cors_config->encode(cors_bl);
3540
3541 return 0;
3542 }
3543
3544 void RGWPutCORS_ObjStore_S3::send_response()
3545 {
3546 if (op_ret)
3547 set_req_state_err(s, op_ret);
3548 dump_errno(s);
3549 end_header(s, NULL, "application/xml");
3550 dump_start(s);
3551 }
3552
3553 void RGWDeleteCORS_ObjStore_S3::send_response()
3554 {
3555 int r = op_ret;
3556 if (!r || r == -ENOENT)
3557 r = STATUS_NO_CONTENT;
3558
3559 set_req_state_err(s, r);
3560 dump_errno(s);
3561 end_header(s, NULL);
3562 }
3563
3564 void RGWOptionsCORS_ObjStore_S3::send_response()
3565 {
3566 string hdrs, exp_hdrs;
3567 uint32_t max_age = CORS_MAX_AGE_INVALID;
3568 /*EACCES means, there is no CORS registered yet for the bucket
3569 *ENOENT means, there is no match of the Origin in the list of CORSRule
3570 */
3571 if (op_ret == -ENOENT)
3572 op_ret = -EACCES;
3573 if (op_ret < 0) {
3574 set_req_state_err(s, op_ret);
3575 dump_errno(s);
3576 end_header(s, NULL);
3577 return;
3578 }
3579 get_response_params(hdrs, exp_hdrs, &max_age);
3580
3581 dump_errno(s);
3582 dump_access_control(s, origin, req_meth, hdrs.c_str(), exp_hdrs.c_str(),
3583 max_age);
3584 end_header(s, NULL);
3585 }
3586
3587 void RGWGetRequestPayment_ObjStore_S3::send_response()
3588 {
3589 dump_errno(s);
3590 end_header(s, this, "application/xml");
3591 dump_start(s);
3592
3593 s->formatter->open_object_section_in_ns("RequestPaymentConfiguration", XMLNS_AWS_S3);
3594 const char *payer = requester_pays ? "Requester" : "BucketOwner";
3595 s->formatter->dump_string("Payer", payer);
3596 s->formatter->close_section();
3597 rgw_flush_formatter_and_reset(s, s->formatter);
3598 }
3599
3600 class RGWSetRequestPaymentParser : public RGWXMLParser
3601 {
3602 XMLObj *alloc_obj(const char *el) override {
3603 return new XMLObj;
3604 }
3605
3606 public:
3607 RGWSetRequestPaymentParser() {}
3608 ~RGWSetRequestPaymentParser() override {}
3609
3610 int get_request_payment_payer(bool *requester_pays) {
3611 XMLObj *config = find_first("RequestPaymentConfiguration");
3612 if (!config)
3613 return -EINVAL;
3614
3615 *requester_pays = false;
3616
3617 XMLObj *field = config->find_first("Payer");
3618 if (!field)
3619 return 0;
3620
3621 auto& s = field->get_data();
3622
3623 if (stringcasecmp(s, "Requester") == 0) {
3624 *requester_pays = true;
3625 } else if (stringcasecmp(s, "BucketOwner") != 0) {
3626 return -EINVAL;
3627 }
3628
3629 return 0;
3630 }
3631 };
3632
3633 int RGWSetRequestPayment_ObjStore_S3::get_params()
3634 {
3635 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
3636
3637 int r = 0;
3638 std::tie(r, in_data) = rgw_rest_read_all_input(s, max_size, false);
3639
3640 if (r < 0) {
3641 return r;
3642 }
3643
3644
3645 RGWSetRequestPaymentParser parser;
3646
3647 if (!parser.init()) {
3648 ldpp_dout(this, 0) << "ERROR: failed to initialize parser" << dendl;
3649 return -EIO;
3650 }
3651
3652 char* buf = in_data.c_str();
3653 if (!parser.parse(buf, in_data.length(), 1)) {
3654 ldpp_dout(this, 10) << "failed to parse data: " << buf << dendl;
3655 return -EINVAL;
3656 }
3657
3658 return parser.get_request_payment_payer(&requester_pays);
3659 }
3660
3661 void RGWSetRequestPayment_ObjStore_S3::send_response()
3662 {
3663 if (op_ret)
3664 set_req_state_err(s, op_ret);
3665 dump_errno(s);
3666 end_header(s);
3667 }
3668
3669 int RGWInitMultipart_ObjStore_S3::get_params()
3670 {
3671 RGWAccessControlPolicy_S3 s3policy(s->cct);
3672 op_ret = create_s3_policy(s, store, s3policy, s->owner);
3673 if (op_ret < 0)
3674 return op_ret;
3675
3676 policy = s3policy;
3677
3678 return 0;
3679 }
3680
3681 void RGWInitMultipart_ObjStore_S3::send_response()
3682 {
3683 if (op_ret)
3684 set_req_state_err(s, op_ret);
3685 dump_errno(s);
3686 for (auto &it : crypt_http_responses)
3687 dump_header(s, it.first, it.second);
3688 end_header(s, this, "application/xml");
3689 if (op_ret == 0) {
3690 dump_start(s);
3691 s->formatter->open_object_section_in_ns("InitiateMultipartUploadResult", XMLNS_AWS_S3);
3692 if (!s->bucket_tenant.empty())
3693 s->formatter->dump_string("Tenant", s->bucket_tenant);
3694 s->formatter->dump_string("Bucket", s->bucket_name);
3695 s->formatter->dump_string("Key", s->object.name);
3696 s->formatter->dump_string("UploadId", upload_id);
3697 s->formatter->close_section();
3698 rgw_flush_formatter_and_reset(s, s->formatter);
3699 }
3700 }
3701
3702 int RGWInitMultipart_ObjStore_S3::prepare_encryption(map<string, bufferlist>& attrs)
3703 {
3704 int res = 0;
3705 res = rgw_s3_prepare_encrypt(s, attrs, nullptr, nullptr, crypt_http_responses);
3706 return res;
3707 }
3708
3709 int RGWCompleteMultipart_ObjStore_S3::get_params()
3710 {
3711 int ret = RGWCompleteMultipart_ObjStore::get_params();
3712 if (ret < 0) {
3713 return ret;
3714 }
3715
3716 map_qs_metadata(s);
3717
3718 return do_aws4_auth_completion();
3719 }
3720
3721 void RGWCompleteMultipart_ObjStore_S3::send_response()
3722 {
3723 if (op_ret)
3724 set_req_state_err(s, op_ret);
3725 dump_errno(s);
3726 dump_header_if_nonempty(s, "x-amz-version-id", version_id);
3727 end_header(s, this, "application/xml");
3728 if (op_ret == 0) {
3729 dump_start(s);
3730 s->formatter->open_object_section_in_ns("CompleteMultipartUploadResult", XMLNS_AWS_S3);
3731 std::string base_uri = compute_domain_uri(s);
3732 if (!s->bucket_tenant.empty()) {
3733 s->formatter->dump_format("Location", "%s/%s:%s/%s",
3734 base_uri.c_str(),
3735 s->bucket_tenant.c_str(),
3736 s->bucket_name.c_str(),
3737 s->object.name.c_str()
3738 );
3739 s->formatter->dump_string("Tenant", s->bucket_tenant);
3740 } else {
3741 s->formatter->dump_format("Location", "%s/%s/%s",
3742 base_uri.c_str(),
3743 s->bucket_name.c_str(),
3744 s->object.name.c_str()
3745 );
3746 }
3747 s->formatter->dump_string("Bucket", s->bucket_name);
3748 s->formatter->dump_string("Key", s->object.name);
3749 s->formatter->dump_string("ETag", etag);
3750 s->formatter->close_section();
3751 rgw_flush_formatter_and_reset(s, s->formatter);
3752 }
3753 }
3754
3755 void RGWAbortMultipart_ObjStore_S3::send_response()
3756 {
3757 int r = op_ret;
3758 if (!r)
3759 r = STATUS_NO_CONTENT;
3760
3761 set_req_state_err(s, r);
3762 dump_errno(s);
3763 end_header(s, this);
3764 }
3765
3766 void RGWListMultipart_ObjStore_S3::send_response()
3767 {
3768 if (op_ret)
3769 set_req_state_err(s, op_ret);
3770 dump_errno(s);
3771 // Explicitly use chunked transfer encoding so that we can stream the result
3772 // to the user without having to wait for the full length of it.
3773 end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
3774
3775 if (op_ret == 0) {
3776 dump_start(s);
3777 s->formatter->open_object_section_in_ns("ListPartsResult", XMLNS_AWS_S3);
3778 map<uint32_t, RGWUploadPartInfo>::iterator iter;
3779 map<uint32_t, RGWUploadPartInfo>::reverse_iterator test_iter;
3780 int cur_max = 0;
3781
3782 iter = parts.begin();
3783 test_iter = parts.rbegin();
3784 if (test_iter != parts.rend()) {
3785 cur_max = test_iter->first;
3786 }
3787 if (!s->bucket_tenant.empty())
3788 s->formatter->dump_string("Tenant", s->bucket_tenant);
3789 s->formatter->dump_string("Bucket", s->bucket_name);
3790 s->formatter->dump_string("Key", s->object.name);
3791 s->formatter->dump_string("UploadId", upload_id);
3792 s->formatter->dump_string("StorageClass", "STANDARD");
3793 s->formatter->dump_int("PartNumberMarker", marker);
3794 s->formatter->dump_int("NextPartNumberMarker", cur_max);
3795 s->formatter->dump_int("MaxParts", max_parts);
3796 s->formatter->dump_string("IsTruncated", (truncated ? "true" : "false"));
3797
3798 ACLOwner& owner = policy.get_owner();
3799 dump_owner(s, owner.get_id(), owner.get_display_name());
3800
3801 for (; iter != parts.end(); ++iter) {
3802 RGWUploadPartInfo& info = iter->second;
3803
3804 s->formatter->open_object_section("Part");
3805
3806 dump_time(s, "LastModified", &info.modified);
3807
3808 s->formatter->dump_unsigned("PartNumber", info.num);
3809 s->formatter->dump_format("ETag", "\"%s\"", info.etag.c_str());
3810 s->formatter->dump_unsigned("Size", info.accounted_size);
3811 s->formatter->close_section();
3812 }
3813 s->formatter->close_section();
3814 rgw_flush_formatter_and_reset(s, s->formatter);
3815 }
3816 }
3817
3818 void RGWListBucketMultiparts_ObjStore_S3::send_response()
3819 {
3820 if (op_ret < 0)
3821 set_req_state_err(s, op_ret);
3822 dump_errno(s);
3823
3824 // Explicitly use chunked transfer encoding so that we can stream the result
3825 // to the user without having to wait for the full length of it.
3826 end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
3827 dump_start(s);
3828 if (op_ret < 0)
3829 return;
3830
3831 s->formatter->open_object_section_in_ns("ListMultipartUploadsResult", XMLNS_AWS_S3);
3832 if (!s->bucket_tenant.empty())
3833 s->formatter->dump_string("Tenant", s->bucket_tenant);
3834 s->formatter->dump_string("Bucket", s->bucket_name);
3835 if (!prefix.empty())
3836 s->formatter->dump_string("ListMultipartUploadsResult.Prefix", prefix);
3837 const string& key_marker = marker.get_key();
3838 if (!key_marker.empty())
3839 s->formatter->dump_string("KeyMarker", key_marker);
3840 const string& upload_id_marker = marker.get_upload_id();
3841 if (!upload_id_marker.empty())
3842 s->formatter->dump_string("UploadIdMarker", upload_id_marker);
3843 string next_key = next_marker.mp.get_key();
3844 if (!next_key.empty())
3845 s->formatter->dump_string("NextKeyMarker", next_key);
3846 string next_upload_id = next_marker.mp.get_upload_id();
3847 if (!next_upload_id.empty())
3848 s->formatter->dump_string("NextUploadIdMarker", next_upload_id);
3849 s->formatter->dump_int("MaxUploads", max_uploads);
3850 if (!delimiter.empty())
3851 s->formatter->dump_string("Delimiter", delimiter);
3852 s->formatter->dump_string("IsTruncated", (is_truncated ? "true" : "false"));
3853
3854 if (op_ret >= 0) {
3855 vector<RGWMultipartUploadEntry>::iterator iter;
3856 for (iter = uploads.begin(); iter != uploads.end(); ++iter) {
3857 RGWMPObj& mp = iter->mp;
3858 s->formatter->open_array_section("Upload");
3859 if (encode_url) {
3860 s->formatter->dump_string("Key", url_encode(mp.get_key(), false));
3861 } else {
3862 s->formatter->dump_string("Key", mp.get_key());
3863 }
3864 s->formatter->dump_string("UploadId", mp.get_upload_id());
3865 dump_owner(s, s->user->get_id(), s->user->get_display_name(), "Initiator");
3866 dump_owner(s, s->user->get_id(), s->user->get_display_name());
3867 s->formatter->dump_string("StorageClass", "STANDARD");
3868 dump_time(s, "Initiated", &iter->obj.meta.mtime);
3869 s->formatter->close_section();
3870 }
3871 if (!common_prefixes.empty()) {
3872 s->formatter->open_array_section("CommonPrefixes");
3873 for (const auto& kv : common_prefixes) {
3874 if (encode_url) {
3875 s->formatter->dump_string("CommonPrefixes.Prefix",
3876 url_encode(kv.first, false));
3877 } else {
3878 s->formatter->dump_string("CommonPrefixes.Prefix", kv.first);
3879 }
3880 }
3881 s->formatter->close_section();
3882 }
3883 }
3884 s->formatter->close_section();
3885 rgw_flush_formatter_and_reset(s, s->formatter);
3886 }
3887
3888 int RGWDeleteMultiObj_ObjStore_S3::get_params()
3889 {
3890 int ret = RGWDeleteMultiObj_ObjStore::get_params();
3891 if (ret < 0) {
3892 return ret;
3893 }
3894
3895 return do_aws4_auth_completion();
3896 }
3897
3898 void RGWDeleteMultiObj_ObjStore_S3::send_status()
3899 {
3900 if (! status_dumped) {
3901 if (op_ret < 0)
3902 set_req_state_err(s, op_ret);
3903 dump_errno(s);
3904 status_dumped = true;
3905 }
3906 }
3907
3908 void RGWDeleteMultiObj_ObjStore_S3::begin_response()
3909 {
3910
3911 if (!status_dumped) {
3912 send_status();
3913 }
3914
3915 dump_start(s);
3916 // Explicitly use chunked transfer encoding so that we can stream the result
3917 // to the user without having to wait for the full length of it.
3918 end_header(s, this, "application/xml", CHUNKED_TRANSFER_ENCODING);
3919 s->formatter->open_object_section_in_ns("DeleteResult", XMLNS_AWS_S3);
3920
3921 rgw_flush_formatter(s, s->formatter);
3922 }
3923
3924 void RGWDeleteMultiObj_ObjStore_S3::send_partial_response(rgw_obj_key& key,
3925 bool delete_marker,
3926 const string& marker_version_id, int ret)
3927 {
3928 if (!key.empty()) {
3929 if (ret == 0 && !quiet) {
3930 s->formatter->open_object_section("Deleted");
3931 s->formatter->dump_string("Key", key.name);
3932 if (!key.instance.empty()) {
3933 s->formatter->dump_string("VersionId", key.instance);
3934 }
3935 if (delete_marker) {
3936 s->formatter->dump_bool("DeleteMarker", true);
3937 s->formatter->dump_string("DeleteMarkerVersionId", marker_version_id);
3938 }
3939 s->formatter->close_section();
3940 } else if (ret < 0) {
3941 struct rgw_http_error r;
3942 int err_no;
3943
3944 s->formatter->open_object_section("Error");
3945
3946 err_no = -ret;
3947 rgw_get_errno_s3(&r, err_no);
3948
3949 s->formatter->dump_string("Key", key.name);
3950 s->formatter->dump_string("VersionId", key.instance);
3951 s->formatter->dump_string("Code", r.s3_code);
3952 s->formatter->dump_string("Message", r.s3_code);
3953 s->formatter->close_section();
3954 }
3955
3956 rgw_flush_formatter(s, s->formatter);
3957 }
3958 }
3959
3960 void RGWDeleteMultiObj_ObjStore_S3::end_response()
3961 {
3962
3963 s->formatter->close_section();
3964 rgw_flush_formatter_and_reset(s, s->formatter);
3965 }
3966
3967 void RGWGetObjLayout_ObjStore_S3::send_response()
3968 {
3969 if (op_ret)
3970 set_req_state_err(s, op_ret);
3971 dump_errno(s);
3972 end_header(s, this, "application/json");
3973
3974 JSONFormatter f;
3975
3976 if (op_ret < 0) {
3977 return;
3978 }
3979
3980 f.open_object_section("result");
3981 ::encode_json("head", head_obj, &f);
3982 ::encode_json("manifest", *manifest, &f);
3983 f.open_array_section("data_location");
3984 for (auto miter = manifest->obj_begin(); miter != manifest->obj_end(); ++miter) {
3985 f.open_object_section("obj");
3986 rgw_raw_obj raw_loc = miter.get_location().get_raw_obj(store->getRados());
3987 uint64_t ofs = miter.get_ofs();
3988 uint64_t left = manifest->get_obj_size() - ofs;
3989 ::encode_json("ofs", miter.get_ofs(), &f);
3990 ::encode_json("loc", raw_loc, &f);
3991 ::encode_json("loc_ofs", miter.location_ofs(), &f);
3992 uint64_t loc_size = miter.get_stripe_size();
3993 if (loc_size > left) {
3994 loc_size = left;
3995 }
3996 ::encode_json("loc_size", loc_size, &f);
3997 f.close_section();
3998 rgw_flush_formatter(s, &f);
3999 }
4000 f.close_section();
4001 f.close_section();
4002 rgw_flush_formatter(s, &f);
4003 }
4004
4005 int RGWConfigBucketMetaSearch_ObjStore_S3::get_params()
4006 {
4007 auto iter = s->info.x_meta_map.find("x-amz-meta-search");
4008 if (iter == s->info.x_meta_map.end()) {
4009 s->err.message = "X-Rgw-Meta-Search header not provided";
4010 ldpp_dout(this, 5) << s->err.message << dendl;
4011 return -EINVAL;
4012 }
4013
4014 list<string> expressions;
4015 get_str_list(iter->second, ",", expressions);
4016
4017 for (auto& expression : expressions) {
4018 vector<string> args;
4019 get_str_vec(expression, ";", args);
4020
4021 if (args.empty()) {
4022 s->err.message = "invalid empty expression";
4023 ldpp_dout(this, 5) << s->err.message << dendl;
4024 return -EINVAL;
4025 }
4026 if (args.size() > 2) {
4027 s->err.message = string("invalid expression: ") + expression;
4028 ldpp_dout(this, 5) << s->err.message << dendl;
4029 return -EINVAL;
4030 }
4031
4032 string key = boost::algorithm::to_lower_copy(rgw_trim_whitespace(args[0]));
4033 string val;
4034 if (args.size() > 1) {
4035 val = boost::algorithm::to_lower_copy(rgw_trim_whitespace(args[1]));
4036 }
4037
4038 if (!boost::algorithm::starts_with(key, RGW_AMZ_META_PREFIX)) {
4039 s->err.message = string("invalid expression, key must start with '" RGW_AMZ_META_PREFIX "' : ") + expression;
4040 ldpp_dout(this, 5) << s->err.message << dendl;
4041 return -EINVAL;
4042 }
4043
4044 key = key.substr(sizeof(RGW_AMZ_META_PREFIX) - 1);
4045
4046 ESEntityTypeMap::EntityType entity_type;
4047
4048 if (val.empty() || val == "str" || val == "string") {
4049 entity_type = ESEntityTypeMap::ES_ENTITY_STR;
4050 } else if (val == "int" || val == "integer") {
4051 entity_type = ESEntityTypeMap::ES_ENTITY_INT;
4052 } else if (val == "date" || val == "datetime") {
4053 entity_type = ESEntityTypeMap::ES_ENTITY_DATE;
4054 } else {
4055 s->err.message = string("invalid entity type: ") + val;
4056 ldpp_dout(this, 5) << s->err.message << dendl;
4057 return -EINVAL;
4058 }
4059
4060 mdsearch_config[key] = entity_type;
4061 }
4062
4063 return 0;
4064 }
4065
4066 void RGWConfigBucketMetaSearch_ObjStore_S3::send_response()
4067 {
4068 if (op_ret)
4069 set_req_state_err(s, op_ret);
4070 dump_errno(s);
4071 end_header(s, this);
4072 }
4073
4074 void RGWGetBucketMetaSearch_ObjStore_S3::send_response()
4075 {
4076 if (op_ret)
4077 set_req_state_err(s, op_ret);
4078 dump_errno(s);
4079 end_header(s, NULL, "application/xml");
4080
4081 Formatter *f = s->formatter;
4082 f->open_array_section("GetBucketMetaSearchResult");
4083 for (auto& e : s->bucket_info.mdsearch_config) {
4084 f->open_object_section("Entry");
4085 string k = string("x-amz-meta-") + e.first;
4086 f->dump_string("Key", k.c_str());
4087 const char *type;
4088 switch (e.second) {
4089 case ESEntityTypeMap::ES_ENTITY_INT:
4090 type = "int";
4091 break;
4092 case ESEntityTypeMap::ES_ENTITY_DATE:
4093 type = "date";
4094 break;
4095 default:
4096 type = "str";
4097 }
4098 f->dump_string("Type", type);
4099 f->close_section();
4100 }
4101 f->close_section();
4102 rgw_flush_formatter(s, f);
4103 }
4104
4105 void RGWDelBucketMetaSearch_ObjStore_S3::send_response()
4106 {
4107 if (op_ret)
4108 set_req_state_err(s, op_ret);
4109 dump_errno(s);
4110 end_header(s, this);
4111 }
4112
4113 void RGWPutBucketObjectLock_ObjStore_S3::send_response()
4114 {
4115 if (op_ret) {
4116 set_req_state_err(s, op_ret);
4117 }
4118 dump_errno(s);
4119 end_header(s);
4120 }
4121
4122 void RGWGetBucketObjectLock_ObjStore_S3::send_response()
4123 {
4124 if (op_ret) {
4125 set_req_state_err(s, op_ret);
4126 }
4127 dump_errno(s);
4128 end_header(s, this, "application/xml");
4129 dump_start(s);
4130
4131 if (op_ret) {
4132 return;
4133 }
4134 encode_xml("ObjectLockConfiguration", s->bucket_info.obj_lock, s->formatter);
4135 rgw_flush_formatter_and_reset(s, s->formatter);
4136 }
4137
4138
4139 int RGWPutObjRetention_ObjStore_S3::get_params()
4140 {
4141 const char *bypass_gov_header = s->info.env->get("HTTP_X_AMZ_BYPASS_GOVERNANCE_RETENTION");
4142 if (bypass_gov_header) {
4143 std::string bypass_gov_decoded = url_decode(bypass_gov_header);
4144 bypass_governance_mode = boost::algorithm::iequals(bypass_gov_decoded, "true");
4145 }
4146
4147 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
4148 std::tie(op_ret, data) = rgw_rest_read_all_input(s, max_size, false);
4149 return op_ret;
4150 }
4151
4152 void RGWPutObjRetention_ObjStore_S3::send_response()
4153 {
4154 if (op_ret) {
4155 set_req_state_err(s, op_ret);
4156 }
4157 dump_errno(s);
4158 end_header(s);
4159 }
4160
4161 void RGWGetObjRetention_ObjStore_S3::send_response()
4162 {
4163 if (op_ret) {
4164 set_req_state_err(s, op_ret);
4165 }
4166 dump_errno(s);
4167 end_header(s, this, "application/xml");
4168 dump_start(s);
4169
4170 if (op_ret) {
4171 return;
4172 }
4173 encode_xml("Retention", obj_retention, s->formatter);
4174 rgw_flush_formatter_and_reset(s, s->formatter);
4175 }
4176
4177 void RGWPutObjLegalHold_ObjStore_S3::send_response()
4178 {
4179 if (op_ret) {
4180 set_req_state_err(s, op_ret);
4181 }
4182 dump_errno(s);
4183 end_header(s);
4184 }
4185
4186 void RGWGetObjLegalHold_ObjStore_S3::send_response()
4187 {
4188 if (op_ret) {
4189 set_req_state_err(s, op_ret);
4190 }
4191 dump_errno(s);
4192 end_header(s, this, "application/xml");
4193 dump_start(s);
4194
4195 if (op_ret) {
4196 return;
4197 }
4198 encode_xml("LegalHold", obj_legal_hold, s->formatter);
4199 rgw_flush_formatter_and_reset(s, s->formatter);
4200 }
4201
4202 void RGWGetBucketPolicyStatus_ObjStore_S3::send_response()
4203 {
4204 if (op_ret) {
4205 set_req_state_err(s, op_ret);
4206 }
4207 dump_errno(s);
4208 end_header(s, this, "application/xml");
4209 dump_start(s);
4210
4211 s->formatter->open_object_section_in_ns("PolicyStatus", XMLNS_AWS_S3);
4212 // https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGETPolicyStatus.html
4213 // mentions TRUE and FALSE, but boto/aws official clients seem to want lower
4214 // case which is returned by AWS as well; so let's be bug to bug compatible
4215 // with the API
4216 s->formatter->dump_bool("IsPublic", isPublic);
4217 s->formatter->close_section();
4218 rgw_flush_formatter_and_reset(s, s->formatter);
4219
4220 }
4221
4222 void RGWPutBucketPublicAccessBlock_ObjStore_S3::send_response()
4223 {
4224 if (op_ret) {
4225 set_req_state_err(s, op_ret);
4226 }
4227 dump_errno(s);
4228 end_header(s);
4229 }
4230
4231 void RGWGetBucketPublicAccessBlock_ObjStore_S3::send_response()
4232 {
4233 if (op_ret) {
4234 set_req_state_err(s, op_ret);
4235 }
4236 dump_errno(s);
4237 end_header(s, this, "application/xml");
4238 dump_start(s);
4239
4240 access_conf.dump_xml(s->formatter);
4241 rgw_flush_formatter_and_reset(s, s->formatter);
4242 }
4243
4244 RGWOp *RGWHandler_REST_Service_S3::op_get()
4245 {
4246 if (is_usage_op()) {
4247 return new RGWGetUsage_ObjStore_S3;
4248 } else {
4249 return new RGWListBuckets_ObjStore_S3;
4250 }
4251 }
4252
4253 RGWOp *RGWHandler_REST_Service_S3::op_head()
4254 {
4255 return new RGWListBuckets_ObjStore_S3;
4256 }
4257
4258 RGWOp *RGWHandler_REST_Service_S3::op_post()
4259 {
4260 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
4261
4262 int ret;
4263 bufferlist data;
4264 std::tie(ret, data) = rgw_rest_read_all_input(s, max_size, false);
4265 if (ret < 0) {
4266 return nullptr;
4267 }
4268
4269 const auto post_body = data.to_str();
4270
4271 if (isSTSEnabled) {
4272 RGWHandler_REST_STS sts_handler(auth_registry, post_body);
4273 sts_handler.init(store, s, s->cio);
4274 auto op = sts_handler.get_op();
4275 if (op) {
4276 return op;
4277 }
4278 }
4279
4280 if (isIAMEnabled) {
4281 RGWHandler_REST_IAM iam_handler(auth_registry, post_body);
4282 iam_handler.init(store, s, s->cio);
4283 auto op = iam_handler.get_op();
4284 if (op) {
4285 return op;
4286 }
4287 }
4288
4289 if (isPSEnabled) {
4290 RGWHandler_REST_PSTopic_AWS topic_handler(auth_registry, post_body);
4291 topic_handler.init(store, s, s->cio);
4292 auto op = topic_handler.get_op();
4293 if (op) {
4294 return op;
4295 }
4296 }
4297
4298 return nullptr;
4299 }
4300
4301 RGWOp *RGWHandler_REST_Bucket_S3::get_obj_op(bool get_data) const
4302 {
4303 // Non-website mode
4304 if (get_data) {
4305 int list_type = 1;
4306 s->info.args.get_int("list-type", &list_type, 1);
4307 switch (list_type) {
4308 case 1:
4309 return new RGWListBucket_ObjStore_S3;
4310 case 2:
4311 return new RGWListBucket_ObjStore_S3v2;
4312 default:
4313 ldpp_dout(s, 5) << __func__ << ": unsupported list-type " << list_type << dendl;
4314 return new RGWListBucket_ObjStore_S3;
4315 }
4316 } else {
4317 return new RGWStatBucket_ObjStore_S3;
4318 }
4319 }
4320
4321 RGWOp *RGWHandler_REST_Bucket_S3::op_get()
4322 {
4323 if (s->info.args.sub_resource_exists("logging"))
4324 return new RGWGetBucketLogging_ObjStore_S3;
4325
4326 if (s->info.args.sub_resource_exists("location"))
4327 return new RGWGetBucketLocation_ObjStore_S3;
4328
4329 if (s->info.args.sub_resource_exists("versioning"))
4330 return new RGWGetBucketVersioning_ObjStore_S3;
4331
4332 if (s->info.args.sub_resource_exists("website")) {
4333 if (!s->cct->_conf->rgw_enable_static_website) {
4334 return NULL;
4335 }
4336 return new RGWGetBucketWebsite_ObjStore_S3;
4337 }
4338
4339 if (s->info.args.exists("mdsearch")) {
4340 return new RGWGetBucketMetaSearch_ObjStore_S3;
4341 }
4342
4343 if (is_acl_op()) {
4344 return new RGWGetACLs_ObjStore_S3;
4345 } else if (is_cors_op()) {
4346 return new RGWGetCORS_ObjStore_S3;
4347 } else if (is_request_payment_op()) {
4348 return new RGWGetRequestPayment_ObjStore_S3;
4349 } else if (s->info.args.exists("uploads")) {
4350 return new RGWListBucketMultiparts_ObjStore_S3;
4351 } else if(is_lc_op()) {
4352 return new RGWGetLC_ObjStore_S3;
4353 } else if(is_policy_op()) {
4354 return new RGWGetBucketPolicy;
4355 } else if (is_tagging_op()) {
4356 return new RGWGetBucketTags_ObjStore_S3;
4357 } else if (is_object_lock_op()) {
4358 return new RGWGetBucketObjectLock_ObjStore_S3;
4359 } else if (is_notification_op()) {
4360 return RGWHandler_REST_PSNotifs_S3::create_get_op();
4361 } else if (is_replication_op()) {
4362 return new RGWGetBucketReplication_ObjStore_S3;
4363 } else if (is_policy_status_op()) {
4364 return new RGWGetBucketPolicyStatus_ObjStore_S3;
4365 } else if (is_block_public_access_op()) {
4366 return new RGWGetBucketPublicAccessBlock_ObjStore_S3;
4367 }
4368 return get_obj_op(true);
4369 }
4370
4371 RGWOp *RGWHandler_REST_Bucket_S3::op_head()
4372 {
4373 if (is_acl_op()) {
4374 return new RGWGetACLs_ObjStore_S3;
4375 } else if (s->info.args.exists("uploads")) {
4376 return new RGWListBucketMultiparts_ObjStore_S3;
4377 }
4378 return get_obj_op(false);
4379 }
4380
4381 RGWOp *RGWHandler_REST_Bucket_S3::op_put()
4382 {
4383 if (s->info.args.sub_resource_exists("logging"))
4384 return NULL;
4385 if (s->info.args.sub_resource_exists("versioning"))
4386 return new RGWSetBucketVersioning_ObjStore_S3;
4387 if (s->info.args.sub_resource_exists("website")) {
4388 if (!s->cct->_conf->rgw_enable_static_website) {
4389 return NULL;
4390 }
4391 return new RGWSetBucketWebsite_ObjStore_S3;
4392 }
4393 if (is_tagging_op()) {
4394 return new RGWPutBucketTags_ObjStore_S3;
4395 } else if (is_acl_op()) {
4396 return new RGWPutACLs_ObjStore_S3;
4397 } else if (is_cors_op()) {
4398 return new RGWPutCORS_ObjStore_S3;
4399 } else if (is_request_payment_op()) {
4400 return new RGWSetRequestPayment_ObjStore_S3;
4401 } else if(is_lc_op()) {
4402 return new RGWPutLC_ObjStore_S3;
4403 } else if(is_policy_op()) {
4404 return new RGWPutBucketPolicy;
4405 } else if (is_object_lock_op()) {
4406 return new RGWPutBucketObjectLock_ObjStore_S3;
4407 } else if (is_notification_op()) {
4408 return RGWHandler_REST_PSNotifs_S3::create_put_op();
4409 } else if (is_replication_op()) {
4410 auto sync_policy_handler = store->svc()->zone->get_sync_policy_handler(nullopt);
4411 if (!sync_policy_handler ||
4412 sync_policy_handler->is_legacy_config()) {
4413 return nullptr;
4414 }
4415
4416 return new RGWPutBucketReplication_ObjStore_S3;
4417 } else if (is_block_public_access_op()) {
4418 return new RGWPutBucketPublicAccessBlock_ObjStore_S3;
4419 }
4420 return new RGWCreateBucket_ObjStore_S3;
4421 }
4422
4423 RGWOp *RGWHandler_REST_Bucket_S3::op_delete()
4424 {
4425 if (is_tagging_op()) {
4426 return new RGWDeleteBucketTags_ObjStore_S3;
4427 } else if (is_cors_op()) {
4428 return new RGWDeleteCORS_ObjStore_S3;
4429 } else if(is_lc_op()) {
4430 return new RGWDeleteLC_ObjStore_S3;
4431 } else if(is_policy_op()) {
4432 return new RGWDeleteBucketPolicy;
4433 } else if (is_notification_op()) {
4434 return RGWHandler_REST_PSNotifs_S3::create_delete_op();
4435 } else if (is_replication_op()) {
4436 return new RGWDeleteBucketReplication_ObjStore_S3;
4437 } else if (is_block_public_access_op()) {
4438 return new RGWDeleteBucketPublicAccessBlock;
4439 }
4440
4441 if (s->info.args.sub_resource_exists("website")) {
4442 if (!s->cct->_conf->rgw_enable_static_website) {
4443 return NULL;
4444 }
4445 return new RGWDeleteBucketWebsite_ObjStore_S3;
4446 }
4447
4448 if (s->info.args.exists("mdsearch")) {
4449 return new RGWDelBucketMetaSearch_ObjStore_S3;
4450 }
4451
4452 return new RGWDeleteBucket_ObjStore_S3;
4453 }
4454
4455 RGWOp *RGWHandler_REST_Bucket_S3::op_post()
4456 {
4457 if (s->info.args.exists("delete")) {
4458 return new RGWDeleteMultiObj_ObjStore_S3;
4459 }
4460
4461 if (s->info.args.exists("mdsearch")) {
4462 return new RGWConfigBucketMetaSearch_ObjStore_S3;
4463 }
4464
4465 return new RGWPostObj_ObjStore_S3;
4466 }
4467
4468 RGWOp *RGWHandler_REST_Bucket_S3::op_options()
4469 {
4470 return new RGWOptionsCORS_ObjStore_S3;
4471 }
4472
4473 RGWOp *RGWHandler_REST_Obj_S3::get_obj_op(bool get_data)
4474 {
4475 RGWGetObj_ObjStore_S3 *get_obj_op = new RGWGetObj_ObjStore_S3;
4476 get_obj_op->set_get_data(get_data);
4477 return get_obj_op;
4478 }
4479
4480 RGWOp *RGWHandler_REST_Obj_S3::op_get()
4481 {
4482 if (is_acl_op()) {
4483 return new RGWGetACLs_ObjStore_S3;
4484 } else if (s->info.args.exists("uploadId")) {
4485 return new RGWListMultipart_ObjStore_S3;
4486 } else if (s->info.args.exists("layout")) {
4487 return new RGWGetObjLayout_ObjStore_S3;
4488 } else if (is_tagging_op()) {
4489 return new RGWGetObjTags_ObjStore_S3;
4490 } else if (is_obj_retention_op()) {
4491 return new RGWGetObjRetention_ObjStore_S3;
4492 } else if (is_obj_legal_hold_op()) {
4493 return new RGWGetObjLegalHold_ObjStore_S3;
4494 }
4495 return get_obj_op(true);
4496 }
4497
4498 RGWOp *RGWHandler_REST_Obj_S3::op_head()
4499 {
4500 if (is_acl_op()) {
4501 return new RGWGetACLs_ObjStore_S3;
4502 } else if (s->info.args.exists("uploadId")) {
4503 return new RGWListMultipart_ObjStore_S3;
4504 }
4505 return get_obj_op(false);
4506 }
4507
4508 RGWOp *RGWHandler_REST_Obj_S3::op_put()
4509 {
4510 if (is_acl_op()) {
4511 return new RGWPutACLs_ObjStore_S3;
4512 } else if (is_tagging_op()) {
4513 return new RGWPutObjTags_ObjStore_S3;
4514 } else if (is_obj_retention_op()) {
4515 return new RGWPutObjRetention_ObjStore_S3;
4516 } else if (is_obj_legal_hold_op()) {
4517 return new RGWPutObjLegalHold_ObjStore_S3;
4518 }
4519
4520 if (s->init_state.src_bucket.empty())
4521 return new RGWPutObj_ObjStore_S3;
4522 else
4523 return new RGWCopyObj_ObjStore_S3;
4524 }
4525
4526 RGWOp *RGWHandler_REST_Obj_S3::op_delete()
4527 {
4528 if (is_tagging_op()) {
4529 return new RGWDeleteObjTags_ObjStore_S3;
4530 }
4531 string upload_id = s->info.args.get("uploadId");
4532
4533 if (upload_id.empty())
4534 return new RGWDeleteObj_ObjStore_S3;
4535 else
4536 return new RGWAbortMultipart_ObjStore_S3;
4537 }
4538
4539 RGWOp *RGWHandler_REST_Obj_S3::op_post()
4540 {
4541 if (s->info.args.exists("uploadId"))
4542 return new RGWCompleteMultipart_ObjStore_S3;
4543
4544 if (s->info.args.exists("uploads"))
4545 return new RGWInitMultipart_ObjStore_S3;
4546
4547 return new RGWPostObj_ObjStore_S3;
4548 }
4549
4550 RGWOp *RGWHandler_REST_Obj_S3::op_options()
4551 {
4552 return new RGWOptionsCORS_ObjStore_S3;
4553 }
4554
4555 int RGWHandler_REST_S3::init_from_header(struct req_state* s,
4556 int default_formatter,
4557 bool configurable_format)
4558 {
4559 string req;
4560 string first;
4561
4562 const char *req_name = s->relative_uri.c_str();
4563 const char *p;
4564
4565 if (*req_name == '?') {
4566 p = req_name;
4567 } else {
4568 p = s->info.request_params.c_str();
4569 }
4570
4571 s->info.args.set(p);
4572 s->info.args.parse();
4573
4574 /* must be called after the args parsing */
4575 int ret = allocate_formatter(s, default_formatter, configurable_format);
4576 if (ret < 0)
4577 return ret;
4578
4579 if (*req_name != '/')
4580 return 0;
4581
4582 req_name++;
4583
4584 if (!*req_name)
4585 return 0;
4586
4587 req = req_name;
4588 int pos = req.find('/');
4589 if (pos >= 0) {
4590 first = req.substr(0, pos);
4591 } else {
4592 first = req;
4593 }
4594
4595 /*
4596 * XXX The intent of the check for empty is apparently to let the bucket
4597 * name from DNS to be set ahead. However, we currently take the DNS
4598 * bucket and re-insert it into URL in rgw_rest.cc:RGWREST::preprocess().
4599 * So, this check is meaningless.
4600 *
4601 * Rather than dropping this, the code needs to be changed into putting
4602 * the bucket (and its tenant) from DNS and Host: header (HTTP_HOST)
4603 * into req_status.bucket_name directly.
4604 */
4605 if (s->init_state.url_bucket.empty()) {
4606 // Save bucket to tide us over until token is parsed.
4607 s->init_state.url_bucket = first;
4608 if (pos >= 0) {
4609 string encoded_obj_str = req.substr(pos+1);
4610 s->object = rgw_obj_key(encoded_obj_str, s->info.args.get("versionId"));
4611 }
4612 } else {
4613 s->object = rgw_obj_key(req_name, s->info.args.get("versionId"));
4614 }
4615 return 0;
4616 }
4617
4618 static int verify_mfa(rgw::sal::RGWRadosStore *store, RGWUserInfo *user, const string& mfa_str, bool *verified, const DoutPrefixProvider *dpp)
4619 {
4620 vector<string> params;
4621 get_str_vec(mfa_str, " ", params);
4622
4623 if (params.size() != 2) {
4624 ldpp_dout(dpp, 5) << "NOTICE: invalid mfa string provided: " << mfa_str << dendl;
4625 return -EINVAL;
4626 }
4627
4628 string& serial = params[0];
4629 string& pin = params[1];
4630
4631 auto i = user->mfa_ids.find(serial);
4632 if (i == user->mfa_ids.end()) {
4633 ldpp_dout(dpp, 5) << "NOTICE: user does not have mfa device with serial=" << serial << dendl;
4634 return -EACCES;
4635 }
4636
4637 int ret = store->svc()->cls->mfa.check_mfa(user->user_id, serial, pin, null_yield);
4638 if (ret < 0) {
4639 ldpp_dout(dpp, 20) << "NOTICE: failed to check MFA, serial=" << serial << dendl;
4640 return -EACCES;
4641 }
4642
4643 *verified = true;
4644
4645 return 0;
4646 }
4647
4648 int RGWHandler_REST_S3::postauth_init()
4649 {
4650 struct req_init_state *t = &s->init_state;
4651
4652 rgw_parse_url_bucket(t->url_bucket, s->user->get_tenant(),
4653 s->bucket_tenant, s->bucket_name);
4654
4655 dout(10) << "s->object=" << (!s->object.empty() ? s->object : rgw_obj_key("<NULL>"))
4656 << " s->bucket=" << rgw_make_bucket_entry_name(s->bucket_tenant, s->bucket_name) << dendl;
4657
4658 int ret;
4659 ret = rgw_validate_tenant_name(s->bucket_tenant);
4660 if (ret)
4661 return ret;
4662 if (!s->bucket_name.empty()) {
4663 ret = validate_object_name(s->object.name);
4664 if (ret)
4665 return ret;
4666 }
4667
4668 if (!t->src_bucket.empty()) {
4669 rgw_parse_url_bucket(t->src_bucket, s->user->get_tenant(),
4670 s->src_tenant_name, s->src_bucket_name);
4671 ret = rgw_validate_tenant_name(s->src_tenant_name);
4672 if (ret)
4673 return ret;
4674 }
4675
4676 const char *mfa = s->info.env->get("HTTP_X_AMZ_MFA");
4677 if (mfa) {
4678 ret = verify_mfa(store, &s->user->get_info(), string(mfa), &s->mfa_verified, s);
4679 }
4680
4681 return 0;
4682 }
4683
4684 int RGWHandler_REST_S3::init(rgw::sal::RGWRadosStore *store, struct req_state *s,
4685 rgw::io::BasicClient *cio)
4686 {
4687 int ret;
4688
4689 s->dialect = "s3";
4690
4691 ret = rgw_validate_tenant_name(s->bucket_tenant);
4692 if (ret)
4693 return ret;
4694 if (!s->bucket_name.empty()) {
4695 ret = validate_object_name(s->object.name);
4696 if (ret)
4697 return ret;
4698 }
4699
4700 const char *cacl = s->info.env->get("HTTP_X_AMZ_ACL");
4701 if (cacl)
4702 s->canned_acl = cacl;
4703
4704 s->has_acl_header = s->info.env->exists_prefix("HTTP_X_AMZ_GRANT");
4705
4706 const char *copy_source = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE");
4707 if (copy_source &&
4708 (! s->info.env->get("HTTP_X_AMZ_COPY_SOURCE_RANGE")) &&
4709 (! s->info.args.exists("uploadId"))) {
4710
4711 ret = RGWCopyObj::parse_copy_location(copy_source,
4712 s->init_state.src_bucket,
4713 s->src_object);
4714 if (!ret) {
4715 ldpp_dout(s, 0) << "failed to parse copy location" << dendl;
4716 return -EINVAL; // XXX why not -ERR_INVALID_BUCKET_NAME or -ERR_BAD_URL?
4717 }
4718 }
4719
4720 const char *sc = s->info.env->get("HTTP_X_AMZ_STORAGE_CLASS");
4721 if (sc) {
4722 s->info.storage_class = sc;
4723 }
4724
4725 return RGWHandler_REST::init(store, s, cio);
4726 }
4727
4728 int RGWHandler_REST_S3::authorize(const DoutPrefixProvider *dpp)
4729 {
4730 if (s->info.args.exists("Action") && s->info.args.get("Action") == "AssumeRoleWithWebIdentity") {
4731 return RGW_Auth_STS::authorize(dpp, store, auth_registry, s);
4732 }
4733 return RGW_Auth_S3::authorize(dpp, store, auth_registry, s);
4734 }
4735
4736 enum class AwsVersion {
4737 UNKNOWN,
4738 V2,
4739 V4
4740 };
4741
4742 enum class AwsRoute {
4743 UNKNOWN,
4744 QUERY_STRING,
4745 HEADERS
4746 };
4747
4748 static inline std::pair<AwsVersion, AwsRoute>
4749 discover_aws_flavour(const req_info& info)
4750 {
4751 using rgw::auth::s3::AWS4_HMAC_SHA256_STR;
4752
4753 AwsVersion version = AwsVersion::UNKNOWN;
4754 AwsRoute route = AwsRoute::UNKNOWN;
4755
4756 const char* http_auth = info.env->get("HTTP_AUTHORIZATION");
4757 if (http_auth && http_auth[0]) {
4758 /* Authorization in Header */
4759 route = AwsRoute::HEADERS;
4760
4761 if (!strncmp(http_auth, AWS4_HMAC_SHA256_STR,
4762 strlen(AWS4_HMAC_SHA256_STR))) {
4763 /* AWS v4 */
4764 version = AwsVersion::V4;
4765 } else if (!strncmp(http_auth, "AWS ", 4)) {
4766 /* AWS v2 */
4767 version = AwsVersion::V2;
4768 }
4769 } else {
4770 route = AwsRoute::QUERY_STRING;
4771
4772 if (info.args.get("X-Amz-Algorithm") == AWS4_HMAC_SHA256_STR) {
4773 /* AWS v4 */
4774 version = AwsVersion::V4;
4775 } else if (!info.args.get("AWSAccessKeyId").empty()) {
4776 /* AWS v2 */
4777 version = AwsVersion::V2;
4778 }
4779 }
4780
4781 return std::make_pair(version, route);
4782 }
4783
4784 /*
4785 * verify that a signed request comes from the keyholder
4786 * by checking the signature against our locally-computed version
4787 *
4788 * it tries AWS v4 before AWS v2
4789 */
4790 int RGW_Auth_S3::authorize(const DoutPrefixProvider *dpp,
4791 rgw::sal::RGWRadosStore* const store,
4792 const rgw::auth::StrategyRegistry& auth_registry,
4793 struct req_state* const s)
4794 {
4795
4796 /* neither keystone and rados enabled; warn and exit! */
4797 if (!store->ctx()->_conf->rgw_s3_auth_use_rados &&
4798 !store->ctx()->_conf->rgw_s3_auth_use_keystone &&
4799 !store->ctx()->_conf->rgw_s3_auth_use_ldap) {
4800 ldpp_dout(dpp, 0) << "WARNING: no authorization backend enabled! Users will never authenticate." << dendl;
4801 return -EPERM;
4802 }
4803
4804 const auto ret = rgw::auth::Strategy::apply(dpp, auth_registry.get_s3_main(), s);
4805 if (ret == 0) {
4806 /* Populate the owner info. */
4807 s->owner.set_id(s->user->get_id());
4808 s->owner.set_name(s->user->get_display_name());
4809 }
4810 return ret;
4811 }
4812
4813 int RGWHandler_Auth_S3::init(rgw::sal::RGWRadosStore *store, struct req_state *state,
4814 rgw::io::BasicClient *cio)
4815 {
4816 int ret = RGWHandler_REST_S3::init_from_header(state, RGW_FORMAT_JSON,
4817 true);
4818 if (ret < 0)
4819 return ret;
4820
4821 return RGWHandler_REST::init(store, state, cio);
4822 }
4823
4824 RGWHandler_REST* RGWRESTMgr_S3::get_handler(struct req_state* const s,
4825 const rgw::auth::StrategyRegistry& auth_registry,
4826 const std::string& frontend_prefix)
4827 {
4828 bool is_s3website = enable_s3website && (s->prot_flags & RGW_REST_WEBSITE);
4829 int ret =
4830 RGWHandler_REST_S3::init_from_header(s,
4831 is_s3website ? RGW_FORMAT_HTML :
4832 RGW_FORMAT_XML, true);
4833 if (ret < 0)
4834 return NULL;
4835
4836 RGWHandler_REST* handler;
4837 // TODO: Make this more readable
4838 if (is_s3website) {
4839 if (s->init_state.url_bucket.empty()) {
4840 handler = new RGWHandler_REST_Service_S3Website(auth_registry);
4841 } else if (s->object.empty()) {
4842 handler = new RGWHandler_REST_Bucket_S3Website(auth_registry);
4843 } else {
4844 handler = new RGWHandler_REST_Obj_S3Website(auth_registry);
4845 }
4846 } else {
4847 if (s->init_state.url_bucket.empty()) {
4848 handler = new RGWHandler_REST_Service_S3(auth_registry, enable_sts, enable_iam, enable_pubsub);
4849 } else if (s->object.empty()) {
4850 handler = new RGWHandler_REST_Bucket_S3(auth_registry, enable_pubsub);
4851 } else {
4852 handler = new RGWHandler_REST_Obj_S3(auth_registry);
4853 }
4854 }
4855
4856 ldpp_dout(s, 20) << __func__ << " handler=" << typeid(*handler).name()
4857 << dendl;
4858 return handler;
4859 }
4860
4861 bool RGWHandler_REST_S3Website::web_dir() const {
4862 std::string subdir_name = url_decode(s->object.name);
4863
4864 if (subdir_name.empty()) {
4865 return false;
4866 } else if (subdir_name.back() == '/') {
4867 subdir_name.pop_back();
4868 }
4869
4870 rgw_obj obj(s->bucket, subdir_name);
4871
4872 RGWObjectCtx& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
4873 obj_ctx.set_atomic(obj);
4874 obj_ctx.set_prefetch_data(obj);
4875
4876 RGWObjState* state = nullptr;
4877 if (store->getRados()->get_obj_state(&obj_ctx, s->bucket_info, obj, &state, false, s->yield) < 0) {
4878 return false;
4879 }
4880 if (! state->exists) {
4881 return false;
4882 }
4883 return state->exists;
4884 }
4885
4886 int RGWHandler_REST_S3Website::init(rgw::sal::RGWRadosStore *store, req_state *s,
4887 rgw::io::BasicClient* cio)
4888 {
4889 // save the original object name before retarget() replaces it with the
4890 // result of get_effective_key(). the error_handler() needs the original
4891 // object name for redirect handling
4892 original_object_name = s->object.name;
4893
4894 return RGWHandler_REST_S3::init(store, s, cio);
4895 }
4896
4897 int RGWHandler_REST_S3Website::retarget(RGWOp* op, RGWOp** new_op) {
4898 *new_op = op;
4899 ldpp_dout(s, 10) << __func__ << " Starting retarget" << dendl;
4900
4901 if (!(s->prot_flags & RGW_REST_WEBSITE))
4902 return 0;
4903
4904 int ret = store->getRados()->get_bucket_info(store->svc(), s->bucket_tenant,
4905 s->bucket_name, s->bucket_info, NULL,
4906 s->yield, &s->bucket_attrs);
4907 if (ret < 0) {
4908 // TODO-FUTURE: if the bucket does not exist, maybe expose it here?
4909 return -ERR_NO_SUCH_BUCKET;
4910 }
4911 if (!s->bucket_info.has_website) {
4912 // TODO-FUTURE: if the bucket has no WebsiteConfig, expose it here
4913 return -ERR_NO_SUCH_WEBSITE_CONFIGURATION;
4914 }
4915
4916 rgw_obj_key new_obj;
4917 bool get_res = s->bucket_info.website_conf.get_effective_key(s->object.name, &new_obj.name, web_dir());
4918 if (!get_res) {
4919 s->err.message = "The IndexDocument Suffix is not configurated or not well formed!";
4920 ldpp_dout(s, 5) << s->err.message << dendl;
4921 return -EINVAL;
4922 }
4923
4924 ldpp_dout(s, 10) << "retarget get_effective_key " << s->object << " -> "
4925 << new_obj << dendl;
4926
4927 RGWBWRoutingRule rrule;
4928 bool should_redirect =
4929 s->bucket_info.website_conf.should_redirect(new_obj.name, 0, &rrule);
4930
4931 if (should_redirect) {
4932 const string& hostname = s->info.env->get("HTTP_HOST", "");
4933 const string& protocol =
4934 (s->info.env->get("SERVER_PORT_SECURE") ? "https" : "http");
4935 int redirect_code = 0;
4936 rrule.apply_rule(protocol, hostname, s->object.name, &s->redirect,
4937 &redirect_code);
4938 // APply a custom HTTP response code
4939 if (redirect_code > 0)
4940 s->err.http_ret = redirect_code; // Apply a custom HTTP response code
4941 ldpp_dout(s, 10) << "retarget redirect code=" << redirect_code
4942 << " proto+host:" << protocol << "://" << hostname
4943 << " -> " << s->redirect << dendl;
4944 return -ERR_WEBSITE_REDIRECT;
4945 }
4946
4947 /*
4948 * FIXME: if s->object != new_obj, drop op and create a new op to handle
4949 * operation. Or remove this comment if it's not applicable anymore
4950 */
4951
4952 s->object = new_obj;
4953
4954 return 0;
4955 }
4956
4957 RGWOp* RGWHandler_REST_S3Website::op_get()
4958 {
4959 return get_obj_op(true);
4960 }
4961
4962 RGWOp* RGWHandler_REST_S3Website::op_head()
4963 {
4964 return get_obj_op(false);
4965 }
4966
4967 int RGWHandler_REST_S3Website::serve_errordoc(int http_ret, const string& errordoc_key) {
4968 int ret = 0;
4969 s->formatter->reset(); /* Try to throw it all away */
4970
4971 std::shared_ptr<RGWGetObj_ObjStore_S3Website> getop( static_cast<RGWGetObj_ObjStore_S3Website*>(op_get()));
4972 if (getop.get() == NULL) {
4973 return -1; // Trigger double error handler
4974 }
4975 getop->init(store, s, this);
4976 getop->range_str = NULL;
4977 getop->if_mod = NULL;
4978 getop->if_unmod = NULL;
4979 getop->if_match = NULL;
4980 getop->if_nomatch = NULL;
4981 s->object = errordoc_key;
4982
4983 ret = init_permissions(getop.get());
4984 if (ret < 0) {
4985 ldpp_dout(s, 20) << "serve_errordoc failed, init_permissions ret=" << ret << dendl;
4986 return -1; // Trigger double error handler
4987 }
4988
4989 ret = read_permissions(getop.get());
4990 if (ret < 0) {
4991 ldpp_dout(s, 20) << "serve_errordoc failed, read_permissions ret=" << ret << dendl;
4992 return -1; // Trigger double error handler
4993 }
4994
4995 if (http_ret) {
4996 getop->set_custom_http_response(http_ret);
4997 }
4998
4999 ret = getop->init_processing();
5000 if (ret < 0) {
5001 ldpp_dout(s, 20) << "serve_errordoc failed, init_processing ret=" << ret << dendl;
5002 return -1; // Trigger double error handler
5003 }
5004
5005 ret = getop->verify_op_mask();
5006 if (ret < 0) {
5007 ldpp_dout(s, 20) << "serve_errordoc failed, verify_op_mask ret=" << ret << dendl;
5008 return -1; // Trigger double error handler
5009 }
5010
5011 ret = getop->verify_permission();
5012 if (ret < 0) {
5013 ldpp_dout(s, 20) << "serve_errordoc failed, verify_permission ret=" << ret << dendl;
5014 return -1; // Trigger double error handler
5015 }
5016
5017 ret = getop->verify_params();
5018 if (ret < 0) {
5019 ldpp_dout(s, 20) << "serve_errordoc failed, verify_params ret=" << ret << dendl;
5020 return -1; // Trigger double error handler
5021 }
5022
5023 // No going back now
5024 getop->pre_exec();
5025 /*
5026 * FIXME Missing headers:
5027 * With a working errordoc, the s3 error fields are rendered as HTTP headers,
5028 * x-amz-error-code: NoSuchKey
5029 * x-amz-error-message: The specified key does not exist.
5030 * x-amz-error-detail-Key: foo
5031 */
5032 getop->execute();
5033 getop->complete();
5034 return 0;
5035
5036 }
5037
5038 int RGWHandler_REST_S3Website::error_handler(int err_no,
5039 string* error_content) {
5040 int new_err_no = -1;
5041 rgw_http_errors::const_iterator r = rgw_http_s3_errors.find(err_no > 0 ? err_no : -err_no);
5042 int http_error_code = -1;
5043
5044 if (r != rgw_http_s3_errors.end()) {
5045 http_error_code = r->second.first;
5046 }
5047 ldpp_dout(s, 10) << "RGWHandler_REST_S3Website::error_handler err_no=" << err_no << " http_ret=" << http_error_code << dendl;
5048
5049 RGWBWRoutingRule rrule;
5050 bool should_redirect =
5051 s->bucket_info.website_conf.should_redirect(original_object_name,
5052 http_error_code, &rrule);
5053
5054 if (should_redirect) {
5055 const string& hostname = s->info.env->get("HTTP_HOST", "");
5056 const string& protocol =
5057 (s->info.env->get("SERVER_PORT_SECURE") ? "https" : "http");
5058 int redirect_code = 0;
5059 rrule.apply_rule(protocol, hostname, original_object_name,
5060 &s->redirect, &redirect_code);
5061 // Apply a custom HTTP response code
5062 if (redirect_code > 0)
5063 s->err.http_ret = redirect_code; // Apply a custom HTTP response code
5064 ldpp_dout(s, 10) << "error handler redirect code=" << redirect_code
5065 << " proto+host:" << protocol << "://" << hostname
5066 << " -> " << s->redirect << dendl;
5067 return -ERR_WEBSITE_REDIRECT;
5068 } else if (err_no == -ERR_WEBSITE_REDIRECT) {
5069 // Do nothing here, this redirect will be handled in abort_early's ERR_WEBSITE_REDIRECT block
5070 // Do NOT fire the ErrorDoc handler
5071 } else if (!s->bucket_info.website_conf.error_doc.empty()) {
5072 /* This serves an entire page!
5073 On success, it will return zero, and no further content should be sent to the socket
5074 On failure, we need the double-error handler
5075 */
5076 new_err_no = RGWHandler_REST_S3Website::serve_errordoc(http_error_code, s->bucket_info.website_conf.error_doc);
5077 if (new_err_no && new_err_no != -1) {
5078 err_no = new_err_no;
5079 }
5080 } else {
5081 ldpp_dout(s, 20) << "No special error handling today!" << dendl;
5082 }
5083
5084 return err_no;
5085 }
5086
5087 RGWOp* RGWHandler_REST_Obj_S3Website::get_obj_op(bool get_data)
5088 {
5089 /** If we are in website mode, then it is explicitly impossible to run GET or
5090 * HEAD on the actual directory. We must convert the request to run on the
5091 * suffix object instead!
5092 */
5093 RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
5094 op->set_get_data(get_data);
5095 return op;
5096 }
5097
5098 RGWOp* RGWHandler_REST_Bucket_S3Website::get_obj_op(bool get_data)
5099 {
5100 /** If we are in website mode, then it is explicitly impossible to run GET or
5101 * HEAD on the actual directory. We must convert the request to run on the
5102 * suffix object instead!
5103 */
5104 RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
5105 op->set_get_data(get_data);
5106 return op;
5107 }
5108
5109 RGWOp* RGWHandler_REST_Service_S3Website::get_obj_op(bool get_data)
5110 {
5111 /** If we are in website mode, then it is explicitly impossible to run GET or
5112 * HEAD on the actual directory. We must convert the request to run on the
5113 * suffix object instead!
5114 */
5115 RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
5116 op->set_get_data(get_data);
5117 return op;
5118 }
5119
5120
5121 namespace rgw::auth::s3 {
5122
5123 static rgw::auth::Completer::cmplptr_t
5124 null_completer_factory(const boost::optional<std::string>& secret_key)
5125 {
5126 return nullptr;
5127 }
5128
5129
5130 AWSEngine::VersionAbstractor::auth_data_t
5131 AWSGeneralAbstractor::get_auth_data(const req_state* const s) const
5132 {
5133 AwsVersion version;
5134 AwsRoute route;
5135 std::tie(version, route) = discover_aws_flavour(s->info);
5136
5137 if (version == AwsVersion::V2) {
5138 return get_auth_data_v2(s);
5139 } else if (version == AwsVersion::V4) {
5140 return get_auth_data_v4(s, route == AwsRoute::QUERY_STRING);
5141 } else {
5142 /* FIXME(rzarzynski): handle anon user. */
5143 throw -EINVAL;
5144 }
5145 }
5146
5147 boost::optional<std::string>
5148 AWSGeneralAbstractor::get_v4_canonical_headers(
5149 const req_info& info,
5150 const boost::string_view& signedheaders,
5151 const bool using_qs) const
5152 {
5153 return rgw::auth::s3::get_v4_canonical_headers(info, signedheaders,
5154 using_qs, false);
5155 }
5156
5157 AWSEngine::VersionAbstractor::auth_data_t
5158 AWSGeneralAbstractor::get_auth_data_v4(const req_state* const s,
5159 const bool using_qs) const
5160 {
5161 boost::string_view access_key_id;
5162 boost::string_view signed_hdrs;
5163
5164 boost::string_view date;
5165 boost::string_view credential_scope;
5166 boost::string_view client_signature;
5167 boost::string_view session_token;
5168
5169 int ret = rgw::auth::s3::parse_v4_credentials(s->info,
5170 access_key_id,
5171 credential_scope,
5172 signed_hdrs,
5173 client_signature,
5174 date,
5175 session_token,
5176 using_qs);
5177 if (ret < 0) {
5178 throw ret;
5179 }
5180
5181 /* craft canonical headers */
5182 boost::optional<std::string> canonical_headers = \
5183 get_v4_canonical_headers(s->info, signed_hdrs, using_qs);
5184 if (canonical_headers) {
5185 using sanitize = rgw::crypt_sanitize::log_content;
5186 ldpp_dout(s, 10) << "canonical headers format = "
5187 << sanitize{*canonical_headers} << dendl;
5188 } else {
5189 throw -EPERM;
5190 }
5191
5192 bool is_non_s3_op = false;
5193 if (s->op_type == RGW_STS_GET_SESSION_TOKEN ||
5194 s->op_type == RGW_STS_ASSUME_ROLE ||
5195 s->op_type == RGW_STS_ASSUME_ROLE_WEB_IDENTITY ||
5196 s->op_type == RGW_OP_CREATE_ROLE ||
5197 s->op_type == RGW_OP_DELETE_ROLE ||
5198 s->op_type == RGW_OP_GET_ROLE ||
5199 s->op_type == RGW_OP_MODIFY_ROLE ||
5200 s->op_type == RGW_OP_LIST_ROLES ||
5201 s->op_type == RGW_OP_PUT_ROLE_POLICY ||
5202 s->op_type == RGW_OP_GET_ROLE_POLICY ||
5203 s->op_type == RGW_OP_LIST_ROLE_POLICIES ||
5204 s->op_type == RGW_OP_DELETE_ROLE_POLICY ||
5205 s->op_type == RGW_OP_PUT_USER_POLICY ||
5206 s->op_type == RGW_OP_GET_USER_POLICY ||
5207 s->op_type == RGW_OP_LIST_USER_POLICIES ||
5208 s->op_type == RGW_OP_DELETE_USER_POLICY) {
5209 is_non_s3_op = true;
5210 }
5211
5212 const char* exp_payload_hash = nullptr;
5213 string payload_hash;
5214 if (is_non_s3_op) {
5215 //For non s3 ops, we need to calculate the payload hash
5216 payload_hash = s->info.args.get("PayloadHash");
5217 exp_payload_hash = payload_hash.c_str();
5218 } else {
5219 /* Get the expected hash. */
5220 exp_payload_hash = rgw::auth::s3::get_v4_exp_payload_hash(s->info);
5221 }
5222
5223 /* Craft canonical URI. Using std::move later so let it be non-const. */
5224 auto canonical_uri = rgw::auth::s3::get_v4_canonical_uri(s->info);
5225
5226 /* Craft canonical query string. std::moving later so non-const here. */
5227 auto canonical_qs = rgw::auth::s3::get_v4_canonical_qs(s->info, using_qs);
5228
5229 /* Craft canonical request. */
5230 auto canonical_req_hash = \
5231 rgw::auth::s3::get_v4_canon_req_hash(s->cct,
5232 s->info.method,
5233 std::move(canonical_uri),
5234 std::move(canonical_qs),
5235 std::move(*canonical_headers),
5236 signed_hdrs,
5237 exp_payload_hash);
5238
5239 auto string_to_sign = \
5240 rgw::auth::s3::get_v4_string_to_sign(s->cct,
5241 AWS4_HMAC_SHA256_STR,
5242 date,
5243 credential_scope,
5244 std::move(canonical_req_hash));
5245
5246 const auto sig_factory = std::bind(rgw::auth::s3::get_v4_signature,
5247 credential_scope,
5248 std::placeholders::_1,
5249 std::placeholders::_2,
5250 std::placeholders::_3);
5251
5252 /* Requests authenticated with the Query Parameters are treated as unsigned.
5253 * From "Authenticating Requests: Using Query Parameters (AWS Signature
5254 * Version 4)":
5255 *
5256 * You don't include a payload hash in the Canonical Request, because
5257 * when you create a presigned URL, you don't know the payload content
5258 * because the URL is used to upload an arbitrary payload. Instead, you
5259 * use a constant string UNSIGNED-PAYLOAD.
5260 *
5261 * This means we have absolutely no business in spawning completer. Both
5262 * aws4_auth_needs_complete and aws4_auth_streaming_mode are set to false
5263 * by default. We don't need to change that. */
5264 if (is_v4_payload_unsigned(exp_payload_hash) || is_v4_payload_empty(s) || is_non_s3_op) {
5265 return {
5266 access_key_id,
5267 client_signature,
5268 session_token,
5269 std::move(string_to_sign),
5270 sig_factory,
5271 null_completer_factory
5272 };
5273 } else {
5274 /* We're going to handle a signed payload. Be aware that even empty HTTP
5275 * body (no payload) requires verification:
5276 *
5277 * The x-amz-content-sha256 header is required for all AWS Signature
5278 * Version 4 requests. It provides a hash of the request payload. If
5279 * there is no payload, you must provide the hash of an empty string. */
5280 if (!is_v4_payload_streamed(exp_payload_hash)) {
5281 ldpp_dout(s, 10) << "delaying v4 auth" << dendl;
5282
5283 /* payload in a single chunk */
5284 switch (s->op_type)
5285 {
5286 case RGW_OP_CREATE_BUCKET:
5287 case RGW_OP_PUT_OBJ:
5288 case RGW_OP_PUT_ACLS:
5289 case RGW_OP_PUT_CORS:
5290 case RGW_OP_INIT_MULTIPART: // in case that Init Multipart uses CHUNK encoding
5291 case RGW_OP_COMPLETE_MULTIPART:
5292 case RGW_OP_SET_BUCKET_VERSIONING:
5293 case RGW_OP_DELETE_MULTI_OBJ:
5294 case RGW_OP_ADMIN_SET_METADATA:
5295 case RGW_OP_SET_BUCKET_WEBSITE:
5296 case RGW_OP_PUT_BUCKET_POLICY:
5297 case RGW_OP_PUT_OBJ_TAGGING:
5298 case RGW_OP_PUT_BUCKET_TAGGING:
5299 case RGW_OP_PUT_BUCKET_REPLICATION:
5300 case RGW_OP_PUT_LC:
5301 case RGW_OP_SET_REQUEST_PAYMENT:
5302 case RGW_OP_PUBSUB_NOTIF_CREATE:
5303 case RGW_OP_PUT_BUCKET_OBJ_LOCK:
5304 case RGW_OP_PUT_OBJ_RETENTION:
5305 case RGW_OP_PUT_OBJ_LEGAL_HOLD:
5306 case RGW_STS_GET_SESSION_TOKEN:
5307 case RGW_STS_ASSUME_ROLE:
5308 case RGW_OP_PUT_BUCKET_PUBLIC_ACCESS_BLOCK:
5309 case RGW_OP_GET_BUCKET_PUBLIC_ACCESS_BLOCK:
5310 case RGW_OP_DELETE_BUCKET_PUBLIC_ACCESS_BLOCK:
5311 break;
5312 default:
5313 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl;
5314 throw -ERR_NOT_IMPLEMENTED;
5315 }
5316
5317 const auto cmpl_factory = std::bind(AWSv4ComplSingle::create,
5318 s,
5319 std::placeholders::_1);
5320 return {
5321 access_key_id,
5322 client_signature,
5323 session_token,
5324 std::move(string_to_sign),
5325 sig_factory,
5326 cmpl_factory
5327 };
5328 } else {
5329 /* IMHO "streamed" doesn't fit too good here. I would prefer to call
5330 * it "chunked" but let's be coherent with Amazon's terminology. */
5331
5332 dout(10) << "body content detected in multiple chunks" << dendl;
5333
5334 /* payload in multiple chunks */
5335
5336 switch(s->op_type)
5337 {
5338 case RGW_OP_PUT_OBJ:
5339 break;
5340 default:
5341 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED (streaming mode)" << dendl;
5342 throw -ERR_NOT_IMPLEMENTED;
5343 }
5344
5345 dout(10) << "aws4 seed signature ok... delaying v4 auth" << dendl;
5346
5347 /* In the case of streamed payload client sets the x-amz-content-sha256
5348 * to "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" but uses "UNSIGNED-PAYLOAD"
5349 * when constructing the Canonical Request. */
5350
5351 /* In the case of single-chunk upload client set the header's value is
5352 * coherent with the one used for Canonical Request crafting. */
5353
5354 /* In the case of query string-based authentication there should be no
5355 * x-amz-content-sha256 header and the value "UNSIGNED-PAYLOAD" is used
5356 * for CanonReq. */
5357 const auto cmpl_factory = std::bind(AWSv4ComplMulti::create,
5358 s,
5359 date,
5360 credential_scope,
5361 client_signature,
5362 std::placeholders::_1);
5363 return {
5364 access_key_id,
5365 client_signature,
5366 session_token,
5367 std::move(string_to_sign),
5368 sig_factory,
5369 cmpl_factory
5370 };
5371 }
5372 }
5373 }
5374
5375
5376 boost::optional<std::string>
5377 AWSGeneralBoto2Abstractor::get_v4_canonical_headers(
5378 const req_info& info,
5379 const boost::string_view& signedheaders,
5380 const bool using_qs) const
5381 {
5382 return rgw::auth::s3::get_v4_canonical_headers(info, signedheaders,
5383 using_qs, true);
5384 }
5385
5386
5387 AWSEngine::VersionAbstractor::auth_data_t
5388 AWSGeneralAbstractor::get_auth_data_v2(const req_state* const s) const
5389 {
5390 boost::string_view access_key_id;
5391 boost::string_view signature;
5392 boost::string_view session_token;
5393 bool qsr = false;
5394
5395 const char* http_auth = s->info.env->get("HTTP_AUTHORIZATION");
5396 if (! http_auth || http_auth[0] == '\0') {
5397 /* Credentials are provided in query string. We also need to verify
5398 * the "Expires" parameter now. */
5399 access_key_id = s->info.args.get("AWSAccessKeyId");
5400 signature = s->info.args.get("Signature");
5401 qsr = true;
5402
5403 boost::string_view expires = s->info.args.get("Expires");
5404 if (expires.empty()) {
5405 throw -EPERM;
5406 }
5407
5408 /* It looks we have the guarantee that expires is a null-terminated,
5409 * and thus string_view::data() can be safely used. */
5410 const time_t exp = atoll(expires.data());
5411 time_t now;
5412 time(&now);
5413
5414 if (now >= exp) {
5415 throw -EPERM;
5416 }
5417 if (s->info.args.exists("X-Amz-Security-Token")) {
5418 session_token = s->info.args.get("X-Amz-Security-Token");
5419 if (session_token.size() == 0) {
5420 throw -EPERM;
5421 }
5422 }
5423
5424 } else {
5425 /* The "Authorization" HTTP header is being used. */
5426 const boost::string_view auth_str(http_auth + strlen("AWS "));
5427 const size_t pos = auth_str.rfind(':');
5428 if (pos != boost::string_view::npos) {
5429 access_key_id = auth_str.substr(0, pos);
5430 signature = auth_str.substr(pos + 1);
5431 }
5432
5433 if (s->info.env->exists("HTTP_X_AMZ_SECURITY_TOKEN")) {
5434 session_token = s->info.env->get("HTTP_X_AMZ_SECURITY_TOKEN");
5435 if (session_token.size() == 0) {
5436 throw -EPERM;
5437 }
5438 }
5439 }
5440
5441 /* Let's canonize the HTTP headers that are covered by the AWS auth v2. */
5442 std::string string_to_sign;
5443 utime_t header_time;
5444 if (! rgw_create_s3_canonical_header(s->info, &header_time, string_to_sign,
5445 qsr)) {
5446 ldpp_dout(s, 10) << "failed to create the canonized auth header\n"
5447 << rgw::crypt_sanitize::auth{s,string_to_sign} << dendl;
5448 throw -EPERM;
5449 }
5450
5451 ldpp_dout(s, 10) << "string_to_sign:\n"
5452 << rgw::crypt_sanitize::auth{s,string_to_sign} << dendl;
5453
5454 if (!qsr && !is_time_skew_ok(header_time)) {
5455 throw -ERR_REQUEST_TIME_SKEWED;
5456 }
5457
5458 return {
5459 std::move(access_key_id),
5460 std::move(signature),
5461 std::move(session_token),
5462 std::move(string_to_sign),
5463 rgw::auth::s3::get_v2_signature,
5464 null_completer_factory
5465 };
5466 }
5467
5468
5469 AWSEngine::VersionAbstractor::auth_data_t
5470 AWSBrowserUploadAbstractor::get_auth_data_v2(const req_state* const s) const
5471 {
5472 return {
5473 s->auth.s3_postobj_creds.access_key,
5474 s->auth.s3_postobj_creds.signature,
5475 s->auth.s3_postobj_creds.x_amz_security_token,
5476 s->auth.s3_postobj_creds.encoded_policy.to_str(),
5477 rgw::auth::s3::get_v2_signature,
5478 null_completer_factory
5479 };
5480 }
5481
5482 AWSEngine::VersionAbstractor::auth_data_t
5483 AWSBrowserUploadAbstractor::get_auth_data_v4(const req_state* const s) const
5484 {
5485 const boost::string_view credential = s->auth.s3_postobj_creds.x_amz_credential;
5486
5487 /* grab access key id */
5488 const size_t pos = credential.find("/");
5489 const boost::string_view access_key_id = credential.substr(0, pos);
5490 dout(10) << "access key id = " << access_key_id << dendl;
5491
5492 /* grab credential scope */
5493 const boost::string_view credential_scope = credential.substr(pos + 1);
5494 dout(10) << "credential scope = " << credential_scope << dendl;
5495
5496 const auto sig_factory = std::bind(rgw::auth::s3::get_v4_signature,
5497 credential_scope,
5498 std::placeholders::_1,
5499 std::placeholders::_2,
5500 std::placeholders::_3);
5501
5502 return {
5503 access_key_id,
5504 s->auth.s3_postobj_creds.signature,
5505 s->auth.s3_postobj_creds.x_amz_security_token,
5506 s->auth.s3_postobj_creds.encoded_policy.to_str(),
5507 sig_factory,
5508 null_completer_factory
5509 };
5510 }
5511
5512 AWSEngine::VersionAbstractor::auth_data_t
5513 AWSBrowserUploadAbstractor::get_auth_data(const req_state* const s) const
5514 {
5515 if (s->auth.s3_postobj_creds.x_amz_algorithm == AWS4_HMAC_SHA256_STR) {
5516 ldpp_dout(s, 0) << "Signature verification algorithm AWS v4"
5517 << " (AWS4-HMAC-SHA256)" << dendl;
5518 return get_auth_data_v4(s);
5519 } else {
5520 ldpp_dout(s, 0) << "Signature verification algorithm AWS v2" << dendl;
5521 return get_auth_data_v2(s);
5522 }
5523 }
5524
5525 AWSEngine::result_t
5526 AWSEngine::authenticate(const DoutPrefixProvider* dpp, const req_state* const s) const
5527 {
5528 /* Small reminder: an ver_abstractor is allowed to throw! */
5529 const auto auth_data = ver_abstractor.get_auth_data(s);
5530
5531 if (auth_data.access_key_id.empty() || auth_data.client_signature.empty()) {
5532 return result_t::deny(-EINVAL);
5533 } else {
5534 return authenticate(dpp,
5535 auth_data.access_key_id,
5536 auth_data.client_signature,
5537 auth_data.session_token,
5538 auth_data.string_to_sign,
5539 auth_data.signature_factory,
5540 auth_data.completer_factory,
5541 s);
5542 }
5543 }
5544
5545 } // namespace rgw::auth::s3
5546
5547 rgw::LDAPHelper* rgw::auth::s3::LDAPEngine::ldh = nullptr;
5548 std::mutex rgw::auth::s3::LDAPEngine::mtx;
5549
5550 void rgw::auth::s3::LDAPEngine::init(CephContext* const cct)
5551 {
5552 if (! cct->_conf->rgw_s3_auth_use_ldap ||
5553 cct->_conf->rgw_ldap_uri.empty()) {
5554 return;
5555 }
5556
5557 if (! ldh) {
5558 std::lock_guard<std::mutex> lck(mtx);
5559 if (! ldh) {
5560 const string& ldap_uri = cct->_conf->rgw_ldap_uri;
5561 const string& ldap_binddn = cct->_conf->rgw_ldap_binddn;
5562 const string& ldap_searchdn = cct->_conf->rgw_ldap_searchdn;
5563 const string& ldap_searchfilter = cct->_conf->rgw_ldap_searchfilter;
5564 const string& ldap_dnattr = cct->_conf->rgw_ldap_dnattr;
5565 std::string ldap_bindpw = parse_rgw_ldap_bindpw(cct);
5566
5567 ldh = new rgw::LDAPHelper(ldap_uri, ldap_binddn, ldap_bindpw,
5568 ldap_searchdn, ldap_searchfilter, ldap_dnattr);
5569
5570 ldh->init();
5571 ldh->bind();
5572 }
5573 }
5574 }
5575
5576 bool rgw::auth::s3::LDAPEngine::valid() {
5577 std::lock_guard<std::mutex> lck(mtx);
5578 return (!!ldh);
5579 }
5580
5581 rgw::auth::RemoteApplier::acl_strategy_t
5582 rgw::auth::s3::LDAPEngine::get_acl_strategy() const
5583 {
5584 //This is based on the assumption that the default acl strategy in
5585 // get_perms_from_aclspec, will take care. Extra acl spec is not required.
5586 return nullptr;
5587 }
5588
5589 rgw::auth::RemoteApplier::AuthInfo
5590 rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken& token) const noexcept
5591 {
5592 /* The short form of "using" can't be used here -- we're aliasing a class'
5593 * member. */
5594 using acct_privilege_t = \
5595 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t;
5596
5597 return rgw::auth::RemoteApplier::AuthInfo {
5598 rgw_user(token.id),
5599 token.id,
5600 RGW_PERM_FULL_CONTROL,
5601 acct_privilege_t::IS_PLAIN_ACCT,
5602 TYPE_LDAP
5603 };
5604 }
5605
5606 rgw::auth::Engine::result_t
5607 rgw::auth::s3::LDAPEngine::authenticate(
5608 const DoutPrefixProvider* dpp,
5609 const boost::string_view& access_key_id,
5610 const boost::string_view& signature,
5611 const boost::string_view& session_token,
5612 const string_to_sign_t& string_to_sign,
5613 const signature_factory_t&,
5614 const completer_factory_t& completer_factory,
5615 const req_state* const s) const
5616 {
5617 /* boost filters and/or string_ref may throw on invalid input */
5618 rgw::RGWToken base64_token;
5619 try {
5620 base64_token = rgw::from_base64(access_key_id);
5621 } catch (...) {
5622 base64_token = std::string("");
5623 }
5624
5625 if (! base64_token.valid()) {
5626 return result_t::deny();
5627 }
5628
5629 //TODO: Uncomment, when we have a migration plan in place.
5630 //Check if a user of type other than 'ldap' is already present, if yes, then
5631 //return error.
5632 /*RGWUserInfo user_info;
5633 user_info.user_id = base64_token.id;
5634 if (rgw_get_user_info_by_uid(store, user_info.user_id, user_info) >= 0) {
5635 if (user_info.type != TYPE_LDAP) {
5636 ldpp_dout(dpp, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl;
5637 return nullptr;
5638 }
5639 }*/
5640
5641 if (ldh->auth(base64_token.id, base64_token.key) != 0) {
5642 return result_t::deny(-ERR_INVALID_ACCESS_KEY);
5643 }
5644
5645 auto apl = apl_factory->create_apl_remote(cct, s, get_acl_strategy(),
5646 get_creds_info(base64_token));
5647 return result_t::grant(std::move(apl), completer_factory(boost::none));
5648 } /* rgw::auth::s3::LDAPEngine::authenticate */
5649
5650 void rgw::auth::s3::LDAPEngine::shutdown() {
5651 if (ldh) {
5652 delete ldh;
5653 ldh = nullptr;
5654 }
5655 }
5656
5657 /* LocalEngine */
5658 rgw::auth::Engine::result_t
5659 rgw::auth::s3::LocalEngine::authenticate(
5660 const DoutPrefixProvider* dpp,
5661 const boost::string_view& _access_key_id,
5662 const boost::string_view& signature,
5663 const boost::string_view& session_token,
5664 const string_to_sign_t& string_to_sign,
5665 const signature_factory_t& signature_factory,
5666 const completer_factory_t& completer_factory,
5667 const req_state* const s) const
5668 {
5669 /* get the user info */
5670 RGWUserInfo user_info;
5671 /* TODO(rzarzynski): we need to have string-view taking variant. */
5672 const std::string access_key_id = _access_key_id.to_string();
5673 if (rgw_get_user_info_by_access_key(ctl->user, access_key_id, user_info) < 0) {
5674 ldpp_dout(dpp, 5) << "error reading user info, uid=" << access_key_id
5675 << " can't authenticate" << dendl;
5676 return result_t::deny(-ERR_INVALID_ACCESS_KEY);
5677 }
5678 //TODO: Uncomment, when we have a migration plan in place.
5679 /*else {
5680 if (s->user->type != TYPE_RGW) {
5681 ldpp_dout(dpp, 10) << "ERROR: User id of type: " << s->user->type
5682 << " is present" << dendl;
5683 throw -EPERM;
5684 }
5685 }*/
5686
5687 const auto iter = user_info.access_keys.find(access_key_id);
5688 if (iter == std::end(user_info.access_keys)) {
5689 ldpp_dout(dpp, 0) << "ERROR: access key not encoded in user info" << dendl;
5690 return result_t::deny(-EPERM);
5691 }
5692 const RGWAccessKey& k = iter->second;
5693
5694 const VersionAbstractor::server_signature_t server_signature = \
5695 signature_factory(cct, k.key, string_to_sign);
5696 auto compare = signature.compare(server_signature);
5697
5698 ldpp_dout(dpp, 15) << "string_to_sign="
5699 << rgw::crypt_sanitize::log_content{string_to_sign}
5700 << dendl;
5701 ldpp_dout(dpp, 15) << "server signature=" << server_signature << dendl;
5702 ldpp_dout(dpp, 15) << "client signature=" << signature << dendl;
5703 ldpp_dout(dpp, 15) << "compare=" << compare << dendl;
5704
5705 if (compare != 0) {
5706 return result_t::deny(-ERR_SIGNATURE_NO_MATCH);
5707 }
5708
5709 auto apl = apl_factory->create_apl_local(cct, s, user_info, k.subuser, boost::none);
5710 return result_t::grant(std::move(apl), completer_factory(k.key));
5711 }
5712
5713 rgw::auth::RemoteApplier::AuthInfo
5714 rgw::auth::s3::STSEngine::get_creds_info(const STS::SessionToken& token) const noexcept
5715 {
5716 using acct_privilege_t = \
5717 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t;
5718
5719 return rgw::auth::RemoteApplier::AuthInfo {
5720 token.user,
5721 token.acct_name,
5722 token.perm_mask,
5723 (token.is_admin) ? acct_privilege_t::IS_ADMIN_ACCT: acct_privilege_t::IS_PLAIN_ACCT,
5724 token.acct_type
5725 };
5726 }
5727
5728 int
5729 rgw::auth::s3::STSEngine::get_session_token(const DoutPrefixProvider* dpp, const boost::string_view& session_token,
5730 STS::SessionToken& token) const
5731 {
5732 string decodedSessionToken;
5733 try {
5734 decodedSessionToken = rgw::from_base64(session_token);
5735 } catch (...) {
5736 ldpp_dout(dpp, 0) << "ERROR: Invalid session token, not base64 encoded." << dendl;
5737 return -EINVAL;
5738 }
5739
5740 auto* cryptohandler = cct->get_crypto_handler(CEPH_CRYPTO_AES);
5741 if (! cryptohandler) {
5742 return -EINVAL;
5743 }
5744 string secret_s = cct->_conf->rgw_sts_key;
5745 buffer::ptr secret(secret_s.c_str(), secret_s.length());
5746 int ret = 0;
5747 if (ret = cryptohandler->validate_secret(secret); ret < 0) {
5748 ldpp_dout(dpp, 0) << "ERROR: Invalid secret key" << dendl;
5749 return -EINVAL;
5750 }
5751 string error;
5752 auto* keyhandler = cryptohandler->get_key_handler(secret, error);
5753 if (! keyhandler) {
5754 return -EINVAL;
5755 }
5756 error.clear();
5757
5758 string decrypted_str;
5759 buffer::list en_input, dec_output;
5760 en_input = buffer::list::static_from_string(decodedSessionToken);
5761
5762 ret = keyhandler->decrypt(en_input, dec_output, &error);
5763 if (ret < 0) {
5764 ldpp_dout(dpp, 0) << "ERROR: Decryption failed: " << error << dendl;
5765 return -EPERM;
5766 } else {
5767 try {
5768 dec_output.append('\0');
5769 auto iter = dec_output.cbegin();
5770 decode(token, iter);
5771 } catch (const buffer::error& e) {
5772 ldout(cct, 0) << "ERROR: decode SessionToken failed: " << error << dendl;
5773 return -EINVAL;
5774 }
5775 }
5776 return 0;
5777 }
5778
5779 rgw::auth::Engine::result_t
5780 rgw::auth::s3::STSEngine::authenticate(
5781 const DoutPrefixProvider* dpp,
5782 const boost::string_view& _access_key_id,
5783 const boost::string_view& signature,
5784 const boost::string_view& session_token,
5785 const string_to_sign_t& string_to_sign,
5786 const signature_factory_t& signature_factory,
5787 const completer_factory_t& completer_factory,
5788 const req_state* const s) const
5789 {
5790 if (! s->info.args.exists("X-Amz-Security-Token") &&
5791 ! s->info.env->exists("HTTP_X_AMZ_SECURITY_TOKEN") &&
5792 s->auth.s3_postobj_creds.x_amz_security_token.empty()) {
5793 return result_t::deny();
5794 }
5795
5796 STS::SessionToken token;
5797 if (int ret = get_session_token(dpp, session_token, token); ret < 0) {
5798 return result_t::reject(ret);
5799 }
5800 //Authentication
5801 //Check if access key is not the same passed in by client
5802 if (token.access_key_id != _access_key_id) {
5803 ldpp_dout(dpp, 0) << "Invalid access key" << dendl;
5804 return result_t::reject(-EPERM);
5805 }
5806 //Check if the token has expired
5807 if (! token.expiration.empty()) {
5808 std::string expiration = token.expiration;
5809 if (! expiration.empty()) {
5810 boost::optional<real_clock::time_point> exp = ceph::from_iso_8601(expiration, false);
5811 if (exp) {
5812 real_clock::time_point now = real_clock::now();
5813 if (now >= *exp) {
5814 ldpp_dout(dpp, 0) << "ERROR: Token expired" << dendl;
5815 return result_t::reject(-EPERM);
5816 }
5817 } else {
5818 ldpp_dout(dpp, 0) << "ERROR: Invalid expiration: " << expiration << dendl;
5819 return result_t::reject(-EPERM);
5820 }
5821 }
5822 }
5823 //Check for signature mismatch
5824 const VersionAbstractor::server_signature_t server_signature = \
5825 signature_factory(cct, token.secret_access_key, string_to_sign);
5826 auto compare = signature.compare(server_signature);
5827
5828 ldpp_dout(dpp, 15) << "string_to_sign="
5829 << rgw::crypt_sanitize::log_content{string_to_sign}
5830 << dendl;
5831 ldpp_dout(dpp, 15) << "server signature=" << server_signature << dendl;
5832 ldpp_dout(dpp, 15) << "client signature=" << signature << dendl;
5833 ldpp_dout(dpp, 15) << "compare=" << compare << dendl;
5834
5835 if (compare != 0) {
5836 return result_t::reject(-ERR_SIGNATURE_NO_MATCH);
5837 }
5838
5839 // Get all the authorization info
5840 RGWUserInfo user_info;
5841 rgw_user user_id;
5842 vector<string> role_policies;
5843 string role_name;
5844 if (! token.roleId.empty()) {
5845 RGWRole role(s->cct, ctl, token.roleId);
5846 if (role.get_by_id() < 0) {
5847 return result_t::deny(-EPERM);
5848 }
5849 vector<string> role_policy_names = role.get_role_policy_names();
5850 for (auto& policy_name : role_policy_names) {
5851 string perm_policy;
5852 if (int ret = role.get_role_policy(policy_name, perm_policy); ret == 0) {
5853 role_policies.push_back(std::move(perm_policy));
5854 }
5855 }
5856 if (! token.policy.empty()) {
5857 role_policies.push_back(std::move(token.policy));
5858 }
5859 // This is mostly needed to assign the owner of a bucket during its creation
5860 user_id = token.user;
5861 role_name = role.get_name();
5862 }
5863
5864 if (! token.user.empty() && token.acct_type != TYPE_ROLE) {
5865 // get user info
5866 int ret = rgw_get_user_info_by_uid(ctl->user, token.user, user_info, NULL);
5867 if (ret < 0) {
5868 ldpp_dout(dpp, 5) << "ERROR: failed reading user info: uid=" << token.user << dendl;
5869 return result_t::reject(-EPERM);
5870 }
5871 }
5872
5873 if (token.acct_type == TYPE_KEYSTONE || token.acct_type == TYPE_LDAP) {
5874 auto apl = remote_apl_factory->create_apl_remote(cct, s, get_acl_strategy(),
5875 get_creds_info(token));
5876 return result_t::grant(std::move(apl), completer_factory(boost::none));
5877 } else if (token.acct_type == TYPE_ROLE) {
5878 auto apl = role_apl_factory->create_apl_role(cct, s, role_name, user_id, role_policies);
5879 return result_t::grant(std::move(apl), completer_factory(token.secret_access_key));
5880 } else { // This is for all local users of type TYPE_RGW or TYPE_NONE
5881 string subuser;
5882 auto apl = local_apl_factory->create_apl_local(cct, s, user_info, subuser, token.perm_mask);
5883 return result_t::grant(std::move(apl), completer_factory(token.secret_access_key));
5884 }
5885 }
5886
5887 bool rgw::auth::s3::S3AnonymousEngine::is_applicable(
5888 const req_state* s
5889 ) const noexcept {
5890 if (s->op == OP_OPTIONS) {
5891 return true;
5892 }
5893
5894 AwsVersion version;
5895 AwsRoute route;
5896 std::tie(version, route) = discover_aws_flavour(s->info);
5897
5898 return route == AwsRoute::QUERY_STRING && version == AwsVersion::UNKNOWN;
5899 }