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