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 static void dump_usage_bucket_info(Formatter
*formatter
, const std::string
& name
, const cls_user_bucket_entry
& entry
)
520 formatter
->open_object_section("Entry");
521 formatter
->dump_string("Bucket", name
);
522 formatter
->dump_int("Bytes", entry
.size
);
523 formatter
->dump_int("Bytes_Rounded", entry
.size_rounded
);
524 formatter
->close_section(); // entry
527 void RGWGetUsage_ObjStore_S3::send_response()
530 set_req_state_err(s
, op_ret
);
533 end_header(s
, this, "application/xml");
538 Formatter
*formatter
= s
->formatter
;
540 bool user_section_open
= false;
542 formatter
->open_object_section("Usage");
543 if (show_log_entries
) {
544 formatter
->open_array_section("Entries");
546 map
<rgw_user_bucket
, rgw_usage_log_entry
>::iterator iter
;
547 for (iter
= usage
.begin(); iter
!= usage
.end(); ++iter
) {
548 const rgw_user_bucket
& ub
= iter
->first
;
549 const rgw_usage_log_entry
& entry
= iter
->second
;
551 if (show_log_entries
) {
552 if (ub
.user
.compare(last_owner
) != 0) {
553 if (user_section_open
) {
554 formatter
->close_section();
555 formatter
->close_section();
557 formatter
->open_object_section("User");
558 formatter
->dump_string("Owner", ub
.user
);
559 formatter
->open_array_section("Buckets");
560 user_section_open
= true;
561 last_owner
= ub
.user
;
563 formatter
->open_object_section("Bucket");
564 formatter
->dump_string("Bucket", ub
.bucket
);
565 utime_t
ut(entry
.epoch
, 0);
566 ut
.gmtime(formatter
->dump_stream("Time"));
567 formatter
->dump_int("Epoch", entry
.epoch
);
568 dump_usage_categories_info(formatter
, entry
, &categories
);
569 formatter
->close_section(); // bucket
572 summary_map
[ub
.user
].aggregate(entry
, &categories
);
575 if (show_log_entries
) {
576 if (user_section_open
) {
577 formatter
->close_section(); // buckets
578 formatter
->close_section(); //user
580 formatter
->close_section(); // entries
584 formatter
->open_array_section("Summary");
585 map
<string
, rgw_usage_log_entry
>::iterator siter
;
586 for (siter
= summary_map
.begin(); siter
!= summary_map
.end(); ++siter
) {
587 const rgw_usage_log_entry
& entry
= siter
->second
;
588 formatter
->open_object_section("User");
589 formatter
->dump_string("User", siter
->first
);
590 dump_usage_categories_info(formatter
, entry
, &categories
);
591 rgw_usage_data total_usage
;
592 entry
.sum(total_usage
, categories
);
593 formatter
->open_object_section("Total");
594 formatter
->dump_int("BytesSent", total_usage
.bytes_sent
);
595 formatter
->dump_int("BytesReceived", total_usage
.bytes_received
);
596 formatter
->dump_int("Ops", total_usage
.ops
);
597 formatter
->dump_int("SuccessfulOps", total_usage
.successful_ops
);
598 formatter
->close_section(); // total
599 formatter
->close_section(); // user
602 if (s
->cct
->_conf
->rgw_rest_getusage_op_compat
) {
603 formatter
->open_object_section("Stats");
606 formatter
->dump_int("TotalBytes", header
.stats
.total_bytes
);
607 formatter
->dump_int("TotalBytesRounded", header
.stats
.total_bytes_rounded
);
608 formatter
->dump_int("TotalEntries", header
.stats
.total_entries
);
610 if (s
->cct
->_conf
->rgw_rest_getusage_op_compat
) {
611 formatter
->close_section(); //Stats
614 formatter
->close_section(); // summary
617 formatter
->open_array_section("CapacityUsed");
618 formatter
->open_object_section("User");
619 formatter
->open_array_section("Buckets");
620 for (const auto& biter
: buckets_usage
) {
621 const cls_user_bucket_entry
& entry
= biter
.second
;
622 dump_usage_bucket_info(formatter
, biter
.first
, entry
);
624 formatter
->close_section(); // Buckets
625 formatter
->close_section(); // User
626 formatter
->close_section(); // CapacityUsed
628 formatter
->close_section(); // usage
629 rgw_flush_formatter_and_reset(s
, s
->formatter
);
632 int RGWListBucket_ObjStore_S3::get_params()
634 list_versions
= s
->info
.args
.exists("versions");
635 prefix
= s
->info
.args
.get("prefix");
636 if (!list_versions
) {
637 marker
= s
->info
.args
.get("marker");
639 marker
.name
= s
->info
.args
.get("key-marker");
640 marker
.instance
= s
->info
.args
.get("version-id-marker");
642 max_keys
= s
->info
.args
.get("max-keys");
643 op_ret
= parse_max_keys();
647 delimiter
= s
->info
.args
.get("delimiter");
648 encoding_type
= s
->info
.args
.get("encoding-type");
649 if (s
->system_request
) {
650 s
->info
.args
.get_bool("objs-container", &objs_container
, false);
651 const char *shard_id_str
= s
->info
.env
->get("HTTP_RGWX_SHARD_ID");
654 shard_id
= strict_strtol(shard_id_str
, 10, &err
);
656 ldout(s
->cct
, 5) << "bad shard id specified: " << shard_id_str
<< dendl
;
660 shard_id
= s
->bucket_instance_shard_id
;
666 void RGWListBucket_ObjStore_S3::send_versioned_response()
668 s
->formatter
->open_object_section_in_ns("ListVersionsResult", XMLNS_AWS_S3
);
669 if (!s
->bucket_tenant
.empty())
670 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
671 s
->formatter
->dump_string("Name", s
->bucket_name
);
672 s
->formatter
->dump_string("Prefix", prefix
);
673 s
->formatter
->dump_string("KeyMarker", marker
.name
);
674 s
->formatter
->dump_string("VersionIdMarker", marker
.instance
);
675 if (is_truncated
&& !next_marker
.empty()) {
676 s
->formatter
->dump_string("NextKeyMarker", next_marker
.name
);
677 s
->formatter
->dump_string("NextVersionIdMarker", next_marker
.instance
);
679 s
->formatter
->dump_int("MaxKeys", max
);
680 if (!delimiter
.empty())
681 s
->formatter
->dump_string("Delimiter", delimiter
);
683 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
686 bool encode_key
= false;
687 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
688 s
->formatter
->dump_string("EncodingType", "url");
693 if (objs_container
) {
694 s
->formatter
->open_array_section("Entries");
697 vector
<rgw_bucket_dir_entry
>::iterator iter
;
698 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
699 const char *section_name
= (iter
->is_delete_marker() ? "DeleteMarker"
701 s
->formatter
->open_object_section(section_name
);
702 if (objs_container
) {
703 s
->formatter
->dump_bool("IsDeleteMarker", iter
->is_delete_marker());
705 rgw_obj_key
key(iter
->key
);
708 url_encode(key
.name
, key_name
);
709 s
->formatter
->dump_string("Key", key_name
);
711 s
->formatter
->dump_string("Key", key
.name
);
713 string version_id
= key
.instance
;
714 if (version_id
.empty()) {
717 if (s
->system_request
) {
718 if (iter
->versioned_epoch
> 0) {
719 s
->formatter
->dump_int("VersionedEpoch", iter
->versioned_epoch
);
721 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
722 utime_t
ut(iter
->meta
.mtime
);
723 ut
.gmtime_nsec(s
->formatter
->dump_stream("RgwxMtime"));
725 s
->formatter
->dump_string("VersionId", version_id
);
726 s
->formatter
->dump_bool("IsLatest", iter
->is_current());
727 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
728 if (!iter
->is_delete_marker()) {
729 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
730 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
731 s
->formatter
->dump_string("StorageClass", "STANDARD");
733 dump_owner(s
, iter
->meta
.owner
, iter
->meta
.owner_display_name
);
734 s
->formatter
->close_section();
736 if (objs_container
) {
737 s
->formatter
->close_section();
740 if (!common_prefixes
.empty()) {
741 map
<string
, bool>::iterator pref_iter
;
742 for (pref_iter
= common_prefixes
.begin();
743 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
744 s
->formatter
->open_array_section("CommonPrefixes");
745 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
746 s
->formatter
->close_section();
750 s
->formatter
->close_section();
751 rgw_flush_formatter_and_reset(s
, s
->formatter
);
754 void RGWListBucket_ObjStore_S3::send_response()
757 set_req_state_err(s
, op_ret
);
760 end_header(s
, this, "application/xml");
766 send_versioned_response();
770 s
->formatter
->open_object_section_in_ns("ListBucketResult", XMLNS_AWS_S3
);
771 if (!s
->bucket_tenant
.empty())
772 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
773 s
->formatter
->dump_string("Name", s
->bucket_name
);
774 s
->formatter
->dump_string("Prefix", prefix
);
775 s
->formatter
->dump_string("Marker", marker
.name
);
776 if (is_truncated
&& !next_marker
.empty())
777 s
->formatter
->dump_string("NextMarker", next_marker
.name
);
778 s
->formatter
->dump_int("MaxKeys", max
);
779 if (!delimiter
.empty())
780 s
->formatter
->dump_string("Delimiter", delimiter
);
782 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
785 bool encode_key
= false;
786 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
787 s
->formatter
->dump_string("EncodingType", "url");
792 vector
<rgw_bucket_dir_entry
>::iterator iter
;
793 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
794 rgw_obj_key
key(iter
->key
);
795 s
->formatter
->open_array_section("Contents");
798 url_encode(key
.name
, key_name
);
799 s
->formatter
->dump_string("Key", key_name
);
801 s
->formatter
->dump_string("Key", key
.name
);
803 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
804 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
805 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
806 s
->formatter
->dump_string("StorageClass", "STANDARD");
807 dump_owner(s
, iter
->meta
.owner
, iter
->meta
.owner_display_name
);
808 if (s
->system_request
) {
809 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
811 s
->formatter
->close_section();
813 if (!common_prefixes
.empty()) {
814 map
<string
, bool>::iterator pref_iter
;
815 for (pref_iter
= common_prefixes
.begin();
816 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
817 s
->formatter
->open_array_section("CommonPrefixes");
818 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
819 s
->formatter
->close_section();
823 s
->formatter
->close_section();
824 rgw_flush_formatter_and_reset(s
, s
->formatter
);
827 void RGWGetBucketLogging_ObjStore_S3::send_response()
830 end_header(s
, this, "application/xml");
833 s
->formatter
->open_object_section_in_ns("BucketLoggingStatus", XMLNS_AWS_S3
);
834 s
->formatter
->close_section();
835 rgw_flush_formatter_and_reset(s
, s
->formatter
);
838 void RGWGetBucketLocation_ObjStore_S3::send_response()
844 RGWZoneGroup zonegroup
;
847 int ret
= store
->get_zonegroup(s
->bucket_info
.zonegroup
, zonegroup
);
849 api_name
= zonegroup
.api_name
;
851 if (s
->bucket_info
.zonegroup
!= "default") {
852 api_name
= s
->bucket_info
.zonegroup
;
856 s
->formatter
->dump_format_ns("LocationConstraint", XMLNS_AWS_S3
,
857 "%s", api_name
.c_str());
858 rgw_flush_formatter_and_reset(s
, s
->formatter
);
861 void RGWGetBucketVersioning_ObjStore_S3::send_response()
864 end_header(s
, this, "application/xml");
867 s
->formatter
->open_object_section_in_ns("VersioningConfiguration", XMLNS_AWS_S3
);
869 const char *status
= (versioning_enabled
? "Enabled" : "Suspended");
870 s
->formatter
->dump_string("Status", status
);
872 s
->formatter
->close_section();
873 rgw_flush_formatter_and_reset(s
, s
->formatter
);
876 class RGWSetBucketVersioningParser
: public RGWXMLParser
878 XMLObj
*alloc_obj(const char *el
) override
{
883 RGWSetBucketVersioningParser() {}
884 ~RGWSetBucketVersioningParser() override
{}
886 int get_versioning_status(bool *status
) {
887 XMLObj
*config
= find_first("VersioningConfiguration");
893 XMLObj
*field
= config
->find_first("Status");
897 string
& s
= field
->get_data();
899 if (stringcasecmp(s
, "Enabled") == 0) {
901 } else if (stringcasecmp(s
, "Suspended") != 0) {
909 int RGWSetBucketVersioning_ObjStore_S3::get_params()
911 char *data
= nullptr;
914 rgw_rest_read_all_input(s
, &data
, &len
, s
->cct
->_conf
->rgw_max_put_param_size
, false);
919 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
921 r
= do_aws4_auth_completion();
926 RGWSetBucketVersioningParser parser
;
928 if (!parser
.init()) {
929 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
934 if (!parser
.parse(data
, len
, 1)) {
935 ldout(s
->cct
, 10) << "failed to parse data: " << data
<< dendl
;
940 if (!store
->is_meta_master()) {
941 /* only need to keep this data around if we're not meta master */
942 in_data
.append(data
, len
);
945 r
= parser
.get_versioning_status(&enable_versioning
);
950 void RGWSetBucketVersioning_ObjStore_S3::send_response()
953 set_req_state_err(s
, op_ret
);
958 int RGWSetBucketWebsite_ObjStore_S3::get_params()
960 char *data
= nullptr;
962 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
963 int r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
969 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
971 r
= do_aws4_auth_completion();
976 bufferptr
in_ptr(data
, len
);
977 in_data
.append(in_ptr
);
979 RGWXMLDecoder::XMLParser parser
;
980 if (!parser
.init()) {
981 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
985 if (!parser
.parse(data
, len
, 1)) {
986 string
str(data
, len
);
987 ldout(s
->cct
, 5) << "failed to parse xml: " << str
<< dendl
;
992 RGWXMLDecoder::decode_xml("WebsiteConfiguration", website_conf
, &parser
, true);
993 } catch (RGWXMLDecoder::err
& err
) {
994 string
str(data
, len
);
995 ldout(s
->cct
, 5) << "unexpected xml: " << str
<< dendl
;
1002 void RGWSetBucketWebsite_ObjStore_S3::send_response()
1005 set_req_state_err(s
, op_ret
);
1010 void RGWDeleteBucketWebsite_ObjStore_S3::send_response()
1013 op_ret
= STATUS_NO_CONTENT
;
1015 set_req_state_err(s
, op_ret
);
1020 void RGWGetBucketWebsite_ObjStore_S3::send_response()
1023 set_req_state_err(s
, op_ret
);
1025 end_header(s
, this, "application/xml");
1032 RGWBucketWebsiteConf
& conf
= s
->bucket_info
.website_conf
;
1034 s
->formatter
->open_object_section_in_ns("WebsiteConfiguration", XMLNS_AWS_S3
);
1035 conf
.dump_xml(s
->formatter
);
1036 s
->formatter
->close_section(); // WebsiteConfiguration
1037 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1040 static void dump_bucket_metadata(struct req_state
*s
, RGWBucketEnt
& bucket
)
1042 dump_header(s
, "X-RGW-Object-Count", static_cast<long long>(bucket
.count
));
1043 dump_header(s
, "X-RGW-Bytes-Used", static_cast<long long>(bucket
.size
));
1046 void RGWStatBucket_ObjStore_S3::send_response()
1049 dump_bucket_metadata(s
, bucket
);
1052 set_req_state_err(s
, op_ret
);
1055 end_header(s
, this);
1059 static int create_s3_policy(struct req_state
*s
, RGWRados
*store
,
1060 RGWAccessControlPolicy_S3
& s3policy
,
1063 if (s
->has_acl_header
) {
1064 if (!s
->canned_acl
.empty())
1065 return -ERR_INVALID_REQUEST
;
1067 return s3policy
.create_from_headers(store
, s
->info
.env
, owner
);
1070 return s3policy
.create_canned(owner
, s
->bucket_owner
, s
->canned_acl
);
1073 class RGWLocationConstraint
: public XMLObj
1076 RGWLocationConstraint() {}
1077 ~RGWLocationConstraint() override
{}
1078 bool xml_end(const char *el
) override
{
1082 location_constraint
= get_data();
1087 string location_constraint
;
1090 class RGWCreateBucketConfig
: public XMLObj
1093 RGWCreateBucketConfig() {}
1094 ~RGWCreateBucketConfig() override
{}
1097 class RGWCreateBucketParser
: public RGWXMLParser
1099 XMLObj
*alloc_obj(const char *el
) override
{
1104 RGWCreateBucketParser() {}
1105 ~RGWCreateBucketParser() override
{}
1107 bool get_location_constraint(string
& zone_group
) {
1108 XMLObj
*config
= find_first("CreateBucketConfiguration");
1112 XMLObj
*constraint
= config
->find_first("LocationConstraint");
1116 zone_group
= constraint
->get_data();
1122 int RGWCreateBucket_ObjStore_S3::get_params()
1124 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1126 int r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
1133 char *data
= nullptr;
1135 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
1136 op_ret
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
1138 if ((op_ret
< 0) && (op_ret
!= -ERR_LENGTH_REQUIRED
))
1141 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
1143 const int auth_ret
= do_aws4_auth_completion();
1148 bufferptr
in_ptr(data
, len
);
1149 in_data
.append(in_ptr
);
1152 RGWCreateBucketParser parser
;
1154 if (!parser
.init()) {
1155 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
1159 bool success
= parser
.parse(data
, len
, 1);
1160 ldout(s
->cct
, 20) << "create bucket input data=" << data
<< dendl
;
1163 ldout(s
->cct
, 0) << "failed to parse input: " << data
<< dendl
;
1167 if (!parser
.get_location_constraint(location_constraint
)) {
1168 ldout(s
->cct
, 0) << "provided input did not specify location constraint correctly" << dendl
;
1172 ldout(s
->cct
, 10) << "create bucket location constraint: "
1173 << location_constraint
<< dendl
;
1176 size_t pos
= location_constraint
.find(':');
1177 if (pos
!= string::npos
) {
1178 placement_rule
= location_constraint
.substr(pos
+ 1);
1179 location_constraint
= location_constraint
.substr(0, pos
);
1185 void RGWCreateBucket_ObjStore_S3::send_response()
1187 if (op_ret
== -ERR_BUCKET_EXISTS
)
1190 set_req_state_err(s
, op_ret
);
1197 if (s
->system_request
) {
1198 JSONFormatter f
; /* use json formatter for system requests output */
1200 f
.open_object_section("info");
1201 encode_json("entry_point_object_ver", ep_objv
, &f
);
1202 encode_json("object_ver", info
.objv_tracker
.read_version
, &f
);
1203 encode_json("bucket_info", info
, &f
);
1205 rgw_flush_formatter_and_reset(s
, &f
);
1209 void RGWDeleteBucket_ObjStore_S3::send_response()
1213 r
= STATUS_NO_CONTENT
;
1215 set_req_state_err(s
, r
);
1217 end_header(s
, this);
1219 if (s
->system_request
) {
1220 JSONFormatter f
; /* use json formatter for system requests output */
1222 f
.open_object_section("info");
1223 encode_json("object_ver", objv_tracker
.read_version
, &f
);
1225 rgw_flush_formatter_and_reset(s
, &f
);
1229 int RGWPutObj_ObjStore_S3::get_params()
1232 return -ERR_LENGTH_REQUIRED
;
1234 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
1235 map
<string
, bufferlist
> src_attrs
;
1239 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1240 ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
1246 if_match
= s
->info
.env
->get("HTTP_IF_MATCH");
1247 if_nomatch
= s
->info
.env
->get("HTTP_IF_NONE_MATCH");
1248 copy_source
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE");
1249 copy_source_range
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE");
1251 /* handle x-amz-copy-source */
1254 if (*copy_source
== '/') ++copy_source
;
1255 copy_source_bucket_name
= copy_source
;
1256 pos
= copy_source_bucket_name
.find("/");
1257 if (pos
== std::string::npos
) {
1259 ldout(s
->cct
, 5) << "x-amz-copy-source bad format" << dendl
;
1262 copy_source_object_name
= copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
1263 copy_source_bucket_name
= copy_source_bucket_name
.substr(0, pos
);
1264 #define VERSION_ID_STR "?versionId="
1265 pos
= copy_source_object_name
.find(VERSION_ID_STR
);
1266 if (pos
== std::string::npos
) {
1267 copy_source_object_name
= url_decode(copy_source_object_name
);
1269 copy_source_version_id
= copy_source_object_name
.substr(pos
+ sizeof(VERSION_ID_STR
) - 1);
1270 copy_source_object_name
= url_decode(copy_source_object_name
.substr(0, pos
));
1272 pos
= copy_source_bucket_name
.find(":");
1273 if (pos
== std::string::npos
) {
1274 copy_source_tenant_name
= s
->src_tenant_name
;
1276 copy_source_tenant_name
= copy_source_bucket_name
.substr(0, pos
);
1277 copy_source_bucket_name
= copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
1278 if (copy_source_bucket_name
.empty()) {
1280 ldout(s
->cct
, 5) << "source bucket name is empty" << dendl
;
1284 ret
= store
->get_bucket_info(obj_ctx
,
1285 copy_source_tenant_name
,
1286 copy_source_bucket_name
,
1287 copy_source_bucket_info
,
1290 ldout(s
->cct
, 5) << __func__
<< "(): get_bucket_info() returned ret=" << ret
<< dendl
;
1294 /* handle x-amz-copy-source-range */
1296 if (copy_source_range
) {
1297 string range
= copy_source_range
;
1298 pos
= range
.find("=");
1299 if (pos
== std::string::npos
) {
1301 ldout(s
->cct
, 5) << "x-amz-copy-source-range bad format" << dendl
;
1304 range
= range
.substr(pos
+ 1);
1305 pos
= range
.find("-");
1306 if (pos
== std::string::npos
) {
1308 ldout(s
->cct
, 5) << "x-amz-copy-source-range bad format" << dendl
;
1311 string first
= range
.substr(0, pos
);
1312 string last
= range
.substr(pos
+ 1);
1313 copy_source_range_fst
= strtoull(first
.c_str(), NULL
, 10);
1314 copy_source_range_lst
= strtoull(last
.c_str(), NULL
, 10);
1319 /* handle object tagging */
1320 auto tag_str
= s
->info
.env
->get("HTTP_X_AMZ_TAGGING");
1322 obj_tags
= ceph::make_unique
<RGWObjTags
>();
1323 ret
= obj_tags
->set_from_string(tag_str
);
1325 ldout(s
->cct
,0) << "setting obj tags failed with " << ret
<< dendl
;
1326 if (ret
== -ERR_INVALID_TAG
){
1327 ret
= -EINVAL
; //s3 returns only -EINVAL for PUT requests
1334 return RGWPutObj_ObjStore::get_params();
1337 int RGWPutObj_ObjStore_S3::get_data(bufferlist
& bl
)
1339 const int ret
= RGWPutObj_ObjStore::get_data(bl
);
1341 const int ret_auth
= do_aws4_auth_completion();
1350 static int get_success_retcode(int code
)
1354 return STATUS_CREATED
;
1356 return STATUS_NO_CONTENT
;
1361 void RGWPutObj_ObjStore_S3::send_response()
1364 set_req_state_err(s
, op_ret
);
1367 if (s
->cct
->_conf
->rgw_s3_success_create_obj_status
) {
1368 op_ret
= get_success_retcode(
1369 s
->cct
->_conf
->rgw_s3_success_create_obj_status
);
1370 set_req_state_err(s
, op_ret
);
1375 dump_content_length(s
, 0);
1376 for (auto &it
: crypt_http_responses
)
1377 dump_header(s
, it
.first
, it
.second
);
1380 end_header(s
, this, "application/xml");
1384 time_t secs
= (time_t)ut
.sec();
1385 gmtime_r(&secs
, &tmp
);
1386 char buf
[TIME_BUF_SIZE
];
1387 s
->formatter
->open_object_section_in_ns("CopyPartResult",
1388 "http://s3.amazonaws.com/doc/2006-03-01/");
1389 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%T.000Z", &tmp
) > 0) {
1390 s
->formatter
->dump_string("LastModified", buf
);
1392 s
->formatter
->dump_string("ETag", etag
);
1393 s
->formatter
->close_section();
1394 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1398 if (s
->system_request
&& !real_clock::is_zero(mtime
)) {
1399 dump_epoch_header(s
, "Rgwx-Mtime", mtime
);
1401 end_header(s
, this);
1404 static inline int get_obj_attrs(RGWRados
*store
, struct req_state
*s
, rgw_obj
& obj
, map
<string
, bufferlist
>& attrs
)
1406 RGWRados::Object
op_target(store
, s
->bucket_info
, *static_cast<RGWObjectCtx
*>(s
->obj_ctx
), obj
);
1407 RGWRados::Object::Read
read_op(&op_target
);
1409 read_op
.params
.attrs
= &attrs
;
1411 return read_op
.prepare();
1414 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const std::string
& value
)
1418 attrs
.emplace(key
, std::move(bl
));
1421 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const char* value
)
1425 attrs
.emplace(key
, std::move(bl
));
1428 int RGWPutObj_ObjStore_S3::get_decrypt_filter(
1429 std::unique_ptr
<RGWGetDataCB
>* filter
,
1431 map
<string
, bufferlist
>& attrs
,
1432 bufferlist
* manifest_bl
)
1434 std::map
<std::string
, std::string
> crypt_http_responses_unused
;
1437 std::unique_ptr
<BlockCrypt
> block_crypt
;
1438 res
= rgw_s3_prepare_decrypt(s
, attrs
, &block_crypt
, crypt_http_responses_unused
);
1440 if (block_crypt
!= nullptr) {
1441 auto f
= std::unique_ptr
<RGWGetObj_BlockDecrypt
>(new RGWGetObj_BlockDecrypt(s
->cct
, cb
, std::move(block_crypt
)));
1442 //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
1444 if (manifest_bl
!= nullptr) {
1445 res
= f
->read_manifest(*manifest_bl
);
1447 *filter
= std::move(f
);
1456 int RGWPutObj_ObjStore_S3::get_encrypt_filter(
1457 std::unique_ptr
<RGWPutObjDataProcessor
>* filter
,
1458 RGWPutObjDataProcessor
* cb
)
1461 RGWPutObjProcessor_Multipart
* multi_processor
=dynamic_cast<RGWPutObjProcessor_Multipart
*>(cb
);
1462 if (multi_processor
!= nullptr) {
1463 RGWMPObj
* mp
= nullptr;
1464 multi_processor
->get_mp(&mp
);
1465 if (mp
!= nullptr) {
1466 map
<string
, bufferlist
> xattrs
;
1468 meta_oid
= mp
->get_meta();
1471 obj
.init_ns(s
->bucket
, meta_oid
, RGW_OBJ_NS_MULTIPART
);
1472 obj
.set_in_extra_data(true);
1473 res
= get_obj_attrs(store
, s
, obj
, xattrs
);
1475 std::unique_ptr
<BlockCrypt
> block_crypt
;
1476 /* We are adding to existing object.
1477 * We use crypto mode that configured as if we were decrypting. */
1478 res
= rgw_s3_prepare_decrypt(s
, xattrs
, &block_crypt
, crypt_http_responses
);
1479 if (res
== 0 && block_crypt
!= nullptr)
1480 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
1481 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1484 /* it is ok, to not have encryption at all */
1488 std::unique_ptr
<BlockCrypt
> block_crypt
;
1489 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, &block_crypt
, crypt_http_responses
);
1490 if (res
== 0 && block_crypt
!= nullptr) {
1491 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
1492 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1498 void RGWPostObj_ObjStore_S3::rebuild_key(string
& key
)
1500 static string var
= "${filename}";
1501 int pos
= key
.find(var
);
1505 string new_key
= key
.substr(0, pos
);
1506 new_key
.append(filename
);
1507 new_key
.append(key
.substr(pos
+ var
.size()));
1512 std::string
RGWPostObj_ObjStore_S3::get_current_filename() const
1514 return s
->object
.name
;
1517 std::string
RGWPostObj_ObjStore_S3::get_current_content_type() const
1519 return content_type
;
1522 int RGWPostObj_ObjStore_S3::get_params()
1524 op_ret
= RGWPostObj_ObjStore::get_params();
1529 ldout(s
->cct
, 20) << "adding bucket to policy env: " << s
->bucket
.name
1531 env
.add_var("bucket", s
->bucket
.name
);
1535 struct post_form_part part
;
1536 int r
= read_form_part_header(&part
, done
);
1540 if (s
->cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 20)) {
1541 ldout(s
->cct
, 20) << "read part header -- part.name="
1542 << part
.name
<< dendl
;
1544 for (const auto& pair
: part
.fields
) {
1545 ldout(s
->cct
, 20) << "field.name=" << pair
.first
<< dendl
;
1546 ldout(s
->cct
, 20) << "field.val=" << pair
.second
.val
<< dendl
;
1547 ldout(s
->cct
, 20) << "field.params:" << dendl
;
1549 for (const auto& param_pair
: pair
.second
.params
) {
1550 ldout(s
->cct
, 20) << " " << param_pair
.first
1551 << " -> " << param_pair
.second
<< dendl
;
1556 if (done
) { /* unexpected here */
1557 err_msg
= "Malformed request";
1561 if (stringcasecmp(part
.name
, "file") == 0) { /* beginning of data transfer */
1562 struct post_part_field
& field
= part
.fields
["Content-Disposition"];
1563 map
<string
, string
>::iterator iter
= field
.params
.find("filename");
1564 if (iter
!= field
.params
.end()) {
1565 filename
= iter
->second
;
1567 parts
[part
.name
] = part
;
1572 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1573 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
1574 if (r
< 0 || !boundary
) {
1575 err_msg
= "Couldn't find boundary";
1578 parts
[part
.name
] = part
;
1579 string
part_str(part
.data
.c_str(), part
.data
.length());
1580 env
.add_var(part
.name
, part_str
);
1584 if (!part_str(parts
, "key", &object_str
)) {
1585 err_msg
= "Key not specified";
1589 s
->object
= rgw_obj_key(object_str
);
1591 rebuild_key(s
->object
.name
);
1593 if (s
->object
.empty()) {
1594 err_msg
= "Empty object name";
1598 env
.add_var("key", s
->object
.name
);
1600 part_str(parts
, "Content-Type", &content_type
);
1601 env
.add_var("Content-Type", content_type
);
1603 map
<string
, struct post_form_part
, ltstr_nocase
>::iterator piter
=
1604 parts
.upper_bound(RGW_AMZ_META_PREFIX
);
1605 for (; piter
!= parts
.end(); ++piter
) {
1606 string n
= piter
->first
;
1607 if (strncasecmp(n
.c_str(), RGW_AMZ_META_PREFIX
,
1608 sizeof(RGW_AMZ_META_PREFIX
) - 1) != 0)
1611 string attr_name
= RGW_ATTR_PREFIX
;
1612 attr_name
.append(n
);
1614 /* need to null terminate it */
1615 bufferlist
& data
= piter
->second
.data
;
1616 string str
= string(data
.c_str(), data
.length());
1619 attr_bl
.append(str
.c_str(), str
.size() + 1);
1621 attrs
[attr_name
] = attr_bl
;
1623 // TODO: refactor this and the above loop to share code
1624 piter
= parts
.find(RGW_AMZ_WEBSITE_REDIRECT_LOCATION
);
1625 if (piter
!= parts
.end()) {
1626 string n
= piter
->first
;
1627 string attr_name
= RGW_ATTR_PREFIX
;
1628 attr_name
.append(n
);
1629 /* need to null terminate it */
1630 bufferlist
& data
= piter
->second
.data
;
1631 string str
= string(data
.c_str(), data
.length());
1634 attr_bl
.append(str
.c_str(), str
.size() + 1);
1636 attrs
[attr_name
] = attr_bl
;
1639 int r
= get_policy();
1648 min_len
= post_policy
.min_length
;
1649 max_len
= post_policy
.max_length
;
1656 int RGWPostObj_ObjStore_S3::get_tags()
1659 if (part_str(parts
, "tagging", &tags_str
)) {
1660 RGWObjTagsXMLParser parser
;
1661 if (!parser
.init()){
1662 ldout(s
->cct
, 0) << "Couldn't init RGWObjTags XML parser" << dendl
;
1663 err_msg
= "Server couldn't process the request";
1664 return -EINVAL
; // TODO: This class of errors in rgw code should be a 5XX error
1666 if (!parser
.parse(tags_str
.c_str(), tags_str
.size(), 1)) {
1667 ldout(s
->cct
,0 ) << "Invalid Tagging XML" << dendl
;
1668 err_msg
= "Invalid Tagging XML";
1672 RGWObjTagSet_S3
*obj_tags_s3
;
1673 RGWObjTagging_S3
*tagging
;
1675 tagging
= static_cast<RGWObjTagging_S3
*>(parser
.find_first("Tagging"));
1676 obj_tags_s3
= static_cast<RGWObjTagSet_S3
*>(tagging
->find_first("TagSet"));
1678 return -ERR_MALFORMED_XML
;
1681 RGWObjTags obj_tags
;
1682 int r
= obj_tags_s3
->rebuild(obj_tags
);
1687 obj_tags
.encode(tags_bl
);
1688 ldout(s
->cct
, 20) << "Read " << obj_tags
.count() << "tags" << dendl
;
1689 attrs
[RGW_ATTR_TAGS
] = tags_bl
;
1696 int RGWPostObj_ObjStore_S3::get_policy()
1698 if (part_bl(parts
, "policy", &s
->auth
.s3_postobj_creds
.encoded_policy
)) {
1699 bool aws4_auth
= false;
1701 /* x-amz-algorithm handling */
1702 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
1703 if ((part_str(parts
, "x-amz-algorithm", &s
->auth
.s3_postobj_creds
.x_amz_algorithm
)) &&
1704 (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
)) {
1705 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v4 (AWS4-HMAC-SHA256)" << dendl
;
1708 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v2" << dendl
;
1711 // check that the signature matches the encoded policy
1715 /* x-amz-credential handling */
1716 if (!part_str(parts
, "x-amz-credential",
1717 &s
->auth
.s3_postobj_creds
.x_amz_credential
)) {
1718 ldout(s
->cct
, 0) << "No S3 aws4 credential found!" << dendl
;
1719 err_msg
= "Missing aws4 credential";
1723 /* x-amz-signature handling */
1724 if (!part_str(parts
, "x-amz-signature",
1725 &s
->auth
.s3_postobj_creds
.signature
)) {
1726 ldout(s
->cct
, 0) << "No aws4 signature found!" << dendl
;
1727 err_msg
= "Missing aws4 signature";
1731 /* x-amz-date handling */
1732 std::string received_date_str
;
1733 if (!part_str(parts
, "x-amz-date", &received_date_str
)) {
1734 ldout(s
->cct
, 0) << "No aws4 date found!" << dendl
;
1735 err_msg
= "Missing aws4 date";
1741 // check that the signature matches the encoded policy
1742 if (!part_str(parts
, "AWSAccessKeyId",
1743 &s
->auth
.s3_postobj_creds
.access_key
)) {
1744 ldout(s
->cct
, 0) << "No S3 aws2 access key found!" << dendl
;
1745 err_msg
= "Missing aws2 access key";
1749 if (!part_str(parts
, "signature", &s
->auth
.s3_postobj_creds
.signature
)) {
1750 ldout(s
->cct
, 0) << "No aws2 signature found!" << dendl
;
1751 err_msg
= "Missing aws2 signature";
1756 /* FIXME: this is a makeshift solution. The browser upload authentication will be
1757 * handled by an instance of rgw::auth::Completer spawned in Handler's authorize()
1759 const int ret
= rgw::auth::Strategy::apply(auth_registry_ptr
->get_s3_post(), s
);
1763 /* Populate the owner info. */
1764 s
->owner
.set_id(s
->user
->user_id
);
1765 s
->owner
.set_name(s
->user
->display_name
);
1766 ldout(s
->cct
, 20) << "Successful Signature Verification!" << dendl
;
1769 ceph::bufferlist decoded_policy
;
1771 decoded_policy
.decode_base64(s
->auth
.s3_postobj_creds
.encoded_policy
);
1772 } catch (buffer::error
& err
) {
1773 ldout(s
->cct
, 0) << "failed to decode_base64 policy" << dendl
;
1774 err_msg
= "Could not decode policy";
1778 decoded_policy
.append('\0'); // NULL terminate
1779 ldout(s
->cct
, 20) << "POST policy: " << decoded_policy
.c_str() << dendl
;
1782 int r
= post_policy
.from_json(decoded_policy
, err_msg
);
1784 if (err_msg
.empty()) {
1785 err_msg
= "Failed to parse policy";
1787 ldout(s
->cct
, 0) << "failed to parse policy" << dendl
;
1793 post_policy
.set_var_checked("x-amz-signature");
1796 post_policy
.set_var_checked("AWSAccessKeyId");
1797 post_policy
.set_var_checked("signature");
1799 post_policy
.set_var_checked("policy");
1801 r
= post_policy
.check(&env
, err_msg
);
1803 if (err_msg
.empty()) {
1804 err_msg
= "Policy check failed";
1806 ldout(s
->cct
, 0) << "policy check failed" << dendl
;
1811 ldout(s
->cct
, 0) << "No attached policy found!" << dendl
;
1815 part_str(parts
, "acl", &canned_acl
);
1817 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1818 ldout(s
->cct
, 20) << "canned_acl=" << canned_acl
<< dendl
;
1819 if (s3policy
.create_canned(s
->owner
, s
->bucket_owner
, canned_acl
) < 0) {
1820 err_msg
= "Bad canned ACLs";
1829 int RGWPostObj_ObjStore_S3::complete_get_params()
1833 struct post_form_part part
;
1834 int r
= read_form_part_header(&part
, done
);
1839 ceph::bufferlist part_data
;
1841 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1842 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
1843 if (r
< 0 || !boundary
) {
1847 /* Just reading the data but not storing any results of that. */
1853 int RGWPostObj_ObjStore_S3::get_data(ceph::bufferlist
& bl
, bool& again
)
1858 const uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1859 int r
= read_data(bl
, chunk_size
, boundary
, done
);
1866 /* Reached end of data, let's drain the rest of the params */
1867 r
= complete_get_params();
1878 void RGWPostObj_ObjStore_S3::send_response()
1880 if (op_ret
== 0 && parts
.count("success_action_redirect")) {
1883 part_str(parts
, "success_action_redirect", &redirect
);
1888 string etag_str
= "\"";
1890 etag_str
.append(etag
);
1891 etag_str
.append("\"");
1895 url_encode(s
->bucket_tenant
, tenant
); /* surely overkill, but cheap */
1896 url_encode(s
->bucket_name
, bucket
);
1897 url_encode(s
->object
.name
, key
);
1898 url_encode(etag_str
, etag_url
);
1900 if (!s
->bucket_tenant
.empty()) {
1902 * What we really would like is to quaily the bucket name, so
1903 * that the client could simply copy it and paste into next request.
1904 * Unfortunately, in S3 we cannot know if the client will decide
1905 * to come through DNS, with "bucket.tenant" sytanx, or through
1906 * URL with "tenant\bucket" syntax. Therefore, we provide the
1907 * tenant separately.
1909 redirect
.append("?tenant=");
1910 redirect
.append(tenant
);
1911 redirect
.append("&bucket=");
1912 redirect
.append(bucket
);
1914 redirect
.append("?bucket=");
1915 redirect
.append(bucket
);
1917 redirect
.append("&key=");
1918 redirect
.append(key
);
1919 redirect
.append("&etag=");
1920 redirect
.append(etag_url
);
1922 int r
= check_utf8(redirect
.c_str(), redirect
.size());
1927 dump_redirect(s
, redirect
);
1928 op_ret
= STATUS_REDIRECT
;
1929 } else if (op_ret
== 0 && parts
.count("success_action_status")) {
1930 string status_string
;
1931 uint32_t status_int
;
1933 part_str(parts
, "success_action_status", &status_string
);
1935 int r
= stringtoul(status_string
, &status_int
);
1941 switch (status_int
) {
1945 op_ret
= STATUS_CREATED
;
1948 op_ret
= STATUS_NO_CONTENT
;
1951 } else if (! op_ret
) {
1952 op_ret
= STATUS_NO_CONTENT
;
1956 if (op_ret
== STATUS_CREATED
) {
1957 for (auto &it
: crypt_http_responses
)
1958 dump_header(s
, it
.first
, it
.second
);
1959 s
->formatter
->open_object_section("PostResponse");
1960 if (g_conf
->rgw_dns_name
.length())
1961 s
->formatter
->dump_format("Location", "%s/%s",
1962 s
->info
.script_uri
.c_str(),
1963 s
->object
.name
.c_str());
1964 if (!s
->bucket_tenant
.empty())
1965 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
1966 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
1967 s
->formatter
->dump_string("Key", s
->object
.name
);
1968 s
->formatter
->close_section();
1970 s
->err
.message
= err_msg
;
1971 set_req_state_err(s
, op_ret
);
1974 dump_content_length(s
, s
->formatter
->get_len());
1976 end_header(s
, this);
1977 if (op_ret
!= STATUS_CREATED
)
1980 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1983 int RGWPostObj_ObjStore_S3::get_encrypt_filter(
1984 std::unique_ptr
<RGWPutObjDataProcessor
>* filter
, RGWPutObjDataProcessor
* cb
)
1987 std::unique_ptr
<BlockCrypt
> block_crypt
;
1988 res
= rgw_s3_prepare_encrypt(s
, attrs
, &parts
, &block_crypt
, crypt_http_responses
);
1989 if (res
== 0 && block_crypt
!= nullptr) {
1990 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
1991 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1998 int RGWDeleteObj_ObjStore_S3::get_params()
2000 const char *if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_DELETE_IF_UNMODIFIED_SINCE");
2002 if (s
->system_request
) {
2003 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"no-precondition-error", &no_precondition_error
, false);
2007 std::string if_unmod_decoded
= url_decode(if_unmod
);
2010 if (utime_t::parse_date(if_unmod_decoded
, &epoch
, &nsec
) < 0) {
2011 ldout(s
->cct
, 10) << "failed to parse time: " << if_unmod_decoded
<< dendl
;
2014 unmod_since
= utime_t(epoch
, nsec
).to_real_time();
2020 void RGWDeleteObj_ObjStore_S3::send_response()
2026 r
= STATUS_NO_CONTENT
;
2028 set_req_state_err(s
, r
);
2030 if (!version_id
.empty()) {
2031 dump_header(s
, "x-amz-version-id", version_id
);
2033 if (delete_marker
) {
2034 dump_header(s
, "x-amz-delete-marker", "true");
2036 end_header(s
, this);
2039 int RGWCopyObj_ObjStore_S3::init_dest_policy()
2041 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2043 /* build a policy for the target object */
2044 int r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
2048 dest_policy
= s3policy
;
2053 int RGWCopyObj_ObjStore_S3::get_params()
2055 if_mod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MODIFIED_SINCE");
2056 if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_UNMODIFIED_SINCE");
2057 if_match
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MATCH");
2058 if_nomatch
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_NONE_MATCH");
2060 src_tenant_name
= s
->src_tenant_name
;
2061 src_bucket_name
= s
->src_bucket_name
;
2062 src_object
= s
->src_object
;
2063 dest_tenant_name
= s
->bucket
.tenant
;
2064 dest_bucket_name
= s
->bucket
.name
;
2065 dest_object
= s
->object
.name
;
2067 if (s
->system_request
) {
2068 source_zone
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"source-zone");
2069 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"copy-if-newer", ©_if_newer
, false);
2070 if (!source_zone
.empty()) {
2071 client_id
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"client-id");
2072 op_id
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"op-id");
2074 if (client_id
.empty() || op_id
.empty()) {
2076 RGW_SYS_PARAM_PREFIX
"client-id or "
2077 RGW_SYS_PARAM_PREFIX
"op-id were not provided, "
2078 "required for intra-region copy"
2085 const char *md_directive
= s
->info
.env
->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
2087 if (strcasecmp(md_directive
, "COPY") == 0) {
2088 attrs_mod
= RGWRados::ATTRSMOD_NONE
;
2089 } else if (strcasecmp(md_directive
, "REPLACE") == 0) {
2090 attrs_mod
= RGWRados::ATTRSMOD_REPLACE
;
2091 } else if (!source_zone
.empty()) {
2092 attrs_mod
= RGWRados::ATTRSMOD_NONE
; // default for intra-zone_group copy
2094 ldout(s
->cct
, 0) << "invalid metadata directive" << dendl
;
2099 if (source_zone
.empty() &&
2100 (dest_tenant_name
.compare(src_tenant_name
) == 0) &&
2101 (dest_bucket_name
.compare(src_bucket_name
) == 0) &&
2102 (dest_object
.compare(src_object
.name
) == 0) &&
2103 src_object
.instance
.empty() &&
2104 (attrs_mod
!= RGWRados::ATTRSMOD_REPLACE
)) {
2105 /* can only copy object into itself if replacing attrs */
2106 ldout(s
->cct
, 0) << "can't copy object into itself if not replacing attrs"
2108 return -ERR_INVALID_REQUEST
;
2113 void RGWCopyObj_ObjStore_S3::send_partial_response(off_t ofs
)
2115 if (! sent_header
) {
2117 set_req_state_err(s
, op_ret
);
2120 end_header(s
, this, "application/xml");
2122 s
->formatter
->open_object_section_in_ns("CopyObjectResult", XMLNS_AWS_S3
);
2126 /* Send progress field. Note that this diverge from the original S3
2127 * spec. We do this in order to keep connection alive.
2129 s
->formatter
->dump_int("Progress", (uint64_t)ofs
);
2131 rgw_flush_formatter(s
, s
->formatter
);
2134 void RGWCopyObj_ObjStore_S3::send_response()
2137 send_partial_response(0);
2140 dump_time(s
, "LastModified", &mtime
);
2141 std::string etag_str
= etag
.to_str();
2142 if (! etag_str
.empty()) {
2143 s
->formatter
->dump_string("ETag", std::move(etag_str
));
2145 s
->formatter
->close_section();
2146 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2150 void RGWGetACLs_ObjStore_S3::send_response()
2153 set_req_state_err(s
, op_ret
);
2155 end_header(s
, this, "application/xml");
2157 rgw_flush_formatter(s
, s
->formatter
);
2161 int RGWPutACLs_ObjStore_S3::get_params()
2163 int ret
= RGWPutACLs_ObjStore::get_params();
2165 const int ret_auth
= do_aws4_auth_completion();
2173 int RGWPutACLs_ObjStore_S3::get_policy_from_state(RGWRados
*store
,
2174 struct req_state
*s
,
2177 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2179 // bucket-* canned acls do not apply to bucket
2180 if (s
->object
.empty()) {
2181 if (s
->canned_acl
.find("bucket") != string::npos
)
2182 s
->canned_acl
.clear();
2185 int r
= create_s3_policy(s
, store
, s3policy
, owner
);
2189 s3policy
.to_xml(ss
);
2194 void RGWPutACLs_ObjStore_S3::send_response()
2197 set_req_state_err(s
, op_ret
);
2199 end_header(s
, this, "application/xml");
2203 void RGWGetLC_ObjStore_S3::execute()
2205 config
.set_ctx(s
->cct
);
2207 map
<string
, bufferlist
>::iterator aiter
= s
->bucket_attrs
.find(RGW_ATTR_LC
);
2208 if (aiter
== s
->bucket_attrs
.end()) {
2213 bufferlist::iterator
iter(&aiter
->second
);
2215 config
.decode(iter
);
2216 } catch (const buffer::error
& e
) {
2217 ldout(s
->cct
, 0) << __func__
<< "decode life cycle config failed" << dendl
;
2223 void RGWGetLC_ObjStore_S3::send_response()
2226 if (op_ret
== -ENOENT
) {
2227 set_req_state_err(s
, ERR_NO_SUCH_LC
);
2229 set_req_state_err(s
, op_ret
);
2233 end_header(s
, this, "application/xml");
2239 config
.dump_xml(s
->formatter
);
2240 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2243 void RGWPutLC_ObjStore_S3::send_response()
2246 set_req_state_err(s
, op_ret
);
2248 end_header(s
, this, "application/xml");
2252 void RGWDeleteLC_ObjStore_S3::send_response()
2255 op_ret
= STATUS_NO_CONTENT
;
2257 set_req_state_err(s
, op_ret
);
2260 end_header(s
, this, "application/xml");
2264 void RGWGetCORS_ObjStore_S3::send_response()
2267 if (op_ret
== -ENOENT
)
2268 set_req_state_err(s
, ERR_NOT_FOUND
);
2270 set_req_state_err(s
, op_ret
);
2273 end_header(s
, NULL
, "application/xml");
2277 RGWCORSConfiguration_S3
*s3cors
=
2278 static_cast<RGWCORSConfiguration_S3
*>(&bucket_cors
);
2287 int RGWPutCORS_ObjStore_S3::get_params()
2290 char *data
= nullptr;
2292 RGWCORSXMLParser_S3
parser(s
->cct
);
2293 RGWCORSConfiguration_S3
*cors_config
;
2295 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2296 r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
2301 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
2303 r
= do_aws4_auth_completion();
2308 if (!parser
.init()) {
2312 if (!data
|| !parser
.parse(data
, len
, 1)) {
2316 static_cast<RGWCORSConfiguration_S3
*>(parser
.find_first(
2317 "CORSConfiguration"));
2322 // forward bucket cors requests to meta master zone
2323 if (!store
->is_meta_master()) {
2324 /* only need to keep this data around if we're not meta master */
2325 in_data
.append(data
, len
);
2328 if (s
->cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 15)) {
2329 ldout(s
->cct
, 15) << "CORSConfiguration";
2330 cors_config
->to_xml(*_dout
);
2334 cors_config
->encode(cors_bl
);
2339 void RGWPutCORS_ObjStore_S3::send_response()
2342 set_req_state_err(s
, op_ret
);
2344 end_header(s
, NULL
, "application/xml");
2348 void RGWDeleteCORS_ObjStore_S3::send_response()
2351 if (!r
|| r
== -ENOENT
)
2352 r
= STATUS_NO_CONTENT
;
2354 set_req_state_err(s
, r
);
2356 end_header(s
, NULL
);
2359 void RGWOptionsCORS_ObjStore_S3::send_response()
2361 string hdrs
, exp_hdrs
;
2362 uint32_t max_age
= CORS_MAX_AGE_INVALID
;
2363 /*EACCES means, there is no CORS registered yet for the bucket
2364 *ENOENT means, there is no match of the Origin in the list of CORSRule
2366 if (op_ret
== -ENOENT
)
2369 set_req_state_err(s
, op_ret
);
2371 end_header(s
, NULL
);
2374 get_response_params(hdrs
, exp_hdrs
, &max_age
);
2377 dump_access_control(s
, origin
, req_meth
, hdrs
.c_str(), exp_hdrs
.c_str(),
2379 end_header(s
, NULL
);
2382 void RGWGetRequestPayment_ObjStore_S3::send_response()
2385 end_header(s
, this, "application/xml");
2388 s
->formatter
->open_object_section_in_ns("RequestPaymentConfiguration", XMLNS_AWS_S3
);
2389 const char *payer
= requester_pays
? "Requester" : "BucketOwner";
2390 s
->formatter
->dump_string("Payer", payer
);
2391 s
->formatter
->close_section();
2392 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2395 class RGWSetRequestPaymentParser
: public RGWXMLParser
2397 XMLObj
*alloc_obj(const char *el
) override
{
2402 RGWSetRequestPaymentParser() {}
2403 ~RGWSetRequestPaymentParser() override
{}
2405 int get_request_payment_payer(bool *requester_pays
) {
2406 XMLObj
*config
= find_first("RequestPaymentConfiguration");
2410 *requester_pays
= false;
2412 XMLObj
*field
= config
->find_first("Payer");
2416 string
& s
= field
->get_data();
2418 if (stringcasecmp(s
, "Requester") == 0) {
2419 *requester_pays
= true;
2420 } else if (stringcasecmp(s
, "BucketOwner") != 0) {
2428 int RGWSetRequestPayment_ObjStore_S3::get_params()
2432 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2433 int r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
2439 RGWSetRequestPaymentParser parser
;
2441 if (!parser
.init()) {
2442 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
2447 if (!parser
.parse(data
, len
, 1)) {
2448 ldout(s
->cct
, 10) << "failed to parse data: " << data
<< dendl
;
2453 r
= parser
.get_request_payment_payer(&requester_pays
);
2461 void RGWSetRequestPayment_ObjStore_S3::send_response()
2464 set_req_state_err(s
, op_ret
);
2469 int RGWInitMultipart_ObjStore_S3::get_params()
2471 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2472 op_ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
2481 void RGWInitMultipart_ObjStore_S3::send_response()
2484 set_req_state_err(s
, op_ret
);
2486 for (auto &it
: crypt_http_responses
)
2487 dump_header(s
, it
.first
, it
.second
);
2488 end_header(s
, this, "application/xml");
2491 s
->formatter
->open_object_section_in_ns("InitiateMultipartUploadResult", XMLNS_AWS_S3
);
2492 if (!s
->bucket_tenant
.empty())
2493 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2494 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2495 s
->formatter
->dump_string("Key", s
->object
.name
);
2496 s
->formatter
->dump_string("UploadId", upload_id
);
2497 s
->formatter
->close_section();
2498 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2502 int RGWInitMultipart_ObjStore_S3::prepare_encryption(map
<string
, bufferlist
>& attrs
)
2505 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, nullptr, crypt_http_responses
);
2509 int RGWCompleteMultipart_ObjStore_S3::get_params()
2511 int ret
= RGWCompleteMultipart_ObjStore::get_params();
2516 return do_aws4_auth_completion();
2519 void RGWCompleteMultipart_ObjStore_S3::send_response()
2522 set_req_state_err(s
, op_ret
);
2524 end_header(s
, this, "application/xml");
2527 s
->formatter
->open_object_section_in_ns("CompleteMultipartUploadResult", XMLNS_AWS_S3
);
2528 if (!s
->bucket_tenant
.empty()) {
2529 if (s
->info
.domain
.length()) {
2530 s
->formatter
->dump_format("Location", "%s.%s.%s",
2531 s
->bucket_name
.c_str(),
2532 s
->bucket_tenant
.c_str(),
2533 s
->info
.domain
.c_str());
2535 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2537 if (s
->info
.domain
.length()) {
2538 s
->formatter
->dump_format("Location", "%s.%s",
2539 s
->bucket_name
.c_str(),
2540 s
->info
.domain
.c_str());
2543 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2544 s
->formatter
->dump_string("Key", s
->object
.name
);
2545 s
->formatter
->dump_string("ETag", etag
);
2546 s
->formatter
->close_section();
2547 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2551 void RGWAbortMultipart_ObjStore_S3::send_response()
2555 r
= STATUS_NO_CONTENT
;
2557 set_req_state_err(s
, r
);
2559 end_header(s
, this);
2562 void RGWListMultipart_ObjStore_S3::send_response()
2565 set_req_state_err(s
, op_ret
);
2567 end_header(s
, this, "application/xml");
2571 s
->formatter
->open_object_section_in_ns("ListPartsResult", XMLNS_AWS_S3
);
2572 map
<uint32_t, RGWUploadPartInfo
>::iterator iter
;
2573 map
<uint32_t, RGWUploadPartInfo
>::reverse_iterator test_iter
;
2576 iter
= parts
.begin();
2577 test_iter
= parts
.rbegin();
2578 if (test_iter
!= parts
.rend()) {
2579 cur_max
= test_iter
->first
;
2581 if (!s
->bucket_tenant
.empty())
2582 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2583 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2584 s
->formatter
->dump_string("Key", s
->object
.name
);
2585 s
->formatter
->dump_string("UploadId", upload_id
);
2586 s
->formatter
->dump_string("StorageClass", "STANDARD");
2587 s
->formatter
->dump_int("PartNumberMarker", marker
);
2588 s
->formatter
->dump_int("NextPartNumberMarker", cur_max
);
2589 s
->formatter
->dump_int("MaxParts", max_parts
);
2590 s
->formatter
->dump_string("IsTruncated", (truncated
? "true" : "false"));
2592 ACLOwner
& owner
= policy
.get_owner();
2593 dump_owner(s
, owner
.get_id(), owner
.get_display_name());
2595 for (; iter
!= parts
.end(); ++iter
) {
2596 RGWUploadPartInfo
& info
= iter
->second
;
2598 s
->formatter
->open_object_section("Part");
2600 dump_time(s
, "LastModified", &info
.modified
);
2602 s
->formatter
->dump_unsigned("PartNumber", info
.num
);
2603 s
->formatter
->dump_format("ETag", "\"%s\"", info
.etag
.c_str());
2604 s
->formatter
->dump_unsigned("Size", info
.accounted_size
);
2605 s
->formatter
->close_section();
2607 s
->formatter
->close_section();
2608 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2612 void RGWListBucketMultiparts_ObjStore_S3::send_response()
2615 set_req_state_err(s
, op_ret
);
2618 end_header(s
, this, "application/xml");
2623 s
->formatter
->open_object_section_in_ns("ListMultipartUploadsResult", XMLNS_AWS_S3
);
2624 if (!s
->bucket_tenant
.empty())
2625 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2626 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2627 if (!prefix
.empty())
2628 s
->formatter
->dump_string("ListMultipartUploadsResult.Prefix", prefix
);
2629 string
& key_marker
= marker
.get_key();
2630 if (!key_marker
.empty())
2631 s
->formatter
->dump_string("KeyMarker", key_marker
);
2632 string
& upload_id_marker
= marker
.get_upload_id();
2633 if (!upload_id_marker
.empty())
2634 s
->formatter
->dump_string("UploadIdMarker", upload_id_marker
);
2635 string next_key
= next_marker
.mp
.get_key();
2636 if (!next_key
.empty())
2637 s
->formatter
->dump_string("NextKeyMarker", next_key
);
2638 string next_upload_id
= next_marker
.mp
.get_upload_id();
2639 if (!next_upload_id
.empty())
2640 s
->formatter
->dump_string("NextUploadIdMarker", next_upload_id
);
2641 s
->formatter
->dump_int("MaxUploads", max_uploads
);
2642 if (!delimiter
.empty())
2643 s
->formatter
->dump_string("Delimiter", delimiter
);
2644 s
->formatter
->dump_string("IsTruncated", (is_truncated
? "true" : "false"));
2647 vector
<RGWMultipartUploadEntry
>::iterator iter
;
2648 for (iter
= uploads
.begin(); iter
!= uploads
.end(); ++iter
) {
2649 RGWMPObj
& mp
= iter
->mp
;
2650 s
->formatter
->open_array_section("Upload");
2651 s
->formatter
->dump_string("Key", mp
.get_key());
2652 s
->formatter
->dump_string("UploadId", mp
.get_upload_id());
2653 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
, "Initiator");
2654 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
);
2655 s
->formatter
->dump_string("StorageClass", "STANDARD");
2656 dump_time(s
, "Initiated", &iter
->obj
.meta
.mtime
);
2657 s
->formatter
->close_section();
2659 if (!common_prefixes
.empty()) {
2660 s
->formatter
->open_array_section("CommonPrefixes");
2661 map
<string
, bool>::iterator pref_iter
;
2662 for (pref_iter
= common_prefixes
.begin();
2663 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
2664 s
->formatter
->dump_string("CommonPrefixes.Prefix", pref_iter
->first
);
2666 s
->formatter
->close_section();
2669 s
->formatter
->close_section();
2670 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2673 int RGWDeleteMultiObj_ObjStore_S3::get_params()
2675 int ret
= RGWDeleteMultiObj_ObjStore::get_params();
2680 return do_aws4_auth_completion();
2683 void RGWDeleteMultiObj_ObjStore_S3::send_status()
2685 if (! status_dumped
) {
2687 set_req_state_err(s
, op_ret
);
2689 status_dumped
= true;
2693 void RGWDeleteMultiObj_ObjStore_S3::begin_response()
2696 if (!status_dumped
) {
2701 end_header(s
, this, "application/xml");
2702 s
->formatter
->open_object_section_in_ns("DeleteResult", XMLNS_AWS_S3
);
2704 rgw_flush_formatter(s
, s
->formatter
);
2707 void RGWDeleteMultiObj_ObjStore_S3::send_partial_response(rgw_obj_key
& key
,
2709 const string
& marker_version_id
, int ret
)
2712 if (op_ret
== 0 && !quiet
) {
2713 s
->formatter
->open_object_section("Deleted");
2714 s
->formatter
->dump_string("Key", key
.name
);
2715 if (!key
.instance
.empty()) {
2716 s
->formatter
->dump_string("VersionId", key
.instance
);
2718 if (delete_marker
) {
2719 s
->formatter
->dump_bool("DeleteMarker", true);
2720 s
->formatter
->dump_string("DeleteMarkerVersionId", marker_version_id
);
2722 s
->formatter
->close_section();
2723 } else if (op_ret
< 0) {
2724 struct rgw_http_error r
;
2727 s
->formatter
->open_object_section("Error");
2730 rgw_get_errno_s3(&r
, err_no
);
2732 s
->formatter
->dump_string("Key", key
.name
);
2733 s
->formatter
->dump_string("VersionId", key
.instance
);
2734 s
->formatter
->dump_int("Code", r
.http_ret
);
2735 s
->formatter
->dump_string("Message", r
.s3_code
);
2736 s
->formatter
->close_section();
2739 rgw_flush_formatter(s
, s
->formatter
);
2743 void RGWDeleteMultiObj_ObjStore_S3::end_response()
2746 s
->formatter
->close_section();
2747 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2750 void RGWGetObjLayout_ObjStore_S3::send_response()
2753 set_req_state_err(s
, op_ret
);
2755 end_header(s
, this, "application/json");
2763 f
.open_object_section("result");
2764 ::encode_json("head", head_obj
, &f
);
2765 ::encode_json("manifest", *manifest
, &f
);
2766 f
.open_array_section("data_location");
2767 for (auto miter
= manifest
->obj_begin(); miter
!= manifest
->obj_end(); ++miter
) {
2768 f
.open_object_section("obj");
2769 rgw_raw_obj raw_loc
= miter
.get_location().get_raw_obj(store
);
2770 ::encode_json("ofs", miter
.get_ofs(), &f
);
2771 ::encode_json("loc", raw_loc
, &f
);
2772 ::encode_json("loc_ofs", miter
.location_ofs(), &f
);
2773 ::encode_json("loc_size", miter
.get_stripe_size(), &f
);
2775 rgw_flush_formatter(s
, &f
);
2779 rgw_flush_formatter(s
, &f
);
2782 int RGWConfigBucketMetaSearch_ObjStore_S3::get_params()
2784 auto iter
= s
->info
.x_meta_map
.find("x-amz-meta-search");
2785 if (iter
== s
->info
.x_meta_map
.end()) {
2786 s
->err
.message
= "X-Rgw-Meta-Search header not provided";
2787 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2791 list
<string
> expressions
;
2792 get_str_list(iter
->second
, ",", expressions
);
2794 for (auto& expression
: expressions
) {
2795 vector
<string
> args
;
2796 get_str_vec(expression
, ";", args
);
2799 s
->err
.message
= "invalid empty expression";
2800 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2803 if (args
.size() > 2) {
2804 s
->err
.message
= string("invalid expression: ") + expression
;
2805 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2809 string key
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[0]));
2811 if (args
.size() > 1) {
2812 val
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[1]));
2815 if (!boost::algorithm::starts_with(key
, RGW_AMZ_META_PREFIX
)) {
2816 s
->err
.message
= string("invalid expression, key must start with '" RGW_AMZ_META_PREFIX
"' : ") + expression
;
2817 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2821 key
= key
.substr(sizeof(RGW_AMZ_META_PREFIX
) - 1);
2823 ESEntityTypeMap::EntityType entity_type
;
2825 if (val
.empty() || val
== "str" || val
== "string") {
2826 entity_type
= ESEntityTypeMap::ES_ENTITY_STR
;
2827 } else if (val
== "int" || val
== "integer") {
2828 entity_type
= ESEntityTypeMap::ES_ENTITY_INT
;
2829 } else if (val
== "date" || val
== "datetime") {
2830 entity_type
= ESEntityTypeMap::ES_ENTITY_DATE
;
2832 s
->err
.message
= string("invalid entity type: ") + val
;
2833 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2837 mdsearch_config
[key
] = entity_type
;
2843 void RGWConfigBucketMetaSearch_ObjStore_S3::send_response()
2846 set_req_state_err(s
, op_ret
);
2848 end_header(s
, this);
2851 void RGWGetBucketMetaSearch_ObjStore_S3::send_response()
2854 set_req_state_err(s
, op_ret
);
2856 end_header(s
, NULL
, "application/xml");
2858 Formatter
*f
= s
->formatter
;
2859 f
->open_array_section("GetBucketMetaSearchResult");
2860 for (auto& e
: s
->bucket_info
.mdsearch_config
) {
2861 f
->open_object_section("Entry");
2862 string k
= string("x-amz-meta-") + e
.first
;
2863 f
->dump_string("Key", k
.c_str());
2866 case ESEntityTypeMap::ES_ENTITY_INT
:
2869 case ESEntityTypeMap::ES_ENTITY_DATE
:
2875 f
->dump_string("Type", type
);
2879 rgw_flush_formatter(s
, f
);
2882 void RGWDelBucketMetaSearch_ObjStore_S3::send_response()
2885 set_req_state_err(s
, op_ret
);
2887 end_header(s
, this);
2891 RGWOp
*RGWHandler_REST_Service_S3::op_get()
2893 if (is_usage_op()) {
2894 return new RGWGetUsage_ObjStore_S3
;
2896 return new RGWListBuckets_ObjStore_S3
;
2900 RGWOp
*RGWHandler_REST_Service_S3::op_head()
2902 return new RGWListBuckets_ObjStore_S3
;
2905 RGWOp
*RGWHandler_REST_Service_S3::op_post()
2907 if (s
->info
.args
.exists("Action")) {
2908 string action
= s
->info
.args
.get("Action");
2909 if (action
.compare("CreateRole") == 0)
2910 return new RGWCreateRole
;
2911 if (action
.compare("DeleteRole") == 0)
2912 return new RGWDeleteRole
;
2913 if (action
.compare("GetRole") == 0)
2914 return new RGWGetRole
;
2915 if (action
.compare("UpdateAssumeRolePolicy") == 0)
2916 return new RGWModifyRole
;
2917 if (action
.compare("ListRoles") == 0)
2918 return new RGWListRoles
;
2919 if (action
.compare("PutRolePolicy") == 0)
2920 return new RGWPutRolePolicy
;
2921 if (action
.compare("GetRolePolicy") == 0)
2922 return new RGWGetRolePolicy
;
2923 if (action
.compare("ListRolePolicies") == 0)
2924 return new RGWListRolePolicies
;
2925 if (action
.compare("DeleteRolePolicy") == 0)
2926 return new RGWDeleteRolePolicy
;
2931 RGWOp
*RGWHandler_REST_Bucket_S3::get_obj_op(bool get_data
)
2935 return new RGWListBucket_ObjStore_S3
;
2937 return new RGWStatBucket_ObjStore_S3
;
2941 RGWOp
*RGWHandler_REST_Bucket_S3::op_get()
2943 if (s
->info
.args
.sub_resource_exists("logging"))
2944 return new RGWGetBucketLogging_ObjStore_S3
;
2946 if (s
->info
.args
.sub_resource_exists("location"))
2947 return new RGWGetBucketLocation_ObjStore_S3
;
2949 if (s
->info
.args
.sub_resource_exists("versioning"))
2950 return new RGWGetBucketVersioning_ObjStore_S3
;
2952 if (s
->info
.args
.sub_resource_exists("website")) {
2953 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
2956 return new RGWGetBucketWebsite_ObjStore_S3
;
2959 if (s
->info
.args
.exists("mdsearch")) {
2960 return new RGWGetBucketMetaSearch_ObjStore_S3
;
2964 return new RGWGetACLs_ObjStore_S3
;
2965 } else if (is_cors_op()) {
2966 return new RGWGetCORS_ObjStore_S3
;
2967 } else if (is_request_payment_op()) {
2968 return new RGWGetRequestPayment_ObjStore_S3
;
2969 } else if (s
->info
.args
.exists("uploads")) {
2970 return new RGWListBucketMultiparts_ObjStore_S3
;
2971 } else if(is_lc_op()) {
2972 return new RGWGetLC_ObjStore_S3
;
2973 } else if(is_policy_op()) {
2974 return new RGWGetBucketPolicy
;
2976 return get_obj_op(true);
2979 RGWOp
*RGWHandler_REST_Bucket_S3::op_head()
2982 return new RGWGetACLs_ObjStore_S3
;
2983 } else if (s
->info
.args
.exists("uploads")) {
2984 return new RGWListBucketMultiparts_ObjStore_S3
;
2986 return get_obj_op(false);
2989 RGWOp
*RGWHandler_REST_Bucket_S3::op_put()
2991 if (s
->info
.args
.sub_resource_exists("logging"))
2993 if (s
->info
.args
.sub_resource_exists("versioning"))
2994 return new RGWSetBucketVersioning_ObjStore_S3
;
2995 if (s
->info
.args
.sub_resource_exists("website")) {
2996 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
2999 return new RGWSetBucketWebsite_ObjStore_S3
;
3002 return new RGWPutACLs_ObjStore_S3
;
3003 } else if (is_cors_op()) {
3004 return new RGWPutCORS_ObjStore_S3
;
3005 } else if (is_request_payment_op()) {
3006 return new RGWSetRequestPayment_ObjStore_S3
;
3007 } else if(is_lc_op()) {
3008 return new RGWPutLC_ObjStore_S3
;
3009 } else if(is_policy_op()) {
3010 return new RGWPutBucketPolicy
;
3012 return new RGWCreateBucket_ObjStore_S3
;
3015 RGWOp
*RGWHandler_REST_Bucket_S3::op_delete()
3018 return new RGWDeleteCORS_ObjStore_S3
;
3019 } else if(is_lc_op()) {
3020 return new RGWDeleteLC_ObjStore_S3
;
3021 } else if(is_policy_op()) {
3022 return new RGWDeleteBucketPolicy
;
3025 if (s
->info
.args
.sub_resource_exists("website")) {
3026 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
3029 return new RGWDeleteBucketWebsite_ObjStore_S3
;
3032 if (s
->info
.args
.exists("mdsearch")) {
3033 return new RGWDelBucketMetaSearch_ObjStore_S3
;
3036 return new RGWDeleteBucket_ObjStore_S3
;
3039 RGWOp
*RGWHandler_REST_Bucket_S3::op_post()
3041 if (s
->info
.args
.exists("delete")) {
3042 return new RGWDeleteMultiObj_ObjStore_S3
;
3045 if (s
->info
.args
.exists("mdsearch")) {
3046 return new RGWConfigBucketMetaSearch_ObjStore_S3
;
3049 return new RGWPostObj_ObjStore_S3
;
3052 RGWOp
*RGWHandler_REST_Bucket_S3::op_options()
3054 return new RGWOptionsCORS_ObjStore_S3
;
3057 RGWOp
*RGWHandler_REST_Obj_S3::get_obj_op(bool get_data
)
3060 return new RGWGetACLs_ObjStore_S3
;
3062 RGWGetObj_ObjStore_S3
*get_obj_op
= new RGWGetObj_ObjStore_S3
;
3063 get_obj_op
->set_get_data(get_data
);
3067 RGWOp
*RGWHandler_REST_Obj_S3::op_get()
3070 return new RGWGetACLs_ObjStore_S3
;
3071 } else if (s
->info
.args
.exists("uploadId")) {
3072 return new RGWListMultipart_ObjStore_S3
;
3073 } else if (s
->info
.args
.exists("layout")) {
3074 return new RGWGetObjLayout_ObjStore_S3
;
3075 } else if (is_tagging_op()) {
3076 return new RGWGetObjTags_ObjStore_S3
;
3078 return get_obj_op(true);
3081 RGWOp
*RGWHandler_REST_Obj_S3::op_head()
3084 return new RGWGetACLs_ObjStore_S3
;
3085 } else if (s
->info
.args
.exists("uploadId")) {
3086 return new RGWListMultipart_ObjStore_S3
;
3088 return get_obj_op(false);
3091 RGWOp
*RGWHandler_REST_Obj_S3::op_put()
3094 return new RGWPutACLs_ObjStore_S3
;
3095 } else if (is_tagging_op()) {
3096 return new RGWPutObjTags_ObjStore_S3
;
3099 if (s
->init_state
.src_bucket
.empty())
3100 return new RGWPutObj_ObjStore_S3
;
3102 return new RGWCopyObj_ObjStore_S3
;
3105 RGWOp
*RGWHandler_REST_Obj_S3::op_delete()
3107 if (is_tagging_op()) {
3108 return new RGWDeleteObjTags_ObjStore_S3
;
3110 string upload_id
= s
->info
.args
.get("uploadId");
3112 if (upload_id
.empty())
3113 return new RGWDeleteObj_ObjStore_S3
;
3115 return new RGWAbortMultipart_ObjStore_S3
;
3118 RGWOp
*RGWHandler_REST_Obj_S3::op_post()
3120 if (s
->info
.args
.exists("uploadId"))
3121 return new RGWCompleteMultipart_ObjStore_S3
;
3123 if (s
->info
.args
.exists("uploads"))
3124 return new RGWInitMultipart_ObjStore_S3
;
3126 return new RGWPostObj_ObjStore_S3
;
3129 RGWOp
*RGWHandler_REST_Obj_S3::op_options()
3131 return new RGWOptionsCORS_ObjStore_S3
;
3134 int RGWHandler_REST_S3::init_from_header(struct req_state
* s
,
3135 int default_formatter
,
3136 bool configurable_format
)
3141 const char *req_name
= s
->relative_uri
.c_str();
3144 if (*req_name
== '?') {
3147 p
= s
->info
.request_params
.c_str();
3150 s
->info
.args
.set(p
);
3151 s
->info
.args
.parse();
3153 /* must be called after the args parsing */
3154 int ret
= allocate_formatter(s
, default_formatter
, configurable_format
);
3158 if (*req_name
!= '/')
3167 int pos
= req
.find('/');
3169 first
= req
.substr(0, pos
);
3175 * XXX The intent of the check for empty is apparently to let the bucket
3176 * name from DNS to be set ahead. However, we currently take the DNS
3177 * bucket and re-insert it into URL in rgw_rest.cc:RGWREST::preprocess().
3178 * So, this check is meaningless.
3180 * Rather than dropping this, the code needs to be changed into putting
3181 * the bucket (and its tenant) from DNS and Host: header (HTTP_HOST)
3182 * into req_status.bucket_name directly.
3184 if (s
->init_state
.url_bucket
.empty()) {
3185 // Save bucket to tide us over until token is parsed.
3186 s
->init_state
.url_bucket
= first
;
3188 string encoded_obj_str
= req
.substr(pos
+1);
3189 s
->object
= rgw_obj_key(encoded_obj_str
, s
->info
.args
.get("versionId"));
3192 s
->object
= rgw_obj_key(req_name
, s
->info
.args
.get("versionId"));
3197 int RGWHandler_REST_S3::postauth_init()
3199 struct req_init_state
*t
= &s
->init_state
;
3200 bool relaxed_names
= s
->cct
->_conf
->rgw_relaxed_s3_bucket_names
;
3202 rgw_parse_url_bucket(t
->url_bucket
, s
->user
->user_id
.tenant
,
3203 s
->bucket_tenant
, s
->bucket_name
);
3205 dout(10) << "s->object=" << (!s
->object
.empty() ? s
->object
: rgw_obj_key("<NULL>"))
3206 << " s->bucket=" << rgw_make_bucket_entry_name(s
->bucket_tenant
, s
->bucket_name
) << dendl
;
3209 ret
= rgw_validate_tenant_name(s
->bucket_tenant
);
3212 if (!s
->bucket_name
.empty()) {
3213 ret
= valid_s3_bucket_name(s
->bucket_name
, relaxed_names
);
3216 ret
= validate_object_name(s
->object
.name
);
3221 if (!t
->src_bucket
.empty()) {
3222 rgw_parse_url_bucket(t
->src_bucket
, s
->user
->user_id
.tenant
,
3223 s
->src_tenant_name
, s
->src_bucket_name
);
3224 ret
= rgw_validate_tenant_name(s
->src_tenant_name
);
3227 ret
= valid_s3_bucket_name(s
->src_bucket_name
, relaxed_names
);
3234 int RGWHandler_REST_S3::init(RGWRados
*store
, struct req_state
*s
,
3235 rgw::io::BasicClient
*cio
)
3241 ret
= rgw_validate_tenant_name(s
->bucket_tenant
);
3244 bool relaxed_names
= s
->cct
->_conf
->rgw_relaxed_s3_bucket_names
;
3245 if (!s
->bucket_name
.empty()) {
3246 ret
= valid_s3_bucket_name(s
->bucket_name
, relaxed_names
);
3249 ret
= validate_object_name(s
->object
.name
);
3254 const char *cacl
= s
->info
.env
->get("HTTP_X_AMZ_ACL");
3256 s
->canned_acl
= cacl
;
3258 s
->has_acl_header
= s
->info
.env
->exists_prefix("HTTP_X_AMZ_GRANT");
3260 const char *copy_source
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE");
3262 if (copy_source
&& !s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE")) {
3263 ret
= RGWCopyObj::parse_copy_location(copy_source
,
3264 s
->init_state
.src_bucket
,
3267 ldout(s
->cct
, 0) << "failed to parse copy location" << dendl
;
3268 return -EINVAL
; // XXX why not -ERR_INVALID_BUCKET_NAME or -ERR_BAD_URL?
3272 return RGWHandler_REST::init(store
, s
, cio
);
3275 enum class AwsVersion
{
3281 enum class AwsRoute
{
3287 static inline std::pair
<AwsVersion
, AwsRoute
>
3288 discover_aws_flavour(const req_info
& info
)
3290 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
3292 AwsVersion version
= AwsVersion::UNKOWN
;
3293 AwsRoute route
= AwsRoute::UNKOWN
;
3295 const char* http_auth
= info
.env
->get("HTTP_AUTHORIZATION");
3296 if (http_auth
&& http_auth
[0]) {
3297 /* Authorization in Header */
3298 route
= AwsRoute::HEADERS
;
3300 if (!strncmp(http_auth
, AWS4_HMAC_SHA256_STR
,
3301 strlen(AWS4_HMAC_SHA256_STR
))) {
3303 version
= AwsVersion::V4
;
3304 } else if (!strncmp(http_auth
, "AWS ", 4)) {
3306 version
= AwsVersion::V2
;
3309 route
= AwsRoute::QUERY_STRING
;
3311 if (info
.args
.get("X-Amz-Algorithm") == AWS4_HMAC_SHA256_STR
) {
3313 version
= AwsVersion::V4
;
3314 } else if (!info
.args
.get("AWSAccessKeyId").empty()) {
3316 version
= AwsVersion::V2
;
3320 return std::make_pair(version
, route
);
3323 static void init_anon_user(struct req_state
*s
)
3325 rgw_get_anon_user(*(s
->user
));
3326 s
->perm_mask
= RGW_PERM_FULL_CONTROL
;
3330 * verify that a signed request comes from the keyholder
3331 * by checking the signature against our locally-computed version
3333 * it tries AWS v4 before AWS v2
3335 int RGW_Auth_S3::authorize(RGWRados
* const store
,
3336 const rgw::auth::StrategyRegistry
& auth_registry
,
3337 struct req_state
* const s
)
3340 /* neither keystone and rados enabled; warn and exit! */
3341 if (!store
->ctx()->_conf
->rgw_s3_auth_use_rados
&&
3342 !store
->ctx()->_conf
->rgw_s3_auth_use_keystone
&&
3343 !store
->ctx()->_conf
->rgw_s3_auth_use_ldap
) {
3344 dout(0) << "WARNING: no authorization backend enabled! Users will never authenticate." << dendl
;
3348 const auto ret
= rgw::auth::Strategy::apply(auth_registry
.get_s3_main(), s
);
3350 /* Populate the owner info. */
3351 s
->owner
.set_id(s
->user
->user_id
);
3352 s
->owner
.set_name(s
->user
->display_name
);
3357 int RGWHandler_Auth_S3::init(RGWRados
*store
, struct req_state
*state
,
3358 rgw::io::BasicClient
*cio
)
3360 int ret
= RGWHandler_REST_S3::init_from_header(state
, RGW_FORMAT_JSON
,
3365 return RGWHandler_REST::init(store
, state
, cio
);
3368 RGWHandler_REST
* RGWRESTMgr_S3::get_handler(struct req_state
* const s
,
3369 const rgw::auth::StrategyRegistry
& auth_registry
,
3370 const std::string
& frontend_prefix
)
3372 bool is_s3website
= enable_s3website
&& (s
->prot_flags
& RGW_REST_WEBSITE
);
3374 RGWHandler_REST_S3::init_from_header(s
,
3375 is_s3website
? RGW_FORMAT_HTML
:
3376 RGW_FORMAT_XML
, true);
3380 RGWHandler_REST
* handler
;
3381 // TODO: Make this more readable
3383 if (s
->init_state
.url_bucket
.empty()) {
3384 handler
= new RGWHandler_REST_Service_S3Website(auth_registry
);
3385 } else if (s
->object
.empty()) {
3386 handler
= new RGWHandler_REST_Bucket_S3Website(auth_registry
);
3388 handler
= new RGWHandler_REST_Obj_S3Website(auth_registry
);
3391 if (s
->init_state
.url_bucket
.empty()) {
3392 handler
= new RGWHandler_REST_Service_S3(auth_registry
);
3393 } else if (s
->object
.empty()) {
3394 handler
= new RGWHandler_REST_Bucket_S3(auth_registry
);
3396 handler
= new RGWHandler_REST_Obj_S3(auth_registry
);
3400 ldout(s
->cct
, 20) << __func__
<< " handler=" << typeid(*handler
).name()
3405 bool RGWHandler_REST_S3Website::web_dir() const {
3406 std::string subdir_name
= url_decode(s
->object
.name
);
3408 if (subdir_name
.empty()) {
3410 } else if (subdir_name
.back() == '/') {
3411 subdir_name
.pop_back();
3414 rgw_obj
obj(s
->bucket
, subdir_name
);
3416 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
3417 obj_ctx
.obj
.set_atomic(obj
);
3418 obj_ctx
.obj
.set_prefetch_data(obj
);
3420 RGWObjState
* state
= nullptr;
3421 if (store
->get_obj_state(&obj_ctx
, s
->bucket_info
, obj
, &state
, false) < 0) {
3424 if (! state
->exists
) {
3427 return state
->exists
;
3430 int RGWHandler_REST_S3Website::retarget(RGWOp
* op
, RGWOp
** new_op
) {
3432 ldout(s
->cct
, 10) << __func__
<< "Starting retarget" << dendl
;
3434 if (!(s
->prot_flags
& RGW_REST_WEBSITE
))
3437 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
3438 int ret
= store
->get_bucket_info(obj_ctx
, s
->bucket_tenant
,
3439 s
->bucket_name
, s
->bucket_info
, NULL
,
3442 // TODO-FUTURE: if the bucket does not exist, maybe expose it here?
3443 return -ERR_NO_SUCH_BUCKET
;
3445 if (!s
->bucket_info
.has_website
) {
3446 // TODO-FUTURE: if the bucket has no WebsiteConfig, expose it here
3447 return -ERR_NO_SUCH_WEBSITE_CONFIGURATION
;
3450 rgw_obj_key new_obj
;
3451 s
->bucket_info
.website_conf
.get_effective_key(s
->object
.name
, &new_obj
.name
, web_dir());
3452 ldout(s
->cct
, 10) << "retarget get_effective_key " << s
->object
<< " -> "
3453 << new_obj
<< dendl
;
3455 RGWBWRoutingRule rrule
;
3456 bool should_redirect
=
3457 s
->bucket_info
.website_conf
.should_redirect(new_obj
.name
, 0, &rrule
);
3459 if (should_redirect
) {
3460 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
3461 const string
& protocol
=
3462 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
3463 int redirect_code
= 0;
3464 rrule
.apply_rule(protocol
, hostname
, s
->object
.name
, &s
->redirect
,
3466 // APply a custom HTTP response code
3467 if (redirect_code
> 0)
3468 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
3469 ldout(s
->cct
, 10) << "retarget redirect code=" << redirect_code
3470 << " proto+host:" << protocol
<< "://" << hostname
3471 << " -> " << s
->redirect
<< dendl
;
3472 return -ERR_WEBSITE_REDIRECT
;
3476 * FIXME: if s->object != new_obj, drop op and create a new op to handle
3477 * operation. Or remove this comment if it's not applicable anymore
3480 s
->object
= new_obj
;
3485 RGWOp
* RGWHandler_REST_S3Website::op_get()
3487 return get_obj_op(true);
3490 RGWOp
* RGWHandler_REST_S3Website::op_head()
3492 return get_obj_op(false);
3495 int RGWHandler_REST_S3Website::serve_errordoc(int http_ret
, const string
& errordoc_key
) {
3497 s
->formatter
->reset(); /* Try to throw it all away */
3499 std::shared_ptr
<RGWGetObj_ObjStore_S3Website
> getop( static_cast<RGWGetObj_ObjStore_S3Website
*>(op_get()));
3500 if (getop
.get() == NULL
) {
3501 return -1; // Trigger double error handler
3503 getop
->init(store
, s
, this);
3504 getop
->range_str
= NULL
;
3505 getop
->if_mod
= NULL
;
3506 getop
->if_unmod
= NULL
;
3507 getop
->if_match
= NULL
;
3508 getop
->if_nomatch
= NULL
;
3509 s
->object
= errordoc_key
;
3511 ret
= init_permissions(getop
.get());
3513 ldout(s
->cct
, 20) << "serve_errordoc failed, init_permissions ret=" << ret
<< dendl
;
3514 return -1; // Trigger double error handler
3517 ret
= read_permissions(getop
.get());
3519 ldout(s
->cct
, 20) << "serve_errordoc failed, read_permissions ret=" << ret
<< dendl
;
3520 return -1; // Trigger double error handler
3524 getop
->set_custom_http_response(http_ret
);
3527 ret
= getop
->init_processing();
3529 ldout(s
->cct
, 20) << "serve_errordoc failed, init_processing ret=" << ret
<< dendl
;
3530 return -1; // Trigger double error handler
3533 ret
= getop
->verify_op_mask();
3535 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_op_mask ret=" << ret
<< dendl
;
3536 return -1; // Trigger double error handler
3539 ret
= getop
->verify_permission();
3541 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_permission ret=" << ret
<< dendl
;
3542 return -1; // Trigger double error handler
3545 ret
= getop
->verify_params();
3547 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_params ret=" << ret
<< dendl
;
3548 return -1; // Trigger double error handler
3551 // No going back now
3554 * FIXME Missing headers:
3555 * With a working errordoc, the s3 error fields are rendered as HTTP headers,
3556 * x-amz-error-code: NoSuchKey
3557 * x-amz-error-message: The specified key does not exist.
3558 * x-amz-error-detail-Key: foo
3566 int RGWHandler_REST_S3Website::error_handler(int err_no
,
3567 string
* error_content
) {
3568 int new_err_no
= -1;
3569 rgw_http_errors::const_iterator r
= rgw_http_s3_errors
.find(err_no
> 0 ? err_no
: -err_no
);
3570 int http_error_code
= -1;
3572 if (r
!= rgw_http_s3_errors
.end()) {
3573 http_error_code
= r
->second
.first
;
3575 ldout(s
->cct
, 10) << "RGWHandler_REST_S3Website::error_handler err_no=" << err_no
<< " http_ret=" << http_error_code
<< dendl
;
3577 RGWBWRoutingRule rrule
;
3578 bool should_redirect
=
3579 s
->bucket_info
.website_conf
.should_redirect(s
->object
.name
, http_error_code
,
3582 if (should_redirect
) {
3583 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
3584 const string
& protocol
=
3585 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
3586 int redirect_code
= 0;
3587 rrule
.apply_rule(protocol
, hostname
, s
->object
.name
, &s
->redirect
,
3589 // Apply a custom HTTP response code
3590 if (redirect_code
> 0)
3591 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
3592 ldout(s
->cct
, 10) << "error handler redirect code=" << redirect_code
3593 << " proto+host:" << protocol
<< "://" << hostname
3594 << " -> " << s
->redirect
<< dendl
;
3595 return -ERR_WEBSITE_REDIRECT
;
3596 } else if (err_no
== -ERR_WEBSITE_REDIRECT
) {
3597 // Do nothing here, this redirect will be handled in abort_early's ERR_WEBSITE_REDIRECT block
3598 // Do NOT fire the ErrorDoc handler
3599 } else if (!s
->bucket_info
.website_conf
.error_doc
.empty()) {
3600 /* This serves an entire page!
3601 On success, it will return zero, and no further content should be sent to the socket
3602 On failure, we need the double-error handler
3604 new_err_no
= RGWHandler_REST_S3Website::serve_errordoc(http_error_code
, s
->bucket_info
.website_conf
.error_doc
);
3605 if (new_err_no
&& new_err_no
!= -1) {
3606 err_no
= new_err_no
;
3609 ldout(s
->cct
, 20) << "No special error handling today!" << dendl
;
3615 RGWOp
* RGWHandler_REST_Obj_S3Website::get_obj_op(bool get_data
)
3617 /** If we are in website mode, then it is explicitly impossible to run GET or
3618 * HEAD on the actual directory. We must convert the request to run on the
3619 * suffix object instead!
3621 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3622 op
->set_get_data(get_data
);
3626 RGWOp
* RGWHandler_REST_Bucket_S3Website::get_obj_op(bool get_data
)
3628 /** If we are in website mode, then it is explicitly impossible to run GET or
3629 * HEAD on the actual directory. We must convert the request to run on the
3630 * suffix object instead!
3632 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3633 op
->set_get_data(get_data
);
3637 RGWOp
* RGWHandler_REST_Service_S3Website::get_obj_op(bool get_data
)
3639 /** If we are in website mode, then it is explicitly impossible to run GET or
3640 * HEAD on the actual directory. We must convert the request to run on the
3641 * suffix object instead!
3643 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3644 op
->set_get_data(get_data
);
3653 bool AWSGeneralAbstractor::is_time_skew_ok(const utime_t
& header_time
,
3654 const bool qsr
) const
3656 /* Check for time skew first. */
3657 const time_t req_sec
= header_time
.sec();
3661 if ((req_sec
< now
- RGW_AUTH_GRACE_MINS
* 60 ||
3662 req_sec
> now
+ RGW_AUTH_GRACE_MINS
* 60) && !qsr
) {
3663 ldout(cct
, 10) << "req_sec=" << req_sec
<< " now=" << now
3664 << "; now - RGW_AUTH_GRACE_MINS="
3665 << now
- RGW_AUTH_GRACE_MINS
* 60
3666 << "; now + RGW_AUTH_GRACE_MINS="
3667 << now
+ RGW_AUTH_GRACE_MINS
* 60
3670 ldout(cct
, 0) << "NOTICE: request time skew too big now="
3672 << " req_time=" << header_time
3681 static rgw::auth::Completer::cmplptr_t
3682 null_completer_factory(const boost::optional
<std::string
>& secret_key
)
3688 AWSEngine::VersionAbstractor::auth_data_t
3689 AWSGeneralAbstractor::get_auth_data(const req_state
* const s
) const
3693 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
3695 if (version
== AwsVersion::V2
) {
3696 return get_auth_data_v2(s
);
3697 } else if (version
== AwsVersion::V4
) {
3698 return get_auth_data_v4(s
, route
== AwsRoute::QUERY_STRING
);
3700 /* FIXME(rzarzynski): handle anon user. */
3705 boost::optional
<std::string
>
3706 AWSGeneralAbstractor::get_v4_canonical_headers(
3707 const req_info
& info
,
3708 const boost::string_view
& signedheaders
,
3709 const bool using_qs
) const
3711 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
3715 AWSEngine::VersionAbstractor::auth_data_t
3716 AWSGeneralAbstractor::get_auth_data_v4(const req_state
* const s
,
3718 bool using_qs
) const
3720 boost::string_view access_key_id
;
3721 boost::string_view signed_hdrs
;
3723 boost::string_view date
;
3724 boost::string_view credential_scope
;
3725 boost::string_view client_signature
;
3727 int ret
= rgw::auth::s3::parse_credentials(s
->info
,
3738 /* craft canonical headers */
3739 boost::optional
<std::string
> canonical_headers
= \
3740 get_v4_canonical_headers(s
->info
, signed_hdrs
, using_qs
);
3741 if (canonical_headers
) {
3742 ldout(s
->cct
, 10) << "canonical headers format = " << *canonical_headers
3748 /* Get the expected hash. */
3749 auto exp_payload_hash
= rgw::auth::s3::get_v4_exp_payload_hash(s
->info
);
3751 /* Craft canonical URI. Using std::move later so let it be non-const. */
3752 auto canonical_uri
= rgw::auth::s3::get_v4_canonical_uri(s
->info
);
3754 /* Craft canonical query string. std::moving later so non-const here. */
3755 auto canonical_qs
= rgw::auth::s3::get_v4_canonical_qs(s
->info
, using_qs
);
3757 /* Craft canonical request. */
3758 auto canonical_req_hash
= \
3759 rgw::auth::s3::get_v4_canon_req_hash(s
->cct
,
3761 std::move(canonical_uri
),
3762 std::move(canonical_qs
),
3763 std::move(*canonical_headers
),
3767 auto string_to_sign
= \
3768 rgw::auth::s3::get_v4_string_to_sign(s
->cct
,
3769 AWS4_HMAC_SHA256_STR
,
3772 std::move(canonical_req_hash
));
3774 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
3776 std::placeholders::_1
,
3777 std::placeholders::_2
,
3778 std::placeholders::_3
);
3780 /* Requests authenticated with the Query Parameters are treated as unsigned.
3781 * From "Authenticating Requests: Using Query Parameters (AWS Signature
3784 * You don't include a payload hash in the Canonical Request, because
3785 * when you create a presigned URL, you don't know the payload content
3786 * because the URL is used to upload an arbitrary payload. Instead, you
3787 * use a constant string UNSIGNED-PAYLOAD.
3789 * This means we have absolutely no business in spawning completer. Both
3790 * aws4_auth_needs_complete and aws4_auth_streaming_mode are set to false
3791 * by default. We don't need to change that. */
3792 if (is_v4_payload_unsigned(exp_payload_hash
) || is_v4_payload_empty(s
)) {
3796 std::move(string_to_sign
),
3798 null_completer_factory
3801 /* We're going to handle a signed payload. Be aware that even empty HTTP
3802 * body (no payload) requires verification:
3804 * The x-amz-content-sha256 header is required for all AWS Signature
3805 * Version 4 requests. It provides a hash of the request payload. If
3806 * there is no payload, you must provide the hash of an empty string. */
3807 if (!is_v4_payload_streamed(exp_payload_hash
)) {
3808 ldout(s
->cct
, 10) << "delaying v4 auth" << dendl
;
3810 /* payload in a single chunk */
3813 case RGW_OP_CREATE_BUCKET
:
3814 case RGW_OP_PUT_OBJ
:
3815 case RGW_OP_PUT_ACLS
:
3816 case RGW_OP_PUT_CORS
:
3817 case RGW_OP_COMPLETE_MULTIPART
:
3818 case RGW_OP_SET_BUCKET_VERSIONING
:
3819 case RGW_OP_DELETE_MULTI_OBJ
:
3820 case RGW_OP_ADMIN_SET_METADATA
:
3821 case RGW_OP_SET_BUCKET_WEBSITE
:
3822 case RGW_OP_PUT_BUCKET_POLICY
:
3823 case RGW_OP_PUT_OBJ_TAGGING
:
3827 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl
;
3828 throw -ERR_NOT_IMPLEMENTED
;
3831 const auto cmpl_factory
= std::bind(AWSv4ComplSingle::create
,
3833 std::placeholders::_1
);
3837 std::move(string_to_sign
),
3842 /* IMHO "streamed" doesn't fit too good here. I would prefer to call
3843 * it "chunked" but let's be coherent with Amazon's terminology. */
3845 dout(10) << "body content detected in multiple chunks" << dendl
;
3847 /* payload in multiple chunks */
3851 case RGW_OP_PUT_OBJ
:
3854 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED (streaming mode)" << dendl
;
3855 throw -ERR_NOT_IMPLEMENTED
;
3858 dout(10) << "aws4 seed signature ok... delaying v4 auth" << dendl
;
3860 /* In the case of streamed payload client sets the x-amz-content-sha256
3861 * to "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" but uses "UNSIGNED-PAYLOAD"
3862 * when constructing the Canonical Request. */
3864 /* In the case of single-chunk upload client set the header's value is
3865 * coherent with the one used for Canonical Request crafting. */
3867 /* In the case of query string-based authentication there should be no
3868 * x-amz-content-sha256 header and the value "UNSIGNED-PAYLOAD" is used
3870 const auto cmpl_factory
= std::bind(AWSv4ComplMulti::create
,
3875 std::placeholders::_1
);
3879 std::move(string_to_sign
),
3888 boost::optional
<std::string
>
3889 AWSGeneralBoto2Abstractor::get_v4_canonical_headers(
3890 const req_info
& info
,
3891 const boost::string_view
& signedheaders
,
3892 const bool using_qs
) const
3894 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
3899 AWSEngine::VersionAbstractor::auth_data_t
3900 AWSGeneralAbstractor::get_auth_data_v2(const req_state
* const s
) const
3902 boost::string_view access_key_id
;
3903 boost::string_view signature
;
3906 const char* http_auth
= s
->info
.env
->get("HTTP_AUTHORIZATION");
3907 if (! http_auth
|| http_auth
[0] == '\0') {
3908 /* Credentials are provided in query string. We also need to verify
3909 * the "Expires" parameter now. */
3910 access_key_id
= s
->info
.args
.get("AWSAccessKeyId");
3911 signature
= s
->info
.args
.get("Signature");
3914 boost::string_view expires
= s
->info
.args
.get("Expires");
3915 if (! expires
.empty()) {
3916 /* It looks we have the guarantee that expires is a null-terminated,
3917 * and thus string_view::data() can be safely used. */
3918 const time_t exp
= atoll(expires
.data());
3927 /* The "Authorization" HTTP header is being used. */
3928 const boost::string_view
auth_str(http_auth
+ strlen("AWS "));
3929 const size_t pos
= auth_str
.rfind(':');
3930 if (pos
!= boost::string_view::npos
) {
3931 access_key_id
= auth_str
.substr(0, pos
);
3932 signature
= auth_str
.substr(pos
+ 1);
3936 /* Let's canonize the HTTP headers that are covered by the AWS auth v2. */
3937 std::string string_to_sign
;
3938 utime_t header_time
;
3939 if (! rgw_create_s3_canonical_header(s
->info
, &header_time
, string_to_sign
,
3941 ldout(cct
, 10) << "failed to create the canonized auth header\n"
3942 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
3946 ldout(cct
, 10) << "string_to_sign:\n"
3947 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
3949 if (! is_time_skew_ok(header_time
, qsr
)) {
3950 throw -ERR_REQUEST_TIME_SKEWED
;
3954 std::move(access_key_id
),
3955 std::move(signature
),
3956 std::move(string_to_sign
),
3957 rgw::auth::s3::get_v2_signature
,
3958 null_completer_factory
3963 AWSEngine::VersionAbstractor::auth_data_t
3964 AWSBrowserUploadAbstractor::get_auth_data_v2(const req_state
* const s
) const
3967 s
->auth
.s3_postobj_creds
.access_key
,
3968 s
->auth
.s3_postobj_creds
.signature
,
3969 s
->auth
.s3_postobj_creds
.encoded_policy
.to_str(),
3970 rgw::auth::s3::get_v2_signature
,
3971 null_completer_factory
3975 AWSEngine::VersionAbstractor::auth_data_t
3976 AWSBrowserUploadAbstractor::get_auth_data_v4(const req_state
* const s
) const
3978 const boost::string_view credential
= s
->auth
.s3_postobj_creds
.x_amz_credential
;
3980 /* grab access key id */
3981 const size_t pos
= credential
.find("/");
3982 const boost::string_view access_key_id
= credential
.substr(0, pos
);
3983 dout(10) << "access key id = " << access_key_id
<< dendl
;
3985 /* grab credential scope */
3986 const boost::string_view credential_scope
= credential
.substr(pos
+ 1);
3987 dout(10) << "credential scope = " << credential_scope
<< dendl
;
3989 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
3991 std::placeholders::_1
,
3992 std::placeholders::_2
,
3993 std::placeholders::_3
);
3997 s
->auth
.s3_postobj_creds
.signature
,
3998 s
->auth
.s3_postobj_creds
.encoded_policy
.to_str(),
4000 null_completer_factory
4004 AWSEngine::VersionAbstractor::auth_data_t
4005 AWSBrowserUploadAbstractor::get_auth_data(const req_state
* const s
) const
4007 if (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
) {
4008 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v4"
4009 << " (AWS4-HMAC-SHA256)" << dendl
;
4010 return get_auth_data_v4(s
);
4012 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v2" << dendl
;
4013 return get_auth_data_v2(s
);
4019 AWSEngine::authenticate(const req_state
* const s
) const
4021 /* Small reminder: an ver_abstractor is allowed to throw! */
4022 const auto auth_data
= ver_abstractor
.get_auth_data(s
);
4024 if (auth_data
.access_key_id
.empty() || auth_data
.client_signature
.empty()) {
4025 return result_t::deny(-EINVAL
);
4027 return authenticate(auth_data
.access_key_id
,
4028 auth_data
.client_signature
,
4029 auth_data
.string_to_sign
,
4030 auth_data
.signature_factory
,
4031 auth_data
.completer_factory
,
4036 } /* namespace s3 */
4037 } /* namespace auth */
4038 } /* namespace rgw */
4040 rgw::LDAPHelper
* rgw::auth::s3::LDAPEngine::ldh
= nullptr;
4041 std::mutex
rgw::auth::s3::LDAPEngine::mtx
;
4043 void rgw::auth::s3::LDAPEngine::init(CephContext
* const cct
)
4046 std::lock_guard
<std::mutex
> lck(mtx
);
4048 const string
& ldap_uri
= cct
->_conf
->rgw_ldap_uri
;
4049 const string
& ldap_binddn
= cct
->_conf
->rgw_ldap_binddn
;
4050 const string
& ldap_searchdn
= cct
->_conf
->rgw_ldap_searchdn
;
4051 const string
& ldap_searchfilter
= cct
->_conf
->rgw_ldap_searchfilter
;
4052 const string
& ldap_dnattr
= cct
->_conf
->rgw_ldap_dnattr
;
4053 std::string ldap_bindpw
= parse_rgw_ldap_bindpw(cct
);
4055 ldh
= new rgw::LDAPHelper(ldap_uri
, ldap_binddn
, ldap_bindpw
,
4056 ldap_searchdn
, ldap_searchfilter
, ldap_dnattr
);
4064 rgw::auth::RemoteApplier::acl_strategy_t
4065 rgw::auth::s3::LDAPEngine::get_acl_strategy() const
4067 //This is based on the assumption that the default acl strategy in
4068 // get_perms_from_aclspec, will take care. Extra acl spec is not required.
4072 rgw::auth::RemoteApplier::AuthInfo
4073 rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken
& token
) const noexcept
4075 /* The short form of "using" can't be used here -- we're aliasing a class'
4077 using acct_privilege_t
= \
4078 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t
;
4080 return rgw::auth::RemoteApplier::AuthInfo
{
4083 RGW_PERM_FULL_CONTROL
,
4084 acct_privilege_t::IS_PLAIN_ACCT
,
4089 rgw::auth::Engine::result_t
4090 rgw::auth::s3::LDAPEngine::authenticate(
4091 const boost::string_view
& access_key_id
,
4092 const boost::string_view
& signature
,
4093 const string_to_sign_t
& string_to_sign
,
4094 const signature_factory_t
&,
4095 const completer_factory_t
& completer_factory
,
4096 const req_state
* const s
) const
4098 /* boost filters and/or string_ref may throw on invalid input */
4099 rgw::RGWToken base64_token
;
4101 base64_token
= rgw::from_base64(access_key_id
);
4103 base64_token
= std::string("");
4106 if (! base64_token
.valid()) {
4107 return result_t::deny();
4110 //TODO: Uncomment, when we have a migration plan in place.
4111 //Check if a user of type other than 'ldap' is already present, if yes, then
4113 /*RGWUserInfo user_info;
4114 user_info.user_id = base64_token.id;
4115 if (rgw_get_user_info_by_uid(store, user_info.user_id, user_info) >= 0) {
4116 if (user_info.type != TYPE_LDAP) {
4117 ldout(cct, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl;
4122 if (ldh
->auth(base64_token
.id
, base64_token
.key
) != 0) {
4123 return result_t::deny();
4126 auto apl
= apl_factory
->create_apl_remote(cct
, s
, get_acl_strategy(),
4127 get_creds_info(base64_token
));
4128 return result_t::grant(std::move(apl
), completer_factory(boost::none
));
4133 rgw::auth::Engine::result_t
4134 rgw::auth::s3::LocalEngine::authenticate(
4135 const boost::string_view
& _access_key_id
,
4136 const boost::string_view
& signature
,
4137 const string_to_sign_t
& string_to_sign
,
4138 const signature_factory_t
& signature_factory
,
4139 const completer_factory_t
& completer_factory
,
4140 const req_state
* const s
) const
4142 /* get the user info */
4143 RGWUserInfo user_info
;
4144 /* TODO(rzarzynski): we need to have string-view taking variant. */
4145 const std::string access_key_id
= _access_key_id
.to_string();
4146 if (rgw_get_user_info_by_access_key(store
, access_key_id
, user_info
) < 0) {
4147 ldout(cct
, 5) << "error reading user info, uid=" << access_key_id
4148 << " can't authenticate" << dendl
;
4149 return result_t::deny(-ERR_INVALID_ACCESS_KEY
);
4151 //TODO: Uncomment, when we have a migration plan in place.
4153 if (s->user->type != TYPE_RGW) {
4154 ldout(cct, 10) << "ERROR: User id of type: " << s->user->type
4155 << " is present" << dendl;
4160 const auto iter
= user_info
.access_keys
.find(access_key_id
);
4161 if (iter
== std::end(user_info
.access_keys
)) {
4162 ldout(cct
, 0) << "ERROR: access key not encoded in user info" << dendl
;
4163 return result_t::deny(-EPERM
);
4165 const RGWAccessKey
& k
= iter
->second
;
4167 const VersionAbstractor::server_signature_t server_signature
= \
4168 signature_factory(cct
, k
.key
, string_to_sign
);
4170 ldout(cct
, 15) << "string_to_sign="
4171 << rgw::crypt_sanitize::log_content
{string_to_sign
}
4173 ldout(cct
, 15) << "server signature=" << server_signature
<< dendl
;
4174 ldout(cct
, 15) << "client signature=" << signature
<< dendl
;
4175 ldout(cct
, 15) << "compare=" << signature
.compare(server_signature
) << dendl
;
4177 if (static_cast<boost::string_view
>(server_signature
) != signature
) {
4178 return result_t::deny(-ERR_SIGNATURE_NO_MATCH
);
4181 auto apl
= apl_factory
->create_apl_local(cct
, s
, user_info
, k
.subuser
);
4182 return result_t::grant(std::move(apl
), completer_factory(k
.key
));
4185 bool rgw::auth::s3::S3AnonymousEngine::is_applicable(
4188 if (s
->op
== OP_OPTIONS
) {
4194 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
4196 return route
== AwsRoute::QUERY_STRING
&& version
== AwsVersion::UNKOWN
;