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