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