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