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 <boost/algorithm/string.hpp>
14 #include <boost/algorithm/string/replace.hpp>
15 #include <boost/utility/string_view.hpp>
18 #include "rgw_rest_s3.h"
19 #include "rgw_rest_s3website.h"
20 #include "rgw_auth_s3.h"
22 #include "rgw_policy_s3.h"
25 #include "rgw_cors_s3.h"
27 #include "rgw_client_io.h"
29 #include "rgw_keystone.h"
30 #include "rgw_auth_keystone.h"
31 #include "rgw_auth_registry.h"
33 #include "rgw_es_query.h"
35 #include <typeinfo> // for 'typeid'
38 #include "rgw_token.h"
39 #include "rgw_rest_role.h"
40 #include "rgw_crypt.h"
41 #include "rgw_crypt_sanitize.h"
43 #include "include/assert.h"
45 #define dout_context g_ceph_context
46 #define dout_subsys ceph_subsys_rgw
49 using namespace ceph::crypto
;
53 void list_all_buckets_start(struct req_state
*s
)
55 s
->formatter
->open_array_section_in_ns("ListAllMyBucketsResult", XMLNS_AWS_S3
);
58 void list_all_buckets_end(struct req_state
*s
)
60 s
->formatter
->close_section();
63 void dump_bucket(struct req_state
*s
, RGWBucketEnt
& obj
)
65 s
->formatter
->open_object_section("Bucket");
66 s
->formatter
->dump_string("Name", obj
.bucket
.name
);
67 dump_time(s
, "CreationDate", &obj
.creation_time
);
68 s
->formatter
->close_section();
71 void rgw_get_errno_s3(rgw_http_error
*e
, int err_no
)
73 rgw_http_errors::const_iterator r
= rgw_http_s3_errors
.find(err_no
);
75 if (r
!= rgw_http_s3_errors
.end()) {
76 e
->http_ret
= r
->second
.first
;
77 e
->s3_code
= r
->second
.second
;
80 e
->s3_code
= "UnknownError";
84 struct response_attr_param
{
86 const char *http_attr
;
89 static struct response_attr_param resp_attr_params
[] = {
90 {"response-content-type", "Content-Type"},
91 {"response-content-language", "Content-Language"},
92 {"response-expires", "Expires"},
93 {"response-cache-control", "Cache-Control"},
94 {"response-content-disposition", "Content-Disposition"},
95 {"response-content-encoding", "Content-Encoding"},
99 int RGWGetObj_ObjStore_S3Website::send_response_data(bufferlist
& bl
, off_t bl_ofs
, off_t bl_len
) {
100 map
<string
, bufferlist
>::iterator iter
;
101 iter
= attrs
.find(RGW_ATTR_AMZ_WEBSITE_REDIRECT_LOCATION
);
102 if (iter
!= attrs
.end()) {
103 bufferlist
&bl
= iter
->second
;
104 s
->redirect
= string(bl
.c_str(), bl
.length());
105 s
->err
.http_ret
= 301;
106 ldout(s
->cct
, 20) << __CEPH_ASSERT_FUNCTION
<< " redirecting per x-amz-website-redirect-location=" << s
->redirect
<< dendl
;
107 op_ret
= -ERR_WEBSITE_REDIRECT
;
108 set_req_state_err(s
, op_ret
);
110 dump_content_length(s
, 0);
111 dump_redirect(s
, s
->redirect
);
115 return RGWGetObj_ObjStore_S3::send_response_data(bl
, bl_ofs
, bl_len
);
119 int RGWGetObj_ObjStore_S3Website::send_response_data_error()
121 return RGWGetObj_ObjStore_S3::send_response_data_error();
124 int RGWGetObj_ObjStore_S3::get_params()
126 // for multisite sync requests, only read the slo manifest itself, rather than
127 // all of the data from its parts. the parts will sync as separate objects
128 skip_manifest
= s
->info
.args
.exists(RGW_SYS_PARAM_PREFIX
"sync-manifest");
130 return RGWGetObj_ObjStore::get_params();
133 int RGWGetObj_ObjStore_S3::send_response_data_error()
136 return send_response_data(bl
, 0 , 0);
140 int decode_attr_bl_single_value(map
<string
, bufferlist
>& attrs
, const char *attr_name
, T
*result
, T def_val
)
142 map
<string
, bufferlist
>::iterator iter
= attrs
.find(attr_name
);
143 if (iter
== attrs
.end()) {
147 bufferlist
& bl
= iter
->second
;
148 if (bl
.length() == 0) {
152 bufferlist::iterator bliter
= bl
.begin();
154 ::decode(*result
, bliter
);
155 } catch (buffer::error
& err
) {
161 int RGWGetObj_ObjStore_S3::send_response_data(bufferlist
& bl
, off_t bl_ofs
,
164 const char *content_type
= NULL
;
165 string content_type_str
;
166 map
<string
, string
> response_attrs
;
167 map
<string
, string
>::iterator riter
;
168 bufferlist metadata_bl
;
173 if (custom_http_ret
) {
174 set_req_state_err(s
, 0);
175 dump_errno(s
, custom_http_ret
);
177 set_req_state_err(s
, (partial_content
&& !op_ret
) ? STATUS_PARTIAL_CONTENT
186 dump_range(s
, start
, end
, s
->obj_size
);
188 if (s
->system_request
&&
189 s
->info
.args
.exists(RGW_SYS_PARAM_PREFIX
"prepend-metadata")) {
191 dump_header(s
, "Rgwx-Object-Size", (long long)total_len
);
195 * in this case, we're not returning the object's content, only the prepended
201 /* JSON encode object metadata */
203 jf
.open_object_section("obj_metadata");
204 encode_json("attrs", attrs
, &jf
);
206 encode_json("mtime", ut
, &jf
);
210 metadata_bl
.append(ss
.str());
211 dump_header(s
, "Rgwx-Embedded-Metadata-Len", metadata_bl
.length());
212 total_len
+= metadata_bl
.length();
215 if (s
->system_request
&& !real_clock::is_zero(lastmod
)) {
216 /* we end up dumping mtime in two different methods, a bit redundant */
217 dump_epoch_header(s
, "Rgwx-Mtime", lastmod
);
219 int r
= decode_attr_bl_single_value(attrs
, RGW_ATTR_PG_VER
, &pg_ver
, (uint64_t)0);
221 ldout(s
->cct
, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl
;
223 dump_header(s
, "Rgwx-Obj-PG-Ver", pg_ver
);
225 uint32_t source_zone_short_id
= 0;
226 r
= decode_attr_bl_single_value(attrs
, RGW_ATTR_SOURCE_ZONE
, &source_zone_short_id
, (uint32_t)0);
228 ldout(s
->cct
, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl
;
230 if (source_zone_short_id
!= 0) {
231 dump_header(s
, "Rgwx-Source-Zone-Short-Id", source_zone_short_id
);
235 for (auto &it
: crypt_http_responses
)
236 dump_header(s
, it
.first
, it
.second
);
238 dump_content_length(s
, total_len
);
239 dump_last_modified(s
, lastmod
);
240 if (!version_id
.empty()) {
241 dump_header(s
, "x-amz-version-id", version_id
);
246 if (! lo_etag
.empty()) {
247 /* Handle etag of Swift API's large objects (DLO/SLO). It's entirerly
248 * legit to perform GET on them through S3 API. In such situation,
249 * a client should receive the composited content with corresponding
251 dump_etag(s
, lo_etag
);
253 auto iter
= attrs
.find(RGW_ATTR_ETAG
);
254 if (iter
!= attrs
.end()) {
255 dump_etag(s
, iter
->second
);
259 for (struct response_attr_param
*p
= resp_attr_params
; p
->param
; p
++) {
261 string val
= s
->info
.args
.get(p
->param
, &exists
);
263 if (strcmp(p
->param
, "response-content-type") != 0) {
264 response_attrs
[p
->http_attr
] = val
;
266 content_type_str
= val
;
267 content_type
= content_type_str
.c_str();
272 for (auto iter
= attrs
.begin(); iter
!= attrs
.end(); ++iter
) {
273 const char *name
= iter
->first
.c_str();
274 map
<string
, string
>::iterator aiter
= rgw_to_http_attrs
.find(name
);
275 if (aiter
!= rgw_to_http_attrs
.end()) {
276 if (response_attrs
.count(aiter
->second
) == 0) {
277 /* Was not already overridden by a response param. */
278 response_attrs
[aiter
->second
] = iter
->second
.c_str();
280 } else if (iter
->first
.compare(RGW_ATTR_CONTENT_TYPE
) == 0) {
281 /* Special handling for content_type. */
283 content_type
= iter
->second
.c_str();
285 } else if (strcmp(name
, RGW_ATTR_SLO_UINDICATOR
) == 0) {
286 // this attr has an extra length prefix from ::encode() in prior versions
287 dump_header(s
, "X-Object-Meta-Static-Large-Object", "True");
288 } else if (strncmp(name
, RGW_ATTR_META_PREFIX
,
289 sizeof(RGW_ATTR_META_PREFIX
)-1) == 0) {
290 /* User custom metadata. */
291 name
+= sizeof(RGW_ATTR_PREFIX
) - 1;
292 dump_header(s
, name
, iter
->second
);
298 for (riter
= response_attrs
.begin(); riter
!= response_attrs
.end();
300 dump_header(s
, riter
->first
, riter
->second
);
303 if (op_ret
== -ERR_NOT_MODIFIED
) {
307 content_type
= "binary/octet-stream";
309 end_header(s
, this, content_type
);
312 if (metadata_bl
.length()) {
313 dump_body(s
, metadata_bl
);
318 if (get_data
&& !op_ret
) {
319 int r
= dump_body(s
, bl
.c_str() + bl_ofs
, bl_len
);
327 int RGWGetObj_ObjStore_S3::get_decrypt_filter(std::unique_ptr
<RGWGetDataCB
> *filter
, RGWGetDataCB
* cb
, bufferlist
* manifest_bl
)
330 std::unique_ptr
<BlockCrypt
> block_crypt
;
331 res
= rgw_s3_prepare_decrypt(s
, attrs
, &block_crypt
, crypt_http_responses
);
333 if (block_crypt
!= nullptr) {
334 auto f
= std::unique_ptr
<RGWGetObj_BlockDecrypt
>(new RGWGetObj_BlockDecrypt(s
->cct
, cb
, std::move(block_crypt
)));
335 //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
337 if (manifest_bl
!= nullptr) {
338 res
= f
->read_manifest(*manifest_bl
);
340 *filter
= std::move(f
);
350 void RGWListBuckets_ObjStore_S3::send_response_begin(bool has_buckets
)
353 set_req_state_err(s
, op_ret
);
356 end_header(s
, NULL
, "application/xml");
359 list_all_buckets_start(s
);
360 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
);
361 s
->formatter
->open_array_section("Buckets");
366 void RGWListBuckets_ObjStore_S3::send_response_data(RGWUserBuckets
& buckets
)
371 map
<string
, RGWBucketEnt
>& m
= buckets
.get_buckets();
372 map
<string
, RGWBucketEnt
>::iterator iter
;
374 for (iter
= m
.begin(); iter
!= m
.end(); ++iter
) {
375 RGWBucketEnt obj
= iter
->second
;
378 rgw_flush_formatter(s
, s
->formatter
);
381 void RGWListBuckets_ObjStore_S3::send_response_end()
384 s
->formatter
->close_section();
385 list_all_buckets_end(s
);
386 rgw_flush_formatter_and_reset(s
, s
->formatter
);
390 int RGWGetUsage_ObjStore_S3::get_params()
392 start_date
= s
->info
.args
.get("start-date");
393 end_date
= s
->info
.args
.get("end-date");
397 static void dump_usage_categories_info(Formatter
*formatter
, const rgw_usage_log_entry
& entry
, map
<string
, bool> *categories
)
399 formatter
->open_array_section("categories");
400 map
<string
, rgw_usage_data
>::const_iterator uiter
;
401 for (uiter
= entry
.usage_map
.begin(); uiter
!= entry
.usage_map
.end(); ++uiter
) {
402 if (categories
&& !categories
->empty() && !categories
->count(uiter
->first
))
404 const rgw_usage_data
& usage
= uiter
->second
;
405 formatter
->open_object_section("Entry");
406 formatter
->dump_string("Category", uiter
->first
);
407 formatter
->dump_int("BytesSent", usage
.bytes_sent
);
408 formatter
->dump_int("BytesReceived", usage
.bytes_received
);
409 formatter
->dump_int("Ops", usage
.ops
);
410 formatter
->dump_int("SuccessfulOps", usage
.successful_ops
);
411 formatter
->close_section(); // Entry
413 formatter
->close_section(); // Category
416 void RGWGetUsage_ObjStore_S3::send_response()
419 set_req_state_err(s
, op_ret
);
422 end_header(s
, this, "application/xml");
427 Formatter
*formatter
= s
->formatter
;
429 bool user_section_open
= false;
431 formatter
->open_object_section("Usage");
432 if (show_log_entries
) {
433 formatter
->open_array_section("Entries");
435 map
<rgw_user_bucket
, rgw_usage_log_entry
>::iterator iter
;
436 for (iter
= usage
.begin(); iter
!= usage
.end(); ++iter
) {
437 const rgw_user_bucket
& ub
= iter
->first
;
438 const rgw_usage_log_entry
& entry
= iter
->second
;
440 if (show_log_entries
) {
441 if (ub
.user
.compare(last_owner
) != 0) {
442 if (user_section_open
) {
443 formatter
->close_section();
444 formatter
->close_section();
446 formatter
->open_object_section("User");
447 formatter
->dump_string("Owner", ub
.user
);
448 formatter
->open_array_section("Buckets");
449 user_section_open
= true;
450 last_owner
= ub
.user
;
452 formatter
->open_object_section("Bucket");
453 formatter
->dump_string("Bucket", ub
.bucket
);
454 utime_t
ut(entry
.epoch
, 0);
455 ut
.gmtime(formatter
->dump_stream("Time"));
456 formatter
->dump_int("Epoch", entry
.epoch
);
457 dump_usage_categories_info(formatter
, entry
, &categories
);
458 formatter
->close_section(); // bucket
461 summary_map
[ub
.user
].aggregate(entry
, &categories
);
464 if (show_log_entries
) {
465 if (user_section_open
) {
466 formatter
->close_section(); // buckets
467 formatter
->close_section(); //user
469 formatter
->close_section(); // entries
473 formatter
->open_array_section("Summary");
474 map
<string
, rgw_usage_log_entry
>::iterator siter
;
475 for (siter
= summary_map
.begin(); siter
!= summary_map
.end(); ++siter
) {
476 const rgw_usage_log_entry
& entry
= siter
->second
;
477 formatter
->open_object_section("User");
478 formatter
->dump_string("User", siter
->first
);
479 dump_usage_categories_info(formatter
, entry
, &categories
);
480 rgw_usage_data total_usage
;
481 entry
.sum(total_usage
, categories
);
482 formatter
->open_object_section("Total");
483 formatter
->dump_int("BytesSent", total_usage
.bytes_sent
);
484 formatter
->dump_int("BytesReceived", total_usage
.bytes_received
);
485 formatter
->dump_int("Ops", total_usage
.ops
);
486 formatter
->dump_int("SuccessfulOps", total_usage
.successful_ops
);
487 formatter
->close_section(); // total
488 formatter
->close_section(); // user
491 if (s
->cct
->_conf
->rgw_rest_getusage_op_compat
) {
492 formatter
->open_object_section("Stats");
495 formatter
->dump_int("TotalBytes", header
.stats
.total_bytes
);
496 formatter
->dump_int("TotalBytesRounded", header
.stats
.total_bytes_rounded
);
497 formatter
->dump_int("TotalEntries", header
.stats
.total_entries
);
499 if (s
->cct
->_conf
->rgw_rest_getusage_op_compat
) {
500 formatter
->close_section(); //Stats
503 formatter
->close_section(); // summary
505 formatter
->close_section(); // usage
506 rgw_flush_formatter_and_reset(s
, s
->formatter
);
509 int RGWListBucket_ObjStore_S3::get_params()
511 list_versions
= s
->info
.args
.exists("versions");
512 prefix
= s
->info
.args
.get("prefix");
513 if (!list_versions
) {
514 marker
= s
->info
.args
.get("marker");
516 marker
.name
= s
->info
.args
.get("key-marker");
517 marker
.instance
= s
->info
.args
.get("version-id-marker");
519 max_keys
= s
->info
.args
.get("max-keys");
520 op_ret
= parse_max_keys();
524 delimiter
= s
->info
.args
.get("delimiter");
525 encoding_type
= s
->info
.args
.get("encoding-type");
526 if (s
->system_request
) {
527 s
->info
.args
.get_bool("objs-container", &objs_container
, false);
528 const char *shard_id_str
= s
->info
.env
->get("HTTP_RGWX_SHARD_ID");
531 shard_id
= strict_strtol(shard_id_str
, 10, &err
);
533 ldout(s
->cct
, 5) << "bad shard id specified: " << shard_id_str
<< dendl
;
537 shard_id
= s
->bucket_instance_shard_id
;
543 void RGWListBucket_ObjStore_S3::send_versioned_response()
545 s
->formatter
->open_object_section_in_ns("ListVersionsResult", XMLNS_AWS_S3
);
546 if (!s
->bucket_tenant
.empty())
547 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
548 s
->formatter
->dump_string("Name", s
->bucket_name
);
549 s
->formatter
->dump_string("Prefix", prefix
);
550 s
->formatter
->dump_string("KeyMarker", marker
.name
);
551 s
->formatter
->dump_string("VersionIdMarker", marker
.instance
);
552 if (is_truncated
&& !next_marker
.empty()) {
553 s
->formatter
->dump_string("NextKeyMarker", next_marker
.name
);
554 s
->formatter
->dump_string("NextVersionIdMarker", next_marker
.instance
);
556 s
->formatter
->dump_int("MaxKeys", max
);
557 if (!delimiter
.empty())
558 s
->formatter
->dump_string("Delimiter", delimiter
);
560 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
563 bool encode_key
= false;
564 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
565 s
->formatter
->dump_string("EncodingType", "url");
570 if (objs_container
) {
571 s
->formatter
->open_array_section("Entries");
574 vector
<rgw_bucket_dir_entry
>::iterator iter
;
575 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
576 const char *section_name
= (iter
->is_delete_marker() ? "DeleteMarker"
578 s
->formatter
->open_object_section(section_name
);
579 if (objs_container
) {
580 s
->formatter
->dump_bool("IsDeleteMarker", iter
->is_delete_marker());
582 rgw_obj_key
key(iter
->key
);
585 url_encode(key
.name
, key_name
);
586 s
->formatter
->dump_string("Key", key_name
);
588 s
->formatter
->dump_string("Key", key
.name
);
590 string version_id
= key
.instance
;
591 if (version_id
.empty()) {
594 if (s
->system_request
) {
595 if (iter
->versioned_epoch
> 0) {
596 s
->formatter
->dump_int("VersionedEpoch", iter
->versioned_epoch
);
598 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
599 utime_t
ut(iter
->meta
.mtime
);
600 ut
.gmtime_nsec(s
->formatter
->dump_stream("RgwxMtime"));
602 s
->formatter
->dump_string("VersionId", version_id
);
603 s
->formatter
->dump_bool("IsLatest", iter
->is_current());
604 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
605 if (!iter
->is_delete_marker()) {
606 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
607 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
608 s
->formatter
->dump_string("StorageClass", "STANDARD");
610 dump_owner(s
, iter
->meta
.owner
, iter
->meta
.owner_display_name
);
611 s
->formatter
->close_section();
613 if (objs_container
) {
614 s
->formatter
->close_section();
617 if (!common_prefixes
.empty()) {
618 map
<string
, bool>::iterator pref_iter
;
619 for (pref_iter
= common_prefixes
.begin();
620 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
621 s
->formatter
->open_array_section("CommonPrefixes");
622 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
623 s
->formatter
->close_section();
627 s
->formatter
->close_section();
628 rgw_flush_formatter_and_reset(s
, s
->formatter
);
631 void RGWListBucket_ObjStore_S3::send_response()
634 set_req_state_err(s
, op_ret
);
637 end_header(s
, this, "application/xml");
643 send_versioned_response();
647 s
->formatter
->open_object_section_in_ns("ListBucketResult", XMLNS_AWS_S3
);
648 if (!s
->bucket_tenant
.empty())
649 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
650 s
->formatter
->dump_string("Name", s
->bucket_name
);
651 s
->formatter
->dump_string("Prefix", prefix
);
652 s
->formatter
->dump_string("Marker", marker
.name
);
653 if (is_truncated
&& !next_marker
.empty())
654 s
->formatter
->dump_string("NextMarker", next_marker
.name
);
655 s
->formatter
->dump_int("MaxKeys", max
);
656 if (!delimiter
.empty())
657 s
->formatter
->dump_string("Delimiter", delimiter
);
659 s
->formatter
->dump_string("IsTruncated", (max
&& is_truncated
? "true"
662 bool encode_key
= false;
663 if (strcasecmp(encoding_type
.c_str(), "url") == 0) {
664 s
->formatter
->dump_string("EncodingType", "url");
669 vector
<rgw_bucket_dir_entry
>::iterator iter
;
670 for (iter
= objs
.begin(); iter
!= objs
.end(); ++iter
) {
671 rgw_obj_key
key(iter
->key
);
672 s
->formatter
->open_array_section("Contents");
675 url_encode(key
.name
, key_name
);
676 s
->formatter
->dump_string("Key", key_name
);
678 s
->formatter
->dump_string("Key", key
.name
);
680 dump_time(s
, "LastModified", &iter
->meta
.mtime
);
681 s
->formatter
->dump_format("ETag", "\"%s\"", iter
->meta
.etag
.c_str());
682 s
->formatter
->dump_int("Size", iter
->meta
.accounted_size
);
683 s
->formatter
->dump_string("StorageClass", "STANDARD");
684 dump_owner(s
, iter
->meta
.owner
, iter
->meta
.owner_display_name
);
685 if (s
->system_request
) {
686 s
->formatter
->dump_string("RgwxTag", iter
->tag
);
688 s
->formatter
->close_section();
690 if (!common_prefixes
.empty()) {
691 map
<string
, bool>::iterator pref_iter
;
692 for (pref_iter
= common_prefixes
.begin();
693 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
694 s
->formatter
->open_array_section("CommonPrefixes");
695 s
->formatter
->dump_string("Prefix", pref_iter
->first
);
696 s
->formatter
->close_section();
700 s
->formatter
->close_section();
701 rgw_flush_formatter_and_reset(s
, s
->formatter
);
704 void RGWGetBucketLogging_ObjStore_S3::send_response()
707 end_header(s
, this, "application/xml");
710 s
->formatter
->open_object_section_in_ns("BucketLoggingStatus", XMLNS_AWS_S3
);
711 s
->formatter
->close_section();
712 rgw_flush_formatter_and_reset(s
, s
->formatter
);
715 void RGWGetBucketLocation_ObjStore_S3::send_response()
721 RGWZoneGroup zonegroup
;
724 int ret
= store
->get_zonegroup(s
->bucket_info
.zonegroup
, zonegroup
);
726 api_name
= zonegroup
.api_name
;
728 if (s
->bucket_info
.zonegroup
!= "default") {
729 api_name
= s
->bucket_info
.zonegroup
;
733 s
->formatter
->dump_format_ns("LocationConstraint", XMLNS_AWS_S3
,
734 "%s", api_name
.c_str());
735 rgw_flush_formatter_and_reset(s
, s
->formatter
);
738 void RGWGetBucketVersioning_ObjStore_S3::send_response()
741 end_header(s
, this, "application/xml");
744 s
->formatter
->open_object_section_in_ns("VersioningConfiguration", XMLNS_AWS_S3
);
746 const char *status
= (versioning_enabled
? "Enabled" : "Suspended");
747 s
->formatter
->dump_string("Status", status
);
749 s
->formatter
->close_section();
750 rgw_flush_formatter_and_reset(s
, s
->formatter
);
753 class RGWSetBucketVersioningParser
: public RGWXMLParser
755 XMLObj
*alloc_obj(const char *el
) override
{
760 RGWSetBucketVersioningParser() {}
761 ~RGWSetBucketVersioningParser() override
{}
763 int get_versioning_status(bool *status
) {
764 XMLObj
*config
= find_first("VersioningConfiguration");
770 XMLObj
*field
= config
->find_first("Status");
774 string
& s
= field
->get_data();
776 if (stringcasecmp(s
, "Enabled") == 0) {
778 } else if (stringcasecmp(s
, "Suspended") != 0) {
786 int RGWSetBucketVersioning_ObjStore_S3::get_params()
788 char *data
= nullptr;
791 rgw_rest_read_all_input(s
, &data
, &len
, s
->cct
->_conf
->rgw_max_put_param_size
, false);
796 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
798 r
= do_aws4_auth_completion();
803 RGWSetBucketVersioningParser parser
;
805 if (!parser
.init()) {
806 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
811 if (!parser
.parse(data
, len
, 1)) {
812 ldout(s
->cct
, 10) << "failed to parse data: " << data
<< dendl
;
817 if (!store
->is_meta_master()) {
818 /* only need to keep this data around if we're not meta master */
819 in_data
.append(data
, len
);
822 r
= parser
.get_versioning_status(&enable_versioning
);
827 void RGWSetBucketVersioning_ObjStore_S3::send_response()
830 set_req_state_err(s
, op_ret
);
835 int RGWSetBucketWebsite_ObjStore_S3::get_params()
837 char *data
= nullptr;
839 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
840 int r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
846 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
848 r
= do_aws4_auth_completion();
853 bufferptr
in_ptr(data
, len
);
854 in_data
.append(in_ptr
);
856 RGWXMLDecoder::XMLParser parser
;
857 if (!parser
.init()) {
858 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
862 if (!parser
.parse(data
, len
, 1)) {
863 string
str(data
, len
);
864 ldout(s
->cct
, 5) << "failed to parse xml: " << str
<< dendl
;
869 RGWXMLDecoder::decode_xml("WebsiteConfiguration", website_conf
, &parser
, true);
870 } catch (RGWXMLDecoder::err
& err
) {
871 string
str(data
, len
);
872 ldout(s
->cct
, 5) << "unexpected xml: " << str
<< dendl
;
879 void RGWSetBucketWebsite_ObjStore_S3::send_response()
882 set_req_state_err(s
, op_ret
);
887 void RGWDeleteBucketWebsite_ObjStore_S3::send_response()
890 op_ret
= STATUS_NO_CONTENT
;
892 set_req_state_err(s
, op_ret
);
897 void RGWGetBucketWebsite_ObjStore_S3::send_response()
900 set_req_state_err(s
, op_ret
);
902 end_header(s
, this, "application/xml");
909 RGWBucketWebsiteConf
& conf
= s
->bucket_info
.website_conf
;
911 s
->formatter
->open_object_section_in_ns("WebsiteConfiguration", XMLNS_AWS_S3
);
912 conf
.dump_xml(s
->formatter
);
913 s
->formatter
->close_section(); // WebsiteConfiguration
914 rgw_flush_formatter_and_reset(s
, s
->formatter
);
917 static void dump_bucket_metadata(struct req_state
*s
, RGWBucketEnt
& bucket
)
919 dump_header(s
, "X-RGW-Object-Count", static_cast<long long>(bucket
.count
));
920 dump_header(s
, "X-RGW-Bytes-Used", static_cast<long long>(bucket
.size
));
923 void RGWStatBucket_ObjStore_S3::send_response()
926 dump_bucket_metadata(s
, bucket
);
929 set_req_state_err(s
, op_ret
);
936 static int create_s3_policy(struct req_state
*s
, RGWRados
*store
,
937 RGWAccessControlPolicy_S3
& s3policy
,
940 if (s
->has_acl_header
) {
941 if (!s
->canned_acl
.empty())
942 return -ERR_INVALID_REQUEST
;
944 return s3policy
.create_from_headers(store
, s
->info
.env
, owner
);
947 return s3policy
.create_canned(owner
, s
->bucket_owner
, s
->canned_acl
);
950 class RGWLocationConstraint
: public XMLObj
953 RGWLocationConstraint() {}
954 ~RGWLocationConstraint() override
{}
955 bool xml_end(const char *el
) override
{
959 location_constraint
= get_data();
964 string location_constraint
;
967 class RGWCreateBucketConfig
: public XMLObj
970 RGWCreateBucketConfig() {}
971 ~RGWCreateBucketConfig() override
{}
974 class RGWCreateBucketParser
: public RGWXMLParser
976 XMLObj
*alloc_obj(const char *el
) override
{
981 RGWCreateBucketParser() {}
982 ~RGWCreateBucketParser() override
{}
984 bool get_location_constraint(string
& zone_group
) {
985 XMLObj
*config
= find_first("CreateBucketConfiguration");
989 XMLObj
*constraint
= config
->find_first("LocationConstraint");
993 zone_group
= constraint
->get_data();
999 int RGWCreateBucket_ObjStore_S3::get_params()
1001 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1003 int r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
1010 char *data
= nullptr;
1012 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
1013 op_ret
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
1015 if ((op_ret
< 0) && (op_ret
!= -ERR_LENGTH_REQUIRED
))
1018 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
1020 const int auth_ret
= do_aws4_auth_completion();
1025 bufferptr
in_ptr(data
, len
);
1026 in_data
.append(in_ptr
);
1029 RGWCreateBucketParser parser
;
1031 if (!parser
.init()) {
1032 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
1036 bool success
= parser
.parse(data
, len
, 1);
1037 ldout(s
->cct
, 20) << "create bucket input data=" << data
<< dendl
;
1040 ldout(s
->cct
, 0) << "failed to parse input: " << data
<< dendl
;
1044 if (!parser
.get_location_constraint(location_constraint
)) {
1045 ldout(s
->cct
, 0) << "provided input did not specify location constraint correctly" << dendl
;
1049 ldout(s
->cct
, 10) << "create bucket location constraint: "
1050 << location_constraint
<< dendl
;
1053 size_t pos
= location_constraint
.find(':');
1054 if (pos
!= string::npos
) {
1055 placement_rule
= location_constraint
.substr(pos
+ 1);
1056 location_constraint
= location_constraint
.substr(0, pos
);
1062 void RGWCreateBucket_ObjStore_S3::send_response()
1064 if (op_ret
== -ERR_BUCKET_EXISTS
)
1067 set_req_state_err(s
, op_ret
);
1074 if (s
->system_request
) {
1075 JSONFormatter f
; /* use json formatter for system requests output */
1077 f
.open_object_section("info");
1078 encode_json("entry_point_object_ver", ep_objv
, &f
);
1079 encode_json("object_ver", info
.objv_tracker
.read_version
, &f
);
1080 encode_json("bucket_info", info
, &f
);
1082 rgw_flush_formatter_and_reset(s
, &f
);
1086 void RGWDeleteBucket_ObjStore_S3::send_response()
1090 r
= STATUS_NO_CONTENT
;
1092 set_req_state_err(s
, r
);
1094 end_header(s
, this);
1096 if (s
->system_request
) {
1097 JSONFormatter f
; /* use json formatter for system requests output */
1099 f
.open_object_section("info");
1100 encode_json("object_ver", objv_tracker
.read_version
, &f
);
1102 rgw_flush_formatter_and_reset(s
, &f
);
1106 int RGWPutObj_ObjStore_S3::get_params()
1109 return -ERR_LENGTH_REQUIRED
;
1111 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
1112 map
<string
, bufferlist
> src_attrs
;
1116 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1117 ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
1123 if_match
= s
->info
.env
->get("HTTP_IF_MATCH");
1124 if_nomatch
= s
->info
.env
->get("HTTP_IF_NONE_MATCH");
1125 copy_source
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE");
1126 copy_source_range
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE");
1128 /* handle x-amz-copy-source */
1131 copy_source_bucket_name
= copy_source
;
1132 pos
= copy_source_bucket_name
.find("/");
1133 if (pos
== std::string::npos
) {
1135 ldout(s
->cct
, 5) << "x-amz-copy-source bad format" << dendl
;
1138 copy_source_object_name
= copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
1139 copy_source_bucket_name
= copy_source_bucket_name
.substr(0, pos
);
1140 #define VERSION_ID_STR "?versionId="
1141 pos
= copy_source_object_name
.find(VERSION_ID_STR
);
1142 if (pos
== std::string::npos
) {
1143 copy_source_object_name
= url_decode(copy_source_object_name
);
1145 copy_source_version_id
= copy_source_object_name
.substr(pos
+ sizeof(VERSION_ID_STR
) - 1);
1146 copy_source_object_name
= url_decode(copy_source_object_name
.substr(0, pos
));
1148 pos
= copy_source_bucket_name
.find(":");
1149 if (pos
== std::string::npos
) {
1150 copy_source_tenant_name
= s
->src_tenant_name
;
1152 copy_source_tenant_name
= copy_source_bucket_name
.substr(0, pos
);
1153 copy_source_bucket_name
= copy_source_bucket_name
.substr(pos
+ 1, copy_source_bucket_name
.size());
1154 if (copy_source_bucket_name
.empty()) {
1156 ldout(s
->cct
, 5) << "source bucket name is empty" << dendl
;
1160 ret
= store
->get_bucket_info(obj_ctx
,
1161 copy_source_tenant_name
,
1162 copy_source_bucket_name
,
1163 copy_source_bucket_info
,
1166 ldout(s
->cct
, 5) << __func__
<< "(): get_bucket_info() returned ret=" << ret
<< dendl
;
1170 /* handle x-amz-copy-source-range */
1172 if (copy_source_range
) {
1173 string range
= copy_source_range
;
1174 pos
= range
.find("=");
1175 if (pos
== std::string::npos
) {
1177 ldout(s
->cct
, 5) << "x-amz-copy-source-range bad format" << dendl
;
1180 range
= range
.substr(pos
+ 1);
1181 pos
= range
.find("-");
1182 if (pos
== std::string::npos
) {
1184 ldout(s
->cct
, 5) << "x-amz-copy-source-range bad format" << dendl
;
1187 string first
= range
.substr(0, pos
);
1188 string last
= range
.substr(pos
+ 1);
1189 copy_source_range_fst
= strtoull(first
.c_str(), NULL
, 10);
1190 copy_source_range_lst
= strtoull(last
.c_str(), NULL
, 10);
1195 return RGWPutObj_ObjStore::get_params();
1198 int RGWPutObj_ObjStore_S3::get_data(bufferlist
& bl
)
1200 const int ret
= RGWPutObj_ObjStore::get_data(bl
);
1202 const int ret_auth
= do_aws4_auth_completion();
1211 static int get_success_retcode(int code
)
1215 return STATUS_CREATED
;
1217 return STATUS_NO_CONTENT
;
1222 void RGWPutObj_ObjStore_S3::send_response()
1225 set_req_state_err(s
, op_ret
);
1228 if (s
->cct
->_conf
->rgw_s3_success_create_obj_status
) {
1229 op_ret
= get_success_retcode(
1230 s
->cct
->_conf
->rgw_s3_success_create_obj_status
);
1231 set_req_state_err(s
, op_ret
);
1236 dump_content_length(s
, 0);
1237 for (auto &it
: crypt_http_responses
)
1238 dump_header(s
, it
.first
, it
.second
);
1241 end_header(s
, this, "application/xml");
1245 time_t secs
= (time_t)ut
.sec();
1246 gmtime_r(&secs
, &tmp
);
1247 char buf
[TIME_BUF_SIZE
];
1248 s
->formatter
->open_object_section_in_ns("CopyPartResult",
1249 "http://s3.amazonaws.com/doc/2006-03-01/");
1250 if (strftime(buf
, sizeof(buf
), "%Y-%m-%dT%T.000Z", &tmp
) > 0) {
1251 s
->formatter
->dump_string("LastModified", buf
);
1253 s
->formatter
->dump_string("ETag", etag
);
1254 s
->formatter
->close_section();
1255 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1259 if (s
->system_request
&& !real_clock::is_zero(mtime
)) {
1260 dump_epoch_header(s
, "Rgwx-Mtime", mtime
);
1262 end_header(s
, this);
1265 static inline int get_obj_attrs(RGWRados
*store
, struct req_state
*s
, rgw_obj
& obj
, map
<string
, bufferlist
>& attrs
)
1267 RGWRados::Object
op_target(store
, s
->bucket_info
, *static_cast<RGWObjectCtx
*>(s
->obj_ctx
), obj
);
1268 RGWRados::Object::Read
read_op(&op_target
);
1270 read_op
.params
.attrs
= &attrs
;
1272 return read_op
.prepare();
1275 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const std::string
& value
)
1279 attrs
.emplace(key
, std::move(bl
));
1282 static inline void set_attr(map
<string
, bufferlist
>& attrs
, const char* key
, const char* value
)
1286 attrs
.emplace(key
, std::move(bl
));
1289 int RGWPutObj_ObjStore_S3::get_decrypt_filter(
1290 std::unique_ptr
<RGWGetDataCB
>* filter
,
1292 map
<string
, bufferlist
>& attrs
,
1293 bufferlist
* manifest_bl
)
1295 std::map
<std::string
, std::string
> crypt_http_responses_unused
;
1298 std::unique_ptr
<BlockCrypt
> block_crypt
;
1299 res
= rgw_s3_prepare_decrypt(s
, attrs
, &block_crypt
, crypt_http_responses_unused
);
1301 if (block_crypt
!= nullptr) {
1302 auto f
= std::unique_ptr
<RGWGetObj_BlockDecrypt
>(new RGWGetObj_BlockDecrypt(s
->cct
, cb
, std::move(block_crypt
)));
1303 //RGWGetObj_BlockDecrypt* f = new RGWGetObj_BlockDecrypt(s->cct, cb, std::move(block_crypt));
1305 if (manifest_bl
!= nullptr) {
1306 res
= f
->read_manifest(*manifest_bl
);
1308 *filter
= std::move(f
);
1317 int RGWPutObj_ObjStore_S3::get_encrypt_filter(
1318 std::unique_ptr
<RGWPutObjDataProcessor
>* filter
,
1319 RGWPutObjDataProcessor
* cb
)
1322 RGWPutObjProcessor_Multipart
* multi_processor
=dynamic_cast<RGWPutObjProcessor_Multipart
*>(cb
);
1323 if (multi_processor
!= nullptr) {
1324 RGWMPObj
* mp
= nullptr;
1325 multi_processor
->get_mp(&mp
);
1326 if (mp
!= nullptr) {
1327 map
<string
, bufferlist
> xattrs
;
1329 meta_oid
= mp
->get_meta();
1332 obj
.init_ns(s
->bucket
, meta_oid
, RGW_OBJ_NS_MULTIPART
);
1333 obj
.set_in_extra_data(true);
1334 res
= get_obj_attrs(store
, s
, obj
, xattrs
);
1336 std::unique_ptr
<BlockCrypt
> block_crypt
;
1337 /* We are adding to existing object.
1338 * We use crypto mode that configured as if we were decrypting. */
1339 res
= rgw_s3_prepare_decrypt(s
, xattrs
, &block_crypt
, crypt_http_responses
);
1340 if (res
== 0 && block_crypt
!= nullptr)
1341 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
1342 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1345 /* it is ok, to not have encryption at all */
1349 std::unique_ptr
<BlockCrypt
> block_crypt
;
1350 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, &block_crypt
, crypt_http_responses
);
1351 if (res
== 0 && block_crypt
!= nullptr) {
1352 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
1353 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1359 void RGWPostObj_ObjStore_S3::rebuild_key(string
& key
)
1361 static string var
= "${filename}";
1362 int pos
= key
.find(var
);
1366 string new_key
= key
.substr(0, pos
);
1367 new_key
.append(filename
);
1368 new_key
.append(key
.substr(pos
+ var
.size()));
1373 std::string
RGWPostObj_ObjStore_S3::get_current_filename() const
1375 return s
->object
.name
;
1378 std::string
RGWPostObj_ObjStore_S3::get_current_content_type() const
1380 return content_type
;
1383 int RGWPostObj_ObjStore_S3::get_params()
1385 op_ret
= RGWPostObj_ObjStore::get_params();
1390 ldout(s
->cct
, 20) << "adding bucket to policy env: " << s
->bucket
.name
1392 env
.add_var("bucket", s
->bucket
.name
);
1396 struct post_form_part part
;
1397 int r
= read_form_part_header(&part
, done
);
1401 if (s
->cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 20)) {
1402 ldout(s
->cct
, 20) << "read part header -- part.name="
1403 << part
.name
<< dendl
;
1405 for (const auto& pair
: part
.fields
) {
1406 ldout(s
->cct
, 20) << "field.name=" << pair
.first
<< dendl
;
1407 ldout(s
->cct
, 20) << "field.val=" << pair
.second
.val
<< dendl
;
1408 ldout(s
->cct
, 20) << "field.params:" << dendl
;
1410 for (const auto& param_pair
: pair
.second
.params
) {
1411 ldout(s
->cct
, 20) << " " << param_pair
.first
1412 << " -> " << param_pair
.second
<< dendl
;
1417 if (done
) { /* unexpected here */
1418 err_msg
= "Malformed request";
1422 if (stringcasecmp(part
.name
, "file") == 0) { /* beginning of data transfer */
1423 struct post_part_field
& field
= part
.fields
["Content-Disposition"];
1424 map
<string
, string
>::iterator iter
= field
.params
.find("filename");
1425 if (iter
!= field
.params
.end()) {
1426 filename
= iter
->second
;
1428 parts
[part
.name
] = part
;
1433 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1434 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
1436 err_msg
= "Couldn't find boundary";
1439 parts
[part
.name
] = part
;
1440 string
part_str(part
.data
.c_str(), part
.data
.length());
1441 env
.add_var(part
.name
, part_str
);
1445 if (!part_str(parts
, "key", &object_str
)) {
1446 err_msg
= "Key not specified";
1450 s
->object
= rgw_obj_key(object_str
);
1452 rebuild_key(s
->object
.name
);
1454 if (s
->object
.empty()) {
1455 err_msg
= "Empty object name";
1459 env
.add_var("key", s
->object
.name
);
1461 part_str(parts
, "Content-Type", &content_type
);
1462 env
.add_var("Content-Type", content_type
);
1464 map
<string
, struct post_form_part
, ltstr_nocase
>::iterator piter
=
1465 parts
.upper_bound(RGW_AMZ_META_PREFIX
);
1466 for (; piter
!= parts
.end(); ++piter
) {
1467 string n
= piter
->first
;
1468 if (strncasecmp(n
.c_str(), RGW_AMZ_META_PREFIX
,
1469 sizeof(RGW_AMZ_META_PREFIX
) - 1) != 0)
1472 string attr_name
= RGW_ATTR_PREFIX
;
1473 attr_name
.append(n
);
1475 /* need to null terminate it */
1476 bufferlist
& data
= piter
->second
.data
;
1477 string str
= string(data
.c_str(), data
.length());
1480 attr_bl
.append(str
.c_str(), str
.size() + 1);
1482 attrs
[attr_name
] = attr_bl
;
1484 // TODO: refactor this and the above loop to share code
1485 piter
= parts
.find(RGW_AMZ_WEBSITE_REDIRECT_LOCATION
);
1486 if (piter
!= parts
.end()) {
1487 string n
= piter
->first
;
1488 string attr_name
= RGW_ATTR_PREFIX
;
1489 attr_name
.append(n
);
1490 /* need to null terminate it */
1491 bufferlist
& data
= piter
->second
.data
;
1492 string str
= string(data
.c_str(), data
.length());
1495 attr_bl
.append(str
.c_str(), str
.size() + 1);
1497 attrs
[attr_name
] = attr_bl
;
1500 int r
= get_policy();
1504 min_len
= post_policy
.min_length
;
1505 max_len
= post_policy
.max_length
;
1510 int RGWPostObj_ObjStore_S3::get_policy()
1512 if (part_bl(parts
, "policy", &s
->auth
.s3_postobj_creds
.encoded_policy
)) {
1513 bool aws4_auth
= false;
1515 /* x-amz-algorithm handling */
1516 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
1517 if ((part_str(parts
, "x-amz-algorithm", &s
->auth
.s3_postobj_creds
.x_amz_algorithm
)) &&
1518 (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
)) {
1519 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v4 (AWS4-HMAC-SHA256)" << dendl
;
1522 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v2" << dendl
;
1525 // check that the signature matches the encoded policy
1529 /* x-amz-credential handling */
1530 if (!part_str(parts
, "x-amz-credential",
1531 &s
->auth
.s3_postobj_creds
.x_amz_credential
)) {
1532 ldout(s
->cct
, 0) << "No S3 aws4 credential found!" << dendl
;
1533 err_msg
= "Missing aws4 credential";
1537 /* x-amz-signature handling */
1538 if (!part_str(parts
, "x-amz-signature",
1539 &s
->auth
.s3_postobj_creds
.signature
)) {
1540 ldout(s
->cct
, 0) << "No aws4 signature found!" << dendl
;
1541 err_msg
= "Missing aws4 signature";
1545 /* x-amz-date handling */
1546 std::string received_date_str
;
1547 if (!part_str(parts
, "x-amz-date", &received_date_str
)) {
1548 ldout(s
->cct
, 0) << "No aws4 date found!" << dendl
;
1549 err_msg
= "Missing aws4 date";
1555 // check that the signature matches the encoded policy
1556 if (!part_str(parts
, "AWSAccessKeyId",
1557 &s
->auth
.s3_postobj_creds
.access_key
)) {
1558 ldout(s
->cct
, 0) << "No S3 aws2 access key found!" << dendl
;
1559 err_msg
= "Missing aws2 access key";
1563 if (!part_str(parts
, "signature", &s
->auth
.s3_postobj_creds
.signature
)) {
1564 ldout(s
->cct
, 0) << "No aws2 signature found!" << dendl
;
1565 err_msg
= "Missing aws2 signature";
1570 /* FIXME: this is a makeshift solution. The browser upload authentication will be
1571 * handled by an instance of rgw::auth::Completer spawned in Handler's authorize()
1573 const int ret
= rgw::auth::Strategy::apply(auth_registry_ptr
->get_s3_post(), s
);
1577 /* Populate the owner info. */
1578 s
->owner
.set_id(s
->user
->user_id
);
1579 s
->owner
.set_name(s
->user
->display_name
);
1580 ldout(s
->cct
, 0) << "Successful Signature Verification!" << dendl
;
1583 ceph::bufferlist decoded_policy
;
1585 decoded_policy
.decode_base64(s
->auth
.s3_postobj_creds
.encoded_policy
);
1586 } catch (buffer::error
& err
) {
1587 ldout(s
->cct
, 0) << "failed to decode_base64 policy" << dendl
;
1588 err_msg
= "Could not decode policy";
1592 decoded_policy
.append('\0'); // NULL terminate
1593 ldout(s
->cct
, 0) << "POST policy: " << decoded_policy
.c_str() << dendl
;
1596 int r
= post_policy
.from_json(decoded_policy
, err_msg
);
1598 if (err_msg
.empty()) {
1599 err_msg
= "Failed to parse policy";
1601 ldout(s
->cct
, 0) << "failed to parse policy" << dendl
;
1607 post_policy
.set_var_checked("x-amz-signature");
1610 post_policy
.set_var_checked("AWSAccessKeyId");
1611 post_policy
.set_var_checked("signature");
1613 post_policy
.set_var_checked("policy");
1615 r
= post_policy
.check(&env
, err_msg
);
1617 if (err_msg
.empty()) {
1618 err_msg
= "Policy check failed";
1620 ldout(s
->cct
, 0) << "policy check failed" << dendl
;
1625 ldout(s
->cct
, 0) << "No attached policy found!" << dendl
;
1629 part_str(parts
, "acl", &canned_acl
);
1631 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1632 ldout(s
->cct
, 20) << "canned_acl=" << canned_acl
<< dendl
;
1633 if (s3policy
.create_canned(s
->owner
, s
->bucket_owner
, canned_acl
) < 0) {
1634 err_msg
= "Bad canned ACLs";
1643 int RGWPostObj_ObjStore_S3::complete_get_params()
1647 struct post_form_part part
;
1648 int r
= read_form_part_header(&part
, done
);
1653 ceph::bufferlist part_data
;
1655 uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1656 r
= read_data(part
.data
, chunk_size
, boundary
, done
);
1661 /* Just reading the data but not storing any results of that. */
1667 int RGWPostObj_ObjStore_S3::get_data(ceph::bufferlist
& bl
, bool& again
)
1672 const uint64_t chunk_size
= s
->cct
->_conf
->rgw_max_chunk_size
;
1673 int r
= read_data(bl
, chunk_size
, boundary
, done
);
1680 /* Reached end of data, let's drain the rest of the params */
1681 r
= complete_get_params();
1692 void RGWPostObj_ObjStore_S3::send_response()
1694 if (op_ret
== 0 && parts
.count("success_action_redirect")) {
1697 part_str(parts
, "success_action_redirect", &redirect
);
1702 string etag_str
= "\"";
1704 etag_str
.append(etag
);
1705 etag_str
.append("\"");
1709 url_encode(s
->bucket_tenant
, tenant
); /* surely overkill, but cheap */
1710 url_encode(s
->bucket_name
, bucket
);
1711 url_encode(s
->object
.name
, key
);
1712 url_encode(etag_str
, etag_url
);
1714 if (!s
->bucket_tenant
.empty()) {
1716 * What we really would like is to quaily the bucket name, so
1717 * that the client could simply copy it and paste into next request.
1718 * Unfortunately, in S3 we cannot know if the client will decide
1719 * to come through DNS, with "bucket.tenant" sytanx, or through
1720 * URL with "tenant\bucket" syntax. Therefore, we provide the
1721 * tenant separately.
1723 redirect
.append("?tenant=");
1724 redirect
.append(tenant
);
1725 redirect
.append("&bucket=");
1726 redirect
.append(bucket
);
1728 redirect
.append("?bucket=");
1729 redirect
.append(bucket
);
1731 redirect
.append("&key=");
1732 redirect
.append(key
);
1733 redirect
.append("&etag=");
1734 redirect
.append(etag_url
);
1736 int r
= check_utf8(redirect
.c_str(), redirect
.size());
1741 dump_redirect(s
, redirect
);
1742 op_ret
= STATUS_REDIRECT
;
1743 } else if (op_ret
== 0 && parts
.count("success_action_status")) {
1744 string status_string
;
1745 uint32_t status_int
;
1747 part_str(parts
, "success_action_status", &status_string
);
1749 int r
= stringtoul(status_string
, &status_int
);
1755 switch (status_int
) {
1759 op_ret
= STATUS_CREATED
;
1762 op_ret
= STATUS_NO_CONTENT
;
1765 } else if (! op_ret
) {
1766 op_ret
= STATUS_NO_CONTENT
;
1770 if (op_ret
== STATUS_CREATED
) {
1771 for (auto &it
: crypt_http_responses
)
1772 dump_header(s
, it
.first
, it
.second
);
1773 s
->formatter
->open_object_section("PostResponse");
1774 if (g_conf
->rgw_dns_name
.length())
1775 s
->formatter
->dump_format("Location", "%s/%s",
1776 s
->info
.script_uri
.c_str(),
1777 s
->object
.name
.c_str());
1778 if (!s
->bucket_tenant
.empty())
1779 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
1780 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
1781 s
->formatter
->dump_string("Key", s
->object
.name
);
1782 s
->formatter
->close_section();
1784 s
->err
.message
= err_msg
;
1785 set_req_state_err(s
, op_ret
);
1788 dump_content_length(s
, s
->formatter
->get_len());
1790 end_header(s
, this);
1791 if (op_ret
!= STATUS_CREATED
)
1794 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1797 int RGWPostObj_ObjStore_S3::get_encrypt_filter(
1798 std::unique_ptr
<RGWPutObjDataProcessor
>* filter
, RGWPutObjDataProcessor
* cb
)
1801 std::unique_ptr
<BlockCrypt
> block_crypt
;
1802 res
= rgw_s3_prepare_encrypt(s
, attrs
, &parts
, &block_crypt
, crypt_http_responses
);
1803 if (res
== 0 && block_crypt
!= nullptr) {
1804 *filter
= std::unique_ptr
<RGWPutObj_BlockEncrypt
>(
1805 new RGWPutObj_BlockEncrypt(s
->cct
, cb
, std::move(block_crypt
)));
1812 int RGWDeleteObj_ObjStore_S3::get_params()
1814 const char *if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_DELETE_IF_UNMODIFIED_SINCE");
1816 if (s
->system_request
) {
1817 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"no-precondition-error", &no_precondition_error
, false);
1821 std::string if_unmod_decoded
= url_decode(if_unmod
);
1824 if (utime_t::parse_date(if_unmod_decoded
, &epoch
, &nsec
) < 0) {
1825 ldout(s
->cct
, 10) << "failed to parse time: " << if_unmod_decoded
<< dendl
;
1828 unmod_since
= utime_t(epoch
, nsec
).to_real_time();
1834 void RGWDeleteObj_ObjStore_S3::send_response()
1840 r
= STATUS_NO_CONTENT
;
1842 set_req_state_err(s
, r
);
1844 if (!version_id
.empty()) {
1845 dump_header(s
, "x-amz-version-id", version_id
);
1847 if (delete_marker
) {
1848 dump_header(s
, "x-amz-delete-marker", "true");
1850 end_header(s
, this);
1853 int RGWCopyObj_ObjStore_S3::init_dest_policy()
1855 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1857 /* build a policy for the target object */
1858 int r
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
1862 dest_policy
= s3policy
;
1867 int RGWCopyObj_ObjStore_S3::get_params()
1869 if_mod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MODIFIED_SINCE");
1870 if_unmod
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_UNMODIFIED_SINCE");
1871 if_match
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_MATCH");
1872 if_nomatch
= s
->info
.env
->get("HTTP_X_AMZ_COPY_IF_NONE_MATCH");
1874 src_tenant_name
= s
->src_tenant_name
;
1875 src_bucket_name
= s
->src_bucket_name
;
1876 src_object
= s
->src_object
;
1877 dest_tenant_name
= s
->bucket
.tenant
;
1878 dest_bucket_name
= s
->bucket
.name
;
1879 dest_object
= s
->object
.name
;
1881 if (s
->system_request
) {
1882 source_zone
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"source-zone");
1883 s
->info
.args
.get_bool(RGW_SYS_PARAM_PREFIX
"copy-if-newer", ©_if_newer
, false);
1884 if (!source_zone
.empty()) {
1885 client_id
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"client-id");
1886 op_id
= s
->info
.args
.get(RGW_SYS_PARAM_PREFIX
"op-id");
1888 if (client_id
.empty() || op_id
.empty()) {
1890 RGW_SYS_PARAM_PREFIX
"client-id or "
1891 RGW_SYS_PARAM_PREFIX
"op-id were not provided, "
1892 "required for intra-region copy"
1899 const char *md_directive
= s
->info
.env
->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
1901 if (strcasecmp(md_directive
, "COPY") == 0) {
1902 attrs_mod
= RGWRados::ATTRSMOD_NONE
;
1903 } else if (strcasecmp(md_directive
, "REPLACE") == 0) {
1904 attrs_mod
= RGWRados::ATTRSMOD_REPLACE
;
1905 } else if (!source_zone
.empty()) {
1906 attrs_mod
= RGWRados::ATTRSMOD_NONE
; // default for intra-zone_group copy
1908 ldout(s
->cct
, 0) << "invalid metadata directive" << dendl
;
1913 if (source_zone
.empty() &&
1914 (dest_tenant_name
.compare(src_tenant_name
) == 0) &&
1915 (dest_bucket_name
.compare(src_bucket_name
) == 0) &&
1916 (dest_object
.compare(src_object
.name
) == 0) &&
1917 src_object
.instance
.empty() &&
1918 (attrs_mod
!= RGWRados::ATTRSMOD_REPLACE
)) {
1919 /* can only copy object into itself if replacing attrs */
1920 ldout(s
->cct
, 0) << "can't copy object into itself if not replacing attrs"
1922 return -ERR_INVALID_REQUEST
;
1927 void RGWCopyObj_ObjStore_S3::send_partial_response(off_t ofs
)
1929 if (! sent_header
) {
1931 set_req_state_err(s
, op_ret
);
1934 end_header(s
, this, "application/xml");
1936 s
->formatter
->open_object_section_in_ns("CopyObjectResult", XMLNS_AWS_S3
);
1940 /* Send progress field. Note that this diverge from the original S3
1941 * spec. We do this in order to keep connection alive.
1943 s
->formatter
->dump_int("Progress", (uint64_t)ofs
);
1945 rgw_flush_formatter(s
, s
->formatter
);
1948 void RGWCopyObj_ObjStore_S3::send_response()
1951 send_partial_response(0);
1954 dump_time(s
, "LastModified", &mtime
);
1955 std::string etag_str
= etag
.to_str();
1956 if (! etag_str
.empty()) {
1957 s
->formatter
->dump_string("ETag", std::move(etag_str
));
1959 s
->formatter
->close_section();
1960 rgw_flush_formatter_and_reset(s
, s
->formatter
);
1964 void RGWGetACLs_ObjStore_S3::send_response()
1967 set_req_state_err(s
, op_ret
);
1969 end_header(s
, this, "application/xml");
1971 rgw_flush_formatter(s
, s
->formatter
);
1975 int RGWPutACLs_ObjStore_S3::get_params()
1977 int ret
= RGWPutACLs_ObjStore::get_params();
1979 const int ret_auth
= do_aws4_auth_completion();
1987 int RGWPutACLs_ObjStore_S3::get_policy_from_state(RGWRados
*store
,
1988 struct req_state
*s
,
1991 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
1993 // bucket-* canned acls do not apply to bucket
1994 if (s
->object
.empty()) {
1995 if (s
->canned_acl
.find("bucket") != string::npos
)
1996 s
->canned_acl
.clear();
1999 int r
= create_s3_policy(s
, store
, s3policy
, owner
);
2003 s3policy
.to_xml(ss
);
2008 void RGWPutACLs_ObjStore_S3::send_response()
2011 set_req_state_err(s
, op_ret
);
2013 end_header(s
, this, "application/xml");
2017 void RGWGetLC_ObjStore_S3::execute()
2019 config
.set_ctx(s
->cct
);
2021 map
<string
, bufferlist
>::iterator aiter
= s
->bucket_attrs
.find(RGW_ATTR_LC
);
2022 if (aiter
== s
->bucket_attrs
.end()) {
2027 bufferlist::iterator
iter(&aiter
->second
);
2029 config
.decode(iter
);
2030 } catch (const buffer::error
& e
) {
2031 ldout(s
->cct
, 0) << __func__
<< "decode life cycle config failed" << dendl
;
2037 void RGWGetLC_ObjStore_S3::send_response()
2040 if (op_ret
== -ENOENT
) {
2041 set_req_state_err(s
, ERR_NO_SUCH_LC
);
2043 set_req_state_err(s
, op_ret
);
2047 end_header(s
, this, "application/xml");
2053 config
.dump_xml(s
->formatter
);
2054 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2057 void RGWPutLC_ObjStore_S3::send_response()
2060 set_req_state_err(s
, op_ret
);
2062 end_header(s
, this, "application/xml");
2066 void RGWDeleteLC_ObjStore_S3::send_response()
2069 op_ret
= STATUS_NO_CONTENT
;
2071 set_req_state_err(s
, op_ret
);
2074 end_header(s
, this, "application/xml");
2078 void RGWGetCORS_ObjStore_S3::send_response()
2081 if (op_ret
== -ENOENT
)
2082 set_req_state_err(s
, ERR_NOT_FOUND
);
2084 set_req_state_err(s
, op_ret
);
2087 end_header(s
, NULL
, "application/xml");
2091 RGWCORSConfiguration_S3
*s3cors
=
2092 static_cast<RGWCORSConfiguration_S3
*>(&bucket_cors
);
2101 int RGWPutCORS_ObjStore_S3::get_params()
2104 char *data
= nullptr;
2106 RGWCORSXMLParser_S3
parser(s
->cct
);
2107 RGWCORSConfiguration_S3
*cors_config
;
2109 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2110 r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
2115 auto data_deleter
= std::unique_ptr
<char, decltype(free
)*>{data
, free
};
2117 r
= do_aws4_auth_completion();
2122 if (!parser
.init()) {
2126 if (!data
|| !parser
.parse(data
, len
, 1)) {
2130 static_cast<RGWCORSConfiguration_S3
*>(parser
.find_first(
2131 "CORSConfiguration"));
2136 // forward bucket cors requests to meta master zone
2137 if (!store
->is_meta_master()) {
2138 /* only need to keep this data around if we're not meta master */
2139 in_data
.append(data
, len
);
2142 if (s
->cct
->_conf
->subsys
.should_gather(ceph_subsys_rgw
, 15)) {
2143 ldout(s
->cct
, 15) << "CORSConfiguration";
2144 cors_config
->to_xml(*_dout
);
2148 cors_config
->encode(cors_bl
);
2153 void RGWPutCORS_ObjStore_S3::send_response()
2156 set_req_state_err(s
, op_ret
);
2158 end_header(s
, NULL
, "application/xml");
2162 void RGWDeleteCORS_ObjStore_S3::send_response()
2165 if (!r
|| r
== -ENOENT
)
2166 r
= STATUS_NO_CONTENT
;
2168 set_req_state_err(s
, r
);
2170 end_header(s
, NULL
);
2173 void RGWOptionsCORS_ObjStore_S3::send_response()
2175 string hdrs
, exp_hdrs
;
2176 uint32_t max_age
= CORS_MAX_AGE_INVALID
;
2177 /*EACCES means, there is no CORS registered yet for the bucket
2178 *ENOENT means, there is no match of the Origin in the list of CORSRule
2180 if (op_ret
== -ENOENT
)
2183 set_req_state_err(s
, op_ret
);
2185 end_header(s
, NULL
);
2188 get_response_params(hdrs
, exp_hdrs
, &max_age
);
2191 dump_access_control(s
, origin
, req_meth
, hdrs
.c_str(), exp_hdrs
.c_str(),
2193 end_header(s
, NULL
);
2196 void RGWGetRequestPayment_ObjStore_S3::send_response()
2199 end_header(s
, this, "application/xml");
2202 s
->formatter
->open_object_section_in_ns("RequestPaymentConfiguration", XMLNS_AWS_S3
);
2203 const char *payer
= requester_pays
? "Requester" : "BucketOwner";
2204 s
->formatter
->dump_string("Payer", payer
);
2205 s
->formatter
->close_section();
2206 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2209 class RGWSetRequestPaymentParser
: public RGWXMLParser
2211 XMLObj
*alloc_obj(const char *el
) override
{
2216 RGWSetRequestPaymentParser() {}
2217 ~RGWSetRequestPaymentParser() override
{}
2219 int get_request_payment_payer(bool *requester_pays
) {
2220 XMLObj
*config
= find_first("RequestPaymentConfiguration");
2224 *requester_pays
= false;
2226 XMLObj
*field
= config
->find_first("Payer");
2230 string
& s
= field
->get_data();
2232 if (stringcasecmp(s
, "Requester") == 0) {
2233 *requester_pays
= true;
2234 } else if (stringcasecmp(s
, "BucketOwner") != 0) {
2242 int RGWSetRequestPayment_ObjStore_S3::get_params()
2246 const auto max_size
= s
->cct
->_conf
->rgw_max_put_param_size
;
2247 int r
= rgw_rest_read_all_input(s
, &data
, &len
, max_size
, false);
2253 RGWSetRequestPaymentParser parser
;
2255 if (!parser
.init()) {
2256 ldout(s
->cct
, 0) << "ERROR: failed to initialize parser" << dendl
;
2261 if (!parser
.parse(data
, len
, 1)) {
2262 ldout(s
->cct
, 10) << "failed to parse data: " << data
<< dendl
;
2267 r
= parser
.get_request_payment_payer(&requester_pays
);
2275 void RGWSetRequestPayment_ObjStore_S3::send_response()
2278 set_req_state_err(s
, op_ret
);
2283 int RGWInitMultipart_ObjStore_S3::get_params()
2285 RGWAccessControlPolicy_S3
s3policy(s
->cct
);
2286 op_ret
= create_s3_policy(s
, store
, s3policy
, s
->owner
);
2295 void RGWInitMultipart_ObjStore_S3::send_response()
2298 set_req_state_err(s
, op_ret
);
2300 for (auto &it
: crypt_http_responses
)
2301 dump_header(s
, it
.first
, it
.second
);
2302 end_header(s
, this, "application/xml");
2305 s
->formatter
->open_object_section_in_ns("InitiateMultipartUploadResult", XMLNS_AWS_S3
);
2306 if (!s
->bucket_tenant
.empty())
2307 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2308 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2309 s
->formatter
->dump_string("Key", s
->object
.name
);
2310 s
->formatter
->dump_string("UploadId", upload_id
);
2311 s
->formatter
->close_section();
2312 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2316 int RGWInitMultipart_ObjStore_S3::prepare_encryption(map
<string
, bufferlist
>& attrs
)
2319 res
= rgw_s3_prepare_encrypt(s
, attrs
, nullptr, nullptr, crypt_http_responses
);
2323 int RGWCompleteMultipart_ObjStore_S3::get_params()
2325 int ret
= RGWCompleteMultipart_ObjStore::get_params();
2330 return do_aws4_auth_completion();
2333 void RGWCompleteMultipart_ObjStore_S3::send_response()
2336 set_req_state_err(s
, op_ret
);
2338 end_header(s
, this, "application/xml");
2341 s
->formatter
->open_object_section_in_ns("CompleteMultipartUploadResult", XMLNS_AWS_S3
);
2342 if (!s
->bucket_tenant
.empty()) {
2343 if (s
->info
.domain
.length()) {
2344 s
->formatter
->dump_format("Location", "%s.%s.%s",
2345 s
->bucket_name
.c_str(),
2346 s
->bucket_tenant
.c_str(),
2347 s
->info
.domain
.c_str());
2349 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2351 if (s
->info
.domain
.length()) {
2352 s
->formatter
->dump_format("Location", "%s.%s",
2353 s
->bucket_name
.c_str(),
2354 s
->info
.domain
.c_str());
2357 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2358 s
->formatter
->dump_string("Key", s
->object
.name
);
2359 s
->formatter
->dump_string("ETag", etag
);
2360 s
->formatter
->close_section();
2361 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2365 void RGWAbortMultipart_ObjStore_S3::send_response()
2369 r
= STATUS_NO_CONTENT
;
2371 set_req_state_err(s
, r
);
2373 end_header(s
, this);
2376 void RGWListMultipart_ObjStore_S3::send_response()
2379 set_req_state_err(s
, op_ret
);
2381 end_header(s
, this, "application/xml");
2385 s
->formatter
->open_object_section_in_ns("ListPartsResult", XMLNS_AWS_S3
);
2386 map
<uint32_t, RGWUploadPartInfo
>::iterator iter
;
2387 map
<uint32_t, RGWUploadPartInfo
>::reverse_iterator test_iter
;
2390 iter
= parts
.begin();
2391 test_iter
= parts
.rbegin();
2392 if (test_iter
!= parts
.rend()) {
2393 cur_max
= test_iter
->first
;
2395 if (!s
->bucket_tenant
.empty())
2396 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2397 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2398 s
->formatter
->dump_string("Key", s
->object
.name
);
2399 s
->formatter
->dump_string("UploadId", upload_id
);
2400 s
->formatter
->dump_string("StorageClass", "STANDARD");
2401 s
->formatter
->dump_int("PartNumberMarker", marker
);
2402 s
->formatter
->dump_int("NextPartNumberMarker", cur_max
);
2403 s
->formatter
->dump_int("MaxParts", max_parts
);
2404 s
->formatter
->dump_string("IsTruncated", (truncated
? "true" : "false"));
2406 ACLOwner
& owner
= policy
.get_owner();
2407 dump_owner(s
, owner
.get_id(), owner
.get_display_name());
2409 for (; iter
!= parts
.end(); ++iter
) {
2410 RGWUploadPartInfo
& info
= iter
->second
;
2412 s
->formatter
->open_object_section("Part");
2414 dump_time(s
, "LastModified", &info
.modified
);
2416 s
->formatter
->dump_unsigned("PartNumber", info
.num
);
2417 s
->formatter
->dump_format("ETag", "\"%s\"", info
.etag
.c_str());
2418 s
->formatter
->dump_unsigned("Size", info
.accounted_size
);
2419 s
->formatter
->close_section();
2421 s
->formatter
->close_section();
2422 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2426 void RGWListBucketMultiparts_ObjStore_S3::send_response()
2429 set_req_state_err(s
, op_ret
);
2432 end_header(s
, this, "application/xml");
2437 s
->formatter
->open_object_section_in_ns("ListMultipartUploadsResult", XMLNS_AWS_S3
);
2438 if (!s
->bucket_tenant
.empty())
2439 s
->formatter
->dump_string("Tenant", s
->bucket_tenant
);
2440 s
->formatter
->dump_string("Bucket", s
->bucket_name
);
2441 if (!prefix
.empty())
2442 s
->formatter
->dump_string("ListMultipartUploadsResult.Prefix", prefix
);
2443 string
& key_marker
= marker
.get_key();
2444 if (!key_marker
.empty())
2445 s
->formatter
->dump_string("KeyMarker", key_marker
);
2446 string
& upload_id_marker
= marker
.get_upload_id();
2447 if (!upload_id_marker
.empty())
2448 s
->formatter
->dump_string("UploadIdMarker", upload_id_marker
);
2449 string next_key
= next_marker
.mp
.get_key();
2450 if (!next_key
.empty())
2451 s
->formatter
->dump_string("NextKeyMarker", next_key
);
2452 string next_upload_id
= next_marker
.mp
.get_upload_id();
2453 if (!next_upload_id
.empty())
2454 s
->formatter
->dump_string("NextUploadIdMarker", next_upload_id
);
2455 s
->formatter
->dump_int("MaxUploads", max_uploads
);
2456 if (!delimiter
.empty())
2457 s
->formatter
->dump_string("Delimiter", delimiter
);
2458 s
->formatter
->dump_string("IsTruncated", (is_truncated
? "true" : "false"));
2461 vector
<RGWMultipartUploadEntry
>::iterator iter
;
2462 for (iter
= uploads
.begin(); iter
!= uploads
.end(); ++iter
) {
2463 RGWMPObj
& mp
= iter
->mp
;
2464 s
->formatter
->open_array_section("Upload");
2465 s
->formatter
->dump_string("Key", mp
.get_key());
2466 s
->formatter
->dump_string("UploadId", mp
.get_upload_id());
2467 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
, "Initiator");
2468 dump_owner(s
, s
->user
->user_id
, s
->user
->display_name
);
2469 s
->formatter
->dump_string("StorageClass", "STANDARD");
2470 dump_time(s
, "Initiated", &iter
->obj
.meta
.mtime
);
2471 s
->formatter
->close_section();
2473 if (!common_prefixes
.empty()) {
2474 s
->formatter
->open_array_section("CommonPrefixes");
2475 map
<string
, bool>::iterator pref_iter
;
2476 for (pref_iter
= common_prefixes
.begin();
2477 pref_iter
!= common_prefixes
.end(); ++pref_iter
) {
2478 s
->formatter
->dump_string("CommonPrefixes.Prefix", pref_iter
->first
);
2480 s
->formatter
->close_section();
2483 s
->formatter
->close_section();
2484 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2487 int RGWDeleteMultiObj_ObjStore_S3::get_params()
2489 int ret
= RGWDeleteMultiObj_ObjStore::get_params();
2494 return do_aws4_auth_completion();
2497 void RGWDeleteMultiObj_ObjStore_S3::send_status()
2499 if (! status_dumped
) {
2501 set_req_state_err(s
, op_ret
);
2503 status_dumped
= true;
2507 void RGWDeleteMultiObj_ObjStore_S3::begin_response()
2510 if (!status_dumped
) {
2515 end_header(s
, this, "application/xml");
2516 s
->formatter
->open_object_section_in_ns("DeleteResult", XMLNS_AWS_S3
);
2518 rgw_flush_formatter(s
, s
->formatter
);
2521 void RGWDeleteMultiObj_ObjStore_S3::send_partial_response(rgw_obj_key
& key
,
2523 const string
& marker_version_id
, int ret
)
2526 if (op_ret
== 0 && !quiet
) {
2527 s
->formatter
->open_object_section("Deleted");
2528 s
->formatter
->dump_string("Key", key
.name
);
2529 if (!key
.instance
.empty()) {
2530 s
->formatter
->dump_string("VersionId", key
.instance
);
2532 if (delete_marker
) {
2533 s
->formatter
->dump_bool("DeleteMarker", true);
2534 s
->formatter
->dump_string("DeleteMarkerVersionId", marker_version_id
);
2536 s
->formatter
->close_section();
2537 } else if (op_ret
< 0) {
2538 struct rgw_http_error r
;
2541 s
->formatter
->open_object_section("Error");
2544 rgw_get_errno_s3(&r
, err_no
);
2546 s
->formatter
->dump_string("Key", key
.name
);
2547 s
->formatter
->dump_string("VersionId", key
.instance
);
2548 s
->formatter
->dump_int("Code", r
.http_ret
);
2549 s
->formatter
->dump_string("Message", r
.s3_code
);
2550 s
->formatter
->close_section();
2553 rgw_flush_formatter(s
, s
->formatter
);
2557 void RGWDeleteMultiObj_ObjStore_S3::end_response()
2560 s
->formatter
->close_section();
2561 rgw_flush_formatter_and_reset(s
, s
->formatter
);
2564 void RGWGetObjLayout_ObjStore_S3::send_response()
2567 set_req_state_err(s
, op_ret
);
2569 end_header(s
, this, "application/json");
2577 f
.open_object_section("result");
2578 ::encode_json("head", head_obj
, &f
);
2579 ::encode_json("manifest", *manifest
, &f
);
2580 f
.open_array_section("data_location");
2581 for (auto miter
= manifest
->obj_begin(); miter
!= manifest
->obj_end(); ++miter
) {
2582 f
.open_object_section("obj");
2583 rgw_raw_obj raw_loc
= miter
.get_location().get_raw_obj(store
);
2584 ::encode_json("ofs", miter
.get_ofs(), &f
);
2585 ::encode_json("loc", raw_loc
, &f
);
2586 ::encode_json("loc_ofs", miter
.location_ofs(), &f
);
2587 ::encode_json("loc_size", miter
.get_stripe_size(), &f
);
2589 rgw_flush_formatter(s
, &f
);
2593 rgw_flush_formatter(s
, &f
);
2596 int RGWConfigBucketMetaSearch_ObjStore_S3::get_params()
2598 auto iter
= s
->info
.x_meta_map
.find("x-amz-meta-search");
2599 if (iter
== s
->info
.x_meta_map
.end()) {
2600 s
->err
.message
= "X-Rgw-Meta-Search header not provided";
2601 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2605 list
<string
> expressions
;
2606 get_str_list(iter
->second
, ",", expressions
);
2608 for (auto& expression
: expressions
) {
2609 vector
<string
> args
;
2610 get_str_vec(expression
, ";", args
);
2613 s
->err
.message
= "invalid empty expression";
2614 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2617 if (args
.size() > 2) {
2618 s
->err
.message
= string("invalid expression: ") + expression
;
2619 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2623 string key
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[0]));
2625 if (args
.size() > 1) {
2626 val
= boost::algorithm::to_lower_copy(rgw_trim_whitespace(args
[1]));
2629 if (!boost::algorithm::starts_with(key
, RGW_AMZ_META_PREFIX
)) {
2630 s
->err
.message
= string("invalid expression, key must start with '" RGW_AMZ_META_PREFIX
"' : ") + expression
;
2631 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2635 key
= key
.substr(sizeof(RGW_AMZ_META_PREFIX
) - 1);
2637 ESEntityTypeMap::EntityType entity_type
;
2639 if (val
.empty() || val
== "str" || val
== "string") {
2640 entity_type
= ESEntityTypeMap::ES_ENTITY_STR
;
2641 } else if (val
== "int" || val
== "integer") {
2642 entity_type
= ESEntityTypeMap::ES_ENTITY_INT
;
2643 } else if (val
== "date" || val
== "datetime") {
2644 entity_type
= ESEntityTypeMap::ES_ENTITY_DATE
;
2646 s
->err
.message
= string("invalid entity type: ") + val
;
2647 ldout(s
->cct
, 5) << s
->err
.message
<< dendl
;
2651 mdsearch_config
[key
] = entity_type
;
2657 void RGWConfigBucketMetaSearch_ObjStore_S3::send_response()
2660 set_req_state_err(s
, op_ret
);
2662 end_header(s
, this);
2665 void RGWGetBucketMetaSearch_ObjStore_S3::send_response()
2668 set_req_state_err(s
, op_ret
);
2670 end_header(s
, NULL
, "application/xml");
2672 Formatter
*f
= s
->formatter
;
2673 f
->open_array_section("GetBucketMetaSearchResult");
2674 for (auto& e
: s
->bucket_info
.mdsearch_config
) {
2675 f
->open_object_section("Entry");
2676 string k
= string("x-amz-meta-") + e
.first
;
2677 f
->dump_string("Key", k
.c_str());
2680 case ESEntityTypeMap::ES_ENTITY_INT
:
2683 case ESEntityTypeMap::ES_ENTITY_DATE
:
2689 f
->dump_string("Type", type
);
2693 rgw_flush_formatter(s
, f
);
2696 void RGWDelBucketMetaSearch_ObjStore_S3::send_response()
2699 set_req_state_err(s
, op_ret
);
2701 end_header(s
, this);
2705 RGWOp
*RGWHandler_REST_Service_S3::op_get()
2707 if (is_usage_op()) {
2708 return new RGWGetUsage_ObjStore_S3
;
2710 return new RGWListBuckets_ObjStore_S3
;
2714 RGWOp
*RGWHandler_REST_Service_S3::op_head()
2716 return new RGWListBuckets_ObjStore_S3
;
2719 RGWOp
*RGWHandler_REST_Service_S3::op_post()
2721 if (s
->info
.args
.exists("Action")) {
2722 string action
= s
->info
.args
.get("Action");
2723 if (action
.compare("CreateRole") == 0)
2724 return new RGWCreateRole
;
2725 if (action
.compare("DeleteRole") == 0)
2726 return new RGWDeleteRole
;
2727 if (action
.compare("GetRole") == 0)
2728 return new RGWGetRole
;
2729 if (action
.compare("UpdateAssumeRolePolicy") == 0)
2730 return new RGWModifyRole
;
2731 if (action
.compare("ListRoles") == 0)
2732 return new RGWListRoles
;
2733 if (action
.compare("PutRolePolicy") == 0)
2734 return new RGWPutRolePolicy
;
2735 if (action
.compare("GetRolePolicy") == 0)
2736 return new RGWGetRolePolicy
;
2737 if (action
.compare("ListRolePolicies") == 0)
2738 return new RGWListRolePolicies
;
2739 if (action
.compare("DeleteRolePolicy") == 0)
2740 return new RGWDeleteRolePolicy
;
2745 RGWOp
*RGWHandler_REST_Bucket_S3::get_obj_op(bool get_data
)
2749 return new RGWListBucket_ObjStore_S3
;
2751 return new RGWStatBucket_ObjStore_S3
;
2755 RGWOp
*RGWHandler_REST_Bucket_S3::op_get()
2757 if (s
->info
.args
.sub_resource_exists("logging"))
2758 return new RGWGetBucketLogging_ObjStore_S3
;
2760 if (s
->info
.args
.sub_resource_exists("location"))
2761 return new RGWGetBucketLocation_ObjStore_S3
;
2763 if (s
->info
.args
.sub_resource_exists("versioning"))
2764 return new RGWGetBucketVersioning_ObjStore_S3
;
2766 if (s
->info
.args
.sub_resource_exists("website")) {
2767 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
2770 return new RGWGetBucketWebsite_ObjStore_S3
;
2773 if (s
->info
.args
.exists("mdsearch")) {
2774 return new RGWGetBucketMetaSearch_ObjStore_S3
;
2778 return new RGWGetACLs_ObjStore_S3
;
2779 } else if (is_cors_op()) {
2780 return new RGWGetCORS_ObjStore_S3
;
2781 } else if (is_request_payment_op()) {
2782 return new RGWGetRequestPayment_ObjStore_S3
;
2783 } else if (s
->info
.args
.exists("uploads")) {
2784 return new RGWListBucketMultiparts_ObjStore_S3
;
2785 } else if(is_lc_op()) {
2786 return new RGWGetLC_ObjStore_S3
;
2787 } else if(is_policy_op()) {
2788 return new RGWGetBucketPolicy
;
2790 return get_obj_op(true);
2793 RGWOp
*RGWHandler_REST_Bucket_S3::op_head()
2796 return new RGWGetACLs_ObjStore_S3
;
2797 } else if (s
->info
.args
.exists("uploads")) {
2798 return new RGWListBucketMultiparts_ObjStore_S3
;
2800 return get_obj_op(false);
2803 RGWOp
*RGWHandler_REST_Bucket_S3::op_put()
2805 if (s
->info
.args
.sub_resource_exists("logging"))
2807 if (s
->info
.args
.sub_resource_exists("versioning"))
2808 return new RGWSetBucketVersioning_ObjStore_S3
;
2809 if (s
->info
.args
.sub_resource_exists("website")) {
2810 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
2813 return new RGWSetBucketWebsite_ObjStore_S3
;
2816 return new RGWPutACLs_ObjStore_S3
;
2817 } else if (is_cors_op()) {
2818 return new RGWPutCORS_ObjStore_S3
;
2819 } else if (is_request_payment_op()) {
2820 return new RGWSetRequestPayment_ObjStore_S3
;
2821 } else if(is_lc_op()) {
2822 return new RGWPutLC_ObjStore_S3
;
2823 } else if(is_policy_op()) {
2824 return new RGWPutBucketPolicy
;
2826 return new RGWCreateBucket_ObjStore_S3
;
2829 RGWOp
*RGWHandler_REST_Bucket_S3::op_delete()
2832 return new RGWDeleteCORS_ObjStore_S3
;
2833 } else if(is_lc_op()) {
2834 return new RGWDeleteLC_ObjStore_S3
;
2835 } else if(is_policy_op()) {
2836 return new RGWDeleteBucketPolicy
;
2839 if (s
->info
.args
.sub_resource_exists("website")) {
2840 if (!s
->cct
->_conf
->rgw_enable_static_website
) {
2843 return new RGWDeleteBucketWebsite_ObjStore_S3
;
2846 if (s
->info
.args
.exists("mdsearch")) {
2847 return new RGWDelBucketMetaSearch_ObjStore_S3
;
2850 return new RGWDeleteBucket_ObjStore_S3
;
2853 RGWOp
*RGWHandler_REST_Bucket_S3::op_post()
2855 if (s
->info
.args
.exists("delete")) {
2856 return new RGWDeleteMultiObj_ObjStore_S3
;
2859 if (s
->info
.args
.exists("mdsearch")) {
2860 return new RGWConfigBucketMetaSearch_ObjStore_S3
;
2863 return new RGWPostObj_ObjStore_S3
;
2866 RGWOp
*RGWHandler_REST_Bucket_S3::op_options()
2868 return new RGWOptionsCORS_ObjStore_S3
;
2871 RGWOp
*RGWHandler_REST_Obj_S3::get_obj_op(bool get_data
)
2874 return new RGWGetACLs_ObjStore_S3
;
2876 RGWGetObj_ObjStore_S3
*get_obj_op
= new RGWGetObj_ObjStore_S3
;
2877 get_obj_op
->set_get_data(get_data
);
2881 RGWOp
*RGWHandler_REST_Obj_S3::op_get()
2884 return new RGWGetACLs_ObjStore_S3
;
2885 } else if (s
->info
.args
.exists("uploadId")) {
2886 return new RGWListMultipart_ObjStore_S3
;
2887 } else if (s
->info
.args
.exists("layout")) {
2888 return new RGWGetObjLayout_ObjStore_S3
;
2890 return get_obj_op(true);
2893 RGWOp
*RGWHandler_REST_Obj_S3::op_head()
2896 return new RGWGetACLs_ObjStore_S3
;
2897 } else if (s
->info
.args
.exists("uploadId")) {
2898 return new RGWListMultipart_ObjStore_S3
;
2900 return get_obj_op(false);
2903 RGWOp
*RGWHandler_REST_Obj_S3::op_put()
2906 return new RGWPutACLs_ObjStore_S3
;
2908 if (s
->init_state
.src_bucket
.empty())
2909 return new RGWPutObj_ObjStore_S3
;
2911 return new RGWCopyObj_ObjStore_S3
;
2914 RGWOp
*RGWHandler_REST_Obj_S3::op_delete()
2916 string upload_id
= s
->info
.args
.get("uploadId");
2918 if (upload_id
.empty())
2919 return new RGWDeleteObj_ObjStore_S3
;
2921 return new RGWAbortMultipart_ObjStore_S3
;
2924 RGWOp
*RGWHandler_REST_Obj_S3::op_post()
2926 if (s
->info
.args
.exists("uploadId"))
2927 return new RGWCompleteMultipart_ObjStore_S3
;
2929 if (s
->info
.args
.exists("uploads"))
2930 return new RGWInitMultipart_ObjStore_S3
;
2932 return new RGWPostObj_ObjStore_S3
;
2935 RGWOp
*RGWHandler_REST_Obj_S3::op_options()
2937 return new RGWOptionsCORS_ObjStore_S3
;
2940 int RGWHandler_REST_S3::init_from_header(struct req_state
* s
,
2941 int default_formatter
,
2942 bool configurable_format
)
2947 const char *req_name
= s
->relative_uri
.c_str();
2950 if (*req_name
== '?') {
2953 p
= s
->info
.request_params
.c_str();
2956 s
->info
.args
.set(p
);
2957 s
->info
.args
.parse();
2959 /* must be called after the args parsing */
2960 int ret
= allocate_formatter(s
, default_formatter
, configurable_format
);
2964 if (*req_name
!= '/')
2973 int pos
= req
.find('/');
2975 first
= req
.substr(0, pos
);
2981 * XXX The intent of the check for empty is apparently to let the bucket
2982 * name from DNS to be set ahead. However, we currently take the DNS
2983 * bucket and re-insert it into URL in rgw_rest.cc:RGWREST::preprocess().
2984 * So, this check is meaningless.
2986 * Rather than dropping this, the code needs to be changed into putting
2987 * the bucket (and its tenant) from DNS and Host: header (HTTP_HOST)
2988 * into req_status.bucket_name directly.
2990 if (s
->init_state
.url_bucket
.empty()) {
2991 // Save bucket to tide us over until token is parsed.
2992 s
->init_state
.url_bucket
= first
;
2994 string encoded_obj_str
= req
.substr(pos
+1);
2995 s
->object
= rgw_obj_key(encoded_obj_str
, s
->info
.args
.get("versionId"));
2998 s
->object
= rgw_obj_key(req_name
, s
->info
.args
.get("versionId"));
3003 int RGWHandler_REST_S3::postauth_init()
3005 struct req_init_state
*t
= &s
->init_state
;
3006 bool relaxed_names
= s
->cct
->_conf
->rgw_relaxed_s3_bucket_names
;
3008 rgw_parse_url_bucket(t
->url_bucket
, s
->user
->user_id
.tenant
,
3009 s
->bucket_tenant
, s
->bucket_name
);
3011 dout(10) << "s->object=" << (!s
->object
.empty() ? s
->object
: rgw_obj_key("<NULL>"))
3012 << " s->bucket=" << rgw_make_bucket_entry_name(s
->bucket_tenant
, s
->bucket_name
) << dendl
;
3015 ret
= validate_tenant_name(s
->bucket_tenant
);
3018 if (!s
->bucket_name
.empty()) {
3019 ret
= valid_s3_bucket_name(s
->bucket_name
, relaxed_names
);
3022 ret
= validate_object_name(s
->object
.name
);
3027 if (!t
->src_bucket
.empty()) {
3028 rgw_parse_url_bucket(t
->src_bucket
, s
->user
->user_id
.tenant
,
3029 s
->src_tenant_name
, s
->src_bucket_name
);
3030 ret
= validate_tenant_name(s
->src_tenant_name
);
3033 ret
= valid_s3_bucket_name(s
->src_bucket_name
, relaxed_names
);
3040 int RGWHandler_REST_S3::init(RGWRados
*store
, struct req_state
*s
,
3041 rgw::io::BasicClient
*cio
)
3047 ret
= validate_tenant_name(s
->bucket_tenant
);
3050 bool relaxed_names
= s
->cct
->_conf
->rgw_relaxed_s3_bucket_names
;
3051 if (!s
->bucket_name
.empty()) {
3052 ret
= valid_s3_bucket_name(s
->bucket_name
, relaxed_names
);
3055 ret
= validate_object_name(s
->object
.name
);
3060 const char *cacl
= s
->info
.env
->get("HTTP_X_AMZ_ACL");
3062 s
->canned_acl
= cacl
;
3064 s
->has_acl_header
= s
->info
.env
->exists_prefix("HTTP_X_AMZ_GRANT");
3066 const char *copy_source
= s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE");
3068 if (copy_source
&& !s
->info
.env
->get("HTTP_X_AMZ_COPY_SOURCE_RANGE")) {
3069 ret
= RGWCopyObj::parse_copy_location(copy_source
,
3070 s
->init_state
.src_bucket
,
3073 ldout(s
->cct
, 0) << "failed to parse copy location" << dendl
;
3074 return -EINVAL
; // XXX why not -ERR_INVALID_BUCKET_NAME or -ERR_BAD_URL?
3078 return RGWHandler_REST::init(store
, s
, cio
);
3081 enum class AwsVersion
{
3087 enum class AwsRoute
{
3093 static inline std::pair
<AwsVersion
, AwsRoute
>
3094 discover_aws_flavour(const req_info
& info
)
3096 using rgw::auth::s3::AWS4_HMAC_SHA256_STR
;
3098 AwsVersion version
= AwsVersion::UNKOWN
;
3099 AwsRoute route
= AwsRoute::UNKOWN
;
3101 const char* http_auth
= info
.env
->get("HTTP_AUTHORIZATION");
3102 if (http_auth
&& http_auth
[0]) {
3103 /* Authorization in Header */
3104 route
= AwsRoute::HEADERS
;
3106 if (!strncmp(http_auth
, AWS4_HMAC_SHA256_STR
,
3107 strlen(AWS4_HMAC_SHA256_STR
))) {
3109 version
= AwsVersion::V4
;
3110 } else if (!strncmp(http_auth
, "AWS ", 4)) {
3112 version
= AwsVersion::V2
;
3115 route
= AwsRoute::QUERY_STRING
;
3117 if (info
.args
.get("X-Amz-Algorithm") == AWS4_HMAC_SHA256_STR
) {
3119 version
= AwsVersion::V4
;
3120 } else if (!info
.args
.get("AWSAccessKeyId").empty()) {
3122 version
= AwsVersion::V2
;
3126 return std::make_pair(version
, route
);
3129 static void init_anon_user(struct req_state
*s
)
3131 rgw_get_anon_user(*(s
->user
));
3132 s
->perm_mask
= RGW_PERM_FULL_CONTROL
;
3136 * verify that a signed request comes from the keyholder
3137 * by checking the signature against our locally-computed version
3139 * it tries AWS v4 before AWS v2
3141 int RGW_Auth_S3::authorize(RGWRados
* const store
,
3142 const rgw::auth::StrategyRegistry
& auth_registry
,
3143 struct req_state
* const s
)
3146 /* neither keystone and rados enabled; warn and exit! */
3147 if (!store
->ctx()->_conf
->rgw_s3_auth_use_rados
&&
3148 !store
->ctx()->_conf
->rgw_s3_auth_use_keystone
&&
3149 !store
->ctx()->_conf
->rgw_s3_auth_use_ldap
) {
3150 dout(0) << "WARNING: no authorization backend enabled! Users will never authenticate." << dendl
;
3154 if (s
->op
== OP_OPTIONS
) {
3161 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
3163 if (route
== AwsRoute::QUERY_STRING
&& version
== AwsVersion::UNKOWN
) {
3164 /* FIXME(rzarzynski): handle anon user. */
3165 init_anon_user(const_cast<req_state
*>(s
));
3169 return authorize_v2(store
, auth_registry
, s
);
3174 * handle v2 signatures
3176 int RGW_Auth_S3::authorize_v2(RGWRados
* const store
,
3177 const rgw::auth::StrategyRegistry
& auth_registry
,
3178 struct req_state
* const s
)
3180 const auto ret
= rgw::auth::Strategy::apply(auth_registry
.get_s3_main(), s
);
3182 /* Populate the owner info. */
3183 s
->owner
.set_id(s
->user
->user_id
);
3184 s
->owner
.set_name(s
->user
->display_name
);
3189 int RGWHandler_Auth_S3::init(RGWRados
*store
, struct req_state
*state
,
3190 rgw::io::BasicClient
*cio
)
3192 int ret
= RGWHandler_REST_S3::init_from_header(state
, RGW_FORMAT_JSON
,
3197 return RGWHandler_REST::init(store
, state
, cio
);
3200 RGWHandler_REST
* RGWRESTMgr_S3::get_handler(struct req_state
* const s
,
3201 const rgw::auth::StrategyRegistry
& auth_registry
,
3202 const std::string
& frontend_prefix
)
3204 bool is_s3website
= enable_s3website
&& (s
->prot_flags
& RGW_REST_WEBSITE
);
3206 RGWHandler_REST_S3::init_from_header(s
,
3207 is_s3website
? RGW_FORMAT_HTML
:
3208 RGW_FORMAT_XML
, true);
3212 RGWHandler_REST
* handler
;
3213 // TODO: Make this more readable
3215 if (s
->init_state
.url_bucket
.empty()) {
3216 handler
= new RGWHandler_REST_Service_S3Website(auth_registry
);
3217 } else if (s
->object
.empty()) {
3218 handler
= new RGWHandler_REST_Bucket_S3Website(auth_registry
);
3220 handler
= new RGWHandler_REST_Obj_S3Website(auth_registry
);
3223 if (s
->init_state
.url_bucket
.empty()) {
3224 handler
= new RGWHandler_REST_Service_S3(auth_registry
);
3225 } else if (s
->object
.empty()) {
3226 handler
= new RGWHandler_REST_Bucket_S3(auth_registry
);
3228 handler
= new RGWHandler_REST_Obj_S3(auth_registry
);
3232 ldout(s
->cct
, 20) << __func__
<< " handler=" << typeid(*handler
).name()
3237 int RGWHandler_REST_S3Website::retarget(RGWOp
* op
, RGWOp
** new_op
) {
3239 ldout(s
->cct
, 10) << __func__
<< "Starting retarget" << dendl
;
3241 if (!(s
->prot_flags
& RGW_REST_WEBSITE
))
3244 RGWObjectCtx
& obj_ctx
= *static_cast<RGWObjectCtx
*>(s
->obj_ctx
);
3245 int ret
= store
->get_bucket_info(obj_ctx
, s
->bucket_tenant
,
3246 s
->bucket_name
, s
->bucket_info
, NULL
,
3249 // TODO-FUTURE: if the bucket does not exist, maybe expose it here?
3250 return -ERR_NO_SUCH_BUCKET
;
3252 if (!s
->bucket_info
.has_website
) {
3253 // TODO-FUTURE: if the bucket has no WebsiteConfig, expose it here
3254 return -ERR_NO_SUCH_WEBSITE_CONFIGURATION
;
3257 rgw_obj_key new_obj
;
3258 s
->bucket_info
.website_conf
.get_effective_key(s
->object
.name
, &new_obj
.name
);
3259 ldout(s
->cct
, 10) << "retarget get_effective_key " << s
->object
<< " -> "
3260 << new_obj
<< dendl
;
3262 RGWBWRoutingRule rrule
;
3263 bool should_redirect
=
3264 s
->bucket_info
.website_conf
.should_redirect(new_obj
.name
, 0, &rrule
);
3266 if (should_redirect
) {
3267 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
3268 const string
& protocol
=
3269 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
3270 int redirect_code
= 0;
3271 rrule
.apply_rule(protocol
, hostname
, s
->object
.name
, &s
->redirect
,
3273 // APply a custom HTTP response code
3274 if (redirect_code
> 0)
3275 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
3276 ldout(s
->cct
, 10) << "retarget redirect code=" << redirect_code
3277 << " proto+host:" << protocol
<< "://" << hostname
3278 << " -> " << s
->redirect
<< dendl
;
3279 return -ERR_WEBSITE_REDIRECT
;
3283 * FIXME: if s->object != new_obj, drop op and create a new op to handle
3284 * operation. Or remove this comment if it's not applicable anymore
3287 s
->object
= new_obj
;
3292 RGWOp
* RGWHandler_REST_S3Website::op_get()
3294 return get_obj_op(true);
3297 RGWOp
* RGWHandler_REST_S3Website::op_head()
3299 return get_obj_op(false);
3302 int RGWHandler_REST_S3Website::serve_errordoc(int http_ret
, const string
& errordoc_key
) {
3304 s
->formatter
->reset(); /* Try to throw it all away */
3306 std::shared_ptr
<RGWGetObj_ObjStore_S3Website
> getop( static_cast<RGWGetObj_ObjStore_S3Website
*>(op_get()));
3307 if (getop
.get() == NULL
) {
3308 return -1; // Trigger double error handler
3310 getop
->init(store
, s
, this);
3311 getop
->range_str
= NULL
;
3312 getop
->if_mod
= NULL
;
3313 getop
->if_unmod
= NULL
;
3314 getop
->if_match
= NULL
;
3315 getop
->if_nomatch
= NULL
;
3316 s
->object
= errordoc_key
;
3318 ret
= init_permissions(getop
.get());
3320 ldout(s
->cct
, 20) << "serve_errordoc failed, init_permissions ret=" << ret
<< dendl
;
3321 return -1; // Trigger double error handler
3324 ret
= read_permissions(getop
.get());
3326 ldout(s
->cct
, 20) << "serve_errordoc failed, read_permissions ret=" << ret
<< dendl
;
3327 return -1; // Trigger double error handler
3331 getop
->set_custom_http_response(http_ret
);
3334 ret
= getop
->init_processing();
3336 ldout(s
->cct
, 20) << "serve_errordoc failed, init_processing ret=" << ret
<< dendl
;
3337 return -1; // Trigger double error handler
3340 ret
= getop
->verify_op_mask();
3342 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_op_mask ret=" << ret
<< dendl
;
3343 return -1; // Trigger double error handler
3346 ret
= getop
->verify_permission();
3348 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_permission ret=" << ret
<< dendl
;
3349 return -1; // Trigger double error handler
3352 ret
= getop
->verify_params();
3354 ldout(s
->cct
, 20) << "serve_errordoc failed, verify_params ret=" << ret
<< dendl
;
3355 return -1; // Trigger double error handler
3358 // No going back now
3361 * FIXME Missing headers:
3362 * With a working errordoc, the s3 error fields are rendered as HTTP headers,
3363 * x-amz-error-code: NoSuchKey
3364 * x-amz-error-message: The specified key does not exist.
3365 * x-amz-error-detail-Key: foo
3373 int RGWHandler_REST_S3Website::error_handler(int err_no
,
3374 string
* error_content
) {
3375 int new_err_no
= -1;
3376 rgw_http_errors::const_iterator r
= rgw_http_s3_errors
.find(err_no
> 0 ? err_no
: -err_no
);
3377 int http_error_code
= -1;
3379 if (r
!= rgw_http_s3_errors
.end()) {
3380 http_error_code
= r
->second
.first
;
3382 ldout(s
->cct
, 10) << "RGWHandler_REST_S3Website::error_handler err_no=" << err_no
<< " http_ret=" << http_error_code
<< dendl
;
3384 RGWBWRoutingRule rrule
;
3385 bool should_redirect
=
3386 s
->bucket_info
.website_conf
.should_redirect(s
->object
.name
, http_error_code
,
3389 if (should_redirect
) {
3390 const string
& hostname
= s
->info
.env
->get("HTTP_HOST", "");
3391 const string
& protocol
=
3392 (s
->info
.env
->get("SERVER_PORT_SECURE") ? "https" : "http");
3393 int redirect_code
= 0;
3394 rrule
.apply_rule(protocol
, hostname
, s
->object
.name
, &s
->redirect
,
3396 // Apply a custom HTTP response code
3397 if (redirect_code
> 0)
3398 s
->err
.http_ret
= redirect_code
; // Apply a custom HTTP response code
3399 ldout(s
->cct
, 10) << "error handler redirect code=" << redirect_code
3400 << " proto+host:" << protocol
<< "://" << hostname
3401 << " -> " << s
->redirect
<< dendl
;
3402 return -ERR_WEBSITE_REDIRECT
;
3403 } else if (err_no
== -ERR_WEBSITE_REDIRECT
) {
3404 // Do nothing here, this redirect will be handled in abort_early's ERR_WEBSITE_REDIRECT block
3405 // Do NOT fire the ErrorDoc handler
3406 } else if (!s
->bucket_info
.website_conf
.error_doc
.empty()) {
3407 /* This serves an entire page!
3408 On success, it will return zero, and no further content should be sent to the socket
3409 On failure, we need the double-error handler
3411 new_err_no
= RGWHandler_REST_S3Website::serve_errordoc(http_error_code
, s
->bucket_info
.website_conf
.error_doc
);
3412 if (new_err_no
&& new_err_no
!= -1) {
3413 err_no
= new_err_no
;
3416 ldout(s
->cct
, 20) << "No special error handling today!" << dendl
;
3422 RGWOp
* RGWHandler_REST_Obj_S3Website::get_obj_op(bool get_data
)
3424 /** If we are in website mode, then it is explicitly impossible to run GET or
3425 * HEAD on the actual directory. We must convert the request to run on the
3426 * suffix object instead!
3428 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3429 op
->set_get_data(get_data
);
3433 RGWOp
* RGWHandler_REST_Bucket_S3Website::get_obj_op(bool get_data
)
3435 /** If we are in website mode, then it is explicitly impossible to run GET or
3436 * HEAD on the actual directory. We must convert the request to run on the
3437 * suffix object instead!
3439 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3440 op
->set_get_data(get_data
);
3444 RGWOp
* RGWHandler_REST_Service_S3Website::get_obj_op(bool get_data
)
3446 /** If we are in website mode, then it is explicitly impossible to run GET or
3447 * HEAD on the actual directory. We must convert the request to run on the
3448 * suffix object instead!
3450 RGWGetObj_ObjStore_S3Website
* op
= new RGWGetObj_ObjStore_S3Website
;
3451 op
->set_get_data(get_data
);
3460 bool AWSGeneralAbstractor::is_time_skew_ok(const utime_t
& header_time
,
3461 const bool qsr
) const
3463 /* Check for time skew first. */
3464 const time_t req_sec
= header_time
.sec();
3468 if ((req_sec
< now
- RGW_AUTH_GRACE_MINS
* 60 ||
3469 req_sec
> now
+ RGW_AUTH_GRACE_MINS
* 60) && !qsr
) {
3470 ldout(cct
, 10) << "req_sec=" << req_sec
<< " now=" << now
3471 << "; now - RGW_AUTH_GRACE_MINS="
3472 << now
- RGW_AUTH_GRACE_MINS
* 60
3473 << "; now + RGW_AUTH_GRACE_MINS="
3474 << now
+ RGW_AUTH_GRACE_MINS
* 60
3477 ldout(cct
, 0) << "NOTICE: request time skew too big now="
3479 << " req_time=" << header_time
3488 static rgw::auth::Completer::cmplptr_t
3489 null_completer_factory(const boost::optional
<std::string
>& secret_key
)
3495 using AWSVerAbstractor
= AWSEngine::VersionAbstractor
;
3497 std::tuple
<AWSVerAbstractor::access_key_id_t
,
3498 AWSVerAbstractor::client_signature_t
,
3499 AWSVerAbstractor::string_to_sign_t
,
3500 AWSVerAbstractor::signature_factory_t
,
3501 AWSVerAbstractor::completer_factory_t
>
3502 AWSGeneralAbstractor::get_auth_data(const req_state
* const s
) const
3506 std::tie(version
, route
) = discover_aws_flavour(s
->info
);
3508 if (version
== AwsVersion::V2
) {
3509 return get_auth_data_v2(s
);
3510 } else if (version
== AwsVersion::V4
) {
3511 return get_auth_data_v4(s
, route
== AwsRoute::QUERY_STRING
);
3513 /* FIXME(rzarzynski): handle anon user. */
3518 boost::optional
<std::string
>
3519 AWSGeneralAbstractor::get_v4_canonical_headers(
3520 const req_info
& info
,
3521 const boost::string_view
& signedheaders
,
3522 const bool using_qs
) const
3524 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
3528 std::tuple
<AWSVerAbstractor::access_key_id_t
,
3529 AWSVerAbstractor::client_signature_t
,
3530 AWSVerAbstractor::string_to_sign_t
,
3531 AWSVerAbstractor::signature_factory_t
,
3532 AWSVerAbstractor::completer_factory_t
>
3533 AWSGeneralAbstractor::get_auth_data_v4(const req_state
* const s
,
3535 bool using_qs
) const
3537 boost::string_view access_key_id
;
3538 boost::string_view signed_hdrs
;
3540 boost::string_view date
;
3541 boost::string_view credential_scope
;
3542 boost::string_view client_signature
;
3544 int ret
= rgw::auth::s3::parse_credentials(s
->info
,
3555 /* craft canonical headers */
3556 boost::optional
<std::string
> canonical_headers
= \
3557 get_v4_canonical_headers(s
->info
, signed_hdrs
, using_qs
);
3558 if (canonical_headers
) {
3559 ldout(s
->cct
, 10) << "canonical headers format = " << *canonical_headers
3565 /* Get the expected hash. */
3566 auto exp_payload_hash
= rgw::auth::s3::get_v4_exp_payload_hash(s
->info
);
3568 /* Craft canonical URI. Using std::move later so let it be non-const. */
3569 auto canonical_uri
= rgw::auth::s3::get_v4_canonical_uri(s
->info
);
3571 /* Craft canonical query string. std::moving later so non-const here. */
3572 auto canonical_qs
= rgw::auth::s3::get_v4_canonical_qs(s
->info
, using_qs
);
3574 /* Craft canonical request. */
3575 auto canonical_req_hash
= \
3576 rgw::auth::s3::get_v4_canon_req_hash(s
->cct
,
3578 std::move(canonical_uri
),
3579 std::move(canonical_qs
),
3580 std::move(*canonical_headers
),
3584 auto string_to_sign
= \
3585 rgw::auth::s3::get_v4_string_to_sign(s
->cct
,
3586 AWS4_HMAC_SHA256_STR
,
3589 std::move(canonical_req_hash
));
3591 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
3593 std::placeholders::_1
,
3594 std::placeholders::_2
,
3595 std::placeholders::_3
);
3597 /* Requests authenticated with the Query Parameters are treated as unsigned.
3598 * From "Authenticating Requests: Using Query Parameters (AWS Signature
3601 * You don't include a payload hash in the Canonical Request, because
3602 * when you create a presigned URL, you don't know the payload content
3603 * because the URL is used to upload an arbitrary payload. Instead, you
3604 * use a constant string UNSIGNED-PAYLOAD.
3606 * This means we have absolutely no business in spawning completer. Both
3607 * aws4_auth_needs_complete and aws4_auth_streaming_mode are set to false
3608 * by default. We don't need to change that. */
3609 if (is_v4_payload_unsigned(exp_payload_hash
) || is_v4_payload_empty(s
)) {
3610 return std::make_tuple(access_key_id
,
3612 std::move(string_to_sign
),
3614 null_completer_factory
);
3616 /* We're going to handle a signed payload. Be aware that even empty HTTP
3617 * body (no payload) requires verification:
3619 * The x-amz-content-sha256 header is required for all AWS Signature
3620 * Version 4 requests. It provides a hash of the request payload. If
3621 * there is no payload, you must provide the hash of an empty string. */
3622 if (!is_v4_payload_streamed(exp_payload_hash
)) {
3623 ldout(s
->cct
, 10) << "delaying v4 auth" << dendl
;
3625 /* payload in a single chunk */
3628 case RGW_OP_CREATE_BUCKET
:
3629 case RGW_OP_PUT_OBJ
:
3630 case RGW_OP_PUT_ACLS
:
3631 case RGW_OP_PUT_CORS
:
3632 case RGW_OP_COMPLETE_MULTIPART
:
3633 case RGW_OP_SET_BUCKET_VERSIONING
:
3634 case RGW_OP_DELETE_MULTI_OBJ
:
3635 case RGW_OP_ADMIN_SET_METADATA
:
3636 case RGW_OP_SET_BUCKET_WEBSITE
:
3637 case RGW_OP_PUT_BUCKET_POLICY
:
3640 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl
;
3641 throw -ERR_NOT_IMPLEMENTED
;
3644 const auto cmpl_factory
= std::bind(AWSv4ComplSingle::create
,
3646 std::placeholders::_1
);
3647 return std::make_tuple(access_key_id
,
3649 std::move(string_to_sign
),
3653 /* IMHO "streamed" doesn't fit too good here. I would prefer to call
3654 * it "chunked" but let's be coherent with Amazon's terminology. */
3656 dout(10) << "body content detected in multiple chunks" << dendl
;
3658 /* payload in multiple chunks */
3662 case RGW_OP_PUT_OBJ
:
3665 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED (streaming mode)" << dendl
;
3666 throw -ERR_NOT_IMPLEMENTED
;
3669 dout(10) << "aws4 seed signature ok... delaying v4 auth" << dendl
;
3671 /* In the case of streamed payload client sets the x-amz-content-sha256
3672 * to "STREAMING-AWS4-HMAC-SHA256-PAYLOAD" but uses "UNSIGNED-PAYLOAD"
3673 * when constructing the Canonical Request. */
3675 /* In the case of single-chunk upload client set the header's value is
3676 * coherent with the one used for Canonical Request crafting. */
3678 /* In the case of query string-based authentication there should be no
3679 * x-amz-content-sha256 header and the value "UNSIGNED-PAYLOAD" is used
3681 const auto cmpl_factory
= std::bind(AWSv4ComplMulti::create
,
3686 std::placeholders::_1
);
3687 return std::make_tuple(access_key_id
,
3689 std::move(string_to_sign
),
3697 boost::optional
<std::string
>
3698 AWSGeneralBoto2Abstractor::get_v4_canonical_headers(
3699 const req_info
& info
,
3700 const boost::string_view
& signedheaders
,
3701 const bool using_qs
) const
3703 return rgw::auth::s3::get_v4_canonical_headers(info
, signedheaders
,
3708 std::tuple
<AWSVerAbstractor::access_key_id_t
,
3709 AWSVerAbstractor::client_signature_t
,
3710 AWSVerAbstractor::string_to_sign_t
,
3711 AWSVerAbstractor::signature_factory_t
,
3712 AWSVerAbstractor::completer_factory_t
>
3713 AWSGeneralAbstractor::get_auth_data_v2(const req_state
* const s
) const
3715 boost::string_view access_key_id
;
3716 boost::string_view signature
;
3719 const char* http_auth
= s
->info
.env
->get("HTTP_AUTHORIZATION");
3720 if (! http_auth
|| http_auth
[0] == '\0') {
3721 /* Credentials are provided in query string. We also need to verify
3722 * the "Expires" parameter now. */
3723 access_key_id
= s
->info
.args
.get("AWSAccessKeyId");
3724 signature
= s
->info
.args
.get("Signature");
3727 boost::string_view expires
= s
->info
.args
.get("Expires");
3728 if (! expires
.empty()) {
3729 /* It looks we have the guarantee that expires is a null-terminated,
3730 * and thus string_view::data() can be safely used. */
3731 const time_t exp
= atoll(expires
.data());
3740 /* The "Authorization" HTTP header is being used. */
3741 const boost::string_view
auth_str(http_auth
+ strlen("AWS "));
3742 const size_t pos
= auth_str
.rfind(':');
3743 if (pos
!= boost::string_view::npos
) {
3744 access_key_id
= auth_str
.substr(0, pos
);
3745 signature
= auth_str
.substr(pos
+ 1);
3749 /* Let's canonize the HTTP headers that are covered by the AWS auth v2. */
3750 std::string string_to_sign
;
3751 utime_t header_time
;
3752 if (! rgw_create_s3_canonical_header(s
->info
, &header_time
, string_to_sign
,
3754 ldout(cct
, 10) << "failed to create the canonized auth header\n"
3755 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
3759 ldout(cct
, 10) << "string_to_sign:\n"
3760 << rgw::crypt_sanitize::auth
{s
,string_to_sign
} << dendl
;
3762 if (! is_time_skew_ok(header_time
, qsr
)) {
3763 throw -ERR_REQUEST_TIME_SKEWED
;
3766 return std::make_tuple(std::move(access_key_id
),
3767 std::move(signature
),
3768 std::move(string_to_sign
),
3769 rgw::auth::s3::get_v2_signature
,
3770 null_completer_factory
);
3774 std::tuple
<AWSVerAbstractor::access_key_id_t
,
3775 AWSVerAbstractor::client_signature_t
,
3776 AWSVerAbstractor::string_to_sign_t
,
3777 AWSVerAbstractor::signature_factory_t
,
3778 AWSVerAbstractor::completer_factory_t
>
3779 AWSBrowserUploadAbstractor::get_auth_data_v2(const req_state
* const s
) const
3781 return std::make_tuple(s
->auth
.s3_postobj_creds
.access_key
,
3782 s
->auth
.s3_postobj_creds
.signature
,
3783 to_string(s
->auth
.s3_postobj_creds
.encoded_policy
),
3784 rgw::auth::s3::get_v2_signature
,
3785 null_completer_factory
);
3788 std::tuple
<AWSVerAbstractor::access_key_id_t
,
3789 AWSVerAbstractor::client_signature_t
,
3790 AWSVerAbstractor::string_to_sign_t
,
3791 AWSVerAbstractor::signature_factory_t
,
3792 AWSVerAbstractor::completer_factory_t
>
3793 AWSBrowserUploadAbstractor::get_auth_data_v4(const req_state
* const s
) const
3795 const boost::string_view credential
= s
->auth
.s3_postobj_creds
.x_amz_credential
;
3797 /* grab access key id */
3798 const size_t pos
= credential
.find("/");
3799 const boost::string_view access_key_id
= credential
.substr(0, pos
);
3800 dout(10) << "access key id = " << access_key_id
<< dendl
;
3802 /* grab credential scope */
3803 const boost::string_view credential_scope
= credential
.substr(pos
+ 1);
3804 dout(10) << "credential scope = " << credential_scope
<< dendl
;
3806 const auto sig_factory
= std::bind(rgw::auth::s3::get_v4_signature
,
3808 std::placeholders::_1
,
3809 std::placeholders::_2
,
3810 std::placeholders::_3
);
3812 return std::make_tuple(access_key_id
,
3813 s
->auth
.s3_postobj_creds
.signature
,
3814 to_string(s
->auth
.s3_postobj_creds
.encoded_policy
),
3816 null_completer_factory
);
3819 std::tuple
<AWSVerAbstractor::access_key_id_t
,
3820 AWSVerAbstractor::client_signature_t
,
3821 AWSVerAbstractor::string_to_sign_t
,
3822 AWSVerAbstractor::signature_factory_t
,
3823 AWSVerAbstractor::completer_factory_t
>
3824 AWSBrowserUploadAbstractor::get_auth_data(const req_state
* const s
) const
3826 if (s
->auth
.s3_postobj_creds
.x_amz_algorithm
== AWS4_HMAC_SHA256_STR
) {
3827 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v4"
3828 << " (AWS4-HMAC-SHA256)" << dendl
;
3829 return get_auth_data_v2(s
);
3831 ldout(s
->cct
, 0) << "Signature verification algorithm AWS v2" << dendl
;
3832 return get_auth_data_v2(s
);
3838 AWSEngine::authenticate(const req_state
* const s
) const
3840 boost::string_view access_key_id
;
3841 boost::string_view signature
;
3842 VersionAbstractor::string_to_sign_t string_to_sign
;
3844 VersionAbstractor::signature_factory_t signature_factory
;
3845 VersionAbstractor::completer_factory_t completer_factory
;
3847 /* Small reminder: an ver_abstractor is allowed to throw! */
3848 std::tie(access_key_id
,
3852 completer_factory
) = ver_abstractor
.get_auth_data(s
);
3854 if (access_key_id
.empty() || signature
.empty()) {
3855 return result_t::deny(-EINVAL
);
3857 return authenticate(access_key_id
, signature
, string_to_sign
,
3858 signature_factory
, completer_factory
, s
);
3862 } /* namespace s3 */
3863 } /* namespace auth */
3864 } /* namespace rgw */
3866 rgw::LDAPHelper
* rgw::auth::s3::LDAPEngine::ldh
= nullptr;
3867 std::mutex
rgw::auth::s3::LDAPEngine::mtx
;
3869 void rgw::auth::s3::LDAPEngine::init(CephContext
* const cct
)
3872 std::lock_guard
<std::mutex
> lck(mtx
);
3874 const string
& ldap_uri
= cct
->_conf
->rgw_ldap_uri
;
3875 const string
& ldap_binddn
= cct
->_conf
->rgw_ldap_binddn
;
3876 const string
& ldap_searchdn
= cct
->_conf
->rgw_ldap_searchdn
;
3877 const string
& ldap_searchfilter
= cct
->_conf
->rgw_ldap_searchfilter
;
3878 const string
& ldap_dnattr
= cct
->_conf
->rgw_ldap_dnattr
;
3879 std::string ldap_bindpw
= parse_rgw_ldap_bindpw(cct
);
3881 ldh
= new rgw::LDAPHelper(ldap_uri
, ldap_binddn
, ldap_bindpw
,
3882 ldap_searchdn
, ldap_searchfilter
, ldap_dnattr
);
3890 rgw::auth::RemoteApplier::acl_strategy_t
3891 rgw::auth::s3::LDAPEngine::get_acl_strategy() const
3893 //This is based on the assumption that the default acl strategy in
3894 // get_perms_from_aclspec, will take care. Extra acl spec is not required.
3898 rgw::auth::RemoteApplier::AuthInfo
3899 rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken
& token
) const noexcept
3901 /* The short form of "using" can't be used here -- we're aliasing a class'
3903 using acct_privilege_t
= \
3904 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t
;
3906 return rgw::auth::RemoteApplier::AuthInfo
{
3909 RGW_PERM_FULL_CONTROL
,
3910 acct_privilege_t::IS_PLAIN_ACCT
,
3915 rgw::auth::Engine::result_t
3916 rgw::auth::s3::LDAPEngine::authenticate(
3917 const boost::string_view
& access_key_id
,
3918 const boost::string_view
& signature
,
3919 const string_to_sign_t
& string_to_sign
,
3920 const signature_factory_t
&,
3921 const completer_factory_t
& completer_factory
,
3922 const req_state
* const s
) const
3924 /* boost filters and/or string_ref may throw on invalid input */
3925 rgw::RGWToken base64_token
;
3927 base64_token
= rgw::from_base64(access_key_id
);
3929 base64_token
= std::string("");
3932 if (! base64_token
.valid()) {
3933 return result_t::deny();
3936 //TODO: Uncomment, when we have a migration plan in place.
3937 //Check if a user of type other than 'ldap' is already present, if yes, then
3939 /*RGWUserInfo user_info;
3940 user_info.user_id = base64_token.id;
3941 if (rgw_get_user_info_by_uid(store, user_info.user_id, user_info) >= 0) {
3942 if (user_info.type != TYPE_LDAP) {
3943 ldout(cct, 10) << "ERROR: User id of type: " << user_info.type << " is already present" << dendl;
3948 if (ldh
->auth(base64_token
.id
, base64_token
.key
) != 0) {
3949 return result_t::deny();
3952 auto apl
= apl_factory
->create_apl_remote(cct
, s
, get_acl_strategy(),
3953 get_creds_info(base64_token
));
3954 return result_t::grant(std::move(apl
), completer_factory(boost::none
));
3959 rgw::auth::Engine::result_t
3960 rgw::auth::s3::LocalEngine::authenticate(
3961 const boost::string_view
& _access_key_id
,
3962 const boost::string_view
& signature
,
3963 const string_to_sign_t
& string_to_sign
,
3964 const signature_factory_t
& signature_factory
,
3965 const completer_factory_t
& completer_factory
,
3966 const req_state
* const s
) const
3968 /* get the user info */
3969 RGWUserInfo user_info
;
3970 /* TODO(rzarzynski): we need to have string-view taking variant. */
3971 const std::string access_key_id
= _access_key_id
.to_string();
3972 if (rgw_get_user_info_by_access_key(store
, access_key_id
, user_info
) < 0) {
3973 ldout(cct
, 5) << "error reading user info, uid=" << access_key_id
3974 << " can't authenticate" << dendl
;
3975 return result_t::deny(-ERR_INVALID_ACCESS_KEY
);
3977 //TODO: Uncomment, when we have a migration plan in place.
3979 if (s->user->type != TYPE_RGW) {
3980 ldout(cct, 10) << "ERROR: User id of type: " << s->user->type
3981 << " is present" << dendl;
3986 const auto iter
= user_info
.access_keys
.find(access_key_id
);
3987 if (iter
== std::end(user_info
.access_keys
)) {
3988 ldout(cct
, 0) << "ERROR: access key not encoded in user info" << dendl
;
3989 return result_t::deny(-EPERM
);
3991 const RGWAccessKey
& k
= iter
->second
;
3993 const VersionAbstractor::server_signature_t server_signature
= \
3994 signature_factory(cct
, k
.key
, string_to_sign
);
3996 ldout(cct
, 15) << "string_to_sign="
3997 << rgw::crypt_sanitize::log_content
{string_to_sign
}
3999 ldout(cct
, 15) << "server signature=" << server_signature
<< dendl
;
4000 ldout(cct
, 15) << "client signature=" << signature
<< dendl
;
4001 ldout(cct
, 15) << "compare=" << signature
.compare(server_signature
) << dendl
;
4003 if (static_cast<boost::string_view
>(server_signature
) != signature
) {
4004 return result_t::deny(-ERR_SIGNATURE_NO_MATCH
);
4007 auto apl
= apl_factory
->create_apl_local(cct
, s
, user_info
, k
.subuser
);
4008 return result_t::grant(std::move(apl
), completer_factory(k
.key
));