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 return RGWGetObj_ObjStore::get_params();
135 int RGWGetObj_ObjStore_S3::send_response_data_error()
138 return send_response_data(bl
, 0 , 0);
142 int decode_attr_bl_single_value(map
<string
, bufferlist
>& attrs
, const char *attr_name
, T
*result
, T def_val
)
144 map
<string
, bufferlist
>::iterator iter
= attrs
.find(attr_name
);
145 if (iter
== attrs
.end()) {
149 bufferlist
& bl
= iter
->second
;
150 if (bl
.length() == 0) {
154 bufferlist::iterator bliter
= bl
.begin();
156 ::decode(*result
, bliter
);
157 } catch (buffer::error
& err
) {
163 int RGWGetObj_ObjStore_S3::send_response_data(bufferlist
& bl
, off_t bl_ofs
,
166 const char *content_type
= NULL
;
167 string content_type_str
;
168 map
<string
, string
> response_attrs
;
169 map
<string
, string
>::iterator riter
;
170 bufferlist metadata_bl
;
175 if (custom_http_ret
) {
176 set_req_state_err(s
, 0);
177 dump_errno(s
, custom_http_ret
);
179 set_req_state_err(s
, (partial_content
&& !op_ret
) ? STATUS_PARTIAL_CONTENT
188 dump_range(s
, start
, end
, s
->obj_size
);
190 if (s
->system_request
&&
191 s
->info
.args
.exists(RGW_SYS_PARAM_PREFIX
"prepend-metadata")) {
193 dump_header(s
, "Rgwx-Object-Size", (long long)total_len
);
197 * in this case, we're not returning the object's content, only the prepended
203 /* JSON encode object metadata */
205 jf
.open_object_section("obj_metadata");
206 encode_json("attrs", attrs
, &jf
);
208 encode_json("mtime", ut
, &jf
);
212 metadata_bl
.append(ss
.str());
213 dump_header(s
, "Rgwx-Embedded-Metadata-Len", metadata_bl
.length());
214 total_len
+= metadata_bl
.length();
217 if (s
->system_request
&& !real_clock::is_zero(lastmod
)) {
218 /* we end up dumping mtime in two different methods, a bit redundant */
219 dump_epoch_header(s
, "Rgwx-Mtime", lastmod
);
221 int r
= decode_attr_bl_single_value(attrs
, RGW_ATTR_PG_VER
, &pg_ver
, (uint64_t)0);
223 ldout(s
->cct
, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl
;
225 dump_header(s
, "Rgwx-Obj-PG-Ver", pg_ver
);
227 uint32_t source_zone_short_id
= 0;
228 r
= decode_attr_bl_single_value(attrs
, RGW_ATTR_SOURCE_ZONE
, &source_zone_short_id
, (uint32_t)0);
230 ldout(s
->cct
, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl
;
232 if (source_zone_short_id
!= 0) {
233 dump_header(s
, "Rgwx-Source-Zone-Short-Id", source_zone_short_id
);
237 for (auto &it
: crypt_http_responses
)
238 dump_header(s
, it
.first
, it
.second
);
240 dump_content_length(s
, total_len
);
241 dump_last_modified(s
, lastmod
);
242 if (!version_id
.empty()) {
243 dump_header(s
, "x-amz-version-id", version_id
);
248 if (! lo_etag
.empty()) {
249 /* Handle etag of Swift API's large objects (DLO/SLO). It's entirerly
250 * legit to perform GET on them through S3 API. In such situation,
251 * a client should receive the composited content with corresponding
253 dump_etag(s
, lo_etag
);
255 auto iter
= attrs
.find(RGW_ATTR_ETAG
);
256 if (iter
!= attrs
.end()) {
257 dump_etag(s
, iter
->second
);
261 for (struct response_attr_param
*p
= resp_attr_params
; p
->param
; p
++) {
263 string val
= s
->info
.args
.get(p
->param
, &exists
);
265 if (strcmp(p
->param
, "response-content-type") != 0) {
266 response_attrs
[p
->http_attr
] = val
;
268 content_type_str
= val
;
269 content_type
= content_type_str
.c_str();
274 for (auto iter
= attrs
.begin(); iter
!= attrs
.end(); ++iter
) {
275 const char *name
= iter
->first
.c_str();
276 map
<string
, string
>::iterator aiter
= rgw_to_http_attrs
.find(name
);
277 if (aiter
!= rgw_to_http_attrs
.end()) {
278 if (response_attrs
.count(aiter
->second
) == 0) {
279 /* Was not already overridden by a response param. */
280 response_attrs
[aiter
->second
] = iter
->second
.c_str();
282 } else if (iter
->first
.compare(RGW_ATTR_CONTENT_TYPE
) == 0) {
283 /* Special handling for content_type. */
285 content_type
= iter
->second
.c_str();
287 } else if (strcmp(name
, RGW_ATTR_SLO_UINDICATOR
) == 0) {
288 // this attr has an extra length prefix from ::encode() in prior versions
289 dump_header(s
, "X-Object-Meta-Static-Large-Object", "True");
290 } else if (strncmp(name
, RGW_ATTR_META_PREFIX
,
291 sizeof(RGW_ATTR_META_PREFIX
)-1) == 0) {
292 /* User custom metadata. */
293 name
+= sizeof(RGW_ATTR_PREFIX
) - 1;
294 dump_header(s
, name
, iter
->second
);
295 } else if (iter
->first
.compare(RGW_ATTR_TAGS
) == 0) {
298 bufferlist::iterator it
= iter
->second
.begin();
300 } catch (buffer::error
&err
) {
301 ldout(s
->cct
,0) << "Error caught buffer::error couldn't decode TagSet " << dendl
;
303 dump_header(s
, RGW_AMZ_TAG_COUNT
, obj_tags
.count());
309 for (riter
= response_attrs
.begin(); riter
!= response_attrs
.end();
311 dump_header(s
, riter
->first
, riter
->second
);
314 if (op_ret
== -ERR_NOT_MODIFIED
) {
318 content_type
= "binary/octet-stream";
320 end_header(s
, this, content_type
);
323 if (metadata_bl
.length()) {
324 dump_body(s
, metadata_bl
);
329 if (get_data
&& !op_ret
) {
330 int r
= dump_body(s
, bl
.c_str() + bl_ofs
, bl_len
);
338 int RGWGetObj_ObjStore_S3::get_decrypt_filter(std::unique_ptr
<RGWGetDataCB
> *filter
, RGWGetDataCB
* cb
, bufferlist
* manifest_bl
)
341 std::unique_ptr
<BlockCrypt
> block_crypt
;
342 res
= rgw_s3_prepare_decrypt(s
, attrs
, &block_crypt
, crypt_http_responses
);
344 if (block_crypt
!= nullptr) {
345 auto f
= std::unique_ptr
<RGWGetObj_BlockDecrypt
>(new RGWGetObj_BlockDecrypt(s
->cct
, cb
, std::move(block_crypt
)));
346 //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
348 if (manifest_bl
!= nullptr) {
349 res
= f
->read_manifest(*manifest_bl
);
351 *filter
= std::move(f
);
360 void RGWGetObjTags_ObjStore_S3::send_response_data(bufferlist
& bl
)
363 end_header(s
, this, "application/xml");
366 s
->formatter
->open_object_section_in_ns("Tagging", XMLNS_AWS_S3
);
367 s
->formatter
->open_object_section("TagSet");
369 RGWObjTagSet_S3 tagset
;
370 bufferlist::iterator iter
= bl
.begin();
373 } catch (buffer::error
& err
) {
374 ldout(s
->cct
,0) << "ERROR: caught buffer::error, couldn't decode TagSet" << dendl
;
378 tagset
.dump_xml(s
->formatter
);
380 s
->formatter
->close_section();
381 s
->formatter
->close_section();
382 rgw_flush_formatter_and_reset(s
, s
->formatter
);
386 int RGWPutObjTags_ObjStore_S3::get_params()
388 RGWObjTagsXMLParser parser
;
397 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
398 int r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
403 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
405 if (!parser
.parse(data
, len
, 1)) {
406 return -ERR_MALFORMED_XML
;
409 RGWObjTagSet_S3
*obj_tags_s3
;
410 RGWObjTagging_S3
*tagging
;
412 tagging
= static_cast<RGWObjTagging_S3
*>(parser
.find_first("Tagging"));
413 obj_tags_s3
= static_cast<RGWObjTagSet_S3
*>(tagging
->find_first("TagSet"));
415 return -ERR_MALFORMED_XML
;
419 r
= obj_tags_s3
->rebuild(obj_tags
);
423 obj_tags
.encode(tags_bl
);
424 ldout(s
->cct
, 20) << "Read " << obj_tags
.count() << "tags" << dendl
;
429 void RGWPutObjTags_ObjStore_S3::send_response()
432 set_req_state_err(s
, op_ret
);
434 end_header(s
, this, "application/xml");
439 void RGWDeleteObjTags_ObjStore_S3::send_response()
445 r
= STATUS_NO_CONTENT
;
447 set_req_state_err(s
, r
);
452 void RGWListBuckets_ObjStore_S3::send_response_begin(bool has_buckets
)
455 set_req_state_err(s
, op_ret
);
458 end_header(s
, NULL
, "application/xml");
461 list_all_buckets_start(s
);
462 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
);
463 s
->formatter
->open_array_section("Buckets");
468 void RGWListBuckets_ObjStore_S3::send_response_data(RGWUserBuckets
& buckets
)
473 map
<string
, RGWBucketEnt
>& m
= buckets
.get_buckets();
474 map
<string
, RGWBucketEnt
>::iterator iter
;
476 for (iter
= m
.begin(); iter
!= m
.end(); ++iter
) {
477 RGWBucketEnt obj
= iter
->second
;
480 rgw_flush_formatter(s
, s
->formatter
);
483 void RGWListBuckets_ObjStore_S3::send_response_end()
486 s
->formatter
->close_section();
487 list_all_buckets_end(s
);
488 rgw_flush_formatter_and_reset(s
, s
->formatter
);
492 int RGWGetUsage_ObjStore_S3::get_params()
494 start_date
= s
->info
.args
.get("start-date");
495 end_date
= s
->info
.args
.get("end-date");
499 static void dump_usage_categories_info(Formatter
*formatter
, const rgw_usage_log_entry
& entry
, map
<string
, bool> *categories
)
501 formatter
->open_array_section("categories");
502 map
<string
, rgw_usage_data
>::const_iterator uiter
;
503 for (uiter
= entry
.usage_map
.begin(); uiter
!= entry
.usage_map
.end(); ++uiter
) {
504 if (categories
&& !categories
->empty() && !categories
->count(uiter
->first
))
506 const rgw_usage_data
& usage
= uiter
->second
;
507 formatter
->open_object_section("Entry");
508 formatter
->dump_string("Category", uiter
->first
);
509 formatter
->dump_int("BytesSent", usage
.bytes_sent
);
510 formatter
->dump_int("BytesReceived", usage
.bytes_received
);
511 formatter
->dump_int("Ops", usage
.ops
);
512 formatter
->dump_int("SuccessfulOps", usage
.successful_ops
);
513 formatter
->close_section(); // Entry
515 formatter
->close_section(); // Category
518 void RGWGetUsage_ObjStore_S3::send_response()
521 set_req_state_err(s
, op_ret
);
524 end_header(s
, this, "application/xml");
529 Formatter
*formatter
= s
->formatter
;
531 bool user_section_open
= false;
533 formatter
->open_object_section("Usage");
534 if (show_log_entries
) {
535 formatter
->open_array_section("Entries");
537 map
<rgw_user_bucket
, rgw_usage_log_entry
>::iterator iter
;
538 for (iter
= usage
.begin(); iter
!= usage
.end(); ++iter
) {
539 const rgw_user_bucket
& ub
= iter
->first
;
540 const rgw_usage_log_entry
& entry
= iter
->second
;
542 if (show_log_entries
) {
543 if (ub
.user
.compare(last_owner
) != 0) {
544 if (user_section_open
) {
545 formatter
->close_section();
546 formatter
->close_section();
548 formatter
->open_object_section("User");
549 formatter
->dump_string("Owner", ub
.user
);
550 formatter
->open_array_section("Buckets");
551 user_section_open
= true;
552 last_owner
= ub
.user
;
554 formatter
->open_object_section("Bucket");
555 formatter
->dump_string("Bucket", ub
.bucket
);
556 utime_t
ut(entry
.epoch
, 0);
557 ut
.gmtime(formatter
->dump_stream("Time"));
558 formatter
->dump_int("Epoch", entry
.epoch
);
559 dump_usage_categories_info(formatter
, entry
, &categories
);
560 formatter
->close_section(); // bucket
563 summary_map
[ub
.user
].aggregate(entry
, &categories
);
566 if (show_log_entries
) {
567 if (user_section_open
) {
568 formatter
->close_section(); // buckets
569 formatter
->close_section(); //user
571 formatter
->close_section(); // entries
575 formatter
->open_array_section("Summary");
576 map
<string
, rgw_usage_log_entry
>::iterator siter
;
577 for (siter
= summary_map
.begin(); siter
!= summary_map
.end(); ++siter
) {
578 const rgw_usage_log_entry
& entry
= siter
->second
;
579 formatter
->open_object_section("User");
580 formatter
->dump_string("User", siter
->first
);
581 dump_usage_categories_info(formatter
, entry
, &categories
);
582 rgw_usage_data total_usage
;
583 entry
.sum(total_usage
, categories
);
584 formatter
->open_object_section("Total");
585 formatter
->dump_int("BytesSent", total_usage
.bytes_sent
);
586 formatter
->dump_int("BytesReceived", total_usage
.bytes_received
);
587 formatter
->dump_int("Ops", total_usage
.ops
);
588 formatter
->dump_int("SuccessfulOps", total_usage
.successful_ops
);
589 formatter
->close_section(); // total
590 formatter
->close_section(); // user
593 if (s
->cct
->_conf
->rgw_rest_getusage_op_compat
) {
594 formatter
->open_object_section("Stats");
597 formatter
->dump_int("TotalBytes", header
.stats
.total_bytes
);
598 formatter
->dump_int("TotalBytesRounded", header
.stats
.total_bytes_rounded
);
599 formatter
->dump_int("TotalEntries", header
.stats
.total_entries
);
601 if (s
->cct
->_conf
->rgw_rest_getusage_op_compat
) {
602 formatter
->close_section(); //Stats
605 formatter
->close_section(); // summary
607 formatter
->close_section(); // usage
608 rgw_flush_formatter_and_reset(s
, s
->formatter
);
611 int RGWListBucket_ObjStore_S3::get_params()
613 list_versions
= s
->info
.args
.exists("versions");
614 prefix
= s
->info
.args
.get("prefix");
615 if (!list_versions
) {
616 marker
= s
->info
.args
.get("marker");
618 marker
.name
= s
->info
.args
.get("key-marker");
619 marker
.instance
= s
->info
.args
.get("version-id-marker");
621 max_keys
= s
->info
.args
.get("max-keys");
622 op_ret
= parse_max_keys();
626 delimiter
= s
->info
.args
.get("delimiter");
627 encoding_type
= s
->info
.args
.get("encoding-type");
628 if (s
->system_request
) {
629 s
->info
.args
.get_bool("objs-container", &objs_container
, false);
630 const char *shard_id_str
= s
->info
.env
->get("HTTP_RGWX_SHARD_ID");
633 shard_id
= strict_strtol(shard_id_str
, 10, &err
);
635 ldout(s
->cct
, 5) << "bad shard id specified: " << shard_id_str
<< dendl
;
639 shard_id
= s
->bucket_instance_shard_id
;
645 void RGWListBucket_ObjStore_S3::send_versioned_response()
647 s
->formatter
->open_object_section_in_ns("ListVersionsResult", XMLNS_AWS_S3
);
648 if (!s
->bucket_tenant
.empty())
649 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
650 s
->formatter
->dump_string("Name", s
->bucket_name
);
651 s
->formatter
->dump_string("Prefix", prefix
);
652 s
->formatter
->dump_string("KeyMarker", marker
.name
);
653 s
->formatter
->dump_string("VersionIdMarker", marker
.instance
);
654 if (is_truncated
&& !next_marker
.empty()) {
655 s
->formatter
->dump_string("NextKeyMarker", next_marker
.name
);
656 s
->formatter
->dump_string("NextVersionIdMarker", next_marker
.instance
);
658 s
->formatter
->dump_int("MaxKeys", max
);
659 if (!delimiter
.empty())
660 s
->formatter
->dump_string("Delimiter", delimiter
);
662 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
665 bool encode_key
= false;
666 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
667 s
->formatter
->dump_string("EncodingType", "url");
672 if (objs_container
) {
673 s
->formatter
->open_array_section("Entries");
676 vector
<rgw_bucket_dir_entry
>::iterator iter
;
677 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
678 const char *section_name
= (iter
->is_delete_marker() ? "DeleteMarker"
680 s
->formatter
->open_object_section(section_name
);
681 if (objs_container
) {
682 s
->formatter
->dump_bool("IsDeleteMarker", iter
->is_delete_marker());
684 rgw_obj_key
key(iter
->key
);
687 url_encode(key
.name
, key_name
);
688 s
->formatter
->dump_string("Key", key_name
);
690 s
->formatter
->dump_string("Key", key
.name
);
692 string version_id
= key
.instance
;
693 if (version_id
.empty()) {
696 if (s
->system_request
) {
697 if (iter
->versioned_epoch
> 0) {
698 s
->formatter
->dump_int("VersionedEpoch", iter
->versioned_epoch
);
700 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
701 utime_t
ut(iter
->meta
.mtime
);
702 ut
.gmtime_nsec(s
->formatter
->dump_stream("RgwxMtime"));
704 s
->formatter
->dump_string("VersionId", version_id
);
705 s
->formatter
->dump_bool("IsLatest", iter
->is_current());
706 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
707 if (!iter
->is_delete_marker()) {
708 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
709 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
710 s
->formatter
->dump_string("StorageClass", "STANDARD");
712 dump_owner(s
, iter
->meta
.owner
, iter
->meta
.owner_display_name
);
713 s
->formatter
->close_section();
715 if (objs_container
) {
716 s
->formatter
->close_section();
719 if (!common_prefixes
.empty()) {
720 map
<string
, bool>::iterator pref_iter
;
721 for (pref_iter
= common_prefixes
.begin();
722 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
723 s
->formatter
->open_array_section("CommonPrefixes");
724 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
725 s
->formatter
->close_section();
729 s
->formatter
->close_section();
730 rgw_flush_formatter_and_reset(s
, s
->formatter
);
733 void RGWListBucket_ObjStore_S3::send_response()
736 set_req_state_err(s
, op_ret
);
739 end_header(s
, this, "application/xml");
745 send_versioned_response();
749 s
->formatter
->open_object_section_in_ns("ListBucketResult", XMLNS_AWS_S3
);
750 if (!s
->bucket_tenant
.empty())
751 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
752 s
->formatter
->dump_string("Name", s
->bucket_name
);
753 s
->formatter
->dump_string("Prefix", prefix
);
754 s
->formatter
->dump_string("Marker", marker
.name
);
755 if (is_truncated
&& !next_marker
.empty())
756 s
->formatter
->dump_string("NextMarker", next_marker
.name
);
757 s
->formatter
->dump_int("MaxKeys", max
);
758 if (!delimiter
.empty())
759 s
->formatter
->dump_string("Delimiter", delimiter
);
761 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
764 bool encode_key
= false;
765 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
766 s
->formatter
->dump_string("EncodingType", "url");
771 vector
<rgw_bucket_dir_entry
>::iterator iter
;
772 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
773 rgw_obj_key
key(iter
->key
);
774 s
->formatter
->open_array_section("Contents");
777 url_encode(key
.name
, key_name
);
778 s
->formatter
->dump_string("Key", key_name
);
780 s
->formatter
->dump_string("Key", key
.name
);
782 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
783 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
784 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
785 s
->formatter
->dump_string("StorageClass", "STANDARD");
786 dump_owner(s
, iter
->meta
.owner
, iter
->meta
.owner_display_name
);
787 if (s
->system_request
) {
788 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
790 s
->formatter
->close_section();
792 if (!common_prefixes
.empty()) {
793 map
<string
, bool>::iterator pref_iter
;
794 for (pref_iter
= common_prefixes
.begin();
795 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
796 s
->formatter
->open_array_section("CommonPrefixes");
797 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
798 s
->formatter
->close_section();
802 s
->formatter
->close_section();
803 rgw_flush_formatter_and_reset(s
, s
->formatter
);
806 void RGWGetBucketLogging_ObjStore_S3::send_response()
809 end_header(s
, this, "application/xml");
812 s
->formatter
->open_object_section_in_ns("BucketLoggingStatus", XMLNS_AWS_S3
);
813 s
->formatter
->close_section();
814 rgw_flush_formatter_and_reset(s
, s
->formatter
);
817 void RGWGetBucketLocation_ObjStore_S3::send_response()
823 RGWZoneGroup zonegroup
;
826 int ret
= store
->get_zonegroup(s
->bucket_info
.zonegroup
, zonegroup
);
828 api_name
= zonegroup
.api_name
;
830 if (s
->bucket_info
.zonegroup
!= "default") {
831 api_name
= s
->bucket_info
.zonegroup
;
835 s
->formatter
->dump_format_ns("LocationConstraint", XMLNS_AWS_S3
,
836 "%s", api_name
.c_str());
837 rgw_flush_formatter_and_reset(s
, s
->formatter
);
840 void RGWGetBucketVersioning_ObjStore_S3::send_response()
843 end_header(s
, this, "application/xml");
846 s
->formatter
->open_object_section_in_ns("VersioningConfiguration", XMLNS_AWS_S3
);
848 const char *status
= (versioning_enabled
? "Enabled" : "Suspended");
849 s
->formatter
->dump_string("Status", status
);
851 s
->formatter
->close_section();
852 rgw_flush_formatter_and_reset(s
, s
->formatter
);
855 class RGWSetBucketVersioningParser
: public RGWXMLParser
857 XMLObj
*alloc_obj(const char *el
) override
{
862 RGWSetBucketVersioningParser() {}
863 ~RGWSetBucketVersioningParser() override
{}
865 int get_versioning_status(bool *status
) {
866 XMLObj
*config
= find_first("VersioningConfiguration");
872 XMLObj
*field
= config
->find_first("Status");
876 string
& s
= field
->get_data();
878 if (stringcasecmp(s
, "Enabled") == 0) {
880 } else if (stringcasecmp(s
, "Suspended") != 0) {
888 int RGWSetBucketVersioning_ObjStore_S3::get_params()
890 char *data
= nullptr;
893 rgw_rest_read_all_input(s
, &data
, &len
, s
->cct
->_conf
->rgw_max_put_param_size
, false);
898 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
900 r
= do_aws4_auth_completion();
905 RGWSetBucketVersioningParser parser
;
907 if (!parser
.init()) {
908 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
913 if (!parser
.parse(data
, len
, 1)) {
914 ldout(s
->cct
, 10) << "failed to parse data: " << data
<< dendl
;
919 if (!store
->is_meta_master()) {
920 /* only need to keep this data around if we're not meta master */
921 in_data
.append(data
, len
);
924 r
= parser
.get_versioning_status(&enable_versioning
);
929 void RGWSetBucketVersioning_ObjStore_S3::send_response()
932 set_req_state_err(s
, op_ret
);
937 int RGWSetBucketWebsite_ObjStore_S3::get_params()
939 char *data
= nullptr;
941 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
942 int r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
948 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
950 r
= do_aws4_auth_completion();
955 bufferptr
in_ptr(data
, len
);
956 in_data
.append(in_ptr
);
958 RGWXMLDecoder::XMLParser parser
;
959 if (!parser
.init()) {
960 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
964 if (!parser
.parse(data
, len
, 1)) {
965 string
str(data
, len
);
966 ldout(s
->cct
, 5) << "failed to parse xml: " << str
<< dendl
;
971 RGWXMLDecoder::decode_xml("WebsiteConfiguration", website_conf
, &parser
, true);
972 } catch (RGWXMLDecoder::err
& err
) {
973 string
str(data
, len
);
974 ldout(s
->cct
, 5) << "unexpected xml: " << str
<< dendl
;
981 void RGWSetBucketWebsite_ObjStore_S3::send_response()
984 set_req_state_err(s
, op_ret
);
989 void RGWDeleteBucketWebsite_ObjStore_S3::send_response()
992 op_ret
= STATUS_NO_CONTENT
;
994 set_req_state_err(s
, op_ret
);
999 void RGWGetBucketWebsite_ObjStore_S3::send_response()
1002 set_req_state_err(s
, op_ret
);
1004 end_header(s
, this, "application/xml");
1011 RGWBucketWebsiteConf
& conf
= s
->bucket_info
.website_conf
;
1013 s
->formatter
->open_object_section_in_ns("WebsiteConfiguration", XMLNS_AWS_S3
);
1014 conf
.dump_xml(s
->formatter
);
1015 s
->formatter
->close_section(); // WebsiteConfiguration
1016 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1019 static void dump_bucket_metadata(struct req_state
*s
, RGWBucketEnt
& bucket
)
1021 dump_header(s
, "X-RGW-Object-Count", static_cast<long long>(bucket
.count
));
1022 dump_header(s
, "X-RGW-Bytes-Used", static_cast<long long>(bucket
.size
));
1025 void RGWStatBucket_ObjStore_S3::send_response()
1028 dump_bucket_metadata(s
, bucket
);
1031 set_req_state_err(s
, op_ret
);
1034 end_header(s
, this);
1038 static int create_s3_policy(struct req_state
*s
, RGWRados
*store
,
1039 RGWAccessControlPolicy_S3
& s3policy
,
1042 if (s
->has_acl_header
) {
1043 if (!s
->canned_acl
.empty())
1044 return -ERR_INVALID_REQUEST
;
1046 return s3policy
.create_from_headers(store
, s
->info
.env
, owner
);
1049 return s3policy
.create_canned(owner
, s
->bucket_owner
, s
->canned_acl
);
1052 class RGWLocationConstraint
: public XMLObj
1055 RGWLocationConstraint() {}
1056 ~RGWLocationConstraint() override
{}
1057 bool xml_end(const char *el
) override
{
1061 location_constraint
= get_data();
1066 string location_constraint
;
1069 class RGWCreateBucketConfig
: public XMLObj
1072 RGWCreateBucketConfig() {}
1073 ~RGWCreateBucketConfig() override
{}
1076 class RGWCreateBucketParser
: public RGWXMLParser
1078 XMLObj
*alloc_obj(const char *el
) override
{
1083 RGWCreateBucketParser() {}
1084 ~RGWCreateBucketParser() override
{}
1086 bool get_location_constraint(string
& zone_group
) {
1087 XMLObj
*config
= find_first("CreateBucketConfiguration");
1091 XMLObj
*constraint
= config
->find_first("LocationConstraint");
1095 zone_group
= constraint
->get_data();
1101 int RGWCreateBucket_ObjStore_S3::get_params()
1103 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1105 int r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
1112 char *data
= nullptr;
1114 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
1115 op_ret
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
1117 if ((op_ret
< 0) && (op_ret
!= -ERR_LENGTH_REQUIRED
))
1120 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
1122 const int auth_ret
= do_aws4_auth_completion();
1127 bufferptr
in_ptr(data
, len
);
1128 in_data
.append(in_ptr
);
1131 RGWCreateBucketParser parser
;
1133 if (!parser
.init()) {
1134 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
1138 bool success
= parser
.parse(data
, len
, 1);
1139 ldout(s
->cct
, 20) << "create bucket input data=" << data
<< dendl
;
1142 ldout(s
->cct
, 0) << "failed to parse input: " << data
<< dendl
;
1146 if (!parser
.get_location_constraint(location_constraint
)) {
1147 ldout(s
->cct
, 0) << "provided input did not specify location constraint correctly" << dendl
;
1151 ldout(s
->cct
, 10) << "create bucket location constraint: "
1152 << location_constraint
<< dendl
;
1155 size_t pos
= location_constraint
.find(':');
1156 if (pos
!= string::npos
) {
1157 placement_rule
= location_constraint
.substr(pos
+ 1);
1158 location_constraint
= location_constraint
.substr(0, pos
);
1164 void RGWCreateBucket_ObjStore_S3::send_response()
1166 if (op_ret
== -ERR_BUCKET_EXISTS
)
1169 set_req_state_err(s
, op_ret
);
1176 if (s
->system_request
) {
1177 JSONFormatter f
; /* use json formatter for system requests output */
1179 f
.open_object_section("info");
1180 encode_json("entry_point_object_ver", ep_objv
, &f
);
1181 encode_json("object_ver", info
.objv_tracker
.read_version
, &f
);
1182 encode_json("bucket_info", info
, &f
);
1184 rgw_flush_formatter_and_reset(s
, &f
);
1188 void RGWDeleteBucket_ObjStore_S3::send_response()
1192 r
= STATUS_NO_CONTENT
;
1194 set_req_state_err(s
, r
);
1196 end_header(s
, this);
1198 if (s
->system_request
) {
1199 JSONFormatter f
; /* use json formatter for system requests output */
1201 f
.open_object_section("info");
1202 encode_json("object_ver", objv_tracker
.read_version
, &f
);
1204 rgw_flush_formatter_and_reset(s
, &f
);
1208 int RGWPutObj_ObjStore_S3::get_params()
1211 return -ERR_LENGTH_REQUIRED
;
1213 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
1214 map
<string
, bufferlist
> src_attrs
;
1218 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1219 ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
1225 if_match
= s
->info
.env
->get("HTTP_IF_MATCH");
1226 if_nomatch
= s
->info
.env
->get("HTTP_IF_NONE_MATCH");
1227 copy_source
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE");
1228 copy_source_range
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE");
1230 /* handle x-amz-copy-source */
1233 if (*copy_source
== '/') ++copy_source
;
1234 copy_source_bucket_name
= copy_source
;
1235 pos
= copy_source_bucket_name
.find("/");
1236 if (pos
== std::string::npos
) {
1238 ldout(s
->cct
, 5) << "x-amz-copy-source bad format" << dendl
;
1241 copy_source_object_name
= copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
1242 copy_source_bucket_name
= copy_source_bucket_name
.substr(0, pos
);
1243 #define VERSION_ID_STR "?versionId="
1244 pos
= copy_source_object_name
.find(VERSION_ID_STR
);
1245 if (pos
== std::string::npos
) {
1246 copy_source_object_name
= url_decode(copy_source_object_name
);
1248 copy_source_version_id
= copy_source_object_name
.substr(pos
+ sizeof(VERSION_ID_STR
) - 1);
1249 copy_source_object_name
= url_decode(copy_source_object_name
.substr(0, pos
));
1251 pos
= copy_source_bucket_name
.find(":");
1252 if (pos
== std::string::npos
) {
1253 copy_source_tenant_name
= s
->src_tenant_name
;
1255 copy_source_tenant_name
= copy_source_bucket_name
.substr(0, pos
);
1256 copy_source_bucket_name
= copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
1257 if (copy_source_bucket_name
.empty()) {
1259 ldout(s
->cct
, 5) << "source bucket name is empty" << dendl
;
1263 ret
= store
->get_bucket_info(obj_ctx
,
1264 copy_source_tenant_name
,
1265 copy_source_bucket_name
,
1266 copy_source_bucket_info
,
1269 ldout(s
->cct
, 5) << __func__
<< "(): get_bucket_info() returned ret=" << ret
<< dendl
;
1273 /* handle x-amz-copy-source-range */
1275 if (copy_source_range
) {
1276 string range
= copy_source_range
;
1277 pos
= range
.find("=");
1278 if (pos
== std::string::npos
) {
1280 ldout(s
->cct
, 5) << "x-amz-copy-source-range bad format" << dendl
;
1283 range
= range
.substr(pos
+ 1);
1284 pos
= range
.find("-");
1285 if (pos
== std::string::npos
) {
1287 ldout(s
->cct
, 5) << "x-amz-copy-source-range bad format" << dendl
;
1290 string first
= range
.substr(0, pos
);
1291 string last
= range
.substr(pos
+ 1);
1292 copy_source_range_fst
= strtoull(first
.c_str(), NULL
, 10);
1293 copy_source_range_lst
= strtoull(last
.c_str(), NULL
, 10);
1298 /* handle object tagging */
1299 auto tag_str
= s
->info
.env
->get("HTTP_X_AMZ_TAGGING");
1301 obj_tags
= ceph::make_unique
<RGWObjTags
>();
1302 ret
= obj_tags
->set_from_string(tag_str
);
1304 ldout(s
->cct
,0) << "setting obj tags failed with " << ret
<< dendl
;
1305 if (ret
== -ERR_INVALID_TAG
){
1306 ret
= -EINVAL
; //s3 returns only -EINVAL for PUT requests
1313 return RGWPutObj_ObjStore::get_params();
1316 int RGWPutObj_ObjStore_S3::get_data(bufferlist
& bl
)
1318 const int ret
= RGWPutObj_ObjStore::get_data(bl
);
1320 const int ret_auth
= do_aws4_auth_completion();
1329 static int get_success_retcode(int code
)
1333 return STATUS_CREATED
;
1335 return STATUS_NO_CONTENT
;
1340 void RGWPutObj_ObjStore_S3::send_response()
1343 set_req_state_err(s
, op_ret
);
1346 if (s
->cct
->_conf
->rgw_s3_success_create_obj_status
) {
1347 op_ret
= get_success_retcode(
1348 s
->cct
->_conf
->rgw_s3_success_create_obj_status
);
1349 set_req_state_err(s
, op_ret
);
1354 dump_content_length(s
, 0);
1355 for (auto &it
: crypt_http_responses
)
1356 dump_header(s
, it
.first
, it
.second
);
1359 end_header(s
, this, "application/xml");
1363 time_t secs
= (time_t)ut
.sec();
1364 gmtime_r(&secs
, &tmp
);
1365 char buf
[TIME_BUF_SIZE
];
1366 s
->formatter
->open_object_section_in_ns("CopyPartResult",
1367 "http://s3.amazonaws.com/doc/2006-03-01/");
1368 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%T.000Z", &tmp
) > 0) {
1369 s
->formatter
->dump_string("LastModified", buf
);
1371 s
->formatter
->dump_string("ETag", etag
);
1372 s
->formatter
->close_section();
1373 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1377 if (s
->system_request
&& !real_clock::is_zero(mtime
)) {
1378 dump_epoch_header(s
, "Rgwx-Mtime", mtime
);
1380 end_header(s
, this);
1383 static inline int get_obj_attrs(RGWRados
*store
, struct req_state
*s
, rgw_obj
& obj
, map
<string
, bufferlist
>& attrs
)
1385 RGWRados::Object
op_target(store
, s
->bucket_info
, *static_cast<RGWObjectCtx
*>(s
->obj_ctx
), obj
);
1386 RGWRados::Object::Read
read_op(&op_target
);
1388 read_op
.params
.attrs
= &attrs
;
1390 return read_op
.prepare();
1393 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const std::string
& value
)
1397 attrs
.emplace(key
, std::move(bl
));
1400 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const char* value
)
1404 attrs
.emplace(key
, std::move(bl
));
1407 int RGWPutObj_ObjStore_S3::get_decrypt_filter(
1408 std::unique_ptr
<RGWGetDataCB
>* filter
,
1410 map
<string
, bufferlist
>& attrs
,
1411 bufferlist
* manifest_bl
)
1413 std::map
<std::string
, std::string
> crypt_http_responses_unused
;
1416 std::unique_ptr
<BlockCrypt
> block_crypt
;
1417 res
= rgw_s3_prepare_decrypt(s
, attrs
, &block_crypt
, crypt_http_responses_unused
);
1419 if (block_crypt
!= nullptr) {
1420 auto f
= std::unique_ptr
<RGWGetObj_BlockDecrypt
>(new RGWGetObj_BlockDecrypt(s
->cct
, cb
, std::move(block_crypt
)));
1421 //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
1423 if (manifest_bl
!= nullptr) {
1424 res
= f
->read_manifest(*manifest_bl
);
1426 *filter
= std::move(f
);
1435 int RGWPutObj_ObjStore_S3::get_encrypt_filter(
1436 std::unique_ptr
<RGWPutObjDataProcessor
>* filter
,
1437 RGWPutObjDataProcessor
* cb
)
1440 RGWPutObjProcessor_Multipart
* multi_processor
=dynamic_cast<RGWPutObjProcessor_Multipart
*>(cb
);
1441 if (multi_processor
!= nullptr) {
1442 RGWMPObj
* mp
= nullptr;
1443 multi_processor
->get_mp(&mp
);
1444 if (mp
!= nullptr) {
1445 map
<string
, bufferlist
> xattrs
;
1447 meta_oid
= mp
->get_meta();
1450 obj
.init_ns(s
->bucket
, meta_oid
, RGW_OBJ_NS_MULTIPART
);
1451 obj
.set_in_extra_data(true);
1452 res
= get_obj_attrs(store
, s
, obj
, xattrs
);
1454 std::unique_ptr
<BlockCrypt
> block_crypt
;
1455 /* We are adding to existing object.
1456 * We use crypto mode that configured as if we were decrypting. */
1457 res
= rgw_s3_prepare_decrypt(s
, xattrs
, &block_crypt
, crypt_http_responses
);
1458 if (res
== 0 && block_crypt
!= nullptr)
1459 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
1460 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1463 /* it is ok, to not have encryption at all */
1467 std::unique_ptr
<BlockCrypt
> block_crypt
;
1468 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, &block_crypt
, crypt_http_responses
);
1469 if (res
== 0 && block_crypt
!= nullptr) {
1470 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
1471 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1477 void RGWPostObj_ObjStore_S3::rebuild_key(string
& key
)
1479 static string var
= "${filename}";
1480 int pos
= key
.find(var
);
1484 string new_key
= key
.substr(0, pos
);
1485 new_key
.append(filename
);
1486 new_key
.append(key
.substr(pos
+ var
.size()));
1491 std::string
RGWPostObj_ObjStore_S3::get_current_filename() const
1493 return s
->object
.name
;
1496 std::string
RGWPostObj_ObjStore_S3::get_current_content_type() const
1498 return content_type
;
1501 int RGWPostObj_ObjStore_S3::get_params()
1503 op_ret
= RGWPostObj_ObjStore::get_params();
1508 ldout(s
->cct
, 20) << "adding bucket to policy env: " << s
->bucket
.name
1510 env
.add_var("bucket", s
->bucket
.name
);
1514 struct post_form_part part
;
1515 int r
= read_form_part_header(&part
, done
);
1519 if (s
->cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 20)) {
1520 ldout(s
->cct
, 20) << "read part header -- part.name="
1521 << part
.name
<< dendl
;
1523 for (const auto& pair
: part
.fields
) {
1524 ldout(s
->cct
, 20) << "field.name=" << pair
.first
<< dendl
;
1525 ldout(s
->cct
, 20) << "field.val=" << pair
.second
.val
<< dendl
;
1526 ldout(s
->cct
, 20) << "field.params:" << dendl
;
1528 for (const auto& param_pair
: pair
.second
.params
) {
1529 ldout(s
->cct
, 20) << " " << param_pair
.first
1530 << " -> " << param_pair
.second
<< dendl
;
1535 if (done
) { /* unexpected here */
1536 err_msg
= "Malformed request";
1540 if (stringcasecmp(part
.name
, "file") == 0) { /* beginning of data transfer */
1541 struct post_part_field
& field
= part
.fields
["Content-Disposition"];
1542 map
<string
, string
>::iterator iter
= field
.params
.find("filename");
1543 if (iter
!= field
.params
.end()) {
1544 filename
= iter
->second
;
1546 parts
[part
.name
] = part
;
1551 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1552 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
1553 if (r
< 0 || !boundary
) {
1554 err_msg
= "Couldn't find boundary";
1557 parts
[part
.name
] = part
;
1558 string
part_str(part
.data
.c_str(), part
.data
.length());
1559 env
.add_var(part
.name
, part_str
);
1563 if (!part_str(parts
, "key", &object_str
)) {
1564 err_msg
= "Key not specified";
1568 s
->object
= rgw_obj_key(object_str
);
1570 rebuild_key(s
->object
.name
);
1572 if (s
->object
.empty()) {
1573 err_msg
= "Empty object name";
1577 env
.add_var("key", s
->object
.name
);
1579 part_str(parts
, "Content-Type", &content_type
);
1580 env
.add_var("Content-Type", content_type
);
1582 map
<string
, struct post_form_part
, ltstr_nocase
>::iterator piter
=
1583 parts
.upper_bound(RGW_AMZ_META_PREFIX
);
1584 for (; piter
!= parts
.end(); ++piter
) {
1585 string n
= piter
->first
;
1586 if (strncasecmp(n
.c_str(), RGW_AMZ_META_PREFIX
,
1587 sizeof(RGW_AMZ_META_PREFIX
) - 1) != 0)
1590 string attr_name
= RGW_ATTR_PREFIX
;
1591 attr_name
.append(n
);
1593 /* need to null terminate it */
1594 bufferlist
& data
= piter
->second
.data
;
1595 string str
= string(data
.c_str(), data
.length());
1598 attr_bl
.append(str
.c_str(), str
.size() + 1);
1600 attrs
[attr_name
] = attr_bl
;
1602 // TODO: refactor this and the above loop to share code
1603 piter
= parts
.find(RGW_AMZ_WEBSITE_REDIRECT_LOCATION
);
1604 if (piter
!= parts
.end()) {
1605 string n
= piter
->first
;
1606 string attr_name
= RGW_ATTR_PREFIX
;
1607 attr_name
.append(n
);
1608 /* need to null terminate it */
1609 bufferlist
& data
= piter
->second
.data
;
1610 string str
= string(data
.c_str(), data
.length());
1613 attr_bl
.append(str
.c_str(), str
.size() + 1);
1615 attrs
[attr_name
] = attr_bl
;
1618 int r
= get_policy();
1627 min_len
= post_policy
.min_length
;
1628 max_len
= post_policy
.max_length
;
1635 int RGWPostObj_ObjStore_S3::get_tags()
1638 if (part_str(parts
, "tagging", &tags_str
)) {
1639 RGWObjTagsXMLParser parser
;
1640 if (!parser
.init()){
1641 ldout(s
->cct
, 0) << "Couldn't init RGWObjTags XML parser" << dendl
;
1642 err_msg
= "Server couldn't process the request";
1643 return -EINVAL
; // TODO: This class of errors in rgw code should be a 5XX error
1645 if (!parser
.parse(tags_str
.c_str(), tags_str
.size(), 1)) {
1646 ldout(s
->cct
,0 ) << "Invalid Tagging XML" << dendl
;
1647 err_msg
= "Invalid Tagging XML";
1651 RGWObjTagSet_S3
*obj_tags_s3
;
1652 RGWObjTagging_S3
*tagging
;
1654 tagging
= static_cast<RGWObjTagging_S3
*>(parser
.find_first("Tagging"));
1655 obj_tags_s3
= static_cast<RGWObjTagSet_S3
*>(tagging
->find_first("TagSet"));
1657 return -ERR_MALFORMED_XML
;
1660 RGWObjTags obj_tags
;
1661 int r
= obj_tags_s3
->rebuild(obj_tags
);
1666 obj_tags
.encode(tags_bl
);
1667 ldout(s
->cct
, 20) << "Read " << obj_tags
.count() << "tags" << dendl
;
1668 attrs
[RGW_ATTR_TAGS
] = tags_bl
;
1675 int RGWPostObj_ObjStore_S3::get_policy()
1677 if (part_bl(parts
, "policy", &s
->auth
.s3_postobj_creds
.encoded_policy
)) {
1678 bool aws4_auth
= false;
1680 /* x-amz-algorithm handling */
1681 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
1682 if ((part_str(parts
, "x-amz-algorithm", &s
->auth
.s3_postobj_creds
.x_amz_algorithm
)) &&
1683 (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
)) {
1684 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v4 (AWS4-HMAC-SHA256)" << dendl
;
1687 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v2" << dendl
;
1690 // check that the signature matches the encoded policy
1694 /* x-amz-credential handling */
1695 if (!part_str(parts
, "x-amz-credential",
1696 &s
->auth
.s3_postobj_creds
.x_amz_credential
)) {
1697 ldout(s
->cct
, 0) << "No S3 aws4 credential found!" << dendl
;
1698 err_msg
= "Missing aws4 credential";
1702 /* x-amz-signature handling */
1703 if (!part_str(parts
, "x-amz-signature",
1704 &s
->auth
.s3_postobj_creds
.signature
)) {
1705 ldout(s
->cct
, 0) << "No aws4 signature found!" << dendl
;
1706 err_msg
= "Missing aws4 signature";
1710 /* x-amz-date handling */
1711 std::string received_date_str
;
1712 if (!part_str(parts
, "x-amz-date", &received_date_str
)) {
1713 ldout(s
->cct
, 0) << "No aws4 date found!" << dendl
;
1714 err_msg
= "Missing aws4 date";
1720 // check that the signature matches the encoded policy
1721 if (!part_str(parts
, "AWSAccessKeyId",
1722 &s
->auth
.s3_postobj_creds
.access_key
)) {
1723 ldout(s
->cct
, 0) << "No S3 aws2 access key found!" << dendl
;
1724 err_msg
= "Missing aws2 access key";
1728 if (!part_str(parts
, "signature", &s
->auth
.s3_postobj_creds
.signature
)) {
1729 ldout(s
->cct
, 0) << "No aws2 signature found!" << dendl
;
1730 err_msg
= "Missing aws2 signature";
1735 /* FIXME: this is a makeshift solution. The browser upload authentication will be
1736 * handled by an instance of rgw::auth::Completer spawned in Handler's authorize()
1738 const int ret
= rgw::auth::Strategy::apply(auth_registry_ptr
->get_s3_post(), s
);
1742 /* Populate the owner info. */
1743 s
->owner
.set_id(s
->user
->user_id
);
1744 s
->owner
.set_name(s
->user
->display_name
);
1745 ldout(s
->cct
, 0) << "Successful Signature Verification!" << dendl
;
1748 ceph::bufferlist decoded_policy
;
1750 decoded_policy
.decode_base64(s
->auth
.s3_postobj_creds
.encoded_policy
);
1751 } catch (buffer::error
& err
) {
1752 ldout(s
->cct
, 0) << "failed to decode_base64 policy" << dendl
;
1753 err_msg
= "Could not decode policy";
1757 decoded_policy
.append('\0'); // NULL terminate
1758 ldout(s
->cct
, 0) << "POST policy: " << decoded_policy
.c_str() << dendl
;
1761 int r
= post_policy
.from_json(decoded_policy
, err_msg
);
1763 if (err_msg
.empty()) {
1764 err_msg
= "Failed to parse policy";
1766 ldout(s
->cct
, 0) << "failed to parse policy" << dendl
;
1772 post_policy
.set_var_checked("x-amz-signature");
1775 post_policy
.set_var_checked("AWSAccessKeyId");
1776 post_policy
.set_var_checked("signature");
1778 post_policy
.set_var_checked("policy");
1780 r
= post_policy
.check(&env
, err_msg
);
1782 if (err_msg
.empty()) {
1783 err_msg
= "Policy check failed";
1785 ldout(s
->cct
, 0) << "policy check failed" << dendl
;
1790 ldout(s
->cct
, 0) << "No attached policy found!" << dendl
;
1794 part_str(parts
, "acl", &canned_acl
);
1796 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1797 ldout(s
->cct
, 20) << "canned_acl=" << canned_acl
<< dendl
;
1798 if (s3policy
.create_canned(s
->owner
, s
->bucket_owner
, canned_acl
) < 0) {
1799 err_msg
= "Bad canned ACLs";
1808 int RGWPostObj_ObjStore_S3::complete_get_params()
1812 struct post_form_part part
;
1813 int r
= read_form_part_header(&part
, done
);
1818 ceph::bufferlist part_data
;
1820 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1821 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
1822 if (r
< 0 || !boundary
) {
1826 /* Just reading the data but not storing any results of that. */
1832 int RGWPostObj_ObjStore_S3::get_data(ceph::bufferlist
& bl
, bool& again
)
1837 const uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1838 int r
= read_data(bl
, chunk_size
, boundary
, done
);
1845 /* Reached end of data, let's drain the rest of the params */
1846 r
= complete_get_params();
1857 void RGWPostObj_ObjStore_S3::send_response()
1859 if (op_ret
== 0 && parts
.count("success_action_redirect")) {
1862 part_str(parts
, "success_action_redirect", &redirect
);
1867 string etag_str
= "\"";
1869 etag_str
.append(etag
);
1870 etag_str
.append("\"");
1874 url_encode(s
->bucket_tenant
, tenant
); /* surely overkill, but cheap */
1875 url_encode(s
->bucket_name
, bucket
);
1876 url_encode(s
->object
.name
, key
);
1877 url_encode(etag_str
, etag_url
);
1879 if (!s
->bucket_tenant
.empty()) {
1881 * What we really would like is to quaily the bucket name, so
1882 * that the client could simply copy it and paste into next request.
1883 * Unfortunately, in S3 we cannot know if the client will decide
1884 * to come through DNS, with "bucket.tenant" sytanx, or through
1885 * URL with "tenant\bucket" syntax. Therefore, we provide the
1886 * tenant separately.
1888 redirect
.append("?tenant=");
1889 redirect
.append(tenant
);
1890 redirect
.append("&bucket=");
1891 redirect
.append(bucket
);
1893 redirect
.append("?bucket=");
1894 redirect
.append(bucket
);
1896 redirect
.append("&key=");
1897 redirect
.append(key
);
1898 redirect
.append("&etag=");
1899 redirect
.append(etag_url
);
1901 int r
= check_utf8(redirect
.c_str(), redirect
.size());
1906 dump_redirect(s
, redirect
);
1907 op_ret
= STATUS_REDIRECT
;
1908 } else if (op_ret
== 0 && parts
.count("success_action_status")) {
1909 string status_string
;
1910 uint32_t status_int
;
1912 part_str(parts
, "success_action_status", &status_string
);
1914 int r
= stringtoul(status_string
, &status_int
);
1920 switch (status_int
) {
1924 op_ret
= STATUS_CREATED
;
1927 op_ret
= STATUS_NO_CONTENT
;
1930 } else if (! op_ret
) {
1931 op_ret
= STATUS_NO_CONTENT
;
1935 if (op_ret
== STATUS_CREATED
) {
1936 for (auto &it
: crypt_http_responses
)
1937 dump_header(s
, it
.first
, it
.second
);
1938 s
->formatter
->open_object_section("PostResponse");
1939 if (g_conf
->rgw_dns_name
.length())
1940 s
->formatter
->dump_format("Location", "%s/%s",
1941 s
->info
.script_uri
.c_str(),
1942 s
->object
.name
.c_str());
1943 if (!s
->bucket_tenant
.empty())
1944 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
1945 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
1946 s
->formatter
->dump_string("Key", s
->object
.name
);
1947 s
->formatter
->close_section();
1949 s
->err
.message
= err_msg
;
1950 set_req_state_err(s
, op_ret
);
1953 dump_content_length(s
, s
->formatter
->get_len());
1955 end_header(s
, this);
1956 if (op_ret
!= STATUS_CREATED
)
1959 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1962 int RGWPostObj_ObjStore_S3::get_encrypt_filter(
1963 std::unique_ptr
<RGWPutObjDataProcessor
>* filter
, RGWPutObjDataProcessor
* cb
)
1966 std::unique_ptr
<BlockCrypt
> block_crypt
;
1967 res
= rgw_s3_prepare_encrypt(s
, attrs
, &parts
, &block_crypt
, crypt_http_responses
);
1968 if (res
== 0 && block_crypt
!= nullptr) {
1969 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
1970 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1977 int RGWDeleteObj_ObjStore_S3::get_params()
1979 const char *if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_DELETE_IF_UNMODIFIED_SINCE");
1981 if (s
->system_request
) {
1982 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"no-precondition-error", &no_precondition_error
, false);
1986 std::string if_unmod_decoded
= url_decode(if_unmod
);
1989 if (utime_t::parse_date(if_unmod_decoded
, &epoch
, &nsec
) < 0) {
1990 ldout(s
->cct
, 10) << "failed to parse time: " << if_unmod_decoded
<< dendl
;
1993 unmod_since
= utime_t(epoch
, nsec
).to_real_time();
1999 void RGWDeleteObj_ObjStore_S3::send_response()
2005 r
= STATUS_NO_CONTENT
;
2007 set_req_state_err(s
, r
);
2009 if (!version_id
.empty()) {
2010 dump_header(s
, "x-amz-version-id", version_id
);
2012 if (delete_marker
) {
2013 dump_header(s
, "x-amz-delete-marker", "true");
2015 end_header(s
, this);
2018 int RGWCopyObj_ObjStore_S3::init_dest_policy()
2020 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2022 /* build a policy for the target object */
2023 int r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
2027 dest_policy
= s3policy
;
2032 int RGWCopyObj_ObjStore_S3::get_params()
2034 if_mod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MODIFIED_SINCE");
2035 if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_UNMODIFIED_SINCE");
2036 if_match
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MATCH");
2037 if_nomatch
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_NONE_MATCH");
2039 src_tenant_name
= s
->src_tenant_name
;
2040 src_bucket_name
= s
->src_bucket_name
;
2041 src_object
= s
->src_object
;
2042 dest_tenant_name
= s
->bucket
.tenant
;
2043 dest_bucket_name
= s
->bucket
.name
;
2044 dest_object
= s
->object
.name
;
2046 if (s
->system_request
) {
2047 source_zone
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"source-zone");
2048 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"copy-if-newer", ©_if_newer
, false);
2049 if (!source_zone
.empty()) {
2050 client_id
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"client-id");
2051 op_id
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"op-id");
2053 if (client_id
.empty() || op_id
.empty()) {
2055 RGW_SYS_PARAM_PREFIX
"client-id or "
2056 RGW_SYS_PARAM_PREFIX
"op-id were not provided, "
2057 "required for intra-region copy"
2064 const char *md_directive
= s
->info
.env
->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
2066 if (strcasecmp(md_directive
, "COPY") == 0) {
2067 attrs_mod
= RGWRados::ATTRSMOD_NONE
;
2068 } else if (strcasecmp(md_directive
, "REPLACE") == 0) {
2069 attrs_mod
= RGWRados::ATTRSMOD_REPLACE
;
2070 } else if (!source_zone
.empty()) {
2071 attrs_mod
= RGWRados::ATTRSMOD_NONE
; // default for intra-zone_group copy
2073 ldout(s
->cct
, 0) << "invalid metadata directive" << dendl
;
2078 if (source_zone
.empty() &&
2079 (dest_tenant_name
.compare(src_tenant_name
) == 0) &&
2080 (dest_bucket_name
.compare(src_bucket_name
) == 0) &&
2081 (dest_object
.compare(src_object
.name
) == 0) &&
2082 src_object
.instance
.empty() &&
2083 (attrs_mod
!= RGWRados::ATTRSMOD_REPLACE
)) {
2084 /* can only copy object into itself if replacing attrs */
2085 ldout(s
->cct
, 0) << "can't copy object into itself if not replacing attrs"
2087 return -ERR_INVALID_REQUEST
;
2092 void RGWCopyObj_ObjStore_S3::send_partial_response(off_t ofs
)
2094 if (! sent_header
) {
2096 set_req_state_err(s
, op_ret
);
2099 end_header(s
, this, "application/xml");
2101 s
->formatter
->open_object_section_in_ns("CopyObjectResult", XMLNS_AWS_S3
);
2105 /* Send progress field. Note that this diverge from the original S3
2106 * spec. We do this in order to keep connection alive.
2108 s
->formatter
->dump_int("Progress", (uint64_t)ofs
);
2110 rgw_flush_formatter(s
, s
->formatter
);
2113 void RGWCopyObj_ObjStore_S3::send_response()
2116 send_partial_response(0);
2119 dump_time(s
, "LastModified", &mtime
);
2120 std::string etag_str
= etag
.to_str();
2121 if (! etag_str
.empty()) {
2122 s
->formatter
->dump_string("ETag", std::move(etag_str
));
2124 s
->formatter
->close_section();
2125 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2129 void RGWGetACLs_ObjStore_S3::send_response()
2132 set_req_state_err(s
, op_ret
);
2134 end_header(s
, this, "application/xml");
2136 rgw_flush_formatter(s
, s
->formatter
);
2140 int RGWPutACLs_ObjStore_S3::get_params()
2142 int ret
= RGWPutACLs_ObjStore::get_params();
2144 const int ret_auth
= do_aws4_auth_completion();
2152 int RGWPutACLs_ObjStore_S3::get_policy_from_state(RGWRados
*store
,
2153 struct req_state
*s
,
2156 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2158 // bucket-* canned acls do not apply to bucket
2159 if (s
->object
.empty()) {
2160 if (s
->canned_acl
.find("bucket") != string::npos
)
2161 s
->canned_acl
.clear();
2164 int r
= create_s3_policy(s
, store
, s3policy
, owner
);
2168 s3policy
.to_xml(ss
);
2173 void RGWPutACLs_ObjStore_S3::send_response()
2176 set_req_state_err(s
, op_ret
);
2178 end_header(s
, this, "application/xml");
2182 void RGWGetLC_ObjStore_S3::execute()
2184 config
.set_ctx(s
->cct
);
2186 map
<string
, bufferlist
>::iterator aiter
= s
->bucket_attrs
.find(RGW_ATTR_LC
);
2187 if (aiter
== s
->bucket_attrs
.end()) {
2192 bufferlist::iterator
iter(&aiter
->second
);
2194 config
.decode(iter
);
2195 } catch (const buffer::error
& e
) {
2196 ldout(s
->cct
, 0) << __func__
<< "decode life cycle config failed" << dendl
;
2202 void RGWGetLC_ObjStore_S3::send_response()
2205 if (op_ret
== -ENOENT
) {
2206 set_req_state_err(s
, ERR_NO_SUCH_LC
);
2208 set_req_state_err(s
, op_ret
);
2212 end_header(s
, this, "application/xml");
2218 config
.dump_xml(s
->formatter
);
2219 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2222 void RGWPutLC_ObjStore_S3::send_response()
2225 set_req_state_err(s
, op_ret
);
2227 end_header(s
, this, "application/xml");
2231 void RGWDeleteLC_ObjStore_S3::send_response()
2234 op_ret
= STATUS_NO_CONTENT
;
2236 set_req_state_err(s
, op_ret
);
2239 end_header(s
, this, "application/xml");
2243 void RGWGetCORS_ObjStore_S3::send_response()
2246 if (op_ret
== -ENOENT
)
2247 set_req_state_err(s
, ERR_NOT_FOUND
);
2249 set_req_state_err(s
, op_ret
);
2252 end_header(s
, NULL
, "application/xml");
2256 RGWCORSConfiguration_S3
*s3cors
=
2257 static_cast<RGWCORSConfiguration_S3
*>(&bucket_cors
);
2266 int RGWPutCORS_ObjStore_S3::get_params()
2269 char *data
= nullptr;
2271 RGWCORSXMLParser_S3
parser(s
->cct
);
2272 RGWCORSConfiguration_S3
*cors_config
;
2274 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2275 r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
2280 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
2282 r
= do_aws4_auth_completion();
2287 if (!parser
.init()) {
2291 if (!data
|| !parser
.parse(data
, len
, 1)) {
2295 static_cast<RGWCORSConfiguration_S3
*>(parser
.find_first(
2296 "CORSConfiguration"));
2301 // forward bucket cors requests to meta master zone
2302 if (!store
->is_meta_master()) {
2303 /* only need to keep this data around if we're not meta master */
2304 in_data
.append(data
, len
);
2307 if (s
->cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 15)) {
2308 ldout(s
->cct
, 15) << "CORSConfiguration";
2309 cors_config
->to_xml(*_dout
);
2313 cors_config
->encode(cors_bl
);
2318 void RGWPutCORS_ObjStore_S3::send_response()
2321 set_req_state_err(s
, op_ret
);
2323 end_header(s
, NULL
, "application/xml");
2327 void RGWDeleteCORS_ObjStore_S3::send_response()
2330 if (!r
|| r
== -ENOENT
)
2331 r
= STATUS_NO_CONTENT
;
2333 set_req_state_err(s
, r
);
2335 end_header(s
, NULL
);
2338 void RGWOptionsCORS_ObjStore_S3::send_response()
2340 string hdrs
, exp_hdrs
;
2341 uint32_t max_age
= CORS_MAX_AGE_INVALID
;
2342 /*EACCES means, there is no CORS registered yet for the bucket
2343 *ENOENT means, there is no match of the Origin in the list of CORSRule
2345 if (op_ret
== -ENOENT
)
2348 set_req_state_err(s
, op_ret
);
2350 end_header(s
, NULL
);
2353 get_response_params(hdrs
, exp_hdrs
, &max_age
);
2356 dump_access_control(s
, origin
, req_meth
, hdrs
.c_str(), exp_hdrs
.c_str(),
2358 end_header(s
, NULL
);
2361 void RGWGetRequestPayment_ObjStore_S3::send_response()
2364 end_header(s
, this, "application/xml");
2367 s
->formatter
->open_object_section_in_ns("RequestPaymentConfiguration", XMLNS_AWS_S3
);
2368 const char *payer
= requester_pays
? "Requester" : "BucketOwner";
2369 s
->formatter
->dump_string("Payer", payer
);
2370 s
->formatter
->close_section();
2371 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2374 class RGWSetRequestPaymentParser
: public RGWXMLParser
2376 XMLObj
*alloc_obj(const char *el
) override
{
2381 RGWSetRequestPaymentParser() {}
2382 ~RGWSetRequestPaymentParser() override
{}
2384 int get_request_payment_payer(bool *requester_pays
) {
2385 XMLObj
*config
= find_first("RequestPaymentConfiguration");
2389 *requester_pays
= false;
2391 XMLObj
*field
= config
->find_first("Payer");
2395 string
& s
= field
->get_data();
2397 if (stringcasecmp(s
, "Requester") == 0) {
2398 *requester_pays
= true;
2399 } else if (stringcasecmp(s
, "BucketOwner") != 0) {
2407 int RGWSetRequestPayment_ObjStore_S3::get_params()
2411 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2412 int r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
2418 RGWSetRequestPaymentParser parser
;
2420 if (!parser
.init()) {
2421 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
2426 if (!parser
.parse(data
, len
, 1)) {
2427 ldout(s
->cct
, 10) << "failed to parse data: " << data
<< dendl
;
2432 r
= parser
.get_request_payment_payer(&requester_pays
);
2440 void RGWSetRequestPayment_ObjStore_S3::send_response()
2443 set_req_state_err(s
, op_ret
);
2448 int RGWInitMultipart_ObjStore_S3::get_params()
2450 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2451 op_ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
2460 void RGWInitMultipart_ObjStore_S3::send_response()
2463 set_req_state_err(s
, op_ret
);
2465 for (auto &it
: crypt_http_responses
)
2466 dump_header(s
, it
.first
, it
.second
);
2467 end_header(s
, this, "application/xml");
2470 s
->formatter
->open_object_section_in_ns("InitiateMultipartUploadResult", XMLNS_AWS_S3
);
2471 if (!s
->bucket_tenant
.empty())
2472 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2473 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2474 s
->formatter
->dump_string("Key", s
->object
.name
);
2475 s
->formatter
->dump_string("UploadId", upload_id
);
2476 s
->formatter
->close_section();
2477 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2481 int RGWInitMultipart_ObjStore_S3::prepare_encryption(map
<string
, bufferlist
>& attrs
)
2484 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, nullptr, crypt_http_responses
);
2488 int RGWCompleteMultipart_ObjStore_S3::get_params()
2490 int ret
= RGWCompleteMultipart_ObjStore::get_params();
2495 return do_aws4_auth_completion();
2498 void RGWCompleteMultipart_ObjStore_S3::send_response()
2501 set_req_state_err(s
, op_ret
);
2503 end_header(s
, this, "application/xml");
2506 s
->formatter
->open_object_section_in_ns("CompleteMultipartUploadResult", XMLNS_AWS_S3
);
2507 if (!s
->bucket_tenant
.empty()) {
2508 if (s
->info
.domain
.length()) {
2509 s
->formatter
->dump_format("Location", "%s.%s.%s",
2510 s
->bucket_name
.c_str(),
2511 s
->bucket_tenant
.c_str(),
2512 s
->info
.domain
.c_str());
2514 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2516 if (s
->info
.domain
.length()) {
2517 s
->formatter
->dump_format("Location", "%s.%s",
2518 s
->bucket_name
.c_str(),
2519 s
->info
.domain
.c_str());
2522 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2523 s
->formatter
->dump_string("Key", s
->object
.name
);
2524 s
->formatter
->dump_string("ETag", etag
);
2525 s
->formatter
->close_section();
2526 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2530 void RGWAbortMultipart_ObjStore_S3::send_response()
2534 r
= STATUS_NO_CONTENT
;
2536 set_req_state_err(s
, r
);
2538 end_header(s
, this);
2541 void RGWListMultipart_ObjStore_S3::send_response()
2544 set_req_state_err(s
, op_ret
);
2546 end_header(s
, this, "application/xml");
2550 s
->formatter
->open_object_section_in_ns("ListPartsResult", XMLNS_AWS_S3
);
2551 map
<uint32_t, RGWUploadPartInfo
>::iterator iter
;
2552 map
<uint32_t, RGWUploadPartInfo
>::reverse_iterator test_iter
;
2555 iter
= parts
.begin();
2556 test_iter
= parts
.rbegin();
2557 if (test_iter
!= parts
.rend()) {
2558 cur_max
= test_iter
->first
;
2560 if (!s
->bucket_tenant
.empty())
2561 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2562 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2563 s
->formatter
->dump_string("Key", s
->object
.name
);
2564 s
->formatter
->dump_string("UploadId", upload_id
);
2565 s
->formatter
->dump_string("StorageClass", "STANDARD");
2566 s
->formatter
->dump_int("PartNumberMarker", marker
);
2567 s
->formatter
->dump_int("NextPartNumberMarker", cur_max
);
2568 s
->formatter
->dump_int("MaxParts", max_parts
);
2569 s
->formatter
->dump_string("IsTruncated", (truncated
? "true" : "false"));
2571 ACLOwner
& owner
= policy
.get_owner();
2572 dump_owner(s
, owner
.get_id(), owner
.get_display_name());
2574 for (; iter
!= parts
.end(); ++iter
) {
2575 RGWUploadPartInfo
& info
= iter
->second
;
2577 s
->formatter
->open_object_section("Part");
2579 dump_time(s
, "LastModified", &info
.modified
);
2581 s
->formatter
->dump_unsigned("PartNumber", info
.num
);
2582 s
->formatter
->dump_format("ETag", "\"%s\"", info
.etag
.c_str());
2583 s
->formatter
->dump_unsigned("Size", info
.accounted_size
);
2584 s
->formatter
->close_section();
2586 s
->formatter
->close_section();
2587 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2591 void RGWListBucketMultiparts_ObjStore_S3::send_response()
2594 set_req_state_err(s
, op_ret
);
2597 end_header(s
, this, "application/xml");
2602 s
->formatter
->open_object_section_in_ns("ListMultipartUploadsResult", XMLNS_AWS_S3
);
2603 if (!s
->bucket_tenant
.empty())
2604 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2605 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2606 if (!prefix
.empty())
2607 s
->formatter
->dump_string("ListMultipartUploadsResult.Prefix", prefix
);
2608 string
& key_marker
= marker
.get_key();
2609 if (!key_marker
.empty())
2610 s
->formatter
->dump_string("KeyMarker", key_marker
);
2611 string
& upload_id_marker
= marker
.get_upload_id();
2612 if (!upload_id_marker
.empty())
2613 s
->formatter
->dump_string("UploadIdMarker", upload_id_marker
);
2614 string next_key
= next_marker
.mp
.get_key();
2615 if (!next_key
.empty())
2616 s
->formatter
->dump_string("NextKeyMarker", next_key
);
2617 string next_upload_id
= next_marker
.mp
.get_upload_id();
2618 if (!next_upload_id
.empty())
2619 s
->formatter
->dump_string("NextUploadIdMarker", next_upload_id
);
2620 s
->formatter
->dump_int("MaxUploads", max_uploads
);
2621 if (!delimiter
.empty())
2622 s
->formatter
->dump_string("Delimiter", delimiter
);
2623 s
->formatter
->dump_string("IsTruncated", (is_truncated
? "true" : "false"));
2626 vector
<RGWMultipartUploadEntry
>::iterator iter
;
2627 for (iter
= uploads
.begin(); iter
!= uploads
.end(); ++iter
) {
2628 RGWMPObj
& mp
= iter
->mp
;
2629 s
->formatter
->open_array_section("Upload");
2630 s
->formatter
->dump_string("Key", mp
.get_key());
2631 s
->formatter
->dump_string("UploadId", mp
.get_upload_id());
2632 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
, "Initiator");
2633 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
);
2634 s
->formatter
->dump_string("StorageClass", "STANDARD");
2635 dump_time(s
, "Initiated", &iter
->obj
.meta
.mtime
);
2636 s
->formatter
->close_section();
2638 if (!common_prefixes
.empty()) {
2639 s
->formatter
->open_array_section("CommonPrefixes");
2640 map
<string
, bool>::iterator pref_iter
;
2641 for (pref_iter
= common_prefixes
.begin();
2642 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
2643 s
->formatter
->dump_string("CommonPrefixes.Prefix", pref_iter
->first
);
2645 s
->formatter
->close_section();
2648 s
->formatter
->close_section();
2649 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2652 int RGWDeleteMultiObj_ObjStore_S3::get_params()
2654 int ret
= RGWDeleteMultiObj_ObjStore::get_params();
2659 return do_aws4_auth_completion();
2662 void RGWDeleteMultiObj_ObjStore_S3::send_status()
2664 if (! status_dumped
) {
2666 set_req_state_err(s
, op_ret
);
2668 status_dumped
= true;
2672 void RGWDeleteMultiObj_ObjStore_S3::begin_response()
2675 if (!status_dumped
) {
2680 end_header(s
, this, "application/xml");
2681 s
->formatter
->open_object_section_in_ns("DeleteResult", XMLNS_AWS_S3
);
2683 rgw_flush_formatter(s
, s
->formatter
);
2686 void RGWDeleteMultiObj_ObjStore_S3::send_partial_response(rgw_obj_key
& key
,
2688 const string
& marker_version_id
, int ret
)
2691 if (op_ret
== 0 && !quiet
) {
2692 s
->formatter
->open_object_section("Deleted");
2693 s
->formatter
->dump_string("Key", key
.name
);
2694 if (!key
.instance
.empty()) {
2695 s
->formatter
->dump_string("VersionId", key
.instance
);
2697 if (delete_marker
) {
2698 s
->formatter
->dump_bool("DeleteMarker", true);
2699 s
->formatter
->dump_string("DeleteMarkerVersionId", marker_version_id
);
2701 s
->formatter
->close_section();
2702 } else if (op_ret
< 0) {
2703 struct rgw_http_error r
;
2706 s
->formatter
->open_object_section("Error");
2709 rgw_get_errno_s3(&r
, err_no
);
2711 s
->formatter
->dump_string("Key", key
.name
);
2712 s
->formatter
->dump_string("VersionId", key
.instance
);
2713 s
->formatter
->dump_int("Code", r
.http_ret
);
2714 s
->formatter
->dump_string("Message", r
.s3_code
);
2715 s
->formatter
->close_section();
2718 rgw_flush_formatter(s
, s
->formatter
);
2722 void RGWDeleteMultiObj_ObjStore_S3::end_response()
2725 s
->formatter
->close_section();
2726 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2729 void RGWGetObjLayout_ObjStore_S3::send_response()
2732 set_req_state_err(s
, op_ret
);
2734 end_header(s
, this, "application/json");
2742 f
.open_object_section("result");
2743 ::encode_json("head", head_obj
, &f
);
2744 ::encode_json("manifest", *manifest
, &f
);
2745 f
.open_array_section("data_location");
2746 for (auto miter
= manifest
->obj_begin(); miter
!= manifest
->obj_end(); ++miter
) {
2747 f
.open_object_section("obj");
2748 rgw_raw_obj raw_loc
= miter
.get_location().get_raw_obj(store
);
2749 ::encode_json("ofs", miter
.get_ofs(), &f
);
2750 ::encode_json("loc", raw_loc
, &f
);
2751 ::encode_json("loc_ofs", miter
.location_ofs(), &f
);
2752 ::encode_json("loc_size", miter
.get_stripe_size(), &f
);
2754 rgw_flush_formatter(s
, &f
);
2758 rgw_flush_formatter(s
, &f
);
2761 int RGWConfigBucketMetaSearch_ObjStore_S3::get_params()
2763 auto iter
= s
->info
.x_meta_map
.find("x-amz-meta-search");
2764 if (iter
== s
->info
.x_meta_map
.end()) {
2765 s
->err
.message
= "X-Rgw-Meta-Search header not provided";
2766 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2770 list
<string
> expressions
;
2771 get_str_list(iter
->second
, ",", expressions
);
2773 for (auto& expression
: expressions
) {
2774 vector
<string
> args
;
2775 get_str_vec(expression
, ";", args
);
2778 s
->err
.message
= "invalid empty expression";
2779 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2782 if (args
.size() > 2) {
2783 s
->err
.message
= string("invalid expression: ") + expression
;
2784 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2788 string key
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[0]));
2790 if (args
.size() > 1) {
2791 val
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[1]));
2794 if (!boost::algorithm::starts_with(key
, RGW_AMZ_META_PREFIX
)) {
2795 s
->err
.message
= string("invalid expression, key must start with '" RGW_AMZ_META_PREFIX
"' : ") + expression
;
2796 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2800 key
= key
.substr(sizeof(RGW_AMZ_META_PREFIX
) - 1);
2802 ESEntityTypeMap::EntityType entity_type
;
2804 if (val
.empty() || val
== "str" || val
== "string") {
2805 entity_type
= ESEntityTypeMap::ES_ENTITY_STR
;
2806 } else if (val
== "int" || val
== "integer") {
2807 entity_type
= ESEntityTypeMap::ES_ENTITY_INT
;
2808 } else if (val
== "date" || val
== "datetime") {
2809 entity_type
= ESEntityTypeMap::ES_ENTITY_DATE
;
2811 s
->err
.message
= string("invalid entity type: ") + val
;
2812 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2816 mdsearch_config
[key
] = entity_type
;
2822 void RGWConfigBucketMetaSearch_ObjStore_S3::send_response()
2825 set_req_state_err(s
, op_ret
);
2827 end_header(s
, this);
2830 void RGWGetBucketMetaSearch_ObjStore_S3::send_response()
2833 set_req_state_err(s
, op_ret
);
2835 end_header(s
, NULL
, "application/xml");
2837 Formatter
*f
= s
->formatter
;
2838 f
->open_array_section("GetBucketMetaSearchResult");
2839 for (auto& e
: s
->bucket_info
.mdsearch_config
) {
2840 f
->open_object_section("Entry");
2841 string k
= string("x-amz-meta-") + e
.first
;
2842 f
->dump_string("Key", k
.c_str());
2845 case ESEntityTypeMap::ES_ENTITY_INT
:
2848 case ESEntityTypeMap::ES_ENTITY_DATE
:
2854 f
->dump_string("Type", type
);
2858 rgw_flush_formatter(s
, f
);
2861 void RGWDelBucketMetaSearch_ObjStore_S3::send_response()
2864 set_req_state_err(s
, op_ret
);
2866 end_header(s
, this);
2870 RGWOp
*RGWHandler_REST_Service_S3::op_get()
2872 if (is_usage_op()) {
2873 return new RGWGetUsage_ObjStore_S3
;
2875 return new RGWListBuckets_ObjStore_S3
;
2879 RGWOp
*RGWHandler_REST_Service_S3::op_head()
2881 return new RGWListBuckets_ObjStore_S3
;
2884 RGWOp
*RGWHandler_REST_Service_S3::op_post()
2886 if (s
->info
.args
.exists("Action")) {
2887 string action
= s
->info
.args
.get("Action");
2888 if (action
.compare("CreateRole") == 0)
2889 return new RGWCreateRole
;
2890 if (action
.compare("DeleteRole") == 0)
2891 return new RGWDeleteRole
;
2892 if (action
.compare("GetRole") == 0)
2893 return new RGWGetRole
;
2894 if (action
.compare("UpdateAssumeRolePolicy") == 0)
2895 return new RGWModifyRole
;
2896 if (action
.compare("ListRoles") == 0)
2897 return new RGWListRoles
;
2898 if (action
.compare("PutRolePolicy") == 0)
2899 return new RGWPutRolePolicy
;
2900 if (action
.compare("GetRolePolicy") == 0)
2901 return new RGWGetRolePolicy
;
2902 if (action
.compare("ListRolePolicies") == 0)
2903 return new RGWListRolePolicies
;
2904 if (action
.compare("DeleteRolePolicy") == 0)
2905 return new RGWDeleteRolePolicy
;
2910 RGWOp
*RGWHandler_REST_Bucket_S3::get_obj_op(bool get_data
)
2914 return new RGWListBucket_ObjStore_S3
;
2916 return new RGWStatBucket_ObjStore_S3
;
2920 RGWOp
*RGWHandler_REST_Bucket_S3::op_get()
2922 if (s
->info
.args
.sub_resource_exists("logging"))
2923 return new RGWGetBucketLogging_ObjStore_S3
;
2925 if (s
->info
.args
.sub_resource_exists("location"))
2926 return new RGWGetBucketLocation_ObjStore_S3
;
2928 if (s
->info
.args
.sub_resource_exists("versioning"))
2929 return new RGWGetBucketVersioning_ObjStore_S3
;
2931 if (s
->info
.args
.sub_resource_exists("website")) {
2932 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
2935 return new RGWGetBucketWebsite_ObjStore_S3
;
2938 if (s
->info
.args
.exists("mdsearch")) {
2939 return new RGWGetBucketMetaSearch_ObjStore_S3
;
2943 return new RGWGetACLs_ObjStore_S3
;
2944 } else if (is_cors_op()) {
2945 return new RGWGetCORS_ObjStore_S3
;
2946 } else if (is_request_payment_op()) {
2947 return new RGWGetRequestPayment_ObjStore_S3
;
2948 } else if (s
->info
.args
.exists("uploads")) {
2949 return new RGWListBucketMultiparts_ObjStore_S3
;
2950 } else if(is_lc_op()) {
2951 return new RGWGetLC_ObjStore_S3
;
2952 } else if(is_policy_op()) {
2953 return new RGWGetBucketPolicy
;
2955 return get_obj_op(true);
2958 RGWOp
*RGWHandler_REST_Bucket_S3::op_head()
2961 return new RGWGetACLs_ObjStore_S3
;
2962 } else if (s
->info
.args
.exists("uploads")) {
2963 return new RGWListBucketMultiparts_ObjStore_S3
;
2965 return get_obj_op(false);
2968 RGWOp
*RGWHandler_REST_Bucket_S3::op_put()
2970 if (s
->info
.args
.sub_resource_exists("logging"))
2972 if (s
->info
.args
.sub_resource_exists("versioning"))
2973 return new RGWSetBucketVersioning_ObjStore_S3
;
2974 if (s
->info
.args
.sub_resource_exists("website")) {
2975 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
2978 return new RGWSetBucketWebsite_ObjStore_S3
;
2981 return new RGWPutACLs_ObjStore_S3
;
2982 } else if (is_cors_op()) {
2983 return new RGWPutCORS_ObjStore_S3
;
2984 } else if (is_request_payment_op()) {
2985 return new RGWSetRequestPayment_ObjStore_S3
;
2986 } else if(is_lc_op()) {
2987 return new RGWPutLC_ObjStore_S3
;
2988 } else if(is_policy_op()) {
2989 return new RGWPutBucketPolicy
;
2991 return new RGWCreateBucket_ObjStore_S3
;
2994 RGWOp
*RGWHandler_REST_Bucket_S3::op_delete()
2997 return new RGWDeleteCORS_ObjStore_S3
;
2998 } else if(is_lc_op()) {
2999 return new RGWDeleteLC_ObjStore_S3
;
3000 } else if(is_policy_op()) {
3001 return new RGWDeleteBucketPolicy
;
3004 if (s
->info
.args
.sub_resource_exists("website")) {
3005 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
3008 return new RGWDeleteBucketWebsite_ObjStore_S3
;
3011 if (s
->info
.args
.exists("mdsearch")) {
3012 return new RGWDelBucketMetaSearch_ObjStore_S3
;
3015 return new RGWDeleteBucket_ObjStore_S3
;
3018 RGWOp
*RGWHandler_REST_Bucket_S3::op_post()
3020 if (s
->info
.args
.exists("delete")) {
3021 return new RGWDeleteMultiObj_ObjStore_S3
;
3024 if (s
->info
.args
.exists("mdsearch")) {
3025 return new RGWConfigBucketMetaSearch_ObjStore_S3
;
3028 return new RGWPostObj_ObjStore_S3
;
3031 RGWOp
*RGWHandler_REST_Bucket_S3::op_options()
3033 return new RGWOptionsCORS_ObjStore_S3
;
3036 RGWOp
*RGWHandler_REST_Obj_S3::get_obj_op(bool get_data
)
3039 return new RGWGetACLs_ObjStore_S3
;
3041 RGWGetObj_ObjStore_S3
*get_obj_op
= new RGWGetObj_ObjStore_S3
;
3042 get_obj_op
->set_get_data(get_data
);
3046 RGWOp
*RGWHandler_REST_Obj_S3::op_get()
3049 return new RGWGetACLs_ObjStore_S3
;
3050 } else if (s
->info
.args
.exists("uploadId")) {
3051 return new RGWListMultipart_ObjStore_S3
;
3052 } else if (s
->info
.args
.exists("layout")) {
3053 return new RGWGetObjLayout_ObjStore_S3
;
3054 } else if (is_tagging_op()) {
3055 return new RGWGetObjTags_ObjStore_S3
;
3057 return get_obj_op(true);
3060 RGWOp
*RGWHandler_REST_Obj_S3::op_head()
3063 return new RGWGetACLs_ObjStore_S3
;
3064 } else if (s
->info
.args
.exists("uploadId")) {
3065 return new RGWListMultipart_ObjStore_S3
;
3067 return get_obj_op(false);
3070 RGWOp
*RGWHandler_REST_Obj_S3::op_put()
3073 return new RGWPutACLs_ObjStore_S3
;
3074 } else if (is_tagging_op()) {
3075 return new RGWPutObjTags_ObjStore_S3
;
3078 if (s
->init_state
.src_bucket
.empty())
3079 return new RGWPutObj_ObjStore_S3
;
3081 return new RGWCopyObj_ObjStore_S3
;
3084 RGWOp
*RGWHandler_REST_Obj_S3::op_delete()
3086 if (is_tagging_op()) {
3087 return new RGWDeleteObjTags_ObjStore_S3
;
3089 string upload_id
= s
->info
.args
.get("uploadId");
3091 if (upload_id
.empty())
3092 return new RGWDeleteObj_ObjStore_S3
;
3094 return new RGWAbortMultipart_ObjStore_S3
;
3097 RGWOp
*RGWHandler_REST_Obj_S3::op_post()
3099 if (s
->info
.args
.exists("uploadId"))
3100 return new RGWCompleteMultipart_ObjStore_S3
;
3102 if (s
->info
.args
.exists("uploads"))
3103 return new RGWInitMultipart_ObjStore_S3
;
3105 return new RGWPostObj_ObjStore_S3
;
3108 RGWOp
*RGWHandler_REST_Obj_S3::op_options()
3110 return new RGWOptionsCORS_ObjStore_S3
;
3113 int RGWHandler_REST_S3::init_from_header(struct req_state
* s
,
3114 int default_formatter
,
3115 bool configurable_format
)
3120 const char *req_name
= s
->relative_uri
.c_str();
3123 if (*req_name
== '?') {
3126 p
= s
->info
.request_params
.c_str();
3129 s
->info
.args
.set(p
);
3130 s
->info
.args
.parse();
3132 /* must be called after the args parsing */
3133 int ret
= allocate_formatter(s
, default_formatter
, configurable_format
);
3137 if (*req_name
!= '/')
3146 int pos
= req
.find('/');
3148 first
= req
.substr(0, pos
);
3154 * XXX The intent of the check for empty is apparently to let the bucket
3155 * name from DNS to be set ahead. However, we currently take the DNS
3156 * bucket and re-insert it into URL in rgw_rest.cc:RGWREST::preprocess().
3157 * So, this check is meaningless.
3159 * Rather than dropping this, the code needs to be changed into putting
3160 * the bucket (and its tenant) from DNS and Host: header (HTTP_HOST)
3161 * into req_status.bucket_name directly.
3163 if (s
->init_state
.url_bucket
.empty()) {
3164 // Save bucket to tide us over until token is parsed.
3165 s
->init_state
.url_bucket
= first
;
3167 string encoded_obj_str
= req
.substr(pos
+1);
3168 s
->object
= rgw_obj_key(encoded_obj_str
, s
->info
.args
.get("versionId"));
3171 s
->object
= rgw_obj_key(req_name
, s
->info
.args
.get("versionId"));
3176 int RGWHandler_REST_S3::postauth_init()
3178 struct req_init_state
*t
= &s
->init_state
;
3179 bool relaxed_names
= s
->cct
->_conf
->rgw_relaxed_s3_bucket_names
;
3181 rgw_parse_url_bucket(t
->url_bucket
, s
->user
->user_id
.tenant
,
3182 s
->bucket_tenant
, s
->bucket_name
);
3184 dout(10) << "s->object=" << (!s
->object
.empty() ? s
->object
: rgw_obj_key("<NULL>"))
3185 << " s->bucket=" << rgw_make_bucket_entry_name(s
->bucket_tenant
, s
->bucket_name
) << dendl
;
3188 ret
= validate_tenant_name(s
->bucket_tenant
);
3191 if (!s
->bucket_name
.empty()) {
3192 ret
= valid_s3_bucket_name(s
->bucket_name
, relaxed_names
);
3195 ret
= validate_object_name(s
->object
.name
);
3200 if (!t
->src_bucket
.empty()) {
3201 rgw_parse_url_bucket(t
->src_bucket
, s
->user
->user_id
.tenant
,
3202 s
->src_tenant_name
, s
->src_bucket_name
);
3203 ret
= validate_tenant_name(s
->src_tenant_name
);
3206 ret
= valid_s3_bucket_name(s
->src_bucket_name
, relaxed_names
);
3213 int RGWHandler_REST_S3::init(RGWRados
*store
, struct req_state
*s
,
3214 rgw::io::BasicClient
*cio
)
3220 ret
= validate_tenant_name(s
->bucket_tenant
);
3223 bool relaxed_names
= s
->cct
->_conf
->rgw_relaxed_s3_bucket_names
;
3224 if (!s
->bucket_name
.empty()) {
3225 ret
= valid_s3_bucket_name(s
->bucket_name
, relaxed_names
);
3228 ret
= validate_object_name(s
->object
.name
);
3233 const char *cacl
= s
->info
.env
->get("HTTP_X_AMZ_ACL");
3235 s
->canned_acl
= cacl
;
3237 s
->has_acl_header
= s
->info
.env
->exists_prefix("HTTP_X_AMZ_GRANT");
3239 const char *copy_source
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE");
3241 if (copy_source
&& !s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE")) {
3242 ret
= RGWCopyObj::parse_copy_location(copy_source
,
3243 s
->init_state
.src_bucket
,
3246 ldout(s
->cct
, 0) << "failed to parse copy location" << dendl
;
3247 return -EINVAL
; // XXX why not -ERR_INVALID_BUCKET_NAME or -ERR_BAD_URL?
3251 return RGWHandler_REST::init(store
, s
, cio
);
3254 enum class AwsVersion
{
3260 enum class AwsRoute
{
3266 static inline std::pair
<AwsVersion
, AwsRoute
>
3267 discover_aws_flavour(const req_info
& info
)
3269 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
3271 AwsVersion version
= AwsVersion::UNKOWN
;
3272 AwsRoute route
= AwsRoute::UNKOWN
;
3274 const char* http_auth
= info
.env
->get("HTTP_AUTHORIZATION");
3275 if (http_auth
&& http_auth
[0]) {
3276 /* Authorization in Header */
3277 route
= AwsRoute::HEADERS
;
3279 if (!strncmp(http_auth
, AWS4_HMAC_SHA256_STR
,
3280 strlen(AWS4_HMAC_SHA256_STR
))) {
3282 version
= AwsVersion::V4
;
3283 } else if (!strncmp(http_auth
, "AWS ", 4)) {
3285 version
= AwsVersion::V2
;
3288 route
= AwsRoute::QUERY_STRING
;
3290 if (info
.args
.get("X-Amz-Algorithm") == AWS4_HMAC_SHA256_STR
) {
3292 version
= AwsVersion::V4
;
3293 } else if (!info
.args
.get("AWSAccessKeyId").empty()) {
3295 version
= AwsVersion::V2
;
3299 return std::make_pair(version
, route
);
3302 static void init_anon_user(struct req_state
*s
)
3304 rgw_get_anon_user(*(s
->user
));
3305 s
->perm_mask
= RGW_PERM_FULL_CONTROL
;
3309 * verify that a signed request comes from the keyholder
3310 * by checking the signature against our locally-computed version
3312 * it tries AWS v4 before AWS v2
3314 int RGW_Auth_S3::authorize(RGWRados
* const store
,
3315 const rgw::auth::StrategyRegistry
& auth_registry
,
3316 struct req_state
* const s
)
3319 /* neither keystone and rados enabled; warn and exit! */
3320 if (!store
->ctx()->_conf
->rgw_s3_auth_use_rados
&&
3321 !store
->ctx()->_conf
->rgw_s3_auth_use_keystone
&&
3322 !store
->ctx()->_conf
->rgw_s3_auth_use_ldap
) {
3323 dout(0) << "WARNING: no authorization backend enabled! Users will never authenticate." << dendl
;
3327 if (s
->op
== OP_OPTIONS
) {
3334 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
3336 if (route
== AwsRoute::QUERY_STRING
&& version
== AwsVersion::UNKOWN
) {
3337 /* FIXME(rzarzynski): handle anon user. */
3338 init_anon_user(const_cast<req_state
*>(s
));
3342 return authorize_v2(store
, auth_registry
, s
);
3347 * handle v2 signatures
3349 int RGW_Auth_S3::authorize_v2(RGWRados
* const store
,
3350 const rgw::auth::StrategyRegistry
& auth_registry
,
3351 struct req_state
* const s
)
3353 const auto ret
= rgw::auth::Strategy::apply(auth_registry
.get_s3_main(), s
);
3355 /* Populate the owner info. */
3356 s
->owner
.set_id(s
->user
->user_id
);
3357 s
->owner
.set_name(s
->user
->display_name
);
3362 int RGWHandler_Auth_S3::init(RGWRados
*store
, struct req_state
*state
,
3363 rgw::io::BasicClient
*cio
)
3365 int ret
= RGWHandler_REST_S3::init_from_header(state
, RGW_FORMAT_JSON
,
3370 return RGWHandler_REST::init(store
, state
, cio
);
3373 RGWHandler_REST
* RGWRESTMgr_S3::get_handler(struct req_state
* const s
,
3374 const rgw::auth::StrategyRegistry
& auth_registry
,
3375 const std::string
& frontend_prefix
)
3377 bool is_s3website
= enable_s3website
&& (s
->prot_flags
& RGW_REST_WEBSITE
);
3379 RGWHandler_REST_S3::init_from_header(s
,
3380 is_s3website
? RGW_FORMAT_HTML
:
3381 RGW_FORMAT_XML
, true);
3385 RGWHandler_REST
* handler
;
3386 // TODO: Make this more readable
3388 if (s
->init_state
.url_bucket
.empty()) {
3389 handler
= new RGWHandler_REST_Service_S3Website(auth_registry
);
3390 } else if (s
->object
.empty()) {
3391 handler
= new RGWHandler_REST_Bucket_S3Website(auth_registry
);
3393 handler
= new RGWHandler_REST_Obj_S3Website(auth_registry
);
3396 if (s
->init_state
.url_bucket
.empty()) {
3397 handler
= new RGWHandler_REST_Service_S3(auth_registry
);
3398 } else if (s
->object
.empty()) {
3399 handler
= new RGWHandler_REST_Bucket_S3(auth_registry
);
3401 handler
= new RGWHandler_REST_Obj_S3(auth_registry
);
3405 ldout(s
->cct
, 20) << __func__
<< " handler=" << typeid(*handler
).name()
3410 bool RGWHandler_REST_S3Website::web_dir() const {
3411 std::string subdir_name
= url_decode(s
->object
.name
);
3413 if (subdir_name
.empty()) {
3415 } else if (subdir_name
.back() == '/') {
3416 subdir_name
.pop_back();
3419 rgw_obj
obj(s
->bucket
, subdir_name
);
3421 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
3422 obj_ctx
.obj
.set_atomic(obj
);
3423 obj_ctx
.obj
.set_prefetch_data(obj
);
3425 RGWObjState
* state
= nullptr;
3426 if (store
->get_obj_state(&obj_ctx
, s
->bucket_info
, obj
, &state
, false) < 0) {
3429 if (! state
->exists
) {
3432 return state
->exists
;
3435 int RGWHandler_REST_S3Website::retarget(RGWOp
* op
, RGWOp
** new_op
) {
3437 ldout(s
->cct
, 10) << __func__
<< "Starting retarget" << dendl
;
3439 if (!(s
->prot_flags
& RGW_REST_WEBSITE
))
3442 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
3443 int ret
= store
->get_bucket_info(obj_ctx
, s
->bucket_tenant
,
3444 s
->bucket_name
, s
->bucket_info
, NULL
,
3447 // TODO-FUTURE: if the bucket does not exist, maybe expose it here?
3448 return -ERR_NO_SUCH_BUCKET
;
3450 if (!s
->bucket_info
.has_website
) {
3451 // TODO-FUTURE: if the bucket has no WebsiteConfig, expose it here
3452 return -ERR_NO_SUCH_WEBSITE_CONFIGURATION
;
3455 rgw_obj_key new_obj
;
3456 s
->bucket_info
.website_conf
.get_effective_key(s
->object
.name
, &new_obj
.name
, web_dir());
3457 ldout(s
->cct
, 10) << "retarget get_effective_key " << s
->object
<< " -> "
3458 << new_obj
<< dendl
;
3460 RGWBWRoutingRule rrule
;
3461 bool should_redirect
=
3462 s
->bucket_info
.website_conf
.should_redirect(new_obj
.name
, 0, &rrule
);
3464 if (should_redirect
) {
3465 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
3466 const string
& protocol
=
3467 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
3468 int redirect_code
= 0;
3469 rrule
.apply_rule(protocol
, hostname
, s
->object
.name
, &s
->redirect
,
3471 // APply a custom HTTP response code
3472 if (redirect_code
> 0)
3473 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
3474 ldout(s
->cct
, 10) << "retarget redirect code=" << redirect_code
3475 << " proto+host:" << protocol
<< "://" << hostname
3476 << " -> " << s
->redirect
<< dendl
;
3477 return -ERR_WEBSITE_REDIRECT
;
3481 * FIXME: if s->object != new_obj, drop op and create a new op to handle
3482 * operation. Or remove this comment if it's not applicable anymore
3485 s
->object
= new_obj
;
3490 RGWOp
* RGWHandler_REST_S3Website::op_get()
3492 return get_obj_op(true);
3495 RGWOp
* RGWHandler_REST_S3Website::op_head()
3497 return get_obj_op(false);
3500 int RGWHandler_REST_S3Website::serve_errordoc(int http_ret
, const string
& errordoc_key
) {
3502 s
->formatter
->reset(); /* Try to throw it all away */
3504 std::shared_ptr
<RGWGetObj_ObjStore_S3Website
> getop( static_cast<RGWGetObj_ObjStore_S3Website
*>(op_get()));
3505 if (getop
.get() == NULL
) {
3506 return -1; // Trigger double error handler
3508 getop
->init(store
, s
, this);
3509 getop
->range_str
= NULL
;
3510 getop
->if_mod
= NULL
;
3511 getop
->if_unmod
= NULL
;
3512 getop
->if_match
= NULL
;
3513 getop
->if_nomatch
= NULL
;
3514 s
->object
= errordoc_key
;
3516 ret
= init_permissions(getop
.get());
3518 ldout(s
->cct
, 20) << "serve_errordoc failed, init_permissions ret=" << ret
<< dendl
;
3519 return -1; // Trigger double error handler
3522 ret
= read_permissions(getop
.get());
3524 ldout(s
->cct
, 20) << "serve_errordoc failed, read_permissions ret=" << ret
<< dendl
;
3525 return -1; // Trigger double error handler
3529 getop
->set_custom_http_response(http_ret
);
3532 ret
= getop
->init_processing();
3534 ldout(s
->cct
, 20) << "serve_errordoc failed, init_processing ret=" << ret
<< dendl
;
3535 return -1; // Trigger double error handler
3538 ret
= getop
->verify_op_mask();
3540 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_op_mask ret=" << ret
<< dendl
;
3541 return -1; // Trigger double error handler
3544 ret
= getop
->verify_permission();
3546 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_permission ret=" << ret
<< dendl
;
3547 return -1; // Trigger double error handler
3550 ret
= getop
->verify_params();
3552 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_params ret=" << ret
<< dendl
;
3553 return -1; // Trigger double error handler
3556 // No going back now
3559 * FIXME Missing headers:
3560 * With a working errordoc, the s3 error fields are rendered as HTTP headers,
3561 * x-amz-error-code: NoSuchKey
3562 * x-amz-error-message: The specified key does not exist.
3563 * x-amz-error-detail-Key: foo
3571 int RGWHandler_REST_S3Website::error_handler(int err_no
,
3572 string
* error_content
) {
3573 int new_err_no
= -1;
3574 rgw_http_errors::const_iterator r
= rgw_http_s3_errors
.find(err_no
> 0 ? err_no
: -err_no
);
3575 int http_error_code
= -1;
3577 if (r
!= rgw_http_s3_errors
.end()) {
3578 http_error_code
= r
->second
.first
;
3580 ldout(s
->cct
, 10) << "RGWHandler_REST_S3Website::error_handler err_no=" << err_no
<< " http_ret=" << http_error_code
<< dendl
;
3582 RGWBWRoutingRule rrule
;
3583 bool should_redirect
=
3584 s
->bucket_info
.website_conf
.should_redirect(s
->object
.name
, http_error_code
,
3587 if (should_redirect
) {
3588 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
3589 const string
& protocol
=
3590 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
3591 int redirect_code
= 0;
3592 rrule
.apply_rule(protocol
, hostname
, s
->object
.name
, &s
->redirect
,
3594 // Apply a custom HTTP response code
3595 if (redirect_code
> 0)
3596 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
3597 ldout(s
->cct
, 10) << "error handler redirect code=" << redirect_code
3598 << " proto+host:" << protocol
<< "://" << hostname
3599 << " -> " << s
->redirect
<< dendl
;
3600 return -ERR_WEBSITE_REDIRECT
;
3601 } else if (err_no
== -ERR_WEBSITE_REDIRECT
) {
3602 // Do nothing here, this redirect will be handled in abort_early's ERR_WEBSITE_REDIRECT block
3603 // Do NOT fire the ErrorDoc handler
3604 } else if (!s
->bucket_info
.website_conf
.error_doc
.empty()) {
3605 /* This serves an entire page!
3606 On success, it will return zero, and no further content should be sent to the socket
3607 On failure, we need the double-error handler
3609 new_err_no
= RGWHandler_REST_S3Website::serve_errordoc(http_error_code
, s
->bucket_info
.website_conf
.error_doc
);
3610 if (new_err_no
&& new_err_no
!= -1) {
3611 err_no
= new_err_no
;
3614 ldout(s
->cct
, 20) << "No special error handling today!" << dendl
;
3620 RGWOp
* RGWHandler_REST_Obj_S3Website::get_obj_op(bool get_data
)
3622 /** If we are in website mode, then it is explicitly impossible to run GET or
3623 * HEAD on the actual directory. We must convert the request to run on the
3624 * suffix object instead!
3626 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3627 op
->set_get_data(get_data
);
3631 RGWOp
* RGWHandler_REST_Bucket_S3Website::get_obj_op(bool get_data
)
3633 /** If we are in website mode, then it is explicitly impossible to run GET or
3634 * HEAD on the actual directory. We must convert the request to run on the
3635 * suffix object instead!
3637 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3638 op
->set_get_data(get_data
);
3642 RGWOp
* RGWHandler_REST_Service_S3Website::get_obj_op(bool get_data
)
3644 /** If we are in website mode, then it is explicitly impossible to run GET or
3645 * HEAD on the actual directory. We must convert the request to run on the
3646 * suffix object instead!
3648 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3649 op
->set_get_data(get_data
);
3658 bool AWSGeneralAbstractor::is_time_skew_ok(const utime_t
& header_time
,
3659 const bool qsr
) const
3661 /* Check for time skew first. */
3662 const time_t req_sec
= header_time
.sec();
3666 if ((req_sec
< now
- RGW_AUTH_GRACE_MINS
* 60 ||
3667 req_sec
> now
+ RGW_AUTH_GRACE_MINS
* 60) && !qsr
) {
3668 ldout(cct
, 10) << "req_sec=" << req_sec
<< " now=" << now
3669 << "; now - RGW_AUTH_GRACE_MINS="
3670 << now
- RGW_AUTH_GRACE_MINS
* 60
3671 << "; now + RGW_AUTH_GRACE_MINS="
3672 << now
+ RGW_AUTH_GRACE_MINS
* 60
3675 ldout(cct
, 0) << "NOTICE: request time skew too big now="
3677 << " req_time=" << header_time
3686 static rgw::auth::Completer::cmplptr_t
3687 null_completer_factory(const boost::optional
<std::string
>& secret_key
)
3693 AWSEngine::VersionAbstractor::auth_data_t
3694 AWSGeneralAbstractor::get_auth_data(const req_state
* const s
) const
3698 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
3700 if (version
== AwsVersion::V2
) {
3701 return get_auth_data_v2(s
);
3702 } else if (version
== AwsVersion::V4
) {
3703 return get_auth_data_v4(s
, route
== AwsRoute::QUERY_STRING
);
3705 /* FIXME(rzarzynski): handle anon user. */
3710 boost::optional
<std::string
>
3711 AWSGeneralAbstractor::get_v4_canonical_headers(
3712 const req_info
& info
,
3713 const boost::string_view
& signedheaders
,
3714 const bool using_qs
) const
3716 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
3720 AWSEngine::VersionAbstractor::auth_data_t
3721 AWSGeneralAbstractor::get_auth_data_v4(const req_state
* const s
,
3723 bool using_qs
) const
3725 boost::string_view access_key_id
;
3726 boost::string_view signed_hdrs
;
3728 boost::string_view date
;
3729 boost::string_view credential_scope
;
3730 boost::string_view client_signature
;
3732 int ret
= rgw::auth::s3::parse_credentials(s
->info
,
3743 /* craft canonical headers */
3744 boost::optional
<std::string
> canonical_headers
= \
3745 get_v4_canonical_headers(s
->info
, signed_hdrs
, using_qs
);
3746 if (canonical_headers
) {
3747 ldout(s
->cct
, 10) << "canonical headers format = " << *canonical_headers
3753 /* Get the expected hash. */
3754 auto exp_payload_hash
= rgw::auth::s3::get_v4_exp_payload_hash(s
->info
);
3756 /* Craft canonical URI. Using std::move later so let it be non-const. */
3757 auto canonical_uri
= rgw::auth::s3::get_v4_canonical_uri(s
->info
);
3759 /* Craft canonical query string. std::moving later so non-const here. */
3760 auto canonical_qs
= rgw::auth::s3::get_v4_canonical_qs(s
->info
, using_qs
);
3762 /* Craft canonical request. */
3763 auto canonical_req_hash
= \
3764 rgw::auth::s3::get_v4_canon_req_hash(s
->cct
,
3766 std::move(canonical_uri
),
3767 std::move(canonical_qs
),
3768 std::move(*canonical_headers
),
3772 auto string_to_sign
= \
3773 rgw::auth::s3::get_v4_string_to_sign(s
->cct
,
3774 AWS4_HMAC_SHA256_STR
,
3777 std::move(canonical_req_hash
));
3779 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
3781 std::placeholders::_1
,
3782 std::placeholders::_2
,
3783 std::placeholders::_3
);
3785 /* Requests authenticated with the Query Parameters are treated as unsigned.
3786 * From "Authenticating Requests: Using Query Parameters (AWS Signature
3789 * You don't include a payload hash in the Canonical Request, because
3790 * when you create a presigned URL, you don't know the payload content
3791 * because the URL is used to upload an arbitrary payload. Instead, you
3792 * use a constant string UNSIGNED-PAYLOAD.
3794 * This means we have absolutely no business in spawning completer. Both
3795 * aws4_auth_needs_complete and aws4_auth_streaming_mode are set to false
3796 * by default. We don't need to change that. */
3797 if (is_v4_payload_unsigned(exp_payload_hash
) || is_v4_payload_empty(s
)) {
3801 std::move(string_to_sign
),
3803 null_completer_factory
3806 /* We're going to handle a signed payload. Be aware that even empty HTTP
3807 * body (no payload) requires verification:
3809 * The x-amz-content-sha256 header is required for all AWS Signature
3810 * Version 4 requests. It provides a hash of the request payload. If
3811 * there is no payload, you must provide the hash of an empty string. */
3812 if (!is_v4_payload_streamed(exp_payload_hash
)) {
3813 ldout(s
->cct
, 10) << "delaying v4 auth" << dendl
;
3815 /* payload in a single chunk */
3818 case RGW_OP_CREATE_BUCKET
:
3819 case RGW_OP_PUT_OBJ
:
3820 case RGW_OP_PUT_ACLS
:
3821 case RGW_OP_PUT_CORS
:
3822 case RGW_OP_COMPLETE_MULTIPART
:
3823 case RGW_OP_SET_BUCKET_VERSIONING
:
3824 case RGW_OP_DELETE_MULTI_OBJ
:
3825 case RGW_OP_ADMIN_SET_METADATA
:
3826 case RGW_OP_SET_BUCKET_WEBSITE
:
3827 case RGW_OP_PUT_BUCKET_POLICY
:
3828 case RGW_OP_PUT_OBJ_TAGGING
:
3831 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl
;
3832 throw -ERR_NOT_IMPLEMENTED
;
3835 const auto cmpl_factory
= std::bind(AWSv4ComplSingle::create
,
3837 std::placeholders::_1
);
3841 std::move(string_to_sign
),
3846 /* IMHO "streamed" doesn't fit too good here. I would prefer to call
3847 * it "chunked" but let's be coherent with Amazon's terminology. */
3849 dout(10) << "body content detected in multiple chunks" << dendl
;
3851 /* payload in multiple chunks */
3855 case RGW_OP_PUT_OBJ
:
3858 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED (streaming mode)" << dendl
;
3859 throw -ERR_NOT_IMPLEMENTED
;
3862 dout(10) << "aws4 seed signature ok... delaying v4 auth" << dendl
;
3864 /* In the case of streamed payload client sets the x-amz-content-sha256
3865 * to "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" but uses "UNSIGNED-PAYLOAD"
3866 * when constructing the Canonical Request. */
3868 /* In the case of single-chunk upload client set the header's value is
3869 * coherent with the one used for Canonical Request crafting. */
3871 /* In the case of query string-based authentication there should be no
3872 * x-amz-content-sha256 header and the value "UNSIGNED-PAYLOAD" is used
3874 const auto cmpl_factory
= std::bind(AWSv4ComplMulti::create
,
3879 std::placeholders::_1
);
3883 std::move(string_to_sign
),
3892 boost::optional
<std::string
>
3893 AWSGeneralBoto2Abstractor::get_v4_canonical_headers(
3894 const req_info
& info
,
3895 const boost::string_view
& signedheaders
,
3896 const bool using_qs
) const
3898 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
3903 AWSEngine::VersionAbstractor::auth_data_t
3904 AWSGeneralAbstractor::get_auth_data_v2(const req_state
* const s
) const
3906 boost::string_view access_key_id
;
3907 boost::string_view signature
;
3910 const char* http_auth
= s
->info
.env
->get("HTTP_AUTHORIZATION");
3911 if (! http_auth
|| http_auth
[0] == '\0') {
3912 /* Credentials are provided in query string. We also need to verify
3913 * the "Expires" parameter now. */
3914 access_key_id
= s
->info
.args
.get("AWSAccessKeyId");
3915 signature
= s
->info
.args
.get("Signature");
3918 boost::string_view expires
= s
->info
.args
.get("Expires");
3919 if (! expires
.empty()) {
3920 /* It looks we have the guarantee that expires is a null-terminated,
3921 * and thus string_view::data() can be safely used. */
3922 const time_t exp
= atoll(expires
.data());
3931 /* The "Authorization" HTTP header is being used. */
3932 const boost::string_view
auth_str(http_auth
+ strlen("AWS "));
3933 const size_t pos
= auth_str
.rfind(':');
3934 if (pos
!= boost::string_view::npos
) {
3935 access_key_id
= auth_str
.substr(0, pos
);
3936 signature
= auth_str
.substr(pos
+ 1);
3940 /* Let's canonize the HTTP headers that are covered by the AWS auth v2. */
3941 std::string string_to_sign
;
3942 utime_t header_time
;
3943 if (! rgw_create_s3_canonical_header(s
->info
, &header_time
, string_to_sign
,
3945 ldout(cct
, 10) << "failed to create the canonized auth header\n"
3946 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
3950 ldout(cct
, 10) << "string_to_sign:\n"
3951 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
3953 if (! is_time_skew_ok(header_time
, qsr
)) {
3954 throw -ERR_REQUEST_TIME_SKEWED
;
3958 std::move(access_key_id
),
3959 std::move(signature
),
3960 std::move(string_to_sign
),
3961 rgw::auth::s3::get_v2_signature
,
3962 null_completer_factory
3967 AWSEngine::VersionAbstractor::auth_data_t
3968 AWSBrowserUploadAbstractor::get_auth_data_v2(const req_state
* const s
) const
3971 s
->auth
.s3_postobj_creds
.access_key
,
3972 s
->auth
.s3_postobj_creds
.signature
,
3973 s
->auth
.s3_postobj_creds
.encoded_policy
.to_str(),
3974 rgw::auth::s3::get_v2_signature
,
3975 null_completer_factory
3979 AWSEngine::VersionAbstractor::auth_data_t
3980 AWSBrowserUploadAbstractor::get_auth_data_v4(const req_state
* const s
) const
3982 const boost::string_view credential
= s
->auth
.s3_postobj_creds
.x_amz_credential
;
3984 /* grab access key id */
3985 const size_t pos
= credential
.find("/");
3986 const boost::string_view access_key_id
= credential
.substr(0, pos
);
3987 dout(10) << "access key id = " << access_key_id
<< dendl
;
3989 /* grab credential scope */
3990 const boost::string_view credential_scope
= credential
.substr(pos
+ 1);
3991 dout(10) << "credential scope = " << credential_scope
<< dendl
;
3993 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
3995 std::placeholders::_1
,
3996 std::placeholders::_2
,
3997 std::placeholders::_3
);
4001 s
->auth
.s3_postobj_creds
.signature
,
4002 s
->auth
.s3_postobj_creds
.encoded_policy
.to_str(),
4004 null_completer_factory
4008 AWSEngine::VersionAbstractor::auth_data_t
4009 AWSBrowserUploadAbstractor::get_auth_data(const req_state
* const s
) const
4011 if (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
) {
4012 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v4"
4013 << " (AWS4-HMAC-SHA256)" << dendl
;
4014 return get_auth_data_v4(s
);
4016 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v2" << dendl
;
4017 return get_auth_data_v2(s
);
4023 AWSEngine::authenticate(const req_state
* const s
) const
4025 /* Small reminder: an ver_abstractor is allowed to throw! */
4026 const auto auth_data
= ver_abstractor
.get_auth_data(s
);
4028 if (auth_data
.access_key_id
.empty() || auth_data
.client_signature
.empty()) {
4029 return result_t::deny(-EINVAL
);
4031 return authenticate(auth_data
.access_key_id
,
4032 auth_data
.client_signature
,
4033 auth_data
.string_to_sign
,
4034 auth_data
.signature_factory
,
4035 auth_data
.completer_factory
,
4040 } /* namespace s3 */
4041 } /* namespace auth */
4042 } /* namespace rgw */
4044 rgw::LDAPHelper
* rgw::auth::s3::LDAPEngine::ldh
= nullptr;
4045 std::mutex
rgw::auth::s3::LDAPEngine::mtx
;
4047 void rgw::auth::s3::LDAPEngine::init(CephContext
* const cct
)
4050 std::lock_guard
<std::mutex
> lck(mtx
);
4052 const string
& ldap_uri
= cct
->_conf
->rgw_ldap_uri
;
4053 const string
& ldap_binddn
= cct
->_conf
->rgw_ldap_binddn
;
4054 const string
& ldap_searchdn
= cct
->_conf
->rgw_ldap_searchdn
;
4055 const string
& ldap_searchfilter
= cct
->_conf
->rgw_ldap_searchfilter
;
4056 const string
& ldap_dnattr
= cct
->_conf
->rgw_ldap_dnattr
;
4057 std::string ldap_bindpw
= parse_rgw_ldap_bindpw(cct
);
4059 ldh
= new rgw::LDAPHelper(ldap_uri
, ldap_binddn
, ldap_bindpw
,
4060 ldap_searchdn
, ldap_searchfilter
, ldap_dnattr
);
4068 rgw::auth::RemoteApplier::acl_strategy_t
4069 rgw::auth::s3::LDAPEngine::get_acl_strategy() const
4071 //This is based on the assumption that the default acl strategy in
4072 // get_perms_from_aclspec, will take care. Extra acl spec is not required.
4076 rgw::auth::RemoteApplier::AuthInfo
4077 rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken
& token
) const noexcept
4079 /* The short form of "using" can't be used here -- we're aliasing a class'
4081 using acct_privilege_t
= \
4082 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t
;
4084 return rgw::auth::RemoteApplier::AuthInfo
{
4087 RGW_PERM_FULL_CONTROL
,
4088 acct_privilege_t::IS_PLAIN_ACCT
,
4093 rgw::auth::Engine::result_t
4094 rgw::auth::s3::LDAPEngine::authenticate(
4095 const boost::string_view
& access_key_id
,
4096 const boost::string_view
& signature
,
4097 const string_to_sign_t
& string_to_sign
,
4098 const signature_factory_t
&,
4099 const completer_factory_t
& completer_factory
,
4100 const req_state
* const s
) const
4102 /* boost filters and/or string_ref may throw on invalid input */
4103 rgw::RGWToken base64_token
;
4105 base64_token
= rgw::from_base64(access_key_id
);
4107 base64_token
= std::string("");
4110 if (! base64_token
.valid()) {
4111 return result_t::deny();
4114 //TODO: Uncomment, when we have a migration plan in place.
4115 //Check if a user of type other than 'ldap' is already present, if yes, then
4117 /*RGWUserInfo user_info;
4118 user_info.user_id = base64_token.id;
4119 if (rgw_get_user_info_by_uid(store, user_info.user_id, user_info) >= 0) {
4120 if (user_info.type != TYPE_LDAP) {
4121 ldout(cct, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl;
4126 if (ldh
->auth(base64_token
.id
, base64_token
.key
) != 0) {
4127 return result_t::deny();
4130 auto apl
= apl_factory
->create_apl_remote(cct
, s
, get_acl_strategy(),
4131 get_creds_info(base64_token
));
4132 return result_t::grant(std::move(apl
), completer_factory(boost::none
));
4137 rgw::auth::Engine::result_t
4138 rgw::auth::s3::LocalEngine::authenticate(
4139 const boost::string_view
& _access_key_id
,
4140 const boost::string_view
& signature
,
4141 const string_to_sign_t
& string_to_sign
,
4142 const signature_factory_t
& signature_factory
,
4143 const completer_factory_t
& completer_factory
,
4144 const req_state
* const s
) const
4146 /* get the user info */
4147 RGWUserInfo user_info
;
4148 /* TODO(rzarzynski): we need to have string-view taking variant. */
4149 const std::string access_key_id
= _access_key_id
.to_string();
4150 if (rgw_get_user_info_by_access_key(store
, access_key_id
, user_info
) < 0) {
4151 ldout(cct
, 5) << "error reading user info, uid=" << access_key_id
4152 << " can't authenticate" << dendl
;
4153 return result_t::deny(-ERR_INVALID_ACCESS_KEY
);
4155 //TODO: Uncomment, when we have a migration plan in place.
4157 if (s->user->type != TYPE_RGW) {
4158 ldout(cct, 10) << "ERROR: User id of type: " << s->user->type
4159 << " is present" << dendl;
4164 const auto iter
= user_info
.access_keys
.find(access_key_id
);
4165 if (iter
== std::end(user_info
.access_keys
)) {
4166 ldout(cct
, 0) << "ERROR: access key not encoded in user info" << dendl
;
4167 return result_t::deny(-EPERM
);
4169 const RGWAccessKey
& k
= iter
->second
;
4171 const VersionAbstractor::server_signature_t server_signature
= \
4172 signature_factory(cct
, k
.key
, string_to_sign
);
4174 ldout(cct
, 15) << "string_to_sign="
4175 << rgw::crypt_sanitize::log_content
{string_to_sign
}
4177 ldout(cct
, 15) << "server signature=" << server_signature
<< dendl
;
4178 ldout(cct
, 15) << "client signature=" << signature
<< dendl
;
4179 ldout(cct
, 15) << "compare=" << signature
.compare(server_signature
) << dendl
;
4181 if (static_cast<boost::string_view
>(server_signature
) != signature
) {
4182 return result_t::deny(-ERR_SIGNATURE_NO_MATCH
);
4185 auto apl
= apl_factory
->create_apl_local(cct
, s
, user_info
, k
.subuser
);
4186 return result_t::grant(std::move(apl
), completer_factory(k
.key
));