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