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");
652 max_keys
= s
->info
.args
.get("max-keys");
653 op_ret
= parse_max_keys();
657 delimiter
= s
->info
.args
.get("delimiter");
658 encoding_type
= s
->info
.args
.get("encoding-type");
659 if (s
->system_request
) {
660 s
->info
.args
.get_bool("objs-container", &objs_container
, false);
661 const char *shard_id_str
= s
->info
.env
->get("HTTP_RGWX_SHARD_ID");
664 shard_id
= strict_strtol(shard_id_str
, 10, &err
);
666 ldout(s
->cct
, 5) << "bad shard id specified: " << shard_id_str
<< dendl
;
670 shard_id
= s
->bucket_instance_shard_id
;
676 void RGWListBucket_ObjStore_S3::send_versioned_response()
678 s
->formatter
->open_object_section_in_ns("ListVersionsResult", XMLNS_AWS_S3
);
679 if (!s
->bucket_tenant
.empty())
680 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
681 s
->formatter
->dump_string("Name", s
->bucket_name
);
682 s
->formatter
->dump_string("Prefix", prefix
);
683 s
->formatter
->dump_string("KeyMarker", marker
.name
);
684 s
->formatter
->dump_string("VersionIdMarker", marker
.instance
);
685 if (is_truncated
&& !next_marker
.empty()) {
686 s
->formatter
->dump_string("NextKeyMarker", next_marker
.name
);
687 s
->formatter
->dump_string("NextVersionIdMarker", next_marker
.instance
);
689 s
->formatter
->dump_int("MaxKeys", max
);
690 if (!delimiter
.empty())
691 s
->formatter
->dump_string("Delimiter", delimiter
);
693 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
696 bool encode_key
= false;
697 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
698 s
->formatter
->dump_string("EncodingType", "url");
703 if (objs_container
) {
704 s
->formatter
->open_array_section("Entries");
707 vector
<rgw_bucket_dir_entry
>::iterator iter
;
708 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
709 const char *section_name
= (iter
->is_delete_marker() ? "DeleteMarker"
711 s
->formatter
->open_object_section(section_name
);
712 if (objs_container
) {
713 s
->formatter
->dump_bool("IsDeleteMarker", iter
->is_delete_marker());
715 rgw_obj_key
key(iter
->key
);
718 url_encode(key
.name
, key_name
);
719 s
->formatter
->dump_string("Key", key_name
);
721 s
->formatter
->dump_string("Key", key
.name
);
723 string version_id
= key
.instance
;
724 if (version_id
.empty()) {
727 if (s
->system_request
) {
728 if (iter
->versioned_epoch
> 0) {
729 s
->formatter
->dump_int("VersionedEpoch", iter
->versioned_epoch
);
731 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
732 utime_t
ut(iter
->meta
.mtime
);
733 ut
.gmtime_nsec(s
->formatter
->dump_stream("RgwxMtime"));
735 s
->formatter
->dump_string("VersionId", version_id
);
736 s
->formatter
->dump_bool("IsLatest", iter
->is_current());
737 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
738 if (!iter
->is_delete_marker()) {
739 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
740 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
741 s
->formatter
->dump_string("StorageClass", "STANDARD");
743 dump_owner(s
, iter
->meta
.owner
, iter
->meta
.owner_display_name
);
744 s
->formatter
->close_section();
746 if (objs_container
) {
747 s
->formatter
->close_section();
750 if (!common_prefixes
.empty()) {
751 map
<string
, bool>::iterator pref_iter
;
752 for (pref_iter
= common_prefixes
.begin();
753 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
754 s
->formatter
->open_array_section("CommonPrefixes");
755 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
756 s
->formatter
->close_section();
760 s
->formatter
->close_section();
761 rgw_flush_formatter_and_reset(s
, s
->formatter
);
764 void RGWListBucket_ObjStore_S3::send_response()
767 set_req_state_err(s
, op_ret
);
770 end_header(s
, this, "application/xml");
776 send_versioned_response();
780 s
->formatter
->open_object_section_in_ns("ListBucketResult", XMLNS_AWS_S3
);
781 if (!s
->bucket_tenant
.empty())
782 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
783 s
->formatter
->dump_string("Name", s
->bucket_name
);
784 s
->formatter
->dump_string("Prefix", prefix
);
785 s
->formatter
->dump_string("Marker", marker
.name
);
786 if (is_truncated
&& !next_marker
.empty())
787 s
->formatter
->dump_string("NextMarker", next_marker
.name
);
788 s
->formatter
->dump_int("MaxKeys", max
);
789 if (!delimiter
.empty())
790 s
->formatter
->dump_string("Delimiter", delimiter
);
792 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
795 bool encode_key
= false;
796 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
797 s
->formatter
->dump_string("EncodingType", "url");
802 vector
<rgw_bucket_dir_entry
>::iterator iter
;
803 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
804 rgw_obj_key
key(iter
->key
);
805 s
->formatter
->open_array_section("Contents");
808 url_encode(key
.name
, key_name
);
809 s
->formatter
->dump_string("Key", key_name
);
811 s
->formatter
->dump_string("Key", key
.name
);
813 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
814 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
815 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
816 s
->formatter
->dump_string("StorageClass", "STANDARD");
817 dump_owner(s
, iter
->meta
.owner
, iter
->meta
.owner_display_name
);
818 if (s
->system_request
) {
819 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
821 s
->formatter
->close_section();
823 if (!common_prefixes
.empty()) {
824 map
<string
, bool>::iterator pref_iter
;
825 for (pref_iter
= common_prefixes
.begin();
826 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
827 s
->formatter
->open_array_section("CommonPrefixes");
828 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
829 s
->formatter
->close_section();
833 s
->formatter
->close_section();
834 rgw_flush_formatter_and_reset(s
, s
->formatter
);
837 void RGWGetBucketLogging_ObjStore_S3::send_response()
840 end_header(s
, this, "application/xml");
843 s
->formatter
->open_object_section_in_ns("BucketLoggingStatus", XMLNS_AWS_S3
);
844 s
->formatter
->close_section();
845 rgw_flush_formatter_and_reset(s
, s
->formatter
);
848 void RGWGetBucketLocation_ObjStore_S3::send_response()
854 RGWZoneGroup zonegroup
;
857 int ret
= store
->get_zonegroup(s
->bucket_info
.zonegroup
, zonegroup
);
859 api_name
= zonegroup
.api_name
;
861 if (s
->bucket_info
.zonegroup
!= "default") {
862 api_name
= s
->bucket_info
.zonegroup
;
866 s
->formatter
->dump_format_ns("LocationConstraint", XMLNS_AWS_S3
,
867 "%s", api_name
.c_str());
868 rgw_flush_formatter_and_reset(s
, s
->formatter
);
871 void RGWGetBucketVersioning_ObjStore_S3::send_response()
874 end_header(s
, this, "application/xml");
877 s
->formatter
->open_object_section_in_ns("VersioningConfiguration", XMLNS_AWS_S3
);
879 const char *status
= (versioning_enabled
? "Enabled" : "Suspended");
880 s
->formatter
->dump_string("Status", status
);
882 s
->formatter
->close_section();
883 rgw_flush_formatter_and_reset(s
, s
->formatter
);
886 class RGWSetBucketVersioningParser
: public RGWXMLParser
888 XMLObj
*alloc_obj(const char *el
) override
{
893 RGWSetBucketVersioningParser() {}
894 ~RGWSetBucketVersioningParser() override
{}
896 int get_versioning_status(bool *status
) {
897 XMLObj
*config
= find_first("VersioningConfiguration");
903 XMLObj
*field
= config
->find_first("Status");
907 string
& s
= field
->get_data();
909 if (stringcasecmp(s
, "Enabled") == 0) {
911 } else if (stringcasecmp(s
, "Suspended") != 0) {
919 int RGWSetBucketVersioning_ObjStore_S3::get_params()
921 char *data
= nullptr;
924 rgw_rest_read_all_input(s
, &data
, &len
, s
->cct
->_conf
->rgw_max_put_param_size
, false);
929 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
931 r
= do_aws4_auth_completion();
936 RGWSetBucketVersioningParser parser
;
938 if (!parser
.init()) {
939 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
944 if (!parser
.parse(data
, len
, 1)) {
945 ldout(s
->cct
, 10) << "failed to parse data: " << data
<< dendl
;
950 if (!store
->is_meta_master()) {
951 /* only need to keep this data around if we're not meta master */
952 in_data
.append(data
, len
);
955 r
= parser
.get_versioning_status(&enable_versioning
);
960 void RGWSetBucketVersioning_ObjStore_S3::send_response()
963 set_req_state_err(s
, op_ret
);
968 int RGWSetBucketWebsite_ObjStore_S3::get_params()
970 char *data
= nullptr;
972 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
973 int r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
979 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
981 r
= do_aws4_auth_completion();
986 bufferptr
in_ptr(data
, len
);
987 in_data
.append(in_ptr
);
989 RGWXMLDecoder::XMLParser parser
;
990 if (!parser
.init()) {
991 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
995 if (!parser
.parse(data
, len
, 1)) {
996 string
str(data
, len
);
997 ldout(s
->cct
, 5) << "failed to parse xml: " << str
<< dendl
;
1002 RGWXMLDecoder::decode_xml("WebsiteConfiguration", website_conf
, &parser
, true);
1003 } catch (RGWXMLDecoder::err
& err
) {
1004 string
str(data
, len
);
1005 ldout(s
->cct
, 5) << "unexpected xml: " << str
<< dendl
;
1012 void RGWSetBucketWebsite_ObjStore_S3::send_response()
1015 set_req_state_err(s
, op_ret
);
1020 void RGWDeleteBucketWebsite_ObjStore_S3::send_response()
1023 op_ret
= STATUS_NO_CONTENT
;
1025 set_req_state_err(s
, op_ret
);
1030 void RGWGetBucketWebsite_ObjStore_S3::send_response()
1033 set_req_state_err(s
, op_ret
);
1035 end_header(s
, this, "application/xml");
1042 RGWBucketWebsiteConf
& conf
= s
->bucket_info
.website_conf
;
1044 s
->formatter
->open_object_section_in_ns("WebsiteConfiguration", XMLNS_AWS_S3
);
1045 conf
.dump_xml(s
->formatter
);
1046 s
->formatter
->close_section(); // WebsiteConfiguration
1047 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1050 static void dump_bucket_metadata(struct req_state
*s
, RGWBucketEnt
& bucket
)
1052 dump_header(s
, "X-RGW-Object-Count", static_cast<long long>(bucket
.count
));
1053 dump_header(s
, "X-RGW-Bytes-Used", static_cast<long long>(bucket
.size
));
1056 void RGWStatBucket_ObjStore_S3::send_response()
1059 dump_bucket_metadata(s
, bucket
);
1062 set_req_state_err(s
, op_ret
);
1065 end_header(s
, this);
1069 static int create_s3_policy(struct req_state
*s
, RGWRados
*store
,
1070 RGWAccessControlPolicy_S3
& s3policy
,
1073 if (s
->has_acl_header
) {
1074 if (!s
->canned_acl
.empty())
1075 return -ERR_INVALID_REQUEST
;
1077 return s3policy
.create_from_headers(store
, s
->info
.env
, owner
);
1080 return s3policy
.create_canned(owner
, s
->bucket_owner
, s
->canned_acl
);
1083 class RGWLocationConstraint
: public XMLObj
1086 RGWLocationConstraint() {}
1087 ~RGWLocationConstraint() override
{}
1088 bool xml_end(const char *el
) override
{
1092 location_constraint
= get_data();
1097 string location_constraint
;
1100 class RGWCreateBucketConfig
: public XMLObj
1103 RGWCreateBucketConfig() {}
1104 ~RGWCreateBucketConfig() override
{}
1107 class RGWCreateBucketParser
: public RGWXMLParser
1109 XMLObj
*alloc_obj(const char *el
) override
{
1114 RGWCreateBucketParser() {}
1115 ~RGWCreateBucketParser() override
{}
1117 bool get_location_constraint(string
& zone_group
) {
1118 XMLObj
*config
= find_first("CreateBucketConfiguration");
1122 XMLObj
*constraint
= config
->find_first("LocationConstraint");
1126 zone_group
= constraint
->get_data();
1132 int RGWCreateBucket_ObjStore_S3::get_params()
1134 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1136 int r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
1143 char *data
= nullptr;
1145 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
1146 op_ret
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
1148 if ((op_ret
< 0) && (op_ret
!= -ERR_LENGTH_REQUIRED
))
1151 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
1153 const int auth_ret
= do_aws4_auth_completion();
1158 bufferptr
in_ptr(data
, len
);
1159 in_data
.append(in_ptr
);
1162 RGWCreateBucketParser parser
;
1164 if (!parser
.init()) {
1165 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
1169 bool success
= parser
.parse(data
, len
, 1);
1170 ldout(s
->cct
, 20) << "create bucket input data=" << data
<< dendl
;
1173 ldout(s
->cct
, 0) << "failed to parse input: " << data
<< dendl
;
1177 if (!parser
.get_location_constraint(location_constraint
)) {
1178 ldout(s
->cct
, 0) << "provided input did not specify location constraint correctly" << dendl
;
1182 ldout(s
->cct
, 10) << "create bucket location constraint: "
1183 << location_constraint
<< dendl
;
1186 size_t pos
= location_constraint
.find(':');
1187 if (pos
!= string::npos
) {
1188 placement_rule
= location_constraint
.substr(pos
+ 1);
1189 location_constraint
= location_constraint
.substr(0, pos
);
1195 void RGWCreateBucket_ObjStore_S3::send_response()
1197 if (op_ret
== -ERR_BUCKET_EXISTS
)
1200 set_req_state_err(s
, op_ret
);
1207 if (s
->system_request
) {
1208 JSONFormatter f
; /* use json formatter for system requests output */
1210 f
.open_object_section("info");
1211 encode_json("entry_point_object_ver", ep_objv
, &f
);
1212 encode_json("object_ver", info
.objv_tracker
.read_version
, &f
);
1213 encode_json("bucket_info", info
, &f
);
1215 rgw_flush_formatter_and_reset(s
, &f
);
1219 void RGWDeleteBucket_ObjStore_S3::send_response()
1223 r
= STATUS_NO_CONTENT
;
1225 set_req_state_err(s
, r
);
1227 end_header(s
, this);
1229 if (s
->system_request
) {
1230 JSONFormatter f
; /* use json formatter for system requests output */
1232 f
.open_object_section("info");
1233 encode_json("object_ver", objv_tracker
.read_version
, &f
);
1235 rgw_flush_formatter_and_reset(s
, &f
);
1239 int RGWPutObj_ObjStore_S3::get_params()
1242 return -ERR_LENGTH_REQUIRED
;
1244 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
1245 map
<string
, bufferlist
> src_attrs
;
1249 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1250 ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
1256 if_match
= s
->info
.env
->get("HTTP_IF_MATCH");
1257 if_nomatch
= s
->info
.env
->get("HTTP_IF_NONE_MATCH");
1258 copy_source
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE");
1259 copy_source_range
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE");
1261 /* handle x-amz-copy-source */
1264 if (*copy_source
== '/') ++copy_source
;
1265 copy_source_bucket_name
= copy_source
;
1266 pos
= copy_source_bucket_name
.find("/");
1267 if (pos
== std::string::npos
) {
1269 ldout(s
->cct
, 5) << "x-amz-copy-source bad format" << dendl
;
1272 copy_source_object_name
= copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
1273 copy_source_bucket_name
= copy_source_bucket_name
.substr(0, pos
);
1274 #define VERSION_ID_STR "?versionId="
1275 pos
= copy_source_object_name
.find(VERSION_ID_STR
);
1276 if (pos
== std::string::npos
) {
1277 copy_source_object_name
= url_decode(copy_source_object_name
);
1279 copy_source_version_id
= copy_source_object_name
.substr(pos
+ sizeof(VERSION_ID_STR
) - 1);
1280 copy_source_object_name
= url_decode(copy_source_object_name
.substr(0, pos
));
1282 pos
= copy_source_bucket_name
.find(":");
1283 if (pos
== std::string::npos
) {
1284 copy_source_tenant_name
= s
->src_tenant_name
;
1286 copy_source_tenant_name
= copy_source_bucket_name
.substr(0, pos
);
1287 copy_source_bucket_name
= copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
1288 if (copy_source_bucket_name
.empty()) {
1290 ldout(s
->cct
, 5) << "source bucket name is empty" << dendl
;
1294 ret
= store
->get_bucket_info(obj_ctx
,
1295 copy_source_tenant_name
,
1296 copy_source_bucket_name
,
1297 copy_source_bucket_info
,
1300 ldout(s
->cct
, 5) << __func__
<< "(): get_bucket_info() returned ret=" << ret
<< dendl
;
1304 /* handle x-amz-copy-source-range */
1306 if (copy_source_range
) {
1307 string range
= copy_source_range
;
1308 pos
= range
.find("=");
1309 if (pos
== std::string::npos
) {
1311 ldout(s
->cct
, 5) << "x-amz-copy-source-range bad format" << dendl
;
1314 range
= range
.substr(pos
+ 1);
1315 pos
= range
.find("-");
1316 if (pos
== std::string::npos
) {
1318 ldout(s
->cct
, 5) << "x-amz-copy-source-range bad format" << dendl
;
1321 string first
= range
.substr(0, pos
);
1322 string last
= range
.substr(pos
+ 1);
1323 copy_source_range_fst
= strtoull(first
.c_str(), NULL
, 10);
1324 copy_source_range_lst
= strtoull(last
.c_str(), NULL
, 10);
1329 /* handle object tagging */
1330 auto tag_str
= s
->info
.env
->get("HTTP_X_AMZ_TAGGING");
1332 obj_tags
= ceph::make_unique
<RGWObjTags
>();
1333 ret
= obj_tags
->set_from_string(tag_str
);
1335 ldout(s
->cct
,0) << "setting obj tags failed with " << ret
<< dendl
;
1336 if (ret
== -ERR_INVALID_TAG
){
1337 ret
= -EINVAL
; //s3 returns only -EINVAL for PUT requests
1344 return RGWPutObj_ObjStore::get_params();
1347 int RGWPutObj_ObjStore_S3::get_data(bufferlist
& bl
)
1349 const int ret
= RGWPutObj_ObjStore::get_data(bl
);
1351 const int ret_auth
= do_aws4_auth_completion();
1360 static int get_success_retcode(int code
)
1364 return STATUS_CREATED
;
1366 return STATUS_NO_CONTENT
;
1371 void RGWPutObj_ObjStore_S3::send_response()
1374 set_req_state_err(s
, op_ret
);
1377 if (s
->cct
->_conf
->rgw_s3_success_create_obj_status
) {
1378 op_ret
= get_success_retcode(
1379 s
->cct
->_conf
->rgw_s3_success_create_obj_status
);
1380 set_req_state_err(s
, op_ret
);
1385 dump_content_length(s
, 0);
1386 for (auto &it
: crypt_http_responses
)
1387 dump_header(s
, it
.first
, it
.second
);
1390 end_header(s
, this, "application/xml");
1394 time_t secs
= (time_t)ut
.sec();
1395 gmtime_r(&secs
, &tmp
);
1396 char buf
[TIME_BUF_SIZE
];
1397 s
->formatter
->open_object_section_in_ns("CopyPartResult",
1398 "http://s3.amazonaws.com/doc/2006-03-01/");
1399 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%T.000Z", &tmp
) > 0) {
1400 s
->formatter
->dump_string("LastModified", buf
);
1402 s
->formatter
->dump_string("ETag", etag
);
1403 s
->formatter
->close_section();
1404 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1408 if (s
->system_request
&& !real_clock::is_zero(mtime
)) {
1409 dump_epoch_header(s
, "Rgwx-Mtime", mtime
);
1411 end_header(s
, this);
1414 static inline int get_obj_attrs(RGWRados
*store
, struct req_state
*s
, rgw_obj
& obj
, map
<string
, bufferlist
>& attrs
)
1416 RGWRados::Object
op_target(store
, s
->bucket_info
, *static_cast<RGWObjectCtx
*>(s
->obj_ctx
), obj
);
1417 RGWRados::Object::Read
read_op(&op_target
);
1419 read_op
.params
.attrs
= &attrs
;
1421 return read_op
.prepare();
1424 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const std::string
& value
)
1428 attrs
.emplace(key
, std::move(bl
));
1431 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const char* value
)
1435 attrs
.emplace(key
, std::move(bl
));
1438 int RGWPutObj_ObjStore_S3::get_decrypt_filter(
1439 std::unique_ptr
<RGWGetDataCB
>* filter
,
1441 map
<string
, bufferlist
>& attrs
,
1442 bufferlist
* manifest_bl
)
1444 std::map
<std::string
, std::string
> crypt_http_responses_unused
;
1447 std::unique_ptr
<BlockCrypt
> block_crypt
;
1448 res
= rgw_s3_prepare_decrypt(s
, attrs
, &block_crypt
, crypt_http_responses_unused
);
1450 if (block_crypt
!= nullptr) {
1451 auto f
= std::unique_ptr
<RGWGetObj_BlockDecrypt
>(new RGWGetObj_BlockDecrypt(s
->cct
, cb
, std::move(block_crypt
)));
1452 //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
1454 if (manifest_bl
!= nullptr) {
1455 res
= f
->read_manifest(*manifest_bl
);
1457 *filter
= std::move(f
);
1466 int RGWPutObj_ObjStore_S3::get_encrypt_filter(
1467 std::unique_ptr
<RGWPutObjDataProcessor
>* filter
,
1468 RGWPutObjDataProcessor
* cb
)
1471 RGWPutObjProcessor_Multipart
* multi_processor
=dynamic_cast<RGWPutObjProcessor_Multipart
*>(cb
);
1472 if (multi_processor
!= nullptr) {
1473 RGWMPObj
* mp
= nullptr;
1474 multi_processor
->get_mp(&mp
);
1475 if (mp
!= nullptr) {
1476 map
<string
, bufferlist
> xattrs
;
1478 meta_oid
= mp
->get_meta();
1481 obj
.init_ns(s
->bucket
, meta_oid
, RGW_OBJ_NS_MULTIPART
);
1482 obj
.set_in_extra_data(true);
1483 res
= get_obj_attrs(store
, s
, obj
, xattrs
);
1485 std::unique_ptr
<BlockCrypt
> block_crypt
;
1486 /* We are adding to existing object.
1487 * We use crypto mode that configured as if we were decrypting. */
1488 res
= rgw_s3_prepare_decrypt(s
, xattrs
, &block_crypt
, crypt_http_responses
);
1489 if (res
== 0 && block_crypt
!= nullptr)
1490 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
1491 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1494 /* it is ok, to not have encryption at all */
1498 std::unique_ptr
<BlockCrypt
> block_crypt
;
1499 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, &block_crypt
, crypt_http_responses
);
1500 if (res
== 0 && block_crypt
!= nullptr) {
1501 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
1502 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1508 void RGWPostObj_ObjStore_S3::rebuild_key(string
& key
)
1510 static string var
= "${filename}";
1511 int pos
= key
.find(var
);
1515 string new_key
= key
.substr(0, pos
);
1516 new_key
.append(filename
);
1517 new_key
.append(key
.substr(pos
+ var
.size()));
1522 std::string
RGWPostObj_ObjStore_S3::get_current_filename() const
1524 return s
->object
.name
;
1527 std::string
RGWPostObj_ObjStore_S3::get_current_content_type() const
1529 return content_type
;
1532 int RGWPostObj_ObjStore_S3::get_params()
1534 op_ret
= RGWPostObj_ObjStore::get_params();
1539 ldout(s
->cct
, 20) << "adding bucket to policy env: " << s
->bucket
.name
1541 env
.add_var("bucket", s
->bucket
.name
);
1545 struct post_form_part part
;
1546 int r
= read_form_part_header(&part
, done
);
1550 if (s
->cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 20)) {
1551 ldout(s
->cct
, 20) << "read part header -- part.name="
1552 << part
.name
<< dendl
;
1554 for (const auto& pair
: part
.fields
) {
1555 ldout(s
->cct
, 20) << "field.name=" << pair
.first
<< dendl
;
1556 ldout(s
->cct
, 20) << "field.val=" << pair
.second
.val
<< dendl
;
1557 ldout(s
->cct
, 20) << "field.params:" << dendl
;
1559 for (const auto& param_pair
: pair
.second
.params
) {
1560 ldout(s
->cct
, 20) << " " << param_pair
.first
1561 << " -> " << param_pair
.second
<< dendl
;
1566 if (done
) { /* unexpected here */
1567 err_msg
= "Malformed request";
1571 if (stringcasecmp(part
.name
, "file") == 0) { /* beginning of data transfer */
1572 struct post_part_field
& field
= part
.fields
["Content-Disposition"];
1573 map
<string
, string
>::iterator iter
= field
.params
.find("filename");
1574 if (iter
!= field
.params
.end()) {
1575 filename
= iter
->second
;
1577 parts
[part
.name
] = part
;
1582 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1583 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
1584 if (r
< 0 || !boundary
) {
1585 err_msg
= "Couldn't find boundary";
1588 parts
[part
.name
] = part
;
1589 string
part_str(part
.data
.c_str(), part
.data
.length());
1590 env
.add_var(part
.name
, part_str
);
1594 if (!part_str(parts
, "key", &object_str
)) {
1595 err_msg
= "Key not specified";
1599 s
->object
= rgw_obj_key(object_str
);
1601 rebuild_key(s
->object
.name
);
1603 if (s
->object
.empty()) {
1604 err_msg
= "Empty object name";
1608 env
.add_var("key", s
->object
.name
);
1610 part_str(parts
, "Content-Type", &content_type
);
1611 env
.add_var("Content-Type", content_type
);
1613 map
<string
, struct post_form_part
, ltstr_nocase
>::iterator piter
=
1614 parts
.upper_bound(RGW_AMZ_META_PREFIX
);
1615 for (; piter
!= parts
.end(); ++piter
) {
1616 string n
= piter
->first
;
1617 if (strncasecmp(n
.c_str(), RGW_AMZ_META_PREFIX
,
1618 sizeof(RGW_AMZ_META_PREFIX
) - 1) != 0)
1621 string attr_name
= RGW_ATTR_PREFIX
;
1622 attr_name
.append(n
);
1624 /* need to null terminate it */
1625 bufferlist
& data
= piter
->second
.data
;
1626 string str
= string(data
.c_str(), data
.length());
1629 attr_bl
.append(str
.c_str(), str
.size() + 1);
1631 attrs
[attr_name
] = attr_bl
;
1633 // TODO: refactor this and the above loop to share code
1634 piter
= parts
.find(RGW_AMZ_WEBSITE_REDIRECT_LOCATION
);
1635 if (piter
!= parts
.end()) {
1636 string n
= piter
->first
;
1637 string attr_name
= RGW_ATTR_PREFIX
;
1638 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
;
1649 int r
= get_policy();
1658 min_len
= post_policy
.min_length
;
1659 max_len
= post_policy
.max_length
;
1666 int RGWPostObj_ObjStore_S3::get_tags()
1669 if (part_str(parts
, "tagging", &tags_str
)) {
1670 RGWObjTagsXMLParser parser
;
1671 if (!parser
.init()){
1672 ldout(s
->cct
, 0) << "Couldn't init RGWObjTags XML parser" << dendl
;
1673 err_msg
= "Server couldn't process the request";
1674 return -EINVAL
; // TODO: This class of errors in rgw code should be a 5XX error
1676 if (!parser
.parse(tags_str
.c_str(), tags_str
.size(), 1)) {
1677 ldout(s
->cct
,0 ) << "Invalid Tagging XML" << dendl
;
1678 err_msg
= "Invalid Tagging XML";
1682 RGWObjTagSet_S3
*obj_tags_s3
;
1683 RGWObjTagging_S3
*tagging
;
1685 tagging
= static_cast<RGWObjTagging_S3
*>(parser
.find_first("Tagging"));
1686 obj_tags_s3
= static_cast<RGWObjTagSet_S3
*>(tagging
->find_first("TagSet"));
1688 return -ERR_MALFORMED_XML
;
1691 RGWObjTags obj_tags
;
1692 int r
= obj_tags_s3
->rebuild(obj_tags
);
1697 obj_tags
.encode(tags_bl
);
1698 ldout(s
->cct
, 20) << "Read " << obj_tags
.count() << "tags" << dendl
;
1699 attrs
[RGW_ATTR_TAGS
] = tags_bl
;
1706 int RGWPostObj_ObjStore_S3::get_policy()
1708 if (part_bl(parts
, "policy", &s
->auth
.s3_postobj_creds
.encoded_policy
)) {
1709 bool aws4_auth
= false;
1711 /* x-amz-algorithm handling */
1712 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
1713 if ((part_str(parts
, "x-amz-algorithm", &s
->auth
.s3_postobj_creds
.x_amz_algorithm
)) &&
1714 (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
)) {
1715 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v4 (AWS4-HMAC-SHA256)" << dendl
;
1718 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v2" << dendl
;
1721 // check that the signature matches the encoded policy
1725 /* x-amz-credential handling */
1726 if (!part_str(parts
, "x-amz-credential",
1727 &s
->auth
.s3_postobj_creds
.x_amz_credential
)) {
1728 ldout(s
->cct
, 0) << "No S3 aws4 credential found!" << dendl
;
1729 err_msg
= "Missing aws4 credential";
1733 /* x-amz-signature handling */
1734 if (!part_str(parts
, "x-amz-signature",
1735 &s
->auth
.s3_postobj_creds
.signature
)) {
1736 ldout(s
->cct
, 0) << "No aws4 signature found!" << dendl
;
1737 err_msg
= "Missing aws4 signature";
1741 /* x-amz-date handling */
1742 std::string received_date_str
;
1743 if (!part_str(parts
, "x-amz-date", &received_date_str
)) {
1744 ldout(s
->cct
, 0) << "No aws4 date found!" << dendl
;
1745 err_msg
= "Missing aws4 date";
1751 // check that the signature matches the encoded policy
1752 if (!part_str(parts
, "AWSAccessKeyId",
1753 &s
->auth
.s3_postobj_creds
.access_key
)) {
1754 ldout(s
->cct
, 0) << "No S3 aws2 access key found!" << dendl
;
1755 err_msg
= "Missing aws2 access key";
1759 if (!part_str(parts
, "signature", &s
->auth
.s3_postobj_creds
.signature
)) {
1760 ldout(s
->cct
, 0) << "No aws2 signature found!" << dendl
;
1761 err_msg
= "Missing aws2 signature";
1766 /* FIXME: this is a makeshift solution. The browser upload authentication will be
1767 * handled by an instance of rgw::auth::Completer spawned in Handler's authorize()
1769 const int ret
= rgw::auth::Strategy::apply(auth_registry_ptr
->get_s3_post(), s
);
1773 /* Populate the owner info. */
1774 s
->owner
.set_id(s
->user
->user_id
);
1775 s
->owner
.set_name(s
->user
->display_name
);
1776 ldout(s
->cct
, 20) << "Successful Signature Verification!" << dendl
;
1779 ceph::bufferlist decoded_policy
;
1781 decoded_policy
.decode_base64(s
->auth
.s3_postobj_creds
.encoded_policy
);
1782 } catch (buffer::error
& err
) {
1783 ldout(s
->cct
, 0) << "failed to decode_base64 policy" << dendl
;
1784 err_msg
= "Could not decode policy";
1788 decoded_policy
.append('\0'); // NULL terminate
1789 ldout(s
->cct
, 20) << "POST policy: " << decoded_policy
.c_str() << dendl
;
1792 int r
= post_policy
.from_json(decoded_policy
, err_msg
);
1794 if (err_msg
.empty()) {
1795 err_msg
= "Failed to parse policy";
1797 ldout(s
->cct
, 0) << "failed to parse policy" << dendl
;
1803 post_policy
.set_var_checked("x-amz-signature");
1806 post_policy
.set_var_checked("AWSAccessKeyId");
1807 post_policy
.set_var_checked("signature");
1809 post_policy
.set_var_checked("policy");
1811 r
= post_policy
.check(&env
, err_msg
);
1813 if (err_msg
.empty()) {
1814 err_msg
= "Policy check failed";
1816 ldout(s
->cct
, 0) << "policy check failed" << dendl
;
1821 ldout(s
->cct
, 0) << "No attached policy found!" << dendl
;
1825 part_str(parts
, "acl", &canned_acl
);
1827 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1828 ldout(s
->cct
, 20) << "canned_acl=" << canned_acl
<< dendl
;
1829 if (s3policy
.create_canned(s
->owner
, s
->bucket_owner
, canned_acl
) < 0) {
1830 err_msg
= "Bad canned ACLs";
1839 int RGWPostObj_ObjStore_S3::complete_get_params()
1843 struct post_form_part part
;
1844 int r
= read_form_part_header(&part
, done
);
1849 ceph::bufferlist part_data
;
1851 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1852 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
1853 if (r
< 0 || !boundary
) {
1857 /* Just reading the data but not storing any results of that. */
1863 int RGWPostObj_ObjStore_S3::get_data(ceph::bufferlist
& bl
, bool& again
)
1868 const uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1869 int r
= read_data(bl
, chunk_size
, boundary
, done
);
1876 /* Reached end of data, let's drain the rest of the params */
1877 r
= complete_get_params();
1888 void RGWPostObj_ObjStore_S3::send_response()
1890 if (op_ret
== 0 && parts
.count("success_action_redirect")) {
1893 part_str(parts
, "success_action_redirect", &redirect
);
1898 string etag_str
= "\"";
1900 etag_str
.append(etag
);
1901 etag_str
.append("\"");
1905 url_encode(s
->bucket_tenant
, tenant
); /* surely overkill, but cheap */
1906 url_encode(s
->bucket_name
, bucket
);
1907 url_encode(s
->object
.name
, key
);
1908 url_encode(etag_str
, etag_url
);
1910 if (!s
->bucket_tenant
.empty()) {
1912 * What we really would like is to quaily the bucket name, so
1913 * that the client could simply copy it and paste into next request.
1914 * Unfortunately, in S3 we cannot know if the client will decide
1915 * to come through DNS, with "bucket.tenant" sytanx, or through
1916 * URL with "tenant\bucket" syntax. Therefore, we provide the
1917 * tenant separately.
1919 redirect
.append("?tenant=");
1920 redirect
.append(tenant
);
1921 redirect
.append("&bucket=");
1922 redirect
.append(bucket
);
1924 redirect
.append("?bucket=");
1925 redirect
.append(bucket
);
1927 redirect
.append("&key=");
1928 redirect
.append(key
);
1929 redirect
.append("&etag=");
1930 redirect
.append(etag_url
);
1932 int r
= check_utf8(redirect
.c_str(), redirect
.size());
1937 dump_redirect(s
, redirect
);
1938 op_ret
= STATUS_REDIRECT
;
1939 } else if (op_ret
== 0 && parts
.count("success_action_status")) {
1940 string status_string
;
1941 uint32_t status_int
;
1943 part_str(parts
, "success_action_status", &status_string
);
1945 int r
= stringtoul(status_string
, &status_int
);
1951 switch (status_int
) {
1955 op_ret
= STATUS_CREATED
;
1958 op_ret
= STATUS_NO_CONTENT
;
1961 } else if (! op_ret
) {
1962 op_ret
= STATUS_NO_CONTENT
;
1966 if (op_ret
== STATUS_CREATED
) {
1967 for (auto &it
: crypt_http_responses
)
1968 dump_header(s
, it
.first
, it
.second
);
1969 s
->formatter
->open_object_section("PostResponse");
1970 if (g_conf
->rgw_dns_name
.length())
1971 s
->formatter
->dump_format("Location", "%s/%s",
1972 s
->info
.script_uri
.c_str(),
1973 s
->object
.name
.c_str());
1974 if (!s
->bucket_tenant
.empty())
1975 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
1976 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
1977 s
->formatter
->dump_string("Key", s
->object
.name
);
1978 s
->formatter
->close_section();
1980 s
->err
.message
= err_msg
;
1981 set_req_state_err(s
, op_ret
);
1984 dump_content_length(s
, s
->formatter
->get_len());
1986 end_header(s
, this);
1987 if (op_ret
!= STATUS_CREATED
)
1990 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1993 int RGWPostObj_ObjStore_S3::get_encrypt_filter(
1994 std::unique_ptr
<RGWPutObjDataProcessor
>* filter
, RGWPutObjDataProcessor
* cb
)
1997 std::unique_ptr
<BlockCrypt
> block_crypt
;
1998 res
= rgw_s3_prepare_encrypt(s
, attrs
, &parts
, &block_crypt
, crypt_http_responses
);
1999 if (res
== 0 && block_crypt
!= nullptr) {
2000 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
2001 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
2008 int RGWDeleteObj_ObjStore_S3::get_params()
2010 const char *if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_DELETE_IF_UNMODIFIED_SINCE");
2012 if (s
->system_request
) {
2013 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"no-precondition-error", &no_precondition_error
, false);
2017 std::string if_unmod_decoded
= url_decode(if_unmod
);
2020 if (utime_t::parse_date(if_unmod_decoded
, &epoch
, &nsec
) < 0) {
2021 ldout(s
->cct
, 10) << "failed to parse time: " << if_unmod_decoded
<< dendl
;
2024 unmod_since
= utime_t(epoch
, nsec
).to_real_time();
2030 void RGWDeleteObj_ObjStore_S3::send_response()
2036 r
= STATUS_NO_CONTENT
;
2038 set_req_state_err(s
, r
);
2040 if (!version_id
.empty()) {
2041 dump_header(s
, "x-amz-version-id", version_id
);
2043 if (delete_marker
) {
2044 dump_header(s
, "x-amz-delete-marker", "true");
2046 end_header(s
, this);
2049 int RGWCopyObj_ObjStore_S3::init_dest_policy()
2051 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2053 /* build a policy for the target object */
2054 int r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
2058 dest_policy
= s3policy
;
2063 int RGWCopyObj_ObjStore_S3::get_params()
2065 if_mod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MODIFIED_SINCE");
2066 if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_UNMODIFIED_SINCE");
2067 if_match
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MATCH");
2068 if_nomatch
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_NONE_MATCH");
2070 src_tenant_name
= s
->src_tenant_name
;
2071 src_bucket_name
= s
->src_bucket_name
;
2072 src_object
= s
->src_object
;
2073 dest_tenant_name
= s
->bucket
.tenant
;
2074 dest_bucket_name
= s
->bucket
.name
;
2075 dest_object
= s
->object
.name
;
2077 if (s
->system_request
) {
2078 source_zone
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"source-zone");
2079 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"copy-if-newer", ©_if_newer
, false);
2080 if (!source_zone
.empty()) {
2081 client_id
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"client-id");
2082 op_id
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"op-id");
2084 if (client_id
.empty() || op_id
.empty()) {
2086 RGW_SYS_PARAM_PREFIX
"client-id or "
2087 RGW_SYS_PARAM_PREFIX
"op-id were not provided, "
2088 "required for intra-region copy"
2095 const char *md_directive
= s
->info
.env
->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
2097 if (strcasecmp(md_directive
, "COPY") == 0) {
2098 attrs_mod
= RGWRados::ATTRSMOD_NONE
;
2099 } else if (strcasecmp(md_directive
, "REPLACE") == 0) {
2100 attrs_mod
= RGWRados::ATTRSMOD_REPLACE
;
2101 } else if (!source_zone
.empty()) {
2102 attrs_mod
= RGWRados::ATTRSMOD_NONE
; // default for intra-zone_group copy
2104 ldout(s
->cct
, 0) << "invalid metadata directive" << dendl
;
2109 if (source_zone
.empty() &&
2110 (dest_tenant_name
.compare(src_tenant_name
) == 0) &&
2111 (dest_bucket_name
.compare(src_bucket_name
) == 0) &&
2112 (dest_object
.compare(src_object
.name
) == 0) &&
2113 src_object
.instance
.empty() &&
2114 (attrs_mod
!= RGWRados::ATTRSMOD_REPLACE
)) {
2115 /* can only copy object into itself if replacing attrs */
2116 ldout(s
->cct
, 0) << "can't copy object into itself if not replacing attrs"
2118 return -ERR_INVALID_REQUEST
;
2123 void RGWCopyObj_ObjStore_S3::send_partial_response(off_t ofs
)
2125 if (! sent_header
) {
2127 set_req_state_err(s
, op_ret
);
2130 end_header(s
, this, "application/xml");
2132 s
->formatter
->open_object_section_in_ns("CopyObjectResult", XMLNS_AWS_S3
);
2136 /* Send progress field. Note that this diverge from the original S3
2137 * spec. We do this in order to keep connection alive.
2139 s
->formatter
->dump_int("Progress", (uint64_t)ofs
);
2141 rgw_flush_formatter(s
, s
->formatter
);
2144 void RGWCopyObj_ObjStore_S3::send_response()
2147 send_partial_response(0);
2150 dump_time(s
, "LastModified", &mtime
);
2151 std::string etag_str
= etag
.to_str();
2152 if (! etag_str
.empty()) {
2153 s
->formatter
->dump_string("ETag", std::move(etag_str
));
2155 s
->formatter
->close_section();
2156 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2160 void RGWGetACLs_ObjStore_S3::send_response()
2163 set_req_state_err(s
, op_ret
);
2165 end_header(s
, this, "application/xml");
2167 rgw_flush_formatter(s
, s
->formatter
);
2171 int RGWPutACLs_ObjStore_S3::get_params()
2173 int ret
= RGWPutACLs_ObjStore::get_params();
2175 const int ret_auth
= do_aws4_auth_completion();
2183 int RGWPutACLs_ObjStore_S3::get_policy_from_state(RGWRados
*store
,
2184 struct req_state
*s
,
2187 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2189 // bucket-* canned acls do not apply to bucket
2190 if (s
->object
.empty()) {
2191 if (s
->canned_acl
.find("bucket") != string::npos
)
2192 s
->canned_acl
.clear();
2195 int r
= create_s3_policy(s
, store
, s3policy
, owner
);
2199 s3policy
.to_xml(ss
);
2204 void RGWPutACLs_ObjStore_S3::send_response()
2207 set_req_state_err(s
, op_ret
);
2209 end_header(s
, this, "application/xml");
2213 void RGWGetLC_ObjStore_S3::execute()
2215 config
.set_ctx(s
->cct
);
2217 map
<string
, bufferlist
>::iterator aiter
= s
->bucket_attrs
.find(RGW_ATTR_LC
);
2218 if (aiter
== s
->bucket_attrs
.end()) {
2223 bufferlist::iterator
iter(&aiter
->second
);
2225 config
.decode(iter
);
2226 } catch (const buffer::error
& e
) {
2227 ldout(s
->cct
, 0) << __func__
<< "decode life cycle config failed" << dendl
;
2233 void RGWGetLC_ObjStore_S3::send_response()
2236 if (op_ret
== -ENOENT
) {
2237 set_req_state_err(s
, ERR_NO_SUCH_LC
);
2239 set_req_state_err(s
, op_ret
);
2243 end_header(s
, this, "application/xml");
2249 config
.dump_xml(s
->formatter
);
2250 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2253 void RGWPutLC_ObjStore_S3::send_response()
2256 set_req_state_err(s
, op_ret
);
2258 end_header(s
, this, "application/xml");
2262 void RGWDeleteLC_ObjStore_S3::send_response()
2265 op_ret
= STATUS_NO_CONTENT
;
2267 set_req_state_err(s
, op_ret
);
2270 end_header(s
, this, "application/xml");
2274 void RGWGetCORS_ObjStore_S3::send_response()
2277 if (op_ret
== -ENOENT
)
2278 set_req_state_err(s
, ERR_NOT_FOUND
);
2280 set_req_state_err(s
, op_ret
);
2283 end_header(s
, NULL
, "application/xml");
2287 RGWCORSConfiguration_S3
*s3cors
=
2288 static_cast<RGWCORSConfiguration_S3
*>(&bucket_cors
);
2297 int RGWPutCORS_ObjStore_S3::get_params()
2300 char *data
= nullptr;
2302 RGWCORSXMLParser_S3
parser(s
->cct
);
2303 RGWCORSConfiguration_S3
*cors_config
;
2305 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2306 r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
2311 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
2313 r
= do_aws4_auth_completion();
2318 if (!parser
.init()) {
2322 if (!data
|| !parser
.parse(data
, len
, 1)) {
2326 static_cast<RGWCORSConfiguration_S3
*>(parser
.find_first(
2327 "CORSConfiguration"));
2332 // forward bucket cors requests to meta master zone
2333 if (!store
->is_meta_master()) {
2334 /* only need to keep this data around if we're not meta master */
2335 in_data
.append(data
, len
);
2338 if (s
->cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 15)) {
2339 ldout(s
->cct
, 15) << "CORSConfiguration";
2340 cors_config
->to_xml(*_dout
);
2344 cors_config
->encode(cors_bl
);
2349 void RGWPutCORS_ObjStore_S3::send_response()
2352 set_req_state_err(s
, op_ret
);
2354 end_header(s
, NULL
, "application/xml");
2358 void RGWDeleteCORS_ObjStore_S3::send_response()
2361 if (!r
|| r
== -ENOENT
)
2362 r
= STATUS_NO_CONTENT
;
2364 set_req_state_err(s
, r
);
2366 end_header(s
, NULL
);
2369 void RGWOptionsCORS_ObjStore_S3::send_response()
2371 string hdrs
, exp_hdrs
;
2372 uint32_t max_age
= CORS_MAX_AGE_INVALID
;
2373 /*EACCES means, there is no CORS registered yet for the bucket
2374 *ENOENT means, there is no match of the Origin in the list of CORSRule
2376 if (op_ret
== -ENOENT
)
2379 set_req_state_err(s
, op_ret
);
2381 end_header(s
, NULL
);
2384 get_response_params(hdrs
, exp_hdrs
, &max_age
);
2387 dump_access_control(s
, origin
, req_meth
, hdrs
.c_str(), exp_hdrs
.c_str(),
2389 end_header(s
, NULL
);
2392 void RGWGetRequestPayment_ObjStore_S3::send_response()
2395 end_header(s
, this, "application/xml");
2398 s
->formatter
->open_object_section_in_ns("RequestPaymentConfiguration", XMLNS_AWS_S3
);
2399 const char *payer
= requester_pays
? "Requester" : "BucketOwner";
2400 s
->formatter
->dump_string("Payer", payer
);
2401 s
->formatter
->close_section();
2402 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2405 class RGWSetRequestPaymentParser
: public RGWXMLParser
2407 XMLObj
*alloc_obj(const char *el
) override
{
2412 RGWSetRequestPaymentParser() {}
2413 ~RGWSetRequestPaymentParser() override
{}
2415 int get_request_payment_payer(bool *requester_pays
) {
2416 XMLObj
*config
= find_first("RequestPaymentConfiguration");
2420 *requester_pays
= false;
2422 XMLObj
*field
= config
->find_first("Payer");
2426 string
& s
= field
->get_data();
2428 if (stringcasecmp(s
, "Requester") == 0) {
2429 *requester_pays
= true;
2430 } else if (stringcasecmp(s
, "BucketOwner") != 0) {
2438 int RGWSetRequestPayment_ObjStore_S3::get_params()
2442 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2443 int r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
2449 RGWSetRequestPaymentParser parser
;
2451 if (!parser
.init()) {
2452 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
2457 if (!parser
.parse(data
, len
, 1)) {
2458 ldout(s
->cct
, 10) << "failed to parse data: " << data
<< dendl
;
2463 r
= parser
.get_request_payment_payer(&requester_pays
);
2471 void RGWSetRequestPayment_ObjStore_S3::send_response()
2474 set_req_state_err(s
, op_ret
);
2479 int RGWInitMultipart_ObjStore_S3::get_params()
2481 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2482 op_ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
2491 void RGWInitMultipart_ObjStore_S3::send_response()
2494 set_req_state_err(s
, op_ret
);
2496 for (auto &it
: crypt_http_responses
)
2497 dump_header(s
, it
.first
, it
.second
);
2498 end_header(s
, this, "application/xml");
2501 s
->formatter
->open_object_section_in_ns("InitiateMultipartUploadResult", XMLNS_AWS_S3
);
2502 if (!s
->bucket_tenant
.empty())
2503 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2504 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2505 s
->formatter
->dump_string("Key", s
->object
.name
);
2506 s
->formatter
->dump_string("UploadId", upload_id
);
2507 s
->formatter
->close_section();
2508 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2512 int RGWInitMultipart_ObjStore_S3::prepare_encryption(map
<string
, bufferlist
>& attrs
)
2515 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, nullptr, crypt_http_responses
);
2519 int RGWCompleteMultipart_ObjStore_S3::get_params()
2521 int ret
= RGWCompleteMultipart_ObjStore::get_params();
2526 return do_aws4_auth_completion();
2529 void RGWCompleteMultipart_ObjStore_S3::send_response()
2532 set_req_state_err(s
, op_ret
);
2534 end_header(s
, this, "application/xml");
2537 s
->formatter
->open_object_section_in_ns("CompleteMultipartUploadResult", XMLNS_AWS_S3
);
2538 if (!s
->bucket_tenant
.empty()) {
2539 if (s
->info
.domain
.length()) {
2540 s
->formatter
->dump_format("Location", "%s.%s.%s",
2541 s
->bucket_name
.c_str(),
2542 s
->bucket_tenant
.c_str(),
2543 s
->info
.domain
.c_str());
2545 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2547 if (s
->info
.domain
.length()) {
2548 s
->formatter
->dump_format("Location", "%s.%s",
2549 s
->bucket_name
.c_str(),
2550 s
->info
.domain
.c_str());
2553 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2554 s
->formatter
->dump_string("Key", s
->object
.name
);
2555 s
->formatter
->dump_string("ETag", etag
);
2556 s
->formatter
->close_section();
2557 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2561 void RGWAbortMultipart_ObjStore_S3::send_response()
2565 r
= STATUS_NO_CONTENT
;
2567 set_req_state_err(s
, r
);
2569 end_header(s
, this);
2572 void RGWListMultipart_ObjStore_S3::send_response()
2575 set_req_state_err(s
, op_ret
);
2577 end_header(s
, this, "application/xml");
2581 s
->formatter
->open_object_section_in_ns("ListPartsResult", XMLNS_AWS_S3
);
2582 map
<uint32_t, RGWUploadPartInfo
>::iterator iter
;
2583 map
<uint32_t, RGWUploadPartInfo
>::reverse_iterator test_iter
;
2586 iter
= parts
.begin();
2587 test_iter
= parts
.rbegin();
2588 if (test_iter
!= parts
.rend()) {
2589 cur_max
= test_iter
->first
;
2591 if (!s
->bucket_tenant
.empty())
2592 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2593 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2594 s
->formatter
->dump_string("Key", s
->object
.name
);
2595 s
->formatter
->dump_string("UploadId", upload_id
);
2596 s
->formatter
->dump_string("StorageClass", "STANDARD");
2597 s
->formatter
->dump_int("PartNumberMarker", marker
);
2598 s
->formatter
->dump_int("NextPartNumberMarker", cur_max
);
2599 s
->formatter
->dump_int("MaxParts", max_parts
);
2600 s
->formatter
->dump_string("IsTruncated", (truncated
? "true" : "false"));
2602 ACLOwner
& owner
= policy
.get_owner();
2603 dump_owner(s
, owner
.get_id(), owner
.get_display_name());
2605 for (; iter
!= parts
.end(); ++iter
) {
2606 RGWUploadPartInfo
& info
= iter
->second
;
2608 s
->formatter
->open_object_section("Part");
2610 dump_time(s
, "LastModified", &info
.modified
);
2612 s
->formatter
->dump_unsigned("PartNumber", info
.num
);
2613 s
->formatter
->dump_format("ETag", "\"%s\"", info
.etag
.c_str());
2614 s
->formatter
->dump_unsigned("Size", info
.accounted_size
);
2615 s
->formatter
->close_section();
2617 s
->formatter
->close_section();
2618 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2622 void RGWListBucketMultiparts_ObjStore_S3::send_response()
2625 set_req_state_err(s
, op_ret
);
2628 end_header(s
, this, "application/xml");
2633 s
->formatter
->open_object_section_in_ns("ListMultipartUploadsResult", XMLNS_AWS_S3
);
2634 if (!s
->bucket_tenant
.empty())
2635 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2636 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2637 if (!prefix
.empty())
2638 s
->formatter
->dump_string("ListMultipartUploadsResult.Prefix", prefix
);
2639 string
& key_marker
= marker
.get_key();
2640 if (!key_marker
.empty())
2641 s
->formatter
->dump_string("KeyMarker", key_marker
);
2642 string
& upload_id_marker
= marker
.get_upload_id();
2643 if (!upload_id_marker
.empty())
2644 s
->formatter
->dump_string("UploadIdMarker", upload_id_marker
);
2645 string next_key
= next_marker
.mp
.get_key();
2646 if (!next_key
.empty())
2647 s
->formatter
->dump_string("NextKeyMarker", next_key
);
2648 string next_upload_id
= next_marker
.mp
.get_upload_id();
2649 if (!next_upload_id
.empty())
2650 s
->formatter
->dump_string("NextUploadIdMarker", next_upload_id
);
2651 s
->formatter
->dump_int("MaxUploads", max_uploads
);
2652 if (!delimiter
.empty())
2653 s
->formatter
->dump_string("Delimiter", delimiter
);
2654 s
->formatter
->dump_string("IsTruncated", (is_truncated
? "true" : "false"));
2657 vector
<RGWMultipartUploadEntry
>::iterator iter
;
2658 for (iter
= uploads
.begin(); iter
!= uploads
.end(); ++iter
) {
2659 RGWMPObj
& mp
= iter
->mp
;
2660 s
->formatter
->open_array_section("Upload");
2661 s
->formatter
->dump_string("Key", mp
.get_key());
2662 s
->formatter
->dump_string("UploadId", mp
.get_upload_id());
2663 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
, "Initiator");
2664 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
);
2665 s
->formatter
->dump_string("StorageClass", "STANDARD");
2666 dump_time(s
, "Initiated", &iter
->obj
.meta
.mtime
);
2667 s
->formatter
->close_section();
2669 if (!common_prefixes
.empty()) {
2670 s
->formatter
->open_array_section("CommonPrefixes");
2671 map
<string
, bool>::iterator pref_iter
;
2672 for (pref_iter
= common_prefixes
.begin();
2673 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
2674 s
->formatter
->dump_string("CommonPrefixes.Prefix", pref_iter
->first
);
2676 s
->formatter
->close_section();
2679 s
->formatter
->close_section();
2680 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2683 int RGWDeleteMultiObj_ObjStore_S3::get_params()
2685 int ret
= RGWDeleteMultiObj_ObjStore::get_params();
2690 return do_aws4_auth_completion();
2693 void RGWDeleteMultiObj_ObjStore_S3::send_status()
2695 if (! status_dumped
) {
2697 set_req_state_err(s
, op_ret
);
2699 status_dumped
= true;
2703 void RGWDeleteMultiObj_ObjStore_S3::begin_response()
2706 if (!status_dumped
) {
2711 end_header(s
, this, "application/xml");
2712 s
->formatter
->open_object_section_in_ns("DeleteResult", XMLNS_AWS_S3
);
2714 rgw_flush_formatter(s
, s
->formatter
);
2717 void RGWDeleteMultiObj_ObjStore_S3::send_partial_response(rgw_obj_key
& key
,
2719 const string
& marker_version_id
, int ret
)
2722 if (op_ret
== 0 && !quiet
) {
2723 s
->formatter
->open_object_section("Deleted");
2724 s
->formatter
->dump_string("Key", key
.name
);
2725 if (!key
.instance
.empty()) {
2726 s
->formatter
->dump_string("VersionId", key
.instance
);
2728 if (delete_marker
) {
2729 s
->formatter
->dump_bool("DeleteMarker", true);
2730 s
->formatter
->dump_string("DeleteMarkerVersionId", marker_version_id
);
2732 s
->formatter
->close_section();
2733 } else if (op_ret
< 0) {
2734 struct rgw_http_error r
;
2737 s
->formatter
->open_object_section("Error");
2740 rgw_get_errno_s3(&r
, err_no
);
2742 s
->formatter
->dump_string("Key", key
.name
);
2743 s
->formatter
->dump_string("VersionId", key
.instance
);
2744 s
->formatter
->dump_int("Code", r
.http_ret
);
2745 s
->formatter
->dump_string("Message", r
.s3_code
);
2746 s
->formatter
->close_section();
2749 rgw_flush_formatter(s
, s
->formatter
);
2753 void RGWDeleteMultiObj_ObjStore_S3::end_response()
2756 s
->formatter
->close_section();
2757 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2760 void RGWGetObjLayout_ObjStore_S3::send_response()
2763 set_req_state_err(s
, op_ret
);
2765 end_header(s
, this, "application/json");
2773 f
.open_object_section("result");
2774 ::encode_json("head", head_obj
, &f
);
2775 ::encode_json("manifest", *manifest
, &f
);
2776 f
.open_array_section("data_location");
2777 for (auto miter
= manifest
->obj_begin(); miter
!= manifest
->obj_end(); ++miter
) {
2778 f
.open_object_section("obj");
2779 rgw_raw_obj raw_loc
= miter
.get_location().get_raw_obj(store
);
2780 ::encode_json("ofs", miter
.get_ofs(), &f
);
2781 ::encode_json("loc", raw_loc
, &f
);
2782 ::encode_json("loc_ofs", miter
.location_ofs(), &f
);
2783 ::encode_json("loc_size", miter
.get_stripe_size(), &f
);
2785 rgw_flush_formatter(s
, &f
);
2789 rgw_flush_formatter(s
, &f
);
2792 int RGWConfigBucketMetaSearch_ObjStore_S3::get_params()
2794 auto iter
= s
->info
.x_meta_map
.find("x-amz-meta-search");
2795 if (iter
== s
->info
.x_meta_map
.end()) {
2796 s
->err
.message
= "X-Rgw-Meta-Search header not provided";
2797 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2801 list
<string
> expressions
;
2802 get_str_list(iter
->second
, ",", expressions
);
2804 for (auto& expression
: expressions
) {
2805 vector
<string
> args
;
2806 get_str_vec(expression
, ";", args
);
2809 s
->err
.message
= "invalid empty expression";
2810 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2813 if (args
.size() > 2) {
2814 s
->err
.message
= string("invalid expression: ") + expression
;
2815 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2819 string key
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[0]));
2821 if (args
.size() > 1) {
2822 val
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[1]));
2825 if (!boost::algorithm::starts_with(key
, RGW_AMZ_META_PREFIX
)) {
2826 s
->err
.message
= string("invalid expression, key must start with '" RGW_AMZ_META_PREFIX
"' : ") + expression
;
2827 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2831 key
= key
.substr(sizeof(RGW_AMZ_META_PREFIX
) - 1);
2833 ESEntityTypeMap::EntityType entity_type
;
2835 if (val
.empty() || val
== "str" || val
== "string") {
2836 entity_type
= ESEntityTypeMap::ES_ENTITY_STR
;
2837 } else if (val
== "int" || val
== "integer") {
2838 entity_type
= ESEntityTypeMap::ES_ENTITY_INT
;
2839 } else if (val
== "date" || val
== "datetime") {
2840 entity_type
= ESEntityTypeMap::ES_ENTITY_DATE
;
2842 s
->err
.message
= string("invalid entity type: ") + val
;
2843 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2847 mdsearch_config
[key
] = entity_type
;
2853 void RGWConfigBucketMetaSearch_ObjStore_S3::send_response()
2856 set_req_state_err(s
, op_ret
);
2858 end_header(s
, this);
2861 void RGWGetBucketMetaSearch_ObjStore_S3::send_response()
2864 set_req_state_err(s
, op_ret
);
2866 end_header(s
, NULL
, "application/xml");
2868 Formatter
*f
= s
->formatter
;
2869 f
->open_array_section("GetBucketMetaSearchResult");
2870 for (auto& e
: s
->bucket_info
.mdsearch_config
) {
2871 f
->open_object_section("Entry");
2872 string k
= string("x-amz-meta-") + e
.first
;
2873 f
->dump_string("Key", k
.c_str());
2876 case ESEntityTypeMap::ES_ENTITY_INT
:
2879 case ESEntityTypeMap::ES_ENTITY_DATE
:
2885 f
->dump_string("Type", type
);
2889 rgw_flush_formatter(s
, f
);
2892 void RGWDelBucketMetaSearch_ObjStore_S3::send_response()
2895 set_req_state_err(s
, op_ret
);
2897 end_header(s
, this);
2901 RGWOp
*RGWHandler_REST_Service_S3::op_get()
2903 if (is_usage_op()) {
2904 return new RGWGetUsage_ObjStore_S3
;
2906 return new RGWListBuckets_ObjStore_S3
;
2910 RGWOp
*RGWHandler_REST_Service_S3::op_head()
2912 return new RGWListBuckets_ObjStore_S3
;
2915 RGWOp
*RGWHandler_REST_Service_S3::op_post()
2917 if (s
->info
.args
.exists("Action")) {
2918 string action
= s
->info
.args
.get("Action");
2919 if (action
.compare("CreateRole") == 0)
2920 return new RGWCreateRole
;
2921 if (action
.compare("DeleteRole") == 0)
2922 return new RGWDeleteRole
;
2923 if (action
.compare("GetRole") == 0)
2924 return new RGWGetRole
;
2925 if (action
.compare("UpdateAssumeRolePolicy") == 0)
2926 return new RGWModifyRole
;
2927 if (action
.compare("ListRoles") == 0)
2928 return new RGWListRoles
;
2929 if (action
.compare("PutRolePolicy") == 0)
2930 return new RGWPutRolePolicy
;
2931 if (action
.compare("GetRolePolicy") == 0)
2932 return new RGWGetRolePolicy
;
2933 if (action
.compare("ListRolePolicies") == 0)
2934 return new RGWListRolePolicies
;
2935 if (action
.compare("DeleteRolePolicy") == 0)
2936 return new RGWDeleteRolePolicy
;
2941 RGWOp
*RGWHandler_REST_Bucket_S3::get_obj_op(bool get_data
)
2945 return new RGWListBucket_ObjStore_S3
;
2947 return new RGWStatBucket_ObjStore_S3
;
2951 RGWOp
*RGWHandler_REST_Bucket_S3::op_get()
2953 if (s
->info
.args
.sub_resource_exists("logging"))
2954 return new RGWGetBucketLogging_ObjStore_S3
;
2956 if (s
->info
.args
.sub_resource_exists("location"))
2957 return new RGWGetBucketLocation_ObjStore_S3
;
2959 if (s
->info
.args
.sub_resource_exists("versioning"))
2960 return new RGWGetBucketVersioning_ObjStore_S3
;
2962 if (s
->info
.args
.sub_resource_exists("website")) {
2963 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
2966 return new RGWGetBucketWebsite_ObjStore_S3
;
2969 if (s
->info
.args
.exists("mdsearch")) {
2970 return new RGWGetBucketMetaSearch_ObjStore_S3
;
2974 return new RGWGetACLs_ObjStore_S3
;
2975 } else if (is_cors_op()) {
2976 return new RGWGetCORS_ObjStore_S3
;
2977 } else if (is_request_payment_op()) {
2978 return new RGWGetRequestPayment_ObjStore_S3
;
2979 } else if (s
->info
.args
.exists("uploads")) {
2980 return new RGWListBucketMultiparts_ObjStore_S3
;
2981 } else if(is_lc_op()) {
2982 return new RGWGetLC_ObjStore_S3
;
2983 } else if(is_policy_op()) {
2984 return new RGWGetBucketPolicy
;
2986 return get_obj_op(true);
2989 RGWOp
*RGWHandler_REST_Bucket_S3::op_head()
2992 return new RGWGetACLs_ObjStore_S3
;
2993 } else if (s
->info
.args
.exists("uploads")) {
2994 return new RGWListBucketMultiparts_ObjStore_S3
;
2996 return get_obj_op(false);
2999 RGWOp
*RGWHandler_REST_Bucket_S3::op_put()
3001 if (s
->info
.args
.sub_resource_exists("logging"))
3003 if (s
->info
.args
.sub_resource_exists("versioning"))
3004 return new RGWSetBucketVersioning_ObjStore_S3
;
3005 if (s
->info
.args
.sub_resource_exists("website")) {
3006 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
3009 return new RGWSetBucketWebsite_ObjStore_S3
;
3012 return new RGWPutACLs_ObjStore_S3
;
3013 } else if (is_cors_op()) {
3014 return new RGWPutCORS_ObjStore_S3
;
3015 } else if (is_request_payment_op()) {
3016 return new RGWSetRequestPayment_ObjStore_S3
;
3017 } else if(is_lc_op()) {
3018 return new RGWPutLC_ObjStore_S3
;
3019 } else if(is_policy_op()) {
3020 return new RGWPutBucketPolicy
;
3022 return new RGWCreateBucket_ObjStore_S3
;
3025 RGWOp
*RGWHandler_REST_Bucket_S3::op_delete()
3028 return new RGWDeleteCORS_ObjStore_S3
;
3029 } else if(is_lc_op()) {
3030 return new RGWDeleteLC_ObjStore_S3
;
3031 } else if(is_policy_op()) {
3032 return new RGWDeleteBucketPolicy
;
3035 if (s
->info
.args
.sub_resource_exists("website")) {
3036 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
3039 return new RGWDeleteBucketWebsite_ObjStore_S3
;
3042 if (s
->info
.args
.exists("mdsearch")) {
3043 return new RGWDelBucketMetaSearch_ObjStore_S3
;
3046 return new RGWDeleteBucket_ObjStore_S3
;
3049 RGWOp
*RGWHandler_REST_Bucket_S3::op_post()
3051 if (s
->info
.args
.exists("delete")) {
3052 return new RGWDeleteMultiObj_ObjStore_S3
;
3055 if (s
->info
.args
.exists("mdsearch")) {
3056 return new RGWConfigBucketMetaSearch_ObjStore_S3
;
3059 return new RGWPostObj_ObjStore_S3
;
3062 RGWOp
*RGWHandler_REST_Bucket_S3::op_options()
3064 return new RGWOptionsCORS_ObjStore_S3
;
3067 RGWOp
*RGWHandler_REST_Obj_S3::get_obj_op(bool get_data
)
3070 return new RGWGetACLs_ObjStore_S3
;
3072 RGWGetObj_ObjStore_S3
*get_obj_op
= new RGWGetObj_ObjStore_S3
;
3073 get_obj_op
->set_get_data(get_data
);
3077 RGWOp
*RGWHandler_REST_Obj_S3::op_get()
3080 return new RGWGetACLs_ObjStore_S3
;
3081 } else if (s
->info
.args
.exists("uploadId")) {
3082 return new RGWListMultipart_ObjStore_S3
;
3083 } else if (s
->info
.args
.exists("layout")) {
3084 return new RGWGetObjLayout_ObjStore_S3
;
3085 } else if (is_tagging_op()) {
3086 return new RGWGetObjTags_ObjStore_S3
;
3088 return get_obj_op(true);
3091 RGWOp
*RGWHandler_REST_Obj_S3::op_head()
3094 return new RGWGetACLs_ObjStore_S3
;
3095 } else if (s
->info
.args
.exists("uploadId")) {
3096 return new RGWListMultipart_ObjStore_S3
;
3098 return get_obj_op(false);
3101 RGWOp
*RGWHandler_REST_Obj_S3::op_put()
3104 return new RGWPutACLs_ObjStore_S3
;
3105 } else if (is_tagging_op()) {
3106 return new RGWPutObjTags_ObjStore_S3
;
3109 if (s
->init_state
.src_bucket
.empty())
3110 return new RGWPutObj_ObjStore_S3
;
3112 return new RGWCopyObj_ObjStore_S3
;
3115 RGWOp
*RGWHandler_REST_Obj_S3::op_delete()
3117 if (is_tagging_op()) {
3118 return new RGWDeleteObjTags_ObjStore_S3
;
3120 string upload_id
= s
->info
.args
.get("uploadId");
3122 if (upload_id
.empty())
3123 return new RGWDeleteObj_ObjStore_S3
;
3125 return new RGWAbortMultipart_ObjStore_S3
;
3128 RGWOp
*RGWHandler_REST_Obj_S3::op_post()
3130 if (s
->info
.args
.exists("uploadId"))
3131 return new RGWCompleteMultipart_ObjStore_S3
;
3133 if (s
->info
.args
.exists("uploads"))
3134 return new RGWInitMultipart_ObjStore_S3
;
3136 return new RGWPostObj_ObjStore_S3
;
3139 RGWOp
*RGWHandler_REST_Obj_S3::op_options()
3141 return new RGWOptionsCORS_ObjStore_S3
;
3144 int RGWHandler_REST_S3::init_from_header(struct req_state
* s
,
3145 int default_formatter
,
3146 bool configurable_format
)
3151 const char *req_name
= s
->relative_uri
.c_str();
3154 if (*req_name
== '?') {
3157 p
= s
->info
.request_params
.c_str();
3160 s
->info
.args
.set(p
);
3161 s
->info
.args
.parse();
3163 /* must be called after the args parsing */
3164 int ret
= allocate_formatter(s
, default_formatter
, configurable_format
);
3168 if (*req_name
!= '/')
3177 int pos
= req
.find('/');
3179 first
= req
.substr(0, pos
);
3185 * XXX The intent of the check for empty is apparently to let the bucket
3186 * name from DNS to be set ahead. However, we currently take the DNS
3187 * bucket and re-insert it into URL in rgw_rest.cc:RGWREST::preprocess().
3188 * So, this check is meaningless.
3190 * Rather than dropping this, the code needs to be changed into putting
3191 * the bucket (and its tenant) from DNS and Host: header (HTTP_HOST)
3192 * into req_status.bucket_name directly.
3194 if (s
->init_state
.url_bucket
.empty()) {
3195 // Save bucket to tide us over until token is parsed.
3196 s
->init_state
.url_bucket
= first
;
3198 string encoded_obj_str
= req
.substr(pos
+1);
3199 s
->object
= rgw_obj_key(encoded_obj_str
, s
->info
.args
.get("versionId"));
3202 s
->object
= rgw_obj_key(req_name
, s
->info
.args
.get("versionId"));
3207 int RGWHandler_REST_S3::postauth_init()
3209 struct req_init_state
*t
= &s
->init_state
;
3210 bool relaxed_names
= s
->cct
->_conf
->rgw_relaxed_s3_bucket_names
;
3212 rgw_parse_url_bucket(t
->url_bucket
, s
->user
->user_id
.tenant
,
3213 s
->bucket_tenant
, s
->bucket_name
);
3215 dout(10) << "s->object=" << (!s
->object
.empty() ? s
->object
: rgw_obj_key("<NULL>"))
3216 << " s->bucket=" << rgw_make_bucket_entry_name(s
->bucket_tenant
, s
->bucket_name
) << dendl
;
3219 ret
= rgw_validate_tenant_name(s
->bucket_tenant
);
3222 if (!s
->bucket_name
.empty()) {
3223 ret
= valid_s3_bucket_name(s
->bucket_name
, relaxed_names
);
3226 ret
= validate_object_name(s
->object
.name
);
3231 if (!t
->src_bucket
.empty()) {
3232 rgw_parse_url_bucket(t
->src_bucket
, s
->user
->user_id
.tenant
,
3233 s
->src_tenant_name
, s
->src_bucket_name
);
3234 ret
= rgw_validate_tenant_name(s
->src_tenant_name
);
3237 ret
= valid_s3_bucket_name(s
->src_bucket_name
, relaxed_names
);
3244 int RGWHandler_REST_S3::init(RGWRados
*store
, struct req_state
*s
,
3245 rgw::io::BasicClient
*cio
)
3251 ret
= rgw_validate_tenant_name(s
->bucket_tenant
);
3254 bool relaxed_names
= s
->cct
->_conf
->rgw_relaxed_s3_bucket_names
;
3255 if (!s
->bucket_name
.empty()) {
3256 ret
= valid_s3_bucket_name(s
->bucket_name
, relaxed_names
);
3259 ret
= validate_object_name(s
->object
.name
);
3264 const char *cacl
= s
->info
.env
->get("HTTP_X_AMZ_ACL");
3266 s
->canned_acl
= cacl
;
3268 s
->has_acl_header
= s
->info
.env
->exists_prefix("HTTP_X_AMZ_GRANT");
3270 const char *copy_source
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE");
3272 if (copy_source
&& !s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE")) {
3273 ret
= RGWCopyObj::parse_copy_location(copy_source
,
3274 s
->init_state
.src_bucket
,
3277 ldout(s
->cct
, 0) << "failed to parse copy location" << dendl
;
3278 return -EINVAL
; // XXX why not -ERR_INVALID_BUCKET_NAME or -ERR_BAD_URL?
3282 return RGWHandler_REST::init(store
, s
, cio
);
3285 enum class AwsVersion
{
3291 enum class AwsRoute
{
3297 static inline std::pair
<AwsVersion
, AwsRoute
>
3298 discover_aws_flavour(const req_info
& info
)
3300 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
3302 AwsVersion version
= AwsVersion::UNKOWN
;
3303 AwsRoute route
= AwsRoute::UNKOWN
;
3305 const char* http_auth
= info
.env
->get("HTTP_AUTHORIZATION");
3306 if (http_auth
&& http_auth
[0]) {
3307 /* Authorization in Header */
3308 route
= AwsRoute::HEADERS
;
3310 if (!strncmp(http_auth
, AWS4_HMAC_SHA256_STR
,
3311 strlen(AWS4_HMAC_SHA256_STR
))) {
3313 version
= AwsVersion::V4
;
3314 } else if (!strncmp(http_auth
, "AWS ", 4)) {
3316 version
= AwsVersion::V2
;
3319 route
= AwsRoute::QUERY_STRING
;
3321 if (info
.args
.get("X-Amz-Algorithm") == AWS4_HMAC_SHA256_STR
) {
3323 version
= AwsVersion::V4
;
3324 } else if (!info
.args
.get("AWSAccessKeyId").empty()) {
3326 version
= AwsVersion::V2
;
3330 return std::make_pair(version
, route
);
3333 static void init_anon_user(struct req_state
*s
)
3335 rgw_get_anon_user(*(s
->user
));
3336 s
->perm_mask
= RGW_PERM_FULL_CONTROL
;
3340 * verify that a signed request comes from the keyholder
3341 * by checking the signature against our locally-computed version
3343 * it tries AWS v4 before AWS v2
3345 int RGW_Auth_S3::authorize(RGWRados
* const store
,
3346 const rgw::auth::StrategyRegistry
& auth_registry
,
3347 struct req_state
* const s
)
3350 /* neither keystone and rados enabled; warn and exit! */
3351 if (!store
->ctx()->_conf
->rgw_s3_auth_use_rados
&&
3352 !store
->ctx()->_conf
->rgw_s3_auth_use_keystone
&&
3353 !store
->ctx()->_conf
->rgw_s3_auth_use_ldap
) {
3354 dout(0) << "WARNING: no authorization backend enabled! Users will never authenticate." << dendl
;
3358 const auto ret
= rgw::auth::Strategy::apply(auth_registry
.get_s3_main(), s
);
3360 /* Populate the owner info. */
3361 s
->owner
.set_id(s
->user
->user_id
);
3362 s
->owner
.set_name(s
->user
->display_name
);
3367 int RGWHandler_Auth_S3::init(RGWRados
*store
, struct req_state
*state
,
3368 rgw::io::BasicClient
*cio
)
3370 int ret
= RGWHandler_REST_S3::init_from_header(state
, RGW_FORMAT_JSON
,
3375 return RGWHandler_REST::init(store
, state
, cio
);
3378 RGWHandler_REST
* RGWRESTMgr_S3::get_handler(struct req_state
* const s
,
3379 const rgw::auth::StrategyRegistry
& auth_registry
,
3380 const std::string
& frontend_prefix
)
3382 bool is_s3website
= enable_s3website
&& (s
->prot_flags
& RGW_REST_WEBSITE
);
3384 RGWHandler_REST_S3::init_from_header(s
,
3385 is_s3website
? RGW_FORMAT_HTML
:
3386 RGW_FORMAT_XML
, true);
3390 RGWHandler_REST
* handler
;
3391 // TODO: Make this more readable
3393 if (s
->init_state
.url_bucket
.empty()) {
3394 handler
= new RGWHandler_REST_Service_S3Website(auth_registry
);
3395 } else if (s
->object
.empty()) {
3396 handler
= new RGWHandler_REST_Bucket_S3Website(auth_registry
);
3398 handler
= new RGWHandler_REST_Obj_S3Website(auth_registry
);
3401 if (s
->init_state
.url_bucket
.empty()) {
3402 handler
= new RGWHandler_REST_Service_S3(auth_registry
);
3403 } else if (s
->object
.empty()) {
3404 handler
= new RGWHandler_REST_Bucket_S3(auth_registry
);
3406 handler
= new RGWHandler_REST_Obj_S3(auth_registry
);
3410 ldout(s
->cct
, 20) << __func__
<< " handler=" << typeid(*handler
).name()
3415 bool RGWHandler_REST_S3Website::web_dir() const {
3416 std::string subdir_name
= url_decode(s
->object
.name
);
3418 if (subdir_name
.empty()) {
3420 } else if (subdir_name
.back() == '/') {
3421 subdir_name
.pop_back();
3424 rgw_obj
obj(s
->bucket
, subdir_name
);
3426 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
3427 obj_ctx
.obj
.set_atomic(obj
);
3428 obj_ctx
.obj
.set_prefetch_data(obj
);
3430 RGWObjState
* state
= nullptr;
3431 if (store
->get_obj_state(&obj_ctx
, s
->bucket_info
, obj
, &state
, false) < 0) {
3434 if (! state
->exists
) {
3437 return state
->exists
;
3440 int RGWHandler_REST_S3Website::retarget(RGWOp
* op
, RGWOp
** new_op
) {
3442 ldout(s
->cct
, 10) << __func__
<< "Starting retarget" << dendl
;
3444 if (!(s
->prot_flags
& RGW_REST_WEBSITE
))
3447 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
3448 int ret
= store
->get_bucket_info(obj_ctx
, s
->bucket_tenant
,
3449 s
->bucket_name
, s
->bucket_info
, NULL
,
3452 // TODO-FUTURE: if the bucket does not exist, maybe expose it here?
3453 return -ERR_NO_SUCH_BUCKET
;
3455 if (!s
->bucket_info
.has_website
) {
3456 // TODO-FUTURE: if the bucket has no WebsiteConfig, expose it here
3457 return -ERR_NO_SUCH_WEBSITE_CONFIGURATION
;
3460 rgw_obj_key new_obj
;
3461 s
->bucket_info
.website_conf
.get_effective_key(s
->object
.name
, &new_obj
.name
, web_dir());
3462 ldout(s
->cct
, 10) << "retarget get_effective_key " << s
->object
<< " -> "
3463 << new_obj
<< dendl
;
3465 RGWBWRoutingRule rrule
;
3466 bool should_redirect
=
3467 s
->bucket_info
.website_conf
.should_redirect(new_obj
.name
, 0, &rrule
);
3469 if (should_redirect
) {
3470 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
3471 const string
& protocol
=
3472 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
3473 int redirect_code
= 0;
3474 rrule
.apply_rule(protocol
, hostname
, s
->object
.name
, &s
->redirect
,
3476 // APply a custom HTTP response code
3477 if (redirect_code
> 0)
3478 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
3479 ldout(s
->cct
, 10) << "retarget redirect code=" << redirect_code
3480 << " proto+host:" << protocol
<< "://" << hostname
3481 << " -> " << s
->redirect
<< dendl
;
3482 return -ERR_WEBSITE_REDIRECT
;
3486 * FIXME: if s->object != new_obj, drop op and create a new op to handle
3487 * operation. Or remove this comment if it's not applicable anymore
3490 s
->object
= new_obj
;
3495 RGWOp
* RGWHandler_REST_S3Website::op_get()
3497 return get_obj_op(true);
3500 RGWOp
* RGWHandler_REST_S3Website::op_head()
3502 return get_obj_op(false);
3505 int RGWHandler_REST_S3Website::serve_errordoc(int http_ret
, const string
& errordoc_key
) {
3507 s
->formatter
->reset(); /* Try to throw it all away */
3509 std::shared_ptr
<RGWGetObj_ObjStore_S3Website
> getop( static_cast<RGWGetObj_ObjStore_S3Website
*>(op_get()));
3510 if (getop
.get() == NULL
) {
3511 return -1; // Trigger double error handler
3513 getop
->init(store
, s
, this);
3514 getop
->range_str
= NULL
;
3515 getop
->if_mod
= NULL
;
3516 getop
->if_unmod
= NULL
;
3517 getop
->if_match
= NULL
;
3518 getop
->if_nomatch
= NULL
;
3519 s
->object
= errordoc_key
;
3521 ret
= init_permissions(getop
.get());
3523 ldout(s
->cct
, 20) << "serve_errordoc failed, init_permissions ret=" << ret
<< dendl
;
3524 return -1; // Trigger double error handler
3527 ret
= read_permissions(getop
.get());
3529 ldout(s
->cct
, 20) << "serve_errordoc failed, read_permissions ret=" << ret
<< dendl
;
3530 return -1; // Trigger double error handler
3534 getop
->set_custom_http_response(http_ret
);
3537 ret
= getop
->init_processing();
3539 ldout(s
->cct
, 20) << "serve_errordoc failed, init_processing ret=" << ret
<< dendl
;
3540 return -1; // Trigger double error handler
3543 ret
= getop
->verify_op_mask();
3545 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_op_mask ret=" << ret
<< dendl
;
3546 return -1; // Trigger double error handler
3549 ret
= getop
->verify_permission();
3551 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_permission ret=" << ret
<< dendl
;
3552 return -1; // Trigger double error handler
3555 ret
= getop
->verify_params();
3557 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_params ret=" << ret
<< dendl
;
3558 return -1; // Trigger double error handler
3561 // No going back now
3564 * FIXME Missing headers:
3565 * With a working errordoc, the s3 error fields are rendered as HTTP headers,
3566 * x-amz-error-code: NoSuchKey
3567 * x-amz-error-message: The specified key does not exist.
3568 * x-amz-error-detail-Key: foo
3576 int RGWHandler_REST_S3Website::error_handler(int err_no
,
3577 string
* error_content
) {
3578 int new_err_no
= -1;
3579 rgw_http_errors::const_iterator r
= rgw_http_s3_errors
.find(err_no
> 0 ? err_no
: -err_no
);
3580 int http_error_code
= -1;
3582 if (r
!= rgw_http_s3_errors
.end()) {
3583 http_error_code
= r
->second
.first
;
3585 ldout(s
->cct
, 10) << "RGWHandler_REST_S3Website::error_handler err_no=" << err_no
<< " http_ret=" << http_error_code
<< dendl
;
3587 RGWBWRoutingRule rrule
;
3588 bool should_redirect
=
3589 s
->bucket_info
.website_conf
.should_redirect(s
->object
.name
, http_error_code
,
3592 if (should_redirect
) {
3593 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
3594 const string
& protocol
=
3595 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
3596 int redirect_code
= 0;
3597 rrule
.apply_rule(protocol
, hostname
, s
->object
.name
, &s
->redirect
,
3599 // Apply a custom HTTP response code
3600 if (redirect_code
> 0)
3601 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
3602 ldout(s
->cct
, 10) << "error handler redirect code=" << redirect_code
3603 << " proto+host:" << protocol
<< "://" << hostname
3604 << " -> " << s
->redirect
<< dendl
;
3605 return -ERR_WEBSITE_REDIRECT
;
3606 } else if (err_no
== -ERR_WEBSITE_REDIRECT
) {
3607 // Do nothing here, this redirect will be handled in abort_early's ERR_WEBSITE_REDIRECT block
3608 // Do NOT fire the ErrorDoc handler
3609 } else if (!s
->bucket_info
.website_conf
.error_doc
.empty()) {
3610 /* This serves an entire page!
3611 On success, it will return zero, and no further content should be sent to the socket
3612 On failure, we need the double-error handler
3614 new_err_no
= RGWHandler_REST_S3Website::serve_errordoc(http_error_code
, s
->bucket_info
.website_conf
.error_doc
);
3615 if (new_err_no
&& new_err_no
!= -1) {
3616 err_no
= new_err_no
;
3619 ldout(s
->cct
, 20) << "No special error handling today!" << dendl
;
3625 RGWOp
* RGWHandler_REST_Obj_S3Website::get_obj_op(bool get_data
)
3627 /** If we are in website mode, then it is explicitly impossible to run GET or
3628 * HEAD on the actual directory. We must convert the request to run on the
3629 * suffix object instead!
3631 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3632 op
->set_get_data(get_data
);
3636 RGWOp
* RGWHandler_REST_Bucket_S3Website::get_obj_op(bool get_data
)
3638 /** If we are in website mode, then it is explicitly impossible to run GET or
3639 * HEAD on the actual directory. We must convert the request to run on the
3640 * suffix object instead!
3642 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3643 op
->set_get_data(get_data
);
3647 RGWOp
* RGWHandler_REST_Service_S3Website::get_obj_op(bool get_data
)
3649 /** If we are in website mode, then it is explicitly impossible to run GET or
3650 * HEAD on the actual directory. We must convert the request to run on the
3651 * suffix object instead!
3653 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3654 op
->set_get_data(get_data
);
3663 bool AWSGeneralAbstractor::is_time_skew_ok(const utime_t
& header_time
,
3664 const bool qsr
) const
3666 /* Check for time skew first. */
3667 const time_t req_sec
= header_time
.sec();
3671 if ((req_sec
< now
- RGW_AUTH_GRACE_MINS
* 60 ||
3672 req_sec
> now
+ RGW_AUTH_GRACE_MINS
* 60) && !qsr
) {
3673 ldout(cct
, 10) << "req_sec=" << req_sec
<< " now=" << now
3674 << "; now - RGW_AUTH_GRACE_MINS="
3675 << now
- RGW_AUTH_GRACE_MINS
* 60
3676 << "; now + RGW_AUTH_GRACE_MINS="
3677 << now
+ RGW_AUTH_GRACE_MINS
* 60
3680 ldout(cct
, 0) << "NOTICE: request time skew too big now="
3682 << " req_time=" << header_time
3691 static rgw::auth::Completer::cmplptr_t
3692 null_completer_factory(const boost::optional
<std::string
>& secret_key
)
3698 AWSEngine::VersionAbstractor::auth_data_t
3699 AWSGeneralAbstractor::get_auth_data(const req_state
* const s
) const
3703 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
3705 if (version
== AwsVersion::V2
) {
3706 return get_auth_data_v2(s
);
3707 } else if (version
== AwsVersion::V4
) {
3708 return get_auth_data_v4(s
, route
== AwsRoute::QUERY_STRING
);
3710 /* FIXME(rzarzynski): handle anon user. */
3715 boost::optional
<std::string
>
3716 AWSGeneralAbstractor::get_v4_canonical_headers(
3717 const req_info
& info
,
3718 const boost::string_view
& signedheaders
,
3719 const bool using_qs
) const
3721 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
3725 AWSEngine::VersionAbstractor::auth_data_t
3726 AWSGeneralAbstractor::get_auth_data_v4(const req_state
* const s
,
3728 bool using_qs
) const
3730 boost::string_view access_key_id
;
3731 boost::string_view signed_hdrs
;
3733 boost::string_view date
;
3734 boost::string_view credential_scope
;
3735 boost::string_view client_signature
;
3737 int ret
= rgw::auth::s3::parse_credentials(s
->info
,
3748 /* craft canonical headers */
3749 boost::optional
<std::string
> canonical_headers
= \
3750 get_v4_canonical_headers(s
->info
, signed_hdrs
, using_qs
);
3751 if (canonical_headers
) {
3752 ldout(s
->cct
, 10) << "canonical headers format = " << *canonical_headers
3758 /* Get the expected hash. */
3759 auto exp_payload_hash
= rgw::auth::s3::get_v4_exp_payload_hash(s
->info
);
3761 /* Craft canonical URI. Using std::move later so let it be non-const. */
3762 auto canonical_uri
= rgw::auth::s3::get_v4_canonical_uri(s
->info
);
3764 /* Craft canonical query string. std::moving later so non-const here. */
3765 auto canonical_qs
= rgw::auth::s3::get_v4_canonical_qs(s
->info
, using_qs
);
3767 /* Craft canonical request. */
3768 auto canonical_req_hash
= \
3769 rgw::auth::s3::get_v4_canon_req_hash(s
->cct
,
3771 std::move(canonical_uri
),
3772 std::move(canonical_qs
),
3773 std::move(*canonical_headers
),
3777 auto string_to_sign
= \
3778 rgw::auth::s3::get_v4_string_to_sign(s
->cct
,
3779 AWS4_HMAC_SHA256_STR
,
3782 std::move(canonical_req_hash
));
3784 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
3786 std::placeholders::_1
,
3787 std::placeholders::_2
,
3788 std::placeholders::_3
);
3790 /* Requests authenticated with the Query Parameters are treated as unsigned.
3791 * From "Authenticating Requests: Using Query Parameters (AWS Signature
3794 * You don't include a payload hash in the Canonical Request, because
3795 * when you create a presigned URL, you don't know the payload content
3796 * because the URL is used to upload an arbitrary payload. Instead, you
3797 * use a constant string UNSIGNED-PAYLOAD.
3799 * This means we have absolutely no business in spawning completer. Both
3800 * aws4_auth_needs_complete and aws4_auth_streaming_mode are set to false
3801 * by default. We don't need to change that. */
3802 if (is_v4_payload_unsigned(exp_payload_hash
) || is_v4_payload_empty(s
)) {
3806 std::move(string_to_sign
),
3808 null_completer_factory
3811 /* We're going to handle a signed payload. Be aware that even empty HTTP
3812 * body (no payload) requires verification:
3814 * The x-amz-content-sha256 header is required for all AWS Signature
3815 * Version 4 requests. It provides a hash of the request payload. If
3816 * there is no payload, you must provide the hash of an empty string. */
3817 if (!is_v4_payload_streamed(exp_payload_hash
)) {
3818 ldout(s
->cct
, 10) << "delaying v4 auth" << dendl
;
3820 /* payload in a single chunk */
3823 case RGW_OP_CREATE_BUCKET
:
3824 case RGW_OP_PUT_OBJ
:
3825 case RGW_OP_PUT_ACLS
:
3826 case RGW_OP_PUT_CORS
:
3827 case RGW_OP_COMPLETE_MULTIPART
:
3828 case RGW_OP_SET_BUCKET_VERSIONING
:
3829 case RGW_OP_DELETE_MULTI_OBJ
:
3830 case RGW_OP_ADMIN_SET_METADATA
:
3831 case RGW_OP_SET_BUCKET_WEBSITE
:
3832 case RGW_OP_PUT_BUCKET_POLICY
:
3833 case RGW_OP_PUT_OBJ_TAGGING
:
3837 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl
;
3838 throw -ERR_NOT_IMPLEMENTED
;
3841 const auto cmpl_factory
= std::bind(AWSv4ComplSingle::create
,
3843 std::placeholders::_1
);
3847 std::move(string_to_sign
),
3852 /* IMHO "streamed" doesn't fit too good here. I would prefer to call
3853 * it "chunked" but let's be coherent with Amazon's terminology. */
3855 dout(10) << "body content detected in multiple chunks" << dendl
;
3857 /* payload in multiple chunks */
3861 case RGW_OP_PUT_OBJ
:
3864 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED (streaming mode)" << dendl
;
3865 throw -ERR_NOT_IMPLEMENTED
;
3868 dout(10) << "aws4 seed signature ok... delaying v4 auth" << dendl
;
3870 /* In the case of streamed payload client sets the x-amz-content-sha256
3871 * to "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" but uses "UNSIGNED-PAYLOAD"
3872 * when constructing the Canonical Request. */
3874 /* In the case of single-chunk upload client set the header's value is
3875 * coherent with the one used for Canonical Request crafting. */
3877 /* In the case of query string-based authentication there should be no
3878 * x-amz-content-sha256 header and the value "UNSIGNED-PAYLOAD" is used
3880 const auto cmpl_factory
= std::bind(AWSv4ComplMulti::create
,
3885 std::placeholders::_1
);
3889 std::move(string_to_sign
),
3898 boost::optional
<std::string
>
3899 AWSGeneralBoto2Abstractor::get_v4_canonical_headers(
3900 const req_info
& info
,
3901 const boost::string_view
& signedheaders
,
3902 const bool using_qs
) const
3904 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
3909 AWSEngine::VersionAbstractor::auth_data_t
3910 AWSGeneralAbstractor::get_auth_data_v2(const req_state
* const s
) const
3912 boost::string_view access_key_id
;
3913 boost::string_view signature
;
3916 const char* http_auth
= s
->info
.env
->get("HTTP_AUTHORIZATION");
3917 if (! http_auth
|| http_auth
[0] == '\0') {
3918 /* Credentials are provided in query string. We also need to verify
3919 * the "Expires" parameter now. */
3920 access_key_id
= s
->info
.args
.get("AWSAccessKeyId");
3921 signature
= s
->info
.args
.get("Signature");
3924 boost::string_view expires
= s
->info
.args
.get("Expires");
3925 if (! expires
.empty()) {
3926 /* It looks we have the guarantee that expires is a null-terminated,
3927 * and thus string_view::data() can be safely used. */
3928 const time_t exp
= atoll(expires
.data());
3937 /* The "Authorization" HTTP header is being used. */
3938 const boost::string_view
auth_str(http_auth
+ strlen("AWS "));
3939 const size_t pos
= auth_str
.rfind(':');
3940 if (pos
!= boost::string_view::npos
) {
3941 access_key_id
= auth_str
.substr(0, pos
);
3942 signature
= auth_str
.substr(pos
+ 1);
3946 /* Let's canonize the HTTP headers that are covered by the AWS auth v2. */
3947 std::string string_to_sign
;
3948 utime_t header_time
;
3949 if (! rgw_create_s3_canonical_header(s
->info
, &header_time
, string_to_sign
,
3951 ldout(cct
, 10) << "failed to create the canonized auth header\n"
3952 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
3956 ldout(cct
, 10) << "string_to_sign:\n"
3957 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
3959 if (! is_time_skew_ok(header_time
, qsr
)) {
3960 throw -ERR_REQUEST_TIME_SKEWED
;
3964 std::move(access_key_id
),
3965 std::move(signature
),
3966 std::move(string_to_sign
),
3967 rgw::auth::s3::get_v2_signature
,
3968 null_completer_factory
3973 AWSEngine::VersionAbstractor::auth_data_t
3974 AWSBrowserUploadAbstractor::get_auth_data_v2(const req_state
* const s
) const
3977 s
->auth
.s3_postobj_creds
.access_key
,
3978 s
->auth
.s3_postobj_creds
.signature
,
3979 s
->auth
.s3_postobj_creds
.encoded_policy
.to_str(),
3980 rgw::auth::s3::get_v2_signature
,
3981 null_completer_factory
3985 AWSEngine::VersionAbstractor::auth_data_t
3986 AWSBrowserUploadAbstractor::get_auth_data_v4(const req_state
* const s
) const
3988 const boost::string_view credential
= s
->auth
.s3_postobj_creds
.x_amz_credential
;
3990 /* grab access key id */
3991 const size_t pos
= credential
.find("/");
3992 const boost::string_view access_key_id
= credential
.substr(0, pos
);
3993 dout(10) << "access key id = " << access_key_id
<< dendl
;
3995 /* grab credential scope */
3996 const boost::string_view credential_scope
= credential
.substr(pos
+ 1);
3997 dout(10) << "credential scope = " << credential_scope
<< dendl
;
3999 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
4001 std::placeholders::_1
,
4002 std::placeholders::_2
,
4003 std::placeholders::_3
);
4007 s
->auth
.s3_postobj_creds
.signature
,
4008 s
->auth
.s3_postobj_creds
.encoded_policy
.to_str(),
4010 null_completer_factory
4014 AWSEngine::VersionAbstractor::auth_data_t
4015 AWSBrowserUploadAbstractor::get_auth_data(const req_state
* const s
) const
4017 if (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
) {
4018 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v4"
4019 << " (AWS4-HMAC-SHA256)" << dendl
;
4020 return get_auth_data_v4(s
);
4022 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v2" << dendl
;
4023 return get_auth_data_v2(s
);
4029 AWSEngine::authenticate(const req_state
* const s
) const
4031 /* Small reminder: an ver_abstractor is allowed to throw! */
4032 const auto auth_data
= ver_abstractor
.get_auth_data(s
);
4034 if (auth_data
.access_key_id
.empty() || auth_data
.client_signature
.empty()) {
4035 return result_t::deny(-EINVAL
);
4037 return authenticate(auth_data
.access_key_id
,
4038 auth_data
.client_signature
,
4039 auth_data
.string_to_sign
,
4040 auth_data
.signature_factory
,
4041 auth_data
.completer_factory
,
4046 } /* namespace s3 */
4047 } /* namespace auth */
4048 } /* namespace rgw */
4050 rgw::LDAPHelper
* rgw::auth::s3::LDAPEngine::ldh
= nullptr;
4051 std::mutex
rgw::auth::s3::LDAPEngine::mtx
;
4053 void rgw::auth::s3::LDAPEngine::init(CephContext
* const cct
)
4056 std::lock_guard
<std::mutex
> lck(mtx
);
4058 const string
& ldap_uri
= cct
->_conf
->rgw_ldap_uri
;
4059 const string
& ldap_binddn
= cct
->_conf
->rgw_ldap_binddn
;
4060 const string
& ldap_searchdn
= cct
->_conf
->rgw_ldap_searchdn
;
4061 const string
& ldap_searchfilter
= cct
->_conf
->rgw_ldap_searchfilter
;
4062 const string
& ldap_dnattr
= cct
->_conf
->rgw_ldap_dnattr
;
4063 std::string ldap_bindpw
= parse_rgw_ldap_bindpw(cct
);
4065 ldh
= new rgw::LDAPHelper(ldap_uri
, ldap_binddn
, ldap_bindpw
,
4066 ldap_searchdn
, ldap_searchfilter
, ldap_dnattr
);
4074 rgw::auth::RemoteApplier::acl_strategy_t
4075 rgw::auth::s3::LDAPEngine::get_acl_strategy() const
4077 //This is based on the assumption that the default acl strategy in
4078 // get_perms_from_aclspec, will take care. Extra acl spec is not required.
4082 rgw::auth::RemoteApplier::AuthInfo
4083 rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken
& token
) const noexcept
4085 /* The short form of "using" can't be used here -- we're aliasing a class'
4087 using acct_privilege_t
= \
4088 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t
;
4090 return rgw::auth::RemoteApplier::AuthInfo
{
4093 RGW_PERM_FULL_CONTROL
,
4094 acct_privilege_t::IS_PLAIN_ACCT
,
4099 rgw::auth::Engine::result_t
4100 rgw::auth::s3::LDAPEngine::authenticate(
4101 const boost::string_view
& access_key_id
,
4102 const boost::string_view
& signature
,
4103 const string_to_sign_t
& string_to_sign
,
4104 const signature_factory_t
&,
4105 const completer_factory_t
& completer_factory
,
4106 const req_state
* const s
) const
4108 /* boost filters and/or string_ref may throw on invalid input */
4109 rgw::RGWToken base64_token
;
4111 base64_token
= rgw::from_base64(access_key_id
);
4113 base64_token
= std::string("");
4116 if (! base64_token
.valid()) {
4117 return result_t::deny();
4120 //TODO: Uncomment, when we have a migration plan in place.
4121 //Check if a user of type other than 'ldap' is already present, if yes, then
4123 /*RGWUserInfo user_info;
4124 user_info.user_id = base64_token.id;
4125 if (rgw_get_user_info_by_uid(store, user_info.user_id, user_info) >= 0) {
4126 if (user_info.type != TYPE_LDAP) {
4127 ldout(cct, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl;
4132 if (ldh
->auth(base64_token
.id
, base64_token
.key
) != 0) {
4133 return result_t::deny();
4136 auto apl
= apl_factory
->create_apl_remote(cct
, s
, get_acl_strategy(),
4137 get_creds_info(base64_token
));
4138 return result_t::grant(std::move(apl
), completer_factory(boost::none
));
4143 rgw::auth::Engine::result_t
4144 rgw::auth::s3::LocalEngine::authenticate(
4145 const boost::string_view
& _access_key_id
,
4146 const boost::string_view
& signature
,
4147 const string_to_sign_t
& string_to_sign
,
4148 const signature_factory_t
& signature_factory
,
4149 const completer_factory_t
& completer_factory
,
4150 const req_state
* const s
) const
4152 /* get the user info */
4153 RGWUserInfo user_info
;
4154 /* TODO(rzarzynski): we need to have string-view taking variant. */
4155 const std::string access_key_id
= _access_key_id
.to_string();
4156 if (rgw_get_user_info_by_access_key(store
, access_key_id
, user_info
) < 0) {
4157 ldout(cct
, 5) << "error reading user info, uid=" << access_key_id
4158 << " can't authenticate" << dendl
;
4159 return result_t::deny(-ERR_INVALID_ACCESS_KEY
);
4161 //TODO: Uncomment, when we have a migration plan in place.
4163 if (s->user->type != TYPE_RGW) {
4164 ldout(cct, 10) << "ERROR: User id of type: " << s->user->type
4165 << " is present" << dendl;
4170 const auto iter
= user_info
.access_keys
.find(access_key_id
);
4171 if (iter
== std::end(user_info
.access_keys
)) {
4172 ldout(cct
, 0) << "ERROR: access key not encoded in user info" << dendl
;
4173 return result_t::deny(-EPERM
);
4175 const RGWAccessKey
& k
= iter
->second
;
4177 const VersionAbstractor::server_signature_t server_signature
= \
4178 signature_factory(cct
, k
.key
, string_to_sign
);
4180 ldout(cct
, 15) << "string_to_sign="
4181 << rgw::crypt_sanitize::log_content
{string_to_sign
}
4183 ldout(cct
, 15) << "server signature=" << server_signature
<< dendl
;
4184 ldout(cct
, 15) << "client signature=" << signature
<< dendl
;
4185 ldout(cct
, 15) << "compare=" << signature
.compare(server_signature
) << dendl
;
4187 if (static_cast<boost::string_view
>(server_signature
) != signature
) {
4188 return result_t::deny(-ERR_SIGNATURE_NO_MATCH
);
4191 auto apl
= apl_factory
->create_apl_local(cct
, s
, user_info
, k
.subuser
);
4192 return result_t::grant(std::move(apl
), completer_factory(k
.key
));
4195 bool rgw::auth::s3::S3AnonymousEngine::is_applicable(
4198 if (s
->op
== OP_OPTIONS
) {
4204 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
4206 return route
== AwsRoute::QUERY_STRING
&& version
== AwsVersion::UNKOWN
;