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