1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
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"
13 #include "common/backport14.h"
14 #include <boost/algorithm/string.hpp>
15 #include <boost/algorithm/string/replace.hpp>
16 #include <boost/utility/string_view.hpp>
19 #include "rgw_rest_s3.h"
20 #include "rgw_rest_s3website.h"
21 #include "rgw_auth_s3.h"
23 #include "rgw_policy_s3.h"
26 #include "rgw_cors_s3.h"
27 #include "rgw_tag_s3.h"
29 #include "rgw_client_io.h"
31 #include "rgw_keystone.h"
32 #include "rgw_auth_keystone.h"
33 #include "rgw_auth_registry.h"
35 #include "rgw_es_query.h"
37 #include <typeinfo> // for 'typeid'
40 #include "rgw_token.h"
41 #include "rgw_rest_role.h"
42 #include "rgw_crypt.h"
43 #include "rgw_crypt_sanitize.h"
45 #include "include/assert.h"
47 #define dout_context g_ceph_context
48 #define dout_subsys ceph_subsys_rgw
51 using namespace ceph::crypto
;
55 void list_all_buckets_start(struct req_state
*s
)
57 s
->formatter
->open_array_section_in_ns("ListAllMyBucketsResult", XMLNS_AWS_S3
);
60 void list_all_buckets_end(struct req_state
*s
)
62 s
->formatter
->close_section();
65 void dump_bucket(struct req_state
*s
, RGWBucketEnt
& obj
)
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();
73 void rgw_get_errno_s3(rgw_http_error
*e
, int err_no
)
75 rgw_http_errors::const_iterator r
= rgw_http_s3_errors
.find(err_no
);
77 if (r
!= rgw_http_s3_errors
.end()) {
78 e
->http_ret
= r
->second
.first
;
79 e
->s3_code
= r
->second
.second
;
82 e
->s3_code
= "UnknownError";
86 struct response_attr_param
{
88 const char *http_attr
;
91 static 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"},
101 int 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
);
112 dump_content_length(s
, 0);
113 dump_redirect(s
, s
->redirect
);
117 return RGWGetObj_ObjStore_S3::send_response_data(bl
, bl_ofs
, bl_len
);
121 int RGWGetObj_ObjStore_S3Website::send_response_data_error()
123 return RGWGetObj_ObjStore_S3::send_response_data_error();
126 int RGWGetObj_ObjStore_S3::get_params()
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");
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");
138 return RGWGetObj_ObjStore::get_params();
141 int RGWGetObj_ObjStore_S3::send_response_data_error()
144 return send_response_data(bl
, 0 , 0);
148 int decode_attr_bl_single_value(map
<string
, bufferlist
>& attrs
, const char *attr_name
, T
*result
, T def_val
)
150 map
<string
, bufferlist
>::iterator iter
= attrs
.find(attr_name
);
151 if (iter
== attrs
.end()) {
155 bufferlist
& bl
= iter
->second
;
156 if (bl
.length() == 0) {
160 bufferlist::iterator bliter
= bl
.begin();
162 ::decode(*result
, bliter
);
163 } catch (buffer::error
& err
) {
169 int RGWGetObj_ObjStore_S3::send_response_data(bufferlist
& bl
, off_t bl_ofs
,
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
;
181 if (custom_http_ret
) {
182 set_req_state_err(s
, 0);
183 dump_errno(s
, custom_http_ret
);
185 set_req_state_err(s
, (partial_content
&& !op_ret
) ? STATUS_PARTIAL_CONTENT
194 dump_range(s
, start
, end
, s
->obj_size
);
196 if (s
->system_request
&&
197 s
->info
.args
.exists(RGW_SYS_PARAM_PREFIX
"prepend-metadata")) {
199 dump_header(s
, "Rgwx-Object-Size", (long long)total_len
);
203 * in this case, we're not returning the object's content, only the prepended
209 /* JSON encode object metadata */
211 jf
.open_object_section("obj_metadata");
212 encode_json("attrs", attrs
, &jf
);
214 encode_json("mtime", ut
, &jf
);
218 metadata_bl
.append(ss
.str());
219 dump_header(s
, "Rgwx-Embedded-Metadata-Len", metadata_bl
.length());
220 total_len
+= metadata_bl
.length();
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
);
227 int r
= decode_attr_bl_single_value(attrs
, RGW_ATTR_PG_VER
, &pg_ver
, (uint64_t)0);
229 ldout(s
->cct
, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl
;
231 dump_header(s
, "Rgwx-Obj-PG-Ver", pg_ver
);
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);
236 ldout(s
->cct
, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl
;
238 if (source_zone_short_id
!= 0) {
239 dump_header(s
, "Rgwx-Source-Zone-Short-Id", source_zone_short_id
);
243 for (auto &it
: crypt_http_responses
)
244 dump_header(s
, it
.first
, it
.second
);
246 dump_content_length(s
, total_len
);
247 dump_last_modified(s
, lastmod
);
248 if (!version_id
.empty()) {
249 dump_header(s
, "x-amz-version-id", version_id
);
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
259 dump_etag(s
, lo_etag
);
261 auto iter
= attrs
.find(RGW_ATTR_ETAG
);
262 if (iter
!= attrs
.end()) {
263 dump_etag(s
, iter
->second
);
267 for (struct response_attr_param
*p
= resp_attr_params
; p
->param
; p
++) {
269 string val
= s
->info
.args
.get(p
->param
, &exists
);
271 if (strcmp(p
->param
, "response-content-type") != 0) {
272 response_attrs
[p
->http_attr
] = val
;
274 content_type_str
= val
;
275 content_type
= content_type_str
.c_str();
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();
288 } else if (iter
->first
.compare(RGW_ATTR_CONTENT_TYPE
) == 0) {
289 /* Special handling for content_type. */
291 content_type
= iter
->second
.c_str();
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");
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
);
301 } else if (iter
->first
.compare(RGW_ATTR_TAGS
) == 0) {
304 bufferlist::iterator it
= iter
->second
.begin();
306 } catch (buffer::error
&err
) {
307 ldout(s
->cct
,0) << "Error caught buffer::error couldn't decode TagSet " << dendl
;
309 dump_header(s
, RGW_AMZ_TAG_COUNT
, obj_tags
.count());
315 for (riter
= response_attrs
.begin(); riter
!= response_attrs
.end();
317 dump_header(s
, riter
->first
, riter
->second
);
320 if (op_ret
== -ERR_NOT_MODIFIED
) {
324 content_type
= "binary/octet-stream";
326 end_header(s
, this, content_type
);
329 if (metadata_bl
.length()) {
330 dump_body(s
, metadata_bl
);
335 if (get_data
&& !op_ret
) {
336 int r
= dump_body(s
, bl
.c_str() + bl_ofs
, bl_len
);
344 int RGWGetObj_ObjStore_S3::get_decrypt_filter(std::unique_ptr
<RGWGetDataCB
> *filter
, RGWGetDataCB
* cb
, bufferlist
* manifest_bl
)
346 if (skip_decrypt
) { // bypass decryption for multisite sync requests
351 std::unique_ptr
<BlockCrypt
> block_crypt
;
352 res
= rgw_s3_prepare_decrypt(s
, attrs
, &block_crypt
, crypt_http_responses
);
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));
358 if (manifest_bl
!= nullptr) {
359 res
= f
->read_manifest(*manifest_bl
);
361 *filter
= std::move(f
);
370 void RGWGetObjTags_ObjStore_S3::send_response_data(bufferlist
& bl
)
373 end_header(s
, this, "application/xml");
376 s
->formatter
->open_object_section_in_ns("Tagging", XMLNS_AWS_S3
);
377 s
->formatter
->open_object_section("TagSet");
379 RGWObjTagSet_S3 tagset
;
380 bufferlist::iterator iter
= bl
.begin();
383 } catch (buffer::error
& err
) {
384 ldout(s
->cct
,0) << "ERROR: caught buffer::error, couldn't decode TagSet" << dendl
;
388 tagset
.dump_xml(s
->formatter
);
390 s
->formatter
->close_section();
391 s
->formatter
->close_section();
392 rgw_flush_formatter_and_reset(s
, s
->formatter
);
396 int RGWPutObjTags_ObjStore_S3::get_params()
398 RGWObjTagsXMLParser parser
;
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);
413 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
415 if (!parser
.parse(data
, len
, 1)) {
416 return -ERR_MALFORMED_XML
;
419 RGWObjTagSet_S3
*obj_tags_s3
;
420 RGWObjTagging_S3
*tagging
;
422 tagging
= static_cast<RGWObjTagging_S3
*>(parser
.find_first("Tagging"));
423 obj_tags_s3
= static_cast<RGWObjTagSet_S3
*>(tagging
->find_first("TagSet"));
425 return -ERR_MALFORMED_XML
;
429 r
= obj_tags_s3
->rebuild(obj_tags
);
433 obj_tags
.encode(tags_bl
);
434 ldout(s
->cct
, 20) << "Read " << obj_tags
.count() << "tags" << dendl
;
439 void RGWPutObjTags_ObjStore_S3::send_response()
442 set_req_state_err(s
, op_ret
);
444 end_header(s
, this, "application/xml");
449 void RGWDeleteObjTags_ObjStore_S3::send_response()
455 r
= STATUS_NO_CONTENT
;
457 set_req_state_err(s
, r
);
462 void RGWListBuckets_ObjStore_S3::send_response_begin(bool has_buckets
)
465 set_req_state_err(s
, op_ret
);
468 end_header(s
, NULL
, "application/xml");
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");
478 void RGWListBuckets_ObjStore_S3::send_response_data(RGWUserBuckets
& buckets
)
483 map
<string
, RGWBucketEnt
>& m
= buckets
.get_buckets();
484 map
<string
, RGWBucketEnt
>::iterator iter
;
486 for (iter
= m
.begin(); iter
!= m
.end(); ++iter
) {
487 RGWBucketEnt obj
= iter
->second
;
490 rgw_flush_formatter(s
, s
->formatter
);
493 void RGWListBuckets_ObjStore_S3::send_response_end()
496 s
->formatter
->close_section();
497 list_all_buckets_end(s
);
498 rgw_flush_formatter_and_reset(s
, s
->formatter
);
502 int RGWGetUsage_ObjStore_S3::get_params()
504 start_date
= s
->info
.args
.get("start-date");
505 end_date
= s
->info
.args
.get("end-date");
509 static void dump_usage_categories_info(Formatter
*formatter
, const rgw_usage_log_entry
& entry
, map
<string
, bool> *categories
)
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
))
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
525 formatter
->close_section(); // Category
528 static void dump_usage_bucket_info(Formatter
*formatter
, const std::string
& name
, const cls_user_bucket_entry
& entry
)
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
537 void RGWGetUsage_ObjStore_S3::send_response()
540 set_req_state_err(s
, op_ret
);
543 end_header(s
, this, "application/xml");
548 Formatter
*formatter
= s
->formatter
;
550 bool user_section_open
= false;
552 formatter
->open_object_section("Usage");
553 if (show_log_entries
) {
554 formatter
->open_array_section("Entries");
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
;
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();
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
;
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
582 summary_map
[ub
.user
].aggregate(entry
, &categories
);
585 if (show_log_entries
) {
586 if (user_section_open
) {
587 formatter
->close_section(); // buckets
588 formatter
->close_section(); //user
590 formatter
->close_section(); // entries
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
);
608 formatter
->close_section(); // total
609 formatter
->close_section(); // user
612 if (s
->cct
->_conf
->rgw_rest_getusage_op_compat
) {
613 formatter
->open_object_section("Stats");
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
);
620 if (s
->cct
->_conf
->rgw_rest_getusage_op_compat
) {
621 formatter
->close_section(); //Stats
624 formatter
->close_section(); // summary
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
);
634 formatter
->close_section(); // Buckets
635 formatter
->close_section(); // User
636 formatter
->close_section(); // CapacityUsed
638 formatter
->close_section(); // usage
639 rgw_flush_formatter_and_reset(s
, s
->formatter
);
642 int RGWListBucket_ObjStore_S3::get_params()
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");
649 marker
.name
= s
->info
.args
.get("key-marker");
650 marker
.instance
= s
->info
.args
.get("version-id-marker");
654 s
->info
.args
.get_bool("allow-unordered", &allow_unordered
, false);
656 delimiter
= s
->info
.args
.get("delimiter");
658 max_keys
= s
->info
.args
.get("max-keys");
659 op_ret
= parse_max_keys();
664 encoding_type
= s
->info
.args
.get("encoding-type");
665 if (s
->system_request
) {
666 s
->info
.args
.get_bool("objs-container", &objs_container
, false);
667 const char *shard_id_str
= s
->info
.env
->get("HTTP_RGWX_SHARD_ID");
670 shard_id
= strict_strtol(shard_id_str
, 10, &err
);
672 ldout(s
->cct
, 5) << "bad shard id specified: " << shard_id_str
<< dendl
;
676 shard_id
= s
->bucket_instance_shard_id
;
683 void RGWListBucket_ObjStore_S3::send_versioned_response()
685 s
->formatter
->open_object_section_in_ns("ListVersionsResult", XMLNS_AWS_S3
);
686 if (!s
->bucket_tenant
.empty())
687 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
688 s
->formatter
->dump_string("Name", s
->bucket_name
);
689 s
->formatter
->dump_string("Prefix", prefix
);
690 s
->formatter
->dump_string("KeyMarker", marker
.name
);
691 s
->formatter
->dump_string("VersionIdMarker", marker
.instance
);
692 if (is_truncated
&& !next_marker
.empty()) {
693 s
->formatter
->dump_string("NextKeyMarker", next_marker
.name
);
694 s
->formatter
->dump_string("NextVersionIdMarker", next_marker
.instance
);
696 s
->formatter
->dump_int("MaxKeys", max
);
697 if (!delimiter
.empty())
698 s
->formatter
->dump_string("Delimiter", delimiter
);
700 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
703 bool encode_key
= false;
704 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
705 s
->formatter
->dump_string("EncodingType", "url");
710 if (objs_container
) {
711 s
->formatter
->open_array_section("Entries");
714 vector
<rgw_bucket_dir_entry
>::iterator iter
;
715 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
716 const char *section_name
= (iter
->is_delete_marker() ? "DeleteMarker"
718 s
->formatter
->open_object_section(section_name
);
719 if (objs_container
) {
720 s
->formatter
->dump_bool("IsDeleteMarker", iter
->is_delete_marker());
722 rgw_obj_key
key(iter
->key
);
725 url_encode(key
.name
, key_name
);
726 s
->formatter
->dump_string("Key", key_name
);
728 s
->formatter
->dump_string("Key", key
.name
);
730 string version_id
= key
.instance
;
731 if (version_id
.empty()) {
734 if (s
->system_request
) {
735 if (iter
->versioned_epoch
> 0) {
736 s
->formatter
->dump_int("VersionedEpoch", iter
->versioned_epoch
);
738 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
739 utime_t
ut(iter
->meta
.mtime
);
740 ut
.gmtime_nsec(s
->formatter
->dump_stream("RgwxMtime"));
742 s
->formatter
->dump_string("VersionId", version_id
);
743 s
->formatter
->dump_bool("IsLatest", iter
->is_current());
744 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
745 if (!iter
->is_delete_marker()) {
746 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
747 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
748 s
->formatter
->dump_string("StorageClass", "STANDARD");
750 dump_owner(s
, iter
->meta
.owner
, iter
->meta
.owner_display_name
);
751 s
->formatter
->close_section();
753 if (objs_container
) {
754 s
->formatter
->close_section();
757 if (!common_prefixes
.empty()) {
758 map
<string
, bool>::iterator pref_iter
;
759 for (pref_iter
= common_prefixes
.begin();
760 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
761 s
->formatter
->open_array_section("CommonPrefixes");
762 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
763 s
->formatter
->close_section();
767 s
->formatter
->close_section();
768 rgw_flush_formatter_and_reset(s
, s
->formatter
);
771 void RGWListBucket_ObjStore_S3::send_response()
774 set_req_state_err(s
, op_ret
);
777 end_header(s
, this, "application/xml");
783 send_versioned_response();
787 s
->formatter
->open_object_section_in_ns("ListBucketResult", XMLNS_AWS_S3
);
788 if (!s
->bucket_tenant
.empty())
789 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
790 s
->formatter
->dump_string("Name", s
->bucket_name
);
791 s
->formatter
->dump_string("Prefix", prefix
);
792 s
->formatter
->dump_string("Marker", marker
.name
);
793 if (is_truncated
&& !next_marker
.empty())
794 s
->formatter
->dump_string("NextMarker", next_marker
.name
);
795 s
->formatter
->dump_int("MaxKeys", max
);
796 if (!delimiter
.empty())
797 s
->formatter
->dump_string("Delimiter", delimiter
);
799 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
802 bool encode_key
= false;
803 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
804 s
->formatter
->dump_string("EncodingType", "url");
809 vector
<rgw_bucket_dir_entry
>::iterator iter
;
810 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
811 rgw_obj_key
key(iter
->key
);
812 s
->formatter
->open_array_section("Contents");
815 url_encode(key
.name
, key_name
);
816 s
->formatter
->dump_string("Key", key_name
);
818 s
->formatter
->dump_string("Key", key
.name
);
820 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
821 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
822 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
823 s
->formatter
->dump_string("StorageClass", "STANDARD");
824 dump_owner(s
, iter
->meta
.owner
, iter
->meta
.owner_display_name
);
825 if (s
->system_request
) {
826 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
828 s
->formatter
->close_section();
830 if (!common_prefixes
.empty()) {
831 map
<string
, bool>::iterator pref_iter
;
832 for (pref_iter
= common_prefixes
.begin();
833 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
834 s
->formatter
->open_array_section("CommonPrefixes");
835 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
836 s
->formatter
->close_section();
840 s
->formatter
->close_section();
841 rgw_flush_formatter_and_reset(s
, s
->formatter
);
844 void RGWGetBucketLogging_ObjStore_S3::send_response()
847 end_header(s
, this, "application/xml");
850 s
->formatter
->open_object_section_in_ns("BucketLoggingStatus", XMLNS_AWS_S3
);
851 s
->formatter
->close_section();
852 rgw_flush_formatter_and_reset(s
, s
->formatter
);
855 void RGWGetBucketLocation_ObjStore_S3::send_response()
861 RGWZoneGroup zonegroup
;
864 int ret
= store
->get_zonegroup(s
->bucket_info
.zonegroup
, zonegroup
);
866 api_name
= zonegroup
.api_name
;
868 if (s
->bucket_info
.zonegroup
!= "default") {
869 api_name
= s
->bucket_info
.zonegroup
;
873 s
->formatter
->dump_format_ns("LocationConstraint", XMLNS_AWS_S3
,
874 "%s", api_name
.c_str());
875 rgw_flush_formatter_and_reset(s
, s
->formatter
);
878 void RGWGetBucketVersioning_ObjStore_S3::send_response()
881 end_header(s
, this, "application/xml");
884 s
->formatter
->open_object_section_in_ns("VersioningConfiguration", XMLNS_AWS_S3
);
886 const char *status
= (versioning_enabled
? "Enabled" : "Suspended");
887 s
->formatter
->dump_string("Status", status
);
889 s
->formatter
->close_section();
890 rgw_flush_formatter_and_reset(s
, s
->formatter
);
893 class RGWSetBucketVersioningParser
: public RGWXMLParser
895 XMLObj
*alloc_obj(const char *el
) override
{
900 RGWSetBucketVersioningParser() {}
901 ~RGWSetBucketVersioningParser() override
{}
903 int get_versioning_status(bool *status
) {
904 XMLObj
*config
= find_first("VersioningConfiguration");
910 XMLObj
*field
= config
->find_first("Status");
914 string
& s
= field
->get_data();
916 if (stringcasecmp(s
, "Enabled") == 0) {
918 } else if (stringcasecmp(s
, "Suspended") != 0) {
926 int RGWSetBucketVersioning_ObjStore_S3::get_params()
928 char *data
= nullptr;
931 rgw_rest_read_all_input(s
, &data
, &len
, s
->cct
->_conf
->rgw_max_put_param_size
, false);
936 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
938 r
= do_aws4_auth_completion();
943 RGWSetBucketVersioningParser parser
;
945 if (!parser
.init()) {
946 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
951 if (!parser
.parse(data
, len
, 1)) {
952 ldout(s
->cct
, 10) << "failed to parse data: " << data
<< dendl
;
957 if (!store
->is_meta_master()) {
958 /* only need to keep this data around if we're not meta master */
959 in_data
.append(data
, len
);
962 r
= parser
.get_versioning_status(&enable_versioning
);
967 void RGWSetBucketVersioning_ObjStore_S3::send_response()
970 set_req_state_err(s
, op_ret
);
975 int RGWSetBucketWebsite_ObjStore_S3::get_params()
977 char *data
= nullptr;
979 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
980 int r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
986 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
988 r
= do_aws4_auth_completion();
993 bufferptr
in_ptr(data
, len
);
994 in_data
.append(in_ptr
);
996 RGWXMLDecoder::XMLParser parser
;
997 if (!parser
.init()) {
998 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
1002 if (!parser
.parse(data
, len
, 1)) {
1003 string
str(data
, len
);
1004 ldout(s
->cct
, 5) << "failed to parse xml: " << str
<< dendl
;
1009 RGWXMLDecoder::decode_xml("WebsiteConfiguration", website_conf
, &parser
, true);
1010 } catch (RGWXMLDecoder::err
& err
) {
1011 string
str(data
, len
);
1012 ldout(s
->cct
, 5) << "unexpected xml: " << str
<< dendl
;
1019 void RGWSetBucketWebsite_ObjStore_S3::send_response()
1022 set_req_state_err(s
, op_ret
);
1027 void RGWDeleteBucketWebsite_ObjStore_S3::send_response()
1030 op_ret
= STATUS_NO_CONTENT
;
1032 set_req_state_err(s
, op_ret
);
1037 void RGWGetBucketWebsite_ObjStore_S3::send_response()
1040 set_req_state_err(s
, op_ret
);
1042 end_header(s
, this, "application/xml");
1049 RGWBucketWebsiteConf
& conf
= s
->bucket_info
.website_conf
;
1051 s
->formatter
->open_object_section_in_ns("WebsiteConfiguration", XMLNS_AWS_S3
);
1052 conf
.dump_xml(s
->formatter
);
1053 s
->formatter
->close_section(); // WebsiteConfiguration
1054 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1057 static void dump_bucket_metadata(struct req_state
*s
, RGWBucketEnt
& bucket
)
1059 dump_header(s
, "X-RGW-Object-Count", static_cast<long long>(bucket
.count
));
1060 dump_header(s
, "X-RGW-Bytes-Used", static_cast<long long>(bucket
.size
));
1063 void RGWStatBucket_ObjStore_S3::send_response()
1066 dump_bucket_metadata(s
, bucket
);
1069 set_req_state_err(s
, op_ret
);
1072 end_header(s
, this);
1076 static int create_s3_policy(struct req_state
*s
, RGWRados
*store
,
1077 RGWAccessControlPolicy_S3
& s3policy
,
1080 if (s
->has_acl_header
) {
1081 if (!s
->canned_acl
.empty())
1082 return -ERR_INVALID_REQUEST
;
1084 return s3policy
.create_from_headers(store
, s
->info
.env
, owner
);
1087 return s3policy
.create_canned(owner
, s
->bucket_owner
, s
->canned_acl
);
1090 class RGWLocationConstraint
: public XMLObj
1093 RGWLocationConstraint() {}
1094 ~RGWLocationConstraint() override
{}
1095 bool xml_end(const char *el
) override
{
1099 location_constraint
= get_data();
1104 string location_constraint
;
1107 class RGWCreateBucketConfig
: public XMLObj
1110 RGWCreateBucketConfig() {}
1111 ~RGWCreateBucketConfig() override
{}
1114 class RGWCreateBucketParser
: public RGWXMLParser
1116 XMLObj
*alloc_obj(const char *el
) override
{
1121 RGWCreateBucketParser() {}
1122 ~RGWCreateBucketParser() override
{}
1124 bool get_location_constraint(string
& zone_group
) {
1125 XMLObj
*config
= find_first("CreateBucketConfiguration");
1129 XMLObj
*constraint
= config
->find_first("LocationConstraint");
1133 zone_group
= constraint
->get_data();
1139 int RGWCreateBucket_ObjStore_S3::get_params()
1141 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1143 int r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
1150 char *data
= nullptr;
1152 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
1153 op_ret
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
1155 if ((op_ret
< 0) && (op_ret
!= -ERR_LENGTH_REQUIRED
))
1158 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
1160 const int auth_ret
= do_aws4_auth_completion();
1165 bufferptr
in_ptr(data
, len
);
1166 in_data
.append(in_ptr
);
1169 RGWCreateBucketParser parser
;
1171 if (!parser
.init()) {
1172 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
1176 bool success
= parser
.parse(data
, len
, 1);
1177 ldout(s
->cct
, 20) << "create bucket input data=" << data
<< dendl
;
1180 ldout(s
->cct
, 0) << "failed to parse input: " << data
<< dendl
;
1184 if (!parser
.get_location_constraint(location_constraint
)) {
1185 ldout(s
->cct
, 0) << "provided input did not specify location constraint correctly" << dendl
;
1189 ldout(s
->cct
, 10) << "create bucket location constraint: "
1190 << location_constraint
<< dendl
;
1193 size_t pos
= location_constraint
.find(':');
1194 if (pos
!= string::npos
) {
1195 placement_rule
= location_constraint
.substr(pos
+ 1);
1196 location_constraint
= location_constraint
.substr(0, pos
);
1202 void RGWCreateBucket_ObjStore_S3::send_response()
1204 if (op_ret
== -ERR_BUCKET_EXISTS
)
1207 set_req_state_err(s
, op_ret
);
1214 if (s
->system_request
) {
1215 JSONFormatter f
; /* use json formatter for system requests output */
1217 f
.open_object_section("info");
1218 encode_json("entry_point_object_ver", ep_objv
, &f
);
1219 encode_json("object_ver", info
.objv_tracker
.read_version
, &f
);
1220 encode_json("bucket_info", info
, &f
);
1222 rgw_flush_formatter_and_reset(s
, &f
);
1226 void RGWDeleteBucket_ObjStore_S3::send_response()
1230 r
= STATUS_NO_CONTENT
;
1232 set_req_state_err(s
, r
);
1234 end_header(s
, this);
1236 if (s
->system_request
) {
1237 JSONFormatter f
; /* use json formatter for system requests output */
1239 f
.open_object_section("info");
1240 encode_json("object_ver", objv_tracker
.read_version
, &f
);
1242 rgw_flush_formatter_and_reset(s
, &f
);
1246 int RGWPutObj_ObjStore_S3::get_params()
1249 return -ERR_LENGTH_REQUIRED
;
1251 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
1252 map
<string
, bufferlist
> src_attrs
;
1256 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1257 ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
1263 if_match
= s
->info
.env
->get("HTTP_IF_MATCH");
1264 if_nomatch
= s
->info
.env
->get("HTTP_IF_NONE_MATCH");
1265 copy_source
= url_decode(s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE", ""));
1266 copy_source_range
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE");
1268 /* handle x-amz-copy-source */
1269 boost::string_view
cs_view(copy_source
);
1270 if (! cs_view
.empty()) {
1271 if (cs_view
[0] == '/')
1272 cs_view
.remove_prefix(1);
1273 copy_source_bucket_name
= cs_view
.to_string();
1274 pos
= copy_source_bucket_name
.find("/");
1275 if (pos
== std::string::npos
) {
1277 ldout(s
->cct
, 5) << "x-amz-copy-source bad format" << dendl
;
1280 copy_source_object_name
=
1281 copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
1282 copy_source_bucket_name
= copy_source_bucket_name
.substr(0, pos
);
1283 #define VERSION_ID_STR "?versionId="
1284 pos
= copy_source_object_name
.find(VERSION_ID_STR
);
1285 if (pos
== std::string::npos
) {
1286 copy_source_object_name
= url_decode(copy_source_object_name
);
1288 copy_source_version_id
=
1289 copy_source_object_name
.substr(pos
+ sizeof(VERSION_ID_STR
) - 1);
1290 copy_source_object_name
=
1291 url_decode(copy_source_object_name
.substr(0, pos
));
1293 pos
= copy_source_bucket_name
.find(":");
1294 if (pos
== std::string::npos
) {
1295 copy_source_tenant_name
= s
->src_tenant_name
;
1297 copy_source_tenant_name
= copy_source_bucket_name
.substr(0, pos
);
1298 copy_source_bucket_name
= copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
1299 if (copy_source_bucket_name
.empty()) {
1301 ldout(s
->cct
, 5) << "source bucket name is empty" << dendl
;
1305 ret
= store
->get_bucket_info(obj_ctx
,
1306 copy_source_tenant_name
,
1307 copy_source_bucket_name
,
1308 copy_source_bucket_info
,
1311 ldout(s
->cct
, 5) << __func__
<< "(): get_bucket_info() returned ret=" << ret
<< dendl
;
1315 /* handle x-amz-copy-source-range */
1317 if (copy_source_range
) {
1318 string range
= copy_source_range
;
1319 pos
= range
.find("=");
1320 if (pos
== std::string::npos
) {
1322 ldout(s
->cct
, 5) << "x-amz-copy-source-range bad format" << dendl
;
1325 range
= range
.substr(pos
+ 1);
1326 pos
= range
.find("-");
1327 if (pos
== std::string::npos
) {
1329 ldout(s
->cct
, 5) << "x-amz-copy-source-range bad format" << dendl
;
1332 string first
= range
.substr(0, pos
);
1333 string last
= range
.substr(pos
+ 1);
1334 copy_source_range_fst
= strtoull(first
.c_str(), NULL
, 10);
1335 copy_source_range_lst
= strtoull(last
.c_str(), NULL
, 10);
1340 /* handle object tagging */
1341 auto tag_str
= s
->info
.env
->get("HTTP_X_AMZ_TAGGING");
1343 obj_tags
= ceph::make_unique
<RGWObjTags
>();
1344 ret
= obj_tags
->set_from_string(tag_str
);
1346 ldout(s
->cct
,0) << "setting obj tags failed with " << ret
<< dendl
;
1347 if (ret
== -ERR_INVALID_TAG
){
1348 ret
= -EINVAL
; //s3 returns only -EINVAL for PUT requests
1355 return RGWPutObj_ObjStore::get_params();
1358 int RGWPutObj_ObjStore_S3::get_data(bufferlist
& bl
)
1360 const int ret
= RGWPutObj_ObjStore::get_data(bl
);
1362 const int ret_auth
= do_aws4_auth_completion();
1371 static int get_success_retcode(int code
)
1375 return STATUS_CREATED
;
1377 return STATUS_NO_CONTENT
;
1382 void RGWPutObj_ObjStore_S3::send_response()
1385 set_req_state_err(s
, op_ret
);
1388 if (s
->cct
->_conf
->rgw_s3_success_create_obj_status
) {
1389 op_ret
= get_success_retcode(
1390 s
->cct
->_conf
->rgw_s3_success_create_obj_status
);
1391 set_req_state_err(s
, op_ret
);
1393 if (copy_source
.empty()) {
1396 dump_content_length(s
, 0);
1397 for (auto &it
: crypt_http_responses
)
1398 dump_header(s
, it
.first
, it
.second
);
1401 end_header(s
, this, "application/xml");
1405 time_t secs
= (time_t)ut
.sec();
1406 gmtime_r(&secs
, &tmp
);
1407 char buf
[TIME_BUF_SIZE
];
1408 s
->formatter
->open_object_section_in_ns("CopyPartResult",
1409 "http://s3.amazonaws.com/doc/2006-03-01/");
1410 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%T.000Z", &tmp
) > 0) {
1411 s
->formatter
->dump_string("LastModified", buf
);
1413 s
->formatter
->dump_string("ETag", etag
);
1414 s
->formatter
->close_section();
1415 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1419 if (s
->system_request
&& !real_clock::is_zero(mtime
)) {
1420 dump_epoch_header(s
, "Rgwx-Mtime", mtime
);
1422 end_header(s
, this);
1425 static inline int get_obj_attrs(RGWRados
*store
, struct req_state
*s
, rgw_obj
& obj
, map
<string
, bufferlist
>& attrs
)
1427 RGWRados::Object
op_target(store
, s
->bucket_info
, *static_cast<RGWObjectCtx
*>(s
->obj_ctx
), obj
);
1428 RGWRados::Object::Read
read_op(&op_target
);
1430 read_op
.params
.attrs
= &attrs
;
1432 return read_op
.prepare();
1435 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const std::string
& value
)
1439 attrs
.emplace(key
, std::move(bl
));
1442 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const char* value
)
1446 attrs
.emplace(key
, std::move(bl
));
1449 int RGWPutObj_ObjStore_S3::get_decrypt_filter(
1450 std::unique_ptr
<RGWGetDataCB
>* filter
,
1452 map
<string
, bufferlist
>& attrs
,
1453 bufferlist
* manifest_bl
)
1455 std::map
<std::string
, std::string
> crypt_http_responses_unused
;
1458 std::unique_ptr
<BlockCrypt
> block_crypt
;
1459 res
= rgw_s3_prepare_decrypt(s
, attrs
, &block_crypt
, crypt_http_responses_unused
);
1461 if (block_crypt
!= nullptr) {
1462 auto f
= std::unique_ptr
<RGWGetObj_BlockDecrypt
>(new RGWGetObj_BlockDecrypt(s
->cct
, cb
, std::move(block_crypt
)));
1463 //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
1465 if (manifest_bl
!= nullptr) {
1466 res
= f
->read_manifest(*manifest_bl
);
1468 *filter
= std::move(f
);
1477 int RGWPutObj_ObjStore_S3::get_encrypt_filter(
1478 std::unique_ptr
<RGWPutObjDataProcessor
>* filter
,
1479 RGWPutObjDataProcessor
* cb
)
1482 RGWPutObjProcessor_Multipart
* multi_processor
=dynamic_cast<RGWPutObjProcessor_Multipart
*>(cb
);
1483 if (multi_processor
!= nullptr) {
1484 RGWMPObj
* mp
= nullptr;
1485 multi_processor
->get_mp(&mp
);
1486 if (mp
!= nullptr) {
1487 map
<string
, bufferlist
> xattrs
;
1489 meta_oid
= mp
->get_meta();
1492 obj
.init_ns(s
->bucket
, meta_oid
, RGW_OBJ_NS_MULTIPART
);
1493 obj
.set_in_extra_data(true);
1494 res
= get_obj_attrs(store
, s
, obj
, xattrs
);
1496 std::unique_ptr
<BlockCrypt
> block_crypt
;
1497 /* We are adding to existing object.
1498 * We use crypto mode that configured as if we were decrypting. */
1499 res
= rgw_s3_prepare_decrypt(s
, xattrs
, &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
)));
1505 /* it is ok, to not have encryption at all */
1509 std::unique_ptr
<BlockCrypt
> block_crypt
;
1510 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, &block_crypt
, crypt_http_responses
);
1511 if (res
== 0 && block_crypt
!= nullptr) {
1512 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
1513 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1519 void RGWPostObj_ObjStore_S3::rebuild_key(string
& key
)
1521 static string var
= "${filename}";
1522 int pos
= key
.find(var
);
1526 string new_key
= key
.substr(0, pos
);
1527 new_key
.append(filename
);
1528 new_key
.append(key
.substr(pos
+ var
.size()));
1533 std::string
RGWPostObj_ObjStore_S3::get_current_filename() const
1535 return s
->object
.name
;
1538 std::string
RGWPostObj_ObjStore_S3::get_current_content_type() const
1540 return content_type
;
1543 int RGWPostObj_ObjStore_S3::get_params()
1545 op_ret
= RGWPostObj_ObjStore::get_params();
1550 ldout(s
->cct
, 20) << "adding bucket to policy env: " << s
->bucket
.name
1552 env
.add_var("bucket", s
->bucket
.name
);
1556 struct post_form_part part
;
1557 int r
= read_form_part_header(&part
, done
);
1561 if (s
->cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 20)) {
1562 ldout(s
->cct
, 20) << "read part header -- part.name="
1563 << part
.name
<< dendl
;
1565 for (const auto& pair
: part
.fields
) {
1566 ldout(s
->cct
, 20) << "field.name=" << pair
.first
<< dendl
;
1567 ldout(s
->cct
, 20) << "field.val=" << pair
.second
.val
<< dendl
;
1568 ldout(s
->cct
, 20) << "field.params:" << dendl
;
1570 for (const auto& param_pair
: pair
.second
.params
) {
1571 ldout(s
->cct
, 20) << " " << param_pair
.first
1572 << " -> " << param_pair
.second
<< dendl
;
1577 if (done
) { /* unexpected here */
1578 err_msg
= "Malformed request";
1582 if (stringcasecmp(part
.name
, "file") == 0) { /* beginning of data transfer */
1583 struct post_part_field
& field
= part
.fields
["Content-Disposition"];
1584 map
<string
, string
>::iterator iter
= field
.params
.find("filename");
1585 if (iter
!= field
.params
.end()) {
1586 filename
= iter
->second
;
1588 parts
[part
.name
] = part
;
1593 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1594 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
1595 if (r
< 0 || !boundary
) {
1596 err_msg
= "Couldn't find boundary";
1599 parts
[part
.name
] = part
;
1600 string
part_str(part
.data
.c_str(), part
.data
.length());
1601 env
.add_var(part
.name
, part_str
);
1605 if (!part_str(parts
, "key", &object_str
)) {
1606 err_msg
= "Key not specified";
1610 s
->object
= rgw_obj_key(object_str
);
1612 rebuild_key(s
->object
.name
);
1614 if (s
->object
.empty()) {
1615 err_msg
= "Empty object name";
1619 env
.add_var("key", s
->object
.name
);
1621 part_str(parts
, "Content-Type", &content_type
);
1623 /* AWS permits POST without Content-Type: http://tracker.ceph.com/issues/20201 */
1624 if (! content_type
.empty()) {
1625 env
.add_var("Content-Type", content_type
);
1628 map
<string
, struct post_form_part
, ltstr_nocase
>::iterator piter
=
1629 parts
.upper_bound(RGW_AMZ_META_PREFIX
);
1630 for (; piter
!= parts
.end(); ++piter
) {
1631 string n
= piter
->first
;
1632 if (strncasecmp(n
.c_str(), RGW_AMZ_META_PREFIX
,
1633 sizeof(RGW_AMZ_META_PREFIX
) - 1) != 0)
1636 string attr_name
= RGW_ATTR_PREFIX
;
1637 attr_name
.append(n
);
1639 /* need to null terminate it */
1640 bufferlist
& data
= piter
->second
.data
;
1641 string str
= string(data
.c_str(), data
.length());
1644 attr_bl
.append(str
.c_str(), str
.size() + 1);
1646 attrs
[attr_name
] = attr_bl
;
1648 // TODO: refactor this and the above loop to share code
1649 piter
= parts
.find(RGW_AMZ_WEBSITE_REDIRECT_LOCATION
);
1650 if (piter
!= parts
.end()) {
1651 string n
= piter
->first
;
1652 string attr_name
= RGW_ATTR_PREFIX
;
1653 attr_name
.append(n
);
1654 /* need to null terminate it */
1655 bufferlist
& data
= piter
->second
.data
;
1656 string str
= string(data
.c_str(), data
.length());
1659 attr_bl
.append(str
.c_str(), str
.size() + 1);
1661 attrs
[attr_name
] = attr_bl
;
1664 int r
= get_policy();
1673 min_len
= post_policy
.min_length
;
1674 max_len
= post_policy
.max_length
;
1681 int RGWPostObj_ObjStore_S3::get_tags()
1684 if (part_str(parts
, "tagging", &tags_str
)) {
1685 RGWObjTagsXMLParser parser
;
1686 if (!parser
.init()){
1687 ldout(s
->cct
, 0) << "Couldn't init RGWObjTags XML parser" << dendl
;
1688 err_msg
= "Server couldn't process the request";
1689 return -EINVAL
; // TODO: This class of errors in rgw code should be a 5XX error
1691 if (!parser
.parse(tags_str
.c_str(), tags_str
.size(), 1)) {
1692 ldout(s
->cct
,0 ) << "Invalid Tagging XML" << dendl
;
1693 err_msg
= "Invalid Tagging XML";
1697 RGWObjTagSet_S3
*obj_tags_s3
;
1698 RGWObjTagging_S3
*tagging
;
1700 tagging
= static_cast<RGWObjTagging_S3
*>(parser
.find_first("Tagging"));
1701 obj_tags_s3
= static_cast<RGWObjTagSet_S3
*>(tagging
->find_first("TagSet"));
1703 return -ERR_MALFORMED_XML
;
1706 RGWObjTags obj_tags
;
1707 int r
= obj_tags_s3
->rebuild(obj_tags
);
1712 obj_tags
.encode(tags_bl
);
1713 ldout(s
->cct
, 20) << "Read " << obj_tags
.count() << "tags" << dendl
;
1714 attrs
[RGW_ATTR_TAGS
] = tags_bl
;
1721 int RGWPostObj_ObjStore_S3::get_policy()
1723 if (part_bl(parts
, "policy", &s
->auth
.s3_postobj_creds
.encoded_policy
)) {
1724 bool aws4_auth
= false;
1726 /* x-amz-algorithm handling */
1727 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
1728 if ((part_str(parts
, "x-amz-algorithm", &s
->auth
.s3_postobj_creds
.x_amz_algorithm
)) &&
1729 (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
)) {
1730 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v4 (AWS4-HMAC-SHA256)" << dendl
;
1733 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v2" << dendl
;
1736 // check that the signature matches the encoded policy
1740 /* x-amz-credential handling */
1741 if (!part_str(parts
, "x-amz-credential",
1742 &s
->auth
.s3_postobj_creds
.x_amz_credential
)) {
1743 ldout(s
->cct
, 0) << "No S3 aws4 credential found!" << dendl
;
1744 err_msg
= "Missing aws4 credential";
1748 /* x-amz-signature handling */
1749 if (!part_str(parts
, "x-amz-signature",
1750 &s
->auth
.s3_postobj_creds
.signature
)) {
1751 ldout(s
->cct
, 0) << "No aws4 signature found!" << dendl
;
1752 err_msg
= "Missing aws4 signature";
1756 /* x-amz-date handling */
1757 std::string received_date_str
;
1758 if (!part_str(parts
, "x-amz-date", &received_date_str
)) {
1759 ldout(s
->cct
, 0) << "No aws4 date found!" << dendl
;
1760 err_msg
= "Missing aws4 date";
1766 // check that the signature matches the encoded policy
1767 if (!part_str(parts
, "AWSAccessKeyId",
1768 &s
->auth
.s3_postobj_creds
.access_key
)) {
1769 ldout(s
->cct
, 0) << "No S3 aws2 access key found!" << dendl
;
1770 err_msg
= "Missing aws2 access key";
1774 if (!part_str(parts
, "signature", &s
->auth
.s3_postobj_creds
.signature
)) {
1775 ldout(s
->cct
, 0) << "No aws2 signature found!" << dendl
;
1776 err_msg
= "Missing aws2 signature";
1781 /* FIXME: this is a makeshift solution. The browser upload authentication will be
1782 * handled by an instance of rgw::auth::Completer spawned in Handler's authorize()
1784 const int ret
= rgw::auth::Strategy::apply(auth_registry_ptr
->get_s3_post(), s
);
1788 /* Populate the owner info. */
1789 s
->owner
.set_id(s
->user
->user_id
);
1790 s
->owner
.set_name(s
->user
->display_name
);
1791 ldout(s
->cct
, 20) << "Successful Signature Verification!" << dendl
;
1794 ceph::bufferlist decoded_policy
;
1796 decoded_policy
.decode_base64(s
->auth
.s3_postobj_creds
.encoded_policy
);
1797 } catch (buffer::error
& err
) {
1798 ldout(s
->cct
, 0) << "failed to decode_base64 policy" << dendl
;
1799 err_msg
= "Could not decode policy";
1803 decoded_policy
.append('\0'); // NULL terminate
1804 ldout(s
->cct
, 20) << "POST policy: " << decoded_policy
.c_str() << dendl
;
1807 int r
= post_policy
.from_json(decoded_policy
, err_msg
);
1809 if (err_msg
.empty()) {
1810 err_msg
= "Failed to parse policy";
1812 ldout(s
->cct
, 0) << "failed to parse policy" << dendl
;
1818 post_policy
.set_var_checked("x-amz-signature");
1821 post_policy
.set_var_checked("AWSAccessKeyId");
1822 post_policy
.set_var_checked("signature");
1824 post_policy
.set_var_checked("policy");
1826 r
= post_policy
.check(&env
, err_msg
);
1828 if (err_msg
.empty()) {
1829 err_msg
= "Policy check failed";
1831 ldout(s
->cct
, 0) << "policy check failed" << dendl
;
1836 ldout(s
->cct
, 0) << "No attached policy found!" << dendl
;
1840 part_str(parts
, "acl", &canned_acl
);
1842 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1843 ldout(s
->cct
, 20) << "canned_acl=" << canned_acl
<< dendl
;
1844 if (s3policy
.create_canned(s
->owner
, s
->bucket_owner
, canned_acl
) < 0) {
1845 err_msg
= "Bad canned ACLs";
1854 int RGWPostObj_ObjStore_S3::complete_get_params()
1858 struct post_form_part part
;
1859 int r
= read_form_part_header(&part
, done
);
1864 ceph::bufferlist part_data
;
1866 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1867 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
1868 if (r
< 0 || !boundary
) {
1872 /* Just reading the data but not storing any results of that. */
1878 int RGWPostObj_ObjStore_S3::get_data(ceph::bufferlist
& bl
, bool& again
)
1883 const uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1884 int r
= read_data(bl
, chunk_size
, boundary
, done
);
1891 /* Reached end of data, let's drain the rest of the params */
1892 r
= complete_get_params();
1903 void RGWPostObj_ObjStore_S3::send_response()
1905 if (op_ret
== 0 && parts
.count("success_action_redirect")) {
1908 part_str(parts
, "success_action_redirect", &redirect
);
1913 string etag_str
= "\"";
1915 etag_str
.append(etag
);
1916 etag_str
.append("\"");
1920 url_encode(s
->bucket_tenant
, tenant
); /* surely overkill, but cheap */
1921 url_encode(s
->bucket_name
, bucket
);
1922 url_encode(s
->object
.name
, key
);
1923 url_encode(etag_str
, etag_url
);
1925 if (!s
->bucket_tenant
.empty()) {
1927 * What we really would like is to quaily the bucket name, so
1928 * that the client could simply copy it and paste into next request.
1929 * Unfortunately, in S3 we cannot know if the client will decide
1930 * to come through DNS, with "bucket.tenant" sytanx, or through
1931 * URL with "tenant\bucket" syntax. Therefore, we provide the
1932 * tenant separately.
1934 redirect
.append("?tenant=");
1935 redirect
.append(tenant
);
1936 redirect
.append("&bucket=");
1937 redirect
.append(bucket
);
1939 redirect
.append("?bucket=");
1940 redirect
.append(bucket
);
1942 redirect
.append("&key=");
1943 redirect
.append(key
);
1944 redirect
.append("&etag=");
1945 redirect
.append(etag_url
);
1947 int r
= check_utf8(redirect
.c_str(), redirect
.size());
1952 dump_redirect(s
, redirect
);
1953 op_ret
= STATUS_REDIRECT
;
1954 } else if (op_ret
== 0 && parts
.count("success_action_status")) {
1955 string status_string
;
1956 uint32_t status_int
;
1958 part_str(parts
, "success_action_status", &status_string
);
1960 int r
= stringtoul(status_string
, &status_int
);
1966 switch (status_int
) {
1970 op_ret
= STATUS_CREATED
;
1973 op_ret
= STATUS_NO_CONTENT
;
1976 } else if (! op_ret
) {
1977 op_ret
= STATUS_NO_CONTENT
;
1981 if (op_ret
== STATUS_CREATED
) {
1982 for (auto &it
: crypt_http_responses
)
1983 dump_header(s
, it
.first
, it
.second
);
1984 s
->formatter
->open_object_section("PostResponse");
1985 if (g_conf
->rgw_dns_name
.length())
1986 s
->formatter
->dump_format("Location", "%s/%s",
1987 s
->info
.script_uri
.c_str(),
1988 s
->object
.name
.c_str());
1989 if (!s
->bucket_tenant
.empty())
1990 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
1991 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
1992 s
->formatter
->dump_string("Key", s
->object
.name
);
1993 s
->formatter
->close_section();
1995 s
->err
.message
= err_msg
;
1996 set_req_state_err(s
, op_ret
);
1999 dump_content_length(s
, s
->formatter
->get_len());
2001 end_header(s
, this);
2002 if (op_ret
!= STATUS_CREATED
)
2005 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2008 int RGWPostObj_ObjStore_S3::get_encrypt_filter(
2009 std::unique_ptr
<RGWPutObjDataProcessor
>* filter
, RGWPutObjDataProcessor
* cb
)
2012 std::unique_ptr
<BlockCrypt
> block_crypt
;
2013 res
= rgw_s3_prepare_encrypt(s
, attrs
, &parts
, &block_crypt
, crypt_http_responses
);
2014 if (res
== 0 && block_crypt
!= nullptr) {
2015 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
2016 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
2023 int RGWDeleteObj_ObjStore_S3::get_params()
2025 const char *if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_DELETE_IF_UNMODIFIED_SINCE");
2027 if (s
->system_request
) {
2028 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"no-precondition-error", &no_precondition_error
, false);
2032 std::string if_unmod_decoded
= url_decode(if_unmod
);
2035 if (utime_t::parse_date(if_unmod_decoded
, &epoch
, &nsec
) < 0) {
2036 ldout(s
->cct
, 10) << "failed to parse time: " << if_unmod_decoded
<< dendl
;
2039 unmod_since
= utime_t(epoch
, nsec
).to_real_time();
2045 void RGWDeleteObj_ObjStore_S3::send_response()
2051 r
= STATUS_NO_CONTENT
;
2053 set_req_state_err(s
, r
);
2055 if (!version_id
.empty()) {
2056 dump_header(s
, "x-amz-version-id", version_id
);
2058 if (delete_marker
) {
2059 dump_header(s
, "x-amz-delete-marker", "true");
2061 end_header(s
, this);
2064 int RGWCopyObj_ObjStore_S3::init_dest_policy()
2066 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2068 /* build a policy for the target object */
2069 int r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
2073 dest_policy
= s3policy
;
2078 int RGWCopyObj_ObjStore_S3::get_params()
2080 if_mod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MODIFIED_SINCE");
2081 if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_UNMODIFIED_SINCE");
2082 if_match
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MATCH");
2083 if_nomatch
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_NONE_MATCH");
2085 src_tenant_name
= s
->src_tenant_name
;
2086 src_bucket_name
= s
->src_bucket_name
;
2087 src_object
= s
->src_object
;
2088 dest_tenant_name
= s
->bucket
.tenant
;
2089 dest_bucket_name
= s
->bucket
.name
;
2090 dest_object
= s
->object
.name
;
2092 if (s
->system_request
) {
2093 source_zone
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"source-zone");
2094 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"copy-if-newer", ©_if_newer
, false);
2095 if (!source_zone
.empty()) {
2096 client_id
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"client-id");
2097 op_id
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"op-id");
2099 if (client_id
.empty() || op_id
.empty()) {
2101 RGW_SYS_PARAM_PREFIX
"client-id or "
2102 RGW_SYS_PARAM_PREFIX
"op-id were not provided, "
2103 "required for intra-region copy"
2110 const char *md_directive
= s
->info
.env
->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
2112 if (strcasecmp(md_directive
, "COPY") == 0) {
2113 attrs_mod
= RGWRados::ATTRSMOD_NONE
;
2114 } else if (strcasecmp(md_directive
, "REPLACE") == 0) {
2115 attrs_mod
= RGWRados::ATTRSMOD_REPLACE
;
2116 } else if (!source_zone
.empty()) {
2117 attrs_mod
= RGWRados::ATTRSMOD_NONE
; // default for intra-zone_group copy
2119 ldout(s
->cct
, 0) << "invalid metadata directive" << dendl
;
2124 if (source_zone
.empty() &&
2125 (dest_tenant_name
.compare(src_tenant_name
) == 0) &&
2126 (dest_bucket_name
.compare(src_bucket_name
) == 0) &&
2127 (dest_object
.compare(src_object
.name
) == 0) &&
2128 src_object
.instance
.empty() &&
2129 (attrs_mod
!= RGWRados::ATTRSMOD_REPLACE
)) {
2130 /* can only copy object into itself if replacing attrs */
2131 ldout(s
->cct
, 0) << "can't copy object into itself if not replacing attrs"
2133 return -ERR_INVALID_REQUEST
;
2138 void RGWCopyObj_ObjStore_S3::send_partial_response(off_t ofs
)
2140 if (! sent_header
) {
2142 set_req_state_err(s
, op_ret
);
2145 end_header(s
, this, "application/xml");
2148 s
->formatter
->open_object_section_in_ns("CopyObjectResult", XMLNS_AWS_S3
);
2152 /* Send progress field. Note that this diverge from the original S3
2153 * spec. We do this in order to keep connection alive.
2155 s
->formatter
->dump_int("Progress", (uint64_t)ofs
);
2157 rgw_flush_formatter(s
, s
->formatter
);
2160 void RGWCopyObj_ObjStore_S3::send_response()
2163 send_partial_response(0);
2166 dump_time(s
, "LastModified", &mtime
);
2167 std::string etag_str
= etag
.to_str();
2168 if (! etag_str
.empty()) {
2169 s
->formatter
->dump_string("ETag", std::move(etag_str
));
2171 s
->formatter
->close_section();
2172 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2176 void RGWGetACLs_ObjStore_S3::send_response()
2179 set_req_state_err(s
, op_ret
);
2181 end_header(s
, this, "application/xml");
2183 rgw_flush_formatter(s
, s
->formatter
);
2187 int RGWPutACLs_ObjStore_S3::get_params()
2189 int ret
= RGWPutACLs_ObjStore::get_params();
2191 const int ret_auth
= do_aws4_auth_completion();
2199 int RGWPutACLs_ObjStore_S3::get_policy_from_state(RGWRados
*store
,
2200 struct req_state
*s
,
2203 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2205 // bucket-* canned acls do not apply to bucket
2206 if (s
->object
.empty()) {
2207 if (s
->canned_acl
.find("bucket") != string::npos
)
2208 s
->canned_acl
.clear();
2211 int r
= create_s3_policy(s
, store
, s3policy
, owner
);
2215 s3policy
.to_xml(ss
);
2220 void RGWPutACLs_ObjStore_S3::send_response()
2223 set_req_state_err(s
, op_ret
);
2225 end_header(s
, this, "application/xml");
2229 void RGWGetLC_ObjStore_S3::execute()
2231 config
.set_ctx(s
->cct
);
2233 map
<string
, bufferlist
>::iterator aiter
= s
->bucket_attrs
.find(RGW_ATTR_LC
);
2234 if (aiter
== s
->bucket_attrs
.end()) {
2239 bufferlist::iterator
iter(&aiter
->second
);
2241 config
.decode(iter
);
2242 } catch (const buffer::error
& e
) {
2243 ldout(s
->cct
, 0) << __func__
<< "decode life cycle config failed" << dendl
;
2249 void RGWGetLC_ObjStore_S3::send_response()
2252 if (op_ret
== -ENOENT
) {
2253 set_req_state_err(s
, ERR_NO_SUCH_LC
);
2255 set_req_state_err(s
, op_ret
);
2259 end_header(s
, this, "application/xml");
2265 config
.dump_xml(s
->formatter
);
2266 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2269 void RGWPutLC_ObjStore_S3::send_response()
2272 set_req_state_err(s
, op_ret
);
2274 end_header(s
, this, "application/xml");
2278 void RGWDeleteLC_ObjStore_S3::send_response()
2281 op_ret
= STATUS_NO_CONTENT
;
2283 set_req_state_err(s
, op_ret
);
2286 end_header(s
, this, "application/xml");
2290 void RGWGetCORS_ObjStore_S3::send_response()
2293 if (op_ret
== -ENOENT
)
2294 set_req_state_err(s
, ERR_NO_SUCH_CORS_CONFIGURATION
);
2296 set_req_state_err(s
, op_ret
);
2299 end_header(s
, NULL
, "application/xml");
2303 RGWCORSConfiguration_S3
*s3cors
=
2304 static_cast<RGWCORSConfiguration_S3
*>(&bucket_cors
);
2313 int RGWPutCORS_ObjStore_S3::get_params()
2316 char *data
= nullptr;
2318 RGWCORSXMLParser_S3
parser(s
->cct
);
2319 RGWCORSConfiguration_S3
*cors_config
;
2321 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2322 r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
2327 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
2329 r
= do_aws4_auth_completion();
2334 if (!parser
.init()) {
2338 if (!data
|| !parser
.parse(data
, len
, 1)) {
2342 static_cast<RGWCORSConfiguration_S3
*>(parser
.find_first(
2343 "CORSConfiguration"));
2348 // forward bucket cors requests to meta master zone
2349 if (!store
->is_meta_master()) {
2350 /* only need to keep this data around if we're not meta master */
2351 in_data
.append(data
, len
);
2354 if (s
->cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 15)) {
2355 ldout(s
->cct
, 15) << "CORSConfiguration";
2356 cors_config
->to_xml(*_dout
);
2360 cors_config
->encode(cors_bl
);
2365 void RGWPutCORS_ObjStore_S3::send_response()
2368 set_req_state_err(s
, op_ret
);
2370 end_header(s
, NULL
, "application/xml");
2374 void RGWDeleteCORS_ObjStore_S3::send_response()
2377 if (!r
|| r
== -ENOENT
)
2378 r
= STATUS_NO_CONTENT
;
2380 set_req_state_err(s
, r
);
2382 end_header(s
, NULL
);
2385 void RGWOptionsCORS_ObjStore_S3::send_response()
2387 string hdrs
, exp_hdrs
;
2388 uint32_t max_age
= CORS_MAX_AGE_INVALID
;
2389 /*EACCES means, there is no CORS registered yet for the bucket
2390 *ENOENT means, there is no match of the Origin in the list of CORSRule
2392 if (op_ret
== -ENOENT
)
2395 set_req_state_err(s
, op_ret
);
2397 end_header(s
, NULL
);
2400 get_response_params(hdrs
, exp_hdrs
, &max_age
);
2403 dump_access_control(s
, origin
, req_meth
, hdrs
.c_str(), exp_hdrs
.c_str(),
2405 end_header(s
, NULL
);
2408 void RGWGetRequestPayment_ObjStore_S3::send_response()
2411 end_header(s
, this, "application/xml");
2414 s
->formatter
->open_object_section_in_ns("RequestPaymentConfiguration", XMLNS_AWS_S3
);
2415 const char *payer
= requester_pays
? "Requester" : "BucketOwner";
2416 s
->formatter
->dump_string("Payer", payer
);
2417 s
->formatter
->close_section();
2418 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2421 class RGWSetRequestPaymentParser
: public RGWXMLParser
2423 XMLObj
*alloc_obj(const char *el
) override
{
2428 RGWSetRequestPaymentParser() {}
2429 ~RGWSetRequestPaymentParser() override
{}
2431 int get_request_payment_payer(bool *requester_pays
) {
2432 XMLObj
*config
= find_first("RequestPaymentConfiguration");
2436 *requester_pays
= false;
2438 XMLObj
*field
= config
->find_first("Payer");
2442 string
& s
= field
->get_data();
2444 if (stringcasecmp(s
, "Requester") == 0) {
2445 *requester_pays
= true;
2446 } else if (stringcasecmp(s
, "BucketOwner") != 0) {
2454 int RGWSetRequestPayment_ObjStore_S3::get_params()
2458 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2459 int r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
2465 RGWSetRequestPaymentParser parser
;
2467 if (!parser
.init()) {
2468 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
2473 if (!parser
.parse(data
, len
, 1)) {
2474 ldout(s
->cct
, 10) << "failed to parse data: " << data
<< dendl
;
2479 r
= parser
.get_request_payment_payer(&requester_pays
);
2487 void RGWSetRequestPayment_ObjStore_S3::send_response()
2490 set_req_state_err(s
, op_ret
);
2495 int RGWInitMultipart_ObjStore_S3::get_params()
2497 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2498 op_ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
2507 void RGWInitMultipart_ObjStore_S3::send_response()
2510 set_req_state_err(s
, op_ret
);
2512 for (auto &it
: crypt_http_responses
)
2513 dump_header(s
, it
.first
, it
.second
);
2514 end_header(s
, this, "application/xml");
2517 s
->formatter
->open_object_section_in_ns("InitiateMultipartUploadResult", XMLNS_AWS_S3
);
2518 if (!s
->bucket_tenant
.empty())
2519 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2520 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2521 s
->formatter
->dump_string("Key", s
->object
.name
);
2522 s
->formatter
->dump_string("UploadId", upload_id
);
2523 s
->formatter
->close_section();
2524 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2528 int RGWInitMultipart_ObjStore_S3::prepare_encryption(map
<string
, bufferlist
>& attrs
)
2531 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, nullptr, crypt_http_responses
);
2535 int RGWCompleteMultipart_ObjStore_S3::get_params()
2537 int ret
= RGWCompleteMultipart_ObjStore::get_params();
2542 return do_aws4_auth_completion();
2545 void RGWCompleteMultipart_ObjStore_S3::send_response()
2548 set_req_state_err(s
, op_ret
);
2550 end_header(s
, this, "application/xml");
2553 s
->formatter
->open_object_section_in_ns("CompleteMultipartUploadResult", XMLNS_AWS_S3
);
2554 std::string base_uri
= compute_domain_uri(s
);
2555 if (!s
->bucket_tenant
.empty()) {
2556 s
->formatter
->dump_format("Location", "%s/%s:%s/%s",
2558 s
->bucket_tenant
.c_str(),
2559 s
->bucket_name
.c_str(),
2560 s
->object
.name
.c_str()
2562 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2564 s
->formatter
->dump_format("Location", "%s/%s/%s",
2566 s
->bucket_name
.c_str(),
2567 s
->object
.name
.c_str()
2570 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2571 s
->formatter
->dump_string("Key", s
->object
.name
);
2572 s
->formatter
->dump_string("ETag", etag
);
2573 s
->formatter
->close_section();
2574 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2578 void RGWAbortMultipart_ObjStore_S3::send_response()
2582 r
= STATUS_NO_CONTENT
;
2584 set_req_state_err(s
, r
);
2586 end_header(s
, this);
2589 void RGWListMultipart_ObjStore_S3::send_response()
2592 set_req_state_err(s
, op_ret
);
2594 end_header(s
, this, "application/xml");
2598 s
->formatter
->open_object_section_in_ns("ListPartsResult", XMLNS_AWS_S3
);
2599 map
<uint32_t, RGWUploadPartInfo
>::iterator iter
;
2600 map
<uint32_t, RGWUploadPartInfo
>::reverse_iterator test_iter
;
2603 iter
= parts
.begin();
2604 test_iter
= parts
.rbegin();
2605 if (test_iter
!= parts
.rend()) {
2606 cur_max
= test_iter
->first
;
2608 if (!s
->bucket_tenant
.empty())
2609 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2610 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2611 s
->formatter
->dump_string("Key", s
->object
.name
);
2612 s
->formatter
->dump_string("UploadId", upload_id
);
2613 s
->formatter
->dump_string("StorageClass", "STANDARD");
2614 s
->formatter
->dump_int("PartNumberMarker", marker
);
2615 s
->formatter
->dump_int("NextPartNumberMarker", cur_max
);
2616 s
->formatter
->dump_int("MaxParts", max_parts
);
2617 s
->formatter
->dump_string("IsTruncated", (truncated
? "true" : "false"));
2619 ACLOwner
& owner
= policy
.get_owner();
2620 dump_owner(s
, owner
.get_id(), owner
.get_display_name());
2622 for (; iter
!= parts
.end(); ++iter
) {
2623 RGWUploadPartInfo
& info
= iter
->second
;
2625 s
->formatter
->open_object_section("Part");
2627 dump_time(s
, "LastModified", &info
.modified
);
2629 s
->formatter
->dump_unsigned("PartNumber", info
.num
);
2630 s
->formatter
->dump_format("ETag", "\"%s\"", info
.etag
.c_str());
2631 s
->formatter
->dump_unsigned("Size", info
.accounted_size
);
2632 s
->formatter
->close_section();
2634 s
->formatter
->close_section();
2635 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2639 void RGWListBucketMultiparts_ObjStore_S3::send_response()
2642 set_req_state_err(s
, op_ret
);
2645 end_header(s
, this, "application/xml");
2650 s
->formatter
->open_object_section_in_ns("ListMultipartUploadsResult", XMLNS_AWS_S3
);
2651 if (!s
->bucket_tenant
.empty())
2652 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2653 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2654 if (!prefix
.empty())
2655 s
->formatter
->dump_string("ListMultipartUploadsResult.Prefix", prefix
);
2656 string
& key_marker
= marker
.get_key();
2657 if (!key_marker
.empty())
2658 s
->formatter
->dump_string("KeyMarker", key_marker
);
2659 string
& upload_id_marker
= marker
.get_upload_id();
2660 if (!upload_id_marker
.empty())
2661 s
->formatter
->dump_string("UploadIdMarker", upload_id_marker
);
2662 string next_key
= next_marker
.mp
.get_key();
2663 if (!next_key
.empty())
2664 s
->formatter
->dump_string("NextKeyMarker", next_key
);
2665 string next_upload_id
= next_marker
.mp
.get_upload_id();
2666 if (!next_upload_id
.empty())
2667 s
->formatter
->dump_string("NextUploadIdMarker", next_upload_id
);
2668 s
->formatter
->dump_int("MaxUploads", max_uploads
);
2669 if (!delimiter
.empty())
2670 s
->formatter
->dump_string("Delimiter", delimiter
);
2671 s
->formatter
->dump_string("IsTruncated", (is_truncated
? "true" : "false"));
2674 vector
<RGWMultipartUploadEntry
>::iterator iter
;
2675 for (iter
= uploads
.begin(); iter
!= uploads
.end(); ++iter
) {
2676 RGWMPObj
& mp
= iter
->mp
;
2677 s
->formatter
->open_array_section("Upload");
2678 s
->formatter
->dump_string("Key", mp
.get_key());
2679 s
->formatter
->dump_string("UploadId", mp
.get_upload_id());
2680 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
, "Initiator");
2681 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
);
2682 s
->formatter
->dump_string("StorageClass", "STANDARD");
2683 dump_time(s
, "Initiated", &iter
->obj
.meta
.mtime
);
2684 s
->formatter
->close_section();
2686 if (!common_prefixes
.empty()) {
2687 s
->formatter
->open_array_section("CommonPrefixes");
2688 map
<string
, bool>::iterator pref_iter
;
2689 for (pref_iter
= common_prefixes
.begin();
2690 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
2691 s
->formatter
->dump_string("CommonPrefixes.Prefix", pref_iter
->first
);
2693 s
->formatter
->close_section();
2696 s
->formatter
->close_section();
2697 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2700 int RGWDeleteMultiObj_ObjStore_S3::get_params()
2702 int ret
= RGWDeleteMultiObj_ObjStore::get_params();
2707 return do_aws4_auth_completion();
2710 void RGWDeleteMultiObj_ObjStore_S3::send_status()
2712 if (! status_dumped
) {
2714 set_req_state_err(s
, op_ret
);
2716 status_dumped
= true;
2720 void RGWDeleteMultiObj_ObjStore_S3::begin_response()
2723 if (!status_dumped
) {
2728 end_header(s
, this, "application/xml");
2729 s
->formatter
->open_object_section_in_ns("DeleteResult", XMLNS_AWS_S3
);
2731 rgw_flush_formatter(s
, s
->formatter
);
2734 void RGWDeleteMultiObj_ObjStore_S3::send_partial_response(rgw_obj_key
& key
,
2736 const string
& marker_version_id
, int ret
)
2739 if (op_ret
== 0 && !quiet
) {
2740 s
->formatter
->open_object_section("Deleted");
2741 s
->formatter
->dump_string("Key", key
.name
);
2742 if (!key
.instance
.empty()) {
2743 s
->formatter
->dump_string("VersionId", key
.instance
);
2745 if (delete_marker
) {
2746 s
->formatter
->dump_bool("DeleteMarker", true);
2747 s
->formatter
->dump_string("DeleteMarkerVersionId", marker_version_id
);
2749 s
->formatter
->close_section();
2750 } else if (op_ret
< 0) {
2751 struct rgw_http_error r
;
2754 s
->formatter
->open_object_section("Error");
2757 rgw_get_errno_s3(&r
, err_no
);
2759 s
->formatter
->dump_string("Key", key
.name
);
2760 s
->formatter
->dump_string("VersionId", key
.instance
);
2761 s
->formatter
->dump_int("Code", r
.http_ret
);
2762 s
->formatter
->dump_string("Message", r
.s3_code
);
2763 s
->formatter
->close_section();
2766 rgw_flush_formatter(s
, s
->formatter
);
2770 void RGWDeleteMultiObj_ObjStore_S3::end_response()
2773 s
->formatter
->close_section();
2774 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2777 void RGWGetObjLayout_ObjStore_S3::send_response()
2780 set_req_state_err(s
, op_ret
);
2782 end_header(s
, this, "application/json");
2790 f
.open_object_section("result");
2791 ::encode_json("head", head_obj
, &f
);
2792 ::encode_json("manifest", *manifest
, &f
);
2793 f
.open_array_section("data_location");
2794 for (auto miter
= manifest
->obj_begin(); miter
!= manifest
->obj_end(); ++miter
) {
2795 f
.open_object_section("obj");
2796 rgw_raw_obj raw_loc
= miter
.get_location().get_raw_obj(store
);
2797 ::encode_json("ofs", miter
.get_ofs(), &f
);
2798 ::encode_json("loc", raw_loc
, &f
);
2799 ::encode_json("loc_ofs", miter
.location_ofs(), &f
);
2800 ::encode_json("loc_size", miter
.get_stripe_size(), &f
);
2802 rgw_flush_formatter(s
, &f
);
2806 rgw_flush_formatter(s
, &f
);
2809 int RGWConfigBucketMetaSearch_ObjStore_S3::get_params()
2811 auto iter
= s
->info
.x_meta_map
.find("x-amz-meta-search");
2812 if (iter
== s
->info
.x_meta_map
.end()) {
2813 s
->err
.message
= "X-Rgw-Meta-Search header not provided";
2814 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2818 list
<string
> expressions
;
2819 get_str_list(iter
->second
, ",", expressions
);
2821 for (auto& expression
: expressions
) {
2822 vector
<string
> args
;
2823 get_str_vec(expression
, ";", args
);
2826 s
->err
.message
= "invalid empty expression";
2827 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2830 if (args
.size() > 2) {
2831 s
->err
.message
= string("invalid expression: ") + expression
;
2832 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2836 string key
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[0]));
2838 if (args
.size() > 1) {
2839 val
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[1]));
2842 if (!boost::algorithm::starts_with(key
, RGW_AMZ_META_PREFIX
)) {
2843 s
->err
.message
= string("invalid expression, key must start with '" RGW_AMZ_META_PREFIX
"' : ") + expression
;
2844 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2848 key
= key
.substr(sizeof(RGW_AMZ_META_PREFIX
) - 1);
2850 ESEntityTypeMap::EntityType entity_type
;
2852 if (val
.empty() || val
== "str" || val
== "string") {
2853 entity_type
= ESEntityTypeMap::ES_ENTITY_STR
;
2854 } else if (val
== "int" || val
== "integer") {
2855 entity_type
= ESEntityTypeMap::ES_ENTITY_INT
;
2856 } else if (val
== "date" || val
== "datetime") {
2857 entity_type
= ESEntityTypeMap::ES_ENTITY_DATE
;
2859 s
->err
.message
= string("invalid entity type: ") + val
;
2860 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2864 mdsearch_config
[key
] = entity_type
;
2870 void RGWConfigBucketMetaSearch_ObjStore_S3::send_response()
2873 set_req_state_err(s
, op_ret
);
2875 end_header(s
, this);
2878 void RGWGetBucketMetaSearch_ObjStore_S3::send_response()
2881 set_req_state_err(s
, op_ret
);
2883 end_header(s
, NULL
, "application/xml");
2885 Formatter
*f
= s
->formatter
;
2886 f
->open_array_section("GetBucketMetaSearchResult");
2887 for (auto& e
: s
->bucket_info
.mdsearch_config
) {
2888 f
->open_object_section("Entry");
2889 string k
= string("x-amz-meta-") + e
.first
;
2890 f
->dump_string("Key", k
.c_str());
2893 case ESEntityTypeMap::ES_ENTITY_INT
:
2896 case ESEntityTypeMap::ES_ENTITY_DATE
:
2902 f
->dump_string("Type", type
);
2906 rgw_flush_formatter(s
, f
);
2909 void RGWDelBucketMetaSearch_ObjStore_S3::send_response()
2912 set_req_state_err(s
, op_ret
);
2914 end_header(s
, this);
2918 RGWOp
*RGWHandler_REST_Service_S3::op_get()
2920 if (is_usage_op()) {
2921 return new RGWGetUsage_ObjStore_S3
;
2923 return new RGWListBuckets_ObjStore_S3
;
2927 RGWOp
*RGWHandler_REST_Service_S3::op_head()
2929 return new RGWListBuckets_ObjStore_S3
;
2932 RGWOp
*RGWHandler_REST_Service_S3::op_post()
2934 if (s
->info
.args
.exists("Action")) {
2935 string action
= s
->info
.args
.get("Action");
2936 if (action
.compare("CreateRole") == 0)
2937 return new RGWCreateRole
;
2938 if (action
.compare("DeleteRole") == 0)
2939 return new RGWDeleteRole
;
2940 if (action
.compare("GetRole") == 0)
2941 return new RGWGetRole
;
2942 if (action
.compare("UpdateAssumeRolePolicy") == 0)
2943 return new RGWModifyRole
;
2944 if (action
.compare("ListRoles") == 0)
2945 return new RGWListRoles
;
2946 if (action
.compare("PutRolePolicy") == 0)
2947 return new RGWPutRolePolicy
;
2948 if (action
.compare("GetRolePolicy") == 0)
2949 return new RGWGetRolePolicy
;
2950 if (action
.compare("ListRolePolicies") == 0)
2951 return new RGWListRolePolicies
;
2952 if (action
.compare("DeleteRolePolicy") == 0)
2953 return new RGWDeleteRolePolicy
;
2958 RGWOp
*RGWHandler_REST_Bucket_S3::get_obj_op(bool get_data
)
2962 return new RGWListBucket_ObjStore_S3
;
2964 return new RGWStatBucket_ObjStore_S3
;
2968 RGWOp
*RGWHandler_REST_Bucket_S3::op_get()
2970 if (s
->info
.args
.sub_resource_exists("logging"))
2971 return new RGWGetBucketLogging_ObjStore_S3
;
2973 if (s
->info
.args
.sub_resource_exists("location"))
2974 return new RGWGetBucketLocation_ObjStore_S3
;
2976 if (s
->info
.args
.sub_resource_exists("versioning"))
2977 return new RGWGetBucketVersioning_ObjStore_S3
;
2979 if (s
->info
.args
.sub_resource_exists("website")) {
2980 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
2983 return new RGWGetBucketWebsite_ObjStore_S3
;
2986 if (s
->info
.args
.exists("mdsearch")) {
2987 return new RGWGetBucketMetaSearch_ObjStore_S3
;
2991 return new RGWGetACLs_ObjStore_S3
;
2992 } else if (is_cors_op()) {
2993 return new RGWGetCORS_ObjStore_S3
;
2994 } else if (is_request_payment_op()) {
2995 return new RGWGetRequestPayment_ObjStore_S3
;
2996 } else if (s
->info
.args
.exists("uploads")) {
2997 return new RGWListBucketMultiparts_ObjStore_S3
;
2998 } else if(is_lc_op()) {
2999 return new RGWGetLC_ObjStore_S3
;
3000 } else if(is_policy_op()) {
3001 return new RGWGetBucketPolicy
;
3003 return get_obj_op(true);
3006 RGWOp
*RGWHandler_REST_Bucket_S3::op_head()
3009 return new RGWGetACLs_ObjStore_S3
;
3010 } else if (s
->info
.args
.exists("uploads")) {
3011 return new RGWListBucketMultiparts_ObjStore_S3
;
3013 return get_obj_op(false);
3016 RGWOp
*RGWHandler_REST_Bucket_S3::op_put()
3018 if (s
->info
.args
.sub_resource_exists("logging"))
3020 if (s
->info
.args
.sub_resource_exists("versioning"))
3021 return new RGWSetBucketVersioning_ObjStore_S3
;
3022 if (s
->info
.args
.sub_resource_exists("website")) {
3023 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
3026 return new RGWSetBucketWebsite_ObjStore_S3
;
3029 return new RGWPutACLs_ObjStore_S3
;
3030 } else if (is_cors_op()) {
3031 return new RGWPutCORS_ObjStore_S3
;
3032 } else if (is_request_payment_op()) {
3033 return new RGWSetRequestPayment_ObjStore_S3
;
3034 } else if(is_lc_op()) {
3035 return new RGWPutLC_ObjStore_S3
;
3036 } else if(is_policy_op()) {
3037 return new RGWPutBucketPolicy
;
3039 return new RGWCreateBucket_ObjStore_S3
;
3042 RGWOp
*RGWHandler_REST_Bucket_S3::op_delete()
3045 return new RGWDeleteCORS_ObjStore_S3
;
3046 } else if(is_lc_op()) {
3047 return new RGWDeleteLC_ObjStore_S3
;
3048 } else if(is_policy_op()) {
3049 return new RGWDeleteBucketPolicy
;
3052 if (s
->info
.args
.sub_resource_exists("website")) {
3053 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
3056 return new RGWDeleteBucketWebsite_ObjStore_S3
;
3059 if (s
->info
.args
.exists("mdsearch")) {
3060 return new RGWDelBucketMetaSearch_ObjStore_S3
;
3063 return new RGWDeleteBucket_ObjStore_S3
;
3066 RGWOp
*RGWHandler_REST_Bucket_S3::op_post()
3068 if (s
->info
.args
.exists("delete")) {
3069 return new RGWDeleteMultiObj_ObjStore_S3
;
3072 if (s
->info
.args
.exists("mdsearch")) {
3073 return new RGWConfigBucketMetaSearch_ObjStore_S3
;
3076 return new RGWPostObj_ObjStore_S3
;
3079 RGWOp
*RGWHandler_REST_Bucket_S3::op_options()
3081 return new RGWOptionsCORS_ObjStore_S3
;
3084 RGWOp
*RGWHandler_REST_Obj_S3::get_obj_op(bool get_data
)
3087 return new RGWGetACLs_ObjStore_S3
;
3089 RGWGetObj_ObjStore_S3
*get_obj_op
= new RGWGetObj_ObjStore_S3
;
3090 get_obj_op
->set_get_data(get_data
);
3094 RGWOp
*RGWHandler_REST_Obj_S3::op_get()
3097 return new RGWGetACLs_ObjStore_S3
;
3098 } else if (s
->info
.args
.exists("uploadId")) {
3099 return new RGWListMultipart_ObjStore_S3
;
3100 } else if (s
->info
.args
.exists("layout")) {
3101 return new RGWGetObjLayout_ObjStore_S3
;
3102 } else if (is_tagging_op()) {
3103 return new RGWGetObjTags_ObjStore_S3
;
3105 return get_obj_op(true);
3108 RGWOp
*RGWHandler_REST_Obj_S3::op_head()
3111 return new RGWGetACLs_ObjStore_S3
;
3112 } else if (s
->info
.args
.exists("uploadId")) {
3113 return new RGWListMultipart_ObjStore_S3
;
3115 return get_obj_op(false);
3118 RGWOp
*RGWHandler_REST_Obj_S3::op_put()
3121 return new RGWPutACLs_ObjStore_S3
;
3122 } else if (is_tagging_op()) {
3123 return new RGWPutObjTags_ObjStore_S3
;
3126 if (s
->init_state
.src_bucket
.empty())
3127 return new RGWPutObj_ObjStore_S3
;
3129 return new RGWCopyObj_ObjStore_S3
;
3132 RGWOp
*RGWHandler_REST_Obj_S3::op_delete()
3134 if (is_tagging_op()) {
3135 return new RGWDeleteObjTags_ObjStore_S3
;
3137 string upload_id
= s
->info
.args
.get("uploadId");
3139 if (upload_id
.empty())
3140 return new RGWDeleteObj_ObjStore_S3
;
3142 return new RGWAbortMultipart_ObjStore_S3
;
3145 RGWOp
*RGWHandler_REST_Obj_S3::op_post()
3147 if (s
->info
.args
.exists("uploadId"))
3148 return new RGWCompleteMultipart_ObjStore_S3
;
3150 if (s
->info
.args
.exists("uploads"))
3151 return new RGWInitMultipart_ObjStore_S3
;
3153 return new RGWPostObj_ObjStore_S3
;
3156 RGWOp
*RGWHandler_REST_Obj_S3::op_options()
3158 return new RGWOptionsCORS_ObjStore_S3
;
3161 int RGWHandler_REST_S3::init_from_header(struct req_state
* s
,
3162 int default_formatter
,
3163 bool configurable_format
)
3168 const char *req_name
= s
->relative_uri
.c_str();
3171 if (*req_name
== '?') {
3174 p
= s
->info
.request_params
.c_str();
3177 s
->info
.args
.set(p
);
3178 s
->info
.args
.parse();
3180 /* must be called after the args parsing */
3181 int ret
= allocate_formatter(s
, default_formatter
, configurable_format
);
3185 if (*req_name
!= '/')
3194 int pos
= req
.find('/');
3196 first
= req
.substr(0, pos
);
3202 * XXX The intent of the check for empty is apparently to let the bucket
3203 * name from DNS to be set ahead. However, we currently take the DNS
3204 * bucket and re-insert it into URL in rgw_rest.cc:RGWREST::preprocess().
3205 * So, this check is meaningless.
3207 * Rather than dropping this, the code needs to be changed into putting
3208 * the bucket (and its tenant) from DNS and Host: header (HTTP_HOST)
3209 * into req_status.bucket_name directly.
3211 if (s
->init_state
.url_bucket
.empty()) {
3212 // Save bucket to tide us over until token is parsed.
3213 s
->init_state
.url_bucket
= first
;
3215 string encoded_obj_str
= req
.substr(pos
+1);
3216 s
->object
= rgw_obj_key(encoded_obj_str
, s
->info
.args
.get("versionId"));
3219 s
->object
= rgw_obj_key(req_name
, s
->info
.args
.get("versionId"));
3224 int RGWHandler_REST_S3::postauth_init()
3226 struct req_init_state
*t
= &s
->init_state
;
3227 bool relaxed_names
= s
->cct
->_conf
->rgw_relaxed_s3_bucket_names
;
3229 rgw_parse_url_bucket(t
->url_bucket
, s
->user
->user_id
.tenant
,
3230 s
->bucket_tenant
, s
->bucket_name
);
3232 dout(10) << "s->object=" << (!s
->object
.empty() ? s
->object
: rgw_obj_key("<NULL>"))
3233 << " s->bucket=" << rgw_make_bucket_entry_name(s
->bucket_tenant
, s
->bucket_name
) << dendl
;
3236 ret
= rgw_validate_tenant_name(s
->bucket_tenant
);
3239 if (!s
->bucket_name
.empty()) {
3240 ret
= valid_s3_bucket_name(s
->bucket_name
, relaxed_names
);
3243 ret
= validate_object_name(s
->object
.name
);
3248 if (!t
->src_bucket
.empty()) {
3249 rgw_parse_url_bucket(t
->src_bucket
, s
->user
->user_id
.tenant
,
3250 s
->src_tenant_name
, s
->src_bucket_name
);
3251 ret
= rgw_validate_tenant_name(s
->src_tenant_name
);
3254 ret
= valid_s3_bucket_name(s
->src_bucket_name
, relaxed_names
);
3261 int RGWHandler_REST_S3::init(RGWRados
*store
, struct req_state
*s
,
3262 rgw::io::BasicClient
*cio
)
3268 ret
= rgw_validate_tenant_name(s
->bucket_tenant
);
3271 bool relaxed_names
= s
->cct
->_conf
->rgw_relaxed_s3_bucket_names
;
3272 if (!s
->bucket_name
.empty()) {
3273 ret
= valid_s3_bucket_name(s
->bucket_name
, relaxed_names
);
3276 ret
= validate_object_name(s
->object
.name
);
3281 const char *cacl
= s
->info
.env
->get("HTTP_X_AMZ_ACL");
3283 s
->canned_acl
= cacl
;
3285 s
->has_acl_header
= s
->info
.env
->exists_prefix("HTTP_X_AMZ_GRANT");
3287 const char *copy_source
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE");
3289 (! s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE")) &&
3290 (! s
->info
.args
.exists("uploadId"))) {
3292 ret
= RGWCopyObj::parse_copy_location(url_decode(copy_source
),
3293 s
->init_state
.src_bucket
,
3296 ldout(s
->cct
, 0) << "failed to parse copy location" << dendl
;
3297 return -EINVAL
; // XXX why not -ERR_INVALID_BUCKET_NAME or -ERR_BAD_URL?
3301 return RGWHandler_REST::init(store
, s
, cio
);
3304 enum class AwsVersion
{
3310 enum class AwsRoute
{
3316 static inline std::pair
<AwsVersion
, AwsRoute
>
3317 discover_aws_flavour(const req_info
& info
)
3319 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
3321 AwsVersion version
= AwsVersion::UNKOWN
;
3322 AwsRoute route
= AwsRoute::UNKOWN
;
3324 const char* http_auth
= info
.env
->get("HTTP_AUTHORIZATION");
3325 if (http_auth
&& http_auth
[0]) {
3326 /* Authorization in Header */
3327 route
= AwsRoute::HEADERS
;
3329 if (!strncmp(http_auth
, AWS4_HMAC_SHA256_STR
,
3330 strlen(AWS4_HMAC_SHA256_STR
))) {
3332 version
= AwsVersion::V4
;
3333 } else if (!strncmp(http_auth
, "AWS ", 4)) {
3335 version
= AwsVersion::V2
;
3338 route
= AwsRoute::QUERY_STRING
;
3340 if (info
.args
.get("X-Amz-Algorithm") == AWS4_HMAC_SHA256_STR
) {
3342 version
= AwsVersion::V4
;
3343 } else if (!info
.args
.get("AWSAccessKeyId").empty()) {
3345 version
= AwsVersion::V2
;
3349 return std::make_pair(version
, route
);
3352 static void init_anon_user(struct req_state
*s
)
3354 rgw_get_anon_user(*(s
->user
));
3355 s
->perm_mask
= RGW_PERM_FULL_CONTROL
;
3359 * verify that a signed request comes from the keyholder
3360 * by checking the signature against our locally-computed version
3362 * it tries AWS v4 before AWS v2
3364 int RGW_Auth_S3::authorize(RGWRados
* const store
,
3365 const rgw::auth::StrategyRegistry
& auth_registry
,
3366 struct req_state
* const s
)
3369 /* neither keystone and rados enabled; warn and exit! */
3370 if (!store
->ctx()->_conf
->rgw_s3_auth_use_rados
&&
3371 !store
->ctx()->_conf
->rgw_s3_auth_use_keystone
&&
3372 !store
->ctx()->_conf
->rgw_s3_auth_use_ldap
) {
3373 dout(0) << "WARNING: no authorization backend enabled! Users will never authenticate." << dendl
;
3377 const auto ret
= rgw::auth::Strategy::apply(auth_registry
.get_s3_main(), s
);
3379 /* Populate the owner info. */
3380 s
->owner
.set_id(s
->user
->user_id
);
3381 s
->owner
.set_name(s
->user
->display_name
);
3386 int RGWHandler_Auth_S3::init(RGWRados
*store
, struct req_state
*state
,
3387 rgw::io::BasicClient
*cio
)
3389 int ret
= RGWHandler_REST_S3::init_from_header(state
, RGW_FORMAT_JSON
,
3394 return RGWHandler_REST::init(store
, state
, cio
);
3397 RGWHandler_REST
* RGWRESTMgr_S3::get_handler(struct req_state
* const s
,
3398 const rgw::auth::StrategyRegistry
& auth_registry
,
3399 const std::string
& frontend_prefix
)
3401 bool is_s3website
= enable_s3website
&& (s
->prot_flags
& RGW_REST_WEBSITE
);
3403 RGWHandler_REST_S3::init_from_header(s
,
3404 is_s3website
? RGW_FORMAT_HTML
:
3405 RGW_FORMAT_XML
, true);
3409 RGWHandler_REST
* handler
;
3410 // TODO: Make this more readable
3412 if (s
->init_state
.url_bucket
.empty()) {
3413 handler
= new RGWHandler_REST_Service_S3Website(auth_registry
);
3414 } else if (s
->object
.empty()) {
3415 handler
= new RGWHandler_REST_Bucket_S3Website(auth_registry
);
3417 handler
= new RGWHandler_REST_Obj_S3Website(auth_registry
);
3420 if (s
->init_state
.url_bucket
.empty()) {
3421 handler
= new RGWHandler_REST_Service_S3(auth_registry
);
3422 } else if (s
->object
.empty()) {
3423 handler
= new RGWHandler_REST_Bucket_S3(auth_registry
);
3425 handler
= new RGWHandler_REST_Obj_S3(auth_registry
);
3429 ldout(s
->cct
, 20) << __func__
<< " handler=" << typeid(*handler
).name()
3434 bool RGWHandler_REST_S3Website::web_dir() const {
3435 std::string subdir_name
= url_decode(s
->object
.name
);
3437 if (subdir_name
.empty()) {
3439 } else if (subdir_name
.back() == '/') {
3440 subdir_name
.pop_back();
3443 rgw_obj
obj(s
->bucket
, subdir_name
);
3445 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
3446 obj_ctx
.obj
.set_atomic(obj
);
3447 obj_ctx
.obj
.set_prefetch_data(obj
);
3449 RGWObjState
* state
= nullptr;
3450 if (store
->get_obj_state(&obj_ctx
, s
->bucket_info
, obj
, &state
, false) < 0) {
3453 if (! state
->exists
) {
3456 return state
->exists
;
3459 int RGWHandler_REST_S3Website::init(RGWRados
*store
, req_state
*s
,
3460 rgw::io::BasicClient
* cio
)
3462 // save the original object name before retarget() replaces it with the
3463 // result of get_effective_key(). the error_handler() needs the original
3464 // object name for redirect handling
3465 original_object_name
= s
->object
.name
;
3467 return RGWHandler_REST_S3::init(store
, s
, cio
);
3470 int RGWHandler_REST_S3Website::retarget(RGWOp
* op
, RGWOp
** new_op
) {
3472 ldout(s
->cct
, 10) << __func__
<< "Starting retarget" << dendl
;
3474 if (!(s
->prot_flags
& RGW_REST_WEBSITE
))
3477 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
3478 int ret
= store
->get_bucket_info(obj_ctx
, s
->bucket_tenant
,
3479 s
->bucket_name
, s
->bucket_info
, NULL
,
3482 // TODO-FUTURE: if the bucket does not exist, maybe expose it here?
3483 return -ERR_NO_SUCH_BUCKET
;
3485 if (!s
->bucket_info
.has_website
) {
3486 // TODO-FUTURE: if the bucket has no WebsiteConfig, expose it here
3487 return -ERR_NO_SUCH_WEBSITE_CONFIGURATION
;
3490 rgw_obj_key new_obj
;
3491 s
->bucket_info
.website_conf
.get_effective_key(s
->object
.name
, &new_obj
.name
, web_dir());
3492 ldout(s
->cct
, 10) << "retarget get_effective_key " << s
->object
<< " -> "
3493 << new_obj
<< dendl
;
3495 RGWBWRoutingRule rrule
;
3496 bool should_redirect
=
3497 s
->bucket_info
.website_conf
.should_redirect(new_obj
.name
, 0, &rrule
);
3499 if (should_redirect
) {
3500 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
3501 const string
& protocol
=
3502 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
3503 int redirect_code
= 0;
3504 rrule
.apply_rule(protocol
, hostname
, s
->object
.name
, &s
->redirect
,
3506 // APply a custom HTTP response code
3507 if (redirect_code
> 0)
3508 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
3509 ldout(s
->cct
, 10) << "retarget redirect code=" << redirect_code
3510 << " proto+host:" << protocol
<< "://" << hostname
3511 << " -> " << s
->redirect
<< dendl
;
3512 return -ERR_WEBSITE_REDIRECT
;
3516 * FIXME: if s->object != new_obj, drop op and create a new op to handle
3517 * operation. Or remove this comment if it's not applicable anymore
3520 s
->object
= new_obj
;
3525 RGWOp
* RGWHandler_REST_S3Website::op_get()
3527 return get_obj_op(true);
3530 RGWOp
* RGWHandler_REST_S3Website::op_head()
3532 return get_obj_op(false);
3535 int RGWHandler_REST_S3Website::serve_errordoc(int http_ret
, const string
& errordoc_key
) {
3537 s
->formatter
->reset(); /* Try to throw it all away */
3539 std::shared_ptr
<RGWGetObj_ObjStore_S3Website
> getop( static_cast<RGWGetObj_ObjStore_S3Website
*>(op_get()));
3540 if (getop
.get() == NULL
) {
3541 return -1; // Trigger double error handler
3543 getop
->init(store
, s
, this);
3544 getop
->range_str
= NULL
;
3545 getop
->if_mod
= NULL
;
3546 getop
->if_unmod
= NULL
;
3547 getop
->if_match
= NULL
;
3548 getop
->if_nomatch
= NULL
;
3549 s
->object
= errordoc_key
;
3551 ret
= init_permissions(getop
.get());
3553 ldout(s
->cct
, 20) << "serve_errordoc failed, init_permissions ret=" << ret
<< dendl
;
3554 return -1; // Trigger double error handler
3557 ret
= read_permissions(getop
.get());
3559 ldout(s
->cct
, 20) << "serve_errordoc failed, read_permissions ret=" << ret
<< dendl
;
3560 return -1; // Trigger double error handler
3564 getop
->set_custom_http_response(http_ret
);
3567 ret
= getop
->init_processing();
3569 ldout(s
->cct
, 20) << "serve_errordoc failed, init_processing ret=" << ret
<< dendl
;
3570 return -1; // Trigger double error handler
3573 ret
= getop
->verify_op_mask();
3575 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_op_mask ret=" << ret
<< dendl
;
3576 return -1; // Trigger double error handler
3579 ret
= getop
->verify_permission();
3581 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_permission ret=" << ret
<< dendl
;
3582 return -1; // Trigger double error handler
3585 ret
= getop
->verify_params();
3587 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_params ret=" << ret
<< dendl
;
3588 return -1; // Trigger double error handler
3591 // No going back now
3594 * FIXME Missing headers:
3595 * With a working errordoc, the s3 error fields are rendered as HTTP headers,
3596 * x-amz-error-code: NoSuchKey
3597 * x-amz-error-message: The specified key does not exist.
3598 * x-amz-error-detail-Key: foo
3606 int RGWHandler_REST_S3Website::error_handler(int err_no
,
3607 string
* error_content
) {
3608 int new_err_no
= -1;
3609 rgw_http_errors::const_iterator r
= rgw_http_s3_errors
.find(err_no
> 0 ? err_no
: -err_no
);
3610 int http_error_code
= -1;
3612 if (r
!= rgw_http_s3_errors
.end()) {
3613 http_error_code
= r
->second
.first
;
3615 ldout(s
->cct
, 10) << "RGWHandler_REST_S3Website::error_handler err_no=" << err_no
<< " http_ret=" << http_error_code
<< dendl
;
3617 RGWBWRoutingRule rrule
;
3618 bool should_redirect
=
3619 s
->bucket_info
.website_conf
.should_redirect(original_object_name
,
3620 http_error_code
, &rrule
);
3622 if (should_redirect
) {
3623 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
3624 const string
& protocol
=
3625 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
3626 int redirect_code
= 0;
3627 rrule
.apply_rule(protocol
, hostname
, original_object_name
,
3628 &s
->redirect
, &redirect_code
);
3629 // Apply a custom HTTP response code
3630 if (redirect_code
> 0)
3631 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
3632 ldout(s
->cct
, 10) << "error handler redirect code=" << redirect_code
3633 << " proto+host:" << protocol
<< "://" << hostname
3634 << " -> " << s
->redirect
<< dendl
;
3635 return -ERR_WEBSITE_REDIRECT
;
3636 } else if (err_no
== -ERR_WEBSITE_REDIRECT
) {
3637 // Do nothing here, this redirect will be handled in abort_early's ERR_WEBSITE_REDIRECT block
3638 // Do NOT fire the ErrorDoc handler
3639 } else if (!s
->bucket_info
.website_conf
.error_doc
.empty()) {
3640 /* This serves an entire page!
3641 On success, it will return zero, and no further content should be sent to the socket
3642 On failure, we need the double-error handler
3644 new_err_no
= RGWHandler_REST_S3Website::serve_errordoc(http_error_code
, s
->bucket_info
.website_conf
.error_doc
);
3645 if (new_err_no
&& new_err_no
!= -1) {
3646 err_no
= new_err_no
;
3649 ldout(s
->cct
, 20) << "No special error handling today!" << dendl
;
3655 RGWOp
* RGWHandler_REST_Obj_S3Website::get_obj_op(bool get_data
)
3657 /** If we are in website mode, then it is explicitly impossible to run GET or
3658 * HEAD on the actual directory. We must convert the request to run on the
3659 * suffix object instead!
3661 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3662 op
->set_get_data(get_data
);
3666 RGWOp
* RGWHandler_REST_Bucket_S3Website::get_obj_op(bool get_data
)
3668 /** If we are in website mode, then it is explicitly impossible to run GET or
3669 * HEAD on the actual directory. We must convert the request to run on the
3670 * suffix object instead!
3672 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3673 op
->set_get_data(get_data
);
3677 RGWOp
* RGWHandler_REST_Service_S3Website::get_obj_op(bool get_data
)
3679 /** If we are in website mode, then it is explicitly impossible to run GET or
3680 * HEAD on the actual directory. We must convert the request to run on the
3681 * suffix object instead!
3683 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3684 op
->set_get_data(get_data
);
3693 static rgw::auth::Completer::cmplptr_t
3694 null_completer_factory(const boost::optional
<std::string
>& secret_key
)
3700 AWSEngine::VersionAbstractor::auth_data_t
3701 AWSGeneralAbstractor::get_auth_data(const req_state
* const s
) const
3705 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
3707 if (version
== AwsVersion::V2
) {
3708 return get_auth_data_v2(s
);
3709 } else if (version
== AwsVersion::V4
) {
3710 return get_auth_data_v4(s
, route
== AwsRoute::QUERY_STRING
);
3712 /* FIXME(rzarzynski): handle anon user. */
3717 boost::optional
<std::string
>
3718 AWSGeneralAbstractor::get_v4_canonical_headers(
3719 const req_info
& info
,
3720 const boost::string_view
& signedheaders
,
3721 const bool using_qs
) const
3723 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
3727 AWSEngine::VersionAbstractor::auth_data_t
3728 AWSGeneralAbstractor::get_auth_data_v4(const req_state
* const s
,
3730 bool using_qs
) const
3732 boost::string_view access_key_id
;
3733 boost::string_view signed_hdrs
;
3735 boost::string_view date
;
3736 boost::string_view credential_scope
;
3737 boost::string_view client_signature
;
3739 int ret
= rgw::auth::s3::parse_credentials(s
->info
,
3750 /* craft canonical headers */
3751 boost::optional
<std::string
> canonical_headers
= \
3752 get_v4_canonical_headers(s
->info
, signed_hdrs
, using_qs
);
3753 if (canonical_headers
) {
3754 ldout(s
->cct
, 10) << "canonical headers format = " << *canonical_headers
3760 /* Get the expected hash. */
3761 auto exp_payload_hash
= rgw::auth::s3::get_v4_exp_payload_hash(s
->info
);
3763 /* Craft canonical URI. Using std::move later so let it be non-const. */
3764 auto canonical_uri
= rgw::auth::s3::get_v4_canonical_uri(s
->info
);
3766 /* Craft canonical query string. std::moving later so non-const here. */
3767 auto canonical_qs
= rgw::auth::s3::get_v4_canonical_qs(s
->info
, using_qs
);
3769 /* Craft canonical request. */
3770 auto canonical_req_hash
= \
3771 rgw::auth::s3::get_v4_canon_req_hash(s
->cct
,
3773 std::move(canonical_uri
),
3774 std::move(canonical_qs
),
3775 std::move(*canonical_headers
),
3779 auto string_to_sign
= \
3780 rgw::auth::s3::get_v4_string_to_sign(s
->cct
,
3781 AWS4_HMAC_SHA256_STR
,
3784 std::move(canonical_req_hash
));
3786 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
3788 std::placeholders::_1
,
3789 std::placeholders::_2
,
3790 std::placeholders::_3
);
3792 /* Requests authenticated with the Query Parameters are treated as unsigned.
3793 * From "Authenticating Requests: Using Query Parameters (AWS Signature
3796 * You don't include a payload hash in the Canonical Request, because
3797 * when you create a presigned URL, you don't know the payload content
3798 * because the URL is used to upload an arbitrary payload. Instead, you
3799 * use a constant string UNSIGNED-PAYLOAD.
3801 * This means we have absolutely no business in spawning completer. Both
3802 * aws4_auth_needs_complete and aws4_auth_streaming_mode are set to false
3803 * by default. We don't need to change that. */
3804 if (is_v4_payload_unsigned(exp_payload_hash
) || is_v4_payload_empty(s
)) {
3808 std::move(string_to_sign
),
3810 null_completer_factory
3813 /* We're going to handle a signed payload. Be aware that even empty HTTP
3814 * body (no payload) requires verification:
3816 * The x-amz-content-sha256 header is required for all AWS Signature
3817 * Version 4 requests. It provides a hash of the request payload. If
3818 * there is no payload, you must provide the hash of an empty string. */
3819 if (!is_v4_payload_streamed(exp_payload_hash
)) {
3820 ldout(s
->cct
, 10) << "delaying v4 auth" << dendl
;
3822 /* payload in a single chunk */
3825 case RGW_OP_CREATE_BUCKET
:
3826 case RGW_OP_PUT_OBJ
:
3827 case RGW_OP_PUT_ACLS
:
3828 case RGW_OP_PUT_CORS
:
3829 case RGW_OP_INIT_MULTIPART
: // in case that Init Multipart uses CHUNK encoding
3830 case RGW_OP_COMPLETE_MULTIPART
:
3831 case RGW_OP_SET_BUCKET_VERSIONING
:
3832 case RGW_OP_DELETE_MULTI_OBJ
:
3833 case RGW_OP_ADMIN_SET_METADATA
:
3834 case RGW_OP_SET_BUCKET_WEBSITE
:
3835 case RGW_OP_PUT_BUCKET_POLICY
:
3836 case RGW_OP_PUT_OBJ_TAGGING
:
3838 case RGW_OP_SET_REQUEST_PAYMENT
:
3841 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl
;
3842 throw -ERR_NOT_IMPLEMENTED
;
3845 const auto cmpl_factory
= std::bind(AWSv4ComplSingle::create
,
3847 std::placeholders::_1
);
3851 std::move(string_to_sign
),
3856 /* IMHO "streamed" doesn't fit too good here. I would prefer to call
3857 * it "chunked" but let's be coherent with Amazon's terminology. */
3859 dout(10) << "body content detected in multiple chunks" << dendl
;
3861 /* payload in multiple chunks */
3865 case RGW_OP_PUT_OBJ
:
3868 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED (streaming mode)" << dendl
;
3869 throw -ERR_NOT_IMPLEMENTED
;
3872 dout(10) << "aws4 seed signature ok... delaying v4 auth" << dendl
;
3874 /* In the case of streamed payload client sets the x-amz-content-sha256
3875 * to "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" but uses "UNSIGNED-PAYLOAD"
3876 * when constructing the Canonical Request. */
3878 /* In the case of single-chunk upload client set the header's value is
3879 * coherent with the one used for Canonical Request crafting. */
3881 /* In the case of query string-based authentication there should be no
3882 * x-amz-content-sha256 header and the value "UNSIGNED-PAYLOAD" is used
3884 const auto cmpl_factory
= std::bind(AWSv4ComplMulti::create
,
3889 std::placeholders::_1
);
3893 std::move(string_to_sign
),
3902 boost::optional
<std::string
>
3903 AWSGeneralBoto2Abstractor::get_v4_canonical_headers(
3904 const req_info
& info
,
3905 const boost::string_view
& signedheaders
,
3906 const bool using_qs
) const
3908 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
3913 AWSEngine::VersionAbstractor::auth_data_t
3914 AWSGeneralAbstractor::get_auth_data_v2(const req_state
* const s
) const
3916 boost::string_view access_key_id
;
3917 boost::string_view signature
;
3920 const char* http_auth
= s
->info
.env
->get("HTTP_AUTHORIZATION");
3921 if (! http_auth
|| http_auth
[0] == '\0') {
3922 /* Credentials are provided in query string. We also need to verify
3923 * the "Expires" parameter now. */
3924 access_key_id
= s
->info
.args
.get("AWSAccessKeyId");
3925 signature
= s
->info
.args
.get("Signature");
3928 boost::string_view expires
= s
->info
.args
.get("Expires");
3929 if (expires
.empty()) {
3933 /* It looks we have the guarantee that expires is a null-terminated,
3934 * and thus string_view::data() can be safely used. */
3935 const time_t exp
= atoll(expires
.data());
3943 /* The "Authorization" HTTP header is being used. */
3944 const boost::string_view
auth_str(http_auth
+ strlen("AWS "));
3945 const size_t pos
= auth_str
.rfind(':');
3946 if (pos
!= boost::string_view::npos
) {
3947 access_key_id
= auth_str
.substr(0, pos
);
3948 signature
= auth_str
.substr(pos
+ 1);
3952 /* Let's canonize the HTTP headers that are covered by the AWS auth v2. */
3953 std::string string_to_sign
;
3954 utime_t header_time
;
3955 if (! rgw_create_s3_canonical_header(s
->info
, &header_time
, string_to_sign
,
3957 ldout(cct
, 10) << "failed to create the canonized auth header\n"
3958 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
3962 ldout(cct
, 10) << "string_to_sign:\n"
3963 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
3965 if (!qsr
&& !is_time_skew_ok(header_time
)) {
3966 throw -ERR_REQUEST_TIME_SKEWED
;
3970 std::move(access_key_id
),
3971 std::move(signature
),
3972 std::move(string_to_sign
),
3973 rgw::auth::s3::get_v2_signature
,
3974 null_completer_factory
3979 AWSEngine::VersionAbstractor::auth_data_t
3980 AWSBrowserUploadAbstractor::get_auth_data_v2(const req_state
* const s
) const
3983 s
->auth
.s3_postobj_creds
.access_key
,
3984 s
->auth
.s3_postobj_creds
.signature
,
3985 s
->auth
.s3_postobj_creds
.encoded_policy
.to_str(),
3986 rgw::auth::s3::get_v2_signature
,
3987 null_completer_factory
3991 AWSEngine::VersionAbstractor::auth_data_t
3992 AWSBrowserUploadAbstractor::get_auth_data_v4(const req_state
* const s
) const
3994 const boost::string_view credential
= s
->auth
.s3_postobj_creds
.x_amz_credential
;
3996 /* grab access key id */
3997 const size_t pos
= credential
.find("/");
3998 const boost::string_view access_key_id
= credential
.substr(0, pos
);
3999 dout(10) << "access key id = " << access_key_id
<< dendl
;
4001 /* grab credential scope */
4002 const boost::string_view credential_scope
= credential
.substr(pos
+ 1);
4003 dout(10) << "credential scope = " << credential_scope
<< dendl
;
4005 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
4007 std::placeholders::_1
,
4008 std::placeholders::_2
,
4009 std::placeholders::_3
);
4013 s
->auth
.s3_postobj_creds
.signature
,
4014 s
->auth
.s3_postobj_creds
.encoded_policy
.to_str(),
4016 null_completer_factory
4020 AWSEngine::VersionAbstractor::auth_data_t
4021 AWSBrowserUploadAbstractor::get_auth_data(const req_state
* const s
) const
4023 if (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
) {
4024 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v4"
4025 << " (AWS4-HMAC-SHA256)" << dendl
;
4026 return get_auth_data_v4(s
);
4028 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v2" << dendl
;
4029 return get_auth_data_v2(s
);
4035 AWSEngine::authenticate(const req_state
* const s
) const
4037 /* Small reminder: an ver_abstractor is allowed to throw! */
4038 const auto auth_data
= ver_abstractor
.get_auth_data(s
);
4040 if (auth_data
.access_key_id
.empty() || auth_data
.client_signature
.empty()) {
4041 return result_t::deny(-EINVAL
);
4043 return authenticate(auth_data
.access_key_id
,
4044 auth_data
.client_signature
,
4045 auth_data
.string_to_sign
,
4046 auth_data
.signature_factory
,
4047 auth_data
.completer_factory
,
4052 } /* namespace s3 */
4053 } /* namespace auth */
4054 } /* namespace rgw */
4056 rgw::LDAPHelper
* rgw::auth::s3::LDAPEngine::ldh
= nullptr;
4057 std::mutex
rgw::auth::s3::LDAPEngine::mtx
;
4059 void rgw::auth::s3::LDAPEngine::init(CephContext
* const cct
)
4062 std::lock_guard
<std::mutex
> lck(mtx
);
4064 const string
& ldap_uri
= cct
->_conf
->rgw_ldap_uri
;
4065 const string
& ldap_binddn
= cct
->_conf
->rgw_ldap_binddn
;
4066 const string
& ldap_searchdn
= cct
->_conf
->rgw_ldap_searchdn
;
4067 const string
& ldap_searchfilter
= cct
->_conf
->rgw_ldap_searchfilter
;
4068 const string
& ldap_dnattr
= cct
->_conf
->rgw_ldap_dnattr
;
4069 std::string ldap_bindpw
= parse_rgw_ldap_bindpw(cct
);
4071 ldh
= new rgw::LDAPHelper(ldap_uri
, ldap_binddn
, ldap_bindpw
,
4072 ldap_searchdn
, ldap_searchfilter
, ldap_dnattr
);
4080 rgw::auth::RemoteApplier::acl_strategy_t
4081 rgw::auth::s3::LDAPEngine::get_acl_strategy() const
4083 //This is based on the assumption that the default acl strategy in
4084 // get_perms_from_aclspec, will take care. Extra acl spec is not required.
4088 rgw::auth::RemoteApplier::AuthInfo
4089 rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken
& token
) const noexcept
4091 /* The short form of "using" can't be used here -- we're aliasing a class'
4093 using acct_privilege_t
= \
4094 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t
;
4096 return rgw::auth::RemoteApplier::AuthInfo
{
4099 RGW_PERM_FULL_CONTROL
,
4100 acct_privilege_t::IS_PLAIN_ACCT
,
4105 rgw::auth::Engine::result_t
4106 rgw::auth::s3::LDAPEngine::authenticate(
4107 const boost::string_view
& access_key_id
,
4108 const boost::string_view
& signature
,
4109 const string_to_sign_t
& string_to_sign
,
4110 const signature_factory_t
&,
4111 const completer_factory_t
& completer_factory
,
4112 const req_state
* const s
) const
4114 /* boost filters and/or string_ref may throw on invalid input */
4115 rgw::RGWToken base64_token
;
4117 base64_token
= rgw::from_base64(access_key_id
);
4119 base64_token
= std::string("");
4122 if (! base64_token
.valid()) {
4123 return result_t::deny();
4126 //TODO: Uncomment, when we have a migration plan in place.
4127 //Check if a user of type other than 'ldap' is already present, if yes, then
4129 /*RGWUserInfo user_info;
4130 user_info.user_id = base64_token.id;
4131 if (rgw_get_user_info_by_uid(store, user_info.user_id, user_info) >= 0) {
4132 if (user_info.type != TYPE_LDAP) {
4133 ldout(cct, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl;
4138 if (ldh
->auth(base64_token
.id
, base64_token
.key
) != 0) {
4139 return result_t::deny();
4142 auto apl
= apl_factory
->create_apl_remote(cct
, s
, get_acl_strategy(),
4143 get_creds_info(base64_token
));
4144 return result_t::grant(std::move(apl
), completer_factory(boost::none
));
4149 rgw::auth::Engine::result_t
4150 rgw::auth::s3::LocalEngine::authenticate(
4151 const boost::string_view
& _access_key_id
,
4152 const boost::string_view
& signature
,
4153 const string_to_sign_t
& string_to_sign
,
4154 const signature_factory_t
& signature_factory
,
4155 const completer_factory_t
& completer_factory
,
4156 const req_state
* const s
) const
4158 /* get the user info */
4159 RGWUserInfo user_info
;
4160 /* TODO(rzarzynski): we need to have string-view taking variant. */
4161 const std::string access_key_id
= _access_key_id
.to_string();
4162 if (rgw_get_user_info_by_access_key(store
, access_key_id
, user_info
) < 0) {
4163 ldout(cct
, 5) << "error reading user info, uid=" << access_key_id
4164 << " can't authenticate" << dendl
;
4165 return result_t::deny(-ERR_INVALID_ACCESS_KEY
);
4167 //TODO: Uncomment, when we have a migration plan in place.
4169 if (s->user->type != TYPE_RGW) {
4170 ldout(cct, 10) << "ERROR: User id of type: " << s->user->type
4171 << " is present" << dendl;
4176 const auto iter
= user_info
.access_keys
.find(access_key_id
);
4177 if (iter
== std::end(user_info
.access_keys
)) {
4178 ldout(cct
, 0) << "ERROR: access key not encoded in user info" << dendl
;
4179 return result_t::deny(-EPERM
);
4181 const RGWAccessKey
& k
= iter
->second
;
4183 const VersionAbstractor::server_signature_t server_signature
= \
4184 signature_factory(cct
, k
.key
, string_to_sign
);
4186 ldout(cct
, 15) << "string_to_sign="
4187 << rgw::crypt_sanitize::log_content
{string_to_sign
}
4189 ldout(cct
, 15) << "server signature=" << server_signature
<< dendl
;
4190 ldout(cct
, 15) << "client signature=" << signature
<< dendl
;
4191 ldout(cct
, 15) << "compare=" << signature
.compare(server_signature
) << dendl
;
4193 if (static_cast<boost::string_view
>(server_signature
) != signature
) {
4194 return result_t::deny(-ERR_SIGNATURE_NO_MATCH
);
4197 auto apl
= apl_factory
->create_apl_local(cct
, s
, user_info
, k
.subuser
);
4198 return result_t::grant(std::move(apl
), completer_factory(k
.key
));
4201 bool rgw::auth::s3::S3AnonymousEngine::is_applicable(
4204 if (s
->op
== OP_OPTIONS
) {
4210 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
4212 return route
== AwsRoute::QUERY_STRING
&& version
== AwsVersion::UNKOWN
;