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