]> git.proxmox.com Git - ceph.git/blob - ceph/src/rgw/rgw_rest_s3.cc
155f1d37c7b39a0f4b6f272353bebf7cc783a17a
[ceph.git] / ceph / src / rgw / rgw_rest_s3.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include <errno.h>
5 #include <array>
6 #include <string.h>
7
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>
16
17 #include "rgw_rest.h"
18 #include "rgw_rest_s3.h"
19 #include "rgw_rest_s3website.h"
20 #include "rgw_auth_s3.h"
21 #include "rgw_acl.h"
22 #include "rgw_policy_s3.h"
23 #include "rgw_user.h"
24 #include "rgw_cors.h"
25 #include "rgw_cors_s3.h"
26
27 #include "rgw_client_io.h"
28
29 #include "rgw_keystone.h"
30 #include "rgw_auth_keystone.h"
31 #include "rgw_auth_registry.h"
32
33 #include "rgw_es_query.h"
34
35 #include <typeinfo> // for 'typeid'
36
37 #include "rgw_ldap.h"
38 #include "rgw_token.h"
39 #include "rgw_rest_role.h"
40 #include "rgw_crypt.h"
41 #include "rgw_crypt_sanitize.h"
42
43 #include "include/assert.h"
44
45 #define dout_context g_ceph_context
46 #define dout_subsys ceph_subsys_rgw
47
48 using namespace rgw;
49 using namespace ceph::crypto;
50
51 using std::get;
52
53 void list_all_buckets_start(struct req_state *s)
54 {
55 s->formatter->open_array_section_in_ns("ListAllMyBucketsResult", XMLNS_AWS_S3);
56 }
57
58 void list_all_buckets_end(struct req_state *s)
59 {
60 s->formatter->close_section();
61 }
62
63 void dump_bucket(struct req_state *s, RGWBucketEnt& obj)
64 {
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();
69 }
70
71 void rgw_get_errno_s3(rgw_http_error *e , int err_no)
72 {
73 rgw_http_errors::const_iterator r = rgw_http_s3_errors.find(err_no);
74
75 if (r != rgw_http_s3_errors.end()) {
76 e->http_ret = r->second.first;
77 e->s3_code = r->second.second;
78 } else {
79 e->http_ret = 500;
80 e->s3_code = "UnknownError";
81 }
82 }
83
84 struct response_attr_param {
85 const char *param;
86 const char *http_attr;
87 };
88
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"},
96 {NULL, NULL},
97 };
98
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);
109 dump_errno(s);
110 dump_content_length(s, 0);
111 dump_redirect(s, s->redirect);
112 end_header(s, this);
113 return op_ret;
114 } else {
115 return RGWGetObj_ObjStore_S3::send_response_data(bl, bl_ofs, bl_len);
116 }
117 }
118
119 int RGWGetObj_ObjStore_S3Website::send_response_data_error()
120 {
121 return RGWGetObj_ObjStore_S3::send_response_data_error();
122 }
123
124 int RGWGetObj_ObjStore_S3::get_params()
125 {
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");
129
130 return RGWGetObj_ObjStore::get_params();
131 }
132
133 int RGWGetObj_ObjStore_S3::send_response_data_error()
134 {
135 bufferlist bl;
136 return send_response_data(bl, 0 , 0);
137 }
138
139 template <class T>
140 int decode_attr_bl_single_value(map<string, bufferlist>& attrs, const char *attr_name, T *result, T def_val)
141 {
142 map<string, bufferlist>::iterator iter = attrs.find(attr_name);
143 if (iter == attrs.end()) {
144 *result = def_val;
145 return 0;
146 }
147 bufferlist& bl = iter->second;
148 if (bl.length() == 0) {
149 *result = def_val;
150 return 0;
151 }
152 bufferlist::iterator bliter = bl.begin();
153 try {
154 ::decode(*result, bliter);
155 } catch (buffer::error& err) {
156 return -EIO;
157 }
158 return 0;
159 }
160
161 int RGWGetObj_ObjStore_S3::send_response_data(bufferlist& bl, off_t bl_ofs,
162 off_t bl_len)
163 {
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;
169
170 if (sent_header)
171 goto send_data;
172
173 if (custom_http_ret) {
174 set_req_state_err(s, 0);
175 dump_errno(s, custom_http_ret);
176 } else {
177 set_req_state_err(s, (partial_content && !op_ret) ? STATUS_PARTIAL_CONTENT
178 : op_ret);
179 dump_errno(s);
180 }
181
182 if (op_ret)
183 goto done;
184
185 if (range_str)
186 dump_range(s, start, end, s->obj_size);
187
188 if (s->system_request &&
189 s->info.args.exists(RGW_SYS_PARAM_PREFIX "prepend-metadata")) {
190
191 dump_header(s, "Rgwx-Object-Size", (long long)total_len);
192
193 if (rgwx_stat) {
194 /*
195 * in this case, we're not returning the object's content, only the prepended
196 * extra metadata
197 */
198 total_len = 0;
199 }
200
201 /* JSON encode object metadata */
202 JSONFormatter jf;
203 jf.open_object_section("obj_metadata");
204 encode_json("attrs", attrs, &jf);
205 utime_t ut(lastmod);
206 encode_json("mtime", ut, &jf);
207 jf.close_section();
208 stringstream ss;
209 jf.flush(ss);
210 metadata_bl.append(ss.str());
211 dump_header(s, "Rgwx-Embedded-Metadata-Len", metadata_bl.length());
212 total_len += metadata_bl.length();
213 }
214
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);
218 uint64_t pg_ver = 0;
219 int r = decode_attr_bl_single_value(attrs, RGW_ATTR_PG_VER, &pg_ver, (uint64_t)0);
220 if (r < 0) {
221 ldout(s->cct, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl;
222 }
223 dump_header(s, "Rgwx-Obj-PG-Ver", pg_ver);
224
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);
227 if (r < 0) {
228 ldout(s->cct, 0) << "ERROR: failed to decode pg ver attr, ignoring" << dendl;
229 }
230 if (source_zone_short_id != 0) {
231 dump_header(s, "Rgwx-Source-Zone-Short-Id", source_zone_short_id);
232 }
233 }
234
235 for (auto &it : crypt_http_responses)
236 dump_header(s, it.first, it.second);
237
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);
242 }
243
244
245 if (! op_ret) {
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
250 * etag value. */
251 dump_etag(s, lo_etag);
252 } else {
253 auto iter = attrs.find(RGW_ATTR_ETAG);
254 if (iter != attrs.end()) {
255 dump_etag(s, iter->second);
256 }
257 }
258
259 for (struct response_attr_param *p = resp_attr_params; p->param; p++) {
260 bool exists;
261 string val = s->info.args.get(p->param, &exists);
262 if (exists) {
263 if (strcmp(p->param, "response-content-type") != 0) {
264 response_attrs[p->http_attr] = val;
265 } else {
266 content_type_str = val;
267 content_type = content_type_str.c_str();
268 }
269 }
270 }
271
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();
279 }
280 } else if (iter->first.compare(RGW_ATTR_CONTENT_TYPE) == 0) {
281 /* Special handling for content_type. */
282 if (!content_type) {
283 content_type = iter->second.c_str();
284 }
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);
293 }
294 }
295 }
296
297 done:
298 for (riter = response_attrs.begin(); riter != response_attrs.end();
299 ++riter) {
300 dump_header(s, riter->first, riter->second);
301 }
302
303 if (op_ret == -ERR_NOT_MODIFIED) {
304 end_header(s, this);
305 } else {
306 if (!content_type)
307 content_type = "binary/octet-stream";
308
309 end_header(s, this, content_type);
310 }
311
312 if (metadata_bl.length()) {
313 dump_body(s, metadata_bl);
314 }
315 sent_header = true;
316
317 send_data:
318 if (get_data && !op_ret) {
319 int r = dump_body(s, bl.c_str() + bl_ofs, bl_len);
320 if (r < 0)
321 return r;
322 }
323
324 return 0;
325 }
326
327 int RGWGetObj_ObjStore_S3::get_decrypt_filter(std::unique_ptr<RGWGetDataCB> *filter, RGWGetDataCB* cb, bufferlist* manifest_bl)
328 {
329 int res = 0;
330 std::unique_ptr<BlockCrypt> block_crypt;
331 res = rgw_s3_prepare_decrypt(s, attrs, &block_crypt, crypt_http_responses);
332 if (res == 0) {
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));
336 if (f != nullptr) {
337 if (manifest_bl != nullptr) {
338 res = f->read_manifest(*manifest_bl);
339 if (res == 0) {
340 *filter = std::move(f);
341 }
342 }
343 }
344 }
345 }
346 return res;
347 }
348
349
350 void RGWListBuckets_ObjStore_S3::send_response_begin(bool has_buckets)
351 {
352 if (op_ret)
353 set_req_state_err(s, op_ret);
354 dump_errno(s);
355 dump_start(s);
356 end_header(s, NULL, "application/xml");
357
358 if (! op_ret) {
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");
362 sent_data = true;
363 }
364 }
365
366 void RGWListBuckets_ObjStore_S3::send_response_data(RGWUserBuckets& buckets)
367 {
368 if (!sent_data)
369 return;
370
371 map<string, RGWBucketEnt>& m = buckets.get_buckets();
372 map<string, RGWBucketEnt>::iterator iter;
373
374 for (iter = m.begin(); iter != m.end(); ++iter) {
375 RGWBucketEnt obj = iter->second;
376 dump_bucket(s, obj);
377 }
378 rgw_flush_formatter(s, s->formatter);
379 }
380
381 void RGWListBuckets_ObjStore_S3::send_response_end()
382 {
383 if (sent_data) {
384 s->formatter->close_section();
385 list_all_buckets_end(s);
386 rgw_flush_formatter_and_reset(s, s->formatter);
387 }
388 }
389
390 int RGWGetUsage_ObjStore_S3::get_params()
391 {
392 start_date = s->info.args.get("start-date");
393 end_date = s->info.args.get("end-date");
394 return 0;
395 }
396
397 static void dump_usage_categories_info(Formatter *formatter, const rgw_usage_log_entry& entry, map<string, bool> *categories)
398 {
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))
403 continue;
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
412 }
413 formatter->close_section(); // Category
414 }
415
416 void RGWGetUsage_ObjStore_S3::send_response()
417 {
418 if (op_ret < 0)
419 set_req_state_err(s, op_ret);
420 dump_errno(s);
421
422 end_header(s, this, "application/xml");
423 dump_start(s);
424 if (op_ret < 0)
425 return;
426
427 Formatter *formatter = s->formatter;
428 string last_owner;
429 bool user_section_open = false;
430
431 formatter->open_object_section("Usage");
432 if (show_log_entries) {
433 formatter->open_array_section("Entries");
434 }
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;
439
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();
445 }
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;
451 }
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
459 }
460
461 summary_map[ub.user].aggregate(entry, &categories);
462 }
463
464 if (show_log_entries) {
465 if (user_section_open) {
466 formatter->close_section(); // buckets
467 formatter->close_section(); //user
468 }
469 formatter->close_section(); // entries
470 }
471
472 if (show_log_sum) {
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
489 }
490
491 if (s->cct->_conf->rgw_rest_getusage_op_compat) {
492 formatter->open_object_section("Stats");
493 }
494
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);
498
499 if (s->cct->_conf->rgw_rest_getusage_op_compat) {
500 formatter->close_section(); //Stats
501 }
502
503 formatter->close_section(); // summary
504 }
505 formatter->close_section(); // usage
506 rgw_flush_formatter_and_reset(s, s->formatter);
507 }
508
509 int RGWListBucket_ObjStore_S3::get_params()
510 {
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");
515 } else {
516 marker.name = s->info.args.get("key-marker");
517 marker.instance = s->info.args.get("version-id-marker");
518 }
519 max_keys = s->info.args.get("max-keys");
520 op_ret = parse_max_keys();
521 if (op_ret < 0) {
522 return op_ret;
523 }
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");
529 if (shard_id_str) {
530 string err;
531 shard_id = strict_strtol(shard_id_str, 10, &err);
532 if (!err.empty()) {
533 ldout(s->cct, 5) << "bad shard id specified: " << shard_id_str << dendl;
534 return -EINVAL;
535 }
536 } else {
537 shard_id = s->bucket_instance_shard_id;
538 }
539 }
540 return 0;
541 }
542
543 void RGWListBucket_ObjStore_S3::send_versioned_response()
544 {
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);
555 }
556 s->formatter->dump_int("MaxKeys", max);
557 if (!delimiter.empty())
558 s->formatter->dump_string("Delimiter", delimiter);
559
560 s->formatter->dump_string("IsTruncated", (max && is_truncated ? "true"
561 : "false"));
562
563 bool encode_key = false;
564 if (strcasecmp(encoding_type.c_str(), "url") == 0) {
565 s->formatter->dump_string("EncodingType", "url");
566 encode_key = true;
567 }
568
569 if (op_ret >= 0) {
570 if (objs_container) {
571 s->formatter->open_array_section("Entries");
572 }
573
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"
577 : "Version");
578 s->formatter->open_object_section(section_name);
579 if (objs_container) {
580 s->formatter->dump_bool("IsDeleteMarker", iter->is_delete_marker());
581 }
582 rgw_obj_key key(iter->key);
583 if (encode_key) {
584 string key_name;
585 url_encode(key.name, key_name);
586 s->formatter->dump_string("Key", key_name);
587 } else {
588 s->formatter->dump_string("Key", key.name);
589 }
590 string version_id = key.instance;
591 if (version_id.empty()) {
592 version_id = "null";
593 }
594 if (s->system_request) {
595 if (iter->versioned_epoch > 0) {
596 s->formatter->dump_int("VersionedEpoch", iter->versioned_epoch);
597 }
598 s->formatter->dump_string("RgwxTag", iter->tag);
599 utime_t ut(iter->meta.mtime);
600 ut.gmtime_nsec(s->formatter->dump_stream("RgwxMtime"));
601 }
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");
609 }
610 dump_owner(s, iter->meta.owner, iter->meta.owner_display_name);
611 s->formatter->close_section();
612 }
613 if (objs_container) {
614 s->formatter->close_section();
615 }
616
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();
624 }
625 }
626 }
627 s->formatter->close_section();
628 rgw_flush_formatter_and_reset(s, s->formatter);
629 }
630
631 void RGWListBucket_ObjStore_S3::send_response()
632 {
633 if (op_ret < 0)
634 set_req_state_err(s, op_ret);
635 dump_errno(s);
636
637 end_header(s, this, "application/xml");
638 dump_start(s);
639 if (op_ret < 0)
640 return;
641
642 if (list_versions) {
643 send_versioned_response();
644 return;
645 }
646
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);
658
659 s->formatter->dump_string("IsTruncated", (max && is_truncated ? "true"
660 : "false"));
661
662 bool encode_key = false;
663 if (strcasecmp(encoding_type.c_str(), "url") == 0) {
664 s->formatter->dump_string("EncodingType", "url");
665 encode_key = true;
666 }
667
668 if (op_ret >= 0) {
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");
673 if (encode_key) {
674 string key_name;
675 url_encode(key.name, key_name);
676 s->formatter->dump_string("Key", key_name);
677 } else {
678 s->formatter->dump_string("Key", key.name);
679 }
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);
687 }
688 s->formatter->close_section();
689 }
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();
697 }
698 }
699 }
700 s->formatter->close_section();
701 rgw_flush_formatter_and_reset(s, s->formatter);
702 }
703
704 void RGWGetBucketLogging_ObjStore_S3::send_response()
705 {
706 dump_errno(s);
707 end_header(s, this, "application/xml");
708 dump_start(s);
709
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);
713 }
714
715 void RGWGetBucketLocation_ObjStore_S3::send_response()
716 {
717 dump_errno(s);
718 end_header(s, this);
719 dump_start(s);
720
721 RGWZoneGroup zonegroup;
722 string api_name;
723
724 int ret = store->get_zonegroup(s->bucket_info.zonegroup, zonegroup);
725 if (ret >= 0) {
726 api_name = zonegroup.api_name;
727 } else {
728 if (s->bucket_info.zonegroup != "default") {
729 api_name = s->bucket_info.zonegroup;
730 }
731 }
732
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);
736 }
737
738 void RGWGetBucketVersioning_ObjStore_S3::send_response()
739 {
740 dump_errno(s);
741 end_header(s, this, "application/xml");
742 dump_start(s);
743
744 s->formatter->open_object_section_in_ns("VersioningConfiguration", XMLNS_AWS_S3);
745 if (versioned) {
746 const char *status = (versioning_enabled ? "Enabled" : "Suspended");
747 s->formatter->dump_string("Status", status);
748 }
749 s->formatter->close_section();
750 rgw_flush_formatter_and_reset(s, s->formatter);
751 }
752
753 class RGWSetBucketVersioningParser : public RGWXMLParser
754 {
755 XMLObj *alloc_obj(const char *el) override {
756 return new XMLObj;
757 }
758
759 public:
760 RGWSetBucketVersioningParser() {}
761 ~RGWSetBucketVersioningParser() override {}
762
763 int get_versioning_status(bool *status) {
764 XMLObj *config = find_first("VersioningConfiguration");
765 if (!config)
766 return -EINVAL;
767
768 *status = false;
769
770 XMLObj *field = config->find_first("Status");
771 if (!field)
772 return 0;
773
774 string& s = field->get_data();
775
776 if (stringcasecmp(s, "Enabled") == 0) {
777 *status = true;
778 } else if (stringcasecmp(s, "Suspended") != 0) {
779 return -EINVAL;
780 }
781
782 return 0;
783 }
784 };
785
786 int RGWSetBucketVersioning_ObjStore_S3::get_params()
787 {
788 char *data = nullptr;
789 int len = 0;
790 int r =
791 rgw_rest_read_all_input(s, &data, &len, s->cct->_conf->rgw_max_put_param_size, false);
792 if (r < 0) {
793 return r;
794 }
795
796 auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
797
798 r = do_aws4_auth_completion();
799 if (r < 0) {
800 return r;
801 }
802
803 RGWSetBucketVersioningParser parser;
804
805 if (!parser.init()) {
806 ldout(s->cct, 0) << "ERROR: failed to initialize parser" << dendl;
807 r = -EIO;
808 return r;
809 }
810
811 if (!parser.parse(data, len, 1)) {
812 ldout(s->cct, 10) << "failed to parse data: " << data << dendl;
813 r = -EINVAL;
814 return r;
815 }
816
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);
820 }
821
822 r = parser.get_versioning_status(&enable_versioning);
823
824 return r;
825 }
826
827 void RGWSetBucketVersioning_ObjStore_S3::send_response()
828 {
829 if (op_ret)
830 set_req_state_err(s, op_ret);
831 dump_errno(s);
832 end_header(s);
833 }
834
835 int RGWSetBucketWebsite_ObjStore_S3::get_params()
836 {
837 char *data = nullptr;
838 int len = 0;
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);
841
842 if (r < 0) {
843 return r;
844 }
845
846 auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
847
848 r = do_aws4_auth_completion();
849 if (r < 0) {
850 return r;
851 }
852
853 bufferptr in_ptr(data, len);
854 in_data.append(in_ptr);
855
856 RGWXMLDecoder::XMLParser parser;
857 if (!parser.init()) {
858 ldout(s->cct, 0) << "ERROR: failed to initialize parser" << dendl;
859 return -EIO;
860 }
861
862 if (!parser.parse(data, len, 1)) {
863 string str(data, len);
864 ldout(s->cct, 5) << "failed to parse xml: " << str << dendl;
865 return -EINVAL;
866 }
867
868 try {
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;
873 return -EINVAL;
874 }
875
876 return 0;
877 }
878
879 void RGWSetBucketWebsite_ObjStore_S3::send_response()
880 {
881 if (op_ret < 0)
882 set_req_state_err(s, op_ret);
883 dump_errno(s);
884 end_header(s);
885 }
886
887 void RGWDeleteBucketWebsite_ObjStore_S3::send_response()
888 {
889 if (op_ret == 0) {
890 op_ret = STATUS_NO_CONTENT;
891 }
892 set_req_state_err(s, op_ret);
893 dump_errno(s);
894 end_header(s);
895 }
896
897 void RGWGetBucketWebsite_ObjStore_S3::send_response()
898 {
899 if (op_ret)
900 set_req_state_err(s, op_ret);
901 dump_errno(s);
902 end_header(s, this, "application/xml");
903 dump_start(s);
904
905 if (op_ret < 0) {
906 return;
907 }
908
909 RGWBucketWebsiteConf& conf = s->bucket_info.website_conf;
910
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);
915 }
916
917 static void dump_bucket_metadata(struct req_state *s, RGWBucketEnt& bucket)
918 {
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));
921 }
922
923 void RGWStatBucket_ObjStore_S3::send_response()
924 {
925 if (op_ret >= 0) {
926 dump_bucket_metadata(s, bucket);
927 }
928
929 set_req_state_err(s, op_ret);
930 dump_errno(s);
931
932 end_header(s, this);
933 dump_start(s);
934 }
935
936 static int create_s3_policy(struct req_state *s, RGWRados *store,
937 RGWAccessControlPolicy_S3& s3policy,
938 ACLOwner& owner)
939 {
940 if (s->has_acl_header) {
941 if (!s->canned_acl.empty())
942 return -ERR_INVALID_REQUEST;
943
944 return s3policy.create_from_headers(store, s->info.env, owner);
945 }
946
947 return s3policy.create_canned(owner, s->bucket_owner, s->canned_acl);
948 }
949
950 class RGWLocationConstraint : public XMLObj
951 {
952 public:
953 RGWLocationConstraint() {}
954 ~RGWLocationConstraint() override {}
955 bool xml_end(const char *el) override {
956 if (!el)
957 return false;
958
959 location_constraint = get_data();
960
961 return true;
962 }
963
964 string location_constraint;
965 };
966
967 class RGWCreateBucketConfig : public XMLObj
968 {
969 public:
970 RGWCreateBucketConfig() {}
971 ~RGWCreateBucketConfig() override {}
972 };
973
974 class RGWCreateBucketParser : public RGWXMLParser
975 {
976 XMLObj *alloc_obj(const char *el) override {
977 return new XMLObj;
978 }
979
980 public:
981 RGWCreateBucketParser() {}
982 ~RGWCreateBucketParser() override {}
983
984 bool get_location_constraint(string& zone_group) {
985 XMLObj *config = find_first("CreateBucketConfiguration");
986 if (!config)
987 return false;
988
989 XMLObj *constraint = config->find_first("LocationConstraint");
990 if (!constraint)
991 return false;
992
993 zone_group = constraint->get_data();
994
995 return true;
996 }
997 };
998
999 int RGWCreateBucket_ObjStore_S3::get_params()
1000 {
1001 RGWAccessControlPolicy_S3 s3policy(s->cct);
1002
1003 int r = create_s3_policy(s, store, s3policy, s->owner);
1004 if (r < 0)
1005 return r;
1006
1007 policy = s3policy;
1008
1009 int len = 0;
1010 char *data = nullptr;
1011
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);
1014
1015 if ((op_ret < 0) && (op_ret != -ERR_LENGTH_REQUIRED))
1016 return op_ret;
1017
1018 auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
1019
1020 const int auth_ret = do_aws4_auth_completion();
1021 if (auth_ret < 0) {
1022 return auth_ret;
1023 }
1024
1025 bufferptr in_ptr(data, len);
1026 in_data.append(in_ptr);
1027
1028 if (len) {
1029 RGWCreateBucketParser parser;
1030
1031 if (!parser.init()) {
1032 ldout(s->cct, 0) << "ERROR: failed to initialize parser" << dendl;
1033 return -EIO;
1034 }
1035
1036 bool success = parser.parse(data, len, 1);
1037 ldout(s->cct, 20) << "create bucket input data=" << data << dendl;
1038
1039 if (!success) {
1040 ldout(s->cct, 0) << "failed to parse input: " << data << dendl;
1041 return -EINVAL;
1042 }
1043
1044 if (!parser.get_location_constraint(location_constraint)) {
1045 ldout(s->cct, 0) << "provided input did not specify location constraint correctly" << dendl;
1046 return -EINVAL;
1047 }
1048
1049 ldout(s->cct, 10) << "create bucket location constraint: "
1050 << location_constraint << dendl;
1051 }
1052
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);
1057 }
1058
1059 return 0;
1060 }
1061
1062 void RGWCreateBucket_ObjStore_S3::send_response()
1063 {
1064 if (op_ret == -ERR_BUCKET_EXISTS)
1065 op_ret = 0;
1066 if (op_ret)
1067 set_req_state_err(s, op_ret);
1068 dump_errno(s);
1069 end_header(s);
1070
1071 if (op_ret < 0)
1072 return;
1073
1074 if (s->system_request) {
1075 JSONFormatter f; /* use json formatter for system requests output */
1076
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);
1081 f.close_section();
1082 rgw_flush_formatter_and_reset(s, &f);
1083 }
1084 }
1085
1086 void RGWDeleteBucket_ObjStore_S3::send_response()
1087 {
1088 int r = op_ret;
1089 if (!r)
1090 r = STATUS_NO_CONTENT;
1091
1092 set_req_state_err(s, r);
1093 dump_errno(s);
1094 end_header(s, this);
1095
1096 if (s->system_request) {
1097 JSONFormatter f; /* use json formatter for system requests output */
1098
1099 f.open_object_section("info");
1100 encode_json("object_ver", objv_tracker.read_version, &f);
1101 f.close_section();
1102 rgw_flush_formatter_and_reset(s, &f);
1103 }
1104 }
1105
1106 int RGWPutObj_ObjStore_S3::get_params()
1107 {
1108 if (!s->length)
1109 return -ERR_LENGTH_REQUIRED;
1110
1111 RGWObjectCtx& obj_ctx = *static_cast<RGWObjectCtx *>(s->obj_ctx);
1112 map<string, bufferlist> src_attrs;
1113 size_t pos;
1114 int ret;
1115
1116 RGWAccessControlPolicy_S3 s3policy(s->cct);
1117 ret = create_s3_policy(s, store, s3policy, s->owner);
1118 if (ret < 0)
1119 return ret;
1120
1121 policy = s3policy;
1122
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");
1127
1128 /* handle x-amz-copy-source */
1129
1130 if (copy_source) {
1131 copy_source_bucket_name = copy_source;
1132 pos = copy_source_bucket_name.find("/");
1133 if (pos == std::string::npos) {
1134 ret = -EINVAL;
1135 ldout(s->cct, 5) << "x-amz-copy-source bad format" << dendl;
1136 return ret;
1137 }
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);
1144 } else {
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));
1147 }
1148 pos = copy_source_bucket_name.find(":");
1149 if (pos == std::string::npos) {
1150 copy_source_tenant_name = s->src_tenant_name;
1151 } else {
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()) {
1155 ret = -EINVAL;
1156 ldout(s->cct, 5) << "source bucket name is empty" << dendl;
1157 return ret;
1158 }
1159 }
1160 ret = store->get_bucket_info(obj_ctx,
1161 copy_source_tenant_name,
1162 copy_source_bucket_name,
1163 copy_source_bucket_info,
1164 NULL, &src_attrs);
1165 if (ret < 0) {
1166 ldout(s->cct, 5) << __func__ << "(): get_bucket_info() returned ret=" << ret << dendl;
1167 return ret;
1168 }
1169
1170 /* handle x-amz-copy-source-range */
1171
1172 if (copy_source_range) {
1173 string range = copy_source_range;
1174 pos = range.find("=");
1175 if (pos == std::string::npos) {
1176 ret = -EINVAL;
1177 ldout(s->cct, 5) << "x-amz-copy-source-range bad format" << dendl;
1178 return ret;
1179 }
1180 range = range.substr(pos + 1);
1181 pos = range.find("-");
1182 if (pos == std::string::npos) {
1183 ret = -EINVAL;
1184 ldout(s->cct, 5) << "x-amz-copy-source-range bad format" << dendl;
1185 return ret;
1186 }
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);
1191 }
1192
1193 } /* copy_source */
1194
1195 return RGWPutObj_ObjStore::get_params();
1196 }
1197
1198 int RGWPutObj_ObjStore_S3::get_data(bufferlist& bl)
1199 {
1200 const int ret = RGWPutObj_ObjStore::get_data(bl);
1201 if (ret == 0) {
1202 const int ret_auth = do_aws4_auth_completion();
1203 if (ret_auth < 0) {
1204 return ret_auth;
1205 }
1206 }
1207
1208 return ret;
1209 }
1210
1211 static int get_success_retcode(int code)
1212 {
1213 switch (code) {
1214 case 201:
1215 return STATUS_CREATED;
1216 case 204:
1217 return STATUS_NO_CONTENT;
1218 }
1219 return 0;
1220 }
1221
1222 void RGWPutObj_ObjStore_S3::send_response()
1223 {
1224 if (op_ret) {
1225 set_req_state_err(s, op_ret);
1226 dump_errno(s);
1227 } else {
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);
1232 }
1233 if (!copy_source) {
1234 dump_errno(s);
1235 dump_etag(s, etag);
1236 dump_content_length(s, 0);
1237 for (auto &it : crypt_http_responses)
1238 dump_header(s, it.first, it.second);
1239 } else {
1240 dump_errno(s);
1241 end_header(s, this, "application/xml");
1242 dump_start(s);
1243 struct tm tmp;
1244 utime_t ut(mtime);
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);
1252 }
1253 s->formatter->dump_string("ETag", etag);
1254 s->formatter->close_section();
1255 rgw_flush_formatter_and_reset(s, s->formatter);
1256 return;
1257 }
1258 }
1259 if (s->system_request && !real_clock::is_zero(mtime)) {
1260 dump_epoch_header(s, "Rgwx-Mtime", mtime);
1261 }
1262 end_header(s, this);
1263 }
1264
1265 static inline int get_obj_attrs(RGWRados *store, struct req_state *s, rgw_obj& obj, map<string, bufferlist>& attrs)
1266 {
1267 RGWRados::Object op_target(store, s->bucket_info, *static_cast<RGWObjectCtx *>(s->obj_ctx), obj);
1268 RGWRados::Object::Read read_op(&op_target);
1269
1270 read_op.params.attrs = &attrs;
1271
1272 return read_op.prepare();
1273 }
1274
1275 static inline void set_attr(map<string, bufferlist>& attrs, const char* key, const std::string& value)
1276 {
1277 bufferlist bl;
1278 ::encode(value,bl);
1279 attrs.emplace(key, std::move(bl));
1280 }
1281
1282 static inline void set_attr(map<string, bufferlist>& attrs, const char* key, const char* value)
1283 {
1284 bufferlist bl;
1285 ::encode(value,bl);
1286 attrs.emplace(key, std::move(bl));
1287 }
1288
1289 int RGWPutObj_ObjStore_S3::get_decrypt_filter(
1290 std::unique_ptr<RGWGetDataCB>* filter,
1291 RGWGetDataCB* cb,
1292 map<string, bufferlist>& attrs,
1293 bufferlist* manifest_bl)
1294 {
1295 std::map<std::string, std::string> crypt_http_responses_unused;
1296
1297 int res = 0;
1298 std::unique_ptr<BlockCrypt> block_crypt;
1299 res = rgw_s3_prepare_decrypt(s, attrs, &block_crypt, crypt_http_responses_unused);
1300 if (res == 0) {
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));
1304 if (f != nullptr) {
1305 if (manifest_bl != nullptr) {
1306 res = f->read_manifest(*manifest_bl);
1307 if (res == 0) {
1308 *filter = std::move(f);
1309 }
1310 }
1311 }
1312 }
1313 }
1314 return res;
1315 }
1316
1317 int RGWPutObj_ObjStore_S3::get_encrypt_filter(
1318 std::unique_ptr<RGWPutObjDataProcessor>* filter,
1319 RGWPutObjDataProcessor* cb)
1320 {
1321 int res = 0;
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;
1328 string meta_oid;
1329 meta_oid = mp->get_meta();
1330
1331 rgw_obj obj;
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);
1335 if (res == 0) {
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)));
1343 }
1344 }
1345 /* it is ok, to not have encryption at all */
1346 }
1347 else
1348 {
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)));
1354 }
1355 }
1356 return res;
1357 }
1358
1359 void RGWPostObj_ObjStore_S3::rebuild_key(string& key)
1360 {
1361 static string var = "${filename}";
1362 int pos = key.find(var);
1363 if (pos < 0)
1364 return;
1365
1366 string new_key = key.substr(0, pos);
1367 new_key.append(filename);
1368 new_key.append(key.substr(pos + var.size()));
1369
1370 key = new_key;
1371 }
1372
1373 std::string RGWPostObj_ObjStore_S3::get_current_filename() const
1374 {
1375 return s->object.name;
1376 }
1377
1378 std::string RGWPostObj_ObjStore_S3::get_current_content_type() const
1379 {
1380 return content_type;
1381 }
1382
1383 int RGWPostObj_ObjStore_S3::get_params()
1384 {
1385 op_ret = RGWPostObj_ObjStore::get_params();
1386 if (op_ret < 0) {
1387 return op_ret;
1388 }
1389
1390 ldout(s->cct, 20) << "adding bucket to policy env: " << s->bucket.name
1391 << dendl;
1392 env.add_var("bucket", s->bucket.name);
1393
1394 bool done;
1395 do {
1396 struct post_form_part part;
1397 int r = read_form_part_header(&part, done);
1398 if (r < 0)
1399 return r;
1400
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;
1404
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;
1409
1410 for (const auto& param_pair : pair.second.params) {
1411 ldout(s->cct, 20) << " " << param_pair.first
1412 << " -> " << param_pair.second << dendl;
1413 }
1414 }
1415 }
1416
1417 if (done) { /* unexpected here */
1418 err_msg = "Malformed request";
1419 return -EINVAL;
1420 }
1421
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;
1427 }
1428 parts[part.name] = part;
1429 break;
1430 }
1431
1432 bool boundary;
1433 uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
1434 r = read_data(part.data, chunk_size, boundary, done);
1435 if (!boundary) {
1436 err_msg = "Couldn't find boundary";
1437 return -EINVAL;
1438 }
1439 parts[part.name] = part;
1440 string part_str(part.data.c_str(), part.data.length());
1441 env.add_var(part.name, part_str);
1442 } while (!done);
1443
1444 string object_str;
1445 if (!part_str(parts, "key", &object_str)) {
1446 err_msg = "Key not specified";
1447 return -EINVAL;
1448 }
1449
1450 s->object = rgw_obj_key(object_str);
1451
1452 rebuild_key(s->object.name);
1453
1454 if (s->object.empty()) {
1455 err_msg = "Empty object name";
1456 return -EINVAL;
1457 }
1458
1459 env.add_var("key", s->object.name);
1460
1461 part_str(parts, "Content-Type", &content_type);
1462 env.add_var("Content-Type", content_type);
1463
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)
1470 break;
1471
1472 string attr_name = RGW_ATTR_PREFIX;
1473 attr_name.append(n);
1474
1475 /* need to null terminate it */
1476 bufferlist& data = piter->second.data;
1477 string str = string(data.c_str(), data.length());
1478
1479 bufferlist attr_bl;
1480 attr_bl.append(str.c_str(), str.size() + 1);
1481
1482 attrs[attr_name] = attr_bl;
1483 }
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());
1493
1494 bufferlist attr_bl;
1495 attr_bl.append(str.c_str(), str.size() + 1);
1496
1497 attrs[attr_name] = attr_bl;
1498 }
1499
1500 int r = get_policy();
1501 if (r < 0)
1502 return r;
1503
1504 min_len = post_policy.min_length;
1505 max_len = post_policy.max_length;
1506
1507 return 0;
1508 }
1509
1510 int RGWPostObj_ObjStore_S3::get_policy()
1511 {
1512 if (part_bl(parts, "policy", &s->auth.s3_postobj_creds.encoded_policy)) {
1513 bool aws4_auth = false;
1514
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;
1520 aws4_auth = true;
1521 } else {
1522 ldout(s->cct, 0) << "Signature verification algorithm AWS v2" << dendl;
1523 }
1524
1525 // check that the signature matches the encoded policy
1526 if (aws4_auth) {
1527 /* AWS4 */
1528
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";
1534 return -EINVAL;
1535 }
1536
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";
1542 return -EINVAL;
1543 }
1544
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";
1550 return -EINVAL;
1551 }
1552 } else {
1553 /* AWS2 */
1554
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";
1560 return -EINVAL;
1561 }
1562
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";
1566 return -EINVAL;
1567 }
1568 }
1569
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()
1572 * method. */
1573 const int ret = rgw::auth::Strategy::apply(auth_registry_ptr->get_s3_post(), s);
1574 if (ret != 0) {
1575 return -EACCES;
1576 } else {
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;
1581 }
1582
1583 ceph::bufferlist decoded_policy;
1584 try {
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";
1589 return -EINVAL;
1590 }
1591
1592 decoded_policy.append('\0'); // NULL terminate
1593 ldout(s->cct, 0) << "POST policy: " << decoded_policy.c_str() << dendl;
1594
1595
1596 int r = post_policy.from_json(decoded_policy, err_msg);
1597 if (r < 0) {
1598 if (err_msg.empty()) {
1599 err_msg = "Failed to parse policy";
1600 }
1601 ldout(s->cct, 0) << "failed to parse policy" << dendl;
1602 return -EINVAL;
1603 }
1604
1605 if (aws4_auth) {
1606 /* AWS4 */
1607 post_policy.set_var_checked("x-amz-signature");
1608 } else {
1609 /* AWS2 */
1610 post_policy.set_var_checked("AWSAccessKeyId");
1611 post_policy.set_var_checked("signature");
1612 }
1613 post_policy.set_var_checked("policy");
1614
1615 r = post_policy.check(&env, err_msg);
1616 if (r < 0) {
1617 if (err_msg.empty()) {
1618 err_msg = "Policy check failed";
1619 }
1620 ldout(s->cct, 0) << "policy check failed" << dendl;
1621 return r;
1622 }
1623
1624 } else {
1625 ldout(s->cct, 0) << "No attached policy found!" << dendl;
1626 }
1627
1628 string canned_acl;
1629 part_str(parts, "acl", &canned_acl);
1630
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";
1635 return -EINVAL;
1636 }
1637
1638 policy = s3policy;
1639
1640 return 0;
1641 }
1642
1643 int RGWPostObj_ObjStore_S3::complete_get_params()
1644 {
1645 bool done;
1646 do {
1647 struct post_form_part part;
1648 int r = read_form_part_header(&part, done);
1649 if (r < 0) {
1650 return r;
1651 }
1652
1653 ceph::bufferlist part_data;
1654 bool boundary;
1655 uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
1656 r = read_data(part.data, chunk_size, boundary, done);
1657 if (!boundary) {
1658 return -EINVAL;
1659 }
1660
1661 /* Just reading the data but not storing any results of that. */
1662 } while (!done);
1663
1664 return 0;
1665 }
1666
1667 int RGWPostObj_ObjStore_S3::get_data(ceph::bufferlist& bl, bool& again)
1668 {
1669 bool boundary;
1670 bool done;
1671
1672 const uint64_t chunk_size = s->cct->_conf->rgw_max_chunk_size;
1673 int r = read_data(bl, chunk_size, boundary, done);
1674 if (r < 0) {
1675 return r;
1676 }
1677
1678 if (boundary) {
1679 if (!done) {
1680 /* Reached end of data, let's drain the rest of the params */
1681 r = complete_get_params();
1682 if (r < 0) {
1683 return r;
1684 }
1685 }
1686 }
1687
1688 again = !boundary;
1689 return bl.length();
1690 }
1691
1692 void RGWPostObj_ObjStore_S3::send_response()
1693 {
1694 if (op_ret == 0 && parts.count("success_action_redirect")) {
1695 string redirect;
1696
1697 part_str(parts, "success_action_redirect", &redirect);
1698
1699 string tenant;
1700 string bucket;
1701 string key;
1702 string etag_str = "\"";
1703
1704 etag_str.append(etag);
1705 etag_str.append("\"");
1706
1707 string etag_url;
1708
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);
1713
1714 if (!s->bucket_tenant.empty()) {
1715 /*
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.
1722 */
1723 redirect.append("?tenant=");
1724 redirect.append(tenant);
1725 redirect.append("&bucket=");
1726 redirect.append(bucket);
1727 } else {
1728 redirect.append("?bucket=");
1729 redirect.append(bucket);
1730 }
1731 redirect.append("&key=");
1732 redirect.append(key);
1733 redirect.append("&etag=");
1734 redirect.append(etag_url);
1735
1736 int r = check_utf8(redirect.c_str(), redirect.size());
1737 if (r < 0) {
1738 op_ret = r;
1739 goto done;
1740 }
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;
1746
1747 part_str(parts, "success_action_status", &status_string);
1748
1749 int r = stringtoul(status_string, &status_int);
1750 if (r < 0) {
1751 op_ret = r;
1752 goto done;
1753 }
1754
1755 switch (status_int) {
1756 case 200:
1757 break;
1758 case 201:
1759 op_ret = STATUS_CREATED;
1760 break;
1761 default:
1762 op_ret = STATUS_NO_CONTENT;
1763 break;
1764 }
1765 } else if (! op_ret) {
1766 op_ret = STATUS_NO_CONTENT;
1767 }
1768
1769 done:
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();
1783 }
1784 s->err.message = err_msg;
1785 set_req_state_err(s, op_ret);
1786 dump_errno(s);
1787 if (op_ret >= 0) {
1788 dump_content_length(s, s->formatter->get_len());
1789 }
1790 end_header(s, this);
1791 if (op_ret != STATUS_CREATED)
1792 return;
1793
1794 rgw_flush_formatter_and_reset(s, s->formatter);
1795 }
1796
1797 int RGWPostObj_ObjStore_S3::get_encrypt_filter(
1798 std::unique_ptr<RGWPutObjDataProcessor>* filter, RGWPutObjDataProcessor* cb)
1799 {
1800 int res = 0;
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)));
1806 }
1807 else
1808 *filter = nullptr;
1809 return res;
1810 }
1811
1812 int RGWDeleteObj_ObjStore_S3::get_params()
1813 {
1814 const char *if_unmod = s->info.env->get("HTTP_X_AMZ_DELETE_IF_UNMODIFIED_SINCE");
1815
1816 if (s->system_request) {
1817 s->info.args.get_bool(RGW_SYS_PARAM_PREFIX "no-precondition-error", &no_precondition_error, false);
1818 }
1819
1820 if (if_unmod) {
1821 std::string if_unmod_decoded = url_decode(if_unmod);
1822 uint64_t epoch;
1823 uint64_t nsec;
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;
1826 return -EINVAL;
1827 }
1828 unmod_since = utime_t(epoch, nsec).to_real_time();
1829 }
1830
1831 return 0;
1832 }
1833
1834 void RGWDeleteObj_ObjStore_S3::send_response()
1835 {
1836 int r = op_ret;
1837 if (r == -ENOENT)
1838 r = 0;
1839 if (!r)
1840 r = STATUS_NO_CONTENT;
1841
1842 set_req_state_err(s, r);
1843 dump_errno(s);
1844 if (!version_id.empty()) {
1845 dump_header(s, "x-amz-version-id", version_id);
1846 }
1847 if (delete_marker) {
1848 dump_header(s, "x-amz-delete-marker", "true");
1849 }
1850 end_header(s, this);
1851 }
1852
1853 int RGWCopyObj_ObjStore_S3::init_dest_policy()
1854 {
1855 RGWAccessControlPolicy_S3 s3policy(s->cct);
1856
1857 /* build a policy for the target object */
1858 int r = create_s3_policy(s, store, s3policy, s->owner);
1859 if (r < 0)
1860 return r;
1861
1862 dest_policy = s3policy;
1863
1864 return 0;
1865 }
1866
1867 int RGWCopyObj_ObjStore_S3::get_params()
1868 {
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");
1873
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;
1880
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", &copy_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");
1887
1888 if (client_id.empty() || op_id.empty()) {
1889 ldout(s->cct, 0) <<
1890 RGW_SYS_PARAM_PREFIX "client-id or "
1891 RGW_SYS_PARAM_PREFIX "op-id were not provided, "
1892 "required for intra-region copy"
1893 << dendl;
1894 return -EINVAL;
1895 }
1896 }
1897 }
1898
1899 const char *md_directive = s->info.env->get("HTTP_X_AMZ_METADATA_DIRECTIVE");
1900 if (md_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
1907 } else {
1908 ldout(s->cct, 0) << "invalid metadata directive" << dendl;
1909 return -EINVAL;
1910 }
1911 }
1912
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"
1921 << dendl;
1922 return -ERR_INVALID_REQUEST;
1923 }
1924 return 0;
1925 }
1926
1927 void RGWCopyObj_ObjStore_S3::send_partial_response(off_t ofs)
1928 {
1929 if (! sent_header) {
1930 if (op_ret)
1931 set_req_state_err(s, op_ret);
1932 dump_errno(s);
1933
1934 end_header(s, this, "application/xml");
1935 if (op_ret == 0) {
1936 s->formatter->open_object_section_in_ns("CopyObjectResult", XMLNS_AWS_S3);
1937 }
1938 sent_header = true;
1939 } else {
1940 /* Send progress field. Note that this diverge from the original S3
1941 * spec. We do this in order to keep connection alive.
1942 */
1943 s->formatter->dump_int("Progress", (uint64_t)ofs);
1944 }
1945 rgw_flush_formatter(s, s->formatter);
1946 }
1947
1948 void RGWCopyObj_ObjStore_S3::send_response()
1949 {
1950 if (!sent_header)
1951 send_partial_response(0);
1952
1953 if (op_ret == 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));
1958 }
1959 s->formatter->close_section();
1960 rgw_flush_formatter_and_reset(s, s->formatter);
1961 }
1962 }
1963
1964 void RGWGetACLs_ObjStore_S3::send_response()
1965 {
1966 if (op_ret)
1967 set_req_state_err(s, op_ret);
1968 dump_errno(s);
1969 end_header(s, this, "application/xml");
1970 dump_start(s);
1971 rgw_flush_formatter(s, s->formatter);
1972 dump_body(s, acls);
1973 }
1974
1975 int RGWPutACLs_ObjStore_S3::get_params()
1976 {
1977 int ret = RGWPutACLs_ObjStore::get_params();
1978 if (ret >= 0) {
1979 const int ret_auth = do_aws4_auth_completion();
1980 if (ret_auth < 0) {
1981 return ret_auth;
1982 }
1983 }
1984 return ret;
1985 }
1986
1987 int RGWPutACLs_ObjStore_S3::get_policy_from_state(RGWRados *store,
1988 struct req_state *s,
1989 stringstream& ss)
1990 {
1991 RGWAccessControlPolicy_S3 s3policy(s->cct);
1992
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();
1997 }
1998
1999 int r = create_s3_policy(s, store, s3policy, owner);
2000 if (r < 0)
2001 return r;
2002
2003 s3policy.to_xml(ss);
2004
2005 return 0;
2006 }
2007
2008 void RGWPutACLs_ObjStore_S3::send_response()
2009 {
2010 if (op_ret)
2011 set_req_state_err(s, op_ret);
2012 dump_errno(s);
2013 end_header(s, this, "application/xml");
2014 dump_start(s);
2015 }
2016
2017 void RGWGetLC_ObjStore_S3::execute()
2018 {
2019 config.set_ctx(s->cct);
2020
2021 map<string, bufferlist>::iterator aiter = s->bucket_attrs.find(RGW_ATTR_LC);
2022 if (aiter == s->bucket_attrs.end()) {
2023 op_ret = -ENOENT;
2024 return;
2025 }
2026
2027 bufferlist::iterator iter(&aiter->second);
2028 try {
2029 config.decode(iter);
2030 } catch (const buffer::error& e) {
2031 ldout(s->cct, 0) << __func__ << "decode life cycle config failed" << dendl;
2032 op_ret = -EIO;
2033 return;
2034 }
2035 }
2036
2037 void RGWGetLC_ObjStore_S3::send_response()
2038 {
2039 if (op_ret) {
2040 if (op_ret == -ENOENT) {
2041 set_req_state_err(s, ERR_NO_SUCH_LC);
2042 } else {
2043 set_req_state_err(s, op_ret);
2044 }
2045 }
2046 dump_errno(s);
2047 end_header(s, this, "application/xml");
2048 dump_start(s);
2049
2050 if (op_ret < 0)
2051 return;
2052
2053 config.dump_xml(s->formatter);
2054 rgw_flush_formatter_and_reset(s, s->formatter);
2055 }
2056
2057 void RGWPutLC_ObjStore_S3::send_response()
2058 {
2059 if (op_ret)
2060 set_req_state_err(s, op_ret);
2061 dump_errno(s);
2062 end_header(s, this, "application/xml");
2063 dump_start(s);
2064 }
2065
2066 void RGWDeleteLC_ObjStore_S3::send_response()
2067 {
2068 if (op_ret == 0)
2069 op_ret = STATUS_NO_CONTENT;
2070 if (op_ret) {
2071 set_req_state_err(s, op_ret);
2072 }
2073 dump_errno(s);
2074 end_header(s, this, "application/xml");
2075 dump_start(s);
2076 }
2077
2078 void RGWGetCORS_ObjStore_S3::send_response()
2079 {
2080 if (op_ret) {
2081 if (op_ret == -ENOENT)
2082 set_req_state_err(s, ERR_NOT_FOUND);
2083 else
2084 set_req_state_err(s, op_ret);
2085 }
2086 dump_errno(s);
2087 end_header(s, NULL, "application/xml");
2088 dump_start(s);
2089 if (! op_ret) {
2090 string cors;
2091 RGWCORSConfiguration_S3 *s3cors =
2092 static_cast<RGWCORSConfiguration_S3 *>(&bucket_cors);
2093 stringstream ss;
2094
2095 s3cors->to_xml(ss);
2096 cors = ss.str();
2097 dump_body(s, cors);
2098 }
2099 }
2100
2101 int RGWPutCORS_ObjStore_S3::get_params()
2102 {
2103 int r;
2104 char *data = nullptr;
2105 int len = 0;
2106 RGWCORSXMLParser_S3 parser(s->cct);
2107 RGWCORSConfiguration_S3 *cors_config;
2108
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);
2111 if (r < 0) {
2112 return r;
2113 }
2114
2115 auto data_deleter = std::unique_ptr<char, decltype(free)*>{data, free};
2116
2117 r = do_aws4_auth_completion();
2118 if (r < 0) {
2119 return r;
2120 }
2121
2122 if (!parser.init()) {
2123 return -EINVAL;
2124 }
2125
2126 if (!data || !parser.parse(data, len, 1)) {
2127 return -EINVAL;
2128 }
2129 cors_config =
2130 static_cast<RGWCORSConfiguration_S3 *>(parser.find_first(
2131 "CORSConfiguration"));
2132 if (!cors_config) {
2133 return -EINVAL;
2134 }
2135
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);
2140 }
2141
2142 if (s->cct->_conf->subsys.should_gather(ceph_subsys_rgw, 15)) {
2143 ldout(s->cct, 15) << "CORSConfiguration";
2144 cors_config->to_xml(*_dout);
2145 *_dout << dendl;
2146 }
2147
2148 cors_config->encode(cors_bl);
2149
2150 return 0;
2151 }
2152
2153 void RGWPutCORS_ObjStore_S3::send_response()
2154 {
2155 if (op_ret)
2156 set_req_state_err(s, op_ret);
2157 dump_errno(s);
2158 end_header(s, NULL, "application/xml");
2159 dump_start(s);
2160 }
2161
2162 void RGWDeleteCORS_ObjStore_S3::send_response()
2163 {
2164 int r = op_ret;
2165 if (!r || r == -ENOENT)
2166 r = STATUS_NO_CONTENT;
2167
2168 set_req_state_err(s, r);
2169 dump_errno(s);
2170 end_header(s, NULL);
2171 }
2172
2173 void RGWOptionsCORS_ObjStore_S3::send_response()
2174 {
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
2179 */
2180 if (op_ret == -ENOENT)
2181 op_ret = -EACCES;
2182 if (op_ret < 0) {
2183 set_req_state_err(s, op_ret);
2184 dump_errno(s);
2185 end_header(s, NULL);
2186 return;
2187 }
2188 get_response_params(hdrs, exp_hdrs, &max_age);
2189
2190 dump_errno(s);
2191 dump_access_control(s, origin, req_meth, hdrs.c_str(), exp_hdrs.c_str(),
2192 max_age);
2193 end_header(s, NULL);
2194 }
2195
2196 void RGWGetRequestPayment_ObjStore_S3::send_response()
2197 {
2198 dump_errno(s);
2199 end_header(s, this, "application/xml");
2200 dump_start(s);
2201
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);
2207 }
2208
2209 class RGWSetRequestPaymentParser : public RGWXMLParser
2210 {
2211 XMLObj *alloc_obj(const char *el) override {
2212 return new XMLObj;
2213 }
2214
2215 public:
2216 RGWSetRequestPaymentParser() {}
2217 ~RGWSetRequestPaymentParser() override {}
2218
2219 int get_request_payment_payer(bool *requester_pays) {
2220 XMLObj *config = find_first("RequestPaymentConfiguration");
2221 if (!config)
2222 return -EINVAL;
2223
2224 *requester_pays = false;
2225
2226 XMLObj *field = config->find_first("Payer");
2227 if (!field)
2228 return 0;
2229
2230 string& s = field->get_data();
2231
2232 if (stringcasecmp(s, "Requester") == 0) {
2233 *requester_pays = true;
2234 } else if (stringcasecmp(s, "BucketOwner") != 0) {
2235 return -EINVAL;
2236 }
2237
2238 return 0;
2239 }
2240 };
2241
2242 int RGWSetRequestPayment_ObjStore_S3::get_params()
2243 {
2244 char *data;
2245 int len = 0;
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);
2248
2249 if (r < 0) {
2250 return r;
2251 }
2252
2253 RGWSetRequestPaymentParser parser;
2254
2255 if (!parser.init()) {
2256 ldout(s->cct, 0) << "ERROR: failed to initialize parser" << dendl;
2257 r = -EIO;
2258 goto done;
2259 }
2260
2261 if (!parser.parse(data, len, 1)) {
2262 ldout(s->cct, 10) << "failed to parse data: " << data << dendl;
2263 r = -EINVAL;
2264 goto done;
2265 }
2266
2267 r = parser.get_request_payment_payer(&requester_pays);
2268
2269 done:
2270 free(data);
2271
2272 return r;
2273 }
2274
2275 void RGWSetRequestPayment_ObjStore_S3::send_response()
2276 {
2277 if (op_ret)
2278 set_req_state_err(s, op_ret);
2279 dump_errno(s);
2280 end_header(s);
2281 }
2282
2283 int RGWInitMultipart_ObjStore_S3::get_params()
2284 {
2285 RGWAccessControlPolicy_S3 s3policy(s->cct);
2286 op_ret = create_s3_policy(s, store, s3policy, s->owner);
2287 if (op_ret < 0)
2288 return op_ret;
2289
2290 policy = s3policy;
2291
2292 return 0;
2293 }
2294
2295 void RGWInitMultipart_ObjStore_S3::send_response()
2296 {
2297 if (op_ret)
2298 set_req_state_err(s, op_ret);
2299 dump_errno(s);
2300 for (auto &it : crypt_http_responses)
2301 dump_header(s, it.first, it.second);
2302 end_header(s, this, "application/xml");
2303 if (op_ret == 0) {
2304 dump_start(s);
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);
2313 }
2314 }
2315
2316 int RGWInitMultipart_ObjStore_S3::prepare_encryption(map<string, bufferlist>& attrs)
2317 {
2318 int res = 0;
2319 res = rgw_s3_prepare_encrypt(s, attrs, nullptr, nullptr, crypt_http_responses);
2320 return res;
2321 }
2322
2323 int RGWCompleteMultipart_ObjStore_S3::get_params()
2324 {
2325 int ret = RGWCompleteMultipart_ObjStore::get_params();
2326 if (ret < 0) {
2327 return ret;
2328 }
2329
2330 return do_aws4_auth_completion();
2331 }
2332
2333 void RGWCompleteMultipart_ObjStore_S3::send_response()
2334 {
2335 if (op_ret)
2336 set_req_state_err(s, op_ret);
2337 dump_errno(s);
2338 end_header(s, this, "application/xml");
2339 if (op_ret == 0) {
2340 dump_start(s);
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());
2348 }
2349 s->formatter->dump_string("Tenant", s->bucket_tenant);
2350 } else {
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());
2355 }
2356 }
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);
2362 }
2363 }
2364
2365 void RGWAbortMultipart_ObjStore_S3::send_response()
2366 {
2367 int r = op_ret;
2368 if (!r)
2369 r = STATUS_NO_CONTENT;
2370
2371 set_req_state_err(s, r);
2372 dump_errno(s);
2373 end_header(s, this);
2374 }
2375
2376 void RGWListMultipart_ObjStore_S3::send_response()
2377 {
2378 if (op_ret)
2379 set_req_state_err(s, op_ret);
2380 dump_errno(s);
2381 end_header(s, this, "application/xml");
2382
2383 if (op_ret == 0) {
2384 dump_start(s);
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;
2388 int cur_max = 0;
2389
2390 iter = parts.begin();
2391 test_iter = parts.rbegin();
2392 if (test_iter != parts.rend()) {
2393 cur_max = test_iter->first;
2394 }
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"));
2405
2406 ACLOwner& owner = policy.get_owner();
2407 dump_owner(s, owner.get_id(), owner.get_display_name());
2408
2409 for (; iter != parts.end(); ++iter) {
2410 RGWUploadPartInfo& info = iter->second;
2411
2412 s->formatter->open_object_section("Part");
2413
2414 dump_time(s, "LastModified", &info.modified);
2415
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();
2420 }
2421 s->formatter->close_section();
2422 rgw_flush_formatter_and_reset(s, s->formatter);
2423 }
2424 }
2425
2426 void RGWListBucketMultiparts_ObjStore_S3::send_response()
2427 {
2428 if (op_ret < 0)
2429 set_req_state_err(s, op_ret);
2430 dump_errno(s);
2431
2432 end_header(s, this, "application/xml");
2433 dump_start(s);
2434 if (op_ret < 0)
2435 return;
2436
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"));
2459
2460 if (op_ret >= 0) {
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();
2472 }
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);
2479 }
2480 s->formatter->close_section();
2481 }
2482 }
2483 s->formatter->close_section();
2484 rgw_flush_formatter_and_reset(s, s->formatter);
2485 }
2486
2487 int RGWDeleteMultiObj_ObjStore_S3::get_params()
2488 {
2489 int ret = RGWDeleteMultiObj_ObjStore::get_params();
2490 if (ret < 0) {
2491 return ret;
2492 }
2493
2494 return do_aws4_auth_completion();
2495 }
2496
2497 void RGWDeleteMultiObj_ObjStore_S3::send_status()
2498 {
2499 if (! status_dumped) {
2500 if (op_ret < 0)
2501 set_req_state_err(s, op_ret);
2502 dump_errno(s);
2503 status_dumped = true;
2504 }
2505 }
2506
2507 void RGWDeleteMultiObj_ObjStore_S3::begin_response()
2508 {
2509
2510 if (!status_dumped) {
2511 send_status();
2512 }
2513
2514 dump_start(s);
2515 end_header(s, this, "application/xml");
2516 s->formatter->open_object_section_in_ns("DeleteResult", XMLNS_AWS_S3);
2517
2518 rgw_flush_formatter(s, s->formatter);
2519 }
2520
2521 void RGWDeleteMultiObj_ObjStore_S3::send_partial_response(rgw_obj_key& key,
2522 bool delete_marker,
2523 const string& marker_version_id, int ret)
2524 {
2525 if (!key.empty()) {
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);
2531 }
2532 if (delete_marker) {
2533 s->formatter->dump_bool("DeleteMarker", true);
2534 s->formatter->dump_string("DeleteMarkerVersionId", marker_version_id);
2535 }
2536 s->formatter->close_section();
2537 } else if (op_ret < 0) {
2538 struct rgw_http_error r;
2539 int err_no;
2540
2541 s->formatter->open_object_section("Error");
2542
2543 err_no = -op_ret;
2544 rgw_get_errno_s3(&r, err_no);
2545
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();
2551 }
2552
2553 rgw_flush_formatter(s, s->formatter);
2554 }
2555 }
2556
2557 void RGWDeleteMultiObj_ObjStore_S3::end_response()
2558 {
2559
2560 s->formatter->close_section();
2561 rgw_flush_formatter_and_reset(s, s->formatter);
2562 }
2563
2564 void RGWGetObjLayout_ObjStore_S3::send_response()
2565 {
2566 if (op_ret)
2567 set_req_state_err(s, op_ret);
2568 dump_errno(s);
2569 end_header(s, this, "application/json");
2570
2571 JSONFormatter f;
2572
2573 if (op_ret < 0) {
2574 return;
2575 }
2576
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);
2588 f.close_section();
2589 rgw_flush_formatter(s, &f);
2590 }
2591 f.close_section();
2592 f.close_section();
2593 rgw_flush_formatter(s, &f);
2594 }
2595
2596 int RGWConfigBucketMetaSearch_ObjStore_S3::get_params()
2597 {
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;
2602 return -EINVAL;
2603 }
2604
2605 list<string> expressions;
2606 get_str_list(iter->second, ",", expressions);
2607
2608 for (auto& expression : expressions) {
2609 vector<string> args;
2610 get_str_vec(expression, ";", args);
2611
2612 if (args.empty()) {
2613 s->err.message = "invalid empty expression";
2614 ldout(s->cct, 5) << s->err.message << dendl;
2615 return -EINVAL;
2616 }
2617 if (args.size() > 2) {
2618 s->err.message = string("invalid expression: ") + expression;
2619 ldout(s->cct, 5) << s->err.message << dendl;
2620 return -EINVAL;
2621 }
2622
2623 string key = boost::algorithm::to_lower_copy(rgw_trim_whitespace(args[0]));
2624 string val;
2625 if (args.size() > 1) {
2626 val = boost::algorithm::to_lower_copy(rgw_trim_whitespace(args[1]));
2627 }
2628
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;
2632 return -EINVAL;
2633 }
2634
2635 key = key.substr(sizeof(RGW_AMZ_META_PREFIX) - 1);
2636
2637 ESEntityTypeMap::EntityType entity_type;
2638
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;
2645 } else {
2646 s->err.message = string("invalid entity type: ") + val;
2647 ldout(s->cct, 5) << s->err.message << dendl;
2648 return -EINVAL;
2649 }
2650
2651 mdsearch_config[key] = entity_type;
2652 }
2653
2654 return 0;
2655 }
2656
2657 void RGWConfigBucketMetaSearch_ObjStore_S3::send_response()
2658 {
2659 if (op_ret)
2660 set_req_state_err(s, op_ret);
2661 dump_errno(s);
2662 end_header(s, this);
2663 }
2664
2665 void RGWGetBucketMetaSearch_ObjStore_S3::send_response()
2666 {
2667 if (op_ret)
2668 set_req_state_err(s, op_ret);
2669 dump_errno(s);
2670 end_header(s, NULL, "application/xml");
2671
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());
2678 const char *type;
2679 switch (e.second) {
2680 case ESEntityTypeMap::ES_ENTITY_INT:
2681 type = "int";
2682 break;
2683 case ESEntityTypeMap::ES_ENTITY_DATE:
2684 type = "date";
2685 break;
2686 default:
2687 type = "str";
2688 }
2689 f->dump_string("Type", type);
2690 f->close_section();
2691 }
2692 f->close_section();
2693 rgw_flush_formatter(s, f);
2694 }
2695
2696 void RGWDelBucketMetaSearch_ObjStore_S3::send_response()
2697 {
2698 if (op_ret)
2699 set_req_state_err(s, op_ret);
2700 dump_errno(s);
2701 end_header(s, this);
2702 }
2703
2704
2705 RGWOp *RGWHandler_REST_Service_S3::op_get()
2706 {
2707 if (is_usage_op()) {
2708 return new RGWGetUsage_ObjStore_S3;
2709 } else {
2710 return new RGWListBuckets_ObjStore_S3;
2711 }
2712 }
2713
2714 RGWOp *RGWHandler_REST_Service_S3::op_head()
2715 {
2716 return new RGWListBuckets_ObjStore_S3;
2717 }
2718
2719 RGWOp *RGWHandler_REST_Service_S3::op_post()
2720 {
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;
2741 }
2742 return NULL;
2743 }
2744
2745 RGWOp *RGWHandler_REST_Bucket_S3::get_obj_op(bool get_data)
2746 {
2747 // Non-website mode
2748 if (get_data) {
2749 return new RGWListBucket_ObjStore_S3;
2750 } else {
2751 return new RGWStatBucket_ObjStore_S3;
2752 }
2753 }
2754
2755 RGWOp *RGWHandler_REST_Bucket_S3::op_get()
2756 {
2757 if (s->info.args.sub_resource_exists("logging"))
2758 return new RGWGetBucketLogging_ObjStore_S3;
2759
2760 if (s->info.args.sub_resource_exists("location"))
2761 return new RGWGetBucketLocation_ObjStore_S3;
2762
2763 if (s->info.args.sub_resource_exists("versioning"))
2764 return new RGWGetBucketVersioning_ObjStore_S3;
2765
2766 if (s->info.args.sub_resource_exists("website")) {
2767 if (!s->cct->_conf->rgw_enable_static_website) {
2768 return NULL;
2769 }
2770 return new RGWGetBucketWebsite_ObjStore_S3;
2771 }
2772
2773 if (s->info.args.exists("mdsearch")) {
2774 return new RGWGetBucketMetaSearch_ObjStore_S3;
2775 }
2776
2777 if (is_acl_op()) {
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;
2789 }
2790 return get_obj_op(true);
2791 }
2792
2793 RGWOp *RGWHandler_REST_Bucket_S3::op_head()
2794 {
2795 if (is_acl_op()) {
2796 return new RGWGetACLs_ObjStore_S3;
2797 } else if (s->info.args.exists("uploads")) {
2798 return new RGWListBucketMultiparts_ObjStore_S3;
2799 }
2800 return get_obj_op(false);
2801 }
2802
2803 RGWOp *RGWHandler_REST_Bucket_S3::op_put()
2804 {
2805 if (s->info.args.sub_resource_exists("logging"))
2806 return NULL;
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) {
2811 return NULL;
2812 }
2813 return new RGWSetBucketWebsite_ObjStore_S3;
2814 }
2815 if (is_acl_op()) {
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;
2825 }
2826 return new RGWCreateBucket_ObjStore_S3;
2827 }
2828
2829 RGWOp *RGWHandler_REST_Bucket_S3::op_delete()
2830 {
2831 if (is_cors_op()) {
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;
2837 }
2838
2839 if (s->info.args.sub_resource_exists("website")) {
2840 if (!s->cct->_conf->rgw_enable_static_website) {
2841 return NULL;
2842 }
2843 return new RGWDeleteBucketWebsite_ObjStore_S3;
2844 }
2845
2846 if (s->info.args.exists("mdsearch")) {
2847 return new RGWDelBucketMetaSearch_ObjStore_S3;
2848 }
2849
2850 return new RGWDeleteBucket_ObjStore_S3;
2851 }
2852
2853 RGWOp *RGWHandler_REST_Bucket_S3::op_post()
2854 {
2855 if (s->info.args.exists("delete")) {
2856 return new RGWDeleteMultiObj_ObjStore_S3;
2857 }
2858
2859 if (s->info.args.exists("mdsearch")) {
2860 return new RGWConfigBucketMetaSearch_ObjStore_S3;
2861 }
2862
2863 return new RGWPostObj_ObjStore_S3;
2864 }
2865
2866 RGWOp *RGWHandler_REST_Bucket_S3::op_options()
2867 {
2868 return new RGWOptionsCORS_ObjStore_S3;
2869 }
2870
2871 RGWOp *RGWHandler_REST_Obj_S3::get_obj_op(bool get_data)
2872 {
2873 if (is_acl_op()) {
2874 return new RGWGetACLs_ObjStore_S3;
2875 }
2876 RGWGetObj_ObjStore_S3 *get_obj_op = new RGWGetObj_ObjStore_S3;
2877 get_obj_op->set_get_data(get_data);
2878 return get_obj_op;
2879 }
2880
2881 RGWOp *RGWHandler_REST_Obj_S3::op_get()
2882 {
2883 if (is_acl_op()) {
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;
2889 }
2890 return get_obj_op(true);
2891 }
2892
2893 RGWOp *RGWHandler_REST_Obj_S3::op_head()
2894 {
2895 if (is_acl_op()) {
2896 return new RGWGetACLs_ObjStore_S3;
2897 } else if (s->info.args.exists("uploadId")) {
2898 return new RGWListMultipart_ObjStore_S3;
2899 }
2900 return get_obj_op(false);
2901 }
2902
2903 RGWOp *RGWHandler_REST_Obj_S3::op_put()
2904 {
2905 if (is_acl_op()) {
2906 return new RGWPutACLs_ObjStore_S3;
2907 }
2908 if (s->init_state.src_bucket.empty())
2909 return new RGWPutObj_ObjStore_S3;
2910 else
2911 return new RGWCopyObj_ObjStore_S3;
2912 }
2913
2914 RGWOp *RGWHandler_REST_Obj_S3::op_delete()
2915 {
2916 string upload_id = s->info.args.get("uploadId");
2917
2918 if (upload_id.empty())
2919 return new RGWDeleteObj_ObjStore_S3;
2920 else
2921 return new RGWAbortMultipart_ObjStore_S3;
2922 }
2923
2924 RGWOp *RGWHandler_REST_Obj_S3::op_post()
2925 {
2926 if (s->info.args.exists("uploadId"))
2927 return new RGWCompleteMultipart_ObjStore_S3;
2928
2929 if (s->info.args.exists("uploads"))
2930 return new RGWInitMultipart_ObjStore_S3;
2931
2932 return new RGWPostObj_ObjStore_S3;
2933 }
2934
2935 RGWOp *RGWHandler_REST_Obj_S3::op_options()
2936 {
2937 return new RGWOptionsCORS_ObjStore_S3;
2938 }
2939
2940 int RGWHandler_REST_S3::init_from_header(struct req_state* s,
2941 int default_formatter,
2942 bool configurable_format)
2943 {
2944 string req;
2945 string first;
2946
2947 const char *req_name = s->relative_uri.c_str();
2948 const char *p;
2949
2950 if (*req_name == '?') {
2951 p = req_name;
2952 } else {
2953 p = s->info.request_params.c_str();
2954 }
2955
2956 s->info.args.set(p);
2957 s->info.args.parse();
2958
2959 /* must be called after the args parsing */
2960 int ret = allocate_formatter(s, default_formatter, configurable_format);
2961 if (ret < 0)
2962 return ret;
2963
2964 if (*req_name != '/')
2965 return 0;
2966
2967 req_name++;
2968
2969 if (!*req_name)
2970 return 0;
2971
2972 req = req_name;
2973 int pos = req.find('/');
2974 if (pos >= 0) {
2975 first = req.substr(0, pos);
2976 } else {
2977 first = req;
2978 }
2979
2980 /*
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.
2985 *
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.
2989 */
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;
2993 if (pos >= 0) {
2994 string encoded_obj_str = req.substr(pos+1);
2995 s->object = rgw_obj_key(encoded_obj_str, s->info.args.get("versionId"));
2996 }
2997 } else {
2998 s->object = rgw_obj_key(req_name, s->info.args.get("versionId"));
2999 }
3000 return 0;
3001 }
3002
3003 int RGWHandler_REST_S3::postauth_init()
3004 {
3005 struct req_init_state *t = &s->init_state;
3006 bool relaxed_names = s->cct->_conf->rgw_relaxed_s3_bucket_names;
3007
3008 rgw_parse_url_bucket(t->url_bucket, s->user->user_id.tenant,
3009 s->bucket_tenant, s->bucket_name);
3010
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;
3013
3014 int ret;
3015 ret = validate_tenant_name(s->bucket_tenant);
3016 if (ret)
3017 return ret;
3018 if (!s->bucket_name.empty()) {
3019 ret = valid_s3_bucket_name(s->bucket_name, relaxed_names);
3020 if (ret)
3021 return ret;
3022 ret = validate_object_name(s->object.name);
3023 if (ret)
3024 return ret;
3025 }
3026
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);
3031 if (ret)
3032 return ret;
3033 ret = valid_s3_bucket_name(s->src_bucket_name, relaxed_names);
3034 if (ret)
3035 return ret;
3036 }
3037 return 0;
3038 }
3039
3040 int RGWHandler_REST_S3::init(RGWRados *store, struct req_state *s,
3041 rgw::io::BasicClient *cio)
3042 {
3043 int ret;
3044
3045 s->dialect = "s3";
3046
3047 ret = validate_tenant_name(s->bucket_tenant);
3048 if (ret)
3049 return ret;
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);
3053 if (ret)
3054 return ret;
3055 ret = validate_object_name(s->object.name);
3056 if (ret)
3057 return ret;
3058 }
3059
3060 const char *cacl = s->info.env->get("HTTP_X_AMZ_ACL");
3061 if (cacl)
3062 s->canned_acl = cacl;
3063
3064 s->has_acl_header = s->info.env->exists_prefix("HTTP_X_AMZ_GRANT");
3065
3066 const char *copy_source = s->info.env->get("HTTP_X_AMZ_COPY_SOURCE");
3067
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,
3071 s->src_object);
3072 if (!ret) {
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?
3075 }
3076 }
3077
3078 return RGWHandler_REST::init(store, s, cio);
3079 }
3080
3081 enum class AwsVersion {
3082 UNKOWN,
3083 V2,
3084 V4
3085 };
3086
3087 enum class AwsRoute {
3088 UNKOWN,
3089 QUERY_STRING,
3090 HEADERS
3091 };
3092
3093 static inline std::pair<AwsVersion, AwsRoute>
3094 discover_aws_flavour(const req_info& info)
3095 {
3096 using rgw::auth::s3::AWS4_HMAC_SHA256_STR;
3097
3098 AwsVersion version = AwsVersion::UNKOWN;
3099 AwsRoute route = AwsRoute::UNKOWN;
3100
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;
3105
3106 if (!strncmp(http_auth, AWS4_HMAC_SHA256_STR,
3107 strlen(AWS4_HMAC_SHA256_STR))) {
3108 /* AWS v4 */
3109 version = AwsVersion::V4;
3110 } else if (!strncmp(http_auth, "AWS ", 4)) {
3111 /* AWS v2 */
3112 version = AwsVersion::V2;
3113 }
3114 } else {
3115 route = AwsRoute::QUERY_STRING;
3116
3117 if (info.args.get("X-Amz-Algorithm") == AWS4_HMAC_SHA256_STR) {
3118 /* AWS v4 */
3119 version = AwsVersion::V4;
3120 } else if (!info.args.get("AWSAccessKeyId").empty()) {
3121 /* AWS v2 */
3122 version = AwsVersion::V2;
3123 }
3124 }
3125
3126 return std::make_pair(version, route);
3127 }
3128
3129 static void init_anon_user(struct req_state *s)
3130 {
3131 rgw_get_anon_user(*(s->user));
3132 s->perm_mask = RGW_PERM_FULL_CONTROL;
3133 }
3134
3135 /*
3136 * verify that a signed request comes from the keyholder
3137 * by checking the signature against our locally-computed version
3138 *
3139 * it tries AWS v4 before AWS v2
3140 */
3141 int RGW_Auth_S3::authorize(RGWRados* const store,
3142 const rgw::auth::StrategyRegistry& auth_registry,
3143 struct req_state* const s)
3144 {
3145
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;
3151 return -EPERM;
3152 }
3153
3154 if (s->op == OP_OPTIONS) {
3155 init_anon_user(s);
3156 return 0;
3157 }
3158
3159 AwsVersion version;
3160 AwsRoute route;
3161 std::tie(version, route) = discover_aws_flavour(s->info);
3162
3163 if (route == AwsRoute::QUERY_STRING && version == AwsVersion::UNKOWN) {
3164 /* FIXME(rzarzynski): handle anon user. */
3165 init_anon_user(const_cast<req_state*>(s));
3166 return 0;
3167 }
3168
3169 return authorize_v2(store, auth_registry, s);
3170 }
3171
3172
3173 /*
3174 * handle v2 signatures
3175 */
3176 int RGW_Auth_S3::authorize_v2(RGWRados* const store,
3177 const rgw::auth::StrategyRegistry& auth_registry,
3178 struct req_state* const s)
3179 {
3180 const auto ret = rgw::auth::Strategy::apply(auth_registry.get_s3_main(), s);
3181 if (ret == 0) {
3182 /* Populate the owner info. */
3183 s->owner.set_id(s->user->user_id);
3184 s->owner.set_name(s->user->display_name);
3185 }
3186 return ret;
3187 }
3188
3189 int RGWHandler_Auth_S3::init(RGWRados *store, struct req_state *state,
3190 rgw::io::BasicClient *cio)
3191 {
3192 int ret = RGWHandler_REST_S3::init_from_header(state, RGW_FORMAT_JSON,
3193 true);
3194 if (ret < 0)
3195 return ret;
3196
3197 return RGWHandler_REST::init(store, state, cio);
3198 }
3199
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)
3203 {
3204 bool is_s3website = enable_s3website && (s->prot_flags & RGW_REST_WEBSITE);
3205 int ret =
3206 RGWHandler_REST_S3::init_from_header(s,
3207 is_s3website ? RGW_FORMAT_HTML :
3208 RGW_FORMAT_XML, true);
3209 if (ret < 0)
3210 return NULL;
3211
3212 RGWHandler_REST* handler;
3213 // TODO: Make this more readable
3214 if (is_s3website) {
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);
3219 } else {
3220 handler = new RGWHandler_REST_Obj_S3Website(auth_registry);
3221 }
3222 } else {
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);
3227 } else {
3228 handler = new RGWHandler_REST_Obj_S3(auth_registry);
3229 }
3230 }
3231
3232 ldout(s->cct, 20) << __func__ << " handler=" << typeid(*handler).name()
3233 << dendl;
3234 return handler;
3235 }
3236
3237 int RGWHandler_REST_S3Website::retarget(RGWOp* op, RGWOp** new_op) {
3238 *new_op = op;
3239 ldout(s->cct, 10) << __func__ << "Starting retarget" << dendl;
3240
3241 if (!(s->prot_flags & RGW_REST_WEBSITE))
3242 return 0;
3243
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,
3247 &s->bucket_attrs);
3248 if (ret < 0) {
3249 // TODO-FUTURE: if the bucket does not exist, maybe expose it here?
3250 return -ERR_NO_SUCH_BUCKET;
3251 }
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;
3255 }
3256
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;
3261
3262 RGWBWRoutingRule rrule;
3263 bool should_redirect =
3264 s->bucket_info.website_conf.should_redirect(new_obj.name, 0, &rrule);
3265
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,
3272 &redirect_code);
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;
3280 }
3281
3282 /*
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
3285 */
3286
3287 s->object = new_obj;
3288
3289 return 0;
3290 }
3291
3292 RGWOp* RGWHandler_REST_S3Website::op_get()
3293 {
3294 return get_obj_op(true);
3295 }
3296
3297 RGWOp* RGWHandler_REST_S3Website::op_head()
3298 {
3299 return get_obj_op(false);
3300 }
3301
3302 int RGWHandler_REST_S3Website::serve_errordoc(int http_ret, const string& errordoc_key) {
3303 int ret = 0;
3304 s->formatter->reset(); /* Try to throw it all away */
3305
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
3309 }
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;
3317
3318 ret = init_permissions(getop.get());
3319 if (ret < 0) {
3320 ldout(s->cct, 20) << "serve_errordoc failed, init_permissions ret=" << ret << dendl;
3321 return -1; // Trigger double error handler
3322 }
3323
3324 ret = read_permissions(getop.get());
3325 if (ret < 0) {
3326 ldout(s->cct, 20) << "serve_errordoc failed, read_permissions ret=" << ret << dendl;
3327 return -1; // Trigger double error handler
3328 }
3329
3330 if (http_ret) {
3331 getop->set_custom_http_response(http_ret);
3332 }
3333
3334 ret = getop->init_processing();
3335 if (ret < 0) {
3336 ldout(s->cct, 20) << "serve_errordoc failed, init_processing ret=" << ret << dendl;
3337 return -1; // Trigger double error handler
3338 }
3339
3340 ret = getop->verify_op_mask();
3341 if (ret < 0) {
3342 ldout(s->cct, 20) << "serve_errordoc failed, verify_op_mask ret=" << ret << dendl;
3343 return -1; // Trigger double error handler
3344 }
3345
3346 ret = getop->verify_permission();
3347 if (ret < 0) {
3348 ldout(s->cct, 20) << "serve_errordoc failed, verify_permission ret=" << ret << dendl;
3349 return -1; // Trigger double error handler
3350 }
3351
3352 ret = getop->verify_params();
3353 if (ret < 0) {
3354 ldout(s->cct, 20) << "serve_errordoc failed, verify_params ret=" << ret << dendl;
3355 return -1; // Trigger double error handler
3356 }
3357
3358 // No going back now
3359 getop->pre_exec();
3360 /*
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
3366 */
3367 getop->execute();
3368 getop->complete();
3369 return 0;
3370
3371 }
3372
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;
3378
3379 if (r != rgw_http_s3_errors.end()) {
3380 http_error_code = r->second.first;
3381 }
3382 ldout(s->cct, 10) << "RGWHandler_REST_S3Website::error_handler err_no=" << err_no << " http_ret=" << http_error_code << dendl;
3383
3384 RGWBWRoutingRule rrule;
3385 bool should_redirect =
3386 s->bucket_info.website_conf.should_redirect(s->object.name, http_error_code,
3387 &rrule);
3388
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,
3395 &redirect_code);
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
3410 */
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;
3414 }
3415 } else {
3416 ldout(s->cct, 20) << "No special error handling today!" << dendl;
3417 }
3418
3419 return err_no;
3420 }
3421
3422 RGWOp* RGWHandler_REST_Obj_S3Website::get_obj_op(bool get_data)
3423 {
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!
3427 */
3428 RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
3429 op->set_get_data(get_data);
3430 return op;
3431 }
3432
3433 RGWOp* RGWHandler_REST_Bucket_S3Website::get_obj_op(bool get_data)
3434 {
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!
3438 */
3439 RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
3440 op->set_get_data(get_data);
3441 return op;
3442 }
3443
3444 RGWOp* RGWHandler_REST_Service_S3Website::get_obj_op(bool get_data)
3445 {
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!
3449 */
3450 RGWGetObj_ObjStore_S3Website* op = new RGWGetObj_ObjStore_S3Website;
3451 op->set_get_data(get_data);
3452 return op;
3453 }
3454
3455
3456 namespace rgw {
3457 namespace auth {
3458 namespace s3 {
3459
3460 bool AWSGeneralAbstractor::is_time_skew_ok(const utime_t& header_time,
3461 const bool qsr) const
3462 {
3463 /* Check for time skew first. */
3464 const time_t req_sec = header_time.sec();
3465 time_t now;
3466 time(&now);
3467
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
3475 << dendl;
3476
3477 ldout(cct, 0) << "NOTICE: request time skew too big now="
3478 << utime_t(now, 0)
3479 << " req_time=" << header_time
3480 << dendl;
3481 return false;
3482 } else {
3483 return true;
3484 }
3485 }
3486
3487
3488 static rgw::auth::Completer::cmplptr_t
3489 null_completer_factory(const boost::optional<std::string>& secret_key)
3490 {
3491 return nullptr;
3492 }
3493
3494
3495 using AWSVerAbstractor = AWSEngine::VersionAbstractor;
3496
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
3503 {
3504 AwsVersion version;
3505 AwsRoute route;
3506 std::tie(version, route) = discover_aws_flavour(s->info);
3507
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);
3512 } else {
3513 /* FIXME(rzarzynski): handle anon user. */
3514 throw -EINVAL;
3515 }
3516 }
3517
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
3523 {
3524 return rgw::auth::s3::get_v4_canonical_headers(info, signedheaders,
3525 using_qs, false);
3526 }
3527
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,
3534 /* FIXME: const. */
3535 bool using_qs) const
3536 {
3537 boost::string_view access_key_id;
3538 boost::string_view signed_hdrs;
3539
3540 boost::string_view date;
3541 boost::string_view credential_scope;
3542 boost::string_view client_signature;
3543
3544 int ret = rgw::auth::s3::parse_credentials(s->info,
3545 access_key_id,
3546 credential_scope,
3547 signed_hdrs,
3548 client_signature,
3549 date,
3550 using_qs);
3551 if (ret < 0) {
3552 throw ret;
3553 }
3554
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
3560 << dendl;
3561 } else {
3562 throw -EPERM;
3563 }
3564
3565 /* Get the expected hash. */
3566 auto exp_payload_hash = rgw::auth::s3::get_v4_exp_payload_hash(s->info);
3567
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);
3570
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);
3573
3574 /* Craft canonical request. */
3575 auto canonical_req_hash = \
3576 rgw::auth::s3::get_v4_canon_req_hash(s->cct,
3577 s->info.method,
3578 std::move(canonical_uri),
3579 std::move(canonical_qs),
3580 std::move(*canonical_headers),
3581 signed_hdrs,
3582 exp_payload_hash);
3583
3584 auto string_to_sign = \
3585 rgw::auth::s3::get_v4_string_to_sign(s->cct,
3586 AWS4_HMAC_SHA256_STR,
3587 date,
3588 credential_scope,
3589 std::move(canonical_req_hash));
3590
3591 const auto sig_factory = std::bind(rgw::auth::s3::get_v4_signature,
3592 credential_scope,
3593 std::placeholders::_1,
3594 std::placeholders::_2,
3595 std::placeholders::_3);
3596
3597 /* Requests authenticated with the Query Parameters are treated as unsigned.
3598 * From "Authenticating Requests: Using Query Parameters (AWS Signature
3599 * Version 4)":
3600 *
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.
3605 *
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,
3611 client_signature,
3612 std::move(string_to_sign),
3613 sig_factory,
3614 null_completer_factory);
3615 } else {
3616 /* We're going to handle a signed payload. Be aware that even empty HTTP
3617 * body (no payload) requires verification:
3618 *
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;
3624
3625 /* payload in a single chunk */
3626 switch (s->op_type)
3627 {
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:
3638 break;
3639 default:
3640 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED" << dendl;
3641 throw -ERR_NOT_IMPLEMENTED;
3642 }
3643
3644 const auto cmpl_factory = std::bind(AWSv4ComplSingle::create,
3645 s,
3646 std::placeholders::_1);
3647 return std::make_tuple(access_key_id,
3648 client_signature,
3649 std::move(string_to_sign),
3650 sig_factory,
3651 cmpl_factory);
3652 } else {
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. */
3655
3656 dout(10) << "body content detected in multiple chunks" << dendl;
3657
3658 /* payload in multiple chunks */
3659
3660 switch(s->op_type)
3661 {
3662 case RGW_OP_PUT_OBJ:
3663 break;
3664 default:
3665 dout(10) << "ERROR: AWS4 completion for this operation NOT IMPLEMENTED (streaming mode)" << dendl;
3666 throw -ERR_NOT_IMPLEMENTED;
3667 }
3668
3669 dout(10) << "aws4 seed signature ok... delaying v4 auth" << dendl;
3670
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. */
3674
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. */
3677
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
3680 * for CanonReq. */
3681 const auto cmpl_factory = std::bind(AWSv4ComplMulti::create,
3682 s,
3683 date,
3684 credential_scope,
3685 client_signature,
3686 std::placeholders::_1);
3687 return std::make_tuple(access_key_id,
3688 client_signature,
3689 std::move(string_to_sign),
3690 sig_factory,
3691 cmpl_factory);
3692 }
3693 }
3694 }
3695
3696
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
3702 {
3703 return rgw::auth::s3::get_v4_canonical_headers(info, signedheaders,
3704 using_qs, true);
3705 }
3706
3707
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
3714 {
3715 boost::string_view access_key_id;
3716 boost::string_view signature;
3717 bool qsr = false;
3718
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");
3725 qsr = true;
3726
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());
3732 time_t now;
3733 time(&now);
3734
3735 if (now >= exp) {
3736 throw -EPERM;
3737 }
3738 }
3739 } else {
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);
3746 }
3747 }
3748
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,
3753 qsr)) {
3754 ldout(cct, 10) << "failed to create the canonized auth header\n"
3755 << rgw::crypt_sanitize::auth{s,string_to_sign} << dendl;
3756 throw -EPERM;
3757 }
3758
3759 ldout(cct, 10) << "string_to_sign:\n"
3760 << rgw::crypt_sanitize::auth{s,string_to_sign} << dendl;
3761
3762 if (! is_time_skew_ok(header_time, qsr)) {
3763 throw -ERR_REQUEST_TIME_SKEWED;
3764 }
3765
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);
3771 }
3772
3773
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
3780 {
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);
3786 }
3787
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
3794 {
3795 const boost::string_view credential = s->auth.s3_postobj_creds.x_amz_credential;
3796
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;
3801
3802 /* grab credential scope */
3803 const boost::string_view credential_scope = credential.substr(pos + 1);
3804 dout(10) << "credential scope = " << credential_scope << dendl;
3805
3806 const auto sig_factory = std::bind(rgw::auth::s3::get_v4_signature,
3807 credential_scope,
3808 std::placeholders::_1,
3809 std::placeholders::_2,
3810 std::placeholders::_3);
3811
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),
3815 sig_factory,
3816 null_completer_factory);
3817 }
3818
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
3825 {
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);
3830 } else {
3831 ldout(s->cct, 0) << "Signature verification algorithm AWS v2" << dendl;
3832 return get_auth_data_v2(s);
3833 }
3834 }
3835
3836
3837 AWSEngine::result_t
3838 AWSEngine::authenticate(const req_state* const s) const
3839 {
3840 boost::string_view access_key_id;
3841 boost::string_view signature;
3842 VersionAbstractor::string_to_sign_t string_to_sign;
3843
3844 VersionAbstractor::signature_factory_t signature_factory;
3845 VersionAbstractor::completer_factory_t completer_factory;
3846
3847 /* Small reminder: an ver_abstractor is allowed to throw! */
3848 std::tie(access_key_id,
3849 signature,
3850 string_to_sign,
3851 signature_factory,
3852 completer_factory) = ver_abstractor.get_auth_data(s);
3853
3854 if (access_key_id.empty() || signature.empty()) {
3855 return result_t::deny(-EINVAL);
3856 } else {
3857 return authenticate(access_key_id, signature, string_to_sign,
3858 signature_factory, completer_factory, s);
3859 }
3860 }
3861
3862 } /* namespace s3 */
3863 } /* namespace auth */
3864 } /* namespace rgw */
3865
3866 rgw::LDAPHelper* rgw::auth::s3::LDAPEngine::ldh = nullptr;
3867 std::mutex rgw::auth::s3::LDAPEngine::mtx;
3868
3869 void rgw::auth::s3::LDAPEngine::init(CephContext* const cct)
3870 {
3871 if (! ldh) {
3872 std::lock_guard<std::mutex> lck(mtx);
3873 if (! ldh) {
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);
3880
3881 ldh = new rgw::LDAPHelper(ldap_uri, ldap_binddn, ldap_bindpw,
3882 ldap_searchdn, ldap_searchfilter, ldap_dnattr);
3883
3884 ldh->init();
3885 ldh->bind();
3886 }
3887 }
3888 }
3889
3890 rgw::auth::RemoteApplier::acl_strategy_t
3891 rgw::auth::s3::LDAPEngine::get_acl_strategy() const
3892 {
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.
3895 return nullptr;
3896 }
3897
3898 rgw::auth::RemoteApplier::AuthInfo
3899 rgw::auth::s3::LDAPEngine::get_creds_info(const rgw::RGWToken& token) const noexcept
3900 {
3901 /* The short form of "using" can't be used here -- we're aliasing a class'
3902 * member. */
3903 using acct_privilege_t = \
3904 rgw::auth::RemoteApplier::AuthInfo::acct_privilege_t;
3905
3906 return rgw::auth::RemoteApplier::AuthInfo {
3907 rgw_user(token.id),
3908 token.id,
3909 RGW_PERM_FULL_CONTROL,
3910 acct_privilege_t::IS_PLAIN_ACCT,
3911 TYPE_LDAP
3912 };
3913 }
3914
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
3923 {
3924 /* boost filters and/or string_ref may throw on invalid input */
3925 rgw::RGWToken base64_token;
3926 try {
3927 base64_token = rgw::from_base64(access_key_id);
3928 } catch (...) {
3929 base64_token = std::string("");
3930 }
3931
3932 if (! base64_token.valid()) {
3933 return result_t::deny();
3934 }
3935
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
3938 //return error.
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;
3944 return nullptr;
3945 }
3946 }*/
3947
3948 if (ldh->auth(base64_token.id, base64_token.key) != 0) {
3949 return result_t::deny();
3950 }
3951
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));
3955 }
3956
3957
3958 /* LocalEndgine */
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
3967 {
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);
3976 }
3977 //TODO: Uncomment, when we have a migration plan in place.
3978 /*else {
3979 if (s->user->type != TYPE_RGW) {
3980 ldout(cct, 10) << "ERROR: User id of type: " << s->user->type
3981 << " is present" << dendl;
3982 throw -EPERM;
3983 }
3984 }*/
3985
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);
3990 }
3991 const RGWAccessKey& k = iter->second;
3992
3993 const VersionAbstractor::server_signature_t server_signature = \
3994 signature_factory(cct, k.key, string_to_sign);
3995
3996 ldout(cct, 15) << "string_to_sign="
3997 << rgw::crypt_sanitize::log_content{string_to_sign}
3998 << dendl;
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;
4002
4003 if (static_cast<boost::string_view>(server_signature) != signature) {
4004 return result_t::deny(-ERR_SIGNATURE_NO_MATCH);
4005 }
4006
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));
4009 }