]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_rest_s3.cc
update sources to v12.2.3
[ceph.git] / ceph / src / rgw / rgw_rest_s3.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
4#include <errno.h>
5#include <array>
6#include <string.h>
7
8#include "common/ceph_crypto.h"
9#include "common/Formatter.h"
10#include "common/utf8.h"
11#include "common/ceph_json.h"
12#include "common/safe_io.h"
224ce89b 13#include "common/backport14.h"
7c673cae
FG
14#include <boost/algorithm/string.hpp>
15#include <boost/algorithm/string/replace.hpp>
31f18b77 16#include <boost/utility/string_view.hpp>
7c673cae
FG
17
18#include "rgw_rest.h"
19#include "rgw_rest_s3.h"
20#include "rgw_rest_s3website.h"
21#include "rgw_auth_s3.h"
22#include "rgw_acl.h"
23#include "rgw_policy_s3.h"
24#include "rgw_user.h"
25#include "rgw_cors.h"
26#include "rgw_cors_s3.h"
224ce89b 27#include "rgw_tag_s3.h"
7c673cae
FG
28
29#include "rgw_client_io.h"
30
31#include "rgw_keystone.h"
32#include "rgw_auth_keystone.h"
33#include "rgw_auth_registry.h"
34
31f18b77
FG
35#include "rgw_es_query.h"
36
7c673cae
FG
37#include <typeinfo> // for 'typeid'
38
39#include "rgw_ldap.h"
40#include "rgw_token.h"
41#include "rgw_rest_role.h"
42#include "rgw_crypt.h"
43#include "rgw_crypt_sanitize.h"
44
45#include "include/assert.h"
46
47#define dout_context g_ceph_context
48#define dout_subsys ceph_subsys_rgw
49
50using namespace rgw;
51using namespace ceph::crypto;
52
53using std::get;
54
55void list_all_buckets_start(struct req_state *s)
56{
57 s->formatter->open_array_section_in_ns("ListAllMyBucketsResult", XMLNS_AWS_S3);
58}
59
60void list_all_buckets_end(struct req_state *s)
61{
62 s->formatter->close_section();
63}
64
65void dump_bucket(struct req_state *s, RGWBucketEnt& obj)
66{
67 s->formatter->open_object_section("Bucket");
68 s->formatter->dump_string("Name", obj.bucket.name);
69 dump_time(s, "CreationDate", &obj.creation_time);
70 s->formatter->close_section();
71}
72
31f18b77 73void rgw_get_errno_s3(rgw_http_error *e , int err_no)
7c673cae 74{
31f18b77 75 rgw_http_errors::const_iterator r = rgw_http_s3_errors.find(err_no);
7c673cae 76
31f18b77
FG
77 if (r != rgw_http_s3_errors.end()) {
78 e->http_ret = r->second.first;
79 e->s3_code = r->second.second;
7c673cae
FG
80 } else {
81 e->http_ret = 500;
82 e->s3_code = "UnknownError";
83 }
84}
85
86struct response_attr_param {
87 const char *param;
88 const char *http_attr;
89};
90
91static struct response_attr_param resp_attr_params[] = {
92 {"response-content-type", "Content-Type"},
93 {"response-content-language", "Content-Language"},
94 {"response-expires", "Expires"},
95 {"response-cache-control", "Cache-Control"},
96 {"response-content-disposition", "Content-Disposition"},
97 {"response-content-encoding", "Content-Encoding"},
98 {NULL, NULL},
99};
100
101int RGWGetObj_ObjStore_S3Website::send_response_data(bufferlist& bl, off_t bl_ofs, off_t bl_len) {
102 map<string, bufferlist>::iterator iter;
103 iter = attrs.find(RGW_ATTR_AMZ_WEBSITE_REDIRECT_LOCATION);
104 if (iter != attrs.end()) {
105 bufferlist &bl = iter->second;
106 s->redirect = string(bl.c_str(), bl.length());
107 s->err.http_ret = 301;
108 ldout(s->cct, 20) << __CEPH_ASSERT_FUNCTION << " redirecting per x-amz-website-redirect-location=" << s->redirect << dendl;
109 op_ret = -ERR_WEBSITE_REDIRECT;
110 set_req_state_err(s, op_ret);
111 dump_errno(s);
112 dump_content_length(s, 0);
113 dump_redirect(s, s->redirect);
114 end_header(s, this);
115 return op_ret;
116 } else {
117 return RGWGetObj_ObjStore_S3::send_response_data(bl, bl_ofs, bl_len);
118 }
119}
120
121int RGWGetObj_ObjStore_S3Website::send_response_data_error()
122{
123 return RGWGetObj_ObjStore_S3::send_response_data_error();
124}
125
126int RGWGetObj_ObjStore_S3::get_params()
127{
128 // for multisite sync requests, only read the slo manifest itself, rather than
129 // all of the data from its parts. the parts will sync as separate objects
130 skip_manifest = s->info.args.exists(RGW_SYS_PARAM_PREFIX "sync-manifest");
131
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");
1258 copy_source = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE");
1259 copy_source_range = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE_RANGE");
1260
1261 /* handle x-amz-copy-source */
1262
1263 if (copy_source) {
224ce89b 1264 if (*copy_source == '/') ++copy_source;
7c673cae
FG
1265 copy_source_bucket_name = copy_source;
1266 pos = copy_source_bucket_name.find("/");
1267 if (pos == std::string::npos) {
1268 ret = -EINVAL;
1269 ldout(s->cct, 5) << "x-amz-copy-source bad format" << dendl;
1270 return ret;
1271 }
1272 copy_source_object_name = copy_source_bucket_name.substr(pos + 1, copy_source_bucket_name.size());
1273 copy_source_bucket_name = copy_source_bucket_name.substr(0, pos);
1274#define VERSION_ID_STR "?versionId="
1275 pos = copy_source_object_name.find(VERSION_ID_STR);
1276 if (pos == std::string::npos) {
31f18b77 1277 copy_source_object_name = url_decode(copy_source_object_name);
7c673cae
FG
1278 } else {
1279 copy_source_version_id = copy_source_object_name.substr(pos + sizeof(VERSION_ID_STR) - 1);
31f18b77 1280 copy_source_object_name = url_decode(copy_source_object_name.substr(0, pos));
7c673cae
FG
1281 }
1282 pos = copy_source_bucket_name.find(":");
1283 if (pos == std::string::npos) {
1284 copy_source_tenant_name = s->src_tenant_name;
1285 } else {
1286 copy_source_tenant_name = copy_source_bucket_name.substr(0, pos);
1287 copy_source_bucket_name = copy_source_bucket_name.substr(pos + 1, copy_source_bucket_name.size());
1288 if (copy_source_bucket_name.empty()) {
1289 ret = -EINVAL;
1290 ldout(s->cct, 5) << "source bucket name is empty" << dendl;
1291 return ret;
1292 }
1293 }
1294 ret = store->get_bucket_info(obj_ctx,
1295 copy_source_tenant_name,
1296 copy_source_bucket_name,
1297 copy_source_bucket_info,
1298 NULL, &src_attrs);
1299 if (ret < 0) {
1300 ldout(s->cct, 5) << __func__ << "(): get_bucket_info() returned ret=" << ret << dendl;
1301 return ret;
1302 }
1303
1304 /* handle x-amz-copy-source-range */
1305
1306 if (copy_source_range) {
1307 string range = copy_source_range;
1308 pos = range.find("=");
1309 if (pos == std::string::npos) {
1310 ret = -EINVAL;
1311 ldout(s->cct, 5) << "x-amz-copy-source-range bad format" << dendl;
1312 return ret;
1313 }
1314 range = range.substr(pos + 1);
1315 pos = range.find("-");
1316 if (pos == std::string::npos) {
1317 ret = -EINVAL;
1318 ldout(s->cct, 5) << "x-amz-copy-source-range bad format" << dendl;
1319 return ret;
1320 }
1321 string first = range.substr(0, pos);
1322 string last = range.substr(pos + 1);
1323 copy_source_range_fst = strtoull(first.c_str(), NULL, 10);
1324 copy_source_range_lst = strtoull(last.c_str(), NULL, 10);
1325 }
1326
1327 } /* copy_source */
1328
224ce89b
WB
1329 /* handle object tagging */
1330 auto tag_str = s->info.env->get("HTTP_X_AMZ_TAGGING");
1331 if (tag_str){
1332 obj_tags = ceph::make_unique<RGWObjTags>();
1333 ret = obj_tags->set_from_string(tag_str);
1334 if (ret < 0){
1335 ldout(s->cct,0) << "setting obj tags failed with " << ret << dendl;
1336 if (ret == -ERR_INVALID_TAG){
1337 ret = -EINVAL; //s3 returns only -EINVAL for PUT requests
1338 }
1339
1340 return ret;
1341 }
1342 }
1343
7c673cae
FG
1344 return RGWPutObj_ObjStore::get_params();
1345}
1346
7c673cae
FG
1347int RGWPutObj_ObjStore_S3::get_data(bufferlist& bl)
1348{
31f18b77
FG
1349 const int ret = RGWPutObj_ObjStore::get_data(bl);
1350 if (ret == 0) {
1351 const int ret_auth = do_aws4_auth_completion();
7c673cae
FG
1352 if (ret_auth < 0) {
1353 return ret_auth;
1354 }
1355 }
1356
1357 return ret;
1358}
1359
1360static int get_success_retcode(int code)
1361{
1362 switch (code) {
1363 case 201:
1364 return STATUS_CREATED;
1365 case 204:
1366 return STATUS_NO_CONTENT;
1367 }
1368 return 0;
1369}
1370
1371void RGWPutObj_ObjStore_S3::send_response()
1372{
1373 if (op_ret) {
1374 set_req_state_err(s, op_ret);
1375 dump_errno(s);
1376 } else {
1377 if (s->cct->_conf->rgw_s3_success_create_obj_status) {
1378 op_ret = get_success_retcode(
1379 s->cct->_conf->rgw_s3_success_create_obj_status);
1380 set_req_state_err(s, op_ret);
1381 }
1382 if (!copy_source) {
1383 dump_errno(s);
1384 dump_etag(s, etag);
1385 dump_content_length(s, 0);
1386 for (auto &it : crypt_http_responses)
1387 dump_header(s, it.first, it.second);
1388 } else {
1389 dump_errno(s);
1390 end_header(s, this, "application/xml");
1391 dump_start(s);
1392 struct tm tmp;
1393 utime_t ut(mtime);
1394 time_t secs = (time_t)ut.sec();
1395 gmtime_r(&secs, &tmp);
1396 char buf[TIME_BUF_SIZE];
1397 s->formatter->open_object_section_in_ns("CopyPartResult",
1398 "http://s3.amazonaws.com/doc/2006-03-01/");
1399 if (strftime(buf, sizeof(buf), "%Y-%m-%dT%T.000Z", &tmp) > 0) {
1400 s->formatter->dump_string("LastModified", buf);
1401 }
1402 s->formatter->dump_string("ETag", etag);
1403 s->formatter->close_section();
1404 rgw_flush_formatter_and_reset(s, s->formatter);
1405 return;
1406 }
1407 }
1408 if (s->system_request && !real_clock::is_zero(mtime)) {
1409 dump_epoch_header(s, "Rgwx-Mtime", mtime);
1410 }
1411 end_header(s, this);
1412}
1413
1414static inline int get_obj_attrs(RGWRados *store, struct req_state *s, rgw_obj& obj, map<string, bufferlist>& attrs)
1415{
1416 RGWRados::Object op_target(store, s->bucket_info, *static_cast<RGWObjectCtx *>(s->obj_ctx), obj);
1417 RGWRados::Object::Read read_op(&op_target);
1418
1419 read_op.params.attrs = &attrs;
7c673cae
FG
1420
1421 return read_op.prepare();
1422}
1423
1424static inline void set_attr(map<string, bufferlist>& attrs, const char* key, const std::string& value)
1425{
1426 bufferlist bl;
1427 ::encode(value,bl);
1428 attrs.emplace(key, std::move(bl));
1429}
1430
1431static inline void set_attr(map<string, bufferlist>& attrs, const char* key, const char* value)
1432{
1433 bufferlist bl;
1434 ::encode(value,bl);
1435 attrs.emplace(key, std::move(bl));
1436}
1437
1438int RGWPutObj_ObjStore_S3::get_decrypt_filter(
1439 std::unique_ptr<RGWGetDataCB>* filter,
1440 RGWGetDataCB* cb,
1441 map<string, bufferlist>& attrs,
1442 bufferlist* manifest_bl)
1443{
1444 std::map<std::string, std::string> crypt_http_responses_unused;
1445
1446 int res = 0;
1447 std::unique_ptr<BlockCrypt> block_crypt;
1448 res = rgw_s3_prepare_decrypt(s, attrs, &block_crypt, crypt_http_responses_unused);
1449 if (res == 0) {
1450 if (block_crypt != nullptr) {
1451 auto f = std::unique_ptr<RGWGetObj_BlockDecrypt>(new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt)));
1452 //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
1453 if (f != nullptr) {
1454 if (manifest_bl != nullptr) {
1455 res = f->read_manifest(*manifest_bl);
1456 if (res == 0) {
1457 *filter = std::move(f);
1458 }
1459 }
1460 }
1461 }
1462 }
1463 return res;
1464}
1465
1466int RGWPutObj_ObjStore_S3::get_encrypt_filter(
1467 std::unique_ptr<RGWPutObjDataProcessor>* filter,
1468 RGWPutObjDataProcessor* cb)
1469{
1470 int res = 0;
1471 RGWPutObjProcessor_Multipart* multi_processor=dynamic_cast<RGWPutObjProcessor_Multipart*>(cb);
1472 if (multi_processor != nullptr) {
1473 RGWMPObj* mp = nullptr;
1474 multi_processor->get_mp(&mp);
1475 if (mp != nullptr) {
1476 map<string, bufferlist> xattrs;
1477 string meta_oid;
1478 meta_oid = mp->get_meta();
1479
1480 rgw_obj obj;
1481 obj.init_ns(s->bucket, meta_oid, RGW_OBJ_NS_MULTIPART);
1482 obj.set_in_extra_data(true);
1483 res = get_obj_attrs(store, s, obj, xattrs);
1484 if (res == 0) {
1485 std::unique_ptr<BlockCrypt> block_crypt;
1486 /* We are adding to existing object.
1487 * We use crypto mode that configured as if we were decrypting. */
1488 res = rgw_s3_prepare_decrypt(s, xattrs, &block_crypt, crypt_http_responses);
1489 if (res == 0 && block_crypt != nullptr)
1490 *filter = std::unique_ptr<RGWPutObj_BlockEncrypt>(
1491 new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
1492 }
1493 }
1494 /* it is ok, to not have encryption at all */
1495 }
1496 else
1497 {
1498 std::unique_ptr<BlockCrypt> block_crypt;
1499 res = rgw_s3_prepare_encrypt(s, attrs, nullptr, &block_crypt, crypt_http_responses);
1500 if (res == 0 && block_crypt != nullptr) {
1501 *filter = std::unique_ptr<RGWPutObj_BlockEncrypt>(
1502 new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
1503 }
1504 }
1505 return res;
1506}
1507
1508void RGWPostObj_ObjStore_S3::rebuild_key(string& key)
1509{
1510 static string var = "${filename}";
1511 int pos = key.find(var);
1512 if (pos < 0)
1513 return;
1514
1515 string new_key = key.substr(0, pos);
1516 new_key.append(filename);
1517 new_key.append(key.substr(pos + var.size()));
1518
1519 key = new_key;
1520}
1521
1522std::string RGWPostObj_ObjStore_S3::get_current_filename() const
1523{
1524 return s->object.name;
1525}
1526
1527std::string RGWPostObj_ObjStore_S3::get_current_content_type() const
1528{
1529 return content_type;
1530}
1531
1532int RGWPostObj_ObjStore_S3::get_params()
1533{
1534 op_ret = RGWPostObj_ObjStore::get_params();
1535 if (op_ret < 0) {
1536 return op_ret;
1537 }
1538
1539 ldout(s->cct, 20) << "adding bucket to policy env: " << s->bucket.name
1540 << dendl;
1541 env.add_var("bucket", s->bucket.name);
1542
1543 bool done;
1544 do {
1545 struct post_form_part part;
1546 int r = read_form_part_header(&part, done);
1547 if (r < 0)
1548 return r;
1549
1550 if (s->cct->_conf->subsys.should_gather(ceph_subsys_rgw, 20)) {
1551 ldout(s->cct, 20) << "read part header -- part.name="
1552 << part.name << dendl;
1553
1554 for (const auto& pair : part.fields) {
1555 ldout(s->cct, 20) << "field.name=" << pair.first << dendl;
1556 ldout(s->cct, 20) << "field.val=" << pair.second.val << dendl;
1557 ldout(s->cct, 20) << "field.params:" << dendl;
1558
1559 for (const auto& param_pair : pair.second.params) {
1560 ldout(s->cct, 20) << " " << param_pair.first
1561 << " -> " << param_pair.second << dendl;
1562 }
1563 }
1564 }
1565
1566 if (done) { /* unexpected here */
1567 err_msg = "Malformed request";
1568 return -EINVAL;
1569 }
1570
1571 if (stringcasecmp(part.name, "file") == 0) { /* beginning of data transfer */
1572 struct post_part_field& field = part.fields["Content-Disposition"];
1573 map<string, string>::iterator iter = field.params.find("filename");
1574 if (iter != field.params.end()) {
1575 filename = iter->second;
1576 }
1577 parts[part.name] = part;
1578 break;
1579 }
1580
1581 bool boundary;
1582 uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
1583 r = read_data(part.data, chunk_size, boundary, done);
224ce89b 1584 if (r < 0 || !boundary) {
7c673cae
FG
1585 err_msg = "Couldn't find boundary";
1586 return -EINVAL;
1587 }
1588 parts[part.name] = part;
1589 string part_str(part.data.c_str(), part.data.length());
1590 env.add_var(part.name, part_str);
1591 } while (!done);
1592
1593 string object_str;
1594 if (!part_str(parts, "key", &object_str)) {
1595 err_msg = "Key not specified";
1596 return -EINVAL;
1597 }
1598
1599 s->object = rgw_obj_key(object_str);
1600
1601 rebuild_key(s->object.name);
1602
1603 if (s->object.empty()) {
1604 err_msg = "Empty object name";
1605 return -EINVAL;
1606 }
1607
1608 env.add_var("key", s->object.name);
1609
1610 part_str(parts, "Content-Type", &content_type);
b32b8144
FG
1611
1612 /* AWS permits POST without Content-Type: http://tracker.ceph.com/issues/20201 */
1613 if (! content_type.empty()) {
1614 env.add_var("Content-Type", content_type);
1615 }
7c673cae
FG
1616
1617 map<string, struct post_form_part, ltstr_nocase>::iterator piter =
1618 parts.upper_bound(RGW_AMZ_META_PREFIX);
1619 for (; piter != parts.end(); ++piter) {
1620 string n = piter->first;
1621 if (strncasecmp(n.c_str(), RGW_AMZ_META_PREFIX,
1622 sizeof(RGW_AMZ_META_PREFIX) - 1) != 0)
1623 break;
1624
1625 string attr_name = RGW_ATTR_PREFIX;
1626 attr_name.append(n);
1627
1628 /* need to null terminate it */
1629 bufferlist& data = piter->second.data;
1630 string str = string(data.c_str(), data.length());
1631
1632 bufferlist attr_bl;
1633 attr_bl.append(str.c_str(), str.size() + 1);
1634
1635 attrs[attr_name] = attr_bl;
1636 }
1637 // TODO: refactor this and the above loop to share code
1638 piter = parts.find(RGW_AMZ_WEBSITE_REDIRECT_LOCATION);
1639 if (piter != parts.end()) {
1640 string n = piter->first;
1641 string attr_name = RGW_ATTR_PREFIX;
1642 attr_name.append(n);
1643 /* need to null terminate it */
1644 bufferlist& data = piter->second.data;
1645 string str = string(data.c_str(), data.length());
1646
1647 bufferlist attr_bl;
1648 attr_bl.append(str.c_str(), str.size() + 1);
1649
1650 attrs[attr_name] = attr_bl;
1651 }
1652
1653 int r = get_policy();
1654 if (r < 0)
1655 return r;
1656
224ce89b
WB
1657 r = get_tags();
1658 if (r < 0)
1659 return r;
1660
1661
7c673cae
FG
1662 min_len = post_policy.min_length;
1663 max_len = post_policy.max_length;
1664
224ce89b
WB
1665
1666
1667 return 0;
1668}
1669
1670int RGWPostObj_ObjStore_S3::get_tags()
1671{
1672 string tags_str;
1673 if (part_str(parts, "tagging", &tags_str)) {
1674 RGWObjTagsXMLParser parser;
1675 if (!parser.init()){
1676 ldout(s->cct, 0) << "Couldn't init RGWObjTags XML parser" << dendl;
1677 err_msg = "Server couldn't process the request";
1678 return -EINVAL; // TODO: This class of errors in rgw code should be a 5XX error
1679 }
1680 if (!parser.parse(tags_str.c_str(), tags_str.size(), 1)) {
1681 ldout(s->cct,0 ) << "Invalid Tagging XML" << dendl;
1682 err_msg = "Invalid Tagging XML";
1683 return -EINVAL;
1684 }
1685
1686 RGWObjTagSet_S3 *obj_tags_s3;
1687 RGWObjTagging_S3 *tagging;
1688
1689 tagging = static_cast<RGWObjTagging_S3 *>(parser.find_first("Tagging"));
1690 obj_tags_s3 = static_cast<RGWObjTagSet_S3 *>(tagging->find_first("TagSet"));
1691 if(!obj_tags_s3){
1692 return -ERR_MALFORMED_XML;
1693 }
1694
1695 RGWObjTags obj_tags;
1696 int r = obj_tags_s3->rebuild(obj_tags);
1697 if (r < 0)
1698 return r;
1699
1700 bufferlist tags_bl;
1701 obj_tags.encode(tags_bl);
1702 ldout(s->cct, 20) << "Read " << obj_tags.count() << "tags" << dendl;
1703 attrs[RGW_ATTR_TAGS] = tags_bl;
1704 }
1705
1706
7c673cae
FG
1707 return 0;
1708}
1709
1710int RGWPostObj_ObjStore_S3::get_policy()
1711{
1712 if (part_bl(parts, "policy", &s->auth.s3_postobj_creds.encoded_policy)) {
31f18b77
FG
1713 bool aws4_auth = false;
1714
1715 /* x-amz-algorithm handling */
1716 using rgw::auth::s3::AWS4_HMAC_SHA256_STR;
1717 if ((part_str(parts, "x-amz-algorithm", &s->auth.s3_postobj_creds.x_amz_algorithm)) &&
1718 (s->auth.s3_postobj_creds.x_amz_algorithm == AWS4_HMAC_SHA256_STR)) {
1719 ldout(s->cct, 0) << "Signature verification algorithm AWS v4 (AWS4-HMAC-SHA256)" << dendl;
1720 aws4_auth = true;
1721 } else {
1722 ldout(s->cct, 0) << "Signature verification algorithm AWS v2" << dendl;
7c673cae
FG
1723 }
1724
31f18b77
FG
1725 // check that the signature matches the encoded policy
1726 if (aws4_auth) {
1727 /* AWS4 */
1728
1729 /* x-amz-credential handling */
1730 if (!part_str(parts, "x-amz-credential",
1731 &s->auth.s3_postobj_creds.x_amz_credential)) {
1732 ldout(s->cct, 0) << "No S3 aws4 credential found!" << dendl;
1733 err_msg = "Missing aws4 credential";
1734 return -EINVAL;
1735 }
7c673cae 1736
31f18b77
FG
1737 /* x-amz-signature handling */
1738 if (!part_str(parts, "x-amz-signature",
1739 &s->auth.s3_postobj_creds.signature)) {
1740 ldout(s->cct, 0) << "No aws4 signature found!" << dendl;
1741 err_msg = "Missing aws4 signature";
1742 return -EINVAL;
7c673cae
FG
1743 }
1744
31f18b77
FG
1745 /* x-amz-date handling */
1746 std::string received_date_str;
1747 if (!part_str(parts, "x-amz-date", &received_date_str)) {
1748 ldout(s->cct, 0) << "No aws4 date found!" << dendl;
1749 err_msg = "Missing aws4 date";
1750 return -EINVAL;
1751 }
1752 } else {
1753 /* AWS2 */
7c673cae 1754
31f18b77
FG
1755 // check that the signature matches the encoded policy
1756 if (!part_str(parts, "AWSAccessKeyId",
1757 &s->auth.s3_postobj_creds.access_key)) {
1758 ldout(s->cct, 0) << "No S3 aws2 access key found!" << dendl;
1759 err_msg = "Missing aws2 access key";
1760 return -EINVAL;
1761 }
7c673cae 1762
31f18b77
FG
1763 if (!part_str(parts, "signature", &s->auth.s3_postobj_creds.signature)) {
1764 ldout(s->cct, 0) << "No aws2 signature found!" << dendl;
1765 err_msg = "Missing aws2 signature";
1766 return -EINVAL;
7c673cae 1767 }
7c673cae
FG
1768 }
1769
31f18b77
FG
1770 /* FIXME: this is a makeshift solution. The browser upload authentication will be
1771 * handled by an instance of rgw::auth::Completer spawned in Handler's authorize()
1772 * method. */
1773 const int ret = rgw::auth::Strategy::apply(auth_registry_ptr->get_s3_post(), s);
1774 if (ret != 0) {
1775 return -EACCES;
1776 } else {
1777 /* Populate the owner info. */
1778 s->owner.set_id(s->user->user_id);
1779 s->owner.set_name(s->user->display_name);
c07f9fc5 1780 ldout(s->cct, 20) << "Successful Signature Verification!" << dendl;
31f18b77 1781 }
7c673cae
FG
1782
1783 ceph::bufferlist decoded_policy;
1784 try {
1785 decoded_policy.decode_base64(s->auth.s3_postobj_creds.encoded_policy);
1786 } catch (buffer::error& err) {
1787 ldout(s->cct, 0) << "failed to decode_base64 policy" << dendl;
1788 err_msg = "Could not decode policy";
1789 return -EINVAL;
1790 }
1791
1792 decoded_policy.append('\0'); // NULL terminate
c07f9fc5 1793 ldout(s->cct, 20) << "POST policy: " << decoded_policy.c_str() << dendl;
7c673cae
FG
1794
1795
1796 int r = post_policy.from_json(decoded_policy, err_msg);
1797 if (r < 0) {
1798 if (err_msg.empty()) {
1799 err_msg = "Failed to parse policy";
1800 }
1801 ldout(s->cct, 0) << "failed to parse policy" << dendl;
1802 return -EINVAL;
1803 }
1804
31f18b77
FG
1805 if (aws4_auth) {
1806 /* AWS4 */
1807 post_policy.set_var_checked("x-amz-signature");
1808 } else {
1809 /* AWS2 */
1810 post_policy.set_var_checked("AWSAccessKeyId");
1811 post_policy.set_var_checked("signature");
1812 }
7c673cae 1813 post_policy.set_var_checked("policy");
7c673cae
FG
1814
1815 r = post_policy.check(&env, err_msg);
1816 if (r < 0) {
1817 if (err_msg.empty()) {
1818 err_msg = "Policy check failed";
1819 }
1820 ldout(s->cct, 0) << "policy check failed" << dendl;
1821 return r;
1822 }
1823
1824 } else {
1825 ldout(s->cct, 0) << "No attached policy found!" << dendl;
1826 }
1827
1828 string canned_acl;
1829 part_str(parts, "acl", &canned_acl);
1830
1831 RGWAccessControlPolicy_S3 s3policy(s->cct);
1832 ldout(s->cct, 20) << "canned_acl=" << canned_acl << dendl;
1833 if (s3policy.create_canned(s->owner, s->bucket_owner, canned_acl) < 0) {
1834 err_msg = "Bad canned ACLs";
1835 return -EINVAL;
1836 }
1837
1838 policy = s3policy;
1839
1840 return 0;
1841}
1842
1843int RGWPostObj_ObjStore_S3::complete_get_params()
1844{
1845 bool done;
1846 do {
1847 struct post_form_part part;
1848 int r = read_form_part_header(&part, done);
1849 if (r < 0) {
1850 return r;
1851 }
1852
1853 ceph::bufferlist part_data;
1854 bool boundary;
1855 uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
1856 r = read_data(part.data, chunk_size, boundary, done);
224ce89b 1857 if (r < 0 || !boundary) {
7c673cae
FG
1858 return -EINVAL;
1859 }
1860
1861 /* Just reading the data but not storing any results of that. */
1862 } while (!done);
1863
1864 return 0;
1865}
1866
1867int RGWPostObj_ObjStore_S3::get_data(ceph::bufferlist& bl, bool& again)
1868{
1869 bool boundary;
1870 bool done;
1871
1872 const uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
1873 int r = read_data(bl, chunk_size, boundary, done);
1874 if (r < 0) {
1875 return r;
1876 }
1877
1878 if (boundary) {
1879 if (!done) {
1880 /* Reached end of data, let's drain the rest of the params */
1881 r = complete_get_params();
1882 if (r < 0) {
1883 return r;
1884 }
1885 }
1886 }
1887
1888 again = !boundary;
1889 return bl.length();
1890}
1891
1892void RGWPostObj_ObjStore_S3::send_response()
1893{
1894 if (op_ret == 0 && parts.count("success_action_redirect")) {
1895 string redirect;
1896
1897 part_str(parts, "success_action_redirect", &redirect);
1898
1899 string tenant;
1900 string bucket;
1901 string key;
1902 string etag_str = "\"";
1903
1904 etag_str.append(etag);
1905 etag_str.append("\"");
1906
1907 string etag_url;
1908
1909 url_encode(s->bucket_tenant, tenant); /* surely overkill, but cheap */
1910 url_encode(s->bucket_name, bucket);
1911 url_encode(s->object.name, key);
1912 url_encode(etag_str, etag_url);
1913
1914 if (!s->bucket_tenant.empty()) {
1915 /*
1916 * What we really would like is to quaily the bucket name, so
1917 * that the client could simply copy it and paste into next request.
1918 * Unfortunately, in S3 we cannot know if the client will decide
1919 * to come through DNS, with "bucket.tenant" sytanx, or through
1920 * URL with "tenant\bucket" syntax. Therefore, we provide the
1921 * tenant separately.
1922 */
1923 redirect.append("?tenant=");
1924 redirect.append(tenant);
1925 redirect.append("&bucket=");
1926 redirect.append(bucket);
1927 } else {
1928 redirect.append("?bucket=");
1929 redirect.append(bucket);
1930 }
1931 redirect.append("&key=");
1932 redirect.append(key);
1933 redirect.append("&etag=");
1934 redirect.append(etag_url);
1935
1936 int r = check_utf8(redirect.c_str(), redirect.size());
1937 if (r < 0) {
1938 op_ret = r;
1939 goto done;
1940 }
1941 dump_redirect(s, redirect);
1942 op_ret = STATUS_REDIRECT;
1943 } else if (op_ret == 0 && parts.count("success_action_status")) {
1944 string status_string;
1945 uint32_t status_int;
1946
1947 part_str(parts, "success_action_status", &status_string);
1948
1949 int r = stringtoul(status_string, &status_int);
1950 if (r < 0) {
1951 op_ret = r;
1952 goto done;
1953 }
1954
1955 switch (status_int) {
1956 case 200:
1957 break;
1958 case 201:
1959 op_ret = STATUS_CREATED;
1960 break;
1961 default:
1962 op_ret = STATUS_NO_CONTENT;
1963 break;
1964 }
1965 } else if (! op_ret) {
1966 op_ret = STATUS_NO_CONTENT;
1967 }
1968
1969done:
1970 if (op_ret == STATUS_CREATED) {
1971 for (auto &it : crypt_http_responses)
1972 dump_header(s, it.first, it.second);
1973 s->formatter->open_object_section("PostResponse");
1974 if (g_conf->rgw_dns_name.length())
1975 s->formatter->dump_format("Location", "%s/%s",
1976 s->info.script_uri.c_str(),
1977 s->object.name.c_str());
1978 if (!s->bucket_tenant.empty())
1979 s->formatter->dump_string("Tenant", s->bucket_tenant);
1980 s->formatter->dump_string("Bucket", s->bucket_name);
1981 s->formatter->dump_string("Key", s->object.name);
1982 s->formatter->close_section();
1983 }
1984 s->err.message = err_msg;
1985 set_req_state_err(s, op_ret);
1986 dump_errno(s);
1987 if (op_ret >= 0) {
1988 dump_content_length(s, s->formatter->get_len());
1989 }
1990 end_header(s, this);
1991 if (op_ret != STATUS_CREATED)
1992 return;
1993
1994 rgw_flush_formatter_and_reset(s, s->formatter);
1995}
1996
1997int RGWPostObj_ObjStore_S3::get_encrypt_filter(
1998 std::unique_ptr<RGWPutObjDataProcessor>* filter, RGWPutObjDataProcessor* cb)
1999{
2000 int res = 0;
2001 std::unique_ptr<BlockCrypt> block_crypt;
2002 res = rgw_s3_prepare_encrypt(s, attrs, &parts, &block_crypt, crypt_http_responses);
2003 if (res == 0 && block_crypt != nullptr) {
2004 *filter = std::unique_ptr<RGWPutObj_BlockEncrypt>(
2005 new RGWPutObj_BlockEncrypt(s->cct, cb, std::move(block_crypt)));
2006 }
2007 else
2008 *filter = nullptr;
2009 return res;
2010}
2011
2012int RGWDeleteObj_ObjStore_S3::get_params()
2013{
2014 const char *if_unmod = s->info.env->get("HTTP_X_AMZ_DELETE_IF_UNMODIFIED_SINCE");
2015
2016 if (s->system_request) {
2017 s->info.args.get_bool(RGW_SYS_PARAM_PREFIX "no-precondition-error", &no_precondition_error, false);
2018 }
2019
2020 if (if_unmod) {
31f18b77 2021 std::string if_unmod_decoded = url_decode(if_unmod);
7c673cae
FG
2022 uint64_t epoch;
2023 uint64_t nsec;
2024 if (utime_t::parse_date(if_unmod_decoded, &epoch, &nsec) < 0) {
2025 ldout(s->cct, 10) << "failed to parse time: " << if_unmod_decoded << dendl;
2026 return -EINVAL;
2027 }
2028 unmod_since = utime_t(epoch, nsec).to_real_time();
2029 }
2030
2031 return 0;
2032}
2033
2034void RGWDeleteObj_ObjStore_S3::send_response()
2035{
2036 int r = op_ret;
2037 if (r == -ENOENT)
2038 r = 0;
2039 if (!r)
2040 r = STATUS_NO_CONTENT;
2041
2042 set_req_state_err(s, r);
2043 dump_errno(s);
2044 if (!version_id.empty()) {
2045 dump_header(s, "x-amz-version-id", version_id);
2046 }
2047 if (delete_marker) {
2048 dump_header(s, "x-amz-delete-marker", "true");
2049 }
2050 end_header(s, this);
2051}
2052
2053int RGWCopyObj_ObjStore_S3::init_dest_policy()
2054{
2055 RGWAccessControlPolicy_S3 s3policy(s->cct);
2056
2057 /* build a policy for the target object */
2058 int r = create_s3_policy(s, store, s3policy, s->owner);
2059 if (r < 0)
2060 return r;
2061
2062 dest_policy = s3policy;
2063
2064 return 0;
2065}
2066
2067int RGWCopyObj_ObjStore_S3::get_params()
2068{
2069 if_mod = s->info.env->get("HTTP_X_AMZ_COPY_IF_MODIFIED_SINCE");
2070 if_unmod = s->info.env->get("HTTP_X_AMZ_COPY_IF_UNMODIFIED_SINCE");
2071 if_match = s->info.env->get("HTTP_X_AMZ_COPY_IF_MATCH");
2072 if_nomatch = s->info.env->get("HTTP_X_AMZ_COPY_IF_NONE_MATCH");
2073
2074 src_tenant_name = s->src_tenant_name;
2075 src_bucket_name = s->src_bucket_name;
2076 src_object = s->src_object;
2077 dest_tenant_name = s->bucket.tenant;
2078 dest_bucket_name = s->bucket.name;
2079 dest_object = s->object.name;
2080
2081 if (s->system_request) {
2082 source_zone = s->info.args.get(RGW_SYS_PARAM_PREFIX "source-zone");
2083 s->info.args.get_bool(RGW_SYS_PARAM_PREFIX "copy-if-newer", &copy_if_newer, false);
2084 if (!source_zone.empty()) {
2085 client_id = s->info.args.get(RGW_SYS_PARAM_PREFIX "client-id");
2086 op_id = s->info.args.get(RGW_SYS_PARAM_PREFIX "op-id");
2087
2088 if (client_id.empty() || op_id.empty()) {
2089 ldout(s->cct, 0) <<
2090 RGW_SYS_PARAM_PREFIX "client-id or "
2091 RGW_SYS_PARAM_PREFIX "op-id were not provided, "
2092 "required for intra-region copy"
2093 << dendl;
2094 return -EINVAL;
2095 }
2096 }
2097 }
2098
2099 const char *md_directive = s->info.env->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
2100 if (md_directive) {
2101 if (strcasecmp(md_directive, "COPY") == 0) {
2102 attrs_mod = RGWRados::ATTRSMOD_NONE;
2103 } else if (strcasecmp(md_directive, "REPLACE") == 0) {
2104 attrs_mod = RGWRados::ATTRSMOD_REPLACE;
2105 } else if (!source_zone.empty()) {
2106 attrs_mod = RGWRados::ATTRSMOD_NONE; // default for intra-zone_group copy
2107 } else {
2108 ldout(s->cct, 0) << "invalid metadata directive" << dendl;
2109 return -EINVAL;
2110 }
2111 }
2112
2113 if (source_zone.empty() &&
2114 (dest_tenant_name.compare(src_tenant_name) == 0) &&
2115 (dest_bucket_name.compare(src_bucket_name) == 0) &&
2116 (dest_object.compare(src_object.name) == 0) &&
2117 src_object.instance.empty() &&
2118 (attrs_mod != RGWRados::ATTRSMOD_REPLACE)) {
2119 /* can only copy object into itself if replacing attrs */
2120 ldout(s->cct, 0) << "can't copy object into itself if not replacing attrs"
2121 << dendl;
2122 return -ERR_INVALID_REQUEST;
2123 }
2124 return 0;
2125}
2126
2127void RGWCopyObj_ObjStore_S3::send_partial_response(off_t ofs)
2128{
2129 if (! sent_header) {
2130 if (op_ret)
2131 set_req_state_err(s, op_ret);
2132 dump_errno(s);
2133
2134 end_header(s, this, "application/xml");
2135 if (op_ret == 0) {
2136 s->formatter->open_object_section_in_ns("CopyObjectResult", XMLNS_AWS_S3);
2137 }
2138 sent_header = true;
2139 } else {
2140 /* Send progress field. Note that this diverge from the original S3
2141 * spec. We do this in order to keep connection alive.
2142 */
2143 s->formatter->dump_int("Progress", (uint64_t)ofs);
2144 }
2145 rgw_flush_formatter(s, s->formatter);
2146}
2147
2148void RGWCopyObj_ObjStore_S3::send_response()
2149{
2150 if (!sent_header)
2151 send_partial_response(0);
2152
2153 if (op_ret == 0) {
2154 dump_time(s, "LastModified", &mtime);
2155 std::string etag_str = etag.to_str();
2156 if (! etag_str.empty()) {
2157 s->formatter->dump_string("ETag", std::move(etag_str));
2158 }
2159 s->formatter->close_section();
2160 rgw_flush_formatter_and_reset(s, s->formatter);
2161 }
2162}
2163
2164void RGWGetACLs_ObjStore_S3::send_response()
2165{
2166 if (op_ret)
2167 set_req_state_err(s, op_ret);
2168 dump_errno(s);
2169 end_header(s, this, "application/xml");
2170 dump_start(s);
2171 rgw_flush_formatter(s, s->formatter);
2172 dump_body(s, acls);
2173}
2174
2175int RGWPutACLs_ObjStore_S3::get_params()
2176{
2177 int ret = RGWPutACLs_ObjStore::get_params();
31f18b77
FG
2178 if (ret >= 0) {
2179 const int ret_auth = do_aws4_auth_completion();
7c673cae
FG
2180 if (ret_auth < 0) {
2181 return ret_auth;
2182 }
2183 }
2184 return ret;
2185}
2186
2187int RGWPutACLs_ObjStore_S3::get_policy_from_state(RGWRados *store,
2188 struct req_state *s,
2189 stringstream& ss)
2190{
2191 RGWAccessControlPolicy_S3 s3policy(s->cct);
2192
2193 // bucket-* canned acls do not apply to bucket
2194 if (s->object.empty()) {
2195 if (s->canned_acl.find("bucket") != string::npos)
2196 s->canned_acl.clear();
2197 }
2198
2199 int r = create_s3_policy(s, store, s3policy, owner);
2200 if (r < 0)
2201 return r;
2202
2203 s3policy.to_xml(ss);
2204
2205 return 0;
2206}
2207
2208void RGWPutACLs_ObjStore_S3::send_response()
2209{
2210 if (op_ret)
2211 set_req_state_err(s, op_ret);
2212 dump_errno(s);
2213 end_header(s, this, "application/xml");
2214 dump_start(s);
2215}
2216
2217void RGWGetLC_ObjStore_S3::execute()
2218{
2219 config.set_ctx(s->cct);
2220
2221 map<string, bufferlist>::iterator aiter = s->bucket_attrs.find(RGW_ATTR_LC);
2222 if (aiter == s->bucket_attrs.end()) {
2223 op_ret = -ENOENT;
2224 return;
2225 }
2226
2227 bufferlist::iterator iter(&aiter->second);
2228 try {
2229 config.decode(iter);
2230 } catch (const buffer::error& e) {
2231 ldout(s->cct, 0) << __func__ << "decode life cycle config failed" << dendl;
2232 op_ret = -EIO;
2233 return;
2234 }
2235}
2236
2237void RGWGetLC_ObjStore_S3::send_response()
2238{
2239 if (op_ret) {
2240 if (op_ret == -ENOENT) {
2241 set_req_state_err(s, ERR_NO_SUCH_LC);
2242 } else {
2243 set_req_state_err(s, op_ret);
2244 }
2245 }
2246 dump_errno(s);
2247 end_header(s, this, "application/xml");
2248 dump_start(s);
2249
2250 if (op_ret < 0)
2251 return;
2252
2253 config.dump_xml(s->formatter);
2254 rgw_flush_formatter_and_reset(s, s->formatter);
2255}
2256
2257void RGWPutLC_ObjStore_S3::send_response()
2258{
2259 if (op_ret)
2260 set_req_state_err(s, op_ret);
2261 dump_errno(s);
2262 end_header(s, this, "application/xml");
2263 dump_start(s);
2264}
2265
2266void RGWDeleteLC_ObjStore_S3::send_response()
2267{
2268 if (op_ret == 0)
2269 op_ret = STATUS_NO_CONTENT;
2270 if (op_ret) {
2271 set_req_state_err(s, op_ret);
2272 }
2273 dump_errno(s);
2274 end_header(s, this, "application/xml");
2275 dump_start(s);
2276}
2277
2278void RGWGetCORS_ObjStore_S3::send_response()
2279{
2280 if (op_ret) {
2281 if (op_ret == -ENOENT)
2282 set_req_state_err(s, ERR_NOT_FOUND);
2283 else
2284 set_req_state_err(s, op_ret);
2285 }
2286 dump_errno(s);
2287 end_header(s, NULL, "application/xml");
2288 dump_start(s);
2289 if (! op_ret) {
2290 string cors;
2291 RGWCORSConfiguration_S3 *s3cors =
2292 static_cast<RGWCORSConfiguration_S3 *>(&bucket_cors);
2293 stringstream ss;
2294
2295 s3cors->to_xml(ss);
2296 cors = ss.str();
2297 dump_body(s, cors);
2298 }
2299}
2300
2301int RGWPutCORS_ObjStore_S3::get_params()
2302{
2303 int r;
2304 char *data = nullptr;
2305 int len = 0;
2306 RGWCORSXMLParser_S3 parser(s->cct);
2307 RGWCORSConfiguration_S3 *cors_config;
2308
2309 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
2310 r = rgw_rest_read_all_input(s, &data, &len, max_size, false);
2311 if (r < 0) {
2312 return r;
2313 }
2314
2315 auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
2316
31f18b77
FG
2317 r = do_aws4_auth_completion();
2318 if (r < 0) {
2319 return r;
7c673cae
FG
2320 }
2321
2322 if (!parser.init()) {
2323 return -EINVAL;
2324 }
2325
2326 if (!data || !parser.parse(data, len, 1)) {
2327 return -EINVAL;
2328 }
2329 cors_config =
2330 static_cast<RGWCORSConfiguration_S3 *>(parser.find_first(
2331 "CORSConfiguration"));
2332 if (!cors_config) {
2333 return -EINVAL;
2334 }
2335
31f18b77
FG
2336 // forward bucket cors requests to meta master zone
2337 if (!store->is_meta_master()) {
2338 /* only need to keep this data around if we're not meta master */
2339 in_data.append(data, len);
2340 }
2341
7c673cae
FG
2342 if (s->cct->_conf->subsys.should_gather(ceph_subsys_rgw, 15)) {
2343 ldout(s->cct, 15) << "CORSConfiguration";
2344 cors_config->to_xml(*_dout);
2345 *_dout << dendl;
2346 }
2347
2348 cors_config->encode(cors_bl);
2349
2350 return 0;
2351}
2352
2353void RGWPutCORS_ObjStore_S3::send_response()
2354{
2355 if (op_ret)
2356 set_req_state_err(s, op_ret);
2357 dump_errno(s);
2358 end_header(s, NULL, "application/xml");
2359 dump_start(s);
2360}
2361
2362void RGWDeleteCORS_ObjStore_S3::send_response()
2363{
2364 int r = op_ret;
2365 if (!r || r == -ENOENT)
2366 r = STATUS_NO_CONTENT;
2367
2368 set_req_state_err(s, r);
2369 dump_errno(s);
2370 end_header(s, NULL);
2371}
2372
2373void RGWOptionsCORS_ObjStore_S3::send_response()
2374{
2375 string hdrs, exp_hdrs;
2376 uint32_t max_age = CORS_MAX_AGE_INVALID;
2377 /*EACCES means, there is no CORS registered yet for the bucket
2378 *ENOENT means, there is no match of the Origin in the list of CORSRule
2379 */
2380 if (op_ret == -ENOENT)
2381 op_ret = -EACCES;
2382 if (op_ret < 0) {
2383 set_req_state_err(s, op_ret);
2384 dump_errno(s);
2385 end_header(s, NULL);
2386 return;
2387 }
2388 get_response_params(hdrs, exp_hdrs, &max_age);
2389
2390 dump_errno(s);
2391 dump_access_control(s, origin, req_meth, hdrs.c_str(), exp_hdrs.c_str(),
2392 max_age);
2393 end_header(s, NULL);
2394}
2395
2396void RGWGetRequestPayment_ObjStore_S3::send_response()
2397{
2398 dump_errno(s);
2399 end_header(s, this, "application/xml");
2400 dump_start(s);
2401
2402 s->formatter->open_object_section_in_ns("RequestPaymentConfiguration", XMLNS_AWS_S3);
2403 const char *payer = requester_pays ? "Requester" : "BucketOwner";
2404 s->formatter->dump_string("Payer", payer);
2405 s->formatter->close_section();
2406 rgw_flush_formatter_and_reset(s, s->formatter);
2407}
2408
2409class RGWSetRequestPaymentParser : public RGWXMLParser
2410{
2411 XMLObj *alloc_obj(const char *el) override {
2412 return new XMLObj;
2413 }
2414
2415public:
2416 RGWSetRequestPaymentParser() {}
2417 ~RGWSetRequestPaymentParser() override {}
2418
2419 int get_request_payment_payer(bool *requester_pays) {
2420 XMLObj *config = find_first("RequestPaymentConfiguration");
2421 if (!config)
2422 return -EINVAL;
2423
2424 *requester_pays = false;
2425
2426 XMLObj *field = config->find_first("Payer");
2427 if (!field)
2428 return 0;
2429
2430 string& s = field->get_data();
2431
2432 if (stringcasecmp(s, "Requester") == 0) {
2433 *requester_pays = true;
2434 } else if (stringcasecmp(s, "BucketOwner") != 0) {
2435 return -EINVAL;
2436 }
2437
2438 return 0;
2439 }
2440};
2441
2442int RGWSetRequestPayment_ObjStore_S3::get_params()
2443{
2444 char *data;
2445 int len = 0;
2446 const auto max_size = s->cct->_conf->rgw_max_put_param_size;
2447 int r = rgw_rest_read_all_input(s, &data, &len, max_size, false);
2448
2449 if (r < 0) {
2450 return r;
2451 }
2452
2453 RGWSetRequestPaymentParser parser;
2454
2455 if (!parser.init()) {
2456 ldout(s->cct, 0) << "ERROR: failed to initialize parser" << dendl;
2457 r = -EIO;
2458 goto done;
2459 }
2460
2461 if (!parser.parse(data, len, 1)) {
2462 ldout(s->cct, 10) << "failed to parse data: " << data << dendl;
2463 r = -EINVAL;
2464 goto done;
2465 }
2466
2467 r = parser.get_request_payment_payer(&requester_pays);
2468
2469done:
2470 free(data);
2471
2472 return r;
2473}
2474
2475void RGWSetRequestPayment_ObjStore_S3::send_response()
2476{
2477 if (op_ret)
2478 set_req_state_err(s, op_ret);
2479 dump_errno(s);
2480 end_header(s);
2481}
2482
2483int RGWInitMultipart_ObjStore_S3::get_params()
2484{
2485 RGWAccessControlPolicy_S3 s3policy(s->cct);
2486 op_ret = create_s3_policy(s, store, s3policy, s->owner);
2487 if (op_ret < 0)
2488 return op_ret;
2489
2490 policy = s3policy;
2491
2492 return 0;
2493}
2494
2495void RGWInitMultipart_ObjStore_S3::send_response()
2496{
2497 if (op_ret)
2498 set_req_state_err(s, op_ret);
2499 dump_errno(s);
2500 for (auto &it : crypt_http_responses)
2501 dump_header(s, it.first, it.second);
2502 end_header(s, this, "application/xml");
2503 if (op_ret == 0) {
2504 dump_start(s);
2505 s->formatter->open_object_section_in_ns("InitiateMultipartUploadResult", XMLNS_AWS_S3);
2506 if (!s->bucket_tenant.empty())
2507 s->formatter->dump_string("Tenant", s->bucket_tenant);
2508 s->formatter->dump_string("Bucket", s->bucket_name);
2509 s->formatter->dump_string("Key", s->object.name);
2510 s->formatter->dump_string("UploadId", upload_id);
2511 s->formatter->close_section();
2512 rgw_flush_formatter_and_reset(s, s->formatter);
2513 }
2514}
2515
2516int RGWInitMultipart_ObjStore_S3::prepare_encryption(map<string, bufferlist>& attrs)
2517{
2518 int res = 0;
2519 res = rgw_s3_prepare_encrypt(s, attrs, nullptr, nullptr, crypt_http_responses);
2520 return res;
2521}
2522
2523int RGWCompleteMultipart_ObjStore_S3::get_params()
2524{
2525 int ret = RGWCompleteMultipart_ObjStore::get_params();
2526 if (ret < 0) {
2527 return ret;
2528 }
2529
31f18b77 2530 return do_aws4_auth_completion();
7c673cae
FG
2531}
2532
2533void RGWCompleteMultipart_ObjStore_S3::send_response()
2534{
2535 if (op_ret)
2536 set_req_state_err(s, op_ret);
2537 dump_errno(s);
2538 end_header(s, this, "application/xml");
2539 if (op_ret == 0) {
2540 dump_start(s);
2541 s->formatter->open_object_section_in_ns("CompleteMultipartUploadResult", XMLNS_AWS_S3);
2542 if (!s->bucket_tenant.empty()) {
2543 if (s->info.domain.length()) {
2544 s->formatter->dump_format("Location", "%s.%s.%s",
2545 s->bucket_name.c_str(),
2546 s->bucket_tenant.c_str(),
2547 s->info.domain.c_str());
2548 }
2549 s->formatter->dump_string("Tenant", s->bucket_tenant);
2550 } else {
2551 if (s->info.domain.length()) {
2552 s->formatter->dump_format("Location", "%s.%s",
2553 s->bucket_name.c_str(),
2554 s->info.domain.c_str());
2555 }
2556 }
2557 s->formatter->dump_string("Bucket", s->bucket_name);
2558 s->formatter->dump_string("Key", s->object.name);
2559 s->formatter->dump_string("ETag", etag);
2560 s->formatter->close_section();
2561 rgw_flush_formatter_and_reset(s, s->formatter);
2562 }
2563}
2564
2565void RGWAbortMultipart_ObjStore_S3::send_response()
2566{
2567 int r = op_ret;
2568 if (!r)
2569 r = STATUS_NO_CONTENT;
2570
2571 set_req_state_err(s, r);
2572 dump_errno(s);
2573 end_header(s, this);
2574}
2575
2576void RGWListMultipart_ObjStore_S3::send_response()
2577{
2578 if (op_ret)
2579 set_req_state_err(s, op_ret);
2580 dump_errno(s);
2581 end_header(s, this, "application/xml");
2582
2583 if (op_ret == 0) {
2584 dump_start(s);
2585 s->formatter->open_object_section_in_ns("ListPartsResult", XMLNS_AWS_S3);
2586 map<uint32_t, RGWUploadPartInfo>::iterator iter;
2587 map<uint32_t, RGWUploadPartInfo>::reverse_iterator test_iter;
2588 int cur_max = 0;
2589
2590 iter = parts.begin();
2591 test_iter = parts.rbegin();
2592 if (test_iter != parts.rend()) {
2593 cur_max = test_iter->first;
2594 }
2595 if (!s->bucket_tenant.empty())
2596 s->formatter->dump_string("Tenant", s->bucket_tenant);
2597 s->formatter->dump_string("Bucket", s->bucket_name);
2598 s->formatter->dump_string("Key", s->object.name);
2599 s->formatter->dump_string("UploadId", upload_id);
2600 s->formatter->dump_string("StorageClass", "STANDARD");
2601 s->formatter->dump_int("PartNumberMarker", marker);
2602 s->formatter->dump_int("NextPartNumberMarker", cur_max);
2603 s->formatter->dump_int("MaxParts", max_parts);
2604 s->formatter->dump_string("IsTruncated", (truncated ? "true" : "false"));
2605
2606 ACLOwner& owner = policy.get_owner();
2607 dump_owner(s, owner.get_id(), owner.get_display_name());
2608
2609 for (; iter != parts.end(); ++iter) {
2610 RGWUploadPartInfo& info = iter->second;
2611
2612 s->formatter->open_object_section("Part");
2613
2614 dump_time(s, "LastModified", &info.modified);
2615
2616 s->formatter->dump_unsigned("PartNumber", info.num);
2617 s->formatter->dump_format("ETag", "\"%s\"", info.etag.c_str());
2618 s->formatter->dump_unsigned("Size", info.accounted_size);
2619 s->formatter->close_section();
2620 }
2621 s->formatter->close_section();
2622 rgw_flush_formatter_and_reset(s, s->formatter);
2623 }
2624}
2625
2626void RGWListBucketMultiparts_ObjStore_S3::send_response()
2627{
2628 if (op_ret < 0)
2629 set_req_state_err(s, op_ret);
2630 dump_errno(s);
2631
2632 end_header(s, this, "application/xml");
2633 dump_start(s);
2634 if (op_ret < 0)
2635 return;
2636
2637 s->formatter->open_object_section_in_ns("ListMultipartUploadsResult", XMLNS_AWS_S3);
2638 if (!s->bucket_tenant.empty())
2639 s->formatter->dump_string("Tenant", s->bucket_tenant);
2640 s->formatter->dump_string("Bucket", s->bucket_name);
2641 if (!prefix.empty())
2642 s->formatter->dump_string("ListMultipartUploadsResult.Prefix", prefix);
2643 string& key_marker = marker.get_key();
2644 if (!key_marker.empty())
2645 s->formatter->dump_string("KeyMarker", key_marker);
2646 string& upload_id_marker = marker.get_upload_id();
2647 if (!upload_id_marker.empty())
2648 s->formatter->dump_string("UploadIdMarker", upload_id_marker);
2649 string next_key = next_marker.mp.get_key();
2650 if (!next_key.empty())
2651 s->formatter->dump_string("NextKeyMarker", next_key);
2652 string next_upload_id = next_marker.mp.get_upload_id();
2653 if (!next_upload_id.empty())
2654 s->formatter->dump_string("NextUploadIdMarker", next_upload_id);
2655 s->formatter->dump_int("MaxUploads", max_uploads);
2656 if (!delimiter.empty())
2657 s->formatter->dump_string("Delimiter", delimiter);
2658 s->formatter->dump_string("IsTruncated", (is_truncated ? "true" : "false"));
2659
2660 if (op_ret >= 0) {
2661 vector<RGWMultipartUploadEntry>::iterator iter;
2662 for (iter = uploads.begin(); iter != uploads.end(); ++iter) {
2663 RGWMPObj& mp = iter->mp;
2664 s->formatter->open_array_section("Upload");
2665 s->formatter->dump_string("Key", mp.get_key());
2666 s->formatter->dump_string("UploadId", mp.get_upload_id());
2667 dump_owner(s, s->user->user_id, s->user->display_name, "Initiator");
2668 dump_owner(s, s->user->user_id, s->user->display_name);
2669 s->formatter->dump_string("StorageClass", "STANDARD");
2670 dump_time(s, "Initiated", &iter->obj.meta.mtime);
2671 s->formatter->close_section();
2672 }
2673 if (!common_prefixes.empty()) {
2674 s->formatter->open_array_section("CommonPrefixes");
2675 map<string, bool>::iterator pref_iter;
2676 for (pref_iter = common_prefixes.begin();
2677 pref_iter != common_prefixes.end(); ++pref_iter) {
2678 s->formatter->dump_string("CommonPrefixes.Prefix", pref_iter->first);
2679 }
2680 s->formatter->close_section();
2681 }
2682 }
2683 s->formatter->close_section();
2684 rgw_flush_formatter_and_reset(s, s->formatter);
2685}
2686
2687int RGWDeleteMultiObj_ObjStore_S3::get_params()
2688{
2689 int ret = RGWDeleteMultiObj_ObjStore::get_params();
2690 if (ret < 0) {
2691 return ret;
2692 }
2693
31f18b77 2694 return do_aws4_auth_completion();
7c673cae
FG
2695}
2696
2697void RGWDeleteMultiObj_ObjStore_S3::send_status()
2698{
2699 if (! status_dumped) {
2700 if (op_ret < 0)
2701 set_req_state_err(s, op_ret);
2702 dump_errno(s);
2703 status_dumped = true;
2704 }
2705}
2706
2707void RGWDeleteMultiObj_ObjStore_S3::begin_response()
2708{
2709
2710 if (!status_dumped) {
2711 send_status();
2712 }
2713
2714 dump_start(s);
2715 end_header(s, this, "application/xml");
2716 s->formatter->open_object_section_in_ns("DeleteResult", XMLNS_AWS_S3);
2717
2718 rgw_flush_formatter(s, s->formatter);
2719}
2720
2721void RGWDeleteMultiObj_ObjStore_S3::send_partial_response(rgw_obj_key& key,
2722 bool delete_marker,
2723 const string& marker_version_id, int ret)
2724{
2725 if (!key.empty()) {
2726 if (op_ret == 0 && !quiet) {
2727 s->formatter->open_object_section("Deleted");
2728 s->formatter->dump_string("Key", key.name);
2729 if (!key.instance.empty()) {
2730 s->formatter->dump_string("VersionId", key.instance);
2731 }
2732 if (delete_marker) {
2733 s->formatter->dump_bool("DeleteMarker", true);
2734 s->formatter->dump_string("DeleteMarkerVersionId", marker_version_id);
2735 }
2736 s->formatter->close_section();
2737 } else if (op_ret < 0) {
31f18b77 2738 struct rgw_http_error r;
7c673cae
FG
2739 int err_no;
2740
2741 s->formatter->open_object_section("Error");
2742
2743 err_no = -op_ret;
2744 rgw_get_errno_s3(&r, err_no);
2745
2746 s->formatter->dump_string("Key", key.name);
2747 s->formatter->dump_string("VersionId", key.instance);
2748 s->formatter->dump_int("Code", r.http_ret);
2749 s->formatter->dump_string("Message", r.s3_code);
2750 s->formatter->close_section();
2751 }
2752
2753 rgw_flush_formatter(s, s->formatter);
2754 }
2755}
2756
2757void RGWDeleteMultiObj_ObjStore_S3::end_response()
2758{
2759
2760 s->formatter->close_section();
2761 rgw_flush_formatter_and_reset(s, s->formatter);
2762}
2763
2764void RGWGetObjLayout_ObjStore_S3::send_response()
2765{
2766 if (op_ret)
2767 set_req_state_err(s, op_ret);
2768 dump_errno(s);
2769 end_header(s, this, "application/json");
2770
2771 JSONFormatter f;
2772
2773 if (op_ret < 0) {
2774 return;
2775 }
2776
2777 f.open_object_section("result");
2778 ::encode_json("head", head_obj, &f);
2779 ::encode_json("manifest", *manifest, &f);
2780 f.open_array_section("data_location");
2781 for (auto miter = manifest->obj_begin(); miter != manifest->obj_end(); ++miter) {
2782 f.open_object_section("obj");
2783 rgw_raw_obj raw_loc = miter.get_location().get_raw_obj(store);
2784 ::encode_json("ofs", miter.get_ofs(), &f);
2785 ::encode_json("loc", raw_loc, &f);
2786 ::encode_json("loc_ofs", miter.location_ofs(), &f);
2787 ::encode_json("loc_size", miter.get_stripe_size(), &f);
2788 f.close_section();
2789 rgw_flush_formatter(s, &f);
2790 }
2791 f.close_section();
2792 f.close_section();
2793 rgw_flush_formatter(s, &f);
2794}
2795
31f18b77
FG
2796int RGWConfigBucketMetaSearch_ObjStore_S3::get_params()
2797{
2798 auto iter = s->info.x_meta_map.find("x-amz-meta-search");
2799 if (iter == s->info.x_meta_map.end()) {
2800 s->err.message = "X-Rgw-Meta-Search header not provided";
2801 ldout(s->cct, 5) << s->err.message << dendl;
2802 return -EINVAL;
2803 }
2804
2805 list<string> expressions;
2806 get_str_list(iter->second, ",", expressions);
2807
2808 for (auto& expression : expressions) {
2809 vector<string> args;
2810 get_str_vec(expression, ";", args);
2811
2812 if (args.empty()) {
2813 s->err.message = "invalid empty expression";
2814 ldout(s->cct, 5) << s->err.message << dendl;
2815 return -EINVAL;
2816 }
2817 if (args.size() > 2) {
2818 s->err.message = string("invalid expression: ") + expression;
2819 ldout(s->cct, 5) << s->err.message << dendl;
2820 return -EINVAL;
2821 }
2822
2823 string key = boost::algorithm::to_lower_copy(rgw_trim_whitespace(args[0]));
2824 string val;
2825 if (args.size() > 1) {
2826 val = boost::algorithm::to_lower_copy(rgw_trim_whitespace(args[1]));
2827 }
2828
2829 if (!boost::algorithm::starts_with(key, RGW_AMZ_META_PREFIX)) {
2830 s->err.message = string("invalid expression, key must start with '" RGW_AMZ_META_PREFIX "' : ") + expression;
2831 ldout(s->cct, 5) << s->err.message << dendl;
2832 return -EINVAL;
2833 }
2834
2835 key = key.substr(sizeof(RGW_AMZ_META_PREFIX) - 1);
2836
2837 ESEntityTypeMap::EntityType entity_type;
2838
2839 if (val.empty() || val == "str" || val == "string") {
2840 entity_type = ESEntityTypeMap::ES_ENTITY_STR;
2841 } else if (val == "int" || val == "integer") {
2842 entity_type = ESEntityTypeMap::ES_ENTITY_INT;
2843 } else if (val == "date" || val == "datetime") {
2844 entity_type = ESEntityTypeMap::ES_ENTITY_DATE;
2845 } else {
2846 s->err.message = string("invalid entity type: ") + val;
2847 ldout(s->cct, 5) << s->err.message << dendl;
2848 return -EINVAL;
2849 }
2850
2851 mdsearch_config[key] = entity_type;
2852 }
2853
2854 return 0;
2855}
2856
2857void RGWConfigBucketMetaSearch_ObjStore_S3::send_response()
2858{
2859 if (op_ret)
2860 set_req_state_err(s, op_ret);
2861 dump_errno(s);
2862 end_header(s, this);
2863}
2864
2865void RGWGetBucketMetaSearch_ObjStore_S3::send_response()
2866{
2867 if (op_ret)
2868 set_req_state_err(s, op_ret);
2869 dump_errno(s);
2870 end_header(s, NULL, "application/xml");
2871
2872 Formatter *f = s->formatter;
2873 f->open_array_section("GetBucketMetaSearchResult");
2874 for (auto& e : s->bucket_info.mdsearch_config) {
2875 f->open_object_section("Entry");
2876 string k = string("x-amz-meta-") + e.first;
2877 f->dump_string("Key", k.c_str());
2878 const char *type;
2879 switch (e.second) {
2880 case ESEntityTypeMap::ES_ENTITY_INT:
2881 type = "int";
2882 break;
2883 case ESEntityTypeMap::ES_ENTITY_DATE:
2884 type = "date";
2885 break;
2886 default:
2887 type = "str";
2888 }
2889 f->dump_string("Type", type);
2890 f->close_section();
2891 }
2892 f->close_section();
2893 rgw_flush_formatter(s, f);
2894}
2895
2896void RGWDelBucketMetaSearch_ObjStore_S3::send_response()
2897{
2898 if (op_ret)
2899 set_req_state_err(s, op_ret);
2900 dump_errno(s);
2901 end_header(s, this);
2902}
2903
2904
7c673cae
FG
2905RGWOp *RGWHandler_REST_Service_S3::op_get()
2906{
2907 if (is_usage_op()) {
2908 return new RGWGetUsage_ObjStore_S3;
2909 } else {
2910 return new RGWListBuckets_ObjStore_S3;
2911 }
2912}
2913
2914RGWOp *RGWHandler_REST_Service_S3::op_head()
2915{
2916 return new RGWListBuckets_ObjStore_S3;
2917}
2918
2919RGWOp *RGWHandler_REST_Service_S3::op_post()
2920{
2921 if (s->info.args.exists("Action")) {
2922 string action = s->info.args.get("Action");
2923 if (action.compare("CreateRole") == 0)
2924 return new RGWCreateRole;
2925 if (action.compare("DeleteRole") == 0)
2926 return new RGWDeleteRole;
2927 if (action.compare("GetRole") == 0)
2928 return new RGWGetRole;
2929 if (action.compare("UpdateAssumeRolePolicy") == 0)
2930 return new RGWModifyRole;
2931 if (action.compare("ListRoles") == 0)
2932 return new RGWListRoles;
2933 if (action.compare("PutRolePolicy") == 0)
2934 return new RGWPutRolePolicy;
2935 if (action.compare("GetRolePolicy") == 0)
2936 return new RGWGetRolePolicy;
2937 if (action.compare("ListRolePolicies") == 0)
2938 return new RGWListRolePolicies;
2939 if (action.compare("DeleteRolePolicy") == 0)
2940 return new RGWDeleteRolePolicy;
2941 }
2942 return NULL;
2943}
2944
2945RGWOp *RGWHandler_REST_Bucket_S3::get_obj_op(bool get_data)
2946{
2947 // Non-website mode
31f18b77 2948 if (get_data) {
7c673cae 2949 return new RGWListBucket_ObjStore_S3;
31f18b77 2950 } else {
7c673cae 2951 return new RGWStatBucket_ObjStore_S3;
31f18b77 2952 }
7c673cae
FG
2953}
2954
2955RGWOp *RGWHandler_REST_Bucket_S3::op_get()
2956{
2957 if (s->info.args.sub_resource_exists("logging"))
2958 return new RGWGetBucketLogging_ObjStore_S3;
2959
2960 if (s->info.args.sub_resource_exists("location"))
2961 return new RGWGetBucketLocation_ObjStore_S3;
2962
2963 if (s->info.args.sub_resource_exists("versioning"))
2964 return new RGWGetBucketVersioning_ObjStore_S3;
2965
2966 if (s->info.args.sub_resource_exists("website")) {
2967 if (!s->cct->_conf->rgw_enable_static_website) {
2968 return NULL;
2969 }
2970 return new RGWGetBucketWebsite_ObjStore_S3;
2971 }
2972
31f18b77
FG
2973 if (s->info.args.exists("mdsearch")) {
2974 return new RGWGetBucketMetaSearch_ObjStore_S3;
2975 }
2976
7c673cae
FG
2977 if (is_acl_op()) {
2978 return new RGWGetACLs_ObjStore_S3;
2979 } else if (is_cors_op()) {
2980 return new RGWGetCORS_ObjStore_S3;
2981 } else if (is_request_payment_op()) {
2982 return new RGWGetRequestPayment_ObjStore_S3;
2983 } else if (s->info.args.exists("uploads")) {
2984 return new RGWListBucketMultiparts_ObjStore_S3;
2985 } else if(is_lc_op()) {
2986 return new RGWGetLC_ObjStore_S3;
31f18b77
FG
2987 } else if(is_policy_op()) {
2988 return new RGWGetBucketPolicy;
7c673cae
FG
2989 }
2990 return get_obj_op(true);
2991}
2992
2993RGWOp *RGWHandler_REST_Bucket_S3::op_head()
2994{
2995 if (is_acl_op()) {
2996 return new RGWGetACLs_ObjStore_S3;
2997 } else if (s->info.args.exists("uploads")) {
2998 return new RGWListBucketMultiparts_ObjStore_S3;
2999 }
3000 return get_obj_op(false);
3001}
3002
3003RGWOp *RGWHandler_REST_Bucket_S3::op_put()
3004{
3005 if (s->info.args.sub_resource_exists("logging"))
3006 return NULL;
3007 if (s->info.args.sub_resource_exists("versioning"))
3008 return new RGWSetBucketVersioning_ObjStore_S3;
3009 if (s->info.args.sub_resource_exists("website")) {
3010 if (!s->cct->_conf->rgw_enable_static_website) {
3011 return NULL;
3012 }
3013 return new RGWSetBucketWebsite_ObjStore_S3;
3014 }
3015 if (is_acl_op()) {
3016 return new RGWPutACLs_ObjStore_S3;
3017 } else if (is_cors_op()) {
3018 return new RGWPutCORS_ObjStore_S3;
3019 } else if (is_request_payment_op()) {
3020 return new RGWSetRequestPayment_ObjStore_S3;
3021 } else if(is_lc_op()) {
3022 return new RGWPutLC_ObjStore_S3;
31f18b77
FG
3023 } else if(is_policy_op()) {
3024 return new RGWPutBucketPolicy;
7c673cae
FG
3025 }
3026 return new RGWCreateBucket_ObjStore_S3;
3027}
3028
3029RGWOp *RGWHandler_REST_Bucket_S3::op_delete()
3030{
3031 if (is_cors_op()) {
3032 return new RGWDeleteCORS_ObjStore_S3;
3033 } else if(is_lc_op()) {
3034 return new RGWDeleteLC_ObjStore_S3;
31f18b77
FG
3035 } else if(is_policy_op()) {
3036 return new RGWDeleteBucketPolicy;
7c673cae
FG
3037 }
3038
3039 if (s->info.args.sub_resource_exists("website")) {
3040 if (!s->cct->_conf->rgw_enable_static_website) {
3041 return NULL;
3042 }
3043 return new RGWDeleteBucketWebsite_ObjStore_S3;
3044 }
3045
31f18b77
FG
3046 if (s->info.args.exists("mdsearch")) {
3047 return new RGWDelBucketMetaSearch_ObjStore_S3;
3048 }
3049
7c673cae
FG
3050 return new RGWDeleteBucket_ObjStore_S3;
3051}
3052
3053RGWOp *RGWHandler_REST_Bucket_S3::op_post()
3054{
3055 if (s->info.args.exists("delete")) {
3056 return new RGWDeleteMultiObj_ObjStore_S3;
3057 }
3058
31f18b77
FG
3059 if (s->info.args.exists("mdsearch")) {
3060 return new RGWConfigBucketMetaSearch_ObjStore_S3;
3061 }
3062
7c673cae
FG
3063 return new RGWPostObj_ObjStore_S3;
3064}
3065
3066RGWOp *RGWHandler_REST_Bucket_S3::op_options()
3067{
3068 return new RGWOptionsCORS_ObjStore_S3;
3069}
3070
3071RGWOp *RGWHandler_REST_Obj_S3::get_obj_op(bool get_data)
3072{
3073 if (is_acl_op()) {
3074 return new RGWGetACLs_ObjStore_S3;
3075 }
3076 RGWGetObj_ObjStore_S3 *get_obj_op = new RGWGetObj_ObjStore_S3;
3077 get_obj_op->set_get_data(get_data);
3078 return get_obj_op;
3079}
3080
3081RGWOp *RGWHandler_REST_Obj_S3::op_get()
3082{
3083 if (is_acl_op()) {
3084 return new RGWGetACLs_ObjStore_S3;
3085 } else if (s->info.args.exists("uploadId")) {
3086 return new RGWListMultipart_ObjStore_S3;
3087 } else if (s->info.args.exists("layout")) {
3088 return new RGWGetObjLayout_ObjStore_S3;
224ce89b
WB
3089 } else if (is_tagging_op()) {
3090 return new RGWGetObjTags_ObjStore_S3;
7c673cae
FG
3091 }
3092 return get_obj_op(true);
3093}
3094
3095RGWOp *RGWHandler_REST_Obj_S3::op_head()
3096{
3097 if (is_acl_op()) {
3098 return new RGWGetACLs_ObjStore_S3;
3099 } else if (s->info.args.exists("uploadId")) {
3100 return new RGWListMultipart_ObjStore_S3;
3101 }
3102 return get_obj_op(false);
3103}
3104
3105RGWOp *RGWHandler_REST_Obj_S3::op_put()
3106{
3107 if (is_acl_op()) {
3108 return new RGWPutACLs_ObjStore_S3;
224ce89b
WB
3109 } else if (is_tagging_op()) {
3110 return new RGWPutObjTags_ObjStore_S3;
7c673cae 3111 }
224ce89b 3112
7c673cae
FG
3113 if (s->init_state.src_bucket.empty())
3114 return new RGWPutObj_ObjStore_S3;
3115 else
3116 return new RGWCopyObj_ObjStore_S3;
3117}
3118
3119RGWOp *RGWHandler_REST_Obj_S3::op_delete()
3120{
224ce89b
WB
3121 if (is_tagging_op()) {
3122 return new RGWDeleteObjTags_ObjStore_S3;
3123 }
7c673cae
FG
3124 string upload_id = s->info.args.get("uploadId");
3125
3126 if (upload_id.empty())
3127 return new RGWDeleteObj_ObjStore_S3;
3128 else
3129 return new RGWAbortMultipart_ObjStore_S3;
3130}
3131
3132RGWOp *RGWHandler_REST_Obj_S3::op_post()
3133{
3134 if (s->info.args.exists("uploadId"))
3135 return new RGWCompleteMultipart_ObjStore_S3;
3136
3137 if (s->info.args.exists("uploads"))
3138 return new RGWInitMultipart_ObjStore_S3;
3139
31f18b77 3140 return new RGWPostObj_ObjStore_S3;
7c673cae
FG
3141}
3142
3143RGWOp *RGWHandler_REST_Obj_S3::op_options()
3144{
3145 return new RGWOptionsCORS_ObjStore_S3;
3146}
3147
3148int RGWHandler_REST_S3::init_from_header(struct req_state* s,
3149 int default_formatter,
3150 bool configurable_format)
3151{
3152 string req;
3153 string first;
3154
3155 const char *req_name = s->relative_uri.c_str();
3156 const char *p;
3157
3158 if (*req_name == '?') {
3159 p = req_name;
3160 } else {
3161 p = s->info.request_params.c_str();
3162 }
3163
3164 s->info.args.set(p);
3165 s->info.args.parse();
3166
3167 /* must be called after the args parsing */
3168 int ret = allocate_formatter(s, default_formatter, configurable_format);
3169 if (ret < 0)
3170 return ret;
3171
3172 if (*req_name != '/')
3173 return 0;
3174
3175 req_name++;
3176
3177 if (!*req_name)
3178 return 0;
3179
3180 req = req_name;
3181 int pos = req.find('/');
3182 if (pos >= 0) {
3183 first = req.substr(0, pos);
3184 } else {
3185 first = req;
3186 }
3187
3188 /*
3189 * XXX The intent of the check for empty is apparently to let the bucket
3190 * name from DNS to be set ahead. However, we currently take the DNS
3191 * bucket and re-insert it into URL in rgw_rest.cc:RGWREST::preprocess().
3192 * So, this check is meaningless.
3193 *
3194 * Rather than dropping this, the code needs to be changed into putting
3195 * the bucket (and its tenant) from DNS and Host: header (HTTP_HOST)
3196 * into req_status.bucket_name directly.
3197 */
3198 if (s->init_state.url_bucket.empty()) {
3199 // Save bucket to tide us over until token is parsed.
3200 s->init_state.url_bucket = first;
3201 if (pos >= 0) {
3202 string encoded_obj_str = req.substr(pos+1);
3203 s->object = rgw_obj_key(encoded_obj_str, s->info.args.get("versionId"));
3204 }
3205 } else {
3206 s->object = rgw_obj_key(req_name, s->info.args.get("versionId"));
3207 }
3208 return 0;
3209}
3210
3211int RGWHandler_REST_S3::postauth_init()
3212{
3213 struct req_init_state *t = &s->init_state;
3214 bool relaxed_names = s->cct->_conf->rgw_relaxed_s3_bucket_names;
3215
3216 rgw_parse_url_bucket(t->url_bucket, s->user->user_id.tenant,
3217 s->bucket_tenant, s->bucket_name);
3218
3219 dout(10) << "s->object=" << (!s->object.empty() ? s->object : rgw_obj_key("<NULL>"))
3220 << " s->bucket=" << rgw_make_bucket_entry_name(s->bucket_tenant, s->bucket_name) << dendl;
3221
3222 int ret;
d2e6a577 3223 ret = rgw_validate_tenant_name(s->bucket_tenant);
7c673cae
FG
3224 if (ret)
3225 return ret;
3226 if (!s->bucket_name.empty()) {
3227 ret = valid_s3_bucket_name(s->bucket_name, relaxed_names);
3228 if (ret)
3229 return ret;
3230 ret = validate_object_name(s->object.name);
3231 if (ret)
3232 return ret;
3233 }
3234
3235 if (!t->src_bucket.empty()) {
3236 rgw_parse_url_bucket(t->src_bucket, s->user->user_id.tenant,
3237 s->src_tenant_name, s->src_bucket_name);
d2e6a577 3238 ret = rgw_validate_tenant_name(s->src_tenant_name);
7c673cae
FG
3239 if (ret)
3240 return ret;
3241 ret = valid_s3_bucket_name(s->src_bucket_name, relaxed_names);
3242 if (ret)
3243 return ret;
3244 }
3245 return 0;
3246}
3247
3248int RGWHandler_REST_S3::init(RGWRados *store, struct req_state *s,
3249 rgw::io::BasicClient *cio)
3250{
3251 int ret;
3252
3253 s->dialect = "s3";
d2e6a577
FG
3254
3255 ret = rgw_validate_tenant_name(s->bucket_tenant);
7c673cae
FG
3256 if (ret)
3257 return ret;
3258 bool relaxed_names = s->cct->_conf->rgw_relaxed_s3_bucket_names;
3259 if (!s->bucket_name.empty()) {
3260 ret = valid_s3_bucket_name(s->bucket_name, relaxed_names);
3261 if (ret)
3262 return ret;
3263 ret = validate_object_name(s->object.name);
3264 if (ret)
3265 return ret;
3266 }
3267
3268 const char *cacl = s->info.env->get("HTTP_X_AMZ_ACL");
3269 if (cacl)
3270 s->canned_acl = cacl;
3271
3272 s->has_acl_header = s->info.env->exists_prefix("HTTP_X_AMZ_GRANT");
3273
3274 const char *copy_source = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE");
3275
3276 if (copy_source && !s->info.env->get("HTTP_X_AMZ_COPY_SOURCE_RANGE")) {
3277 ret = RGWCopyObj::parse_copy_location(copy_source,
3278 s->init_state.src_bucket,
3279 s->src_object);
3280 if (!ret) {
3281 ldout(s->cct, 0) << "failed to parse copy location" << dendl;
3282 return -EINVAL; // XXX why not -ERR_INVALID_BUCKET_NAME or -ERR_BAD_URL?
3283 }
3284 }
3285
3286 return RGWHandler_REST::init(store, s, cio);
3287}
3288
31f18b77
FG
3289enum class AwsVersion {
3290 UNKOWN,
3291 V2,
3292 V4
3293};
3294
3295enum class AwsRoute {
3296 UNKOWN,
3297 QUERY_STRING,
3298 HEADERS
3299};
3300
3301static inline std::pair<AwsVersion, AwsRoute>
3302discover_aws_flavour(const req_info& info)
3303{
3304 using rgw::auth::s3::AWS4_HMAC_SHA256_STR;
3305
3306 AwsVersion version = AwsVersion::UNKOWN;
3307 AwsRoute route = AwsRoute::UNKOWN;
3308
3309 const char* http_auth = info.env->get("HTTP_AUTHORIZATION");
3310 if (http_auth && http_auth[0]) {
3311 /* Authorization in Header */
3312 route = AwsRoute::HEADERS;
3313
3314 if (!strncmp(http_auth, AWS4_HMAC_SHA256_STR,
3315 strlen(AWS4_HMAC_SHA256_STR))) {
3316 /* AWS v4 */
3317 version = AwsVersion::V4;
3318 } else if (!strncmp(http_auth, "AWS ", 4)) {
3319 /* AWS v2 */
3320 version = AwsVersion::V2;
3321 }
3322 } else {
3323 route = AwsRoute::QUERY_STRING;
3324
3325 if (info.args.get("X-Amz-Algorithm") == AWS4_HMAC_SHA256_STR) {
3326 /* AWS v4 */
3327 version = AwsVersion::V4;
3328 } else if (!info.args.get("AWSAccessKeyId").empty()) {
3329 /* AWS v2 */
3330 version = AwsVersion::V2;
3331 }
3332 }
3333
3334 return std::make_pair(version, route);
3335}
3336
7c673cae
FG
3337static void init_anon_user(struct req_state *s)
3338{
3339 rgw_get_anon_user(*(s->user));
3340 s->perm_mask = RGW_PERM_FULL_CONTROL;
3341}
3342
3343/*
3344 * verify that a signed request comes from the keyholder
3345 * by checking the signature against our locally-computed version
3346 *
3347 * it tries AWS v4 before AWS v2
3348 */
3349int RGW_Auth_S3::authorize(RGWRados* const store,
3350 const rgw::auth::StrategyRegistry& auth_registry,
3351 struct req_state* const s)
3352{
3353
3354 /* neither keystone and rados enabled; warn and exit! */
3355 if (!store->ctx()->_conf->rgw_s3_auth_use_rados &&
3356 !store->ctx()->_conf->rgw_s3_auth_use_keystone &&
3357 !store->ctx()->_conf->rgw_s3_auth_use_ldap) {
3358 dout(0) << "WARNING: no authorization backend enabled! Users will never authenticate." << dendl;
3359 return -EPERM;
3360 }
3361
31f18b77
FG
3362 const auto ret = rgw::auth::Strategy::apply(auth_registry.get_s3_main(), s);
3363 if (ret == 0) {
3364 /* Populate the owner info. */
3365 s->owner.set_id(s->user->user_id);
3366 s->owner.set_name(s->user->display_name);
3367 }
3368 return ret;
7c673cae
FG
3369}
3370
3371int RGWHandler_Auth_S3::init(RGWRados *store, struct req_state *state,
3372 rgw::io::BasicClient *cio)
3373{
3374 int ret = RGWHandler_REST_S3::init_from_header(state, RGW_FORMAT_JSON,
3375 true);
3376 if (ret < 0)
3377 return ret;
3378
3379 return RGWHandler_REST::init(store, state, cio);
3380}
3381
3382RGWHandler_REST* RGWRESTMgr_S3::get_handler(struct req_state* const s,
3383 const rgw::auth::StrategyRegistry& auth_registry,
3384 const std::string& frontend_prefix)
3385{
3386 bool is_s3website = enable_s3website && (s->prot_flags & RGW_REST_WEBSITE);
3387 int ret =
3388 RGWHandler_REST_S3::init_from_header(s,
3389 is_s3website ? RGW_FORMAT_HTML :
3390 RGW_FORMAT_XML, true);
3391 if (ret < 0)
3392 return NULL;
3393
3394 RGWHandler_REST* handler;
3395 // TODO: Make this more readable
3396 if (is_s3website) {
3397 if (s->init_state.url_bucket.empty()) {
3398 handler = new RGWHandler_REST_Service_S3Website(auth_registry);
3399 } else if (s->object.empty()) {
3400 handler = new RGWHandler_REST_Bucket_S3Website(auth_registry);
3401 } else {
3402 handler = new RGWHandler_REST_Obj_S3Website(auth_registry);
3403 }
3404 } else {
3405 if (s->init_state.url_bucket.empty()) {
3406 handler = new RGWHandler_REST_Service_S3(auth_registry);
3407 } else if (s->object.empty()) {
3408 handler = new RGWHandler_REST_Bucket_S3(auth_registry);
3409 } else {
3410 handler = new RGWHandler_REST_Obj_S3(auth_registry);
3411 }
3412 }
3413
3414 ldout(s->cct, 20) << __func__ << " handler=" << typeid(*handler).name()
3415 << dendl;
3416 return handler;
3417}
3418
224ce89b
WB
3419bool RGWHandler_REST_S3Website::web_dir() const {
3420 std::string subdir_name = url_decode(s->object.name);
3421
3422 if (subdir_name.empty()) {
3423 return false;
3424 } else if (subdir_name.back() == '/') {
3425 subdir_name.pop_back();
3426 }
3427
3428 rgw_obj obj(s->bucket, subdir_name);
3429
3430 RGWObjectCtx& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
3431 obj_ctx.obj.set_atomic(obj);
3432 obj_ctx.obj.set_prefetch_data(obj);
3433
3434 RGWObjState* state = nullptr;
3435 if (store->get_obj_state(&obj_ctx, s->bucket_info, obj, &state, false) < 0) {
3436 return false;
3437 }
3438 if (! state->exists) {
3439 return false;
3440 }
3441 return state->exists;
3442}
3443
7c673cae
FG
3444int RGWHandler_REST_S3Website::retarget(RGWOp* op, RGWOp** new_op) {
3445 *new_op = op;
3446 ldout(s->cct, 10) << __func__ << "Starting retarget" << dendl;
3447
3448 if (!(s->prot_flags & RGW_REST_WEBSITE))
3449 return 0;
3450
3451 RGWObjectCtx& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
3452 int ret = store->get_bucket_info(obj_ctx, s->bucket_tenant,
3453 s->bucket_name, s->bucket_info, NULL,
3454 &s->bucket_attrs);
3455 if (ret < 0) {
3456 // TODO-FUTURE: if the bucket does not exist, maybe expose it here?
3457 return -ERR_NO_SUCH_BUCKET;
3458 }
3459 if (!s->bucket_info.has_website) {
3460 // TODO-FUTURE: if the bucket has no WebsiteConfig, expose it here
3461 return -ERR_NO_SUCH_WEBSITE_CONFIGURATION;
3462 }
3463
3464 rgw_obj_key new_obj;
224ce89b 3465 s->bucket_info.website_conf.get_effective_key(s->object.name, &new_obj.name, web_dir());
7c673cae
FG
3466 ldout(s->cct, 10) << "retarget get_effective_key " << s->object << " -> "
3467 << new_obj << dendl;
3468
3469 RGWBWRoutingRule rrule;
3470 bool should_redirect =
3471 s->bucket_info.website_conf.should_redirect(new_obj.name, 0, &rrule);
3472
3473 if (should_redirect) {
3474 const string& hostname = s->info.env->get("HTTP_HOST", "");
3475 const string& protocol =
3476 (s->info.env->get("SERVER_PORT_SECURE") ? "https" : "http");
3477 int redirect_code = 0;
3478 rrule.apply_rule(protocol, hostname, s->object.name, &s->redirect,
3479 &redirect_code);
3480 // APply a custom HTTP response code
3481 if (redirect_code > 0)
3482 s->err.http_ret = redirect_code; // Apply a custom HTTP response code
3483 ldout(s->cct, 10) << "retarget redirect code=" << redirect_code
3484 << " proto+host:" << protocol << "://" << hostname
3485 << " -> " << s->redirect << dendl;
3486 return -ERR_WEBSITE_REDIRECT;
3487 }
3488
3489 /*
3490 * FIXME: if s->object != new_obj, drop op and create a new op to handle
3491 * operation. Or remove this comment if it's not applicable anymore
3492 */
3493
3494 s->object = new_obj;
3495
3496 return 0;
3497}
3498
3499RGWOp* RGWHandler_REST_S3Website::op_get()
3500{
3501 return get_obj_op(true);
3502}
3503
3504RGWOp* RGWHandler_REST_S3Website::op_head()
3505{
3506 return get_obj_op(false);
3507}
3508
3509int RGWHandler_REST_S3Website::serve_errordoc(int http_ret, const string& errordoc_key) {
3510 int ret = 0;
3511 s->formatter->reset(); /* Try to throw it all away */
3512
3513 std::shared_ptr<RGWGetObj_ObjStore_S3Website> getop( static_cast<RGWGetObj_ObjStore_S3Website*>(op_get()));
3514 if (getop.get() == NULL) {
3515 return -1; // Trigger double error handler
3516 }
3517 getop->init(store, s, this);
3518 getop->range_str = NULL;
3519 getop->if_mod = NULL;
3520 getop->if_unmod = NULL;
3521 getop->if_match = NULL;
3522 getop->if_nomatch = NULL;
3523 s->object = errordoc_key;
3524
3525 ret = init_permissions(getop.get());
3526 if (ret < 0) {
3527 ldout(s->cct, 20) << "serve_errordoc failed, init_permissions ret=" << ret << dendl;
3528 return -1; // Trigger double error handler
3529 }
3530
3531 ret = read_permissions(getop.get());
3532 if (ret < 0) {
3533 ldout(s->cct, 20) << "serve_errordoc failed, read_permissions ret=" << ret << dendl;
3534 return -1; // Trigger double error handler
3535 }
3536
3537 if (http_ret) {
3538 getop->set_custom_http_response(http_ret);
3539 }
3540
3541 ret = getop->init_processing();
3542 if (ret < 0) {
3543 ldout(s->cct, 20) << "serve_errordoc failed, init_processing ret=" << ret << dendl;
3544 return -1; // Trigger double error handler
3545 }
3546
3547 ret = getop->verify_op_mask();
3548 if (ret < 0) {
3549 ldout(s->cct, 20) << "serve_errordoc failed, verify_op_mask ret=" << ret << dendl;
3550 return -1; // Trigger double error handler
3551 }
3552
3553 ret = getop->verify_permission();
3554 if (ret < 0) {
3555 ldout(s->cct, 20) << "serve_errordoc failed, verify_permission ret=" << ret << dendl;
3556 return -1; // Trigger double error handler
3557 }
3558
3559 ret = getop->verify_params();
3560 if (ret < 0) {
3561 ldout(s->cct, 20) << "serve_errordoc failed, verify_params ret=" << ret << dendl;
3562 return -1; // Trigger double error handler
3563 }
3564
3565 // No going back now
3566 getop->pre_exec();
3567 /*
3568 * FIXME Missing headers:
3569 * With a working errordoc, the s3 error fields are rendered as HTTP headers,
3570 * x-amz-error-code: NoSuchKey
3571 * x-amz-error-message: The specified key does not exist.
3572 * x-amz-error-detail-Key: foo
3573 */
3574 getop->execute();
3575 getop->complete();
3576 return 0;
3577
3578}
3579
3580int RGWHandler_REST_S3Website::error_handler(int err_no,
3581 string* error_content) {
3582 int new_err_no = -1;
31f18b77 3583 rgw_http_errors::const_iterator r = rgw_http_s3_errors.find(err_no > 0 ? err_no : -err_no);
7c673cae 3584 int http_error_code = -1;
31f18b77
FG
3585
3586 if (r != rgw_http_s3_errors.end()) {
3587 http_error_code = r->second.first;
7c673cae
FG
3588 }
3589 ldout(s->cct, 10) << "RGWHandler_REST_S3Website::error_handler err_no=" << err_no << " http_ret=" << http_error_code << dendl;
3590
3591 RGWBWRoutingRule rrule;
3592 bool should_redirect =
3593 s->bucket_info.website_conf.should_redirect(s->object.name, http_error_code,
3594 &rrule);
3595
3596 if (should_redirect) {
3597 const string& hostname = s->info.env->get("HTTP_HOST", "");
3598 const string& protocol =
3599 (s->info.env->get("SERVER_PORT_SECURE") ? "https" : "http");
3600 int redirect_code = 0;
3601 rrule.apply_rule(protocol, hostname, s->object.name, &s->redirect,
3602 &redirect_code);
3603 // Apply a custom HTTP response code
3604 if (redirect_code > 0)
3605 s->err.http_ret = redirect_code; // Apply a custom HTTP response code
3606 ldout(s->cct, 10) << "error handler redirect code=" << redirect_code
3607 << " proto+host:" << protocol << "://" << hostname
3608 << " -> " << s->redirect << dendl;
3609 return -ERR_WEBSITE_REDIRECT;
3610 } else if (err_no == -ERR_WEBSITE_REDIRECT) {
3611 // Do nothing here, this redirect will be handled in abort_early's ERR_WEBSITE_REDIRECT block
3612 // Do NOT fire the ErrorDoc handler
3613 } else if (!s->bucket_info.website_conf.error_doc.empty()) {
3614 /* This serves an entire page!
3615 On success, it will return zero, and no further content should be sent to the socket
3616 On failure, we need the double-error handler
3617 */
3618 new_err_no = RGWHandler_REST_S3Website::serve_errordoc(http_error_code, s->bucket_info.website_conf.error_doc);
3619 if (new_err_no && new_err_no != -1) {
3620 err_no = new_err_no;
3621 }
3622 } else {
3623 ldout(s->cct, 20) << "No special error handling today!" << dendl;
3624 }
3625
3626 return err_no;
3627}
3628
3629RGWOp* RGWHandler_REST_Obj_S3Website::get_obj_op(bool get_data)
3630{
3631 /** If we are in website mode, then it is explicitly impossible to run GET or
3632 * HEAD on the actual directory. We must convert the request to run on the
3633 * suffix object instead!
3634 */
3635 RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
3636 op->set_get_data(get_data);
3637 return op;
3638}
3639
3640RGWOp* RGWHandler_REST_Bucket_S3Website::get_obj_op(bool get_data)
3641{
3642 /** If we are in website mode, then it is explicitly impossible to run GET or
3643 * HEAD on the actual directory. We must convert the request to run on the
3644 * suffix object instead!
3645 */
3646 RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
3647 op->set_get_data(get_data);
3648 return op;
3649}
3650
3651RGWOp* RGWHandler_REST_Service_S3Website::get_obj_op(bool get_data)
3652{
3653 /** If we are in website mode, then it is explicitly impossible to run GET or
3654 * HEAD on the actual directory. We must convert the request to run on the
3655 * suffix object instead!
3656 */
3657 RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
3658 op->set_get_data(get_data);
3659 return op;
3660}
3661
3662
3663namespace rgw {
3664namespace auth {
3665namespace s3 {
3666
31f18b77
FG
3667bool AWSGeneralAbstractor::is_time_skew_ok(const utime_t& header_time,
3668 const bool qsr) const
7c673cae
FG
3669{
3670 /* Check for time skew first. */
3671 const time_t req_sec = header_time.sec();
3672 time_t now;
3673 time(&now);
3674
3675 if ((req_sec < now - RGW_AUTH_GRACE_MINS * 60 ||
3676 req_sec > now + RGW_AUTH_GRACE_MINS * 60) && !qsr) {
3677 ldout(cct, 10) << "req_sec=" << req_sec << " now=" << now
3678 << "; now - RGW_AUTH_GRACE_MINS="
3679 << now - RGW_AUTH_GRACE_MINS * 60
3680 << "; now + RGW_AUTH_GRACE_MINS="
3681 << now + RGW_AUTH_GRACE_MINS * 60
3682 << dendl;
3683
3684 ldout(cct, 0) << "NOTICE: request time skew too big now="
3685 << utime_t(now, 0)
3686 << " req_time=" << header_time
3687 << dendl;
3688 return false;
3689 } else {
3690 return true;
3691 }
3692}
3693
31f18b77
FG
3694
3695static rgw::auth::Completer::cmplptr_t
3696null_completer_factory(const boost::optional<std::string>& secret_key)
7c673cae 3697{
31f18b77
FG
3698 return nullptr;
3699}
3700
3701
224ce89b 3702AWSEngine::VersionAbstractor::auth_data_t
31f18b77
FG
3703AWSGeneralAbstractor::get_auth_data(const req_state* const s) const
3704{
3705 AwsVersion version;
3706 AwsRoute route;
3707 std::tie(version, route) = discover_aws_flavour(s->info);
3708
3709 if (version == AwsVersion::V2) {
3710 return get_auth_data_v2(s);
3711 } else if (version == AwsVersion::V4) {
3712 return get_auth_data_v4(s, route == AwsRoute::QUERY_STRING);
3713 } else {
3714 /* FIXME(rzarzynski): handle anon user. */
3715 throw -EINVAL;
3716 }
3717}
3718
3719boost::optional<std::string>
3720AWSGeneralAbstractor::get_v4_canonical_headers(
3721 const req_info& info,
3722 const boost::string_view& signedheaders,
3723 const bool using_qs) const
3724{
3725 return rgw::auth::s3::get_v4_canonical_headers(info, signedheaders,
3726 using_qs, false);
3727}
3728
224ce89b 3729AWSEngine::VersionAbstractor::auth_data_t
31f18b77
FG
3730AWSGeneralAbstractor::get_auth_data_v4(const req_state* const s,
3731 /* FIXME: const. */
3732 bool using_qs) const
3733{
3734 boost::string_view access_key_id;
3735 boost::string_view signed_hdrs;
3736
3737 boost::string_view date;
3738 boost::string_view credential_scope;
3739 boost::string_view client_signature;
3740
3741 int ret = rgw::auth::s3::parse_credentials(s->info,
3742 access_key_id,
3743 credential_scope,
3744 signed_hdrs,
3745 client_signature,
3746 date,
3747 using_qs);
3748 if (ret < 0) {
3749 throw ret;
3750 }
3751
3752 /* craft canonical headers */
3753 boost::optional<std::string> canonical_headers = \
3754 get_v4_canonical_headers(s->info, signed_hdrs, using_qs);
3755 if (canonical_headers) {
3756 ldout(s->cct, 10) << "canonical headers format = " << *canonical_headers
3757 << dendl;
3758 } else {
3759 throw -EPERM;
3760 }
3761
3762 /* Get the expected hash. */
3763 auto exp_payload_hash = rgw::auth::s3::get_v4_exp_payload_hash(s->info);
3764
3765 /* Craft canonical URI. Using std::move later so let it be non-const. */
3766 auto canonical_uri = rgw::auth::s3::get_v4_canonical_uri(s->info);
3767
3768 /* Craft canonical query string. std::moving later so non-const here. */
3769 auto canonical_qs = rgw::auth::s3::get_v4_canonical_qs(s->info, using_qs);
3770
3771 /* Craft canonical request. */
3772 auto canonical_req_hash = \
3773 rgw::auth::s3::get_v4_canon_req_hash(s->cct,
3774 s->info.method,
3775 std::move(canonical_uri),
3776 std::move(canonical_qs),
3777 std::move(*canonical_headers),
3778 signed_hdrs,
3779 exp_payload_hash);
3780
3781 auto string_to_sign = \
3782 rgw::auth::s3::get_v4_string_to_sign(s->cct,
3783 AWS4_HMAC_SHA256_STR,
3784 date,
3785 credential_scope,
3786 std::move(canonical_req_hash));
3787
3788 const auto sig_factory = std::bind(rgw::auth::s3::get_v4_signature,
3789 credential_scope,
3790 std::placeholders::_1,
3791 std::placeholders::_2,
3792 std::placeholders::_3);
3793
3794 /* Requests authenticated with the Query Parameters are treated as unsigned.
3795 * From "Authenticating Requests: Using Query Parameters (AWS Signature
3796 * Version 4)":
3797 *
3798 * You don't include a payload hash in the Canonical Request, because
3799 * when you create a presigned URL, you don't know the payload content
3800 * because the URL is used to upload an arbitrary payload. Instead, you
3801 * use a constant string UNSIGNED-PAYLOAD.
3802 *
3803 * This means we have absolutely no business in spawning completer. Both
3804 * aws4_auth_needs_complete and aws4_auth_streaming_mode are set to false
3805 * by default. We don't need to change that. */
3806 if (is_v4_payload_unsigned(exp_payload_hash) || is_v4_payload_empty(s)) {
224ce89b
WB
3807 return {
3808 access_key_id,
3809 client_signature,
3810 std::move(string_to_sign),
3811 sig_factory,
3812 null_completer_factory
3813 };
31f18b77
FG
3814 } else {
3815 /* We're going to handle a signed payload. Be aware that even empty HTTP
3816 * body (no payload) requires verification:
3817 *
3818 * The x-amz-content-sha256 header is required for all AWS Signature
3819 * Version 4 requests. It provides a hash of the request payload. If
3820 * there is no payload, you must provide the hash of an empty string. */
3821 if (!is_v4_payload_streamed(exp_payload_hash)) {
3822 ldout(s->cct, 10) << "delaying v4 auth" << dendl;
3823
3824 /* payload in a single chunk */
3825 switch (s->op_type)
3826 {
3827 case RGW_OP_CREATE_BUCKET:
3828 case RGW_OP_PUT_OBJ:
3829 case RGW_OP_PUT_ACLS:
3830 case RGW_OP_PUT_CORS:
b32b8144 3831 case RGW_OP_INIT_MULTIPART: // in case that Init Multipart uses CHUNK encoding
31f18b77
FG
3832 case RGW_OP_COMPLETE_MULTIPART:
3833 case RGW_OP_SET_BUCKET_VERSIONING:
3834 case RGW_OP_DELETE_MULTI_OBJ:
3835 case RGW_OP_ADMIN_SET_METADATA:
3836 case RGW_OP_SET_BUCKET_WEBSITE:
3837 case RGW_OP_PUT_BUCKET_POLICY:
224ce89b 3838 case RGW_OP_PUT_OBJ_TAGGING:
d2e6a577 3839 case RGW_OP_PUT_LC:
31f18b77
FG
3840 break;
3841 default:
3842 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl;
3843 throw -ERR_NOT_IMPLEMENTED;
3844 }
3845
3846 const auto cmpl_factory = std::bind(AWSv4ComplSingle::create,
3847 s,
3848 std::placeholders::_1);
224ce89b
WB
3849 return {
3850 access_key_id,
3851 client_signature,
3852 std::move(string_to_sign),
3853 sig_factory,
3854 cmpl_factory
3855 };
31f18b77
FG
3856 } else {
3857 /* IMHO "streamed" doesn't fit too good here. I would prefer to call
3858 * it "chunked" but let's be coherent with Amazon's terminology. */
3859
3860 dout(10) << "body content detected in multiple chunks" << dendl;
3861
3862 /* payload in multiple chunks */
3863
3864 switch(s->op_type)
3865 {
3866 case RGW_OP_PUT_OBJ:
3867 break;
3868 default:
3869 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED (streaming mode)" << dendl;
3870 throw -ERR_NOT_IMPLEMENTED;
3871 }
3872
3873 dout(10) << "aws4 seed signature ok... delaying v4 auth" << dendl;
3874
3875 /* In the case of streamed payload client sets the x-amz-content-sha256
3876 * to "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" but uses "UNSIGNED-PAYLOAD"
3877 * when constructing the Canonical Request. */
3878
3879 /* In the case of single-chunk upload client set the header's value is
3880 * coherent with the one used for Canonical Request crafting. */
3881
3882 /* In the case of query string-based authentication there should be no
3883 * x-amz-content-sha256 header and the value "UNSIGNED-PAYLOAD" is used
3884 * for CanonReq. */
3885 const auto cmpl_factory = std::bind(AWSv4ComplMulti::create,
3886 s,
3887 date,
3888 credential_scope,
3889 client_signature,
3890 std::placeholders::_1);
224ce89b
WB
3891 return {
3892 access_key_id,
3893 client_signature,
3894 std::move(string_to_sign),
3895 sig_factory,
3896 cmpl_factory
3897 };
31f18b77
FG
3898 }
3899 }
3900}
3901
3902
3903boost::optional<std::string>
3904AWSGeneralBoto2Abstractor::get_v4_canonical_headers(
3905 const req_info& info,
3906 const boost::string_view& signedheaders,
3907 const bool using_qs) const
3908{
3909 return rgw::auth::s3::get_v4_canonical_headers(info, signedheaders,
3910 using_qs, true);
3911}
3912
3913
224ce89b 3914AWSEngine::VersionAbstractor::auth_data_t
31f18b77
FG
3915AWSGeneralAbstractor::get_auth_data_v2(const req_state* const s) const
3916{
3917 boost::string_view access_key_id;
3918 boost::string_view signature;
7c673cae
FG
3919 bool qsr = false;
3920
31f18b77
FG
3921 const char* http_auth = s->info.env->get("HTTP_AUTHORIZATION");
3922 if (! http_auth || http_auth[0] == '\0') {
7c673cae
FG
3923 /* Credentials are provided in query string. We also need to verify
3924 * the "Expires" parameter now. */
3925 access_key_id = s->info.args.get("AWSAccessKeyId");
3926 signature = s->info.args.get("Signature");
3927 qsr = true;
3928
31f18b77 3929 boost::string_view expires = s->info.args.get("Expires");
7c673cae 3930 if (! expires.empty()) {
31f18b77
FG
3931 /* It looks we have the guarantee that expires is a null-terminated,
3932 * and thus string_view::data() can be safely used. */
3933 const time_t exp = atoll(expires.data());
7c673cae
FG
3934 time_t now;
3935 time(&now);
3936
3937 if (now >= exp) {
3938 throw -EPERM;
3939 }
3940 }
3941 } else {
3942 /* The "Authorization" HTTP header is being used. */
31f18b77 3943 const boost::string_view auth_str(http_auth + strlen("AWS "));
7c673cae 3944 const size_t pos = auth_str.rfind(':');
31f18b77 3945 if (pos != boost::string_view::npos) {
7c673cae
FG
3946 access_key_id = auth_str.substr(0, pos);
3947 signature = auth_str.substr(pos + 1);
3948 }
3949 }
3950
3951 /* Let's canonize the HTTP headers that are covered by the AWS auth v2. */
3952 std::string string_to_sign;
3953 utime_t header_time;
3954 if (! rgw_create_s3_canonical_header(s->info, &header_time, string_to_sign,
3955 qsr)) {
3956 ldout(cct, 10) << "failed to create the canonized auth header\n"
3957 << rgw::crypt_sanitize::auth{s,string_to_sign} << dendl;
3958 throw -EPERM;
3959 }
3960
3961 ldout(cct, 10) << "string_to_sign:\n"
3962 << rgw::crypt_sanitize::auth{s,string_to_sign} << dendl;
3963
3964 if (! is_time_skew_ok(header_time, qsr)) {
3965 throw -ERR_REQUEST_TIME_SKEWED;
3966 }
3967
224ce89b
WB
3968 return {
3969 std::move(access_key_id),
3970 std::move(signature),
3971 std::move(string_to_sign),
3972 rgw::auth::s3::get_v2_signature,
3973 null_completer_factory
3974 };
31f18b77
FG
3975}
3976
3977
224ce89b 3978AWSEngine::VersionAbstractor::auth_data_t
31f18b77
FG
3979AWSBrowserUploadAbstractor::get_auth_data_v2(const req_state* const s) const
3980{
224ce89b
WB
3981 return {
3982 s->auth.s3_postobj_creds.access_key,
3983 s->auth.s3_postobj_creds.signature,
3984 s->auth.s3_postobj_creds.encoded_policy.to_str(),
3985 rgw::auth::s3::get_v2_signature,
3986 null_completer_factory
3987 };
31f18b77
FG
3988}
3989
224ce89b 3990AWSEngine::VersionAbstractor::auth_data_t
31f18b77
FG
3991AWSBrowserUploadAbstractor::get_auth_data_v4(const req_state* const s) const
3992{
3993 const boost::string_view credential = s->auth.s3_postobj_creds.x_amz_credential;
3994
3995 /* grab access key id */
3996 const size_t pos = credential.find("/");
3997 const boost::string_view access_key_id = credential.substr(0, pos);
3998 dout(10) << "access key id = " << access_key_id << dendl;
3999
4000 /* grab credential scope */
4001 const boost::string_view credential_scope = credential.substr(pos + 1);
4002 dout(10) << "credential scope = " << credential_scope << dendl;
4003
4004 const auto sig_factory = std::bind(rgw::auth::s3::get_v4_signature,
4005 credential_scope,
4006 std::placeholders::_1,
4007 std::placeholders::_2,
4008 std::placeholders::_3);
4009
224ce89b
WB
4010 return {
4011 access_key_id,
4012 s->auth.s3_postobj_creds.signature,
4013 s->auth.s3_postobj_creds.encoded_policy.to_str(),
4014 sig_factory,
4015 null_completer_factory
4016 };
31f18b77
FG
4017}
4018
224ce89b 4019AWSEngine::VersionAbstractor::auth_data_t
31f18b77
FG
4020AWSBrowserUploadAbstractor::get_auth_data(const req_state* const s) const
4021{
4022 if (s->auth.s3_postobj_creds.x_amz_algorithm == AWS4_HMAC_SHA256_STR) {
4023 ldout(s->cct, 0) << "Signature verification algorithm AWS v4"
4024 << " (AWS4-HMAC-SHA256)" << dendl;
224ce89b 4025 return get_auth_data_v4(s);
31f18b77
FG
4026 } else {
4027 ldout(s->cct, 0) << "Signature verification algorithm AWS v2" << dendl;
4028 return get_auth_data_v2(s);
4029 }
4030}
4031
4032
4033AWSEngine::result_t
4034AWSEngine::authenticate(const req_state* const s) const
4035{
31f18b77 4036 /* Small reminder: an ver_abstractor is allowed to throw! */
224ce89b 4037 const auto auth_data = ver_abstractor.get_auth_data(s);
31f18b77 4038
224ce89b 4039 if (auth_data.access_key_id.empty() || auth_data.client_signature.empty()) {
31f18b77
FG
4040 return result_t::deny(-EINVAL);
4041 } else {
224ce89b
WB
4042 return authenticate(auth_data.access_key_id,
4043 auth_data.client_signature,
4044 auth_data.string_to_sign,
4045 auth_data.signature_factory,
4046 auth_data.completer_factory,
4047 s);
31f18b77 4048 }
7c673cae
FG
4049}
4050
4051} /* namespace s3 */
4052} /* namespace auth */
4053} /* namespace rgw */
4054
4055rgw::LDAPHelper* rgw::auth::s3::LDAPEngine::ldh = nullptr;
4056std::mutex rgw::auth::s3::LDAPEngine::mtx;
4057
4058void rgw::auth::s3::LDAPEngine::init(CephContext* const cct)
4059{
4060 if (! ldh) {
4061 std::lock_guard<std::mutex> lck(mtx);
4062 if (! ldh) {
4063 const string& ldap_uri = cct->_conf->rgw_ldap_uri;
4064 const string& ldap_binddn = cct->_conf->rgw_ldap_binddn;
4065 const string& ldap_searchdn = cct->_conf->rgw_ldap_searchdn;
4066 const string& ldap_searchfilter = cct->_conf->rgw_ldap_searchfilter;
4067 const string& ldap_dnattr = cct->_conf->rgw_ldap_dnattr;
4068 std::string ldap_bindpw = parse_rgw_ldap_bindpw(cct);
4069
4070 ldh = new rgw::LDAPHelper(ldap_uri, ldap_binddn, ldap_bindpw,
4071 ldap_searchdn, ldap_searchfilter, ldap_dnattr);
4072
4073 ldh->init();
4074 ldh->bind();
4075 }
4076 }
4077}
4078
4079rgw::auth::RemoteApplier::acl_strategy_t
4080rgw::auth::s3::LDAPEngine::get_acl_strategy() const
4081{
4082 //This is based on the assumption that the default acl strategy in
4083 // get_perms_from_aclspec, will take care. Extra acl spec is not required.
4084 return nullptr;
4085}
4086
4087rgw::auth::RemoteApplier::AuthInfo
4088rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken& token) const noexcept
4089{
4090 /* The short form of "using" can't be used here -- we're aliasing a class'
4091 * member. */
4092 using acct_privilege_t = \
4093 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t;
4094
4095 return rgw::auth::RemoteApplier::AuthInfo {
4096 rgw_user(token.id),
4097 token.id,
4098 RGW_PERM_FULL_CONTROL,
4099 acct_privilege_t::IS_PLAIN_ACCT,
4100 TYPE_LDAP
4101 };
4102}
4103
4104rgw::auth::Engine::result_t
31f18b77
FG
4105rgw::auth::s3::LDAPEngine::authenticate(
4106 const boost::string_view& access_key_id,
4107 const boost::string_view& signature,
4108 const string_to_sign_t& string_to_sign,
4109 const signature_factory_t&,
4110 const completer_factory_t& completer_factory,
4111 const req_state* const s) const
7c673cae
FG
4112{
4113 /* boost filters and/or string_ref may throw on invalid input */
4114 rgw::RGWToken base64_token;
4115 try {
4116 base64_token = rgw::from_base64(access_key_id);
4117 } catch (...) {
4118 base64_token = std::string("");
4119 }
4120
4121 if (! base64_token.valid()) {
4122 return result_t::deny();
4123 }
4124
4125 //TODO: Uncomment, when we have a migration plan in place.
4126 //Check if a user of type other than 'ldap' is already present, if yes, then
4127 //return error.
4128 /*RGWUserInfo user_info;
4129 user_info.user_id = base64_token.id;
4130 if (rgw_get_user_info_by_uid(store, user_info.user_id, user_info) >= 0) {
4131 if (user_info.type != TYPE_LDAP) {
4132 ldout(cct, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl;
4133 return nullptr;
4134 }
4135 }*/
4136
4137 if (ldh->auth(base64_token.id, base64_token.key) != 0) {
4138 return result_t::deny();
4139 }
4140
4141 auto apl = apl_factory->create_apl_remote(cct, s, get_acl_strategy(),
4142 get_creds_info(base64_token));
31f18b77 4143 return result_t::grant(std::move(apl), completer_factory(boost::none));
7c673cae
FG
4144}
4145
4146
31f18b77 4147/* LocalEndgine */
7c673cae 4148rgw::auth::Engine::result_t
31f18b77
FG
4149rgw::auth::s3::LocalEngine::authenticate(
4150 const boost::string_view& _access_key_id,
4151 const boost::string_view& signature,
4152 const string_to_sign_t& string_to_sign,
4153 const signature_factory_t& signature_factory,
4154 const completer_factory_t& completer_factory,
4155 const req_state* const s) const
7c673cae
FG
4156{
4157 /* get the user info */
4158 RGWUserInfo user_info;
31f18b77
FG
4159 /* TODO(rzarzynski): we need to have string-view taking variant. */
4160 const std::string access_key_id = _access_key_id.to_string();
7c673cae
FG
4161 if (rgw_get_user_info_by_access_key(store, access_key_id, user_info) < 0) {
4162 ldout(cct, 5) << "error reading user info, uid=" << access_key_id
4163 << " can't authenticate" << dendl;
4164 return result_t::deny(-ERR_INVALID_ACCESS_KEY);
4165 }
4166 //TODO: Uncomment, when we have a migration plan in place.
4167 /*else {
4168 if (s->user->type != TYPE_RGW) {
31f18b77
FG
4169 ldout(cct, 10) << "ERROR: User id of type: " << s->user->type
4170 << " is present" << dendl;
7c673cae
FG
4171 throw -EPERM;
4172 }
4173 }*/
4174
4175 const auto iter = user_info.access_keys.find(access_key_id);
4176 if (iter == std::end(user_info.access_keys)) {
4177 ldout(cct, 0) << "ERROR: access key not encoded in user info" << dendl;
4178 return result_t::deny(-EPERM);
4179 }
4180 const RGWAccessKey& k = iter->second;
4181
31f18b77
FG
4182 const VersionAbstractor::server_signature_t server_signature = \
4183 signature_factory(cct, k.key, string_to_sign);
7c673cae 4184
31f18b77
FG
4185 ldout(cct, 15) << "string_to_sign="
4186 << rgw::crypt_sanitize::log_content{string_to_sign}
4187 << dendl;
4188 ldout(cct, 15) << "server signature=" << server_signature << dendl;
4189 ldout(cct, 15) << "client signature=" << signature << dendl;
4190 ldout(cct, 15) << "compare=" << signature.compare(server_signature) << dendl;
7c673cae 4191
31f18b77 4192 if (static_cast<boost::string_view>(server_signature) != signature) {
7c673cae
FG
4193 return result_t::deny(-ERR_SIGNATURE_NO_MATCH);
4194 }
4195
4196 auto apl = apl_factory->create_apl_local(cct, s, user_info, k.subuser);
31f18b77 4197 return result_t::grant(std::move(apl), completer_factory(k.key));
7c673cae 4198}
d2e6a577
FG
4199
4200bool rgw::auth::s3::S3AnonymousEngine::is_applicable(
4201 const req_state* s
4202) const noexcept {
4203 if (s->op == OP_OPTIONS) {
4204 return true;
4205 }
4206
4207 AwsVersion version;
4208 AwsRoute route;
4209 std::tie(version, route) = discover_aws_flavour(s->info);
4210
4211 return route == AwsRoute::QUERY_STRING && version == AwsVersion::UNKOWN;
4212}